d0a6faece5e290168e518c8d2241ae8e1501a23e
[hildon] / hildon-widgets / hildon-app.c
1 /*
2  * This file is part of hildon-libs
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-app.c
27  *
28  * This file implements the HildonApp widget
29  *
30  */
31
32 #include <gdk/gdk.h>
33 #include "hildon-app.h"
34 #include "hildon-app-private.h"
35 #include "hildon-appview.h"
36 #include "gtk-infoprint.h"
37
38 #include <gdk/gdkevents.h>
39 #include <gdk/gdkkeysyms.h>
40 #include <X11/Xatom.h>
41 #include <gtk/gtkmenu.h>
42 #include <gtk/gtkmain.h>
43 #include <gtk/gtkeditable.h>
44 #include <gtk/gtktextview.h>
45 #include <gtk/gtkentry.h>
46 #include <gtk/gtkscrolledwindow.h>
47 #include <gtk/gtkuimanager.h>
48 #include <gtk/gtkactiongroup.h>
49 #include <gtk/gtkdialog.h>
50
51 #include <libintl.h>
52 #include <string.h>
53
54 #include <libmb/mbutil.h>
55
56 #ifdef HAVE_CONFIG_H
57 #include <config.h>
58 #endif
59
60 #define TITLE_DELIMITER " - "
61
62 /*
63  * 'Magic' values for the titlebar menu area limits
64  */
65 #define MENUAREA_LEFT_LIMIT 80
66 #define MENUAREA_RIGHT_LIMIT MENUAREA_LEFT_LIMIT + 307
67 #define MENUAREA_TOP_LIMIT 0
68 #define MENUAREA_BOTTOM_LIMIT 39
69
70 #define KILLABLE "CANKILL"
71
72 #define _(String) dgettext(PACKAGE, String)
73
74 #define HILDON_APP_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), \
75       HILDON_TYPE_APP, HildonAppPrivate));
76
77 static GtkWindowClass *parent_class;
78 static guint app_signals[HILDON_APP_LAST_SIGNAL] = { 0 };
79
80 typedef struct _HildonAppPrivate HildonAppPrivate;
81
82 static void
83 hildon_app_switch_to_desktop (void);
84 static gboolean
85 hildon_app_key_press (GtkWidget *widget, GdkEventKey *keyevent);
86 static gboolean
87 hildon_app_key_release (GtkWidget *widget, GdkEventKey *keyevent);
88 static gboolean
89 hildon_app_key_snooper (GtkWidget *widget, GdkEventKey *keyevent, HildonApp *app);
90 static GdkFilterReturn
91 hildon_app_event_filter (GdkXEvent *xevent, GdkEvent *event, gpointer data);
92 static void
93 hildon_app_construct_title (HildonApp *self);
94 static void
95 hildon_app_finalize (GObject *obj_self);
96 static void
97 hildon_app_destroy (GtkObject *obj);
98 static void
99 hildon_app_init (HildonApp *self);
100 static void
101 hildon_app_class_init (HildonAppClass *app_class);
102 static void
103 hildon_app_real_topmost_status_acquire (HildonApp *self);
104 static void
105 hildon_app_real_topmost_status_lose (HildonApp *self);
106 static void
107 hildon_app_real_switch_to (HildonApp *self);
108 static gboolean
109 hildon_app_button (GtkWidget *widget, GdkEventButton *event);
110 static GdkWindow *
111 find_window (GdkWindow *window, gint by, gint co);
112 static void
113 hildon_app_clipboard_copy(HildonApp *self, GtkWidget *widget);
114 static void
115 hildon_app_clipboard_cut(HildonApp *self, GtkWidget *widget);
116 static void
117 hildon_app_clipboard_paste(HildonApp *self, GtkWidget *widget);
118 static gboolean hildon_app_escape_timeout(gpointer data);
119         
120 static void hildon_app_set_property(GObject * object, guint property_id,
121                                     const GValue * value, GParamSpec * pspec);
122 static void hildon_app_get_property(GObject * object, guint property_id,
123                                     GValue * value, GParamSpec * pspec);
124
125 static void hildon_app_add (GtkContainer *container, GtkWidget *child);
126 static void hildon_app_remove (GtkContainer *container, GtkWidget *child);
127 static void hildon_app_forall (GtkContainer *container, gboolean include_internals,
128                                GtkCallback callback, gpointer callback_data);
129
130 enum {
131   PROP_0,
132   PROP_SCROLL_CONTROL,
133   /* FIXME: Zoom is deprecated, should be removed */
134   PROP_ZOOM,
135   PROP_TWO_PART_TITLE,
136   PROP_APP_TITLE,
137   PROP_KILLABLE,
138   PROP_AUTOREGISTRATION,
139   PROP_APPVIEW,
140   PROP_UI_MANAGER
141 };
142
143 static gpointer find_view(HildonApp *self, unsigned long view_id);
144
145 /* FIXME: Zoom level is deprecated, should be removed */
146 /**
147  * hildon_zoom_level_get_type:
148  * @Returns : GType of #HildonZoomLevel
149  *
150  * Initialises, and returns the type of a hildon zoom level
151  */
152
153 GType
154 hildon_zoom_level_get_type (void)
155 {
156   static GType etype = 0;
157   if (etype == 0) {
158     static const GEnumValue values[] = {
159       { HILDON_ZOOM_SMALL, "HILDON_ZOOM_SMALL", "small" },
160       { HILDON_ZOOM_MEDIUM, "HILDON_ZOOM_MEDIUM", "medium" },
161       { HILDON_ZOOM_LARGE, "HILDON_ZOOM_LARGE", "large" },
162       { 0, NULL, NULL }
163     };
164     etype = g_enum_register_static ("HildonZoomLevel", values);
165   }
166   return etype;
167 }
168
169 GType hildon_app_get_type(void)
170 {
171     static GType app_type = 0;
172
173     if (!app_type)
174       {
175         static const GTypeInfo app_info =
176           {
177             sizeof(HildonAppClass),
178             NULL,       /* base_init */
179             NULL,       /* base_finalize */
180             (GClassInitFunc) hildon_app_class_init,
181             NULL,       /* class_finalize */
182             NULL,       /* class_data */
183             sizeof(HildonApp),
184             0,  /* n_preallocs */
185             (GInstanceInitFunc) hildon_app_init,
186           };
187         app_type = g_type_register_static(GTK_TYPE_WINDOW,
188                                           "HildonApp", &app_info, 0);
189       }
190     return app_type;
191 }
192
193 /*
194  * Sets or delete a custom property into the XServer, according
195  * to the boolean value of HildonAppPrivate::killable
196  */ 
197 static void hildon_app_apply_killable(HildonApp *self)
198 {
199     HildonAppPrivate *priv;
200     Atom killability_atom = XInternAtom (GDK_DISPLAY(),
201                                        "_HILDON_APP_KILLABLE", False);
202     priv = HILDON_APP_GET_PRIVATE (self);
203
204     g_assert (HILDON_IS_APP (self) );
205     g_assert(GTK_WIDGET_REALIZED(self));
206
207     if (priv->killable)
208     {
209       /* Set the atom to specific value, because perhaps in the future,
210                there may be other possible values? */
211       XChangeProperty(GDK_DISPLAY(),
212                       GDK_WINDOW_XID(GTK_WIDGET(self)->window),
213                       killability_atom, XA_STRING, 8,
214                       PropModeReplace, (unsigned char *)KILLABLE,
215                       strlen(KILLABLE));
216     }
217     else
218     {
219       XDeleteProperty(GDK_DISPLAY(),
220                       GDK_WINDOW_XID(GTK_WIDGET(self)->window),
221                       killability_atom);
222     }
223 }
224
225 /*
226  * Updates the _NET_CLIENT_LIST property into the XServer.
227  * It is the list of the views associated to the HildonApp.
228  * It will be used by the Task Navigator in order to be able to show a list
229  * of all the views, and let the user switch and navigate them.
230  */
231 static void hildon_app_apply_client_list(HildonApp *self)
232 {
233   HildonAppPrivate *priv;
234   Window *win_array;
235   GSList *list_ptr;
236   int loopctr = 0;
237   Atom clientlist;
238
239   g_assert (HILDON_IS_APP (self) );
240   g_assert(GTK_WIDGET_REALIZED(self));
241
242   /* Get the client list handle */
243   clientlist = XInternAtom (GDK_DISPLAY(),
244     "_NET_CLIENT_LIST", False);
245
246   /* Allocate a new array for window IDs */
247   priv = HILDON_APP_GET_PRIVATE(self);
248   win_array = g_new(Window, g_slist_length(priv->view_ids));
249   
250   /* Fill the contents of the window array with current view IDs */
251   for (list_ptr = priv->view_ids; list_ptr; list_ptr = list_ptr->next)
252   {
253       win_array[loopctr] = 
254         (unsigned long)(((view_item *)(list_ptr->data))->view_id);
255       loopctr++;
256   }
257
258   /* Update the details of current view IDs to our X property */
259   XChangeProperty(GDK_DISPLAY(), GDK_WINDOW_XID(GTK_WIDGET(self)->window),
260                   clientlist, XA_WINDOW, 32, PropModeReplace,
261                   (unsigned char *)win_array,
262                   g_slist_length(priv->view_ids));
263
264   XFlush(GDK_DISPLAY());
265   g_free(win_array);
266 }
267
268 /*
269  * Performs the standard gtk realize function.
270  */
271 static void hildon_app_realize(GtkWidget *widget)
272 {
273     HildonApp *self;
274     HildonAppPrivate *priv;
275     GdkWindow *window;
276     Atom *old_atoms, *new_atoms;
277     gint atom_count;
278     Display *disp;
279
280     g_assert(widget != NULL);
281
282     self = HILDON_APP(widget);
283     priv = HILDON_APP_GET_PRIVATE(self);
284
285     /*
286      * Of course we need to realize the parent.
287      * parent_class got already initialised in the hildon_app_init function
288      */
289     GTK_WIDGET_CLASS(parent_class)->realize(widget);
290
291     /* some initialisation code */
292     hildon_app_apply_killable(self); 
293     hildon_app_construct_title(self);
294     hildon_app_apply_client_list(self);
295     hildon_app_notify_view_changed(self, hildon_app_get_appview(self));
296     window = widget->window;
297     disp = GDK_WINDOW_XDISPLAY(window);
298
299     /* Install a key snooper for the Home button - so that it works everywhere */
300     priv->key_snooper = gtk_key_snooper_install 
301         ((GtkKeySnoopFunc) hildon_app_key_snooper, widget);
302
303     /* Get the list of Atoms for the WM_PROTOCOLS property... */
304     XGetWMProtocols(disp, GDK_WINDOW_XID(window), &old_atoms, &atom_count);
305     new_atoms = g_new(Atom, atom_count + 1);
306
307     memcpy(new_atoms, old_atoms, sizeof(Atom) * atom_count);
308
309     /* ... creates a new Atom... */
310     new_atoms[atom_count++] =
311         XInternAtom(disp, "_NET_WM_CONTEXT_CUSTOM", False);
312
313     /* ... and finally update the property within the XServer */
314     XSetWMProtocols(disp, GDK_WINDOW_XID(window), new_atoms, atom_count);
315
316     XFree(old_atoms);
317     g_free(new_atoms);
318
319     /* Add the GDK_SUBSTRUCTURE_MASK (receive events about window configuration
320      * changes of child windows) to the window.
321      */
322     gdk_window_set_events(window, gdk_window_get_events(window) | GDK_SUBSTRUCTURE_MASK);
323 }
324
325 /*
326  * Performs the standard gtk unrealize function.
327  */
328 static void hildon_app_unrealize(GtkWidget *widget)
329 {
330   HildonAppPrivate *priv = HILDON_APP_GET_PRIVATE(widget);
331
332   if (priv->key_snooper)
333   {
334     /* removing the snooper that handles MENU and HOME key presses */
335     gtk_key_snooper_remove(priv->key_snooper);
336     priv->key_snooper = 0;
337   }
338
339   gdk_window_remove_filter(NULL, hildon_app_event_filter, widget);
340   GTK_WIDGET_CLASS(parent_class)->unrealize(widget);
341 }
342
343 /*
344  * Class initialisation.
345  */
346 static void hildon_app_class_init (HildonAppClass *app_class)
347 {
348     /* get convenience variables */
349     GObjectClass *object_class = G_OBJECT_CLASS(app_class);
350     GtkContainerClass *container_class = GTK_CONTAINER_CLASS (app_class);
351     GtkObjectClass *gtkobject_class = GTK_OBJECT_CLASS(app_class);
352     GtkWidgetClass *widget_class = GTK_WIDGET_CLASS(app_class);
353
354     /* set the global parent_class here */
355     parent_class = g_type_class_peek_parent(app_class);
356
357     g_type_class_add_private(app_class, sizeof(HildonAppPrivate));
358
359     /* now the object stuff */
360     object_class->finalize = hildon_app_finalize;
361     object_class->set_property = hildon_app_set_property;
362     object_class->get_property = hildon_app_get_property;
363
364     gtkobject_class->destroy = hildon_app_destroy;
365
366     widget_class->key_press_event = hildon_app_key_press;
367     widget_class->key_release_event = hildon_app_key_release;
368     widget_class->button_press_event = hildon_app_button;
369     widget_class->button_release_event = hildon_app_button;
370     widget_class->realize = hildon_app_realize;
371     widget_class->unrealize = hildon_app_unrealize;
372
373     container_class->add = hildon_app_add;
374     container_class->remove = hildon_app_remove;
375     container_class->forall = hildon_app_forall;
376
377     app_class->topmost_status_acquire =
378         hildon_app_real_topmost_status_acquire;
379     app_class->topmost_status_lose = hildon_app_real_topmost_status_lose;
380     app_class->switch_to = hildon_app_real_switch_to;
381
382     /* create the signals */
383     app_signals[TOPMOST_STATUS_ACQUIRE] =
384         g_signal_new("topmost_status_acquire",
385                      G_OBJECT_CLASS_TYPE(object_class),
386                      G_SIGNAL_RUN_FIRST,
387                      G_STRUCT_OFFSET(HildonAppClass,
388                                      topmost_status_acquire), NULL, NULL,
389                      g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0);
390
391     app_signals[TOPMOST_STATUS_LOSE] =
392         g_signal_new("topmost_status_lose",
393                      G_OBJECT_CLASS_TYPE(object_class),
394                      G_SIGNAL_RUN_FIRST,
395                      G_STRUCT_OFFSET(HildonAppClass, topmost_status_lose),
396                      NULL, NULL,
397                      g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0);
398
399     app_signals[SWITCH_TO] =
400       g_signal_new("switch_to",
401                    G_OBJECT_CLASS_TYPE(object_class),
402                    G_SIGNAL_RUN_FIRST,
403                    G_STRUCT_OFFSET(HildonAppClass, switch_to),
404                    NULL, NULL,
405                    g_cclosure_marshal_VOID__POINTER, G_TYPE_NONE, 1,
406                    G_TYPE_POINTER);
407                    
408     app_signals[IM_CLOSE] =
409       g_signal_new("im_close",
410                    G_OBJECT_CLASS_TYPE(object_class),
411                    G_SIGNAL_RUN_FIRST,
412                    G_STRUCT_OFFSET(HildonAppClass, im_close),
413                    NULL, NULL,
414                    g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0);
415
416     app_signals[CLIPBOARD_COPY] =
417       g_signal_new("clipboard_copy",
418                    G_OBJECT_CLASS_TYPE(object_class),
419                    G_SIGNAL_RUN_FIRST,
420                    G_STRUCT_OFFSET(HildonAppClass, clipboard_copy),
421                    NULL, NULL,
422                    g_cclosure_marshal_VOID__POINTER, G_TYPE_NONE, 1,
423                    G_TYPE_POINTER);
424     app_signals[CLIPBOARD_CUT] =
425       g_signal_new("clipboard_cut",
426                    G_OBJECT_CLASS_TYPE(object_class),
427                    G_SIGNAL_RUN_FIRST,
428                    G_STRUCT_OFFSET(HildonAppClass, clipboard_cut),
429                    NULL, NULL,
430                    g_cclosure_marshal_VOID__POINTER, G_TYPE_NONE, 1,
431                    G_TYPE_POINTER);
432     app_signals[CLIPBOARD_PASTE] =
433       g_signal_new("clipboard_paste",
434                    G_OBJECT_CLASS_TYPE(object_class),
435                    G_SIGNAL_RUN_FIRST,
436                    G_STRUCT_OFFSET(HildonAppClass, clipboard_paste),
437                    NULL, NULL,
438                    g_cclosure_marshal_VOID__POINTER, G_TYPE_NONE, 1,
439                    G_TYPE_POINTER);
440
441     /* properties */
442     g_object_class_install_property(object_class, PROP_SCROLL_CONTROL,
443         g_param_spec_boolean("scroll-control",
444                             "Scroll control",
445                             "Set the scroll control ON/OFF",
446                              TRUE, G_PARAM_READWRITE));
447
448     g_object_class_install_property(object_class, PROP_TWO_PART_TITLE,
449                                     g_param_spec_boolean("two-part-title",
450                                                          "Two part title",
451                                                          "Use two part title or not",
452                                                          FALSE, G_PARAM_READWRITE));
453 #ifndef HILDON_DISABLE_DEPRECATED
454     g_object_class_install_property(object_class, PROP_ZOOM,
455                                     g_param_spec_enum ("zoom",
456                                                        "Zoom level",
457                                                        "Set the zoom level",
458                                                        HILDON_TYPE_ZOOM_LEVEL,
459                                                        HILDON_ZOOM_MEDIUM,
460                                                        G_PARAM_READWRITE));
461 #endif
462     g_object_class_install_property(object_class, PROP_APP_TITLE,
463                                     g_param_spec_string ("app-title",
464                                                          "Application title",
465                                                          "Set the application title",
466                                                          "",
467                                                          G_PARAM_READWRITE));
468
469     g_object_class_install_property(object_class, PROP_KILLABLE,
470                                     g_param_spec_boolean("killable",
471                                                          "Killable",
472                                                          "Whether the application is killable or not",
473                                                          FALSE,
474                                                          G_PARAM_READWRITE));
475     g_object_class_install_property(object_class, PROP_AUTOREGISTRATION,
476                                     g_param_spec_boolean("autoregistration",
477                                                          "Autoregistration",
478                                                          "Whether the application views should be registered automatically",
479                                                          TRUE,
480                                                          G_PARAM_READWRITE));
481     g_object_class_install_property(object_class, PROP_APPVIEW,
482                                     g_param_spec_object("appview",
483                                                          "Appplication View",
484                                                          "The currently active application view",
485                                                          HILDON_TYPE_APPVIEW,
486                                                          G_PARAM_READWRITE));
487     g_object_class_install_property(object_class, PROP_UI_MANAGER,
488                                     g_param_spec_object("ui-manager",
489                                                          "UIManager",
490                                                          "The associated GtkUIManager for this app",
491                                                          GTK_TYPE_UI_MANAGER,
492                                                          G_PARAM_READWRITE));
493 }
494
495 /*
496  * Performs the standard gtk finalize function, freeing allocated
497  * memory and propagating the finalization to the parent.
498  */
499 static void
500 hildon_app_finalize (GObject *obj)
501 {
502   HildonAppPrivate *priv = NULL;
503   
504   g_assert (obj != NULL);
505
506   priv = HILDON_APP_GET_PRIVATE (obj);
507
508   g_free (priv->title);
509
510   if (G_OBJECT_CLASS(parent_class)->finalize)
511     G_OBJECT_CLASS(parent_class)->finalize(obj);
512
513   /* FIXME: This is legacy code, but cannot be removed 
514      without changing functionality */
515   gtk_main_quit ();
516 }
517
518 /*
519  * Removes the long escape ("cancel" hw key) press timeout.
520  */
521 static void
522 hildon_app_remove_timeout(HildonAppPrivate *priv)
523 {
524   g_assert(priv != NULL);
525
526   if (priv->escape_timeout > 0)
527     {
528       g_source_remove (priv->escape_timeout);
529       priv->escape_timeout = 0;
530     }
531 }
532
533 /*
534  * Frees all the resources and propagates the destroy call to the parent.
535  */
536 static void
537 hildon_app_destroy (GtkObject *obj)
538 {
539   HildonAppPrivate *priv = NULL;
540
541   g_assert (obj != NULL);
542   
543   priv = HILDON_APP_GET_PRIVATE (obj);
544
545   /* Just in case a GDK_Escape key was pressed shortly before the propagation
546    * of this event, it is safer to remove the timeout that will generate a
547    * GDK_DELETE key press. We are destroying the app, so we're not interested
548    * anymore in processing key presses.
549    */
550   hildon_app_remove_timeout(priv);
551
552   if (priv->uim != NULL)
553     {
554       g_object_unref (G_OBJECT (priv->uim));
555       priv->uim = NULL;
556     }
557
558   /* Free all the views */
559   if (priv->view_ids)
560     {
561       g_slist_foreach (priv->view_ids, (GFunc)g_free, NULL);
562       g_slist_free (priv->view_ids);
563       priv->view_ids = NULL;
564     }
565
566   if (GTK_OBJECT_CLASS (parent_class)->destroy)
567     GTK_OBJECT_CLASS (parent_class)->destroy(obj);
568 }
569
570 /*
571  * Overrides gtk_container_forall, calling the callback function for each of
572  * the children of HildonAppPrivate.
573  */
574 static void hildon_app_forall (GtkContainer *container, gboolean include_internals,
575                                GtkCallback callback, gpointer callback_data)
576 {
577   HildonAppPrivate *priv = NULL;
578
579   g_return_if_fail (container != NULL);
580   g_return_if_fail (callback != NULL);
581
582   priv = HILDON_APP_GET_PRIVATE (container);
583
584   /* Note! we only have user added children, no internals */
585   g_list_foreach (priv->children, (GFunc)callback, callback_data);
586 }
587
588 /*
589  * An accessor to set private properties of HildonAppPrivate.
590  */
591 static void hildon_app_set_property(GObject * object, guint property_id,
592                                     const GValue * value, GParamSpec * pspec)
593 {
594     HildonAppPrivate *priv = HILDON_APP_GET_PRIVATE(object);
595
596     switch (property_id) {
597     case PROP_SCROLL_CONTROL:
598         priv->scroll_control = g_value_get_boolean(value);
599         break;
600 #ifndef HILDON_DISABLE_DEPRECATED
601     case PROP_ZOOM:
602         hildon_app_set_zoom( HILDON_APP (object), g_value_get_enum (value) );
603         break; 
604 #endif
605     case PROP_TWO_PART_TITLE:
606         hildon_app_set_two_part_title( HILDON_APP (object), 
607                                        g_value_get_boolean (value) );
608         break;
609      case PROP_APP_TITLE:
610         hildon_app_set_title( HILDON_APP (object), g_value_get_string (value));
611         break;
612     case PROP_KILLABLE:
613         hildon_app_set_killable( HILDON_APP (object), 
614                                g_value_get_boolean (value));
615         break;
616     case PROP_AUTOREGISTRATION:
617         hildon_app_set_autoregistration( HILDON_APP (object), 
618                                g_value_get_boolean (value));
619         break;
620     case PROP_APPVIEW:
621         hildon_app_set_appview( HILDON_APP (object), 
622                                HILDON_APPVIEW (g_value_get_object (value)));
623         break;
624     case PROP_UI_MANAGER:
625         hildon_app_set_ui_manager( HILDON_APP (object), 
626                                   GTK_UI_MANAGER (g_value_get_object (value)));
627         break;
628     default:
629         G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec);
630         break;
631     }
632 }
633
634 /*
635  * An accessor to get private properties of HildonAppPrivate.
636  */
637 static void hildon_app_get_property(GObject * object, guint property_id,
638                                     GValue * value, GParamSpec * pspec)
639 {
640     HildonAppPrivate *priv = HILDON_APP_GET_PRIVATE(object);
641
642     switch (property_id) {
643     case PROP_SCROLL_CONTROL:
644         g_value_set_boolean( value, priv->scroll_control );
645         break;
646 #ifndef HILDON_DISABLE_DEPRECATED
647     case PROP_ZOOM:
648         g_value_set_enum( value, priv->zoom);
649         break;
650 #endif
651     case PROP_TWO_PART_TITLE:
652         g_value_set_boolean( value, priv->twoparttitle);
653         break;
654     case PROP_APP_TITLE:
655         g_value_set_string (value, priv->title);
656         break;
657     case PROP_KILLABLE:
658         g_value_set_boolean (value, priv->killable);
659         break;
660     case PROP_AUTOREGISTRATION:
661         g_value_set_boolean (value, priv->autoregistration);
662         break;
663     case PROP_APPVIEW:
664         g_value_set_object (value, hildon_app_get_appview (HILDON_APP (object)));
665         break;
666     case PROP_UI_MANAGER:
667         g_value_set_object (value, hildon_app_get_ui_manager (HILDON_APP (object)));
668         break;
669     default:
670         G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec);
671         break;
672     }
673 }
674
675 /*
676  * Adds a child widget to HildonApp.
677  */
678 static void hildon_app_add (GtkContainer *container, GtkWidget *child)
679 {
680   HildonApp        *app  = NULL;
681   HildonAppPrivate *priv = NULL;
682   
683   g_return_if_fail (container != NULL);
684   g_return_if_fail (GTK_IS_WIDGET (child));
685
686   app  = HILDON_APP (container);
687   priv = HILDON_APP_GET_PRIVATE (app);
688
689   /* Check if child is already added here */
690   if (g_list_find (priv->children, child) != NULL)
691     return;
692
693   priv->children = g_list_append (priv->children, child);
694   GTK_BIN (container)->child = child;
695   gtk_widget_set_parent (child, GTK_WIDGET (app));
696
697   /* If the default direction (RTL/LTR) is different from the real
698    default, change it This happens if the locale has been changed
699    but this appview was orphaned and thus never got to know about
700    it. "default_direction" could be RTL, but the widget direction
701    of the view might still be LTR. Thats what we're fixing here. */
702
703   /* FIXME: This is legacy stuff */
704   if (gtk_widget_get_default_direction () !=
705       gtk_widget_get_direction (GTK_WIDGET (child)))
706     {
707         gtk_widget_set_direction (GTK_WIDGET (child),
708                               gtk_widget_get_default_direction ());
709     }
710
711   if (HILDON_IS_APPVIEW (child))
712     {
713       g_signal_connect_swapped (G_OBJECT (child), "title_change",
714                                 G_CALLBACK (hildon_app_construct_title), app);
715       if (priv->autoregistration)
716         hildon_app_register_view (app, child);
717     }
718 }
719
720 /*
721  * Removes a child widget from HildonApp.
722  */
723 static void hildon_app_remove (GtkContainer *container, GtkWidget *child)
724 {
725   HildonAppPrivate *priv;
726   GtkBin *bin;
727   HildonApp *app;
728
729   g_return_if_fail (container != NULL);
730   g_return_if_fail (GTK_IS_WIDGET (child));
731
732   priv = HILDON_APP_GET_PRIVATE (container);
733   bin = GTK_BIN (container);
734   app = HILDON_APP (bin);
735
736   /* Make sure that child is found in the list */
737   if (g_list_find (priv->children, child) == NULL)
738     return;
739
740   priv->children = g_list_remove (priv->children, child);
741
742   if (HILDON_IS_APPVIEW (child))
743     {
744   /* FIXME: This is a compilation workaround for gcc > 3.3, since glib-2 API is buggy.
745    * see http://bugzilla.gnome.org/show_bug.cgi?id=310175 */
746
747 G_GNUC_EXTENSION
748
749       g_signal_handlers_disconnect_by_func (G_OBJECT (child),
750                                             (gpointer)hildon_app_construct_title, app);
751
752       if (priv->autoregistration)
753         hildon_app_unregister_view (app, HILDON_APPVIEW (child));
754     }
755
756   /* If that was our visible child, we need to recalculate size.
757      We could chain up to parent as well... */
758   gtk_widget_unparent (child);
759
760   if (bin->child == child)
761     {
762       bin->child = NULL;
763       gtk_widget_queue_resize (GTK_WIDGET (bin));
764     }
765 }
766
767
768 /*
769  * Long escape keypress handler.
770  * Long press of the escape key means "close this window", so we fake a delete-event
771  * for our GdkWindow to make it act the same as if the user had closed the window the
772  * usual way. This allows any application code to gracefully exit.
773  *
774  * It returns FALSE in order to get called only once.
775  */
776 static gboolean
777 hildon_app_escape_timeout(gpointer app)
778 {
779         HildonAppPrivate *priv;
780         GdkEvent *event;
781
782   g_assert(GTK_WIDGET_REALIZED(app));
783
784         priv = HILDON_APP_GET_PRIVATE(app);
785
786   /* Send fake event, simulation a situation that user 
787      pressed 'x' from the corner */
788   event = gdk_event_new(GDK_DELETE);
789   ((GdkEventAny *)event)->window = GTK_WIDGET(app)->window;
790   gtk_main_do_event(event);
791   gdk_event_free(event);
792
793         priv->escape_timeout = 0;
794
795         return FALSE;   
796 }
797
798 /*
799  * Looks for the visible window to whom the point of coordinates (co,by)
800  * belongs. Search recursively all the children of the window.
801  *
802  * This functionality is only needed for scrollbar remote control.
803  */
804 static GdkWindow *find_window (GdkWindow *window, gint by, gint co)
805 {
806   GdkWindow *child;
807   GList *children = gdk_window_peek_children (window);
808
809         /* If the window has no children, then the coordinates must match it */
810   if (!children)
811     return window;
812
813   if (!(child = (GdkWindow *)children->data))
814     return window;
815
816   do
817     {
818       /* It makes sense to process a child window only if it's visible and it's
819        * capable to get the GDK_BUTTON_PRESS_MASK event. */
820       if (gdk_window_is_visible (child) &&
821           gdk_window_get_events (child) & GDK_BUTTON_PRESS_MASK)
822         {
823           gint x, width, y, height;
824
825           gdk_window_get_geometry (child, &x, &y, &width, &height, NULL);
826       /* This checks that the the point of coordinates (co,by) is in the rectangle
827        * made by (x,y,x+width,y+height). If so, then we spotted which child of the
828        * original window is in the point (co,by). We can now recursively search
829        * its own children.
830        */
831           if (x < co && x + width > co && y < by && y + height > by)
832             return find_window (child, by, co);
833         }
834
835       /* If the window has no more children, then it's the one we're looking for */ 
836       if (!(children = g_list_next (children)))
837         return window;
838
839     } while ( (child = children->data));
840
841   return NULL;
842 }
843
844 /*
845  * This is the callback function that gets called on a mouse button press
846  * event. If the press is happens in a "sensitive border" area in the right side of
847  * the window, the event is translated to the left edge of that border (which
848  * usually will contain a scrollbar).
849  *
850  * This functionality is used for right-hand side scroll control feature (dragging on the
851  * right edge of the window controls the rightmost scrollbar if present).
852  */
853 static gboolean
854 hildon_app_button (GtkWidget *widget, GdkEventButton *event)
855 {
856     HildonAppPrivate *priv = NULL;
857
858     /* FIXME: This is an estimate, but the AppView does not expose the
859     width of it's borders so we default to something */
860     gint sensitive_border = 31;
861
862     if (!GTK_WIDGET_REALIZED(widget))
863     {
864         return FALSE;
865     }
866     
867     priv = HILDON_APP_GET_PRIVATE (widget);
868
869     if (!priv->scroll_control)
870     {
871         return FALSE;
872     }
873     
874     /* We can easily get the location of the vertical scrollbar and get the exact
875     * area for the scroll_control *if* the setup is such that the HildonAppview
876     * contains a GtkScrolledWindow, so we check for it before defaulting to
877     * the previous guess. More complex situations are not feasible to autodetect.
878     * Applications should provide the GtkAdjustment to be changed for this to work
879     * flawlessly.
880     */
881     if (HILDON_IS_APPVIEW(GTK_BIN(widget)->child))
882     {
883         GtkBin *avbin = GTK_BIN(GTK_BIN(widget)->child);
884         if (GTK_IS_SCROLLED_WINDOW(avbin->child))
885         {
886             GtkScrolledWindow *win;
887             win = GTK_SCROLLED_WINDOW(avbin->child);
888             
889             if (GTK_WIDGET_VISIBLE(win->vscrollbar))
890             {
891                 /* Calculate the distance between the AppView's right border and
892                  * the scrollbars center
893                  */
894                 sensitive_border = (GTK_WIDGET(avbin)->allocation.x +
895                                                 GTK_WIDGET(avbin)->allocation.width) -
896                                                 (win->vscrollbar->allocation.x +
897                                                  win->vscrollbar->allocation.width / 2);
898             }
899         }
900     }
901
902     /* If the press event comes to the sensitive area, we send a fake event to the
903      * area we think the scrollbar is in to make it think the button press happened on it
904      */
905     if (event->x > widget->allocation.width - sensitive_border)
906     {
907         GdkWindow *window = NULL;
908         gint co = widget->allocation.width - sensitive_border;
909
910         /* We now need to know in which window the _modified_ coordinates are */
911         if ((window = find_window (widget->window, event->y, co)))
912         {
913             GdkEventButton nevent;
914
915             if (window == widget->window)
916                 return FALSE;
917
918             /* Build a new event and associate the proper window to it */
919             nevent = *event;
920             nevent.x = 8;
921             nevent.window = window;
922             g_object_ref (nevent.window);
923             gtk_main_do_event ((GdkEvent*)&nevent);
924         }
925     }
926     return FALSE;
927 }
928
929 /*
930  * Performs the initialisation of the widget.
931  */
932 static void
933 hildon_app_init (HildonApp *self)
934 {
935     HildonAppPrivate *priv;
936
937     priv = HILDON_APP_GET_PRIVATE(self);
938
939     /* init private */
940     priv->title = g_strdup("");
941 #ifndef HILDON_DISABLE_DEPRECATED
942     priv->zoom = HILDON_ZOOM_MEDIUM;
943 #endif
944     priv->twoparttitle = FALSE;
945     priv->lastmenuclick = 0;
946     priv->is_topmost = FALSE;
947     priv->curr_view_id = 0;
948     priv->view_id_counter = 1;
949     priv->view_ids = NULL;
950     priv->killable = FALSE;
951     priv->autoregistration = TRUE;
952     priv->scroll_control = TRUE;
953     priv->uim = NULL;
954     priv->active_menu_id = 0;
955
956     /* grab the events here since HildonApp isn't necessarily ever shown */
957     gdk_window_set_events(gdk_get_default_root_window(),
958                           gdk_window_get_events(gdk_get_default_root_window()) |
959                           GDK_PROPERTY_CHANGE_MASK);
960
961     /* For some reason, the titlebar menu has problems with the grab
962        (bugzilla bug 1527). This is part of somewhat ugly fix for it to
963        get it to work until a more satisfactory solution is found */
964     gdk_window_add_filter(NULL, hildon_app_event_filter, self);
965
966     gtk_widget_set_events (GTK_WIDGET(self), GDK_BUTTON_PRESS_MASK |
967                            GDK_BUTTON_RELEASE_MASK |
968                            GDK_POINTER_MOTION_MASK);
969 }
970
971 /*public functions */
972
973 /**
974  * hildon_app_new:
975  *
976  * Creates a new #HildonApp
977  *
978  * Return value: Pointer to a new @HildonApp structure.
979  **/
980 GtkWidget *
981 hildon_app_new (void)
982 {
983     return GTK_WIDGET(g_object_new(HILDON_TYPE_APP, NULL));
984 }
985
986 /**
987  * hildon_app_new_with_appview:
988  * @appview : A @HildonAppView
989  * 
990  * Creates an app, and sets it's initial appview.
991  * 
992  * Return value: Pointer to a new @HildonApp structure.
993  **/
994 GtkWidget *
995 hildon_app_new_with_appview (HildonAppView *appview)
996 {
997     GtkWidget *app;
998
999     g_return_val_if_fail (HILDON_IS_APPVIEW (appview), NULL);
1000
1001     app = hildon_app_new ();
1002
1003     hildon_app_set_appview(HILDON_APP(app), appview);
1004     
1005     return app;
1006 }
1007
1008 /**
1009  * hildon_app_get_appview:
1010  * @self : A @HildonApp
1011  *
1012  * Gets the currently shown appview.
1013  * 
1014  * Return value: The currently shown appview in this HildonApp.
1015  *               If no appview is currently set for this HildonApp,
1016  *               returns NULL.
1017  **/
1018 HildonAppView *
1019 hildon_app_get_appview (HildonApp *self)
1020 {
1021   GtkBin *bin;
1022
1023   g_return_val_if_fail (HILDON_IS_APP (self), NULL);
1024   bin = GTK_BIN (self);
1025   if (HILDON_IS_APPVIEW (bin->child))
1026     {
1027       return HILDON_APPVIEW (bin->child);
1028     }
1029   
1030   return NULL;
1031 }
1032
1033 /**
1034  * hildon_app_set_appview:
1035  * @self : A @HildonApp
1036  * @appview : A @HildonAppView.
1037  * 
1038  * Sets (switches to) appview.
1039  */
1040 void
1041 hildon_app_set_appview (HildonApp *app, HildonAppView *view)
1042 {
1043   HildonAppPrivate *priv;
1044   GtkBin *bin;
1045   GtkWidget *widget; /*(view to be set)*/
1046   gchar *menu_ui;
1047
1048   g_return_if_fail (HILDON_IS_APP (app));
1049   g_return_if_fail (HILDON_IS_APPVIEW (view));
1050
1051   bin = GTK_BIN (app);
1052   priv = HILDON_APP_GET_PRIVATE (app);
1053   widget = GTK_WIDGET (view);
1054
1055   if (widget == bin->child)
1056     return;
1057
1058   /* Make old appview dissapear */
1059   if (bin->child)
1060     {
1061       gtk_widget_hide (bin->child);
1062       g_signal_emit_by_name (bin->child, "switched_from", NULL);
1063       
1064       if (priv->active_menu_id > 0)
1065       {
1066         if (priv->uim != NULL)
1067           {
1068             gtk_ui_manager_remove_ui (priv->uim,
1069                                       priv->active_menu_id);
1070           }
1071         priv->active_menu_id = 0;
1072       }
1073       
1074       bin->child = NULL;
1075     }
1076
1077   /* Ensure that new view is in our child list */
1078   if (!g_list_find (priv->children, widget))
1079     gtk_container_add (GTK_CONTAINER (app), widget);
1080
1081   bin->child = widget;
1082
1083   gtk_widget_show (widget);
1084
1085   /* UI manager support, merge menu for activated view */
1086   g_object_get (G_OBJECT (view),
1087                 "menu-ui", &menu_ui,
1088                 NULL);
1089
1090   if (menu_ui && priv->uim)
1091     {
1092      
1093       priv->active_menu_id =
1094         gtk_ui_manager_add_ui_from_string (priv->uim, menu_ui, -1, NULL);
1095       
1096       gtk_ui_manager_ensure_update (priv->uim);
1097
1098     }
1099
1100   g_free (menu_ui);
1101
1102   g_signal_emit_by_name (widget, "switched_to", NULL);
1103
1104   /* Inform task navigator about changed view */
1105   hildon_app_notify_view_changed (app, view);
1106
1107   /* Update title to show currently activated view */
1108   hildon_app_construct_title (app);
1109   gtk_widget_child_focus (widget, GTK_DIR_TAB_FORWARD);
1110 }
1111
1112 /**
1113  * hildon_app_set_title:
1114  * @self : A @HildonApp
1115  * @newtitle : The new title assigned to the application.
1116  *
1117  * Sets title of the application.
1118  **/
1119 void
1120 hildon_app_set_title (HildonApp *self, const gchar *newtitle)
1121 {
1122     HildonAppPrivate *priv;
1123     gchar *oldstr;
1124
1125     g_return_if_fail(HILDON_IS_APP(self));
1126
1127     priv = HILDON_APP_GET_PRIVATE(self);
1128     oldstr = priv->title;
1129
1130     if (newtitle)
1131       {
1132         priv->title = g_strdup(newtitle);
1133         g_strstrip(priv->title);
1134       }
1135     else
1136         priv->title = g_strdup("");
1137
1138     if (oldstr)
1139         g_free(oldstr);
1140
1141     hildon_app_construct_title(self);
1142 }
1143
1144 /**
1145  * hildon_app_get_title:
1146  * @self : A @HildonApp
1147  *
1148  * Gets the title of the application.
1149  *
1150  * Return value: The title currently assigned to the application. This
1151  *  value is not to be freed or modified by the calling application.
1152  **/
1153 const gchar *
1154 hildon_app_get_title (HildonApp *self)
1155 {
1156     HildonAppPrivate *priv;
1157
1158     g_return_val_if_fail (HILDON_IS_APP(self), NULL);
1159     priv = HILDON_APP_GET_PRIVATE(self);
1160     return priv->title;
1161 }
1162
1163 /* FIXME: Zoom is deprecated, remove */
1164 /**
1165  * hildon_app_set_zoom:
1166  * @self : A @HildonApp
1167  * @newzoom: The zoom level of type @HildonZoomLevel to be assigned to an
1168  *  application.
1169  *
1170  * Sets the zoom level. Warning! This function is deprecated and
1171  * should not be used. It's lecacy stuff from ancient specs.
1172  **/
1173 void
1174 hildon_app_set_zoom (HildonApp *self, HildonZoomLevel newzoom)
1175 {
1176     HildonAppPrivate *priv;
1177
1178     g_return_if_fail(HILDON_IS_APP(self));
1179
1180     priv = HILDON_APP_GET_PRIVATE(self);
1181
1182     if (newzoom != priv->zoom)
1183       {
1184         if (newzoom < HILDON_ZOOM_SMALL)
1185           {
1186             newzoom = HILDON_ZOOM_SMALL;
1187             gtk_infoprint(GTK_WINDOW(self), _("ckct_ib_min_zoom_level_reached"));
1188           }
1189         else if (newzoom > HILDON_ZOOM_LARGE) {
1190             newzoom = HILDON_ZOOM_LARGE;
1191             gtk_infoprint(GTK_WINDOW(self), _("ckct_ib_max_zoom_level_reached"));
1192           }
1193         priv->zoom = newzoom;
1194       }
1195 }
1196
1197 /**
1198  * hildon_app_get_zoom:
1199  * @self : A @HildonApp
1200  *
1201  * Gets the zoom level. Warning! This function is deprecated and
1202  * should not be used. It's lecacy stuff from ancient specifications.
1203  *
1204  * Return value: Returns the zoom level of the Hildon application. The
1205  *  returned zoom level is of type @HildonZoomLevel.
1206  **/
1207 HildonZoomLevel
1208 hildon_app_get_zoom (HildonApp *self)
1209 {
1210     HildonAppPrivate *priv;
1211
1212     g_return_val_if_fail(HILDON_IS_APP(self), HILDON_ZOOM_MEDIUM);
1213     priv = HILDON_APP_GET_PRIVATE(self);
1214     return priv->zoom;
1215 }
1216
1217 /**
1218  * hildon_app_get_default_font:
1219  * @self : A @HildonApp
1220  *
1221  * Gets default font. Warning! This function is deprecated and should
1222  * not be used. It's legacy stuff from ancient version of specification.
1223  *
1224  * Return value: Pointer to PangoFontDescription for the default,
1225  *  normal size font.
1226  **/
1227 PangoFontDescription *
1228 hildon_app_get_default_font (HildonApp *self)
1229 {
1230     PangoFontDescription *font_desc = NULL;
1231     GtkStyle *fontstyle = NULL;
1232
1233     g_return_val_if_fail(HILDON_IS_APP(self), NULL);
1234
1235     fontstyle =
1236         gtk_rc_get_style_by_paths (gtk_widget_get_settings
1237                                   (GTK_WIDGET(self)), NULL, NULL,
1238                                    gtk_widget_get_type());
1239
1240     if (!fontstyle)
1241       {
1242         g_print("WARNING : default font not found. "
1243                 "Defaulting to swissa 19\n");
1244         font_desc = pango_font_description_from_string("swissa 19");
1245
1246       }
1247     else
1248         font_desc = pango_font_description_copy(fontstyle->font_desc);
1249
1250     return font_desc;
1251 }
1252
1253 /**
1254  * hildon_app_get_zoom_font:
1255  * @self : A @HildonApp
1256  *
1257  * Gets the description of the default font. Warning! This function
1258  * is deprecated and should not be used. It's legacy stuff from
1259  * ancient specs.
1260  * 
1261  * Return value: Pointer to PangoFontDescription for the default,
1262  *  normal size font.
1263  **/
1264 PangoFontDescription *
1265 hildon_app_get_zoom_font (HildonApp *self)
1266 {
1267     HildonAppPrivate *priv;
1268     PangoFontDescription *font_desc = NULL;
1269     gchar *style_name = 0;
1270     GtkStyle *fontstyle = NULL;
1271
1272     g_return_val_if_fail(HILDON_IS_APP(self), NULL);
1273
1274     priv = HILDON_APP_GET_PRIVATE(self);
1275     if (priv->zoom == HILDON_ZOOM_SMALL)
1276         style_name = g_strdup("hildon-zoom-small");
1277     else if (priv->zoom == HILDON_ZOOM_MEDIUM)
1278         style_name = g_strdup("hildon-zoom-medium");
1279     else if (priv->zoom == HILDON_ZOOM_LARGE)
1280         style_name = g_strdup("hildon-zoom-large");
1281     else
1282       {
1283         g_warning("Invalid Zoom Value\n");
1284         style_name = g_strdup("");
1285       }
1286
1287     fontstyle =
1288         gtk_rc_get_style_by_paths (gtk_widget_get_settings
1289                                   (GTK_WIDGET(self)), style_name, NULL,
1290                                    G_TYPE_NONE);
1291     g_free (style_name);
1292
1293     if (!fontstyle)
1294       {
1295         g_print("WARNING : theme specific zoomed font not found. "
1296                 "Defaulting to preset zoom-specific fonts\n");
1297         if (priv->zoom == HILDON_ZOOM_SMALL)
1298             font_desc = pango_font_description_from_string("swissa 16");
1299         else if (priv->zoom == HILDON_ZOOM_MEDIUM)
1300             font_desc = pango_font_description_from_string("swissa 19");
1301         else if (priv->zoom == HILDON_ZOOM_LARGE)
1302             font_desc = pango_font_description_from_string("swissa 23");
1303       }
1304     else
1305         font_desc = pango_font_description_copy(fontstyle->font_desc);
1306
1307     return font_desc;
1308 }
1309
1310  /*  FIXME: Zoom is deprecated, remove the code above */
1311
1312 /**
1313  * hildon_app_set_two_part_title:
1314  * @self : A @HildonApp
1315  * @istwoparttitle : A gboolean indicating wheter to activate
1316  *  a title that has both the application title and application
1317  *  view title separated by a triangle.
1318  * 
1319  * Sets the two part title.
1320  */
1321 void
1322 hildon_app_set_two_part_title (HildonApp *self, gboolean istwoparttitle)
1323 {
1324     HildonAppPrivate *priv;
1325     g_return_if_fail(HILDON_IS_APP(self));
1326     priv = HILDON_APP_GET_PRIVATE(self);
1327
1328     if (istwoparttitle != priv->twoparttitle)
1329       {
1330         priv->twoparttitle = istwoparttitle;
1331         hildon_app_construct_title(self);
1332       }
1333 }
1334
1335 /**
1336  * hildon_app_get_two_part_title:
1337  * @self : A @HildonApp
1338  *
1339  * Gets the 'twopart' represention of the title inside #HildonApp.
1340  * 
1341  * Return value: A boolean indicating wheter title shown has both
1342  *  application, and application view title separated by a triangle.
1343  **/
1344 gboolean
1345 hildon_app_get_two_part_title (HildonApp *self)
1346 {
1347     HildonAppPrivate *priv;
1348
1349     g_return_val_if_fail(HILDON_IS_APP(self), FALSE);
1350     priv = HILDON_APP_GET_PRIVATE(self);
1351     return priv->twoparttitle;
1352 }
1353
1354
1355 /* private functions */
1356
1357
1358 /*
1359  * Generates and sends an event of type _NET_SHOWING_DESKTOP to the XServer,
1360  * which will be then caught by the window Manager.
1361  */
1362 static void
1363 hildon_app_switch_to_desktop (void)
1364 {
1365
1366     XEvent ev;
1367     Atom showing_desktop = XInternAtom(GDK_DISPLAY(),
1368                                   "_NET_SHOWING_DESKTOP", False);
1369     memset(&ev, 0, sizeof(ev));
1370     ev.xclient.type = ClientMessage;
1371     gdk_error_trap_push();
1372     ev.xclient.window = GDK_ROOT_WINDOW();
1373     ev.xclient.message_type = showing_desktop;
1374     ev.xclient.format = 32;
1375     ev.xclient.data.l[0] = 1;
1376     gdk_error_trap_push();
1377     XSendEvent(GDK_DISPLAY(), GDK_ROOT_WINDOW(), False,
1378                SubstructureRedirectMask, &ev);
1379     gdk_error_trap_pop();
1380 }
1381
1382 /*
1383  * Handles the key press of the Escape, Increase and Decrease keys. Other keys
1384  * are handled by the parent GtkWidgetClass.
1385  */
1386 static gboolean
1387 hildon_app_key_press (GtkWidget *widget, GdkEventKey *keyevent)
1388 {
1389   HildonApp *app = HILDON_APP (widget);
1390   HildonAppView *appview;
1391   HildonAppPrivate *priv = HILDON_APP_GET_PRIVATE(app);
1392
1393   if (HILDON_IS_APPVIEW(GTK_BIN (app)->child))
1394     {
1395       appview = HILDON_APPVIEW (GTK_BIN (app)->child);
1396     }
1397   else
1398     {
1399       return FALSE;
1400     }
1401
1402     if (keyevent->keyval == GDK_Escape && priv->escape_timeout == 0)
1403       {
1404         /* Call hildon_app_escape_timeout every 1500ms until it returns FALSE 
1405          * and store the relative GSource id. Since hildon_app_escape_timeout
1406          * can only return FALSE, the call will occurr only once.
1407          */
1408         priv->escape_timeout = g_timeout_add(1500, hildon_app_escape_timeout, app);
1409       }
1410
1411     /* FIXME: Handling +/- keys here is not usefull. Applications
1412               can equally easily handle the keypress themselves. */
1413     else if (HILDON_KEYEVENT_IS_INCREASE_KEY (keyevent))
1414       {
1415         _hildon_appview_increase_button_state_changed (appview,
1416                                                        keyevent->type);
1417       }
1418     else if (HILDON_KEYEVENT_IS_DECREASE_KEY (keyevent))
1419       {
1420         _hildon_appview_decrease_button_state_changed (appview,
1421                                                        keyevent->type);
1422       }
1423
1424     /* Activate default bindings and let the widget with focus to handle key */
1425     return GTK_WIDGET_CLASS (parent_class)->key_press_event (widget, keyevent);
1426 }
1427
1428 /*
1429  * Handles the key release event for the Escape, Toolbar and Fullscreen keys.
1430  */
1431 static gboolean
1432 hildon_app_key_release (GtkWidget *widget, GdkEventKey *keyevent)
1433 {
1434   HildonApp *app = HILDON_APP (widget);
1435   HildonAppView *appview;
1436   HildonAppPrivate *priv = HILDON_APP_GET_PRIVATE(app);
1437
1438   if (HILDON_IS_APPVIEW(GTK_BIN (app)->child))
1439     {
1440       appview = HILDON_APPVIEW (GTK_BIN (app)->child);
1441     }
1442   else
1443     {
1444       return FALSE;
1445     }
1446
1447     if (keyevent->keyval == GDK_Escape)
1448       {
1449         /*
1450          * This will prevent the hildon_app_escape_timeout from being called.
1451          * See hildon_app_escape_timeout and hildon_app_remove_timeout for more.
1452          */
1453         hildon_app_remove_timeout(priv);
1454       }
1455     else if (HILDON_KEYEVENT_IS_TOOLBAR_KEY (keyevent))
1456       {
1457         g_signal_emit_by_name(G_OBJECT(appview),
1458                               "toolbar-toggle-request");
1459       }
1460      else if (HILDON_KEYEVENT_IS_FULLSCREEN_KEY (keyevent))
1461        {
1462          /* Emit the fullscreen_state_change directly, it'll save one step */
1463          if (hildon_appview_get_fullscreen_key_allowed (appview))
1464            {
1465               gboolean fullscreen;
1466               
1467               fullscreen = hildon_appview_get_fullscreen(appview);
1468               g_signal_emit_by_name(G_OBJECT(appview),
1469                           "fullscreen_state_change",
1470                           fullscreen);
1471            }
1472        }
1473
1474     /* FIXME: Should the event be marked as handled if any of the three
1475               above cases took an action */
1476
1477     /* Activate default bindings and let the widget with focus to handle key */
1478     return GTK_WIDGET_CLASS (parent_class)->key_release_event (widget, keyevent);
1479 }
1480
1481 /*
1482  * Handles the MENU and HOME key presses.
1483  */
1484 static gboolean
1485 hildon_app_key_snooper (GtkWidget *widget, GdkEventKey *keyevent, HildonApp *app)
1486 {
1487     /* FIXME: Using normal keypress handler would be better choise. All
1488               keyevents come to window anyway, so we would get the same
1489               keys in that way as well, but we wouldn't need to struggle
1490               with grabs (modal dialogs etc). */
1491
1492     /* Menu key handling is done here */
1493     if ( HILDON_KEYEVENT_IS_MENU_KEY (keyevent) ) {
1494             HildonAppView *appview;
1495             HildonAppPrivate *priv;
1496                   GtkWidget *toplevel;
1497                   
1498       /* Don't act on modal dialogs */
1499       toplevel = gtk_widget_get_toplevel (widget);
1500       if (GTK_IS_DIALOG (toplevel)
1501           && gtk_window_get_modal (GTK_WINDOW (toplevel)))
1502         {
1503           return TRUE;
1504         }
1505                     
1506             appview = HILDON_APPVIEW (GTK_BIN(app)->child);
1507             priv = HILDON_APP_GET_PRIVATE(app);
1508
1509             if ( keyevent->type == GDK_KEY_PRESS ) {
1510             /* Toggle menu on press, avoid key repeat */
1511                     if ( priv->lastmenuclick == 0 ){
1512                             _hildon_appview_toggle_menu(appview,
1513                                             gtk_get_current_event_time());
1514                             
1515                             priv->lastmenuclick = 1;
1516                     }
1517             } else if ( keyevent->type == GDK_KEY_RELEASE ) {
1518             /* We got release, so next press is really a new press, 
1519                not a repeat */
1520                     if ( priv->lastmenuclick == 1 ) {
1521                             priv->lastmenuclick = 0;
1522                     }
1523                     
1524             } else {
1525                     /* Unknown key event */
1526                     return FALSE;
1527             }
1528
1529             /* don't stop the key event so that it reaches GTK where it
1530                closes all existing menus that might be open */
1531             return FALSE;
1532     }
1533     
1534     /* Home key handling is done here. */
1535     if ((keyevent->type == GDK_KEY_RELEASE) &&
1536         HILDON_KEYEVENT_IS_HOME_KEY (keyevent))
1537       {
1538         hildon_app_switch_to_desktop();
1539
1540         return TRUE;
1541       }
1542
1543     return FALSE;
1544 }
1545
1546 /*
1547  * Returns the message_type of the Atom registered with a certain name.
1548  */
1549 static int
1550 xclient_message_type_check(XClientMessageEvent *cm, const gchar *name)
1551 {
1552   return cm->message_type == XInternAtom(GDK_DISPLAY(), name, FALSE);
1553 }
1554
1555 /*
1556  * Returns the GtkWidget associated to a certain Window.
1557  */
1558 static GtkWidget *
1559 hildon_app_xwindow_lookup_widget(Window xwindow)
1560 {
1561   GdkWindow *window;
1562   gpointer widget;
1563
1564   window = gdk_xid_table_lookup(xwindow);
1565   if (window == NULL)
1566     return NULL;
1567
1568   gdk_window_get_user_data(window, &widget);
1569   return widget;
1570 }
1571
1572 /*
1573  * Let's search a actual main window using tranciency hints. 
1574  * Note that there can be several levels of menus/dialogs above
1575  * the actual main window.
1576  */
1577 static Window get_active_main_window(Window window)
1578 {
1579   Window parent_window;
1580   gint limit = 0;
1581
1582   gdk_error_trap_push ();
1583
1584   while (XGetTransientForHint(GDK_DISPLAY(), window, &parent_window))
1585   {
1586         /* The limit > TRANSIENCY_MAXITER ensures that we can't be stuck
1587            here forever if we have circular transiencies for some reason.
1588            Use of _MB_CURRENT_APP_WINDOW might be more elegant... */
1589
1590     if (!parent_window || parent_window == GDK_ROOT_WINDOW() ||
1591         parent_window == window || limit > TRANSIENCY_MAXITER)
1592       {
1593         break;
1594       }
1595
1596     limit++;
1597     window = parent_window;
1598   }
1599   
1600   gdk_flush ();
1601
1602   if (gdk_error_trap_pop ())
1603     return 0;
1604
1605   return window;
1606 }
1607
1608 /*
1609  * Filters every GDK event first.
1610  */
1611 static GdkFilterReturn
1612 hildon_app_event_filter (GdkXEvent *xevent, GdkEvent *event, gpointer data)
1613 {
1614     gint x,y;
1615     HildonApp *app = data;
1616     HildonAppPrivate *priv;
1617     HildonAppView *appview = NULL;
1618
1619     XAnyEvent *eventti = xevent;
1620
1621     if (HILDON_IS_APPVIEW (GTK_BIN (app)->child))
1622       {
1623         appview = HILDON_APPVIEW (GTK_BIN (app)->child);
1624       }
1625
1626     g_return_val_if_fail (app, GDK_FILTER_CONTINUE);
1627     g_return_val_if_fail (HILDON_IS_APP(app), GDK_FILTER_CONTINUE);
1628
1629     priv = HILDON_APP_GET_PRIVATE(app);
1630     if (eventti->type == ClientMessage)
1631       {
1632         XClientMessageEvent *cm = xevent;
1633
1634         /* Check if a message indicating a click on titlebar has been
1635            received. Don't open it if mouse is grabbed (eg. modal dialog
1636            was just opened).
1637            _MB_GRAB_TRANSFER is emitted by MatchBox, and signals that a button
1638            has just been released. */
1639         if (xclient_message_type_check(cm, "_MB_GRAB_TRANSFER") &&
1640             HILDON_IS_APPVIEW(appview) &&
1641             gtk_grab_get_current() == NULL &&
1642             !_hildon_appview_menu_visible(appview))
1643         {
1644           _hildon_appview_toggle_menu(appview, cm->data.l[0]);
1645           return GDK_FILTER_REMOVE;
1646         }
1647         /* IM_CLOSE is input method specific hack that is really questionable */
1648         else if (xclient_message_type_check(cm, "_HILDON_IM_CLOSE"))
1649         {
1650           g_signal_emit_by_name(app, "im_close", NULL);
1651           return GDK_FILTER_REMOVE;
1652         }
1653         /* Task user changed the view through task navigator? */
1654         else if (xclient_message_type_check(cm, "_NET_ACTIVE_WINDOW"))
1655           {
1656             unsigned long view_id = cm->window;
1657             gpointer view_ptr = find_view(app, view_id);
1658                                                 
1659             /* When getting a _NET_ACTIVE_WINDOW signal from the WM we need
1660              * to bring the application to the front */
1661             if (!priv->is_topmost)
1662               g_signal_emit_by_name (G_OBJECT(app), "topmost_status_acquire");
1663
1664             if (HILDON_IS_APPVIEW(view_ptr))
1665               /* Sets the current view to the "window" that the _NET_ACTIVE_WINDOW
1666                * specified */
1667               hildon_app_set_appview(app, (HILDON_APPVIEW(view_ptr)));
1668             else
1669               /* there was no view, so we have to switch to an actual application */
1670               g_signal_emit_by_name (G_OBJECT(app), "switch_to", view_ptr);
1671
1672             /* FIXME: This is a hack. This was once just gtk_window_present, but
1673                       was changed into this at some day!! */
1674             if (GTK_WIDGET(app)->window)
1675             {
1676               mb_util_window_activate(GDK_DISPLAY(),
1677                                       GDK_WINDOW_XID(GTK_WIDGET(app)->window));
1678             }
1679           }
1680         /* FIXME: IM hack */
1681         else if (xclient_message_type_check(cm, "_HILDON_IM_CLIPBOARD_COPY"))
1682         {
1683           Window xwindow = cm->data.l[0];
1684           GtkWidget *widget = hildon_app_xwindow_lookup_widget(xwindow);
1685
1686           g_signal_emit_by_name (G_OBJECT(app), "clipboard_copy", widget);
1687         }
1688         /* FIXME: IM hack */
1689         else if (xclient_message_type_check(cm, "_HILDON_IM_CLIPBOARD_CUT"))
1690         {
1691           Window xwindow = cm->data.l[0];
1692           GtkWidget *widget = hildon_app_xwindow_lookup_widget(xwindow);
1693
1694           g_signal_emit_by_name (G_OBJECT(app), "clipboard_cut", widget);
1695         }
1696         /* FIXME: IM hack */
1697         else if (xclient_message_type_check(cm, "_HILDON_IM_CLIPBOARD_PASTE"))
1698         {
1699           Window xwindow = cm->data.l[0];
1700           GtkWidget *widget = hildon_app_xwindow_lookup_widget(xwindow);
1701
1702           g_signal_emit_by_name (G_OBJECT(app), "clipboard_paste", widget);
1703         }
1704       }
1705     
1706      if (eventti->type == ButtonPress)
1707        {
1708
1709      /* FIXME: This is mysterious bugfix related to problems to open the
1710         application menu (bugzilla N#3204) */
1711          XButtonEvent *bev = (XButtonEvent *)xevent;
1712
1713          if (HILDON_IS_APPVIEW(appview) &&
1714              _hildon_appview_menu_visible(appview) &&
1715              !hildon_appview_get_fullscreen(appview))
1716           {
1717             x = bev->x_root;
1718             y = bev->y_root;
1719             if ( (x >= MENUAREA_LEFT_LIMIT) && (x <= MENUAREA_RIGHT_LIMIT) &&
1720                  (y >= MENUAREA_TOP_LIMIT) && (y <= MENUAREA_BOTTOM_LIMIT))
1721               {
1722                 _hildon_appview_toggle_menu(appview, bev->time);
1723                 return GDK_FILTER_CONTINUE;
1724               }
1725           }
1726        }
1727     
1728     /* FIXME: as above */
1729     if (eventti->type == ButtonRelease)
1730       {
1731         if (HILDON_IS_APPVIEW(appview) &&
1732             _hildon_appview_menu_visible(appview) &&
1733             !hildon_appview_get_fullscreen(appview))
1734           {
1735             XButtonEvent *bev = (XButtonEvent *)xevent;
1736             x = bev->x_root;
1737             y = bev->y_root;
1738             if ( (x >= MENUAREA_LEFT_LIMIT) && (x < MENUAREA_RIGHT_LIMIT) &&
1739                  (y >= MENUAREA_TOP_LIMIT) && (y <= MENUAREA_BOTTOM_LIMIT))
1740               {
1741                 return GDK_FILTER_REMOVE;
1742               }
1743           }
1744         return GDK_FILTER_CONTINUE;
1745       }
1746
1747     /* Application stacking order changed */
1748     if (eventti->type == PropertyNotify)
1749       {
1750         Atom active_app_atom =
1751             XInternAtom (GDK_DISPLAY(), "_MB_CURRENT_APP_WINDOW", False);
1752         XPropertyEvent *prop = xevent;
1753
1754         if ((prop->atom == active_app_atom)
1755             && (prop->window == GDK_ROOT_WINDOW()))
1756           {
1757             Atom realtype;
1758             int format;
1759             int status;
1760             unsigned long n;
1761             unsigned long extra;
1762             Window my_window;
1763             union
1764             {
1765                 Window *win;
1766                 unsigned char *char_pointer;
1767             } win;
1768
1769             win.win = NULL;
1770
1771             status = XGetWindowProperty(GDK_DISPLAY(), GDK_ROOT_WINDOW(),
1772                                         active_app_atom, 0L, 16L,
1773                                         0, XA_WINDOW, &realtype, &format,
1774                                         &n, &extra, &win.char_pointer);
1775             if (!(status == Success && realtype == XA_WINDOW && format == 32
1776                  && n == 1 && win.win != NULL))
1777               {
1778                 if (win.win != NULL)
1779                     XFree(win.char_pointer);
1780                 return GDK_FILTER_CONTINUE;
1781               }
1782
1783             my_window = GDK_WINDOW_XID(GTK_WIDGET(app)->window);
1784
1785             /* Are we the topmost one? */
1786             if (win.win[0] == my_window || 
1787                 get_active_main_window(win.win[0]) == my_window)
1788               {
1789                 if (!priv->is_topmost)
1790                     g_signal_emit_by_name (G_OBJECT(app),
1791                                           "topmost_status_acquire");
1792               }
1793             else if (priv->is_topmost)
1794             {
1795               GtkWidget *focus = gtk_window_get_focus(GTK_WINDOW(app));
1796
1797               /* FIXME: IM hack, IM-module should do this in response to
1798                         topmost_status_lose (emission hook?) */
1799               if (GTK_IS_ENTRY(focus))
1800                 gtk_im_context_focus_out(GTK_ENTRY(focus)->im_context);
1801               if (GTK_IS_TEXT_VIEW(focus))
1802                 gtk_im_context_focus_out(GTK_TEXT_VIEW(focus)->im_context);
1803
1804               g_signal_emit_by_name (app, "topmost_status_lose");
1805             }
1806
1807             if (win.win != NULL)
1808                 XFree(win.char_pointer);
1809           }
1810       }
1811
1812     return GDK_FILTER_CONTINUE;
1813       }
1814
1815 /*
1816  * Sets the GTK Window title to the application's title, or 
1817  * combined appview/app title, if two part title is asked.
1818  */
1819 static void
1820 hildon_app_construct_title (HildonApp *self)
1821 {
1822   g_return_if_fail (HILDON_IS_APP (self));
1823
1824   if (GTK_WIDGET_REALIZED(self))
1825   {
1826     HildonAppPrivate *priv;
1827     GdkAtom subname;
1828     gchar *concatenated_title = NULL;
1829     HildonAppView *appview;
1830
1831     priv = HILDON_APP_GET_PRIVATE (self);
1832     appview = hildon_app_get_appview(self);
1833     
1834     /* FIXME: The subname property is legacy stuff no longer supported by
1835        Matchbox. However, it is still set for the convenience of
1836        the Task Navigator. */
1837     subname = gdk_atom_intern("_MB_WIN_SUB_NAME", FALSE);
1838
1839     if (!appview || !hildon_app_get_two_part_title(self) ||
1840         g_utf8_strlen(hildon_appview_get_title(appview), -1) < 1 )
1841       {
1842         /* Set an invisible dummy value if there is no appview title */
1843         gdk_property_change (GTK_WIDGET(self)->window, subname,
1844                              gdk_atom_intern ("UTF8_STRING", FALSE),
1845                              8, GDK_PROP_MODE_REPLACE, (guchar *) " \0", 1);
1846         gtk_window_set_title (GTK_WINDOW(self), priv->title);
1847       }
1848     else
1849       {
1850         gdk_property_change (GTK_WIDGET(self)->window, subname,
1851                             gdk_atom_intern ("UTF8_STRING", FALSE),
1852                             8, GDK_PROP_MODE_REPLACE,
1853                             (guchar *)hildon_appview_get_title(appview),
1854                             strlen(hildon_appview_get_title (appview)));
1855         concatenated_title = g_strjoin(TITLE_DELIMITER, priv->title,
1856             hildon_appview_get_title(appview), NULL);
1857         /* priv->title should always be non-null, but check anyway */
1858         if (concatenated_title != NULL)
1859         {
1860           gtk_window_set_title (GTK_WINDOW(self), concatenated_title);
1861           g_free(concatenated_title);
1862         }
1863       }
1864   }    
1865 }
1866
1867 /*
1868  * Callback function to the topmost_status_acquire signal emitted by
1869  * hildon_app_event_filter function. See it for more details.
1870  */
1871 void
1872 hildon_app_real_topmost_status_acquire (HildonApp *self)
1873 {
1874   HildonAppPrivate *priv;
1875   g_return_if_fail (HILDON_IS_APP (self));
1876   priv = HILDON_APP_GET_PRIVATE (self);
1877
1878   /* FIXME: What is the logic not to update topmost status now? */
1879   if (!GTK_BIN (self)->child)
1880     return;
1881
1882   priv->is_topmost = TRUE;
1883 }
1884
1885 /*
1886  * Callback function to the topmost_status_lose signal emitted by
1887  * hildon_app_event_filter function. See it for more details.
1888  */
1889 void
1890 hildon_app_real_topmost_status_lose (HildonApp *self)
1891 {
1892   HildonAppPrivate *priv;
1893   g_return_if_fail (HILDON_IS_APP (self));
1894   priv = HILDON_APP_GET_PRIVATE (self);
1895
1896   /* FIXME: What is the logic not to update topmost status now? */
1897   if (!GTK_BIN (self)->child)
1898     return;
1899
1900   priv->is_topmost = FALSE;
1901 }
1902
1903 void
1904 hildon_app_real_switch_to (HildonApp *self)
1905 {
1906   g_return_if_fail (HILDON_IS_APP (self));
1907   /* Do we have to do anything here? */
1908 }
1909
1910
1911 /**
1912  * hildon_app_set_autoregistration
1913  * @self : a @HildonApp
1914  * @auto_reg : Whether the (app)view autoregistration should be active
1915  *
1916  * Controls the autoregistration/unregistration of (app)views.
1917  **/
1918
1919 void hildon_app_set_autoregistration(HildonApp *self, gboolean auto_reg)
1920 {
1921   HildonAppPrivate *priv;
1922   g_return_if_fail (HILDON_IS_APP (self));
1923
1924   priv = HILDON_APP_GET_PRIVATE (self);
1925   priv->autoregistration = auto_reg;
1926 }
1927
1928
1929 /**
1930  * hildon_app_register_view:
1931  * @self : a @HildonApp
1932  * @view_ptr : Pointer to the view instance to be registered
1933  *
1934  * Registers a new view. For appviews, this can be done automatically
1935  * if autoregistration is set.
1936  */
1937
1938 void hildon_app_register_view(HildonApp *self, gpointer view_ptr)
1939 {
1940   HildonAppPrivate *priv;
1941   view_item *view_item_inst;
1942
1943   g_return_if_fail (HILDON_IS_APP (self) || view_ptr != NULL);
1944
1945   priv = HILDON_APP_GET_PRIVATE (self);  
1946
1947   if (hildon_app_find_view_id(self, view_ptr) == 0)
1948   {
1949     /* The pointer to the view was unique, so add it to the list */
1950     view_item_inst = g_malloc(sizeof(view_item));
1951     view_item_inst->view_id = priv->view_id_counter;
1952     view_item_inst->view_ptr = view_ptr;
1953
1954     priv->view_id_counter++;
1955
1956     priv->view_ids = 
1957       g_slist_append(priv->view_ids, view_item_inst);
1958
1959     /* Update the list of views */
1960     if (GTK_WIDGET_REALIZED(self))
1961       hildon_app_apply_client_list(self);
1962   }
1963 }
1964
1965
1966 /**
1967  * hildon_app_register_view_with_id:
1968  * @self : a @HildonApp
1969  * @view_ptr : Pointer to the view instance to be registered
1970  * @view_id : The ID of the view
1971  * 
1972  * Registers a new view. Allows the application to specify any ID.
1973  * 
1974  * Return value: TRUE if the view registration succeeded, FALSE otherwise.
1975  *          The probable cause of failure is that view with that ID
1976  *          already existed.
1977  */
1978
1979 gboolean hildon_app_register_view_with_id(HildonApp *self,
1980                                           gpointer view_ptr,
1981                                           unsigned long view_id)
1982 {
1983   view_item *view_item_inst;  
1984   HildonAppPrivate *priv;
1985   GSList *list_ptr = NULL;
1986
1987   g_return_val_if_fail (HILDON_IS_APP (self), FALSE);
1988   g_return_val_if_fail (view_ptr, FALSE);
1989
1990   priv = HILDON_APP_GET_PRIVATE (self);
1991   
1992   list_ptr = priv->view_ids;
1993
1994   /* Check that the view is not already registered */
1995   while (list_ptr)
1996     {
1997       if ( (gpointer)((view_item *)list_ptr->data)->view_ptr == view_ptr
1998         && (unsigned long)((view_item *)list_ptr->data)->view_id == view_id)
1999         {
2000           return FALSE;
2001         }
2002       list_ptr = list_ptr->next;
2003     }
2004
2005    /* The pointer to the view was unique, so add it to the list */
2006   view_item_inst = g_malloc(sizeof(view_item));
2007   view_item_inst->view_id = view_id;
2008   view_item_inst->view_ptr = view_ptr;
2009
2010   priv->view_ids = 
2011     g_slist_append(priv->view_ids, view_item_inst);
2012
2013   priv->view_id_counter++;
2014
2015   /* Finally, update the _NET_CLIENT_LIST property */
2016   if (GTK_WIDGET_REALIZED(self))
2017     hildon_app_apply_client_list(self);
2018
2019   return TRUE;
2020 }
2021
2022 /**
2023  * hildon_app_unregister_view:
2024  * @self : A @HildonApp
2025  * @view_ptr : Pointer to the view instance to be unregistered
2026  *
2027  * Unregisters a view from HildonApp. Done usually when a view is
2028  * destroyed. For appviews, this is can be automatically
2029  * if autoregistration is set.
2030  */
2031 void hildon_app_unregister_view(HildonApp *self, gpointer view_ptr)
2032 {
2033   HildonAppPrivate *priv = NULL;
2034   GSList *list_ptr = NULL;
2035
2036   g_return_if_fail (HILDON_IS_APP (self));
2037   g_return_if_fail (view_ptr != NULL);
2038   
2039   priv = HILDON_APP_GET_PRIVATE (self);
2040   
2041   /* Search the view from the list */
2042   list_ptr = priv->view_ids;
2043   
2044   while (list_ptr)
2045     { 
2046       if ( (gpointer)((view_item *)list_ptr->data)->view_ptr == view_ptr)
2047               {
2048             /* Found the view, kick it off */
2049             g_free (list_ptr->data);
2050                   priv->view_ids = g_slist_delete_link(priv->view_ids, list_ptr);
2051                   break;
2052                }
2053       list_ptr = list_ptr->next;
2054     }
2055   
2056   if (GTK_WIDGET_REALIZED(self))
2057     hildon_app_apply_client_list(self);
2058 }
2059
2060
2061 /**
2062  * hildon_app_unregister_view_with_id:
2063  * @self : A @HildonApp
2064  * @view_id : The ID of the view that should be unregistered
2065  * 
2066  * Unregisters a view with specified ID, if it exists.
2067  */
2068 void hildon_app_unregister_view_with_id(HildonApp *self,
2069                                         unsigned long view_id)
2070 {
2071   HildonAppPrivate *priv;
2072   GSList *list_ptr = NULL;
2073   
2074   g_return_if_fail (HILDON_IS_APP (self));
2075   
2076   priv = HILDON_APP_GET_PRIVATE (self);
2077   
2078   /* Search the view from the list */
2079   list_ptr = priv->view_ids;
2080   
2081   while (list_ptr)
2082     { 
2083       if ( (unsigned long)((view_item *)list_ptr->data)->view_id == view_id)
2084         {
2085       /* Found view with given id, kick it off */
2086           g_free (list_ptr->data);
2087           priv->view_ids = g_slist_delete_link(priv->view_ids, list_ptr);
2088           break;
2089         }
2090       list_ptr = list_ptr->next;
2091     }
2092
2093   /* Update client list to reflect new situation. If we are not
2094      realized, then nobody knows about us anyway... */
2095   if (GTK_WIDGET_REALIZED(self))
2096     hildon_app_apply_client_list(self);  
2097 }
2098
2099
2100 /**
2101  * hildon_app_notify_view_changed:
2102  * @self : A @HildonApp
2103  * @view_ptr : Pointer to the view that is switched to
2104  * 
2105  * Updates the X property that contains the currently active view
2106  */
2107 void hildon_app_notify_view_changed(HildonApp *self, gpointer view_ptr)
2108 {
2109   g_return_if_fail (HILDON_IS_APP (self));
2110   g_return_if_fail (view_ptr != NULL);
2111
2112   /* We need GdkWindow before we can send X messages */ 
2113   if (GTK_WIDGET_REALIZED(self))
2114   {
2115     gulong id = hildon_app_find_view_id(self, view_ptr);
2116     Atom active_view = XInternAtom (GDK_DISPLAY(),
2117                                  "_NET_ACTIVE_WINDOW", False);
2118
2119     if (id) {
2120       /* Set _NET_ACTIVE_WINDOW for our own toplevel to contain view id */
2121       XChangeProperty(GDK_DISPLAY(), GDK_WINDOW_XID(GTK_WIDGET(self)->window),
2122                       active_view, XA_WINDOW, 32, PropModeReplace,
2123                       (unsigned char *)&id, 1);
2124     XFlush(GDK_DISPLAY());
2125     }
2126   }
2127 }
2128
2129
2130 /**
2131  * hildon_app_find_view_id:
2132  * @self : a @HildonApp
2133  * @view_ptr : Pointer to the view whose ID we want to acquire
2134  * 
2135  * Return value: The ID of the view, or 0 if not found.
2136  *
2137  * Allows mapping of view pointer to its view ID. If NULL is passed
2138  * as the view pointer, returns the ID of the current view.
2139  */
2140 unsigned long hildon_app_find_view_id(HildonApp *self, gpointer view_ptr)
2141 {
2142   HildonAppPrivate *priv;
2143   GSList *iter;
2144
2145   priv = HILDON_APP_GET_PRIVATE (self);
2146
2147   /* If no view is given, find the ID for the currently visible view */
2148   if (!view_ptr)
2149     view_ptr = GTK_BIN (self)->child;
2150   if (!view_ptr)
2151     return 0;
2152
2153   /* Iterate through list and search for given view pointer */
2154   for (iter = priv->view_ids; iter; iter = iter->next)
2155   {
2156     if ( (gpointer)((view_item *)iter->data)->view_ptr == view_ptr)
2157           return (unsigned long)((view_item *)iter->data)->view_id;
2158   }
2159
2160   return 0;
2161 }
2162
2163 /**
2164  * hildon_app_set_killable:
2165  * @self : A @HildonApp
2166  * @killability : Truth value indicating whether the app can be killed
2167  *                       
2168  * Updates information about whether the application can be killed or not by
2169  * Task Navigator (i.e. whether its statesave is up to date)
2170  */
2171 void hildon_app_set_killable(HildonApp *self, gboolean killability)
2172 {
2173   HildonAppPrivate *priv = HILDON_APP_GET_PRIVATE (self);
2174   g_return_if_fail (HILDON_IS_APP (self) );
2175
2176   if (killability != priv->killable)
2177   {
2178     priv->killable = killability;
2179
2180     /* If we have a window, then we can actually set this 
2181        property. Otherwise we wait until we are realized */
2182     if (GTK_WIDGET_REALIZED(self))
2183       hildon_app_apply_killable(self);
2184   }
2185 }
2186
2187
2188 /**
2189  * hildon_app_set_ui_manager:
2190  * @self : #HildonApp
2191  * @uim : #GtkUIManager to be set
2192  * 
2193  * Sets the #GtkUIManager assigned to the #HildonApp.
2194  * If @uim is NULL, unsets the current ui manager.
2195  * The @HildonApp holds a reference to the ui manager until
2196  * the @HildonApp is destroyed or unset.
2197  **/
2198 void hildon_app_set_ui_manager(HildonApp *self, GtkUIManager *uim)
2199 {
2200     HildonAppPrivate *priv;
2201
2202     g_return_if_fail(self && HILDON_IS_APP(self));
2203     
2204     priv = HILDON_APP_GET_PRIVATE (self);
2205
2206     /* Release old ui-manager object if such exists */    
2207     if (priv->uim != NULL)
2208       {
2209         g_object_unref (G_OBJECT (priv->uim));
2210       }
2211     
2212     priv->uim = uim;
2213
2214     /* If we got new ui-manager (it's perfectly valid not 
2215        to give one), acquire reference to it */
2216     if (priv->uim != NULL)
2217       {
2218         g_object_ref (G_OBJECT (uim));
2219       }
2220
2221     g_object_notify (G_OBJECT(self), "ui-manager");
2222 }
2223
2224 /**
2225  * hildon_app_get_ui_manager:
2226  * @self : #HildonApp
2227  * 
2228  * Gets the #GtkUIManager assigned to the #HildonApp.
2229  *
2230  * Return value: The #GtkUIManager assigned to this application
2231  * or null if no manager is assigned.
2232  **/
2233 GtkUIManager *hildon_app_get_ui_manager(HildonApp *self)
2234 {
2235     HildonAppPrivate *priv;
2236     
2237     g_return_val_if_fail(self && HILDON_IS_APP(self), NULL);
2238
2239     priv = HILDON_APP_GET_PRIVATE (self);
2240
2241     return (priv->uim);
2242 }
2243
2244 /*
2245  * Search for a view with the given id within HildonApp.
2246  * Returns a pointer to the found view, or NULL if not found.
2247  */
2248 static gpointer find_view(HildonApp *self, unsigned long view_id)
2249 {
2250   HildonAppPrivate *priv;
2251   GSList *iter;
2252   
2253   priv = HILDON_APP_GET_PRIVATE (self);
2254
2255   /* Iterate through the list of view ids and search given id */
2256   for (iter = priv->view_ids; iter; iter = iter->next)
2257   {
2258     if ( (unsigned long)((view_item *)iter->data)->view_id == view_id)
2259                   return (gpointer)((view_item *)iter->data)->view_ptr;
2260   }
2261
2262   return NULL;
2263 }