]> de.git.xonotic.org Git - xonotic/netradiant.git/blob - libs/gtkutil/menu.cpp
allow undo “make detail/structural”, <3 @SpiKe, thanks @Garux, fix #76
[xonotic/netradiant.git] / libs / gtkutil / menu.cpp
1 /*
2    Copyright (C) 2001-2006, William Joseph.
3    All Rights Reserved.
4
5    This file is part of GtkRadiant.
6
7    GtkRadiant is free software; you can redistribute it and/or modify
8    it under the terms of the GNU General Public License as published by
9    the Free Software Foundation; either version 2 of the License, or
10    (at your option) any later version.
11
12    GtkRadiant is distributed in the hope that it will be useful,
13    but WITHOUT ANY WARRANTY; without even the implied warranty of
14    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15    GNU General Public License for more details.
16
17    You should have received a copy of the GNU General Public License
18    along with GtkRadiant; if not, write to the Free Software
19    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
20  */
21
22 #include "menu.h"
23
24 #include <ctype.h>
25 #include <gtk/gtk.h>
26 #include <uilib/uilib.h>
27 #include <debugging/debugging.h>
28
29 #include "generic/callback.h"
30
31 #include "accelerator.h"
32 #include "closure.h"
33 #include "container.h"
34 #include "pointer.h"
35
36 void menu_add_item(ui::Menu menu, ui::MenuItem item)
37 {
38     menu.add(item);
39 }
40
41 ui::MenuItem menu_separator(ui::Menu menu)
42 {
43     auto menu_item = ui::MenuItem::from(gtk_menu_item_new());
44     menu.add(menu_item);
45     gtk_widget_set_sensitive(menu_item, FALSE);
46     menu_item.show();
47     return menu_item;
48 }
49
50 ui::TearoffMenuItem menu_tearoff(ui::Menu menu)
51 {
52     auto menu_item = ui::TearoffMenuItem::from(gtk_tearoff_menu_item_new());
53     menu.add(menu_item);
54 // gtk_widget_set_sensitive(menu_item, FALSE); -- controls whether menu is detachable
55     menu_item.show();
56     return menu_item;
57 }
58
59 ui::MenuItem new_sub_menu_item_with_mnemonic(const char *mnemonic)
60 {
61     auto item = ui::MenuItem(mnemonic, true);
62     item.show();
63
64     auto sub_menu = ui::Menu(ui::New);
65     gtk_menu_item_set_submenu(item, sub_menu);
66
67     return item;
68 }
69
70 ui::Menu create_sub_menu_with_mnemonic(ui::MenuShell parent, const char *mnemonic)
71 {
72     auto item = new_sub_menu_item_with_mnemonic(mnemonic);
73     parent.add(item);
74     return ui::Menu::from(gtk_menu_item_get_submenu(item));
75 }
76
77 ui::Menu create_sub_menu_with_mnemonic(ui::MenuBar bar, const char *mnemonic)
78 {
79     return create_sub_menu_with_mnemonic(ui::MenuShell::from(bar._handle), mnemonic);
80 }
81
82 ui::Menu create_sub_menu_with_mnemonic(ui::Menu parent, const char *mnemonic)
83 {
84     return create_sub_menu_with_mnemonic(ui::MenuShell::from(parent._handle), mnemonic);
85 }
86
87 void activate_closure_callback(ui::Widget widget, gpointer data)
88 {
89     (*reinterpret_cast<Callback<void()> *>( data ))();
90 }
91
92 guint menu_item_connect_callback(ui::MenuItem item, const Callback<void()> &callback)
93 {
94 #if 1
95     return g_signal_connect_swapped(G_OBJECT(item), "activate", G_CALLBACK(callback.getThunk()),
96                                     callback.getEnvironment());
97 #else
98     return g_signal_connect_closure( G_OBJECT( item ), "activate", create_cclosure( G_CALLBACK( activate_closure_callback ), callback ), FALSE );
99 #endif
100 }
101
102 guint check_menu_item_connect_callback(ui::CheckMenuItem item, const Callback<void()> &callback)
103 {
104 #if 1
105     guint handler = g_signal_connect_swapped(G_OBJECT(item), "toggled", G_CALLBACK(callback.getThunk()),
106                                              callback.getEnvironment());
107 #else
108     guint handler = g_signal_connect_closure( G_OBJECT( item ), "toggled", create_cclosure( G_CALLBACK( activate_closure_callback ), callback ), TRUE );
109 #endif
110     g_object_set_data(G_OBJECT(item), "handler", gint_to_pointer(handler));
111     return handler;
112 }
113
114 ui::MenuItem new_menu_item_with_mnemonic(const char *mnemonic, const Callback<void()> &callback)
115 {
116     auto item = ui::MenuItem(mnemonic, true);
117     item.show();
118     menu_item_connect_callback(item, callback);
119     return item;
120 }
121
122 ui::MenuItem create_menu_item_with_mnemonic(ui::Menu menu, const char *mnemonic, const Callback<void()> &callback)
123 {
124     auto item = new_menu_item_with_mnemonic(mnemonic, callback);
125     menu.add(item);
126     return item;
127 }
128
129 ui::CheckMenuItem new_check_menu_item_with_mnemonic(const char *mnemonic, const Callback<void()> &callback)
130 {
131     auto item = ui::CheckMenuItem::from(gtk_check_menu_item_new_with_mnemonic(mnemonic));
132     item.show();
133     check_menu_item_connect_callback(item, callback);
134     return item;
135 }
136
137 ui::CheckMenuItem
138 create_check_menu_item_with_mnemonic(ui::Menu menu, const char *mnemonic, const Callback<void()> &callback)
139 {
140     auto item = new_check_menu_item_with_mnemonic(mnemonic, callback);
141     menu.add(item);
142     return item;
143 }
144
145 ui::RadioMenuItem
146 new_radio_menu_item_with_mnemonic(GSList **group, const char *mnemonic, const Callback<void()> &callback)
147 {
148     auto item = ui::RadioMenuItem::from(gtk_radio_menu_item_new_with_mnemonic(*group, mnemonic));
149     if (*group == 0) {
150         gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(item), TRUE);
151     }
152     *group = gtk_radio_menu_item_get_group(item);
153     item.show();
154     check_menu_item_connect_callback(item, callback);
155     return item;
156 }
157
158 ui::RadioMenuItem create_radio_menu_item_with_mnemonic(ui::Menu menu, GSList **group, const char *mnemonic,
159                                                        const Callback<void()> &callback)
160 {
161     auto item = new_radio_menu_item_with_mnemonic(group, mnemonic, callback);
162     menu.add(item);
163     return item;
164 }
165
166 void check_menu_item_set_active_no_signal(ui::CheckMenuItem item, gboolean active)
167 {
168     guint handler_id = gpointer_to_int(g_object_get_data(G_OBJECT(item), "handler"));
169     g_signal_handler_block(G_OBJECT(item), handler_id);
170     gtk_check_menu_item_set_active(item, active);
171     g_signal_handler_unblock(G_OBJECT(item), handler_id);
172 }
173
174
175 void radio_menu_item_set_active_no_signal(ui::RadioMenuItem item, gboolean active)
176 {
177     {
178         for (GSList *l = gtk_radio_menu_item_get_group(item); l != 0; l = g_slist_next(l)) {
179             g_signal_handler_block(G_OBJECT(l->data), gpointer_to_int(g_object_get_data(G_OBJECT(l->data), "handler")));
180         }
181     }
182     gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(item), active);
183     {
184         for (GSList *l = gtk_radio_menu_item_get_group(item); l != 0; l = g_slist_next(l)) {
185             g_signal_handler_unblock(G_OBJECT(l->data),
186                                      gpointer_to_int(g_object_get_data(G_OBJECT(l->data), "handler")));
187         }
188     }
189 }
190
191
192 void menu_item_set_accelerator(ui::MenuItem item, GClosure *closure)
193 {
194     GtkAccelLabel *accel_label = GTK_ACCEL_LABEL(gtk_bin_get_child(GTK_BIN(item)));
195     gtk_accel_label_set_accel_closure(accel_label, closure);
196 }
197
198 void accelerator_name(const Accelerator &accelerator, GString *gstring)
199 {
200     gboolean had_mod = FALSE;
201     if (accelerator.modifiers & GDK_SHIFT_MASK) {
202         g_string_append(gstring, "Shift");
203         had_mod = TRUE;
204     }
205     if (accelerator.modifiers & GDK_CONTROL_MASK) {
206         if (had_mod) {
207             g_string_append(gstring, "+");
208         }
209         g_string_append(gstring, "Ctrl");
210         had_mod = TRUE;
211     }
212     if (accelerator.modifiers & GDK_MOD1_MASK) {
213         if (had_mod) {
214             g_string_append(gstring, "+");
215         }
216         g_string_append(gstring, "Alt");
217         had_mod = TRUE;
218     }
219
220     if (had_mod) {
221         g_string_append(gstring, "+");
222     }
223     if (accelerator.key < 0x80 || (accelerator.key > 0x80 && accelerator.key <= 0xff)) {
224         switch (accelerator.key) {
225             case ' ':
226                 g_string_append(gstring, "Space");
227                 break;
228             case '\\':
229                 g_string_append(gstring, "Backslash");
230                 break;
231             default:
232                 g_string_append_c(gstring, gchar(toupper(accelerator.key)));
233                 break;
234         }
235     } else {
236         gchar *tmp;
237
238         tmp = gtk_accelerator_name(accelerator.key, (GdkModifierType) 0);
239         if (tmp[0] != 0 && tmp[1] == 0) {
240             tmp[0] = gchar(toupper(tmp[0]));
241         }
242         g_string_append(gstring, tmp);
243         g_free(tmp);
244     }
245 }
246
247 void menu_item_add_accelerator(ui::MenuItem item, Accelerator accelerator)
248 {
249     if (accelerator.key != 0 && gtk_accelerator_valid(accelerator.key, accelerator.modifiers)) {
250         GClosure *closure = global_accel_group_find(accelerator);
251         ASSERT_NOTNULL(closure);
252         menu_item_set_accelerator(item, closure);
253     }
254 }
255
256 ui::MenuItem create_menu_item_with_mnemonic(ui::Menu menu, const char *mnemonic, const Command &command)
257 {
258     auto item = create_menu_item_with_mnemonic(menu, mnemonic, command.m_callback);
259     menu_item_add_accelerator(item, command.m_accelerator);
260     return item;
261 }
262
263 void check_menu_item_set_active_callback(void *it, bool enabled)
264 {
265     auto item = ui::CheckMenuItem::from(it);
266     check_menu_item_set_active_no_signal(item, enabled);
267 }
268
269 ui::CheckMenuItem create_check_menu_item_with_mnemonic(ui::Menu menu, const char *mnemonic, const Toggle &toggle)
270 {
271     auto item = create_check_menu_item_with_mnemonic(menu, mnemonic, toggle.m_command.m_callback);
272     menu_item_add_accelerator(item, toggle.m_command.m_accelerator);
273     toggle.m_exportCallback(PointerCaller<void, void(bool), check_menu_item_set_active_callback>(item._handle));
274     return item;
275 }