Changeset 64:cc3763817b8a

Show
Ignore:
Timestamp:
06/29/08 06:55:55 (6 months ago)
Author:
Diggory Hardy <diggory.hardy@gmail.com>
branch:
default
Message:

Overhauled Options so that it now uses templates and mixins for type-specific internals, and supported types can be adjusted via just one list.

Files:

Legend:

Unmodified
Added
Removed
Modified
Copied
Moved
  • codeDoc/jobs.txt

    r61 r64  
    5454 
    5555Done (for mercurial log message): 
     56Added content IDs... 
     57Added widget strings... 
  • data/conf/gui.mtt

    r61 r64  
    88<int|x=150> 
    99<int|y=200> 
    10 <int[][int]|widgetData=[0:[0xB004,5,5,2,1,22,1,2,1,1,1,1,1,2,1,22,1,2,1,1,1,1,1,2,1,22,1,2],1:[0x3001],2:[0x21,0xFFFF00],22:[0x22,0xFF00]]> 
     10<int[][int]|widgetData=[0:[0xB004,5,5,3,1,22,1,2,1,1,1,1,1,22,1,22,1,22,1,1,1,1,1,2,1,22,1,3],1:[0x3001],2:[0x21,0,0xFFFF00],3:[0x21,1,0xBFFF00],22:[0x22,1,0xFF00]]> 
     11<char[][int]|widgetStrings=[0:"alpha=α", 1:"beta=β"]> 
    1112{WEmbedded} 
    1213<int|x=20> 
  • mde/font/font.d

    r63 r64  
    8383                 
    8484                // Reset the default filter (in case an invalid value was set in config files). 
    85                 Options.setInt ("font", "lcdFilter", FT_LcdFilter.FT_LCD_FILTER_DEFAULT); 
     85                fontOpts.set!(int) ("lcdFilter", FT_LcdFilter.FT_LCD_FILTER_DEFAULT); 
    8686                 
    8787                /* If no support for LCD filtering, then LCD rendering only emulates NORMAL with 3 
    8888                 * times wider glyphs. So disable and save the extra work. */ 
    89                 Options.setInt ("font", "renderMode", FT_LOAD_TARGET_NORMAL); 
     89                fontOpts.set!(int) ("renderMode", FT_LOAD_TARGET_NORMAL); 
    9090            } 
    9191             
  • mde/lookup/Options.d

    r63 r64  
    4545* should be used for reading variables, and via the addOptionsClass() hook will be loaded from 
    4646* files during pre-init (init0 stage). Do not write changes directly to the subclasses or they will 
    47 * not be saved; use, for example, Options.setBool(...). Use an example like OptionsMisc as a 
    48 * template for creating a new Options sub-class. 
     47* not be saved; instead use set(), for example, miscOpts.set!(char[])("L10n","en-GB"). Use an 
     48* example like OptionsMisc as a template for creating a new Options sub-class. 
    4949* 
    5050* Details: Options sub-classes hold associative arrays of pointers to all option variables, with a 
     
    5656class Options : IDataSection 
    5757{ 
    58     // No actual options are stored by this class. However, much of the infrastructure is 
    59     // present since it need not be redefined in sub-classes. 
    60      
    61     // The "pointer lists": 
    62     protected bool*  [ID]   optsBool; 
    63     protected int*   [ID]   optsInt; 
    64     protected double*[ID]   optsDouble; 
    65     protected char[]*[ID]   optsCharA; 
     58    // All supported types, for generic handling via templates. It should be possible to change 
     59    // the supported types simply by changing this list now (untested). 
     60    template store(A...) { alias A store; } 
     61    alias store!(bool, int, double, char[]) TYPES; 
     62    //BEGIN Templates: internal 
     63    private { 
     64        // Get name of a type. Basically just stringof, but special handling for arrays. 
     65        // Use TName!(T) for a valid symbol name, and T.stringof for a type. 
     66        template TName(T : T[]) { 
     67            const char[] TName = TName!(T) ~ "A"; 
     68        } 
     69        template TName(T) { 
     70            const char[] TName = T.stringof; 
     71        } 
     72         
     73        // Pointer lists 
     74        template PLists(T, A...) { 
     75            static if (A.length) { 
     76                const char[] PLists = T.stringof~"*[ID] opts"~TName!(T)~";\n" ~ PLists!(A); 
     77            } else 
     78                const char[] PLists = T.stringof~"*[ID] opts"~TName!(T)~";\n"; 
     79        } 
     80         
     81        // True if type is one of A 
     82        template TIsIn(T, A...) { 
     83            static if (A.length) { 
     84                static if (is(T == A[0])) 
     85                    const bool TIsIn = true; 
     86                else 
     87                    const bool TIsIn = TIsIn!(T,A[1..$]); 
     88            } else 
     89                const bool TIsIn = false;   // no more possibilities 
     90        } 
     91         
     92        // For addTag 
     93        template addTagMixin(T, A...) { 
     94            const char[] ifBlock = `if (tp == "`~T.stringof~`") { 
     95                `~T.stringof~`** p = id in opts`~TName!(T)~`; 
     96                if (p !is null) **p = parseTo!(`~T.stringof~`) (dt); 
     97            }`; 
     98            static if (A.length) 
     99                const char[] addTagMixin = ifBlock~` else `~addTagMixin!(A).addTagMixin; 
     100            else 
     101                const char[] addTagMixin = ifBlock; 
     102        } 
     103         
     104        // For writeAll 
     105        template writeAllMixin(A...) { 
     106            static if (A.length) { 
     107                const char[] writeAllMixin = 
     108                        `foreach (ID id, `~A[0].stringof~`* val; opts`~TName!(A[0])~`) dlg ("`~A[0].stringof~`", id, parseFrom!(`~A[0].stringof~`) (*val));` ~ writeAllMixin!(A[1..$]); 
     109            } else 
     110                const char[] writeAllMixin = ``; 
     111        } 
     112    } 
     113    //END Templates: internal 
     114     
     115     
     116    //BEGIN Static 
     117    static { 
     118        /** Add an options sub-class to the list for loading and saving. 
     119         * 
     120         * Call from static this() (before Init calls load()). */ 
     121        void addOptionsClass (Options c, char[] i) 
     122        in {    // Trap a couple of potential coding errors: 
     123            assert (c !is null);    // Instance must be created before calling addOptionsClass 
     124            assert (((cast(ID) i) in subClasses) is null);  // Don't allow a silent replacement 
     125        } body { 
     126            subClasses[cast(ID) i] = c; 
     127        } 
     128     
     129        // Track all sections for saving/loading/other generic handling. 
     130        Options[ID] subClasses; 
     131        bool changed = false;    // any changes at all, i.e. do we need to save? 
     132     
     133        /* Load/save options from file. 
     134         * 
     135         * If the file doesn't exist, no reading is attempted (options are left at default values). 
     136        */ 
     137        private const fileName = "options"; 
     138        private const MT_LOAD_EXC = "Loading options aborted:"; 
     139        void load () { 
     140        // Check it exists (if not it should still be created on exit). 
     141        // Don't bother checking it's not a folder, because it could still be a block or something. 
     142            if (!confDir.exists (fileName)) return; 
     143         
     144            try { 
     145                IReader reader; 
     146                reader = confDir.makeMTReader (fileName, PRIORITY.LOW_HIGH); 
     147                reader.dataSecCreator = delegate IDataSection(ID id) { 
     148                    /* Recognise each defined section, and return null for unrecognised sections. */ 
     149                    Options* p = id in subClasses; 
     150                    if (p !is null) return *p; 
     151                    else return null; 
     152                }; 
     153                reader.read; 
     154            } catch (MTException e) { 
     155                logger.fatal (MT_LOAD_EXC); 
     156                logger.fatal (e.msg); 
     157                throw new optionsLoadException ("Mergetag exception (see above message)"); 
     158            } 
     159        } 
     160        void save () { 
     161            if (!changed) return;   // no changes to save 
     162         
     163            DataSet ds = new DataSet(); 
     164            foreach (id, subOpts; subClasses) ds.sec[id] = subOpts.optionChanges; 
     165         
     166            // Read locally-stored options 
     167            try { 
     168                IReader reader; 
     169                reader = confDir.makeMTReader (fileName, PRIORITY.HIGH_ONLY, ds); 
     170                reader.dataSecCreator = delegate IDataSection(ID id) { 
     171                    return null;    // All recognised sections are already in the dataset. 
     172                }; 
     173                reader.read; 
     174            } catch (MTFileIOException) { 
     175                // File either didn't exist or couldn't be opened. 
     176                // Presuming the former, this is not a problem. 
     177            } catch (MTException e) { 
     178                // Log a message and continue, overwriting the file: 
     179                logger.error (MT_LOAD_EXC); 
     180                logger.error (e.msg); 
     181            } 
     182         
     183            try { 
     184                IWriter writer; 
     185                writer = confDir.makeMTWriter (fileName, ds); 
     186                writer.write(); 
     187            } catch (MTException e) { 
     188                logger.error ("Saving options aborted! Reason:"); 
     189                logger.error (e.msg); 
     190            } 
     191        } 
     192     
     193        private Logger logger; 
     194        static this() { 
     195            logger = Log.getLogger ("mde.options"); 
     196        } 
     197    } 
     198    //END Static 
     199     
     200     
     201    //BEGIN Non-static 
     202    /** Set option symbol of an Options sub-class to val. 
     203     * 
     204     * Due to the way options are handled generically, string IDs must be used to access the options 
     205     * via hash-maps, which is a little slower than direct access but necessary since the option 
     206     * must be changed in two separate places. */ 
     207    void set(T) (char[] symbol, T val) { 
     208        static if (!TIsIn!(T,TYPES)) 
     209            static assert (false, "Options.set does not currently support type "~T.stringof); 
     210         
     211        mixin (`alias opts`~T.stringof~` optsVars;`); 
     212         
     213        changed = true;     // something got set (don't bother checking this isn't what it already was) 
     214         
     215        try { 
     216            *(optsVars[cast(ID) symbol]) = val; 
     217            optionChanges.set!(T) (cast(ID) symbol, val); 
     218        } catch (ArrayBoundsException) { 
     219            // log and ignore: 
     220            logger.error ("Options.set: unkw!"); 
     221        } 
     222    } 
     223     
     224    protected { 
     225        OptionChanges optionChanges;    // all changes to options (for saving) 
     226         
     227        // The "pointer lists": 
     228        mixin (PLists!(TYPES)); 
     229    } 
    66230     
    67231    //BEGIN Mergetag loading/saving code 
    68232    void addTag (char[] tp, ID id, char[] dt) { 
    69         if (tp == "bool") { 
    70             bool** p = id in optsBool; 
    71             if (p !is null) **p = parseTo!(bool) (dt); 
    72         } else if (tp == "char[]") { 
    73             char[]** p = id in optsCharA; 
    74             if (p !is null) **p = parseTo!(char[]) (dt); 
    75         } else if (tp == "double") { 
    76             double** p = id in optsDouble; 
    77             if (p !is null) **p = parseTo!(double) (dt); 
    78         } else if (tp == "int") { 
    79             int** p = id in optsInt; 
    80             if (p !is null) **p = parseTo!(int) (dt); 
    81         } 
     233        mixin(addTagMixin!(TYPES).addTagMixin); 
    82234    } 
    83235 
    84236    void writeAll (ItemDelg dlg) { 
    85         foreach (ID id, bool*   val; optsBool)  dlg ("bool"  , id, parseFrom!(bool  ) (*val)); 
    86         foreach (ID id, char[]* val; optsCharA) dlg ("char[]", id, parseFrom!(char[]) (*val)); 
    87         foreach (ID id, double* val; optsDouble)dlg ("double", id, parseFrom!(double) (*val)); 
    88         foreach (ID id, int*    val; optsInt)   dlg ("int"   , id, parseFrom!(int   ) (*val)); 
     237        mixin(writeAllMixin!(TYPES)); 
    89238    } 
    90239    //END Mergetag loading/saving code 
    91      
    92     //BEGIN Static 
    93     /** Add an options sub-class to the list for loading and saving. 
    94     * 
    95     * Call from static this() (before Init calls load()). */ 
    96     static void addOptionsClass (Options c, char[] i) 
    97     in {    // Trap a couple of potential coding errors: 
    98         assert (c !is null);    // Instance must be created before calling addOptionsClass 
    99         assert (((cast(ID) i) in subClasses) is null);  // Don't allow a silent replacement 
    100     } body { 
    101         subClasses[cast(ID) i] = c; 
    102         subClassChanges[cast(ID) i] = new OptionsGeneric; 
    103     } 
    104      
    105     /** Set option symbol of Options class subClass to val. 
    106     * 
    107     * Due to the way options are handled generically, string IDs must be used to access the options 
    108     * via hash-maps, which is a little slower than direct access but necessary since the option 
    109     * must be changed in two separate places. */ 
    110     private static const ERR_MSG = "Options.setXXX called with incorrect parameters!"; 
    111     static void setBool (char[] subClass, char[] symbol, bool val) { 
    112         changed = true;     // something got set (don't bother checking this isn't what it already was) 
    113          
    114         try { 
    115             *(subClasses[cast(ID) subClass].optsBool[cast(ID) symbol]) = val; 
    116             subClassChanges[cast(ID) subClass].setBool (cast(ID) symbol, val); 
    117         } catch (ArrayBoundsException) { 
    118             // log and ignore: 
    119             logger.error (ERR_MSG); 
    120         } 
    121     } 
    122     static void setInt (char[] subClass, char[] symbol, int val) { 
    123         changed = true;     // something got set (don't bother checking this isn't what it already was) 
    124          
    125         try { 
    126             *(subClasses[cast(ID) subClass].optsInt[cast(ID) symbol]) = val; 
    127             subClassChanges[cast(ID) subClass].setInt (cast(ID) symbol, val); 
    128         } catch (ArrayBoundsException) { 
    129             // log and ignore: 
    130             logger.error (ERR_MSG); 
    131         } 
    132     } 
    133     static void setDouble (char[] subClass, char[] symbol, double val) { 
    134         changed = true;     // something got set (don't bother checking this isn't what it already was) 
    135          
    136         try { 
    137             *(subClasses[cast(ID) subClass].optsDouble[cast(ID) symbol]) = val; 
    138             subClassChanges[cast(ID) subClass].setDouble (cast(ID) symbol, val); 
    139         } catch (ArrayBoundsException) { 
    140             // log and ignore: 
    141             logger.error (ERR_MSG); 
    142         } 
    143     } 
    144     static void setCharA (char[] subClass, char[] symbol, char[] val) { 
    145         changed = true;     // something got set (don't bother checking this isn't what it already was) 
    146          
    147         try { 
    148             *(subClasses[cast(ID) subClass].optsCharA[cast(ID) symbol]) = val; 
    149             subClassChanges[cast(ID) subClass].setCharA (cast(ID) symbol, val); 
    150         } catch (ArrayBoundsException) { 
    151             // log and ignore: 
    152             logger.error (ERR_MSG); 
    153         } 
    154     } 
    155      
    156     // Track all sections for saving/loading/other generic handling. 
    157     static Options[ID] subClasses; 
    158     static OptionsGeneric[ID] subClassChanges; 
    159     static bool changed = false;    // any changes at all, i.e. do we need to save? 
    160      
    161     /* Load/save options from file. 
    162     * 
    163     * If the file doesn't exist, no reading is attempted (options are left at default values). 
    164     */ 
    165     private static const fileName = "options"; 
    166     private static const MT_LOAD_EXC = "Loading options aborted:"; 
    167     static void load () { 
    168         // Check it exists (if not it should still be created on exit). 
    169         // Don't bother checking it's not a folder, because it could still be a block or something. 
    170         if (!confDir.exists (fileName)) return; 
    171          
    172         try { 
    173             IReader reader; 
    174             reader = confDir.makeMTReader (fileName, PRIORITY.LOW_HIGH); 
    175             reader.dataSecCreator = delegate IDataSection(ID id) { 
    176                 /* Recognise each defined section, and return null for unrecognised sections. */ 
    177                 Options* p = id in subClasses; 
    178                 if (p !is null) return *p; 
    179                 else return null; 
    180             }; 
    181             reader.read; 
    182         } catch (MTException e) { 
    183             logger.fatal (MT_LOAD_EXC); 
    184             logger.fatal (e.msg); 
    185             throw new optionsLoadException ("Mergetag exception (see above message)"); 
    186         } 
    187     } 
    188     static void save () { 
    189         if (!changed) return;   // no changes to save 
    190          
    191         DataSet ds = new DataSet(); 
    192         foreach (id, sec; subClassChanges) ds.sec[id] = sec; 
    193          
    194         // Read locally-stored options 
    195         try { 
    196             IReader reader; 
    197             reader = confDir.makeMTReader (fileName, PRIORITY.HIGH_ONLY, ds); 
    198             reader.dataSecCreator = delegate IDataSection(ID id) { 
    199                 return null;    // All recognised sections are already in the dataset. 
    200             }; 
    201             reader.read; 
    202         } catch (MTFileIOException) { 
    203             // File either didn't exist or couldn't be opened. 
    204             // Presuming the former, this is not a problem. 
    205         } catch (MTException e) { 
    206             // Log a message and continue, overwriting the file: 
    207             logger.error (MT_LOAD_EXC); 
    208             logger.error (e.msg); 
    209         } 
    210          
    211         try { 
    212             IWriter writer; 
    213             writer = confDir.makeMTWriter (fileName, ds); 
    214             writer.write(); 
    215         } catch (MTException e) { 
    216             logger.error ("Saving options aborted! Reason:"); 
    217             logger.error (e.msg); 
    218         } 
    219     } 
    220      
    221     private static Logger logger; 
    222     static this() { 
    223         logger = Log.getLogger ("mde.options"); 
    224     } 
    225     //END Static 
    226      
    227     //BEGIN Templates 
     240    //END Non-static 
     241     
     242     
     243    //BEGIN Templates: impl & optionsThis 
    228244    private { 
    229245        // Return index of first comma, or halts if not found. 
     
    285301                const char[] listOrNull = "["~A~"]"; 
    286302        } 
     303        // for recursing on TYPES 
     304        template optionsThisInternal(char[] A, B...) { 
     305            static if (B.length) { 
     306                const char[] optionsThisInternal = `opts`~TName!(B[0])~` = `~listOrNull!(sTC!(aaVars!(parseT!(B[0].stringof,A))))~";\n" ~ optionsThisInternal!(A,B[1..$]); 
     307            } else 
     308                const char[] optionsThisInternal = ``; 
     309        } 
    287310    } protected { 
    288311        /** Produces the implementation code to go in the constuctor. */ 
    289         template aaDefs(char[] A) { 
    290             const char[] aaDefs = 
    291                     "optsBool = "  ~ listOrNull!(sTC!(aaVars!(parseT!("bool"  , A)))) ~ ";\n" ~ 
    292                     "optsInt = "   ~ listOrNull!(sTC!(aaVars!(parseT!("int"   , A)))) ~ ";\n" ~ 
    293                     "optsDouble = "~ listOrNull!(sTC!(aaVars!(parseT!("double", A)))) ~ ";\n" ~ 
    294                     "optsCharA = " ~ listOrNull!(sTC!(aaVars!(parseT!("char[]", A)))) ~ ";\n"; 
    295         } 
    296         /+/** Produces the implementation code to go in the static constuctor. */ 
     312        template optionsThis(char[] A) { 
     313            const char[] optionsThis = 
     314                    "optionChanges = new OptionChanges;\n" ~ 
     315                    optionsThisInternal!(A,TYPES); 
     316        } 
     317        /+ Needs too many custom parameters to be worth it? Plus makes class less readable. 
     318        /** Produces the implementation code to go in the static constuctor. */ 
    297319        template optClassAdd(char[] symb) { 
    298320            const char[] optClassAdd = symb ~ "=new "~classinfo(this).name~";\n";//Options.addOptionsClass("~symb~", );\n"; 
     
    307329         * In case this() needs to be customized, mixin(impl!(A)) is equivalent to: 
    308330         * --- 
    309          * mixin (A~"\nthis(){\n"~aaDefs!(A)~"}"); 
     331         * mixin (A~` 
     332        this () { 
     333        `~optionsThis!(A)~` 
     334    }`); 
    310335         * --- 
    311336         * 
    312337         * Notes: Only use space as whitespace (no new-lines or tabs). Make sure to add a trailing 
    313338         * semi-colon (;) or you'll get told off! :D 
    314          * 
    315339         * In general errors aren't reported well. Trial with pragma (msg, impl!(...)); if 
    316340         * necessary. 
     
    319343         * class, but doing so would rather decrease readability of any implementation. */ 
    320344        template impl(char[] A /+, char[] symb+/) { 
    321             const char[] impl = A~"\nthis(){\n"~aaDefs!(A)~"}"; 
     345            const char[] impl = A~"\nthis(){\n"~optionsThis!(A)~"}"; 
    322346            // ~"\nstatic this(){\n"~optClassAdd!(symb)~"}" 
    323347        } 
    324348    } 
    325     /+/** mixin impl("type symbol[, symbol[...]];[type symbol[...];][...]") 
    326      * 
    327      * E.g. 
    328      * --- 
    329      * mixin (impl ("bool a, b; int i;")); 
    330      * --- 
    331      * The parser isn't highly accurate. */ 
    332     // Try using templates instead? See std.metastrings 
    333     static char[] impl (char[] A) { 
    334         char[] bools; 
    335         char[] ints; 
    336          
    337         while (A.length) { 
    338             // Trim whitespace 
    339             { 
    340                 size_t i = 0; 
    341                 while (i < A.length && (A[i] == ' ' || (A[i] >= 9u && A[i] <= 0xD))) 
    342                     ++i; 
    343                 A = A[i..$]; 
    344             } 
    345             if (A.length == 0) break; 
    346              
    347             char[] type; 
    348             for (size_t i = 0; i < A.length; ++i) { 
    349                 if (A[i] == ' ' || (A[i] >= 9u && A[i] <= 0xD)) { 
    350                     type = A[0..i]; 
    351                     A = A[i+1..$]; 
    352                     break; 
    353                 } 
    354             } 
    355              
    356             char[] symbols; 
    357             for (size_t i = 0; i < A.length; ++i) { 
    358                 if (A[i] == ';') { 
    359                     symbols = A[0..i]; 
    360                     A = A[i+1..$]; 
    361                     break; 
    362                 } 
    363             } 
    364              
    365             if (type == "bool") { 
    366                 if (bools.length) 
    367                     bools = bools ~ "," ~ symbols; 
    368                 else 
    369                     bools = symbols; 
    370             } 
    371             else if (type == "int") { 
    372                 if (ints.length) 
    373                     ints = ints ~ "," ~ symbols; 
    374                 else 
    375                     ints = symbols; 
    376             } 
    377             else { 
    378                 // Unfortunately, we cannot output non-const strings (even though func is compile-time) 
    379                 // We also cannot use pragma(msg) because the message gets printed even if the code isn't run. 
    380                 //pragma(msg, "Warning: impl failed to parse whole input string"); 
    381                 // Cannot use Cout / logger either. 
    382                 break; 
    383             } 
    384         } 
    385          
    386         char[] ret; 
    387         if (bools.length) 
    388             ret = "bool "~bools~";\n"; 
    389         if (ints.length) 
    390             ret = ret ~ "int "~ints~";\n"; 
    391          
    392          
    393          
    394         return ret; 
    395     }+/ 
     349    //END Templates: impl & optionsThis 
     350
     351 
     352/* Special class to store all locally changed options, whatever the section. */ 
     353class OptionChanges : Options { 
     354    //BEGIN Templates 
     355    private { 
     356        template Vars(A...) { 
     357            static if (A.length) { 
     358                const char[] Vars = A[0].stringof~`[] `~TName!(A[0])~`s;` ~ Vars!(A[1..$]); 
     359            } else 
     360                const char[] Vars = ``; 
     361        } 
     362         
     363        // For addTag; different to Options.addTag(). 
     364        // Reverse priority: only load symbols not currently existing 
     365        template addTagMixin(T, A...) { 
     366            const char[] ifBlock = `if (tp == "`~T.stringof~`") { 
     367                if ((id in opts`~TName!(T)~`) is null) { 
     368                    `~TName!(T)~`s ~= parseTo!(`~T.stringof~`) (dt); 
     369                    opts`~TName!(T)~`[id] = &`~TName!(T)~`s[$-1]; 
     370            } 
     371            }`; 
     372            static if (A.length) 
     373                    const char[] addTagMixin = ifBlock~` else `~addTagMixin!(A).addTagMixin; 
     374            else 
     375                const char[] addTagMixin = ifBlock; 
     376        } 
     377 
     378    } 
    396379    //END Templates 
    397 } 
    398  
    399 /* Special class to store all locally changed options, whatever the section. */ 
    400 class OptionsGeneric : Options { 
    401380    // These store the actual values, but are never accessed directly except when initially added. 
    402381    // optsX store pointers to each item added along with the ID and are used for access. 
    403     bool[] bools; 
    404     int[] ints; 
    405     double[] doubles; 
    406     char[][] strings; 
     382    mixin(Vars!(TYPES)); 
    407383     
    408384    this () {} 
    409385     
    410     void setBool (ID id, bool x) { 
    411         bool** p = id in optsBool; 
     386    void set(T) (ID id, T x) { 
     387        static if (!TIsIn!(T,TYPES)) 
     388            static assert (false, "OptionChanges.set does not currently support type "~T.stringof); 
     389         
     390        mixin (`alias opts`~T.stringof~` optsVars;`); 
     391        mixin (`alias `~T.stringof~`s vars;`); 
     392         
     393        T** p = id in optsVars; 
    412394        if (p !is null) **p = x; 
    413395        else { 
    414             bools ~= x; 
    415             optsBool[id] = &bools[$-1]; 
    416         } 
    417     } 
    418     void setInt (ID id, int x) { 
    419         int** p = id in optsInt; 
    420         if (p !is null) **p = x; 
    421         else { 
    422             ints ~= x; 
    423             optsInt[id] = &ints[$-1]; 
    424         } 
    425     } 
    426     void setDouble (ID id, double x) { 
    427         double** p = id in optsDouble; 
    428         if (p !is null) **p = x; 
    429         else { 
    430             doubles ~= x; 
    431             optsDouble[id] = &doubles[$-1]; 
    432         } 
    433     } 
    434     void setCharA (ID id, char[] x) { 
    435         char[]** p = id in optsCharA; 
    436         if (p !is null) **p = x; 
    437         else { 
    438             strings ~= x; 
    439             optsCharA[id] = &strings[$-1]; 
     396            vars ~= x; 
     397            optsVars[id] = &vars[$-1]; 
    440398        } 
    441399    } 
     
    444402    // Reverse priority: only load symbols not currently existing 
    445403    void addTag (char[] tp, ID id, char[] dt) { 
    446         if (tp == "bool") { 
    447             if ((id in optsBool) is null) { 
    448                 bools ~= parseTo!(bool) (dt); 
    449                 optsBool[id] = &bools[$-1]; 
    450             } 
    451         } else if (tp == "char[]") { 
    452             if ((id in optsCharA) is null) { 
    453                 strings ~= parseTo!(char[]) (dt); 
    454                 optsCharA[id] = &strings[$-1]; 
    455             } 
    456             char[]** p = id in optsCharA; 
    457             if (p !is null) **p = parseTo!(char[]) (dt); 
    458         } else if (tp == "int") { 
    459             if ((id in optsInt) is null) { 
    460                 ints ~= parseTo!(int) (dt); 
    461                 optsInt[id] = &ints[$-1]; 
    462             } 
    463         } 
     404        mixin (addTagMixin!(TYPES).addTagMixin); 
    464405    } 
    465406    //END Mergetag loading/saving code 
  • mde/mde.d

    r63 r64  
    5555     
    5656    if (miscOpts.pollInterval !<= 1.0 || miscOpts.pollInterval !>= 0.0) 
    57         Options.setDouble ("misc", "pollInterval", 0.01); 
     57        miscOpts.set!(double) ("pollInterval", 0.01); 
    5858    //END Initialisation 
    5959     
  • mde/setup/Init.d

    r63 r64  
    186186            } else if (!(miscOpts.logOptions & LOG.CONSOLE)) { 
    187187                // make sure at least one logger is enabled 
    188                 Options.setInt ("misc", "logOptions", miscOpts.logOptions | LOG.CONSOLE); 
     188                miscOpts.set!(int) ("logOptions", miscOpts.logOptions | LOG.CONSOLE); 
    189189            } 
    190190            if (miscOpts.logOptions & LOG.CONSOLE) {    // Log to the console 
     
    328328                logger.info ("Will disable threads and continue, assuming no threads were created."); 
    329329             
    330                 Options.setBool("misc", "useThreads", false);   // Disable threads entirely 
     330                miscOpts.set!(bool)("useThreads", false);   // Disable threads entirely 
    331331                return true;                // Try again without threads 
    332332            } 
  • mde/setup/sdl.d

    r63 r64  
    159159void resizeWindow (int w, int h) { 
    160160    if (vidOpts.fullscreen) { 
    161         Options.setInt ("video", "screenW", w); 
    162         Options.setInt ("video", "screenH", h); 
     161        vidOpts.set!(int) ("screenW", w); 
     162        vidOpts.set!(int) ("screenH", h); 
    163163    } else { 
    164         Options.setInt ("video", "windowW", w); 
    165         Options.setInt ("video", "windowH", h); 
     164        vidOpts.set!(int) ("windowW", w); 
     165        vidOpts.set!(int) ("windowH", h); 
    166166    } 
    167167