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.
20 #include "gtkaccellabel.h"
23 #include "gtkmenuitem.h"
24 #include "gtksignal.h"
27 #define BORDER_SPACING 3
28 #define SELECT_TIMEOUT 20
30 #define MENU_ITEM_CLASS(w) GTK_MENU_ITEM_CLASS (GTK_OBJECT (w)->klass)
39 static void gtk_menu_item_class_init (GtkMenuItemClass *klass);
40 static void gtk_menu_item_init (GtkMenuItem *menu_item);
41 static void gtk_menu_item_destroy (GtkObject *object);
42 static void gtk_menu_item_size_request (GtkWidget *widget,
43 GtkRequisition *requisition);
44 static void gtk_menu_item_size_allocate (GtkWidget *widget,
45 GtkAllocation *allocation);
46 static void gtk_menu_item_paint (GtkWidget *widget,
48 static void gtk_menu_item_draw (GtkWidget *widget,
50 static gint gtk_menu_item_expose (GtkWidget *widget,
51 GdkEventExpose *event);
52 static void gtk_real_menu_item_select (GtkItem *item);
53 static void gtk_real_menu_item_deselect (GtkItem *item);
54 static gint gtk_menu_item_select_timeout (gpointer data);
55 static void gtk_menu_item_position_menu (GtkMenu *menu,
59 static void gtk_menu_item_show_all (GtkWidget *widget);
60 static void gtk_menu_item_hide_all (GtkWidget *widget);
62 static GtkItemClass *parent_class;
63 static guint menu_item_signals[LAST_SIGNAL] = { 0 };
67 gtk_menu_item_get_type (void)
69 static GtkType menu_item_type = 0;
73 GtkTypeInfo menu_item_info =
77 sizeof (GtkMenuItemClass),
78 (GtkClassInitFunc) gtk_menu_item_class_init,
79 (GtkObjectInitFunc) gtk_menu_item_init,
84 menu_item_type = gtk_type_unique (gtk_item_get_type (), &menu_item_info);
85 gtk_type_set_chunk_alloc (menu_item_type, 16);
88 return menu_item_type;
92 gtk_menu_item_class_init (GtkMenuItemClass *klass)
94 GtkObjectClass *object_class;
95 GtkWidgetClass *widget_class;
96 GtkItemClass *item_class;
98 object_class = (GtkObjectClass*) klass;
99 widget_class = (GtkWidgetClass*) klass;
100 item_class = (GtkItemClass*) klass;
102 parent_class = gtk_type_class (gtk_item_get_type ());
104 menu_item_signals[ACTIVATE] =
105 gtk_signal_new ("activate",
108 GTK_SIGNAL_OFFSET (GtkMenuItemClass, activate),
109 gtk_signal_default_marshaller,
112 gtk_object_class_add_signals (object_class, menu_item_signals, LAST_SIGNAL);
114 object_class->destroy = gtk_menu_item_destroy;
116 widget_class->activate_signal = menu_item_signals[ACTIVATE];
117 widget_class->size_request = gtk_menu_item_size_request;
118 widget_class->size_allocate = gtk_menu_item_size_allocate;
119 widget_class->draw = gtk_menu_item_draw;
120 widget_class->expose_event = gtk_menu_item_expose;
121 widget_class->show_all = gtk_menu_item_show_all;
122 widget_class->hide_all = gtk_menu_item_hide_all;
124 item_class->select = gtk_real_menu_item_select;
125 item_class->deselect = gtk_real_menu_item_deselect;
127 klass->activate = NULL;
129 klass->toggle_size = 0;
133 gtk_menu_item_init (GtkMenuItem *menu_item)
135 menu_item->submenu = NULL;
136 menu_item->accelerator_signal = menu_item_signals[ACTIVATE];
137 menu_item->toggle_size = 0;
138 menu_item->accelerator_width = 0;
139 menu_item->show_toggle_indicator = FALSE;
140 menu_item->show_submenu_indicator = FALSE;
141 menu_item->submenu_direction = GTK_DIRECTION_RIGHT;
142 menu_item->submenu_placement = GTK_TOP_BOTTOM;
143 menu_item->right_justify = FALSE;
145 menu_item->timer = 0;
149 gtk_menu_item_new (void)
151 return GTK_WIDGET (gtk_type_new (gtk_menu_item_get_type ()));
155 gtk_menu_item_new_with_label (const gchar *label)
157 GtkWidget *menu_item;
158 GtkWidget *accel_label;
160 menu_item = gtk_menu_item_new ();
161 accel_label = gtk_accel_label_new (label);
162 gtk_misc_set_alignment (GTK_MISC (accel_label), 0.0, 0.5);
164 gtk_container_add (GTK_CONTAINER (menu_item), accel_label);
165 gtk_accel_label_set_accel_widget (GTK_ACCEL_LABEL (accel_label), menu_item);
166 gtk_widget_show (accel_label);
172 gtk_menu_item_destroy (GtkObject *object)
174 GtkMenuItem *menu_item;
176 g_return_if_fail (object != NULL);
177 g_return_if_fail (GTK_IS_MENU_ITEM (object));
179 menu_item = GTK_MENU_ITEM (object);
181 if (menu_item->submenu)
182 gtk_widget_destroy (menu_item->submenu);
184 if (GTK_OBJECT_CLASS (parent_class)->destroy)
185 (* GTK_OBJECT_CLASS (parent_class)->destroy) (object);
189 gtk_menu_item_detacher (GtkWidget *widget,
192 GtkMenuItem *menu_item;
194 g_return_if_fail (widget != NULL);
195 g_return_if_fail (GTK_IS_MENU_ITEM (widget));
197 menu_item = GTK_MENU_ITEM (widget);
198 g_return_if_fail (menu_item->submenu == (GtkWidget*) menu);
200 menu_item->submenu = NULL;
204 gtk_menu_item_set_submenu (GtkMenuItem *menu_item,
207 g_return_if_fail (menu_item != NULL);
208 g_return_if_fail (GTK_IS_MENU_ITEM (menu_item));
210 if (menu_item->submenu != submenu)
212 gtk_menu_item_remove_submenu (menu_item);
214 menu_item->submenu = submenu;
215 gtk_menu_attach_to_widget (GTK_MENU (submenu),
216 GTK_WIDGET (menu_item),
217 gtk_menu_item_detacher);
219 if (GTK_WIDGET (menu_item)->parent)
220 gtk_widget_queue_resize (GTK_WIDGET (menu_item));
225 gtk_menu_item_remove_submenu (GtkMenuItem *menu_item)
227 g_return_if_fail (menu_item != NULL);
228 g_return_if_fail (GTK_IS_MENU_ITEM (menu_item));
230 if (menu_item->submenu)
231 gtk_menu_detach (GTK_MENU (menu_item->submenu));
235 gtk_menu_item_set_placement (GtkMenuItem *menu_item,
236 GtkSubmenuPlacement placement)
238 g_return_if_fail (menu_item != NULL);
239 g_return_if_fail (GTK_IS_MENU_ITEM (menu_item));
241 menu_item->submenu_placement = placement;
245 gtk_menu_item_configure (GtkMenuItem *menu_item,
246 gint show_toggle_indicator,
247 gint show_submenu_indicator)
249 g_return_if_fail (menu_item != NULL);
250 g_return_if_fail (GTK_IS_MENU_ITEM (menu_item));
252 menu_item->show_toggle_indicator = (show_toggle_indicator == TRUE);
253 menu_item->show_submenu_indicator = (show_submenu_indicator == TRUE);
257 gtk_menu_item_select (GtkMenuItem *menu_item)
259 gtk_item_select (GTK_ITEM (menu_item));
263 gtk_menu_item_deselect (GtkMenuItem *menu_item)
265 gtk_item_deselect (GTK_ITEM (menu_item));
269 gtk_menu_item_activate (GtkMenuItem *menu_item)
271 gtk_signal_emit (GTK_OBJECT (menu_item), menu_item_signals[ACTIVATE]);
275 gtk_menu_item_accel_width_foreach (GtkWidget *widget,
280 if (GTK_IS_ACCEL_LABEL (widget))
284 w = gtk_accel_label_accelerator_width (GTK_ACCEL_LABEL (widget));
285 *width = MAX (*width, w);
287 else if (GTK_IS_CONTAINER (widget))
288 gtk_container_foreach (GTK_CONTAINER (widget),
289 gtk_menu_item_accel_width_foreach,
294 gtk_menu_item_size_request (GtkWidget *widget,
295 GtkRequisition *requisition)
297 GtkMenuItem *menu_item;
301 g_return_if_fail (widget != NULL);
302 g_return_if_fail (GTK_IS_MENU_ITEM (widget));
303 g_return_if_fail (requisition != NULL);
305 bin = GTK_BIN (widget);
306 menu_item = GTK_MENU_ITEM (widget);
308 requisition->width = (GTK_CONTAINER (widget)->border_width +
309 widget->style->klass->xthickness +
311 requisition->height = (GTK_CONTAINER (widget)->border_width +
312 widget->style->klass->ythickness) * 2;
314 if (bin->child && GTK_WIDGET_VISIBLE (bin->child))
316 gtk_widget_size_request (bin->child, &bin->child->requisition);
318 requisition->width += bin->child->requisition.width;
319 requisition->height += bin->child->requisition.height;
322 if (menu_item->submenu && menu_item->show_submenu_indicator)
323 requisition->width += 21;
326 gtk_container_foreach (GTK_CONTAINER (menu_item),
327 gtk_menu_item_accel_width_foreach,
329 menu_item->accelerator_width = accel_width;
333 gtk_menu_item_size_allocate (GtkWidget *widget,
334 GtkAllocation *allocation)
336 GtkMenuItem *menu_item;
338 GtkAllocation child_allocation;
340 g_return_if_fail (widget != NULL);
341 g_return_if_fail (GTK_IS_MENU_ITEM (widget));
342 g_return_if_fail (allocation != NULL);
344 menu_item = GTK_MENU_ITEM (widget);
345 bin = GTK_BIN (widget);
347 widget->allocation = *allocation;
351 child_allocation.x = (GTK_CONTAINER (widget)->border_width +
352 widget->style->klass->xthickness +
354 child_allocation.y = (GTK_CONTAINER (widget)->border_width +
355 widget->style->klass->ythickness);
356 child_allocation.width = MAX (1, allocation->width - child_allocation.x * 2);
357 child_allocation.height = MAX (1, allocation->height - child_allocation.y * 2);
358 child_allocation.x += GTK_MENU_ITEM (widget)->toggle_size;
359 child_allocation.width -= GTK_MENU_ITEM (widget)->toggle_size;
360 if (menu_item->submenu && menu_item->show_submenu_indicator)
361 child_allocation.width -= 21;
363 gtk_widget_size_allocate (bin->child, &child_allocation);
366 if (GTK_WIDGET_REALIZED (widget))
367 gdk_window_move_resize (widget->window,
368 allocation->x, allocation->y,
369 allocation->width, allocation->height);
371 if (menu_item->submenu)
372 gtk_menu_reposition (GTK_MENU (menu_item->submenu));
376 gtk_menu_item_paint (GtkWidget *widget,
379 GtkMenuItem *menu_item;
380 GtkStateType state_type;
381 GtkShadowType shadow_type;
385 g_return_if_fail (widget != NULL);
386 g_return_if_fail (GTK_IS_MENU_ITEM (widget));
388 if (GTK_WIDGET_DRAWABLE (widget))
390 menu_item = GTK_MENU_ITEM (widget);
392 state_type = widget->state;
393 if (!GTK_WIDGET_IS_SENSITIVE (widget))
394 state_type = GTK_STATE_INSENSITIVE;
396 gtk_style_set_background (widget->style, widget->window, state_type);
397 gdk_window_clear_area (widget->window, area->x, area->y, area->width, area->height);
399 x = GTK_CONTAINER (menu_item)->border_width;
400 y = GTK_CONTAINER (menu_item)->border_width;
401 width = widget->allocation.width - x * 2;
402 height = widget->allocation.height - y * 2;
404 if ((state_type == GTK_STATE_PRELIGHT) &&
405 (GTK_BIN (menu_item)->child))
406 gtk_draw_shadow (widget->style,
410 x, y, width, height);
412 if (menu_item->submenu && menu_item->show_submenu_indicator)
414 shadow_type = GTK_SHADOW_OUT;
415 if (state_type == GTK_STATE_PRELIGHT)
416 shadow_type = GTK_SHADOW_IN;
418 gtk_draw_arrow (widget->style, widget->window,
419 state_type, shadow_type, GTK_ARROW_RIGHT, FALSE,
420 x + width - 15, y + height / 2 - 5, 10, 10);
422 else if (!GTK_BIN (menu_item)->child)
424 gtk_draw_hline (widget->style, widget->window, GTK_STATE_NORMAL,
425 0, widget->allocation.width, 0);
431 gtk_menu_item_draw (GtkWidget *widget,
435 GdkRectangle child_area;
437 g_return_if_fail (widget != NULL);
438 g_return_if_fail (GTK_IS_MENU_ITEM (widget));
439 g_return_if_fail (area != NULL);
441 if (GTK_WIDGET_DRAWABLE (widget))
443 gtk_menu_item_paint (widget, area);
445 bin = GTK_BIN (widget);
449 if (gtk_widget_intersect (bin->child, area, &child_area))
450 gtk_widget_draw (bin->child, &child_area);
456 gtk_menu_item_expose (GtkWidget *widget,
457 GdkEventExpose *event)
460 GdkEventExpose child_event;
462 g_return_val_if_fail (widget != NULL, FALSE);
463 g_return_val_if_fail (GTK_IS_MENU_ITEM (widget), FALSE);
464 g_return_val_if_fail (event != NULL, FALSE);
466 if (GTK_WIDGET_DRAWABLE (widget))
468 gtk_menu_item_paint (widget, &event->area);
470 bin = GTK_BIN (widget);
474 child_event = *event;
476 if (GTK_WIDGET_NO_WINDOW (bin->child) &&
477 gtk_widget_intersect (bin->child, &event->area, &child_event.area))
478 gtk_widget_event (bin->child, (GdkEvent*) &child_event);
486 gtk_real_menu_item_select (GtkItem *item)
488 GtkMenuItem *menu_item;
490 g_return_if_fail (item != NULL);
491 g_return_if_fail (GTK_IS_MENU_ITEM (item));
493 menu_item = GTK_MENU_ITEM (item);
495 if (menu_item->submenu && !GTK_WIDGET_VISIBLE (menu_item->submenu))
496 menu_item->timer = gtk_timeout_add (SELECT_TIMEOUT, gtk_menu_item_select_timeout, menu_item);
498 gtk_widget_set_state (GTK_WIDGET (menu_item), GTK_STATE_PRELIGHT);
499 gtk_widget_draw (GTK_WIDGET (menu_item), NULL);
503 gtk_real_menu_item_deselect (GtkItem *item)
505 GtkMenuItem *menu_item;
507 g_return_if_fail (item != NULL);
508 g_return_if_fail (GTK_IS_MENU_ITEM (item));
510 menu_item = GTK_MENU_ITEM (item);
512 if (menu_item->submenu)
514 if (menu_item->timer)
515 gtk_timeout_remove (menu_item->timer);
517 gtk_menu_popdown (GTK_MENU (menu_item->submenu));
520 gtk_widget_set_state (GTK_WIDGET (menu_item), GTK_STATE_NORMAL);
521 gtk_widget_draw (GTK_WIDGET (menu_item), NULL);
525 gtk_menu_item_select_timeout (gpointer data)
527 GtkMenuItem *menu_item;
529 menu_item = GTK_MENU_ITEM (data);
530 menu_item->timer = 0;
532 gtk_menu_popup (GTK_MENU (menu_item->submenu),
533 GTK_WIDGET (menu_item)->parent,
534 GTK_WIDGET (menu_item),
535 gtk_menu_item_position_menu,
537 GTK_MENU_SHELL (GTK_WIDGET (menu_item)->parent)->button,
544 gtk_menu_item_position_menu (GtkMenu *menu,
549 GtkMenuItem *menu_item;
550 GtkWidget *parent_menu_item;
553 gint twidth, theight;
556 g_return_if_fail (menu != NULL);
557 g_return_if_fail (x != NULL);
558 g_return_if_fail (y != NULL);
560 menu_item = GTK_MENU_ITEM (user_data);
562 twidth = GTK_WIDGET (menu)->requisition.width;
563 theight = GTK_WIDGET (menu)->requisition.height;
565 screen_width = gdk_screen_width ();
566 screen_height = gdk_screen_height ();
568 if (!gdk_window_get_origin (GTK_WIDGET (menu_item)->window, &tx, &ty))
570 g_warning ("Menu not on screen");
574 switch (menu_item->submenu_placement)
577 if ((ty + GTK_WIDGET (menu_item)->allocation.height + theight) <= screen_height)
578 ty += GTK_WIDGET (menu_item)->allocation.height;
579 else if ((ty - theight) >= 0)
582 ty += GTK_WIDGET (menu_item)->allocation.height;
584 if ((tx + twidth) > screen_width)
586 tx -= ((tx + twidth) - screen_width);
593 menu_item->submenu_direction = GTK_DIRECTION_RIGHT;
594 parent_menu_item = GTK_MENU (GTK_WIDGET (menu_item)->parent)->parent_menu_item;
595 if (parent_menu_item)
596 menu_item->submenu_direction = GTK_MENU_ITEM (parent_menu_item)->submenu_direction;
598 switch (menu_item->submenu_direction)
600 case GTK_DIRECTION_LEFT:
601 if ((tx - twidth) >= 0)
605 menu_item->submenu_direction = GTK_DIRECTION_RIGHT;
606 tx += GTK_WIDGET (menu_item)->allocation.width - 5;
610 case GTK_DIRECTION_RIGHT:
611 if ((tx + GTK_WIDGET (menu_item)->allocation.width + twidth - 5) <= screen_width)
612 tx += GTK_WIDGET (menu_item)->allocation.width - 5;
615 menu_item->submenu_direction = GTK_DIRECTION_LEFT;
621 if ((ty + GTK_WIDGET (menu_item)->allocation.height / 4 + theight) <= screen_height)
622 ty += GTK_WIDGET (menu_item)->allocation.height / 4;
625 ty -= ((ty + theight) - screen_height);
637 gtk_menu_item_right_justify(GtkMenuItem *menuitem)
639 g_return_if_fail (menuitem != NULL);
640 g_return_if_fail (GTK_IS_MENU_ITEM (menuitem));
642 menuitem->right_justify = 1;
646 gtk_menu_item_show_all (GtkWidget *widget)
648 GtkContainer *container;
649 GtkMenuItem *menu_item;
651 g_return_if_fail (widget != NULL);
652 g_return_if_fail (GTK_IS_MENU_ITEM (widget));
653 container = GTK_CONTAINER (widget);
654 menu_item = GTK_MENU_ITEM (widget);
656 /* Show children, traverse to submenu, show self. */
657 gtk_container_foreach (container, (GtkCallback) gtk_widget_show_all, NULL);
658 if (menu_item->submenu)
659 gtk_widget_show_all (menu_item->submenu);
660 gtk_widget_show (widget);
665 gtk_menu_item_hide_all (GtkWidget *widget)
667 GtkContainer *container;
668 GtkMenuItem *menu_item;
670 g_return_if_fail (widget != NULL);
671 g_return_if_fail (GTK_IS_MENU_ITEM (widget));
672 container = GTK_CONTAINER (widget);
673 menu_item = GTK_MENU_ITEM (widget);
675 /* Reverse order of gtk_menu_item_show_all */
676 gtk_widget_hide (widget);
677 if (menu_item->submenu)
678 gtk_widget_hide_all (menu_item->submenu);
679 gtk_container_foreach (container, (GtkCallback) gtk_widget_hide_all, NULL);