Subversion Repositories filter_foundry

Rev

Rev 307 | Rev 315 | Go to most recent revision | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
259 daniel-mar 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);
305 daniel-mar 222
                                DISABLEDLGITEM(dp,HELPITEM);
259 daniel-mar 223
                        }
224
                        return;
225
                }
226
 
227
        /* we have valid expression trees in all slots...proceed! */
228
        updateglobals(dp);
229
        if(setup(gpb))
230
                recalc_preview(gpb,dp);
231
 
232
        ENABLEDLGITEM(dp,IDOK);
233
        if(!gdata->standalone){
234
                ENABLEDLGITEM(dp,SAVEITEM);
235
                ENABLEDLGITEM(dp,MAKEITEM);
305 daniel-mar 236
                ENABLEDLGITEM(dp,HELPITEM);
259 daniel-mar 237
        }
238
}
239
 
240
/* one-time initialisation of dialog box */
241
 
242
void maindlginit(DIALOGREF dp){
243
        char s[0x100];
244
        int i;
245
        const char *channelsuffixes[] = {
246
                "", "KA", "I", "RGBA",
247
                "CMYK", "HSL", "HSB", "1234",
248
                "DA", "LabA"
249
        };
250
 
251
        /* hide unused expression items */
252
        if(gdata->standalone){
253
                myp2cstrcpy(s,gdata->parm.author);
254
                SetDlgItemText(dp,PARAMAUTHORITEM,s);
255
                myp2cstrcpy(s,gdata->parm.copyright);
256
                SetDlgItemText(dp,PARAMCOPYITEM,s);
257
 
258
                // update labels for map() or ctl() sliders
259
                for(i = 0; i < 8; ++i){
260
                        if(gdata->parm.map_used[i/2]){
261
                                if((i&1) == 0){
262
                                        // even (0, 2, 4, 6)
263
                                        myp2cstrcpy(s,gdata->parm.map[i/2]);
264
                                        SetDlgItemText(dp, FIRSTMAPLABELITEM+(i/2),s);
265
                                        HideDialogItem(dp, FIRSTCTLLABELITEM + i);
266
                                        HideDialogItem(dp, FIRSTCTLLABELITEM + i + 1);
267
                                }
268
                        } else if(gdata->parm.ctl_used[i]){
269
                                myp2cstrcpy(s,gdata->parm.ctl[i]);
312 daniel-mar 270
                                SetDlgItemText(dp, FIRSTCTLLABELITEM+i,s);
259 daniel-mar 271
                                HideDialogItem(dp, FIRSTMAPLABELITEM + i/2);
272
                        }else{
312 daniel-mar 273
                                HideDialogItem(dp, FIRSTCTLITEM+i);
274
                                HideDialogItem(dp, FIRSTCTLTEXTITEM+i);
259 daniel-mar 275
                                HideDialogItem(dp, FIRSTCTLLABELITEM + i);
276
                                HideDialogItem(dp, FIRSTMAPLABELITEM + i/2);
277
                        }
278
                }
279
        }
280
 
281
        strcpy(s,"X =");
282
        for(i = 0; i < 4; ++i){
283
                if(i >= nplanes){
284
                        HideDialogItem(dp,FIRSTICONITEM+i);
285
                        HideDialogItem(dp,FIRSTEXPRITEM+i);
286
                        HideDialogItem(dp,FIRSTLABELITEM+i);
287
                }else{
288
                        s[0] = channelsuffixes[gpb->imageMode][i];
289
                        SetDlgItemText(dp,FIRSTLABELITEM+i,s);
290
                }
291
        }
292
 
293
        if(setup_preview(gpb,nplanes)){
294
                // On very large images, processing a fully zoomed out preview (the initial default)
295
                // can cause out of memory errors, because Photoshop can't page in all data
296
                // during advanceState. To prevent this problem, zoom in until we aren't
297
                // previewing more than say 10% of Photoshop's indicated maxSpace.
298
                // (e.g., on a 1GB WinXP system, PS CS2 reports 520MB maxSpace, so this will let us
299
                // preview about 50MB of image data.)
300
 
301
                /* Workaround: GIMP/PSPI sets maxSpace to 100 MB hardcoded, so the zoom is not adjusted correctly. */
302
                int disable_zoom_memory_check;
303
                disable_zoom_memory_check = !maxspace_available();
304
 
305
                if (!disable_zoom_memory_check) {
306
                        zoomfactor = sqrt(maxspace()/(10.*preview_w*preview_h*nplanes));
307
                        if(zoomfactor > fitzoom)
308
                                zoomfactor = fitzoom;
309
                        if(zoomfactor < 1.)
310
                                zoomfactor = 1.;
311
                } else {
312
                        zoomfactor = fitzoom;
313
                }
314
 
315
                updatezoom(dp);
316
        }else{
317
                HideDialogItem(dp,ZOOMINITEM);
318
                HideDialogItem(dp,ZOOMOUTITEM);
319
                HideDialogItem(dp,ZOOMLEVELITEM);
320
        }
321
 
322
        updatedialog(dp);
323
        maindlgupdate(dp);
324
}
325
 
