* src/hildon-stackable-window.c (hildon_stackable_window_map, hildon_stackable_window...
[hildon] / src / hildon-stackable-window.c
1 /*
2  * This file is a part of hildon
3  *
4  * Copyright (C) 2008 Nokia Corporation, all rights reserved.
5  *
6  * Contact: Karl Lattimer <karl.lattimer@nokia.com>
7  *
8  * This library is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Lesser General Public License
10  * as published by the Free Software Foundation; version 2.1 of
11  * the License, or (at your option) any later version.
12  *
13  * This library is distributed in the hope that it will be useful, but
14  * WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16  * Lesser General Public License for more details.
17  *
18  * You should have received a copy of the GNU Lesser General Public
19  * License along with this library; if not, write to the Free Software
20  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
21  * 02110-1301 USA
22  *
23  */
24
25 /**
26  * SECTION:hildon-stackable-window
27  * @short_description: Widget representing a stackable, top-level window in the Hildon framework.
28  *
29  * The #HildonStackableWindow is a GTK+ widget which represents a
30  * top-level window in the Hildon framework. It is derived from
31  * #HildonWindow. Applications that use stackable windows are organized
32  * in a hierarchical way so users can go from any window back to the
33  * application's root window.
34  */
35
36 #include                                        <X11/X.h>
37 #include                                        <X11/Xatom.h>
38 #include                                        "hildon-stackable-window.h"
39 #include                                        "hildon-stackable-window-private.h"
40 #include                                        "hildon-program.h"
41 #include                                        "hildon-window-private.h"
42 #include                                        "hildon-program-private.h"
43
44 G_DEFINE_TYPE (HildonStackableWindow, hildon_stackable_window, HILDON_TYPE_WINDOW);
45
46 void G_GNUC_INTERNAL
47 hildon_stackable_window_set_going_home          (HildonStackableWindow *self,
48                                                  gboolean going_home)
49 {
50     HildonStackableWindowPrivate *priv = HILDON_STACKABLE_WINDOW_GET_PRIVATE (self);
51     priv->going_home = going_home;
52 }
53
54 gboolean G_GNUC_INTERNAL
55 hildon_stackable_window_get_going_home          (HildonStackableWindow *self)
56 {
57     HildonStackableWindowPrivate *priv = HILDON_STACKABLE_WINDOW_GET_PRIVATE (self);
58     return priv->going_home;
59 }
60
61 static GSList*
62 get_window_list                                 (GtkWidget *widget)
63 {
64     HildonWindowPrivate *wpriv;
65     HildonProgramPrivate *ppriv;
66     GSList *retval = NULL;
67     g_return_val_if_fail (widget != NULL, NULL);
68
69     wpriv = HILDON_WINDOW_GET_PRIVATE (widget);
70     g_assert (wpriv != NULL);
71
72     if (wpriv->program)
73     {
74         ppriv = HILDON_PROGRAM_GET_PRIVATE (wpriv->program);
75         g_assert (ppriv != NULL);
76         retval = ppriv->windows;
77     }
78
79     return retval;
80 }
81
82 static GtkWidget*
83 get_previous_window_if_last                     (GtkWidget *widget)
84 {
85     GtkWidget *retval = NULL;
86     GSList *iter = get_window_list (widget);
87     GSList *previous = NULL;
88
89     /* Return NULL if the window hasn't been added to the HildonProgram */
90     if (iter == NULL)
91     {
92         return NULL;
93     }
94
95     /* Go to the end of the window list */
96     while (iter->next != NULL)
97     {
98         previous = iter;
99         iter = iter->next;
100     }
101
102     if ((iter->data == widget) && (previous != NULL))
103     {
104         retval = GTK_WIDGET (previous->data);
105     }
106
107     return retval;
108 }
109
110 static void
111 hildon_stackable_window_map                     (GtkWidget *widget)
112 {
113     GtkWidget *previous = get_previous_window_if_last (widget);
114
115     if (GTK_WIDGET_CLASS (hildon_stackable_window_parent_class)->map)
116         GTK_WIDGET_CLASS (hildon_stackable_window_parent_class)->map (widget);
117
118     if (HILDON_IS_STACKABLE_WINDOW (previous) && GTK_WIDGET_VISIBLE (previous))
119         gtk_widget_hide (previous);
120 }
121
122 static void
123 hildon_stackable_window_unmap                   (GtkWidget *widget)
124 {
125     GtkWidget *previous = get_previous_window_if_last (widget);
126
127     if (GTK_WIDGET_CLASS (hildon_stackable_window_parent_class)->unmap)
128         GTK_WIDGET_CLASS (hildon_stackable_window_parent_class)->unmap (widget);
129
130     if (HILDON_IS_STACKABLE_WINDOW (previous) && !GTK_WIDGET_VISIBLE (previous) &&
131         !hildon_stackable_window_get_going_home (HILDON_STACKABLE_WINDOW (widget)))
132     {
133         gtk_widget_show (previous);
134     }
135 }
136
137 static void
138 hildon_stackable_window_unset_program           (HildonWindow *hwin)
139 {
140     GtkWidget *previous = get_previous_window_if_last (GTK_WIDGET (hwin));
141
142     if (HILDON_WINDOW_CLASS (hildon_stackable_window_parent_class)->unset_program)
143         HILDON_WINDOW_CLASS (hildon_stackable_window_parent_class)->unset_program (hwin);
144
145     if (HILDON_IS_STACKABLE_WINDOW (previous) && !GTK_WIDGET_VISIBLE (previous) &&
146         !hildon_stackable_window_get_going_home (HILDON_STACKABLE_WINDOW (hwin)))
147     {
148         gtk_widget_show (previous);
149     }
150 }
151
152 static void
153 hildon_stackable_window_realize                 (GtkWidget *widget)
154 {
155     GdkDisplay *display;
156     Atom atom;
157
158     GTK_WIDGET_CLASS (hildon_stackable_window_parent_class)->realize (widget);
159
160     /* Set the window type to "_HILDON_WM_WINDOW_TYPE_STACKABLE", to allow the WM to manage
161        it properly.  */
162     display = gdk_drawable_get_display (widget->window);
163     atom = gdk_x11_get_xatom_by_name_for_display (display, "_HILDON_WM_WINDOW_TYPE_STACKABLE");
164     XChangeProperty (GDK_DISPLAY_XDISPLAY (display), GDK_WINDOW_XID (widget->window),
165                      gdk_x11_get_xatom_by_name_for_display (display, "_NET_WM_WINDOW_TYPE"),
166                      XA_ATOM, 32, PropModeAppend,
167                      (guchar *)&atom, 1);
168 }
169
170 static gboolean
171 hildon_stackable_window_delete_event            (GtkWidget *widget,
172                                                  GdkEventAny *event)
173 {
174     GSList *list = get_window_list (widget);
175     list = g_slist_find (list, widget);
176
177     /* Ignore the delete event if this is not the topmost window */
178     if (list != NULL && list->next != NULL)
179         return TRUE;
180     else if (GTK_WIDGET_CLASS (hildon_stackable_window_parent_class)->delete_event)
181         return GTK_WIDGET_CLASS (hildon_stackable_window_parent_class)->delete_event (widget, event);
182     else
183         return FALSE;
184 }
185
186 static void
187 hildon_stackable_window_class_init              (HildonStackableWindowClass *klass)
188 {
189     GtkWidgetClass    *widget_class = GTK_WIDGET_CLASS (klass);
190     HildonWindowClass *window_class = HILDON_WINDOW_CLASS (klass);
191
192     widget_class->map               = hildon_stackable_window_map;
193     widget_class->unmap             = hildon_stackable_window_unmap;
194     widget_class->realize           = hildon_stackable_window_realize;
195     widget_class->delete_event      = hildon_stackable_window_delete_event;
196
197     window_class->unset_program     = hildon_stackable_window_unset_program;
198
199     g_type_class_add_private (klass, sizeof (HildonStackableWindowPrivate));
200 }
201
202 static void
203 hildon_stackable_window_init                    (HildonStackableWindow *self)
204 {
205     hildon_stackable_window_set_going_home (self, FALSE);
206 }
207
208 /**
209  * hildon_stackable_window_new:
210  *
211  * Creates a new #HildonStackableWindow.
212  *
213  * Return value: A #HildonStackableWindow
214  **/
215 GtkWidget*
216 hildon_stackable_window_new                     (void)
217 {
218     HildonStackableWindow *newwindow = g_object_new (HILDON_TYPE_STACKABLE_WINDOW, NULL);
219
220     return GTK_WIDGET (newwindow);
221 }