network_gui.cpp

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