summaryrefslogtreecommitdiff
path: root/app/bin/appdefaults.c
blob: 55a2201b424569dee5f0705acdeaf1d8cb120bbf (plain)
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
/** \file appdefaults.c
* Provide defaults, mostly for first run of the program.
*/

/*  XTrkCad - Model Railroad CAD
*  Copyright (C) 2017 Martin Fischer
*
*  This program is free software; you can redistribute it and/or modify
*  it under the terms of the GNU General Public License as published by
*  the Free Software Foundation; either version 2 of the License, or
*  (at your option) any later version.
*
*  This program is distributed in the hope that it will be useful,
*  but WITHOUT ANY WARRANTY; without even the implied warranty of
*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
*  GNU General Public License for more details.
*
*  You should have received a copy of the GNU General Public License
*  along with this program; if not, write to the Free Software
*  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/

#include <locale.h>
#include <stdbool.h>
#include <stdio.h>
#include <string.h>
#include <wchar.h>

#ifdef WINDOWS
#include <Windows.h>
#include <malloc.h>
#endif

#include "common.h"
#include "custom.h"
#include "fileio.h"
#include "paths.h"
#include "wlib.h"

enum defaultTypes {
    INTEGERCONSTANT,
    FLOATCONSTANT,
    STRINGCONSTANT,
    INTEGERFUNCTION,
    FLOATFUNCTION,
    STRINGFUNCTION
};

struct appDefault {
    char *defaultKey;						/**< the key used to access the value */
    bool wasUsed;							/**< value has already been used on this run */
    enum defaultTypes
    valueType;			/**< type of default, constant or pointer to a function */
    union {
        int 	intValue;
        double  floatValue;
        char    *stringValue;
        int		(*intFunction)(struct appDefault *, void *);
        double	(*floatFunction)(struct appDefault *, void *);		
        char	*(*stringFunction)(struct appDefault *, void *);
    } defaultValue;
    void *additionalData;
};

static int GetLocalMeasureSystem(struct appDefault *ptrDefault,
                                 void *additionalData);
static int GetLocalDistanceFormat(struct appDefault *ptrDefault,
                                  void *additionalData);
static char *GetLocalPopularScale(struct appDefault *ptrDefault,
                                  void *additionalData);
static double GetLocalRoomSize(struct appDefault *ptrDefault,
                               void *additionalData);
static char *GetParamFullPath(struct appDefault *ptrDefault,
                              void *additionalData);
static char *GetParamPrototype(struct appDefault *ptrDefault,
								void *additionalData);

/**
 * List of application default settings. As this is searched by binary search, the list has to be kept sorted
 * alphabetically for the key, the first element
 * Also the search is case sensitive on this field.
 */

