newgrf_gui.cpp

Go to the documentation of this file.
00001 /* $Id$ */
00002 
00003 /*
00004  * This file is part of OpenTTD.
00005  * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
00006  * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
00007  * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
00008  */
00009 
00012 #include "stdafx.h"
00013 #include "error.h"
00014 #include "settings_gui.h"
00015 #include "newgrf.h"
00016 #include "strings_func.h"
00017 #include "window_func.h"
00018 #include "gamelog.h"
00019 #include "settings_type.h"
00020 #include "settings_func.h"
00021 #include "widgets/dropdown_type.h"
00022 #include "widgets/dropdown_func.h"
00023 #include "network/network.h"
00024 #include "network/network_content.h"
00025 #include "sortlist_type.h"
00026 #include "stringfilter_type.h"
00027 #include "querystring_gui.h"
00028 #include "core/geometry_func.hpp"
00029 #include "newgrf_text.h"
00030 #include "textfile_gui.h"
00031 #include "tilehighlight_func.h"
00032 
00033 #include "widgets/newgrf_widget.h"
00034 #include "widgets/misc_widget.h"
00035 
00036 #include "table/sprites.h"
00037 
00041 void ShowNewGRFError()
00042 {
00043   /* Do not show errors when entering the main screen */
00044   if (_game_mode == GM_MENU) return;
00045 
00046   for (const GRFConfig *c = _grfconfig; c != NULL; c = c->next) {
00047     /* We only want to show fatal errors */
00048     if (c->error == NULL || c->error->severity != STR_NEWGRF_ERROR_MSG_FATAL) continue;
00049 
00050     SetDParam   (0, c->error->custom_message == NULL ? c->error->message : STR_JUST_RAW_STRING);
00051     SetDParamStr(1, c->error->custom_message);
00052     SetDParamStr(2, c->filename);
00053     SetDParamStr(3, c->error->data);
00054     for (uint i = 0; i < lengthof(c->error->param_value); i++) {
00055       SetDParam(4 + i, c->error->param_value[i]);
00056     }
00057     ShowErrorMessage(STR_NEWGRF_ERROR_FATAL_POPUP, INVALID_STRING_ID, WL_CRITICAL);
00058     break;
00059   }
00060 }
00061 
00062 static void ShowNewGRFInfo(const GRFConfig *c, uint x, uint y, uint right, uint bottom, bool show_params)
00063 {
00064   if (c->error != NULL) {
00065     char message[512];
00066     SetDParamStr(0, c->error->custom_message); // is skipped by built-in messages
00067     SetDParamStr(1, c->filename);
00068     SetDParamStr(2, c->error->data);
00069     for (uint i = 0; i < lengthof(c->error->param_value); i++) {
00070       SetDParam(3 + i, c->error->param_value[i]);
00071     }
00072     GetString(message, c->error->custom_message == NULL ? c->error->message : STR_JUST_RAW_STRING, lastof(message));
00073 
00074     SetDParamStr(0, message);
00075     y = DrawStringMultiLine(x, right, y, bottom, c->error->severity);
00076   }
00077 
00078   /* Draw filename or not if it is not known (GRF sent over internet) */
00079   if (c->filename != NULL) {
00080     SetDParamStr(0, c->filename);
00081     y = DrawStringMultiLine(x, right, y, bottom, STR_NEWGRF_SETTINGS_FILENAME);
00082   }
00083 
00084   /* Prepare and draw GRF ID */
00085   char buff[256];
00086   snprintf(buff, lengthof(buff), "%08X", BSWAP32(c->ident.grfid));
00087   SetDParamStr(0, buff);
00088   y = DrawStringMultiLine(x, right, y, bottom, STR_NEWGRF_SETTINGS_GRF_ID);
00089 
00090   if ((_settings_client.gui.newgrf_developer_tools || _settings_client.gui.newgrf_show_old_versions) && c->version != 0) {
00091     SetDParam(0, c->version);
00092     y = DrawStringMultiLine(x, right, y, bottom, STR_NEWGRF_SETTINGS_VERSION);
00093   }
00094   if ((_settings_client.gui.newgrf_developer_tools || _settings_client.gui.newgrf_show_old_versions) && c->min_loadable_version != 0) {
00095     SetDParam(0, c->min_loadable_version);
00096     y = DrawStringMultiLine(x, right, y, bottom, STR_NEWGRF_SETTINGS_MIN_VERSION);
00097   }
00098 
00099   /* Prepare and draw MD5 sum */
00100   md5sumToString(buff, lastof(buff), c->ident.md5sum);
00101   SetDParamStr(0, buff);
00102   y = DrawStringMultiLine(x, right, y, bottom, STR_NEWGRF_SETTINGS_MD5SUM);
00103 
00104   /* Show GRF parameter list */
00105   if (show_params) {
00106     if (c->num_params > 0) {
00107       GRFBuildParamList(buff, c, lastof(buff));
00108       SetDParam(0, STR_JUST_RAW_STRING);
00109       SetDParamStr(1, buff);
00110     } else {
00111       SetDParam(0, STR_LAND_AREA_INFORMATION_LOCAL_AUTHORITY_NONE);
00112     }
00113     y = DrawStringMultiLine(x, right, y, bottom, STR_NEWGRF_SETTINGS_PARAMETER);
00114 
00115     /* Draw the palette of the NewGRF */
00116     if (c->palette & GRFP_BLT_32BPP) {
00117       SetDParamStr(0, (c->palette & GRFP_USE_WINDOWS) ? "Windows / 32 bpp" : "DOS / 32 bpp");
00118     } else {
00119       SetDParamStr(0, (c->palette & GRFP_USE_WINDOWS) ? "Windows" : "DOS");
00120     }
00121     y = DrawStringMultiLine(x, right, y, bottom, STR_NEWGRF_SETTINGS_PALETTE);
00122   }
00123 
00124   /* Show flags */
00125   if (c->status == GCS_NOT_FOUND)       y = DrawStringMultiLine(x, right, y, bottom, STR_NEWGRF_SETTINGS_NOT_FOUND);
00126   if (c->status == GCS_DISABLED)        y = DrawStringMultiLine(x, right, y, bottom, STR_NEWGRF_SETTINGS_DISABLED);
00127   if (HasBit(c->flags, GCF_INVALID))    y = DrawStringMultiLine(x, right, y, bottom, STR_NEWGRF_SETTINGS_INCOMPATIBLE);
00128   if (HasBit(c->flags, GCF_COMPATIBLE)) y = DrawStringMultiLine(x, right, y, bottom, STR_NEWGRF_COMPATIBLE_LOADED);
00129 
00130   /* Draw GRF info if it exists */
00131   if (!StrEmpty(c->GetDescription())) {
00132     SetDParam(0, STR_JUST_RAW_STRING);
00133     SetDParamStr(1, c->GetDescription());
00134     y = DrawStringMultiLine(x, right, y, bottom, STR_BLACK_STRING);
00135   } else {
00136     y = DrawStringMultiLine(x, right, y, bottom, STR_NEWGRF_SETTINGS_NO_INFO);
00137   }
00138 }
00139 
00143 struct NewGRFParametersWindow : public Window {
00144   static GRFParameterInfo dummy_parameter_info; 
00145   GRFConfig *grf_config; 
00146   uint clicked_button;   
00147   bool clicked_increase; 
00148   bool clicked_dropdown; 
00149   bool closing_dropdown; 
00150   int timeout;           
00151   uint clicked_row;      
00152   int line_height;       
00153   Scrollbar *vscroll;
00154   bool action14present;  
00155   bool editable;         
00156 
00157   NewGRFParametersWindow(const WindowDesc *desc, GRFConfig *c, bool editable) : Window(),
00158     grf_config(c),
00159     clicked_button(UINT_MAX),
00160     clicked_dropdown(false),
00161     closing_dropdown(false),
00162     timeout(0),
00163     clicked_row(UINT_MAX),
00164     editable(editable)
00165   {
00166     this->action14present = (c->num_valid_params != lengthof(c->param) || c->param_info.Length() != 0);
00167 
00168     this->CreateNestedTree(desc);
00169     this->vscroll = this->GetScrollbar(WID_NP_SCROLLBAR);
00170     this->GetWidget<NWidgetStacked>(WID_NP_SHOW_NUMPAR)->SetDisplayedPlane(this->action14present ? SZSP_HORIZONTAL : 0);
00171     this->GetWidget<NWidgetStacked>(WID_NP_SHOW_DESCRIPTION)->SetDisplayedPlane(this->action14present ? 0 : SZSP_HORIZONTAL);
00172     this->FinishInitNested(desc);  // Initializes 'this->line_height' as side effect.
00173 
00174     this->SetWidgetDisabledState(WID_NP_RESET, !this->editable);
00175 
00176     this->InvalidateData();
00177   }
00178 
00184   static GRFParameterInfo *GetDummyParameterInfo(uint nr)
00185   {
00186     dummy_parameter_info.param_nr = nr;
00187     return &dummy_parameter_info;
00188   }
00189 
00190   virtual void UpdateWidgetSize(int widget, Dimension *size, const Dimension &padding, Dimension *fill, Dimension *resize)
00191   {
00192     switch (widget) {
00193       case WID_NP_NUMPAR_DEC:
00194       case WID_NP_NUMPAR_INC: {
00195         size->width = size->height = FONT_HEIGHT_NORMAL;
00196         break;
00197       }
00198 
00199       case WID_NP_NUMPAR: {
00200         SetDParamMaxValue(0, lengthof(this->grf_config->param));
00201         Dimension d = GetStringBoundingBox(this->GetWidget<NWidgetCore>(widget)->widget_data);
00202         d.width += padding.width;
00203         d.height += padding.height;
00204         *size = maxdim(*size, d);
00205         break;
00206       }
00207 
00208       case WID_NP_BACKGROUND:
00209         this->line_height = FONT_HEIGHT_NORMAL + WD_MATRIX_TOP + WD_MATRIX_BOTTOM;
00210 
00211         resize->width = 1;
00212         resize->height = this->line_height;
00213         size->height = GB(this->GetWidget<NWidgetCore>(widget)->widget_data, MAT_ROW_START, MAT_ROW_BITS) * this->line_height;
00214         break;
00215 
00216       case WID_NP_DESCRIPTION:
00217         /* Minimum size of 4 lines. The 500 is the default size of the window. */
00218         Dimension suggestion = {500 - WD_FRAMERECT_LEFT - WD_FRAMERECT_RIGHT, FONT_HEIGHT_NORMAL * 4 + WD_TEXTPANEL_TOP + WD_TEXTPANEL_BOTTOM};
00219         for (uint i = 0; i < this->grf_config->param_info.Length(); i++) {
00220           const GRFParameterInfo *par_info = this->grf_config->param_info[i];
00221           if (par_info == NULL) continue;
00222           const char *desc = GetGRFStringFromGRFText(par_info->desc);
00223           if (desc == NULL) continue;
00224           Dimension d = GetStringMultiLineBoundingBox(desc, suggestion);
00225           d.height += WD_TEXTPANEL_TOP + WD_TEXTPANEL_BOTTOM;
00226           suggestion = maxdim(d, suggestion);
00227         }
00228         size->height = suggestion.height;
00229         break;
00230     }
00231   }
00232 
00233   virtual void SetStringParameters(int widget) const
00234   {
00235     switch (widget) {
00236       case WID_NP_NUMPAR:
00237         SetDParam(0, this->vscroll->GetCount());
00238         break;
00239     }
00240   }
00241 
00242   virtual void DrawWidget(const Rect &r, int widget) const
00243   {
00244     if (widget == WID_NP_DESCRIPTION) {
00245       const GRFParameterInfo *par_info = (this->clicked_row < this->grf_config->param_info.Length()) ? this->grf_config->param_info[this->clicked_row] : NULL;
00246       if (par_info == NULL) return;
00247       const char *desc = GetGRFStringFromGRFText(par_info->desc);
00248       if (desc == NULL) return;
00249       DrawStringMultiLine(r.left + WD_FRAMERECT_LEFT, r.right - WD_FRAMERECT_RIGHT, r.top + WD_TEXTPANEL_TOP, r.bottom - WD_TEXTPANEL_BOTTOM, desc, TC_BLACK);
00250       return;
00251     } else if (widget != WID_NP_BACKGROUND) {
00252       return;
00253     }
00254 
00255     bool rtl = _current_text_dir == TD_RTL;
00256     uint buttons_left = rtl ? r.right - SETTING_BUTTON_WIDTH - 3 : r.left + 4;
00257     uint text_left    = r.left + (rtl ? WD_FRAMERECT_LEFT : SETTING_BUTTON_WIDTH + 8);
00258     uint text_right   = r.right - (rtl ? SETTING_BUTTON_WIDTH + 8 : WD_FRAMERECT_RIGHT);
00259 
00260     int y = r.top;
00261     int button_y_offset = (this->line_height - SETTING_BUTTON_HEIGHT) / 2;
00262     for (uint i = this->vscroll->GetPosition(); this->vscroll->IsVisible(i) && i < this->vscroll->GetCount(); i++) {
00263       GRFParameterInfo *par_info = (i < this->grf_config->param_info.Length()) ? this->grf_config->param_info[i] : NULL;
00264       if (par_info == NULL) par_info = GetDummyParameterInfo(i);
00265       uint32 current_value = par_info->GetValue(this->grf_config);
00266       bool selected = (i == this->clicked_row);
00267 
00268       if (par_info->type == PTYPE_BOOL) {
00269         DrawBoolButton(buttons_left, y + button_y_offset, current_value != 0, this->editable);
00270         SetDParam(2, par_info->GetValue(this->grf_config) == 0 ? STR_CONFIG_SETTING_OFF : STR_CONFIG_SETTING_ON);
00271       } else if (par_info->type == PTYPE_UINT_ENUM) {
00272         if (par_info->complete_labels) {
00273           DrawDropDownButton(buttons_left, y + button_y_offset, COLOUR_YELLOW, this->clicked_row == i && this->clicked_dropdown, this->editable);
00274         } else {
00275           DrawArrowButtons(buttons_left, y + button_y_offset, COLOUR_YELLOW, (this->clicked_button == i) ? 1 + (this->clicked_increase != rtl) : 0, this->editable && current_value > par_info->min_value, this->editable && current_value < par_info->max_value);
00276         }
00277         SetDParam(2, STR_JUST_INT);
00278         SetDParam(3, current_value);
00279         if (par_info->value_names.Contains(current_value)) {
00280           const char *label = GetGRFStringFromGRFText(par_info->value_names.Find(current_value)->second);
00281           if (label != NULL) {
00282             SetDParam(2, STR_JUST_RAW_STRING);
00283             SetDParamStr(3, label);
00284           }
00285         }
00286       }
00287 
00288       const char *name = GetGRFStringFromGRFText(par_info->name);
00289       if (name != NULL) {
00290         SetDParam(0, STR_JUST_RAW_STRING);
00291         SetDParamStr(1, name);
00292       } else {
00293         SetDParam(0, STR_NEWGRF_PARAMETERS_DEFAULT_NAME);
00294         SetDParam(1, i + 1);
00295       }
00296 
00297       DrawString(text_left, text_right, y + WD_MATRIX_TOP, STR_NEWGRF_PARAMETERS_SETTING, selected ? TC_WHITE : TC_LIGHT_BLUE);
00298       y += this->line_height;
00299     }
00300   }
00301 
00302   virtual void OnPaint()
00303   {
00304     if (this->closing_dropdown) {
00305       this->closing_dropdown = false;
00306       this->clicked_dropdown = false;
00307     }
00308     this->DrawWidgets();
00309   }
00310 
00311   virtual void OnClick(Point pt, int widget, int click_count)
00312   {
00313     switch (widget) {
00314       case WID_NP_NUMPAR_DEC:
00315         if (this->editable && !this->action14present && this->grf_config->num_params > 0) {
00316           this->grf_config->num_params--;
00317           this->InvalidateData();
00318           SetWindowDirty(WC_GAME_OPTIONS, WN_GAME_OPTIONS_NEWGRF_STATE);
00319         }
00320         break;
00321 
00322       case WID_NP_NUMPAR_INC: {
00323         GRFConfig *c = this->grf_config;
00324         if (this->editable && !this->action14present && c->num_params < c->num_valid_params) {
00325           c->param[c->num_params++] = 0;
00326           this->InvalidateData();
00327           SetWindowDirty(WC_GAME_OPTIONS, WN_GAME_OPTIONS_NEWGRF_STATE);
00328         }
00329         break;
00330       }
00331 
00332       case WID_NP_BACKGROUND: {
00333         if (!this->editable) break;
00334         uint num = this->vscroll->GetScrolledRowFromWidget(pt.y, this, WID_NP_BACKGROUND);
00335         if (num >= this->vscroll->GetCount()) break;
00336         if (this->clicked_row != num) {
00337           DeleteChildWindows(WC_QUERY_STRING);
00338           HideDropDownMenu(this);
00339           this->clicked_row = num;
00340           this->clicked_dropdown = false;
00341         }
00342 
00343         const NWidgetBase *wid = this->GetWidget<NWidgetBase>(WID_NP_BACKGROUND);
00344         int x = pt.x - wid->pos_x;
00345         if (_current_text_dir == TD_RTL) x = wid->current_x - 1 - x;
00346         x -= 4;
00347 
00348         GRFParameterInfo *par_info = (num < this->grf_config->param_info.Length()) ? this->grf_config->param_info[num] : NULL;
00349         if (par_info == NULL) par_info = GetDummyParameterInfo(num);
00350 
00351         /* One of the arrows is clicked */
00352         uint32 old_val = par_info->GetValue(this->grf_config);
00353         if (par_info->type != PTYPE_BOOL && IsInsideMM(x, 0, SETTING_BUTTON_WIDTH) && par_info->complete_labels) {
00354           if (this->clicked_dropdown) {
00355             /* unclick the dropdown */
00356             HideDropDownMenu(this);
00357             this->clicked_dropdown = false;
00358             this->closing_dropdown = false;
00359           } else {
00360             const NWidgetBase *wid = this->GetWidget<NWidgetBase>(WID_NP_BACKGROUND);
00361             int rel_y = (pt.y - (int)wid->pos_y) % this->line_height;
00362 
00363             Rect wi_rect;
00364             wi_rect.left = pt.x - (_current_text_dir == TD_RTL ? SETTING_BUTTON_WIDTH - 1 - x : x);;
00365             wi_rect.right = wi_rect.left + SETTING_BUTTON_WIDTH - 1;
00366             wi_rect.top = pt.y - rel_y + (this->line_height - SETTING_BUTTON_HEIGHT) / 2;
00367             wi_rect.bottom = wi_rect.top + SETTING_BUTTON_HEIGHT - 1;
00368 
00369             /* For dropdowns we also have to check the y position thoroughly, the mouse may not above the just opening dropdown */
00370             if (pt.y >= wi_rect.top && pt.y <= wi_rect.bottom) {
00371               this->clicked_dropdown = true;
00372               this->closing_dropdown = false;
00373 
00374               DropDownList *list = new DropDownList();
00375               for (uint32 i = par_info->min_value; i <= par_info->max_value; i++) {
00376                 list->push_back(new DropDownListCharStringItem(GetGRFStringFromGRFText(par_info->value_names.Find(i)->second), i, false));
00377               }
00378 
00379               ShowDropDownListAt(this, list, old_val, -1, wi_rect, COLOUR_ORANGE, true);
00380             }
00381           }
00382         } else if (IsInsideMM(x, 0, SETTING_BUTTON_WIDTH)) {
00383           uint32 val = old_val;
00384           if (par_info->type == PTYPE_BOOL) {
00385             val = !val;
00386           } else {
00387             if (x >= SETTING_BUTTON_WIDTH / 2) {
00388               /* Increase button clicked */
00389               if (val < par_info->max_value) val++;
00390               this->clicked_increase = true;
00391             } else {
00392               /* Decrease button clicked */
00393               if (val > par_info->min_value) val--;
00394               this->clicked_increase = false;
00395             }
00396           }
00397           if (val != old_val) {
00398             par_info->SetValue(this->grf_config, val);
00399 
00400             this->clicked_button = num;
00401             this->timeout = 5;
00402           }
00403         } else if (par_info->type == PTYPE_UINT_ENUM && !par_info->complete_labels && click_count >= 2) {
00404           /* Display a query box so users can enter a custom value. */
00405           SetDParam(0, old_val);
00406           ShowQueryString(STR_JUST_INT, STR_CONFIG_SETTING_QUERY_CAPTION, 10, this, CS_NUMERAL, QSF_NONE);
00407         }
00408         this->SetDirty();
00409         break;
00410       }
00411 
00412       case WID_NP_RESET:
00413         if (!this->editable) break;
00414         this->grf_config->SetParameterDefaults();
00415         this->InvalidateData();
00416         SetWindowDirty(WC_GAME_OPTIONS, WN_GAME_OPTIONS_NEWGRF_STATE);
00417         break;
00418 
00419       case WID_NP_ACCEPT:
00420         delete this;
00421         break;
00422     }
00423   }
00424 
00425   virtual void OnQueryTextFinished(char *str)
00426   {
00427     if (StrEmpty(str)) return;
00428     int32 value = atoi(str);
00429     GRFParameterInfo *par_info = ((uint)this->clicked_row < this->grf_config->param_info.Length()) ? this->grf_config->param_info[this->clicked_row] : NULL;
00430     if (par_info == NULL) par_info = GetDummyParameterInfo(this->clicked_row);
00431     uint32 val = Clamp<uint32>(value, par_info->min_value, par_info->max_value);
00432     par_info->SetValue(this->grf_config, val);
00433     this->SetDirty();
00434   }
00435 
00436   virtual void OnDropdownSelect(int widget, int index)
00437   {
00438     assert(this->clicked_dropdown);
00439     GRFParameterInfo *par_info = ((uint)this->clicked_row < this->grf_config->param_info.Length()) ? this->grf_config->param_info[this->clicked_row] : NULL;
00440     if (par_info == NULL) par_info = GetDummyParameterInfo(this->clicked_row);
00441     par_info->SetValue(this->grf_config, index);
00442     this->SetDirty();
00443   }
00444 
00445   virtual void OnDropdownClose(Point pt, int widget, int index, bool instant_close)
00446   {
00447     /* We cannot raise the dropdown button just yet. OnClick needs some hint, whether
00448      * the same dropdown button was clicked again, and then not open the dropdown again.
00449      * So, we only remember that it was closed, and process it on the next OnPaint, which is
00450      * after OnClick. */
00451     assert(this->clicked_dropdown);
00452     this->closing_dropdown = true;
00453     this->SetDirty();
00454   }
00455 
00456   virtual void OnResize()
00457   {
00458     NWidgetCore *nwi = this->GetWidget<NWidgetCore>(WID_NP_BACKGROUND);
00459     this->vscroll->SetCapacity(nwi->current_y / this->line_height);
00460     nwi->widget_data = (this->vscroll->GetCapacity() << MAT_ROW_START) + (1 << MAT_COL_START);
00461   }
00462 
00468   virtual void OnInvalidateData(int data = 0, bool gui_scope = true)
00469   {
00470     if (!gui_scope) return;
00471     if (!this->action14present) {
00472       this->SetWidgetDisabledState(WID_NP_NUMPAR_DEC, !this->editable || this->grf_config->num_params == 0);
00473       this->SetWidgetDisabledState(WID_NP_NUMPAR_INC, !this->editable || this->grf_config->num_params >= this->grf_config->num_valid_params);
00474     }
00475 
00476     this->vscroll->SetCount(this->action14present ? this->grf_config->num_valid_params : this->grf_config->num_params);
00477     if (this->clicked_row != UINT_MAX && this->clicked_row >= this->vscroll->GetCount()) {
00478       this->clicked_row = UINT_MAX;
00479       DeleteChildWindows(WC_QUERY_STRING);
00480     }
00481   }
00482 
00483   virtual void OnTick()
00484   {
00485     if (--this->timeout == 0) {
00486       this->clicked_button = UINT_MAX;
00487       this->SetDirty();
00488     }
00489   }
00490 };
00491 GRFParameterInfo NewGRFParametersWindow::dummy_parameter_info(0);
00492 
00493 
00494 static const NWidgetPart _nested_newgrf_parameter_widgets[] = {
00495   NWidget(NWID_HORIZONTAL),
00496     NWidget(WWT_CLOSEBOX, COLOUR_MAUVE),
00497     NWidget(WWT_CAPTION, COLOUR_MAUVE), SetDataTip(STR_NEWGRF_PARAMETERS_CAPTION, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS),
00498   EndContainer(),
00499   NWidget(NWID_SELECTION, INVALID_COLOUR, WID_NP_SHOW_NUMPAR),
00500     NWidget(WWT_PANEL, COLOUR_MAUVE), SetResize(1, 0), SetFill(1, 0), SetPIP(4, 0, 4),
00501       NWidget(NWID_HORIZONTAL), SetPIP(4, 0, 4),
00502         NWidget(WWT_PUSHARROWBTN, COLOUR_YELLOW, WID_NP_NUMPAR_DEC), SetMinimalSize(12, 12), SetDataTip(AWV_DECREASE, STR_NULL),
00503         NWidget(WWT_PUSHARROWBTN, COLOUR_YELLOW, WID_NP_NUMPAR_INC), SetMinimalSize(12, 12), SetDataTip(AWV_INCREASE, STR_NULL),
00504         NWidget(WWT_TEXT, COLOUR_MAUVE, WID_NP_NUMPAR), SetResize(1, 0), SetFill(1, 0), SetPadding(0, 0, 0, 4), SetDataTip(STR_NEWGRF_PARAMETERS_NUM_PARAM, STR_NULL),
00505       EndContainer(),
00506     EndContainer(),
00507   EndContainer(),
00508   NWidget(NWID_HORIZONTAL),
00509     NWidget(WWT_MATRIX, COLOUR_MAUVE, WID_NP_BACKGROUND), SetMinimalSize(188, 182), SetResize(1, 1), SetFill(1, 0), SetDataTip(0x501, STR_NULL), SetScrollbar(WID_NP_SCROLLBAR),
00510     NWidget(NWID_VSCROLLBAR, COLOUR_MAUVE, WID_NP_SCROLLBAR),
00511   EndContainer(),
00512   NWidget(NWID_SELECTION, INVALID_COLOUR, WID_NP_SHOW_DESCRIPTION),
00513     NWidget(WWT_PANEL, COLOUR_MAUVE, WID_NP_DESCRIPTION), SetResize(1, 0), SetFill(1, 0),
00514     EndContainer(),
00515   EndContainer(),
00516   NWidget(NWID_HORIZONTAL),
00517     NWidget(NWID_HORIZONTAL, NC_EQUALSIZE),
00518       NWidget(WWT_PUSHTXTBTN, COLOUR_MAUVE, WID_NP_ACCEPT), SetResize(1, 0), SetFill(1, 0), SetDataTip(STR_NEWGRF_PARAMETERS_CLOSE, STR_NULL),
00519       NWidget(WWT_PUSHTXTBTN, COLOUR_MAUVE, WID_NP_RESET), SetResize(1, 0), SetFill(1, 0), SetDataTip(STR_NEWGRF_PARAMETERS_RESET, STR_NEWGRF_PARAMETERS_RESET_TOOLTIP),
00520     EndContainer(),
00521     NWidget(WWT_RESIZEBOX, COLOUR_MAUVE),
00522   EndContainer(),
00523 };
00524 
00526 static const WindowDesc _newgrf_parameters_desc(
00527   WDP_CENTER, 500, 208,
00528   WC_GRF_PARAMETERS, WC_NONE,
00529   0,
00530   _nested_newgrf_parameter_widgets, lengthof(_nested_newgrf_parameter_widgets)
00531 );
00532 
00533 static void OpenGRFParameterWindow(GRFConfig *c, bool editable)
00534 {
00535   DeleteWindowByClass(WC_GRF_PARAMETERS);
00536   new NewGRFParametersWindow(&_newgrf_parameters_desc, c, editable);
00537 }
00538 
00540 struct NewGRFTextfileWindow : public TextfileWindow {
00541   const GRFConfig *grf_config; 
00542 
00543   NewGRFTextfileWindow(TextfileType file_type, const GRFConfig *c) : TextfileWindow(file_type), grf_config(c)
00544   {
00545     const char *textfile = this->grf_config->GetTextfile(file_type);
00546     this->LoadTextfile(textfile, NEWGRF_DIR);
00547   }
00548 
00549   /* virtual */ void SetStringParameters(int widget) const
00550   {
00551     if (widget == WID_TF_CAPTION) {
00552       SetDParam(0, STR_CONTENT_TYPE_NEWGRF);
00553       SetDParamStr(1, this->grf_config->GetName());
00554     }
00555   }
00556 };
00557 
00558 void ShowNewGRFTextfileWindow(TextfileType file_type, const GRFConfig *c)
00559 {
00560   DeleteWindowByClass(WC_TEXTFILE);
00561   new NewGRFTextfileWindow(file_type, c);
00562 }
00563 
00564 static GRFPresetList _grf_preset_list;
00565 
00566 class DropDownListPresetItem : public DropDownListItem {
00567 public:
00568   DropDownListPresetItem(int result) : DropDownListItem(result, false) {}
00569 
00570   virtual ~DropDownListPresetItem() {}
00571 
00572   bool Selectable() const
00573   {
00574     return true;
00575   }
00576 
00577   void Draw(int left, int right, int top, int bottom, bool sel, int bg_colour) const
00578   {
00579     DrawString(left + 2, right + 2, top, _grf_preset_list[this->result], sel ? TC_WHITE : TC_BLACK);
00580   }
00581 };
00582 
00583 static void NewGRFConfirmationCallback(Window *w, bool confirmed);
00584 
00588 struct NewGRFWindow : public Window, NewGRFScanCallback {
00589   typedef GUIList<const GRFConfig *, StringFilter &> GUIGRFConfigList;
00590 
00591   static const uint EDITBOX_MAX_SIZE   =  50;
00592 
00593   static Listing   last_sorting;   
00594   static Filtering last_filtering; 
00595   static GUIGRFConfigList::SortFunction   * const sorter_funcs[]; 
00596   static GUIGRFConfigList::FilterFunction * const filter_funcs[]; 
00597 
00598   GUIGRFConfigList avails;    
00599   const GRFConfig *avail_sel; 
00600   int avail_pos;              
00601   StringFilter string_filter; 
00602   QueryString filter_editbox; 
00603 
00604   GRFConfig *actives;         
00605   GRFConfig *active_sel;      
00606 
00607   GRFConfig **orig_list;      
00608   bool editable;              
00609   bool show_params;           
00610   bool execute;               
00611   int preset;                 
00612   int active_over;            
00613 
00614   Scrollbar *vscroll;
00615   Scrollbar *vscroll2;
00616 
00617   NewGRFWindow(const WindowDesc *desc, bool editable, bool show_params, bool execute, GRFConfig **orig_list) : filter_editbox(EDITBOX_MAX_SIZE)
00618   {
00619     this->avail_sel   = NULL;
00620     this->avail_pos   = -1;
00621     this->active_sel  = NULL;
00622     this->actives     = NULL;
00623     this->orig_list   = orig_list;
00624     this->editable    = editable;
00625     this->execute     = execute;
00626     this->show_params = show_params;
00627     this->preset      = -1;
00628     this->active_over = -1;
00629 
00630     CopyGRFConfigList(&this->actives, *orig_list, false);
00631     GetGRFPresetList(&_grf_preset_list);
00632 
00633     this->CreateNestedTree(desc);
00634     this->vscroll = this->GetScrollbar(WID_NS_SCROLLBAR);
00635     this->vscroll2 = this->GetScrollbar(WID_NS_SCROLL2BAR);
00636 
00637     this->GetWidget<NWidgetStacked>(WID_NS_SHOW_REMOVE)->SetDisplayedPlane(this->editable ? 0 : 1);
00638     this->GetWidget<NWidgetStacked>(WID_NS_SHOW_APPLY)->SetDisplayedPlane(this->editable ? 0 : this->show_params ? 1 : SZSP_HORIZONTAL);
00639     this->FinishInitNested(desc, WN_GAME_OPTIONS_NEWGRF_STATE);
00640 
00641     this->querystrings[WID_NS_FILTER] = &this->filter_editbox;
00642     this->filter_editbox.cancel_button = QueryString::ACTION_CLEAR;
00643     this->SetFocusedWidget(WID_NS_FILTER);
00644 
00645     this->avails.SetListing(this->last_sorting);
00646     this->avails.SetFiltering(this->last_filtering);
00647     this->avails.SetSortFuncs(this->sorter_funcs);
00648     this->avails.SetFilterFuncs(this->filter_funcs);
00649     this->avails.ForceRebuild();
00650 
00651     this->OnInvalidateData(GOID_NEWGRF_LIST_EDITED);
00652   }
00653 
00654   ~NewGRFWindow()
00655   {
00656     DeleteWindowByClass(WC_GRF_PARAMETERS);
00657     DeleteWindowByClass(WC_TEXTFILE);
00658 
00659     if (this->editable && !this->execute) {
00660       CopyGRFConfigList(this->orig_list, this->actives, true);
00661       ResetGRFConfig(false);
00662       ReloadNewGRFData();
00663     }
00664 
00665     /* Remove the temporary copy of grf-list used in window */
00666     ClearGRFConfigList(&this->actives);
00667     _grf_preset_list.Clear();
00668   }
00669 
00670   virtual void UpdateWidgetSize(int widget, Dimension *size, const Dimension &padding, Dimension *fill, Dimension *resize)
00671   {
00672     switch (widget) {
00673       case WID_NS_FILE_LIST:
00674       {
00675         Dimension d = maxdim(GetSpriteSize(SPR_SQUARE), GetSpriteSize(SPR_WARNING_SIGN));
00676         resize->height = max(d.height + 2U, FONT_HEIGHT_NORMAL + 2U);
00677         size->height = max(size->height, WD_FRAMERECT_TOP + 6 * resize->height + WD_FRAMERECT_BOTTOM);
00678         break;
00679       }
00680 
00681       case WID_NS_AVAIL_LIST:
00682         resize->height = max(12, FONT_HEIGHT_NORMAL + 2);
00683         size->height = max(size->height, WD_FRAMERECT_TOP + 8 * resize->height + WD_FRAMERECT_BOTTOM);
00684         break;
00685 
00686       case WID_NS_NEWGRF_INFO_TITLE: {
00687         Dimension dim = GetStringBoundingBox(STR_NEWGRF_SETTINGS_INFO_TITLE);
00688         size->height = max(size->height, dim.height + WD_FRAMETEXT_TOP + WD_FRAMETEXT_BOTTOM);
00689         size->width  = max(size->width,  dim.width  + WD_FRAMETEXT_LEFT + WD_FRAMETEXT_RIGHT);
00690         break;
00691       }
00692 
00693       case WID_NS_NEWGRF_INFO:
00694         size->height = max(size->height, WD_FRAMERECT_TOP + 10 * FONT_HEIGHT_NORMAL + WD_FRAMERECT_BOTTOM + padding.height + 2);
00695         break;
00696 
00697       case WID_NS_PRESET_LIST: {
00698         Dimension d = GetStringBoundingBox(STR_NUM_CUSTOM);
00699         for (uint i = 0; i < _grf_preset_list.Length(); i++) {
00700           if (_grf_preset_list[i] != NULL) {
00701             SetDParamStr(0, _grf_preset_list[i]);
00702             d = maxdim(d, GetStringBoundingBox(STR_JUST_RAW_STRING));
00703           }
00704         }
00705         d.width += padding.width;
00706         *size = maxdim(d, *size);
00707         break;
00708       }
00709 
00710       case WID_NS_CONTENT_DOWNLOAD:
00711       case WID_NS_CONTENT_DOWNLOAD2: {
00712         Dimension d = GetStringBoundingBox(STR_NEWGRF_SETTINGS_FIND_MISSING_CONTENT_BUTTON);
00713         *size = maxdim(d, GetStringBoundingBox(STR_INTRO_ONLINE_CONTENT));
00714         size->width  += padding.width;
00715         size->height += padding.height;
00716         break;
00717       }
00718     }
00719   }
00720 
00721   virtual void OnResize()
00722   {
00723     this->vscroll->SetCapacityFromWidget(this, WID_NS_FILE_LIST);
00724     this->vscroll2->SetCapacityFromWidget(this, WID_NS_AVAIL_LIST);
00725   }
00726 
00727   virtual void SetStringParameters(int widget) const
00728   {
00729     switch (widget) {
00730       case WID_NS_PRESET_LIST:
00731         if (this->preset == -1) {
00732           SetDParam(0, STR_NUM_CUSTOM);
00733         } else {
00734           SetDParam(0, STR_JUST_RAW_STRING);
00735           SetDParamStr(1, _grf_preset_list[this->preset]);
00736         }
00737         break;
00738     }
00739   }
00740 
00746   inline PaletteID GetPalette(const GRFConfig *c) const
00747   {
00748     PaletteID pal;
00749 
00750     /* Pick a colour */
00751     switch (c->status) {
00752       case GCS_NOT_FOUND:
00753       case GCS_DISABLED:
00754         pal = PALETTE_TO_RED;
00755         break;
00756       case GCS_ACTIVATED:
00757         pal = PALETTE_TO_GREEN;
00758         break;
00759       default:
00760         pal = PALETTE_TO_BLUE;
00761         break;
00762     }
00763 
00764     /* Do not show a "not-failure" colour when it actually failed to load */
00765     if (pal != PALETTE_TO_RED) {
00766       if (HasBit(c->flags, GCF_STATIC)) {
00767         pal = PALETTE_TO_GREY;
00768       } else if (HasBit(c->flags, GCF_COMPATIBLE)) {
00769         pal = PALETTE_TO_ORANGE;
00770       }
00771     }
00772 
00773     return pal;
00774   }
00775 
00776   virtual void DrawWidget(const Rect &r, int widget) const
00777   {
00778     switch (widget) {
00779       case WID_NS_FILE_LIST: {
00780         GfxFillRect(r.left + 1, r.top + 1, r.right - 1, r.bottom - 1, PC_BLACK);
00781 
00782         uint step_height = this->GetWidget<NWidgetBase>(WID_NS_FILE_LIST)->resize_y;
00783         uint y = r.top + WD_FRAMERECT_TOP;
00784         Dimension square = GetSpriteSize(SPR_SQUARE);
00785         Dimension warning = GetSpriteSize(SPR_WARNING_SIGN);
00786         int square_offset_y = (step_height - square.height) / 2;
00787         int warning_offset_y = (step_height - warning.height) / 2;
00788         int offset_y = (step_height - FONT_HEIGHT_NORMAL) / 2;
00789 
00790         bool rtl = _current_text_dir == TD_RTL;
00791         uint text_left    = rtl ? r.left + WD_FRAMERECT_LEFT : r.left + square.width + 15;
00792         uint text_right   = rtl ? r.right - square.width - 15 : r.right - WD_FRAMERECT_RIGHT;
00793         uint square_left  = rtl ? r.right - square.width - 5 : r.left + 5;
00794         uint warning_left = rtl ? r.right - square.width - warning.width - 10 : r.left + square.width + 10;
00795 
00796         int i = 0;
00797         for (const GRFConfig *c = this->actives; c != NULL; c = c->next, i++) {
00798           if (this->vscroll->IsVisible(i)) {
00799             const char *text = c->GetName();
00800             bool h = (this->active_sel == c);
00801             PaletteID pal = this->GetPalette(c);
00802 
00803             if (h) {
00804               GfxFillRect(r.left + 1, y, r.right - 1, y + step_height - 1, PC_DARK_BLUE);
00805             } else if (i == this->active_over) {
00806               /* Get index of current selection. */
00807               int active_sel_pos = 0;
00808               for (GRFConfig *c = this->actives; c != NULL && c != this->active_sel; c = c->next, active_sel_pos++) {}
00809               if (active_sel_pos != this->active_over) {
00810                 uint top = this->active_over < active_sel_pos ? y + 1 : y + step_height - 2;
00811                 GfxFillRect(r.left + WD_FRAMERECT_LEFT, top - 1, r.right - WD_FRAMERECT_RIGHT, top + 1, PC_GREY);
00812               }
00813             }
00814             DrawSprite(SPR_SQUARE, pal, square_left, y + square_offset_y);
00815             if (c->error != NULL) DrawSprite(SPR_WARNING_SIGN, 0, warning_left, y + warning_offset_y);
00816             uint txtoffset = c->error == NULL ? 0 : warning.width;
00817             DrawString(text_left + (rtl ? 0 : txtoffset), text_right - (rtl ? txtoffset : 0), y + offset_y, text, h ? TC_WHITE : TC_ORANGE);
00818             y += step_height;
00819           }
00820         }
00821         if (i == this->active_over && this->vscroll->IsVisible(i)) { // Highlight is after the last GRF entry.
00822           GfxFillRect(r.left + WD_FRAMERECT_LEFT, y, r.right - WD_FRAMERECT_RIGHT, y + 2, PC_GREY);
00823         }
00824         break;
00825       }
00826 
00827       case WID_NS_AVAIL_LIST: {
00828         GfxFillRect(r.left + 1, r.top + 1, r.right - 1, r.bottom - 1, this->active_over == -2 ? PC_DARK_GREY : PC_BLACK);
00829 
00830         uint step_height = this->GetWidget<NWidgetBase>(WID_NS_AVAIL_LIST)->resize_y;
00831         int offset_y = (step_height - FONT_HEIGHT_NORMAL) / 2;
00832         uint y = r.top + WD_FRAMERECT_TOP;
00833         uint min_index = this->vscroll2->GetPosition();
00834         uint max_index = min(min_index + this->vscroll2->GetCapacity(), this->avails.Length());
00835 
00836         for (uint i = min_index; i < max_index; i++) {
00837           const GRFConfig *c = this->avails[i];
00838           bool h = (c == this->avail_sel);
00839           const char *text = c->GetName();
00840 
00841           if (h) GfxFillRect(r.left + 1, y, r.right - 1, y + step_height - 1, PC_DARK_BLUE);
00842           DrawString(r.left + WD_FRAMERECT_LEFT, r.right - WD_FRAMERECT_RIGHT, y + offset_y, text, h ? TC_WHITE : TC_SILVER);
00843           y += step_height;
00844         }
00845         break;
00846       }
00847 
00848       case WID_NS_NEWGRF_INFO_TITLE:
00849         /* Create the nice grayish rectangle at the details top. */
00850         GfxFillRect(r.left + 1, r.top + 1, r.right - 1, r.bottom - 1, PC_DARK_BLUE);
00851         DrawString(r.left, r.right, (r.top + r.bottom - FONT_HEIGHT_NORMAL) / 2, STR_NEWGRF_SETTINGS_INFO_TITLE, TC_FROMSTRING, SA_HOR_CENTER);
00852         break;
00853 
00854       case WID_NS_NEWGRF_INFO: {
00855         const GRFConfig *selected = this->active_sel;
00856         if (selected == NULL) selected = this->avail_sel;
00857         if (selected != NULL) {
00858           ShowNewGRFInfo(selected, r.left + WD_FRAMERECT_LEFT, r.top + WD_FRAMERECT_TOP, r.right - WD_FRAMERECT_RIGHT, r.bottom - WD_FRAMERECT_BOTTOM, this->show_params);
00859         }
00860         break;
00861       }
00862     }
00863   }
00864 
00865   virtual void OnClick(Point pt, int widget, int click_count)
00866   {
00867     if (widget >= WID_NS_NEWGRF_TEXTFILE && widget < WID_NS_NEWGRF_TEXTFILE + TFT_END) {
00868       if (this->active_sel == NULL && this->avail_sel == NULL) return;
00869 
00870       ShowNewGRFTextfileWindow((TextfileType)(widget - WID_NS_NEWGRF_TEXTFILE), this->active_sel != NULL ? this->active_sel : this->avail_sel);
00871       return;
00872     }
00873 
00874     switch (widget) {
00875       case WID_NS_PRESET_LIST: {
00876         DropDownList *list = new DropDownList();
00877 
00878         /* Add 'None' option for clearing list */
00879         list->push_back(new DropDownListStringItem(STR_NONE, -1, false));
00880 
00881         for (uint i = 0; i < _grf_preset_list.Length(); i++) {
00882           if (_grf_preset_list[i] != NULL) {
00883             list->push_back(new DropDownListPresetItem(i));
00884           }
00885         }
00886 
00887         this->DeleteChildWindows(WC_QUERY_STRING); // Remove the parameter query window
00888         ShowDropDownList(this, list, this->preset, WID_NS_PRESET_LIST);
00889         break;
00890       }
00891 
00892       case WID_NS_OPEN_URL: {
00893         const GRFConfig *c = (this->avail_sel == NULL) ? this->active_sel : this->avail_sel;
00894 
00895         extern void OpenBrowser(const char *url);
00896         OpenBrowser(c->GetURL());
00897         break;
00898       }
00899 
00900       case WID_NS_PRESET_SAVE:
00901         ShowQueryString(STR_EMPTY, STR_NEWGRF_SETTINGS_PRESET_SAVE_QUERY, 32, this, CS_ALPHANUMERAL, QSF_NONE);
00902         break;
00903 
00904       case WID_NS_PRESET_DELETE:
00905         if (this->preset == -1) return;
00906 
00907         DeleteGRFPresetFromConfig(_grf_preset_list[this->preset]);
00908         GetGRFPresetList(&_grf_preset_list);
00909         this->preset = -1;
00910         this->InvalidateData();
00911         this->DeleteChildWindows(WC_QUERY_STRING); // Remove the parameter query window
00912         break;
00913 
00914       case WID_NS_MOVE_UP: { // Move GRF up
00915         if (this->active_sel == NULL || !this->editable) break;
00916 
00917         int pos = 0;
00918         for (GRFConfig **pc = &this->actives; *pc != NULL; pc = &(*pc)->next, pos++) {
00919           GRFConfig *c = *pc;
00920           if (c->next == this->active_sel) {
00921             c->next = this->active_sel->next;
00922             this->active_sel->next = c;
00923             *pc = this->active_sel;
00924             break;
00925           }
00926         }
00927         this->vscroll->ScrollTowards(pos);
00928         this->preset = -1;
00929         this->InvalidateData();
00930         break;
00931       }
00932 
00933       case WID_NS_MOVE_DOWN: { // Move GRF down
00934         if (this->active_sel == NULL || !this->editable) break;
00935 
00936         int pos = 1; // Start at 1 as we swap the selected newgrf with the next one
00937         for (GRFConfig **pc = &this->actives; *pc != NULL; pc = &(*pc)->next, pos++) {
00938           GRFConfig *c = *pc;
00939           if (c == this->active_sel) {
00940             *pc = c->next;
00941             c->next = c->next->next;
00942             (*pc)->next = c;
00943             break;
00944           }
00945         }
00946         this->vscroll->ScrollTowards(pos);
00947         this->preset = -1;
00948         this->InvalidateData();
00949         break;
00950       }
00951 
00952       case WID_NS_FILE_LIST: { // Select an active GRF.
00953         ResetObjectToPlace();
00954 
00955         uint i = this->vscroll->GetScrolledRowFromWidget(pt.y, this, WID_NS_FILE_LIST);
00956 
00957         GRFConfig *c;
00958         for (c = this->actives; c != NULL && i > 0; c = c->next, i--) {}
00959 
00960         if (this->active_sel != c) DeleteWindowByClass(WC_GRF_PARAMETERS);
00961         this->active_sel = c;
00962         this->avail_sel = NULL;
00963         this->avail_pos = -1;
00964 
00965         this->InvalidateData();
00966         if (click_count == 1) {
00967           if (this->editable && this->active_sel != NULL) SetObjectToPlaceWnd(SPR_CURSOR_MOUSE, PAL_NONE, HT_DRAG, this);
00968           break;
00969         }
00970         /* FALL THROUGH, with double click. */
00971       }
00972 
00973       case WID_NS_REMOVE: { // Remove GRF
00974         if (this->active_sel == NULL || !this->editable) break;
00975         DeleteWindowByClass(WC_GRF_PARAMETERS);
00976 
00977         /* Choose the next GRF file to be the selected file. */
00978         GRFConfig *newsel = this->active_sel->next;
00979         for (GRFConfig **pc = &this->actives; *pc != NULL; pc = &(*pc)->next) {
00980           GRFConfig *c = *pc;
00981           /* If the new selection is empty (i.e. we're deleting the last item
00982            * in the list, pick the file just before the selected file */
00983           if (newsel == NULL && c->next == this->active_sel) newsel = c;
00984 
00985           if (c == this->active_sel) {
00986             *pc = c->next;
00987             delete c;
00988             break;
00989           }
00990         }
00991 
00992         this->active_sel = newsel;
00993         this->preset = -1;
00994         this->avail_pos = -1;
00995         this->avail_sel = NULL;
00996         this->avails.ForceRebuild();
00997         this->InvalidateData(GOID_NEWGRF_LIST_EDITED);
00998         break;
00999       }
01000 
01001       case WID_NS_AVAIL_LIST: { // Select a non-active GRF.
01002         ResetObjectToPlace();
01003 
01004         uint i = this->vscroll2->GetScrolledRowFromWidget(pt.y, this, WID_NS_AVAIL_LIST);
01005         this->active_sel = NULL;
01006         DeleteWindowByClass(WC_GRF_PARAMETERS);
01007         if (i < this->avails.Length()) {
01008           this->avail_sel = this->avails[i];
01009           this->avail_pos = i;
01010         }
01011         this->InvalidateData();
01012         if (click_count == 1) {
01013           if (this->editable && this->avail_sel != NULL && !HasBit(this->avail_sel->flags, GCF_INVALID)) SetObjectToPlaceWnd(SPR_CURSOR_MOUSE, PAL_NONE, HT_DRAG, this);
01014           break;
01015         }
01016         /* FALL THROUGH, with double click. */
01017       }
01018 
01019       case WID_NS_ADD:
01020         if (this->avail_sel == NULL || !this->editable || HasBit(this->avail_sel->flags, GCF_INVALID)) break;
01021 
01022         this->AddGRFToActive();
01023         break;
01024 
01025       case WID_NS_APPLY_CHANGES: // Apply changes made to GRF list
01026         if (!this->editable) break;
01027         if (this->execute) {
01028           ShowQuery(
01029             STR_NEWGRF_POPUP_CAUTION_CAPTION,
01030             STR_NEWGRF_CONFIRMATION_TEXT,
01031             this,
01032             NewGRFConfirmationCallback
01033           );
01034         } else {
01035           CopyGRFConfigList(this->orig_list, this->actives, true);
01036           ResetGRFConfig(false);
01037           ReloadNewGRFData();
01038         }
01039         this->DeleteChildWindows(WC_QUERY_STRING); // Remove the parameter query window
01040         break;
01041 
01042       case WID_NS_VIEW_PARAMETERS:
01043       case WID_NS_SET_PARAMETERS: { // Edit parameters
01044         if (this->active_sel == NULL || !this->show_params || this->active_sel->num_valid_params == 0) break;
01045 
01046         OpenGRFParameterWindow(this->active_sel, this->editable);
01047         break;
01048       }
01049 
01050       case WID_NS_TOGGLE_PALETTE:
01051         if (this->active_sel != NULL || !this->editable) {
01052           this->active_sel->palette ^= GRFP_USE_MASK;
01053           this->SetDirty();
01054         }
01055         break;
01056 
01057       case WID_NS_CONTENT_DOWNLOAD:
01058       case WID_NS_CONTENT_DOWNLOAD2:
01059         if (!_network_available) {
01060           ShowErrorMessage(STR_NETWORK_ERROR_NOTAVAILABLE, INVALID_STRING_ID, WL_ERROR);
01061         } else {
01062 #if defined(ENABLE_NETWORK)
01063           this->DeleteChildWindows(WC_QUERY_STRING); // Remove the parameter query window
01064 
01065           ShowMissingContentWindow(this->actives);
01066 #endif
01067         }
01068         break;
01069 
01070       case WID_NS_RESCAN_FILES:
01071       case WID_NS_RESCAN_FILES2:
01072         ScanNewGRFFiles(this);
01073         break;
01074     }
01075   }
01076 
01077   virtual void OnNewGRFsScanned()
01078   {
01079     this->avail_sel = NULL;
01080     this->avail_pos = -1;
01081     this->avails.ForceRebuild();
01082     this->DeleteChildWindows(WC_QUERY_STRING); // Remove the parameter query window
01083     this->DeleteChildWindows(WC_TEXTFILE);     // Remove the view textfile window
01084   }
01085 
01086   virtual void OnDropdownSelect(int widget, int index)
01087   {
01088     if (!this->editable) return;
01089 
01090     ClearGRFConfigList(&this->actives);
01091     this->preset = index;
01092 
01093     if (index != -1) {
01094       this->actives = LoadGRFPresetFromConfig(_grf_preset_list[index]);
01095     }
01096     this->avails.ForceRebuild();
01097 
01098     ResetObjectToPlace();
01099     DeleteWindowByClass(WC_GRF_PARAMETERS);
01100     this->active_sel = NULL;
01101     this->InvalidateData(GOID_NEWGRF_PRESET_LOADED);
01102   }
01103 
01104   virtual void OnQueryTextFinished(char *str)
01105   {
01106     if (str == NULL) return;
01107 
01108     SaveGRFPresetToConfig(str, this->actives);
01109     GetGRFPresetList(&_grf_preset_list);
01110 
01111     /* Switch to this preset */
01112     for (uint i = 0; i < _grf_preset_list.Length(); i++) {
01113       if (_grf_preset_list[i] != NULL && strcmp(_grf_preset_list[i], str) == 0) {
01114         this->preset = i;
01115         break;
01116       }
01117     }
01118 
01119     this->InvalidateData();
01120   }
01121 
01127   virtual void OnInvalidateData(int data = 0, bool gui_scope = true)
01128   {
01129     if (!gui_scope) return;
01130     switch (data) {
01131       default:
01132         /* Nothing important to do */
01133         break;
01134 
01135       case GOID_NEWGRF_RESCANNED:
01136         /* Search the list for items that are now found and mark them as such. */
01137         for (GRFConfig **l = &this->actives; *l != NULL; l = &(*l)->next) {
01138           GRFConfig *c = *l;
01139           bool compatible = HasBit(c->flags, GCF_COMPATIBLE);
01140           if (c->status != GCS_NOT_FOUND && !compatible) continue;
01141 
01142           const GRFConfig *f = FindGRFConfig(c->ident.grfid, FGCM_EXACT, compatible ? c->original_md5sum : c->ident.md5sum);
01143           if (f == NULL || HasBit(f->flags, GCF_INVALID)) continue;
01144 
01145           *l = new GRFConfig(*f);
01146           (*l)->next = c->next;
01147 
01148           if (active_sel == c) active_sel = *l;
01149 
01150           delete c;
01151         }
01152 
01153         this->avails.ForceRebuild();
01154         /* FALL THROUGH */
01155       case GOID_NEWGRF_LIST_EDITED:
01156         this->preset = -1;
01157         /* FALL THROUGH */
01158       case GOID_NEWGRF_PRESET_LOADED: {
01159         /* Update scrollbars */
01160         int i = 0;
01161         for (const GRFConfig *c = this->actives; c != NULL; c = c->next, i++) {}
01162 
01163         this->vscroll->SetCapacityFromWidget(this, WID_NS_FILE_LIST);
01164         this->vscroll->SetCount(i + 1); // Reserve empty space for drag and drop handling.
01165 
01166         this->vscroll2->SetCapacityFromWidget(this, WID_NS_AVAIL_LIST);
01167         if (this->avail_pos >= 0) this->vscroll2->ScrollTowards(this->avail_pos);
01168         break;
01169       }
01170     }
01171 
01172     this->BuildAvailables();
01173 
01174     this->SetWidgetsDisabledState(!this->editable,
01175       WID_NS_PRESET_LIST,
01176       WID_NS_APPLY_CHANGES,
01177       WID_NS_TOGGLE_PALETTE,
01178       WIDGET_LIST_END
01179     );
01180     this->SetWidgetDisabledState(WID_NS_ADD, !this->editable || this->avail_sel == NULL || HasBit(this->avail_sel->flags, GCF_INVALID));
01181 
01182     bool disable_all = this->active_sel == NULL || !this->editable;
01183     this->SetWidgetsDisabledState(disable_all,
01184       WID_NS_REMOVE,
01185       WID_NS_MOVE_UP,
01186       WID_NS_MOVE_DOWN,
01187       WIDGET_LIST_END
01188     );
01189 
01190     const GRFConfig *c = (this->avail_sel == NULL) ? this->active_sel : this->avail_sel;
01191     for (TextfileType tft = TFT_BEGIN; tft < TFT_END; tft++) {
01192       this->SetWidgetDisabledState(WID_NS_NEWGRF_TEXTFILE + tft, c == NULL || c->GetTextfile(tft) == NULL);
01193     }
01194     this->SetWidgetDisabledState(WID_NS_OPEN_URL, c == NULL || StrEmpty(c->GetURL()));
01195 
01196     this->SetWidgetDisabledState(WID_NS_SET_PARAMETERS, !this->show_params || this->active_sel == NULL || this->active_sel->num_valid_params == 0);
01197     this->SetWidgetDisabledState(WID_NS_VIEW_PARAMETERS, !this->show_params || this->active_sel == NULL || this->active_sel->num_valid_params == 0);
01198     this->SetWidgetDisabledState(WID_NS_TOGGLE_PALETTE, disable_all);
01199 
01200     if (!disable_all) {
01201       /* All widgets are now enabled, so disable widgets we can't use */
01202       if (this->active_sel == this->actives)    this->DisableWidget(WID_NS_MOVE_UP);
01203       if (this->active_sel->next == NULL)       this->DisableWidget(WID_NS_MOVE_DOWN);
01204       if (this->active_sel->IsOpenTTDBaseGRF()) this->DisableWidget(WID_NS_REMOVE);
01205     }
01206 
01207     this->SetWidgetDisabledState(WID_NS_PRESET_DELETE, this->preset == -1);
01208 
01209     bool has_missing = false;
01210     bool has_compatible = false;
01211     for (const GRFConfig *c = this->actives; !has_missing && c != NULL; c = c->next) {
01212       has_missing    |= c->status == GCS_NOT_FOUND;
01213       has_compatible |= HasBit(c->flags, GCF_COMPATIBLE);
01214     }
01215     uint32 widget_data;
01216     StringID tool_tip;
01217     if (has_missing || has_compatible) {
01218       widget_data = STR_NEWGRF_SETTINGS_FIND_MISSING_CONTENT_BUTTON;
01219       tool_tip    = STR_NEWGRF_SETTINGS_FIND_MISSING_CONTENT_TOOLTIP;
01220     } else {
01221       widget_data = STR_INTRO_ONLINE_CONTENT;
01222       tool_tip    = STR_INTRO_TOOLTIP_ONLINE_CONTENT;
01223     }
01224     this->GetWidget<NWidgetCore>(WID_NS_CONTENT_DOWNLOAD)->widget_data  = widget_data;
01225     this->GetWidget<NWidgetCore>(WID_NS_CONTENT_DOWNLOAD)->tool_tip     = tool_tip;
01226     this->GetWidget<NWidgetCore>(WID_NS_CONTENT_DOWNLOAD2)->widget_data = widget_data;
01227     this->GetWidget<NWidgetCore>(WID_NS_CONTENT_DOWNLOAD2)->tool_tip    = tool_tip;
01228 
01229     this->SetWidgetDisabledState(WID_NS_PRESET_SAVE, has_missing);
01230   }
01231 
01232   virtual EventState OnKeyPress(uint16 key, uint16 keycode)
01233   {
01234     if (!this->editable) return ES_NOT_HANDLED;
01235 
01236     switch (keycode) {
01237       case WKC_UP:
01238         /* scroll up by one */
01239         if (this->avail_pos > 0) this->avail_pos--;
01240         break;
01241 
01242       case WKC_DOWN:
01243         /* scroll down by one */
01244         if (this->avail_pos < (int)this->avails.Length() - 1) this->avail_pos++;
01245         break;
01246 
01247       case WKC_PAGEUP:
01248         /* scroll up a page */
01249         this->avail_pos = (this->avail_pos < this->vscroll2->GetCapacity()) ? 0 : this->avail_pos - this->vscroll2->GetCapacity();
01250         break;
01251 
01252       case WKC_PAGEDOWN:
01253         /* scroll down a page */
01254         this->avail_pos = min(this->avail_pos + this->vscroll2->GetCapacity(), (int)this->avails.Length() - 1);
01255         break;
01256 
01257       case WKC_HOME:
01258         /* jump to beginning */
01259         this->avail_pos = 0;
01260         break;
01261 
01262       case WKC_END:
01263         /* jump to end */
01264         this->avail_pos = this->avails.Length() - 1;
01265         break;
01266 
01267       default:
01268         return ES_NOT_HANDLED;
01269     }
01270 
01271     if (this->avails.Length() == 0) this->avail_pos = -1;
01272     if (this->avail_pos >= 0) {
01273       this->avail_sel = this->avails[this->avail_pos];
01274       this->vscroll2->ScrollTowards(this->avail_pos);
01275       this->InvalidateData(0);
01276     }
01277 
01278     return ES_HANDLED;
01279   }
01280 
01281   virtual void OnEditboxChanged(int wid)
01282   {
01283     if (!this->editable) return;
01284 
01285     string_filter.SetFilterTerm(this->filter_editbox.text.buf);
01286     this->avails.SetFilterState(!string_filter.IsEmpty());
01287     this->avails.ForceRebuild();
01288     this->InvalidateData(0);
01289   }
01290 
01291   virtual void OnDragDrop(Point pt, int widget)
01292   {
01293     if (!this->editable) return;
01294 
01295     if (widget == WID_NS_FILE_LIST) {
01296       if (this->active_sel != NULL) {
01297         /* Get pointer to the selected file in the active list. */
01298         int from_pos = 0;
01299         GRFConfig **from_prev;
01300         for (from_prev = &this->actives; *from_prev != this->active_sel; from_prev = &(*from_prev)->next, from_pos++) {}
01301 
01302         /* Gets the drag-and-drop destination offset. Ignore the last dummy line. */
01303         int to_pos = min(this->vscroll->GetScrolledRowFromWidget(pt.y, this, WID_NS_FILE_LIST), this->vscroll->GetCount() - 2);
01304         if (to_pos != from_pos) { // Don't move NewGRF file over itself.
01305           /* Get pointer to destination position. */
01306           GRFConfig **to_prev = &this->actives;
01307           for (int i = from_pos < to_pos ? -1 : 0; *to_prev != NULL && i < to_pos; to_prev = &(*to_prev)->next, i++) {}
01308 
01309           /* Detach NewGRF file from its original position. */
01310           *from_prev = this->active_sel->next;
01311 
01312           /* Attach NewGRF file to its new position. */
01313           this->active_sel->next = *to_prev;
01314           *to_prev = this->active_sel;
01315 
01316           this->vscroll->ScrollTowards(to_pos);
01317           this->preset = -1;
01318           this->InvalidateData();
01319         }
01320       } else if (this->avail_sel != NULL) {
01321         int to_pos = min(this->vscroll->GetScrolledRowFromWidget(pt.y, this, WID_NS_FILE_LIST), this->vscroll->GetCount() - 1);
01322         this->AddGRFToActive(to_pos);
01323       }
01324     } else if (widget == WID_NS_AVAIL_LIST && this->active_sel != NULL) {
01325       /* Remove active NewGRF file by dragging it over available list. */
01326       Point dummy = {-1, -1};
01327       this->OnClick(dummy, WID_NS_REMOVE, 1);
01328     }
01329 
01330     ResetObjectToPlace();
01331 
01332     if (this->active_over != -1) {
01333       /* End of drag-and-drop, hide dragged destination highlight. */
01334       this->SetWidgetDirty(this->active_over == -2 ? WID_NS_AVAIL_LIST : WID_NS_FILE_LIST);
01335       this->active_over = -1;
01336     }
01337   }
01338 
01339   virtual void OnMouseDrag(Point pt, int widget)
01340   {
01341     if (!this->editable) return;
01342 
01343     if (widget == WID_NS_FILE_LIST && (this->active_sel != NULL || this->avail_sel != NULL)) {
01344       /* An NewGRF file is dragged over the active list. */
01345       int to_pos = this->vscroll->GetScrolledRowFromWidget(pt.y, this, WID_NS_FILE_LIST);
01346       /* Skip the last dummy line if the source is from the active list. */
01347       to_pos = min(to_pos, this->vscroll->GetCount() - (this->active_sel != NULL ? 2 : 1));
01348 
01349       if (to_pos != this->active_over) {
01350         this->active_over = to_pos;
01351         this->SetWidgetDirty(WID_NS_FILE_LIST);
01352       }
01353     } else if (widget == WID_NS_AVAIL_LIST && this->active_sel != NULL) {
01354       this->active_over = -2;
01355       this->SetWidgetDirty(WID_NS_AVAIL_LIST);
01356     } else if (this->active_over != -1) {
01357       this->SetWidgetDirty(this->active_over == -2 ? WID_NS_AVAIL_LIST : WID_NS_FILE_LIST);
01358       this->active_over = -1;
01359     }
01360   }
01361 
01362 private:
01364   static int CDECL NameSorter(const GRFConfig * const *a, const GRFConfig * const *b)
01365   {
01366     int i = strnatcmp((*a)->GetName(), (*b)->GetName()); // Sort by name (natural sorting).
01367     if (i != 0) return i;
01368 
01369     i = (*a)->version - (*b)->version;
01370     if (i != 0) return i;
01371 
01372     return memcmp((*a)->ident.md5sum, (*b)->ident.md5sum, lengthof((*b)->ident.md5sum));
01373   }
01374 
01376   static bool CDECL TagNameFilter(const GRFConfig * const *a, StringFilter &filter)
01377   {
01378     filter.ResetState();
01379     filter.AddLine((*a)->GetName());
01380     filter.AddLine((*a)->filename);
01381     filter.AddLine((*a)->GetDescription());
01382     return filter.GetState();;
01383   }
01384 
01385   void BuildAvailables()
01386   {
01387     if (!this->avails.NeedRebuild()) return;
01388 
01389     this->avails.Clear();
01390 
01391     for (const GRFConfig *c = _all_grfs; c != NULL; c = c->next) {
01392       bool found = false;
01393       for (const GRFConfig *grf = this->actives; grf != NULL && !found; grf = grf->next) found = grf->ident.HasGrfIdentifier(c->ident.grfid, c->ident.md5sum);
01394       if (found) continue;
01395 
01396       if (_settings_client.gui.newgrf_show_old_versions) {
01397         *this->avails.Append() = c;
01398       } else {
01399         const GRFConfig *best = FindGRFConfig(c->ident.grfid, HasBit(c->flags, GCF_INVALID) ? FGCM_NEWEST : FGCM_NEWEST_VALID);
01400         /*
01401          * If the best version is 0, then all NewGRF with this GRF ID
01402          * have version 0, so for backward compatibility reasons we
01403          * want to show them all.
01404          * If we are the best version, then we definitely want to
01405          * show that NewGRF!.
01406          */
01407         if (best->version == 0 || best->ident.HasGrfIdentifier(c->ident.grfid, c->ident.md5sum)) {
01408           *this->avails.Append() = c;
01409         }
01410       }
01411     }
01412 
01413     this->avails.Filter(this->string_filter);
01414     this->avails.Compact();
01415     this->avails.RebuildDone();
01416     this->avails.Sort();
01417 
01418     if (this->avail_sel != NULL) {
01419       this->avail_pos = this->avails.FindIndex(this->avail_sel);
01420       if (this->avail_pos < 0) this->avail_sel = NULL;
01421     }
01422 
01423     this->vscroll2->SetCount(this->avails.Length()); // Update the scrollbar
01424   }
01425 
01431   bool AddGRFToActive(int ins_pos = -1)
01432   {
01433     if (this->avail_sel == NULL || !this->editable || HasBit(this->avail_sel->flags, GCF_INVALID)) return false;
01434 
01435     GRFConfig **entry = NULL;
01436     GRFConfig **list;
01437     /* Find last entry in the list, checking for duplicate grfid on the way */
01438     for (list = &this->actives; *list != NULL; list = &(*list)->next, ins_pos--) {
01439       if (ins_pos == 0) entry = list; // Insert position? Save.
01440       if ((*list)->ident.grfid == this->avail_sel->ident.grfid) {
01441         ShowErrorMessage(STR_NEWGRF_DUPLICATE_GRFID, INVALID_STRING_ID, WL_INFO);
01442         return false;
01443       }
01444     }
01445     if (entry == NULL) entry = list;
01446 
01447     GRFConfig *c = new GRFConfig(*this->avail_sel); // Copy GRF details from scanned list.
01448     c->SetParameterDefaults();
01449 
01450     /* Insert GRF config to configuration list. */
01451     c->next = *entry;
01452     *entry = c;
01453 
01454     /* Select next (or previous, if last one) item in the list. */
01455     int new_pos = this->avail_pos + 1;
01456     if (new_pos >= (int)this->avails.Length()) new_pos = this->avail_pos - 1;
01457     this->avail_pos = new_pos;
01458     if (new_pos >= 0) this->avail_sel = this->avails[new_pos];
01459 
01460     this->avails.ForceRebuild();
01461     this->InvalidateData(GOID_NEWGRF_LIST_EDITED);
01462     return true;
01463   }
01464 };
01465 
01466 #if defined(ENABLE_NETWORK)
01467 
01471 void ShowMissingContentWindow(const GRFConfig *list)
01472 {
01473   /* Only show the things in the current list, or everything when nothing's selected */
01474   ContentVector cv;
01475   for (const GRFConfig *c = list; c != NULL; c = c->next) {
01476     if (c->status != GCS_NOT_FOUND && !HasBit(c->flags, GCF_COMPATIBLE)) continue;
01477 
01478     ContentInfo *ci = new ContentInfo();
01479     ci->type = CONTENT_TYPE_NEWGRF;
01480     ci->state = ContentInfo::DOES_NOT_EXIST;
01481     ttd_strlcpy(ci->name, c->GetName(), lengthof(ci->name));
01482     ci->unique_id = BSWAP32(c->ident.grfid);
01483     memcpy(ci->md5sum, HasBit(c->flags, GCF_COMPATIBLE) ? c->original_md5sum : c->ident.md5sum, sizeof(ci->md5sum));
01484     *cv.Append() = ci;
01485   }
01486   ShowNetworkContentListWindow(cv.Length() == 0 ? NULL : &cv, CONTENT_TYPE_NEWGRF);
01487 }
01488 #endif
01489 
01490 Listing NewGRFWindow::last_sorting     = {false, 0};
01491 Filtering NewGRFWindow::last_filtering = {false, 0};
01492 
01493 NewGRFWindow::GUIGRFConfigList::SortFunction * const NewGRFWindow::sorter_funcs[] = {
01494   &NameSorter,
01495 };
01496 
01497 NewGRFWindow::GUIGRFConfigList::FilterFunction * const NewGRFWindow::filter_funcs[] = {
01498   &TagNameFilter,
01499 };
01500 
01507 class NWidgetNewGRFDisplay : public NWidgetContainer {
01508 public:
01509   static const uint INTER_LIST_SPACING;      
01510   static const uint INTER_COLUMN_SPACING;    
01511   static const uint MAX_EXTRA_INFO_WIDTH;    
01512   static const uint MIN_EXTRA_FOR_3_COLUMNS; 
01513 
01514   NWidgetBase *avs; 
01515   NWidgetBase *acs; 
01516   NWidgetBase *inf; 
01517   bool editable;    
01518 
01519   NWidgetNewGRFDisplay(NWidgetBase *avs, NWidgetBase *acs, NWidgetBase *inf) : NWidgetContainer(NWID_HORIZONTAL)
01520   {
01521     this->avs = avs;
01522     this->acs = acs;
01523     this->inf = inf;
01524 
01525     this->Add(this->avs);
01526     this->Add(this->acs);
01527     this->Add(this->inf);
01528 
01529     this->editable = true; // Temporary setting, 'real' value is set in SetupSmallestSize().
01530   }
01531 
01532   virtual void SetupSmallestSize(Window *w, bool init_array)
01533   {
01534     /* Copy state flag from the window. */
01535     assert(dynamic_cast<NewGRFWindow *>(w) != NULL);
01536     NewGRFWindow *ngw = (NewGRFWindow *)w;
01537     this->editable = ngw->editable;
01538 
01539     this->avs->SetupSmallestSize(w, init_array);
01540     this->acs->SetupSmallestSize(w, init_array);
01541     this->inf->SetupSmallestSize(w, init_array);
01542 
01543     uint min_avs_width = this->avs->smallest_x + this->avs->padding_left + this->avs->padding_right;
01544     uint min_acs_width = this->acs->smallest_x + this->acs->padding_left + this->acs->padding_right;
01545     uint min_inf_width = this->inf->smallest_x + this->inf->padding_left + this->inf->padding_right;
01546 
01547     uint min_avs_height = this->avs->smallest_y + this->avs->padding_top + this->avs->padding_bottom;
01548     uint min_acs_height = this->acs->smallest_y + this->acs->padding_top + this->acs->padding_bottom;
01549     uint min_inf_height = this->inf->smallest_y + this->inf->padding_top + this->inf->padding_bottom;
01550 
01551     /* Smallest window is in two column mode. */
01552     this->smallest_x = max(min_avs_width, min_acs_width) + INTER_COLUMN_SPACING + min_inf_width;
01553     this->smallest_y = max(min_inf_height, min_acs_height + INTER_LIST_SPACING + min_avs_height);
01554 
01555     /* Filling. */
01556     this->fill_x = LeastCommonMultiple(this->avs->fill_x, this->acs->fill_x);
01557     if (this->inf->fill_x > 0 && (this->fill_x == 0 || this->fill_x > this->inf->fill_x)) this->fill_x = this->inf->fill_x;
01558 
01559     this->fill_y = this->avs->fill_y;
01560     if (this->acs->fill_y > 0 && (this->fill_y == 0 || this->fill_y > this->acs->fill_y)) this->fill_y = this->acs->fill_y;
01561     this->fill_y = LeastCommonMultiple(this->fill_y, this->inf->fill_y);
01562 
01563     /* Resizing. */
01564     this->resize_x = LeastCommonMultiple(this->avs->resize_x, this->acs->resize_x);
01565     if (this->inf->resize_x > 0 && (this->resize_x == 0 || this->resize_x > this->inf->resize_x)) this->resize_x = this->inf->resize_x;
01566 
01567     this->resize_y = this->avs->resize_y;
01568     if (this->acs->resize_y > 0 && (this->resize_y == 0 || this->resize_y > this->acs->resize_y)) this->resize_y = this->acs->resize_y;
01569     this->resize_y = LeastCommonMultiple(this->resize_y, this->inf->resize_y);
01570 
01571     /* Make sure the height suits the 3 column (resp. not-editable) format; the 2 column format can easily fill space between the lists */
01572     this->smallest_y = ComputeMaxSize(min_acs_height, this->smallest_y + this->resize_y - 1, this->resize_y);
01573   }
01574 
01575   virtual void AssignSizePosition(SizingType sizing, uint x, uint y, uint given_width, uint given_height, bool rtl)
01576   {
01577     this->StoreSizePosition(sizing, x, y, given_width, given_height);
01578 
01579     uint min_avs_width = this->avs->smallest_x + this->avs->padding_left + this->avs->padding_right;
01580     uint min_acs_width = this->acs->smallest_x + this->acs->padding_left + this->acs->padding_right;
01581     uint min_inf_width = this->inf->smallest_x + this->inf->padding_left + this->inf->padding_right;
01582 
01583     uint min_list_width = max(min_avs_width, min_acs_width); // Smallest width of the lists such that they have equal width (incl padding).
01584     uint avs_extra_width = min_list_width - min_avs_width;   // Additional width needed for avs to reach min_list_width.
01585     uint acs_extra_width = min_list_width - min_acs_width;   // Additional width needed for acs to reach min_list_width.
01586 
01587     /* Use 2 or 3 colmuns? */
01588     uint min_three_columns = min_avs_width + min_acs_width + min_inf_width + 2 * INTER_COLUMN_SPACING;
01589     uint min_two_columns   = min_list_width + min_inf_width + INTER_COLUMN_SPACING;
01590     bool use_three_columns = this->editable && (min_three_columns + MIN_EXTRA_FOR_3_COLUMNS <= given_width);
01591 
01592     /* Info panel is a seperate column in both modes. Compute its width first. */
01593     uint extra_width, inf_width;
01594     if (use_three_columns) {
01595       extra_width = given_width - min_three_columns;
01596       inf_width = min(MAX_EXTRA_INFO_WIDTH, extra_width / 2);
01597     } else {
01598       extra_width = given_width - min_two_columns;
01599       inf_width = min(MAX_EXTRA_INFO_WIDTH, extra_width / 2);
01600     }
01601     inf_width = ComputeMaxSize(this->inf->smallest_x, this->inf->smallest_x + inf_width, this->inf->GetHorizontalStepSize(sizing));
01602     extra_width -= inf_width - this->inf->smallest_x;
01603 
01604     uint inf_height = ComputeMaxSize(this->inf->smallest_y, given_height, this->inf->GetVerticalStepSize(sizing));
01605 
01606     if (use_three_columns) {
01607       /* Three column display, first make both lists equally wide, then divide whatever is left between both lists.
01608        * Only keep track of what avs gets, all other space goes to acs. */
01609       uint avs_width = min(avs_extra_width, extra_width);
01610       extra_width -= avs_width;
01611       extra_width -= min(acs_extra_width, extra_width);
01612       avs_width += extra_width / 2;
01613 
01614       avs_width = ComputeMaxSize(this->avs->smallest_x, this->avs->smallest_x + avs_width, this->avs->GetHorizontalStepSize(sizing));
01615 
01616       uint acs_width = given_width - // Remaining space, including horizontal padding.
01617           inf_width - this->inf->padding_left - this->inf->padding_right -
01618           avs_width - this->avs->padding_left - this->avs->padding_right - 2 * INTER_COLUMN_SPACING;
01619       acs_width = ComputeMaxSize(min_acs_width, acs_width, this->acs->GetHorizontalStepSize(sizing)) -
01620           this->acs->padding_left - this->acs->padding_right;
01621 
01622       /* Never use fill_y on these; the minimal size is choosen, so that the 3 column view looks nice */
01623       uint avs_height = ComputeMaxSize(this->avs->smallest_y, given_height, this->avs->resize_y);
01624       uint acs_height = ComputeMaxSize(this->acs->smallest_y, given_height, this->acs->resize_y);
01625 
01626       /* Assign size and position to the childs. */
01627       if (rtl) {
01628         x += this->inf->padding_left;
01629         this->inf->AssignSizePosition(sizing, x, y + this->inf->padding_top, inf_width, inf_height, rtl);
01630         x += inf_width + this->inf->padding_right + INTER_COLUMN_SPACING;
01631       } else {
01632         x += this->avs->padding_left;
01633         this->avs->AssignSizePosition(sizing, x, y + this->avs->padding_top, avs_width, avs_height, rtl);
01634         x += avs_width + this->avs->padding_right + INTER_COLUMN_SPACING;
01635       }
01636 
01637       x += this->acs->padding_left;
01638       this->acs->AssignSizePosition(sizing, x, y + this->acs->padding_top, acs_width, acs_height, rtl);
01639       x += acs_width + this->acs->padding_right + INTER_COLUMN_SPACING;
01640 
01641       if (rtl) {
01642         x += this->avs->padding_left;
01643         this->avs->AssignSizePosition(sizing, x, y + this->avs->padding_top, avs_width, avs_height, rtl);
01644       } else {
01645         x += this->inf->padding_left;
01646         this->inf->AssignSizePosition(sizing, x, y + this->inf->padding_top, inf_width, inf_height, rtl);
01647       }
01648     } else {
01649       /* Two columns, all space in extra_width goes to both lists. Since the lists are underneath each other,
01650        * the column is min_list_width wide at least. */
01651       uint avs_width = ComputeMaxSize(this->avs->smallest_x, this->avs->smallest_x + avs_extra_width + extra_width,
01652           this->avs->GetHorizontalStepSize(sizing));
01653       uint acs_width = ComputeMaxSize(this->acs->smallest_x, this->acs->smallest_x + acs_extra_width + extra_width,
01654           this->acs->GetHorizontalStepSize(sizing));
01655 
01656       uint min_avs_height = (!this->editable) ? 0 : this->avs->smallest_y + this->avs->padding_top + this->avs->padding_bottom + INTER_LIST_SPACING;
01657       uint min_acs_height = this->acs->smallest_y + this->acs->padding_top + this->acs->padding_bottom;
01658       uint extra_height = given_height - min_acs_height - min_avs_height;
01659 
01660       /* Never use fill_y on these; instead use the INTER_LIST_SPACING as filler */
01661       uint avs_height = ComputeMaxSize(this->avs->smallest_y, this->avs->smallest_y + extra_height / 2, this->avs->resize_y);
01662       if (this->editable) extra_height -= avs_height - this->avs->smallest_y;
01663       uint acs_height = ComputeMaxSize(this->acs->smallest_y, this->acs->smallest_y + extra_height, this->acs->resize_y);
01664 
01665       /* Assign size and position to the childs. */
01666       if (rtl) {
01667         x += this->inf->padding_left;
01668         this->inf->AssignSizePosition(sizing, x, y + this->inf->padding_top, inf_width, inf_height, rtl);
01669         x += inf_width + this->inf->padding_right + INTER_COLUMN_SPACING;
01670 
01671         this->acs->AssignSizePosition(sizing, x + this->acs->padding_left, y + this->acs->padding_top, acs_width, acs_height, rtl);
01672         if (this->editable) {
01673           this->avs->AssignSizePosition(sizing, x + this->avs->padding_left, y + given_height - avs_height - this->avs->padding_bottom, avs_width, avs_height, rtl);
01674         } else {
01675           this->avs->AssignSizePosition(sizing, 0, 0, this->avs->smallest_x, this->avs->smallest_y, rtl);
01676         }
01677       } else {
01678         this->acs->AssignSizePosition(sizing, x + this->acs->padding_left, y + this->acs->padding_top, acs_width, acs_height, rtl);
01679         if (this->editable) {
01680           this->avs->AssignSizePosition(sizing, x + this->avs->padding_left, y + given_height - avs_height - this->avs->padding_bottom, avs_width, avs_height, rtl);
01681         } else {
01682           this->avs->AssignSizePosition(sizing, 0, 0, this->avs->smallest_x, this->avs->smallest_y, rtl);
01683         }
01684         uint dx = this->acs->current_x + this->acs->padding_left + this->acs->padding_right;
01685         if (this->editable) {
01686           dx = max(dx, this->avs->current_x + this->avs->padding_left + this->avs->padding_right);
01687         }
01688         x += dx + INTER_COLUMN_SPACING + this->inf->padding_left;
01689         this->inf->AssignSizePosition(sizing, x, y + this->inf->padding_top, inf_width, inf_height, rtl);
01690       }
01691     }
01692   }
01693 
01694   virtual NWidgetCore *GetWidgetFromPos(int x, int y)
01695   {
01696     if (!IsInsideBS(x, this->pos_x, this->current_x) || !IsInsideBS(y, this->pos_y, this->current_y)) return NULL;
01697 
01698     NWidgetCore *nw = (this->editable) ? this->avs->GetWidgetFromPos(x, y) : NULL;
01699     if (nw == NULL) nw = this->acs->GetWidgetFromPos(x, y);
01700     if (nw == NULL) nw = this->inf->GetWidgetFromPos(x, y);
01701     return nw;
01702   }
01703 
01704   virtual void Draw(const Window *w)
01705   {
01706     if (this->editable) this->avs->Draw(w);
01707     this->acs->Draw(w);
01708     this->inf->Draw(w);
01709   }
01710 };
01711 
01712 const uint NWidgetNewGRFDisplay::INTER_LIST_SPACING      = WD_RESIZEBOX_WIDTH + 1;
01713 const uint NWidgetNewGRFDisplay::INTER_COLUMN_SPACING    = WD_RESIZEBOX_WIDTH;
01714 const uint NWidgetNewGRFDisplay::MAX_EXTRA_INFO_WIDTH    = 150;
01715 const uint NWidgetNewGRFDisplay::MIN_EXTRA_FOR_3_COLUMNS = 50;
01716 
01717 static const NWidgetPart _nested_newgrf_actives_widgets[] = {
01718   /* Left side, presets. */
01719   NWidget(NWID_HORIZONTAL),
01720     NWidget(WWT_TEXT, COLOUR_MAUVE), SetDataTip(STR_NEWGRF_SETTINGS_SELECT_PRESET, STR_NULL),
01721         SetPadding(0, WD_FRAMETEXT_RIGHT, 0, 0),
01722     NWidget(WWT_DROPDOWN, COLOUR_YELLOW, WID_NS_PRESET_LIST), SetFill(1, 0), SetResize(1, 0),
01723         SetDataTip(STR_JUST_STRING, STR_NEWGRF_SETTINGS_PRESET_LIST_TOOLTIP),
01724   EndContainer(),
01725   NWidget(NWID_HORIZONTAL, NC_EQUALSIZE),
01726     NWidget(WWT_PUSHTXTBTN, COLOUR_YELLOW, WID_NS_PRESET_SAVE), SetFill(1, 0), SetResize(1, 0),
01727         SetDataTip(STR_NEWGRF_SETTINGS_PRESET_SAVE, STR_NEWGRF_SETTINGS_PRESET_SAVE_TOOLTIP),
01728     NWidget(WWT_PUSHTXTBTN, COLOUR_YELLOW, WID_NS_PRESET_DELETE), SetFill(1, 0), SetResize(1, 0),
01729         SetDataTip(STR_NEWGRF_SETTINGS_PRESET_DELETE, STR_NEWGRF_SETTINGS_PRESET_DELETE_TOOLTIP),
01730   EndContainer(),
01731 
01732   NWidget(NWID_SPACER), SetMinimalSize(0, WD_RESIZEBOX_WIDTH), SetResize(1, 0), SetFill(1, 0),
01733   NWidget(WWT_PANEL, COLOUR_MAUVE),
01734     NWidget(WWT_LABEL, COLOUR_MAUVE), SetDataTip(STR_NEWGRF_SETTINGS_ACTIVE_LIST, STR_NULL),
01735         SetFill(1, 0), SetResize(1, 0), SetPadding(3, WD_FRAMETEXT_RIGHT, 0, WD_FRAMETEXT_LEFT),
01736     /* Left side, active grfs. */
01737     NWidget(NWID_HORIZONTAL), SetPadding(0, 2, 0, 2),
01738       NWidget(WWT_PANEL, COLOUR_MAUVE),
01739         NWidget(WWT_INSET, COLOUR_MAUVE, WID_NS_FILE_LIST), SetMinimalSize(100, 1), SetPadding(2, 2, 2, 2),
01740             SetFill(1, 1), SetResize(1, 1), SetScrollbar(WID_NS_SCROLLBAR), SetDataTip(STR_NULL, STR_NEWGRF_SETTINGS_FILE_TOOLTIP),
01741         EndContainer(),
01742       EndContainer(),
01743       NWidget(NWID_VSCROLLBAR, COLOUR_MAUVE, WID_NS_SCROLLBAR),
01744     EndContainer(),
01745     /* Buttons. */
01746     NWidget(NWID_SELECTION, INVALID_COLOUR, WID_NS_SHOW_REMOVE),
01747       NWidget(NWID_HORIZONTAL, NC_EQUALSIZE), SetPadding(2, 2, 2, 2), SetPIP(0, WD_RESIZEBOX_WIDTH, 0),
01748         NWidget(WWT_PUSHTXTBTN, COLOUR_YELLOW, WID_NS_REMOVE), SetFill(1, 0), SetResize(1, 0),
01749             SetDataTip(STR_NEWGRF_SETTINGS_REMOVE, STR_NEWGRF_SETTINGS_REMOVE_TOOLTIP),
01750         NWidget(NWID_VERTICAL),
01751           NWidget(WWT_PUSHTXTBTN, COLOUR_YELLOW, WID_NS_MOVE_UP), SetFill(1, 0), SetResize(1, 0),
01752               SetDataTip(STR_NEWGRF_SETTINGS_MOVEUP, STR_NEWGRF_SETTINGS_MOVEUP_TOOLTIP),
01753           NWidget(WWT_PUSHTXTBTN, COLOUR_YELLOW, WID_NS_MOVE_DOWN), SetFill(1, 0), SetResize(1, 0),
01754               SetDataTip(STR_NEWGRF_SETTINGS_MOVEDOWN, STR_NEWGRF_SETTINGS_MOVEDOWN_TOOLTIP),
01755         EndContainer(),
01756       EndContainer(),
01757 
01758       NWidget(NWID_VERTICAL, NC_EQUALSIZE), SetPadding(2, 2, 2, 2),
01759         NWidget(WWT_PUSHTXTBTN, COLOUR_YELLOW, WID_NS_RESCAN_FILES2), SetFill(1, 0), SetResize(1, 0),
01760             SetDataTip(STR_NEWGRF_SETTINGS_RESCAN_FILES, STR_NEWGRF_SETTINGS_RESCAN_FILES_TOOLTIP),
01761         NWidget(WWT_PUSHTXTBTN, COLOUR_YELLOW, WID_NS_CONTENT_DOWNLOAD2), SetFill(1, 0), SetResize(1, 0),
01762             SetDataTip(STR_INTRO_ONLINE_CONTENT, STR_INTRO_TOOLTIP_ONLINE_CONTENT),
01763       EndContainer(),
01764     EndContainer(),
01765   EndContainer(),
01766 };
01767 
01768 static const NWidgetPart _nested_newgrf_availables_widgets[] = {
01769   NWidget(WWT_PANEL, COLOUR_MAUVE),
01770     NWidget(WWT_LABEL, COLOUR_MAUVE), SetDataTip(STR_NEWGRF_SETTINGS_INACTIVE_LIST, STR_NULL),
01771         SetFill(1, 0), SetResize(1, 0), SetPadding(3, WD_FRAMETEXT_RIGHT, 0, WD_FRAMETEXT_LEFT),
01772     /* Left side, available grfs, filter edit box. */
01773     NWidget(NWID_HORIZONTAL), SetPadding(WD_TEXTPANEL_TOP, 0, WD_TEXTPANEL_BOTTOM, 0),
01774         SetPIP(WD_FRAMETEXT_LEFT, WD_FRAMETEXT_RIGHT, WD_FRAMETEXT_RIGHT),
01775       NWidget(WWT_TEXT, COLOUR_MAUVE), SetFill(0, 1), SetDataTip(STR_NEWGRF_FILTER_TITLE, STR_NULL),
01776       NWidget(WWT_EDITBOX, COLOUR_MAUVE, WID_NS_FILTER), SetFill(1, 0), SetMinimalSize(50, 12), SetResize(1, 0),
01777           SetDataTip(STR_LIST_FILTER_OSKTITLE, STR_LIST_FILTER_TOOLTIP),
01778     EndContainer(),
01779     /* Left side, available grfs. */
01780     NWidget(NWID_HORIZONTAL), SetPadding(0, 2, 0, 2),
01781       NWidget(WWT_PANEL, COLOUR_MAUVE),
01782         NWidget(WWT_INSET, COLOUR_MAUVE, WID_NS_AVAIL_LIST), SetMinimalSize(100, 1), SetPadding(2, 2, 2, 2),
01783             SetFill(1, 1), SetResize(1, 1), SetScrollbar(WID_NS_SCROLL2BAR),
01784         EndContainer(),
01785       EndContainer(),
01786       NWidget(NWID_VSCROLLBAR, COLOUR_MAUVE, WID_NS_SCROLL2BAR),
01787     EndContainer(),
01788     /* Left side, available grfs, buttons. */
01789     NWidget(NWID_HORIZONTAL, NC_EQUALSIZE), SetPadding(2, 2, 2, 2), SetPIP(0, WD_RESIZEBOX_WIDTH, 0),
01790       NWidget(WWT_PUSHTXTBTN, COLOUR_YELLOW, WID_NS_ADD), SetFill(1, 0), SetResize(1, 0),
01791           SetDataTip(STR_NEWGRF_SETTINGS_ADD, STR_NEWGRF_SETTINGS_ADD_FILE_TOOLTIP),
01792       NWidget(NWID_VERTICAL),
01793         NWidget(WWT_PUSHTXTBTN, COLOUR_YELLOW, WID_NS_RESCAN_FILES), SetFill(1, 0), SetResize(1, 0),
01794             SetDataTip(STR_NEWGRF_SETTINGS_RESCAN_FILES, STR_NEWGRF_SETTINGS_RESCAN_FILES_TOOLTIP),
01795         NWidget(WWT_PUSHTXTBTN, COLOUR_YELLOW, WID_NS_CONTENT_DOWNLOAD), SetFill(1, 0), SetResize(1, 0),
01796             SetDataTip(STR_INTRO_ONLINE_CONTENT, STR_INTRO_TOOLTIP_ONLINE_CONTENT),
01797       EndContainer(),
01798     EndContainer(),
01799   EndContainer(),
01800 };
01801 
01802 static const NWidgetPart _nested_newgrf_infopanel_widgets[] = {
01803   /* Right side, info panel. */
01804   NWidget(NWID_VERTICAL), SetPadding(0, 0, 2, 0),
01805     NWidget(WWT_PANEL, COLOUR_MAUVE), SetPadding(0, 0, 2, 0),
01806       NWidget(WWT_EMPTY, COLOUR_MAUVE, WID_NS_NEWGRF_INFO_TITLE), SetFill(1, 0), SetResize(1, 0),
01807       NWidget(WWT_EMPTY, COLOUR_MAUVE, WID_NS_NEWGRF_INFO), SetFill(1, 1), SetResize(1, 1), SetMinimalSize(150, 100),
01808     EndContainer(),
01809     NWidget(WWT_PUSHTXTBTN, COLOUR_YELLOW, WID_NS_OPEN_URL), SetFill(1, 0), SetResize(1, 0),
01810         SetDataTip(STR_CONTENT_OPEN_URL, STR_CONTENT_OPEN_URL_TOOLTIP),
01811     NWidget(WWT_PUSHTXTBTN, COLOUR_YELLOW, WID_NS_NEWGRF_TEXTFILE + TFT_README), SetFill(1, 0), SetResize(1, 0),
01812         SetDataTip(STR_TEXTFILE_VIEW_README, STR_NULL),
01813     NWidget(NWID_HORIZONTAL, NC_EQUALSIZE),
01814       NWidget(WWT_PUSHTXTBTN, COLOUR_YELLOW, WID_NS_NEWGRF_TEXTFILE + TFT_CHANGELOG), SetFill(1, 0), SetResize(1, 0),
01815           SetDataTip(STR_TEXTFILE_VIEW_CHANGELOG, STR_NULL),
01816       NWidget(WWT_PUSHTXTBTN, COLOUR_YELLOW, WID_NS_NEWGRF_TEXTFILE + TFT_LICENSE), SetFill(1, 0), SetResize(1, 0),
01817           SetDataTip(STR_TEXTFILE_VIEW_LICENCE, STR_NULL),
01818     EndContainer(),
01819   EndContainer(),
01820   NWidget(NWID_SELECTION, INVALID_COLOUR, WID_NS_SHOW_APPLY),
01821     /* Right side, buttons. */
01822     NWidget(NWID_HORIZONTAL, NC_EQUALSIZE), SetPIP(0, WD_RESIZEBOX_WIDTH, 0),
01823       NWidget(NWID_VERTICAL),
01824         NWidget(WWT_PUSHTXTBTN, COLOUR_YELLOW, WID_NS_SET_PARAMETERS), SetFill(1, 0), SetResize(1, 0),
01825             SetDataTip(STR_NEWGRF_SETTINGS_SET_PARAMETERS, STR_NULL),
01826         NWidget(WWT_PUSHTXTBTN, COLOUR_YELLOW, WID_NS_TOGGLE_PALETTE), SetFill(1, 0), SetResize(1, 0),
01827             SetDataTip(STR_NEWGRF_SETTINGS_TOGGLE_PALETTE, STR_NEWGRF_SETTINGS_TOGGLE_PALETTE_TOOLTIP),
01828       EndContainer(),
01829       NWidget(WWT_PUSHTXTBTN, COLOUR_YELLOW, WID_NS_APPLY_CHANGES), SetFill(1, 0), SetResize(1, 0),
01830           SetDataTip(STR_NEWGRF_SETTINGS_APPLY_CHANGES, STR_NULL),
01831     EndContainer(),
01832     NWidget(WWT_PUSHTXTBTN, COLOUR_YELLOW, WID_NS_VIEW_PARAMETERS), SetFill(1, 0), SetResize(1, 0),
01833       SetDataTip(STR_NEWGRF_SETTINGS_SHOW_PARAMETERS, STR_NULL),
01834   EndContainer(),
01835 };
01836 
01838 NWidgetBase* NewGRFDisplay(int *biggest_index)
01839 {
01840   NWidgetBase *avs = MakeNWidgets(_nested_newgrf_availables_widgets, lengthof(_nested_newgrf_availables_widgets), biggest_index, NULL);
01841 
01842   int biggest2;
01843   NWidgetBase *acs = MakeNWidgets(_nested_newgrf_actives_widgets, lengthof(_nested_newgrf_actives_widgets), &biggest2, NULL);
01844   *biggest_index = max(*biggest_index, biggest2);
01845 
01846   NWidgetBase *inf = MakeNWidgets(_nested_newgrf_infopanel_widgets, lengthof(_nested_newgrf_infopanel_widgets), &biggest2, NULL);
01847   *biggest_index = max(*biggest_index, biggest2);
01848 
01849   return new NWidgetNewGRFDisplay(avs, acs, inf);
01850 }
01851 
01852 /* Widget definition of the manage newgrfs window */
01853 static const NWidgetPart _nested_newgrf_widgets[] = {
01854   NWidget(NWID_HORIZONTAL),
01855     NWidget(WWT_CLOSEBOX, COLOUR_MAUVE),
01856     NWidget(WWT_CAPTION, COLOUR_MAUVE), SetDataTip(STR_NEWGRF_SETTINGS_CAPTION, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS),
01857   EndContainer(),
01858   NWidget(WWT_PANEL, COLOUR_MAUVE),
01859     NWidgetFunction(NewGRFDisplay), SetPadding(WD_RESIZEBOX_WIDTH, WD_RESIZEBOX_WIDTH, 2, WD_RESIZEBOX_WIDTH),
01860     /* Resize button. */
01861     NWidget(NWID_HORIZONTAL),
01862       NWidget(NWID_SPACER), SetFill(1, 0), SetResize(1, 0),
01863       NWidget(WWT_RESIZEBOX, COLOUR_MAUVE),
01864     EndContainer(),
01865   EndContainer(),
01866 };
01867 
01868 /* Window definition of the manage newgrfs window */
01869 static const WindowDesc _newgrf_desc(
01870   WDP_CENTER, 300, 263,
01871   WC_GAME_OPTIONS, WC_NONE,
01872   0,
01873   _nested_newgrf_widgets, lengthof(_nested_newgrf_widgets)
01874 );
01875 
01881 static void NewGRFConfirmationCallback(Window *w, bool confirmed)
01882 {
01883   if (confirmed) {
01884     DeleteWindowByClass(WC_GRF_PARAMETERS);
01885     NewGRFWindow *nw = dynamic_cast<NewGRFWindow*>(w);
01886 
01887     GamelogStartAction(GLAT_GRF);
01888     GamelogGRFUpdate(_grfconfig, nw->actives); // log GRF changes
01889     CopyGRFConfigList(nw->orig_list, nw->actives, false);
01890     ReloadNewGRFData();
01891     GamelogStopAction();
01892 
01893     /* Show new, updated list */
01894     GRFConfig *c;
01895     int i = 0;
01896     for (c = nw->actives; c != NULL && c != nw->active_sel; c = c->next, i++) {}
01897     CopyGRFConfigList(&nw->actives, *nw->orig_list, false);
01898     for (c = nw->actives; c != NULL && i > 0; c = c->next, i--) {}
01899     nw->active_sel = c;
01900     nw->avails.ForceRebuild();
01901 
01902     w->InvalidateData();
01903 
01904     ReInitAllWindows();
01905     DeleteWindowByClass(WC_BUILD_OBJECT);
01906   }
01907 }
01908 
01909 
01910 
01919 void ShowNewGRFSettings(bool editable, bool show_params, bool exec_changes, GRFConfig **config)
01920 {
01921   DeleteWindowByClass(WC_GAME_OPTIONS);
01922   new NewGRFWindow(&_newgrf_desc, editable, show_params, exec_changes, config);
01923 }
01924 
01926 static const NWidgetPart _nested_scan_progress_widgets[] = {
01927   NWidget(WWT_CAPTION, COLOUR_GREY), SetDataTip(STR_NEWGRF_SCAN_CAPTION, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS),
01928   NWidget(WWT_PANEL, COLOUR_GREY),
01929     NWidget(NWID_HORIZONTAL), SetPIP(20, 0, 20),
01930       NWidget(NWID_VERTICAL), SetPIP(11, 8, 11),
01931         NWidget(WWT_LABEL, INVALID_COLOUR), SetDataTip(STR_NEWGRF_SCAN_MESSAGE, STR_NULL), SetFill(1, 0),
01932         NWidget(WWT_EMPTY, INVALID_COLOUR, WID_SP_PROGRESS_BAR), SetFill(1, 0),
01933         NWidget(WWT_EMPTY, INVALID_COLOUR, WID_SP_PROGRESS_TEXT), SetFill(1, 0),
01934       EndContainer(),
01935     EndContainer(),
01936   EndContainer(),
01937 };
01938 
01940 static const WindowDesc _scan_progress_desc(
01941   WDP_CENTER, 0, 0,
01942   WC_MODAL_PROGRESS, WC_NONE,
01943   0,
01944   _nested_scan_progress_widgets, lengthof(_nested_scan_progress_widgets)
01945 );
01946 
01948 struct ScanProgressWindow : public Window {
01949   char *last_name; 
01950   int scanned;     
01951 
01953   ScanProgressWindow() : Window(), last_name(NULL), scanned(0)
01954   {
01955     this->InitNested(&_scan_progress_desc, 1);
01956   }
01957 
01959   ~ScanProgressWindow()
01960   {
01961     free(last_name);
01962   }
01963 
01964   virtual void UpdateWidgetSize(int widget, Dimension *size, const Dimension &padding, Dimension *fill, Dimension *resize)
01965   {
01966     switch (widget) {
01967       case WID_SP_PROGRESS_BAR: {
01968         SetDParamMaxValue(0, 100);
01969         *size = GetStringBoundingBox(STR_GENERATION_PROGRESS);
01970         /* We need some spacing for the 'border' */
01971         size->height += 8;
01972         size->width += 8;
01973         break;
01974       }
01975 
01976       case WID_SP_PROGRESS_TEXT:
01977         SetDParamMaxDigits(0, 4);
01978         SetDParamMaxDigits(1, 4);
01979         /* We really don't know the width. We could determine it by scanning the NewGRFs,
01980          * but this is the status window for scanning them... */
01981         size->width = max(400U, GetStringBoundingBox(STR_NEWGRF_SCAN_STATUS).width);
01982         size->height = FONT_HEIGHT_NORMAL * 2 + WD_PAR_VSEP_NORMAL;
01983         break;
01984     }
01985   }
01986 
01987   virtual void DrawWidget(const Rect &r, int widget) const
01988   {
01989     switch (widget) {
01990       case WID_SP_PROGRESS_BAR: {
01991         /* Draw the % complete with a bar and a text */
01992         DrawFrameRect(r.left, r.top, r.right, r.bottom, COLOUR_GREY, FR_BORDERONLY);
01993         uint percent = scanned * 100 / max(1U, _settings_client.gui.last_newgrf_count);
01994         DrawFrameRect(r.left + 1, r.top + 1, (int)((r.right - r.left - 2) * percent / 100) + r.left + 1, r.bottom - 1, COLOUR_MAUVE, FR_NONE);
01995         SetDParam(0, percent);
01996         DrawString(r.left, r.right, r.top + 5, STR_GENERATION_PROGRESS, TC_FROMSTRING, SA_HOR_CENTER);
01997         break;
01998       }
01999 
02000       case WID_SP_PROGRESS_TEXT:
02001         SetDParam(0, this->scanned);
02002         SetDParam(1, _settings_client.gui.last_newgrf_count);
02003         DrawString(r.left, r.right, r.top, STR_NEWGRF_SCAN_STATUS, TC_FROMSTRING, SA_HOR_CENTER);
02004 
02005         DrawString(r.left, r.right, r.top + FONT_HEIGHT_NORMAL + WD_PAR_VSEP_NORMAL, this->last_name == NULL ? "" : this->last_name, TC_BLACK, SA_HOR_CENTER);
02006         break;
02007     }
02008   }
02009 
02015   void UpdateNewGRFScanStatus(uint num, const char *name)
02016   {
02017     free(this->last_name);
02018     if (name == NULL) {
02019       char buf[256];
02020       GetString(buf, STR_NEWGRF_SCAN_ARCHIVES, lastof(buf));
02021       this->last_name = strdup(buf);
02022     } else {
02023       this->last_name = strdup(name);
02024     }
02025     this->scanned = num;
02026     if (num > _settings_client.gui.last_newgrf_count) _settings_client.gui.last_newgrf_count = num;
02027 
02028     this->SetDirty();
02029   }
02030 };
02031 
02037 void UpdateNewGRFScanStatus(uint num, const char *name)
02038 {
02039   ScanProgressWindow *w  = dynamic_cast<ScanProgressWindow *>(FindWindowByClass(WC_MODAL_PROGRESS));
02040   if (w == NULL) w = new ScanProgressWindow();
02041   w->UpdateNewGRFScanStatus(num, name);
02042 }