# network_gui.cpp

Go to the documentation of this file.

    00001 /* $Id: network_gui.cpp 15428 2009-02-09 02:57:15Z rubidium $ */
    00002
    00005 #ifdef ENABLE_NETWORK
    00006 #include "../stdafx.h"
    00007 #include "../openttd.h"
    00008 #include "../strings_func.h"
    00009 #include "../date_func.h"
    00010 #include "../fios.h"
    00011 #include "network_internal.h"
    00012 #include "network_client.h"
    00013 #include "network_gui.h"
    00014 #include "network_gamelist.h"
    00015 #include "../gui.h"
    00016 #include "network_server.h"
    00017 #include "network_udp.h"
    00018 #include "../window_func.h"
    00019 #include "../string_func.h"
    00020 #include "../gfx_func.h"
    00021 #include "../settings_type.h"
    00022 #include "../widgets/dropdown_func.h"
    00023 #include "../querystring_gui.h"
    00024 #include "../sortlist_type.h"
    00025 #include "../company_base.h"
    00026
    00027 #include "table/strings.h"
    00028 #include "../table/sprites.h"
    00029
    00030
    00031 static void ShowNetworkStartServerWindow();
    00032 static void ShowNetworkLobbyWindow(NetworkGameList *ngl);
    00033 extern void SwitchMode(int new_mode);
    00034
    00035 static const StringID _connection_types_dropdown[] = {
    00036   STR_NETWORK_LAN_INTERNET,
    00037   STR_NETWORK_INTERNET_ADVERTISE,
    00038   INVALID_STRING_ID
    00039 };
    00040
    00041 static const StringID _lan_internet_types_dropdown[] = {
    00042   STR_NETWORK_LAN,
    00043   STR_NETWORK_INTERNET,
    00044   INVALID_STRING_ID
    00045 };
    00046
    00047 static StringID _language_dropdown[NETLANG_COUNT + 1] = {STR_NULL};
    00048
    00049 void SortNetworkLanguages()
    00050 {
    00051   /* Init the strings */
    00052   if (_language_dropdown[0] == STR_NULL) {
    00053     for (int i = 0; i < NETLANG_COUNT; i++) _language_dropdown[i] = STR_NETWORK_LANG_ANY + i;
    00054     _language_dropdown[NETLANG_COUNT] = INVALID_STRING_ID;
    00055   }
    00056
    00057   /* Sort the strings (we don't move 'any' and the 'invalid' one) */
    00058   qsort(&_language_dropdown[1], NETLANG_COUNT - 1, sizeof(StringID), &StringIDSorter);
    00059 }
    00060
    00061 enum {
    00062   NET_PRC__OFFSET_TOP_WIDGET          = 54,
    00063   NET_PRC__OFFSET_TOP_WIDGET_COMPANY  = 52,
    00064   NET_PRC__SIZE_OF_ROW                = 14,
    00065 };
    00066
    00070 void UpdateNetworkGameWindow(bool unselect)
    00071 {
    00072   InvalidateWindowData(WC_NETWORK_WINDOW, 0, unselect ? 1 : 0);
    00073 }
    00074
    00076 enum NetworkGameWindowWidgets {
    00077   NGWW_CLOSE,
    00078   NGWW_CAPTION,
    00079   NGWW_RESIZE,
    00080
    00081   NGWW_CONNECTION,
    00082   NGWW_CONN_BTN,
    00083   NGWW_CLIENT,
    00084
    00085   NGWW_NAME,
    00086   NGWW_CLIENTS,
    00087   NGWW_MAPSIZE,
    00088   NGWW_DATE,
    00089   NGWW_YEARS,
    00090   NGWW_INFO,
    00091
    00092   NGWW_MATRIX,
    00093   NGWW_SCROLLBAR,
    00094
    00095   NGWW_LASTJOINED_LABEL,
    00096   NGWW_LASTJOINED,
    00097
    00098   NGWW_DETAILS,
    00099   NGWW_JOIN,
    00100   NGWW_REFRESH,
    00101   NGWW_NEWGRF,
    00102
    00103   NGWW_FIND,
    00104   NGWW_ADD,
    00105   NGWW_START,
    00106   NGWW_CANCEL,
    00107 };
    00108
    00109 typedef GUIList<NetworkGameList*> GUIGameServerList;
    00110 typedef uint16 ServerListPosition;
    00111 static const ServerListPosition SLP_INVALID = 0xFFFF;
    00112
    00113 class NetworkGameWindow : public QueryStringBaseWindow {
    00114 protected:
    00115   /* Runtime saved values */
    00116   static Listing last_sorting;
    00117
    00118   /* Constants for sorting servers */
    00119   static GUIGameServerList::SortFunction *const sorter_funcs[];
    00120
    00121   byte field;
    00122   NetworkGameList *server;
    00123   GUIGameServerList servers;
    00124   ServerListPosition list_pos;
    00125
    00130   void BuildNetworkGameList()
    00131   {
    00132     if (!this->servers.NeedRebuild()) return;
    00133
    00134     /* Create temporary array of games to use for listing */
    00135     this->servers.Clear();
    00136
    00137     for (NetworkGameList *ngl = _network_game_list; ngl != NULL; ngl = ngl->next) {
    00138       *this->servers.Append() = ngl;
    00139     }
    00140
    00141     this->servers.Compact();
    00142     this->servers.RebuildDone();
    00143   }
    00144
    00146   static int CDECL NGameNameSorter(NetworkGameList * const *a, NetworkGameList * const *b)
    00147   {
    00148     return strcasecmp((*a)->info.server_name, (*b)->info.server_name);
    00149   }
    00150
    00154   static int CDECL NGameClientSorter(NetworkGameList * const *a, NetworkGameList * const *b)
    00155   {
    00156     /* Reverse as per default we are interested in most-clients first */
    00157     int r = (*a)->info.clients_on - (*b)->info.clients_on;
    00158
    00159     if (r == 0) r = (*a)->info.clients_max - (*b)->info.clients_max;
    00160     if (r == 0) r = NGameNameSorter(a, b);
    00161
    00162     return r;
    00163   }
    00164
    00166   static int CDECL NGameMapSizeSorter(NetworkGameList * const *a, NetworkGameList * const *b)
    00167   {
    00168     /* Sort by the area of the map. */
    00169     int r = ((*a)->info.map_height) * ((*a)->info.map_width) - ((*b)->info.map_height) * ((*b)->info.map_width);
    00170
    00171     if (r == 0) r = (*a)->info.map_width - (*b)->info.map_width;
    00172     return (r != 0) ? r : NGameClientSorter(a, b);
    00173   }
    00174
    00176   static int CDECL NGameDateSorter(NetworkGameList * const *a, NetworkGameList * const *b)
    00177   {
    00178     int r = (*a)->info.game_date - (*b)->info.game_date;
    00179     return (r != 0) ? r : NGameClientSorter(a, b);
    00180   }
    00181
    00183   static int CDECL NGameYearsSorter(NetworkGameList * const *a, NetworkGameList * const *b)
    00184   {
    00185     int r = (*a)->info.game_date - (*a)->info.start_date - (*b)->info.game_date + (*b)->info.start_date;
    00186     return (r != 0) ? r : NGameDateSorter(a, b);
    00187   }
    00188
    00191   static int CDECL NGameAllowedSorter(NetworkGameList * const *a, NetworkGameList * const *b)
    00192   {
    00193     /* The servers we do not know anything about (the ones that did not reply) should be at the bottom) */
    00194     int r = StrEmpty((*a)->info.server_revision) - StrEmpty((*b)->info.server_revision);
    00195
    00196     /* Reverse default as we are interested in version-compatible clients first */
    00197     if (r == 0) r = (*b)->info.version_compatible - (*a)->info.version_compatible;
    00198     /* The version-compatible ones are then sorted with NewGRF compatible first, incompatible last */
    00199     if (r == 0) r = (*b)->info.compatible - (*a)->info.compatible;
    00200     /* Passworded servers should be below unpassworded servers */
    00201     if (r == 0) r = (*a)->info.use_password - (*b)->info.use_password;
    00202     /* Finally sort on the name of the server */
    00203     if (r == 0) r = NGameNameSorter(a, b);
    00204
    00205     return r;
    00206   }
    00207
    00209   void SortNetworkGameList()
    00210   {
    00211     if (!this->servers.Sort()) return;
    00212
    00213     /* After sorting ngl->sort_list contains the sorted items. Put these back
    00214      * into the original list. Basically nothing has changed, we are only
    00215      * shuffling the ->next pointers. While iterating, look for the
    00216      * currently selected server and set list_pos to its position */
    00217     this->list_pos = SLP_INVALID;
    00218     _network_game_list = this->servers[0];
    00219     NetworkGameList *item = _network_game_list;
    00220     if (item == this->server) this->list_pos = 0;
    00221     for (uint i = 1; i != this->servers.Length(); i++) {
    00222       item->next = this->servers[i];
    00223       item = item->next;
    00224       if (item == this->server) this->list_pos = i;
    00225     }
    00226     item->next = NULL;
    00227   }
    00228
    00235   void DrawServerLine(const NetworkGameList *cur_item, uint y, bool highlight)
    00236   {
    00237     /* show highlighted item with a different colour */
    00238     if (highlight) GfxFillRect(this->widget[NGWW_NAME].left + 1, y - 2, this->widget[NGWW_INFO].right - 1, y + 9, 10);
    00239
    00240     SetDParamStr(0, cur_item->info.server_name);
    00241     DrawStringTruncated(this->widget[NGWW_NAME].left + 5, y, STR_JUST_RAW_STRING, TC_BLACK, this->widget[NGWW_NAME].right - this->widget[NGWW_NAME].left - 5);
    00242
    00243     /* only draw details if the server is online */
    00244     if (cur_item->online) {
    00245       SetDParam(0, cur_item->info.clients_on);
    00246       SetDParam(1, cur_item->info.clients_max);
    00247       SetDParam(2, cur_item->info.companies_on);
    00248       SetDParam(3, cur_item->info.companies_max);
    00249       DrawStringCentered(this->widget[NGWW_CLIENTS].left + 39, y, STR_NETWORK_GENERAL_ONLINE, TC_GOLD);
    00250
    00251       /* map size */
    00252       if (!this->IsWidgetHidden(NGWW_MAPSIZE)) {
    00253         SetDParam(0, cur_item->info.map_width);
    00254         SetDParam(1, cur_item->info.map_height);
    00255         DrawStringCentered(this->widget[NGWW_MAPSIZE].left + 39, y, STR_NETWORK_MAP_SIZE_SHORT, TC_BLACK);
    00256       }
    00257
    00258       /* current date */
    00259       if (!this->IsWidgetHidden(NGWW_DATE)) {
    00260         YearMonthDay ymd;
    00261         ConvertDateToYMD(cur_item->info.game_date, &ymd);
    00262         SetDParam(0, ymd.year);
    00263         DrawStringCentered(this->widget[NGWW_DATE].left + 29, y, STR_JUST_INT, TC_BLACK);
    00264       }
    00265
    00266       /* number of years the game is running */
    00267       if (!this->IsWidgetHidden(NGWW_YEARS)) {
    00268         YearMonthDay ymd_cur, ymd_start;
    00269         ConvertDateToYMD(cur_item->info.game_date, &ymd_cur);
    00270         ConvertDateToYMD(cur_item->info.start_date, &ymd_start);
    00271         SetDParam(0, ymd_cur.year - ymd_start.year);
    00272         DrawStringCentered(this->widget[NGWW_YEARS].left + 29, y, STR_JUST_INT, TC_BLACK);
    00273       }
    00274
    00275       /* draw a lock if the server is password protected */
    00276       if (cur_item->info.use_password) DrawSprite(SPR_LOCK, PAL_NONE, this->widget[NGWW_INFO].left + 5, y - 1);
    00277
    00278       /* draw red or green icon, depending on compatibility with server */
    00279       DrawSprite(SPR_BLOT, (cur_item->info.compatible ? PALETTE_TO_GREEN : (cur_item->info.version_compatible ? PALETTE_TO_YELLOW : PALETTE_TO_RED)), this->widget[NGWW_INFO].left + 15, y);
    00280
    00281       /* draw flag according to server language */
    00282       DrawSprite(SPR_FLAGS_BASE + cur_item->info.server_lang, PAL_NONE, this->widget[NGWW_INFO].left + 25, y);
    00283     }
    00284   }
    00285
    00293   void ScrollToSelectedServer()
    00294   {
    00295     if (this->list_pos == SLP_INVALID) return; // no server selected
    00296     if (this->list_pos < this->vscroll.pos) {
    00297       /* scroll up to the server */
    00298       this->vscroll.pos = this->list_pos;
    00299     } else if (this->list_pos >= this->vscroll.pos + this->vscroll.cap) {
    00300       /* scroll down so that the server is at the bottom */
    00301       this->vscroll.pos = this->list_pos - this->vscroll.cap + 1;
    00302     }
    00303   }
    00304
    00305 public:
    00306   NetworkGameWindow(const WindowDesc *desc) : QueryStringBaseWindow(NETWORK_NAME_LENGTH, desc)
    00307   {
    00308     ttd_strlcpy(this->edit_str_buf, _settings_client.network.client_name, this->edit_str_size);
    00309     this->afilter = CS_ALPHANUMERAL;
    00310     InitializeTextBuffer(&this->text, this->edit_str_buf, this->edit_str_size, 120);
    00311     this->SetFocusedWidget(NGWW_CLIENT);
    00312
    00313     UpdateNetworkGameWindow(true);
    00314
    00315     this->vscroll.cap = 11;
    00316     this->resize.step_height = NET_PRC__SIZE_OF_ROW;
    00317
    00318     this->field = NGWW_CLIENT;
    00319     this->server = NULL;
    00320     this->list_pos = SLP_INVALID;
    00321
    00322     this->servers.SetListing(this->last_sorting);
    00323     this->servers.SetSortFuncs(this->sorter_funcs);
    00324     this->servers.ForceRebuild();
    00325     this->SortNetworkGameList();
    00326
    00327     this->FindWindowPlacementAndResize(desc);
    00328   }
    00329
    00330   ~NetworkGameWindow()
    00331   {
    00332     this->last_sorting = this->servers.GetListing();
    00333   }
    00334
    00335   virtual void OnPaint()
    00336   {
    00337     const NetworkGameList *sel = this->server;
    00338     const SortButtonState arrow = this->servers.IsDescSortOrder() ? SBS_DOWN : SBS_UP;
    00339
    00340     if (this->servers.NeedRebuild()) {
    00341       this->BuildNetworkGameList();
    00342       SetVScrollCount(this, this->servers.Length());
    00343     }
    00344     this->SortNetworkGameList();
    00345
    00346     /* 'Refresh' button invisible if no server selected */
    00347     this->SetWidgetDisabledState(NGWW_REFRESH, sel == NULL);
    00348     /* 'Join' button disabling conditions */
    00349     this->SetWidgetDisabledState(NGWW_JOIN, sel == NULL || // no Selected Server
    00350         !sel->online || // Server offline
    00351         sel->info.clients_on >= sel->info.clients_max || // Server full
    00352         !sel->info.compatible); // Revision mismatch
    00353
    00354     /* 'NewGRF Settings' button invisible if no NewGRF is used */
    00355     this->SetWidgetHiddenState(NGWW_NEWGRF, sel == NULL ||
    00356         !sel->online ||
    00357         sel->info.grfconfig == NULL);
    00358
    00359     SetDParam(0, 0x00);
    00360     SetDParam(1, _lan_internet_types_dropdown[_settings_client.network.lan_internet]);
    00361     this->DrawWidgets();
    00362
    00363     /* Edit box to set client name */
    00364     this->DrawEditBox(NGWW_CLIENT);
    00365
    00366     DrawString(this->widget[NGWW_CLIENT].left - 100, 23, STR_NETWORK_PLAYER_NAME, TC_GOLD);
    00367
    00368     /* Sort based on widgets: name, clients, compatibility */
    00369     switch (this->servers.SortType()) {
    00370       case NGWW_NAME    - NGWW_NAME: this->DrawSortButtonState(NGWW_NAME,    arrow); break;
    00371       case NGWW_CLIENTS - NGWW_NAME: this->DrawSortButtonState(NGWW_CLIENTS, arrow); break;
    00372       case NGWW_MAPSIZE - NGWW_NAME: if (!this->IsWidgetHidden(NGWW_MAPSIZE)) this->DrawSortButtonState(NGWW_MAPSIZE, arrow); break;
    00373       case NGWW_DATE    - NGWW_NAME: if (!this->IsWidgetHidden(NGWW_DATE))    this->DrawSortButtonState(NGWW_DATE,    arrow); break;
    00374       case NGWW_YEARS   - NGWW_NAME: if (!this->IsWidgetHidden(NGWW_YEARS))   this->DrawSortButtonState(NGWW_YEARS,   arrow); break;
    00375       case NGWW_INFO    - NGWW_NAME: this->DrawSortButtonState(NGWW_INFO,    arrow); break;
    00376     }
    00377
    00378     uint16 y = NET_PRC__OFFSET_TOP_WIDGET + 3;
    00379
    00380     const int max = min(this->vscroll.pos + this->vscroll.cap, (int)this->servers.Length());
    00381
    00382     for (int i = this->vscroll.pos; i < max; ++i) {
    00383       const NetworkGameList *ngl = this->servers[i];
    00384       this->DrawServerLine(ngl, y, ngl == sel);
    00385       y += NET_PRC__SIZE_OF_ROW;
    00386     }
    00387
    00388     const NetworkGameList *last_joined = NetworkGameListAddItem(inet_addr(_settings_client.network.last_host), _settings_client.network.last_port);
    00389     /* Draw the last joined server, if any */
    00390     if (last_joined != NULL) this->DrawServerLine(last_joined, y = this->widget[NGWW_LASTJOINED].top + 3, last_joined == sel);
    00391
    00392     /* Draw the right menu */
    00393     GfxFillRect(this->widget[NGWW_DETAILS].left + 1, 43, this->widget[NGWW_DETAILS].right - 1, 92, 157);
    00394     if (sel == NULL) {
    00395       DrawStringCentered(this->widget[NGWW_DETAILS].left + 115, 58, STR_NETWORK_GAME_INFO, TC_FROMSTRING);
    00396     } else if (!sel->online) {
    00397       SetDParamStr(0, sel->info.server_name);
    00398       DrawStringCentered(this->widget[NGWW_DETAILS].left + 115, 68, STR_JUST_RAW_STRING, TC_ORANGE); // game name
    00399
    00400       DrawStringCentered(this->widget[NGWW_DETAILS].left + 115, 132, STR_NETWORK_SERVER_OFFLINE, TC_FROMSTRING); // server offline
    00401     } else { // show game info
    00402       uint16 y = 100;
    00403       const uint16 x = this->widget[NGWW_DETAILS].left + 5;
    00404
    00405       DrawStringCentered(this->widget[NGWW_DETAILS].left + 115, 48, STR_NETWORK_GAME_INFO, TC_FROMSTRING);
    00406
    00407
    00408       SetDParamStr(0, sel->info.server_name);
    00409       DrawStringCenteredTruncated(this->widget[NGWW_DETAILS].left, this->widget[NGWW_DETAILS].right, 62, STR_JUST_RAW_STRING, TC_ORANGE); // game name
    00410
    00411       SetDParamStr(0, sel->info.map_name);
    00412       DrawStringCenteredTruncated(this->widget[NGWW_DETAILS].left, this->widget[NGWW_DETAILS].right, 74, STR_JUST_RAW_STRING, TC_BLACK); // map name
    00413
    00414       SetDParam(0, sel->info.clients_on);
    00415       SetDParam(1, sel->info.clients_max);
    00416       SetDParam(2, sel->info.companies_on);
    00417       SetDParam(3, sel->info.companies_max);
    00418       DrawString(x, y, STR_NETWORK_CLIENTS, TC_GOLD);
    00419       y += 10;
    00420
    00421       SetDParam(0, STR_NETWORK_LANG_ANY + sel->info.server_lang);
    00422       DrawString(x, y, STR_NETWORK_LANGUAGE, TC_GOLD); // server language
    00423       y += 10;
    00424
    00425       SetDParam(0, STR_TEMPERATE_LANDSCAPE + sel->info.map_set);
    00426       DrawString(x, y, STR_NETWORK_TILESET, TC_GOLD); // tileset
    00427       y += 10;
    00428
    00429       SetDParam(0, sel->info.map_width);
    00430       SetDParam(1, sel->info.map_height);
    00431       DrawString(x, y, STR_NETWORK_MAP_SIZE, TC_GOLD); // map size
    00432       y += 10;
    00433
    00434       SetDParamStr(0, sel->info.server_revision);
    00435       DrawString(x, y, STR_NETWORK_SERVER_VERSION, TC_GOLD); // server version
    00436       y += 10;
    00437
    00438       SetDParamStr(0, sel->info.hostname);
    00439       SetDParam(1, sel->port);
    00440       DrawString(x, y, STR_NETWORK_SERVER_ADDRESS, TC_GOLD); // server address
    00441       y += 10;
    00442
    00443       SetDParam(0, sel->info.start_date);
    00444       DrawString(x, y, STR_NETWORK_START_DATE, TC_GOLD); // start date
    00445       y += 10;
    00446
    00447       SetDParam(0, sel->info.game_date);
    00448       DrawString(x, y, STR_NETWORK_CURRENT_DATE, TC_GOLD); // current date
    00449       y += 10;
    00450
    00451       y += 2;
    00452
    00453       if (!sel->info.compatible) {
    00454         DrawStringCentered(this->widget[NGWW_DETAILS].left + 115, y, sel->info.version_compatible ? STR_NETWORK_GRF_MISMATCH : STR_NETWORK_VERSION_MISMATCH, TC_FROMSTRING); // server mismatch
    00455       } else if (sel->info.clients_on == sel->info.clients_max) {
    00456         /* Show: server full, when clients_on == max_clients */
    00457         DrawStringCentered(this->widget[NGWW_DETAILS].left + 115, y, STR_NETWORK_SERVER_FULL, TC_FROMSTRING); // server full
    00458       } else if (sel->info.use_password) {
    00459         DrawStringCentered(this->widget[NGWW_DETAILS].left + 115, y, STR_NETWORK_PASSWORD, TC_FROMSTRING); // password warning
    00460       }
    00461
    00462       y += 10;
    00463     }
    00464   }
    00465
    00466   virtual void OnClick(Point pt, int widget)
    00467   {
    00468     this->field = widget;
    00469     switch (widget) {
    00470       case NGWW_CANCEL: // Cancel button
    00471         DeleteWindowById(WC_NETWORK_WINDOW, 0);
    00472         break;
    00473
    00474       case NGWW_CONN_BTN: // 'Connection' droplist
    00475         ShowDropDownMenu(this, _lan_internet_types_dropdown, _settings_client.network.lan_internet, NGWW_CONN_BTN, 0, 0); // do it for widget NSSW_CONN_BTN
    00476         break;
    00477
    00478       case NGWW_NAME:    // Sort by name
    00479       case NGWW_CLIENTS: // Sort by connected clients
    00480       case NGWW_MAPSIZE: // Sort by map size
    00481       case NGWW_DATE:    // Sort by date
    00482       case NGWW_YEARS:   // Sort by years
    00483       case NGWW_INFO:    // Connectivity (green dot)
    00484         if (this->servers.SortType() == widget - NGWW_NAME) {
    00485           this->servers.ToggleSortOrder();
    00486           if (this->list_pos != SLP_INVALID) this->list_pos = this->servers.Length() - this->list_pos - 1;
    00487         } else {
    00488           this->servers.SetSortType(widget - NGWW_NAME);
    00489           this->servers.ForceResort();
    00490           this->SortNetworkGameList();
    00491         }
    00492         this->ScrollToSelectedServer();
    00493         this->SetDirty();
    00494         break;
    00495
    00496       case NGWW_MATRIX: { // Matrix to show networkgames
    00497         uint32 id_v = (pt.y - NET_PRC__OFFSET_TOP_WIDGET) / NET_PRC__SIZE_OF_ROW;
    00498
    00499         if (id_v >= this->vscroll.cap) return; // click out of bounds
    00500         id_v += this->vscroll.pos;
    00501
    00502         this->server = (id_v < this->servers.Length()) ? this->servers[id_v] : NULL;
    00503         this->list_pos = (server == NULL) ? SLP_INVALID : id_v;
    00504         this->SetDirty();
    00505       } break;
    00506
    00507       case NGWW_LASTJOINED: {
    00508         NetworkGameList *last_joined = NetworkGameListAddItem(inet_addr(_settings_client.network.last_host), _settings_client.network.last_port);
    00509         if (last_joined != NULL) {
    00510           this->server = last_joined;
    00511
    00512           /* search the position of the newly selected server */
    00513           for (uint i = 0; i < this->servers.Length(); i++) {
    00514             if (this->servers[i] == this->server) {
    00515               this->list_pos = i;
    00516               break;
    00517             }
    00518           }
    00519           this->ScrollToSelectedServer();
    00520           this->SetDirty();
    00521         }
    00522       } break;
    00523
    00524       case NGWW_FIND: // Find server automatically
    00525         switch (_settings_client.network.lan_internet) {
    00526           case 0: NetworkUDPSearchGame(); break;
    00527           case 1: NetworkUDPQueryMasterServer(); break;
    00528         }
    00529         break;
    00530
    00531       case NGWW_ADD: // Add a server
    00532         SetDParamStr(0, _settings_client.network.connect_to_ip);
    00533         ShowQueryString(
    00534           STR_JUST_RAW_STRING,
    00535           STR_NETWORK_ENTER_IP,
    00536           31,  // maximum number of characters
    00537           250, // characters up to this width pixels, whichever is satisfied first
    00538           this, CS_ALPHANUMERAL, QSF_ACCEPT_UNCHANGED);
    00539         break;
    00540
    00541       case NGWW_START: // Start server
    00542         ShowNetworkStartServerWindow();
    00543         break;
    00544
    00545       case NGWW_JOIN: // Join Game
    00546         if (this->server != NULL) {
    00547           snprintf(_settings_client.network.last_host, sizeof(_settings_client.network.last_host), "%s", inet_ntoa(*(struct in_addr *)&this->server->ip));
    00548           _settings_client.network.last_port = this->server->port;
    00549           ShowNetworkLobbyWindow(this->server);
    00550         }
    00551         break;
    00552
    00553       case NGWW_REFRESH: // Refresh
    00554         if (this->server != NULL) NetworkUDPQueryServer(NetworkAddress(this->server->info.hostname, this->server->port));
    00555         break;
    00556
    00557       case NGWW_NEWGRF: // NewGRF Settings
    00558         if (this->server != NULL) ShowNewGRFSettings(false, false, false, &this->server->info.grfconfig);
    00559         break;
    00560     }
    00561   }
    00562
    00563   virtual void OnDoubleClick(Point pt, int widget)
    00564   {
    00565     if (widget == NGWW_MATRIX || widget == NGWW_LASTJOINED) {
    00566       /* is the Join button enabled? */
    00567       if (!this->IsWidgetDisabled(NGWW_JOIN)) this->OnClick(pt, NGWW_JOIN);
    00568     }
    00569   }
    00570
    00571   virtual void OnDropdownSelect(int widget, int index)
    00572   {
    00573     switch (widget) {
    00574       case NGWW_CONN_BTN:
    00575         _settings_client.network.lan_internet = index;
    00576         break;
    00577
    00578       default:
    00579         NOT_REACHED();
    00580     }
    00581
    00582     this->SetDirty();
    00583   }
    00584
    00585   virtual void OnMouseLoop()
    00586   {
    00587     if (this->field == NGWW_CLIENT) this->HandleEditBox(NGWW_CLIENT);
    00588   }
    00589
    00590   virtual void OnInvalidateData(int data)
    00591   {
    00592     switch (data) {
    00593       /* Remove the selection */
    00594       case 1:
    00595         this->server = NULL;
    00596         this->list_pos = SLP_INVALID;
    00597         break;
    00598
    00599       /* Reiterate the whole server list as we downloaded some files */
    00600       case 2:
    00601         for (NetworkGameList **iter = this->servers.Begin(); iter != this->servers.End(); iter++) {
    00602           NetworkGameList *item = *iter;
    00603           bool missing_grfs = false;
    00604           for (GRFConfig *c = item->info.grfconfig; c != NULL; c = c->next) {
    00605             if (c->status != GCS_NOT_FOUND) continue;
    00606
    00607             const GRFConfig *f = FindGRFConfig(c->grfid, c->md5sum);
    00608             if (f == NULL) {
    00609               missing_grfs = true;
    00610               continue;
    00611             }
    00612
    00613             c->filename  = f->filename;
    00614             c->name      = f->name;
    00615             c->info      = f->info;
    00616             c->status    = GCS_UNKNOWN;
    00617           }
    00618
    00619           if (!missing_grfs) item->info.compatible = item->info.version_compatible;
    00620         }
    00621         break;
    00622     }
    00623     this->servers.ForceRebuild();
    00624     this->SetDirty();
    00625   }
    00626
    00627   virtual EventState OnKeyPress(uint16 key, uint16 keycode)
    00628   {
    00629     EventState state = ES_NOT_HANDLED;
    00630
    00631     /* handle up, down, pageup, pagedown, home and end */
    00632     if (keycode == WKC_UP || keycode == WKC_DOWN || keycode == WKC_PAGEUP || keycode == WKC_PAGEDOWN || keycode == WKC_HOME || keycode == WKC_END) {
    00633       if (this->servers.Length() == 0) return ES_HANDLED;
    00634       switch (keycode) {
    00635         case WKC_UP:
    00636           /* scroll up by one */
    00637           if (this->server == NULL) return ES_HANDLED;
    00638           if (this->list_pos > 0) this->list_pos--;
    00639           break;
    00640         case WKC_DOWN:
    00641           /* scroll down by one */
    00642           if (this->server == NULL) return ES_HANDLED;
    00643           if (this->list_pos < this->servers.Length() - 1) this->list_pos++;
    00644           break;
    00645         case WKC_PAGEUP:
    00646           /* scroll up a page */
    00647           if (this->server == NULL) return ES_HANDLED;
    00648           this->list_pos = (this->list_pos < this->vscroll.cap) ? 0 : this->list_pos - this->vscroll.cap;
    00649           break;
    00650         case WKC_PAGEDOWN:
    00651           /* scroll down a page */
    00652           if (this->server == NULL) return ES_HANDLED;
    00653           this->list_pos = min(this->list_pos + this->vscroll.cap, (int)this->servers.Length() - 1);
    00654           break;
    00655         case WKC_HOME:
    00656           /* jump to beginning */
    00657           this->list_pos = 0;
    00658           break;
    00659         case WKC_END:
    00660           /* jump to end */
    00661           this->list_pos = this->servers.Length() - 1;
    00662           break;
    00663         default: break;
    00664       }
    00665
    00666       this->server = this->servers[this->list_pos];
    00667
    00668       /* scroll to the new server if it is outside the current range */
    00669       this->ScrollToSelectedServer();
    00670
    00671       /* redraw window */
    00672       this->SetDirty();
    00673       return ES_HANDLED;
    00674     }
    00675
    00676     if (this->field != NGWW_CLIENT) {
    00677       if (this->server != NULL) {
    00678         if (keycode == WKC_DELETE) { // Press 'delete' to remove servers
    00679           NetworkGameListRemoveItem(this->server);
    00680           NetworkRebuildHostList();
    00681           this->server = NULL;
    00682           this->list_pos = SLP_INVALID;
    00683         }
    00684       }
    00685       return state;
    00686     }
    00687
    00688     if (this->HandleEditBoxKey(NGWW_CLIENT, key, keycode, state) == HEBR_CONFIRM) return state;
    00689
    00690     /* The name is only allowed when it starts with a letter! */
    00691     if (!StrEmpty(this->edit_str_buf) && this->edit_str_buf[0] != ' ') {
    00692       strecpy(_settings_client.network.client_name, this->edit_str_buf, lastof(_settings_client.network.client_name));
    00693     } else {
    00694       strecpy(_settings_client.network.client_name, "Player", lastof(_settings_client.network.client_name));
    00695     }
    00696     return state;
    00697   }
    00698
    00699   virtual void OnQueryTextFinished(char *str)
    00700   {
    00701     if (!StrEmpty(str)) {
    00702       NetworkAddServer(str);
    00703       NetworkRebuildHostList();
    00704     }
    00705   }
    00706
    00707   virtual void OnResize(Point new_size, Point delta)
    00708   {
    00709     this->vscroll.cap += delta.y / (int)this->resize.step_height;
    00710
    00711     this->widget[NGWW_MATRIX].data = (this->vscroll.cap << 8) + 1;
    00712
    00713     SetVScrollCount(this, this->servers.Length());
    00714
    00715     /* Additional colums in server list */
    00716     if (this->width > NetworkGameWindow::MIN_EXTRA_COLUMNS_WIDTH + GetWidgetWidth(NGWW_MAPSIZE)
    00717         + GetWidgetWidth(NGWW_DATE) + GetWidgetWidth(NGWW_YEARS)) {
    00718       /* show columns 'Map size', 'Date' and 'Years' */
    00719       this->SetWidgetsHiddenState(false, NGWW_MAPSIZE, NGWW_DATE, NGWW_YEARS, WIDGET_LIST_END);
    00720       AlignWidgetRight(NGWW_YEARS,   NGWW_INFO);
    00721       AlignWidgetRight(NGWW_DATE,    NGWW_YEARS);
    00722       AlignWidgetRight(NGWW_MAPSIZE, NGWW_DATE);
    00723       AlignWidgetRight(NGWW_CLIENTS, NGWW_MAPSIZE);
    00724     } else if (this->width > NetworkGameWindow::MIN_EXTRA_COLUMNS_WIDTH + GetWidgetWidth(NGWW_MAPSIZE) + GetWidgetWidth(NGWW_DATE)) {
    00725       /* show columns 'Map size' and 'Date' */
    00726       this->SetWidgetsHiddenState(false, NGWW_MAPSIZE, NGWW_DATE, WIDGET_LIST_END);
    00727       this->HideWidget(NGWW_YEARS);
    00728       AlignWidgetRight(NGWW_DATE,    NGWW_INFO);
    00729       AlignWidgetRight(NGWW_MAPSIZE, NGWW_DATE);
    00730       AlignWidgetRight(NGWW_CLIENTS, NGWW_MAPSIZE);
    00731     } else if (this->width > NetworkGameWindow::MIN_EXTRA_COLUMNS_WIDTH + GetWidgetWidth(NGWW_MAPSIZE)) {
    00732       /* show column 'Map size' */
    00733       this->ShowWidget(NGWW_MAPSIZE);
    00734       this->SetWidgetsHiddenState(true, NGWW_DATE, NGWW_YEARS, WIDGET_LIST_END);
    00735       AlignWidgetRight(NGWW_MAPSIZE, NGWW_INFO);
    00736       AlignWidgetRight(NGWW_CLIENTS, NGWW_MAPSIZE);
    00737     } else {
    00738       /* hide columns 'Map size', 'Date' and 'Years' */
    00739       this->SetWidgetsHiddenState(true, NGWW_MAPSIZE, NGWW_DATE, NGWW_YEARS, WIDGET_LIST_END);
    00740       AlignWidgetRight(NGWW_CLIENTS, NGWW_INFO);
    00741     }
    00742     this->widget[NGWW_NAME].right = this->widget[NGWW_CLIENTS].left - 1;
    00743
    00744     /* BOTTOM */
    00745     int widget_width = this->widget[NGWW_FIND].right - this->widget[NGWW_FIND].left;
    00746     int space = (this->width - 4 * widget_width - 25) / 3;
    00747
    00748     int offset = 10;
    00749     for (uint i = 0; i < 4; i++) {
    00750       this->widget[NGWW_FIND + i].left  = offset;
    00751       offset += widget_width;
    00752       this->widget[NGWW_FIND + i].right = offset;
    00753       offset += space;
    00754     }
    00755   }
    00756
    00757   static const int MIN_EXTRA_COLUMNS_WIDTH = 550;
    00758 };
    00759
    00760 Listing NetworkGameWindow::last_sorting = {false, 5};
    00761 GUIGameServerList::SortFunction *const NetworkGameWindow::sorter_funcs[] = {
    00762   &NGameNameSorter,
    00763   &NGameClientSorter,
    00764   &NGameMapSizeSorter,
    00765   &NGameDateSorter,
    00766   &NGameYearsSorter,
    00767   &NGameAllowedSorter
    00768 };
    00769
    00770
    00771 static const Widget _network_game_window_widgets[] = {
    00772 /* TOP */
    00773 {   WWT_CLOSEBOX,   RESIZE_NONE,   COLOUR_LIGHT_BLUE,     0,    10,     0,    13, STR_00C5,                         STR_018B_CLOSE_WINDOW},            // NGWW_CLOSE
    00774 {    WWT_CAPTION,   RESIZE_RIGHT,  COLOUR_LIGHT_BLUE,    11,   449,     0,    13, STR_NETWORK_MULTIPLAYER,          STR_NULL},                         // NGWW_CAPTION
    00775 {      WWT_PANEL,   RESIZE_RB,     COLOUR_LIGHT_BLUE,     0,   449,    14,   263, 0x0,                              STR_NULL},                         // NGWW_RESIZE
    00776
    00777 {       WWT_TEXT,   RESIZE_NONE,   COLOUR_LIGHT_BLUE,     9,    85,    23,    35, STR_NETWORK_CONNECTION,           STR_NULL},                         // NGWW_CONNECTION
    00778 { WWT_DROPDOWNIN,   RESIZE_NONE,   COLOUR_LIGHT_BLUE,    90,   181,    22,    33, STR_NETWORK_LAN_INTERNET_COMBO,   STR_NETWORK_CONNECTION_TIP},       // NGWW_CONN_BTN
    00779
    00780 {    WWT_EDITBOX,   RESIZE_LR,     COLOUR_LIGHT_BLUE,   290,   440,    22,    33, STR_NETWORK_PLAYER_NAME_OSKTITLE, STR_NETWORK_ENTER_NAME_TIP},       // NGWW_CLIENT
    00781
    00782 /* LEFT SIDE */
    00783 { WWT_PUSHTXTBTN,   RESIZE_NONE,   COLOUR_WHITE,         10,    70,    42,    53, STR_NETWORK_GAME_NAME,            STR_NETWORK_GAME_NAME_TIP},        // NGWW_NAME
    00784 { WWT_PUSHTXTBTN,   RESIZE_NONE,   COLOUR_WHITE,         71,   150,    42,    53, STR_NETWORK_CLIENTS_CAPTION,      STR_NETWORK_CLIENTS_CAPTION_TIP},  // NGWW_CLIENTS
    00785 { WWT_PUSHTXTBTN,   RESIZE_NONE,   COLOUR_WHITE,         71,   150,    42,    53, STR_NETWORK_MAP_SIZE_CAPTION,     STR_NETWORK_MAP_SIZE_CAPTION_TIP}, // NGWW_MAPSIZE
    00786 { WWT_PUSHTXTBTN,   RESIZE_NONE,   COLOUR_WHITE,         71,   130,    42,    53, STR_NETWORK_DATE_CAPTION,         STR_NETWORK_DATE_CAPTION_TIP},     // NGWW_DATE
    00787 { WWT_PUSHTXTBTN,   RESIZE_NONE,   COLOUR_WHITE,         71,   130,    42,    53, STR_NETWORK_YEARS_CAPTION,        STR_NETWORK_YEARS_CAPTION_TIP},    // NGWW_YEARS
    00788 { WWT_PUSHTXTBTN,   RESIZE_LR,     COLOUR_WHITE,        151,   190,    42,    53, STR_EMPTY,                        STR_NETWORK_INFO_ICONS_TIP},       // NGWW_INFO
    00789
    00790 {     WWT_MATRIX,   RESIZE_RB,     COLOUR_LIGHT_BLUE,    10,   190,    54,   208, (11 << 8) + 1,                    STR_NETWORK_CLICK_GAME_TO_SELECT}, // NGWW_MATRIX
    00791 {  WWT_SCROLLBAR,   RESIZE_LRB,    COLOUR_LIGHT_BLUE,   191,   202,    42,   208, 0x0,                              STR_0190_SCROLL_BAR_SCROLLS_LIST}, // NGWW_SCROLLBAR
    00792 {       WWT_TEXT,   RESIZE_RTB,    COLOUR_LIGHT_BLUE,    10,   190,   211,   222, STR_NETWORK_LAST_JOINED_SERVER,   STR_NULL},                         // NGWW_LASTJOINED_LABEL
    00793 {      WWT_PANEL,   RESIZE_RTB,    COLOUR_LIGHT_BLUE,    10,   190,   223,   236, 0x0,                              STR_NETWORK_CLICK_TO_SELECT_LAST}, // NGWW_LASTJOINED
    00794
    00795 /* RIGHT SIDE */
    00796 {      WWT_PANEL,   RESIZE_LRB,    COLOUR_LIGHT_BLUE,   210,   440,    42,   236, 0x0,                              STR_NULL},                         // NGWW_DETAILS
    00797
    00798 { WWT_PUSHTXTBTN,   RESIZE_LRTB,   COLOUR_WHITE,        215,   315,   215,   226, STR_NETWORK_JOIN_GAME,            STR_NULL},                         // NGWW_JOIN
    00799 { WWT_PUSHTXTBTN,   RESIZE_LRTB,   COLOUR_WHITE,        330,   435,   215,   226, STR_NETWORK_REFRESH,              STR_NETWORK_REFRESH_TIP},          // NGWW_REFRESH
    00800
    00801 { WWT_PUSHTXTBTN,   RESIZE_LRTB,   COLOUR_WHITE,        330,   435,   197,   208, STR_NEWGRF_SETTINGS_BUTTON,       STR_NULL},                         // NGWW_NEWGRF
    00802
    00803 /* BOTTOM */
    00804 { WWT_PUSHTXTBTN,   RESIZE_TB,     COLOUR_WHITE,         10,   110,   246,   257, STR_NETWORK_FIND_SERVER,          STR_NETWORK_FIND_SERVER_TIP},      // NGWW_FIND
    00805 { WWT_PUSHTXTBTN,   RESIZE_TB,     COLOUR_WHITE,        118,   218,   246,   257, STR_NETWORK_ADD_SERVER,           STR_NETWORK_ADD_SERVER_TIP},       // NGWW_ADD
    00806 { WWT_PUSHTXTBTN,   RESIZE_TB,     COLOUR_WHITE,        226,   326,   246,   257, STR_NETWORK_START_SERVER,         STR_NETWORK_START_SERVER_TIP},     // NGWW_START
    00807 { WWT_PUSHTXTBTN,   RESIZE_TB,     COLOUR_WHITE,        334,   434,   246,   257, STR_012E_CANCEL,                  STR_NULL},                         // NGWW_CANCEL
    00808
    00809 {  WWT_RESIZEBOX,   RESIZE_LRTB,   COLOUR_LIGHT_BLUE,   438,   449,   252,   263, 0x0,                              STR_RESIZE_BUTTON },
    00810
    00811 {   WIDGETS_END},
    00812 };
    00813
    00814 static const WindowDesc _network_game_window_desc = {
    00815   WDP_CENTER, WDP_CENTER, 450, 264, 780, 264,
    00816   WC_NETWORK_WINDOW, WC_NONE,
    00817   WDF_STD_TOOLTIPS | WDF_DEF_WIDGET | WDF_STD_BTN | WDF_UNCLICK_BUTTONS | WDF_RESIZABLE,
    00818   _network_game_window_widgets,
    00819 };
    00820
    00821 void ShowNetworkGameWindow()
    00822 {
    00823   static bool first = true;
    00824   DeleteWindowById(WC_NETWORK_WINDOW, 0);
    00825
    00826   /* Only show once */
    00827   if (first) {
    00828     char * const *srv;
    00829
    00830     first = false;
    00831     // add all servers from the config file to our list
    00832     for (srv = &_network_host_list[0]; srv != endof(_network_host_list) && *srv != NULL; srv++) {
    00833       NetworkAddServer(*srv);
    00834     }
    00835   }
    00836
    00837   new NetworkGameWindow(&_network_game_window_desc);
    00838 }
    00839
    00840 enum {
    00841   NSSWND_START = 64,
    00842   NSSWND_ROWSIZE = 12
    00843 };
    00844
    00846 enum NetworkStartServerWidgets {
    00847   NSSW_CLOSE           =  0,
    00848   NSSW_GAMENAME        =  4,
    00849   NSSW_SETPWD          =  5,
    00850   NSSW_SELMAP          =  7,
    00851   NSSW_CONNTYPE_BTN    = 10,
    00852   NSSW_CLIENTS_BTND    = 12,
    00853   NSSW_CLIENTS_TXT     = 13,
    00854   NSSW_CLIENTS_BTNU    = 14,
    00855   NSSW_COMPANIES_BTND  = 16,
    00856   NSSW_COMPANIES_TXT   = 17,
    00857   NSSW_COMPANIES_BTNU  = 18,
    00858   NSSW_SPECTATORS_BTND = 20,
    00859   NSSW_SPECTATORS_TXT  = 21,
    00860   NSSW_SPECTATORS_BTNU = 22,
    00861   NSSW_LANGUAGE_BTN    = 24,
    00862   NSSW_START           = 25,
    00863   NSSW_LOAD            = 26,
    00864   NSSW_CANCEL          = 27,
    00865 };
    00866
    00867 struct NetworkStartServerWindow : public QueryStringBaseWindow {
    00868   byte field;
    00869   FiosItem *map;
    00870   byte widget_id;
    00871
    00872   NetworkStartServerWindow(const WindowDesc *desc) : QueryStringBaseWindow(NETWORK_NAME_LENGTH, desc)
    00873   {
    00874     ttd_strlcpy(this->edit_str_buf, _settings_client.network.server_name, this->edit_str_size);
    00875
    00876     _saveload_mode = SLD_NEW_GAME;
    00877     BuildFileList();
    00878     this->vscroll.cap = 12;
    00879     this->vscroll.count = _fios_items.Length() + 1;
    00880
    00881     this->afilter = CS_ALPHANUMERAL;
    00882     InitializeTextBuffer(&this->text, this->edit_str_buf, this->edit_str_size, 160);
    00883     this->SetFocusedWidget(NSSW_GAMENAME);
    00884
    00885     this->field = NSSW_GAMENAME;
    00886
    00887     this->FindWindowPlacementAndResize(desc);
    00888   }
    00889
    00890   virtual void OnPaint()
    00891   {
    00892     int y = NSSWND_START;
    00893     const FiosItem *item;
    00894
    00895     /* draw basic widgets */
    00896     SetDParam(1, _connection_types_dropdown[_settings_client.network.server_advertise]);
    00897     SetDParam(2, _settings_client.network.max_clients);
    00898     SetDParam(3, _settings_client.network.max_companies);
    00899     SetDParam(4, _settings_client.network.max_spectators);
    00900     SetDParam(5, STR_NETWORK_LANG_ANY + _settings_client.network.server_lang);
    00901     this->DrawWidgets();
    00902
    00903     /* editbox to set game name */
    00904     this->DrawEditBox(NSSW_GAMENAME);
    00905
    00906     /* if password is set, draw red '*' next to 'Set password' button */
    00907     if (!StrEmpty(_settings_client.network.server_password)) DoDrawString("*", 408, 23, TC_RED);
    00908
    00909     /* draw list of maps */
    00910     GfxFillRect(11, 63, 258, 215, 0xD7);  // black background of maps list
    00911
    00912     for (uint pos = this->vscroll.pos; pos < _fios_items.Length() + 1; pos++) {
    00913       item = _fios_items.Get(pos - 1);
    00914       if (item == this->map || (pos == 0 && this->map == NULL))
    00915         GfxFillRect(11, y - 1, 258, y + 10, 155); // show highlighted item with a different colour
    00916
    00917       if (pos == 0) {
    00918         DrawString(14, y, STR_4010_GENERATE_RANDOM_NEW_GAME, TC_DARK_GREEN);
    00919       } else {
    00920         DoDrawString(item->title, 14, y, _fios_colours[item->type] );
    00921       }
    00922       y += NSSWND_ROWSIZE;
    00923
    00924       if (y >= this->vscroll.cap * NSSWND_ROWSIZE + NSSWND_START) break;
    00925     }
    00926   }
    00927
    00928   virtual void OnClick(Point pt, int widget)
    00929   {
    00930     this->field = widget;
    00931     switch (widget) {
    00932       case NSSW_CLOSE:  // Close 'X'
    00933       case NSSW_CANCEL: // Cancel button
    00934         ShowNetworkGameWindow();
    00935         break;
    00936
    00937       case NSSW_SETPWD: // Set password button
    00938         this->widget_id = NSSW_SETPWD;
    00939         SetDParamStr(0, _settings_client.network.server_password);
    00940         ShowQueryString(STR_JUST_RAW_STRING, STR_NETWORK_SET_PASSWORD, 20, 250, this, CS_ALPHANUMERAL, QSF_NONE);
    00941         break;
    00942
    00943       case NSSW_SELMAP: { // Select map
    00944         int y = (pt.y - NSSWND_START) / NSSWND_ROWSIZE;
    00945
    00946         y += this->vscroll.pos;
    00947         if (y >= this->vscroll.count) return;
    00948
    00949         this->map = (y == 0) ? NULL : _fios_items.Get(y - 1);
    00950         this->SetDirty();
    00951       } break;
    00952
    00953       case NSSW_CONNTYPE_BTN: // Connection type
    00954         ShowDropDownMenu(this, _connection_types_dropdown, _settings_client.network.server_advertise, NSSW_CONNTYPE_BTN, 0, 0); // do it for widget NSSW_CONNTYPE_BTN
    00955         break;
    00956
    00957       case NSSW_CLIENTS_BTND:    case NSSW_CLIENTS_BTNU:    // Click on up/down button for number of clients
    00958       case NSSW_COMPANIES_BTND:  case NSSW_COMPANIES_BTNU:  // Click on up/down button for number of companies
    00959       case NSSW_SPECTATORS_BTND: case NSSW_SPECTATORS_BTNU: // Click on up/down button for number of spectators
    00960         /* Don't allow too fast scrolling */
    00961         if ((this->flags4 & WF_TIMEOUT_MASK) <= WF_TIMEOUT_TRIGGER) {
    00962           this->HandleButtonClick(widget);
    00963           this->SetDirty();
    00964           switch (widget) {
    00965             default: NOT_REACHED();
    00966             case NSSW_CLIENTS_BTND: case NSSW_CLIENTS_BTNU:
    00967               _settings_client.network.max_clients    = Clamp(_settings_client.network.max_clients    + widget - NSSW_CLIENTS_TXT,    2, MAX_CLIENTS);
    00968               break;
    00969             case NSSW_COMPANIES_BTND: case NSSW_COMPANIES_BTNU:
    00970               _settings_client.network.max_companies  = Clamp(_settings_client.network.max_companies  + widget - NSSW_COMPANIES_TXT,  1, MAX_COMPANIES);
    00971               break;
    00972             case NSSW_SPECTATORS_BTND: case NSSW_SPECTATORS_BTNU:
    00973               _settings_client.network.max_spectators = Clamp(_settings_client.network.max_spectators + widget - NSSW_SPECTATORS_TXT, 0, MAX_CLIENTS);
    00974               break;
    00975           }
    00976         }
    00977         _left_button_clicked = false;
    00978         break;
    00979
    00980       case NSSW_CLIENTS_TXT:    // Click on number of clients
    00981         this->widget_id = NSSW_CLIENTS_TXT;
    00982         SetDParam(0, _settings_client.network.max_clients);
    00983         ShowQueryString(STR_CONFIG_SETTING_INT32, STR_NETWORK_NUMBER_OF_CLIENTS,    4, 50, this, CS_NUMERAL, QSF_NONE);
    00984         break;
    00985
    00986       case NSSW_COMPANIES_TXT:  // Click on number of companies
    00987         this->widget_id = NSSW_COMPANIES_TXT;
    00988         SetDParam(0, _settings_client.network.max_companies);
    00989         ShowQueryString(STR_CONFIG_SETTING_INT32, STR_NETWORK_NUMBER_OF_COMPANIES,  3, 50, this, CS_NUMERAL, QSF_NONE);
    00990         break;
    00991
    00992       case NSSW_SPECTATORS_TXT: // Click on number of spectators
    00993         this->widget_id = NSSW_SPECTATORS_TXT;
    00994         SetDParam(0, _settings_client.network.max_spectators);
    00995         ShowQueryString(STR_CONFIG_SETTING_INT32, STR_NETWORK_NUMBER_OF_SPECTATORS, 4, 50, this, CS_NUMERAL, QSF_NONE);
    00996         break;
    00997
    00998       case NSSW_LANGUAGE_BTN: { // Language
    00999         uint sel = 0;
    01000         for (uint i = 0; i < lengthof(_language_dropdown) - 1; i++) {
    01001           if (_language_dropdown[i] == STR_NETWORK_LANG_ANY + _settings_client.network.server_lang) {
    01002             sel = i;
    01003             break;
    01004           }
    01005         }
    01006         ShowDropDownMenu(this, _language_dropdown, sel, NSSW_LANGUAGE_BTN, 0, 0);
    01007       } break;
    01008
    01009       case NSSW_START: // Start game
    01010         _is_network_server = true;
    01011
    01012         if (this->map == NULL) { // start random new game
    01013           ShowGenerateLandscape();
    01014         } else { // load a scenario
    01015           char *name = FiosBrowseTo(this->map);
    01016           if (name != NULL) {
    01017             SetFiosType(this->map->type);
    01018             _file_to_saveload.filetype = FT_SCENARIO;
    01019             strecpy(_file_to_saveload.name, name, lastof(_file_to_saveload.name));
    01020             strecpy(_file_to_saveload.title, this->map->title, lastof(_file_to_saveload.title));
    01021
    01022             delete this;
    01023             SwitchMode(SM_START_SCENARIO);
    01024           }
    01025         }
    01026         break;
    01027
    01028       case NSSW_LOAD: // Load game
    01029         _is_network_server = true;
    01030         /* XXX - WC_NETWORK_WINDOW (this window) should stay, but if it stays, it gets
    01031         * copied all the elements of 'load game' and upon closing that, it segfaults */
    01032         delete this;
    01033         ShowSaveLoadDialog(SLD_LOAD_GAME);
    01034         break;
    01035     }
    01036   }
    01037
    01038   virtual void OnDropdownSelect(int widget, int index)
    01039   {
    01040     switch (widget) {
    01041       case NSSW_CONNTYPE_BTN:
    01042         _settings_client.network.server_advertise = (index != 0);
    01043         break;
    01044       case NSSW_LANGUAGE_BTN:
    01045         _settings_client.network.server_lang = _language_dropdown[index] - STR_NETWORK_LANG_ANY;
    01046         break;
    01047       default:
    01048         NOT_REACHED();
    01049     }
    01050
    01051     this->SetDirty();
    01052   }
    01053
    01054   virtual void OnMouseLoop()
    01055   {
    01056     if (this->field == NSSW_GAMENAME) this->HandleEditBox(NSSW_GAMENAME);
    01057   }
    01058
    01059   virtual EventState OnKeyPress(uint16 key, uint16 keycode)
    01060   {
    01061     EventState state = ES_NOT_HANDLED;
    01062     if (this->field == NSSW_GAMENAME) {
    01063       if (this->HandleEditBoxKey(NSSW_GAMENAME, key, keycode, state) == HEBR_CONFIRM) return state;
    01064
    01065       strecpy(_settings_client.network.server_name, this->text.buf, lastof(_settings_client.network.server_name));
    01066     }
    01067
    01068     return state;
    01069   }
    01070
    01071   virtual void OnQueryTextFinished(char *str)
    01072   {
    01073     if (str == NULL) return;
    01074
    01075     if (this->widget_id == NSSW_SETPWD) {
    01076       strecpy(_settings_client.network.server_password, str, lastof(_settings_client.network.server_password));
    01077     } else {
    01078       int32 value = atoi(str);
    01079       this->InvalidateWidget(this->widget_id);
    01080       switch (this->widget_id) {
    01081         default: NOT_REACHED();
    01082         case NSSW_CLIENTS_TXT:    _settings_client.network.max_clients    = Clamp(value, 2, MAX_CLIENTS); break;
    01083         case NSSW_COMPANIES_TXT:  _settings_client.network.max_companies  = Clamp(value, 1, MAX_COMPANIES); break;
    01084         case NSSW_SPECTATORS_TXT: _settings_client.network.max_spectators = Clamp(value, 0, MAX_CLIENTS); break;
    01085       }
    01086     }
    01087
    01088     this->SetDirty();
    01089   }
    01090 };
    01091
    01092 static const Widget _network_start_server_window_widgets[] = {
    01093 /* Window decoration and background panel */
    01094 {   WWT_CLOSEBOX,   RESIZE_NONE,   COLOUR_LIGHT_BLUE,     0,    10,     0,    13, STR_00C5,                           STR_018B_CLOSE_WINDOW },               // NSSW_CLOSE
    01095 {    WWT_CAPTION,   RESIZE_NONE,   COLOUR_LIGHT_BLUE,    11,   419,     0,    13, STR_NETWORK_START_GAME_WINDOW,      STR_NULL},
    01096 {      WWT_PANEL,   RESIZE_NONE,   COLOUR_LIGHT_BLUE,     0,   419,    14,   243, 0x0,                                STR_NULL},
    01097
    01098 /* Set game name and password widgets */
    01099 {       WWT_TEXT,   RESIZE_NONE,   COLOUR_LIGHT_BLUE,    10,    90,    22,    34, STR_NETWORK_NEW_GAME_NAME,          STR_NULL},
    01100 {    WWT_EDITBOX,   RESIZE_NONE,   COLOUR_LIGHT_BLUE,   100,   272,    22,    33, STR_NETWORK_NEW_GAME_NAME_OSKTITLE, STR_NETWORK_NEW_GAME_NAME_TIP},        // NSSW_GAMENAME
    01101 { WWT_PUSHTXTBTN,   RESIZE_NONE,   COLOUR_WHITE,        285,   405,    22,    33, STR_NETWORK_SET_PASSWORD,           STR_NETWORK_PASSWORD_TIP},             // NSSW_SETPWD
    01102
    01103 /* List of playable scenarios */
    01104 {       WWT_TEXT,   RESIZE_NONE,   COLOUR_LIGHT_BLUE,    10,   110,    43,    55, STR_NETWORK_SELECT_MAP,             STR_NULL},
    01105 {      WWT_INSET,   RESIZE_NONE,   COLOUR_LIGHT_BLUE,    10,   271,    62,   216, STR_NULL,                           STR_NETWORK_SELECT_MAP_TIP},           // NSSW_SELMAP
    01106 {  WWT_SCROLLBAR,   RESIZE_NONE,   COLOUR_LIGHT_BLUE,   259,   270,    63,   215, 0x0,                                STR_0190_SCROLL_BAR_SCROLLS_LIST},
    01107
    01108 /* Combo/selection boxes to control Connection Type / Max Clients / Max Companies / Max Observers / Language */
    01109 {       WWT_TEXT,   RESIZE_NONE,   COLOUR_LIGHT_BLUE,   280,   419,    63,    75, STR_NETWORK_CONNECTION,             STR_NULL},
    01110 { WWT_DROPDOWNIN,   RESIZE_NONE,   COLOUR_LIGHT_BLUE,   280,   410,    77,    88, STR_NETWORK_LAN_INTERNET_COMBO,     STR_NETWORK_CONNECTION_TIP},           // NSSW_CONNTYPE_BTN
    01111
    01112 {       WWT_TEXT,   RESIZE_NONE,   COLOUR_LIGHT_BLUE,   280,   419,    95,   107, STR_NETWORK_NUMBER_OF_CLIENTS,      STR_NULL},
    01113 {     WWT_IMGBTN,   RESIZE_NONE,   COLOUR_LIGHT_BLUE,   280,   291,   109,   120, SPR_ARROW_DOWN,                     STR_NETWORK_NUMBER_OF_CLIENTS_TIP},    // NSSW_CLIENTS_BTND
    01114 { WWT_PUSHTXTBTN,   RESIZE_NONE,   COLOUR_LIGHT_BLUE,   292,   397,   109,   120, STR_NETWORK_CLIENTS_SELECT,         STR_NETWORK_NUMBER_OF_CLIENTS_TIP},    // NSSW_CLIENTS_TXT
    01115 {     WWT_IMGBTN,   RESIZE_NONE,   COLOUR_LIGHT_BLUE,   398,   410,   109,   120, SPR_ARROW_UP,                       STR_NETWORK_NUMBER_OF_CLIENTS_TIP},    // NSSW_CLIENTS_BTNU
    01116
    01117 {       WWT_TEXT,   RESIZE_NONE,   COLOUR_LIGHT_BLUE,   280,   419,   127,   139, STR_NETWORK_NUMBER_OF_COMPANIES,    STR_NULL},
    01118 {     WWT_IMGBTN,   RESIZE_NONE,   COLOUR_LIGHT_BLUE,   280,   291,   141,   152, SPR_ARROW_DOWN,                     STR_NETWORK_NUMBER_OF_COMPANIES_TIP},  // NSSW_COMPANIES_BTND
    01119 { WWT_PUSHTXTBTN,   RESIZE_NONE,   COLOUR_LIGHT_BLUE,   292,   397,   141,   152, STR_NETWORK_COMPANIES_SELECT,       STR_NETWORK_NUMBER_OF_COMPANIES_TIP},  // NSSW_COMPANIES_TXT
    01120 {     WWT_IMGBTN,   RESIZE_NONE,   COLOUR_LIGHT_BLUE,   398,   410,   141,   152, SPR_ARROW_UP,                       STR_NETWORK_NUMBER_OF_COMPANIES_TIP},  // NSSW_COMPANIES_BTNU
    01121
    01122 {       WWT_TEXT,   RESIZE_NONE,   COLOUR_LIGHT_BLUE,   280,   419,   159,   171, STR_NETWORK_NUMBER_OF_SPECTATORS,   STR_NULL},
    01123 {     WWT_IMGBTN,   RESIZE_NONE,   COLOUR_LIGHT_BLUE,   280,   291,   173,   184, SPR_ARROW_DOWN,                     STR_NETWORK_NUMBER_OF_SPECTATORS_TIP}, // NSSW_SPECTATORS_BTND
    01124 { WWT_PUSHTXTBTN,   RESIZE_NONE,   COLOUR_LIGHT_BLUE,   292,   397,   173,   184, STR_NETWORK_SPECTATORS_SELECT,      STR_NETWORK_NUMBER_OF_SPECTATORS_TIP}, // NSSW_SPECTATORS_TXT
    01125 {     WWT_IMGBTN,   RESIZE_NONE,   COLOUR_LIGHT_BLUE,   398,   410,   173,   184, SPR_ARROW_UP,                       STR_NETWORK_NUMBER_OF_SPECTATORS_TIP}, // NSSW_SPECTATORS_BTNU
    01126
    01127 {       WWT_TEXT,   RESIZE_NONE,   COLOUR_LIGHT_BLUE,   280,   419,   191,   203, STR_NETWORK_LANGUAGE_SPOKEN,        STR_NULL},
    01128 { WWT_DROPDOWNIN,   RESIZE_NONE,   COLOUR_LIGHT_BLUE,   280,   410,   205,   216, STR_NETWORK_LANGUAGE_COMBO,         STR_NETWORK_LANGUAGE_TIP},             // NSSW_LANGUAGE_BTN
    01129
    01130 /* Buttons Start / Load / Cancel */
    01131 { WWT_PUSHTXTBTN,   RESIZE_NONE,   COLOUR_WHITE,         40,   140,   224,   235, STR_NETWORK_START_GAME,             STR_NETWORK_START_GAME_TIP},           // NSSW_START
    01132 { WWT_PUSHTXTBTN,   RESIZE_NONE,   COLOUR_WHITE,        150,   250,   224,   235, STR_NETWORK_LOAD_GAME,              STR_NETWORK_LOAD_GAME_TIP},            // NSSW_LOAD
    01133 { WWT_PUSHTXTBTN,   RESIZE_NONE,   COLOUR_WHITE,        260,   360,   224,   235, STR_012E_CANCEL,                    STR_NULL},                             // NSSW_CANCEL
    01134
    01135 {   WIDGETS_END},
    01136 };
    01137
    01138 static const WindowDesc _network_start_server_window_desc = {
    01139   WDP_CENTER, WDP_CENTER, 420, 244, 420, 244,
    01140   WC_NETWORK_WINDOW, WC_NONE,
    01141   WDF_STD_TOOLTIPS | WDF_DEF_WIDGET | WDF_UNCLICK_BUTTONS,
    01142   _network_start_server_window_widgets,
    01143 };
    01144
    01145 static void ShowNetworkStartServerWindow()
    01146 {
    01147   DeleteWindowById(WC_NETWORK_WINDOW, 0);
    01148
    01149   new NetworkStartServerWindow(&_network_start_server_window_desc);
    01150 }
    01151
    01153 enum NetworkLobbyWindowWidgets {
    01154   NLWW_CLOSE    =  0,
    01155   NLWW_MATRIX   =  5,
    01156   NLWW_DETAILS  =  7,
    01157   NLWW_JOIN     =  8,
    01158   NLWW_NEW      =  9,
    01159   NLWW_SPECTATE = 10,
    01160   NLWW_REFRESH  = 11,
    01161   NLWW_CANCEL   = 12,
    01162 };
    01163
    01164 struct NetworkLobbyWindow : public Window {
    01165   CompanyID company;
    01166   NetworkGameList *server;
    01167   NetworkCompanyInfo company_info[MAX_COMPANIES];
    01168
    01169   NetworkLobbyWindow(const WindowDesc *desc, NetworkGameList *ngl) :
    01170       Window(desc), company(INVALID_COMPANY), server(ngl)
    01171   {
    01172     this->vscroll.cap = 10;
    01173
    01174     this->FindWindowPlacementAndResize(desc);
    01175   }
    01176
    01177   CompanyID NetworkLobbyFindCompanyIndex(byte pos)
    01178   {
    01179     /* Scroll through all this->company_info and get the 'pos' item that is not empty */
    01180     for (CompanyID i = COMPANY_FIRST; i < MAX_COMPANIES; i++) {
    01181       if (!StrEmpty(this->company_info[i].company_name)) {
    01182         if (pos-- == 0) return i;
    01183       }
    01184     }
    01185
    01186     return COMPANY_FIRST;
    01187   }
    01188
    01189   virtual void OnPaint()
    01190   {
    01191     const NetworkGameInfo *gi = &this->server->info;
    01192     int y = NET_PRC__OFFSET_TOP_WIDGET_COMPANY, pos;
    01193
    01194     /* Join button is disabled when no company is selected and for AI companies*/
    01195     this->SetWidgetDisabledState(NLWW_JOIN, this->company == INVALID_COMPANY || GetLobbyCompanyInfo(this->company)->ai);
    01196     /* Cannot start new company if there are too many */
    01197     this->SetWidgetDisabledState(NLWW_NEW, gi->companies_on >= gi->companies_max);
    01198     /* Cannot spectate if there are too many spectators */
    01199     this->SetWidgetDisabledState(NLWW_SPECTATE, gi->spectators_on >= gi->spectators_max);
    01200
    01201     /* Draw window widgets */
    01202     SetDParamStr(0, gi->server_name);
    01203     this->DrawWidgets();
    01204
    01205     SetVScrollCount(this, gi->companies_on);
    01206
    01207     /* Draw company list */
    01208     pos = this->vscroll.pos;
    01209     while (pos < gi->companies_on) {
    01210       byte company = NetworkLobbyFindCompanyIndex(pos);
    01211       bool income = false;
    01212       if (this->company == company) {
    01213         GfxFillRect(11, y - 1, 154, y + 10, 10); // show highlighted item with a different colour
    01214       }
    01215
    01216       DoDrawStringTruncated(this->company_info[company].company_name, 13, y, TC_BLACK, 135 - 13);
    01217       if (this->company_info[company].use_password != 0) DrawSprite(SPR_LOCK, PAL_NONE, 135, y);
    01218
    01219       /* If the company's income was positive puts a green dot else a red dot */
    01220       if (this->company_info[company].income >= 0) income = true;
    01221       DrawSprite(SPR_BLOT, income ? PALETTE_TO_GREEN : PALETTE_TO_RED, 145, y);
    01222
    01223       pos++;
    01224       y += NET_PRC__SIZE_OF_ROW;
    01225       if (pos >= this->vscroll.pos + this->vscroll.cap) break;
    01226     }
    01227
    01228     /* Draw info about selected company when it is selected in the left window */
    01229     GfxFillRect(174, 39, 403, 75, 157);
    01230     DrawStringCentered(290, 50, STR_NETWORK_COMPANY_INFO, TC_FROMSTRING);
    01231     if (this->company != INVALID_COMPANY && !StrEmpty(this->company_info[this->company].company_name)) {
    01232       const uint x = 183;
    01233       const uint trunc_width = this->widget[NLWW_DETAILS].right - x;
    01234       y = 80;
    01235
    01236       SetDParam(0, gi->clients_on);
    01237       SetDParam(1, gi->clients_max);
    01238       SetDParam(2, gi->companies_on);
    01239       SetDParam(3, gi->companies_max);
    01240       DrawString(x, y, STR_NETWORK_CLIENTS, TC_GOLD);
    01241       y += 10;
    01242
    01243       SetDParamStr(0, this->company_info[this->company].company_name);
    01244       DrawStringTruncated(x, y, STR_NETWORK_COMPANY_NAME, TC_GOLD, trunc_width);
    01245       y += 10;
    01246
    01247       SetDParam(0, this->company_info[this->company].inaugurated_year);
    01248       DrawString(x, y, STR_NETWORK_INAUGURATION_YEAR, TC_GOLD); // inauguration year
    01249       y += 10;
    01250
    01251       SetDParam(0, this->company_info[this->company].company_value);
    01252       DrawString(x, y, STR_NETWORK_VALUE, TC_GOLD); // company value
    01253       y += 10;
    01254
    01255       SetDParam(0, this->company_info[this->company].money);
    01256       DrawString(x, y, STR_NETWORK_CURRENT_BALANCE, TC_GOLD); // current balance
    01257       y += 10;
    01258
    01259       SetDParam(0, this->company_info[this->company].income);
    01260       DrawString(x, y, STR_NETWORK_LAST_YEARS_INCOME, TC_GOLD); // last year's income
    01261       y += 10;
    01262
    01263       SetDParam(0, this->company_info[this->company].performance);
    01264       DrawString(x, y, STR_NETWORK_PERFORMANCE, TC_GOLD); // performance
    01265       y += 10;
    01266
    01267       SetDParam(0, this->company_info[this->company].num_vehicle[0]);
    01268       SetDParam(1, this->company_info[this->company].num_vehicle[1]);
    01269       SetDParam(2, this->company_info[this->company].num_vehicle[2]);
    01270       SetDParam(3, this->company_info[this->company].num_vehicle[3]);
    01271       SetDParam(4, this->company_info[this->company].num_vehicle[4]);
    01272       DrawString(x, y, STR_NETWORK_VEHICLES, TC_GOLD); // vehicles
    01273       y += 10;
    01274
    01275       SetDParam(0, this->company_info[this->company].num_station[0]);
    01276       SetDParam(1, this->company_info[this->company].num_station[1]);
    01277       SetDParam(2, this->company_info[this->company].num_station[2]);
    01278       SetDParam(3, this->company_info[this->company].num_station[3]);
    01279       SetDParam(4, this->company_info[this->company].num_station[4]);
    01280       DrawString(x, y, STR_NETWORK_STATIONS, TC_GOLD); // stations
    01281       y += 10;
    01282
    01283       SetDParamStr(0, this->company_info[this->company].clients);
    01284       DrawStringTruncated(x, y, STR_NETWORK_PLAYERS, TC_GOLD, trunc_width); // players
    01285     }
    01286   }
    01287
    01288   virtual void OnClick(Point pt, int widget)
    01289   {
    01290     switch (widget) {
    01291       case NLWW_CLOSE:    // Close 'X'
    01292       case NLWW_CANCEL:   // Cancel button
    01293         ShowNetworkGameWindow();
    01294         break;
    01295
    01296       case NLWW_MATRIX: { // Company list
    01297         uint32 id_v = (pt.y - NET_PRC__OFFSET_TOP_WIDGET_COMPANY) / NET_PRC__SIZE_OF_ROW;
    01298
    01299         if (id_v >= this->vscroll.cap) break;
    01300
    01301         id_v += this->vscroll.pos;
    01302         this->company = (id_v >= this->server->info.companies_on) ? INVALID_COMPANY : NetworkLobbyFindCompanyIndex(id_v);
    01303         this->SetDirty();
    01304       } break;
    01305
    01306       case NLWW_JOIN:     // Join company
    01307         /* Button can be clicked only when it is enabled */
    01308         _network_playas = this->company;
    01309         NetworkClientConnectGame(NetworkAddress(_settings_client.network.last_host, _settings_client.network.last_port));
    01310         break;
    01311
    01312       case NLWW_NEW:      // New company
    01313         _network_playas = COMPANY_NEW_COMPANY;
    01314         NetworkClientConnectGame(NetworkAddress(_settings_client.network.last_host, _settings_client.network.last_port));
    01315         break;
    01316
    01317       case NLWW_SPECTATE: // Spectate game
    01318         _network_playas = COMPANY_SPECTATOR;
    01319         NetworkClientConnectGame(NetworkAddress(_settings_client.network.last_host, _settings_client.network.last_port));
    01320         break;
    01321
    01322       case NLWW_REFRESH:  // Refresh
    01323         NetworkTCPQueryServer(NetworkAddress(_settings_client.network.last_host, _settings_client.network.last_port)); // company info
    01324         NetworkUDPQueryServer(NetworkAddress(_settings_client.network.last_host, _settings_client.network.last_port)); // general data
    01325         /* Clear the information so removed companies don't remain */
    01326         memset(this->company_info, 0, sizeof(company_info));
    01327         break;
    01328     }
    01329   }
    01330
    01331   virtual void OnDoubleClick(Point pt, int widget)
    01332   {
    01333     if (widget == NLWW_MATRIX) {
    01334       /* is the Join button enabled? */
    01335       if (!this->IsWidgetDisabled(NLWW_JOIN)) this->OnClick(pt, NLWW_JOIN);
    01336     }
    01337   }
    01338 };
    01339
    01340 static const Widget _network_lobby_window_widgets[] = {
    01341 {   WWT_CLOSEBOX,   RESIZE_NONE,   COLOUR_LIGHT_BLUE,     0,    10,     0,    13, STR_00C5,                    STR_018B_CLOSE_WINDOW },           // NLWW_CLOSE
    01342 {    WWT_CAPTION,   RESIZE_NONE,   COLOUR_LIGHT_BLUE,    11,   419,     0,    13, STR_NETWORK_GAME_LOBBY,      STR_NULL},
    01343 {      WWT_PANEL,   RESIZE_NONE,   COLOUR_LIGHT_BLUE,     0,   419,    14,   234, 0x0,                         STR_NULL},
    01344 {       WWT_TEXT,   RESIZE_NONE,   COLOUR_LIGHT_BLUE,    10,   419,    22,    34, STR_NETWORK_PREPARE_TO_JOIN, STR_NULL},
    01345
    01346 /* company list */
    01347 {      WWT_PANEL,   RESIZE_NONE,   COLOUR_WHITE,         10,   155,    38,    49, 0x0,                         STR_NULL},
    01348 {     WWT_MATRIX,   RESIZE_NONE,   COLOUR_LIGHT_BLUE,    10,   155,    50,   190, (10 << 8) + 1,               STR_NETWORK_COMPANY_LIST_TIP},     // NLWW_MATRIX
    01349 {  WWT_SCROLLBAR,   RESIZE_NONE,   COLOUR_LIGHT_BLUE,   156,   167,    38,   190, 0x0,                         STR_0190_SCROLL_BAR_SCROLLS_LIST},
    01350
    01351 /* company info */
    01352 {      WWT_PANEL,   RESIZE_NONE,   COLOUR_LIGHT_BLUE,   173,   404,    38,   190, 0x0,                         STR_NULL},                         // NLWW_DETAILS
    01353
    01354 /* buttons */
    01355 { WWT_PUSHTXTBTN,   RESIZE_NONE,   COLOUR_WHITE,         10,   151,   200,   211, STR_NETWORK_JOIN_COMPANY,    STR_NETWORK_JOIN_COMPANY_TIP},     // NLWW_JOIN
    01356 { WWT_PUSHTXTBTN,   RESIZE_NONE,   COLOUR_WHITE,         10,   151,   215,   226, STR_NETWORK_NEW_COMPANY,     STR_NETWORK_NEW_COMPANY_TIP},      // NLWW_NEW
    01357 { WWT_PUSHTXTBTN,   RESIZE_NONE,   COLOUR_WHITE,        158,   268,   200,   211, STR_NETWORK_SPECTATE_GAME,   STR_NETWORK_SPECTATE_GAME_TIP},    // NLWW_SPECTATE
    01358 { WWT_PUSHTXTBTN,   RESIZE_NONE,   COLOUR_WHITE,        158,   268,   215,   226, STR_NETWORK_REFRESH,         STR_NETWORK_REFRESH_TIP},          // NLWW_REFRESH
    01359 { WWT_PUSHTXTBTN,   RESIZE_NONE,   COLOUR_WHITE,        278,   388,   200,   211, STR_012E_CANCEL,             STR_NULL},                         // NLWW_CANCEL
    01360
    01361 {   WIDGETS_END},
    01362 };
    01363
    01364 static const WindowDesc _network_lobby_window_desc = {
    01365   WDP_CENTER, WDP_CENTER, 420, 235, 420, 235,
    01366   WC_NETWORK_WINDOW, WC_NONE,
    01367   WDF_STD_TOOLTIPS | WDF_DEF_WIDGET | WDF_UNCLICK_BUTTONS,
    01368   _network_lobby_window_widgets,
    01369 };
    01370
    01371 /* Show the networklobbywindow with the selected server
    01372  * @param ngl Selected game pointer which is passed to the new window */
    01373 static void ShowNetworkLobbyWindow(NetworkGameList *ngl)
    01374 {
    01375   DeleteWindowById(WC_NETWORK_WINDOW, 0);
    01376
    01377   NetworkTCPQueryServer(NetworkAddress(_settings_client.network.last_host, _settings_client.network.last_port)); // company info
    01378   NetworkUDPQueryServer(NetworkAddress(_settings_client.network.last_host, _settings_client.network.last_port)); // general data
    01379
    01380   new NetworkLobbyWindow(&_network_lobby_window_desc, ngl);
    01381 }
    01382
    01388 NetworkCompanyInfo *GetLobbyCompanyInfo(CompanyID company)
    01389 {
    01390   NetworkLobbyWindow *lobby = dynamic_cast<NetworkLobbyWindow*>(FindWindowById(WC_NETWORK_WINDOW, 0));
    01391   return (lobby != NULL && company < MAX_COMPANIES) ? &lobby->company_info[company] : NULL;
    01392 }
    01393
    01394 // The window below gives information about the connected clients
    01395 //  and also makes able to give money to them, kick them (if server)
    01396 //  and stuff like that.
    01397
    01398 extern void DrawCompanyIcon(CompanyID cid, int x, int y);
    01399
    01400 // Every action must be of this form
    01401 typedef void ClientList_Action_Proc(byte client_no);
    01402
    01403 // Max 10 actions per client
    01404 #define MAX_CLIENTLIST_ACTION 10
    01405
    01406 enum {
    01407   CLNWND_OFFSET = 16,
    01408   CLNWND_ROWSIZE = 10
    01409 };
    01410
    01411 static const Widget _client_list_widgets[] = {
    01412 {   WWT_CLOSEBOX,   RESIZE_NONE,  COLOUR_GREY,     0,    10,     0,    13, STR_00C5,                 STR_018B_CLOSE_WINDOW},
    01413 {    WWT_CAPTION,   RESIZE_NONE,  COLOUR_GREY,    11,   237,     0,    13, STR_NETWORK_CLIENT_LIST,  STR_018C_WINDOW_TITLE_DRAG_THIS},
    01414 {  WWT_STICKYBOX,   RESIZE_NONE,  COLOUR_GREY,   238,   249,     0,    13, STR_NULL,                 STR_STICKY_BUTTON},
    01415
    01416 {      WWT_PANEL,   RESIZE_NONE,  COLOUR_GREY,     0,   249,    14,    14 + CLNWND_ROWSIZE + 1, 0x0, STR_NULL},
    01417 {   WIDGETS_END},
    01418 };
    01419
    01420 static const Widget _client_list_popup_widgets[] = {
    01421 {      WWT_PANEL,   RESIZE_NONE,  COLOUR_GREY,     0,   99,     0,     0,     0, STR_NULL},
    01422 {   WIDGETS_END},
    01423 };
    01424
    01425 static const WindowDesc _client_list_desc = {
    01426   WDP_AUTO, WDP_AUTO, 250, 1, 250, 1,
    01427   WC_CLIENT_LIST, WC_NONE,
    01428   WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET | WDF_STICKY_BUTTON,
    01429   _client_list_widgets,
    01430 };
    01431
    01432 // Finds the Xth client-info that is active
    01433 static const NetworkClientInfo *NetworkFindClientInfo(byte client_no)
    01434 {
    01435   const NetworkClientInfo *ci;
    01436
    01437   FOR_ALL_CLIENT_INFOS(ci) {
    01438     if (client_no == 0) return ci;
    01439     client_no--;
    01440   }
    01441
    01442   return NULL;
    01443 }
    01444
    01445 // Here we start to define the options out of the menu
    01446 static void ClientList_Kick(byte client_no)
    01447 {
    01448   if (client_no < MAX_CLIENTS) {
    01449     SEND_COMMAND(PACKET_SERVER_ERROR)(GetNetworkClientSocket(client_no), NETWORK_ERROR_KICKED);
    01450   }
    01451 }
    01452
    01453 static void ClientList_Ban(byte client_no)
    01454 {
    01455   uint32 ip = NetworkFindClientInfo(client_no)->client_ip;
    01456
    01457   for (uint i = 0; i < lengthof(_network_ban_list); i++) {
    01458     if (_network_ban_list[i] == NULL) {
    01459       _network_ban_list[i] = strdup(inet_ntoa(*(struct in_addr *)&ip));
    01460       break;
    01461     }
    01462   }
    01463
    01464   if (client_no < MAX_CLIENTS) {
    01465     SEND_COMMAND(PACKET_SERVER_ERROR)(GetNetworkClientSocket(client_no), NETWORK_ERROR_KICKED);
    01466   }
    01467 }
    01468
    01469 static void ClientList_GiveMoney(byte client_no)
    01470 {
    01471   if (NetworkFindClientInfo(client_no) != NULL) {
    01472     ShowNetworkGiveMoneyWindow(NetworkFindClientInfo(client_no)->client_playas);
    01473   }
    01474 }
    01475
    01476 static void ClientList_SpeakToClient(byte client_no)
    01477 {
    01478   if (NetworkFindClientInfo(client_no) != NULL) {
    01479     ShowNetworkChatQueryWindow(DESTTYPE_CLIENT, NetworkFindClientInfo(client_no)->client_id);
    01480   }
    01481 }
    01482
    01483 static void ClientList_SpeakToCompany(byte client_no)
    01484 {
    01485   if (NetworkFindClientInfo(client_no) != NULL) {
    01486     ShowNetworkChatQueryWindow(DESTTYPE_TEAM, NetworkFindClientInfo(client_no)->client_playas);
    01487   }
    01488 }
    01489
    01490 static void ClientList_SpeakToAll(byte client_no)
    01491 {
    01492   ShowNetworkChatQueryWindow(DESTTYPE_BROADCAST, 0);
    01493 }
    01494
    01495 static void ClientList_None(byte client_no)
    01496 {
    01497   /* No action ;) */
    01498 }
    01499
    01500
    01501
    01502 struct NetworkClientListPopupWindow : Window {
    01503   int sel_index;
    01504   int client_no;
    01505   char action[MAX_CLIENTLIST_ACTION][50];
    01506   ClientList_Action_Proc *proc[MAX_CLIENTLIST_ACTION];
    01507
    01508   NetworkClientListPopupWindow(int x, int y, const Widget *widgets, int client_no) :
    01509       Window(x, y, 150, 100, WC_TOOLBAR_MENU, widgets),
    01510       sel_index(0), client_no(client_no)
    01511   {
    01512     /*
    01513      * Fill the actions this client has.
    01514      * Watch is, max 50 chars long!
    01515      */
    01516
    01517     const NetworkClientInfo *ci = NetworkFindClientInfo(client_no);
    01518
    01519     int i = 0;
    01520     if (_network_own_client_id != ci->client_id) {
    01521       GetString(this->action[i], STR_NETWORK_CLIENTLIST_SPEAK_TO_CLIENT, lastof(this->action[i]));
    01522       this->proc[i++] = &ClientList_SpeakToClient;
    01523     }
    01524
    01525     if (IsValidCompanyID(ci->client_playas) || ci->client_playas == COMPANY_SPECTATOR) {
    01526       GetString(this->action[i], STR_NETWORK_CLIENTLIST_SPEAK_TO_COMPANY, lastof(this->action[i]));
    01527       this->proc[i++] = &ClientList_SpeakToCompany;
    01528     }
    01529     GetString(this->action[i], STR_NETWORK_CLIENTLIST_SPEAK_TO_ALL, lastof(this->action[i]));
    01530     this->proc[i++] = &ClientList_SpeakToAll;
    01531
    01532     if (_network_own_client_id != ci->client_id) {
    01533       /* We are no spectator and the company we want to give money to is no spectator and money gifts are allowed */
    01534       if (IsValidCompanyID(_network_playas) && IsValidCompanyID(ci->client_playas) && _settings_game.economy.give_money) {
    01535         GetString(this->action[i], STR_NETWORK_CLIENTLIST_GIVE_MONEY, lastof(this->action[i]));
    01536         this->proc[i++] = &ClientList_GiveMoney;
    01537       }
    01538     }
    01539
    01540     /* A server can kick clients (but not himself) */
    01541     if (_network_server && _network_own_client_id != ci->client_id) {
    01542       GetString(this->action[i], STR_NETWORK_CLIENTLIST_KICK, lastof(this->action[i]));
    01543       this->proc[i++] = &ClientList_Kick;
    01544
    01545       seprintf(this->action[i], lastof(this->action[i]), "Ban"); // XXX GetString?
    01546       this->proc[i++] = &ClientList_Ban;
    01547     }
    01548
    01549     if (i == 0) {
    01550       GetString(this->action[i], STR_NETWORK_CLIENTLIST_NONE, lastof(this->action[i]));
    01551       this->proc[i++] = &ClientList_None;
    01552     }
    01553
    01554     /* Calculate the height */
    01555     int h = ClientListPopupHeight();
    01556
    01557     /* Allocate the popup */
    01558     this->widget[0].bottom = this->widget[0].top + h;
    01559     this->widget[0].right = this->widget[0].left + 150;
    01560
    01561     this->flags4 &= ~WF_WHITE_BORDER_MASK;
    01562
    01563     this->FindWindowPlacementAndResize(150, h + 1);
    01564   }
    01565
    01569   void HandleClientListPopupClick(byte index)
    01570   {
    01571     /* A click on the Popup of the ClientList.. handle the command */
    01572     if (index < MAX_CLIENTLIST_ACTION && this->proc[index] != NULL) {
    01573       this->proc[index](this->client_no);
    01574     }
    01575   }
    01576
    01580   uint ClientListPopupHeight()
    01581   {
    01582     int num = 0;
    01583
    01584     // Find the amount of actions
    01585     for (int i = 0; i < MAX_CLIENTLIST_ACTION; i++) {
    01586       if (this->action[i][0] == '\0') continue;
    01587       if (this->proc[i] == NULL) continue;
    01588       num++;
    01589     }
    01590
    01591     num *= CLNWND_ROWSIZE;
    01592
    01593     return num + 1;
    01594   }
    01595
    01596
    01597   virtual void OnPaint()
    01598   {
    01599     this->DrawWidgets();
    01600
    01601     /* Draw the actions */
    01602     int sel = this->sel_index;
    01603     int y = 1;
    01604     for (int i = 0; i < MAX_CLIENTLIST_ACTION; i++, y += CLNWND_ROWSIZE) {
    01605       if (this->action[i][0] == '\0') continue;
    01606       if (this->proc[i] == NULL) continue;
    01607
    01608       TextColour colour;
    01609       if (sel-- == 0) { // Selected item, highlight it
    01610         GfxFillRect(1, y, 150 - 2, y + CLNWND_ROWSIZE - 1, 0);
    01611         colour = TC_WHITE;
    01612       } else {
    01613         colour = TC_BLACK;
    01614       }
    01615
    01616       DoDrawString(this->action[i], 4, y, colour);
    01617     }
    01618   }
    01619
    01620   virtual void OnMouseLoop()
    01621   {
    01622     /* We selected an action */
    01623     int index = (_cursor.pos.y - this->top) / CLNWND_ROWSIZE;
    01624
    01625     if (_left_button_down) {
    01626       if (index == -1 || index == this->sel_index) return;
    01627
    01628       this->sel_index = index;
    01629       this->SetDirty();
    01630     } else {
    01631       if (index >= 0 && _cursor.pos.y >= this->top) {
    01632         HandleClientListPopupClick(index);
    01633       }
    01634
    01635       DeleteWindowById(WC_TOOLBAR_MENU, 0);
    01636     }
    01637   }
    01638 };
    01639
    01643 static void PopupClientList(int client_no, int x, int y)
    01644 {
    01645   DeleteWindowById(WC_TOOLBAR_MENU, 0);
    01646
    01647   if (NetworkFindClientInfo(client_no) == NULL) return;
    01648
    01649   new NetworkClientListPopupWindow(x, y, _client_list_popup_widgets, client_no);
    01650 }
    01651
    01655 struct NetworkClientListWindow : Window
    01656 {
    01657   int selected_item;
    01658   int selected_y;
    01659
    01660   NetworkClientListWindow(const WindowDesc *desc, WindowNumber window_number) :
    01661       Window(desc, window_number),
    01662       selected_item(-1),
    01663       selected_y(0)
    01664   {
    01665     this->FindWindowPlacementAndResize(desc);
    01666   }
    01667
    01671   bool CheckClientListHeight()
    01672   {
    01673     int num = 0;
    01674     const NetworkClientInfo *ci;
    01675
    01676     /* Should be replaced with a loop through all clients */
    01677     FOR_ALL_CLIENT_INFOS(ci) {
    01678       if (ci->client_playas != COMPANY_INACTIVE_CLIENT) num++;
    01679     }
    01680
    01681     num *= CLNWND_ROWSIZE;
    01682
    01683     /* If height is changed */
    01684     if (this->height != CLNWND_OFFSET + num + 1) {
    01685       // XXX - magic unfortunately; (num + 2) has to be one bigger than heigh (num + 1)
    01686       this->SetDirty();
    01687       this->widget[3].bottom = this->widget[3].top + num + 2;
    01688       this->height = CLNWND_OFFSET + num + 1;
    01689       this->SetDirty();
    01690       return false;
    01691     }
    01692     return true;
    01693   }
    01694
    01695   virtual void OnPaint()
    01696   {
    01697     NetworkClientInfo *ci;
    01698     int i = 0;
    01699
    01700     /* Check if we need to reset the height */
    01701     if (!this->CheckClientListHeight()) return;
    01702
    01703     this->DrawWidgets();
    01704
    01705     int y = CLNWND_OFFSET;
    01706
    01707     FOR_ALL_CLIENT_INFOS(ci) {
    01708       TextColour colour;
    01709       if (this->selected_item == i++) { // Selected item, highlight it
    01710         GfxFillRect(1, y, 248, y + CLNWND_ROWSIZE - 1, 0);
    01711         colour = TC_WHITE;
    01712       } else {
    01713         colour = TC_BLACK;
    01714       }
    01715
    01716       if (ci->client_id == CLIENT_ID_SERVER) {
    01717         DrawString(4, y, STR_NETWORK_SERVER, colour);
    01718       } else {
    01719         DrawString(4, y, STR_NETWORK_CLIENT, colour);
    01720       }
    01721
    01722       /* Filter out spectators */
    01723       if (IsValidCompanyID(ci->client_playas)) DrawCompanyIcon(ci->client_playas, 64, y + 1);
    01724
    01725       DoDrawString(ci->client_name, 81, y, colour);
    01726
    01727       y += CLNWND_ROWSIZE;
    01728     }
    01729   }
    01730
    01731   virtual void OnClick(Point pt, int widget)
    01732   {
    01733     /* Show the popup with option */
    01734     if (this->selected_item != -1) {
    01735       PopupClientList(this->selected_item, pt.x + this->left, pt.y + this->top);
    01736     }
    01737   }
    01738
    01739   virtual void OnMouseOver(Point pt, int widget)
    01740   {
    01741     /* -1 means we left the current window */
    01742     if (pt.y == -1) {
    01743       this->selected_y = 0;
    01744       this->selected_item = -1;
    01745       this->SetDirty();
    01746       return;
    01747     }
    01748     /* It did not change.. no update! */
    01749     if (pt.y == this->selected_y) return;
    01750
    01751     /* Find the new selected item (if any) */
    01752     this->selected_y = pt.y;
    01753     if (pt.y > CLNWND_OFFSET) {
    01754       this->selected_item = (pt.y - CLNWND_OFFSET) / CLNWND_ROWSIZE;
    01755     } else {
    01756       this->selected_item = -1;
    01757     }
    01758
    01759     /* Repaint */
    01760     this->SetDirty();
    01761   }
    01762 };
    01763
    01764 void ShowClientList()
    01765 {
    01766   AllocateWindowDescFront<NetworkClientListWindow>(&_client_list_desc, 0);
    01767 }
    01768
    01769
    01770 static NetworkPasswordType pw_type;
    01771
    01772
    01773 void ShowNetworkNeedPassword(NetworkPasswordType npt)
    01774 {
    01775   StringID caption;
    01776
    01777   pw_type = npt;
    01778   switch (npt) {
    01779     default: NOT_REACHED();
    01780     case NETWORK_GAME_PASSWORD:    caption = STR_NETWORK_NEED_GAME_PASSWORD_CAPTION; break;
    01781     case NETWORK_COMPANY_PASSWORD: caption = STR_NETWORK_NEED_COMPANY_PASSWORD_CAPTION; break;
    01782   }
    01783   ShowQueryString(STR_EMPTY, caption, 20, 180, FindWindowById(WC_NETWORK_STATUS_WINDOW, 0), CS_ALPHANUMERAL, QSF_NONE);
    01784 }
    01785
    01786 // Vars needed for the join-GUI
    01787 NetworkJoinStatus _network_join_status;
    01788 uint8 _network_join_waiting;
    01789 uint32 _network_join_bytes;
    01790 uint32 _network_join_bytes_total;
    01791
    01792 struct NetworkJoinStatusWindow : Window {
    01793   NetworkJoinStatusWindow(const WindowDesc *desc) : Window(desc)
    01794   {
    01795     this->parent = FindWindowById(WC_NETWORK_WINDOW, 0);
    01796     this->FindWindowPlacementAndResize(desc);
    01797   }
    01798
    01799   virtual void OnPaint()
    01800   {
    01801     uint8 progress; // used for progress bar
    01802     this->DrawWidgets();
    01803
    01804     DrawStringCentered(125, 35, STR_NETWORK_CONNECTING_1 + _network_join_status, TC_GREY);
    01805     switch (_network_join_status) {
    01806       case NETWORK_JOIN_STATUS_CONNECTING: case NETWORK_JOIN_STATUS_AUTHORIZING:
    01807       case NETWORK_JOIN_STATUS_GETTING_COMPANY_INFO:
    01808         progress = 10; // first two stages 10%
    01809         break;
    01810       case NETWORK_JOIN_STATUS_WAITING:
    01811         SetDParam(0, _network_join_waiting);
    01812         DrawStringCentered(125, 46, STR_NETWORK_CONNECTING_WAITING, TC_GREY);
    01813         progress = 15; // third stage is 15%
    01814         break;
    01815       case NETWORK_JOIN_STATUS_DOWNLOADING:
    01816         SetDParam(0, _network_join_bytes);
    01817         SetDParam(1, _network_join_bytes_total);
    01818         DrawStringCentered(125, 46, STR_NETWORK_CONNECTING_DOWNLOADING, TC_GREY);
    01819         /* Fallthrough */
    01820       default: /* Waiting is 15%, so the resting receivement of map is maximum 70% */
    01821         progress = 15 + _network_join_bytes * (100 - 15) / _network_join_bytes_total;
    01822     }
    01823
    01824     /* Draw nice progress bar :) */
    01825     DrawFrameRect(20, 18, (int)((this->width - 20) * progress / 100), 28, COLOUR_MAUVE, FR_NONE);
    01826   }
    01827
    01828   virtual void OnClick(Point pt, int widget)
    01829   {
    01830     if (widget == 2) { //Disconnect button
    01831       NetworkDisconnect();
    01832       SwitchMode(SM_MENU);
    01833       ShowNetworkGameWindow();
    01834     }
    01835   }
    01836
    01837   virtual void OnQueryTextFinished(char *str)
    01838   {
    01839     if (StrEmpty(str)) {
    01840       NetworkDisconnect();
    01841       ShowNetworkGameWindow();
    01842     } else {
    01843       SEND_COMMAND(PACKET_CLIENT_PASSWORD)(pw_type, str);
    01844     }
    01845   }
    01846 };
    01847
    01848 static const Widget _network_join_status_window_widget[] = {
    01849 {    WWT_CAPTION,   RESIZE_NONE,  COLOUR_GREY,      0,   249,     0,    13, STR_NETWORK_CONNECTING, STR_018C_WINDOW_TITLE_DRAG_THIS},
    01850 {      WWT_PANEL,   RESIZE_NONE,  COLOUR_GREY,      0,   249,    14,    84, 0x0,                    STR_NULL},
    01851 { WWT_PUSHTXTBTN,   RESIZE_NONE,  COLOUR_WHITE,    75,   175,    69,    80, STR_NETWORK_DISCONNECT, STR_NULL},
    01852 {   WIDGETS_END},
    01853 };
    01854
    01855 static const WindowDesc _network_join_status_window_desc = {
    01856   WDP_CENTER, WDP_CENTER, 250, 85, 250, 85,
    01857   WC_NETWORK_STATUS_WINDOW, WC_NONE,
    01858   WDF_STD_TOOLTIPS | WDF_DEF_WIDGET | WDF_MODAL,
    01859   _network_join_status_window_widget,
    01860 };
    01861
    01862 void ShowJoinStatusWindow()
    01863 {
    01864   DeleteWindowById(WC_NETWORK_STATUS_WINDOW, 0);
    01865   new NetworkJoinStatusWindow(&_network_join_status_window_desc);
    01866 }
    01867
    01868
    01870 enum NetworkCompanyPasswordWindowWidgets {
    01871   NCPWW_CLOSE,
    01872   NCPWW_CAPTION,
    01873   NCPWW_BACKGROUND,
    01874   NCPWW_LABEL,
    01875   NCPWW_PASSWORD,
    01876   NCPWW_SAVE_AS_DEFAULT_PASSWORD,
    01877   NCPWW_CANCEL,
    01878   NCPWW_OK,
    01879 };
    01880
    01881 struct NetworkCompanyPasswordWindow : public QueryStringBaseWindow {
    01882   NetworkCompanyPasswordWindow(const WindowDesc *desc, Window *parent) : QueryStringBaseWindow(lengthof(_settings_client.network.default_company_pass), desc)
    01883   {
    01884     this->parent = parent;
    01885     this->afilter = CS_ALPHANUMERAL;
    01886     InitializeTextBuffer(&this->text, this->edit_str_buf, this->edit_str_size, 0);
    01887     this->SetFocusedWidget(NCPWW_PASSWORD);
    01888
    01889     this->FindWindowPlacementAndResize(desc);
    01890   }
    01891
    01892   void OnOk()
    01893   {
    01894     if (this->IsWidgetLowered(NCPWW_SAVE_AS_DEFAULT_PASSWORD)) {
    01895       snprintf(_settings_client.network.default_company_pass, lengthof(_settings_client.network.default_company_pass), "%s", this->edit_str_buf);
    01896     }
    01897
    01898     /* empty password is a '*' because of console argument */
    01899     if (StrEmpty(this->edit_str_buf)) snprintf(this->edit_str_buf, this->edit_str_size, "*");
    01900     char *password = this->edit_str_buf;
    01901     NetworkChangeCompanyPassword(1, &password);
    01902   }
    01903
    01904   virtual void OnPaint()
    01905   {
    01906     this->DrawWidgets();
    01907     this->DrawEditBox(4);
    01908   }
    01909
    01910   virtual void OnClick(Point pt, int widget)
    01911   {
    01912     switch (widget) {
    01913       case NCPWW_OK:
    01914         this->OnOk();
    01915
    01916       /* FALL THROUGH */
    01917       case NCPWW_CANCEL:
    01918         delete this;
    01919         break;
    01920
    01921       case NCPWW_SAVE_AS_DEFAULT_PASSWORD:
    01922         this->ToggleWidgetLoweredState(NCPWW_SAVE_AS_DEFAULT_PASSWORD);
    01923         this->SetDirty();
    01924         break;
    01925     }
    01926   }
    01927
    01928   virtual void OnMouseLoop()
    01929   {
    01930     this->HandleEditBox(4);
    01931   }
    01932
    01933   virtual EventState OnKeyPress(uint16 key, uint16 keycode)
    01934   {
    01935     EventState state;
    01936     switch (this->HandleEditBoxKey(4, key, keycode, state)) {
    01937       default: break;
    01938
    01939       case HEBR_CONFIRM:
    01940         this->OnOk();
    01941         /* FALL THROUGH */
    01942
    01943       case HEBR_CANCEL:
    01944         delete this;
    01945         break;
    01946     }
    01947     return state;
    01948   }
    01949
    01950   virtual void OnOpenOSKWindow(int wid)
    01951   {
    01952     ShowOnScreenKeyboard(this, wid, NCPWW_CANCEL, NCPWW_OK);
    01953   }
    01954 };
    01955
    01956 static const Widget _ncp_window_widgets[] = {
    01957 {   WWT_CLOSEBOX,  RESIZE_NONE,  COLOUR_GREY,   0,  10,  0, 13, STR_00C5,                          STR_018B_CLOSE_WINDOW},
    01958 {    WWT_CAPTION,  RESIZE_NONE,  COLOUR_GREY,  11, 299,  0, 13, STR_COMPANY_PASSWORD_CAPTION,      STR_018C_WINDOW_TITLE_DRAG_THIS},
    01959 {      WWT_PANEL,  RESIZE_NONE,  COLOUR_GREY,   0, 299, 14, 50, 0x0,                               STR_NULL},
    01960 {       WWT_TEXT,  RESIZE_NONE,  COLOUR_GREY,   5, 100, 19, 30, STR_COMPANY_PASSWORD,              STR_NULL},
    01961 {    WWT_EDITBOX,  RESIZE_NONE,  COLOUR_GREY, 101, 294, 19, 30, STR_SET_COMPANY_PASSWORD,          STR_NULL},
    01962 {    WWT_TEXTBTN,  RESIZE_NONE,  COLOUR_GREY, 101, 294, 35, 46, STR_MAKE_DEFAULT_COMPANY_PASSWORD, STR_MAKE_DEFAULT_COMPANY_PASSWORD_TIP},
    01963 { WWT_PUSHTXTBTN,  RESIZE_NONE,  COLOUR_GREY,   0, 149, 51, 62, STR_012E_CANCEL,                   STR_COMPANY_PASSWORD_CANCEL},
    01964 { WWT_PUSHTXTBTN,  RESIZE_NONE,  COLOUR_GREY, 150, 299, 51, 62, STR_012F_OK,                       STR_COMPANY_PASSWORD_OK},
    01965 {   WIDGETS_END},
    01966 };
    01967
    01968 static const WindowDesc _ncp_window_desc = {
    01969   WDP_AUTO, WDP_AUTO, 300, 63, 300, 63,
    01970   WC_COMPANY_PASSWORD_WINDOW, WC_NONE,
    01971   WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET | WDF_UNCLICK_BUTTONS | WDF_STICKY_BUTTON,
    01972   _ncp_window_widgets,
    01973 };
    01974
    01975 void ShowNetworkCompanyPasswordWindow(Window *parent)
    01976 {
    01977   DeleteWindowById(WC_COMPANY_PASSWORD_WINDOW, 0);
    01978
    01979   new NetworkCompanyPasswordWindow(&_ncp_window_desc, parent);
    01980 }
    01981
    01982 #endif /* ENABLE_NETWORK */