Ignore BadWindow on update topmost
[hildon] / hildon / hildon-app-menu.c
index 7e838e4..9c98081 100644 (file)
 
 /**
  * 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
  * 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
 #include                                        "hildon-app-menu-private.h"
 #include                                        "hildon-window.h"
 #include                                        "hildon-banner.h"
+#include                                        "hildon-animation-actor.h"
 
 static GdkWindow *
 grab_transfer_window_get                        (GtkWidget *widget);
@@ -126,6 +131,11 @@ filter_visibility_changed                       (GtkWidget     *item,
                                                  GParamSpec    *arg1,
                                                  HildonAppMenu *menu);
 
+static gboolean
+menu_item_button_event                          (GtkButton      *item,
+                                                 GdkEventButton *event,
+                                                 GtkWidget      *menu);
+
 static void
 remove_item_from_list                           (GList    **list,
                                                  gpointer   item);
@@ -190,6 +200,10 @@ hildon_app_menu_insert                          (HildonAppMenu *menu,
     g_signal_connect_swapped (item, "clicked", G_CALLBACK (gtk_widget_hide), menu);
     g_signal_connect (item, "notify::visible", G_CALLBACK (item_visibility_changed), menu);
 
+    /* Keep track of the latest menu item to receive a button-press event */
+    g_signal_connect (item, "button-press-event", G_CALLBACK (menu_item_button_event), menu);
+    g_signal_connect (item, "button-release-event", G_CALLBACK (menu_item_button_event), menu);
+
     /* Remove item from list when it is destroyed */
     g_object_weak_ref (G_OBJECT (item), (GWeakNotify) remove_item_from_list, &(priv->buttons));
 }
@@ -297,6 +311,10 @@ hildon_app_menu_add_filter                      (HildonAppMenu *menu,
     g_signal_connect_swapped (filter, "clicked", G_CALLBACK (gtk_widget_hide), menu);
     g_signal_connect (filter, "notify::visible", G_CALLBACK (filter_visibility_changed), menu);
 
+    /* Keep track of the latest menu item to receive a button-press event */
+    g_signal_connect (filter, "button-press-event", G_CALLBACK (menu_item_button_event), menu);
+    g_signal_connect (filter, "button-release-event", G_CALLBACK (menu_item_button_event), menu);
+
     /* Remove filter from list when it is destroyed */
     g_object_weak_ref (G_OBJECT (filter), (GWeakNotify) remove_item_from_list, &(priv->filters));
 }
@@ -418,6 +436,27 @@ filter_visibility_changed                       (GtkWidget     *item,
         hildon_app_menu_repack_filters (menu);
 }
 
+static gboolean
+menu_item_button_event                          (GtkButton      *item,
+                                                 GdkEventButton *event,
+                                                 GtkWidget      *menu)
+{
+    HildonAppMenuPrivate *priv = HILDON_APP_MENU_GET_PRIVATE (menu);
+
+    if (event->type == GDK_BUTTON_PRESS) {
+        priv->last_pressed_button = item;
+    } else if (event->type == GDK_BUTTON_RELEASE) {
+        /* A pressed button might not receive the button-release event due
+         * to the grab that HildonAppMenu has, so we have to simulate that
+         * event. See NB#108337 */
+        if (priv->last_pressed_button && priv->last_pressed_button != item) {
+            gtk_button_released (priv->last_pressed_button);
+        }
+        priv->last_pressed_button = NULL;
+    }
+    return FALSE;
+}
+
 static void
 remove_item_from_list                           (GList    **list,
                                                  gpointer   item)
@@ -492,7 +531,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;
                     }
                 }
@@ -515,14 +554,15 @@ hildon_app_menu_map                             (GtkWidget *widget)
 {
     HildonAppMenuPrivate *priv = HILDON_APP_MENU_GET_PRIVATE(widget);
 
+    if (priv->transfer_window == NULL)
+        priv->transfer_window = grab_transfer_window_get (widget);
+
     GTK_WIDGET_CLASS (hildon_app_menu_parent_class)->map (widget);
 
     /* Grab pointer and keyboard */
-    if (priv->transfer_window == NULL) {
+    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 |
@@ -549,7 +589,8 @@ hildon_app_menu_map                             (GtkWidget *widget)
      * 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);
+    if (priv->find_intruder_idle_id == 0)
+        priv->find_intruder_idle_id = gdk_threads_add_idle (hildon_app_menu_find_intruder, widget);
 }
 
 static void
@@ -691,6 +732,8 @@ hildon_app_menu_button_release                  (GtkWidget *widget,
         }
 
         priv->pressed_outside = FALSE; /* Always reset pressed_outside to FALSE */
+    } else if (priv->last_pressed_button) {
+        menu_item_button_event (NULL, event, widget);
     }
 
     if (GTK_WIDGET_CLASS (hildon_app_menu_parent_class)->button_release_event) {
@@ -790,6 +833,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;
@@ -799,6 +843,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,
@@ -812,6 +857,9 @@ 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)) {
@@ -868,14 +916,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) {
@@ -883,10 +935,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;
@@ -906,14 +964,6 @@ 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);
-    }
 }
 
 /**
@@ -1009,6 +1059,7 @@ hildon_app_menu_init                            (HildonAppMenu *menu)
     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;
@@ -1113,6 +1164,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",