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: A stack of windows in Hildon applications.
26 * @see_also: #HildonStackableWindow
28 * The #HildonWindowStack is a stack of top-level windows.
30 * Stacks contain all #HildonStackableWindow<!-- -->s that are being
31 * shown. The user can only interact with the topmost window from each
32 * stack (as it covers all the others), but all of them are mapped and
33 * visible from the Gtk point of view.
35 * Each window can only be in one stack at a time. All stacked windows
36 * are visible and all visible windows are stacked.
38 * Each application has a default stack, and windows are automatically
39 * added to it when they are shown with gtk_widget_show().
41 * Additional stacks can be created at any time using
42 * hildon_window_stack_new(). To add a window to a specific stack, use
43 * hildon_window_stack_push_1() (remember that, for the default stack,
44 * gtk_widget_show() can be used instead).
46 * To remove a window from a stack use hildon_window_stack_pop_1(), or
47 * simply gtk_widget_hide().
49 * For more complex layout changes, applications can push and/or pop
50 * several windows at the same time in a single step. See
51 * hildon_window_stack_push(), hildon_window_stack_pop() and
52 * hildon_window_stack_pop_and_push() for more details.
55 #include "hildon-window-stack.h"
56 #include "hildon-window-stack-private.h"
57 #include "hildon-stackable-window-private.h"
59 struct _HildonWindowStackPrivate
62 GtkWindowGroup *group;
63 GdkWindow *leader; /* X Window group hint for all windows in a group */
66 #define HILDON_WINDOW_STACK_GET_PRIVATE(obj) \
67 (G_TYPE_INSTANCE_GET_PRIVATE ((obj),\
68 HILDON_TYPE_WINDOW_STACK, HildonWindowStackPrivate))
70 G_DEFINE_TYPE (HildonWindowStack, hildon_window_stack, G_TYPE_OBJECT);
77 hildon_window_stack_set_window_group (HildonWindowStack *stack,
78 GtkWindowGroup *group)
80 g_return_if_fail (HILDON_IS_WINDOW_STACK (stack));
81 g_return_if_fail (!group || GTK_IS_WINDOW_GROUP (group));
83 /* The window group is only to be set once during construction */
84 g_return_if_fail (stack->priv->group == NULL);
87 group = gtk_window_group_new ();
89 stack->priv->group = group;
92 static GtkWindowGroup *
93 hildon_window_stack_get_window_group (HildonWindowStack *stack)
95 g_return_val_if_fail (HILDON_IS_WINDOW_STACK (stack), NULL);
97 return stack->priv->group;
101 * hildon_window_stack_get_default:
103 * Returns the default window stack. This stack always exists and
104 * doesn't need to be created by the application.
106 * Return value: the default #HildonWindowStack
111 hildon_window_stack_get_default (void)
113 static HildonWindowStack *stack = NULL;
114 if (G_UNLIKELY (stack == NULL)) {
115 stack = g_object_new (HILDON_TYPE_WINDOW_STACK,
116 "window-group", gtk_window_get_group (NULL),
123 * hildon_window_stack_new:
125 * Creates a new #HildonWindowStack. The stack is initially empty.
127 * Return value: a new #HildonWindowStack
132 hildon_window_stack_new (void)
134 HildonWindowStack *stack = g_object_new (HILDON_TYPE_WINDOW_STACK, NULL);
139 * hildon_window_stack_size:
140 * @stack: A #HildonWindowStack
142 * Returns the number of windows in @stack
144 * Return value: Number of windows in @stack
149 hildon_window_stack_size (HildonWindowStack *stack)
151 g_return_val_if_fail (HILDON_IS_WINDOW_STACK (stack), 0);
153 return g_list_length (stack->priv->list);
157 hildon_window_stack_get_leader_window (HildonWindowStack *stack,
160 /* Create the X Window group (leader) if we haven't. */
161 if (!stack->priv->leader) {
162 if (stack == hildon_window_stack_get_default ()) {
165 /* We're the default stack, use the default group. */
166 dpy = gtk_widget_get_display (win);
167 stack->priv->leader = gdk_display_get_default_group (dpy);
169 static GdkWindowAttr attr = {
170 .window_type = GDK_WINDOW_TOPLEVEL,
171 .x = 10, .y = 10, .width = 10, .height = 10,
172 .wclass = GDK_INPUT_OUTPUT, .event_mask = 0,
176 /* Create a new X Window group. */
177 root = gtk_widget_get_root_window (win);
178 stack->priv->leader = gdk_window_new (root, &attr, GDK_WA_X | GDK_WA_Y);
182 return stack->priv->leader;
185 /* Set the X Window group of a window when it is realized. */
187 hildon_window_stack_window_realized (GtkWidget *win,
188 HildonWindowStack *stack)
190 GdkWindow *leader = hildon_window_stack_get_leader_window (stack, win);
191 gdk_window_set_group (win->window, leader);
194 /* Remove a window from its stack, no matter its position */
196 hildon_window_stack_remove (HildonStackableWindow *win)
198 HildonWindowStack *stack = hildon_stackable_window_get_stack (win);
200 /* If the window is stacked */
204 hildon_stackable_window_set_stack (win, NULL, -1);
205 gtk_window_set_transient_for (GTK_WINDOW (win), NULL);
206 if (GTK_WIDGET (win)->window) {
207 gdk_window_set_group (GTK_WIDGET (win)->window, NULL);
210 /* If the window removed is in the middle of the stack, update
211 * transiency of other windows */
212 pos = g_list_find (stack->priv->list, win);
213 g_assert (pos != NULL);
215 GtkWindow *upper = GTK_WINDOW (pos->prev->data);
216 GtkWindow *lower = pos->next ? GTK_WINDOW (pos->next->data) : NULL;
217 gtk_window_set_transient_for (upper, lower);
220 stack->priv->list = g_list_remove (stack->priv->list, win);
222 g_signal_handlers_disconnect_by_func (win, hildon_window_stack_window_realized, stack);
227 * hildon_window_stack_get_windows:
228 * @stack: a #HildonWindowStack
230 * Returns the list of windows on this stack (topmost first). The
231 * widgets in the list are not individually referenced. Once you are
232 * done with the list you must call g_list_free().
234 * Returns: a newly-allocated list of #HildonStackableWindow<!-- -->s
239 hildon_window_stack_get_windows (HildonWindowStack *stack)
241 g_return_val_if_fail (HILDON_IS_WINDOW_STACK (stack), NULL);
243 return g_list_copy (stack->priv->list);
247 * hildon_window_stack_peek:
248 * @stack: A %HildonWindowStack
250 * Returns the window on top of @stack. The stack is never modified.
252 * Return value: the window on top of the stack, or %NULL if the stack
258 hildon_window_stack_peek (HildonWindowStack *stack)
260 GtkWidget *win = NULL;
262 g_return_val_if_fail (HILDON_IS_WINDOW_STACK (stack), NULL);
264 if (stack->priv->list != NULL) {
265 win = GTK_WIDGET (stack->priv->list->data);
271 /* This function does everything to push a window to the stack _but_
272 * actually calling gtk_widget_show().
273 * It's up to each specific push function to decide the order in which
274 * to show windows. */
275 gboolean G_GNUC_INTERNAL
276 _hildon_window_stack_do_push (HildonWindowStack *stack,
277 HildonStackableWindow *win)
279 HildonWindowStack *current_stack;
281 g_return_val_if_fail (HILDON_IS_WINDOW_STACK (stack), FALSE);
282 g_return_val_if_fail (HILDON_IS_STACKABLE_WINDOW (win), FALSE);
284 current_stack = hildon_stackable_window_get_stack (win);
286 if (current_stack == NULL) {
287 GtkWidget *parent = hildon_window_stack_peek (stack);
291 pos = HILDON_STACKABLE_WINDOW_GET_PRIVATE (parent)->stack_position + 1;
294 /* Push the window */
295 hildon_stackable_window_set_stack (win, stack, pos);
296 stack->priv->list = g_list_prepend (stack->priv->list, win);
298 /* Make the window part of the same group as its parent */
300 gtk_window_set_transient_for (GTK_WINDOW (win), GTK_WINDOW (parent));
302 gtk_window_group_add_window (stack->priv->group, GTK_WINDOW (win));
305 /* Set window group */
306 if (GTK_WIDGET_REALIZED (win)) {
307 hildon_window_stack_window_realized (GTK_WIDGET (win), stack);
309 g_signal_connect (win, "realize",
310 G_CALLBACK (hildon_window_stack_window_realized),
316 g_warning ("Trying to push a window that is already on a stack");
322 _hildon_window_stack_do_pop (HildonWindowStack *stack)
324 GtkWidget *win = hildon_window_stack_peek (stack);
327 hildon_window_stack_remove (HILDON_STACKABLE_WINDOW (win));
333 * hildon_window_stack_push_1:
334 * @stack: A %HildonWindowStack
335 * @win: A %HildonStackableWindow
337 * Adds @win to the top of @stack, and shows it. The window must not
338 * be already stacked.
343 hildon_window_stack_push_1 (HildonWindowStack *stack,
344 HildonStackableWindow *win)
346 if (_hildon_window_stack_do_push (stack, win))
347 gtk_widget_show (GTK_WIDGET (win));
351 * hildon_window_stack_pop_1:
352 * @stack: A %HildonWindowStack
354 * Removes the window on top of @stack, and hides it. If the stack is
355 * empty nothing happens.
357 * Return value: the window on top of the stack, or %NULL if the stack
363 hildon_window_stack_pop_1 (HildonWindowStack *stack)
365 GtkWidget *win = _hildon_window_stack_do_pop (stack);
367 gtk_widget_hide (win);
372 * hildon_window_stack_push_list:
373 * @stack: A %HildonWindowStack
374 * @list: A list of %HildonStackableWindow<!-- -->s to push
376 * Pushes all windows in @list to the top of @stack, and shows
377 * them. Everything is done in a single transition, so the user will
378 * only see the last window in @list during this operation. None of
379 * the windows must be already stacked.
384 hildon_window_stack_push_list (HildonWindowStack *stack,
387 HildonStackableWindow *win;
389 GList *pushed = NULL;
391 g_return_if_fail (HILDON_IS_WINDOW_STACK (stack));
393 /* Stack all windows */
394 for (l = list; l != NULL; l = g_list_next (l)) {
395 win = HILDON_STACKABLE_WINDOW (l->data);
397 _hildon_window_stack_do_push (stack, win);
398 pushed = g_list_prepend (pushed, win);
400 g_warning ("Trying to stack a non-stackable window!");
404 /* Show windows in reverse order (topmost first) */
405 g_list_foreach (pushed, (GFunc) gtk_widget_show, NULL);
407 g_list_free (pushed);
411 * hildon_window_stack_push:
412 * @stack: A %HildonWindowStack
413 * @win1: The first window to push
414 * @Varargs: A %NULL-terminated list of additional #HildonStackableWindow<!-- -->s to push.
416 * Pushes all windows to the top of @stack, and shows them. Everything
417 * is done in a single transition, so the user will only see the last
418 * window. None of the windows must be already stacked.
423 hildon_window_stack_push (HildonWindowStack *stack,
424 HildonStackableWindow *win1,
427 HildonStackableWindow *win = win1;
431 va_start (args, win1);
433 while (win != NULL) {
434 list = g_list_prepend (list, win);
435 win = va_arg (args, HildonStackableWindow *);
440 list = g_list_reverse (list);
442 hildon_window_stack_push_list (stack, list);
447 * hildon_window_stack_pop:
448 * @stack: A %HildonWindowStack
449 * @nwindows: Number of windows to pop
450 * @popped_windows: if non-%NULL, the list of popped windows is stored here
452 * Pops @nwindows windows from @stack, and hides them. Everything is
453 * done in a single transition, so the user will not see any of the
454 * windows being popped in this operation.
456 * If @popped_windows is not %NULL, the list of popped windows is
457 * stored there (ordered bottom-up). That list must be freed by the
463 hildon_window_stack_pop (HildonWindowStack *stack,
465 GList **popped_windows)
468 GList *popped = NULL;
470 g_return_if_fail (HILDON_IS_WINDOW_STACK (stack));
471 g_return_if_fail (nwindows > 0);
472 g_return_if_fail (g_list_length (stack->priv->list) >= nwindows);
475 for (i = 0; i < nwindows; i++) {
476 GtkWidget *win = _hildon_window_stack_do_pop (stack);
477 popped = g_list_prepend (popped, win);
480 /* Hide windows in reverse order (topmost last) */
481 g_list_foreach (popped, (GFunc) gtk_widget_hide, NULL);
483 if (popped_windows) {
484 *popped_windows = popped;
486 g_list_free (popped);
491 * hildon_window_stack_pop_and_push_list:
492 * @stack: A %HildonWindowStack
493 * @nwindows: Number of windows to pop.
494 * @popped_windows: if non-%NULL, the list of popped windows is stored here
495 * @list: A list of %HildonStackableWindow<!-- -->s to push
497 * Pops @nwindows windows from @stack (and hides them), then pushes
498 * all windows in @list (and shows them). Everything is done in a
499 * single transition, so the user will only see the last window from
500 * @list. None of the pushed windows must be already stacked.
502 * If @popped_windows is not %NULL, the list of popped windows is
503 * stored there (ordered bottom-up). That list must be freed by the
509 hildon_window_stack_pop_and_push_list (HildonWindowStack *stack,
511 GList **popped_windows,
514 gint i, topmost_index;
516 GList *popped = NULL;
517 GList *pushed = NULL;
518 HildonStackableWindowPrivate *priv;
520 g_return_if_fail (HILDON_IS_WINDOW_STACK (stack));
521 g_return_if_fail (nwindows > 0);
522 g_return_if_fail (g_list_length (stack->priv->list) >= nwindows);
524 /* Store the index of the topmost window */
525 priv = HILDON_STACKABLE_WINDOW_GET_PRIVATE (hildon_window_stack_peek (stack));
526 topmost_index = priv->stack_position;
529 for (i = 0; i < nwindows; i++) {
530 GtkWidget *win = _hildon_window_stack_do_pop (stack);
531 popped = g_list_prepend (popped, win);
535 for (l = list; l != NULL; l = g_list_next (l)) {
536 HildonStackableWindow *win = HILDON_STACKABLE_WINDOW (l->data);
538 _hildon_window_stack_do_push (stack, win);
539 pushed = g_list_prepend (pushed, win);
541 g_warning ("Trying to stack a non-stackable window!");
545 if (pushed != NULL) {
546 /* The WM will be confused if the old topmost window and the new
547 * one have the same index, so make sure that they're different */
548 priv = HILDON_STACKABLE_WINDOW_GET_PRIVATE (hildon_window_stack_peek (stack));
549 if (priv->stack_position == topmost_index) {
550 priv->stack_position++;
554 /* Show windows in reverse order (topmost first) */
555 g_list_foreach (pushed, (GFunc) gtk_widget_show, NULL);
557 /* Hide windows in reverse order (topmost last) */
558 g_list_foreach (popped, (GFunc) gtk_widget_hide, NULL);
560 g_list_free (pushed);
561 if (popped_windows) {
562 *popped_windows = popped;
564 g_list_free (popped);
569 * hildon_window_stack_pop_and_push:
570 * @stack: A %HildonWindowStack
571 * @nwindows: Number of windows to pop.
572 * @popped_windows: if non-%NULL, the list of popped windows is stored here
573 * @win1: The first window to push
574 * @Varargs: A %NULL-terminated list of additional #HildonStackableWindow<!-- -->s to push.
576 * Pops @nwindows windows from @stack (and hides them), then pushes
577 * all passed windows (and shows them). Everything is done in a single
578 * transition, so the user will only see the last pushed window. None
579 * of the pushed windows must be already stacked.
581 * If @popped_windows is not %NULL, the list of popped windows is
582 * stored there (ordered bottom-up). That list must be freed by the
588 hildon_window_stack_pop_and_push (HildonWindowStack *stack,
590 GList **popped_windows,
591 HildonStackableWindow *win1,
594 HildonStackableWindow *win = win1;
598 va_start (args, win1);
600 while (win != NULL) {
601 list = g_list_prepend (list, win);
602 win = va_arg (args, HildonStackableWindow *);
607 list = g_list_reverse (list);
609 hildon_window_stack_pop_and_push_list (stack, nwindows, popped_windows, list);
614 hildon_window_stack_finalize (GObject *object)
616 HildonWindowStack *stack = HILDON_WINDOW_STACK (object);
618 if (stack->priv->list)
619 hildon_window_stack_pop (stack, hildon_window_stack_size (stack), NULL);
621 if (stack->priv->group)
622 g_object_unref (stack->priv->group);
624 /* Since the default group stack shouldn't be finalized,
625 * it's safe to destroy the X Window group we created. */
626 if (stack->priv->leader)
627 gdk_window_destroy (stack->priv->leader);
629 G_OBJECT_CLASS (hildon_window_stack_parent_class)->finalize (object);
633 hildon_window_stack_set_property (GObject *object,
638 HildonWindowStack *stack = HILDON_WINDOW_STACK (object);
643 hildon_window_stack_set_window_group (stack, g_value_get_object (value));
646 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
652 hildon_window_stack_get_property (GObject *object,
657 HildonWindowStack *stack = HILDON_WINDOW_STACK (object);
662 g_value_set_object (value, hildon_window_stack_get_window_group (stack));
665 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
671 hildon_window_stack_class_init (HildonWindowStackClass *klass)
673 GObjectClass *gobject_class = (GObjectClass *)klass;
675 gobject_class->set_property = hildon_window_stack_set_property;
676 gobject_class->get_property = hildon_window_stack_get_property;
677 gobject_class->finalize = hildon_window_stack_finalize;
679 g_object_class_install_property (
682 g_param_spec_object (
684 "GtkWindowGroup for this stack",
685 "GtkWindowGroup that all windows on this stack belong to",
686 GTK_TYPE_WINDOW_GROUP,
687 G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
689 g_type_class_add_private (klass, sizeof (HildonWindowStackPrivate));
693 hildon_window_stack_init (HildonWindowStack *self)
695 HildonWindowStackPrivate *priv;
697 priv = self->priv = HILDON_WINDOW_STACK_GET_PRIVATE (self);