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