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