2008-08-08 Alberto Garcia <agarcia@igalia.com>
authorAlberto Garcia <agarcia@igalia.com>
Fri, 8 Aug 2008 00:45:17 +0000 (00:45 +0000)
committerAlberto Garcia <agarcia@igalia.com>
Fri, 8 Aug 2008 00:45:17 +0000 (00:45 +0000)
* src/hildon-program-private.h
* src/hildon-program.c
* src/hildon-program.h
(_hildon_program_remove_from_stack, _hildon_program_add_to_stack)
(hildon_program_peek_window_stack)
(hildon_program_pop_window_stack)
(hildon_program_go_to_root_window): HildonProgram now has a
separate list for stackable windows. Functions are provided to
manage that list with a stack-like API.

* src/hildon-stackable-window.c: Stackable window management is
now much simpler with the new HildonProgram API. Windows no
longer need to be manually added to the program, they're
automatically pushed to the top of the stack when shown.

* examples/hildon-stackable-window-example.c: Updated example to
reflect the API changes.

ChangeLog
examples/hildon-stackable-window-example.c
src/hildon-program-private.h
src/hildon-program.c
src/hildon-program.h
src/hildon-stackable-window.c

index e17c49f..7ae6d86 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,23 @@
+2008-08-08  Alberto Garcia  <agarcia@igalia.com>
+
+       * src/hildon-program-private.h
+       * src/hildon-program.c
+       * src/hildon-program.h
+       (_hildon_program_remove_from_stack, _hildon_program_add_to_stack)
+       (hildon_program_peek_window_stack)
+       (hildon_program_pop_window_stack)
+       (hildon_program_go_to_root_window): HildonProgram now has a
+       separate list for stackable windows. Functions are provided to
+       manage that list with a stack-like API.
+
+       * src/hildon-stackable-window.c: Stackable window management is
+       now much simpler with the new HildonProgram API. Windows no
+       longer need to be manually added to the program, they're
+       automatically pushed to the top of the stack when shown.
+
+       * examples/hildon-stackable-window-example.c: Updated example to
+       reflect the API changes.
+
 2008-08-07  Claudio Saavedra  <csaavedra@igalia.com>
 
        * src/hildon-touch-selector.c: Comment out unimplemented
index 09367f2..aabeda0 100644 (file)
 static void
 add_window                                      (GtkWidget* w);
 
