# network_gui.cpp

Go to the documentation of this file.

    00001 /* $Id: network_gui.cpp 20092 2010-07-08 19:44:00Z rubidium $ */
    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 #ifdef ENABLE_NETWORK
    00013 #include "../stdafx.h"
    00014 #include "../strings_func.h"
    00015 #include "../date_func.h"
    00016 #include "../fios.h"
    00017 #include "network_client.h"
    00018 #include "network_gui.h"
    00019 #include "network_gamelist.h"
    00020 #include "network.h"
    00021 #include "network_base.h"
    00022 #include "../gui.h"
    00023 #include "network_udp.h"
    00024 #include "../window_func.h"
    00025 #include "../gfx_func.h"
    00026 #include "../widgets/dropdown_func.h"
    00027 #include "../querystring_gui.h"
    00028 #include "../sortlist_type.h"
    00029 #include "../company_base.h"
    00030 #include "../company_func.h"
    00031 #include "../core/geometry_func.hpp"
    00032
    00033 #include "table/strings.h"
    00034 #include "../table/sprites.h"
    00035
    00036
    00037 static void ShowNetworkStartServerWindow();
    00038 static void ShowNetworkLobbyWindow(NetworkGameList *ngl);
    00039 extern void SwitchToMode(SwitchMode new_mode);
    00040
    00041 static const StringID _connection_types_dropdown[] = {
    00042   STR_NETWORK_START_SERVER_LAN_INTERNET,
    00043   STR_NETWORK_START_SERVER_INTERNET_ADVERTISE,
    00044   INVALID_STRING_ID
    00045 };
    00046
    00047 static const StringID _lan_internet_types_dropdown[] = {
    00048   STR_NETWORK_SERVER_LIST_LAN,
    00049   STR_NETWORK_SERVER_LIST_INTERNET,
    00050   INVALID_STRING_ID
    00051 };
    00052
    00053 static StringID _language_dropdown[NETLANG_COUNT + 1] = {STR_NULL};
    00054
    00055 void SortNetworkLanguages()
    00056 {
    00057   /* Init the strings */
    00058   if (_language_dropdown[0] == STR_NULL) {
    00059     for (int i = 0; i < NETLANG_COUNT; i++) _language_dropdown[i] = STR_NETWORK_LANG_ANY + i;
    00060     _language_dropdown[NETLANG_COUNT] = INVALID_STRING_ID;
    00061   }
    00062
    00063   /* Sort the strings (we don't move 'any' and the 'invalid' one) */
    00064   QSortT(_language_dropdown, NETLANG_COUNT - 1, &StringIDSorter);
    00065 }
    00066
    00070 void UpdateNetworkGameWindow(bool unselect)
    00071 {
    00072   InvalidateWindowData(WC_NETWORK_WINDOW, 0, unselect ? 1 : 0);
    00073 }
    00074
    00076 enum NetworkGameWindowWidgets {
    00077   NGWW_MAIN,
    00078
    00079   NGWW_CONNECTION,
    00080   NGWW_CONN_BTN,
    00081   NGWW_CLIENT_LABEL,
    00082   NGWW_CLIENT,
    00083
    00084   NGWW_HEADER,
    00085   NGWW_NAME,
    00086   NGWW_CLIENTS,
    00087   NGWW_MAPSIZE,
    00088   NGWW_DATE,
    00089   NGWW_YEARS,
    00090   NGWW_INFO,
    00091
    00092   NGWW_MATRIX,
    00093   NGWW_SCROLLBAR,
    00094
    00095   NGWW_LASTJOINED_LABEL,
    00096   NGWW_LASTJOINED,
    00097
    00098   NGWW_DETAILS,
    00099   NGWW_DETAILS_SPACER,
    00100   NGWW_JOIN,
    00101   NGWW_REFRESH,
    00102   NGWW_NEWGRF,
    00103   NGWW_NEWGRF_SEL,
    00104
    00105   NGWW_FIND,
    00106   NGWW_ADD,
    00107   NGWW_START,
    00108   NGWW_CANCEL,
    00109 };
    00110
    00111 typedef GUIList<NetworkGameList*> GUIGameServerList;
    00112 typedef uint16 ServerListPosition;
    00113 static const ServerListPosition SLP_INVALID = 0xFFFF;
    00114
    00116 class NWidgetServerListHeader : public NWidgetContainer {
    00117   static const uint MINIMUM_NAME_WIDTH_BEFORE_NEW_HEADER = 150;
    00118   bool visible[6];
    00119 public:
    00120   NWidgetServerListHeader() : NWidgetContainer(NWID_HORIZONTAL)
    00121   {
    00122     NWidgetLeaf *leaf = new NWidgetLeaf(WWT_PUSHTXTBTN, COLOUR_WHITE, NGWW_NAME, STR_NETWORK_SERVER_LIST_GAME_NAME, STR_NETWORK_SERVER_LIST_GAME_NAME_TOOLTIP);
    00123     leaf->SetResize(1, 0);
    00124     leaf->SetFill(1, 0);
    00125     this->Add(leaf);
    00126
    00127     this->Add(new NWidgetLeaf(WWT_PUSHTXTBTN, COLOUR_WHITE, NGWW_CLIENTS, STR_NETWORK_SERVER_LIST_CLIENTS_CAPTION, STR_NETWORK_SERVER_LIST_CLIENTS_CAPTION_TOOLTIP));
    00128     this->Add(new NWidgetLeaf(WWT_PUSHTXTBTN, COLOUR_WHITE, NGWW_MAPSIZE, STR_NETWORK_SERVER_LIST_MAP_SIZE_CAPTION, STR_NETWORK_SERVER_LIST_MAP_SIZE_CAPTION_TOOLTIP));
    00129     this->Add(new NWidgetLeaf(WWT_PUSHTXTBTN, COLOUR_WHITE, NGWW_DATE, STR_NETWORK_SERVER_LIST_DATE_CAPTION, STR_NETWORK_SERVER_LIST_DATE_CAPTION_TOOLTIP));
    00130     this->Add(new NWidgetLeaf(WWT_PUSHTXTBTN, COLOUR_WHITE, NGWW_YEARS, STR_NETWORK_SERVER_LIST_YEARS_CAPTION, STR_NETWORK_SERVER_LIST_YEARS_CAPTION_TOOLTIP));
    00131
    00132     leaf = new NWidgetLeaf(WWT_PUSHTXTBTN, COLOUR_WHITE, NGWW_INFO, STR_EMPTY, STR_NETWORK_SERVER_LIST_INFO_ICONS_TOOLTIP);
    00133     leaf->SetMinimalSize(40, 12);
    00134     leaf->SetFill(0, 1);
    00135     this->Add(leaf);
    00136
    00137     /* First and last are always visible, the rest is implicitly zeroed */
    00138     this->visible[0] = true;
    00139     *lastof(this->visible) = true;
    00140   }
    00141
    00142   void SetupSmallestSize(Window *w, bool init_array)
    00143   {
    00144     /* Oh yeah, we ought to be findable! */
    00145     w->nested_array[NGWW_HEADER] = this;
    00146
    00147     this->smallest_x = this->head->smallest_x + this->tail->smallest_x; // First and last are always shown, rest not
    00148     this->smallest_y = 0; // Biggest child.
    00149     this->fill_x = 1;
    00150     this->fill_y = 0;
    00151     this->resize_x = 1; // We only resize in this direction
    00152     this->resize_y = 0; // We never resize in this direction
    00153
    00154     /* First initialise some variables... */
    00155     for (NWidgetBase *child_wid = this->head; child_wid != NULL; child_wid = child_wid->next) {
    00156       child_wid->SetupSmallestSize(w, init_array);
    00157       this->smallest_y = max(this->smallest_y, child_wid->smallest_y + child_wid->padding_top + child_wid->padding_bottom);
    00158     }
    00159
    00160     /* ... then in a second pass make sure the 'current' sizes are set. Won't change for most widgets. */
    00161     for (NWidgetBase *child_wid = this->head; child_wid != NULL; child_wid = child_wid->next) {
    00162       child_wid->current_x = child_wid->smallest_x;
    00163       child_wid->current_y = this->smallest_y;
    00164     }
    00165   }
    00166
    00167   void AssignSizePosition(SizingType sizing, uint x, uint y, uint given_width, uint given_height, bool rtl)
    00168   {
    00169     assert(given_width >= this->smallest_x && given_height >= this->smallest_y);
    00170
    00171     this->pos_x = x;
    00172     this->pos_y = y;
    00173     this->current_x = given_width;
    00174     this->current_y = given_height;
    00175
    00176     given_width -= this->tail->smallest_x;
    00177     NWidgetBase *child_wid = this->head->next;
    00178     /* The first and last widget are always visible, determine which other should be visible */
    00179     for (uint i = 1; i < lengthof(this->visible) - 1; i++) {
    00180       if (given_width - child_wid->smallest_x > MINIMUM_NAME_WIDTH_BEFORE_NEW_HEADER && this->visible[i - 1]) {
    00181         this->visible[i] = true;
    00182         given_width -= child_wid->smallest_x;
    00183       } else {
    00184         this->visible[i] = false;
    00185       }
    00186       child_wid = child_wid->next;
    00187     }
    00188
    00189     /* All remaining space goes to the first (name) widget */
    00190     this->head->current_x = given_width;
    00191
    00192     /* Now assign the widgets to their rightful place */
    00193     uint position = 0; // Place to put next child relative to origin of the container.
    00194     uint i = rtl ? lengthof(this->visible) - 1 : 0;
    00195     child_wid = rtl ? this->tail : this->head;
    00196     while (child_wid != NULL) {
    00197       if (this->visible[i]) {
    00198         child_wid->AssignSizePosition(sizing, x + position, y, child_wid->current_x, this->current_y, rtl);
    00199         position += child_wid->current_x;
    00200       }
    00201
    00202       child_wid = rtl ? child_wid->prev : child_wid->next;
    00203       i += rtl ? -1 : 1;
    00204     }
    00205   }
    00206
    00207   /* virtual */ void Draw(const Window *w)
    00208   {
    00209     int i = 0;
    00210     for (NWidgetBase *child_wid = this->head; child_wid != NULL; child_wid = child_wid->next) {
    00211       if (!this->visible[i++]) continue;
    00212
    00213       child_wid->Draw(w);
    00214     }
    00215   }
    00216
    00217   /* virtual */ NWidgetCore *GetWidgetFromPos(int x, int y)
    00218   {
    00219     if (!IsInsideBS(x, this->pos_x, this->current_x) || !IsInsideBS(y, this->pos_y, this->current_y)) return NULL;
    00220
    00221     int i = 0;
    00222     for (NWidgetBase *child_wid = this->head; child_wid != NULL; child_wid = child_wid->next) {
    00223       if (!this->visible[i++]) continue;
    00224       NWidgetCore *nwid = child_wid->GetWidgetFromPos(x, y);
    00225       if (nwid != NULL) return nwid;
    00226     }
    00227     return NULL;
    00228   }
    00229
    00235   bool IsWidgetVisible(NetworkGameWindowWidgets widget) const
    00236   {
    00237     assert((uint)(widget - NGWW_NAME) < lengthof(this->visible));
    00238     return this->visible[widget - NGWW_NAME];
    00239   }
    00240 };
    00241
    00242 class NetworkGameWindow : public QueryStringBaseWindow {
    00243 protected:
    00244   /* Runtime saved values */
    00245   static Listing last_sorting;
    00246
    00247   /* Constants for sorting servers */
    00248   static GUIGameServerList::SortFunction * const sorter_funcs[];
    00249
    00250   byte field;
    00251   NetworkGameList *server;
    00252   NetworkGameList *last_joined;
    00253   GUIGameServerList servers;
    00254   ServerListPosition list_pos;
    00255
    00260   void BuildNetworkGameList()
    00261   {
    00262     if (!this->servers.NeedRebuild()) return;
    00263
    00264     /* Create temporary array of games to use for listing */
    00265     this->servers.Clear();
    00266
    00267     for (NetworkGameList *ngl = _network_game_list; ngl != NULL; ngl = ngl->next) {
    00268       *this->servers.Append() = ngl;
    00269     }
    00270
    00271     this->servers.Compact();
    00272     this->servers.RebuildDone();
    00273     this->vscroll.SetCount(this->servers.Length());
    00274   }
    00275
    00277   static int CDECL NGameNameSorter(NetworkGameList * const *a, NetworkGameList * const *b)
    00278   {
    00279     return strcasecmp((*a)->info.server_name, (*b)->info.server_name);
    00280   }
    00281
    00285   static int CDECL NGameClientSorter(NetworkGameList * const *a, NetworkGameList * const *b)
    00286   {
    00287     /* Reverse as per default we are interested in most-clients first */
    00288     int r = (*a)->info.clients_on - (*b)->info.clients_on;
    00289
    00290     if (r == 0) r = (*a)->info.clients_max - (*b)->info.clients_max;
    00291     if (r == 0) r = NGameNameSorter(a, b);
    00292
    00293     return r;
    00294   }
    00295
    00297   static int CDECL NGameMapSizeSorter(NetworkGameList * const *a, NetworkGameList * const *b)
    00298   {
    00299     /* Sort by the area of the map. */
    00300     int r = ((*a)->info.map_height) * ((*a)->info.map_width) - ((*b)->info.map_height) * ((*b)->info.map_width);
    00301
    00302     if (r == 0) r = (*a)->info.map_width - (*b)->info.map_width;
    00303     return (r != 0) ? r : NGameClientSorter(a, b);
    00304   }
    00305
    00307   static int CDECL NGameDateSorter(NetworkGameList * const *a, NetworkGameList * const *b)
    00308   {
    00309     int r = (*a)->info.game_date - (*b)->info.game_date;
    00310     return (r != 0) ? r : NGameClientSorter(a, b);
    00311   }
    00312
    00314   static int CDECL NGameYearsSorter(NetworkGameList * const *a, NetworkGameList * const *b)
    00315   {
    00316     int r = (*a)->info.game_date - (*a)->info.start_date - (*b)->info.game_date + (*b)->info.start_date;
    00317     return (r != 0) ? r : NGameDateSorter(a, b);
    00318   }
    00319
    00322   static int CDECL NGameAllowedSorter(NetworkGameList * const *a, NetworkGameList * const *b)
    00323   {
    00324     /* The servers we do not know anything about (the ones that did not reply) should be at the bottom) */
    00325     int r = StrEmpty((*a)->info.server_revision) - StrEmpty((*b)->info.server_revision);
    00326
    00327     /* Reverse default as we are interested in version-compatible clients first */
    00328     if (r == 0) r = (*b)->info.version_compatible - (*a)->info.version_compatible;
    00329     /* The version-compatible ones are then sorted with NewGRF compatible first, incompatible last */
    00330     if (r == 0) r = (*b)->info.compatible - (*a)->info.compatible;
    00331     /* Passworded servers should be below unpassworded servers */
    00332     if (r == 0) r = (*a)->info.use_password - (*b)->info.use_password;
    00333     /* Finally sort on the name of the server */
    00334     if (r == 0) r = NGameNameSorter(a, b);
    00335
    00336     return r;
    00337   }
    00338
    00340   void SortNetworkGameList()
    00341   {
    00342     if (!this->servers.Sort()) return;
    00343
    00344     /* After sorting ngl->sort_list contains the sorted items. Put these back
    00345      * into the original list. Basically nothing has changed, we are only
    00346      * shuffling the ->next pointers. While iterating, look for the
    00347      * currently selected server and set list_pos to its position */
    00348     this->list_pos = SLP_INVALID;
    00349     _network_game_list = this->servers[0];
    00350     NetworkGameList *item = _network_game_list;
    00351     if (item == this->server) this->list_pos = 0;
    00352     for (uint i = 1; i != this->servers.Length(); i++) {
    00353       item->next = this->servers[i];
    00354       item = item->next;
    00355       if (item == this->server) this->list_pos = i;
    00356     }
    00357     item->next = NULL;
    00358   }
    00359
    00366   void DrawServerLine(const NetworkGameList *cur_item, uint y, bool highlight) const
    00367   {
    00368     const NWidgetBase *nwi_name = this->GetWidget<NWidgetBase>(NGWW_NAME);
    00369     const NWidgetBase *nwi_info = this->GetWidget<NWidgetBase>(NGWW_INFO);
    00370
    00371     /* show highlighted item with a different colour */
    00372     if (highlight) GfxFillRect(nwi_name->pos_x + 1, y - 2, nwi_info->pos_x + nwi_info->current_x - 2, y + FONT_HEIGHT_NORMAL - 1, 10);
    00373
    00374     DrawString(nwi_name->pos_x + WD_FRAMERECT_LEFT, nwi_name->pos_x + nwi_name->current_x - WD_FRAMERECT_RIGHT, y, cur_item->info.server_name, TC_BLACK);
    00375
    00376     /* only draw details if the server is online */
    00377     if (cur_item->online) {
    00378       const NWidgetServerListHeader *nwi_header = this->GetWidget<NWidgetServerListHeader>(NGWW_HEADER);
    00379
    00380       if (nwi_header->IsWidgetVisible(NGWW_CLIENTS)) {
    00381         const NWidgetBase *nwi_clients = this->GetWidget<NWidgetBase>(NGWW_CLIENTS);
    00382         SetDParam(0, cur_item->info.clients_on);
    00383         SetDParam(1, cur_item->info.clients_max);
    00384         SetDParam(2, cur_item->info.companies_on);
    00385         SetDParam(3, cur_item->info.companies_max);
    00386         DrawString(nwi_clients->pos_x, nwi_clients->pos_x + nwi_clients->current_x - 1, y, STR_NETWORK_SERVER_LIST_GENERAL_ONLINE, TC_FROMSTRING, SA_CENTER);
    00387       }
    00388
    00389       if (nwi_header->IsWidgetVisible(NGWW_MAPSIZE)) {
    00390         /* map size */
    00391         const NWidgetBase *nwi_mapsize = this->GetWidget<NWidgetBase>(NGWW_MAPSIZE);
    00392         SetDParam(0, cur_item->info.map_width);
    00393         SetDParam(1, cur_item->info.map_height);
    00394         DrawString(nwi_mapsize->pos_x, nwi_mapsize->pos_x + nwi_mapsize->current_x - 1, y, STR_NETWORK_SERVER_LIST_MAP_SIZE_SHORT, TC_FROMSTRING, SA_CENTER);
    00395       }
    00396
    00397       if (nwi_header->IsWidgetVisible(NGWW_DATE)) {
    00398         /* current date */
    00399         const NWidgetBase *nwi_date = this->GetWidget<NWidgetBase>(NGWW_DATE);
    00400         YearMonthDay ymd;
    00401         ConvertDateToYMD(cur_item->info.game_date, &ymd);
    00402         SetDParam(0, ymd.year);
    00403         DrawString(nwi_date->pos_x, nwi_date->pos_x + nwi_date->current_x - 1, y, STR_JUST_INT, TC_BLACK, SA_CENTER);
    00404       }
    00405
    00406       if (nwi_header->IsWidgetVisible(NGWW_YEARS)) {
    00407         /* number of years the game is running */
    00408         const NWidgetBase *nwi_years = this->GetWidget<NWidgetBase>(NGWW_YEARS);
    00409         YearMonthDay ymd_cur, ymd_start;
    00410         ConvertDateToYMD(cur_item->info.game_date, &ymd_cur);
    00411         ConvertDateToYMD(cur_item->info.start_date, &ymd_start);
    00412         SetDParam(0, ymd_cur.year - ymd_start.year);
    00413         DrawString(nwi_years->pos_x, nwi_years->pos_x + nwi_years->current_x - 1, y, STR_JUST_INT, TC_BLACK, SA_CENTER);
    00414       }
    00415
    00416       /* Align the sprites */
    00417       y += (FONT_HEIGHT_NORMAL - 10) / 2;
    00418
    00419       /* draw a lock if the server is password protected */
    00420       if (cur_item->info.use_password) DrawSprite(SPR_LOCK, PAL_NONE, nwi_info->pos_x + 5, y - 1);
    00421
    00422       /* draw red or green icon, depending on compatibility with server */
    00423       DrawSprite(SPR_BLOT, (cur_item->info.compatible ? PALETTE_TO_GREEN : (cur_item->info.version_compatible ? PALETTE_TO_YELLOW : PALETTE_TO_RED)), nwi_info->pos_x + 15, y);
    00424
    00425       /* draw flag according to server language */
    00426       DrawSprite(SPR_FLAGS_BASE + cur_item->info.server_lang, PAL_NONE, nwi_info->pos_x + 25, y);
    00427     }
    00428   }
    00429
    00437   void ScrollToSelectedServer()
    00438   {
    00439     if (this->list_pos == SLP_INVALID) return; // no server selected
    00440     this->vscroll.ScrollTowards(this->list_pos);
    00441   }
    00442
    00443 public:
    00444   NetworkGameWindow(const WindowDesc *desc) : QueryStringBaseWindow(NETWORK_CLIENT_NAME_LENGTH)
    00445   {
    00446     this->InitNested(desc, 0);
    00447
    00448     ttd_strlcpy(this->edit_str_buf, _settings_client.network.client_name, this->edit_str_size);
    00449     this->afilter = CS_ALPHANUMERAL;
    00450     InitializeTextBuffer(&this->text, this->edit_str_buf, this->edit_str_size, 120);
    00451     this->SetFocusedWidget(NGWW_CLIENT);
    00452
    00453     UpdateNetworkGameWindow(true);
    00454
    00455     this->field = NGWW_CLIENT;
    00456     this->last_joined = NetworkGameListAddItem(NetworkAddress(_settings_client.network.last_host, _settings_client.network.last_port));
    00457     this->server = this->last_joined;
    00458
    00459     this->servers.SetListing(this->last_sorting);
    00460     this->servers.SetSortFuncs(this->sorter_funcs);
    00461     this->servers.ForceRebuild();
    00462     this->SortNetworkGameList();
    00463   }
    00464
    00465   ~NetworkGameWindow()
    00466   {
    00467     this->last_sorting = this->servers.GetListing();
    00468   }
    00469
    00470   virtual void SetStringParameters(int widget) const
    00471   {
    00472     switch (widget) {
    00473       case NGWW_CONN_BTN:
    00474         SetDParam(0, _lan_internet_types_dropdown[_settings_client.network.lan_internet]);
    00475         break;
    00476     }
    00477   }
    00478
    00479   virtual void UpdateWidgetSize(int widget, Dimension *size, const Dimension &padding, Dimension *fill, Dimension *resize)
    00480   {
    00481     switch (widget) {
    00482       case NGWW_CONN_BTN:
    00483         *size = maxdim(GetStringBoundingBox(_lan_internet_types_dropdown[0]), GetStringBoundingBox(_lan_internet_types_dropdown[1]));
    00484         size->width += padding.width;
    00485         size->height += padding.height;
    00486         break;
    00487
    00488       case NGWW_MATRIX:
    00489         resize->height = WD_MATRIX_TOP + FONT_HEIGHT_NORMAL + WD_MATRIX_BOTTOM;
    00490         size->height = 10 * resize->height;
    00491         break;
    00492
    00493       case NGWW_LASTJOINED:
    00494         size->height = WD_MATRIX_TOP + FONT_HEIGHT_NORMAL + WD_MATRIX_BOTTOM;
    00495         break;
    00496
    00497       case NGWW_NAME:
    00498         size->width += 2 * WD_SORTBUTTON_ARROW_WIDTH; // Make space for the arrow
    00499         break;
    00500
    00501       case NGWW_CLIENTS:
    00502         size->width += 2 * WD_SORTBUTTON_ARROW_WIDTH; // Make space for the arrow
    00503         SetDParam(0, 255);
    00504         SetDParam(1, 255);
    00505         SetDParam(2, 15);
    00506         SetDParam(3, 15);
    00507         *size = maxdim(*size, GetStringBoundingBox(STR_NETWORK_SERVER_LIST_GENERAL_ONLINE));
    00508         break;
    00509
    00510       case NGWW_MAPSIZE:
    00511         size->width += 2 * WD_SORTBUTTON_ARROW_WIDTH; // Make space for the arrow
    00512         SetDParam(0, 2048);
    00513         SetDParam(1, 2048);
    00514         *size = maxdim(*size, GetStringBoundingBox(STR_NETWORK_SERVER_LIST_MAP_SIZE_SHORT));
    00515         break;
    00516
    00517       case NGWW_DATE:
    00518       case NGWW_YEARS:
    00519         size->width += 2 * WD_SORTBUTTON_ARROW_WIDTH; // Make space for the arrow
    00520         SetDParam(0, 99999);
    00521         *size = maxdim(*size, GetStringBoundingBox(STR_JUST_INT));
    00522         break;
    00523
    00524       case NGWW_DETAILS_SPACER:
    00525         size->height = 20 + 12 * FONT_HEIGHT_NORMAL;
    00526         break;
    00527     }
    00528   }
    00529
    00530   virtual void DrawWidget(const Rect &r, int widget) const
    00531   {
    00532     switch (widget) {
    00533       case NGWW_MATRIX: {
    00534         uint16 y = r.top + WD_MATRIX_TOP;
    00535
    00536         const int max = min(this->vscroll.GetPosition() + this->vscroll.GetCapacity(), (int)this->servers.Length());
    00537
    00538         for (int i = this->vscroll.GetPosition(); i < max; ++i) {
    00539           const NetworkGameList *ngl = this->servers[i];
    00540           this->DrawServerLine(ngl, y, ngl == this->server);
    00541           y += this->resize.step_height;
    00542         }
    00543       } break;
    00544
    00545       case NGWW_LASTJOINED:
    00546         /* Draw the last joined server, if any */
    00547         if (this->last_joined != NULL) this->DrawServerLine(this->last_joined, r.top + WD_MATRIX_TOP, this->last_joined == this->server);
    00548         break;
    00549
    00550       case NGWW_DETAILS:
    00551         this->DrawDetails(r);
    00552         break;
    00553
    00554       case NGWW_NAME:
    00555       case NGWW_CLIENTS:
    00556       case NGWW_MAPSIZE:
    00557       case NGWW_DATE:
    00558       case NGWW_YEARS:
    00559       case NGWW_INFO:
    00560         if (widget - NGWW_NAME == this->servers.SortType()) this->DrawSortButtonState(widget, this->servers.IsDescSortOrder() ? SBS_DOWN : SBS_UP);
    00561         break;
    00562     }
    00563   }
    00564
    00565
    00566   virtual void OnPaint()
    00567   {
    00568     if (this->servers.NeedRebuild()) {
    00569       this->BuildNetworkGameList();
    00570     }
    00571     this->SortNetworkGameList();
    00572
    00573     NetworkGameList *sel = this->server;
    00574     /* 'Refresh' button invisible if no server selected */
    00575     this->SetWidgetDisabledState(NGWW_REFRESH, sel == NULL);
    00576     /* 'Join' button disabling conditions */
    00577     this->SetWidgetDisabledState(NGWW_JOIN, sel == NULL || // no Selected Server
    00578         !sel->online || // Server offline
    00579         sel->info.clients_on >= sel->info.clients_max || // Server full
    00580         !sel->info.compatible); // Revision mismatch
    00581
    00582     /* 'NewGRF Settings' button invisible if no NewGRF is used */
    00583     this->GetWidget<NWidgetStacked>(NGWW_NEWGRF_SEL)->SetDisplayedPlane(sel == NULL || !sel->online || sel->info.grfconfig == NULL);
    00584
    00585     this->DrawWidgets();
    00586     /* Edit box to set client name */
    00587     this->DrawEditBox(NGWW_CLIENT);
    00588   }
    00589
    00590   void DrawDetails(const Rect &r) const
    00591   {
    00592     NetworkGameList *sel = this->server;
    00593
    00594     const int detail_height = 6 + 8 + 6 + 3 * FONT_HEIGHT_NORMAL;
    00595
    00596     /* Draw the right menu */
    00597     GfxFillRect(r.left + 1, r.top + 1, r.right - 1, r.top + detail_height - 1, 157);
    00598     if (sel == NULL) {
    00599       DrawString(r.left + WD_FRAMERECT_LEFT, r.right - WD_FRAMERECT_RIGHT, r.top + 6 + 4 + FONT_HEIGHT_NORMAL, STR_NETWORK_SERVER_LIST_GAME_INFO, TC_FROMSTRING, SA_CENTER);
    00600     } else if (!sel->online) {
    00601       DrawString(r.left + WD_FRAMERECT_LEFT, r.right - WD_FRAMERECT_RIGHT, r.top + 6 + 4 + FONT_HEIGHT_NORMAL, sel->info.server_name, TC_ORANGE, SA_CENTER); // game name
    00602
    00603       DrawString(r.left + WD_FRAMERECT_LEFT, r.right - WD_FRAMERECT_RIGHT, r.top + detail_height + 4, STR_NETWORK_SERVER_LIST_SERVER_OFFLINE, TC_FROMSTRING, SA_CENTER); // server offline
    00604     } else { // show game info
    00605
    00606       DrawString(r.left + WD_FRAMERECT_LEFT, r.right - WD_FRAMERECT_RIGHT, r.top + 6, STR_NETWORK_SERVER_LIST_GAME_INFO, TC_FROMSTRING, SA_CENTER);
    00607       DrawString(r.left + WD_FRAMERECT_LEFT, r.right - WD_FRAMERECT_RIGHT, r.top + 6 + 4 + FONT_HEIGHT_NORMAL, sel->info.server_name, TC_ORANGE, SA_CENTER); // game name
    00608       DrawString(r.left + WD_FRAMERECT_LEFT, r.right - WD_FRAMERECT_RIGHT, r.top + 6 + 8 + 2 * FONT_HEIGHT_NORMAL, sel->info.map_name, TC_BLACK, SA_CENTER); // map name
    00609
    00610       uint16 y = r.top + detail_height + 4;
    00611
    00612       SetDParam(0, sel->info.clients_on);
    00613       SetDParam(1, sel->info.clients_max);
    00614       SetDParam(2, sel->info.companies_on);
    00615       SetDParam(3, sel->info.companies_max);
    00616       DrawString(r.left + WD_FRAMERECT_LEFT, r.right - WD_FRAMERECT_RIGHT, y, STR_NETWORK_SERVER_LIST_CLIENTS);
    00617       y += FONT_HEIGHT_NORMAL;
    00618
    00619       SetDParam(0, STR_NETWORK_LANG_ANY + sel->info.server_lang);
    00620       DrawString(r.left + WD_FRAMERECT_LEFT, r.right - WD_FRAMERECT_RIGHT, y, STR_NETWORK_SERVER_LIST_LANGUAGE); // server language
    00621       y += FONT_HEIGHT_NORMAL;
    00622
    00623       SetDParam(0, STR_CHEAT_SWITCH_CLIMATE_TEMPERATE_LANDSCAPE + sel->info.map_set);
    00624       DrawString(r.left + WD_FRAMERECT_LEFT, r.right - WD_FRAMERECT_RIGHT, y, STR_NETWORK_SERVER_LIST_LANDSCAPE); // landscape
    00625       y += FONT_HEIGHT_NORMAL;
    00626
    00627       SetDParam(0, sel->info.map_width);
    00628       SetDParam(1, sel->info.map_height);
    00629       DrawString(r.left + WD_FRAMERECT_LEFT, r.right - WD_FRAMERECT_RIGHT, y, STR_NETWORK_SERVER_LIST_MAP_SIZE); // map size
    00630       y += FONT_HEIGHT_NORMAL;
    00631
    00632       SetDParamStr(0, sel->info.server_revision);
    00633       DrawString(r.left + WD_FRAMERECT_LEFT, r.right - WD_FRAMERECT_RIGHT, y, STR_NETWORK_SERVER_LIST_SERVER_VERSION); // server version
    00634       y += FONT_HEIGHT_NORMAL;
    00635
    00636       SetDParamStr(0, sel->address.GetAddressAsString());
    00637       DrawString(r.left + WD_FRAMERECT_LEFT, r.right - WD_FRAMERECT_RIGHT, y, STR_NETWORK_SERVER_LIST_SERVER_ADDRESS); // server address
    00638       y += FONT_HEIGHT_NORMAL;
    00639
    00640       SetDParam(0, sel->info.start_date);
    00641       DrawString(r.left + WD_FRAMERECT_LEFT, r.right - WD_FRAMERECT_RIGHT, y, STR_NETWORK_SERVER_LIST_START_DATE); // start date
    00642       y += FONT_HEIGHT_NORMAL;
    00643
    00644       SetDParam(0, sel->info.game_date);
    00645       DrawString(r.left + WD_FRAMERECT_LEFT, r.right - WD_FRAMERECT_RIGHT, y, STR_NETWORK_SERVER_LIST_CURRENT_DATE); // current date
    00646       y += FONT_HEIGHT_NORMAL;
    00647
    00648       y += WD_PAR_VSEP_NORMAL;
    00649
    00650       if (!sel->info.compatible) {
    00651         DrawString(r.left + WD_FRAMERECT_LEFT, r.right - WD_FRAMERECT_RIGHT, y, sel->info.version_compatible ? STR_NETWORK_SERVER_LIST_GRF_MISMATCH : STR_NETWORK_SERVER_LIST_VERSION_MISMATCH, TC_FROMSTRING, SA_CENTER); // server mismatch
    00652       } else if (sel->info.clients_on == sel->info.clients_max) {
    00653         /* Show: server full, when clients_on == max_clients */
    00654         DrawString(r.left + WD_FRAMERECT_LEFT, r.right - WD_FRAMERECT_RIGHT, y, STR_NETWORK_SERVER_LIST_SERVER_FULL, TC_FROMSTRING, SA_CENTER); // server full
    00655       } else if (sel->info.use_password) {
    00656         DrawString(r.left + WD_FRAMERECT_LEFT, r.right - WD_FRAMERECT_RIGHT, y, STR_NETWORK_SERVER_LIST_PASSWORD, TC_FROMSTRING, SA_CENTER); // password warning
    00657       }
    00658     }
    00659   }
    00660
    00661   virtual void OnClick(Point pt, int widget, int click_count)
    00662   {
    00663     this->field = widget;
    00664     switch (widget) {
    00665       case NGWW_CANCEL: // Cancel button
    00666         DeleteWindowById(WC_NETWORK_WINDOW, 0);
    00667         break;
    00668
    00669       case NGWW_CONN_BTN: // 'Connection' droplist
    00670         ShowDropDownMenu(this, _lan_internet_types_dropdown, _settings_client.network.lan_internet, NGWW_CONN_BTN, 0, 0); // do it for widget NSSW_CONN_BTN
    00671         break;
    00672
    00673       case NGWW_NAME:    // Sort by name
    00674       case NGWW_CLIENTS: // Sort by connected clients
    00675       case NGWW_MAPSIZE: // Sort by map size
    00676       case NGWW_DATE:    // Sort by date
    00677       case NGWW_YEARS:   // Sort by years
    00678       case NGWW_INFO:    // Connectivity (green dot)
    00679         if (this->servers.SortType() == widget - NGWW_NAME) {
    00680           this->servers.ToggleSortOrder();
    00681           if (this->list_pos != SLP_INVALID) this->list_pos = this->servers.Length() - this->list_pos - 1;
    00682         } else {
    00683           this->servers.SetSortType(widget - NGWW_NAME);
    00684           this->servers.ForceResort();
    00685           this->SortNetworkGameList();
    00686         }
    00687         this->ScrollToSelectedServer();
    00688         this->SetDirty();
    00689         break;
    00690
    00691       case NGWW_MATRIX: { // Matrix to show networkgames
    00692         uint32 id_v = (pt.y - this->GetWidget<NWidgetBase>(NGWW_MATRIX)->pos_y) / this->resize.step_height;
    00693
    00694         if (id_v >= this->vscroll.GetCapacity()) return; // click out of bounds
    00695         id_v += this->vscroll.GetPosition();
    00696
    00697         this->server = (id_v < this->servers.Length()) ? this->servers[id_v] : NULL;
    00698         this->list_pos = (server == NULL) ? SLP_INVALID : id_v;
    00699         this->SetDirty();
    00700
    00701         /* FIXME the disabling should go into some InvalidateData, which is called instead of the SetDirty */
    00702         if (click_count > 1 && !this->IsWidgetDisabled(NGWW_JOIN)) this->OnClick(pt, NGWW_JOIN, 1);
    00703       } break;
    00704
    00705       case NGWW_LASTJOINED: {
    00706         NetworkGameList *last_joined = NetworkGameListAddItem(NetworkAddress(_settings_client.network.last_host, _settings_client.network.last_port));
    00707         if (last_joined != NULL) {
    00708           this->server = last_joined;
    00709
    00710           /* search the position of the newly selected server */
    00711           for (uint i = 0; i < this->servers.Length(); i++) {
    00712             if (this->servers[i] == this->server) {
    00713               this->list_pos = i;
    00714               break;
    00715             }
    00716           }
    00717           this->ScrollToSelectedServer();
    00718           this->SetDirty();
    00719
    00720           /* FIXME the disabling should go into some InvalidateData, which is called instead of the SetDirty */
    00721           if (click_count > 1 && !this->IsWidgetDisabled(NGWW_JOIN)) this->OnClick(pt, NGWW_JOIN, 1);
    00722         }
    00723       } break;
    00724
    00725       case NGWW_FIND: // Find server automatically
    00726         switch (_settings_client.network.lan_internet) {
    00727           case 0: NetworkUDPSearchGame(); break;
    00728           case 1: NetworkUDPQueryMasterServer(); break;
    00729         }
    00730         break;
    00731
    00732       case NGWW_ADD: // Add a server
    00733         SetDParamStr(0, _settings_client.network.connect_to_ip);
    00734         ShowQueryString(
    00735           STR_JUST_RAW_STRING,
    00736           STR_NETWORK_SERVER_LIST_ENTER_IP,
    00737           NETWORK_HOSTNAME_LENGTH,  // maximum number of characters including '\0'
    00738           0,                        // no limit in pixels
    00739           this, CS_ALPHANUMERAL, QSF_ACCEPT_UNCHANGED);
    00740         break;
    00741
    00742       case NGWW_START: // Start server
    00743         ShowNetworkStartServerWindow();
    00744         break;
    00745
    00746       case NGWW_JOIN: // Join Game
    00747         if (this->server != NULL) {
    00748           snprintf(_settings_client.network.last_host, sizeof(_settings_client.network.last_host), "%s", this->server->address.GetHostname());
    00749           _settings_client.network.last_port = this->server->address.GetPort();
    00750           ShowNetworkLobbyWindow(this->server);
    00751         }
    00752         break;
    00753
    00754       case NGWW_REFRESH: // Refresh
    00755         if (this->server != NULL) NetworkUDPQueryServer(this->server->address);
    00756         break;
    00757
    00758       case NGWW_NEWGRF: // NewGRF Settings
    00759         if (this->server != NULL) ShowNewGRFSettings(false, false, false, &this->server->info.grfconfig);
    00760         break;
    00761     }
    00762   }
    00763
    00764   virtual void OnDropdownSelect(int widget, int index)
    00765   {
    00766     switch (widget) {
    00767       case NGWW_CONN_BTN:
    00768         _settings_client.network.lan_internet = index;
    00769         break;
    00770
    00771       default:
    00772         NOT_REACHED();
    00773     }
    00774
    00775     this->SetDirty();
    00776   }
    00777
    00778   virtual void OnMouseLoop()
    00779   {
    00780     if (this->field == NGWW_CLIENT) this->HandleEditBox(NGWW_CLIENT);
    00781   }
    00782
    00783   virtual void OnInvalidateData(int data)
    00784   {
    00785     switch (data) {
    00786       /* Remove the selection */
    00787       case 1:
    00788         this->server = NULL;
    00789         this->list_pos = SLP_INVALID;
    00790         break;
    00791
    00792       /* Reiterate the whole server list as we downloaded some files */
    00793       case 2:
    00794         for (NetworkGameList **iter = this->servers.Begin(); iter != this->servers.End(); iter++) {
    00795           NetworkGameList *item = *iter;
    00796           bool missing_grfs = false;
    00797           for (GRFConfig *c = item->info.grfconfig; c != NULL; c = c->next) {
    00798             if (c->status != GCS_NOT_FOUND) continue;
    00799
    00800             const GRFConfig *f = FindGRFConfig(c->grfid, c->md5sum);
    00801             if (f == NULL) {
    00802               missing_grfs = true;
    00803               continue;
    00804             }
    00805
    00806             c->filename  = f->filename;
    00807             c->name      = f->name;
    00808             c->info      = f->info;
    00809             c->status    = GCS_UNKNOWN;
    00810           }
    00811
    00812           if (!missing_grfs) item->info.compatible = item->info.version_compatible;
    00813         }
    00814         break;
    00815     }
    00816     this->servers.ForceRebuild();
    00817     this->SetDirty();
    00818   }
    00819
    00820   virtual EventState OnKeyPress(uint16 key, uint16 keycode)
    00821   {
    00822     EventState state = ES_NOT_HANDLED;
    00823
    00824     /* handle up, down, pageup, pagedown, home and end */
    00825     if (keycode == WKC_UP || keycode == WKC_DOWN || keycode == WKC_PAGEUP || keycode == WKC_PAGEDOWN || keycode == WKC_HOME || keycode == WKC_END) {
    00826       if (this->servers.Length() == 0) return ES_HANDLED;
    00827       switch (keycode) {
    00828         case WKC_UP:
    00829           /* scroll up by one */
    00830           if (this->server == NULL) return ES_HANDLED;
    00831           if (this->list_pos > 0) this->list_pos--;
    00832           break;
    00833         case WKC_DOWN:
    00834           /* scroll down by one */
    00835           if (this->server == NULL) return ES_HANDLED;
    00836           if (this->list_pos < this->servers.Length() - 1) this->list_pos++;
    00837           break;
    00838         case WKC_PAGEUP:
    00839           /* scroll up a page */
    00840           if (this->server == NULL) return ES_HANDLED;
    00841           this->list_pos = (this->list_pos < this->vscroll.GetCapacity()) ? 0 : this->list_pos - this->vscroll.GetCapacity();
    00842           break;
    00843         case WKC_PAGEDOWN:
    00844           /* scroll down a page */
    00845           if (this->server == NULL) return ES_HANDLED;
    00846           this->list_pos = min(this->list_pos + this->vscroll.GetCapacity(), (int)this->servers.Length() - 1);
    00847           break;
    00848         case WKC_HOME:
    00849           /* jump to beginning */
    00850           this->list_pos = 0;
    00851           break;
    00852         case WKC_END:
    00853           /* jump to end */
    00854           this->list_pos = this->servers.Length() - 1;
    00855           break;
    00856         default: break;
    00857       }
    00858
    00859       this->server = this->servers[this->list_pos];
    00860
    00861       /* scroll to the new server if it is outside the current range */
    00862       this->ScrollToSelectedServer();
    00863
    00864       /* redraw window */
    00865       this->SetDirty();
    00866       return ES_HANDLED;
    00867     }
    00868
    00869     if (this->field != NGWW_CLIENT) {
    00870       if (this->server != NULL) {
    00871         if (keycode == WKC_DELETE) { // Press 'delete' to remove servers
    00872           NetworkGameListRemoveItem(this->server);
    00873           this->server = NULL;
    00874           this->list_pos = SLP_INVALID;
    00875         }
    00876       }
    00877       return state;
    00878     }
    00879
    00880     if (this->HandleEditBoxKey(NGWW_CLIENT, key, keycode, state) == HEBR_CONFIRM) return state;
    00881
    00882     /* The name is only allowed when it starts with a letter! */
    00883     if (!StrEmpty(this->edit_str_buf) && this->edit_str_buf[0] != ' ') {
    00884       strecpy(_settings_client.network.client_name, this->edit_str_buf, lastof(_settings_client.network.client_name));
    00885     } else {
    00886       strecpy(_settings_client.network.client_name, "Player", lastof(_settings_client.network.client_name));
    00887     }
    00888     return state;
    00889   }
    00890
    00891   virtual void OnQueryTextFinished(char *str)
    00892   {
    00893     if (!StrEmpty(str)) NetworkAddServer(str);
    00894   }
    00895
    00896   virtual void OnResize()
    00897   {
    00898     this->vscroll.SetCapacityFromWidget(this, NGWW_MATRIX);
    00899     this->GetWidget<NWidgetCore>(NGWW_MATRIX)->widget_data = (this->vscroll.GetCapacity() << MAT_ROW_START) + (1 << MAT_COL_START);
    00900   }
    00901 };
    00902
    00903 Listing NetworkGameWindow::last_sorting = {false, 5};
    00904 GUIGameServerList::SortFunction * const NetworkGameWindow::sorter_funcs[] = {
    00905   &NGameNameSorter,
    00906   &NGameClientSorter,
    00907   &NGameMapSizeSorter,
    00908   &NGameDateSorter,
    00909   &NGameYearsSorter,
    00910   &NGameAllowedSorter
    00911 };
    00912
    00913 static NWidgetBase *MakeResizableHeader(int *biggest_index)
    00914 {
    00915   *biggest_index = max<int>(*biggest_index, NGWW_INFO);
    00916   return new NWidgetServerListHeader();
    00917 }
    00918
    00919 static const NWidgetPart _nested_network_game_widgets[] = {
    00920   /* TOP */
    00921   NWidget(NWID_HORIZONTAL),
    00922     NWidget(WWT_CLOSEBOX, COLOUR_LIGHT_BLUE),
    00923     NWidget(WWT_CAPTION, COLOUR_LIGHT_BLUE), SetDataTip(STR_NETWORK_SERVER_LIST_CAPTION, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS),
    00924   EndContainer(),
    00925   NWidget(WWT_PANEL, COLOUR_LIGHT_BLUE, NGWW_MAIN),
    00926     NWidget(NWID_VERTICAL), SetPIP(10, 7, 0),
    00927       NWidget(NWID_HORIZONTAL), SetPIP(10, 7, 10),
    00928         NWidget(WWT_TEXT, COLOUR_LIGHT_BLUE, NGWW_CONNECTION), SetDataTip(STR_NETWORK_SERVER_LIST_CONNECTION, STR_NULL),
    00929         NWidget(WWT_DROPDOWN, COLOUR_LIGHT_BLUE, NGWW_CONN_BTN),
    00930                     SetDataTip(STR_BLACK_STRING, STR_NETWORK_SERVER_LIST_CONNECTION_TOOLTIP),
    00931         NWidget(NWID_SPACER), SetFill(1, 0), SetResize(1, 0),
    00932         NWidget(WWT_TEXT, COLOUR_LIGHT_BLUE, NGWW_CLIENT_LABEL), SetDataTip(STR_NETWORK_SERVER_LIST_PLAYER_NAME, STR_NULL),
    00933         NWidget(WWT_EDITBOX, COLOUR_LIGHT_BLUE, NGWW_CLIENT), SetMinimalSize(151, 12),
    00934                     SetDataTip(STR_NETWORK_SERVER_LIST_PLAYER_NAME_OSKTITLE, STR_NETWORK_SERVER_LIST_ENTER_NAME_TOOLTIP),
    00935       EndContainer(),
    00936       NWidget(NWID_HORIZONTAL), SetPIP(10, 7, 10),
    00937         /* LEFT SIDE */
    00938         NWidget(NWID_VERTICAL),
    00939           NWidget(NWID_HORIZONTAL),
    00940             NWidget(NWID_VERTICAL),
    00941               NWidgetFunction(MakeResizableHeader),
    00942               NWidget(WWT_MATRIX, COLOUR_LIGHT_BLUE, NGWW_MATRIX), SetResize(1, 1), SetFill(1, 0),
    00943                         SetDataTip(0, STR_NETWORK_SERVER_LIST_CLICK_GAME_TO_SELECT),
    00944             EndContainer(),
    00945             NWidget(WWT_SCROLLBAR, COLOUR_LIGHT_BLUE, NGWW_SCROLLBAR),
    00946           EndContainer(),
    00947           NWidget(NWID_SPACER), SetMinimalSize(0, 7), SetResize(1, 0), SetFill(1, 1),
    00948           NWidget(WWT_TEXT, COLOUR_LIGHT_BLUE, NGWW_LASTJOINED_LABEL), SetFill(1, 0),
    00949                     SetDataTip(STR_NETWORK_SERVER_LIST_LAST_JOINED_SERVER, STR_NULL), SetResize(1, 0),
    00950           NWidget(NWID_HORIZONTAL), SetPIP(0, 0, WD_VSCROLLBAR_WIDTH),
    00951             NWidget(WWT_PANEL, COLOUR_LIGHT_BLUE, NGWW_LASTJOINED), SetFill(1, 0), SetResize(1, 0),
    00952                       SetDataTip(0x0, STR_NETWORK_SERVER_LIST_CLICK_TO_SELECT_LAST),
    00953             EndContainer(),
    00954           EndContainer(),
    00955         EndContainer(),
    00956         /* RIGHT SIDE */
    00957         NWidget(WWT_PANEL, COLOUR_LIGHT_BLUE, NGWW_DETAILS),
    00958           NWidget(NWID_VERTICAL, NC_EQUALSIZE), SetPIP(5, 5, 5),
    00959             NWidget(WWT_EMPTY, INVALID_COLOUR, NGWW_DETAILS_SPACER), SetMinimalSize(140, 155), SetResize(0, 1), SetFill(1, 1), // Make sure it's at least this wide
    00960             NWidget(NWID_HORIZONTAL, NC_EQUALSIZE), SetPIP(5, 5, 5),
    00961               NWidget(NWID_SPACER), SetFill(1, 0),
    00962               NWidget(NWID_SELECTION, INVALID_COLOUR, NGWW_NEWGRF_SEL),
    00963                 NWidget(WWT_PUSHTXTBTN, COLOUR_WHITE, NGWW_NEWGRF), SetFill(1, 0), SetDataTip(STR_INTRO_NEWGRF_SETTINGS, STR_NULL),
    00964                 NWidget(NWID_SPACER), SetFill(1, 0),
    00965               EndContainer(),
    00966             EndContainer(),
    00967             NWidget(NWID_HORIZONTAL, NC_EQUALSIZE), SetPIP(5, 5, 5),
    00968               NWidget(WWT_PUSHTXTBTN, COLOUR_WHITE, NGWW_JOIN), SetFill(1, 0), SetDataTip(STR_NETWORK_SERVER_LIST_JOIN_GAME, STR_NULL),
    00969               NWidget(WWT_PUSHTXTBTN, COLOUR_WHITE, NGWW_REFRESH), SetFill(1, 0), SetDataTip(STR_NETWORK_SERVER_LIST_REFRESH, STR_NETWORK_SERVER_LIST_REFRESH_TOOLTIP),
    00970             EndContainer(),
    00971           EndContainer(),
    00972         EndContainer(),
    00973       EndContainer(),
    00974       /* BOTTOM */
    00975       NWidget(NWID_HORIZONTAL),
    00976         NWidget(NWID_VERTICAL),
    00977           NWidget(NWID_HORIZONTAL, NC_EQUALSIZE), SetPIP(10, 7, 4),
    00978             NWidget(WWT_PUSHTXTBTN, COLOUR_WHITE, NGWW_FIND), SetResize(1, 0), SetFill(1, 0), SetDataTip(STR_NETWORK_SERVER_LIST_FIND_SERVER, STR_NETWORK_SERVER_LIST_FIND_SERVER_TOOLTIP),
    00979             NWidget(WWT_PUSHTXTBTN, COLOUR_WHITE, NGWW_ADD), SetResize(1, 0), SetFill(1, 0), SetDataTip(STR_NETWORK_SERVER_LIST_ADD_SERVER, STR_NETWORK_SERVER_LIST_ADD_SERVER_TOOLTIP),
    00980             NWidget(WWT_PUSHTXTBTN, COLOUR_WHITE, NGWW_START), SetResize(1, 0), SetFill(1, 0), SetDataTip(STR_NETWORK_SERVER_LIST_START_SERVER, STR_NETWORK_SERVER_LIST_START_SERVER_TOOLTIP),
    00981             NWidget(WWT_PUSHTXTBTN, COLOUR_WHITE, NGWW_CANCEL), SetResize(1, 0), SetFill(1, 0), SetDataTip(STR_BUTTON_CANCEL, STR_NULL),
    00982           EndContainer(),
    00983           NWidget(NWID_SPACER), SetMinimalSize(0, 6), SetResize(1, 0), SetFill(1, 0),
    00984         EndContainer(),
    00985         NWidget(NWID_VERTICAL),
    00986           NWidget(NWID_SPACER), SetFill(0, 1),
    00987           NWidget(WWT_RESIZEBOX, COLOUR_LIGHT_BLUE),
    00988         EndContainer(),
    00989       EndContainer(),
    00990     EndContainer(),
    00991   EndContainer(),
    00992 };
    00993
    00994 static const WindowDesc _network_game_window_desc(
    00995   WDP_CENTER, 1000, 730,
    00996   WC_NETWORK_WINDOW, WC_NONE,
    00997   WDF_UNCLICK_BUTTONS,
    00998   _nested_network_game_widgets, lengthof(_nested_network_game_widgets)
    00999 );
    01000
    01001 void ShowNetworkGameWindow()
    01002 {
    01003   static bool first = true;
    01004   DeleteWindowById(WC_NETWORK_WINDOW, 0);
    01005
    01006   /* Only show once */
    01007   if (first) {
    01008     first = false;
    01009     /* add all servers from the config file to our list */
    01010     for (char **iter = _network_host_list.Begin(); iter != _network_host_list.End(); iter++) {
    01011       NetworkAddServer(*iter);
    01012     }
    01013   }
    01014
    01015   new NetworkGameWindow(&_network_game_window_desc);
    01016 }
    01017
    01019 enum NetworkStartServerWidgets {
    01020   NSSW_BACKGROUND,
    01021   NSSW_GAMENAME_LABEL,
    01022   NSSW_GAMENAME,
    01023   NSSW_SETPWD,
    01024   NSSW_SELECT_MAP_LABEL,
    01025   NSSW_SELMAP,
    01026   NSSW_SCROLLBAR,
    01027   NSSW_CONNTYPE_LABEL,
    01028   NSSW_CONNTYPE_BTN,
    01029   NSSW_CLIENTS_LABEL,
    01030   NSSW_CLIENTS_BTND,
    01031   NSSW_CLIENTS_TXT,
    01032   NSSW_CLIENTS_BTNU,
    01033   NSSW_COMPANIES_LABEL,
    01034   NSSW_COMPANIES_BTND,
    01035   NSSW_COMPANIES_TXT,
    01036   NSSW_COMPANIES_BTNU,
    01037   NSSW_SPECTATORS_LABEL,
    01038   NSSW_SPECTATORS_BTND,
    01039   NSSW_SPECTATORS_TXT,
    01040   NSSW_SPECTATORS_BTNU,
    01041
    01042   NSSW_LANGUAGE_LABEL,
    01043   NSSW_LANGUAGE_BTN,
    01044   NSSW_START,
    01045   NSSW_LOAD,
    01046   NSSW_CANCEL,
    01047 };
    01048
    01049 struct NetworkStartServerWindow : public QueryStringBaseWindow {
    01050   byte field;
    01051   FiosItem *map;
    01052   byte widget_id;
    01053
    01054   NetworkStartServerWindow(const WindowDesc *desc) : QueryStringBaseWindow(NETWORK_NAME_LENGTH)
    01055   {
    01056     this->InitNested(desc, 0);
    01057
    01058     ttd_strlcpy(this->edit_str_buf, _settings_client.network.server_name, this->edit_str_size);
    01059
    01060     _saveload_mode = SLD_NEW_GAME;
    01061     BuildFileList();
    01062     this->vscroll.SetCapacity(14);
    01063     this->vscroll.SetCount(_fios_items.Length() + 1);
    01064
    01065     this->afilter = CS_ALPHANUMERAL;
    01066     InitializeTextBuffer(&this->text, this->edit_str_buf, this->edit_str_size, 160);
    01067     this->SetFocusedWidget(NSSW_GAMENAME);
    01068
    01069     this->field = NSSW_GAMENAME;
    01070   }
    01071
    01072   virtual void SetStringParameters(int widget) const
    01073   {
    01074     switch (widget) {
    01075       case NSSW_CONNTYPE_BTN:
    01076         SetDParam(0, _connection_types_dropdown[_settings_client.network.server_advertise]);
    01077         break;
    01078
    01079       case NSSW_CLIENTS_TXT:
    01080         SetDParam(0, _settings_client.network.max_clients);
    01081         break;
    01082
    01083       case NSSW_COMPANIES_TXT:
    01084         SetDParam(0, _settings_client.network.max_companies);
    01085         break;
    01086
    01087       case NSSW_SPECTATORS_TXT:
    01088         SetDParam(0, _settings_client.network.max_spectators);
    01089         break;
    01090
    01091       case NSSW_LANGUAGE_BTN:
    01092         SetDParam(0, STR_NETWORK_LANG_ANY + _settings_client.network.server_lang);
    01093         break;
    01094     }
    01095   }
    01096
    01097   virtual void UpdateWidgetSize(int widget, Dimension *size, const Dimension &padding, Dimension *fill, Dimension *resize)
    01098   {
    01099     switch (widget) {
    01100       case NSSW_CONNTYPE_BTN:
    01101         *size = maxdim(GetStringBoundingBox(_connection_types_dropdown[0]), GetStringBoundingBox(_connection_types_dropdown[1]));
    01102         size->width += padding.width;
    01103         size->height += padding.height;
    01104         break;
    01105
    01106       case NSSW_SELMAP:
    01107         resize->height = FONT_HEIGHT_NORMAL;
    01108         size->height = 14 * resize->height + WD_FRAMERECT_TOP + WD_FRAMERECT_BOTTOM;
    01109         break;
    01110     }
    01111   }
    01112
    01113   virtual void DrawWidget(const Rect &r, int widget) const
    01114   {
    01115     switch (widget) {
    01116       case NSSW_SELMAP:
    01117         this->DrawMapSelection(r);
    01118         break;
    01119
    01120       case NSSW_SETPWD:
    01121         /* if password is set, draw red '*' next to 'Set password' button */
    01122         if (!StrEmpty(_settings_client.network.server_password)) DrawString(r.right + WD_FRAMERECT_LEFT, this->width - WD_FRAMERECT_RIGHT, r.top, "*", TC_RED);
    01123     }
    01124   }
    01125
    01126   virtual void OnPaint()
    01127   {
    01128     /* draw basic widgets */
    01129     this->DrawWidgets();
    01130
    01131     /* editbox to set game name */
    01132     this->DrawEditBox(NSSW_GAMENAME);
    01133   }
    01134
    01135   void DrawMapSelection(const Rect &r) const
    01136   {
    01137     int y = r.top + WD_FRAMERECT_TOP;
    01138     /* draw list of maps */
    01139     GfxFillRect(r.left + 1, r.top + 1, r.right - 1, r.bottom - 1, 0xD7);  // black background of maps list
    01140
    01141     for (uint pos = this->vscroll.GetPosition(); pos < _fios_items.Length() + 1; pos++) {
    01142       const FiosItem *item = (pos == 0) ? NULL : _fios_items.Get(pos - 1);
    01143       if (item == this->map) { // this->map == NULL for first item
    01144         GfxFillRect(r.left + 1, y, r.right - 1, y + FONT_HEIGHT_NORMAL - 1, 155); // show highlighted item with a different colour
    01145       }
    01146
    01147       if (pos == 0) {
    01148         DrawString(r.left + WD_FRAMERECT_LEFT, r.right - WD_FRAMERECT_RIGHT, y, STR_NETWORK_START_SERVER_SERVER_RANDOM_GAME, TC_DARK_GREEN);
    01149       } else {
    01150         DrawString(r.left + WD_FRAMERECT_LEFT, r.right - WD_FRAMERECT_RIGHT, y, item->title, _fios_colours[item->type] );
    01151       }
    01152       y += FONT_HEIGHT_NORMAL;
    01153
    01154       if (y >= this->vscroll.GetCapacity() * FONT_HEIGHT_NORMAL + r.top) break;
    01155     }
    01156   }
    01157
    01158   virtual void OnClick(Point pt, int widget, int click_count)
    01159   {
    01160     this->field = widget;
    01161     switch (widget) {
    01162       case NSSW_CANCEL: // Cancel button
    01163         ShowNetworkGameWindow();
    01164         break;
    01165
    01166       case NSSW_SETPWD: // Set password button
    01167         this->widget_id = NSSW_SETPWD;
    01168         SetDParamStr(0, _settings_client.network.server_password);
    01169         ShowQueryString(STR_JUST_RAW_STRING, STR_NETWORK_START_SERVER_SET_PASSWORD, 20, 250, this, CS_ALPHANUMERAL, QSF_NONE);
    01170         break;
    01171
    01172       case NSSW_SELMAP: { // Select map
    01173         int y = (pt.y - this->GetWidget<NWidgetBase>(NSSW_SELMAP)->pos_y - WD_FRAMERECT_TOP) / FONT_HEIGHT_NORMAL;
    01174
    01175         y += this->vscroll.GetPosition();
    01176         if (y >= this->vscroll.GetCount()) return;
    01177
    01178         this->map = (y == 0) ? NULL : _fios_items.Get(y - 1);
    01179         this->SetDirty();
    01180       } break;
    01181
    01182       case NSSW_CONNTYPE_BTN: // Connection type
    01183         ShowDropDownMenu(this, _connection_types_dropdown, _settings_client.network.server_advertise, NSSW_CONNTYPE_BTN, 0, 0); // do it for widget NSSW_CONNTYPE_BTN
    01184         break;
    01185
    01186       case NSSW_CLIENTS_BTND:    case NSSW_CLIENTS_BTNU:    // Click on up/down button for number of clients
    01187       case NSSW_COMPANIES_BTND:  case NSSW_COMPANIES_BTNU:  // Click on up/down button for number of companies
    01188       case NSSW_SPECTATORS_BTND: case NSSW_SPECTATORS_BTNU: // Click on up/down button for number of spectators
    01189         /* Don't allow too fast scrolling */
    01190         if ((this->flags4 & WF_TIMEOUT_MASK) <= WF_TIMEOUT_TRIGGER) {
    01191           this->HandleButtonClick(widget);
    01192           this->SetDirty();
    01193           switch (widget) {
    01194             default: NOT_REACHED();
    01195             case NSSW_CLIENTS_BTND: case NSSW_CLIENTS_BTNU:
    01196               _settings_client.network.max_clients    = Clamp(_settings_client.network.max_clients    + widget - NSSW_CLIENTS_TXT,    2, MAX_CLIENTS);
    01197               break;
    01198             case NSSW_COMPANIES_BTND: case NSSW_COMPANIES_BTNU:
    01199               _settings_client.network.max_companies  = Clamp(_settings_client.network.max_companies  + widget - NSSW_COMPANIES_TXT,  1, MAX_COMPANIES);
    01200               break;
    01201             case NSSW_SPECTATORS_BTND: case NSSW_SPECTATORS_BTNU:
    01202               _settings_client.network.max_spectators = Clamp(_settings_client.network.max_spectators + widget - NSSW_SPECTATORS_TXT, 0, MAX_CLIENTS);
    01203               break;
    01204           }
    01205         }
    01206         _left_button_clicked = false;
    01207         break;
    01208
    01209       case NSSW_CLIENTS_TXT:    // Click on number of clients
    01210         this->widget_id = NSSW_CLIENTS_TXT;
    01211         SetDParam(0, _settings_client.network.max_clients);
    01212         ShowQueryString(STR_JUST_INT, STR_NETWORK_START_SERVER_NUMBER_OF_CLIENTS,    4, 50, this, CS_NUMERAL, QSF_NONE);
    01213         break;
    01214
    01215       case NSSW_COMPANIES_TXT:  // Click on number of companies
    01216         this->widget_id = NSSW_COMPANIES_TXT;
    01217         SetDParam(0, _settings_client.network.max_companies);
    01218         ShowQueryString(STR_JUST_INT, STR_NETWORK_START_SERVER_NUMBER_OF_COMPANIES,  3, 50, this, CS_NUMERAL, QSF_NONE);
    01219         break;
    01220
    01221       case NSSW_SPECTATORS_TXT: // Click on number of spectators
    01222         this->widget_id = NSSW_SPECTATORS_TXT;
    01223         SetDParam(0, _settings_client.network.max_spectators);
    01224         ShowQueryString(STR_JUST_INT, STR_NETWORK_START_SERVER_NUMBER_OF_SPECTATORS, 4, 50, this, CS_NUMERAL, QSF_NONE);
    01225         break;
    01226
    01227       case NSSW_LANGUAGE_BTN: { // Language
    01228         uint sel = 0;
    01229         for (uint i = 0; i < lengthof(_language_dropdown) - 1; i++) {
    01230           if (_language_dropdown[i] == STR_NETWORK_LANG_ANY + _settings_client.network.server_lang) {
    01231             sel = i;
    01232             break;
    01233           }
    01234         }
    01235         ShowDropDownMenu(this, _language_dropdown, sel, NSSW_LANGUAGE_BTN, 0, 0);
    01236       } break;
    01237
    01238       case NSSW_START: // Start game
    01239         _is_network_server = true;
    01240
    01241         if (this->map == NULL) { // start random new game
    01242           ShowGenerateLandscape();
    01243         } else { // load a scenario
    01244           const char *name = FiosBrowseTo(this->map);
    01245           if (name != NULL) {
    01246             SetFiosType(this->map->type);
    01247             _file_to_saveload.filetype = FT_SCENARIO;
    01248             strecpy(_file_to_saveload.name, name, lastof(_file_to_saveload.name));
    01249             strecpy(_file_to_saveload.title, this->map->title, lastof(_file_to_saveload.title));
    01250
    01251             delete this;
    01252             SwitchToMode(SM_START_SCENARIO);
    01253           }
    01254         }
    01255         break;
    01256
    01257       case NSSW_LOAD: // Load game
    01258         _is_network_server = true;
    01259         /* XXX - WC_NETWORK_WINDOW (this window) should stay, but if it stays, it gets
    01260          * copied all the elements of 'load game' and upon closing that, it segfaults */
    01261         delete this;
    01262         ShowSaveLoadDialog(SLD_LOAD_GAME);
    01263         break;
    01264     }
    01265   }
    01266
    01267   virtual void OnDropdownSelect(int widget, int index)
    01268   {
    01269     switch (widget) {
    01270       case NSSW_CONNTYPE_BTN:
    01271         _settings_client.network.server_advertise = (index != 0);
    01272         break;
    01273       case NSSW_LANGUAGE_BTN:
    01274         _settings_client.network.server_lang = _language_dropdown[index] - STR_NETWORK_LANG_ANY;
    01275         break;
    01276       default:
    01277         NOT_REACHED();
    01278     }
    01279
    01280     this->SetDirty();
    01281   }
    01282
    01283   virtual void OnMouseLoop()
    01284   {
    01285     if (this->field == NSSW_GAMENAME) this->HandleEditBox(NSSW_GAMENAME);
    01286   }
    01287
    01288   virtual EventState OnKeyPress(uint16 key, uint16 keycode)
    01289   {
    01290     EventState state = ES_NOT_HANDLED;
    01291     if (this->field == NSSW_GAMENAME) {
    01292       if (this->HandleEditBoxKey(NSSW_GAMENAME, key, keycode, state) == HEBR_CONFIRM) return state;
    01293
    01294       strecpy(_settings_client.network.server_name, this->text.buf, lastof(_settings_client.network.server_name));
    01295     }
    01296
    01297     return state;
    01298   }
    01299
    01300   virtual void OnTimeout()
    01301   {
    01302     static const int raise_widgets[] = {NSSW_CLIENTS_BTND, NSSW_CLIENTS_BTNU, NSSW_COMPANIES_BTND, NSSW_COMPANIES_BTNU, NSSW_SPECTATORS_BTND, NSSW_SPECTATORS_BTNU, WIDGET_LIST_END};
    01303     for (const int *widget = raise_widgets; *widget != WIDGET_LIST_END; widget++) {
    01304       if (this->IsWidgetLowered(*widget)) {
    01305         this->RaiseWidget(*widget);
    01306         this->SetWidgetDirty(*widget);
    01307       }
    01308     }
    01309   }
    01310
    01311   virtual void OnQueryTextFinished(char *str)
    01312   {
    01313     if (str == NULL) return;
    01314
    01315     if (this->widget_id == NSSW_SETPWD) {
    01316       strecpy(_settings_client.network.server_password, str, lastof(_settings_client.network.server_password));
    01317     } else {
    01318       int32 value = atoi(str);
    01319       this->SetWidgetDirty(this->widget_id);
    01320       switch (this->widget_id) {
    01321         default: NOT_REACHED();
    01322         case NSSW_CLIENTS_TXT:    _settings_client.network.max_clients    = Clamp(value, 2, MAX_CLIENTS); break;
    01323         case NSSW_COMPANIES_TXT:  _settings_client.network.max_companies  = Clamp(value, 1, MAX_COMPANIES); break;
    01324         case NSSW_SPECTATORS_TXT: _settings_client.network.max_spectators = Clamp(value, 0, MAX_CLIENTS); break;
    01325       }
    01326     }
    01327
    01328     this->SetDirty();
    01329   }
    01330 };
    01331
    01332 static const NWidgetPart _nested_network_start_server_window_widgets[] = {
    01333   NWidget(NWID_HORIZONTAL),
    01334     NWidget(WWT_CLOSEBOX, COLOUR_LIGHT_BLUE),
    01335     NWidget(WWT_CAPTION, COLOUR_LIGHT_BLUE), SetDataTip(STR_NETWORK_START_SERVER_CAPTION, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS),
    01336   EndContainer(),
    01337   NWidget(WWT_PANEL, COLOUR_LIGHT_BLUE, NSSW_BACKGROUND),
    01338     NWidget(NWID_HORIZONTAL), SetPIP(10, 8, 10),
    01339       NWidget(NWID_VERTICAL), SetPIP(10, 0, 10),
    01340         /* Game name widgets */
    01341         NWidget(NWID_HORIZONTAL), SetPIP(0, 2, 0),
    01342           NWidget(WWT_TEXT, COLOUR_LIGHT_BLUE, NSSW_GAMENAME_LABEL), SetDataTip(STR_NETWORK_START_SERVER_NEW_GAME_NAME, STR_NULL),
    01343           NWidget(WWT_EDITBOX, COLOUR_LIGHT_BLUE, NSSW_GAMENAME), SetMinimalSize(10, 12), SetFill(1, 0),
    01344                             SetDataTip(STR_NETWORK_START_SERVER_NEW_GAME_NAME_OSKTITLE, STR_NETWORK_START_SERVER_NEW_GAME_NAME_TOOLTIP),
    01345         EndContainer(),
    01346         /* List of playable scenarios. */
    01347         NWidget(NWID_SPACER), SetMinimalSize(0, 8), SetFill(1, 0),
    01348         NWidget(WWT_TEXT, COLOUR_LIGHT_BLUE, NSSW_SELECT_MAP_LABEL), SetFill(1, 0), SetDataTip(STR_NETWORK_START_SERVER_SELECT_MAP, STR_NULL),
    01349         NWidget(NWID_SPACER), SetMinimalSize(0, 6), SetFill(1, 0),
    01350         NWidget(NWID_HORIZONTAL),
    01351           NWidget(WWT_PANEL, COLOUR_LIGHT_BLUE, NSSW_SELMAP), SetMinimalSize(250, 0), SetFill(1, 1), SetDataTip(STR_NULL, STR_NETWORK_START_SERVER_SELECT_MAP_TOOLTIP), EndContainer(),
    01352           NWidget(WWT_SCROLLBAR, COLOUR_LIGHT_BLUE, NSSW_SCROLLBAR),
    01353         EndContainer(),
    01354       EndContainer(),
    01355       NWidget(NWID_VERTICAL), SetPIP(10, 0, 10),
    01356         /* Password widgets. */
    01357         NWidget(WWT_PUSHTXTBTN, COLOUR_WHITE, NSSW_SETPWD), SetFill(1, 0),
    01358                             SetDataTip(STR_NETWORK_START_SERVER_SET_PASSWORD, STR_NETWORK_START_SERVER_PASSWORD_TOOLTIP),
    01359         /* Combo/selection boxes to control Connection Type / Max Clients / Max Companies / Max Observers / Language */
    01360         NWidget(NWID_SPACER), SetFill(1, 1),
    01361         NWidget(WWT_TEXT, COLOUR_LIGHT_BLUE, NSSW_CONNTYPE_LABEL), SetFill(1, 0), SetDataTip(STR_NETWORK_SERVER_LIST_CONNECTION, STR_NULL),
    01362         NWidget(NWID_SPACER), SetMinimalSize(0, 1),
    01363         NWidget(WWT_DROPDOWN, COLOUR_LIGHT_BLUE, NSSW_CONNTYPE_BTN), SetFill(1, 0),
    01364                         SetDataTip(STR_BLACK_STRING, STR_NETWORK_SERVER_LIST_CONNECTION_TOOLTIP),
    01365
    01366         NWidget(NWID_SPACER), SetMinimalSize(0, 6),
    01367         NWidget(WWT_TEXT, COLOUR_LIGHT_BLUE, NSSW_CLIENTS_LABEL), SetFill(1, 0), SetDataTip(STR_NETWORK_START_SERVER_NUMBER_OF_CLIENTS, STR_NULL),
    01368         NWidget(NWID_SPACER), SetMinimalSize(0, 1),
    01369         NWidget(NWID_HORIZONTAL),
    01370           NWidget(WWT_IMGBTN, COLOUR_LIGHT_BLUE, NSSW_CLIENTS_BTND), SetMinimalSize(12, 12), SetFill(0, 1),
    01371                         SetDataTip(SPR_ARROW_DOWN, STR_NETWORK_START_SERVER_NUMBER_OF_CLIENTS_TOOLTIP),
    01372           NWidget(WWT_PUSHTXTBTN, COLOUR_LIGHT_BLUE, NSSW_CLIENTS_TXT), SetFill(1, 0),
    01373                         SetDataTip(STR_NETWORK_START_SERVER_CLIENTS_SELECT, STR_NETWORK_START_SERVER_NUMBER_OF_CLIENTS_TOOLTIP),
    01374           NWidget(WWT_IMGBTN, COLOUR_LIGHT_BLUE, NSSW_CLIENTS_BTNU), SetMinimalSize(12, 12), SetFill(0, 1),
    01375                         SetDataTip(SPR_ARROW_UP, STR_NETWORK_START_SERVER_NUMBER_OF_CLIENTS_TOOLTIP),
    01376         EndContainer(),
    01377
    01378         NWidget(NWID_SPACER), SetMinimalSize(0, 6),
    01379         NWidget(WWT_TEXT, COLOUR_LIGHT_BLUE, NSSW_COMPANIES_LABEL), SetFill(1, 0), SetDataTip(STR_NETWORK_START_SERVER_NUMBER_OF_COMPANIES, STR_NULL),
    01380         NWidget(NWID_SPACER), SetMinimalSize(0, 1),
    01381         NWidget(NWID_HORIZONTAL),
    01382           NWidget(WWT_IMGBTN, COLOUR_LIGHT_BLUE, NSSW_COMPANIES_BTND), SetMinimalSize(12, 12), SetFill(0, 1),
    01383                         SetDataTip(SPR_ARROW_DOWN, STR_NETWORK_START_SERVER_NUMBER_OF_COMPANIES_TOOLTIP),
    01384           NWidget(WWT_PUSHTXTBTN, COLOUR_LIGHT_BLUE, NSSW_COMPANIES_TXT), SetFill(1, 0),
    01385                         SetDataTip(STR_NETWORK_START_SERVER_COMPANIES_SELECT, STR_NETWORK_START_SERVER_NUMBER_OF_COMPANIES_TOOLTIP),
    01386           NWidget(WWT_IMGBTN, COLOUR_LIGHT_BLUE, NSSW_COMPANIES_BTNU), SetMinimalSize(12, 12), SetFill(0, 1),
    01387                         SetDataTip(SPR_ARROW_UP, STR_NETWORK_START_SERVER_NUMBER_OF_COMPANIES_TOOLTIP),
    01388         EndContainer(),
    01389
    01390         NWidget(NWID_SPACER), SetMinimalSize(0, 6),
    01391         NWidget(WWT_TEXT, COLOUR_LIGHT_BLUE, NSSW_SPECTATORS_LABEL), SetFill(1, 0), SetDataTip(STR_NETWORK_START_SERVER_NUMBER_OF_SPECTATORS, STR_NULL),
    01392         NWidget(NWID_SPACER), SetMinimalSize(0, 1),
    01393         NWidget(NWID_HORIZONTAL),
    01394           NWidget(WWT_IMGBTN, COLOUR_LIGHT_BLUE, NSSW_SPECTATORS_BTND), SetMinimalSize(12, 12), SetFill(0, 1),
    01395                         SetDataTip(SPR_ARROW_DOWN, STR_NETWORK_START_SERVER_NUMBER_OF_SPECTATORS_TOOLTIP),
    01396           NWidget(WWT_PUSHTXTBTN, COLOUR_LIGHT_BLUE, NSSW_SPECTATORS_TXT), SetFill(1, 0),
    01397                         SetDataTip(STR_NETWORK_START_SERVER_SPECTATORS_SELECT, STR_NETWORK_START_SERVER_NUMBER_OF_SPECTATORS_TOOLTIP),
    01398           NWidget(WWT_IMGBTN, COLOUR_LIGHT_BLUE, NSSW_SPECTATORS_BTNU), SetMinimalSize(12, 12), SetFill(0, 1),
    01399                         SetDataTip(SPR_ARROW_UP, STR_NETWORK_START_SERVER_NUMBER_OF_SPECTATORS_TOOLTIP),
    01400         EndContainer(),
    01401
    01402         NWidget(NWID_SPACER), SetMinimalSize(0, 6),
    01403         NWidget(WWT_TEXT, COLOUR_LIGHT_BLUE, NSSW_LANGUAGE_LABEL), SetFill(1, 0), SetDataTip(STR_NETWORK_START_SERVER_LANGUAGE_SPOKEN, STR_NULL),
    01404         NWidget(NWID_SPACER), SetMinimalSize(0, 1),
    01405         NWidget(WWT_DROPDOWN, COLOUR_LIGHT_BLUE, NSSW_LANGUAGE_BTN), SetFill(1, 0),
    01406                         SetDataTip(STR_BLACK_STRING, STR_NETWORK_START_SERVER_LANGUAGE_TOOLTIP),
    01407       EndContainer(),
    01408     EndContainer(),
    01409     /* Buttons Start / Load / Cancel. */
    01410     NWidget(NWID_HORIZONTAL, NC_EQUALSIZE), SetPIP(10, 5, 10),
    01411       NWidget(WWT_PUSHTXTBTN, COLOUR_WHITE, NSSW_START), SetFill(1, 0), SetDataTip(STR_NETWORK_START_SERVER_START_GAME, STR_NETWORK_START_SERVER_START_GAME_TOOLTIP),
    01412       NWidget(WWT_PUSHTXTBTN, COLOUR_WHITE, NSSW_LOAD), SetFill(1, 0), SetDataTip(STR_NETWORK_START_SERVER_LOAD_GAME, STR_NETWORK_START_SERVER_LOAD_GAME_TOOLTIP),
    01413       NWidget(WWT_PUSHTXTBTN, COLOUR_WHITE, NSSW_CANCEL), SetFill(1, 0), SetDataTip(STR_BUTTON_CANCEL, STR_NULL),
    01414     EndContainer(),
    01415     NWidget(NWID_SPACER), SetMinimalSize(0, 10),
    01416   EndContainer(),
    01417 };
    01418
    01419 static const WindowDesc _network_start_server_window_desc(
    01420   WDP_CENTER, 0, 0,
    01421   WC_NETWORK_WINDOW, WC_NONE,
    01422   WDF_UNCLICK_BUTTONS,
    01423   _nested_network_start_server_window_widgets, lengthof(_nested_network_start_server_window_widgets)
    01424 );
    01425
    01426 static void ShowNetworkStartServerWindow()
    01427 {
    01428   DeleteWindowById(WC_NETWORK_WINDOW, 0);
    01429
    01430   new NetworkStartServerWindow(&_network_start_server_window_desc);
    01431 }
    01432
    01434 enum NetworkLobbyWindowWidgets {
    01435   NLWW_BACKGROUND,
    01436   NLWW_TEXT,
    01437   NLWW_HEADER,
    01438   NLWW_MATRIX,
    01439   NLWW_SCROLLBAR,
    01440   NLWW_DETAILS,
    01441   NLWW_JOIN,
    01442   NLWW_NEW,
    01443   NLWW_SPECTATE,
    01444   NLWW_REFRESH,
    01445   NLWW_CANCEL,
    01446 };
    01447
    01448 struct NetworkLobbyWindow : public Window {
    01449   CompanyID company;
    01450   NetworkGameList *server;
    01451   NetworkCompanyInfo company_info[MAX_COMPANIES];
    01452
    01453   NetworkLobbyWindow(const WindowDesc *desc, NetworkGameList *ngl) :
    01454       Window(), company(INVALID_COMPANY), server(ngl)
    01455   {
    01456     this->InitNested(desc, 0);
    01457     this->OnResize();
    01458   }
    01459
    01460   CompanyID NetworkLobbyFindCompanyIndex(byte pos) const
    01461   {
    01462     /* Scroll through all this->company_info and get the 'pos' item that is not empty */
    01463     for (CompanyID i = COMPANY_FIRST; i < MAX_COMPANIES; i++) {
    01464       if (!StrEmpty(this->company_info[i].company_name)) {
    01465         if (pos-- == 0) return i;
    01466       }
    01467     }
    01468
    01469     return COMPANY_FIRST;
    01470   }
    01471
    01472   virtual void UpdateWidgetSize(int widget, Dimension *size, const Dimension &padding, Dimension *fill, Dimension *resize)
    01473   {
    01474     switch (widget) {
    01475       case NLWW_HEADER:
    01476         size->height = WD_MATRIX_TOP + FONT_HEIGHT_NORMAL + WD_MATRIX_BOTTOM;
    01477         break;
    01478
    01479       case NLWW_MATRIX:
    01480         resize->height = WD_MATRIX_TOP + FONT_HEIGHT_NORMAL + WD_MATRIX_BOTTOM;
    01481         size->height = 10 * resize->height;
    01482         break;
    01483
    01484       case NLWW_DETAILS:
    01485         size->height = 30 + 11 * FONT_HEIGHT_NORMAL;
    01486         break;
    01487     }
    01488   }
    01489
    01490   virtual void SetStringParameters(int widget) const
    01491   {
    01492     switch (widget) {
    01493       case NLWW_TEXT:
    01494         SetDParamStr(0, this->server->info.server_name);
    01495         break;
    01496     }
    01497   }
    01498
    01499   virtual void DrawWidget(const Rect &r, int widget) const
    01500   {
    01501     switch (widget) {
    01502       case NLWW_DETAILS:
    01503         this->DrawDetails(r);
    01504         break;
    01505
    01506       case NLWW_MATRIX:
    01507         this->DrawMatrix(r);
    01508         break;
    01509     }
    01510   }
    01511
    01512   virtual void OnPaint()
    01513   {
    01514     const NetworkGameInfo *gi = &this->server->info;
    01515
    01516     /* Join button is disabled when no company is selected and for AI companies*/
    01517     this->SetWidgetDisabledState(NLWW_JOIN, this->company == INVALID_COMPANY || GetLobbyCompanyInfo(this->company)->ai);
    01518     /* Cannot start new company if there are too many */
    01519     this->SetWidgetDisabledState(NLWW_NEW, gi->companies_on >= gi->companies_max);
    01520     /* Cannot spectate if there are too many spectators */
    01521     this->SetWidgetDisabledState(NLWW_SPECTATE, gi->spectators_on >= gi->spectators_max);
    01522
    01523     this->vscroll.SetCount(gi->companies_on);
    01524
    01525     /* Draw window widgets */
    01526     this->DrawWidgets();
    01527   }
    01528
    01529   void DrawMatrix(const Rect &r) const
    01530   {
    01531     bool rtl = _dynlang.text_dir == TD_RTL;
    01532     uint left = r.left + WD_FRAMERECT_LEFT;
    01533     uint right = r.right - WD_FRAMERECT_RIGHT;
    01534
    01535     uint text_left  = left + (rtl ? 20 : 0);
    01536     uint text_right = right - (rtl ? 0 : 20);
    01537     uint blob_left  = rtl ? left : right - 10;
    01538     uint lock_left  = rtl ? left + 10 : right - 20;
    01539
    01540     int y = r.top + WD_MATRIX_TOP;
    01541     /* Draw company list */
    01542     int pos = this->vscroll.GetPosition();
    01543     while (pos < this->server->info.companies_on) {
    01544       byte company = NetworkLobbyFindCompanyIndex(pos);
    01545       bool income = false;
    01546       if (this->company == company) {
    01547         GfxFillRect(r.left + 1, y - 2, r.right - 1, y + FONT_HEIGHT_NORMAL, 10); // show highlighted item with a different colour
    01548       }
    01549
    01550       DrawString(text_left, text_right, y, this->company_info[company].company_name, TC_BLACK);
    01551       if (this->company_info[company].use_password != 0) DrawSprite(SPR_LOCK, PAL_NONE, lock_left, y);
    01552
    01553       /* If the company's income was positive puts a green dot else a red dot */
    01554       if (this->company_info[company].income >= 0) income = true;
    01555       DrawSprite(SPR_BLOT, income ? PALETTE_TO_GREEN : PALETTE_TO_RED, blob_left, y + (FONT_HEIGHT_NORMAL - 10) / 2);
    01556
    01557       pos++;
    01558       y += this->resize.step_height;
    01559       if (pos >= this->vscroll.GetPosition() + this->vscroll.GetCapacity()) break;
    01560     }
    01561   }
    01562
    01563   void DrawDetails(const Rect &r) const
    01564   {
    01565     const int detail_height = 12 + FONT_HEIGHT_NORMAL + 12;
    01566     /* Draw info about selected company when it is selected in the left window */
    01567     GfxFillRect(r.left + 1, r.top + 1, r.right - 1, r.top + detail_height - 1, 157);
    01568     DrawString(r.left + WD_FRAMERECT_LEFT, r.right - WD_FRAMERECT_RIGHT, r.top + 12, STR_NETWORK_GAME_LOBBY_COMPANY_INFO, TC_FROMSTRING, SA_CENTER);
    01569
    01570     if (this->company == INVALID_COMPANY || StrEmpty(this->company_info[this->company].company_name)) return;
    01571
    01572     int y = r.top + detail_height + 4;
    01573     const NetworkGameInfo *gi = &this->server->info;
    01574
    01575     SetDParam(0, gi->clients_on);
    01576     SetDParam(1, gi->clients_max);
    01577     SetDParam(2, gi->companies_on);
    01578     SetDParam(3, gi->companies_max);
    01579     DrawString(r.left + WD_FRAMERECT_LEFT, r.right - WD_FRAMERECT_RIGHT, y, STR_NETWORK_SERVER_LIST_CLIENTS);
    01580     y += FONT_HEIGHT_NORMAL;
    01581
    01582     SetDParamStr(0, this->company_info[this->company].company_name);
    01583     DrawString(r.left + WD_FRAMERECT_LEFT, r.right - WD_FRAMERECT_RIGHT, y, STR_NETWORK_GAME_LOBBY_COMPANY_NAME);
    01584     y += FONT_HEIGHT_NORMAL;
    01585
    01586     SetDParam(0, this->company_info[this->company].inaugurated_year);
    01587     DrawString(r.left + WD_FRAMERECT_LEFT, r.right - WD_FRAMERECT_RIGHT, y, STR_NETWORK_GAME_LOBBY_INAUGURATION_YEAR); // inauguration year
    01588     y += FONT_HEIGHT_NORMAL;
    01589
    01590     SetDParam(0, this->company_info[this->company].company_value);
    01591     DrawString(r.left + WD_FRAMERECT_LEFT, r.right - WD_FRAMERECT_RIGHT, y, STR_NETWORK_GAME_LOBBY_VALUE); // company value
    01592     y += FONT_HEIGHT_NORMAL;
    01593
    01594     SetDParam(0, this->company_info[this->company].money);
    01595     DrawString(r.left + WD_FRAMERECT_LEFT, r.right - WD_FRAMERECT_RIGHT, y, STR_NETWORK_GAME_LOBBY_CURRENT_BALANCE); // current balance
    01596     y += FONT_HEIGHT_NORMAL;
    01597
    01598     SetDParam(0, this->company_info[this->company].income);
    01599     DrawString(r.left + WD_FRAMERECT_LEFT, r.right - WD_FRAMERECT_RIGHT, y, STR_NETWORK_GAME_LOBBY_LAST_YEARS_INCOME); // last year's income
    01600     y += FONT_HEIGHT_NORMAL;
    01601
    01602     SetDParam(0, this->company_info[this->company].performance);
    01603     DrawString(r.left + WD_FRAMERECT_LEFT, r.right - WD_FRAMERECT_RIGHT, y, STR_NETWORK_GAME_LOBBY_PERFORMANCE); // performance
    01604     y += FONT_HEIGHT_NORMAL;
    01605
    01606     for (uint i = 0; i < lengthof(this->company_info[this->company].num_vehicle); i++) {
    01607       SetDParam(i, this->company_info[this->company].num_vehicle[i]);
    01608     }
    01609     DrawString(r.left + WD_FRAMERECT_LEFT, r.right - WD_FRAMERECT_RIGHT, y, STR_NETWORK_GAME_LOBBY_VEHICLES); // vehicles
    01610     y += FONT_HEIGHT_NORMAL;
    01611
    01612     for (uint i = 0; i < lengthof(this->company_info[this->company].num_station); i++) {
    01613       SetDParam(i, this->company_info[this->company].num_station[i]);
    01614     }
    01615     DrawString(r.left + WD_FRAMERECT_LEFT, r.right - WD_FRAMERECT_RIGHT, y, STR_NETWORK_GAME_LOBBY_STATIONS); // stations
    01616     y += FONT_HEIGHT_NORMAL;
    01617
    01618     SetDParamStr(0, this->company_info[this->company].clients);
    01619     DrawString(r.left + WD_FRAMERECT_LEFT, r.right - WD_FRAMERECT_RIGHT, y, STR_NETWORK_GAME_LOBBY_PLAYERS); // players
    01620   }
    01621
    01622   virtual void OnClick(Point pt, int widget, int click_count)
    01623   {
    01624     switch (widget) {
    01625       case NLWW_CANCEL:   // Cancel button
    01626         ShowNetworkGameWindow();
    01627         break;
    01628
    01629       case NLWW_MATRIX: { // Company list
    01630         uint32 id_v = (pt.y - this->GetWidget<NWidgetBase>(NLWW_MATRIX)->pos_y) / this->resize.step_height;
    01631
    01632         if (id_v >= this->vscroll.GetCapacity()) break;
    01633
    01634         id_v += this->vscroll.GetPosition();
    01635         this->company = (id_v >= this->server->info.companies_on) ? INVALID_COMPANY : NetworkLobbyFindCompanyIndex(id_v);
    01636         this->SetDirty();
    01637
    01638         /* FIXME the disabling should go into some InvalidateData, which is called instead of the SetDirty */
    01639         if (click_count > 1 && !this->IsWidgetDisabled(NLWW_JOIN)) this->OnClick(pt, NLWW_JOIN, 1);
    01640       } break;
    01641
    01642       case NLWW_JOIN:     // Join company
    01643         /* Button can be clicked only when it is enabled */
    01644         NetworkClientConnectGame(NetworkAddress(_settings_client.network.last_host, _settings_client.network.last_port), this->company);
    01645         break;
    01646
    01647       case NLWW_NEW:      // New company
    01648         NetworkClientConnectGame(NetworkAddress(_settings_client.network.last_host, _settings_client.network.last_port), COMPANY_NEW_COMPANY);
    01649         break;
    01650
    01651       case NLWW_SPECTATE: // Spectate game
    01652         NetworkClientConnectGame(NetworkAddress(_settings_client.network.last_host, _settings_client.network.last_port), COMPANY_SPECTATOR);
    01653         break;
    01654
    01655       case NLWW_REFRESH:  // Refresh
    01656         NetworkTCPQueryServer(NetworkAddress(_settings_client.network.last_host, _settings_client.network.last_port)); // company info
    01657         NetworkUDPQueryServer(NetworkAddress(_settings_client.network.last_host, _settings_client.network.last_port)); // general data
    01658         /* Clear the information so removed companies don't remain */
    01659         memset(this->company_info, 0, sizeof(this->company_info));
    01660         break;
    01661     }
    01662   }
    01663
    01664   virtual void OnResize()
    01665   {
    01666     this->vscroll.SetCapacityFromWidget(this, NLWW_MATRIX);
    01667     this->GetWidget<NWidgetCore>(NLWW_MATRIX)->widget_data = (this->vscroll.GetCapacity() << MAT_ROW_START) + (1 << MAT_COL_START);
    01668   }
    01669 };
    01670
    01671 static const NWidgetPart _nested_network_lobby_window_widgets[] = {
    01672   NWidget(NWID_HORIZONTAL),
    01673     NWidget(WWT_CLOSEBOX, COLOUR_LIGHT_BLUE),
    01674     NWidget(WWT_CAPTION, COLOUR_LIGHT_BLUE), SetDataTip(STR_NETWORK_GAME_LOBBY_CAPTION, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS),
    01675   EndContainer(),
    01676   NWidget(WWT_PANEL, COLOUR_LIGHT_BLUE, NLWW_BACKGROUND),
    01677     NWidget(WWT_TEXT, COLOUR_LIGHT_BLUE, NLWW_TEXT), SetDataTip(STR_NETWORK_GAME_LOBBY_PREPARE_TO_JOIN, STR_NULL), SetResize(1, 0), SetPadding(10, 10, 0, 10),
    01678     NWidget(NWID_SPACER), SetMinimalSize(0, 3),
    01679     NWidget(NWID_HORIZONTAL), SetPIP(10, 0, 10),
    01680       /* Company list. */
    01681       NWidget(NWID_VERTICAL),
    01682         NWidget(WWT_PANEL, COLOUR_WHITE, NLWW_HEADER), SetMinimalSize(146, 0), SetResize(1, 0), SetFill(1, 0), EndContainer(),
    01683         NWidget(WWT_MATRIX, COLOUR_LIGHT_BLUE, NLWW_MATRIX), SetMinimalSize(146, 0), SetResize(1, 1), SetFill(1, 1), SetDataTip(0, STR_NETWORK_GAME_LOBBY_COMPANY_LIST_TOOLTIP),
    01684       EndContainer(),
    01685       NWidget(WWT_SCROLLBAR, COLOUR_LIGHT_BLUE, NLWW_SCROLLBAR),
    01686       NWidget(NWID_SPACER), SetMinimalSize(5, 0), SetResize(0, 1),
    01687       /* Company info. */
    01688       NWidget(WWT_PANEL, COLOUR_LIGHT_BLUE, NLWW_DETAILS), SetMinimalSize(232, 0), SetResize(1, 1), SetFill(1, 1), EndContainer(),
    01689     EndContainer(),
    01690     NWidget(NWID_SPACER), SetMinimalSize(0, 9),
    01691     /* Buttons. */
    01692     NWidget(NWID_HORIZONTAL, NC_EQUALSIZE), SetPIP(10, 3, 10),
    01693       NWidget(NWID_VERTICAL, NC_EQUALSIZE), SetPIP(0, 3, 0),
    01694         NWidget(WWT_PUSHTXTBTN, COLOUR_WHITE, NLWW_JOIN), SetResize(1, 0), SetFill(1, 0), SetDataTip(STR_NETWORK_GAME_LOBBY_JOIN_COMPANY, STR_NETWORK_GAME_LOBBY_JOIN_COMPANY_TOOLTIP),
    01695         NWidget(WWT_PUSHTXTBTN, COLOUR_WHITE, NLWW_NEW), SetResize(1, 0), SetFill(1, 0), SetDataTip(STR_NETWORK_GAME_LOBBY_NEW_COMPANY, STR_NETWORK_GAME_LOBBY_NEW_COMPANY_TOOLTIP),
    01696       EndContainer(),
    01697       NWidget(NWID_VERTICAL, NC_EQUALSIZE), SetPIP(0, 3, 0),
    01698         NWidget(WWT_PUSHTXTBTN, COLOUR_WHITE, NLWW_SPECTATE), SetResize(1, 0), SetFill(1, 0), SetDataTip(STR_NETWORK_GAME_LOBBY_SPECTATE_GAME, STR_NETWORK_GAME_LOBBY_SPECTATE_GAME_TOOLTIP),
    01699         NWidget(WWT_PUSHTXTBTN, COLOUR_WHITE, NLWW_REFRESH), SetResize(1, 0), SetFill(1, 0), SetDataTip(STR_NETWORK_SERVER_LIST_REFRESH, STR_NETWORK_SERVER_LIST_REFRESH_TOOLTIP),
    01700       EndContainer(),
    01701       NWidget(NWID_VERTICAL, NC_EQUALSIZE), SetPIP(0, 3, 0),
    01702         NWidget(WWT_PUSHTXTBTN, COLOUR_WHITE, NLWW_CANCEL), SetResize(1, 0), SetFill(1, 0), SetDataTip(STR_BUTTON_CANCEL, STR_NULL),
    01703         NWidget(NWID_SPACER), SetFill(1, 1),
    01704       EndContainer(),
    01705     EndContainer(),
    01706     NWidget(NWID_SPACER), SetMinimalSize(0, 8),
    01707   EndContainer(),
    01708 };
    01709
    01710 static const WindowDesc _network_lobby_window_desc(
    01711   WDP_CENTER, 0, 0,
    01712   WC_NETWORK_WINDOW, WC_NONE,
    01713   WDF_UNCLICK_BUTTONS,
    01714   _nested_network_lobby_window_widgets, lengthof(_nested_network_lobby_window_widgets)
    01715 );
    01716
    01717 /* Show the networklobbywindow with the selected server
    01718  * @param ngl Selected game pointer which is passed to the new window */
    01719 static void ShowNetworkLobbyWindow(NetworkGameList *ngl)
    01720 {
    01721   DeleteWindowById(WC_NETWORK_WINDOW, 0);
    01722
    01723   NetworkTCPQueryServer(NetworkAddress(_settings_client.network.last_host, _settings_client.network.last_port)); // company info
    01724   NetworkUDPQueryServer(NetworkAddress(_settings_client.network.last_host, _settings_client.network.last_port)); // general data
    01725
    01726   new NetworkLobbyWindow(&_network_lobby_window_desc, ngl);
    01727 }
    01728
    01734 NetworkCompanyInfo *GetLobbyCompanyInfo(CompanyID company)
    01735 {
    01736   NetworkLobbyWindow *lobby = dynamic_cast<NetworkLobbyWindow*>(FindWindowById(WC_NETWORK_WINDOW, 0));
    01737   return (lobby != NULL && company < MAX_COMPANIES) ? &lobby->company_info[company] : NULL;
    01738 }
    01739
    01740 /* The window below gives information about the connected clients
    01741  *  and also makes able to give money to them, kick them (if server)
    01742  *  and stuff like that. */
    01743
    01744 extern void DrawCompanyIcon(CompanyID cid, int x, int y);
    01745
    01746 /* Every action must be of this form */
    01747 typedef void ClientList_Action_Proc(byte client_no);
    01748
    01749 static const NWidgetPart _nested_client_list_popup_widgets[] = {
    01750   NWidget(WWT_PANEL, COLOUR_GREY, 0), EndContainer(),
    01751 };
    01752
    01753 static const WindowDesc _client_list_popup_desc(
    01754   WDP_AUTO, 0, 0,
    01755   WC_TOOLBAR_MENU, WC_CLIENT_LIST,
    01756   0,
    01757   _nested_client_list_popup_widgets, lengthof(_nested_client_list_popup_widgets)
    01758 );
    01759
    01760 /* Finds the Xth client-info that is active */
    01761 static NetworkClientInfo *NetworkFindClientInfo(byte client_no)
    01762 {
    01763   NetworkClientInfo *ci;
    01764
    01765   FOR_ALL_CLIENT_INFOS(ci) {
    01766     if (client_no == 0) return ci;
    01767     client_no--;
    01768   }
    01769
    01770   return NULL;
    01771 }
    01772
    01773 /* Here we start to define the options out of the menu */
    01774 static void ClientList_Kick(byte client_no)
    01775 {
    01776   const NetworkClientInfo *ci = NetworkFindClientInfo(client_no);
    01777
    01778   if (ci == NULL) return;
    01779
    01780   NetworkServerKickClient(ci->client_id);
    01781 }
    01782
    01783 static void ClientList_Ban(byte client_no)
    01784 {
    01785   NetworkClientInfo *ci = NetworkFindClientInfo(client_no);
    01786
    01787   if (ci == NULL) return;
    01788
    01789   NetworkServerKickOrBanIP(GetClientIP(ci), true);
    01790 }
    01791
    01792 static void ClientList_GiveMoney(byte client_no)
    01793 {
    01794   if (NetworkFindClientInfo(client_no) != NULL) {
    01795     ShowNetworkGiveMoneyWindow(NetworkFindClientInfo(client_no)->client_playas);
    01796   }
    01797 }
    01798
    01799 static void ClientList_SpeakToClient(byte client_no)
    01800 {
    01801   if (NetworkFindClientInfo(client_no) != NULL) {
    01802     ShowNetworkChatQueryWindow(DESTTYPE_CLIENT, NetworkFindClientInfo(client_no)->client_id);
    01803   }
    01804 }
    01805
    01806 static void ClientList_SpeakToCompany(byte client_no)
    01807 {
    01808   if (NetworkFindClientInfo(client_no) != NULL) {
    01809     ShowNetworkChatQueryWindow(DESTTYPE_TEAM, NetworkFindClientInfo(client_no)->client_playas);
    01810   }
    01811 }
    01812
    01813 static void ClientList_SpeakToAll(byte client_no)
    01814 {
    01815   ShowNetworkChatQueryWindow(DESTTYPE_BROADCAST, 0);
    01816 }
    01817
    01819 struct NetworkClientListPopupWindow : Window {
    01821   struct ClientListAction {
    01822     StringID name;
    01823     ClientList_Action_Proc *proc;
    01824   };
    01825
    01826   uint sel_index;
    01827   int client_no;
    01828   Point desired_location;
    01829   SmallVector<ClientListAction, 2> actions;
    01830
    01836   inline void AddAction(StringID name, ClientList_Action_Proc *proc)
    01837   {
    01838     ClientListAction *action = this->actions.Append();
    01839     action->name = name;
    01840     action->proc = proc;
    01841   }
    01842
    01843   NetworkClientListPopupWindow(const WindowDesc *desc, int x, int y, int client_no) :
    01844       Window(),
    01845       sel_index(0), client_no(client_no)
    01846   {
    01847     this->desired_location.x = x;
    01848     this->desired_location.y = y;
    01849
    01850     const NetworkClientInfo *ci = NetworkFindClientInfo(client_no);
    01851
    01852     if (_network_own_client_id != ci->client_id) {
    01853       this->AddAction(STR_NETWORK_CLIENTLIST_SPEAK_TO_CLIENT, &ClientList_SpeakToClient);
    01854     }
    01855
    01856     if (Company::IsValidID(ci->client_playas) || ci->client_playas == COMPANY_SPECTATOR) {
    01857       this->AddAction(STR_NETWORK_CLIENTLIST_SPEAK_TO_COMPANY, &ClientList_SpeakToCompany);
    01858     }
    01859     this->AddAction(STR_NETWORK_CLIENTLIST_SPEAK_TO_ALL, &ClientList_SpeakToAll);
    01860
    01861     if (_network_own_client_id != ci->client_id) {
    01862       /* We are no spectator and the company we want to give money to is no spectator and money gifts are allowed */
    01863       if (Company::IsValidID(_local_company) && Company::IsValidID(ci->client_playas) && _settings_game.economy.give_money) {
    01864         this->AddAction(STR_NETWORK_CLIENTLIST_GIVE_MONEY, &ClientList_GiveMoney);
    01865       }
    01866     }
    01867
    01868     /* A server can kick clients (but not himself) */
    01869     if (_network_server && _network_own_client_id != ci->client_id) {
    01870       this->AddAction(STR_NETWORK_CLIENTLIST_KICK, &ClientList_Kick);
    01871       this->AddAction(STR_NETWORK_CLIENTLIST_BAN, &ClientList_Ban);
    01872     }
    01873
    01874     this->flags4 &= ~WF_WHITE_BORDER_MASK;
    01875     this->InitNested(desc, 0);
    01876   }
    01877
    01878   virtual Point OnInitialPosition(const WindowDesc *desc, int16 sm_width, int16 sm_height, int window_number)
    01879   {
    01880     return this->desired_location;
    01881   }
    01882
    01883   virtual void UpdateWidgetSize(int widget, Dimension *size, const Dimension &padding, Dimension *fill, Dimension *resize)
    01884   {
    01885     Dimension d = *size;
    01886     for (const ClientListAction *action = this->actions.Begin(); action != this->actions.End(); action++) {
    01887       d = maxdim(GetStringBoundingBox(action->name), d);
    01888     }
    01889
    01890     d.height *= this->actions.Length();
    01891     d.width += WD_FRAMERECT_LEFT + WD_FRAMERECT_RIGHT;
    01892     d.height += WD_FRAMERECT_TOP + WD_FRAMERECT_BOTTOM;
    01893     *size = d;
    01894   }
    01895
    01896   virtual void DrawWidget(const Rect &r, int widget) const
    01897   {
    01898     /* Draw the actions */
    01899     int sel = this->sel_index;
    01900     int y = r.top + WD_FRAMERECT_TOP;
    01901     for (const ClientListAction *action = this->actions.Begin(); action != this->actions.End(); action++, y += FONT_HEIGHT_NORMAL) {
    01902       TextColour colour;
    01903       if (sel-- == 0) { // Selected item, highlight it
    01904         GfxFillRect(r.left + 1, y, r.right - 1, y + FONT_HEIGHT_NORMAL - 1, 0);
    01905         colour = TC_WHITE;
    01906       } else {
    01907         colour = TC_BLACK;
    01908       }
    01909
    01910       DrawString(r.left + WD_FRAMERECT_LEFT, r.right - WD_FRAMERECT_RIGHT, y, action->name, colour);
    01911     }
    01912   }
    01913
    01914   virtual void OnPaint()
    01915   {
    01916     this->DrawWidgets();
    01917   }
    01918
    01919   virtual void OnMouseLoop()
    01920   {
    01921     /* We selected an action */
    01922     uint index = (_cursor.pos.y - this->top - WD_FRAMERECT_TOP) / FONT_HEIGHT_NORMAL;
    01923
    01924     if (_left_button_down) {
    01925       if (index == this->sel_index || index >= this->actions.Length()) return;
    01926
    01927       this->sel_index = index;
    01928       this->SetDirty();
    01929     } else {
    01930       if (index < this->actions.Length() && _cursor.pos.y >= this->top) {
    01931         this->actions[index].proc(this->client_no);
    01932       }
    01933
    01934       DeleteWindowById(WC_TOOLBAR_MENU, 0);
    01935     }
    01936   }
    01937 };
    01938
    01942 static void PopupClientList(int client_no, int x, int y)
    01943 {
    01944   DeleteWindowById(WC_TOOLBAR_MENU, 0);
    01945
    01946   if (NetworkFindClientInfo(client_no) == NULL) return;
    01947
    01948   new NetworkClientListPopupWindow(&_client_list_popup_desc, x, y, client_no);
    01949 }
    01950
    01951
    01953 enum ClientListWidgets {
    01954   CLW_PANEL,
    01955 };
    01956
    01957 static const NWidgetPart _nested_client_list_widgets[] = {
    01958   NWidget(NWID_HORIZONTAL),
    01959     NWidget(WWT_CLOSEBOX, COLOUR_GREY),
    01960     NWidget(WWT_CAPTION, COLOUR_GREY), SetDataTip(STR_NETWORK_COMPANY_LIST_CLIENT_LIST, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS),
    01961     NWidget(WWT_STICKYBOX, COLOUR_GREY),
    01962   EndContainer(),
    01963   NWidget(WWT_PANEL, COLOUR_GREY, CLW_PANEL), SetMinimalSize(250, WD_FRAMERECT_TOP + WD_FRAMERECT_BOTTOM), SetResize(1, 1), EndContainer(),
    01964 };
    01965
    01966 static const WindowDesc _client_list_desc(
    01967   WDP_AUTO, 0, 0,
    01968   WC_CLIENT_LIST, WC_NONE,
    01969   0,
    01970   _nested_client_list_widgets, lengthof(_nested_client_list_widgets)
    01971 );
    01972
    01976 struct NetworkClientListWindow : Window {
    01977   int selected_item;
    01978
    01979   uint server_client_width;
    01980   uint company_icon_width;
    01981
    01982   NetworkClientListWindow(const WindowDesc *desc, WindowNumber window_number) :
    01983       Window(),
    01984       selected_item(-1)
    01985   {
    01986     this->InitNested(desc, window_number);
    01987   }
    01988
    01992   bool CheckClientListHeight()
    01993   {
    01994     int num = 0;
    01995     const NetworkClientInfo *ci;
    01996
    01997     /* Should be replaced with a loop through all clients */
    01998     FOR_ALL_CLIENT_INFOS(ci) {
    01999       if (ci->client_playas != COMPANY_INACTIVE_CLIENT) num++;
    02000     }
    02001
    02002     num *= FONT_HEIGHT_NORMAL;
    02003
    02004     int diff = (num + WD_FRAMERECT_TOP + WD_FRAMERECT_BOTTOM) - (this->GetWidget<NWidgetBase>(CLW_PANEL)->current_y);
    02005     /* If height is changed */
    02006     if (diff != 0) {
    02007       ResizeWindow(this, 0, diff);
    02008       return false;
    02009     }
    02010     return true;
    02011   }
    02012
    02013   virtual void UpdateWidgetSize(int widget, Dimension *size, const Dimension &padding, Dimension *fill, Dimension *resize)
    02014   {
    02015     if (widget != CLW_PANEL) return;
    02016
    02017     this->server_client_width = max(GetStringBoundingBox(STR_NETWORK_SERVER).width, GetStringBoundingBox(STR_NETWORK_CLIENT).width) + WD_FRAMERECT_RIGHT;
    02018     this->company_icon_width = GetSpriteSize(SPR_COMPANY_ICON).width + WD_FRAMERECT_LEFT;
    02019
    02020     uint width = 100; // Default width
    02021     const NetworkClientInfo *ci;
    02022     FOR_ALL_CLIENT_INFOS(ci) {
    02023       width = max(width, GetStringBoundingBox(ci->client_name).width);
    02024     }
    02025
    02026     size->width = WD_FRAMERECT_LEFT + this->server_client_width + this->company_icon_width + width + WD_FRAMERECT_RIGHT;
    02027   }
    02028
    02029   virtual void OnPaint()
    02030   {
    02031     /* Check if we need to reset the height */
    02032     if (!this->CheckClientListHeight()) return;
    02033
    02034     this->DrawWidgets();
    02035   }
    02036
    02037   virtual void DrawWidget(const Rect &r, int widget) const
    02038   {
    02039     if (widget != CLW_PANEL) return;
    02040
    02041     bool rtl = _dynlang.text_dir == TD_RTL;
    02042     int icon_y_offset = 1 + (FONT_HEIGHT_NORMAL - 10) / 2;
    02043     uint y = r.top + WD_FRAMERECT_TOP;
    02044     uint left = r.left + WD_FRAMERECT_LEFT;
    02045     uint right = r.right - WD_FRAMERECT_RIGHT;
    02046     uint type_icon_width = this->server_client_width + this->company_icon_width;
    02047
    02048
    02049     uint type_left  = rtl ? right - this->server_client_width : left;
    02050     uint type_right = rtl ? right : left + this->server_client_width - 1;
    02051     uint icon_left  = rtl ? right - type_icon_width + WD_FRAMERECT_LEFT : left + this->server_client_width;
    02052     uint name_left  = rtl ? left : left + type_icon_width;
    02053     uint name_right = rtl ? right - type_icon_width : right;
    02054
    02055     int i = 0;
    02056     const NetworkClientInfo *ci;
    02057     FOR_ALL_CLIENT_INFOS(ci) {
    02058       TextColour colour;
    02059       if (this->selected_item == i++) { // Selected item, highlight it
    02060         GfxFillRect(r.left + 1, y, r.right - 1, y + FONT_HEIGHT_NORMAL - 1, 0);
    02061         colour = TC_WHITE;
    02062       } else {
    02063         colour = TC_BLACK;
    02064       }
    02065
    02066       if (ci->client_id == CLIENT_ID_SERVER) {
    02067         DrawString(type_left, type_right, y, STR_NETWORK_SERVER, colour);
    02068       } else {
    02069         DrawString(type_left, type_right, y, STR_NETWORK_CLIENT, colour);
    02070       }
    02071
    02072       /* Filter out spectators */
    02073       if (Company::IsValidID(ci->client_playas)) DrawCompanyIcon(ci->client_playas, icon_left, y + icon_y_offset);
    02074
    02075       DrawString(name_left, name_right, y, ci->client_name, colour);
    02076
    02077       y += FONT_HEIGHT_NORMAL;
    02078     }
    02079   }
    02080
    02081   virtual void OnClick(Point pt, int widget, int click_count)
    02082   {
    02083     /* Show the popup with option */
    02084     if (this->selected_item != -1) {
    02085       PopupClientList(this->selected_item, pt.x + this->left, pt.y + this->top);
    02086     }
    02087   }
    02088
    02089   virtual void OnMouseOver(Point pt, int widget)
    02090   {
    02091     /* -1 means we left the current window */
    02092     if (pt.y == -1) {
    02093       this->selected_item = -1;
    02094       this->SetDirty();
    02095       return;
    02096     }
    02097
    02098     /* Find the new selected item (if any) */
    02099     pt.y -= this->GetWidget<NWidgetBase>(CLW_PANEL)->pos_y;
    02100     int item = -1;
    02101     if (IsInsideMM(pt.y, WD_FRAMERECT_TOP, this->GetWidget<NWidgetBase>(CLW_PANEL)->current_y - WD_FRAMERECT_BOTTOM)) {
    02102       item = (pt.y - WD_FRAMERECT_TOP) / FONT_HEIGHT_NORMAL;
    02103     }
    02104
    02105     /* It did not change.. no update! */
    02106     if (item == this->selected_item) return;
    02107     this->selected_item = item;
    02108
    02109     /* Repaint */
    02110     this->SetDirty();
    02111   }
    02112 };
    02113
    02114 void ShowClientList()
    02115 {
    02116   AllocateWindowDescFront<NetworkClientListWindow>(&_client_list_desc, 0);
    02117 }
    02118
    02119 /* Vars needed for the join-GUI */
    02120 NetworkJoinStatus _network_join_status;
    02121 uint8 _network_join_waiting;
    02122 uint32 _network_join_bytes;
    02123 uint32 _network_join_bytes_total;
    02124
    02126 enum NetworkJoinStatusWidgets {
    02127   NJSW_BACKGROUND,
    02128   NJSW_CANCELOK,
    02129 };
    02130
    02131 struct NetworkJoinStatusWindow : Window {
    02132   NetworkPasswordType password_type;
    02133
    02134   NetworkJoinStatusWindow(const WindowDesc *desc) : Window()
    02135   {
    02136     this->parent = FindWindowById(WC_NETWORK_WINDOW, 0);
    02137     this->InitNested(desc, 0);
    02138   }
    02139
    02140   virtual void OnPaint()
    02141   {
    02142     this->DrawWidgets();
    02143   }
    02144
    02145   virtual void DrawWidget(const Rect &r, int widget) const
    02146   {
    02147     if (widget != NJSW_BACKGROUND) return;
    02148
    02149     uint8 progress; // used for progress bar
    02150     DrawString(r.left + 2, r.right - 2, r.top + 20, STR_NETWORK_CONNECTING_1 + _network_join_status, TC_FROMSTRING, SA_CENTER);
    02151     switch (_network_join_status) {
    02152       case NETWORK_JOIN_STATUS_CONNECTING: case NETWORK_JOIN_STATUS_AUTHORIZING:
    02153       case NETWORK_JOIN_STATUS_GETTING_COMPANY_INFO:
    02154         progress = 10; // first two stages 10%
    02155         break;
    02156       case NETWORK_JOIN_STATUS_WAITING:
    02157         SetDParam(0, _network_join_waiting);
    02158         DrawString(r.left + 2, r.right - 2, r.top + 20 + FONT_HEIGHT_NORMAL, STR_NETWORK_CONNECTING_WAITING, TC_FROMSTRING, SA_CENTER);
    02159         progress = 15; // third stage is 15%
    02160         break;
    02161       case NETWORK_JOIN_STATUS_DOWNLOADING:
    02162         SetDParam(0, _network_join_bytes);
    02163         SetDParam(1, _network_join_bytes_total);
    02164         DrawString(r.left + 2, r.right - 2, r.top + 20 + FONT_HEIGHT_NORMAL, STR_NETWORK_CONNECTING_DOWNLOADING, TC_FROMSTRING, SA_CENTER);
    02165         /* Fallthrough */
    02166       default: // Waiting is 15%, so the resting receivement of map is maximum 70%
    02167         progress = 15 + _network_join_bytes * (100 - 15) / _network_join_bytes_total;
    02168     }
    02169
    02170     /* Draw nice progress bar :) */
    02171     DrawFrameRect(r.left + 20, r.top + 5, (int)((this->width - 20) * progress / 100), r.top + 15, COLOUR_MAUVE, FR_NONE);
    02172   }
    02173
    02174   virtual void UpdateWidgetSize(int widget, Dimension *size, const Dimension &padding, Dimension *fill, Dimension *resize)
    02175   {
    02176     if (widget != NJSW_BACKGROUND) return;
    02177
    02178     size->height = 25 + 2 * FONT_HEIGHT_NORMAL;
    02179
    02180     /* Account for the statuses */
    02181     uint width = 0;
    02182     for (uint i = 0; i < NETWORK_JOIN_STATUS_END; i++) {
    02183       width = max(width, GetStringBoundingBox(STR_NETWORK_CONNECTING_1 + i).width);
    02184     }
    02185
    02186     /* For the number of waiting (other) players */
    02187     SetDParam(0, 255);
    02188     width = max(width, GetStringBoundingBox(STR_NETWORK_CONNECTING_WAITING).width);
    02189
    02190     /* Account for downloading ~ 10 MiB */
    02191     SetDParam(0, 10000000);
    02192     SetDParam(1, 10000000);
    02193     width = max(width, GetStringBoundingBox(STR_NETWORK_CONNECTING_DOWNLOADING).width);
    02194
    02195     /* Give a bit more clearing for the widest strings than strictly needed */
    02196     size->width = width + WD_FRAMERECT_LEFT + WD_FRAMERECT_BOTTOM + 10;
    02197   }
    02198
    02199   virtual void OnClick(Point pt, int widget, int click_count)
    02200   {
    02201     if (widget == NJSW_CANCELOK) { // Disconnect button
    02202       NetworkDisconnect();
    02203       SwitchToMode(SM_MENU);
    02204       ShowNetworkGameWindow();
    02205     }
    02206   }
    02207
    02208   virtual void OnQueryTextFinished(char *str)
    02209   {
    02210     if (StrEmpty(str)) {
    02211       NetworkDisconnect();
    02212       ShowNetworkGameWindow();
    02213       return;
    02214     }
    02215
    02216     switch (this->password_type) {
    02217       case NETWORK_GAME_PASSWORD:    SEND_COMMAND(PACKET_CLIENT_GAME_PASSWORD)   (str); break;
    02218       case NETWORK_COMPANY_PASSWORD: SEND_COMMAND(PACKET_CLIENT_COMPANY_PASSWORD)(str); break;
    02219       default: NOT_REACHED();
    02220     }
    02221   }
    02222 };
    02223
    02224 static const NWidgetPart _nested_network_join_status_window_widgets[] = {
    02225   NWidget(WWT_CAPTION, COLOUR_GREY), SetDataTip(STR_NETWORK_CONNECTING_CAPTION, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS),
    02226   NWidget(WWT_PANEL, COLOUR_GREY),
    02227     NWidget(WWT_EMPTY, COLOUR_GREY, NJSW_BACKGROUND),
    02228     NWidget(NWID_HORIZONTAL),
    02229       NWidget(NWID_SPACER), SetMinimalSize(75, 0), SetFill(1, 0),
    02230       NWidget(WWT_PUSHTXTBTN, COLOUR_WHITE, NJSW_CANCELOK), SetMinimalSize(101, 12), SetDataTip(STR_NETWORK_CONNECTION_DISCONNECT, STR_NULL),
    02231       NWidget(NWID_SPACER), SetMinimalSize(75, 0), SetFill(1, 0),
    02232     EndContainer(),
    02233     NWidget(NWID_SPACER), SetMinimalSize(0, 4),
    02234   EndContainer(),
    02235 };
    02236
    02237 static const WindowDesc _network_join_status_window_desc(
    02238   WDP_CENTER, 0, 0,
    02239   WC_NETWORK_STATUS_WINDOW, WC_NONE,
    02240   WDF_MODAL,
    02241   _nested_network_join_status_window_widgets, lengthof(_nested_network_join_status_window_widgets)
    02242 );
    02243
    02244 void ShowJoinStatusWindow()
    02245 {
    02246   DeleteWindowById(WC_NETWORK_STATUS_WINDOW, 0);
    02247   new NetworkJoinStatusWindow(&_network_join_status_window_desc);
    02248 }
    02249
    02250 void ShowNetworkNeedPassword(NetworkPasswordType npt)
    02251 {
    02252   NetworkJoinStatusWindow *w = (NetworkJoinStatusWindow *)FindWindowById(WC_NETWORK_STATUS_WINDOW, 0);
    02253   if (w == NULL) return;
    02254   w->password_type = npt;
    02255
    02256   StringID caption;
    02257   switch (npt) {
    02258     default: NOT_REACHED();
    02259     case NETWORK_GAME_PASSWORD:    caption = STR_NETWORK_NEED_GAME_PASSWORD_CAPTION; break;
    02260     case NETWORK_COMPANY_PASSWORD: caption = STR_NETWORK_NEED_COMPANY_PASSWORD_CAPTION; break;
    02261   }
    02262   ShowQueryString(STR_EMPTY, caption, NETWORK_PASSWORD_LENGTH, 180, w, CS_ALPHANUMERAL, QSF_NONE);
    02263 }
    02264
    02265
    02267 enum NetworkCompanyPasswordWindowWidgets {
    02268   NCPWW_BACKGROUND,
    02269   NCPWW_LABEL,
    02270   NCPWW_PASSWORD,
    02271   NCPWW_SAVE_AS_DEFAULT_PASSWORD,
    02272   NCPWW_CANCEL,
    02273   NCPWW_OK,
    02274 };
    02275
    02276 struct NetworkCompanyPasswordWindow : public QueryStringBaseWindow {
    02277   NetworkCompanyPasswordWindow(const WindowDesc *desc, Window *parent) : QueryStringBaseWindow(lengthof(_settings_client.network.default_company_pass))
    02278   {
    02279     this->InitNested(desc, 0);
    02280
    02281     this->parent = parent;
    02282     this->afilter = CS_ALPHANUMERAL;
    02283     InitializeTextBuffer(&this->text, this->edit_str_buf, this->edit_str_size, 0);
    02284     this->SetFocusedWidget(NCPWW_PASSWORD);
    02285   }
    02286
    02287   void OnOk()
    02288   {
    02289     if (this->IsWidgetLowered(NCPWW_SAVE_AS_DEFAULT_PASSWORD)) {
    02290       snprintf(_settings_client.network.default_company_pass, lengthof(_settings_client.network.default_company_pass), "%s", this->edit_str_buf);
    02291     }
    02292
    02293     NetworkChangeCompanyPassword(this->edit_str_buf);
    02294   }
    02295
    02296   virtual void OnPaint()
    02297   {
    02298     this->DrawWidgets();
    02299     this->DrawEditBox(NCPWW_PASSWORD);
    02300   }
    02301
    02302   virtual void OnClick(Point pt, int widget, int click_count)
    02303   {
    02304     switch (widget) {
    02305       case NCPWW_OK:
    02306         this->OnOk();
    02307
    02308       /* FALL THROUGH */
    02309       case NCPWW_CANCEL:
    02310         delete this;
    02311         break;
    02312
    02313       case NCPWW_SAVE_AS_DEFAULT_PASSWORD:
    02314         this->ToggleWidgetLoweredState(NCPWW_SAVE_AS_DEFAULT_PASSWORD);
    02315         this->SetDirty();
    02316         break;
    02317     }
    02318   }
    02319
    02320   virtual void OnMouseLoop()
    02321   {
    02322     this->HandleEditBox(4);
    02323   }
    02324
    02325   virtual EventState OnKeyPress(uint16 key, uint16 keycode)
    02326   {
    02327     EventState state = ES_NOT_HANDLED;
    02328     switch (this->HandleEditBoxKey(NCPWW_PASSWORD, key, keycode, state)) {
    02329       default: break;
    02330
    02331       case HEBR_CONFIRM:
    02332         this->OnOk();
    02333         /* FALL THROUGH */
    02334
    02335       case HEBR_CANCEL:
    02336         delete this;
    02337         break;
    02338     }
    02339     return state;
    02340   }
    02341
    02342   virtual void OnOpenOSKWindow(int wid)
    02343   {
    02344     ShowOnScreenKeyboard(this, wid, NCPWW_CANCEL, NCPWW_OK);
    02345   }
    02346 };
    02347
    02348 static const NWidgetPart _nested_network_company_password_window_widgets[] = {
    02349   NWidget(NWID_HORIZONTAL),
    02350     NWidget(WWT_CLOSEBOX, COLOUR_GREY),
    02351     NWidget(WWT_CAPTION, COLOUR_GREY), SetDataTip(STR_COMPANY_PASSWORD_CAPTION, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS),
    02352   EndContainer(),
    02353   NWidget(WWT_PANEL, COLOUR_GREY, NCPWW_BACKGROUND),
    02354     NWidget(NWID_VERTICAL), SetPIP(5, 5, 5),
    02355       NWidget(NWID_HORIZONTAL), SetPIP(5, 5, 5),
    02356         NWidget(WWT_TEXT, COLOUR_GREY, NCPWW_LABEL), SetDataTip(STR_COMPANY_VIEW_PASSWORD, STR_NULL),
    02357         NWidget(WWT_EDITBOX, COLOUR_GREY, NCPWW_PASSWORD), SetMinimalSize(194, 12), SetDataTip(STR_COMPANY_VIEW_SET_PASSWORD, STR_NULL),
    02358       EndContainer(),
    02359       NWidget(NWID_HORIZONTAL), SetPIP(5, 0, 5),
    02360         NWidget(NWID_SPACER), SetFill(1, 0),
    02361         NWidget(WWT_TEXTBTN, COLOUR_GREY, NCPWW_SAVE_AS_DEFAULT_PASSWORD), SetMinimalSize(194, 12),
    02362                       SetDataTip(STR_COMPANY_PASSWORD_MAKE_DEFAULT, STR_COMPANY_PASSWORD_MAKE_DEFAULT_TOOLTIP),
    02363       EndContainer(),
    02364     EndContainer(),
    02365   EndContainer(),
    02366   NWidget(NWID_HORIZONTAL, NC_EQUALSIZE),
    02367     NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, NCPWW_CANCEL), SetFill(1, 0), SetDataTip(STR_BUTTON_CANCEL, STR_COMPANY_PASSWORD_CANCEL),
    02368     NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, NCPWW_OK), SetFill(1, 0), SetDataTip(STR_BUTTON_OK, STR_COMPANY_PASSWORD_OK),
    02369   EndContainer(),
    02370 };
    02371
    02372 static const WindowDesc _network_company_password_window_desc(
    02373   WDP_AUTO, 0, 0,
    02374   WC_COMPANY_PASSWORD_WINDOW, WC_NONE,
    02375   WDF_UNCLICK_BUTTONS,
    02376   _nested_network_company_password_window_widgets, lengthof(_nested_network_company_password_window_widgets)
    02377 );
    02378
    02379 void ShowNetworkCompanyPasswordWindow(Window *parent)
    02380 {
    02381   DeleteWindowById(WC_COMPANY_PASSWORD_WINDOW, 0);
    02382
    02383   new NetworkCompanyPasswordWindow(&_network_company_password_window_desc, parent);
    02384 }
    02385
    02386 #endif /* ENABLE_NETWORK */