2008-11-25 Alberto Garcia <agarcia@igalia.com>
[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  * @see_also: #HildonWindowStack
29  *
30  * The #HildonStackableWindow is a GTK+ widget which represents a
31  * top-level window in the Hildon framework. It is derived from
32  * #HildonWindow. Applications that use stackable windows are organized
33  * in a hierarchical way so users can go from any window back to the
34  * application's root window.
35  *
36  * The user can only see and interact with the window on top of the
37  * stack. Although all other windows are mapped and visible, they are
38  * obscured by the topmost one so in practice they appear as if they
39  * were hidden.
40  *
41  * To add a window to the stack, just use gtk_widget_show(). The
42  * previous one will be obscured by the new one. When the new window
43  * is destroyed, the previous one will appear again.
44  *
45  * Alternatively, you can remove a window from the top of the stack
46  * without destroying it by using hildon_window_stack_pop(). The
47  * window will be automatically hidden and the previous one will
48  * appear.
49  *
50  * For advanced details on stack handling, see #HildonWindowStack
51  *
52  * <example>
53  * <title>Basic HildonStackableWindow example</title>
54  * <programlisting>
55  * static void
56  * show_new_window (void)
57  * {
58  *     GtkWidget *win;
59  * <!-- -->
60  *     win = hildon_stackable_window_new ();
61  * <!-- -->
62  *     // ... configure new window
63  * <!-- -->
64  *     gtk_widget_show (win);
65  * }
66  * <!-- -->
67  * int
68  * main (int argc, char **argv)
69  * {
70  *     GtkWidget *win;
71  *     GtkWidget *button;
72  * <!-- -->
73  *     gtk_init (&amp;argc, &amp;args);
74  * <!-- -->
75  *     win = hildon_stackable_window_new ();
76  *     gtk_window_set_title (GTK_WINDOW (win), "Main window);
77  * <!-- -->
78  *     // ... add some widgets to the window
79  * <!-- -->
80  *     g_signal_connect (button, "clicked", G_CALLBACK (show_new_window), NULL);
81  *     g_signal_connect (win, "destroy", G_CALLBACK (gtk_main_quit), NULL);
82  * <!-- -->
83  *     gtk_widget_show_all (win);
84  *     gtk_main ();
85  * <!-- -->
86  *     return 0;
87  * }
88  * </programlisting>
89  * </example>
90  */
91
92 #include                                        <X11/X.h>
93 #include                                        <X11/Xatom.h>
94 #include                                        <gdk/gdkx.h>
95
96 #include                                        "hildon-stackable-window.h"
97 #include                                        "hildon-stackable-window-private.h"
98 #include                                        "hildon-app-menu-private.h"
99 #include                                        "hildon-window-stack.h"
100 #include                                        "hildon-window-stack-private.h"
101
102 G_DEFINE_TYPE (HildonStackableWindow, hildon_stackable_window, HILDON_TYPE_WINDOW);
103
104 void G_GNUC_INTERNAL
105 hildon_stackable_window_set_stack               (HildonStackableWindow *self,
106                                                  HildonWindowStack     *stack,
107                                                  gint                   position)
108 {
109     HildonStackableWindowPrivate *priv = HILDON_STACKABLE_WINDOW_GET_PRIVATE (self);
110
111     if (stack)
112         g_object_ref (stack);
113
114     if (priv->stack)
115         g_object_unref (priv->stack);
116
117     priv->stack = stack;
118     priv->stack_position = position;
119 }
120
121 /**
122  * hildon_stackable_window_get_stack:
123  * @self: a #HildonStackableWindow
124  *
125  * Returns the stack where window @self is on, or %NULL if the window
126  * is not stacked.
127  *
128  * Return value: a #HildonWindowStack, or %NULL
129  **/
130 HildonWindowStack *
131 hildon_stackable_window_get_stack               (HildonStackableWindow *self)
132 {
133     HildonStackableWindowPrivate *priv;
134
135     g_return_val_if_fail (HILDON_IS_STACKABLE_WINDOW (self), NULL);
136
137     priv = HILDON_STACKABLE_WINDOW_GET_PRIVATE (self);
138
139     return priv->stack;
140 }
141
142 /**
143  * hildon_stackable_window_set_main_menu:
144  * @self: a #HildonStackableWindow
145  * @menu: a #HildonAppMenu to be used for this window
146  *
147  * Sets the menu to be used for this window. Pass %NULL to remove the
148  * current menu. Any reference to a previous menu will be dropped.
149  * #HildonStackableWindow takes ownership of the passed menu and
150  * you're not supposed to free it yourself anymore.
151  *
152  * Note that #HildonStackableWindow widgets use #HildonAppMenu rather
153  * than #GtkMenu, so you're not supposed to use
154  * hildon_window_set_main_menu() with a #HildonStackableWindow.
155  **/
156 void
157 hildon_stackable_window_set_main_menu           (HildonStackableWindow *self,
158                                                  HildonAppMenu *menu)
159 {
160     HildonStackableWindowPrivate *priv;
161     HildonAppMenu *old_menu;
162
163     g_return_if_fail (HILDON_IS_STACKABLE_WINDOW (self));
164     g_return_if_fail (!menu || HILDON_IS_APP_MENU (menu));
165     priv = HILDON_STACKABLE_WINDOW_GET_PRIVATE (self);
166
167     old_menu = priv->app_menu;
168
169     /* Add new menu */
170     priv->app_menu = menu;
171     if (menu)
172         g_object_ref_sink (menu);
173
174     /* Unref old menu */
175     if (old_menu)
176         g_object_unref (old_menu);
177 }
178
179 static gboolean
180 hildon_stackable_window_toggle_menu             (HildonWindow *self,
181                                                  guint button,
182                                                  guint32 time)
183 {
184     HildonStackableWindowPrivate *priv;
185
186     g_return_val_if_fail (HILDON_IS_STACKABLE_WINDOW (self), FALSE);
187     priv = HILDON_STACKABLE_WINDOW_GET_PRIVATE (self);
188     g_assert (priv != NULL);
189
190     if (priv->app_menu)
191     {
192         if (GTK_WIDGET_MAPPED (GTK_WIDGET (priv->app_menu))) {
193             gtk_widget_hide (GTK_WIDGET (priv->app_menu));
194         } else {
195             hildon_app_menu_set_parent_window (priv->app_menu, GTK_WINDOW (self));
196             gtk_widget_show (GTK_WIDGET (priv->app_menu));
197         }
198
199         return TRUE;
200     }
201     else if (HILDON_WINDOW_CLASS (hildon_stackable_window_parent_class)->toggle_menu)
202     {
203         return HILDON_WINDOW_CLASS (hildon_stackable_window_parent_class)->toggle_menu (self, button, time);
204     }
205     else
206     {
207         return FALSE;
208     }
209 }
210
211 static void
212 hildon_stackable_window_map                     (GtkWidget *widget)
213 {
214     GdkDisplay *display;
215     Atom atom;
216     unsigned long val;
217     HildonStackableWindowPrivate *priv = HILDON_STACKABLE_WINDOW_GET_PRIVATE (widget);
218
219     val = priv->stack_position;
220
221     /* Set additional property "_HILDON_STACKABLE_WINDOW", to allow the WM to manage
222        it as a stackable window. */
223     display = gdk_drawable_get_display (widget->window);
224     atom = gdk_x11_get_xatom_by_name_for_display (display, "_HILDON_STACKABLE_WINDOW");
225     XChangeProperty (GDK_DISPLAY_XDISPLAY (display), GDK_WINDOW_XID (widget->window), atom,
226                      XA_INTEGER, 32, PropModeReplace,
227                      (unsigned char *) &val, 1);
228
229     GTK_WIDGET_CLASS (hildon_stackable_window_parent_class)->map (widget);
230 }
231
232 static void
233 hildon_stackable_window_show                    (GtkWidget *widget)
234 {
235     HildonStackableWindowPrivate *priv = HILDON_STACKABLE_WINDOW_GET_PRIVATE (widget);
236
237     /* Stack the window if not already stacked */
238     if (priv->stack == NULL) {
239         HildonWindowStack *stack = hildon_window_stack_get_default ();
240         hildon_window_stack_push_1 (stack, HILDON_STACKABLE_WINDOW (widget));
241     } else {
242         GTK_WIDGET_CLASS (hildon_stackable_window_parent_class)->show (widget);
243     }
244 }
245
246 static void
247 hildon_stackable_window_hide                    (GtkWidget *widget)
248 {
249     HildonStackableWindowPrivate *priv = HILDON_STACKABLE_WINDOW_GET_PRIVATE (widget);
250     GTK_WIDGET_CLASS (hildon_stackable_window_parent_class)->hide (widget);
251
252     if (priv->stack) {
253         hildon_window_stack_remove (HILDON_STACKABLE_WINDOW (widget));
254     }
255 }
256
257 static void
258 hildon_stackable_window_finalize                (GObject *object)
259 {
260     HildonStackableWindowPrivate *priv = HILDON_STACKABLE_WINDOW_GET_PRIVATE (object);
261
262     if (priv->app_menu) {
263         hildon_app_menu_set_parent_window (priv->app_menu, NULL);
264         g_object_unref (GTK_WIDGET (priv->app_menu));
265     }
266
267     if (G_OBJECT_CLASS (hildon_stackable_window_parent_class)->finalize)
268         G_OBJECT_CLASS (hildon_stackable_window_parent_class)->finalize (object);
269 }
270
271 static void
272 hildon_stackable_window_class_init              (HildonStackableWindowClass *klass)
273 {
274     GObjectClass      *obj_class    = G_OBJECT_CLASS (klass);
275     GtkWidgetClass    *widget_class = GTK_WIDGET_CLASS (klass);
276     HildonWindowClass *window_class = HILDON_WINDOW_CLASS (klass);
277
278     obj_class->finalize             = hildon_stackable_window_finalize;
279
280     widget_class->map               = hildon_stackable_window_map;
281     widget_class->show              = hildon_stackable_window_show;
282     widget_class->hide              = hildon_stackable_window_hide;
283
284     window_class->toggle_menu       = hildon_stackable_window_toggle_menu;
285
286     g_type_class_add_private (klass, sizeof (HildonStackableWindowPrivate));
287 }
288
289 static void
290 hildon_stackable_window_init                    (HildonStackableWindow *self)
291 {
292     HildonStackableWindowPrivate *priv = HILDON_STACKABLE_WINDOW_GET_PRIVATE (self);
293
294     priv->app_menu = NULL;
295     priv->stack = NULL;
296     priv->stack_position = -1;
297 }
298
299 /**
300  * hildon_stackable_window_new:
301  *
302  * Creates a new #HildonStackableWindow.
303  *
304  * Return value: A #HildonStackableWindow
305  **/
306 GtkWidget*
307 hildon_stackable_window_new                     (void)
308 {
309     HildonStackableWindow *newwindow = g_object_new (HILDON_TYPE_STACKABLE_WINDOW, NULL);
310
311     return GTK_WIDGET (newwindow);
312 }