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 Library 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 * Library General Public License for more details.
14 * You should have received a copy of the GNU Library 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-1999. 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_CLASS (GTK_OBJECT (w)->klass)
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_window_new (GTK_WINDOW_POPUP);
196 gtk_signal_connect (GTK_OBJECT (menu->toplevel),
198 GTK_SIGNAL_FUNC (gtk_menu_window_event),
200 gtk_window_set_policy (GTK_WINDOW (menu->toplevel),
203 gtk_container_add (GTK_CONTAINER (menu->toplevel), GTK_WIDGET (menu));
205 /* Refloat the menu, so that reference counting for the menu isn't
206 * affected by it being a child of the toplevel
208 GTK_WIDGET_SET_FLAGS (menu, GTK_FLOATING);
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 (object != NULL);
223 g_return_if_fail (GTK_IS_MENU (object));
225 menu = GTK_MENU (object);
227 gtk_object_ref (object);
229 data = gtk_object_get_data (object, attach_data_key);
231 gtk_menu_detach (menu);
233 gtk_menu_set_accel_group (menu, NULL);
235 if (menu->old_active_menu_item)
237 gtk_widget_unref (menu->old_active_menu_item);
238 menu->old_active_menu_item = NULL;
241 /* Add back the reference count for being a child */
242 gtk_object_ref (object);
244 gtk_widget_destroy (menu->toplevel);
245 if (menu->tearoff_window)
246 gtk_widget_destroy (menu->tearoff_window);
248 if (GTK_OBJECT_CLASS (parent_class)->destroy)
249 (* GTK_OBJECT_CLASS (parent_class)->destroy) (object);
251 gtk_object_unref (object);
256 gtk_menu_attach_to_widget (GtkMenu *menu,
257 GtkWidget *attach_widget,
258 GtkMenuDetachFunc detacher)
260 GtkMenuAttachData *data;
262 g_return_if_fail (menu != NULL);
263 g_return_if_fail (GTK_IS_MENU (menu));
264 g_return_if_fail (attach_widget != NULL);
265 g_return_if_fail (GTK_IS_WIDGET (attach_widget));
266 g_return_if_fail (detacher != NULL);
268 /* keep this function in sync with gtk_widget_set_parent()
271 data = gtk_object_get_data (GTK_OBJECT (menu), attach_data_key);
274 g_warning ("gtk_menu_attach_to_widget(): menu already attached to %s",
275 gtk_type_name (GTK_OBJECT_TYPE (data->attach_widget)));
279 gtk_object_ref (GTK_OBJECT (menu));
280 gtk_object_sink (GTK_OBJECT (menu));
282 data = g_new (GtkMenuAttachData, 1);
283 data->attach_widget = attach_widget;
284 data->detacher = detacher;
285 gtk_object_set_data (GTK_OBJECT (menu), attach_data_key, data);
287 if (GTK_WIDGET_STATE (menu) != GTK_STATE_NORMAL)
288 gtk_widget_set_state (GTK_WIDGET (menu), GTK_STATE_NORMAL);
290 /* we don't need to set the style here, since
291 * we are a toplevel widget.
296 gtk_menu_get_attach_widget (GtkMenu *menu)
298 GtkMenuAttachData *data;
300 g_return_val_if_fail (menu != NULL, NULL);
301 g_return_val_if_fail (GTK_IS_MENU (menu), NULL);
303 data = gtk_object_get_data (GTK_OBJECT (menu), attach_data_key);
305 return data->attach_widget;
310 gtk_menu_detach (GtkMenu *menu)
312 GtkMenuAttachData *data;
314 g_return_if_fail (menu != NULL);
315 g_return_if_fail (GTK_IS_MENU (menu));
317 /* keep this function in sync with gtk_widget_unparent()
319 data = gtk_object_get_data (GTK_OBJECT (menu), attach_data_key);
322 g_warning ("gtk_menu_detach(): menu is not attached");
325 gtk_object_remove_data (GTK_OBJECT (menu), attach_data_key);
327 data->detacher (data->attach_widget, menu);
329 if (GTK_WIDGET_REALIZED (menu))
330 gtk_widget_unrealize (GTK_WIDGET (menu));
334 gtk_widget_unref (GTK_WIDGET (menu));
340 return GTK_WIDGET (gtk_type_new (gtk_menu_get_type ()));
344 gtk_menu_append (GtkMenu *menu,
347 gtk_menu_shell_append (GTK_MENU_SHELL (menu), child);
351 gtk_menu_prepend (GtkMenu *menu,
354 gtk_menu_shell_prepend (GTK_MENU_SHELL (menu), child);
358 gtk_menu_insert (GtkMenu *menu,
362 gtk_menu_shell_insert (GTK_MENU_SHELL (menu), child, position);
366 gtk_menu_tearoff_bg_copy (GtkMenu *menu)
370 widget = GTK_WIDGET (menu);
376 GdkGCValues gc_values;
378 gc_values.subwindow_mode = GDK_INCLUDE_INFERIORS;
379 gc = gdk_gc_new_with_values (widget->window,
380 &gc_values, GDK_GC_SUBWINDOW);
382 pixmap = gdk_pixmap_new (widget->window,
383 widget->requisition.width,
384 widget->requisition.height,
387 gdk_draw_pixmap (pixmap, gc,
392 gtk_widget_set_usize (menu->tearoff_window,
393 widget->requisition.width,
394 widget->requisition.height);
396 gdk_window_set_back_pixmap (menu->tearoff_window->window, pixmap, FALSE);
397 gdk_pixmap_unref (pixmap);
402 gtk_menu_popup (GtkMenu *menu,
403 GtkWidget *parent_menu_shell,
404 GtkWidget *parent_menu_item,
405 GtkMenuPositionFunc func,
408 guint32 activate_time)
411 GtkWidget *xgrab_shell;
413 GdkEvent *current_event;
414 GtkMenuShell *menu_shell;
416 g_return_if_fail (menu != NULL);
417 g_return_if_fail (GTK_IS_MENU (menu));
419 widget = GTK_WIDGET (menu);
420 menu_shell = GTK_MENU_SHELL (menu);
422 menu_shell->parent_menu_shell = parent_menu_shell;
423 menu_shell->active = TRUE;
424 menu_shell->button = button;
426 /* If we are popping up the menu from something other than, a button
427 * press then, as a heuristic, we ignore enter events for the menu
428 * until we get a MOTION_NOTIFY.
431 current_event = gtk_get_current_event();
434 if ((current_event->type != GDK_BUTTON_PRESS) &&
435 (current_event->type != GDK_ENTER_NOTIFY))
436 menu_shell->ignore_enter = TRUE;
437 gdk_event_free (current_event);
442 gtk_menu_tearoff_bg_copy (menu);
444 /* We force an unrealize here so that we don't trigger redrawing/
445 * clearing code - we just want to reveal our backing pixmap.
447 gtk_menu_reparent (menu, menu->toplevel, TRUE);
450 menu->parent_menu_item = parent_menu_item;
451 menu->position_func = func;
452 menu->position_func_data = data;
453 menu_shell->activate_time = activate_time;
455 gtk_menu_position (menu);
457 /* We need to show the menu _here_ because code expects to be
458 * able to tell if the menu is onscreen by looking at the
459 * GTK_WIDGET_VISIBLE (menu)
461 gtk_widget_show (GTK_WIDGET (menu));
462 gtk_widget_show (menu->toplevel);
464 /* Find the last viewable ancestor, and make an X grab on it
466 parent = GTK_WIDGET (menu);
470 gboolean viewable = TRUE;
471 GtkWidget *tmp = parent;
475 if (!GTK_WIDGET_MAPPED (tmp))
484 xgrab_shell = parent;
486 parent = GTK_MENU_SHELL (parent)->parent_menu_shell;
489 if (xgrab_shell && (!GTK_MENU_SHELL (xgrab_shell)->have_xgrab))
491 GdkCursor *cursor = gdk_cursor_new (GDK_ARROW);
493 if ((gdk_pointer_grab (xgrab_shell->window, TRUE,
494 GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |
495 GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK |
496 GDK_POINTER_MOTION_MASK,
497 NULL, cursor, activate_time) == 0))
499 if (gdk_keyboard_grab (xgrab_shell->window, TRUE,
501 GTK_MENU_SHELL (xgrab_shell)->have_xgrab = TRUE;
504 gdk_pointer_ungrab (activate_time);
508 gdk_cursor_destroy (cursor);
511 gtk_grab_add (GTK_WIDGET (menu));
515 gtk_menu_popdown (GtkMenu *menu)
517 GtkMenuShell *menu_shell;
519 g_return_if_fail (menu != NULL);
520 g_return_if_fail (GTK_IS_MENU (menu));
522 menu_shell = GTK_MENU_SHELL (menu);
524 menu_shell->parent_menu_shell = NULL;
525 menu_shell->active = FALSE;
526 menu_shell->ignore_enter = FALSE;
528 if (menu_shell->active_menu_item)
530 if (menu->old_active_menu_item)
531 gtk_widget_unref (menu->old_active_menu_item);
532 menu->old_active_menu_item = menu_shell->active_menu_item;
533 gtk_widget_ref (menu->old_active_menu_item);
536 gtk_menu_shell_deselect (menu_shell);
538 /* The X Grab, if present, will automatically be removed when we hide
540 gtk_widget_hide (menu->toplevel);
544 if (GTK_BIN (menu->toplevel)->child)
546 gtk_menu_reparent (menu, menu->tearoff_window, FALSE);
550 /* We popped up the menu from the tearoff, so we need to
551 * release the grab - we aren't actually hiding the menu.
553 if (menu_shell->have_xgrab)
555 gdk_pointer_ungrab (GDK_CURRENT_TIME);
556 gdk_keyboard_ungrab (GDK_CURRENT_TIME);
561 gtk_widget_hide (GTK_WIDGET (menu));
563 menu_shell->have_xgrab = FALSE;
564 gtk_grab_remove (GTK_WIDGET (menu));
568 gtk_menu_get_active (GtkMenu *menu)
573 g_return_val_if_fail (menu != NULL, NULL);
574 g_return_val_if_fail (GTK_IS_MENU (menu), NULL);
576 if (!menu->old_active_menu_item)
579 children = GTK_MENU_SHELL (menu)->children;
583 child = children->data;
584 children = children->next;
586 if (GTK_BIN (child)->child)
591 menu->old_active_menu_item = child;
592 if (menu->old_active_menu_item)
593 gtk_widget_ref (menu->old_active_menu_item);
596 return menu->old_active_menu_item;
600 gtk_menu_set_active (GtkMenu *menu,
606 g_return_if_fail (menu != NULL);
607 g_return_if_fail (GTK_IS_MENU (menu));
609 tmp_list = g_list_nth (GTK_MENU_SHELL (menu)->children, index);
612 child = tmp_list->data;
613 if (GTK_BIN (child)->child)
615 if (menu->old_active_menu_item)
616 gtk_widget_unref (menu->old_active_menu_item);
617 menu->old_active_menu_item = child;
618 gtk_widget_ref (menu->old_active_menu_item);
624 gtk_menu_set_accel_group (GtkMenu *menu,
625 GtkAccelGroup *accel_group)
627 g_return_if_fail (GTK_IS_MENU (menu));
629 if (menu->accel_group != accel_group)
631 if (menu->accel_group)
632 gtk_accel_group_unref (menu->accel_group);
633 menu->accel_group = accel_group;
634 if (menu->accel_group)
635 gtk_accel_group_ref (menu->accel_group);
640 gtk_menu_get_accel_group (GtkMenu *menu)
642 g_return_val_if_fail (GTK_IS_MENU (menu), NULL);
644 return menu->accel_group;
648 gtk_menu_ensure_uline_accel_group (GtkMenu *menu)
650 GtkAccelGroup *accel_group;
652 g_return_val_if_fail (GTK_IS_MENU (menu), NULL);
654 if (!quark_uline_accel_group)
655 quark_uline_accel_group = g_quark_from_static_string ("GtkMenu-uline-accel-group");
657 accel_group = gtk_object_get_data_by_id (GTK_OBJECT (menu), quark_uline_accel_group);
660 accel_group = gtk_accel_group_new ();
661 gtk_accel_group_attach (accel_group, GTK_OBJECT (menu));
662 gtk_object_set_data_by_id_full (GTK_OBJECT (menu),
663 quark_uline_accel_group,
665 (GtkDestroyNotify) gtk_accel_group_unref);
672 gtk_menu_get_uline_accel_group (GtkMenu *menu)
674 g_return_val_if_fail (GTK_IS_MENU (menu), NULL);
676 return gtk_object_get_data_by_id (GTK_OBJECT (menu), quark_uline_accel_group);
680 gtk_menu_reposition (GtkMenu *menu)
682 g_return_if_fail (menu != NULL);
683 g_return_if_fail (GTK_IS_MENU (menu));
685 if (GTK_WIDGET_DRAWABLE (menu) && !menu->torn_off)
686 gtk_menu_position (menu);
691 gtk_menu_set_tearoff_state (GtkMenu *menu,
694 g_return_if_fail (menu != NULL);
695 g_return_if_fail (GTK_IS_MENU (menu));
697 if (menu->torn_off != torn_off)
699 menu->torn_off = torn_off;
703 if (GTK_WIDGET_VISIBLE (menu))
704 gtk_menu_popdown (menu);
706 if (!menu->tearoff_window)
708 GtkWidget *attach_widget;
711 menu->tearoff_window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
712 gtk_widget_set_app_paintable (menu->tearoff_window, TRUE);
713 gtk_signal_connect (GTK_OBJECT (menu->tearoff_window),
715 GTK_SIGNAL_FUNC (gtk_menu_window_event),
717 gtk_widget_realize (menu->tearoff_window);
719 title = gtk_object_get_data (GTK_OBJECT (menu), "gtk-menu-title");
722 attach_widget = gtk_menu_get_attach_widget (menu);
723 if (GTK_IS_MENU_ITEM (attach_widget))
725 GtkWidget *child = GTK_BIN (attach_widget)->child;
726 if (GTK_IS_LABEL (child))
727 gtk_label_get (GTK_LABEL (child), &title);
732 gdk_window_set_title (menu->tearoff_window->window, title);
734 gdk_window_set_decorations (menu->tearoff_window->window,
739 gtk_window_set_policy (GTK_WINDOW (menu->tearoff_window),
742 gtk_menu_reparent (menu, menu->tearoff_window, FALSE);
744 gtk_menu_position (menu);
746 gtk_widget_show (GTK_WIDGET (menu));
747 gtk_widget_show (menu->tearoff_window);
751 gtk_widget_hide (menu->tearoff_window);
752 gtk_menu_reparent (menu, menu->toplevel, FALSE);
758 gtk_menu_set_title (GtkMenu *menu,
761 g_return_if_fail (menu != NULL);
762 g_return_if_fail (GTK_IS_MENU (menu));
764 gtk_object_set_data_full (GTK_OBJECT (menu), "gtk-menu-title",
765 g_strdup (title), (GtkDestroyNotify) g_free);
769 gtk_menu_reorder_child (GtkMenu *menu,
773 GtkMenuShell *menu_shell;
774 g_return_if_fail (GTK_IS_MENU (menu));
775 g_return_if_fail (GTK_IS_MENU_ITEM (child));
776 menu_shell = GTK_MENU_SHELL (menu);
777 if (g_list_find (menu_shell->children, child))
779 menu_shell->children = g_list_remove (menu_shell->children, child);
780 menu_shell->children = g_list_insert (menu_shell->children, child, position);
781 if (GTK_WIDGET_VISIBLE (menu_shell))
782 gtk_widget_queue_resize (GTK_WIDGET (menu_shell));
787 gtk_menu_realize (GtkWidget *widget)
789 GdkWindowAttr attributes;
790 gint attributes_mask;
792 g_return_if_fail (widget != NULL);
793 g_return_if_fail (GTK_IS_MENU (widget));
795 GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
797 attributes.window_type = GDK_WINDOW_CHILD;
798 attributes.x = widget->allocation.x;
799 attributes.y = widget->allocation.y;
800 attributes.width = widget->allocation.width;
801 attributes.height = widget->allocation.height;
802 attributes.wclass = GDK_INPUT_OUTPUT;
803 attributes.visual = gtk_widget_get_visual (widget);
804 attributes.colormap = gtk_widget_get_colormap (widget);
805 attributes.event_mask = gtk_widget_get_events (widget);
806 attributes.event_mask |= (GDK_EXPOSURE_MASK | GDK_KEY_PRESS_MASK);
808 attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
809 widget->window = gdk_window_new (gtk_widget_get_parent_window (widget), &attributes, attributes_mask);
810 gdk_window_set_user_data (widget->window, widget);
812 widget->style = gtk_style_attach (widget->style, widget->window);
813 gtk_style_set_background (widget->style, widget->window, GTK_STATE_NORMAL);
814 gtk_menu_paint(widget);
818 gtk_menu_size_request (GtkWidget *widget,
819 GtkRequisition *requisition)
822 GtkMenuShell *menu_shell;
825 guint max_toggle_size;
826 guint max_accel_width;
827 GtkRequisition child_requisition;
829 g_return_if_fail (widget != NULL);
830 g_return_if_fail (GTK_IS_MENU (widget));
831 g_return_if_fail (requisition != NULL);
833 menu = GTK_MENU (widget);
834 menu_shell = GTK_MENU_SHELL (widget);
836 requisition->width = 0;
837 requisition->height = 0;
842 children = menu_shell->children;
845 child = children->data;
846 children = children->next;
848 if (GTK_WIDGET_VISIBLE (child))
850 GTK_MENU_ITEM (child)->show_submenu_indicator = TRUE;
851 gtk_widget_size_request (child, &child_requisition);
853 requisition->width = MAX (requisition->width, child_requisition.width);
854 requisition->height += child_requisition.height;
856 max_toggle_size = MAX (max_toggle_size, MENU_ITEM_CLASS (child)->toggle_size);
857 max_accel_width = MAX (max_accel_width, GTK_MENU_ITEM (child)->accelerator_width);
861 requisition->width += max_toggle_size + max_accel_width;
862 requisition->width += (GTK_CONTAINER (menu)->border_width +
863 widget->style->klass->xthickness) * 2;
864 requisition->height += (GTK_CONTAINER (menu)->border_width +
865 widget->style->klass->ythickness) * 2;
867 children = menu_shell->children;
870 child = children->data;
871 children = children->next;
873 GTK_MENU_ITEM (child)->toggle_size = max_toggle_size;
878 gtk_menu_size_allocate (GtkWidget *widget,
879 GtkAllocation *allocation)
882 GtkMenuShell *menu_shell;
884 GtkAllocation child_allocation;
887 g_return_if_fail (widget != NULL);
888 g_return_if_fail (GTK_IS_MENU (widget));
889 g_return_if_fail (allocation != NULL);
891 menu = GTK_MENU (widget);
892 menu_shell = GTK_MENU_SHELL (widget);
894 widget->allocation = *allocation;
895 if (GTK_WIDGET_REALIZED (widget))
896 gdk_window_move_resize (widget->window,
897 allocation->x, allocation->y,
898 allocation->width, allocation->height);
901 if (menu_shell->children)
903 child_allocation.x = (GTK_CONTAINER (menu)->border_width +
904 widget->style->klass->xthickness);
905 child_allocation.y = (GTK_CONTAINER (menu)->border_width +
906 widget->style->klass->ythickness);
907 child_allocation.width = MAX (1, (gint)allocation->width - child_allocation.x * 2);
909 children = menu_shell->children;
912 child = children->data;
913 children = children->next;
915 if (GTK_WIDGET_VISIBLE (child))
917 GtkRequisition child_requisition;
918 gtk_widget_get_child_requisition (child, &child_requisition);
920 child_allocation.height = child_requisition.height;
922 gtk_widget_size_allocate (child, &child_allocation);
923 gtk_widget_queue_draw (child);
925 child_allocation.y += child_allocation.height;
932 gtk_menu_paint (GtkWidget *widget)
934 g_return_if_fail (widget != NULL);
935 g_return_if_fail (GTK_IS_MENU (widget));
937 if (GTK_WIDGET_DRAWABLE (widget))
939 gtk_paint_box (widget->style,
943 NULL, widget, "menu",
949 gtk_menu_draw (GtkWidget *widget,
952 GtkMenuShell *menu_shell;
954 GdkRectangle child_area;
957 g_return_if_fail (widget != NULL);
958 g_return_if_fail (GTK_IS_MENU (widget));
959 g_return_if_fail (area != NULL);
961 if (GTK_WIDGET_DRAWABLE (widget))
963 gtk_menu_paint (widget);
965 menu_shell = GTK_MENU_SHELL (widget);
967 children = menu_shell->children;
970 child = children->data;
971 children = children->next;
973 if (gtk_widget_intersect (child, area, &child_area))
974 gtk_widget_draw (child, &child_area);
980 gtk_menu_expose (GtkWidget *widget,
981 GdkEventExpose *event)
983 GtkMenuShell *menu_shell;
985 GdkEventExpose child_event;
989 g_return_val_if_fail (widget != NULL, FALSE);
990 g_return_val_if_fail (GTK_IS_MENU (widget), FALSE);
991 g_return_val_if_fail (event != NULL, FALSE);
993 menu_shell = GTK_MENU_SHELL (widget);
994 menu = GTK_MENU (widget);
996 if (GTK_WIDGET_DRAWABLE (widget))
998 gtk_menu_paint (widget);
1000 child_event = *event;
1002 children = menu_shell->children;
1005 child = children->data;
1006 children = children->next;
1008 if (GTK_WIDGET_NO_WINDOW (child) &&
1009 gtk_widget_intersect (child, &event->area, &child_event.area))
1010 gtk_widget_event (child, (GdkEvent*) &child_event);
1018 gtk_menu_key_press (GtkWidget *widget,
1021 GtkMenuShell *menu_shell;
1022 gboolean delete = FALSE;
1024 g_return_val_if_fail (widget != NULL, FALSE);
1025 g_return_val_if_fail (GTK_IS_MENU (widget), FALSE);
1026 g_return_val_if_fail (event != NULL, FALSE);
1028 menu_shell = GTK_MENU_SHELL (widget);
1030 if (GTK_WIDGET_CLASS (parent_class)->key_press_event (widget, event))
1033 switch (event->keyval)
1044 /* Modify the accelerators */
1045 if (menu_shell->active_menu_item &&
1046 GTK_BIN (menu_shell->active_menu_item)->child &&
1047 GTK_MENU_ITEM (menu_shell->active_menu_item)->submenu == NULL &&
1048 !gtk_widget_accelerators_locked (menu_shell->active_menu_item) &&
1050 (gtk_accelerator_valid (event->keyval, event->state) &&
1052 !gtk_menu_get_uline_accel_group (GTK_MENU (menu_shell)) ||
1053 (event->keyval >= GDK_F1 && event->keyval <= GDK_F35)))))
1055 GtkMenuItem *menu_item;
1056 GtkAccelGroup *accel_group;
1058 menu_item = GTK_MENU_ITEM (menu_shell->active_menu_item);
1060 if (!GTK_MENU (widget)->accel_group)
1061 accel_group = gtk_accel_group_get_default ();
1063 accel_group = GTK_MENU (widget)->accel_group;
1065 gtk_widget_remove_accelerators (GTK_WIDGET (menu_item),
1066 gtk_signal_name (menu_item->accelerator_signal),
1070 0 == gtk_widget_accelerator_signal (GTK_WIDGET (menu_item),
1077 slist = gtk_accel_group_entries_from_object (GTK_OBJECT (menu_item));
1080 GtkAccelEntry *ac_entry;
1082 ac_entry = slist->data;
1084 if (ac_entry->signal_id == menu_item->accelerator_signal)
1087 slist = slist->next;
1091 gtk_widget_add_accelerator (GTK_WIDGET (menu_item),
1092 gtk_signal_name (menu_item->accelerator_signal),
1104 gtk_menu_motion_notify (GtkWidget *widget,
1105 GdkEventMotion *event)
1107 g_return_val_if_fail (widget != NULL, FALSE);
1108 g_return_val_if_fail (GTK_IS_MENU (widget), FALSE);
1110 if (GTK_MENU_SHELL (widget)->ignore_enter)
1111 GTK_MENU_SHELL (widget)->ignore_enter = FALSE;
1116 gdk_window_get_size (event->window, &width, &height);
1117 if (event->x >= 0 && event->x < width &&
1118 event->y >= 0 && event->y < height)
1120 GdkEvent send_event;
1122 send_event.crossing.type = GDK_ENTER_NOTIFY;
1123 send_event.crossing.window = event->window;
1124 send_event.crossing.time = event->time;
1125 send_event.crossing.send_event = TRUE;
1127 gtk_widget_event (widget, &send_event);
1135 gtk_menu_deactivate (GtkMenuShell *menu_shell)
1139 g_return_if_fail (menu_shell != NULL);
1140 g_return_if_fail (GTK_IS_MENU (menu_shell));
1142 parent = menu_shell->parent_menu_shell;
1144 menu_shell->activate_time = 0;
1145 gtk_menu_popdown (GTK_MENU (menu_shell));
1148 gtk_menu_shell_deactivate (GTK_MENU_SHELL (parent));
1153 gtk_menu_position (GtkMenu *menu)
1156 GtkRequisition requisition;
1159 g_return_if_fail (menu != NULL);
1160 g_return_if_fail (GTK_IS_MENU (menu));
1162 widget = GTK_WIDGET (menu);
1164 gdk_window_get_pointer (NULL, &x, &y, NULL);
1166 /* We need the requisition to figure out the right place to
1167 * popup the menu. In fact, we always need to ask here, since
1168 * if one a size_request was queued while we weren't popped up,
1169 * the requisition won't have been recomputed yet.
1171 gtk_widget_size_request (widget, &requisition);
1173 if (menu->position_func)
1174 (* menu->position_func) (menu, &x, &y, menu->position_func_data);
1180 screen_width = gdk_screen_width ();
1181 screen_height = gdk_screen_height ();
1183 x = CLAMP (x - 2, 0, MAX (0, screen_width - requisition.width));
1184 y = CLAMP (y - 2, 0, MAX (0, screen_height - requisition.height));
1187 /* FIXME: The MAX() here is because gtk_widget_set_uposition
1188 * is broken. Once we provide an alternate interface that
1189 * allows negative values, then we can remove them.
1191 gtk_widget_set_uposition (GTK_MENU_SHELL (menu)->active ?
1192 menu->toplevel : menu->tearoff_window,
1193 MAX (x, 0), MAX (y, 0));
1196 /* Reparent the menu, taking care of the refcounting
1199 gtk_menu_reparent (GtkMenu *menu,
1200 GtkWidget *new_parent,
1203 GtkObject *object = GTK_OBJECT (menu);
1204 GtkWidget *widget = GTK_WIDGET (menu);
1205 gboolean was_floating = GTK_OBJECT_FLOATING (object);
1207 gtk_object_ref (object);
1208 gtk_object_sink (object);
1212 gtk_object_ref (object);
1213 gtk_container_remove (GTK_CONTAINER (widget->parent), widget);
1214 gtk_container_add (GTK_CONTAINER (new_parent), widget);
1215 gtk_object_unref (object);
1218 gtk_widget_reparent (GTK_WIDGET (menu), new_parent);
1219 gtk_widget_set_usize (new_parent, -1, -1);
1222 GTK_OBJECT_SET_FLAGS (object, GTK_FLOATING);
1224 gtk_object_unref (object);
1228 gtk_menu_show_all (GtkWidget *widget)
1230 g_return_if_fail (widget != NULL);
1231 g_return_if_fail (GTK_IS_MENU (widget));
1233 /* Show children, but not self. */
1234 gtk_container_foreach (GTK_CONTAINER (widget), (GtkCallback) gtk_widget_show_all, NULL);
1239 gtk_menu_hide_all (GtkWidget *widget)
1241 g_return_if_fail (widget != NULL);
1242 g_return_if_fail (GTK_IS_MENU (widget));
1244 /* Hide children, but not self. */
1245 gtk_container_foreach (GTK_CONTAINER (widget), (GtkCallback) gtk_widget_hide_all, NULL);