2 * This file is a part of hildon
4 * Copyright (C) 2006 Nokia Corporation, all rights reserved.
6 * Contact: Michael Dominic Kostrzewa <michael.kostrzewa@nokia.com>
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.
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.
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
26 * SECTION:hildon-program
27 * @short_description: An object that represents an application running in the Hildon framework.
28 * @see_also: #HildonWindow, #HildonStackableWindow
30 * The #HildonProgram is an object used to represent an application running
31 * in the Hildon framework.
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().
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().
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
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().
56 * HildonProgram *program;
57 * HildonWindow *window1;
58 * HildonWindow *window2;
59 * GtkToolbar *common_toolbar, *window_specific_toolbar;
62 * program = HILDON_PROGRAM (hildon_program_get_instance ());
64 * window1 = HILDON_WINDOW (hildon_window_new ());
65 * window2 = HILDON_WINDOW (hildon_window_new ());
67 * common_toolbar = create_common_toolbar ();
68 * window_specific_toolbar = create_window_specific_toolbar ();
70 * menu = create_menu ();
72 * hildon_program_add_window (program, window1);
73 * hildon_program_add_window (program, window2);
75 * hildon_program_set_common_menu (program, menu);
77 * hildon_program_set_common_toolbar (program, common_toolbar);
78 * hildon_window_add_toolbar (window1, window_specific_toolbar);
80 * hildon_program_set_can_hibernate (program, TRUE);
89 #include <X11/Xatom.h>
91 #include "hildon-program.h"
92 #include "hildon-program-private.h"
93 #include "hildon-window-private.h"
94 #include "hildon-stackable-window-private.h"
97 hildon_program_init (HildonProgram *self);
100 hildon_program_finalize (GObject *self);
103 hildon_program_class_init (HildonProgramClass *self);
106 hildon_program_get_property (GObject *object,
111 hildon_program_set_property (GObject *object,
124 hildon_program_get_type (void)
126 static GType program_type = 0;
130 static const GTypeInfo program_info =
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),
140 (GInstanceInitFunc) hildon_program_init,
142 program_type = g_type_register_static(G_TYPE_OBJECT,
143 "HildonProgram", &program_info, 0);
149 hildon_program_init (HildonProgram *self)
151 HildonProgramPrivate *priv = HILDON_PROGRAM_GET_PRIVATE (self);
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;
160 priv->windows = NULL;
161 priv->window_stack = NULL;
165 hildon_program_finalize (GObject *self)
167 HildonProgramPrivate *priv = HILDON_PROGRAM_GET_PRIVATE (HILDON_PROGRAM (self));
170 if (priv->common_toolbar)
172 g_object_unref (priv->common_toolbar);
173 priv->common_toolbar = NULL;
176 if (priv->common_menu)
178 g_object_unref (priv->common_menu);
179 priv->common_menu = NULL;
186 hildon_program_class_init (HildonProgramClass *self)
188 GObjectClass *object_class = G_OBJECT_CLASS (self);
190 g_type_class_add_private (self, sizeof (HildonProgramPrivate));
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;
197 /* Install properties */
200 * HildonProgram:is-topmost:
202 * Whether one of the program's window or dialog currently
203 * is activated by window manager.
205 g_object_class_install_property (object_class, PROP_IS_TOPMOST,
206 g_param_spec_boolean ("is-topmost",
208 "Whether one of the program's window or dialog currently "
209 "is activated by window manager",
214 * HildonProgram:can-hibernate:
216 * Whether the program should be set to hibernate by the Task
217 * Navigator in low memory situation.
219 g_object_class_install_property (object_class, PROP_KILLABLE,
220 g_param_spec_boolean ("can-hibernate",
222 "Whether the program should be set to hibernate by the Task "
223 "Navigator in low memory situation",
230 hildon_program_set_property (GObject *object,
235 switch (property_id) {
238 hildon_program_set_can_hibernate (HILDON_PROGRAM (object), g_value_get_boolean (value));
242 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
249 hildon_program_get_property (GObject *object,
254 HildonProgramPrivate *priv = HILDON_PROGRAM_GET_PRIVATE (object);
260 g_value_set_boolean (value, priv->killable);
263 case PROP_IS_TOPMOST:
264 g_value_set_boolean (value, priv->is_topmost);
268 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
274 * hildon_program_pop_window_stack:
275 * @self: A #HildonProgram
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.
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
288 * If the stack is empty, %NULL is returned.
290 * Returns: A #HildonStackableWindow, or %NULL.
292 HildonStackableWindow *
293 hildon_program_pop_window_stack (HildonProgram *self)
295 HildonStackableWindow *top;
297 top = hildon_program_peek_window_stack (self);
301 HildonStackableWindow *next;
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);
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));
311 gtk_widget_hide (GTK_WIDGET (top));
318 * hildon_program_peek_window_stack:
319 * @self: A #HildonProgram
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.
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.
329 * Returns: A #HildonStackableWindow, or %NULL.
331 HildonStackableWindow *
332 hildon_program_peek_window_stack (HildonProgram *self)
334 HildonStackableWindow *top = NULL;
335 HildonProgramPrivate *priv;
337 g_return_val_if_fail (HILDON_IS_PROGRAM (self), NULL);
339 priv = HILDON_PROGRAM_GET_PRIVATE (self);
342 if (priv->window_stack != NULL)
343 top = HILDON_STACKABLE_WINDOW (priv->window_stack->data);
349 _hildon_program_add_to_stack (HildonProgram *self,
350 HildonStackableWindow *win)
352 HildonProgramPrivate *priv;
354 g_return_if_fail (HILDON_IS_PROGRAM (self));
355 g_return_if_fail (HILDON_IS_STACKABLE_WINDOW (win));
357 priv = HILDON_PROGRAM_GET_PRIVATE (self);
360 if (g_slist_find (priv->window_stack, win) == NULL)
362 priv->window_stack = g_slist_prepend (priv->window_stack, win);
366 g_critical ("%s: window already in the stack!", __FUNCTION__);
371 gboolean G_GNUC_INTERNAL
372 _hildon_program_remove_from_stack (HildonProgram *self,
373 HildonStackableWindow *win)
376 HildonProgramPrivate *priv;
378 g_return_val_if_fail (HILDON_IS_PROGRAM (self), FALSE);
379 g_return_val_if_fail (HILDON_IS_STACKABLE_WINDOW (win), FALSE);
381 priv = HILDON_PROGRAM_GET_PRIVATE (self);
384 pos = g_slist_find (priv->window_stack, win);
387 priv->window_stack = g_slist_delete_link (priv->window_stack, pos);
389 return (pos != NULL);
394 hildon_program_window_list_compare (gconstpointer window_a,
395 gconstpointer window_b)
397 g_return_val_if_fail (HILDON_IS_WINDOW(window_a) &&
398 HILDON_IS_WINDOW(window_b), 1);
400 return window_a != window_b;
404 * foreach function, checks if a window is topmost and acts consequently
407 hildon_program_window_list_is_is_topmost (gpointer data,
410 if (data && HILDON_IS_WINDOW (data))
412 HildonWindow *window = HILDON_WINDOW (data);
413 Window window_id = * (Window*)window_id_;
415 hildon_window_update_topmost (window, window_id);
420 * Check the _MB_CURRENT_APP_WINDOW on the root window, and update
421 * the top_most status accordingly
424 hildon_program_update_top_most (HildonProgram *program)
427 Window active_window;
428 HildonProgramPrivate *priv;
430 priv = HILDON_PROGRAM_GET_PRIVATE (program);
433 active_window = hildon_window_get_active_window();
439 gdk_error_trap_push ();
440 wm_hints = XGetWMHints (GDK_DISPLAY (), active_window);
441 xerror = gdk_error_trap_pop ();
448 if (wm_hints->window_group == priv->window_group)
450 if (!priv->is_topmost)
452 priv->is_topmost = TRUE;
453 g_object_notify (G_OBJECT (program), "is-topmost");
456 else if (priv->is_topmost)
458 priv->is_topmost = FALSE;
459 g_object_notify (G_OBJECT (program), "is-topmost");
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);
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.
475 static GdkFilterReturn
476 hildon_program_root_window_event_filter (GdkXEvent *xevent,
480 XAnyEvent *eventti = xevent;
481 HildonProgram *program = HILDON_PROGRAM (data);
482 Atom active_app_atom =
483 XInternAtom (GDK_DISPLAY (), "_MB_CURRENT_APP_WINDOW", False);
485 if (eventti->type == PropertyNotify)
487 XPropertyEvent *pevent = xevent;
489 if (pevent->atom == active_app_atom)
491 hildon_program_update_top_most (program);
495 return GDK_FILTER_CONTINUE;
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.
503 hildon_program_common_toolbar_topmost_window (gpointer window,
506 if (HILDON_IS_WINDOW (window) && hildon_window_get_is_topmost (HILDON_WINDOW (window)))
507 hildon_window_take_common_toolbar (HILDON_WINDOW (window));
511 * hildon_program_get_instance:
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.
518 hildon_program_get_instance (void)
520 static HildonProgram *program = NULL;
524 program = g_object_new (HILDON_TYPE_PROGRAM, NULL);
531 * hildon_program_add_window:
532 * @self: The #HildonProgram to which the window should be registered
533 * @window: A #HildonWindow to be added
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()
541 hildon_program_add_window (HildonProgram *self,
542 HildonWindow *window)
544 HildonProgramPrivate *priv;
546 g_return_if_fail (HILDON_IS_PROGRAM (self));
548 priv = HILDON_PROGRAM_GET_PRIVATE (self);
551 if (g_slist_find_custom (priv->windows, window,
552 hildon_program_window_list_compare) )
554 /* We already have that window */
558 if (!priv->window_count)
560 hildon_program_update_top_most (self);
562 /* Now that we have a window we should start keeping track of
564 gdk_window_set_events (gdk_get_default_root_window (),
565 gdk_window_get_events (gdk_get_default_root_window ()) | GDK_PROPERTY_CHANGE_MASK);
567 gdk_window_add_filter (gdk_get_default_root_window (),
568 hildon_program_root_window_event_filter, self );
571 hildon_window_set_can_hibernate_property (window, &priv->killable);
573 hildon_window_set_program (window, G_OBJECT (self));
575 priv->windows = g_slist_append (priv->windows, window);
576 priv->window_count ++;
580 * hildon_program_remove_window:
581 * @self: The #HildonProgram to which the window should be unregistered
582 * @window: The @HildonWindow to unregister
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
589 hildon_program_remove_window (HildonProgram *self,
590 HildonWindow *window)
592 HildonProgramPrivate *priv;
594 g_return_if_fail (HILDON_IS_PROGRAM (self));
596 priv = HILDON_PROGRAM_GET_PRIVATE (self);
599 hildon_window_unset_program (window);
601 priv->windows = g_slist_remove (priv->windows, window);
603 priv->window_count --;
605 if (! priv->window_count)
606 gdk_window_remove_filter (gdk_get_default_root_window(),
607 hildon_program_root_window_event_filter,
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
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
620 hildon_program_set_can_hibernate (HildonProgram *self,
621 gboolean can_hibernate)
623 HildonProgramPrivate *priv;
625 g_return_if_fail (HILDON_IS_PROGRAM (self));
627 priv = HILDON_PROGRAM_GET_PRIVATE (self);
630 if (priv->killable != can_hibernate)
632 g_slist_foreach (priv->windows,
633 (GFunc) hildon_window_set_can_hibernate_property, &can_hibernate);
636 priv->killable = can_hibernate;
640 * hildon_program_get_can_hibernate:
641 * @self: The #HildonProgram which can hibernate or not
643 * Return value: Whether or not this #HildonProgram is set to be
644 * support hibernation from the Hildon task navigator
647 hildon_program_get_can_hibernate (HildonProgram *self)
649 HildonProgramPrivate *priv;
651 g_return_val_if_fail (HILDON_IS_PROGRAM (self), FALSE);
653 priv = HILDON_PROGRAM_GET_PRIVATE (self);
656 return priv->killable;
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
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()
669 * This method does not support #HildonAppMenu objects.
672 hildon_program_set_common_menu (HildonProgram *self,
675 HildonProgramPrivate *priv;
677 g_return_if_fail (HILDON_IS_PROGRAM (self));
679 priv = HILDON_PROGRAM_GET_PRIVATE (self);
682 if (priv->common_menu)
684 if (GTK_WIDGET_VISIBLE (priv->common_menu))
686 gtk_menu_popdown (GTK_MENU (priv->common_menu));
687 gtk_menu_shell_deactivate (GTK_MENU_SHELL (priv->common_menu));
690 if (gtk_menu_get_attach_widget (GTK_MENU (priv->common_menu)))
692 gtk_menu_detach (GTK_MENU (priv->common_menu));
696 g_object_unref (priv->common_menu);
700 priv->common_menu = GTK_WIDGET (menu);
702 if (priv->common_menu)
705 gtk_object_sink (GTK_OBJECT (menu));
706 gtk_widget_show_all (GTK_WIDGET (menu));
711 * hildon_program_get_common_menu:
712 * @self: The #HildonProgram from which to retrieve the common menu
714 * Return value: the GtkMenu that was set as common menu for this
715 * #HildonProgram, or %NULL of no common menu was set.
718 hildon_program_get_common_menu (HildonProgram *self)
720 HildonProgramPrivate *priv;
722 g_return_val_if_fail (HILDON_IS_PROGRAM (self), NULL);
724 priv = HILDON_PROGRAM_GET_PRIVATE (self);
727 return GTK_MENU (priv->common_menu);
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
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
742 hildon_program_set_common_toolbar (HildonProgram *self,
745 HildonProgramPrivate *priv;
747 g_return_if_fail (HILDON_IS_PROGRAM (self));
749 priv = HILDON_PROGRAM_GET_PRIVATE (self);
752 if (priv->common_toolbar)
754 if (priv->common_toolbar->parent)
756 gtk_container_remove (GTK_CONTAINER (priv->common_toolbar->parent),
757 priv->common_toolbar);
760 g_object_unref (priv->common_toolbar);
763 priv->common_toolbar = GTK_WIDGET (toolbar);
765 if (priv->common_toolbar)
767 g_object_ref (priv->common_toolbar);
768 gtk_object_sink (GTK_OBJECT (priv->common_toolbar) );
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)
775 g_slist_foreach (priv->windows,
776 (GFunc) hildon_program_common_toolbar_topmost_window, NULL);
781 * hildon_program_get_common_toolbar:
782 * @self: The #HildonProgram from which to retrieve the common toolbar
784 * Return value: the GtkToolbar that was set as common toolbar for this
785 * #HildonProgram, or %NULL of no common menu was set.
788 hildon_program_get_common_toolbar (HildonProgram *self)
790 HildonProgramPrivate *priv;
792 g_return_val_if_fail (HILDON_IS_PROGRAM (self), NULL);
794 priv = HILDON_PROGRAM_GET_PRIVATE (self);
797 return priv->common_toolbar ? GTK_TOOLBAR (priv->common_toolbar) : NULL;
801 * hildon_program_get_is_topmost:
802 * @self: A #HildonWindow
804 * Return value: Whether or not one of the program's window or dialog is
805 * currenltly activated by the window manager.
808 hildon_program_get_is_topmost (HildonProgram *self)
810 HildonProgramPrivate *priv;
812 g_return_val_if_fail (HILDON_IS_PROGRAM (self), FALSE);
814 priv = HILDON_PROGRAM_GET_PRIVATE (self);
817 return priv->is_topmost;
821 * hildon_program_go_to_root_window:
822 * @self: A #HildonProgram
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
831 hildon_program_go_to_root_window (HildonProgram *self)
833 HildonProgramPrivate *priv;
834 GSList *windows, *iter;
835 gboolean windows_left;
837 g_return_if_fail (HILDON_IS_PROGRAM (self));
838 priv = HILDON_PROGRAM_GET_PRIVATE (self);
841 /* List of stacked windows (starting from the topmost one) */
842 windows = g_slist_copy (priv->window_stack);
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);
850 if (HILDON_IS_STACKABLE_WINDOW (iter->data))
853 HildonStackableWindow *win;
855 /* Mark the window as "going home" */
856 win = HILDON_STACKABLE_WINDOW (iter->data);
857 hildon_stackable_window_set_going_home (win, TRUE);
859 /* Set win pointer to NULL if the window is destroyed */
860 g_object_add_weak_pointer (G_OBJECT (win), (gpointer) &win);
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);
868 /* Continue sending delete events if the window has been destroyed */
872 windows_left = (iter != NULL && iter->next != NULL);
876 g_object_remove_weak_pointer (G_OBJECT (win), (gpointer) &win);
877 hildon_stackable_window_set_going_home (win, FALSE);
878 windows_left = FALSE;
883 g_critical ("Window list contains a non-stackable window");
884 windows_left = FALSE;
888 /* Show the last window that hasn't been destroyed */
889 if (iter != NULL && GTK_IS_WIDGET (iter->data))
891 gtk_widget_show (GTK_WIDGET (iter->data));
894 g_slist_free (windows);