webperimental: killstack decides stack protects.
[freeciv.git] / client / gui-gtk-4.0 / gui_stuff.c
blob35e7f38103cf64065c99d770a65e294007d9ad29
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 "fcintl.h"
28 #include "log.h"
29 #include "mem.h"
30 #include "support.h"
32 /* client */
33 #include "options.h"
35 /* client/gui-gtk-4.0 */
36 #include "colors.h"
37 #include "gui_main.h"
39 #include "gui_stuff.h"
42 static GList *dialog_list;
44 static GtkSizeGroup *gui_action;
46 static GtkCssProvider *dlg_tab_provider = NULL;
49 /**************************************************************************
50 Draw widget now
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)
79 GtkWidget *button;
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);
87 return button;
90 /**************************************************************************
91 Changes the label (with mnemonic) on an existing stockbutton. See
92 gtk_stockbutton_new.
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)
111 int i;
113 if (!*done) {
114 for (i = 0; i < n; i++) {
115 s[i] = Q_(s[i]);
118 *done = TRUE;
122 /****************************************************************
123 Set itree to the beginning
124 *****************************************************************/
125 void itree_begin(GtkTreeModel *model, ITree *it)
127 it->model = model;
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)
136 return it->end;
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, ...)
152 va_list ap;
154 va_start(ap, it);
155 gtk_tree_store_set_valist(GTK_TREE_STORE(it->model), &it->it, ap);
156 va_end(ap);
159 /****************************************************************
160 Get values from itree
161 *****************************************************************/
162 void itree_get(ITree *it, ...)
164 va_list ap;
166 va_start(ap, it);
167 gtk_tree_model_get_valist(it->model, &it->it, ap);
168 va_end(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);
177 if (parent)
178 gtk_tree_store_append(GTK_TREE_STORE(it->model), &it->it, &parent->it);
179 else
180 gtk_tree_store_append(GTK_TREE_STORE(it->model), &it->it, NULL);
181 it->end = FALSE;
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)
214 GtkTreeModel *model;
215 GtkTreeIter it;
216 gint row = -1;
218 if (gtk_tree_selection_get_selected(selection, &model, &it)) {
219 GtkTreePath *path;
220 gint *idx;
222 path = gtk_tree_model_get_path(model, &it);
223 idx = gtk_tree_path_get_indices(path);
224 row = idx[0];
225 gtk_tree_path_free(path);
227 return row;
230 /**************************************************************************
231 Give focus to view
232 **************************************************************************/
233 void gtk_tree_view_focus(GtkTreeView *view)
235 GtkTreeModel *model;
236 GtkTreePath *path;
237 GtkTreeIter iter;
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
250 the window).
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);
267 return menubar;
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),
287 GTK_WINDOW(parent));
288 gtk_window_set_type_hint(GTK_WINDOW(shell),
289 GDK_WINDOW_TYPE_HINT_DIALOG);
290 } else {
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,
315 gpointer data)
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) {
342 GList *it;
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);
347 break;
352 if (dlg->title) {
353 free(dlg->title);
356 free(dlg);
359 /**************************************************************************
360 Emit a delete event response on dialog deletion in case the end-user
361 needs to know when a deletion took place.
362 Popup dialog version
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. */
373 return FALSE;
376 /**************************************************************************
377 Emit a delete event response on dialog deletion in case the end-user
378 needs to know when a deletion took place.
379 TAB version
380 **************************************************************************/
381 static gint gui_dialog_delete_tab_handler(struct gui_dialog* dlg)
383 GtkWidget* notebook;
384 int n;
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. */
397 return FALSE;
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,
405 gpointer data)
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. */
416 return FALSE;
419 /**************************************************************************
420 Resets tab colour on tab activation.
421 **************************************************************************/
422 static void gui_dialog_switch_page_handler(GtkNotebook *notebook,
423 GtkWidget *page,
424 guint num,
425 struct gui_dialog *dlg)
427 gint n;
429 n = gtk_notebook_page_num(GTK_NOTEBOOK(dlg->v.tab.notebook), dlg->vbox);
431 if (n == num) {
432 gtk_style_context_remove_class(gtk_widget_get_style_context(dlg->v.tab.label),
433 "alert");
437 /**************************************************************************
438 Changes a tab into a window.
439 **************************************************************************/
440 static void gui_dialog_detach(struct gui_dialog* dlg)
442 gint n;
443 GtkWidget *window, *notebook;
444 gulong handler_id;
446 if (dlg->type != GUI_DIALOG_TAB) {
447 return;
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),
475 dlg->default_width,
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,
485 gpointer data)
487 if (button->type != GDK_2BUTTON_PRESS) {
488 return FALSE;
490 if (button->button != 1) {
491 return FALSE;
493 gui_dialog_detach((struct gui_dialog*) data);
494 return TRUE;
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
502 dialog destruction.
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);
516 dlg->source = pdlg;
517 *pdlg = dlg;
518 dlg->user_data = user_data;
519 dlg->title = NULL;
521 dlg->default_width = 200;
522 dlg->default_height = 300;
524 if (GUI_GTK_OPTION(enable_tabs)) {
525 dlg->type = GUI_DIALOG_TAB;
526 } else {
527 dlg->type = GUI_DIALOG_WINDOW;
530 if (!gui_action) {
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);
546 } else {
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);
567 switch (dlg->type) {
568 case GUI_DIALOG_WINDOW:
570 GtkWidget *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);
583 break;
584 case GUI_DIALOG_TAB:
586 GtkWidget *hbox, *label, *button, *event_box;
587 gint w, h;
588 gchar *buf;
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);
610 g_free(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);
638 break;
641 dlg->vbox = vbox;
642 dlg->action_area = action_area;
644 dlg->response_callback = gui_dialog_destroyed;
646 dlg->id = dialog_id_counter;
647 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");
665 gpointer arg2 =
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,
675 int response)
677 gint signal_id;
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))) {
685 GClosure *closure;
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)
704 GtkWidget *button;
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);
710 return button;
713 /**************************************************************************
714 Adds a widget to a dialog.
715 **************************************************************************/
716 GtkWidget *gui_dialog_add_widget(struct gui_dialog *dlg,
717 GtkWidget *widget)
719 gtk_container_add(GTK_CONTAINER(dlg->action_area), widget);
720 gtk_size_group_add_widget(gui_action, widget);
722 return widget;
725 /**************************************************************************
726 Changes the default dialog response.
727 **************************************************************************/
728 void gui_dialog_set_default_response(struct gui_dialog *dlg, int response)
730 GList *children;
731 GList *list;
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)
757 GList *children;
758 GList *list;
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) {
794 GList *children;
795 GList *list;
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)) {
804 num_visible++;
805 } else {
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) {
812 num_visible++;
813 } else {
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);
833 switch (dlg->type) {
834 case GUI_DIALOG_WINDOW:
835 gtk_widget_show(dlg->v.window);
836 break;
837 case GUI_DIALOG_TAB:
839 GtkNotebook *notebook = GTK_NOTEBOOK(dlg->v.tab.notebook);
840 gint current, n;
842 current = gtk_notebook_get_current_page(notebook);
843 n = gtk_notebook_page_num(notebook, dlg->vbox);
845 if (current != n) {
846 GtkWidget *label = dlg->v.tab.label;
848 gtk_style_context_add_class(gtk_widget_get_style_context(label),
849 "alert");
852 break;
856 /**************************************************************************
857 Raise dialog to top.
858 **************************************************************************/
859 void gui_dialog_raise(struct gui_dialog *dlg)
861 fc_assert_ret(NULL != dlg);
863 switch (dlg->type) {
864 case GUI_DIALOG_WINDOW:
865 gtk_window_present(GTK_WINDOW(dlg->v.window));
866 break;
867 case GUI_DIALOG_TAB:
869 GtkNotebook *notebook = GTK_NOTEBOOK(dlg->v.tab.notebook);
870 gint n;
872 n = gtk_notebook_page_num(notebook, dlg->vbox);
873 gtk_notebook_set_current_page(notebook, n);
875 break;
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);
886 switch (dlg->type) {
887 case GUI_DIALOG_WINDOW:
888 break;
889 case GUI_DIALOG_TAB:
891 GtkNotebook *notebook = GTK_NOTEBOOK(dlg->v.tab.notebook);
892 gint current, n;
894 current = gtk_notebook_get_current_page(notebook);
895 n = gtk_notebook_page_num(notebook, dlg->vbox);
897 if (current != n) {
898 GtkWidget *label = dlg->v.tab.label;
900 gtk_style_context_add_class(gtk_widget_get_style_context(label),
901 "alert");
904 break;
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;
915 switch (dlg->type) {
916 case GUI_DIALOG_WINDOW:
917 gtk_window_set_default_size(GTK_WINDOW(dlg->v.window), width, height);
918 break;
919 case GUI_DIALOG_TAB:
920 break;
924 /**************************************************************************
925 Changes a dialog's title.
926 **************************************************************************/
927 void gui_dialog_set_title(struct gui_dialog *dlg, const char *title)
929 if (dlg->title) {
930 free(dlg->title);
932 dlg->title = fc_strdup(title);
933 switch (dlg->type) {
934 case GUI_DIALOG_WINDOW:
935 gtk_window_set_title(GTK_WINDOW(dlg->v.window), title);
936 break;
937 case GUI_DIALOG_TAB:
938 gtk_label_set_text_with_mnemonic(GTK_LABEL(dlg->v.tab.label), title);
939 break;
943 /**************************************************************************
944 Destroy a dialog.
945 **************************************************************************/
946 void gui_dialog_destroy(struct gui_dialog *dlg)
948 switch (dlg->type) {
949 case GUI_DIALOG_WINDOW:
950 gtk_widget_destroy(dlg->v.window);
951 break;
952 case GUI_DIALOG_TAB:
954 gint n;
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);
959 break;
963 /**************************************************************************
964 Destroy all dialogs.
965 **************************************************************************/
966 void gui_dialog_destroy_all(void)
968 GList *it, *it_next;
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;
994 } else {
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)
1004 char *str;
1005 GtkCssProvider *provider;
1006 PangoFontDescription *desc;
1007 int size;
1008 const char *fam;
1009 const char *style;
1010 const char *weight;
1012 desc = pango_font_description_from_string(font_value);
1014 if (desc == NULL) {
1015 return;
1018 fam = pango_font_description_get_family(desc);
1020 if (fam == NULL) {
1021 return;
1024 if (pango_font_description_get_style(desc) == PANGO_STYLE_ITALIC) {
1025 style = "\n font-style: italic;";
1026 } else {
1027 style = "";
1030 if (pango_font_description_get_weight(desc) >= 700) {
1031 weight = "\n font-weight: bold;";
1032 } else {
1033 weight = "";
1036 size = pango_font_description_get_size(desc);
1038 if (size != 0) {
1039 str = g_strdup_printf("#Freeciv #%s { font-family: %s; font-size: %dpx;%s%s}",
1040 font_name, fam, size / PANGO_SCALE, style, weight);
1041 } else {
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),
1050 str, -1);
1051 gtk_style_context_add_provider_for_screen(
1052 gtk_widget_get_screen(toplevel), GTK_STYLE_PROVIDER(provider),
1053 GTK_STYLE_PROVIDER_PRIORITY_APPLICATION);
1054 g_free(str);
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)
1079 gulong hid;
1081 if (!obj || !cb) {
1082 return;
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)
1095 gulong hid;
1097 if (!obj || !cb) {
1098 return;
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;
1115 const char *attr;
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();
1123 attr = "active";
1124 } else if (gtype == GDK_TYPE_PIXBUF) {
1125 rend = gtk_cell_renderer_pixbuf_new();
1126 attr = "pixbuf";
1127 } else {
1128 rend = gtk_cell_renderer_text_new();
1129 attr = "text";
1132 col = gtk_tree_view_column_new_with_attributes(title, rend, attr,
1133 model_index, NULL);
1134 gtk_tree_view_append_column(GTK_TREE_VIEW(view), col);
1136 return 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,
1147 ".alert {\n"
1148 "color: rgba(255, 0, 0, 255);\n"
1149 "}\n",
1150 -1);