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