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