Subversion Repositories filter_foundry

Rev

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