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