/* * go-optionmenu.c * * Copyright (C) 2002 Andreas J. Guelzow * Copyright (C) 2006 Morten Welinder (terra@gnome.org) * * based extensively on: * * GTK - The GIMP Toolkit * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald * * Modified by the GTK+ Team and others 1997-2000. See the GTK AUTHORS * file for a list of people on the GTK+ Team. See the ChangeLog * files for a list of changes. These files are distributed with * GTK+ at ftp://ftp.gtk.org/pub/gtk/. * * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 * USA. */ #include "config.h" #include "go-optionmenu.h" #include #include enum { CHANGED, LAST_SIGNAL }; enum { PROP_0, PROP_MENU }; static GtkButtonClass *parent_class = NULL; static guint signals[LAST_SIGNAL] = { 0 }; GtkWidget* go_option_menu_new(void) { return g_object_new(GO_TYPE_OPTION_MENU, NULL); } static void go_option_menu_detacher(GtkWidget *widget, GtkMenu *menu) { #if 0 GOOptionMenu *option_menu = GO_OPTION_MENU (widget); /* What? */ #endif } static void go_option_menu_update_contents(GOOptionMenu *option_menu) { const char *text; GtkWidget *w; g_return_if_fail(GO_IS_OPTION_MENU(option_menu)); w = gtk_bin_get_child(GTK_BIN(option_menu->selected)); text = g_object_get_data(G_OBJECT(w), "option-menu-text"); if (!text && GTK_IS_LABEL(w)) text = gtk_label_get_text(GTK_LABEL(w)); if (!text) text = ""; #if 0 g_print ("text = \"%s\"\n", text); #endif gtk_label_set_text(option_menu->button_label, text); } static void go_option_menu_select_item(GOOptionMenu *option_menu, GtkMenuItem *item) { if (item == option_menu->selected) return; if (GTK_IS_CHECK_MENU_ITEM(option_menu->selected)) gtk_check_menu_item_set_active( GTK_CHECK_MENU_ITEM(option_menu->selected), FALSE); option_menu->selected = item; if (GTK_IS_CHECK_MENU_ITEM(item)) gtk_check_menu_item_set_active( GTK_CHECK_MENU_ITEM(option_menu->selected), TRUE); go_option_menu_update_contents(option_menu); } static void go_option_menu_position(GtkMenu *menu, gint *x, gint *y, gboolean *push_in, gpointer user_data) { GOOptionMenu *option_menu = user_data; GtkWidget *widget; GtkRequisition requisition; GList *children; gint screen_width; gint menu_xpos; gint menu_ypos; gint menu_width; GtkAllocation allocation; widget = GTK_WIDGET(option_menu); gtk_widget_get_child_requisition(GTK_WIDGET(menu), &requisition); menu_width = requisition.width; gdk_window_get_origin(gtk_widget_get_window(widget), &menu_xpos, &menu_ypos); gtk_widget_get_allocation(widget, &allocation); menu_xpos += allocation.x; menu_ypos += allocation.y + allocation.height / 2 - 2; children = gtk_container_get_children(GTK_CONTAINER(option_menu->menu)); while (children) { GtkWidget *child = children->data; if (GTK_IS_CHECK_MENU_ITEM(child) && gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(child))) { gtk_widget_get_child_requisition(child, &requisition); menu_ypos -= requisition.height / 2; break; } if (gtk_widget_get_visible(child)) { gtk_widget_get_child_requisition(child, &requisition); menu_ypos -= requisition.height; } children = children->next; } screen_width = gdk_screen_get_width(gtk_widget_get_screen(widget)); if (menu_xpos + menu_width > screen_width) menu_xpos -= (menu_xpos + menu_width) - screen_width; if (menu_xpos < 0) menu_xpos = 0; *x = menu_xpos; *y = menu_ypos; *push_in = TRUE; } static gint go_option_menu_button_press(GtkWidget *widget, GdkEventButton *event) { GOOptionMenu *option_menu; g_return_val_if_fail(GO_IS_OPTION_MENU(widget), FALSE); g_return_val_if_fail(event != NULL, FALSE); option_menu = GO_OPTION_MENU(widget); if (event->type == GDK_BUTTON_PRESS && event->button == 1) { gtk_menu_popup(GTK_MENU(option_menu->menu), NULL, NULL, go_option_menu_position, option_menu, event->button, event->time); return TRUE; } return FALSE; } static gint go_option_menu_key_press(GtkWidget *widget, GdkEventKey *event) { GOOptionMenu *option_menu = GO_OPTION_MENU(widget); switch (event->keyval) { case GDK_KEY_KP_Space: case GDK_KEY_space: gtk_menu_popup(GTK_MENU(option_menu->menu), NULL, NULL, go_option_menu_position, option_menu, 0, event->time); return TRUE; } return FALSE; } static void cb_select(GtkMenuItem *item, GOOptionMenu *option_menu) { go_option_menu_select_item(option_menu, item); g_signal_emit(option_menu, signals[CHANGED], 0); } static void handle_menu_signals(GOOptionMenu *option_menu, gboolean connect) { GList *children = gtk_container_get_children( GTK_CONTAINER(option_menu->menu)); while (children) { GtkWidget *child = children->data; children = g_list_remove(children, child); if (GTK_IS_MENU_ITEM(child)) { GtkWidget *sub = gtk_menu_item_get_submenu(GTK_MENU_ITEM(child)); if (sub) children = g_list_concat(children, gtk_container_get_children(GTK_CONTAINER(sub))); else if (connect) g_signal_connect(child, "activate", G_CALLBACK(cb_select), option_menu); else g_signal_handlers_disconnect_by_func(child, G_CALLBACK(cb_select), option_menu); } } } void go_option_menu_set_menu(GOOptionMenu *option_menu, GtkWidget *menu) { GtkMenuShell *shell; g_return_if_fail(GO_IS_OPTION_MENU(option_menu)); g_return_if_fail(GTK_IS_MENU_SHELL(menu)); shell = (GtkMenuShell *) menu; if (option_menu->menu == shell) return; if (option_menu->menu) { gtk_menu_shell_cancel(option_menu->menu); handle_menu_signals(option_menu, FALSE); gtk_menu_detach(GTK_MENU(option_menu->menu)); g_object_unref(option_menu->menu); } option_menu->menu = shell; if (shell) { g_object_ref(shell); gtk_menu_attach_to_widget(GTK_MENU(shell), GTK_WIDGET(option_menu), go_option_menu_detacher); handle_menu_signals(option_menu, TRUE); go_option_menu_select_item(option_menu, GTK_MENU_ITEM(gtk_menu_get_active(GTK_MENU(shell)))); } g_object_notify(G_OBJECT(option_menu), "menu"); } void go_option_menu_set_history(GOOptionMenu *option_menu, GSList *selection) { g_return_if_fail(selection != NULL); g_return_if_fail(GO_IS_OPTION_MENU(option_menu)); if (option_menu->menu) { GtkMenuShell *menu = option_menu->menu; while (1) { int n = GPOINTER_TO_INT(selection->data); GtkMenuItem *item = g_list_nth_data( gtk_container_get_children(GTK_CONTAINER(menu)), n); selection = selection->next; if (selection) menu = GTK_MENU_SHELL(gtk_menu_item_get_submenu(item)); else { go_option_menu_select_item(option_menu, item); break; } } } } /** * go_option_menu_get_history: * @option_menu: a #GOOptionMenu * * Retrieves the currently selected menu item. * * Return value: the selected menu_item **/ GtkWidget * go_option_menu_get_history(GOOptionMenu *option_menu) { return GTK_WIDGET(option_menu->selected); } static void go_option_menu_set_property(GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) { GOOptionMenu *option_menu = GO_OPTION_MENU(object); switch (prop_id) { case PROP_MENU: go_option_menu_set_menu(option_menu, g_value_get_object(value)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); break; } } static void go_option_menu_get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) { GOOptionMenu *option_menu = GO_OPTION_MENU(object); switch (prop_id) { case PROP_MENU: g_value_set_object(value, option_menu->menu); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); break; } } static void go_option_menu_destroy(GtkWidget *widget) { GOOptionMenu *option_menu; g_return_if_fail(GO_IS_OPTION_MENU(widget)); option_menu = GO_OPTION_MENU(widget); if (option_menu->menu) { gtk_widget_destroy(GTK_WIDGET(option_menu->menu)); g_object_unref(option_menu->menu); option_menu->menu = NULL; } option_menu->selected = NULL; GTK_WIDGET_CLASS(parent_class)->destroy(widget); } static void go_option_menu_class_init(GOOptionMenuClass *class) { GObjectClass *gobject_class = (GObjectClass*) class; GtkWidgetClass *widget_class = (GtkWidgetClass*) class; parent_class = g_type_class_peek_parent(class); signals[CHANGED] = g_signal_new("changed", G_OBJECT_CLASS_TYPE(class), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET(GOOptionMenuClass, changed), NULL, NULL, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0); gobject_class->set_property = go_option_menu_set_property; gobject_class->get_property = go_option_menu_get_property; widget_class->destroy = go_option_menu_destroy; widget_class->button_press_event = go_option_menu_button_press; widget_class->key_press_event = go_option_menu_key_press; g_object_class_install_property(gobject_class, PROP_MENU, g_param_spec_object("menu", _("Menu"), _("The menu of options"), GTK_TYPE_MENU, G_PARAM_READABLE | G_PARAM_WRITABLE)); } static void go_option_menu_init(GOOptionMenu *option_menu) { GtkBox *box; GtkWidget *arrow, *sep; gtk_widget_set_can_focus(GTK_WIDGET(option_menu), TRUE); gtk_widget_set_can_default(GTK_WIDGET(option_menu), FALSE); gtk_widget_set_receives_default(GTK_WIDGET(option_menu), FALSE); box = GTK_BOX(gtk_hbox_new(FALSE, FALSE)); option_menu->menu = NULL; option_menu->selected = NULL; option_menu->button_label = GTK_LABEL(gtk_label_new("")); gtk_box_pack_start(box, GTK_WIDGET(option_menu->button_label), FALSE, TRUE, 0); arrow = gtk_arrow_new(GTK_ARROW_DOWN, GTK_SHADOW_NONE); g_object_set(arrow, "xalign", 0.75, NULL); gtk_box_pack_end(box, arrow, FALSE, FALSE, 0); sep = gtk_vseparator_new(); gtk_box_pack_end(box, sep, FALSE, FALSE, 0); gtk_container_add(GTK_CONTAINER(option_menu), GTK_WIDGET(box)); } GType go_option_menu_get_type(void) { static GType option_menu_type = 0; if (!option_menu_type) { static const GTypeInfo option_menu_info = { sizeof(GOOptionMenuClass), NULL, /* base_init */ NULL, /* base_finalize */ (GClassInitFunc) go_option_menu_class_init, NULL, /* class_finalize */ NULL, /* class_data */ sizeof(GOOptionMenu), 0, /* n_preallocs */ (GInstanceInitFunc) go_option_menu_init, }; option_menu_type = g_type_register_static(GTK_TYPE_BUTTON, "GOOptionMenu", &option_menu_info, 0); } return option_menu_type; }