2 * This file is a part of hildon
4 * Copyright (C) 2008 Nokia Corporation, all rights reserved.
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public License
8 * as published by the Free Software Foundation; version 2.1 of
9 * the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful, but
12 * WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
24 * SECTION:hildon-window-stack
25 * @short_description: Object representing a stack of windows in the Hildon framework
26 * @see_also: #HildonStackableWindow
28 * The #HildonWindowStack is an object used to represent a stack of
29 * windows in the Hildon framework.
31 * Stacks contain all #HildonStackableWindow<!-- -->s that are being
32 * shown. The user can only interact with the topmost window from each
33 * stack (as it covers all the others), but all of them are mapped and
34 * visible from the Gtk point of view.
36 * Each window can only be in one stack at a time. All stacked windows
37 * are visible and all visible windows are stacked.
39 * Each application has a default stack, and windows are automatically
40 * added to it when they are shown with gtk_widget_show().
42 * Additional stacks can be created at any time using
43 * hildon_window_stack_new(). To add a window to a specific stack, use
44 * hildon_window_stack_push_1() (remember that, for the default stack,
45 * gtk_widget_show() can be used instead).
47 * To remove a window from a stack use hildon_window_stack_pop_1(), or
48 * simply gtk_widget_hide().
50 * For more complex layout changes, applications can push and/or pop
51 * several windows at the same time in a single step. See
52 * hildon_window_stack_push(), hildon_window_stack_pop() and
53 * hildon_window_stack_pop_and_push() for more details.
56 #include "hildon-window-stack.h"
57 #include "hildon-window-stack-private.h"
58 #include "hildon-stackable-window-private.h"
60 struct _HildonWindowStackPrivate
63 GtkWindowGroup *group;
64 GdkWindow *leader; /* X Window group hint for all windows in a group */
67 #define HILDON_WINDOW_STACK_GET_PRIVATE(obj) \
68 (G_TYPE_INSTANCE_GET_PRIVATE ((obj),\
69 HILDON_TYPE_WINDOW_STACK, HildonWindowStackPrivate))
71 G_DEFINE_TYPE (HildonWindowStack, hildon_window_stack, G_TYPE_OBJECT);
74 * hildon_window_stack_get_default:
76 * Returns the default window stack. This stack always exists and
77 * doesn't need to be created by the application.
79 * Return value: the default #HildonWindowStack
84 hildon_window_stack_get_default (void)
86 static HildonWindowStack *stack = NULL;
87 if (G_UNLIKELY (stack == NULL)) {
88 stack = g_object_new (HILDON_TYPE_WINDOW_STACK, NULL);
89 stack->priv->group = gtk_window_get_group (NULL);
95 * hildon_window_stack_new:
97 * Creates a new #HildonWindowStack. The stack is initially empty.
99 * Return value: a new #HildonWindowStack
104 hildon_window_stack_new (void)
106 HildonWindowStack *stack = g_object_new (HILDON_TYPE_WINDOW_STACK, NULL);
107 stack->priv->group = gtk_window_group_new ();
112 * hildon_window_stack_size:
113 * @stack: A #HildonWindowStack
115 * Returns the number of windows in @stack
117 * Return value: Number of windows in @stack
122 hildon_window_stack_size (HildonWindowStack *stack)
124 g_return_val_if_fail (HILDON_IS_WINDOW_STACK (stack), 0);
126 return g_list_length (stack->priv->list);
130 hildon_window_stack_get_leader_window (HildonWindowStack *stack,
133 /* Create the X Window group (leader) if we haven't. */
134 if (!stack->priv->leader) {
135 if (stack == hildon_window_stack_get_default ()) {
138 /* We're the default stack, use the default group. */
139 dpy = gtk_widget_get_display (win);
140 stack->priv->leader = gdk_display_get_default_group (dpy);
142 static GdkWindowAttr attr = {
143 .window_type = GDK_WINDOW_TOPLEVEL,
144 .x = 10, .y = 10, .width = 10, .height = 10,
145 .wclass = GDK_INPUT_OUTPUT, .event_mask = 0,
149 /* Create a new X Window group. */
150 root = gtk_widget_get_root_window (win);
151 stack->priv->leader = gdk_window_new (root, &attr, GDK_WA_X | GDK_WA_Y);
155 return stack->priv->leader;
158 /* Set the X Window group of a window when it is realized. */
160 hildon_window_stack_window_realized (GtkWidget *win,
161 HildonWindowStack *stack)
163 GdkWindow *leader = hildon_window_stack_get_leader_window (stack, win);
164 gdk_window_set_group (win->window, leader);
167 /* Remove a window from its stack, no matter its position */
169 hildon_window_stack_remove (HildonStackableWindow *win)
171 HildonWindowStack *stack = hildon_stackable_window_get_stack (win);
173 /* If the window is stacked */
177 hildon_stackable_window_set_stack (win, NULL, -1);
178 gtk_window_set_transient_for (GTK_WINDOW (win), NULL);
179 if (GTK_WIDGET (win)->window) {
180 gdk_window_set_group (GTK_WIDGET (win)->window, NULL);
183 /* If the window removed is in the middle of the stack, update
184 * transiency of other windows */
185 pos = g_list_find (stack->priv->list, win);
186 g_assert (pos != NULL);
188 GtkWindow *upper = GTK_WINDOW (pos->prev->data);
189 GtkWindow *lower = pos->next ? GTK_WINDOW (pos->next->data) : NULL;
190 gtk_window_set_transient_for (upper, lower);
193 stack->priv->list = g_list_remove (stack->priv->list, win);
195 g_signal_handlers_disconnect_by_func (win, hildon_window_stack_window_realized, stack);
200 * hildon_window_stack_peek:
201 * @stack: A %HildonWindowStack
203 * Returns the window on top of @stack. The stack is never modified.
205 * Return value: the window on top of the stack, or %NULL if the stack
211 hildon_window_stack_peek (HildonWindowStack *stack)
213 GtkWidget *win = NULL;
215 g_return_val_if_fail (HILDON_IS_WINDOW_STACK (stack), NULL);
217 if (stack->priv->list != NULL) {
218 win = GTK_WIDGET (stack->priv->list->data);
224 /* This function does everything to push a window to the stack _but_
225 * actually calling gtk_widget_show().
226 * It's up to each specific push function to decide the order in which
227 * to show windows. */
228 gboolean G_GNUC_INTERNAL
229 _hildon_window_stack_do_push (HildonWindowStack *stack,
230 HildonStackableWindow *win)
232 HildonWindowStack *current_stack;
234 g_return_val_if_fail (HILDON_IS_WINDOW_STACK (stack), FALSE);
235 g_return_val_if_fail (HILDON_IS_STACKABLE_WINDOW (win), FALSE);
237 current_stack = hildon_stackable_window_get_stack (win);
239 if (current_stack == NULL) {
240 GtkWidget *parent = hildon_window_stack_peek (stack);
242 /* Push the window */
243 hildon_stackable_window_set_stack (win, stack, g_list_length (stack->priv->list));
244 stack->priv->list = g_list_prepend (stack->priv->list, win);
246 /* Make the window part of the same group as its parent */
248 gtk_window_set_transient_for (GTK_WINDOW (win), GTK_WINDOW (parent));
250 gtk_window_group_add_window (stack->priv->group, GTK_WINDOW (win));
253 /* Set win's group after it's been realized. */
254 g_signal_connect (win, "realize",
255 G_CALLBACK (hildon_window_stack_window_realized),
260 g_warning ("Trying to push a window that is already on a stack");
266 _hildon_window_stack_do_pop (HildonWindowStack *stack)
268 GtkWidget *win = hildon_window_stack_peek (stack);
271 hildon_window_stack_remove (HILDON_STACKABLE_WINDOW (win));
277 * hildon_window_stack_push_1:
278 * @stack: A %HildonWindowStack
279 * @win: A %HildonStackableWindow
281 * Adds @win to the top of @stack, and shows it. The window must not
282 * be already stacked.
287 hildon_window_stack_push_1 (HildonWindowStack *stack,
288 HildonStackableWindow *win)
290 if (_hildon_window_stack_do_push (stack, win))
291 gtk_widget_show (GTK_WIDGET (win));
295 * hildon_window_stack_pop_1:
296 * @stack: A %HildonWindowStack
298 * Removes the window on top of @stack, and hides it. If the stack is
299 * empty nothing happens.
301 * Return value: the window on top of the stack, or %NULL if the stack
307 hildon_window_stack_pop_1 (HildonWindowStack *stack)
309 GtkWidget *win = _hildon_window_stack_do_pop (stack);
311 gtk_widget_hide (win);
316 * hildon_window_stack_push_list:
317 * @stack: A %HildonWindowStack
318 * @list: A list of %HildonStackableWindow<!-- -->s to push
320 * Pushes all windows in @list to the top of @stack, and shows
321 * them. Everything is done in a single transition, so the user will
322 * only see the last window in @list during this operation. None of
323 * the windows must be already stacked.
328 hildon_window_stack_push_list (HildonWindowStack *stack,
331 HildonStackableWindow *win;
333 GList *pushed = NULL;
335 g_return_if_fail (HILDON_IS_WINDOW_STACK (stack));
337 /* Stack all windows */
338 for (l = list; l != NULL; l = g_list_next (l)) {
339 win = HILDON_STACKABLE_WINDOW (l->data);
341 _hildon_window_stack_do_push (stack, win);
342 pushed = g_list_prepend (pushed, win);
344 g_warning ("Trying to stack a non-stackable window!");
348 /* Show windows in reverse order (topmost first) */
349 g_list_foreach (pushed, (GFunc) gtk_widget_show, NULL);
351 g_list_free (pushed);
355 * hildon_window_stack_push:
356 * @stack: A %HildonWindowStack
357 * @win1: The first window to push
358 * @Varargs: A %NULL-terminated list of additional #HildonStackableWindow<!-- -->s to push.
360 * Pushes all windows to the top of @stack, and shows them. Everything
361 * is done in a single transition, so the user will only see the last
362 * window. None of the windows must be already stacked.
367 hildon_window_stack_push (HildonWindowStack *stack,
368 HildonStackableWindow *win1,
371 HildonStackableWindow *win = win1;
375 va_start (args, win1);
377 while (win != NULL) {
378 list = g_list_prepend (list, win);
379 win = va_arg (args, HildonStackableWindow *);
384 list = g_list_reverse (list);
386 hildon_window_stack_push_list (stack, list);
391 * hildon_window_stack_pop:
392 * @stack: A %HildonWindowStack
393 * @nwindows: Number of windows to pop
394 * @popped_windows: if non-%NULL, the list of popped windows is stored here
396 * Pops @nwindows windows from @stack, and hides them. Everything is
397 * done in a single transition, so the user will not see any of the
398 * windows being popped in this operation.
400 * If @popped_windows is not %NULL, the list of popped windows is
401 * stored there (ordered bottom-up). That list must be freed by the
407 hildon_window_stack_pop (HildonWindowStack *stack,
409 GList **popped_windows)
412 GList *popped = NULL;
414 g_return_if_fail (HILDON_IS_WINDOW_STACK (stack));
415 g_return_if_fail (nwindows > 0);
416 g_return_if_fail (g_list_length (stack->priv->list) >= nwindows);
419 for (i = 0; i < nwindows; i++) {
420 GtkWidget *win = _hildon_window_stack_do_pop (stack);
421 popped = g_list_prepend (popped, win);
424 /* Hide windows in reverse order (topmost last) */
425 g_list_foreach (popped, (GFunc) gtk_widget_hide, NULL);
427 if (popped_windows) {
428 *popped_windows = popped;
430 g_list_free (popped);
435 * hildon_window_stack_pop_and_push_list:
436 * @stack: A %HildonWindowStack
437 * @nwindows: Number of windows to pop.
438 * @popped_windows: if non-%NULL, the list of popped windows is stored here
439 * @list: A list of %HildonStackableWindow<!-- -->s to push
441 * Pops @nwindows windows from @stack (and hides them), then pushes
442 * all windows in @list (and shows them). Everything is done in a
443 * single transition, so the user will only see the last window from
444 * @list. None of the pushed windows must be already stacked.
446 * If @popped_windows is not %NULL, the list of popped windows is
447 * stored there (ordered bottom-up). That list must be freed by the
453 hildon_window_stack_pop_and_push_list (HildonWindowStack *stack,
455 GList **popped_windows,
460 GList *popped = NULL;
461 GList *pushed = NULL;
463 g_return_if_fail (HILDON_IS_WINDOW_STACK (stack));
464 g_return_if_fail (nwindows > 0);
465 g_return_if_fail (g_list_length (stack->priv->list) >= nwindows);
468 for (i = 0; i < nwindows; i++) {
469 GtkWidget *win = _hildon_window_stack_do_pop (stack);
470 popped = g_list_prepend (popped, win);
474 for (l = list; l != NULL; l = g_list_next (l)) {
475 HildonStackableWindow *win = HILDON_STACKABLE_WINDOW (l->data);
477 _hildon_window_stack_do_push (stack, win);
478 pushed = g_list_prepend (pushed, win);
480 g_warning ("Trying to stack a non-stackable window!");
484 /* Show windows in reverse order (topmost first) */
485 g_list_foreach (pushed, (GFunc) gtk_widget_show, NULL);
487 /* Hide windows in reverse order (topmost last) */
488 g_list_foreach (popped, (GFunc) gtk_widget_hide, NULL);
490 g_list_free (pushed);
491 if (popped_windows) {
492 *popped_windows = popped;
494 g_list_free (popped);
499 * hildon_window_stack_pop_and_push:
500 * @stack: A %HildonWindowStack
501 * @nwindows: Number of windows to pop.
502 * @popped_windows: if non-%NULL, the list of popped windows is stored here
503 * @win1: The first window to push
504 * @Varargs: A %NULL-terminated list of additional #HildonStackableWindow<!-- -->s to push.
506 * Pops @nwindows windows from @stack (and hides them), then pushes
507 * all passed windows (and shows them). Everything is done in a single
508 * transition, so the user will only see the last pushed window. None
509 * of the pushed windows must be already stacked.
511 * If @popped_windows is not %NULL, the list of popped windows is
512 * stored there (ordered bottom-up). That list must be freed by the
518 hildon_window_stack_pop_and_push (HildonWindowStack *stack,
520 GList **popped_windows,
521 HildonStackableWindow *win1,
524 HildonStackableWindow *win = win1;
528 va_start (args, win1);
530 while (win != NULL) {
531 list = g_list_prepend (list, win);
532 win = va_arg (args, HildonStackableWindow *);
537 list = g_list_reverse (list);
539 hildon_window_stack_pop_and_push_list (stack, nwindows, popped_windows, list);
544 hildon_window_stack_finalize (GObject *object)
546 HildonWindowStack *stack = HILDON_WINDOW_STACK (object);
548 if (stack->priv->list)
549 hildon_window_stack_pop (stack, hildon_window_stack_size (stack), NULL);
551 if (stack->priv->group)
552 g_object_unref (stack->priv->group);
554 /* Since the default group stack shouldn't be finalized,
555 * it's safe to destroy the X Window group we created. */
556 if (stack->priv->leader)
557 gdk_window_destroy (stack->priv->leader);
559 G_OBJECT_CLASS (hildon_window_stack_parent_class)->finalize (object);
563 hildon_window_stack_class_init (HildonWindowStackClass *klass)
565 GObjectClass *gobject_class = (GObjectClass *)klass;
567 gobject_class->finalize = hildon_window_stack_finalize;
569 g_type_class_add_private (klass, sizeof (HildonWindowStackPrivate));
573 hildon_window_stack_init (HildonWindowStack *self)
575 HildonWindowStackPrivate *priv;
577 priv = self->priv = HILDON_WINDOW_STACK_GET_PRIVATE (self);