File: prefs.cpp

package info (click to toggle)
golly 3.3-1.1
  • links: PTS, VCS
  • area: main
  • in suites: bookworm, forky, sid, trixie
  • size: 20,176 kB
  • sloc: cpp: 72,638; ansic: 25,919; python: 7,921; sh: 4,245; objc: 3,721; java: 2,781; xml: 1,362; makefile: 530; javascript: 279; perl: 69
file content (600 lines) | stat: -rw-r--r-- 23,070 bytes parent folder | download | duplicates (3)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
// This file is part of Golly.
// See docs/License.html for the copyright notice.

#include "lifealgo.h"
#include "viewport.h"       // for MAX_MAG
#include "util.h"           // for linereader, lifeerrors

#include "utils.h"          // for gColor, SetColor, Warning, Fatal, Beep
#include "status.h"         // for DisplayMessage
#include "algos.h"          // for NumAlgos, algoinfo, etc
#include "layer.h"          // for currlayer
#include "prefs.h"

#ifdef ANDROID_GUI
    #include "jnicalls.h"   // for BeginProgress, etc
#endif

#ifdef WEB_GUI
    #include "webcalls.h"   // for BeginProgress, etc
#endif

#ifdef IOS_GUI
    #import "PatternViewController.h"   // for BeginProgress, etc
#endif

// -----------------------------------------------------------------------------

// Golly's preferences file is a simple text file.

const int PREFS_VERSION = 1;        // increment if necessary due to changes in syntax/semantics
int currversion = PREFS_VERSION;    // might be changed by prefs_version
const int PREF_LINE_SIZE = 5000;    // must be quite long for storing file paths

std::string supplieddir;            // path of parent directory for supplied help/patterns/rules
std::string helpdir;                // path of directory for supplied help
std::string patternsdir;            // path of directory for supplied patterns
std::string rulesdir;               // path of directory for supplied rules
std::string userdir;                // path of parent directory for user's rules/patterns/downloads
std::string userrules;              // path of directory for user's rules
std::string savedir;                // path of directory for user's saved patterns
std::string downloaddir;            // path of directory for user's downloaded files
std::string tempdir;                // path of directory for temporary data
std::string clipfile;               // path of temporary file for storing clipboard data
std::string prefsfile;              // path of file for storing user's preferences

// initialize exported preferences:

int debuglevel = 0;                 // for displaying debug info if > 0
int helpfontsize = 10;              // font size in help window
char initrule[256] = "B3/S23";      // initial rule
bool initautofit = false;           // initial autofit setting
bool inithyperspeed = false;        // initial hyperspeed setting
bool initshowhashinfo = false;      // initial showhashinfo setting
bool savexrle = true;               // save RLE file using XRLE format?
bool showtool = true;               // show tool bar?
bool showlayer = false;             // show layer bar?
bool showedit = true;               // show edit bar?
bool showallstates = false;         // show all cell states in edit bar?
bool showstatus = true;             // show status bar?
bool showexact = false;             // show exact numbers in status bar?
bool showtimeline = false;          // show timeline bar?
bool showtiming = false;            // show timing messages?
bool showgridlines = true;          // display grid lines?
bool showicons = false;             // display icons for cell states?
bool swapcolors = false;            // swap colors used for cell states?
bool allowundo = true;              // allow undo/redo?
bool allowbeep = true;              // okay to play beep sound?
bool restoreview = true;            // should reset/undo restore view?
int canchangerule = 1;              // if > 0 then paste can change rule if pattern is empty
int randomfill = 50;                // random fill percentage (1..100)
int opacity = 80;                   // percentage opacity of live cells in overlays (1..100)
int tileborder = 3;                 // thickness of tiled window borders
int mingridmag = 2;                 // minimum mag to draw grid lines
int boldspacing = 10;               // spacing of bold grid lines
bool showboldlines = true;          // show bold grid lines?
bool mathcoords = false;            // show Y values increasing upwards?
bool syncviews = false;             // synchronize viewports?
bool syncmodes = true;              // synchronize touch modes?
bool stacklayers = false;           // stack all layers?
bool tilelayers = false;            // tile all layers?
bool asktosave = true;              // ask to save changes?
int newmag = 5;                     // mag setting for new pattern
bool newremovesel = true;           // new pattern removes selection?
bool openremovesel = true;          // opening pattern removes selection?
int mindelay = 250;                 // minimum millisec delay
int maxdelay = 2000;                // maximum millisec delay
int maxhashmem = 100;               // maximum memory (in MB) for hashlife-based algos

