Line data Source code
1 : /*
2 : * go-optionmenu.c
3 : *
4 : * Copyright (C) 2002 Andreas J. Guelzow <aguelzow@taliesin.ca>
5 : * Copyright (C) 2006 Morten Welinder (terra@gnome.org)
6 : *
7 : * based extensively on:
8 : *
9 : * GTK - The GIMP Toolkit
10 : * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
11 : *
12 : * Modified by the GTK+ Team and others 1997-2000. See the GTK AUTHORS
13 : * file for a list of people on the GTK+ Team. See the ChangeLog
14 : * files for a list of changes. These files are distributed with
15 : * GTK+ at ftp://ftp.gtk.org/pub/gtk/.
16 : *
17 : *
18 : * This library is free software; you can redistribute it and/or
19 : * modify it under the terms of the GNU Lesser General Public
20 : * License as published by the Free Software Foundation; either
21 : * version 2 of the License, or (at your option) any later version.
22 : *
23 : * This library is distributed in the hope that it will be useful,
24 : * but WITHOUT ANY WARRANTY; without even the implied warranty of
25 : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
26 : * Lesser General Public License for more details.
27 : *
28 : * You should have received a copy of the GNU Lesser General Public
29 : * License along with this library; if not, write to the Free Software
30 : * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301
31 : * USA.
32 : */
33 :
34 : #include <config.h>
35 : #include "go-optionmenu.h"
36 :
37 : #include <gdk/gdkkeysyms.h>
38 : #include <glib/gi18n-lib.h>
39 :
40 : enum
41 : {
42 : CHANGED, LAST_SIGNAL
43 : };
44 :
45 : enum
46 : {
47 : PROP_0,
48 : PROP_MENU
49 : };
50 :
51 : static guint signals[LAST_SIGNAL] = { 0 };
52 :
53 : struct _GOOptionMenu
54 : {
55 : GtkButton button;
56 :
57 : GtkMenuShell *menu;
58 : GtkMenuItem *selected;
59 : GtkLabel *button_label;
60 : };
61 :
62 0 : G_DEFINE_TYPE (GOOptionMenu, go_option_menu, GTK_TYPE_BUTTON)
63 :
64 : GtkWidget*
65 0 : go_option_menu_new(void)
66 : {
67 0 : return g_object_new(GO_TYPE_OPTION_MENU, NULL);
68 : }
69 :
70 0 : static void go_option_menu_detacher(GtkWidget *widget, GtkMenu *menu)
71 : {
72 : #if 0
73 : GOOptionMenu *option_menu = GO_OPTION_MENU (widget);
74 : /* What? */
75 : #endif
76 0 : }
77 :
78 0 : static void go_option_menu_update_contents(GOOptionMenu *option_menu)
79 : {
80 : const char *text;
81 : GtkWidget *w;
82 0 : g_return_if_fail(GO_IS_OPTION_MENU(option_menu));
83 :
84 0 : w = gtk_bin_get_child(GTK_BIN(option_menu->selected));
85 0 : text = g_object_get_data(G_OBJECT(w), "option-menu-text");
86 :
87 0 : if (!text && GTK_IS_LABEL(w))
88 0 : text = gtk_label_get_text(GTK_LABEL(w));
89 :
90 0 : if (!text)
91 0 : text = "";
92 :
93 : #if 0
94 : g_print ("text = \"%s\"\n", text);
95 : #endif
96 :
97 0 : gtk_label_set_text(option_menu->button_label, text);
98 : }
99 :
100 0 : static void go_option_menu_select_item(GOOptionMenu *option_menu,
101 : GtkMenuItem *item)
102 : {
103 0 : if (item == option_menu->selected)
104 0 : return;
105 :
106 0 : if (GTK_IS_CHECK_MENU_ITEM(option_menu->selected))
107 0 : gtk_check_menu_item_set_active(
108 0 : GTK_CHECK_MENU_ITEM(option_menu->selected), FALSE);
109 :
110 0 : option_menu->selected = item;
111 :
112 0 : if (GTK_IS_CHECK_MENU_ITEM(item))
113 0 : gtk_check_menu_item_set_active(
114 0 : GTK_CHECK_MENU_ITEM(option_menu->selected), TRUE);
115 :
116 0 : go_option_menu_update_contents(option_menu);
117 : }
118 :
119 0 : static gint go_option_menu_button_press(GtkWidget *widget,
120 : GdkEventButton *event)
121 : {
122 : GOOptionMenu *option_menu;
123 :
124 0 : g_return_val_if_fail(GO_IS_OPTION_MENU(widget), FALSE);
125 0 : g_return_val_if_fail(event != NULL, FALSE);
126 :
127 0 : option_menu = GO_OPTION_MENU(widget);
128 :
129 0 : if (event->type == GDK_BUTTON_PRESS && event->button == 1)
130 : {
131 0 : gtk_menu_popup_at_widget (GTK_MENU(option_menu->menu),
132 : widget,
133 : GDK_GRAVITY_SOUTH_WEST,
134 : GDK_GRAVITY_NORTH_WEST,
135 : (GdkEvent *) event);
136 0 : return TRUE;
137 : }
138 0 : return FALSE;
139 : }
140 :
141 0 : static gint go_option_menu_key_press(GtkWidget *widget, GdkEventKey *event)
142 : {
143 0 : GOOptionMenu *option_menu = GO_OPTION_MENU(widget);
144 :
145 0 : switch (event->keyval)
146 : {
147 0 : case GDK_KEY_KP_Space:
148 : case GDK_KEY_space:
149 0 : gtk_menu_popup_at_widget (GTK_MENU(option_menu->menu),
150 : widget,
151 : GDK_GRAVITY_SOUTH_WEST,
152 : GDK_GRAVITY_NORTH_WEST,
153 : (GdkEvent *) event);
154 0 : return TRUE;
155 : }
156 :
157 0 : return FALSE;
158 : }
159 :
160 0 : static void cb_select(GtkMenuItem *item, GOOptionMenu *option_menu)
161 : {
162 0 : go_option_menu_select_item(option_menu, item);
163 0 : g_signal_emit(option_menu, signals[CHANGED], 0);
164 0 : }
165 :
166 0 : static void handle_menu_signals(GOOptionMenu *option_menu, gboolean connect)
167 : {
168 0 : GList *children = gtk_container_get_children(
169 0 : GTK_CONTAINER(option_menu->menu));
170 :
171 0 : while (children)
172 : {
173 0 : GtkWidget *child = children->data;
174 0 : children = g_list_remove(children, child);
175 :
176 0 : if (GTK_IS_MENU_ITEM(child))
177 : {
178 0 : GtkWidget *sub = gtk_menu_item_get_submenu(GTK_MENU_ITEM(child));
179 :
180 0 : if (sub)
181 0 : children = g_list_concat(children,
182 0 : gtk_container_get_children(GTK_CONTAINER(sub)));
183 0 : else if (connect)
184 0 : g_signal_connect(child, "activate", G_CALLBACK(cb_select),
185 : option_menu);
186 :
187 : else
188 0 : g_signal_handlers_disconnect_by_func(child,
189 : G_CALLBACK(cb_select), option_menu);
190 :
191 : }
192 : }
193 :
194 0 : g_list_free (children);
195 0 : }
196 :
197 0 : void go_option_menu_set_menu(GOOptionMenu *option_menu, GtkWidget *menu)
198 : {
199 : GtkMenuShell *shell;
200 :
201 0 : g_return_if_fail(GO_IS_OPTION_MENU(option_menu));
202 0 : g_return_if_fail(GTK_IS_MENU_SHELL(menu));
203 :
204 0 : shell = (GtkMenuShell *) menu;
205 0 : if (option_menu->menu == shell)
206 0 : return;
207 :
208 0 : if (option_menu->menu)
209 : {
210 0 : gtk_menu_shell_cancel(option_menu->menu);
211 :
212 0 : handle_menu_signals(option_menu, FALSE);
213 :
214 0 : gtk_menu_detach(GTK_MENU(option_menu->menu));
215 0 : g_object_unref(option_menu->menu);
216 : }
217 :
218 0 : option_menu->menu = shell;
219 :
220 0 : if (shell)
221 : {
222 0 : g_object_ref(shell);
223 :
224 0 : gtk_menu_attach_to_widget(GTK_MENU(shell), GTK_WIDGET(option_menu),
225 : go_option_menu_detacher);
226 :
227 0 : handle_menu_signals(option_menu, TRUE);
228 :
229 0 : go_option_menu_select_item(option_menu,
230 0 : GTK_MENU_ITEM(gtk_menu_get_active(GTK_MENU(shell))));
231 : }
232 :
233 0 : g_object_notify(G_OBJECT(option_menu), "menu");
234 : }
235 :
236 0 : void go_option_menu_set_history(GOOptionMenu *option_menu, GSList *selection)
237 : {
238 0 : g_return_if_fail(selection != NULL);
239 0 : g_return_if_fail(GO_IS_OPTION_MENU(option_menu));
240 :
241 0 : if (option_menu->menu)
242 : {
243 0 : GtkMenuShell *menu = option_menu->menu;
244 :
245 : while (1)
246 0 : {
247 0 : int n = GPOINTER_TO_INT(selection->data);
248 0 : GList *children = gtk_container_get_children (GTK_CONTAINER(menu));
249 0 : GtkMenuItem *item = g_list_nth_data (children, n);
250 0 : g_list_free (children);
251 0 : selection = selection->next;
252 0 : if (selection)
253 0 : menu = GTK_MENU_SHELL(gtk_menu_item_get_submenu(item));
254 : else
255 : {
256 0 : go_option_menu_select_item(option_menu, item);
257 0 : break;
258 : }
259 : }
260 : }
261 : }
262 :
263 : /**
264 : * go_option_menu_get_history:
265 : * @option_menu: a #GOOptionMenu
266 : *
267 : * Retrieves the currently selected menu item.
268 : *
269 : * Return value: the selected menu_item
270 : **/
271 :
272 : GtkWidget *
273 0 : go_option_menu_get_history(GOOptionMenu *option_menu)
274 : {
275 0 : return GTK_WIDGET(option_menu->selected);
276 : }
277 :
278 0 : static void go_option_menu_set_property(GObject *object, guint prop_id,
279 : const GValue *value, GParamSpec *pspec)
280 : {
281 0 : GOOptionMenu *option_menu = GO_OPTION_MENU(object);
282 :
283 0 : switch (prop_id)
284 : {
285 0 : case PROP_MENU:
286 0 : go_option_menu_set_menu(option_menu, g_value_get_object(value));
287 0 : break;
288 :
289 0 : default:
290 0 : G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
291 0 : break;
292 : }
293 0 : }
294 :
295 0 : static void go_option_menu_get_property(GObject *object, guint prop_id,
296 : GValue *value, GParamSpec *pspec)
297 : {
298 0 : GOOptionMenu *option_menu = GO_OPTION_MENU(object);
299 :
300 0 : switch (prop_id)
301 : {
302 0 : case PROP_MENU:
303 0 : g_value_set_object(value, option_menu->menu);
304 0 : break;
305 :
306 0 : default:
307 0 : G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
308 0 : break;
309 : }
310 0 : }
311 :
312 0 : static void go_option_menu_destroy(GtkWidget *widget)
313 : {
314 : GOOptionMenu *option_menu;
315 :
316 0 : g_return_if_fail(GO_IS_OPTION_MENU(widget));
317 :
318 0 : option_menu = GO_OPTION_MENU(widget);
319 :
320 0 : if (option_menu->menu)
321 : {
322 0 : gtk_widget_destroy(GTK_WIDGET(option_menu->menu));
323 0 : g_object_unref(option_menu->menu);
324 0 : option_menu->menu = NULL;
325 : }
326 0 : option_menu->selected = NULL;
327 :
328 0 : GTK_WIDGET_CLASS(go_option_menu_parent_class)->destroy(widget);
329 : }
330 :
331 0 : static void go_option_menu_class_init(GOOptionMenuClass *class)
332 : {
333 0 : GObjectClass *gobject_class = (GObjectClass*) class;
334 0 : GtkWidgetClass *widget_class = (GtkWidgetClass*) class;
335 :
336 0 : signals[CHANGED] = g_signal_new("changed", G_OBJECT_CLASS_TYPE(class),
337 : G_SIGNAL_RUN_LAST, 0,
338 : NULL, NULL, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0);
339 :
340 0 : gobject_class->set_property = go_option_menu_set_property;
341 0 : gobject_class->get_property = go_option_menu_get_property;
342 0 : widget_class->destroy = go_option_menu_destroy;
343 0 : widget_class->button_press_event = go_option_menu_button_press;
344 0 : widget_class->key_press_event = go_option_menu_key_press;
345 :
346 0 : g_object_class_install_property(gobject_class, PROP_MENU,
347 : g_param_spec_object("menu", _("Menu"), _("The menu of options"),
348 : GTK_TYPE_MENU, G_PARAM_READABLE | G_PARAM_WRITABLE));
349 0 : }
350 :
351 0 : static void go_option_menu_init(GOOptionMenu *option_menu)
352 : {
353 : GtkWidget *box;
354 : GtkWidget *arrow, *sep;
355 :
356 0 : gtk_widget_set_can_focus(GTK_WIDGET(option_menu), TRUE);
357 0 : gtk_widget_set_can_default(GTK_WIDGET(option_menu), FALSE);
358 0 : gtk_widget_set_receives_default(GTK_WIDGET(option_menu), FALSE);
359 :
360 0 : box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
361 0 : gtk_box_set_homogeneous (GTK_BOX (box), FALSE);
362 :
363 0 : option_menu->menu = NULL;
364 0 : option_menu->selected = NULL;
365 :
366 0 : option_menu->button_label = GTK_LABEL(gtk_label_new(""));
367 0 : gtk_box_pack_start(GTK_BOX(box), GTK_WIDGET(option_menu->button_label), FALSE, TRUE, 0);
368 0 : arrow = gtk_image_new_from_icon_name ("pan-down-symbolic", GTK_ICON_SIZE_BUTTON);
369 0 : gtk_widget_set_margin_start (GTK_WIDGET(arrow), 5);
370 :
371 0 : gtk_box_pack_end(GTK_BOX(box), arrow, FALSE, FALSE, 0);
372 :
373 0 : sep = gtk_separator_new (GTK_ORIENTATION_VERTICAL);
374 0 : gtk_box_pack_end(GTK_BOX(box), sep, FALSE, FALSE, 0);
375 :
376 0 : gtk_container_add(GTK_CONTAINER(option_menu), GTK_WIDGET(box));
377 0 : }
|