2 * This file is a part of hildon
4 * Copyright (C) 2006 Nokia Corporation, all rights reserved.
6 * Contact: Rodrigo Novo <rodrigo.novo@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: Application abstraction in the Hildon framework.
28 * @see_also: #HildonWindow, #HildonStackableWindow
30 * #HildonProgram is an object used to represent an application running
31 * in the Hildon framework.
33 * Applications can have one or more #HildonWindow<!-- -->s. These
34 * can be registered in the #HildonProgram with hildon_program_add_window(),
35 * and can be unregistered similarly with hildon_program_remove_window().
37 * #HildonProgram provides the programmer with commodities such
38 * as applying a common toolbar and menu to all registered
39 * #HildonWindow<!-- -->s. This is done with hildon_program_set_common_menu(),
40 * hildon_program_set_common_app_menu() and hildon_program_set_common_toolbar().
42 * #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
50 * HildonProgram *program;
51 * HildonWindow *window1;
52 * HildonWindow *window2;
53 * GtkToolbar *common_toolbar, *window_specific_toolbar;
54 * HildonAppMenu *menu;
56 * program = HILDON_PROGRAM (hildon_program_get_instance ());
58 * window1 = HILDON_WINDOW (hildon_window_new ());
59 * window2 = HILDON_WINDOW (hildon_window_new ());
61 * common_toolbar = create_common_toolbar ();
62 * window_specific_toolbar = create_window_specific_toolbar ();
64 * menu = create_menu ();
66 * hildon_program_add_window (program, window1);
67 * hildon_program_add_window (program, window2);
69 * hildon_program_set_common_app_menu (program, menu);
71 * hildon_program_set_common_toolbar (program, common_toolbar);
72 * hildon_window_add_toolbar (window1, window_specific_toolbar);
74 * hildon_program_set_can_hibernate (program, TRUE);
79 #undef HILDON_DISABLE_DEPRECATED
85 #include <X11/Xatom.h>
87 #include "hildon-program.h"
88 #include "hildon-program-private.h"
89 #include "hildon-window-private.h"
90 #include "hildon-window-stack.h"
91 #include "hildon-app-menu-private.h"
94 hildon_program_init (HildonProgram *self);
97 hildon_program_finalize (GObject *self);
100 hildon_program_class_init (HildonProgramClass *self);
103 hildon_program_get_property (GObject *object,
108 hildon_program_set_property (GObject *object,
121 hildon_program_get_type (void)
123 static GType program_type = 0;
127 static const GTypeInfo program_info =
129 sizeof (HildonProgramClass),
130 NULL, /* base_init */
131 NULL, /* base_finalize */
132 (GClassInitFunc) hildon_program_class_init,
133 NULL, /* class_finalize */
134 NULL, /* class_data */
135 sizeof (HildonProgram),
137 (GInstanceInitFunc) hildon_program_init,
139 program_type = g_type_register_static(G_TYPE_OBJECT,
140 "HildonProgram", &program_info, 0);
146 hildon_program_init (HildonProgram *self)
148 HildonProgramPrivate *priv = HILDON_PROGRAM_GET_PRIVATE (self);
151 priv->killable = FALSE;
152 priv->window_count = 0;
153 priv->is_topmost = FALSE;
154 priv->window_group = GDK_WINDOW_XID (gdk_display_get_default_group (gdk_display_get_default()));
155 priv->common_menu = NULL;
156 priv->common_app_menu = NULL;
157 priv->common_toolbar = NULL;
158 priv->windows = NULL;
162 hildon_program_finalize (GObject *self)
164 HildonProgramPrivate *priv = HILDON_PROGRAM_GET_PRIVATE (HILDON_PROGRAM (self));
167 if (priv->common_toolbar)
169 g_object_unref (priv->common_toolbar);
170 priv->common_toolbar = NULL;
173 if (priv->common_menu)
175 g_object_unref (priv->common_menu);
176 priv->common_menu = NULL;
181 hildon_program_class_init (HildonProgramClass *self)
183 GObjectClass *object_class = G_OBJECT_CLASS (self);
185 g_type_class_add_private (self, sizeof (HildonProgramPrivate));
187 /* Set up object virtual functions */
188 object_class->finalize = hildon_program_finalize;
189 object_class->set_property = hildon_program_set_property;
190 object_class->get_property = hildon_program_get_property;
192 /* Install properties */
195 * HildonProgram:is-topmost:
197 * Whether one of the program's window or dialog currently
198 * is activated by window manager.
200 g_object_class_install_property (object_class, PROP_IS_TOPMOST,
201 g_param_spec_boolean ("is-topmost",
203 "Whether one of the program's window or dialog currently "
204 "is activated by window manager",
209 * HildonProgram:can-hibernate:
211 * Whether the program should be set to hibernate by the Task
212 * Navigator in low memory situation.
214 g_object_class_install_property (object_class, PROP_KILLABLE,
215 g_param_spec_boolean ("can-hibernate",
217 "Whether the program should be set to hibernate by the Task "
218 "Navigator in low memory situation",
225 hildon_program_set_property (GObject *object,
230 switch (property_id) {
233 hildon_program_set_can_hibernate (HILDON_PROGRAM (object), g_value_get_boolean (value));
237 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
244 hildon_program_get_property (GObject *object,
249 HildonProgramPrivate *priv = HILDON_PROGRAM_GET_PRIVATE (object);
255 g_value_set_boolean (value, priv->killable);
258 case PROP_IS_TOPMOST:
259 g_value_set_boolean (value, priv->is_topmost);
263 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
269 * hildon_program_pop_window_stack:
270 * @self: A #HildonProgram
272 * Pops a window from the stack.
274 * Deprecated: Use hildon_window_stack_pop() instead
276 * Returns: A #HildonStackableWindow, or %NULL
280 HildonStackableWindow *
281 hildon_program_pop_window_stack (HildonProgram *self)
283 HildonWindowStack *stack = hildon_window_stack_get_default ();
284 GtkWidget *win = hildon_window_stack_pop_1 (stack);
285 g_warning ("%s: this function is deprecated. Use hildon_window_stack_pop() instead", __FUNCTION__);
286 return win ? HILDON_STACKABLE_WINDOW (win) : NULL;
290 * hildon_program_peek_window_stack:
291 * @self: A #HildonProgram
293 * Deprecated: Use hildon_window_stack_peek() instead
295 * Returns: A #HildonStackableWindow, or %NULL
299 HildonStackableWindow *
300 hildon_program_peek_window_stack (HildonProgram *self)
302 HildonWindowStack *stack = hildon_window_stack_get_default ();
303 GtkWidget *win = hildon_window_stack_peek (stack);
304 g_warning ("%s: this function is deprecated. Use hildon_window_stack_peek() instead", __FUNCTION__);
305 return win ? HILDON_STACKABLE_WINDOW (win) : NULL;
310 hildon_program_window_list_compare (gconstpointer window_a,
311 gconstpointer window_b)
313 g_return_val_if_fail (HILDON_IS_WINDOW(window_a) &&
314 HILDON_IS_WINDOW(window_b), 1);
316 return window_a != window_b;
320 * foreach function, checks if a window is topmost and acts consequently
323 hildon_program_window_list_is_is_topmost (gpointer data,
326 if (data && HILDON_IS_WINDOW (data))
328 HildonWindow *window = HILDON_WINDOW (data);
329 Window window_id = * (Window*)window_id_;
331 hildon_window_update_topmost (window, window_id);
336 * Check the _MB_CURRENT_APP_WINDOW on the root window, and update
337 * the top_most status accordingly
340 hildon_program_update_top_most (HildonProgram *program)
343 Window active_window;
344 HildonProgramPrivate *priv;
346 priv = HILDON_PROGRAM_GET_PRIVATE (program);
349 active_window = hildon_window_get_active_window();
357 gdk_error_trap_push ();
358 wm_hints = XGetWMHints (GDK_DISPLAY (), active_window);
359 xerror = gdk_error_trap_pop ();
360 if (xerror && xerror != BadWindow)
369 is_topmost = (wm_hints->window_group == priv->window_group);
374 /* Send notification if is_topmost has changed */
375 if (!priv->is_topmost != !is_topmost)
377 priv->is_topmost = is_topmost;
378 g_object_notify (G_OBJECT (program), "is-topmost");
381 /* Check each window if it was is_topmost */
382 g_slist_foreach (priv->windows,
383 (GFunc)hildon_program_window_list_is_is_topmost, &active_window);
387 * We keep track of the _MB_CURRENT_APP_WINDOW property on the root window,
388 * to detect when a window belonging to this program was is_topmost. This
389 * is based on the window group WM hint.
391 static GdkFilterReturn
392 hildon_program_root_window_event_filter (GdkXEvent *xevent,
396 XAnyEvent *eventti = xevent;
397 HildonProgram *program = HILDON_PROGRAM (data);
398 Atom active_app_atom =
399 XInternAtom (GDK_DISPLAY (), "_MB_CURRENT_APP_WINDOW", False);
401 if (eventti->type == PropertyNotify)
403 XPropertyEvent *pevent = xevent;
405 if (pevent->atom == active_app_atom)
407 hildon_program_update_top_most (program);
411 return GDK_FILTER_CONTINUE;
415 hildon_program_window_set_common_menu_flag (HildonWindow *window,
416 gboolean common_menu)
418 if (HILDON_IS_WINDOW (window))
420 gboolean has_menu = hildon_window_get_app_menu (window) ||
421 hildon_window_get_main_menu (window);
424 hildon_window_set_menu_flag (window, common_menu);
430 hildon_program_set_common_menu_flag (HildonProgram *self,
431 gboolean common_menu)
433 HildonProgramPrivate *priv = HILDON_PROGRAM_GET_PRIVATE (self);
435 g_slist_foreach (priv->windows,
436 (GFunc) hildon_program_window_set_common_menu_flag,
437 GINT_TO_POINTER (common_menu));
441 * Checks if the window is the topmost window of the program and in
442 * that case forces the window to take the common toolbar.
445 hildon_program_common_toolbar_topmost_window (gpointer window,
448 if (HILDON_IS_WINDOW (window) && hildon_window_get_is_topmost (HILDON_WINDOW (window)))
449 hildon_window_take_common_toolbar (HILDON_WINDOW (window));
453 * hildon_program_get_instance:
455 * Returns the #HildonProgram for the current process. The object is
456 * created on the first call. Note that you're not supposed to unref
457 * the returned object since it's not reffed in the first place.
459 * Return value: the #HildonProgram.
462 hildon_program_get_instance (void)
464 static HildonProgram *program = NULL;
468 program = g_object_new (HILDON_TYPE_PROGRAM, NULL);
475 * hildon_program_add_window:
476 * @self: The #HildonProgram to which the window should be registered
477 * @window: A #HildonWindow to be added
479 * Registers a #HildonWindow as belonging to a given #HildonProgram. This
480 * allows to apply program-wide settings as all the registered windows,
481 * such as hildon_program_set_common_menu(), hildon_program_set_common_app_menu()
482 * and hildon_program_set_common_toolbar().
485 hildon_program_add_window (HildonProgram *self,
486 HildonWindow *window)
488 HildonProgramPrivate *priv;
490 g_return_if_fail (HILDON_IS_PROGRAM (self));
491 g_return_if_fail (HILDON_IS_WINDOW (window));
493 priv = HILDON_PROGRAM_GET_PRIVATE (self);
496 if (g_slist_find_custom (priv->windows, window,
497 hildon_program_window_list_compare) )
499 /* We already have that window */
503 if (!priv->window_count)
505 hildon_program_update_top_most (self);
507 /* Now that we have a window we should start keeping track of
509 gdk_window_set_events (gdk_get_default_root_window (),
510 gdk_window_get_events (gdk_get_default_root_window ()) | GDK_PROPERTY_CHANGE_MASK);
512 gdk_window_add_filter (gdk_get_default_root_window (),
513 hildon_program_root_window_event_filter, self );
516 hildon_window_set_can_hibernate_property (window, &priv->killable);
518 hildon_window_set_program (window, G_OBJECT (self));
520 if (priv->common_menu || priv->common_app_menu)
521 hildon_program_window_set_common_menu_flag (window, TRUE);
523 priv->windows = g_slist_append (priv->windows, window);
524 priv->window_count ++;
528 * hildon_program_remove_window:
529 * @self: The #HildonProgram to which the window should be unregistered
530 * @window: The #HildonWindow to unregister
532 * Used to unregister a window from the program. Subsequent calls to
533 * hildon_program_set_common_menu(), hildon_program_set_common_app_menu()
534 * and hildon_program_set_common_toolbar() will not affect the window.
537 hildon_program_remove_window (HildonProgram *self,
538 HildonWindow *window)
540 HildonProgramPrivate *priv;
542 g_return_if_fail (HILDON_IS_PROGRAM (self));
543 g_return_if_fail (HILDON_IS_WINDOW (window));
545 priv = HILDON_PROGRAM_GET_PRIVATE (self);
548 g_return_if_fail (g_slist_find (priv->windows, window));
550 hildon_window_unset_program (window);
552 priv->windows = g_slist_remove (priv->windows, window);
554 priv->window_count --;
556 if (! priv->window_count)
557 gdk_window_remove_filter (gdk_get_default_root_window(),
558 hildon_program_root_window_event_filter,
561 if (priv->common_menu || priv->common_app_menu)
562 hildon_program_window_set_common_menu_flag (window, FALSE);
566 * hildon_program_set_can_hibernate:
567 * @self: The #HildonProgram which can hibernate or not
568 * @can_hibernate: whether or not the #HildonProgram can hibernate
570 * Used to set whether or not the Hildon task navigator should
571 * be able to set the program to hibernation in case of low memory
574 hildon_program_set_can_hibernate (HildonProgram *self,
575 gboolean can_hibernate)
577 HildonProgramPrivate *priv;
579 g_return_if_fail (HILDON_IS_PROGRAM (self));
581 priv = HILDON_PROGRAM_GET_PRIVATE (self);
584 if (priv->killable != can_hibernate)
586 g_slist_foreach (priv->windows,
587 (GFunc) hildon_window_set_can_hibernate_property, &can_hibernate);
590 priv->killable = can_hibernate;
594 * hildon_program_get_can_hibernate:
595 * @self: The #HildonProgram which can hibernate or not
597 * Returns whether the #HildonProgram is set to be support hibernation
598 * from the Hildon task navigator
600 * Return value: %TRUE if the program can hibernate, %FALSE otherwise.
603 hildon_program_get_can_hibernate (HildonProgram *self)
605 HildonProgramPrivate *priv;
607 g_return_val_if_fail (HILDON_IS_PROGRAM (self), FALSE);
609 priv = HILDON_PROGRAM_GET_PRIVATE (self);
612 return priv->killable;
616 * hildon_program_set_common_menu:
617 * @self: The #HildonProgram in which the common menu should be used
618 * @menu: A #GtkMenu to use as common menu for the program
620 * Sets a #GtkMenu that will appear in all #HildonWindow<!-- -->s
621 * registered with the #HildonProgram. Only one common #GtkMenu can be
622 * set, further calls will detach the previous common #GtkMenu. A
623 * #HildonWindow can use its own #GtkMenu with
624 * hildon_window_set_menu()
626 * This method does not support #HildonAppMenu<!-- -->s. See
627 * hildon_program_set_common_app_menu() for that.
630 hildon_program_set_common_menu (HildonProgram *self,
633 HildonProgramPrivate *priv;
635 g_return_if_fail (HILDON_IS_PROGRAM (self));
637 priv = HILDON_PROGRAM_GET_PRIVATE (self);
640 if (priv->common_menu)
642 if (GTK_WIDGET_VISIBLE (priv->common_menu))
644 gtk_menu_popdown (priv->common_menu);
645 gtk_menu_shell_deactivate (GTK_MENU_SHELL (priv->common_menu));
648 if (gtk_menu_get_attach_widget (priv->common_menu))
650 gtk_menu_detach (priv->common_menu);
654 g_object_unref (priv->common_menu);
658 /* Only set the menu flag if there was no common menu and
659 we are setting one. If we are unsetting the current common menu,
660 remove the commmon menu flag. Otherwise, nothing to do. */
662 GList *menu_children = gtk_container_get_children (GTK_CONTAINER (menu));
663 if (!priv->common_menu
664 && menu && menu_children != NULL) {
665 hildon_program_set_common_menu_flag (self, TRUE);
666 } else if (priv->common_menu &&
667 (!menu || menu_children == NULL))
669 hildon_program_set_common_menu_flag (self, FALSE);
671 g_list_free (menu_children);
673 priv->common_menu = menu;
675 if (priv->common_menu)
678 gtk_object_sink (GTK_OBJECT (menu));
679 gtk_widget_show_all (GTK_WIDGET (menu));
684 * hildon_program_get_common_menu:
685 * @self: The #HildonProgram from which to retrieve the common menu
687 * Returns the #GtkMenu that was set as common menu for this
690 * Return value: the #GtkMenu or %NULL of no common menu was set.
693 hildon_program_get_common_menu (HildonProgram *self)
695 HildonProgramPrivate *priv;
697 g_return_val_if_fail (HILDON_IS_PROGRAM (self), NULL);
699 priv = HILDON_PROGRAM_GET_PRIVATE (self);
702 return priv->common_menu;
706 hildon_program_on_common_app_menu_changed (HildonAppMenu *menu,
707 HildonProgram *program)
709 hildon_program_set_common_menu_flag (program,
710 hildon_app_menu_has_visible_children (menu));
714 * hildon_program_set_common_app_menu:
715 * @self: The #HildonProgram in which the common menu should be used
716 * @menu: A #HildonAppMenu to use as common menu for the program
718 * Sets a #HildonAppMenu that will appear in all
719 * #HildonWindow<!-- -->s registered with the #HildonProgram. Only
720 * one common #HildonAppMenu can be set, further calls will detach the
721 * previous common #HildonAppMenu. A #HildonWindow can use its own
722 * #HildonAppMenu with hildon_window_set_app_menu()
724 * This method does not support #GtkMenu<!-- -->s. See
725 * hildon_program_set_common_menu() for that.
730 hildon_program_set_common_app_menu (HildonProgram *self,
733 HildonProgramPrivate *priv;
734 HildonAppMenu *old_menu;
736 g_return_if_fail (HILDON_IS_PROGRAM (self));
737 g_return_if_fail (menu == NULL || HILDON_IS_APP_MENU (menu));
739 priv = HILDON_PROGRAM_GET_PRIVATE (self);
742 old_menu = priv->common_app_menu;
744 /* Only set the menu flag if there was no common menu and
745 we are setting one. If we are unsetting the current common menu,
746 remove the commmon menu flag. Otherwise, nothing to do. */
747 if (!priv->common_app_menu
748 && menu && hildon_app_menu_has_visible_children (menu)) {
749 hildon_program_set_common_menu_flag (self, TRUE);
750 } else if (priv->common_app_menu &&
751 (!menu || !hildon_app_menu_has_visible_children (menu))) {
752 hildon_program_set_common_menu_flag (self, FALSE);
756 priv->common_app_menu = menu;
758 g_signal_connect (menu, "changed",
759 G_CALLBACK (hildon_program_on_common_app_menu_changed), self);
760 g_object_ref_sink (menu);
763 /* Hide and unref old menu */
765 hildon_app_menu_set_parent_window (old_menu, NULL);
766 g_signal_handlers_disconnect_by_func (old_menu,
767 hildon_program_on_common_app_menu_changed,
769 g_object_unref (old_menu);
774 * hildon_program_get_common_app_menu:
775 * @self: The #HildonProgram from which to retrieve the common app menu
777 * Returns the #HildonAppMenu that was set as common menu for this
780 * Return value: the #HildonAppMenu or %NULL of no common app menu was
786 hildon_program_get_common_app_menu (HildonProgram *self)
788 HildonProgramPrivate *priv;
790 g_return_val_if_fail (HILDON_IS_PROGRAM (self), NULL);
792 priv = HILDON_PROGRAM_GET_PRIVATE (self);
795 return priv->common_app_menu;
799 * hildon_program_set_common_toolbar:
800 * @self: The #HildonProgram in which the common toolbar should be used
801 * @toolbar: A #GtkToolbar to use as common toolbar for the program
803 * Sets a #GtkToolbar that will appear in all the #HildonWindow registered
804 * to the #HildonProgram. Only one common #GtkToolbar can be set, further
805 * call will detach the previous common #GtkToolbar. A #HildonWindow
806 * can use its own #GtkToolbar with hildon_window_add_toolbar(). Both
807 * #HildonProgram and #HildonWindow specific toolbars will be shown
810 hildon_program_set_common_toolbar (HildonProgram *self,
813 HildonProgramPrivate *priv;
815 g_return_if_fail (HILDON_IS_PROGRAM (self));
817 priv = HILDON_PROGRAM_GET_PRIVATE (self);
820 if (priv->common_toolbar)
822 if (priv->common_toolbar->parent)
824 gtk_container_remove (GTK_CONTAINER (priv->common_toolbar->parent),
825 priv->common_toolbar);
828 g_object_unref (priv->common_toolbar);
831 priv->common_toolbar = GTK_WIDGET (toolbar);
833 if (priv->common_toolbar)
835 g_object_ref (priv->common_toolbar);
836 gtk_object_sink (GTK_OBJECT (priv->common_toolbar) );
839 /* if the program is the topmost we have to update the common
840 toolbar right now for the topmost window */
841 if (priv->is_topmost)
843 g_slist_foreach (priv->windows,
844 (GFunc) hildon_program_common_toolbar_topmost_window, NULL);
849 * hildon_program_get_common_toolbar:
850 * @self: The #HildonProgram from which to retrieve the common toolbar
852 * Returns the #GtkToolbar that was set as common toolbar for this
855 * Return value: the #GtkToolbar or %NULL of no common toolbar was
859 hildon_program_get_common_toolbar (HildonProgram *self)
861 HildonProgramPrivate *priv;
863 g_return_val_if_fail (HILDON_IS_PROGRAM (self), NULL);
865 priv = HILDON_PROGRAM_GET_PRIVATE (self);
868 return priv->common_toolbar ? GTK_TOOLBAR (priv->common_toolbar) : NULL;
872 * hildon_program_get_is_topmost:
873 * @self: A #HildonWindow
875 * Returns whether one of the program's windows or dialogs is
876 * currently activated by the window manager.
878 * Return value: %TRUE if a window or dialog is topmost, %FALSE
882 hildon_program_get_is_topmost (HildonProgram *self)
884 HildonProgramPrivate *priv;
886 g_return_val_if_fail (HILDON_IS_PROGRAM (self), FALSE);
888 priv = HILDON_PROGRAM_GET_PRIVATE (self);
891 return priv->is_topmost;
895 * hildon_program_go_to_root_window:
896 * @self: A #HildonProgram
898 * Goes to the root window of the stack.
900 * Deprecated: See #HildonWindowStack
905 hildon_program_go_to_root_window (HildonProgram *self)
907 HildonWindowStack *stack = hildon_window_stack_get_default ();
908 gint n = hildon_window_stack_size (stack);
909 g_warning ("%s: this function is deprecated. Use hildon_window_stack_pop() instead.", __FUNCTION__);
911 hildon_window_stack_pop (stack, n-1, NULL);