Subversion Repositories filter_foundry

Rev

Rev 492 | Rev 495 | 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.  
  71.         UNREFERENCED_PARAMETER(dp);
  72.  
  73.         // DM 28 Nov 2021: Removed this function. It makes no sense! The internal state is in the memory
  74.         // and the dialog is only the view!
  75.  
  76.         /*
  77.         int i;
  78.         char s[MAXEXPR+1];
  79.  
  80.         for(i = 0; i < 8; ++i)
  81.                 slider[i] = (value_type)(GETSLIDERVALUE(dp,FIRSTCTLITEM+i));
  82.  
  83.         if(!gdata->standalone)
  84.                 for(i = 0; i < 4; ++i){
  85.                         // stash expression strings
  86.                         if(GETCTLTEXT(dp,FIRSTEXPRITEM+i,s,MAXEXPR)){
  87.                                 if(expr[i])
  88.                                         free(expr[i]);
  89.                                 expr[i] = _strdup(s);
  90.                         }
  91.                         if(!expr[i])
  92.                                 expr[i] = _strdup("c");
  93.                 }
  94.         */
  95. }
  96.  
  97. struct node *updateexpr(DIALOGREF dp,int item){
  98.         char s[MAXEXPR+1];
  99.         int i;
  100.  
  101.         i = item - FIRSTEXPRITEM;
  102.  
  103.         freetree(tree[i]);
  104.  
  105.         if(!gdata->standalone){
  106.                 GETCTLTEXT(dp,item,s,MAXEXPR);
  107.  
  108.                 if(expr[i])
  109.                         free(expr[i]);
  110.                 expr[i] = _strdup(s);
  111.         }
  112.  
  113.         tree[i] = parseexpr(expr[i]);
  114.  
  115.         if(!gdata->standalone){
  116.                 if(tree[i])
  117.                         HideDialogItem(dp,FIRSTICONITEM+i);
  118.                 else{
  119.                         err[i] = errstr;
  120.                         errstart[i] = tokstart;
  121.                         errpos[i] = tokpos;
  122.                         ShowDialogItem(dp,FIRSTICONITEM+i);
  123.                 }
  124.         }
  125.         return tree[i];
  126. }
  127.  
  128. void updatezoom(DIALOGREF dp){
  129.         char s[10];
  130.         sprintf(s, "%d%%", (int)(100./zoomfactor));
  131.         SETCTLTEXT(dp,ZOOMLEVELITEM,s);
  132.         if (zoomfactor > 1.)
  133.                 ENABLEDLGITEM(dp, ZOOMINITEM);   // ShowDialogItem(dp,ZOOMINITEM);
  134.         else
  135.                 DISABLEDLGITEM(dp, ZOOMINITEM);  // HideDialogItem(dp, ZOOMINITEM);
  136.         if(zoomfactor < fitzoom)
  137.                 ENABLEDLGITEM(dp, ZOOMOUTITEM);  // ShowDialogItem(dp,ZOOMOUTITEM);
  138.         else
  139.                 DISABLEDLGITEM(dp, ZOOMOUTITEM); // HideDialogItem(dp,ZOOMOUTITEM);
  140. }
  141.  
  142. /* traverse expression tree, looking for constant references to sliders/maps */
  143.  
  144. static int _checksl(struct node*p,int ctlflags[],int mapflags[]){
  145.         int s, i, result;
  146.  
  147.         result = 0;
  148.         if(p){
  149.                 if( (p->kind==TOK_FN1 && p->v.sym->fn == (pfunc_type)ff_ctl)
  150.                  || (p->kind==TOK_FN3 && p->v.sym->fn == (pfunc_type)ff_val) ){
  151.                         if(p->child[0]->kind == TOK_NUM){
  152.                                 s = p->child[0]->v.value;
  153.                                 if(s>=0 && s<=7)
  154.                                         ctlflags[s] = 1;
  155.                         }else
  156.                                 result |= CHECKSLIDERS_CTL_AMBIGUOUS; /* can't determine which ctl() */
  157.                 }else if(p->kind==TOK_FN2 && p->v.sym->fn == (pfunc_type)ff_map){
  158.                         if(p->child[0]->kind == TOK_NUM){
  159.                                 s = p->child[0]->v.value;
  160.                                 if(s>=0 && s<=3){
  161.                                         mapflags[s] = 1;
  162.                                         ctlflags[s*2] = ctlflags[s*2+1] = 1;
  163.                                 }
  164.                         }else
  165.                                 result |= CHECKSLIDERS_MAP_AMBIGUOUS; /* can't determine which map() */
  166.                  }
  167.  
  168.                 for( i = 0 ; i < MAXCHILDREN ; ++i )
  169.                         result |= _checksl(p->child[i],ctlflags,mapflags);
  170.         }
  171.  
  172.         return result;
  173. }
  174.  
  175. int checksliders(int exprs,int ctlflags[],int mapflags[]){
  176.         int i, result;
  177.  
  178.         result = 0;
  179.  
  180.         for(i = 4; i--;)
  181.                 mapflags[i] = 0;
  182.         for(i = 8; i--;)
  183.                 ctlflags[i] = 0;
  184.  
  185.         for(i = 0; i < exprs; i++)
  186.                 result |= _checksl(tree[i],ctlflags,mapflags);
  187.  
  188.         return result;
  189. }
  190.  
  191. void slidermoved(DIALOGREF dp,int i){
  192.         int v = GETSLIDERVALUE(dp,i);
  193.         if (v < 0) v = 0;
  194.         else if (v > 255) v = 255;
  195.         i -= FIRSTCTLITEM;
  196.         slider[i] = (uint8_t)v;
  197.         SETCTLTEXTINT(dp,i+FIRSTCTLTEXTITEM,v,false);
  198. }
  199.  
  200. void slidertextchanged(DIALOGREF dp,int i){
  201.         int v = GETCTLTEXTINT(dp,i,NULL,false);
  202.         if (v < 0) v = 0;
  203.         else if (v > 255) v = 255;
  204.         i -= FIRSTCTLTEXTITEM;
  205.         SETSLIDERVALUE(dp,i+FIRSTCTLITEM,v);
  206.         slider[i] = (uint8_t)v;
  207. }
  208.  
  209. void maindlgupdate(DIALOGREF dp){
  210.         int i,unknown,ctls[8],maps[4];
  211.  
  212.         unknown = checksliders(nplanes,ctls,maps);
  213.  
  214.         for(i = 0; i < 8; i++)
  215.                 if(unknown || ctls[i]){
  216.                         ENABLEDLGITEM(dp,FIRSTCTLITEM+i); // TODO: slider is still shown as disabled
  217.                         REPAINTCTL(dp, FIRSTCTLITEM + i); // required for PLUGIN.DLL sliders
  218.                         ENABLEDLGITEM(dp,FIRSTCTLLABELITEM+i);
  219.                         ShowDialogItem(dp,FIRSTCTLTEXTITEM+i); /* FIXME: this changes keyboard focus */
  220.                 }else{
  221.                         DISABLEDLGITEM(dp,FIRSTCTLITEM+i);
  222.                         REPAINTCTL(dp,FIRSTCTLITEM+i); // required for PLUGIN.DLL sliders
  223.                         DISABLEDLGITEM(dp,FIRSTCTLLABELITEM+i);
  224.                         HideDialogItem(dp,FIRSTCTLTEXTITEM+i); /* FIXME: this changes keyboard focus */
  225.                 }
  226.  
  227.         for(i = 0; i < nplanes; i++)
  228.                 if(!tree[i]){
  229.                         /* uh oh, couldn't parse one of the saved expressions...this is fatal */
  230.                         DISABLEDLGITEM(dp,IDOK);
  231.                         if(gdata->standalone){
  232.                                 simplealert_id(MSG_SAVED_EXPR_ERR_ID);
  233.                         }else{
  234.                                 DISABLEDLGITEM(dp,SAVEITEM);
  235.                                 DISABLEDLGITEM(dp,MAKEITEM);
  236.                         }
  237.                         return;
  238.                 }
  239.  
  240.         /* we have valid expression trees in all slots...proceed! */
  241.         updateglobals(dp);
  242.         if(setup(gpb))
  243.                 recalc_preview(gpb,dp);
  244.  
  245.         ENABLEDLGITEM(dp,IDOK);
  246.         if(!gdata->standalone){
  247.                 ENABLEDLGITEM(dp,SAVEITEM);
  248.                 ENABLEDLGITEM(dp,MAKEITEM);
  249.                 ENABLEDLGITEM(dp,HELPITEM);
  250.         }
  251. }
  252.  
  253. /* one-time initialisation of dialog box */
  254.  
  255. void maindlginit(DIALOGREF dp){
  256.         char s[0x100];
  257.         int i;
  258.         const char *channelsuffixes[] = {
  259.                 "", "KA", "I", "RGBA",
  260.                 "CMYK", "HSL", "HSB", "1234",
  261.                 "DA", "LabA"
  262.         };
  263.  
  264.         /* hide unused expression items */
  265.         if(gdata->standalone){
  266.                 SetDlgItemTextA(dp,PARAMAUTHORITEM,gdata->parm.szAuthor);
  267.                 SetDlgItemTextA(dp,PARAMCOPYITEM,gdata->parm.szCopyright);
  268.  
  269.                 // update labels for map() or ctl() sliders
  270.                 for(i = 0; i < 8; ++i){
  271.                         if(gdata->parm.map_used[i/2]){
  272.                                 if((i&1) == 0){
  273.                                         // even (0, 2, 4, 6)
  274.                                         strcpy(s,gdata->parm.szMap[i/2]);
  275.                                         SetDlgItemTextA(dp, FIRSTMAPLABELITEM+(i/2),s);
  276.                                         HideDialogItem(dp, FIRSTCTLLABELITEM + i);
  277.                                         HideDialogItem(dp, FIRSTCTLLABELITEM + i + 1);
  278.                                 }
  279.                         } else if(gdata->parm.ctl_used[i]){
  280.                                 strcpy(s,gdata->parm.szCtl[i]);
  281.                                 SetDlgItemTextA(dp, FIRSTCTLLABELITEM+i,s);
  282.                                 HideDialogItem(dp, FIRSTMAPLABELITEM + i/2);
  283.                         }else{
  284.                                 HideDialogItem(dp, FIRSTCTLITEM+i);
  285.                                 HideDialogItem(dp, FIRSTCTLTEXTITEM+i);
  286.                                 HideDialogItem(dp, FIRSTCTLLABELITEM + i);
  287.                                 HideDialogItem(dp, FIRSTMAPLABELITEM + i/2);
  288.                         }
  289.                 }
  290.         }
  291.  
  292.         strcpy(s,"X =");
  293.         for(i = 0; i < 4; ++i){
  294.                 if(i >= nplanes){
  295.                         HideDialogItem(dp,FIRSTICONITEM+i);
  296.                         HideDialogItem(dp,FIRSTEXPRITEM+i);
  297.                         HideDialogItem(dp,FIRSTLABELITEM+i);
  298.                 }else{
  299.                         s[0] = channelsuffixes[gpb->imageMode][i];
  300.                         SetDlgItemTextA(dp,FIRSTLABELITEM+i,s);
  301.                 }
  302.         }
  303.  
  304.         if(setup_preview(gpb,nplanes)){
  305.                 // On very large images, processing a fully zoomed out preview (the initial default)
  306.                 // can cause out of memory errors, because Photoshop can't page in all data
  307.                 // during advanceState. To prevent this problem, zoom in until we aren't
  308.                 // previewing more than say 10% of Photoshop's indicated maxSpace.
  309.                 // (e.g., on a 1GB WinXP system, PS CS2 reports 520MB maxSpace, so this will let us
  310.                 // preview about 50MB of image data.)
  311.  
  312.                 /* Workaround: GIMP/PSPI sets maxSpace to 100 MB hardcoded, so the zoom is not adjusted correctly. */
  313.                 int disable_zoom_memory_check;
  314.                 disable_zoom_memory_check = !maxspace_available();
  315.  
  316.                 if (!disable_zoom_memory_check) {
  317.                         zoomfactor = sqrt(maxspace()/(10.*preview_w*preview_h*nplanes));
  318.                         if(zoomfactor > fitzoom)
  319.                                 zoomfactor = fitzoom;
  320.                         if(zoomfactor < 1.)
  321.                                 zoomfactor = 1.;
  322.                 } else {
  323.                         zoomfactor = fitzoom;
  324.                 }
  325.  
  326.                 updatezoom(dp);
  327.         }else{
  328.                 DISABLEDLGITEM(dp, ZOOMINITEM);    // HideDialogItem(dp,ZOOMINITEM);
  329.                 DISABLEDLGITEM(dp, ZOOMOUTITEM);   // HideDialogItem(dp,ZOOMOUTITEM);
  330.                 DISABLEDLGITEM(dp, ZOOMLEVELITEM); // HideDialogItem(dp,ZOOMLEVELITEM);
  331.         }
  332.  
  333.         updatedialog(dp);
  334.         maindlgupdate(dp);
  335. }
  336.  
  337.  
  338. /* process an item hit. return false if the dialog is finished; otherwise return true. */
  339.  
  340. Boolean maindlgitem(DIALOGREF dp,int item){
  341.         extern int previewerr;
  342.  
  343.         StandardFileReply sfr;
  344.         NavReplyRecord reply;
  345.         static OSType types[] = {TEXT_FILETYPE,PS_FILTER_FILETYPE};
  346.         TCHAR*reason;
  347.         HINSTANCE hShellRes;
  348.         InternalState bakState;
  349.  
  350.         switch(item){
  351. #ifdef MAC_ENV
  352.         case ok:
  353.         case cancel:
  354. #else
  355.         case IDOK:
  356.         case IDCANCEL:
  357. #endif
  358.                 dispose_preview();
  359.                 return false; // end dialog
  360.         case OPENITEM:
  361.         {
  362.                 TCHAR filters[3000];
  363.                 TCHAR *tmp1, *tmp2;
  364.                 size_t len;
  365.  
  366.                 memset(&filters[0], 0, sizeof(filters));
  367.                 tmp1 = &filters[0];
  368.  
  369.                 FF_GetMsg(tmp1, MSG_ALL_SUPPORTED_FILES_ID);
  370.                 tmp1 += xstrlen(tmp1);
  371.                 len = xstrlen(tmp2 = TEXT(" (*.afs, *.8bf, *.pff, *.prm, *.bin, *.rsrc, *.txt, *.ffx)"));
  372.                 memcpy(tmp1, tmp2, len * sizeof(TCHAR));
  373.                 tmp1 += (len + 1);
  374.                 len = xstrlen(tmp2 = TEXT("*.afs;*.8bf;*.pff;*.prm;*.bin;*.rsrc;*.txt;*.ffx"));
  375.                 memcpy(tmp1, tmp2, len * sizeof(TCHAR));
  376.                 tmp1 += (len + 1);
  377.  
  378.                 FF_GetMsg(tmp1, MSG_OPEN_AFS_ID);
  379.                 tmp1 += xstrlen(tmp1);
  380.                 len = xstrlen(tmp2 = TEXT(" (*.afs)"));
  381.                 memcpy(tmp1, tmp2, len * sizeof(TCHAR));
  382.                 tmp1 += (len + 1);
  383.                 len = xstrlen(tmp2 = TEXT("*.afs"));
  384.                 memcpy(tmp1, tmp2, len * sizeof(TCHAR));
  385.                 tmp1 += (len + 1);
  386.  
  387.                 FF_GetMsg(tmp1, MSG_OPEN_TXT_ID);
  388.                 tmp1 += xstrlen(tmp1);
  389.                 len = xstrlen(tmp2 = TEXT(" (*.txt)"));
  390.                 memcpy(tmp1, tmp2, len * sizeof(TCHAR));
  391.                 tmp1 += (len + 1);
  392.                 len = xstrlen(tmp2 = TEXT("*.txt"));
  393.                 memcpy(tmp1, tmp2, len * sizeof(TCHAR));
  394.                 tmp1 += (len + 1);
  395.  
  396.                 FF_GetMsg(tmp1, MSG_OPEN_8BF_ID);
  397.                 tmp1 += xstrlen(tmp1);
  398.                 len = xstrlen(tmp2 = TEXT(" (*.8bf)"));
  399.                 memcpy(tmp1, tmp2, len * sizeof(TCHAR));
  400.                 tmp1 += (len + 1);
  401.                 len = xstrlen(tmp2 = TEXT("*.8bf"));
  402.                 memcpy(tmp1, tmp2, len * sizeof(TCHAR));
  403.                 tmp1 += (len + 1);
  404.  
  405.                 FF_GetMsg(tmp1, MSG_OPEN_PFF_ID);
  406.                 tmp1 += xstrlen(tmp1);
  407.                 len = xstrlen(tmp2 = TEXT(" (*.pff)"));
  408.                 memcpy(tmp1, tmp2, len * sizeof(TCHAR));
  409.                 tmp1 += (len + 1);
  410.                 len = xstrlen(tmp2 = TEXT("*.pff"));
  411.                 memcpy(tmp1, tmp2, len * sizeof(TCHAR));
  412.                 tmp1 += (len + 1);
  413.  
  414.                 FF_GetMsg(tmp1, MSG_OPEN_PRM_ID);
  415.                 tmp1 += xstrlen(tmp1);
  416.                 len = xstrlen(tmp2 = TEXT(" (*.prm)"));
  417.                 memcpy(tmp1, tmp2, len * sizeof(TCHAR));
  418.                 tmp1 += (len + 1);
  419.                 len = xstrlen(tmp2 = TEXT("*.prm"));
  420.                 memcpy(tmp1, tmp2, len * sizeof(TCHAR));
  421.                 tmp1 += (len + 1);
  422.  
  423.                 FF_GetMsg(tmp1, MSG_OPEN_RSRC_ID);
  424.                 tmp1 += xstrlen(tmp1);
  425.                 len = xstrlen(tmp2 = TEXT(" (*.bin, *.rsrc)"));
  426.                 memcpy(tmp1, tmp2, len * sizeof(TCHAR));
  427.                 tmp1 += (len + 1);
  428.                 len = xstrlen(tmp2 = TEXT("*.bin;*.rsrc"));
  429.                 memcpy(tmp1, tmp2, len * sizeof(TCHAR));
  430.                 tmp1 += (len + 1);
  431.  
  432.                 FF_GetMsg(tmp1, MSG_OPEN_FFX_ID);
  433.                 tmp1 += xstrlen(tmp1);
  434.                 len = xstrlen(tmp2 = TEXT(" (*.ffx)"));
  435.                 memcpy(tmp1, tmp2, len * sizeof(TCHAR));
  436.                 tmp1 += (len + 1);
  437.                 len = xstrlen(tmp2 = TEXT("*.ffx"));
  438.                 memcpy(tmp1, tmp2, len * sizeof(TCHAR));
  439.                 tmp1 += (len + 1);
  440.  
  441.                 FF_GetMsg(tmp1, MSG_ALL_FILES_ID);
  442.                 tmp1 += xstrlen(tmp1);
  443.                 len = xstrlen(tmp2 = TEXT(" (*.*)"));
  444.                 memcpy(tmp1, tmp2, len * sizeof(TCHAR));
  445.                 tmp1 += (len + 1);
  446.                 len = xstrlen(tmp2 = TEXT("*.*"));
  447.                 memcpy(tmp1, tmp2, len * sizeof(TCHAR));
  448.                 tmp1 += (len + 1);
  449.  
  450.                 if (!gdata->standalone && choosefiletypes(
  451.                 #ifdef MAC_ENV
  452.                 (StringPtr)_strdup("\pChoose filter settings"), // "\p" means "Pascal string" // TODO (Not important yet): TRANSLATE
  453.                         &sfr, &reply, types, 2,
  454.                         &filters[0]
  455.                 #else
  456.                         FF_GetMsg_Cpy(MSG_LOAD_FILTER_SETTINGS_TITLE_ID),
  457.                         &sfr, &reply, types, 2,
  458.                         &filters[0], gdata->hWndMainDlg
  459.                 #endif
  460.                 )) {
  461.                         // Backup everything, otherwise we might lose parameter data if the loading fails
  462.                         bakState = saveInternalState();
  463.  
  464.                         if (loadfile(&sfr, &reason)) {
  465.                                 updatedialog(dp);
  466.                                 maindlgupdate(dp);
  467.                         }
  468.                         else {
  469.                                 alertuser_id(MSG_CANNOT_LOAD_SETTINGS_ID, reason);
  470.  
  471.                                 // Restore
  472.                                 restoreInternalState(bakState);
  473.                         }
  474.                 }
  475.                 break;
  476.         }
  477.         case SAVEITEM:
  478.         {
  479.                 TCHAR filters[3000];
  480.                 TCHAR* tmp1, * tmp2;
  481.                 size_t len;
  482.  
  483.                 memset(&filters[0], 0, sizeof(filters));
  484.                 tmp1 = &filters[0];
  485.  
  486.                 FF_GetMsg(tmp1, MSG_ALL_SUPPORTED_FILES_ID);
  487.                 tmp1 += xstrlen(tmp1);
  488.                 len = xstrlen(tmp2 = TEXT(" (*.afs, *.pff, *.txt)"));
  489.                 memcpy(tmp1, tmp2, len * sizeof(TCHAR));
  490.                 tmp1 += (len + 1);
  491.                 len = xstrlen(tmp2 = TEXT("*.afs;*.pff;*.txt"));
  492.                 memcpy(tmp1, tmp2, len * sizeof(TCHAR));
  493.                 tmp1 += (len + 1);
  494.  
  495.                 FF_GetMsg(tmp1, MSG_SAVE_AFS_ID);
  496.                 tmp1 += xstrlen(tmp1);
  497.                 len = xstrlen(tmp2 = TEXT(" (*.afs)"));
  498.                 memcpy(tmp1, tmp2, len * sizeof(TCHAR));
  499.                 tmp1 += (len + 1);
  500.                 len = xstrlen(tmp2 = TEXT("*.afs"));
  501.                 memcpy(tmp1, tmp2, len * sizeof(TCHAR));
  502.                 tmp1 += (len + 1);
  503.  
  504.                 FF_GetMsg(tmp1, MSG_SAVE_PFF_ID);
  505.                 tmp1 += xstrlen(tmp1);
  506.                 len = xstrlen(tmp2 = TEXT(" (*.pff)"));
  507.                 memcpy(tmp1, tmp2, len * sizeof(TCHAR));
  508.                 tmp1 += (len + 1);
  509.                 len = xstrlen(tmp2 = TEXT("*.pff"));
  510.                 memcpy(tmp1, tmp2, len * sizeof(TCHAR));
  511.                 tmp1 += (len + 1);
  512.  
  513.                 FF_GetMsg(tmp1, MSG_SAVE_TXT_ID);
  514.                 tmp1 += xstrlen(tmp1);
  515.                 len = xstrlen(tmp2 = TEXT(" (*.txt)"));
  516.                 memcpy(tmp1, tmp2, len * sizeof(TCHAR));
  517.                 tmp1 += (len + 1);
  518.                 len = xstrlen(tmp2 = TEXT("*.txt"));
  519.                 memcpy(tmp1, tmp2, len * sizeof(TCHAR));
  520.                 tmp1 += (len + 1);
  521.  
  522.                 FF_GetMsg(tmp1, MSG_ALL_FILES_ID);
  523.                 tmp1 += xstrlen(tmp1);
  524.                 len = xstrlen(tmp2 = TEXT(" (*.*)"));
  525.                 memcpy(tmp1, tmp2, len * sizeof(TCHAR));
  526.                 tmp1 += (len + 1);
  527.                 len = xstrlen(tmp2 = TEXT("*.*"));
  528.                 memcpy(tmp1, tmp2, len * sizeof(TCHAR));
  529.                 tmp1 += (len + 1);
  530.  
  531.                 if (!gdata->standalone && putfile(
  532.                 #ifdef MAC_ENV
  533.                 (StringPtr)_strdup("\pSave filter settings"), // "\p" means "Pascal string" // TODO (Not important yet): TRANSLATE
  534.                         (StringPtr)_strdup("\0"),
  535.                         TEXT_FILETYPE, SIG_SIMPLETEXT, &reply, &sfr,
  536.                         "afs",
  537.                         & filters[0], 1
  538.                 #else
  539.                         FF_GetMsg_Cpy(MSG_SAVE_FILTER_SETTINGS_TITLE_ID),
  540.                         TEXT("\0"),
  541.                         TEXT_FILETYPE, SIG_SIMPLETEXT, &reply, &sfr,
  542.                         TEXT("afs"),
  543.                         & filters[0], 1, gdata->hWndMainDlg
  544.                 #endif
  545.                 )) {
  546.                         if (savefile_afs_pff_picotxt(&sfr)) {
  547.                                 completesave(&reply);
  548.  
  549.                                 if (fileHasExtension(&sfr, TEXT(".txt"))) {
  550.                                         showmessage_id(MSG_PICO_SAVED_ID);
  551.  
  552.                                         #ifdef MAC_ENV
  553.                                         // TODO: Open text file instead
  554.                                         showmessage_id(MSG_PLEASE_EDIT_MANUALLY_ID);
  555.                                         #else
  556.                                         hShellRes = ShellExecute(
  557.                                                 gdata->hWndMainDlg,
  558.                                                 TEXT("open"),
  559.                                                 &sfr.sfFile.szName[0],
  560.                                                 NULL,
  561.                                                 NULL,
  562.                                                 SW_SHOWNORMAL
  563.                                         );
  564.                                         if (hShellRes <= (HINSTANCE)32) {
  565.                                                 // MSDN states: "If the function succeeds, it returns a value greater than 32."
  566.  
  567.                                                 TCHAR s[0x300];
  568.                                                 xstrcpy(s, (TCHAR*)TEXT("ShellExecute failed: ")); // TODO (Not so important): TRANSLATE
  569.                                                 FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, NULL, GetLastError(), 0, s + xstrlen(s), 0x300 - (DWORD)xstrlen(s), NULL);
  570.                                                 simplealert(&s[0]);
  571.  
  572.                                                 showmessage_id(MSG_PLEASE_EDIT_MANUALLY_ID);
  573.                                         }
  574.                                         #endif
  575.                                 }
  576.  
  577.                         }
  578.                 }
  579.                 break;
  580.         }
  581.         case MAKEITEM:
  582.                 if (gdata->standalone) return true; // should not happen since the button should be grayed out
  583.  
  584.                 builddialog(gpb);
  585.  
  586.                 break;
  587.         case HELPITEM:
  588.                 #ifdef MAC_ENV
  589.                 // TODO: Open web-browser instead
  590.                 showmessage_id(MSG_FIND_DOKU_HERE_ID);
  591.                 #else
  592.                 hShellRes = ShellExecute(
  593.                         gdata->hWndMainDlg,
  594.                         TEXT("open"),
  595.                         TEXT("https://github.com/danielmarschall/filter_foundry/blob/master/doc/The%20Filter%20Foundry.pdf"),
  596.                         NULL,
  597.                         NULL,
  598.                         SW_SHOWNORMAL
  599.                 );
  600.                 if (hShellRes == (HINSTANCE)ERROR_FILE_NOT_FOUND) {
  601.                         // On Win98 we get ERROR_FILE_NOT_FOUND, but the browser still opens!
  602.                         // So we ignore it for now...
  603.                 }
  604.                 else if (hShellRes <= (HINSTANCE)32) {
  605.                         // MSDN states: "If the function succeeds, it returns a value greater than 32."
  606.  
  607.                         TCHAR s[0x300];
  608.                         xstrcpy(s, (TCHAR*)TEXT("ShellExecute failed: ")); // TODO (Not so important): TRANSLATE
  609.                         FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, NULL, GetLastError(), 0, s + xstrlen(s), 0x300 - (DWORD)xstrlen(s), NULL);
  610.                         simplealert(&s[0]);
  611.  
  612.                         showmessage_id(MSG_FIND_DOKU_HERE_ID);
  613.                 }
  614.                 #endif
  615.                 break;
  616.         case ZOOMINITEM:
  617.                 zoomfactor = zoomfactor > 2. ? zoomfactor/2. : 1.;
  618.                 updatezoom(dp);
  619.                 previewerr = false;
  620.                 recalc_preview(gpb,dp);
  621.                 break;
  622.         case ZOOMOUTITEM:
  623.                 zoomfactor *= 2.;
  624.                 if(zoomfactor > fitzoom)
  625.                         zoomfactor = fitzoom;
  626.                 updatezoom(dp);
  627.                 previewerr = false;
  628.                 recalc_preview(gpb,dp);
  629.                 break;
  630.         case ZOOMLEVELITEM:
  631.                 zoomfactor = zoomfactor > 1. ? 1. : (fitzoom < 1. ? 1. : fitzoom);
  632.                 updatezoom(dp);
  633.                 previewerr = false;
  634.                 recalc_preview(gpb,dp);
  635.                 break;
  636.         case FIRSTCTLITEM:
  637.         case FIRSTCTLITEM+1:
  638.         case FIRSTCTLITEM+2:
  639.         case FIRSTCTLITEM+3:
  640.         case FIRSTCTLITEM+4:
  641.         case FIRSTCTLITEM+5:
  642.         case FIRSTCTLITEM+6:
  643.         case FIRSTCTLITEM+7:
  644.                 slidermoved(dp,item);
  645.                 recalc_preview(gpb,dp);
  646.                 break;
  647.         case FIRSTCTLTEXTITEM:
  648.         case FIRSTCTLTEXTITEM+1:
  649.         case FIRSTCTLTEXTITEM+2:
  650.         case FIRSTCTLTEXTITEM+3:
  651.         case FIRSTCTLTEXTITEM+4:
  652.         case FIRSTCTLTEXTITEM+5:
  653.         case FIRSTCTLTEXTITEM+6:
  654.         case FIRSTCTLTEXTITEM+7:
  655.                 slidertextchanged(dp,item);
  656.                 recalc_preview(gpb,dp);
  657.                 break;
  658.         case FIRSTICONITEM:
  659.         case FIRSTICONITEM+1:
  660.         case FIRSTICONITEM+2:
  661.         case FIRSTICONITEM+3:
  662.                 item -= FIRSTICONITEM;
  663.                 {
  664.                         simplealert(err[item]);
  665.                 }
  666.                 SELECTCTLTEXT(dp,FIRSTEXPRITEM+item,errstart[item],errpos[item]);
  667.                 break;
  668.         case FIRSTEXPRITEM:
  669.         case FIRSTEXPRITEM+1:
  670.         case FIRSTEXPRITEM+2:
  671.         case FIRSTEXPRITEM+3:
  672.                 if((item-FIRSTEXPRITEM) < nplanes){
  673.                         updateexpr(dp,item);
  674.                         maindlgupdate(dp);
  675.                 }
  676.                 break;
  677.         }
  678.  
  679.         return true; // keep going
  680. }
  681.  
  682. Boolean alertuser(TCHAR *err,TCHAR *more){
  683.         TCHAR *s, *q;
  684.         Boolean res;
  685.         size_t i;
  686.  
  687.         if (more == NULL) {
  688.                 return simplealert(err);
  689.         }
  690.  
  691.         s = (TCHAR*)malloc((xstrlen(err) + xstrlen(more) + 3) * sizeof(TCHAR)); // 3=CR+LF+NUL
  692.         if (s == NULL) return false;
  693.        
  694.         q = s;
  695.  
  696.         for (i = 0; i < xstrlen(err); i++) {
  697.                 *q++ = err[i];
  698.         }
  699.  
  700.         #ifdef WIN_ENV
  701.         *q++ = CR;
  702.         #endif
  703.         *q++ = LF;
  704.  
  705.         for (i = 0; i < xstrlen(more); i++) {
  706.                 *q++ = more[i];
  707.         }
  708.  
  709.         *q++ = 0;
  710.  
  711.         res = simplealert(s);
  712.         free(s);
  713.         return res;
  714. }
  715.  
  716. Boolean alertuser_id(int MsgId, TCHAR* more) {
  717.         TCHAR msg[1000];
  718.         FF_GetMsg(&msg[0], MsgId);
  719.         return alertuser(&msg[0], more);
  720. }
  721.  
  722. Boolean simplealert_id(int MsgId) {
  723.         TCHAR msg[1000];
  724.         FF_GetMsg(&msg[0], MsgId);
  725.         return simplealert(&msg[0]);
  726. }
  727.  
  728. Boolean simplewarning_id(int MsgId) {
  729.         TCHAR msg[1000];
  730.         FF_GetMsg(&msg[0], MsgId);
  731.         return simplewarning(&msg[0]);
  732. }
  733.  
  734. Boolean showmessage_id(int MsgId) {
  735.         TCHAR msg[1000];
  736.         FF_GetMsg(&msg[0], MsgId);
  737.         return showmessage(&msg[0]);
  738. }
  739.