src/network/network_gui.cpp
changeset 9179 780666b686ed
parent 9178 3235f895bf12
child 9180 5ee3a1bb314c
equal deleted inserted replaced
9178:3235f895bf12 9179:780666b686ed
    27 #include "../string_func.h"
    27 #include "../string_func.h"
    28 #include "../gfx_func.h"
    28 #include "../gfx_func.h"
    29 #include "../player_func.h"
    29 #include "../player_func.h"
    30 #include "../settings_type.h"
    30 #include "../settings_type.h"
    31 #include "../widgets/dropdown_func.h"
    31 #include "../widgets/dropdown_func.h"
       
    32 #include "../querystring_gui.h"
    32 
    33 
    33 #include "table/strings.h"
    34 #include "table/strings.h"
    34 #include "../table/sprites.h"
    35 #include "../table/sprites.h"
    35 
    36 
    36 #define BGC 5
    37 #define BGC 5
    37 #define BTC 15
    38 #define BTC 15
    38 
    39 
    39 struct chatquerystr_d : public querystr_d {
    40 /*
    40 	DestType dtype;
       
    41 	int dest;
       
    42 };
       
    43 assert_compile(WINDOW_CUSTOM_SIZE >= sizeof(chatquerystr_d));
       
    44 
       
    45 struct network_d {
    41 struct network_d {
    46 	byte field;              // select text-field in start-server and game-listing
    42 	byte field;              // select text-field in start-server and game-listing
    47 	byte widget_id;          ///< The widget that has the pop-up input menu
    43 	byte widget_id;          ///< The widget that has the pop-up input menu
       
    44 	byte field;              // select text-field in start-server and game-listing
    48 	NetworkGameList *server; // selected server in lobby and game-listing
    45 	NetworkGameList *server; // selected server in lobby and game-listing
    49 	FiosItem *map;           // selected map in start-server
    46 	FiosItem *map;           // selected map in start-server
    50 };
    47 };
    51 assert_compile(WINDOW_CUSTOM_SIZE >= sizeof(network_d));
    48 assert_compile(WINDOW_CUSTOM_SIZE >= sizeof(network_d));
    52 
    49 
    53 struct network_ql_d {
    50 struct network_ql_d {
    54 	network_d n;                 // see above; general stuff
    51 	network_d n;                 // see above; general stuff
    55 	querystr_d q;                // text-input in start-server and game-listing
    52 	querystr_d q;                // text-input in start-server and game-listing
    56 	NetworkGameList **sort_list; // list of games (sorted)
       
    57 	list_d l;                    // accompanying list-administration
    53 	list_d l;                    // accompanying list-administration
    58 };
    54 };
    59 assert_compile(WINDOW_CUSTOM_SIZE >= sizeof(network_ql_d));
    55 assert_compile(WINDOW_CUSTOM_SIZE >= sizeof(network_ql_d));
       
    56 */
    60 
    57 
    61 /* Global to remember sorting after window has been closed */
    58 /* Global to remember sorting after window has been closed */
    62 static Listing _ng_sorting;
    59 static Listing _ng_sorting;
    63 
    60 
    64 static char _edit_str_net_buf[150];
       
    65 static bool _chat_tab_completion_active;
    61 static bool _chat_tab_completion_active;
    66 
    62 
    67 static void ShowNetworkStartServerWindow();
    63 static void ShowNetworkStartServerWindow();
    68 static void ShowNetworkLobbyWindow(NetworkGameList *ngl);
    64 static void ShowNetworkLobbyWindow(NetworkGameList *ngl);
    69 extern void SwitchMode(int new_mode);
    65 extern void SwitchMode(int new_mode);
   157 	if (r == 0) r = strcasecmp(cmp1->info.server_name, cmp2->info.server_name);
   153 	if (r == 0) r = strcasecmp(cmp1->info.server_name, cmp2->info.server_name);
   158 
   154 
   159 	return _internal_sort_order ? -r : r;
   155 	return _internal_sort_order ? -r : r;
   160 }
   156 }
   161 
   157 
   162 /** (Re)build the network game list as its amount has changed because
       
   163  * an item has been added or deleted for example
       
   164  * @param ngl list_d struct that contains all necessary information for sorting */
       
   165 static void BuildNetworkGameList(network_ql_d *nqld)
       
   166 {
       
   167 	NetworkGameList *ngl_temp;
       
   168 	uint n = 0;
       
   169 
       
   170 	if (!(nqld->l.flags & VL_REBUILD)) return;
       
   171 
       
   172 	/* Count the number of games in the list */
       
   173 	for (ngl_temp = _network_game_list; ngl_temp != NULL; ngl_temp = ngl_temp->next) n++;
       
   174 	if (n == 0) return;
       
   175 
       
   176 	/* Create temporary array of games to use for listing */
       
   177 	free(nqld->sort_list);
       
   178 	nqld->sort_list = MallocT<NetworkGameList*>(n);
       
   179 	nqld->l.list_length = n;
       
   180 
       
   181 	for (n = 0, ngl_temp = _network_game_list; ngl_temp != NULL; ngl_temp = ngl_temp->next) {
       
   182 		nqld->sort_list[n++] = ngl_temp;
       
   183 	}
       
   184 
       
   185 	/* Force resort */
       
   186 	nqld->l.flags &= ~VL_REBUILD;
       
   187 	nqld->l.flags |= VL_RESORT;
       
   188 }
       
   189 
       
   190 static void SortNetworkGameList(network_ql_d *nqld)
       
   191 {
       
   192 	static NGameNameSortFunction * const ngame_sorter[] = {
       
   193 		&NGameNameSorter,
       
   194 		&NGameClientSorter,
       
   195 		&NGameAllowedSorter
       
   196 	};
       
   197 
       
   198 	NetworkGameList *item;
       
   199 	uint i;
       
   200 
       
   201 	if (!(nqld->l.flags & VL_RESORT)) return;
       
   202 	if (nqld->l.list_length == 0) return;
       
   203 
       
   204 	_internal_sort_order = !!(nqld->l.flags & VL_DESC);
       
   205 	qsort(nqld->sort_list, nqld->l.list_length, sizeof(nqld->sort_list[0]), ngame_sorter[nqld->l.sort_type]);
       
   206 
       
   207 	/* After sorting ngl->sort_list contains the sorted items. Put these back
       
   208 	 * into the original list. Basically nothing has changed, we are only
       
   209 	 * shuffling the ->next pointers */
       
   210 	_network_game_list = nqld->sort_list[0];
       
   211 	for (item = _network_game_list, i = 1; i != nqld->l.list_length; i++) {
       
   212 		item->next = nqld->sort_list[i];
       
   213 		item = item->next;
       
   214 	}
       
   215 	item->next = NULL;
       
   216 
       
   217 	nqld->l.flags &= ~VL_RESORT;
       
   218 }
       
   219 
       
   220 /** Enum for NetworkGameWindow, referring to _network_game_window_widgets */
   158 /** Enum for NetworkGameWindow, referring to _network_game_window_widgets */
   221 enum NetworkGameWindowWidgets {
   159 enum NetworkGameWindowWidgets {
   222 	NGWW_CLOSE,         ///< Close 'X' button
   160 	NGWW_CLOSE,         ///< Close 'X' button
   223 	NGWW_CAPTION,       ///< Caption of the window
   161 	NGWW_CAPTION,       ///< Caption of the window
   224 	NGWW_RESIZE,        ///< Resize button
   162 	NGWW_RESIZE,        ///< Resize button
   246 	NGWW_ADD,           ///< 'Add server' button
   184 	NGWW_ADD,           ///< 'Add server' button
   247 	NGWW_START,         ///< 'Start server' button
   185 	NGWW_START,         ///< 'Start server' button
   248 	NGWW_CANCEL,        ///< 'Cancel' button
   186 	NGWW_CANCEL,        ///< 'Cancel' button
   249 };
   187 };
   250 
   188 
   251 /**
   189 struct NetworkGameWindow : public QueryStringBaseWindow {
   252  * Draw a single server line.
   190 	byte field;                  ///< selected text-field
   253  * @param cur_item  the server to draw.
   191 	NetworkGameList *server;     ///< selected server
   254  * @param y         from where to draw?
   192 	NetworkGameList **sort_list; ///< list of games (sorted)
   255  * @param highlight does the line need to be highlighted?
   193 	list_d ld;                   ///< accompanying list-administration
   256  */
   194 
   257 static void DrawServerLine(const Window *w, const NetworkGameList *cur_item, uint y, bool highlight)
   195 	NetworkGameWindow(const WindowDesc *desc) : QueryStringBaseWindow(desc)
   258 {
   196 	{
   259 	/* show highlighted item with a different colour */
   197 		ttd_strlcpy(this->edit_str_buf, _network_player_name, lengthof(this->edit_str_buf));
   260 	if (highlight) GfxFillRect(w->widget[NGWW_NAME].left + 1, y - 2, w->widget[NGWW_INFO].right - 1, y + 9, 10);
   198 		this->afilter = CS_ALPHANUMERAL;
   261 
   199 		InitializeTextBuffer(&this->text, this->edit_str_buf, lengthof(this->edit_str_buf), 120);
   262 	SetDParamStr(0, cur_item->info.server_name);
   200 
   263 	DrawStringTruncated(w->widget[NGWW_NAME].left + 5, y, STR_02BD, TC_BLACK, w->widget[NGWW_NAME].right - w->widget[NGWW_NAME].left - 5);
   201 		UpdateNetworkGameWindow(true);
   264 
   202 
   265 	SetDParam(0, cur_item->info.clients_on);
   203 		this->vscroll.cap = 11;
   266 	SetDParam(1, cur_item->info.clients_max);
   204 		this->resize.step_height = NET_PRC__SIZE_OF_ROW;
   267 	SetDParam(2, cur_item->info.companies_on);
   205 
   268 	SetDParam(3, cur_item->info.companies_max);
   206 		this->field = NGWW_PLAYER;
   269 	DrawStringCentered(w->widget[NGWW_CLIENTS].left + 39, y, STR_NETWORK_GENERAL_ONLINE, TC_GOLD);
   207 		this->server = NULL;
   270 
   208 
   271 	/* only draw icons if the server is online */
   209 		this->sort_list = NULL;
   272 	if (cur_item->online) {
   210 		this->ld.flags = VL_REBUILD | (_ng_sorting.order ? VL_DESC : VL_NONE);
   273 		/* draw a lock if the server is password protected */
   211 		this->ld.sort_type = _ng_sorting.criteria;
   274 		if (cur_item->info.use_password) DrawSprite(SPR_LOCK, PAL_NONE, w->widget[NGWW_INFO].left + 5, y - 1);
   212 
   275 
   213 		this->FindWindowPlacementAndResize(desc);
   276 		/* draw red or green icon, depending on compatibility with server */
   214 	}
   277 		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);
   215 
   278 
   216 	~NetworkGameWindow()
   279 		/* draw flag according to server language */
   217 	{
   280 		DrawSprite(SPR_FLAGS_BASE + cur_item->info.server_lang, PAL_NONE, w->widget[NGWW_INFO].left + 25, y);
   218 		free(this->sort_list);
   281 	}
   219 	}
   282 }
   220 
   283 
   221 	/**
   284 /**
   222 	 * (Re)build the network game list as its amount has changed because
   285  * Handler of actions done in the NetworkStartServer window
   223 	 * an item has been added or deleted for example
   286  *
   224 	 */
   287  * @param w pointer to the Window structure
   225 	void BuildNetworkGameList()
   288  * @param e pointer to window event
   226 	{
   289  * @note    Uses network_ql_d (network_d, querystr_d and list_d) WP macro
   227 		NetworkGameList *ngl_temp;
   290  * @see     struct _network_game_window_widgets
   228 		uint n = 0;
   291  * @see     enum NetworkGameWindowWidgets
   229 
   292  */
   230 		if (!(this->ld.flags & VL_REBUILD)) return;
   293 
   231 
   294 static void NetworkGameWindowWndProc(Window *w, WindowEvent *e)
   232 		/* Count the number of games in the list */
   295 {
   233 		for (ngl_temp = _network_game_list; ngl_temp != NULL; ngl_temp = ngl_temp->next) n++;
   296 	network_d *nd = &WP(w, network_ql_d).n;
   234 		if (n == 0) return;
   297 	list_d *ld = &WP(w, network_ql_d).l;
   235 
   298 
   236 		/* Create temporary array of games to use for listing */
   299 	switch (e->event) {
   237 		this->sort_list = ReallocT(this->sort_list, n);
   300 		case WE_CREATE: // Focus input box
   238 		this->ld.list_length = n;
   301 			w->vscroll.cap = 11;
   239 
   302 			w->resize.step_height = NET_PRC__SIZE_OF_ROW;
   240 		for (n = 0, ngl_temp = _network_game_list; ngl_temp != NULL; ngl_temp = ngl_temp->next) {
   303 
   241 			this->sort_list[n++] = ngl_temp;
   304 			nd->field = NGWW_PLAYER;
   242 		}
   305 			nd->server = NULL;
   243 
   306 
   244 		/* Force resort */
   307 			WP(w, network_ql_d).sort_list = NULL;
   245 		this->ld.flags &= ~VL_REBUILD;
   308 			ld->flags = VL_REBUILD | (_ng_sorting.order ? VL_DESC : VL_NONE);
   246 		this->ld.flags |= VL_RESORT;
   309 			ld->sort_type = _ng_sorting.criteria;
   247 	}
   310 			break;
   248 
   311 
   249 	void SortNetworkGameList()
   312 		case WE_PAINT: {
   250 	{
   313 			const NetworkGameList *sel = nd->server;
   251 		static NGameNameSortFunction * const ngame_sorter[] = {
   314 			const SortButtonState arrow = (ld->flags & VL_DESC) ? SBS_DOWN : SBS_UP;
   252 			&NGameNameSorter,
   315 
   253 			&NGameClientSorter,
   316 			if (ld->flags & VL_REBUILD) {
   254 			&NGameAllowedSorter
   317 				BuildNetworkGameList(&WP(w, network_ql_d));
   255 		};
   318 				SetVScrollCount(w, ld->list_length);
   256 
       
   257 		NetworkGameList *item;
       
   258 		uint i;
       
   259 
       
   260 		if (!(this->ld.flags & VL_RESORT)) return;
       
   261 		if (this->ld.list_length == 0) return;
       
   262 
       
   263 		_internal_sort_order = !!(this->ld.flags & VL_DESC);
       
   264 		qsort(this->sort_list, this->ld.list_length, sizeof(this->sort_list[0]), ngame_sorter[this->ld.sort_type]);
       
   265 
       
   266 		/* After sorting ngl->sort_list contains the sorted items. Put these back
       
   267 		 * into the original list. Basically nothing has changed, we are only
       
   268 		 * shuffling the ->next pointers */
       
   269 		_network_game_list = this->sort_list[0];
       
   270 		for (item = _network_game_list, i = 1; i != this->ld.list_length; i++) {
       
   271 			item->next = this->sort_list[i];
       
   272 			item = item->next;
       
   273 		}
       
   274 		item->next = NULL;
       
   275 
       
   276 		this->ld.flags &= ~VL_RESORT;
       
   277 	}
       
   278 
       
   279 	/**
       
   280 	 * Draw a single server line.
       
   281 	 * @param cur_item  the server to draw.
       
   282 	 * @param y         from where to draw?
       
   283 	 * @param highlight does the line need to be highlighted?
       
   284 	 */
       
   285 	void DrawServerLine(const NetworkGameList *cur_item, uint y, bool highlight)
       
   286 	{
       
   287 		/* show highlighted item with a different colour */
       
   288 		if (highlight) GfxFillRect(this->widget[NGWW_NAME].left + 1, y - 2, this->widget[NGWW_INFO].right - 1, y + 9, 10);
       
   289 
       
   290 		SetDParamStr(0, cur_item->info.server_name);
       
   291 		DrawStringTruncated(this->widget[NGWW_NAME].left + 5, y, STR_02BD, TC_BLACK, this->widget[NGWW_NAME].right - this->widget[NGWW_NAME].left - 5);
       
   292 
       
   293 		SetDParam(0, cur_item->info.clients_on);
       
   294 		SetDParam(1, cur_item->info.clients_max);
       
   295 		SetDParam(2, cur_item->info.companies_on);
       
   296 		SetDParam(3, cur_item->info.companies_max);
       
   297 		DrawStringCentered(this->widget[NGWW_CLIENTS].left + 39, y, STR_NETWORK_GENERAL_ONLINE, TC_GOLD);
       
   298 
       
   299 		/* only draw icons if the server is online */
       
   300 		if (cur_item->online) {
       
   301 			/* draw a lock if the server is password protected */
       
   302 			if (cur_item->info.use_password) DrawSprite(SPR_LOCK, PAL_NONE, this->widget[NGWW_INFO].left + 5, y - 1);
       
   303 
       
   304 			/* draw red or green icon, depending on compatibility with server */
       
   305 			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);
       
   306 
       
   307 			/* draw flag according to server language */
       
   308 			DrawSprite(SPR_FLAGS_BASE + cur_item->info.server_lang, PAL_NONE, this->widget[NGWW_INFO].left + 25, y);
       
   309 		}
       
   310 	}
       
   311 
       
   312 	virtual void OnPaint()
       
   313 	{
       
   314 		const NetworkGameList *sel = this->server;
       
   315 		const SortButtonState arrow = (this->ld.flags & VL_DESC) ? SBS_DOWN : SBS_UP;
       
   316 
       
   317 		if (this->ld.flags & VL_REBUILD) {
       
   318 			this->BuildNetworkGameList();
       
   319 			SetVScrollCount(this, this->ld.list_length);
       
   320 		}
       
   321 		if (this->ld.flags & VL_RESORT) this->SortNetworkGameList();
       
   322 
       
   323 		/* 'Refresh' button invisible if no server selected */
       
   324 		this->SetWidgetDisabledState(NGWW_REFRESH, sel == NULL);
       
   325 		/* 'Join' button disabling conditions */
       
   326 		this->SetWidgetDisabledState(NGWW_JOIN, sel == NULL || // no Selected Server
       
   327 				!sel->online || // Server offline
       
   328 				sel->info.clients_on >= sel->info.clients_max || // Server full
       
   329 				!sel->info.compatible); // Revision mismatch
       
   330 
       
   331 		/* 'NewGRF Settings' button invisible if no NewGRF is used */
       
   332 		this->SetWidgetHiddenState(NGWW_NEWGRF, sel == NULL ||
       
   333 				!sel->online ||
       
   334 				sel->info.grfconfig == NULL);
       
   335 
       
   336 		SetDParam(0, 0x00);
       
   337 		SetDParam(1, _lan_internet_types_dropdown[_network_lan_internet]);
       
   338 		DrawWindowWidgets(this);
       
   339 
       
   340 		/* Edit box to set player name */
       
   341 		this->DrawEditBox(NGWW_PLAYER);
       
   342 
       
   343 		DrawString(this->widget[NGWW_PLAYER].left - 100, 23, STR_NETWORK_PLAYER_NAME, TC_GOLD);
       
   344 
       
   345 		/* Sort based on widgets: name, clients, compatibility */
       
   346 		switch (this->ld.sort_type) {
       
   347 			case NGWW_NAME    - NGWW_NAME: DrawSortButtonState(this, NGWW_NAME,    arrow); break;
       
   348 			case NGWW_CLIENTS - NGWW_NAME: DrawSortButtonState(this, NGWW_CLIENTS, arrow); break;
       
   349 			case NGWW_INFO    - NGWW_NAME: DrawSortButtonState(this, NGWW_INFO,    arrow); break;
       
   350 		}
       
   351 
       
   352 		uint16 y = NET_PRC__OFFSET_TOP_WIDGET + 3;
       
   353 		int32 n = 0;
       
   354 		int32 pos = this->vscroll.pos;
       
   355 		const NetworkGameList *cur_item = _network_game_list;
       
   356 
       
   357 		while (pos > 0 && cur_item != NULL) {
       
   358 			pos--;
       
   359 			cur_item = cur_item->next;
       
   360 		}
       
   361 
       
   362 		while (cur_item != NULL) {
       
   363 			this->DrawServerLine(cur_item, y, cur_item == sel);
       
   364 
       
   365 			cur_item = cur_item->next;
       
   366 			y += NET_PRC__SIZE_OF_ROW;
       
   367 			if (++n == this->vscroll.cap) break; // max number of games in the window
       
   368 		}
       
   369 
       
   370 		const NetworkGameList *last_joined = NetworkGameListAddItem(inet_addr(_network_last_host), _network_last_port);
       
   371 		/* Draw the last joined server, if any */
       
   372 		if (last_joined != NULL) this->DrawServerLine(last_joined, y = this->widget[NGWW_LASTJOINED].top + 3, last_joined == sel);
       
   373 
       
   374 		/* Draw the right menu */
       
   375 		GfxFillRect(this->widget[NGWW_DETAILS].left + 1, 43, this->widget[NGWW_DETAILS].right - 1, 92, 157);
       
   376 		if (sel == NULL) {
       
   377 			DrawStringCentered(this->widget[NGWW_DETAILS].left + 115, 58, STR_NETWORK_GAME_INFO, TC_FROMSTRING);
       
   378 		} else if (!sel->online) {
       
   379 			SetDParamStr(0, sel->info.server_name);
       
   380 			DrawStringCentered(this->widget[NGWW_DETAILS].left + 115, 68, STR_ORANGE, TC_FROMSTRING); // game name
       
   381 
       
   382 			DrawStringCentered(this->widget[NGWW_DETAILS].left + 115, 132, STR_NETWORK_SERVER_OFFLINE, TC_FROMSTRING); // server offline
       
   383 		} else { // show game info
       
   384 			uint16 y = 100;
       
   385 			const uint16 x = this->widget[NGWW_DETAILS].left + 5;
       
   386 
       
   387 			DrawStringCentered(this->widget[NGWW_DETAILS].left + 115, 48, STR_NETWORK_GAME_INFO, TC_FROMSTRING);
       
   388 
       
   389 
       
   390 			SetDParamStr(0, sel->info.server_name);
       
   391 			DrawStringCenteredTruncated(this->widget[NGWW_DETAILS].left, this->widget[NGWW_DETAILS].right, 62, STR_ORANGE, TC_BLACK); // game name
       
   392 
       
   393 			SetDParamStr(0, sel->info.map_name);
       
   394 			DrawStringCenteredTruncated(this->widget[NGWW_DETAILS].left, this->widget[NGWW_DETAILS].right, 74, STR_02BD, TC_BLACK); // map name
       
   395 
       
   396 			SetDParam(0, sel->info.clients_on);
       
   397 			SetDParam(1, sel->info.clients_max);
       
   398 			SetDParam(2, sel->info.companies_on);
       
   399 			SetDParam(3, sel->info.companies_max);
       
   400 			DrawString(x, y, STR_NETWORK_CLIENTS, TC_GOLD);
       
   401 			y += 10;
       
   402 
       
   403 			SetDParam(0, STR_NETWORK_LANG_ANY + sel->info.server_lang);
       
   404 			DrawString(x, y, STR_NETWORK_LANGUAGE, TC_GOLD); // server language
       
   405 			y += 10;
       
   406 
       
   407 			SetDParam(0, STR_TEMPERATE_LANDSCAPE + sel->info.map_set);
       
   408 			DrawString(x, y, STR_NETWORK_TILESET, TC_GOLD); // tileset
       
   409 			y += 10;
       
   410 
       
   411 			SetDParam(0, sel->info.map_width);
       
   412 			SetDParam(1, sel->info.map_height);
       
   413 			DrawString(x, y, STR_NETWORK_MAP_SIZE, TC_GOLD); // map size
       
   414 			y += 10;
       
   415 
       
   416 			SetDParamStr(0, sel->info.server_revision);
       
   417 			DrawString(x, y, STR_NETWORK_SERVER_VERSION, TC_GOLD); // server version
       
   418 			y += 10;
       
   419 
       
   420 			SetDParamStr(0, sel->info.hostname);
       
   421 			SetDParam(1, sel->port);
       
   422 			DrawString(x, y, STR_NETWORK_SERVER_ADDRESS, TC_GOLD); // server address
       
   423 			y += 10;
       
   424 
       
   425 			SetDParam(0, sel->info.start_date);
       
   426 			DrawString(x, y, STR_NETWORK_START_DATE, TC_GOLD); // start date
       
   427 			y += 10;
       
   428 
       
   429 			SetDParam(0, sel->info.game_date);
       
   430 			DrawString(x, y, STR_NETWORK_CURRENT_DATE, TC_GOLD); // current date
       
   431 			y += 10;
       
   432 
       
   433 			y += 2;
       
   434 
       
   435 			if (!sel->info.compatible) {
       
   436 				DrawStringCentered(this->widget[NGWW_DETAILS].left + 115, y, sel->info.version_compatible ? STR_NETWORK_GRF_MISMATCH : STR_NETWORK_VERSION_MISMATCH, TC_FROMSTRING); // server mismatch
       
   437 			} else if (sel->info.clients_on == sel->info.clients_max) {
       
   438 				/* Show: server full, when clients_on == clients_max */
       
   439 				DrawStringCentered(this->widget[NGWW_DETAILS].left + 115, y, STR_NETWORK_SERVER_FULL, TC_FROMSTRING); // server full
       
   440 			} else if (sel->info.use_password) {
       
   441 				DrawStringCentered(this->widget[NGWW_DETAILS].left + 115, y, STR_NETWORK_PASSWORD, TC_FROMSTRING); // password warning
   319 			}
   442 			}
   320 			if (ld->flags & VL_RESORT) SortNetworkGameList(&WP(w, network_ql_d));
   443 
   321 
   444 			y += 10;
   322 			/* 'Refresh' button invisible if no server selected */
   445 		}
   323 			w->SetWidgetDisabledState(NGWW_REFRESH, sel == NULL);
   446 	}
   324 			/* 'Join' button disabling conditions */
   447 
   325 			w->SetWidgetDisabledState(NGWW_JOIN, sel == NULL || // no Selected Server
   448 	virtual void OnClick(Point pt, int widget)
   326 					!sel->online || // Server offline
   449 	{
   327 					sel->info.clients_on >= sel->info.clients_max || // Server full
   450 		this->field = widget;
   328 					!sel->info.compatible); // Revision mismatch
   451 		switch (widget) {
   329 
   452 			case NGWW_PLAYER:
   330 			/* 'NewGRF Settings' button invisible if no NewGRF is used */
   453 				ShowOnScreenKeyboard(this, NGWW_PLAYER, 0, 0);
   331 			w->SetWidgetHiddenState(NGWW_NEWGRF, sel == NULL ||
   454 				break;
   332 					!sel->online ||
   455 
   333 					sel->info.grfconfig == NULL);
   456 			case NGWW_CANCEL: // Cancel button
   334 
   457 				DeleteWindowById(WC_NETWORK_WINDOW, 0);
   335 			SetDParam(0, 0x00);
   458 				break;
   336 			SetDParam(1, _lan_internet_types_dropdown[_network_lan_internet]);
   459 
   337 			DrawWindowWidgets(w);
   460 			case NGWW_CONN_BTN: // 'Connection' droplist
   338 
   461 				ShowDropDownMenu(this, _lan_internet_types_dropdown, _network_lan_internet, NGWW_CONN_BTN, 0, 0); // do it for widget NSSW_CONN_BTN
   339 			/* Edit box to set player name */
   462 				break;
   340 			DrawEditBox(w, &WP(w, network_ql_d).q, NGWW_PLAYER);
   463 
   341 
   464 			case NGWW_NAME: // Sort by name
   342 			DrawString(w->widget[NGWW_PLAYER].left - 100, 23, STR_NETWORK_PLAYER_NAME, TC_GOLD);
   465 			case NGWW_CLIENTS: // Sort by connected clients
   343 
   466 			case NGWW_INFO: // Connectivity (green dot)
   344 			/* Sort based on widgets: name, clients, compatibility */
   467 				if (this->ld.sort_type == widget - NGWW_NAME) this->ld.flags ^= VL_DESC;
   345 			switch (ld->sort_type) {
   468 				this->ld.flags |= VL_RESORT;
   346 				case NGWW_NAME    - NGWW_NAME: DrawSortButtonState(w, NGWW_NAME,    arrow); break;
   469 				this->ld.sort_type = widget - NGWW_NAME;
   347 				case NGWW_CLIENTS - NGWW_NAME: DrawSortButtonState(w, NGWW_CLIENTS, arrow); break;
   470 
   348 				case NGWW_INFO    - NGWW_NAME: DrawSortButtonState(w, NGWW_INFO,    arrow); break;
   471 				_ng_sorting.order = !!(this->ld.flags & VL_DESC);
       
   472 				_ng_sorting.criteria = this->ld.sort_type;
       
   473 				this->SetDirty();
       
   474 				break;
       
   475 
       
   476 			case NGWW_MATRIX: { // Matrix to show networkgames
       
   477 				NetworkGameList *cur_item;
       
   478 				uint32 id_v = (pt.y - NET_PRC__OFFSET_TOP_WIDGET) / NET_PRC__SIZE_OF_ROW;
       
   479 
       
   480 				if (id_v >= this->vscroll.cap) return; // click out of bounds
       
   481 				id_v += this->vscroll.pos;
       
   482 
       
   483 				cur_item = _network_game_list;
       
   484 				for (; id_v > 0 && cur_item != NULL; id_v--) cur_item = cur_item->next;
       
   485 
       
   486 				this->server = cur_item;
       
   487 				this->SetDirty();
       
   488 			} break;
       
   489 
       
   490 			case NGWW_LASTJOINED: {
       
   491 				NetworkGameList *last_joined = NetworkGameListAddItem(inet_addr(_network_last_host), _network_last_port);
       
   492 				if (last_joined != NULL) {
       
   493 					this->server = last_joined;
       
   494 					this->SetDirty();
       
   495 				}
       
   496 			} break;
       
   497 
       
   498 			case NGWW_FIND: // Find server automatically
       
   499 				switch (_network_lan_internet) {
       
   500 					case 0: NetworkUDPSearchGame(); break;
       
   501 					case 1: NetworkUDPQueryMasterServer(); break;
       
   502 				}
       
   503 				break;
       
   504 
       
   505 			case NGWW_ADD: // Add a server
       
   506 				ShowQueryString(
       
   507 					BindCString(_network_default_ip),
       
   508 					STR_NETWORK_ENTER_IP,
       
   509 					31 | 0x1000,  // maximum number of characters OR
       
   510 					250, // characters up to this width pixels, whichever is satisfied first
       
   511 					this, CS_ALPHANUMERAL);
       
   512 				break;
       
   513 
       
   514 			case NGWW_START: // Start server
       
   515 				ShowNetworkStartServerWindow();
       
   516 				break;
       
   517 
       
   518 			case NGWW_JOIN: // Join Game
       
   519 				if (this->server != NULL) {
       
   520 					snprintf(_network_last_host, sizeof(_network_last_host), "%s", inet_ntoa(*(struct in_addr *)&this->server->ip));
       
   521 					_network_last_port = this->server->port;
       
   522 					ShowNetworkLobbyWindow(this->server);
       
   523 				}
       
   524 				break;
       
   525 
       
   526 			case NGWW_REFRESH: // Refresh
       
   527 				if (this->server != NULL) NetworkUDPQueryServer(this->server->info.hostname, this->server->port);
       
   528 				break;
       
   529 
       
   530 			case NGWW_NEWGRF: // NewGRF Settings
       
   531 				if (this->server != NULL) ShowNewGRFSettings(false, false, false, &this->server->info.grfconfig);
       
   532 				break;
       
   533 		}
       
   534 	}
       
   535 
       
   536 	virtual void OnDropdownSelect(int widget, int index)
       
   537 	{
       
   538 		switch (widget) {
       
   539 			case NGWW_CONN_BTN:
       
   540 				_network_lan_internet = index;
       
   541 				break;
       
   542 
       
   543 			default:
       
   544 				NOT_REACHED();
       
   545 		}
       
   546 
       
   547 		this->SetDirty();
       
   548 	}
       
   549 
       
   550 	virtual void OnMouseLoop()
       
   551 	{
       
   552 		if (this->field == NGWW_PLAYER) this->HandleEditBox(NGWW_PLAYER);
       
   553 	}
       
   554 
       
   555 	virtual void OnInvalidateData(int data)
       
   556 	{
       
   557 		if (data != 0) this->server = NULL;
       
   558 		this->ld.flags |= VL_REBUILD;
       
   559 		this->SetDirty();
       
   560 	}
       
   561 
       
   562 	virtual bool OnKeyPress(uint16 key, uint16 keycode)
       
   563 	{
       
   564 		bool cont = true;
       
   565 		if (this->field != NGWW_PLAYER) {
       
   566 			if (this->server != NULL) {
       
   567 				if (keycode == WKC_DELETE) { // Press 'delete' to remove servers
       
   568 					NetworkGameListRemoveItem(this->server);
       
   569 					NetworkRebuildHostList();
       
   570 					this->server = NULL;
       
   571 				}
   349 			}
   572 			}
   350 
   573 			return cont;
   351 			uint16 y = NET_PRC__OFFSET_TOP_WIDGET + 3;
   574 		}
   352 			int32 n = 0;
   575 
   353 			int32 pos = w->vscroll.pos;
   576 		if (this->HandleEditBoxKey(NGWW_PLAYER, keycode, key, cont) == 1) return cont; // enter pressed
   354 			const NetworkGameList *cur_item = _network_game_list;
   577 
   355 
   578 		/* The name is only allowed when it starts with a letter! */
   356 			while (pos > 0 && cur_item != NULL) {
   579 		if (StrEmpty(this->edit_str_buf) && this->edit_str_buf[0] != ' ') {
   357 				pos--;
   580 			ttd_strlcpy(_network_player_name, this->edit_str_buf, lengthof(_network_player_name));
   358 				cur_item = cur_item->next;
   581 		} else {
   359 			}
   582 			ttd_strlcpy(_network_player_name, "Player", lengthof(_network_player_name));
   360 
   583 		}
   361 			while (cur_item != NULL) {
   584 		return cont;
   362 				DrawServerLine(w, cur_item, y, cur_item == sel);
   585 	}
   363 
   586 
   364 				cur_item = cur_item->next;
   587 	virtual void OnQueryTextFinished(char *str)
   365 				y += NET_PRC__SIZE_OF_ROW;
   588 	{
   366 				if (++n == w->vscroll.cap) break; // max number of games in the window
   589 		if (!StrEmpty(str)) {
   367 			}
   590 			NetworkAddServer(str);
   368 
   591 			NetworkRebuildHostList();
   369 			const NetworkGameList *last_joined = NetworkGameListAddItem(inet_addr(_network_last_host), _network_last_port);
   592 		}
   370 			/* Draw the last joined server, if any */
   593 	}
   371 			if (last_joined != NULL) DrawServerLine(w, last_joined, y = w->widget[NGWW_LASTJOINED].top + 3, last_joined == sel);
   594 
   372 
   595 	virtual void OnResize(Point new_size, Point delta)
   373 			/* Draw the right menu */
   596 	{
   374 			GfxFillRect(w->widget[NGWW_DETAILS].left + 1, 43, w->widget[NGWW_DETAILS].right - 1, 92, 157);
   597 		this->vscroll.cap += delta.y / (int)this->resize.step_height;
   375 			if (sel == NULL) {
   598 
   376 				DrawStringCentered(w->widget[NGWW_DETAILS].left + 115, 58, STR_NETWORK_GAME_INFO, TC_FROMSTRING);
   599 		this->widget[NGWW_MATRIX].data = (this->vscroll.cap << 8) + 1;
   377 			} else if (!sel->online) {
   600 
   378 				SetDParamStr(0, sel->info.server_name);
   601 		SetVScrollCount(this, this->ld.list_length);
   379 				DrawStringCentered(w->widget[NGWW_DETAILS].left + 115, 68, STR_ORANGE, TC_FROMSTRING); // game name
   602 
   380 
   603 		int widget_width = this->widget[NGWW_FIND].right - this->widget[NGWW_FIND].left;
   381 				DrawStringCentered(w->widget[NGWW_DETAILS].left + 115, 132, STR_NETWORK_SERVER_OFFLINE, TC_FROMSTRING); // server offline
   604 		int space = (this->width - 4 * widget_width - 25) / 3;
   382 			} else { // show game info
   605 
   383 				uint16 y = 100;
   606 		int offset = 10;
   384 				const uint16 x = w->widget[NGWW_DETAILS].left + 5;
   607 		for (uint i = 0; i < 4; i++) {
   385 
   608 			this->widget[NGWW_FIND + i].left  = offset;
   386 				DrawStringCentered(w->widget[NGWW_DETAILS].left + 115, 48, STR_NETWORK_GAME_INFO, TC_FROMSTRING);
   609 			offset += widget_width;
   387 
   610 			this->widget[NGWW_FIND + i].right = offset;
   388 
   611 			offset += space;
   389 				SetDParamStr(0, sel->info.server_name);
   612 		}
   390 				DrawStringCenteredTruncated(w->widget[NGWW_DETAILS].left, w->widget[NGWW_DETAILS].right, 62, STR_ORANGE, TC_BLACK); // game name
   613 	}
   391 
   614 };
   392 				SetDParamStr(0, sel->info.map_name);
       
   393 				DrawStringCenteredTruncated(w->widget[NGWW_DETAILS].left, w->widget[NGWW_DETAILS].right, 74, STR_02BD, TC_BLACK); // map name
       
   394 
       
   395 				SetDParam(0, sel->info.clients_on);
       
   396 				SetDParam(1, sel->info.clients_max);
       
   397 				SetDParam(2, sel->info.companies_on);
       
   398 				SetDParam(3, sel->info.companies_max);
       
   399 				DrawString(x, y, STR_NETWORK_CLIENTS, TC_GOLD);
       
   400 				y += 10;
       
   401 
       
   402 				SetDParam(0, STR_NETWORK_LANG_ANY + sel->info.server_lang);
       
   403 				DrawString(x, y, STR_NETWORK_LANGUAGE, TC_GOLD); // server language
       
   404 				y += 10;
       
   405 
       
   406 				SetDParam(0, STR_TEMPERATE_LANDSCAPE + sel->info.map_set);
       
   407 				DrawString(x, y, STR_NETWORK_TILESET, TC_GOLD); // tileset
       
   408 				y += 10;
       
   409 
       
   410 				SetDParam(0, sel->info.map_width);
       
   411 				SetDParam(1, sel->info.map_height);
       
   412 				DrawString(x, y, STR_NETWORK_MAP_SIZE, TC_GOLD); // map size
       
   413 				y += 10;
       
   414 
       
   415 				SetDParamStr(0, sel->info.server_revision);
       
   416 				DrawString(x, y, STR_NETWORK_SERVER_VERSION, TC_GOLD); // server version
       
   417 				y += 10;
       
   418 
       
   419 				SetDParamStr(0, sel->info.hostname);
       
   420 				SetDParam(1, sel->port);
       
   421 				DrawString(x, y, STR_NETWORK_SERVER_ADDRESS, TC_GOLD); // server address
       
   422 				y += 10;
       
   423 
       
   424 				SetDParam(0, sel->info.start_date);
       
   425 				DrawString(x, y, STR_NETWORK_START_DATE, TC_GOLD); // start date
       
   426 				y += 10;
       
   427 
       
   428 				SetDParam(0, sel->info.game_date);
       
   429 				DrawString(x, y, STR_NETWORK_CURRENT_DATE, TC_GOLD); // current date
       
   430 				y += 10;
       
   431 
       
   432 				y += 2;
       
   433 
       
   434 				if (!sel->info.compatible) {
       
   435 					DrawStringCentered(w->widget[NGWW_DETAILS].left + 115, y, sel->info.version_compatible ? STR_NETWORK_GRF_MISMATCH : STR_NETWORK_VERSION_MISMATCH, TC_FROMSTRING); // server mismatch
       
   436 				} else if (sel->info.clients_on == sel->info.clients_max) {
       
   437 					/* Show: server full, when clients_on == clients_max */
       
   438 					DrawStringCentered(w->widget[NGWW_DETAILS].left + 115, y, STR_NETWORK_SERVER_FULL, TC_FROMSTRING); // server full
       
   439 				} else if (sel->info.use_password) {
       
   440 					DrawStringCentered(w->widget[NGWW_DETAILS].left + 115, y, STR_NETWORK_PASSWORD, TC_FROMSTRING); // password warning
       
   441 				}
       
   442 
       
   443 				y += 10;
       
   444 			}
       
   445 		} break;
       
   446 
       
   447 		case WE_CLICK:
       
   448 			nd->field = e->we.click.widget;
       
   449 			switch (e->we.click.widget) {
       
   450 				case NGWW_PLAYER:
       
   451 					ShowOnScreenKeyboard(w, &WP(w, network_ql_d).q,  NGWW_PLAYER, 0, 0);
       
   452 					break;
       
   453 
       
   454 				case NGWW_CANCEL: // Cancel button
       
   455 					DeleteWindowById(WC_NETWORK_WINDOW, 0);
       
   456 					break;
       
   457 
       
   458 				case NGWW_CONN_BTN: // 'Connection' droplist
       
   459 					ShowDropDownMenu(w, _lan_internet_types_dropdown, _network_lan_internet, NGWW_CONN_BTN, 0, 0); // do it for widget NSSW_CONN_BTN
       
   460 					break;
       
   461 
       
   462 				case NGWW_NAME: // Sort by name
       
   463 				case NGWW_CLIENTS: // Sort by connected clients
       
   464 				case NGWW_INFO: // Connectivity (green dot)
       
   465 					if (ld->sort_type == e->we.click.widget - NGWW_NAME) ld->flags ^= VL_DESC;
       
   466 					ld->flags |= VL_RESORT;
       
   467 					ld->sort_type = e->we.click.widget - NGWW_NAME;
       
   468 
       
   469 					_ng_sorting.order = !!(ld->flags & VL_DESC);
       
   470 					_ng_sorting.criteria = ld->sort_type;
       
   471 					SetWindowDirty(w);
       
   472 					break;
       
   473 
       
   474 				case NGWW_MATRIX: { // Matrix to show networkgames
       
   475 					NetworkGameList *cur_item;
       
   476 					uint32 id_v = (e->we.click.pt.y - NET_PRC__OFFSET_TOP_WIDGET) / NET_PRC__SIZE_OF_ROW;
       
   477 
       
   478 					if (id_v >= w->vscroll.cap) return; // click out of bounds
       
   479 					id_v += w->vscroll.pos;
       
   480 
       
   481 					cur_item = _network_game_list;
       
   482 					for (; id_v > 0 && cur_item != NULL; id_v--) cur_item = cur_item->next;
       
   483 
       
   484 					nd->server = cur_item;
       
   485 					SetWindowDirty(w);
       
   486 				} break;
       
   487 
       
   488 				case NGWW_LASTJOINED: {
       
   489 					NetworkGameList *last_joined = NetworkGameListAddItem(inet_addr(_network_last_host), _network_last_port);
       
   490 					if (last_joined != NULL) {
       
   491 						nd->server = last_joined;
       
   492 						SetWindowDirty(w);
       
   493 					}
       
   494 				} break;
       
   495 
       
   496 				case NGWW_FIND: // Find server automatically
       
   497 					switch (_network_lan_internet) {
       
   498 						case 0: NetworkUDPSearchGame(); break;
       
   499 						case 1: NetworkUDPQueryMasterServer(); break;
       
   500 					}
       
   501 					break;
       
   502 
       
   503 				case NGWW_ADD: // Add a server
       
   504 					ShowQueryString(
       
   505 						BindCString(_network_default_ip),
       
   506 						STR_NETWORK_ENTER_IP,
       
   507 						31 | 0x1000,  // maximum number of characters OR
       
   508 						250, // characters up to this width pixels, whichever is satisfied first
       
   509 						w, CS_ALPHANUMERAL);
       
   510 					break;
       
   511 
       
   512 				case NGWW_START: // Start server
       
   513 					ShowNetworkStartServerWindow();
       
   514 					break;
       
   515 
       
   516 				case NGWW_JOIN: // Join Game
       
   517 					if (nd->server != NULL) {
       
   518 						snprintf(_network_last_host, sizeof(_network_last_host), "%s", inet_ntoa(*(struct in_addr *)&nd->server->ip));
       
   519 						_network_last_port = nd->server->port;
       
   520 						ShowNetworkLobbyWindow(nd->server);
       
   521 					}
       
   522 					break;
       
   523 
       
   524 				case NGWW_REFRESH: // Refresh
       
   525 					if (nd->server != NULL) NetworkUDPQueryServer(nd->server->info.hostname, nd->server->port);
       
   526 					break;
       
   527 
       
   528 				case NGWW_NEWGRF: // NewGRF Settings
       
   529 					if (nd->server != NULL) ShowNewGRFSettings(false, false, false, &nd->server->info.grfconfig);
       
   530 					break;
       
   531 			}
       
   532 			break;
       
   533 
       
   534 		case WE_DROPDOWN_SELECT: // we have selected a dropdown item in the list
       
   535 			switch (e->we.dropdown.button) {
       
   536 				case NGWW_CONN_BTN:
       
   537 					_network_lan_internet = e->we.dropdown.index;
       
   538 					break;
       
   539 
       
   540 				default:
       
   541 					NOT_REACHED();
       
   542 			}
       
   543 
       
   544 			SetWindowDirty(w);
       
   545 			break;
       
   546 
       
   547 		case WE_MOUSELOOP:
       
   548 			if (nd->field == NGWW_PLAYER) HandleEditBox(w, &WP(w, network_ql_d).q, NGWW_PLAYER);
       
   549 			break;
       
   550 
       
   551 		case WE_INVALIDATE_DATA:
       
   552 			if (e->we.invalidate.data != 0) nd->server = NULL;
       
   553 			ld->flags |= VL_REBUILD;
       
   554 			SetWindowDirty(w);
       
   555 			break;
       
   556 
       
   557 		case WE_KEYPRESS:
       
   558 			if (nd->field != NGWW_PLAYER) {
       
   559 				if (nd->server != NULL) {
       
   560 					if (e->we.keypress.keycode == WKC_DELETE) { // Press 'delete' to remove servers
       
   561 						NetworkGameListRemoveItem(nd->server);
       
   562 						NetworkRebuildHostList();
       
   563 						nd->server = NULL;
       
   564 					}
       
   565 				}
       
   566 				break;
       
   567 			}
       
   568 
       
   569 			if (HandleEditBoxKey(w, &WP(w, network_ql_d).q, NGWW_PLAYER, e) == 1) break; // enter pressed
       
   570 
       
   571 			/* The name is only allowed when it starts with a letter! */
       
   572 			if (_edit_str_net_buf[0] != '\0' && _edit_str_net_buf[0] != ' ') {
       
   573 				ttd_strlcpy(_network_player_name, _edit_str_net_buf, lengthof(_network_player_name));
       
   574 			} else {
       
   575 				ttd_strlcpy(_network_player_name, "Player", lengthof(_network_player_name));
       
   576 			}
       
   577 
       
   578 			break;
       
   579 
       
   580 		case WE_ON_EDIT_TEXT:
       
   581 			if (!StrEmpty(e->we.edittext.str)) {
       
   582 				NetworkAddServer(e->we.edittext.str);
       
   583 				NetworkRebuildHostList();
       
   584 			}
       
   585 			break;
       
   586 
       
   587 		case WE_RESIZE: {
       
   588 			w->vscroll.cap += e->we.sizing.diff.y / (int)w->resize.step_height;
       
   589 
       
   590 			w->widget[NGWW_MATRIX].data = (w->vscroll.cap << 8) + 1;
       
   591 
       
   592 			SetVScrollCount(w, ld->list_length);
       
   593 
       
   594 			int widget_width = w->widget[NGWW_FIND].right - w->widget[NGWW_FIND].left;
       
   595 			int space = (w->width - 4 * widget_width - 25) / 3;
       
   596 
       
   597 			int offset = 10;
       
   598 			for (uint i = 0; i < 4; i++) {
       
   599 				w->widget[NGWW_FIND + i].left  = offset;
       
   600 				offset += widget_width;
       
   601 				w->widget[NGWW_FIND + i].right = offset;
       
   602 				offset += space;
       
   603 			}
       
   604 		} break;
       
   605 
       
   606 		case WE_DESTROY: // Nicely clean up the sort-list
       
   607 			free(WP(w, network_ql_d).sort_list);
       
   608 			break;
       
   609 	}
       
   610 }
       
   611 
   615 
   612 static const Widget _network_game_window_widgets[] = {
   616 static const Widget _network_game_window_widgets[] = {
   613 /* TOP */
   617 /* TOP */
   614 {   WWT_CLOSEBOX,   RESIZE_NONE,   BGC,     0,    10,     0,    13, STR_00C5,                         STR_018B_CLOSE_WINDOW},            // NGWW_CLOSE
   618 {   WWT_CLOSEBOX,   RESIZE_NONE,   BGC,     0,    10,     0,    13, STR_00C5,                         STR_018B_CLOSE_WINDOW},            // NGWW_CLOSE
   615 {    WWT_CAPTION,   RESIZE_RIGHT,  BGC,    11,   449,     0,    13, STR_NETWORK_MULTIPLAYER,          STR_NULL},                         // NGWW_CAPTION
   619 {    WWT_CAPTION,   RESIZE_RIGHT,  BGC,    11,   449,     0,    13, STR_NETWORK_MULTIPLAYER,          STR_NULL},                         // NGWW_CAPTION
   652 static const WindowDesc _network_game_window_desc = {
   656 static const WindowDesc _network_game_window_desc = {
   653 	WDP_CENTER, WDP_CENTER, 450, 264, 550, 264,
   657 	WDP_CENTER, WDP_CENTER, 450, 264, 550, 264,
   654 	WC_NETWORK_WINDOW, WC_NONE,
   658 	WC_NETWORK_WINDOW, WC_NONE,
   655 	WDF_STD_TOOLTIPS | WDF_DEF_WIDGET | WDF_STD_BTN | WDF_UNCLICK_BUTTONS | WDF_RESIZABLE,
   659 	WDF_STD_TOOLTIPS | WDF_DEF_WIDGET | WDF_STD_BTN | WDF_UNCLICK_BUTTONS | WDF_RESIZABLE,
   656 	_network_game_window_widgets,
   660 	_network_game_window_widgets,
   657 	NetworkGameWindowWndProc,
   661 	NULL,
   658 };
   662 };
   659 
   663 
   660 void ShowNetworkGameWindow()
   664 void ShowNetworkGameWindow()
   661 {
   665 {
   662 	static bool first = true;
   666 	static bool first = true;
   674 
   678 
   675 		_ng_sorting.criteria = 2; // sort default by collectivity (green-dots on top)
   679 		_ng_sorting.criteria = 2; // sort default by collectivity (green-dots on top)
   676 		_ng_sorting.order = 0;    // sort ascending by default
   680 		_ng_sorting.order = 0;    // sort ascending by default
   677 	}
   681 	}
   678 
   682 
   679 	Window *w = new Window(&_network_game_window_desc);
   683 	new NetworkGameWindow(&_network_game_window_desc);
   680 	if (w != NULL) {
       
   681 		querystr_d *querystr = &WP(w, network_ql_d).q;
       
   682 
       
   683 		ttd_strlcpy(_edit_str_net_buf, _network_player_name, lengthof(_edit_str_net_buf));
       
   684 		querystr->afilter = CS_ALPHANUMERAL;
       
   685 		InitializeTextBuffer(&querystr->text, _edit_str_net_buf, lengthof(_edit_str_net_buf), 120);
       
   686 
       
   687 		UpdateNetworkGameWindow(true);
       
   688 	}
       
   689 }
   684 }
   690 
   685 
   691 enum {
   686 enum {
   692 	NSSWND_START = 64,
   687 	NSSWND_START = 64,
   693 	NSSWND_ROWSIZE = 12
   688 	NSSWND_ROWSIZE = 12
   713 	NSSW_START           = 25,   ///< 'Start' button
   708 	NSSW_START           = 25,   ///< 'Start' button
   714 	NSSW_LOAD            = 26,   ///< 'Load' button
   709 	NSSW_LOAD            = 26,   ///< 'Load' button
   715 	NSSW_CANCEL          = 27,   ///< 'Cancel' button
   710 	NSSW_CANCEL          = 27,   ///< 'Cancel' button
   716 };
   711 };
   717 
   712 
   718 /**
   713 struct NetworkStartServerWindow : public QueryStringBaseWindow {
   719  * Handler of actions done in the NetworkStartServer window
   714 	byte field;                  ///< Selected text-field
   720  *
   715 	FiosItem *map;               ///< Selected map
   721  * @param w pointer to the Window structure
   716 	byte widget_id;              ///< The widget that has the pop-up input menu
   722  * @param e pointer to window event
   717 
   723  * @note    Uses network_ql_d (network_d, querystr_d and list_d) WP macro
   718 	NetworkStartServerWindow(const WindowDesc *desc) : QueryStringBaseWindow(desc)
   724  * @see     struct _network_start_server_window_widgets
   719 	{
   725  * @see     enum NetworkStartServerWidgets
   720 		ttd_strlcpy(this->edit_str_buf, _network_server_name, lengthof(this->edit_str_buf));
   726  */
   721 
   727 static void NetworkStartServerWindowWndProc(Window *w, WindowEvent *e)
   722 		_saveload_mode = SLD_NEW_GAME;
   728 {
   723 		BuildFileList();
   729 	network_d *nd = &WP(w, network_ql_d).n;
   724 		this->vscroll.cap = 12;
   730 
   725 		this->vscroll.count = _fios_num + 1;
   731 	switch (e->event) {
   726 
   732 		case WE_CREATE: // focus input box
   727 		this->afilter = CS_ALPHANUMERAL;
   733 			nd->field = NSSW_GAMENAME;
   728 		InitializeTextBuffer(&this->text, this->edit_str_buf, lengthof(this->edit_str_buf), 160);
   734 			_network_game_info.use_password = (_network_server_password[0] != '\0');
   729 
   735 			break;
   730 		this->field = NSSW_GAMENAME;
   736 
   731 		_network_game_info.use_password = !StrEmpty(_network_server_password);
   737 		case WE_PAINT: {
   732 
   738 			int y = NSSWND_START, pos;
   733 		this->FindWindowPlacementAndResize(desc);
   739 			const FiosItem *item;
   734 	}
   740 
   735 
   741 			/* draw basic widgets */
   736 	virtual void OnPaint()
   742 			SetDParam(1, _connection_types_dropdown[_network_advertise]);
   737 	{
   743 			SetDParam(2, _network_game_info.clients_max);
   738 		int y = NSSWND_START, pos;
   744 			SetDParam(3, _network_game_info.companies_max);
   739 		const FiosItem *item;
   745 			SetDParam(4, _network_game_info.spectators_max);
   740 
   746 			SetDParam(5, STR_NETWORK_LANG_ANY + _network_game_info.server_lang);
   741 		/* draw basic widgets */
   747 			DrawWindowWidgets(w);
   742 		SetDParam(1, _connection_types_dropdown[_network_advertise]);
   748 
   743 		SetDParam(2, _network_game_info.clients_max);
   749 			/* editbox to set game name */
   744 		SetDParam(3, _network_game_info.companies_max);
   750 			DrawEditBox(w, &WP(w, network_ql_d).q, NSSW_GAMENAME);
   745 		SetDParam(4, _network_game_info.spectators_max);
   751 
   746 		SetDParam(5, STR_NETWORK_LANG_ANY + _network_game_info.server_lang);
   752 			/* if password is set, draw red '*' next to 'Set password' button */
   747 		DrawWindowWidgets(this);
   753 			if (_network_game_info.use_password) DoDrawString("*", 408, 23, TC_RED);
   748 
   754 
   749 		/* editbox to set game name */
   755 			/* draw list of maps */
   750 		this->DrawEditBox(NSSW_GAMENAME);
   756 			GfxFillRect(11, 63, 258, 215, 0xD7);  // black background of maps list
   751 
   757 
   752 		/* if password is set, draw red '*' next to 'Set password' button */
   758 			pos = w->vscroll.pos;
   753 		if (_network_game_info.use_password) DoDrawString("*", 408, 23, TC_RED);
   759 			while (pos < _fios_num + 1) {
   754 
   760 				item = _fios_list + pos - 1;
   755 		/* draw list of maps */
   761 				if (item == nd->map || (pos == 0 && nd->map == NULL))
   756 		GfxFillRect(11, 63, 258, 215, 0xD7);  // black background of maps list
   762 					GfxFillRect(11, y - 1, 258, y + 10, 155); // show highlighted item with a different colour
   757 
   763 
   758 		pos = this->vscroll.pos;
   764 				if (pos == 0) {
   759 		while (pos < _fios_num + 1) {
   765 					DrawString(14, y, STR_4010_GENERATE_RANDOM_NEW_GAME, TC_DARK_GREEN);
   760 			item = _fios_list + pos - 1;
   766 				} else {
   761 			if (item == this->map || (pos == 0 && this->map == NULL))
   767 					DoDrawString(item->title, 14, y, _fios_colors[item->type] );
   762 				GfxFillRect(11, y - 1, 258, y + 10, 155); // show highlighted item with a different colour
       
   763 
       
   764 			if (pos == 0) {
       
   765 				DrawString(14, y, STR_4010_GENERATE_RANDOM_NEW_GAME, TC_DARK_GREEN);
       
   766 			} else {
       
   767 				DoDrawString(item->title, 14, y, _fios_colors[item->type] );
       
   768 			}
       
   769 			pos++;
       
   770 			y += NSSWND_ROWSIZE;
       
   771 
       
   772 			if (y >= this->vscroll.cap * NSSWND_ROWSIZE + NSSWND_START) break;
       
   773 		}
       
   774 	}
       
   775 
       
   776 	virtual void OnClick(Point pt, int widget)
       
   777 	{
       
   778 		if (widget != NSSW_CONNTYPE_BTN && widget != NSSW_LANGUAGE_BTN) HideDropDownMenu(this);
       
   779 		this->field = widget;
       
   780 		switch (widget) {
       
   781 			case NSSW_CLOSE:  // Close 'X'
       
   782 			case NSSW_CANCEL: // Cancel button
       
   783 				ShowNetworkGameWindow();
       
   784 				break;
       
   785 
       
   786 			case NSSW_GAMENAME:
       
   787 				ShowOnScreenKeyboard(this, NSSW_GAMENAME, 0, 0);
       
   788 				break;
       
   789 
       
   790 			case NSSW_SETPWD: // Set password button
       
   791 				this->widget_id = NSSW_SETPWD;
       
   792 				ShowQueryString(BindCString(_network_server_password), STR_NETWORK_SET_PASSWORD, 20, 250, this, CS_ALPHANUMERAL);
       
   793 				break;
       
   794 
       
   795 			case NSSW_SELMAP: { // Select map
       
   796 				int y = (pt.y - NSSWND_START) / NSSWND_ROWSIZE;
       
   797 
       
   798 				y += this->vscroll.pos;
       
   799 				if (y >= this->vscroll.count) return;
       
   800 
       
   801 				this->map = (y == 0) ? NULL : _fios_list + y - 1;
       
   802 				this->SetDirty();
       
   803 			} break;
       
   804 
       
   805 			case NSSW_CONNTYPE_BTN: // Connection type
       
   806 				ShowDropDownMenu(this, _connection_types_dropdown, _network_advertise, NSSW_CONNTYPE_BTN, 0, 0); // do it for widget NSSW_CONNTYPE_BTN
       
   807 				break;
       
   808 
       
   809 			case NSSW_CLIENTS_BTND:    case NSSW_CLIENTS_BTNU:    // Click on up/down button for number of clients
       
   810 			case NSSW_COMPANIES_BTND:  case NSSW_COMPANIES_BTNU:  // Click on up/down button for number of companies
       
   811 			case NSSW_SPECTATORS_BTND: case NSSW_SPECTATORS_BTNU: // Click on up/down button for number of spectators
       
   812 				/* Don't allow too fast scrolling */
       
   813 				if ((this->flags4 & WF_TIMEOUT_MASK) <= 2 << WF_TIMEOUT_SHL) {
       
   814 					this->HandleButtonClick(widget);
       
   815 					this->SetDirty();
       
   816 					switch (widget) {
       
   817 						default: NOT_REACHED();
       
   818 						case NSSW_CLIENTS_BTND: case NSSW_CLIENTS_BTNU:
       
   819 							_network_game_info.clients_max    = Clamp(_network_game_info.clients_max    + widget - NSSW_CLIENTS_TXT,    2, MAX_CLIENTS);
       
   820 							break;
       
   821 						case NSSW_COMPANIES_BTND: case NSSW_COMPANIES_BTNU:
       
   822 							_network_game_info.companies_max  = Clamp(_network_game_info.companies_max  + widget - NSSW_COMPANIES_TXT,  1, MAX_PLAYERS);
       
   823 							break;
       
   824 						case NSSW_SPECTATORS_BTND: case NSSW_SPECTATORS_BTNU:
       
   825 							_network_game_info.spectators_max = Clamp(_network_game_info.spectators_max + widget - NSSW_SPECTATORS_TXT, 0, MAX_CLIENTS);
       
   826 							break;
       
   827 					}
   768 				}
   828 				}
   769 				pos++;
   829 				_left_button_clicked = false;
   770 				y += NSSWND_ROWSIZE;
   830 				break;
   771 
   831 
   772 				if (y >= w->vscroll.cap * NSSWND_ROWSIZE + NSSWND_START) break;
   832 			case NSSW_CLIENTS_TXT:    // Click on number of players
       
   833 				this->widget_id = NSSW_CLIENTS_TXT;
       
   834 				SetDParam(0, _network_game_info.clients_max);
       
   835 				ShowQueryString(STR_CONFIG_PATCHES_INT32, STR_NETWORK_NUMBER_OF_CLIENTS,    3, 50, this, CS_NUMERAL);
       
   836 				break;
       
   837 
       
   838 			case NSSW_COMPANIES_TXT:  // Click on number of companies
       
   839 				this->widget_id = NSSW_COMPANIES_TXT;
       
   840 				SetDParam(0, _network_game_info.companies_max);
       
   841 				ShowQueryString(STR_CONFIG_PATCHES_INT32, STR_NETWORK_NUMBER_OF_COMPANIES,  3, 50, this, CS_NUMERAL);
       
   842 				break;
       
   843 
       
   844 			case NSSW_SPECTATORS_TXT: // Click on number of spectators
       
   845 				this->widget_id = NSSW_SPECTATORS_TXT;
       
   846 				SetDParam(0, _network_game_info.spectators_max);
       
   847 				ShowQueryString(STR_CONFIG_PATCHES_INT32, STR_NETWORK_NUMBER_OF_SPECTATORS, 3, 50, this, CS_NUMERAL);
       
   848 				break;
       
   849 
       
   850 			case NSSW_LANGUAGE_BTN: { // Language
       
   851 				uint sel = 0;
       
   852 				for (uint i = 0; i < lengthof(_language_dropdown) - 1; i++) {
       
   853 					if (_language_dropdown[i] == STR_NETWORK_LANG_ANY + _network_game_info.server_lang) {
       
   854 						sel = i;
       
   855 						break;
       
   856 					}
       
   857 				}
       
   858 				ShowDropDownMenu(this, _language_dropdown, sel, NSSW_LANGUAGE_BTN, 0, 0);
       
   859 			} break;
       
   860 
       
   861 			case NSSW_START: // Start game
       
   862 				_is_network_server = true;
       
   863 
       
   864 				if (this->map == NULL) { // start random new game
       
   865 					ShowGenerateLandscape();
       
   866 				} else { // load a scenario
       
   867 					char *name = FiosBrowseTo(this->map);
       
   868 					if (name != NULL) {
       
   869 						SetFiosType(this->map->type);
       
   870 						_file_to_saveload.filetype = FT_SCENARIO;
       
   871 						ttd_strlcpy(_file_to_saveload.name, name, sizeof(_file_to_saveload.name));
       
   872 						ttd_strlcpy(_file_to_saveload.title, this->map->title, sizeof(_file_to_saveload.title));
       
   873 
       
   874 						delete this;
       
   875 						SwitchMode(SM_START_SCENARIO);
       
   876 					}
       
   877 				}
       
   878 				break;
       
   879 
       
   880 			case NSSW_LOAD: // Load game
       
   881 				_is_network_server = true;
       
   882 				/* XXX - WC_NETWORK_WINDOW (this window) should stay, but if it stays, it gets
       
   883 				* copied all the elements of 'load game' and upon closing that, it segfaults */
       
   884 				delete this;
       
   885 				ShowSaveLoadDialog(SLD_LOAD_GAME);
       
   886 				break;
       
   887 		}
       
   888 	}
       
   889 
       
   890 	virtual void OnDropdownSelect(int widget, int index)
       
   891 	{
       
   892 		switch (widget) {
       
   893 			case NSSW_CONNTYPE_BTN:
       
   894 				_network_advertise = (index != 0);
       
   895 				break;
       
   896 			case NSSW_LANGUAGE_BTN:
       
   897 				_network_game_info.server_lang = _language_dropdown[index] - STR_NETWORK_LANG_ANY;
       
   898 				break;
       
   899 			default:
       
   900 				NOT_REACHED();
       
   901 		}
       
   902 
       
   903 		this->SetDirty();
       
   904 	}
       
   905 
       
   906 	virtual void OnMouseLoop()
       
   907 	{
       
   908 		if (this->field == NSSW_GAMENAME) this->HandleEditBox(NSSW_GAMENAME);
       
   909 	}
       
   910 
       
   911 	virtual bool OnKeyPress(uint16 key, uint16 keycode)
       
   912 	{
       
   913 		bool cont = true;
       
   914 		if (this->field == NSSW_GAMENAME) {
       
   915 			if (this->HandleEditBoxKey(NSSW_GAMENAME, key, keycode, cont) == 1) return cont; // enter pressed
       
   916 
       
   917 			ttd_strlcpy(_network_server_name, this->text.buf, sizeof(_network_server_name));
       
   918 		}
       
   919 
       
   920 		return cont;
       
   921 	}
       
   922 
       
   923 	virtual void OnQueryTextFinished(char *str)
       
   924 	{
       
   925 		if (str == NULL) return;
       
   926 
       
   927 		if (this->widget_id == NSSW_SETPWD) {
       
   928 			ttd_strlcpy(_network_server_password, str, lengthof(_network_server_password));
       
   929 			_network_game_info.use_password = !StrEmpty(_network_server_password);
       
   930 		} else {
       
   931 			int32 value = atoi(str);
       
   932 			this->InvalidateWidget(this->widget_id);
       
   933 			switch (this->widget_id) {
       
   934 				default: NOT_REACHED();
       
   935 				case NSSW_CLIENTS_TXT:    _network_game_info.clients_max    = Clamp(value, 2, MAX_CLIENTS); break;
       
   936 				case NSSW_COMPANIES_TXT:  _network_game_info.companies_max  = Clamp(value, 1, MAX_PLAYERS); break;
       
   937 				case NSSW_SPECTATORS_TXT: _network_game_info.spectators_max = Clamp(value, 0, MAX_CLIENTS); break;
   773 			}
   938 			}
   774 		} break;
   939 		}
   775 
   940 
   776 		case WE_CLICK:
   941 		this->SetDirty();
   777 			if (e->we.click.widget != NSSW_CONNTYPE_BTN && e->we.click.widget != NSSW_LANGUAGE_BTN) HideDropDownMenu(w);
   942 	}
   778 			nd->field = e->we.click.widget;
   943 };
   779 			switch (e->we.click.widget) {
       
   780 				case NSSW_CLOSE:  // Close 'X'
       
   781 				case NSSW_CANCEL: // Cancel button
       
   782 					ShowNetworkGameWindow();
       
   783 					break;
       
   784 
       
   785 				case NSSW_GAMENAME:
       
   786 					ShowOnScreenKeyboard(w, &WP(w, network_ql_d).q,  NSSW_GAMENAME, 0, 0);
       
   787 					break;
       
   788 
       
   789 				case NSSW_SETPWD: // Set password button
       
   790 					nd->widget_id = NSSW_SETPWD;
       
   791 					ShowQueryString(BindCString(_network_server_password), STR_NETWORK_SET_PASSWORD, 20, 250, w, CS_ALPHANUMERAL);
       
   792 					break;
       
   793 
       
   794 				case NSSW_SELMAP: { // Select map
       
   795 					int y = (e->we.click.pt.y - NSSWND_START) / NSSWND_ROWSIZE;
       
   796 
       
   797 					y += w->vscroll.pos;
       
   798 					if (y >= w->vscroll.count) return;
       
   799 
       
   800 					nd->map = (y == 0) ? NULL : _fios_list + y - 1;
       
   801 					SetWindowDirty(w);
       
   802 				} break;
       
   803 
       
   804 				case NSSW_CONNTYPE_BTN: // Connection type
       
   805 					ShowDropDownMenu(w, _connection_types_dropdown, _network_advertise, NSSW_CONNTYPE_BTN, 0, 0); // do it for widget NSSW_CONNTYPE_BTN
       
   806 					break;
       
   807 
       
   808 				case NSSW_CLIENTS_BTND:    case NSSW_CLIENTS_BTNU:    // Click on up/down button for number of clients
       
   809 				case NSSW_COMPANIES_BTND:  case NSSW_COMPANIES_BTNU:  // Click on up/down button for number of companies
       
   810 				case NSSW_SPECTATORS_BTND: case NSSW_SPECTATORS_BTNU: // Click on up/down button for number of spectators
       
   811 					/* Don't allow too fast scrolling */
       
   812 					if ((w->flags4 & WF_TIMEOUT_MASK) <= 2 << WF_TIMEOUT_SHL) {
       
   813 						w->HandleButtonClick(e->we.click.widget);
       
   814 						SetWindowDirty(w);
       
   815 						switch (e->we.click.widget) {
       
   816 							default: NOT_REACHED();
       
   817 							case NSSW_CLIENTS_BTND: case NSSW_CLIENTS_BTNU:
       
   818 								_network_game_info.clients_max    = Clamp(_network_game_info.clients_max    + e->we.click.widget - NSSW_CLIENTS_TXT,    2, MAX_CLIENTS);
       
   819 								break;
       
   820 							case NSSW_COMPANIES_BTND: case NSSW_COMPANIES_BTNU:
       
   821 								_network_game_info.companies_max  = Clamp(_network_game_info.companies_max  + e->we.click.widget - NSSW_COMPANIES_TXT,  1, MAX_PLAYERS);
       
   822 								break;
       
   823 							case NSSW_SPECTATORS_BTND: case NSSW_SPECTATORS_BTNU:
       
   824 								_network_game_info.spectators_max = Clamp(_network_game_info.spectators_max + e->we.click.widget - NSSW_SPECTATORS_TXT, 0, MAX_CLIENTS);
       
   825 								break;
       
   826 						}
       
   827 					}
       
   828 					_left_button_clicked = false;
       
   829 					break;
       
   830 
       
   831 				case NSSW_CLIENTS_TXT:    // Click on number of players
       
   832 					nd->widget_id = NSSW_CLIENTS_TXT;
       
   833 					SetDParam(0, _network_game_info.clients_max);
       
   834 					ShowQueryString(STR_CONFIG_PATCHES_INT32, STR_NETWORK_NUMBER_OF_CLIENTS,    3, 50, w, CS_NUMERAL);
       
   835 					break;
       
   836 
       
   837 				case NSSW_COMPANIES_TXT:  // Click on number of companies
       
   838 					nd->widget_id = NSSW_COMPANIES_TXT;
       
   839 					SetDParam(0, _network_game_info.companies_max);
       
   840 					ShowQueryString(STR_CONFIG_PATCHES_INT32, STR_NETWORK_NUMBER_OF_COMPANIES,  3, 50, w, CS_NUMERAL);
       
   841 					break;
       
   842 
       
   843 				case NSSW_SPECTATORS_TXT: // Click on number of spectators
       
   844 					nd->widget_id = NSSW_SPECTATORS_TXT;
       
   845 					SetDParam(0, _network_game_info.spectators_max);
       
   846 					ShowQueryString(STR_CONFIG_PATCHES_INT32, STR_NETWORK_NUMBER_OF_SPECTATORS, 3, 50, w, CS_NUMERAL);
       
   847 					break;
       
   848 
       
   849 				case NSSW_LANGUAGE_BTN: { // Language
       
   850 					uint sel = 0;
       
   851 					for (uint i = 0; i < lengthof(_language_dropdown) - 1; i++) {
       
   852 						if (_language_dropdown[i] == STR_NETWORK_LANG_ANY + _network_game_info.server_lang) {
       
   853 							sel = i;
       
   854 							break;
       
   855 						}
       
   856 					}
       
   857 					ShowDropDownMenu(w, _language_dropdown, sel, NSSW_LANGUAGE_BTN, 0, 0);
       
   858 				} break;
       
   859 
       
   860 				case NSSW_START: // Start game
       
   861 					_is_network_server = true;
       
   862 
       
   863 					if (nd->map == NULL) { // start random new game
       
   864 						ShowGenerateLandscape();
       
   865 					} else { // load a scenario
       
   866 						char *name = FiosBrowseTo(nd->map);
       
   867 						if (name != NULL) {
       
   868 							SetFiosType(nd->map->type);
       
   869 							_file_to_saveload.filetype = FT_SCENARIO;
       
   870 							ttd_strlcpy(_file_to_saveload.name, name, sizeof(_file_to_saveload.name));
       
   871 							ttd_strlcpy(_file_to_saveload.title, nd->map->title, sizeof(_file_to_saveload.title));
       
   872 
       
   873 							delete w;
       
   874 							SwitchMode(SM_START_SCENARIO);
       
   875 						}
       
   876 					}
       
   877 					break;
       
   878 
       
   879 				case NSSW_LOAD: // Load game
       
   880 					_is_network_server = true;
       
   881 					/* XXX - WC_NETWORK_WINDOW (this window) should stay, but if it stays, it gets
       
   882 					* copied all the elements of 'load game' and upon closing that, it segfaults */
       
   883 					delete w;
       
   884 					ShowSaveLoadDialog(SLD_LOAD_GAME);
       
   885 					break;
       
   886 			}
       
   887 			break;
       
   888 
       
   889 		case WE_DROPDOWN_SELECT: // we have selected a dropdown item in the list
       
   890 			switch (e->we.dropdown.button) {
       
   891 				case NSSW_CONNTYPE_BTN:
       
   892 					_network_advertise = (e->we.dropdown.index != 0);
       
   893 					break;
       
   894 				case NSSW_LANGUAGE_BTN:
       
   895 					_network_game_info.server_lang = _language_dropdown[e->we.dropdown.index] - STR_NETWORK_LANG_ANY;
       
   896 					break;
       
   897 				default:
       
   898 					NOT_REACHED();
       
   899 			}
       
   900 
       
   901 			SetWindowDirty(w);
       
   902 			break;
       
   903 
       
   904 		case WE_MOUSELOOP:
       
   905 			if (nd->field == NSSW_GAMENAME) HandleEditBox(w, &WP(w, network_ql_d).q, NSSW_GAMENAME);
       
   906 			break;
       
   907 
       
   908 		case WE_KEYPRESS:
       
   909 			if (nd->field == NSSW_GAMENAME) {
       
   910 				if (HandleEditBoxKey(w, &WP(w, network_ql_d).q, NSSW_GAMENAME, e) == 1) break; // enter pressed
       
   911 
       
   912 				ttd_strlcpy(_network_server_name, WP(w, network_ql_d).q.text.buf, sizeof(_network_server_name));
       
   913 			}
       
   914 			break;
       
   915 
       
   916 		case WE_ON_EDIT_TEXT:
       
   917 			if (e->we.edittext.str == NULL) break;
       
   918 
       
   919 			if (nd->widget_id == NSSW_SETPWD) {
       
   920 				ttd_strlcpy(_network_server_password, e->we.edittext.str, lengthof(_network_server_password));
       
   921 				_network_game_info.use_password = !StrEmpty(_network_server_password);
       
   922 			} else {
       
   923 				int32 value = atoi(e->we.edittext.str);
       
   924 				w->InvalidateWidget(nd->widget_id);
       
   925 				switch (nd->widget_id) {
       
   926 					default: NOT_REACHED();
       
   927 					case NSSW_CLIENTS_TXT:    _network_game_info.clients_max    = Clamp(value, 2, MAX_CLIENTS); break;
       
   928 					case NSSW_COMPANIES_TXT:  _network_game_info.companies_max  = Clamp(value, 1, MAX_PLAYERS); break;
       
   929 					case NSSW_SPECTATORS_TXT: _network_game_info.spectators_max = Clamp(value, 0, MAX_CLIENTS); break;
       
   930 				}
       
   931 			}
       
   932 
       
   933 			SetWindowDirty(w);
       
   934 			break;
       
   935 	}
       
   936 }
       
   937 
   944 
   938 static const Widget _network_start_server_window_widgets[] = {
   945 static const Widget _network_start_server_window_widgets[] = {
   939 /* Window decoration and background panel */
   946 /* Window decoration and background panel */
   940 {   WWT_CLOSEBOX,   RESIZE_NONE,   BGC,     0,    10,     0,    13, STR_00C5,                           STR_018B_CLOSE_WINDOW },               // NSSW_CLOSE
   947 {   WWT_CLOSEBOX,   RESIZE_NONE,   BGC,     0,    10,     0,    13, STR_00C5,                           STR_018B_CLOSE_WINDOW },               // NSSW_CLOSE
   941 {    WWT_CAPTION,   RESIZE_NONE,   BGC,    11,   419,     0,    13, STR_NETWORK_START_GAME_WINDOW,      STR_NULL},
   948 {    WWT_CAPTION,   RESIZE_NONE,   BGC,    11,   419,     0,    13, STR_NETWORK_START_GAME_WINDOW,      STR_NULL},
   984 static const WindowDesc _network_start_server_window_desc = {
   991 static const WindowDesc _network_start_server_window_desc = {
   985 	WDP_CENTER, WDP_CENTER, 420, 244, 420, 244,
   992 	WDP_CENTER, WDP_CENTER, 420, 244, 420, 244,
   986 	WC_NETWORK_WINDOW, WC_NONE,
   993 	WC_NETWORK_WINDOW, WC_NONE,
   987 	WDF_STD_TOOLTIPS | WDF_DEF_WIDGET | WDF_UNCLICK_BUTTONS,
   994 	WDF_STD_TOOLTIPS | WDF_DEF_WIDGET | WDF_UNCLICK_BUTTONS,
   988 	_network_start_server_window_widgets,
   995 	_network_start_server_window_widgets,
   989 	NetworkStartServerWindowWndProc,
   996 	NULL,
   990 };
   997 };
   991 
   998 
   992 static void ShowNetworkStartServerWindow()
   999 static void ShowNetworkStartServerWindow()
   993 {
  1000 {
   994 	DeleteWindowById(WC_NETWORK_WINDOW, 0);
  1001 	DeleteWindowById(WC_NETWORK_WINDOW, 0);
   995 
  1002 
   996 	Window *w = new Window(&_network_start_server_window_desc);
  1003 	new NetworkStartServerWindow(&_network_start_server_window_desc);
   997 	ttd_strlcpy(_edit_str_net_buf, _network_server_name, lengthof(_edit_str_net_buf));
       
   998 
       
   999 	_saveload_mode = SLD_NEW_GAME;
       
  1000 	BuildFileList();
       
  1001 	w->vscroll.cap = 12;
       
  1002 	w->vscroll.count = _fios_num + 1;
       
  1003 
       
  1004 	WP(w, network_ql_d).q.afilter = CS_ALPHANUMERAL;
       
  1005 	InitializeTextBuffer(&WP(w, network_ql_d).q.text, _edit_str_net_buf, lengthof(_edit_str_net_buf), 160);
       
  1006 }
  1004 }
  1007 
  1005 
  1008 static PlayerID NetworkLobbyFindCompanyIndex(byte pos)
  1006 static PlayerID NetworkLobbyFindCompanyIndex(byte pos)
  1009 {
  1007 {
  1010 	/* Scroll through all _network_player_info and get the 'pos' item that is not empty */
  1008 	/* Scroll through all _network_player_info and get the 'pos' item that is not empty */
  1034 	NetworkGameList *server; ///< Selected server
  1032 	NetworkGameList *server; ///< Selected server
  1035 
  1033 
  1036 	NetworkLobbyWindow(const WindowDesc *desc, NetworkGameList *ngl) :
  1034 	NetworkLobbyWindow(const WindowDesc *desc, NetworkGameList *ngl) :
  1037 			Window(desc), company(INVALID_PLAYER), server(ngl)
  1035 			Window(desc), company(INVALID_PLAYER), server(ngl)
  1038 	{
  1036 	{
  1039 		strcpy(_edit_str_net_buf, "");
       
  1040 		this->vscroll.cap = 10;
  1037 		this->vscroll.cap = 10;
  1041 
  1038 
  1042 		this->FindWindowPlacementAndResize(desc);
  1039 		this->FindWindowPlacementAndResize(desc);
  1043 	}
  1040 	}
  1044 
  1041 
  1368 	num *= CLNWND_ROWSIZE;
  1365 	num *= CLNWND_ROWSIZE;
  1369 
  1366 
  1370 	/* If height is changed */
  1367 	/* If height is changed */
  1371 	if (w->height != CLNWND_OFFSET + num + 1) {
  1368 	if (w->height != CLNWND_OFFSET + num + 1) {
  1372 		// XXX - magic unfortunately; (num + 2) has to be one bigger than heigh (num + 1)
  1369 		// XXX - magic unfortunately; (num + 2) has to be one bigger than heigh (num + 1)
  1373 		SetWindowDirty(w);
  1370 		w->SetDirty();
  1374 		w->widget[3].bottom = w->widget[3].top + num + 2;
  1371 		w->widget[3].bottom = w->widget[3].top + num + 2;
  1375 		w->height = CLNWND_OFFSET + num + 1;
  1372 		w->height = CLNWND_OFFSET + num + 1;
  1376 		SetWindowDirty(w);
  1373 		w->SetDirty();
  1377 		return false;
  1374 		return false;
  1378 	}
  1375 	}
  1379 	return true;
  1376 	return true;
  1380 }
  1377 }
  1381 
  1378 
  1506 
  1503 
  1507 			if (_left_button_down) {
  1504 			if (_left_button_down) {
  1508 				if (index == -1 || index == WP(w, menu_d).sel_index) return;
  1505 				if (index == -1 || index == WP(w, menu_d).sel_index) return;
  1509 
  1506 
  1510 				WP(w, menu_d).sel_index = index;
  1507 				WP(w, menu_d).sel_index = index;
  1511 				SetWindowDirty(w);
  1508 				w->SetDirty();
  1512 			} else {
  1509 			} else {
  1513 				if (index >= 0 && _cursor.pos.y >= w->top) {
  1510 				if (index >= 0 && _cursor.pos.y >= w->top) {
  1514 					HandleClientListPopupClick(index, WP(w, menu_d).main_button);
  1511 					HandleClientListPopupClick(index, WP(w, menu_d).main_button);
  1515 				}
  1512 				}
  1516 
  1513 
  1571 		case WE_MOUSEOVER:
  1568 		case WE_MOUSEOVER:
  1572 			/* -1 means we left the current window */
  1569 			/* -1 means we left the current window */
  1573 			if (e->we.mouseover.pt.y == -1) {
  1570 			if (e->we.mouseover.pt.y == -1) {
  1574 				_selected_clientlist_y = 0;
  1571 				_selected_clientlist_y = 0;
  1575 				_selected_clientlist_item = 255;
  1572 				_selected_clientlist_item = 255;
  1576 				SetWindowDirty(w);
  1573 				w->SetDirty();
  1577 				break;
  1574 				break;
  1578 			}
  1575 			}
  1579 			/* It did not change.. no update! */
  1576 			/* It did not change.. no update! */
  1580 			if (e->we.mouseover.pt.y == _selected_clientlist_y) break;
  1577 			if (e->we.mouseover.pt.y == _selected_clientlist_y) break;
  1581 
  1578 
  1586 			} else {
  1583 			} else {
  1587 				_selected_clientlist_item = 255;
  1584 				_selected_clientlist_item = 255;
  1588 			}
  1585 			}
  1589 
  1586 
  1590 			/* Repaint */
  1587 			/* Repaint */
  1591 			SetWindowDirty(w);
  1588 			w->SetDirty();
  1592 			break;
  1589 			break;
  1593 
  1590 
  1594 		case WE_DESTROY: case WE_CREATE:
  1591 		case WE_DESTROY: case WE_CREATE:
  1595 			/* When created or destroyed, data is reset */
  1592 			/* When created or destroyed, data is reset */
  1596 			_selected_clientlist_item = 255;
  1593 			_selected_clientlist_item = 255;
  1704 	} else {
  1701 	} else {
  1705 		NetworkServer_HandleChat((NetworkAction)(NETWORK_ACTION_CHAT + type), type, dest, buf, NETWORK_SERVER_INDEX);
  1702 		NetworkServer_HandleChat((NetworkAction)(NETWORK_ACTION_CHAT + type), type, dest, buf, NETWORK_SERVER_INDEX);
  1706 	}
  1703 	}
  1707 }
  1704 }
  1708 
  1705 
  1709 /**
  1706 
  1710  * Find the next item of the list of things that can be auto-completed.
  1707 struct NetworkChatWindow : public QueryStringBaseWindow {
  1711  * @param item The current indexed item to return. This function can, and most
  1708 	DestType dtype;
  1712  *     likely will, alter item, to skip empty items in the arrays.
  1709 	int dest;
  1713  * @return Returns the char that matched to the index.
  1710 
  1714  */
  1711 	NetworkChatWindow (const WindowDesc *desc, DestType type, int dest) : QueryStringBaseWindow(desc)
  1715 static const char *ChatTabCompletionNextItem(uint *item)
  1712 	{
  1716 {
  1713 		this->LowerWidget(2);
  1717 	static char chat_tab_temp_buffer[64];
  1714 		this->dtype   = type;
  1718 
  1715 		this->dest    = dest;
  1719 	/* First, try clients */
  1716 		this->afilter = CS_ALPHANUMERAL;
  1720 	if (*item < MAX_CLIENT_INFO) {
  1717 		InitializeTextBuffer(&this->text, this->edit_str_buf, lengthof(this->edit_str_buf), 0);
  1721 		/* Skip inactive clients */
  1718 
  1722 		while (_network_client_info[*item].client_index == NETWORK_EMPTY_INDEX && *item < MAX_CLIENT_INFO) (*item)++;
  1719 		InvalidateWindowData(WC_NEWS_WINDOW, 0, this->height);
  1723 		if (*item < MAX_CLIENT_INFO) return _network_client_info[*item].client_name;
  1720 		SetBit(_no_scroll, SCROLL_CHAT); // do not scroll the game with the arrow-keys
  1724 	}
  1721 
  1725 
  1722 		_chat_tab_completion_active = false;
  1726 	/* Then, try townnames */
  1723 
  1727 	/* Not that the following assumes all town indices are adjacent, ie no
  1724 		this->FindWindowPlacementAndResize(desc);
  1728 	 * towns have been deleted. */
  1725 	}
  1729 	if (*item <= (uint)MAX_CLIENT_INFO + GetMaxTownIndex()) {
  1726 
  1730 		const Town *t;
  1727 	~NetworkChatWindow ()
  1731 
  1728 	{
  1732 		FOR_ALL_TOWNS_FROM(t, *item - MAX_CLIENT_INFO) {
  1729 		InvalidateWindowData(WC_NEWS_WINDOW, 0, 0);
  1733 			/* Get the town-name via the string-system */
  1730 		ClrBit(_no_scroll, SCROLL_CHAT);
  1734 			SetDParam(0, t->index);
  1731 	}
  1735 			GetString(chat_tab_temp_buffer, STR_TOWN, lastof(chat_tab_temp_buffer));
  1732 
  1736 			return &chat_tab_temp_buffer[0];
  1733 	/**
  1737 		}
  1734 	 * Find the next item of the list of things that can be auto-completed.
  1738 	}
  1735 	 * @param item The current indexed item to return. This function can, and most
  1739 
  1736 	 *     likely will, alter item, to skip empty items in the arrays.
  1740 	return NULL;
  1737 	 * @return Returns the char that matched to the index.
  1741 }
  1738 	 */
  1742 
  1739 	const char *ChatTabCompletionNextItem(uint *item)
  1743 /**
  1740 	{
  1744  * Find what text to complete. It scans for a space from the left and marks
  1741 		static char chat_tab_temp_buffer[64];
  1745  *  the word right from that as to complete. It also writes a \0 at the
  1742 
  1746  *  position of the space (if any). If nothing found, buf is returned.
  1743 		/* First, try clients */
  1747  */
  1744 		if (*item < MAX_CLIENT_INFO) {
  1748 static char *ChatTabCompletionFindText(char *buf)
  1745 			/* Skip inactive clients */
  1749 {
  1746 			while (_network_client_info[*item].client_index == NETWORK_EMPTY_INDEX && *item < MAX_CLIENT_INFO) (*item)++;
  1750 	char *p = strrchr(buf, ' ');
  1747 			if (*item < MAX_CLIENT_INFO) return _network_client_info[*item].client_name;
  1751 	if (p == NULL) return buf;
  1748 		}
  1752 
  1749 
  1753 	*p = '\0';
  1750 		/* Then, try townnames */
  1754 	return p + 1;
  1751 		/* Not that the following assumes all town indices are adjacent, ie no
  1755 }
  1752 		* towns have been deleted. */
  1756 
  1753 		if (*item <= (uint)MAX_CLIENT_INFO + GetMaxTownIndex()) {
  1757 /**
  1754 			const Town *t;
  1758  * See if we can auto-complete the current text of the user.
  1755 
  1759  */
  1756 			FOR_ALL_TOWNS_FROM(t, *item - MAX_CLIENT_INFO) {
  1760 static void ChatTabCompletion(Window *w)
  1757 				/* Get the town-name via the string-system */
  1761 {
  1758 				SetDParam(0, t->index);
  1762 	static char _chat_tab_completion_buf[lengthof(_edit_str_net_buf)];
  1759 				GetString(chat_tab_temp_buffer, STR_TOWN, lastof(chat_tab_temp_buffer));
  1763 	Textbuf *tb = &WP(w, chatquerystr_d).text;
  1760 				return &chat_tab_temp_buffer[0];
  1764 	size_t len, tb_len;
  1761 			}
  1765 	uint item;
  1762 		}
  1766 	char *tb_buf, *pre_buf;
  1763 
  1767 	const char *cur_name;
  1764 		return NULL;
  1768 	bool second_scan = false;
  1765 	}
  1769 
  1766 
  1770 	item = 0;
  1767 	/**
  1771 
  1768 	 * Find what text to complete. It scans for a space from the left and marks
  1772 	/* Copy the buffer so we can modify it without damaging the real data */
  1769 	 *  the word right from that as to complete. It also writes a \0 at the
  1773 	pre_buf = (_chat_tab_completion_active) ? strdup(_chat_tab_completion_buf) : strdup(tb->buf);
  1770 	 *  position of the space (if any). If nothing found, buf is returned.
  1774 
  1771 	 */
  1775 	tb_buf  = ChatTabCompletionFindText(pre_buf);
  1772 	static char *ChatTabCompletionFindText(char *buf)
  1776 	tb_len  = strlen(tb_buf);
  1773 	{
  1777 
  1774 		char *p = strrchr(buf, ' ');
  1778 	while ((cur_name = ChatTabCompletionNextItem(&item)) != NULL) {
  1775 		if (p == NULL) return buf;
  1779 		item++;
  1776 
  1780 
  1777 		*p = '\0';
  1781 		if (_chat_tab_completion_active) {
  1778 		return p + 1;
  1782 			/* We are pressing TAB again on the same name, is there an other name
  1779 	}
  1783 			 *  that starts with this? */
  1780 
  1784 			if (!second_scan) {
  1781 	/**
  1785 				size_t offset;
  1782 	 * See if we can auto-complete the current text of the user.
  1786 				size_t length;
  1783 	 */
  1787 
  1784 	void ChatTabCompletion()
  1788 				/* If we are completing at the begin of the line, skip the ': ' we added */
  1785 	{
  1789 				if (tb_buf == pre_buf) {
  1786 		static char _chat_tab_completion_buf[lengthof(this->edit_str_buf)];
  1790 					offset = 0;
  1787 		Textbuf *tb = &this->text;
  1791 					length = tb->length - 2;
  1788 		size_t len, tb_len;
       
  1789 		uint item;
       
  1790 		char *tb_buf, *pre_buf;
       
  1791 		const char *cur_name;
       
  1792 		bool second_scan = false;
       
  1793 
       
  1794 		item = 0;
       
  1795 
       
  1796 		/* Copy the buffer so we can modify it without damaging the real data */
       
  1797 		pre_buf = (_chat_tab_completion_active) ? strdup(_chat_tab_completion_buf) : strdup(tb->buf);
       
  1798 
       
  1799 		tb_buf  = ChatTabCompletionFindText(pre_buf);
       
  1800 		tb_len  = strlen(tb_buf);
       
  1801 
       
  1802 		while ((cur_name = ChatTabCompletionNextItem(&item)) != NULL) {
       
  1803 			item++;
       
  1804 
       
  1805 			if (_chat_tab_completion_active) {
       
  1806 				/* We are pressing TAB again on the same name, is there an other name
       
  1807 				*  that starts with this? */
       
  1808 				if (!second_scan) {
       
  1809 					size_t offset;
       
  1810 					size_t length;
       
  1811 
       
  1812 					/* If we are completing at the begin of the line, skip the ': ' we added */
       
  1813 					if (tb_buf == pre_buf) {
       
  1814 						offset = 0;
       
  1815 						length = tb->length - 2;
       
  1816 					} else {
       
  1817 						/* Else, find the place we are completing at */
       
  1818 						offset = strlen(pre_buf) + 1;
       
  1819 						length = tb->length - offset;
       
  1820 					}
       
  1821 
       
  1822 					/* Compare if we have a match */
       
  1823 					if (strlen(cur_name) == length && strncmp(cur_name, tb->buf + offset, length) == 0) second_scan = true;
       
  1824 
       
  1825 					continue;
       
  1826 				}
       
  1827 
       
  1828 				/* Now any match we make on _chat_tab_completion_buf after this, is perfect */
       
  1829 			}
       
  1830 
       
  1831 			len = strlen(cur_name);
       
  1832 			if (tb_len < len && strncasecmp(cur_name, tb_buf, tb_len) == 0) {
       
  1833 				/* Save the data it was before completion */
       
  1834 				if (!second_scan) snprintf(_chat_tab_completion_buf, lengthof(_chat_tab_completion_buf), "%s", tb->buf);
       
  1835 				_chat_tab_completion_active = true;
       
  1836 
       
  1837 				/* Change to the found name. Add ': ' if we are at the start of the line (pretty) */
       
  1838 				if (pre_buf == tb_buf) {
       
  1839 					snprintf(tb->buf, lengthof(this->edit_str_buf), "%s: ", cur_name);
  1792 				} else {
  1840 				} else {
  1793 					/* Else, find the place we are completing at */
  1841 					snprintf(tb->buf, lengthof(this->edit_str_buf), "%s %s", pre_buf, cur_name);
  1794 					offset = strlen(pre_buf) + 1;
       
  1795 					length = tb->length - offset;
       
  1796 				}
  1842 				}
  1797 
  1843 
  1798 				/* Compare if we have a match */
  1844 				/* Update the textbuffer */
  1799 				if (strlen(cur_name) == length && strncmp(cur_name, tb->buf + offset, length) == 0) second_scan = true;
  1845 				UpdateTextBufferSize(&this->text);
  1800 
  1846 
  1801 				continue;
  1847 				this->SetDirty();
       
  1848 				free(pre_buf);
       
  1849 				return;
  1802 			}
  1850 			}
  1803 
  1851 		}
  1804 			/* Now any match we make on _chat_tab_completion_buf after this, is perfect */
  1852 
  1805 		}
  1853 		if (second_scan) {
  1806 
  1854 			/* We walked all posibilities, and the user presses tab again.. revert to original text */
  1807 		len = strlen(cur_name);
  1855 			strcpy(tb->buf, _chat_tab_completion_buf);
  1808 		if (tb_len < len && strncasecmp(cur_name, tb_buf, tb_len) == 0) {
  1856 			_chat_tab_completion_active = false;
  1809 			/* Save the data it was before completion */
  1857 
  1810 			if (!second_scan) snprintf(_chat_tab_completion_buf, lengthof(_chat_tab_completion_buf), "%s", tb->buf);
  1858 			/* Update the textbuffer */
  1811 			_chat_tab_completion_active = true;
  1859 			UpdateTextBufferSize(&this->text);
  1812 
  1860 
  1813 			/* Change to the found name. Add ': ' if we are at the start of the line (pretty) */
  1861 			this->SetDirty();
  1814 			if (pre_buf == tb_buf) {
  1862 		}
  1815 				snprintf(tb->buf, lengthof(_edit_str_net_buf), "%s: ", cur_name);
  1863 		free(pre_buf);
  1816 			} else {
  1864 	}
  1817 				snprintf(tb->buf, lengthof(_edit_str_net_buf), "%s %s", pre_buf, cur_name);
  1865 
       
  1866 	virtual void OnPaint()
       
  1867 	{
       
  1868 		static const StringID chat_captions[] = {
       
  1869 			STR_NETWORK_CHAT_ALL_CAPTION,
       
  1870 			STR_NETWORK_CHAT_COMPANY_CAPTION,
       
  1871 			STR_NETWORK_CHAT_CLIENT_CAPTION
       
  1872 		};
       
  1873 
       
  1874 		DrawWindowWidgets(this);
       
  1875 
       
  1876 		assert((uint)this->dtype < lengthof(chat_captions));
       
  1877 		DrawStringRightAligned(this->widget[2].left - 2, this->widget[2].top + 1, chat_captions[this->dtype], TC_BLACK);
       
  1878 		this->DrawEditBox(2);
       
  1879 	}
       
  1880 
       
  1881 	virtual void OnClick(Point pt, int widget)
       
  1882 	{
       
  1883 		switch (widget) {
       
  1884 			case 2:
       
  1885 				ShowOnScreenKeyboard(this, 2, 0, 3);
       
  1886 				break;
       
  1887 
       
  1888 			case 3: /* Send */
       
  1889 				SendChat(this->text.buf, this->dtype, this->dest);
       
  1890 			/* FALLTHROUGH */
       
  1891 			case 0: /* Cancel */ delete this; break;
       
  1892 		}
       
  1893 	}
       
  1894 
       
  1895 	virtual void OnMouseLoop()
       
  1896 	{
       
  1897 		this->HandleEditBox(2);
       
  1898 	}
       
  1899 
       
  1900 	virtual bool OnKeyPress(uint16 key, uint16 keycode)
       
  1901 	{
       
  1902 		bool cont = true;
       
  1903 		if (keycode == WKC_TAB) {
       
  1904 			ChatTabCompletion();
       
  1905 		} else {
       
  1906 			_chat_tab_completion_active = false;
       
  1907 			switch (this->HandleEditBoxKey(2, key, keycode, cont)) {
       
  1908 				case 1: /* Return */
       
  1909 					SendChat(this->text.buf, this->dtype, this->dest);
       
  1910 				/* FALLTHROUGH */
       
  1911 				case 2: /* Escape */ delete this; break;
  1818 			}
  1912 			}
  1819 
  1913 		}
  1820 			/* Update the textbuffer */
  1914 		return cont;
  1821 			UpdateTextBufferSize(&WP(w, chatquerystr_d).text);
  1915 	}
  1822 
  1916 };
  1823 			SetWindowDirty(w);
       
  1824 			free(pre_buf);
       
  1825 			return;
       
  1826 		}
       
  1827 	}
       
  1828 
       
  1829 	if (second_scan) {
       
  1830 		/* We walked all posibilities, and the user presses tab again.. revert to original text */
       
  1831 		strcpy(tb->buf, _chat_tab_completion_buf);
       
  1832 		_chat_tab_completion_active = false;
       
  1833 
       
  1834 		/* Update the textbuffer */
       
  1835 		UpdateTextBufferSize(&WP(w, chatquerystr_d).text);
       
  1836 
       
  1837 		SetWindowDirty(w);
       
  1838 	}
       
  1839 	free(pre_buf);
       
  1840 }
       
  1841 
       
  1842 /*
       
  1843  * uses chatquerystr_d WP macro
       
  1844  * uses chatquerystr_d->dtype to store type of chat message (Private/Team/All)
       
  1845  */
       
  1846 static void ChatWindowWndProc(Window *w, WindowEvent *e)
       
  1847 {
       
  1848 	switch (e->event) {
       
  1849 		case WE_CREATE:
       
  1850 			InvalidateWindowData(WC_NEWS_WINDOW, 0, w->height);
       
  1851 			SetBit(_no_scroll, SCROLL_CHAT); // do not scroll the game with the arrow-keys
       
  1852 			break;
       
  1853 
       
  1854 		case WE_PAINT: {
       
  1855 			static const StringID chat_captions[] = {
       
  1856 				STR_NETWORK_CHAT_ALL_CAPTION,
       
  1857 				STR_NETWORK_CHAT_COMPANY_CAPTION,
       
  1858 				STR_NETWORK_CHAT_CLIENT_CAPTION
       
  1859 			};
       
  1860 
       
  1861 			DrawWindowWidgets(w);
       
  1862 
       
  1863 			assert((uint)WP(w, chatquerystr_d).dtype < lengthof(chat_captions));
       
  1864 			DrawStringRightAligned(w->widget[2].left - 2, w->widget[2].top + 1, chat_captions[WP(w, chatquerystr_d).dtype], TC_BLACK);
       
  1865 			DrawEditBox(w, &WP(w, chatquerystr_d), 2);
       
  1866 		} break;
       
  1867 
       
  1868 		case WE_CLICK:
       
  1869 			switch (e->we.click.widget) {
       
  1870 				case 2:
       
  1871 					ShowOnScreenKeyboard(w, &WP(w, chatquerystr_d), 2, 0, 3);
       
  1872 					break;
       
  1873 
       
  1874 				case 3: /* Send */
       
  1875 					SendChat(WP(w, chatquerystr_d).text.buf, WP(w, chatquerystr_d).dtype, WP(w, chatquerystr_d).dest);
       
  1876 				/* FALLTHROUGH */
       
  1877 				case 0: /* Cancel */ delete w; break;
       
  1878 			}
       
  1879 			break;
       
  1880 
       
  1881 		case WE_MOUSELOOP:
       
  1882 			HandleEditBox(w, &WP(w, chatquerystr_d), 2);
       
  1883 			break;
       
  1884 
       
  1885 		case WE_KEYPRESS:
       
  1886 			if (e->we.keypress.keycode == WKC_TAB) {
       
  1887 				ChatTabCompletion(w);
       
  1888 			} else {
       
  1889 				_chat_tab_completion_active = false;
       
  1890 				switch (HandleEditBoxKey(w, &WP(w, chatquerystr_d), 2, e)) {
       
  1891 					case 1: /* Return */
       
  1892 						SendChat(WP(w, chatquerystr_d).text.buf, WP(w, chatquerystr_d).dtype, WP(w, chatquerystr_d).dest);
       
  1893 					/* FALLTHROUGH */
       
  1894 					case 2: /* Escape */ delete w; break;
       
  1895 				}
       
  1896 			}
       
  1897 			break;
       
  1898 
       
  1899 		case WE_DESTROY:
       
  1900 			InvalidateWindowData(WC_NEWS_WINDOW, 0, 0);
       
  1901 			ClrBit(_no_scroll, SCROLL_CHAT);
       
  1902 			break;
       
  1903 	}
       
  1904 }
       
  1905 
  1917 
  1906 static const Widget _chat_window_widgets[] = {
  1918 static const Widget _chat_window_widgets[] = {
  1907 {   WWT_CLOSEBOX, RESIZE_NONE,  14,   0,  10,  0, 13, STR_00C5,                  STR_018B_CLOSE_WINDOW},
  1919 {   WWT_CLOSEBOX, RESIZE_NONE,  14,   0,  10,  0, 13, STR_00C5,                  STR_018B_CLOSE_WINDOW},
  1908 {      WWT_PANEL, RESIZE_RIGHT, 14,  11, 319,  0, 13, 0x0,                       STR_NULL}, // background
  1920 {      WWT_PANEL, RESIZE_RIGHT, 14,  11, 319,  0, 13, 0x0,                       STR_NULL}, // background
  1909 {    WWT_EDITBOX, RESIZE_RIGHT, 14,  75, 257,  1, 12, STR_NETWORK_CHAT_OSKTITLE, STR_NULL}, // text box
  1921 {    WWT_EDITBOX, RESIZE_RIGHT, 14,  75, 257,  1, 12, STR_NETWORK_CHAT_OSKTITLE, STR_NULL}, // text box
  1914 static const WindowDesc _chat_window_desc = {
  1926 static const WindowDesc _chat_window_desc = {
  1915 	WDP_CENTER, -26, 320, 14, 640, 14, // x, y, width, height
  1927 	WDP_CENTER, -26, 320, 14, 640, 14, // x, y, width, height
  1916 	WC_SEND_NETWORK_MSG, WC_NONE,
  1928 	WC_SEND_NETWORK_MSG, WC_NONE,
  1917 	WDF_STD_TOOLTIPS | WDF_DEF_WIDGET,
  1929 	WDF_STD_TOOLTIPS | WDF_DEF_WIDGET,
  1918 	_chat_window_widgets,
  1930 	_chat_window_widgets,
  1919 	ChatWindowWndProc
  1931 	NULL
  1920 };
  1932 };
  1921 
  1933 
  1922 void ShowNetworkChatQueryWindow(DestType type, int dest)
  1934 void ShowNetworkChatQueryWindow(DestType type, int dest)
  1923 {
  1935 {
  1924 	DeleteWindowById(WC_SEND_NETWORK_MSG, 0);
  1936 	DeleteWindowById(WC_SEND_NETWORK_MSG, 0);
  1925 
  1937 	new NetworkChatWindow (&_chat_window_desc, type, dest);
  1926 	_edit_str_net_buf[0] = '\0';
       
  1927 	_chat_tab_completion_active = false;
       
  1928 
       
  1929 	Window *w = new Window(&_chat_window_desc);
       
  1930 
       
  1931 	w->LowerWidget(2);
       
  1932 	WP(w, chatquerystr_d).dtype   = type;
       
  1933 	WP(w, chatquerystr_d).dest    = dest;
       
  1934 	WP(w, chatquerystr_d).afilter = CS_ALPHANUMERAL;
       
  1935 	InitializeTextBuffer(&WP(w, chatquerystr_d).text, _edit_str_net_buf, lengthof(_edit_str_net_buf), 0);
       
  1936 }
  1938 }
  1937 
  1939 
  1938 /** Enum for NetworkGameWindow, referring to _network_game_window_widgets */
  1940 /** Enum for NetworkGameWindow, referring to _network_game_window_widgets */
  1939 enum NetworkCompanyPasswordWindowWidgets {
  1941 enum NetworkCompanyPasswordWindowWidgets {
  1940 	NCPWW_CLOSE,                    ///< Close 'X' button
  1942 	NCPWW_CLOSE,                    ///< Close 'X' button
  1945 	NCPWW_SAVE_AS_DEFAULT_PASSWORD, ///< Toggle 'button' for saving the current password as default password
  1947 	NCPWW_SAVE_AS_DEFAULT_PASSWORD, ///< Toggle 'button' for saving the current password as default password
  1946 	NCPWW_CANCEL,                   ///< Close the window without changing anything
  1948 	NCPWW_CANCEL,                   ///< Close the window without changing anything
  1947 	NCPWW_OK,                       ///< Safe the password etc.
  1949 	NCPWW_OK,                       ///< Safe the password etc.
  1948 };
  1950 };
  1949 
  1951 
  1950 static void NetworkCompanyPasswordWindowWndProc(Window *w, WindowEvent *e)
  1952 struct NetworkCompanyPasswordWindow : public QueryStringBaseWindow {
  1951 {
  1953 	NetworkCompanyPasswordWindow(const WindowDesc *desc) : QueryStringBaseWindow(desc)
  1952 	switch (e->event) {
  1954 	{
  1953 		case WE_PAINT:
  1955 		this->afilter = CS_ALPHANUMERAL;
  1954 			DrawWindowWidgets(w);
  1956 		InitializeTextBuffer(&this->text, this->edit_str_buf, min(lengthof(_network_default_company_pass), lengthof(this->edit_str_buf)), 0);
  1955 			DrawEditBox(w, &WP(w, chatquerystr_d), 4);
  1957 
  1956 			break;
  1958 		this->FindWindowPlacementAndResize(desc);
  1957 
  1959 	}
  1958 		case WE_CLICK:
  1960 
  1959 			switch (e->we.click.widget) {
  1961 	void OnOk()
  1960 				case NCPWW_OK: {
  1962 	{
  1961 					if (w->IsWidgetLowered(NCPWW_SAVE_AS_DEFAULT_PASSWORD)) {
  1963 		if (this->IsWidgetLowered(NCPWW_SAVE_AS_DEFAULT_PASSWORD)) {
  1962 						snprintf(_network_default_company_pass, lengthof(_network_default_company_pass), "%s", _edit_str_net_buf);
  1964 			snprintf(_network_default_company_pass, lengthof(_network_default_company_pass), "%s", this->edit_str_buf);
  1963 					}
  1965 		}
  1964 
  1966 
  1965 					/* empty password is a '*' because of console argument */
  1967 		/* empty password is a '*' because of console argument */
  1966 					if (StrEmpty(_edit_str_net_buf)) snprintf(_edit_str_net_buf, lengthof(_edit_str_net_buf), "*");
  1968 		if (StrEmpty(this->edit_str_buf)) snprintf(this->edit_str_buf, lengthof(this->edit_str_buf), "*");
  1967 					char *password = _edit_str_net_buf;
  1969 		char *password = this->edit_str_buf;
  1968 					NetworkChangeCompanyPassword(1, &password);
  1970 		NetworkChangeCompanyPassword(1, &password);
  1969 				}
  1971 	}
  1970 
  1972 
       
  1973 	virtual void OnPaint()
       
  1974 	{
       
  1975 		DrawWindowWidgets(this);
       
  1976 		this->DrawEditBox(4);
       
  1977 	}
       
  1978 
       
  1979 	virtual void OnClick(Point pt, int widget)
       
  1980 	{
       
  1981 		switch (widget) {
       
  1982 			case NCPWW_OK:
       
  1983 				this->OnOk();
       
  1984 
       
  1985 			/* FALL THROUGH */
       
  1986 			case NCPWW_CANCEL:
       
  1987 				delete this;
       
  1988 				break;
       
  1989 
       
  1990 			case NCPWW_SAVE_AS_DEFAULT_PASSWORD:
       
  1991 				this->ToggleWidgetLoweredState(NCPWW_SAVE_AS_DEFAULT_PASSWORD);
       
  1992 				this->SetDirty();
       
  1993 				break;
       
  1994 
       
  1995 			case NCPWW_PASSWORD:
       
  1996 				ShowOnScreenKeyboard(this, NCPWW_PASSWORD, 2, 1);
       
  1997 				break;
       
  1998 		}
       
  1999 	}
       
  2000 
       
  2001 	virtual void OnMouseLoop()
       
  2002 	{
       
  2003 		this->HandleEditBox(4);
       
  2004 	}
       
  2005 
       
  2006 	virtual bool OnKeyPress(uint16 key, uint16 keycode)
       
  2007 	{
       
  2008 		bool cont;
       
  2009 		switch (this->HandleEditBoxKey(4, key, keycode, cont)) {
       
  2010 			case 1: // Return
       
  2011 				this->OnOk();
  1971 				/* FALL THROUGH */
  2012 				/* FALL THROUGH */
  1972 				case NCPWW_CANCEL:
  2013 
  1973 					delete w;
  2014 			case 2: // Escape
  1974 					break;
  2015 				delete this;
  1975 
  2016 				break;
  1976 				case NCPWW_SAVE_AS_DEFAULT_PASSWORD:
  2017 		}
  1977 					w->ToggleWidgetLoweredState(NCPWW_SAVE_AS_DEFAULT_PASSWORD);
  2018 		return cont;
  1978 					SetWindowDirty(w);
  2019 	}
  1979 					break;
  2020 };
  1980 				case NCPWW_PASSWORD:
       
  1981 					ShowOnScreenKeyboard(w, &WP(w, chatquerystr_d), NCPWW_PASSWORD, 2, 1);
       
  1982 					break;
       
  1983 			}
       
  1984 			break;
       
  1985 
       
  1986 		case WE_MOUSELOOP:
       
  1987 			HandleEditBox(w, &WP(w, chatquerystr_d), 4);
       
  1988 			break;
       
  1989 
       
  1990 		case WE_KEYPRESS:
       
  1991 			switch (HandleEditBoxKey(w, &WP(w, chatquerystr_d), 4, e)) {
       
  1992 				case 1: // Return
       
  1993 					e->event = WE_CLICK;
       
  1994 					e->we.click.widget = NCPWW_OK;
       
  1995 					NetworkCompanyPasswordWindowWndProc(w, e);
       
  1996 					break;
       
  1997 
       
  1998 				case 2: // Escape
       
  1999 					delete w;
       
  2000 					break;
       
  2001 			}
       
  2002 			break;
       
  2003 	}
       
  2004 }
       
  2005 
  2021 
  2006 static const Widget _ncp_window_widgets[] = {
  2022 static const Widget _ncp_window_widgets[] = {
  2007 {   WWT_CLOSEBOX, RESIZE_NONE, 14,   0,  10,  0, 13, STR_00C5,                          STR_018B_CLOSE_WINDOW},
  2023 {   WWT_CLOSEBOX, RESIZE_NONE, 14,   0,  10,  0, 13, STR_00C5,                          STR_018B_CLOSE_WINDOW},
  2008 {    WWT_CAPTION, RESIZE_NONE, 14,  11, 299,  0, 13, STR_COMPANY_PASSWORD_CAPTION,      STR_018C_WINDOW_TITLE_DRAG_THIS},
  2024 {    WWT_CAPTION, RESIZE_NONE, 14,  11, 299,  0, 13, STR_COMPANY_PASSWORD_CAPTION,      STR_018C_WINDOW_TITLE_DRAG_THIS},
  2009 {      WWT_PANEL, RESIZE_NONE, 14,   0, 299, 14, 50, 0x0,                               STR_NULL},
  2025 {      WWT_PANEL, RESIZE_NONE, 14,   0, 299, 14, 50, 0x0,                               STR_NULL},
  2018 static const WindowDesc _ncp_window_desc = {
  2034 static const WindowDesc _ncp_window_desc = {
  2019 	WDP_AUTO, WDP_AUTO, 300, 63, 300, 63,
  2035 	WDP_AUTO, WDP_AUTO, 300, 63, 300, 63,
  2020 	WC_COMPANY_PASSWORD_WINDOW, WC_NONE,
  2036 	WC_COMPANY_PASSWORD_WINDOW, WC_NONE,
  2021 	WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET | WDF_UNCLICK_BUTTONS | WDF_STICKY_BUTTON,
  2037 	WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET | WDF_UNCLICK_BUTTONS | WDF_STICKY_BUTTON,
  2022 	_ncp_window_widgets,
  2038 	_ncp_window_widgets,
  2023 	NetworkCompanyPasswordWindowWndProc
  2039 	NULL
  2024 };
  2040 };
  2025 
  2041 
  2026 void ShowNetworkCompanyPasswordWindow()
  2042 void ShowNetworkCompanyPasswordWindow()
  2027 {
  2043 {
  2028 	DeleteWindowById(WC_COMPANY_PASSWORD_WINDOW, 0);
  2044 	DeleteWindowById(WC_COMPANY_PASSWORD_WINDOW, 0);
  2029 
  2045 
  2030 	_edit_str_net_buf[0] = '\0';
  2046 	new NetworkCompanyPasswordWindow(&_ncp_window_desc);
  2031 	Window *w = new Window(&_ncp_window_desc);
       
  2032 	WP(w, chatquerystr_d).afilter = CS_ALPHANUMERAL;
       
  2033 	InitializeTextBuffer(&WP(w, chatquerystr_d).text, _edit_str_net_buf, min(lengthof(_network_default_company_pass), lengthof(_edit_str_net_buf)), 0);
       
  2034 }
  2047 }
  2035 
  2048 
  2036 #endif /* ENABLE_NETWORK */
  2049 #endif /* ENABLE_NETWORK */