-static void
-detach_window                                   (GtkWidget* w)
-{
-    HildonProgram *program = hildon_program_get_instance ();
-    hildon_program_remove_window (program, HILDON_WINDOW (w));
-}
-
 static GtkWidget*
 new_window                                      (gboolean ismain)
 {
@@ -74,11 +67,11 @@ new_window                                      (gboolean ismain)
     if (!ismain)
     {
         GtkWidget *detach, *back;
-        detach = GTK_WIDGET (gtk_button_new_with_label ("Detach"));
+        detach = GTK_WIDGET (gtk_button_new_with_label ("Destroy"));
         gtk_box_pack_end (GTK_BOX (hbbox), detach, FALSE, FALSE, 0);
 
         g_signal_connect_swapped (G_OBJECT (detach), "clicked",
-                                  G_CALLBACK (detach_window),
+                                  G_CALLBACK (gtk_widget_destroy),
                                   HILDON_STACKABLE_WINDOW (window));
 
         back = GTK_WIDGET (gtk_button_new_with_label ("Back to root"));
@@ -96,10 +89,6 @@ static void
 add_window                                      (GtkWidget *w)
 {
     GtkWidget *window = new_window (FALSE);
-    HildonProgram *program = hildon_program_get_instance ();
-
-    hildon_program_add_window (program, HILDON_WINDOW (window));
-
     gtk_widget_show_all (window);
 
     return;
@@ -109,16 +98,13 @@ int
 main                                            (int argc,
                                                  char **args)
 {
-    HildonProgram *program;
     GtkWidget *window;
 
     gtk_init (&argc, &args);
 
     g_set_application_name ("stack");
 
-    program = hildon_program_get_instance ();
     window = new_window (TRUE);
-    hildon_program_add_window (program, HILDON_WINDOW (window));
 
     g_signal_connect (G_OBJECT (window), "delete_event",
                       G_CALLBACK (gtk_main_quit), NULL);
index 2a96012..b1bdc87 100644 (file)
@@ -27,6 +27,7 @@
 
 #include                                        <glib-object.h>
 #include                                        "hildon-window.h"
+#include                                        "hildon-stackable-window.h"
 
 G_BEGIN_DECLS
 
@@ -45,10 +46,19 @@ struct                                          _HildonProgramPrivate
     GtkWidget *common_menu;
     GtkWidget *common_toolbar;
     GSList *windows;
+    GSList *window_stack;
     Window window_group;
     gchar *name;
 };
 
+void G_GNUC_INTERNAL
+_hildon_program_add_to_stack                    (HildonProgram         *self,
+                                                 HildonStackableWindow *win);
+
+gboolean G_GNUC_INTERNAL
+_hildon_program_remove_from_stack               (HildonProgram         *self,
+                                                 HildonStackableWindow *win);
+
 G_END_DECLS
 
 #endif                                          /* __HILDON_PROGRAM_PRIVATE_H__ */
index 98f86fc..e5d5c8d 100644 (file)
@@ -150,6 +150,8 @@ hildon_program_init                             (HildonProgram *self)
     priv->window_group = GDK_WINDOW_XID (gdk_display_get_default_group (gdk_display_get_default()));
     priv->common_toolbar = NULL;
     priv->name = NULL;
+    priv->windows = NULL;
+    priv->window_stack = NULL;
 }
 
 static void
@@ -261,6 +263,125 @@ hildon_program_get_property                     (GObject *object,
     }
 }
 
+/**
+ * hildon_program_pop_window_stack:
+ * @self: A #HildonProgram
+ *
+ * The #HildonProgram object maintains a list of stackable
+ * windows. Each time a #HildonStackableWindow is shown, it is
+ * automatically added to the top of the stack. Windows are removed
+ * from the stack when they are destroyed.
+ *
+ * This function removes the #HildonStackableWindow from the top of
+ * the stack and returns it. The window is automatically hidden, but
+ * not destroyed. If that window was visible and there are more
+ * windows left in the stack, the next one will be shown
+ * automatically.
+ *
+ * If the stack is empty, %NULL is returned.
+ *
+ * Returns: A #HildonStackableWindow, or %NULL.
+ */
+HildonStackableWindow *
+hildon_program_pop_window_stack                 (HildonProgram *self)
+{
+    HildonStackableWindow *top;
+
+    top = hildon_program_peek_window_stack (self);
+
+    if (top)
+    {
+        HildonStackableWindow *next;
+
+        /* Remove the window from the stack and get the next one */
+        _hildon_program_remove_from_stack (self, top);
+        next = hildon_program_peek_window_stack (self);
+
+        /* Hide the window just removed and show the next one if necessary */
+        if (GTK_WIDGET_VISIBLE (GTK_WIDGET (top)) && next != NULL);
+            gtk_widget_show (GTK_WIDGET (next));
+
+        gtk_widget_hide (GTK_WIDGET (top));
+    }
+
+    return top;
+}
+
+/**
+ * hildon_program_peek_window_stack:
+ * @self: A #HildonProgram
+ *
+ * The #HildonProgram object maintains a list of stackable
+ * windows. Each time a #HildonStackableWindow is shown, it is
+ * automatically added to the top of the stack. Windows are removed
+ * from the stack when they are destroyed.
+ *
+ * This function returns the #HildonStackableWindow from the top of
+ * the stack or %NULL if the stack is empty. The stack is never modified.
+ *
+ * Returns: A #HildonStackableWindow, or %NULL.
+ */
+HildonStackableWindow *
+hildon_program_peek_window_stack                (HildonProgram *self)
+{
+    HildonStackableWindow *top = NULL;
+    HildonProgramPrivate *priv;
+
+    g_return_val_if_fail (HILDON_IS_PROGRAM (self), NULL);
+
+    priv = HILDON_PROGRAM_GET_PRIVATE (self);
+    g_assert (priv);
+
+    if (priv->window_stack != NULL)
+        top = HILDON_STACKABLE_WINDOW (priv->window_stack->data);
+
+    return top;
+}
+
+void G_GNUC_INTERNAL
+_hildon_program_add_to_stack                    (HildonProgram         *self,
+                                                 HildonStackableWindow *win)
+{
+    HildonProgramPrivate *priv;
+
+    g_return_if_fail (HILDON_IS_PROGRAM (self));
+    g_return_if_fail (HILDON_IS_STACKABLE_WINDOW (win));
+
+    priv = HILDON_PROGRAM_GET_PRIVATE (self);
+    g_assert (priv);
+
+    if (g_slist_find (priv->window_stack, win) == NULL)
+    {
+        priv->window_stack = g_slist_prepend (priv->window_stack, win);
+    }
+    else
+    {
+        g_critical ("%s: window already in the stack!", __FUNCTION__);
+    }
+
+}
+
+gboolean G_GNUC_INTERNAL
+_hildon_program_remove_from_stack               (HildonProgram         *self,
+                                                 HildonStackableWindow *win)
+{
+    GSList *pos;
+    HildonProgramPrivate *priv;
+
+    g_return_val_if_fail (HILDON_IS_PROGRAM (self), FALSE);
+    g_return_val_if_fail (HILDON_IS_STACKABLE_WINDOW (win), FALSE);
+
+    priv = HILDON_PROGRAM_GET_PRIVATE (self);
+    g_assert (priv);
+
+    pos = g_slist_find (priv->window_stack, win);
+
+    if (pos != NULL)
+        priv->window_stack = g_slist_delete_link (priv->window_stack, pos);
+
+    return (pos != NULL);
+}
+
 /* Utilities */
 static gint 
 hildon_program_window_list_compare              (gconstpointer window_a, 
@@ -708,8 +829,8 @@ hildon_program_go_to_root_window                (HildonProgram *self)
     priv = HILDON_PROGRAM_GET_PRIVATE (self);
     g_assert (priv);
 
-    /* List of windows in reverse order (starting from the topmost one) */
-    windows = g_slist_reverse (g_slist_copy (priv->windows));
+    /* List of stacked windows (starting from the topmost one) */
+    windows = g_slist_copy (priv->window_stack);
     iter = windows;
 
     /* Destroy all the windows but the last one (which is the root
@@ -750,7 +871,7 @@ hildon_program_go_to_root_window                (HildonProgram *self)
         }
         else
         {
-            g_warning ("Window list contains a non-stackable window");
+            g_critical ("Window list contains a non-stackable window");
             windows_left = FALSE;
         }
     }
index c2460e4..8785dd6 100644 (file)
@@ -27,6 +27,7 @@
 
 #include                                        <glib-object.h>
 #include                                        "hildon-window.h"
+#include                                        "hildon-stackable-window.h"
 
 G_BEGIN_DECLS
 
@@ -108,6 +109,12 @@ hildon_program_get_common_toolbar               (HildonProgram *self);
 gboolean
 hildon_program_get_is_topmost                   (HildonProgram *self);
 
+HildonStackableWindow *
+hildon_program_pop_window_stack                 (HildonProgram *self);
+
+HildonStackableWindow *
+hildon_program_peek_window_stack                (HildonProgram *self);
+
 void
 hildon_program_go_to_root_window                (HildonProgram *self);
 
index f457887..02b5ac4 100644 (file)
  * in a hierarchical way so users can go from any window back to the
  * application's root window.
  *
- * To add a window to the stack, use hildon_program_add_window(). Once
- * you show the new window, the previous one will be automatically
- * hidden. When the new window is destroyed, the previous one will
- * appear again.
+ * To add a window to the stack, just use gtk_widget_show(). The
+ * previous one will be automatically hidden. When the new window is
+ * destroyed, the previous one will appear again.
  *
  * <example>
  * <programlisting>
@@ -50,7 +49,6 @@
  * <!-- -->
  *     // ... configure first window
  * <!-- -->
- *     hildon_program_add_window (program, HILDON_WINDOW (win1));
  *     gtk_widget_show (win1);
  * }
  * <!-- -->
@@ -65,7 +63,6 @@
  * <!-- -->
  *     // ... configure second window
  * <!-- -->
- *     hildon_program_add_window (program, HILDON_WINDOW (win2));
  *     gtk_widget_show (win2);
  * }
  * </programlisting>
@@ -97,97 +94,6 @@ hildon_stackable_window_get_going_home          (HildonStackableWindow *self)
     return priv->going_home;
 }
 
-static GSList*
-get_window_list                                 (GtkWidget *widget)
-{
-    HildonWindowPrivate *wpriv;
-    HildonProgramPrivate *ppriv;
-    GSList *retval = NULL;
-    g_return_val_if_fail (widget != NULL, NULL);
-
-    wpriv = HILDON_WINDOW_GET_PRIVATE (widget);
-    g_assert (wpriv != NULL);
-
-    if (wpriv->program)
-    {
-        ppriv = HILDON_PROGRAM_GET_PRIVATE (wpriv->program);
-        g_assert (ppriv != NULL);
-        retval = ppriv->windows;
-    }
-
-    return retval;
-}
-
-static GtkWidget*
-get_previous_window_if_last                     (GtkWidget *widget)
-{
-    GtkWidget *retval = NULL;
-    GSList *iter = get_window_list (widget);
-    GSList *previous = NULL;
-
-    /* Return NULL if the window hasn't been added to the HildonProgram */
-    if (iter == NULL)
-    {
-        return NULL;
-    }
-
-    /* Go to the end of the window list */
-    while (iter->next != NULL)
-    {
-        previous = iter;
-        iter = iter->next;
-    }
-
-    if ((iter->data == widget) && (previous != NULL))
-    {
-        retval = GTK_WIDGET (previous->data);
-    }
-
-    return retval;
-}
-
-static void
-hildon_stackable_window_map                     (GtkWidget *widget)
-{
-    GtkWidget *previous = get_previous_window_if_last (widget);
-
-    if (GTK_WIDGET_CLASS (hildon_stackable_window_parent_class)->map)
-        GTK_WIDGET_CLASS (hildon_stackable_window_parent_class)->map (widget);
-
-    if (HILDON_IS_STACKABLE_WINDOW (previous) && GTK_WIDGET_VISIBLE (previous))
-        gtk_widget_hide (previous);
-}
-
-static void
-hildon_stackable_window_unmap                   (GtkWidget *widget)
-{
-    GtkWidget *previous = get_previous_window_if_last (widget);
-
-    if (GTK_WIDGET_CLASS (hildon_stackable_window_parent_class)->unmap)
-        GTK_WIDGET_CLASS (hildon_stackable_window_parent_class)->unmap (widget);
-
-    if (HILDON_IS_STACKABLE_WINDOW (previous) && !GTK_WIDGET_VISIBLE (previous) &&
-        !hildon_stackable_window_get_going_home (HILDON_STACKABLE_WINDOW (widget)))
-    {
-        gtk_widget_show (previous);
-    }
-}
-
-static void
-hildon_stackable_window_unset_program           (HildonWindow *hwin)
-{
-    GtkWidget *previous = get_previous_window_if_last (GTK_WIDGET (hwin));
-
-    if (HILDON_WINDOW_CLASS (hildon_stackable_window_parent_class)->unset_program)
-        HILDON_WINDOW_CLASS (hildon_stackable_window_parent_class)->unset_program (hwin);
-
-    if (HILDON_IS_STACKABLE_WINDOW (previous) && !GTK_WIDGET_VISIBLE (previous) &&
-        !hildon_stackable_window_get_going_home (HILDON_STACKABLE_WINDOW (hwin)))
-    {
-        gtk_widget_show (previous);
-    }
-}
-
 void
 hildon_stackable_window_set_main_menu           (HildonStackableWindow *self,
                                                  HildonAppMenu *menu)
@@ -256,20 +162,40 @@ hildon_stackable_window_realize                 (GtkWidget *widget)
                     (guchar *)&atom, 1);
 }
 
