From 8c908e27d527aa6feed0b15b5f2a3b8487d10033 Mon Sep 17 00:00:00 2001 From: Alberto Garcia Date: Tue, 25 Nov 2008 14:31:06 +0000 Subject: [PATCH] 2008-11-25 Alberto Garcia * doc/hildon-docs.sgml * doc/hildon.types * src/Makefile.am * src/hildon.h * src/hildon-window-stack-private.h * src/hildon-window-stack.h * src/hildon-window-stack.c: New HildonWindowStack object, that adds support for multiple stacks of windows per process. * src/hildon-stackable-window-private.h * src/hildon-stackable-window.h * src/hildon-stackable-window.c (hildon_stackable_window_set_stack) (hildon_stackable_window_get_stack, hildon_stackable_window_map) (hildon_stackable_window_show, hildon_stackable_window_hide) (hildon_stackable_window_class_init) (hildon_stackable_window_init): Use HildonWindowStack for stack management. * src/hildon-program.c (hildon_program_pop_window_stack) (hildon_program_peek_window_stack) (hildon_program_go_to_root_window): Add a fallback implementation to the deprecated functions using HildonWindowStack. * examples/hildon-stackable-window-example.c: Use the new HildonWindowStack API. --- ChangeLog | 30 ++ doc/hildon-docs.sgml | 1 + doc/hildon.types | 2 + examples/hildon-stackable-window-example.c | 188 ++++++++--- src/Makefile.am | 3 + src/hildon-program.c | 25 +- src/hildon-stackable-window-private.h | 7 + src/hildon-stackable-window.c | 90 +++++- src/hildon-stackable-window.h | 5 + src/hildon-window-stack-private.h | 33 ++ src/hildon-window-stack.c | 486 ++++++++++++++++++++++++++++ src/hildon-window-stack.h | 130 ++++++++ src/hildon.h | 1 + 13 files changed, 934 insertions(+), 67 deletions(-) create mode 100644 src/hildon-window-stack-private.h create mode 100644 src/hildon-window-stack.c create mode 100644 src/hildon-window-stack.h diff --git a/ChangeLog b/ChangeLog index c7b3157..92be684 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,35 @@ 2008-11-25 Alberto Garcia + * doc/hildon-docs.sgml + * doc/hildon.types + * src/Makefile.am + * src/hildon.h + * src/hildon-window-stack-private.h + * src/hildon-window-stack.h + * src/hildon-window-stack.c: + New HildonWindowStack object, that adds support for multiple + stacks of windows per process. + + * src/hildon-stackable-window-private.h + * src/hildon-stackable-window.h + * src/hildon-stackable-window.c (hildon_stackable_window_set_stack) + (hildon_stackable_window_get_stack, hildon_stackable_window_map) + (hildon_stackable_window_show, hildon_stackable_window_hide) + (hildon_stackable_window_class_init) + (hildon_stackable_window_init): + Use HildonWindowStack for stack management. + + * src/hildon-program.c (hildon_program_pop_window_stack) + (hildon_program_peek_window_stack) + (hildon_program_go_to_root_window): + Add a fallback implementation to the deprecated functions using + HildonWindowStack. + + * examples/hildon-stackable-window-example.c: + Use the new HildonWindowStack API. + +2008-11-25 Alberto Garcia + * src/hildon-stackable-window-private.h * src/hildon-stackable-window.c (hildon_stackable_window_show) (hildon_stackable_window_hide, hildon_stackable_window_init): diff --git a/doc/hildon-docs.sgml b/doc/hildon-docs.sgml index f9b4e4c..ba38c06 100644 --- a/doc/hildon-docs.sgml +++ b/doc/hildon-docs.sgml @@ -103,6 +103,7 @@ + diff --git a/doc/hildon.types b/doc/hildon.types index f169706..a1bf776 100644 --- a/doc/hildon.types +++ b/doc/hildon.types @@ -37,6 +37,7 @@ #include #include #include +#include #include #include #include @@ -86,6 +87,7 @@ hildon_calendar_get_type hildon_dialog_get_type hildon_pannable_area_get_type hildon_stackable_window_get_type +hildon_window_stack_get_type hildon_app_menu_get_type hildon_entry_get_type hildon_text_view_get_type diff --git a/examples/hildon-stackable-window-example.c b/examples/hildon-stackable-window-example.c index a6ac028..83aed2d 100644 --- a/examples/hildon-stackable-window-example.c +++ b/examples/hildon-stackable-window-example.c @@ -3,8 +3,6 @@ * * Copyright (C) 2008 Nokia Corporation, all rights reserved. * - * Author: Karl Lattimer - * * 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 @@ -22,94 +20,186 @@ * */ -#include -#include -#include -#include #include "hildon.h" -#include -#include -#include +static gint global_stack_count = 1; + +static void +add_window (GtkWidget *button, + HildonStackableWindow *parent); static void -add_window (GtkWidget* w); +push_windows (GtkWidget *button, + GtkSpinButton *spin); + +static void +pop_windows (GtkWidget *button, + GtkSpinButton *spin); static GtkWidget* -new_window (gboolean ismain) +new_window (HildonStackableWindow *parent) { - GtkWidget *window, *hbbox, *add; - static int count = 0; - gchar* title; + GtkWidget *window, *hbbox, *vbox, *label, *add, *new; + GtkWidget *spin1hbox, *spin1label1, *spin1, *spin1label2, *pushbtn, *align1; + GtkWidget *spin2hbox, *spin2label1, *spin2, *spin2label2, *popbtn, *align2; + gint stack_number, win_number; + gchar *text; window = hildon_stackable_window_new (); - if (count == 0) - title = g_strdup ("main window"); - else - title = g_strdup_printf ("win%d", count); + if (parent) { + stack_number = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (parent), "stack-number")); + win_number = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (parent), "win-number")) + 1; + } else { + stack_number = global_stack_count++; + win_number = 1; + } + g_object_set_data (G_OBJECT (window), "stack-number", GINT_TO_POINTER (stack_number)); + g_object_set_data (G_OBJECT (window), "win-number", GINT_TO_POINTER (win_number)); - gtk_window_set_title (GTK_WINDOW (window), title); - g_free (title); + /* Window title */ + text = g_strdup_printf ("Stack number %d - window %d", stack_number, win_number); + gtk_window_set_title (GTK_WINDOW (window), text); + g_free (text); - count++; - - gtk_container_set_border_width (GTK_CONTAINER (window), 6); + /* Main label */ + text = g_strdup_printf ("Stack number %d\nWindow number %d", stack_number, win_number); + label = gtk_label_new (text); + g_free (text); hbbox = gtk_hbutton_box_new (); - gtk_container_add (GTK_CONTAINER (window), hbbox); - add = gtk_button_new_with_label ("Add a window"); + /* Button to push a window to the current stack */ + add = hildon_gtk_button_new (HILDON_SIZE_FINGER_HEIGHT | HILDON_SIZE_AUTO_WIDTH); + gtk_button_set_label (GTK_BUTTON (add), "Add a window to this stack"); gtk_box_pack_start (GTK_BOX (hbbox), add, FALSE, FALSE, 0); + g_signal_connect (G_OBJECT (add), "clicked", G_CALLBACK (add_window), window); + + /* Button to create a new stack */ + new = hildon_gtk_button_new (HILDON_SIZE_FINGER_HEIGHT | HILDON_SIZE_AUTO_WIDTH); + gtk_button_set_label (GTK_BUTTON (new), "Add a window to a new stack"); + gtk_box_pack_start (GTK_BOX (hbbox), new, FALSE, FALSE, 0); + g_signal_connect (G_OBJECT (new), "clicked", G_CALLBACK (add_window), NULL); + + /* Spinbox and button to push many windows */ + spin1hbox = gtk_hbox_new (FALSE, 0); + spin1label1 = gtk_label_new ("Push"); + spin1 = gtk_spin_button_new (GTK_ADJUSTMENT (gtk_adjustment_new (2, 2, 5, 1, 1, 1)), 1, 0); + gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (spin1), TRUE); + spin1label2 = gtk_label_new ("windows"); + pushbtn = hildon_gtk_button_new (HILDON_SIZE_FINGER_HEIGHT | HILDON_SIZE_AUTO_WIDTH); + gtk_button_set_label (GTK_BUTTON (pushbtn), "Push windows"); + gtk_box_pack_start (GTK_BOX (spin1hbox), spin1label1, FALSE, FALSE, 0); + gtk_box_pack_start (GTK_BOX (spin1hbox), spin1, FALSE, FALSE, 0); + gtk_box_pack_start (GTK_BOX (spin1hbox), spin1label2, FALSE, FALSE, 0); + gtk_box_pack_start (GTK_BOX (spin1hbox), pushbtn, FALSE, FALSE, 10); + align1 = gtk_alignment_new (0.5, 0.5, 0, 0); + gtk_container_add (GTK_CONTAINER (align1), spin1hbox); + g_signal_connect (G_OBJECT (pushbtn), "clicked", G_CALLBACK (push_windows), spin1); + + /* Spinbox and button to pop many windows */ + spin2hbox = gtk_hbox_new (FALSE, 0); + spin2label1 = gtk_label_new ("Pop"); + spin2 = gtk_spin_button_new (GTK_ADJUSTMENT (gtk_adjustment_new (2, 2, 5, 1, 1, 1)), 1, 0); + gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (spin2), TRUE); + spin2label2 = gtk_label_new ("windows"); + popbtn = hildon_gtk_button_new (HILDON_SIZE_FINGER_HEIGHT | HILDON_SIZE_AUTO_WIDTH); + gtk_button_set_label (GTK_BUTTON (popbtn), "Pop windows"); + gtk_box_pack_start (GTK_BOX (spin2hbox), spin2label1, FALSE, FALSE, 0); + gtk_box_pack_start (GTK_BOX (spin2hbox), spin2, FALSE, FALSE, 0); + gtk_box_pack_start (GTK_BOX (spin2hbox), spin2label2, FALSE, FALSE, 0); + gtk_box_pack_start (GTK_BOX (spin2hbox), popbtn, FALSE, FALSE, 10); + align2 = gtk_alignment_new (0.5, 0.5, 0, 0); + gtk_container_add (GTK_CONTAINER (align2), spin2hbox); + g_signal_connect (G_OBJECT (popbtn), "clicked", G_CALLBACK (pop_windows), spin2); + + vbox = gtk_vbox_new (FALSE, 0); + gtk_box_pack_start (GTK_BOX (vbox), label, TRUE, TRUE, 0); + gtk_box_pack_start (GTK_BOX (vbox), hbbox, FALSE, FALSE, 10); + gtk_box_pack_start (GTK_BOX (vbox), align1, FALSE, FALSE, 10); + gtk_box_pack_start (GTK_BOX (vbox), align2, FALSE, FALSE, 10); - g_signal_connect (G_OBJECT (add), "clicked", G_CALLBACK (add_window), NULL); + gtk_container_set_border_width (GTK_CONTAINER (window), 6); + gtk_container_add (GTK_CONTAINER (window), vbox); + gtk_widget_show_all (vbox); - if (!ismain) - { - GtkWidget *detach, *back; - detach = GTK_WIDGET (gtk_button_new_with_label ("Destroy")); - gtk_box_pack_end (GTK_BOX (hbbox), detach, FALSE, FALSE, 0); + return window; +} - g_signal_connect_swapped (G_OBJECT (detach), "clicked", - G_CALLBACK (gtk_widget_destroy), - HILDON_STACKABLE_WINDOW (window)); +static void +add_window (GtkWidget *button, + HildonStackableWindow *parent) +{ + HildonWindowStack *stack = NULL; + GtkWidget *window; + + if (parent) { + stack = hildon_stackable_window_get_stack (parent); + } else { + stack = hildon_window_stack_new (); + } - back = GTK_WIDGET (gtk_button_new_with_label ("Back to root")); - gtk_box_pack_end (GTK_BOX (hbbox), back, FALSE, FALSE, 0); + window = new_window (parent); - g_signal_connect_swapped (G_OBJECT (back), "clicked", - G_CALLBACK (hildon_program_go_to_root_window), - hildon_program_get_instance ()); + if (!stack) { + stack = hildon_window_stack_get_default (); } + hildon_window_stack_push_1 (stack, HILDON_STACKABLE_WINDOW (window)); - return window; + return; +} + +static void +push_windows (GtkWidget *button, + GtkSpinButton *spin) +{ + GList *l = NULL; + HildonWindowStack *stack = NULL; + HildonStackableWindow *parent; + gint nwindows = gtk_spin_button_get_value_as_int (spin); + + parent = HILDON_STACKABLE_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (spin))); + stack = hildon_stackable_window_get_stack (parent); + + while (nwindows > 0) { + parent = HILDON_STACKABLE_WINDOW (new_window (parent)); + l = g_list_append (l, parent); + nwindows--; + } + hildon_window_stack_push_list (stack, l); + g_list_free (l); } static void -add_window (GtkWidget *w) +pop_windows (GtkWidget *button, + GtkSpinButton *spin) { - GtkWidget *window = new_window (FALSE); - gtk_widget_show_all (window); + HildonWindowStack *stack = NULL; + HildonStackableWindow *win; + gint nwindows = gtk_spin_button_get_value_as_int (spin); - return; + win = HILDON_STACKABLE_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (spin))); + stack = hildon_stackable_window_get_stack (win); + + hildon_window_stack_pop (stack, nwindows, NULL); } int -main (int argc, +main (int argc, char **argv) { GtkWidget *window; hildon_gtk_init (&argc, &argv); - g_set_application_name ("stack"); + g_set_application_name ("hildon-stackable-window-example"); - window = new_window (TRUE); + window = new_window (NULL); - g_signal_connect (G_OBJECT (window), "delete_event", + g_signal_connect (G_OBJECT (window), "destroy", G_CALLBACK (gtk_main_quit), NULL); - gtk_widget_show_all (GTK_WIDGET (window)); + gtk_widget_show (window); gtk_main (); diff --git a/src/Makefile.am b/src/Makefile.am index 881130e..a861afa 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -68,6 +68,7 @@ libhildon_@API_VERSION_MAJOR@_la_SOURCES = \ hildon-caption.c \ hildon-window.c \ hildon-stackable-window.c \ + hildon-window-stack.c \ hildon-program.c \ hildon-code-dialog.c \ hildon-enum-types.c \ @@ -137,6 +138,7 @@ libhildon_@API_VERSION_MAJOR@_public_headers = \ hildon.h \ hildon-window.h \ hildon-stackable-window.h \ + hildon-window-stack.h \ hildon-wizard-dialog.h \ hildon-calendar.h \ hildon-pannable-area.h \ @@ -183,6 +185,7 @@ noinst_HEADERS = hildon-banner-private.h \ hildon-weekday-picker-private.h \ hildon-window-private.h \ hildon-stackable-window-private.h \ + hildon-window-stack-private.h \ hildon-wizard-dialog-private.h \ hildon-calendar-private.h \ hildon-app-menu-private.h \ diff --git a/src/hildon-program.c b/src/hildon-program.c index ca930c8..3cb63fa 100644 --- a/src/hildon-program.c +++ b/src/hildon-program.c @@ -87,6 +87,7 @@ #include "hildon-program.h" #include "hildon-program-private.h" #include "hildon-window-private.h" +#include "hildon-window-stack.h" static void hildon_program_init (HildonProgram *self); @@ -265,28 +266,34 @@ hildon_program_get_property (GObject *object, * hildon_program_pop_window_stack: * @self: A #HildonProgram * - * Deprecated: See #HildonWindowStack + * Deprecated: Use hildon_window_stack_pop() instead * - * Returns: %NULL + * Returns: A #HildonStackableWindow, or %NULL */ HildonStackableWindow * hildon_program_pop_window_stack (HildonProgram *self) { - return NULL; + HildonWindowStack *stack = hildon_window_stack_get_default (); + GtkWidget *win = hildon_window_stack_pop_1 (stack); + g_warning ("%s: this function is deprecated. Use hildon_window_stack_pop() instead", __FUNCTION__); + return win ? HILDON_STACKABLE_WINDOW (win) : NULL; } /** * hildon_program_peek_window_stack: * @self: A #HildonProgram * - * Deprecated: See #HildonWindowStack + * Deprecated: Use hildon_window_stack_peek() instead * - * Returns: %NULL + * Returns: A #HildonStackableWindow, or %NULL */ HildonStackableWindow * hildon_program_peek_window_stack (HildonProgram *self) { - return NULL; + HildonWindowStack *stack = hildon_window_stack_get_default (); + GtkWidget *win = hildon_window_stack_peek (stack); + g_warning ("%s: this function is deprecated. Use hildon_window_stack_peek() instead", __FUNCTION__); + return win ? HILDON_STACKABLE_WINDOW (win) : NULL; } /* Utilities */ @@ -726,4 +733,10 @@ hildon_program_get_is_topmost (HildonProgram *self) void hildon_program_go_to_root_window (HildonProgram *self) { + HildonWindowStack *stack = hildon_window_stack_get_default (); + gint n = hildon_window_stack_size (stack); + g_warning ("%s: this function is deprecated. Use hildon_window_stack_pop() instead.", __FUNCTION__); + if (n > 1) { + hildon_window_stack_pop (stack, n-1, NULL); + } } diff --git a/src/hildon-stackable-window-private.h b/src/hildon-stackable-window-private.h index aa1c03a..27d798d 100644 --- a/src/hildon-stackable-window-private.h +++ b/src/hildon-stackable-window-private.h @@ -32,12 +32,19 @@ typedef struct _HildonStackableWindowPrivate Hi struct _HildonStackableWindowPrivate { HildonAppMenu *app_menu; + HildonWindowStack *stack; + gint stack_position; }; #define HILDON_STACKABLE_WINDOW_GET_PRIVATE(obj) \ (G_TYPE_INSTANCE_GET_PRIVATE ((obj),\ HILDON_TYPE_STACKABLE_WINDOW, HildonStackableWindowPrivate)) +void G_GNUC_INTERNAL +hildon_stackable_window_set_stack (HildonStackableWindow *self, + HildonWindowStack *stack, + gint position); + G_END_DECLS #endif /* __HILDON_STACKABLE_WINDOW_PRIVATE_H__ */ diff --git a/src/hildon-stackable-window.c b/src/hildon-stackable-window.c index fda04d7..df95905 100644 --- a/src/hildon-stackable-window.c +++ b/src/hildon-stackable-window.c @@ -25,6 +25,7 @@ /** * SECTION:hildon-stackable-window * @short_description: Widget representing a stackable, top-level window in the Hildon framework. + * @see_also: #HildonWindowStack * * The #HildonStackableWindow is a GTK+ widget which represents a * top-level window in the Hildon framework. It is derived from @@ -32,14 +33,21 @@ * in a hierarchical way so users can go from any window back to the * application's root window. * + * The user can only see and interact with the window on top of the + * stack. Although all other windows are mapped and visible, they are + * obscured by the topmost one so in practice they appear as if they + * were hidden. + * * 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. + * previous one will be obscured by the new one. When the new window + * is destroyed, the previous one will appear again. * * Alternatively, you can remove a window from the top of the stack - * without destroying it by using - * hildon_program_pop_window_stack(). The window will be automatically - * hidden and the previous one will appear. + * without destroying it by using hildon_window_stack_pop(). The + * window will be automatically hidden and the previous one will + * appear. + * + * For advanced details on stack handling, see #HildonWindowStack * * * Basic HildonStackableWindow example @@ -53,7 +61,6 @@ * * // ... configure new window * - * // This automatically hides the previous window * gtk_widget_show (win); * } * @@ -84,13 +91,54 @@ #include #include +#include #include "hildon-stackable-window.h" #include "hildon-stackable-window-private.h" #include "hildon-app-menu-private.h" +#include "hildon-window-stack.h" +#include "hildon-window-stack-private.h" G_DEFINE_TYPE (HildonStackableWindow, hildon_stackable_window, HILDON_TYPE_WINDOW); +void G_GNUC_INTERNAL +hildon_stackable_window_set_stack (HildonStackableWindow *self, + HildonWindowStack *stack, + gint position) +{ + HildonStackableWindowPrivate *priv = HILDON_STACKABLE_WINDOW_GET_PRIVATE (self); + + if (stack) + g_object_ref (stack); + + if (priv->stack) + g_object_unref (priv->stack); + + priv->stack = stack; + priv->stack_position = position; +} + +/** + * hildon_stackable_window_get_stack: + * @self: a #HildonStackableWindow + * + * Returns the stack where window @self is on, or %NULL if the window + * is not stacked. + * + * Return value: a #HildonWindowStack, or %NULL + **/ +HildonWindowStack * +hildon_stackable_window_get_stack (HildonStackableWindow *self) +{ + HildonStackableWindowPrivate *priv; + + g_return_val_if_fail (HILDON_IS_STACKABLE_WINDOW (self), NULL); + + priv = HILDON_STACKABLE_WINDOW_GET_PRIVATE (self); + + return priv->stack; +} + /** * hildon_stackable_window_set_main_menu: * @self: a #HildonStackableWindow @@ -161,33 +209,49 @@ hildon_stackable_window_toggle_menu (HildonWindow *self, } static void -hildon_stackable_window_realize (GtkWidget *widget) +hildon_stackable_window_map (GtkWidget *widget) { GdkDisplay *display; Atom atom; - unsigned long val = 1; + unsigned long val; + HildonStackableWindowPrivate *priv = HILDON_STACKABLE_WINDOW_GET_PRIVATE (widget); - GTK_WIDGET_CLASS (hildon_stackable_window_parent_class)->realize (widget); + val = priv->stack_position; /* Set additional property "_HILDON_STACKABLE_WINDOW", to allow the WM to manage it as a stackable window. */ display = gdk_drawable_get_display (widget->window); atom = gdk_x11_get_xatom_by_name_for_display (display, "_HILDON_STACKABLE_WINDOW"); XChangeProperty (GDK_DISPLAY_XDISPLAY (display), GDK_WINDOW_XID (widget->window), atom, - XA_ATOM, 32, PropModeReplace, + XA_INTEGER, 32, PropModeReplace, (unsigned char *) &val, 1); + + GTK_WIDGET_CLASS (hildon_stackable_window_parent_class)->map (widget); } static void hildon_stackable_window_show (GtkWidget *widget) { - GTK_WIDGET_CLASS (hildon_stackable_window_parent_class)->show (widget); + HildonStackableWindowPrivate *priv = HILDON_STACKABLE_WINDOW_GET_PRIVATE (widget); + + /* Stack the window if not already stacked */ + if (priv->stack == NULL) { + HildonWindowStack *stack = hildon_window_stack_get_default (); + hildon_window_stack_push_1 (stack, HILDON_STACKABLE_WINDOW (widget)); + } else { + GTK_WIDGET_CLASS (hildon_stackable_window_parent_class)->show (widget); + } } static void hildon_stackable_window_hide (GtkWidget *widget) { + HildonStackableWindowPrivate *priv = HILDON_STACKABLE_WINDOW_GET_PRIVATE (widget); GTK_WIDGET_CLASS (hildon_stackable_window_parent_class)->hide (widget); + + if (priv->stack) { + hildon_window_stack_remove (HILDON_STACKABLE_WINDOW (widget)); + } } static void @@ -213,7 +277,7 @@ hildon_stackable_window_class_init (HildonStackableWindowClass *kla obj_class->finalize = hildon_stackable_window_finalize; - widget_class->realize = hildon_stackable_window_realize; + widget_class->map = hildon_stackable_window_map; widget_class->show = hildon_stackable_window_show; widget_class->hide = hildon_stackable_window_hide; @@ -228,6 +292,8 @@ hildon_stackable_window_init (HildonStackableWindow *self) HildonStackableWindowPrivate *priv = HILDON_STACKABLE_WINDOW_GET_PRIVATE (self); priv->app_menu = NULL; + priv->stack = NULL; + priv->stack_position = -1; } /** diff --git a/src/hildon-stackable-window.h b/src/hildon-stackable-window.h index 06a7bae..8507af5 100644 --- a/src/hildon-stackable-window.h +++ b/src/hildon-stackable-window.h @@ -56,6 +56,8 @@ G_BEGIN_DECLS HILDON_TYPE_STACKABLE_WINDOW, \ HildonStackableWindowClass)) +typedef struct _HildonWindowStack HildonWindowStack; + typedef struct _HildonStackableWindow HildonStackableWindow; typedef struct _HildonStackableWindowClass HildonStackableWindowClass; @@ -85,6 +87,9 @@ void hildon_stackable_window_set_main_menu (HildonStackableWindow *self, HildonAppMenu *menu); +HildonWindowStack * +hildon_stackable_window_get_stack (HildonStackableWindow *self); + G_END_DECLS #endif /* __HILDON_STACKABLE_WINDOW_H__ */ diff --git a/src/hildon-window-stack-private.h b/src/hildon-window-stack-private.h new file mode 100644 index 0000000..49a619f --- /dev/null +++ b/src/hildon-window-stack-private.h @@ -0,0 +1,33 @@ +/* + * This file is a part of hildon + * + * Copyright (C) 2008 Nokia Corporation, all rights reserved. + * + * 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, 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 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + +#ifndef __HILDON_WINDOW_STACK_PRIVATE_H__ +#define __HILDON_WINDOW_STACK_PRIVATE_H__ + +G_BEGIN_DECLS + +void G_GNUC_INTERNAL +hildon_window_stack_remove (HildonStackableWindow *win); + +G_END_DECLS + +#endif /* __HILDON_WINDOW_STACK_PRIVATE_H__ */ diff --git a/src/hildon-window-stack.c b/src/hildon-window-stack.c new file mode 100644 index 0000000..b7ec0f0 --- /dev/null +++ b/src/hildon-window-stack.c @@ -0,0 +1,486 @@ +/* + * This file is a part of hildon + * + * Copyright (C) 2008 Nokia Corporation, all rights reserved. + * + * 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, 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 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + +/** + * SECTION:hildon-window-stack + * @short_description: Object representing a stack of windows in the Hildon framework + * @see_also: #HildonStackableWindow + * + * The #HildonWindowStack is an object used to represent a stack of + * windows in the Hildon framework. + * + * Stacks contain all #HildonStackableWindows that are being + * shown. The user can only interact with the topmost window from each + * stack (as it covers all the others), but all of them are mapped and + * visible from the Gtk point of view. + * + * Each window can only be in one stack at a time. All stacked windows + * are visible and all visible windows are stacked. + * + * Each application has a default stack, and windows are automatically + * added to it when they are shown with gtk_widget_show(). + * + * Additional stacks can be created at any time using + * hildon_window_stack_new(). To add a window to a specific stack, use + * hildon_window_stack_push_1() (remember that, for the default stack, + * gtk_widget_show() can be used instead). + * + * To remove a window from a stack use hildon_window_stack_pop_1(), or + * simply gtk_widget_hide(). + * + * For more complex layout changes, applications can push and/or pop + * several windows at the same time in a single step. See + * hildon_window_stack_push(), hildon_window_stack_pop() and + * hildon_window_stack_pop_and_push() for more details. + */ + +#include "hildon-window-stack.h" +#include "hildon-window-stack-private.h" +#include "hildon-stackable-window-private.h" + +struct _HildonWindowStackPrivate +{ + GList *list; + GtkWindowGroup *group; +}; + +#define HILDON_WINDOW_STACK_GET_PRIVATE(obj) \ + (G_TYPE_INSTANCE_GET_PRIVATE ((obj),\ + HILDON_TYPE_WINDOW_STACK, HildonWindowStackPrivate)) + +G_DEFINE_TYPE (HildonWindowStack, hildon_window_stack, G_TYPE_OBJECT); + +/** + * hildon_window_stack_get_default: + * + * Returns the default window stack. This stack always exists and + * doesn't need to be created by the application. + * + * Return value: the default #HildonWindowStack + **/ +HildonWindowStack * +hildon_window_stack_get_default (void) +{ + static HildonWindowStack *stack = NULL; + if (G_UNLIKELY (stack == NULL)) { + stack = g_object_new (HILDON_TYPE_WINDOW_STACK, NULL); + stack->priv->group = gtk_window_get_group (NULL); + } + return stack; +} + +/** + * hildon_window_stack_new: + * + * Creates a new #HildonWindowStack. The stack is initially empty. + * + * Return value: a new #HildonWindowStack + **/ +HildonWindowStack * +hildon_window_stack_new (void) +{ + HildonWindowStack *stack = g_object_new (HILDON_TYPE_WINDOW_STACK, NULL); + stack->priv->group = gtk_window_group_new (); + return stack; +} + +/** + * hildon_window_stack_size: + * @stack: A #HildonWindowStack + * + * Returns the number of windows in @stack + * + * Return value: Number of windows in @stack + **/ +gint +hildon_window_stack_size (HildonWindowStack *stack) +{ + g_return_val_if_fail (HILDON_IS_WINDOW_STACK (stack), 0); + + return g_list_length (stack->priv->list); +} + +/* Remove a window from its stack, no matter its position */ +void G_GNUC_INTERNAL +hildon_window_stack_remove (HildonStackableWindow *win) +{ + HildonWindowStack *stack = hildon_stackable_window_get_stack (win); + + /* If the window is stacked */ + if (stack) { + hildon_stackable_window_set_stack (win, NULL, -1); + stack->priv->list = g_list_remove (stack->priv->list, win); + gtk_window_set_transient_for (GTK_WINDOW (win), NULL); + } +} + +/** + * hildon_window_stack_peek: + * @stack: A %HildonWindowStack + * + * Returns the window on top of @stack. The stack is never modified. + * + * Return value: the window on top of the stack, or %NULL if the stack + * is empty. + **/ +GtkWidget * +hildon_window_stack_peek (HildonWindowStack *stack) +{ + GtkWidget *win = NULL; + + g_return_val_if_fail (HILDON_IS_WINDOW_STACK (stack), NULL); + + if (stack->priv->list != NULL) { + win = GTK_WIDGET (stack->priv->list->data); + } + + return win; +} + +static gboolean +_hildon_window_stack_do_push (HildonWindowStack *stack, + HildonStackableWindow *win) +{ + HildonWindowStack *current_stack; + + g_return_val_if_fail (HILDON_IS_WINDOW_STACK (stack), FALSE); + g_return_val_if_fail (HILDON_IS_STACKABLE_WINDOW (win), FALSE); + + current_stack = hildon_stackable_window_get_stack (win); + + if (current_stack == NULL) { + GtkWidget *parent = hildon_window_stack_peek (stack); + + /* Push the window */ + hildon_stackable_window_set_stack (win, stack, g_list_length (stack->priv->list)); + stack->priv->list = g_list_prepend (stack->priv->list, win); + + /* Make the window part of the same group as its parent */ + if (parent) { + gtk_window_set_transient_for (GTK_WINDOW (win), GTK_WINDOW (parent)); + } else { + gtk_window_group_add_window (stack->priv->group, GTK_WINDOW (win)); + } + + return TRUE; + } else { + g_warning ("Trying to push a window that is already on a stack"); + return FALSE; + } +} + +static GtkWidget * +_hildon_window_stack_do_pop (HildonWindowStack *stack) +{ + GtkWidget *win = hildon_window_stack_peek (stack); + + if (win) + hildon_window_stack_remove (HILDON_STACKABLE_WINDOW (win)); + + return win; +} + +/** + * hildon_window_stack_push_1: + * @stack: A %HildonWindowStack + * @win: A %HildonStackableWindow + * + * Adds @win to the top of @stack, and shows it. The window must not + * be already stacked. + **/ +void +hildon_window_stack_push_1 (HildonWindowStack *stack, + HildonStackableWindow *win) +{ + if (_hildon_window_stack_do_push (stack, win)) + gtk_widget_show (GTK_WIDGET (win)); +} + +/** + * hildon_window_stack_pop_1: + * @stack: A %HildonWindowStack + * + * Removes the window on top of @stack, and hides it. If the stack is + * empty nothing happens. + * + * Return value: the window on top of the stack, or %NULL if the stack + * is empty. + **/ +GtkWidget * +hildon_window_stack_pop_1 (HildonWindowStack *stack) +{ + GtkWidget *win = _hildon_window_stack_do_pop (stack); + if (win) + gtk_widget_hide (win); + return win; +} + +/** + * hildon_window_stack_push_list: + * @stack: A %HildonWindowStack + * @list: A list of %HildonStackableWindows to push + * + * Pushes all windows in @list to the top of @stack, and shows + * them. Everything is done in a single transition, so the user will + * only see the last window in @list during this operation. None of + * the windows must be already stacked. + **/ +void +hildon_window_stack_push_list (HildonWindowStack *stack, + GList *list) +{ + HildonStackableWindow *win; + GList *l; + GList *pushed = NULL; + + g_return_if_fail (HILDON_IS_WINDOW_STACK (stack)); + + /* Stack all windows */ + for (l = list; l != NULL; l = g_list_next (l)) { + win = HILDON_STACKABLE_WINDOW (l->data); + if (win) { + _hildon_window_stack_do_push (stack, win); + pushed = g_list_prepend (pushed, win); + } else { + g_warning ("Trying to stack a non-stackable window!"); + } + } + + /* Show windows in reverse order (topmost first) */ + g_list_foreach (pushed, (GFunc) gtk_widget_show, NULL); + + g_list_free (pushed); +} + +/** + * hildon_window_stack_push: + * @stack: A %HildonWindowStack + * @win1: The first window to push + * @Varargs: A %NULL-terminated list of additional #HildonStackableWindows to push. + * + * Pushes all windows to the top of @stack, and shows them. Everything + * is done in a single transition, so the user will only see the last + * window. None of the windows must be already stacked. + **/ +void +hildon_window_stack_push (HildonWindowStack *stack, + HildonStackableWindow *win1, + ...) +{ + HildonStackableWindow *win; + GList *list = NULL; + va_list args; + + va_start (args, win1); + win = va_arg (args, HildonStackableWindow *); + + while (win != NULL) { + list = g_list_prepend (list, win); + win = va_arg (args, HildonStackableWindow *); + } + + va_end (args); + + hildon_window_stack_push_list (stack, list); + g_list_free (list); +} + +/** + * hildon_window_stack_pop: + * @stack: A %HildonWindowStack + * @nwindows: Number of windows to pop + * @popped_windows: if non-%NULL, the list of popped windows is stored here + * + * Pops @nwindows windows from @stack, and hides them. Everything is + * done in a single transition, so the user will not see any of the + * windows being popped in this operation. + * + * If @popped_windows is not %NULL, the list of popped windows is + * stored there (ordered bottom-up). That list must be freed by the + * user. + **/ +void +hildon_window_stack_pop (HildonWindowStack *stack, + gint nwindows, + GList **popped_windows) +{ + gint i; + GList *popped = NULL; + + g_return_if_fail (HILDON_IS_WINDOW_STACK (stack)); + g_return_if_fail (nwindows > 0); + g_return_if_fail (g_list_length (stack->priv->list) >= nwindows); + + /* Pop windows */ + for (i = 0; i < nwindows; i++) { + GtkWidget *win = _hildon_window_stack_do_pop (stack); + popped = g_list_prepend (popped, win); + } + + /* Hide windows in reverse order (topmost last) */ + g_list_foreach (popped, (GFunc) gtk_widget_hide, NULL); + + if (popped_windows) { + *popped_windows = popped; + } else { + g_list_free (popped); + } +} + +/** + * hildon_window_stack_pop_and_push_list: + * @stack: A %HildonWindowStack + * @nwindows: Number of windows to pop. + * @popped_windows: if non-%NULL, the list of popped windows is stored here + * @list: A list of %HildonStackableWindows to push + * + * Pops @nwindows windows from @stack (and hides them), then pushes + * all windows in @list (and shows them). Everything is done in a + * single transition, so the user will only see the last window from + * @list. None of the pushed windows must be already stacked. + * + * If @popped_windows is not %NULL, the list of popped windows is + * stored there (ordered bottom-up). That list must be freed by the + * user. + **/ +void +hildon_window_stack_pop_and_push_list (HildonWindowStack *stack, + gint nwindows, + GList **popped_windows, + GList *list) +{ + gint i; + GList *l; + GList *popped = NULL; + GList *pushed = NULL; + + g_return_if_fail (HILDON_IS_WINDOW_STACK (stack)); + g_return_if_fail (nwindows > 0); + g_return_if_fail (g_list_length (stack->priv->list) >= nwindows); + + /* Pop windows */ + for (i = 0; i < nwindows; i++) { + GtkWidget *win = _hildon_window_stack_do_pop (stack); + popped = g_list_prepend (popped, win); + } + + /* Push windows */ + for (l = list; l != NULL; l = g_list_next (l)) { + HildonStackableWindow *win = HILDON_STACKABLE_WINDOW (l->data); + if (win) { + _hildon_window_stack_do_push (stack, win); + pushed = g_list_prepend (pushed, win); + } else { + g_warning ("Trying to stack a non-stackable window!"); + } + } + + /* Show windows in reverse order (topmost first) */ + g_list_foreach (pushed, (GFunc) gtk_widget_show, NULL); + + /* Hide windows in reverse order (topmost last) */ + g_list_foreach (popped, (GFunc) gtk_widget_hide, NULL); + + g_list_free (pushed); + if (popped_windows) { + *popped_windows = popped; + } else { + g_list_free (popped); + } +} + +/** + * hildon_window_stack_pop_and_push: + * @stack: A %HildonWindowStack + * @nwindows: Number of windows to pop. + * @popped_windows: if non-%NULL, the list of popped windows is stored here + * @win1: The first window to push + * @Varargs: A %NULL-terminated list of additional #HildonStackableWindows to push. + * + * Pops @nwindows windows from @stack (and hides them), then pushes + * all passed windows (and shows them). Everything is done in a single + * transition, so the user will only see the last pushed window. None + * of the pushed windows must be already stacked. + * + * If @popped_windows is not %NULL, the list of popped windows is + * stored there (ordered bottom-up). That list must be freed by the + * user. + **/ +void +hildon_window_stack_pop_and_push (HildonWindowStack *stack, + gint nwindows, + GList **popped_windows, + HildonStackableWindow *win1, + ...) +{ + HildonStackableWindow *win; + GList *list = NULL; + va_list args; + + va_start (args, win1); + win = va_arg (args, HildonStackableWindow *); + + while (win != NULL) { + list = g_list_prepend (list, win); + win = va_arg (args, HildonStackableWindow *); + } + + va_end (args); + + hildon_window_stack_pop_and_push_list (stack, nwindows, popped_windows, list); + g_list_free (list); +} + +static void +hildon_window_stack_finalize (GObject *object) +{ + HildonWindowStack *stack = HILDON_WINDOW_STACK (object); + + if (stack->priv->list) + hildon_window_stack_pop (stack, hildon_window_stack_size (stack), NULL); + + if (stack->priv->group) + g_object_unref (stack->priv->group); + + G_OBJECT_CLASS (hildon_window_stack_parent_class)->finalize (object); +} + +static void +hildon_window_stack_class_init (HildonWindowStackClass *klass) +{ + GObjectClass *gobject_class = (GObjectClass *)klass; + + gobject_class->finalize = hildon_window_stack_finalize; + + g_type_class_add_private (klass, sizeof (HildonWindowStackPrivate)); +} + +static void +hildon_window_stack_init (HildonWindowStack *self) +{ + HildonWindowStackPrivate *priv; + + priv = self->priv = HILDON_WINDOW_STACK_GET_PRIVATE (self); + + priv->list = NULL; + priv->group = NULL; +} diff --git a/src/hildon-window-stack.h b/src/hildon-window-stack.h new file mode 100644 index 0000000..ac2df9d --- /dev/null +++ b/src/hildon-window-stack.h @@ -0,0 +1,130 @@ +/* + * This file is a part of hildon + * + * Copyright (C) 2008 Nokia Corporation, all rights reserved. + * + * 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, 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 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + +#ifndef __HILDON_WINDOW_STACK_H__ +#define __HILDON_WINDOW_STACK_H__ + +#include "hildon-stackable-window.h" + +G_BEGIN_DECLS + +#define HILDON_TYPE_WINDOW_STACK \ + (hildon_window_stack_get_type()) + +#define HILDON_WINDOW_STACK(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST ((obj), \ + HILDON_TYPE_WINDOW_STACK, \ + HildonWindowStack)) + +#define HILDON_WINDOW_STACK_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST ((klass), \ + HILDON_TYPE_WINDOW_STACK, \ + HildonWindowStackClass)) + +#define HILDON_IS_WINDOW_STACK(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE ((obj), \ + HILDON_TYPE_WINDOW_STACK)) + +#define HILDON_IS_WINDOW_STACK_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE ((klass), \ + HILDON_TYPE_WINDOW_STACK)) + +#define HILDON_WINDOW_STACK_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS ((obj), \ + HILDON_TYPE_WINDOW_STACK, \ + HildonWindowStackClass)) + +typedef struct _HildonWindowStackPrivate HildonWindowStackPrivate; + +typedef struct _HildonWindowStackClass HildonWindowStackClass; + +struct _HildonWindowStack +{ + GObject parent; + + /* private */ + HildonWindowStackPrivate *priv; +}; + +struct _HildonWindowStackClass +{ + GObjectClass parent_class; + + /* Padding for future extension */ + void (*_hildon_reserved1)(void); + void (*_hildon_reserved2)(void); + void (*_hildon_reserved3)(void); + void (*_hildon_reserved4)(void); +}; + +GType +hildon_window_stack_get_type (void) G_GNUC_CONST; + +HildonWindowStack * +hildon_window_stack_get_default (void); + +HildonWindowStack * +hildon_window_stack_new (void); + +gint +hildon_window_stack_size (HildonWindowStack *stack); + +GtkWidget * +hildon_window_stack_peek (HildonWindowStack *stack); + +void +hildon_window_stack_push (HildonWindowStack *stack, + HildonStackableWindow *win1, + ...); + +void +hildon_window_stack_push_list (HildonWindowStack *stack, + GList *list); + +void +hildon_window_stack_push_1 (HildonWindowStack *stack, + HildonStackableWindow *win); + +void +hildon_window_stack_pop (HildonWindowStack *stack, + gint nwindows, + GList **popped_windows); + +GtkWidget * +hildon_window_stack_pop_1 (HildonWindowStack *stack); + +void +hildon_window_stack_pop_and_push (HildonWindowStack *stack, + gint nwindows, + GList **popped_windows, + HildonStackableWindow *win1, + ...); + +void +hildon_window_stack_pop_and_push_list (HildonWindowStack *stack, + gint nwindows, + GList **popped_windows, + GList *list); + +G_END_DECLS + +#endif /* __HILDON_WINDOW_STACK_H__ */ diff --git a/src/hildon.h b/src/hildon.h index 8bb6777..04eccaa 100644 --- a/src/hildon.h +++ b/src/hildon.h @@ -69,6 +69,7 @@ #include "hildon-weekday-picker.h" #include "hildon-window.h" #include "hildon-stackable-window.h" +#include "hildon-window-stack.h" #include "hildon-wizard-dialog.h" #include "hildon-calendar.h" #include "hildon-bread-crumb-trail.h" -- 1.7.9.5