Do not return NULL as boolean from wonder_is_lost() nor wonder_is_built()
[freeciv.git] / client / gui-gtk-2.0 / gui_stuff.c
blobe86630232a094cd6d98782711ce4a665383dd784
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 /* gui-gtk-2.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;
47 /**************************************************************************
48 Draw widget now
49 **************************************************************************/
50 void gtk_expose_now(GtkWidget *w)
52 gtk_widget_queue_draw(w);
55 /**************************************************************************
56 Set widget position relative to reference widget
57 **************************************************************************/
58 void gtk_set_relative_position(GtkWidget *ref, GtkWidget *w, int px, int py)
60 gint x, y, width, height;
62 gtk_window_get_position(GTK_WINDOW(ref), &x, &y);
63 gtk_window_get_size(GTK_WINDOW(ref), &width, &height);
65 x += px*width/100;
66 y += py*height/100;
68 gtk_window_move(GTK_WINDOW(w), x, y);
71 /**************************************************************************
72 Create new stock button
73 **************************************************************************/
74 GtkWidget *gtk_stockbutton_new(const gchar *stock, const gchar *label_text)
76 GtkWidget *label;
77 GtkWidget *image;
78 GtkWidget *hbox;
79 GtkWidget *align;
80 GtkWidget *button;
82 button = gtk_button_new();
84 label = gtk_label_new_with_mnemonic(label_text);
85 gtk_label_set_mnemonic_widget(GTK_LABEL(label), button);
86 g_object_set_data(G_OBJECT(button), "label", label);
88 image = gtk_image_new_from_stock(stock, GTK_ICON_SIZE_BUTTON);
89 g_object_set_data(G_OBJECT(button), "image", image);
91 hbox = gtk_hbox_new(FALSE, 2);
93 align = gtk_alignment_new(0.5, 0.5, 0.0, 0.0);
95 gtk_box_pack_start(GTK_BOX (hbox), image, FALSE, FALSE, 0);
96 gtk_box_pack_end(GTK_BOX (hbox), label, FALSE, FALSE, 0);
98 gtk_container_add(GTK_CONTAINER(button), align);
99 gtk_container_add(GTK_CONTAINER(align), hbox);
100 gtk_widget_show_all(align);
101 return button;
104 /**************************************************************************
105 Changes the label (with mnemonic) on an existing stockbutton. See
106 gtk_stockbutton_new.
107 **************************************************************************/
108 void gtk_stockbutton_set_label(GtkWidget *button, const gchar *label_text)
110 GtkWidget *label;
112 label = g_object_get_data(G_OBJECT(button), "label");
113 gtk_label_set_markup_with_mnemonic(GTK_LABEL(label), label_text);
116 /**************************************************************************
117 Returns gettext-converted list of n strings. The individual strings
118 in the list are as returned by gettext(). In case of no NLS, the strings
119 will be the original strings, so caller should ensure that the originals
120 persist for as long as required. (For no NLS, still allocate the
121 list, for consistency.)
123 (This is not directly gui/gtk related, but it fits in here
124 because so far it is used for doing i18n for gtk titles...)
125 **************************************************************************/
126 void intl_slist(int n, const char **s, bool *done)
128 int i;
130 if (!*done) {
131 for(i=0; i<n; i++) {
132 s[i] = Q_(s[i]);
135 *done = TRUE;
139 /****************************************************************
140 Set itree to the beginning
141 *****************************************************************/
142 void itree_begin(GtkTreeModel *model, ITree *it)
144 it->model = model;
145 it->end = !gtk_tree_model_get_iter_first(it->model, &it->it);
148 /****************************************************************
149 Return whether itree end has been reached
150 *****************************************************************/
151 gboolean itree_end(ITree *it)
153 return it->end;
156 /****************************************************************
157 Make itree to go forward one step
158 *****************************************************************/
159 void itree_next(ITree *it)
161 it->end = !gtk_tree_model_iter_next(it->model, &it->it);
164 /****************************************************************
165 Store values to itree
166 *****************************************************************/
167 void itree_set(ITree *it, ...)
169 va_list ap;
171 va_start(ap, it);
172 gtk_tree_store_set_valist(GTK_TREE_STORE(it->model), &it->it, ap);
173 va_end(ap);
176 /****************************************************************
177 Get values from itree
178 *****************************************************************/
179 void itree_get(ITree *it, ...)
181 va_list ap;
183 va_start(ap, it);
184 gtk_tree_model_get_valist(it->model, &it->it, ap);
185 va_end(ap);
188 /****************************************************************
189 Append one item to the end of tree store
190 *****************************************************************/
191 void tstore_append(GtkTreeStore *store, ITree *it, ITree *parent)
193 it->model = GTK_TREE_MODEL(store);
194 if (parent)
195 gtk_tree_store_append(GTK_TREE_STORE(it->model), &it->it, &parent->it);
196 else
197 gtk_tree_store_append(GTK_TREE_STORE(it->model), &it->it, NULL);
198 it->end = FALSE;
201 /****************************************************************
202 Return whether current itree item is selected
203 *****************************************************************/
204 gboolean itree_is_selected(GtkTreeSelection *selection, ITree *it)
206 return gtk_tree_selection_iter_is_selected(selection, &it->it);
209 /****************************************************************
210 Add current itree item to selection
211 *****************************************************************/
212 void itree_select(GtkTreeSelection *selection, ITree *it)
214 gtk_tree_selection_select_iter(selection, &it->it);
217 /****************************************************************
218 Remove current itree item from selection
219 *****************************************************************/
220 void itree_unselect(GtkTreeSelection *selection, ITree *it)
222 gtk_tree_selection_unselect_iter(selection, &it->it);
225 /**************************************************************************
226 Return the selected row in a GtkTreeSelection.
227 If no row is selected return -1.
228 **************************************************************************/
229 gint gtk_tree_selection_get_row(GtkTreeSelection *selection)
231 GtkTreeModel *model;
232 GtkTreeIter it;
233 gint row = -1;
235 if (gtk_tree_selection_get_selected(selection, &model, &it)) {
236 GtkTreePath *path;
237 gint *idx;
239 path = gtk_tree_model_get_path(model, &it);
240 idx = gtk_tree_path_get_indices(path);
241 row = idx[0];
242 gtk_tree_path_free(path);
244 return row;
247 /**************************************************************************
248 Give focus to view
249 **************************************************************************/
250 void gtk_tree_view_focus(GtkTreeView *view)
252 GtkTreeModel *model;
253 GtkTreePath *path;
254 GtkTreeIter iter;
256 if ((model = gtk_tree_view_get_model(view))
257 && gtk_tree_model_get_iter_first(model, &iter)
258 && (path = gtk_tree_model_get_path(model, &iter))) {
259 gtk_tree_view_set_cursor(view, path, NULL, FALSE);
260 gtk_tree_path_free(path);
261 gtk_widget_grab_focus(GTK_WIDGET(view));
265 /**************************************************************************
266 Create an auxiliary menubar (i.e., not the main menubar at the top of
267 the window).
268 **************************************************************************/
269 GtkWidget *gtk_aux_menu_bar_new(void) {
270 GtkWidget *menubar = gtk_menu_bar_new();
273 * Ubuntu Linux's Ayatana/Unity desktop environment likes to steal the
274 * application's main menu bar from its window and put it at the top of
275 * the screen. It needs a hint in order not to steal menu bars other
276 * than the main one. Gory details at
277 * https://bugs.launchpad.net/ubuntu/+source/freeciv/+bug/743265
279 if (g_object_class_find_property(
280 G_OBJECT_CLASS(GTK_MENU_BAR_GET_CLASS(menubar)), "ubuntu-local")) {
281 g_object_set(G_OBJECT(menubar), "ubuntu-local", TRUE, NULL);
284 return menubar;
287 /**************************************************************************
288 Generic close callback for all widgets
289 **************************************************************************/
290 static void close_callback(GtkDialog *dialog, gpointer data)
292 gtk_widget_destroy(GTK_WIDGET(dialog));
295 /**********************************************************************
296 This function handles new windows which are subwindows to the
297 toplevel window. It must be called on every dialog in the game,
298 so fullscreen windows are handled properly by the window manager.
299 ***********************************************************************/
300 void setup_dialog(GtkWidget *shell, GtkWidget *parent)
302 if (gui_options.gui_gtk2_dialogs_on_top || gui_options.gui_gtk2_fullscreen) {
303 gtk_window_set_transient_for(GTK_WINDOW(shell),
304 GTK_WINDOW(parent));
305 gtk_window_set_type_hint(GTK_WINDOW(shell),
306 GDK_WINDOW_TYPE_HINT_DIALOG);
307 } else {
308 gtk_window_set_type_hint(GTK_WINDOW(shell),
309 GDK_WINDOW_TYPE_HINT_NORMAL);
312 /* Close dialog window on Escape keypress. */
313 if (GTK_IS_DIALOG(shell)) {
314 g_signal_connect_after(shell, "close", G_CALLBACK(close_callback), shell);
318 /**************************************************************************
319 Emit a dialog response.
320 **************************************************************************/
321 static void gui_dialog_response(struct gui_dialog *dlg, int response)
323 if (dlg->response_callback) {
324 (*dlg->response_callback)(dlg, response, dlg->user_data);
328 /**************************************************************************
329 Default dialog response handler. Destroys the dialog.
330 **************************************************************************/
331 static void gui_dialog_destroyed(struct gui_dialog *dlg, int response,
332 gpointer data)
334 gui_dialog_destroy(dlg);
337 /**************************************************************************
338 Cleanups the leftovers after a dialog is destroyed.
339 **************************************************************************/
340 static void gui_dialog_destroy_handler(GtkWidget *w, struct gui_dialog *dlg)
342 if (dlg->type == GUI_DIALOG_TAB) {
343 GtkWidget *notebook = dlg->v.tab.notebook;
344 gulong handler_id = dlg->v.tab.handler_id;
346 g_signal_handler_disconnect(notebook, handler_id);
349 g_object_unref(dlg->gui_button);
351 if (*(dlg->source)) {
352 *(dlg->source) = NULL;
355 dialog_list = g_list_remove(dialog_list, dlg);
357 /* Raise the return dialog set by gui_dialog_set_return_dialog() */
358 if (dlg->return_dialog_id != -1) {
359 GList *it;
360 for (it = dialog_list; it; it = g_list_next(it)) {
361 struct gui_dialog * adialog = (struct gui_dialog *)it->data;
362 if (adialog->id == dlg->return_dialog_id) {
363 gui_dialog_raise(adialog);
364 break;
369 if (dlg->title) {
370 free(dlg->title);
373 free(dlg);
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 Popup dialog version
380 **************************************************************************/
381 static gint gui_dialog_delete_handler(GtkWidget *widget,
382 GdkEventAny *ev, gpointer data)
384 struct gui_dialog *dlg = data;
386 /* emit response signal. */
387 gui_dialog_response(dlg, GTK_RESPONSE_DELETE_EVENT);
389 /* do the destroy by default. */
390 return FALSE;
393 /**************************************************************************
394 Emit a delete event response on dialog deletion in case the end-user
395 needs to know when a deletion took place.
396 TAB version
397 **************************************************************************/
398 static gint gui_dialog_delete_tab_handler(struct gui_dialog* dlg)
400 GtkWidget* notebook;
401 int n;
403 notebook = dlg->v.tab.notebook;
404 n = gtk_notebook_get_current_page(GTK_NOTEBOOK(notebook));
405 if (gtk_notebook_get_nth_page(GTK_NOTEBOOK(notebook), n)
406 != dlg->v.tab.child) {
407 gui_dialog_set_return_dialog(dlg, NULL);
410 /* emit response signal. */
411 gui_dialog_response(dlg, GTK_RESPONSE_DELETE_EVENT);
413 /* do the destroy by default. */
414 return FALSE;
418 /**************************************************************************
419 Allow the user to close a dialog using Escape or CTRL+W.
420 **************************************************************************/
421 static gboolean gui_dialog_key_press_handler(GtkWidget *w, GdkEventKey *ev,
422 gpointer data)
424 struct gui_dialog *dlg = data;
426 if (ev->keyval == GDK_Escape
427 || ((ev->state & GDK_CONTROL_MASK) && ev->keyval == GDK_w)) {
428 /* emit response signal. */
429 gui_dialog_response(dlg, GTK_RESPONSE_DELETE_EVENT);
432 /* propagate event further. */
433 return FALSE;
436 /**************************************************************************
437 Resets tab colour on tab activation.
438 **************************************************************************/
439 static void gui_dialog_switch_page_handler(GtkNotebook *notebook,
440 GtkNotebookPage *page,
441 guint num,
442 struct gui_dialog *dlg)
444 gint n;
446 n = gtk_notebook_page_num(GTK_NOTEBOOK(dlg->v.tab.notebook), dlg->vbox);
448 if (n == num) {
449 GtkRcStyle *rc_style = gtk_widget_get_modifier_style(dlg->v.tab.label);
451 rc_style->color_flags[GTK_STATE_ACTIVE] &= ~GTK_RC_FG;
452 gtk_widget_modify_style(dlg->v.tab.label, rc_style);
456 /**************************************************************************
457 Changes a tab into a window.
458 **************************************************************************/
459 static void gui_dialog_detach(struct gui_dialog* dlg)
461 gint n;
462 GtkWidget *window, *notebook;
463 gulong handler_id;
465 if (dlg->type != GUI_DIALOG_TAB) {
466 return;
468 dlg->type = GUI_DIALOG_WINDOW;
470 /* Create a new reference to the main widget, so it won't be
471 * destroyed in gtk_notebook_remove_page() */
472 g_object_ref(dlg->vbox);
474 /* Remove widget from the notebook */
475 notebook = dlg->v.tab.notebook;
476 handler_id = dlg->v.tab.handler_id;
477 g_signal_handler_disconnect(notebook, handler_id);
479 n = gtk_notebook_page_num(GTK_NOTEBOOK(dlg->v.tab.notebook), dlg->vbox);
480 gtk_notebook_remove_page(GTK_NOTEBOOK(dlg->v.tab.notebook), n);
483 /* Create window and put the widget inside */
484 window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
485 gtk_window_set_title(GTK_WINDOW(window), dlg->title);
486 setup_dialog(window, toplevel);
488 gtk_container_add(GTK_CONTAINER(window), dlg->vbox);
489 dlg->v.window = window;
490 g_signal_connect(window, "delete_event",
491 G_CALLBACK(gui_dialog_delete_handler), dlg);
493 gtk_window_set_default_size(GTK_WINDOW(dlg->v.window),
494 dlg->default_width,
495 dlg->default_height);
496 gtk_widget_show_all(window);
499 /***************************************************************************
500 Someone has clicked on a label in a notebook
501 ***************************************************************************/
502 static gboolean click_on_tab_callback(GtkWidget* w,
503 GdkEventButton* button,
504 gpointer data)
506 if (button->type != GDK_2BUTTON_PRESS) {
507 return FALSE;
509 if (button->button != 1) {
510 return FALSE;
512 gui_dialog_detach((struct gui_dialog*) data);
513 return TRUE;
517 /**************************************************************************
518 Creates a new dialog. It will be a tab or a window depending on the
519 current user setting of 'gui_gtk2_enable_tabs'.
520 Sets pdlg to point to the dialog once it is create, Zeroes pdlg on
521 dialog destruction.
522 user_data will be passed through response function
523 check_top indicates if the layout deision should depend on the parent.
524 **************************************************************************/
525 void gui_dialog_new(struct gui_dialog **pdlg, GtkNotebook *notebook,
526 gpointer user_data, bool check_top)
528 struct gui_dialog *dlg;
529 GtkWidget *vbox, *action_area;
530 static int dialog_id_counter;
532 dlg = fc_malloc(sizeof(*dlg));
533 dialog_list = g_list_prepend(dialog_list, dlg);
535 dlg->source = pdlg;
536 *pdlg = dlg;
537 dlg->user_data = user_data;
538 dlg->title = NULL;
540 dlg->default_width = 200;
541 dlg->default_height = 300;
543 if (gui_options.gui_gtk2_enable_tabs) {
544 dlg->type = GUI_DIALOG_TAB;
545 } else {
546 dlg->type = GUI_DIALOG_WINDOW;
549 if (!gui_action) {
550 gui_action = gtk_size_group_new(GTK_SIZE_GROUP_VERTICAL);
552 dlg->gui_button = gtk_size_group_new(GTK_SIZE_GROUP_BOTH);
554 if (gui_options.gui_gtk2_enable_tabs
555 && (check_top && notebook != GTK_NOTEBOOK(top_notebook))
556 && !gui_options.gui_gtk2_small_display_layout) {
557 /* We expect this to be short (as opposed to tall); maximise usable
558 * height by putting buttons down the right hand side */
559 vbox = gtk_hbox_new(FALSE, 0);
560 action_area = gtk_vbox_new(FALSE, 2);
561 } else {
562 /* We expect this to be reasonably tall; maximise usable width by
563 * putting buttons along the bottom */
564 vbox = gtk_vbox_new(FALSE, 0);
565 action_area = gtk_hbox_new(FALSE, 2);
568 gtk_widget_show(vbox);
569 gtk_box_pack_end(GTK_BOX(vbox), action_area, FALSE, TRUE, 0);
570 gtk_widget_show(action_area);
572 gtk_container_set_border_width(GTK_CONTAINER(vbox), 2);
573 gtk_box_set_spacing(GTK_BOX(action_area), 4);
574 gtk_container_set_border_width(GTK_CONTAINER(action_area), 2);
576 switch (dlg->type) {
577 case GUI_DIALOG_WINDOW:
579 GtkWidget *window;
581 window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
582 gtk_widget_set_name(window, "Freeciv");
583 gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_MOUSE);
584 setup_dialog(window, toplevel);
586 gtk_container_add(GTK_CONTAINER(window), vbox);
587 dlg->v.window = window;
588 g_signal_connect(window, "delete_event",
589 G_CALLBACK(gui_dialog_delete_handler), dlg);
592 break;
593 case GUI_DIALOG_TAB:
595 GtkWidget *hbox, *label, *image, *button, *event_box;
596 gint w, h;
597 gchar *buf;
599 gtk_icon_size_lookup(GTK_ICON_SIZE_MENU, &w, &h);
601 hbox = gtk_hbox_new(FALSE, 0);
603 label = gtk_label_new(NULL);
604 gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.5);
605 gtk_misc_set_padding(GTK_MISC(label), 4, 0);
606 gtk_box_pack_start(GTK_BOX(hbox), label, TRUE, TRUE, 0);
608 button = gtk_button_new();
609 gtk_button_set_relief(GTK_BUTTON(button), GTK_RELIEF_NONE);
610 g_signal_connect_swapped(button, "clicked",
611 G_CALLBACK(gui_dialog_delete_tab_handler), dlg);
613 buf = g_strdup_printf(_("Close Tab:\n%s"), _("Ctrl+W"));
614 gtk_widget_set_tooltip_text(button, buf);
615 g_free(buf);
617 image = gtk_image_new_from_stock(GTK_STOCK_CLOSE, GTK_ICON_SIZE_MENU);
618 gtk_widget_set_size_request(button, w, h);
619 gtk_container_add(GTK_CONTAINER(button), image);
621 gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 0);
623 gtk_widget_show_all(hbox);
625 event_box = gtk_event_box_new();
626 gtk_container_add(GTK_CONTAINER(event_box), hbox);
628 gtk_notebook_append_page(GTK_NOTEBOOK(notebook), vbox, event_box);
629 dlg->v.tab.handler_id =
630 g_signal_connect(notebook, "switch-page",
631 G_CALLBACK(gui_dialog_switch_page_handler), dlg);
632 dlg->v.tab.child = vbox;
634 dlg->v.tab.label = label;
635 dlg->v.tab.notebook = GTK_WIDGET(notebook);
637 gtk_widget_add_events(event_box, GDK_BUTTON2_MOTION_MASK);
638 g_signal_connect(event_box, "button-press-event",
639 G_CALLBACK(click_on_tab_callback), dlg);
641 break;
644 dlg->vbox = vbox;
645 dlg->action_area = action_area;
647 dlg->response_callback = gui_dialog_destroyed;
649 dlg->id = dialog_id_counter;
650 dialog_id_counter++;
651 dlg->return_dialog_id = -1;
653 g_signal_connect(vbox, "destroy",
654 G_CALLBACK(gui_dialog_destroy_handler), dlg);
655 g_signal_connect(vbox, "key_press_event",
656 G_CALLBACK(gui_dialog_key_press_handler), dlg);
658 g_object_set_data(G_OBJECT(vbox), "gui-dialog-data", dlg);
661 /**************************************************************************
662 Called when a dialog button is activated.
663 **************************************************************************/
664 static void action_widget_activated(GtkWidget *button, GtkWidget *vbox)
666 struct gui_dialog *dlg =
667 g_object_get_data(G_OBJECT(vbox), "gui-dialog-data");
668 gpointer arg2 =
669 g_object_get_data(G_OBJECT(button), "gui-dialog-response-data");
671 gui_dialog_response(dlg, GPOINTER_TO_INT(arg2));
674 /**************************************************************************
675 Places a button into a dialog, taking care of setting up signals, etc.
676 **************************************************************************/
677 static void gui_dialog_pack_button(struct gui_dialog *dlg, GtkWidget *button,
678 int response)
680 gint signal_id;
682 fc_assert_ret(GTK_IS_BUTTON(button));
684 g_object_set_data(G_OBJECT(button), "gui-dialog-response-data",
685 GINT_TO_POINTER(response));
687 if ((signal_id = g_signal_lookup("clicked", GTK_TYPE_BUTTON))) {
688 GClosure *closure;
690 closure = g_cclosure_new_object(G_CALLBACK(action_widget_activated),
691 G_OBJECT(dlg->vbox));
692 g_signal_connect_closure_by_id(button, signal_id, 0, closure, FALSE);
695 gtk_box_pack_end(GTK_BOX(dlg->action_area), button, FALSE, TRUE, 0);
696 gtk_size_group_add_widget(gui_action, button);
697 gtk_size_group_add_widget(dlg->gui_button, button);
700 /**************************************************************************
701 Adds a button to a dialog, allowing the choice of a special stock item.
702 **************************************************************************/
703 GtkWidget *gui_dialog_add_stockbutton(struct gui_dialog *dlg,
704 const char *stock,
705 const char *text, int response)
707 GtkWidget *button;
709 button = gtk_stockbutton_new(stock, text);
710 GTK_WIDGET_SET_FLAGS(button, GTK_CAN_DEFAULT);
711 gui_dialog_pack_button(dlg, button, response);
713 return button;
716 /**************************************************************************
717 Adds a button to a dialog.
718 **************************************************************************/
719 GtkWidget *gui_dialog_add_button(struct gui_dialog *dlg,
720 const char *text, int response)
722 GtkWidget *button;
724 button = gtk_button_new_from_stock(text);
725 GTK_WIDGET_SET_FLAGS(button, GTK_CAN_DEFAULT);
726 gui_dialog_pack_button(dlg, button, response);
728 return button;
731 /**************************************************************************
732 Adds a widget to a dialog.
733 **************************************************************************/
734 GtkWidget *gui_dialog_add_widget(struct gui_dialog *dlg,
735 GtkWidget *widget)
737 gtk_box_pack_start(GTK_BOX(dlg->action_area), widget, FALSE, TRUE, 0);
738 gtk_size_group_add_widget(gui_action, widget);
740 return widget;
743 /**************************************************************************
744 Changes the default dialog response.
745 **************************************************************************/
746 void gui_dialog_set_default_response(struct gui_dialog *dlg, int response)
748 GList *children;
749 GList *list;
751 children = gtk_container_get_children(GTK_CONTAINER(dlg->action_area));
753 for (list = children; list; list = g_list_next(list)) {
754 GtkWidget *button = list->data;
756 if (GTK_IS_BUTTON(button)) {
757 gpointer data = g_object_get_data(G_OBJECT(button),
758 "gui-dialog-response-data");
760 if (response == GPOINTER_TO_INT(data)) {
761 gtk_widget_grab_default(button);
766 g_list_free(children);
769 /**************************************************************************
770 Change the sensitivity of a dialog button.
771 **************************************************************************/
772 void gui_dialog_set_response_sensitive(struct gui_dialog *dlg,
773 int response, bool setting)
775 GList *children;
776 GList *list;
778 children = gtk_container_get_children(GTK_CONTAINER(dlg->action_area));
780 for (list = children; list; list = g_list_next(list)) {
781 GtkWidget *button = list->data;
783 if (GTK_IS_BUTTON(button)) {
784 gpointer data = g_object_get_data(G_OBJECT(button),
785 "gui-dialog-response-data");
787 if (response == GPOINTER_TO_INT(data)) {
788 gtk_widget_set_sensitive(button, setting);
793 g_list_free(children);
796 /**************************************************************************
797 Get the dialog's toplevel window.
798 **************************************************************************/
799 GtkWidget *gui_dialog_get_toplevel(struct gui_dialog *dlg)
801 return gtk_widget_get_toplevel(dlg->vbox);
804 /**************************************************************************
805 Show the dialog contents, but not the dialog per se.
806 **************************************************************************/
807 void gui_dialog_show_all(struct gui_dialog *dlg)
809 gtk_widget_show_all(dlg->vbox);
811 if (dlg->type == GUI_DIALOG_TAB) {
812 GList *children;
813 GList *list;
814 gint num_visible = 0;
816 children = gtk_container_get_children(GTK_CONTAINER(dlg->action_area));
818 for (list = children; list; list = g_list_next(list)) {
819 GtkWidget *button = list->data;
821 if (!GTK_IS_BUTTON(button)) {
822 num_visible++;
823 } else {
824 gpointer data = g_object_get_data(G_OBJECT(button),
825 "gui-dialog-response-data");
826 int response = GPOINTER_TO_INT(data);
828 if (response != GTK_RESPONSE_CLOSE
829 && response != GTK_RESPONSE_CANCEL) {
830 num_visible++;
831 } else {
832 gtk_widget_hide(button);
836 g_list_free(children);
838 if (num_visible == 0) {
839 gtk_widget_hide(dlg->action_area);
844 /**************************************************************************
845 Notify the user the dialog has changed.
846 **************************************************************************/
847 void gui_dialog_present(struct gui_dialog *dlg)
849 fc_assert_ret(NULL != dlg);
851 switch (dlg->type) {
852 case GUI_DIALOG_WINDOW:
853 gtk_widget_show(dlg->v.window);
854 break;
855 case GUI_DIALOG_TAB:
857 GtkNotebook *notebook = GTK_NOTEBOOK(dlg->v.tab.notebook);
858 gint current, n;
860 current = gtk_notebook_get_current_page(notebook);
861 n = gtk_notebook_page_num(notebook, dlg->vbox);
863 if (current != n) {
864 GtkWidget *label = dlg->v.tab.label;
865 GdkColormap *cmap = gtk_widget_get_default_colormap();
866 GdkColor color = {.red = 255 << 8, .green = 0, .blue = 0};
868 gdk_rgb_find_color(cmap, &color);
869 gtk_widget_modify_fg(label, GTK_STATE_ACTIVE, &color);
872 break;
876 /**************************************************************************
877 Raise dialog to top.
878 **************************************************************************/
879 void gui_dialog_raise(struct gui_dialog *dlg)
881 fc_assert_ret(NULL != dlg);
883 switch (dlg->type) {
884 case GUI_DIALOG_WINDOW:
885 gtk_window_present(GTK_WINDOW(dlg->v.window));
886 break;
887 case GUI_DIALOG_TAB:
889 GtkNotebook *notebook = GTK_NOTEBOOK(dlg->v.tab.notebook);
890 gint n;
892 n = gtk_notebook_page_num(notebook, dlg->vbox);
893 gtk_notebook_set_current_page(notebook, n);
895 break;
899 /**************************************************************************
900 Alert the user to an important event.
901 **************************************************************************/
902 void gui_dialog_alert(struct gui_dialog *dlg)
904 fc_assert_ret(NULL != dlg);
906 switch (dlg->type) {
907 case GUI_DIALOG_WINDOW:
908 break;
909 case GUI_DIALOG_TAB:
911 GtkNotebook *notebook = GTK_NOTEBOOK(dlg->v.tab.notebook);
912 gint current, n;
914 current = gtk_notebook_get_current_page(notebook);
915 n = gtk_notebook_page_num(notebook, dlg->vbox);
917 if (current != n) {
918 GtkWidget *label = dlg->v.tab.label;
919 GdkColormap *cmap = gtk_widget_get_default_colormap();
920 GdkColor color = {.red = 0, .green = 0, .blue = 255 << 8};
922 gdk_rgb_find_color(cmap, &color);
923 gtk_widget_modify_fg(label, GTK_STATE_ACTIVE, &color);
926 break;
930 /**************************************************************************
931 Sets the dialog's default size (applies to toplevel windows only).
932 **************************************************************************/
933 void gui_dialog_set_default_size(struct gui_dialog *dlg, int width, int height)
935 dlg->default_width = width;
936 dlg->default_height = height;
937 switch (dlg->type) {
938 case GUI_DIALOG_WINDOW:
939 gtk_window_set_default_size(GTK_WINDOW(dlg->v.window), width, height);
940 break;
941 case GUI_DIALOG_TAB:
942 break;
946 /**************************************************************************
947 Changes a dialog's title.
948 **************************************************************************/
949 void gui_dialog_set_title(struct gui_dialog *dlg, const char *title)
951 if (dlg->title) {
952 free(dlg->title);
954 dlg->title = fc_strdup(title);
955 switch (dlg->type) {
956 case GUI_DIALOG_WINDOW:
957 gtk_window_set_title(GTK_WINDOW(dlg->v.window), title);
958 break;
959 case GUI_DIALOG_TAB:
960 gtk_label_set_text_with_mnemonic(GTK_LABEL(dlg->v.tab.label), title);
961 break;
965 /**************************************************************************
966 Destroy a dialog.
967 **************************************************************************/
968 void gui_dialog_destroy(struct gui_dialog *dlg)
970 switch (dlg->type) {
971 case GUI_DIALOG_WINDOW:
972 gtk_widget_destroy(dlg->v.window);
973 break;
974 case GUI_DIALOG_TAB:
976 gint n;
978 n = gtk_notebook_page_num(GTK_NOTEBOOK(dlg->v.tab.notebook), dlg->vbox);
979 gtk_notebook_remove_page(GTK_NOTEBOOK(dlg->v.tab.notebook), n);
981 break;
985 /**************************************************************************
986 Destroy all dialogs.
987 **************************************************************************/
988 void gui_dialog_destroy_all(void)
990 GList *it, *it_next;
992 for (it = dialog_list; it; it = it_next) {
993 it_next = g_list_next(it);
995 gui_dialog_destroy((struct gui_dialog *)it->data);
999 /**************************************************************************
1000 Set the response callback for a dialog.
1001 **************************************************************************/
1002 void gui_dialog_response_set_callback(struct gui_dialog *dlg,
1003 GUI_DIALOG_RESPONSE_FUN fun)
1005 dlg->response_callback = fun;
1008 /**************************************************************************
1009 When the dlg dialog is destroyed the return_dialog will be raised
1010 **************************************************************************/
1011 void gui_dialog_set_return_dialog(struct gui_dialog *dlg,
1012 struct gui_dialog *return_dialog)
1014 if (return_dialog == NULL) {
1015 dlg->return_dialog_id = -1;
1016 } else {
1017 dlg->return_dialog_id = return_dialog->id;
1021 /**************************************************************************
1022 Updates a gui font style.
1023 **************************************************************************/
1024 void gui_update_font(const char *font_name, const char *font_value)
1026 char str[512];
1028 fc_snprintf(str, sizeof(str),
1029 "style \"ext-%s\" {\n"
1030 " font_name = \"%s\"\n"
1031 "}\n"
1032 "\n"
1033 "widget \"Freeciv*.%s\" style \"ext-%s\"",
1034 font_name, font_value, font_name, font_name);
1036 gtk_rc_parse_string(str);
1039 /****************************************************************************
1040 Update a font option which is not attached to a widget.
1041 ****************************************************************************/
1042 void gui_update_font_full(const char *font_name, const char *font_value,
1043 GtkStyle **pstyle)
1045 GtkSettings *settings;
1046 GdkScreen *screen;
1047 GtkStyle *style;
1048 char buf[64];
1050 gui_update_font(font_name, font_value);
1052 screen = gdk_screen_get_default();
1053 settings = gtk_settings_get_for_screen(screen);
1055 fc_snprintf(buf, sizeof(buf), "Freeciv*.%s", font_name);
1056 style = gtk_rc_get_style_by_paths(settings, buf, NULL, G_TYPE_NONE);
1058 if (style) {
1059 g_object_ref(style);
1060 } else {
1061 style = gtk_style_new();
1064 if (*pstyle) {
1065 g_object_unref(*pstyle);
1067 *pstyle = style;
1070 /****************************************************************************
1071 Temporarily disable signal invocation of the given callback for the given
1072 GObject. Re-enable the signal with enable_gobject_callback.
1073 ****************************************************************************/
1074 void disable_gobject_callback(GObject *obj, GCallback cb)
1076 gulong hid;
1078 if (!obj || !cb) {
1079 return;
1082 hid = g_signal_handler_find(obj, G_SIGNAL_MATCH_FUNC,
1083 0, 0, NULL, cb, NULL);
1084 g_signal_handler_block(obj, hid);
1087 /****************************************************************************
1088 Re-enable a signal callback blocked by disable_gobject_callback.
1089 ****************************************************************************/
1090 void enable_gobject_callback(GObject *obj, GCallback cb)
1092 gulong hid;
1094 if (!obj || !cb) {
1095 return;
1098 hid = g_signal_handler_find(obj, G_SIGNAL_MATCH_FUNC,
1099 0, 0, NULL, cb, NULL);
1100 g_signal_handler_unblock(obj, hid);
1103 /**************************************************************************
1104 Convenience function to add a column to a GtkTreeView. Returns the added
1105 column, or NULL if an error occurred.
1106 **************************************************************************/
1107 GtkTreeViewColumn *add_treeview_column(GtkWidget *view, const char *title,
1108 GType gtype, int model_index)
1110 GtkTreeViewColumn *col;
1111 GtkCellRenderer *rend;
1112 const char *attr;
1114 fc_assert_ret_val(view != NULL, NULL);
1115 fc_assert_ret_val(GTK_IS_TREE_VIEW(view), NULL);
1116 fc_assert_ret_val(title != NULL, NULL);
1118 if (gtype == G_TYPE_BOOLEAN) {
1119 rend = gtk_cell_renderer_toggle_new();
1120 attr = "active";
1121 } else if (gtype == GDK_TYPE_PIXBUF) {
1122 rend = gtk_cell_renderer_pixbuf_new();
1123 attr = "pixbuf";
1124 } else {
1125 rend = gtk_cell_renderer_text_new();
1126 attr = "text";
1129 col = gtk_tree_view_column_new_with_attributes(title, rend, attr,
1130 model_index, NULL);
1131 gtk_tree_view_append_column(GTK_TREE_VIEW(view), col);
1132 return col;