-static gboolean
-hildon_stackable_window_delete_event            (GtkWidget *widget,
-                                                 GdkEventAny *event)
+static void
+hildon_stackable_window_show                    (GtkWidget *widget)
 {
-    GSList *list = get_window_list (widget);
-    list = g_slist_find (list, widget);
+    HildonProgram *program = hildon_program_get_instance ();
+    HildonStackableWindow *current_win = HILDON_STACKABLE_WINDOW (widget);
+    HildonStackableWindow *previous_win = hildon_program_peek_window_stack (program);
 
-    /* Ignore the delete event if this is not the topmost window */
-    if (list != NULL && list->next != NULL)
-        return TRUE;
-    else if (GTK_WIDGET_CLASS (hildon_stackable_window_parent_class)->delete_event)
-        return GTK_WIDGET_CLASS (hildon_stackable_window_parent_class)->delete_event (widget, event);
-    else
-        return FALSE;
+    if (previous_win != current_win)
+        _hildon_program_add_to_stack (program, current_win);
+
+    GTK_WIDGET_CLASS (hildon_stackable_window_parent_class)->show (widget);
+
+    if (previous_win != NULL && previous_win != current_win)
+        gtk_widget_hide (GTK_WIDGET (previous_win));
+}
+
+static void
+hildon_stackable_window_destroy                 (GtkObject *obj)
+{
+    HildonProgram *program = hildon_program_get_instance ();
+    HildonStackableWindow *topmost = hildon_program_peek_window_stack (program);
+
+    if (_hildon_program_remove_from_stack (program, HILDON_STACKABLE_WINDOW (obj)))
+    {
+        if (topmost != NULL && GTK_OBJECT (topmost) == obj)
+        {
+            HildonStackableWindowPrivate *priv = HILDON_STACKABLE_WINDOW_GET_PRIVATE (obj);
+            topmost = hildon_program_peek_window_stack (program);
+            if (topmost != NULL && !priv->going_home)
+                gtk_widget_show (GTK_WIDGET (topmost));
+        }
+    }
+
+    GTK_OBJECT_CLASS (hildon_stackable_window_parent_class)->destroy (obj);
 }
 
 static void
