# town_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 "town.h"
    00014 #include "viewport_func.h"
    00015 #include "error.h"
    00016 #include "gui.h"
    00017 #include "command_func.h"
    00018 #include "company_func.h"
    00019 #include "company_base.h"
    00020 #include "company_gui.h"
    00021 #include "network/network.h"
    00022 #include "string_func.h"
    00023 #include "strings_func.h"
    00024 #include "sound_func.h"
    00025 #include "tilehighlight_func.h"
    00026 #include "sortlist_type.h"
    00027 #include "road_cmd.h"
    00028 #include "landscape.h"
    00029 #include "querystring_gui.h"
    00030 #include "window_func.h"
    00031 #include "townname_func.h"
    00032 #include "core/geometry_func.hpp"
    00033 #include "genworld.h"
    00034
    00035 #include "widgets/town_widget.h"
    00036
    00037 #include "table/strings.h"
    00038
    00039 typedef GUIList<const Town*> GUITownList;
    00040
    00041 static const NWidgetPart _nested_town_authority_widgets[] = {
    00042   NWidget(NWID_HORIZONTAL),
    00043     NWidget(WWT_CLOSEBOX, COLOUR_BROWN),
    00044     NWidget(WWT_CAPTION, COLOUR_BROWN, WID_TA_CAPTION), SetDataTip(STR_LOCAL_AUTHORITY_CAPTION, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS),
    00045     NWidget(WWT_SHADEBOX, COLOUR_BROWN),
    00046     NWidget(WWT_STICKYBOX, COLOUR_BROWN),
    00047   EndContainer(),
    00048   NWidget(WWT_PANEL, COLOUR_BROWN, WID_TA_RATING_INFO), SetMinimalSize(317, 92), SetResize(1, 1), EndContainer(),
    00049   NWidget(NWID_HORIZONTAL),
    00050     NWidget(WWT_PANEL, COLOUR_BROWN, WID_TA_COMMAND_LIST), SetMinimalSize(305, 52), SetResize(1, 0), SetDataTip(0x0, STR_LOCAL_AUTHORITY_ACTIONS_TOOLTIP), SetScrollbar(WID_TA_SCROLLBAR), EndContainer(),
    00051     NWidget(NWID_VSCROLLBAR, COLOUR_BROWN, WID_TA_SCROLLBAR),
    00052   EndContainer(),
    00053   NWidget(WWT_PANEL, COLOUR_BROWN, WID_TA_ACTION_INFO), SetMinimalSize(317, 52), SetResize(1, 0), EndContainer(),
    00054   NWidget(NWID_HORIZONTAL),
    00055     NWidget(WWT_PUSHTXTBTN, COLOUR_BROWN, WID_TA_EXECUTE),  SetMinimalSize(317, 12), SetResize(1, 0), SetFill(1, 0), SetDataTip(STR_LOCAL_AUTHORITY_DO_IT_BUTTON, STR_LOCAL_AUTHORITY_DO_IT_TOOLTIP),
    00056     NWidget(WWT_RESIZEBOX, COLOUR_BROWN),
    00057   EndContainer()
    00058 };
    00059
    00061 struct TownAuthorityWindow : Window {
    00062 private:
    00063   Town *town;
    00064   int sel_index;
    00065   Scrollbar *vscroll;
    00066   uint displayed_actions_on_previous_painting;
    00067
    00077   static int GetNthSetBit(uint32 bits, int n)
    00078   {
    00079     if (n >= 0) {
    00080       uint i;
    00081       FOR_EACH_SET_BIT(i, bits) {
    00082         n--;
    00083         if (n < 0) return i;
    00084       }
    00085     }
    00086     return -1;
    00087   }
    00088
    00089 public:
    00090   TownAuthorityWindow(const WindowDesc *desc, WindowNumber window_number) : Window(), sel_index(-1), displayed_actions_on_previous_painting(0)
    00091   {
    00092     this->town = Town::Get(window_number);
    00093     this->InitNested(desc, window_number);
    00094     this->vscroll = this->GetScrollbar(WID_TA_SCROLLBAR);
    00095     this->vscroll->SetCapacity((this->GetWidget<NWidgetBase>(WID_TA_COMMAND_LIST)->current_y - WD_FRAMERECT_TOP - WD_FRAMERECT_BOTTOM) / FONT_HEIGHT_NORMAL);
    00096   }
    00097
    00098   virtual void OnPaint()
    00099   {
    00100     int numact;
    00101     uint buttons = GetMaskOfTownActions(&numact, _local_company, this->town);
    00102     if (buttons != displayed_actions_on_previous_painting) this->SetDirty();
    00103     displayed_actions_on_previous_painting = buttons;
    00104
    00105     this->vscroll->SetCount(numact + 1);
    00106
    00107     if (this->sel_index != -1 && !HasBit(buttons, this->sel_index)) {
    00108       this->sel_index = -1;
    00109     }
    00110
    00111     this->SetWidgetDisabledState(WID_TA_EXECUTE, this->sel_index == -1);
    00112
    00113     this->DrawWidgets();
    00114     if (!this->IsShaded()) this->DrawRatings();
    00115   }
    00116
    00118   void DrawRatings()
    00119   {
    00120     NWidgetBase *nwid = this->GetWidget<NWidgetBase>(WID_TA_RATING_INFO);
    00121     uint left = nwid->pos_x + WD_FRAMERECT_LEFT;
    00122     uint right = nwid->pos_x + nwid->current_x - 1 - WD_FRAMERECT_RIGHT;
    00123
    00124     uint y = nwid->pos_y + WD_FRAMERECT_TOP;
    00125
    00126     DrawString(left, right, y, STR_LOCAL_AUTHORITY_COMPANY_RATINGS);
    00127     y += FONT_HEIGHT_NORMAL;
    00128
    00129     Dimension icon_size = GetSpriteSize(SPR_COMPANY_ICON);
    00130     int icon_width      = icon_size.width;
    00131     int icon_y_offset   = (FONT_HEIGHT_NORMAL - icon_size.height) / 2;
    00132
    00133     Dimension exclusive_size = GetSpriteSize(SPR_EXCLUSIVE_TRANSPORT);
    00134     int exclusive_width      = exclusive_size.width;
    00135     int exclusive_y_offset   = (FONT_HEIGHT_NORMAL - exclusive_size.height) / 2;
    00136
    00137     bool rtl = _current_text_dir == TD_RTL;
    00138     uint text_left      = left  + (rtl ? 0 : icon_width + exclusive_width + 4);
    00139     uint text_right     = right - (rtl ? icon_width + exclusive_width + 4 : 0);
    00140     uint icon_left      = rtl ? right - icon_width : left;
    00141     uint exclusive_left = rtl ? right - icon_width - exclusive_width - 2 : left + icon_width + 2;
    00142
    00143     /* Draw list of companies */
    00144     const Company *c;
    00145     FOR_ALL_COMPANIES(c) {
    00146       if ((HasBit(this->town->have_ratings, c->index) || this->town->exclusivity == c->index)) {
    00147         DrawCompanyIcon(c->index, icon_left, y + icon_y_offset);
    00148
    00149         SetDParam(0, c->index);
    00150         SetDParam(1, c->index);
    00151
    00152         int r = this->town->ratings[c->index];
    00153         StringID str;
    00154         (str = STR_CARGO_RATING_APPALLING, r <= RATING_APPALLING) || // Apalling
    00155         (str++,                    r <= RATING_VERYPOOR)  || // Very Poor
    00156         (str++,                    r <= RATING_POOR)      || // Poor
    00157         (str++,                    r <= RATING_MEDIOCRE)  || // Mediocore
    00158         (str++,                    r <= RATING_GOOD)      || // Good
    00159         (str++,                    r <= RATING_VERYGOOD)  || // Very Good
    00160         (str++,                    r <= RATING_EXCELLENT) || // Excellent
    00161         (str++,                    true);                    // Outstanding
    00162
    00163         SetDParam(2, str);
    00164         if (this->town->exclusivity == c->index) {
    00165           DrawSprite(SPR_EXCLUSIVE_TRANSPORT, COMPANY_SPRITE_COLOUR(c->index), exclusive_left, y + exclusive_y_offset);
    00166         }
    00167
    00168         DrawString(text_left, text_right, y, STR_LOCAL_AUTHORITY_COMPANY_RATING);
    00169         y += FONT_HEIGHT_NORMAL;
    00170       }
    00171     }
    00172
    00173     y = y + WD_FRAMERECT_BOTTOM - nwid->pos_y; // Compute needed size of the widget.
    00174     if (y > nwid->current_y) {
    00175       /* If the company list is too big to fit, mark ourself dirty and draw again. */
    00176       ResizeWindow(this, 0, y - nwid->current_y);
    00177     }
    00178   }
    00179
    00180   virtual void SetStringParameters(int widget) const
    00181   {
    00182     if (widget == WID_TA_CAPTION) SetDParam(0, this->window_number);
    00183   }
    00184
    00185   virtual void DrawWidget(const Rect &r, int widget) const
    00186   {
    00187     switch (widget) {
    00188       case WID_TA_ACTION_INFO:
    00189         if (this->sel_index != -1) {
    00190           SetDParam(0, _price[PR_TOWN_ACTION] * _town_action_costs[this->sel_index] >> 8);
    00191           DrawStringMultiLine(r.left + WD_FRAMERECT_LEFT, r.right - WD_FRAMERECT_RIGHT, r.top + WD_FRAMERECT_TOP, r.bottom - WD_FRAMERECT_BOTTOM,
    00192                 STR_LOCAL_AUTHORITY_ACTION_TOOLTIP_SMALL_ADVERTISING + this->sel_index);
    00193         }
    00194         break;
    00195       case WID_TA_COMMAND_LIST: {
    00196         int numact;
    00197         uint buttons = GetMaskOfTownActions(&numact, _local_company, this->town);
    00198         int y = r.top + WD_FRAMERECT_TOP;
    00199         int pos = this->vscroll->GetPosition();
    00200
    00201         if (--pos < 0) {
    00202           DrawString(r.left + WD_FRAMERECT_LEFT, r.right - WD_FRAMERECT_RIGHT, y, STR_LOCAL_AUTHORITY_ACTIONS_TITLE);
    00203           y += FONT_HEIGHT_NORMAL;
    00204         }
    00205
    00206         for (int i = 0; buttons; i++, buttons >>= 1) {
    00207           if (pos <= -5) break;
    00208
    00209           if ((buttons & 1) && --pos < 0) {
    00210             DrawString(r.left + WD_FRAMERECT_LEFT, r.right - WD_FRAMERECT_RIGHT, y,
    00211                 STR_LOCAL_AUTHORITY_ACTION_SMALL_ADVERTISING_CAMPAIGN + i, this->sel_index == i ? TC_WHITE : TC_ORANGE);
    00212             y += FONT_HEIGHT_NORMAL;
    00213           }
    00214         }
    00215         break;
    00216       }
    00217     }
    00218   }
    00219
    00220   virtual void UpdateWidgetSize(int widget, Dimension *size, const Dimension &padding, Dimension *fill, Dimension *resize)
    00221   {
    00222     switch (widget) {
    00223       case WID_TA_ACTION_INFO: {
    00224         assert(size->width > padding.width && size->height > padding.height);
    00225         size->width -= WD_FRAMERECT_LEFT + WD_FRAMERECT_RIGHT;
    00226         size->height -= WD_FRAMERECT_TOP + WD_FRAMERECT_BOTTOM;
    00227         Dimension d = {0, 0};
    00228         for (int i = 0; i < TACT_COUNT; i++) {
    00229           SetDParam(0, _price[PR_TOWN_ACTION] * _town_action_costs[i] >> 8);
    00230           d = maxdim(d, GetStringMultiLineBoundingBox(STR_LOCAL_AUTHORITY_ACTION_TOOLTIP_SMALL_ADVERTISING + i, *size));
    00231         }
    00232         *size = maxdim(*size, d);
    00233         size->width += WD_FRAMERECT_LEFT + WD_FRAMERECT_RIGHT;
    00234         size->height += WD_FRAMERECT_TOP + WD_FRAMERECT_BOTTOM;
    00235         break;
    00236       }
    00237
    00238       case WID_TA_COMMAND_LIST:
    00239         size->height = WD_FRAMERECT_TOP + 5 * FONT_HEIGHT_NORMAL + WD_FRAMERECT_BOTTOM;
    00240         size->width = GetStringBoundingBox(STR_LOCAL_AUTHORITY_ACTIONS_TITLE).width;
    00241         for (uint i = 0; i < TACT_COUNT; i++ ) {
    00242           size->width = max(size->width, GetStringBoundingBox(STR_LOCAL_AUTHORITY_ACTION_SMALL_ADVERTISING_CAMPAIGN + i).width);
    00243         }
    00244         size->width += WD_FRAMERECT_LEFT + WD_FRAMERECT_RIGHT;
    00245         break;
    00246
    00247       case WID_TA_RATING_INFO:
    00248         resize->height = FONT_HEIGHT_NORMAL;
    00249         size->height = WD_FRAMERECT_TOP + 9 * FONT_HEIGHT_NORMAL + WD_FRAMERECT_BOTTOM;
    00250         break;
    00251     }
    00252   }
    00253
    00254   virtual void OnClick(Point pt, int widget, int click_count)
    00255   {
    00256     switch (widget) {
    00257       case WID_TA_COMMAND_LIST: {
    00258         int y = this->GetRowFromWidget(pt.y, WID_TA_COMMAND_LIST, 1, FONT_HEIGHT_NORMAL);
    00259         if (!IsInsideMM(y, 0, 5)) return;
    00260
    00261         y = GetNthSetBit(GetMaskOfTownActions(NULL, _local_company, this->town), y + this->vscroll->GetPosition() - 1);
    00262         if (y >= 0) {
    00263           this->sel_index = y;
    00264           this->SetDirty();
    00265         }
    00266         /* FALL THROUGH, when double-clicking. */
    00267         if (click_count == 1 || y < 0) break;
    00268       }
    00269
    00270       case WID_TA_EXECUTE:
    00271         DoCommandP(this->town->xy, this->window_number, this->sel_index, CMD_DO_TOWN_ACTION | CMD_MSG(STR_ERROR_CAN_T_DO_THIS));
    00272         break;
    00273     }
    00274   }
    00275
    00276   virtual void OnHundredthTick()
    00277   {
    00278     this->SetDirty();
    00279   }
    00280 };
    00281
    00282 static const WindowDesc _town_authority_desc(
    00283   WDP_AUTO, 317, 222,
    00284   WC_TOWN_AUTHORITY, WC_NONE,
    00285   0,
    00286   _nested_town_authority_widgets, lengthof(_nested_town_authority_widgets)
    00287 );
    00288
    00289 static void ShowTownAuthorityWindow(uint town)
    00290 {
    00291   AllocateWindowDescFront<TownAuthorityWindow>(&_town_authority_desc, town);
    00292 }
    00293
    00294
    00295 /* Town view window. */
    00296 struct TownViewWindow : Window {
    00297 private:
    00298   Town *town;
    00299
    00300 public:
    00301   static const int WID_TV_HEIGHT_NORMAL = 150;
    00302
    00303   TownViewWindow(const WindowDesc *desc, WindowNumber window_number) : Window()
    00304   {
    00305     this->CreateNestedTree(desc);
    00306
    00307     this->town = Town::Get(window_number);
    00308     if (this->town->larger_town) this->GetWidget<NWidgetCore>(WID_TV_CAPTION)->widget_data = STR_TOWN_VIEW_CITY_CAPTION;
    00309
    00310     this->FinishInitNested(desc, window_number);
    00311
    00312     this->flags |= WF_DISABLE_VP_SCROLL;
    00313     NWidgetViewport *nvp = this->GetWidget<NWidgetViewport>(WID_TV_VIEWPORT);
    00314     nvp->InitializeViewport(this, this->town->xy, ZOOM_LVL_NEWS);
    00315
    00316     /* disable renaming town in network games if you are not the server */
    00317     this->SetWidgetDisabledState(WID_TV_CHANGE_NAME, _networking && !_network_server);
    00318   }
    00319
    00320   virtual void SetStringParameters(int widget) const
    00321   {
    00322     if (widget == WID_TV_CAPTION) SetDParam(0, this->town->index);
    00323   }
    00324
    00325   virtual void DrawWidget(const Rect &r, int widget) const
    00326   {
    00327     if (widget != WID_TV_INFO) return;
    00328
    00329     uint y = r.top + WD_FRAMERECT_TOP;
    00330
    00331     SetDParam(0, this->town->cache.population);
    00332     SetDParam(1, this->town->cache.num_houses);
    00333     DrawString(r.left + WD_FRAMERECT_LEFT, r.right - WD_FRAMERECT_LEFT, y, STR_TOWN_VIEW_POPULATION_HOUSES);
    00334
    00335     SetDParam(0, this->town->supplied[CT_PASSENGERS].old_act);
    00336     SetDParam(1, this->town->supplied[CT_PASSENGERS].old_max);
    00337     DrawString(r.left + WD_FRAMERECT_LEFT, r.right - WD_FRAMERECT_LEFT, y += FONT_HEIGHT_NORMAL, STR_TOWN_VIEW_PASSENGERS_LAST_MONTH_MAX);
    00338
    00339     SetDParam(0, this->town->supplied[CT_MAIL].old_act);
    00340     SetDParam(1, this->town->supplied[CT_MAIL].old_max);
    00341     DrawString(r.left + WD_FRAMERECT_LEFT, r.right - WD_FRAMERECT_LEFT, y += FONT_HEIGHT_NORMAL, STR_TOWN_VIEW_MAIL_LAST_MONTH_MAX);
    00342
    00343     bool first = true;
    00344     for (int i = TE_BEGIN; i < TE_END; i++) {
    00345       if (this->town->goal[i] == 0) continue;
    00346       if (this->town->goal[i] == TOWN_GROWTH_WINTER && (TileHeight(this->town->xy) < LowestSnowLine() || this->town->cache.population <= 90)) continue;
    00347       if (this->town->goal[i] == TOWN_GROWTH_DESERT && (GetTropicZone(this->town->xy) != TROPICZONE_DESERT || this->town->cache.population <= 60)) continue;
    00348
    00349       if (first) {
    00350         DrawString(r.left + WD_FRAMERECT_LEFT, r.right - WD_FRAMERECT_LEFT, y += FONT_HEIGHT_NORMAL, STR_TOWN_VIEW_CARGO_FOR_TOWNGROWTH);
    00351         first = false;
    00352       }
    00353
    00354       bool rtl = _current_text_dir == TD_RTL;
    00355       uint cargo_text_left = r.left + WD_FRAMERECT_LEFT + (rtl ? 0 : 20);
    00356       uint cargo_text_right = r.right - WD_FRAMERECT_RIGHT - (rtl ? 20 : 0);
    00357
    00358       const CargoSpec *cargo = FindFirstCargoWithTownEffect((TownEffect)i);
    00359       assert(cargo != NULL);
    00360
    00361       StringID string;
    00362
    00363       if (this->town->goal[i] == TOWN_GROWTH_DESERT || this->town->goal[i] == TOWN_GROWTH_WINTER) {
    00364         /* For 'original' gameplay, don't show the amount required (you need 1 or more ..) */
    00365         string = STR_TOWN_VIEW_CARGO_FOR_TOWNGROWTH_DELIVERED_GENERAL;
    00366         if (this->town->received[i].old_act == 0) {
    00367           string = STR_TOWN_VIEW_CARGO_FOR_TOWNGROWTH_REQUIRED_GENERAL;
    00368
    00369           if (this->town->goal[i] == TOWN_GROWTH_WINTER && TileHeight(this->town->xy) < GetSnowLine()) {
    00370             string = STR_TOWN_VIEW_CARGO_FOR_TOWNGROWTH_REQUIRED_WINTER;
    00371           }
    00372         }
    00373
    00374         SetDParam(0, cargo->name);
    00375       } else {
    00376         string = STR_TOWN_VIEW_CARGO_FOR_TOWNGROWTH_DELIVERED;
    00377         if (this->town->received[i].old_act < this->town->goal[i]) {
    00378           string = STR_TOWN_VIEW_CARGO_FOR_TOWNGROWTH_REQUIRED;
    00379         }
    00380
    00381         SetDParam(0, cargo->Index());
    00382         SetDParam(1, this->town->received[i].old_act);
    00383         SetDParam(2, cargo->Index());
    00384         SetDParam(3, this->town->goal[i]);
    00385       }
    00386       DrawString(cargo_text_left, cargo_text_right, y += FONT_HEIGHT_NORMAL, string);
    00387     }
    00388
    00389     if (HasBit(this->town->flags, TOWN_IS_FUNDED)) {
    00390       SetDParam(0, ((this->town->growth_rate & (~TOWN_GROW_RATE_CUSTOM)) * TOWN_GROWTH_TICKS + DAY_TICKS) / DAY_TICKS);
    00391       DrawString(r.left + WD_FRAMERECT_LEFT, r.right - WD_FRAMERECT_LEFT, y += FONT_HEIGHT_NORMAL, this->town->fund_buildings_months == 0 ? STR_TOWN_VIEW_TOWN_GROWS_EVERY : STR_TOWN_VIEW_TOWN_GROWS_EVERY_FUNDED);
    00392     } else {
    00393       DrawString(r.left + WD_FRAMERECT_LEFT, r.right - WD_FRAMERECT_LEFT, y += FONT_HEIGHT_NORMAL, STR_TOWN_VIEW_TOWN_GROW_STOPPED);
    00394     }
    00395
    00396     /* only show the town noise, if the noise option is activated. */
    00397     if (_settings_game.economy.station_noise_level) {
    00398       SetDParam(0, this->town->noise_reached);
    00399       SetDParam(1, this->town->MaxTownNoise());
    00400       DrawString(r.left + WD_FRAMERECT_LEFT, r.right - WD_FRAMERECT_LEFT, y += FONT_HEIGHT_NORMAL, STR_TOWN_VIEW_NOISE_IN_TOWN);
    00401     }
    00402
    00403     if (this->town->text != NULL) {
    00404       SetDParamStr(0, this->town->text);
    00405       DrawStringMultiLine(r.left + WD_FRAMERECT_LEFT, r.right - WD_FRAMERECT_LEFT, y += FONT_HEIGHT_NORMAL, UINT16_MAX, STR_JUST_RAW_STRING, TC_BLACK);
    00406     }
    00407   }
    00408
    00409   virtual void OnClick(Point pt, int widget, int click_count)
    00410   {
    00411     switch (widget) {
    00412       case WID_TV_CENTER_VIEW: // scroll to location
    00413         if (_ctrl_pressed) {
    00414           ShowExtraViewPortWindow(this->town->xy);
    00415         } else {
    00416           ScrollMainWindowToTile(this->town->xy);
    00417         }
    00418         break;
    00419
    00420       case WID_TV_SHOW_AUTHORITY: // town authority
    00421         ShowTownAuthorityWindow(this->window_number);
    00422         break;
    00423
    00424       case WID_TV_CHANGE_NAME: // rename
    00425         SetDParam(0, this->window_number);
    00426         ShowQueryString(STR_TOWN_NAME, STR_TOWN_VIEW_RENAME_TOWN_BUTTON, MAX_LENGTH_TOWN_NAME_CHARS, this, CS_ALPHANUMERAL, QSF_ENABLE_DEFAULT | QSF_LEN_IN_CHARS);
    00427         break;
    00428
    00429       case WID_TV_EXPAND: { // expand town - only available on Scenario editor
    00430         /* Warn the user if towns are not allowed to build roads, but do this only once per OpenTTD run. */
    00431         static bool _warn_town_no_roads = false;
    00432
    00433         if (!_settings_game.economy.allow_town_roads && !_warn_town_no_roads) {
    00434           ShowErrorMessage(STR_ERROR_TOWN_EXPAND_WARN_NO_ROADS, INVALID_STRING_ID, WL_WARNING);
    00435           _warn_town_no_roads = true;
    00436         }
    00437
    00438         DoCommandP(0, this->window_number, 0, CMD_EXPAND_TOWN | CMD_MSG(STR_ERROR_CAN_T_EXPAND_TOWN));
    00439         break;
    00440       }
    00441
    00442       case WID_TV_DELETE: // delete town - only available on Scenario editor
    00443         DoCommandP(0, this->window_number, 0, CMD_DELETE_TOWN | CMD_MSG(STR_ERROR_TOWN_CAN_T_DELETE));
    00444         break;
    00445     }
    00446   }
    00447
    00448   virtual void UpdateWidgetSize(int widget, Dimension *size, const Dimension &padding, Dimension *fill, Dimension *resize)
    00449   {
    00450     switch (widget) {
    00451       case WID_TV_INFO:
    00452         size->height = GetDesiredInfoHeight(size->width);
    00453         break;
    00454     }
    00455   }
    00456
    00461   uint GetDesiredInfoHeight(int width) const
    00462   {
    00463     uint aimed_height = 3 * FONT_HEIGHT_NORMAL + WD_FRAMERECT_TOP + WD_FRAMERECT_BOTTOM;
    00464
    00465     bool first = true;
    00466     for (int i = TE_BEGIN; i < TE_END; i++) {
    00467       if (this->town->goal[i] == 0) continue;
    00468       if (this->town->goal[i] == TOWN_GROWTH_WINTER && (TileHeight(this->town->xy) < LowestSnowLine() || this->town->cache.population <= 90)) continue;
    00469       if (this->town->goal[i] == TOWN_GROWTH_DESERT && (GetTropicZone(this->town->xy) != TROPICZONE_DESERT || this->town->cache.population <= 60)) continue;
    00470
    00471       if (first) {
    00472         aimed_height += FONT_HEIGHT_NORMAL;
    00473         first = false;
    00474       }
    00475       aimed_height += FONT_HEIGHT_NORMAL;
    00476     }
    00477     aimed_height += FONT_HEIGHT_NORMAL;
    00478
    00479     if (_settings_game.economy.station_noise_level) aimed_height += FONT_HEIGHT_NORMAL;
    00480
    00481     if (this->town->text != NULL) {
    00482       SetDParamStr(0, this->town->text);
    00483       aimed_height += GetStringHeight(STR_JUST_RAW_STRING, width);
    00484     }
    00485
    00486     return aimed_height;
    00487   }
    00488
    00489   void ResizeWindowAsNeeded()
    00490   {
    00491     const NWidgetBase *nwid_info = this->GetWidget<NWidgetBase>(WID_TV_INFO);
    00492     uint aimed_height = GetDesiredInfoHeight(nwid_info->current_x);
    00493     if (aimed_height > nwid_info->current_y || (aimed_height < nwid_info->current_y && nwid_info->current_y > nwid_info->smallest_y)) {
    00494       this->ReInit();
    00495     }
    00496   }
    00497
    00498   virtual void OnResize()
    00499   {
    00500     if (this->viewport != NULL) {
    00501       NWidgetViewport *nvp = this->GetWidget<NWidgetViewport>(WID_TV_VIEWPORT);
    00502       nvp->UpdateViewportCoordinates(this);
    00503
    00504       ScrollWindowToTile(this->town->xy, this, true); // Re-center viewport.
    00505     }
    00506   }
    00507
    00513   virtual void OnInvalidateData(int data = 0, bool gui_scope = true)
    00514   {
    00515     if (!gui_scope) return;
    00516     /* Called when setting station noise or required cargoes have changed, in order to resize the window */
    00517     this->SetDirty(); // refresh display for current size. This will allow to avoid glitches when downgrading
    00518     this->ResizeWindowAsNeeded();
    00519   }
    00520
    00521   virtual void OnQueryTextFinished(char *str)
    00522   {
    00523     if (str == NULL) return;
    00524
    00525     DoCommandP(0, this->window_number, 0, CMD_RENAME_TOWN | CMD_MSG(STR_ERROR_CAN_T_RENAME_TOWN), NULL, str);
    00526   }
    00527 };
    00528
    00529 static const NWidgetPart _nested_town_game_view_widgets[] = {
    00530   NWidget(NWID_HORIZONTAL),
    00531     NWidget(WWT_CLOSEBOX, COLOUR_BROWN),
    00532     NWidget(WWT_CAPTION, COLOUR_BROWN, WID_TV_CAPTION), SetDataTip(STR_TOWN_VIEW_TOWN_CAPTION, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS),
    00533     NWidget(WWT_SHADEBOX, COLOUR_BROWN),
    00534     NWidget(WWT_STICKYBOX, COLOUR_BROWN),
    00535   EndContainer(),
    00536   NWidget(WWT_PANEL, COLOUR_BROWN),
    00537     NWidget(WWT_INSET, COLOUR_BROWN), SetPadding(2, 2, 2, 2),
    00538       NWidget(NWID_VIEWPORT, INVALID_COLOUR, WID_TV_VIEWPORT), SetMinimalSize(254, 86), SetFill(1, 0), SetResize(1, 1), SetPadding(1, 1, 1, 1),
    00539     EndContainer(),
    00540   EndContainer(),
    00541   NWidget(WWT_PANEL, COLOUR_BROWN, WID_TV_INFO), SetMinimalSize(260, 32), SetResize(1, 0), SetFill(1, 0), EndContainer(),
    00542   NWidget(NWID_HORIZONTAL),
    00543     NWidget(NWID_HORIZONTAL, NC_EQUALSIZE),
    00544       NWidget(WWT_PUSHTXTBTN, COLOUR_BROWN, WID_TV_CENTER_VIEW), SetMinimalSize(80, 12), SetFill(1, 1), SetResize(1, 0), SetDataTip(STR_BUTTON_LOCATION, STR_TOWN_VIEW_CENTER_TOOLTIP),
    00545       NWidget(WWT_PUSHTXTBTN, COLOUR_BROWN, WID_TV_SHOW_AUTHORITY), SetMinimalSize(80, 12), SetFill(1, 1), SetResize(1, 0), SetDataTip(STR_TOWN_VIEW_LOCAL_AUTHORITY_BUTTON, STR_TOWN_VIEW_LOCAL_AUTHORITY_TOOLTIP),
    00546       NWidget(WWT_PUSHTXTBTN, COLOUR_BROWN, WID_TV_CHANGE_NAME), SetMinimalSize(80, 12), SetFill(1, 1), SetResize(1, 0), SetDataTip(STR_BUTTON_RENAME, STR_TOWN_VIEW_RENAME_TOOLTIP),
    00547     EndContainer(),
    00548     NWidget(WWT_RESIZEBOX, COLOUR_BROWN),
    00549   EndContainer(),
    00550 };
    00551
    00552 static const WindowDesc _town_game_view_desc(
    00553   WDP_AUTO, 260, TownViewWindow::WID_TV_HEIGHT_NORMAL,
    00554   WC_TOWN_VIEW, WC_NONE,
    00555   0,
    00556   _nested_town_game_view_widgets, lengthof(_nested_town_game_view_widgets)
    00557 );
    00558
    00559 static const NWidgetPart _nested_town_editor_view_widgets[] = {
    00560   NWidget(NWID_HORIZONTAL),
    00561     NWidget(WWT_CLOSEBOX, COLOUR_BROWN),
    00562     NWidget(WWT_CAPTION, COLOUR_BROWN, WID_TV_CAPTION), SetDataTip(STR_TOWN_VIEW_TOWN_CAPTION, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS),
    00563     NWidget(WWT_PUSHTXTBTN, COLOUR_BROWN, WID_TV_CHANGE_NAME), SetMinimalSize(76, 14), SetDataTip(STR_BUTTON_RENAME, STR_TOWN_VIEW_RENAME_TOOLTIP),
    00564     NWidget(WWT_SHADEBOX, COLOUR_BROWN),
    00565     NWidget(WWT_STICKYBOX, COLOUR_BROWN),
    00566   EndContainer(),
    00567   NWidget(WWT_PANEL, COLOUR_BROWN),
    00568     NWidget(WWT_INSET, COLOUR_BROWN), SetPadding(2, 2, 2, 2),
    00569       NWidget(NWID_VIEWPORT, INVALID_COLOUR, WID_TV_VIEWPORT), SetMinimalSize(254, 86), SetFill(1, 1), SetResize(1, 1), SetPadding(1, 1, 1, 1),
    00570     EndContainer(),
    00571   EndContainer(),
    00572   NWidget(WWT_PANEL, COLOUR_BROWN, WID_TV_INFO), SetMinimalSize(260, 32), SetResize(1, 0), SetFill(1, 0), EndContainer(),
    00573   NWidget(NWID_HORIZONTAL),
    00574     NWidget(NWID_HORIZONTAL, NC_EQUALSIZE),
    00575       NWidget(WWT_PUSHTXTBTN, COLOUR_BROWN, WID_TV_CENTER_VIEW), SetMinimalSize(80, 12), SetFill(1, 1), SetResize(1, 0), SetDataTip(STR_BUTTON_LOCATION, STR_TOWN_VIEW_CENTER_TOOLTIP),
    00576       NWidget(WWT_PUSHTXTBTN, COLOUR_BROWN, WID_TV_EXPAND), SetMinimalSize(80, 12), SetFill(1, 1), SetResize(1, 0), SetDataTip(STR_TOWN_VIEW_EXPAND_BUTTON, STR_TOWN_VIEW_EXPAND_TOOLTIP),
    00577       NWidget(WWT_PUSHTXTBTN, COLOUR_BROWN, WID_TV_DELETE), SetMinimalSize(80, 12), SetFill(1, 1), SetResize(1, 0), SetDataTip(STR_TOWN_VIEW_DELETE_BUTTON, STR_TOWN_VIEW_DELETE_TOOLTIP),
    00578     EndContainer(),
    00579     NWidget(WWT_RESIZEBOX, COLOUR_BROWN),
    00580   EndContainer(),
    00581 };
    00582
    00583 static const WindowDesc _town_editor_view_desc(
    00584   WDP_AUTO, 260, TownViewWindow::WID_TV_HEIGHT_NORMAL,
    00585   WC_TOWN_VIEW, WC_NONE,
    00586   0,
    00587   _nested_town_editor_view_widgets, lengthof(_nested_town_editor_view_widgets)
    00588 );
    00589
    00590 void ShowTownViewWindow(TownID town)
    00591 {
    00592   if (_game_mode == GM_EDITOR) {
    00593     AllocateWindowDescFront<TownViewWindow>(&_town_editor_view_desc, town);
    00594   } else {
    00595     AllocateWindowDescFront<TownViewWindow>(&_town_game_view_desc, town);
    00596   }
    00597 }
    00598
    00599 static const NWidgetPart _nested_town_directory_widgets[] = {
    00600   NWidget(NWID_HORIZONTAL),
    00601     NWidget(WWT_CLOSEBOX, COLOUR_BROWN),
    00602     NWidget(WWT_CAPTION, COLOUR_BROWN), SetDataTip(STR_TOWN_DIRECTORY_CAPTION, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS),
    00603     NWidget(WWT_SHADEBOX, COLOUR_BROWN),
    00604     NWidget(WWT_STICKYBOX, COLOUR_BROWN),
    00605   EndContainer(),
    00606   NWidget(NWID_HORIZONTAL),
    00607     NWidget(NWID_VERTICAL),
    00608       NWidget(NWID_HORIZONTAL),
    00609         NWidget(WWT_PUSHTXTBTN, COLOUR_BROWN, WID_TD_SORT_NAME), SetMinimalSize(99, 12), SetDataTip(STR_SORT_BY_CAPTION_NAME, STR_TOOLTIP_SORT_ORDER), SetFill(1, 0),
    00610         NWidget(WWT_PUSHTXTBTN, COLOUR_BROWN, WID_TD_SORT_POPULATION), SetMinimalSize(97, 12), SetDataTip(STR_SORT_BY_CAPTION_POPULATION, STR_TOOLTIP_SORT_ORDER), SetFill(1, 0),
    00611       EndContainer(),
    00612       NWidget(WWT_PANEL, COLOUR_BROWN, WID_TD_LIST), SetMinimalSize(196, 0), SetDataTip(0x0, STR_TOWN_DIRECTORY_LIST_TOOLTIP),
    00613               SetFill(1, 0), SetResize(0, 10), SetScrollbar(WID_TD_SCROLLBAR), EndContainer(),
    00614       NWidget(WWT_PANEL, COLOUR_BROWN),
    00615         NWidget(WWT_TEXT, COLOUR_BROWN, WID_TD_WORLD_POPULATION), SetPadding(2, 0, 0, 2), SetMinimalSize(196, 12), SetFill(1, 0), SetDataTip(STR_TOWN_POPULATION, STR_NULL),
    00616       EndContainer(),
    00617     EndContainer(),
    00618     NWidget(NWID_VERTICAL),
    00619       NWidget(NWID_VSCROLLBAR, COLOUR_BROWN, WID_TD_SCROLLBAR),
    00620       NWidget(WWT_RESIZEBOX, COLOUR_BROWN),
    00621     EndContainer(),
    00622   EndContainer(),
    00623 };
    00624
    00626 struct TownDirectoryWindow : public Window {
    00627 private:
    00628   /* Runtime saved values */
    00629   static Listing last_sorting;
    00630   static const Town *last_town;
    00631
    00632   /* Constants for sorting towns */
    00633   static GUITownList::SortFunction * const sorter_funcs[];
    00634
    00635   GUITownList towns;
    00636
    00637   Scrollbar *vscroll;
    00638
    00639   void BuildSortTownList()
    00640   {
    00641     if (this->towns.NeedRebuild()) {
    00642       this->towns.Clear();
    00643
    00644       const Town *t;
    00645       FOR_ALL_TOWNS(t) {
    00646         *this->towns.Append() = t;
    00647       }
    00648
    00649       this->towns.Compact();
    00650       this->towns.RebuildDone();
    00651       this->vscroll->SetCount(this->towns.Length()); // Update scrollbar as well.
    00652     }
    00653     /* Always sort the towns. */
    00654     this->last_town = NULL;
    00655     this->towns.Sort();
    00656   }
    00657
    00659   static int CDECL TownNameSorter(const Town * const *a, const Town * const *b)
    00660   {
    00661     static char buf_cache[64];
    00662     const Town *ta = *a;
    00663     const Town *tb = *b;
    00664     char buf[64];
    00665
    00666     SetDParam(0, ta->index);
    00667     GetString(buf, STR_TOWN_NAME, lastof(buf));
    00668
    00669     /* If 'b' is the same town as in the last round, use the cached value
    00670      * We do this to speed stuff up ('b' is called with the same value a lot of
    00671      * times after eachother) */
    00672     if (tb != last_town) {
    00673       last_town = tb;
    00674       SetDParam(0, tb->index);
    00675       GetString(buf_cache, STR_TOWN_NAME, lastof(buf_cache));
    00676     }
    00677
    00678     return strnatcmp(buf, buf_cache); // Sort by name (natural sorting).
    00679   }
    00680
    00682   static int CDECL TownPopulationSorter(const Town * const *a, const Town * const *b)
    00683   {
    00684     return (*a)->cache.population - (*b)->cache.population;
    00685   }
    00686
    00687 public:
    00688   TownDirectoryWindow(const WindowDesc *desc) : Window()
    00689   {
    00690     this->CreateNestedTree(desc);
    00691
    00692     this->vscroll = this->GetScrollbar(WID_TD_SCROLLBAR);
    00693
    00694     this->towns.SetListing(this->last_sorting);
    00695     this->towns.SetSortFuncs(TownDirectoryWindow::sorter_funcs);
    00696     this->towns.ForceRebuild();
    00697     this->BuildSortTownList();
    00698
    00699     this->FinishInitNested(desc, 0);
    00700   }
    00701
    00702   ~TownDirectoryWindow()
    00703   {
    00704     this->last_sorting = this->towns.GetListing();
    00705   }
    00706
    00707   virtual void SetStringParameters(int widget) const
    00708   {
    00709     if (widget == WID_TD_WORLD_POPULATION) SetDParam(0, GetWorldPopulation());
    00710   }
    00711
    00712   virtual void DrawWidget(const Rect &r, int widget) const
    00713   {
    00714     switch (widget) {
    00715       case WID_TD_SORT_NAME:
    00716         if (this->towns.SortType() == 0) this->DrawSortButtonState(widget, this->towns.IsDescSortOrder() ? SBS_DOWN : SBS_UP);
    00717         break;
    00718
    00719       case WID_TD_SORT_POPULATION:
    00720         if (this->towns.SortType() != 0) this->DrawSortButtonState(widget, this->towns.IsDescSortOrder() ? SBS_DOWN : SBS_UP);
    00721         break;
    00722
    00723       case WID_TD_LIST: {
    00724         int n = 0;
    00725         int y = r.top + WD_FRAMERECT_TOP;
    00726         if (this->towns.Length() == 0) { // No towns available.
    00727           DrawString(r.left + WD_FRAMERECT_LEFT, r.right, y, STR_TOWN_DIRECTORY_NONE);
    00728           break;
    00729         }
    00730
    00731         /* At least one town available. */
    00732         bool rtl = _current_text_dir == TD_RTL;
    00733         Dimension icon_size = GetSpriteSize(SPR_TOWN_RATING_GOOD);
    00734         int text_left  = r.left + WD_FRAMERECT_LEFT + (rtl ? 0 : icon_size.width + 2);
    00735         int text_right = r.right - WD_FRAMERECT_RIGHT - (rtl ? icon_size.width + 2 : 0);
    00736         int icon_x = rtl ? r.right - WD_FRAMERECT_RIGHT - icon_size.width : r.left + WD_FRAMERECT_LEFT;
    00737
    00738         for (uint i = this->vscroll->GetPosition(); i < this->towns.Length(); i++) {
    00739           const Town *t = this->towns[i];
    00740           assert(t->xy != INVALID_TILE);
    00741
    00742           /* Draw rating icon. */
    00743           if (_game_mode == GM_EDITOR || !HasBit(t->have_ratings, _local_company)) {
    00744             DrawSprite(SPR_TOWN_RATING_NA, PAL_NONE, icon_x, y + (this->resize.step_height - icon_size.height) / 2);
    00745           } else {
    00746             SpriteID icon = SPR_TOWN_RATING_APALLING;
    00747             if (t->ratings[_local_company] > RATING_VERYPOOR) icon = SPR_TOWN_RATING_MEDIOCRE;
    00748             if (t->ratings[_local_company] > RATING_GOOD)     icon = SPR_TOWN_RATING_GOOD;
    00749             DrawSprite(icon, PAL_NONE, icon_x, y + (this->resize.step_height - icon_size.height) / 2);
    00750           }
    00751
    00752           SetDParam(0, t->index);
    00753           SetDParam(1, t->cache.population);
    00754           DrawString(text_left, text_right, y + (this->resize.step_height - FONT_HEIGHT_NORMAL) / 2, STR_TOWN_DIRECTORY_TOWN);
    00755
    00756           y += this->resize.step_height;
    00757           if (++n == this->vscroll->GetCapacity()) break; // max number of towns in 1 window
    00758         }
    00759         break;
    00760       }
    00761     }
    00762   }
    00763
    00764   virtual void UpdateWidgetSize(int widget, Dimension *size, const Dimension &padding, Dimension *fill, Dimension *resize)
    00765   {
    00766     switch (widget) {
    00767       case WID_TD_SORT_NAME:
    00768       case WID_TD_SORT_POPULATION: {
    00769         Dimension d = GetStringBoundingBox(this->GetWidget<NWidgetCore>(widget)->widget_data);
    00770         d.width += padding.width + WD_SORTBUTTON_ARROW_WIDTH * 2; // Doubled since the string is centred and it also looks better.
    00771         d.height += padding.height;
    00772         *size = maxdim(*size, d);
    00773         break;
    00774       }
    00775       case WID_TD_LIST: {
    00776         Dimension d = GetStringBoundingBox(STR_TOWN_DIRECTORY_NONE);
    00777         for (uint i = 0; i < this->towns.Length(); i++) {
    00778           const Town *t = this->towns[i];
    00779
    00780           assert(t != NULL);
    00781
    00782           SetDParam(0, t->index);
    00783           SetDParam(1, 10000000); // 10^7
    00784           d = maxdim(d, GetStringBoundingBox(STR_TOWN_DIRECTORY_TOWN));
    00785         }
    00786         Dimension icon_size = GetSpriteSize(SPR_TOWN_RATING_GOOD);
    00787         d.width += icon_size.width + 2;
    00788         d.height = max(d.height, icon_size.height);
    00789         resize->height = d.height;
    00790         d.height *= 5;
    00791         d.width += padding.width + WD_FRAMERECT_LEFT + WD_FRAMERECT_RIGHT;
    00792         d.height += padding.height + WD_FRAMERECT_TOP + WD_FRAMERECT_BOTTOM;
    00793         *size = maxdim(*size, d);
    00794         break;
    00795       }
    00796       case WID_TD_WORLD_POPULATION: {
    00797         SetDParam(0, 1000000000); // 10^9
    00798         Dimension d = GetStringBoundingBox(STR_TOWN_POPULATION);
    00799         d.width += padding.width;
    00800         d.height += padding.height;
    00801         *size = maxdim(*size, d);
    00802         break;
    00803       }
    00804     }
    00805   }
    00806
    00807   virtual void OnClick(Point pt, int widget, int click_count)
    00808   {
    00809     switch (widget) {
    00810       case WID_TD_SORT_NAME: // Sort by Name ascending/descending
    00811         if (this->towns.SortType() == 0) {
    00812           this->towns.ToggleSortOrder();
    00813         } else {
    00814           this->towns.SetSortType(0);
    00815         }
    00816         this->BuildSortTownList();
    00817         this->SetDirty();
    00818         break;
    00819
    00820       case WID_TD_SORT_POPULATION: // Sort by Population ascending/descending
    00821         if (this->towns.SortType() == 1) {
    00822           this->towns.ToggleSortOrder();
    00823         } else {
    00824           this->towns.SetSortType(1);
    00825         }
    00826         this->BuildSortTownList();
    00827         this->SetDirty();
    00828         break;
    00829
    00830       case WID_TD_LIST: { // Click on Town Matrix
    00831         uint id_v = this->vscroll->GetScrolledRowFromWidget(pt.y, this, WID_TD_LIST, WD_FRAMERECT_TOP);
    00832         if (id_v >= this->towns.Length()) return; // click out of town bounds
    00833
    00834         const Town *t = this->towns[id_v];
    00835         assert(t != NULL);
    00836         if (_ctrl_pressed) {
    00837           ShowExtraViewPortWindow(t->xy);
    00838         } else {
    00839           ScrollMainWindowToTile(t->xy);
    00840         }
    00841         break;
    00842       }
    00843     }
    00844   }
    00845
    00846   virtual void OnPaint()
    00847   {
    00848     if (this->towns.NeedRebuild()) this->BuildSortTownList();
    00849     this->DrawWidgets();
    00850   }
    00851
    00852   virtual void OnHundredthTick()
    00853   {
    00854     this->BuildSortTownList();
    00855     this->SetDirty();
    00856   }
    00857
    00858   virtual void OnResize()
    00859   {
    00860     this->vscroll->SetCapacityFromWidget(this, WID_TD_LIST);
    00861   }
    00862
    00868   virtual void OnInvalidateData(int data = 0, bool gui_scope = true)
    00869   {
    00870     if (data == 0) {
    00871       /* This needs to be done in command-scope to enforce rebuilding before resorting invalid data */
    00872       this->towns.ForceRebuild();
    00873     } else {
    00874       this->towns.ForceResort();
    00875     }
    00876   }
    00877 };
    00878
    00879 Listing TownDirectoryWindow::last_sorting = {false, 0};
    00880 const Town *TownDirectoryWindow::last_town = NULL;
    00881
    00882 /* Available town directory sorting functions */
    00883 GUITownList::SortFunction * const TownDirectoryWindow::sorter_funcs[] = {
    00884   &TownNameSorter,
    00885   &TownPopulationSorter,
    00886 };
    00887
    00888 static const WindowDesc _town_directory_desc(
    00889   WDP_AUTO, 208, 202,
    00890   WC_TOWN_DIRECTORY, WC_NONE,
    00891   0,
    00892   _nested_town_directory_widgets, lengthof(_nested_town_directory_widgets)
    00893 );
    00894
    00895 void ShowTownDirectory()
    00896 {
    00897   if (BringWindowToFrontById(WC_TOWN_DIRECTORY, 0)) return;
    00898   new TownDirectoryWindow(&_town_directory_desc);
    00899 }
    00900
    00901 void CcFoundTown(const CommandCost &result, TileIndex tile, uint32 p1, uint32 p2)
    00902 {
    00903   if (result.Failed()) return;
    00904
    00905   SndPlayTileFx(SND_1F_SPLAT, tile);
    00906   if (!_settings_client.gui.persistent_buildingtools) ResetObjectToPlace();
    00907 }
    00908
    00909 void CcFoundRandomTown(const CommandCost &result, TileIndex tile, uint32 p1, uint32 p2)
    00910 {
    00911   if (result.Succeeded()) ScrollMainWindowToTile(Town::Get(_new_town_id)->xy);
    00912 }
    00913
    00914 static const NWidgetPart _nested_found_town_widgets[] = {
    00915   NWidget(NWID_HORIZONTAL),
    00916     NWidget(WWT_CLOSEBOX, COLOUR_DARK_GREEN),
    00917     NWidget(WWT_CAPTION, COLOUR_DARK_GREEN), SetDataTip(STR_FOUND_TOWN_CAPTION, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS),
    00918     NWidget(WWT_SHADEBOX, COLOUR_DARK_GREEN),
    00919     NWidget(WWT_STICKYBOX, COLOUR_DARK_GREEN),
    00920   EndContainer(),
    00921   /* Construct new town(s) buttons. */
    00922   NWidget(WWT_PANEL, COLOUR_DARK_GREEN),
    00923     NWidget(NWID_SPACER), SetMinimalSize(0, 2),
    00924     NWidget(WWT_TEXTBTN, COLOUR_GREY, WID_TF_NEW_TOWN), SetMinimalSize(156, 12), SetFill(1, 0),
    00925                     SetDataTip(STR_FOUND_TOWN_NEW_TOWN_BUTTON, STR_FOUND_TOWN_NEW_TOWN_TOOLTIP), SetPadding(0, 2, 1, 2),
    00926     NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_TF_RANDOM_TOWN), SetMinimalSize(156, 12), SetFill(1, 0),
    00927                     SetDataTip(STR_FOUND_TOWN_RANDOM_TOWN_BUTTON, STR_FOUND_TOWN_RANDOM_TOWN_TOOLTIP), SetPadding(0, 2, 1, 2),
    00928     NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_TF_MANY_RANDOM_TOWNS), SetMinimalSize(156, 12), SetFill(1, 0),
    00929                     SetDataTip(STR_FOUND_TOWN_MANY_RANDOM_TOWNS, STR_FOUND_TOWN_RANDOM_TOWNS_TOOLTIP), SetPadding(0, 2, 0, 2),
    00930     /* Town name selection. */
    00931     NWidget(WWT_LABEL, COLOUR_DARK_GREEN), SetMinimalSize(156, 14), SetPadding(0, 2, 0, 2), SetDataTip(STR_FOUND_TOWN_NAME_TITLE, STR_NULL),
    00932     NWidget(WWT_EDITBOX, COLOUR_GREY, WID_TF_TOWN_NAME_EDITBOX), SetMinimalSize(156, 12), SetPadding(0, 2, 3, 2),
    00933                     SetDataTip(STR_FOUND_TOWN_NAME_EDITOR_TITLE, STR_FOUND_TOWN_NAME_EDITOR_HELP),
    00934     NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_TF_TOWN_NAME_RANDOM), SetMinimalSize(78, 12), SetPadding(0, 2, 0, 2), SetFill(1, 0),
    00935                     SetDataTip(STR_FOUND_TOWN_NAME_RANDOM_BUTTON, STR_FOUND_TOWN_NAME_RANDOM_TOOLTIP),
    00936     /* Town size selection. */
    00937     NWidget(NWID_HORIZONTAL), SetPIP(2, 0, 2),
    00938       NWidget(NWID_SPACER), SetFill(1, 0),
    00939       NWidget(WWT_LABEL, COLOUR_DARK_GREEN), SetMinimalSize(148, 14), SetDataTip(STR_FOUND_TOWN_INITIAL_SIZE_TITLE, STR_NULL),
    00940       NWidget(NWID_SPACER), SetFill(1, 0),
    00941     EndContainer(),
    00942     NWidget(NWID_HORIZONTAL, NC_EQUALSIZE), SetPIP(2, 0, 2),
    00943       NWidget(WWT_TEXTBTN, COLOUR_GREY, WID_TF_SIZE_SMALL), SetMinimalSize(78, 12), SetFill(1, 0),
    00944                     SetDataTip(STR_FOUND_TOWN_INITIAL_SIZE_SMALL_BUTTON, STR_FOUND_TOWN_INITIAL_SIZE_TOOLTIP),
    00945       NWidget(WWT_TEXTBTN, COLOUR_GREY, WID_TF_SIZE_MEDIUM), SetMinimalSize(78, 12), SetFill(1, 0),
    00946                     SetDataTip(STR_FOUND_TOWN_INITIAL_SIZE_MEDIUM_BUTTON, STR_FOUND_TOWN_INITIAL_SIZE_TOOLTIP),
    00947     EndContainer(),
    00948     NWidget(NWID_SPACER), SetMinimalSize(0, 1),
    00949     NWidget(NWID_HORIZONTAL, NC_EQUALSIZE), SetPIP(2, 0, 2),
    00950       NWidget(WWT_TEXTBTN, COLOUR_GREY, WID_TF_SIZE_LARGE), SetMinimalSize(78, 12), SetFill(1, 0),
    00951                     SetDataTip(STR_FOUND_TOWN_INITIAL_SIZE_LARGE_BUTTON, STR_FOUND_TOWN_INITIAL_SIZE_TOOLTIP),
    00952       NWidget(WWT_TEXTBTN, COLOUR_GREY, WID_TF_SIZE_RANDOM), SetMinimalSize(78, 12), SetFill(1, 0),
    00953                     SetDataTip(STR_FOUND_TOWN_SIZE_RANDOM, STR_FOUND_TOWN_INITIAL_SIZE_TOOLTIP),
    00954     EndContainer(),
    00955     NWidget(NWID_SPACER), SetMinimalSize(0, 3),
    00956     NWidget(WWT_TEXTBTN, COLOUR_GREY, WID_TF_CITY), SetPadding(0, 2, 0, 2), SetMinimalSize(156, 12), SetFill(1, 0),
    00957                     SetDataTip(STR_FOUND_TOWN_CITY, STR_FOUND_TOWN_CITY_TOOLTIP), SetFill(1, 0),
    00958     /* Town roads selection. */
    00959     NWidget(NWID_HORIZONTAL), SetPIP(2, 0, 2),
    00960       NWidget(NWID_SPACER), SetFill(1, 0),
    00961       NWidget(WWT_LABEL, COLOUR_DARK_GREEN), SetMinimalSize(148, 14), SetDataTip(STR_FOUND_TOWN_ROAD_LAYOUT, STR_NULL),
    00962       NWidget(NWID_SPACER), SetFill(1, 0),
    00963     EndContainer(),
    00964     NWidget(NWID_HORIZONTAL, NC_EQUALSIZE), SetPIP(2, 0, 2),
    00965       NWidget(WWT_TEXTBTN, COLOUR_GREY, WID_TF_LAYOUT_ORIGINAL), SetMinimalSize(78, 12), SetFill(1, 0), SetDataTip(STR_FOUND_TOWN_SELECT_LAYOUT_ORIGINAL, STR_FOUND_TOWN_SELECT_TOWN_ROAD_LAYOUT),
    00966       NWidget(WWT_TEXTBTN, COLOUR_GREY, WID_TF_LAYOUT_BETTER), SetMinimalSize(78, 12), SetFill(1, 0), SetDataTip(STR_FOUND_TOWN_SELECT_LAYOUT_BETTER_ROADS, STR_FOUND_TOWN_SELECT_TOWN_ROAD_LAYOUT),
    00967     EndContainer(),
    00968     NWidget(NWID_SPACER), SetMinimalSize(0, 1),
    00969     NWidget(NWID_HORIZONTAL, NC_EQUALSIZE), SetPIP(2, 0, 2),
    00970       NWidget(WWT_TEXTBTN, COLOUR_GREY, WID_TF_LAYOUT_GRID2), SetMinimalSize(78, 12), SetFill(1, 0), SetDataTip(STR_FOUND_TOWN_SELECT_LAYOUT_2X2_GRID, STR_FOUND_TOWN_SELECT_TOWN_ROAD_LAYOUT),
    00971       NWidget(WWT_TEXTBTN, COLOUR_GREY, WID_TF_LAYOUT_GRID3), SetMinimalSize(78, 12), SetFill(1, 0), SetDataTip(STR_FOUND_TOWN_SELECT_LAYOUT_3X3_GRID, STR_FOUND_TOWN_SELECT_TOWN_ROAD_LAYOUT),
    00972     EndContainer(),
    00973     NWidget(NWID_SPACER), SetMinimalSize(0, 1),
    00974     NWidget(WWT_TEXTBTN, COLOUR_GREY, WID_TF_LAYOUT_RANDOM), SetPadding(0, 2, 0, 2), SetMinimalSize(0, 12), SetFill(1, 0),
    00975                     SetDataTip(STR_FOUND_TOWN_SELECT_LAYOUT_RANDOM, STR_FOUND_TOWN_SELECT_TOWN_ROAD_LAYOUT), SetFill(1, 0),
    00976     NWidget(NWID_SPACER), SetMinimalSize(0, 2),
    00977   EndContainer(),
    00978 };
    00979
    00981 struct FoundTownWindow : Window {
    00982 private:
    00983   TownSize town_size;
    00984   TownLayout town_layout;
    00985   bool city;
    00986   QueryString townname_editbox;
    00987   bool townnamevalid;
    00988   uint32 townnameparts;
    00989   TownNameParams params;
    00990
    00991 public:
    00992   FoundTownWindow(const WindowDesc *desc, WindowNumber window_number) :
    00993       town_size(TSZ_MEDIUM),
    00994       town_layout(_settings_game.economy.town_layout),
    00995       townname_editbox(MAX_LENGTH_TOWN_NAME_CHARS * MAX_CHAR_LENGTH, MAX_LENGTH_TOWN_NAME_CHARS),
    00996       params(_settings_game.game_creation.town_name)
    00997   {
    00998     this->InitNested(desc, window_number);
    00999     this->querystrings[WID_TF_TOWN_NAME_EDITBOX] = &this->townname_editbox;
    01000     this->RandomTownName();
    01001     this->UpdateButtons(true);
    01002   }
    01003
    01004   void RandomTownName()
    01005   {
    01006     this->townnamevalid = GenerateTownName(&this->townnameparts);
    01007
    01008     if (!this->townnamevalid) {
    01009       this->townname_editbox.text.DeleteAll();
    01010     } else {
    01011       GetTownName(this->townname_editbox.text.buf, &this->params, this->townnameparts, &this->townname_editbox.text.buf[this->townname_editbox.text.max_bytes - 1]);
    01012       this->townname_editbox.text.UpdateSize();
    01013     }
    01014     UpdateOSKOriginalText(this, WID_TF_TOWN_NAME_EDITBOX);
    01015
    01016     this->SetWidgetDirty(WID_TF_TOWN_NAME_EDITBOX);
    01017   }
    01018
    01019   void UpdateButtons(bool check_availability)
    01020   {
    01021     if (check_availability && _game_mode != GM_EDITOR) {
    01022       this->SetWidgetsDisabledState(true, WID_TF_RANDOM_TOWN, WID_TF_MANY_RANDOM_TOWNS, WID_TF_SIZE_LARGE, WIDGET_LIST_END);
    01023       this->SetWidgetsDisabledState(_settings_game.economy.found_town != TF_CUSTOM_LAYOUT,
    01024           WID_TF_LAYOUT_ORIGINAL, WID_TF_LAYOUT_BETTER, WID_TF_LAYOUT_GRID2, WID_TF_LAYOUT_GRID3, WID_TF_LAYOUT_RANDOM, WIDGET_LIST_END);
    01025       if (_settings_game.economy.found_town != TF_CUSTOM_LAYOUT) town_layout = _settings_game.economy.town_layout;
    01026     }
    01027
    01028     for (int i = WID_TF_SIZE_SMALL; i <= WID_TF_SIZE_RANDOM; i++) {
    01029       this->SetWidgetLoweredState(i, i == WID_TF_SIZE_SMALL + this->town_size);
    01030     }
    01031
    01032     this->SetWidgetLoweredState(WID_TF_CITY, this->city);
    01033
    01034     for (int i = WID_TF_LAYOUT_ORIGINAL; i <= WID_TF_LAYOUT_RANDOM; i++) {
    01035       this->SetWidgetLoweredState(i, i == WID_TF_LAYOUT_ORIGINAL + this->town_layout);
    01036     }
    01037
    01038     this->SetDirty();
    01039   }
    01040
    01041   void ExecuteFoundTownCommand(TileIndex tile, bool random, StringID errstr, CommandCallback cc)
    01042   {
    01043     const char *name = NULL;
    01044
    01045     if (!this->townnamevalid) {
    01046       name = this->townname_editbox.text.buf;
    01047     } else {
    01048       /* If user changed the name, send it */
    01049       char buf[MAX_LENGTH_TOWN_NAME_CHARS * MAX_CHAR_LENGTH];
    01050       GetTownName(buf, &this->params, this->townnameparts, lastof(buf));
    01051       if (strcmp(buf, this->townname_editbox.text.buf) != 0) name = this->townname_editbox.text.buf;
    01052     }
    01053
    01054     bool success = DoCommandP(tile, this->town_size | this->city << 2 | this->town_layout << 3 | random << 6,
    01055         townnameparts, CMD_FOUND_TOWN | CMD_MSG(errstr), cc, name);
    01056
    01057     if (success) this->RandomTownName();
    01058   }
    01059
    01060   virtual void OnClick(Point pt, int widget, int click_count)
    01061   {
    01062     switch (widget) {
    01063       case WID_TF_NEW_TOWN:
    01064         HandlePlacePushButton(this, WID_TF_NEW_TOWN, SPR_CURSOR_TOWN, HT_RECT);
    01065         break;
    01066
    01067       case WID_TF_RANDOM_TOWN:
    01068         this->ExecuteFoundTownCommand(0, true, STR_ERROR_CAN_T_GENERATE_TOWN, CcFoundRandomTown);
    01069         break;
    01070
    01071       case WID_TF_TOWN_NAME_RANDOM:
    01072         this->RandomTownName();
    01073         this->SetFocusedWidget(WID_TF_TOWN_NAME_EDITBOX);
    01074         break;
    01075
    01076       case WID_TF_MANY_RANDOM_TOWNS:
    01077         _generating_world = true;
    01078         UpdateNearestTownForRoadTiles(true);
    01079         if (!GenerateTowns(this->town_layout)) {
    01080           ShowErrorMessage(STR_ERROR_CAN_T_GENERATE_TOWN, STR_ERROR_NO_SPACE_FOR_TOWN, WL_INFO);
    01081         }
    01082         UpdateNearestTownForRoadTiles(false);
    01083         _generating_world = false;
    01084         break;
    01085
    01086       case WID_TF_SIZE_SMALL: case WID_TF_SIZE_MEDIUM: case WID_TF_SIZE_LARGE: case WID_TF_SIZE_RANDOM:
    01087         this->town_size = (TownSize)(widget - WID_TF_SIZE_SMALL);
    01088         this->UpdateButtons(false);
    01089         break;
    01090
    01091       case WID_TF_CITY:
    01092         this->city ^= true;
    01093         this->SetWidgetLoweredState(WID_TF_CITY, this->city);
    01094         this->SetDirty();
    01095         break;
    01096
    01097       case WID_TF_LAYOUT_ORIGINAL: case WID_TF_LAYOUT_BETTER: case WID_TF_LAYOUT_GRID2:
    01098       case WID_TF_LAYOUT_GRID3: case WID_TF_LAYOUT_RANDOM:
    01099         this->town_layout = (TownLayout)(widget - WID_TF_LAYOUT_ORIGINAL);
    01100         this->UpdateButtons(false);
    01101         break;
    01102     }
    01103   }
    01104
    01105   virtual void OnPlaceObject(Point pt, TileIndex tile)
    01106   {
    01107     this->ExecuteFoundTownCommand(tile, false, STR_ERROR_CAN_T_FOUND_TOWN_HERE, CcFoundTown);
    01108   }
    01109
    01110   virtual void OnPlaceObjectAbort()
    01111   {
    01112     this->RaiseButtons();
    01113     this->UpdateButtons(false);
    01114   }
    01115
    01121   virtual void OnInvalidateData(int data = 0, bool gui_scope = true)
    01122   {
    01123     if (!gui_scope) return;
    01124     this->UpdateButtons(true);
    01125   }
    01126 };
    01127
    01128 static const WindowDesc _found_town_desc(
    01129   WDP_AUTO, 160, 162,
    01130   WC_FOUND_TOWN, WC_NONE,
    01131   WDF_CONSTRUCTION,
    01132   _nested_found_town_widgets, lengthof(_nested_found_town_widgets)
    01133 );
    01134
    01135 void ShowFoundTownWindow()
    01136 {
    01137   if (_game_mode != GM_EDITOR && !Company::IsValidID(_local_company)) return;
    01138   AllocateWindowDescFront<FoundTownWindow>(&_found_town_desc, 0);
    01139 }