2008-03-03 Sven Herzberg <sven@imendio.com>
[hildon] / src / hildon-window.c
index 4fc166d..a9904c5 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * This file is part of hildon-libs
+ * This file is a part of hildon
  *
  * Copyright (C) 2006 Nokia Corporation, all rights reserved.
  *
@@ -8,7 +8,7 @@
  * 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; version 2.1 of
- * the License.
+ * 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
  *
  */
 
+/**
+ * SECTION:hildon-window
+ * @short_description: Widget representing a top-level window in the Hildon framework.
+ *
+ * The HildonWindow is a GTK widget which represents a top-level
+ * window in the Hildon framework. It is derived from the GtkWindow
+ * and provides additional commodities specific to the Hildon
+ * framework.
+
+ * Among these windows in the Hildon framework can have a single menu
+ * attached, which is toggled with a hardware key or by tapping
+ * a custom button in the window frame. This menu can be set
+ * by providing a GtkMenu to the hildon_window_set_menu() method.
+
+ * Similarly a window in the Hildon framework can have several toolbars
+ * attached. These can be added to the HildonWindow with
+ * hildon_window_add_toolbar()..
+ * 
+ * <example>
+ * <title>Creating a HildonWindow</title>
+ * <programlisting>
+ * HildonWindow *window;
+ * GtkToolbar *toolbar;
+ * GtkMenu *menu;
+ * GdkPixbuf *icon_pixbuf;
+ * <!-- -->
+ * window = HILDON_WINDOW (hildon_window_new());
+ * <!-- -->
+ * toolbar = create_toolbar();
+ * <!-- -->
+ * menu = create_menu();
+ * <!-- -->
+ * icon_pixbuf = create_icon();
+ * <!-- -->
+ * hildon_window_set_menu (window, menu);
+ * <!-- -->
+ * hildon_window_add_toolbar (window, toolbar);
+ * <!-- -->
+ * // Can be used to set the window fullscreen
+ * gtk_window_fullscreen (GTK_WINDOW (window));
+ * <!-- -->
+ * // Used to trigger the blinking of the window's icon in the task navigator
+ * gtk_window_set_urgency_hint (GTK_WINDOW (window), TRUE);
+ * <!-- -->
+ * // Change the window's icon in the task navigator
+ * gtk_window_set_icon (GTK_WINDOW (window), icon_pixbuf);
+ * </programlisting>
+ * </example>
+ *
+ */
+
 #include                                        "hildon-window.h"
 #include                                        <memory.h>
 #include                                        <string.h>
 
 #define TITLE_SEPARATOR                         " - "
 
-static GtkWindowClass                           *parent_class;
-
 typedef void                                    (*HildonWindowSignal) (HildonWindow *, gint, gpointer);
 
 static void
@@ -124,6 +173,12 @@ hildon_window_realize                           (GtkWidget *widget);
 static void
 hildon_window_unrealize                         (GtkWidget *widget);
 
+static void
+hildon_window_map                               (GtkWidget *widget);
+
+static void
+hildon_window_unmap                             (GtkWidget *widget);
+
 static gboolean
 hildon_window_key_press_event                   (GtkWidget *widget,
                                                  GdkEventKey *event);
