2008-09-15 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  *
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 yourself 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     /* Remove reference to old menu */
135     if (priv->app_menu)
136         g_object_unref (priv->app_menu);
137
138     /* Add new menu */
139     priv->app_menu = menu;
140     if (priv->app_menu)
141         g_object_ref (priv->app_menu);
142 }
143
144 static gboolean
145 hildon_stackable_window_toggle_menu             (HildonWindow *self,
146                                                  guint button,
147                                                  guint32 time)
148 {
149     HildonStackableWindowPrivate *priv;
150
151     g_return_val_if_fail (HILDON_IS_STACKABLE_WINDOW (self), FALSE);
152     priv = HILDON_STACKABLE_WINDOW_GET_PRIVATE (self);
153     g_assert (priv != NULL);
154
155     if (priv->app_menu)
156     {
157         if (GTK_WIDGET_MAPPED (GTK_WIDGET (priv->app_menu)))
158             gtk_widget_hide (GTK_WIDGET (priv->app_menu));
159         else
160             gtk_widget_show (GTK_WIDGET (priv->app_menu));
161
162         return TRUE;
163     }
164     else if (HILDON_WINDOW_CLASS (hildon_stackable_window_parent_class)->toggle_menu)
165     {
166         return HILDON_WINDOW_CLASS (hildon_stackable_window_parent_class)->toggle_menu (self, button, time);
167     }
168     else
169     {
170         return FALSE;
171     }
172 }
173
174 static void
175 hildon_stackable_window_realize                 (GtkWidget *widget)
176 {
177     GdkDisplay *display;
178     Atom atom;
179
180     GTK_WIDGET_CLASS (hildon_stackable_window_parent_class)->realize (widget);
181
182     /* Set the window type to "_HILDON_WM_WINDOW_TYPE_STACKABLE", to allow the WM to manage
183        it properly.  */
184     display = gdk_drawable_get_display (widget->window);
185     atom = gdk_x11_get_xatom_by_name_for_display (display, "_HILDON_WM_WINDOW_TYPE_STACKABLE");
186     XChangeProperty (GDK_DISPLAY_XDISPLAY (display), GDK_WINDOW_XID (widget->window),
187                      gdk_x11_get_xatom_by_name_for_display (display, "_NET_WM_WINDOW_TYPE"),
188                      XA_ATOM, 32, PropModeAppend,
189                      (guchar *)&atom, 1);
190 }
191
192 static void
193 hildon_stackable_window_show                    (GtkWidget *widget)
194 {
195     HildonProgram *program = hildon_program_get_instance ();
196     HildonStackableWindow *current_win = HILDON_STACKABLE_WINDOW (widget);
197     HildonStackableWindow *previous_win = hildon_program_peek_window_stack (program);
198
199     if (previous_win != current_win)
200         _hildon_program_add_to_stack (program, current_win);
201
202     GTK_WIDGET_CLASS (hildon_stackable_window_parent_class)->show (widget);
203
204     if (previous_win != NULL && previous_win != current_win)
205         gtk_widget_hide (GTK_WIDGET (previous_win));
206 }
207
208 static void
209 hildon_stackable_window_destroy                 (GtkObject *obj)
210 {
211     HildonProgram *program = hildon_program_get_instance ();
212     HildonStackableWindow *topmost = hildon_program_peek_window_stack (program);
213
214     if (_hildon_program_remove_from_stack (program, HILDON_STACKABLE_WINDOW (obj)))
215     {
216         if (topmost != NULL && GTK_OBJECT (topmost) == obj)
217         {
218             HildonStackableWindowPrivate *priv = HILDON_STACKABLE_WINDOW_GET_PRIVATE (obj);
219             topmost = hildon_program_peek_window_stack (program);
220             if (topmost != NULL && !priv->going_home)
221                 gtk_widget_show (GTK_WIDGET (topmost));
222         }
223     }
224
225     GTK_OBJECT_CLASS (hildon_stackable_window_parent_class)->destroy (obj);
226 }
227
228 static void
229 hildon_stackable_window_finalize                (GObject *object)
230 {
231     HildonStackableWindowPrivate *priv = HILDON_STACKABLE_WINDOW_GET_PRIVATE (object);
232
233     if (priv->app_menu)
234         gtk_widget_destroy (GTK_WIDGET (priv->app_menu));
235
236     if (G_OBJECT_CLASS (hildon_stackable_window_parent_class)->finalize)
237         G_OBJECT_CLASS (hildon_stackable_window_parent_class)->finalize (object);
238 }
239
240 static void
241 hildon_stackable_window_class_init              (HildonStackableWindowClass *klass)
242 {
243     GObjectClass      *obj_class    = G_OBJECT_CLASS (klass);
244     GtkObjectClass    *gtk_obj_class = GTK_OBJECT_CLASS (klass);
245     GtkWidgetClass    *widget_class = GTK_WIDGET_CLASS (klass);
246     HildonWindowClass *window_class = HILDON_WINDOW_CLASS (klass);
247
248     obj_class->finalize             = hildon_stackable_window_finalize;
249
250     gtk_obj_class->destroy          = hildon_stackable_window_destroy;
251
252     widget_class->realize           = hildon_stackable_window_realize;
253     widget_class->show              = hildon_stackable_window_show;
254
255     window_class->toggle_menu       = hildon_stackable_window_toggle_menu;
256
257     g_type_class_add_private (klass, sizeof (HildonStackableWindowPrivate));
258 }
259
260 static void
261 hildon_stackable_window_init                    (HildonStackableWindow *self)
262 {
263     HildonStackableWindowPrivate *priv = HILDON_STACKABLE_WINDOW_GET_PRIVATE (self);
264
265     priv->going_home = FALSE;
266     priv->app_menu = NULL;
267 }
268
269 /**
270  * hildon_stackable_window_new:
271  *
272  * Creates a new #HildonStackableWindow.
273  *
274  * Return value: A #HildonStackableWindow
275  **/
276 GtkWidget*
277 hildon_stackable_window_new                     (void)
278 {
279     HildonStackableWindow *newwindow = g_object_new (HILDON_TYPE_STACKABLE_WINDOW, NULL);
280
281     return GTK_WIDGET (newwindow);
282 }