2008-09-24 Claudio Saavedra <csaavedra@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  *
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, just use gtk_widget_show(). The
36  * previous one will be automatically hidden. When the new window is
37  * destroyed, the previous one will appear again.
38  *
39  * Alternatively, you can remove a window from the top of the stack
40  * without destroying it by using
41  * hildon_program_pop_window_stack(). The window will be automatically
42  * hidden and the previous one will appear.
43  *
44  * <example>
45  * <title>Basic HildonStackableWindow example</title>
46  * <programlisting>
47  * static void
48  * show_new_window (void)
49  * {
50  *     GtkWidget *win;
51  * <!-- -->
52  *     win = hildon_stackable_window_new ();
53  * <!-- -->
54  *     // ... configure new window
55  * <!-- -->
56  *     // This automatically hides the previous window
57  *     gtk_widget_show (win);
58  * }
59  * <!-- -->
60  * int
61  * main (int argc, char **argv)
62  * {
63  *     GtkWidget *win;
64  *     GtkWidget *button;
65  * <!-- -->
66  *     gtk_init (&amp;argc, &amp;args);
67  * <!-- -->
68  *     win = hildon_stackable_window_new ();
69  *     gtk_window_set_title (GTK_WINDOW (win), "Main window);
70  * <!-- -->
71  *     // ... add some widgets to the window
72  * <!-- -->
73  *     g_signal_connect (button, "clicked", G_CALLBACK (show_new_window), NULL);
74  *     g_signal_connect (win, "destroy", G_CALLBACK (gtk_main_quit), NULL);
75  * <!-- -->
76  *     gtk_widget_show_all (win);
77  *     gtk_main ();
78  * <!-- -->
79  *     return 0;
80  * }
81  * </programlisting>
82  * </example>
83  */
84
85 #include                                        <X11/X.h>
86 #include                                        <X11/Xatom.h>
87
88 #include                                        "hildon-stackable-window.h"
89 #include                                        "hildon-stackable-window-private.h"
90 #include                                        "hildon-program.h"
91 #include                                        "hildon-window-private.h"
92 #include                                        "hildon-program-private.h"
93
94 G_DEFINE_TYPE (HildonStackableWindow, hildon_stackable_window, HILDON_TYPE_WINDOW);
95
96 void G_GNUC_INTERNAL
97 hildon_stackable_window_set_going_home          (HildonStackableWindow *self,
98                                                  gboolean going_home)
99 {
100     HildonStackableWindowPrivate *priv = HILDON_STACKABLE_WINDOW_GET_PRIVATE (self);
101     priv->going_home = going_home;
102 }
103
104 gboolean G_GNUC_INTERNAL
105 hildon_stackable_window_get_going_home          (HildonStackableWindow *self)
106 {
107     HildonStackableWindowPrivate *priv = HILDON_STACKABLE_WINDOW_GET_PRIVATE (self);
108     return priv->going_home;
109 }
110
111 /**
112  * hildon_stackable_window_set_main_menu:
113  * @self: a #HildonStackableWindow
114  * @menu: a #HildonAppMenu to be used for this window
115  *
116  * Sets the menu to be used for this window. Pass %NULL to remove the
117  * current menu. #HildonStackableWindow takes ownership of the passed
118  * menu and you're not supposed to free it or reuse it anymore.
119  *
120  * Note that #HildonStackableWindow widgets use #HildonAppMenu rather
121  * than #GtkMenu, so you're not supposed to use
122  * hildon_window_set_main_menu() with a #HildonStackableWindow.
123  **/
124 void
125 hildon_stackable_window_set_main_menu           (HildonStackableWindow *self,
126                                                  HildonAppMenu *menu)
127 {
128     HildonStackableWindowPrivate *priv;
129
130     g_return_if_fail (HILDON_IS_STACKABLE_WINDOW (self));
131     g_return_if_fail (!menu || HILDON_IS_APP_MENU (menu));
132     priv = HILDON_STACKABLE_WINDOW_GET_PRIVATE (self);
133
134     /* Destroy old menu */
135     if (priv->app_menu)
136         gtk_widget_destroy (GTK_WIDGET (priv->app_menu));
137
138     /* Add new menu */
139     priv->app_menu = menu;
140 }
141
142 static gboolean
143 hildon_stackable_window_toggle_menu             (HildonWindow *self,
144                                                  guint button,
145                                                  guint32 time)
146 {
147     HildonStackableWindowPrivate *priv;
148
149     g_return_val_if_fail (HILDON_IS_STACKABLE_WINDOW (self), FALSE);
150     priv = HILDON_STACKABLE_WINDOW_GET_PRIVATE (self);
151     g_assert (priv != NULL);
152
153     if (priv->app_menu)
154     {
155         if (GTK_WIDGET_MAPPED (GTK_WIDGET (priv->app_menu)))
156             gtk_widget_hide (GTK_WIDGET (priv->app_menu));
157         else
158             gtk_widget_show (GTK_WIDGET (priv->app_menu));
159
160         return TRUE;
161     }
162     else if (HILDON_WINDOW_CLASS (hildon_stackable_window_parent_class)->toggle_menu)
163     {
164         return HILDON_WINDOW_CLASS (hildon_stackable_window_parent_class)->toggle_menu (self, button, time);
165     }
166     else
167     {
168         return FALSE;
169     }
170 }
171
172 static void
173 hildon_stackable_window_realize                 (GtkWidget *widget)
174 {
175     GdkDisplay *display;
176     Atom atom;
177
178     GTK_WIDGET_CLASS (hildon_stackable_window_parent_class)->realize (widget);
179
180     /* Set the window type to "_HILDON_WM_WINDOW_TYPE_STACKABLE", to allow the WM to manage
181        it properly.  */
182     display = gdk_drawable_get_display (widget->window);
183     atom = gdk_x11_get_xatom_by_name_for_display (display, "_HILDON_WM_WINDOW_TYPE_STACKABLE");
184     XChangeProperty (GDK_DISPLAY_XDISPLAY (display), GDK_WINDOW_XID (widget->window),
185                      gdk_x11_get_xatom_by_name_for_display (display, "_NET_WM_WINDOW_TYPE"),
186                      XA_ATOM, 32, PropModeAppend,
187                      (guchar *)&atom, 1);
188 }
189
190 static void
191 hildon_stackable_window_show                    (GtkWidget *widget)
192 {
193     HildonProgram *program = hildon_program_get_instance ();
194     HildonStackableWindow *current_win = HILDON_STACKABLE_WINDOW (widget);
195     HildonStackableWindow *previous_win = hildon_program_peek_window_stack (program);
196
197     if (previous_win != current_win)
198         _hildon_program_add_to_stack (program, current_win);
199
200     GTK_WIDGET_CLASS (hildon_stackable_window_parent_class)->show (widget);
201
202     if (previous_win != NULL && previous_win != current_win)
203         gtk_widget_hide (GTK_WIDGET (previous_win));
204 }
205
206 static void
207 hildon_stackable_window_destroy                 (GtkObject *obj)
208 {
209     HildonProgram *program = hildon_program_get_instance ();
210     HildonStackableWindow *topmost = hildon_program_peek_window_stack (program);
211
212     if (_hildon_program_remove_from_stack (program, HILDON_STACKABLE_WINDOW (obj)))
213     {
214         if (topmost != NULL && GTK_OBJECT (topmost) == obj)
215         {
216             HildonStackableWindowPrivate *priv = HILDON_STACKABLE_WINDOW_GET_PRIVATE (obj);
217             topmost = hildon_program_peek_window_stack (program);
218             if (topmost != NULL && !priv->going_home)
219                 gtk_widget_show (GTK_WIDGET (topmost));
220         }
221     }
222
223     GTK_OBJECT_CLASS (hildon_stackable_window_parent_class)->destroy (obj);
224 }
225
226 static void
227 hildon_stackable_window_finalize                (GObject *object)
228 {
229     HildonStackableWindowPrivate *priv = HILDON_STACKABLE_WINDOW_GET_PRIVATE (object);
230
231     if (priv->app_menu)
232         gtk_widget_destroy (GTK_WIDGET (priv->app_menu));
233
234     if (G_OBJECT_CLASS (hildon_stackable_window_parent_class)->finalize)
235         G_OBJECT_CLASS (hildon_stackable_window_parent_class)->finalize (object);
236 }
237
238 static void
239 hildon_stackable_window_class_init              (HildonStackableWindowClass *klass)
240 {
241     GObjectClass      *obj_class    = G_OBJECT_CLASS (klass);
242     GtkObjectClass    *gtk_obj_class = GTK_OBJECT_CLASS (klass);
243     GtkWidgetClass    *widget_class = GTK_WIDGET_CLASS (klass);
244     HildonWindowClass *window_class = HILDON_WINDOW_CLASS (klass);
245
246     obj_class->finalize             = hildon_stackable_window_finalize;
247
248     gtk_obj_class->destroy          = hildon_stackable_window_destroy;
249
250     widget_class->realize           = hildon_stackable_window_realize;
251     widget_class->show              = hildon_stackable_window_show;
252
253     window_class->toggle_menu       = hildon_stackable_window_toggle_menu;
254
255     g_type_class_add_private (klass, sizeof (HildonStackableWindowPrivate));
256 }
257
258 static void
259 hildon_stackable_window_init                    (HildonStackableWindow *self)
260 {
261     HildonStackableWindowPrivate *priv = HILDON_STACKABLE_WINDOW_GET_PRIVATE (self);
262
263     priv->going_home = FALSE;
264     priv->app_menu = NULL;
265 }
266
267 /**
268  * hildon_stackable_window_new:
269  *
270  * Creates a new #HildonStackableWindow.
271  *
272  * Return value: A #HildonStackableWindow
273  **/
274 GtkWidget*
275 hildon_stackable_window_new                     (void)
276 {
277     HildonStackableWindow *newwindow = g_object_new (HILDON_TYPE_STACKABLE_WINDOW, NULL);
278
279     return GTK_WIDGET (newwindow);
280 }