2008-10-16 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. Any reference to a previous menu will be dropped.
118  * #HildonStackableWindow takes ownership of the passed menu and
119  * you're not supposed to free it yourself anymore.
120  *
121  * Note that #HildonStackableWindow widgets use #HildonAppMenu rather
122  * than #GtkMenu, so you're not supposed to use
123  * hildon_window_set_main_menu() with a #HildonStackableWindow.
124  **/
125 void
126 hildon_stackable_window_set_main_menu           (HildonStackableWindow *self,
127                                                  HildonAppMenu *menu)
128 {
129     HildonStackableWindowPrivate *priv;
130     HildonAppMenu *old_menu;
131
132     g_return_if_fail (HILDON_IS_STACKABLE_WINDOW (self));
133     g_return_if_fail (!menu || HILDON_IS_APP_MENU (menu));
134     priv = HILDON_STACKABLE_WINDOW_GET_PRIVATE (self);
135
136     old_menu = priv->app_menu;
137
138     /* Add new menu */
139     priv->app_menu = menu;
140     if (menu)
141         g_object_ref_sink (menu);
142
143     /* Unref old menu */
144     if (old_menu)
145         g_object_unref (old_menu);
146 }
147
148 static gboolean
149 hildon_stackable_window_toggle_menu             (HildonWindow *self,
150                                                  guint button,
151                                                  guint32 time)
152 {
153     HildonStackableWindowPrivate *priv;
154
155     g_return_val_if_fail (HILDON_IS_STACKABLE_WINDOW (self), FALSE);
156     priv = HILDON_STACKABLE_WINDOW_GET_PRIVATE (self);
157     g_assert (priv != NULL);
158
159     if (priv->app_menu)
160     {
161         if (GTK_WIDGET_MAPPED (GTK_WIDGET (priv->app_menu)))
162             gtk_widget_hide (GTK_WIDGET (priv->app_menu));
163         else
164             gtk_widget_show (GTK_WIDGET (priv->app_menu));
165
166         return TRUE;
167     }
168     else if (HILDON_WINDOW_CLASS (hildon_stackable_window_parent_class)->toggle_menu)
169     {
170         return HILDON_WINDOW_CLASS (hildon_stackable_window_parent_class)->toggle_menu (self, button, time);
171     }
172     else
173     {
174         return FALSE;
175     }
176 }
177
178 static void
179 hildon_stackable_window_realize                 (GtkWidget *widget)
180 {
181     GdkDisplay *display;
182     Atom atom;
183     unsigned long val = 1;
184
185     GTK_WIDGET_CLASS (hildon_stackable_window_parent_class)->realize (widget);
186
187     /* Set additional property "_HILDON_STACKABLE_WINDOW", to allow the WM to manage
188        it as a stackable window. */
189     display = gdk_drawable_get_display (widget->window);
190     atom = gdk_x11_get_xatom_by_name_for_display (display, "_HILDON_STACKABLE_WINDOW");
191     XChangeProperty (GDK_DISPLAY_XDISPLAY (display), GDK_WINDOW_XID (widget->window), atom,
192                      XA_ATOM, 32, PropModeReplace,
193                      (unsigned char *) &val, 1);
194 }
195
196 static void
197 hildon_stackable_window_show                    (GtkWidget *widget)
198 {
199     HildonProgram *program = hildon_program_get_instance ();
200     HildonStackableWindow *current_win = HILDON_STACKABLE_WINDOW (widget);
201     HildonStackableWindow *previous_win = hildon_program_peek_window_stack (program);
202
203     if (previous_win != current_win)
204         _hildon_program_add_to_stack (program, current_win);
205
206     GTK_WIDGET_CLASS (hildon_stackable_window_parent_class)->show (widget);
207 }
208
209 static void
210 hildon_stackable_window_hide                    (GtkWidget *widget)
211 {
212     HildonProgram *program = hildon_program_get_instance ();
213     _hildon_program_remove_from_stack (program, HILDON_STACKABLE_WINDOW (widget));
214     GTK_WIDGET_CLASS (hildon_stackable_window_parent_class)->hide (widget);
215 }
216
217 static void
218 hildon_stackable_window_finalize                (GObject *object)
219 {
220     HildonStackableWindowPrivate *priv = HILDON_STACKABLE_WINDOW_GET_PRIVATE (object);
221
222     if (priv->app_menu)
223         gtk_widget_destroy (GTK_WIDGET (priv->app_menu));
224
225     if (G_OBJECT_CLASS (hildon_stackable_window_parent_class)->finalize)
226         G_OBJECT_CLASS (hildon_stackable_window_parent_class)->finalize (object);
227 }
228
229 static void
230 hildon_stackable_window_class_init              (HildonStackableWindowClass *klass)
231 {
232     GObjectClass      *obj_class    = G_OBJECT_CLASS (klass);
233     GtkWidgetClass    *widget_class = GTK_WIDGET_CLASS (klass);
234     HildonWindowClass *window_class = HILDON_WINDOW_CLASS (klass);
235
236     obj_class->finalize             = hildon_stackable_window_finalize;
237
238     widget_class->realize           = hildon_stackable_window_realize;
239     widget_class->show              = hildon_stackable_window_show;
240     widget_class->hide              = hildon_stackable_window_hide;
241
242     window_class->toggle_menu       = hildon_stackable_window_toggle_menu;
243
244     g_type_class_add_private (klass, sizeof (HildonStackableWindowPrivate));
245 }
246
247 static void
248 hildon_stackable_window_init                    (HildonStackableWindow *self)
249 {
250     HildonStackableWindowPrivate *priv = HILDON_STACKABLE_WINDOW_GET_PRIVATE (self);
251
252     priv->going_home = FALSE;
253     priv->app_menu = NULL;
254 }
255
256 /**
257  * hildon_stackable_window_new:
258  *
259  * Creates a new #HildonStackableWindow.
260  *
261  * Return value: A #HildonStackableWindow
262  **/
263 GtkWidget*
264 hildon_stackable_window_new                     (void)
265 {
266     HildonStackableWindow *newwindow = g_object_new (HILDON_TYPE_STACKABLE_WINDOW, NULL);
267
268     return GTK_WIDGET (newwindow);
269 }