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.
29 * The HildonProgram is an object used to represent an application running
30 * in the Hildon framework.
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.
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().
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
49 * HildonProgram *program;
50 * HildonWindow *window1;
51 * HildonWindow *window2;
52 * GtkToolbar *common_toolbar, *window_specific_toolbar;
55 * program = HILDON_PROGRAM (hildon_program_get_instance ());
57 * window1 = HILDON_WINDOW (hildon_window_new ());
58 * window2 = HILDON_WINDOW (hildon_window_new ());
60 * common_toolbar = create_common_toolbar ();
61 * window_specific_toolbar = create_window_specific_toolbar ();
63 * menu = create_menu ();
65 * hildon_program_add_window (program, window1);
66 * hildon_program_add_window (program, window2);
68 * hildon_program_set_common_menu (program, menu);
70 * hildon_program_set_common_toolbar (program, common_toolbar);
71 * hildon_window_add_toolbar (window1, window_specific_toolbar);
73 * hildon_program_set_can_hibernate (program, TRUE);
82 #include <X11/Xatom.h>
84 #include "hildon-program.h"
85 #include "hildon-program-private.h"
86 #include "hildon-window-private.h"
87 #include "hildon-stackable-window-private.h"
90 hildon_program_init (HildonProgram *self);
93 hildon_program_finalize (GObject *self);
96 hildon_program_class_init (HildonProgramClass *self);
99 hildon_program_get_property (GObject *object,
104 hildon_program_set_property (GObject *object,
117 hildon_program_get_type (void)
119 static GType program_type = 0;
123 static const GTypeInfo program_info =
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),
133 (GInstanceInitFunc) hildon_program_init,
135 program_type = g_type_register_static(G_TYPE_OBJECT,
136 "HildonProgram", &program_info, 0);
142 hildon_program_init (HildonProgram *self)
144 HildonProgramPrivate *priv = HILDON_PROGRAM_GET_PRIVATE (self);
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;
153 priv->windows = NULL;
154 priv->window_stack = NULL;
158 hildon_program_finalize (GObject *self)
160 HildonProgramPrivate *priv = HILDON_PROGRAM_GET_PRIVATE (HILDON_PROGRAM (self));
163 if (priv->common_toolbar)
165 g_object_unref (priv->common_toolbar);
166 priv->common_toolbar = NULL;
169 if (priv->common_menu)
171 g_object_unref (priv->common_menu);
172 priv->common_menu = NULL;
179 hildon_program_class_init (HildonProgramClass *self)
181 GObjectClass *object_class = G_OBJECT_CLASS (self);
183 g_type_class_add_private (self, sizeof (HildonProgramPrivate));
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;
190 /* Install properties */
193 * HildonProgram:is-topmost:
195 * Whether one of the program's window or dialog currently
196 * is activated by window manager.
198 g_object_class_install_property (object_class, PROP_IS_TOPMOST,
199 g_param_spec_boolean ("is-topmost",
201 "Whether one of the program's window or dialog currently "
202 "is activated by window manager",
207 * HildonProgram:can-hibernate:
209 * Whether the program should be set to hibernate by the Task
210 * Navigator in low memory situation.
212 g_object_class_install_property (object_class, PROP_KILLABLE,
213 g_param_spec_boolean ("can-hibernate",
215 "Whether the program should be set to hibernate by the Task "
216 "Navigator in low memory situation",
223 hildon_program_set_property (GObject *object,
228 switch (property_id) {
231 hildon_program_set_can_hibernate (HILDON_PROGRAM (object), g_value_get_boolean (value));
235 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
242 hildon_program_get_property (GObject *object,
247 HildonProgramPrivate *priv = HILDON_PROGRAM_GET_PRIVATE (object);
253 g_value_set_boolean (value, priv->killable);
256 case PROP_IS_TOPMOST:
257 g_value_set_boolean (value, priv->is_topmost);
261 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
267 * hildon_program_pop_window_stack:
268 * @self: A #HildonProgram
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.
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
281 * If the stack is empty, %NULL is returned.
283 * Returns: A #HildonStackableWindow, or %NULL.
285 HildonStackableWindow *
286 hildon_program_pop_window_stack (HildonProgram *self)
288 HildonStackableWindow *top;
290 top = hildon_program_peek_window_stack (self);
294 HildonStackableWindow *next;
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);
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));
304 gtk_widget_hide (GTK_WIDGET (top));
311 * hildon_program_peek_window_stack:
312 * @self: A #HildonProgram
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.
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.
322 * Returns: A #HildonStackableWindow, or %NULL.
324 HildonStackableWindow *
325 hildon_program_peek_window_stack (HildonProgram *self)
327 HildonStackableWindow *top = NULL;
328 HildonProgramPrivate *priv;
330 g_return_val_if_fail (HILDON_IS_PROGRAM (self), NULL);
332 priv = HILDON_PROGRAM_GET_PRIVATE (self);
335 if (priv->window_stack != NULL)
336 top = HILDON_STACKABLE_WINDOW (priv->window_stack->data);
342 _hildon_program_add_to_stack (HildonProgram *self,
343 HildonStackableWindow *win)
345 HildonProgramPrivate *priv;
347 g_return_if_fail (HILDON_IS_PROGRAM (self));
348 g_return_if_fail (HILDON_IS_STACKABLE_WINDOW (win));
350 priv = HILDON_PROGRAM_GET_PRIVATE (self);
353 if (g_slist_find (priv->window_stack, win) == NULL)
355 priv->window_stack = g_slist_prepend (priv->window_stack, win);
359 g_critical ("%s: window already in the stack!", __FUNCTION__);
364 gboolean G_GNUC_INTERNAL
365 _hildon_program_remove_from_stack (HildonProgram *self,
366 HildonStackableWindow *win)
369 HildonProgramPrivate *priv;
371 g_return_val_if_fail (HILDON_IS_PROGRAM (self), FALSE);
372 g_return_val_if_fail (HILDON_IS_STACKABLE_WINDOW (win), FALSE);
374 priv = HILDON_PROGRAM_GET_PRIVATE (self);
377 pos = g_slist_find (priv->window_stack, win);
380 priv->window_stack = g_slist_delete_link (priv->window_stack, pos);
382 return (pos != NULL);
387 hildon_program_window_list_compare (gconstpointer window_a,
388 gconstpointer window_b)
390 g_return_val_if_fail (HILDON_IS_WINDOW(window_a) &&
391 HILDON_IS_WINDOW(window_b), 1);
393 return window_a != window_b;
397 * foreach function, checks if a window is topmost and acts consequently
400 hildon_program_window_list_is_is_topmost (gpointer data,
403 if (data && HILDON_IS_WINDOW (data))
405 HildonWindow *window = HILDON_WINDOW (data);
406 Window window_id = * (Window*)window_id_;
408 hildon_window_update_topmost (window, window_id);
413 * Check the _MB_CURRENT_APP_WINDOW on the root window, and update
414 * the top_most status accordingly
417 hildon_program_update_top_most (HildonProgram *program)
420 Window active_window;
421 HildonProgramPrivate *priv;
423 priv = HILDON_PROGRAM_GET_PRIVATE (program);
426 active_window = hildon_window_get_active_window();
432 gdk_error_trap_push ();
433 wm_hints = XGetWMHints (GDK_DISPLAY (), active_window);
434 xerror = gdk_error_trap_pop ();
441 if (wm_hints->window_group == priv->window_group)
443 if (!priv->is_topmost)
445 priv->is_topmost = TRUE;
446 g_object_notify (G_OBJECT (program), "is-topmost");
449 else if (priv->is_topmost)
451 priv->is_topmost = FALSE;
452 g_object_notify (G_OBJECT (program), "is-topmost");
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);
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.
468 static GdkFilterReturn
469 hildon_program_root_window_event_filter (GdkXEvent *xevent,
473 XAnyEvent *eventti = xevent;
474 HildonProgram *program = HILDON_PROGRAM (data);
475 Atom active_app_atom =
476 XInternAtom (GDK_DISPLAY (), "_MB_CURRENT_APP_WINDOW", False);
478 if (eventti->type == PropertyNotify)
480 XPropertyEvent *pevent = xevent;
482 if (pevent->atom == active_app_atom)
484 hildon_program_update_top_most (program);
488 return GDK_FILTER_CONTINUE;
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.
496 hildon_program_common_toolbar_topmost_window (gpointer window,
499 if (HILDON_IS_WINDOW (window) && hildon_window_get_is_topmost (HILDON_WINDOW (window)))
500 hildon_window_take_common_toolbar (HILDON_WINDOW (window));
504 * hildon_program_get_instance:
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.
511 hildon_program_get_instance (void)
513 static HildonProgram *program = NULL;
517 program = g_object_new (HILDON_TYPE_PROGRAM, NULL);
524 * hildon_program_add_window:
525 * @self: The @HildonProgram to which the window should be registered
526 * @window: A @HildonWindow to be added
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()
534 hildon_program_add_window (HildonProgram *self,
535 HildonWindow *window)
537 HildonProgramPrivate *priv;
539 g_return_if_fail (HILDON_IS_PROGRAM (self));
541 priv = HILDON_PROGRAM_GET_PRIVATE (self);
544 if (g_slist_find_custom (priv->windows, window,
545 hildon_program_window_list_compare) )
547 /* We already have that window */
551 if (!priv->window_count)
553 hildon_program_update_top_most (self);
555 /* Now that we have a window we should start keeping track of
557 gdk_window_set_events (gdk_get_default_root_window (),
558 gdk_window_get_events (gdk_get_default_root_window ()) | GDK_PROPERTY_CHANGE_MASK);
560 gdk_window_add_filter (gdk_get_default_root_window (),
561 hildon_program_root_window_event_filter, self );
564 hildon_window_set_can_hibernate_property (window, &priv->killable);
566 hildon_window_set_program (window, G_OBJECT (self));
568 priv->windows = g_slist_append (priv->windows, window);
569 priv->window_count ++;
573 * hildon_program_remove_window:
574 * @self: The #HildonProgram to which the window should be unregistered
575 * @window: The @HildonWindow to unregister
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
582 hildon_program_remove_window (HildonProgram *self,
583 HildonWindow *window)
585 HildonProgramPrivate *priv;
587 g_return_if_fail (HILDON_IS_PROGRAM (self));
589 priv = HILDON_PROGRAM_GET_PRIVATE (self);
592 hildon_window_unset_program (window);
594 priv->windows = g_slist_remove (priv->windows, window);
596 priv->window_count --;
598 if (! priv->window_count)
599 gdk_window_remove_filter (gdk_get_default_root_window(),
600 hildon_program_root_window_event_filter,
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
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
613 hildon_program_set_can_hibernate (HildonProgram *self,
614 gboolean can_hibernate)
616 HildonProgramPrivate *priv;
618 g_return_if_fail (HILDON_IS_PROGRAM (self));
620 priv = HILDON_PROGRAM_GET_PRIVATE (self);
623 if (priv->killable != can_hibernate)
625 g_slist_foreach (priv->windows,
626 (GFunc) hildon_window_set_can_hibernate_property, &can_hibernate);
629 priv->killable = can_hibernate;
633 * hildon_program_get_can_hibernate:
634 * @self: The #HildonProgram which can hibernate or not
636 * Return value: Whether or not this #HildonProgram is set to be
637 * support hibernation from the Hildon task navigator
640 hildon_program_get_can_hibernate (HildonProgram *self)
642 HildonProgramPrivate *priv;
644 g_return_val_if_fail (HILDON_IS_PROGRAM (self), FALSE);
646 priv = HILDON_PROGRAM_GET_PRIVATE (self);
649 return priv->killable;
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
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
663 hildon_program_set_common_menu (HildonProgram *self,
666 HildonProgramPrivate *priv;
668 g_return_if_fail (HILDON_IS_PROGRAM (self));
670 priv = HILDON_PROGRAM_GET_PRIVATE (self);
673 if (priv->common_menu)
675 if (GTK_WIDGET_VISIBLE (priv->common_menu))
677 gtk_menu_popdown (GTK_MENU (priv->common_menu));
678 gtk_menu_shell_deactivate (GTK_MENU_SHELL (priv->common_menu));
681 if (gtk_menu_get_attach_widget (GTK_MENU (priv->common_menu)))
683 gtk_menu_detach (GTK_MENU (priv->common_menu));
687 g_object_unref (priv->common_menu);
691 priv->common_menu = GTK_WIDGET (menu);
693 if (priv->common_menu)
696 gtk_object_sink (GTK_OBJECT (menu));
697 gtk_widget_show_all (GTK_WIDGET (menu));
702 * hildon_program_get_common_menu:
703 * @self: The #HildonProgram from which to retrieve the common menu
705 * Return value: the GtkMenu that was set as common menu for this
706 * #HildonProgram, or NULL of no common menu was set.
709 hildon_program_get_common_menu (HildonProgram *self)
711 HildonProgramPrivate *priv;
713 g_return_val_if_fail (HILDON_IS_PROGRAM (self), NULL);
715 priv = HILDON_PROGRAM_GET_PRIVATE (self);
718 return GTK_MENU (priv->common_menu);
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
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
733 hildon_program_set_common_toolbar (HildonProgram *self,
736 HildonProgramPrivate *priv;
738 g_return_if_fail (HILDON_IS_PROGRAM (self));
740 priv = HILDON_PROGRAM_GET_PRIVATE (self);
743 if (priv->common_toolbar)
745 if (priv->common_toolbar->parent)
747 gtk_container_remove (GTK_CONTAINER (priv->common_toolbar->parent),
748 priv->common_toolbar);
751 g_object_unref (priv->common_toolbar);
754 priv->common_toolbar = GTK_WIDGET (toolbar);
756 if (priv->common_toolbar)
758 g_object_ref (priv->common_toolbar);
759 gtk_object_sink (GTK_OBJECT (priv->common_toolbar) );
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)
766 g_slist_foreach (priv->windows,
767 (GFunc) hildon_program_common_toolbar_topmost_window, NULL);
772 * hildon_program_get_common_toolbar:
773 * @self: The #HildonProgram from which to retrieve the common toolbar
775 * Return value: the GtkToolbar that was set as common toolbar for this
776 * #HildonProgram, or NULL of no common menu was set.
779 hildon_program_get_common_toolbar (HildonProgram *self)
781 HildonProgramPrivate *priv;
783 g_return_val_if_fail (HILDON_IS_PROGRAM (self), NULL);
785 priv = HILDON_PROGRAM_GET_PRIVATE (self);
788 return priv->common_toolbar ? GTK_TOOLBAR (priv->common_toolbar) : NULL;
792 * hildon_program_get_is_topmost:
793 * @self: A #HildonWindow
795 * Return value: Whether or not one of the program's window or dialog is
796 * currenltly activated by the window manager.
799 hildon_program_get_is_topmost (HildonProgram *self)
801 HildonProgramPrivate *priv;
803 g_return_val_if_fail (HILDON_IS_PROGRAM (self), FALSE);
805 priv = HILDON_PROGRAM_GET_PRIVATE (self);
808 return priv->is_topmost;
812 * hildon_program_go_to_root_window:
813 * @self: A #HildonProgram
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
822 hildon_program_go_to_root_window (HildonProgram *self)
824 HildonProgramPrivate *priv;
825 GSList *windows, *iter;
826 gboolean windows_left;
828 g_return_if_fail (HILDON_IS_PROGRAM (self));
829 priv = HILDON_PROGRAM_GET_PRIVATE (self);
832 /* List of stacked windows (starting from the topmost one) */
833 windows = g_slist_copy (priv->window_stack);
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);
841 if (HILDON_IS_STACKABLE_WINDOW (iter->data))
844 HildonStackableWindow *win;
846 /* Mark the window as "going home" */
847 win = HILDON_STACKABLE_WINDOW (iter->data);
848 hildon_stackable_window_set_going_home (win, TRUE);
850 /* Set win pointer to NULL if the window is destroyed */
851 g_object_add_weak_pointer (G_OBJECT (win), (gpointer) &win);
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);
859 /* Continue sending delete events if the window has been destroyed */
863 windows_left = (iter != NULL && iter->next != NULL);
867 g_object_remove_weak_pointer (G_OBJECT (win), (gpointer) &win);
868 hildon_stackable_window_set_going_home (win, FALSE);
869 windows_left = FALSE;
874 g_critical ("Window list contains a non-stackable window");
875 windows_left = FALSE;
879 /* Show the last window that hasn't been destroyed */
880 if (iter != NULL && GTK_IS_WIDGET (iter->data))
882 gtk_widget_show (GTK_WIDGET (iter->data));
885 g_slist_free (windows);