Qt client - remove right click menu from end turn sidebar
[freeciv.git] / client / gui-gtk-3.0 / dialogs.c
blobc7e169732888a501712c6892d5b603782ea57770
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.
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 ***********************************************************************/
14 #ifdef HAVE_CONFIG_H
15 #include <fc_config.h>
16 #endif
18 #include <stdarg.h>
19 #include <stdio.h>
20 #include <stdlib.h>
21 #include <string.h>
23 #include <gtk/gtk.h>
24 #include <gdk/gdkkeysyms.h>
26 /* utility */
27 #include "astring.h"
28 #include "bitvector.h"
29 #include "fcintl.h"
30 #include "log.h"
31 #include "mem.h"
32 #include "rand.h"
33 #include "support.h"
35 /* common */
36 #include "game.h"
37 #include "government.h"
38 #include "map.h"
39 #include "packets.h"
40 #include "player.h"
42 /* client */
43 #include "client_main.h"
44 #include "climisc.h"
45 #include "connectdlg_common.h"
46 #include "control.h"
47 #include "helpdata.h" /* for helptext_nation() */
48 #include "goto.h"
49 #include "options.h"
50 #include "packhand.h"
51 #include "text.h"
52 #include "tilespec.h"
54 /* client/gui-gtk-3.0 */
55 #include "chatline.h"
56 #include "choice_dialog.h"
57 #include "citydlg.h"
58 #include "editprop.h"
59 #include "graphics.h"
60 #include "gui_main.h"
61 #include "gui_stuff.h"
62 #include "mapview.h"
63 #include "plrdlg.h"
64 #include "wldlg.h"
65 #include "unitselect.h"
67 #include "dialogs.h"
69 /******************************************************************/
70 static GtkWidget *races_shell;
71 static GtkWidget *nationsets_chooser;
72 struct player *races_player;
73 /* One entry per nation group, plus one at the end for 'all nations' */
74 static GtkWidget *races_nation_list[MAX_NUM_NATION_GROUPS + 1];
75 static GtkWidget *races_notebook;
76 static GtkWidget *races_properties;
77 static GtkWidget *races_leader;
78 static GtkWidget *races_sex[2];
79 static GtkWidget *races_style_list;
80 static GtkTextBuffer *races_text;
82 static void create_races_dialog(struct player *pplayer);
83 static void races_response(GtkWidget *w, gint response, gpointer data);
84 static void races_nation_callback(GtkTreeSelection *select, gpointer data);
85 static void races_leader_callback(void);
86 static void races_sex_callback(GtkWidget *w, gpointer data);
87 static void races_style_callback(GtkTreeSelection *select, gpointer data);
88 static gboolean races_selection_func(GtkTreeSelection *select,
89 GtkTreeModel *model, GtkTreePath *path,
90 gboolean selected, gpointer data);
92 static int selected_nation;
93 static int selected_sex;
94 static int selected_style;
96 static int is_showing_pillage_dialog = FALSE;
97 static int unit_to_use_to_pillage;
99 /**************************************************************************
100 Popup a generic dialog to display some generic information.
101 **************************************************************************/
102 void popup_notify_dialog(const char *caption, const char *headline,
103 const char *lines)
105 static struct gui_dialog *shell;
106 GtkWidget *vbox, *label, *headline_label, *sw;
108 gui_dialog_new(&shell, GTK_NOTEBOOK(bottom_notebook), NULL, TRUE);
109 gui_dialog_set_title(shell, caption);
111 gui_dialog_add_button(shell, GTK_STOCK_CLOSE, GTK_RESPONSE_CLOSE);
112 gui_dialog_set_default_response(shell, GTK_RESPONSE_CLOSE);
114 vbox = gtk_grid_new();
115 gtk_orientable_set_orientation(GTK_ORIENTABLE(vbox),
116 GTK_ORIENTATION_VERTICAL);
117 gtk_grid_set_row_spacing(GTK_GRID(vbox), 2);
118 gtk_container_add(GTK_CONTAINER(shell->vbox), vbox);
120 headline_label = gtk_label_new(headline);
121 gtk_container_add(GTK_CONTAINER(vbox), headline_label);
122 gtk_widget_set_name(headline_label, "notify_label");
124 gtk_label_set_justify(GTK_LABEL(headline_label), GTK_JUSTIFY_LEFT);
125 gtk_widget_set_halign(headline_label, GTK_ALIGN_START);
126 gtk_widget_set_valign(headline_label, GTK_ALIGN_START);
128 sw = gtk_scrolled_window_new(NULL, NULL);
129 gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(sw),
130 GTK_SHADOW_ETCHED_IN);
131 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(sw),
132 GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
133 label = gtk_label_new(lines);
134 gtk_widget_set_hexpand(label, TRUE);
135 gtk_widget_set_vexpand(label, TRUE);
136 gtk_container_add(GTK_CONTAINER(sw), label);
138 gtk_widget_set_name(label, "notify_label");
139 gtk_label_set_justify(GTK_LABEL(label), GTK_JUSTIFY_LEFT);
140 gtk_widget_set_halign(label, GTK_ALIGN_START);
141 gtk_widget_set_valign(label, GTK_ALIGN_START);
143 gtk_container_add(GTK_CONTAINER(vbox), sw);
145 gui_dialog_show_all(shell);
147 gui_dialog_set_default_size(shell, -1, 265);
148 gui_dialog_present(shell);
150 shell = NULL;
153 /****************************************************************
154 User has responded to notify dialog with possibility to
155 center (goto) on event location.
156 *****************************************************************/
157 static void notify_goto_response(GtkWidget *w, gint response)
159 struct city *pcity = NULL;
160 struct tile *ptile = g_object_get_data(G_OBJECT(w), "tile");
162 switch (response) {
163 case 1:
164 center_tile_mapcanvas(ptile);
165 break;
166 case 2:
167 pcity = tile_city(ptile);
169 if (gui_options.center_when_popup_city) {
170 center_tile_mapcanvas(ptile);
173 if (pcity) {
174 popup_city_dialog(pcity);
176 break;
178 gtk_widget_destroy(w);
181 /****************************************************************
182 User clicked close for connect message dialog
183 *****************************************************************/
184 static void notify_connect_msg_response(GtkWidget *w, gint response)
186 gtk_widget_destroy(w);
189 /**************************************************************************
190 Popup a dialog to display information about an event that has a
191 specific location. The user should be given the option to goto that
192 location.
193 **************************************************************************/
194 void popup_notify_goto_dialog(const char *headline, const char *lines,
195 const struct text_tag_list *tags,
196 struct tile *ptile)
198 GtkWidget *shell, *label, *goto_command, *popcity_command;
200 shell = gtk_dialog_new();
201 gtk_window_set_title(GTK_WINDOW(shell), headline);
202 setup_dialog(shell, toplevel);
203 gtk_dialog_set_default_response(GTK_DIALOG(shell), GTK_RESPONSE_CLOSE);
204 gtk_window_set_position(GTK_WINDOW(shell), GTK_WIN_POS_CENTER_ON_PARENT);
206 label = gtk_label_new(lines);
207 gtk_container_add(GTK_CONTAINER(gtk_dialog_get_content_area(GTK_DIALOG(shell))), label);
208 gtk_widget_show(label);
210 goto_command = gtk_stockbutton_new(GTK_STOCK_JUMP_TO,
211 _("Goto _Location"));
212 gtk_dialog_add_action_widget(GTK_DIALOG(shell), goto_command, 1);
213 gtk_widget_show(goto_command);
215 popcity_command = gtk_stockbutton_new(GTK_STOCK_ZOOM_IN,
216 _("I_nspect City"));
217 gtk_dialog_add_action_widget(GTK_DIALOG(shell), popcity_command, 2);
218 gtk_widget_show(popcity_command);
220 gtk_dialog_add_button(GTK_DIALOG(shell), GTK_STOCK_CLOSE,
221 GTK_RESPONSE_CLOSE);
223 if (!ptile) {
224 gtk_widget_set_sensitive(goto_command, FALSE);
225 gtk_widget_set_sensitive(popcity_command, FALSE);
226 } else {
227 struct city *pcity;
229 pcity = tile_city(ptile);
230 gtk_widget_set_sensitive(popcity_command,
231 (NULL != pcity && city_owner(pcity) == client.conn.playing));
234 g_object_set_data(G_OBJECT(shell), "tile", ptile);
236 g_signal_connect(shell, "response", G_CALLBACK(notify_goto_response), NULL);
237 gtk_widget_show(shell);
240 /**************************************************************************
241 Popup a dialog to display connection message from server.
242 **************************************************************************/
243 void popup_connect_msg(const char *headline, const char *message)
245 GtkWidget *shell, *label;
247 shell = gtk_dialog_new();
248 gtk_window_set_title(GTK_WINDOW(shell), headline);
249 setup_dialog(shell, toplevel);
250 gtk_dialog_set_default_response(GTK_DIALOG(shell), GTK_RESPONSE_CLOSE);
251 gtk_window_set_position(GTK_WINDOW(shell), GTK_WIN_POS_CENTER_ON_PARENT);
253 label = gtk_label_new(message);
254 gtk_label_set_selectable(GTK_LABEL(label), 1);
256 gtk_container_add(GTK_CONTAINER(gtk_dialog_get_content_area(GTK_DIALOG(shell))), label);
257 gtk_widget_show(label);
259 gtk_dialog_add_button(GTK_DIALOG(shell), GTK_STOCK_CLOSE,
260 GTK_RESPONSE_CLOSE);
262 g_signal_connect(shell, "response", G_CALLBACK(notify_connect_msg_response),
263 NULL);
264 gtk_widget_show(shell);
267 /****************************************************************
268 User has responded to revolution dialog
269 *****************************************************************/
270 static void revolution_response(GtkWidget *w, gint response, gpointer data)
272 struct government *government = data;
274 if (response == GTK_RESPONSE_YES) {
275 if (!government) {
276 start_revolution();
277 } else {
278 set_government_choice(government);
281 if (w) {
282 gtk_widget_destroy(w);
286 /****************************************************************
287 Popup revolution dialog for user
288 *****************************************************************/
289 void popup_revolution_dialog(struct government *government)
291 static GtkWidget *shell = NULL;
293 if (0 > client.conn.playing->revolution_finishes) {
294 if (!shell) {
295 shell = gtk_message_dialog_new(NULL,
297 GTK_MESSAGE_WARNING,
298 GTK_BUTTONS_YES_NO,
299 _("You say you wanna revolution?"));
300 gtk_window_set_title(GTK_WINDOW(shell), _("Revolution!"));
301 setup_dialog(shell, toplevel);
303 g_signal_connect(shell, "destroy",
304 G_CALLBACK(gtk_widget_destroyed), &shell);
306 g_signal_connect(shell, "response",
307 G_CALLBACK(revolution_response), government);
309 gtk_window_present(GTK_WINDOW(shell));
310 } else {
311 revolution_response(shell, GTK_RESPONSE_YES, government);
316 /***********************************************************************
317 NB: 'data' is a value of enum tile_special_type casted to a pointer.
318 ***********************************************************************/
319 static void pillage_callback(GtkWidget *w, gpointer data)
321 struct unit *punit;
322 int what = GPOINTER_TO_INT(data);
324 punit = game_unit_by_number(unit_to_use_to_pillage);
325 if (punit) {
326 struct extra_type *target = extra_by_number(what);
328 request_new_unit_activity_targeted(punit, ACTIVITY_PILLAGE,
329 target);
333 /****************************************************************
334 Pillage dialog destroyed
335 *****************************************************************/
336 static void pillage_destroy_callback(GtkWidget *w, gpointer data)
338 is_showing_pillage_dialog = FALSE;
341 /****************************************************************
342 Opens pillage dialog listing possible pillage targets.
343 *****************************************************************/
344 void popup_pillage_dialog(struct unit *punit, bv_extras extras)
346 GtkWidget *shl;
348 if (!is_showing_pillage_dialog) {
349 struct extra_type *tgt;
351 is_showing_pillage_dialog = TRUE;
352 unit_to_use_to_pillage = punit->id;
354 shl = choice_dialog_start(GTK_WINDOW(toplevel),
355 _("What To Pillage"),
356 _("Select what to pillage:"));
358 while ((tgt = get_preferred_pillage(extras))) {
359 int what;
361 what = extra_index(tgt);
362 BV_CLR(extras, what);
364 choice_dialog_add(shl, extra_name_translation(tgt),
365 G_CALLBACK(pillage_callback),
366 GINT_TO_POINTER(what),
367 FALSE, NULL);
370 choice_dialog_add(shl, GTK_STOCK_CANCEL, 0, 0, FALSE, NULL);
372 choice_dialog_end(shl);
374 g_signal_connect(shl, "destroy", G_CALLBACK(pillage_destroy_callback),
375 NULL);
379 /*****************************************************************************
380 Popup unit selection dialog. It is a wrapper for the main function; see
381 unitselect.c:unit_select_dialog_popup_main().
382 *****************************************************************************/
383 void unit_select_dialog_popup(struct tile *ptile)
385 unit_select_dialog_popup_main(ptile, TRUE);
388 /*****************************************************************************
389 Update unit selection dialog. It is a wrapper for the main function; see
390 unitselect.c:unit_select_dialog_popup_main().
391 *****************************************************************************/
392 void unit_select_dialog_update_real(void)
394 unit_select_dialog_popup_main(NULL, FALSE);
397 /*****************************************************************************
398 NATION SELECTION DIALOG
399 *****************************************************************************/
400 /*****************************************************************************
401 Return the GtkTreePath for a given nation on the specified list, or NULL
402 if it's not there at all.
403 Caller must free with gtk_tree_path_free().
404 *****************************************************************************/
405 static GtkTreePath *path_to_nation_on_list(Nation_type_id nation,
406 GtkTreeView *list)
408 if (nation == -1 || list == NULL) {
409 return NULL;
410 } else {
411 GtkTreeModel *model = gtk_tree_view_get_model(list);
412 GtkTreeIter iter;
413 GtkTreePath *path = NULL;
414 gtk_tree_model_get_iter_first(model, &iter);
415 do {
416 int nation_of_row;
417 gtk_tree_model_get(model, &iter, 0, &nation_of_row, -1);
418 if (nation == nation_of_row) {
419 path = gtk_tree_model_get_path(model, &iter);
420 break;
422 } while (gtk_tree_model_iter_next(model, &iter));
423 return path;
427 /*****************************************************************************
428 Make sure the given nation is selected in the list on a given groups
429 notebook tab, if it's present on that tab.
430 Intended for synchronising the tabs to the current selection, so does not
431 disturb the controls on the right-hand side.
432 *****************************************************************************/
433 static void select_nation_on_tab(GtkWidget *tab_list, int nation)
435 /* tab_list is a GtkTreeView (not its enclosing GtkScrolledWindow). */
436 GtkTreeView *list = GTK_TREE_VIEW(tab_list);
437 GtkTreeSelection *select = gtk_tree_view_get_selection(GTK_TREE_VIEW(list));
438 GtkTreePath *path = path_to_nation_on_list(nation, list);
439 /* Suppress normal effects of selection change to avoid loops. */
440 g_signal_handlers_block_by_func(select, races_nation_callback, NULL);
441 if (path) {
442 /* Found nation on this list. */
443 /* Avoid disturbing tabs that already have the correct selection. */
444 if (!gtk_tree_selection_path_is_selected(select, path)) {
445 /* Set cursor -- this will cause the nation to be selected */
446 gtk_tree_view_set_cursor(list, path, NULL, FALSE);
447 /* Make sure selected nation is visible in list */
448 gtk_tree_view_scroll_to_cell(list, path, NULL, FALSE, 0, 0);
450 } else {
451 /* Either no nation was selected, or the nation is not mentioned in
452 * this tab. Either way we want to end up with no selection. */
453 GtkTreePath *cursorpath;
454 /* If there is no cursor, Gtk tends to focus and select the first row
455 * at the first opportunity, disturbing any existing state. We want to
456 * allow the the no-rows-selected state, so detect this case and defuse
457 * it by setting a cursor. */
458 gtk_tree_view_get_cursor(list, &cursorpath, NULL);
459 /* Set the cursor in the case above, or if there was a previous
460 * selection */
461 if (!cursorpath || gtk_tree_selection_get_selected(select, NULL, NULL)) {
462 cursorpath = gtk_tree_path_new_first();
463 gtk_tree_view_set_cursor(list, cursorpath, NULL, FALSE);
465 gtk_tree_selection_unselect_all(select);
466 gtk_tree_path_free(cursorpath);
468 gtk_tree_path_free(path);
469 /* Re-enable selection change side-effects */
470 g_signal_handlers_unblock_by_func(select, races_nation_callback, NULL);
473 /*****************************************************************************
474 Select the given nation in the nation lists in the left-hand-side notebook.
475 *****************************************************************************/
476 static void sync_tabs_to_nation(int nation)
478 /* Ensure that all tabs are in sync with the new selection */
479 int i;
480 for (i = 0; i <= nation_group_count(); i++) {
481 if (races_nation_list[i]) {
482 select_nation_on_tab(races_nation_list[i], nation);
487 /*****************************************************************************
488 Populates leader list.
489 If no nation selected, blanks it.
490 *****************************************************************************/
491 static void populate_leader_list(void)
493 int i;
494 GtkListStore *model =
495 GTK_LIST_STORE(gtk_combo_box_get_model(GTK_COMBO_BOX(races_leader)));
497 i = 0;
498 gtk_list_store_clear(model);
499 if (selected_nation >= 0) {
500 nation_leader_list_iterate(nation_leaders(nation_by_number
501 (selected_nation)), pleader) {
502 const char *leader_name = nation_leader_name(pleader);
503 GtkTreeIter iter; /* unused */
505 gtk_list_store_insert_with_values(model, &iter, i, 0, leader_name, -1);
506 i++;
507 } nation_leader_list_iterate_end;
511 /*****************************************************************************
512 Update dialog state by selecting a nation and choosing values for its
513 parameters, and update the right-hand side of the dialog accordingly.
514 If 'leadername' is NULL, pick a random leader name and sex from the
515 nation's list (ignoring the 'is_male' parameter).
516 *****************************************************************************/
517 static void select_nation(int nation,
518 const char *leadername, bool is_male,
519 int style_id)
521 selected_nation = nation;
523 /* Refresh the available leaders. */
524 populate_leader_list();
526 if (selected_nation != -1) {
528 /* Select leader name and sex. */
529 if (leadername) {
530 gtk_entry_set_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN(races_leader))),
531 leadername);
532 /* Assume is_male is valid too. */
533 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(races_sex[is_male]),
534 TRUE);
535 } else {
536 int idx = fc_rand(nation_leader_list_size(
537 nation_leaders(nation_by_number(selected_nation))));
538 gtk_combo_box_set_active(GTK_COMBO_BOX(races_leader), idx);
539 /* This also updates the leader sex, eventually. */
542 /* Select the appropriate city style entry. */
544 int i;
545 int j = 0;
546 GtkTreePath *path;
548 styles_iterate(pstyle) {
549 i = basic_city_style_for_style(pstyle);
551 if (i >= 0 && i < style_id) {
552 j++;
553 } else {
554 break;
556 } styles_iterate_end;
558 path = gtk_tree_path_new();
559 gtk_tree_path_append_index(path, j);
560 gtk_tree_view_set_cursor(GTK_TREE_VIEW(races_style_list), path,
561 NULL, FALSE);
562 gtk_tree_path_free(path);
565 /* Update nation description. */
567 char buf[4096];
568 helptext_nation(buf, sizeof(buf),
569 nation_by_number(selected_nation), NULL);
570 gtk_text_buffer_set_text(races_text, buf, -1);
573 gtk_widget_set_sensitive(races_properties, TRUE);
574 /* Once we've made a nation selection, allow user to ok */
575 gtk_dialog_set_response_sensitive(GTK_DIALOG(races_shell),
576 GTK_RESPONSE_ACCEPT, TRUE);
577 } else {
578 /* No nation selected. Blank properties and make controls insensitive. */
579 /* Leader name */
580 gtk_entry_set_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN(races_leader))),
581 "");
582 /* Leader sex (*shrug*) */
583 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(races_sex[0]), TRUE);
584 /* City style */
586 GtkTreeSelection* select
587 = gtk_tree_view_get_selection(GTK_TREE_VIEW(races_style_list));
588 gtk_tree_selection_unselect_all(select);
590 /* Nation description */
591 gtk_text_buffer_set_text(races_text, "", 0);
593 gtk_widget_set_sensitive(races_properties, FALSE);
594 /* Don't allow OK without a selection
595 * (user can still do "Random Nation") */
596 gtk_dialog_set_response_sensitive(GTK_DIALOG(races_shell),
597 GTK_RESPONSE_ACCEPT, FALSE);
600 /* Update notebook to reflect the current selection */
601 sync_tabs_to_nation(selected_nation);
604 /*****************************************************************************
605 Creates a list of currently-pickable nations in the given group
606 Inserts appropriate gtk_tree_view into races_nation_list[index] (or NULL if
607 the group has no nations)
608 If group == NULL, create a list of all nations
609 *****************************************************************************/
610 static GtkWidget* create_list_of_nations_in_group(struct nation_group* group,
611 int index)
613 GtkWidget *sw = NULL;
614 GtkListStore *store = NULL;
615 GtkWidget *list = NULL;
617 /* Populate nation list store. */
618 nations_iterate(pnation) {
619 bool used;
620 GdkPixbuf *img;
621 GtkTreeIter it;
622 GValue value = { 0, };
624 if (!is_nation_playable(pnation) || !is_nation_pickable(pnation)) {
625 continue;
628 if (NULL != group && !nation_is_in_group(pnation, group)) {
629 continue;
632 /* Only create tab on demand -- we don't want it if there aren't any
633 * currently pickable nations in this group. */
634 if (sw == NULL) {
635 GtkTreeSelection *select;
636 GtkCellRenderer *render;
637 GtkTreeViewColumn *column;
639 store = gtk_list_store_new(4, G_TYPE_INT, G_TYPE_BOOLEAN,
640 GDK_TYPE_PIXBUF, G_TYPE_STRING);
641 gtk_tree_sortable_set_sort_column_id(GTK_TREE_SORTABLE(store),
642 3, GTK_SORT_ASCENDING);
644 list = gtk_tree_view_new_with_model(GTK_TREE_MODEL(store));
645 gtk_widget_set_hexpand(list, TRUE);
646 gtk_widget_set_vexpand(list, TRUE);
647 gtk_tree_view_set_search_column(GTK_TREE_VIEW(list), 3);
648 gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(list), FALSE);
649 g_object_unref(store);
651 select = gtk_tree_view_get_selection(GTK_TREE_VIEW(list));
652 g_signal_connect(select, "changed", G_CALLBACK(races_nation_callback),
653 NULL);
654 gtk_tree_selection_set_select_function(select, races_selection_func,
655 NULL, NULL);
657 sw = gtk_scrolled_window_new(NULL, NULL);
658 gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(sw),
659 GTK_SHADOW_ETCHED_IN);
660 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(sw),
661 GTK_POLICY_NEVER, GTK_POLICY_ALWAYS);
662 gtk_container_add(GTK_CONTAINER(sw), list);
664 render = gtk_cell_renderer_pixbuf_new();
665 column = gtk_tree_view_column_new_with_attributes("Flag", render,
666 "pixbuf", 2, NULL);
667 gtk_tree_view_append_column(GTK_TREE_VIEW(list), column);
668 render = gtk_cell_renderer_text_new();
669 column = gtk_tree_view_column_new_with_attributes("Nation", render,
670 "text", 3, "strikethrough", 1, NULL);
671 gtk_tree_view_append_column(GTK_TREE_VIEW(list), column);
674 gtk_list_store_append(store, &it);
676 used = (pnation->player != NULL && pnation->player != races_player);
677 gtk_list_store_set(store, &it, 0, nation_number(pnation), 1, used, -1);
678 img = get_flag(pnation);
679 if (img != NULL) {
680 gtk_list_store_set(store, &it, 2, img, -1);
681 g_object_unref(img);
684 g_value_init(&value, G_TYPE_STRING);
685 g_value_set_static_string(&value, nation_adjective_translation(pnation));
686 gtk_list_store_set_value(store, &it, 3, &value);
687 g_value_unset(&value);
688 } nations_iterate_end;
690 races_nation_list[index] = list;
691 return sw;
694 /*****************************************************************************
695 Creates lists of nations for left side of nation selection dialog
696 *****************************************************************************/
697 static void create_nation_selection_lists(void)
699 GtkWidget *nation_list;
700 GtkWidget *group_name_label;
702 int i;
704 for (i = 0; i < nation_group_count(); i++) {
705 struct nation_group* group = (nation_group_by_number(i));
706 if (is_nation_group_hidden(group)) {
707 races_nation_list[i] = NULL;
708 continue;
710 nation_list = create_list_of_nations_in_group(group, i);
711 if (nation_list) {
712 group_name_label = gtk_label_new(nation_group_name_translation(group));
713 gtk_notebook_append_page(GTK_NOTEBOOK(races_notebook), nation_list,
714 group_name_label);
718 nation_list = create_list_of_nations_in_group(NULL, nation_group_count());
719 /* Even this list can be empty if there are no pickable nations (due to
720 * a combination of start position and nationset restrictions). */
721 if (nation_list) {
722 group_name_label = gtk_label_new(_("All"));
723 gtk_notebook_append_page(GTK_NOTEBOOK(races_notebook), nation_list,
724 group_name_label);
728 /*****************************************************************************
729 The server has changed the set of selectable nations.
730 Update any current nations dialog accordingly.
731 *****************************************************************************/
732 void races_update_pickable(bool nationset_change)
734 int tab, groupidx;
736 if (!races_shell) {
737 return;
740 /* Save selected tab */
741 tab = gtk_notebook_get_current_page(GTK_NOTEBOOK(races_notebook));
742 if (tab != -1) {
743 int i = 0;
744 groupidx = 0;
745 /* Turn tab index into a nation group index (they're not always equal,
746 * as some groups may not currently have tabs). */
747 do {
748 while (groupidx <= nation_group_count()
749 && races_nation_list[groupidx] == NULL) {
750 groupidx++;
752 fc_assert_action(groupidx <= nation_group_count(), break);
753 /* Nation group 'groupidx' is what's displayed on the i'th tab */
754 if (i == tab) {
755 break;
757 i++;
758 groupidx++;
759 } while(1);
760 } else {
761 /* No tabs currently */
762 groupidx = -1;
765 /* selected_nation already contains currently selected nation; however,
766 * it may no longer be a valid choice */
767 if (selected_nation != -1
768 && !is_nation_pickable(nation_by_number(selected_nation))) {
769 select_nation(-1, NULL, FALSE, 0);
772 /* Delete all list stores, treeviews, tabs */
773 while (gtk_notebook_get_n_pages(GTK_NOTEBOOK(races_notebook)) > 0) {
774 gtk_notebook_remove_page(GTK_NOTEBOOK(races_notebook), -1);
777 /* (Re)create all of them */
778 create_nation_selection_lists();
780 /* Can't set current tab before child widget is visible */
781 gtk_widget_show_all(GTK_WIDGET(races_notebook));
783 /* Restore selected tab */
784 if (groupidx != -1 && races_nation_list[groupidx] != NULL) {
785 int i;
786 tab = 0;
787 for (i = 0; i < groupidx; i++) {
788 if (races_nation_list[i] != NULL) {
789 tab++;
792 gtk_notebook_set_current_page(GTK_NOTEBOOK(races_notebook), tab);
795 /* Restore selected nation */
796 sync_tabs_to_nation(selected_nation);
799 /*****************************************************************************
800 Sync nationset control with the current state of the server.
801 *****************************************************************************/
802 void nationset_sync_to_server(const char *nationset)
804 if (nationsets_chooser) {
805 struct nation_set *set = nation_set_by_setting_value(nationset);
806 gtk_combo_box_set_active(GTK_COMBO_BOX(nationsets_chooser),
807 nation_set_index(set));
811 /*****************************************************************************
812 Called when the nationset control's value has changed.
813 *****************************************************************************/
814 static void nationset_callback(GtkComboBox *b, gpointer data)
816 GtkTreeIter iter;
817 if (gtk_combo_box_get_active_iter(b, &iter)) {
818 struct option *poption = optset_option_by_name(server_optset, "nationset");
819 gchar *rule_name;
820 gtk_tree_model_get(gtk_combo_box_get_model(b), &iter,
821 0, &rule_name, -1);
822 /* Suppress propagation of an option value equivalent to the current
823 * server state, after canonicalisation, to avoid loops from
824 * nationset_sync_to_server().
825 * (HACK: relies on local Gtk "changed" signal getting here before
826 * server response.) */
827 if (nation_set_by_setting_value(option_str_get(poption))
828 != nation_set_by_rule_name(rule_name)) {
829 option_str_set(poption, rule_name);
831 FC_FREE(rule_name);
835 /*****************************************************************************
836 Create nations dialog
837 *****************************************************************************/
838 static void create_races_dialog(struct player *pplayer)
840 GtkWidget *shell;
841 GtkWidget *cmd;
842 GtkWidget *hbox, *table;
843 GtkWidget *frame, *label, *combo;
844 GtkWidget *text;
845 GtkWidget *notebook;
847 GtkWidget *sw;
848 GtkWidget *list;
849 GtkListStore *store;
850 GtkCellRenderer *render;
851 GtkTreeViewColumn *column;
853 int i;
854 char *title;
856 /* Init. */
857 selected_nation = -1;
859 if (C_S_RUNNING == client_state()) {
860 title = _("Edit Nation");
861 } else if (NULL != pplayer && pplayer == client.conn.playing) {
862 title = _("What Nation Will You Be?");
863 } else {
864 title = _("Pick Nation");
867 shell = gtk_dialog_new_with_buttons(title,
868 NULL,
870 GTK_STOCK_CANCEL,
871 GTK_RESPONSE_CANCEL,
872 _("_Random Nation"),
873 GTK_RESPONSE_NO, /* arbitrary */
874 GTK_STOCK_OK,
875 GTK_RESPONSE_ACCEPT,
876 NULL);
877 races_shell = shell;
878 races_player = pplayer;
879 setup_dialog(shell, toplevel);
881 gtk_window_set_position(GTK_WINDOW(shell), GTK_WIN_POS_CENTER_ON_PARENT);
882 gtk_window_set_default_size(GTK_WINDOW(shell), -1, 590);
884 frame = gtk_frame_new(_("Select a nation"));
885 gtk_container_add(GTK_CONTAINER(gtk_dialog_get_content_area(GTK_DIALOG(shell))), frame);
887 hbox = gtk_grid_new();
888 gtk_grid_set_column_spacing(GTK_GRID(hbox), 18);
889 gtk_container_set_border_width(GTK_CONTAINER(hbox), 3);
890 gtk_container_add(GTK_CONTAINER(frame), hbox);
892 /* Left side: nation list */
894 GtkWidget* nation_selection_list = gtk_grid_new();
895 nationsets_chooser = NULL;
897 gtk_grid_set_row_spacing(GTK_GRID(nation_selection_list), 2);
899 /* Nationset selector dropdown */
900 /* Only present this if there is more than one choice.
901 * (If ruleset is changed, possibly changing the number of available sets
902 * and invalidating this decision, then dialog will be popped down.) */
903 if (nation_set_count() > 1) {
904 GtkListStore *sets_model = gtk_list_store_new(4, G_TYPE_STRING,
905 G_TYPE_STRING,
906 G_TYPE_STRING,
907 G_TYPE_STRING);
908 GtkCellRenderer *renderer;
909 nation_sets_iterate(pset) {
910 /* Index in list store must match nation_set_index(). */
911 gchar *escaped;
912 struct astring s = ASTRING_INIT;
913 int num_nations = 0;
914 nations_iterate(pnation) {
915 if (is_nation_playable(pnation) && nation_is_in_set(pnation, pset)) {
916 num_nations++;
918 } nations_iterate_end;
919 escaped = g_markup_escape_text(nation_set_name_translation(pset), -1);
920 /* TRANS: nation set name followed by number of playable nations;
921 * <b> and </b> are Pango markup and should be left alone */
922 astr_set(&s, PL_("<b>%s</b> (%d nation)",
923 "<b>%s</b> (%d nations)", num_nations),
924 escaped, num_nations);
925 g_free(escaped);
926 if (strlen(nation_set_description(pset)) > 0) {
927 /* While in principle it would be better to get Gtk to wrap the
928 * drop-down (e.g. via "wrap-width" property), there's no way
929 * to specify the indentation we want. So we do it ourselves. */
930 char *desc = fc_strdup(_(nation_set_description(pset)));
931 char *p = desc;
932 fc_break_lines(desc, 70);
933 astr_add(&s, "\n");
934 while (*p) {
935 int len = strcspn(p, "\n");
936 if (p[len] == '\n') len++;
937 escaped = g_markup_escape_text(p, len);
938 astr_add(&s, "\t%s", escaped);
939 g_free(escaped);
940 p += len;
942 FC_FREE(desc);
944 gtk_list_store_insert_with_values(sets_model, NULL, -1,
945 0, nation_set_rule_name(pset),
946 1, astr_str(&s),
947 2, nation_set_name_translation(pset),
948 -1);
949 astr_free(&s);
950 } nation_sets_iterate_end;
952 /* We want a combo box where the button displays just the set name,
953 * but the dropdown displays the expanded description. */
954 nationsets_chooser
955 = gtk_combo_box_new_with_model_and_entry(GTK_TREE_MODEL(sets_model));
956 g_object_unref(G_OBJECT(sets_model));
958 /* Do our best to turn the text-entry widget into something more
959 * like a cell-view: disable editing, and focusing (which removes
960 * the caret). */
961 GtkWidget *entry = gtk_bin_get_child(GTK_BIN(nationsets_chooser));
962 gtk_editable_set_editable(GTK_EDITABLE(entry), FALSE);
963 gtk_widget_set_can_focus(entry, FALSE);
965 /* The entry displays the set name. */
966 gtk_combo_box_set_entry_text_column(GTK_COMBO_BOX(nationsets_chooser),
968 /* The dropdown displays the marked-up description. */
969 renderer = gtk_cell_renderer_text_new();
970 gtk_cell_layout_clear(GTK_CELL_LAYOUT(nationsets_chooser));
971 gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(nationsets_chooser),
972 renderer, TRUE);
973 gtk_cell_layout_set_attributes(GTK_CELL_LAYOUT(nationsets_chooser),
974 renderer, "markup", 1, NULL);
975 g_signal_connect(nationsets_chooser, "destroy",
976 G_CALLBACK(gtk_widget_destroyed), &nationsets_chooser);
977 g_signal_connect(nationsets_chooser, "changed",
978 G_CALLBACK(nationset_callback), NULL);
980 /* Populate initially from client's view of server setting */
981 struct option *poption = optset_option_by_name(server_optset,
982 "nationset");
983 if (poption) {
984 nationset_sync_to_server(option_str_get(poption));
988 label = g_object_new(GTK_TYPE_LABEL,
989 "use-underline", TRUE,
990 "label", _("_Nation Set:"),
991 "xalign", 0.0,
992 "yalign", 0.5,
993 NULL);
994 gtk_label_set_mnemonic_widget(GTK_LABEL(label), nationsets_chooser);
996 gtk_widget_set_hexpand(nationsets_chooser, TRUE);
997 gtk_grid_attach(GTK_GRID(nation_selection_list), label,
998 0, 0, 1, 1);
999 gtk_grid_attach(GTK_GRID(nation_selection_list), nationsets_chooser,
1000 1, 0, 1, 1);
1003 races_notebook = gtk_notebook_new();
1004 gtk_notebook_set_tab_pos(GTK_NOTEBOOK(races_notebook), GTK_POS_LEFT);
1005 gtk_grid_attach(GTK_GRID(nation_selection_list), races_notebook,
1006 0, 2, 2, 1);
1008 /* Suppress notebook tabs if there will be only one ("All") */
1010 bool show_groups = FALSE;
1011 nation_groups_iterate(pgroup) {
1012 if (!is_nation_group_hidden(pgroup)) {
1013 show_groups = TRUE;
1014 break;
1016 } nation_groups_iterate_end;
1017 if (!show_groups) {
1018 gtk_notebook_set_show_tabs(GTK_NOTEBOOK(races_notebook), FALSE);
1019 } else {
1020 label = g_object_new(GTK_TYPE_LABEL,
1021 "use-underline", TRUE,
1022 "label", _("Nation _Groups:"),
1023 "xalign", 0.0,
1024 "yalign", 0.5,
1025 NULL);
1026 gtk_label_set_mnemonic_widget(GTK_LABEL(label), races_notebook);
1027 gtk_grid_attach(GTK_GRID(nation_selection_list), label,
1028 0, 1, 2, 1);
1029 gtk_notebook_set_show_tabs(GTK_NOTEBOOK(races_notebook), TRUE);
1033 /* Populate treeview */
1034 create_nation_selection_lists();
1036 gtk_container_add(GTK_CONTAINER(hbox), nation_selection_list);
1039 /* Right side. */
1040 notebook = gtk_notebook_new();
1041 gtk_notebook_set_tab_pos(GTK_NOTEBOOK(notebook), GTK_POS_BOTTOM);
1042 gtk_container_add(GTK_CONTAINER(hbox), notebook);
1044 /* Properties pane. */
1045 label = gtk_label_new_with_mnemonic(_("_Properties"));
1047 races_properties = table = gtk_grid_new();
1048 g_signal_connect(table, "destroy",
1049 G_CALLBACK(gtk_widget_destroyed), &races_properties);
1050 g_object_set(table, "margin", 6, NULL);
1051 gtk_grid_set_row_spacing(GTK_GRID(table), 2);
1052 gtk_notebook_append_page(GTK_NOTEBOOK(notebook), table, label);
1054 /* Leader. */
1056 GtkListStore *model = gtk_list_store_new(1, G_TYPE_STRING);
1057 combo = gtk_combo_box_new_with_model_and_entry(GTK_TREE_MODEL(model));
1058 gtk_combo_box_set_entry_text_column(GTK_COMBO_BOX(combo), 0);
1059 g_object_unref(G_OBJECT(model));
1061 races_leader = combo;
1062 label = g_object_new(GTK_TYPE_LABEL,
1063 "use-underline", TRUE,
1064 "mnemonic-widget", GTK_COMBO_BOX(combo),
1065 "label", _("_Leader:"),
1066 "xalign", 0.0,
1067 "yalign", 0.5,
1068 NULL);
1069 gtk_widget_set_margin_bottom(label, 6);
1070 gtk_widget_set_margin_right(label, 12);
1071 gtk_grid_attach(GTK_GRID(table), label, 0, 0, 1, 2);
1072 gtk_grid_attach(GTK_GRID(table), combo, 1, 0, 2, 1);
1074 cmd = gtk_radio_button_new_with_mnemonic(NULL, _("_Female"));
1075 gtk_widget_set_margin_bottom(cmd, 6);
1076 races_sex[0] = cmd;
1077 gtk_grid_attach(GTK_GRID(table), cmd, 1, 1, 1, 1);
1079 cmd = gtk_radio_button_new_with_mnemonic_from_widget(GTK_RADIO_BUTTON(cmd),
1080 _("_Male"));
1081 gtk_widget_set_margin_bottom(cmd, 6);
1082 races_sex[1] = cmd;
1083 gtk_grid_attach(GTK_GRID(table), cmd, 2, 1, 1, 1);
1085 /* City style. */
1086 store = gtk_list_store_new(3, G_TYPE_INT,
1087 GDK_TYPE_PIXBUF, G_TYPE_STRING);
1089 list = gtk_tree_view_new_with_model(GTK_TREE_MODEL(store));
1090 gtk_widget_set_hexpand(list, TRUE);
1091 gtk_widget_set_vexpand(list, TRUE);
1092 races_style_list = list;
1093 g_object_unref(store);
1094 gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(list), FALSE);
1095 g_signal_connect(gtk_tree_view_get_selection(GTK_TREE_VIEW(list)), "changed",
1096 G_CALLBACK(races_style_callback), NULL);
1098 sw = gtk_scrolled_window_new(NULL, NULL);
1099 gtk_widget_set_margin_top(sw, 6);
1100 gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(sw),
1101 GTK_SHADOW_ETCHED_IN);
1102 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(sw),
1103 GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
1104 gtk_container_add(GTK_CONTAINER(sw), list);
1105 gtk_grid_attach(GTK_GRID(table), sw, 1, 2, 2, 2);
1107 label = g_object_new(GTK_TYPE_LABEL,
1108 "use-underline", TRUE,
1109 "mnemonic-widget", list,
1110 "label", _("City _Styles:"),
1111 "xalign", 0.0,
1112 "yalign", 0.5,
1113 NULL);
1114 gtk_widget_set_margin_top(label, 6);
1115 gtk_widget_set_margin_right(label, 12);
1116 gtk_grid_attach(GTK_GRID(table), label, 0, 2, 1, 1);
1118 render = gtk_cell_renderer_pixbuf_new();
1119 column = gtk_tree_view_column_new_with_attributes(NULL, render,
1120 "pixbuf", 1, NULL);
1121 gtk_tree_view_append_column(GTK_TREE_VIEW(list), column);
1122 render = gtk_cell_renderer_text_new();
1123 column = gtk_tree_view_column_new_with_attributes(NULL, render,
1124 "text", 2, NULL);
1125 gtk_tree_view_append_column(GTK_TREE_VIEW(list), column);
1127 /* Populate style store. */
1128 styles_iterate(pstyle) {
1129 GdkPixbuf *img;
1130 struct sprite *s;
1131 GtkTreeIter it;
1133 i = basic_city_style_for_style(pstyle);
1135 if (i >= 0) {
1136 gtk_list_store_append(store, &it);
1138 s = crop_blankspace(get_sample_city_sprite(tileset, i));
1139 img = sprite_get_pixbuf(s);
1140 free_sprite(s);
1141 gtk_list_store_set(store, &it, 0, i, 1, img, 2,
1142 city_style_name_translation(i), -1);
1143 g_object_unref(img);
1145 } styles_iterate_end;
1147 /* Legend pane. */
1148 label = gtk_label_new_with_mnemonic(_("_Description"));
1150 text = gtk_text_view_new();
1151 g_object_set(text, "margin", 6, NULL);
1152 gtk_widget_set_hexpand(text, TRUE);
1153 gtk_widget_set_vexpand(text, TRUE);
1154 races_text = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
1155 gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(text), GTK_WRAP_WORD);
1156 gtk_text_view_set_editable(GTK_TEXT_VIEW(text), FALSE);
1157 gtk_text_view_set_cursor_visible(GTK_TEXT_VIEW(text), FALSE);
1158 gtk_text_view_set_left_margin(GTK_TEXT_VIEW(text), 6);
1159 gtk_text_view_set_right_margin(GTK_TEXT_VIEW(text), 6);
1161 gtk_notebook_append_page(GTK_NOTEBOOK(notebook), text, label);
1163 /* Signals. */
1164 g_signal_connect(shell, "destroy",
1165 G_CALLBACK(gtk_widget_destroyed), &races_shell);
1166 g_signal_connect(shell, "response",
1167 G_CALLBACK(races_response), NULL);
1169 g_signal_connect(GTK_COMBO_BOX(races_leader), "changed",
1170 G_CALLBACK(races_leader_callback), NULL);
1172 g_signal_connect(races_sex[0], "toggled",
1173 G_CALLBACK(races_sex_callback), GINT_TO_POINTER(0));
1174 g_signal_connect(races_sex[1], "toggled",
1175 G_CALLBACK(races_sex_callback), GINT_TO_POINTER(1));
1177 /* Finish up. */
1178 gtk_dialog_set_default_response(GTK_DIALOG(shell), GTK_RESPONSE_CANCEL);
1180 /* You can't assign NO_NATION during a running game. */
1181 if (C_S_RUNNING == client_state()) {
1182 gtk_dialog_set_response_sensitive(GTK_DIALOG(shell), GTK_RESPONSE_NO,
1183 FALSE);
1186 gtk_widget_show_all(gtk_dialog_get_content_area(GTK_DIALOG(shell)));
1188 /* Select player's current nation in UI, if any */
1189 if (races_player->nation) {
1190 select_nation(nation_number(races_player->nation),
1191 player_name(races_player),
1192 races_player->is_male,
1193 style_number(races_player->style));
1194 /* Make sure selected nation is visible
1195 * (last page, "All", will certainly contain it) */
1196 fc_assert(gtk_notebook_get_n_pages(GTK_NOTEBOOK(races_notebook)) > 0);
1197 gtk_notebook_set_current_page(GTK_NOTEBOOK(races_notebook), -1);
1198 } else {
1199 select_nation(-1, NULL, FALSE, 0);
1203 /****************************************************************
1204 popup the dialog 10% inside the main-window
1205 *****************************************************************/
1206 void popup_races_dialog(struct player *pplayer)
1208 if (!pplayer) {
1209 return;
1212 if (!races_shell) {
1213 create_races_dialog(pplayer);
1214 gtk_window_present(GTK_WINDOW(races_shell));
1218 /****************************************************************
1219 Close nations dialog
1220 *****************************************************************/
1221 void popdown_races_dialog(void)
1223 if (races_shell) {
1224 gtk_widget_destroy(races_shell);
1227 /* We're probably starting a new game, maybe with a new ruleset.
1228 So we warn the worklist dialog. */
1229 blank_max_unit_size();
1232 /**************************************************************************
1233 Update which nations are allowed to be selected (due to e.g. another
1234 player choosing a nation).
1235 **************************************************************************/
1236 void races_toggles_set_sensitive(void)
1238 int i;
1240 if (!races_shell) {
1241 return;
1244 for (i = 0; i <= nation_group_count(); i++) {
1245 if (races_nation_list[i]) {
1246 GtkTreeView *list = GTK_TREE_VIEW(races_nation_list[i]);
1247 GtkTreeModel *model = gtk_tree_view_get_model(list);
1248 GtkTreeSelection* select = gtk_tree_view_get_selection(list);
1249 GtkTreeIter it;
1250 gboolean chosen;
1252 /* Update 'chosen' column in model */
1253 if (gtk_tree_model_get_iter_first(model, &it)) {
1254 do {
1255 int nation_no;
1256 struct nation_type *nation;
1258 gtk_tree_model_get(model, &it, 0, &nation_no, -1);
1259 nation = nation_by_number(nation_no);
1261 chosen = !is_nation_pickable(nation)
1262 || (nation->player && nation->player != races_player);
1264 gtk_list_store_set(GTK_LIST_STORE(model), &it, 1, chosen, -1);
1266 } while (gtk_tree_model_iter_next(model, &it));
1269 /* If our selection is now invalid, deselect it */
1270 if (gtk_tree_selection_get_selected(select, &model, &it)) {
1271 gtk_tree_model_get(model, &it, 1, &chosen, -1);
1273 if (chosen) {
1274 gtk_tree_selection_unselect_all(select);
1281 /*****************************************************************************
1282 Called whenever a user selects a nation in nation list
1283 *****************************************************************************/
1284 static void races_nation_callback(GtkTreeSelection *select, gpointer data)
1286 GtkTreeModel *model;
1287 GtkTreeIter it;
1289 if (gtk_tree_selection_get_selected(select, &model, &it)) {
1290 gboolean chosen;
1291 int newnation;
1293 gtk_tree_model_get(model, &it, 0, &newnation, 1, &chosen, -1);
1295 /* Only allow nations not chosen by another player */
1296 if (!chosen) {
1297 if (newnation != selected_nation) {
1298 /* Choose a random leader */
1299 select_nation(newnation, NULL, FALSE,
1300 style_number(style_of_nation(nation_by_number(newnation))));
1302 return;
1306 /* Fall-through if no valid nation selected */
1307 select_nation(-1, NULL, FALSE, 0);
1310 /**************************************************************************
1311 Leader name has been chosen
1312 **************************************************************************/
1313 static void races_leader_callback(void)
1315 const struct nation_leader *pleader;
1316 const gchar *name;
1318 name =
1319 gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN(races_leader))));
1321 if (selected_nation != -1
1322 &&(pleader = nation_leader_by_name(nation_by_number(selected_nation),
1323 name))) {
1324 selected_sex = nation_leader_is_male(pleader);
1325 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(races_sex[selected_sex]),
1326 TRUE);
1330 /**************************************************************************
1331 Leader sex has been chosen
1332 **************************************************************************/
1333 static void races_sex_callback(GtkWidget *w, gpointer data)
1335 selected_sex = GPOINTER_TO_INT(data);
1338 /**************************************************************************
1339 Determines which nations can be selected in the UI
1340 **************************************************************************/
1341 static gboolean races_selection_func(GtkTreeSelection *select,
1342 GtkTreeModel *model, GtkTreePath *path,
1343 gboolean selected, gpointer data)
1345 GtkTreeIter it;
1346 gboolean chosen;
1348 gtk_tree_model_get_iter(model, &it, path);
1349 gtk_tree_model_get(model, &it, 1, &chosen, -1);
1350 return (!chosen || selected);
1353 /**************************************************************************
1354 City style has been chosen
1355 **************************************************************************/
1356 static void races_style_callback(GtkTreeSelection *select, gpointer data)
1358 GtkTreeModel *model;
1359 GtkTreeIter it;
1361 if (gtk_tree_selection_get_selected(select, &model, &it)) {
1362 gtk_tree_model_get(model, &it, 0, &selected_style, -1);
1363 } else {
1364 selected_style = -1;
1368 /**************************************************************************
1369 User has selected some of the responses for whole nations dialog
1370 **************************************************************************/
1371 static void races_response(GtkWidget *w, gint response, gpointer data)
1373 if (response == GTK_RESPONSE_ACCEPT) {
1374 const char *s;
1376 /* This shouldn't be possible but... */
1377 if (selected_nation == -1) {
1378 return;
1381 if (selected_sex == -1) {
1382 output_window_append(ftc_client, _("You must select your sex."));
1383 return;
1386 if (selected_style == -1) {
1387 output_window_append(ftc_client, _("You must select your style."));
1388 return;
1391 s = gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN(races_leader))));
1393 /* Perform a minimum of sanity test on the name. */
1394 /* This could call is_allowed_player_name if it were available. */
1395 if (strlen(s) == 0) {
1396 output_window_append(ftc_client, _("You must type a legal name."));
1397 return;
1400 dsend_packet_nation_select_req(&client.conn,
1401 player_number(races_player), selected_nation,
1402 selected_sex, s,
1403 selected_style);
1404 } else if (response == GTK_RESPONSE_NO) {
1405 dsend_packet_nation_select_req(&client.conn,
1406 player_number(races_player),
1407 -1, FALSE, "", 0);
1410 popdown_races_dialog();
1414 /**************************************************************************
1415 Adjust tax rates from main window
1416 **************************************************************************/
1417 gboolean taxrates_callback(GtkWidget * w, GdkEventButton * ev, gpointer data)
1419 common_taxrates_callback((size_t) data);
1420 return TRUE;
1423 /****************************************************************************
1424 Pops up a dialog to confirm upgrading of the unit.
1425 ****************************************************************************/
1426 void popup_upgrade_dialog(struct unit_list *punits)
1428 GtkWidget *shell;
1429 char buf[512];
1431 if (!punits || unit_list_size(punits) == 0) {
1432 return;
1435 if (!get_units_upgrade_info(buf, sizeof(buf), punits)) {
1436 shell = gtk_message_dialog_new(NULL, 0,
1437 GTK_MESSAGE_INFO, GTK_BUTTONS_CLOSE,
1438 "%s", buf);
1439 gtk_window_set_title(GTK_WINDOW(shell), _("Upgrade Unit!"));
1440 setup_dialog(shell, toplevel);
1441 g_signal_connect(shell, "response", G_CALLBACK(gtk_widget_destroy),
1442 NULL);
1443 gtk_window_present(GTK_WINDOW(shell));
1444 } else {
1445 shell = gtk_message_dialog_new(NULL, 0,
1446 GTK_MESSAGE_QUESTION, GTK_BUTTONS_YES_NO,
1447 "%s", buf);
1448 gtk_window_set_title(GTK_WINDOW(shell), _("Upgrade Obsolete Units"));
1449 setup_dialog(shell, toplevel);
1450 gtk_dialog_set_default_response(GTK_DIALOG(shell), GTK_RESPONSE_YES);
1452 if (gtk_dialog_run(GTK_DIALOG(shell)) == GTK_RESPONSE_YES) {
1453 unit_list_iterate(punits, punit) {
1454 request_unit_upgrade(punit);
1455 } unit_list_iterate_end;
1457 gtk_widget_destroy(shell);
1461 /****************************************************************************
1462 Pops up a dialog to confirm disband of the unit(s).
1463 ****************************************************************************/
1464 void popup_disband_dialog(struct unit_list *punits)
1466 GtkWidget *shell;
1467 char buf[512];
1469 if (!punits || unit_list_size(punits) == 0) {
1470 return;
1473 if (!get_units_disband_info(buf, sizeof(buf), punits)) {
1474 shell = gtk_message_dialog_new(NULL, 0,
1475 GTK_MESSAGE_INFO, GTK_BUTTONS_CLOSE,
1476 "%s", buf);
1477 gtk_window_set_title(GTK_WINDOW(shell), _("Disband Units"));
1478 setup_dialog(shell, toplevel);
1479 g_signal_connect(shell, "response", G_CALLBACK(gtk_widget_destroy),
1480 NULL);
1481 gtk_window_present(GTK_WINDOW(shell));
1482 } else {
1483 shell = gtk_message_dialog_new(NULL, 0,
1484 GTK_MESSAGE_QUESTION, GTK_BUTTONS_YES_NO,
1485 "%s", buf);
1486 gtk_window_set_title(GTK_WINDOW(shell), _("Disband Units"));
1487 setup_dialog(shell, toplevel);
1488 gtk_dialog_set_default_response(GTK_DIALOG(shell), GTK_RESPONSE_YES);
1490 if (gtk_dialog_run(GTK_DIALOG(shell)) == GTK_RESPONSE_YES) {
1491 unit_list_iterate(punits, punit) {
1492 if (!unit_has_type_flag(punit, UTYF_UNDISBANDABLE)) {
1493 request_unit_disband(punit);
1495 } unit_list_iterate_end;
1497 gtk_widget_destroy(shell);
1501 /**********************************************************************
1502 This function is called when the client disconnects or the game is
1503 over. It should close all dialog windows for that game.
1504 ***********************************************************************/
1505 void popdown_all_game_dialogs(void)
1507 gui_dialog_destroy_all();
1508 property_editor_popdown(editprop_get_property_editor());
1509 unit_select_dialog_popdown();
1512 /****************************************************************
1513 Player has gained a new tech.
1514 *****************************************************************/
1515 void show_tech_gained_dialog(Tech_type_id tech)
1517 const struct advance *padvance = valid_advance_by_number(tech);
1519 if (NULL != padvance
1520 && (gui_options.gui_gtk3_popup_tech_help == GUI_POPUP_TECH_HELP_ENABLED
1521 || (gui_options.gui_gtk3_popup_tech_help == GUI_POPUP_TECH_HELP_RULESET
1522 && game.control.popup_tech_help))) {
1523 popup_help_dialog_typed(advance_name_translation(padvance), HELP_TECH);
1527 /****************************************************************
1528 Show tileset error dialog. It's blocking as client will
1529 shutdown as soon as this function returns.
1530 *****************************************************************/
1531 void show_tileset_error(const char *msg)
1533 if (is_gui_up()) {
1534 GtkWidget *dialog;
1536 dialog = gtk_message_dialog_new(NULL, GTK_DIALOG_DESTROY_WITH_PARENT, GTK_MESSAGE_ERROR,
1537 GTK_BUTTONS_CLOSE,
1538 _("Tileset problem, it's probably incompatible with the ruleset:\n%s"),
1539 msg);
1540 setup_dialog(dialog, toplevel);
1542 gtk_dialog_run(GTK_DIALOG(dialog));
1544 gtk_widget_destroy(dialog);
1548 /****************************************************************
1549 Give a warning when user is about to edit scenario with manually
1550 set properties.
1551 *****************************************************************/
1552 bool handmade_scenario_warning(void)
1554 /* Just tell the client common code to handle this. */
1555 return FALSE;
1558 /***************************************************************************
1559 Popup detailed information about battle or save information for
1560 some kind of statistics
1561 ***************************************************************************/
1562 void popup_combat_info(int attacker_unit_id, int defender_unit_id,
1563 int attacker_hp, int defender_hp,
1564 bool make_winner_veteran)