Subversion Repositories filter_foundry

Rev

Rev 419 | Rev 430 | 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){
422 daniel-mar 70
 
71
        // DM 28 Nov 2021: Removed this function. It makes no sense! The internal state is in the memory
72
        // and the dialog is only the view!
73
 
74
        /*
259 daniel-mar 75
        int i;
76
        char s[MAXEXPR+1];
77
 
78
        for(i = 0; i < 8; ++i)
79
                slider[i] = (value_type)(GETSLIDERVALUE(dp,FIRSTCTLITEM+i));
80
 
81
        if(!gdata->standalone)
82
                for(i = 0; i < 4; ++i){
422 daniel-mar 83
                        // stash expression strings
259 daniel-mar 84
                        if(GETCTLTEXT(dp,FIRSTEXPRITEM+i,s,MAXEXPR)){
85
                                if(expr[i])
86
                                        free(expr[i]);
87
                                expr[i] = _strdup(s);
88
                        }
89
                        if(!expr[i])
90
                                expr[i] = _strdup("c");
91
                }
422 daniel-mar 92
        */
259 daniel-mar 93
}
94
 
95
struct node *updateexpr(DIALOGREF dp,int item){
96
        char s[MAXEXPR+1];
97
        int i;
98
 
99
        i = item - FIRSTEXPRITEM;
100
 
101
        freetree(tree[i]);
102
 
103
        if(!gdata->standalone){
104
                GETCTLTEXT(dp,item,s,MAXEXPR);
105
 
106
                if(expr[i])
107
                        free(expr[i]);
108
                expr[i] = _strdup(s);
109
        }
110
 
111
        tree[i] = parseexpr(expr[i]);
112
 
113
        if(!gdata->standalone){
114
                if(tree[i])
115
                        HideDialogItem(dp,FIRSTICONITEM+i);
116
                else{
117
                        err[i] = errstr;
118
                        errstart[i] = tokstart;
119
                        errpos[i] = tokpos;
120
                        ShowDialogItem(dp,FIRSTICONITEM+i);
121
                }
122
        }
123
        return tree[i];
124
}
125
 
126
void updatezoom(DIALOGREF dp){
127
        char s[10];
128
        sprintf(s, "%d%%", (int)(100./zoomfactor));
129
        SETCTLTEXT(dp,ZOOMLEVELITEM,s);
419 daniel-mar 130
        if (zoomfactor > 1.)
131
                ENABLEDLGITEM(dp, ZOOMINITEM);   // ShowDialogItem(dp,ZOOMINITEM);
259 daniel-mar 132
        else
419 daniel-mar 133
                DISABLEDLGITEM(dp, ZOOMINITEM);  // HideDialogItem(dp, ZOOMINITEM);
259 daniel-mar 134
        if(zoomfactor < fitzoom)
419 daniel-mar 135
                ENABLEDLGITEM(dp, ZOOMOUTITEM);  // ShowDialogItem(dp,ZOOMOUTITEM);
259 daniel-mar 136
        else
419 daniel-mar 137
                DISABLEDLGITEM(dp, ZOOMOUTITEM); // HideDialogItem(dp,ZOOMOUTITEM);
259 daniel-mar 138
}
139
 
140
/* traverse expression tree, looking for constant references to sliders/maps */
141
 