int numpatterns = 0;                // current number of recent pattern files
int maxpatterns = 20;               // maximum number of recent pattern files
std::list<std::string> recentpatterns; // list of recent pattern files

gColor borderrgb;                   // color for border around bounded grid
gColor selectrgb;                   // color for selected cells
gColor pastergb;                    // color for pasted pattern

paste_mode pmode = Or;              // logical paste mode

// -----------------------------------------------------------------------------

const char* GetPasteMode()
{
    switch (pmode) {
        case And:   return "AND";
        case Copy:  return "COPY";
        case Or:    return "OR";
        case Xor:   return "XOR";
        default:    return "unknown";
    }
}

// -----------------------------------------------------------------------------

void SetPasteMode(const char* s)
{
    if (strcmp(s, "AND") == 0) {
        pmode = And;
    } else if (strcmp(s, "COPY") == 0) {
        pmode = Copy;
    } else if (strcmp(s, "OR") == 0) {
        pmode = Or;
    } else {
        pmode = Xor;
    }
}

// -----------------------------------------------------------------------------

void CreateDefaultColors()
{
    SetColor(borderrgb, 128, 128, 128);     // 50% gray
    SetColor(selectrgb,  75, 175,   0);     // dark green
    SetColor(pastergb,  255,   0,   0);     // red
}

// -----------------------------------------------------------------------------

void GetColor(const char* value, gColor& rgb)
{
    unsigned int r, g, b;
    sscanf(value, "%u,%u,%u", &r, &g, &b);
    SetColor(rgb, r, g, b);
}

// -----------------------------------------------------------------------------

void SaveColor(FILE* f, const char* name, const gColor rgb)
{
    fprintf(f, "%s=%u,%u,%u\n", name, rgb.r, rgb.g, rgb.b);
}

// -----------------------------------------------------------------------------