326
 
327
/* process an item hit. return false if the dialog is finished; otherwise return true. */
328
 
329
Boolean maindlgitem(DIALOGREF dp,int item){
330
        extern int previewerr;
331
 
332
        StandardFileReply sfr;
333
        NavReplyRecord reply;
334
        static OSType types[] = {TEXT_FILETYPE,PS_FILTER_FILETYPE};
335
        char *reason;
336
        Str255 fname;
337
        Boolean bak_obfusc, bak_standalone, bak_parmloaded;
338
        PARM_T bak_parm;
339
 
340
        switch(item){
341
        case IDOK:
342
        case IDCANCEL:
343
                dispose_preview();
344
                return false; // end dialog
345
        case OPENITEM:
346
                if(!gdata->standalone && choosefiletypes(
271 daniel-mar 347
                        #ifdef MAC_ENV
348
                        (StringPtr)_strdup("\pChoose filter settings"), // "\p" means "Pascal string"
349
                        #else
350
                        (StringPtr)_strdup("\026Choose filter settings"),
351
                        #endif
352
                        &sfr,&reply,types,2,
353
                        "All supported files (*.afs, *.8bf, *.pff, *.prm, *.bin, *.txt)\0*.afs;*.8bf;*.pff;*.prm;*.bin;*.txt\0Filter Factory Settings (*.afs, *.txt)\0*.afs;*.txt\0Filter Factory for Windows, Standalone Filter (*.8bf)\0*.8bf\0Premiere TF/FF Settings (*.pff, *.txt)\0*.pff;*.txt\0Premiere TT/FF for Windows, Standalone Filter (*.prm)\0*.prm\0FilterFactory for MacOS, Standalone Filter (*.bin)\0*.bin\0All files (*.*)\0*.*\0\0"
354
                        #ifdef _WIN32
355
                        ,gdata->hWndMainDlg
356
                        #endif /* _WIN32 */
357
                )){
292 daniel-mar 358
                        bak_obfusc = gdata->obfusc;
359
                        bak_standalone = gdata->standalone;
360
                        bak_parmloaded = gdata->parmloaded;
361
                        memcpy(&bak_parm, &gdata->parm, sizeof(PARM_T));
362
 
259 daniel-mar 363
                        if(loadfile(&sfr,&reason)){
364
                                updatedialog(dp);
365
                                maindlgupdate(dp);
292 daniel-mar 366
                        }
367
                        else {
368
                                alertuser(_strdup("Cannot load settings."), reason);
369
 
370
                                gdata->obfusc = bak_obfusc;
371
                                gdata->standalone = bak_standalone;
372
                                gdata->parmloaded = bak_parmloaded;
373
                                memcpy(&gdata->parm, &bak_parm, sizeof(PARM_T));
374
                        }
259 daniel-mar 375
                }
376
                break;
377
        case SAVEITEM:
378
                if(!gdata->standalone && putfile(
271 daniel-mar 379
                        #ifdef MAC_ENV
380
                        (StringPtr)_strdup("\pSave filter settings"), // "\p" means "Pascal string"
381
                        #else
382
                        (StringPtr)_strdup("\024Save filter settings"),
383
                        #endif
384
                        (StringPtr)_strdup("\0"),
385
                        TEXT_FILETYPE,SIG_SIMPLETEXT,&reply,&sfr,
386
                        "afs","Settings file (.afs, .txt)\0*.afs;*.txt\0\0",1
387
                        #ifdef _WIN32
388
                        ,gdata->hWndMainDlg
389
                        #endif /* _WIN32 */
390
                )){
259 daniel-mar 391
                        if(savefile(&sfr))
392
                                completesave(&reply);
393
                }
394
                break;
395
        case MAKEITEM:
396
                bak_obfusc = gdata->obfusc;
397
                bak_standalone = gdata->standalone;
398
                bak_parmloaded = gdata->parmloaded;
399
                memcpy(&bak_parm,&gdata->parm,sizeof(PARM_T));
400
 
401
                if( !gdata->standalone && builddialog(gpb) ){
402
                        PLstrcpy(fname,gdata->parm.title);
268 daniel-mar 403
                        #ifdef MACMACHO
259 daniel-mar 404
                        PLstrcat(fname,(StringPtr)"\p.plugin"); // "\p" means "Pascal string"
268 daniel-mar 405
                        #endif
259 daniel-mar 406
                        if( putfile(
271 daniel-mar 407
                                #ifdef MAC_ENV
408
                                (StringPtr)_strdup("\pMake standalone filter"), // "\p" means "Pascal string"
409
                                #else
410
                                (StringPtr)_strdup("\026Make standalone filter"),
411
                                #endif
412
                                fname,
413
                                PS_FILTER_FILETYPE,kPhotoshopSignature,&reply,&sfr,
414
                                "8bf","Filter plugin file (.8bf)\0*.8bf\0\0",1
415
                                #ifdef _WIN32
416
                                ,gdata->hWndMainDlg
417
                                #endif /* _WIN32 */
418
                        )) {
259 daniel-mar 419
                                make_standalone(&sfr);
271 daniel-mar 420
                        }
259 daniel-mar 421
                }
422
 
423
                gdata->obfusc = bak_obfusc;
424
                gdata->standalone = bak_standalone;
425
                gdata->parmloaded = bak_parmloaded;
426
                memcpy(&gdata->parm, &bak_parm, sizeof(PARM_T));
427
 
428
                break;
305 daniel-mar 429
        case HELPITEM:
430
                #ifdef MAC_ENV
431
                // TODO: Open web-browser instead
312 daniel-mar 432
                simplealert(_strdup("You can find the documentation here: https://github.com/danielmarschall/filter_foundry/tree/master/doc"));
305 daniel-mar 433
                #else
312 daniel-mar 434
                if (ShellExecuteA(
305 daniel-mar 435
                        gdata->hWndMainDlg,
436
                        "open",
437
                        "https://github.com/danielmarschall/filter_foundry/blob/master/doc/The%20Filter%20Foundry.pdf",
312 daniel-mar 438
                        NULL,
439
                        NULL,
440
                        SW_SHOWNORMAL
441
                ) <= 32) {
442
                        // MSDN states: "If the function succeeds, it returns a value greater than 32."
443
                        simplealert(_strdup("You can find the documentation here: https://github.com/danielmarschall/filter_foundry/tree/master/doc"));
444
                }
305 daniel-mar 445
                #endif
446
                break;
259 daniel-mar 447
        case ZOOMINITEM:
448
                zoomfactor = zoomfactor > 2. ? zoomfactor/2. : 1.;
449
                updatezoom(dp);
450
                previewerr = false;
451
                recalc_preview(gpb,dp);
452
                break;
453
        case ZOOMOUTITEM:
454
                zoomfactor *= 2.;
455
                if(zoomfactor > fitzoom)
456
                        zoomfactor = fitzoom;
457
                updatezoom(dp);
458
                previewerr = false;
459
                recalc_preview(gpb,dp);
460
                break;
461
        case ZOOMLEVELITEM:
462
                zoomfactor = zoomfactor > 1. ? 1. : (fitzoom < 1. ? 1. : fitzoom);
463
                updatezoom(dp);
464
                previewerr = false;
465
                recalc_preview(gpb,dp);
466
                break;
467
        case FIRSTCTLITEM:
468
        case FIRSTCTLITEM+1:
469
        case FIRSTCTLITEM+2:
470
        case FIRSTCTLITEM+3:
471
        case FIRSTCTLITEM+4:
472
        case FIRSTCTLITEM+5:
473
        case FIRSTCTLITEM+6:
474
        case FIRSTCTLITEM+7:
475
                slidermoved(dp,item);
476
                recalc_preview(gpb,dp);
477
                break;
478
        case FIRSTCTLTEXTITEM:
479
        case FIRSTCTLTEXTITEM+1:
480
        case FIRSTCTLTEXTITEM+2:
481
        case FIRSTCTLTEXTITEM+3:
482
        case FIRSTCTLTEXTITEM+4:
483
        case FIRSTCTLTEXTITEM+5:
484
        case FIRSTCTLTEXTITEM+6:
485
        case FIRSTCTLTEXTITEM+7:
486
                slidertextchanged(dp,item);
487
                recalc_preview(gpb,dp);
488
                break;
489
        case FIRSTICONITEM:
490
        case FIRSTICONITEM+1:
491
        case FIRSTICONITEM+2:
492
        case FIRSTICONITEM+3:
493
                item -= FIRSTICONITEM;
494
                alertuser(err[item],_strdup(""));
495
                SELECTCTLTEXT(dp,FIRSTEXPRITEM+item,errstart[item],errpos[item]);
496
                break;
497
        case FIRSTEXPRITEM:
498
        case FIRSTEXPRITEM+1:
499
        case FIRSTEXPRITEM+2:
500
        case FIRSTEXPRITEM+3:
501
                if((item-FIRSTEXPRITEM) < nplanes){
502
                        updateexpr(dp,item);
503
                        maindlgupdate(dp);
504
                }
505
                break;
506
        }
507
 
508
        return true; // keep going
509
}
510
 
511
Boolean alertuser(char *err,char *more){
512
        char *s = (char*)malloc(strlen(err)+strlen(more)+2),*q;
513
        Boolean res;
514
 
515
        q = cat(s,err);
516
        *q++ = '\n';
517
        q = cat(q,more);
518
        *q = 0;
519
        res = simplealert(s);
520
        free(s);
521
        return res;
522
}