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>
35 /* client/gui-gtk-4.0 */
39 #include "gui_stuff.h"
42 static GList
*dialog_list
;
44 static GtkSizeGroup
*gui_action
;
46 static GtkCssProvider
*dlg_tab_provider
= NULL
;
49 /**************************************************************************
51 **************************************************************************/
52 void gtk_expose_now(GtkWidget
*w
)
54 gtk_widget_queue_draw(w
);
57 /**************************************************************************
58 Set window position relative to reference window
59 **************************************************************************/
60 void set_relative_window_position(GtkWindow
*ref
, GtkWindow
*w
, int px
, int py
)
62 gint x
, y
, width
, height
;
64 gtk_window_get_position(ref
, &x
, &y
);
65 gtk_window_get_size(ref
, &width
, &height
);
67 x
+= px
* width
/ 100;
68 y
+= py
* height
/ 100;
70 gtk_window_move(w
, x
, y
);
73 /**************************************************************************
74 Create new icon button with label
75 **************************************************************************/
76 GtkWidget
*icon_label_button_new(const gchar
*icon_name
,
77 const gchar
*label_text
)
81 button
= gtk_button_new_with_mnemonic(label_text
);
83 if (icon_name
!= NULL
) {
84 gtk_button_set_icon_name(GTK_BUTTON(button
), icon_name
);
90 /**************************************************************************
91 Changes the label (with mnemonic) on an existing stockbutton. See
93 **************************************************************************/
94 void gtk_stockbutton_set_label(GtkWidget
*button
, const gchar
*label_text
)
96 gtk_button_set_label(GTK_BUTTON(button
), label_text
);
99 /**************************************************************************
100 Returns gettext-converted list of n strings. The individual strings
101 in the list are as returned by gettext(). In case of no NLS, the strings
102 will be the original strings, so caller should ensure that the originals
103 persist for as long as required. (For no NLS, still allocate the
104 list, for consistency.)
106 (This is not directly gui/gtk related, but it fits in here
107 because so far it is used for doing i18n for gtk titles...)
108 **************************************************************************/
109 void intl_slist(int n
, const char **s
, bool *done
)
114 for (i
= 0; i
< n
; i
++) {
122 /****************************************************************
123 Set itree to the beginning
124 *****************************************************************/
125 void itree_begin(GtkTreeModel
*model
, ITree
*it
)
128 it
->end
= !gtk_tree_model_get_iter_first(it
->model
, &it
->it
);
131 /****************************************************************
132 Return whether itree end has been reached
133 *****************************************************************/
134 gboolean
itree_end(ITree
*it
)
139 /****************************************************************
140 Make itree to go forward one step
141 *****************************************************************/
142 void itree_next(ITree
*it
)
144 it
->end
= !gtk_tree_model_iter_next(it
->model
, &it
->it
);
147 /****************************************************************
148 Store values to itree
149 *****************************************************************/
150 void itree_set(ITree
*it
, ...)
155 gtk_tree_store_set_valist(GTK_TREE_STORE(it
->model
), &it
->it
, ap
);
159 /****************************************************************
160 Get values from itree
161 *****************************************************************/
162 void itree_get(ITree
*it
, ...)
167 gtk_tree_model_get_valist(it
->model
, &it
->it
, ap
);
171 /****************************************************************
172 Append one item to the end of tree store
173 *****************************************************************/
174 void tstore_append(GtkTreeStore
*store
, ITree
*it
, ITree
*parent
)
176 it
->model
= GTK_TREE_MODEL(store
);
178 gtk_tree_store_append(GTK_TREE_STORE(it
->model
), &it
->it
, &parent
->it
);
180 gtk_tree_store_append(GTK_TREE_STORE(it
->model
), &it
->it
, NULL
);
184 /****************************************************************
185 Return whether current itree item is selected
186 *****************************************************************/
187 gboolean
itree_is_selected(GtkTreeSelection
*selection
, ITree
*it
)
189 return gtk_tree_selection_iter_is_selected(selection
, &it
->it
);
192 /****************************************************************
193 Add current itree item to selection
194 *****************************************************************/
195 void itree_select(GtkTreeSelection
*selection
, ITree
*it
)
197 gtk_tree_selection_select_iter(selection
, &it
->it
);
200 /****************************************************************
201 Remove current itree item from selection
202 *****************************************************************/
203 void itree_unselect(GtkTreeSelection
*selection
, ITree
*it
)
205 gtk_tree_selection_unselect_iter(selection
, &it
->it
);
208 /**************************************************************************
209 Return the selected row in a GtkTreeSelection.
210 If no row is selected return -1.
211 **************************************************************************/
212 gint
gtk_tree_selection_get_row(GtkTreeSelection
*selection
)
218 if (gtk_tree_selection_get_selected(selection
, &model
, &it
)) {
222 path
= gtk_tree_model_get_path(model
, &it
);
223 idx
= gtk_tree_path_get_indices(path
);
225 gtk_tree_path_free(path
);
230 /**************************************************************************
232 **************************************************************************/
233 void gtk_tree_view_focus(GtkTreeView
*view
)
239 if ((model
= gtk_tree_view_get_model(view
))
240 && gtk_tree_model_get_iter_first(model
, &iter
)
241 && (path
= gtk_tree_model_get_path(model
, &iter
))) {
242 gtk_tree_view_set_cursor(view
, path
, NULL
, FALSE
);
243 gtk_tree_path_free(path
);
244 gtk_widget_grab_focus(GTK_WIDGET(view
));
248 /**************************************************************************
249 Create an auxiliary menubar (i.e., not the main menubar at the top of
251 **************************************************************************/
252 GtkWidget
*gtk_aux_menu_bar_new(void) {
253 GtkWidget
*menubar
= gtk_menu_bar_new();
256 * Ubuntu Linux's Ayatana/Unity desktop environment likes to steal the
257 * application's main menu bar from its window and put it at the top of
258 * the screen. It needs a hint in order not to steal menu bars other
259 * than the main one. Gory details at
260 * https://bugs.launchpad.net/ubuntu/+source/freeciv/+bug/743265
262 if (g_object_class_find_property(
263 G_OBJECT_CLASS(GTK_MENU_BAR_GET_CLASS(menubar
)), "ubuntu-local")) {
264 g_object_set(G_OBJECT(menubar
), "ubuntu-local", TRUE
, NULL
);
270 /**************************************************************************
271 Generic close callback for all widgets
272 **************************************************************************/
273 static void close_callback(GtkDialog
*dialog
, gpointer data
)
275 gtk_widget_destroy(GTK_WIDGET(dialog
));
278 /**********************************************************************
279 This function handles new windows which are subwindows to the
280 toplevel window. It must be called on every dialog in the game,
281 so fullscreen windows are handled properly by the window manager.
282 ***********************************************************************/
283 void setup_dialog(GtkWidget
*shell
, GtkWidget
*parent
)
285 if (GUI_GTK_OPTION(dialogs_on_top
) || GUI_GTK_OPTION(fullscreen
)) {
286 gtk_window_set_transient_for(GTK_WINDOW(shell
),
288 gtk_window_set_type_hint(GTK_WINDOW(shell
),
289 GDK_WINDOW_TYPE_HINT_DIALOG
);
291 gtk_window_set_type_hint(GTK_WINDOW(shell
),
292 GDK_WINDOW_TYPE_HINT_NORMAL
);
295 /* Close dialog window on Escape keypress. */
296 if (GTK_IS_DIALOG(shell
)) {
297 g_signal_connect_after(shell
, "close", G_CALLBACK(close_callback
), shell
);
301 /**************************************************************************
302 Emit a dialog response.
303 **************************************************************************/
304 static void gui_dialog_response(struct gui_dialog
*dlg
, int response
)
306 if (dlg
->response_callback
) {
307 (*dlg
->response_callback
)(dlg
, response
, dlg
->user_data
);
311 /**************************************************************************
312 Default dialog response handler. Destroys the dialog.
313 **************************************************************************/
314 static void gui_dialog_destroyed(struct gui_dialog
*dlg
, int response
,
317 gui_dialog_destroy(dlg
);
320 /**************************************************************************
321 Cleanups the leftovers after a dialog is destroyed.
322 **************************************************************************/
323 static void gui_dialog_destroy_handler(GtkWidget
*w
, struct gui_dialog
*dlg
)
325 if (dlg
->type
== GUI_DIALOG_TAB
) {
326 GtkWidget
*notebook
= dlg
->v
.tab
.notebook
;
327 gulong handler_id
= dlg
->v
.tab
.handler_id
;
329 g_signal_handler_disconnect(notebook
, handler_id
);
332 g_object_unref(dlg
->gui_button
);
334 if (*(dlg
->source
)) {
335 *(dlg
->source
) = NULL
;
338 dialog_list
= g_list_remove(dialog_list
, dlg
);
340 /* Raise the return dialog set by gui_dialog_set_return_dialog() */
341 if (dlg
->return_dialog_id
!= -1) {
343 for (it
= dialog_list
; it
; it
= g_list_next(it
)) {
344 struct gui_dialog
* adialog
= (struct gui_dialog
*)it
->data
;
345 if (adialog
->id
== dlg
->return_dialog_id
) {
346 gui_dialog_raise(adialog
);
359 /**************************************************************************
360 Emit a delete event response on dialog deletion in case the end-user
361 needs to know when a deletion took place.
363 **************************************************************************/
364 static gint
gui_dialog_delete_handler(GtkWidget
*widget
,
365 GdkEventAny
*ev
, gpointer data
)
367 struct gui_dialog
*dlg
= data
;
369 /* emit response signal. */
370 gui_dialog_response(dlg
, GTK_RESPONSE_DELETE_EVENT
);
372 /* do the destroy by default. */
376 /**************************************************************************
377 Emit a delete event response on dialog deletion in case the end-user
378 needs to know when a deletion took place.
380 **************************************************************************/
381 static gint
gui_dialog_delete_tab_handler(struct gui_dialog
* dlg
)
386 notebook
= dlg
->v
.tab
.notebook
;
387 n
= gtk_notebook_get_current_page(GTK_NOTEBOOK(notebook
));
388 if (gtk_notebook_get_nth_page(GTK_NOTEBOOK(notebook
), n
)
389 != dlg
->v
.tab
.child
) {
390 gui_dialog_set_return_dialog(dlg
, NULL
);
393 /* emit response signal. */
394 gui_dialog_response(dlg
, GTK_RESPONSE_DELETE_EVENT
);
396 /* do the destroy by default. */
401 /**************************************************************************
402 Allow the user to close a dialog using Escape or CTRL+W.
403 **************************************************************************/
404 static gboolean
gui_dialog_key_press_handler(GtkWidget
*w
, GdkEventKey
*ev
,
407 struct gui_dialog
*dlg
= data
;
409 if (ev
->keyval
== GDK_KEY_Escape
410 || ((ev
->state
& GDK_CONTROL_MASK
) && ev
->keyval
== GDK_KEY_w
)) {
411 /* emit response signal. */
412 gui_dialog_response(dlg
, GTK_RESPONSE_DELETE_EVENT
);
415 /* propagate event further. */
419 /**************************************************************************
420 Resets tab colour on tab activation.
421 **************************************************************************/
422 static void gui_dialog_switch_page_handler(GtkNotebook
*notebook
,
425 struct gui_dialog
*dlg
)
429 n
= gtk_notebook_page_num(GTK_NOTEBOOK(dlg
->v
.tab
.notebook
), dlg
->vbox
);
432 gtk_style_context_remove_class(gtk_widget_get_style_context(dlg
->v
.tab
.label
),
437 /**************************************************************************
438 Changes a tab into a window.
439 **************************************************************************/
440 static void gui_dialog_detach(struct gui_dialog
* dlg
)
443 GtkWidget
*window
, *notebook
;
446 if (dlg
->type
!= GUI_DIALOG_TAB
) {
449 dlg
->type
= GUI_DIALOG_WINDOW
;
451 /* Create a new reference to the main widget, so it won't be
452 * destroyed in gtk_notebook_remove_page() */
453 g_object_ref(dlg
->vbox
);
455 /* Remove widget from the notebook */
456 notebook
= dlg
->v
.tab
.notebook
;
457 handler_id
= dlg
->v
.tab
.handler_id
;
458 g_signal_handler_disconnect(notebook
, handler_id
);
460 n
= gtk_notebook_page_num(GTK_NOTEBOOK(dlg
->v
.tab
.notebook
), dlg
->vbox
);
461 gtk_notebook_remove_page(GTK_NOTEBOOK(dlg
->v
.tab
.notebook
), n
);
464 /* Create window and put the widget inside */
465 window
= gtk_window_new(GTK_WINDOW_TOPLEVEL
);
466 gtk_window_set_title(GTK_WINDOW(window
), dlg
->title
);
467 setup_dialog(window
, toplevel
);
469 gtk_container_add(GTK_CONTAINER(window
), dlg
->vbox
);
470 dlg
->v
.window
= window
;
471 g_signal_connect(window
, "delete_event",
472 G_CALLBACK(gui_dialog_delete_handler
), dlg
);
474 gtk_window_set_default_size(GTK_WINDOW(dlg
->v
.window
),
476 dlg
->default_height
);
477 gtk_widget_show(window
);
480 /***************************************************************************
481 Someone has clicked on a label in a notebook
482 ***************************************************************************/
483 static gboolean
click_on_tab_callback(GtkWidget
* w
,
484 GdkEventButton
* button
,
487 if (button
->type
!= GDK_2BUTTON_PRESS
) {
490 if (button
->button
!= 1) {
493 gui_dialog_detach((struct gui_dialog
*) data
);
498 /**************************************************************************
499 Creates a new dialog. It will be a tab or a window depending on the
500 current user setting of 'enable_tabs' gtk-gui option.
501 Sets pdlg to point to the dialog once it is create, Zeroes pdlg on
503 user_data will be passed through response function
504 check_top indicates if the layout deision should depend on the parent.
505 **************************************************************************/
506 void gui_dialog_new(struct gui_dialog
**pdlg
, GtkNotebook
*notebook
,
507 gpointer user_data
, bool check_top
)
509 struct gui_dialog
*dlg
;
510 GtkWidget
*vbox
, *action_area
;
511 static int dialog_id_counter
;
513 dlg
= fc_malloc(sizeof(*dlg
));
514 dialog_list
= g_list_prepend(dialog_list
, dlg
);
518 dlg
->user_data
= user_data
;
521 dlg
->default_width
= 200;
522 dlg
->default_height
= 300;
524 if (GUI_GTK_OPTION(enable_tabs
)) {
525 dlg
->type
= GUI_DIALOG_TAB
;
527 dlg
->type
= GUI_DIALOG_WINDOW
;
531 gui_action
= gtk_size_group_new(GTK_SIZE_GROUP_VERTICAL
);
533 dlg
->gui_button
= gtk_size_group_new(GTK_SIZE_GROUP_BOTH
);
535 vbox
= gtk_grid_new();
536 action_area
= gtk_grid_new();
537 gtk_grid_set_row_spacing(GTK_GRID(action_area
), 4);
538 gtk_grid_set_column_spacing(GTK_GRID(action_area
), 4);
539 if (GUI_GTK_OPTION(enable_tabs
) &&
540 (check_top
&& notebook
!= GTK_NOTEBOOK(top_notebook
))
541 && !GUI_GTK_OPTION(small_display_layout
)) {
542 /* We expect this to be short (as opposed to tall); maximise usable
543 * height by putting buttons down the right hand side */
544 gtk_orientable_set_orientation(GTK_ORIENTABLE(action_area
),
545 GTK_ORIENTATION_VERTICAL
);
547 /* We expect this to be reasonably tall; maximise usable width by
548 * putting buttons along the bottom */
549 gtk_orientable_set_orientation(GTK_ORIENTABLE(vbox
),
550 GTK_ORIENTATION_VERTICAL
);
553 gtk_widget_show(vbox
);
554 gtk_container_add(GTK_CONTAINER(vbox
), action_area
);
555 gtk_widget_show(action_area
);
557 gtk_widget_set_margin_start(vbox
, 2);
558 gtk_widget_set_margin_end(vbox
, 2);
559 gtk_widget_set_margin_top(vbox
, 2);
560 gtk_widget_set_margin_bottom(vbox
, 2);
562 gtk_widget_set_margin_start(action_area
, 2);
563 gtk_widget_set_margin_end(action_area
, 2);
564 gtk_widget_set_margin_top(action_area
, 2);
565 gtk_widget_set_margin_bottom(action_area
, 2);
568 case GUI_DIALOG_WINDOW
:
572 window
= gtk_window_new(GTK_WINDOW_TOPLEVEL
);
573 gtk_widget_set_name(window
, "Freeciv");
574 gtk_window_set_position(GTK_WINDOW(window
), GTK_WIN_POS_MOUSE
);
575 setup_dialog(window
, toplevel
);
577 gtk_container_add(GTK_CONTAINER(window
), vbox
);
578 dlg
->v
.window
= window
;
579 g_signal_connect(window
, "delete_event",
580 G_CALLBACK(gui_dialog_delete_handler
), dlg
);
586 GtkWidget
*hbox
, *label
, *button
, *event_box
;
590 gtk_icon_size_lookup(GTK_ICON_SIZE_MENU
, &w
, &h
);
592 hbox
= gtk_grid_new();
594 label
= gtk_label_new(NULL
);
595 gtk_widget_set_halign(label
, GTK_ALIGN_START
);
596 gtk_widget_set_valign(label
, GTK_ALIGN_CENTER
);
597 gtk_widget_set_margin_start(label
, 4);
598 gtk_widget_set_margin_end(label
, 4);
599 gtk_widget_set_margin_top(label
, 0);
600 gtk_widget_set_margin_bottom(label
, 0);
601 gtk_container_add(GTK_CONTAINER(hbox
), label
);
603 button
= gtk_button_new();
604 gtk_button_set_relief(GTK_BUTTON(button
), GTK_RELIEF_NONE
);
605 g_signal_connect_swapped(button
, "clicked",
606 G_CALLBACK(gui_dialog_delete_tab_handler
), dlg
);
608 buf
= g_strdup_printf(_("Close Tab:\n%s"), _("Ctrl+W"));
609 gtk_widget_set_tooltip_text(button
, buf
);
612 gtk_button_set_icon_name(GTK_BUTTON(button
), "window-close");
614 gtk_container_add(GTK_CONTAINER(hbox
), button
);
616 gtk_widget_show(hbox
);
618 event_box
= gtk_event_box_new();
619 gtk_event_box_set_visible_window(GTK_EVENT_BOX(event_box
), FALSE
);
620 gtk_container_add(GTK_CONTAINER(event_box
), hbox
);
622 gtk_notebook_append_page(GTK_NOTEBOOK(notebook
), vbox
, event_box
);
623 dlg
->v
.tab
.handler_id
=
624 g_signal_connect(notebook
, "switch-page",
625 G_CALLBACK(gui_dialog_switch_page_handler
), dlg
);
626 dlg
->v
.tab
.child
= vbox
;
628 gtk_style_context_add_provider(gtk_widget_get_style_context(label
),
629 GTK_STYLE_PROVIDER(dlg_tab_provider
),
630 GTK_STYLE_PROVIDER_PRIORITY_APPLICATION
);
631 dlg
->v
.tab
.label
= label
;
632 dlg
->v
.tab
.notebook
= GTK_WIDGET(notebook
);
634 gtk_widget_add_events(event_box
, GDK_BUTTON2_MOTION_MASK
);
635 g_signal_connect(event_box
, "button-press-event",
636 G_CALLBACK(click_on_tab_callback
), dlg
);
642 dlg
->action_area
= action_area
;
644 dlg
->response_callback
= gui_dialog_destroyed
;
646 dlg
->id
= dialog_id_counter
;
648 dlg
->return_dialog_id
= -1;
650 g_signal_connect(vbox
, "destroy",
651 G_CALLBACK(gui_dialog_destroy_handler
), dlg
);
652 g_signal_connect(vbox
, "key_press_event",
653 G_CALLBACK(gui_dialog_key_press_handler
), dlg
);
655 g_object_set_data(G_OBJECT(vbox
), "gui-dialog-data", dlg
);
658 /**************************************************************************
659 Called when a dialog button is activated.
660 **************************************************************************/
661 static void action_widget_activated(GtkWidget
*button
, GtkWidget
*vbox
)
663 struct gui_dialog
*dlg
=
664 g_object_get_data(G_OBJECT(vbox
), "gui-dialog-data");
666 g_object_get_data(G_OBJECT(button
), "gui-dialog-response-data");
668 gui_dialog_response(dlg
, GPOINTER_TO_INT(arg2
));
671 /**************************************************************************
672 Places a button into a dialog, taking care of setting up signals, etc.
673 **************************************************************************/
674 static void gui_dialog_pack_button(struct gui_dialog
*dlg
, GtkWidget
*button
,
679 fc_assert_ret(GTK_IS_BUTTON(button
));
681 g_object_set_data(G_OBJECT(button
), "gui-dialog-response-data",
682 GINT_TO_POINTER(response
));
684 if ((signal_id
= g_signal_lookup("clicked", GTK_TYPE_BUTTON
))) {
687 closure
= g_cclosure_new_object(G_CALLBACK(action_widget_activated
),
688 G_OBJECT(dlg
->vbox
));
689 g_signal_connect_closure_by_id(button
, signal_id
, 0, closure
, FALSE
);
692 gtk_container_add(GTK_CONTAINER(dlg
->action_area
), button
);
693 gtk_size_group_add_widget(gui_action
, button
);
694 gtk_size_group_add_widget(dlg
->gui_button
, button
);
697 /**************************************************************************
698 Adds a button to a dialog.
699 **************************************************************************/
700 GtkWidget
*gui_dialog_add_button(struct gui_dialog
*dlg
,
701 const char *icon_name
,
702 const char *text
, int response
)
706 button
= icon_label_button_new(icon_name
, text
);
707 gtk_widget_set_can_default(button
, TRUE
);
708 gui_dialog_pack_button(dlg
, button
, response
);
713 /**************************************************************************
714 Adds a widget to a dialog.
715 **************************************************************************/
716 GtkWidget
*gui_dialog_add_widget(struct gui_dialog
*dlg
,
719 gtk_container_add(GTK_CONTAINER(dlg
->action_area
), widget
);
720 gtk_size_group_add_widget(gui_action
, widget
);
725 /**************************************************************************
726 Changes the default dialog response.
727 **************************************************************************/
728 void gui_dialog_set_default_response(struct gui_dialog
*dlg
, int response
)
733 children
= gtk_container_get_children(GTK_CONTAINER(dlg
->action_area
));
735 for (list
= children
; list
; list
= g_list_next(list
)) {
736 GtkWidget
*button
= list
->data
;
738 if (GTK_IS_BUTTON(button
)) {
739 gpointer data
= g_object_get_data(G_OBJECT(button
),
740 "gui-dialog-response-data");
742 if (response
== GPOINTER_TO_INT(data
)) {
743 gtk_widget_grab_default(button
);
748 g_list_free(children
);
751 /**************************************************************************
752 Change the sensitivity of a dialog button.
753 **************************************************************************/
754 void gui_dialog_set_response_sensitive(struct gui_dialog
*dlg
,
755 int response
, bool setting
)
760 children
= gtk_container_get_children(GTK_CONTAINER(dlg
->action_area
));
762 for (list
= children
; list
; list
= g_list_next(list
)) {
763 GtkWidget
*button
= list
->data
;
765 if (GTK_IS_BUTTON(button
)) {
766 gpointer data
= g_object_get_data(G_OBJECT(button
),
767 "gui-dialog-response-data");
769 if (response
== GPOINTER_TO_INT(data
)) {
770 gtk_widget_set_sensitive(button
, setting
);
775 g_list_free(children
);
778 /**************************************************************************
779 Get the dialog's toplevel window.
780 **************************************************************************/
781 GtkWidget
*gui_dialog_get_toplevel(struct gui_dialog
*dlg
)
783 return gtk_widget_get_toplevel(dlg
->vbox
);
786 /**************************************************************************
787 Show the dialog contents, but not the dialog per se.
788 **************************************************************************/
789 void gui_dialog_show_all(struct gui_dialog
*dlg
)
791 gtk_widget_show(dlg
->vbox
);
793 if (dlg
->type
== GUI_DIALOG_TAB
) {
796 gint num_visible
= 0;
798 children
= gtk_container_get_children(GTK_CONTAINER(dlg
->action_area
));
800 for (list
= children
; list
; list
= g_list_next(list
)) {
801 GtkWidget
*button
= list
->data
;
803 if (!GTK_IS_BUTTON(button
)) {
806 gpointer data
= g_object_get_data(G_OBJECT(button
),
807 "gui-dialog-response-data");
808 int response
= GPOINTER_TO_INT(data
);
810 if (response
!= GTK_RESPONSE_CLOSE
811 && response
!= GTK_RESPONSE_CANCEL
) {
814 gtk_widget_hide(button
);
818 g_list_free(children
);
820 if (num_visible
== 0) {
821 gtk_widget_hide(dlg
->action_area
);
826 /**************************************************************************
827 Notify the user the dialog has changed.
828 **************************************************************************/
829 void gui_dialog_present(struct gui_dialog
*dlg
)
831 fc_assert_ret(NULL
!= dlg
);
834 case GUI_DIALOG_WINDOW
:
835 gtk_widget_show(dlg
->v
.window
);
839 GtkNotebook
*notebook
= GTK_NOTEBOOK(dlg
->v
.tab
.notebook
);
842 current
= gtk_notebook_get_current_page(notebook
);
843 n
= gtk_notebook_page_num(notebook
, dlg
->vbox
);
846 GtkWidget
*label
= dlg
->v
.tab
.label
;
848 gtk_style_context_add_class(gtk_widget_get_style_context(label
),
856 /**************************************************************************
858 **************************************************************************/
859 void gui_dialog_raise(struct gui_dialog
*dlg
)
861 fc_assert_ret(NULL
!= dlg
);
864 case GUI_DIALOG_WINDOW
:
865 gtk_window_present(GTK_WINDOW(dlg
->v
.window
));
869 GtkNotebook
*notebook
= GTK_NOTEBOOK(dlg
->v
.tab
.notebook
);
872 n
= gtk_notebook_page_num(notebook
, dlg
->vbox
);
873 gtk_notebook_set_current_page(notebook
, n
);
879 /**************************************************************************
880 Alert the user to an important event.
881 **************************************************************************/
882 void gui_dialog_alert(struct gui_dialog
*dlg
)
884 fc_assert_ret(NULL
!= dlg
);
887 case GUI_DIALOG_WINDOW
:
891 GtkNotebook
*notebook
= GTK_NOTEBOOK(dlg
->v
.tab
.notebook
);
894 current
= gtk_notebook_get_current_page(notebook
);
895 n
= gtk_notebook_page_num(notebook
, dlg
->vbox
);
898 GtkWidget
*label
= dlg
->v
.tab
.label
;
900 gtk_style_context_add_class(gtk_widget_get_style_context(label
),
908 /**************************************************************************
909 Sets the dialog's default size (applies to toplevel windows only).
910 **************************************************************************/
911 void gui_dialog_set_default_size(struct gui_dialog
*dlg
, int width
, int height
)
913 dlg
->default_width
= width
;
914 dlg
->default_height
= height
;
916 case GUI_DIALOG_WINDOW
:
917 gtk_window_set_default_size(GTK_WINDOW(dlg
->v
.window
), width
, height
);
924 /**************************************************************************
925 Changes a dialog's title.
926 **************************************************************************/
927 void gui_dialog_set_title(struct gui_dialog
*dlg
, const char *title
)
932 dlg
->title
= fc_strdup(title
);
934 case GUI_DIALOG_WINDOW
:
935 gtk_window_set_title(GTK_WINDOW(dlg
->v
.window
), title
);
938 gtk_label_set_text_with_mnemonic(GTK_LABEL(dlg
->v
.tab
.label
), title
);
943 /**************************************************************************
945 **************************************************************************/
946 void gui_dialog_destroy(struct gui_dialog
*dlg
)
949 case GUI_DIALOG_WINDOW
:
950 gtk_widget_destroy(dlg
->v
.window
);
956 n
= gtk_notebook_page_num(GTK_NOTEBOOK(dlg
->v
.tab
.notebook
), dlg
->vbox
);
957 gtk_notebook_remove_page(GTK_NOTEBOOK(dlg
->v
.tab
.notebook
), n
);
963 /**************************************************************************
965 **************************************************************************/
966 void gui_dialog_destroy_all(void)
970 for (it
= dialog_list
; it
; it
= it_next
) {
971 it_next
= g_list_next(it
);
973 gui_dialog_destroy((struct gui_dialog
*)it
->data
);
977 /**************************************************************************
978 Set the response callback for a dialog.
979 **************************************************************************/
980 void gui_dialog_response_set_callback(struct gui_dialog
*dlg
,
981 GUI_DIALOG_RESPONSE_FUN fun
)
983 dlg
->response_callback
= fun
;
986 /**************************************************************************
987 When the dlg dialog is destroyed the return_dialog will be raised
988 **************************************************************************/
989 void gui_dialog_set_return_dialog(struct gui_dialog
*dlg
,
990 struct gui_dialog
*return_dialog
)
992 if (return_dialog
== NULL
) {
993 dlg
->return_dialog_id
= -1;
995 dlg
->return_dialog_id
= return_dialog
->id
;
999 /**************************************************************************
1000 Updates a gui font style.
1001 **************************************************************************/
1002 void gui_update_font(const char *font_name
, const char *font_value
)
1005 GtkCssProvider
*provider
;
1006 PangoFontDescription
*desc
;
1012 desc
= pango_font_description_from_string(font_value
);
1018 fam
= pango_font_description_get_family(desc
);
1024 if (pango_font_description_get_style(desc
) == PANGO_STYLE_ITALIC
) {
1025 style
= "\n font-style: italic;";
1030 if (pango_font_description_get_weight(desc
) >= 700) {
1031 weight
= "\n font-weight: bold;";
1036 size
= pango_font_description_get_size(desc
);
1039 str
= g_strdup_printf("#Freeciv #%s { font-family: %s; font-size: %dpx;%s%s}",
1040 font_name
, fam
, size
/ PANGO_SCALE
, style
, weight
);
1042 str
= g_strdup_printf("#Freeciv #%s { font-family: %s;%s%s}",
1043 font_name
, fam
, style
, weight
);
1046 pango_font_description_free(desc
);
1048 provider
= gtk_css_provider_new();
1049 gtk_css_provider_load_from_data(GTK_CSS_PROVIDER(provider
),
1051 gtk_style_context_add_provider_for_screen(
1052 gtk_widget_get_screen(toplevel
), GTK_STYLE_PROVIDER(provider
),
1053 GTK_STYLE_PROVIDER_PRIORITY_APPLICATION
);
1057 /****************************************************************************
1058 Update a font option which is not attached to a widget.
1059 ****************************************************************************/
1060 void gui_update_font_full(const char *font_name
, const char *font_value
,
1061 PangoFontDescription
**font_desc
)
1063 PangoFontDescription
*f_desc
;
1065 gui_update_font(font_name
, font_value
);
1067 f_desc
= pango_font_description_from_string(font_value
);
1068 pango_font_description_free(*font_desc
);
1070 *font_desc
= f_desc
;
1073 /****************************************************************************
1074 Temporarily disable signal invocation of the given callback for the given
1075 GObject. Re-enable the signal with enable_gobject_callback.
1076 ****************************************************************************/
1077 void disable_gobject_callback(GObject
*obj
, GCallback cb
)
1085 hid
= g_signal_handler_find(obj
, G_SIGNAL_MATCH_FUNC
,
1086 0, 0, NULL
, cb
, NULL
);
1087 g_signal_handler_block(obj
, hid
);
1090 /****************************************************************************
1091 Re-enable a signal callback blocked by disable_gobject_callback.
1092 ****************************************************************************/
1093 void enable_gobject_callback(GObject
*obj
, GCallback cb
)
1101 hid
= g_signal_handler_find(obj
, G_SIGNAL_MATCH_FUNC
,
1102 0, 0, NULL
, cb
, NULL
);
1103 g_signal_handler_unblock(obj
, hid
);
1106 /**************************************************************************
1107 Convenience function to add a column to a GtkTreeView. Returns the added
1108 column, or NULL if an error occurred.
1109 **************************************************************************/
1110 GtkTreeViewColumn
*add_treeview_column(GtkWidget
*view
, const char *title
,
1111 GType gtype
, int model_index
)
1113 GtkTreeViewColumn
*col
;
1114 GtkCellRenderer
*rend
;
1117 fc_assert_ret_val(view
!= NULL
, NULL
);
1118 fc_assert_ret_val(GTK_IS_TREE_VIEW(view
), NULL
);
1119 fc_assert_ret_val(title
!= NULL
, NULL
);
1121 if (gtype
== G_TYPE_BOOLEAN
) {
1122 rend
= gtk_cell_renderer_toggle_new();
1124 } else if (gtype
== GDK_TYPE_PIXBUF
) {
1125 rend
= gtk_cell_renderer_pixbuf_new();
1128 rend
= gtk_cell_renderer_text_new();
1132 col
= gtk_tree_view_column_new_with_attributes(title
, rend
, attr
,
1134 gtk_tree_view_append_column(GTK_TREE_VIEW(view
), col
);
1139 /**************************************************************************
1140 Prepare dialog tab style provider.
1141 **************************************************************************/
1142 void dlg_tab_provider_prepare(void)
1144 dlg_tab_provider
= gtk_css_provider_new();
1146 gtk_css_provider_load_from_data(dlg_tab_provider
,
1148 "color: rgba(255, 0, 0, 255);\n"