void SavePrefs()
{
    if (currlayer == NULL) {
        // should never happen but play safe
        Warning("Bug: currlayer is NULL!");
        return;
    }

    FILE* f = fopen(prefsfile.c_str(), "w");
    if (f == NULL) {
        Warning("Could not save preferences file!");
        return;
    }

    fprintf(f, "prefs_version=%d\n", PREFS_VERSION);
    fprintf(f, "debug_level=%d\n", debuglevel);
    fprintf(f, "help_font_size=%d (%d..%d)\n", helpfontsize, minfontsize, maxfontsize);
    fprintf(f, "allow_undo=%d\n", allowundo ? 1 : 0);
    fprintf(f, "allow_beep=%d\n", allowbeep ? 1 : 0);
    fprintf(f, "restore_view=%d\n", restoreview ? 1 : 0);
    fprintf(f, "paste_mode=%s\n", GetPasteMode());
    fprintf(f, "can_change_rule=%d (0..2)\n", canchangerule);
    fprintf(f, "random_fill=%d (1..100)\n", randomfill);
    fprintf(f, "min_delay=%d (0..%d millisecs)\n", mindelay, MAX_DELAY);
    fprintf(f, "max_delay=%d (0..%d millisecs)\n", maxdelay, MAX_DELAY);
    fprintf(f, "auto_fit=%d\n", currlayer->autofit ? 1 : 0);
    fprintf(f, "hyperspeed=%d\n", currlayer->hyperspeed ? 1 : 0);
    fprintf(f, "hash_info=%d\n", currlayer->showhashinfo ? 1 : 0);
    fprintf(f, "max_hash_mem=%d\n", maxhashmem);

    fputs("\n", f);

    fprintf(f, "init_algo=%s\n", GetAlgoName(currlayer->algtype));
    for (int i = 0; i < NumAlgos(); i++) {
        fputs("\n", f);
        fprintf(f, "algorithm=%s\n", GetAlgoName(i));
        fprintf(f, "base_step=%d\n", algoinfo[i]->defbase);
        SaveColor(f, "status_rgb", algoinfo[i]->statusrgb);
        SaveColor(f, "from_rgb", algoinfo[i]->fromrgb);
        SaveColor(f, "to_rgb", algoinfo[i]->torgb);
        fprintf(f, "use_gradient=%d\n", algoinfo[i]->gradient ? 1 : 0);
        fputs("colors=", f);
        for (int state = 0; state < algoinfo[i]->maxstates; state++) {
            // only write out state,r,g,b tuple if color is different to default
            if (algoinfo[i]->algor[state] != algoinfo[i]->defr[state] ||
                algoinfo[i]->algog[state] != algoinfo[i]->defg[state] ||
                algoinfo[i]->algob[state] != algoinfo[i]->defb[state] ) {
                fprintf(f, "%d,%d,%d,%d,", state, algoinfo[i]->algor[state],
                                                  algoinfo[i]->algog[state],
                                                  algoinfo[i]->algob[state]);
            }
        }
        fputs("\n", f);
    }

    fputs("\n", f);

    fprintf(f, "rule=%s\n", currlayer->algo->getrule());
    fprintf(f, "show_tool=%d\n", showtool ? 1 : 0);
    fprintf(f, "show_layer=%d\n", showlayer ? 1 : 0);
    fprintf(f, "show_edit=%d\n", showedit ? 1 : 0);
    fprintf(f, "show_states=%d\n", showallstates ? 1 : 0);
    fprintf(f, "show_status=%d\n", showstatus ? 1 : 0);
    fprintf(f, "show_exact=%d\n", showexact ? 1 : 0);
    fprintf(f, "show_timeline=%d\n", showtimeline ? 1 : 0);
    fprintf(f, "show_timing=%d\n", showtiming ? 1 : 0);
    fprintf(f, "grid_lines=%d\n", showgridlines ? 1 : 0);
    fprintf(f, "min_grid_mag=%d (2..%d)\n", mingridmag, MAX_MAG);
    fprintf(f, "bold_spacing=%d (2..%d)\n", boldspacing, MAX_SPACING);
    fprintf(f, "show_bold_lines=%d\n", showboldlines ? 1 : 0);
    fprintf(f, "math_coords=%d\n", mathcoords ? 1 : 0);

    fputs("\n", f);

    fprintf(f, "sync_views=%d\n", syncviews ? 1 : 0);
    fprintf(f, "sync_modes=%d\n", syncmodes ? 1 : 0);
    fprintf(f, "stack_layers=%d\n", stacklayers ? 1 : 0);
    fprintf(f, "tile_layers=%d\n", tilelayers ? 1 : 0);
    fprintf(f, "tile_border=%d (1..10)\n", tileborder);
    fprintf(f, "ask_to_save=%d\n", asktosave ? 1 : 0);

    fputs("\n", f);

    fprintf(f, "show_icons=%d\n", showicons ? 1 : 0);
    fprintf(f, "swap_colors=%d\n", swapcolors ? 1 : 0);
    fprintf(f, "opacity=%d (1..100)\n", opacity);
    SaveColor(f, "border_rgb", borderrgb);
    SaveColor(f, "select_rgb", selectrgb);
    SaveColor(f, "paste_rgb", pastergb);

    fputs("\n", f);

    fprintf(f, "new_mag=%d (0..%d)\n", newmag, MAX_MAG);
    fprintf(f, "new_remove_sel=%d\n", newremovesel ? 1 : 0);
    fprintf(f, "open_remove_sel=%d\n", openremovesel ? 1 : 0);
    fprintf(f, "save_xrle=%d\n", savexrle ? 1 : 0);
    fprintf(f, "max_patterns=%d (1..%d)\n", maxpatterns, MAX_RECENT);

    if (!recentpatterns.empty()) {
        fputs("\n", f);
        std::list<std::string>::iterator next = recentpatterns.begin();
        while (next != recentpatterns.end()) {
            std::string path = *next;
            fprintf(f, "recent_pattern=%s\n", path.c_str());
            next++;
        }
    }

    fclose(f);
}