142
static int _checksl(struct node*p,int ctlflags[],int mapflags[]){
143
        int s, i, result;
144
 
145
        result = 0;
146
        if(p){
147
                if( (p->kind==TOK_FN1 && p->v.sym->fn == (pfunc_type)ff_ctl)
148
                 || (p->kind==TOK_FN3 && p->v.sym->fn == (pfunc_type)ff_val) ){
149
                        if(p->child[0]->kind == TOK_NUM){
150
                                s = p->child[0]->v.value;
151
                                if(s>=0 && s<=7)
152
                                        ctlflags[s] = 1;
153
                        }else
154
                                result |= CHECKSLIDERS_CTL_AMBIGUOUS; /* can't determine which ctl() */
155
                }else if(p->kind==TOK_FN2 && p->v.sym->fn == (pfunc_type)ff_map){
156
                        if(p->child[0]->kind == TOK_NUM){
157
                                s = p->child[0]->v.value;
158
                                if(s>=0 && s<=3){
159
                                        mapflags[s] = 1;
160
                                        ctlflags[s*2] = ctlflags[s*2+1] = 1;
161
                                }
162
                        }else
163
                                result |= CHECKSLIDERS_MAP_AMBIGUOUS; /* can't determine which map() */
164
                 }
165
 
166
                for( i = 0 ; i < MAXCHILDREN ; ++i )
167
                        result |= _checksl(p->child[i],ctlflags,mapflags);
168
        }
169
 
170
        return result;
171
}
172
 
173
int checksliders(int exprs,int ctlflags[],int mapflags[]){
174
        int i, result;
175
 
176
        result = 0;
177
 
178
        for(i = 4; i--;)
179
                mapflags[i] = 0;
180
        for(i = 8; i--;)
181
                ctlflags[i] = 0;
182
 
183
        for(i = 0; i < exprs; i++)
184
                result |= _checksl(tree[i],ctlflags,mapflags);
185
 
186
        return result;
187
}
188
 
189
void slidermoved(DIALOGREF dp,int i){
190
        int v = GETSLIDERVALUE(dp,i);
191
        i -= FIRSTCTLITEM;
192
        slider[i] = v;
193
        SETCTLTEXTINT(dp,i+FIRSTCTLTEXTITEM,v,false);
194
}
195
 
196
void slidertextchanged(DIALOGREF dp,int i){
197
        int v = GETCTLTEXTINT(dp,i,NULL,false);
198
        i -= FIRSTCTLTEXTITEM;
199
        SETSLIDERVALUE(dp,i+FIRSTCTLITEM,v);
200
        slider[i] = v;
201
}
202
 
203
void maindlgupdate(DIALOGREF dp){
204
        int i,unknown,ctls[8],maps[4];
205
 
206
        unknown = checksliders(nplanes,ctls,maps);
207
 
208
        for(i = 0; i < 8; i++)
209
                if(unknown || ctls[i]){
422 daniel-mar 210
                        ENABLEDLGITEM(dp,FIRSTCTLITEM+i); // TODO: slider is still shown as disabled
211
                        REPAINTCTL(dp, FIRSTCTLITEM + i); // required for PLUGIN.DLL sliders
259 daniel-mar 212
                        ENABLEDLGITEM(dp,FIRSTCTLLABELITEM+i);
213
                        ShowDialogItem(dp,FIRSTCTLTEXTITEM+i); /* FIXME: this changes keyboard focus */
214
                }else{
215
                        DISABLEDLGITEM(dp,FIRSTCTLITEM+i);
422 daniel-mar 216
                        REPAINTCTL(dp,FIRSTCTLITEM+i); // required for PLUGIN.DLL sliders
259 daniel-mar 217
                        DISABLEDLGITEM(dp,FIRSTCTLLABELITEM+i);
218
                        HideDialogItem(dp,FIRSTCTLTEXTITEM+i); /* FIXME: this changes keyboard focus */
219
                }
220
 
221
        for(i = 0; i < nplanes; i++)
222
                if(!tree[i]){
223
                        /* uh oh, couldn't parse one of the saved expressions...this is fatal */
224
                        DISABLEDLGITEM(dp,IDOK);
225
                        if(gdata->standalone){
226
                                alertuser(_strdup("Can't run this filter (there is a problem with the saved expressions)."),_strdup(""));
227
                        }else{
228
                                DISABLEDLGITEM(dp,SAVEITEM);
229
                                DISABLEDLGITEM(dp,MAKEITEM);
230
                        }
231
                        return;
232
                }
233
 
234
        /* we have valid expression trees in all slots...proceed! */
235
        updateglobals(dp);
236
        if(setup(gpb))
237
                recalc_preview(gpb,dp);
238
 
239
        ENABLEDLGITEM(dp,IDOK);
240
        if(!gdata->standalone){
241
                ENABLEDLGITEM(dp,SAVEITEM);
242
                ENABLEDLGITEM(dp,MAKEITEM);
305 daniel-mar 243
                ENABLEDLGITEM(dp,HELPITEM);
259 daniel-mar 244
        }
245
}
246
 
