2 * This file is part of hildon-libs
4 * Copyright (C) 2005 Nokia Corporation.
6 * Contact: Luc Pionchon <luc.pionchon@nokia.com>
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.
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.
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
28 * This file implements the HildonApp widget
33 #include "hildon-app.h"
34 #include "hildon-app-private.h"
35 #include "hildon-appview.h"
36 #include "gtk-infoprint.h"
38 #include <gdk/gdkevents.h>
39 #include <gdk/gdkkeysyms.h>
40 #include <X11/Xatom.h>
41 #include <gtk/gtkmenu.h>
42 #include <gtk/gtkmain.h>
43 #include <gtk/gtkeditable.h>
44 #include <gtk/gtktextview.h>
45 #include <gtk/gtkentry.h>
46 #include <gtk/gtkscrolledwindow.h>
47 #include <gtk/gtkuimanager.h>
48 #include <gtk/gtkactiongroup.h>
49 #include <gtk/gtkdialog.h>
54 #include <libmb/mbutil.h>
60 #define TITLE_DELIMITER " - "
63 * 'Magic' values for the titlebar menu area limits
65 #define MENUAREA_LEFT_LIMIT 80
66 #define MENUAREA_RIGHT_LIMIT MENUAREA_LEFT_LIMIT + 307
67 #define MENUAREA_TOP_LIMIT 0
68 #define MENUAREA_BOTTOM_LIMIT 39
70 #define KILLABLE "CANKILL"
72 #define _(String) dgettext(PACKAGE, String)
74 #define HILDON_APP_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), \
75 HILDON_TYPE_APP, HildonAppPrivate));
77 static GtkWindowClass *parent_class;
78 static guint app_signals[HILDON_APP_LAST_SIGNAL] = { 0 };
80 typedef struct _HildonAppPrivate HildonAppPrivate;
83 hildon_app_switch_to_desktop (void);
85 hildon_app_key_press (GtkWidget *widget, GdkEventKey *keyevent);
87 hildon_app_key_release (GtkWidget *widget, GdkEventKey *keyevent);
89 hildon_app_key_snooper (GtkWidget *widget, GdkEventKey *keyevent, HildonApp *app);
90 static GdkFilterReturn
91 hildon_app_event_filter (GdkXEvent *xevent, GdkEvent *event, gpointer data);
93 hildon_app_construct_title (HildonApp *self);
95 hildon_app_finalize (GObject *obj_self);
97 hildon_app_destroy (GtkObject *obj);
99 hildon_app_init (HildonApp *self);
101 hildon_app_class_init (HildonAppClass *app_class);
103 hildon_app_real_topmost_status_acquire (HildonApp *self);
105 hildon_app_real_topmost_status_lose (HildonApp *self);
107 hildon_app_real_switch_to (HildonApp *self);
109 hildon_app_button (GtkWidget *widget, GdkEventButton *event);
111 find_window (GdkWindow *window, gint by, gint co);
113 hildon_app_clipboard_copy(HildonApp *self, GtkWidget *widget);
115 hildon_app_clipboard_cut(HildonApp *self, GtkWidget *widget);
117 hildon_app_clipboard_paste(HildonApp *self, GtkWidget *widget);
118 static gboolean hildon_app_escape_timeout(gpointer data);
120 static void hildon_app_set_property(GObject * object, guint property_id,
121 const GValue * value, GParamSpec * pspec);
122 static void hildon_app_get_property(GObject * object, guint property_id,
123 GValue * value, GParamSpec * pspec);
125 static void hildon_app_add (GtkContainer *container, GtkWidget *child);
126 static void hildon_app_remove (GtkContainer *container, GtkWidget *child);
127 static void hildon_app_forall (GtkContainer *container, gboolean include_internals,
128 GtkCallback callback, gpointer callback_data);
133 /* FIXME: Zoom is deprecated, should be removed */
138 PROP_AUTOREGISTRATION,
143 static gpointer find_view(HildonApp *self, unsigned long view_id);
145 /* FIXME: Zoom level is deprecated, should be removed */
147 * hildon_zoom_level_get_type:
148 * @Returns : GType of #HildonZoomLevel
150 * Initialises, and returns the type of a hildon zoom level
154 hildon_zoom_level_get_type (void)
156 static GType etype = 0;
158 static const GEnumValue values[] = {
159 { HILDON_ZOOM_SMALL, "HILDON_ZOOM_SMALL", "small" },
160 { HILDON_ZOOM_MEDIUM, "HILDON_ZOOM_MEDIUM", "medium" },
161 { HILDON_ZOOM_LARGE, "HILDON_ZOOM_LARGE", "large" },
164 etype = g_enum_register_static ("HildonZoomLevel", values);
169 GType hildon_app_get_type(void)
171 static GType app_type = 0;
175 static const GTypeInfo app_info =
177 sizeof(HildonAppClass),
178 NULL, /* base_init */
179 NULL, /* base_finalize */
180 (GClassInitFunc) hildon_app_class_init,
181 NULL, /* class_finalize */
182 NULL, /* class_data */
185 (GInstanceInitFunc) hildon_app_init,
187 app_type = g_type_register_static(GTK_TYPE_WINDOW,
188 "HildonApp", &app_info, 0);
194 * Sets or delete a custom property into the XServer, according
195 * to the boolean value of HildonAppPrivate::killable
197 static void hildon_app_apply_killable(HildonApp *self)
199 HildonAppPrivate *priv;
200 Atom killability_atom = XInternAtom (GDK_DISPLAY(),
201 "_HILDON_APP_KILLABLE", False);
202 priv = HILDON_APP_GET_PRIVATE (self);
204 g_assert (HILDON_IS_APP (self) );
205 g_assert(GTK_WIDGET_REALIZED(self));
209 /* Set the atom to specific value, because perhaps in the future,
210 there may be other possible values? */
211 XChangeProperty(GDK_DISPLAY(),
212 GDK_WINDOW_XID(GTK_WIDGET(self)->window),
213 killability_atom, XA_STRING, 8,
214 PropModeReplace, (unsigned char *)KILLABLE,
219 XDeleteProperty(GDK_DISPLAY(),
220 GDK_WINDOW_XID(GTK_WIDGET(self)->window),
226 * Updates the _NET_CLIENT_LIST property into the XServer.
227 * It is the list of the views associated to the HildonApp.
228 * It will be used by the Task Navigator in order to be able to show a list
229 * of all the views, and let the user switch and navigate them.
231 static void hildon_app_apply_client_list(HildonApp *self)
233 HildonAppPrivate *priv;
239 g_assert (HILDON_IS_APP (self) );
240 g_assert(GTK_WIDGET_REALIZED(self));
242 /* Get the client list handle */
243 clientlist = XInternAtom (GDK_DISPLAY(),
244 "_NET_CLIENT_LIST", False);
246 /* Allocate a new array for window IDs */
247 priv = HILDON_APP_GET_PRIVATE(self);
248 win_array = g_new(Window, g_slist_length(priv->view_ids));
250 /* Fill the contents of the window array with current view IDs */
251 for (list_ptr = priv->view_ids; list_ptr; list_ptr = list_ptr->next)
254 (unsigned long)(((view_item *)(list_ptr->data))->view_id);
258 /* Update the details of current view IDs to our X property */
259 XChangeProperty(GDK_DISPLAY(), GDK_WINDOW_XID(GTK_WIDGET(self)->window),
260 clientlist, XA_WINDOW, 32, PropModeReplace,
261 (unsigned char *)win_array,
262 g_slist_length(priv->view_ids));
264 XFlush(GDK_DISPLAY());
269 * Performs the standard gtk realize function.
271 static void hildon_app_realize(GtkWidget *widget)
274 HildonAppPrivate *priv;
276 Atom *old_atoms, *new_atoms;
280 g_assert(widget != NULL);
282 self = HILDON_APP(widget);
283 priv = HILDON_APP_GET_PRIVATE(self);
286 * Of course we need to realize the parent.
287 * parent_class got already initialised in the hildon_app_init function
289 GTK_WIDGET_CLASS(parent_class)->realize(widget);
291 /* some initialisation code */
292 hildon_app_apply_killable(self);
293 hildon_app_construct_title(self);
294 hildon_app_apply_client_list(self);
295 hildon_app_notify_view_changed(self, hildon_app_get_appview(self));
296 window = widget->window;
297 disp = GDK_WINDOW_XDISPLAY(window);
299 /* Install a key snooper for the Home button - so that it works everywhere */
300 priv->key_snooper = gtk_key_snooper_install
301 ((GtkKeySnoopFunc) hildon_app_key_snooper, widget);
303 /* Get the list of Atoms for the WM_PROTOCOLS property... */
304 XGetWMProtocols(disp, GDK_WINDOW_XID(window), &old_atoms, &atom_count);
305 new_atoms = g_new(Atom, atom_count + 1);
307 memcpy(new_atoms, old_atoms, sizeof(Atom) * atom_count);
309 /* ... creates a new Atom... */
310 new_atoms[atom_count++] =
311 XInternAtom(disp, "_NET_WM_CONTEXT_CUSTOM", False);
313 /* ... and finally update the property within the XServer */
314 XSetWMProtocols(disp, GDK_WINDOW_XID(window), new_atoms, atom_count);
319 /* Add the GDK_SUBSTRUCTURE_MASK (receive events about window configuration
320 * changes of child windows) to the window.
322 gdk_window_set_events(window, gdk_window_get_events(window) | GDK_SUBSTRUCTURE_MASK);
326 * Performs the standard gtk unrealize function.
328 static void hildon_app_unrealize(GtkWidget *widget)
330 HildonAppPrivate *priv = HILDON_APP_GET_PRIVATE(widget);
332 if (priv->key_snooper)
334 /* removing the snooper that handles MENU and HOME key presses */
335 gtk_key_snooper_remove(priv->key_snooper);
336 priv->key_snooper = 0;
339 gdk_window_remove_filter(NULL, hildon_app_event_filter, widget);
340 GTK_WIDGET_CLASS(parent_class)->unrealize(widget);
344 * Class initialisation.
346 static void hildon_app_class_init (HildonAppClass *app_class)
348 /* get convenience variables */
349 GObjectClass *object_class = G_OBJECT_CLASS(app_class);
350 GtkContainerClass *container_class = GTK_CONTAINER_CLASS (app_class);
351 GtkObjectClass *gtkobject_class = GTK_OBJECT_CLASS(app_class);
352 GtkWidgetClass *widget_class = GTK_WIDGET_CLASS(app_class);
354 /* set the global parent_class here */
355 parent_class = g_type_class_peek_parent(app_class);
357 g_type_class_add_private(app_class, sizeof(HildonAppPrivate));
359 /* now the object stuff */
360 object_class->finalize = hildon_app_finalize;
361 object_class->set_property = hildon_app_set_property;
362 object_class->get_property = hildon_app_get_property;
364 gtkobject_class->destroy = hildon_app_destroy;
366 widget_class->key_press_event = hildon_app_key_press;
367 widget_class->key_release_event = hildon_app_key_release;
368 widget_class->button_press_event = hildon_app_button;
369 widget_class->button_release_event = hildon_app_button;
370 widget_class->realize = hildon_app_realize;
371 widget_class->unrealize = hildon_app_unrealize;
373 container_class->add = hildon_app_add;
374 container_class->remove = hildon_app_remove;
375 container_class->forall = hildon_app_forall;
377 app_class->topmost_status_acquire =
378 hildon_app_real_topmost_status_acquire;
379 app_class->topmost_status_lose = hildon_app_real_topmost_status_lose;
380 app_class->switch_to = hildon_app_real_switch_to;
382 /* create the signals */
383 app_signals[TOPMOST_STATUS_ACQUIRE] =
384 g_signal_new("topmost_status_acquire",
385 G_OBJECT_CLASS_TYPE(object_class),
387 G_STRUCT_OFFSET(HildonAppClass,
388 topmost_status_acquire), NULL, NULL,
389 g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0);
391 app_signals[TOPMOST_STATUS_LOSE] =
392 g_signal_new("topmost_status_lose",
393 G_OBJECT_CLASS_TYPE(object_class),
395 G_STRUCT_OFFSET(HildonAppClass, topmost_status_lose),
397 g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0);
399 app_signals[SWITCH_TO] =
400 g_signal_new("switch_to",
401 G_OBJECT_CLASS_TYPE(object_class),
403 G_STRUCT_OFFSET(HildonAppClass, switch_to),
405 g_cclosure_marshal_VOID__POINTER, G_TYPE_NONE, 1,
408 app_signals[IM_CLOSE] =
409 g_signal_new("im_close",
410 G_OBJECT_CLASS_TYPE(object_class),
412 G_STRUCT_OFFSET(HildonAppClass, im_close),
414 g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0);
416 app_signals[CLIPBOARD_COPY] =
417 g_signal_new("clipboard_copy",
418 G_OBJECT_CLASS_TYPE(object_class),
420 G_STRUCT_OFFSET(HildonAppClass, clipboard_copy),
422 g_cclosure_marshal_VOID__POINTER, G_TYPE_NONE, 1,
424 app_signals[CLIPBOARD_CUT] =
425 g_signal_new("clipboard_cut",
426 G_OBJECT_CLASS_TYPE(object_class),
428 G_STRUCT_OFFSET(HildonAppClass, clipboard_cut),
430 g_cclosure_marshal_VOID__POINTER, G_TYPE_NONE, 1,
432 app_signals[CLIPBOARD_PASTE] =
433 g_signal_new("clipboard_paste",
434 G_OBJECT_CLASS_TYPE(object_class),
436 G_STRUCT_OFFSET(HildonAppClass, clipboard_paste),
438 g_cclosure_marshal_VOID__POINTER, G_TYPE_NONE, 1,
442 g_object_class_install_property(object_class, PROP_SCROLL_CONTROL,
443 g_param_spec_boolean("scroll-control",
445 "Set the scroll control ON/OFF",
446 TRUE, G_PARAM_READWRITE));
448 g_object_class_install_property(object_class, PROP_TWO_PART_TITLE,
449 g_param_spec_boolean("two-part-title",
451 "Use two part title or not",
452 FALSE, G_PARAM_READWRITE));
453 #ifndef HILDON_DISABLE_DEPRECATED
454 g_object_class_install_property(object_class, PROP_ZOOM,
455 g_param_spec_enum ("zoom",
457 "Set the zoom level",
458 HILDON_TYPE_ZOOM_LEVEL,
462 g_object_class_install_property(object_class, PROP_APP_TITLE,
463 g_param_spec_string ("app-title",
465 "Set the application title",
469 g_object_class_install_property(object_class, PROP_KILLABLE,
470 g_param_spec_boolean("killable",
472 "Whether the application is killable or not",
475 g_object_class_install_property(object_class, PROP_AUTOREGISTRATION,
476 g_param_spec_boolean("autoregistration",
478 "Whether the application views should be registered automatically",
481 g_object_class_install_property(object_class, PROP_APPVIEW,
482 g_param_spec_object("appview",
484 "The currently active application view",
487 g_object_class_install_property(object_class, PROP_UI_MANAGER,
488 g_param_spec_object("ui-manager",
490 "The associated GtkUIManager for this app",
496 * Performs the standard gtk finalize function, freeing allocated
497 * memory and propagating the finalization to the parent.
500 hildon_app_finalize (GObject *obj)
502 HildonAppPrivate *priv = NULL;
504 g_assert (obj != NULL);
506 priv = HILDON_APP_GET_PRIVATE (obj);
508 g_free (priv->title);
510 if (G_OBJECT_CLASS(parent_class)->finalize)
511 G_OBJECT_CLASS(parent_class)->finalize(obj);
513 /* FIXME: This is legacy code, but cannot be removed
514 without changing functionality */
519 * Removes the long escape ("cancel" hw key) press timeout.
522 hildon_app_remove_timeout(HildonAppPrivate *priv)
524 g_assert(priv != NULL);
526 if (priv->escape_timeout > 0)
528 g_source_remove (priv->escape_timeout);
529 priv->escape_timeout = 0;
534 * Frees all the resources and propagates the destroy call to the parent.
537 hildon_app_destroy (GtkObject *obj)
539 HildonAppPrivate *priv = NULL;
541 g_assert (obj != NULL);
543 priv = HILDON_APP_GET_PRIVATE (obj);
545 /* Just in case a GDK_Escape key was pressed shortly before the propagation
546 * of this event, it is safer to remove the timeout that will generate a
547 * GDK_DELETE key press. We are destroying the app, so we're not interested
548 * anymore in processing key presses.
550 hildon_app_remove_timeout(priv);
552 if (priv->uim != NULL)
554 g_object_unref (G_OBJECT (priv->uim));
558 /* Free all the views */
561 g_slist_foreach (priv->view_ids, (GFunc)g_free, NULL);
562 g_slist_free (priv->view_ids);
563 priv->view_ids = NULL;
566 if (GTK_OBJECT_CLASS (parent_class)->destroy)
567 GTK_OBJECT_CLASS (parent_class)->destroy(obj);
571 * Overrides gtk_container_forall, calling the callback function for each of
572 * the children of HildonAppPrivate.
574 static void hildon_app_forall (GtkContainer *container, gboolean include_internals,
575 GtkCallback callback, gpointer callback_data)
577 HildonAppPrivate *priv = NULL;
579 g_return_if_fail (container != NULL);
580 g_return_if_fail (callback != NULL);
582 priv = HILDON_APP_GET_PRIVATE (container);
584 /* Note! we only have user added children, no internals */
585 g_list_foreach (priv->children, (GFunc)callback, callback_data);
589 * An accessor to set private properties of HildonAppPrivate.
591 static void hildon_app_set_property(GObject * object, guint property_id,
592 const GValue * value, GParamSpec * pspec)
594 HildonAppPrivate *priv = HILDON_APP_GET_PRIVATE(object);
596 switch (property_id) {
597 case PROP_SCROLL_CONTROL:
598 priv->scroll_control = g_value_get_boolean(value);
600 #ifndef HILDON_DISABLE_DEPRECATED
602 hildon_app_set_zoom( HILDON_APP (object), g_value_get_enum (value) );
605 case PROP_TWO_PART_TITLE:
606 hildon_app_set_two_part_title( HILDON_APP (object),
607 g_value_get_boolean (value) );
610 hildon_app_set_title( HILDON_APP (object), g_value_get_string (value));
613 hildon_app_set_killable( HILDON_APP (object),
614 g_value_get_boolean (value));
616 case PROP_AUTOREGISTRATION:
617 hildon_app_set_autoregistration( HILDON_APP (object),
618 g_value_get_boolean (value));
621 hildon_app_set_appview( HILDON_APP (object),
622 HILDON_APPVIEW (g_value_get_object (value)));
624 case PROP_UI_MANAGER:
625 hildon_app_set_ui_manager( HILDON_APP (object),
626 GTK_UI_MANAGER (g_value_get_object (value)));
629 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec);
635 * An accessor to get private properties of HildonAppPrivate.
637 static void hildon_app_get_property(GObject * object, guint property_id,
638 GValue * value, GParamSpec * pspec)
640 HildonAppPrivate *priv = HILDON_APP_GET_PRIVATE(object);
642 switch (property_id) {
643 case PROP_SCROLL_CONTROL:
644 g_value_set_boolean( value, priv->scroll_control );
646 #ifndef HILDON_DISABLE_DEPRECATED
648 g_value_set_enum( value, priv->zoom);
651 case PROP_TWO_PART_TITLE:
652 g_value_set_boolean( value, priv->twoparttitle);
655 g_value_set_string (value, priv->title);
658 g_value_set_boolean (value, priv->killable);
660 case PROP_AUTOREGISTRATION:
661 g_value_set_boolean (value, priv->autoregistration);
664 g_value_set_object (value, hildon_app_get_appview (HILDON_APP (object)));
666 case PROP_UI_MANAGER:
667 g_value_set_object (value, hildon_app_get_ui_manager (HILDON_APP (object)));
670 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec);
676 * Adds a child widget to HildonApp.
678 static void hildon_app_add (GtkContainer *container, GtkWidget *child)
680 HildonApp *app = NULL;
681 HildonAppPrivate *priv = NULL;
683 g_return_if_fail (container != NULL);
684 g_return_if_fail (GTK_IS_WIDGET (child));
686 app = HILDON_APP (container);
687 priv = HILDON_APP_GET_PRIVATE (app);
689 /* Check if child is already added here */
690 if (g_list_find (priv->children, child) != NULL)
693 priv->children = g_list_append (priv->children, child);
694 GTK_BIN (container)->child = child;
695 gtk_widget_set_parent (child, GTK_WIDGET (app));
697 /* If the default direction (RTL/LTR) is different from the real
698 default, change it This happens if the locale has been changed
699 but this appview was orphaned and thus never got to know about
700 it. "default_direction" could be RTL, but the widget direction
701 of the view might still be LTR. Thats what we're fixing here. */
703 /* FIXME: This is legacy stuff */
704 if (gtk_widget_get_default_direction () !=
705 gtk_widget_get_direction (GTK_WIDGET (child)))
707 gtk_widget_set_direction (GTK_WIDGET (child),
708 gtk_widget_get_default_direction ());
711 if (HILDON_IS_APPVIEW (child))
713 g_signal_connect_swapped (G_OBJECT (child), "title_change",
714 G_CALLBACK (hildon_app_construct_title), app);
715 if (priv->autoregistration)
716 hildon_app_register_view (app, child);
721 * Removes a child widget from HildonApp.
723 static void hildon_app_remove (GtkContainer *container, GtkWidget *child)
725 HildonAppPrivate *priv;
729 g_return_if_fail (container != NULL);
730 g_return_if_fail (GTK_IS_WIDGET (child));
732 priv = HILDON_APP_GET_PRIVATE (container);
733 bin = GTK_BIN (container);
734 app = HILDON_APP (bin);
736 /* Make sure that child is found in the list */
737 if (g_list_find (priv->children, child) == NULL)
740 priv->children = g_list_remove (priv->children, child);
742 if (HILDON_IS_APPVIEW (child))
744 /* FIXME: This is a compilation workaround for gcc > 3.3, since glib-2 API is buggy.
745 * see http://bugzilla.gnome.org/show_bug.cgi?id=310175 */
749 g_signal_handlers_disconnect_by_func (G_OBJECT (child),
750 (gpointer)hildon_app_construct_title, app);
752 if (priv->autoregistration)
753 hildon_app_unregister_view (app, HILDON_APPVIEW (child));
756 /* If that was our visible child, we need to recalculate size.
757 We could chain up to parent as well... */
758 gtk_widget_unparent (child);
760 if (bin->child == child)
763 gtk_widget_queue_resize (GTK_WIDGET (bin));
769 * Long escape keypress handler.
770 * Long press of the escape key means "close this window", so we fake a delete-event
771 * for our GdkWindow to make it act the same as if the user had closed the window the
772 * usual way. This allows any application code to gracefully exit.
774 * It returns FALSE in order to get called only once.
777 hildon_app_escape_timeout(gpointer app)
779 HildonAppPrivate *priv;
782 g_assert(GTK_WIDGET_REALIZED(app));
784 priv = HILDON_APP_GET_PRIVATE(app);
786 /* Send fake event, simulation a situation that user
787 pressed 'x' from the corner */
788 event = gdk_event_new(GDK_DELETE);
789 ((GdkEventAny *)event)->window = GTK_WIDGET(app)->window;
790 gtk_main_do_event(event);
791 gdk_event_free(event);
793 priv->escape_timeout = 0;
799 * Looks for the visible window to whom the point of coordinates (co,by)
800 * belongs. Search recursively all the children of the window.
802 * This functionality is only needed for scrollbar remote control.
804 static GdkWindow *find_window (GdkWindow *window, gint by, gint co)
807 GList *children = gdk_window_peek_children (window);
809 /* If the window has no children, then the coordinates must match it */
813 if (!(child = (GdkWindow *)children->data))
818 /* It makes sense to process a child window only if it's visible and it's
819 * capable to get the GDK_BUTTON_PRESS_MASK event. */
820 if (gdk_window_is_visible (child) &&
821 gdk_window_get_events (child) & GDK_BUTTON_PRESS_MASK)
823 gint x, width, y, height;
825 gdk_window_get_geometry (child, &x, &y, &width, &height, NULL);
826 /* This checks that the the point of coordinates (co,by) is in the rectangle
827 * made by (x,y,x+width,y+height). If so, then we spotted which child of the
828 * original window is in the point (co,by). We can now recursively search
831 if (x < co && x + width > co && y < by && y + height > by)
832 return find_window (child, by, co);
835 /* If the window has no more children, then it's the one we're looking for */
836 if (!(children = g_list_next (children)))
839 } while ( (child = children->data));
845 * This is the callback function that gets called on a mouse button press
846 * event. If the press is happens in a "sensitive border" area in the right side of
847 * the window, the event is translated to the left edge of that border (which
848 * usually will contain a scrollbar).
850 * This functionality is used for right-hand side scroll control feature (dragging on the
851 * right edge of the window controls the rightmost scrollbar if present).
854 hildon_app_button (GtkWidget *widget, GdkEventButton *event)
856 HildonAppPrivate *priv = NULL;
858 /* FIXME: This is an estimate, but the AppView does not expose the
859 width of it's borders so we default to something */
860 gint sensitive_border = 31;
862 if (!GTK_WIDGET_REALIZED(widget))
867 priv = HILDON_APP_GET_PRIVATE (widget);
869 if (!priv->scroll_control)
874 /* We can easily get the location of the vertical scrollbar and get the exact
875 * area for the scroll_control *if* the setup is such that the HildonAppview
876 * contains a GtkScrolledWindow, so we check for it before defaulting to
877 * the previous guess. More complex situations are not feasible to autodetect.
878 * Applications should provide the GtkAdjustment to be changed for this to work
881 if (HILDON_IS_APPVIEW(GTK_BIN(widget)->child))
883 GtkBin *avbin = GTK_BIN(GTK_BIN(widget)->child);
884 if (GTK_IS_SCROLLED_WINDOW(avbin->child))
886 GtkScrolledWindow *win;
887 win = GTK_SCROLLED_WINDOW(avbin->child);
889 if (GTK_WIDGET_VISIBLE(win->vscrollbar))
891 /* Calculate the distance between the AppView's right border and
892 * the scrollbars center
894 sensitive_border = (GTK_WIDGET(avbin)->allocation.x +
895 GTK_WIDGET(avbin)->allocation.width) -
896 (win->vscrollbar->allocation.x +
897 win->vscrollbar->allocation.width / 2);
902 /* If the press event comes to the sensitive area, we send a fake event to the
903 * area we think the scrollbar is in to make it think the button press happened on it
905 if (event->x > widget->allocation.width - sensitive_border)
907 GdkWindow *window = NULL;
908 gint co = widget->allocation.width - sensitive_border;
910 /* We now need to know in which window the _modified_ coordinates are */
911 if ((window = find_window (widget->window, event->y, co)))
913 GdkEventButton nevent;
915 if (window == widget->window)
918 /* Build a new event and associate the proper window to it */
921 nevent.window = window;
922 g_object_ref (nevent.window);
923 gtk_main_do_event ((GdkEvent*)&nevent);
930 * Performs the initialisation of the widget.
933 hildon_app_init (HildonApp *self)
935 HildonAppPrivate *priv;
937 priv = HILDON_APP_GET_PRIVATE(self);
940 priv->title = g_strdup("");
941 #ifndef HILDON_DISABLE_DEPRECATED
942 priv->zoom = HILDON_ZOOM_MEDIUM;
944 priv->twoparttitle = FALSE;
945 priv->lastmenuclick = 0;
946 priv->is_topmost = FALSE;
947 priv->curr_view_id = 0;
948 priv->view_id_counter = 1;
949 priv->view_ids = NULL;
950 priv->killable = FALSE;
951 priv->autoregistration = TRUE;
952 priv->scroll_control = TRUE;
954 priv->active_menu_id = 0;
956 /* grab the events here since HildonApp isn't necessarily ever shown */
957 gdk_window_set_events(gdk_get_default_root_window(),
958 gdk_window_get_events(gdk_get_default_root_window()) |
959 GDK_PROPERTY_CHANGE_MASK);
961 /* For some reason, the titlebar menu has problems with the grab
962 (bugzilla bug 1527). This is part of somewhat ugly fix for it to
963 get it to work until a more satisfactory solution is found */
964 gdk_window_add_filter(NULL, hildon_app_event_filter, self);
966 gtk_widget_set_events (GTK_WIDGET(self), GDK_BUTTON_PRESS_MASK |
967 GDK_BUTTON_RELEASE_MASK |
968 GDK_POINTER_MOTION_MASK);
971 /*public functions */
976 * Creates a new #HildonApp
978 * Return value: Pointer to a new @HildonApp structure.
981 hildon_app_new (void)
983 return GTK_WIDGET(g_object_new(HILDON_TYPE_APP, NULL));
987 * hildon_app_new_with_appview:
988 * @appview : A @HildonAppView
990 * Creates an app, and sets it's initial appview.
992 * Return value: Pointer to a new @HildonApp structure.
995 hildon_app_new_with_appview (HildonAppView *appview)
999 g_return_val_if_fail (HILDON_IS_APPVIEW (appview), NULL);
1001 app = hildon_app_new ();
1003 hildon_app_set_appview(HILDON_APP(app), appview);
1009 * hildon_app_get_appview:
1010 * @self : A @HildonApp
1012 * Gets the currently shown appview.
1014 * Return value: The currently shown appview in this HildonApp.
1015 * If no appview is currently set for this HildonApp,
1019 hildon_app_get_appview (HildonApp *self)
1023 g_return_val_if_fail (HILDON_IS_APP (self), NULL);
1024 bin = GTK_BIN (self);
1025 if (HILDON_IS_APPVIEW (bin->child))
1027 return HILDON_APPVIEW (bin->child);
1034 * hildon_app_set_appview:
1035 * @self : A @HildonApp
1036 * @appview : A @HildonAppView.
1038 * Sets (switches to) appview.
1041 hildon_app_set_appview (HildonApp *app, HildonAppView *view)
1043 HildonAppPrivate *priv;
1045 GtkWidget *widget; /*(view to be set)*/
1048 g_return_if_fail (HILDON_IS_APP (app));
1049 g_return_if_fail (HILDON_IS_APPVIEW (view));
1051 bin = GTK_BIN (app);
1052 priv = HILDON_APP_GET_PRIVATE (app);
1053 widget = GTK_WIDGET (view);
1055 if (widget == bin->child)
1058 /* Make old appview dissapear */
1061 gtk_widget_hide (bin->child);
1062 g_signal_emit_by_name (bin->child, "switched_from", NULL);
1064 if (priv->active_menu_id > 0)
1066 if (priv->uim != NULL)
1068 gtk_ui_manager_remove_ui (priv->uim,
1069 priv->active_menu_id);
1071 priv->active_menu_id = 0;
1077 /* Ensure that new view is in our child list */
1078 if (!g_list_find (priv->children, widget))
1079 gtk_container_add (GTK_CONTAINER (app), widget);
1081 bin->child = widget;
1083 gtk_widget_show (widget);
1085 /* UI manager support, merge menu for activated view */
1086 g_object_get (G_OBJECT (view),
1087 "menu-ui", &menu_ui,
1090 if (menu_ui && priv->uim)
1093 priv->active_menu_id =
1094 gtk_ui_manager_add_ui_from_string (priv->uim, menu_ui, -1, NULL);
1096 gtk_ui_manager_ensure_update (priv->uim);
1102 g_signal_emit_by_name (widget, "switched_to", NULL);
1104 /* Inform task navigator about changed view */
1105 hildon_app_notify_view_changed (app, view);
1107 /* Update title to show currently activated view */
1108 hildon_app_construct_title (app);
1109 gtk_widget_child_focus (widget, GTK_DIR_TAB_FORWARD);
1113 * hildon_app_set_title:
1114 * @self : A @HildonApp
1115 * @newtitle : The new title assigned to the application.
1117 * Sets title of the application.
1120 hildon_app_set_title (HildonApp *self, const gchar *newtitle)
1122 HildonAppPrivate *priv;
1125 g_return_if_fail(HILDON_IS_APP(self));
1127 priv = HILDON_APP_GET_PRIVATE(self);
1128 oldstr = priv->title;
1132 priv->title = g_strdup(newtitle);
1133 g_strstrip(priv->title);
1136 priv->title = g_strdup("");
1141 hildon_app_construct_title(self);
1145 * hildon_app_get_title:
1146 * @self : A @HildonApp
1148 * Gets the title of the application.
1150 * Return value: The title currently assigned to the application. This
1151 * value is not to be freed or modified by the calling application.
1154 hildon_app_get_title (HildonApp *self)
1156 HildonAppPrivate *priv;
1158 g_return_val_if_fail (HILDON_IS_APP(self), NULL);
1159 priv = HILDON_APP_GET_PRIVATE(self);
1163 /* FIXME: Zoom is deprecated, remove */
1165 * hildon_app_set_zoom:
1166 * @self : A @HildonApp
1167 * @newzoom: The zoom level of type @HildonZoomLevel to be assigned to an
1170 * Sets the zoom level. Warning! This function is deprecated and
1171 * should not be used. It's lecacy stuff from ancient specs.
1174 hildon_app_set_zoom (HildonApp *self, HildonZoomLevel newzoom)
1176 HildonAppPrivate *priv;
1178 g_return_if_fail(HILDON_IS_APP(self));
1180 priv = HILDON_APP_GET_PRIVATE(self);
1182 if (newzoom != priv->zoom)
1184 if (newzoom < HILDON_ZOOM_SMALL)
1186 newzoom = HILDON_ZOOM_SMALL;
1187 gtk_infoprint(GTK_WINDOW(self), _("ckct_ib_min_zoom_level_reached"));
1189 else if (newzoom > HILDON_ZOOM_LARGE) {
1190 newzoom = HILDON_ZOOM_LARGE;
1191 gtk_infoprint(GTK_WINDOW(self), _("ckct_ib_max_zoom_level_reached"));
1193 priv->zoom = newzoom;
1198 * hildon_app_get_zoom:
1199 * @self : A @HildonApp
1201 * Gets the zoom level. Warning! This function is deprecated and
1202 * should not be used. It's lecacy stuff from ancient specifications.
1204 * Return value: Returns the zoom level of the Hildon application. The
1205 * returned zoom level is of type @HildonZoomLevel.
1208 hildon_app_get_zoom (HildonApp *self)
1210 HildonAppPrivate *priv;
1212 g_return_val_if_fail(HILDON_IS_APP(self), HILDON_ZOOM_MEDIUM);
1213 priv = HILDON_APP_GET_PRIVATE(self);
1218 * hildon_app_get_default_font:
1219 * @self : A @HildonApp
1221 * Gets default font. Warning! This function is deprecated and should
1222 * not be used. It's legacy stuff from ancient version of specification.
1224 * Return value: Pointer to PangoFontDescription for the default,
1227 PangoFontDescription *
1228 hildon_app_get_default_font (HildonApp *self)
1230 PangoFontDescription *font_desc = NULL;
1231 GtkStyle *fontstyle = NULL;
1233 g_return_val_if_fail(HILDON_IS_APP(self), NULL);
1236 gtk_rc_get_style_by_paths (gtk_widget_get_settings
1237 (GTK_WIDGET(self)), NULL, NULL,
1238 gtk_widget_get_type());
1242 g_print("WARNING : default font not found. "
1243 "Defaulting to swissa 19\n");
1244 font_desc = pango_font_description_from_string("swissa 19");
1248 font_desc = pango_font_description_copy(fontstyle->font_desc);
1254 * hildon_app_get_zoom_font:
1255 * @self : A @HildonApp
1257 * Gets the description of the default font. Warning! This function
1258 * is deprecated and should not be used. It's legacy stuff from
1261 * Return value: Pointer to PangoFontDescription for the default,
1264 PangoFontDescription *
1265 hildon_app_get_zoom_font (HildonApp *self)
1267 HildonAppPrivate *priv;
1268 PangoFontDescription *font_desc = NULL;
1269 gchar *style_name = 0;
1270 GtkStyle *fontstyle = NULL;
1272 g_return_val_if_fail(HILDON_IS_APP(self), NULL);
1274 priv = HILDON_APP_GET_PRIVATE(self);
1275 if (priv->zoom == HILDON_ZOOM_SMALL)
1276 style_name = g_strdup("hildon-zoom-small");
1277 else if (priv->zoom == HILDON_ZOOM_MEDIUM)
1278 style_name = g_strdup("hildon-zoom-medium");
1279 else if (priv->zoom == HILDON_ZOOM_LARGE)
1280 style_name = g_strdup("hildon-zoom-large");
1283 g_warning("Invalid Zoom Value\n");
1284 style_name = g_strdup("");
1288 gtk_rc_get_style_by_paths (gtk_widget_get_settings
1289 (GTK_WIDGET(self)), style_name, NULL,
1291 g_free (style_name);
1295 g_print("WARNING : theme specific zoomed font not found. "
1296 "Defaulting to preset zoom-specific fonts\n");
1297 if (priv->zoom == HILDON_ZOOM_SMALL)
1298 font_desc = pango_font_description_from_string("swissa 16");
1299 else if (priv->zoom == HILDON_ZOOM_MEDIUM)
1300 font_desc = pango_font_description_from_string("swissa 19");
1301 else if (priv->zoom == HILDON_ZOOM_LARGE)
1302 font_desc = pango_font_description_from_string("swissa 23");
1305 font_desc = pango_font_description_copy(fontstyle->font_desc);
1310 /* FIXME: Zoom is deprecated, remove the code above */
1313 * hildon_app_set_two_part_title:
1314 * @self : A @HildonApp
1315 * @istwoparttitle : A gboolean indicating wheter to activate
1316 * a title that has both the application title and application
1317 * view title separated by a triangle.
1319 * Sets the two part title.
1322 hildon_app_set_two_part_title (HildonApp *self, gboolean istwoparttitle)
1324 HildonAppPrivate *priv;
1325 g_return_if_fail(HILDON_IS_APP(self));
1326 priv = HILDON_APP_GET_PRIVATE(self);
1328 if (istwoparttitle != priv->twoparttitle)
1330 priv->twoparttitle = istwoparttitle;
1331 hildon_app_construct_title(self);
1336 * hildon_app_get_two_part_title:
1337 * @self : A @HildonApp
1339 * Gets the 'twopart' represention of the title inside #HildonApp.
1341 * Return value: A boolean indicating wheter title shown has both
1342 * application, and application view title separated by a triangle.
1345 hildon_app_get_two_part_title (HildonApp *self)
1347 HildonAppPrivate *priv;
1349 g_return_val_if_fail(HILDON_IS_APP(self), FALSE);
1350 priv = HILDON_APP_GET_PRIVATE(self);
1351 return priv->twoparttitle;
1355 /* private functions */
1359 * Generates and sends an event of type _NET_SHOWING_DESKTOP to the XServer,
1360 * which will be then caught by the window Manager.
1363 hildon_app_switch_to_desktop (void)
1367 Atom showing_desktop = XInternAtom(GDK_DISPLAY(),
1368 "_NET_SHOWING_DESKTOP", False);
1369 memset(&ev, 0, sizeof(ev));
1370 ev.xclient.type = ClientMessage;
1371 gdk_error_trap_push();
1372 ev.xclient.window = GDK_ROOT_WINDOW();
1373 ev.xclient.message_type = showing_desktop;
1374 ev.xclient.format = 32;
1375 ev.xclient.data.l[0] = 1;
1376 gdk_error_trap_push();
1377 XSendEvent(GDK_DISPLAY(), GDK_ROOT_WINDOW(), False,
1378 SubstructureRedirectMask, &ev);
1379 gdk_error_trap_pop();
1383 * Handles the key press of the Escape, Increase and Decrease keys. Other keys
1384 * are handled by the parent GtkWidgetClass.
1387 hildon_app_key_press (GtkWidget *widget, GdkEventKey *keyevent)
1389 HildonApp *app = HILDON_APP (widget);
1390 HildonAppView *appview;
1391 HildonAppPrivate *priv = HILDON_APP_GET_PRIVATE(app);
1393 if (HILDON_IS_APPVIEW(GTK_BIN (app)->child))
1395 appview = HILDON_APPVIEW (GTK_BIN (app)->child);
1402 if (keyevent->keyval == GDK_Escape && priv->escape_timeout == 0)
1404 /* Call hildon_app_escape_timeout every 1500ms until it returns FALSE
1405 * and store the relative GSource id. Since hildon_app_escape_timeout
1406 * can only return FALSE, the call will occurr only once.
1408 priv->escape_timeout = g_timeout_add(1500, hildon_app_escape_timeout, app);
1411 /* FIXME: Handling +/- keys here is not usefull. Applications
1412 can equally easily handle the keypress themselves. */
1413 else if (HILDON_KEYEVENT_IS_INCREASE_KEY (keyevent))
1415 _hildon_appview_increase_button_state_changed (appview,
1418 else if (HILDON_KEYEVENT_IS_DECREASE_KEY (keyevent))
1420 _hildon_appview_decrease_button_state_changed (appview,
1424 /* Activate default bindings and let the widget with focus to handle key */
1425 return GTK_WIDGET_CLASS (parent_class)->key_press_event (widget, keyevent);
1429 * Handles the key release event for the Escape, Toolbar and Fullscreen keys.
1432 hildon_app_key_release (GtkWidget *widget, GdkEventKey *keyevent)
1434 HildonApp *app = HILDON_APP (widget);
1435 HildonAppView *appview;
1436 HildonAppPrivate *priv = HILDON_APP_GET_PRIVATE(app);
1438 if (HILDON_IS_APPVIEW(GTK_BIN (app)->child))
1440 appview = HILDON_APPVIEW (GTK_BIN (app)->child);
1447 if (keyevent->keyval == GDK_Escape)
1450 * This will prevent the hildon_app_escape_timeout from being called.
1451 * See hildon_app_escape_timeout and hildon_app_remove_timeout for more.
1453 hildon_app_remove_timeout(priv);
1455 else if (HILDON_KEYEVENT_IS_TOOLBAR_KEY (keyevent))
1457 g_signal_emit_by_name(G_OBJECT(appview),
1458 "toolbar-toggle-request");
1460 else if (HILDON_KEYEVENT_IS_FULLSCREEN_KEY (keyevent))
1462 /* Emit the fullscreen_state_change directly, it'll save one step */
1463 if (hildon_appview_get_fullscreen_key_allowed (appview))
1465 gboolean fullscreen;
1467 fullscreen = hildon_appview_get_fullscreen(appview);
1468 g_signal_emit_by_name(G_OBJECT(appview),
1469 "fullscreen_state_change",
1474 /* FIXME: Should the event be marked as handled if any of the three
1475 above cases took an action */
1477 /* Activate default bindings and let the widget with focus to handle key */
1478 return GTK_WIDGET_CLASS (parent_class)->key_release_event (widget, keyevent);
1482 * Handles the MENU and HOME key presses.
1485 hildon_app_key_snooper (GtkWidget *widget, GdkEventKey *keyevent, HildonApp *app)
1487 /* FIXME: Using normal keypress handler would be better choise. All
1488 keyevents come to window anyway, so we would get the same
1489 keys in that way as well, but we wouldn't need to struggle
1490 with grabs (modal dialogs etc). */
1492 /* Menu key handling is done here */
1493 if ( HILDON_KEYEVENT_IS_MENU_KEY (keyevent) ) {
1494 HildonAppView *appview;
1495 HildonAppPrivate *priv;
1496 GtkWidget *toplevel;
1498 /* Don't act on modal dialogs */
1499 toplevel = gtk_widget_get_toplevel (widget);
1500 if (GTK_IS_DIALOG (toplevel)
1501 && gtk_window_get_modal (GTK_WINDOW (toplevel)))
1506 appview = HILDON_APPVIEW (GTK_BIN(app)->child);
1507 priv = HILDON_APP_GET_PRIVATE(app);
1509 if ( keyevent->type == GDK_KEY_PRESS ) {
1510 /* Toggle menu on press, avoid key repeat */
1511 if ( priv->lastmenuclick == 0 ){
1512 _hildon_appview_toggle_menu(appview,
1513 gtk_get_current_event_time());
1515 priv->lastmenuclick = 1;
1517 } else if ( keyevent->type == GDK_KEY_RELEASE ) {
1518 /* We got release, so next press is really a new press,
1520 if ( priv->lastmenuclick == 1 ) {
1521 priv->lastmenuclick = 0;
1525 /* Unknown key event */
1529 /* don't stop the key event so that it reaches GTK where it
1530 closes all existing menus that might be open */
1534 /* Home key handling is done here. */
1535 if ((keyevent->type == GDK_KEY_RELEASE) &&
1536 HILDON_KEYEVENT_IS_HOME_KEY (keyevent))
1538 hildon_app_switch_to_desktop();
1547 * Returns the message_type of the Atom registered with a certain name.
1550 xclient_message_type_check(XClientMessageEvent *cm, const gchar *name)
1552 return cm->message_type == XInternAtom(GDK_DISPLAY(), name, FALSE);
1556 * Returns the GtkWidget associated to a certain Window.
1559 hildon_app_xwindow_lookup_widget(Window xwindow)
1564 window = gdk_xid_table_lookup(xwindow);
1568 gdk_window_get_user_data(window, &widget);
1573 * Let's search a actual main window using tranciency hints.
1574 * Note that there can be several levels of menus/dialogs above
1575 * the actual main window.
1577 static Window get_active_main_window(Window window)
1579 Window parent_window;
1582 gdk_error_trap_push ();
1584 while (XGetTransientForHint(GDK_DISPLAY(), window, &parent_window))
1586 /* The limit > TRANSIENCY_MAXITER ensures that we can't be stuck
1587 here forever if we have circular transiencies for some reason.
1588 Use of _MB_CURRENT_APP_WINDOW might be more elegant... */
1590 if (!parent_window || parent_window == GDK_ROOT_WINDOW() ||
1591 parent_window == window || limit > TRANSIENCY_MAXITER)
1597 window = parent_window;
1602 if (gdk_error_trap_pop ())
1609 * Filters every GDK event first.
1611 static GdkFilterReturn
1612 hildon_app_event_filter (GdkXEvent *xevent, GdkEvent *event, gpointer data)
1615 HildonApp *app = data;
1616 HildonAppPrivate *priv;
1617 HildonAppView *appview = NULL;
1619 XAnyEvent *eventti = xevent;
1621 if (HILDON_IS_APPVIEW (GTK_BIN (app)->child))
1623 appview = HILDON_APPVIEW (GTK_BIN (app)->child);
1626 g_return_val_if_fail (app, GDK_FILTER_CONTINUE);
1627 g_return_val_if_fail (HILDON_IS_APP(app), GDK_FILTER_CONTINUE);
1629 priv = HILDON_APP_GET_PRIVATE(app);
1630 if (eventti->type == ClientMessage)
1632 XClientMessageEvent *cm = xevent;
1634 /* Check if a message indicating a click on titlebar has been
1635 received. Don't open it if mouse is grabbed (eg. modal dialog
1637 _MB_GRAB_TRANSFER is emitted by MatchBox, and signals that a button
1638 has just been released. */
1639 if (xclient_message_type_check(cm, "_MB_GRAB_TRANSFER") &&
1640 HILDON_IS_APPVIEW(appview) &&
1641 gtk_grab_get_current() == NULL &&
1642 !_hildon_appview_menu_visible(appview))
1644 _hildon_appview_toggle_menu(appview, cm->data.l[0]);
1645 return GDK_FILTER_REMOVE;
1647 /* IM_CLOSE is input method specific hack that is really questionable */
1648 else if (xclient_message_type_check(cm, "_HILDON_IM_CLOSE"))
1650 g_signal_emit_by_name(app, "im_close", NULL);
1651 return GDK_FILTER_REMOVE;
1653 /* Task user changed the view through task navigator? */
1654 else if (xclient_message_type_check(cm, "_NET_ACTIVE_WINDOW"))
1656 unsigned long view_id = cm->window;
1657 gpointer view_ptr = find_view(app, view_id);
1659 /* When getting a _NET_ACTIVE_WINDOW signal from the WM we need
1660 * to bring the application to the front */
1661 if (!priv->is_topmost)
1662 g_signal_emit_by_name (G_OBJECT(app), "topmost_status_acquire");
1664 if (HILDON_IS_APPVIEW(view_ptr))
1665 /* Sets the current view to the "window" that the _NET_ACTIVE_WINDOW
1667 hildon_app_set_appview(app, (HILDON_APPVIEW(view_ptr)));
1669 /* there was no view, so we have to switch to an actual application */
1670 g_signal_emit_by_name (G_OBJECT(app), "switch_to", view_ptr);
1672 /* FIXME: This is a hack. This was once just gtk_window_present, but
1673 was changed into this at some day!! */
1674 if (GTK_WIDGET(app)->window)
1676 mb_util_window_activate(GDK_DISPLAY(),
1677 GDK_WINDOW_XID(GTK_WIDGET(app)->window));
1680 /* FIXME: IM hack */
1681 else if (xclient_message_type_check(cm, "_HILDON_IM_CLIPBOARD_COPY"))
1683 Window xwindow = cm->data.l[0];
1684 GtkWidget *widget = hildon_app_xwindow_lookup_widget(xwindow);
1686 g_signal_emit_by_name (G_OBJECT(app), "clipboard_copy", widget);
1688 /* FIXME: IM hack */
1689 else if (xclient_message_type_check(cm, "_HILDON_IM_CLIPBOARD_CUT"))
1691 Window xwindow = cm->data.l[0];
1692 GtkWidget *widget = hildon_app_xwindow_lookup_widget(xwindow);
1694 g_signal_emit_by_name (G_OBJECT(app), "clipboard_cut", widget);
1696 /* FIXME: IM hack */
1697 else if (xclient_message_type_check(cm, "_HILDON_IM_CLIPBOARD_PASTE"))
1699 Window xwindow = cm->data.l[0];
1700 GtkWidget *widget = hildon_app_xwindow_lookup_widget(xwindow);
1702 g_signal_emit_by_name (G_OBJECT(app), "clipboard_paste", widget);
1706 if (eventti->type == ButtonPress)
1709 /* FIXME: This is mysterious bugfix related to problems to open the
1710 application menu (bugzilla N#3204) */
1711 XButtonEvent *bev = (XButtonEvent *)xevent;
1713 if (HILDON_IS_APPVIEW(appview) &&
1714 _hildon_appview_menu_visible(appview) &&
1715 !hildon_appview_get_fullscreen(appview))
1719 if ( (x >= MENUAREA_LEFT_LIMIT) && (x <= MENUAREA_RIGHT_LIMIT) &&
1720 (y >= MENUAREA_TOP_LIMIT) && (y <= MENUAREA_BOTTOM_LIMIT))
1722 _hildon_appview_toggle_menu(appview, bev->time);
1723 return GDK_FILTER_CONTINUE;
1728 /* FIXME: as above */
1729 if (eventti->type == ButtonRelease)
1731 if (HILDON_IS_APPVIEW(appview) &&
1732 _hildon_appview_menu_visible(appview) &&
1733 !hildon_appview_get_fullscreen(appview))
1735 XButtonEvent *bev = (XButtonEvent *)xevent;
1738 if ( (x >= MENUAREA_LEFT_LIMIT) && (x < MENUAREA_RIGHT_LIMIT) &&
1739 (y >= MENUAREA_TOP_LIMIT) && (y <= MENUAREA_BOTTOM_LIMIT))
1741 return GDK_FILTER_REMOVE;
1744 return GDK_FILTER_CONTINUE;
1747 /* Application stacking order changed */
1748 if (eventti->type == PropertyNotify)
1750 Atom active_app_atom =
1751 XInternAtom (GDK_DISPLAY(), "_MB_CURRENT_APP_WINDOW", False);
1752 XPropertyEvent *prop = xevent;
1754 if ((prop->atom == active_app_atom)
1755 && (prop->window == GDK_ROOT_WINDOW()))
1761 unsigned long extra;
1766 unsigned char *char_pointer;
1771 status = XGetWindowProperty(GDK_DISPLAY(), GDK_ROOT_WINDOW(),
1772 active_app_atom, 0L, 16L,
1773 0, XA_WINDOW, &realtype, &format,
1774 &n, &extra, &win.char_pointer);
1775 if (!(status == Success && realtype == XA_WINDOW && format == 32
1776 && n == 1 && win.win != NULL))
1778 if (win.win != NULL)
1779 XFree(win.char_pointer);
1780 return GDK_FILTER_CONTINUE;
1783 my_window = GDK_WINDOW_XID(GTK_WIDGET(app)->window);
1785 /* Are we the topmost one? */
1786 if (win.win[0] == my_window ||
1787 get_active_main_window(win.win[0]) == my_window)
1789 if (!priv->is_topmost)
1790 g_signal_emit_by_name (G_OBJECT(app),
1791 "topmost_status_acquire");
1793 else if (priv->is_topmost)
1795 GtkWidget *focus = gtk_window_get_focus(GTK_WINDOW(app));
1797 /* FIXME: IM hack, IM-module should do this in response to
1798 topmost_status_lose (emission hook?) */
1799 if (GTK_IS_ENTRY(focus))
1800 gtk_im_context_focus_out(GTK_ENTRY(focus)->im_context);
1801 if (GTK_IS_TEXT_VIEW(focus))
1802 gtk_im_context_focus_out(GTK_TEXT_VIEW(focus)->im_context);
1804 g_signal_emit_by_name (app, "topmost_status_lose");
1807 if (win.win != NULL)
1808 XFree(win.char_pointer);
1812 return GDK_FILTER_CONTINUE;
1816 * Sets the GTK Window title to the application's title, or
1817 * combined appview/app title, if two part title is asked.
1820 hildon_app_construct_title (HildonApp *self)
1822 g_return_if_fail (HILDON_IS_APP (self));
1824 if (GTK_WIDGET_REALIZED(self))
1826 HildonAppPrivate *priv;
1828 gchar *concatenated_title = NULL;
1829 HildonAppView *appview;
1831 priv = HILDON_APP_GET_PRIVATE (self);
1832 appview = hildon_app_get_appview(self);
1834 /* FIXME: The subname property is legacy stuff no longer supported by
1835 Matchbox. However, it is still set for the convenience of
1836 the Task Navigator. */
1837 subname = gdk_atom_intern("_MB_WIN_SUB_NAME", FALSE);
1839 if (!appview || !hildon_app_get_two_part_title(self) ||
1840 g_utf8_strlen(hildon_appview_get_title(appview), -1) < 1 )
1842 /* Set an invisible dummy value if there is no appview title */
1843 gdk_property_change (GTK_WIDGET(self)->window, subname,
1844 gdk_atom_intern ("UTF8_STRING", FALSE),
1845 8, GDK_PROP_MODE_REPLACE, (guchar *) " \0", 1);
1846 gtk_window_set_title (GTK_WINDOW(self), priv->title);
1850 gdk_property_change (GTK_WIDGET(self)->window, subname,
1851 gdk_atom_intern ("UTF8_STRING", FALSE),
1852 8, GDK_PROP_MODE_REPLACE,
1853 (guchar *)hildon_appview_get_title(appview),
1854 strlen(hildon_appview_get_title (appview)));
1855 concatenated_title = g_strjoin(TITLE_DELIMITER, priv->title,
1856 hildon_appview_get_title(appview), NULL);
1857 /* priv->title should always be non-null, but check anyway */
1858 if (concatenated_title != NULL)
1860 gtk_window_set_title (GTK_WINDOW(self), concatenated_title);
1861 g_free(concatenated_title);
1868 * Callback function to the topmost_status_acquire signal emitted by
1869 * hildon_app_event_filter function. See it for more details.
1872 hildon_app_real_topmost_status_acquire (HildonApp *self)
1874 HildonAppPrivate *priv;
1875 g_return_if_fail (HILDON_IS_APP (self));
1876 priv = HILDON_APP_GET_PRIVATE (self);
1878 /* FIXME: What is the logic not to update topmost status now? */
1879 if (!GTK_BIN (self)->child)
1882 priv->is_topmost = TRUE;
1886 * Callback function to the topmost_status_lose signal emitted by
1887 * hildon_app_event_filter function. See it for more details.
1890 hildon_app_real_topmost_status_lose (HildonApp *self)
1892 HildonAppPrivate *priv;
1893 g_return_if_fail (HILDON_IS_APP (self));
1894 priv = HILDON_APP_GET_PRIVATE (self);
1896 /* FIXME: What is the logic not to update topmost status now? */
1897 if (!GTK_BIN (self)->child)
1900 priv->is_topmost = FALSE;
1904 hildon_app_real_switch_to (HildonApp *self)
1906 g_return_if_fail (HILDON_IS_APP (self));
1907 /* Do we have to do anything here? */
1912 * hildon_app_set_autoregistration
1913 * @self : a @HildonApp
1914 * @auto_reg : Whether the (app)view autoregistration should be active
1916 * Controls the autoregistration/unregistration of (app)views.
1919 void hildon_app_set_autoregistration(HildonApp *self, gboolean auto_reg)
1921 HildonAppPrivate *priv;
1922 g_return_if_fail (HILDON_IS_APP (self));
1924 priv = HILDON_APP_GET_PRIVATE (self);
1925 priv->autoregistration = auto_reg;
1930 * hildon_app_register_view:
1931 * @self : a @HildonApp
1932 * @view_ptr : Pointer to the view instance to be registered
1934 * Registers a new view. For appviews, this can be done automatically
1935 * if autoregistration is set.
1938 void hildon_app_register_view(HildonApp *self, gpointer view_ptr)
1940 HildonAppPrivate *priv;
1941 view_item *view_item_inst;
1943 g_return_if_fail (HILDON_IS_APP (self) || view_ptr != NULL);
1945 priv = HILDON_APP_GET_PRIVATE (self);
1947 if (hildon_app_find_view_id(self, view_ptr) == 0)
1949 /* The pointer to the view was unique, so add it to the list */
1950 view_item_inst = g_malloc(sizeof(view_item));
1951 view_item_inst->view_id = priv->view_id_counter;
1952 view_item_inst->view_ptr = view_ptr;
1954 priv->view_id_counter++;
1957 g_slist_append(priv->view_ids, view_item_inst);
1959 /* Update the list of views */
1960 if (GTK_WIDGET_REALIZED(self))
1961 hildon_app_apply_client_list(self);
1967 * hildon_app_register_view_with_id:
1968 * @self : a @HildonApp
1969 * @view_ptr : Pointer to the view instance to be registered
1970 * @view_id : The ID of the view
1972 * Registers a new view. Allows the application to specify any ID.
1974 * Return value: TRUE if the view registration succeeded, FALSE otherwise.
1975 * The probable cause of failure is that view with that ID
1979 gboolean hildon_app_register_view_with_id(HildonApp *self,
1981 unsigned long view_id)
1983 view_item *view_item_inst;
1984 HildonAppPrivate *priv;
1985 GSList *list_ptr = NULL;
1987 g_return_val_if_fail (HILDON_IS_APP (self), FALSE);
1988 g_return_val_if_fail (view_ptr, FALSE);
1990 priv = HILDON_APP_GET_PRIVATE (self);
1992 list_ptr = priv->view_ids;
1994 /* Check that the view is not already registered */
1997 if ( (gpointer)((view_item *)list_ptr->data)->view_ptr == view_ptr
1998 && (unsigned long)((view_item *)list_ptr->data)->view_id == view_id)
2002 list_ptr = list_ptr->next;
2005 /* The pointer to the view was unique, so add it to the list */
2006 view_item_inst = g_malloc(sizeof(view_item));
2007 view_item_inst->view_id = view_id;
2008 view_item_inst->view_ptr = view_ptr;
2011 g_slist_append(priv->view_ids, view_item_inst);
2013 priv->view_id_counter++;
2015 /* Finally, update the _NET_CLIENT_LIST property */
2016 if (GTK_WIDGET_REALIZED(self))
2017 hildon_app_apply_client_list(self);
2023 * hildon_app_unregister_view:
2024 * @self : A @HildonApp
2025 * @view_ptr : Pointer to the view instance to be unregistered
2027 * Unregisters a view from HildonApp. Done usually when a view is
2028 * destroyed. For appviews, this is can be automatically
2029 * if autoregistration is set.
2031 void hildon_app_unregister_view(HildonApp *self, gpointer view_ptr)
2033 HildonAppPrivate *priv = NULL;
2034 GSList *list_ptr = NULL;
2036 g_return_if_fail (HILDON_IS_APP (self));
2037 g_return_if_fail (view_ptr != NULL);
2039 priv = HILDON_APP_GET_PRIVATE (self);
2041 /* Search the view from the list */
2042 list_ptr = priv->view_ids;
2046 if ( (gpointer)((view_item *)list_ptr->data)->view_ptr == view_ptr)
2048 /* Found the view, kick it off */
2049 g_free (list_ptr->data);
2050 priv->view_ids = g_slist_delete_link(priv->view_ids, list_ptr);
2053 list_ptr = list_ptr->next;
2056 if (GTK_WIDGET_REALIZED(self))
2057 hildon_app_apply_client_list(self);
2062 * hildon_app_unregister_view_with_id:
2063 * @self : A @HildonApp
2064 * @view_id : The ID of the view that should be unregistered
2066 * Unregisters a view with specified ID, if it exists.
2068 void hildon_app_unregister_view_with_id(HildonApp *self,
2069 unsigned long view_id)
2071 HildonAppPrivate *priv;
2072 GSList *list_ptr = NULL;
2074 g_return_if_fail (HILDON_IS_APP (self));
2076 priv = HILDON_APP_GET_PRIVATE (self);
2078 /* Search the view from the list */
2079 list_ptr = priv->view_ids;
2083 if ( (unsigned long)((view_item *)list_ptr->data)->view_id == view_id)
2085 /* Found view with given id, kick it off */
2086 g_free (list_ptr->data);
2087 priv->view_ids = g_slist_delete_link(priv->view_ids, list_ptr);
2090 list_ptr = list_ptr->next;
2093 /* Update client list to reflect new situation. If we are not
2094 realized, then nobody knows about us anyway... */
2095 if (GTK_WIDGET_REALIZED(self))
2096 hildon_app_apply_client_list(self);
2101 * hildon_app_notify_view_changed:
2102 * @self : A @HildonApp
2103 * @view_ptr : Pointer to the view that is switched to
2105 * Updates the X property that contains the currently active view
2107 void hildon_app_notify_view_changed(HildonApp *self, gpointer view_ptr)
2109 g_return_if_fail (HILDON_IS_APP (self));
2110 g_return_if_fail (view_ptr != NULL);
2112 /* We need GdkWindow before we can send X messages */
2113 if (GTK_WIDGET_REALIZED(self))
2115 gulong id = hildon_app_find_view_id(self, view_ptr);
2116 Atom active_view = XInternAtom (GDK_DISPLAY(),
2117 "_NET_ACTIVE_WINDOW", False);
2120 /* Set _NET_ACTIVE_WINDOW for our own toplevel to contain view id */
2121 XChangeProperty(GDK_DISPLAY(), GDK_WINDOW_XID(GTK_WIDGET(self)->window),
2122 active_view, XA_WINDOW, 32, PropModeReplace,
2123 (unsigned char *)&id, 1);
2124 XFlush(GDK_DISPLAY());
2131 * hildon_app_find_view_id:
2132 * @self : a @HildonApp
2133 * @view_ptr : Pointer to the view whose ID we want to acquire
2135 * Return value: The ID of the view, or 0 if not found.
2137 * Allows mapping of view pointer to its view ID. If NULL is passed
2138 * as the view pointer, returns the ID of the current view.
2140 unsigned long hildon_app_find_view_id(HildonApp *self, gpointer view_ptr)
2142 HildonAppPrivate *priv;
2145 priv = HILDON_APP_GET_PRIVATE (self);
2147 /* If no view is given, find the ID for the currently visible view */
2149 view_ptr = GTK_BIN (self)->child;
2153 /* Iterate through list and search for given view pointer */
2154 for (iter = priv->view_ids; iter; iter = iter->next)
2156 if ( (gpointer)((view_item *)iter->data)->view_ptr == view_ptr)
2157 return (unsigned long)((view_item *)iter->data)->view_id;
2164 * hildon_app_set_killable:
2165 * @self : A @HildonApp
2166 * @killability : Truth value indicating whether the app can be killed
2168 * Updates information about whether the application can be killed or not by
2169 * Task Navigator (i.e. whether its statesave is up to date)
2171 void hildon_app_set_killable(HildonApp *self, gboolean killability)
2173 HildonAppPrivate *priv = HILDON_APP_GET_PRIVATE (self);
2174 g_return_if_fail (HILDON_IS_APP (self) );
2176 if (killability != priv->killable)
2178 priv->killable = killability;
2180 /* If we have a window, then we can actually set this
2181 property. Otherwise we wait until we are realized */
2182 if (GTK_WIDGET_REALIZED(self))
2183 hildon_app_apply_killable(self);
2189 * hildon_app_set_ui_manager:
2190 * @self : #HildonApp
2191 * @uim : #GtkUIManager to be set
2193 * Sets the #GtkUIManager assigned to the #HildonApp.
2194 * If @uim is NULL, unsets the current ui manager.
2195 * The @HildonApp holds a reference to the ui manager until
2196 * the @HildonApp is destroyed or unset.
2198 void hildon_app_set_ui_manager(HildonApp *self, GtkUIManager *uim)
2200 HildonAppPrivate *priv;
2202 g_return_if_fail(self && HILDON_IS_APP(self));
2204 priv = HILDON_APP_GET_PRIVATE (self);
2206 /* Release old ui-manager object if such exists */
2207 if (priv->uim != NULL)
2209 g_object_unref (G_OBJECT (priv->uim));
2214 /* If we got new ui-manager (it's perfectly valid not
2215 to give one), acquire reference to it */
2216 if (priv->uim != NULL)
2218 g_object_ref (G_OBJECT (uim));
2221 g_object_notify (G_OBJECT(self), "ui-manager");
2225 * hildon_app_get_ui_manager:
2226 * @self : #HildonApp
2228 * Gets the #GtkUIManager assigned to the #HildonApp.
2230 * Return value: The #GtkUIManager assigned to this application
2231 * or null if no manager is assigned.
2233 GtkUIManager *hildon_app_get_ui_manager(HildonApp *self)
2235 HildonAppPrivate *priv;
2237 g_return_val_if_fail(self && HILDON_IS_APP(self), NULL);
2239 priv = HILDON_APP_GET_PRIVATE (self);
2245 * Search for a view with the given id within HildonApp.
2246 * Returns a pointer to the found view, or NULL if not found.
2248 static gpointer find_view(HildonApp *self, unsigned long view_id)
2250 HildonAppPrivate *priv;
2253 priv = HILDON_APP_GET_PRIVATE (self);
2255 /* Iterate through the list of view ids and search given id */
2256 for (iter = priv->view_ids; iter; iter = iter->next)
2258 if ( (unsigned long)((view_item *)iter->data)->view_id == view_id)
2259 return (gpointer)((view_item *)iter->data)->view_ptr;