struct appDefault xtcDefaults[] = {
	{ "DialogItem.cmdopt-preselect", 0, INTEGERCONSTANT,{ .intValue = 1 } },       /**< default command is select */
    { "DialogItem.cmdopt-rightclickmode", 0, INTEGERCONSTANT,{ .intValue = 1 } },	/**< swap default to context */
	{ "DialogItem.cmdopt-selectmode", 0, INTEGERCONSTANT,{ .intValue = 0 } },		/**< 'Only' mode               */
	{ "DialogItem.cmdopt-selectzero", 0, INTEGERCONSTANT,{ .intValue = 1 } },		/**< 'On' mode               */
	{ "DialogItem.grid-horzenable", 0, INTEGERCONSTANT, { .intValue = 0 }},
	{ "DialogItem.grid-vertenable", 0, INTEGERCONSTANT,{ .intValue = 0 } },
	{ "DialogItem.pref-dstfmt", 0, INTEGERFUNCTION,{ .intFunction = GetLocalDistanceFormat } },	/**< number format for distances */
    { "DialogItem.pref-units", 0, INTEGERFUNCTION,{ .intFunction = GetLocalMeasureSystem } },	/**< default unit depends on region */
	{ "DialogItem.rgbcolor-exception", 0, INTEGERCONSTANT, { .intValue = 15923462 }},		/**< rich yellow as exception color */
	{ "Parameter File Map.British stock", 0, STRINGFUNCTION,{ .stringFunction = GetParamFullPath }, "br.xtp" },
	{ "Parameter File Map.European stock", 0, STRINGFUNCTION,{ .stringFunction = GetParamFullPath }, "eu.xtp" },
	{ "Parameter File Map.NMRA RP12-25 Feb 2015 O scale Turnouts", 0, STRINGFUNCTION,{ .stringFunction = GetParamFullPath }, "nmra-o.xtp" },
    { "Parameter File Map.NMRA RP12-27 Feb 2015 S Scale Turnouts", 0, STRINGFUNCTION,{ .stringFunction = GetParamFullPath }, "nmra-s.xtp"  },
    { "Parameter File Map.NMRA RP12-31 Feb 2015 HO Scale Turnouts", 0, STRINGFUNCTION,{ .stringFunction = GetParamFullPath }, "nmra-ho.xtp" },
    { "Parameter File Map.NMRA RP12-33 Feb 2015 TT Scale Turnouts", 0, STRINGFUNCTION,{ .stringFunction = GetParamFullPath }, "nmra-tt.xtp" },
    { "Parameter File Map.NMRA RP12-35 Feb 2015 N Scale Turnouts", 0, STRINGFUNCTION,{ .stringFunction = GetParamFullPath }, "nmra-n.xtp" },
    { "Parameter File Map.NMRA RP12-37 Feb 2015 Z scale Turnouts", 0, STRINGFUNCTION,{ .stringFunction = GetParamFullPath }, "nmra-z.xtp" },
	{ "Parameter File Map.North American Prototypes", 0, STRINGFUNCTION,{ .stringFunction = GetParamFullPath }, "protoam.xtp" },
    { "Parameter File Map.Trees", 0, STRINGFUNCTION,{ .stringFunction = GetParamFullPath } , "trees.xtp" },
	{ "Parameter File Names.File1", 0, STRINGFUNCTION,{ .stringFunction = GetParamPrototype }},
	{ "Parameter File Names.File2", 0, STRINGCONSTANT,{ .stringValue = "Trees" } },
	{ "Parameter File Names.File3", 0, STRINGCONSTANT,{ .stringValue = "NMRA RP12-37 Feb 2015 Z scale Turnouts" } },
    { "Parameter File Names.File4", 0, STRINGCONSTANT,{ .stringValue = "NMRA RP12-35 Feb 2015 N Scale Turnouts" } },
    { "Parameter File Names.File5", 0, STRINGCONSTANT,{ .stringValue = "NMRA RP12-33 Feb 2015 TT Scale Turnouts" } },
    { "Parameter File Names.File6", 0, STRINGCONSTANT,{ .stringValue = "NMRA RP12-31 Feb 2015 HO Scale Turnouts" } },
    { "Parameter File Names.File7", 0, STRINGCONSTANT,{ .stringValue = "NMRA RP12-27 Feb 2015 S Scale Turnouts" } },
    { "Parameter File Names.File8", 0, STRINGCONSTANT,{ .stringValue = "NMRA RP12-25 Feb 2015 O scale Turnouts" } },
    { "draw.roomsizeX", 0, FLOATFUNCTION, {.floatFunction = GetLocalRoomSize }},				/**< layout width */
    { "draw.roomsizeY", 0, FLOATFUNCTION,{ .floatFunction = GetLocalRoomSize } },				/**< layout depth */
    { "misc.scale", 0, STRINGFUNCTION, { .stringFunction = GetLocalPopularScale}},				/**< the (probably) most popular scale for a region */
};

#define DEFAULTCOUNT (sizeof(xtcDefaults)/sizeof(xtcDefaults[0]))


static long bFirstRun;						/**< TRUE if appl is run the first time */
static char regionCode[3];					/**< will be initialized to the locale's region code */

static wBool_t(*GetIntegerPref)(const char *, const char *, long *, long) = wPrefGetIntegerExt;	/**< pointer to active integer pref getter */
static wBool_t(*GetFloatPref)(const char *, const char *, double *, double) = wPrefGetFloatExt; /**< pointer to active float pref getter */
static char *(*GetStringPref)(const char *, const char *) = wPrefGetStringExt;					/**< pointer to active string pref getter */

/**
 * A recursive binary search function. It returns location of x in
 * given array arr[l..r] is present, otherwise -1
 * Taken from http://www.geeksforgeeks.org/binary-search/ and modified
 *
 * \param arr IN array to search
 * \param l IN starting index
 * \param r IN highest index in array
 * \param key IN key to search
 * \return index if found, -1 otherwise
 */

