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 Free
16 * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19 #include "gdk/gdkkeysyms.h"
22 #include "gtkmenuitem.h"
23 #include "gtksignal.h"
26 #define MENU_ITEM_CLASS(w) GTK_MENU_ITEM_CLASS (GTK_OBJECT (w)->klass)
29 static void gtk_menu_class_init (GtkMenuClass *klass);
30 static void gtk_menu_init (GtkMenu *menu);
31 static void gtk_menu_show (GtkWidget *widget);
32 static void gtk_menu_map (GtkWidget *widget);
33 static void gtk_menu_unmap (GtkWidget *widget);
34 static void gtk_menu_realize (GtkWidget *widget);
35 static void gtk_menu_size_request (GtkWidget *widget,
36 GtkRequisition *requisition);
37 static void gtk_menu_size_allocate (GtkWidget *widget,
38 GtkAllocation *allocation);
39 static void gtk_menu_paint (GtkWidget *widget);
40 static void gtk_menu_draw (GtkWidget *widget,
42 static gint gtk_menu_expose (GtkWidget *widget,
43 GdkEventExpose *event);
44 static gint gtk_menu_configure (GtkWidget *widget,
45 GdkEventConfigure *event);
46 static gint gtk_menu_key_press (GtkWidget *widget,
48 static gint gtk_menu_need_resize (GtkContainer *container);
49 static void gtk_menu_deactivate (GtkMenuShell *menu_shell);
55 static guint menu_type = 0;
59 GtkTypeInfo menu_info =
63 sizeof (GtkMenuClass),
64 (GtkClassInitFunc) gtk_menu_class_init,
65 (GtkObjectInitFunc) gtk_menu_init,
69 menu_type = gtk_type_unique (gtk_menu_shell_get_type (), &menu_info);
76 gtk_menu_class_init (GtkMenuClass *class)
78 GtkWidgetClass *widget_class;
79 GtkContainerClass *container_class;
80 GtkMenuShellClass *menu_shell_class;
82 widget_class = (GtkWidgetClass*) class;
83 container_class = (GtkContainerClass*) class;
84 menu_shell_class = (GtkMenuShellClass*) class;
86 widget_class->show = gtk_menu_show;
87 widget_class->map = gtk_menu_map;
88 widget_class->unmap = gtk_menu_unmap;
89 widget_class->realize = gtk_menu_realize;
90 widget_class->draw = gtk_menu_draw;
91 widget_class->size_request = gtk_menu_size_request;
92 widget_class->size_allocate = gtk_menu_size_allocate;
93 widget_class->expose_event = gtk_menu_expose;
94 widget_class->configure_event = gtk_menu_configure;
95 widget_class->key_press_event = gtk_menu_key_press;
97 container_class->need_resize = gtk_menu_need_resize;
99 menu_shell_class->submenu_placement = GTK_LEFT_RIGHT;
100 menu_shell_class->deactivate = gtk_menu_deactivate;
104 gtk_menu_init (GtkMenu *menu)
106 GTK_WIDGET_SET_FLAGS (menu, GTK_ANCHORED);
108 menu->parent_menu_item = NULL;
109 menu->old_active_menu_item = NULL;
110 menu->accelerator_table = NULL;
111 menu->position_func = NULL;
112 menu->position_func_data = NULL;
114 GTK_MENU_SHELL (menu)->menu_flag = TRUE;
120 return GTK_WIDGET (gtk_type_new (gtk_menu_get_type ()));
124 gtk_menu_append (GtkMenu *menu,
127 gtk_menu_shell_append (GTK_MENU_SHELL (menu), child);
131 gtk_menu_prepend (GtkMenu *menu,
134 gtk_menu_shell_prepend (GTK_MENU_SHELL (menu), child);
138 gtk_menu_insert (GtkMenu *menu,
142 gtk_menu_shell_insert (GTK_MENU_SHELL (menu), child, position);
146 gtk_menu_popup (GtkMenu *menu,
147 GtkWidget *parent_menu_shell,
148 GtkWidget *parent_menu_item,
149 GtkMenuPositionFunc func,
152 guint32 activate_time)
154 g_return_if_fail (menu != NULL);
155 g_return_if_fail (GTK_IS_MENU (menu));
157 GTK_MENU_SHELL (menu)->parent_menu_shell = parent_menu_shell;
158 GTK_MENU_SHELL (menu)->active = TRUE;
159 GTK_MENU_SHELL (menu)->button = button;
161 menu->parent_menu_item = parent_menu_item;
162 menu->position_func = func;
163 menu->position_func_data = data;
164 GTK_MENU_SHELL (menu)->activate_time = activate_time;
166 gtk_widget_show (GTK_WIDGET (menu));
167 gtk_grab_add (GTK_WIDGET (menu));
171 gtk_menu_popdown (GtkMenu *menu)
173 GtkMenuShell *menu_shell;
175 g_return_if_fail (menu != NULL);
176 g_return_if_fail (GTK_IS_MENU (menu));
178 menu_shell = GTK_MENU_SHELL (menu);
180 menu_shell->parent_menu_shell = NULL;
181 menu_shell->active = FALSE;
183 if (menu_shell->active_menu_item)
185 menu->old_active_menu_item = menu_shell->active_menu_item;
186 gtk_menu_item_deselect (GTK_MENU_ITEM (menu_shell->active_menu_item));
187 menu_shell->active_menu_item = NULL;
190 gtk_widget_hide (GTK_WIDGET (menu));
191 gtk_grab_remove (GTK_WIDGET (menu));
195 gtk_menu_get_active (GtkMenu *menu)
200 g_return_val_if_fail (menu != NULL, NULL);
201 g_return_val_if_fail (GTK_IS_MENU (menu), NULL);
203 if (!menu->old_active_menu_item)
206 children = GTK_MENU_SHELL (menu)->children;
210 child = children->data;
211 children = children->next;
213 if (GTK_BIN (child)->child)
218 menu->old_active_menu_item = child;
221 return menu->old_active_menu_item;
225 gtk_menu_set_active (GtkMenu *menu,
231 g_return_if_fail (menu != NULL);
232 g_return_if_fail (GTK_IS_MENU (menu));
234 tmp_list = g_list_nth (GTK_MENU_SHELL (menu)->children, index);
237 child = tmp_list->data;
238 if (GTK_BIN (child)->child)
239 menu->old_active_menu_item = child;
244 gtk_menu_set_accelerator_table (GtkMenu *menu,
245 GtkAcceleratorTable *table)
247 g_return_if_fail (menu != NULL);
248 g_return_if_fail (GTK_IS_MENU (menu));
250 if (menu->accelerator_table)
251 gtk_accelerator_table_unref (menu->accelerator_table);
253 menu->accelerator_table = table;
254 if (menu->accelerator_table)
255 gtk_accelerator_table_ref (menu->accelerator_table);
260 gtk_menu_show (GtkWidget *widget)
262 g_return_if_fail (widget != NULL);
263 g_return_if_fail (GTK_IS_MENU (widget));
265 GTK_WIDGET_SET_FLAGS (widget, GTK_VISIBLE);
266 gtk_widget_map (widget);
270 gtk_menu_map (GtkWidget *widget)
273 GtkMenuShell *menu_shell;
276 GtkAllocation allocation;
279 g_return_if_fail (widget != NULL);
280 g_return_if_fail (GTK_IS_MENU (widget));
282 menu = GTK_MENU (widget);
283 menu_shell = GTK_MENU_SHELL (widget);
284 GTK_WIDGET_SET_FLAGS (menu_shell, GTK_MAPPED);
285 GTK_WIDGET_UNSET_FLAGS (menu_shell, GTK_UNMAPPED);
287 gtk_widget_size_request (widget, &widget->requisition);
289 if (menu_shell->menu_flag)
291 menu_shell->menu_flag = FALSE;
293 allocation.x = widget->allocation.x;
294 allocation.y = widget->allocation.y;
295 allocation.width = widget->requisition.width;
296 allocation.height = widget->requisition.height;
298 gtk_widget_size_allocate (widget, &allocation);
301 gdk_window_get_pointer (NULL, &x, &y, NULL);
303 if (menu->position_func)
304 (* menu->position_func) (menu, &x, &y, menu->position_func_data);
310 screen_width = gdk_screen_width ();
311 screen_height = gdk_screen_height ();
316 if ((x + widget->requisition.width) > screen_width)
317 x -= ((x + widget->requisition.width) - screen_width);
320 if ((y + widget->requisition.height) > screen_height)
321 y -= ((y + widget->requisition.height) - screen_height);
326 gdk_window_move_resize (widget->window, x, y,
327 widget->requisition.width,
328 widget->requisition.height);
330 children = menu_shell->children;
333 child = children->data;
334 children = children->next;
336 if (GTK_WIDGET_VISIBLE (child) && !GTK_WIDGET_MAPPED (child))
337 gtk_widget_map (child);
340 gdk_window_show (widget->window);
344 gtk_menu_unmap (GtkWidget *widget)
346 g_return_if_fail (widget != NULL);
347 g_return_if_fail (GTK_IS_MENU (widget));
349 GTK_WIDGET_UNSET_FLAGS (widget, GTK_MAPPED);
350 GTK_WIDGET_SET_FLAGS (widget, GTK_UNMAPPED);
351 gdk_window_hide (widget->window);
355 gtk_menu_realize (GtkWidget *widget)
357 GdkWindowAttr attributes;
358 gint attributes_mask;
360 g_return_if_fail (widget != NULL);
361 g_return_if_fail (GTK_IS_MENU (widget));
363 GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
365 attributes.x = widget->allocation.x;
366 attributes.y = widget->allocation.y;
367 attributes.width = widget->allocation.width;
368 attributes.height = widget->allocation.height;
369 attributes.wclass = GDK_INPUT_OUTPUT;
370 attributes.visual = gtk_widget_get_visual (widget);
371 attributes.colormap = gtk_widget_get_colormap (widget);
372 attributes.window_type = GDK_WINDOW_TEMP;
373 attributes.event_mask = gtk_widget_get_events (widget);
374 attributes.event_mask |= (GDK_EXPOSURE_MASK |
378 attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
379 widget->window = gdk_window_new (NULL, &attributes, attributes_mask);
380 gdk_window_set_user_data (widget->window, widget);
382 widget->style = gtk_style_attach (widget->style, widget->window);
383 gtk_style_set_background (widget->style, widget->window, GTK_STATE_NORMAL);
387 gtk_menu_size_request (GtkWidget *widget,
388 GtkRequisition *requisition)
391 GtkMenuShell *menu_shell;
394 gint max_accelerator_size;
395 gint max_toggle_size;
397 g_return_if_fail (widget != NULL);
398 g_return_if_fail (GTK_IS_MENU (widget));
399 g_return_if_fail (requisition != NULL);
401 menu = GTK_MENU (widget);
402 menu_shell = GTK_MENU_SHELL (widget);
404 requisition->width = 0;
405 requisition->height = 0;
407 max_accelerator_size = 0;
410 children = menu_shell->children;
413 child = children->data;
414 children = children->next;
416 if (GTK_WIDGET_VISIBLE (child))
418 GTK_MENU_ITEM (child)->show_submenu_indicator = TRUE;
419 gtk_widget_size_request (child, &child->requisition);
421 requisition->width = MAX (requisition->width, child->requisition.width);
422 requisition->height += child->requisition.height;
424 max_accelerator_size = MAX (max_accelerator_size, GTK_MENU_ITEM (child)->accelerator_size);
425 max_toggle_size = MAX (max_toggle_size, MENU_ITEM_CLASS (child)->toggle_size);
429 requisition->width += max_toggle_size + max_accelerator_size;
430 requisition->width += (GTK_CONTAINER (menu)->border_width +
431 widget->style->klass->xthickness) * 2;
432 requisition->height += (GTK_CONTAINER (menu)->border_width +
433 widget->style->klass->ythickness) * 2;
435 children = menu_shell->children;
438 child = children->data;
439 children = children->next;
441 GTK_MENU_ITEM (child)->accelerator_size = max_accelerator_size;
442 GTK_MENU_ITEM (child)->toggle_size = max_toggle_size;
447 gtk_menu_size_allocate (GtkWidget *widget,
448 GtkAllocation *allocation)
451 GtkMenuShell *menu_shell;
453 GtkAllocation child_allocation;
456 g_return_if_fail (widget != NULL);
457 g_return_if_fail (GTK_IS_MENU (widget));
458 g_return_if_fail (allocation != NULL);
460 menu = GTK_MENU (widget);
461 menu_shell = GTK_MENU_SHELL (widget);
463 widget->allocation = *allocation;
465 if (menu_shell->children)
467 child_allocation.x = (GTK_CONTAINER (menu)->border_width +
468 widget->style->klass->xthickness);
469 child_allocation.y = (GTK_CONTAINER (menu)->border_width +
470 widget->style->klass->ythickness);
471 child_allocation.width = allocation->width - child_allocation.x * 2;
473 children = menu_shell->children;
476 child = children->data;
477 children = children->next;
479 if (GTK_WIDGET_VISIBLE (child))
481 child_allocation.height = child->requisition.height;
483 gtk_widget_size_allocate (child, &child_allocation);
485 child_allocation.y += child_allocation.height;
492 gtk_menu_paint (GtkWidget *widget)
494 g_return_if_fail (widget != NULL);
495 g_return_if_fail (GTK_IS_MENU (widget));
497 if (GTK_WIDGET_DRAWABLE (widget))
499 gtk_draw_shadow (widget->style,
504 widget->allocation.width,
505 widget->allocation.height);
510 gtk_menu_draw (GtkWidget *widget,
513 GtkMenuShell *menu_shell;
515 GdkRectangle child_area;
518 g_return_if_fail (widget != NULL);
519 g_return_if_fail (GTK_IS_MENU (widget));
520 g_return_if_fail (area != NULL);
522 if (GTK_WIDGET_DRAWABLE (widget))
524 gtk_menu_paint (widget);
526 menu_shell = GTK_MENU_SHELL (widget);
528 children = menu_shell->children;
531 child = children->data;
532 children = children->next;
534 if (gtk_widget_intersect (child, area, &child_area))
535 gtk_widget_draw (child, &child_area);
541 gtk_menu_expose (GtkWidget *widget,
542 GdkEventExpose *event)
544 GtkMenuShell *menu_shell;
546 GdkEventExpose child_event;
549 g_return_val_if_fail (widget != NULL, FALSE);
550 g_return_val_if_fail (GTK_IS_MENU (widget), FALSE);
551 g_return_val_if_fail (event != NULL, FALSE);
553 if (!GTK_WIDGET_UNMAPPED (widget))
554 GTK_WIDGET_SET_FLAGS (widget, GTK_MAPPED);
556 if (GTK_WIDGET_DRAWABLE (widget))
558 gtk_menu_paint (widget);
560 menu_shell = GTK_MENU_SHELL (widget);
561 child_event = *event;
563 children = menu_shell->children;
566 child = children->data;
567 children = children->next;
569 if (GTK_WIDGET_NO_WINDOW (child) &&
570 gtk_widget_intersect (child, &event->area, &child_event.area))
571 gtk_widget_event (child, (GdkEvent*) &child_event);
579 gtk_menu_configure (GtkWidget *widget,
580 GdkEventConfigure *event)
582 GtkAllocation allocation;
584 g_return_val_if_fail (widget != NULL, FALSE);
585 g_return_val_if_fail (GTK_IS_MENU (widget), FALSE);
586 g_return_val_if_fail (event != NULL, FALSE);
588 if (GTK_MENU_SHELL (widget)->menu_flag)
590 GTK_MENU_SHELL (widget)->menu_flag = FALSE;
594 allocation.width = event->width;
595 allocation.height = event->height;
597 gtk_widget_size_allocate (widget, &allocation);
604 gtk_menu_key_press (GtkWidget *widget,
607 GtkAllocation allocation;
608 GtkAcceleratorTable *table;
609 GtkMenuShell *menu_shell;
610 GtkMenuItem *menu_item;
614 g_return_val_if_fail (widget != NULL, FALSE);
615 g_return_val_if_fail (GTK_IS_MENU (widget), FALSE);
616 g_return_val_if_fail (event != NULL, FALSE);
618 delete = ((event->keyval == GDK_Delete) ||
619 (event->keyval == GDK_BackSpace));
621 if (delete || ((event->keyval >= 0x20) && (event->keyval <= 0xFF)))
623 menu_shell = GTK_MENU_SHELL (widget);
624 menu_item = GTK_MENU_ITEM (menu_shell->active_menu_item);
626 if (menu_item && GTK_BIN (menu_item)->child)
629 gtk_container_block_resize (GTK_CONTAINER (widget));
632 /* if the menu item currently has an accelerator then we'll
633 * remove it before we do anything else.
635 if (menu_item->accelerator_signal)
637 signame = gtk_signal_name (menu_item->accelerator_signal);
638 table = gtk_accelerator_table_find (GTK_OBJECT (widget),
640 menu_item->accelerator_key,
641 menu_item->accelerator_mods);
643 table = GTK_MENU (widget)->accelerator_table;
644 gtk_widget_remove_accelerator (GTK_WIDGET (menu_item),
649 table = GTK_MENU (widget)->accelerator_table;
651 /* if we aren't simply deleting the accelerator, then we'll install
655 gtk_widget_install_accelerator (GTK_WIDGET (menu_item),
657 toupper (event->keyval),
660 /* check and see if the menu has changed size. */
661 gtk_widget_size_request (widget, &widget->requisition);
663 allocation.x = widget->allocation.x;
664 allocation.y = widget->allocation.y;
665 allocation.width = widget->requisition.width;
666 allocation.height = widget->requisition.height;
668 if ((allocation.width == widget->allocation.width) &&
669 (allocation.height == widget->allocation.height))
671 gtk_widget_queue_draw (widget);
675 gtk_widget_size_allocate (GTK_WIDGET (widget), &allocation);
676 gtk_menu_map (widget);
679 /* unblock resizes */
680 gtk_container_unblock_resize (GTK_CONTAINER (widget));
688 gtk_menu_need_resize (GtkContainer *container)
690 GtkAllocation allocation;
692 g_return_val_if_fail (container != NULL, FALSE);
693 g_return_val_if_fail (GTK_IS_MENU (container), FALSE);
695 if (GTK_WIDGET_VISIBLE (container))
697 GTK_MENU_SHELL (container)->menu_flag = FALSE;
699 gtk_widget_size_request (GTK_WIDGET (container),
700 >K_WIDGET (container)->requisition);
702 allocation.x = GTK_WIDGET (container)->allocation.x;
703 allocation.y = GTK_WIDGET (container)->allocation.y;
704 allocation.width = GTK_WIDGET (container)->requisition.width;
705 allocation.height = GTK_WIDGET (container)->requisition.height;
707 gtk_widget_size_allocate (GTK_WIDGET (container), &allocation);
711 GTK_MENU_SHELL (container)->menu_flag = TRUE;
718 gtk_menu_deactivate (GtkMenuShell *menu_shell)
720 GtkMenuShell *parent;
722 g_return_if_fail (menu_shell != NULL);
723 g_return_if_fail (GTK_IS_MENU (menu_shell));
725 parent = GTK_MENU_SHELL (menu_shell->parent_menu_shell);
727 menu_shell->activate_time = 0;
728 gtk_menu_popdown (GTK_MENU (menu_shell));
731 gtk_menu_shell_deactivate (parent);