247
/* one-time initialisation of dialog box */
248
 
249
void maindlginit(DIALOGREF dp){
250
        char s[0x100];
251
        int i;
252
        const char *channelsuffixes[] = {
253
                "", "KA", "I", "RGBA",
254
                "CMYK", "HSL", "HSB", "1234",
255
                "DA", "LabA"
256
        };
257
 
258
        /* hide unused expression items */
259
        if(gdata->standalone){
393 daniel-mar 260
                SetDlgItemText(dp,PARAMAUTHORITEM,gdata->parm.szAuthor);
261
                SetDlgItemText(dp,PARAMCOPYITEM,gdata->parm.szCopyright);
259 daniel-mar 262
 
263
                // update labels for map() or ctl() sliders
264
                for(i = 0; i < 8; ++i){
265
                        if(gdata->parm.map_used[i/2]){
266
                                if((i&1) == 0){
267
                                        // even (0, 2, 4, 6)
393 daniel-mar 268
                                        strcpy(s,gdata->parm.szMap[i/2]);
259 daniel-mar 269
                                        SetDlgItemText(dp, FIRSTMAPLABELITEM+(i/2),s);
270
                                        HideDialogItem(dp, FIRSTCTLLABELITEM + i);
271
                                        HideDialogItem(dp, FIRSTCTLLABELITEM + i + 1);
272
                                }
273
                        } else if(gdata->parm.ctl_used[i]){
393 daniel-mar 274
                                strcpy(s,gdata->parm.szCtl[i]);
312 daniel-mar 275
                                SetDlgItemText(dp, FIRSTCTLLABELITEM+i,s);
259 daniel-mar 276
                                HideDialogItem(dp, FIRSTMAPLABELITEM + i/2);
277
                        }else{
312 daniel-mar 278
                                HideDialogItem(dp, FIRSTCTLITEM+i);
279
                                HideDialogItem(dp, FIRSTCTLTEXTITEM+i);
259 daniel-mar 280
                                HideDialogItem(dp, FIRSTCTLLABELITEM + i);
281
                                HideDialogItem(dp, FIRSTMAPLABELITEM + i/2);
282
                        }
283
                }
284
        }
285
 
286
        strcpy(s,"X =");
287
        for(i = 0; i < 4; ++i){
288
                if(i >= nplanes){
289
                        HideDialogItem(dp,FIRSTICONITEM+i);
290
                        HideDialogItem(dp,FIRSTEXPRITEM+i);
291
                        HideDialogItem(dp,FIRSTLABELITEM+i);
292
                }else{
293
                        s[0] = channelsuffixes[gpb->imageMode][i];
294
                        SetDlgItemText(dp,FIRSTLABELITEM+i,s);
295
                }
296
        }
297
 
298
        if(setup_preview(gpb,nplanes)){
299
                // On very large images, processing a fully zoomed out preview (the initial default)
300
                // can cause out of memory errors, because Photoshop can't page in all data
301
                // during advanceState. To prevent this problem, zoom in until we aren't
302
                // previewing more than say 10% of Photoshop's indicated maxSpace.
303
                // (e.g., on a 1GB WinXP system, PS CS2 reports 520MB maxSpace, so this will let us
304
                // preview about 50MB of image data.)
305
 
306
                /* Workaround: GIMP/PSPI sets maxSpace to 100 MB hardcoded, so the zoom is not adjusted correctly. */
307
                int disable_zoom_memory_check;
308
                disable_zoom_memory_check = !maxspace_available();
309
 
310
                if (!disable_zoom_memory_check) {
311
                        zoomfactor = sqrt(maxspace()/(10.*preview_w*preview_h*nplanes));
312
                        if(zoomfactor > fitzoom)
313
                                zoomfactor = fitzoom;
314
                        if(zoomfactor < 1.)
315
                                zoomfactor = 1.;
316
                } else {
317
                        zoomfactor = fitzoom;
318
                }
319
 
320
                updatezoom(dp);
321
        }else{
419 daniel-mar 322
                DISABLEDLGITEM(dp, ZOOMINITEM);    // HideDialogItem(dp,ZOOMINITEM);
323
                DISABLEDLGITEM(dp, ZOOMOUTITEM);   // HideDialogItem(dp,ZOOMOUTITEM);
324
                DISABLEDLGITEM(dp, ZOOMLEVELITEM); // HideDialogItem(dp,ZOOMLEVELITEM);
259 daniel-mar 325
        }
326
 
327
        updatedialog(dp);
328
        maindlgupdate(dp);
329
}
330
 
