Freeciv-3.1
Loading...
Searching...
No Matches
pages.c
Go to the documentation of this file.
1/***********************************************************************
2 Freeciv - Copyright (C) 1996 - A Kjeldberg, L Gregersen, P Unold
3 This program is free software; you can redistribute it and/or modify
4 it under the terms of the GNU General Public License as published by
5 the Free Software Foundation; either version 2, or (at your option)
6 any later version.
7
8 This program is distributed in the hope that it will be useful,
9 but WITHOUT ANY WARRANTY; without even the implied warranty of
10 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 GNU General Public License for more details.
12***********************************************************************/
13
14#ifdef HAVE_CONFIG_H
15#include <fc_config.h>
16#endif
17
18#include <stdio.h>
19#include <time.h>
20
21#include <sys/stat.h>
22
23#include <gtk/gtk.h>
24
25/* utility */
26#include "fcintl.h"
27#include "log.h"
28#include "mem.h"
29#include "shared.h"
30#include "support.h"
31
32/* common */
33#include "dataio.h"
34#include "game.h"
35#include "mapimg.h"
36#include "version.h"
37
38/* client */
39#include "client_main.h"
40#include "climisc.h"
41#include "clinet.h"
42#include "connectdlg_common.h"
43#include "packhand.h"
44#include "servers.h"
45#include "update_queue.h"
46
47/* client/gui-gtk-3.22 */
48#include "chatline.h"
49#include "connectdlg.h"
50#include "dialogs.h"
51#include "graphics.h"
52#include "gui_main.h"
53#include "gui_stuff.h"
54#include "mapview.h"
55#include "optiondlg.h"
56#include "plrdlg.h" /* get_flag() */
57#include "repodlgs.h"
58#include "voteinfo_bar.h"
59
60#include "pages.h"
61
62
63static GtkWidget *scenario_description;
64static GtkWidget *scenario_authors;
65static GtkWidget *scenario_filename;
66static GtkWidget *scenario_version;
67
68static GtkListStore *load_store, *scenario_store, *meta_store, *lan_store;
69
70static GtkListStore *server_playerlist_store;
71static GtkWidget *server_playerlist_view;
72
73static GtkTreeSelection *load_selection, *scenario_selection;
74static GtkTreeSelection *meta_selection, *lan_selection;
75
76/* This is the current page. Invalid value at start, to be sure that it won't
77 * be caught through a switch() statement. */
78static enum client_pages current_page = -1;
79
81{
82 struct server_scan *scan;
83 guint timer;
84};
85
86static struct server_scan_timer_data meta_scan = { NULL, 0 };
87static struct server_scan_timer_data lan_scan = { NULL, 0 };
88
89static GtkWidget *statusbar, *statusbar_frame;
90static GQueue *statusbar_queue;
91static guint statusbar_timer = 0;
92
93static GtkWidget *ruleset_combo;
94
96
97static void connection_state_reset(void);
98
99/**********************************************************************/
102static void start_new_game_callback(GtkWidget *w, gpointer data)
103{
104 if (!is_server_running()) {
106
107 /* Saved settings are sent in client/options.c
108 * resend_desired_settable_options() */
109 }
110}
111
112/**********************************************************************/
115static void start_scenario_callback(GtkWidget *w, gpointer data)
116{
117 output_window_append(ftc_client, _("Compiling scenario list."));
119}
120
121/**********************************************************************/
124static void load_saved_game_callback(GtkWidget *w, gpointer data)
125{
127}
128
129/**********************************************************************/
132static void connect_network_game_callback(GtkWidget *w, gpointer data)
133{
135 set_client_page(PAGE_NETWORK);
136}
137
138/**********************************************************************/
141static void open_settings(void)
142{
143 option_dialog_popup(_("Set local options"), client_optset);
144}
145
146/**********************************************************************/
149static void main_callback(GtkWidget *w, gpointer data)
150{
151 enum client_pages page = PAGE_MAIN;
152
153 if (client.conn.used) {
155 }
156 if (page != get_client_page()) {
157 set_client_page(page);
158 }
159}
160
161/**********************************************************************/
164static gboolean intro_expose(GtkWidget *w, cairo_t *cr, gpointer *data)
165{
166 static PangoLayout *layout;
167 PangoFontDescription* desc;
168 static int width, height;
169 static bool left = FALSE;
170 GtkAllocation allocation;
171 struct sprite *intro = (struct sprite *)data;
172
173 cairo_set_source_surface(cr, intro->surface, 0, 0);
174 cairo_paint(cr);
175
176 if (!layout) {
177 char msgbuf[128];
178 const char *rev_ver;
179
180 layout = pango_layout_new(gtk_widget_create_pango_context(w));
181 desc = pango_font_description_from_string("Sans Bold 10");
182 pango_layout_set_font_description(layout, desc);
183 pango_font_description_free(desc);
184
185 rev_ver = fc_git_revision();
186
187 if (rev_ver == NULL) {
188 /* TRANS: "version 2.6.0, gui-gtk-3.22 client" */
189 fc_snprintf(msgbuf, sizeof(msgbuf), _("%s%s, %s client"),
190 word_version(), VERSION_STRING, client_string);
191 } else {
192 /* TRANS: "version 2.6.0
193 * commit: [modified] <git commit id>
194 * gui-gtk-3.22 client" */
195 fc_snprintf(msgbuf, sizeof(msgbuf), _("%s%s\ncommit: %s\n%s client"),
196 word_version(), VERSION_STRING, rev_ver, client_string);
197 left = TRUE;
198 }
199 pango_layout_set_text(layout, msgbuf, -1);
200
201 pango_layout_get_pixel_size(layout, &width, &height);
202 }
203 gtk_widget_get_allocation(w, &allocation);
204
205 cairo_set_source_rgb(cr, 0, 0, 0);
206 cairo_move_to(cr, left ? 4 : allocation.width - width - 3,
207 allocation.height - height - 3);
208 pango_cairo_show_layout(cr, layout);
209
210 cairo_set_source_rgb(cr, 1, 1, 1);
211 cairo_move_to(cr, left ? 3 : allocation.width - width - 4,
212 allocation.height - height - 4);
213 pango_cairo_show_layout(cr, layout);
214
215 return TRUE;
216}
217
218/**********************************************************************/
221static void intro_free(GtkWidget *w, gpointer *data)
222{
223 struct sprite *intro = (struct sprite *)data;
224
225 free_sprite(intro);
226}
227
228/**********************************************************************/
231GtkWidget *create_main_page(void)
232{
233 GtkWidget *widget, *vbox, *frame, *darea, *button, *table;
234 GtkSizeGroup *size;
235 struct sprite *intro_in, *intro;
236 int width, height;
237 int sh;
238 int space_needed;
239
240 size = gtk_size_group_new(GTK_SIZE_GROUP_BOTH);
241
242 vbox = gtk_grid_new();
243 gtk_orientable_set_orientation(GTK_ORIENTABLE(vbox),
244 GTK_ORIENTATION_VERTICAL);
245 widget = vbox;
246
247 frame = gtk_frame_new(NULL);
248 g_object_set(frame, "margin", 18, NULL);
249 gtk_widget_set_halign(frame, GTK_ALIGN_CENTER);
250 gtk_frame_set_shadow_type(GTK_FRAME(frame), GTK_SHADOW_ETCHED_OUT);
251 gtk_container_add(GTK_CONTAINER(vbox), frame);
252
254 get_sprite_dimensions(intro_in, &width, &height);
255 sh = screen_height();
256
257 if (sh <= 0) {
258 /* Assume some minimum height */
259 sh = 600;
260 }
261
262 space_needed = 250;
263#if IS_BETA_VERSION || IS_DEVEL_VERSION
264 /* Alpha or Beta notice takes extra space */
265 space_needed += 50;
266#endif
267
268 if (sh - height < space_needed) {
269 float scale;
270
271 if (sh < (space_needed + 0.2 * height)) {
272 /* Screen is simply too small, use minimum scale */
273 scale = 0.2;
274 } else {
275 scale = (double)(sh - space_needed) / height;
276 }
277 height *= scale;
278 width *= scale;
279 intro = sprite_scale(intro_in, width, height);
280 free_sprite(intro_in);
281 } else {
282 intro = intro_in;
283 }
284 darea = gtk_drawing_area_new();
285 gtk_widget_set_size_request(darea, width, height);
286 g_signal_connect(darea, "draw",
287 G_CALLBACK(intro_expose), intro);
288 g_signal_connect(widget, "destroy",
289 G_CALLBACK(intro_free), intro);
290 gtk_container_add(GTK_CONTAINER(frame), darea);
291
292#if IS_BETA_VERSION || IS_DEVEL_VERSION
293 {
294 GtkWidget *label;
295
296 label = gtk_label_new(unstable_message());
297 gtk_widget_set_name(label, "beta_label");
298 gtk_widget_set_halign(label, GTK_ALIGN_CENTER);
299 gtk_widget_set_valign(label, GTK_ALIGN_CENTER);
300 gtk_label_set_justify(GTK_LABEL(label), GTK_JUSTIFY_CENTER);
301 gtk_container_add(GTK_CONTAINER(vbox), label);
302 }
303#endif /* IS_BETA_VERSION || IS_DEVEL_VERSION */
304
305 table = gtk_grid_new();
306 g_object_set(table, "margin", 12, NULL);
307 gtk_widget_set_hexpand(table, TRUE);
308 gtk_widget_set_vexpand(table, TRUE);
309 gtk_widget_set_halign(table, GTK_ALIGN_CENTER);
310 gtk_widget_set_valign(table, GTK_ALIGN_CENTER);
311
312 gtk_grid_set_row_spacing(GTK_GRID(table), 8);
313 gtk_grid_set_column_spacing(GTK_GRID(table), 18);
314 gtk_container_add(GTK_CONTAINER(vbox), table);
315
316 button = gtk_button_new_with_mnemonic(_("Start _New Game"));
317 gtk_size_group_add_widget(size, button);
318 gtk_grid_attach(GTK_GRID(table), button, 0, 0, 1, 1);
319 gtk_widget_set_tooltip_text(button,
320 _("Launches local server, and connects to it for a single-player game."));
321 g_signal_connect(button, "clicked",
322 G_CALLBACK(start_new_game_callback), NULL);
323
324 button = gtk_button_new_with_mnemonic(_("Start _Scenario Game"));
325 gtk_size_group_add_widget(size, button);
326 gtk_grid_attach(GTK_GRID(table), button, 0, 1, 1, 1);
327 gtk_widget_set_tooltip_text(button,
328 _("Loads one of the scenarios for a single-player game. "
329 "Tutorial is one of the scenarios."));
330 g_signal_connect(button, "clicked",
331 G_CALLBACK(start_scenario_callback), NULL);
332
333 button = gtk_button_new_with_mnemonic(_("_Load Saved Game"));
334 gtk_size_group_add_widget(size, button);
335 gtk_grid_attach(GTK_GRID(table), button, 0, 2, 1, 1);
336 gtk_widget_set_tooltip_text(button,
337 _("Continues previously saved single-player game."));
338 g_signal_connect(button, "clicked",
339 G_CALLBACK(load_saved_game_callback), NULL);
340
341 button = gtk_button_new_with_mnemonic(_("C_onnect to Network Game"));
342 gtk_size_group_add_widget(size, button);
343 gtk_grid_attach(GTK_GRID(table), button, 1, 0, 1, 1);
344 gtk_widget_set_tooltip_text(button,
345 _("Connects to outside server. "
346 "Sometimes you want to launch a separate server even for local games."));
347 g_signal_connect(button, "clicked",
348 G_CALLBACK(connect_network_game_callback), NULL);
349
350 button = gtk_button_new_with_mnemonic(_("Client Settings"));
351 gtk_size_group_add_widget(size, button);
352 gtk_grid_attach(GTK_GRID(table), button, 1, 1, 1, 1);
353 gtk_widget_set_tooltip_text(button,
354 _("Adjusting client-side options."));
355 g_signal_connect(button, "clicked", open_settings, NULL);
356
357 button = icon_label_button_new("application-exit", _("E_xit"));
358 gtk_size_group_add_widget(size, button);
359 g_object_unref(size);
360 gtk_grid_attach(GTK_GRID(table), button, 1, 2, 1, 1);
361 gtk_widget_set_tooltip_text(button,
362 _("Gives you a break from playing freeciv."));
363 g_signal_connect(button, "clicked",
364 G_CALLBACK(quit_gtk_main), NULL);
365
366 return widget;
367}
368
369/****************************************************************************
370 GENERIC SAVE DIALOG
371****************************************************************************/
372typedef void (*save_dialog_action_fn_t) (const char *filename);
373typedef struct fileinfo_list * (*save_dialog_files_fn_t) (void);
374
375struct save_dialog {
376 GtkDialog *shell;
377 GtkTreeView *tree_view;
378 GtkEntry *entry;
381};
382
389
395
396/**********************************************************************/
399static inline GtkListStore *save_dialog_store_new(void)
400{
401 return gtk_list_store_new(SD_COL_NUM,
402 G_TYPE_STRING, /* SD_COL_PRETTY_NAME */
403 G_TYPE_STRING); /* SD_COL_FULL_PATH */
404}
405
406/**********************************************************************/
409static void save_dialog_store_update(GtkListStore *store,
410 const struct fileinfo_list *files)
411{
412 GtkTreeIter iter;
413
414 gtk_list_store_clear(store);
415 fileinfo_list_iterate(files, pfile) {
416 gtk_list_store_append(store, &iter);
417 gtk_list_store_set(store, &iter,
418 SD_COL_PRETTY_NAME, pfile->name,
419 SD_COL_FULL_PATH, pfile->fullname,
420 -1);
422}
423
424/**********************************************************************/
427static void save_dialog_update(struct save_dialog *pdialog)
428{
429 struct fileinfo_list *files;
430
431 fc_assert_ret(NULL != pdialog);
432
433 /* Update the store. */
434 files = pdialog->files();
435 save_dialog_store_update(GTK_LIST_STORE
436 (gtk_tree_view_get_model(pdialog->tree_view)),
437 files);
438 fileinfo_list_destroy(files);
439}
440
441/**********************************************************************/
445 gint response, gpointer data)
446{
447 if (response == GTK_RESPONSE_OK) {
449 gchar *filename = g_filename_to_utf8(gtk_file_chooser_get_filename
450 (GTK_FILE_CHOOSER(widget)),
451 -1, NULL, NULL, NULL);
452
453 if (NULL != filename) {
454 action(filename);
455 g_free(filename);
456 }
457 }
458 gtk_widget_destroy(widget);
459}
460
461/**********************************************************************/
464static void save_dialog_file_chooser_popup(const char *title,
465 GtkFileChooserAction action,
467{
468 GtkWidget *filechoose;
469
470 /* Create the chooser */
471 filechoose = gtk_file_chooser_dialog_new(title, GTK_WINDOW(toplevel), action,
472 _("_Cancel"), GTK_RESPONSE_CANCEL,
473 (action == GTK_FILE_CHOOSER_ACTION_SAVE) ?
474 _("_Save") : _("_Open"),
475 GTK_RESPONSE_OK, NULL);
476 setup_dialog(filechoose, toplevel);
477 gtk_window_set_position(GTK_WINDOW(filechoose), GTK_WIN_POS_MOUSE);
478
479 g_signal_connect(filechoose, "response",
480 G_CALLBACK(save_dialog_file_chooser_callback), cb);
481
482 /* Display that dialog */
483 gtk_window_present(GTK_WINDOW(filechoose));
484}
485
486/**********************************************************************/
489static void save_dialog_response_callback(GtkWidget *w, gint response,
490 gpointer data)
491{
492 struct save_dialog *pdialog = data;
493
494 switch (response) {
495 case SD_RES_BROWSE:
496 save_dialog_file_chooser_popup(_("Select Location to Save"),
497 GTK_FILE_CHOOSER_ACTION_SAVE,
498 pdialog->action);
499 break;
500 case SD_RES_DELETE:
501 {
502 GtkTreeSelection *selection;
503 GtkTreeModel *model;
504 GtkTreeIter iter;
505 const gchar *full_path;
506
507 selection = gtk_tree_view_get_selection(pdialog->tree_view);
508 if (!gtk_tree_selection_get_selected(selection, &model, &iter)) {
509 return;
510 }
511
512 gtk_tree_model_get(model, &iter, SD_COL_FULL_PATH, &full_path, -1);
513 fc_remove(full_path);
514 save_dialog_update(pdialog);
515 }
516 return;
517 case SD_RES_SAVE:
518 {
519 const char *text = gtk_entry_get_text(pdialog->entry);
520 gchar *filename = g_filename_from_utf8(text, -1, NULL, NULL, NULL);
521
522 if (NULL == filename) {
523 return;
524 }
525 pdialog->action(filename);
526 g_free(filename);
527 }
528 break;
529 default:
530 break;
531 }
532 gtk_widget_destroy(GTK_WIDGET(pdialog->shell));
533}
534
535/**********************************************************************/
538static void save_dialog_row_callback(GtkTreeView *tree_view,
539 GtkTreePath *path,
540 GtkTreeViewColumn *column,
541 gpointer data)
542{
544}
545
546/**********************************************************************/
549static void save_dialog_entry_callback(GtkEntry *entry, gpointer data)
550{
552}
553
554/**********************************************************************/
557static void save_dialog_list_callback(GtkTreeSelection *selection,
558 gpointer data)
559{
560 struct save_dialog *pdialog = data;
561 GtkTreeModel *model;
562 GtkTreeIter iter;
563 const gchar *filename;
564
565 if (!gtk_tree_selection_get_selected(selection, &model, &iter)) {
566 gtk_dialog_set_response_sensitive(pdialog->shell, SD_RES_DELETE, FALSE);
567 return;
568 }
569
570 gtk_dialog_set_response_sensitive(pdialog->shell, SD_RES_DELETE, TRUE);
571 gtk_tree_model_get(model, &iter, SD_COL_PRETTY_NAME, &filename, -1);
572 gtk_entry_set_text(pdialog->entry, filename);
573}
574
575/**********************************************************************/
578static GtkWidget *save_dialog_new(const char *title, const char *savelabel,
579 const char *savefilelabel,
582{
583 GtkWidget *shell, *sbox, *sw, *label, *view, *entry;
584 GtkContainer *vbox;
585 GtkListStore *store;
586 GtkCellRenderer *rend;
587 GtkTreeSelection *selection;
588 struct save_dialog *pdialog;
589
590 fc_assert_ret_val(NULL != action, NULL);
591 fc_assert_ret_val(NULL != files, NULL);
592
593 /* Save dialog structure. */
594 pdialog = fc_malloc(sizeof(*pdialog));
595 pdialog->action = action;
596 pdialog->files = files;
597
598 /* Shell. */
599 shell = gtk_dialog_new_with_buttons(title, NULL, 0,
600 _("_Browse..."), SD_RES_BROWSE,
601 _("_Delete"), SD_RES_DELETE,
602 _("_Cancel"), GTK_RESPONSE_CANCEL,
603 _("_Save"), SD_RES_SAVE,
604 NULL);
605 g_object_set_data_full(G_OBJECT(shell), "save_dialog", pdialog,
606 (GDestroyNotify) free);
607 gtk_dialog_set_default_response(GTK_DIALOG(shell), GTK_RESPONSE_CANCEL);
608 gtk_dialog_set_response_sensitive(GTK_DIALOG(shell), SD_RES_DELETE, FALSE);
610 g_signal_connect(shell, "response",
611 G_CALLBACK(save_dialog_response_callback), pdialog);
612 pdialog->shell = GTK_DIALOG(shell);
613 vbox = GTK_CONTAINER(gtk_dialog_get_content_area(GTK_DIALOG(shell)));
614
615 /* Tree view. */
616 store = save_dialog_store_new();
617 view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(store));
618 gtk_widget_set_hexpand(view, TRUE);
619 gtk_widget_set_vexpand(view, TRUE);
620 g_object_unref(store);
621 gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(view), FALSE);
622 g_signal_connect(view, "row-activated",
623 G_CALLBACK(save_dialog_row_callback), pdialog);
624 pdialog->tree_view = GTK_TREE_VIEW(view);
625
626 sbox = gtk_grid_new();
627 gtk_orientable_set_orientation(GTK_ORIENTABLE(sbox),
628 GTK_ORIENTATION_VERTICAL);
629 gtk_grid_set_row_spacing(GTK_GRID(sbox), 2);
630 gtk_container_add(vbox, sbox);
631
632 label = g_object_new(GTK_TYPE_LABEL,
633 "use-underline", TRUE,
634 "mnemonic-widget", view,
635 "label", savelabel,
636 "xalign", 0.0,
637 "yalign", 0.5,
638 NULL);
639 gtk_container_add(GTK_CONTAINER(sbox), label);
640
641 sw = gtk_scrolled_window_new(NULL, NULL);
642 gtk_scrolled_window_set_min_content_width(GTK_SCROLLED_WINDOW(sw), 300);
643 gtk_scrolled_window_set_min_content_height(GTK_SCROLLED_WINDOW(sw), 300);
644 gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(sw),
645 GTK_SHADOW_ETCHED_IN);
646 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(sw),
647 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
648 gtk_container_add(GTK_CONTAINER(sw), view);
649 gtk_container_add(GTK_CONTAINER(sbox), sw);
650
651 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(view));
652 gtk_tree_selection_set_mode(selection, GTK_SELECTION_SINGLE);
653 g_signal_connect(selection, "changed",
654 G_CALLBACK(save_dialog_list_callback), pdialog);
655
656 rend = gtk_cell_renderer_text_new();
657 gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(view),
658 -1, NULL, rend, "text",
659 SD_COL_PRETTY_NAME, NULL);
660
661 /* Entry. */
662 entry = gtk_entry_new();
663 gtk_widget_set_hexpand(entry, TRUE);
664 g_signal_connect(entry, "activate",
665 G_CALLBACK(save_dialog_entry_callback), pdialog);
666 pdialog->entry = GTK_ENTRY(entry);
667
668 sbox = gtk_grid_new();
669 g_object_set(sbox, "margin", 12, NULL);
670 gtk_orientable_set_orientation(GTK_ORIENTABLE(sbox),
671 GTK_ORIENTATION_VERTICAL);
672 gtk_grid_set_row_spacing(GTK_GRID(sbox), 2);
673
674 label = g_object_new(GTK_TYPE_LABEL,
675 "use-underline", TRUE,
676 "mnemonic-widget", entry,
677 "label", savefilelabel,
678 "xalign", 0.0,
679 "yalign", 0.5,
680 NULL);
681 gtk_container_add(GTK_CONTAINER(sbox), label);
682
683 gtk_container_add(GTK_CONTAINER(sbox), entry);
684 gtk_container_add(vbox, sbox);
685
686 save_dialog_update(pdialog);
687 gtk_window_set_focus(GTK_WINDOW(shell), entry);
688 gtk_widget_show_all(GTK_WIDGET(vbox));
689 return shell;
690}
691
692/****************************************************************************
693 NETWORK PAGE
694****************************************************************************/
700
701/**********************************************************************/
704static void update_server_list(enum server_scan_type sstype,
705 const struct server_list *list)
706{
707 GtkTreeSelection *sel = NULL;
708 GtkTreeView *view;
709 GtkTreeIter it;
710 GtkListStore *store;
711 const gchar *host, *portstr;
712 int port;
713
714 switch (sstype) {
716 sel = lan_selection;
717 break;
719 sel = meta_selection;
720 break;
721 default:
722 break;
723 }
724
725 if (!sel) {
726 return;
727 }
728
729 view = gtk_tree_selection_get_tree_view(sel);
730 store = GTK_LIST_STORE(gtk_tree_view_get_model(view));
731 gtk_list_store_clear(store);
732
733 if (!list) {
734 return;
735 }
736
737 host = gtk_entry_get_text(GTK_ENTRY(network_host));
738 portstr = gtk_entry_get_text(GTK_ENTRY(network_port));
739 port = atoi(portstr);
740
741 server_list_iterate(list, pserver) {
742 char buf[35];
743
744 if (pserver->humans >= 0) {
745 fc_snprintf(buf, sizeof(buf), "%d", pserver->humans);
746 } else {
747 sz_strlcpy(buf, _("Unknown"));
748 }
749 gtk_list_store_append(store, &it);
750 gtk_list_store_set(store, &it,
751 0, pserver->host,
752 1, pserver->port,
753 2, pserver->version,
754 3, _(pserver->state),
755 4, pserver->nplayers,
756 5, buf,
757 6, pserver->message,
758 -1);
759 if (strcmp(host, pserver->host) == 0 && port == pserver->port) {
760 gtk_tree_selection_select_iter(sel, &it);
761 }
763}
764
765/**********************************************************************/
769{
770 if (meta_scan.scan) {
772 meta_scan.scan = NULL;
773 }
774 if (meta_scan.timer != 0) {
775 g_source_remove(meta_scan.timer);
776 meta_scan.timer = 0;
777 }
778 if (lan_scan.scan) {
780 lan_scan.scan = NULL;
781 }
782 if (lan_scan.timer != 0) {
783 g_source_remove(lan_scan.timer);
784 lan_scan.timer = 0;
785 }
786}
787
788/**********************************************************************/
791static gboolean check_server_scan(gpointer data)
792{
793 struct server_scan_timer_data *scan_data = data;
794 struct server_scan *scan = scan_data->scan;
795 enum server_scan_status stat;
796
797 if (!scan) {
798 return FALSE;
799 }
800
801 stat = server_scan_poll(scan);
802 if (stat >= SCAN_STATUS_PARTIAL) {
804 struct srv_list *srvrs;
805
807 srvrs = server_scan_get_list(scan);
808 fc_allocate_mutex(&srvrs->mutex);
812 fc_release_mutex(&srvrs->mutex);
813 }
814
815 if (stat == SCAN_STATUS_ERROR || stat == SCAN_STATUS_DONE) {
816 scan_data->timer = 0;
817 return FALSE;
818 }
819 return TRUE;
820}
821
822/**********************************************************************/
825static void server_scan_error(struct server_scan *scan,
826 const char *message)
827{
829 log_error("%s", message);
830
831 /* Main thread will finalize the scan later (or even concurrently) -
832 * do not do anything here to cause double free or raze condition. */
833}
834
835/**********************************************************************/
848
849/**************************************************************************
850 Network connection state defines.
851**************************************************************************/
858
860
861/**********************************************************************/
864static gboolean update_network_statusbar(gpointer data)
865{
866 if (!g_queue_is_empty(statusbar_queue)) {
867 char *txt;
868
869 txt = g_queue_pop_head(statusbar_queue);
870 gtk_label_set_text(GTK_LABEL(statusbar), txt);
871 free(txt);
872 }
873
874 return TRUE;
875}
876
877/**********************************************************************/
880static void clear_network_statusbar(void)
881{
882 while (!g_queue_is_empty(statusbar_queue)) {
883 char *txt;
884
885 txt = g_queue_pop_head(statusbar_queue);
886 free(txt);
887 }
888 gtk_label_set_text(GTK_LABEL(statusbar), "");
889}
890
891/**********************************************************************/
894void append_network_statusbar(const char *text, bool force)
895{
896 if (gtk_widget_get_visible(statusbar_frame)) {
897 if (force) {
899 gtk_label_set_text(GTK_LABEL(statusbar), text);
900 } else {
901 g_queue_push_tail(statusbar_queue, fc_strdup(text));
902 }
903 }
904}
905
906/**********************************************************************/
909GtkWidget *create_statusbar(void)
910{
911 statusbar_frame = gtk_frame_new(NULL);
912 gtk_frame_set_shadow_type(GTK_FRAME(statusbar_frame), GTK_SHADOW_IN);
913
914 statusbar = gtk_label_new("");
915 gtk_widget_set_margin_start(statusbar, 2);
916 gtk_widget_set_margin_end(statusbar, 2);
917 gtk_widget_set_margin_top(statusbar, 2);
918 gtk_widget_set_margin_bottom(statusbar, 2);
919 gtk_container_add(GTK_CONTAINER(statusbar_frame), statusbar);
920
921 statusbar_queue = g_queue_new();
922 statusbar_timer = g_timeout_add(2000, update_network_statusbar, NULL);
923
924 return statusbar_frame;
925}
926
927/**********************************************************************/
931{
932 switch (state) {
933 case LOGIN_TYPE:
935
936 gtk_entry_set_text(GTK_ENTRY(network_password), "");
937 gtk_entry_set_text(GTK_ENTRY(network_confirm_password), "");
938
939 gtk_widget_set_sensitive(network_host, TRUE);
940 gtk_widget_set_sensitive(network_port, TRUE);
941 gtk_widget_set_sensitive(network_login, TRUE);
942 gtk_widget_set_sensitive(network_password_label, FALSE);
943 gtk_label_set_markup_with_mnemonic(GTK_LABEL(network_password_label), _("Pass_word:"));
944 gtk_widget_set_sensitive(network_password, FALSE);
945 gtk_widget_set_sensitive(network_confirm_password_label, FALSE);
946 gtk_widget_set_sensitive(network_confirm_password, FALSE);
947 break;
949 set_client_page(PAGE_NETWORK);
950 gtk_entry_set_text(GTK_ENTRY(network_password), "");
951 gtk_entry_set_text(GTK_ENTRY(network_confirm_password), "");
952
953 gtk_widget_set_sensitive(network_host, FALSE);
954 gtk_widget_set_sensitive(network_port, FALSE);
955 gtk_widget_set_sensitive(network_login, FALSE);
956 gtk_widget_set_sensitive(network_password_label, TRUE);
957 gtk_label_set_markup_with_mnemonic(GTK_LABEL(network_password_label), _("New Pass_word:"));
958 gtk_widget_set_sensitive(network_password, TRUE);
959 gtk_widget_set_sensitive(network_confirm_password_label, TRUE);
960 gtk_widget_set_sensitive(network_confirm_password, TRUE);
961
962 gtk_widget_grab_focus(network_password);
963 break;
965 set_client_page(PAGE_NETWORK);
966 gtk_entry_set_text(GTK_ENTRY(network_password), "");
967 gtk_entry_set_text(GTK_ENTRY(network_confirm_password), "");
968
969 gtk_widget_set_sensitive(network_host, FALSE);
970 gtk_widget_set_sensitive(network_port, FALSE);
971 gtk_widget_set_sensitive(network_login, FALSE);
972 gtk_widget_set_sensitive(network_password_label, TRUE);
973 gtk_label_set_markup_with_mnemonic(GTK_LABEL(network_password_label), _("Pass_word:"));
974 gtk_widget_set_sensitive(network_password, TRUE);
975 gtk_widget_set_sensitive(network_confirm_password_label, FALSE);
976 gtk_widget_set_sensitive(network_confirm_password, FALSE);
977
978 gtk_widget_grab_focus(network_password);
979 break;
980 case WAITING_TYPE:
982
983 gtk_widget_set_sensitive(network_login, FALSE);
984 gtk_widget_set_sensitive(network_password_label, FALSE);
985 gtk_label_set_markup_with_mnemonic(GTK_LABEL(network_password_label), _("Pass_word:"));
986 gtk_widget_set_sensitive(network_password, FALSE);
987 gtk_widget_set_sensitive(network_confirm_password_label, FALSE);
988 gtk_widget_set_sensitive(network_confirm_password, FALSE);
989 break;
990 }
991
992 connection_status = state;
993}
994
995/**********************************************************************/
998static void connection_state_reset(void)
999{
1001}
1002
1003/**********************************************************************/
1008 const char *message)
1009{
1011
1012 switch (type) {
1013 case AUTH_NEWUSER_FIRST:
1014 case AUTH_NEWUSER_RETRY:
1016 return;
1017 case AUTH_LOGIN_FIRST:
1018 /* if we magically have a password already present in 'fc_password'
1019 * then, use that and skip the password entry dialog */
1020 if (fc_password[0] != '\0') {
1021 struct packet_authentication_reply reply;
1022
1025 return;
1026 } else {
1028 }
1029 return;
1030 case AUTH_LOGIN_RETRY:
1032 return;
1033 }
1034
1035 log_error("Unsupported authentication type %d: %s.", type, message);
1036}
1037
1038/**********************************************************************/
1043static void connect_callback(GtkWidget *w, gpointer data)
1044{
1045 char errbuf [512];
1046 struct packet_authentication_reply reply;
1047
1048 switch (connection_status) {
1049 case LOGIN_TYPE:
1050 sz_strlcpy(user_name, gtk_entry_get_text(GTK_ENTRY(network_login)));
1051 sz_strlcpy(server_host, gtk_entry_get_text(GTK_ENTRY(network_host)));
1052 server_port = atoi(gtk_entry_get_text(GTK_ENTRY(network_port)));
1053
1055 errbuf, sizeof(errbuf)) != -1) {
1056 } else {
1058
1060 }
1061 return;
1062 case NEW_PASSWORD_TYPE:
1063 if (w != network_password) {
1065 gtk_entry_get_text(GTK_ENTRY(network_password)));
1066 sz_strlcpy(reply.password,
1067 gtk_entry_get_text(GTK_ENTRY(network_confirm_password)));
1068 if (strncmp(reply.password, fc_password, MAX_LEN_NAME) == 0) {
1069 fc_password[0] = '\0';
1071
1073 } else {
1074 append_network_statusbar(_("Passwords don't match, enter password."),
1075 TRUE);
1076
1078 }
1079 }
1080 return;
1082 sz_strlcpy(reply.password,
1083 gtk_entry_get_text(GTK_ENTRY(network_password)));
1085
1087 return;
1088 case WAITING_TYPE:
1089 return;
1090 }
1091
1092 log_error("Unsupported connection status: %d", connection_status);
1093}
1094
1095/**********************************************************************/
1098static void network_activate_callback(GtkTreeView *view,
1099 GtkTreePath *arg1,
1100 GtkTreeViewColumn *arg2,
1101 gpointer data)
1102{
1103 connect_callback(NULL, data);
1104}
1105
1106/**********************************************************************/
1110static void update_server_playerlist(const struct server *pserver)
1111{
1112 GtkListStore *store;
1113 GtkTreeIter iter;
1114 int n, i;
1115
1117 fc_assert_ret(store != NULL);
1118
1119 gtk_list_store_clear(store);
1120 if (!pserver || !pserver->players) {
1121 return;
1122 }
1123
1124 n = pserver->nplayers;
1125 for (i = 0; i < n; i++) {
1126 gtk_list_store_append(store, &iter);
1127 gtk_list_store_set(store, &iter,
1128 0, pserver->players[i].name,
1129 1, pserver->players[i].type,
1130 2, pserver->players[i].host,
1131 3, pserver->players[i].nation,
1132 -1);
1133 }
1134}
1135
1136/**********************************************************************/
1139static void network_list_callback(GtkTreeSelection *select, gpointer data)
1140{
1141 GtkTreeModel *model;
1142 GtkTreeIter it;
1143 const char *host;
1144 int port;
1145 char portstr[32];
1146 const struct server *pserver = NULL;
1147
1148 if (!gtk_tree_selection_get_selected(select, &model, &it)) {
1149 return;
1150 }
1151
1152 if (select == meta_selection) {
1153 GtkTreePath *path;
1154 struct srv_list *srvrs;
1155
1157 path = gtk_tree_model_get_path(model, &it);
1159 /* We are not yet inside mutex protected block */
1160 fc_allocate_mutex(&srvrs->mutex);
1161 }
1162 if (srvrs->servers && path) {
1163 gint pos = gtk_tree_path_get_indices(path)[0];
1164 pserver = server_list_get(srvrs->servers, pos);
1165 }
1167 /* We are not yet inside mutex protected block */
1168 fc_release_mutex(&srvrs->mutex);
1169 }
1170 gtk_tree_path_free(path);
1171 }
1172 update_server_playerlist(pserver);
1173
1174 gtk_tree_model_get(model, &it, 0, &host, 1, &port, -1);
1175
1176 gtk_entry_set_text(GTK_ENTRY(network_host), host);
1177 fc_snprintf(portstr, sizeof(portstr), "%d", port);
1178 gtk_entry_set_text(GTK_ENTRY(network_port), portstr);
1179}
1180
1181/**********************************************************************/
1184static void update_network_page(void)
1185{
1186 char buf[256];
1187
1188 gtk_tree_selection_unselect_all(lan_selection);
1189 gtk_tree_selection_unselect_all(meta_selection);
1190
1191 gtk_entry_set_text(GTK_ENTRY(network_login), user_name);
1192 gtk_entry_set_text(GTK_ENTRY(network_host), server_host);
1193 fc_snprintf(buf, sizeof(buf), "%d", server_port);
1194 gtk_entry_set_text(GTK_ENTRY(network_port), buf);
1195}
1196
1197/**********************************************************************/
1200GtkWidget *create_network_page(void)
1201{
1202 GtkWidget *box, *sbox, *bbox, *hbox, *notebook;
1203 GtkWidget *button, *label, *view, *sw, *table;
1204 GtkTreeSelection *selection;
1205 GtkListStore *store;
1206
1207 box = gtk_grid_new();
1208 gtk_orientable_set_orientation(GTK_ORIENTABLE(box),
1209 GTK_ORIENTATION_VERTICAL);
1210 gtk_container_set_border_width(GTK_CONTAINER(box), 4);
1211
1212 notebook = gtk_notebook_new();
1213 gtk_container_add(GTK_CONTAINER(box), notebook);
1214
1215 /* LAN pane. */
1216 lan_store = gtk_list_store_new(7, G_TYPE_STRING, /* host */
1217 G_TYPE_INT, /* port */
1218 G_TYPE_STRING, /* version */
1219 G_TYPE_STRING, /* state */
1220 G_TYPE_INT, /* nplayers */
1221 G_TYPE_STRING, /* humans */
1222 G_TYPE_STRING); /* message */
1223
1224 view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(lan_store));
1225 gtk_widget_set_hexpand(view, TRUE);
1226 gtk_widget_set_vexpand(view, TRUE);
1227 g_object_unref(lan_store);
1228 gtk_tree_view_columns_autosize(GTK_TREE_VIEW(view));
1229
1230 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(view));
1231 lan_selection = selection;
1232 gtk_tree_selection_set_mode(selection, GTK_SELECTION_SINGLE);
1233 g_signal_connect(view, "focus",
1234 G_CALLBACK(gtk_true), NULL);
1235 g_signal_connect(view, "row-activated",
1236 G_CALLBACK(network_activate_callback), NULL);
1237 g_signal_connect(selection, "changed",
1238 G_CALLBACK(network_list_callback), NULL);
1239
1240 add_treeview_column(view, _("Server Name"), G_TYPE_STRING, 0);
1241 add_treeview_column(view, _("Port"), G_TYPE_INT, 1);
1242 add_treeview_column(view, _("Version"), G_TYPE_STRING, 2);
1243 add_treeview_column(view, _("Status"), G_TYPE_STRING, 3);
1244 add_treeview_column(view, _("Players"), G_TYPE_INT, 4);
1245 add_treeview_column(view, _("Humans"), G_TYPE_STRING, 5);
1246 add_treeview_column(view, _("Comment"), G_TYPE_STRING, 6);
1247
1248 label = gtk_label_new_with_mnemonic(_("Local _Area Network"));
1249
1250 sw = gtk_scrolled_window_new(NULL, NULL);
1251 gtk_container_set_border_width(GTK_CONTAINER(sw), 4);
1252 gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(sw),
1253 GTK_SHADOW_ETCHED_IN);
1254 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(sw),
1255 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
1256 gtk_container_add(GTK_CONTAINER(sw), view);
1257 gtk_notebook_append_page(GTK_NOTEBOOK(notebook), sw, label);
1258
1259
1260 /* Metaserver pane. */
1261 meta_store = gtk_list_store_new(7, G_TYPE_STRING, /* host */
1262 G_TYPE_INT, /* port */
1263 G_TYPE_STRING, /* version */
1264 G_TYPE_STRING, /* state */
1265 G_TYPE_INT, /* nplayers */
1266 G_TYPE_STRING, /* humans */
1267 G_TYPE_STRING); /* message */
1268
1269 view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(meta_store));
1270 gtk_widget_set_hexpand(view, TRUE);
1271 gtk_widget_set_vexpand(view, TRUE);
1272 g_object_unref(meta_store);
1273 gtk_tree_view_columns_autosize(GTK_TREE_VIEW(view));
1274
1275 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(view));
1276 meta_selection = selection;
1277 gtk_tree_selection_set_mode(selection, GTK_SELECTION_SINGLE);
1278 g_signal_connect(view, "focus",
1279 G_CALLBACK(gtk_true), NULL);
1280 g_signal_connect(view, "row-activated",
1281 G_CALLBACK(network_activate_callback), NULL);
1282 g_signal_connect(selection, "changed",
1283 G_CALLBACK(network_list_callback), NULL);
1284
1285 add_treeview_column(view, _("Server Name"), G_TYPE_STRING, 0);
1286 add_treeview_column(view, _("Port"), G_TYPE_INT, 1);
1287 add_treeview_column(view, _("Version"), G_TYPE_STRING, 2);
1288 add_treeview_column(view, _("Status"), G_TYPE_STRING, 3);
1289 add_treeview_column(view, _("Players"), G_TYPE_INT, 4);
1290 add_treeview_column(view, _("Humans"), G_TYPE_STRING, 5);
1291 add_treeview_column(view, _("Comment"), G_TYPE_STRING, 6);
1292
1293 label = gtk_label_new_with_mnemonic(_("Internet _Metaserver"));
1294
1295 sw = gtk_scrolled_window_new(NULL, NULL);
1296 gtk_container_set_border_width(GTK_CONTAINER(sw), 4);
1297 gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(sw),
1298 GTK_SHADOW_ETCHED_IN);
1299 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(sw),
1300 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
1301 gtk_container_add(GTK_CONTAINER(sw), view);
1302 if (GUI_GTK_OPTION(metaserver_tab_first)) {
1303 gtk_notebook_prepend_page(GTK_NOTEBOOK(notebook), sw, label);
1304 } else {
1305 gtk_notebook_append_page(GTK_NOTEBOOK(notebook), sw, label);
1306 }
1307
1308 /* Bottom part of the page, outside the inner notebook. */
1309 sbox = gtk_grid_new();
1310 gtk_orientable_set_orientation(GTK_ORIENTABLE(sbox),
1311 GTK_ORIENTATION_VERTICAL);
1312 gtk_container_add(GTK_CONTAINER(box), sbox);
1313
1314 hbox = gtk_grid_new();
1315 gtk_grid_set_column_spacing(GTK_GRID(hbox), 12);
1316 g_object_set(hbox, "margin", 8, NULL);
1317 gtk_container_add(GTK_CONTAINER(sbox), hbox);
1318
1319 table = gtk_grid_new();
1320 gtk_grid_set_row_spacing(GTK_GRID(table), 2);
1321 gtk_grid_set_column_spacing(GTK_GRID(table), 12);
1322 gtk_container_add(GTK_CONTAINER(hbox), table);
1323
1324 network_host = gtk_entry_new();
1325 g_signal_connect(network_host, "activate",
1326 G_CALLBACK(connect_callback), NULL);
1327 gtk_grid_attach(GTK_GRID(table), network_host, 1, 0, 1, 1);
1328
1329 label = g_object_new(GTK_TYPE_LABEL,
1330 "use-underline", TRUE,
1331 "mnemonic-widget", network_host,
1332 "label", _("_Host:"),
1333 "xalign", 0.0,
1334 "yalign", 0.5,
1335 NULL);
1336 network_host_label = label;
1337 gtk_grid_attach(GTK_GRID(table), label, 0, 0, 1, 1);
1338
1339 network_port = gtk_entry_new();
1340 g_signal_connect(network_port, "activate",
1341 G_CALLBACK(connect_callback), NULL);
1342 gtk_grid_attach(GTK_GRID(table), network_port, 1, 1, 1, 1);
1343
1344 label = g_object_new(GTK_TYPE_LABEL,
1345 "use-underline", TRUE,
1346 "mnemonic-widget", network_port,
1347 "label", _("_Port:"),
1348 "xalign", 0.0,
1349 "yalign", 0.5,
1350 NULL);
1351 network_port_label = label;
1352 gtk_grid_attach(GTK_GRID(table), label, 0, 1, 1, 1);
1353
1354 network_login = gtk_entry_new();
1355 gtk_widget_set_margin_top(network_login, 10);
1356 g_signal_connect(network_login, "activate",
1357 G_CALLBACK(connect_callback), NULL);
1358 gtk_grid_attach(GTK_GRID(table), network_login, 1, 3, 1, 1);
1359
1360 label = g_object_new(GTK_TYPE_LABEL,
1361 "use-underline", TRUE,
1362 "mnemonic-widget", network_login,
1363 "label", _("_Login:"),
1364 "xalign", 0.0,
1365 "yalign", 0.5,
1366 NULL);
1367 gtk_widget_set_margin_top(label, 10);
1368 network_login_label = label;
1369 gtk_grid_attach(GTK_GRID(table), label, 0, 3, 1, 1);
1370
1371 network_password = gtk_entry_new();
1372 g_signal_connect(network_password, "activate",
1373 G_CALLBACK(connect_callback), NULL);
1374 gtk_entry_set_visibility(GTK_ENTRY(network_password), FALSE);
1375 gtk_grid_attach(GTK_GRID(table), network_password, 1, 4, 1, 1);
1376
1377 label = g_object_new(GTK_TYPE_LABEL,
1378 "use-underline", TRUE,
1379 "mnemonic-widget", network_password,
1380 "label", _("Pass_word:"),
1381 "xalign", 0.0,
1382 "yalign", 0.5,
1383 NULL);
1384 network_password_label = label;
1385 gtk_grid_attach(GTK_GRID(table), label, 0, 4, 1, 1);
1386
1387 network_confirm_password = gtk_entry_new();
1388 g_signal_connect(network_confirm_password, "activate",
1389 G_CALLBACK(connect_callback), NULL);
1390 gtk_entry_set_visibility(GTK_ENTRY(network_confirm_password), FALSE);
1391 gtk_grid_attach(GTK_GRID(table), network_confirm_password, 1, 5, 1, 1);
1392
1393 label = g_object_new(GTK_TYPE_LABEL,
1394 "use-underline", TRUE,
1395 "mnemonic-widget", network_confirm_password,
1396 "label", _("Conf_irm Password:"),
1397 "xalign", 0.0,
1398 "yalign", 0.5,
1399 NULL);
1401 gtk_grid_attach(GTK_GRID(table), label, 0, 5, 1, 1);
1402
1403 /* Server player list. */
1404 store = gtk_list_store_new(4, G_TYPE_STRING,
1405 G_TYPE_STRING,
1406 G_TYPE_STRING,
1407 G_TYPE_STRING);
1409
1410 view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(store));
1411 gtk_widget_set_hexpand(view, TRUE);
1412 add_treeview_column(view, _("Name"), G_TYPE_STRING, 0);
1413 add_treeview_column(view, _("Type"), G_TYPE_STRING, 1);
1414 add_treeview_column(view, _("Host"), G_TYPE_STRING, 2);
1415 add_treeview_column(view, _("Nation"), G_TYPE_STRING, 3);
1417
1418 sw = gtk_scrolled_window_new(NULL, NULL);
1419 gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(sw),
1420 GTK_SHADOW_ETCHED_IN);
1421 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(sw),
1422 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
1423 gtk_container_add(GTK_CONTAINER(sw), view);
1424 gtk_container_add(GTK_CONTAINER(hbox), sw);
1425
1426
1427 bbox = gtk_button_box_new(GTK_ORIENTATION_HORIZONTAL);
1428 g_object_set(bbox, "margin", 2, NULL);
1429 gtk_button_box_set_layout(GTK_BUTTON_BOX(bbox), GTK_BUTTONBOX_END);
1430 gtk_box_set_spacing(GTK_BOX(bbox), 12);
1431 gtk_container_add(GTK_CONTAINER(sbox), bbox);
1432
1433 button = gtk_button_new_from_icon_name("view-refresh", GTK_ICON_SIZE_BUTTON);
1434 gtk_container_add(GTK_CONTAINER(bbox), button);
1435 gtk_button_box_set_child_secondary(GTK_BUTTON_BOX(bbox), button, TRUE);
1436 g_signal_connect(button, "clicked",
1437 G_CALLBACK(update_network_lists), NULL);
1438
1439 button = gtk_button_new_with_mnemonic(_("_Cancel"));
1440 gtk_container_add(GTK_CONTAINER(bbox), button);
1441 g_signal_connect(button, "clicked",
1442 G_CALLBACK(main_callback), NULL);
1443
1444 button = gtk_button_new_with_mnemonic(_("C_onnect"));
1445 gtk_container_add(GTK_CONTAINER(bbox), button);
1446 g_signal_connect(button, "clicked",
1447 G_CALLBACK(connect_callback), NULL);
1448
1449 return box;
1450}
1451
1452
1453/****************************************************************************
1454 START PAGE
1455****************************************************************************/
1457
1458static GtkWidget *start_options_table;
1460static GtkTreeStore *connection_list_store;
1461static GtkTreeView *connection_list_view;
1462static GtkWidget *start_aifill_spin = NULL;
1463static GtkWidget *ai_lvl_combobox = NULL;
1464
1465
1466/* NB: Must match creation arguments in connection_list_store_new(). */
1483
1484/**********************************************************************/
1487static inline GtkTreeStore *connection_list_store_new(void)
1488{
1489 return gtk_tree_store_new(CL_NUM_COLUMNS,
1490 G_TYPE_INT, /* CL_COL_PLAYER_NUMBER */
1491 G_TYPE_STRING, /* CL_COL_USER_NAME */
1492 G_TYPE_BOOLEAN, /* CL_COL_READY_STATE */
1493 G_TYPE_STRING, /* CL_COL_PLAYER_NAME */
1494 GDK_TYPE_PIXBUF, /* CL_COL_FLAG */
1495 GDK_TYPE_PIXBUF, /* CL_COL_COLOR */
1496 G_TYPE_STRING, /* CL_COL_NATION */
1497 G_TYPE_STRING, /* CL_COL_TEAM */
1498 G_TYPE_INT, /* CL_COL_CONN_ID */
1499 G_TYPE_INT, /* CL_COL_STYLE */
1500 G_TYPE_INT, /* CL_COL_WEIGHT */
1501 G_TYPE_BOOLEAN); /* CL_COL_COLLAPSED */
1502}
1503
1504/**********************************************************************/
1508static void client_aitoggle_player(void *data)
1509{
1510 struct player *pplayer = player_by_number(FC_PTR_TO_INT(data));
1511
1512 if (NULL != pplayer
1513 && pplayer == client_player()
1514 && !is_human(pplayer)) {
1515 send_chat("/away");
1516 }
1517}
1518
1519/**********************************************************************/
1522static void client_take_player(struct player *pplayer)
1523{
1524 int request_id = send_chat_printf("/take \"%s\"", player_name(pplayer));
1525 void *data = FC_INT_TO_PTR(player_number(pplayer));
1526
1529}
1530
1531/**********************************************************************/
1534static void object_put(GObject *object, struct player *pplayer,
1535 struct connection *pconn)
1536{
1537 /* Note that passing -1 to GINT_TO_POINTER() is buggy with some versions
1538 * of gcc. player_slot_count() is not a valid player number. 0 is not
1539 * a valid connection id (see comment in server/sernet.c:
1540 * makeup_connection_name()). */
1541 g_object_set_data(object, "player_id",
1542 GINT_TO_POINTER(NULL != pplayer
1543 ? player_number(pplayer)
1544 : player_slot_count()));
1545 g_object_set_data(object, "connection_id",
1546 GINT_TO_POINTER(NULL != pconn ? pconn->id : 0));
1547}
1548
1549/**********************************************************************/
1553static bool object_extract(GObject *object, struct player **ppplayer,
1554 struct connection **ppconn)
1555{
1556 bool ret = FALSE;
1557 int id;
1558
1559 if (NULL != ppplayer) {
1560 id = GPOINTER_TO_INT(g_object_get_data(object, "player_id"));
1561 *ppplayer = player_by_number(id);
1562 if (NULL != *ppplayer) {
1563 ret = TRUE;
1564 }
1565 }
1566 if (NULL != ppconn) {
1567 id = GPOINTER_TO_INT(g_object_get_data(object, "connection_id"));
1568 *ppconn = conn_by_number(id);
1569 if (NULL != *ppconn) {
1570 ret = TRUE;
1571 }
1572 }
1573
1574 return ret;
1575}
1576
1577/**********************************************************************/
1580static void game_options_callback(GtkWidget *w, gpointer data)
1581{
1582 option_dialog_popup(_("Game Settings"), server_optset);
1583}
1584
1585/**********************************************************************/
1588static void ai_skill_callback(GtkWidget *w, gpointer data)
1589{
1590 enum ai_level *levels = (enum ai_level *)data;
1591 const char *name;
1592 int i;
1593
1594 i = gtk_combo_box_get_active(GTK_COMBO_BOX(w));
1595
1596 if (i != -1) {
1597 enum ai_level level = levels[i];
1598
1599 /* Suppress changes provoked by server rather than local user */
1600 if (server_ai_level() != level) {
1602 send_chat_printf("/%s", name);
1603 }
1604 }
1605}
1606
1607/* HACK: sometimes when creating the ruleset combo the value is set without
1608 * the user's control. In this case we don't want to do a /read. */
1610
1611/**********************************************************************/
1614static void ruleset_selected(const char *name)
1615{
1616 if (name && name[0] != '\0' && !no_ruleset_callback) {
1618 }
1619}
1620
1621/**********************************************************************/
1626static void ruleset_entry_changed(GtkWidget *w, gpointer data)
1627{
1628 const char *name = NULL;
1629
1630 name = gtk_combo_box_text_get_active_text(GTK_COMBO_BOX_TEXT(ruleset_combo));
1631
1632 if (name != NULL) {
1634 }
1635}
1636
1637/**********************************************************************/
1641static void ai_fill_changed_by_user(GtkWidget *w, gpointer data)
1642{
1645 gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(w)));
1646 }
1647}
1648
1649/**********************************************************************/
1653{
1654 if (start_aifill_spin) {
1655 bool old = send_new_aifill_to_server;
1656
1657 /* Suppress callback from this change to avoid a loop. */
1659 /* HACK: this GUI control doesn't have quite the same semantics as the
1660 * server 'aifill' option, in that it claims to represent the minimum
1661 * number of players _including humans_. The GUI control has a minimum
1662 * value of 1, so aifill == 0 will not be represented correctly.
1663 * But there's generally exactly one human player because the control
1664 * only shows up for a locally spawned server, so we more or less
1665 * get away with this. */
1666 gtk_spin_button_set_value(GTK_SPIN_BUTTON(start_aifill_spin), aifill);
1668 }
1669}
1670
1671/**********************************************************************/
1675{
1677}
1678
1679/**********************************************************************/
1682static void conn_menu_team_chosen(GObject *object, gpointer data)
1683{
1684 struct player *pplayer;
1685 struct team_slot *tslot = data;
1686
1687 if (object_extract(object, &pplayer, NULL)
1688 && NULL != tslot
1689 && team_slot_index(tslot) != team_number(pplayer->team)) {
1690 send_chat_printf("/team \"%s\" \"%s\"",
1691 player_name(pplayer),
1692 team_slot_rule_name(tslot));
1693 }
1694}
1695
1696/**********************************************************************/
1699static void conn_menu_ready_chosen(GObject *object, gpointer data)
1700{
1701 struct player *pplayer;
1702
1703 if (object_extract(object, &pplayer, NULL)) {
1705 player_number(pplayer), !pplayer->is_ready);
1706 }
1707}
1708
1709/**********************************************************************/
1712static void conn_menu_nation_chosen(GObject *object, gpointer data)
1713{
1714 struct player *pplayer;
1715
1716 if (object_extract(object, &pplayer, NULL)) {
1717 popup_races_dialog(pplayer);
1718 }
1719}
1720
1721/**********************************************************************/
1725static void conn_menu_player_command(GObject *object, gpointer data)
1726{
1727 struct player *pplayer;
1728
1729 if (object_extract(object, &pplayer, NULL)) {
1730 send_chat_printf("/%s \"%s\"",
1731 (char *) g_object_get_data(G_OBJECT(data), "command"),
1732 player_name(pplayer));
1733 }
1734}
1735
1736/**********************************************************************/
1739static void conn_menu_player_take(GObject *object, gpointer data)
1740{
1741 struct player *pplayer;
1742
1743 if (object_extract(object, &pplayer, NULL)) {
1744 client_take_player(pplayer);
1745 }
1746}
1747
1748/**********************************************************************/
1752static void conn_menu_connection_command(GObject *object, gpointer data)
1753{
1754 struct connection *pconn;
1755
1756 if (object_extract(object, NULL, &pconn)) {
1757 send_chat_printf("/%s \"%s\"",
1758 (char *) g_object_get_data(G_OBJECT(data), "command"),
1759 pconn->username);
1760 }
1761}
1762
1763/**********************************************************************/
1766static void show_conn_popup(struct player *pplayer, struct connection *pconn)
1767{
1768 GtkWidget *popup;
1769 char buf[4096] = "";
1770
1771 if (pconn) {
1772 cat_snprintf(buf, sizeof(buf), _("Connection name: %s"),
1773 pconn->username);
1774 } else {
1775 cat_snprintf(buf, sizeof(buf), _("Player name: %s"),
1776 player_name(pplayer));
1777 }
1778 cat_snprintf(buf, sizeof(buf), "\n");
1779 if (pconn) {
1780 cat_snprintf(buf, sizeof(buf), _("Host: %s"), pconn->addr);
1781 }
1782 cat_snprintf(buf, sizeof(buf), "\n");
1783
1784 /* Show popup. */
1785 popup = gtk_message_dialog_new(NULL, 0,
1786 GTK_MESSAGE_INFO, GTK_BUTTONS_CLOSE,
1787 "%s", buf);
1788 gtk_window_set_title(GTK_WINDOW(popup), _("Player/conn info"));
1789 setup_dialog(popup, toplevel);
1790 g_signal_connect(popup, "response", G_CALLBACK(gtk_widget_destroy), NULL);
1791 gtk_window_present(GTK_WINDOW(popup));
1792}
1793
1794/**********************************************************************/
1797static void conn_menu_info_chosen(GObject *object, gpointer data)
1798{
1799 struct player *pplayer;
1800 struct connection *pconn;
1801
1802 if (object_extract(object, &pplayer, &pconn)) {
1803 show_conn_popup(pplayer, pconn);
1804 }
1805}
1806
1807/**********************************************************************/
1811static GtkWidget *create_conn_menu(struct player *pplayer,
1812 struct connection *pconn)
1813{
1814 GtkWidget *menu;
1815 GtkWidget *item;
1816 gchar *buf;
1817
1818 menu = gtk_menu_new();
1819 object_put(G_OBJECT(menu), pplayer, pconn);
1820
1821 buf = g_strdup_printf(_("%s info"),
1822 pconn ? pconn->username : player_name(pplayer));
1823 item = gtk_menu_item_new_with_label(buf);
1824 g_free(buf);
1825 gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
1826 g_signal_connect_swapped(item, "activate",
1827 G_CALLBACK(conn_menu_info_chosen), menu);
1828
1829 if (NULL != pplayer) {
1830 item = gtk_menu_item_new_with_label(_("Toggle player ready"));
1831 gtk_widget_set_sensitive(item, is_human(pplayer));
1832 gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
1833 g_signal_connect_swapped(item, "activate",
1834 G_CALLBACK(conn_menu_ready_chosen), menu);
1835
1836 item = gtk_menu_item_new_with_label(_("Pick nation"));
1837 gtk_widget_set_sensitive(item,
1839 pplayer));
1840 gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
1841 g_signal_connect_swapped(item, "activate",
1842 G_CALLBACK(conn_menu_nation_chosen), menu);
1843
1844 item = gtk_menu_item_new_with_label(_("Observe this player"));
1845 gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
1846 g_object_set_data_full(G_OBJECT(item), "command", g_strdup("observe"),
1847 (GDestroyNotify) g_free);
1848 g_signal_connect_swapped(item, "activate",
1849 G_CALLBACK(conn_menu_player_command), menu);
1850
1851 item = gtk_menu_item_new_with_label(_("Take this player"));
1852 gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
1853 g_signal_connect_swapped(item, "activate",
1854 G_CALLBACK(conn_menu_player_take), menu);
1855 }
1856
1857 if (ALLOW_CTRL <= client.conn.access_level && NULL != pconn
1858 && (pconn->id != client.conn.id || NULL != pplayer)) {
1859 item = gtk_separator_menu_item_new();
1860 gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
1861
1862 if (pconn->id != client.conn.id) {
1863 item = gtk_menu_item_new_with_label(_("Cut connection"));
1864 gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
1865 g_object_set_data_full(G_OBJECT(item), "command", g_strdup("cut"),
1866 (GDestroyNotify) g_free);
1867 g_signal_connect_swapped(item, "activate",
1868 G_CALLBACK(conn_menu_connection_command),
1869 menu);
1870 }
1871 }
1872
1873 if (ALLOW_CTRL <= client.conn.access_level && NULL != pplayer) {
1874 item = gtk_menu_item_new_with_label(_("Aitoggle player"));
1875 gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
1876 g_object_set_data_full(G_OBJECT(item), "command", g_strdup("aitoggle"),
1877 (GDestroyNotify) g_free);
1878 g_signal_connect_swapped(item, "activate",
1879 G_CALLBACK(conn_menu_player_command), menu);
1880
1881 if (pplayer != client.conn.playing && game.info.is_new_game) {
1882 item = gtk_menu_item_new_with_label(_("Remove player"));
1883 gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
1884 g_object_set_data_full(G_OBJECT(item), "command", g_strdup("remove"),
1885 (GDestroyNotify) g_free);
1886 g_signal_connect_swapped(item, "activate",
1887 G_CALLBACK(conn_menu_player_command), menu);
1888 }
1889 }
1890
1891 if (ALLOW_ADMIN <= client.conn.access_level && NULL != pconn
1892 && pconn->id != client.conn.id) {
1893 enum cmdlevel level;
1894
1895 /* No item for hack access; that would be a serious security hole. */
1896 for (level = cmdlevel_min(); level < client.conn.access_level; level++) {
1897 /* TRANS: Give access level to a connection. */
1898 buf = g_strdup_printf(_("Give %s access"),
1899 cmdlevel_name(level));
1900 item = gtk_menu_item_new_with_label(buf);
1901 g_free(buf);
1902 gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
1903 g_object_set_data_full(G_OBJECT(item), "command",
1904 g_strdup_printf("cmdlevel %s",
1905 cmdlevel_name(level)),
1906 (GDestroyNotify) g_free);
1907 g_signal_connect_swapped(item, "activate",
1908 G_CALLBACK(conn_menu_connection_command),
1909 menu);
1910 }
1911 }
1912
1913 if (ALLOW_CTRL <= client.conn.access_level
1914 && NULL != pplayer && is_ai(pplayer)) {
1915 enum ai_level level;
1916
1917 item = gtk_separator_menu_item_new();
1918 gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
1919
1920 for (level = 0; level < AI_LEVEL_COUNT; level++) {
1922 const char *level_name = ai_level_translated_name(level);
1923 const char *level_cmd = ai_level_cmd(level);
1924
1925 item = gtk_menu_item_new_with_label(level_name);
1926 gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
1927 g_object_set_data_full(G_OBJECT(item), "command",
1928 g_strdup(level_cmd), (GDestroyNotify) g_free);
1929 g_signal_connect_swapped(item, "activate",
1930 G_CALLBACK(conn_menu_player_command), menu);
1931 }
1932 }
1933 }
1934
1935 if (pplayer && game.info.is_new_game) {
1936 const int count = pplayer->team
1937 ? player_list_size(team_members(pplayer->team)) : 0;
1938 bool need_empty_team = (count != 1);
1939
1940 item = gtk_separator_menu_item_new();
1941 gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
1942
1943 /* Can't use team_iterate here since it skips empty teams. */
1944 team_slots_iterate(tslot) {
1945 if (!team_slot_is_used(tslot)) {
1946 if (!need_empty_team) {
1947 continue;
1948 }
1949 need_empty_team = FALSE;
1950 }
1951
1952 /* TRANS: e.g., "Put on Team 5" */
1953 buf = g_strdup_printf(_("Put on %s"),
1955 item = gtk_menu_item_new_with_label(buf);
1956 g_free(buf);
1957 gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
1958 object_put(G_OBJECT(item), pplayer, NULL);
1959 g_signal_connect(item, "activate", G_CALLBACK(conn_menu_team_chosen),
1960 tslot);
1962 }
1963
1964 gtk_widget_show_all(menu);
1965
1966 return menu;
1967}
1968
1969/**********************************************************************/
1972static gboolean delayed_unselect_path(gpointer data)
1973{
1974 if (NULL != connection_list_view) {
1975 GtkTreeSelection *selection =
1976 gtk_tree_view_get_selection(connection_list_view);
1977 GtkTreePath *path = data;
1978
1979 gtk_tree_selection_unselect_path(selection, path);
1980 gtk_tree_path_free(path);
1981 }
1982 return FALSE;
1983}
1984
1985/**********************************************************************/
1988static gboolean connection_list_event(GtkWidget *widget,
1989 GdkEventButton *event,
1990 gpointer data)
1991{
1992 GtkTreeView *tree = GTK_TREE_VIEW(widget);
1993 GtkTreePath *path = NULL;
1994 GtkTreeSelection *selection = gtk_tree_view_get_selection(tree);
1995 gboolean ret = FALSE;
1996
1997 if ((1 != event->button && 3 != event->button)
1998 || GDK_BUTTON_PRESS != event->type
1999 || !gtk_tree_view_get_path_at_pos(tree,
2000 event->x, event->y,
2001 &path, NULL, NULL, NULL)) {
2002 return FALSE;
2003 }
2004
2005 if (1 == event->button) {
2006 if (gtk_tree_selection_path_is_selected(selection, path)) {
2007 /* Need to delay to avoid problem with the expander. */
2008 g_idle_add(delayed_unselect_path, path);
2009 return FALSE; /* Return now, don't free the path. */
2010 }
2011 } else if (3 == event->button) {
2012 GtkTreeModel *model = gtk_tree_view_get_model(tree);
2013 GtkTreeIter iter;
2014 GtkWidget *menu;
2015 int player_no, conn_id;
2016 struct player *pplayer;
2017 struct connection *pconn;
2018
2019 if (!gtk_tree_selection_path_is_selected(selection, path)) {
2020 gtk_tree_selection_select_path(selection, path);
2021 }
2022 gtk_tree_model_get_iter(model, &iter, path);
2023
2024 gtk_tree_model_get(model, &iter, CL_COL_PLAYER_NUMBER, &player_no, -1);
2025 pplayer = player_by_number(player_no);
2026
2027 gtk_tree_model_get(model, &iter, CL_COL_CONN_ID, &conn_id, -1);
2028 pconn = conn_by_number(conn_id);
2029
2030 menu = create_conn_menu(pplayer, pconn);
2031 gtk_menu_popup_at_pointer(GTK_MENU(menu), NULL);
2032 ret = TRUE;
2033 }
2034
2035 gtk_tree_path_free(path);
2036 return ret;
2037}
2038
2039/**********************************************************************/
2042static void connection_list_row_callback(GtkTreeView *tree_view,
2043 GtkTreeIter *iter,
2044 GtkTreePath *path,
2045 gpointer data)
2046{
2047 GtkTreeStore *store = GTK_TREE_STORE(gtk_tree_view_get_model(tree_view));
2048
2049 gtk_tree_store_set(store, iter,
2050 CL_COL_COLLAPSED, GPOINTER_TO_INT(data), -1);
2051}
2052
2053/**********************************************************************/
2057static bool conn_list_selection(struct player **ppplayer,
2058 struct connection **ppconn)
2059{
2060 if (NULL != connection_list_view) {
2061 GtkTreeIter iter;
2062 GtkTreeModel *model;
2063 GtkTreeSelection *selection =
2064 gtk_tree_view_get_selection(connection_list_view);
2065
2066 if (gtk_tree_selection_get_selected(selection, &model, &iter)) {
2067 int id;
2068
2069 if (NULL != ppplayer) {
2070 gtk_tree_model_get(model, &iter, CL_COL_PLAYER_NUMBER, &id, -1);
2071 *ppplayer = player_by_number(id);
2072 }
2073 if (NULL != ppconn) {
2074 gtk_tree_model_get(model, &iter, CL_COL_CONN_ID, &id, -1);
2075 *ppconn = conn_by_number(id);
2076 }
2077 return TRUE;
2078 }
2079 }
2080
2081 if (NULL != ppplayer) {
2082 *ppplayer = NULL;
2083 }
2084 if (NULL != ppconn) {
2085 *ppconn = NULL;
2086 }
2087 return FALSE;
2088}
2089
2090/**********************************************************************/
2093static void conn_list_select_conn(struct connection *pconn)
2094{
2095 GtkTreeModel *model;
2096 GtkTreeIter parent, child, *iter = NULL;
2097 GtkTreeSelection *selection;
2098 gboolean valid;
2099 const int search_id = pconn->id;
2100 int id;
2101
2102 if (NULL == connection_list_view) {
2103 return;
2104 }
2105
2106 model = gtk_tree_view_get_model(connection_list_view);
2107 selection = gtk_tree_view_get_selection(connection_list_view);
2108
2109 /* Main iteration. */
2110 valid = gtk_tree_model_get_iter_first(model, &parent);
2111 while (valid && NULL == iter) {
2112 gtk_tree_model_get(model, &parent, CL_COL_CONN_ID, &id, -1);
2113 if (search_id == id) {
2114 iter = &parent;
2115 break;
2116 }
2117
2118 /* Node children iteration. */
2119 valid = gtk_tree_model_iter_children(model, &child, &parent);
2120 while (valid && NULL == iter) {
2121 gtk_tree_model_get(model, &child, CL_COL_CONN_ID, &id, -1);
2122 if (search_id == id) {
2123 iter = &child;
2124 break;
2125 }
2126 valid = gtk_tree_model_iter_next(model, &child);
2127 }
2128
2129 valid = gtk_tree_model_iter_next(model, &parent);
2130 }
2131
2132 /* Select iterator. */
2133 if (NULL != iter) {
2134 gtk_tree_selection_select_iter(selection, iter);
2135 } else {
2136 log_error("%s(): connection %s not found.",
2137 __FUNCTION__, conn_description(pconn));
2138 }
2139}
2140
2141/**********************************************************************/
2144static void ready_button_callback(GtkWidget *w, gpointer data)
2145{
2146 if (can_client_control()) {
2149 !client_player()->is_ready);
2150 } else {
2152 }
2153}
2154
2155/**********************************************************************/
2158static void nation_button_callback(GtkWidget *w, gpointer data)
2159{
2160 struct player *selected_plr;
2161 bool row_selected = conn_list_selection(&selected_plr, NULL);
2162
2163 if (row_selected && NULL != selected_plr) {
2164 /* "Take <player_name>" */
2165 client_take_player(selected_plr);
2166 } else if (can_client_control()) {
2167 /* "Pick Nation" */
2169 } else {
2170 /* "Take a Player" */
2171 send_chat("/take -");
2172 }
2173}
2174
2175/**********************************************************************/
2178static void observe_button_callback(GtkWidget *w, gpointer data)
2179{
2180 struct player *selected_plr;
2181 bool row_selected = conn_list_selection(&selected_plr, NULL);
2182
2183 if (row_selected && NULL != selected_plr) {
2184 /* "Observe <player_name>" */
2185 send_chat_printf("/observe \"%s\"", player_name(selected_plr));
2186 } else if (!client_is_global_observer()) {
2187 /* "Observe" */
2188 send_chat("/observe");
2189 } else {
2190 /* "Do not observe" */
2191 send_chat("/detach");
2192 }
2193}
2194
2195/**********************************************************************/
2199{
2200 char buf[2 * MAX_LEN_NAME];
2201 const char *text;
2202 struct player *selected_plr;
2203 bool row_selected = conn_list_selection(&selected_plr, NULL);
2204 bool sensitive;
2205
2206 /*** Ready button. ***/
2207 if (can_client_control()) {
2208 sensitive = client_player()->is_alive;
2209 if (client_player()->is_ready) {
2210 text = _("Not _ready");
2211 } else {
2212 int num_unready = 0;
2213
2214 players_iterate_alive(pplayer) {
2215 if (is_human(pplayer) && !pplayer->is_ready) {
2216 num_unready++;
2217 }
2219
2220 if (num_unready > 1) {
2221 text = _("_Ready");
2222 } else {
2223 /* We are the last unready player so clicking here will
2224 * immediately start the game. */
2225 text = _("_Start");
2226 }
2227 }
2228 } else {
2229 text = _("_Start");
2230 if (can_client_access_hack()) {
2231 sensitive = TRUE;
2233 if (is_human(plr)) {
2234 /* There's human controlled player(s) in game, so it's their
2235 * job to start the game. */
2236 sensitive = FALSE;
2237 break;
2238 }
2240 } else {
2241 sensitive = FALSE;
2242 }
2243 }
2245 gtk_widget_set_sensitive(ready_button, sensitive);
2246
2247 /*** Nation button. ***/
2248 if (row_selected && NULL != selected_plr) {
2249 fc_snprintf(buf, sizeof(buf), _("_Take %s"), player_name(selected_plr));
2250 text = buf;
2251 sensitive = (client_is_observer() || selected_plr != client_player());
2252 } else if (can_client_control()) {
2253 text = _("Pick _Nation");
2254 sensitive = game.info.is_new_game;
2255 } else {
2256 text = _("_Take a Player");
2257 sensitive = game.info.is_new_game;
2258 }
2260 gtk_widget_set_sensitive(nation_button, sensitive);
2261
2262 /*** Observe button. ***/
2263 if (row_selected && NULL != selected_plr) {
2264 fc_snprintf(buf, sizeof(buf), _("_Observe %s"),
2265 player_name(selected_plr));
2266 text = buf;
2267 sensitive = (!client_is_observer() || selected_plr != client_player());
2268 } else if (!client_is_global_observer()) {
2269 text = _("_Observe");
2270 sensitive = TRUE;
2271 } else {
2272 text = _("Do not _observe");
2273 sensitive = TRUE;
2274 }
2276 gtk_widget_set_sensitive(observe_button, sensitive);
2277}
2278
2279/**********************************************************************/
2283static bool model_get_player_iter(GtkTreeModel *model,
2284 GtkTreeIter *iter,
2285 GtkTreeIter *start,
2286 const struct player *pplayer)
2287{
2288 const int search_id = player_number(pplayer);
2289 int id;
2290
2291 if (NULL != start) {
2292 *iter = *start;
2293 if (!gtk_tree_model_iter_next(model, iter)) {
2294 return FALSE;
2295 }
2296 } else if (!gtk_tree_model_get_iter_first(model, iter)) {
2297 return FALSE;
2298 }
2299
2300 do {
2301 gtk_tree_model_get(model, iter, CL_COL_PLAYER_NUMBER, &id, -1);
2302 if (id == search_id) {
2303 return TRUE;
2304 }
2305 } while (gtk_tree_model_iter_next(model, iter));
2306
2307 return FALSE;
2308}
2309
2310/**********************************************************************/
2314static bool model_get_conn_iter(GtkTreeModel *model, GtkTreeIter *iter,
2315 GtkTreeIter *parent, GtkTreeIter *start,
2316 const struct connection *pconn)
2317{
2318 const int search_id = pconn->id;
2319 int id;
2320
2321 if (NULL != start) {
2322 *iter = *start;
2323 if (!gtk_tree_model_iter_next(model, iter)) {
2324 return FALSE;
2325 }
2326 } else if (!gtk_tree_model_iter_children(model, iter, parent)) {
2327 return FALSE;
2328 }
2329
2330 do {
2331 gtk_tree_model_get(model, iter, CL_COL_CONN_ID, &id, -1);
2332 if (id == search_id) {
2333 return TRUE;
2334 }
2335 } while (gtk_tree_model_iter_next(model, iter));
2336
2337 return FALSE;
2338}
2339
2340/**********************************************************************/
2344{
2346 && get_client_page() == PAGE_START
2347 && connection_list_store != NULL) {
2348 GtkTreeStore *store = connection_list_store;
2349 GtkTreeModel *model = GTK_TREE_MODEL(store);
2350 GtkTreePath *path;
2351 GtkTreeIter child, prev_child, *pprev_child;
2352 GtkTreeIter parent, prev_parent, *pprev_parent = NULL;
2353 GdkPixbuf *flag, *color;
2354 gboolean collapsed;
2355 struct player *pselected_player;
2356 struct connection *pselected_conn;
2357 bool is_ready;
2358 const char *nation, *plr_name, *team;
2359 char name[MAX_LEN_NAME + 8];
2360 enum cmdlevel access_level;
2361 int conn_id;
2362
2363 /* Refresh the AI skill level control */
2364 if (ai_lvl_combobox) {
2365 enum ai_level new_level = server_ai_level(), level;
2366 int i = 0;
2367
2368 for (level = 0; level < AI_LEVEL_COUNT; level++) {
2370 if (level == new_level) {
2371 gtk_combo_box_set_active(GTK_COMBO_BOX(ai_lvl_combobox), i);
2372 break;
2373 }
2374 i++;
2375 }
2376 }
2377 if (level == AI_LEVEL_COUNT) {
2378 /* Probably ai_level_invalid() */
2379 gtk_combo_box_set_active(GTK_COMBO_BOX(ai_lvl_combobox), -1);
2380 }
2381 }
2382
2383 /* Save the selected connection. */
2384 (void) conn_list_selection(&pselected_player, &pselected_conn);
2385
2386 /* Insert players into the connection list. */
2387 players_iterate(pplayer) {
2388 if (!player_has_flag(pplayer, PLRF_SCENARIO_RESERVED)) {
2389 conn_id = -1;
2390 access_level = ALLOW_NONE;
2391 flag = pplayer->nation ? get_flag(pplayer->nation) : NULL;
2392 color = create_player_icon(pplayer);
2393
2394 conn_list_iterate(pplayer->connections, pconn) {
2395 if (pconn->playing == pplayer && !pconn->observer) {
2396 conn_id = pconn->id;
2397 access_level = pconn->access_level;
2398 break;
2399 }
2401
2402 if (is_ai(pplayer) && !pplayer->was_created
2403 && !pplayer->is_connected) {
2404 /* TRANS: "<Novice AI>" */
2405 fc_snprintf(name, sizeof(name), _("<%s AI>"),
2406 ai_level_translated_name(pplayer->ai_common.skill_level));
2407 } else {
2408 sz_strlcpy(name, pplayer->username);
2409 if (access_level > ALLOW_BASIC) {
2410 sz_strlcat(name, "*");
2411 }
2412 }
2413
2414 is_ready = !is_human(pplayer) ? TRUE : pplayer->is_ready;
2415
2416 if (pplayer->nation == NO_NATION_SELECTED) {
2417 nation = _("Random");
2418 if (pplayer->was_created) {
2419 plr_name = player_name(pplayer);
2420 } else {
2421 plr_name = "";
2422 }
2423 } else {
2424 nation = nation_adjective_for_player(pplayer);
2425 plr_name = player_name(pplayer);
2426 }
2427
2428 team = pplayer->team ? team_name_translation(pplayer->team) : "";
2429
2430 if (model_get_player_iter(model, &parent, pprev_parent, pplayer)) {
2431 gtk_tree_store_move_after(store, &parent, pprev_parent);
2432 } else {
2433 gtk_tree_store_insert_after(store, &parent, NULL, pprev_parent);
2434 }
2435
2436 gtk_tree_store_set(store, &parent,
2439 CL_COL_READY_STATE, is_ready,
2440 CL_COL_PLAYER_NAME, plr_name,
2441 CL_COL_FLAG, flag,
2443 CL_COL_NATION, nation,
2445 CL_COL_CONN_ID, conn_id,
2446 CL_COL_STYLE, PANGO_STYLE_NORMAL,
2447 CL_COL_WEIGHT, PANGO_WEIGHT_BOLD,
2448 -1);
2449
2450 /* Insert observers of this player as child nodes. */
2451 pprev_child = NULL;
2452 conn_list_iterate(pplayer->connections, pconn) {
2453 if (pconn->id == conn_id) {
2454 continue;
2455 }
2456 if (model_get_conn_iter(model, &child, &parent,
2457 pprev_child, pconn)) {
2458 gtk_tree_store_move_after(store, &child, pprev_child);
2459 } else {
2460 gtk_tree_store_insert_after(store, &child, &parent, pprev_child);
2461 }
2462
2463 gtk_tree_store_set(store, &child,
2465 CL_COL_USER_NAME, pconn->username,
2466 CL_COL_TEAM, _("Observer"),
2467 CL_COL_CONN_ID, pconn->id,
2468 CL_COL_STYLE, PANGO_STYLE_NORMAL,
2469 CL_COL_WEIGHT, PANGO_WEIGHT_NORMAL,
2470 -1);
2471
2472 prev_child = child;
2473 pprev_child = &prev_child;
2475
2476 /* Expand node? */
2477 if (NULL != pprev_child) {
2478 gtk_tree_model_get(model, &parent, CL_COL_COLLAPSED, &collapsed, -1);
2479 if (!collapsed) {
2480 path = gtk_tree_model_get_path(model, &parent);
2481 gtk_tree_view_expand_row(GTK_TREE_VIEW(connection_list_view),
2482 path, FALSE);
2483 gtk_tree_path_free(path);
2484 }
2485 }
2486
2487 /* Remove trailing rows. */
2488 if (NULL != pprev_child) {
2489 child = prev_child;
2490 if (gtk_tree_model_iter_next(model, &child)) {
2491 while (gtk_tree_store_remove(store, &child)) {
2492 /* Do nothing more. */
2493 }
2494 }
2495 } else if (gtk_tree_model_iter_children(model, &child, &parent)) {
2496 while (gtk_tree_store_remove(store, &child)) {
2497 /* Do nothing more. */
2498 }
2499 }
2500
2501 prev_parent = parent;
2502 pprev_parent = &prev_parent;
2503 if (flag) {
2504 g_object_unref(flag);
2505 }
2506 if (color) {
2507 g_object_unref(color);
2508 }
2509 }
2511
2512 /* Finally, insert global observers... */
2514 if (NULL != pconn->playing || !pconn->observer) {
2515 continue;
2516 }
2517
2518 if (model_get_conn_iter(model, &parent, NULL, pprev_parent, pconn)) {
2519 gtk_tree_store_move_after(store, &parent, pprev_parent);
2520 } else {
2521 gtk_tree_store_insert_after(store, &parent, NULL, pprev_parent);
2522 }
2523
2524 gtk_tree_store_set(store, &parent,
2526 CL_COL_USER_NAME, pconn->username,
2527 CL_COL_TEAM, _("Observer"),
2528 CL_COL_CONN_ID, pconn->id,
2529 CL_COL_STYLE, PANGO_STYLE_NORMAL,
2530 CL_COL_WEIGHT, PANGO_WEIGHT_NORMAL,
2531 -1);
2532
2533 prev_parent = parent;
2534 pprev_parent = &prev_parent;
2536
2537 /* ...and detached connections. */
2539 if (NULL != pconn->playing || pconn->observer) {
2540 continue;
2541 }
2542
2543 if (model_get_conn_iter(model, &parent, NULL, pprev_parent, pconn)) {
2544 gtk_tree_store_move_after(store, &parent, pprev_parent);
2545 } else {
2546 gtk_tree_store_insert_after(store, &parent, NULL, pprev_parent);
2547 }
2548
2549 gtk_tree_store_set(store, &parent,
2551 CL_COL_USER_NAME, pconn->username,
2552 CL_COL_TEAM, _("Detached"),
2553 CL_COL_CONN_ID, pconn->id,
2554 CL_COL_STYLE, PANGO_STYLE_ITALIC,
2555 CL_COL_WEIGHT, PANGO_WEIGHT_NORMAL,
2556 -1);
2557
2558 prev_parent = parent;
2559 pprev_parent = &prev_parent;
2561
2562 /* Remove trailing rows. */
2563 if (NULL != pprev_parent) {
2564 parent = prev_parent;
2565 if (gtk_tree_model_iter_next(model, &parent)) {
2566 while (gtk_tree_store_remove(store, &parent)) {
2567 /* Do nothing more. */
2568 }
2569 }
2570 } else {
2571 gtk_tree_store_clear(store);
2572 }
2573
2574 /* If we were selecting a single connection, let's try to reselect it. */
2575 if (NULL == pselected_player && NULL != pselected_conn) {
2576 conn_list_select_conn(pselected_conn);
2577 }
2578 }
2579
2581}
2582
2583/**********************************************************************/
2588static void add_tree_col(GtkWidget *treeview, GType gtype,
2589 const char *title, int colnum, const char *key)
2590{
2591 GtkCellRenderer *rend;
2592 GtkTreeViewColumn *col;
2593
2594 if (gtype == G_TYPE_BOOLEAN) {
2595 rend = gtk_cell_renderer_toggle_new();
2596 col = gtk_tree_view_column_new_with_attributes(title, rend,
2597 "active", colnum, NULL);
2598 } else if (gtype == GDK_TYPE_PIXBUF) {
2599 rend = gtk_cell_renderer_pixbuf_new();
2600 col = gtk_tree_view_column_new_with_attributes(title, rend,
2601 "pixbuf", colnum, NULL);
2602 } else {
2603 rend = gtk_cell_renderer_text_new();
2604 col = gtk_tree_view_column_new_with_attributes(title, rend,
2605 "text", colnum,
2606 "style", CL_COL_STYLE,
2607 "weight", CL_COL_WEIGHT,
2608 NULL);
2609 }
2610
2611 gtk_tree_view_column_set_sizing(col, GTK_TREE_VIEW_COLUMN_AUTOSIZE);
2612 gtk_tree_view_append_column(GTK_TREE_VIEW(treeview), col);
2613
2614 if (key != NULL) {
2615 g_object_set_data(G_OBJECT(treeview), key, col);
2616 }
2617}
2618
2619/**********************************************************************/
2622GtkWidget *create_start_page(void)
2623{
2624 GtkWidget *box, *sbox, *table, *vbox;
2625 GtkWidget *view, *sw, *text, *toolkit_view, *button, *spin;
2626 GtkWidget *label;
2627 GtkTreeSelection *selection;
2628 enum ai_level level;
2629 /* There's less than AI_LEVEL_COUNT entries as not all levels have
2630 entries (that's the whole point of this array: index != value),
2631 but this is set safely to the max */
2632 static enum ai_level levels[AI_LEVEL_COUNT];
2633 int i = 0;
2634
2635 box = gtk_grid_new();
2636 gtk_orientable_set_orientation(GTK_ORIENTABLE(box),
2637 GTK_ORIENTATION_VERTICAL);
2638 gtk_grid_set_row_spacing(GTK_GRID(box), 8);
2639 gtk_container_set_border_width(GTK_CONTAINER(box), 4);
2640
2641 sbox = gtk_grid_new();
2642 gtk_grid_set_column_spacing(GTK_GRID(sbox), 12);
2643 gtk_container_add(GTK_CONTAINER(box), sbox);
2644
2645 vbox = gtk_grid_new();
2646 g_object_set(vbox, "margin", 12, NULL);
2647 gtk_orientable_set_orientation(GTK_ORIENTABLE(vbox),
2648 GTK_ORIENTATION_VERTICAL);
2649 gtk_grid_set_row_spacing(GTK_GRID(vbox), 2);
2650 gtk_widget_set_halign(vbox, GTK_ALIGN_CENTER);
2651 gtk_widget_set_valign(vbox, GTK_ALIGN_CENTER);
2652 gtk_container_add(GTK_CONTAINER(sbox), vbox);
2653
2654 table = gtk_grid_new();
2655 start_options_table = table;
2656 gtk_grid_set_row_spacing(GTK_GRID(table), 2);
2657 gtk_grid_set_column_spacing(GTK_GRID(table), 12);
2658 gtk_container_add(GTK_CONTAINER(vbox), table);
2659
2660 spin = gtk_spin_button_new_with_range(1, MAX_NUM_PLAYERS, 1);
2661 start_aifill_spin = spin;
2662 gtk_spin_button_set_digits(GTK_SPIN_BUTTON(spin), 0);
2663 gtk_spin_button_set_update_policy(GTK_SPIN_BUTTON(spin),
2664 GTK_UPDATE_IF_VALID);
2665 if (server_optset != NULL) {
2666 struct option *paifill = optset_option_by_name(server_optset, "aifill");
2667 if (paifill) {
2668 gtk_spin_button_set_value(GTK_SPIN_BUTTON(spin),
2669 option_int_get(paifill));
2670 } /* else it'll be updated later */
2671 }
2672 g_signal_connect_after(spin, "value_changed",
2673 G_CALLBACK(ai_fill_changed_by_user), NULL);
2674
2675 gtk_grid_attach(GTK_GRID(table), spin, 1, 0, 1, 1);
2676
2677 label = g_object_new(GTK_TYPE_LABEL,
2678 "use-underline", TRUE,
2679 "mnemonic-widget", spin,
2680 /* TRANS: Keep individual lines short */
2681 "label", _("Number of _Players\n(including AI):"),
2682 "xalign", 0.0,
2683 "yalign", 0.5,
2684 NULL);
2685 gtk_grid_attach(GTK_GRID(table), label, 0, 0, 1, 1);
2686
2687 ai_lvl_combobox = gtk_combo_box_text_new();
2688
2689 for (level = 0; level < AI_LEVEL_COUNT; level++) {
2691 const char *level_name = ai_level_translated_name(level);
2692
2693 gtk_combo_box_text_insert_text(GTK_COMBO_BOX_TEXT(ai_lvl_combobox), i, level_name);
2694 levels[i] = level;
2695 i++;
2696 }
2697 }
2698 gtk_combo_box_set_active(GTK_COMBO_BOX(ai_lvl_combobox), -1);
2699 g_signal_connect(ai_lvl_combobox, "changed",
2700 G_CALLBACK(ai_skill_callback), levels);
2701
2702 gtk_grid_attach(GTK_GRID(table), ai_lvl_combobox, 1, 1, 1, 1);
2703
2704 label = g_object_new(GTK_TYPE_LABEL,
2705 "use-underline", TRUE,
2706 "mnemonic-widget", ai_lvl_combobox,
2707 "label", _("AI Skill _Level:"),
2708 "xalign", 0.0,
2709 "yalign", 0.5,
2710 NULL);
2711 gtk_grid_attach(GTK_GRID(table), label, 0, 1, 1, 1);
2712
2713 ruleset_combo = gtk_combo_box_text_new();
2714 g_signal_connect(G_OBJECT(ruleset_combo), "changed",
2715 G_CALLBACK(ruleset_entry_changed), NULL);
2716
2717 gtk_grid_attach(GTK_GRID(table), ruleset_combo, 1, 2, 1, 1);
2718
2719 label = g_object_new(GTK_TYPE_LABEL,
2720 "use-underline", TRUE,
2721 "mnemonic-widget", GTK_COMBO_BOX_TEXT(ruleset_combo),
2722 "label", _("Ruleset:"),
2723 "xalign", 0.0,
2724 "yalign", 0.5,
2725 NULL);
2726 gtk_grid_attach(GTK_GRID(table), label, 0, 2, 1, 1);
2727
2728 button = icon_label_button_new("preferences-system",
2729 _("_More Game Options..."));
2730 g_object_set(button, "margin", 8, NULL);
2731 gtk_widget_set_halign(button, GTK_ALIGN_CENTER);
2732 gtk_widget_set_valign(button, GTK_ALIGN_CENTER);
2733 g_signal_connect(button, "clicked",
2734 G_CALLBACK(game_options_callback), NULL);
2735 gtk_container_add(GTK_CONTAINER(vbox), button);
2736
2738 view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(connection_list_store));
2739 gtk_widget_set_hexpand(view, TRUE);
2740 g_object_unref(G_OBJECT(connection_list_store));
2741 gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(view), TRUE);
2742 connection_list_view = GTK_TREE_VIEW(view);
2743
2744 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(view));
2745 gtk_tree_selection_set_mode(selection, GTK_SELECTION_SINGLE);
2746 g_signal_connect(selection, "changed",
2747 G_CALLBACK(update_start_page_buttons), NULL);
2748
2749 add_tree_col(view, G_TYPE_STRING, _("Name"),
2750 CL_COL_USER_NAME, NULL);
2751 add_tree_col(view, G_TYPE_BOOLEAN, _("Ready"),
2752 CL_COL_READY_STATE, NULL);
2753 add_tree_col(view, G_TYPE_STRING, Q_("?player:Leader"),
2754 CL_COL_PLAYER_NAME, NULL);
2755 add_tree_col(view, GDK_TYPE_PIXBUF, _("Flag"),
2756 CL_COL_FLAG, NULL);
2757 add_tree_col(view, GDK_TYPE_PIXBUF, _("Border"),
2758 CL_COL_COLOR, NULL);
2759 add_tree_col(view, G_TYPE_STRING, _("Nation"),
2760 CL_COL_NATION, NULL);
2761 add_tree_col(view, G_TYPE_STRING, _("Team"),
2762 CL_COL_TEAM, NULL);
2763
2764 g_signal_connect(view, "button-press-event",
2765 G_CALLBACK(connection_list_event), NULL);
2766 g_signal_connect(view, "row-collapsed",
2767 G_CALLBACK(connection_list_row_callback),
2768 GINT_TO_POINTER(TRUE));
2769 g_signal_connect(view, "row-expanded",
2770 G_CALLBACK(connection_list_row_callback),
2771 GINT_TO_POINTER(FALSE));
2772
2773 sw = gtk_scrolled_window_new(NULL, NULL);
2774 gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(sw),
2775 GTK_SHADOW_ETCHED_IN);
2776 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(sw),
2777 GTK_POLICY_AUTOMATIC,
2778 GTK_POLICY_ALWAYS);
2779 gtk_scrolled_window_set_min_content_height(GTK_SCROLLED_WINDOW(sw), 200);
2780 gtk_container_add(GTK_CONTAINER(sw), view);
2781 gtk_container_add(GTK_CONTAINER(sbox), sw);
2782
2783
2784 sw = gtk_scrolled_window_new(NULL, NULL);
2785 gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(sw),
2786 GTK_SHADOW_ETCHED_IN);
2787 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(sw),
2788 GTK_POLICY_AUTOMATIC,
2789 GTK_POLICY_ALWAYS);
2790 gtk_container_add(GTK_CONTAINER(box), sw);
2791
2792 text = gtk_text_view_new_with_buffer(message_buffer);
2793 gtk_widget_set_hexpand(text, TRUE);
2794 gtk_widget_set_vexpand(text, TRUE);
2795 start_message_area = text;
2796 gtk_widget_set_name(text, "chatline");
2797 gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(text), GTK_WRAP_WORD);
2798 gtk_text_view_set_left_margin(GTK_TEXT_VIEW(text), 5);
2799 gtk_text_view_set_editable(GTK_TEXT_VIEW(text), FALSE);
2800 gtk_container_add(GTK_CONTAINER(sw), text);
2801
2802
2803 /* Vote widgets. */
2804 if (pregame_votebar == NULL) {
2806 }
2807 gtk_container_add(GTK_CONTAINER(box), pregame_votebar);
2808
2809
2810 toolkit_view = inputline_toolkit_view_new();
2811 gtk_container_add(GTK_CONTAINER(box), toolkit_view);
2812
2813 button = gtk_button_new_with_mnemonic(_("_Cancel"));
2814 inputline_toolkit_view_append_button(toolkit_view, button);
2815 g_signal_connect(button, "clicked", G_CALLBACK(main_callback), NULL);
2816
2817 nation_button = icon_label_button_new("document-properties",
2818 _("Pick _Nation"));
2820 g_signal_connect(nation_button, "clicked",
2821 G_CALLBACK(nation_button_callback), NULL);
2822
2823 observe_button = icon_label_button_new("zoom-in", _("_Observe"));
2825 g_signal_connect(observe_button, "clicked",
2826 G_CALLBACK(observe_button_callback), NULL);
2827
2828 ready_button = icon_label_button_new("system-run", _("_Ready"));
2830 g_signal_connect(ready_button, "clicked",
2831 G_CALLBACK(ready_button_callback), NULL);
2832
2833 return box;
2834}
2835
2836
2837/**********************************************************************/
2840void handle_game_load(bool load_successful, const char *filename)
2841{
2842 if (load_successful) {
2843 set_client_page(PAGE_START);
2844
2845 if (game.info.is_new_game) {
2846 /* It's pregame. Create a player and connect to it */
2847 send_chat("/take -");
2848 }
2849 }
2850}
2851
2852/**********************************************************************/
2855static void load_filename(const char *filename)
2856{
2857 send_chat_printf("/load %s", filename);
2858}
2859
2860/**********************************************************************/
2863static void load_callback(void)
2864{
2865 GtkTreeIter it;
2866 const gchar *filename;
2867
2868 if (!gtk_tree_selection_get_selected(load_selection, NULL, &it)) {
2869 return;
2870 }
2871
2872 gtk_tree_model_get(GTK_TREE_MODEL(load_store), &it,
2873 SD_COL_FULL_PATH, &filename, -1);
2874 load_filename(filename);
2875}
2876
2877/**********************************************************************/
2880static void load_browse_callback(GtkWidget *w, gpointer data)
2881{
2882 save_dialog_file_chooser_popup(_("Choose Saved Game to Load"),
2883 GTK_FILE_CHOOSER_ACTION_OPEN,
2885}
2886
2887/**********************************************************************/
2890static void update_load_page(void)
2891{
2892 /* Search for user saved games. */
2893 struct fileinfo_list *files = fileinfolist_infix(get_save_dirs(),
2894 ".sav", FALSE);
2895
2897 fileinfo_list_destroy(files);
2898}
2899
2900/**********************************************************************/
2903GtkWidget *create_load_page(void)
2904{
2905 GtkWidget *box, *sbox, *bbox;
2906
2907 GtkWidget *button, *label, *view, *sw;
2908 GtkCellRenderer *rend;
2909
2910 box = gtk_grid_new();
2911 gtk_orientable_set_orientation(GTK_ORIENTABLE(box),
2912 GTK_ORIENTATION_VERTICAL);
2913 gtk_grid_set_row_spacing(GTK_GRID(box), 18);
2914 gtk_container_set_border_width(GTK_CONTAINER(box), 4);
2915
2917 view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(load_store));
2918 gtk_widget_set_vexpand(view, TRUE);
2919 g_object_unref(load_store);
2920
2921 rend = gtk_cell_renderer_text_new();
2922 gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(view),
2923 -1, NULL, rend, "text", 0, NULL);
2924
2925 load_selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(view));
2926 gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(view), FALSE);
2927
2928 gtk_tree_selection_set_mode(load_selection, GTK_SELECTION_SINGLE);
2929
2930 g_signal_connect(view, "row-activated",
2931 G_CALLBACK(load_callback), NULL);
2932
2933 sbox = gtk_grid_new();
2934 gtk_widget_set_halign(sbox, GTK_ALIGN_CENTER);
2935 gtk_orientable_set_orientation(GTK_ORIENTABLE(sbox),
2936 GTK_ORIENTATION_VERTICAL);
2937 gtk_grid_set_row_spacing(GTK_GRID(sbox), 2);
2938 gtk_container_add(GTK_CONTAINER(box), sbox);
2939
2940 label = g_object_new(GTK_TYPE_LABEL,
2941 "use-underline", TRUE,
2942 "mnemonic-widget", view,
2943 "label", _("Choose Saved Game to _Load:"),
2944 "xalign", 0.0,
2945 "yalign", 0.5,
2946 NULL);
2947 gtk_container_add(GTK_CONTAINER(sbox), label);
2948
2949 sw = gtk_scrolled_window_new(NULL, NULL);
2950 gtk_scrolled_window_set_min_content_width(GTK_SCROLLED_WINDOW(sw), 300);
2951 gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(sw),
2952 GTK_SHADOW_ETCHED_IN);
2953 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(sw), GTK_POLICY_AUTOMATIC,
2954 GTK_POLICY_AUTOMATIC);
2955 gtk_container_add(GTK_CONTAINER(sw), view);
2956 gtk_container_add(GTK_CONTAINER(sbox), sw);
2957
2958 bbox = gtk_button_box_new(GTK_ORIENTATION_HORIZONTAL);
2959 gtk_widget_set_hexpand(bbox, TRUE);
2960 gtk_button_box_set_layout(GTK_BUTTON_BOX(bbox), GTK_BUTTONBOX_END);
2961 gtk_box_set_spacing(GTK_BOX(bbox), 12);
2962 gtk_container_add(GTK_CONTAINER(box), bbox);
2963
2964 button = gtk_button_new_with_mnemonic(_("_Browse..."));
2965 gtk_container_add(GTK_CONTAINER(bbox), button);
2966 gtk_button_box_set_child_secondary(GTK_BUTTON_BOX(bbox), button, TRUE);
2967 g_signal_connect(button, "clicked",
2968 G_CALLBACK(load_browse_callback), NULL);
2969
2970 button = gtk_button_new_with_mnemonic(_("_Cancel"));
2971 gtk_container_add(GTK_CONTAINER(bbox), button);
2972 g_signal_connect(button, "clicked",
2973 G_CALLBACK(main_callback), NULL);
2974
2975 button = gtk_button_new_with_mnemonic(_("_OK"));
2976 gtk_container_add(GTK_CONTAINER(bbox), button);
2977 g_signal_connect(button, "clicked",
2978 G_CALLBACK(load_callback), NULL);
2979
2980 return box;
2981}
2982
2983/**********************************************************************/
2986static void scenario_list_callback(void)
2987{
2988 GtkTreeIter it;
2989 GtkTextBuffer *buffer;
2990 char *description;
2991 char *authors;
2992 char *filename;
2993 int ver;
2994 char vername[50];
2995
2996 if (gtk_tree_selection_get_selected(scenario_selection, NULL, &it)) {
2997 gtk_tree_model_get(GTK_TREE_MODEL(scenario_store), &it,
2998 2, &description, -1);
2999 gtk_tree_model_get(GTK_TREE_MODEL(scenario_store), &it,
3000 3, &authors, -1);
3001 gtk_tree_model_get(GTK_TREE_MODEL(scenario_store), &it,
3002 1, &filename, -1);
3003 gtk_tree_model_get(GTK_TREE_MODEL(scenario_store), &it,
3004 4, &ver, -1);
3005 filename = skip_to_basename(filename);
3006 if (ver > 0) {
3007 int maj;
3008 int min;
3009
3010 maj = ver / 1000000;
3011 ver %= 1000000;
3012 min = ver / 10000;
3013 ver %= 10000;
3014 if (ver >= 9000) {
3015 /* Development version, have '+' */
3016 fc_snprintf(vername, sizeof(vername), "%d.%d+", maj, min);
3017 } else {
3018 fc_snprintf(vername, sizeof(vername), "%d.%d", maj, min);
3019 }
3020 } else {
3021 /* TRANS: Old scenario format version */
3022 fc_snprintf(vername, sizeof(vername), _("pre-2.6"));
3023 }
3024 } else {
3025 description = "";
3026 authors = "";
3027 filename = "";
3028 vername[0] = '\0';
3029 }
3030
3031 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(scenario_description));
3032 gtk_text_buffer_set_text(buffer, description, -1);
3033 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(scenario_authors));
3034 gtk_text_buffer_set_text(buffer, authors, -1);
3035 gtk_label_set_text(GTK_LABEL(scenario_filename), filename);
3036 gtk_label_set_text(GTK_LABEL(scenario_version), vername);
3037}
3038
3039/**********************************************************************/
3042static void scenario_callback(void)
3043{
3044 GtkTreeIter it;
3045 char *filename;
3046
3047 if (!gtk_tree_selection_get_selected(scenario_selection, NULL, &it)) {
3048 return;
3049 }
3050
3051 gtk_tree_model_get(GTK_TREE_MODEL(scenario_store), &it, 1, &filename, -1);
3052 load_filename(filename);
3053}
3054
3055/**********************************************************************/
3058static void scenario_browse_callback(GtkWidget *w, gpointer data)
3059{
3060 save_dialog_file_chooser_popup(_("Choose a Scenario"),
3061 GTK_FILE_CHOOSER_ACTION_OPEN,
3063}
3064
3065/**********************************************************************/
3068static void update_scenario_page(void)
3069{
3070 struct fileinfo_list *files;
3071
3072 gtk_list_store_clear(scenario_store);
3073
3074 /* search for scenario files. */
3075 files = fileinfolist_infix(get_scenario_dirs(), ".sav", TRUE);
3076 fileinfo_list_iterate(files, pfile) {
3077 struct section_file *sf;
3078
3079 if ((sf = secfile_load_section(pfile->fullname, "scenario", TRUE))
3080 && secfile_lookup_bool_default(sf, TRUE, "scenario.is_scenario")) {
3081 const char *sname, *sdescription, *sauthors;
3082 int fcver;
3083 int fcdev;
3084 int current_ver = MAJOR_VERSION * 1000000 + MINOR_VERSION * 10000;
3085 int current_dev;
3086
3087 current_dev = current_ver;
3088 if (PATCH_VERSION >= 90) {
3089 /* Patch level matters on development versions */
3090 current_dev += PATCH_VERSION * 100;
3091 }
3092
3093 fcver = secfile_lookup_int_default(sf, 0, "scenario.game_version");
3094 if (fcver < 30000) {
3095 /* Pre-3.0 versions stored version number without emergency version
3096 * part in the end. To get comparable version number stored,
3097 * multiply by 100. */
3098 fcver *= 100;
3099 }
3100 if (fcver % 10000 >= 9000) {
3101 fcdev = fcver - (fcver % 100); /* Emergency version does not count. */
3102 } else {
3103 fcdev = fcver - (fcver % 10000); /* Patch version does not count. */
3104 }
3105 sname = secfile_lookup_str_default(sf, NULL, "scenario.name");
3106 sdescription = secfile_lookup_str_default(sf, NULL,
3107 "scenario.description");
3108 sauthors = secfile_lookup_str_default(sf, NULL, "scenario.authors");
3109 log_debug("scenario file: %s from %s", sname, pfile->fullname);
3110
3111 /* Ignore scenarios for newer freeciv versions than we are. */
3112 if (fcdev <= current_dev) {
3113 bool add_new = TRUE;
3114
3115 if (sname != NULL) {
3116 GtkTreeIter it;
3117 bool valid;
3118
3119 valid = gtk_tree_model_get_iter_first(GTK_TREE_MODEL(scenario_store), &it);
3120 while (valid) {
3121 char *oname;
3122
3123 gtk_tree_model_get(GTK_TREE_MODEL(scenario_store), &it,
3124 0, &oname, -1);
3125
3126 if (!strcmp(sname, oname)) {
3127 /* Already listed scenario has the same name as the one we just found */
3128 int existing;
3129
3130 gtk_tree_model_get(GTK_TREE_MODEL(scenario_store), &it,
3131 4, &existing, -1);
3132 log_debug("Duplicate %s (%d vs %d)", sname, existing, fcver);
3133
3134 if (existing > fcver) {
3135 /* Already listed one has higher version number */
3136 add_new = FALSE;
3137 } else if (existing < fcver) {
3138 /* New one has higher version number */
3139 add_new = FALSE;
3140
3141 gtk_list_store_set(scenario_store, &it,
3142 0, sname && strlen(sname) ? Q_(sname) : pfile->name,
3143 1, pfile->fullname,
3144 2, (NULL != sdescription && '\0' != sdescription[0]
3145 ? Q_(sdescription) : ""),
3146 3, (NULL != sauthors && sauthors[0] != '\0'
3147 ? Q_(sauthors) : ""),
3148 4, fcver,
3149 -1);
3150 } else {
3151 /* Same version number -> list both */
3152 }
3153 }
3154 valid = gtk_tree_model_iter_next(GTK_TREE_MODEL(scenario_store), &it);
3155 }
3156 }
3157
3158 if (add_new) {
3159 GtkTreeIter it;
3160
3161 gtk_list_store_append(scenario_store, &it);
3162 gtk_list_store_set(scenario_store, &it,
3163 0, sname && strlen(sname) ? Q_(sname) : pfile->name,
3164 1, pfile->fullname,
3165 2, (NULL != sdescription && '\0' != sdescription[0]
3166 ? Q_(sdescription) : ""),
3167 3, (NULL != sauthors && sauthors[0] != '\0'
3168 ? Q_(sauthors) : ""),
3169 4, fcver,
3170 -1);
3171 }
3172 }
3173
3174 secfile_destroy(sf);
3175 }
3177
3178 fileinfo_list_destroy(files);
3179}
3180
3181/**********************************************************************/
3184GtkWidget *create_scenario_page(void)
3185{
3186 GtkWidget *vbox, *hbox, *sbox, *bbox, *filenamebox, *descbox;
3187 GtkWidget *versionbox, *vertext;
3188 GtkWidget *button, *label, *view, *sw, *swa, *text;
3189 GtkCellRenderer *rend;
3190
3191 vbox = gtk_grid_new();
3192 gtk_orientable_set_orientation(GTK_ORIENTABLE(vbox),
3193 GTK_ORIENTATION_VERTICAL);
3194 gtk_grid_set_row_spacing(GTK_GRID(vbox), 18);
3195 gtk_container_set_border_width(GTK_CONTAINER(vbox), 4);
3196
3197 scenario_store = gtk_list_store_new(5, G_TYPE_STRING,
3198 G_TYPE_STRING,
3199 G_TYPE_STRING,
3200 G_TYPE_STRING,
3201 G_TYPE_INT);
3202 view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(scenario_store));
3203 gtk_widget_set_hexpand(view, TRUE);
3204 gtk_widget_set_vexpand(view, TRUE);
3205 g_object_unref(scenario_store);
3206
3207 rend = gtk_cell_renderer_text_new();
3208 gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(view),
3209 -1, NULL, rend, "text", 0, NULL);
3210
3211 scenario_selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(view));
3212 g_signal_connect(scenario_selection, "changed",
3213 G_CALLBACK(scenario_list_callback), NULL);
3214 gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(view), FALSE);
3215
3216 gtk_tree_selection_set_mode(scenario_selection, GTK_SELECTION_SINGLE);
3217
3218 g_signal_connect(view, "row-activated",
3219 G_CALLBACK(scenario_callback), NULL);
3220
3221 label = g_object_new(GTK_TYPE_LABEL,
3222 "use-underline", TRUE,
3223 "mnemonic-widget", view,
3224 "label", _("Choose a _Scenario:"),
3225 "xalign", 0.0,
3226 "yalign", 0.5,
3227 NULL);
3228 gtk_container_add(GTK_CONTAINER(vbox), label);
3229
3230 sbox = gtk_grid_new();
3231 gtk_grid_set_column_spacing(GTK_GRID(sbox), 12);
3232 gtk_grid_set_row_homogeneous(GTK_GRID(sbox), TRUE);
3233 gtk_orientable_set_orientation(GTK_ORIENTABLE(sbox),
3234 GTK_ORIENTATION_VERTICAL);
3235 gtk_grid_set_row_spacing(GTK_GRID(sbox), 2);
3236 gtk_container_add(GTK_CONTAINER(vbox), sbox);
3237
3238 hbox = gtk_grid_new();
3239 gtk_grid_set_column_homogeneous(GTK_GRID(hbox), TRUE);
3240 gtk_grid_set_column_spacing(GTK_GRID(hbox), 12);
3241
3242 sw = gtk_scrolled_window_new(NULL, NULL);
3243 gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(sw),
3244 GTK_SHADOW_ETCHED_IN);
3245 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(sw), GTK_POLICY_AUTOMATIC,
3246 GTK_POLICY_AUTOMATIC);
3247 gtk_container_add(GTK_CONTAINER(sw), view);
3248 gtk_grid_attach(GTK_GRID(sbox), sw, 0, 0, 1, 2);
3249
3250 text = gtk_text_view_new();
3251 gtk_widget_set_hexpand(text, TRUE);
3252 gtk_widget_set_vexpand(text, TRUE);
3253 gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(text), GTK_WRAP_WORD);
3254 gtk_text_view_set_left_margin(GTK_TEXT_VIEW(text), 2);
3255 gtk_text_view_set_editable(GTK_TEXT_VIEW(text), FALSE);
3256 scenario_description = text;
3257
3258 sw = gtk_scrolled_window_new(NULL, NULL);
3259 gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(sw),
3260 GTK_SHADOW_ETCHED_IN);
3261 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(sw), GTK_POLICY_AUTOMATIC,
3262 GTK_POLICY_AUTOMATIC);
3263 gtk_container_add(GTK_CONTAINER(sw), text);
3264
3265 text = gtk_text_view_new();
3266 gtk_widget_set_hexpand(text, TRUE);
3267 gtk_widget_set_vexpand(text, TRUE);
3268 gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(text), GTK_WRAP_WORD);
3269 gtk_text_view_set_left_margin(GTK_TEXT_VIEW(text), 2);
3270 gtk_text_view_set_editable(GTK_TEXT_VIEW(text), FALSE);
3271 scenario_authors = text;
3272
3273 swa = gtk_scrolled_window_new(NULL, NULL);
3274 gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(swa),
3275 GTK_SHADOW_ETCHED_IN);
3276 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(swa), GTK_POLICY_AUTOMATIC,
3277 GTK_POLICY_AUTOMATIC);
3278 gtk_container_add(GTK_CONTAINER(swa), text);
3279
3280 text = gtk_label_new(_("Filename:"));
3281 scenario_filename = gtk_label_new("");
3282 gtk_widget_set_halign(scenario_filename, GTK_ALIGN_START);
3283 gtk_widget_set_valign(scenario_filename, GTK_ALIGN_CENTER);
3284 gtk_label_set_selectable(GTK_LABEL(scenario_filename), TRUE);
3285
3286 filenamebox = gtk_grid_new();
3287 gtk_grid_set_column_spacing(GTK_GRID(hbox), 12);
3288 g_object_set(filenamebox, "margin", 5, NULL);
3289
3290 gtk_container_add(GTK_CONTAINER(filenamebox), text);
3291 gtk_container_add(GTK_CONTAINER(filenamebox), scenario_filename);
3292
3293 /* TRANS: Scenario format version */
3294 vertext = gtk_label_new(_("Format:"));
3295 scenario_version = gtk_label_new("");
3296 gtk_widget_set_halign(scenario_version, GTK_ALIGN_START);
3297 gtk_widget_set_valign(scenario_version, GTK_ALIGN_CENTER);
3298 gtk_label_set_selectable(GTK_LABEL(scenario_version), TRUE);
3299
3300 versionbox = gtk_grid_new();
3301 gtk_grid_set_column_spacing(GTK_GRID(hbox), 12);
3302 g_object_set(versionbox, "margin", 5, NULL);
3303
3304 gtk_container_add(GTK_CONTAINER(versionbox), vertext);
3305 gtk_container_add(GTK_CONTAINER(versionbox), scenario_version);
3306
3307 descbox = gtk_grid_new();
3308 gtk_orientable_set_orientation(GTK_ORIENTABLE(descbox),
3309 GTK_ORIENTATION_VERTICAL);
3310 gtk_grid_set_row_spacing(GTK_GRID(descbox), 6);
3311 gtk_container_add(GTK_CONTAINER(descbox), sw);
3312 gtk_container_add(GTK_CONTAINER(descbox), swa);
3313 gtk_container_add(GTK_CONTAINER(descbox), filenamebox);
3314 gtk_container_add(GTK_CONTAINER(descbox), versionbox);
3315 gtk_grid_attach(GTK_GRID(sbox), descbox, 1, 0, 1, 2);
3316
3317 bbox = gtk_button_box_new(GTK_ORIENTATION_HORIZONTAL);
3318 gtk_button_box_set_layout(GTK_BUTTON_BOX(bbox), GTK_BUTTONBOX_END);
3319 gtk_box_set_spacing(GTK_BOX(bbox), 12);
3320 gtk_container_add(GTK_CONTAINER(vbox), bbox);
3321
3322 button = gtk_button_new_with_mnemonic(_("_Browse..."));
3323 gtk_container_add(GTK_CONTAINER(bbox), button);
3324 gtk_button_box_set_child_secondary(GTK_BUTTON_BOX(bbox), button, TRUE);
3325 g_signal_connect(button, "clicked",
3326 G_CALLBACK(scenario_browse_callback), NULL);
3327
3328 button = gtk_button_new_with_mnemonic(_("_Cancel"));
3329 gtk_container_add(GTK_CONTAINER(bbox), button);
3330 g_signal_connect(button, "clicked",
3331 G_CALLBACK(main_callback), NULL);
3332
3333 button = gtk_button_new_with_mnemonic(_("_OK"));
3334 gtk_container_add(GTK_CONTAINER(bbox), button);
3335 g_signal_connect(button, "clicked",
3336 G_CALLBACK(scenario_callback), NULL);
3337
3338 return vbox;
3339}
3340
3341/**********************************************************************/
3344enum client_pages get_current_client_page(void)
3345{
3346 return current_page;
3347}
3348
3349/**********************************************************************/
3352void real_set_client_page(enum client_pages new_page)
3353{
3354 /* Don't use current_page directly here because maybe it could be modified
3355 * before we reach the end of this function. */
3356 enum client_pages old_page = current_page;
3357
3358 /* If the page remains the same, don't do anything. */
3359 if (old_page == new_page) {
3360 return;
3361 }
3362
3363 log_debug("Switching client page from %s to %s.",
3364 -1 == old_page ? "(no page)" : client_pages_name(old_page),
3365 client_pages_name(new_page));
3366
3367 current_page = new_page;
3368
3369 switch (old_page) {
3370 case PAGE_SCENARIO:
3371 case PAGE_LOAD:
3372 break;
3373 case PAGE_GAME:
3374 {
3376
3378 if (vmode == NULL) {
3379 gtk_window_unmaximize(GTK_WINDOW(toplevel));
3380 }
3381 }
3382 break;
3383 default:
3384 break;
3385 }
3386
3387 switch (new_page) {
3388 case PAGE_MAIN:
3389 case PAGE_START:
3390 if (is_server_running()) {
3391 if (game.info.is_new_game) {
3392 gtk_widget_show(start_options_table);
3393 } else {
3394 gtk_widget_hide(start_options_table);
3395 }
3397 } else {
3398 gtk_widget_hide(start_options_table);
3399 }
3403 break;
3404 case PAGE_GAME:
3405 {
3407
3410 if (vmode == NULL) {
3411 gtk_window_maximize(GTK_WINDOW(toplevel));
3412 }
3415 }
3416 break;
3417 case PAGE_LOAD:
3419 break;
3420 case PAGE_SCENARIO:
3422 break;
3423 case PAGE_NETWORK:
3425 break;
3426 }
3427
3428 /* hide/show statusbar. */
3429 if (new_page == PAGE_START || new_page == PAGE_GAME) {
3431 gtk_widget_hide(statusbar_frame);
3432 } else {
3433 gtk_widget_show(statusbar_frame);
3434 }
3435
3436 gtk_notebook_set_current_page(GTK_NOTEBOOK(toplevel_tabs), new_page);
3437
3438 /* Update the GUI. */
3439 while (gtk_events_pending()) {
3440 gtk_main_iteration();
3441 }
3442
3443 switch (new_page) {
3444 case PAGE_MAIN:
3445 break;
3446 case PAGE_START:
3449 break;
3450 case PAGE_LOAD:
3451 gtk_tree_view_focus(gtk_tree_selection_get_tree_view(load_selection));
3452 break;
3453 case PAGE_SCENARIO:
3454 gtk_tree_view_focus(gtk_tree_selection_get_tree_view(scenario_selection));
3455 break;
3456 case PAGE_GAME:
3460 mapview_thaw();
3461 break;
3462 case PAGE_NETWORK:
3464 gtk_widget_grab_focus(network_login);
3465 gtk_editable_set_position(GTK_EDITABLE(network_login), 0);
3467 break;
3468 }
3469}
3470
3471/****************************************************************************
3472 SAVE GAME DIALOGs
3473****************************************************************************/
3474
3475/**********************************************************************/
3478static struct fileinfo_list *save_dialog_savegame_list(void)
3479{
3480 return fileinfolist_infix(get_save_dirs(), ".sav", FALSE);
3481}
3482
3483/**********************************************************************/
3487{
3488 static GtkWidget *shell = NULL;
3489
3490 if (NULL != shell) {
3491 return;
3492 }
3493
3494 shell = save_dialog_new(_("Save Game"), _("Saved _Games:"),
3495 _("Save _Filename:"), send_save_game,
3497 g_signal_connect(shell, "destroy", G_CALLBACK(gtk_widget_destroyed),
3498 &shell);
3499 gtk_window_present(GTK_WINDOW(shell));
3500}
3501
3502/**********************************************************************/
3505static void save_dialog_save_scenario(const char *filename)
3506{
3508}
3509
3510/**********************************************************************/
3513static struct fileinfo_list *save_dialog_scenario_list(void)
3514{
3515 return fileinfolist_infix(get_scenario_dirs(), ".sav", FALSE);
3516}
3517
3518/**********************************************************************/
3522{
3523 static GtkWidget *shell = NULL;
3524
3525 if (NULL != shell) {
3526 return;
3527 }
3528
3529 shell = save_dialog_new(_("Save Scenario"), _("Saved Sce_narios:"),
3530 _("Save Sc_enario:"), save_dialog_save_scenario,
3532 g_signal_connect(shell, "destroy", G_CALLBACK(gtk_widget_destroyed),
3533 &shell);
3534 gtk_window_present(GTK_WINDOW(shell));
3535}
3536
3537/**********************************************************************/
3542static struct fileinfo_list *save_dialog_mapimg_list(void)
3543{
3544 return fileinfolist_infix(get_save_dirs(), ".map", FALSE);
3545}
3546
3547/**********************************************************************/
3551{
3552 static GtkWidget *shell = NULL;
3553
3554 if (NULL != shell) {
3555 return;
3556 }
3557
3558 shell = save_dialog_new(_("Save Map Image"), _("Saved Map _Images:"),
3559 _("Save _Map Images:"), mapimg_client_save,
3561 g_signal_connect(shell, "destroy", G_CALLBACK(gtk_widget_destroyed),
3562 &shell);
3563 gtk_window_present(GTK_WINDOW(shell));
3564}
3565
3566/**********************************************************************/
3569void mapimg_client_save(const char *filename)
3570{
3571 if (!mapimg_client_createmap(filename)) {
3572 char msg[512];
3573
3574 fc_snprintf(msg, sizeof(msg), "(%s)", mapimg_error());
3575 popup_notify_dialog(_("Error"),
3576 _("Error Creating the Map Image!"), msg);
3577 }
3578}
3579
3580/**********************************************************************/
3585void set_rulesets(int num_rulesets, char **rulesets)
3586{
3587 int i;
3588 int def_idx = -1;
3589
3590 gtk_combo_box_text_remove_all(GTK_COMBO_BOX_TEXT(ruleset_combo));
3591 for (i = 0; i < num_rulesets; i++) {
3592
3593 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(ruleset_combo), rulesets[i]);
3594 if (!strcmp("default", rulesets[i])) {
3595 def_idx = i;
3596 }
3597 }
3598
3600
3601 /* HACK: server should tell us the current ruleset. */
3602 gtk_combo_box_set_active(GTK_COMBO_BOX(ruleset_combo), def_idx);
3603
3605}
#define n
Definition astring.c:77
struct canvas int int struct sprite int int int int height
Definition canvas_g.h:44
struct canvas int int struct sprite int int int width
Definition canvas_g.h:44
int send_chat_printf(const char *format,...)
int send_chat(const char *message)
void output_window_append(const struct ft_color color, const char *featured_text)
bool can_client_control(void)
bool client_is_global_observer(void)
bool client_is_observer(void)
char user_name[512]
char server_host[512]
struct civclient client
enum client_states client_state(void)
char fc_password[MAX_LEN_PASSWORD]
int server_port
#define client_player()
@ C_S_PREPARING
Definition client_main.h:46
enum ai_level server_ai_level(void)
Definition climisc.c:1512
bool mapimg_client_createmap(const char *filename)
Definition climisc.c:1459
void center_on_something(void)
Definition climisc.c:424
static struct fc_sockaddr_list * list
Definition clinet.c:102
int connect_to_server(const char *username, const char *hostname, int port, char *errbuf, int errbufsize)
Definition clinet.c:248
void disconnect_from_server(void)
Definition clinet.c:305
void send_save_game(const char *filename)
void set_ruleset(const char *ruleset)
bool is_server_running(void)
bool client_start_server(void)
bool can_client_access_hack(void)
const char * conn_description(const struct connection *pconn)
Definition connection.c:473
struct connection * conn_by_number(int id)
Definition connection.c:420
#define conn_list_iterate(connlist, pconn)
Definition connection.h:113
#define conn_list_iterate_end
Definition connection.h:115
popup_notify_dialog
Definition dialogs_g.h:36
int int id
Definition editgui_g.h:28
enum event_type event
Definition events.c:81
#define MAX_NUM_PLAYERS
Definition fc_types.h:36
#define MAX_LEN_NAME
Definition fc_types.h:66
#define Q_(String)
Definition fcintl.h:70
#define _(String)
Definition fcintl.h:67
void fc_allocate_mutex(fc_mutex *mutex)
void fc_release_mutex(fc_mutex *mutex)
const struct ft_color ftc_client
struct civ_game game
Definition game.c:57
static PangoLayout * layout
Definition canvas.c:331
void inputline_toolkit_view_append_button(GtkWidget *toolkit_view, GtkWidget *button)
Definition chatline.c:1288
GtkWidget * inputline_toolkit_view_new(void)
Definition chatline.c:1266
void inputline_grab_focus(void)
Definition chatline.c:79
void chatline_scroll_to_bottom(bool delayed)
Definition chatline.c:1006
void popup_races_dialog(struct player *pplayer)
Definition dialogs.c:1223
static struct tile * pos
Definition finddlg.c:53
const char * client_string
Definition gui_main.c:104
GtkTextBuffer * message_buffer
Definition gui_main.c:177
struct video_mode * resolution_request_get(void)
Definition gui_main.c:2396
void enable_menus(bool enable)
Definition gui_main.c:1001
void refresh_chat_buttons(void)
Definition gui_main.c:2313
void reset_unit_table(void)
Definition gui_main.c:972
GtkWidget * toplevel
Definition gui_main.c:124
void quit_gtk_main(void)
Definition gui_main.c:2142
static struct video_mode vmode
Definition gui_main.c:187
GtkWidget * toplevel_tabs
Definition gui_main.c:126
int screen_height(void)
Definition gui_main.c:2376
#define GUI_GTK_OPTION(optname)
Definition gui_main.h:25
void gtk_tree_view_focus(GtkTreeView *view)
Definition gui_stuff.c:230
void gtk_stockbutton_set_label(GtkWidget *button, const gchar *label_text)
Definition gui_stuff.c:90
GtkTreeViewColumn * add_treeview_column(GtkWidget *view, const char *title, GType gtype, int model_index)
Definition gui_stuff.c:1120
void setup_dialog(GtkWidget *shell, GtkWidget *parent)
Definition gui_stuff.c:281
void mapview_freeze(void)
Definition mapview.c:356
void overview_size_changed(void)
Definition mapview.c:309
void mapview_thaw(void)
Definition mapview.c:364
static struct gui_dialog * shell
Definition messagedlg.c:39
void option_dialog_popup(const char *name, const struct option_set *poptset)
Definition optiondlg.c:979
static GtkListStore * load_store
Definition pages.c:68
static void update_network_page(void)
Definition pages.c:1183
void handle_game_load(bool load_successful, const char *filename)
Definition pages.c:2839
GtkWidget * create_start_page(void)
Definition pages.c:2621
GtkWidget * create_network_page(void)
Definition pages.c:1199
static void client_aitoggle_player(void *data)
Definition pages.c:1507
static GtkWidget * nation_button
Definition pages.c:1458
static void update_server_list(enum server_scan_type sstype, const struct server_list *list)
Definition pages.c:703
static void start_scenario_callback(GtkWidget *w, gpointer data)
Definition pages.c:115
static GtkTreeSelection * scenario_selection
Definition pages.c:73
static void update_start_page_buttons(void)
Definition pages.c:2197
static void save_dialog_response_callback(GtkWidget *w, gint response, gpointer data)
Definition pages.c:488
GtkWidget * create_scenario_page(void)
Definition pages.c:3184
static void show_conn_popup(struct player *pplayer, struct connection *pconn)
Definition pages.c:1764
static GtkWidget * network_confirm_password_label
Definition pages.c:698
void save_mapimg_dialog_popup(void)
Definition pages.c:3551
static void ruleset_selected(const char *name)
Definition pages.c:1613
static void ready_button_callback(GtkWidget *w, gpointer data)
Definition pages.c:2143
static bool conn_list_selection(struct player **ppplayer, struct connection **ppconn)
Definition pages.c:2056
static struct server_scan_timer_data meta_scan
Definition pages.c:86
static void update_network_lists(void)
Definition pages.c:837
static GtkWidget * statusbar
Definition pages.c:89
static GtkWidget * server_playerlist_view
Definition pages.c:71
static GtkListStore * save_dialog_store_new(void)
Definition pages.c:399
void append_network_statusbar(const char *text, bool force)
Definition pages.c:893
static gboolean update_network_statusbar(gpointer data)
Definition pages.c:863
void set_rulesets(int num_rulesets, char **rulesets)
Definition pages.c:3586
void mapimg_client_save(const char *filename)
Definition pages.c:3570
static void conn_menu_player_command(GObject *object, gpointer data)
Definition pages.c:1723
static void load_filename(const char *filename)
Definition pages.c:2854
static void conn_menu_nation_chosen(GObject *object, gpointer data)
Definition pages.c:1710
static GtkWidget * scenario_description
Definition pages.c:63
void save_game_dialog_popup(void)
Definition pages.c:3487
enum client_pages get_current_client_page(void)
Definition pages.c:3345
static GtkListStore * lan_store
Definition pages.c:68
static enum connection_state connection_status
Definition pages.c:858
save_dialog_columns
Definition pages.c:383
@ SD_COL_NUM
Definition pages.c:387
@ SD_COL_FULL_PATH
Definition pages.c:385
@ SD_COL_PRETTY_NAME
Definition pages.c:384
static struct server_scan_timer_data lan_scan
Definition pages.c:87
static void save_dialog_list_callback(GtkTreeSelection *selection, gpointer data)
Definition pages.c:556
static void connect_network_game_callback(GtkWidget *w, gpointer data)
Definition pages.c:132
static GtkTreeView * connection_list_view
Definition pages.c:1460
static GtkWidget * network_login
Definition pages.c:694
static GtkWidget * network_login_label
Definition pages.c:694
connection_state
Definition pages.c:851
@ LOGIN_TYPE
Definition pages.c:852
@ NEW_PASSWORD_TYPE
Definition pages.c:853
@ WAITING_TYPE
Definition pages.c:855
@ ENTER_PASSWORD_TYPE
Definition pages.c:854
static void connect_callback(GtkWidget *w, gpointer data)
Definition pages.c:1042
static void scenario_browse_callback(GtkWidget *w, gpointer data)
Definition pages.c:3058
static GtkListStore * meta_store
Definition pages.c:68
connection_list_columns
Definition pages.c:1466
@ CL_COL_STYLE
Definition pages.c:1476
@ CL_COL_USER_NAME
Definition pages.c:1468
@ CL_COL_READY_STATE
Definition pages.c:1469
@ CL_COL_WEIGHT
Definition pages.c:1477
@ CL_COL_PLAYER_NAME
Definition pages.c:1470
@ CL_COL_NATION
Definition pages.c:1473
@ CL_COL_CONN_ID
Definition pages.c:1475
@ CL_COL_PLAYER_NUMBER
Definition pages.c:1467
@ CL_NUM_COLUMNS
Definition pages.c:1480
@ CL_COL_TEAM
Definition pages.c:1474
@ CL_COL_COLOR
Definition pages.c:1472
@ CL_COL_COLLAPSED
Definition pages.c:1478
@ CL_COL_FLAG
Definition pages.c:1471
static void update_load_page(void)
Definition pages.c:2889
static void save_dialog_update(struct save_dialog *pdialog)
Definition pages.c:427
static GtkWidget * scenario_authors
Definition pages.c:64
static GtkWidget * start_aifill_spin
Definition pages.c:1461
static void ruleset_entry_changed(GtkWidget *w, gpointer data)
Definition pages.c:1625
void(* save_dialog_action_fn_t)(const char *filename)
Definition pages.c:372
static void open_settings(void)
Definition pages.c:141
static void set_connection_state(enum connection_state state)
Definition pages.c:929
static GtkWidget * ruleset_combo
Definition pages.c:93
static void observe_button_callback(GtkWidget *w, gpointer data)
Definition pages.c:2177
static void conn_menu_ready_chosen(GObject *object, gpointer data)
Definition pages.c:1697
static void server_scan_error(struct server_scan *scan, const char *message)
Definition pages.c:824
static void load_callback(void)
Definition pages.c:2862
static void save_dialog_file_chooser_callback(GtkWidget *widget, gint response, gpointer data)
Definition pages.c:444
static void network_list_callback(GtkTreeSelection *select, gpointer data)
Definition pages.c:1138
static GtkListStore * server_playerlist_store
Definition pages.c:70
static void nation_button_callback(GtkWidget *w, gpointer data)
Definition pages.c:2157
void real_set_client_page(enum client_pages new_page)
Definition pages.c:3353
static void conn_menu_connection_command(GObject *object, gpointer data)
Definition pages.c:1750
static void scenario_list_callback(void)
Definition pages.c:2986
static gboolean intro_expose(GtkWidget *w, cairo_t *cr, gpointer *data)
Definition pages.c:164
static void load_browse_callback(GtkWidget *w, gpointer data)
Definition pages.c:2879
static void network_activate_callback(GtkTreeView *view, GtkTreePath *arg1, GtkTreeViewColumn *arg2, gpointer data)
Definition pages.c:1097
static struct fileinfo_list * save_dialog_scenario_list(void)
Definition pages.c:3514
static void connection_state_reset(void)
Definition pages.c:997
void real_conn_list_dialog_update(void *unused)
Definition pages.c:2342
static gboolean connection_list_event(GtkWidget *widget, GdkEventButton *event, gpointer data)
Definition pages.c:1986
static bool holding_srv_list_mutex
Definition pages.c:95
static GtkTreeStore * connection_list_store
Definition pages.c:1459
static struct fileinfo_list * save_dialog_savegame_list(void)
Definition pages.c:3479
static void start_new_game_callback(GtkWidget *w, gpointer data)
Definition pages.c:102
void destroy_server_scans(void)
Definition pages.c:767
void save_scenario_dialog_popup(void)
Definition pages.c:3522
static GtkTreeSelection * lan_selection
Definition pages.c:74
static void save_dialog_save_scenario(const char *filename)
Definition pages.c:3506
struct fileinfo_list *(* save_dialog_files_fn_t)(void)
Definition pages.c:373
static gboolean check_server_scan(gpointer data)
Definition pages.c:790
static void save_dialog_store_update(GtkListStore *store, const struct fileinfo_list *files)
Definition pages.c:409
static void ai_fill_changed_by_user(GtkWidget *w, gpointer data)
Definition pages.c:1640
static gboolean delayed_unselect_path(gpointer data)
Definition pages.c:1970
static void conn_menu_player_take(GObject *object, gpointer data)
Definition pages.c:1737
static void conn_menu_info_chosen(GObject *object, gpointer data)
Definition pages.c:1795
static bool model_get_player_iter(GtkTreeModel *model, GtkTreeIter *iter, GtkTreeIter *start, const struct player *pplayer)
Definition pages.c:2282
static GtkTreeSelection * load_selection
Definition pages.c:73
static void ai_skill_callback(GtkWidget *w, gpointer data)
Definition pages.c:1587
static enum client_pages current_page
Definition pages.c:78
static GtkWidget * statusbar_frame
Definition pages.c:89
static GtkWidget * network_confirm_password
Definition pages.c:698
static void client_take_player(struct player *pplayer)
Definition pages.c:1521
static void clear_network_statusbar(void)
Definition pages.c:879
static GtkTreeSelection * meta_selection
Definition pages.c:74
static void save_dialog_row_callback(GtkTreeView *tree_view, GtkTreePath *path, GtkTreeViewColumn *column, gpointer data)
Definition pages.c:537
static void connection_list_row_callback(GtkTreeView *tree_view, GtkTreeIter *iter, GtkTreePath *path, gpointer data)
Definition pages.c:2041
save_dialog_response
Definition pages.c:390
@ SD_RES_DELETE
Definition pages.c:392
@ SD_RES_SAVE
Definition pages.c:393
@ SD_RES_BROWSE
Definition pages.c:391
GtkWidget * create_load_page(void)
Definition pages.c:2902
static GtkWidget * ai_lvl_combobox
Definition pages.c:1462
static GtkWidget * network_password_label
Definition pages.c:697
static GtkWidget * save_dialog_new(const char *title, const char *savelabel, const char *savefilelabel, save_dialog_action_fn_t action, save_dialog_files_fn_t files)
Definition pages.c:577
static void intro_free(GtkWidget *w, gpointer *data)
Definition pages.c:221
static GtkWidget * network_password
Definition pages.c:697
static GtkWidget * network_host
Definition pages.c:695
void handle_authentication_req(enum authentication_type type, const char *message)
Definition pages.c:1006
static GtkTreeStore * connection_list_store_new(void)
Definition pages.c:1486
static bool send_new_aifill_to_server
Definition pages.c:1639
static void save_dialog_file_chooser_popup(const char *title, GtkFileChooserAction action, save_dialog_action_fn_t cb)
Definition pages.c:464
static GtkWidget * network_port
Definition pages.c:696
static void add_tree_col(GtkWidget *treeview, GType gtype, const char *title, int colnum, const char *key)
Definition pages.c:2587
static void save_dialog_entry_callback(GtkEntry *entry, gpointer data)
Definition pages.c:548
static void main_callback(GtkWidget *w, gpointer data)
Definition pages.c:149
void update_start_page(void)
Definition pages.c:1672
static void scenario_callback(void)
Definition pages.c:3042
static GtkWidget * start_options_table
Definition pages.c:1457
static bool no_ruleset_callback
Definition pages.c:1608
void ai_fill_changed_by_server(int aifill)
Definition pages.c:1651
static void update_server_playerlist(const struct server *pserver)
Definition pages.c:1109
static GtkWidget * observe_button
Definition pages.c:1458
static GtkWidget * scenario_version
Definition pages.c:66
GtkWidget * create_statusbar(void)
Definition pages.c:908
static void game_options_callback(GtkWidget *w, gpointer data)
Definition pages.c:1579
static GtkListStore * scenario_store
Definition pages.c:68
static GtkWidget * network_host_label
Definition pages.c:695
static bool model_get_conn_iter(GtkTreeModel *model, GtkTreeIter *iter, GtkTreeIter *parent, GtkTreeIter *start, const struct connection *pconn)
Definition pages.c:2313
static GQueue * statusbar_queue
Definition pages.c:90
GtkWidget * start_message_area
Definition pages.c:1455
static GtkWidget * network_port_label
Definition pages.c:696
static void update_scenario_page(void)
Definition pages.c:3068
static GtkWidget * scenario_filename
Definition pages.c:65
static void conn_menu_team_chosen(GObject *object, gpointer data)
Definition pages.c:1680
static GtkWidget * create_conn_menu(struct player *pplayer, struct connection *pconn)
Definition pages.c:1809
static void load_saved_game_callback(GtkWidget *w, gpointer data)
Definition pages.c:124
static guint statusbar_timer
Definition pages.c:91
static bool object_extract(GObject *object, struct player **ppplayer, struct connection **ppconn)
Definition pages.c:1552
static void object_put(GObject *object, struct player *pplayer, struct connection *pconn)
Definition pages.c:1533
static struct fileinfo_list * save_dialog_mapimg_list(void)
Definition pages.c:3543
GtkWidget * create_main_page(void)
Definition pages.c:231
static void conn_list_select_conn(struct connection *pconn)
Definition pages.c:2092
static GtkWidget * ready_button
Definition pages.c:1458
GdkPixbuf * create_player_icon(const struct player *plr)
Definition plrdlg.c:117
GdkPixbuf * get_flag(const struct nation_type *nation)
Definition plrdlg.c:609
const char * title
Definition repodlgs.c:1313
GType type
Definition repodlgs.c:1312
void get_sprite_dimensions(struct sprite *sprite, int *width, int *height)
Definition sprite.c:107
void free_sprite(struct sprite *s)
Definition sprite.c:278
struct sprite * sprite_scale(struct sprite *src, int new_w, int new_h)
Definition sprite.c:291
struct sprite * load_gfxfile(const char *filename)
Definition sprite.c:170
void voteinfo_gui_update(void)
GtkWidget * voteinfo_bar_new(bool split_bar)
GtkWidget * pregame_votebar
GtkWidget * icon_label_button_new(const gchar *icon_name, const gchar *label_text)
Definition gui_stuff.c:76
void(* save_dialog_action_fn_t)(const char *filename)
Definition pages.c:381
struct fileinfo_list *(* save_dialog_files_fn_t)(void)
Definition pages.c:382
static struct server_list * server_list
Definition connectdlg.c:61
static enum client_pages old_page
Definition pages.c:46
void conn_list_dialog_update(void)
const char * name
Definition inputfile.c:127
#define fc_assert_ret(condition)
Definition log.h:191
#define fc_assert_ret_val(condition, val)
Definition log.h:194
#define log_debug(message,...)
Definition log.h:115
#define log_error(message,...)
Definition log.h:103
const char * mapimg_error(void)
Definition mapimg.c:758
#define fc_strdup(str)
Definition mem.h:43
#define fc_malloc(sz)
Definition mem.h:34
const char * nation_adjective_for_player(const struct player *pplayer)
Definition nation.c:168
bool can_conn_edit_players_nation(const struct connection *pconn, const struct player *pplayer)
Definition nation.c:1186
#define NO_NATION_SELECTED
Definition nation.h:29
const struct option_set * server_optset
Definition options.c:4009
const struct option_set * client_optset
Definition options.c:1255
int option_int_get(const struct option *poption)
Definition options.c:809
struct option * optset_option_by_name(const struct option_set *poptset, const char *name)
Definition options.c:406
bool option_int_set(struct option *poption, int val)
Definition options.c:853
authentication_type
Definition packets.h:88
@ AUTH_NEWUSER_RETRY
Definition packets.h:92
@ AUTH_NEWUSER_FIRST
Definition packets.h:90
@ AUTH_LOGIN_RETRY
Definition packets.h:91
@ AUTH_LOGIN_FIRST
Definition packets.h:89
int send_packet_authentication_reply(struct connection *pc, const struct packet_authentication_reply *packet)
int dsend_packet_save_scenario(struct connection *pc, const char *name)
int dsend_packet_player_ready(struct connection *pc, int player_no, bool is_ready)
void client_start_server_and_set_page(enum client_pages page)
void set_client_page(enum client_pages page)
enum client_pages get_client_page(void)
bool is_settable_ai_level(enum ai_level level)
Definition player.c:1883
struct player * player_by_number(const int player_id)
Definition player.c:840
int player_slot_count(void)
Definition player.c:411
int player_number(const struct player *pplayer)
Definition player.c:828
const char * player_name(const struct player *pplayer)
Definition player.c:886
bool player_has_flag(const struct player *pplayer, enum plr_flag_id flag)
Definition player.c:1954
#define ai_level_cmd(_level_)
Definition player.h:565
#define players_iterate_end
Definition player.h:535
#define players_iterate(_pplayer)
Definition player.h:530
#define is_ai(plr)
Definition player.h:234
#define players_iterate_alive_end
Definition player.h:545
#define is_human(plr)
Definition player.h:233
#define players_iterate_alive(_pplayer)
Definition player.h:540
void secfile_destroy(struct section_file *secfile)
struct section_file * secfile_load_section(const char *filename, const char *section, bool allow_duplicates)
bool secfile_lookup_bool_default(const struct section_file *secfile, bool def, const char *path,...)
int secfile_lookup_int_default(const struct section_file *secfile, int def, const char *path,...)
const char * secfile_lookup_str_default(const struct section_file *secfile, const char *def, const char *path,...)
struct srv_list * server_scan_get_list(struct server_scan *scan)
Definition servers.c:855
void server_scan_finish(struct server_scan *scan)
Definition servers.c:868
struct server_scan * server_scan_begin(enum server_scan_type type, ServerScanErrorFunc error_func)
Definition servers.c:744
enum server_scan_type server_scan_get_type(const struct server_scan *scan)
Definition servers.c:798
enum server_scan_status server_scan_poll(struct server_scan *scan)
Definition servers.c:821
#define server_list_iterate_end
Definition servers.h:57
server_scan_status
Definition servers.h:79
@ SCAN_STATUS_PARTIAL
Definition servers.h:82
@ SCAN_STATUS_ERROR
Definition servers.h:80
@ SCAN_STATUS_DONE
Definition servers.h:83
#define server_list_iterate(serverlist, pserver)
Definition servers.h:55
server_scan_type
Definition servers.h:67
@ SERVER_SCAN_LOCAL
Definition servers.h:68
@ SERVER_SCAN_GLOBAL
Definition servers.h:69
struct setting_list * level[OLEVELS_NUM]
Definition settings.c:183
const struct strvec * get_scenario_dirs(void)
Definition shared.c:971
const struct strvec * get_save_dirs(void)
Definition shared.c:934
char * skip_to_basename(char *filepath)
Definition shared.c:1751
struct fileinfo_list * fileinfolist_infix(const struct strvec *dirs, const char *infix, bool nodups)
Definition shared.c:1204
#define fileinfo_list_iterate(list, pnode)
Definition shared.h:176
#define FC_PTR_TO_INT(p)
Definition shared.h:95
#define fileinfo_list_iterate_end
Definition shared.h:178
#define FC_INT_TO_PTR(i)
Definition shared.h:94
size_t size
Definition specvec.h:72
const char * aifill(int amount)
Definition srv_main.c:2395
struct conn_list * est_connections
Definition game.h:97
struct packet_game_info info
Definition game.h:89
struct connection conn
Definition client_main.h:96
Definition colors.h:20
struct player * playing
Definition connection.h:156
enum cmdlevel access_level
Definition connection.h:182
char username[MAX_LEN_NAME]
Definition connection.h:169
char addr[MAX_LEN_ADDR]
Definition connection.h:170
Definition climisc.h:82
char password[MAX_LEN_PASSWORD]
Definition packets_gen.h:68
struct team * team
Definition player.h:261
bool is_ready
Definition player.h:262
save_dialog_action_fn_t action
Definition pages.c:379
GtkEntry * entry
Definition pages.c:378
GtkTreeView * tree_view
Definition pages.c:377
GtkDialog * shell
Definition pages.c:376
save_dialog_files_fn_t files
Definition pages.c:380
char * host
Definition servers.h:43
char * name
Definition servers.h:41
char * nation
Definition servers.h:44
char * type
Definition servers.h:42
struct server_scan * scan
Definition pages.c:82
struct server::players * players
int nplayers
Definition servers.h:47
cairo_surface_t * surface
Definition sprite.h:23
fc_mutex mutex
Definition servers.h:64
struct server_list * servers
Definition servers.h:63
Definition team.c:40
int fc_snprintf(char *str, size_t n, const char *format,...)
Definition support.c:969
int cat_snprintf(char *str, size_t n, const char *format,...)
Definition support.c:995
int fc_remove(const char *filename)
Definition support.c:556
#define sz_strlcpy(dest, src)
Definition support.h:167
#define TRUE
Definition support.h:46
#define FALSE
Definition support.h:47
#define sz_strlcat(dest, src)
Definition support.h:168
const char * team_name_translation(const struct team *pteam)
Definition team.c:420
const char * team_slot_name_translation(const struct team_slot *tslot)
Definition team.c:253
int team_number(const struct team *pteam)
Definition team.c:391
int team_slot_index(const struct team_slot *tslot)
Definition team.c:138
const struct player_list * team_members(const struct team *pteam)
Definition team.c:456
bool team_slot_is_used(const struct team_slot *tslot)
Definition team.c:162
const char * team_slot_rule_name(const struct team_slot *tslot)
Definition team.c:233
#define team_slots_iterate_end
Definition team.h:75
#define team_slots_iterate(_tslot)
Definition team.h:70
const char * tileset_main_intro_filename(const struct tileset *t)
Definition tilespec.c:910
void update_queue_connect_processing_finished(int request_id, uq_callback_t callback, void *data)
const char * fc_git_revision(void)
Definition version.c:75
const char * unstable_message(void)
Definition version.c:160
const char * word_version(void)
Definition version.c:62