1 /* GTK - The GIMP Toolkit
2 * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later version.
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library; if not, write to the
16 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17 * Boston, MA 02111-1307, USA.
21 * Modified by the GTK+ Team and others 1997-2000. See the AUTHORS
22 * file for a list of people on the GTK+ Team. See the ChangeLog
23 * files for a list of changes. These files are distributed with
24 * GTK+ at ftp://ftp.gtk.org/pub/gtk/.
28 #include "gdk/gdkkeysyms.h"
29 #include "gtkbindings.h"
33 #include "gtkmenuitem.h"
34 #include "gtksignal.h"
35 #include "gtkwindow.h"
38 #define MENU_ITEM_CLASS(w) GTK_MENU_ITEM_GET_CLASS (w)
39 #define MENU_NEEDS_RESIZE(m) GTK_MENU_SHELL (m)->menu_flag
41 typedef struct _GtkMenuAttachData GtkMenuAttachData;
43 struct _GtkMenuAttachData
45 GtkWidget *attach_widget;
46 GtkMenuDetachFunc detacher;
50 static void gtk_menu_class_init (GtkMenuClass *klass);
51 static void gtk_menu_init (GtkMenu *menu);
52 static void gtk_menu_destroy (GtkObject *object);
53 static void gtk_menu_realize (GtkWidget *widget);
54 static void gtk_menu_size_request (GtkWidget *widget,
55 GtkRequisition *requisition);
56 static void gtk_menu_size_allocate (GtkWidget *widget,
57 GtkAllocation *allocation);
58 static void gtk_menu_paint (GtkWidget *widget);
59 static void gtk_menu_draw (GtkWidget *widget,
61 static gint gtk_menu_expose (GtkWidget *widget,
62 GdkEventExpose *event);
63 static gint gtk_menu_key_press (GtkWidget *widget,
65 static gint gtk_menu_motion_notify (GtkWidget *widget,
66 GdkEventMotion *event);
67 static void gtk_menu_deactivate (GtkMenuShell *menu_shell);
68 static void gtk_menu_show_all (GtkWidget *widget);
69 static void gtk_menu_hide_all (GtkWidget *widget);
70 static void gtk_menu_position (GtkMenu *menu);
71 static void gtk_menu_reparent (GtkMenu *menu,
72 GtkWidget *new_parent,
75 static GtkMenuShellClass *parent_class = NULL;
76 static const gchar *attach_data_key = "gtk-menu-attach-data";
77 static GQuark quark_uline_accel_group = 0;
81 gtk_menu_get_type (void)
83 static GtkType menu_type = 0;
87 static const GtkTypeInfo menu_info =
91 sizeof (GtkMenuClass),
92 (GtkClassInitFunc) gtk_menu_class_init,
93 (GtkObjectInitFunc) gtk_menu_init,
94 /* reserved_1 */ NULL,
95 /* reserved_2 */ NULL,
96 (GtkClassInitFunc) NULL,
99 menu_type = gtk_type_unique (gtk_menu_shell_get_type (), &menu_info);
106 gtk_menu_class_init (GtkMenuClass *class)
108 GtkObjectClass *object_class;
109 GtkWidgetClass *widget_class;
110 GtkContainerClass *container_class;
111 GtkMenuShellClass *menu_shell_class;
113 GtkBindingSet *binding_set;
115 object_class = (GtkObjectClass*) class;
116 widget_class = (GtkWidgetClass*) class;
117 container_class = (GtkContainerClass*) class;
118 menu_shell_class = (GtkMenuShellClass*) class;
119 parent_class = gtk_type_class (gtk_menu_shell_get_type ());
121 object_class->destroy = gtk_menu_destroy;
123 widget_class->realize = gtk_menu_realize;
124 widget_class->draw = gtk_menu_draw;
125 widget_class->size_request = gtk_menu_size_request;
126 widget_class->size_allocate = gtk_menu_size_allocate;
127 widget_class->expose_event = gtk_menu_expose;
128 widget_class->key_press_event = gtk_menu_key_press;
129 widget_class->motion_notify_event = gtk_menu_motion_notify;
130 widget_class->show_all = gtk_menu_show_all;
131 widget_class->hide_all = gtk_menu_hide_all;
133 menu_shell_class->submenu_placement = GTK_LEFT_RIGHT;
134 menu_shell_class->deactivate = gtk_menu_deactivate;
136 binding_set = gtk_binding_set_by_class (class);
137 gtk_binding_entry_add_signal (binding_set,
140 GTK_TYPE_MENU_DIRECTION_TYPE,
142 gtk_binding_entry_add_signal (binding_set,
145 GTK_TYPE_MENU_DIRECTION_TYPE,
147 gtk_binding_entry_add_signal (binding_set,
150 GTK_TYPE_MENU_DIRECTION_TYPE,
151 GTK_MENU_DIR_PARENT);
152 gtk_binding_entry_add_signal (binding_set,
155 GTK_TYPE_MENU_DIRECTION_TYPE,
160 gtk_menu_window_event (GtkWidget *window,
164 gboolean handled = FALSE;
166 gtk_widget_ref (window);
167 gtk_widget_ref (menu);
172 case GDK_KEY_RELEASE:
173 gtk_widget_event (menu, event);
180 gtk_widget_unref (window);
181 gtk_widget_unref (menu);
187 gtk_menu_init (GtkMenu *menu)
189 menu->parent_menu_item = NULL;
190 menu->old_active_menu_item = NULL;
191 menu->accel_group = NULL;
192 menu->position_func = NULL;
193 menu->position_func_data = NULL;
195 menu->toplevel = gtk_widget_new (GTK_TYPE_WINDOW,
196 "type", GTK_WINDOW_POPUP,
197 "signal::event", gtk_menu_window_event, menu,
198 "signal::destroy", gtk_widget_destroyed, &menu->toplevel,
201 gtk_window_set_policy (GTK_WINDOW (menu->toplevel),
204 /* Refloat the menu, so that reference counting for the menu isn't
205 * affected by it being a child of the toplevel
207 GTK_WIDGET_SET_FLAGS (menu, GTK_FLOATING);
208 menu->needs_destruction_ref_count = TRUE;
210 menu->tearoff_window = NULL;
211 menu->torn_off = FALSE;
213 MENU_NEEDS_RESIZE (menu) = TRUE;
217 gtk_menu_destroy (GtkObject *object)
220 GtkMenuAttachData *data;
222 g_return_if_fail (GTK_IS_MENU (object));
224 menu = GTK_MENU (object);
226 data = gtk_object_get_data (object, attach_data_key);
228 gtk_menu_detach (menu);
230 gtk_menu_set_accel_group (menu, NULL);
232 if (menu->old_active_menu_item)
234 gtk_widget_unref (menu->old_active_menu_item);
235 menu->old_active_menu_item = NULL;
238 /* Add back the reference count for being a child */
239 if (menu->needs_destruction_ref_count)
241 menu->needs_destruction_ref_count = FALSE;
242 gtk_object_ref (object);
246 gtk_widget_destroy (menu->toplevel);
247 if (menu->tearoff_window)
248 gtk_widget_destroy (menu->tearoff_window);
250 GTK_OBJECT_CLASS (parent_class)->destroy (object);
255 gtk_menu_attach_to_widget (GtkMenu *menu,
256 GtkWidget *attach_widget,
257 GtkMenuDetachFunc detacher)
259 GtkMenuAttachData *data;
261 g_return_if_fail (menu != NULL);
262 g_return_if_fail (GTK_IS_MENU (menu));
263 g_return_if_fail (attach_widget != NULL);
264 g_return_if_fail (GTK_IS_WIDGET (attach_widget));
265 g_return_if_fail (detacher != NULL);
267 /* keep this function in sync with gtk_widget_set_parent()
270 data = gtk_object_get_data (GTK_OBJECT (menu), attach_data_key);
273 g_warning ("gtk_menu_attach_to_widget(): menu already attached to %s",
274 gtk_type_name (GTK_OBJECT_TYPE (data->attach_widget)));
278 gtk_object_ref (GTK_OBJECT (menu));
279 gtk_object_sink (GTK_OBJECT (menu));
281 data = g_new (GtkMenuAttachData, 1);
282 data->attach_widget = attach_widget;
283 data->detacher = detacher;
284 gtk_object_set_data (GTK_OBJECT (menu), attach_data_key, data);
286 if (GTK_WIDGET_STATE (menu) != GTK_STATE_NORMAL)
287 gtk_widget_set_state (GTK_WIDGET (menu), GTK_STATE_NORMAL);
289 /* we don't need to set the style here, since
290 * we are a toplevel widget.
295 gtk_menu_get_attach_widget (GtkMenu *menu)
297 GtkMenuAttachData *data;
299 g_return_val_if_fail (menu != NULL, NULL);
300 g_return_val_if_fail (GTK_IS_MENU (menu), NULL);
302 data = gtk_object_get_data (GTK_OBJECT (menu), attach_data_key);
304 return data->attach_widget;
309 gtk_menu_detach (GtkMenu *menu)
311 GtkMenuAttachData *data;
313 g_return_if_fail (menu != NULL);
314 g_return_if_fail (GTK_IS_MENU (menu));
316 /* keep this function in sync with gtk_widget_unparent()
318 data = gtk_object_get_data (GTK_OBJECT (menu), attach_data_key);
321 g_warning ("gtk_menu_detach(): menu is not attached");
324 gtk_object_remove_data (GTK_OBJECT (menu), attach_data_key);
326 data->detacher (data->attach_widget, menu);
328 if (GTK_WIDGET_REALIZED (menu))
329 gtk_widget_unrealize (GTK_WIDGET (menu));
333 gtk_widget_unref (GTK_WIDGET (menu));
339 return GTK_WIDGET (gtk_type_new (gtk_menu_get_type ()));
343 gtk_menu_append (GtkMenu *menu,
346 gtk_menu_shell_append (GTK_MENU_SHELL (menu), child);
350 gtk_menu_prepend (GtkMenu *menu,
353 gtk_menu_shell_prepend (GTK_MENU_SHELL (menu), child);
357 gtk_menu_insert (GtkMenu *menu,
361 gtk_menu_shell_insert (GTK_MENU_SHELL (menu), child, position);
365 gtk_menu_tearoff_bg_copy (GtkMenu *menu)
369 widget = GTK_WIDGET (menu);
375 GdkGCValues gc_values;
377 gc_values.subwindow_mode = GDK_INCLUDE_INFERIORS;
378 gc = gdk_gc_new_with_values (widget->window,
379 &gc_values, GDK_GC_SUBWINDOW);
381 pixmap = gdk_pixmap_new (widget->window,
382 widget->requisition.width,
383 widget->requisition.height,
386 gdk_draw_pixmap (pixmap, gc,
391 gtk_widget_set_usize (menu->tearoff_window,
392 widget->requisition.width,
393 widget->requisition.height);
395 gdk_window_set_back_pixmap (menu->tearoff_window->window, pixmap, FALSE);
396 gdk_pixmap_unref (pixmap);
401 gtk_menu_popup (GtkMenu *menu,
402 GtkWidget *parent_menu_shell,
403 GtkWidget *parent_menu_item,
404 GtkMenuPositionFunc func,
407 guint32 activate_time)
410 GtkWidget *xgrab_shell;
412 GdkEvent *current_event;
413 GtkMenuShell *menu_shell;
415 g_return_if_fail (menu != NULL);
416 g_return_if_fail (GTK_IS_MENU (menu));
418 widget = GTK_WIDGET (menu);
419 menu_shell = GTK_MENU_SHELL (menu);
421 menu_shell->parent_menu_shell = parent_menu_shell;
422 menu_shell->active = TRUE;
423 menu_shell->button = button;
425 /* If we are popping up the menu from something other than, a button
426 * press then, as a heuristic, we ignore enter events for the menu
427 * until we get a MOTION_NOTIFY.
430 current_event = gtk_get_current_event();
433 if ((current_event->type != GDK_BUTTON_PRESS) &&
434 (current_event->type != GDK_ENTER_NOTIFY))
435 menu_shell->ignore_enter = TRUE;
436 gdk_event_free (current_event);
441 gtk_menu_tearoff_bg_copy (menu);
443 /* We force an unrealize here so that we don't trigger redrawing/
444 * clearing code - we just want to reveal our backing pixmap.
446 gtk_menu_reparent (menu, menu->toplevel, TRUE);
449 menu->parent_menu_item = parent_menu_item;
450 menu->position_func = func;
451 menu->position_func_data = data;
452 menu_shell->activate_time = activate_time;
454 gtk_menu_position (menu);
456 /* We need to show the menu _here_ because code expects to be
457 * able to tell if the menu is onscreen by looking at the
458 * GTK_WIDGET_VISIBLE (menu)
460 gtk_widget_show (GTK_WIDGET (menu));
461 gtk_widget_show (menu->toplevel);
463 /* Find the last viewable ancestor, and make an X grab on it
465 parent = GTK_WIDGET (menu);
469 gboolean viewable = TRUE;
470 GtkWidget *tmp = parent;
474 if (!GTK_WIDGET_MAPPED (tmp))
483 xgrab_shell = parent;
485 parent = GTK_MENU_SHELL (parent)->parent_menu_shell;
488 if (xgrab_shell && (!GTK_MENU_SHELL (xgrab_shell)->have_xgrab))
490 GdkCursor *cursor = gdk_cursor_new (GDK_ARROW);
492 if ((gdk_pointer_grab (xgrab_shell->window, TRUE,
493 GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |
494 GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK |
495 GDK_POINTER_MOTION_MASK,
496 NULL, cursor, activate_time) == 0))
498 if (gdk_keyboard_grab (xgrab_shell->window, TRUE,
500 GTK_MENU_SHELL (xgrab_shell)->have_xgrab = TRUE;
503 gdk_pointer_ungrab (activate_time);
507 gdk_cursor_destroy (cursor);
510 gtk_grab_add (GTK_WIDGET (menu));
514 gtk_menu_popdown (GtkMenu *menu)
516 GtkMenuShell *menu_shell;
518 g_return_if_fail (menu != NULL);
519 g_return_if_fail (GTK_IS_MENU (menu));
521 menu_shell = GTK_MENU_SHELL (menu);
523 menu_shell->parent_menu_shell = NULL;
524 menu_shell->active = FALSE;
525 menu_shell->ignore_enter = FALSE;
527 if (menu_shell->active_menu_item)
529 if (menu->old_active_menu_item)
530 gtk_widget_unref (menu->old_active_menu_item);
531 menu->old_active_menu_item = menu_shell->active_menu_item;
532 gtk_widget_ref (menu->old_active_menu_item);
535 gtk_menu_shell_deselect (menu_shell);
537 /* The X Grab, if present, will automatically be removed when we hide
539 gtk_widget_hide (menu->toplevel);
543 if (GTK_BIN (menu->toplevel)->child)
545 gtk_menu_reparent (menu, menu->tearoff_window, FALSE);
549 /* We popped up the menu from the tearoff, so we need to
550 * release the grab - we aren't actually hiding the menu.
552 if (menu_shell->have_xgrab)
554 gdk_pointer_ungrab (GDK_CURRENT_TIME);
555 gdk_keyboard_ungrab (GDK_CURRENT_TIME);
560 gtk_widget_hide (GTK_WIDGET (menu));
562 menu_shell->have_xgrab = FALSE;
563 gtk_grab_remove (GTK_WIDGET (menu));
567 gtk_menu_get_active (GtkMenu *menu)
572 g_return_val_if_fail (menu != NULL, NULL);
573 g_return_val_if_fail (GTK_IS_MENU (menu), NULL);
575 if (!menu->old_active_menu_item)
578 children = GTK_MENU_SHELL (menu)->children;
582 child = children->data;
583 children = children->next;
585 if (GTK_BIN (child)->child)
590 menu->old_active_menu_item = child;
591 if (menu->old_active_menu_item)
592 gtk_widget_ref (menu->old_active_menu_item);
595 return menu->old_active_menu_item;
599 gtk_menu_set_active (GtkMenu *menu,
605 g_return_if_fail (menu != NULL);
606 g_return_if_fail (GTK_IS_MENU (menu));
608 tmp_list = g_list_nth (GTK_MENU_SHELL (menu)->children, index);
611 child = tmp_list->data;
612 if (GTK_BIN (child)->child)
614 if (menu->old_active_menu_item)
615 gtk_widget_unref (menu->old_active_menu_item);
616 menu->old_active_menu_item = child;
617 gtk_widget_ref (menu->old_active_menu_item);
623 gtk_menu_set_accel_group (GtkMenu *menu,
624 GtkAccelGroup *accel_group)
626 g_return_if_fail (GTK_IS_MENU (menu));
628 if (menu->accel_group != accel_group)
630 if (menu->accel_group)
631 gtk_accel_group_unref (menu->accel_group);
632 menu->accel_group = accel_group;
633 if (menu->accel_group)
634 gtk_accel_group_ref (menu->accel_group);
639 gtk_menu_get_accel_group (GtkMenu *menu)
641 g_return_val_if_fail (GTK_IS_MENU (menu), NULL);
643 return menu->accel_group;
647 gtk_menu_ensure_uline_accel_group (GtkMenu *menu)
649 GtkAccelGroup *accel_group;
651 g_return_val_if_fail (GTK_IS_MENU (menu), NULL);
653 if (!quark_uline_accel_group)
654 quark_uline_accel_group = g_quark_from_static_string ("GtkMenu-uline-accel-group");
656 accel_group = gtk_object_get_data_by_id (GTK_OBJECT (menu), quark_uline_accel_group);
659 accel_group = gtk_accel_group_new ();
660 gtk_accel_group_attach (accel_group, GTK_OBJECT (menu));
661 gtk_object_set_data_by_id_full (GTK_OBJECT (menu),
662 quark_uline_accel_group,
664 (GtkDestroyNotify) gtk_accel_group_unref);
671 gtk_menu_get_uline_accel_group (GtkMenu *menu)
673 g_return_val_if_fail (GTK_IS_MENU (menu), NULL);
675 return gtk_object_get_data_by_id (GTK_OBJECT (menu), quark_uline_accel_group);
679 gtk_menu_reposition (GtkMenu *menu)
681 g_return_if_fail (menu != NULL);
682 g_return_if_fail (GTK_IS_MENU (menu));
684 if (GTK_WIDGET_DRAWABLE (menu) && !menu->torn_off)
685 gtk_menu_position (menu);
690 gtk_menu_set_tearoff_state (GtkMenu *menu,
693 g_return_if_fail (menu != NULL);
694 g_return_if_fail (GTK_IS_MENU (menu));
696 if (menu->torn_off != torn_off)
698 menu->torn_off = torn_off;
702 if (GTK_WIDGET_VISIBLE (menu))
703 gtk_menu_popdown (menu);
705 if (!menu->tearoff_window)
707 GtkWidget *attach_widget;
710 menu->tearoff_window = gtk_widget_new (GTK_TYPE_WINDOW,
711 "type", GTK_WINDOW_TOPLEVEL,
712 "signal::destroy", gtk_widget_destroyed, &menu->tearoff_window,
714 gtk_widget_set_app_paintable (menu->tearoff_window, TRUE);
715 gtk_signal_connect (GTK_OBJECT (menu->tearoff_window),
717 GTK_SIGNAL_FUNC (gtk_menu_window_event),
719 gtk_widget_realize (menu->tearoff_window);
721 title = gtk_object_get_data (GTK_OBJECT (menu), "gtk-menu-title");
724 attach_widget = gtk_menu_get_attach_widget (menu);
725 if (GTK_IS_MENU_ITEM (attach_widget))
727 GtkWidget *child = GTK_BIN (attach_widget)->child;
728 if (GTK_IS_LABEL (child))
729 gtk_label_get (GTK_LABEL (child), &title);
734 gdk_window_set_title (menu->tearoff_window->window, title);
736 gdk_window_set_decorations (menu->tearoff_window->window,
741 gtk_window_set_policy (GTK_WINDOW (menu->tearoff_window),
744 gtk_menu_reparent (menu, menu->tearoff_window, FALSE);
746 gtk_menu_position (menu);
748 gtk_widget_show (GTK_WIDGET (menu));
749 gtk_widget_show (menu->tearoff_window);
753 gtk_widget_hide (menu->tearoff_window);
754 gtk_menu_reparent (menu, menu->toplevel, FALSE);
760 gtk_menu_set_title (GtkMenu *menu,
763 g_return_if_fail (menu != NULL);
764 g_return_if_fail (GTK_IS_MENU (menu));
766 gtk_object_set_data_full (GTK_OBJECT (menu), "gtk-menu-title",
767 g_strdup (title), (GtkDestroyNotify) g_free);
771 gtk_menu_reorder_child (GtkMenu *menu,
775 GtkMenuShell *menu_shell;
776 g_return_if_fail (GTK_IS_MENU (menu));
777 g_return_if_fail (GTK_IS_MENU_ITEM (child));
778 menu_shell = GTK_MENU_SHELL (menu);
779 if (g_list_find (menu_shell->children, child))
781 menu_shell->children = g_list_remove (menu_shell->children, child);
782 menu_shell->children = g_list_insert (menu_shell->children, child, position);
783 if (GTK_WIDGET_VISIBLE (menu_shell))
784 gtk_widget_queue_resize (GTK_WIDGET (menu_shell));
789 gtk_menu_realize (GtkWidget *widget)
791 GdkWindowAttr attributes;
792 gint attributes_mask;
794 g_return_if_fail (widget != NULL);
795 g_return_if_fail (GTK_IS_MENU (widget));
797 GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
799 attributes.window_type = GDK_WINDOW_CHILD;
800 attributes.x = widget->allocation.x;
801 attributes.y = widget->allocation.y;
802 attributes.width = widget->allocation.width;
803 attributes.height = widget->allocation.height;
804 attributes.wclass = GDK_INPUT_OUTPUT;
805 attributes.visual = gtk_widget_get_visual (widget);
806 attributes.colormap = gtk_widget_get_colormap (widget);
807 attributes.event_mask = gtk_widget_get_events (widget);
808 attributes.event_mask |= (GDK_EXPOSURE_MASK | GDK_KEY_PRESS_MASK);
810 attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
811 widget->window = gdk_window_new (gtk_widget_get_parent_window (widget), &attributes, attributes_mask);
812 gdk_window_set_user_data (widget->window, widget);
814 widget->style = gtk_style_attach (widget->style, widget->window);
815 gtk_style_set_background (widget->style, widget->window, GTK_STATE_NORMAL);
816 gtk_menu_paint(widget);
820 gtk_menu_size_request (GtkWidget *widget,
821 GtkRequisition *requisition)
824 GtkMenuShell *menu_shell;
827 guint max_toggle_size;
828 guint max_accel_width;
829 GtkRequisition child_requisition;
831 g_return_if_fail (widget != NULL);
832 g_return_if_fail (GTK_IS_MENU (widget));
833 g_return_if_fail (requisition != NULL);
835 menu = GTK_MENU (widget);
836 menu_shell = GTK_MENU_SHELL (widget);
838 requisition->width = 0;
839 requisition->height = 0;
844 children = menu_shell->children;
847 child = children->data;
848 children = children->next;
850 if (GTK_WIDGET_VISIBLE (child))
852 GTK_MENU_ITEM (child)->show_submenu_indicator = TRUE;
853 gtk_widget_size_request (child, &child_requisition);
855 requisition->width = MAX (requisition->width, child_requisition.width);
856 requisition->height += child_requisition.height;
858 max_toggle_size = MAX (max_toggle_size, MENU_ITEM_CLASS (child)->toggle_size);
859 max_accel_width = MAX (max_accel_width, GTK_MENU_ITEM (child)->accelerator_width);
863 requisition->width += max_toggle_size + max_accel_width;
864 requisition->width += (GTK_CONTAINER (menu)->border_width +
865 widget->style->xthickness) * 2;
866 requisition->height += (GTK_CONTAINER (menu)->border_width +
867 widget->style->ythickness) * 2;
869 children = menu_shell->children;
872 child = children->data;
873 children = children->next;
875 GTK_MENU_ITEM (child)->toggle_size = max_toggle_size;
880 gtk_menu_size_allocate (GtkWidget *widget,
881 GtkAllocation *allocation)
884 GtkMenuShell *menu_shell;
886 GtkAllocation child_allocation;
889 g_return_if_fail (widget != NULL);
890 g_return_if_fail (GTK_IS_MENU (widget));
891 g_return_if_fail (allocation != NULL);
893 menu = GTK_MENU (widget);
894 menu_shell = GTK_MENU_SHELL (widget);
896 widget->allocation = *allocation;
897 if (GTK_WIDGET_REALIZED (widget))
898 gdk_window_move_resize (widget->window,
899 allocation->x, allocation->y,
900 allocation->width, allocation->height);
903 if (menu_shell->children)
905 child_allocation.x = (GTK_CONTAINER (menu)->border_width +
906 widget->style->xthickness);
907 child_allocation.y = (GTK_CONTAINER (menu)->border_width +
908 widget->style->ythickness);
909 child_allocation.width = MAX (1, (gint)allocation->width - child_allocation.x * 2);
911 children = menu_shell->children;
914 child = children->data;
915 children = children->next;
917 if (GTK_WIDGET_VISIBLE (child))
919 GtkRequisition child_requisition;
920 gtk_widget_get_child_requisition (child, &child_requisition);
922 child_allocation.height = child_requisition.height;
924 gtk_widget_size_allocate (child, &child_allocation);
925 gtk_widget_queue_draw (child);
927 child_allocation.y += child_allocation.height;
934 gtk_menu_paint (GtkWidget *widget)
936 g_return_if_fail (widget != NULL);
937 g_return_if_fail (GTK_IS_MENU (widget));
939 if (GTK_WIDGET_DRAWABLE (widget))
941 gtk_paint_box (widget->style,
945 NULL, widget, "menu",
951 gtk_menu_draw (GtkWidget *widget,
954 GtkMenuShell *menu_shell;
956 GdkRectangle child_area;
959 g_return_if_fail (widget != NULL);
960 g_return_if_fail (GTK_IS_MENU (widget));
961 g_return_if_fail (area != NULL);
963 if (GTK_WIDGET_DRAWABLE (widget))
965 gtk_menu_paint (widget);
967 menu_shell = GTK_MENU_SHELL (widget);
969 children = menu_shell->children;
972 child = children->data;
973 children = children->next;
975 if (gtk_widget_intersect (child, area, &child_area))
976 gtk_widget_draw (child, &child_area);
982 gtk_menu_expose (GtkWidget *widget,
983 GdkEventExpose *event)
985 GtkMenuShell *menu_shell;
987 GdkEventExpose child_event;
991 g_return_val_if_fail (widget != NULL, FALSE);
992 g_return_val_if_fail (GTK_IS_MENU (widget), FALSE);
993 g_return_val_if_fail (event != NULL, FALSE);
995 menu_shell = GTK_MENU_SHELL (widget);
996 menu = GTK_MENU (widget);
998 if (GTK_WIDGET_DRAWABLE (widget))
1000 gtk_menu_paint (widget);
1002 child_event = *event;
1004 children = menu_shell->children;
1007 child = children->data;
1008 children = children->next;
1010 if (GTK_WIDGET_NO_WINDOW (child) &&
1011 gtk_widget_intersect (child, &event->area, &child_event.area))
1012 gtk_widget_event (child, (GdkEvent*) &child_event);
1020 gtk_menu_key_press (GtkWidget *widget,
1023 GtkMenuShell *menu_shell;
1024 gboolean delete = FALSE;
1026 g_return_val_if_fail (widget != NULL, FALSE);
1027 g_return_val_if_fail (GTK_IS_MENU (widget), FALSE);
1028 g_return_val_if_fail (event != NULL, FALSE);
1030 menu_shell = GTK_MENU_SHELL (widget);
1032 if (GTK_WIDGET_CLASS (parent_class)->key_press_event (widget, event))
1035 switch (event->keyval)
1046 /* Modify the accelerators */
1047 if (menu_shell->active_menu_item &&
1048 GTK_BIN (menu_shell->active_menu_item)->child &&
1049 GTK_MENU_ITEM (menu_shell->active_menu_item)->submenu == NULL &&
1050 !gtk_widget_accelerators_locked (menu_shell->active_menu_item) &&
1052 (gtk_accelerator_valid (event->keyval, event->state) &&
1054 !gtk_menu_get_uline_accel_group (GTK_MENU (menu_shell)) ||
1055 (event->keyval >= GDK_F1 && event->keyval <= GDK_F35)))))
1057 GtkMenuItem *menu_item;
1058 GtkAccelGroup *accel_group;
1060 menu_item = GTK_MENU_ITEM (menu_shell->active_menu_item);
1062 if (!GTK_MENU (widget)->accel_group)
1063 accel_group = gtk_accel_group_get_default ();
1065 accel_group = GTK_MENU (widget)->accel_group;
1067 gtk_widget_remove_accelerators (GTK_WIDGET (menu_item),
1068 gtk_signal_name (menu_item->accelerator_signal),
1072 0 == gtk_widget_accelerator_signal (GTK_WIDGET (menu_item),
1079 slist = gtk_accel_group_entries_from_object (GTK_OBJECT (menu_item));
1082 GtkAccelEntry *ac_entry;
1084 ac_entry = slist->data;
1086 if (ac_entry->signal_id == menu_item->accelerator_signal)
1089 slist = slist->next;
1093 gtk_widget_add_accelerator (GTK_WIDGET (menu_item),
1094 gtk_signal_name (menu_item->accelerator_signal),
1106 gtk_menu_motion_notify (GtkWidget *widget,
1107 GdkEventMotion *event)
1109 g_return_val_if_fail (widget != NULL, FALSE);
1110 g_return_val_if_fail (GTK_IS_MENU (widget), FALSE);
1112 if (GTK_MENU_SHELL (widget)->ignore_enter)
1113 GTK_MENU_SHELL (widget)->ignore_enter = FALSE;
1118 gdk_window_get_size (event->window, &width, &height);
1119 if (event->x >= 0 && event->x < width &&
1120 event->y >= 0 && event->y < height)
1122 GdkEvent send_event;
1124 send_event.crossing.type = GDK_ENTER_NOTIFY;
1125 send_event.crossing.window = event->window;
1126 send_event.crossing.time = event->time;
1127 send_event.crossing.send_event = TRUE;
1129 gtk_widget_event (widget, &send_event);
1137 gtk_menu_deactivate (GtkMenuShell *menu_shell)
1141 g_return_if_fail (menu_shell != NULL);
1142 g_return_if_fail (GTK_IS_MENU (menu_shell));
1144 parent = menu_shell->parent_menu_shell;
1146 menu_shell->activate_time = 0;
1147 gtk_menu_popdown (GTK_MENU (menu_shell));
1150 gtk_menu_shell_deactivate (GTK_MENU_SHELL (parent));
1155 gtk_menu_position (GtkMenu *menu)
1158 GtkRequisition requisition;
1161 g_return_if_fail (menu != NULL);
1162 g_return_if_fail (GTK_IS_MENU (menu));
1164 widget = GTK_WIDGET (menu);
1166 gdk_window_get_pointer (NULL, &x, &y, NULL);
1168 /* We need the requisition to figure out the right place to
1169 * popup the menu. In fact, we always need to ask here, since
1170 * if a size_request was queued while we weren't popped up,
1171 * the requisition won't have been recomputed yet.
1173 gtk_widget_size_request (widget, &requisition);
1175 if (menu->position_func)
1176 (* menu->position_func) (menu, &x, &y, menu->position_func_data);
1182 screen_width = gdk_screen_width ();
1183 screen_height = gdk_screen_height ();
1185 x = CLAMP (x - 2, 0, MAX (0, screen_width - requisition.width));
1186 y = CLAMP (y - 2, 0, MAX (0, screen_height - requisition.height));
1189 /* FIXME: The MAX() here is because gtk_widget_set_uposition
1190 * is broken. Once we provide an alternate interface that
1191 * allows negative values, then we can remove them.
1193 gtk_widget_set_uposition (GTK_MENU_SHELL (menu)->active ?
1194 menu->toplevel : menu->tearoff_window,
1195 MAX (x, 0), MAX (y, 0));
1198 /* Reparent the menu, taking care of the refcounting
1201 gtk_menu_reparent (GtkMenu *menu,
1202 GtkWidget *new_parent,
1205 GtkObject *object = GTK_OBJECT (menu);
1206 GtkWidget *widget = GTK_WIDGET (menu);
1207 gboolean was_floating = GTK_OBJECT_FLOATING (object);
1209 gtk_object_ref (object);
1210 gtk_object_sink (object);
1214 gtk_object_ref (object);
1215 gtk_container_remove (GTK_CONTAINER (widget->parent), widget);
1216 gtk_container_add (GTK_CONTAINER (new_parent), widget);
1217 gtk_object_unref (object);
1220 gtk_widget_reparent (GTK_WIDGET (menu), new_parent);
1221 gtk_widget_set_usize (new_parent, -1, -1);
1224 GTK_OBJECT_SET_FLAGS (object, GTK_FLOATING);
1226 gtk_object_unref (object);
1230 gtk_menu_show_all (GtkWidget *widget)
1232 g_return_if_fail (widget != NULL);
1233 g_return_if_fail (GTK_IS_MENU (widget));
1235 /* Show children, but not self. */
1236 gtk_container_foreach (GTK_CONTAINER (widget), (GtkCallback) gtk_widget_show_all, NULL);
1241 gtk_menu_hide_all (GtkWidget *widget)
1243 g_return_if_fail (widget != NULL);
1244 g_return_if_fail (GTK_IS_MENU (widget));
1246 /* Hide children, but not self. */
1247 gtk_container_foreach (GTK_CONTAINER (widget), (GtkCallback) gtk_widget_hide_all, NULL);