static int binarySearch(struct appDefault arr[], int l, int r, char *key)
{
    if (r >= l) {
        int mid = l + (r - l) / 2;
        int res = strcmp(key, arr[mid].defaultKey);

        // If the element is present at the middle itself
        if (!res) {
            return mid;
        }

        // If the array size is 1
        if (r == 0) {
            return -1;
        }

        // If element is smaller than mid, then it can only be present
        // in left subarray
        if (res < 0) {
            return binarySearch(arr, l, mid - 1, key);
        }

        // Else the element can only be present in right subarray
        return binarySearch(arr, mid + 1, r, key);
    }

    // We reach here when element is not present in array
    return -1;
}

/**
 * Lookup default  for a value
 *
 * \param defaultValues IN array of all default values
 * \param section IN default's section
 * \param name IN default's name
 * \return pointer to default if found, NULL otherwise
 */
struct appDefault *
FindDefault(struct appDefault *defaultValues, const char *section,
            const char *name)
{
    char *searchString = malloc(strlen(section) + strlen(name) +
                                2); //includes separator and terminating \0
    int res;
    sprintf(searchString, "%s.%s", section, name);

    res = binarySearch(defaultValues, 0, DEFAULTCOUNT-1, searchString);
    free(searchString);

    if (res != -1 && defaultValues[res].wasUsed == FALSE) {
        defaultValues[res].wasUsed = TRUE;
        return (defaultValues + res);
    } else {
        return (NULL);
    }
}
/**
 * Get the application's default region code. On Windows, the system's API is used.
 * On *ix the environment variable LANG is supposed to contain a value in the
 * format ll_RR* where rr is the two character region code.
 */
static void
InitializeRegionCode(void)
{
    strcpy(regionCode, "US");

#ifdef WINDOWS
    {
        LCID lcid;
        char iso3166[10];

        lcid = GetThreadLocale();
        GetLocaleInfo(lcid, LOCALE_SISO3166CTRYNAME, iso3166, sizeof(iso3166));
        strncpy(regionCode, iso3166, 2);
    }
#else
    {
        char *pLang;
        pLang = getenv("LANG");

        if (pLang) {
            char *ptr;
            ptr = strpbrk(pLang, "_-");

            if (ptr) {
                strncpy(regionCode, ptr + 1, 2);
            }
        }
    }
#endif
}

/**
 * Use Metric measures everywhere except United States and Canada\
 */
static bool UseMetric()
{
	return ( strcmp( regionCode, "US" ) != 0 &&
		 strcmp( regionCode, "CA" ) != 0 );
}
/**
 * For the US the classical 4x8 sheet is used as default size. in the metric world 1,25x2,0m is used.
 */

static double
GetLocalRoomSize(struct appDefault *ptrDefault, void *data)
{
    if (!strcmp(ptrDefault->defaultKey, "draw.roomsizeY")) {
        return (UseMetric() ? 125.0/2.54 : 48);
    }

    if (!strcmp(ptrDefault->defaultKey, "draw.roomsizeX")) {
        return (UseMetric() ? 200.0 / 2.54 : 96);
    }

    return (0.0);		// should never get here
}

/**
 * The most popular scale is supposed to be HO except for UK where OO is assumed.
 */

static char *
GetLocalPopularScale(struct appDefault *ptrDefault, void *data)
{
    return (strcmp(regionCode, "GB") ? "HO" : "OO");
}

/**
 *	The measurement system is english for the US and Canada and metric elsewhere
 */
static int
GetLocalMeasureSystem(struct appDefault *ptrDefault, void *data)
{
    return (UseMetric() ? 1 : 0);
}

/**
*	The distance format is 999.9 cm for metric and 999.99 for english
*/
static int
GetLocalDistanceFormat(struct appDefault *ptrDefault, void *data)
{
    return (UseMetric() ? 8 : 4);
}

/**
* Prototype definitions currently only exist for US and British. So US 
* is assumed to be the default. 
*/

static char*
GetParamPrototype(struct appDefault *ptrDefault, void *additionalData)
{
	return (strcmp(regionCode, "GB") ? "North American Prototypes" : "British stock");
}

/**
 * The full path to the applications parameter directory
 */