331
 
332
/* process an item hit. return false if the dialog is finished; otherwise return true. */
333
 
334
Boolean maindlgitem(DIALOGREF dp,int item){
335
        extern int previewerr;
336
 
337
        StandardFileReply sfr;
338
        NavReplyRecord reply;
339
        static OSType types[] = {TEXT_FILETYPE,PS_FILTER_FILETYPE};
340
        char *reason;
323 daniel-mar 341
        HINSTANCE hShellRes;
379 daniel-mar 342
        struct InternalState bakState;
259 daniel-mar 343
 
344
        switch(item){
363 daniel-mar 345
#ifdef MAC_ENV
346
        case ok:
347
        case cancel:
348
#else
259 daniel-mar 349
        case IDOK:
350
        case IDCANCEL:
363 daniel-mar 351
#endif
259 daniel-mar 352
                dispose_preview();
353
                return false; // end dialog
354
        case OPENITEM:
355
                if(!gdata->standalone && choosefiletypes(
271 daniel-mar 356
                        #ifdef MAC_ENV
357
                        (StringPtr)_strdup("\pChoose filter settings"), // "\p" means "Pascal string"
358
                        #else
359
                        (StringPtr)_strdup("\026Choose filter settings"),
360
                        #endif
361
                        &sfr,&reply,types,2,
399 daniel-mar 362
                        "All supported files (*.afs, *.8bf, *.pff, *.prm, *.bin, *.rsrc, *.txt, *.ffx)\0*.afs;*.8bf;*.pff;*.prm;*.bin;*.rsrc;*.txt;*.ffx\0Filter Factory Settings (*.afs, *.txt)\0*.afs;*.txt\0PluginCommander or FFDecomp 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, *.rsrc)\0*.bin\0\"Filters Unlimited\" filter (*.ffx)\0*.ffx\0All files (*.*)\0*.*\0\0"
271 daniel-mar 363
                        #ifdef _WIN32
364
                        ,gdata->hWndMainDlg
365
                        #endif /* _WIN32 */
366
                )){
408 daniel-mar 367
                        // Backup everything, otherwise we might lose parameter data if the loading fails
379 daniel-mar 368
                        bakState = saveInternalState();
292 daniel-mar 369
 
408 daniel-mar 370
                        if (loadfile(&sfr,&reason)) {
259 daniel-mar 371
                                updatedialog(dp);
372
                                maindlgupdate(dp);
292 daniel-mar 373
                        }
374
                        else {
375
                                alertuser(_strdup("Cannot load settings."), reason);
376
 
377 daniel-mar 377
                                // Restore
379 daniel-mar 378
                                restoreInternalState(bakState);
292 daniel-mar 379
                        }
259 daniel-mar 380
                }
381
                break;
382
        case SAVEITEM:
383
                if(!gdata->standalone && putfile(
271 daniel-mar 384
                        #ifdef MAC_ENV
385
                        (StringPtr)_strdup("\pSave filter settings"), // "\p" means "Pascal string"
386
                        #else
387
                        (StringPtr)_strdup("\024Save filter settings"),
388
                        #endif
389
                        (StringPtr)_strdup("\0"),
390
                        TEXT_FILETYPE,SIG_SIMPLETEXT,&reply,&sfr,
389 daniel-mar 391
                        "afs","All supported files (.afs, *.pff, .txt)\0*.afs;*.pff;*.txt\0Filter Factory Settings (*.afs)\0*.afs\0Premiere TF/FF Settings (*.pff)\0*.pff\0PluginCommander TXT file (*.txt)\0*.txt\0All files (*.*)\0*.*\0\0",1
271 daniel-mar 392
                        #ifdef _WIN32
393
                        ,gdata->hWndMainDlg
394
                        #endif /* _WIN32 */
395
                )){
389 daniel-mar 396
                        if(savefile_afs_pff_picotxt(&sfr)) {
259 daniel-mar 397
                                completesave(&reply);
389 daniel-mar 398
 
408 daniel-mar 399
                                if (fileHasExtension(&sfr, ".txt")) {
400
                                        char filename[MAX_PATH];
389 daniel-mar 401
 
408 daniel-mar 402
                                        showmessage(_strdup("The file was successfully saved in the \"PluginCommander\" TXT format. The file will now be opened in a text editor, so that you can fill in the missing data: Category, Title, Copyright, Author, Filename, Slider/Map names."));
403
 
389 daniel-mar 404
                                        #ifdef MAC_ENV
405
                                        // TODO: Open text file instead
406
                                        showmessage(_strdup("Please edit the file manually to enter the title, category, authorname, copyright, slidernames etc."));
407
                                        #else
408
                                        myp2cstrcpy(filename, sfr.sfFile.name);
409
                                        hShellRes = ShellExecuteA(
410
                                                gdata->hWndMainDlg,
411
                                                "open",
412
                                                filename,
413
                                                NULL,
414
                                                NULL,
415
                                                SW_SHOWNORMAL
416
                                        );
417
                                        if (hShellRes <= (HINSTANCE)32) {
418
                                                // MSDN states: "If the function succeeds, it returns a value greater than 32."
419
 
420
                                                char s[100];
421
                                                strcpy(s, "ShellExecuteA failed: ");
422
                                                FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, NULL, GetLastError(), 0, s + strlen(s), 0x100, NULL);
423
                                                dbg(s);
424
 
425
                                                showmessage(_strdup("Please edit the file manually to enter the title, category, author, copyright, filename, slidernames etc."));
426
                                        }
427
                                        #endif
428
                                }
429
 
430
                        }
259 daniel-mar 431
                }
432
                break;
433
        case MAKEITEM:
365 daniel-mar 434
                if (gdata->standalone) return true; // should not happen since the button should be grayed out
435
 
436
                builddialog(gpb);
259 daniel-mar 437
 
438
                break;
305 daniel-mar 439
        case HELPITEM:
440
                #ifdef MAC_ENV
441
                // TODO: Open web-browser instead
322 daniel-mar 442
                showmessage(_strdup("You can find the documentation here: https://github.com/danielmarschall/filter_foundry/tree/master/doc"));
305 daniel-mar 443
                #else
323 daniel-mar 444
                hShellRes = ShellExecuteA(
305 daniel-mar 445
                        gdata->hWndMainDlg,
446
                        "open",
447
                        "https://github.com/danielmarschall/filter_foundry/blob/master/doc/The%20Filter%20Foundry.pdf",
312 daniel-mar 448
                        NULL,
449
                        NULL,
450
                        SW_SHOWNORMAL
322 daniel-mar 451
                );
323 daniel-mar 452
                if (hShellRes == (HINSTANCE)ERROR_FILE_NOT_FOUND) {
322 daniel-mar 453
                        // On Win98 we get ERROR_FILE_NOT_FOUND, but the browser still opens!
454
                        // So we ignore it for now...
455
                }
323 daniel-mar 456
                else if (hShellRes <= (HINSTANCE)32) {
312 daniel-mar 457
                        // MSDN states: "If the function succeeds, it returns a value greater than 32."
322 daniel-mar 458
 
459
                        char s[100];
460
                        strcpy(s, "ShellExecuteA failed: ");
461
                        FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, NULL, GetLastError(), 0, s + strlen(s), 0x100, NULL);
462
                        dbg(s);
463
 
464
                        showmessage(_strdup("You can find the documentation here: https://github.com/danielmarschall/filter_foundry/tree/master/doc"));
312 daniel-mar 465
                }
