X-Git-Url: https://vcs.maemo.org/git/?a=blobdiff_plain;f=hildon%2Fhildon-app-menu.c;h=e7a769471923adb59bf3d3675d7c1ad416b2cd45;hb=293c5746d90f0f9e5282dce4f99e85cb91777178;hp=178586a84a56e5b3d9ce1d4d716f20c07a0245cf;hpb=54db71bf9767afda7ce277c5f97d14ebb9a6b5a3;p=hildon diff --git a/hildon/hildon-app-menu.c b/hildon/hildon-app-menu.c index 178586a..e7a7694 100644 --- a/hildon/hildon-app-menu.c +++ b/hildon/hildon-app-menu.c @@ -18,10 +18,10 @@ /** * SECTION:hildon-app-menu - * @short_description: Widget representing the application menu in the Hildon framework. + * @short_description: Application menu for Hildon applications. * - * The #HildonAppMenu is a GTK widget which represents an application - * menu in the Hildon framework. + * #HildonAppMenu is an application menu for applications in the Hildon + * framework. * * This menu opens from the top of the screen and contains a number of * entries (#GtkButton) organized in one or two columns, depending on @@ -29,8 +29,12 @@ * if the screen is resized). Entries are added left to right and top * to bottom. * - * Besides that, the #HildonAppMenu can contain a group of filter buttons - * (#GtkToggleButton or #GtkRadioButton). + * Besides that, #HildonAppMenu can contain a group of filter buttons + * (#GtkToggleButton or #GtkRadioButton). Filters are meant to change + * the way data is presented in the application, rather than change + * the layout of the menu itself. For example, a file manager can have + * filters to decide the order used to display a list of files (name, + * date, size, etc.). * * To use a #HildonAppMenu, add it to a #HildonWindow using * hildon_window_set_app_menu(). The menu will appear when the user @@ -100,9 +104,7 @@ #include "hildon-app-menu-private.h" #include "hildon-window.h" #include "hildon-banner.h" - -static GdkWindow * -grab_transfer_window_get (GtkWidget *widget); +#include "hildon-animation-actor.h" static void hildon_app_menu_repack_items (HildonAppMenu *menu, @@ -131,10 +133,22 @@ remove_item_from_list (GList **list, gpointer item); static void +emit_menu_changed (HildonAppMenu *menu, + gpointer item); + +static void hildon_app_menu_apply_style (GtkWidget *widget); G_DEFINE_TYPE (HildonAppMenu, hildon_app_menu, GTK_TYPE_WINDOW); +enum +{ + CHANGED, + LAST_SIGNAL +}; + +static guint app_menu_signals[LAST_SIGNAL] = { 0 }; + /** * hildon_app_menu_new: * @@ -178,10 +192,10 @@ hildon_app_menu_insert (HildonAppMenu *menu, HILDON_SIZE_FINGER_HEIGHT | HILDON_SIZE_AUTO_WIDTH); /* Add the item to the menu */ - gtk_widget_show (GTK_WIDGET (item)); g_object_ref_sink (item); priv->buttons = g_list_insert (priv->buttons, item, position); - hildon_app_menu_repack_items (menu, position); + if (GTK_WIDGET_VISIBLE (item)) + hildon_app_menu_repack_items (menu, position); /* Enable accelerators */ g_signal_connect (item, "can-activate-accel", G_CALLBACK (can_activate_accel), NULL); @@ -192,6 +206,9 @@ hildon_app_menu_insert (HildonAppMenu *menu, /* Remove item from list when it is destroyed */ g_object_weak_ref (G_OBJECT (item), (GWeakNotify) remove_item_from_list, &(priv->buttons)); + g_object_weak_ref (G_OBJECT (item), (GWeakNotify) emit_menu_changed, menu); + + g_signal_emit (menu, app_menu_signals[CHANGED], 0); } /** @@ -285,10 +302,10 @@ hildon_app_menu_add_filter (HildonAppMenu *menu, HILDON_SIZE_FINGER_HEIGHT | HILDON_SIZE_AUTO_WIDTH); /* Add the filter to the menu */ - gtk_widget_show (GTK_WIDGET (filter)); g_object_ref_sink (filter); priv->filters = g_list_append (priv->filters, filter); - hildon_app_menu_repack_filters (menu); + if (GTK_WIDGET_VISIBLE (filter)) + hildon_app_menu_repack_filters (menu); /* Enable accelerators */ g_signal_connect (filter, "can-activate-accel", G_CALLBACK (can_activate_accel), NULL); @@ -299,6 +316,9 @@ hildon_app_menu_add_filter (HildonAppMenu *menu, /* Remove filter from list when it is destroyed */ g_object_weak_ref (G_OBJECT (filter), (GWeakNotify) remove_item_from_list, &(priv->filters)); + g_object_weak_ref (G_OBJECT (filter), (GWeakNotify) emit_menu_changed, menu); + + g_signal_emit (menu, app_menu_signals[CHANGED], 0); } static void @@ -403,7 +423,9 @@ item_visibility_changed (GtkWidget *item, { HildonAppMenuPrivate *priv = HILDON_APP_MENU_GET_PRIVATE (menu); - hildon_app_menu_repack_items (menu, g_list_index (priv->buttons, item)); + if (! priv->inhibit_repack) + hildon_app_menu_repack_items (menu, g_list_index (priv->buttons, item)); + g_signal_emit (menu, app_menu_signals[CHANGED], 0); } static void @@ -411,7 +433,11 @@ filter_visibility_changed (GtkWidget *item, GParamSpec *arg1, HildonAppMenu *menu) { - hildon_app_menu_repack_filters (menu); + HildonAppMenuPrivate *priv = HILDON_APP_MENU_GET_PRIVATE (menu); + + if (! priv->inhibit_repack) + hildon_app_menu_repack_filters (menu); + g_signal_emit (menu, app_menu_signals[CHANGED], 0); } static void @@ -422,24 +448,47 @@ remove_item_from_list (GList **list, } static void +emit_menu_changed (HildonAppMenu *menu, + gpointer item) +{ + g_signal_emit (menu, app_menu_signals[CHANGED], 0); +} + +static void hildon_app_menu_show_all (GtkWidget *widget) { + HildonAppMenu *menu = HILDON_APP_MENU (widget); HildonAppMenuPrivate *priv = HILDON_APP_MENU_GET_PRIVATE (widget); + priv->inhibit_repack = TRUE; + /* Show children, but not self. */ g_list_foreach (priv->buttons, (GFunc) gtk_widget_show_all, NULL); g_list_foreach (priv->filters, (GFunc) gtk_widget_show_all, NULL); + + priv->inhibit_repack = FALSE; + + hildon_app_menu_repack_items (menu, 0); + hildon_app_menu_repack_filters (menu); } static void hildon_app_menu_hide_all (GtkWidget *widget) { + HildonAppMenu *menu = HILDON_APP_MENU (widget); HildonAppMenuPrivate *priv = HILDON_APP_MENU_GET_PRIVATE (widget); + priv->inhibit_repack = TRUE; + /* Hide children, but not self. */ g_list_foreach (priv->buttons, (GFunc) gtk_widget_hide_all, NULL); g_list_foreach (priv->filters, (GFunc) gtk_widget_hide_all, NULL); + + priv->inhibit_repack = FALSE; + + hildon_app_menu_repack_items (menu, 0); + hildon_app_menu_repack_filters (menu); } /* @@ -472,7 +521,7 @@ hildon_app_menu_find_intruder (gpointer data) * Yes, this is a hack. See NB#111027 */ if (HILDON_IS_BANNER (i->data)) { gtk_widget_hide (i->data); - } else { + } else if (!HILDON_IS_ANIMATION_ACTOR (i->data)) { intruder_found = TRUE; } } @@ -497,59 +546,8 @@ hildon_app_menu_map (GtkWidget *widget) GTK_WIDGET_CLASS (hildon_app_menu_parent_class)->map (widget); - /* Grab pointer and keyboard */ - if (priv->transfer_window == NULL) { - gboolean has_grab = FALSE; - - priv->transfer_window = grab_transfer_window_get (widget); - - if (gdk_pointer_grab (priv->transfer_window, TRUE, - GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | - GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK | - GDK_POINTER_MOTION_MASK, NULL, NULL, - GDK_CURRENT_TIME) == GDK_GRAB_SUCCESS) { - if (gdk_keyboard_grab (priv->transfer_window, TRUE, - GDK_CURRENT_TIME) == GDK_GRAB_SUCCESS) { - has_grab = TRUE; - } else { - gdk_display_pointer_ungrab (gtk_widget_get_display (widget), - GDK_CURRENT_TIME); - } - } - - if (has_grab) { - gtk_grab_add (widget); - } else { - gdk_window_destroy (priv->transfer_window); - priv->transfer_window = NULL; - } - } - - /* Make the menu temporary when it's mapped, so it's closed if a - * new window appears */ - gtk_window_set_is_temporary (GTK_WINDOW (widget), TRUE); - - priv->find_intruder_idle_id = gdk_threads_add_idle (hildon_app_menu_find_intruder, widget); -} - -static void -hildon_app_menu_unmap (GtkWidget *widget) -{ - HildonAppMenuPrivate *priv = HILDON_APP_MENU_GET_PRIVATE(widget); - - /* Remove the grab */ - if (priv->transfer_window != NULL) { - gdk_display_pointer_ungrab (gtk_widget_get_display (widget), - GDK_CURRENT_TIME); - gtk_grab_remove (widget); - - gdk_window_destroy (priv->transfer_window); - priv->transfer_window = NULL; - } - - GTK_WIDGET_CLASS (hildon_app_menu_parent_class)->unmap (widget); - - gtk_window_set_is_temporary (GTK_WINDOW (widget), FALSE); + if (priv->find_intruder_idle_id == 0) + priv->find_intruder_idle_id = gdk_threads_add_idle (hildon_app_menu_find_intruder, widget); } static void @@ -631,56 +629,6 @@ hildon_app_menu_key_press (GtkWidget *widget, } static gboolean -hildon_app_menu_button_press (GtkWidget *widget, - GdkEventButton *event) -{ - int x, y; - HildonAppMenuPrivate *priv = HILDON_APP_MENU_GET_PRIVATE(widget); - - gdk_window_get_position (widget->window, &x, &y); - - /* Whether the button has been pressed outside the widget */ - priv->pressed_outside = (event->x_root < x || event->x_root > x + widget->allocation.width || - event->y_root < y || event->y_root > y + widget->allocation.height); - - if (GTK_WIDGET_CLASS (hildon_app_menu_parent_class)->button_press_event) { - return GTK_WIDGET_CLASS (hildon_app_menu_parent_class)->button_press_event (widget, event); - } else { - return FALSE; - } -} - -static gboolean -hildon_app_menu_button_release (GtkWidget *widget, - GdkEventButton *event) -{ - HildonAppMenuPrivate *priv = HILDON_APP_MENU_GET_PRIVATE(widget); - - if (priv->pressed_outside) { - int x, y; - gboolean released_outside; - - gdk_window_get_position (widget->window, &x, &y); - - /* Whether the button has been released outside the widget */ - released_outside = (event->x_root < x || event->x_root > x + widget->allocation.width || - event->y_root < y || event->y_root > y + widget->allocation.height); - - if (released_outside) { - gtk_widget_hide (widget); - } - - priv->pressed_outside = FALSE; /* Always reset pressed_outside to FALSE */ - } - - if (GTK_WIDGET_CLASS (hildon_app_menu_parent_class)->button_release_event) { - return GTK_WIDGET_CLASS (hildon_app_menu_parent_class)->button_release_event (widget, event); - } else { - return FALSE; - } -} - -static gboolean hildon_app_menu_delete_event_handler (GtkWidget *widget, GdkEventAny *event) { @@ -689,34 +637,6 @@ hildon_app_menu_delete_event_handler (GtkWidget *widget, return TRUE; } -/* Grab transfer window (based on the one from GtkMenu) */ -static GdkWindow * -grab_transfer_window_get (GtkWidget *widget) -{ - GdkWindow *window; - GdkWindowAttr attributes; - gint attributes_mask; - - attributes.x = 0; - attributes.y = 0; - attributes.width = 10; - attributes.height = 10; - attributes.window_type = GDK_WINDOW_TEMP; - attributes.wclass = GDK_INPUT_ONLY; - attributes.override_redirect = TRUE; - attributes.event_mask = 0; - - attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_NOREDIR; - - window = gdk_window_new (gtk_widget_get_root_window (widget), - &attributes, attributes_mask); - gdk_window_set_user_data (window, widget); - - gdk_window_show (window); - - return window; -} - static void hildon_app_menu_size_request (GtkWidget *widget, GtkRequisition *requisition) @@ -770,6 +690,7 @@ static void hildon_app_menu_apply_style (GtkWidget *widget) { GdkScreen *screen; + gint filter_group_width; guint horizontal_spacing, vertical_spacing, filter_vertical_spacing; guint inner_border, external_border; HildonAppMenuPrivate *priv; @@ -779,6 +700,7 @@ hildon_app_menu_apply_style (GtkWidget *widget) gtk_widget_style_get (widget, "horizontal-spacing", &horizontal_spacing, "vertical-spacing", &vertical_spacing, + "filter-group-width", &filter_group_width, "filter-vertical-spacing", &filter_vertical_spacing, "inner-border", &inner_border, "external-border", &external_border, @@ -792,13 +714,20 @@ hildon_app_menu_apply_style (GtkWidget *widget) /* Set inner border */ gtk_container_set_border_width (GTK_CONTAINER (widget), inner_border); + /* Set width of the group of filter buttons */ + gtk_widget_set_size_request (GTK_WIDGET (priv->filters_hbox), filter_group_width, -1); + /* Compute width request */ screen = gtk_widget_get_screen (widget); if (gdk_screen_get_width (screen) < gdk_screen_get_height (screen)) { external_border = 0; } priv->width_request = gdk_screen_get_width (screen) - external_border * 2; - gtk_window_move (GTK_WINDOW (widget), external_border, 0); + + if (widget->window) + gdk_window_move_resize (widget->window, + external_border, 0, 1, 1); + gtk_widget_queue_resize (widget); } @@ -848,14 +777,18 @@ hildon_app_menu_repack_items (HildonAppMenu *menu, gint start_from) { HildonAppMenuPrivate *priv; - gint row, col; + gint row, col, nvisible, i; GList *iter; priv = HILDON_APP_MENU_GET_PRIVATE(menu); - /* Remove buttons from their parent */ - if (start_from != -1) { - for (iter = g_list_nth (priv->buttons, start_from); iter != NULL; iter = iter->next) { + i = nvisible = 0; + for (iter = priv->buttons; iter != NULL; iter = iter->next) { + /* Count number of visible items */ + if (GTK_WIDGET_VISIBLE (iter->data)) + nvisible++; + /* Remove buttons from their parent */ + if (start_from != -1 && i >= start_from) { GtkWidget *item = GTK_WIDGET (iter->data); GtkWidget *parent = gtk_widget_get_parent (item); if (parent) { @@ -863,10 +796,16 @@ hildon_app_menu_repack_items (HildonAppMenu *menu, gtk_container_remove (GTK_CONTAINER (parent), item); } } + i++; + } - /* If items have been removed, recalculate the size of the menu */ + /* If items have been removed, recalculate the size of the menu */ + if (start_from != -1) gtk_window_resize (GTK_WINDOW (menu), 1, 1); - } + + /* Set the final size now to avoid unnecessary resizes later */ + if (nvisible > 0) + gtk_table_resize (priv->table, ((nvisible - 1) / priv->columns) + 1, priv->columns); /* Add buttons */ row = col = 0; @@ -887,13 +826,38 @@ hildon_app_menu_repack_items (HildonAppMenu *menu, } } - /* The number of rows/columns might have changed, so we have to - * resize the table */ - if (col == 0) { - gtk_table_resize (priv->table, MAX (row, 1), priv->columns); - } else { - gtk_table_resize (priv->table, row + 1, priv->columns); - } + gtk_widget_queue_draw (GTK_WIDGET (menu)); +} + +/** + * hildon_app_menu_has_visible_children: + * @menu: a #HildonAppMenu + * + * Returns whether this menu has any visible items + * and/or filters. If this is %FALSE, the menu will + * not be popped up. + * + * Returns: whether there are visible items or filters + * + * Since: 2.2 + **/ +gboolean +hildon_app_menu_has_visible_children (HildonAppMenu *menu) +{ + HildonAppMenuPrivate *priv; + GList *i; + gboolean show_menu = FALSE; + + priv = HILDON_APP_MENU_GET_PRIVATE (menu); + + /* Don't show menu if it doesn't contain visible items */ + for (i = priv->buttons; i && !show_menu; i = i->next) + show_menu = GTK_WIDGET_VISIBLE (i->data); + + for (i = priv->filters; i && !show_menu; i = i->next) + show_menu = GTK_WIDGET_VISIBLE (i->data); + + return show_menu; } /** @@ -910,23 +874,10 @@ void hildon_app_menu_popup (HildonAppMenu *menu, GtkWindow *parent_window) { - HildonAppMenuPrivate *priv; - gboolean show_menu = FALSE; - GList *i; - g_return_if_fail (HILDON_IS_APP_MENU (menu)); g_return_if_fail (GTK_IS_WINDOW (parent_window)); - priv = HILDON_APP_MENU_GET_PRIVATE (menu); - - /* Don't show menu if it doesn't contain visible items */ - for (i = priv->buttons; i && !show_menu; i = i->next) - show_menu = GTK_WIDGET_VISIBLE (i->data); - - for (i = priv->filters; i && !show_menu; i = i->next) - show_menu = GTK_WIDGET_VISIBLE (i->data); - - if (show_menu) { + if (hildon_app_menu_has_visible_children (menu)) { hildon_app_menu_set_parent_window (menu, parent_window); gtk_widget_show (GTK_WIDGET (menu)); } @@ -988,6 +939,8 @@ hildon_app_menu_init (HildonAppMenu *menu) priv->parent_window = NULL; priv->transfer_window = NULL; priv->pressed_outside = FALSE; + priv->inhibit_repack = FALSE; + priv->last_pressed_button = NULL; priv->buttons = NULL; priv->filters = NULL; priv->columns = 2; @@ -1059,13 +1012,10 @@ hildon_app_menu_class_init (HildonAppMenuClass *klass) widget_class->show_all = hildon_app_menu_show_all; widget_class->hide_all = hildon_app_menu_hide_all; widget_class->map = hildon_app_menu_map; - widget_class->unmap = hildon_app_menu_unmap; widget_class->realize = hildon_app_menu_realize; widget_class->unrealize = hildon_app_menu_unrealize; widget_class->grab_notify = hildon_app_menu_grab_notify; widget_class->key_press_event = hildon_app_menu_key_press; - widget_class->button_press_event = hildon_app_menu_button_press; - widget_class->button_release_event = hildon_app_menu_button_release; widget_class->style_set = hildon_app_menu_style_set; widget_class->delete_event = hildon_app_menu_delete_event_handler; widget_class->size_request = hildon_app_menu_size_request; @@ -1092,6 +1042,16 @@ hildon_app_menu_class_init (HildonAppMenuClass *klass) gtk_widget_class_install_style_property ( widget_class, + g_param_spec_int ( + "filter-group-width", + "Width of the group of filter buttons", + "Total width of the group of filter buttons, " + "or -1 to use the natural size request.", + -1, G_MAXINT, 444, + G_PARAM_READABLE)); + + gtk_widget_class_install_style_property ( + widget_class, g_param_spec_uint ( "filter-vertical-spacing", "Vertical spacing between filters and menu items", @@ -1117,4 +1077,22 @@ hildon_app_menu_class_init (HildonAppMenuClass *klass) "the screen edges (in horizontal mode)", 0, G_MAXUINT, 50, G_PARAM_READABLE)); + + + /** + * HildonAppMenu::changed: + * @widget: the widget that received the signal + * + * The HildonAppMenu::changed signal is emitted whenever an + * item or filter is added or removed from the menu. + * + * Since: 2.2 + */ + app_menu_signals[CHANGED] = + g_signal_new ("changed", + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, + 0, + NULL, NULL, + g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0, NULL); }