diff options
Diffstat (limited to 'lib/egg/eggtoolbar.c')
-rw-r--r-- | lib/egg/eggtoolbar.c | 344 |
1 files changed, 305 insertions, 39 deletions
diff --git a/lib/egg/eggtoolbar.c b/lib/egg/eggtoolbar.c index 7c1d8c1b5..b4cdbc62e 100644 --- a/lib/egg/eggtoolbar.c +++ b/lib/egg/eggtoolbar.c @@ -32,6 +32,10 @@ #include <gtk/gtkmenu.h> #include <gtk/gtkradiobutton.h> #include <gtk/gtktoolbar.h> +#include <gtk/gtkbindings.h> +#include <gdk/gdkkeysyms.h> +#include "eggmarshalers.h" +#include <gtk/gtkmain.h> #define DEFAULT_IPADDING 0 #define DEFAULT_SPACE_SIZE 5 @@ -61,9 +65,19 @@ enum { }; enum { + CHILD_PROP_0, + CHILD_PROP_EXPAND, + CHILD_PROP_HOMOGENEOUS, + CHILD_PROP_PACK_END, +}; + +enum { ORIENTATION_CHANGED, STYLE_CHANGED, POPUP_CONTEXT_MENU, + MOVE_FOCUS, + FOCUS_HOME, + FOCUS_END, LAST_SIGNAL }; @@ -91,7 +105,6 @@ static void egg_toolbar_style_set (GtkWidget *widget, GtkStyle *prev_style); static void egg_toolbar_direction_changed (GtkWidget *widget, GtkTextDirection previous_direction); - static gboolean egg_toolbar_focus (GtkWidget *widget, GtkDirectionType dir); static void egg_toolbar_screen_changed (GtkWidget *widget, @@ -116,18 +129,24 @@ static void egg_toolbar_forall (GtkContainer *container, gpointer callback_data); static GType egg_toolbar_child_type (GtkContainer *container); - static void egg_toolbar_real_orientation_changed (EggToolbar *toolbar, GtkOrientation orientation); static void egg_toolbar_real_style_changed (EggToolbar *toolbar, GtkToolbarStyle style); +static gboolean egg_toolbar_move_focus (EggToolbar *toolbar, + GtkDirectionType dir); +static gboolean egg_toolbar_focus_home (EggToolbar *toolbar); +static gboolean egg_toolbar_focus_end (EggToolbar *toolbar); + static gboolean egg_toolbar_button_press (GtkWidget *button, GdkEventButton *event, EggToolbar *toolbar); -static void egg_toolbar_arrow_button_press (GtkWidget *button, +static gboolean egg_toolbar_arrow_button_press (GtkWidget *button, GdkEventButton *event, EggToolbar *toolbar); +static void egg_toolbar_arrow_button_clicked (GtkWidget *button, + EggToolbar *toolbar); static void egg_toolbar_update_button_relief (EggToolbar *toolbar); static GtkReliefStyle get_button_relief (EggToolbar *toolbar); static gint get_space_size (EggToolbar *toolbar); @@ -165,6 +184,7 @@ typedef struct gint drop_index; GdkWindow *drag_highlight; + GtkMenu *menu; } EggToolbarPrivate; static GtkContainerClass *parent_class = NULL; @@ -199,11 +219,27 @@ egg_toolbar_get_type (void) } static void +add_arrow_bindings (GtkBindingSet *binding_set, + guint keysym, + GtkDirectionType dir) +{ + guint keypad_keysym = keysym - GDK_Left + GDK_KP_Left; + + gtk_binding_entry_add_signal (binding_set, keysym, 0, + "move_focus", 1, + GTK_TYPE_DIRECTION_TYPE, dir); + gtk_binding_entry_add_signal (binding_set, keypad_keysym, 0, + "move_focus", 1, + GTK_TYPE_DIRECTION_TYPE, dir); +} + +static void egg_toolbar_class_init (EggToolbarClass *klass) { GObjectClass *gobject_class; GtkWidgetClass *widget_class; GtkContainerClass *container_class; + GtkBindingSet *binding_set; parent_class = g_type_class_peek_parent (klass); @@ -234,7 +270,10 @@ egg_toolbar_class_init (EggToolbarClass *klass) klass->orientation_changed = egg_toolbar_real_orientation_changed; klass->style_changed = egg_toolbar_real_style_changed; - + klass->move_focus = egg_toolbar_move_focus; + klass->focus_home = egg_toolbar_focus_home; + klass->focus_end = egg_toolbar_focus_end; + toolbar_signals[ORIENTATION_CHANGED] = g_signal_new ("orientation_changed", G_OBJECT_CLASS_TYPE (klass), @@ -253,7 +292,6 @@ egg_toolbar_class_init (EggToolbarClass *klass) g_cclosure_marshal_VOID__ENUM, G_TYPE_NONE, 1, GTK_TYPE_TOOLBAR_STYLE); - toolbar_signals[POPUP_CONTEXT_MENU] = g_signal_new ("popup_context_menu", G_OBJECT_CLASS_TYPE (klass), @@ -262,7 +300,33 @@ egg_toolbar_class_init (EggToolbarClass *klass) NULL, NULL, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0); - + toolbar_signals[MOVE_FOCUS] = + g_signal_new ("move_focus", + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, + G_STRUCT_OFFSET (EggToolbarClass, move_focus), + NULL, NULL, + _egg_marshal_BOOLEAN__ENUM, + G_TYPE_BOOLEAN, 1, + GTK_TYPE_DIRECTION_TYPE); + toolbar_signals[FOCUS_HOME] = + g_signal_new ("focus_home", + G_OBJECT_CLASS_TYPE (klass), + G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, + G_STRUCT_OFFSET (EggToolbarClass, focus_home), + NULL, NULL, + _egg_marshal_BOOLEAN__VOID, + G_TYPE_BOOLEAN, 0); + toolbar_signals[FOCUS_END] = + g_signal_new ("focus_end", + G_OBJECT_CLASS_TYPE (klass), + G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, + G_STRUCT_OFFSET (EggToolbarClass, focus_end), + NULL, NULL, + _egg_marshal_BOOLEAN__VOID, + G_TYPE_BOOLEAN, 0); + + /* properties */ g_object_class_install_property (gobject_class, PROP_ORIENTATION, g_param_spec_enum ("orientation", @@ -288,6 +352,34 @@ egg_toolbar_class_init (EggToolbarClass *klass) FALSE, G_PARAM_READWRITE)); +#if 0 + /* child properties */ + gtk_container_class_install_child_property (container_class, + CHILD_PROP_EXPAND, + g_param_spec_boolean ("expand", + _("Expand"), + _("Whether the item should receive extra space when the toolbar grows"), + TRUE, + G_PARAM_READWRITE)); + + gtk_container_class_install_child_property (container_class, + CHILD_PROP_HOMOGENEOUS, + g_param_spec_boolean ("homogeneous", + _("Homogeneous"), + _("Whether the item should be the same size as other homogeneous items"), + TRUE, + G_PARAM_READWRITE)); + + gtk_container_class_install_child_property (container_class, + CHILD_PROP_PACK_END, + g_param_spec_uint ("pack_end", + _("Pack End"), + _("Whether the item is positioned at the end of the toolbar"), + 0, G_MAXINT, 0, + G_PARAM_READWRITE)); +#endif + + /* style properties */ gtk_widget_class_install_style_property (widget_class, g_param_spec_int ("space_size", _("Spacer size"), @@ -342,6 +434,22 @@ egg_toolbar_class_init (EggToolbarClass *klass) GTK_TYPE_ICON_SIZE, DEFAULT_ICON_SIZE, G_PARAM_READWRITE)); + + binding_set = gtk_binding_set_by_class (klass); + + add_arrow_bindings (binding_set, GDK_Left, GTK_DIR_LEFT); + add_arrow_bindings (binding_set, GDK_Right, GTK_DIR_RIGHT); + add_arrow_bindings (binding_set, GDK_Up, GTK_DIR_UP); + add_arrow_bindings (binding_set, GDK_Down, GTK_DIR_DOWN); + + gtk_binding_entry_add_signal (binding_set, GDK_KP_Home, 0, + "focus_home", 0); + gtk_binding_entry_add_signal (binding_set, GDK_Home, 0, + "focus_home", 0); + gtk_binding_entry_add_signal (binding_set, GDK_KP_End, 0, + "focus_end", 0); + gtk_binding_entry_add_signal (binding_set, GDK_End, 0, + "focus_end", 0); } static void @@ -363,9 +471,15 @@ egg_toolbar_init (EggToolbar *toolbar) priv->button = gtk_toggle_button_new (); g_signal_connect (priv->button, "button_press_event", G_CALLBACK (egg_toolbar_arrow_button_press), toolbar); + g_signal_connect_after (priv->button, "clicked", + G_CALLBACK (egg_toolbar_arrow_button_clicked), toolbar); gtk_button_set_relief (GTK_BUTTON (priv->button), get_button_relief (toolbar)); - GTK_WIDGET_UNSET_FLAGS (priv->button, GTK_CAN_FOCUS); + +#if 0 + /* FIXME: enable this when we can depend on gtk+ 2.3.0 */ + gtk_button_set_focus_on_click (GTK_BUTTON (priv->button), FALSE); +#endif priv->arrow = gtk_arrow_new (GTK_ARROW_DOWN, GTK_SHADOW_NONE); gtk_widget_show (priv->arrow); @@ -379,6 +493,8 @@ egg_toolbar_init (EggToolbar *toolbar) /* which child position a drop will occur at */ priv->drop_index = -1; priv->drag_highlight = NULL; + + priv->menu = NULL; } static void @@ -1083,7 +1199,7 @@ egg_toolbar_style_set (GtkWidget *widget, GtkStyle *prev_style) { if (GTK_WIDGET_REALIZED (widget)) - gtk_style_set_background (widget->style, widget->window, widget->state); + gtk_style_set_background (widget->style, widget->window, widget->state); if (prev_style) egg_toolbar_update_button_relief (EGG_TOOLBAR (widget)); @@ -1107,13 +1223,136 @@ egg_toolbar_direction_changed (GtkWidget *widget, GTK_WIDGET_CLASS (parent_class)->direction_changed (widget, previous_dir); } +static GList * +egg_toolbar_list_items_in_focus_order (EggToolbar *toolbar, + GtkDirectionType dir) +{ + EggToolbarPrivate *priv = EGG_TOOLBAR_GET_PRIVATE (toolbar); + GList *result = NULL; + GList *list; + + for (list = priv->items; list != NULL; list = list->next) + { + EggToolItem *item = list->data; + if (!item->pack_end) + result = g_list_prepend (result, item); + } + + for (list = priv->items; list != NULL; list = list->next) + { + EggToolItem *item = list->data; + + if (item->pack_end) + result = g_list_prepend (result, item); + } + + result = g_list_prepend (result, priv->button); + + if (dir == GTK_DIR_RIGHT || dir == GTK_DIR_DOWN || dir == GTK_DIR_TAB_FORWARD) + result = g_list_reverse (result); + + if (gtk_widget_get_direction (GTK_WIDGET (toolbar)) == GTK_TEXT_DIR_RTL) + result = g_list_reverse (result); + + return result; +} + +static gboolean +egg_toolbar_move_focus (EggToolbar *toolbar, + GtkDirectionType dir) +{ + GList *list; + gboolean retval = FALSE; + gboolean try_focus = FALSE; + GList *items = egg_toolbar_list_items_in_focus_order (toolbar, dir); + + for (list = items; list != NULL; list = list->next) + { + GtkWidget *tool_item = list->data; + + if (try_focus && gtk_widget_child_focus (tool_item, dir)) + { + retval = TRUE; + break; + } + + if (tool_item == GTK_CONTAINER (toolbar)->focus_child) + try_focus = TRUE; + } + + g_list_free (items); + return retval; +} + +static gboolean +egg_toolbar_focus_home (EggToolbar *toolbar) +{ + GList *items, *list; + GtkTextDirection direction = gtk_widget_get_direction (GTK_WIDGET (toolbar)); + + if (direction == GTK_TEXT_DIR_RTL) + items = egg_toolbar_list_items_in_focus_order (toolbar, GTK_DIR_LEFT); + else + items = egg_toolbar_list_items_in_focus_order (toolbar, GTK_DIR_RIGHT); + + for (list = items; list != NULL; list = list->next) + { + if (GTK_CONTAINER (toolbar)->focus_child == list->data) + break; + + if (gtk_widget_child_focus (list->data, GTK_DIR_RIGHT)) + break; + } + + g_list_free (items); + + return TRUE; +} + +static gboolean +egg_toolbar_focus_end (EggToolbar *toolbar) +{ + GList *items, *list; + GtkTextDirection direction = gtk_widget_get_direction (GTK_WIDGET (toolbar)); + + if (direction == GTK_TEXT_DIR_RTL) + items = egg_toolbar_list_items_in_focus_order (toolbar, GTK_DIR_RIGHT); + else + items = egg_toolbar_list_items_in_focus_order (toolbar, GTK_DIR_LEFT); + + for (list = items; list != NULL; list = list->next) + { + if (GTK_CONTAINER (toolbar)->focus_child == list->data) + break; + + if (gtk_widget_child_focus (list->data, GTK_DIR_RIGHT)) + break; + } + + g_list_free (items); + + return TRUE; +} + static gboolean egg_toolbar_focus (GtkWidget *widget, GtkDirectionType dir) { - /* Focus can't go in toolbars */ + EggToolbar *toolbar = EGG_TOOLBAR (widget); + GList *items; + gboolean retval = FALSE; + + if (GTK_CONTAINER (widget)->focus_child) + return FALSE; + + items = egg_toolbar_list_items_in_focus_order (toolbar, dir); + + if (items) + retval = gtk_widget_child_focus (items->data, dir); + + g_list_free (items); - return FALSE; + return retval; } static void @@ -1367,8 +1606,8 @@ egg_toolbar_add (GtkContainer *container, g_return_if_fail (EGG_IS_TOOLBAR (container)); g_return_if_fail (EGG_IS_TOOL_ITEM (widget)); - egg_toolbar_append_tool_item (EGG_TOOLBAR (container), - EGG_TOOL_ITEM (widget)); + egg_toolbar_append (EGG_TOOLBAR (container), + EGG_TOOL_ITEM (widget)); } static void @@ -1514,25 +1753,25 @@ menu_position_func (GtkMenu *menu, } static void -menu_deactivated (GtkWidget *menu, GtkWidget *button) +menu_deactivated (GtkWidget *menu, EggToolbar *toolbar) { - gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), FALSE); + EggToolbarPrivate *priv = EGG_TOOLBAR_GET_PRIVATE (toolbar); + + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (priv->button), FALSE); } static void -egg_toolbar_arrow_button_press (GtkWidget *button, - GdkEventButton *event, - EggToolbar *toolbar) +show_menu (EggToolbar *toolbar, GdkEventButton *event) { - EggToolbarPrivate *priv = EGG_TOOLBAR_GET_PRIVATE (toolbar); - GtkWidget *menu; - GtkWidget *menu_item; + EggToolbarPrivate *priv = EGG_TOOLBAR_GET_PRIVATE (toolbar); GList *items; + GtkWidget *menu_item; - gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE); - - menu = gtk_menu_new (); - g_signal_connect (menu, "deactivate", G_CALLBACK (menu_deactivated), button); + if (priv->menu) + gtk_widget_destroy (GTK_WIDGET (priv->menu)); + + priv->menu = GTK_MENU (gtk_menu_new ()); + g_signal_connect (priv->menu, "deactivate", G_CALLBACK (menu_deactivated), toolbar); items = priv->first_non_fitting_item; while (items) @@ -1545,18 +1784,44 @@ egg_toolbar_arrow_button_press (GtkWidget *button, g_signal_emit_by_name (item, "create_menu_proxy", &menu_item); if (menu_item) - { - gtk_menu_shell_append (GTK_MENU_SHELL (menu), menu_item); - } + gtk_menu_shell_append (GTK_MENU_SHELL (priv->menu), menu_item); } items = items->next; } - gtk_widget_show_all (menu); + gtk_widget_show_all (GTK_WIDGET (priv->menu)); - gtk_menu_popup (GTK_MENU (menu), NULL, NULL, + gtk_menu_popup (GTK_MENU (priv->menu), NULL, NULL, menu_position_func, toolbar, - event->button, event->time); + event? event->button : 0, event? event->time : gtk_get_current_event_time()); +} + +static void +egg_toolbar_arrow_button_clicked (GtkWidget *button, EggToolbar *toolbar) +{ + EggToolbarPrivate *priv = EGG_TOOLBAR_GET_PRIVATE (toolbar); + + /* We only get here when the button is clicked with the keybaord, + * because we block mouse button presses by returning TRUE from + * egg_toolbar_arrow_button_press + */ + if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (priv->button))) + { + show_menu (toolbar, NULL); + gtk_menu_shell_select_first (GTK_MENU_SHELL (priv->menu), FALSE); + } +} + +static gboolean +egg_toolbar_arrow_button_press (GtkWidget *button, + GdkEventButton *event, + EggToolbar *toolbar) +{ + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE); + + show_menu (toolbar, event); + + return TRUE; } static gboolean @@ -1645,13 +1910,13 @@ egg_toolbar_new (void) } void -egg_toolbar_append_tool_item (EggToolbar *toolbar, - EggToolItem *item) +egg_toolbar_append (EggToolbar *toolbar, + EggToolItem *item) { g_return_if_fail (EGG_IS_TOOLBAR (toolbar)); g_return_if_fail (EGG_IS_TOOL_ITEM (item)); - egg_toolbar_insert_tool_item (toolbar, item, toolbar->num_children); + egg_toolbar_insert (toolbar, item, toolbar->num_children); } void @@ -1661,7 +1926,7 @@ egg_toolbar_prepend_tool_item (EggToolbar *toolbar, g_return_if_fail (EGG_IS_TOOLBAR (toolbar)); g_return_if_fail (EGG_IS_TOOL_ITEM (item)); - egg_toolbar_insert_tool_item (toolbar, item, 0); + egg_toolbar_insert (toolbar, item, 0); } void @@ -1699,9 +1964,9 @@ egg_toolbar_remove_tool_item (EggToolbar *toolbar, } void -egg_toolbar_insert_tool_item (EggToolbar *toolbar, - EggToolItem *item, - gint pos) +egg_toolbar_insert (EggToolbar *toolbar, + EggToolItem *item, + gint pos) { EggToolbarPrivate *priv; @@ -1718,7 +1983,6 @@ egg_toolbar_insert_tool_item (EggToolbar *toolbar, egg_tool_item_set_relief_style (item, get_button_relief (toolbar)); gtk_widget_set_parent (GTK_WIDGET (item), GTK_WIDGET (toolbar)); - GTK_WIDGET_UNSET_FLAGS (item, GTK_CAN_FOCUS); } gint @@ -1869,6 +2133,8 @@ egg_toolbar_unset_icon_size (EggToolbar *toolbar) { GtkIconSize size; + g_return_if_fail (EGG_IS_TOOLBAR (toolbar)); + if (toolbar->icon_size_set) { GtkSettings *settings = toolbar_get_settings (toolbar); @@ -2219,7 +2485,7 @@ egg_toolbar_internal_insert_element (EggToolbar *toolbar, tooltip_text, tooltip_private_text); toolbar->children = g_list_insert (toolbar->children, child, position); - egg_toolbar_insert_tool_item (toolbar, item, position); + egg_toolbar_insert (toolbar, item, position); return child->widget; } |