* src/hildon-program.c * src/hildon-stackable-window.c: Update documentation.
[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  * To add a window to the stack, use hildon_program_add_window(). Once
36  * you show the new window, the previous one will be automatically
37  * hidden. When the new window is destroyed, the previous one will
38  * appear again.
39  *
40  * <example>
41  * <programlisting>
42  * void
43  * show_first_window (void)
44  * {
45  *     HildonProgram *program;
46  *     GtkWidget *win1;
47  * <!-- -->
48  *     program = hildon_program_get_instance ();
49  *     win1 = hildon_stackable_window_new ();
50  * <!-- -->
51  *     // ... configure first window
52  * <!-- -->
53  *     hildon_program_add_window (program, HILDON_WINDOW (win1));
54  *     gtk_widget_show (win1);
55  * }
56  * <!-- -->
57  * void
58  * show_second_window (void)
59  * {
60  *     HildonProgram *program;
61  *     GtkWidget *win2;
62  * <!-- -->
63  *     program = hildon_program_get_instance ();
64  *     win2 = hildon_stackable_window_new ();
65  * <!-- -->
66  *     // ... configure second window
67  * <!-- -->
68  *     hildon_program_add_window (program, HILDON_WINDOW (win2));
69  *     gtk_widget_show (win2);
70  * }
71  * </programlisting>
72  * </example>
73  */
74
75 #include                                        <X11/X.h>
76 #include                                        <X11/Xatom.h>
77 #include                                        "hildon-stackable-window.h"
78 #include                                        "hildon-stackable-window-private.h"
79 #include                                        "hildon-program.h"
80 #include                                        "hildon-window-private.h"
81 #include                                        "hildon-program-private.h"
82
83 G_DEFINE_TYPE (HildonStackableWindow, hildon_stackable_window, HILDON_TYPE_WINDOW);
84
85 void G_GNUC_INTERNAL
86 hildon_stackable_window_set_going_home          (HildonStackableWindow *self,
87                                                  gboolean going_home)
88 {
89     HildonStackableWindowPrivate *priv = HILDON_STACKABLE_WINDOW_GET_PRIVATE (self);
90     priv->going_home = going_home;
91 }
92
93 gboolean G_GNUC_INTERNAL
94 hildon_stackable_window_get_going_home          (HildonStackableWindow *self)
95 {
96     HildonStackableWindowPrivate *priv = HILDON_STACKABLE_WINDOW_GET_PRIVATE (self);
97     return priv->going_home;
98 }
99
100 static GSList*
101 get_window_list                                 (GtkWidget *widget)
102 {
103     HildonWindowPrivate *wpriv;
104     HildonProgramPrivate *ppriv;
105     GSList *retval = NULL;
106     g_return_val_if_fail (widget != NULL, NULL);
107
108     wpriv = HILDON_WINDOW_GET_PRIVATE (widget);
109     g_assert (wpriv != NULL);
110
111     if (wpriv->program)
112     {
113         ppriv = HILDON_PROGRAM_GET_PRIVATE (wpriv->program);
114         g_assert (ppriv != NULL);
115         retval = ppriv->windows;
116     }
117
118     return retval;
119 }
120
121 static GtkWidget*
122 get_previous_window_if_last                     (GtkWidget *widget)
123 {
124     GtkWidget *retval = NULL;
125     GSList *iter = get_window_list (widget);
126     GSList *previous = NULL;
127
128     /* Return NULL if the window hasn't been added to the HildonProgram */
129     if (iter == NULL)
130     {
131         return NULL;
132     }
133
134     /* Go to the end of the window list */
135     while (iter->next != NULL)
136     {
137         previous = iter;
138         iter = iter->next;
139     }
140
141     if ((iter->data == widget) && (previous != NULL))
142     {
143         retval = GTK_WIDGET (previous->data);
144     }
145
146     return retval;
147 }
148
149 static void
150 hildon_stackable_window_map                     (GtkWidget *widget)
151 {
152     GtkWidget *previous = get_previous_window_if_last (widget);
153
154     if (GTK_WIDGET_CLASS (hildon_stackable_window_parent_class)->map)
155         GTK_WIDGET_CLASS (hildon_stackable_window_parent_class)->map (widget);
156
157     if (HILDON_IS_STACKABLE_WINDOW (previous) && GTK_WIDGET_VISIBLE (previous))
158         gtk_widget_hide (previous);
159 }
160
161 static void
162 hildon_stackable_window_unmap                   (GtkWidget *widget)
163 {
164     GtkWidget *previous = get_previous_window_if_last (widget);
165
166     if (GTK_WIDGET_CLASS (hildon_stackable_window_parent_class)->unmap)
167         GTK_WIDGET_CLASS (hildon_stackable_window_parent_class)->unmap (widget);
168
169     if (HILDON_IS_STACKABLE_WINDOW (previous) && !GTK_WIDGET_VISIBLE (previous) &&
170         !hildon_stackable_window_get_going_home (HILDON_STACKABLE_WINDOW (widget)))
171     {
172         gtk_widget_show (previous);
173     }
174 }
175
176 static void
177 hildon_stackable_window_unset_program           (HildonWindow *hwin)
178 {
179     GtkWidget *previous = get_previous_window_if_last (GTK_WIDGET (hwin));
180
181     if (HILDON_WINDOW_CLASS (hildon_stackable_window_parent_class)->unset_program)
182         HILDON_WINDOW_CLASS (hildon_stackable_window_parent_class)->unset_program (hwin);
183
184     if (HILDON_IS_STACKABLE_WINDOW (previous) && !GTK_WIDGET_VISIBLE (previous) &&
185         !hildon_stackable_window_get_going_home (HILDON_STACKABLE_WINDOW (hwin)))
186     {
187         gtk_widget_show (previous);
188     }
189 }
190
191 static void
192 hildon_stackable_window_realize                 (GtkWidget *widget)
193 {
194     GdkDisplay *display;
195     Atom atom;
196
197     GTK_WIDGET_CLASS (hildon_stackable_window_parent_class)->realize (widget);
198
199     /* Set the window type to "_HILDON_WM_WINDOW_TYPE_STACKABLE", to allow the WM to manage
200        it properly.  */
201     display = gdk_drawable_get_display (widget->window);
202     atom = gdk_x11_get_xatom_by_name_for_display (display, "_HILDON_WM_WINDOW_TYPE_STACKABLE");
203     XChangeProperty (GDK_DISPLAY_XDISPLAY (display), GDK_WINDOW_XID (widget->window),
204                      gdk_x11_get_xatom_by_name_for_display (display, "_NET_WM_WINDOW_TYPE"),
205                      XA_ATOM, 32, PropModeAppend,
206                      (guchar *)&atom, 1);
207 }
208
209 static gboolean
210 hildon_stackable_window_delete_event            (GtkWidget *widget,
211                                                  GdkEventAny *event)
212 {
213     GSList *list = get_window_list (widget);
214     list = g_slist_find (list, widget);
215
216     /* Ignore the delete event if this is not the topmost window */
217     if (list != NULL && list->next != NULL)
218         return TRUE;
219     else if (GTK_WIDGET_CLASS (hildon_stackable_window_parent_class)->delete_event)
220         return GTK_WIDGET_CLASS (hildon_stackable_window_parent_class)->delete_event (widget, event);
221     else
222         return FALSE;
223 }
224
225 static void
226 hildon_stackable_window_class_init              (HildonStackableWindowClass *klass)
227 {
228     GtkWidgetClass    *widget_class = GTK_WIDGET_CLASS (klass);
229     HildonWindowClass *window_class = HILDON_WINDOW_CLASS (klass);
230
231     widget_class->map               = hildon_stackable_window_map;
232     widget_class->unmap             = hildon_stackable_window_unmap;
233     widget_class->realize           = hildon_stackable_window_realize;
234     widget_class->delete_event      = hildon_stackable_window_delete_event;
235
236     window_class->unset_program     = hildon_stackable_window_unset_program;
237
238     g_type_class_add_private (klass, sizeof (HildonStackableWindowPrivate));
239 }
240
241 static void
242 hildon_stackable_window_init                    (HildonStackableWindow *self)
243 {
244     hildon_stackable_window_set_going_home (self, FALSE);
245 }
246
247 /**
248  * hildon_stackable_window_new:
249  *
250  * Creates a new #HildonStackableWindow.
251  *
252  * Return value: A #HildonStackableWindow
253  **/
254 GtkWidget*
255 hildon_stackable_window_new                     (void)
256 {
257     HildonStackableWindow *newwindow = g_object_new (HILDON_TYPE_STACKABLE_WINDOW, NULL);
258
259     return GTK_WIDGET (newwindow);
260 }