2008-09-15 Alberto Garcia <agarcia@igalia.com>
[hildon] / src / hildon-program.c
1 /*
2  * This file is a part of hildon
3  *
4  * Copyright (C) 2006 Nokia Corporation, all rights reserved.
5  *
6  * Contact: Michael Dominic Kostrzewa <michael.kostrzewa@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-program
27  * @short_description: An object that represents an application running in the Hildon framework.
28  * @see_also: #HildonWindow, #HildonStackableWindow
29  *
30  * The #HildonProgram is an object used to represent an application running
31  * in the Hildon framework.
32  *
33  * Such an application is thought to have one or more #HildonWindow. These
34  * shall be registered to the #HildonProgram with hildon_program_add_window(),
35  * and can be unregistered similarly with hildon_program_remove_window().
36  *
37  * The #HildonProgram provides the programmer with commodities such
38  * as applying a common toolbar and menu to all the #HildonWindow
39  * registered to it. This is done with hildon_program_set_common_menu()
40  * and hildon_program_set_common_toolbar().
41  *
42  * The #HildonProgram is also used to apply program-wide properties that
43  * are specific to the Hildon framework. For instance
44  * hildon_program_set_can_hibernate() sets whether or not an application
45  * can be set to hibernate by the Hildon task navigator, in situations of
46  * low memory.
47  *
48  * The #HildonProgram also contains a stack of
49  * #HildonStackableWindow. Such windows will be automatically added to
50  * the stack when shown, and removed when destroyed. The developer can
51  * use the stack with hildon_program_pop_window_stack(),
52  * hildon_program_peek_window_stack() and hildon_program_go_to_root_window().
53  *
54  * <example>
55  * <programlisting>
56  * HildonProgram *program;
57  * HildonWindow *window1;
58  * HildonWindow *window2;
59  * GtkToolbar *common_toolbar, *window_specific_toolbar;
60  * GtkMenu *menu;
61  * <!-- -->
62  * program = HILDON_PROGRAM (hildon_program_get_instance ());
63  * <!-- -->
64  * window1 = HILDON_WINDOW (hildon_window_new ());
65  * window2 = HILDON_WINDOW (hildon_window_new ());
66  * <!-- -->
67  * common_toolbar = create_common_toolbar ();
68  * window_specific_toolbar = create_window_specific_toolbar ();
69  * <!-- -->
70  * menu = create_menu ();
71  * <!-- -->
72  * hildon_program_add_window (program, window1);
73  * hildon_program_add_window (program, window2);
74  * <!-- -->
75  * hildon_program_set_common_menu (program, menu);
76  * <!-- -->
77  * hildon_program_set_common_toolbar (program, common_toolbar);
78  * hildon_window_add_toolbar (window1, window_specific_toolbar);
79  * <!-- -->
80  * hildon_program_set_can_hibernate (program, TRUE);
81  * </programlisting>
82  * </example>
83  */
84
85 #ifdef                                          HAVE_CONFIG_H
86 #include                                        <config.h>
87 #endif
88
89 #include                                        <X11/Xatom.h>
90
91 #include                                        "hildon-program.h"
92 #include                                        "hildon-program-private.h"
93 #include                                        "hildon-window-private.h"
94 #include                                        "hildon-stackable-window-private.h"
95
96 static void
97 hildon_program_init                             (HildonProgram *self);
98
99 static void
100 hildon_program_finalize                         (GObject *self);
101
102 static void
103 hildon_program_class_init                       (HildonProgramClass *self);
104
105 static void
106 hildon_program_get_property                     (GObject *object, 
107                                                  guint property_id,
108                                                  GValue *value, 
109                                                  GParamSpec *pspec);
110 static void
111 hildon_program_set_property                     (GObject *object, 
112                                                  guint property_id,
113                                                  const GValue *value, 
114                                                  GParamSpec *pspec);
115
116 enum
117 {
118     PROP_0,
119     PROP_IS_TOPMOST,
120     PROP_KILLABLE
121 };
122
123 GType G_GNUC_CONST
124 hildon_program_get_type                         (void)
125 {
126     static GType program_type = 0;
127
128     if (! program_type)
129     {
130         static const GTypeInfo program_info =
131         {
132             sizeof (HildonProgramClass),
133             NULL,       /* base_init */
134             NULL,       /* base_finalize */
135             (GClassInitFunc) hildon_program_class_init,
136             NULL,       /* class_finalize */
137             NULL,       /* class_data */
138             sizeof (HildonProgram),
139             0,  /* n_preallocs */
140             (GInstanceInitFunc) hildon_program_init,
141         };
142         program_type = g_type_register_static(G_TYPE_OBJECT,
143                 "HildonProgram", &program_info, 0);
144     }
145     return program_type;
146 }
147
148 static void
149 hildon_program_init                             (HildonProgram *self)
150 {
151     HildonProgramPrivate *priv = HILDON_PROGRAM_GET_PRIVATE (self);
152     g_assert (priv);
153     
154     priv->killable = FALSE;
155     priv->window_count = 0;
156     priv->is_topmost = FALSE;
157     priv->window_group = GDK_WINDOW_XID (gdk_display_get_default_group (gdk_display_get_default()));
158     priv->common_toolbar = NULL;
159     priv->name = NULL;
160     priv->windows = NULL;
161     priv->window_stack = NULL;
162 }
163
164 static void
165 hildon_program_finalize                         (GObject *self)
166 {
167     HildonProgramPrivate *priv = HILDON_PROGRAM_GET_PRIVATE (HILDON_PROGRAM (self));
168     g_assert (priv);
169     
170     if (priv->common_toolbar)
171     {
172         g_object_unref (priv->common_toolbar);
173         priv->common_toolbar = NULL;
174     }
175
176     if (priv->common_menu)
177     {
178         g_object_unref (priv->common_menu);
179         priv->common_menu = NULL;
180     }
181
182     g_free (priv->name);
183 }
184
185 static void
186 hildon_program_class_init                       (HildonProgramClass *self)
187 {
188     GObjectClass *object_class = G_OBJECT_CLASS (self);
189
190     g_type_class_add_private (self, sizeof (HildonProgramPrivate));
191
192     /* Set up object virtual functions */
193     object_class->finalize      = hildon_program_finalize;
194     object_class->set_property  = hildon_program_set_property;
195     object_class->get_property  = hildon_program_get_property;
196
197     /* Install properties */
198
199     /**
200      * HildonProgram:is-topmost:
201      *
202      * Whether one of the program's window or dialog currently
203      * is activated by window manager. 
204      */
205     g_object_class_install_property (object_class, PROP_IS_TOPMOST,
206                 g_param_spec_boolean ("is-topmost",
207                 "Is top-most",
208                 "Whether one of the program's window or dialog currently "
209                 "is activated by window manager",
210                 FALSE,
211                 G_PARAM_READABLE)); 
212
213     /**
214      * HildonProgram:can-hibernate:
215      *
216      * Whether the program should be set to hibernate by the Task
217      * Navigator in low memory situation.
218      */
219     g_object_class_install_property (object_class, PROP_KILLABLE,
220                 g_param_spec_boolean ("can-hibernate",
221                 "Can hibernate",
222                 "Whether the program should be set to hibernate by the Task "
223                 "Navigator in low memory situation",
224                 FALSE,
225                 G_PARAM_READWRITE)); 
226     return;
227 }
228
229 static void
230 hildon_program_set_property                     (GObject *object, 
231                                                  guint property_id,
232                                                  const GValue *value, 
233                                                  GParamSpec *pspec)
234 {
235     switch (property_id) {
236
237         case PROP_KILLABLE:
238             hildon_program_set_can_hibernate (HILDON_PROGRAM (object), g_value_get_boolean (value));
239             break;
240             
241         default:
242             G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
243             break;
244     }
245
246 }
247
248 static void
249 hildon_program_get_property                     (GObject *object, 
250                                                  guint property_id,
251                                                  GValue *value, 
252                                                  GParamSpec *pspec)
253 {
254     HildonProgramPrivate *priv = HILDON_PROGRAM_GET_PRIVATE (object);
255     g_assert (priv);
256
257     switch (property_id)
258     {
259         case PROP_KILLABLE:
260             g_value_set_boolean (value, priv->killable);
261             break;
262
263         case PROP_IS_TOPMOST:
264             g_value_set_boolean (value, priv->is_topmost);
265             break;
266
267         default:
268             G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
269             break;
270     }
271 }
272
273 /**
274  * hildon_program_pop_window_stack:
275  * @self: A #HildonProgram
276  *
277  * The #HildonProgram object maintains a list of stackable
278  * windows. Each time a #HildonStackableWindow is shown, it is
279  * automatically added to the top of the stack. Windows are removed
280  * from the stack when they are destroyed.
281  *
282  * This function removes the #HildonStackableWindow from the top of
283  * the stack and returns it. The window is automatically hidden, but
284  * not destroyed. If that window was visible and there are more
285  * windows left in the stack, the next one will be shown
286  * automatically.
287  *
288  * If the stack is empty, %NULL is returned.
289  *
290  * Returns: A #HildonStackableWindow, or %NULL.
291  */
292 HildonStackableWindow *
293 hildon_program_pop_window_stack                 (HildonProgram *self)
294 {
295     HildonStackableWindow *top;
296
297     top = hildon_program_peek_window_stack (self);
298
299     if (top)
300     {
301         HildonStackableWindow *next;
302
303         /* Remove the window from the stack and get the next one */
304         _hildon_program_remove_from_stack (self, top);
305         next = hildon_program_peek_window_stack (self);
306
307         /* Hide the window just removed and show the next one if necessary */
308         if (GTK_WIDGET_VISIBLE (GTK_WIDGET (top)) && next != NULL);
309             gtk_widget_show (GTK_WIDGET (next));
310
311         gtk_widget_hide (GTK_WIDGET (top));
312     }
313
314     return top;
315 }
316
317 /**
318  * hildon_program_peek_window_stack:
319  * @self: A #HildonProgram
320  *
321  * The #HildonProgram object maintains a list of stackable
322  * windows. Each time a #HildonStackableWindow is shown, it is
323  * automatically added to the top of the stack. Windows are removed
324  * from the stack when they are destroyed.
325  *
326  * This function returns the #HildonStackableWindow from the top of
327  * the stack, or %NULL if the stack is empty. The stack is never modified.
328  *
329  * Returns: A #HildonStackableWindow, or %NULL.
330  */
331 HildonStackableWindow *
332 hildon_program_peek_window_stack                (HildonProgram *self)
333 {
334     HildonStackableWindow *top = NULL;
335     HildonProgramPrivate *priv;
336
337     g_return_val_if_fail (HILDON_IS_PROGRAM (self), NULL);
338
339     priv = HILDON_PROGRAM_GET_PRIVATE (self);
340     g_assert (priv);
341
342     if (priv->window_stack != NULL)
343         top = HILDON_STACKABLE_WINDOW (priv->window_stack->data);
344
345     return top;
346 }
347
348 void G_GNUC_INTERNAL
349 _hildon_program_add_to_stack                    (HildonProgram         *self,
350                                                  HildonStackableWindow *win)
351 {
352     HildonProgramPrivate *priv;
353
354     g_return_if_fail (HILDON_IS_PROGRAM (self));
355     g_return_if_fail (HILDON_IS_STACKABLE_WINDOW (win));
356
357     priv = HILDON_PROGRAM_GET_PRIVATE (self);
358     g_assert (priv);
359
360     if (g_slist_find (priv->window_stack, win) == NULL)
361     {
362         priv->window_stack = g_slist_prepend (priv->window_stack, win);
363     }
364     else
365     {
366         g_critical ("%s: window already in the stack!", __FUNCTION__);
367     }
368
369 }
370
371 gboolean G_GNUC_INTERNAL
372 _hildon_program_remove_from_stack               (HildonProgram         *self,
373                                                  HildonStackableWindow *win)
374 {
375     GSList *pos;
376     HildonProgramPrivate *priv;
377
378     g_return_val_if_fail (HILDON_IS_PROGRAM (self), FALSE);
379     g_return_val_if_fail (HILDON_IS_STACKABLE_WINDOW (win), FALSE);
380
381     priv = HILDON_PROGRAM_GET_PRIVATE (self);
382     g_assert (priv);
383
384     pos = g_slist_find (priv->window_stack, win);
385
386     if (pos != NULL)
387         priv->window_stack = g_slist_delete_link (priv->window_stack, pos);
388
389     return (pos != NULL);
390 }
391
392 /* Utilities */
393 static gint 
394 hildon_program_window_list_compare              (gconstpointer window_a, 
395                                                  gconstpointer window_b)
396 {
397     g_return_val_if_fail (HILDON_IS_WINDOW(window_a) && 
398                           HILDON_IS_WINDOW(window_b), 1);
399
400     return window_a != window_b;
401 }
402
403 /*
404  * foreach function, checks if a window is topmost and acts consequently
405  */
406 static void
407 hildon_program_window_list_is_is_topmost        (gpointer data, 
408                                                  gpointer window_id_)
409 {
410     if (data && HILDON_IS_WINDOW (data))
411     {
412         HildonWindow *window = HILDON_WINDOW (data);
413         Window window_id = * (Window*)window_id_;
414
415         hildon_window_update_topmost (window, window_id);
416     }
417 }
418
419 /*
420  * Check the _MB_CURRENT_APP_WINDOW on the root window, and update
421  * the top_most status accordingly
422  */
423 static void
424 hildon_program_update_top_most                  (HildonProgram *program)
425 {
426     XWMHints *wm_hints;
427     Window active_window;
428     HildonProgramPrivate *priv;
429
430     priv = HILDON_PROGRAM_GET_PRIVATE (program);
431     g_assert (priv);
432     
433     active_window = hildon_window_get_active_window();
434
435     if (active_window)
436     {
437       gint xerror;
438       
439       gdk_error_trap_push ();
440       wm_hints = XGetWMHints (GDK_DISPLAY (), active_window);
441       xerror = gdk_error_trap_pop ();
442       if (xerror)
443         return;
444
445       if (wm_hints)
446       {
447
448           if (wm_hints->window_group == priv->window_group)
449           {
450               if (!priv->is_topmost)
451               {
452                   priv->is_topmost = TRUE;
453                   g_object_notify (G_OBJECT (program), "is-topmost");
454               }
455           }
456           else if (priv->is_topmost)
457           {
458             priv->is_topmost = FALSE;
459             g_object_notify (G_OBJECT (program), "is-topmost");
460           }
461       }
462       XFree (wm_hints);
463     }
464
465     /* Check each window if it was is_topmost */
466     g_slist_foreach (priv->windows, 
467             (GFunc)hildon_program_window_list_is_is_topmost, &active_window);
468 }
469
470 /*
471  * We keep track of the _MB_CURRENT_APP_WINDOW property on the root window,
472  * to detect when a window belonging to this program was is_topmost. This
473  * is based on the window group WM hint.
474  */
475 static GdkFilterReturn
476 hildon_program_root_window_event_filter         (GdkXEvent *xevent,
477                                                  GdkEvent *event,
478                                                  gpointer data)
479 {
480     XAnyEvent *eventti = xevent;
481     HildonProgram *program = HILDON_PROGRAM (data);
482     Atom active_app_atom =
483             XInternAtom (GDK_DISPLAY (), "_MB_CURRENT_APP_WINDOW", False);
484
485     if (eventti->type == PropertyNotify)
486     {
487         XPropertyEvent *pevent = xevent;
488
489         if (pevent->atom == active_app_atom)
490         {
491             hildon_program_update_top_most (program);
492         }
493     }
494
495     return GDK_FILTER_CONTINUE;
496 }
497
498 /* 
499  * Checks if the window is the topmost window of the program and in
500  * that case forces the window to take the common toolbar.
501  */
502 static void
503 hildon_program_common_toolbar_topmost_window    (gpointer window, 
504                                                  gpointer data)
505 {
506     if (HILDON_IS_WINDOW (window) && hildon_window_get_is_topmost (HILDON_WINDOW (window)))
507         hildon_window_take_common_toolbar (HILDON_WINDOW (window));
508 }
509
510 /**
511  * hildon_program_get_instance:
512  *
513  * Return value: Returns the #HildonProgram for the current process.
514  * The object is created on the first call. Note that you're not supposed 
515  * to unref the returned object since it's not reffed in the first place.
516  **/
517 HildonProgram*
518 hildon_program_get_instance                     (void)
519 {
520     static HildonProgram *program = NULL;
521
522     if (! program)
523     {
524         program = g_object_new (HILDON_TYPE_PROGRAM, NULL);
525     }
526
527     return program;
528 }
529
530 /**
531  * hildon_program_add_window:
532  * @self: The #HildonProgram to which the window should be registered
533  * @window: A #HildonWindow to be added
534  *
535  * Registers a #HildonWindow as belonging to a given #HildonProgram. This
536  * allows to apply program-wide settings as all the registered windows,
537  * such as hildon_program_set_common_menu() and
538  * hildon_pogram_set_common_toolbar()
539  **/
540 void
541 hildon_program_add_window                       (HildonProgram *self, 
542                                                  HildonWindow *window)
543 {
544     HildonProgramPrivate *priv;
545     
546     g_return_if_fail (HILDON_IS_PROGRAM (self));
547     
548     priv = HILDON_PROGRAM_GET_PRIVATE (self);
549     g_assert (priv);
550
551     if (g_slist_find_custom (priv->windows, window,
552            hildon_program_window_list_compare) )
553     {
554         /* We already have that window */
555         return;
556     }
557
558     if (!priv->window_count)
559     {
560         hildon_program_update_top_most (self);
561         
562         /* Now that we have a window we should start keeping track of
563          * the root window */
564         gdk_window_set_events (gdk_get_default_root_window (),
565                 gdk_window_get_events (gdk_get_default_root_window ()) | GDK_PROPERTY_CHANGE_MASK);
566
567         gdk_window_add_filter (gdk_get_default_root_window (),
568                 hildon_program_root_window_event_filter, self );
569     }
570     
571     hildon_window_set_can_hibernate_property (window, &priv->killable);
572
573     hildon_window_set_program (window, G_OBJECT (self));
574
575     priv->windows = g_slist_append (priv->windows, window);
576     priv->window_count ++;
577 }
578
579 /**
580  * hildon_program_remove_window:
581  * @self: The #HildonProgram to which the window should be unregistered
582  * @window: The @HildonWindow to unregister
583  *
584  * Used to unregister a window from the program. Subsequent calls to
585  * hildon_program_set_common_menu() and hildon_pogram_set_common_toolbar()
586  * will not affect the window
587  **/
588 void
589 hildon_program_remove_window                    (HildonProgram *self, 
590                                                  HildonWindow *window)
591 {
592     HildonProgramPrivate *priv;
593     
594     g_return_if_fail (HILDON_IS_PROGRAM (self));
595     
596     priv = HILDON_PROGRAM_GET_PRIVATE (self);
597     g_assert (priv);
598     
599     hildon_window_unset_program (window);
600
601     priv->windows = g_slist_remove (priv->windows, window);
602
603     priv->window_count --;
604
605     if (! priv->window_count)
606         gdk_window_remove_filter (gdk_get_default_root_window(),
607                 hildon_program_root_window_event_filter,
608                 self);
609 }
610
611 /**
612  * hildon_program_set_can_hibernate:
613  * @self: The #HildonProgram which can hibernate or not
614  * @can_hibernate: whether or not the #HildonProgram can hibernate
615  *
616  * Used to set whether or not the Hildon task navigator should
617  * be able to set the program to hibernation in case of low memory
618  **/
619 void
620 hildon_program_set_can_hibernate                (HildonProgram *self, 
621                                                  gboolean can_hibernate)
622 {
623     HildonProgramPrivate *priv;
624     
625     g_return_if_fail (HILDON_IS_PROGRAM (self));
626     
627     priv = HILDON_PROGRAM_GET_PRIVATE (self);
628     g_assert (priv);
629
630     if (priv->killable != can_hibernate)
631     {
632         g_slist_foreach (priv->windows, 
633                 (GFunc) hildon_window_set_can_hibernate_property, &can_hibernate);
634     }
635
636     priv->killable = can_hibernate;
637 }
638
639 /**
640  * hildon_program_get_can_hibernate:
641  * @self: The #HildonProgram which can hibernate or not
642  * 
643  * Return value: Whether or not this #HildonProgram is set to be
644  * support hibernation from the Hildon task navigator
645  **/
646 gboolean
647 hildon_program_get_can_hibernate                (HildonProgram *self)
648 {
649     HildonProgramPrivate *priv;
650     
651     g_return_val_if_fail (HILDON_IS_PROGRAM (self), FALSE);
652    
653     priv = HILDON_PROGRAM_GET_PRIVATE (self);
654     g_assert (priv);
655
656     return priv->killable;
657 }
658
659 /**
660  * hildon_program_set_common_menu:
661  * @self: The #HildonProgram in which the common menu should be used
662  * @menu: A GtkMenu to use as common menu for the program
663  *
664  * Sets a GtkMenu that will appear in all the #HildonWindow registered
665  * with the #HildonProgram. Only one common GtkMenu can be set, further
666  * calls will detach the previous common GtkMenu. A #HildonWindow
667  * can use it's own GtkMenu with hildon_window_set_menu()
668  *
669  * This method does not support #HildonAppMenu objects.
670  **/
671 void
672 hildon_program_set_common_menu                  (HildonProgram *self, 
673                                                  GtkMenu *menu)
674 {
675     HildonProgramPrivate *priv;
676
677     g_return_if_fail (HILDON_IS_PROGRAM (self));
678
679     priv = HILDON_PROGRAM_GET_PRIVATE (self);
680     g_assert (priv);
681
682     if (priv->common_menu)
683     {
684         if (GTK_WIDGET_VISIBLE (priv->common_menu))
685         {
686             gtk_menu_popdown (GTK_MENU (priv->common_menu));
687             gtk_menu_shell_deactivate (GTK_MENU_SHELL (priv->common_menu));
688         }
689
690         if (gtk_menu_get_attach_widget (GTK_MENU (priv->common_menu)))
691         {
692             gtk_menu_detach (GTK_MENU (priv->common_menu));
693         }
694         else
695         {
696             g_object_unref (priv->common_menu);
697         }
698     }
699
700     priv->common_menu = GTK_WIDGET (menu);
701
702     if (priv->common_menu)
703     {
704         g_object_ref (menu);
705         gtk_object_sink (GTK_OBJECT (menu));
706         gtk_widget_show_all (GTK_WIDGET (menu));
707     }
708 }
709
710 /**
711  * hildon_program_get_common_menu:
712  * @self: The #HildonProgram from which to retrieve the common menu
713  *
714  * Return value: the GtkMenu that was set as common menu for this
715  * #HildonProgram, or %NULL of no common menu was set.
716  **/
717 GtkMenu*
718 hildon_program_get_common_menu                  (HildonProgram *self)
719 {
720     HildonProgramPrivate *priv;
721
722     g_return_val_if_fail (HILDON_IS_PROGRAM (self), NULL);
723
724     priv = HILDON_PROGRAM_GET_PRIVATE (self);
725     g_assert (priv);
726
727     return GTK_MENU (priv->common_menu);
728 }
729
730 /**
731  * hildon_program_set_common_toolbar:
732  * @self: The #HildonProgram in which the common toolbar should be used
733  * @toolbar: A GtkToolbar to use as common toolbar for the program
734  *
735  * Sets a GtkToolbar that will appear in all the #HildonWindow registered
736  * to the #HildonProgram. Only one common GtkToolbar can be set, further
737  * call will detach the previous common GtkToolbar. A #HildonWindow
738  * can use its own GtkToolbar with hildon_window_add_toolbar(). Both
739  * #HildonProgram and #HildonWindow specific toolbars will be shown
740  **/
741 void
742 hildon_program_set_common_toolbar               (HildonProgram *self, 
743                                                  GtkToolbar *toolbar)
744 {
745     HildonProgramPrivate *priv;
746
747     g_return_if_fail (HILDON_IS_PROGRAM (self));
748
749     priv = HILDON_PROGRAM_GET_PRIVATE (self);
750     g_assert (priv);
751
752     if (priv->common_toolbar)
753     {
754         if (priv->common_toolbar->parent)
755         {
756             gtk_container_remove (GTK_CONTAINER (priv->common_toolbar->parent), 
757                                   priv->common_toolbar);
758         }
759         
760         g_object_unref (priv->common_toolbar);
761     }
762
763     priv->common_toolbar = GTK_WIDGET (toolbar);
764
765     if (priv->common_toolbar)
766     {
767         g_object_ref (priv->common_toolbar);
768         gtk_object_sink (GTK_OBJECT (priv->common_toolbar) );
769     }
770
771     /* if the program is the topmost we have to update the common
772        toolbar right now for the topmost window */
773     if (priv->is_topmost)
774       {
775         g_slist_foreach (priv->windows, 
776                          (GFunc) hildon_program_common_toolbar_topmost_window, NULL);
777       }
778 }
779
780 /**
781  * hildon_program_get_common_toolbar:
782  * @self: The #HildonProgram from which to retrieve the common toolbar
783  *
784  * Return value: the GtkToolbar that was set as common toolbar for this
785  * #HildonProgram, or %NULL of no common menu was set.
786  **/
787 GtkToolbar*
788 hildon_program_get_common_toolbar               (HildonProgram *self)
789 {
790     HildonProgramPrivate *priv;
791
792     g_return_val_if_fail (HILDON_IS_PROGRAM (self), NULL);
793
794     priv = HILDON_PROGRAM_GET_PRIVATE (self);
795     g_assert (priv);
796
797     return priv->common_toolbar ? GTK_TOOLBAR (priv->common_toolbar) : NULL;
798 }
799
800 /**
801  * hildon_program_get_is_topmost:
802  * @self: A #HildonWindow
803  *
804  * Return value: Whether or not one of the program's window or dialog is 
805  * currenltly activated by the window manager.
806  **/
807 gboolean
808 hildon_program_get_is_topmost                   (HildonProgram *self)
809 {
810     HildonProgramPrivate *priv;
811
812     g_return_val_if_fail (HILDON_IS_PROGRAM (self), FALSE);
813     
814     priv = HILDON_PROGRAM_GET_PRIVATE (self);
815     g_assert (priv);
816
817     return priv->is_topmost;
818 }
819
820 /**
821  * hildon_program_go_to_root_window:
822  * @self: A #HildonProgram
823  *
824  * Will close all windows in the #HildonProgram but the first one (the
825  * root window) by sending them a delete event. If any of the windows
826  * refuses to close (by capturing the event) no further events will be
827  * sent. Only windows of type #HildonStackableWindow will be affected
828  * by this.
829  */
830 void
831 hildon_program_go_to_root_window                (HildonProgram *self)
832 {
833     HildonProgramPrivate *priv;
834     GSList *windows, *iter;
835     gboolean windows_left;
836
837     g_return_if_fail (HILDON_IS_PROGRAM (self));
838     priv = HILDON_PROGRAM_GET_PRIVATE (self);
839     g_assert (priv);
840
841     /* List of stacked windows (starting from the topmost one) */
842     windows = g_slist_copy (priv->window_stack);
843     iter = windows;
844
845     /* Destroy all the windows but the last one (which is the root
846      * window, as the list is reversed) */
847     windows_left = (iter != NULL && iter->next != NULL);
848     while (windows_left)
849     {
850         if (HILDON_IS_STACKABLE_WINDOW (iter->data))
851         {
852             GdkEvent *event;
853             HildonStackableWindow *win;
854
855             /* Mark the window as "going home" */
856             win = HILDON_STACKABLE_WINDOW (iter->data);
857             hildon_stackable_window_set_going_home (win, TRUE);
858
859             /* Set win pointer to NULL if the window is destroyed */
860             g_object_add_weak_pointer (G_OBJECT (win), (gpointer) &win);
861
862             /* Send a delete event */
863             event = gdk_event_new (GDK_DELETE);
864             event->any.window = g_object_ref (GTK_WIDGET (win)->window);
865             gtk_main_do_event (event);
866             gdk_event_free (event);
867
868             /* Continue sending delete events if the window has been destroyed */
869             if (win == NULL)
870             {
871                 iter = iter->next;
872                 windows_left = (iter != NULL && iter->next != NULL);
873             }
874             else
875             {
876                 g_object_remove_weak_pointer (G_OBJECT (win), (gpointer) &win);
877                 hildon_stackable_window_set_going_home (win, FALSE);
878                 windows_left = FALSE;
879             }
880         }
881         else
882         {
883             g_critical ("Window list contains a non-stackable window");
884             windows_left = FALSE;
885         }
886     }
887
888     /* Show the last window that hasn't been destroyed */
889     if (iter != NULL && GTK_IS_WIDGET (iter->data))
890     {
891         gtk_widget_show (GTK_WIDGET (iter->data));
892     }
893
894     g_slist_free (windows);
895 }