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)
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 ***********************************************************************/
15 #include <fc_config.h>
24 #include <gdk/gdkkeysyms.h>
28 #include "bitvector.h"
37 #include "government.h"
43 #include "client_main.h"
45 #include "connectdlg_common.h"
47 #include "helpdata.h" /* for helptext_nation() */
54 /* client/gui-gtk-3.0 */
56 #include "choice_dialog.h"
61 #include "gui_stuff.h"
65 #include "unitselect.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
,
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
);
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");
164 center_tile_mapcanvas(ptile
);
167 pcity
= tile_city(ptile
);
169 if (gui_options
.center_when_popup_city
) {
170 center_tile_mapcanvas(ptile
);
174 popup_city_dialog(pcity
);
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
193 **************************************************************************/
194 void popup_notify_goto_dialog(const char *headline
, const char *lines
,
195 const struct text_tag_list
*tags
,
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
,
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
,
224 gtk_widget_set_sensitive(goto_command
, FALSE
);
225 gtk_widget_set_sensitive(popcity_command
, FALSE
);
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
,
262 g_signal_connect(shell
, "response", G_CALLBACK(notify_connect_msg_response
),
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
) {
278 set_government_choice(government
);
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
) {
295 shell
= gtk_message_dialog_new(NULL
,
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
));
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
)
322 int what
= GPOINTER_TO_INT(data
);
324 punit
= game_unit_by_number(unit_to_use_to_pillage
);
326 struct extra_type
*target
= extra_by_number(what
);
328 request_new_unit_activity_targeted(punit
, ACTIVITY_PILLAGE
,
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
)
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
))) {
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
),
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
),
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
,
408 if (nation
== -1 || list
== NULL
) {
411 GtkTreeModel
*model
= gtk_tree_view_get_model(list
);
413 GtkTreePath
*path
= NULL
;
414 gtk_tree_model_get_iter_first(model
, &iter
);
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
);
422 } while (gtk_tree_model_iter_next(model
, &iter
));
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
);
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);
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
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 */
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)
494 GtkListStore
*model
=
495 GTK_LIST_STORE(gtk_combo_box_get_model(GTK_COMBO_BOX(races_leader
)));
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);
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
,
521 selected_nation
= nation
;
523 /* Refresh the available leaders. */
524 populate_leader_list();
526 if (selected_nation
!= -1) {
528 /* Select leader name and sex. */
530 gtk_entry_set_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN(races_leader
))),
532 /* Assume is_male is valid too. */
533 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(races_sex
[is_male
]),
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. */
548 styles_iterate(pstyle
) {
549 i
= basic_city_style_for_style(pstyle
);
551 if (i
>= 0 && i
< style_id
) {
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
,
562 gtk_tree_path_free(path
);
565 /* Update nation description. */
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
);
578 /* No nation selected. Blank properties and make controls insensitive. */
580 gtk_entry_set_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN(races_leader
))),
582 /* Leader sex (*shrug*) */
583 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(races_sex
[0]), TRUE
);
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
,
613 GtkWidget
*sw
= NULL
;
614 GtkListStore
*store
= NULL
;
615 GtkWidget
*list
= NULL
;
617 /* Populate nation list store. */
618 nations_iterate(pnation
) {
622 GValue value
= { 0, };
624 if (!is_nation_playable(pnation
) || !is_nation_pickable(pnation
)) {
628 if (NULL
!= group
&& !nation_is_in_group(pnation
, group
)) {
632 /* Only create tab on demand -- we don't want it if there aren't any
633 * currently pickable nations in this group. */
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
),
654 gtk_tree_selection_set_select_function(select
, races_selection_func
,
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
,
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
);
680 gtk_list_store_set(store
, &it
, 2, img
, -1);
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
;
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
;
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
;
710 nation_list
= create_list_of_nations_in_group(group
, i
);
712 group_name_label
= gtk_label_new(nation_group_name_translation(group
));
713 gtk_notebook_append_page(GTK_NOTEBOOK(races_notebook
), nation_list
,
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). */
722 group_name_label
= gtk_label_new(_("All"));
723 gtk_notebook_append_page(GTK_NOTEBOOK(races_notebook
), nation_list
,
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
)
740 /* Save selected tab */
741 tab
= gtk_notebook_get_current_page(GTK_NOTEBOOK(races_notebook
));
745 /* Turn tab index into a nation group index (they're not always equal,
746 * as some groups may not currently have tabs). */
748 while (groupidx
<= nation_group_count()
749 && races_nation_list
[groupidx
] == NULL
) {
752 fc_assert_action(groupidx
<= nation_group_count(), break);
753 /* Nation group 'groupidx' is what's displayed on the i'th tab */
761 /* No tabs currently */
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
) {
787 for (i
= 0; i
< groupidx
; i
++) {
788 if (races_nation_list
[i
] != NULL
) {
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
)
817 if (gtk_combo_box_get_active_iter(b
, &iter
)) {
818 struct option
*poption
= optset_option_by_name(server_optset
, "nationset");
820 gtk_tree_model_get(gtk_combo_box_get_model(b
), &iter
,
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
);
835 /*****************************************************************************
836 Create nations dialog
837 *****************************************************************************/
838 static void create_races_dialog(struct player
*pplayer
)
842 GtkWidget
*hbox
, *table
;
843 GtkWidget
*frame
, *label
, *combo
;
850 GtkCellRenderer
*render
;
851 GtkTreeViewColumn
*column
;
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?");
864 title
= _("Pick Nation");
867 shell
= gtk_dialog_new_with_buttons(title
,
873 GTK_RESPONSE_NO
, /* arbitrary */
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
,
908 GtkCellRenderer
*renderer
;
909 nation_sets_iterate(pset
) {
910 /* Index in list store must match nation_set_index(). */
912 struct astring s
= ASTRING_INIT
;
914 nations_iterate(pnation
) {
915 if (is_nation_playable(pnation
) && nation_is_in_set(pnation
, pset
)) {
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
);
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
)));
932 fc_break_lines(desc
, 70);
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
);
944 gtk_list_store_insert_with_values(sets_model
, NULL
, -1,
945 0, nation_set_rule_name(pset
),
947 2, nation_set_name_translation(pset
),
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. */
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
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
),
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
,
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:"),
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
,
999 gtk_grid_attach(GTK_GRID(nation_selection_list
), nationsets_chooser
,
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
,
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
)) {
1016 } nation_groups_iterate_end
;
1018 gtk_notebook_set_show_tabs(GTK_NOTEBOOK(races_notebook
), FALSE
);
1020 label
= g_object_new(GTK_TYPE_LABEL
,
1021 "use-underline", TRUE
,
1022 "label", _("Nation _Groups:"),
1026 gtk_label_set_mnemonic_widget(GTK_LABEL(label
), races_notebook
);
1027 gtk_grid_attach(GTK_GRID(nation_selection_list
), label
,
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
);
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
);
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:"),
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);
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
),
1081 gtk_widget_set_margin_bottom(cmd
, 6);
1083 gtk_grid_attach(GTK_GRID(table
), cmd
, 2, 1, 1, 1);
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:"),
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
,
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
,
1125 gtk_tree_view_append_column(GTK_TREE_VIEW(list
), column
);
1127 /* Populate style store. */
1128 styles_iterate(pstyle
) {
1133 i
= basic_city_style_for_style(pstyle
);
1136 gtk_list_store_append(store
, &it
);
1138 s
= crop_blankspace(get_sample_city_sprite(tileset
, i
));
1139 img
= sprite_get_pixbuf(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
;
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
);
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));
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
,
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);
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
)
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)
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)
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
);
1252 /* Update 'chosen' column in model */
1253 if (gtk_tree_model_get_iter_first(model
, &it
)) {
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);
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
;
1289 if (gtk_tree_selection_get_selected(select
, &model
, &it
)) {
1293 gtk_tree_model_get(model
, &it
, 0, &newnation
, 1, &chosen
, -1);
1295 /* Only allow nations not chosen by another player */
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
))));
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
;
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
),
1324 selected_sex
= nation_leader_is_male(pleader
);
1325 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(races_sex
[selected_sex
]),
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
)
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
;
1361 if (gtk_tree_selection_get_selected(select
, &model
, &it
)) {
1362 gtk_tree_model_get(model
, &it
, 0, &selected_style
, -1);
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
) {
1376 /* This shouldn't be possible but... */
1377 if (selected_nation
== -1) {
1381 if (selected_sex
== -1) {
1382 output_window_append(ftc_client
, _("You must select your sex."));
1386 if (selected_style
== -1) {
1387 output_window_append(ftc_client
, _("You must select your style."));
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."));
1400 dsend_packet_nation_select_req(&client
.conn
,
1401 player_number(races_player
), selected_nation
,
1404 } else if (response
== GTK_RESPONSE_NO
) {
1405 dsend_packet_nation_select_req(&client
.conn
,
1406 player_number(races_player
),
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
);
1423 /****************************************************************************
1424 Pops up a dialog to confirm upgrading of the unit.
1425 ****************************************************************************/
1426 void popup_upgrade_dialog(struct unit_list
*punits
)
1431 if (!punits
|| unit_list_size(punits
) == 0) {
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
,
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
),
1443 gtk_window_present(GTK_WINDOW(shell
));
1445 shell
= gtk_message_dialog_new(NULL
, 0,
1446 GTK_MESSAGE_QUESTION
, GTK_BUTTONS_YES_NO
,
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
)
1469 if (!punits
|| unit_list_size(punits
) == 0) {
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
,
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
),
1481 gtk_window_present(GTK_WINDOW(shell
));
1483 shell
= gtk_message_dialog_new(NULL
, 0,
1484 GTK_MESSAGE_QUESTION
, GTK_BUTTONS_YES_NO
,
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
)
1536 dialog
= gtk_message_dialog_new(NULL
, GTK_DIALOG_DESTROY_WITH_PARENT
, GTK_MESSAGE_ERROR
,
1538 _("Tileset problem, it's probably incompatible with the ruleset:\n%s"),
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
1551 *****************************************************************/
1552 bool handmade_scenario_warning(void)
1554 /* Just tell the client common code to handle this. */
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
)