/* GTK - The GIMP Toolkit * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public * License along with this library; if not, write to the Free * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include "gtkmenu.h" #include "gtkmenuitem.h" #include "gtkoptionmenu.h" #include "gtksignal.h" #define CHILD_LEFT_SPACING 5 #define CHILD_RIGHT_SPACING 1 #define CHILD_TOP_SPACING 1 #define CHILD_BOTTOM_SPACING 1 #define OPTION_INDICATOR_WIDTH 12 #define OPTION_INDICATOR_HEIGHT 8 #define OPTION_INDICATOR_SPACING 2 static void gtk_option_menu_class_init (GtkOptionMenuClass *klass); static void gtk_option_menu_init (GtkOptionMenu *option_menu); static void gtk_option_menu_destroy (GtkObject *object); static void gtk_option_menu_size_request (GtkWidget *widget, GtkRequisition *requisition); static void gtk_option_menu_size_allocate (GtkWidget *widget, GtkAllocation *allocation); static void gtk_option_menu_paint (GtkWidget *widget, GdkRectangle *area); static void gtk_option_menu_draw (GtkWidget *widget, GdkRectangle *area); static gint gtk_option_menu_expose (GtkWidget *widget, GdkEventExpose *event); static gint gtk_option_menu_button_press (GtkWidget *widget, GdkEventButton *event); static void gtk_option_menu_deactivate (GtkMenuShell *menu_shell, GtkOptionMenu *option_menu); static void gtk_option_menu_update_contents (GtkOptionMenu *option_menu); static void gtk_option_menu_remove_contents (GtkOptionMenu *option_menu); static void gtk_option_menu_calc_size (GtkOptionMenu *option_menu); static void gtk_option_menu_position (GtkMenu *menu, gint *x, gint *y, gpointer user_data); static GtkButtonClass *parent_class = NULL; guint gtk_option_menu_get_type () { static guint option_menu_type = 0; if (!option_menu_type) { GtkTypeInfo option_menu_info = { "GtkOptionMenu", sizeof (GtkOptionMenu), sizeof (GtkOptionMenuClass), (GtkClassInitFunc) gtk_option_menu_class_init, (GtkObjectInitFunc) gtk_option_menu_init, (GtkArgFunc) NULL, }; option_menu_type = gtk_type_unique (gtk_button_get_type (), &option_menu_info); } return option_menu_type; } static void gtk_option_menu_class_init (GtkOptionMenuClass *class) { GtkObjectClass *object_class; GtkWidgetClass *widget_class; GtkButtonClass *button_class; object_class = (GtkObjectClass*) class; widget_class = (GtkWidgetClass*) class; button_class = (GtkButtonClass*) class; parent_class = gtk_type_class (gtk_button_get_type ()); object_class->destroy = gtk_option_menu_destroy; widget_class->draw = gtk_option_menu_draw; widget_class->draw_focus = NULL; widget_class->size_request = gtk_option_menu_size_request; widget_class->size_allocate = gtk_option_menu_size_allocate; widget_class->expose_event = gtk_option_menu_expose; widget_class->button_press_event = gtk_option_menu_button_press; } static void gtk_option_menu_init (GtkOptionMenu *option_menu) { GTK_WIDGET_UNSET_FLAGS (option_menu, GTK_CAN_FOCUS); option_menu->menu = NULL; option_menu->menu_item = NULL; option_menu->width = 0; option_menu->height = 0; } GtkWidget* gtk_option_menu_new () { return GTK_WIDGET (gtk_type_new (gtk_option_menu_get_type ())); } GtkWidget* gtk_option_menu_get_menu (GtkOptionMenu *option_menu) { g_return_val_if_fail (option_menu != NULL, NULL); g_return_val_if_fail (GTK_IS_OPTION_MENU (option_menu), NULL); return option_menu->menu; } void gtk_option_menu_set_menu (GtkOptionMenu *option_menu, GtkWidget *menu) { g_return_if_fail (option_menu != NULL); g_return_if_fail (GTK_IS_OPTION_MENU (option_menu)); g_return_if_fail (menu != NULL); g_return_if_fail (GTK_IS_MENU (menu)); gtk_option_menu_remove_menu (option_menu); option_menu->menu = menu; gtk_object_ref (GTK_OBJECT (option_menu->menu)); gtk_option_menu_calc_size (option_menu); gtk_signal_connect (GTK_OBJECT (option_menu->menu), "deactivate", (GtkSignalFunc) gtk_option_menu_deactivate, option_menu); if (GTK_WIDGET (option_menu)->parent) gtk_widget_queue_resize (GTK_WIDGET (option_menu)); gtk_option_menu_update_contents (option_menu); } void gtk_option_menu_remove_menu (GtkOptionMenu *option_menu) { g_return_if_fail (option_menu != NULL); g_return_if_fail (GTK_IS_OPTION_MENU (option_menu)); if (option_menu->menu) { gtk_option_menu_remove_contents (option_menu); gtk_signal_disconnect_by_data (GTK_OBJECT (option_menu->menu), option_menu); gtk_object_unref (GTK_OBJECT (option_menu->menu)); option_menu->menu = NULL; } } void gtk_option_menu_set_history (GtkOptionMenu *option_menu, gint index) { GtkWidget *menu_item; g_return_if_fail (option_menu != NULL); g_return_if_fail (GTK_IS_OPTION_MENU (option_menu)); if (option_menu->menu) { gtk_menu_set_active (GTK_MENU (option_menu->menu), index); menu_item = gtk_menu_get_active (GTK_MENU (option_menu->menu)); if (menu_item != option_menu->menu_item) { gtk_option_menu_remove_contents (option_menu); gtk_option_menu_update_contents (option_menu); } } } static void gtk_option_menu_destroy (GtkObject *object) { GtkOptionMenu *option_menu; g_return_if_fail (object != NULL); g_return_if_fail (GTK_IS_OPTION_MENU (object)); option_menu = GTK_OPTION_MENU (object); gtk_option_menu_remove_contents (option_menu); if (option_menu->menu) { gtk_object_unref (GTK_OBJECT (option_menu->menu)); gtk_widget_destroy (option_menu->menu); } if (GTK_OBJECT_CLASS (parent_class)->destroy) (* GTK_OBJECT_CLASS (parent_class)->destroy) (object); } static void gtk_option_menu_size_request (GtkWidget *widget, GtkRequisition *requisition) { GtkOptionMenu *option_menu; gint tmp; g_return_if_fail (widget != NULL); g_return_if_fail (GTK_IS_OPTION_MENU (widget)); g_return_if_fail (requisition != NULL); option_menu = GTK_OPTION_MENU (widget); requisition->width = ((GTK_CONTAINER (widget)->border_width + GTK_WIDGET (widget)->style->klass->xthickness) * 2 + option_menu->width + OPTION_INDICATOR_WIDTH + OPTION_INDICATOR_SPACING * 5 + CHILD_LEFT_SPACING + CHILD_RIGHT_SPACING); requisition->height = ((GTK_CONTAINER (widget)->border_width + GTK_WIDGET (widget)->style->klass->ythickness) * 2 + option_menu->height + CHILD_TOP_SPACING + CHILD_BOTTOM_SPACING); tmp = (requisition->height - option_menu->height + OPTION_INDICATOR_HEIGHT + OPTION_INDICATOR_SPACING * 2); requisition->height = MAX (requisition->height, tmp); } static void gtk_option_menu_size_allocate (GtkWidget *widget, GtkAllocation *allocation) { GtkWidget *child; GtkAllocation child_allocation; g_return_if_fail (widget != NULL); g_return_if_fail (GTK_IS_OPTION_MENU (widget)); g_return_if_fail (allocation != NULL); widget->allocation = *allocation; if (GTK_WIDGET_REALIZED (widget)) gdk_window_move_resize (widget->window, allocation->x, allocation->y, allocation->width, allocation->height); child = GTK_BUTTON (widget)->child; if (child && GTK_WIDGET_VISIBLE (child)) { child_allocation.x = (GTK_CONTAINER (widget)->border_width + GTK_WIDGET (widget)->style->klass->xthickness); child_allocation.y = (GTK_CONTAINER (widget)->border_width + GTK_WIDGET (widget)->style->klass->ythickness); child_allocation.width = (allocation->width - child_allocation.x * 2 - OPTION_INDICATOR_WIDTH - OPTION_INDICATOR_SPACING * 5 - CHILD_LEFT_SPACING - CHILD_RIGHT_SPACING); child_allocation.height = (allocation->height - child_allocation.y * 2 - CHILD_TOP_SPACING - CHILD_BOTTOM_SPACING); child_allocation.x += CHILD_LEFT_SPACING; child_allocation.y += CHILD_RIGHT_SPACING; gtk_widget_size_allocate (child, &child_allocation); } } static void gtk_option_menu_paint (GtkWidget *widget, GdkRectangle *area) { GdkRectangle restrict_area; GdkRectangle new_area; g_return_if_fail (widget != NULL); g_return_if_fail (GTK_IS_OPTION_MENU (widget)); g_return_if_fail (area != NULL); if (GTK_WIDGET_DRAWABLE (widget)) { restrict_area.x = GTK_CONTAINER (widget)->border_width; restrict_area.y = GTK_CONTAINER (widget)->border_width; restrict_area.width = widget->allocation.width - restrict_area.x * 2; restrict_area.height = widget->allocation.height - restrict_area.y * 2; if (gdk_rectangle_intersect (area, &restrict_area, &new_area)) { gtk_style_set_background (widget->style, widget->window, GTK_WIDGET_STATE (widget)); gdk_window_clear_area (widget->window, new_area.x, new_area.y, new_area.width, new_area.height); gtk_draw_shadow (widget->style, widget->window, GTK_WIDGET_STATE (widget), GTK_SHADOW_OUT, restrict_area.x, restrict_area.y, restrict_area.width, restrict_area.height); gtk_draw_shadow (widget->style, widget->window, GTK_WIDGET_STATE (widget), GTK_SHADOW_OUT, restrict_area.x + restrict_area.width - restrict_area.x - OPTION_INDICATOR_WIDTH - OPTION_INDICATOR_SPACING * 4, restrict_area.y + (restrict_area.height - OPTION_INDICATOR_HEIGHT) / 2, OPTION_INDICATOR_WIDTH, OPTION_INDICATOR_HEIGHT); } } } static void gtk_option_menu_draw (GtkWidget *widget, GdkRectangle *area) { GtkWidget *child; GdkRectangle child_area; g_return_if_fail (widget != NULL); g_return_if_fail (GTK_IS_OPTION_MENU (widget)); g_return_if_fail (area != NULL); if (GTK_WIDGET_DRAWABLE (widget)) { gtk_option_menu_paint (widget, area); child = GTK_BUTTON (widget)->child; if (child && gtk_widget_intersect (child, area, &child_area)) gtk_widget_draw (child, &child_area); } } static gint gtk_option_menu_expose (GtkWidget *widget, GdkEventExpose *event) { GtkWidget *child; GdkEventExpose child_event; gint remove_child; g_return_val_if_fail (widget != NULL, FALSE); g_return_val_if_fail (GTK_IS_OPTION_MENU (widget), FALSE); g_return_val_if_fail (event != NULL, FALSE); if (GTK_WIDGET_DRAWABLE (widget)) { gtk_option_menu_paint (widget, &event->area); remove_child = FALSE; child = GTK_BUTTON (widget)->child; if (!child) { if (!GTK_OPTION_MENU (widget)->menu) return FALSE; gtk_option_menu_update_contents (GTK_OPTION_MENU (widget)); child = GTK_BUTTON (widget)->child; if (!child) return FALSE; remove_child = TRUE; } child_event = *event; if (GTK_WIDGET_NO_WINDOW (child) && gtk_widget_intersect (child, &event->area, &child_event.area)) gtk_widget_event (child, (GdkEvent*) &child_event); if (remove_child) gtk_option_menu_remove_contents (GTK_OPTION_MENU (widget)); } return FALSE; } static gint gtk_option_menu_button_press (GtkWidget *widget, GdkEventButton *event) { GtkOptionMenu *option_menu; g_return_val_if_fail (widget != NULL, FALSE); g_return_val_if_fail (GTK_IS_OPTION_MENU (widget), FALSE); g_return_val_if_fail (event != NULL, FALSE); if ((event->type == GDK_BUTTON_PRESS) && (event->button == 1)) { option_menu = GTK_OPTION_MENU (widget); gtk_option_menu_remove_contents (option_menu); gtk_menu_popup (GTK_MENU (option_menu->menu), NULL, NULL, gtk_option_menu_position, option_menu, event->button, event->time); } return FALSE; } static void gtk_option_menu_deactivate (GtkMenuShell *menu_shell, GtkOptionMenu *option_menu) { g_return_if_fail (menu_shell != NULL); g_return_if_fail (option_menu != NULL); g_return_if_fail (GTK_IS_OPTION_MENU (option_menu)); gtk_option_menu_update_contents (option_menu); } static void gtk_option_menu_update_contents (GtkOptionMenu *option_menu) { GtkWidget *child; g_return_if_fail (option_menu != NULL); g_return_if_fail (GTK_IS_OPTION_MENU (option_menu)); if (option_menu->menu) { gtk_option_menu_remove_contents (option_menu); option_menu->menu_item = gtk_menu_get_active (GTK_MENU (option_menu->menu)); if (option_menu->menu_item) { child = GTK_BIN (option_menu->menu_item)->child; if (child) { gtk_container_block_resize (GTK_CONTAINER (option_menu)); if (GTK_WIDGET (option_menu)->state != child->state) gtk_widget_set_state (child, GTK_WIDGET (option_menu)->state); gtk_widget_reparent (child, GTK_WIDGET (option_menu)); gtk_container_unblock_resize (GTK_CONTAINER (option_menu)); } gtk_widget_size_request (child, &child->requisition); gtk_widget_size_allocate (GTK_WIDGET (option_menu), &(GTK_WIDGET (option_menu)->allocation)); if (GTK_WIDGET_DRAWABLE (option_menu)) gtk_widget_queue_draw (GTK_WIDGET (option_menu)); } } } static void gtk_option_menu_remove_contents (GtkOptionMenu *option_menu) { g_return_if_fail (option_menu != NULL); g_return_if_fail (GTK_IS_OPTION_MENU (option_menu)); if (GTK_BUTTON (option_menu)->child) { gtk_container_block_resize (GTK_CONTAINER (option_menu)); if (GTK_WIDGET (option_menu->menu_item)->state != GTK_BUTTON (option_menu)->child->state) gtk_widget_set_state (GTK_BUTTON (option_menu)->child, GTK_WIDGET (option_menu->menu_item)->state); GTK_WIDGET_UNSET_FLAGS (GTK_BUTTON (option_menu)->child, GTK_MAPPED | GTK_REALIZED); gtk_widget_reparent (GTK_BUTTON (option_menu)->child, option_menu->menu_item); gtk_container_unblock_resize (GTK_CONTAINER (option_menu)); option_menu->menu_item = NULL; } } static void gtk_option_menu_calc_size (GtkOptionMenu *option_menu) { GtkWidget *child; GList *children; g_return_if_fail (option_menu != NULL); g_return_if_fail (GTK_IS_OPTION_MENU (option_menu)); option_menu->width = 0; option_menu->height = 0; if (option_menu->menu) { children = GTK_MENU_SHELL (option_menu->menu)->children; while (children) { child = children->data; children = children->next; if (GTK_WIDGET_VISIBLE (child)) { gtk_widget_size_request (child, &child->requisition); option_menu->width = MAX (option_menu->width, child->requisition.width); option_menu->height = MAX (option_menu->height, child->requisition.height); } } } } static void gtk_option_menu_position (GtkMenu *menu, gint *x, gint *y, gpointer user_data) { GtkOptionMenu *option_menu; GtkWidget *active; GtkWidget *child; GList *children; gint shift_menu; gint screen_width; gint screen_height; gint menu_xpos; gint menu_ypos; gint width; gint height; g_return_if_fail (user_data != NULL); g_return_if_fail (GTK_IS_OPTION_MENU (user_data)); option_menu = GTK_OPTION_MENU (user_data); width = GTK_WIDGET (menu)->allocation.width; height = GTK_WIDGET (menu)->allocation.height; active = gtk_menu_get_active (GTK_MENU (option_menu->menu)); children = GTK_MENU_SHELL (option_menu->menu)->children; gdk_window_get_origin (GTK_WIDGET (option_menu)->window, &menu_xpos, &menu_ypos); menu_ypos += GTK_WIDGET (option_menu)->allocation.height / 2 - 2; if (active != NULL) menu_ypos -= active->requisition.height / 2; while (children) { child = children->data; if (active == child) break; menu_ypos -= child->allocation.height; children = children->next; } screen_width = gdk_screen_width (); screen_height = gdk_screen_height (); shift_menu = FALSE; if (menu_ypos < 0) { menu_ypos = 0; shift_menu = TRUE; } else if ((menu_ypos + height) > screen_height) { menu_ypos -= ((menu_ypos + height) - screen_height); shift_menu = TRUE; } if (shift_menu) { if ((menu_xpos + GTK_WIDGET (option_menu)->allocation.width + width) <= screen_width) menu_xpos += GTK_WIDGET (option_menu)->allocation.width; else menu_xpos -= width; } if (menu_xpos < 0) menu_xpos = 0; else if ((menu_xpos + width) > screen_width) menu_xpos -= ((menu_xpos + width) - screen_width); *x = menu_xpos; *y = menu_ypos; }