305 daniel-mar 466
                #endif
467
                break;
259 daniel-mar 468
        case ZOOMINITEM:
469
                zoomfactor = zoomfactor > 2. ? zoomfactor/2. : 1.;
470
                updatezoom(dp);
471
                previewerr = false;
472
                recalc_preview(gpb,dp);
473
                break;
474
        case ZOOMOUTITEM:
475
                zoomfactor *= 2.;
476
                if(zoomfactor > fitzoom)
477
                        zoomfactor = fitzoom;
478
                updatezoom(dp);
479
                previewerr = false;
480
                recalc_preview(gpb,dp);
481
                break;
482
        case ZOOMLEVELITEM:
483
                zoomfactor = zoomfactor > 1. ? 1. : (fitzoom < 1. ? 1. : fitzoom);
484
                updatezoom(dp);
485
                previewerr = false;
486
                recalc_preview(gpb,dp);
487
                break;
488
        case FIRSTCTLITEM:
489
        case FIRSTCTLITEM+1:
490
        case FIRSTCTLITEM+2:
491
        case FIRSTCTLITEM+3:
492
        case FIRSTCTLITEM+4:
493
        case FIRSTCTLITEM+5:
494
        case FIRSTCTLITEM+6:
495
        case FIRSTCTLITEM+7:
