2006-11-14 Michael Dominic Kostrzewa <michael.kostrzewa@nokia.com>
[hildon] / src / hildon-app.c
1 /*
2  * This file is part of hildon-libs
3  *
4  * Copyright (C) 2005, 2006 Nokia Corporation, all rights reserved.
5  *
6  * Contact: Michael Dominic Kostrzewa <michael.kostrzewa@nokia.com>
7  *
8  * This library is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Lesser General Public License
10  * as published by the Free Software Foundation; version 2.1 of
11  * the License.
12  *
13  * This library is distributed in the hope that it will be useful, but
14  * WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16  * Lesser General Public License for more details.
17  *
18  * You should have received a copy of the GNU Lesser General Public
19  * License along with this library; if not, write to the Free Software
20  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
21  * 02110-1301 USA
22  *
23  */
24
25 /**
26  * 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 #include <gtk/gtktogglebutton.h>
55 #include <gtk/gtkcombobox.h>
56
57 #include <libintl.h>
58 #include <string.h>
59
60 #include <libmb/mbutil.h>
61
62 #ifdef HAVE_CONFIG_H
63 #include <config.h>
64 #endif
65
66 #define TITLE_DELIMITER " - "
67
68 /*
69  * 'Magic' values for the titlebar menu area limits
70  */
71 #define MENUAREA_LEFT_LIMIT 80
72 #define MENUAREA_RIGHT_LIMIT MENUAREA_LEFT_LIMIT + 307
73 #define MENUAREA_TOP_LIMIT 0
74 #define MENUAREA_BOTTOM_LIMIT 39
75
76 #define KILLABLE "CANKILL"
77
78 #define _(String) dgettext(PACKAGE, String)
79
80 #define HILDON_APP_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), \
81       HILDON_TYPE_APP, HildonAppPrivate));
82
83 static GtkWindowClass *parent_class;
84 static guint app_signals[HILDON_APP_LAST_SIGNAL] = { 0 };
85
86 typedef struct _HildonAppPrivate HildonAppPrivate;
87
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 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  * Handles the key press of the Escape, Increase and Decrease keys. Other keys
1368  * are handled by the parent GtkWidgetClass.
1369  */
1370 static gboolean
1371 hildon_app_key_press (GtkWidget *widget, GdkEventKey *keyevent)
1372 {
1373   HildonApp *app = HILDON_APP (widget);
1374   HildonAppView *appview;
1375   HildonAppPrivate *priv = HILDON_APP_GET_PRIVATE(app);
1376
1377   if (HILDON_IS_APPVIEW(GTK_BIN (app)->child))
1378     {
1379       appview = HILDON_APPVIEW (GTK_BIN (app)->child);
1380     }
1381   else
1382     {
1383       return FALSE;
1384     }
1385
1386     if (keyevent->keyval == GDK_Escape && priv->escape_timeout == 0)
1387       {
1388         /* Call hildon_app_escape_timeout every 1500ms until it returns FALSE 
1389          * and store the relative GSource id. Since hildon_app_escape_timeout
1390          * can only return FALSE, the call will occurr only once.
1391          */
1392         priv->escape_timeout = g_timeout_add(1500, hildon_app_escape_timeout, app);
1393       }
1394
1395     /* FIXME: Handling +/- keys here is not usefull. Applications
1396               can equally easily handle the keypress themselves. */
1397     else if (HILDON_KEYEVENT_IS_INCREASE_KEY (keyevent))
1398       {
1399         _hildon_appview_increase_button_state_changed (appview,
1400                                                        keyevent->type);
1401       }
1402     else if (HILDON_KEYEVENT_IS_DECREASE_KEY (keyevent))
1403       {
1404         _hildon_appview_decrease_button_state_changed (appview,
1405                                                        keyevent->type);
1406       }
1407
1408     /* Activate default bindings and let the widget with focus to handle key */
1409     return GTK_WIDGET_CLASS (parent_class)->key_press_event (widget, keyevent);
1410 }
1411
1412 /*
1413  * Handles the key release event for the Escape, Toolbar and Fullscreen keys.
1414  */
1415 static gboolean
1416 hildon_app_key_release (GtkWidget *widget, GdkEventKey *keyevent)
1417 {
1418   HildonApp *app = HILDON_APP (widget);
1419   HildonAppView *appview;
1420   HildonAppPrivate *priv = HILDON_APP_GET_PRIVATE(app);
1421
1422   if (HILDON_IS_APPVIEW(GTK_BIN (app)->child))
1423     {
1424       appview = HILDON_APPVIEW (GTK_BIN (app)->child);
1425     }
1426   else
1427     {
1428       return FALSE;
1429     }
1430
1431     if (keyevent->keyval == GDK_Escape)
1432       {
1433         /*
1434          * This will prevent the hildon_app_escape_timeout from being called.
1435          * See hildon_app_escape_timeout and hildon_app_remove_timeout for more.
1436          */
1437         hildon_app_remove_timeout(priv);
1438       }
1439     else if (HILDON_KEYEVENT_IS_TOOLBAR_KEY (keyevent))
1440       {
1441         g_signal_emit_by_name(G_OBJECT(appview),
1442                               "toolbar-toggle-request");
1443       }
1444      else if (HILDON_KEYEVENT_IS_FULLSCREEN_KEY (keyevent))
1445        {
1446          /* Emit the fullscreen_state_change directly, it'll save one step */
1447          if (hildon_appview_get_fullscreen_key_allowed (appview))
1448            {
1449               gboolean fullscreen;
1450               
1451               fullscreen = hildon_appview_get_fullscreen(appview);
1452               g_signal_emit_by_name(G_OBJECT(appview),
1453                           "fullscreen_state_change",
1454                           !fullscreen);
1455            }
1456        }
1457
1458     /* FIXME: Should the event be marked as handled if any of the three
1459               above cases took an action */
1460
1461     /* Activate default bindings and let the widget with focus to handle key */
1462     return GTK_WIDGET_CLASS (parent_class)->key_release_event (widget, keyevent);
1463 }
1464
1465 /*
1466  * Handles the MENU key presses.
1467  */
1468 static gboolean
1469 hildon_app_key_snooper (GtkWidget *widget, GdkEventKey *keyevent, HildonApp *app)
1470 {
1471     /* FIXME: Using normal keypress handler would be better choise. All
1472               keyevents come to window anyway, so we would get the same
1473               keys in that way as well, but we wouldn't need to struggle
1474               with grabs (modal dialogs etc). */
1475
1476     /* Menu key handling is done here */
1477     if ( HILDON_KEYEVENT_IS_MENU_KEY (keyevent) ) {
1478             HildonAppView *appview;
1479             HildonAppPrivate *priv;
1480             GtkWidget *toplevel;
1481             GtkWidget *focus = NULL;
1482
1483       /* Don't act on modal dialogs */
1484       toplevel = gtk_widget_get_toplevel (widget);
1485       focus = gtk_window_get_focus(GTK_WINDOW(app));
1486
1487       /* Don't act when comboboxes are active, if a togglebutton
1488          (combobox) has the focus and it is active, we deactivate the
1489          combobox and do nothing */ 
1490       if (GTK_IS_TOGGLE_BUTTON (focus))
1491         {
1492           GtkWidget *parent = gtk_widget_get_parent (focus);
1493           
1494           if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (focus)) && 
1495               GTK_IS_COMBO_BOX (parent)) 
1496             {
1497               gtk_combo_box_popdown (GTK_COMBO_BOX (parent));
1498               return TRUE;
1499             }
1500         }
1501
1502       /* Don't act when a GtkWindow of a combobox is selected, this
1503          can happen in some applications that change the properties of
1504          the widget focus attribute, WARNING: we are using the name of
1505          the hildon combobox widget to identify the window
1506          (gtkcombobox.c, HILDON_COMBO_BOX_POPUP), if it changes we
1507          must change this name */
1508       if (GTK_IS_WINDOW (widget) && 
1509           !g_ascii_strcasecmp("hildon-combobox-window", gtk_widget_get_name (widget)))
1510         {
1511           return TRUE;
1512         }
1513
1514       if (GTK_IS_DIALOG (toplevel)
1515           && gtk_window_get_modal (GTK_WINDOW (toplevel)))
1516         {
1517           return TRUE;
1518         }
1519                     
1520             appview = HILDON_APPVIEW (GTK_BIN(app)->child);
1521             priv = HILDON_APP_GET_PRIVATE(app);
1522
1523             if ( keyevent->type == GDK_KEY_PRESS ) {
1524             /* Toggle menu on press, avoid key repeat */
1525                     if ( priv->lastmenuclick == 0 ){
1526                             priv->lastmenuclick = 1;
1527                             if (_hildon_appview_toggle_menu(appview,
1528                                             gtk_get_current_event_time()))
1529                 {
1530                     return TRUE;
1531                 }
1532                     }
1533             } else if ( keyevent->type == GDK_KEY_RELEASE ) {
1534             /* We got release, so next press is really a new press, 
1535                not a repeat */
1536                     if ( priv->lastmenuclick == 1 ) {
1537                             priv->lastmenuclick = 0;
1538                     }
1539                     
1540             } else {
1541                     /* Unknown key event */
1542                     return FALSE;
1543             }
1544
1545             /* don't stop the key event so that it reaches GTK where it
1546                closes all existing menus that might be open */
1547             return FALSE;
1548     }
1549
1550     return FALSE;
1551 }
1552
1553 /*
1554  * Returns the message_type of the Atom registered with a certain name.
1555  */
1556 static int
1557 xclient_message_type_check(XClientMessageEvent *cm, const gchar *name)
1558 {
1559   return cm->message_type == XInternAtom(GDK_DISPLAY(), name, FALSE);
1560 }
1561
1562 /*
1563  * Returns the GtkWidget associated to a certain Window.
1564  */
1565 static GtkWidget *
1566 hildon_app_xwindow_lookup_widget(Window xwindow)
1567 {
1568   GdkWindow *window;
1569   gpointer widget;
1570
1571   window = gdk_xid_table_lookup(xwindow);
1572   if (window == NULL)
1573     return NULL;
1574
1575   gdk_window_get_user_data(window, &widget);
1576   return widget;
1577 }
1578
1579 /*
1580  * Let's search a actual main window using tranciency hints. 
1581  * Note that there can be several levels of menus/dialogs above
1582  * the actual main window.
1583  */
1584 static Window get_active_main_window(Window window)
1585 {
1586   Window parent_window;
1587   gint limit = 0;
1588
1589   gdk_error_trap_push ();
1590
1591   while (XGetTransientForHint(GDK_DISPLAY(), window, &parent_window))
1592   {
1593         /* The limit > TRANSIENCY_MAXITER ensures that we can't be stuck
1594            here forever if we have circular transiencies for some reason.
1595            Use of _MB_CURRENT_APP_WINDOW might be more elegant... */
1596
1597     if (!parent_window || parent_window == GDK_ROOT_WINDOW() ||
1598         parent_window == window || limit > TRANSIENCY_MAXITER)
1599       {
1600         break;
1601       }
1602
1603     limit++;
1604     window = parent_window;
1605   }
1606   
1607   gdk_flush ();
1608
1609   if (gdk_error_trap_pop ())
1610     return 0;
1611
1612   return window;
1613 }
1614
1615 /*
1616  * Filters every GDK event first.
1617  */
1618 static GdkFilterReturn
1619 hildon_app_event_filter (GdkXEvent *xevent, GdkEvent *event, gpointer data)
1620 {
1621     gint x,y;
1622     HildonApp *app = data;
1623     HildonAppPrivate *priv;
1624     HildonAppView *appview = NULL;
1625
1626     XAnyEvent *eventti = xevent;
1627
1628     if (HILDON_IS_APPVIEW (GTK_BIN (app)->child))
1629       {
1630         appview = HILDON_APPVIEW (GTK_BIN (app)->child);
1631       }
1632
1633     g_return_val_if_fail (app, GDK_FILTER_CONTINUE);
1634     g_return_val_if_fail (HILDON_IS_APP(app), GDK_FILTER_CONTINUE);
1635
1636     priv = HILDON_APP_GET_PRIVATE(app);
1637     if (eventti->type == ClientMessage)
1638       {
1639         XClientMessageEvent *cm = xevent;
1640
1641         /* Check if a message indicating a click on titlebar has been
1642            received. Don't open it if mouse is grabbed (eg. modal dialog
1643            was just opened).
1644            _MB_GRAB_TRANSFER is emitted by MatchBox, and signals that a button
1645            has just been released. */
1646         if (xclient_message_type_check(cm, "_MB_GRAB_TRANSFER") &&
1647             HILDON_IS_APPVIEW(appview) &&
1648             gtk_grab_get_current() == NULL &&
1649             !_hildon_appview_menu_visible(appview))
1650         {
1651           _hildon_appview_toggle_menu(appview, cm->data.l[0]);
1652           return GDK_FILTER_REMOVE;
1653         }
1654         /* IM_CLOSE is input method specific hack that is really questionable */
1655         else if (xclient_message_type_check(cm, "_HILDON_IM_CLOSE"))
1656         {
1657           g_signal_emit_by_name(app, "im_close", NULL);
1658           return GDK_FILTER_REMOVE;
1659         }
1660         /* Task user changed the view through task navigator? */
1661         else if (xclient_message_type_check(cm, "_NET_ACTIVE_WINDOW"))
1662           {
1663             unsigned long view_id = cm->window;
1664             gpointer view_ptr = find_view(app, view_id);
1665                                                 
1666             /* When getting a _NET_ACTIVE_WINDOW signal from the WM we need
1667              * to bring the application to the front */
1668             if (!priv->is_topmost)
1669               g_signal_emit_by_name (G_OBJECT(app), "topmost_status_acquire");
1670
1671             if (HILDON_IS_APPVIEW(view_ptr))
1672               /* Sets the current view to the "window" that the _NET_ACTIVE_WINDOW
1673                * specified */
1674               hildon_app_set_appview(app, (HILDON_APPVIEW(view_ptr)));
1675             else
1676               /* there was no view, so we have to switch to an actual application */
1677               g_signal_emit_by_name (G_OBJECT(app), "switch_to", view_ptr);
1678
1679             /* FIXME: This is a hack. This was once just gtk_window_present, but
1680                       was changed into this at some day!! */
1681             if (GTK_WIDGET(app)->window)
1682             {
1683               mb_util_window_activate(GDK_DISPLAY(),
1684                                       GDK_WINDOW_XID(GTK_WIDGET(app)->window));
1685             }
1686           }
1687         /* FIXME: IM hack */
1688         else if (xclient_message_type_check(cm, "_HILDON_IM_CLIPBOARD_COPY"))
1689         {
1690           Window xwindow = cm->data.l[0];
1691           GtkWidget *widget = hildon_app_xwindow_lookup_widget(xwindow);
1692
1693           g_signal_emit_by_name (G_OBJECT(app), "clipboard_copy", widget);
1694         }
1695         /* FIXME: IM hack */
1696         else if (xclient_message_type_check(cm, "_HILDON_IM_CLIPBOARD_CUT"))
1697         {
1698           Window xwindow = cm->data.l[0];
1699           GtkWidget *widget = hildon_app_xwindow_lookup_widget(xwindow);
1700
1701           g_signal_emit_by_name (G_OBJECT(app), "clipboard_cut", widget);
1702         }
1703         /* FIXME: IM hack */
1704         else if (xclient_message_type_check(cm, "_HILDON_IM_CLIPBOARD_PASTE"))
1705         {
1706           Window xwindow = cm->data.l[0];
1707           GtkWidget *widget = hildon_app_xwindow_lookup_widget(xwindow);
1708
1709           g_signal_emit_by_name (G_OBJECT(app), "clipboard_paste", widget);
1710         }
1711       }
1712     
1713      if (eventti->type == ButtonPress)
1714        {
1715
1716      /* FIXME: This is mysterious bugfix related to problems to open the
1717         application menu (bugzilla N#3204) */
1718          XButtonEvent *bev = (XButtonEvent *)xevent;
1719
1720          if (HILDON_IS_APPVIEW(appview) &&
1721              _hildon_appview_menu_visible(appview) &&
1722              !hildon_appview_get_fullscreen(appview))
1723           {
1724             x = bev->x_root;
1725             y = bev->y_root;
1726             if ( (x >= MENUAREA_LEFT_LIMIT) && (x <= MENUAREA_RIGHT_LIMIT) &&
1727                  (y >= MENUAREA_TOP_LIMIT) && (y <= MENUAREA_BOTTOM_LIMIT))
1728               {
1729                 _hildon_appview_toggle_menu(appview, bev->time);
1730                 return GDK_FILTER_CONTINUE;
1731               }
1732           }
1733        }
1734     
1735     /* FIXME: as above */
1736     if (eventti->type == ButtonRelease)
1737       {
1738         if (HILDON_IS_APPVIEW(appview) &&
1739             _hildon_appview_menu_visible(appview) &&
1740             !hildon_appview_get_fullscreen(appview))
1741           {
1742             XButtonEvent *bev = (XButtonEvent *)xevent;
1743             x = bev->x_root;
1744             y = bev->y_root;
1745             if ( (x >= MENUAREA_LEFT_LIMIT) && (x < MENUAREA_RIGHT_LIMIT) &&
1746                  (y >= MENUAREA_TOP_LIMIT) && (y <= MENUAREA_BOTTOM_LIMIT))
1747               {
1748                 return GDK_FILTER_REMOVE;
1749               }
1750           }
1751         return GDK_FILTER_CONTINUE;
1752       }
1753
1754     /* Application stacking order changed */
1755     if (eventti->type == PropertyNotify)
1756       {
1757         Atom active_app_atom =
1758             XInternAtom (GDK_DISPLAY(), "_MB_CURRENT_APP_WINDOW", False);
1759         XPropertyEvent *prop = xevent;
1760
1761         if ((prop->atom == active_app_atom)
1762             && (prop->window == GDK_ROOT_WINDOW()))
1763           {
1764             Atom realtype;
1765             int format;
1766             int status;
1767             unsigned long n;
1768             unsigned long extra;
1769             Window my_window;
1770             union
1771             {
1772                 Window *win;
1773                 unsigned char *char_pointer;
1774             } win;
1775
1776             win.win = NULL;
1777
1778             status = XGetWindowProperty(GDK_DISPLAY(), GDK_ROOT_WINDOW(),
1779                                         active_app_atom, 0L, 16L,
1780                                         0, XA_WINDOW, &realtype, &format,
1781                                         &n, &extra, &win.char_pointer);
1782             if (!(status == Success && realtype == XA_WINDOW && format == 32
1783                  && n == 1 && win.win != NULL))
1784               {
1785                 if (win.win != NULL)
1786                     XFree(win.char_pointer);
1787                 return GDK_FILTER_CONTINUE;
1788               }
1789
1790             my_window = GDK_WINDOW_XID(GTK_WIDGET(app)->window);
1791
1792             /* Are we the topmost one? */
1793             if (win.win[0] == my_window || 
1794                 get_active_main_window(win.win[0]) == my_window)
1795               {
1796                 if (!priv->is_topmost)
1797                     g_signal_emit_by_name (G_OBJECT(app),
1798                                           "topmost_status_acquire");
1799               }
1800             else if (priv->is_topmost)
1801             {
1802               GtkWidget *focus = gtk_window_get_focus(GTK_WINDOW(app));
1803
1804               /* FIXME: IM hack, IM-module should do this in response to
1805                         topmost_status_lose (emission hook?) */
1806               if (GTK_IS_ENTRY(focus))
1807                 gtk_im_context_focus_out(GTK_ENTRY(focus)->im_context);
1808               if (GTK_IS_TEXT_VIEW(focus))
1809                 gtk_im_context_focus_out(GTK_TEXT_VIEW(focus)->im_context);
1810
1811               g_signal_emit_by_name (app, "topmost_status_lose");
1812             }
1813
1814             if (win.win != NULL)
1815                 XFree(win.char_pointer);
1816           }
1817       }
1818
1819     return GDK_FILTER_CONTINUE;
1820       }
1821
1822 /*
1823  * Sets the GTK Window title to the application's title, or 
1824  * combined appview/app title, if two part title is asked.
1825  */
1826 static void
1827 hildon_app_construct_title (HildonApp *self)
1828 {
1829   g_return_if_fail (HILDON_IS_APP (self));
1830
1831   if (GTK_WIDGET_REALIZED(self))
1832   {
1833     HildonAppPrivate *priv;
1834     GdkAtom subname;
1835     gchar *concatenated_title = NULL;
1836     HildonAppView *appview;
1837
1838     priv = HILDON_APP_GET_PRIVATE (self);
1839     appview = hildon_app_get_appview(self);
1840     
1841     /* FIXME: The subname property is legacy stuff no longer supported by
1842        Matchbox. However, it is still set for the convenience of
1843        the Task Navigator. */
1844     subname = gdk_atom_intern("_MB_WIN_SUB_NAME", FALSE);
1845
1846     if (!appview || !hildon_app_get_two_part_title(self) ||
1847         g_utf8_strlen(hildon_appview_get_title(appview), -1) < 1 )
1848       {
1849         /* Set an invisible dummy value if there is no appview title */
1850         gdk_property_change (GTK_WIDGET(self)->window, subname,
1851                              gdk_atom_intern ("UTF8_STRING", FALSE),
1852                              8, GDK_PROP_MODE_REPLACE, (guchar *) " \0", 1);
1853         gtk_window_set_title (GTK_WINDOW(self), priv->title);
1854       }
1855     else
1856       {
1857         gdk_property_change (GTK_WIDGET(self)->window, subname,
1858                             gdk_atom_intern ("UTF8_STRING", FALSE),
1859                             8, GDK_PROP_MODE_REPLACE,
1860                             (guchar *)hildon_appview_get_title(appview),
1861                             strlen(hildon_appview_get_title (appview)));
1862         concatenated_title = g_strjoin(TITLE_DELIMITER, priv->title,
1863             hildon_appview_get_title(appview), NULL);
1864         /* priv->title should always be non-null, but check anyway */
1865         if (concatenated_title != NULL)
1866         {
1867           gtk_window_set_title (GTK_WINDOW(self), concatenated_title);
1868           g_free(concatenated_title);
1869         }
1870       }
1871   }    
1872 }
1873
1874 /*
1875  * Callback function to the topmost_status_acquire signal emitted by
1876  * hildon_app_event_filter function. See it for more details.
1877  */
1878 void
1879 hildon_app_real_topmost_status_acquire (HildonApp *self)
1880 {
1881   HildonAppPrivate *priv;
1882   g_return_if_fail (HILDON_IS_APP (self));
1883   priv = HILDON_APP_GET_PRIVATE (self);
1884
1885   /* FIXME: What is the logic not to update topmost status now? */
1886   if (!GTK_BIN (self)->child)
1887     return;
1888
1889   priv->is_topmost = TRUE;
1890 }
1891
1892 /*
1893  * Callback function to the topmost_status_lose signal emitted by
1894  * hildon_app_event_filter function. See it for more details.
1895  */
1896 void
1897 hildon_app_real_topmost_status_lose (HildonApp *self)
1898 {
1899   HildonAppPrivate *priv;
1900   g_return_if_fail (HILDON_IS_APP (self));
1901   priv = HILDON_APP_GET_PRIVATE (self);
1902
1903   /* FIXME: What is the logic not to update topmost status now? */
1904   if (!GTK_BIN (self)->child)
1905     return;
1906
1907   priv->is_topmost = FALSE;
1908 }
1909
1910 void
1911 hildon_app_real_switch_to (HildonApp *self)
1912 {
1913   g_return_if_fail (HILDON_IS_APP (self));
1914   /* Do we have to do anything here? */
1915 }
1916
1917
1918 /**
1919  * hildon_app_set_autoregistration
1920  * @self : a #HildonApp
1921  * @auto_reg : whether the (app)view autoregistration should be active
1922  *
1923  * Controls the autoregistration/unregistration of (app)views.
1924  */
1925
1926 void hildon_app_set_autoregistration(HildonApp *self, gboolean auto_reg)
1927 {
1928   HildonAppPrivate *priv;
1929   g_return_if_fail (HILDON_IS_APP (self));
1930
1931   priv = HILDON_APP_GET_PRIVATE (self);
1932   priv->autoregistration = auto_reg;
1933 }
1934
1935
1936 /**
1937  * hildon_app_register_view:
1938  * @self : a #HildonApp
1939  * @view_ptr : pointer to the view instance to be registered
1940  *
1941  * Registers a new view. For appviews, this can be done automatically
1942  * if autoregistration is set.
1943  */
1944
1945 void hildon_app_register_view(HildonApp *self, gpointer view_ptr)
1946 {
1947   HildonAppPrivate *priv;
1948   view_item *view_item_inst;
1949
1950   g_return_if_fail (HILDON_IS_APP (self) || view_ptr != NULL);
1951
1952   priv = HILDON_APP_GET_PRIVATE (self);  
1953
1954   if (hildon_app_find_view_id(self, view_ptr) == 0)
1955   {
1956     /* The pointer to the view was unique, so add it to the list */
1957     view_item_inst = g_malloc(sizeof(view_item));
1958     view_item_inst->view_id = priv->view_id_counter;
1959     view_item_inst->view_ptr = view_ptr;
1960
1961     priv->view_id_counter++;
1962
1963     priv->view_ids = 
1964       g_slist_append(priv->view_ids, view_item_inst);
1965
1966     /* Update the list of views */
1967     if (GTK_WIDGET_REALIZED(self))
1968       hildon_app_apply_client_list(self);
1969   }
1970 }
1971
1972
1973 /**
1974  * hildon_app_register_view_with_id:
1975  * @self : a #HildonApp
1976  * @view_ptr : pointer to the view instance to be registered
1977  * @view_id : the ID of the view
1978  * 
1979  * Registers a new view. Allows the application to specify any ID.
1980  * 
1981  * Returns: TRUE if the view registration succeeded, FALSE otherwise.
1982  *          The probable cause of failure is that view with that ID
1983  *          already existed.
1984  */
1985
1986 gboolean hildon_app_register_view_with_id(HildonApp *self,
1987                                           gpointer view_ptr,
1988                                           unsigned long view_id)
1989 {
1990   view_item *view_item_inst;  
1991   HildonAppPrivate *priv;
1992   GSList *list_ptr = NULL;
1993
1994   g_return_val_if_fail (HILDON_IS_APP (self), FALSE);
1995   g_return_val_if_fail (view_ptr, FALSE);
1996
1997   priv = HILDON_APP_GET_PRIVATE (self);
1998   
1999   list_ptr = priv->view_ids;
2000
2001   /* Check that the view is not already registered */
2002   while (list_ptr)
2003     {
2004       if ( (gpointer)((view_item *)list_ptr->data)->view_ptr == view_ptr
2005         && (unsigned long)((view_item *)list_ptr->data)->view_id == view_id)
2006         {
2007           return FALSE;
2008         }
2009       list_ptr = list_ptr->next;
2010     }
2011
2012    /* The pointer to the view was unique, so add it to the list */
2013   view_item_inst = g_malloc(sizeof(view_item));
2014   view_item_inst->view_id = view_id;
2015   view_item_inst->view_ptr = view_ptr;
2016
2017   priv->view_ids = 
2018     g_slist_append(priv->view_ids, view_item_inst);
2019
2020   priv->view_id_counter++;
2021
2022   /* Finally, update the _NET_CLIENT_LIST property */
2023   if (GTK_WIDGET_REALIZED(self))
2024     hildon_app_apply_client_list(self);
2025
2026   return TRUE;
2027 }
2028
2029 /**
2030  * hildon_app_unregister_view:
2031  * @self : a #HildonApp
2032  * @view_ptr : pointer to the view instance to be unregistered
2033  *
2034  * Unregisters a view from HildonApp. Done usually when a view is
2035  * destroyed. For appviews, this is can be automatically
2036  * if autoregistration is set.
2037  */
2038 void hildon_app_unregister_view(HildonApp *self, gpointer view_ptr)
2039 {
2040   HildonAppPrivate *priv = NULL;
2041   GSList *list_ptr = NULL;
2042
2043   g_return_if_fail (HILDON_IS_APP (self));
2044   g_return_if_fail (view_ptr != NULL);
2045   
2046   priv = HILDON_APP_GET_PRIVATE (self);
2047   
2048   /* Search the view from the list */
2049   list_ptr = priv->view_ids;
2050   
2051   while (list_ptr)
2052     { 
2053       if ( (gpointer)((view_item *)list_ptr->data)->view_ptr == view_ptr)
2054               {
2055             /* Found the view, kick it off */
2056             g_free (list_ptr->data);
2057                   priv->view_ids = g_slist_delete_link(priv->view_ids, list_ptr);
2058                   break;
2059                }
2060       list_ptr = list_ptr->next;
2061     }
2062   
2063   if (GTK_WIDGET_REALIZED(self))
2064     hildon_app_apply_client_list(self);
2065 }
2066
2067
2068 /**
2069  * hildon_app_unregister_view_with_id:
2070  * @self: a #HildonApp
2071  * @view_id: the ID of the view that should be unregistered
2072  * 
2073  * Unregisters a view with specified ID, if it exists.
2074  */
2075 void hildon_app_unregister_view_with_id(HildonApp *self,
2076                                         unsigned long view_id)
2077 {
2078   HildonAppPrivate *priv;
2079   GSList *list_ptr = NULL;
2080   
2081   g_return_if_fail (HILDON_IS_APP (self));
2082   
2083   priv = HILDON_APP_GET_PRIVATE (self);
2084   
2085   /* Search the view from the list */
2086   list_ptr = priv->view_ids;
2087   
2088   while (list_ptr)
2089     { 
2090       if ( (unsigned long)((view_item *)list_ptr->data)->view_id == view_id)
2091         {
2092       /* Found view with given id, kick it off */
2093           g_free (list_ptr->data);
2094           priv->view_ids = g_slist_delete_link(priv->view_ids, list_ptr);
2095           break;
2096         }
2097       list_ptr = list_ptr->next;
2098     }
2099
2100   /* Update client list to reflect new situation. If we are not
2101      realized, then nobody knows about us anyway... */
2102   if (GTK_WIDGET_REALIZED(self))
2103     hildon_app_apply_client_list(self);  
2104 }
2105
2106
2107 /**
2108  * hildon_app_notify_view_changed:
2109  * @self : a #HildonApp
2110  * @view_ptr : pointer to the view that is switched to
2111  * 
2112  * Updates the X property that contains the currently active view
2113  */
2114 void hildon_app_notify_view_changed(HildonApp *self, gpointer view_ptr)
2115 {
2116   g_return_if_fail (HILDON_IS_APP (self));
2117   g_return_if_fail (view_ptr != NULL);
2118
2119   /* We need GdkWindow before we can send X messages */ 
2120   if (GTK_WIDGET_REALIZED(self))
2121   {
2122     gulong id = hildon_app_find_view_id(self, view_ptr);
2123     Atom active_view = XInternAtom (GDK_DISPLAY(),
2124                                  "_NET_ACTIVE_WINDOW", False);
2125
2126     if (id) {
2127       /* Set _NET_ACTIVE_WINDOW for our own toplevel to contain view id */
2128       XChangeProperty(GDK_DISPLAY(), GDK_WINDOW_XID(GTK_WIDGET(self)->window),
2129                       active_view, XA_WINDOW, 32, PropModeReplace,
2130                       (unsigned char *)&id, 1);
2131     XFlush(GDK_DISPLAY());
2132     }
2133   }
2134 }
2135
2136
2137 /**
2138  * hildon_app_find_view_id:
2139  * @self : a #HildonApp
2140  * @view_ptr : pointer to the view whose ID we want to acquire
2141  * 
2142  * Returns: the ID of the view, or 0 if not found
2143  *
2144  * Allows mapping of view pointer to its view ID. If NULL is passed
2145  * as the view pointer, returns the ID of the current view.
2146  */
2147 unsigned long hildon_app_find_view_id(HildonApp *self, gpointer view_ptr)
2148 {
2149   HildonAppPrivate *priv;
2150   GSList *iter;
2151
2152   priv = HILDON_APP_GET_PRIVATE (self);
2153
2154   /* If no view is given, find the ID for the currently visible view */
2155   if (!view_ptr)
2156     view_ptr = GTK_BIN (self)->child;
2157   if (!view_ptr)
2158     return 0;
2159
2160   /* Iterate through list and search for given view pointer */
2161   for (iter = priv->view_ids; iter; iter = iter->next)
2162   {
2163     if ( (gpointer)((view_item *)iter->data)->view_ptr == view_ptr)
2164           return (unsigned long)((view_item *)iter->data)->view_id;
2165   }
2166
2167   return 0;
2168 }
2169
2170 /**
2171  * hildon_app_set_killable:
2172  * @self : a #HildonApp
2173  * @killability : truth value indicating whether the app can be killed
2174  *                       
2175  * Updates information about whether the application can be killed or not by
2176  * Task Navigator (i.e. whether its statesave is up to date)
2177  */
2178 void hildon_app_set_killable(HildonApp *self, gboolean killability)
2179 {
2180   HildonAppPrivate *priv = HILDON_APP_GET_PRIVATE (self);
2181   g_return_if_fail (HILDON_IS_APP (self) );
2182
2183   if (killability != priv->killable)
2184   {
2185     priv->killable = killability;
2186
2187     /* If we have a window, then we can actually set this 
2188        property. Otherwise we wait until we are realized */
2189     if (GTK_WIDGET_REALIZED(self))
2190       hildon_app_apply_killable(self);
2191   }
2192 }
2193
2194
2195 /**
2196  * hildon_app_set_ui_manager:
2197  * @self : #HildonApp
2198  * @uim : #GtkUIManager to be set
2199  * 
2200  * Sets the #GtkUIManager assigned to the #HildonApp.
2201  * If @uim is NULL, unsets the current ui manager.
2202  * The @HildonApp holds a reference to the ui manager until
2203  * the @HildonApp is destroyed or unset.
2204  */
2205 void hildon_app_set_ui_manager(HildonApp *self, GtkUIManager *uim)
2206 {
2207     HildonAppPrivate *priv;
2208
2209     g_return_if_fail(self && HILDON_IS_APP(self));
2210     
2211     priv = HILDON_APP_GET_PRIVATE (self);
2212
2213     /* Release old ui-manager object if such exists */    
2214     if (priv->uim != NULL)
2215       {
2216         g_object_unref (G_OBJECT (priv->uim));
2217       }
2218     
2219     priv->uim = uim;
2220
2221     /* If we got new ui-manager (it's perfectly valid not 
2222        to give one), acquire reference to it */
2223     if (priv->uim != NULL)
2224       {
2225         g_object_ref (G_OBJECT (uim));
2226       }
2227
2228     g_object_notify (G_OBJECT(self), "ui-manager");
2229 }
2230
2231 /**
2232  * hildon_app_get_ui_manager:
2233  * @self : #HildonApp
2234  * 
2235  * Gets the #GtkUIManager assigned to the #HildonApp.
2236  *
2237  * Returns: the #GtkUIManager assigned to this application
2238  * or null if no manager is assigned
2239  */
2240 GtkUIManager *hildon_app_get_ui_manager(HildonApp *self)
2241 {
2242     HildonAppPrivate *priv;
2243     
2244     g_return_val_if_fail(self && HILDON_IS_APP(self), NULL);
2245
2246     priv = HILDON_APP_GET_PRIVATE (self);
2247
2248     return (priv->uim);
2249 }
2250
2251 /*
2252  * Search for a view with the given id within HildonApp.
2253  * Returns a pointer to the found view, or NULL if not found.
2254  */
2255 static gpointer find_view(HildonApp *self, unsigned long view_id)
2256 {
2257   HildonAppPrivate *priv;
2258   GSList *iter;
2259   
2260   priv = HILDON_APP_GET_PRIVATE (self);
2261
2262   /* Iterate through the list of view ids and search given id */
2263   for (iter = priv->view_ids; iter; iter = iter->next)
2264   {
2265     if ( (unsigned long)((view_item *)iter->data)->view_id == view_id)
2266                   return (gpointer)((view_item *)iter->data)->view_ptr;
2267   }
2268
2269   return NULL;
2270 }