// -----------------------------------------------------------------------------

bool GetKeywordAndValue(linereader& lr, char* line, char** keyword, char** value)
{
    // the linereader class handles all line endings (CR, CR+LF, LF)
    // and terminates line buffer with \0
    while ( lr.fgets(line, PREF_LINE_SIZE) != 0 ) {
        if ( line[0] == '#' || line[0] == 0 ) {
            // skip comment line or empty line
        } else {
            // line should have format keyword=value
            *keyword = line;
            *value = line;
            while ( **value != '=' && **value != 0 ) *value += 1;
            **value = 0;   // terminate keyword
            *value += 1;
            return true;
        }
    }
    return false;
}

// -----------------------------------------------------------------------------

char* ReplaceDeprecatedAlgo(char* algoname)
{
    if (strcmp(algoname, "RuleTable") == 0 ||
        strcmp(algoname, "RuleTree") == 0) {
        // RuleTable and RuleTree algos have been replaced by RuleLoader
        return (char*)"RuleLoader";
    } else {
        return algoname;
    }
}

// -----------------------------------------------------------------------------

// let gollybase code call Fatal, Warning, BeginProgress, etc

class my_errors : public lifeerrors
{
public:
    virtual void fatal(const char* s) {
        Fatal(s);
    }

    virtual void warning(const char* s) {
        Warning(s);
    }

    virtual void status(const char* s) {
        DisplayMessage(s);
    }

    virtual void beginprogress(const char* s) {
        BeginProgress(s);
        // init flag for isaborted() calls
        aborted = false;
    }

    virtual bool abortprogress(double f, const char* s) {
        return AbortProgress(f, s);
    }

    virtual void endprogress() {
        EndProgress();
    }

    virtual const char* getuserrules() {
        return (const char*) userrules.c_str();
    }

    virtual const char* getrulesdir() {
        return (const char*) rulesdir.c_str();
    }
};

static my_errors myerrhandler;    // create instance

// -----------------------------------------------------------------------------

