Qt client - remove right click menu from end turn sidebar
[freeciv.git] / client / gui-gtk-2.0 / dialogs.c
blobe576482df34b7b0a67a216f7dd40e27712c8c725
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-2.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_vbox_new(FALSE, 2);
115 gtk_box_pack_start(GTK_BOX(shell->vbox), vbox, TRUE, TRUE, 0);
117 headline_label = gtk_label_new(headline);
118 gtk_box_pack_start(GTK_BOX(vbox), headline_label, FALSE, FALSE, 0);
119 gtk_widget_set_name(headline_label, "notify_label");
121 gtk_label_set_justify(GTK_LABEL(headline_label), GTK_JUSTIFY_LEFT);
122 gtk_misc_set_alignment(GTK_MISC(headline_label), 0.0, 0.0);
124 sw = gtk_scrolled_window_new(NULL, NULL);
125 gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(sw),
126 GTK_SHADOW_ETCHED_IN);
127 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(sw),
128 GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
129 label = gtk_label_new(lines);
130 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(sw), label);
132 gtk_widget_set_name(label, "notify_label");
133 gtk_label_set_justify(GTK_LABEL(label), GTK_JUSTIFY_LEFT);
134 gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.0);
136 gtk_box_pack_start(GTK_BOX(vbox), sw, TRUE, TRUE, 0);
138 gui_dialog_show_all(shell);
140 gui_dialog_set_default_size(shell, -1, 265);
141 gui_dialog_present(shell);
143 shell = NULL;
146 /****************************************************************
147 User has responded to notify dialog with possibility to
148 center (goto) on event location.
149 *****************************************************************/
150 static void notify_goto_response(GtkWidget *w, gint response)
152 struct city *pcity = NULL;
153 struct tile *ptile = g_object_get_data(G_OBJECT(w), "tile");
155 switch (response) {
156 case 1:
157 center_tile_mapcanvas(ptile);
158 break;
159 case 2:
160 pcity = tile_city(ptile);
162 if (gui_options.center_when_popup_city) {
163 center_tile_mapcanvas(ptile);
166 if (pcity) {
167 popup_city_dialog(pcity);
169 break;
171 gtk_widget_destroy(w);
174 /****************************************************************
175 User clicked close for connect message dialog
176 *****************************************************************/
177 static void notify_connect_msg_response(GtkWidget *w, gint response)
179 gtk_widget_destroy(w);
182 /**************************************************************************
183 Popup a dialog to display information about an event that has a
184 specific location. The user should be given the option to goto that
185 location.
186 **************************************************************************/
187 void popup_notify_goto_dialog(const char *headline, const char *lines,
188 const struct text_tag_list *tags,
189 struct tile *ptile)
191 GtkWidget *shell, *label, *goto_command, *popcity_command;
193 shell = gtk_dialog_new_with_buttons(headline,
194 NULL,
196 NULL);
197 setup_dialog(shell, toplevel);
198 gtk_dialog_set_default_response(GTK_DIALOG(shell), GTK_RESPONSE_CLOSE);
199 gtk_window_set_position(GTK_WINDOW(shell), GTK_WIN_POS_CENTER_ON_PARENT);
201 label = gtk_label_new(lines);
202 gtk_container_add(GTK_CONTAINER(GTK_DIALOG(shell)->vbox), label);
203 gtk_widget_show(label);
205 goto_command = gtk_stockbutton_new(GTK_STOCK_JUMP_TO,
206 _("Goto _Location"));
207 gtk_dialog_add_action_widget(GTK_DIALOG(shell), goto_command, 1);
208 gtk_widget_show(goto_command);
210 popcity_command = gtk_stockbutton_new(GTK_STOCK_ZOOM_IN,
211 _("I_nspect City"));
212 gtk_dialog_add_action_widget(GTK_DIALOG(shell), popcity_command, 2);
213 gtk_widget_show(popcity_command);
215 gtk_dialog_add_button(GTK_DIALOG(shell), GTK_STOCK_CLOSE,
216 GTK_RESPONSE_CLOSE);
218 if (!ptile) {
219 gtk_widget_set_sensitive(goto_command, FALSE);
220 gtk_widget_set_sensitive(popcity_command, FALSE);
221 } else {
222 struct city *pcity;
224 pcity = tile_city(ptile);
225 gtk_widget_set_sensitive(popcity_command,
226 (NULL != pcity && city_owner(pcity) == client.conn.playing));
229 g_object_set_data(G_OBJECT(shell), "tile", ptile);
231 g_signal_connect(shell, "response", G_CALLBACK(notify_goto_response), NULL);
232 gtk_widget_show(shell);
235 /**************************************************************************
236 Popup a dialog to display connection message from server.
237 **************************************************************************/
238 void popup_connect_msg(const char *headline, const char *message)
240 GtkWidget *shell, *label;
242 shell = gtk_dialog_new_with_buttons(headline,
243 NULL,
245 NULL);
246 setup_dialog(shell, toplevel);
247 gtk_dialog_set_default_response(GTK_DIALOG(shell), GTK_RESPONSE_CLOSE);
248 gtk_window_set_position(GTK_WINDOW(shell), GTK_WIN_POS_CENTER_ON_PARENT);
250 label = gtk_label_new(message);
251 gtk_label_set_selectable(GTK_LABEL(label), 1);
253 gtk_container_add(GTK_CONTAINER(GTK_DIALOG(shell)->vbox), label);
254 gtk_widget_show(label);
256 gtk_dialog_add_button(GTK_DIALOG(shell), GTK_STOCK_CLOSE,
257 GTK_RESPONSE_CLOSE);
259 g_signal_connect(shell, "response", G_CALLBACK(notify_connect_msg_response),
260 NULL);
261 gtk_widget_show(shell);
264 /****************************************************************
265 User has responded to revolution dialog
266 *****************************************************************/
267 static void revolution_response(GtkWidget *w, gint response, gpointer data)
269 struct government *government = data;
271 if (response == GTK_RESPONSE_YES) {
272 if (!government) {
273 start_revolution();
274 } else {
275 set_government_choice(government);
278 if (w) {
279 gtk_widget_destroy(w);
283 /****************************************************************
284 Popup revolution dialog for user
285 *****************************************************************/
286 void popup_revolution_dialog(struct government *government)
288 static GtkWidget *shell = NULL;
290 if (0 > client.conn.playing->revolution_finishes) {
291 if (!shell) {
292 shell = gtk_message_dialog_new(NULL,
294 GTK_MESSAGE_WARNING,
295 GTK_BUTTONS_YES_NO,
296 _("You say you wanna revolution?"));
297 gtk_window_set_title(GTK_WINDOW(shell), _("Revolution!"));
298 setup_dialog(shell, toplevel);
300 g_signal_connect(shell, "destroy",
301 G_CALLBACK(gtk_widget_destroyed), &shell);
303 g_signal_connect(shell, "response",
304 G_CALLBACK(revolution_response), government);
306 gtk_window_present(GTK_WINDOW(shell));
307 } else {
308 revolution_response(shell, GTK_RESPONSE_YES, government);
313 /***********************************************************************
314 NB: 'data' is a value of enum tile_special_type casted to a pointer.
315 ***********************************************************************/
316 static void pillage_callback(GtkWidget *w, gpointer data)
318 struct unit *punit;
319 int what = GPOINTER_TO_INT(data);
321 punit = game_unit_by_number(unit_to_use_to_pillage);
322 if (punit) {
323 struct extra_type *target = extra_by_number(what);
325 request_new_unit_activity_targeted(punit, ACTIVITY_PILLAGE,
326 target);
330 /****************************************************************
331 Pillage dialog destroyed
332 *****************************************************************/
333 static void pillage_destroy_callback(GtkWidget *w, gpointer data)
335 is_showing_pillage_dialog = FALSE;
338 /****************************************************************
339 Opens pillage dialog listing possible pillage targets.
340 *****************************************************************/
341 void popup_pillage_dialog(struct unit *punit, bv_extras extras)
343 GtkWidget *shl;
345 if (!is_showing_pillage_dialog) {
346 struct extra_type *tgt;
348 is_showing_pillage_dialog = TRUE;
349 unit_to_use_to_pillage = punit->id;
351 shl = choice_dialog_start(GTK_WINDOW(toplevel),
352 _("What To Pillage"),
353 _("Select what to pillage:"));
355 while ((tgt = get_preferred_pillage(extras))) {
356 int what;
358 what = extra_index(tgt);
359 BV_CLR(extras, what);
361 choice_dialog_add(shl, extra_name_translation(tgt),
362 G_CALLBACK(pillage_callback),
363 GINT_TO_POINTER(what),
364 FALSE, NULL);
367 choice_dialog_add(shl, GTK_STOCK_CANCEL, 0, 0, FALSE, NULL);
369 choice_dialog_end(shl);
371 g_signal_connect(shl, "destroy", G_CALLBACK(pillage_destroy_callback),
372 NULL);
376 /*****************************************************************************
377 Popup unit selection dialog. It is a wrapper for the main function; see
378 unitselect.c:unit_select_dialog_popup_main().
379 *****************************************************************************/
380 void unit_select_dialog_popup(struct tile *ptile)
382 unit_select_dialog_popup_main(ptile, TRUE);
385 /*****************************************************************************
386 Update unit selection dialog. It is a wrapper for the main function; see
387 unitselect.c:unit_select_dialog_popup_main().
388 *****************************************************************************/
389 void unit_select_dialog_update_real(void)
391 unit_select_dialog_popup_main(NULL, FALSE);
394 /*****************************************************************************
395 NATION SELECTION DIALOG
396 *****************************************************************************/
397 /*****************************************************************************
398 Return the GtkTreePath for a given nation on the specified list, or NULL
399 if it's not there at all.
400 Caller must free with gtk_tree_path_free().
401 *****************************************************************************/
402 static GtkTreePath *path_to_nation_on_list(Nation_type_id nation,
403 GtkTreeView *list)
405 if (nation == -1 || list == NULL) {
406 return NULL;
407 } else {
408 GtkTreeModel *model = gtk_tree_view_get_model(list);
409 GtkTreeIter iter;
410 GtkTreePath *path = NULL;
411 gtk_tree_model_get_iter_first(model, &iter);
412 do {
413 int nation_of_row;
414 gtk_tree_model_get(model, &iter, 0, &nation_of_row, -1);
415 if (nation == nation_of_row) {
416 path = gtk_tree_model_get_path(model, &iter);
417 break;
419 } while (gtk_tree_model_iter_next(model, &iter));
420 return path;
424 /*****************************************************************************
425 Make sure the given nation is selected in the list on a given groups
426 notebook tab, if it's present on that tab.
427 Intended for synchronising the tabs to the current selection, so does not
428 disturb the controls on the right-hand side.
429 *****************************************************************************/
430 static void select_nation_on_tab(GtkWidget *tab_list, int nation)
432 /* tab_list is a GtkTreeView (not its enclosing GtkScrolledWindow). */
433 GtkTreeView *list = GTK_TREE_VIEW(tab_list);
434 GtkTreeSelection *select = gtk_tree_view_get_selection(GTK_TREE_VIEW(list));
435 GtkTreePath *path = path_to_nation_on_list(nation, list);
436 /* Suppress normal effects of selection change to avoid loops. */
437 g_signal_handlers_block_by_func(select, races_nation_callback, NULL);
438 if (path) {
439 /* Found nation on this list. */
440 /* Avoid disturbing tabs that already have the correct selection. */
441 if (!gtk_tree_selection_path_is_selected(select, path)) {
442 /* Set cursor -- this will cause the nation to be selected */
443 gtk_tree_view_set_cursor(list, path, NULL, FALSE);
444 /* Make sure selected nation is visible in list */
445 gtk_tree_view_scroll_to_cell(list, path, NULL, FALSE, 0, 0);
447 } else {
448 /* Either no nation was selected, or the nation is not mentioned in
449 * this tab. Either way we want to end up with no selection. */
450 /* If there was a previous selection, reset the cursor */
451 if (gtk_tree_selection_get_selected(select, NULL, NULL)) {
452 GtkTreePath *cursorpath = gtk_tree_path_new_first();
453 gtk_tree_view_set_cursor(list, cursorpath, NULL, FALSE);
454 gtk_tree_path_free(cursorpath);
456 gtk_tree_selection_unselect_all(select);
458 gtk_tree_path_free(path);
459 /* Re-enable selection change side-effects */
460 g_signal_handlers_unblock_by_func(select, races_nation_callback, NULL);
463 /*****************************************************************************
464 Select the given nation in the nation lists in the left-hand-side notebook.
465 *****************************************************************************/
466 static void sync_tabs_to_nation(int nation)
468 /* Ensure that all tabs are in sync with the new selection */
469 int i;
470 for (i = 0; i <= nation_group_count(); i++) {
471 if (races_nation_list[i]) {
472 select_nation_on_tab(races_nation_list[i], nation);
477 /*****************************************************************************
478 Populates leader list.
479 If no nation selected, blanks it.
480 *****************************************************************************/
481 static void populate_leader_list(void)
483 int i;
484 GtkListStore *model =
485 GTK_LIST_STORE(gtk_combo_box_get_model(GTK_COMBO_BOX(races_leader)));
487 i = 0;
488 gtk_list_store_clear(model);
489 if (selected_nation >= 0) {
490 nation_leader_list_iterate(nation_leaders(nation_by_number
491 (selected_nation)), pleader) {
492 const char *leader_name = nation_leader_name(pleader);
493 GtkTreeIter iter; /* unused */
495 gtk_list_store_insert_with_values(model, &iter, i, 0, leader_name, -1);
496 i++;
497 } nation_leader_list_iterate_end;
501 /*****************************************************************************
502 Update dialog state by selecting a nation and choosing values for its
503 parameters, and update the right-hand side of the dialog accordingly.
504 If 'leadername' is NULL, pick a random leader name and sex from the
505 nation's list (ignoring the 'is_male' parameter).
506 *****************************************************************************/
507 static void select_nation(int nation,
508 const char *leadername, bool is_male,
509 int style_id)
511 selected_nation = nation;
513 /* Refresh the available leaders. */
514 populate_leader_list();
516 if (selected_nation != -1) {
518 /* Select leader name and sex. */
519 if (leadername) {
520 gtk_entry_set_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN(races_leader))),
521 leadername);
522 /* Assume is_male is valid too. */
523 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(races_sex[is_male]),
524 TRUE);
525 } else {
526 int idx = fc_rand(nation_leader_list_size(
527 nation_leaders(nation_by_number(selected_nation))));
528 gtk_combo_box_set_active(GTK_COMBO_BOX(races_leader), idx);
529 /* This also updates the leader sex, eventually. */
532 /* Select the appropriate city style entry. */
534 int i;
535 int j = 0;
536 GtkTreePath *path;
538 styles_iterate(pstyle) {
539 i = basic_city_style_for_style(pstyle);
541 if (i >= 0 && i < style_id) {
542 j++;
543 } else {
544 break;
546 } styles_iterate_end;
548 path = gtk_tree_path_new();
549 gtk_tree_path_append_index(path, j);
550 gtk_tree_view_set_cursor(GTK_TREE_VIEW(races_style_list), path,
551 NULL, FALSE);
552 gtk_tree_path_free(path);
555 /* Update nation description. */
557 char buf[4096];
558 helptext_nation(buf, sizeof(buf),
559 nation_by_number(selected_nation), NULL);
560 gtk_text_buffer_set_text(races_text, buf, -1);
563 gtk_widget_set_sensitive(races_properties, TRUE);
564 /* Once we've made a nation selection, allow user to ok */
565 gtk_dialog_set_response_sensitive(GTK_DIALOG(races_shell),
566 GTK_RESPONSE_ACCEPT, TRUE);
567 } else {
568 /* No nation selected. Blank properties and make controls insensitive. */
569 /* Leader name */
570 gtk_entry_set_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN(races_leader))),
571 "");
572 /* Leader sex (*shrug*) */
573 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(races_sex[0]), TRUE);
574 /* City style */
576 GtkTreeSelection* select
577 = gtk_tree_view_get_selection(GTK_TREE_VIEW(races_style_list));
578 gtk_tree_selection_unselect_all(select);
580 /* Nation description */
581 gtk_text_buffer_set_text(races_text, "", 0);
583 gtk_widget_set_sensitive(races_properties, FALSE);
584 /* Don't allow OK without a selection
585 * (user can still do "Random Nation") */
586 gtk_dialog_set_response_sensitive(GTK_DIALOG(races_shell),
587 GTK_RESPONSE_ACCEPT, FALSE);
590 /* Update notebook to reflect the current selection */
591 sync_tabs_to_nation(selected_nation);
594 /*****************************************************************************
595 Creates a list of currently-pickable nations in the given group
596 Inserts appropriate gtk_tree_view into races_nation_list[index] (or NULL if
597 the group has no nations)
598 If group == NULL, create a list of all nations
599 *****************************************************************************/
600 static GtkWidget* create_list_of_nations_in_group(struct nation_group* group,
601 int index)
603 GtkWidget *sw = NULL;
604 GtkListStore *store = NULL;
605 GtkWidget *list = NULL;
607 /* Populate nation list store. */
608 nations_iterate(pnation) {
609 bool used;
610 GdkPixbuf *img;
611 GtkTreeIter it;
612 GValue value = { 0, };
614 if (!is_nation_playable(pnation) || !is_nation_pickable(pnation)) {
615 continue;
618 if (NULL != group && !nation_is_in_group(pnation, group)) {
619 continue;
622 /* Only create tab on demand -- we don't want it if there aren't any
623 * currently pickable nations in this group. */
624 if (sw == NULL) {
625 GtkTreeSelection *select;
626 GtkCellRenderer *render;
627 GtkTreeViewColumn *column;
629 store = gtk_list_store_new(4, G_TYPE_INT, G_TYPE_BOOLEAN,
630 GDK_TYPE_PIXBUF, G_TYPE_STRING);
631 gtk_tree_sortable_set_sort_column_id(GTK_TREE_SORTABLE(store),
632 3, GTK_SORT_ASCENDING);
634 list = gtk_tree_view_new_with_model(GTK_TREE_MODEL(store));
635 gtk_tree_view_set_search_column(GTK_TREE_VIEW(list), 3);
636 gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(list), FALSE);
637 g_object_unref(store);
639 select = gtk_tree_view_get_selection(GTK_TREE_VIEW(list));
640 g_signal_connect(select, "changed", G_CALLBACK(races_nation_callback),
641 NULL);
642 gtk_tree_selection_set_select_function(select, races_selection_func,
643 NULL, NULL);
645 sw = gtk_scrolled_window_new(NULL, NULL);
646 gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(sw),
647 GTK_SHADOW_ETCHED_IN);
648 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(sw),
649 GTK_POLICY_NEVER, GTK_POLICY_ALWAYS);
650 gtk_container_add(GTK_CONTAINER(sw), list);
652 render = gtk_cell_renderer_pixbuf_new();
653 column = gtk_tree_view_column_new_with_attributes("Flag", render,
654 "pixbuf", 2, NULL);
655 gtk_tree_view_append_column(GTK_TREE_VIEW(list), column);
656 render = gtk_cell_renderer_text_new();
657 column = gtk_tree_view_column_new_with_attributes("Nation", render,
658 "text", 3, "strikethrough", 1, NULL);
659 gtk_tree_view_append_column(GTK_TREE_VIEW(list), column);
662 gtk_list_store_append(store, &it);
664 used = (pnation->player != NULL && pnation->player != races_player);
665 gtk_list_store_set(store, &it, 0, nation_number(pnation), 1, used, -1);
666 img = get_flag(pnation);
667 if (img != NULL) {
668 gtk_list_store_set(store, &it, 2, img, -1);
669 g_object_unref(img);
672 g_value_init(&value, G_TYPE_STRING);
673 g_value_set_static_string(&value, nation_adjective_translation(pnation));
674 gtk_list_store_set_value(store, &it, 3, &value);
675 g_value_unset(&value);
676 } nations_iterate_end;
678 races_nation_list[index] = list;
679 return sw;
682 /*****************************************************************************
683 Creates lists of nations for left side of nation selection dialog
684 *****************************************************************************/
685 static void create_nation_selection_lists(void)
687 GtkWidget *nation_list;
688 GtkWidget *group_name_label;
690 int i;
692 for (i = 0; i < nation_group_count(); i++) {
693 struct nation_group* group = (nation_group_by_number(i));
694 if (is_nation_group_hidden(group)) {
695 races_nation_list[i] = NULL;
696 continue;
698 nation_list = create_list_of_nations_in_group(group, i);
699 if (nation_list) {
700 group_name_label = gtk_label_new(nation_group_name_translation(group));
701 gtk_notebook_append_page(GTK_NOTEBOOK(races_notebook), nation_list,
702 group_name_label);
706 nation_list = create_list_of_nations_in_group(NULL, nation_group_count());
707 /* Even this list can be empty if there are no pickable nations (due to
708 * a combination of start position and nationset restrictions). */
709 if (nation_list) {
710 group_name_label = gtk_label_new(_("All"));
711 gtk_notebook_append_page(GTK_NOTEBOOK(races_notebook), nation_list,
712 group_name_label);
716 /*****************************************************************************
717 The server has changed the set of selectable nations.
718 Update any current nations dialog accordingly.
719 *****************************************************************************/
720 void races_update_pickable(bool nationset_change)
722 int tab, groupidx;
724 if (!races_shell) {
725 return;
728 /* Save selected tab */
729 tab = gtk_notebook_get_current_page(GTK_NOTEBOOK(races_notebook));
730 if (tab != -1) {
731 int i = 0;
732 groupidx = 0;
733 /* Turn tab index into a nation group index (they're not always equal,
734 * as some groups may not currently have tabs). */
735 do {
736 while (groupidx <= nation_group_count()
737 && races_nation_list[groupidx] == NULL) {
738 groupidx++;
740 fc_assert_action(groupidx <= nation_group_count(), break);
741 /* Nation group 'groupidx' is what's displayed on the i'th tab */
742 if (i == tab) {
743 break;
745 i++;
746 groupidx++;
747 } while(1);
748 } else {
749 /* No tabs currently */
750 groupidx = -1;
753 /* selected_nation already contains currently selected nation; however,
754 * it may no longer be a valid choice */
755 if (selected_nation != -1
756 && !is_nation_pickable(nation_by_number(selected_nation))) {
757 select_nation(-1, NULL, FALSE, 0);
760 /* Delete all list stores, treeviews, tabs */
761 while (gtk_notebook_get_n_pages(GTK_NOTEBOOK(races_notebook)) > 0) {
762 gtk_notebook_remove_page(GTK_NOTEBOOK(races_notebook), -1);
765 /* (Re)create all of them */
766 create_nation_selection_lists();
768 /* Can't set current tab before child widget is visible */
769 gtk_widget_show_all(GTK_WIDGET(races_notebook));
771 /* Restore selected tab */
772 if (groupidx != -1 && races_nation_list[groupidx] != NULL) {
773 int i;
774 tab = 0;
775 for (i = 0; i < groupidx; i++) {
776 if (races_nation_list[i] != NULL) {
777 tab++;
780 gtk_notebook_set_current_page(GTK_NOTEBOOK(races_notebook), tab);
783 /* Restore selected nation */
784 sync_tabs_to_nation(selected_nation);
787 /*****************************************************************************
788 Sync nationset control with the current state of the server.
789 *****************************************************************************/
790 void nationset_sync_to_server(const char *nationset)
792 if (nationsets_chooser) {
793 struct nation_set *set = nation_set_by_setting_value(nationset);
794 gtk_combo_box_set_active(GTK_COMBO_BOX(nationsets_chooser),
795 nation_set_index(set));
799 /*****************************************************************************
800 Called when the nationset control's value has changed.
801 *****************************************************************************/
802 static void nationset_callback(GtkComboBox *b, gpointer data)
804 GtkTreeIter iter;
805 if (gtk_combo_box_get_active_iter(b, &iter)) {
806 struct option *poption = optset_option_by_name(server_optset, "nationset");
807 gchar *rule_name;
808 gtk_tree_model_get(gtk_combo_box_get_model(b), &iter,
809 0, &rule_name, -1);
810 /* Suppress propagation of an option value equivalent to the current
811 * server state, after canonicalisation, to avoid loops from
812 * nationset_sync_to_server().
813 * (HACK: relies on local Gtk "changed" signal getting here before
814 * server response.) */
815 if (nation_set_by_setting_value(option_str_get(poption))
816 != nation_set_by_rule_name(rule_name)) {
817 option_str_set(poption, rule_name);
819 FC_FREE(rule_name);
823 /*****************************************************************************
824 Create nations dialog
825 *****************************************************************************/
826 static void create_races_dialog(struct player *pplayer)
828 GtkWidget *shell;
829 GtkWidget *cmd;
830 GtkWidget *vbox, *hbox, *table;
831 GtkWidget *frame, *label, *combo;
832 GtkWidget *text;
833 GtkWidget *notebook;
835 GtkWidget *sw;
836 GtkWidget *list;
837 GtkListStore *store;
838 GtkCellRenderer *render;
839 GtkTreeViewColumn *column;
841 int i;
842 char *title;
844 /* Init. */
845 selected_nation = -1;
847 if (C_S_RUNNING == client_state()) {
848 title = _("Edit Nation");
849 } else if (NULL != pplayer && pplayer == client.conn.playing) {
850 title = _("What Nation Will You Be?");
851 } else {
852 title = _("Pick Nation");
855 shell = gtk_dialog_new_with_buttons(title,
856 NULL,
858 GTK_STOCK_CANCEL,
859 GTK_RESPONSE_CANCEL,
860 _("_Random Nation"),
861 GTK_RESPONSE_NO, /* arbitrary */
862 GTK_STOCK_OK,
863 GTK_RESPONSE_ACCEPT,
864 NULL);
865 races_shell = shell;
866 races_player = pplayer;
867 setup_dialog(shell, toplevel);
869 gtk_window_set_position(GTK_WINDOW(shell), GTK_WIN_POS_CENTER_ON_PARENT);
870 gtk_window_set_default_size(GTK_WINDOW(shell), -1, 590);
872 frame = gtk_frame_new(_("Select a nation"));
873 gtk_container_add(GTK_CONTAINER(GTK_DIALOG(shell)->vbox), frame);
875 hbox = gtk_hbox_new(FALSE, 18);
876 gtk_container_set_border_width(GTK_CONTAINER(hbox), 3);
877 gtk_container_add(GTK_CONTAINER(frame), hbox);
879 /* Left side: nation list */
881 GtkWidget* nation_selection_list = gtk_vbox_new(FALSE, 2);
882 GtkWidget *nsetctrls = NULL;
883 nationsets_chooser = NULL;
885 /* Nationset selector dropdown */
886 /* Only present this if there is more than one choice.
887 * (If ruleset is changed, possibly changing the number of available sets
888 * and invalidating this decision, then dialog will be popped down.) */
889 if (nation_set_count() > 1) {
890 GtkListStore *sets_model = gtk_list_store_new(4, G_TYPE_STRING,
891 G_TYPE_STRING,
892 G_TYPE_STRING,
893 G_TYPE_STRING);
894 GtkCellRenderer *renderer;
895 nation_sets_iterate(pset) {
896 /* Index in list store must match nation_set_index(). */
897 gchar *escaped;
898 struct astring s = ASTRING_INIT;
899 int num_nations = 0;
900 nations_iterate(pnation) {
901 if (is_nation_playable(pnation) && nation_is_in_set(pnation, pset)) {
902 num_nations++;
904 } nations_iterate_end;
905 escaped = g_markup_escape_text(nation_set_name_translation(pset), -1);
906 /* TRANS: nation set name followed by number of playable nations;
907 * <b> and </b> are Pango markup and should be left alone */
908 astr_set(&s, PL_("<b>%s</b> (%d nation)",
909 "<b>%s</b> (%d nations)", num_nations),
910 escaped, num_nations);
911 g_free(escaped);
912 if (strlen(nation_set_description(pset)) > 0) {
913 /* While in principle it would be better to get Gtk to wrap the
914 * drop-down (e.g. via "wrap-width" property), there's no way
915 * to specify the indentation we want. So we do it ourselves. */
916 char *desc = fc_strdup(_(nation_set_description(pset)));
917 char *p = desc;
918 fc_break_lines(desc, 70);
919 astr_add(&s, "\n");
920 while (*p) {
921 int len = strcspn(p, "\n");
922 if (p[len] == '\n') len++;
923 escaped = g_markup_escape_text(p, len);
924 astr_add(&s, "\t%s", escaped);
925 g_free(escaped);
926 p += len;
928 FC_FREE(desc);
930 gtk_list_store_insert_with_values(sets_model, NULL, -1,
931 0, nation_set_rule_name(pset),
932 1, astr_str(&s),
933 2, nation_set_name_translation(pset),
934 -1);
935 astr_free(&s);
936 } nation_sets_iterate_end;
938 /* We want a combo box where the button displays just the set name,
939 * but the dropdown displays the expanded description.
940 * The entry displays the set name. */
941 nationsets_chooser
942 = gtk_combo_box_entry_new_with_model(GTK_TREE_MODEL(sets_model), 2);
943 g_object_unref(G_OBJECT(sets_model));
945 /* Do our best to turn the text-entry widget into something more
946 * like a cell-view: disable editing, and focusing (which removes
947 * the caret). */
948 GtkWidget *entry = gtk_bin_get_child(GTK_BIN(nationsets_chooser));
949 gtk_editable_set_editable(GTK_EDITABLE(entry), FALSE);
950 gtk_widget_set_can_focus(entry, FALSE);
952 /* The dropdown displays the marked-up description. */
953 renderer = gtk_cell_renderer_text_new();
954 gtk_cell_layout_clear(GTK_CELL_LAYOUT(nationsets_chooser));
955 gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(nationsets_chooser),
956 renderer, TRUE);
957 gtk_cell_layout_set_attributes(GTK_CELL_LAYOUT(nationsets_chooser),
958 renderer, "markup", 1, NULL);
959 g_signal_connect(nationsets_chooser, "destroy",
960 G_CALLBACK(gtk_widget_destroyed), &nationsets_chooser);
961 g_signal_connect(nationsets_chooser, "changed",
962 G_CALLBACK(nationset_callback), NULL);
964 /* Populate initially from client's view of server setting */
965 struct option *poption = optset_option_by_name(server_optset,
966 "nationset");
967 if (poption) {
968 nationset_sync_to_server(option_str_get(poption));
972 nsetctrls = gtk_hbox_new(FALSE, 2);
973 label = g_object_new(GTK_TYPE_LABEL,
974 "use-underline", TRUE,
975 "label", _("_Nation Set:"),
976 "xalign", 0.0,
977 "yalign", 0.5,
978 NULL);
979 gtk_label_set_mnemonic_widget(GTK_LABEL(label), nationsets_chooser);
980 gtk_box_pack_start(GTK_BOX(nsetctrls), label, FALSE, FALSE, 0);
981 gtk_box_pack_start(GTK_BOX(nsetctrls), nationsets_chooser,
982 TRUE, TRUE, 0);
985 if (nsetctrls) {
986 gtk_box_pack_start(GTK_BOX(nation_selection_list), nsetctrls,
987 FALSE, FALSE, 0);
990 races_notebook = gtk_notebook_new();
991 gtk_notebook_set_tab_pos(GTK_NOTEBOOK(races_notebook), GTK_POS_LEFT);
993 /* Suppress notebook tabs if there will be only one ("All") */
995 bool show_groups = FALSE;
996 nation_groups_iterate(pgroup) {
997 if (!is_nation_group_hidden(pgroup)) {
998 show_groups = TRUE;
999 break;
1001 } nation_groups_iterate_end;
1002 if (!show_groups) {
1003 gtk_notebook_set_show_tabs(GTK_NOTEBOOK(races_notebook), FALSE);
1004 } else {
1005 label = g_object_new(GTK_TYPE_LABEL,
1006 "use-underline", TRUE,
1007 "label", _("Nation _Groups:"),
1008 "xalign", 0.0,
1009 "yalign", 0.5,
1010 NULL);
1011 gtk_label_set_mnemonic_widget(GTK_LABEL(label), races_notebook);
1012 gtk_box_pack_start(GTK_BOX(nation_selection_list), label,
1013 FALSE, FALSE, 0);
1014 gtk_notebook_set_show_tabs(GTK_NOTEBOOK(races_notebook), TRUE);
1018 gtk_box_pack_start(GTK_BOX(nation_selection_list), races_notebook,
1019 TRUE, TRUE, 0);
1021 /* Populate treeview */
1022 create_nation_selection_lists();
1024 gtk_container_add(GTK_CONTAINER(hbox), nation_selection_list);
1027 /* Right side. */
1028 notebook = gtk_notebook_new();
1029 gtk_notebook_set_tab_pos(GTK_NOTEBOOK(notebook), GTK_POS_BOTTOM);
1030 gtk_container_add(GTK_CONTAINER(hbox), notebook);
1032 /* Properties pane. */
1033 label = gtk_label_new_with_mnemonic(_("_Properties"));
1035 races_properties = vbox = gtk_vbox_new(FALSE, 6);
1036 gtk_container_set_border_width(GTK_CONTAINER(vbox), 6);
1037 gtk_notebook_append_page(GTK_NOTEBOOK(notebook), vbox, label);
1038 g_signal_connect(vbox, "destroy",
1039 G_CALLBACK(gtk_widget_destroyed), &races_properties);
1041 table = gtk_table_new(3, 4, FALSE);
1042 gtk_table_set_row_spacings(GTK_TABLE(table), 2);
1043 gtk_table_set_col_spacing(GTK_TABLE(table), 0, 12);
1044 gtk_table_set_col_spacing(GTK_TABLE(table), 1, 12);
1045 gtk_box_pack_start(GTK_BOX(vbox), table, TRUE, TRUE, 0);
1047 /* Leader. */
1049 GtkListStore *model = gtk_list_store_new(1, G_TYPE_STRING);
1050 combo = gtk_combo_box_entry_new_with_model(GTK_TREE_MODEL(model), 0);
1051 g_object_unref(G_OBJECT(model));
1053 races_leader = combo;
1054 label = g_object_new(GTK_TYPE_LABEL,
1055 "use-underline", TRUE,
1056 "mnemonic-widget", GTK_COMBO_BOX(combo),
1057 "label", _("_Leader:"),
1058 "xalign", 0.0,
1059 "yalign", 0.5,
1060 NULL);
1061 gtk_table_attach(GTK_TABLE(table), label, 0, 1, 0, 2, 0, 0, 0, 0);
1062 gtk_table_attach(GTK_TABLE(table), combo, 1, 3, 0, 1, 0, 0, 0, 0);
1064 cmd = gtk_radio_button_new_with_mnemonic(NULL, _("_Female"));
1065 races_sex[0] = cmd;
1066 gtk_table_attach(GTK_TABLE(table), cmd, 1, 2, 1, 2, 0, 0, 0, 0);
1068 cmd = gtk_radio_button_new_with_mnemonic_from_widget(GTK_RADIO_BUTTON(cmd),
1069 _("_Male"));
1070 races_sex[1] = cmd;
1071 gtk_table_attach(GTK_TABLE(table), cmd, 2, 3, 1, 2, 0, 0, 0, 0);
1073 /* City style. */
1074 store = gtk_list_store_new(3, G_TYPE_INT,
1075 GDK_TYPE_PIXBUF, G_TYPE_STRING);
1077 list = gtk_tree_view_new_with_model(GTK_TREE_MODEL(store));
1078 races_style_list = list;
1079 g_object_unref(store);
1080 gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(list), FALSE);
1081 g_signal_connect(gtk_tree_view_get_selection(GTK_TREE_VIEW(list)), "changed",
1082 G_CALLBACK(races_style_callback), NULL);
1084 sw = gtk_scrolled_window_new(NULL, NULL);
1085 gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(sw),
1086 GTK_SHADOW_ETCHED_IN);
1087 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(sw),
1088 GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
1089 gtk_container_add(GTK_CONTAINER(sw), list);
1090 gtk_table_attach(GTK_TABLE(table), sw, 1, 3, 2, 4,
1091 GTK_EXPAND|GTK_FILL, GTK_EXPAND|GTK_FILL, 0, 0);
1093 label = g_object_new(GTK_TYPE_LABEL,
1094 "use-underline", TRUE,
1095 "mnemonic-widget", list,
1096 "label", _("City _Styles:"),
1097 "xalign", 0.0,
1098 "yalign", 0.5,
1099 NULL);
1100 gtk_table_attach(GTK_TABLE(table), label, 0, 1, 2, 3, 0, 0, 0, 0);
1102 render = gtk_cell_renderer_pixbuf_new();
1103 column = gtk_tree_view_column_new_with_attributes(NULL, render,
1104 "pixbuf", 1, NULL);
1105 gtk_tree_view_append_column(GTK_TREE_VIEW(list), column);
1106 render = gtk_cell_renderer_text_new();
1107 column = gtk_tree_view_column_new_with_attributes(NULL, render,
1108 "text", 2, NULL);
1109 gtk_tree_view_append_column(GTK_TREE_VIEW(list), column);
1111 gtk_table_set_row_spacing(GTK_TABLE(table), 1, 12);
1113 /* Populate style store. */
1114 styles_iterate(pstyle) {
1115 GdkPixbuf *img;
1116 struct sprite *s;
1117 GtkTreeIter it;
1119 i = basic_city_style_for_style(pstyle);
1121 if (i >= 0) {
1122 gtk_list_store_append(store, &it);
1124 s = crop_blankspace(get_sample_city_sprite(tileset, i));
1125 img = sprite_get_pixbuf(s);
1126 g_object_ref(img);
1127 free_sprite(s);
1128 gtk_list_store_set(store, &it, 0, i, 1, img, 2,
1129 city_style_name_translation(i), -1);
1130 g_object_unref(img);
1132 } styles_iterate_end;
1134 /* Legend pane. */
1135 label = gtk_label_new_with_mnemonic(_("_Description"));
1137 vbox = gtk_vbox_new(FALSE, 6);
1138 gtk_container_set_border_width(GTK_CONTAINER(vbox), 6);
1139 gtk_notebook_append_page(GTK_NOTEBOOK(notebook), vbox, label);
1141 text = gtk_text_view_new();
1142 races_text = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
1143 gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(text), GTK_WRAP_WORD);
1144 gtk_text_view_set_editable(GTK_TEXT_VIEW(text), FALSE);
1145 gtk_text_view_set_cursor_visible(GTK_TEXT_VIEW(text), FALSE);
1146 gtk_text_view_set_left_margin(GTK_TEXT_VIEW(text), 6);
1147 gtk_text_view_set_right_margin(GTK_TEXT_VIEW(text), 6);
1149 gtk_box_pack_start(GTK_BOX(vbox), text, TRUE, TRUE, 0);
1151 /* Signals. */
1152 g_signal_connect(shell, "destroy",
1153 G_CALLBACK(gtk_widget_destroyed), &races_shell);
1154 g_signal_connect(shell, "response",
1155 G_CALLBACK(races_response), NULL);
1157 g_signal_connect(GTK_COMBO_BOX(races_leader), "changed",
1158 G_CALLBACK(races_leader_callback), NULL);
1160 g_signal_connect(races_sex[0], "toggled",
1161 G_CALLBACK(races_sex_callback), GINT_TO_POINTER(0));
1162 g_signal_connect(races_sex[1], "toggled",
1163 G_CALLBACK(races_sex_callback), GINT_TO_POINTER(1));
1165 /* Finish up. */
1166 gtk_dialog_set_default_response(GTK_DIALOG(shell), GTK_RESPONSE_CANCEL);
1168 /* You can't assign NO_NATION during a running game. */
1169 if (C_S_RUNNING == client_state()) {
1170 gtk_dialog_set_response_sensitive(GTK_DIALOG(shell), GTK_RESPONSE_NO,
1171 FALSE);
1174 gtk_widget_show_all(GTK_DIALOG(shell)->vbox);
1176 /* Select player's current nation in UI, if any */
1177 if (races_player->nation) {
1178 select_nation(nation_number(races_player->nation),
1179 player_name(races_player),
1180 races_player->is_male,
1181 style_number(races_player->style));
1182 /* Make sure selected nation is visible
1183 * (last page, "All", will certainly contain it) */
1184 fc_assert(gtk_notebook_get_n_pages(GTK_NOTEBOOK(races_notebook)) > 0);
1185 gtk_notebook_set_current_page(GTK_NOTEBOOK(races_notebook), -1);
1186 } else {
1187 select_nation(-1, NULL, FALSE, 0);
1191 /****************************************************************
1192 popup the dialog 10% inside the main-window
1193 *****************************************************************/
1194 void popup_races_dialog(struct player *pplayer)
1196 if (!pplayer) {
1197 return;
1200 if (!races_shell) {
1201 create_races_dialog(pplayer);
1202 gtk_window_present(GTK_WINDOW(races_shell));
1206 /****************************************************************
1207 Close nations dialog
1208 *****************************************************************/
1209 void popdown_races_dialog(void)
1211 if (races_shell) {
1212 gtk_widget_destroy(races_shell);
1215 /* We're probably starting a new game, maybe with a new ruleset.
1216 So we warn the worklist dialog. */
1217 blank_max_unit_size();
1220 /**************************************************************************
1221 Update which nations are allowed to be selected (due to e.g. another
1222 player choosing a nation).
1223 **************************************************************************/
1224 void races_toggles_set_sensitive(void)
1226 int i;
1228 if (!races_shell) {
1229 return;
1232 for (i = 0; i <= nation_group_count(); i++) {
1233 if (races_nation_list[i]) {
1234 GtkTreeView *list = GTK_TREE_VIEW(races_nation_list[i]);
1235 GtkTreeModel *model = gtk_tree_view_get_model(list);
1236 GtkTreeSelection* select = gtk_tree_view_get_selection(list);
1237 GtkTreeIter it;
1238 gboolean chosen;
1240 /* Update 'chosen' column in model */
1241 if (gtk_tree_model_get_iter_first(model, &it)) {
1242 do {
1243 int nation_no;
1244 struct nation_type *nation;
1246 gtk_tree_model_get(model, &it, 0, &nation_no, -1);
1247 nation = nation_by_number(nation_no);
1249 chosen = !is_nation_pickable(nation)
1250 || (nation->player && nation->player != races_player);
1252 gtk_list_store_set(GTK_LIST_STORE(model), &it, 1, chosen, -1);
1254 } while (gtk_tree_model_iter_next(model, &it));
1257 /* If our selection is now invalid, deselect it */
1258 if (gtk_tree_selection_get_selected(select, &model, &it)) {
1259 gtk_tree_model_get(model, &it, 1, &chosen, -1);
1261 if (chosen) {
1262 gtk_tree_selection_unselect_all(select);
1269 /*****************************************************************************
1270 Called whenever a user selects a nation in nation list
1271 *****************************************************************************/
1272 static void races_nation_callback(GtkTreeSelection *select, gpointer data)
1274 GtkTreeModel *model;
1275 GtkTreeIter it;
1277 if (gtk_tree_selection_get_selected(select, &model, &it)) {
1278 gboolean chosen;
1279 int newnation;
1281 gtk_tree_model_get(model, &it, 0, &newnation, 1, &chosen, -1);
1283 /* Only allow nations not chosen by another player */
1284 if (!chosen) {
1285 if (newnation != selected_nation) {
1286 /* Choose a random leader */
1287 select_nation(newnation, NULL, FALSE,
1288 style_number(style_of_nation(nation_by_number(newnation))));
1290 return;
1294 /* Fall-through if no valid nation selected */
1295 select_nation(-1, NULL, FALSE, 0);
1298 /**************************************************************************
1299 Leader name has been chosen
1300 **************************************************************************/
1301 static void races_leader_callback(void)
1303 const struct nation_leader *pleader;
1304 const gchar *name;
1306 name =
1307 gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN(races_leader))));
1309 if (selected_nation != -1
1310 &&(pleader = nation_leader_by_name(nation_by_number(selected_nation),
1311 name))) {
1312 selected_sex = nation_leader_is_male(pleader);
1313 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(races_sex[selected_sex]),
1314 TRUE);
1318 /**************************************************************************
1319 Leader sex has been chosen
1320 **************************************************************************/
1321 static void races_sex_callback(GtkWidget *w, gpointer data)
1323 selected_sex = GPOINTER_TO_INT(data);
1326 /**************************************************************************
1327 Determines which nations can be selected in the UI
1328 **************************************************************************/
1329 static gboolean races_selection_func(GtkTreeSelection *select,
1330 GtkTreeModel *model, GtkTreePath *path,
1331 gboolean selected, gpointer data)
1333 GtkTreeIter it;
1334 gboolean chosen;
1336 gtk_tree_model_get_iter(model, &it, path);
1337 gtk_tree_model_get(model, &it, 1, &chosen, -1);
1338 return (!chosen || selected);
1341 /**************************************************************************
1342 City style has been chosen
1343 **************************************************************************/
1344 static void races_style_callback(GtkTreeSelection *select, gpointer data)
1346 GtkTreeModel *model;
1347 GtkTreeIter it;
1349 if (gtk_tree_selection_get_selected(select, &model, &it)) {
1350 gtk_tree_model_get(model, &it, 0, &selected_style, -1);
1351 } else {
1352 selected_style = -1;
1356 /**************************************************************************
1357 User has selected some of the responses for whole nations dialog
1358 **************************************************************************/
1359 static void races_response(GtkWidget *w, gint response, gpointer data)
1361 if (response == GTK_RESPONSE_ACCEPT) {
1362 const char *s;
1364 /* This shouldn't be possible but... */
1365 if (selected_nation == -1) {
1366 return;
1369 if (selected_sex == -1) {
1370 output_window_append(ftc_client, _("You must select your sex."));
1371 return;
1374 if (selected_style == -1) {
1375 output_window_append(ftc_client, _("You must select your style."));
1376 return;
1379 s = gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN(races_leader))));
1381 /* Perform a minimum of sanity test on the name. */
1382 /* This could call is_allowed_player_name if it were available. */
1383 if (strlen(s) == 0) {
1384 output_window_append(ftc_client, _("You must type a legal name."));
1385 return;
1388 dsend_packet_nation_select_req(&client.conn,
1389 player_number(races_player), selected_nation,
1390 selected_sex, s,
1391 selected_style);
1392 } else if (response == GTK_RESPONSE_NO) {
1393 dsend_packet_nation_select_req(&client.conn,
1394 player_number(races_player),
1395 -1, FALSE, "", 0);
1398 popdown_races_dialog();
1402 /**************************************************************************
1403 Adjust tax rates from main window
1404 **************************************************************************/
1405 gboolean taxrates_callback(GtkWidget * w, GdkEventButton * ev, gpointer data)
1407 common_taxrates_callback((size_t) data);
1408 return TRUE;
1411 /****************************************************************************
1412 Pops up a dialog to confirm upgrading of the unit.
1413 ****************************************************************************/
1414 void popup_upgrade_dialog(struct unit_list *punits)
1416 GtkWidget *shell;
1417 char buf[512];
1419 if (!punits || unit_list_size(punits) == 0) {
1420 return;
1423 if (!get_units_upgrade_info(buf, sizeof(buf), punits)) {
1424 shell = gtk_message_dialog_new(NULL, 0,
1425 GTK_MESSAGE_INFO, GTK_BUTTONS_CLOSE,
1426 "%s", buf);
1427 gtk_window_set_title(GTK_WINDOW(shell), _("Upgrade Unit!"));
1428 setup_dialog(shell, toplevel);
1429 g_signal_connect(shell, "response", G_CALLBACK(gtk_widget_destroy),
1430 NULL);
1431 gtk_window_present(GTK_WINDOW(shell));
1432 } else {
1433 shell = gtk_message_dialog_new(NULL, 0,
1434 GTK_MESSAGE_QUESTION, GTK_BUTTONS_YES_NO,
1435 "%s", buf);
1436 gtk_window_set_title(GTK_WINDOW(shell), _("Upgrade Obsolete Units"));
1437 setup_dialog(shell, toplevel);
1438 gtk_dialog_set_default_response(GTK_DIALOG(shell), GTK_RESPONSE_YES);
1440 if (gtk_dialog_run(GTK_DIALOG(shell)) == GTK_RESPONSE_YES) {
1441 unit_list_iterate(punits, punit) {
1442 request_unit_upgrade(punit);
1443 } unit_list_iterate_end;
1445 gtk_widget_destroy(shell);
1449 /****************************************************************************
1450 Pops up a dialog to confirm disband of the unit(s).
1451 ****************************************************************************/
1452 void popup_disband_dialog(struct unit_list *punits)
1454 GtkWidget *shell;
1455 char buf[512];
1457 if (!punits || unit_list_size(punits) == 0) {
1458 return;
1461 if (!get_units_disband_info(buf, sizeof(buf), punits)) {
1462 shell = gtk_message_dialog_new(NULL, 0,
1463 GTK_MESSAGE_INFO, GTK_BUTTONS_CLOSE,
1464 "%s", buf);
1465 gtk_window_set_title(GTK_WINDOW(shell), _("Disband Units"));
1466 setup_dialog(shell, toplevel);
1467 g_signal_connect(shell, "response", G_CALLBACK(gtk_widget_destroy),
1468 NULL);
1469 gtk_window_present(GTK_WINDOW(shell));
1470 } else {
1471 shell = gtk_message_dialog_new(NULL, 0,
1472 GTK_MESSAGE_QUESTION, GTK_BUTTONS_YES_NO,
1473 "%s", buf);
1474 gtk_window_set_title(GTK_WINDOW(shell), _("Disband Units"));
1475 setup_dialog(shell, toplevel);
1476 gtk_dialog_set_default_response(GTK_DIALOG(shell), GTK_RESPONSE_YES);
1478 if (gtk_dialog_run(GTK_DIALOG(shell)) == GTK_RESPONSE_YES) {
1479 unit_list_iterate(punits, punit) {
1480 if (!unit_has_type_flag(punit, UTYF_UNDISBANDABLE)) {
1481 request_unit_disband(punit);
1483 } unit_list_iterate_end;
1485 gtk_widget_destroy(shell);
1489 /**********************************************************************
1490 This function is called when the client disconnects or the game is
1491 over. It should close all dialog windows for that game.
1492 ***********************************************************************/
1493 void popdown_all_game_dialogs(void)
1495 gui_dialog_destroy_all();
1496 property_editor_popdown(editprop_get_property_editor());
1497 unit_select_dialog_popdown();
1500 /****************************************************************
1501 Player has gained a new tech.
1502 *****************************************************************/
1503 void show_tech_gained_dialog(Tech_type_id tech)
1505 const struct advance *padvance = valid_advance_by_number(tech);
1507 if (NULL != padvance
1508 && (gui_options.gui_gtk2_popup_tech_help == GUI_POPUP_TECH_HELP_ENABLED
1509 || (gui_options.gui_gtk2_popup_tech_help == GUI_POPUP_TECH_HELP_RULESET
1510 && game.control.popup_tech_help))) {
1511 popup_help_dialog_typed(advance_name_translation(padvance), HELP_TECH);
1515 /****************************************************************
1516 Show tileset error dialog. It's blocking as client will
1517 shutdown as soon as this function returns.
1518 *****************************************************************/
1519 void show_tileset_error(const char *msg)
1521 if (is_gui_up()) {
1522 GtkWidget *dialog;
1524 dialog = gtk_message_dialog_new(NULL, GTK_DIALOG_DESTROY_WITH_PARENT, GTK_MESSAGE_ERROR,
1525 GTK_BUTTONS_CLOSE,
1526 _("Tileset problem, it's probably incompatible with the ruleset:\n%s"),
1527 msg);
1528 setup_dialog(dialog, toplevel);
1530 gtk_dialog_run(GTK_DIALOG(dialog));
1532 gtk_widget_destroy(dialog);
1536 /****************************************************************
1537 Give a warning when user is about to edit scenario with manually
1538 set properties.
1539 *****************************************************************/
1540 bool handmade_scenario_warning(void)
1542 /* Just tell the client common code to handle this. */
1543 return FALSE;
1546 /****************************************************************
1547 Unit wants to get into some transport on given tile.
1548 *****************************************************************/
1549 bool request_transport(struct unit *pcargo, struct tile *ptile)
1551 return FALSE; /* Unit was not handled here. */
1554 /***************************************************************************
1555 Popup detailed information about battle or save information for
1556 some kind of statistics
1557 ***************************************************************************/
1558 void popup_combat_info(int attacker_unit_id, int defender_unit_id,
1559 int attacker_hp, int defender_hp,
1560 bool make_winner_veteran)