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