@@ -277,26 +203,25 @@ hildon_stackable_window_finalize                (GObject *object)
 {
     HildonStackableWindowPrivate *priv = HILDON_STACKABLE_WINDOW_GET_PRIVATE (object);
 
-    if (priv->app_menu) {
+    if (priv->app_menu)
         gtk_widget_destroy (GTK_WIDGET (priv->app_menu));
-    }
 }
 
 static void
 hildon_stackable_window_class_init              (HildonStackableWindowClass *klass)
 {
     GObjectClass      *obj_class    = G_OBJECT_CLASS (klass);
+    GtkObjectClass    *gtk_obj_class = GTK_OBJECT_CLASS (klass);
     GtkWidgetClass    *widget_class = GTK_WIDGET_CLASS (klass);
     HildonWindowClass *window_class = HILDON_WINDOW_CLASS (klass);
 
     obj_class->finalize             = hildon_stackable_window_finalize;
 
-    widget_class->map               = hildon_stackable_window_map;
-    widget_class->unmap             = hildon_stackable_window_unmap;
+    gtk_obj_class->destroy          = hildon_stackable_window_destroy;
+
     widget_class->realize           = hildon_stackable_window_realize;
-    widget_class->delete_event      = hildon_stackable_window_delete_event;
+    widget_class->show              = hildon_stackable_window_show;
 
-    window_class->unset_program     = hildon_stackable_window_unset_program;
     window_class->toggle_menu       = hildon_stackable_window_toggle_menu;
 
     g_type_class_add_private (klass, sizeof (HildonStackableWindowPrivate));