1 /* Pioneers - Implementation of the excellent Settlers of Catan board game.
2  *   Go buy a copy.
3  *
4  * Copyright (C) 1999 Dave Cole
5  * Copyright (C) 2003,2006 Bas Wijnen <shevek@fmf.nl>
6  * Copyright (C) 2004,2010 Roland Clobus <rclobus@rclobus.nl>
7  *
8  * This program is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License as published by
10  * the Free Software Foundation; either version 2 of the License, or
11  * (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program; if not, write to the Free Software
20  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
21  */
22 
23 #include "config.h"
24 #include <ctype.h>
25 #include <stdlib.h>
26 #include <errno.h>
27 #include <fcntl.h>
28 
29 #include "frontend.h"
30 #include "network.h"
31 #include "log.h"
32 #include "config-gnome.h"
33 #include "select-game.h"
34 #include "game-settings.h"
35 #include "game-rules.h"
36 #include "metaserver.h"
37 #include "avahi.h"
38 #include "avahi-browser.h"
39 #include "client.h"
40 #include "common_gtk.h"
41 #include "game-list.h"
42 
43 const int PRIVATE_GAME_HISTORY_SIZE = 10;
44 
45 static gboolean connect_spectator;	/* Prefer to be a spectator */
46 static gboolean connect_spectator_allowed;	/* Spectator allowed */
47 static gchar *connect_server;	/* Name of the server */
48 static gchar *connect_port;	/* Port of the server */
49 
50 static GtkWidget *connect_dlg;	/* Dialog for starting a new game */
51 static GtkWidget *name_entry;	/* Name of the player */
52 static GtkWidget *spectator_toggle;	/* Prefer to be a spectator */
53 static GtkWidget *metaserver_entry;	/* Name of the metaserver */
54 
55 static GtkWidget *meta_dlg;	/* Dialog for joining a public game */
56 static GtkWidget *server_status;	/* Description of the current metaserver */
57 static GtkListStore *meta_games_model;	/* The list of the games at the metaserver */
58 static GtkWidget *meta_games_view;	/* The view on meta_games_model */
59 
60 enum {
61 	META_RESPONSE_NEW = 1,	/* Response for new game */
62 	META_RESPONSE_REFRESH = 2	/* Response for refresh of the list */
63 };
64 
65 enum {				/* The columns of the meta_games_model */
66 	C_META_HOST,
67 	C_META_PORT,
68 	C_META_HOST_SORTABLE,	/* Invisible, used for sorting */
69 	C_META_VERSION,
70 	C_META_MAX,
71 	C_META_CUR,
72 	C_META_TERRAIN,
73 	C_META_VICTORY,
74 	C_META_SEVENS,
75 	C_META_MAP,
76 	META_N_COLUMNS
77 };
78 
79 static GtkWidget *cserver_dlg;	/* Dialog for creating a public game */
80 static GtkWidget *select_game;	/* select game type */
81 static GtkWidget *game_settings;	/* game settings widget */
82 static GtkWidget *aiplayers_spin;	/* number of AI players */
83 static GtkWidget *game_rules;	/* Adjust some rules */
84 
85 static gboolean cfg_terrain;	/* Random terrain */
86 static guint cfg_num_players;
87 static guint cfg_victory_points;
88 static guint cfg_sevens_rule;
89 static guint cfg_ai_players;
90 static const gchar *cfg_gametype;	/* Will be set be the widget */
91 
92 static GtkWidget *connect_private_dlg;	/* Join a private game */
93 static GtkWidget *host_entry;	/* Host name entry */
94 static GtkWidget *port_entry;	/* Host port entry */
95 
96 static enum {
97 	GAMETYPE_MODE_SIGNON,
98 	GAMETYPE_MODE_LIST
99 } gametype_mode;
100 
101 static enum {
102 	CREATE_MODE_SIGNON,
103 	CREATE_MODE_WAIT_FOR_INFO,
104 	CREATE_MODE_STARTING_OK,
105 	CREATE_MODE_STARTING_ERROR
106 } create_mode;
107 
108 static enum {
109 	MODE_SIGNON,
110 	MODE_REDIRECT,
111 	MODE_LIST,
112 	MODE_CAPABILITY,
113 	MODE_REDIRECT_OVERFLOW,
114 	MODE_DONE,
115 	MODE_SERVER_INFO
116 } meta_mode;
117 
118 /** Information about the metaserver */
119 static struct {
120 	/** Server name */
121 	gchar *server;
122 	/** Port */
123 	gchar *port;
124 	/** Major version number of metaserver protocol */
125 	gint version_major;
126 	/** Minor version number of metaserver protocol */
127 	gint version_minor;
128 	/** Number of times the metaserver has redirected */
129 	gint num_redirects;
130 	/** The metaserver can create remote games */
131 	gboolean can_create_games;
132 	/** Active session */
133 	Session *session;
134 	/** Number of available game titles */
135 	guint num_available_titles;
136 	/** The settings of a game */
137 	GameParams *params;
138 } metaserver_info = {
139 	NULL, NULL, 0, 0, 0, FALSE, NULL, 0u, NULL
140 };
141 
142 #define STRARG_LEN 128
143 #define INTARG_LEN 16
144 
145 static gchar server_host[STRARG_LEN];
146 static gchar server_port[STRARG_LEN];
147 static gchar server_version[STRARG_LEN];
148 static gchar server_max[INTARG_LEN];
149 static gchar server_curr[INTARG_LEN];
150 static gchar server_vpoints[STRARG_LEN];
151 static gchar *server_sevenrule = NULL;
152 static gchar *server_terrain = NULL;
153 static gchar server_title[STRARG_LEN];
154 
155 static void query_metaserver(const gchar * server, const gchar * port);
156 static void show_waiting_box(const gchar * message, const gchar * server,
157 			     const gchar * port);
158 static void close_waiting_box(void);
159 
160 static void connect_set_field(gchar ** field, const gchar * value);
161 static void connect_close_all(gboolean user_pressed_ok,
162 			      gboolean can_be_spectator);
163 static void set_metaserver_info(void);
164 static void connect_private_dialog(G_GNUC_UNUSED GtkWidget * widget,
165 				   GtkWindow * parent);
166 
connect_set_field(gchar ** field,const gchar * value)167 static void connect_set_field(gchar ** field, const gchar * value)
168 {
169 	gchar *temp = g_strdup(value);
170 	g_free(*field);
171 	if (temp != NULL) {
172 		*field = g_strdup(g_strstrip(temp));
173 	} else {
174 		*field = NULL;
175 	}
176 	g_free(temp);
177 }
178 
179 /* Reset all pointers to the destroyed children of the dialog */
connect_dlg_destroyed(GtkWidget * widget,GtkWidget ** widget_pointer)180 static void connect_dlg_destroyed(GtkWidget * widget,
181 				  GtkWidget ** widget_pointer)
182 {
183 	name_entry = NULL;
184 	spectator_toggle = NULL;
185 	metaserver_entry = NULL;
186 	gtk_widget_destroyed(widget, widget_pointer);
187 }
188 
connect_name_change_cb(G_GNUC_UNUSED gpointer ns)189 static void connect_name_change_cb(G_GNUC_UNUSED gpointer ns)
190 {
191 	gchar *name = notifying_string_get(requested_name);
192 	if (name_entry != NULL)
193 		gtk_entry_set_text(GTK_ENTRY(name_entry), name);
194 	g_free(name);
195 }
196 
197 /* Public functions */
connect_get_spectator(void)198 gboolean connect_get_spectator(void)
199 {
200 	return connect_spectator && connect_spectator_allowed;
201 }
202 
connect_set_spectator(gboolean spectator)203 void connect_set_spectator(gboolean spectator)
204 {
205 	connect_spectator = spectator;
206 	connect_spectator_allowed = TRUE;
207 	if (spectator_toggle != NULL)
208 		gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON
209 					     (spectator_toggle),
210 					     connect_spectator);
211 }
212 
connect_get_server(void)213 const gchar *connect_get_server(void)
214 {
215 	return connect_server;
216 }
217 
connect_set_server(const gchar * server)218 void connect_set_server(const gchar * server)
219 {
220 	connect_set_field(&connect_server, server);
221 }
222 
connect_get_metaserver(void)223 static gchar *connect_get_metaserver(void)
224 {
225 	if (metaserver_entry == NULL)
226 		return NULL;
227 	return metaserver_get(METASERVER(metaserver_entry));
228 }
229 
connect_set_metaserver(const gchar * metaserver)230 void connect_set_metaserver(const gchar * metaserver)
231 {
232 	connect_set_field(&metaserver_info.server, metaserver);
233 	if (metaserver_entry != NULL)
234 		metaserver_add(METASERVER(metaserver_entry),
235 			       metaserver_info.server);
236 }
237 
connect_get_port(void)238 const gchar *connect_get_port(void)
239 {
240 	return connect_port;
241 }
242 
connect_set_port(const gchar * port)243 void connect_set_port(const gchar * port)
244 {
245 	connect_set_field(&connect_port, port);
246 }
247 
connect_close_all(gboolean user_pressed_ok,gboolean can_be_spectator)248 static void connect_close_all(gboolean user_pressed_ok,
249 			      gboolean can_be_spectator)
250 {
251 	connect_spectator_allowed = can_be_spectator;
252 	if (user_pressed_ok) {
253 		gchar *metaserver;
254 
255 		notifying_string_set(requested_name,
256 				     gtk_entry_get_text(GTK_ENTRY
257 							(name_entry)));
258 		connect_spectator =
259 		    gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON
260 						 (spectator_toggle));
261 		/* Save connect dialogue entries */
262 		metaserver = connect_get_metaserver();
263 		config_set_string("connect/metaserver", metaserver);
264 		g_free(metaserver);
265 
266 		frontend_gui_register_destroy(connect_dlg,
267 					      GUI_CONNECT_TRY);
268 	} else {
269 		frontend_gui_register_destroy(connect_dlg,
270 					      GUI_CONNECT_CANCEL);
271 	}
272 	if (connect_dlg)
273 		gtk_widget_destroy(GTK_WIDGET(connect_dlg));
274 	if (meta_dlg)
275 		gtk_widget_destroy(GTK_WIDGET(meta_dlg));
276 	if (cserver_dlg)
277 		gtk_widget_destroy(GTK_WIDGET(cserver_dlg));
278 	if (connect_private_dlg)
279 		gtk_widget_destroy(GTK_WIDGET(connect_private_dlg));
280 }
281 
282 /* Messages explaining some delays */
show_waiting_box(const gchar * message,const gchar * server,const gchar * port)283 static void show_waiting_box(const gchar * message, const gchar * server,
284 			     const gchar * port)
285 {
286 	if (meta_dlg) {
287 		gchar *s = g_strdup_printf(_("Metaserver at %s, port %s"),
288 					   server, port);
289 		gtk_label_set_text(GTK_LABEL(server_status), s);
290 		g_free(s);
291 	}
292 	log_message(MSG_INFO, message);
293 }
294 
close_waiting_box(void)295 static void close_waiting_box(void)
296 {
297 	log_message(MSG_INFO, _("Finished.\n"));
298 }
299 
300 /* -------------------- get game types -------------------- */
301 
meta_gametype_notify(Session * ses,NetEvent event,const gchar * line,G_GNUC_UNUSED gpointer user_data)302 static void meta_gametype_notify(Session * ses, NetEvent event,
303 				 const gchar * line,
304 				 G_GNUC_UNUSED gpointer user_data)
305 {
306 	switch (event) {
307 	case NET_CONNECT:
308 		metaserver_info.num_available_titles = 0u;
309 		break;
310 	case NET_CONNECT_FAIL:
311 		log_message(MSG_ERROR,
312 			    _("The metaserver is not available "
313 			      "anymore.\n"));
314 		net_free(&ses);
315 		metaserver_info.session = NULL;
316 		if (cserver_dlg != NULL) {
317 			gtk_widget_destroy(GTK_WIDGET(cserver_dlg));
318 		}
319 		break;
320 	case NET_CLOSE:
321 		if (gametype_mode == GAMETYPE_MODE_SIGNON) {
322 			log_message(MSG_ERROR,
323 				    _("The metaserver closed "
324 				      "the connection "
325 				      "unexpectedly.\n"));
326 			if (cserver_dlg != NULL) {
327 				gtk_widget_destroy(GTK_WIDGET
328 						   (cserver_dlg));
329 			}
330 		} else {
331 			close_waiting_box();
332 		}
333 		if (cserver_dlg != NULL
334 		    && metaserver_info.num_available_titles > 0u) {
335 			gtk_dialog_set_response_sensitive(GTK_DIALOG
336 							  (cserver_dlg),
337 							  GTK_RESPONSE_OK,
338 							  TRUE);
339 		}
340 		net_free(&ses);
341 		metaserver_info.session = NULL;
342 		break;
343 	case NET_READ:
344 		switch (gametype_mode) {
345 		case GAMETYPE_MODE_SIGNON:
346 			net_printf(ses, "listtypes\n");
347 			gametype_mode = GAMETYPE_MODE_LIST;
348 			break;
349 		case GAMETYPE_MODE_LIST:
350 			/* A server description looks like this:
351 			 * title=%s\n
352 			 */
353 			if (strncmp(line, "title=", 6) == 0) {
354 				const GameParams *params;
355 
356 				line += 6;
357 				if (game_list_is_empty()) {
358 					game_list_prepare();
359 				}
360 				params = game_list_find_item(line);
361 				if (params != NULL) {
362 					select_game_add_details(SELECTGAME
363 								(select_game),
364 								params);
365 				} else {
366 					select_game_add(SELECTGAME
367 							(select_game),
368 							line);
369 				}
370 				metaserver_info.num_available_titles++;
371 			}
372 			break;
373 		}
374 		break;
375 	}
376 }
377 
get_metaserver_games_types(const gchar * server,const gchar * port)378 static void get_metaserver_games_types(const gchar * server,
379 				       const gchar * port)
380 {
381 	show_waiting_box(_("Receiving game names from the metaserver.\n"),
382 			 server, port);
383 	g_assert(metaserver_info.session == NULL);
384 	metaserver_info.session = net_new(meta_gametype_notify, NULL);
385 	if (net_connect(metaserver_info.session, server, port))
386 		gametype_mode = GAMETYPE_MODE_SIGNON;
387 	else {
388 		net_free(&metaserver_info.session);
389 		log_message(MSG_ERROR,
390 			    _("The metaserver is not available "
391 			      "anymore.\n"));
392 		if (cserver_dlg != NULL) {
393 			gtk_widget_destroy(GTK_WIDGET(cserver_dlg));
394 		}
395 	}
396 }
397 
398 /* -------------------- create game server -------------------- */
399 
meta_create_notify(Session * ses,NetEvent event,const gchar * line,G_GNUC_UNUSED gpointer user_data)400 static void meta_create_notify(Session * ses, NetEvent event,
401 			       const gchar * line,
402 			       G_GNUC_UNUSED gpointer user_data)
403 {
404 	switch (event) {
405 	case NET_CONNECT:
406 		break;
407 	case NET_CONNECT_FAIL:
408 		log_message(MSG_ERROR,
409 			    _("The metaserver is not available "
410 			      "anymore.\n"));
411 		net_free(&ses);
412 		metaserver_info.session = NULL;
413 		if (cserver_dlg != NULL) {
414 			gtk_widget_destroy(GTK_WIDGET(cserver_dlg));
415 		}
416 		break;
417 	case NET_CLOSE:
418 		net_free(&ses);
419 		metaserver_info.session = NULL;
420 		switch (create_mode) {
421 		case CREATE_MODE_STARTING_OK:
422 			log_message(MSG_INFO,
423 				    _("New game server requested "
424 				      "on %s port %s.\n"),
425 				    connect_server, connect_port);
426 			/* The metaserver is now busy creating the new game.
427 			 * UGLY FIX: Wait for some time */
428 			g_usleep(500000);
429 			connect_close_all(TRUE, FALSE);
430 			break;
431 		case CREATE_MODE_STARTING_ERROR:
432 			log_message(MSG_ERROR,
433 				    _("Incomplete information about the "
434 				      "new game server received.\n"));
435 			if (cserver_dlg != NULL) {
436 				gtk_dialog_set_response_sensitive
437 				    (GTK_DIALOG(cserver_dlg),
438 				     GTK_RESPONSE_OK, TRUE);
439 			}
440 			break;
441 		case CREATE_MODE_SIGNON:
442 		case CREATE_MODE_WAIT_FOR_INFO:
443 			log_message(MSG_ERROR,
444 				    _("The metaserver closed "
445 				      "the connection "
446 				      "unexpectedly.\n"));
447 			if (cserver_dlg != NULL) {
448 				gtk_dialog_set_response_sensitive
449 				    (GTK_DIALOG(cserver_dlg),
450 				     GTK_RESPONSE_OK, TRUE);
451 			}
452 			break;
453 		}
454 		break;
455 	case NET_READ:
456 		switch (create_mode) {
457 		case CREATE_MODE_SIGNON:
458 			net_printf(ses,
459 				   "create %d %d %d %d %d %s\n",
460 				   cfg_terrain, cfg_num_players,
461 				   cfg_victory_points, cfg_sevens_rule,
462 				   cfg_ai_players, cfg_gametype);
463 			create_mode = CREATE_MODE_WAIT_FOR_INFO;
464 			connect_set_field(&connect_server, NULL);
465 			connect_set_field(&connect_port, NULL);
466 			break;
467 
468 		case CREATE_MODE_WAIT_FOR_INFO:
469 			if (strncmp(line, "host=", 5) == 0)
470 				connect_set_field(&connect_server,
471 						  line + 5);
472 			else if (strncmp(line, "port=", 5) == 0)
473 				connect_set_field(&connect_port, line + 5);
474 			else if (strcmp(line, "started") == 0) {
475 				if (connect_get_server() != NULL
476 				    && connect_get_port() != NULL) {
477 					create_mode =
478 					    CREATE_MODE_STARTING_OK;
479 				} else {
480 					create_mode =
481 					    CREATE_MODE_STARTING_ERROR;
482 				}
483 				net_close(ses);
484 				metaserver_info.session = NULL;
485 			} else
486 				log_message(MSG_ERROR,
487 					    _("Unknown message from the "
488 					      "metaserver: %s\n"), line);
489 			break;
490 		case CREATE_MODE_STARTING_OK:
491 		case CREATE_MODE_STARTING_ERROR:
492 			log_message(MSG_ERROR,
493 				    _("Unknown message from the "
494 				      "metaserver: %s\n"), line);
495 			break;
496 		}
497 	}
498 }
499 
500 /* -------------------- get running servers info -------------------- */
501 
check_str_info(const gchar * line,const gchar * prefix,gchar * data)502 static gboolean check_str_info(const gchar * line, const gchar * prefix,
503 			       gchar * data)
504 {
505 	size_t len = strlen(prefix);
506 
507 	if (strncmp(line, prefix, len) != 0)
508 		return FALSE;
509 	strncpy(data, line + len, STRARG_LEN);
510 	return TRUE;
511 }
512 
check_int_info(const gchar * line,const gchar * prefix,gchar * data)513 static gboolean check_int_info(const gchar * line, const gchar * prefix,
514 			       gchar * data)
515 {
516 	size_t len = strlen(prefix);
517 
518 	if (strncmp(line, prefix, len) != 0)
519 		return FALSE;
520 	sprintf(data, "%d", atoi(line + len));
521 	return TRUE;
522 }
523 
server_end(void)524 static void server_end(void)
525 {
526 	GtkTreeIter iter;
527 
528 	if (meta_dlg) {
529 		gtk_list_store_append(meta_games_model, &iter);
530 		gtk_list_store_set(meta_games_model, &iter,
531 				   C_META_HOST, server_host,
532 				   C_META_PORT, server_port,
533 				   C_META_HOST_SORTABLE,
534 				   g_strdup_printf("%s:%s", server_host,
535 						   server_port),
536 				   C_META_VERSION, server_version,
537 				   C_META_MAX, server_max, C_META_CUR,
538 				   server_curr, C_META_TERRAIN,
539 				   server_terrain, C_META_VICTORY,
540 				   server_vpoints, C_META_SEVENS,
541 				   server_sevenrule, C_META_MAP,
542 				   server_title, -1);
543 	}
544 }
545 
meta_free_session(Session * ses)546 static void meta_free_session(Session * ses)
547 {
548 	if (ses == metaserver_info.session) {
549 		metaserver_info.session = NULL;
550 	}
551 	net_free(&ses);
552 }
553 
554 /* Developer note: very similar code exists in server/meta.c
555  * Keep both routines up-to-date
556  */
meta_notify(Session * ses,NetEvent event,const gchar * line,G_GNUC_UNUSED gpointer user_data)557 static void meta_notify(Session * ses,
558 			NetEvent event, const gchar * line,
559 			G_GNUC_UNUSED gpointer user_data)
560 {
561 	gchar argument[STRARG_LEN];
562 	switch (event) {
563 	case NET_READ:
564 		if (ses != metaserver_info.session) {
565 			log_message(MSG_ERROR,
566 				    _("Receiving data from inactive "
567 				      "session: %s\n"), line);
568 			return;
569 		}
570 		switch (meta_mode) {
571 		case MODE_SIGNON:
572 		case MODE_REDIRECT:
573 			if (strncmp(line, "goto ", 5) == 0) {
574 				gchar **split_result;
575 				const gchar *port;
576 				if (metaserver_info.num_redirects++ >= 10) {
577 					log_message(MSG_ERROR,
578 						    _("Too many "
579 						      "metaserver "
580 						      "redirects.\n"));
581 					meta_mode = MODE_REDIRECT_OVERFLOW;
582 					net_close(ses);
583 					return;
584 				}
585 				meta_mode = MODE_REDIRECT;
586 				meta_free_session(ses);
587 				split_result = g_strsplit(line, " ", 0);
588 				g_assert(split_result[0] != NULL);
589 				g_assert(!strcmp(split_result[0], "goto"));
590 				if (split_result[1]) {
591 					g_free(metaserver_info.server);
592 					g_free(metaserver_info.port);
593 
594 					metaserver_info.server =
595 					    g_strdup(split_result[1]);
596 
597 					port = PIONEERS_DEFAULT_META_PORT;
598 					if (split_result[2])
599 						port = split_result[2];
600 					metaserver_info.port =
601 					    g_strdup(port);
602 					query_metaserver
603 					    (metaserver_info.server,
604 					     metaserver_info.port);
605 				} else {
606 					log_message(MSG_ERROR,
607 						    _("Bad redirect line: "
608 						      "%s\n"), line);
609 				};
610 				g_strfreev(split_result);
611 				break;
612 			}
613 
614 			metaserver_info.version_major = 0;
615 			metaserver_info.version_minor = 0;
616 			metaserver_info.can_create_games = FALSE;
617 			if (strncmp(line, "welcome ", 8) == 0) {
618 				char *p = strstr(line, "version ");
619 				if (p) {
620 					p += 8;
621 					metaserver_info.version_major =
622 					    atoi(p);
623 					p += strspn(p, "0123456789");
624 					if (*p == '.')
625 						metaserver_info.
626 						    version_minor =
627 						    atoi(p + 1);
628 				}
629 			}
630 			if (metaserver_info.version_major < 1) {
631 				log_message(MSG_INFO,
632 					    _("The metaserver is too old "
633 					      "to create servers "
634 					      "(version %d.%d)\n"),
635 					    metaserver_info.version_major,
636 					    metaserver_info.version_minor);
637 			} else {
638 				net_printf(ses, "version %s\n",
639 					   META_PROTOCOL_VERSION);
640 			}
641 
642 			if ((metaserver_info.version_major > 1) ||
643 			    (metaserver_info.version_major == 1
644 			     && metaserver_info.version_minor >= 1)) {
645 				net_printf(ses, "capability\n");
646 				meta_mode = MODE_CAPABILITY;
647 			} else {
648 				net_printf(ses,
649 					   metaserver_info.version_major >=
650 					   1 ? "listservers\n" :
651 					   "client\n");
652 				meta_mode = MODE_LIST;
653 			}
654 			break;
655 		case MODE_CAPABILITY:
656 			if (!strcmp(line, "create games")) {
657 				metaserver_info.can_create_games = TRUE;
658 			} else if (!strcmp(line, "end")) {
659 				net_printf(ses,
660 					   metaserver_info.version_major >=
661 					   1 ? "listservers\n" :
662 					   "client\n");
663 				meta_mode = MODE_LIST;
664 			}
665 			break;
666 		case MODE_LIST:
667 			if (strcmp(line, "server") == 0) {
668 				meta_mode = MODE_SERVER_INFO;
669 			} else {
670 				log_message(MSG_ERROR,
671 					    _("Unexpected data from the "
672 					      "metaserver: %s\n"), line);
673 			}
674 			break;
675 		case MODE_SERVER_INFO:
676 			if (strcmp(line, "end") == 0) {
677 				server_end();
678 				meta_mode = MODE_LIST;
679 			} else
680 			    if (check_str_info(line, "host=", server_host))
681 				break;
682 			else if (check_str_info
683 				 (line, "port=", server_port))
684 				break;
685 			else if (check_str_info
686 				 (line, "version=", server_version))
687 				break;
688 			else if (check_int_info(line, "max=", server_max))
689 				break;
690 			else if (check_int_info
691 				 (line, "curr=", server_curr))
692 				break;
693 			else if (check_str_info
694 				 (line, "vpoints=", server_vpoints))
695 				break;
696 			else if (check_str_info
697 				 (line, "sevenrule=", argument)) {
698 				if (server_sevenrule)
699 					g_free(server_sevenrule);
700 				if (!strcmp(argument, "normal")) {
701 					server_sevenrule =
702 					    g_strdup(_("Normal"));
703 				} else
704 				    if (!strcmp
705 					(argument, "reroll first 2")) {
706 					server_sevenrule =
707 					    g_strdup(_(""
708 						       "Reroll on 1st 2 turns"));
709 				} else if (!strcmp(argument, "reroll all")) {
710 					server_sevenrule =
711 					    g_strdup(_("Reroll all 7s"));
712 				} else {
713 					g_warning("Unknown seven rule: %s",
714 						  argument);
715 					server_sevenrule =
716 					    g_strdup(argument);
717 				}
718 				break;
719 			} else if (check_str_info
720 				   (line, "terrain=", argument)) {
721 				if (server_terrain)
722 					g_free(server_terrain);
723 				if (!strcmp(argument, "default"))
724 					server_terrain =
725 					    g_strdup(_("Default"));
726 				else if (!strcmp(argument, "random"))
727 					server_terrain =
728 					    g_strdup(_("Random"));
729 				else {
730 					g_warning
731 					    ("Unknown terrain type: %s",
732 					     argument);
733 					server_terrain =
734 					    g_strdup(argument);
735 				}
736 				break;
737 			} else
738 			    if (check_str_info
739 				(line, "title=", server_title))
740 				break;
741 			/* meta-protocol 0 compat */
742 			else if (check_str_info
743 				 (line, "map=", server_terrain))
744 				break;
745 			else if (check_str_info
746 				 (line, "comment=", server_title))
747 				break;
748 			else
749 				log_message(MSG_ERROR,
750 					    _("Unexpected data from the "
751 					      "metaserver: %s\n"), line);
752 			break;
753 		case MODE_DONE:
754 		case MODE_REDIRECT_OVERFLOW:
755 			log_message(MSG_ERROR,
756 				    _("Unexpected data from the "
757 				      "metaserver: %s\n"), line);
758 			break;
759 		}
760 		break;
761 	case NET_CLOSE:
762 		/* During a reconnect, different sessions might co-exist */
763 		if (ses == metaserver_info.session) {
764 			metaserver_info.session = NULL;
765 			switch (meta_mode) {
766 			case MODE_SIGNON:
767 			case MODE_REDIRECT:
768 			case MODE_SERVER_INFO:
769 			case MODE_CAPABILITY:
770 				log_message(MSG_ERROR,
771 					    _("The metaserver closed "
772 					      "the connection "
773 					      "unexpectedly.\n"));
774 				break;
775 			case MODE_REDIRECT_OVERFLOW:
776 				if (meta_dlg)
777 					gtk_widget_destroy(GTK_WIDGET(meta_dlg));	/* Close the dialog */
778 				if (ses == metaserver_info.session) {
779 					metaserver_info.session = NULL;
780 				}
781 				break;
782 			case MODE_LIST:
783 			case MODE_DONE:
784 				close_waiting_box();
785 				break;
786 			}
787 			if (meta_dlg) {
788 				gtk_dialog_set_response_sensitive
789 				    (GTK_DIALOG(meta_dlg),
790 				     META_RESPONSE_NEW,
791 				     metaserver_info.can_create_games);
792 				gtk_dialog_set_response_sensitive
793 				    (GTK_DIALOG(meta_dlg),
794 				     META_RESPONSE_REFRESH, TRUE);
795 			}
796 		}
797 		net_free(&ses);
798 		break;
799 	case NET_CONNECT:
800 		break;
801 	case NET_CONNECT_FAIL:
802 		/* Can't connect to the metaserver, don't show the GUI */
803 		if (meta_dlg)
804 			gtk_widget_destroy(GTK_WIDGET(meta_dlg));
805 		if (ses == metaserver_info.session) {
806 			metaserver_info.session = NULL;
807 		}
808 		net_free(&ses);
809 		break;
810 	}
811 }
812 
query_metaserver(const gchar * server,const gchar * port)813 static void query_metaserver(const gchar * server, const gchar * port)
814 {
815 	gchar *message;
816 
817 	if (metaserver_info.num_redirects > 0) {
818 		if (strcmp(port, PIONEERS_DEFAULT_META_PORT) == 0) {
819 			message =
820 			    g_strdup_printf(_("Redirected to the "
821 					      "metaserver at %s.\n"),
822 					    server);
823 		} else {
824 			message =
825 			    g_strdup_printf(_("Redirected to the "
826 					      "metaserver at %s, port %s.\n"),
827 					    server, port);
828 		}
829 	} else
830 		message =
831 		    g_strdup(_
832 			     ("Receiving a list of Pioneers servers "
833 			      "from the metaserver.\n"));
834 	show_waiting_box(message, server, port);
835 	g_free(message);
836 
837 	g_assert(metaserver_info.session == NULL);
838 	metaserver_info.session = net_new(meta_notify, NULL);
839 	if (net_connect(metaserver_info.session, server, port))
840 		meta_mode = MODE_SIGNON;
841 	else {
842 		net_free(&metaserver_info.session);
843 		close_waiting_box();
844 	}
845 }
846 
847 /* -------------------- create server dialog -------------------- */
848 
player_change_cb(GameSettings * gs,G_GNUC_UNUSED gpointer user_data)849 static void player_change_cb(GameSettings * gs,
850 			     G_GNUC_UNUSED gpointer user_data)
851 {
852 	guint players;
853 	guint ai_players;
854 	GtkSpinButton *ai_spin;
855 
856 	ai_spin = GTK_SPIN_BUTTON(aiplayers_spin);
857 	players = game_settings_get_players(gs);
858 	ai_players = (guint) gtk_spin_button_get_value_as_int(ai_spin);
859 	gtk_spin_button_set_range(ai_spin, 0, players - 1);
860 	if (ai_players >= players)
861 		gtk_spin_button_set_value(ai_spin, players - 1);
862 }
863 
game_select_cb(SelectGame * sg,G_GNUC_UNUSED gpointer user_data)864 static void game_select_cb(SelectGame * sg,
865 			   G_GNUC_UNUSED gpointer user_data)
866 {
867 	const GameParams *params;
868 
869 	params = select_game_get_active_game(sg);
870 
871 	if (params != NULL) {
872 		game_settings_set_players(GAMESETTINGS(game_settings),
873 					  params->num_players);
874 		game_settings_set_victory_points(GAMESETTINGS
875 						 (game_settings),
876 						 params->victory_points);
877 		game_rules_set_random_terrain(GAMERULES(game_rules),
878 					      params->random_terrain);
879 		game_rules_set_sevens_rule(GAMERULES(game_rules),
880 					   params->sevens_rule);
881 
882 		player_change_cb(GAMESETTINGS(game_settings), NULL);
883 	}
884 }
885 
build_create_interface(void)886 static GtkWidget *build_create_interface(void)
887 {
888 	GtkWidget *vbox;
889 	GtkWidget *label;
890 	GtkAdjustment *adj;
891 	guint row;
892 
893 	vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
894 	gtk_widget_show(vbox);
895 	gtk_container_set_border_width(GTK_CONTAINER(vbox), 3);
896 
897 	select_game = select_game_new();
898 	gtk_widget_show(select_game);
899 	gtk_box_pack_start(GTK_BOX(vbox), select_game, FALSE, FALSE, 3);
900 
901 	g_signal_connect(G_OBJECT(select_game), "activate",
902 			 G_CALLBACK(game_select_cb), NULL);
903 
904 	game_settings = game_settings_new(FALSE);
905 	gtk_widget_show(game_settings);
906 	gtk_box_pack_start(GTK_BOX(vbox), game_settings, FALSE, FALSE, 3);
907 
908 	/* Dynamically adjust the maximum number of AI's */
909 	g_signal_connect(G_OBJECT(game_settings),
910 			 "change-players",
911 			 G_CALLBACK(player_change_cb), NULL);
912 
913 	/* Add the number of computer players at the end of the grid */
914 	row = 99;
915 	/* Label */
916 	label = gtk_label_new(_("Number of computer players"));
917 	gtk_widget_show(label);
918 	gtk_grid_attach(GTK_GRID(game_settings), label, 0, row, 1, 1);
919 	gtk_label_set_xalign(GTK_LABEL(label), 0.0);
920 
921 	adj = GTK_ADJUSTMENT(gtk_adjustment_new(0,
922 						0,
923 						game_settings_get_players
924 						(GAMESETTINGS
925 						 (game_settings))
926 						- 1, 1, 4, 0));
927 	aiplayers_spin = gtk_spin_button_new(adj, 1, 0);
928 	gtk_spin_button_set_numeric(GTK_SPIN_BUTTON(aiplayers_spin), TRUE);
929 	gtk_widget_show(aiplayers_spin);
930 	gtk_entry_set_alignment(GTK_ENTRY(aiplayers_spin), 1.0);
931 	gtk_grid_attach(GTK_GRID(game_settings), aiplayers_spin, 1, row, 1,
932 			1);
933 	gtk_widget_set_tooltip_text(aiplayers_spin,
934 				    /* Tooltip */
935 				    _("The number of computer players"));
936 
937 	game_rules = game_rules_new_metaserver();
938 	gtk_widget_show(game_rules);
939 	gtk_box_pack_start(GTK_BOX(vbox), game_rules, FALSE, FALSE, 3);
940 
941 	return vbox;
942 }
943 
create_server_dlg_cb(GtkDialog * dlg,gint arg1,G_GNUC_UNUSED gpointer user_data)944 static void create_server_dlg_cb(GtkDialog * dlg, gint arg1,
945 				 G_GNUC_UNUSED gpointer user_data)
946 {
947 	GameSettings *gs = GAMESETTINGS(game_settings);
948 	SelectGame *sg = SELECTGAME(select_game);
949 	GameRules *gr = GAMERULES(game_rules);
950 
951 	switch (arg1) {
952 	case GTK_RESPONSE_OK:
953 		gtk_dialog_set_response_sensitive(dlg, GTK_RESPONSE_OK,
954 						  FALSE);
955 
956 		if (metaserver_info.session != NULL) {
957 			log_message(MSG_INFO, _("Canceled.\n"));
958 			net_close(metaserver_info.session);
959 		}
960 		log_message(MSG_INFO, _("Requesting new game server.\n"));
961 
962 		cfg_terrain = game_rules_get_random_terrain(gr);
963 		cfg_num_players = game_settings_get_players(gs);
964 		cfg_victory_points = game_settings_get_victory_points(gs);
965 		cfg_sevens_rule = game_rules_get_sevens_rule(gr);
966 		cfg_ai_players = (guint)
967 		    gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON
968 						     (aiplayers_spin));
969 		cfg_gametype = select_game_get_active_title(sg);
970 
971 		metaserver_info.session =
972 		    net_new(meta_create_notify, NULL);
973 		if (net_connect
974 		    (metaserver_info.session, metaserver_info.server,
975 		     metaserver_info.port)) {
976 			create_mode = CREATE_MODE_SIGNON;
977 		} else {
978 			log_message(MSG_ERROR,
979 				    _("The metaserver is not available "
980 				      "anymore.\n"));
981 			net_free(&metaserver_info.session);
982 			gtk_widget_destroy(GTK_WIDGET(dlg));
983 		}
984 		break;
985 	case GTK_RESPONSE_CANCEL:
986 	default:		/* For the compiler */
987 		gtk_widget_destroy(GTK_WIDGET(dlg));
988 		if (metaserver_info.session != NULL) {
989 			net_close(metaserver_info.session);
990 			/* Canceled retrieving information
991 			 * from the metaserver */
992 			log_message(MSG_INFO, _("Canceled.\n"));
993 		}
994 		break;
995 	};
996 }
997 
998 /** Launch the server gtk. */
launch_server_gtk(G_GNUC_UNUSED GtkWidget * widget,G_GNUC_UNUSED GtkWindow * parent)999 static void launch_server_gtk(G_GNUC_UNUSED GtkWidget * widget,
1000 			      G_GNUC_UNUSED GtkWindow * parent)
1001 {
1002 	gchar *child_argv[3];
1003 	GSpawnFlags flags = G_SPAWN_STDOUT_TO_DEV_NULL |
1004 	    G_SPAWN_STDERR_TO_DEV_NULL | G_SPAWN_SEARCH_PATH;
1005 	GError *error = NULL;
1006 	gint i;
1007 
1008 	child_argv[0] = g_strdup(PIONEERS_SERVER_GTK_PROGRAM_NAME);
1009 	child_argv[1] = g_strdup(PIONEERS_SERVER_GTK_PROGRAM_NAME);
1010 	child_argv[2] = NULL;
1011 	if (!g_spawn_async(NULL, child_argv, NULL, flags, NULL, NULL, NULL,
1012 			   &error)) {
1013 		log_message(MSG_ERROR,
1014 			    /* Error message when program %1 is started, reason is %2 */
1015 			    _("Error starting %s: %s\n"),
1016 			    child_argv[0], error->message);
1017 		g_error_free(error);
1018 	}
1019 	for (i = 0; child_argv[i] != NULL; i++)
1020 		g_free(child_argv[i]);
1021 }
1022 
create_server_dlg(G_GNUC_UNUSED GtkWidget * widget,GtkWindow * parent)1023 static void create_server_dlg(G_GNUC_UNUSED GtkWidget * widget,
1024 			      GtkWindow * parent)
1025 {
1026 	GtkWidget *dlg_vbox;
1027 	GtkWidget *vbox;
1028 
1029 	if (cserver_dlg) {
1030 		gtk_window_present(GTK_WINDOW(cserver_dlg));
1031 		return;
1032 	}
1033 	set_metaserver_info();
1034 
1035 	cserver_dlg =
1036 	    /* Dialog caption */
1037 	    gtk_dialog_new_with_buttons(_("Create a Public Game"), parent,
1038 					GTK_DIALOG_DESTROY_WITH_PARENT,
1039 					/* Button text */
1040 					_("_Cancel"), GTK_RESPONSE_CANCEL,
1041 					/* Button text */
1042 					_("C_reate"), GTK_RESPONSE_OK,
1043 					NULL);
1044 	g_signal_connect(G_OBJECT(cserver_dlg), "destroy",
1045 			 G_CALLBACK(gtk_widget_destroyed), &cserver_dlg);
1046 	gtk_widget_realize(cserver_dlg);
1047 
1048 	dlg_vbox = gtk_dialog_get_content_area(GTK_DIALOG(cserver_dlg));
1049 	gtk_widget_show(dlg_vbox);
1050 
1051 	vbox = build_create_interface();
1052 	gtk_widget_show(vbox);
1053 	gtk_box_pack_start(GTK_BOX(dlg_vbox), vbox, TRUE, TRUE, 0);
1054 	gtk_container_set_border_width(GTK_CONTAINER(vbox), 5);
1055 
1056 	if (cserver_dlg != NULL) {
1057 		gtk_dialog_set_response_sensitive(GTK_DIALOG(cserver_dlg),
1058 						  GTK_RESPONSE_OK, FALSE);
1059 	}
1060 	g_signal_connect(G_OBJECT(cserver_dlg), "response",
1061 			 G_CALLBACK(create_server_dlg_cb), NULL);
1062 	gtk_widget_show(cserver_dlg);
1063 
1064 	get_metaserver_games_types(metaserver_info.server,
1065 				   metaserver_info.port);
1066 
1067 }
1068 
1069 /* -------------------- select server dialog -------------------- */
1070 
meta_click_cb(G_GNUC_UNUSED GtkWidget * widget,G_GNUC_UNUSED GdkEventButton * event,G_GNUC_UNUSED gpointer user_data)1071 static gint meta_click_cb(G_GNUC_UNUSED GtkWidget * widget,
1072 			  G_GNUC_UNUSED GdkEventButton * event,
1073 			  G_GNUC_UNUSED gpointer user_data)
1074 {
1075 	if (event->type == GDK_2BUTTON_PRESS) {
1076 		gtk_dialog_response(GTK_DIALOG(meta_dlg), GTK_RESPONSE_OK);
1077 	};
1078 	return FALSE;
1079 }
1080 
meta_select_cb(G_GNUC_UNUSED GtkTreeSelection * selection,G_GNUC_UNUSED gpointer user_data)1081 static void meta_select_cb(G_GNUC_UNUSED GtkTreeSelection * selection,
1082 			   G_GNUC_UNUSED gpointer user_data)
1083 {
1084 	gtk_dialog_set_response_sensitive(GTK_DIALOG(meta_dlg),
1085 					  GTK_RESPONSE_OK, TRUE);
1086 }
1087 
meta_dlg_cb(GtkDialog * dlg,gint arg1,G_GNUC_UNUSED gpointer userdata)1088 static void meta_dlg_cb(GtkDialog * dlg, gint arg1,
1089 			G_GNUC_UNUSED gpointer userdata)
1090 {
1091 	GtkTreePath *path;
1092 	GtkTreeViewColumn *column;
1093 	GtkTreeIter iter;
1094 	gchar *host;
1095 	gchar *port;
1096 
1097 	switch (arg1) {
1098 	case META_RESPONSE_REFRESH:	/* Refresh the list */
1099 		gtk_list_store_clear(meta_games_model);
1100 		metaserver_info.num_redirects = 0;
1101 		query_metaserver(metaserver_info.server,
1102 				 metaserver_info.port);
1103 		break;
1104 	case META_RESPONSE_NEW:	/* Add a server */
1105 		create_server_dlg(NULL, GTK_WINDOW(dlg));
1106 		break;
1107 	case GTK_RESPONSE_OK:	/* Select this server */
1108 		gtk_tree_view_get_cursor(GTK_TREE_VIEW(meta_games_view),
1109 					 &path, &column);
1110 		if (path != NULL) {
1111 			gtk_tree_model_get_iter(GTK_TREE_MODEL
1112 						(meta_games_model), &iter,
1113 						path);
1114 			gtk_tree_model_get(GTK_TREE_MODEL
1115 					   (meta_games_model), &iter,
1116 					   C_META_HOST, &host, C_META_PORT,
1117 					   &port, -1);
1118 			connect_set_field(&connect_server, host);
1119 			connect_set_field(&connect_port, port);
1120 			g_free(host);
1121 			g_free(port);
1122 			gtk_tree_path_free(path);
1123 			connect_close_all(TRUE, TRUE);
1124 		}
1125 		break;
1126 	case GTK_RESPONSE_CANCEL:	/* Cancel */
1127 	default:
1128 		gtk_widget_destroy(GTK_WIDGET(dlg));
1129 		if (metaserver_info.session != NULL) {
1130 			net_close(metaserver_info.session);
1131 			/* Canceled retrieving information
1132 			 * from the metaserver */
1133 			log_message(MSG_INFO, _("Canceled.\n"));
1134 		}
1135 		break;
1136 	}
1137 }
1138 
set_metaserver_info(void)1139 static void set_metaserver_info(void)
1140 {
1141 	gchar *meta_tmp;
1142 
1143 	g_free(metaserver_info.server);
1144 	g_free(metaserver_info.port);
1145 
1146 	meta_tmp = metaserver_get(METASERVER(metaserver_entry));
1147 	metaserver_info.server = meta_tmp;	/* Take-over of the pointer */
1148 	metaserver_info.port = g_strdup(PIONEERS_DEFAULT_META_PORT);
1149 }
1150 
create_meta_dlg(G_GNUC_UNUSED GtkWidget * widget,GtkWidget * parent)1151 static void create_meta_dlg(G_GNUC_UNUSED GtkWidget * widget,
1152 			    GtkWidget * parent)
1153 {
1154 	GtkWidget *dlg_vbox;
1155 	GtkWidget *vbox;
1156 	GtkWidget *scroll_win;
1157 	GtkTreeViewColumn *column;
1158 	GtkCellRenderer *renderer;
1159 
1160 	set_metaserver_info();
1161 
1162 	if (meta_dlg != NULL) {
1163 		if (metaserver_info.session == NULL) {
1164 			gtk_list_store_clear(meta_games_model);
1165 			metaserver_info.num_redirects = 0;
1166 			metaserver_info.version_major = 0;
1167 			metaserver_info.version_minor = 0;
1168 			metaserver_info.can_create_games = FALSE;
1169 			gtk_dialog_set_response_sensitive
1170 			    (GTK_DIALOG(meta_dlg),
1171 			     META_RESPONSE_NEW,
1172 			     metaserver_info.can_create_games);
1173 
1174 			query_metaserver(metaserver_info.server,
1175 					 metaserver_info.port);
1176 		}
1177 		gtk_window_present(GTK_WINDOW(meta_dlg));
1178 		return;
1179 	}
1180 
1181 	/* Dialog caption */
1182 	meta_dlg = gtk_dialog_new_with_buttons(_("Join a Public Game"),
1183 					       GTK_WINDOW(parent), 0,
1184 					       /* Button text */
1185 					       _("_Refresh"),
1186 					       META_RESPONSE_REFRESH,
1187 					       /* Button text */
1188 					       _("_New Remote Game"),
1189 					       META_RESPONSE_NEW,
1190 					       /* Button text */
1191 					       _("_Cancel"),
1192 					       GTK_RESPONSE_CANCEL,
1193 					       /* Button text */
1194 					       _("_Join"),
1195 					       GTK_RESPONSE_OK, NULL);
1196 	g_signal_connect(G_OBJECT(meta_dlg), "destroy",
1197 			 G_CALLBACK(gtk_widget_destroyed), &meta_dlg);
1198 	g_signal_connect(G_OBJECT(meta_dlg), "response",
1199 			 G_CALLBACK(meta_dlg_cb), NULL);
1200 	gtk_widget_realize(meta_dlg);
1201 
1202 	dlg_vbox = gtk_dialog_get_content_area(GTK_DIALOG(meta_dlg));
1203 	gtk_widget_show(dlg_vbox);
1204 
1205 	vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 2);
1206 	gtk_widget_show(vbox);
1207 	gtk_box_pack_start(GTK_BOX(dlg_vbox), vbox, TRUE, TRUE, 0);
1208 	gtk_container_set_border_width(GTK_CONTAINER(vbox), 5);
1209 
1210 	server_status = gtk_label_new("");
1211 	gtk_widget_show(server_status);
1212 	gtk_box_pack_start(GTK_BOX(vbox), server_status, FALSE, TRUE, 0);
1213 
1214 	scroll_win = gtk_scrolled_window_new(NULL, NULL);
1215 	gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroll_win),
1216 				       GTK_POLICY_NEVER,
1217 				       GTK_POLICY_AUTOMATIC);
1218 	gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW
1219 					    (scroll_win), GTK_SHADOW_IN);
1220 	gtk_widget_show(scroll_win);
1221 	gtk_box_pack_start(GTK_BOX(vbox), scroll_win, TRUE, TRUE, 0);
1222 
1223 	meta_games_model = gtk_list_store_new(META_N_COLUMNS,
1224 					      G_TYPE_STRING, G_TYPE_STRING,
1225 					      G_TYPE_STRING, G_TYPE_STRING,
1226 					      G_TYPE_STRING, G_TYPE_STRING,
1227 					      G_TYPE_STRING, G_TYPE_STRING,
1228 					      G_TYPE_STRING,
1229 					      G_TYPE_STRING);
1230 
1231 	meta_games_view =
1232 	    gtk_tree_view_new_with_model(GTK_TREE_MODEL(meta_games_model));
1233 	gtk_widget_show(meta_games_view);
1234 	gtk_container_add(GTK_CONTAINER(scroll_win), meta_games_view);
1235 	gtk_widget_set_tooltip_text(meta_games_view,
1236 				    /* Tooltip */
1237 				    _("Select a game to join"));
1238 
1239 	column =
1240 	    /* Column name */
1241 	    gtk_tree_view_column_new_with_attributes(_("Map Name"),
1242 						     gtk_cell_renderer_text_new
1243 						     (), "text",
1244 						     C_META_MAP, NULL);
1245 	gtk_tree_view_append_column(GTK_TREE_VIEW(meta_games_view),
1246 				    column);
1247 	gtk_tree_view_column_set_sort_column_id(column, C_META_MAP);
1248 	/* Tooltip for column 'Map Name' */
1249 	set_tooltip_on_column(column, _("Name of the game"));
1250 
1251 	renderer = gtk_cell_renderer_text_new();
1252 	g_object_set(renderer, "xalign", 1.0f, NULL);
1253 	column =
1254 	    /* Column name */
1255 	    gtk_tree_view_column_new_with_attributes(_("Curr"), renderer,
1256 						     "text", C_META_CUR,
1257 						     NULL);
1258 	gtk_tree_view_append_column(GTK_TREE_VIEW(meta_games_view),
1259 				    column);
1260 	gtk_tree_view_column_set_sort_column_id(column, C_META_CUR);
1261 	/* Tooltip for column 'Curr' */
1262 	set_tooltip_on_column(column, _("Number of players in the game"));
1263 
1264 	renderer = gtk_cell_renderer_text_new();
1265 	g_object_set(renderer, "xalign", 1.0f, NULL);
1266 	column =
1267 	    /* Column name */
1268 	    gtk_tree_view_column_new_with_attributes(_("Max"), renderer,
1269 						     "text", C_META_MAX,
1270 						     NULL);
1271 	gtk_tree_view_append_column(GTK_TREE_VIEW(meta_games_view),
1272 				    column);
1273 	gtk_tree_view_column_set_sort_column_id(column, C_META_MAX);
1274 	/* Tooltip for column 'Max' */
1275 	set_tooltip_on_column(column, _("Maximum players for the game"));
1276 
1277 	column =
1278 	    /* Column name */
1279 	    gtk_tree_view_column_new_with_attributes(_("Terrain"),
1280 						     gtk_cell_renderer_text_new
1281 						     (), "text",
1282 						     C_META_TERRAIN, NULL);
1283 	gtk_tree_view_append_column(GTK_TREE_VIEW(meta_games_view),
1284 				    column);
1285 	gtk_tree_view_column_set_sort_column_id(column, C_META_TERRAIN);
1286 	/* Tooltip for column 'Terrain' */
1287 	set_tooltip_on_column(column, _("Random of default terrain"));
1288 
1289 	renderer = gtk_cell_renderer_text_new();
1290 	g_object_set(renderer, "xalign", 1.0f, NULL);
1291 	column =
1292 	    /* Column name */
1293 	    gtk_tree_view_column_new_with_attributes(_("Vic. Points"),
1294 						     renderer, "text",
1295 						     C_META_VICTORY, NULL);
1296 	gtk_tree_view_append_column(GTK_TREE_VIEW(meta_games_view),
1297 				    column);
1298 	gtk_tree_view_column_set_sort_column_id(column, C_META_VICTORY);
1299 	/* Tooltip for column 'Vic. Points' */
1300 	set_tooltip_on_column(column, _("Points needed to win"));
1301 
1302 	column =
1303 	    /* Column name */
1304 	    gtk_tree_view_column_new_with_attributes(_("Sevens Rule"),
1305 						     gtk_cell_renderer_text_new
1306 						     (), "text",
1307 						     C_META_SEVENS, NULL);
1308 	gtk_tree_view_append_column(GTK_TREE_VIEW(meta_games_view),
1309 				    column);
1310 	gtk_tree_view_column_set_sort_column_id(column, C_META_SEVENS);
1311 	/* Tooltip for column 'Sevens Rule' */
1312 	set_tooltip_on_column(column, _("Sevens rule"));
1313 
1314 	column =
1315 	    /* Column name */
1316 	    gtk_tree_view_column_new_with_attributes(_("Host"),
1317 						     gtk_cell_renderer_text_new
1318 						     (), "text",
1319 						     C_META_HOST, NULL);
1320 	gtk_tree_view_append_column(GTK_TREE_VIEW(meta_games_view),
1321 				    column);
1322 	gtk_tree_view_column_set_sort_column_id(column,
1323 						C_META_HOST_SORTABLE);
1324 	/* Tooltip for column 'Host' */
1325 	set_tooltip_on_column(column, _("Host of the game"));
1326 
1327 	renderer = gtk_cell_renderer_text_new();
1328 	g_object_set(renderer, "xalign", 1.0f, NULL);
1329 	column =
1330 	    /* Column name */
1331 	    gtk_tree_view_column_new_with_attributes(_("Port"), renderer,
1332 						     "text", C_META_PORT,
1333 						     NULL);
1334 	gtk_tree_view_append_column(GTK_TREE_VIEW(meta_games_view),
1335 				    column);
1336 	gtk_tree_view_column_set_sort_column_id(column,
1337 						C_META_HOST_SORTABLE);
1338 	/* Tooltip for column 'Port' */
1339 	set_tooltip_on_column(column, _("Port of the game"));
1340 
1341 	column =
1342 	    /* Column name */
1343 	    gtk_tree_view_column_new_with_attributes(_("Version"),
1344 						     gtk_cell_renderer_text_new
1345 						     (), "text",
1346 						     C_META_VERSION, NULL);
1347 	gtk_tree_view_append_column(GTK_TREE_VIEW(meta_games_view),
1348 				    column);
1349 	gtk_tree_view_column_set_sort_column_id(column, C_META_VERSION);
1350 	/* Tooltip for column 'Version' */
1351 	set_tooltip_on_column(column, _("Version of the host"));
1352 	gtk_tree_sortable_set_sort_column_id(GTK_TREE_SORTABLE
1353 					     (meta_games_model),
1354 					     C_META_HOST_SORTABLE,
1355 					     GTK_SORT_ASCENDING);
1356 
1357 	/* Register double-click */
1358 	g_signal_connect(G_OBJECT(meta_games_view), "button_press_event",
1359 			 G_CALLBACK(meta_click_cb), NULL);
1360 
1361 	g_signal_connect(G_OBJECT
1362 			 (gtk_tree_view_get_selection
1363 			  (GTK_TREE_VIEW(meta_games_view))), "changed",
1364 			 G_CALLBACK(meta_select_cb), NULL);
1365 
1366 	/* This button will be enabled when a game is selected */
1367 	gtk_dialog_set_response_sensitive(GTK_DIALOG(meta_dlg),
1368 					  GTK_RESPONSE_OK, FALSE);
1369 
1370 	/* These buttons will be enabled when the metaserver has responded */
1371 	gtk_dialog_set_response_sensitive(GTK_DIALOG(meta_dlg),
1372 					  META_RESPONSE_NEW, FALSE);
1373 	gtk_dialog_set_response_sensitive(GTK_DIALOG(meta_dlg),
1374 					  META_RESPONSE_REFRESH, FALSE);
1375 
1376 	gtk_widget_show(meta_dlg);
1377 
1378 	metaserver_info.num_redirects = 0;
1379 	query_metaserver(metaserver_info.server, metaserver_info.port);
1380 
1381 	/* Workaround: Set the size of the widget as late as possible, to avoid a strange display */
1382 	gtk_widget_set_size_request(scroll_win, -1, 150);
1383 }
1384 
connect_dlg_cb(G_GNUC_UNUSED GtkDialog * dlg,G_GNUC_UNUSED gint arg1,G_GNUC_UNUSED gpointer userdata)1385 static void connect_dlg_cb(G_GNUC_UNUSED GtkDialog * dlg,
1386 			   G_GNUC_UNUSED gint arg1,
1387 			   G_GNUC_UNUSED gpointer userdata)
1388 {
1389 	connect_close_all(FALSE, FALSE);
1390 }
1391 
1392 #ifdef HAVE_AVAHI
connect_avahi_cb(G_GNUC_UNUSED GtkWidget * widget,GtkWidget * avahibrowser_entry)1393 static void connect_avahi_cb(G_GNUC_UNUSED GtkWidget * widget,
1394 			     GtkWidget * avahibrowser_entry)
1395 {
1396 	gchar *server =
1397 	    avahibrowser_get_server(AVAHIBROWSER(avahibrowser_entry));
1398 	gchar *port =
1399 	    avahibrowser_get_port(AVAHIBROWSER(avahibrowser_entry));
1400 
1401 	connect_set_field(&connect_server, server);
1402 	connect_set_field(&connect_port, port);
1403 
1404 	g_free(server);
1405 	g_free(port);
1406 
1407 	/* connect */
1408 	connect_close_all(TRUE, TRUE);
1409 }
1410 #endif
1411 
connect_create_dlg(void)1412 void connect_create_dlg(void)
1413 {
1414 	GtkWidget *dlg_vbox;
1415 	GtkWidget *grid;
1416 	GtkWidget *lbl;
1417 	GtkWidget *hbox;
1418 	GtkWidget *btn;
1419 	GtkWidget *sep;
1420 #ifdef HAVE_AVAHI
1421 	GtkWidget *avahibrowser_entry;
1422 #endif				/* HAVE_AVAHI */
1423 	gchar *fullname;
1424 	gchar *name;
1425 	guint row;
1426 
1427 	if (connect_dlg) {
1428 		gtk_window_present(GTK_WINDOW(connect_dlg));
1429 		return;
1430 	}
1431 
1432 	g_signal_connect(requested_name, "changed",
1433 			 G_CALLBACK(connect_name_change_cb), NULL);
1434 
1435 	/* Dialog caption */
1436 	connect_dlg = gtk_dialog_new_with_buttons(_("Start a New Game"),
1437 						  GTK_WINDOW(app_window),
1438 						  0,
1439 						  /* Button text */
1440 						  _("_Cancel"),
1441 						  GTK_RESPONSE_CANCEL,
1442 						  NULL);
1443 	g_signal_connect(G_OBJECT(connect_dlg), "response",
1444 			 G_CALLBACK(connect_dlg_cb), NULL);
1445 	g_signal_connect(G_OBJECT(connect_dlg), "destroy",
1446 			 G_CALLBACK(connect_dlg_destroyed), &connect_dlg);
1447 
1448 	gtk_widget_realize(connect_dlg);
1449 	gdk_window_set_functions(gtk_widget_get_window(connect_dlg),
1450 				 GDK_FUNC_MOVE | GDK_FUNC_CLOSE);
1451 
1452 	dlg_vbox = gtk_dialog_get_content_area(GTK_DIALOG(connect_dlg));
1453 	gtk_widget_show(dlg_vbox);
1454 
1455 	grid = gtk_grid_new();
1456 	row = 0;
1457 	gtk_widget_show(grid);
1458 	gtk_box_pack_start(GTK_BOX(dlg_vbox), grid, FALSE, TRUE, 0);
1459 	gtk_container_set_border_width(GTK_CONTAINER(grid), 5);
1460 	gtk_grid_set_row_spacing(GTK_GRID(grid), 3);
1461 	gtk_grid_set_column_spacing(GTK_GRID(grid), 5);
1462 
1463 	/* Label */
1464 	lbl = gtk_label_new(_("Player name"));
1465 	gtk_widget_show(lbl);
1466 	gtk_grid_attach(GTK_GRID(grid), lbl, 0, row, 1, 1);
1467 	gtk_label_set_xalign(GTK_LABEL(lbl), 0.0);
1468 
1469 	name = notifying_string_get(requested_name);
1470 	name_entry = gtk_entry_new();
1471 	gtk_entry_set_max_length(GTK_ENTRY(name_entry), MAX_NAME_LENGTH);
1472 	gtk_widget_show(name_entry);
1473 	gtk_entry_set_text(GTK_ENTRY(name_entry), name);
1474 	g_free(name);
1475 
1476 	gtk_grid_attach(GTK_GRID(grid), name_entry, 1, row, 1, 1);
1477 	/* Tooltip */
1478 	gtk_widget_set_tooltip_text(name_entry, _("Enter your name"));
1479 
1480 	/* Check button */
1481 	spectator_toggle = gtk_check_button_new_with_label(_("Spectator"));
1482 	gtk_widget_show(spectator_toggle);
1483 	gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(spectator_toggle),
1484 				     connect_spectator);
1485 
1486 	gtk_grid_attach(GTK_GRID(grid), spectator_toggle, 2, row, 1, 1);
1487 	gtk_widget_set_tooltip_text(spectator_toggle,
1488 				    /* Tooltip for checkbox Spectator */
1489 				    _
1490 				    ("Check if you want to be a spectator"));
1491 	row++;
1492 
1493 	sep = gtk_separator_new(GTK_ORIENTATION_HORIZONTAL);
1494 	gtk_widget_show(sep);
1495 	gtk_grid_attach(GTK_GRID(grid), sep, 0, row, 3, 1);
1496 	row++;
1497 
1498 #ifdef HAVE_AVAHI
1499 	/* Label */
1500 	lbl = gtk_label_new(_("Avahi"));
1501 	gtk_widget_show(lbl);
1502 	gtk_grid_attach(GTK_GRID(grid), lbl, 0, row, 1, 1);
1503 	gtk_label_set_xalign(GTK_LABEL(lbl), 0.0);
1504 
1505 	/* Button */
1506 	btn = gtk_button_new_with_label(_("Join"));
1507 	gtk_widget_show(btn);
1508 	gtk_widget_set_tooltip_text(btn,
1509 				    /* Tooltip for button Join */
1510 				    _(""
1511 				      "Join an automatically discovered game"));
1512 	gtk_grid_attach(GTK_GRID(grid), btn, 2, row, 1, 1);
1513 
1514 	avahibrowser_entry = avahibrowser_new(btn);
1515 	gtk_widget_show(avahibrowser_entry);
1516 	gtk_grid_attach(GTK_GRID(grid), avahibrowser_entry, 1, row, 1, 1);
1517 	row++;
1518 
1519 	g_signal_connect(G_OBJECT(btn), "clicked",
1520 			 G_CALLBACK(connect_avahi_cb), avahibrowser_entry);
1521 
1522 	/* enable avahi */
1523 	/* storing the pointer to this widget for later use */
1524 	avahi_register(AVAHIBROWSER(avahibrowser_entry));
1525 
1526 	sep = gtk_separator_new(GTK_ORIENTATION_HORIZONTAL);
1527 	gtk_widget_show(sep);
1528 	gtk_grid_attach(GTK_GRID(grid), sep, 0, row, 3, 1);
1529 	row++;
1530 #endif				/* HAVE_AVAHI */
1531 
1532 	/* Label */
1533 	lbl = gtk_label_new(_("Metaserver"));
1534 	gtk_widget_show(lbl);
1535 	gtk_grid_attach(GTK_GRID(grid), lbl, 0, row, 1, 1);
1536 	gtk_label_set_xalign(GTK_LABEL(lbl), 0.0);
1537 
1538 	metaserver_entry = metaserver_new();
1539 	gtk_widget_show(metaserver_entry);
1540 	gtk_grid_attach(GTK_GRID(grid), metaserver_entry, 1, row, 2, 1);
1541 	metaserver_add(METASERVER(metaserver_entry),
1542 		       metaserver_info.server);
1543 	row++;
1544 
1545 	hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 3);
1546 	gtk_widget_show(hbox);
1547 	gtk_grid_attach(GTK_GRID(grid), hbox, 0, row, 3, 1);
1548 	row++;
1549 
1550 	/* Button */
1551 	btn = gtk_button_new_with_label(_("Join Public Game"));
1552 	gtk_widget_show(btn);
1553 	gtk_box_pack_start(GTK_BOX(hbox), btn, TRUE, TRUE, 0);
1554 	g_signal_connect(G_OBJECT(btn), "clicked",
1555 			 G_CALLBACK(create_meta_dlg), app_window);
1556 	/* Tooltip */
1557 	gtk_widget_set_tooltip_text(btn, _("Join a public game"));
1558 	gtk_widget_set_can_default(btn, TRUE);
1559 	gtk_widget_grab_default(btn);
1560 
1561 	/* Button */
1562 	btn = gtk_button_new_with_label(_("Create Game"));
1563 	gtk_widget_show(btn);
1564 	gtk_box_pack_start(GTK_BOX(hbox), btn, TRUE, TRUE, 0);
1565 	/* Tooltip */
1566 	gtk_widget_set_tooltip_text(btn, _("Create a game"));
1567 	g_signal_connect(G_OBJECT(btn), "clicked",
1568 			 G_CALLBACK(launch_server_gtk), app_window);
1569 	fullname =
1570 	    g_find_program_in_path(PIONEERS_SERVER_GTK_PROGRAM_NAME);
1571 	gtk_widget_set_sensitive(GTK_WIDGET(btn), fullname != NULL);
1572 	g_free(fullname);
1573 
1574 	/* Button */
1575 	btn = gtk_button_new_with_label(_("Join Private Game"));
1576 	gtk_widget_show(btn);
1577 	gtk_box_pack_start(GTK_BOX(hbox), btn, TRUE, TRUE, 0);
1578 	/* Tooltip */
1579 	gtk_widget_set_tooltip_text(btn, _("Join a private game"));
1580 	g_signal_connect(G_OBJECT(btn), "clicked",
1581 			 G_CALLBACK(connect_private_dialog), app_window);
1582 
1583 	gtk_entry_set_activates_default(GTK_ENTRY(name_entry), TRUE);
1584 
1585 	gtk_widget_show(connect_dlg);
1586 
1587 	gtk_widget_grab_focus(name_entry);
1588 }
1589 
1590 /* ------------ Join a private game dialog ------------------- */
1591 
update_recent_servers_list(void)1592 static void update_recent_servers_list(void)
1593 {
1594 	gchar keyname1[50], keyname2[50];
1595 	gchar *temp_name, *temp_port;
1596 	gchar *cur_name, *cur_port;
1597 	gchar *conn_name, *conn_port;
1598 	gboolean default_used;
1599 	gboolean done;
1600 	gint i;
1601 
1602 	done = FALSE;
1603 	i = 0;
1604 
1605 	conn_name = g_strdup(connect_get_server());
1606 	conn_port = g_strdup(connect_get_port());
1607 
1608 	temp_name = g_strdup(conn_name);
1609 	temp_port = g_strdup(conn_port);
1610 
1611 	do {
1612 		sprintf(keyname1, "favorites/server%dname=", i);
1613 		sprintf(keyname2, "favorites/server%dport=", i);
1614 		cur_name =
1615 		    g_strdup(config_get_string(keyname1, &default_used));
1616 		cur_port =
1617 		    g_strdup(config_get_string(keyname2, &default_used));
1618 
1619 		if (temp_name) {
1620 			sprintf(keyname1, "favorites/server%dname", i);
1621 			sprintf(keyname2, "favorites/server%dport", i);
1622 			config_set_string(keyname1, temp_name);
1623 			config_set_string(keyname2, temp_port);
1624 		} else {
1625 			g_free(cur_name);
1626 			g_free(cur_port);
1627 			break;
1628 		}
1629 
1630 		if (strlen(cur_name) == 0) {
1631 			g_free(cur_name);
1632 			g_free(cur_port);
1633 			break;
1634 		}
1635 
1636 		g_free(temp_name);
1637 		g_free(temp_port);
1638 		if (!strcmp(cur_name, conn_name)
1639 		    && !strcmp(cur_port, conn_port)) {
1640 			temp_name = NULL;
1641 			temp_port = NULL;
1642 		} else {
1643 			temp_name = g_strdup(cur_name);
1644 			temp_port = g_strdup(cur_port);
1645 		}
1646 
1647 		i++;
1648 		if (i > PRIVATE_GAME_HISTORY_SIZE) {
1649 			done = TRUE;
1650 		}
1651 		g_free(cur_name);
1652 		g_free(cur_port);
1653 	} while (!done);
1654 	g_free(temp_name);
1655 	g_free(temp_port);
1656 	g_free(conn_name);
1657 	g_free(conn_port);
1658 }
1659 
host_list_select_cb(GtkWidget * widget,gpointer user_data)1660 static void host_list_select_cb(GtkWidget * widget, gpointer user_data)
1661 {
1662 	GPtrArray *host_entries = user_data;
1663 	gint idx;
1664 	gchar *entry;
1665 	gchar **strs;
1666 
1667 	idx = gtk_combo_box_get_active(GTK_COMBO_BOX(widget));
1668 	entry = g_ptr_array_index(host_entries, idx);
1669 	strs = g_strsplit(entry, ":", 2);
1670 
1671 	connect_set_field(&connect_server, strs[0]);
1672 	connect_set_field(&connect_port, strs[1]);
1673 	gtk_entry_set_text(GTK_ENTRY(host_entry), connect_server);
1674 	gtk_entry_set_text(GTK_ENTRY(port_entry), connect_port);
1675 	g_strfreev(strs);
1676 }
1677 
1678 
connect_private_dlg_cb(GtkDialog * dlg,gint arg1,G_GNUC_UNUSED gpointer user_data)1679 static void connect_private_dlg_cb(GtkDialog * dlg, gint arg1,
1680 				   G_GNUC_UNUSED gpointer user_data)
1681 {
1682 	switch (arg1) {
1683 	case GTK_RESPONSE_OK:
1684 		connect_set_field(&connect_server,
1685 				  gtk_entry_get_text(GTK_ENTRY
1686 						     (host_entry)));
1687 		connect_set_field(&connect_port,
1688 				  gtk_entry_get_text(GTK_ENTRY
1689 						     (port_entry)));
1690 		update_recent_servers_list();
1691 		config_set_string("connect/server", connect_server);
1692 		config_set_string("connect/port", connect_port);
1693 		connect_close_all(TRUE, TRUE);
1694 		break;
1695 	case GTK_RESPONSE_CANCEL:
1696 	default:		/* For the compiler */
1697 		gtk_widget_destroy(GTK_WIDGET(dlg));
1698 		break;
1699 	};
1700 }
1701 
connect_private_dialog(G_GNUC_UNUSED GtkWidget * widget,GtkWindow * parent)1702 static void connect_private_dialog(G_GNUC_UNUSED GtkWidget * widget,
1703 				   GtkWindow * parent)
1704 {
1705 	GtkWidget *dlg_vbox;
1706 	GtkWidget *grid;
1707 	GtkWidget *lbl;
1708 	GtkWidget *hbox;
1709 
1710 	GtkWidget *host_list;
1711 	GPtrArray *host_entries;
1712 
1713 	gint i;
1714 	gchar *host_name, *host_port, *host_name_port, temp_str[50];
1715 	gboolean default_returned;
1716 
1717 	if (connect_private_dlg) {
1718 		gtk_window_present(GTK_WINDOW(connect_private_dlg));
1719 		return;
1720 	}
1721 
1722 	connect_private_dlg = gtk_dialog_new_with_buttons(
1723 								 /* Dialog caption */
1724 								 _(""
1725 								   "Join a private game"),
1726 								 GTK_WINDOW
1727 								 (parent),
1728 								 0,
1729 								 /* Button text */
1730 								 _
1731 								 ("_Cancel"),
1732 								 GTK_RESPONSE_CANCEL,
1733 								 /* Button text */
1734 								 _
1735 								 ("_Join"),
1736 								 GTK_RESPONSE_OK,
1737 								 NULL);
1738 	gtk_dialog_set_default_response(GTK_DIALOG(connect_private_dlg),
1739 					GTK_RESPONSE_OK);
1740 	g_signal_connect(G_OBJECT(connect_private_dlg), "response",
1741 			 G_CALLBACK(connect_private_dlg_cb), NULL);
1742 	g_signal_connect(G_OBJECT(connect_private_dlg), "destroy",
1743 			 G_CALLBACK(gtk_widget_destroyed),
1744 			 &connect_private_dlg);
1745 
1746 
1747 	gtk_widget_realize(connect_private_dlg);
1748 	gdk_window_set_functions(gtk_widget_get_window
1749 				 (connect_private_dlg),
1750 				 GDK_FUNC_MOVE | GDK_FUNC_CLOSE);
1751 
1752 	dlg_vbox =
1753 	    gtk_dialog_get_content_area(GTK_DIALOG(connect_private_dlg));
1754 	gtk_widget_show(dlg_vbox);
1755 
1756 	grid = gtk_grid_new();
1757 	gtk_widget_show(grid);
1758 	gtk_box_pack_start(GTK_BOX(dlg_vbox), grid, FALSE, TRUE, 0);
1759 	gtk_container_set_border_width(GTK_CONTAINER(grid), 5);
1760 	gtk_grid_set_row_spacing(GTK_GRID(grid), 3);
1761 	gtk_grid_set_column_spacing(GTK_GRID(grid), 5);
1762 
1763 	/* Label */
1764 	lbl = gtk_label_new(_("Server host"));
1765 	gtk_widget_show(lbl);
1766 	gtk_grid_attach(GTK_GRID(grid), lbl, 0, 0, 1, 1);
1767 	gtk_label_set_xalign(GTK_LABEL(lbl), 0.0);
1768 
1769 	host_entry = gtk_entry_new();
1770 	gtk_widget_show(host_entry);
1771 	gtk_grid_attach(GTK_GRID(grid), host_entry, 1, 0, 1, 1);
1772 	gtk_entry_set_text(GTK_ENTRY(host_entry), connect_server);
1773 	gtk_widget_set_tooltip_text(host_entry,
1774 				    /* Tooltip */
1775 				    _("Name of the host of the game"));
1776 	connect_set_field(&connect_server, connect_server);
1777 
1778 	/* Label */
1779 	lbl = gtk_label_new(_("Server port"));
1780 	gtk_widget_show(lbl);
1781 	gtk_grid_attach(GTK_GRID(grid), lbl, 0, 1, 1, 1);
1782 	gtk_label_set_xalign(GTK_LABEL(lbl), 0.0);
1783 
1784 	hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
1785 	gtk_widget_show(hbox);
1786 	gtk_grid_attach(GTK_GRID(grid), hbox, 1, 1, 1, 1);
1787 
1788 	port_entry = gtk_entry_new();
1789 	gtk_widget_show(port_entry);
1790 	gtk_box_pack_start(GTK_BOX(hbox), port_entry, FALSE, TRUE, 0);
1791 	gtk_entry_set_text(GTK_ENTRY(port_entry), connect_port);
1792 	gtk_widget_set_tooltip_text(port_entry,
1793 				    /* Tooltip */
1794 				    _("Port of the host of the game"));
1795 	connect_set_field(&connect_port, connect_port);
1796 
1797 	host_list = gtk_combo_box_text_new();
1798 	host_entries = g_ptr_array_new();
1799 
1800 	gtk_widget_show(host_list);
1801 
1802 	for (i = 0; i < PRIVATE_GAME_HISTORY_SIZE; i++) {
1803 		sprintf(temp_str, "favorites/server%dname=", i);
1804 		host_name = config_get_string(temp_str, &default_returned);
1805 		if (default_returned || !strlen(host_name)) {
1806 			g_free(host_name);
1807 			break;
1808 		}
1809 
1810 		sprintf(temp_str, "favorites/server%dport=", i);
1811 		host_port = config_get_string(temp_str, &default_returned);
1812 		if (default_returned || !strlen(host_port)) {
1813 			g_free(host_name);
1814 			g_free(host_port);
1815 			break;
1816 		}
1817 
1818 		host_name_port =
1819 		    g_strconcat(host_name, ":", host_port, NULL);
1820 		g_free(host_name);
1821 		g_free(host_port);
1822 
1823 		g_ptr_array_add(host_entries, host_name_port);
1824 		gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT
1825 					       (host_list),
1826 					       host_name_port);
1827 	}
1828 	if (i > 0)
1829 		gtk_combo_box_set_active(GTK_COMBO_BOX(host_list), 0);
1830 	g_signal_connect(G_OBJECT(host_list), "changed",
1831 			 G_CALLBACK(host_list_select_cb), host_entries);
1832 
1833 	gtk_grid_attach(GTK_GRID(grid), host_list, 1, 2, 1, 1);
1834 	/* Tooltip */
1835 	gtk_widget_set_tooltip_text(host_list, _("Recent games"));
1836 
1837 	/* Label */
1838 	lbl = gtk_label_new(_("Recent games"));
1839 	gtk_widget_show(lbl);
1840 	gtk_grid_attach(GTK_GRID(grid), lbl, 0, 2, 1, 1);
1841 	gtk_label_set_xalign(GTK_LABEL(lbl), 0.0);
1842 
1843 	gtk_entry_set_activates_default(GTK_ENTRY(host_entry), TRUE);
1844 	gtk_entry_set_activates_default(GTK_ENTRY(port_entry), TRUE);
1845 
1846 	gtk_widget_show(connect_private_dlg);
1847 }
1848