]> icculus.org git repositories - divverent/netradiant.git/blob - libs/gtkutil/menu.cpp
correctly support [ and ] keys
[divverent/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/gtkmenu.h>
26 #include <gtk/gtkmenubar.h>
27 #include <gtk/gtkradiomenuitem.h>
28 #include <gtk/gtktearoffmenuitem.h>
29 #include <gtk/gtkaccellabel.h>
30
31 #include "generic/callback.h"
32
33 #include "accelerator.h"
34 #include "closure.h"
35 #include "container.h"
36 #include "pointer.h"
37
38 void menu_add_item(GtkMenu* menu, GtkMenuItem* item)
39 {
40         gtk_container_add(GTK_CONTAINER(menu), GTK_WIDGET(item));
41 }
42
43 GtkMenuItem* menu_separator(GtkMenu* menu)
44 {
45   GtkMenuItem* menu_item = GTK_MENU_ITEM(gtk_menu_item_new());
46   container_add_widget(GTK_CONTAINER(menu), GTK_WIDGET(menu_item));
47   gtk_widget_set_sensitive(GTK_WIDGET(menu_item), FALSE);
48   gtk_widget_show(GTK_WIDGET(menu_item));
49   return menu_item;
50 }
51
52 GtkTearoffMenuItem* menu_tearoff(GtkMenu* menu)
53 {
54   GtkTearoffMenuItem* menu_item = GTK_TEAROFF_MENU_ITEM(gtk_tearoff_menu_item_new());
55   container_add_widget(GTK_CONTAINER(menu), GTK_WIDGET(menu_item));
56 // gtk_widget_set_sensitive(GTK_WIDGET(menu_item), FALSE); -- controls whether menu is detachable
57   gtk_widget_show(GTK_WIDGET(menu_item));
58   return menu_item;
59 }
60
61 GtkMenuItem* new_sub_menu_item_with_mnemonic(const char* mnemonic)
62 {
63   GtkMenuItem* item = GTK_MENU_ITEM(gtk_menu_item_new_with_mnemonic(mnemonic));
64   gtk_widget_show(GTK_WIDGET(item));
65
66   GtkWidget* sub_menu = gtk_menu_new();
67   gtk_menu_item_set_submenu(item, sub_menu);
68
69   return item;
70 }
71
72 GtkMenu* create_sub_menu_with_mnemonic(GtkMenuShell* parent, const char* mnemonic)
73 {
74   GtkMenuItem* item = new_sub_menu_item_with_mnemonic(mnemonic);
75   container_add_widget(GTK_CONTAINER(parent), GTK_WIDGET(item));
76   return GTK_MENU(gtk_menu_item_get_submenu(item));
77 }
78
79 GtkMenu* create_sub_menu_with_mnemonic(GtkMenuBar* bar, const char* mnemonic)
80 {
81   return create_sub_menu_with_mnemonic(GTK_MENU_SHELL(bar), mnemonic);
82 }
83
84 GtkMenu* create_sub_menu_with_mnemonic(GtkMenu* parent, const char* mnemonic)
85 {
86   return create_sub_menu_with_mnemonic(GTK_MENU_SHELL(parent), mnemonic);
87 }
88
89 void activate_closure_callback(GtkWidget* widget, gpointer data)
90 {
91   (*reinterpret_cast<Callback*>(data))();
92 }
93
94 guint menu_item_connect_callback(GtkMenuItem* item, const Callback& callback)
95 {
96 #if 1
97   return g_signal_connect_swapped(G_OBJECT(item), "activate", G_CALLBACK(callback.getThunk()), callback.getEnvironment());
98 #else
99   return g_signal_connect_closure(G_OBJECT(item), "activate", create_cclosure(G_CALLBACK(activate_closure_callback), callback), FALSE);
100 #endif
101 }
102
103 guint check_menu_item_connect_callback(GtkCheckMenuItem* item, const Callback& callback)
104 {
105 #if 1
106   guint handler = g_signal_connect_swapped(G_OBJECT(item), "toggled", G_CALLBACK(callback.getThunk()), 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 GtkMenuItem* new_menu_item_with_mnemonic(const char *mnemonic, const Callback& callback)
115 {
116   GtkMenuItem* item = GTK_MENU_ITEM(gtk_menu_item_new_with_mnemonic(mnemonic));
117   gtk_widget_show(GTK_WIDGET(item));
118   menu_item_connect_callback(item, callback);
119   return item;
120 }
121
122 GtkMenuItem* create_menu_item_with_mnemonic(GtkMenu* menu, const char *mnemonic, const Callback& callback)
123 {
124   GtkMenuItem* item = new_menu_item_with_mnemonic(mnemonic, callback);
125   container_add_widget(GTK_CONTAINER(menu), GTK_WIDGET(item));
126   return item;
127 }
128
129 GtkCheckMenuItem* new_check_menu_item_with_mnemonic(const char* mnemonic, const Callback& callback)
130 {
131   GtkCheckMenuItem* item = GTK_CHECK_MENU_ITEM(gtk_check_menu_item_new_with_mnemonic(mnemonic));
132   gtk_widget_show(GTK_WIDGET(item));
133   check_menu_item_connect_callback(item, callback);
134   return item;
135 }
136
137 GtkCheckMenuItem* create_check_menu_item_with_mnemonic(GtkMenu* menu, const char* mnemonic, const Callback& callback)
138 {
139   GtkCheckMenuItem* item = new_check_menu_item_with_mnemonic(mnemonic, callback);
140   container_add_widget(GTK_CONTAINER(menu), GTK_WIDGET(item));
141   return item;
142 }
143
144 GtkRadioMenuItem* new_radio_menu_item_with_mnemonic(GSList** group, const char* mnemonic, const Callback& callback)
145 {
146   GtkRadioMenuItem* item = GTK_RADIO_MENU_ITEM(gtk_radio_menu_item_new_with_mnemonic(*group, mnemonic));
147   if(*group == 0)
148   {
149     gtk_check_menu_item_set_state(GTK_CHECK_MENU_ITEM(item), TRUE);
150   }
151   *group = gtk_radio_menu_item_group(item);
152   gtk_widget_show(GTK_WIDGET(item));
153   check_menu_item_connect_callback(GTK_CHECK_MENU_ITEM(item), callback);
154   return item;
155 }
156
157 GtkRadioMenuItem* create_radio_menu_item_with_mnemonic(GtkMenu* menu, GSList** group, const char* mnemonic, const Callback& callback)
158 {
159   GtkRadioMenuItem* item = new_radio_menu_item_with_mnemonic(group, mnemonic, callback);
160   container_add_widget(GTK_CONTAINER(menu), GTK_WIDGET(item));
161   return item;
162 }
163
164 void check_menu_item_set_active_no_signal(GtkCheckMenuItem* item, gboolean active)
165 {
166   guint handler_id = gpointer_to_int(g_object_get_data(G_OBJECT(item), "handler"));
167   g_signal_handler_block(G_OBJECT(item), handler_id);
168   gtk_check_menu_item_set_active(item, active);
169   g_signal_handler_unblock(G_OBJECT(item), handler_id);
170 }
171
172
173
174 void radio_menu_item_set_active_no_signal(GtkRadioMenuItem* item, gboolean active)
175 {
176   {
177     for(GSList* l = gtk_radio_menu_item_get_group(item); l != 0; l = g_slist_next(l))
178     {
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     {
186       g_signal_handler_unblock(G_OBJECT(l->data), gpointer_to_int(g_object_get_data(G_OBJECT(l->data), "handler")));
187     }
188   }
189 }
190
191
192 void menu_item_set_accelerator(GtkMenuItem* 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   {
203     g_string_append (gstring, "Shift");
204     had_mod = TRUE;
205   }
206   if (accelerator.modifiers & GDK_CONTROL_MASK)
207   {
208     if (had_mod)
209       g_string_append (gstring, "+");
210     g_string_append (gstring, "Ctrl");
211     had_mod = TRUE;
212   }
213   if (accelerator.modifiers & GDK_MOD1_MASK)
214   {
215     if (had_mod)
216       g_string_append (gstring, "+");
217     g_string_append (gstring, "Alt");
218     had_mod = TRUE;
219   }
220
221   if (had_mod)
222     g_string_append (gstring, "+");
223   if (accelerator.key < 0x80 || (accelerator.key > 0x80 && accelerator.key <= 0xff))
224   {
225     switch (accelerator.key)
226     {
227     case ' ':
228       g_string_append (gstring, "Space");
229       break;
230     case '\\':
231       g_string_append (gstring, "Backslash");
232       break;
233     default:
234       g_string_append_c (gstring, gchar(toupper(accelerator.key)));
235       break;
236     }
237   }
238   else
239   {
240     gchar *tmp;
241
242     tmp = gtk_accelerator_name (accelerator.key, (GdkModifierType)0);
243     if (tmp[0] != 0 && tmp[1] == 0)
244       tmp[0] = gchar(toupper(tmp[0]));
245     g_string_append (gstring, tmp);
246     g_free (tmp);
247   }
248 }
249
250 void menu_item_set_accelerator(GtkMenuItem* item, Accelerator accelerator)
251 {
252   GtkAccelLabel* accel_label = GTK_ACCEL_LABEL(gtk_bin_get_child(GTK_BIN(item)));
253
254   g_free (accel_label->accel_string);
255   accel_label->accel_string = 0;
256
257   GString* gstring = g_string_new (accel_label->accel_string);
258   g_string_append (gstring, "   ");
259
260   accelerator_name(accelerator, gstring);
261
262   g_free (accel_label->accel_string);
263   accel_label->accel_string = gstring->str;
264   g_string_free (gstring, FALSE);
265
266   if (!accel_label->accel_string)
267     accel_label->accel_string = g_strdup ("");
268
269   gtk_widget_queue_resize (GTK_WIDGET (accel_label));
270 }
271
272 void menu_item_add_accelerator(GtkMenuItem* item, Accelerator accelerator)
273 {
274   if(accelerator.key != 0)
275   {
276     GClosure* closure = global_accel_group_find(accelerator);
277     if(closure != 0)
278     {
279       menu_item_set_accelerator(item, closure);
280     }
281     else
282     {
283       menu_item_set_accelerator(item, accelerator);
284     }
285   }
286 }
287
288 GtkMenuItem* create_menu_item_with_mnemonic(GtkMenu* menu, const char* mnemonic, const Command& command)
289 {
290   GtkMenuItem* item = create_menu_item_with_mnemonic(menu, mnemonic, command.m_callback);
291   menu_item_add_accelerator(item, command.m_accelerator);
292   return item;
293 }
294
295 void check_menu_item_set_active_callback(GtkCheckMenuItem& item, bool enabled)
296 {
297   check_menu_item_set_active_no_signal(&item, enabled);
298 }
299 typedef ReferenceCaller1<GtkCheckMenuItem, bool, check_menu_item_set_active_callback> CheckMenuItemSetActiveCaller;
300
301 GtkCheckMenuItem* create_check_menu_item_with_mnemonic(GtkMenu* menu, const char* mnemonic, const Toggle& toggle)
302 {
303   GtkCheckMenuItem* item = create_check_menu_item_with_mnemonic(menu, mnemonic, toggle.m_command.m_callback);
304   menu_item_add_accelerator(GTK_MENU_ITEM(item), toggle.m_command.m_accelerator);
305   toggle.m_exportCallback(CheckMenuItemSetActiveCaller(*item));
306   return item;
307 }
308
309
310
311