Subversion Repositories filter_foundry

Rev

Rev 379 | Rev 387 | Go to most recent revision | Blame | Compare with Previous | Last modification | View Log | RSS feed

  1. /*
  2.     This file is part of "Filter Foundry", a filter plugin for Adobe Photoshop
  3.     Copyright (C) 2003-2009 Toby Thain, toby@telegraphics.com.au
  4.     Copyright (C) 2018-2021 Daniel Marschall, ViaThinkSoft
  5.  
  6.     This program is free software; you can redistribute it and/or modify
  7.     it under the terms of the GNU General Public License as published by
  8.     the Free Software Foundation; either version 2 of the License, or
  9.     (at your option) any later version.
  10.  
  11.     This program is distributed in the hope that it will be useful,
  12.     but WITHOUT ANY WARRANTY; without even the implied warranty of
  13.     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  14.     GNU General Public License for more details.
  15.  
  16.     You should have received a copy of the GNU General Public License
  17.     along with this program; if not, write to the Free Software
  18.     Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  19. */
  20.  
  21. /* This is PLATFORM INDEPENDENT user interface code - mainly dialog logic */
  22.  
  23. #include "ff.h"
  24.  
  25. #include "node.h"
  26. #include "funcs.h"
  27. #include "y.tab.h"
  28. #include "choosefile.h"
  29. #include "sprintf_tiny.h"
  30. #include "compat_string.h"
  31.  
  32. #ifdef MAC_ENV
  33.         #include <plstringfuncs.h>
  34. #endif
  35.  
  36. Boolean doupdates = true;
  37.  
  38. void updateglobals(DIALOGREF dp);
  39. struct node *updateexpr(DIALOGREF dp,int i);
  40. void updatedialog(DIALOGREF dp);
  41. void slidertextchanged(DIALOGREF dp,int item);
  42. void updatezoom(DIALOGREF dp);
  43.  
  44. void updatedialog(DIALOGREF dp){
  45.         int i;
  46.  
  47.         doupdates = false;
  48.  
  49.         for(i = 0; i < 8; ++i){
  50.                 SETSLIDERVALUE(dp,FIRSTCTLITEM+i,slider[i]);
  51.                 SETCTLTEXTINT(dp,FIRSTCTLTEXTITEM+i,slider[i],false);
  52.         }
  53.  
  54.         for(i = 0; i < 4; ++i){
  55.                 if(!gdata->standalone)
  56.                         SETCTLTEXT(dp,FIRSTEXPRITEM+i,expr[i] ? expr[i] : "");
  57.                 if(i < nplanes)
  58.                         updateexpr(dp,FIRSTEXPRITEM+i);
  59.         }
  60.  
  61.         if(!gdata->standalone)
  62.                 SELECTCTLTEXT(dp,FIRSTEXPRITEM,0,-1);
  63.  
  64.         doupdates = true;
  65. }
  66.  
  67. /* copy dialog settings to global variables (sliders, expressions) */
  68.  
  69. void updateglobals(DIALOGREF dp){
  70.         int i;
  71.         char s[MAXEXPR+1];
  72.  
  73.         for(i = 0; i < 8; ++i)
  74.                 slider[i] = (value_type)(GETSLIDERVALUE(dp,FIRSTCTLITEM+i));
  75.  
  76.         if(!gdata->standalone)
  77.                 for(i = 0; i < 4; ++i){
  78.                         /* stash expression strings */
  79.                         if(GETCTLTEXT(dp,FIRSTEXPRITEM+i,s,MAXEXPR)){
  80.                                 if(expr[i])
  81.                                         free(expr[i]);
  82.                                 expr[i] = _strdup(s);
  83.                         }
  84.                         if(!expr[i])
  85.                                 expr[i] = _strdup("c");
  86.                 }
  87. }
  88.  
  89. struct node *updateexpr(DIALOGREF dp,int item){
  90.         char s[MAXEXPR+1];
  91.         int i;
  92.  
  93.         i = item - FIRSTEXPRITEM;
  94.  
  95.         freetree(tree[i]);
  96.  
  97.         if(!gdata->standalone){
  98.                 GETCTLTEXT(dp,item,s,MAXEXPR);
  99.  
  100.                 if(expr[i])
  101.                         free(expr[i]);
  102.                 expr[i] = _strdup(s);
  103.         }
  104.  
  105.         tree[i] = parseexpr(expr[i]);
  106.  
  107.         if(!gdata->standalone){
  108.                 if(tree[i])
  109.                         HideDialogItem(dp,FIRSTICONITEM+i);
  110.                 else{
  111.                         err[i] = errstr;
  112.                         errstart[i] = tokstart;
  113.                         errpos[i] = tokpos;
  114.                         ShowDialogItem(dp,FIRSTICONITEM+i);
  115.                 }
  116.         }
  117.         return tree[i];
  118. }
  119.  
  120. void updatezoom(DIALOGREF dp){
  121.         char s[10];
  122.         sprintf(s, "%d%%", (int)(100./zoomfactor));
  123.         SETCTLTEXT(dp,ZOOMLEVELITEM,s);
  124.         if(zoomfactor > 1.)
  125.                 ShowDialogItem(dp,ZOOMINITEM);
  126.         else
  127.                 HideDialogItem(dp,ZOOMINITEM);
  128.         if(zoomfactor < fitzoom)
  129.                 ShowDialogItem(dp,ZOOMOUTITEM);
  130.         else
  131.                 HideDialogItem(dp,ZOOMOUTITEM);
  132. }
  133.  
  134. /* traverse expression tree, looking for constant references to sliders/maps */
  135.  
  136. static int _checksl(struct node*p,int ctlflags[],int mapflags[]){
  137.         int s, i, result;
  138.  
  139.         result = 0;
  140.         if(p){
  141.                 if( (p->kind==TOK_FN1 && p->v.sym->fn == (pfunc_type)ff_ctl)
  142.                  || (p->kind==TOK_FN3 && p->v.sym->fn == (pfunc_type)ff_val) ){
  143.                         if(p->child[0]->kind == TOK_NUM){
  144.                                 s = p->child[0]->v.value;
  145.                                 if(s>=0 && s<=7)
  146.                                         ctlflags[s] = 1;
  147.                         }else
  148.                                 result |= CHECKSLIDERS_CTL_AMBIGUOUS; /* can't determine which ctl() */
  149.                 }else if(p->kind==TOK_FN2 && p->v.sym->fn == (pfunc_type)ff_map){
  150.                         if(p->child[0]->kind == TOK_NUM){
  151.                                 s = p->child[0]->v.value;
  152.                                 if(s>=0 && s<=3){
  153.                                         mapflags[s] = 1;
  154.                                         ctlflags[s*2] = ctlflags[s*2+1] = 1;
  155.                                 }
  156.                         }else
  157.                                 result |= CHECKSLIDERS_MAP_AMBIGUOUS; /* can't determine which map() */
  158.                  }
  159.  
  160.                 for( i = 0 ; i < MAXCHILDREN ; ++i )
  161.                         result |= _checksl(p->child[i],ctlflags,mapflags);
  162.         }
  163.  
  164.         return result;
  165. }
  166.  
  167. int checksliders(int exprs,int ctlflags[],int mapflags[]){
  168.         int i, result;
  169.  
  170.         result = 0;
  171.  
  172.         for(i = 4; i--;)
  173.                 mapflags[i] = 0;
  174.         for(i = 8; i--;)
  175.                 ctlflags[i] = 0;
  176.  
  177.         for(i = 0; i < exprs; i++)
  178.                 result |= _checksl(tree[i],ctlflags,mapflags);
  179.  
  180.         return result;
  181. }
  182.  
  183. void slidermoved(DIALOGREF dp,int i){
  184.         int v = GETSLIDERVALUE(dp,i);
  185.         i -= FIRSTCTLITEM;
  186.         slider[i] = v;
  187.         SETCTLTEXTINT(dp,i+FIRSTCTLTEXTITEM,v,false);
  188. }
  189.  
  190. void slidertextchanged(DIALOGREF dp,int i){
  191.         int v = GETCTLTEXTINT(dp,i,NULL,false);
  192.         i -= FIRSTCTLTEXTITEM;
  193.         SETSLIDERVALUE(dp,i+FIRSTCTLITEM,v);
  194.         slider[i] = v;
  195. }
  196.  
  197. void maindlgupdate(DIALOGREF dp){
  198.         int i,unknown,ctls[8],maps[4];
  199.  
  200.         unknown = checksliders(nplanes,ctls,maps);
  201.  
  202.         for(i = 0; i < 8; i++)
  203.                 if(unknown || ctls[i]){
  204.                         ENABLEDLGITEM(dp,FIRSTCTLITEM+i);
  205.                         ENABLEDLGITEM(dp,FIRSTCTLLABELITEM+i);
  206.                         ShowDialogItem(dp,FIRSTCTLTEXTITEM+i); /* FIXME: this changes keyboard focus */
  207.                 }else{
  208.                         DISABLEDLGITEM(dp,FIRSTCTLITEM+i);
  209.                         DISABLEDLGITEM(dp,FIRSTCTLLABELITEM+i);
  210.                         HideDialogItem(dp,FIRSTCTLTEXTITEM+i); /* FIXME: this changes keyboard focus */
  211.                 }
  212.  
  213.         for(i = 0; i < nplanes; i++)
  214.                 if(!tree[i]){
  215.                         /* uh oh, couldn't parse one of the saved expressions...this is fatal */
  216.                         DISABLEDLGITEM(dp,IDOK);
  217.                         if(gdata->standalone){
  218.                                 alertuser(_strdup("Can't run this filter (there is a problem with the saved expressions)."),_strdup(""));
  219.                         }else{
  220.                                 DISABLEDLGITEM(dp,SAVEITEM);
  221.                                 DISABLEDLGITEM(dp,MAKEITEM);
  222.                         }
  223.                         return;
  224.                 }
  225.  
  226.         /* we have valid expression trees in all slots...proceed! */
  227.         updateglobals(dp);
  228.         if(setup(gpb))
  229.                 recalc_preview(gpb,dp);
  230.  
  231.         ENABLEDLGITEM(dp,IDOK);
  232.         if(!gdata->standalone){
  233.                 ENABLEDLGITEM(dp,SAVEITEM);
  234.                 ENABLEDLGITEM(dp,MAKEITEM);
  235.                 ENABLEDLGITEM(dp,HELPITEM);
  236.         }
  237. }
  238.  
  239. /* one-time initialisation of dialog box */
  240.  
  241. void maindlginit(DIALOGREF dp){
  242.         char s[0x100];
  243.         int i;
  244.         const char *channelsuffixes[] = {
  245.                 "", "KA", "I", "RGBA",
  246.                 "CMYK", "HSL", "HSB", "1234",
  247.                 "DA", "LabA"
  248.         };
  249.  
  250.         /* hide unused expression items */
  251.         if(gdata->standalone){
  252.                 myp2cstrcpy(s,gdata->parm.author);
  253.                 SetDlgItemText(dp,PARAMAUTHORITEM,s);
  254.                 myp2cstrcpy(s,gdata->parm.copyright);
  255.                 SetDlgItemText(dp,PARAMCOPYITEM,s);
  256.  
  257.                 // update labels for map() or ctl() sliders
  258.                 for(i = 0; i < 8; ++i){
  259.                         if(gdata->parm.map_used[i/2]){
  260.                                 if((i&1) == 0){
  261.                                         // even (0, 2, 4, 6)
  262.                                         myp2cstrcpy(s,gdata->parm.map[i/2]);
  263.                                         SetDlgItemText(dp, FIRSTMAPLABELITEM+(i/2),s);
  264.                                         HideDialogItem(dp, FIRSTCTLLABELITEM + i);
  265.                                         HideDialogItem(dp, FIRSTCTLLABELITEM + i + 1);
  266.                                 }
  267.                         } else if(gdata->parm.ctl_used[i]){
  268.                                 myp2cstrcpy(s,gdata->parm.ctl[i]);
  269.                                 SetDlgItemText(dp, FIRSTCTLLABELITEM+i,s);
  270.                                 HideDialogItem(dp, FIRSTMAPLABELITEM + i/2);
  271.                         }else{
  272.                                 HideDialogItem(dp, FIRSTCTLITEM+i);
  273.                                 HideDialogItem(dp, FIRSTCTLTEXTITEM+i);
  274.                                 HideDialogItem(dp, FIRSTCTLLABELITEM + i);
  275.                                 HideDialogItem(dp, FIRSTMAPLABELITEM + i/2);
  276.                         }
  277.                 }
  278.         }
  279.  
  280.         strcpy(s,"X =");
  281.         for(i = 0; i < 4; ++i){
  282.                 if(i >= nplanes){
  283.                         HideDialogItem(dp,FIRSTICONITEM+i);
  284.                         HideDialogItem(dp,FIRSTEXPRITEM+i);
  285.                         HideDialogItem(dp,FIRSTLABELITEM+i);
  286.                 }else{
  287.                         s[0] = channelsuffixes[gpb->imageMode][i];
  288.                         SetDlgItemText(dp,FIRSTLABELITEM+i,s);
  289.                 }
  290.         }
  291.  
  292.         if(setup_preview(gpb,nplanes)){
  293.                 // On very large images, processing a fully zoomed out preview (the initial default)
  294.                 // can cause out of memory errors, because Photoshop can't page in all data
  295.                 // during advanceState. To prevent this problem, zoom in until we aren't
  296.                 // previewing more than say 10% of Photoshop's indicated maxSpace.
  297.                 // (e.g., on a 1GB WinXP system, PS CS2 reports 520MB maxSpace, so this will let us
  298.                 // preview about 50MB of image data.)
  299.  
  300.                 /* Workaround: GIMP/PSPI sets maxSpace to 100 MB hardcoded, so the zoom is not adjusted correctly. */
  301.                 int disable_zoom_memory_check;
  302.                 disable_zoom_memory_check = !maxspace_available();
  303.  
  304.                 if (!disable_zoom_memory_check) {
  305.                         zoomfactor = sqrt(maxspace()/(10.*preview_w*preview_h*nplanes));
  306.                         if(zoomfactor > fitzoom)
  307.                                 zoomfactor = fitzoom;
  308.                         if(zoomfactor < 1.)
  309.                                 zoomfactor = 1.;
  310.                 } else {
  311.                         zoomfactor = fitzoom;
  312.                 }
  313.  
  314.                 updatezoom(dp);
  315.         }else{
  316.                 HideDialogItem(dp,ZOOMINITEM);
  317.                 HideDialogItem(dp,ZOOMOUTITEM);
  318.                 HideDialogItem(dp,ZOOMLEVELITEM);
  319.         }
  320.  
  321.         updatedialog(dp);
  322.         maindlgupdate(dp);
  323. }
  324.  
  325.  
  326. /* process an item hit. return false if the dialog is finished; otherwise return true. */
  327.  
  328. Boolean maindlgitem(DIALOGREF dp,int item){
  329.         extern int previewerr;
  330.  
  331.         StandardFileReply sfr;
  332.         NavReplyRecord reply;
  333.         static OSType types[] = {TEXT_FILETYPE,PS_FILTER_FILETYPE};
  334.         char *reason;
  335.         HINSTANCE hShellRes;
  336.         struct InternalState bakState;
  337.  
  338.         switch(item){
  339. #ifdef MAC_ENV
  340.         case ok:
  341.         case cancel:
  342. #else
  343.         case IDOK:
  344.         case IDCANCEL:
  345. #endif
  346.                 dispose_preview();
  347.                 return false; // end dialog
  348.         case OPENITEM:
  349.                 if(!gdata->standalone && choosefiletypes(
  350.                         #ifdef MAC_ENV
  351.                         (StringPtr)_strdup("\pChoose filter settings"), // "\p" means "Pascal string"
  352.                         #else
  353.                         (StringPtr)_strdup("\026Choose filter settings"),
  354.                         #endif
  355.                         &sfr,&reply,types,2,
  356.                         "All supported files (*.afs, *.8bf, *.pff, *.prm, *.bin, *.txt; *.ffx)\0*.afs;*.8bf;*.pff;*.prm;*.bin;*.txt;*.ffx\0Filter Factory Settings (*.afs, *.txt)\0*.afs;*.txt\0PluginCommander TXT file (*.txt)\0*.txt\0Filter Factory for Windows, Standalone Filter (*.8bf)\0*.8bf\0Premiere TF/FF Settings (*.pff)\0*.pff\0Premiere TT/FF for Windows, Standalone Filter (*.prm)\0*.prm\0FilterFactory for MacOS, Standalone Filter (*.bin)\0*.bin\0\"Filters Unlimited\" filter (*.ffx)\0*.ffx\0All files (*.*)\0*.*\0\0"
  357.                         #ifdef _WIN32
  358.                         ,gdata->hWndMainDlg
  359.                         #endif /* _WIN32 */
  360.                 )){
  361.                         // Backup everything, because obfuscation et. al. causes changing of the state (yes, that's messy :( )
  362.                         bakState = saveInternalState();
  363.  
  364.                         if(loadfile(&sfr,&reason)){
  365.                                 updatedialog(dp);
  366.                                 maindlgupdate(dp);
  367.                         }
  368.                         else {
  369.                                 alertuser(_strdup("Cannot load settings."), reason);
  370.  
  371.                                 // Restore
  372.                                 restoreInternalState(bakState);
  373.                         }
  374.                 }
  375.                 break;
  376.         case SAVEITEM:
  377.                 if(!gdata->standalone && putfile(
  378.                         #ifdef MAC_ENV
  379.                         (StringPtr)_strdup("\pSave filter settings"), // "\p" means "Pascal string"
  380.                         #else
  381.                         (StringPtr)_strdup("\024Save filter settings"),
  382.                         #endif
  383.                         (StringPtr)_strdup("\0"),
  384.                         TEXT_FILETYPE,SIG_SIMPLETEXT,&reply,&sfr,
  385.                         "afs","All supported files (.afs, .txt, *.pff)\0*.afs;*.txt;*.pff\0Filter Factory Settings (*.afs, *.txt)\0*.afs;*.txt\0Premiere TF/FF Settings (*.pff)\0*.pff\0All files (*.*)\0*.*\0\0",1
  386.                         #ifdef _WIN32
  387.                         ,gdata->hWndMainDlg
  388.                         #endif /* _WIN32 */
  389.                 )){
  390.                         if(savefile_afs_pff(&sfr))
  391.                                 completesave(&reply);
  392.                 }
  393.                 break;
  394.         case MAKEITEM:
  395.                 if (gdata->standalone) return true; // should not happen since the button should be grayed out
  396.  
  397.                 // Backup everything, because obfuscation et. al. causes changing of the state (yes, that's messy :( )
  398.                 bakState = saveInternalState();
  399.  
  400.                 builddialog(gpb);
  401.  
  402.                 // Restore
  403.                 restoreInternalState(bakState);
  404.  
  405.                 break;
  406.         case HELPITEM:
  407.                 #ifdef MAC_ENV
  408.                 // TODO: Open web-browser instead
  409.                 showmessage(_strdup("You can find the documentation here: https://github.com/danielmarschall/filter_foundry/tree/master/doc"));
  410.                 #else
  411.                 hShellRes = ShellExecuteA(
  412.                         gdata->hWndMainDlg,
  413.                         "open",
  414.                         "https://github.com/danielmarschall/filter_foundry/blob/master/doc/The%20Filter%20Foundry.pdf",
  415.                         NULL,
  416.                         NULL,
  417.                         SW_SHOWNORMAL
  418.                 );
  419.                 if (hShellRes == (HINSTANCE)ERROR_FILE_NOT_FOUND) {
  420.                         // On Win98 we get ERROR_FILE_NOT_FOUND, but the browser still opens!
  421.                         // So we ignore it for now...
  422.                 }
  423.                 else if (hShellRes <= (HINSTANCE)32) {
  424.                         // MSDN states: "If the function succeeds, it returns a value greater than 32."
  425.  
  426.                         char s[100];
  427.                         strcpy(s, "ShellExecuteA failed: ");
  428.                         FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, NULL, GetLastError(), 0, s + strlen(s), 0x100, NULL);
  429.                         dbg(s);
  430.  
  431.                         showmessage(_strdup("You can find the documentation here: https://github.com/danielmarschall/filter_foundry/tree/master/doc"));
  432.                 }
  433.                 #endif
  434.                 break;
  435.         case ZOOMINITEM:
  436.                 zoomfactor = zoomfactor > 2. ? zoomfactor/2. : 1.;
  437.                 updatezoom(dp);
  438.                 previewerr = false;
  439.                 recalc_preview(gpb,dp);
  440.                 break;
  441.         case ZOOMOUTITEM:
  442.                 zoomfactor *= 2.;
  443.                 if(zoomfactor > fitzoom)
  444.                         zoomfactor = fitzoom;
  445.                 updatezoom(dp);
  446.                 previewerr = false;
  447.                 recalc_preview(gpb,dp);
  448.                 break;
  449.         case ZOOMLEVELITEM:
  450.                 zoomfactor = zoomfactor > 1. ? 1. : (fitzoom < 1. ? 1. : fitzoom);
  451.                 updatezoom(dp);
  452.                 previewerr = false;
  453.                 recalc_preview(gpb,dp);
  454.                 break;
  455.         case FIRSTCTLITEM:
  456.         case FIRSTCTLITEM+1:
  457.         case FIRSTCTLITEM+2:
  458.         case FIRSTCTLITEM+3:
  459.         case FIRSTCTLITEM+4:
  460.         case FIRSTCTLITEM+5:
  461.         case FIRSTCTLITEM+6:
  462.         case FIRSTCTLITEM+7:
  463.                 slidermoved(dp,item);
  464.                 recalc_preview(gpb,dp);
  465.                 break;
  466.         case FIRSTCTLTEXTITEM:
  467.         case FIRSTCTLTEXTITEM+1:
  468.         case FIRSTCTLTEXTITEM+2:
  469.         case FIRSTCTLTEXTITEM+3:
  470.         case FIRSTCTLTEXTITEM+4:
  471.         case FIRSTCTLTEXTITEM+5:
  472.         case FIRSTCTLTEXTITEM+6:
  473.         case FIRSTCTLTEXTITEM+7:
  474.                 slidertextchanged(dp,item);
  475.                 recalc_preview(gpb,dp);
  476.                 break;
  477.         case FIRSTICONITEM:
  478.         case FIRSTICONITEM+1:
  479.         case FIRSTICONITEM+2:
  480.         case FIRSTICONITEM+3:
  481.                 item -= FIRSTICONITEM;
  482.                 alertuser(err[item],_strdup(""));
  483.                 SELECTCTLTEXT(dp,FIRSTEXPRITEM+item,errstart[item],errpos[item]);
  484.                 break;
  485.         case FIRSTEXPRITEM:
  486.         case FIRSTEXPRITEM+1:
  487.         case FIRSTEXPRITEM+2:
  488.         case FIRSTEXPRITEM+3:
  489.                 if((item-FIRSTEXPRITEM) < nplanes){
  490.                         updateexpr(dp,item);
  491.                         maindlgupdate(dp);
  492.                 }
  493.                 break;
  494.         }
  495.  
  496.         return true; // keep going
  497. }
  498.  
  499. Boolean alertuser(char *err,char *more){
  500.         char *s = (char*)malloc(strlen(err)+strlen(more)+2),*q;
  501.         Boolean res;
  502.  
  503.         q = cat(s,err);
  504.         *q++ = '\n';
  505.         q = cat(q,more);
  506.         *q = 0;
  507.         res = simplealert(s);
  508.         free(s);
  509.         return res;
  510. }
  511.