diff -r 22c441f5adf9 -r 33cb70ff2f5d src/network/network_gui.cpp --- a/src/network/network_gui.cpp Wed May 07 21:09:51 2008 +0000 +++ b/src/network/network_gui.cpp Sun May 11 20:09:34 2008 +0000 @@ -29,6 +29,7 @@ #include "../player_func.h" #include "../settings_type.h" #include "../widgets/dropdown_func.h" +#include "../querystring_gui.h" #include "table/strings.h" #include "../table/sprites.h" @@ -36,33 +37,9 @@ #define BGC 5 #define BTC 15 -struct chatquerystr_d : public querystr_d { - DestType dtype; - int dest; -}; -assert_compile(WINDOW_CUSTOM_SIZE >= sizeof(chatquerystr_d)); - -struct network_d { - PlayerID company; // select company in network lobby - byte field; // select text-field in start-server and game-listing - byte widget_id; ///< The widget that has the pop-up input menu - NetworkGameList *server; // selected server in lobby and game-listing - FiosItem *map; // selected map in start-server -}; -assert_compile(WINDOW_CUSTOM_SIZE >= sizeof(network_d)); - -struct network_ql_d { - network_d n; // see above; general stuff - querystr_d q; // text-input in start-server and game-listing - NetworkGameList **sort_list; // list of games (sorted) - list_d l; // accompanying list-administration -}; -assert_compile(WINDOW_CUSTOM_SIZE >= sizeof(network_ql_d)); - /* Global to remember sorting after window has been closed */ static Listing _ng_sorting; -static char _edit_str_net_buf[150]; static bool _chat_tab_completion_active; static void ShowNetworkStartServerWindow(); @@ -106,7 +83,7 @@ * @param unselect unselect the currently selected item */ void UpdateNetworkGameWindow(bool unselect) { - SendWindowMessage(WC_NETWORK_WINDOW, 0, unselect, 0, 0); + InvalidateWindowData(WC_NETWORK_WINDOW, 0, unselect); } static bool _internal_sort_order; // Used for Qsort order-flipping @@ -160,64 +137,6 @@ return _internal_sort_order ? -r : r; } -/** (Re)build the network game list as its amount has changed because - * an item has been added or deleted for example - * @param ngl list_d struct that contains all necessary information for sorting */ -static void BuildNetworkGameList(network_ql_d *nqld) -{ - NetworkGameList *ngl_temp; - uint n = 0; - - if (!(nqld->l.flags & VL_REBUILD)) return; - - /* Count the number of games in the list */ - for (ngl_temp = _network_game_list; ngl_temp != NULL; ngl_temp = ngl_temp->next) n++; - if (n == 0) return; - - /* Create temporary array of games to use for listing */ - free(nqld->sort_list); - nqld->sort_list = MallocT(n); - nqld->l.list_length = n; - - for (n = 0, ngl_temp = _network_game_list; ngl_temp != NULL; ngl_temp = ngl_temp->next) { - nqld->sort_list[n++] = ngl_temp; - } - - /* Force resort */ - nqld->l.flags &= ~VL_REBUILD; - nqld->l.flags |= VL_RESORT; -} - -static void SortNetworkGameList(network_ql_d *nqld) -{ - static NGameNameSortFunction * const ngame_sorter[] = { - &NGameNameSorter, - &NGameClientSorter, - &NGameAllowedSorter - }; - - NetworkGameList *item; - uint i; - - if (!(nqld->l.flags & VL_RESORT)) return; - if (nqld->l.list_length == 0) return; - - _internal_sort_order = !!(nqld->l.flags & VL_DESC); - qsort(nqld->sort_list, nqld->l.list_length, sizeof(nqld->sort_list[0]), ngame_sorter[nqld->l.sort_type]); - - /* After sorting ngl->sort_list contains the sorted items. Put these back - * into the original list. Basically nothing has changed, we are only - * shuffling the ->next pointers */ - _network_game_list = nqld->sort_list[0]; - for (item = _network_game_list, i = 1; i != nqld->l.list_length; i++) { - item->next = nqld->sort_list[i]; - item = item->next; - } - item->next = NULL; - - nqld->l.flags &= ~VL_RESORT; -} - /** Enum for NetworkGameWindow, referring to _network_game_window_widgets */ enum NetworkGameWindowWidgets { NGWW_CLOSE, ///< Close 'X' button @@ -249,364 +168,433 @@ NGWW_CANCEL, ///< 'Cancel' button }; -/** - * Draw a single server line. - * @param cur_item the server to draw. - * @param y from where to draw? - * @param highlight does the line need to be highlighted? - */ -static void DrawServerLine(const Window *w, const NetworkGameList *cur_item, uint y, bool highlight) -{ - /* show highlighted item with a different colour */ - if (highlight) GfxFillRect(w->widget[NGWW_NAME].left + 1, y - 2, w->widget[NGWW_INFO].right - 1, y + 9, 10); - - SetDParamStr(0, cur_item->info.server_name); - DrawStringTruncated(w->widget[NGWW_NAME].left + 5, y, STR_02BD, TC_BLACK, w->widget[NGWW_NAME].right - w->widget[NGWW_NAME].left - 5); - - SetDParam(0, cur_item->info.clients_on); - SetDParam(1, cur_item->info.clients_max); - SetDParam(2, cur_item->info.companies_on); - SetDParam(3, cur_item->info.companies_max); - DrawStringCentered(w->widget[NGWW_CLIENTS].left + 39, y, STR_NETWORK_GENERAL_ONLINE, TC_GOLD); - - /* only draw icons if the server is online */ - if (cur_item->online) { - /* draw a lock if the server is password protected */ - if (cur_item->info.use_password) DrawSprite(SPR_LOCK, PAL_NONE, w->widget[NGWW_INFO].left + 5, y - 1); - - /* draw red or green icon, depending on compatibility with server */ - DrawSprite(SPR_BLOT, (cur_item->info.compatible ? PALETTE_TO_GREEN : (cur_item->info.version_compatible ? PALETTE_TO_YELLOW : PALETTE_TO_RED)), w->widget[NGWW_INFO].left + 15, y); - - /* draw flag according to server language */ - DrawSprite(SPR_FLAGS_BASE + cur_item->info.server_lang, PAL_NONE, w->widget[NGWW_INFO].left + 25, y); - } -} - -/** - * Handler of actions done in the NetworkStartServer window - * - * @param w pointer to the Window structure - * @param e pointer to window event - * @note Uses network_ql_d (network_d, querystr_d and list_d) WP macro - * @see struct _network_game_window_widgets - * @see enum NetworkGameWindowWidgets - */ - -static void NetworkGameWindowWndProc(Window *w, WindowEvent *e) -{ - network_d *nd = &WP(w, network_ql_d).n; - list_d *ld = &WP(w, network_ql_d).l; - - switch (e->event) { - case WE_CREATE: // Focus input box - w->vscroll.cap = 11; - w->resize.step_height = NET_PRC__SIZE_OF_ROW; - - nd->field = NGWW_PLAYER; - nd->server = NULL; - - WP(w, network_ql_d).sort_list = NULL; - ld->flags = VL_REBUILD | (_ng_sorting.order ? VL_DESC : VL_NONE); - ld->sort_type = _ng_sorting.criteria; - break; - - case WE_PAINT: { - const NetworkGameList *sel = nd->server; - const SortButtonState arrow = (ld->flags & VL_DESC) ? SBS_DOWN : SBS_UP; +typedef GUIList GUIGameServerList; - if (ld->flags & VL_REBUILD) { - BuildNetworkGameList(&WP(w, network_ql_d)); - SetVScrollCount(w, ld->list_length); - } - if (ld->flags & VL_RESORT) SortNetworkGameList(&WP(w, network_ql_d)); - - /* 'Refresh' button invisible if no server selected */ - w->SetWidgetDisabledState(NGWW_REFRESH, sel == NULL); - /* 'Join' button disabling conditions */ - w->SetWidgetDisabledState(NGWW_JOIN, sel == NULL || // no Selected Server - !sel->online || // Server offline - sel->info.clients_on >= sel->info.clients_max || // Server full - !sel->info.compatible); // Revision mismatch - - /* 'NewGRF Settings' button invisible if no NewGRF is used */ - w->SetWidgetHiddenState(NGWW_NEWGRF, sel == NULL || - !sel->online || - sel->info.grfconfig == NULL); - - SetDParam(0, 0x00); - SetDParam(1, _lan_internet_types_dropdown[_network_lan_internet]); - DrawWindowWidgets(w); - - /* Edit box to set player name */ - DrawEditBox(w, &WP(w, network_ql_d).q, NGWW_PLAYER); - - DrawString(w->widget[NGWW_PLAYER].left - 100, 23, STR_NETWORK_PLAYER_NAME, TC_GOLD); - - /* Sort based on widgets: name, clients, compatibility */ - switch (ld->sort_type) { - case NGWW_NAME - NGWW_NAME: DrawSortButtonState(w, NGWW_NAME, arrow); break; - case NGWW_CLIENTS - NGWW_NAME: DrawSortButtonState(w, NGWW_CLIENTS, arrow); break; - case NGWW_INFO - NGWW_NAME: DrawSortButtonState(w, NGWW_INFO, arrow); break; - } +struct NetworkGameWindow : public QueryStringBaseWindow { + byte field; ///< selected text-field + NetworkGameList *server; ///< selected server + GUIGameServerList servers; ///< list with game servers. - uint16 y = NET_PRC__OFFSET_TOP_WIDGET + 3; - int32 n = 0; - int32 pos = w->vscroll.pos; - const NetworkGameList *cur_item = _network_game_list; - - while (pos > 0 && cur_item != NULL) { - pos--; - cur_item = cur_item->next; - } - - while (cur_item != NULL) { - DrawServerLine(w, cur_item, y, cur_item == sel); - - cur_item = cur_item->next; - y += NET_PRC__SIZE_OF_ROW; - if (++n == w->vscroll.cap) break; // max number of games in the window - } + NetworkGameWindow(const WindowDesc *desc) : QueryStringBaseWindow(desc) + { + ttd_strlcpy(this->edit_str_buf, _network_player_name, lengthof(this->edit_str_buf)); + this->afilter = CS_ALPHANUMERAL; + InitializeTextBuffer(&this->text, this->edit_str_buf, lengthof(this->edit_str_buf), 120); - const NetworkGameList *last_joined = NetworkGameListAddItem(inet_addr(_network_last_host), _network_last_port); - /* Draw the last joined server, if any */ - if (last_joined != NULL) DrawServerLine(w, last_joined, y = w->widget[NGWW_LASTJOINED].top + 3, last_joined == sel); + UpdateNetworkGameWindow(true); - /* Draw the right menu */ - GfxFillRect(w->widget[NGWW_DETAILS].left + 1, 43, w->widget[NGWW_DETAILS].right - 1, 92, 157); - if (sel == NULL) { - DrawStringCentered(w->widget[NGWW_DETAILS].left + 115, 58, STR_NETWORK_GAME_INFO, TC_FROMSTRING); - } else if (!sel->online) { - SetDParamStr(0, sel->info.server_name); - DrawStringCentered(w->widget[NGWW_DETAILS].left + 115, 68, STR_ORANGE, TC_FROMSTRING); // game name + this->vscroll.cap = 11; + this->resize.step_height = NET_PRC__SIZE_OF_ROW; - DrawStringCentered(w->widget[NGWW_DETAILS].left + 115, 132, STR_NETWORK_SERVER_OFFLINE, TC_FROMSTRING); // server offline - } else { // show game info - uint16 y = 100; - const uint16 x = w->widget[NGWW_DETAILS].left + 5; + this->field = NGWW_PLAYER; + this->server = NULL; - DrawStringCentered(w->widget[NGWW_DETAILS].left + 115, 48, STR_NETWORK_GAME_INFO, TC_FROMSTRING); + this->servers.sort_list = NULL; + this->servers.flags = VL_REBUILD | (_ng_sorting.order ? VL_DESC : VL_NONE); + this->servers.sort_type = _ng_sorting.criteria; + + this->FindWindowPlacementAndResize(desc); + } + + ~NetworkGameWindow() + { + free(this->servers.sort_list); + } + + /** + * (Re)build the network game list as its amount has changed because + * an item has been added or deleted for example + */ + void BuildNetworkGameList() + { + NetworkGameList *ngl_temp; + uint n = 0; + + if (!(this->servers.flags & VL_REBUILD)) return; + + /* Count the number of games in the list */ + for (ngl_temp = _network_game_list; ngl_temp != NULL; ngl_temp = ngl_temp->next) n++; + if (n == 0) return; + + /* Create temporary array of games to use for listing */ + this->servers.sort_list = ReallocT(this->servers.sort_list, n); + this->servers.list_length = n; + + for (n = 0, ngl_temp = _network_game_list; ngl_temp != NULL; ngl_temp = ngl_temp->next) { + this->servers.sort_list[n++] = ngl_temp; + } + + /* Force resort */ + this->servers.flags &= ~VL_REBUILD; + this->servers.flags |= VL_RESORT; + } + + void SortNetworkGameList() + { + static NGameNameSortFunction * const ngame_sorter[] = { + &NGameNameSorter, + &NGameClientSorter, + &NGameAllowedSorter + }; + + NetworkGameList *item; + uint i; + + if (!(this->servers.flags & VL_RESORT)) return; + if (this->servers.list_length == 0) return; + + _internal_sort_order = !!(this->servers.flags & VL_DESC); + qsort(this->servers.sort_list, this->servers.list_length, sizeof(this->servers.sort_list[0]), ngame_sorter[this->servers.sort_type]); + + /* After sorting ngl->sort_list contains the sorted items. Put these back + * into the original list. Basically nothing has changed, we are only + * shuffling the ->next pointers */ + _network_game_list = this->servers.sort_list[0]; + for (item = _network_game_list, i = 1; i != this->servers.list_length; i++) { + item->next = this->servers.sort_list[i]; + item = item->next; + } + item->next = NULL; + + this->servers.flags &= ~VL_RESORT; + } + + /** + * Draw a single server line. + * @param cur_item the server to draw. + * @param y from where to draw? + * @param highlight does the line need to be highlighted? + */ + void DrawServerLine(const NetworkGameList *cur_item, uint y, bool highlight) + { + /* show highlighted item with a different colour */ + if (highlight) GfxFillRect(this->widget[NGWW_NAME].left + 1, y - 2, this->widget[NGWW_INFO].right - 1, y + 9, 10); + + SetDParamStr(0, cur_item->info.server_name); + DrawStringTruncated(this->widget[NGWW_NAME].left + 5, y, STR_02BD, TC_BLACK, this->widget[NGWW_NAME].right - this->widget[NGWW_NAME].left - 5); + + SetDParam(0, cur_item->info.clients_on); + SetDParam(1, cur_item->info.clients_max); + SetDParam(2, cur_item->info.companies_on); + SetDParam(3, cur_item->info.companies_max); + DrawStringCentered(this->widget[NGWW_CLIENTS].left + 39, y, STR_NETWORK_GENERAL_ONLINE, TC_GOLD); + + /* only draw icons if the server is online */ + if (cur_item->online) { + /* draw a lock if the server is password protected */ + if (cur_item->info.use_password) DrawSprite(SPR_LOCK, PAL_NONE, this->widget[NGWW_INFO].left + 5, y - 1); + + /* draw red or green icon, depending on compatibility with server */ + 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); + + /* draw flag according to server language */ + DrawSprite(SPR_FLAGS_BASE + cur_item->info.server_lang, PAL_NONE, this->widget[NGWW_INFO].left + 25, y); + } + } + + virtual void OnPaint() + { + const NetworkGameList *sel = this->server; + const SortButtonState arrow = (this->servers.flags & VL_DESC) ? SBS_DOWN : SBS_UP; + + if (this->servers.flags & VL_REBUILD) { + this->BuildNetworkGameList(); + SetVScrollCount(this, this->servers.list_length); + } + if (this->servers.flags & VL_RESORT) this->SortNetworkGameList(); + + /* 'Refresh' button invisible if no server selected */ + this->SetWidgetDisabledState(NGWW_REFRESH, sel == NULL); + /* 'Join' button disabling conditions */ + this->SetWidgetDisabledState(NGWW_JOIN, sel == NULL || // no Selected Server + !sel->online || // Server offline + sel->info.clients_on >= sel->info.clients_max || // Server full + !sel->info.compatible); // Revision mismatch + + /* 'NewGRF Settings' button invisible if no NewGRF is used */ + this->SetWidgetHiddenState(NGWW_NEWGRF, sel == NULL || + !sel->online || + sel->info.grfconfig == NULL); + + SetDParam(0, 0x00); + SetDParam(1, _lan_internet_types_dropdown[_network_lan_internet]); + DrawWindowWidgets(this); + + /* Edit box to set player name */ + this->DrawEditBox(NGWW_PLAYER); + + DrawString(this->widget[NGWW_PLAYER].left - 100, 23, STR_NETWORK_PLAYER_NAME, TC_GOLD); + + /* Sort based on widgets: name, clients, compatibility */ + switch (this->servers.sort_type) { + case NGWW_NAME - NGWW_NAME: DrawSortButtonState(this, NGWW_NAME, arrow); break; + case NGWW_CLIENTS - NGWW_NAME: DrawSortButtonState(this, NGWW_CLIENTS, arrow); break; + case NGWW_INFO - NGWW_NAME: DrawSortButtonState(this, NGWW_INFO, arrow); break; + } + + uint16 y = NET_PRC__OFFSET_TOP_WIDGET + 3; + int32 n = 0; + int32 pos = this->vscroll.pos; + const NetworkGameList *cur_item = _network_game_list; + + while (pos > 0 && cur_item != NULL) { + pos--; + cur_item = cur_item->next; + } + + while (cur_item != NULL) { + this->DrawServerLine(cur_item, y, cur_item == sel); + + cur_item = cur_item->next; + y += NET_PRC__SIZE_OF_ROW; + if (++n == this->vscroll.cap) break; // max number of games in the window + } + + const NetworkGameList *last_joined = NetworkGameListAddItem(inet_addr(_network_last_host), _network_last_port); + /* Draw the last joined server, if any */ + if (last_joined != NULL) this->DrawServerLine(last_joined, y = this->widget[NGWW_LASTJOINED].top + 3, last_joined == sel); + + /* Draw the right menu */ + GfxFillRect(this->widget[NGWW_DETAILS].left + 1, 43, this->widget[NGWW_DETAILS].right - 1, 92, 157); + if (sel == NULL) { + DrawStringCentered(this->widget[NGWW_DETAILS].left + 115, 58, STR_NETWORK_GAME_INFO, TC_FROMSTRING); + } else if (!sel->online) { + SetDParamStr(0, sel->info.server_name); + DrawStringCentered(this->widget[NGWW_DETAILS].left + 115, 68, STR_ORANGE, TC_FROMSTRING); // game name + + DrawStringCentered(this->widget[NGWW_DETAILS].left + 115, 132, STR_NETWORK_SERVER_OFFLINE, TC_FROMSTRING); // server offline + } else { // show game info + uint16 y = 100; + const uint16 x = this->widget[NGWW_DETAILS].left + 5; + + DrawStringCentered(this->widget[NGWW_DETAILS].left + 115, 48, STR_NETWORK_GAME_INFO, TC_FROMSTRING); - SetDParamStr(0, sel->info.server_name); - DrawStringCenteredTruncated(w->widget[NGWW_DETAILS].left, w->widget[NGWW_DETAILS].right, 62, STR_ORANGE, TC_BLACK); // game name - - SetDParamStr(0, sel->info.map_name); - DrawStringCenteredTruncated(w->widget[NGWW_DETAILS].left, w->widget[NGWW_DETAILS].right, 74, STR_02BD, TC_BLACK); // map name - - SetDParam(0, sel->info.clients_on); - SetDParam(1, sel->info.clients_max); - SetDParam(2, sel->info.companies_on); - SetDParam(3, sel->info.companies_max); - DrawString(x, y, STR_NETWORK_CLIENTS, TC_GOLD); - y += 10; - - SetDParam(0, STR_NETWORK_LANG_ANY + sel->info.server_lang); - DrawString(x, y, STR_NETWORK_LANGUAGE, TC_GOLD); // server language - y += 10; - - SetDParam(0, STR_TEMPERATE_LANDSCAPE + sel->info.map_set); - DrawString(x, y, STR_NETWORK_TILESET, TC_GOLD); // tileset - y += 10; - - SetDParam(0, sel->info.map_width); - SetDParam(1, sel->info.map_height); - DrawString(x, y, STR_NETWORK_MAP_SIZE, TC_GOLD); // map size - y += 10; - - SetDParamStr(0, sel->info.server_revision); - DrawString(x, y, STR_NETWORK_SERVER_VERSION, TC_GOLD); // server version - y += 10; - - SetDParamStr(0, sel->info.hostname); - SetDParam(1, sel->port); - DrawString(x, y, STR_NETWORK_SERVER_ADDRESS, TC_GOLD); // server address - y += 10; - - SetDParam(0, sel->info.start_date); - DrawString(x, y, STR_NETWORK_START_DATE, TC_GOLD); // start date - y += 10; - - SetDParam(0, sel->info.game_date); - DrawString(x, y, STR_NETWORK_CURRENT_DATE, TC_GOLD); // current date - y += 10; - - y += 2; - - if (!sel->info.compatible) { - DrawStringCentered(w->widget[NGWW_DETAILS].left + 115, y, sel->info.version_compatible ? STR_NETWORK_GRF_MISMATCH : STR_NETWORK_VERSION_MISMATCH, TC_FROMSTRING); // server mismatch - } else if (sel->info.clients_on == sel->info.clients_max) { - /* Show: server full, when clients_on == clients_max */ - DrawStringCentered(w->widget[NGWW_DETAILS].left + 115, y, STR_NETWORK_SERVER_FULL, TC_FROMSTRING); // server full - } else if (sel->info.use_password) { - DrawStringCentered(w->widget[NGWW_DETAILS].left + 115, y, STR_NETWORK_PASSWORD, TC_FROMSTRING); // password warning - } - - y += 10; - } - } break; - - case WE_CLICK: - nd->field = e->we.click.widget; - switch (e->we.click.widget) { - case NGWW_PLAYER: - ShowOnScreenKeyboard(w, &WP(w, network_ql_d).q, NGWW_PLAYER, 0, 0); - break; - - case NGWW_CANCEL: // Cancel button - DeleteWindowById(WC_NETWORK_WINDOW, 0); - break; - - case NGWW_CONN_BTN: // 'Connection' droplist - ShowDropDownMenu(w, _lan_internet_types_dropdown, _network_lan_internet, NGWW_CONN_BTN, 0, 0); // do it for widget NSSW_CONN_BTN - break; + SetDParamStr(0, sel->info.server_name); + DrawStringCenteredTruncated(this->widget[NGWW_DETAILS].left, this->widget[NGWW_DETAILS].right, 62, STR_ORANGE, TC_BLACK); // game name - case NGWW_NAME: // Sort by name - case NGWW_CLIENTS: // Sort by connected clients - case NGWW_INFO: // Connectivity (green dot) - if (ld->sort_type == e->we.click.widget - NGWW_NAME) ld->flags ^= VL_DESC; - ld->flags |= VL_RESORT; - ld->sort_type = e->we.click.widget - NGWW_NAME; - - _ng_sorting.order = !!(ld->flags & VL_DESC); - _ng_sorting.criteria = ld->sort_type; - SetWindowDirty(w); - break; - - case NGWW_MATRIX: { // Matrix to show networkgames - NetworkGameList *cur_item; - uint32 id_v = (e->we.click.pt.y - NET_PRC__OFFSET_TOP_WIDGET) / NET_PRC__SIZE_OF_ROW; - - if (id_v >= w->vscroll.cap) return; // click out of bounds - id_v += w->vscroll.pos; - - cur_item = _network_game_list; - for (; id_v > 0 && cur_item != NULL; id_v--) cur_item = cur_item->next; - - nd->server = cur_item; - SetWindowDirty(w); - } break; - - case NGWW_LASTJOINED: { - NetworkGameList *last_joined = NetworkGameListAddItem(inet_addr(_network_last_host), _network_last_port); - if (last_joined != NULL) { - nd->server = last_joined; - SetWindowDirty(w); - } - } break; + SetDParamStr(0, sel->info.map_name); + DrawStringCenteredTruncated(this->widget[NGWW_DETAILS].left, this->widget[NGWW_DETAILS].right, 74, STR_02BD, TC_BLACK); // map name - case NGWW_FIND: // Find server automatically - switch (_network_lan_internet) { - case 0: NetworkUDPSearchGame(); break; - case 1: NetworkUDPQueryMasterServer(); break; - } - break; - - case NGWW_ADD: // Add a server - ShowQueryString( - BindCString(_network_default_ip), - STR_NETWORK_ENTER_IP, - 31 | 0x1000, // maximum number of characters OR - 250, // characters up to this width pixels, whichever is satisfied first - w, CS_ALPHANUMERAL); - break; - - case NGWW_START: // Start server - ShowNetworkStartServerWindow(); - break; + SetDParam(0, sel->info.clients_on); + SetDParam(1, sel->info.clients_max); + SetDParam(2, sel->info.companies_on); + SetDParam(3, sel->info.companies_max); + DrawString(x, y, STR_NETWORK_CLIENTS, TC_GOLD); + y += 10; - case NGWW_JOIN: // Join Game - if (nd->server != NULL) { - snprintf(_network_last_host, sizeof(_network_last_host), "%s", inet_ntoa(*(struct in_addr *)&nd->server->ip)); - _network_last_port = nd->server->port; - ShowNetworkLobbyWindow(nd->server); - } - break; - - case NGWW_REFRESH: // Refresh - if (nd->server != NULL) NetworkUDPQueryServer(nd->server->info.hostname, nd->server->port); - break; + SetDParam(0, STR_NETWORK_LANG_ANY + sel->info.server_lang); + DrawString(x, y, STR_NETWORK_LANGUAGE, TC_GOLD); // server language + y += 10; - case NGWW_NEWGRF: // NewGRF Settings - if (nd->server != NULL) ShowNewGRFSettings(false, false, false, &nd->server->info.grfconfig); - break; - } - break; + SetDParam(0, STR_TEMPERATE_LANDSCAPE + sel->info.map_set); + DrawString(x, y, STR_NETWORK_TILESET, TC_GOLD); // tileset + y += 10; - case WE_DROPDOWN_SELECT: // we have selected a dropdown item in the list - switch (e->we.dropdown.button) { - case NGWW_CONN_BTN: - _network_lan_internet = e->we.dropdown.index; - break; + SetDParam(0, sel->info.map_width); + SetDParam(1, sel->info.map_height); + DrawString(x, y, STR_NETWORK_MAP_SIZE, TC_GOLD); // map size + y += 10; - default: - NOT_REACHED(); + SetDParamStr(0, sel->info.server_revision); + DrawString(x, y, STR_NETWORK_SERVER_VERSION, TC_GOLD); // server version + y += 10; + + SetDParamStr(0, sel->info.hostname); + SetDParam(1, sel->port); + DrawString(x, y, STR_NETWORK_SERVER_ADDRESS, TC_GOLD); // server address + y += 10; + + SetDParam(0, sel->info.start_date); + DrawString(x, y, STR_NETWORK_START_DATE, TC_GOLD); // start date + y += 10; + + SetDParam(0, sel->info.game_date); + DrawString(x, y, STR_NETWORK_CURRENT_DATE, TC_GOLD); // current date + y += 10; + + y += 2; + + if (!sel->info.compatible) { + DrawStringCentered(this->widget[NGWW_DETAILS].left + 115, y, sel->info.version_compatible ? STR_NETWORK_GRF_MISMATCH : STR_NETWORK_VERSION_MISMATCH, TC_FROMSTRING); // server mismatch + } else if (sel->info.clients_on == sel->info.clients_max) { + /* Show: server full, when clients_on == clients_max */ + DrawStringCentered(this->widget[NGWW_DETAILS].left + 115, y, STR_NETWORK_SERVER_FULL, TC_FROMSTRING); // server full + } else if (sel->info.use_password) { + DrawStringCentered(this->widget[NGWW_DETAILS].left + 115, y, STR_NETWORK_PASSWORD, TC_FROMSTRING); // password warning } - SetWindowDirty(w); - break; - - case WE_MOUSELOOP: - if (nd->field == NGWW_PLAYER) HandleEditBox(w, &WP(w, network_ql_d).q, NGWW_PLAYER); - break; + y += 10; + } + } - case WE_MESSAGE: - if (e->we.message.msg != 0) nd->server = NULL; - ld->flags |= VL_REBUILD; - SetWindowDirty(w); - break; + virtual void OnClick(Point pt, int widget) + { + this->field = widget; + switch (widget) { + case NGWW_PLAYER: + ShowOnScreenKeyboard(this, NGWW_PLAYER, 0, 0); + break; - case WE_KEYPRESS: - if (nd->field != NGWW_PLAYER) { - if (nd->server != NULL) { - if (e->we.keypress.keycode == WKC_DELETE) { // Press 'delete' to remove servers - NetworkGameListRemoveItem(nd->server); - NetworkRebuildHostList(); - nd->server = NULL; - } + case NGWW_CANCEL: // Cancel button + DeleteWindowById(WC_NETWORK_WINDOW, 0); + break; + + case NGWW_CONN_BTN: // 'Connection' droplist + ShowDropDownMenu(this, _lan_internet_types_dropdown, _network_lan_internet, NGWW_CONN_BTN, 0, 0); // do it for widget NSSW_CONN_BTN + break; + + case NGWW_NAME: // Sort by name + case NGWW_CLIENTS: // Sort by connected clients + case NGWW_INFO: // Connectivity (green dot) + if (this->servers.sort_type == widget - NGWW_NAME) this->servers.flags ^= VL_DESC; + this->servers.flags |= VL_RESORT; + this->servers.sort_type = widget - NGWW_NAME; + + _ng_sorting.order = !!(this->servers.flags & VL_DESC); + _ng_sorting.criteria = this->servers.sort_type; + this->SetDirty(); + break; + + case NGWW_MATRIX: { // Matrix to show networkgames + NetworkGameList *cur_item; + uint32 id_v = (pt.y - NET_PRC__OFFSET_TOP_WIDGET) / NET_PRC__SIZE_OF_ROW; + + if (id_v >= this->vscroll.cap) return; // click out of bounds + id_v += this->vscroll.pos; + + cur_item = _network_game_list; + for (; id_v > 0 && cur_item != NULL; id_v--) cur_item = cur_item->next; + + this->server = cur_item; + this->SetDirty(); + } break; + + case NGWW_LASTJOINED: { + NetworkGameList *last_joined = NetworkGameListAddItem(inet_addr(_network_last_host), _network_last_port); + if (last_joined != NULL) { + this->server = last_joined; + this->SetDirty(); + } + } break; + + case NGWW_FIND: // Find server automatically + switch (_network_lan_internet) { + case 0: NetworkUDPSearchGame(); break; + case 1: NetworkUDPQueryMasterServer(); break; } break; - } - - if (HandleEditBoxKey(w, &WP(w, network_ql_d).q, NGWW_PLAYER, e) == 1) break; // enter pressed - - /* The name is only allowed when it starts with a letter! */ - if (_edit_str_net_buf[0] != '\0' && _edit_str_net_buf[0] != ' ') { - ttd_strlcpy(_network_player_name, _edit_str_net_buf, lengthof(_network_player_name)); - } else { - ttd_strlcpy(_network_player_name, "Player", lengthof(_network_player_name)); - } - - break; - - case WE_ON_EDIT_TEXT: - NetworkAddServer(e->we.edittext.str); - NetworkRebuildHostList(); - break; - - case WE_RESIZE: { - w->vscroll.cap += e->we.sizing.diff.y / (int)w->resize.step_height; - w->widget[NGWW_MATRIX].data = (w->vscroll.cap << 8) + 1; - - SetVScrollCount(w, ld->list_length); - - int widget_width = w->widget[NGWW_FIND].right - w->widget[NGWW_FIND].left; - int space = (w->width - 4 * widget_width - 25) / 3; + case NGWW_ADD: // Add a server + ShowQueryString( + BindCString(_network_default_ip), + STR_NETWORK_ENTER_IP, + 31 | 0x1000, // maximum number of characters OR + 250, // characters up to this width pixels, whichever is satisfied first + this, CS_ALPHANUMERAL); + break; - int offset = 10; - for (uint i = 0; i < 4; i++) { - w->widget[NGWW_FIND + i].left = offset; - offset += widget_width; - w->widget[NGWW_FIND + i].right = offset; - offset += space; + case NGWW_START: // Start server + ShowNetworkStartServerWindow(); + break; + + case NGWW_JOIN: // Join Game + if (this->server != NULL) { + snprintf(_network_last_host, sizeof(_network_last_host), "%s", inet_ntoa(*(struct in_addr *)&this->server->ip)); + _network_last_port = this->server->port; + ShowNetworkLobbyWindow(this->server); + } + break; + + case NGWW_REFRESH: // Refresh + if (this->server != NULL) NetworkUDPQueryServer(this->server->info.hostname, this->server->port); + break; + + case NGWW_NEWGRF: // NewGRF Settings + if (this->server != NULL) ShowNewGRFSettings(false, false, false, &this->server->info.grfconfig); + break; + } + } + + virtual void OnDropdownSelect(int widget, int index) + { + switch (widget) { + case NGWW_CONN_BTN: + _network_lan_internet = index; + break; + + default: + NOT_REACHED(); + } + + this->SetDirty(); + } + + virtual void OnMouseLoop() + { + if (this->field == NGWW_PLAYER) this->HandleEditBox(NGWW_PLAYER); + } + + virtual void OnInvalidateData(int data) + { + if (data != 0) this->server = NULL; + this->servers.flags |= VL_REBUILD; + this->SetDirty(); + } + + virtual bool OnKeyPress(uint16 key, uint16 keycode) + { + bool cont = true; + if (this->field != NGWW_PLAYER) { + if (this->server != NULL) { + if (keycode == WKC_DELETE) { // Press 'delete' to remove servers + NetworkGameListRemoveItem(this->server); + NetworkRebuildHostList(); + this->server = NULL; + } } - } break; + return cont; + } - case WE_DESTROY: // Nicely clean up the sort-list - free(WP(w, network_ql_d).sort_list); - break; + if (this->HandleEditBoxKey(NGWW_PLAYER, keycode, key, cont) == 1) return cont; // enter pressed + + /* The name is only allowed when it starts with a letter! */ + if (StrEmpty(this->edit_str_buf) && this->edit_str_buf[0] != ' ') { + ttd_strlcpy(_network_player_name, this->edit_str_buf, lengthof(_network_player_name)); + } else { + ttd_strlcpy(_network_player_name, "Player", lengthof(_network_player_name)); + } + return cont; } -} + + virtual void OnQueryTextFinished(char *str) + { + if (!StrEmpty(str)) { + NetworkAddServer(str); + NetworkRebuildHostList(); + } + } + + virtual void OnResize(Point new_size, Point delta) + { + this->vscroll.cap += delta.y / (int)this->resize.step_height; + + this->widget[NGWW_MATRIX].data = (this->vscroll.cap << 8) + 1; + + SetVScrollCount(this, this->servers.list_length); + + int widget_width = this->widget[NGWW_FIND].right - this->widget[NGWW_FIND].left; + int space = (this->width - 4 * widget_width - 25) / 3; + + int offset = 10; + for (uint i = 0; i < 4; i++) { + this->widget[NGWW_FIND + i].left = offset; + offset += widget_width; + this->widget[NGWW_FIND + i].right = offset; + offset += space; + } + } +}; static const Widget _network_game_window_widgets[] = { /* TOP */ @@ -653,7 +641,7 @@ WC_NETWORK_WINDOW, WC_NONE, WDF_STD_TOOLTIPS | WDF_DEF_WIDGET | WDF_STD_BTN | WDF_UNCLICK_BUTTONS | WDF_RESIZABLE, _network_game_window_widgets, - NetworkGameWindowWndProc, + NULL, }; void ShowNetworkGameWindow() @@ -675,16 +663,7 @@ _ng_sorting.order = 0; // sort ascending by default } - Window *w = AllocateWindowDesc(&_network_game_window_desc); - if (w != NULL) { - querystr_d *querystr = &WP(w, network_ql_d).q; - - ttd_strlcpy(_edit_str_net_buf, _network_player_name, lengthof(_edit_str_net_buf)); - querystr->afilter = CS_ALPHANUMERAL; - InitializeTextBuffer(&querystr->text, _edit_str_net_buf, lengthof(_edit_str_net_buf), 120); - - UpdateNetworkGameWindow(true); - } + new NetworkGameWindow(&_network_game_window_desc); } enum { @@ -714,225 +693,237 @@ NSSW_CANCEL = 27, ///< 'Cancel' button }; -/** - * Handler of actions done in the NetworkStartServer window - * - * @param w pointer to the Window structure - * @param e pointer to window event - * @note Uses network_ql_d (network_d, querystr_d and list_d) WP macro - * @see struct _network_start_server_window_widgets - * @see enum NetworkStartServerWidgets - */ -static void NetworkStartServerWindowWndProc(Window *w, WindowEvent *e) -{ - network_d *nd = &WP(w, network_ql_d).n; - - switch (e->event) { - case WE_CREATE: // focus input box - nd->field = NSSW_GAMENAME; - _network_game_info.use_password = (_network_server_password[0] != '\0'); - break; - - case WE_PAINT: { - int y = NSSWND_START, pos; - const FiosItem *item; - - /* draw basic widgets */ - SetDParam(1, _connection_types_dropdown[_network_advertise]); - SetDParam(2, _network_game_info.clients_max); - SetDParam(3, _network_game_info.companies_max); - SetDParam(4, _network_game_info.spectators_max); - SetDParam(5, STR_NETWORK_LANG_ANY + _network_game_info.server_lang); - DrawWindowWidgets(w); - - /* editbox to set game name */ - DrawEditBox(w, &WP(w, network_ql_d).q, NSSW_GAMENAME); - - /* if password is set, draw red '*' next to 'Set password' button */ - if (_network_game_info.use_password) DoDrawString("*", 408, 23, TC_RED); - - /* draw list of maps */ - GfxFillRect(11, 63, 258, 215, 0xD7); // black background of maps list - - pos = w->vscroll.pos; - while (pos < _fios_num + 1) { - item = _fios_list + pos - 1; - if (item == nd->map || (pos == 0 && nd->map == NULL)) - GfxFillRect(11, y - 1, 258, y + 10, 155); // show highlighted item with a different colour - - if (pos == 0) { - DrawString(14, y, STR_4010_GENERATE_RANDOM_NEW_GAME, TC_DARK_GREEN); - } else { - DoDrawString(item->title, 14, y, _fios_colors[item->type] ); - } - pos++; - y += NSSWND_ROWSIZE; - - if (y >= w->vscroll.cap * NSSWND_ROWSIZE + NSSWND_START) break; - } - } break; - - case WE_CLICK: - if (e->we.click.widget != NSSW_CONNTYPE_BTN && e->we.click.widget != NSSW_LANGUAGE_BTN) HideDropDownMenu(w); - nd->field = e->we.click.widget; - switch (e->we.click.widget) { - case NSSW_CLOSE: // Close 'X' - case NSSW_CANCEL: // Cancel button - ShowNetworkGameWindow(); - break; - - case NSSW_GAMENAME: - ShowOnScreenKeyboard(w, &WP(w, network_ql_d).q, NSSW_GAMENAME, 0, 0); - break; - - case NSSW_SETPWD: // Set password button - nd->widget_id = NSSW_SETPWD; - ShowQueryString(BindCString(_network_server_password), STR_NETWORK_SET_PASSWORD, 20, 250, w, CS_ALPHANUMERAL); - break; - - case NSSW_SELMAP: { // Select map - int y = (e->we.click.pt.y - NSSWND_START) / NSSWND_ROWSIZE; - - y += w->vscroll.pos; - if (y >= w->vscroll.count) return; - - nd->map = (y == 0) ? NULL : _fios_list + y - 1; - SetWindowDirty(w); - } break; - - case NSSW_CONNTYPE_BTN: // Connection type - ShowDropDownMenu(w, _connection_types_dropdown, _network_advertise, NSSW_CONNTYPE_BTN, 0, 0); // do it for widget NSSW_CONNTYPE_BTN - break; +struct NetworkStartServerWindow : public QueryStringBaseWindow { + byte field; ///< Selected text-field + FiosItem *map; ///< Selected map + byte widget_id; ///< The widget that has the pop-up input menu - case NSSW_CLIENTS_BTND: case NSSW_CLIENTS_BTNU: // Click on up/down button for number of clients - case NSSW_COMPANIES_BTND: case NSSW_COMPANIES_BTNU: // Click on up/down button for number of companies - case NSSW_SPECTATORS_BTND: case NSSW_SPECTATORS_BTNU: // Click on up/down button for number of spectators - /* Don't allow too fast scrolling */ - if ((w->flags4 & WF_TIMEOUT_MASK) <= 2 << WF_TIMEOUT_SHL) { - w->HandleButtonClick(e->we.click.widget); - SetWindowDirty(w); - switch (e->we.click.widget) { - default: NOT_REACHED(); - case NSSW_CLIENTS_BTND: case NSSW_CLIENTS_BTNU: - _network_game_info.clients_max = Clamp(_network_game_info.clients_max + e->we.click.widget - NSSW_CLIENTS_TXT, 2, MAX_CLIENTS); - break; - case NSSW_COMPANIES_BTND: case NSSW_COMPANIES_BTNU: - _network_game_info.companies_max = Clamp(_network_game_info.companies_max + e->we.click.widget - NSSW_COMPANIES_TXT, 1, MAX_PLAYERS); - break; - case NSSW_SPECTATORS_BTND: case NSSW_SPECTATORS_BTNU: - _network_game_info.spectators_max = Clamp(_network_game_info.spectators_max + e->we.click.widget - NSSW_SPECTATORS_TXT, 0, MAX_CLIENTS); - break; - } - } - _left_button_clicked = false; - break; - - case NSSW_CLIENTS_TXT: // Click on number of players - nd->widget_id = NSSW_CLIENTS_TXT; - SetDParam(0, _network_game_info.clients_max); - ShowQueryString(STR_CONFIG_PATCHES_INT32, STR_NETWORK_NUMBER_OF_CLIENTS, 3, 50, w, CS_NUMERAL); - break; - - case NSSW_COMPANIES_TXT: // Click on number of companies - nd->widget_id = NSSW_COMPANIES_TXT; - SetDParam(0, _network_game_info.companies_max); - ShowQueryString(STR_CONFIG_PATCHES_INT32, STR_NETWORK_NUMBER_OF_COMPANIES, 3, 50, w, CS_NUMERAL); - break; - - case NSSW_SPECTATORS_TXT: // Click on number of spectators - nd->widget_id = NSSW_SPECTATORS_TXT; - SetDParam(0, _network_game_info.spectators_max); - ShowQueryString(STR_CONFIG_PATCHES_INT32, STR_NETWORK_NUMBER_OF_SPECTATORS, 3, 50, w, CS_NUMERAL); - break; - - case NSSW_LANGUAGE_BTN: { // Language - uint sel = 0; - for (uint i = 0; i < lengthof(_language_dropdown) - 1; i++) { - if (_language_dropdown[i] == STR_NETWORK_LANG_ANY + _network_game_info.server_lang) { - sel = i; - break; - } - } - ShowDropDownMenu(w, _language_dropdown, sel, NSSW_LANGUAGE_BTN, 0, 0); - } break; - - case NSSW_START: // Start game - _is_network_server = true; + NetworkStartServerWindow(const WindowDesc *desc) : QueryStringBaseWindow(desc) + { + ttd_strlcpy(this->edit_str_buf, _network_server_name, lengthof(this->edit_str_buf)); - if (nd->map == NULL) { // start random new game - ShowGenerateLandscape(); - } else { // load a scenario - char *name = FiosBrowseTo(nd->map); - if (name != NULL) { - SetFiosType(nd->map->type); - _file_to_saveload.filetype = FT_SCENARIO; - ttd_strlcpy(_file_to_saveload.name, name, sizeof(_file_to_saveload.name)); - ttd_strlcpy(_file_to_saveload.title, nd->map->title, sizeof(_file_to_saveload.title)); - - delete w; - SwitchMode(SM_START_SCENARIO); - } - } - break; + _saveload_mode = SLD_NEW_GAME; + BuildFileList(); + this->vscroll.cap = 12; + this->vscroll.count = _fios_num + 1; - case NSSW_LOAD: // Load game - _is_network_server = true; - /* XXX - WC_NETWORK_WINDOW (this window) should stay, but if it stays, it gets - * copied all the elements of 'load game' and upon closing that, it segfaults */ - delete w; - ShowSaveLoadDialog(SLD_LOAD_GAME); - break; - } - break; + this->afilter = CS_ALPHANUMERAL; + InitializeTextBuffer(&this->text, this->edit_str_buf, lengthof(this->edit_str_buf), 160); - case WE_DROPDOWN_SELECT: // we have selected a dropdown item in the list - switch (e->we.dropdown.button) { - case NSSW_CONNTYPE_BTN: - _network_advertise = (e->we.dropdown.index != 0); - break; - case NSSW_LANGUAGE_BTN: - _network_game_info.server_lang = _language_dropdown[e->we.dropdown.index] - STR_NETWORK_LANG_ANY; - break; - default: - NOT_REACHED(); + this->field = NSSW_GAMENAME; + _network_game_info.use_password = !StrEmpty(_network_server_password); + + this->FindWindowPlacementAndResize(desc); + } + + virtual void OnPaint() + { + int y = NSSWND_START, pos; + const FiosItem *item; + + /* draw basic widgets */ + SetDParam(1, _connection_types_dropdown[_network_advertise]); + SetDParam(2, _network_game_info.clients_max); + SetDParam(3, _network_game_info.companies_max); + SetDParam(4, _network_game_info.spectators_max); + SetDParam(5, STR_NETWORK_LANG_ANY + _network_game_info.server_lang); + DrawWindowWidgets(this); + + /* editbox to set game name */ + this->DrawEditBox(NSSW_GAMENAME); + + /* if password is set, draw red '*' next to 'Set password' button */ + if (_network_game_info.use_password) DoDrawString("*", 408, 23, TC_RED); + + /* draw list of maps */ + GfxFillRect(11, 63, 258, 215, 0xD7); // black background of maps list + + pos = this->vscroll.pos; + while (pos < _fios_num + 1) { + item = _fios_list + pos - 1; + if (item == this->map || (pos == 0 && this->map == NULL)) + GfxFillRect(11, y - 1, 258, y + 10, 155); // show highlighted item with a different colour + + if (pos == 0) { + DrawString(14, y, STR_4010_GENERATE_RANDOM_NEW_GAME, TC_DARK_GREEN); + } else { + DoDrawString(item->title, 14, y, _fios_colors[item->type] ); } - - SetWindowDirty(w); - break; - - case WE_MOUSELOOP: - if (nd->field == NSSW_GAMENAME) HandleEditBox(w, &WP(w, network_ql_d).q, NSSW_GAMENAME); - break; - - case WE_KEYPRESS: - if (nd->field == NSSW_GAMENAME) { - if (HandleEditBoxKey(w, &WP(w, network_ql_d).q, NSSW_GAMENAME, e) == 1) break; // enter pressed - - ttd_strlcpy(_network_server_name, WP(w, network_ql_d).q.text.buf, sizeof(_network_server_name)); - } - break; + pos++; + y += NSSWND_ROWSIZE; - case WE_ON_EDIT_TEXT: - if (e->we.edittext.str == NULL) break; + if (y >= this->vscroll.cap * NSSWND_ROWSIZE + NSSWND_START) break; + } + } - if (nd->widget_id == NSSW_SETPWD) { - ttd_strlcpy(_network_server_password, e->we.edittext.str, lengthof(_network_server_password)); - _network_game_info.use_password = (_network_server_password[0] != '\0'); - } else { - int32 value = atoi(e->we.edittext.str); - w->InvalidateWidget(nd->widget_id); - switch (nd->widget_id) { - default: NOT_REACHED(); - case NSSW_CLIENTS_TXT: _network_game_info.clients_max = Clamp(value, 2, MAX_CLIENTS); break; - case NSSW_COMPANIES_TXT: _network_game_info.companies_max = Clamp(value, 1, MAX_PLAYERS); break; - case NSSW_SPECTATORS_TXT: _network_game_info.spectators_max = Clamp(value, 0, MAX_CLIENTS); break; + virtual void OnClick(Point pt, int widget) + { + if (widget != NSSW_CONNTYPE_BTN && widget != NSSW_LANGUAGE_BTN) HideDropDownMenu(this); + this->field = widget; + switch (widget) { + case NSSW_CLOSE: // Close 'X' + case NSSW_CANCEL: // Cancel button + ShowNetworkGameWindow(); + break; + + case NSSW_GAMENAME: + ShowOnScreenKeyboard(this, NSSW_GAMENAME, 0, 0); + break; + + case NSSW_SETPWD: // Set password button + this->widget_id = NSSW_SETPWD; + ShowQueryString(BindCString(_network_server_password), STR_NETWORK_SET_PASSWORD, 20, 250, this, CS_ALPHANUMERAL); + break; + + case NSSW_SELMAP: { // Select map + int y = (pt.y - NSSWND_START) / NSSWND_ROWSIZE; + + y += this->vscroll.pos; + if (y >= this->vscroll.count) return; + + this->map = (y == 0) ? NULL : _fios_list + y - 1; + this->SetDirty(); + } break; + + case NSSW_CONNTYPE_BTN: // Connection type + ShowDropDownMenu(this, _connection_types_dropdown, _network_advertise, NSSW_CONNTYPE_BTN, 0, 0); // do it for widget NSSW_CONNTYPE_BTN + break; + + case NSSW_CLIENTS_BTND: case NSSW_CLIENTS_BTNU: // Click on up/down button for number of clients + case NSSW_COMPANIES_BTND: case NSSW_COMPANIES_BTNU: // Click on up/down button for number of companies + case NSSW_SPECTATORS_BTND: case NSSW_SPECTATORS_BTNU: // Click on up/down button for number of spectators + /* Don't allow too fast scrolling */ + if ((this->flags4 & WF_TIMEOUT_MASK) <= 2 << WF_TIMEOUT_SHL) { + this->HandleButtonClick(widget); + this->SetDirty(); + switch (widget) { + default: NOT_REACHED(); + case NSSW_CLIENTS_BTND: case NSSW_CLIENTS_BTNU: + _network_game_info.clients_max = Clamp(_network_game_info.clients_max + widget - NSSW_CLIENTS_TXT, 2, MAX_CLIENTS); + break; + case NSSW_COMPANIES_BTND: case NSSW_COMPANIES_BTNU: + _network_game_info.companies_max = Clamp(_network_game_info.companies_max + widget - NSSW_COMPANIES_TXT, 1, MAX_PLAYERS); + break; + case NSSW_SPECTATORS_BTND: case NSSW_SPECTATORS_BTNU: + _network_game_info.spectators_max = Clamp(_network_game_info.spectators_max + widget - NSSW_SPECTATORS_TXT, 0, MAX_CLIENTS); + break; + } } - } + _left_button_clicked = false; + break; - SetWindowDirty(w); - break; + case NSSW_CLIENTS_TXT: // Click on number of players + this->widget_id = NSSW_CLIENTS_TXT; + SetDParam(0, _network_game_info.clients_max); + ShowQueryString(STR_CONFIG_PATCHES_INT32, STR_NETWORK_NUMBER_OF_CLIENTS, 3, 50, this, CS_NUMERAL); + break; + + case NSSW_COMPANIES_TXT: // Click on number of companies + this->widget_id = NSSW_COMPANIES_TXT; + SetDParam(0, _network_game_info.companies_max); + ShowQueryString(STR_CONFIG_PATCHES_INT32, STR_NETWORK_NUMBER_OF_COMPANIES, 3, 50, this, CS_NUMERAL); + break; + + case NSSW_SPECTATORS_TXT: // Click on number of spectators + this->widget_id = NSSW_SPECTATORS_TXT; + SetDParam(0, _network_game_info.spectators_max); + ShowQueryString(STR_CONFIG_PATCHES_INT32, STR_NETWORK_NUMBER_OF_SPECTATORS, 3, 50, this, CS_NUMERAL); + break; + + case NSSW_LANGUAGE_BTN: { // Language + uint sel = 0; + for (uint i = 0; i < lengthof(_language_dropdown) - 1; i++) { + if (_language_dropdown[i] == STR_NETWORK_LANG_ANY + _network_game_info.server_lang) { + sel = i; + break; + } + } + ShowDropDownMenu(this, _language_dropdown, sel, NSSW_LANGUAGE_BTN, 0, 0); + } break; + + case NSSW_START: // Start game + _is_network_server = true; + + if (this->map == NULL) { // start random new game + ShowGenerateLandscape(); + } else { // load a scenario + char *name = FiosBrowseTo(this->map); + if (name != NULL) { + SetFiosType(this->map->type); + _file_to_saveload.filetype = FT_SCENARIO; + ttd_strlcpy(_file_to_saveload.name, name, sizeof(_file_to_saveload.name)); + ttd_strlcpy(_file_to_saveload.title, this->map->title, sizeof(_file_to_saveload.title)); + + delete this; + SwitchMode(SM_START_SCENARIO); + } + } + break; + + case NSSW_LOAD: // Load game + _is_network_server = true; + /* XXX - WC_NETWORK_WINDOW (this window) should stay, but if it stays, it gets + * copied all the elements of 'load game' and upon closing that, it segfaults */ + delete this; + ShowSaveLoadDialog(SLD_LOAD_GAME); + break; + } } -} + + virtual void OnDropdownSelect(int widget, int index) + { + switch (widget) { + case NSSW_CONNTYPE_BTN: + _network_advertise = (index != 0); + break; + case NSSW_LANGUAGE_BTN: + _network_game_info.server_lang = _language_dropdown[index] - STR_NETWORK_LANG_ANY; + break; + default: + NOT_REACHED(); + } + + this->SetDirty(); + } + + virtual void OnMouseLoop() + { + if (this->field == NSSW_GAMENAME) this->HandleEditBox(NSSW_GAMENAME); + } + + virtual bool OnKeyPress(uint16 key, uint16 keycode) + { + bool cont = true; + if (this->field == NSSW_GAMENAME) { + if (this->HandleEditBoxKey(NSSW_GAMENAME, key, keycode, cont) == 1) return cont; // enter pressed + + ttd_strlcpy(_network_server_name, this->text.buf, sizeof(_network_server_name)); + } + + return cont; + } + + virtual void OnQueryTextFinished(char *str) + { + if (str == NULL) return; + + if (this->widget_id == NSSW_SETPWD) { + ttd_strlcpy(_network_server_password, str, lengthof(_network_server_password)); + _network_game_info.use_password = !StrEmpty(_network_server_password); + } else { + int32 value = atoi(str); + this->InvalidateWidget(this->widget_id); + switch (this->widget_id) { + default: NOT_REACHED(); + case NSSW_CLIENTS_TXT: _network_game_info.clients_max = Clamp(value, 2, MAX_CLIENTS); break; + case NSSW_COMPANIES_TXT: _network_game_info.companies_max = Clamp(value, 1, MAX_PLAYERS); break; + case NSSW_SPECTATORS_TXT: _network_game_info.spectators_max = Clamp(value, 0, MAX_CLIENTS); break; + } + } + + this->SetDirty(); + } +}; static const Widget _network_start_server_window_widgets[] = { /* Window decoration and background panel */ @@ -985,23 +976,14 @@ WC_NETWORK_WINDOW, WC_NONE, WDF_STD_TOOLTIPS | WDF_DEF_WIDGET | WDF_UNCLICK_BUTTONS, _network_start_server_window_widgets, - NetworkStartServerWindowWndProc, + NULL, }; static void ShowNetworkStartServerWindow() { DeleteWindowById(WC_NETWORK_WINDOW, 0); - Window *w = AllocateWindowDesc(&_network_start_server_window_desc); - ttd_strlcpy(_edit_str_net_buf, _network_server_name, lengthof(_edit_str_net_buf)); - - _saveload_mode = SLD_NEW_GAME; - BuildFileList(); - w->vscroll.cap = 12; - w->vscroll.count = _fios_num + 1; - - WP(w, network_ql_d).q.afilter = CS_ALPHANUMERAL; - InitializeTextBuffer(&WP(w, network_ql_d).q.text, _edit_str_net_buf, lengthof(_edit_str_net_buf), 160); + new NetworkStartServerWindow(&_network_start_server_window_desc); } static PlayerID NetworkLobbyFindCompanyIndex(byte pos) @@ -1028,164 +1010,156 @@ NLWW_CANCEL = 12, ///< 'Cancel' button }; -/** - * Handler of actions done in the NetworkLobby window - * - * @param w pointer to the Window structure - * @param e pointer to window event - * @note uses network_d WP macro - * @see struct _network_lobby_window_widgets - * @see enum NetworkLobbyWindowWidgets - */ -static void NetworkLobbyWindowWndProc(Window *w, WindowEvent *e) -{ - network_d *nd = &WP(w, network_d); - - switch (e->event) { - case WE_CREATE: - nd->company = INVALID_PLAYER; - break; - - case WE_PAINT: { - const NetworkGameInfo *gi = &nd->server->info; - int y = NET_PRC__OFFSET_TOP_WIDGET_COMPANY, pos; +struct NetworkLobbyWindow : public Window { + PlayerID company; ///< Select company + NetworkGameList *server; ///< Selected server - /* Join button is disabled when no company is selected */ - w->SetWidgetDisabledState(NLWW_JOIN, nd->company == INVALID_PLAYER); - /* Cannot start new company if there are too many */ - w->SetWidgetDisabledState(NLWW_NEW, gi->companies_on >= gi->companies_max); - /* Cannot spectate if there are too many spectators */ - w->SetWidgetDisabledState(NLWW_SPECTATE, gi->spectators_on >= gi->spectators_max); - - /* Draw window widgets */ - SetDParamStr(0, gi->server_name); - DrawWindowWidgets(w); + NetworkLobbyWindow(const WindowDesc *desc, NetworkGameList *ngl) : + Window(desc), company(INVALID_PLAYER), server(ngl) + { + this->vscroll.cap = 10; - /* Draw company list */ - pos = w->vscroll.pos; - while (pos < gi->companies_on) { - byte company = NetworkLobbyFindCompanyIndex(pos); - bool income = false; - if (nd->company == company) - GfxFillRect(11, y - 1, 154, y + 10, 10); // show highlighted item with a different colour + this->FindWindowPlacementAndResize(desc); + } - DoDrawStringTruncated(_network_player_info[company].company_name, 13, y, TC_BLACK, 135 - 13); - if (_network_player_info[company].use_password != 0) DrawSprite(SPR_LOCK, PAL_NONE, 135, y); + virtual void OnPaint() + { + const NetworkGameInfo *gi = &this->server->info; + int y = NET_PRC__OFFSET_TOP_WIDGET_COMPANY, pos; - /* If the company's income was positive puts a green dot else a red dot */ - if (_network_player_info[company].income >= 0) income = true; - DrawSprite(SPR_BLOT, income ? PALETTE_TO_GREEN : PALETTE_TO_RED, 145, y); + /* Join button is disabled when no company is selected */ + this->SetWidgetDisabledState(NLWW_JOIN, this->company == INVALID_PLAYER); + /* Cannot start new company if there are too many */ + this->SetWidgetDisabledState(NLWW_NEW, gi->companies_on >= gi->companies_max); + /* Cannot spectate if there are too many spectators */ + this->SetWidgetDisabledState(NLWW_SPECTATE, gi->spectators_on >= gi->spectators_max); - pos++; - y += NET_PRC__SIZE_OF_ROW; - if (pos >= w->vscroll.cap) break; + /* Draw window widgets */ + SetDParamStr(0, gi->server_name); + DrawWindowWidgets(this); + + /* Draw company list */ + pos = this->vscroll.pos; + while (pos < gi->companies_on) { + byte company = NetworkLobbyFindCompanyIndex(pos); + bool income = false; + if (this->company == company) { + GfxFillRect(11, y - 1, 154, y + 10, 10); // show highlighted item with a different colour } - /* Draw info about selected company when it is selected in the left window */ - GfxFillRect(174, 39, 403, 75, 157); - DrawStringCentered(290, 50, STR_NETWORK_COMPANY_INFO, TC_FROMSTRING); - if (nd->company != INVALID_PLAYER) { - const uint x = 183; - const uint trunc_width = w->widget[NLWW_DETAILS].right - x; - y = 80; - - SetDParam(0, nd->server->info.clients_on); - SetDParam(1, nd->server->info.clients_max); - SetDParam(2, nd->server->info.companies_on); - SetDParam(3, nd->server->info.companies_max); - DrawString(x, y, STR_NETWORK_CLIENTS, TC_GOLD); - y += 10; - - SetDParamStr(0, _network_player_info[nd->company].company_name); - DrawStringTruncated(x, y, STR_NETWORK_COMPANY_NAME, TC_GOLD, trunc_width); - y += 10; - - SetDParam(0, _network_player_info[nd->company].inaugurated_year); - DrawString(x, y, STR_NETWORK_INAUGURATION_YEAR, TC_GOLD); // inauguration year - y += 10; - - SetDParam(0, _network_player_info[nd->company].company_value); - DrawString(x, y, STR_NETWORK_VALUE, TC_GOLD); // company value - y += 10; - - SetDParam(0, _network_player_info[nd->company].money); - DrawString(x, y, STR_NETWORK_CURRENT_BALANCE, TC_GOLD); // current balance - y += 10; - - SetDParam(0, _network_player_info[nd->company].income); - DrawString(x, y, STR_NETWORK_LAST_YEARS_INCOME, TC_GOLD); // last year's income - y += 10; - - SetDParam(0, _network_player_info[nd->company].performance); - DrawString(x, y, STR_NETWORK_PERFORMANCE, TC_GOLD); // performance - y += 10; - - SetDParam(0, _network_player_info[nd->company].num_vehicle[0]); - SetDParam(1, _network_player_info[nd->company].num_vehicle[1]); - SetDParam(2, _network_player_info[nd->company].num_vehicle[2]); - SetDParam(3, _network_player_info[nd->company].num_vehicle[3]); - SetDParam(4, _network_player_info[nd->company].num_vehicle[4]); - DrawString(x, y, STR_NETWORK_VEHICLES, TC_GOLD); // vehicles - y += 10; + DoDrawStringTruncated(_network_player_info[company].company_name, 13, y, TC_BLACK, 135 - 13); + if (_network_player_info[company].use_password != 0) DrawSprite(SPR_LOCK, PAL_NONE, 135, y); - SetDParam(0, _network_player_info[nd->company].num_station[0]); - SetDParam(1, _network_player_info[nd->company].num_station[1]); - SetDParam(2, _network_player_info[nd->company].num_station[2]); - SetDParam(3, _network_player_info[nd->company].num_station[3]); - SetDParam(4, _network_player_info[nd->company].num_station[4]); - DrawString(x, y, STR_NETWORK_STATIONS, TC_GOLD); // stations - y += 10; - - SetDParamStr(0, _network_player_info[nd->company].players); - DrawStringTruncated(x, y, STR_NETWORK_PLAYERS, TC_GOLD, trunc_width); // players - } - } break; - - case WE_CLICK: - switch (e->we.click.widget) { - case NLWW_CLOSE: // Close 'X' - case NLWW_CANCEL: // Cancel button - ShowNetworkGameWindow(); - break; - - case NLWW_MATRIX: { // Company list - uint32 id_v = (e->we.click.pt.y - NET_PRC__OFFSET_TOP_WIDGET_COMPANY) / NET_PRC__SIZE_OF_ROW; - - if (id_v >= w->vscroll.cap) break; + /* If the company's income was positive puts a green dot else a red dot */ + if (_network_player_info[company].income >= 0) income = true; + DrawSprite(SPR_BLOT, income ? PALETTE_TO_GREEN : PALETTE_TO_RED, 145, y); - id_v += w->vscroll.pos; - nd->company = (id_v >= nd->server->info.companies_on) ? INVALID_PLAYER : NetworkLobbyFindCompanyIndex(id_v); - SetWindowDirty(w); - } break; - - case NLWW_JOIN: // Join company - /* Button can be clicked only when it is enabled */ - _network_playas = nd->company; - NetworkClientConnectGame(_network_last_host, _network_last_port); - break; - - case NLWW_NEW: // New company - _network_playas = PLAYER_NEW_COMPANY; - NetworkClientConnectGame(_network_last_host, _network_last_port); - break; + pos++; + y += NET_PRC__SIZE_OF_ROW; + if (pos >= this->vscroll.cap) break; + } - case NLWW_SPECTATE: // Spectate game - _network_playas = PLAYER_SPECTATOR; - NetworkClientConnectGame(_network_last_host, _network_last_port); - break; + /* Draw info about selected company when it is selected in the left window */ + GfxFillRect(174, 39, 403, 75, 157); + DrawStringCentered(290, 50, STR_NETWORK_COMPANY_INFO, TC_FROMSTRING); + if (this->company != INVALID_PLAYER) { + const uint x = 183; + const uint trunc_width = this->widget[NLWW_DETAILS].right - x; + y = 80; - case NLWW_REFRESH: // Refresh - NetworkTCPQueryServer(_network_last_host, _network_last_port); // company info - NetworkUDPQueryServer(_network_last_host, _network_last_port); // general data - break; - } - break; + SetDParam(0, gi->clients_on); + SetDParam(1, gi->clients_max); + SetDParam(2, gi->companies_on); + SetDParam(3, gi->companies_max); + DrawString(x, y, STR_NETWORK_CLIENTS, TC_GOLD); + y += 10; - case WE_MESSAGE: - SetWindowDirty(w); - break; + SetDParamStr(0, _network_player_info[this->company].company_name); + DrawStringTruncated(x, y, STR_NETWORK_COMPANY_NAME, TC_GOLD, trunc_width); + y += 10; + + SetDParam(0, _network_player_info[this->company].inaugurated_year); + DrawString(x, y, STR_NETWORK_INAUGURATION_YEAR, TC_GOLD); // inauguration year + y += 10; + + SetDParam(0, _network_player_info[this->company].company_value); + DrawString(x, y, STR_NETWORK_VALUE, TC_GOLD); // company value + y += 10; + + SetDParam(0, _network_player_info[this->company].money); + DrawString(x, y, STR_NETWORK_CURRENT_BALANCE, TC_GOLD); // current balance + y += 10; + + SetDParam(0, _network_player_info[this->company].income); + DrawString(x, y, STR_NETWORK_LAST_YEARS_INCOME, TC_GOLD); // last year's income + y += 10; + + SetDParam(0, _network_player_info[this->company].performance); + DrawString(x, y, STR_NETWORK_PERFORMANCE, TC_GOLD); // performance + y += 10; + + SetDParam(0, _network_player_info[this->company].num_vehicle[0]); + SetDParam(1, _network_player_info[this->company].num_vehicle[1]); + SetDParam(2, _network_player_info[this->company].num_vehicle[2]); + SetDParam(3, _network_player_info[this->company].num_vehicle[3]); + SetDParam(4, _network_player_info[this->company].num_vehicle[4]); + DrawString(x, y, STR_NETWORK_VEHICLES, TC_GOLD); // vehicles + y += 10; + + SetDParam(0, _network_player_info[this->company].num_station[0]); + SetDParam(1, _network_player_info[this->company].num_station[1]); + SetDParam(2, _network_player_info[this->company].num_station[2]); + SetDParam(3, _network_player_info[this->company].num_station[3]); + SetDParam(4, _network_player_info[this->company].num_station[4]); + DrawString(x, y, STR_NETWORK_STATIONS, TC_GOLD); // stations + y += 10; + + SetDParamStr(0, _network_player_info[this->company].players); + DrawStringTruncated(x, y, STR_NETWORK_PLAYERS, TC_GOLD, trunc_width); // players + } } -} + + virtual void OnClick(Point pt, int widget) + { + switch (widget) { + case NLWW_CLOSE: // Close 'X' + case NLWW_CANCEL: // Cancel button + ShowNetworkGameWindow(); + break; + + case NLWW_MATRIX: { // Company list + uint32 id_v = (pt.y - NET_PRC__OFFSET_TOP_WIDGET_COMPANY) / NET_PRC__SIZE_OF_ROW; + + if (id_v >= this->vscroll.cap) break; + + id_v += this->vscroll.pos; + this->company = (id_v >= this->server->info.companies_on) ? INVALID_PLAYER : NetworkLobbyFindCompanyIndex(id_v); + this->SetDirty(); + } break; + + case NLWW_JOIN: // Join company + /* Button can be clicked only when it is enabled */ + _network_playas = this->company; + NetworkClientConnectGame(_network_last_host, _network_last_port); + break; + + case NLWW_NEW: // New company + _network_playas = PLAYER_NEW_COMPANY; + NetworkClientConnectGame(_network_last_host, _network_last_port); + break; + + case NLWW_SPECTATE: // Spectate game + _network_playas = PLAYER_SPECTATOR; + NetworkClientConnectGame(_network_last_host, _network_last_port); + break; + + case NLWW_REFRESH: // Refresh + NetworkTCPQueryServer(_network_last_host, _network_last_port); // company info + NetworkUDPQueryServer(_network_last_host, _network_last_port); // general data + break; + } + } +}; static const Widget _network_lobby_window_widgets[] = { { WWT_CLOSEBOX, RESIZE_NONE, BGC, 0, 10, 0, 13, STR_00C5, STR_018B_CLOSE_WINDOW }, // NLWW_CLOSE @@ -1216,7 +1190,7 @@ WC_NETWORK_WINDOW, WC_NONE, WDF_STD_TOOLTIPS | WDF_DEF_WIDGET | WDF_UNCLICK_BUTTONS, _network_lobby_window_widgets, - NetworkLobbyWindowWndProc, + NULL, }; /* Show the networklobbywindow with the selected server @@ -1228,12 +1202,7 @@ NetworkTCPQueryServer(_network_last_host, _network_last_port); // company info NetworkUDPQueryServer(_network_last_host, _network_last_port); // general data - Window *w = AllocateWindowDesc(&_network_lobby_window_desc); - if (w != NULL) { - WP(w, network_ql_d).n.server = ngl; - strcpy(_edit_str_net_buf, ""); - w->vscroll.cap = 10; - } + new NetworkLobbyWindow(&_network_lobby_window_desc, ngl); } // The window below gives information about the connected clients @@ -1381,10 +1350,10 @@ /* If height is changed */ if (w->height != CLNWND_OFFSET + num + 1) { // XXX - magic unfortunately; (num + 2) has to be one bigger than heigh (num + 1) - SetWindowDirty(w); + w->SetDirty(); w->widget[3].bottom = w->widget[3].top + num + 2; w->height = CLNWND_OFFSET + num + 1; - SetWindowDirty(w); + w->SetDirty(); return false; } return true; @@ -1471,7 +1440,7 @@ int h = ClientListPopupHeight(); /* Allocate the popup */ - w = AllocateWindow(x, y, 150, h + 1, ClientListPopupWndProc, WC_TOOLBAR_MENU, _client_list_popup_widgets); + w = new Window(x, y, 150, h + 1, ClientListPopupWndProc, WC_TOOLBAR_MENU, _client_list_popup_widgets); w->widget[0].bottom = w->widget[0].top + h; w->widget[0].right = w->widget[0].left + 150; @@ -1480,8 +1449,6 @@ // Save our client WP(w, menu_d).main_button = client_no; WP(w, menu_d).sel_index = 0; - // We are a popup - _popup_menu_active = true; return w; } @@ -1513,25 +1480,22 @@ } } break; - case WE_POPUPMENU_SELECT: { + case WE_MOUSELOOP: { /* We selected an action */ - int index = (e->we.popupmenu.pt.y - w->top) / CLNWND_ROWSIZE; - - if (index >= 0 && e->we.popupmenu.pt.y >= w->top) { - HandleClientListPopupClick(index, WP(w, menu_d).main_button); - } + int index = (_cursor.pos.y - w->top) / CLNWND_ROWSIZE; - DeleteWindowById(WC_TOOLBAR_MENU, 0); - } break; + if (_left_button_down) { + if (index == -1 || index == WP(w, menu_d).sel_index) return; - case WE_POPUPMENU_OVER: { - /* Our mouse hoovers over an action? Select it! */ - int index = (e->we.popupmenu.pt.y - w->top) / CLNWND_ROWSIZE; + WP(w, menu_d).sel_index = index; + w->SetDirty(); + } else { + if (index >= 0 && _cursor.pos.y >= w->top) { + HandleClientListPopupClick(index, WP(w, menu_d).main_button); + } - if (index == -1 || index == WP(w, menu_d).sel_index) return; - - WP(w, menu_d).sel_index = index; - SetWindowDirty(w); + DeleteWindowById(WC_TOOLBAR_MENU, 0); + } } break; } } @@ -1589,7 +1553,7 @@ if (e->we.mouseover.pt.y == -1) { _selected_clientlist_y = 0; _selected_clientlist_item = 255; - SetWindowDirty(w); + w->SetDirty(); break; } /* It did not change.. no update! */ @@ -1604,7 +1568,7 @@ } /* Repaint */ - SetWindowDirty(w); + w->SetDirty(); break; case WE_DESTROY: case WE_CREATE: @@ -1617,7 +1581,7 @@ void ShowClientList() { - AllocateWindowDescFront(&_client_list_desc, 0); + AllocateWindowDescFront(&_client_list_desc, 0); } @@ -1672,21 +1636,19 @@ case WE_CLICK: if (e->we.click.widget == 2) { //Disconnect button NetworkDisconnect(); - delete w; SwitchMode(SM_MENU); ShowNetworkGameWindow(); } break; - /* If the server asks for a password, we need to fill it in */ - case WE_ON_EDIT_TEXT_CANCEL: + case WE_ON_EDIT_TEXT: + if (StrEmpty(e->we.edittext.str)) { NetworkDisconnect(); ShowNetworkGameWindow(); - break; - - case WE_ON_EDIT_TEXT: + } else { SEND_COMMAND(PACKET_CLIENT_PASSWORD)(pw_type, e->we.edittext.str); - break; + } + break; } } @@ -1708,7 +1670,7 @@ void ShowJoinStatusWindow() { DeleteWindowById(WC_NETWORK_STATUS_WINDOW, 0); - Window *w = AllocateWindowDesc(&_network_join_status_window_desc); + Window *w = new Window(&_network_join_status_window_desc); /* Parent the status window to the lobby */ if (w != NULL) w->parent = FindWindowById(WC_NETWORK_WINDOW, 0); } @@ -1723,202 +1685,217 @@ } } -/** - * Find the next item of the list of things that can be auto-completed. - * @param item The current indexed item to return. This function can, and most - * likely will, alter item, to skip empty items in the arrays. - * @return Returns the char that matched to the index. - */ -static const char *ChatTabCompletionNextItem(uint *item) -{ - static char chat_tab_temp_buffer[64]; - /* First, try clients */ - if (*item < MAX_CLIENT_INFO) { - /* Skip inactive clients */ - while (_network_client_info[*item].client_index == NETWORK_EMPTY_INDEX && *item < MAX_CLIENT_INFO) (*item)++; - if (*item < MAX_CLIENT_INFO) return _network_client_info[*item].client_name; +struct NetworkChatWindow : public QueryStringBaseWindow { + DestType dtype; + int dest; + + NetworkChatWindow (const WindowDesc *desc, DestType type, int dest) : QueryStringBaseWindow(desc) + { + this->LowerWidget(2); + this->dtype = type; + this->dest = dest; + this->afilter = CS_ALPHANUMERAL; + InitializeTextBuffer(&this->text, this->edit_str_buf, lengthof(this->edit_str_buf), 0); + + InvalidateWindowData(WC_NEWS_WINDOW, 0, this->height); + SetBit(_no_scroll, SCROLL_CHAT); // do not scroll the game with the arrow-keys + + _chat_tab_completion_active = false; + + this->FindWindowPlacementAndResize(desc); } - /* Then, try townnames */ - /* Not that the following assumes all town indices are adjacent, ie no - * towns have been deleted. */ - if (*item <= (uint)MAX_CLIENT_INFO + GetMaxTownIndex()) { - const Town *t; + ~NetworkChatWindow () + { + InvalidateWindowData(WC_NEWS_WINDOW, 0, 0); + ClrBit(_no_scroll, SCROLL_CHAT); + } - FOR_ALL_TOWNS_FROM(t, *item - MAX_CLIENT_INFO) { - /* Get the town-name via the string-system */ - SetDParam(0, t->index); - GetString(chat_tab_temp_buffer, STR_TOWN, lastof(chat_tab_temp_buffer)); - return &chat_tab_temp_buffer[0]; + /** + * Find the next item of the list of things that can be auto-completed. + * @param item The current indexed item to return. This function can, and most + * likely will, alter item, to skip empty items in the arrays. + * @return Returns the char that matched to the index. + */ + const char *ChatTabCompletionNextItem(uint *item) + { + static char chat_tab_temp_buffer[64]; + + /* First, try clients */ + if (*item < MAX_CLIENT_INFO) { + /* Skip inactive clients */ + while (_network_client_info[*item].client_index == NETWORK_EMPTY_INDEX && *item < MAX_CLIENT_INFO) (*item)++; + if (*item < MAX_CLIENT_INFO) return _network_client_info[*item].client_name; + } + + /* Then, try townnames */ + /* Not that the following assumes all town indices are adjacent, ie no + * towns have been deleted. */ + if (*item <= (uint)MAX_CLIENT_INFO + GetMaxTownIndex()) { + const Town *t; + + FOR_ALL_TOWNS_FROM(t, *item - MAX_CLIENT_INFO) { + /* Get the town-name via the string-system */ + SetDParam(0, t->index); + GetString(chat_tab_temp_buffer, STR_TOWN, lastof(chat_tab_temp_buffer)); + return &chat_tab_temp_buffer[0]; + } + } + + return NULL; + } + + /** + * Find what text to complete. It scans for a space from the left and marks + * the word right from that as to complete. It also writes a \0 at the + * position of the space (if any). If nothing found, buf is returned. + */ + static char *ChatTabCompletionFindText(char *buf) + { + char *p = strrchr(buf, ' '); + if (p == NULL) return buf; + + *p = '\0'; + return p + 1; + } + + /** + * See if we can auto-complete the current text of the user. + */ + void ChatTabCompletion() + { + static char _chat_tab_completion_buf[lengthof(this->edit_str_buf)]; + Textbuf *tb = &this->text; + size_t len, tb_len; + uint item; + char *tb_buf, *pre_buf; + const char *cur_name; + bool second_scan = false; + + item = 0; + + /* Copy the buffer so we can modify it without damaging the real data */ + pre_buf = (_chat_tab_completion_active) ? strdup(_chat_tab_completion_buf) : strdup(tb->buf); + + tb_buf = ChatTabCompletionFindText(pre_buf); + tb_len = strlen(tb_buf); + + while ((cur_name = ChatTabCompletionNextItem(&item)) != NULL) { + item++; + + if (_chat_tab_completion_active) { + /* We are pressing TAB again on the same name, is there an other name + * that starts with this? */ + if (!second_scan) { + size_t offset; + size_t length; + + /* If we are completing at the begin of the line, skip the ': ' we added */ + if (tb_buf == pre_buf) { + offset = 0; + length = tb->length - 2; + } else { + /* Else, find the place we are completing at */ + offset = strlen(pre_buf) + 1; + length = tb->length - offset; + } + + /* Compare if we have a match */ + if (strlen(cur_name) == length && strncmp(cur_name, tb->buf + offset, length) == 0) second_scan = true; + + continue; + } + + /* Now any match we make on _chat_tab_completion_buf after this, is perfect */ + } + + len = strlen(cur_name); + if (tb_len < len && strncasecmp(cur_name, tb_buf, tb_len) == 0) { + /* Save the data it was before completion */ + if (!second_scan) snprintf(_chat_tab_completion_buf, lengthof(_chat_tab_completion_buf), "%s", tb->buf); + _chat_tab_completion_active = true; + + /* Change to the found name. Add ': ' if we are at the start of the line (pretty) */ + if (pre_buf == tb_buf) { + snprintf(tb->buf, lengthof(this->edit_str_buf), "%s: ", cur_name); + } else { + snprintf(tb->buf, lengthof(this->edit_str_buf), "%s %s", pre_buf, cur_name); + } + + /* Update the textbuffer */ + UpdateTextBufferSize(&this->text); + + this->SetDirty(); + free(pre_buf); + return; + } + } + + if (second_scan) { + /* We walked all posibilities, and the user presses tab again.. revert to original text */ + strcpy(tb->buf, _chat_tab_completion_buf); + _chat_tab_completion_active = false; + + /* Update the textbuffer */ + UpdateTextBufferSize(&this->text); + + this->SetDirty(); + } + free(pre_buf); + } + + virtual void OnPaint() + { + static const StringID chat_captions[] = { + STR_NETWORK_CHAT_ALL_CAPTION, + STR_NETWORK_CHAT_COMPANY_CAPTION, + STR_NETWORK_CHAT_CLIENT_CAPTION + }; + + DrawWindowWidgets(this); + + assert((uint)this->dtype < lengthof(chat_captions)); + DrawStringRightAligned(this->widget[2].left - 2, this->widget[2].top + 1, chat_captions[this->dtype], TC_BLACK); + this->DrawEditBox(2); + } + + virtual void OnClick(Point pt, int widget) + { + switch (widget) { + case 2: + ShowOnScreenKeyboard(this, 2, 0, 3); + break; + + case 3: /* Send */ + SendChat(this->text.buf, this->dtype, this->dest); + /* FALLTHROUGH */ + case 0: /* Cancel */ delete this; break; } } - return NULL; -} - -/** - * Find what text to complete. It scans for a space from the left and marks - * the word right from that as to complete. It also writes a \0 at the - * position of the space (if any). If nothing found, buf is returned. - */ -static char *ChatTabCompletionFindText(char *buf) -{ - char *p = strrchr(buf, ' '); - if (p == NULL) return buf; - - *p = '\0'; - return p + 1; -} - -/** - * See if we can auto-complete the current text of the user. - */ -static void ChatTabCompletion(Window *w) -{ - static char _chat_tab_completion_buf[lengthof(_edit_str_net_buf)]; - Textbuf *tb = &WP(w, chatquerystr_d).text; - uint len, tb_len; - uint item; - char *tb_buf, *pre_buf; - const char *cur_name; - bool second_scan = false; - - item = 0; - - /* Copy the buffer so we can modify it without damaging the real data */ - pre_buf = (_chat_tab_completion_active) ? strdup(_chat_tab_completion_buf) : strdup(tb->buf); - - tb_buf = ChatTabCompletionFindText(pre_buf); - tb_len = strlen(tb_buf); - - while ((cur_name = ChatTabCompletionNextItem(&item)) != NULL) { - item++; - - if (_chat_tab_completion_active) { - /* We are pressing TAB again on the same name, is there an other name - * that starts with this? */ - if (!second_scan) { - uint offset; - uint length; - - /* If we are completing at the begin of the line, skip the ': ' we added */ - if (tb_buf == pre_buf) { - offset = 0; - length = tb->length - 2; - } else { - /* Else, find the place we are completing at */ - offset = strlen(pre_buf) + 1; - length = tb->length - offset; - } - - /* Compare if we have a match */ - if (strlen(cur_name) == length && strncmp(cur_name, tb->buf + offset, length) == 0) second_scan = true; - - continue; - } - - /* Now any match we make on _chat_tab_completion_buf after this, is perfect */ - } - - len = strlen(cur_name); - if (tb_len < len && strncasecmp(cur_name, tb_buf, tb_len) == 0) { - /* Save the data it was before completion */ - if (!second_scan) snprintf(_chat_tab_completion_buf, lengthof(_chat_tab_completion_buf), "%s", tb->buf); - _chat_tab_completion_active = true; - - /* Change to the found name. Add ': ' if we are at the start of the line (pretty) */ - if (pre_buf == tb_buf) { - snprintf(tb->buf, lengthof(_edit_str_net_buf), "%s: ", cur_name); - } else { - snprintf(tb->buf, lengthof(_edit_str_net_buf), "%s %s", pre_buf, cur_name); - } - - /* Update the textbuffer */ - UpdateTextBufferSize(&WP(w, chatquerystr_d).text); - - SetWindowDirty(w); - free(pre_buf); - return; - } + virtual void OnMouseLoop() + { + this->HandleEditBox(2); } - if (second_scan) { - /* We walked all posibilities, and the user presses tab again.. revert to original text */ - strcpy(tb->buf, _chat_tab_completion_buf); - _chat_tab_completion_active = false; - - /* Update the textbuffer */ - UpdateTextBufferSize(&WP(w, chatquerystr_d).text); - - SetWindowDirty(w); + virtual bool OnKeyPress(uint16 key, uint16 keycode) + { + bool cont = true; + if (keycode == WKC_TAB) { + ChatTabCompletion(); + } else { + _chat_tab_completion_active = false; + switch (this->HandleEditBoxKey(2, key, keycode, cont)) { + case 1: /* Return */ + SendChat(this->text.buf, this->dtype, this->dest); + /* FALLTHROUGH */ + case 2: /* Escape */ delete this; break; + } + } + return cont; } - free(pre_buf); -} - -/* - * uses chatquerystr_d WP macro - * uses chatquerystr_d->dtype to store type of chat message (Private/Team/All) - */ -static void ChatWindowWndProc(Window *w, WindowEvent *e) -{ - switch (e->event) { - case WE_CREATE: - SendWindowMessage(WC_NEWS_WINDOW, 0, WE_CREATE, w->height, 0); - SetBit(_no_scroll, SCROLL_CHAT); // do not scroll the game with the arrow-keys - break; - - case WE_PAINT: { - static const StringID chat_captions[] = { - STR_NETWORK_CHAT_ALL_CAPTION, - STR_NETWORK_CHAT_COMPANY_CAPTION, - STR_NETWORK_CHAT_CLIENT_CAPTION - }; - - DrawWindowWidgets(w); - - assert((uint)WP(w, chatquerystr_d).dtype < lengthof(chat_captions)); - DrawStringRightAligned(w->widget[2].left - 2, w->widget[2].top + 1, chat_captions[WP(w, chatquerystr_d).dtype], TC_BLACK); - DrawEditBox(w, &WP(w, chatquerystr_d), 2); - } break; - - case WE_CLICK: - switch (e->we.click.widget) { - case 2: - ShowOnScreenKeyboard(w, &WP(w, chatquerystr_d), 2, 0, 3); - break; - - case 3: /* Send */ - SendChat(WP(w, chatquerystr_d).text.buf, WP(w, chatquerystr_d).dtype, WP(w, chatquerystr_d).dest); - /* FALLTHROUGH */ - case 0: /* Cancel */ delete w; break; - } - break; - - case WE_MOUSELOOP: - HandleEditBox(w, &WP(w, chatquerystr_d), 2); - break; - - case WE_KEYPRESS: - if (e->we.keypress.keycode == WKC_TAB) { - ChatTabCompletion(w); - } else { - _chat_tab_completion_active = false; - switch (HandleEditBoxKey(w, &WP(w, chatquerystr_d), 2, e)) { - case 1: /* Return */ - SendChat(WP(w, chatquerystr_d).text.buf, WP(w, chatquerystr_d).dtype, WP(w, chatquerystr_d).dest); - /* FALLTHROUGH */ - case 2: /* Escape */ delete w; break; - } - } - break; - - case WE_DESTROY: - SendWindowMessage(WC_NEWS_WINDOW, 0, WE_DESTROY, 0, 0); - ClrBit(_no_scroll, SCROLL_CHAT); - break; - } -} +}; static const Widget _chat_window_widgets[] = { { WWT_CLOSEBOX, RESIZE_NONE, 14, 0, 10, 0, 13, STR_00C5, STR_018B_CLOSE_WINDOW}, @@ -1933,23 +1910,13 @@ WC_SEND_NETWORK_MSG, WC_NONE, WDF_STD_TOOLTIPS | WDF_DEF_WIDGET, _chat_window_widgets, - ChatWindowWndProc + NULL }; void ShowNetworkChatQueryWindow(DestType type, int dest) { DeleteWindowById(WC_SEND_NETWORK_MSG, 0); - - _edit_str_net_buf[0] = '\0'; - _chat_tab_completion_active = false; - - Window *w = AllocateWindowDesc(&_chat_window_desc); - - w->LowerWidget(2); - WP(w, chatquerystr_d).dtype = type; - WP(w, chatquerystr_d).dest = dest; - WP(w, chatquerystr_d).afilter = CS_ALPHANUMERAL; - InitializeTextBuffer(&WP(w, chatquerystr_d).text, _edit_str_net_buf, lengthof(_edit_str_net_buf), 0); + new NetworkChatWindow (&_chat_window_desc, type, dest); } /** Enum for NetworkGameWindow, referring to _network_game_window_widgets */ @@ -1964,61 +1931,75 @@ NCPWW_OK, ///< Safe the password etc. }; -static void NetworkCompanyPasswordWindowWndProc(Window *w, WindowEvent *e) -{ - switch (e->event) { - case WE_PAINT: - DrawWindowWidgets(w); - DrawEditBox(w, &WP(w, chatquerystr_d), 4); - break; - - case WE_CLICK: - switch (e->we.click.widget) { - case NCPWW_OK: { - if (w->IsWidgetLowered(NCPWW_SAVE_AS_DEFAULT_PASSWORD)) { - snprintf(_network_default_company_pass, lengthof(_network_default_company_pass), "%s", _edit_str_net_buf); - } - - /* empty password is a '*' because of console argument */ - if (StrEmpty(_edit_str_net_buf)) snprintf(_edit_str_net_buf, lengthof(_edit_str_net_buf), "*"); - char *password = _edit_str_net_buf; - NetworkChangeCompanyPassword(1, &password); - } - - /* FALL THROUGH */ - case NCPWW_CANCEL: - delete w; - break; +struct NetworkCompanyPasswordWindow : public QueryStringBaseWindow { + NetworkCompanyPasswordWindow(const WindowDesc *desc) : QueryStringBaseWindow(desc) + { + this->afilter = CS_ALPHANUMERAL; + InitializeTextBuffer(&this->text, this->edit_str_buf, min(lengthof(_network_default_company_pass), lengthof(this->edit_str_buf)), 0); - case NCPWW_SAVE_AS_DEFAULT_PASSWORD: - w->ToggleWidgetLoweredState(NCPWW_SAVE_AS_DEFAULT_PASSWORD); - SetWindowDirty(w); - break; - case NCPWW_PASSWORD: - ShowOnScreenKeyboard(w, &WP(w, chatquerystr_d), NCPWW_PASSWORD, 2, 1); - break; - } - break; - - case WE_MOUSELOOP: - HandleEditBox(w, &WP(w, chatquerystr_d), 4); - break; + this->FindWindowPlacementAndResize(desc); + } - case WE_KEYPRESS: - switch (HandleEditBoxKey(w, &WP(w, chatquerystr_d), 4, e)) { - case 1: // Return - e->event = WE_CLICK; - e->we.click.widget = NCPWW_OK; - NetworkCompanyPasswordWindowWndProc(w, e); - break; + void OnOk() + { + if (this->IsWidgetLowered(NCPWW_SAVE_AS_DEFAULT_PASSWORD)) { + snprintf(_network_default_company_pass, lengthof(_network_default_company_pass), "%s", this->edit_str_buf); + } - case 2: // Escape - delete w; - break; - } - break; + /* empty password is a '*' because of console argument */ + if (StrEmpty(this->edit_str_buf)) snprintf(this->edit_str_buf, lengthof(this->edit_str_buf), "*"); + char *password = this->edit_str_buf; + NetworkChangeCompanyPassword(1, &password); } -} + + virtual void OnPaint() + { + DrawWindowWidgets(this); + this->DrawEditBox(4); + } + + virtual void OnClick(Point pt, int widget) + { + switch (widget) { + case NCPWW_OK: + this->OnOk(); + + /* FALL THROUGH */ + case NCPWW_CANCEL: + delete this; + break; + + case NCPWW_SAVE_AS_DEFAULT_PASSWORD: + this->ToggleWidgetLoweredState(NCPWW_SAVE_AS_DEFAULT_PASSWORD); + this->SetDirty(); + break; + + case NCPWW_PASSWORD: + ShowOnScreenKeyboard(this, NCPWW_PASSWORD, 2, 1); + break; + } + } + + virtual void OnMouseLoop() + { + this->HandleEditBox(4); + } + + virtual bool OnKeyPress(uint16 key, uint16 keycode) + { + bool cont; + switch (this->HandleEditBoxKey(4, key, keycode, cont)) { + case 1: // Return + this->OnOk(); + /* FALL THROUGH */ + + case 2: // Escape + delete this; + break; + } + return cont; + } +}; static const Widget _ncp_window_widgets[] = { { WWT_CLOSEBOX, RESIZE_NONE, 14, 0, 10, 0, 13, STR_00C5, STR_018B_CLOSE_WINDOW}, @@ -2037,17 +2018,14 @@ WC_COMPANY_PASSWORD_WINDOW, WC_NONE, WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET | WDF_UNCLICK_BUTTONS | WDF_STICKY_BUTTON, _ncp_window_widgets, - NetworkCompanyPasswordWindowWndProc + NULL }; void ShowNetworkCompanyPasswordWindow() { DeleteWindowById(WC_COMPANY_PASSWORD_WINDOW, 0); - _edit_str_net_buf[0] = '\0'; - Window *w = AllocateWindowDesc(&_ncp_window_desc); - WP(w, chatquerystr_d).afilter = CS_ALPHANUMERAL; - InitializeTextBuffer(&WP(w, chatquerystr_d).text, _edit_str_net_buf, min(lengthof(_network_default_company_pass), lengthof(_edit_str_net_buf)), 0); + new NetworkCompanyPasswordWindow(&_ncp_window_desc); } #endif /* ENABLE_NETWORK */