1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
2 * gdl-combo-button.c
3 *
4 * Copyright (C) 2003 Jeroen Zwartepoorte
5 *
6 *
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version.
11 *
12 * This library 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 GNU
15 * Lesser General Public License for more details.
16 *
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
20 */
22 #ifdef HAVE_CONFIG_H
23 #include <config.h>
24 #endif
26 #include <gtk/gtk.h>
27 #include "gdl-tools.h"
28 #include "gdl-combo-button.h"
30 struct _GdlComboButtonPrivate {
31 GtkWidget *default_button;
32 GtkWidget *image;
33 GtkWidget *label;
34 GtkWidget *menu_button;
35 GtkWidget *menu;
36 gboolean menu_popped_up;
37 };
39 GDL_CLASS_BOILERPLATE (GdlComboButton, gdl_combo_button, GtkHBox, GTK_TYPE_HBOX);
41 static void
42 default_button_clicked_cb (GtkButton *button,
43 gpointer user_data)
44 {
45 GdlComboButton *combo;
46 GdlComboButtonPrivate *priv;
48 combo = GDL_COMBO_BUTTON (user_data);
49 priv = combo->priv;
51 if (!priv->menu_popped_up)
52 g_signal_emit_by_name (G_OBJECT (combo),
53 "activate-default", NULL);
54 }
56 static gboolean
57 default_button_press_event_cb (GtkWidget *widget,
58 GdkEventButton *event,
59 gpointer user_data)
60 {
61 GdlComboButton *combo_button;
62 GdlComboButtonPrivate *priv;
64 combo_button = GDL_COMBO_BUTTON (user_data);
65 priv = combo_button->priv;
67 if (event->type == GDK_BUTTON_PRESS && event->button == 1) {
68 GTK_BUTTON (priv->menu_button)->button_down = TRUE;
69 gtk_button_pressed (GTK_BUTTON (priv->menu_button));
70 }
72 return FALSE;
73 }
75 static gboolean
76 default_button_release_event_cb (GtkWidget *widget,
77 GdkEventButton *event,
78 gpointer user_data)
79 {
80 GdlComboButton *combo_button;
81 GdlComboButtonPrivate *priv;
83 combo_button = GDL_COMBO_BUTTON (user_data);
84 priv = combo_button->priv;
86 if (event->button == 1) {
87 gtk_button_released (GTK_BUTTON (priv->menu_button));
88 }
90 return FALSE;
91 }
93 static gboolean
94 button_enter_notify_cb (GtkWidget *widget,
95 GdkEventCrossing *event,
96 gpointer user_data)
97 {
98 GdlComboButton *combo_button;
99 GdlComboButtonPrivate *priv;
101 combo_button = GDL_COMBO_BUTTON (user_data);
102 priv = combo_button->priv;
104 if (event->detail != GDK_NOTIFY_INFERIOR) {
105 GTK_BUTTON (priv->default_button)->in_button = TRUE;
106 GTK_BUTTON (priv->menu_button)->in_button = TRUE;
107 gtk_button_enter (GTK_BUTTON (priv->default_button));
108 gtk_button_enter (GTK_BUTTON (priv->menu_button));
109 }
111 return TRUE;
112 }
114 static gboolean
115 button_leave_notify_cb (GtkWidget *widget,
116 GdkEventCrossing *event,
117 gpointer user_data)
118 {
119 GdlComboButton *combo_button;
120 GdlComboButtonPrivate *priv;
122 combo_button = GDL_COMBO_BUTTON (user_data);
123 priv = combo_button->priv;
125 if (priv->menu_popped_up)
126 return TRUE;
128 if (event->detail != GDK_NOTIFY_INFERIOR) {
129 GTK_BUTTON (priv->default_button)->in_button = FALSE;
130 GTK_BUTTON (priv->menu_button)->in_button = FALSE;
131 gtk_button_leave (GTK_BUTTON (priv->default_button));
132 gtk_button_leave (GTK_BUTTON (priv->menu_button));
133 }
135 return TRUE;
136 }
138 static void
139 menu_position_func (GtkMenu *menu,
140 gint *x_return,
141 gint *y_return,
142 gboolean *push_in,
143 gpointer user_data)
144 {
145 GdlComboButton *combo_button;
146 GdlComboButtonPrivate *priv;
147 GtkAllocation *allocation;
149 combo_button = GDL_COMBO_BUTTON (user_data);
150 priv = combo_button->priv;
151 allocation = &(priv->default_button->allocation);
153 gdk_window_get_origin (priv->default_button->window, x_return, y_return);
155 *x_return += allocation->x;
156 *y_return += allocation->height;
157 }
159 static gboolean
160 menu_button_press_event_cb (GtkWidget *widget,
161 GdkEventButton *event,
162 gpointer user_data)
163 {
164 GdlComboButton *combo_button;
165 GdlComboButtonPrivate *priv;
167 combo_button = GDL_COMBO_BUTTON (user_data);
168 priv = combo_button->priv;
170 if (event->type == GDK_BUTTON_PRESS &&
171 (event->button == 1 || event->button == 3)) {
172 GTK_BUTTON (priv->menu_button)->button_down = TRUE;
174 gtk_button_pressed (GTK_BUTTON (priv->menu_button));
176 priv->menu_popped_up = TRUE;
177 gtk_menu_popup (GTK_MENU (priv->menu), NULL, NULL,
178 menu_position_func, combo_button,
179 event->button, event->time);
180 }
182 return TRUE;
183 }
185 static void
186 menu_deactivate_cb (GtkMenuShell *menu_shell,
187 gpointer user_data)
188 {
189 GdlComboButton *combo_button;
190 GdlComboButtonPrivate *priv;
192 combo_button = GDL_COMBO_BUTTON (user_data);
193 priv = combo_button->priv;
195 priv->menu_popped_up = FALSE;
197 GTK_BUTTON (priv->menu_button)->button_down = FALSE;
198 GTK_BUTTON (priv->menu_button)->in_button = FALSE;
199 GTK_BUTTON (priv->default_button)->in_button = FALSE;
200 gtk_button_leave (GTK_BUTTON (priv->menu_button));
201 gtk_button_leave (GTK_BUTTON (priv->default_button));
202 gtk_button_clicked (GTK_BUTTON (priv->menu_button));
203 }
205 static void
206 menu_detacher (GtkWidget *widget,
207 GtkMenu *menu)
208 {
209 GdlComboButton *combo_button;
211 combo_button = GDL_COMBO_BUTTON (widget);
213 g_signal_handlers_disconnect_by_func (G_OBJECT (menu),
214 menu_deactivate_cb,
215 combo_button);
216 combo_button->priv->menu = NULL;
217 }
219 static void
220 gdl_combo_button_destroy (GtkObject *object)
221 {
222 GdlComboButton *combo_button;
223 GdlComboButtonPrivate *priv;
225 combo_button = GDL_COMBO_BUTTON (object);
226 priv = combo_button->priv;
228 if (priv) {
229 g_free (priv);
230 combo_button->priv = NULL;
231 }
233 (* GTK_OBJECT_CLASS (parent_class)->destroy) (object);
234 }
236 static void
237 gdl_combo_button_class_init (GdlComboButtonClass *klass)
238 {
239 GtkObjectClass *object_class;
240 GtkWidgetClass *widget_class;
242 parent_class = g_type_class_peek_parent (klass);
243 object_class = GTK_OBJECT_CLASS (klass);
244 widget_class = GTK_WIDGET_CLASS (klass);
246 object_class->destroy = gdl_combo_button_destroy;
248 g_signal_new ("activate-default",
249 G_TYPE_FROM_CLASS (klass),
250 G_SIGNAL_RUN_FIRST,
251 G_STRUCT_OFFSET (GdlComboButtonClass, activate_default),
252 NULL, NULL,
253 g_cclosure_marshal_VOID__VOID,
254 G_TYPE_NONE, 0);
255 }
257 static void
258 gdl_combo_button_instance_init (GdlComboButton *combo_button)
259 {
260 GdlComboButtonPrivate *priv;
261 GtkWidget *hbox, *align, *arrow;
263 priv = g_new (GdlComboButtonPrivate, 1);
264 combo_button->priv = priv;
266 priv->menu = NULL;
267 priv->menu_popped_up = FALSE;
269 priv->default_button = gtk_button_new ();
270 gtk_button_set_relief (GTK_BUTTON (priv->default_button), GTK_RELIEF_NONE);
272 /* Following code copied from gtk_button_construct_child. */
273 priv->label = gtk_label_new ("");
274 gtk_label_set_use_underline (GTK_LABEL (priv->label), TRUE);
275 gtk_label_set_mnemonic_widget (GTK_LABEL (priv->label),
276 priv->default_button);
278 priv->image = gtk_image_new ();
279 hbox = gtk_hbox_new (FALSE, 2);
281 align = gtk_alignment_new (0.5, 0.5, 0.0, 0.0);
283 gtk_box_pack_start (GTK_BOX (hbox), priv->image, FALSE, FALSE, 0);
284 gtk_box_pack_end (GTK_BOX (hbox), priv->label, FALSE, FALSE, 0);
286 gtk_container_add (GTK_CONTAINER (priv->default_button), align);
287 gtk_container_add (GTK_CONTAINER (align), hbox);
288 /* End copied block. */
290 gtk_box_pack_start (GTK_BOX (combo_button), priv->default_button,
291 FALSE, FALSE, 0);
292 gtk_widget_show_all (priv->default_button);
294 priv->menu_button = gtk_button_new ();
295 gtk_button_set_relief (GTK_BUTTON (priv->menu_button), GTK_RELIEF_NONE);
296 arrow = gtk_arrow_new (GTK_ARROW_DOWN, GTK_SHADOW_NONE);
297 gtk_container_add (GTK_CONTAINER (priv->menu_button), arrow);
298 gtk_box_pack_start (GTK_BOX (combo_button), priv->menu_button, FALSE,
299 FALSE, 0);
300 gtk_widget_show_all (priv->menu_button);
302 /* Default button. */
303 g_signal_connect (G_OBJECT (priv->default_button), "clicked",
304 G_CALLBACK (default_button_clicked_cb), combo_button);
305 g_signal_connect (G_OBJECT (priv->default_button), "button_press_event",
306 G_CALLBACK (default_button_press_event_cb), combo_button);
307 g_signal_connect (G_OBJECT (priv->default_button), "button_release_event",
308 G_CALLBACK (default_button_release_event_cb), combo_button);
309 g_signal_connect (G_OBJECT (priv->default_button), "enter_notify_event",
310 G_CALLBACK (button_enter_notify_cb), combo_button);
311 g_signal_connect (G_OBJECT (priv->default_button), "leave_notify_event",
312 G_CALLBACK (button_leave_notify_cb), combo_button);
314 /* Menu button. */
315 g_signal_connect (G_OBJECT (priv->menu_button), "button_press_event",
316 G_CALLBACK (menu_button_press_event_cb), combo_button);
317 g_signal_connect (G_OBJECT (priv->menu_button), "enter_notify_event",
318 G_CALLBACK (button_enter_notify_cb), combo_button);
319 g_signal_connect (G_OBJECT (priv->menu_button), "leave_notify_event",
320 G_CALLBACK (button_leave_notify_cb), combo_button);
321 }
323 GtkWidget *
324 gdl_combo_button_new (void)
325 {
326 GtkWidget *combo_button;
328 combo_button = GTK_WIDGET (g_object_new (GDL_TYPE_COMBO_BUTTON, NULL));
330 return combo_button;
331 }
333 void
334 gdl_combo_button_set_icon (GdlComboButton *combo_button,
335 GdkPixbuf *pixbuf)
336 {
337 GdlComboButtonPrivate *priv;
339 g_return_if_fail (GDL_IS_COMBO_BUTTON (combo_button));
340 g_return_if_fail (GDK_IS_PIXBUF (pixbuf));
342 priv = combo_button->priv;
344 gtk_image_set_from_pixbuf (GTK_IMAGE (priv->image), pixbuf);
345 }
347 void
348 gdl_combo_button_set_label (GdlComboButton *combo_button,
349 const gchar *label)
350 {
351 GdlComboButtonPrivate *priv;
353 g_return_if_fail (GDL_IS_COMBO_BUTTON (combo_button));
354 g_return_if_fail (label != NULL);
356 priv = combo_button->priv;
358 gtk_label_set_text (GTK_LABEL (priv->label), label);
359 }
361 void
362 gdl_combo_button_set_menu (GdlComboButton *combo_button,
363 GtkMenu *menu)
364 {
365 GdlComboButtonPrivate *priv;
367 g_return_if_fail (GDL_IS_COMBO_BUTTON (combo_button));
368 g_return_if_fail (GTK_IS_MENU (menu));
370 priv = combo_button->priv;
372 if (priv->menu != NULL)
373 gtk_menu_detach (GTK_MENU (priv->menu));
375 priv->menu = GTK_WIDGET (menu);
376 if (menu == NULL)
377 return;
379 gtk_menu_attach_to_widget (menu, GTK_WIDGET (combo_button), menu_detacher);
381 g_signal_connect (G_OBJECT (menu), "deactivate",
382 G_CALLBACK (menu_deactivate_cb), combo_button);
383 }