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 "chat.h"
34#include "dataio.h"
35#include "game.h"
36#include "mapimg.h"
37#include "version.h"
38
39/* client */
40#include "client_main.h"
41#include "climisc.h"
42#include "clinet.h"
43#include "connectdlg_common.h"
44#include "packhand.h"
45#include "servers.h"
46#include "update_queue.h"
47
48/* client/gui-gtk-4.0 */
49#include "chatline.h"
50#include "connectdlg.h"
51#include "dialogs.h"
52#include "graphics.h"
53#include "gui_main.h"
54#include "gui_stuff.h"
55#include "mapview.h"
56#include "menu.h"
57#include "optiondlg.h"
58#include "plrdlg.h" /* get_flag() */
59#include "repodlgs.h"
60#include "voteinfo_bar.h"
61
62#include "pages.h"
63
64
65static GtkWidget *scenario_description;
66static GtkWidget *scenario_authors;
67static GtkWidget *scenario_filename;
68static GtkWidget *scenario_version;
69
70static GtkListStore *load_store, *scenario_store, *meta_store, *lan_store;
71
72static GtkListStore *server_playerlist_store;
73static GtkWidget *server_playerlist_view;
74
75static GtkTreeSelection *load_selection, *scenario_selection;
76static GtkTreeSelection *meta_selection, *lan_selection;
77
78/* This is the current page. Invalid value at start, to be sure that it won't
79 * be caught through a switch() statement. */
80static enum client_pages current_page = -1;
81
83{
84 struct server_scan *scan;
85 guint timer;
86};
87
88static struct server_scan_timer_data meta_scan = { NULL, 0 };
89static struct server_scan_timer_data lan_scan = { NULL, 0 };
90
91static GtkWidget *statusbar, *statusbar_frame;
92static GQueue *statusbar_queue;
93static guint statusbar_timer = 0;
94
95static GtkWidget *ruleset_combo;
96
97static GtkWidget *conn_popover = NULL;
98
100
101static void connection_state_reset(void);
102
103/**********************************************************************/
106static void start_new_game_callback(GtkWidget *w, gpointer data)
107{
108 if (!is_server_running()) {
110
111 /* Saved settings are sent in client/options.c
112 * resend_desired_settable_options() */
113 }
114}
115
116/**********************************************************************/
119static void start_scenario_callback(GtkWidget *w, gpointer data)
120{
121 output_window_append(ftc_client, _("Compiling scenario list."));
123}
124
125/**********************************************************************/
128static void load_saved_game_callback(GtkWidget *w, gpointer data)
129{
131}
132
133/**********************************************************************/
136static void connect_network_game_callback(GtkWidget *w, gpointer data)
137{
139 set_client_page(PAGE_NETWORK);
140}
141
142/**********************************************************************/
145static void open_settings(void)
146{
147 option_dialog_popup(_("Set local options"), client_optset);
148}
149
150/**********************************************************************/
153static void main_callback(GtkWidget *w, gpointer data)
154{
155 enum client_pages page = PAGE_MAIN;
156
157 if (client.conn.used) {
159 }
160 if (page != get_client_page()) {
161 set_client_page(page);
162 }
163}
164
165/**********************************************************************/
168static void intro_expose(GtkDrawingArea *w, cairo_t *cr,
169 int width, int height, gpointer data)
170{
171 static PangoLayout *layout;
172 PangoFontDescription* desc;
173 static int pwidth, pheight;
174 static bool left = FALSE;
175 GtkAllocation allocation;
176 struct sprite *intro = (struct sprite *)data;
177
178 cairo_set_source_surface(cr, intro->surface, 0, 0);
179 cairo_paint(cr);
180
181 if (!layout) {
182 char msgbuf[128];
183 const char *rev_ver;
184
185 layout = pango_layout_new(gtk_widget_create_pango_context(GTK_WIDGET(w)));
186 desc = pango_font_description_from_string("Sans Bold 10");
187 pango_layout_set_font_description(layout, desc);
188 pango_font_description_free(desc);
189
190 rev_ver = fc_git_revision();
191
192 if (rev_ver == NULL) {
193 /* TRANS: "version 2.6.0, gui-gtk-3.22 client" */
194 fc_snprintf(msgbuf, sizeof(msgbuf), _("%s%s, %s client"),
195 word_version(), VERSION_STRING, client_string);
196 } else {
197 /* TRANS: "version 2.6.0
198 * commit: [modified] <git commit id>
199 * gui-gtk-3.22 client" */
200 fc_snprintf(msgbuf, sizeof(msgbuf), _("%s%s\ncommit: %s\n%s client"),
201 word_version(), VERSION_STRING, rev_ver, client_string);
202 left = TRUE;
203 }
204 pango_layout_set_text(layout, msgbuf, -1);
205
206 pango_layout_get_pixel_size(layout, &pwidth, &pheight);
207 }
208 gtk_widget_get_allocation(GTK_WIDGET(w), &allocation);
209
210 cairo_set_source_rgb(cr, 0, 0, 0);
211 cairo_move_to(cr, left ? 4 : allocation.width - pwidth - 3,
212 allocation.height - pheight - 3);
213 pango_cairo_show_layout(cr, layout);
214
215 cairo_set_source_rgb(cr, 1, 1, 1);
216 cairo_move_to(cr, left ? 3 : allocation.width - pwidth - 4,
217 allocation.height - pheight - 4);
218 pango_cairo_show_layout(cr, layout);
219}
220
221/**********************************************************************/
224static void intro_free(GtkWidget *w, gpointer *data)
225{
226 struct sprite *intro = (struct sprite *)data;
227
228 free_sprite(intro);
229}
230
231/**********************************************************************/
234GtkWidget *create_main_page(void)
235{
236 GtkWidget *widget, *vgrid, *frame, *darea, *button, *table;
237 GtkSizeGroup *size;
238 struct sprite *intro_in, *intro;
239 int width, height;
240 int sh;
241 int space_needed;
242 int grid_row = 0;
243
244 size = gtk_size_group_new(GTK_SIZE_GROUP_BOTH);
245
246 vgrid = gtk_grid_new();
247 gtk_orientable_set_orientation(GTK_ORIENTABLE(vgrid),
248 GTK_ORIENTATION_VERTICAL);
249 widget = vgrid;
250
251 frame = gtk_frame_new(NULL);
252 gtk_widget_set_margin_bottom(frame, 18);
253 gtk_widget_set_margin_end(frame, 18);
254 gtk_widget_set_margin_start(frame, 18);
255 gtk_widget_set_margin_top(frame, 18);
256 gtk_widget_set_halign(frame, GTK_ALIGN_CENTER);
257 gtk_grid_attach(GTK_GRID(vgrid), frame, 0, grid_row++, 1, 1);
258
260 get_sprite_dimensions(intro_in, &width, &height);
261 sh = screen_height();
262
263 if (sh <= 0) {
264 /* Assume some minimum height */
265 sh = 600;
266 }
267
268 space_needed = 250;
269#if IS_BETA_VERSION || IS_DEVEL_VERSION
270 /* Alpha or Beta notice takes extra space */
271 space_needed += 50;
272#endif
273
274 if (sh - height < space_needed) {
275 float scale;
276
277 if (sh < (space_needed + 0.2 * height)) {
278 /* Screen is simply too small, use minimum scale */
279 scale = 0.2;
280 } else {
281 scale = (double)(sh - space_needed) / height;
282 }
283 height *= scale;
284 width *= scale;
285 intro = sprite_scale(intro_in, width, height);
286 free_sprite(intro_in);
287 } else {
288 intro = intro_in;
289 }
290 darea = gtk_drawing_area_new();
291 gtk_widget_set_size_request(darea, width, height);
292 gtk_drawing_area_set_draw_func(GTK_DRAWING_AREA(darea),
293 intro_expose, intro, NULL);
294 g_signal_connect(widget, "destroy",
295 G_CALLBACK(intro_free), intro);
296 gtk_frame_set_child(GTK_FRAME(frame), darea);
297
298#if IS_BETA_VERSION || IS_DEVEL_VERSION
299 {
300 GtkWidget *label;
301
302 label = gtk_label_new(unstable_message());
303 gtk_widget_set_name(label, "beta_label");
304 gtk_widget_set_halign(label, GTK_ALIGN_CENTER);
305 gtk_widget_set_valign(label, GTK_ALIGN_CENTER);
306 gtk_label_set_justify(GTK_LABEL(label), GTK_JUSTIFY_CENTER);
307 gtk_grid_attach(GTK_GRID(vgrid), label, 0, grid_row++, 1, 1);
308 }
309#endif /* IS_BETA_VERSION || IS_DEVEL_VERSION */
310
311 table = gtk_grid_new();
312 gtk_widget_set_margin_bottom(table, 12);
313 gtk_widget_set_margin_end(table, 12);
314 gtk_widget_set_margin_start(table, 12);
315 gtk_widget_set_margin_top(table, 12);
316 gtk_widget_set_hexpand(table, TRUE);
317 gtk_widget_set_vexpand(table, TRUE);
318 gtk_widget_set_halign(table, GTK_ALIGN_CENTER);
319 gtk_widget_set_valign(table, GTK_ALIGN_CENTER);
320
321 gtk_grid_set_row_spacing(GTK_GRID(table), 8);
322 gtk_grid_set_column_spacing(GTK_GRID(table), 18);
323 gtk_grid_attach(GTK_GRID(vgrid), table, 0, grid_row++, 1, 1);
324
325 button = gtk_button_new_with_mnemonic(_("Start _New Game"));
326 gtk_size_group_add_widget(size, button);
327 gtk_grid_attach(GTK_GRID(table), button, 0, 0, 1, 1);
328 gtk_widget_set_tooltip_text(button,
329 _("Launches local server, and connects to it for a single-player game."));
330 g_signal_connect(button, "clicked",
331 G_CALLBACK(start_new_game_callback), NULL);
332
333 button = gtk_button_new_with_mnemonic(_("Start _Scenario Game"));
334 gtk_size_group_add_widget(size, button);
335 gtk_grid_attach(GTK_GRID(table), button, 0, 1, 1, 1);
336 gtk_widget_set_tooltip_text(button,
337 _("Loads one of the scenarios for a single-player game. "
338 "Tutorial is one of the scenarios."));
339 g_signal_connect(button, "clicked",
340 G_CALLBACK(start_scenario_callback), NULL);
341
342 button = gtk_button_new_with_mnemonic(_("_Load Saved Game"));
343 gtk_size_group_add_widget(size, button);
344 gtk_grid_attach(GTK_GRID(table), button, 0, 2, 1, 1);
345 gtk_widget_set_tooltip_text(button,
346 _("Continues previously saved single-player game."));
347 g_signal_connect(button, "clicked",
348 G_CALLBACK(load_saved_game_callback), NULL);
349
350 button = gtk_button_new_with_mnemonic(_("C_onnect to Network Game"));
351 gtk_size_group_add_widget(size, button);
352 gtk_grid_attach(GTK_GRID(table), button, 1, 0, 1, 1);
353 gtk_widget_set_tooltip_text(button,
354 _("Connects to outside server. "
355 "Sometimes you want to launch a separate server even for local games."));
356 g_signal_connect(button, "clicked",
357 G_CALLBACK(connect_network_game_callback), NULL);
358
359 button = gtk_button_new_with_mnemonic(_("Client Settings"));
360 gtk_size_group_add_widget(size, button);
361 gtk_grid_attach(GTK_GRID(table), button, 1, 1, 1, 1);
362 gtk_widget_set_tooltip_text(button,
363 _("Adjusting client-side options."));
364 g_signal_connect(button, "clicked", open_settings, NULL);
365
366 button = icon_label_button_new("application-exit", _("E_xit"));
367 gtk_size_group_add_widget(size, button);
368 g_object_unref(size);
369 gtk_grid_attach(GTK_GRID(table), button, 1, 2, 1, 1);
370 gtk_widget_set_tooltip_text(button,
371 _("Gives you a break from playing freeciv."));
372 g_signal_connect(button, "clicked",
373 G_CALLBACK(quit_gtk_main), NULL);
374
375 return widget;
376}
377
378/****************************************************************************
379 GENERIC SAVE DIALOG
380****************************************************************************/
381typedef void (*save_dialog_action_fn_t) (const char *filename);
382typedef struct fileinfo_list * (*save_dialog_files_fn_t) (void);
383
384struct save_dialog {
385 GtkDialog *shell;
386 GtkTreeView *tree_view;
387 GtkEntry *entry;
390};
391
398
404
405/**********************************************************************/
408static inline GtkListStore *save_dialog_store_new(void)
409{
410 return gtk_list_store_new(SD_COL_NUM,
411 G_TYPE_STRING, /* SD_COL_PRETTY_NAME */
412 G_TYPE_STRING); /* SD_COL_FULL_PATH */
413}
414
415/**********************************************************************/
418static void save_dialog_store_update(GtkListStore *store,
419 const struct fileinfo_list *files)
420{
421 GtkTreeIter iter;
422
423 gtk_list_store_clear(store);
424 fileinfo_list_iterate(files, pfile) {
425 gtk_list_store_append(store, &iter);
426 gtk_list_store_set(store, &iter,
427 SD_COL_PRETTY_NAME, pfile->name,
428 SD_COL_FULL_PATH, pfile->fullname,
429 -1);
431}
432
433/**********************************************************************/
436static void save_dialog_update(struct save_dialog *pdialog)
437{
438 struct fileinfo_list *files;
439
440 fc_assert_ret(NULL != pdialog);
441
442 /* Update the store. */
443 files = pdialog->files();
444 save_dialog_store_update(GTK_LIST_STORE
445 (gtk_tree_view_get_model(pdialog->tree_view)),
446 files);
447 fileinfo_list_destroy(files);
448}
449
450/**********************************************************************/
454 gint response, gpointer data)
455{
456 if (response == GTK_RESPONSE_OK) {
458 GFile *file = gtk_file_chooser_get_file(GTK_FILE_CHOOSER(widget));
459
460 if (file != NULL) {
461 gchar *filename = g_file_get_parse_name(file);
462
463 if (NULL != filename) {
464 action(filename);
465 g_free(filename);
466 }
467
468 g_object_unref(file);
469 }
470 }
471
472 gtk_window_destroy(GTK_WINDOW(widget));
473}
474
475/**********************************************************************/
478static void save_dialog_file_chooser_popup(const char *title,
479 GtkFileChooserAction action,
481{
482 GtkWidget *filechoose;
483
484 /* Create the chooser */
485 filechoose = gtk_file_chooser_dialog_new(title, GTK_WINDOW(toplevel), action,
486 _("_Cancel"), GTK_RESPONSE_CANCEL,
487 (action == GTK_FILE_CHOOSER_ACTION_SAVE) ?
488 _("_Save") : _("_Open"),
489 GTK_RESPONSE_OK, NULL);
490 setup_dialog(filechoose, toplevel);
491
492 g_signal_connect(filechoose, "response",
493 G_CALLBACK(save_dialog_file_chooser_callback), cb);
494
495 /* Display that dialog */
496 gtk_window_present(GTK_WINDOW(filechoose));
497}
498
499/**********************************************************************/
502static void save_dialog_response_callback(GtkWidget *w, gint response,
503 gpointer data)
504{
505 struct save_dialog *pdialog = data;
506
507 switch (response) {
508 case SD_RES_BROWSE:
509 save_dialog_file_chooser_popup(_("Select Location to Save"),
510 GTK_FILE_CHOOSER_ACTION_SAVE,
511 pdialog->action);
512 break;
513 case SD_RES_DELETE:
514 {
515 GtkTreeSelection *selection;
516 GtkTreeModel *model;
517 GtkTreeIter iter;
518 const gchar *full_path;
519
520 selection = gtk_tree_view_get_selection(pdialog->tree_view);
521 if (!gtk_tree_selection_get_selected(selection, &model, &iter)) {
522 return;
523 }
524
525 gtk_tree_model_get(model, &iter, SD_COL_FULL_PATH, &full_path, -1);
526 fc_remove(full_path);
527 save_dialog_update(pdialog);
528 }
529 return;
530 case SD_RES_SAVE:
531 {
532 const char *text = gtk_entry_buffer_get_text(gtk_entry_get_buffer(pdialog->entry));
533 gchar *filename = g_filename_from_utf8(text, -1, NULL, NULL, NULL);
534
535 if (NULL == filename) {
536 return;
537 }
538 pdialog->action(filename);
539 g_free(filename);
540 }
541 break;
542 default:
543 break;
544 }
545
546 gtk_window_destroy(GTK_WINDOW(pdialog->shell));
547}
548
549/**********************************************************************/
552static void save_dialog_row_callback(GtkTreeView *tree_view,
553 GtkTreePath *path,
554 GtkTreeViewColumn *column,
555 gpointer data)
556{
558}
559
560/**********************************************************************/
563static void save_dialog_entry_callback(GtkEntry *entry, gpointer data)
564{
566}
567
568/**********************************************************************/
571static void save_dialog_list_callback(GtkTreeSelection *selection,
572 gpointer data)
573{
574 struct save_dialog *pdialog = data;
575 GtkTreeModel *model;
576 GtkTreeIter iter;
577 const gchar *filename;
578
579 if (!gtk_tree_selection_get_selected(selection, &model, &iter)) {
580 gtk_dialog_set_response_sensitive(pdialog->shell, SD_RES_DELETE, FALSE);
581 return;
582 }
583
584 gtk_dialog_set_response_sensitive(pdialog->shell, SD_RES_DELETE, TRUE);
585 gtk_tree_model_get(model, &iter, SD_COL_PRETTY_NAME, &filename, -1);
586 gtk_entry_buffer_set_text(gtk_entry_get_buffer(pdialog->entry), filename, -1);
587}
588
589/**********************************************************************/
592static GtkWidget *save_dialog_new(const char *title, const char *savelabel,
593 const char *savefilelabel,
596{
597 GtkWidget *shell, *sbox, *sw, *label, *view, *entry;
598 GtkBox *vbox;
599 GtkListStore *store;
600 GtkCellRenderer *rend;
601 GtkTreeSelection *selection;
602 struct save_dialog *pdialog;
603 int grids_row = 0;
604
605 fc_assert_ret_val(NULL != action, NULL);
606 fc_assert_ret_val(NULL != files, NULL);
607
608 /* Save dialog structure. */
609 pdialog = fc_malloc(sizeof(*pdialog));
610 pdialog->action = action;
611 pdialog->files = files;
612
613 /* Shell. */
614 shell = gtk_dialog_new_with_buttons(title, NULL, 0,
615 _("_Browse..."), SD_RES_BROWSE,
616 _("_Delete"), SD_RES_DELETE,
617 _("_Cancel"), GTK_RESPONSE_CANCEL,
618 _("_Save"), SD_RES_SAVE,
619 NULL);
620 g_object_set_data_full(G_OBJECT(shell), "save_dialog", pdialog,
621 (GDestroyNotify) free);
622 gtk_dialog_set_default_response(GTK_DIALOG(shell), GTK_RESPONSE_CANCEL);
623 gtk_dialog_set_response_sensitive(GTK_DIALOG(shell), SD_RES_DELETE, FALSE);
625 g_signal_connect(shell, "response",
626 G_CALLBACK(save_dialog_response_callback), pdialog);
627 pdialog->shell = GTK_DIALOG(shell);
628 vbox = GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(shell)));
629
630 /* Tree view. */
631 store = save_dialog_store_new();
632 view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(store));
633 gtk_widget_set_hexpand(view, TRUE);
634 gtk_widget_set_vexpand(view, TRUE);
635 g_object_unref(store);
636 gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(view), FALSE);
637 g_signal_connect(view, "row-activated",
638 G_CALLBACK(save_dialog_row_callback), pdialog);
639 pdialog->tree_view = GTK_TREE_VIEW(view);
640
641 sbox = gtk_grid_new();
642 gtk_orientable_set_orientation(GTK_ORIENTABLE(sbox),
643 GTK_ORIENTATION_VERTICAL);
644 gtk_grid_set_row_spacing(GTK_GRID(sbox), 2);
645 gtk_box_append(vbox, sbox);
646
647 label = g_object_new(GTK_TYPE_LABEL,
648 "use-underline", TRUE,
649 "mnemonic-widget", view,
650 "label", savelabel,
651 "xalign", 0.0,
652 "yalign", 0.5,
653 NULL);
654 gtk_grid_attach(GTK_GRID(sbox), label, 0, grids_row++, 1, 1);
655
656 sw = gtk_scrolled_window_new();
657 gtk_scrolled_window_set_min_content_width(GTK_SCROLLED_WINDOW(sw), 300);
658 gtk_scrolled_window_set_min_content_height(GTK_SCROLLED_WINDOW(sw), 300);
659 gtk_scrolled_window_set_has_frame(GTK_SCROLLED_WINDOW(sw), TRUE);
660 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(sw),
661 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
662 gtk_scrolled_window_set_child(GTK_SCROLLED_WINDOW(sw), view);
663 gtk_grid_attach(GTK_GRID(sbox), sw, 0, grids_row++, 1, 1);
664
665 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(view));
666 gtk_tree_selection_set_mode(selection, GTK_SELECTION_SINGLE);
667 g_signal_connect(selection, "changed",
668 G_CALLBACK(save_dialog_list_callback), pdialog);
669
670 rend = gtk_cell_renderer_text_new();
671 gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(view),
672 -1, NULL, rend, "text",
673 SD_COL_PRETTY_NAME, NULL);
674
675 /* Entry. */
676 entry = gtk_entry_new();
677 gtk_widget_set_hexpand(entry, TRUE);
678 g_signal_connect(entry, "activate",
679 G_CALLBACK(save_dialog_entry_callback), pdialog);
680 pdialog->entry = GTK_ENTRY(entry);
681
682 sbox = gtk_grid_new();
683 grids_row = 0;
684 gtk_widget_set_margin_bottom(sbox, 12);
685 gtk_widget_set_margin_end(sbox, 12);
686 gtk_widget_set_margin_start(sbox, 12);
687 gtk_widget_set_margin_top(sbox, 12);
688 gtk_orientable_set_orientation(GTK_ORIENTABLE(sbox),
689 GTK_ORIENTATION_VERTICAL);
690 gtk_grid_set_row_spacing(GTK_GRID(sbox), 2);
691
692 label = g_object_new(GTK_TYPE_LABEL,
693 "use-underline", TRUE,
694 "mnemonic-widget", entry,
695 "label", savefilelabel,
696 "xalign", 0.0,
697 "yalign", 0.5,
698 NULL);
699 gtk_grid_attach(GTK_GRID(sbox), label, 0, grids_row++, 1, 1);
700
701 gtk_grid_attach(GTK_GRID(sbox), entry, 0, grids_row++, 1, 1);
702 gtk_box_append(vbox, sbox);
703
704 save_dialog_update(pdialog);
705 gtk_window_set_focus(GTK_WINDOW(shell), entry);
706 gtk_widget_show(GTK_WIDGET(vbox));
707
708 return shell;
709}
710
711/****************************************************************************
712 NETWORK PAGE
713****************************************************************************/
719
720/**********************************************************************/
723static void update_server_list(enum server_scan_type sstype,
724 const struct server_list *list)
725{
726 GtkTreeSelection *sel = NULL;
727 GtkTreeView *view;
728 GtkTreeIter it;
729 GtkListStore *store;
730 const gchar *host, *portstr;
731 int port;
732
733 switch (sstype) {
735 sel = lan_selection;
736 break;
738 sel = meta_selection;
739 break;
740 default:
741 break;
742 }
743
744 if (!sel) {
745 return;
746 }
747
748 view = gtk_tree_selection_get_tree_view(sel);
749 store = GTK_LIST_STORE(gtk_tree_view_get_model(view));
750 gtk_list_store_clear(store);
751
752 if (!list) {
753 return;
754 }
755
756 host = gtk_entry_buffer_get_text(gtk_entry_get_buffer(GTK_ENTRY(network_host)));
757 portstr = gtk_entry_buffer_get_text(gtk_entry_get_buffer(GTK_ENTRY(network_port)));
758 port = atoi(portstr);
759
760 server_list_iterate(list, pserver) {
761 char buf[35];
762
763 if (pserver->humans >= 0) {
764 fc_snprintf(buf, sizeof(buf), "%d", pserver->humans);
765 } else {
766 sz_strlcpy(buf, _("Unknown"));
767 }
768 gtk_list_store_append(store, &it);
769 gtk_list_store_set(store, &it,
770 0, pserver->host,
771 1, pserver->port,
772 2, pserver->version,
773 3, _(pserver->state),
774 4, pserver->nplayers,
775 5, buf,
776 6, pserver->message,
777 -1);
778 if (strcmp(host, pserver->host) == 0 && port == pserver->port) {
779 gtk_tree_selection_select_iter(sel, &it);
780 }
782}
783
784/**********************************************************************/
788{
789 if (meta_scan.scan) {
791 meta_scan.scan = NULL;
792 }
793 if (meta_scan.timer != 0) {
794 g_source_remove(meta_scan.timer);
795 meta_scan.timer = 0;
796 }
797 if (lan_scan.scan) {
799 lan_scan.scan = NULL;
800 }
801 if (lan_scan.timer != 0) {
802 g_source_remove(lan_scan.timer);
803 lan_scan.timer = 0;
804 }
805}
806
807/**********************************************************************/
810static gboolean check_server_scan(gpointer data)
811{
812 struct server_scan_timer_data *scan_data = data;
813 struct server_scan *scan = scan_data->scan;
814 enum server_scan_status stat;
815
816 if (!scan) {
817 return FALSE;
818 }
819
820 stat = server_scan_poll(scan);
821 if (stat >= SCAN_STATUS_PARTIAL) {
823 struct srv_list *srvrs;
824
826 srvrs = server_scan_get_list(scan);
827 fc_allocate_mutex(&srvrs->mutex);
831 fc_release_mutex(&srvrs->mutex);
832 }
833
834 if (stat == SCAN_STATUS_ERROR || stat == SCAN_STATUS_DONE) {
835 scan_data->timer = 0;
836 return FALSE;
837 }
838 return TRUE;
839}
840
841/**********************************************************************/
844static void server_scan_error(struct server_scan *scan,
845 const char *message)
846{
848 log_error("%s", message);
849
850 /* Main thread will finalize the scan later (or even concurrently) -
851 * do not do anything here to cause double free or raze condition. */
852}
853
854/**********************************************************************/
867
868/**************************************************************************
869 Network connection state defines.
870**************************************************************************/
877
879
880/**********************************************************************/
883static gboolean update_network_statusbar(gpointer data)
884{
885 if (!g_queue_is_empty(statusbar_queue)) {
886 char *txt;
887
888 txt = g_queue_pop_head(statusbar_queue);
889 gtk_label_set_text(GTK_LABEL(statusbar), txt);
890 free(txt);
891 }
892
893 return TRUE;
894}
895
896/**********************************************************************/
899static void clear_network_statusbar(void)
900{
901 while (!g_queue_is_empty(statusbar_queue)) {
902 char *txt;
903
904 txt = g_queue_pop_head(statusbar_queue);
905 free(txt);
906 }
907 gtk_label_set_text(GTK_LABEL(statusbar), "");
908}
909
910/**********************************************************************/
913void append_network_statusbar(const char *text, bool force)
914{
915 if (gtk_widget_get_visible(statusbar_frame)) {
916 if (force) {
918 gtk_label_set_text(GTK_LABEL(statusbar), text);
919 } else {
920 g_queue_push_tail(statusbar_queue, fc_strdup(text));
921 }
922 }
923}
924
925/**********************************************************************/
928GtkWidget *create_statusbar(void)
929{
930 statusbar_frame = gtk_frame_new(NULL);
931
932 statusbar = gtk_label_new("");
933 gtk_widget_set_margin_start(statusbar, 2);
934 gtk_widget_set_margin_end(statusbar, 2);
935 gtk_widget_set_margin_top(statusbar, 2);
936 gtk_widget_set_margin_bottom(statusbar, 2);
937 gtk_frame_set_child(GTK_FRAME(statusbar_frame), statusbar);
938
939 statusbar_queue = g_queue_new();
940 statusbar_timer = g_timeout_add(2000, update_network_statusbar, NULL);
941
942 return statusbar_frame;
943}
944
945/**********************************************************************/
949{
950 switch (state) {
951 case LOGIN_TYPE:
953
954 gtk_entry_buffer_set_text(gtk_entry_get_buffer(GTK_ENTRY(network_password)),
955 "", -1);
956 gtk_entry_buffer_set_text(gtk_entry_get_buffer(GTK_ENTRY(network_confirm_password)),
957 "", -1);
958
959 gtk_widget_set_sensitive(network_host, TRUE);
960 gtk_widget_set_sensitive(network_port, TRUE);
961 gtk_widget_set_sensitive(network_login, TRUE);
962 gtk_widget_set_sensitive(network_password_label, FALSE);
963 gtk_label_set_markup_with_mnemonic(GTK_LABEL(network_password_label), _("Pass_word:"));
964 gtk_widget_set_sensitive(network_password, FALSE);
965 gtk_widget_set_sensitive(network_confirm_password_label, FALSE);
966 gtk_widget_set_sensitive(network_confirm_password, FALSE);
967 break;
969 set_client_page(PAGE_NETWORK);
970 gtk_entry_buffer_set_text(gtk_entry_get_buffer(GTK_ENTRY(network_password)), "", -1);
971 gtk_entry_buffer_set_text(gtk_entry_get_buffer(GTK_ENTRY(network_confirm_password)),
972 "", -1);
973
974 gtk_widget_set_sensitive(network_host, FALSE);
975 gtk_widget_set_sensitive(network_port, FALSE);
976 gtk_widget_set_sensitive(network_login, FALSE);
977 gtk_widget_set_sensitive(network_password_label, TRUE);
978 gtk_label_set_markup_with_mnemonic(GTK_LABEL(network_password_label), _("New Pass_word:"));
979 gtk_widget_set_sensitive(network_password, TRUE);
980 gtk_widget_set_sensitive(network_confirm_password_label, TRUE);
981 gtk_widget_set_sensitive(network_confirm_password, TRUE);
982
983 gtk_widget_grab_focus(network_password);
984 break;
986 set_client_page(PAGE_NETWORK);
987 gtk_entry_buffer_set_text(gtk_entry_get_buffer(GTK_ENTRY(network_password)), "", -1);
988 gtk_entry_buffer_set_text(gtk_entry_get_buffer(GTK_ENTRY(network_confirm_password)),
989 "", -1);
990
991 gtk_widget_set_sensitive(network_host, FALSE);
992 gtk_widget_set_sensitive(network_port, FALSE);
993 gtk_widget_set_sensitive(network_login, FALSE);
994 gtk_widget_set_sensitive(network_password_label, TRUE);
995 gtk_label_set_markup_with_mnemonic(GTK_LABEL(network_password_label), _("Pass_word:"));
996 gtk_widget_set_sensitive(network_password, TRUE);
997 gtk_widget_set_sensitive(network_confirm_password_label, FALSE);
998 gtk_widget_set_sensitive(network_confirm_password, FALSE);
999
1000 gtk_widget_grab_focus(network_password);
1001 break;
1002 case WAITING_TYPE:
1004
1005 gtk_widget_set_sensitive(network_login, FALSE);
1006 gtk_widget_set_sensitive(network_password_label, FALSE);
1007 gtk_label_set_markup_with_mnemonic(GTK_LABEL(network_password_label), _("Pass_word:"));
1008 gtk_widget_set_sensitive(network_password, FALSE);
1009 gtk_widget_set_sensitive(network_confirm_password_label, FALSE);
1010 gtk_widget_set_sensitive(network_confirm_password, FALSE);
1011 break;
1012 }
1013
1014 connection_status = state;
1015}
1016
1017/**********************************************************************/
1020static void connection_state_reset(void)
1021{
1023}
1024
1025/**********************************************************************/
1030 const char *message)
1031{
1033
1034 switch (type) {
1035 case AUTH_NEWUSER_FIRST:
1036 case AUTH_NEWUSER_RETRY:
1038 return;
1039 case AUTH_LOGIN_FIRST:
1040 /* if we magically have a password already present in 'fc_password'
1041 * then, use that and skip the password entry dialog */
1042 if (fc_password[0] != '\0') {
1043 struct packet_authentication_reply reply;
1044
1047 return;
1048 } else {
1050 }
1051 return;
1052 case AUTH_LOGIN_RETRY:
1054 return;
1055 }
1056
1057 log_error("Unsupported authentication type %d: %s.", type, message);
1058}
1059
1060/**********************************************************************/
1065static void connect_callback(GtkWidget *w, gpointer data)
1066{
1067 char errbuf [512];
1068 struct packet_authentication_reply reply;
1069
1070 switch (connection_status) {
1071 case LOGIN_TYPE:
1073 gtk_entry_buffer_get_text(gtk_entry_get_buffer(GTK_ENTRY(network_login))));
1075 gtk_entry_buffer_get_text(gtk_entry_get_buffer(GTK_ENTRY(network_host))));
1076 server_port = atoi(gtk_entry_buffer_get_text(gtk_entry_get_buffer(GTK_ENTRY(network_port))));
1077
1079 errbuf, sizeof(errbuf)) != -1) {
1080 } else {
1082
1084 }
1085 return;
1086 case NEW_PASSWORD_TYPE:
1087 if (w != network_password) {
1089 gtk_entry_buffer_get_text(gtk_entry_get_buffer(GTK_ENTRY(network_password))));
1090 sz_strlcpy(reply.password,
1091 gtk_entry_buffer_get_text(gtk_entry_get_buffer(GTK_ENTRY(network_confirm_password))));
1092 if (strncmp(reply.password, fc_password, MAX_LEN_NAME) == 0) {
1093 fc_password[0] = '\0';
1095
1097 } else {
1098 append_network_statusbar(_("Passwords don't match, enter password."),
1099 TRUE);
1100
1102 }
1103 }
1104 return;
1106 sz_strlcpy(reply.password,
1107 gtk_entry_buffer_get_text(gtk_entry_get_buffer(GTK_ENTRY(network_password))));
1109
1111 return;
1112 case WAITING_TYPE:
1113 return;
1114 }
1115
1116 log_error("Unsupported connection status: %d", connection_status);
1117}
1118
1119/**********************************************************************/
1122static void network_activate_callback(GtkTreeView *view,
1123 GtkTreePath *arg1,
1124 GtkTreeViewColumn *arg2,
1125 gpointer data)
1126{
1127 connect_callback(NULL, data);
1128}
1129
1130/**********************************************************************/
1134static void update_server_playerlist(const struct server *pserver)
1135{
1136 GtkListStore *store;
1137 GtkTreeIter iter;
1138 int n, i;
1139
1141 fc_assert_ret(store != NULL);
1142
1143 gtk_list_store_clear(store);
1144 if (!pserver || !pserver->players) {
1145 return;
1146 }
1147
1148 n = pserver->nplayers;
1149 for (i = 0; i < n; i++) {
1150 gtk_list_store_append(store, &iter);
1151 gtk_list_store_set(store, &iter,
1152 0, pserver->players[i].name,
1153 1, pserver->players[i].type,
1154 2, pserver->players[i].host,
1155 3, pserver->players[i].nation,
1156 -1);
1157 }
1158}
1159
1160/**********************************************************************/
1163static void network_list_callback(GtkTreeSelection *select, gpointer data)
1164{
1165 GtkTreeModel *model;
1166 GtkTreeIter it;
1167 const char *host;
1168 int port;
1169 char portstr[32];
1170 const struct server *pserver = NULL;
1171
1172 if (!gtk_tree_selection_get_selected(select, &model, &it)) {
1173 return;
1174 }
1175
1176 if (select == meta_selection) {
1177 GtkTreePath *path;
1178 struct srv_list *srvrs;
1179
1181 path = gtk_tree_model_get_path(model, &it);
1183 /* We are not yet inside mutex protected block */
1184 fc_allocate_mutex(&srvrs->mutex);
1185 }
1186 if (srvrs->servers && path) {
1187 gint pos = gtk_tree_path_get_indices(path)[0];
1188
1189 pserver = server_list_get(srvrs->servers, pos);
1190 }
1192 /* We are not yet inside mutex protected block */
1193 fc_release_mutex(&srvrs->mutex);
1194 }
1195 gtk_tree_path_free(path);
1196 }
1197 update_server_playerlist(pserver);
1198
1199 gtk_tree_model_get(model, &it, 0, &host, 1, &port, -1);
1200
1201 gtk_entry_buffer_set_text(gtk_entry_get_buffer(GTK_ENTRY(network_host)), host, -1);
1202 fc_snprintf(portstr, sizeof(portstr), "%d", port);
1203 gtk_entry_buffer_set_text(gtk_entry_get_buffer(GTK_ENTRY(network_port)), portstr, -1);
1204}
1205
1206/**********************************************************************/
1209static void update_network_page(void)
1210{
1211 char buf[256];
1212
1213 gtk_tree_selection_unselect_all(lan_selection);
1214 gtk_tree_selection_unselect_all(meta_selection);
1215
1216 gtk_entry_buffer_set_text(gtk_entry_get_buffer(GTK_ENTRY(network_login)), user_name, -1);
1217 gtk_entry_buffer_set_text(gtk_entry_get_buffer(GTK_ENTRY(network_host)), server_host, -1);
1218 fc_snprintf(buf, sizeof(buf), "%d", server_port);
1219 gtk_entry_buffer_set_text(gtk_entry_get_buffer(GTK_ENTRY(network_port)), buf, -1);
1220}
1221
1222/**********************************************************************/
1225GtkWidget *create_network_page(void)
1226{
1227 GtkWidget *box, *sbox, *bbox, *hbox, *notebook;
1228 GtkWidget *button, *label, *view, *sw, *table;
1229 GtkTreeSelection *selection;
1230 GtkListStore *store;
1231 GtkEventController *controller;
1232
1233 box = gtk_box_new(GTK_ORIENTATION_VERTICAL, 2);
1234 gtk_widget_set_margin_start(box, 4);
1235 gtk_widget_set_margin_end(box, 4);
1236 gtk_widget_set_margin_top(box, 4);
1237 gtk_widget_set_margin_bottom(box, 4);
1238
1239 notebook = gtk_notebook_new();
1240 gtk_box_append(GTK_BOX(box), notebook);
1241
1242 /* LAN pane. */
1243 lan_store = gtk_list_store_new(7, G_TYPE_STRING, /* host */
1244 G_TYPE_INT, /* port */
1245 G_TYPE_STRING, /* version */
1246 G_TYPE_STRING, /* state */
1247 G_TYPE_INT, /* nplayers */
1248 G_TYPE_STRING, /* humans */
1249 G_TYPE_STRING); /* message */
1250
1251 view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(lan_store));
1252 gtk_widget_set_hexpand(view, TRUE);
1253 gtk_widget_set_vexpand(view, TRUE);
1254 g_object_unref(lan_store);
1255 gtk_tree_view_columns_autosize(GTK_TREE_VIEW(view));
1256
1257 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(view));
1258 lan_selection = selection;
1259 gtk_tree_selection_set_mode(selection, GTK_SELECTION_SINGLE);
1260
1261 controller = gtk_event_controller_focus_new();
1262 g_signal_connect(controller, "enter",
1263 G_CALLBACK(terminate_signal_processing), NULL);
1264 gtk_widget_add_controller(view, controller);
1265
1266 g_signal_connect(view, "row-activated",
1267 G_CALLBACK(network_activate_callback), NULL);
1268 g_signal_connect(selection, "changed",
1269 G_CALLBACK(network_list_callback), NULL);
1270
1271 add_treeview_column(view, _("Server Name"), G_TYPE_STRING, 0);
1272 add_treeview_column(view, _("Port"), G_TYPE_INT, 1);
1273 add_treeview_column(view, _("Version"), G_TYPE_STRING, 2);
1274 add_treeview_column(view, _("Status"), G_TYPE_STRING, 3);
1275 add_treeview_column(view, _("Players"), G_TYPE_INT, 4);
1276 add_treeview_column(view, _("Humans"), G_TYPE_STRING, 5);
1277 add_treeview_column(view, _("Comment"), G_TYPE_STRING, 6);
1278
1279 label = gtk_label_new_with_mnemonic(_("Local _Area Network"));
1280
1281 sw = gtk_scrolled_window_new();
1282 gtk_widget_set_margin_start(sw, 4);
1283 gtk_widget_set_margin_end(sw, 4);
1284 gtk_widget_set_margin_top(sw, 4);
1285 gtk_widget_set_margin_bottom(sw, 4);
1286 gtk_scrolled_window_set_has_frame(GTK_SCROLLED_WINDOW(sw), TRUE);
1287 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(sw),
1288 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
1289 gtk_scrolled_window_set_child(GTK_SCROLLED_WINDOW(sw), view);
1290 gtk_notebook_append_page(GTK_NOTEBOOK(notebook), sw, label);
1291
1292
1293 /* Metaserver pane. */
1294 meta_store = gtk_list_store_new(7, G_TYPE_STRING, /* host */
1295 G_TYPE_INT, /* port */
1296 G_TYPE_STRING, /* version */
1297 G_TYPE_STRING, /* state */
1298 G_TYPE_INT, /* nplayers */
1299 G_TYPE_STRING, /* humans */
1300 G_TYPE_STRING); /* message */
1301
1302 view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(meta_store));
1303 gtk_widget_set_hexpand(view, TRUE);
1304 gtk_widget_set_vexpand(view, TRUE);
1305 g_object_unref(meta_store);
1306 gtk_tree_view_columns_autosize(GTK_TREE_VIEW(view));
1307
1308 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(view));
1309 meta_selection = selection;
1310 gtk_tree_selection_set_mode(selection, GTK_SELECTION_SINGLE);
1311
1312 controller = gtk_event_controller_focus_new();
1313 g_signal_connect(controller, "enter",
1314 G_CALLBACK(terminate_signal_processing), NULL);
1315 gtk_widget_add_controller(view, controller);
1316
1317 g_signal_connect(view, "row-activated",
1318 G_CALLBACK(network_activate_callback), NULL);
1319 g_signal_connect(selection, "changed",
1320 G_CALLBACK(network_list_callback), NULL);
1321
1322 add_treeview_column(view, _("Server Name"), G_TYPE_STRING, 0);
1323 add_treeview_column(view, _("Port"), G_TYPE_INT, 1);
1324 add_treeview_column(view, _("Version"), G_TYPE_STRING, 2);
1325 add_treeview_column(view, _("Status"), G_TYPE_STRING, 3);
1326 add_treeview_column(view, _("Players"), G_TYPE_INT, 4);
1327 add_treeview_column(view, _("Humans"), G_TYPE_STRING, 5);
1328 add_treeview_column(view, _("Comment"), G_TYPE_STRING, 6);
1329
1330 label = gtk_label_new_with_mnemonic(_("Internet _Metaserver"));
1331
1332 sw = gtk_scrolled_window_new();
1333 gtk_widget_set_margin_start(sw, 4);
1334 gtk_widget_set_margin_end(sw, 4);
1335 gtk_widget_set_margin_top(sw, 4);
1336 gtk_widget_set_margin_bottom(sw, 4);
1337 gtk_scrolled_window_set_has_frame(GTK_SCROLLED_WINDOW(sw), TRUE);
1338 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(sw),
1339 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
1340 gtk_scrolled_window_set_child(GTK_SCROLLED_WINDOW(sw), view);
1341 if (GUI_GTK_OPTION(metaserver_tab_first)) {
1342 gtk_notebook_prepend_page(GTK_NOTEBOOK(notebook), sw, label);
1343 } else {
1344 gtk_notebook_append_page(GTK_NOTEBOOK(notebook), sw, label);
1345 }
1346
1347 /* Bottom part of the page, outside the inner notebook. */
1348 sbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 2);
1349 gtk_box_append(GTK_BOX(box), sbox);
1350
1351 hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 12);
1352 gtk_widget_set_margin_bottom(hbox, 8);
1353 gtk_widget_set_margin_end(hbox, 8);
1354 gtk_widget_set_margin_start(hbox, 8);
1355 gtk_widget_set_margin_top(hbox, 8);
1356 gtk_box_append(GTK_BOX(sbox), hbox);
1357
1358 table = gtk_grid_new();
1359 gtk_grid_set_row_spacing(GTK_GRID(table), 2);
1360 gtk_grid_set_column_spacing(GTK_GRID(table), 12);
1361 gtk_box_append(GTK_BOX(hbox), table);
1362
1363 network_host = gtk_entry_new();
1364 g_signal_connect(network_host, "activate",
1365 G_CALLBACK(connect_callback), NULL);
1366 gtk_grid_attach(GTK_GRID(table), network_host, 1, 0, 1, 1);
1367
1368 label = g_object_new(GTK_TYPE_LABEL,
1369 "use-underline", TRUE,
1370 "mnemonic-widget", network_host,
1371 "label", _("_Host:"),
1372 "xalign", 0.0,
1373 "yalign", 0.5,
1374 NULL);
1375 network_host_label = label;
1376 gtk_grid_attach(GTK_GRID(table), label, 0, 0, 1, 1);
1377
1378 network_port = gtk_entry_new();
1379 g_signal_connect(network_port, "activate",
1380 G_CALLBACK(connect_callback), NULL);
1381 gtk_grid_attach(GTK_GRID(table), network_port, 1, 1, 1, 1);
1382
1383 label = g_object_new(GTK_TYPE_LABEL,
1384 "use-underline", TRUE,
1385 "mnemonic-widget", network_port,
1386 "label", _("_Port:"),
1387 "xalign", 0.0,
1388 "yalign", 0.5,
1389 NULL);
1390 network_port_label = label;
1391 gtk_grid_attach(GTK_GRID(table), label, 0, 1, 1, 1);
1392
1393 network_login = gtk_entry_new();
1394 gtk_widget_set_margin_top(network_login, 10);
1395 g_signal_connect(network_login, "activate",
1396 G_CALLBACK(connect_callback), NULL);
1397 gtk_grid_attach(GTK_GRID(table), network_login, 1, 3, 1, 1);
1398
1399 label = g_object_new(GTK_TYPE_LABEL,
1400 "use-underline", TRUE,
1401 "mnemonic-widget", network_login,
1402 "label", _("_Login:"),
1403 "xalign", 0.0,
1404 "yalign", 0.5,
1405 NULL);
1406 gtk_widget_set_margin_top(label, 10);
1407 network_login_label = label;
1408 gtk_grid_attach(GTK_GRID(table), label, 0, 3, 1, 1);
1409
1410 network_password = gtk_entry_new();
1411 g_signal_connect(network_password, "activate",
1412 G_CALLBACK(connect_callback), NULL);
1413 gtk_entry_set_visibility(GTK_ENTRY(network_password), FALSE);
1414 gtk_grid_attach(GTK_GRID(table), network_password, 1, 4, 1, 1);
1415
1416 label = g_object_new(GTK_TYPE_LABEL,
1417 "use-underline", TRUE,
1418 "mnemonic-widget", network_password,
1419 "label", _("Pass_word:"),
1420 "xalign", 0.0,
1421 "yalign", 0.5,
1422 NULL);
1423 network_password_label = label;
1424 gtk_grid_attach(GTK_GRID(table), label, 0, 4, 1, 1);
1425
1426 network_confirm_password = gtk_entry_new();
1427 g_signal_connect(network_confirm_password, "activate",
1428 G_CALLBACK(connect_callback), NULL);
1429 gtk_entry_set_visibility(GTK_ENTRY(network_confirm_password), FALSE);
1430 gtk_grid_attach(GTK_GRID(table), network_confirm_password, 1, 5, 1, 1);
1431
1432 label = g_object_new(GTK_TYPE_LABEL,
1433 "use-underline", TRUE,
1434 "mnemonic-widget", network_confirm_password,
1435 "label", _("Conf_irm Password:"),
1436 "xalign", 0.0,
1437 "yalign", 0.5,
1438 NULL);
1440 gtk_grid_attach(GTK_GRID(table), label, 0, 5, 1, 1);
1441
1442 /* Server player list. */
1443 store = gtk_list_store_new(4, G_TYPE_STRING,
1444 G_TYPE_STRING,
1445 G_TYPE_STRING,
1446 G_TYPE_STRING);
1448
1449 view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(store));
1450 gtk_widget_set_hexpand(view, TRUE);
1451 add_treeview_column(view, _("Name"), G_TYPE_STRING, 0);
1452 add_treeview_column(view, _("Type"), G_TYPE_STRING, 1);
1453 add_treeview_column(view, _("Host"), G_TYPE_STRING, 2);
1454 add_treeview_column(view, _("Nation"), G_TYPE_STRING, 3);
1456
1457 sw = gtk_scrolled_window_new();
1458 gtk_scrolled_window_set_has_frame(GTK_SCROLLED_WINDOW(sw), TRUE);
1459 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(sw),
1460 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
1461 gtk_scrolled_window_set_child(GTK_SCROLLED_WINDOW(sw), view);
1462 gtk_box_append(GTK_BOX(hbox), sw);
1463
1464 bbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 2);
1465 gtk_widget_set_margin_bottom(bbox, 2);
1466 gtk_widget_set_margin_end(bbox, 2);
1467 gtk_widget_set_margin_start(bbox, 2);
1468 gtk_widget_set_margin_top(bbox, 2);
1469 gtk_box_set_spacing(GTK_BOX(bbox), 12);
1470 gtk_box_append(GTK_BOX(sbox), bbox);
1471
1472 button = gtk_button_new_from_icon_name("view-refresh");
1473 gtk_box_append(GTK_BOX(bbox), button);
1474 g_signal_connect(button, "clicked",
1475 G_CALLBACK(update_network_lists), NULL);
1476
1477 button = gtk_button_new_with_mnemonic(_("_Cancel"));
1478 gtk_box_append(GTK_BOX(bbox), button);
1479 g_signal_connect(button, "clicked",
1480 G_CALLBACK(main_callback), NULL);
1481
1482 button = gtk_button_new_with_mnemonic(_("C_onnect"));
1483 gtk_box_append(GTK_BOX(bbox), button);
1484 g_signal_connect(button, "clicked",
1485 G_CALLBACK(connect_callback), NULL);
1486
1487 return box;
1488}
1489
1490/****************************************************************************
1491 START PAGE
1492****************************************************************************/
1494
1495static GtkWidget *start_options_table;
1497static GtkTreeStore *connection_list_store;
1498static GtkTreeView *connection_list_view;
1499static GtkWidget *start_aifill_spin = NULL;
1500static GtkWidget *ai_lvl_combobox = NULL;
1501
1502
1503/* NB: Must match creation arguments in connection_list_store_new(). */
1520
1521/**********************************************************************/
1524static inline GtkTreeStore *connection_list_store_new(void)
1525{
1526 return gtk_tree_store_new(CL_NUM_COLUMNS,
1527 G_TYPE_INT, /* CL_COL_PLAYER_NUMBER */
1528 G_TYPE_STRING, /* CL_COL_USER_NAME */
1529 G_TYPE_BOOLEAN, /* CL_COL_READY_STATE */
1530 G_TYPE_STRING, /* CL_COL_PLAYER_NAME */
1531 GDK_TYPE_PIXBUF, /* CL_COL_FLAG */
1532 GDK_TYPE_PIXBUF, /* CL_COL_COLOR */
1533 G_TYPE_STRING, /* CL_COL_NATION */
1534 G_TYPE_STRING, /* CL_COL_TEAM */
1535 G_TYPE_INT, /* CL_COL_CONN_ID */
1536 G_TYPE_INT, /* CL_COL_STYLE */
1537 G_TYPE_INT, /* CL_COL_WEIGHT */
1538 G_TYPE_BOOLEAN); /* CL_COL_COLLAPSED */
1539}
1540
1541/**********************************************************************/
1545static void client_aitoggle_player(void *data)
1546{
1547 struct player *pplayer = player_by_number(FC_PTR_TO_INT(data));
1548
1549 if (NULL != pplayer
1550 && pplayer == client_player()
1551 && !is_human(pplayer)) {
1552 send_chat("/away");
1553 }
1554}
1555
1556/**********************************************************************/
1559static void client_take_player(struct player *pplayer)
1560{
1561 int request_id = send_chat_printf("/take \"%s\"", player_name(pplayer));
1562 void *data = FC_INT_TO_PTR(player_number(pplayer));
1563
1566}
1567
1568/**********************************************************************/
1571static void object_put(GObject *object, struct player *pplayer,
1572 struct connection *pconn)
1573{
1574 /* Note that passing -1 to GINT_TO_POINTER() is buggy with some versions
1575 * of gcc. player_slot_count() is not a valid player number. 0 is not
1576 * a valid connection id (see comment in server/sernet.c:
1577 * makeup_connection_name()). */
1578 g_object_set_data(object, "player_id",
1579 GINT_TO_POINTER(NULL != pplayer
1580 ? player_number(pplayer)
1581 : player_slot_count()));
1582 g_object_set_data(object, "connection_id",
1583 GINT_TO_POINTER(NULL != pconn ? pconn->id : 0));
1584}
1585
1586/**********************************************************************/
1590static bool object_extract(GObject *object, struct player **ppplayer,
1591 struct connection **ppconn)
1592{
1593 bool ret = FALSE;
1594 int id;
1595
1596 if (NULL != ppplayer) {
1597 id = GPOINTER_TO_INT(g_object_get_data(object, "player_id"));
1598 *ppplayer = player_by_number(id);
1599 if (NULL != *ppplayer) {
1600 ret = TRUE;
1601 }
1602 }
1603 if (NULL != ppconn) {
1604 id = GPOINTER_TO_INT(g_object_get_data(object, "connection_id"));
1605 *ppconn = conn_by_number(id);
1606 if (NULL != *ppconn) {
1607 ret = TRUE;
1608 }
1609 }
1610
1611 return ret;
1612}
1613
1614/**********************************************************************/
1617static void game_options_callback(GtkWidget *w, gpointer data)
1618{
1619 option_dialog_popup(_("Game Settings"), server_optset);
1620}
1621
1622/**********************************************************************/
1625static void ai_skill_callback(GtkWidget *w, gpointer data)
1626{
1627 enum ai_level *levels = (enum ai_level *)data;
1628 const char *name;
1629 int i;
1630
1631 i = gtk_combo_box_get_active(GTK_COMBO_BOX(w));
1632
1633 if (i != -1) {
1634 enum ai_level level = levels[i];
1635
1636 /* Suppress changes provoked by server rather than local user */
1637 if (server_ai_level() != level) {
1639 send_chat_printf("/%s", name);
1640 }
1641 }
1642}
1643
1644/* HACK: sometimes when creating the ruleset combo the value is set without
1645 * the user's control. In this case we don't want to do a /read. */
1647
1648/**********************************************************************/
1651static void ruleset_selected(const char *name)
1652{
1653 if (name && name[0] != '\0' && !no_ruleset_callback) {
1655 }
1656}
1657
1658/**********************************************************************/
1663static void ruleset_entry_changed(GtkWidget *w, gpointer data)
1664{
1665 const char *name = NULL;
1666
1667 name = gtk_combo_box_text_get_active_text(GTK_COMBO_BOX_TEXT(ruleset_combo));
1668
1669 if (name != NULL) {
1671 }
1672}
1673
1674/**********************************************************************/
1678static void ai_fill_changed_by_user(GtkWidget *w, gpointer data)
1679{
1682 gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(w)));
1683 }
1684}
1685
1686/**********************************************************************/
1690{
1691 if (start_aifill_spin) {
1692 bool old = send_new_aifill_to_server;
1693 /* Suppress callback from this change to avoid a loop. */
1695 /* HACK: this GUI control doesn't have quite the same semantics as the
1696 * server 'aifill' option, in that it claims to represent the minimum
1697 * number of players _including humans_. The GUI control has a minimum
1698 * value of 1, so aifill == 0 will not be represented correctly.
1699 * But there's generally exactly one human player because the control
1700 * only shows up for a locally spawned server, so we more or less
1701 * get away with this. */
1702 gtk_spin_button_set_value(GTK_SPIN_BUTTON(start_aifill_spin), aifill);
1704 }
1705}
1706
1707/**********************************************************************/
1711{
1713}
1714
1715/**********************************************************************/
1719{
1720 if (conn_popover != NULL){
1721 gtk_widget_unparent(conn_popover);
1722 g_object_unref(conn_popover);
1723
1724 conn_popover = NULL;
1725 }
1726}
1727
1728/**********************************************************************/
1731static void conn_menu_team_chosen(GSimpleAction *action, GVariant *parameter,
1732 gpointer data)
1733{
1734 struct player *pplayer;
1735 struct team_slot *tslot = g_object_get_data(G_OBJECT(action), "slot");
1736 GMenu *menu = data;
1737
1738 if (object_extract(G_OBJECT(menu), &pplayer, NULL)
1739 && NULL != tslot
1740 && team_slot_index(tslot) != team_number(pplayer->team)) {
1741 send_chat_printf("/team \"%s\" \"%s\"",
1742 player_name(pplayer),
1743 team_slot_rule_name(tslot));
1744 }
1745
1747}
1748
1749/**********************************************************************/
1752static void conn_menu_ready_chosen(GSimpleAction *action, GVariant *parameter,
1753 gpointer data)
1754{
1755 struct player *pplayer;
1756 GMenu *menu = data;
1757
1758 if (object_extract(G_OBJECT(menu), &pplayer, NULL)) {
1760 player_number(pplayer), !pplayer->is_ready);
1761 }
1762
1764}
1765
1766/**********************************************************************/
1769static void conn_menu_nation_chosen(GSimpleAction *action, GVariant *parameter,
1770 gpointer data)
1771{
1772 struct player *pplayer;
1773 GMenu *menu = data;
1774
1775 if (object_extract(G_OBJECT(menu), &pplayer, NULL)) {
1776 popup_races_dialog(pplayer);
1777 }
1778
1780}
1781
1782/**********************************************************************/
1786static void conn_menu_player_command(GSimpleAction *action, GVariant *parameter,
1787 gpointer data)
1788{
1789 struct player *pplayer;
1790 GMenu *menu = data;
1791
1792 if (object_extract(G_OBJECT(menu), &pplayer, NULL)) {
1793 send_chat_printf("/%s \"%s\"",
1794 (char *) g_object_get_data(G_OBJECT(action), "command"),
1795 player_name(pplayer));
1796 }
1797
1799}
1800
1801/**********************************************************************/
1804static void conn_menu_player_take(GSimpleAction *action, GVariant *parameter,
1805 gpointer data)
1806{
1807 struct player *pplayer;
1808 GMenu *menu = data;
1809
1810 if (object_extract(G_OBJECT(menu), &pplayer, NULL)) {
1811 client_take_player(pplayer);
1812 }
1813
1815}
1816
1817/**********************************************************************/
1821static void conn_menu_connection_command(GSimpleAction *action,
1822 GVariant *parameter, gpointer data)
1823{
1824 struct connection *pconn;
1825 GMenu *menu = data;
1826
1827 if (object_extract(G_OBJECT(menu), NULL, &pconn)) {
1829 (char *) g_object_get_data(G_OBJECT(action), "command"),
1830 pconn->username);
1831 }
1832
1834}
1835
1836/**********************************************************************/
1839static void show_conn_popup(struct player *pplayer, struct connection *pconn)
1840{
1841 GtkWidget *popup;
1842 char buf[4096] = "";
1843
1844 if (pconn) {
1845 cat_snprintf(buf, sizeof(buf), _("Connection name: %s"),
1846 pconn->username);
1847 } else {
1848 cat_snprintf(buf, sizeof(buf), _("Player name: %s"),
1849 player_name(pplayer));
1850 }
1851 cat_snprintf(buf, sizeof(buf), "\n");
1852 if (pconn) {
1853 cat_snprintf(buf, sizeof(buf), _("Host: %s"), pconn->addr);
1854 }
1855 cat_snprintf(buf, sizeof(buf), "\n");
1856
1857 /* Show popup. */
1858 popup = gtk_message_dialog_new(NULL, 0,
1859 GTK_MESSAGE_INFO, GTK_BUTTONS_CLOSE,
1860 "%s", buf);
1861 gtk_window_set_title(GTK_WINDOW(popup), _("Player/conn info"));
1862 setup_dialog(popup, toplevel);
1863 g_signal_connect(popup, "response", G_CALLBACK(gtk_window_destroy), NULL);
1864 gtk_window_present(GTK_WINDOW(popup));
1865}
1866
1867/**********************************************************************/
1870static void conn_menu_info_chosen(GSimpleAction *action, GVariant *parameter,
1871 gpointer data)
1872{
1873 struct player *pplayer;
1874 struct connection *pconn;
1875 GMenu *menu = data;
1876
1877 if (object_extract(G_OBJECT(menu), &pplayer, &pconn)) {
1878 show_conn_popup(pplayer, pconn);
1879 }
1880
1882}
1883
1884/**********************************************************************/
1888static GtkWidget *create_conn_menu(struct player *pplayer,
1889 struct connection *pconn)
1890{
1891 GMenu *menu;
1892 gchar *buf;
1893 GSimpleAction *act;
1894 GActionGroup *group;
1895
1896 group = G_ACTION_GROUP(g_simple_action_group_new());
1897
1898 menu = g_menu_new();
1899
1900 object_put(G_OBJECT(menu), pplayer, pconn);
1901
1902 buf = g_strdup_printf(_("%s info"),
1903 pconn ? pconn->username : player_name(pplayer));
1904
1905 act = g_simple_action_new("info", NULL);
1906 g_action_map_add_action(G_ACTION_MAP(group), G_ACTION(act));
1907 g_signal_connect(act, "activate", G_CALLBACK(conn_menu_info_chosen), menu);
1908 menu_item_append_unref(menu, g_menu_item_new(buf, "win.info"));
1909
1910 if (NULL != pplayer) {
1911 act = g_simple_action_new("toggle_ready", NULL);
1912 g_action_map_add_action(G_ACTION_MAP(group), G_ACTION(act));
1913 g_signal_connect(act, "activate", G_CALLBACK(conn_menu_ready_chosen), menu);
1914 g_simple_action_set_enabled(G_SIMPLE_ACTION(act), is_human(pplayer));
1915 menu_item_append_unref(menu, g_menu_item_new(_("Toggle player ready"),
1916 "win.toggle_ready"));
1917
1918 act = g_simple_action_new("pick_nation", NULL);
1919 g_action_map_add_action(G_ACTION_MAP(group), G_ACTION(act));
1920 g_signal_connect(act, "activate", G_CALLBACK(conn_menu_nation_chosen), menu);
1921 g_simple_action_set_enabled(G_SIMPLE_ACTION(act),
1923 pplayer));
1924 menu_item_append_unref(menu, g_menu_item_new(_("Pick nation"), "win.pick_nation"));
1925
1926 act = g_simple_action_new("observe", NULL);
1927 g_object_set_data_full(G_OBJECT(act), "command", g_strdup("observe"),
1928 (GDestroyNotify) g_free);
1929 g_action_map_add_action(G_ACTION_MAP(group), G_ACTION(act));
1930 g_signal_connect(act, "activate", G_CALLBACK(conn_menu_player_command), menu);
1931 menu_item_append_unref(menu, g_menu_item_new(_("Observe this player"),
1932 "win.observe"));
1933
1934 act = g_simple_action_new("take_plr", NULL);
1935 g_action_map_add_action(G_ACTION_MAP(group), G_ACTION(act));
1936 g_signal_connect(act, "activate", G_CALLBACK(conn_menu_player_take), menu);
1937 menu_item_append_unref(menu, g_menu_item_new(_("Take this player"),
1938 "win.take_plr"));
1939 }
1940
1941 if (ALLOW_CTRL <= client.conn.access_level && NULL != pconn
1942 && pconn->id != client.conn.id) {
1943 act = g_simple_action_new("cut_conn", NULL);
1944 g_object_set_data_full(G_OBJECT(act), "command", g_strdup("cut"),
1945 (GDestroyNotify) g_free);
1946 g_action_map_add_action(G_ACTION_MAP(group), G_ACTION(act));
1947 g_signal_connect(act, "activate", G_CALLBACK(conn_menu_connection_command), menu);
1948 menu_item_append_unref(menu, g_menu_item_new(_("Cut connection"), "win.cut_conn"));
1949 }
1950
1951 if (ALLOW_CTRL <= client.conn.access_level && NULL != pplayer) {
1952 act = g_simple_action_new("aitoggle", NULL);
1953 g_object_set_data_full(G_OBJECT(act), "command", g_strdup("aitoggle"),
1954 (GDestroyNotify) g_free);
1955 g_action_map_add_action(G_ACTION_MAP(group), G_ACTION(act));
1956 g_signal_connect(act, "activate", G_CALLBACK(conn_menu_connection_command), menu);
1957 menu_item_append_unref(menu, g_menu_item_new(_("Aitoggle player"), "win.aitoggle"));
1958
1959 if (pplayer != client.conn.playing && game.info.is_new_game) {
1960 act = g_simple_action_new("remove", NULL);
1961 g_object_set_data_full(G_OBJECT(act), "command", g_strdup("remove"),
1962 (GDestroyNotify) g_free);
1963 g_action_map_add_action(G_ACTION_MAP(group), G_ACTION(act));
1964 g_signal_connect(act, "activate", G_CALLBACK(conn_menu_connection_command), menu);
1965 menu_item_append_unref(menu, g_menu_item_new(_("Remove player"), "win.remove"));
1966 }
1967 }
1968
1969 if (ALLOW_ADMIN <= client.conn.access_level && NULL != pconn
1970 && pconn->id != client.conn.id) {
1971 enum cmdlevel level;
1972
1973 /* No item for hack access; that would be a serious security hole. */
1974 for (level = cmdlevel_min(); level < client.conn.access_level; level++) {
1975 char actbuf[128];
1976
1977 buf = g_strdup_printf(_("Give %s access"), cmdlevel_name(level));
1978 fc_snprintf(actbuf, sizeof(actbuf), "cmdlevel_%d", level);
1979
1980 act = g_simple_action_new(actbuf, NULL);
1981 g_object_set_data_full(G_OBJECT(act), "command",
1982 g_strdup_printf("cmdlevel %s",
1983 cmdlevel_name(level)),
1984 (GDestroyNotify) g_free);
1985 g_action_map_add_action(G_ACTION_MAP(group), G_ACTION(act));
1986 g_signal_connect(act, "activate", G_CALLBACK(conn_menu_connection_command), menu);
1987 fc_snprintf(actbuf, sizeof(actbuf), "win.cmdlevel_%d", level);
1988 menu_item_append_unref(menu, g_menu_item_new(buf, actbuf));
1989 g_free(buf);
1990 }
1991 }
1992
1993 if (ALLOW_CTRL <= client.conn.access_level
1994 && NULL != pplayer && is_ai(pplayer)) {
1995 enum ai_level level;
1996
1997 for (level = 0; level < AI_LEVEL_COUNT; level++) {
1999 char actbuf[128];
2000
2001 buf = g_strdup_printf(_("Difficulty: %s"), ai_level_translated_name(level));
2002 fc_snprintf(actbuf, sizeof(actbuf), "ailevel_%d", level);
2003
2004 act = g_simple_action_new(actbuf, NULL);
2005 g_object_set_data_full(G_OBJECT(act), "command",
2006 g_strdup(ai_level_cmd(level)),
2007 (GDestroyNotify) g_free);
2008 g_action_map_add_action(G_ACTION_MAP(group), G_ACTION(act));
2009 g_signal_connect(act, "activate", G_CALLBACK(conn_menu_player_command), menu);
2010 fc_snprintf(actbuf, sizeof(actbuf), "win.ailevel_%d", level);
2011 menu_item_append_unref(menu, g_menu_item_new(buf, actbuf));
2012 g_free(buf);
2013 }
2014 }
2015 }
2016
2017 if (pplayer != NULL /* && game.info.is_new_game */) {
2018 const int count = pplayer->team
2019 ? player_list_size(team_members(pplayer->team)) : 0;
2020 bool need_empty_team = (count != 1);
2021
2022 /* Can't use team_iterate here since it skips empty teams. */
2023 team_slots_iterate(tslot) {
2024 char actbuf[128];
2025 int id;
2026
2027 if (!team_slot_is_used(tslot)) {
2028 if (!need_empty_team) {
2029 continue;
2030 }
2031 need_empty_team = FALSE;
2032 }
2033
2034 id = team_number(team_slot_get_team(tslot));
2035
2036 /* TRANS: e.g., "Put on Team 5" */
2037 buf = g_strdup_printf(_("Put on %s"),
2039 fc_snprintf(actbuf, sizeof(actbuf), "team_%d", id);
2040
2041 act = g_simple_action_new(actbuf, NULL);
2042 g_object_set_data(G_OBJECT(act), "slot", tslot);
2043 g_action_map_add_action(G_ACTION_MAP(group), G_ACTION(act));
2044 g_signal_connect(act, "activate", G_CALLBACK(conn_menu_team_chosen), menu);
2045 fc_snprintf(actbuf, sizeof(actbuf), "win.team_%d", id);
2046 menu_item_append_unref(menu, g_menu_item_new(buf, actbuf));
2047 g_free(buf);
2049 }
2050
2052 conn_popover = gtk_popover_menu_new_from_model(G_MENU_MODEL(menu));
2053 g_object_ref(conn_popover);
2054 gtk_widget_insert_action_group(conn_popover, "win", group);
2055
2056 return conn_popover;
2057}
2058
2059/**********************************************************************/
2062static gboolean delayed_unselect_path(gpointer data)
2063{
2064 if (NULL != connection_list_view) {
2065 GtkTreeSelection *selection =
2066 gtk_tree_view_get_selection(connection_list_view);
2067 GtkTreePath *path = data;
2068
2069 gtk_tree_selection_unselect_path(selection, path);
2070 gtk_tree_path_free(path);
2071 }
2072 return FALSE;
2073}
2074
2075/**********************************************************************/
2078static gboolean connect_list_left_button(GtkGestureClick *gesture,
2079 int n_press,
2080 double x, double y, gpointer data)
2081{
2082 GtkEventController *controller = GTK_EVENT_CONTROLLER(gesture);
2083 GtkTreeView *tree = GTK_TREE_VIEW(gtk_event_controller_get_widget(controller));
2084 GtkTreePath *path = NULL;
2085 GtkTreeSelection *selection = gtk_tree_view_get_selection(tree);
2086
2087 if (!gtk_tree_view_get_path_at_pos(tree,
2088 x, y,
2089 &path, NULL, NULL, NULL)) {
2090 return FALSE;
2091 }
2092
2093 if (gtk_tree_selection_path_is_selected(selection, path)) {
2094 /* Need to delay to avoid problem with the expander. */
2095 g_idle_add(delayed_unselect_path, path);
2096 return FALSE; /* Return now, don't free the path. */
2097 }
2098
2099 gtk_tree_path_free(path);
2100
2101 return FALSE;
2102}
2103
2104/**********************************************************************/
2107static gboolean connect_list_right_button(GtkGestureClick *gesture,
2108 int n_press,
2109 double x, double y, gpointer data)
2110{
2111 GtkEventController *controller = GTK_EVENT_CONTROLLER(gesture);
2112 GtkWidget *tree = gtk_event_controller_get_widget(controller);
2113 GtkWidget *parent = data;
2114 GtkTreePath *path = NULL;
2115 GtkWidget *menu;
2116 GtkTreeSelection *selection;
2117 GtkTreeModel *model;
2118 GtkTreeIter iter;
2119 int player_no, conn_id;
2120 struct player *pplayer;
2121 struct connection *pconn;
2122 int bx, by;
2123 GdkRectangle rect = { .x = x, .y = y, .width = 1, .height = 1};
2124
2125 gtk_tree_view_convert_widget_to_bin_window_coords(GTK_TREE_VIEW(tree), x, y, &bx, &by);
2126
2127 if (!gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(tree), bx, by,
2128 &path, NULL, NULL, NULL)) {
2129 return FALSE;
2130 }
2131
2132 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree));
2133 model = gtk_tree_view_get_model(GTK_TREE_VIEW(tree));
2134
2135 if (!gtk_tree_selection_path_is_selected(selection, path)) {
2136 gtk_tree_selection_select_path(selection, path);
2137 }
2138 gtk_tree_model_get_iter(model, &iter, path);
2139
2140 gtk_tree_model_get(model, &iter, CL_COL_PLAYER_NUMBER, &player_no, -1);
2141 pplayer = player_by_number(player_no);
2142
2143 gtk_tree_model_get(model, &iter, CL_COL_CONN_ID, &conn_id, -1);
2144 pconn = conn_by_number(conn_id);
2145
2146 menu = create_conn_menu(pplayer, pconn);
2147 gtk_widget_set_parent(menu, parent); /* Gtk bug prevents tree view parenting */
2148 gtk_popover_set_pointing_to(GTK_POPOVER(menu), &rect);
2149
2150 gtk_popover_popup(GTK_POPOVER(menu));
2151
2152 gtk_tree_path_free(path);
2153
2154 return TRUE;
2155}
2156
2157/**********************************************************************/
2160static void connection_list_row_callback(GtkTreeView *tree_view,
2161 GtkTreeIter *iter,
2162 GtkTreePath *path,
2163 gpointer data)
2164{
2165 GtkTreeStore *store = GTK_TREE_STORE(gtk_tree_view_get_model(tree_view));
2166
2167 gtk_tree_store_set(store, iter,
2168 CL_COL_COLLAPSED, GPOINTER_TO_INT(data), -1);
2169}
2170
2171/**********************************************************************/
2175static bool conn_list_selection(struct player **ppplayer,
2176 struct connection **ppconn)
2177{
2178 if (NULL != connection_list_view) {
2179 GtkTreeIter iter;
2180 GtkTreeModel *model;
2181 GtkTreeSelection *selection =
2182 gtk_tree_view_get_selection(connection_list_view);
2183
2184 if (gtk_tree_selection_get_selected(selection, &model, &iter)) {
2185 int id;
2186
2187 if (NULL != ppplayer) {
2188 gtk_tree_model_get(model, &iter, CL_COL_PLAYER_NUMBER, &id, -1);
2189 *ppplayer = player_by_number(id);
2190 }
2191 if (NULL != ppconn) {
2192 gtk_tree_model_get(model, &iter, CL_COL_CONN_ID, &id, -1);
2193 *ppconn = conn_by_number(id);
2194 }
2195 return TRUE;
2196 }
2197 }
2198
2199 if (NULL != ppplayer) {
2200 *ppplayer = NULL;
2201 }
2202 if (NULL != ppconn) {
2203 *ppconn = NULL;
2204 }
2205 return FALSE;
2206}
2207
2208/**********************************************************************/
2211static void conn_list_select_conn(struct connection *pconn)
2212{
2213 GtkTreeModel *model;
2214 GtkTreeIter parent, child, *iter = NULL;
2215 GtkTreeSelection *selection;
2216 gboolean valid;
2217 const int search_id = pconn->id;
2218 int id;
2219
2220 if (NULL == connection_list_view) {
2221 return;
2222 }
2223
2224 model = gtk_tree_view_get_model(connection_list_view);
2225 selection = gtk_tree_view_get_selection(connection_list_view);
2226
2227 /* Main iteration. */
2228 valid = gtk_tree_model_get_iter_first(model, &parent);
2229 while (valid && NULL == iter) {
2230 gtk_tree_model_get(model, &parent, CL_COL_CONN_ID, &id, -1);
2231 if (search_id == id) {
2232 iter = &parent;
2233 break;
2234 }
2235
2236 /* Node children iteration. */
2237 valid = gtk_tree_model_iter_children(model, &child, &parent);
2238 while (valid && NULL == iter) {
2239 gtk_tree_model_get(model, &child, CL_COL_CONN_ID, &id, -1);
2240 if (search_id == id) {
2241 iter = &child;
2242 break;
2243 }
2244 valid = gtk_tree_model_iter_next(model, &child);
2245 }
2246
2247 valid = gtk_tree_model_iter_next(model, &parent);
2248 }
2249
2250 /* Select iterator. */
2251 if (NULL != iter) {
2252 gtk_tree_selection_select_iter(selection, iter);
2253 } else {
2254 log_error("%s(): connection %s not found.",
2255 __FUNCTION__, conn_description(pconn));
2256 }
2257}
2258
2259/**********************************************************************/
2262static void ready_button_callback(GtkWidget *w, gpointer data)
2263{
2264 if (can_client_control()) {
2267 !client_player()->is_ready);
2268 } else {
2270 }
2271}
2272
2273/**********************************************************************/
2276static void nation_button_callback(GtkWidget *w, gpointer data)
2277{
2278 struct player *selected_plr;
2279 bool row_selected = conn_list_selection(&selected_plr, NULL);
2280
2281 if (row_selected && NULL != selected_plr) {
2282 /* "Take <player_name>" */
2283 client_take_player(selected_plr);
2284 } else if (can_client_control()) {
2285 /* "Pick Nation" */
2287 } else {
2288 /* "Take a Player" */
2289 send_chat("/take -");
2290 }
2291}
2292
2293/**********************************************************************/
2296static void observe_button_callback(GtkWidget *w, gpointer data)
2297{
2298 struct player *selected_plr;
2299 bool row_selected = conn_list_selection(&selected_plr, NULL);
2300
2301 if (row_selected && NULL != selected_plr) {
2302 /* "Observe <player_name>" */
2303 send_chat_printf("/observe \"%s\"", player_name(selected_plr));
2304 } else if (!client_is_global_observer()) {
2305 /* "Observe" */
2306 send_chat("/observe");
2307 } else {
2308 /* "Do not observe" */
2309 send_chat("/detach");
2310 }
2311}
2312
2313/**********************************************************************/
2317{
2318 char buf[2 * MAX_LEN_NAME];
2319 const char *text;
2320 struct player *selected_plr;
2321 bool row_selected = conn_list_selection(&selected_plr, NULL);
2322 bool sensitive;
2323
2324 /*** Ready button. ***/
2325 if (can_client_control()) {
2326 sensitive = client_player()->is_alive;
2327 if (client_player()->is_ready) {
2328 text = _("Not _ready");
2329 } else {
2330 int num_unready = 0;
2331
2332 players_iterate_alive(pplayer) {
2333 if (is_human(pplayer) && !pplayer->is_ready) {
2334 num_unready++;
2335 }
2337
2338 if (num_unready > 1) {
2339 text = _("_Ready");
2340 } else {
2341 /* We are the last unready player so clicking here will
2342 * immediately start the game. */
2343 text = _("_Start");
2344 }
2345 }
2346 } else {
2347 text = _("_Start");
2348 if (can_client_access_hack()) {
2349 sensitive = TRUE;
2351 if (is_human(plr)) {
2352 /* There's human controlled player(s) in game, so it's their
2353 * job to start the game. */
2354 sensitive = FALSE;
2355 break;
2356 }
2358 } else {
2359 sensitive = FALSE;
2360 }
2361 }
2363 gtk_widget_set_sensitive(ready_button, sensitive);
2364
2365 /*** Nation button. ***/
2366 if (row_selected && NULL != selected_plr) {
2367 fc_snprintf(buf, sizeof(buf), _("_Take %s"), player_name(selected_plr));
2368 text = buf;
2369 sensitive = (client_is_observer() || selected_plr != client_player());
2370 } else if (can_client_control()) {
2371 text = _("Pick _Nation");
2372 sensitive = game.info.is_new_game;
2373 } else {
2374 text = _("_Take a Player");
2375 sensitive = game.info.is_new_game;
2376 }
2378 gtk_widget_set_sensitive(nation_button, sensitive);
2379
2380 /*** Observe button. ***/
2381 if (row_selected && NULL != selected_plr) {
2382 fc_snprintf(buf, sizeof(buf), _("_Observe %s"),
2383 player_name(selected_plr));
2384 text = buf;
2385 sensitive = (!client_is_observer() || selected_plr != client_player());
2386 } else if (!client_is_global_observer()) {
2387 text = _("_Observe");
2388 sensitive = TRUE;
2389 } else {
2390 text = _("Do not _observe");
2391 sensitive = TRUE;
2392 }
2394 gtk_widget_set_sensitive(observe_button, sensitive);
2395}
2396
2397/**********************************************************************/
2401static bool model_get_player_iter(GtkTreeModel *model,
2402 GtkTreeIter *iter,
2403 GtkTreeIter *start,
2404 const struct player *pplayer)
2405{
2406 const int search_id = player_number(pplayer);
2407 int id;
2408
2409 if (NULL != start) {
2410 *iter = *start;
2411 if (!gtk_tree_model_iter_next(model, iter)) {
2412 return FALSE;
2413 }
2414 } else if (!gtk_tree_model_get_iter_first(model, iter)) {
2415 return FALSE;
2416 }
2417
2418 do {
2419 gtk_tree_model_get(model, iter, CL_COL_PLAYER_NUMBER, &id, -1);
2420 if (id == search_id) {
2421 return TRUE;
2422 }
2423 } while (gtk_tree_model_iter_next(model, iter));
2424
2425 return FALSE;
2426}
2427
2428/**********************************************************************/
2432static bool model_get_conn_iter(GtkTreeModel *model, GtkTreeIter *iter,
2433 GtkTreeIter *parent, GtkTreeIter *start,
2434 const struct connection *pconn)
2435{
2436 const int search_id = pconn->id;
2437 int id;
2438
2439 if (NULL != start) {
2440 *iter = *start;
2441 if (!gtk_tree_model_iter_next(model, iter)) {
2442 return FALSE;
2443 }
2444 } else if (!gtk_tree_model_iter_children(model, iter, parent)) {
2445 return FALSE;
2446 }
2447
2448 do {
2449 gtk_tree_model_get(model, iter, CL_COL_CONN_ID, &id, -1);
2450 if (id == search_id) {
2451 return TRUE;
2452 }
2453 } while (gtk_tree_model_iter_next(model, iter));
2454
2455 return FALSE;
2456}
2457
2458/**********************************************************************/
2462{
2464 && get_client_page() == PAGE_START
2465 && connection_list_store != NULL) {
2466 GtkTreeStore *store = connection_list_store;
2467 GtkTreeModel *model = GTK_TREE_MODEL(store);
2468 GtkTreePath *path;
2469 GtkTreeIter child, prev_child, *pprev_child;
2470 GtkTreeIter parent, prev_parent, *pprev_parent = NULL;
2471 GdkPixbuf *flag, *color;
2472 gboolean collapsed;
2473 struct player *pselected_player;
2474 struct connection *pselected_conn;
2475 bool is_ready;
2476 const char *nation, *plr_name, *team;
2477 char name[MAX_LEN_NAME + 8];
2478 enum cmdlevel access_level;
2479 int conn_id;
2480
2481 /* Refresh the AI skill level control */
2482 if (ai_lvl_combobox) {
2483 enum ai_level new_level = server_ai_level(), level;
2484 int i = 0;
2485
2486 for (level = 0; level < AI_LEVEL_COUNT; level++) {
2488 if (level == new_level) {
2489 gtk_combo_box_set_active(GTK_COMBO_BOX(ai_lvl_combobox), i);
2490 break;
2491 }
2492 i++;
2493 }
2494 }
2495 if (level == AI_LEVEL_COUNT) {
2496 /* Probably ai_level_invalid() */
2497 gtk_combo_box_set_active(GTK_COMBO_BOX(ai_lvl_combobox), -1);
2498 }
2499 }
2500
2501 /* Save the selected connection. */
2502 (void) conn_list_selection(&pselected_player, &pselected_conn);
2503
2504 /* Insert players into the connection list. */
2505 players_iterate(pplayer) {
2506 if (!player_has_flag(pplayer, PLRF_SCENARIO_RESERVED)) {
2507 conn_id = -1;
2508 access_level = ALLOW_NONE;
2509 flag = pplayer->nation ? get_flag(pplayer->nation) : NULL;
2510 color = create_player_icon(pplayer);
2511
2512 conn_list_iterate(pplayer->connections, pconn) {
2513 if (pconn->playing == pplayer && !pconn->observer) {
2514 conn_id = pconn->id;
2515 access_level = pconn->access_level;
2516 break;
2517 }
2519
2520 if (is_ai(pplayer) && !pplayer->was_created
2521 && !pplayer->is_connected) {
2522 /* TRANS: "<Novice AI>" */
2523 fc_snprintf(name, sizeof(name), _("<%s AI>"),
2524 ai_level_translated_name(pplayer->ai_common.skill_level));
2525 } else {
2526 sz_strlcpy(name, pplayer->username);
2527 if (access_level > ALLOW_BASIC) {
2528 sz_strlcat(name, "*");
2529 }
2530 }
2531
2532 is_ready = !is_human(pplayer) ? TRUE : pplayer->is_ready;
2533
2534 if (pplayer->nation == NO_NATION_SELECTED) {
2535 nation = _("Random");
2536 if (pplayer->was_created) {
2537 plr_name = player_name(pplayer);
2538 } else {
2539 plr_name = "";
2540 }
2541 } else {
2542 nation = nation_adjective_for_player(pplayer);
2543 plr_name = player_name(pplayer);
2544 }
2545
2546 team = pplayer->team ? team_name_translation(pplayer->team) : "";
2547
2548 if (model_get_player_iter(model, &parent, pprev_parent, pplayer)) {
2549 gtk_tree_store_move_after(store, &parent, pprev_parent);
2550 } else {
2551 gtk_tree_store_insert_after(store, &parent, NULL, pprev_parent);
2552 }
2553
2554 gtk_tree_store_set(store, &parent,
2557 CL_COL_READY_STATE, is_ready,
2558 CL_COL_PLAYER_NAME, plr_name,
2559 CL_COL_FLAG, flag,
2561 CL_COL_NATION, nation,
2563 CL_COL_CONN_ID, conn_id,
2564 CL_COL_STYLE, PANGO_STYLE_NORMAL,
2565 CL_COL_WEIGHT, PANGO_WEIGHT_BOLD,
2566 -1);
2567
2568 /* Insert observers of this player as child nodes. */
2569 pprev_child = NULL;
2570 conn_list_iterate(pplayer->connections, pconn) {
2571 if (pconn->id == conn_id) {
2572 continue;
2573 }
2574 if (model_get_conn_iter(model, &child, &parent,
2575 pprev_child, pconn)) {
2576 gtk_tree_store_move_after(store, &child, pprev_child);
2577 } else {
2578 gtk_tree_store_insert_after(store, &child, &parent, pprev_child);
2579 }
2580
2581 gtk_tree_store_set(store, &child,
2583 CL_COL_USER_NAME, pconn->username,
2584 CL_COL_TEAM, _("Observer"),
2585 CL_COL_CONN_ID, pconn->id,
2586 CL_COL_STYLE, PANGO_STYLE_NORMAL,
2587 CL_COL_WEIGHT, PANGO_WEIGHT_NORMAL,
2588 -1);
2589
2590 prev_child = child;
2591 pprev_child = &prev_child;
2593
2594 /* Expand node? */
2595 if (NULL != pprev_child) {
2596 gtk_tree_model_get(model, &parent, CL_COL_COLLAPSED, &collapsed, -1);
2597 if (!collapsed) {
2598 path = gtk_tree_model_get_path(model, &parent);
2599 gtk_tree_view_expand_row(GTK_TREE_VIEW(connection_list_view),
2600 path, FALSE);
2601 gtk_tree_path_free(path);
2602 }
2603 }
2604
2605 /* Remove trailing rows. */
2606 if (NULL != pprev_child) {
2607 child = prev_child;
2608 if (gtk_tree_model_iter_next(model, &child)) {
2609 while (gtk_tree_store_remove(store, &child)) {
2610 /* Do nothing more. */
2611 }
2612 }
2613 } else if (gtk_tree_model_iter_children(model, &child, &parent)) {
2614 while (gtk_tree_store_remove(store, &child)) {
2615 /* Do nothing more. */
2616 }
2617 }
2618
2619 prev_parent = parent;
2620 pprev_parent = &prev_parent;
2621 if (flag) {
2622 g_object_unref(flag);
2623 }
2624 if (color) {
2625 g_object_unref(color);
2626 }
2627 }
2629
2630 /* Finally, insert global observers... */
2632 if (NULL != pconn->playing || !pconn->observer) {
2633 continue;
2634 }
2635
2636 if (model_get_conn_iter(model, &parent, NULL, pprev_parent, pconn)) {
2637 gtk_tree_store_move_after(store, &parent, pprev_parent);
2638 } else {
2639 gtk_tree_store_insert_after(store, &parent, NULL, pprev_parent);
2640 }
2641
2642 gtk_tree_store_set(store, &parent,
2644 CL_COL_USER_NAME, pconn->username,
2645 CL_COL_TEAM, _("Observer"),
2646 CL_COL_CONN_ID, pconn->id,
2647 CL_COL_STYLE, PANGO_STYLE_NORMAL,
2648 CL_COL_WEIGHT, PANGO_WEIGHT_NORMAL,
2649 -1);
2650
2651 prev_parent = parent;
2652 pprev_parent = &prev_parent;
2654
2655 /* ...and detached connections. */
2657 if (NULL != pconn->playing || pconn->observer) {
2658 continue;
2659 }
2660
2661 if (model_get_conn_iter(model, &parent, NULL, pprev_parent, pconn)) {
2662 gtk_tree_store_move_after(store, &parent, pprev_parent);
2663 } else {
2664 gtk_tree_store_insert_after(store, &parent, NULL, pprev_parent);
2665 }
2666
2667 gtk_tree_store_set(store, &parent,
2669 CL_COL_USER_NAME, pconn->username,
2670 CL_COL_TEAM, _("Detached"),
2671 CL_COL_CONN_ID, pconn->id,
2672 CL_COL_STYLE, PANGO_STYLE_ITALIC,
2673 CL_COL_WEIGHT, PANGO_WEIGHT_NORMAL,
2674 -1);
2675
2676 prev_parent = parent;
2677 pprev_parent = &prev_parent;
2679
2680 /* Remove trailing rows. */
2681 if (NULL != pprev_parent) {
2682 parent = prev_parent;
2683 if (gtk_tree_model_iter_next(model, &parent)) {
2684 while (gtk_tree_store_remove(store, &parent)) {
2685 /* Do nothing more. */
2686 }
2687 }
2688 } else {
2689 gtk_tree_store_clear(store);
2690 }
2691
2692 /* If we were selecting a single connection, let's try to reselect it. */
2693 if (NULL == pselected_player && NULL != pselected_conn) {
2694 conn_list_select_conn(pselected_conn);
2695 }
2696 }
2697
2699}
2700
2701/**********************************************************************/
2706static void add_tree_col(GtkWidget *treeview, GType gtype,
2707 const char *title, int colnum, const char *key)
2708{
2709 GtkCellRenderer *rend;
2710 GtkTreeViewColumn *col;
2711
2712 if (gtype == G_TYPE_BOOLEAN) {
2713 rend = gtk_cell_renderer_toggle_new();
2714 col = gtk_tree_view_column_new_with_attributes(title, rend,
2715 "active", colnum, NULL);
2716 } else if (gtype == GDK_TYPE_PIXBUF) {
2717 rend = gtk_cell_renderer_pixbuf_new();
2718 col = gtk_tree_view_column_new_with_attributes(title, rend,
2719 "pixbuf", colnum, NULL);
2720 } else {
2721 rend = gtk_cell_renderer_text_new();
2722 col = gtk_tree_view_column_new_with_attributes(title, rend,
2723 "text", colnum,
2724 "style", CL_COL_STYLE,
2725 "weight", CL_COL_WEIGHT,
2726 NULL);
2727 }
2728
2729 gtk_tree_view_column_set_sizing(col, GTK_TREE_VIEW_COLUMN_AUTOSIZE);
2730 gtk_tree_view_append_column(GTK_TREE_VIEW(treeview), col);
2731
2732 if (key != NULL) {
2733 g_object_set_data(G_OBJECT(treeview), key, col);
2734 }
2735}
2736
2737/**********************************************************************/
2740GtkWidget *create_start_page(void)
2741{
2742 GtkWidget *box, *sbox, *table, *vgrid;
2743 GtkWidget *view, *sw, *text, *toolkit_view, *button, *spin;
2744 GtkWidget *label;
2745 GtkTreeSelection *selection;
2746 enum ai_level level;
2747 /* There's less than AI_LEVEL_COUNT entries as not all levels have
2748 entries (that's the whole point of this array: index != value),
2749 but this is set safely to the max */
2750 static enum ai_level levels[AI_LEVEL_COUNT];
2751 int i = 0;
2752 int box_row = 0;
2753 int sbox_col = 0;
2754 int grid_row = 0;
2755 GtkGesture *gesture;
2756 GtkEventController *controller;
2757
2758 box = gtk_grid_new();
2759 gtk_orientable_set_orientation(GTK_ORIENTABLE(box),
2760 GTK_ORIENTATION_VERTICAL);
2761 gtk_grid_set_row_spacing(GTK_GRID(box), 8);
2762 gtk_widget_set_margin_start(box, 4);
2763 gtk_widget_set_margin_end(box, 4);
2764 gtk_widget_set_margin_top(box, 4);
2765 gtk_widget_set_margin_bottom(box, 4);
2766
2767 sbox = gtk_grid_new();
2768 gtk_grid_set_column_spacing(GTK_GRID(sbox), 12);
2769 gtk_grid_attach(GTK_GRID(box), sbox, 0, box_row++, 1, 1);
2770
2771 vgrid = gtk_grid_new();
2772 gtk_widget_set_margin_bottom(vgrid, 12);
2773 gtk_widget_set_margin_end(vgrid, 12);
2774 gtk_widget_set_margin_start(vgrid, 12);
2775 gtk_widget_set_margin_top(vgrid, 12);
2776 gtk_orientable_set_orientation(GTK_ORIENTABLE(vgrid),
2777 GTK_ORIENTATION_VERTICAL);
2778 gtk_grid_set_row_spacing(GTK_GRID(vgrid), 2);
2779 gtk_widget_set_halign(vgrid, GTK_ALIGN_CENTER);
2780 gtk_widget_set_valign(vgrid, GTK_ALIGN_CENTER);
2781 gtk_grid_attach(GTK_GRID(sbox), vgrid, sbox_col++, 0, 1, 1);
2782
2783 table = gtk_grid_new();
2784 start_options_table = table;
2785 gtk_grid_set_row_spacing(GTK_GRID(table), 2);
2786 gtk_grid_set_column_spacing(GTK_GRID(table), 12);
2787 gtk_grid_attach(GTK_GRID(vgrid), table, 0, grid_row++, 1, 1);
2788
2789 spin = gtk_spin_button_new_with_range(1, MAX_NUM_PLAYERS, 1);
2790 start_aifill_spin = spin;
2791 gtk_spin_button_set_digits(GTK_SPIN_BUTTON(spin), 0);
2792 gtk_spin_button_set_update_policy(GTK_SPIN_BUTTON(spin),
2793 GTK_UPDATE_IF_VALID);
2794 if (server_optset != NULL) {
2795 struct option *paifill = optset_option_by_name(server_optset, "aifill");
2796 if (paifill) {
2797 gtk_spin_button_set_value(GTK_SPIN_BUTTON(spin),
2798 option_int_get(paifill));
2799 } /* else it'll be updated later */
2800 }
2801 g_signal_connect_after(spin, "value_changed",
2802 G_CALLBACK(ai_fill_changed_by_user), NULL);
2803
2804 gtk_grid_attach(GTK_GRID(table), spin, 1, 0, 1, 1);
2805
2806 label = g_object_new(GTK_TYPE_LABEL,
2807 "use-underline", TRUE,
2808 "mnemonic-widget", spin,
2809 /* TRANS: Keep individual lines short */
2810 "label", _("Number of _Players\n(including AI):"),
2811 "xalign", 0.0,
2812 "yalign", 0.5,
2813 NULL);
2814 gtk_grid_attach(GTK_GRID(table), label, 0, 0, 1, 1);
2815
2816 ai_lvl_combobox = gtk_combo_box_text_new();
2817
2818 for (level = 0; level < AI_LEVEL_COUNT; level++) {
2820 const char *level_name = ai_level_translated_name(level);
2821
2822 gtk_combo_box_text_insert_text(GTK_COMBO_BOX_TEXT(ai_lvl_combobox), i, level_name);
2823 levels[i] = level;
2824 i++;
2825 }
2826 }
2827 gtk_combo_box_set_active(GTK_COMBO_BOX(ai_lvl_combobox), -1);
2828 g_signal_connect(ai_lvl_combobox, "changed",
2829 G_CALLBACK(ai_skill_callback), levels);
2830
2831 gtk_grid_attach(GTK_GRID(table), ai_lvl_combobox, 1, 1, 1, 1);
2832
2833 label = g_object_new(GTK_TYPE_LABEL,
2834 "use-underline", TRUE,
2835 "mnemonic-widget", ai_lvl_combobox,
2836 "label", _("AI Skill _Level:"),
2837 "xalign", 0.0,
2838 "yalign", 0.5,
2839 NULL);
2840 gtk_grid_attach(GTK_GRID(table), label, 0, 1, 1, 1);
2841
2842 ruleset_combo = gtk_combo_box_text_new();
2843 g_signal_connect(G_OBJECT(ruleset_combo), "changed",
2844 G_CALLBACK(ruleset_entry_changed), NULL);
2845
2846 gtk_grid_attach(GTK_GRID(table), ruleset_combo, 1, 2, 1, 1);
2847
2848 label = g_object_new(GTK_TYPE_LABEL,
2849 "use-underline", TRUE,
2850 "mnemonic-widget", GTK_COMBO_BOX_TEXT(ruleset_combo),
2851 "label", _("Ruleset:"),
2852 "xalign", 0.0,
2853 "yalign", 0.5,
2854 NULL);
2855 gtk_grid_attach(GTK_GRID(table), label, 0, 2, 1, 1);
2856
2857 button = icon_label_button_new("preferences-system",
2858 _("_More Game Options..."));
2859 gtk_widget_set_margin_bottom(button, 8);
2860 gtk_widget_set_margin_end(button, 8);
2861 gtk_widget_set_margin_start(button, 8);
2862 gtk_widget_set_margin_top(button, 8);
2863 gtk_widget_set_halign(button, GTK_ALIGN_CENTER);
2864 gtk_widget_set_valign(button, GTK_ALIGN_CENTER);
2865 g_signal_connect(button, "clicked",
2866 G_CALLBACK(game_options_callback), NULL);
2867 gtk_grid_attach(GTK_GRID(vgrid), button, 0, grid_row++, 1, 1);
2868
2870 view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(connection_list_store));
2871 gtk_widget_set_hexpand(view, TRUE);
2872 g_object_unref(G_OBJECT(connection_list_store));
2873 gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(view), TRUE);
2874 connection_list_view = GTK_TREE_VIEW(view);
2875
2876 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(view));
2877 gtk_tree_selection_set_mode(selection, GTK_SELECTION_SINGLE);
2878 g_signal_connect(selection, "changed",
2879 G_CALLBACK(update_start_page_buttons), NULL);
2880
2881 add_tree_col(view, G_TYPE_STRING, _("Name"),
2882 CL_COL_USER_NAME, NULL);
2883 add_tree_col(view, G_TYPE_BOOLEAN, _("Ready"),
2884 CL_COL_READY_STATE, NULL);
2885 add_tree_col(view, G_TYPE_STRING, Q_("?player:Leader"),
2886 CL_COL_PLAYER_NAME, NULL);
2887 add_tree_col(view, GDK_TYPE_PIXBUF, _("Flag"),
2888 CL_COL_FLAG, NULL);
2889 add_tree_col(view, GDK_TYPE_PIXBUF, _("Border"),
2890 CL_COL_COLOR, NULL);
2891 add_tree_col(view, G_TYPE_STRING, _("Nation"),
2892 CL_COL_NATION, NULL);
2893 add_tree_col(view, G_TYPE_STRING, _("Team"),
2894 CL_COL_TEAM, NULL);
2895
2896 controller = GTK_EVENT_CONTROLLER(gtk_gesture_click_new());
2897 g_signal_connect(controller, "pressed",
2898 G_CALLBACK(connect_list_left_button), NULL);
2899 gtk_widget_add_controller(view, controller);
2900
2901 g_signal_connect(view, "row-collapsed",
2902 G_CALLBACK(connection_list_row_callback),
2903 GINT_TO_POINTER(TRUE));
2904 g_signal_connect(view, "row-expanded",
2905 G_CALLBACK(connection_list_row_callback),
2906 GINT_TO_POINTER(FALSE));
2907
2908 sw = gtk_scrolled_window_new();
2909 gtk_scrolled_window_set_has_frame(GTK_SCROLLED_WINDOW(sw), TRUE);
2910 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(sw),
2911 GTK_POLICY_AUTOMATIC,
2912 GTK_POLICY_ALWAYS);
2913 gtk_scrolled_window_set_min_content_height(GTK_SCROLLED_WINDOW(sw), 200);
2914 gtk_scrolled_window_set_child(GTK_SCROLLED_WINDOW(sw), view);
2915
2916 /* This is for a workaround against gtk bug that we can't parent
2917 * popup to the tree view - we have to parent it all the way to sw */
2918 gesture = gtk_gesture_click_new();
2919 gtk_gesture_single_set_button(GTK_GESTURE_SINGLE(gesture), 3);
2920 controller = GTK_EVENT_CONTROLLER(gesture);
2921 g_signal_connect(controller, "pressed",
2922 G_CALLBACK(connect_list_right_button), sw);
2923 gtk_widget_add_controller(view, controller);
2924
2925 gtk_grid_attach(GTK_GRID(sbox), sw, sbox_col++, 0, 1, 1);
2926
2927 sw = gtk_scrolled_window_new();
2928 gtk_scrolled_window_set_has_frame(GTK_SCROLLED_WINDOW(sw), TRUE);
2929 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(sw),
2930 GTK_POLICY_AUTOMATIC,
2931 GTK_POLICY_ALWAYS);
2932 gtk_grid_attach(GTK_GRID(box), sw, 0, box_row++, 1, 1);
2933
2934 text = gtk_text_view_new_with_buffer(message_buffer);
2935 gtk_widget_set_hexpand(text, TRUE);
2936 gtk_widget_set_vexpand(text, TRUE);
2937 start_message_area = text;
2938 gtk_widget_set_name(text, "chatline");
2939 gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(text), GTK_WRAP_WORD);
2940 gtk_text_view_set_left_margin(GTK_TEXT_VIEW(text), 5);
2941 gtk_text_view_set_editable(GTK_TEXT_VIEW(text), FALSE);
2942 gtk_scrolled_window_set_child(GTK_SCROLLED_WINDOW(sw), text);
2943
2944
2945 /* Vote widgets. */
2946 if (pregame_votebar == NULL) {
2948 }
2949 gtk_grid_attach(GTK_GRID(box), pregame_votebar, 0, box_row++, 1, 1);
2950
2951 toolkit_view = inputline_toolkit_view_new();
2952 gtk_grid_attach(GTK_GRID(box), toolkit_view, 0, box_row++, 1, 1);
2953
2954 button = gtk_button_new_with_mnemonic(_("_Cancel"));
2955 inputline_toolkit_view_append_button(toolkit_view, button);
2956 g_signal_connect(button, "clicked", G_CALLBACK(main_callback), NULL);
2957
2958 nation_button = icon_label_button_new("document-properties",
2959 _("Pick _Nation"));
2961 g_signal_connect(nation_button, "clicked",
2962 G_CALLBACK(nation_button_callback), NULL);
2963
2964 observe_button = icon_label_button_new("zoom-in", _("_Observe"));
2966 g_signal_connect(observe_button, "clicked",
2967 G_CALLBACK(observe_button_callback), NULL);
2968
2969 ready_button = icon_label_button_new("system-run", _("_Ready"));
2971 g_signal_connect(ready_button, "clicked",
2972 G_CALLBACK(ready_button_callback), NULL);
2973
2974 return box;
2975}
2976
2977
2978/**********************************************************************/
2981void handle_game_load(bool load_successful, const char *filename)
2982{
2983 if (load_successful) {
2984 set_client_page(PAGE_START);
2985
2986 if (game.info.is_new_game) {
2987 /* It's pregame. Create a player and connect to it */
2988 send_chat("/take -");
2989 }
2990 }
2991}
2992
2993/**********************************************************************/
2996static void load_filename(const char *filename)
2997{
2998 send_chat_printf("/load %s", filename);
2999}
3000
3001/**********************************************************************/
3004static void load_callback(void)
3005{
3006 GtkTreeIter it;
3007 const gchar *filename;
3008
3009 if (!gtk_tree_selection_get_selected(load_selection, NULL, &it)) {
3010 return;
3011 }
3012
3013 gtk_tree_model_get(GTK_TREE_MODEL(load_store), &it,
3014 SD_COL_FULL_PATH, &filename, -1);
3015 load_filename(filename);
3016}
3017
3018/**********************************************************************/
3021static void load_browse_callback(GtkWidget *w, gpointer data)
3022{
3023 save_dialog_file_chooser_popup(_("Choose Saved Game to Load"),
3024 GTK_FILE_CHOOSER_ACTION_OPEN,
3026}
3027
3028/**********************************************************************/
3031static void update_load_page(void)
3032{
3033 /* Search for user saved games. */
3034 struct fileinfo_list *files = fileinfolist_infix(get_save_dirs(),
3035 ".sav", FALSE);
3036
3038 fileinfo_list_destroy(files);
3039}
3040
3041/**********************************************************************/
3044GtkWidget *create_load_page(void)
3045{
3046 GtkWidget *box, *sbox, *bbox;
3047
3048 GtkWidget *button, *label, *view, *sw;
3049 GtkCellRenderer *rend;
3050 int box_row = 0;
3051 int sbox_row = 0;
3052
3053 box = gtk_grid_new();
3054 gtk_orientable_set_orientation(GTK_ORIENTABLE(box),
3055 GTK_ORIENTATION_VERTICAL);
3056 gtk_grid_set_row_spacing(GTK_GRID(box), 18);
3057 gtk_widget_set_margin_start(box, 4);
3058 gtk_widget_set_margin_end(box, 4);
3059 gtk_widget_set_margin_top(box, 4);
3060 gtk_widget_set_margin_bottom(box, 4);
3061
3063 view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(load_store));
3064 gtk_widget_set_vexpand(view, TRUE);
3065 g_object_unref(load_store);
3066
3067 rend = gtk_cell_renderer_text_new();
3068 gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(view),
3069 -1, NULL, rend, "text", 0, NULL);
3070
3071 load_selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(view));
3072 gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(view), FALSE);
3073
3074 gtk_tree_selection_set_mode(load_selection, GTK_SELECTION_SINGLE);
3075
3076 g_signal_connect(view, "row-activated",
3077 G_CALLBACK(load_callback), NULL);
3078
3079 sbox = gtk_grid_new();
3080 gtk_widget_set_halign(sbox, GTK_ALIGN_CENTER);
3081 gtk_orientable_set_orientation(GTK_ORIENTABLE(sbox),
3082 GTK_ORIENTATION_VERTICAL);
3083 gtk_grid_set_row_spacing(GTK_GRID(sbox), 2);
3084 gtk_grid_attach(GTK_GRID(box), sbox, 0, box_row++, 1, 1);
3085
3086 label = g_object_new(GTK_TYPE_LABEL,
3087 "use-underline", TRUE,
3088 "mnemonic-widget", view,
3089 "label", _("Choose Saved Game to _Load:"),
3090 "xalign", 0.0,
3091 "yalign", 0.5,
3092 NULL);
3093 gtk_grid_attach(GTK_GRID(sbox), label, 0, sbox_row++, 1, 1);
3094
3095 sw = gtk_scrolled_window_new();
3096 gtk_scrolled_window_set_min_content_width(GTK_SCROLLED_WINDOW(sw), 300);
3097 gtk_scrolled_window_set_has_frame(GTK_SCROLLED_WINDOW(sw), TRUE);
3098 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(sw), GTK_POLICY_AUTOMATIC,
3099 GTK_POLICY_AUTOMATIC);
3100 gtk_scrolled_window_set_child(GTK_SCROLLED_WINDOW(sw), view);
3101 gtk_grid_attach(GTK_GRID(sbox), sw, 0, sbox_row++, 1, 1);
3102
3103 bbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 2);
3104 gtk_widget_set_hexpand(bbox, TRUE);
3105 gtk_box_set_spacing(GTK_BOX(bbox), 12);
3106 gtk_grid_attach(GTK_GRID(box), bbox, 0, box_row++, 1, 1);
3107
3108 button = gtk_button_new_with_mnemonic(_("_Browse..."));
3109 gtk_box_append(GTK_BOX(bbox), button);
3110 g_signal_connect(button, "clicked",
3111 G_CALLBACK(load_browse_callback), NULL);
3112
3113 button = gtk_button_new_with_mnemonic(_("_Cancel"));
3114 gtk_box_append(GTK_BOX(bbox), button);
3115 g_signal_connect(button, "clicked",
3116 G_CALLBACK(main_callback), NULL);
3117
3118 button = gtk_button_new_with_mnemonic(_("_OK"));
3119 gtk_box_append(GTK_BOX(bbox), button);
3120 g_signal_connect(button, "clicked",
3121 G_CALLBACK(load_callback), NULL);
3122
3123 return box;
3124}
3125
3126/**********************************************************************/
3129static void scenario_list_callback(void)
3130{
3131 GtkTreeIter it;
3132 GtkTextBuffer *buffer;
3133 char *description;
3134 char *authors;
3135 char *filename;
3136 int ver;
3137 char vername[50];
3138
3139 if (gtk_tree_selection_get_selected(scenario_selection, NULL, &it)) {
3140 gtk_tree_model_get(GTK_TREE_MODEL(scenario_store), &it,
3141 2, &description, -1);
3142 gtk_tree_model_get(GTK_TREE_MODEL(scenario_store), &it,
3143 3, &authors, -1);
3144 gtk_tree_model_get(GTK_TREE_MODEL(scenario_store), &it,
3145 1, &filename, -1);
3146 gtk_tree_model_get(GTK_TREE_MODEL(scenario_store), &it,
3147 4, &ver, -1);
3148 filename = skip_to_basename(filename);
3149 if (ver > 0) {
3150 int maj;
3151 int min;
3152
3153 maj = ver / 1000000;
3154 ver %= 1000000;
3155 min = ver / 10000;
3156 ver %= 10000;
3157 if (ver >= 9000) {
3158 /* Development version, have '+' */
3159 fc_snprintf(vername, sizeof(vername), "%d.%d+", maj, min);
3160 } else {
3161 fc_snprintf(vername, sizeof(vername), "%d.%d", maj, min);
3162 }
3163 } else {
3164 /* TRANS: Old scenario format version */
3165 fc_snprintf(vername, sizeof(vername), _("pre-2.6"));
3166 }
3167 } else {
3168 description = "";
3169 authors = "";
3170 filename = "";
3171 vername[0] = '\0';
3172 }
3173
3174 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(scenario_description));
3175 gtk_text_buffer_set_text(buffer, description, -1);
3176 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(scenario_authors));
3177 gtk_text_buffer_set_text(buffer, authors, -1);
3178 gtk_label_set_text(GTK_LABEL(scenario_filename), filename);
3179 gtk_label_set_text(GTK_LABEL(scenario_version), vername);
3180}
3181
3182/**********************************************************************/
3185static void scenario_callback(void)
3186{
3187 GtkTreeIter it;
3188 char *filename;
3189
3190 if (!gtk_tree_selection_get_selected(scenario_selection, NULL, &it)) {
3191 return;
3192 }
3193
3194 gtk_tree_model_get(GTK_TREE_MODEL(scenario_store), &it, 1, &filename, -1);
3195 load_filename(filename);
3196}
3197
3198/**********************************************************************/
3201static void scenario_browse_callback(GtkWidget *w, gpointer data)
3202{
3203 save_dialog_file_chooser_popup(_("Choose a Scenario"),
3204 GTK_FILE_CHOOSER_ACTION_OPEN,
3206}
3207
3208/**********************************************************************/
3211static void update_scenario_page(void)
3212{
3213 struct fileinfo_list *files;
3214
3215 gtk_list_store_clear(scenario_store);
3216
3217 /* search for scenario files. */
3218 files = fileinfolist_infix(get_scenario_dirs(), ".sav", TRUE);
3219 fileinfo_list_iterate(files, pfile) {
3220 struct section_file *sf;
3221
3222 if ((sf = secfile_load_section(pfile->fullname, "scenario", TRUE))
3223 && secfile_lookup_bool_default(sf, TRUE, "scenario.is_scenario")) {
3224 const char *sname, *sdescription, *sauthors;
3225 int fcver;
3226 int fcdev;
3227 int current_ver = MAJOR_VERSION * 1000000 + MINOR_VERSION * 10000;
3228 int current_dev;
3229
3230 current_dev = current_ver;
3231 if (PATCH_VERSION >= 90) {
3232 /* Patch level matters on development versions */
3233 current_dev += PATCH_VERSION * 100;
3234 }
3235
3236 fcver = secfile_lookup_int_default(sf, 0, "scenario.game_version");
3237 if (fcver < 30000) {
3238 /* Pre-3.0 versions stored version number without emergency version
3239 * part in the end. To get comparable version number stored,
3240 * multiply by 100. */
3241 fcver *= 100;
3242 }
3243 if (fcver % 10000 >= 9000) {
3244 fcdev = fcver - (fcver % 100); /* Emergency version does not count. */
3245 } else {
3246 fcdev = fcver - (fcver % 10000); /* Patch version does not count. */
3247 }
3248 sname = secfile_lookup_str_default(sf, NULL, "scenario.name");
3249 sdescription = secfile_lookup_str_default(sf, NULL,
3250 "scenario.description");
3251 sauthors = secfile_lookup_str_default(sf, NULL, "scenario.authors");
3252 log_debug("scenario file: %s from %s", sname, pfile->fullname);
3253
3254 /* Ignore scenarios for newer freeciv versions than we are. */
3255 if (fcdev <= current_dev) {
3256 bool add_new = TRUE;
3257
3258 if (sname != NULL) {
3259 GtkTreeIter it;
3260 bool valid;
3261
3262 valid = gtk_tree_model_get_iter_first(GTK_TREE_MODEL(scenario_store), &it);
3263 while (valid) {
3264 char *oname;
3265
3266 gtk_tree_model_get(GTK_TREE_MODEL(scenario_store), &it,
3267 0, &oname, -1);
3268
3269 if (!strcmp(sname, oname)) {
3270 /* Already listed scenario has the same name as the one we just found */
3271 int existing;
3272
3273 gtk_tree_model_get(GTK_TREE_MODEL(scenario_store), &it,
3274 4, &existing, -1);
3275 log_debug("Duplicate %s (%d vs %d)", sname, existing, fcver);
3276
3277 if (existing > fcver) {
3278 /* Already listed one has higher version number */
3279 add_new = FALSE;
3280 } else if (existing < fcver) {
3281 /* New one has higher version number */
3282 add_new = FALSE;
3283
3284 gtk_list_store_set(scenario_store, &it,
3285 0, sname && strlen(sname) ? Q_(sname) : pfile->name,
3286 1, pfile->fullname,
3287 2, (NULL != sdescription && '\0' != sdescription[0]
3288 ? Q_(sdescription) : ""),
3289 3, (NULL != sauthors && sauthors[0] != '\0'
3290 ? Q_(sauthors) : ""),
3291 4, fcver,
3292 -1);
3293 } else {
3294 /* Same version number -> list both */
3295 }
3296 }
3297 valid = gtk_tree_model_iter_next(GTK_TREE_MODEL(scenario_store), &it);
3298 }
3299 }
3300
3301 if (add_new) {
3302 GtkTreeIter it;
3303
3304 gtk_list_store_append(scenario_store, &it);
3305 gtk_list_store_set(scenario_store, &it,
3306 0, sname && strlen(sname) ? Q_(sname) : pfile->name,
3307 1, pfile->fullname,
3308 2, (NULL != sdescription && '\0' != sdescription[0]
3309 ? Q_(sdescription) : ""),
3310 3, (NULL != sauthors && sauthors[0] != '\0'
3311 ? Q_(sauthors) : ""),
3312 4, fcver,
3313 -1);
3314 }
3315 }
3316
3317 secfile_destroy(sf);
3318 }
3320
3321 fileinfo_list_destroy(files);
3322}
3323
3324/**********************************************************************/
3327GtkWidget *create_scenario_page(void)
3328{
3329 GtkWidget *vgrid, *hbox, *sbox, *bbox, *filenamebox, *descbox;
3330 GtkWidget *versionbox, *vertext;
3331 GtkWidget *button, *label, *view, *sw, *swa, *text;
3332 GtkCellRenderer *rend;
3333 int grid_row = 0;
3334 int filenamecol = 0;
3335 int vercol = 0;
3336 int descrow = 0;
3337
3338 vgrid = gtk_grid_new();
3339 gtk_orientable_set_orientation(GTK_ORIENTABLE(vgrid),
3340 GTK_ORIENTATION_VERTICAL);
3341 gtk_grid_set_row_spacing(GTK_GRID(vgrid), 18);
3342 gtk_widget_set_margin_start(vgrid, 4);
3343 gtk_widget_set_margin_end(vgrid, 4);
3344 gtk_widget_set_margin_top(vgrid, 4);
3345 gtk_widget_set_margin_bottom(vgrid, 4);
3346
3347 scenario_store = gtk_list_store_new(5, G_TYPE_STRING,
3348 G_TYPE_STRING,
3349 G_TYPE_STRING,
3350 G_TYPE_STRING,
3351 G_TYPE_INT);
3352 view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(scenario_store));
3353 gtk_widget_set_hexpand(view, TRUE);
3354 gtk_widget_set_vexpand(view, TRUE);
3355 g_object_unref(scenario_store);
3356
3357 rend = gtk_cell_renderer_text_new();
3358 gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(view),
3359 -1, NULL, rend, "text", 0, NULL);
3360
3361 scenario_selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(view));
3362 g_signal_connect(scenario_selection, "changed",
3363 G_CALLBACK(scenario_list_callback), NULL);
3364 gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(view), FALSE);
3365
3366 gtk_tree_selection_set_mode(scenario_selection, GTK_SELECTION_SINGLE);
3367
3368 g_signal_connect(view, "row-activated",
3369 G_CALLBACK(scenario_callback), NULL);
3370
3371 label = g_object_new(GTK_TYPE_LABEL,
3372 "use-underline", TRUE,
3373 "mnemonic-widget", view,
3374 "label", _("Choose a _Scenario:"),
3375 "xalign", 0.0,
3376 "yalign", 0.5,
3377 NULL);
3378 gtk_grid_attach(GTK_GRID(vgrid), label, 0, grid_row++, 1, 1);
3379
3380 sbox = gtk_grid_new();
3381 gtk_grid_set_column_spacing(GTK_GRID(sbox), 12);
3382 gtk_grid_set_row_homogeneous(GTK_GRID(sbox), TRUE);
3383 gtk_orientable_set_orientation(GTK_ORIENTABLE(sbox),
3384 GTK_ORIENTATION_VERTICAL);
3385 gtk_grid_set_row_spacing(GTK_GRID(sbox), 2);
3386 gtk_grid_attach(GTK_GRID(vgrid), sbox, 0, grid_row++, 1, 1);
3387
3388 hbox = gtk_grid_new();
3389 gtk_grid_set_column_homogeneous(GTK_GRID(hbox), TRUE);
3390 gtk_grid_set_column_spacing(GTK_GRID(hbox), 12);
3391
3392 sw = gtk_scrolled_window_new();
3393 gtk_scrolled_window_set_has_frame(GTK_SCROLLED_WINDOW(sw), TRUE);
3394 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(sw), GTK_POLICY_AUTOMATIC,
3395 GTK_POLICY_AUTOMATIC);
3396 gtk_scrolled_window_set_child(GTK_SCROLLED_WINDOW(sw), view);
3397 gtk_grid_attach(GTK_GRID(sbox), sw, 0, 0, 1, 2);
3398
3399 text = gtk_text_view_new();
3400 gtk_widget_set_hexpand(text, TRUE);
3401 gtk_widget_set_vexpand(text, TRUE);
3402 gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(text), GTK_WRAP_WORD);
3403 gtk_text_view_set_left_margin(GTK_TEXT_VIEW(text), 2);
3404 gtk_text_view_set_editable(GTK_TEXT_VIEW(text), FALSE);
3405 scenario_description = text;
3406
3407 sw = gtk_scrolled_window_new();
3408 gtk_scrolled_window_set_has_frame(GTK_SCROLLED_WINDOW(sw), TRUE);
3409 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(sw), GTK_POLICY_AUTOMATIC,
3410 GTK_POLICY_AUTOMATIC);
3411 gtk_scrolled_window_set_child(GTK_SCROLLED_WINDOW(sw), text);
3412
3413 text = gtk_text_view_new();
3414 gtk_widget_set_hexpand(text, TRUE);
3415 gtk_widget_set_vexpand(text, TRUE);
3416 gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(text), GTK_WRAP_WORD);
3417 gtk_text_view_set_left_margin(GTK_TEXT_VIEW(text), 2);
3418 gtk_text_view_set_editable(GTK_TEXT_VIEW(text), FALSE);
3419 scenario_authors = text;
3420
3421 swa = gtk_scrolled_window_new();
3422 gtk_scrolled_window_set_has_frame(GTK_SCROLLED_WINDOW(swa), TRUE);
3423 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(swa), GTK_POLICY_AUTOMATIC,
3424 GTK_POLICY_AUTOMATIC);
3425 gtk_scrolled_window_set_child(GTK_SCROLLED_WINDOW(swa), text);
3426
3427 text = gtk_label_new(_("Filename:"));
3428 scenario_filename = gtk_label_new("");
3429 gtk_widget_set_halign(scenario_filename, GTK_ALIGN_START);
3430 gtk_widget_set_valign(scenario_filename, GTK_ALIGN_CENTER);
3431 gtk_label_set_selectable(GTK_LABEL(scenario_filename), TRUE);
3432
3433 filenamebox = gtk_grid_new();
3434 gtk_grid_set_column_spacing(GTK_GRID(hbox), 12);
3435 gtk_widget_set_margin_bottom(filenamebox, 5);
3436 gtk_widget_set_margin_end(filenamebox, 5);
3437 gtk_widget_set_margin_start(filenamebox, 5);
3438 gtk_widget_set_margin_top(filenamebox, 5);
3439
3440 gtk_grid_attach(GTK_GRID(filenamebox), text, filenamecol++, 0, 1, 1);
3441 gtk_grid_attach(GTK_GRID(filenamebox), scenario_filename,
3442 filenamecol++, 0, 1, 1);
3443
3444 /* TRANS: Scenario format version */
3445 vertext = gtk_label_new(_("Format:"));
3446 scenario_version = gtk_label_new("");
3447 gtk_widget_set_halign(scenario_version, GTK_ALIGN_START);
3448 gtk_widget_set_valign(scenario_version, GTK_ALIGN_CENTER);
3449 gtk_label_set_selectable(GTK_LABEL(scenario_version), TRUE);
3450
3451 versionbox = gtk_grid_new();
3452 gtk_grid_set_column_spacing(GTK_GRID(hbox), 12);
3453 gtk_widget_set_margin_bottom(versionbox, 5);
3454 gtk_widget_set_margin_end(versionbox, 5);
3455 gtk_widget_set_margin_start(versionbox, 5);
3456 gtk_widget_set_margin_top(versionbox, 5);
3457
3458 gtk_grid_attach(GTK_GRID(versionbox), vertext, vercol++, 0, 1, 1);
3459 gtk_grid_attach(GTK_GRID(versionbox), scenario_version,
3460 vercol++, 0, 1, 1);
3461
3462 descbox = gtk_grid_new();
3463 gtk_orientable_set_orientation(GTK_ORIENTABLE(descbox),
3464 GTK_ORIENTATION_VERTICAL);
3465 gtk_grid_set_row_spacing(GTK_GRID(descbox), 6);
3466 gtk_grid_attach(GTK_GRID(descbox), sw, 0, descrow++, 1, 1);
3467 gtk_grid_attach(GTK_GRID(descbox), swa, 0, descrow++, 1, 1);
3468 gtk_grid_attach(GTK_GRID(descbox), filenamebox,
3469 0, descrow++, 1, 1);
3470 gtk_grid_attach(GTK_GRID(descbox), versionbox,
3471 0, descrow++, 1, 1);
3472 gtk_grid_attach(GTK_GRID(sbox), descbox, 1, 0, 1, 2);
3473
3474 bbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 2);
3475 gtk_box_set_spacing(GTK_BOX(bbox), 12);
3476 gtk_grid_attach(GTK_GRID(vgrid), bbox, 0, grid_row++, 1, 1);
3477
3478 button = gtk_button_new_with_mnemonic(_("_Browse..."));
3479 gtk_box_append(GTK_BOX(bbox), button);
3480 g_signal_connect(button, "clicked",
3481 G_CALLBACK(scenario_browse_callback), NULL);
3482
3483 button = gtk_button_new_with_mnemonic(_("_Cancel"));
3484 gtk_box_append(GTK_BOX(bbox), button);
3485 g_signal_connect(button, "clicked",
3486 G_CALLBACK(main_callback), NULL);
3487
3488 button = gtk_button_new_with_mnemonic(_("_OK"));
3489 gtk_box_append(GTK_BOX(bbox), button);
3490 g_signal_connect(button, "clicked",
3491 G_CALLBACK(scenario_callback), NULL);
3492
3493 return vgrid;
3494}
3495
3496/**********************************************************************/
3499enum client_pages get_current_client_page(void)
3500{
3501 return current_page;
3502}
3503
3504/**********************************************************************/
3507void real_set_client_page(enum client_pages new_page)
3508{
3509 /* Don't use current_page directly here because maybe it could be modified
3510 * before we reach the end of this function. */
3511 enum client_pages old_page = current_page;
3512
3513 /* If the page remains the same, don't do anything. */
3514 if (old_page == new_page) {
3515 return;
3516 }
3517
3518 log_debug("Switching client page from %s to %s.",
3519 -1 == old_page ? "(no page)" : client_pages_name(old_page),
3520 client_pages_name(new_page));
3521
3522 current_page = new_page;
3523
3524 switch (old_page) {
3525 case PAGE_SCENARIO:
3526 case PAGE_LOAD:
3527 break;
3528 case PAGE_GAME:
3529 {
3531
3533 if (vmode == NULL) {
3534 gtk_window_unmaximize(GTK_WINDOW(toplevel));
3535 }
3536 }
3537 break;
3538 default:
3539 break;
3540 }
3541
3542 switch (new_page) {
3543 case PAGE_MAIN:
3544 case PAGE_START:
3545 if (is_server_running()) {
3546 if (game.info.is_new_game) {
3547 gtk_widget_show(start_options_table);
3548 } else {
3549 gtk_widget_hide(start_options_table);
3550 }
3552 } else {
3553 gtk_widget_hide(start_options_table);
3554 }
3558 break;
3559 case PAGE_GAME:
3560 {
3562
3565 if (vmode == NULL) {
3566 gtk_window_maximize(GTK_WINDOW(toplevel));
3567 }
3570 }
3571 break;
3572 case PAGE_LOAD:
3574 break;
3575 case PAGE_SCENARIO:
3577 break;
3578 case PAGE_NETWORK:
3580 break;
3581 }
3582
3583 /* hide/show statusbar. */
3584 if (new_page == PAGE_START || new_page == PAGE_GAME) {
3586 gtk_widget_hide(statusbar_frame);
3587 } else {
3588 gtk_widget_show(statusbar_frame);
3589 }
3590
3591 gtk_notebook_set_current_page(GTK_NOTEBOOK(toplevel_tabs), new_page);
3592
3593 /* Update the GUI. */
3594 while (g_main_context_pending(NULL)) {
3595 g_main_context_iteration(NULL, FALSE);
3596 }
3597
3598 switch (new_page) {
3599 case PAGE_MAIN:
3600 break;
3601 case PAGE_START:
3604 break;
3605 case PAGE_LOAD:
3606 gtk_tree_view_focus(gtk_tree_selection_get_tree_view(load_selection));
3607 break;
3608 case PAGE_SCENARIO:
3609 gtk_tree_view_focus(gtk_tree_selection_get_tree_view(scenario_selection));
3610 break;
3611 case PAGE_GAME:
3615 mapview_thaw();
3618 break;
3619 case PAGE_NETWORK:
3621 gtk_widget_grab_focus(network_login);
3622 gtk_editable_set_position(GTK_EDITABLE(network_login), 0);
3624 break;
3625 }
3626}
3627
3628/****************************************************************************
3629 SAVE GAME DIALOGs
3630****************************************************************************/
3631
3632/**********************************************************************/
3635static struct fileinfo_list *save_dialog_savegame_list(void)
3636{
3637 return fileinfolist_infix(get_save_dirs(), ".sav", FALSE);
3638}
3639
3640/**********************************************************************/
3644{
3645 static GtkWidget *shell = NULL;
3646
3647 if (NULL != shell) {
3648 return;
3649 }
3650
3651 shell = save_dialog_new(_("Save Game"), _("Saved _Games:"),
3652 _("Save _Filename:"), send_save_game,
3654 g_signal_connect(shell, "destroy", G_CALLBACK(widget_destroyed),
3655 &shell);
3656 gtk_window_present(GTK_WINDOW(shell));
3657}
3658
3659/**********************************************************************/
3662static void save_dialog_save_scenario(const char *filename)
3663{
3665}
3666
3667/**********************************************************************/
3670static struct fileinfo_list *save_dialog_scenario_list(void)
3671{
3672 return fileinfolist_infix(get_scenario_dirs(), ".sav", FALSE);
3673}
3674
3675/**********************************************************************/
3679{
3680 static GtkWidget *shell = NULL;
3681
3682 if (NULL != shell) {
3683 return;
3684 }
3685
3686 shell = save_dialog_new(_("Save Scenario"), _("Saved Sce_narios:"),
3687 _("Save Sc_enario:"), save_dialog_save_scenario,
3689 g_signal_connect(shell, "destroy", G_CALLBACK(widget_destroyed),
3690 &shell);
3691 gtk_window_present(GTK_WINDOW(shell));
3692}
3693
3694/**********************************************************************/
3699static struct fileinfo_list *save_dialog_mapimg_list(void)
3700{
3701 return fileinfolist_infix(get_save_dirs(), ".map", FALSE);
3702}
3703
3704/**********************************************************************/
3708{
3709 static GtkWidget *shell = NULL;
3710
3711 if (NULL != shell) {
3712 return;
3713 }
3714
3715 shell = save_dialog_new(_("Save Map Image"), _("Saved Map _Images:"),
3716 _("Save _Map Images:"), mapimg_client_save,
3718 g_signal_connect(shell, "destroy", G_CALLBACK(widget_destroyed),
3719 &shell);
3720 gtk_window_present(GTK_WINDOW(shell));
3721}
3722
3723/**********************************************************************/
3726void mapimg_client_save(const char *filename)
3727{
3728 if (!mapimg_client_createmap(filename)) {
3729 char msg[512];
3730
3731 fc_snprintf(msg, sizeof(msg), "(%s)", mapimg_error());
3732 popup_notify_dialog(_("Error"),
3733 _("Error Creating the Map Image!"), msg);
3734 }
3735}
3736
3737/**********************************************************************/
3742void set_rulesets(int num_rulesets, char **rulesets)
3743{
3744 int i;
3745 int def_idx = -1;
3746
3747 gtk_combo_box_text_remove_all(GTK_COMBO_BOX_TEXT(ruleset_combo));
3748 for (i = 0; i < num_rulesets; i++) {
3749
3750 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(ruleset_combo), rulesets[i]);
3751 if (!strcmp("default", rulesets[i])) {
3752 def_idx = i;
3753 }
3754 }
3755
3757
3758 /* HACK: server should tell us the current ruleset. */
3759 gtk_combo_box_set_active(GTK_COMBO_BOX(ruleset_combo), def_idx);
3760
3762}
#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
#define SERVER_COMMAND_PREFIX_STR
Definition chat.h:29
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
#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 add_idle_callback(void(callback)(void *), void *data)
Definition gui_main.c:2210
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 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
gboolean terminate_signal_processing(GtkEventControllerFocus *controller, gpointer data)
Definition gui_main.c:995
void animation_idle_cb(void *data)
Definition gui_main.c:2372
void main_message_area_resize(void *data)
Definition gui_main.c:334
void widget_destroyed(GtkWidget *wdg, void *data)
Definition gui_stuff.c:1145
#define menu_item_append_unref(menu, item)
Definition gui_stuff.h:149
static gboolean connect_list_right_button(GtkGestureClick *gesture, int n_press, double x, double y, gpointer data)
Definition pages.c:2107
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 void close_conn_menu_popover(void)
Definition pages.c:1718
static gboolean connect_list_left_button(GtkGestureClick *gesture, int n_press, double x, double y, gpointer data)
Definition pages.c:2078
static GtkWidget * conn_popover
Definition pages.c:97
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
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
struct team * team_slot_get_team(const struct team_slot *tslot)
Definition team.c:150
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