2009-02-27 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: Rodrigo Novo <rodrigo.novo@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 #include                                        "hildon-window-private.h"
102 #include                                        "hildon-program.h"
103
104 G_DEFINE_TYPE (HildonStackableWindow, hildon_stackable_window, HILDON_TYPE_WINDOW);
105
106 void G_GNUC_INTERNAL
107 hildon_stackable_window_set_stack               (HildonStackableWindow *self,
108                                                  HildonWindowStack     *stack,
109                                                  gint                   position)
110 {
111     HildonStackableWindowPrivate *priv = HILDON_STACKABLE_WINDOW_GET_PRIVATE (self);
112
113     if (stack)
114         g_object_ref (stack);
115
116     if (priv->stack)
117         g_object_unref (priv->stack);
118
119     priv->stack = stack;
120     priv->stack_position = position;
121 }
122
123 /**
124  * hildon_stackable_window_get_stack:
125  * @self: a #HildonStackableWindow
126  *
127  * Returns the stack where window @self is on, or %NULL if the window
128  * is not stacked.
129  *
130  * Return value: a #HildonWindowStack, or %NULL
131  *
132  * Since: 2.2
133  **/
134 HildonWindowStack *
135 hildon_stackable_window_get_stack               (HildonStackableWindow *self)
136 {
137     HildonStackableWindowPrivate *priv;
138
139     g_return_val_if_fail (HILDON_IS_STACKABLE_WINDOW (self), NULL);
140
141     priv = HILDON_STACKABLE_WINDOW_GET_PRIVATE (self);
142
143     return priv->stack;
144 }
145
146 /**
147  * hildon_stackable_window_set_main_menu:
148  * @self: a #HildonStackableWindow
149  * @menu: a #HildonAppMenu to be used for this window
150  *
151  * Sets the menu to be used for this window. Pass %NULL to remove the
152  * current menu. Any reference to a previous menu will be dropped.
153  * #HildonStackableWindow takes ownership of the passed menu and
154  * you're not supposed to free it yourself anymore.
155  *
156  * Note that #HildonStackableWindow widgets use #HildonAppMenu rather
157  * than #GtkMenu, so you're not supposed to use
158  * hildon_window_set_main_menu() with a #HildonStackableWindow.
159  *
160  * Since: 2.2
161  **/
162 void
163 hildon_stackable_window_set_main_menu           (HildonStackableWindow *self,
164                                                  HildonAppMenu *menu)
165 {
166     HildonStackableWindowPrivate *priv;
167     HildonAppMenu *old_menu;
168
169     g_return_if_fail (HILDON_IS_STACKABLE_WINDOW (self));
170     g_return_if_fail (!menu || HILDON_IS_APP_MENU (menu));
171     priv = HILDON_STACKABLE_WINDOW_GET_PRIVATE (self);
172
173     old_menu = priv->app_menu;
174
175     /* Add new menu */
176     priv->app_menu = menu;
177     if (menu)
178         g_object_ref_sink (menu);
179
180     /* Unref old menu */
181     if (old_menu)
182         g_object_unref (old_menu);
183 }
184
185 static gboolean
186 hildon_stackable_window_toggle_menu             (HildonWindow *self,
187                                                  guint button,
188                                                  guint32 time)
189 {
190     HildonStackableWindowPrivate *priv;
191     HildonAppMenu *menu_to_use = NULL;
192
193     g_return_val_if_fail (HILDON_IS_STACKABLE_WINDOW (self), FALSE);
194     priv = HILDON_STACKABLE_WINDOW_GET_PRIVATE (self);
195     g_assert (priv != NULL);
196
197     if (priv->app_menu) {
198         menu_to_use = priv->app_menu;
199     } else {
200         HildonProgram *program = HILDON_WINDOW_GET_PRIVATE (self)->program;
201
202         if (program) {
203             menu_to_use = hildon_program_get_common_app_menu (program);
204             if (menu_to_use) {
205                 if (self != hildon_app_menu_get_parent_window (HILDON_APP_MENU (menu_to_use)))
206                     gtk_widget_hide (GTK_WIDGET (menu_to_use));
207             }
208         }
209     }
210
211     if (menu_to_use) {
212         if (GTK_WIDGET_MAPPED (GTK_WIDGET (menu_to_use))) {
213             gtk_widget_hide (GTK_WIDGET (menu_to_use));
214         } else {
215             hildon_app_menu_popup (menu_to_use, GTK_WINDOW (self));
216         }
217
218         return TRUE;
219     } else if (HILDON_WINDOW_CLASS (hildon_stackable_window_parent_class)->toggle_menu) {
220         return HILDON_WINDOW_CLASS (hildon_stackable_window_parent_class)->toggle_menu (self, button, time);
221     } else {
222         return FALSE;
223     }
224 }
225
226 static void
227 hildon_stackable_window_map                     (GtkWidget *widget)
228 {
229     GdkDisplay *display;
230     Atom atom;
231     unsigned long val;
232     HildonStackableWindowPrivate *priv = HILDON_STACKABLE_WINDOW_GET_PRIVATE (widget);
233
234     val = priv->stack_position;
235
236     /* Set additional property "_HILDON_STACKABLE_WINDOW", to allow the WM to manage
237        it as a stackable window. */
238     display = gdk_drawable_get_display (widget->window);
239     atom = gdk_x11_get_xatom_by_name_for_display (display, "_HILDON_STACKABLE_WINDOW");
240     XChangeProperty (GDK_DISPLAY_XDISPLAY (display), GDK_WINDOW_XID (widget->window), atom,
241                      XA_INTEGER, 32, PropModeReplace,
242                      (unsigned char *) &val, 1);
243
244     GTK_WIDGET_CLASS (hildon_stackable_window_parent_class)->map (widget);
245 }
246
247 static void
248 hildon_stackable_window_show                    (GtkWidget *widget)
249 {
250     HildonStackableWindowPrivate *priv = HILDON_STACKABLE_WINDOW_GET_PRIVATE (widget);
251
252     /* Stack the window if not already stacked */
253     if (priv->stack == NULL) {
254         HildonWindowStack *stack = hildon_window_stack_get_default ();
255         _hildon_window_stack_do_push (stack, HILDON_STACKABLE_WINDOW (widget));
256     }
257
258     GTK_WIDGET_CLASS (hildon_stackable_window_parent_class)->show (widget);
259 }
260
261 static void
262 hildon_stackable_window_hide                    (GtkWidget *widget)
263 {
264     HildonStackableWindowPrivate *priv = HILDON_STACKABLE_WINDOW_GET_PRIVATE (widget);
265
266     if (priv->stack) {
267         hildon_window_stack_remove (HILDON_STACKABLE_WINDOW (widget));
268     }
269
270     GTK_WIDGET_CLASS (hildon_stackable_window_parent_class)->hide (widget);
271 }
272
273 static gboolean
274 hildon_stackable_window_delete_event            (GtkWidget   *widget,
275                                                  GdkEventAny *event)
276 {
277     HildonStackableWindowPrivate *priv = HILDON_STACKABLE_WINDOW_GET_PRIVATE (widget);
278     gboolean retvalue = FALSE;
279
280     if (priv->stack && hildon_window_stack_peek (priv->stack) != widget) {
281         /* Ignore the delete event if this window is not the topmost one */
282         retvalue = TRUE;
283     } else if (GTK_WIDGET_CLASS (hildon_stackable_window_parent_class)->delete_event) {
284         retvalue = GTK_WIDGET_CLASS (hildon_stackable_window_parent_class)->delete_event (widget, event);
285     }
286
287     return retvalue;
288 }
289
290 static void
291 hildon_stackable_window_finalize                (GObject *object)
292 {
293     HildonStackableWindowPrivate *priv = HILDON_STACKABLE_WINDOW_GET_PRIVATE (object);
294
295     if (priv->app_menu) {
296         hildon_app_menu_set_parent_window (priv->app_menu, NULL);
297         g_object_unref (GTK_WIDGET (priv->app_menu));
298     }
299
300     if (G_OBJECT_CLASS (hildon_stackable_window_parent_class)->finalize)
301         G_OBJECT_CLASS (hildon_stackable_window_parent_class)->finalize (object);
302 }
303
304 static void
305 hildon_stackable_window_class_init              (HildonStackableWindowClass *klass)
306 {
307     GObjectClass      *obj_class    = G_OBJECT_CLASS (klass);
308     GtkWidgetClass    *widget_class = GTK_WIDGET_CLASS (klass);
309     HildonWindowClass *window_class = HILDON_WINDOW_CLASS (klass);
310
311     obj_class->finalize             = hildon_stackable_window_finalize;
312
313     widget_class->map               = hildon_stackable_window_map;
314     widget_class->show              = hildon_stackable_window_show;
315     widget_class->hide              = hildon_stackable_window_hide;
316     widget_class->delete_event      = hildon_stackable_window_delete_event;
317
318     window_class->toggle_menu       = hildon_stackable_window_toggle_menu;
319
320     g_type_class_add_private (klass, sizeof (HildonStackableWindowPrivate));
321 }
322
323 static void
324 hildon_stackable_window_init                    (HildonStackableWindow *self)
325 {
326     HildonStackableWindowPrivate *priv = HILDON_STACKABLE_WINDOW_GET_PRIVATE (self);
327
328     priv->app_menu = NULL;
329     priv->stack = NULL;
330     priv->stack_position = -1;
331 }
332
333 /**
334  * hildon_stackable_window_new:
335  *
336  * Creates a new #HildonStackableWindow.
337  *
338  * Return value: A #HildonStackableWindow
339  *
340  * Since: 2.2
341  **/
342 GtkWidget*
343 hildon_stackable_window_new                     (void)
344 {
345     HildonStackableWindow *newwindow = g_object_new (HILDON_TYPE_STACKABLE_WINDOW, NULL);
346
347     return GTK_WIDGET (newwindow);
348 }