void GetPrefs()
{
    int algoindex = -1;     // unknown algorithm

    // let gollybase code call Fatal, Warning, BeginProgress, etc
    lifeerrors::seterrorhandler(&myerrhandler);

    CreateDefaultColors();

    FILE* f = fopen(prefsfile.c_str(), "r");
    if (f == NULL) {
        // should only happen 1st time app is run
        return;
    }

    linereader reader(f);
    char line[PREF_LINE_SIZE];
    char* keyword;
    char* value;
    while ( GetKeywordAndValue(reader, line, &keyword, &value) ) {

        if (strcmp(keyword, "prefs_version") == 0) {
            sscanf(value, "%d", &currversion);

        } else if (strcmp(keyword, "debug_level") == 0) {
            sscanf(value, "%d", &debuglevel);

        } else if (strcmp(keyword, "help_font_size") == 0) {
            sscanf(value, "%d", &helpfontsize);
            if (helpfontsize < minfontsize) helpfontsize = minfontsize;
            if (helpfontsize > maxfontsize) helpfontsize = maxfontsize;

        } else if (strcmp(keyword, "allow_undo") == 0) {
            allowundo = value[0] == '1';

        } else if (strcmp(keyword, "allow_beep") == 0) {
            allowbeep = value[0] == '1';

        } else if (strcmp(keyword, "restore_view") == 0) {
            restoreview = value[0] == '1';

        } else if (strcmp(keyword, "paste_mode") == 0) {
            SetPasteMode(value);

        } else if (strcmp(keyword, "can_change_rule") == 0) {
            sscanf(value, "%d", &canchangerule);
            if (canchangerule < 0) canchangerule = 0;
            if (canchangerule > 2) canchangerule = 2;

        } else if (strcmp(keyword, "random_fill") == 0) {
            sscanf(value, "%d", &randomfill);
            if (randomfill < 1) randomfill = 1;
            if (randomfill > 100) randomfill = 100;

        } else if (strcmp(keyword, "algorithm") == 0) {
            if (strcmp(value, "RuleTable") == 0) {
                // use deprecated RuleTable settings for RuleLoader
                // (deprecated RuleTree settings will simply be ignored)
                value = (char*)"RuleLoader";
            }
            algoindex = -1;
            for (int i = 0; i < NumAlgos(); i++) {
                if (strcmp(value, GetAlgoName(i)) == 0) {
                    algoindex = i;
                    break;
                }
            }

        } else if (strcmp(keyword, "base_step") == 0) {
            if (algoindex >= 0 && algoindex < NumAlgos()) {
                int base;
                sscanf(value, "%d", &base);
                if (base < 2) base = 2;
                if (base > MAX_BASESTEP) base = MAX_BASESTEP;
                algoinfo[algoindex]->defbase = base;
            }

        } else if (strcmp(keyword, "status_rgb") == 0) {
            if (algoindex >= 0 && algoindex < NumAlgos())
                GetColor(value, algoinfo[algoindex]->statusrgb);

        } else if (strcmp(keyword, "from_rgb") == 0) {
            if (algoindex >= 0 && algoindex < NumAlgos())
                GetColor(value, algoinfo[algoindex]->fromrgb);

        } else if (strcmp(keyword, "to_rgb") == 0) {
            if (algoindex >= 0 && algoindex < NumAlgos())
                GetColor(value, algoinfo[algoindex]->torgb);

        } else if (strcmp(keyword, "use_gradient") == 0) {
            if (algoindex >= 0 && algoindex < NumAlgos())
                algoinfo[algoindex]->gradient = value[0] == '1';

        } else if (strcmp(keyword, "colors") == 0) {
            if (algoindex >= 0 && algoindex < NumAlgos()) {
                int state, r, g, b;
                while (sscanf(value, "%d,%d,%d,%d,", &state, &r, &g, &b) == 4) {
                    if (state >= 0 && state < algoinfo[algoindex]->maxstates) {
                        algoinfo[algoindex]->algor[state] = r;
                        algoinfo[algoindex]->algog[state] = g;
                        algoinfo[algoindex]->algob[state] = b;
                    }
                    while (*value != ',') value++; value++;
                    while (*value != ',') value++; value++;
                    while (*value != ',') value++; value++;
                    while (*value != ',') value++; value++;
                }
            }

        } else if (strcmp(keyword, "min_delay") == 0) {
            sscanf(value, "%d", &mindelay);
            if (mindelay < 0) mindelay = 0;
            if (mindelay > MAX_DELAY) mindelay = MAX_DELAY;

        } else if (strcmp(keyword, "max_delay") == 0) {
            sscanf(value, "%d", &maxdelay);
            if (maxdelay < 0) maxdelay = 0;
            if (maxdelay > MAX_DELAY) maxdelay = MAX_DELAY;

        } else if (strcmp(keyword, "auto_fit") == 0) {
            initautofit = value[0] == '1';

        } else if (strcmp(keyword, "init_algo") == 0) {
            value = ReplaceDeprecatedAlgo(value);
            int i = staticAlgoInfo::nameToIndex(value);
            if (i >= 0 && i < NumAlgos())
                initalgo = i;

        } else if (strcmp(keyword, "hyperspeed") == 0) {
            inithyperspeed = value[0] == '1';

        } else if (strcmp(keyword, "hash_info") == 0) {
            initshowhashinfo = value[0] == '1';

        } else if (strcmp(keyword, "max_hash_mem") == 0) {
            sscanf(value, "%d", &maxhashmem);
            if (maxhashmem < MIN_MEM_MB) maxhashmem = MIN_MEM_MB;
            if (maxhashmem > MAX_MEM_MB) maxhashmem = MAX_MEM_MB;

        } else if (strcmp(keyword, "rule") == 0) {
            strncpy(initrule, value, sizeof(initrule));

        } else if (strcmp(keyword, "show_tool") == 0) {
            showtool = value[0] == '1';

        } else if (strcmp(keyword, "show_layer") == 0) {
            showlayer = value[0] == '1';

        } else if (strcmp(keyword, "show_edit") == 0) {
            showedit = value[0] == '1';

        } else if (strcmp(keyword, "show_states") == 0) {
            showallstates = value[0] == '1';

        } else if (strcmp(keyword, "show_status") == 0) {
            showstatus = value[0] == '1';

        } else if (strcmp(keyword, "show_exact") == 0) {
            showexact = value[0] == '1';

        } else if (strcmp(keyword, "show_timeline") == 0) {
            showtimeline = value[0] == '1';

        } else if (strcmp(keyword, "show_timing") == 0) {
            showtiming = value[0] == '1';

        } else if (strcmp(keyword, "grid_lines") == 0) {
            showgridlines = value[0] == '1';

        } else if (strcmp(keyword, "min_grid_mag") == 0) {
            sscanf(value, "%d", &mingridmag);
            if (mingridmag < 2) mingridmag = 2;
            if (mingridmag > MAX_MAG) mingridmag = MAX_MAG;

        } else if (strcmp(keyword, "bold_spacing") == 0) {
            sscanf(value, "%d", &boldspacing);
            if (boldspacing < 2) boldspacing = 2;
            if (boldspacing > MAX_SPACING) boldspacing = MAX_SPACING;

        } else if (strcmp(keyword, "show_bold_lines") == 0) {
            showboldlines = value[0] == '1';

        } else if (strcmp(keyword, "math_coords") == 0) {
            mathcoords = value[0] == '1';

        } else if (strcmp(keyword, "sync_views") == 0) {
            syncviews = value[0] == '1';

        } else if (strcmp(keyword, "sync_modes") == 0) {
            syncmodes = value[0] == '1';

        } else if (strcmp(keyword, "stack_layers") == 0) {
            stacklayers = value[0] == '1';

        } else if (strcmp(keyword, "tile_layers") == 0) {
            tilelayers = value[0] == '1';

        } else if (strcmp(keyword, "tile_border") == 0) {
            sscanf(value, "%d", &tileborder);
            if (tileborder < 1) tileborder = 1;
            if (tileborder > 10) tileborder = 10;

        } else if (strcmp(keyword, "ask_to_save") == 0) {
            asktosave = value[0] == '1';

        } else if (strcmp(keyword, "show_icons") == 0) {
            showicons = value[0] == '1';

        } else if (strcmp(keyword, "swap_colors") == 0) {
            swapcolors = value[0] == '1';

        } else if (strcmp(keyword, "opacity") == 0) {
            sscanf(value, "%d", &opacity);
            if (opacity < 1) opacity = 1;
            if (opacity > 100) opacity = 100;

        } else if (strcmp(keyword, "border_rgb") == 0) { GetColor(value, borderrgb);
        } else if (strcmp(keyword, "select_rgb") == 0) { GetColor(value, selectrgb);
        } else if (strcmp(keyword, "paste_rgb") == 0)  { GetColor(value, pastergb);

        } else if (strcmp(keyword, "new_mag") == 0) {
            sscanf(value, "%d", &newmag);
            if (newmag < 0) newmag = 0;
            if (newmag > MAX_MAG) newmag = MAX_MAG;

        } else if (strcmp(keyword, "new_remove_sel") == 0) {
            newremovesel = value[0] == '1';

        } else if (strcmp(keyword, "open_remove_sel") == 0) {
            openremovesel = value[0] == '1';

        } else if (strcmp(keyword, "save_xrle") == 0) {
            savexrle = value[0] == '1';

        } else if (strcmp(keyword, "max_patterns") == 0) {
            sscanf(value, "%d", &maxpatterns);
            if (maxpatterns < 1) maxpatterns = 1;
            if (maxpatterns > MAX_RECENT) maxpatterns = MAX_RECENT;

        } else if (strcmp(keyword, "recent_pattern") == 0) {
            if (numpatterns < maxpatterns && value[0]) {
                // append path to recentpatterns if file exists
                std::string path = value;
                if (path.find("Patterns/") == 0 || FileExists(userdir + path)) {
                    recentpatterns.push_back(path);
                    numpatterns++;
                }
            }
        }
    }
    reader.close();

    // stacklayers and tilelayers must not both be true
    if (stacklayers && tilelayers) tilelayers = false;
}