static char *
GetParamFullPath(struct appDefault *ptrDefault, void *additionalData)
{
    char *str;
    MakeFullpath(&str, libDir, PARAM_SUBDIR, (char*)additionalData, (void *)0);
    return str;
}


/**
 * The following are three jump points for the correct implementation. Changing the funtion pointer
 * allows to switch from the extended default version to the basic implementation.
 */

wBool_t 
wPrefGetInteger(const char *section, const char *name, long *result, long defaultValue)
{
	return GetIntegerPref(section, name, result, defaultValue);
}

wBool_t
wPrefGetFloat(const char *section, const char *name, double *result, double defaultValue)
{
	return GetFloatPref(section, name, result, defaultValue);
}

char * 
wPrefGetString(const char *section, const char *name)
{
	return GetStringPref(section, name);
}

/**
 * Get an integer value from the configuration file. The is a wrapper for the real
 * file access and adds a region specific default value.
 *
 * \param section IN section in config file
 * \param name IN name in config file
 * \param result OUT pointer to result
 * \param defaultValue IN the default value to use if config is not found
 * \return returncode of wPrefGetIntegerBasic()
 */
wBool_t
wPrefGetIntegerExt(const char *section, const char *name, long *result,
                   long defaultValue)
{
    struct appDefault *thisDefault;

    thisDefault = FindDefault(xtcDefaults, section, name);

    if (thisDefault) {
        if (thisDefault->valueType == INTEGERCONSTANT) {
            defaultValue = thisDefault->defaultValue.intValue;
        } else {
            defaultValue = (thisDefault->defaultValue.intFunction)(thisDefault,
                           thisDefault->additionalData);
        }
    }

    return (wPrefGetIntegerBasic(section, name, result, defaultValue));
}

/**
 * Get a float value from the configuration file. The is a wrapper for the real
 * file access and adds a region specific default value.
 *
 * \param section IN section in config file
 * \param name IN name in config file
 * \param result OUT pointer to result
 * \param defaultValue IN the default value to use if config is not found
 * \return returncode of wPrefGetFloatBasic()
 */

wBool_t
wPrefGetFloatExt(const char *section, const char *name, double *result,
                 double defaultValue)
{
    struct appDefault *thisDefault;

    thisDefault = FindDefault(xtcDefaults, section, name);

    if (thisDefault) {
        if (thisDefault->valueType == FLOATCONSTANT) {
            defaultValue = thisDefault->defaultValue.floatValue;
        } else {
            defaultValue = (thisDefault->defaultValue.floatFunction)(thisDefault,
                           thisDefault->additionalData);
        }
    }

    return (wPrefGetFloatBasic(section, name, result, defaultValue));
}

/**
 * Get a string from the configuration file. The is a wrapper for the real
 * file access and adds a region specific default value.
 *
 * \param section IN section in config file
 * \param name IN name in config file
 * \return returncode of wPrefGetStringBasic()
 */
char *
wPrefGetStringExt(const char *section, const char *name)
{
    struct appDefault *thisDefault;

    thisDefault = FindDefault(xtcDefaults, section, name);

    if (thisDefault) {
        char *prefString;
        char *defaultValue;

        if (thisDefault->valueType == STRINGCONSTANT) {
            defaultValue = thisDefault->defaultValue.stringValue;
        } else {
            defaultValue = (thisDefault->defaultValue.stringFunction)(thisDefault,
                           thisDefault->additionalData);
        }

        prefString = (char *)wPrefGetStringBasic(section, name);
        return (prefString ? prefString : defaultValue);
    } else {
        return ((char *)wPrefGetStringBasic(section, name));
    }
}

/**
 * Initialize the application default system. The flag firstrun is used to find
 * out whether the application was run before. This is accomplished by trying
 * to read it from the configuration file. As it is only written after this 
 * test, it can never be found on the first run of the application ie. when the
 * configuration file does not exist yet.
 */

void 
InitAppDefaults(void)
{
	wPrefGetIntegerBasic( "misc", "firstrun", &bFirstRun, TRUE);
	if (bFirstRun) {
		wPrefSetInteger("misc", "firstrun", FALSE);
		InitializeRegionCode();
	} else {
		GetIntegerPref = wPrefGetIntegerBasic;
		GetFloatPref = wPrefGetFloatBasic;
		GetStringPref = wPrefGetStringBasic;
	}
}