496
                slidermoved(dp,item);
497
                recalc_preview(gpb,dp);
498
                break;
499
        case FIRSTCTLTEXTITEM:
500
        case FIRSTCTLTEXTITEM+1:
501
        case FIRSTCTLTEXTITEM+2:
502
        case FIRSTCTLTEXTITEM+3:
503
        case FIRSTCTLTEXTITEM+4:
504
        case FIRSTCTLTEXTITEM+5:
505
        case FIRSTCTLTEXTITEM+6:
506
        case FIRSTCTLTEXTITEM+7:
507
                slidertextchanged(dp,item);
508
                recalc_preview(gpb,dp);
509
                break;
510
        case FIRSTICONITEM:
511
        case FIRSTICONITEM+1:
512
        case FIRSTICONITEM+2:
513
        case FIRSTICONITEM+3:
514
                item -= FIRSTICONITEM;
515
                alertuser(err[item],_strdup(""));
516
                SELECTCTLTEXT(dp,FIRSTEXPRITEM+item,errstart[item],errpos[item]);
517
                break;
518
        case FIRSTEXPRITEM:
519
        case FIRSTEXPRITEM+1:
520
        case FIRSTEXPRITEM+2:
521
        case FIRSTEXPRITEM+3:
522
                if((item-FIRSTEXPRITEM) < nplanes){
523
                        updateexpr(dp,item);
524
                        maindlgupdate(dp);
525
                }
526
                break;
527
        }
528
 
529
        return true; // keep going
530
}
531
 
532
Boolean alertuser(char *err,char *more){
533
        char *s = (char*)malloc(strlen(err)+strlen(more)+2),*q;
534
        Boolean res;
535
 
536
        q = cat(s,err);
387 daniel-mar 537
        *q++ = LF;
259 daniel-mar 538
        q = cat(q,more);
539
        *q = 0;
540
        res = simplealert(s);
541
        free(s);
542
        return res;
543
}