network/network_gui.c
branchcustombridgeheads
changeset 5642 bfa6074e2833
equal deleted inserted replaced
5641:d4d00a16ef26 5642:bfa6074e2833
       
     1 /* $Id$ */
       
     2 
       
     3 #ifdef ENABLE_NETWORK
       
     4 #include "../stdafx.h"
       
     5 #include "../openttd.h"
       
     6 #include "../string.h"
       
     7 #include "../strings.h"
       
     8 #include "../table/sprites.h"
       
     9 #include "network.h"
       
    10 #include "../date.h"
       
    11 
       
    12 #include "../fios.h"
       
    13 #include "../table/strings.h"
       
    14 #include "../functions.h"
       
    15 #include "network_data.h"
       
    16 #include "network_client.h"
       
    17 #include "network_gui.h"
       
    18 #include "network_gamelist.h"
       
    19 #include "../window.h"
       
    20 #include "../gui.h"
       
    21 #include "../gfx.h"
       
    22 #include "../command.h"
       
    23 #include "../variables.h"
       
    24 #include "network_server.h"
       
    25 #include "network_udp.h"
       
    26 #include "../settings.h"
       
    27 #include "../string.h"
       
    28 #include "../town.h"
       
    29 #include "../newgrf.h"
       
    30 
       
    31 #define BGC 5
       
    32 #define BTC 15
       
    33 
       
    34 typedef struct network_d {
       
    35 	PlayerID company;        // select company in network lobby
       
    36 	byte field;              // select text-field in start-server and game-listing
       
    37 	NetworkGameList *server; // selected server in lobby and game-listing
       
    38 	FiosItem *map;           // selected map in start-server
       
    39 } network_d;
       
    40 assert_compile(WINDOW_CUSTOM_SIZE >= sizeof(network_d));
       
    41 
       
    42 typedef struct network_ql_d {
       
    43 	network_d n;                 // see above; general stuff
       
    44 	querystr_d q;                // text-input in start-server and game-listing
       
    45 	NetworkGameList **sort_list; // list of games (sorted)
       
    46 	list_d l;                    // accompanying list-administration
       
    47 } network_ql_d;
       
    48 assert_compile(WINDOW_CUSTOM_SIZE >= sizeof(network_ql_d));
       
    49 
       
    50 /* Global to remember sorting after window has been closed */
       
    51 static Listing _ng_sorting;
       
    52 
       
    53 static char _edit_str_buf[150];
       
    54 static bool _chat_tab_completion_active;
       
    55 
       
    56 static void ShowNetworkStartServerWindow(void);
       
    57 static void ShowNetworkLobbyWindow(NetworkGameList *ngl);
       
    58 extern void SwitchMode(int new_mode);
       
    59 
       
    60 static const StringID _connection_types_dropdown[] = {
       
    61 	STR_NETWORK_LAN_INTERNET,
       
    62 	STR_NETWORK_INTERNET_ADVERTISE,
       
    63 	INVALID_STRING_ID
       
    64 };
       
    65 
       
    66 static const StringID _lan_internet_types_dropdown[] = {
       
    67 	STR_NETWORK_LAN,
       
    68 	STR_NETWORK_INTERNET,
       
    69 	INVALID_STRING_ID
       
    70 };
       
    71 
       
    72 static const StringID _players_dropdown[] = {
       
    73 	STR_NETWORK_0_PLAYERS,
       
    74 	STR_NETWORK_1_PLAYERS,
       
    75 	STR_NETWORK_2_PLAYERS,
       
    76 	STR_NETWORK_3_PLAYERS,
       
    77 	STR_NETWORK_4_PLAYERS,
       
    78 	STR_NETWORK_5_PLAYERS,
       
    79 	STR_NETWORK_6_PLAYERS,
       
    80 	STR_NETWORK_7_PLAYERS,
       
    81 	STR_NETWORK_8_PLAYERS,
       
    82 	STR_NETWORK_9_PLAYERS,
       
    83 	STR_NETWORK_10_PLAYERS,
       
    84 	INVALID_STRING_ID
       
    85 };
       
    86 
       
    87 static const StringID _language_dropdown[] = {
       
    88 	STR_NETWORK_LANG_ANY,
       
    89 	STR_NETWORK_LANG_ENGLISH,
       
    90 	STR_NETWORK_LANG_GERMAN,
       
    91 	STR_NETWORK_LANG_FRENCH,
       
    92 	INVALID_STRING_ID
       
    93 };
       
    94 
       
    95 enum {
       
    96 	NET_PRC__OFFSET_TOP_WIDGET          = 54,
       
    97 	NET_PRC__OFFSET_TOP_WIDGET_COMPANY  = 52,
       
    98 	NET_PRC__SIZE_OF_ROW                = 14,
       
    99 };
       
   100 
       
   101 /** Update the network new window because a new server is
       
   102  * found on the network.
       
   103  * @param unselect unselect the currently selected item */
       
   104 void UpdateNetworkGameWindow(bool unselect)
       
   105 {
       
   106 	SendWindowMessage(WC_NETWORK_WINDOW, 0, unselect, 0, 0);
       
   107 }
       
   108 
       
   109 static bool _internal_sort_order; // Used for Qsort order-flipping
       
   110 typedef int CDECL NGameNameSortFunction(const void*, const void*);
       
   111 
       
   112 /** Qsort function to sort by name. */
       
   113 static int CDECL NGameNameSorter(const void *a, const void *b)
       
   114 {
       
   115 	const NetworkGameList *cmp1 = *(const NetworkGameList**)a;
       
   116 	const NetworkGameList *cmp2 = *(const NetworkGameList**)b;
       
   117 	int r = strcasecmp(cmp1->info.server_name, cmp2->info.server_name);
       
   118 
       
   119 	return _internal_sort_order ? -r : r;
       
   120 }
       
   121 
       
   122 /** Qsort function to sort by the amount of clients online on a
       
   123  * server. If the two servers have the same amount, the one with the
       
   124  * higher maximum is preferred. */
       
   125 static int CDECL NGameClientSorter(const void *a, const void *b)
       
   126 {
       
   127 	const NetworkGameList *cmp1 = *(const NetworkGameList**)a;
       
   128 	const NetworkGameList *cmp2 = *(const NetworkGameList**)b;
       
   129 	/* Reverse as per default we are interested in most-clients first */
       
   130 	int r = cmp1->info.clients_on - cmp2->info.clients_on;
       
   131 
       
   132 	if (r == 0) r = cmp1->info.clients_max - cmp2->info.clients_max;
       
   133 	if (r == 0) r = strcasecmp(cmp1->info.server_name, cmp2->info.server_name);
       
   134 
       
   135 	return _internal_sort_order ? -r : r;
       
   136 }
       
   137 
       
   138 /** Qsort function to sort by joinability. If both servers are the
       
   139  * same, prefer the non-passworded server first. */
       
   140 static int CDECL NGameAllowedSorter(const void *a, const void *b)
       
   141 {
       
   142 	const NetworkGameList *cmp1 = *(const NetworkGameList**)a;
       
   143 	const NetworkGameList *cmp2 = *(const NetworkGameList**)b;
       
   144 	/* Reverse default as we are interested in compatible clients first */
       
   145 	int r = cmp2->info.compatible - cmp1->info.compatible;
       
   146 
       
   147 	if (r == 0) r = cmp1->info.use_password - cmp2->info.use_password;
       
   148 	if (r == 0) r = strcasecmp(cmp1->info.server_name, cmp2->info.server_name);
       
   149 
       
   150 	return _internal_sort_order ? -r : r;
       
   151 }
       
   152 
       
   153 /** (Re)build the network game list as its amount has changed because
       
   154  * an item has been added or deleted for example
       
   155  * @param ngl list_d struct that contains all necessary information for sorting */
       
   156 static void BuildNetworkGameList(network_ql_d *nqld)
       
   157 {
       
   158 	NetworkGameList *ngl_temp;
       
   159 	uint n = 0;
       
   160 
       
   161 	if (!(nqld->l.flags & VL_REBUILD)) return;
       
   162 
       
   163 	/* Count the number of games in the list */
       
   164 	for (ngl_temp = _network_game_list; ngl_temp != NULL; ngl_temp = ngl_temp->next) n++;
       
   165 	if (n == 0) return;
       
   166 
       
   167 	/* Create temporary array of games to use for listing */
       
   168 	free(nqld->sort_list);
       
   169 	nqld->sort_list = malloc(n * sizeof(nqld->sort_list[0]));
       
   170 	if (nqld->sort_list == NULL) error("Could not allocate memory for the network-game-sorting-list");
       
   171 	nqld->l.list_length = n;
       
   172 
       
   173 	for (n = 0, ngl_temp = _network_game_list; ngl_temp != NULL; ngl_temp = ngl_temp->next) {
       
   174 		nqld->sort_list[n++] = ngl_temp;
       
   175 	}
       
   176 
       
   177 	/* Force resort */
       
   178 	nqld->l.flags &= ~VL_REBUILD;
       
   179 	nqld->l.flags |= VL_RESORT;
       
   180 }
       
   181 
       
   182 static void SortNetworkGameList(network_ql_d *nqld)
       
   183 {
       
   184 	static NGameNameSortFunction * const ngame_sorter[] = {
       
   185 		&NGameNameSorter,
       
   186 		&NGameClientSorter,
       
   187 		&NGameAllowedSorter
       
   188 	};
       
   189 
       
   190 	NetworkGameList *item;
       
   191 	uint i;
       
   192 
       
   193 	if (!(nqld->l.flags & VL_RESORT)) return;
       
   194 	if (nqld->l.list_length == 0) return;
       
   195 
       
   196 	_internal_sort_order = !!(nqld->l.flags & VL_DESC);
       
   197 	qsort(nqld->sort_list, nqld->l.list_length, sizeof(nqld->sort_list[0]), ngame_sorter[nqld->l.sort_type]);
       
   198 
       
   199 	/* After sorting ngl->sort_list contains the sorted items. Put these back
       
   200 	 * into the original list. Basically nothing has changed, we are only
       
   201 	 * shuffling the ->next pointers */
       
   202 	_network_game_list = nqld->sort_list[0];
       
   203 	for (item = _network_game_list, i = 1; i != nqld->l.list_length; i++) {
       
   204 		item->next = nqld->sort_list[i];
       
   205 		item = item->next;
       
   206 	}
       
   207 	item->next = NULL;
       
   208 
       
   209 	nqld->l.flags &= ~VL_RESORT;
       
   210 }
       
   211 
       
   212 /* Uses network_ql_d (network_d, querystr_d and list_d) WP macro */
       
   213 static void NetworkGameWindowWndProc(Window *w, WindowEvent *e)
       
   214 {
       
   215 	network_d *nd = &WP(w, network_ql_d).n;
       
   216 	list_d *ld = &WP(w, network_ql_d).l;
       
   217 
       
   218 	switch (e->event) {
       
   219 	case WE_CREATE: /* Focus input box */
       
   220 		nd->field = 3;
       
   221 		nd->server = NULL;
       
   222 
       
   223 		WP(w, network_ql_d).sort_list = NULL;
       
   224 		ld->flags = VL_REBUILD | (_ng_sorting.order << (VL_DESC - 1));
       
   225 		ld->sort_type = _ng_sorting.criteria;
       
   226 		break;
       
   227 
       
   228 	case WE_PAINT: {
       
   229 		const NetworkGameList *sel = nd->server;
       
   230 		const char *arrow = (ld->flags & VL_DESC) ? DOWNARROW : UPARROW;
       
   231 
       
   232 		if (ld->flags & VL_REBUILD) {
       
   233 			BuildNetworkGameList(&WP(w, network_ql_d));
       
   234 			SetVScrollCount(w, ld->list_length);
       
   235 		}
       
   236 		if (ld->flags & VL_RESORT) SortNetworkGameList(&WP(w, network_ql_d));
       
   237 
       
   238 		SetWindowWidgetDisabledState(w, 17, sel == NULL);
       
   239 		/* Join Button disabling conditions */
       
   240 		SetWindowWidgetDisabledState(w, 16, sel == NULL || // no Selected Server
       
   241 				!sel->online || // Server offline
       
   242 				sel->info.clients_on >= sel->info.clients_max || // Server full
       
   243 				!sel->info.compatible); // Revision mismatch
       
   244 
       
   245 		SetWindowWidgetHiddenState(w, 18, sel == NULL ||
       
   246 				!sel->online ||
       
   247 				sel->info.grfconfig == NULL);
       
   248 
       
   249 		SetDParam(0, 0x00);
       
   250 		SetDParam(7, _lan_internet_types_dropdown[_network_lan_internet]);
       
   251 		DrawWindowWidgets(w);
       
   252 
       
   253 		DrawEditBox(w, &WP(w, network_ql_d).q, 3);
       
   254 
       
   255 		DrawString(9, 23, STR_NETWORK_CONNECTION, 2);
       
   256 		DrawString(210, 23, STR_NETWORK_PLAYER_NAME, 2);
       
   257 
       
   258 		/* Sort based on widgets: name, clients, compatibility */
       
   259 		switch (ld->sort_type) {
       
   260 			case 6 - 6: DoDrawString(arrow, w->widget[6].right - 10, 42, 0x10); break;
       
   261 			case 7 - 6: DoDrawString(arrow, w->widget[7].right - 10, 42, 0x10); break;
       
   262 			case 8 - 6: DoDrawString(arrow, w->widget[8].right - 10, 42, 0x10); break;
       
   263 		}
       
   264 
       
   265 		{ // draw list of games
       
   266 			uint16 y = NET_PRC__OFFSET_TOP_WIDGET + 3;
       
   267 			int32 n = 0;
       
   268 			int32 pos = w->vscroll.pos;
       
   269 			uint max_name_width = w->widget[6].right - w->widget[6].left - 5;
       
   270 			const NetworkGameList *cur_item = _network_game_list;
       
   271 
       
   272 			while (pos > 0 && cur_item != NULL) {
       
   273 				pos--;
       
   274 				cur_item = cur_item->next;
       
   275 			}
       
   276 
       
   277 			while (cur_item != NULL) {
       
   278 				// show highlighted item with a different colour
       
   279 				if (cur_item == sel) GfxFillRect(w->widget[6].left + 1, y - 2, w->widget[8].right - 1, y + 9, 10);
       
   280 
       
   281 				SetDParamStr(0, cur_item->info.server_name);
       
   282 				DrawStringTruncated(w->widget[6].left + 5, y, STR_02BD, 16, max_name_width);
       
   283 
       
   284 				SetDParam(0, cur_item->info.clients_on);
       
   285 				SetDParam(1, cur_item->info.clients_max);
       
   286 				SetDParam(2, cur_item->info.companies_on);
       
   287 				SetDParam(3, cur_item->info.companies_max);
       
   288 				DrawStringCentered(210, y, STR_NETWORK_GENERAL_ONLINE, 2);
       
   289 
       
   290 				// only draw icons if the server is online
       
   291 				if (cur_item->online) {
       
   292 					// draw a lock if the server is password protected.
       
   293 					if (cur_item->info.use_password) DrawSprite(SPR_LOCK, w->widget[8].left + 5, y - 1);
       
   294 
       
   295 					// draw red or green icon, depending on compatibility with server.
       
   296 					DrawSprite(SPR_BLOT | (cur_item->info.compatible ? PALETTE_TO_GREEN : (cur_item->info.version_compatible ? PALETTE_TO_YELLOW : PALETTE_TO_RED)), w->widget[8].left + 15, y);
       
   297 
       
   298 					// draw flag according to server language
       
   299 					DrawSprite(SPR_FLAGS_BASE + cur_item->info.server_lang, w->widget[8].left + 25, y);
       
   300 				}
       
   301 
       
   302 				cur_item = cur_item->next;
       
   303 				y += NET_PRC__SIZE_OF_ROW;
       
   304 				if (++n == w->vscroll.cap) break; // max number of games in the window
       
   305 			}
       
   306 		}
       
   307 
       
   308 		/* Draw the right menu */
       
   309 		GfxFillRect(311, 43, 539, 92, 157);
       
   310 		if (sel == NULL) {
       
   311 			DrawStringCentered(425, 58, STR_NETWORK_GAME_INFO, 0);
       
   312 		} else if (!sel->online) {
       
   313 			SetDParamStr(0, sel->info.server_name);
       
   314 			DrawStringCentered(425, 68, STR_ORANGE, 0); // game name
       
   315 
       
   316 			DrawStringCentered(425, 132, STR_NETWORK_SERVER_OFFLINE, 0); // server offline
       
   317 		} else { // show game info
       
   318 			uint16 y = 100;
       
   319 			const uint16 x = w->widget[15].left + 5;
       
   320 
       
   321 			DrawStringCentered(425, 48, STR_NETWORK_GAME_INFO, 0);
       
   322 
       
   323 
       
   324 			SetDParamStr(0, sel->info.server_name);
       
   325 			DrawStringCenteredTruncated(w->widget[15].left, w->widget[15].right, 62, STR_ORANGE, 16); // game name
       
   326 
       
   327 			SetDParamStr(0, sel->info.map_name);
       
   328 			DrawStringCenteredTruncated(w->widget[15].left, w->widget[15].right, 74, STR_02BD, 16); // map name
       
   329 
       
   330 			SetDParam(0, sel->info.clients_on);
       
   331 			SetDParam(1, sel->info.clients_max);
       
   332 			SetDParam(2, sel->info.companies_on);
       
   333 			SetDParam(3, sel->info.companies_max);
       
   334 			DrawString(x, y, STR_NETWORK_CLIENTS, 2);
       
   335 			y += 10;
       
   336 
       
   337 			SetDParam(0, _language_dropdown[sel->info.server_lang]);
       
   338 			DrawString(x, y, STR_NETWORK_LANGUAGE, 2); // server language
       
   339 			y += 10;
       
   340 
       
   341 			SetDParam(0, STR_TEMPERATE_LANDSCAPE + sel->info.map_set);
       
   342 			DrawString(x, y, STR_NETWORK_TILESET, 2); // tileset
       
   343 			y += 10;
       
   344 
       
   345 			SetDParam(0, sel->info.map_width);
       
   346 			SetDParam(1, sel->info.map_height);
       
   347 			DrawString(x, y, STR_NETWORK_MAP_SIZE, 2); // map size
       
   348 			y += 10;
       
   349 
       
   350 			SetDParamStr(0, sel->info.server_revision);
       
   351 			DrawString(x, y, STR_NETWORK_SERVER_VERSION, 2); // server version
       
   352 			y += 10;
       
   353 
       
   354 			SetDParamStr(0, sel->info.hostname);
       
   355 			SetDParam(1, sel->port);
       
   356 			DrawString(x, y, STR_NETWORK_SERVER_ADDRESS, 2); // server address
       
   357 			y += 10;
       
   358 
       
   359 			SetDParam(0, sel->info.start_date);
       
   360 			DrawString(x, y, STR_NETWORK_START_DATE, 2); // start date
       
   361 			y += 10;
       
   362 
       
   363 			SetDParam(0, sel->info.game_date);
       
   364 			DrawString(x, y, STR_NETWORK_CURRENT_DATE, 2); // current date
       
   365 			y += 10;
       
   366 
       
   367 			y += 2;
       
   368 
       
   369 			if (!sel->info.compatible) {
       
   370 				DrawStringCentered(425, y, sel->info.version_compatible ? STR_NETWORK_GRF_MISMATCH : STR_NETWORK_VERSION_MISMATCH, 0); // server mismatch
       
   371 			} else if (sel->info.clients_on == sel->info.clients_max) {
       
   372 				// Show: server full, when clients_on == clients_max
       
   373 				DrawStringCentered(425, y, STR_NETWORK_SERVER_FULL, 0); // server full
       
   374 			} else if (sel->info.use_password) {
       
   375 				DrawStringCentered(425, y, STR_NETWORK_PASSWORD, 0); // password warning
       
   376 			}
       
   377 
       
   378 			y += 10;
       
   379 		}
       
   380 	}	break;
       
   381 
       
   382 	case WE_CLICK:
       
   383 		nd->field = e->we.click.widget;
       
   384 		switch (e->we.click.widget) {
       
   385 		case 0: case 14: /* Close 'X' | Cancel button */
       
   386 			DeleteWindowById(WC_NETWORK_WINDOW, 0);
       
   387 			break;
       
   388 		case 4: case 5:
       
   389 			ShowDropDownMenu(w, _lan_internet_types_dropdown, _network_lan_internet, 5, 0, 0); // do it for widget 5
       
   390 			break;
       
   391 		case 6: /* Sort by name */
       
   392 		case 7: /* Sort by connected clients */
       
   393 		case 8: /* Connectivity (green dot) */
       
   394 			if (ld->sort_type == e->we.click.widget - 6) ld->flags ^= VL_DESC;
       
   395 			ld->flags |= VL_RESORT;
       
   396 			ld->sort_type = e->we.click.widget - 6;
       
   397 
       
   398 			_ng_sorting.order = !!(ld->flags & VL_DESC);
       
   399 			_ng_sorting.criteria = ld->sort_type;
       
   400 			SetWindowDirty(w);
       
   401 			break;
       
   402 		case 9: { /* Matrix to show networkgames */
       
   403 			NetworkGameList *cur_item;
       
   404 			uint32 id_v = (e->we.click.pt.y - NET_PRC__OFFSET_TOP_WIDGET) / NET_PRC__SIZE_OF_ROW;
       
   405 
       
   406 			if (id_v >= w->vscroll.cap) return; // click out of bounds
       
   407 			id_v += w->vscroll.pos;
       
   408 
       
   409 			cur_item = _network_game_list;
       
   410 			for (; id_v > 0 && cur_item != NULL; id_v--) cur_item = cur_item->next;
       
   411 
       
   412 			nd->server = cur_item;
       
   413 			SetWindowDirty(w);
       
   414 		} break;
       
   415 		case 11: /* Find server automatically */
       
   416 			switch (_network_lan_internet) {
       
   417 				case 0: NetworkUDPSearchGame(); break;
       
   418 				case 1: NetworkUDPQueryMasterServer(); break;
       
   419 			}
       
   420 			break;
       
   421 		case 12: { // Add a server
       
   422 				ShowQueryString(
       
   423 				BindCString(_network_default_ip),
       
   424 				STR_NETWORK_ENTER_IP,
       
   425 				31 | 0x1000,  // maximum number of characters OR
       
   426 				250, // characters up to this width pixels, whichever is satisfied first
       
   427 				w, CS_ALPHANUMERAL);
       
   428 		} break;
       
   429 		case 13: /* Start server */
       
   430 			ShowNetworkStartServerWindow();
       
   431 			break;
       
   432 		case 16: /* Join Game */
       
   433 			if (nd->server != NULL) {
       
   434 				snprintf(_network_last_host, sizeof(_network_last_host), "%s", inet_ntoa(*(struct in_addr *)&nd->server->ip));
       
   435 				_network_last_port = nd->server->port;
       
   436 				ShowNetworkLobbyWindow(nd->server);
       
   437 			}
       
   438 			break;
       
   439 		case 17: // Refresh
       
   440 			if (nd->server != NULL)
       
   441 				NetworkQueryServer(nd->server->info.hostname, nd->server->port, true);
       
   442 			break;
       
   443 		case 18: // NewGRF Settings
       
   444 			if (nd->server != NULL) ShowNewGRFSettings(false, false, false, &nd->server->info.grfconfig);
       
   445 			break;
       
   446 
       
   447 	}	break;
       
   448 
       
   449 	case WE_DROPDOWN_SELECT: /* we have selected a dropdown item in the list */
       
   450 		switch (e->we.dropdown.button) {
       
   451 			case 5:
       
   452 				_network_lan_internet = e->we.dropdown.index;
       
   453 				break;
       
   454 		}
       
   455 
       
   456 		SetWindowDirty(w);
       
   457 		break;
       
   458 
       
   459 	case WE_MOUSELOOP:
       
   460 		if (nd->field == 3) HandleEditBox(w, &WP(w, network_ql_d).q, 3);
       
   461 		break;
       
   462 
       
   463 	case WE_MESSAGE:
       
   464 		if (e->we.message.msg != 0) nd->server = NULL;
       
   465 		ld->flags |= VL_REBUILD;
       
   466 		SetWindowDirty(w);
       
   467 		break;
       
   468 
       
   469 	case WE_KEYPRESS:
       
   470 		if (nd->field != 3) {
       
   471 			if (nd->server != NULL) {
       
   472 				if (e->we.keypress.keycode == WKC_DELETE) { /* Press 'delete' to remove servers */
       
   473 					NetworkGameListRemoveItem(nd->server);
       
   474 					NetworkRebuildHostList();
       
   475 					nd->server = NULL;
       
   476 				}
       
   477 			}
       
   478 			break;
       
   479 		}
       
   480 
       
   481 		if (HandleEditBoxKey(w, &WP(w, network_ql_d).q, 3, e) == 1) break; // enter pressed
       
   482 
       
   483 		// The name is only allowed when it starts with a letter!
       
   484 		if (_edit_str_buf[0] != '\0' && _edit_str_buf[0] != ' ') {
       
   485 			ttd_strlcpy(_network_player_name, _edit_str_buf, lengthof(_network_player_name));
       
   486 		} else {
       
   487 			ttd_strlcpy(_network_player_name, "Player", lengthof(_network_player_name));
       
   488 		}
       
   489 
       
   490 		break;
       
   491 
       
   492 	case WE_ON_EDIT_TEXT:
       
   493 		NetworkAddServer(e->we.edittext.str);
       
   494 		NetworkRebuildHostList();
       
   495 		break;
       
   496 
       
   497 	case WE_DESTROY: /* Nicely clean up the sort-list */
       
   498 		free(WP(w, network_ql_d).sort_list);
       
   499 		break;
       
   500 	}
       
   501 }
       
   502 
       
   503 static const Widget _network_game_window_widgets[] = {
       
   504 {   WWT_CLOSEBOX,   RESIZE_NONE,   BGC,     0,    10,     0,    13, STR_00C5,                    STR_018B_CLOSE_WINDOW},
       
   505 {    WWT_CAPTION,   RESIZE_NONE,   BGC,    11,   549,     0,    13, STR_NETWORK_MULTIPLAYER,     STR_NULL},
       
   506 {      WWT_PANEL,   RESIZE_NONE,   BGC,     0,   549,    14,   263, 0x0,                         STR_NULL},
       
   507 
       
   508 /* LEFT SIDE */
       
   509 {      WWT_PANEL,   RESIZE_NONE,   BGC,   310,   461,    22,    33, 0x0,                         STR_NETWORK_ENTER_NAME_TIP},
       
   510 
       
   511 {      WWT_INSET,   RESIZE_NONE,   BGC,    90,   181,    22,    33, STR_NETWORK_COMBO1,          STR_NETWORK_CONNECTION_TIP},
       
   512 {    WWT_TEXTBTN,   RESIZE_NONE,   BGC,   170,   180,    23,    32, STR_0225,                    STR_NETWORK_CONNECTION_TIP},
       
   513 
       
   514 { WWT_PUSHTXTBTN,   RESIZE_NONE,   BTC,    10,   170,    42,    53, STR_NETWORK_GAME_NAME,       STR_NETWORK_GAME_NAME_TIP},
       
   515 { WWT_PUSHTXTBTN,   RESIZE_NONE,   BTC,   171,   250,    42,    53, STR_NETWORK_CLIENTS_CAPTION, STR_NETWORK_CLIENTS_CAPTION_TIP},
       
   516 { WWT_PUSHTXTBTN,   RESIZE_NONE,   BTC,   251,   290,    42,    53, STR_EMPTY,                   STR_NETWORK_INFO_ICONS_TIP},
       
   517 
       
   518 {     WWT_MATRIX,   RESIZE_NONE,   BGC,    10,   290,    54,   236, (13 << 8) + 1,               STR_NETWORK_CLICK_GAME_TO_SELECT},
       
   519 {  WWT_SCROLLBAR,   RESIZE_NONE,   BGC,   291,   302,    42,   236, STR_NULL,                    STR_0190_SCROLL_BAR_SCROLLS_LIST},
       
   520 
       
   521 { WWT_PUSHTXTBTN,   RESIZE_NONE,   BTC,    30,   130,   246,   257, STR_NETWORK_FIND_SERVER,     STR_NETWORK_FIND_SERVER_TIP},
       
   522 { WWT_PUSHTXTBTN,   RESIZE_NONE,   BTC,   180,   280,   246,   257, STR_NETWORK_ADD_SERVER,      STR_NETWORK_ADD_SERVER_TIP},
       
   523 
       
   524 /* RIGHT SIDE */
       
   525 { WWT_PUSHTXTBTN,   RESIZE_NONE,   BTC,   315,   415,   246,   257, STR_NETWORK_START_SERVER,    STR_NETWORK_START_SERVER_TIP},
       
   526 { WWT_PUSHTXTBTN,   RESIZE_NONE,   BTC,   430,   535,   246,   257, STR_012E_CANCEL,             STR_NULL},
       
   527 
       
   528 {      WWT_PANEL,   RESIZE_NONE,   BGC,   310,   540,    42,   236, 0x0,                         STR_NULL},
       
   529 
       
   530 { WWT_PUSHTXTBTN,   RESIZE_NONE,   BTC,   315,   415,   215,   226, STR_NETWORK_JOIN_GAME,       STR_NULL},
       
   531 { WWT_PUSHTXTBTN,   RESIZE_NONE,   BTC,   430,   535,   215,   226, STR_NETWORK_REFRESH,         STR_NETWORK_REFRESH_TIP},
       
   532 
       
   533 { WWT_PUSHTXTBTN,   RESIZE_NONE,   BTC,   430,   535,   197,   208, STR_NEWGRF_SETTINGS_BUTTON,  STR_NULL},
       
   534 
       
   535 {   WIDGETS_END},
       
   536 };
       
   537 
       
   538 static const WindowDesc _network_game_window_desc = {
       
   539 	WDP_CENTER, WDP_CENTER, 550, 264,
       
   540 	WC_NETWORK_WINDOW,0,
       
   541 	WDF_STD_TOOLTIPS | WDF_DEF_WIDGET | WDF_UNCLICK_BUTTONS,
       
   542 	_network_game_window_widgets,
       
   543 	NetworkGameWindowWndProc,
       
   544 };
       
   545 
       
   546 void ShowNetworkGameWindow(void)
       
   547 {
       
   548 	static bool first = true;
       
   549 	Window *w;
       
   550 	DeleteWindowById(WC_NETWORK_WINDOW, 0);
       
   551 
       
   552 	/* Only show once */
       
   553 	if (first) {
       
   554 		char* const *srv;
       
   555 
       
   556 		first = false;
       
   557 		// add all servers from the config file to our list
       
   558 		for (srv = &_network_host_list[0]; srv != endof(_network_host_list) && *srv != NULL; srv++) {
       
   559 			NetworkAddServer(*srv);
       
   560 		}
       
   561 
       
   562 		_ng_sorting.criteria = 2; // sort default by collectivity (green-dots on top)
       
   563 		_ng_sorting.order = 0;    // sort ascending by default
       
   564 	}
       
   565 
       
   566 	w = AllocateWindowDesc(&_network_game_window_desc);
       
   567 	if (w != NULL) {
       
   568 		querystr_d *querystr = &WP(w, network_ql_d).q;
       
   569 
       
   570 		ttd_strlcpy(_edit_str_buf, _network_player_name, lengthof(_edit_str_buf));
       
   571 		w->vscroll.cap = 13;
       
   572 
       
   573 		querystr->afilter = CS_ALPHANUMERAL;
       
   574 		InitializeTextBuffer(&querystr->text, _edit_str_buf, lengthof(_edit_str_buf), 120);
       
   575 
       
   576 		UpdateNetworkGameWindow(true);
       
   577 	}
       
   578 }
       
   579 
       
   580 enum {
       
   581 	NSSWND_START = 64,
       
   582 	NSSWND_ROWSIZE = 12
       
   583 };
       
   584 
       
   585 /* Uses network_ql_d (network_d, querystr_d and list_d) WP macro */
       
   586 static void NetworkStartServerWindowWndProc(Window *w, WindowEvent *e)
       
   587 {
       
   588 	network_d *nd = &WP(w, network_ql_d).n;
       
   589 
       
   590 	switch (e->event) {
       
   591 	case WE_CREATE: /* focus input box */
       
   592 		nd->field = 3;
       
   593 		_network_game_info.use_password = (_network_server_password[0] != '\0');
       
   594 		break;
       
   595 
       
   596 	case WE_PAINT: {
       
   597 		int y = NSSWND_START, pos;
       
   598 		const FiosItem *item;
       
   599 
       
   600 		SetDParam( 7, _connection_types_dropdown[_network_advertise]);
       
   601 		SetDParam( 9, _players_dropdown[_network_game_info.clients_max]);
       
   602 		SetDParam(11, _players_dropdown[_network_game_info.companies_max]);
       
   603 		SetDParam(13, _players_dropdown[_network_game_info.spectators_max]);
       
   604 		SetDParam(15, _language_dropdown[_network_game_info.server_lang]);
       
   605 		DrawWindowWidgets(w);
       
   606 
       
   607 		GfxFillRect(11, 63, 258, 215, 0xD7);
       
   608 		DrawEditBox(w, &WP(w, network_ql_d).q, 3);
       
   609 
       
   610 		DrawString(10, 22, STR_NETWORK_NEW_GAME_NAME, 2);
       
   611 
       
   612 		DrawString(10, 43, STR_NETWORK_SELECT_MAP, 2);
       
   613 
       
   614 		DrawString(280,  63, STR_NETWORK_CONNECTION, 2);
       
   615 		DrawString(280,  95, STR_NETWORK_NUMBER_OF_CLIENTS, 2);
       
   616 		DrawString(280, 127, STR_NETWORK_NUMBER_OF_COMPANIES, 2);
       
   617 		DrawString(280, 159, STR_NETWORK_NUMBER_OF_SPECTATORS, 2);
       
   618 		DrawString(280, 191, STR_NETWORK_LANGUAGE_SPOKEN, 2);
       
   619 
       
   620 		if (_network_game_info.use_password) DoDrawString("*", 408, 23, 3);
       
   621 
       
   622 		// draw list of maps
       
   623 		pos = w->vscroll.pos;
       
   624 		while (pos < _fios_num + 1) {
       
   625 			item = _fios_list + pos - 1;
       
   626 			if (item == nd->map || (pos == 0 && nd->map == NULL))
       
   627 				GfxFillRect(11, y - 1, 258, y + 10, 155); // show highlighted item with a different colour
       
   628 
       
   629 			if (pos == 0) {
       
   630 				DrawString(14, y, STR_4010_GENERATE_RANDOM_NEW_GAME, 9);
       
   631 			} else {
       
   632 				DoDrawString(item->title, 14, y, _fios_colors[item->type] );
       
   633 			}
       
   634 			pos++;
       
   635 			y += NSSWND_ROWSIZE;
       
   636 
       
   637 			if (y >= w->vscroll.cap * NSSWND_ROWSIZE + NSSWND_START) break;
       
   638 		}
       
   639 	}	break;
       
   640 
       
   641 	case WE_CLICK:
       
   642 		nd->field = e->we.click.widget;
       
   643 		switch (e->we.click.widget) {
       
   644 		case 0: /* Close 'X' */
       
   645 		case 19: /* Cancel button */
       
   646 			ShowNetworkGameWindow();
       
   647 			break;
       
   648 
       
   649 		case 4: /* Set password button */
       
   650 			ShowQueryString(BindCString(_network_server_password), STR_NETWORK_SET_PASSWORD, 20, 250, w, CS_ALPHANUMERAL);
       
   651 			break;
       
   652 
       
   653 		case 5: { /* Select map */
       
   654 			int y = (e->we.click.pt.y - NSSWND_START) / NSSWND_ROWSIZE;
       
   655 
       
   656 			y += w->vscroll.pos;
       
   657 			if (y >= w->vscroll.count) return;
       
   658 
       
   659 			nd->map = (y == 0) ? NULL : _fios_list + y - 1;
       
   660 			SetWindowDirty(w);
       
   661 			} break;
       
   662 		case 7: case 8: /* Connection type */
       
   663 			ShowDropDownMenu(w, _connection_types_dropdown, _network_advertise, 8, 0, 0); // do it for widget 8
       
   664 			break;
       
   665 		case 9: case 10: /* Number of Players (hide 0 and 1 players) */
       
   666 			ShowDropDownMenu(w, _players_dropdown, _network_game_info.clients_max, 10, 0, 3);
       
   667 			break;
       
   668 		case 11: case 12: /* Number of Companies (hide 0, 9 and 10 companies; max is 8) */
       
   669 			ShowDropDownMenu(w, _players_dropdown, _network_game_info.companies_max, 12, 0, 1537);
       
   670 			break;
       
   671 		case 13: case 14: /* Number of Spectators */
       
   672 			ShowDropDownMenu(w, _players_dropdown, _network_game_info.spectators_max, 14, 0, 0);
       
   673 			break;
       
   674 		case 15: case 16: /* Language */
       
   675 			ShowDropDownMenu(w, _language_dropdown, _network_game_info.server_lang, 16, 0, 0);
       
   676 			break;
       
   677 		case 17: /* Start game */
       
   678 			_is_network_server = true;
       
   679 
       
   680 			if (nd->map == NULL) { // start random new game
       
   681 				ShowGenerateLandscape();
       
   682 			} else { // load a scenario
       
   683 				char *name = FiosBrowseTo(nd->map);
       
   684 				if (name != NULL) {
       
   685 					SetFiosType(nd->map->type);
       
   686 					ttd_strlcpy(_file_to_saveload.name, name, sizeof(_file_to_saveload.name));
       
   687 					ttd_strlcpy(_file_to_saveload.title, nd->map->title, sizeof(_file_to_saveload.title));
       
   688 
       
   689 					DeleteWindow(w);
       
   690 					SwitchMode(SM_START_SCENARIO);
       
   691 				}
       
   692 			}
       
   693 			break;
       
   694 		case 18: /* Load game */
       
   695 			_is_network_server = true;
       
   696 			/* XXX - WC_NETWORK_WINDOW should stay, but if it stays, it gets
       
   697 			 * copied all the elements of 'load game' and upon closing that, it segfaults */
       
   698 			DeleteWindowById(WC_NETWORK_WINDOW, 0);
       
   699 			ShowSaveLoadDialog(SLD_LOAD_GAME);
       
   700 			break;
       
   701 		}
       
   702 		break;
       
   703 
       
   704 	case WE_DROPDOWN_SELECT: /* we have selected a dropdown item in the list */
       
   705 		switch (e->we.dropdown.button) {
       
   706 			case  8: _network_advertise                = (e->we.dropdown.index != 0); break;
       
   707 			case 10: _network_game_info.clients_max    = e->we.dropdown.index;        break;
       
   708 			case 12: _network_game_info.companies_max  = e->we.dropdown.index;        break;
       
   709 			case 14: _network_game_info.spectators_max = e->we.dropdown.index;        break;
       
   710 			case 16: _network_game_info.server_lang    = e->we.dropdown.index;        break;
       
   711 		}
       
   712 
       
   713 		SetWindowDirty(w);
       
   714 		break;
       
   715 
       
   716 	case WE_MOUSELOOP:
       
   717 		if (nd->field == 3) HandleEditBox(w, &WP(w, network_ql_d).q, 3);
       
   718 		break;
       
   719 
       
   720 	case WE_KEYPRESS:
       
   721 		if (nd->field == 3) {
       
   722 			if (HandleEditBoxKey(w, &WP(w, network_ql_d).q, 3, e) == 1) break; // enter pressed
       
   723 
       
   724 			ttd_strlcpy(_network_server_name, WP(w, network_ql_d).q.text.buf, sizeof(_network_server_name));
       
   725 			UpdateTextBufferSize(&WP(w, network_ql_d).q.text);
       
   726 		}
       
   727 		break;
       
   728 
       
   729 	case WE_ON_EDIT_TEXT: {
       
   730 		ttd_strlcpy(_network_server_password, e->we.edittext.str, lengthof(_network_server_password));
       
   731 		_network_game_info.use_password = (_network_server_password[0] != '\0');
       
   732 		SetWindowDirty(w);
       
   733 	} break;
       
   734 	}
       
   735 }
       
   736 
       
   737 static const Widget _network_start_server_window_widgets[] = {
       
   738 {   WWT_CLOSEBOX,   RESIZE_NONE,   BGC,     0,    10,     0,    13, STR_00C5,                      STR_018B_CLOSE_WINDOW },
       
   739 {    WWT_CAPTION,   RESIZE_NONE,   BGC,    11,   419,     0,    13, STR_NETWORK_START_GAME_WINDOW, STR_NULL},
       
   740 {      WWT_PANEL,   RESIZE_NONE,   BGC,     0,   419,    14,   243, 0x0,                           STR_NULL},
       
   741 
       
   742 {      WWT_PANEL,   RESIZE_NONE,   BGC,   100,   272,    22,    33, 0x0,                           STR_NETWORK_NEW_GAME_NAME_TIP},
       
   743 { WWT_PUSHTXTBTN,   RESIZE_NONE,   BTC,   285,   405,    22,    33, STR_NETWORK_SET_PASSWORD,      STR_NETWORK_PASSWORD_TIP},
       
   744 
       
   745 {      WWT_INSET,   RESIZE_NONE,   BGC,    10,   271,    62,   216, 0x0,                           STR_NETWORK_SELECT_MAP_TIP},
       
   746 {  WWT_SCROLLBAR,   RESIZE_NONE,   BGC,   259,   270,    63,   215, 0x0,                           STR_0190_SCROLL_BAR_SCROLLS_LIST},
       
   747 /* Combo boxes to control Connection Type / Max Clients / Max Companies / Max Observers / Language */
       
   748 {      WWT_INSET,   RESIZE_NONE,   BGC,   280,   410,    77,    88, STR_NETWORK_COMBO1,            STR_NETWORK_CONNECTION_TIP},
       
   749 {    WWT_TEXTBTN,   RESIZE_NONE,   BGC,   399,   409,    78,    87, STR_0225,                      STR_NETWORK_CONNECTION_TIP},
       
   750 {      WWT_INSET,   RESIZE_NONE,   BGC,   280,   410,   109,   120, STR_NETWORK_COMBO2,            STR_NETWORK_NUMBER_OF_CLIENTS_TIP},
       
   751 {    WWT_TEXTBTN,   RESIZE_NONE,   BGC,   399,   409,   110,   119, STR_0225,                      STR_NETWORK_NUMBER_OF_CLIENTS_TIP},
       
   752 {      WWT_INSET,   RESIZE_NONE,   BGC,   280,   410,   141,   152, STR_NETWORK_COMBO3,            STR_NETWORK_NUMBER_OF_COMPANIES_TIP},
       
   753 {    WWT_TEXTBTN,   RESIZE_NONE,   BGC,   399,   409,   142,   151, STR_0225,                      STR_NETWORK_NUMBER_OF_COMPANIES_TIP},
       
   754 {      WWT_INSET,   RESIZE_NONE,   BGC,   280,   410,   173,   184, STR_NETWORK_COMBO4,            STR_NETWORK_NUMBER_OF_SPECTATORS_TIP},
       
   755 {    WWT_TEXTBTN,   RESIZE_NONE,   BGC,   399,   409,   174,   183, STR_0225,                      STR_NETWORK_NUMBER_OF_SPECTATORS_TIP},
       
   756 {      WWT_INSET,   RESIZE_NONE,   BGC,   280,   410,   205,   216, STR_NETWORK_COMBO5,            STR_NETWORK_LANGUAGE_TIP},
       
   757 {    WWT_TEXTBTN,   RESIZE_NONE,   BGC,   399,   409,   206,   215, STR_0225,                      STR_NETWORK_LANGUAGE_TIP},
       
   758 
       
   759 { WWT_PUSHTXTBTN,   RESIZE_NONE,   BTC,    40,   140,   224,   235, STR_NETWORK_START_GAME,        STR_NETWORK_START_GAME_TIP},
       
   760 { WWT_PUSHTXTBTN,   RESIZE_NONE,   BTC,   150,   250,   224,   235, STR_NETWORK_LOAD_GAME,         STR_NETWORK_LOAD_GAME_TIP},
       
   761 { WWT_PUSHTXTBTN,   RESIZE_NONE,   BTC,   260,   360,   224,   235, STR_012E_CANCEL,               STR_NULL},
       
   762 {   WIDGETS_END},
       
   763 };
       
   764 
       
   765 static const WindowDesc _network_start_server_window_desc = {
       
   766 	WDP_CENTER, WDP_CENTER, 420, 244,
       
   767 	WC_NETWORK_WINDOW,0,
       
   768 	WDF_STD_TOOLTIPS | WDF_DEF_WIDGET | WDF_UNCLICK_BUTTONS,
       
   769 	_network_start_server_window_widgets,
       
   770 	NetworkStartServerWindowWndProc,
       
   771 };
       
   772 
       
   773 static void ShowNetworkStartServerWindow(void)
       
   774 {
       
   775 	Window *w;
       
   776 	DeleteWindowById(WC_NETWORK_WINDOW, 0);
       
   777 
       
   778 	w = AllocateWindowDesc(&_network_start_server_window_desc);
       
   779 	ttd_strlcpy(_edit_str_buf, _network_server_name, lengthof(_edit_str_buf));
       
   780 
       
   781 	_saveload_mode = SLD_NEW_GAME;
       
   782 	BuildFileList();
       
   783 	w->vscroll.cap = 12;
       
   784 	w->vscroll.count = _fios_num+1;
       
   785 
       
   786 	WP(w, network_ql_d).q.afilter = CS_ALPHANUMERAL;
       
   787 	InitializeTextBuffer(&WP(w, network_ql_d).q.text, _edit_str_buf, lengthof(_edit_str_buf), 160);
       
   788 }
       
   789 
       
   790 static byte NetworkLobbyFindCompanyIndex(byte pos)
       
   791 {
       
   792 	byte i;
       
   793 
       
   794 	/* Scroll through all _network_player_info and get the 'pos' item
       
   795 	    that is not empty */
       
   796 	for (i = 0; i < MAX_PLAYERS; i++) {
       
   797 		if (_network_player_info[i].company_name[0] != '\0') {
       
   798 			if (pos-- == 0) return i;
       
   799 		}
       
   800 	}
       
   801 
       
   802 	return 0;
       
   803 }
       
   804 
       
   805 /* uses network_d WP macro */
       
   806 static void NetworkLobbyWindowWndProc(Window *w, WindowEvent *e)
       
   807 {
       
   808 	network_d *nd = &WP(w, network_d);
       
   809 
       
   810 	switch (e->event) {
       
   811 	case WE_CREATE:
       
   812 		nd->company = (byte)-1;
       
   813 		break;
       
   814 
       
   815 	case WE_PAINT: {
       
   816 		const NetworkGameInfo *gi = &nd->server->info;
       
   817 		int y = NET_PRC__OFFSET_TOP_WIDGET_COMPANY, pos;
       
   818 
       
   819 		SetWindowWidgetDisabledState(w, 7, nd->company == (byte)-1);
       
   820 		SetWindowWidgetDisabledState(w, 8, gi->companies_on >= gi->companies_max);
       
   821 		/* You can not join a server as spectator when it has no companies active..
       
   822 		 * it causes some nasty crashes */
       
   823 		SetWindowWidgetDisabledState(w, 9, gi->spectators_on >= gi->spectators_max ||
       
   824 				gi->companies_on == 0);
       
   825 
       
   826 		DrawWindowWidgets(w);
       
   827 
       
   828 		SetDParamStr(0, gi->server_name);
       
   829 		DrawString(10, 22, STR_NETWORK_PREPARE_TO_JOIN, 2);
       
   830 
       
   831 		/* Draw company list */
       
   832 		pos = w->vscroll.pos;
       
   833 		while (pos < gi->companies_on) {
       
   834 			byte company = NetworkLobbyFindCompanyIndex(pos);
       
   835 			bool income = false;
       
   836 			if (nd->company == company)
       
   837 				GfxFillRect(11, y - 1, 154, y + 10, 10); // show highlighted item with a different colour
       
   838 
       
   839 			DoDrawStringTruncated(_network_player_info[company].company_name, 13, y, 16, 135 - 13);
       
   840 			if (_network_player_info[company].use_password != 0) DrawSprite(SPR_LOCK, 135, y);
       
   841 
       
   842 			/* If the company's income was positive puts a green dot else a red dot */
       
   843 			if (_network_player_info[company].income >= 0) income = true;
       
   844 			DrawSprite(SPR_BLOT | (income ? PALETTE_TO_GREEN : PALETTE_TO_RED), 145, y);
       
   845 
       
   846 			pos++;
       
   847 			y += NET_PRC__SIZE_OF_ROW;
       
   848 			if (pos >= w->vscroll.cap) break;
       
   849 		}
       
   850 
       
   851 		/* Draw info about selected company when it is selected in the left window */
       
   852 		GfxFillRect(174, 39, 403, 75, 157);
       
   853 		DrawStringCentered(290, 50, STR_NETWORK_COMPANY_INFO, 0);
       
   854 		if (nd->company != (byte)-1) {
       
   855 			const uint x = 183;
       
   856 			const uint trunc_width = w->widget[6].right - x;
       
   857 			y = 80;
       
   858 
       
   859 			SetDParam(0, nd->server->info.clients_on);
       
   860 			SetDParam(1, nd->server->info.clients_max);
       
   861 			SetDParam(2, nd->server->info.companies_on);
       
   862 			SetDParam(3, nd->server->info.companies_max);
       
   863 			DrawString(x, y, STR_NETWORK_CLIENTS, 2);
       
   864 			y += 10;
       
   865 
       
   866 			SetDParamStr(0, _network_player_info[nd->company].company_name);
       
   867 			DrawStringTruncated(x, y, STR_NETWORK_COMPANY_NAME, 2, trunc_width);
       
   868 			y += 10;
       
   869 
       
   870 			SetDParam(0, _network_player_info[nd->company].inaugurated_year);
       
   871 			DrawString(x, y, STR_NETWORK_INAUGURATION_YEAR, 2); // inauguration year
       
   872 			y += 10;
       
   873 
       
   874 			SetDParam64(0, _network_player_info[nd->company].company_value);
       
   875 			DrawString(x, y, STR_NETWORK_VALUE, 2); // company value
       
   876 			y += 10;
       
   877 
       
   878 			SetDParam64(0, _network_player_info[nd->company].money);
       
   879 			DrawString(x, y, STR_NETWORK_CURRENT_BALANCE, 2); // current balance
       
   880 			y += 10;
       
   881 
       
   882 			SetDParam64(0, _network_player_info[nd->company].income);
       
   883 			DrawString(x, y, STR_NETWORK_LAST_YEARS_INCOME, 2); // last year's income
       
   884 			y += 10;
       
   885 
       
   886 			SetDParam(0, _network_player_info[nd->company].performance);
       
   887 			DrawString(x, y, STR_NETWORK_PERFORMANCE, 2); // performance
       
   888 			y += 10;
       
   889 
       
   890 			SetDParam(0, _network_player_info[nd->company].num_vehicle[0]);
       
   891 			SetDParam(1, _network_player_info[nd->company].num_vehicle[1]);
       
   892 			SetDParam(2, _network_player_info[nd->company].num_vehicle[2]);
       
   893 			SetDParam(3, _network_player_info[nd->company].num_vehicle[3]);
       
   894 			SetDParam(4, _network_player_info[nd->company].num_vehicle[4]);
       
   895 			DrawString(x, y, STR_NETWORK_VEHICLES, 2); // vehicles
       
   896 			y += 10;
       
   897 
       
   898 			SetDParam(0, _network_player_info[nd->company].num_station[0]);
       
   899 			SetDParam(1, _network_player_info[nd->company].num_station[1]);
       
   900 			SetDParam(2, _network_player_info[nd->company].num_station[2]);
       
   901 			SetDParam(3, _network_player_info[nd->company].num_station[3]);
       
   902 			SetDParam(4, _network_player_info[nd->company].num_station[4]);
       
   903 			DrawString(x, y, STR_NETWORK_STATIONS, 2); // stations
       
   904 			y += 10;
       
   905 
       
   906 			SetDParamStr(0, _network_player_info[nd->company].players);
       
   907 			DrawStringTruncated(x, y, STR_NETWORK_PLAYERS, 2, trunc_width); // players
       
   908 		}
       
   909 	}	break;
       
   910 
       
   911 	case WE_CLICK:
       
   912 		switch (e->we.click.widget) {
       
   913 		case 0: case 11: /* Close 'X' | Cancel button */
       
   914 			ShowNetworkGameWindow();
       
   915 			break;
       
   916 		case 4: { /* Company list */
       
   917 			uint32 id_v = (e->we.click.pt.y - NET_PRC__OFFSET_TOP_WIDGET_COMPANY) / NET_PRC__SIZE_OF_ROW;
       
   918 
       
   919 			if (id_v >= w->vscroll.cap) return;
       
   920 
       
   921 			id_v += w->vscroll.pos;
       
   922 			nd->company = (id_v >= nd->server->info.companies_on) ? (byte)-1 : NetworkLobbyFindCompanyIndex(id_v);
       
   923 			SetWindowDirty(w);
       
   924 		}	break;
       
   925 		case 7: /* Join company */
       
   926 			if (nd->company != (byte)-1) {
       
   927 				_network_playas = nd->company;
       
   928 				NetworkClientConnectGame(_network_last_host, _network_last_port);
       
   929 			}
       
   930 			break;
       
   931 		case 8: /* New company */
       
   932 			_network_playas = PLAYER_NEW_COMPANY;
       
   933 			NetworkClientConnectGame(_network_last_host, _network_last_port);
       
   934 			break;
       
   935 		case 9: /* Spectate game */
       
   936 			_network_playas = PLAYER_SPECTATOR;
       
   937 			NetworkClientConnectGame(_network_last_host, _network_last_port);
       
   938 			break;
       
   939 		case 10: /* Refresh */
       
   940 			NetworkQueryServer(_network_last_host, _network_last_port, false); // company info
       
   941 			NetworkUDPQueryServer(_network_last_host, _network_last_port);     // general data
       
   942 			break;
       
   943 		}	break;
       
   944 
       
   945 	case WE_MESSAGE:
       
   946 		SetWindowDirty(w);
       
   947 		break;
       
   948 	}
       
   949 }
       
   950 
       
   951 static const Widget _network_lobby_window_widgets[] = {
       
   952 {   WWT_CLOSEBOX,   RESIZE_NONE,   BGC,     0,    10,     0,    13, STR_00C5,                  STR_018B_CLOSE_WINDOW },
       
   953 {    WWT_CAPTION,   RESIZE_NONE,   BGC,    11,   419,     0,    13, STR_NETWORK_GAME_LOBBY,    STR_NULL},
       
   954 {      WWT_PANEL,   RESIZE_NONE,   BGC,     0,   419,    14,   234, 0x0,                       STR_NULL},
       
   955 
       
   956 // company list
       
   957 {      WWT_PANEL,   RESIZE_NONE,   BTC,    10,   155,    38,    49, 0x0,                       STR_NULL},
       
   958 {     WWT_MATRIX,   RESIZE_NONE,   BGC,    10,   155,    50,   190, (10 << 8) + 1,             STR_NETWORK_COMPANY_LIST_TIP},
       
   959 {  WWT_SCROLLBAR,   RESIZE_NONE,   BGC,   156,   167,    38,   190, STR_NULL,                  STR_0190_SCROLL_BAR_SCROLLS_LIST},
       
   960 
       
   961 // company/player info
       
   962 {      WWT_PANEL,   RESIZE_NONE,   BGC,   173,   404,    38,   190, 0x0,                       STR_NULL},
       
   963 
       
   964 // buttons
       
   965 { WWT_PUSHTXTBTN,   RESIZE_NONE,   BTC,    10,   151,   200,   211, STR_NETWORK_JOIN_COMPANY,  STR_NETWORK_JOIN_COMPANY_TIP},
       
   966 { WWT_PUSHTXTBTN,   RESIZE_NONE,   BTC,    10,   151,   215,   226, STR_NETWORK_NEW_COMPANY,   STR_NETWORK_NEW_COMPANY_TIP},
       
   967 { WWT_PUSHTXTBTN,   RESIZE_NONE,   BTC,   158,   268,   200,   211, STR_NETWORK_SPECTATE_GAME, STR_NETWORK_SPECTATE_GAME_TIP},
       
   968 { WWT_PUSHTXTBTN,   RESIZE_NONE,   BTC,   158,   268,   215,   226, STR_NETWORK_REFRESH,       STR_NETWORK_REFRESH_TIP},
       
   969 { WWT_PUSHTXTBTN,   RESIZE_NONE,   BTC,   278,   388,   200,   211, STR_012E_CANCEL,           STR_NULL},
       
   970 
       
   971 {   WIDGETS_END},
       
   972 };
       
   973 
       
   974 static const WindowDesc _network_lobby_window_desc = {
       
   975 	WDP_CENTER, WDP_CENTER, 420, 235,
       
   976 	WC_NETWORK_WINDOW,0,
       
   977 	WDF_STD_TOOLTIPS | WDF_DEF_WIDGET | WDF_UNCLICK_BUTTONS,
       
   978 	_network_lobby_window_widgets,
       
   979 	NetworkLobbyWindowWndProc,
       
   980 };
       
   981 
       
   982 /* Show the networklobbywindow with the selected server
       
   983  * @param ngl Selected game pointer which is passed to the new window */
       
   984 static void ShowNetworkLobbyWindow(NetworkGameList *ngl)
       
   985 {
       
   986 	Window *w;
       
   987 	DeleteWindowById(WC_NETWORK_WINDOW, 0);
       
   988 
       
   989 	NetworkQueryServer(_network_last_host, _network_last_port, false); // company info
       
   990 	NetworkUDPQueryServer(_network_last_host, _network_last_port);     // general data
       
   991 
       
   992 	w = AllocateWindowDesc(&_network_lobby_window_desc);
       
   993 	if (w != NULL) {
       
   994 		WP(w, network_ql_d).n.server = ngl;
       
   995 		strcpy(_edit_str_buf, "");
       
   996 		w->vscroll.cap = 10;
       
   997 	}
       
   998 }
       
   999 
       
  1000 // The window below gives information about the connected clients
       
  1001 //  and also makes able to give money to them, kick them (if server)
       
  1002 //  and stuff like that.
       
  1003 
       
  1004 extern void DrawPlayerIcon(PlayerID pid, int x, int y);
       
  1005 
       
  1006 // Every action must be of this form
       
  1007 typedef void ClientList_Action_Proc(byte client_no);
       
  1008 
       
  1009 // Max 10 actions per client
       
  1010 #define MAX_CLIENTLIST_ACTION 10
       
  1011 
       
  1012 // Some standard bullshit.. defines variables ;)
       
  1013 static void ClientListWndProc(Window *w, WindowEvent *e);
       
  1014 static void ClientListPopupWndProc(Window *w, WindowEvent *e);
       
  1015 static byte _selected_clientlist_item = 255;
       
  1016 static byte _selected_clientlist_y = 0;
       
  1017 static char _clientlist_action[MAX_CLIENTLIST_ACTION][50];
       
  1018 static ClientList_Action_Proc *_clientlist_proc[MAX_CLIENTLIST_ACTION];
       
  1019 
       
  1020 enum {
       
  1021 	CLNWND_OFFSET = 16,
       
  1022 	CLNWND_ROWSIZE = 10
       
  1023 };
       
  1024 
       
  1025 static const Widget _client_list_widgets[] = {
       
  1026 {   WWT_CLOSEBOX,   RESIZE_NONE,    14,     0,    10,     0,    13, STR_00C5,                 STR_018B_CLOSE_WINDOW},
       
  1027 {    WWT_CAPTION,   RESIZE_NONE,    14,    11,   249,     0,    13, STR_NETWORK_CLIENT_LIST,  STR_018C_WINDOW_TITLE_DRAG_THIS},
       
  1028 
       
  1029 {      WWT_PANEL,   RESIZE_NONE,    14,     0,   249,    14,    14 + CLNWND_ROWSIZE + 1, 0x0, STR_NULL},
       
  1030 {   WIDGETS_END},
       
  1031 };
       
  1032 
       
  1033 static const Widget _client_list_popup_widgets[] = {
       
  1034 {      WWT_PANEL,   RESIZE_NONE,    14,     0,   99,     0,     0,     0, STR_NULL},
       
  1035 {   WIDGETS_END},
       
  1036 };
       
  1037 
       
  1038 static WindowDesc _client_list_desc = {
       
  1039 	WDP_AUTO, WDP_AUTO, 250, 1,
       
  1040 	WC_CLIENT_LIST,0,
       
  1041 	WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET,
       
  1042 	_client_list_widgets,
       
  1043 	ClientListWndProc
       
  1044 };
       
  1045 
       
  1046 // Finds the Xth client-info that is active
       
  1047 static const NetworkClientInfo *NetworkFindClientInfo(byte client_no)
       
  1048 {
       
  1049 	const NetworkClientInfo *ci;
       
  1050 
       
  1051 	FOR_ALL_ACTIVE_CLIENT_INFOS(ci) {
       
  1052 		if (client_no == 0) return ci;
       
  1053 		client_no--;
       
  1054 	}
       
  1055 
       
  1056 	return NULL;
       
  1057 }
       
  1058 
       
  1059 // Here we start to define the options out of the menu
       
  1060 static void ClientList_Kick(byte client_no)
       
  1061 {
       
  1062 	if (client_no < MAX_PLAYERS)
       
  1063 		SEND_COMMAND(PACKET_SERVER_ERROR)(DEREF_CLIENT(client_no), NETWORK_ERROR_KICKED);
       
  1064 }
       
  1065 
       
  1066 static void ClientList_Ban(byte client_no)
       
  1067 {
       
  1068 	uint i;
       
  1069 	uint32 ip = NetworkFindClientInfo(client_no)->client_ip;
       
  1070 
       
  1071 	for (i = 0; i < lengthof(_network_ban_list); i++) {
       
  1072 		if (_network_ban_list[i] == NULL) {
       
  1073 			_network_ban_list[i] = strdup(inet_ntoa(*(struct in_addr *)&ip));
       
  1074 			break;
       
  1075 		}
       
  1076 	}
       
  1077 
       
  1078 	if (client_no < MAX_PLAYERS)
       
  1079 		SEND_COMMAND(PACKET_SERVER_ERROR)(DEREF_CLIENT(client_no), NETWORK_ERROR_KICKED);
       
  1080 }
       
  1081 
       
  1082 static void ClientList_GiveMoney(byte client_no)
       
  1083 {
       
  1084 	if (NetworkFindClientInfo(client_no) != NULL)
       
  1085 		ShowNetworkGiveMoneyWindow(NetworkFindClientInfo(client_no)->client_playas);
       
  1086 }
       
  1087 
       
  1088 static void ClientList_SpeakToClient(byte client_no)
       
  1089 {
       
  1090 	if (NetworkFindClientInfo(client_no) != NULL)
       
  1091 		ShowNetworkChatQueryWindow(DESTTYPE_CLIENT, NetworkFindClientInfo(client_no)->client_index);
       
  1092 }
       
  1093 
       
  1094 static void ClientList_SpeakToCompany(byte client_no)
       
  1095 {
       
  1096 	if (NetworkFindClientInfo(client_no) != NULL)
       
  1097 		ShowNetworkChatQueryWindow(DESTTYPE_TEAM, NetworkFindClientInfo(client_no)->client_playas);
       
  1098 }
       
  1099 
       
  1100 static void ClientList_SpeakToAll(byte client_no)
       
  1101 {
       
  1102 	ShowNetworkChatQueryWindow(DESTTYPE_BROADCAST, 0);
       
  1103 }
       
  1104 
       
  1105 static void ClientList_None(byte client_no)
       
  1106 {
       
  1107 	// No action ;)
       
  1108 }
       
  1109 
       
  1110 
       
  1111 
       
  1112 // Help, a action is clicked! What do we do?
       
  1113 static void HandleClientListPopupClick(byte index, byte clientno) {
       
  1114 	// A click on the Popup of the ClientList.. handle the command
       
  1115 	if (index < MAX_CLIENTLIST_ACTION && _clientlist_proc[index] != NULL) {
       
  1116 		_clientlist_proc[index](clientno);
       
  1117 	}
       
  1118 }
       
  1119 
       
  1120 // Finds the amount of clients and set the height correct
       
  1121 static bool CheckClientListHeight(Window *w)
       
  1122 {
       
  1123 	int num = 0;
       
  1124 	const NetworkClientInfo *ci;
       
  1125 
       
  1126 	// Should be replaced with a loop through all clients
       
  1127 	FOR_ALL_ACTIVE_CLIENT_INFOS(ci) {
       
  1128 		num++;
       
  1129 	}
       
  1130 
       
  1131 	num *= CLNWND_ROWSIZE;
       
  1132 
       
  1133 	// If height is changed
       
  1134 	if (w->height != CLNWND_OFFSET + num + 1) {
       
  1135 		// XXX - magic unfortunately; (num + 2) has to be one bigger than heigh (num + 1)
       
  1136 		SetWindowDirty(w);
       
  1137 		w->widget[2].bottom = w->widget[2].top + num + 2;
       
  1138 		w->height = CLNWND_OFFSET + num + 1;
       
  1139 		SetWindowDirty(w);
       
  1140 		return false;
       
  1141 	}
       
  1142 	return true;
       
  1143 }
       
  1144 
       
  1145 // Finds the amount of actions in the popup and set the height correct
       
  1146 static uint ClientListPopupHeigth(void) {
       
  1147 	int i, num = 0;
       
  1148 
       
  1149 	// Find the amount of actions
       
  1150 	for (i = 0; i < MAX_CLIENTLIST_ACTION; i++) {
       
  1151 		if (_clientlist_action[i][0] == '\0') continue;
       
  1152 		if (_clientlist_proc[i] == NULL) continue;
       
  1153 		num++;
       
  1154 	}
       
  1155 
       
  1156 	num *= CLNWND_ROWSIZE;
       
  1157 
       
  1158 	return num + 1;
       
  1159 }
       
  1160 
       
  1161 // Show the popup (action list)
       
  1162 static Window *PopupClientList(Window *w, int client_no, int x, int y)
       
  1163 {
       
  1164 	int i, h;
       
  1165 	const NetworkClientInfo *ci;
       
  1166 	DeleteWindowById(WC_TOOLBAR_MENU, 0);
       
  1167 
       
  1168 	// Clean the current actions
       
  1169 	for (i = 0; i < MAX_CLIENTLIST_ACTION; i++) {
       
  1170 		_clientlist_action[i][0] = '\0';
       
  1171 		_clientlist_proc[i] = NULL;
       
  1172 	}
       
  1173 
       
  1174 	// Fill the actions this client has
       
  1175 	// Watch is, max 50 chars long!
       
  1176 
       
  1177 	ci = NetworkFindClientInfo(client_no);
       
  1178 	if (ci == NULL) return NULL;
       
  1179 
       
  1180 	i = 0;
       
  1181 	if (_network_own_client_index != ci->client_index) {
       
  1182 		GetString(_clientlist_action[i], STR_NETWORK_CLIENTLIST_SPEAK_TO_CLIENT, lastof(_clientlist_action[i]));
       
  1183 		_clientlist_proc[i++] = &ClientList_SpeakToClient;
       
  1184 	}
       
  1185 
       
  1186 	if (IsValidPlayer(ci->client_playas) || ci->client_playas == PLAYER_SPECTATOR) {
       
  1187 		GetString(_clientlist_action[i], STR_NETWORK_CLIENTLIST_SPEAK_TO_COMPANY, lastof(_clientlist_action[i]));
       
  1188 		_clientlist_proc[i++] = &ClientList_SpeakToCompany;
       
  1189 	}
       
  1190 	GetString(_clientlist_action[i], STR_NETWORK_CLIENTLIST_SPEAK_TO_ALL, lastof(_clientlist_action[i]));
       
  1191 	_clientlist_proc[i++] = &ClientList_SpeakToAll;
       
  1192 
       
  1193 	if (_network_own_client_index != ci->client_index) {
       
  1194 		/* We are no spectator and the player we want to give money to is no spectator */
       
  1195 		if (IsValidPlayer(_network_playas) && IsValidPlayer(ci->client_playas)) {
       
  1196 			GetString(_clientlist_action[i], STR_NETWORK_CLIENTLIST_GIVE_MONEY, lastof(_clientlist_action[i]));
       
  1197 			_clientlist_proc[i++] = &ClientList_GiveMoney;
       
  1198 		}
       
  1199 	}
       
  1200 
       
  1201 	// A server can kick clients (but not himself)
       
  1202 	if (_network_server && _network_own_client_index != ci->client_index) {
       
  1203 		GetString(_clientlist_action[i], STR_NETWORK_CLIENTLIST_KICK, lastof(_clientlist_action[i]));
       
  1204 		_clientlist_proc[i++] = &ClientList_Kick;
       
  1205 
       
  1206 		sprintf(_clientlist_action[i],"Ban"); // XXX GetString?
       
  1207 		_clientlist_proc[i++] = &ClientList_Ban;
       
  1208 	}
       
  1209 
       
  1210 	if (i == 0) {
       
  1211 		GetString(_clientlist_action[i], STR_NETWORK_CLIENTLIST_NONE, lastof(_clientlist_action[i]));
       
  1212 		_clientlist_proc[i++] = &ClientList_None;
       
  1213 	}
       
  1214 
       
  1215 	/* Calculate the height */
       
  1216 	h = ClientListPopupHeigth();
       
  1217 
       
  1218 	// Allocate the popup
       
  1219 	w = AllocateWindow(x, y, 150, h + 1, ClientListPopupWndProc, WC_TOOLBAR_MENU, _client_list_popup_widgets);
       
  1220 	w->widget[0].bottom = w->widget[0].top + h;
       
  1221 	w->widget[0].right = w->widget[0].left + 150;
       
  1222 
       
  1223 	w->flags4 &= ~WF_WHITE_BORDER_MASK;
       
  1224 	WP(w,menu_d).item_count = 0;
       
  1225 	// Save our client
       
  1226 	WP(w,menu_d).main_button = client_no;
       
  1227 	WP(w,menu_d).sel_index = 0;
       
  1228 	// We are a popup
       
  1229 	_popup_menu_active = true;
       
  1230 
       
  1231 	return w;
       
  1232 }
       
  1233 
       
  1234 /** Main handle for the client popup list
       
  1235  * uses menu_d WP macro */
       
  1236 static void ClientListPopupWndProc(Window *w, WindowEvent *e)
       
  1237 {
       
  1238 	switch (e->event) {
       
  1239 	case WE_PAINT: {
       
  1240 		int i, y, sel;
       
  1241 		byte colour;
       
  1242 		DrawWindowWidgets(w);
       
  1243 
       
  1244 		// Draw the actions
       
  1245 		sel = WP(w,menu_d).sel_index;
       
  1246 		y = 1;
       
  1247 		for (i = 0; i < MAX_CLIENTLIST_ACTION; i++, y += CLNWND_ROWSIZE) {
       
  1248 			if (_clientlist_action[i][0] == '\0') continue;
       
  1249 			if (_clientlist_proc[i] == NULL) continue;
       
  1250 
       
  1251 			if (sel-- == 0) { // Selected item, highlight it
       
  1252 				GfxFillRect(1, y, 150 - 2, y + CLNWND_ROWSIZE - 1, 0);
       
  1253 				colour = 0xC;
       
  1254 			} else {
       
  1255 				colour = 0x10;
       
  1256 			}
       
  1257 
       
  1258 			DoDrawString(_clientlist_action[i], 4, y, colour);
       
  1259 		}
       
  1260 	}	break;
       
  1261 
       
  1262 	case WE_POPUPMENU_SELECT: {
       
  1263 		// We selected an action
       
  1264 		int index = (e->we.popupmenu.pt.y - w->top) / CLNWND_ROWSIZE;
       
  1265 
       
  1266 		if (index >= 0 && e->we.popupmenu.pt.y >= w->top)
       
  1267 			HandleClientListPopupClick(index, WP(w,menu_d).main_button);
       
  1268 
       
  1269 		DeleteWindowById(WC_TOOLBAR_MENU, 0);
       
  1270 	}	break;
       
  1271 
       
  1272 	case WE_POPUPMENU_OVER: {
       
  1273 		// Our mouse hoovers over an action? Select it!
       
  1274 		int index = (e->we.popupmenu.pt.y - w->top) / CLNWND_ROWSIZE;
       
  1275 
       
  1276 		if (index == -1 || index == WP(w,menu_d).sel_index) return;
       
  1277 
       
  1278 		WP(w,menu_d).sel_index = index;
       
  1279 		SetWindowDirty(w);
       
  1280 	} break;
       
  1281 
       
  1282 	}
       
  1283 }
       
  1284 
       
  1285 // Main handle for clientlist
       
  1286 static void ClientListWndProc(Window *w, WindowEvent *e)
       
  1287 {
       
  1288 	switch (e->event) {
       
  1289 	case WE_PAINT: {
       
  1290 		NetworkClientInfo *ci;
       
  1291 		int y, i = 0;
       
  1292 		byte colour;
       
  1293 
       
  1294 		// Check if we need to reset the height
       
  1295 		if (!CheckClientListHeight(w)) break;
       
  1296 
       
  1297 		DrawWindowWidgets(w);
       
  1298 
       
  1299 		y = CLNWND_OFFSET;
       
  1300 
       
  1301 		FOR_ALL_ACTIVE_CLIENT_INFOS(ci) {
       
  1302 			if (_selected_clientlist_item == i++) { // Selected item, highlight it
       
  1303 				GfxFillRect(1, y, 248, y + CLNWND_ROWSIZE - 1, 0);
       
  1304 				colour = 0xC;
       
  1305 			} else {
       
  1306 				colour = 0x10;
       
  1307 			}
       
  1308 
       
  1309 			if (ci->client_index == NETWORK_SERVER_INDEX) {
       
  1310 				DrawString(4, y, STR_NETWORK_SERVER, colour);
       
  1311 			} else {
       
  1312 				DrawString(4, y, STR_NETWORK_CLIENT, colour);
       
  1313 			}
       
  1314 
       
  1315 			// Filter out spectators
       
  1316 			if (IsValidPlayer(ci->client_playas)) DrawPlayerIcon(ci->client_playas, 64, y + 1);
       
  1317 
       
  1318 			DoDrawString(ci->client_name, 81, y, colour);
       
  1319 
       
  1320 			y += CLNWND_ROWSIZE;
       
  1321 		}
       
  1322 	}	break;
       
  1323 
       
  1324 	case WE_CLICK:
       
  1325 		// Show the popup with option
       
  1326 		if (_selected_clientlist_item != 255) {
       
  1327 			PopupClientList(w, _selected_clientlist_item, e->we.click.pt.x + w->left, e->we.click.pt.y + w->top);
       
  1328 		}
       
  1329 
       
  1330 		break;
       
  1331 
       
  1332 	case WE_MOUSEOVER:
       
  1333 		// -1 means we left the current window
       
  1334 		if (e->we.mouseover.pt.y == -1) {
       
  1335 			_selected_clientlist_y = 0;
       
  1336 			_selected_clientlist_item = 255;
       
  1337 			SetWindowDirty(w);
       
  1338 			break;
       
  1339 		}
       
  1340 		// It did not change.. no update!
       
  1341 		if (e->we.mouseover.pt.y == _selected_clientlist_y) break;
       
  1342 
       
  1343 		// Find the new selected item (if any)
       
  1344 		_selected_clientlist_y = e->we.mouseover.pt.y;
       
  1345 		if (e->we.mouseover.pt.y > CLNWND_OFFSET) {
       
  1346 			_selected_clientlist_item = (e->we.mouseover.pt.y - CLNWND_OFFSET) / CLNWND_ROWSIZE;
       
  1347 		} else {
       
  1348 			_selected_clientlist_item = 255;
       
  1349 		}
       
  1350 
       
  1351 		// Repaint
       
  1352 		SetWindowDirty(w);
       
  1353 		break;
       
  1354 
       
  1355 	case WE_DESTROY: case WE_CREATE:
       
  1356 		// When created or destroyed, data is reset
       
  1357 		_selected_clientlist_item = 255;
       
  1358 		_selected_clientlist_y = 0;
       
  1359 		break;
       
  1360 	}
       
  1361 }
       
  1362 
       
  1363 void ShowClientList(void)
       
  1364 {
       
  1365 	AllocateWindowDescFront(&_client_list_desc, 0);
       
  1366 }
       
  1367 
       
  1368 
       
  1369 static NetworkPasswordType pw_type;
       
  1370 
       
  1371 
       
  1372 void ShowNetworkNeedPassword(NetworkPasswordType npt)
       
  1373 {
       
  1374 	StringID caption;
       
  1375 
       
  1376 	pw_type = npt;
       
  1377 	switch (npt) {
       
  1378 		default: NOT_REACHED();
       
  1379 		case NETWORK_GAME_PASSWORD:    caption = STR_NETWORK_NEED_GAME_PASSWORD_CAPTION; break;
       
  1380 		case NETWORK_COMPANY_PASSWORD: caption = STR_NETWORK_NEED_COMPANY_PASSWORD_CAPTION; break;
       
  1381 	}
       
  1382 	ShowQueryString(STR_EMPTY, caption, 20, 180, FindWindowById(WC_NETWORK_STATUS_WINDOW, 0), CS_ALPHANUMERAL);
       
  1383 }
       
  1384 
       
  1385 
       
  1386 static void NetworkJoinStatusWindowWndProc(Window *w, WindowEvent *e)
       
  1387 {
       
  1388 	switch (e->event) {
       
  1389 	case WE_PAINT: {
       
  1390 		uint8 progress; // used for progress bar
       
  1391 		DrawWindowWidgets(w);
       
  1392 
       
  1393 		DrawStringCentered(125, 35, STR_NETWORK_CONNECTING_1 + _network_join_status, 14);
       
  1394 		switch (_network_join_status) {
       
  1395 			case NETWORK_JOIN_STATUS_CONNECTING: case NETWORK_JOIN_STATUS_AUTHORIZING:
       
  1396 			case NETWORK_JOIN_STATUS_GETTING_COMPANY_INFO:
       
  1397 				progress = 10; // first two stages 10%
       
  1398 				break;
       
  1399 			case NETWORK_JOIN_STATUS_WAITING:
       
  1400 				SetDParam(0, _network_join_waiting);
       
  1401 				DrawStringCentered(125, 46, STR_NETWORK_CONNECTING_WAITING, 14);
       
  1402 				progress = 15; // third stage is 15%
       
  1403 				break;
       
  1404 			case NETWORK_JOIN_STATUS_DOWNLOADING:
       
  1405 				SetDParam(0, _network_join_kbytes);
       
  1406 				SetDParam(1, _network_join_kbytes_total);
       
  1407 				DrawStringCentered(125, 46, STR_NETWORK_CONNECTING_DOWNLOADING, 14);
       
  1408 				/* Fallthrough */
       
  1409 			default: /* Waiting is 15%, so the resting receivement of map is maximum 70% */
       
  1410 				progress = 15 + _network_join_kbytes * (100 - 15) / _network_join_kbytes_total;
       
  1411 		}
       
  1412 
       
  1413 		/* Draw nice progress bar :) */
       
  1414 		DrawFrameRect(20, 18, (int)((w->width - 20) * progress / 100), 28, 10, 0);
       
  1415 	}	break;
       
  1416 
       
  1417 	case WE_CLICK:
       
  1418 		switch (e->we.click.widget) {
       
  1419 			case 2: /* Disconnect button */
       
  1420 				NetworkDisconnect();
       
  1421 				DeleteWindow(w);
       
  1422 				SwitchMode(SM_MENU);
       
  1423 				ShowNetworkGameWindow();
       
  1424 				break;
       
  1425 		}
       
  1426 		break;
       
  1427 
       
  1428 		/* If the server asks for a password, we need to fill it in */
       
  1429 		case WE_ON_EDIT_TEXT_CANCEL:
       
  1430 			NetworkDisconnect();
       
  1431 			ShowNetworkGameWindow();
       
  1432 			break;
       
  1433 
       
  1434 		case WE_ON_EDIT_TEXT:
       
  1435 			SEND_COMMAND(PACKET_CLIENT_PASSWORD)(pw_type, e->we.edittext.str);
       
  1436 			break;
       
  1437 	}
       
  1438 }
       
  1439 
       
  1440 static const Widget _network_join_status_window_widget[] = {
       
  1441 {    WWT_CAPTION,   RESIZE_NONE,    14,     0,   249,     0,    13, STR_NETWORK_CONNECTING, STR_018C_WINDOW_TITLE_DRAG_THIS},
       
  1442 {      WWT_PANEL,   RESIZE_NONE,    14,     0,   249,    14,    84, 0x0,                    STR_NULL},
       
  1443 { WWT_PUSHTXTBTN,   RESIZE_NONE,   BTC,    75,   175,    69,    80, STR_NETWORK_DISCONNECT, STR_NULL},
       
  1444 {   WIDGETS_END},
       
  1445 };
       
  1446 
       
  1447 static const WindowDesc _network_join_status_window_desc = {
       
  1448 	WDP_CENTER, WDP_CENTER, 250, 85,
       
  1449 	WC_NETWORK_STATUS_WINDOW, 0,
       
  1450 	WDF_STD_TOOLTIPS | WDF_DEF_WIDGET | WDF_MODAL,
       
  1451 	_network_join_status_window_widget,
       
  1452 	NetworkJoinStatusWindowWndProc,
       
  1453 };
       
  1454 
       
  1455 void ShowJoinStatusWindow(void)
       
  1456 {
       
  1457 	Window *w;
       
  1458 	DeleteWindowById(WC_NETWORK_STATUS_WINDOW, 0);
       
  1459 	w = AllocateWindowDesc(&_network_join_status_window_desc);
       
  1460 	/* Parent the status window to the lobby */
       
  1461 	if (w != NULL) w->parent = FindWindowById(WC_NETWORK_WINDOW, 0);
       
  1462 }
       
  1463 
       
  1464 static void SendChat(const char *buf, DestType type, byte dest)
       
  1465 {
       
  1466 	if (buf[0] == '\0') return;
       
  1467 	if (!_network_server) {
       
  1468 		SEND_COMMAND(PACKET_CLIENT_CHAT)(NETWORK_ACTION_CHAT + type, type, dest, buf);
       
  1469 	} else {
       
  1470 		NetworkServer_HandleChat(NETWORK_ACTION_CHAT + type, type, dest, buf, NETWORK_SERVER_INDEX);
       
  1471 	}
       
  1472 }
       
  1473 
       
  1474 /**
       
  1475  * Find the next item of the list of things that can be auto-completed.
       
  1476  * @param item The current indexed item to return. This function can, and most
       
  1477  *     likely will, alter item, to skip empty items in the arrays.
       
  1478  * @return Returns the char that matched to the index.
       
  1479  */
       
  1480 static const char *ChatTabCompletionNextItem(uint *item)
       
  1481 {
       
  1482 	static char chat_tab_temp_buffer[64];
       
  1483 
       
  1484 	/* First, try clients */
       
  1485 	if (*item < MAX_CLIENT_INFO) {
       
  1486 		/* Skip inactive clients */
       
  1487 		while (_network_client_info[*item].client_index == NETWORK_EMPTY_INDEX && *item < MAX_CLIENT_INFO) (*item)++;
       
  1488 		if (*item < MAX_CLIENT_INFO) return _network_client_info[*item].client_name;
       
  1489 	}
       
  1490 
       
  1491 	/* Then, try townnames */
       
  1492 	/* Not that the following assumes all town indices are adjacent, ie no
       
  1493 	 * towns have been deleted. */
       
  1494 	if (*item <= (uint)MAX_CLIENT_INFO + GetMaxTownIndex()) {
       
  1495 		const Town *t;
       
  1496 
       
  1497 		FOR_ALL_TOWNS_FROM(t, *item - MAX_CLIENT_INFO) {
       
  1498 			/* Get the town-name via the string-system */
       
  1499 			SetDParam(0, t->townnameparts);
       
  1500 			GetString(chat_tab_temp_buffer, t->townnametype, lastof(chat_tab_temp_buffer));
       
  1501 			return &chat_tab_temp_buffer[0];
       
  1502 		}
       
  1503 	}
       
  1504 
       
  1505 	return NULL;
       
  1506 }
       
  1507 
       
  1508 /**
       
  1509  * Find what text to complete. It scans for a space from the left and marks
       
  1510  *  the word right from that as to complete. It also writes a \0 at the
       
  1511  *  position of the space (if any). If nothing found, buf is returned.
       
  1512  */
       
  1513 static char *ChatTabCompletionFindText(char *buf)
       
  1514 {
       
  1515 	char *p = strrchr(buf, ' ');
       
  1516 	if (p == NULL) return buf;
       
  1517 
       
  1518 	*p = '\0';
       
  1519 	return p + 1;
       
  1520 }
       
  1521 
       
  1522 /**
       
  1523  * See if we can auto-complete the current text of the user.
       
  1524  */
       
  1525 static void ChatTabCompletion(Window *w)
       
  1526 {
       
  1527 	static char _chat_tab_completion_buf[lengthof(_edit_str_buf)];
       
  1528 	Textbuf *tb = &WP(w, querystr_d).text;
       
  1529 	uint len, tb_len;
       
  1530 	uint item;
       
  1531 	char *tb_buf, *pre_buf;
       
  1532 	const char *cur_name;
       
  1533 	bool second_scan = false;
       
  1534 
       
  1535 	item = 0;
       
  1536 
       
  1537 	/* Copy the buffer so we can modify it without damaging the real data */
       
  1538 	pre_buf = (_chat_tab_completion_active) ? strdup(_chat_tab_completion_buf) : strdup(tb->buf);
       
  1539 
       
  1540 	tb_buf  = ChatTabCompletionFindText(pre_buf);
       
  1541 	tb_len  = strlen(tb_buf);
       
  1542 
       
  1543 	while ((cur_name = ChatTabCompletionNextItem(&item)) != NULL) {
       
  1544 		item++;
       
  1545 
       
  1546 		if (_chat_tab_completion_active) {
       
  1547 			/* We are pressing TAB again on the same name, is there an other name
       
  1548 			 *  that starts with this? */
       
  1549 			if (!second_scan) {
       
  1550 				uint offset;
       
  1551 				uint length;
       
  1552 
       
  1553 				/* If we are completing at the begin of the line, skip the ': ' we added */
       
  1554 				if (tb_buf == pre_buf) {
       
  1555 					offset = 0;
       
  1556 					length = tb->length - 2;
       
  1557 				} else {
       
  1558 					/* Else, find the place we are completing at */
       
  1559 					offset = strlen(pre_buf) + 1;
       
  1560 					length = tb->length - offset;
       
  1561 				}
       
  1562 
       
  1563 				/* Compare if we have a match */
       
  1564 				if (strlen(cur_name) == length && strncmp(cur_name, tb->buf + offset, length) == 0) second_scan = true;
       
  1565 
       
  1566 				continue;
       
  1567 			}
       
  1568 
       
  1569 			/* Now any match we make on _chat_tab_completion_buf after this, is perfect */
       
  1570 		}
       
  1571 
       
  1572 		len = strlen(cur_name);
       
  1573 		if (tb_len < len && strncasecmp(cur_name, tb_buf, tb_len) == 0) {
       
  1574 			/* Save the data it was before completion */
       
  1575 			if (!second_scan) snprintf(_chat_tab_completion_buf, lengthof(_chat_tab_completion_buf), "%s", tb->buf);
       
  1576 			_chat_tab_completion_active = true;
       
  1577 
       
  1578 			/* Change to the found name. Add ': ' if we are at the start of the line (pretty) */
       
  1579 			if (pre_buf == tb_buf) {
       
  1580 				snprintf(tb->buf, lengthof(_edit_str_buf), "%s: ", cur_name);
       
  1581 			} else {
       
  1582 				snprintf(tb->buf, lengthof(_edit_str_buf), "%s %s", pre_buf, cur_name);
       
  1583 			}
       
  1584 
       
  1585 			/* Update the textbuffer */
       
  1586 			UpdateTextBufferSize(&WP(w, querystr_d).text);
       
  1587 
       
  1588 			SetWindowDirty(w);
       
  1589 			free(pre_buf);
       
  1590 			return;
       
  1591 		}
       
  1592 	}
       
  1593 
       
  1594 	if (second_scan) {
       
  1595 		/* We walked all posibilities, and the user presses tab again.. revert to original text */
       
  1596 		strcpy(tb->buf, _chat_tab_completion_buf);
       
  1597 		_chat_tab_completion_active = false;
       
  1598 
       
  1599 		/* Update the textbuffer */
       
  1600 		UpdateTextBufferSize(&WP(w, querystr_d).text);
       
  1601 
       
  1602 		SetWindowDirty(w);
       
  1603 	}
       
  1604 	free(pre_buf);
       
  1605 }
       
  1606 
       
  1607 /* uses querystr_d WP macro
       
  1608  * uses querystr_d->caption to store
       
  1609  * - type of chat message (Private/Team/All) in bytes 0-7
       
  1610  * - destination of chat message in the case of Team/Private in bytes 8-15 */
       
  1611 static void ChatWindowWndProc(Window *w, WindowEvent *e)
       
  1612 {
       
  1613 	switch (e->event) {
       
  1614 	case WE_CREATE:
       
  1615 		SendWindowMessage(WC_NEWS_WINDOW, 0, WE_CREATE, w->height, 0);
       
  1616 		SETBIT(_no_scroll, SCROLL_CHAT); // do not scroll the game with the arrow-keys
       
  1617 		break;
       
  1618 
       
  1619 	case WE_PAINT: {
       
  1620 		static const StringID chat_captions[] = {
       
  1621 			STR_NETWORK_CHAT_ALL_CAPTION,
       
  1622 			STR_NETWORK_CHAT_COMPANY_CAPTION,
       
  1623 			STR_NETWORK_CHAT_CLIENT_CAPTION
       
  1624 		};
       
  1625 		StringID msg;
       
  1626 
       
  1627 		DrawWindowWidgets(w);
       
  1628 
       
  1629 		assert(GB(WP(w, querystr_d).caption, 0, 8) < lengthof(chat_captions));
       
  1630 		msg = chat_captions[GB(WP(w, querystr_d).caption, 0, 8)];
       
  1631 		DrawStringRightAligned(w->widget[2].left - 2, w->widget[2].top + 1, msg, 16);
       
  1632 		DrawEditBox(w, &WP(w, querystr_d), 2);
       
  1633 	} break;
       
  1634 
       
  1635 	case WE_CLICK:
       
  1636 		switch (e->we.click.widget) {
       
  1637 			case 3: { /* Send */
       
  1638 				DestType type = GB(WP(w, querystr_d).caption, 0, 8);
       
  1639 				byte dest = GB(WP(w, querystr_d).caption, 8, 8);
       
  1640 				SendChat(WP(w, querystr_d).text.buf, type, dest);
       
  1641 			} /* FALLTHROUGH */
       
  1642 			case 0: /* Cancel */ DeleteWindow(w); break;
       
  1643 		}
       
  1644 		break;
       
  1645 
       
  1646 	case WE_MOUSELOOP:
       
  1647 		HandleEditBox(w, &WP(w, querystr_d), 2);
       
  1648 		break;
       
  1649 
       
  1650 	case WE_KEYPRESS:
       
  1651 		if (e->we.keypress.keycode == WKC_TAB) {
       
  1652 			ChatTabCompletion(w);
       
  1653 		} else {
       
  1654 			_chat_tab_completion_active = false;
       
  1655 			switch (HandleEditBoxKey(w, &WP(w, querystr_d), 2, e)) {
       
  1656 				case 1: { /* Return */
       
  1657 				DestType type = GB(WP(w, querystr_d).caption, 0, 8);
       
  1658 				byte dest = GB(WP(w, querystr_d).caption, 8, 8);
       
  1659 				SendChat(WP(w, querystr_d).text.buf, type, dest);
       
  1660 			} /* FALLTHROUGH */
       
  1661 				case 2: /* Escape */ DeleteWindow(w); break;
       
  1662 			}
       
  1663 		}
       
  1664 		break;
       
  1665 
       
  1666 	case WE_DESTROY:
       
  1667 		SendWindowMessage(WC_NEWS_WINDOW, 0, WE_DESTROY, 0, 0);
       
  1668 		CLRBIT(_no_scroll, SCROLL_CHAT);
       
  1669 		break;
       
  1670 	}
       
  1671 }
       
  1672 
       
  1673 static const Widget _chat_window_widgets[] = {
       
  1674 {   WWT_CLOSEBOX, RESIZE_NONE, 14,   0,  10,  0, 13, STR_00C5,         STR_018B_CLOSE_WINDOW},
       
  1675 {      WWT_PANEL, RESIZE_NONE, 14,  11, 639,  0, 13, 0x0,              STR_NULL}, // background
       
  1676 {      WWT_PANEL, RESIZE_NONE, 14,  75, 577,  1, 12, 0x0,              STR_NULL}, // text box
       
  1677 { WWT_PUSHTXTBTN, RESIZE_NONE, 14, 578, 639,  1, 12, STR_NETWORK_SEND, STR_NULL}, // send button
       
  1678 {   WIDGETS_END},
       
  1679 };
       
  1680 
       
  1681 static const WindowDesc _chat_window_desc = {
       
  1682 	WDP_CENTER, -26, 640, 14, // x, y, width, height
       
  1683 	WC_SEND_NETWORK_MSG,0,
       
  1684 	WDF_STD_TOOLTIPS | WDF_DEF_WIDGET,
       
  1685 	_chat_window_widgets,
       
  1686 	ChatWindowWndProc
       
  1687 };
       
  1688 
       
  1689 void ShowNetworkChatQueryWindow(DestType type, byte dest)
       
  1690 {
       
  1691 	Window *w;
       
  1692 
       
  1693 	DeleteWindowById(WC_SEND_NETWORK_MSG, 0);
       
  1694 
       
  1695 	_edit_str_buf[0] = '\0';
       
  1696 	_chat_tab_completion_active = false;
       
  1697 
       
  1698 	w = AllocateWindowDesc(&_chat_window_desc);
       
  1699 
       
  1700 	LowerWindowWidget(w, 2);
       
  1701 	WP(w, querystr_d).caption = GB(type, 0, 8) | (dest << 8); // Misuse of caption
       
  1702 	WP(w, querystr_d).afilter = CS_ALPHANUMERAL;
       
  1703 	InitializeTextBuffer(&WP(w, querystr_d).text, _edit_str_buf, lengthof(_edit_str_buf), 0);
       
  1704 }
       
  1705 
       
  1706 #endif /* ENABLE_NETWORK */