Branch data 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 : }
|