@@ -134,6 +189,9 @@ hildon_window_key_release_event                 (GtkWidget *widget,
 static gboolean
 hildon_window_window_state_event                (GtkWidget *widget, 
                                                  GdkEventWindowState *event);
+static gboolean
+hildon_window_focus_out_event                   (GtkWidget *widget, 
+                                                 GdkEventFocus *event);
 
 static void
 hildon_window_notify                            (GObject *gobject, 
@@ -143,7 +201,9 @@ static void
 hildon_window_is_topmost_notify                 (HildonWindow *window);
 
 static gboolean
-hildon_window_toggle_menu                       (HildonWindow * self);
+hildon_window_toggle_menu                       (HildonWindow * self,
+                                                guint button,
+                                                guint32 time);
 
 static gboolean
 hildon_window_escape_timeout                    (gpointer data);
@@ -184,60 +244,36 @@ enum
     MAX_WIN_MESSAGES
 };
 
-GType 
-hildon_window_get_type                          (void)
-{
-    static GType window_type = 0;
-
-    if (!window_type) {
-        static const GTypeInfo window_info = {
-            sizeof(HildonWindowClass),
-            NULL,       /* base_init */
-            NULL,       /* base_finalize */
-            (GClassInitFunc) hildon_window_class_init,
-            NULL,       /* class_finalize */
-            NULL,       /* class_data */
-            sizeof(HildonWindow),
-            0,  /* n_preallocs */
-            (GInstanceInitFunc) hildon_window_init,
-        };
-        window_type = g_type_register_static(GTK_TYPE_WINDOW,
-                "HildonWindow",
-                &window_info, 0);
-    }
-    return window_type;
-}
+G_DEFINE_TYPE (HildonWindow, hildon_window, GTK_TYPE_WINDOW);
 
 static void 
 hildon_window_class_init                        (HildonWindowClass * window_class)
 {
     /* Get convenience variables */
-    GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (window_class);
-    GObjectClass *object_class = G_OBJECT_CLASS (window_class);
-    GtkContainerClass *container_class = GTK_CONTAINER_CLASS (window_class);
-
-    /* Set the global parent_class here */
-    parent_class = g_type_class_peek_parent (window_class);
-
-    object_class->get_property = hildon_window_get_property;
-    object_class->notify = hildon_window_notify;
-
-    /* Set the widgets virtual functions */
-    widget_class->size_allocate = hildon_window_size_allocate;
-    widget_class->size_request = hildon_window_size_request;
-    widget_class->expose_event = hildon_window_expose;
-    widget_class->show_all = hildon_window_show_all;
-    widget_class->realize = hildon_window_realize;
-    widget_class->unrealize = hildon_window_unrealize;
-    widget_class->key_press_event = hildon_window_key_press_event;
-    widget_class->key_release_event = hildon_window_key_release_event;
-    widget_class->window_state_event = hildon_window_window_state_event;
+    GtkWidgetClass *widget_class        = GTK_WIDGET_CLASS (window_class);
+    GObjectClass *object_class          = G_OBJECT_CLASS (window_class);
+    GtkContainerClass *container_class  = GTK_CONTAINER_CLASS (window_class);
+
+    object_class->get_property          = hildon_window_get_property;
+    object_class->notify                = hildon_window_notify;
+    widget_class->size_allocate         = hildon_window_size_allocate;
+    widget_class->size_request          = hildon_window_size_request;
+    widget_class->expose_event          = hildon_window_expose;
+    widget_class->show_all              = hildon_window_show_all;
+    widget_class->realize               = hildon_window_realize;
+    widget_class->unrealize             = hildon_window_unrealize;
+    widget_class->key_press_event       = hildon_window_key_press_event;
+    widget_class->key_release_event     = hildon_window_key_release_event;
+    widget_class->window_state_event    = hildon_window_window_state_event;
+    widget_class->focus_out_event       = hildon_window_focus_out_event;
+    widget_class->map                   = hildon_window_map;
+    widget_class->unmap                 = hildon_window_unmap;
 
     /* now the object stuff */
-    object_class->finalize = hildon_window_finalize;
+    object_class->finalize              = hildon_window_finalize;
 
     /* To the container */
-    container_class->forall = hildon_window_forall;
+    container_class->forall             = hildon_window_forall;
 
     /* gtkobject stuff*/
     GTK_OBJECT_CLASS (window_class)->destroy = hildon_window_destroy; 
@@ -246,6 +282,12 @@ hildon_window_class_init                        (HildonWindowClass * window_clas
             sizeof (struct _HildonWindowPrivate));
 
     /* Install properties */
+    
+    /**
+     * HildonWindow:is-topmost:
+     *
+     * Whether the window is currently activated by the window manager.
+     */
     g_object_class_install_property (object_class, PROP_IS_TOPMOST,
             g_param_spec_boolean ("is-topmost",
                 "Is top-most",
@@ -318,11 +360,19 @@ hildon_window_finalize                          (GObject * obj_self)
     
     self = HILDON_WINDOW (obj_self);
 
-    g_free (priv->borders);
-    g_free (priv->toolbar_borders);
+    if (priv->escape_timeout) {
+      g_source_remove (priv->escape_timeout);
+      priv->escape_timeout = 0;
+    }
+
+    if (priv->borders)
+        gtk_border_free (priv->borders);
+
+    if (priv->toolbar_borders)
+        gtk_border_free (priv->toolbar_borders);
 
-    if (G_OBJECT_CLASS (parent_class)->finalize)
-        G_OBJECT_CLASS (parent_class)->finalize (obj_self);
+    if (G_OBJECT_CLASS (hildon_window_parent_class)->finalize)
+        G_OBJECT_CLASS (hildon_window_parent_class)->finalize (obj_self);
 
 }
 
@@ -336,7 +386,7 @@ hildon_window_realize                           (GtkWidget *widget)
     Window active_window;
     HildonWindowPrivate *priv;
 
-    GTK_WIDGET_CLASS (parent_class)->realize (widget);
+    GTK_WIDGET_CLASS (hildon_window_parent_class)->realize (widget);
 
     priv = HILDON_WINDOW_GET_PRIVATE (widget);
     g_assert (priv != NULL);
@@ -391,7 +441,32 @@ hildon_window_unrealize                         (GtkWidget *widget)
             widget);
 
     gtk_widget_unrealize (GTK_WIDGET (priv->vbox));
-    GTK_WIDGET_CLASS(parent_class)->unrealize(widget);
+    GTK_WIDGET_CLASS(hildon_window_parent_class)->unrealize(widget);
+}
+
+static void
+hildon_window_map                             (GtkWidget *widget)
+{
+  HildonWindowPrivate *priv = HILDON_WINDOW_GET_PRIVATE (widget);
+  g_assert (priv != NULL);
+
+  if (GTK_WIDGET_CLASS (hildon_window_parent_class)->map)
+    GTK_WIDGET_CLASS (hildon_window_parent_class)->map (widget);
+
+  if (GTK_WIDGET_VISIBLE (priv->vbox))
+    gtk_widget_map (priv->vbox);
+}
+
+static void
+hildon_window_unmap                             (GtkWidget *widget)
+{
+  HildonWindowPrivate *priv = HILDON_WINDOW_GET_PRIVATE (widget);
+  g_assert (priv != NULL);
+
+  gtk_widget_unmap (priv->vbox);
+
+  if (GTK_WIDGET_CLASS (hildon_window_parent_class)->unmap)
+    GTK_WIDGET_CLASS (hildon_window_parent_class)->unmap (widget);
 }
 
 static void
@@ -421,29 +496,41 @@ hildon_window_get_property                      (GObject *object,
 static void
 hildon_window_get_borders                       (HildonWindow *window)
 {
+    GtkBorder zero = {0, 0, 0, 0};
     HildonWindowPrivate *priv = HILDON_WINDOW_GET_PRIVATE (window);
     g_assert (priv);
 
-    g_free (priv->borders);
-    g_free (priv->toolbar_borders);
+    GtkBorder *borders = NULL;
+    GtkBorder *toolbar_borders = NULL;
 
-    gtk_widget_style_get (GTK_WIDGET (window), "borders",&priv->borders,
-            "toolbar-borders", &priv->toolbar_borders,
-            NULL);
+    if (priv->borders)
+        gtk_border_free (priv->borders);
+    if (priv->toolbar_borders)
+        gtk_border_free (priv->toolbar_borders);
 
-    if (! priv->borders)
-        priv->borders = (GtkBorder *) g_malloc0 (sizeof (GtkBorder));
+    priv->borders = NULL;
+    priv->toolbar_borders = NULL;
 
-    if (! priv->toolbar_borders)
-        priv->toolbar_borders = (GtkBorder *) g_malloc0 (sizeof (GtkBorder));
-}
+    gtk_widget_style_get (GTK_WIDGET (window), "borders",&borders,
+            "toolbar-borders", &toolbar_borders,
+            NULL);
 
-static void
-visible_toolbars                                (gpointer data, 
-                                                 gpointer user_data)
-{
-    if (GTK_WIDGET_VISIBLE (GTK_WIDGET (((GtkBoxChild *)data)->widget)))
-        (*((gint *)user_data)) ++;
+    // We're doing a copy here instead of reusing the pointer, 
+    // as we don't know where it comes from (has it been allocated using 
+    // malloc or slices... and we want to free it sanely. Blowing on 
+    // cold probbably.
+
+    if (borders) {
+        priv->borders = gtk_border_copy (borders);
+        gtk_border_free (borders);
+    } else
+        priv->borders = g_boxed_copy (GTK_TYPE_BORDER, &zero);
+
+    if (toolbar_borders) {
+        priv->toolbar_borders = gtk_border_copy (toolbar_borders);
+        gtk_border_free (toolbar_borders);
+    } else
+        priv->toolbar_borders = g_boxed_copy (GTK_TYPE_BORDER, &zero);
 }
 
 static gboolean
@@ -458,7 +545,6 @@ hildon_window_expose                            (GtkWidget *widget,
     GtkBorder *b = priv->borders;
     GtkBorder *tb = priv->toolbar_borders;
     gint tb_height = 0;
-    gint currently_visible_toolbars = 0;
 
     if (! priv->borders) {
         hildon_window_get_borders (HILDON_WINDOW (widget));
@@ -468,9 +554,6 @@ hildon_window_expose                            (GtkWidget *widget,
 
     tb_height = bx->allocation.height + tb->top + tb->bottom;
 
-    g_list_foreach (box->children, visible_toolbars, 
-            &currently_visible_toolbars);
-
     paint_toolbar (widget, box,
             event, priv->fullscreen);
 
@@ -479,7 +562,7 @@ hildon_window_expose                            (GtkWidget *widget,
         /* Draw the left and right window border */
         gint side_borders_height = widget->allocation.height - b->top;
 
-        if (currently_visible_toolbars)
+        if (priv->visible_toolbars)
             side_borders_height -= tb_height;
         else
             side_borders_height -= b->bottom;
@@ -504,7 +587,7 @@ hildon_window_expose                            (GtkWidget *widget,
         }
 
         /* If no toolbar, draw the bottom window border */
-        if (!currently_visible_toolbars && b->bottom > 0)
+        if (! priv->visible_toolbars && b->bottom > 0)
         {
             gtk_paint_box (widget->style, widget->window,
                     GTK_WIDGET_STATE(widget), GTK_SHADOW_OUT,
@@ -529,10 +612,10 @@ hildon_window_expose                            (GtkWidget *widget,
 
     /* don't draw the window stuff as it overwrites our borders with a blank
      * rectangle. Instead start with the drawing of the GtkBin */
-    GTK_WIDGET_CLASS (g_type_class_peek_parent (parent_class))->expose_event (widget, event);
+    GTK_WIDGET_CLASS (g_type_class_peek_parent (hildon_window_parent_class))->expose_event (widget, event);
 
     /* FIXME Not sure why this is commented out 
-     * GTK_WIDGET_CLASS (parent_class))->
+     * GTK_WIDGET_CLASS (hildon_window_parent_class))->
      *  expose_event (widget, event); 
      */
 
@@ -672,7 +755,7 @@ hildon_window_forall                            (GtkContainer *container,
     g_return_if_fail (callback != NULL);
     g_assert (priv);
 
-    GTK_CONTAINER_CLASS (parent_class)->forall (container, include_internals,
+    GTK_CONTAINER_CLASS (hildon_window_parent_class)->forall (container, include_internals,
             callback, callback_data);
     if (include_internals && priv->vbox != NULL)
         (* callback)(GTK_WIDGET (priv->vbox), callback_data);
@@ -686,7 +769,7 @@ hildon_window_show_all                          (GtkWidget *widget)
 
     g_assert (priv != NULL);
 
-    GTK_WIDGET_CLASS (parent_class)->show_all (widget);
+    GTK_WIDGET_CLASS (hildon_window_parent_class)->show_all (widget);
     gtk_widget_show_all (priv->vbox);
 }
 
@@ -695,8 +778,9 @@ hildon_window_destroy                           (GtkObject *obj)
 {
     HildonWindow *self = HILDON_WINDOW (obj);
     HildonWindowPrivate *priv = HILDON_WINDOW_GET_PRIVATE (obj);
-    GList *menu_list;
-    
+    GList *menu_list = NULL;
+    GList *menu_node = NULL;
+
     g_assert (priv != NULL);
 
     if (priv->vbox != NULL)
@@ -718,22 +802,31 @@ hildon_window_destroy                           (GtkObject *obj)
     }
 
     menu_list = g_list_copy (gtk_menu_get_for_attach_widget (GTK_WIDGET (obj)));
+    menu_node = menu_list;
 
-    while (menu_list)
+    while (menu_node)
     {
-        if (GTK_IS_MENU(menu_list->data))
+        if (GTK_IS_MENU (menu_node->data))
         {
-            if (GTK_WIDGET_VISIBLE (GTK_WIDGET (menu_list->data)))
+            if (GTK_WIDGET_VISIBLE (GTK_WIDGET (menu_node->data)))
             {
-                gtk_menu_popdown (GTK_MENU (menu_list->data));
-                gtk_menu_shell_deactivate (GTK_MENU_SHELL (menu_list->data));
+                gtk_menu_popdown (GTK_MENU (menu_node->data));
+                gtk_menu_shell_deactivate (GTK_MENU_SHELL (menu_node->data));
+            }
+            gtk_menu_detach (GTK_MENU (menu_node->data));
+
+            /* Destroy it, but only if it's not a common menu */
+            if (priv->program && 
+                hildon_program_get_common_menu (priv->program) != menu_node->data) {
+                    gtk_object_destroy (GTK_OBJECT (menu_node->data));
+                    g_object_unref (menu_node->data);
             }
-            gtk_menu_detach (GTK_MENU (menu_list->data));
         }
-        menu_list = menu_list->next;
+        menu_node = menu_node->next;
     }
 
     g_list_free (menu_list);
+    menu_list = NULL;
 
     if (priv->program)
     {
@@ -746,28 +839,27 @@ hildon_window_destroy                           (GtkObject *obj)
 
     gtk_widget_set_events (GTK_WIDGET(obj), 0);
 
-    GTK_OBJECT_CLASS (parent_class)->destroy (obj);
+    GTK_OBJECT_CLASS (hildon_window_parent_class)->destroy (obj);
 }
 
-
 static void
 hildon_window_notify                            (GObject *gobject, 
                                                  GParamSpec *param)
 {
     HildonWindow *window = HILDON_WINDOW (gobject);
 
-    if (strcmp (param->name, "title") == 0)
+    if (g_str_equal (param->name, "title"))
     {
 
         hildon_window_update_title (window);
     }
-    else if (strcmp (param->name, "is-topmost"))
+    else if (g_str_equal (param->name, "is-topmost"))
     {
         hildon_window_is_topmost_notify (window);
     }
 
-    if (G_OBJECT_CLASS(parent_class)->notify)
-        G_OBJECT_CLASS(parent_class)->notify (gobject, param);
+    if (G_OBJECT_CLASS(hildon_window_parent_class)->notify)
+        G_OBJECT_CLASS(hildon_window_parent_class)->notify (gobject, param);
 }
 
 
@@ -940,6 +1032,7 @@ Window
 hildon_window_get_active_window                 (void)
 {
     Atom realtype;
+    gint xerror;
     int format;
     int status;
     Window ret;
@@ -955,11 +1048,13 @@ hildon_window_get_active_window                 (void)
 
     win.win = NULL;
 
+    gdk_error_trap_push ();
     status = XGetWindowProperty (GDK_DISPLAY(), GDK_ROOT_WINDOW(),
             active_app_atom, 0L, 16L,
             0, XA_WINDOW, &realtype, &format,
             &n, &extra, &win.char_pointer);
-    if (!(status == Success && realtype == XA_WINDOW && format == 32
+    xerror = gdk_error_trap_pop ();
+    if (xerror || !(status == Success && realtype == XA_WINDOW && format == 32
                 && n == 1 && win.win != NULL))
     {
         if (win.win != NULL)
@@ -999,7 +1094,7 @@ hildon_window_event_filter                      (GdkXEvent *xevent,
 
         if (xclient_message_type_check (cm, "_MB_GRAB_TRANSFER"))
         {
-            hildon_window_toggle_menu (HILDON_WINDOW ( data ));
+            hildon_window_toggle_menu (HILDON_WINDOW ( data ), cm->data.l[2], cm->data.l[0]);
             return GDK_FILTER_REMOVE;
         }
         /* opera hack clipboard client message */
@@ -1070,7 +1165,7 @@ hildon_window_key_press_event                   (GtkWidget *widget,
     switch (event->keyval)
     {
         case HILDON_HARDKEY_MENU:
-            if (hildon_window_toggle_menu (HILDON_WINDOW (widget)))
+            if (hildon_window_toggle_menu (HILDON_WINDOW (widget), 0, GDK_CURRENT_TIME))
                 return TRUE;
             break;
         case HILDON_HARDKEY_ESC:
@@ -1083,7 +1178,7 @@ hildon_window_key_press_event                   (GtkWidget *widget,
             break;
     }
 
-    return GTK_WIDGET_CLASS (parent_class)->key_press_event (widget, event);
+    return GTK_WIDGET_CLASS (hildon_window_parent_class)->key_press_event (widget, event);
 }
 
 static gboolean
@@ -1106,7 +1201,7 @@ hildon_window_key_release_event                 (GtkWidget *widget,
             break;
     }
 
-    return GTK_WIDGET_CLASS (parent_class)->key_release_event (widget, event);
+    return GTK_WIDGET_CLASS (hildon_window_parent_class)->key_release_event (widget, event);
 
 }
 
@@ -1124,9 +1219,9 @@ hildon_window_window_state_event                (GtkWidget *widget,
     if (event->changed_mask & GDK_WINDOW_STATE_FULLSCREEN)
         priv->fullscreen = event->new_window_state & GDK_WINDOW_STATE_FULLSCREEN;
 
-    if (GTK_WIDGET_CLASS (parent_class)->window_state_event)
+    if (GTK_WIDGET_CLASS (hildon_window_parent_class)->window_state_event)
     {
-        return GTK_WIDGET_CLASS (parent_class)->window_state_event (
+        return GTK_WIDGET_CLASS (hildon_window_parent_class)->window_state_event (
                 widget,
                 event);
     }
@@ -1137,16 +1232,23 @@ hildon_window_window_state_event                (GtkWidget *widget,
 }
 
 /*
-   static void 
-   hildon_window_title_notify (GObject *gobject,
-   GParamSpec *arg1,
-   gpointer user_data)
-   {
-   HildonWindow *window = HILDON_WINDOW (gobject);
+ * If the window lost focus while the user started to press the ESC key, we
+ * won't get the release event. We need to stop the timeout.
+ */
+static gboolean
+hildon_window_focus_out_event                   (GtkWidget *widget, 
+                                                 GdkEventFocus *event)
+{
+  HildonWindowPrivate *priv = HILDON_WINDOW_GET_PRIVATE (widget);
 
-   hildon_window_update_title (window);
+  if (priv->escape_timeout)
+  {
+      g_source_remove (priv->escape_timeout);
+      priv->escape_timeout = 0;
+  }
 
-   }*/
+  return GTK_WIDGET_CLASS (hildon_window_parent_class)->focus_out_event (widget, event);
+}
 
 /*
  * The menu popuping needs a menu popup-function
@@ -1204,18 +1306,6 @@ hildon_window_is_topmost_notify                 (HildonWindow *window)
     {
         hildon_window_take_common_toolbar (window);
     }
-
-    else
-    {
-        /* If the window lost focus while the user started to press
-         * the ESC key, we won't get the release event. We need to
-         * stop the timeout*/
-        if (priv->escape_timeout)
-        {
-            g_source_remove (priv->escape_timeout);
-            priv->escape_timeout = 0;
-        }
-    }
 }
 
 /*
@@ -1415,12 +1505,15 @@ hildon_window_update_title                      (HildonWindow *window)
     {
         const gchar *old_title = gtk_window_get_title (GTK_WINDOW (window));
 
-        if (old_title && old_title[0])
+        if (old_title)
         {
             gchar *title = NULL;
-
-            title = g_strjoin (TITLE_SEPARATOR, application_name,
-                    old_title, NULL);
+                
+            if (strlen (old_title) == 0) 
+                title = g_strdup (application_name);
+            else
+                title = g_strjoin (TITLE_SEPARATOR, application_name,
+                                   old_title, NULL);
 
             gdk_window_set_title (GTK_WIDGET (window)->window, title);
 
@@ -1443,7 +1536,9 @@ detach_menu_func                                (GtkWidget *attach_widget,
  * to toggle)
  */
 static gboolean
-hildon_window_toggle_menu                       (HildonWindow * self)
+hildon_window_toggle_menu                       (HildonWindow * self,
+                                                guint button,
+                                                guint32 time)
 {
     GtkMenu *menu_to_use = NULL;
     GList *menu_children = NULL;
@@ -1505,16 +1600,14 @@ hildon_window_toggle_menu                       (HildonWindow * self)
             gtk_menu_popup (menu_to_use, NULL, NULL,
                     (GtkMenuPositionFunc)
                     hildon_window_menu_popup_func_full,
-                    self, 0, 
-                    gtk_get_current_event_time ());
+                    self, button, time);
         }
         else
         {
             gtk_menu_popup (menu_to_use, NULL, NULL,
                     (GtkMenuPositionFunc)
                     hildon_window_menu_popup_func,
-                    self, 0, 
-                    gtk_get_current_event_time ());
+                    self, button, time);
         }
         gtk_menu_shell_select_first (GTK_MENU_SHELL (menu_to_use), TRUE);
         return TRUE;
@@ -1553,7 +1646,6 @@ hildon_window_escape_timeout                    (gpointer data)
     return FALSE;
 }
 
-
 /**
  * hildon_window_new: 
  * 
@@ -1606,31 +1698,72 @@ hildon_window_add_with_scrollbar                (HildonWindow *self,
     gtk_container_add (GTK_CONTAINER (self), GTK_WIDGET (scrolledw));
 }
 
+static void
+calculate_visible_toolbars                      (gpointer data,
+                                                 gpointer user_data)
+{
+  if (GTK_WIDGET_VISIBLE (GTK_WIDGET (((GtkBoxChild *)data)->widget)))
+    (*((gint *)user_data)) ++;
+}
+
+static void
+toolbar_visible_notify                          (GtkWidget *toolbar, GParamSpec *pspec,
+                                                 HildonWindow *window)
+{
+  HildonWindowPrivate *priv = HILDON_WINDOW_GET_PRIVATE (window);
+
+  g_assert (priv);
+
+  /* Recalculate from scratch the value just in case */
+  priv->visible_toolbars = 0;
+
+  g_list_foreach (GTK_BOX (priv->vbox)->children, calculate_visible_toolbars, 
+                  &priv->visible_toolbars);
+
+  if (priv->visible_toolbars == 0)
+    gtk_widget_hide (priv->vbox);
+  else
+    gtk_widget_show (priv->vbox);
+}
+
 /**
  * hildon_window_add_toolbar:
  * @self: A @HildonWindow
  * @toolbar: A #GtkToolbar to add to the HildonWindow
  *
- * Adds a toolbar to the window.
+ * Adds a toolbar to the window. Note that the toolbar is not automatically
+ * shown. You need to call #gtk_widget_show_all on it to make it visible. 
+ * It's also possible to hide the toolbar (without removing it) by calling
+ * #gtk_widget_hide.
  **/
 void 
 hildon_window_add_toolbar                       (HildonWindow *self, 
                                                  GtkToolbar *toolbar)
 {
     GtkBox *vbox;
-    HildonWindowPrivate *priv = HILDON_WINDOW_GET_PRIVATE (self);
+    HildonWindowPrivate *priv;
 
     g_return_if_fail (HILDON_IS_WINDOW (self));
     g_return_if_fail (toolbar && GTK_IS_TOOLBAR (toolbar));
-    g_assert (priv);
+
+    priv = HILDON_WINDOW_GET_PRIVATE (self);
 
     vbox = GTK_BOX (priv->vbox);
 
-    gtk_box_pack_start (vbox, GTK_WIDGET(toolbar), TRUE, TRUE, 0);
-    gtk_box_reorder_child (vbox, GTK_WIDGET(toolbar), 0);
+    gtk_box_pack_start (vbox, GTK_WIDGET (toolbar), TRUE, TRUE, 0);
+    gtk_box_reorder_child (vbox, GTK_WIDGET (toolbar), 0);
     gtk_widget_set_size_request (GTK_WIDGET (toolbar), -1, TOOLBAR_HEIGHT);
 
-    gtk_widget_queue_resize (GTK_WIDGET(self));
+    g_signal_connect (G_OBJECT (toolbar), "notify::visible",
+                      G_CALLBACK (toolbar_visible_notify), self);
+
+    if (GTK_WIDGET_VISIBLE (toolbar))
+      {
+        priv->visible_toolbars++;
+        gtk_widget_show (priv->vbox);
+      }
+
+    gtk_widget_queue_resize (GTK_WIDGET (self));
 }
 
 /**
@@ -1638,73 +1771,152 @@ hildon_window_add_toolbar                       (HildonWindow *self,
  * @self: A @HildonWindow
  * @toolbar: A #GtkToolbar to remove from the HildonWindow
  *
- * Removes a toolbar from the window.
+ * Removes a toolbar from the window. Note that this decreases the refference
+ * count on the widget. If you want to keep the toolbar alive call #g_object_ref 
+ * before calling this function.
  **/
 void
 hildon_window_remove_toolbar                    (HildonWindow *self, 
                                                  GtkToolbar *toolbar)
 {
-    HildonWindowPrivate *priv = HILDON_WINDOW_GET_PRIVATE (self);
-    
+    HildonWindowPrivate *priv;
+
     g_return_if_fail (HILDON_IS_WINDOW (self));
-    g_assert (priv);
     
-    gtk_container_remove (GTK_CONTAINER (priv->vbox), GTK_WIDGET(toolbar));
+    priv = HILDON_WINDOW_GET_PRIVATE (self);
+
+    if (GTK_WIDGET_VISIBLE (toolbar))
+      {
+        if (--(priv->visible_toolbars) == 0)
+          gtk_widget_hide (priv->vbox);
+      }
+
+    g_signal_handlers_disconnect_by_func (toolbar, toolbar_visible_notify, self);
+
+    gtk_container_remove (GTK_CONTAINER (priv->vbox), GTK_WIDGET (toolbar));
 }
 
 /**
  * hildon_window_get_menu:
  * @self : #HildonWindow
  * 
- * Gets the #GtMenu assigned to the #HildonAppview.
+ * Gets the #GtMenu assigned to the #HildonAppview. Note that the 
+ * window is still the owner of the menu.
  * 
- * Return value: The #GtkMenu assigned to this application view.
+ * Return value: The #GtkMenu assigned to this application view. 
  **/
 GtkMenu*
 hildon_window_get_menu                          (HildonWindow * self)
 {
-    HildonWindowPrivate *priv = HILDON_WINDOW_GET_PRIVATE (self);
-    
+    HildonWindowPrivate *priv;
+
     g_return_val_if_fail (HILDON_IS_WINDOW (self), NULL);
-    g_assert (priv);
+
+    priv = HILDON_WINDOW_GET_PRIVATE (self);
 
     return GTK_MENU (priv->menu);
 }
 
+/* Since we've been asking developers to call gtk_window_add_accel_group()
+ * themselves, do not trigger criticals by trying it again.
+ */
+static void
+hildon_window_add_accel_group (HildonWindow *self,
+                              GtkAccelGroup *accel_group)
+{
+    GSList *groups, *l;
+
+    groups = gtk_accel_groups_from_object (G_OBJECT (self));
+    for (l = groups; l != NULL; l = l->next)
+      if (l->data == (gpointer)accel_group)
+       /* Maybe print a warning here? */
+       return;
+
+    gtk_window_add_accel_group (GTK_WINDOW (self), accel_group);
+}
+
 /**
- * hildon_window_set_menu:
+ * hildon_window_set_main_menu:
  * @self: A #HildonWindow
  * @menu: The #GtkMenu to be used for this #HildonWindow
  * 
  * Sets the menu to be used for this window. This menu overrides
  * a program-wide menu that may have been set with
- * hildon_program_set_common_menu. Pass NULL to remove the current
- * menu.
+ * hildon_program_set_common_menu(). Pass %NULL to remove the current
+ * menu. #HildonWindow takes ownership of the passed menu and you're
+ * not supposed to free it yourself anymore.
+ *
+ * Since: Hildon 2.2
  **/ 
 void
-hildon_window_set_menu                          (HildonWindow *self, 
-                                                 GtkMenu *menu)
+hildon_window_set_main_menu (HildonWindow* self,
+                            GtkMenu     * menu)
 {
-    HildonWindowPrivate *priv = HILDON_WINDOW_GET_PRIVATE (self);
+    HildonWindowPrivate *priv;
+    GtkAccelGroup *accel_group;
 
     g_return_if_fail (HILDON_IS_WINDOW (self));
-    g_assert (priv);
 
-    if (priv->menu != NULL) {
+    priv = HILDON_WINDOW_GET_PRIVATE (self);
+
+    if (priv->menu != NULL)
+    {
+       accel_group = gtk_menu_get_accel_group (GTK_MENU (priv->menu));
+       if (accel_group != NULL)
+           gtk_window_remove_accel_group (GTK_WINDOW (self), accel_group);
+
         gtk_menu_detach (GTK_MENU (priv->menu));
         g_object_unref (priv->menu);
     }
 
     priv->menu = (menu != NULL) ? GTK_WIDGET (menu) : NULL;
-    if (priv->menu != NULL) {
+    if (priv->menu != NULL)
+    {
         gtk_widget_set_name (priv->menu, "menu_force_with_corners");
         gtk_menu_attach_to_widget (GTK_MENU (priv->menu), GTK_WIDGET (self), &detach_menu_func);
         g_object_ref (GTK_MENU (priv->menu));
-        gtk_widget_show_all (GTK_WIDGET (priv->menu));
+
+       accel_group = gtk_menu_get_accel_group (GTK_MENU (priv->menu));
+       if (accel_group != NULL)
+           hildon_window_add_accel_group (self, accel_group);
     }
 }
 
 /**
+ * hildon_window_set_menu:
+ * @self: A #HildonWindow
+ * @menu: The #GtkMenu to be used for this #HildonWindow
+ * 
+ * Sets the menu to be used for this window. This menu overrides
+ * a program-wide menu that may have been set with
+ * hildon_program_set_common_menu. Pass NULL to remove the current
+ * menu. HildonWindow takes ownership of the passed menu and you're
+ * not supposed to free it yourself anymore.
+ *
+ * Note: hildon_window_set_menu() calls gtk_widget_show_all() for the
+ * #GtkMenu. To pass control about visibility to the application
+ * developer, hildon_window_set_main_menu() was introduced, which
+ * doesn't do this.
+ *
+ * Deprecated: Hildon 2.2: use hildon_window_set_main_menu()
+ **/ 
+void
+hildon_window_set_menu                          (HildonWindow *self, 
+                                                 GtkMenu *menu)
+{
+    HildonWindowPrivate *priv;
+
+    g_return_if_fail (HILDON_IS_WINDOW (self));
+
+    hildon_window_set_main_menu (self, menu);
+
+    priv = HILDON_WINDOW_GET_PRIVATE (self);
+
+    if (priv->menu != NULL)
+        gtk_widget_show_all (GTK_WIDGET (priv->menu));
+}
+
+/**
  * hildon_window_get_is_topmost:
  * @self: A #HildonWindow
  * 
@@ -1714,11 +1926,11 @@ hildon_window_set_menu                          (HildonWindow *self,
 gboolean
 hildon_window_get_is_topmost                    (HildonWindow *self)
 {
-    HildonWindowPrivate *priv = HILDON_WINDOW_GET_PRIVATE (self);
+    HildonWindowPrivate *priv;
 
     g_return_val_if_fail (HILDON_IS_WINDOW (self), FALSE);
-    g_assert (priv);
 
+    priv = HILDON_WINDOW_GET_PRIVATE (self);
     return priv->is_topmost;
 }