5b1d303179c6e53cb054e132e2298bbecb911392
[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.
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 used to control a hildon program.
28  *
29  * HildonProgram is not a widget itself nor a container. HildonProgram can 
30  * contain one or more @HildonWindow. 
31  */
32
33 #ifdef                                          HAVE_CONFIG_H
34 #include                                        <config.h>
35 #endif
36
37 #include                                        "hildon-program.h"
38 #include                                        "hildon-program-private.h"
39 #include                                        "hildon-window-private.h"
40 #include                                        <X11/Xatom.h>
41
42 static void
43 hildon_program_init                             (HildonProgram *self);
44
45 static void
46 hildon_program_finalize                         (GObject *self);
47
48 static void
49 hildon_program_class_init                       (HildonProgramClass *self);
50
51 static void
52 hildon_program_get_property                     (GObject *object, 
53                                                  guint property_id,
54                                                  GValue *value, 
55                                                  GParamSpec *pspec);
56 static void
57 hildon_program_set_property                     (GObject *object, 
58                                                  guint property_id,
59                                                  const GValue *value, 
60                                                  GParamSpec *pspec);
61
62 enum
63 {
64     PROP_0,
65     PROP_IS_TOPMOST,
66     PROP_KILLABLE
67 };
68
69 GType G_GNUC_CONST
70 hildon_program_get_type                         (void)
71 {
72     static GType program_type = 0;
73
74     if (! program_type)
75     {
76         static const GTypeInfo program_info =
77         {
78             sizeof (HildonProgramClass),
79             NULL,       /* base_init */
80             NULL,       /* base_finalize */
81             (GClassInitFunc) hildon_program_class_init,
82             NULL,       /* class_finalize */
83             NULL,       /* class_data */
84             sizeof (HildonProgram),
85             0,  /* n_preallocs */
86             (GInstanceInitFunc) hildon_program_init,
87         };
88         program_type = g_type_register_static(G_TYPE_OBJECT,
89                 "HildonProgram", &program_info, 0);
90     }
91     return program_type;
92 }
93
94 static void
95 hildon_program_init                             (HildonProgram *self)
96 {
97     HildonProgramPrivate *priv = HILDON_PROGRAM_GET_PRIVATE (self);
98     g_assert (priv);
99     
100     priv->killable = FALSE;
101     priv->window_count = 0;
102     priv->is_topmost = FALSE;
103     priv->window_group = GDK_WINDOW_XID (gdk_display_get_default_group (gdk_display_get_default()));
104     priv->common_toolbar = NULL;
105     priv->name = NULL;
106 }
107
108 static void
109 hildon_program_finalize                         (GObject *self)
110 {
111     HildonProgramPrivate *priv = HILDON_PROGRAM_GET_PRIVATE (HILDON_PROGRAM (self));
112     g_assert (priv);
113     
114     if (priv->common_toolbar)
115     {
116         g_object_unref (priv->common_toolbar);
117         priv->common_toolbar = NULL;
118     }
119
120     if (priv->common_menu)
121     {
122         g_object_unref (priv->common_menu);
123         priv->common_menu = NULL;
124     }
125
126     g_free (priv->name);
127 }
128
129 static void
130 hildon_program_class_init                       (HildonProgramClass *self)
131 {
132     GObjectClass *object_class = G_OBJECT_CLASS (self);
133
134     g_type_class_add_private (self, sizeof (HildonProgramPrivate));
135
136     /* Set up object virtual functions */
137     object_class->finalize      = hildon_program_finalize;
138     object_class->set_property  = hildon_program_set_property;
139     object_class->get_property  = hildon_program_get_property;
140
141     /* Install properties */
142
143     /**
144      * HildonProgram:is-topmost:
145      *
146      * Whether one of the program's window or dialog currently
147      * is activated by window manager. 
148      */
149     g_object_class_install_property (object_class, PROP_IS_TOPMOST,
150                 g_param_spec_boolean ("is-topmost",
151                 "Is top-most",
152                 "Whether one of the program's window or dialog currently "
153                 "is activated by window manager",
154                 FALSE,
155                 G_PARAM_READABLE)); 
156
157     /**
158      * HildonProgram:can-hibernate:
159      *
160      * Whether the program should be set to hibernate by the Task
161      * Navigator in low memory situation.
162      */
163     g_object_class_install_property (object_class, PROP_KILLABLE,
164                 g_param_spec_boolean ("can-hibernate",
165                 "Can hibernate",
166                 "Whether the program should be set to hibernate by the Task "
167                 "Navigator in low memory situation",
168                 FALSE,
169                 G_PARAM_READWRITE)); 
170     return;
171 }
172
173 static void
174 hildon_program_set_property                     (GObject *object, 
175                                                  guint property_id,
176                                                  const GValue *value, 
177                                                  GParamSpec *pspec)
178 {
179     switch (property_id) {
180
181         case PROP_KILLABLE:
182             hildon_program_set_can_hibernate (HILDON_PROGRAM (object), g_value_get_boolean (value));
183             break;
184             
185         default:
186             G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
187             break;
188     }
189
190 }
191
192 static void
193 hildon_program_get_property                     (GObject *object, 
194                                                  guint property_id,
195                                                  GValue *value, 
196                                                  GParamSpec *pspec)
197 {
198     HildonProgramPrivate *priv = HILDON_PROGRAM_GET_PRIVATE (object);
199     g_assert (priv);
200
201     switch (property_id)
202     {
203         case PROP_KILLABLE:
204             g_value_set_boolean (value, priv->killable);
205             break;
206
207         case PROP_IS_TOPMOST:
208             g_value_set_boolean (value, priv->is_topmost);
209             break;
210
211         default:
212             G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
213             break;
214     }
215 }
216
217 /* Utilities */
218 static gint 
219 hildon_program_window_list_compare              (gconstpointer window_a, 
220                                                  gconstpointer window_b)
221 {
222     g_return_val_if_fail (HILDON_IS_WINDOW(window_a) && 
223                           HILDON_IS_WINDOW(window_b), 1);
224
225     return window_a != window_b;
226 }
227
228 /*
229  * foreach function, checks if a window is topmost and acts consequently
230  */
231 static void
232 hildon_program_window_list_is_is_topmost        (gpointer data, 
233                                                  gpointer window_id_)
234 {
235     if (data && HILDON_IS_WINDOW (data))
236     {
237         HildonWindow *window = HILDON_WINDOW (data);
238         Window window_id = * (Window*)window_id_;
239
240         hildon_window_update_topmost (window, window_id);
241     }
242 }
243
244 /*
245  * Check the _MB_CURRENT_APP_WINDOW on the root window, and update
246  * the top_most status accordingly
247  */
248 static void
249 hildon_program_update_top_most                  (HildonProgram *program)
250 {
251     XWMHints *wm_hints;
252     Window active_window;
253     HildonProgramPrivate *priv;
254
255     priv = HILDON_PROGRAM_GET_PRIVATE (program);
256     g_assert (priv);
257     
258     active_window = hildon_window_get_active_window();
259
260     if (active_window)
261     {
262       wm_hints = XGetWMHints (GDK_DISPLAY (), active_window);
263
264       if (wm_hints)
265       {
266
267           if (wm_hints->window_group == priv->window_group)
268           {
269               if (!priv->is_topmost)
270               {
271                   priv->is_topmost = TRUE;
272                   g_object_notify (G_OBJECT (program), "is-topmost");
273               }
274           }
275           else if (priv->is_topmost)
276           {
277             priv->is_topmost = FALSE;
278             g_object_notify (G_OBJECT (program), "is-topmost");
279           }
280       }
281       XFree (wm_hints);
282     }
283
284     /* Check each window if it was is_topmost */
285     g_slist_foreach (priv->windows, 
286             (GFunc)hildon_program_window_list_is_is_topmost, &active_window);
287 }
288
289 /*
290  * We keep track of the _MB_CURRENT_APP_WINDOW property on the root window,
291  * to detect when a window belonging to this program was is_topmost. This
292  * is based on the window group WM hint.
293  */
294 static GdkFilterReturn
295 hildon_program_root_window_event_filter         (GdkXEvent *xevent,
296                                                  GdkEvent *event,
297                                                  gpointer data)
298 {
299     XAnyEvent *eventti = xevent;
300     HildonProgram *program = HILDON_PROGRAM (data);
301     Atom active_app_atom =
302             XInternAtom (GDK_DISPLAY (), "_MB_CURRENT_APP_WINDOW", False);
303
304     if (eventti->type == PropertyNotify)
305     {
306         XPropertyEvent *pevent = xevent;
307
308         if (pevent->atom == active_app_atom)
309         {
310             hildon_program_update_top_most (program);
311         }
312     }
313
314     return GDK_FILTER_CONTINUE;
315 }
316
317 /* 
318  * Checks if the window is the topmost window of the program and in
319  * that case forces the window to take the common toolbar.
320  */
321 static void
322 hildon_program_common_toolbar_topmost_window    (gpointer window, 
323                                                  gpointer data)
324 {
325     if (HILDON_IS_WINDOW (window) && hildon_window_get_is_topmost (HILDON_WINDOW (window)))
326         hildon_window_take_common_toolbar (HILDON_WINDOW (window));
327 }
328
329 /**
330  * hildon_program_get_instance:
331  *
332  * Return value: Returns the #HildonProgram for the current process.
333  * The object is created on the first call.
334  **/
335 HildonProgram*
336 hildon_program_get_instance                     (void)
337 {
338     static HildonProgram *program = NULL;
339
340     if (! program)
341     {
342         program = g_object_new (HILDON_TYPE_PROGRAM, NULL);
343     }
344
345     return program;
346 }
347
348 /**
349  * hildon_program_add_window:
350  * @self: The @HildonProgram to which the window should be registered
351  * @window: A @HildonWindow to be added
352  *
353  * Registers a @HildonWindow as belonging to a given @HildonProgram. This
354  * allows to apply program-wide settings as all the registered windows,
355  * such as hildon_program_set_common_menu() and
356  * hildon_pogram_set_common_toolbar()
357  **/
358 void
359 hildon_program_add_window                       (HildonProgram *self, 
360                                                  HildonWindow *window)
361 {
362     HildonProgramPrivate *priv;
363     
364     g_return_if_fail (HILDON_IS_PROGRAM (self));
365     
366     priv = HILDON_PROGRAM_GET_PRIVATE (self);
367     g_assert (priv);
368
369     if (g_slist_find_custom (priv->windows, window,
370            hildon_program_window_list_compare) )
371     {
372         /* We already have that window */
373         return;
374     }
375
376     if (!priv->window_count)
377     {
378         hildon_program_update_top_most (self);
379         
380         /* Now that we have a window we should start keeping track of
381          * the root window */
382         gdk_window_set_events (gdk_get_default_root_window (),
383                 gdk_window_get_events (gdk_get_default_root_window ()) | GDK_PROPERTY_CHANGE_MASK);
384
385         gdk_window_add_filter (gdk_get_default_root_window (),
386                 hildon_program_root_window_event_filter, self );
387     }
388     
389     hildon_window_set_can_hibernate_property (window, &priv->killable);
390
391     hildon_window_set_program (window, G_OBJECT (self));
392
393     priv->windows = g_slist_append (priv->windows, window);
394     priv->window_count ++;
395 }
396
397 /**
398  * hildon_program_remove_window:
399  * @self: The #HildonProgram to which the window should be unregistered
400  * @window: The @HildonWindow to unregister
401  *
402  * Used to unregister a window from the program. Subsequent calls to
403  * hildon_program_set_common_menu() and hildon_pogram_set_common_toolbar()
404  * will not affect the window
405  **/
406 void
407 hildon_program_remove_window                    (HildonProgram *self, 
408                                                  HildonWindow *window)
409 {
410     HildonProgramPrivate *priv;
411     
412     g_return_if_fail (HILDON_IS_PROGRAM (self));
413     
414     priv = HILDON_PROGRAM_GET_PRIVATE (self);
415     g_assert (priv);
416     
417     hildon_window_unset_program (window);
418
419     priv->windows = g_slist_remove (priv->windows, window);
420
421     priv->window_count --;
422
423     if (! priv->window_count)
424         gdk_window_remove_filter (gdk_get_default_root_window(),
425                 hildon_program_root_window_event_filter,
426                 self);
427 }
428
429 /**
430  * hildon_program_set_can_hibernate:
431  * @self: The #HildonProgram which can hibernate or not
432  * @can_hibernate: whether or not the #HildonProgram can hibernate
433  *
434  * Used to set whether or not the Hildon task navigator should
435  * be able to set the program to hibernation in case of low memory
436  **/
437 void
438 hildon_program_set_can_hibernate                (HildonProgram *self, 
439                                                  gboolean can_hibernate)
440 {
441     HildonProgramPrivate *priv;
442     
443     g_return_if_fail (HILDON_IS_PROGRAM (self));
444     
445     priv = HILDON_PROGRAM_GET_PRIVATE (self);
446     g_assert (priv);
447
448     if (priv->killable != can_hibernate)
449     {
450         g_slist_foreach (priv->windows, 
451                 (GFunc) hildon_window_set_can_hibernate_property, &can_hibernate);
452     }
453
454     priv->killable = can_hibernate;
455 }
456
457 /**
458  * hildon_program_get_can_hibernate:
459  * @self: The #HildonProgram which can hibernate or not
460  * 
461  * Return value: Whether or not this #HildonProgram is set to be
462  * support hibernation from the Hildon task navigator
463  **/
464 gboolean
465 hildon_program_get_can_hibernate                (HildonProgram *self)
466 {
467     HildonProgramPrivate *priv;
468     
469     g_return_val_if_fail (HILDON_IS_PROGRAM (self), FALSE);
470    
471     priv = HILDON_PROGRAM_GET_PRIVATE (self);
472     g_assert (priv);
473
474     return priv->killable;
475 }
476
477 /**
478  * hildon_program_set_common_menu:
479  * @self: The #HildonProgram in which the common menu should be used
480  * @menu: A GtkMenu to use as common menu for the program
481  *
482  * Sets a GtkMenu that will appear in all the @HildonWindow registered
483  * to the #HildonProgram. Only one common GtkMenu can be set, further
484  * call will detach the previous common GtkMenu. A @HildonWindow
485  * can use it's own GtkMenu with @hildon_window_set_menu
486  **/
487 void
488 hildon_program_set_common_menu                  (HildonProgram *self, 
489                                                  GtkMenu *menu)
490 {
491     HildonProgramPrivate *priv;
492
493     g_return_if_fail (HILDON_IS_PROGRAM (self));
494
495     priv = HILDON_PROGRAM_GET_PRIVATE (self);
496     g_assert (priv);
497
498     if (priv->common_menu)
499     {
500         if (GTK_WIDGET_VISIBLE (priv->common_menu))
501         {
502             gtk_menu_popdown (GTK_MENU (priv->common_menu));
503             gtk_menu_shell_deactivate (GTK_MENU_SHELL (priv->common_menu));
504         }
505
506         if (gtk_menu_get_attach_widget (GTK_MENU (priv->common_menu)))
507         {
508             gtk_menu_detach (GTK_MENU (priv->common_menu));
509         }
510         else
511         {
512             g_object_unref (priv->common_menu);
513         }
514     }
515
516     priv->common_menu = GTK_WIDGET (menu);
517
518     if (priv->common_menu)
519     {
520         g_object_ref (menu);
521         gtk_object_sink (GTK_OBJECT (menu));
522         gtk_widget_show_all (GTK_WIDGET (menu));
523     }
524 }
525
526 /**
527  * hildon_program_get_common_menu:
528  * @self: The #HildonProgram from which to retrieve the common menu
529  *
530  * Return value: the GtkMenu that was set as common menu for this
531  * #HildonProgram, or NULL of no common menu was set.
532  **/
533 GtkMenu*
534 hildon_program_get_common_menu                  (HildonProgram *self)
535 {
536     HildonProgramPrivate *priv;
537
538     g_return_val_if_fail (HILDON_IS_PROGRAM (self), NULL);
539
540     priv = HILDON_PROGRAM_GET_PRIVATE (self);
541     g_assert (priv);
542
543     return GTK_MENU (priv->common_menu);
544 }
545
546 /**
547  * hildon_program_set_common_toolbar:
548  * @self: The #HildonProgram in which the common toolbar should be used
549  * @toolbar: A GtkToolbar to use as common toolbar for the program
550  *
551  * Sets a GtkToolbar that will appear in all the @HildonWindow registered
552  * to the #HildonProgram. Only one common GtkToolbar can be set, further
553  * call will detach the previous common GtkToolbar. A @HildonWindow
554  * can use its own GtkToolbar with @hildon_window_set_toolbar. Both
555  * #HildonProgram and @HildonWindow specific toolbars will be shown
556  **/
557 void
558 hildon_program_set_common_toolbar               (HildonProgram *self, 
559                                                  GtkToolbar *toolbar)
560 {
561     HildonProgramPrivate *priv;
562
563     g_return_if_fail (HILDON_IS_PROGRAM (self));
564
565     priv = HILDON_PROGRAM_GET_PRIVATE (self);
566     g_assert (priv);
567
568     if (priv->common_toolbar)
569     {
570         if (priv->common_toolbar->parent)
571         {
572             gtk_container_remove (GTK_CONTAINER (priv->common_toolbar->parent), 
573                                   priv->common_toolbar);
574         }
575         
576         g_object_unref (priv->common_toolbar);
577     }
578
579     priv->common_toolbar = GTK_WIDGET (toolbar);
580
581     if (priv->common_toolbar)
582     {
583         g_object_ref (priv->common_toolbar);
584         gtk_object_sink (GTK_OBJECT (priv->common_toolbar) );
585     }
586
587     /* if the program is the topmost we have to update the common
588        toolbar right now for the topmost window */
589     if (priv->is_topmost)
590       {
591         g_slist_foreach (priv->windows, 
592                          (GFunc) hildon_program_common_toolbar_topmost_window, NULL);
593       }
594 }
595
596 /**
597  * hildon_program_get_common_toolbar:
598  * @self: The #HildonProgram from which to retrieve the common toolbar
599  *
600  * Return value: the GtkToolbar that was set as common toolbar for this
601  * #HildonProgram, or NULL of no common menu was set.
602  **/
603 GtkToolbar*
604 hildon_program_get_common_toolbar               (HildonProgram *self)
605 {
606     HildonProgramPrivate *priv;
607
608     g_return_val_if_fail (HILDON_IS_PROGRAM (self), NULL);
609
610     priv = HILDON_PROGRAM_GET_PRIVATE (self);
611     g_assert (priv);
612
613     return priv->common_toolbar ? GTK_TOOLBAR (priv->common_toolbar) : NULL;
614 }
615
616 /**
617  * hildon_program_get_is_topmost:
618  * @self: A #HildonWindow
619  *
620  * Return value: Whether or not one of the program's window or dialog is 
621  * currenltly activated by the window manager.
622  **/
623 gboolean
624 hildon_program_get_is_topmost                   (HildonProgram *self)
625 {
626     HildonProgramPrivate *priv;
627
628     g_return_val_if_fail (HILDON_IS_PROGRAM (self), FALSE);
629     
630     priv = HILDON_PROGRAM_GET_PRIVATE (self);
631     g_assert (priv);
632
633     return priv->is_topmost;
634 }