settings_gui.cpp

Go to the documentation of this file.
00001 /* $Id: settings_gui.cpp 15723 2009-03-15 15:12:06Z rubidium $ */
00002 
00005 #include "stdafx.h"
00006 #include "openttd.h"
00007 #include "currency.h"
00008 #include "gui.h"
00009 #include "window_gui.h"
00010 #include "textbuf_gui.h"
00011 #include "command_func.h"
00012 #include "engine_func.h"
00013 #include "screenshot.h"
00014 #include "network/network.h"
00015 #include "town.h"
00016 #include "variables.h"
00017 #include "settings_internal.h"
00018 #include "newgrf_townname.h"
00019 #include "strings_func.h"
00020 #include "window_func.h"
00021 #include "string_func.h"
00022 #include "gfx_func.h"
00023 #include "waypoint.h"
00024 #include "widgets/dropdown_type.h"
00025 #include "widgets/dropdown_func.h"
00026 #include "station_func.h"
00027 #include "highscore.h"
00028 #include "gfxinit.h"
00029 #include <map>
00030 
00031 #include "table/sprites.h"
00032 #include "table/strings.h"
00033 
00034 static const StringID _units_dropdown[] = {
00035   STR_UNITS_IMPERIAL,
00036   STR_UNITS_METRIC,
00037   STR_UNITS_SI,
00038   INVALID_STRING_ID
00039 };
00040 
00041 static const StringID _driveside_dropdown[] = {
00042   STR_02E9_DRIVE_ON_LEFT,
00043   STR_02EA_DRIVE_ON_RIGHT,
00044   INVALID_STRING_ID
00045 };
00046 
00047 static const StringID _autosave_dropdown[] = {
00048   STR_02F7_OFF,
00049   STR_AUTOSAVE_1_MONTH,
00050   STR_02F8_EVERY_3_MONTHS,
00051   STR_02F9_EVERY_6_MONTHS,
00052   STR_02FA_EVERY_12_MONTHS,
00053   INVALID_STRING_ID,
00054 };
00055 
00056 static StringID *BuildDynamicDropdown(StringID base, int num)
00057 {
00058   static StringID buf[32 + 1];
00059   StringID *p = buf;
00060   while (--num >= 0) *p++ = base++;
00061   *p = INVALID_STRING_ID;
00062   return buf;
00063 }
00064 
00065 int _nb_orig_names = SPECSTR_TOWNNAME_LAST - SPECSTR_TOWNNAME_START + 1;
00066 static StringID *_grf_names = NULL;
00067 static int _nb_grf_names = 0;
00068 
00069 void InitGRFTownGeneratorNames()
00070 {
00071   free(_grf_names);
00072   _grf_names = GetGRFTownNameList();
00073   _nb_grf_names = 0;
00074   for (StringID *s = _grf_names; *s != INVALID_STRING_ID; s++) _nb_grf_names++;
00075 }
00076 
00077 static inline StringID TownName(int town_name)
00078 {
00079   if (town_name < _nb_orig_names) return STR_TOWNNAME_ORIGINAL_ENGLISH + town_name;
00080   town_name -= _nb_orig_names;
00081   if (town_name < _nb_grf_names) return _grf_names[town_name];
00082   return STR_UNDEFINED;
00083 }
00084 
00085 static int GetCurRes()
00086 {
00087   int i;
00088 
00089   for (i = 0; i != _num_resolutions; i++) {
00090     if (_resolutions[i].width == _screen.width &&
00091         _resolutions[i].height == _screen.height) {
00092       break;
00093     }
00094   }
00095   return i;
00096 }
00097 
00098 enum GameOptionsWidgets {
00099   GAMEOPT_CURRENCY_BTN    =  4,
00100   GAMEOPT_DISTANCE_BTN    =  6,
00101   GAMEOPT_ROADSIDE_BTN    =  8,
00102   GAMEOPT_TOWNNAME_BTN    = 10,
00103   GAMEOPT_AUTOSAVE_BTN    = 12,
00104   GAMEOPT_LANG_BTN        = 14,
00105   GAMEOPT_RESOLUTION_BTN  = 16,
00106   GAMEOPT_FULLSCREEN,
00107   GAMEOPT_SCREENSHOT_BTN  = 19,
00108   GAMEOPT_BASE_GRF_BTN    = 21,
00109 };
00110 
00116 static void ShowTownnameDropdown(Window *w, int sel)
00117 {
00118   typedef std::map<StringID, int, StringIDCompare> TownList;
00119   TownList townnames;
00120 
00121   /* Add and sort original townnames generators */
00122   for (int i = 0; i < _nb_orig_names; i++) townnames[STR_TOWNNAME_ORIGINAL_ENGLISH + i] = i;
00123 
00124   /* Add and sort newgrf townnames generators */
00125   for (int i = 0; i < _nb_grf_names; i++) townnames[_grf_names[i]] = _nb_orig_names + i;
00126 
00127   DropDownList *list = new DropDownList();
00128   for (TownList::iterator it = townnames.begin(); it != townnames.end(); it++) {
00129     list->push_back(new DropDownListStringItem((*it).first, (*it).second, !(_game_mode == GM_MENU || GetNumTowns() == 0 || (*it).second == sel)));
00130   }
00131 
00132   ShowDropDownList(w, list, sel, GAMEOPT_TOWNNAME_BTN);
00133 }
00134 
00135 static void ShowCustCurrency();
00136 
00137 static void ShowGraphicsSetMenu(Window *w)
00138 {
00139   int n = GetNumGraphicsSets();
00140   int current = GetIndexOfCurrentGraphicsSet();
00141 
00142   DropDownList *list = new DropDownList();
00143   for (int i = 0; i < n; i++) {
00144     list->push_back(new DropDownListCharStringItem(GetGraphicsSetName(i), i, (_game_mode == GM_MENU) ? false : (current != i)));
00145   }
00146 
00147   ShowDropDownList(w, list, current, GAMEOPT_BASE_GRF_BTN);
00148 }
00149 
00150 struct GameOptionsWindow : Window {
00151   GameSettings *opt;
00152   bool reload;
00153 
00154   GameOptionsWindow(const WindowDesc *desc) : Window(desc)
00155   {
00156     this->opt = (_game_mode == GM_MENU) ? &_settings_newgame : &_settings_game;
00157     this->reload = false;
00158     this->FindWindowPlacementAndResize(desc);
00159   }
00160 
00161   ~GameOptionsWindow()
00162   {
00163     DeleteWindowById(WC_CUSTOM_CURRENCY, 0);
00164     if (this->reload) _switch_mode = SM_MENU;
00165   }
00166 
00167   virtual void OnPaint()
00168   {
00169     SetDParam(1, _currency_specs[this->opt->locale.currency].name);
00170     SetDParam(2, STR_UNITS_IMPERIAL + this->opt->locale.units);
00171     SetDParam(3, STR_02E9_DRIVE_ON_LEFT + this->opt->vehicle.road_side);
00172     SetDParam(4, TownName(this->opt->game_creation.town_name));
00173     SetDParam(5, _autosave_dropdown[_settings_client.gui.autosave]);
00174     SetDParam(6, SPECSTR_LANGUAGE_START + _dynlang.curr);
00175     int i = GetCurRes();
00176     SetDParam(7, i == _num_resolutions ? STR_RES_OTHER : SPECSTR_RESOLUTION_START + i);
00177     SetDParam(8, SPECSTR_SCREENSHOT_START + _cur_screenshot_format);
00178     this->SetWidgetLoweredState(GAMEOPT_FULLSCREEN, _fullscreen);
00179     SetDParamStr(9, GetGraphicsSetName(GetIndexOfCurrentGraphicsSet()));
00180 
00181     this->DrawWidgets();
00182     DrawString(20, 175, STR_OPTIONS_FULLSCREEN, TC_FROMSTRING); // fullscreen
00183   }
00184 
00185   virtual void OnClick(Point pt, int widget)
00186   {
00187     switch (widget) {
00188       case GAMEOPT_CURRENCY_BTN: // Setup currencies dropdown
00189         ShowDropDownMenu(this, BuildCurrencyDropdown(), this->opt->locale.currency, GAMEOPT_CURRENCY_BTN, _game_mode == GM_MENU ? 0 : ~GetMaskOfAllowedCurrencies(), 0);
00190         break;
00191 
00192       case GAMEOPT_DISTANCE_BTN: // Setup distance unit dropdown
00193         ShowDropDownMenu(this, _units_dropdown, this->opt->locale.units, GAMEOPT_DISTANCE_BTN, 0, 0);
00194         break;
00195 
00196       case GAMEOPT_ROADSIDE_BTN: { // Setup road-side dropdown
00197         int i = 0;
00198         extern bool RoadVehiclesAreBuilt();
00199 
00200         /* You can only change the drive side if you are in the menu or ingame with
00201          * no vehicles present. In a networking game only the server can change it */
00202         if ((_game_mode != GM_MENU && RoadVehiclesAreBuilt()) || (_networking && !_network_server)) {
00203           i = (-1) ^ (1 << this->opt->vehicle.road_side); // disable the other value
00204         }
00205 
00206         ShowDropDownMenu(this, _driveside_dropdown, this->opt->vehicle.road_side, GAMEOPT_ROADSIDE_BTN, i, 0);
00207       } break;
00208 
00209       case GAMEOPT_TOWNNAME_BTN: // Setup townname dropdown
00210         ShowTownnameDropdown(this, this->opt->game_creation.town_name);
00211         break;
00212 
00213       case GAMEOPT_AUTOSAVE_BTN: // Setup autosave dropdown
00214         ShowDropDownMenu(this, _autosave_dropdown, _settings_client.gui.autosave, GAMEOPT_AUTOSAVE_BTN, 0, 0);
00215         break;
00216 
00217       case GAMEOPT_LANG_BTN: { // Setup interface language dropdown
00218         typedef std::map<StringID, int, StringIDCompare> LangList;
00219 
00220         /* Sort language names */
00221         LangList langs;
00222         for (int i = 0; i < _dynlang.num; i++) langs[SPECSTR_LANGUAGE_START + i] = i;
00223 
00224         DropDownList *list = new DropDownList();
00225         for (LangList::iterator it = langs.begin(); it != langs.end(); it++) {
00226           list->push_back(new DropDownListStringItem((*it).first, (*it).second, false));
00227         }
00228 
00229         ShowDropDownList(this, list, _dynlang.curr, GAMEOPT_LANG_BTN);
00230       } break;
00231 
00232       case GAMEOPT_RESOLUTION_BTN: // Setup resolution dropdown
00233         ShowDropDownMenu(this, BuildDynamicDropdown(SPECSTR_RESOLUTION_START, _num_resolutions), GetCurRes(), GAMEOPT_RESOLUTION_BTN, 0, 0);
00234         break;
00235 
00236       case GAMEOPT_FULLSCREEN: // Click fullscreen on/off
00237         /* try to toggle full-screen on/off */
00238         if (!ToggleFullScreen(!_fullscreen)) {
00239           ShowErrorMessage(INVALID_STRING_ID, STR_FULLSCREEN_FAILED, 0, 0);
00240         }
00241         this->SetWidgetLoweredState(GAMEOPT_FULLSCREEN, _fullscreen);
00242         this->SetDirty();
00243         break;
00244 
00245       case GAMEOPT_SCREENSHOT_BTN: // Setup screenshot format dropdown
00246         ShowDropDownMenu(this, BuildDynamicDropdown(SPECSTR_SCREENSHOT_START, _num_screenshot_formats), _cur_screenshot_format, GAMEOPT_SCREENSHOT_BTN, 0, 0);
00247         break;
00248 
00249       case GAMEOPT_BASE_GRF_BTN:
00250         ShowGraphicsSetMenu(this);
00251         break;
00252     }
00253   }
00254 
00255   virtual void OnDropdownSelect(int widget, int index)
00256   {
00257     switch (widget) {
00258       case GAMEOPT_CURRENCY_BTN: // Currency
00259         if (index == CUSTOM_CURRENCY_ID) ShowCustCurrency();
00260         this->opt->locale.currency = index;
00261         MarkWholeScreenDirty();
00262         break;
00263 
00264       case GAMEOPT_DISTANCE_BTN: // Measuring units
00265         this->opt->locale.units = index;
00266         MarkWholeScreenDirty();
00267         break;
00268 
00269       case GAMEOPT_ROADSIDE_BTN: // Road side
00270         if (this->opt->vehicle.road_side != index) { // only change if setting changed
00271           uint i;
00272           if (GetSettingFromName("vehicle.road_side", &i) == NULL) NOT_REACHED();
00273           SetSettingValue(i, index);
00274           MarkWholeScreenDirty();
00275         }
00276         break;
00277 
00278       case GAMEOPT_TOWNNAME_BTN: // Town names
00279         if (_game_mode == GM_MENU || GetNumTowns() == 0) {
00280           this->opt->game_creation.town_name = index;
00281           InvalidateWindow(WC_GAME_OPTIONS, 0);
00282         }
00283         break;
00284 
00285       case GAMEOPT_AUTOSAVE_BTN: // Autosave options
00286         _settings_client.gui.autosave = index;
00287         this->SetDirty();
00288         break;
00289 
00290       case GAMEOPT_LANG_BTN: // Change interface language
00291         ReadLanguagePack(index);
00292         CheckForMissingGlyphsInLoadedLanguagePack();
00293         UpdateAllStationVirtCoord();
00294         UpdateAllWaypointSigns();
00295         MarkWholeScreenDirty();
00296         break;
00297 
00298       case GAMEOPT_RESOLUTION_BTN: // Change resolution
00299         if (index < _num_resolutions && ChangeResInGame(_resolutions[index].width, _resolutions[index].height)) {
00300           this->SetDirty();
00301         }
00302         break;
00303 
00304       case GAMEOPT_SCREENSHOT_BTN: // Change screenshot format
00305         SetScreenshotFormat(index);
00306         this->SetDirty();
00307         break;
00308 
00309       case GAMEOPT_BASE_GRF_BTN:
00310         if (_game_mode == GM_MENU) {
00311           const char *name = GetGraphicsSetName(index);
00312 
00313           free(_ini_graphics_set);
00314           _ini_graphics_set = strdup(name);
00315 
00316           SetGraphicsSet(name);
00317           this->reload = true;
00318         }
00319         break;
00320     }
00321   }
00322 };
00323 
00324 static const Widget _game_options_widgets[] = {
00325 {   WWT_CLOSEBOX,   RESIZE_NONE,  COLOUR_GREY,     0,    10,     0,    13, STR_00C5,                          STR_018B_CLOSE_WINDOW},
00326 {    WWT_CAPTION,   RESIZE_NONE,  COLOUR_GREY,    11,   369,     0,    13, STR_00B1_GAME_OPTIONS,             STR_018C_WINDOW_TITLE_DRAG_THIS},
00327 {      WWT_PANEL,   RESIZE_NONE,  COLOUR_GREY,     0,   369,    14,   242, 0x0,                               STR_NULL},
00328 {      WWT_FRAME,   RESIZE_NONE,  COLOUR_GREY,    10,   179,    20,    55, STR_02E0_CURRENCY_UNITS,           STR_NULL},
00329 { WWT_DROPDOWNIN,   RESIZE_NONE,  COLOUR_GREY,    20,   169,    34,    45, STR_02E1,                          STR_02E2_CURRENCY_UNITS_SELECTION},
00330 {      WWT_FRAME,   RESIZE_NONE,  COLOUR_GREY,   190,   359,    20,    55, STR_MEASURING_UNITS,               STR_NULL},
00331 { WWT_DROPDOWNIN,   RESIZE_NONE,  COLOUR_GREY,   200,   349,    34,    45, STR_02E4,                          STR_MEASURING_UNITS_SELECTION},
00332 {      WWT_FRAME,   RESIZE_NONE,  COLOUR_GREY,    10,   179,    62,    97, STR_02E6_ROAD_VEHICLES,            STR_NULL},
00333 { WWT_DROPDOWNIN,   RESIZE_NONE,  COLOUR_GREY,    20,   169,    76,    87, STR_02E7,                          STR_02E8_SELECT_SIDE_OF_ROAD_FOR},
00334 {      WWT_FRAME,   RESIZE_NONE,  COLOUR_GREY,   190,   359,    62,    97, STR_02EB_TOWN_NAMES,               STR_NULL},
00335 { WWT_DROPDOWNIN,   RESIZE_NONE,  COLOUR_GREY,   200,   349,    76,    87, STR_02EC,                          STR_02ED_SELECT_STYLE_OF_TOWN_NAMES},
00336 {      WWT_FRAME,   RESIZE_NONE,  COLOUR_GREY,    10,   179,   104,   139, STR_02F4_AUTOSAVE,                 STR_NULL},
00337 { WWT_DROPDOWNIN,   RESIZE_NONE,  COLOUR_GREY,    20,   169,   118,   129, STR_02F5,                          STR_02F6_SELECT_INTERVAL_BETWEEN},
00338 
00339 {      WWT_FRAME,   RESIZE_NONE,  COLOUR_GREY,   190,   359,   104,   139, STR_OPTIONS_LANG,                  STR_NULL},
00340 { WWT_DROPDOWNIN,   RESIZE_NONE,  COLOUR_GREY,   200,   349,   118,   129, STR_OPTIONS_LANG_CBO,              STR_OPTIONS_LANG_TIP},
00341 
00342 {      WWT_FRAME,   RESIZE_NONE,  COLOUR_GREY,    10,   179,   146,   190, STR_OPTIONS_RES,                   STR_NULL},
00343 { WWT_DROPDOWNIN,   RESIZE_NONE,  COLOUR_GREY,    20,   169,   160,   171, STR_OPTIONS_RES_CBO,               STR_OPTIONS_RES_TIP},
00344 {    WWT_TEXTBTN,   RESIZE_NONE,  COLOUR_GREY,   149,   169,   176,   184, STR_EMPTY,                         STR_OPTIONS_FULLSCREEN_TIP},
00345 
00346 {      WWT_FRAME,   RESIZE_NONE,  COLOUR_GREY,   190,   359,   146,   190, STR_OPTIONS_SCREENSHOT_FORMAT,     STR_NULL},
00347 { WWT_DROPDOWNIN,   RESIZE_NONE,  COLOUR_GREY,   200,   349,   160,   171, STR_OPTIONS_SCREENSHOT_FORMAT_CBO, STR_OPTIONS_SCREENSHOT_FORMAT_TIP},
00348 
00349 {      WWT_FRAME,   RESIZE_NONE,  COLOUR_GREY,    10,   179,   197,   232, STR_OPTIONS_BASE_GRF,              STR_NULL},
00350 { WWT_DROPDOWNIN,   RESIZE_NONE,  COLOUR_GREY,    20,   169,   211,   222, STR_OPTIONS_BASE_GRF_CBO,          STR_OPTIONS_BASE_GRF_TIP},
00351 
00352 {   WIDGETS_END},
00353 };
00354 
00355 static const WindowDesc _game_options_desc(
00356   WDP_CENTER, WDP_CENTER, 370, 243, 370, 243,
00357   WC_GAME_OPTIONS, WC_NONE,
00358   WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET | WDF_UNCLICK_BUTTONS,
00359   _game_options_widgets
00360 );
00361 
00362 
00363 void ShowGameOptions()
00364 {
00365   DeleteWindowById(WC_GAME_OPTIONS, 0);
00366   new GameOptionsWindow(&_game_options_desc);
00367 }
00368 
00369 extern void StartupEconomy();
00370 
00371 /* Widget definition for the game difficulty settings window */
00372 static const Widget _game_difficulty_widgets[] = {
00373 {   WWT_CLOSEBOX,   RESIZE_NONE,  COLOUR_MAUVE,      0,    10,     0,    13, STR_00C5,                     STR_018B_CLOSE_WINDOW},           // GDW_CLOSEBOX
00374 {    WWT_CAPTION,   RESIZE_NONE,  COLOUR_MAUVE,     11,   369,     0,    13, STR_6800_DIFFICULTY_LEVEL,    STR_018C_WINDOW_TITLE_DRAG_THIS}, // GDW_CAPTION
00375 {      WWT_PANEL,   RESIZE_NONE,  COLOUR_MAUVE,      0,   369,    14,    41, 0x0,                          STR_NULL},                        // GDW_UPPER_BG
00376 { WWT_PUSHTXTBTN,   RESIZE_NONE,  COLOUR_YELLOW,    10,    96,    16,    27, STR_6801_EASY,                STR_NULL},                        // GDW_LVL_EASY
00377 { WWT_PUSHTXTBTN,   RESIZE_NONE,  COLOUR_YELLOW,    97,   183,    16,    27, STR_6802_MEDIUM,              STR_NULL},                        // GDW_LVL_MEDIUM
00378 { WWT_PUSHTXTBTN,   RESIZE_NONE,  COLOUR_YELLOW,   184,   270,    16,    27, STR_6803_HARD,                STR_NULL},                        // GDW_LVL_HARD
00379 { WWT_PUSHTXTBTN,   RESIZE_NONE,  COLOUR_YELLOW,   271,   357,    16,    27, STR_6804_CUSTOM,              STR_NULL},                        // GDW_LVL_CUSTOM
00380 {    WWT_TEXTBTN,   RESIZE_NONE,  COLOUR_GREEN,     10,   357,    28,    39, STR_6838_SHOW_HI_SCORE_CHART, STR_NULL},                        // GDW_HIGHSCORE
00381 {      WWT_PANEL,   RESIZE_NONE,  COLOUR_MAUVE,      0,   369,    42,   262, 0x0,                          STR_NULL},                        // GDW_SETTING_BG
00382 {      WWT_PANEL,   RESIZE_NONE,  COLOUR_MAUVE,      0,   369,   263,   278, 0x0,                          STR_NULL},                        // GDW_LOWER_BG
00383 { WWT_PUSHTXTBTN,   RESIZE_NONE,  COLOUR_YELLOW,   105,   185,   265,   276, STR_OPTIONS_SAVE_CHANGES,     STR_NULL},                        // GDW_ACCEPT
00384 { WWT_PUSHTXTBTN,   RESIZE_NONE,  COLOUR_YELLOW,   186,   266,   265,   276, STR_012E_CANCEL,              STR_NULL},                        // GDW_CANCEL
00385 {   WIDGETS_END},
00386 };
00387 
00388 /* Window definition for the game difficulty settings window */
00389 static const WindowDesc _game_difficulty_desc(
00390   WDP_CENTER, WDP_CENTER, 370, 279, 370, 279,
00391   WC_GAME_OPTIONS, WC_NONE,
00392   WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET,
00393   _game_difficulty_widgets
00394 );
00395 
00396 void SetDifficultyLevel(int mode, DifficultySettings *gm_opt);
00397 
00398 struct GameDifficultyWindow : public Window {
00399 private:
00400   static const uint GAME_DIFFICULTY_NUM = 18;
00401   bool clicked_increase;
00402   uint8 clicked_button;
00403   uint8 timeout;
00404 
00405   /* Temporary holding place of values in the difficulty window until 'Save' is clicked */
00406   GameSettings opt_mod_temp;
00407 
00408   enum {
00409     GAMEDIFF_WND_TOP_OFFSET = 45,
00410     GAMEDIFF_WND_ROWSIZE    = 9,
00411     NO_SETTINGS_BUTTON = 0xFF,
00412   };
00413 
00414   /* Names of the game difficulty settings window */
00415   enum GameDifficultyWidgets {
00416     GDW_CLOSEBOX = 0,
00417     GDW_CAPTION,
00418     GDW_UPPER_BG,
00419     GDW_LVL_EASY,
00420     GDW_LVL_MEDIUM,
00421     GDW_LVL_HARD,
00422     GDW_LVL_CUSTOM,
00423     GDW_HIGHSCORE,
00424     GDW_SETTING_BG,
00425     GDW_LOWER_BG,
00426     GDW_ACCEPT,
00427     GDW_CANCEL,
00428   };
00429 
00430 public:
00431   GameDifficultyWindow() : Window(&_game_difficulty_desc)
00432   {
00433     /* Copy current settings (ingame or in intro) to temporary holding place
00434      * change that when setting stuff, copy back on clicking 'OK' */
00435     this->opt_mod_temp = (_game_mode == GM_MENU) ? _settings_newgame : _settings_game;
00436     this->clicked_increase = false;
00437     this->clicked_button = NO_SETTINGS_BUTTON;
00438     this->timeout = 0;
00439     /* Hide the closebox to make sure that the user aborts or confirms his changes */
00440     this->HideWidget(GDW_CLOSEBOX);
00441     this->widget[GDW_CAPTION].left = 0;
00442     /* Setup disabled buttons when creating window
00443      * disable all other difficulty buttons during gameplay except for 'custom' */
00444     this->SetWidgetsDisabledState(_game_mode == GM_NORMAL,
00445       GDW_LVL_EASY,
00446       GDW_LVL_MEDIUM,
00447       GDW_LVL_HARD,
00448       GDW_LVL_CUSTOM,
00449       WIDGET_LIST_END);
00450     this->SetWidgetDisabledState(GDW_HIGHSCORE, _game_mode == GM_EDITOR || _networking); // highscore chart in multiplayer
00451     this->SetWidgetDisabledState(GDW_ACCEPT, _networking && !_network_server); // Save-button in multiplayer (and if client)
00452     this->LowerWidget(GDW_LVL_EASY + this->opt_mod_temp.difficulty.diff_level);
00453     this->FindWindowPlacementAndResize(&_game_difficulty_desc);
00454   }
00455 
00456   virtual void OnPaint()
00457   {
00458     this->DrawWidgets();
00459 
00460     uint i;
00461     const SettingDesc *sd = GetSettingFromName("difficulty.max_no_competitors", &i);
00462     int y = GAMEDIFF_WND_TOP_OFFSET;
00463     StringID str = STR_6805_MAXIMUM_NO_COMPETITORS;
00464     for (i = 0; i < GAME_DIFFICULTY_NUM; i++, sd++) {
00465       const SettingDescBase *sdb = &sd->desc;
00466       /* skip deprecated difficulty options */
00467       if (!SlIsObjectCurrentlyValid(sd->save.version_from, sd->save.version_to)) continue;
00468       int32 value = (int32)ReadValue(GetVariableAddress(&this->opt_mod_temp, &sd->save), sd->save.conv);
00469       bool editable = (_game_mode == GM_MENU || (sdb->flags & SGF_NEWGAME_ONLY) == 0);
00470 
00471       DrawArrowButtons(5, y, COLOUR_YELLOW,
00472           (this->clicked_button == i) ? 1 + !!this->clicked_increase : 0,
00473           editable && sdb->min != value,
00474           editable && sdb->max != value);
00475 
00476       value += sdb->str;
00477       SetDParam(0, value);
00478       DrawString(30, y, str, TC_FROMSTRING);
00479 
00480       y += GAMEDIFF_WND_ROWSIZE + 2; // space items apart a bit
00481       str++;
00482     }
00483   }
00484 
00485   virtual void OnClick(Point pt, int widget)
00486   {
00487     switch (widget) {
00488       case GDW_SETTING_BG: { // Difficulty settings widget, decode click
00489         /* Don't allow clients to make any changes */
00490         if (_networking && !_network_server) return;
00491 
00492         const int x = pt.x - 5;
00493         if (!IsInsideMM(x, 0, 21)) return; // Button area
00494 
00495         const int y = pt.y - GAMEDIFF_WND_TOP_OFFSET;
00496         if (y < 0) return;
00497 
00498         /* Get button from Y coord. */
00499         uint8 btn = y / (GAMEDIFF_WND_ROWSIZE + 2);
00500         if (btn >= 1) btn++; // Skip the deprecated option "competitor start time"
00501         if (btn >= 8) btn++; // Skip the deprecated option "competitor intelligence"
00502         if (btn >= GAME_DIFFICULTY_NUM || y % (GAMEDIFF_WND_ROWSIZE + 2) >= 9) return;
00503 
00504         uint i;
00505         const SettingDesc *sd = GetSettingFromName("difficulty.max_no_competitors", &i) + btn;
00506         const SettingDescBase *sdb = &sd->desc;
00507 
00508         /* Clicked disabled button? */
00509         bool editable = (_game_mode == GM_MENU || (sdb->flags & SGF_NEWGAME_ONLY) == 0);
00510         if (!editable) return;
00511 
00512         this->timeout = 5;
00513         int32 val = (int32)ReadValue(GetVariableAddress(&this->opt_mod_temp, &sd->save), sd->save.conv);
00514 
00515         if (x >= 10) {
00516           /* Increase button clicked */
00517           val = min(val + sdb->interval, sdb->max);
00518           this->clicked_increase = true;
00519         } else {
00520           /* Decrease button clicked */
00521           val -= sdb->interval;
00522           val = max(val, sdb->min);
00523           this->clicked_increase = false;
00524         }
00525         this->clicked_button = btn;
00526 
00527         /* save value in temporary variable */
00528         WriteValue(GetVariableAddress(&this->opt_mod_temp, &sd->save), sd->save.conv, val);
00529         this->RaiseWidget(GDW_LVL_EASY + this->opt_mod_temp.difficulty.diff_level);
00530         SetDifficultyLevel(3, &this->opt_mod_temp.difficulty); // set difficulty level to custom
00531         this->LowerWidget(GDW_LVL_CUSTOM);
00532         this->SetDirty();
00533       } break;
00534 
00535       case GDW_LVL_EASY:
00536       case GDW_LVL_MEDIUM:
00537       case GDW_LVL_HARD:
00538       case GDW_LVL_CUSTOM:
00539         /* temporarily change difficulty level */
00540         this->RaiseWidget(GDW_LVL_EASY + this->opt_mod_temp.difficulty.diff_level);
00541         SetDifficultyLevel(widget - GDW_LVL_EASY, &this->opt_mod_temp.difficulty);
00542         this->LowerWidget(GDW_LVL_EASY + this->opt_mod_temp.difficulty.diff_level);
00543         this->SetDirty();
00544         break;
00545 
00546       case GDW_HIGHSCORE: // Highscore Table
00547         ShowHighscoreTable(this->opt_mod_temp.difficulty.diff_level, -1);
00548         break;
00549 
00550       case GDW_ACCEPT: { // Save button - save changes
00551         GameSettings *opt_ptr = (_game_mode == GM_MENU) ? &_settings_newgame : &_settings_game;
00552 
00553         uint i;
00554         const SettingDesc *sd = GetSettingFromName("difficulty.max_no_competitors", &i);
00555         for (uint btn = 0; btn != GAME_DIFFICULTY_NUM; btn++, sd++) {
00556           int32 new_val = (int32)ReadValue(GetVariableAddress(&this->opt_mod_temp, &sd->save), sd->save.conv);
00557           int32 cur_val = (int32)ReadValue(GetVariableAddress(opt_ptr, &sd->save), sd->save.conv);
00558           /* if setting has changed, change it */
00559           if (new_val != cur_val) {
00560             DoCommandP(0, i + btn, new_val, CMD_CHANGE_SETTING);
00561           }
00562         }
00563 
00564         GetSettingFromName("difficulty.diff_level", &i);
00565         DoCommandP(0, i, this->opt_mod_temp.difficulty.diff_level, CMD_CHANGE_SETTING);
00566         delete this;
00567         /* If we are in the editor, we should reload the economy.
00568          * This way when you load a game, the max loan and interest rate
00569          * are loaded correctly. */
00570         if (_game_mode == GM_EDITOR) StartupEconomy();
00571         break;
00572       }
00573 
00574       case GDW_CANCEL: // Cancel button - close window, abandon changes
00575         delete this;
00576         break;
00577     }
00578   }
00579 
00580   virtual void OnTick()
00581   {
00582     if (this->timeout != 0) {
00583       this->timeout--;
00584       if (this->timeout == 0) this->clicked_button = NO_SETTINGS_BUTTON;
00585       this->SetDirty();
00586     }
00587   }
00588 };
00589 
00590 void ShowGameDifficulty()
00591 {
00592   DeleteWindowById(WC_GAME_OPTIONS, 0);
00593   new GameDifficultyWindow();
00594 }
00595 
00596 static const int SETTING_HEIGHT = 11; 
00597 static const int LEVEL_WIDTH = 15;    
00598 
00603 enum SettingEntryFlags {
00604   SEF_LEFT_DEPRESSED  = 0x01, 
00605   SEF_RIGHT_DEPRESSED = 0x02, 
00606   SEF_BUTTONS_MASK = (SEF_LEFT_DEPRESSED | SEF_RIGHT_DEPRESSED), 
00607 
00608   SEF_LAST_FIELD = 0x04, 
00609 
00610   /* Entry kind */
00611   SEF_SETTING_KIND = 0x10, 
00612   SEF_SUBTREE_KIND = 0x20, 
00613   SEF_KIND_MASK    = (SEF_SETTING_KIND | SEF_SUBTREE_KIND), 
00614 };
00615 
00616 struct SettingsPage; // Forward declaration
00617 
00619 struct SettingEntrySubtree {
00620   SettingsPage *page; 
00621   bool folded;        
00622   StringID title;     
00623 };
00624 
00626 struct SettingEntrySetting {
00627   const char *name;           
00628   const SettingDesc *setting; 
00629   uint index;                 
00630 };
00631 
00633 struct SettingEntry {
00634   byte flags; 
00635   byte level; 
00636   union {
00637     SettingEntrySetting entry; 
00638     SettingEntrySubtree sub;   
00639   } d; 
00640 
00641   SettingEntry(const char *nm);
00642   SettingEntry(SettingsPage *sub, StringID title);
00643 
00644   void Init(byte level, bool last_field);
00645   void FoldAll();
00646   void SetButtons(byte new_val);
00647 
00648   uint Length() const;
00649   SettingEntry *FindEntry(uint row, uint *cur_row);
00650 
00651   uint Draw(GameSettings *settings_ptr, int base_x, int base_y, int max_x, uint first_row, uint max_row, uint cur_row, uint parent_last);
00652 
00653 private:
00654   void DrawSetting(GameSettings *settings_ptr, const SettingDesc *sd, int x, int y, int max_x, int state);
00655 };
00656 
00658 struct SettingsPage {
00659   SettingEntry *entries; 
00660   byte num;              
00661 
00662   void Init(byte level = 0);
00663   void FoldAll();
00664 
00665   uint Length() const;
00666   SettingEntry *FindEntry(uint row, uint *cur_row) const;
00667 
00668   uint Draw(GameSettings *settings_ptr, int base_x, int base_y, int max_x, uint first_row, uint max_row, uint cur_row = 0, uint parent_last = 0) const;
00669 };
00670 
00671 
00672 /* == SettingEntry methods == */
00673 
00678 SettingEntry::SettingEntry(const char *nm)
00679 {
00680   this->flags = SEF_SETTING_KIND;
00681   this->level = 0;
00682   this->d.entry.name = nm;
00683   this->d.entry.setting = NULL;
00684   this->d.entry.index = 0;
00685 }
00686 
00692 SettingEntry::SettingEntry(SettingsPage *sub, StringID title)
00693 {
00694   this->flags = SEF_SUBTREE_KIND;
00695   this->level = 0;
00696   this->d.sub.page = sub;
00697   this->d.sub.folded = true;
00698   this->d.sub.title = title;
00699 }
00700 
00706 void SettingEntry::Init(byte level, bool last_field)
00707 {
00708   this->level = level;
00709   if (last_field) this->flags |= SEF_LAST_FIELD;
00710 
00711   switch (this->flags & SEF_KIND_MASK) {
00712     case SEF_SETTING_KIND:
00713       this->d.entry.setting = GetSettingFromName(this->d.entry.name, &this->d.entry.index);
00714       assert(this->d.entry.setting != NULL);
00715       break;
00716     case SEF_SUBTREE_KIND:
00717       this->d.sub.page->Init(level + 1);
00718       break;
00719     default: NOT_REACHED();
00720   }
00721 }
00722 
00724 void SettingEntry::FoldAll()
00725 {
00726   switch(this->flags & SEF_KIND_MASK) {
00727     case SEF_SETTING_KIND:
00728       break;
00729 
00730     case SEF_SUBTREE_KIND:
00731       this->d.sub.folded = true;
00732       this->d.sub.page->FoldAll();
00733       break;
00734 
00735     default: NOT_REACHED();
00736   }
00737 }
00738 
00739 
00745 void SettingEntry::SetButtons(byte new_val)
00746 {
00747   assert((new_val & ~SEF_BUTTONS_MASK) == 0); // Should not touch any flags outside the buttons
00748   this->flags = (this->flags & ~SEF_BUTTONS_MASK) | new_val;
00749 }
00750 
00752 uint SettingEntry::Length() const
00753 {
00754   switch (this->flags & SEF_KIND_MASK) {
00755     case SEF_SETTING_KIND:
00756       return 1;
00757     case SEF_SUBTREE_KIND:
00758       if (this->d.sub.folded) return 1; // Only displaying the title
00759 
00760       return 1 + this->d.sub.page->Length(); // 1 extra row for the title
00761     default: NOT_REACHED();
00762   }
00763 }
00764 
00771 SettingEntry *SettingEntry::FindEntry(uint row_num, uint *cur_row)
00772 {
00773   if (row_num == *cur_row) return this;
00774 
00775   switch (this->flags & SEF_KIND_MASK) {
00776     case SEF_SETTING_KIND:
00777       (*cur_row)++;
00778       break;
00779     case SEF_SUBTREE_KIND:
00780       (*cur_row)++; // add one for row containing the title
00781       if (this->d.sub.folded) {
00782         break;
00783       }
00784 
00785       /* sub-page is visible => search it too */
00786       return this->d.sub.page->FindEntry(row_num, cur_row);
00787     default: NOT_REACHED();
00788   }
00789   return NULL;
00790 }
00791 
00818 uint SettingEntry::Draw(GameSettings *settings_ptr, int base_x, int base_y, int max_x, uint first_row, uint max_row, uint cur_row, uint parent_last)
00819 {
00820   if (cur_row >= max_row) return cur_row;
00821 
00822   int x = base_x;
00823   int y = base_y;
00824   if (cur_row >= first_row) {
00825     int colour = _colour_gradient[COLOUR_ORANGE][4];
00826     y = base_y + (cur_row - first_row) * SETTING_HEIGHT; // Compute correct y start position
00827 
00828     /* Draw vertical for parent nesting levels */
00829     for (uint lvl = 0; lvl < this->level; lvl++) {
00830       if (!HasBit(parent_last, lvl)) GfxDrawLine(x + 4, y, x + 4, y + SETTING_HEIGHT - 1, colour);
00831       x += LEVEL_WIDTH;
00832     }
00833     /* draw own |- prefix */
00834     int halfway_y = y + SETTING_HEIGHT / 2;
00835     int bottom_y = (flags & SEF_LAST_FIELD) ? halfway_y : y + SETTING_HEIGHT - 1;
00836     GfxDrawLine(x + 4, y, x + 4, bottom_y, colour);
00837     /* Small horizontal line from the last vertical line */
00838     GfxDrawLine(x + 4, halfway_y, x + LEVEL_WIDTH - 4, halfway_y, colour);
00839     x += LEVEL_WIDTH;
00840   }
00841 
00842   switch(this->flags & SEF_KIND_MASK) {
00843     case SEF_SETTING_KIND:
00844       if (cur_row >= first_row) {
00845         DrawSetting(settings_ptr, this->d.entry.setting, x, y, max_x, this->flags & SEF_BUTTONS_MASK);
00846       }
00847       cur_row++;
00848       break;
00849     case SEF_SUBTREE_KIND:
00850       if (cur_row >= first_row) {
00851         DrawSprite((this->d.sub.folded ? SPR_CIRCLE_FOLDED : SPR_CIRCLE_UNFOLDED), PAL_NONE, x, y);
00852         DrawStringTruncated(x + 12, y, this->d.sub.title, TC_FROMSTRING, max_x - x - 12);
00853       }
00854       cur_row++;
00855       if (!this->d.sub.folded) {
00856         if (this->flags & SEF_LAST_FIELD) {
00857           assert(this->level < sizeof(parent_last));
00858           SetBit(parent_last, this->level); // Add own last-field state
00859         }
00860 
00861         cur_row = this->d.sub.page->Draw(settings_ptr, base_x, base_y, max_x, first_row, max_row, cur_row, parent_last);
00862       }
00863       break;
00864     default: NOT_REACHED();
00865   }
00866   return cur_row;
00867 }
00868 
00878 void SettingEntry::DrawSetting(GameSettings *settings_ptr, const SettingDesc *sd, int x, int y, int max_x, int state)
00879 {
00880   const SettingDescBase *sdb = &sd->desc;
00881   const void *var = GetVariableAddress(settings_ptr, &sd->save);
00882   bool editable = true;
00883   bool disabled = false;
00884 
00885   /* We do not allow changes of some items when we are a client in a networkgame */
00886   if (!(sd->save.conv & SLF_NETWORK_NO) && _networking && !_network_server) editable = false;
00887   if ((sdb->flags & SGF_NETWORK_ONLY) && !_networking) editable = false;
00888   if ((sdb->flags & SGF_NO_NETWORK) && _networking) editable = false;
00889 
00890   if (sdb->cmd == SDT_BOOLX) {
00891     static const Colours _bool_ctabs[2][2] = {{COLOUR_CREAM, COLOUR_RED}, {COLOUR_DARK_GREEN, COLOUR_GREEN}};
00892     /* Draw checkbox for boolean-value either on/off */
00893     bool on = (*(bool*)var);
00894 
00895     DrawFrameRect(x, y, x + 19, y + 8, _bool_ctabs[!!on][!!editable], on ? FR_LOWERED : FR_NONE);
00896     SetDParam(0, on ? STR_CONFIG_SETTING_ON : STR_CONFIG_SETTING_OFF);
00897   } else {
00898     int32 value;
00899 
00900     value = (int32)ReadValue(var, sd->save.conv);
00901 
00902     /* Draw [<][>] boxes for settings of an integer-type */
00903     DrawArrowButtons(x, y, COLOUR_YELLOW, state, editable && value != (sdb->flags & SGF_0ISDISABLED ? 0 : sdb->min), editable && value != sdb->max);
00904 
00905     disabled = (value == 0) && (sdb->flags & SGF_0ISDISABLED);
00906     if (disabled) {
00907       SetDParam(0, STR_CONFIG_SETTING_DISABLED);
00908     } else {
00909       if (sdb->flags & SGF_CURRENCY) {
00910         SetDParam(0, STR_CONFIG_SETTING_CURRENCY);
00911       } else if (sdb->flags & SGF_MULTISTRING) {
00912         SetDParam(0, sdb->str + value + 1);
00913       } else {
00914         SetDParam(0, (sdb->flags & SGF_NOCOMMA) ? STR_CONFIG_SETTING_INT32 : STR_7024);
00915       }
00916       SetDParam(1, value);
00917     }
00918   }
00919   DrawStringTruncated(x + 25, y, (sdb->str) + disabled, TC_FROMSTRING, max_x - x - 25);
00920 }
00921 
00922 
00923 /* == SettingsPage methods == */
00924 
00929 void SettingsPage::Init(byte level)
00930 {
00931   for (uint field = 0; field < this->num; field++) {
00932     this->entries[field].Init(level, field + 1 == num);
00933   }
00934 }
00935 
00937 void SettingsPage::FoldAll()
00938 {
00939   for (uint field = 0; field < this->num; field++) {
00940     this->entries[field].FoldAll();
00941   }
00942 }
00943 
00945 uint SettingsPage::Length() const
00946 {
00947   uint length = 0;
00948   for (uint field = 0; field < this->num; field++) {
00949     length += this->entries[field].Length();
00950   }
00951   return length;
00952 }
00953 
00960 SettingEntry *SettingsPage::FindEntry(uint row_num, uint *cur_row) const
00961 {
00962   SettingEntry *pe = NULL;
00963 
00964   for (uint field = 0; field < this->num; field++) {
00965     pe = this->entries[field].FindEntry(row_num, cur_row);
00966     if (pe != NULL) {
00967       break;
00968     }
00969   }
00970   return pe;
00971 }
00972 
00990 uint SettingsPage::Draw(GameSettings *settings_ptr, int base_x, int base_y, int max_x, uint first_row, uint max_row, uint cur_row, uint parent_last) const
00991 {
00992   if (cur_row >= max_row) return cur_row;
00993 
00994   for (uint i = 0; i < this->num; i++) {
00995     cur_row = this->entries[i].Draw(settings_ptr, base_x, base_y, max_x, first_row, max_row, cur_row, parent_last);
00996     if (cur_row >= max_row) {
00997       break;
00998     }
00999   }
01000   return cur_row;
01001 }
01002 
01003 
01004 static SettingEntry _settings_ui_display[] = {
01005   SettingEntry("gui.vehicle_speed"),
01006   SettingEntry("gui.status_long_date"),
01007   SettingEntry("gui.date_format_in_default_names"),
01008   SettingEntry("gui.population_in_label"),
01009   SettingEntry("gui.measure_tooltip"),
01010   SettingEntry("gui.loading_indicators"),
01011   SettingEntry("gui.liveries"),
01012   SettingEntry("gui.show_track_reservation"),
01013   SettingEntry("gui.expenses_layout"),
01014 };
01016 static SettingsPage _settings_ui_display_page = {_settings_ui_display, lengthof(_settings_ui_display)};
01017 
01018 static SettingEntry _settings_ui_interaction[] = {
01019   SettingEntry("gui.window_snap_radius"),
01020   SettingEntry("gui.window_soft_limit"),
01021   SettingEntry("gui.link_terraform_toolbar"),
01022   SettingEntry("gui.prefer_teamchat"),
01023   SettingEntry("gui.autoscroll"),
01024   SettingEntry("gui.reverse_scroll"),
01025   SettingEntry("gui.smooth_scroll"),
01026   SettingEntry("gui.left_mouse_btn_scrolling"),
01027   /* While the horizontal scrollwheel scrolling is written as general code, only
01028    *  the cocoa (OSX) driver generates input for it.
01029    *  Since it's also able to completely disable the scrollwheel will we display it on all platforms anyway */
01030   SettingEntry("gui.scrollwheel_scrolling"),
01031   SettingEntry("gui.scrollwheel_multiplier"),
01032 #ifdef __APPLE__
01033   /* We might need to emulate a right mouse button on mac */
01034   SettingEntry("gui.right_mouse_btn_emulation"),
01035 #endif
01036 };
01038 static SettingsPage _settings_ui_interaction_page = {_settings_ui_interaction, lengthof(_settings_ui_interaction)};
01039 
01040 static SettingEntry _settings_ui[] = {
01041   SettingEntry(&_settings_ui_display_page, STR_CONFIG_SETTING_DISPLAY_OPTIONS),
01042   SettingEntry(&_settings_ui_interaction_page, STR_CONFIG_SETTING_INTERACTION),
01043   SettingEntry("gui.show_finances"),
01044   SettingEntry("gui.errmsg_duration"),
01045   SettingEntry("gui.toolbar_pos"),
01046   SettingEntry("gui.pause_on_newgame"),
01047   SettingEntry("gui.advanced_vehicle_list"),
01048   SettingEntry("gui.timetable_in_ticks"),
01049   SettingEntry("gui.quick_goto"),
01050   SettingEntry("gui.default_rail_type"),
01051   SettingEntry("gui.always_build_infrastructure"),
01052   SettingEntry("gui.persistent_buildingtools"),
01053   SettingEntry("gui.coloured_news_year"),
01054 };
01056 static SettingsPage _settings_ui_page = {_settings_ui, lengthof(_settings_ui)};
01057 
01058 static SettingEntry _settings_construction_signals[] = {
01059   SettingEntry("construction.signal_side"),
01060   SettingEntry("gui.enable_signal_gui"),
01061   SettingEntry("gui.drag_signals_density"),
01062   SettingEntry("gui.semaphore_build_before"),
01063   SettingEntry("gui.default_signal_type"),
01064   SettingEntry("gui.cycle_signal_types"),
01065 };
01067 static SettingsPage _settings_construction_signals_page = {_settings_construction_signals, lengthof(_settings_construction_signals)};
01068 
01069 static SettingEntry _settings_construction[] = {
01070   SettingEntry(&_settings_construction_signals_page, STR_CONFIG_SETTING_CONSTRUCTION_SIGNALS),
01071   SettingEntry("construction.build_on_slopes"),
01072   SettingEntry("construction.autoslope"),
01073   SettingEntry("construction.extra_dynamite"),
01074   SettingEntry("construction.longbridges"),
01075   SettingEntry("station.always_small_airport"),
01076   SettingEntry("construction.freeform_edges"),
01077 };
01079 static SettingsPage _settings_construction_page = {_settings_construction, lengthof(_settings_construction)};
01080 
01081 static SettingEntry _settings_stations_cargo[] = {
01082   SettingEntry("order.improved_load"),
01083   SettingEntry("order.gradual_loading"),
01084   SettingEntry("order.selectgoods"),
01085 };
01087 static SettingsPage _settings_stations_cargo_page = {_settings_stations_cargo, lengthof(_settings_stations_cargo)};
01088 
01089 static SettingEntry _settings_stations[] = {
01090   SettingEntry(&_settings_stations_cargo_page, STR_CONFIG_SETTING_STATIONS_CARGOHANDLING),
01091   SettingEntry("station.join_stations"),
01092   SettingEntry("station.nonuniform_stations"),
01093   SettingEntry("station.adjacent_stations"),
01094   SettingEntry("station.distant_join_stations"),
01095   SettingEntry("station.station_spread"),
01096   SettingEntry("economy.station_noise_level"),
01097   SettingEntry("station.modified_catchment"),
01098   SettingEntry("construction.road_stop_on_town_road"),
01099   SettingEntry("construction.road_stop_on_competitor_road"),
01100 };
01102 static SettingsPage _settings_stations_page = {_settings_stations, lengthof(_settings_stations)};
01103 
01104 static SettingEntry _settings_economy_towns[] = {
01105   SettingEntry("economy.bribe"),
01106   SettingEntry("economy.exclusive_rights"),
01107   SettingEntry("economy.town_layout"),
01108   SettingEntry("economy.allow_town_roads"),
01109   SettingEntry("economy.mod_road_rebuild"),
01110   SettingEntry("economy.town_growth_rate"),
01111   SettingEntry("economy.larger_towns"),
01112   SettingEntry("economy.initial_city_size"),
01113 };
01115 static SettingsPage _settings_economy_towns_page = {_settings_economy_towns, lengthof(_settings_economy_towns)};
01116 
01117 static SettingEntry _settings_economy_industries[] = {
01118   SettingEntry("construction.raw_industry_construction"),
01119   SettingEntry("economy.multiple_industry_per_town"),
01120   SettingEntry("economy.same_industry_close"),
01121   SettingEntry("game_creation.oil_refinery_limit"),
01122 };
01124 static SettingsPage _settings_economy_industries_page = {_settings_economy_industries, lengthof(_settings_economy_industries)};
01125 
01126 static SettingEntry _settings_economy[] = {
01127   SettingEntry(&_settings_economy_towns_page, STR_CONFIG_SETTING_ECONOMY_TOWNS),
01128   SettingEntry(&_settings_economy_industries_page, STR_CONFIG_SETTING_ECONOMY_INDUSTRIES),
01129   SettingEntry("economy.inflation"),
01130   SettingEntry("economy.smooth_economy"),
01131 };
01133 static SettingsPage _settings_economy_page = {_settings_economy, lengthof(_settings_economy)};
01134 
01135 static SettingEntry _settings_ai_npc[] = {
01136   SettingEntry("ai.ai_in_multiplayer"),
01137   SettingEntry("ai.ai_disable_veh_train"),
01138   SettingEntry("ai.ai_disable_veh_roadveh"),
01139   SettingEntry("ai.ai_disable_veh_aircraft"),
01140   SettingEntry("ai.ai_disable_veh_ship"),
01141   SettingEntry("ai.ai_max_opcode_till_suspend"),
01142 };
01144 static SettingsPage _settings_ai_npc_page = {_settings_ai_npc, lengthof(_settings_ai_npc)};
01145 
01146 static SettingEntry _settings_ai[] = {
01147   SettingEntry(&_settings_ai_npc_page, STR_CONFIG_SETTING_AI_NPC),
01148   SettingEntry("economy.give_money"),
01149   SettingEntry("economy.allow_shares"),
01150 };
01152 static SettingsPage _settings_ai_page = {_settings_ai, lengthof(_settings_ai)};
01153 
01154 static SettingEntry _settings_vehicles_routing[] = {
01155   SettingEntry("pf.pathfinder_for_trains"),
01156   SettingEntry("pf.forbid_90_deg"),
01157   SettingEntry("pf.pathfinder_for_roadvehs"),
01158   SettingEntry("pf.roadveh_queue"),
01159   SettingEntry("pf.pathfinder_for_ships"),
01160 };
01162 static SettingsPage _settings_vehicles_routing_page = {_settings_vehicles_routing, lengthof(_settings_vehicles_routing)};
01163 
01164 static SettingEntry _settings_vehicles_autorenew[] = {
01165   SettingEntry("gui.autorenew"),
01166   SettingEntry("gui.autorenew_months"),
01167   SettingEntry("gui.autorenew_money"),
01168 };
01170 static SettingsPage _settings_vehicles_autorenew_page = {_settings_vehicles_autorenew, lengthof(_settings_vehicles_autorenew)};
01171 
01172 static SettingEntry _settings_vehicles_servicing[] = {
01173   SettingEntry("vehicle.servint_ispercent"),
01174   SettingEntry("vehicle.servint_trains"),
01175   SettingEntry("vehicle.servint_roadveh"),
01176   SettingEntry("vehicle.servint_ships"),
01177   SettingEntry("vehicle.servint_aircraft"),
01178   SettingEntry("order.no_servicing_if_no_breakdowns"),
01179   SettingEntry("order.serviceathelipad"),
01180 };
01182 static SettingsPage _settings_vehicles_servicing_page = {_settings_vehicles_servicing, lengthof(_settings_vehicles_servicing)};
01183 
01184 static SettingEntry _settings_vehicles_trains[] = {
01185   SettingEntry("vehicle.train_acceleration_model"),
01186   SettingEntry("vehicle.mammoth_trains"),
01187   SettingEntry("gui.lost_train_warn"),
01188   SettingEntry("vehicle.wagon_speed_limits"),
01189   SettingEntry("vehicle.disable_elrails"),
01190   SettingEntry("vehicle.freight_trains"),
01191 };
01193 static SettingsPage _settings_vehicles_trains_page = {_settings_vehicles_trains, lengthof(_settings_vehicles_trains)};
01194 
01195 static SettingEntry _settings_vehicles[] = {
01196   SettingEntry(&_settings_vehicles_routing_page, STR_CONFIG_SETTING_VEHICLES_ROUTING),
01197   SettingEntry(&_settings_vehicles_autorenew_page, STR_CONFIG_SETTING_VEHICLES_AUTORENEW),
01198   SettingEntry(&_settings_vehicles_servicing_page, STR_CONFIG_SETTING_VEHICLES_SERVICING),
01199   SettingEntry(&_settings_vehicles_trains_page, STR_CONFIG_SETTING_VEHICLES_TRAINS),
01200   SettingEntry("order.gotodepot"),
01201   SettingEntry("gui.new_nonstop"),
01202   SettingEntry("gui.order_review_system"),
01203   SettingEntry("gui.vehicle_income_warn"),
01204   SettingEntry("vehicle.never_expire_vehicles"),
01205   SettingEntry("vehicle.max_trains"),
01206   SettingEntry("vehicle.max_roadveh"),
01207   SettingEntry("vehicle.max_aircraft"),
01208   SettingEntry("vehicle.max_ships"),
01209   SettingEntry("vehicle.plane_speed"),
01210   SettingEntry("order.timetabling"),
01211   SettingEntry("vehicle.dynamic_engines"),
01212 };
01214 static SettingsPage _settings_vehicles_page = {_settings_vehicles, lengthof(_settings_vehicles)};
01215 
01216 static SettingEntry _settings_main[] = {
01217   SettingEntry(&_settings_ui_page,           STR_CONFIG_SETTING_GUI),
01218   SettingEntry(&_settings_construction_page, STR_CONFIG_SETTING_CONSTRUCTION),
01219   SettingEntry(&_settings_vehicles_page,     STR_CONFIG_SETTING_VEHICLES),
01220   SettingEntry(&_settings_stations_page,     STR_CONFIG_SETTING_STATIONS),
01221   SettingEntry(&_settings_economy_page,      STR_CONFIG_SETTING_ECONOMY),
01222   SettingEntry(&_settings_ai_page,           STR_CONFIG_SETTING_AI),
01223 };
01224 
01226 static SettingsPage _settings_main_page = {_settings_main, lengthof(_settings_main)};
01227 
01229 enum GameSettingsWidgets {
01230   SETTINGSEL_OPTIONSPANEL = 2, 
01231   SETTINGSEL_SCROLLBAR,        
01232   SETTINGSEL_RESIZE,           
01233 };
01234 
01235 struct GameSettingsWindow : Window {
01236   static const int SETTINGTREE_LEFT_OFFSET; 
01237   static const int SETTINGTREE_TOP_OFFSET;  
01238 
01239   static GameSettings *settings_ptr;  
01240 
01241   SettingEntry *valuewindow_entry; 
01242   SettingEntry *clicked_entry; 
01243 
01244   GameSettingsWindow(const WindowDesc *desc) : Window(desc)
01245   {
01246     /* Check that the widget doesn't get moved without adapting the constant as well.
01247      *  - SETTINGTREE_LEFT_OFFSET should be 5 pixels to the right of the left edge of the panel
01248      *  - SETTINGTREE_TOP_OFFSET should be 5 pixels below the top edge of the panel
01249      */
01250     assert(this->widget[SETTINGSEL_OPTIONSPANEL].left + 5 == SETTINGTREE_LEFT_OFFSET);
01251     assert(this->widget[SETTINGSEL_OPTIONSPANEL].top + 5 == SETTINGTREE_TOP_OFFSET);
01252 
01253     static bool first_time = true;
01254 
01255     settings_ptr = (_game_mode == GM_MENU) ? &_settings_newgame : &_settings_game;
01256 
01257     /* Build up the dynamic settings-array only once per OpenTTD session */
01258     if (first_time) {
01259       _settings_main_page.Init();
01260       first_time = false;
01261     } else {
01262       _settings_main_page.FoldAll(); // Close all sub-pages
01263     }
01264 
01265     this->valuewindow_entry = NULL; // No setting entry for which a entry window is opened
01266     this->clicked_entry = NULL; // No numeric setting buttons are depressed
01267     this->vscroll.pos = 0;
01268     this->vscroll.cap = (this->widget[SETTINGSEL_OPTIONSPANEL].bottom - this->widget[SETTINGSEL_OPTIONSPANEL].top - 8) / SETTING_HEIGHT;
01269     SetVScrollCount(this, _settings_main_page.Length());
01270 
01271     this->resize.step_height = SETTING_HEIGHT;
01272     this->resize.height = this->height;
01273     this->resize.step_width = 1;
01274     this->resize.width = this->width;
01275 
01276     this->FindWindowPlacementAndResize(desc);
01277   }
01278 
01279   virtual void OnPaint()
01280   {
01281     this->DrawWidgets();
01282     _settings_main_page.Draw(settings_ptr, SETTINGTREE_LEFT_OFFSET, SETTINGTREE_TOP_OFFSET,
01283         this->width - 13, this->vscroll.pos, this->vscroll.pos + this->vscroll.cap);
01284   }
01285 
01286   virtual void OnClick(Point pt, int widget)
01287   {
01288     if (widget != SETTINGSEL_OPTIONSPANEL) return;
01289 
01290     int y = pt.y - SETTINGTREE_TOP_OFFSET;  // Shift y coordinate
01291     if (y < 0) return;  // Clicked above first entry
01292 
01293     byte btn = this->vscroll.pos + y / SETTING_HEIGHT;  // Compute which setting is selected
01294     if (y % SETTING_HEIGHT > SETTING_HEIGHT - 2) return;  // Clicked too low at the setting
01295 
01296     uint cur_row = 0;
01297     SettingEntry *pe = _settings_main_page.FindEntry(btn, &cur_row);
01298 
01299     if (pe == NULL) return;  // Clicked below the last setting of the page
01300 
01301     int x = pt.x - SETTINGTREE_LEFT_OFFSET - (pe->level + 1) * LEVEL_WIDTH;  // Shift x coordinate
01302     if (x < 0) return;  // Clicked left of the entry
01303 
01304     if ((pe->flags & SEF_KIND_MASK) == SEF_SUBTREE_KIND) {
01305       pe->d.sub.folded = !pe->d.sub.folded; // Flip 'folded'-ness of the sub-page
01306 
01307       SetVScrollCount(this, _settings_main_page.Length());
01308       this->SetDirty();
01309       return;
01310     }
01311 
01312     assert((pe->flags & SEF_KIND_MASK) == SEF_SETTING_KIND);
01313     const SettingDesc *sd = pe->d.entry.setting;
01314 
01315     /* return if action is only active in network, or only settable by server */
01316     if (!(sd->save.conv & SLF_NETWORK_NO) && _networking && !_network_server) return;
01317     if ((sd->desc.flags & SGF_NETWORK_ONLY) && !_networking) return;
01318     if ((sd->desc.flags & SGF_NO_NETWORK) && _networking) return;
01319 
01320     void *var = GetVariableAddress(settings_ptr, &sd->save);
01321     int32 value = (int32)ReadValue(var, sd->save.conv);
01322 
01323     /* clicked on the icon on the left side. Either scroller or bool on/off */
01324     if (x < 21) {
01325       const SettingDescBase *sdb = &sd->desc;
01326       int32 oldvalue = value;
01327 
01328       switch (sdb->cmd) {
01329         case SDT_BOOLX: value ^= 1; break;
01330         case SDT_ONEOFMANY:
01331         case SDT_NUMX: {
01332           /* Add a dynamic step-size to the scroller. In a maximum of
01333            * 50-steps you should be able to get from min to max,
01334            * unless specified otherwise in the 'interval' variable
01335            * of the current setting. */
01336           uint32 step = (sdb->interval == 0) ? ((sdb->max - sdb->min) / 50) : sdb->interval;
01337           if (step == 0) step = 1;
01338 
01339           /* don't allow too fast scrolling */
01340           if ((this->flags4 & WF_TIMEOUT_MASK) > WF_TIMEOUT_TRIGGER) {
01341             _left_button_clicked = false;
01342             return;
01343           }
01344 
01345           /* Increase or decrease the value and clamp it to extremes */
01346           if (x >= 10) {
01347             value += step;
01348             if (value > sdb->max) value = sdb->max;
01349             if (value < sdb->min) value = sdb->min; // skip between "disabled" and minimum
01350           } else {
01351             value -= step;
01352             if (value < sdb->min) value = (sdb->flags & SGF_0ISDISABLED) ? 0 : sdb->min;
01353           }
01354 
01355           /* Set up scroller timeout for numeric values */
01356           if (value != oldvalue && !(sd->desc.flags & SGF_MULTISTRING)) {
01357             if (this->clicked_entry != NULL) { // Release previous buttons if any
01358               this->clicked_entry->SetButtons(0);
01359             }
01360             this->clicked_entry = pe;
01361             this->clicked_entry->SetButtons((x >= 10) ? SEF_RIGHT_DEPRESSED : SEF_LEFT_DEPRESSED);
01362             this->flags4 |= WF_TIMEOUT_BEGIN;
01363             _left_button_clicked = false;
01364           }
01365         } break;
01366 
01367         default: NOT_REACHED();
01368       }
01369 
01370       if (value != oldvalue) {
01371         SetSettingValue(pe->d.entry.index, value);
01372         this->SetDirty();
01373       }
01374     } else {
01375       /* only open editbox for types that its sensible for */
01376       if (sd->desc.cmd != SDT_BOOLX && !(sd->desc.flags & SGF_MULTISTRING)) {
01377         /* Show the correct currency-translated value */
01378         if (sd->desc.flags & SGF_CURRENCY) value *= _currency->rate;
01379 
01380         this->valuewindow_entry = pe;
01381         SetDParam(0, value);
01382         ShowQueryString(STR_CONFIG_SETTING_INT32, STR_CONFIG_SETTING_QUERY_CAPT, 10, 100, this, CS_NUMERAL, QSF_NONE);
01383       }
01384     }
01385   }
01386 
01387   virtual void OnTimeout()
01388   {
01389     if (this->clicked_entry != NULL) { // On timeout, release any depressed buttons
01390       this->clicked_entry->SetButtons(0);
01391       this->clicked_entry = NULL;
01392       this->SetDirty();
01393     }
01394   }
01395 
01396   virtual void OnQueryTextFinished(char *str)
01397   {
01398     if (!StrEmpty(str)) {
01399       assert(this->valuewindow_entry != NULL);
01400       assert((this->valuewindow_entry->flags & SEF_KIND_MASK) == SEF_SETTING_KIND);
01401       const SettingDesc *sd = this->valuewindow_entry->d.entry.setting;
01402       int32 value = atoi(str);
01403 
01404       /* Save the correct currency-translated value */
01405       if (sd->desc.flags & SGF_CURRENCY) value /= _currency->rate;
01406 
01407       SetSettingValue(this->valuewindow_entry->d.entry.index, value);
01408       this->SetDirty();
01409     }
01410   }
01411 
01412   virtual void OnResize(Point new_size, Point delta)
01413   {
01414     this->vscroll.cap += delta.y / SETTING_HEIGHT;
01415     SetVScrollCount(this, _settings_main_page.Length());
01416   }
01417 };
01418 
01419 GameSettings *GameSettingsWindow::settings_ptr = NULL;
01420 const int GameSettingsWindow::SETTINGTREE_LEFT_OFFSET = 5;
01421 const int GameSettingsWindow::SETTINGTREE_TOP_OFFSET = 19;
01422 
01423 static const Widget _settings_selection_widgets[] = {
01424 {   WWT_CLOSEBOX,   RESIZE_NONE,  COLOUR_MAUVE,     0,    10,     0,    13, STR_00C5,                        STR_018B_CLOSE_WINDOW},
01425 {    WWT_CAPTION,  RESIZE_RIGHT,  COLOUR_MAUVE,    11,   411,     0,    13, STR_CONFIG_SETTING_CAPTION,      STR_018C_WINDOW_TITLE_DRAG_THIS},
01426 {      WWT_PANEL,     RESIZE_RB,  COLOUR_MAUVE,     0,   399,    14,   187, 0x0,                             STR_NULL}, // SETTINGSEL_OPTIONSPANEL
01427 {  WWT_SCROLLBAR,    RESIZE_LRB,  COLOUR_MAUVE,   400,   411,    14,   175, 0x0,                             STR_0190_SCROLL_BAR_SCROLLS_LIST}, // SETTINGSEL_SCROLLBAR
01428 {  WWT_RESIZEBOX,   RESIZE_LRTB,  COLOUR_MAUVE,   400,   411,   176,   187, 0x0,                             STR_RESIZE_BUTTON}, // SETTINGSEL_RESIZE
01429 {   WIDGETS_END},
01430 };
01431 
01432 static const WindowDesc _settings_selection_desc(
01433   WDP_CENTER, WDP_CENTER, 412, 188, 450, 397,
01434   WC_GAME_OPTIONS, WC_NONE,
01435   WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET | WDF_RESIZABLE,
01436   _settings_selection_widgets
01437 );
01438 
01439 void ShowGameSettings()
01440 {
01441   DeleteWindowById(WC_GAME_OPTIONS, 0);
01442   new GameSettingsWindow(&_settings_selection_desc);
01443 }
01444 
01445 
01455 void DrawArrowButtons(int x, int y, Colours button_colour, byte state, bool clickable_left, bool clickable_right)
01456 {
01457   int colour = _colour_gradient[button_colour][2];
01458 
01459   DrawFrameRect(x,      y + 1, x +  9, y + 9, button_colour, (state == 1) ? FR_LOWERED : FR_NONE);
01460   DrawFrameRect(x + 10, y + 1, x + 19, y + 9, button_colour, (state == 2) ? FR_LOWERED : FR_NONE);
01461   DrawStringCentered(x +  5, y + 1, STR_6819, TC_FROMSTRING); // [<]
01462   DrawStringCentered(x + 15, y + 1, STR_681A, TC_FROMSTRING); // [>]
01463 
01464   /* Grey out the buttons that aren't clickable */
01465   if (!clickable_left) {
01466     GfxFillRect(x +  1, y + 1, x +  1 + 8, y + 8, colour, FILLRECT_CHECKER);
01467   }
01468   if (!clickable_right) {
01469     GfxFillRect(x + 11, y + 1, x + 11 + 8, y + 8, colour, FILLRECT_CHECKER);
01470   }
01471 }
01472 
01476 enum CustomCurrenciesWidgets {
01477   CUSTCURR_EXCHANGERATE = 0,
01478   CUSTCURR_SEPARATOR,
01479   CUSTCURR_PREFIX,
01480   CUSTCURR_SUFFIX,
01481   CUSTCURR_TO_EURO,
01482 };
01483 
01484 struct CustomCurrencyWindow : Window {
01485   char separator[2];
01486   int click;
01487   int query_widget;
01488 
01489   CustomCurrencyWindow(const WindowDesc *desc) : Window(desc)
01490   {
01491     this->separator[0] = _custom_currency.separator;
01492     this->separator[1] = '\0';
01493     this->FindWindowPlacementAndResize(desc);
01494   }
01495 
01496   virtual void OnPaint()
01497   {
01498     int x;
01499     int y = 20;
01500     this->DrawWidgets();
01501 
01502     /* exchange rate */
01503     DrawArrowButtons(10, y, COLOUR_YELLOW, GB(this->click, 0, 2), true, true);
01504     SetDParam(0, 1);
01505     SetDParam(1, 1);
01506     DrawString(35, y + 1, STR_CURRENCY_EXCHANGE_RATE, TC_FROMSTRING);
01507     y += 12;
01508 
01509     /* separator */
01510     DrawFrameRect(10, y + 1, 29, y + 9, COLOUR_DARK_BLUE, GB(this->click, 2, 2) ? FR_LOWERED : FR_NONE);
01511     x = DrawString(35, y + 1, STR_CURRENCY_SEPARATOR, TC_FROMSTRING);
01512     DoDrawString(this->separator, x + 4, y + 1, TC_ORANGE);
01513     y += 12;
01514 
01515     /* prefix */
01516     DrawFrameRect(10, y + 1, 29, y + 9, COLOUR_DARK_BLUE, GB(this->click, 4, 2) ? FR_LOWERED : FR_NONE);
01517     x = DrawString(35, y + 1, STR_CURRENCY_PREFIX, TC_FROMSTRING);
01518     DoDrawString(_custom_currency.prefix, x + 4, y + 1, TC_ORANGE);
01519     y += 12;
01520 
01521     /* suffix */
01522     DrawFrameRect(10, y + 1, 29, y + 9, COLOUR_DARK_BLUE, GB(this->click, 6, 2) ? FR_LOWERED : FR_NONE);
01523     x = DrawString(35, y + 1, STR_CURRENCY_SUFFIX, TC_FROMSTRING);
01524     DoDrawString(_custom_currency.suffix, x + 4, y + 1, TC_ORANGE);
01525     y += 12;
01526 
01527     /* switch to euro */
01528     DrawArrowButtons(10, y, COLOUR_YELLOW, GB(this->click, 8, 2), true, true);
01529     SetDParam(0, _custom_currency.to_euro);
01530     DrawString(35, y + 1, (_custom_currency.to_euro != CF_NOEURO) ? STR_CURRENCY_SWITCH_TO_EURO : STR_CURRENCY_SWITCH_TO_EURO_NEVER, TC_FROMSTRING);
01531     y += 12;
01532 
01533     /* Preview */
01534     y += 12;
01535     SetDParam(0, 10000);
01536     DrawString(35, y + 1, STR_CURRENCY_PREVIEW, TC_FROMSTRING);
01537   }
01538 
01539   virtual void OnClick(Point pt, int widget)
01540   {
01541     int line = (pt.y - 20) / 12;
01542     int len = 0;
01543     int x = pt.x;
01544     StringID str = 0;
01545     CharSetFilter afilter = CS_ALPHANUMERAL;
01546 
01547     switch (line) {
01548       case CUSTCURR_EXCHANGERATE:
01549         if (IsInsideMM(x, 10, 30)) { // clicked buttons
01550           if (x < 20) {
01551             if (_custom_currency.rate > 1) _custom_currency.rate--;
01552             this->click = 1 << (line * 2 + 0);
01553           } else {
01554             if (_custom_currency.rate < UINT16_MAX) _custom_currency.rate++;
01555             this->click = 1 << (line * 2 + 1);
01556           }
01557         } else { // enter text
01558           SetDParam(0, _custom_currency.rate);
01559           str = STR_CONFIG_SETTING_INT32;
01560           len = 5;
01561           afilter = CS_NUMERAL;
01562         }
01563         break;
01564 
01565       case CUSTCURR_SEPARATOR:
01566         if (IsInsideMM(x, 10, 30)) { // clicked button
01567           this->click = 1 << (line * 2 + 1);
01568         }
01569         SetDParamStr(0, this->separator);
01570         str = STR_JUST_RAW_STRING;
01571         len = 1;
01572         break;
01573 
01574       case CUSTCURR_PREFIX:
01575         if (IsInsideMM(x, 10, 30)) { // clicked button
01576           this->click = 1 << (line * 2 + 1);
01577         }
01578         SetDParamStr(0, _custom_currency.prefix);
01579         str = STR_JUST_RAW_STRING;
01580         len = 12;
01581         break;
01582 
01583       case CUSTCURR_SUFFIX:
01584         if (IsInsideMM(x, 10, 30)) { // clicked button
01585           this->click = 1 << (line * 2 + 1);
01586         }
01587         SetDParamStr(0, _custom_currency.suffix);
01588         str = STR_JUST_RAW_STRING;
01589         len = 12;
01590         break;
01591 
01592       case CUSTCURR_TO_EURO:
01593         if (IsInsideMM(x, 10, 30)) { // clicked buttons
01594           if (x < 20) {
01595             _custom_currency.to_euro = (_custom_currency.to_euro <= 2000) ? CF_NOEURO : _custom_currency.to_euro - 1;
01596             this->click = 1 << (line * 2 + 0);
01597           } else {
01598             _custom_currency.to_euro = Clamp(_custom_currency.to_euro + 1, 2000, MAX_YEAR);
01599             this->click = 1 << (line * 2 + 1);
01600           }
01601         } else { // enter text
01602           SetDParam(0, _custom_currency.to_euro);
01603           str = STR_CONFIG_SETTING_INT32;
01604           len = 7;
01605           afilter = CS_NUMERAL;
01606         }
01607         break;
01608     }
01609 
01610     if (len != 0) {
01611       this->query_widget = line;
01612       ShowQueryString(str, STR_CURRENCY_CHANGE_PARAMETER, len + 1, 250, this, afilter, QSF_NONE);
01613     }
01614 
01615     this->flags4 |= WF_TIMEOUT_BEGIN;
01616     this->SetDirty();
01617   }
01618 
01619   virtual void OnQueryTextFinished(char *str)
01620   {
01621     if (str == NULL) return;
01622 
01623     switch (this->query_widget) {
01624       case CUSTCURR_EXCHANGERATE:
01625         _custom_currency.rate = Clamp(atoi(str), 1, UINT16_MAX);
01626         break;
01627 
01628       case CUSTCURR_SEPARATOR: // Thousands seperator
01629         _custom_currency.separator = StrEmpty(str) ? ' ' : str[0];
01630         strecpy(this->separator, str, lastof(this->separator));
01631         break;
01632 
01633       case CUSTCURR_PREFIX:
01634         strecpy(_custom_currency.prefix, str, lastof(_custom_currency.prefix));
01635         break;
01636 
01637       case CUSTCURR_SUFFIX:
01638         strecpy(_custom_currency.suffix, str, lastof(_custom_currency.suffix));
01639         break;
01640 
01641       case CUSTCURR_TO_EURO: { // Year to switch to euro
01642         int val = atoi(str);
01643 
01644         _custom_currency.to_euro = (val < 2000 ? CF_NOEURO : min(val, MAX_YEAR));
01645         break;
01646       }
01647     }
01648     MarkWholeScreenDirty();
01649   }
01650 
01651   virtual void OnTimeout()
01652   {
01653     this->click = 0;
01654     this->SetDirty();
01655   }
01656 };
01657 
01658 static const Widget _cust_currency_widgets[] = {
01659 {   WWT_CLOSEBOX,   RESIZE_NONE,  COLOUR_GREY,     0,    10,     0,    13, STR_00C5,            STR_018B_CLOSE_WINDOW},
01660 {    WWT_CAPTION,   RESIZE_NONE,  COLOUR_GREY,    11,   229,     0,    13, STR_CURRENCY_WINDOW, STR_018C_WINDOW_TITLE_DRAG_THIS},
01661 {      WWT_PANEL,   RESIZE_NONE,  COLOUR_GREY,     0,   229,    14,   119, 0x0,                 STR_NULL},
01662 {   WIDGETS_END},
01663 };
01664 
01665 static const WindowDesc _cust_currency_desc(
01666   WDP_CENTER, WDP_CENTER, 230, 120, 230, 120,
01667   WC_CUSTOM_CURRENCY, WC_NONE,
01668   WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET | WDF_UNCLICK_BUTTONS,
01669   _cust_currency_widgets
01670 );
01671 
01672 static void ShowCustCurrency()
01673 {
01674   DeleteWindowById(WC_CUSTOM_CURRENCY, 0);
01675   new CustomCurrencyWindow(&_cust_currency_desc);
01676 }

Generated on Wed Jun 3 19:05:14 2009 for OpenTTD by  doxygen 1.5.6