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
27 * @short_description: A base widget to present application. Deprecated, use #HildonProgram instead.
28 * @see_also: #HildonAppView
30 * #HildonApp is the base for any hildon application.
31 * It controls basic looks and functionality of an application, like a title.
33 * This widget is deprecated use #HildonProgram instead.
37 #include "hildon-app.h"
38 #include "hildon-app-private.h"
39 #include "hildon-appview.h"
40 #include "gtk-infoprint.h"
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>
58 #include <libmb/mbutil.h>
64 #define TITLE_DELIMITER " - "
67 * 'Magic' values for the titlebar menu area limits
69 #define MENUAREA_LEFT_LIMIT 80
70 #define MENUAREA_RIGHT_LIMIT MENUAREA_LEFT_LIMIT + 307
71 #define MENUAREA_TOP_LIMIT 0
72 #define MENUAREA_BOTTOM_LIMIT 39
74 #define KILLABLE "CANKILL"
76 #define _(String) dgettext(PACKAGE, String)
78 #define HILDON_APP_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), \
79 HILDON_TYPE_APP, HildonAppPrivate));
81 static GtkWindowClass *parent_class;
82 static guint app_signals[HILDON_APP_LAST_SIGNAL] = { 0 };
84 typedef struct _HildonAppPrivate HildonAppPrivate;
87 hildon_app_switch_to_desktop (void);
89 hildon_app_key_press (GtkWidget *widget, GdkEventKey *keyevent);
91 hildon_app_key_release (GtkWidget *widget, GdkEventKey *keyevent);
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);
97 hildon_app_construct_title (HildonApp *self);
99 hildon_app_finalize (GObject *obj_self);
101 hildon_app_destroy (GtkObject *obj);
103 hildon_app_init (HildonApp *self);
105 hildon_app_class_init (HildonAppClass *app_class);
107 hildon_app_real_topmost_status_acquire (HildonApp *self);
109 hildon_app_real_topmost_status_lose (HildonApp *self);
111 hildon_app_real_switch_to (HildonApp *self);
113 hildon_app_button (GtkWidget *widget, GdkEventButton *event);
115 find_window (GdkWindow *window, gint by, gint co);
117 hildon_app_clipboard_copy(HildonApp *self, GtkWidget *widget);
119 hildon_app_clipboard_cut(HildonApp *self, GtkWidget *widget);
121 hildon_app_clipboard_paste(HildonApp *self, GtkWidget *widget);
122 static gboolean hildon_app_escape_timeout(gpointer data);
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);
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);
137 /* FIXME: Zoom is deprecated, should be removed */
142 PROP_AUTOREGISTRATION,
147 static gpointer find_view(HildonApp *self, unsigned long view_id);
149 /* FIXME: Zoom level is deprecated, should be removed */
151 * hildon_zoom_level_get_type:
153 * Initialises, and returns the type of a hildon zoom level
155 * Returns: GType of #HildonZoomLevel
159 hildon_zoom_level_get_type (void)
161 static GType 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" },
169 etype = g_enum_register_static ("HildonZoomLevel", values);
174 GType hildon_app_get_type(void)
176 static GType app_type = 0;
180 static const GTypeInfo app_info =
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 */
190 (GInstanceInitFunc) hildon_app_init,
192 app_type = g_type_register_static(GTK_TYPE_WINDOW,
193 "HildonApp", &app_info, 0);
199 * Sets or delete a custom property into the XServer, according
200 * to the boolean value of HildonAppPrivate::killable
202 static void hildon_app_apply_killable(HildonApp *self)
204 HildonAppPrivate *priv;
205 Atom killability_atom = XInternAtom (GDK_DISPLAY(),
206 "_HILDON_APP_KILLABLE", False);
207 priv = HILDON_APP_GET_PRIVATE (self);
209 g_assert (HILDON_IS_APP (self) );
210 g_assert(GTK_WIDGET_REALIZED(self));
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,
224 XDeleteProperty(GDK_DISPLAY(),
225 GDK_WINDOW_XID(GTK_WIDGET(self)->window),
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.
236 static void hildon_app_apply_client_list(HildonApp *self)
238 HildonAppPrivate *priv;
244 g_assert (HILDON_IS_APP (self) );
245 g_assert(GTK_WIDGET_REALIZED(self));
247 /* Get the client list handle */
248 clientlist = XInternAtom (GDK_DISPLAY(),
249 "_NET_CLIENT_LIST", False);
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));
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)
259 (unsigned long)(((view_item *)(list_ptr->data))->view_id);
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));
269 XFlush(GDK_DISPLAY());
274 * Performs the standard gtk realize function.
276 static void hildon_app_realize(GtkWidget *widget)
279 HildonAppPrivate *priv;
281 Atom *old_atoms, *new_atoms;
285 g_assert(widget != NULL);
287 self = HILDON_APP(widget);
288 priv = HILDON_APP_GET_PRIVATE(self);
291 * Of course we need to realize the parent.
292 * parent_class got already initialised in the hildon_app_init function
294 GTK_WIDGET_CLASS(parent_class)->realize(widget);
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);
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);
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);
312 memcpy(new_atoms, old_atoms, sizeof(Atom) * atom_count);
314 /* ... creates a new Atom... */
315 new_atoms[atom_count++] =
316 XInternAtom(disp, "_NET_WM_CONTEXT_CUSTOM", False);
318 /* ... and finally update the property within the XServer */
319 XSetWMProtocols(disp, GDK_WINDOW_XID(window), new_atoms, atom_count);
324 /* Add the GDK_SUBSTRUCTURE_MASK (receive events about window configuration
325 * changes of child windows) to the window.
327 gdk_window_set_events(window, gdk_window_get_events(window) | GDK_SUBSTRUCTURE_MASK);
331 * Performs the standard gtk unrealize function.
333 static void hildon_app_unrealize(GtkWidget *widget)
335 HildonAppPrivate *priv = HILDON_APP_GET_PRIVATE(widget);
337 if (priv->key_snooper)
339 /* removing the snooper that handles MENU and HOME key presses */
340 gtk_key_snooper_remove(priv->key_snooper);
341 priv->key_snooper = 0;
344 gdk_window_remove_filter(NULL, hildon_app_event_filter, widget);
345 GTK_WIDGET_CLASS(parent_class)->unrealize(widget);
349 * Class initialisation.
351 static void hildon_app_class_init (HildonAppClass *app_class)
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);
359 /* set the global parent_class here */
360 parent_class = g_type_class_peek_parent(app_class);
362 g_type_class_add_private(app_class, sizeof(HildonAppPrivate));
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;
369 gtkobject_class->destroy = hildon_app_destroy;
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;
378 container_class->add = hildon_app_add;
379 container_class->remove = hildon_app_remove;
380 container_class->forall = hildon_app_forall;
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;
387 /* create the signals */
388 app_signals[TOPMOST_STATUS_ACQUIRE] =
389 g_signal_new("topmost_status_acquire",
390 G_OBJECT_CLASS_TYPE(object_class),
392 G_STRUCT_OFFSET(HildonAppClass,
393 topmost_status_acquire), NULL, NULL,
394 g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0);
396 app_signals[TOPMOST_STATUS_LOSE] =
397 g_signal_new("topmost_status_lose",
398 G_OBJECT_CLASS_TYPE(object_class),
400 G_STRUCT_OFFSET(HildonAppClass, topmost_status_lose),
402 g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0);
404 app_signals[SWITCH_TO] =
405 g_signal_new("switch_to",
406 G_OBJECT_CLASS_TYPE(object_class),
408 G_STRUCT_OFFSET(HildonAppClass, switch_to),
410 g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0);
412 app_signals[IM_CLOSE] =
413 g_signal_new("im_close",
414 G_OBJECT_CLASS_TYPE(object_class),
416 G_STRUCT_OFFSET(HildonAppClass, im_close),
418 g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0);
420 app_signals[CLIPBOARD_COPY] =
421 g_signal_new("clipboard_copy",
422 G_OBJECT_CLASS_TYPE(object_class),
424 G_STRUCT_OFFSET(HildonAppClass, clipboard_copy),
426 g_cclosure_marshal_VOID__OBJECT, G_TYPE_NONE, 1,
428 app_signals[CLIPBOARD_CUT] =
429 g_signal_new("clipboard_cut",
430 G_OBJECT_CLASS_TYPE(object_class),
432 G_STRUCT_OFFSET(HildonAppClass, clipboard_cut),
434 g_cclosure_marshal_VOID__OBJECT, G_TYPE_NONE, 1,
436 app_signals[CLIPBOARD_PASTE] =
437 g_signal_new("clipboard_paste",
438 G_OBJECT_CLASS_TYPE(object_class),
440 G_STRUCT_OFFSET(HildonAppClass, clipboard_paste),
442 g_cclosure_marshal_VOID__OBJECT, G_TYPE_NONE, 1,
446 g_object_class_install_property(object_class, PROP_SCROLL_CONTROL,
447 g_param_spec_boolean("scroll-control",
449 "Set the scroll control ON/OFF",
450 TRUE, G_PARAM_READWRITE));
452 g_object_class_install_property(object_class, PROP_TWO_PART_TITLE,
453 g_param_spec_boolean("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",
461 "Set the zoom level",
462 HILDON_TYPE_ZOOM_LEVEL,
466 g_object_class_install_property(object_class, PROP_APP_TITLE,
467 g_param_spec_string ("app-title",
469 "Set the application title",
473 g_object_class_install_property(object_class, PROP_KILLABLE,
474 g_param_spec_boolean("killable",
476 "Whether the application is killable or not",
479 g_object_class_install_property(object_class, PROP_AUTOREGISTRATION,
480 g_param_spec_boolean("autoregistration",
482 "Whether the application views should be registered automatically",
485 g_object_class_install_property(object_class, PROP_APPVIEW,
486 g_param_spec_object("appview",
488 "The currently active application view",
491 g_object_class_install_property(object_class, PROP_UI_MANAGER,
492 g_param_spec_object("ui-manager",
494 "The associated GtkUIManager for this app",
500 * Performs the standard gtk finalize function, freeing allocated
501 * memory and propagating the finalization to the parent.
504 hildon_app_finalize (GObject *obj)
506 HildonAppPrivate *priv = NULL;
508 g_assert (obj != NULL);
510 priv = HILDON_APP_GET_PRIVATE (obj);
512 g_free (priv->title);
514 if (G_OBJECT_CLASS(parent_class)->finalize)
515 G_OBJECT_CLASS(parent_class)->finalize(obj);
517 /* FIXME: This is legacy code, but cannot be removed
518 without changing functionality */
523 * Removes the long escape ("cancel" hw key) press timeout.
526 hildon_app_remove_timeout(HildonAppPrivate *priv)
528 g_assert(priv != NULL);
530 if (priv->escape_timeout > 0)
532 g_source_remove (priv->escape_timeout);
533 priv->escape_timeout = 0;
538 * Frees all the resources and propagates the destroy call to the parent.
541 hildon_app_destroy (GtkObject *obj)
543 HildonAppPrivate *priv = NULL;
545 g_assert (obj != NULL);
547 priv = HILDON_APP_GET_PRIVATE (obj);
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.
554 hildon_app_remove_timeout(priv);
556 if (priv->uim != NULL)
558 g_object_unref (G_OBJECT (priv->uim));
562 /* Free all the views */
565 g_slist_foreach (priv->view_ids, (GFunc)g_free, NULL);
566 g_slist_free (priv->view_ids);
567 priv->view_ids = NULL;
570 if (GTK_OBJECT_CLASS (parent_class)->destroy)
571 GTK_OBJECT_CLASS (parent_class)->destroy(obj);
575 * Overrides gtk_container_forall, calling the callback function for each of
576 * the children of HildonAppPrivate.
578 static void hildon_app_forall (GtkContainer *container, gboolean include_internals,
579 GtkCallback callback, gpointer callback_data)
581 HildonAppPrivate *priv = NULL;
583 g_return_if_fail (container != NULL);
584 g_return_if_fail (callback != NULL);
586 priv = HILDON_APP_GET_PRIVATE (container);
588 /* Note! we only have user added children, no internals */
589 g_list_foreach (priv->children, (GFunc)callback, callback_data);
593 * An accessor to set private properties of HildonAppPrivate.
595 static void hildon_app_set_property(GObject * object, guint property_id,
596 const GValue * value, GParamSpec * pspec)
598 HildonAppPrivate *priv = HILDON_APP_GET_PRIVATE(object);
600 switch (property_id) {
601 case PROP_SCROLL_CONTROL:
602 priv->scroll_control = g_value_get_boolean(value);
604 #ifndef HILDON_DISABLE_DEPRECATED
606 hildon_app_set_zoom( HILDON_APP (object), g_value_get_enum (value) );
609 case PROP_TWO_PART_TITLE:
610 hildon_app_set_two_part_title( HILDON_APP (object),
611 g_value_get_boolean (value) );
614 hildon_app_set_title( HILDON_APP (object), g_value_get_string (value));
617 hildon_app_set_killable( HILDON_APP (object),
618 g_value_get_boolean (value));
620 case PROP_AUTOREGISTRATION:
621 hildon_app_set_autoregistration( HILDON_APP (object),
622 g_value_get_boolean (value));
625 hildon_app_set_appview( HILDON_APP (object),
626 HILDON_APPVIEW (g_value_get_object (value)));
628 case PROP_UI_MANAGER:
629 hildon_app_set_ui_manager( HILDON_APP (object),
630 GTK_UI_MANAGER (g_value_get_object (value)));
633 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec);
639 * An accessor to get private properties of HildonAppPrivate.
641 static void hildon_app_get_property(GObject * object, guint property_id,
642 GValue * value, GParamSpec * pspec)
644 HildonAppPrivate *priv = HILDON_APP_GET_PRIVATE(object);
646 switch (property_id) {
647 case PROP_SCROLL_CONTROL:
648 g_value_set_boolean( value, priv->scroll_control );
650 #ifndef HILDON_DISABLE_DEPRECATED
652 g_value_set_enum( value, priv->zoom);
655 case PROP_TWO_PART_TITLE:
656 g_value_set_boolean( value, priv->twoparttitle);
659 g_value_set_string (value, priv->title);
662 g_value_set_boolean (value, priv->killable);
664 case PROP_AUTOREGISTRATION:
665 g_value_set_boolean (value, priv->autoregistration);
668 g_value_set_object (value, hildon_app_get_appview (HILDON_APP (object)));
670 case PROP_UI_MANAGER:
671 g_value_set_object (value, hildon_app_get_ui_manager (HILDON_APP (object)));
674 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec);
680 * Adds a child widget to HildonApp.
682 static void hildon_app_add (GtkContainer *container, GtkWidget *child)
684 HildonApp *app = NULL;
685 HildonAppPrivate *priv = NULL;
687 g_return_if_fail (container != NULL);
688 g_return_if_fail (GTK_IS_WIDGET (child));
690 app = HILDON_APP (container);
691 priv = HILDON_APP_GET_PRIVATE (app);
693 /* Check if child is already added here */
694 if (g_list_find (priv->children, child) != NULL)
697 priv->children = g_list_append (priv->children, child);
698 GTK_BIN (container)->child = child;
699 gtk_widget_set_parent (child, GTK_WIDGET (app));
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. */
707 /* FIXME: This is legacy stuff */
708 if (gtk_widget_get_default_direction () !=
709 gtk_widget_get_direction (GTK_WIDGET (child)))
711 gtk_widget_set_direction (GTK_WIDGET (child),
712 gtk_widget_get_default_direction ());
715 if (HILDON_IS_APPVIEW (child))
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);
725 * Removes a child widget from HildonApp.
727 static void hildon_app_remove (GtkContainer *container, GtkWidget *child)
729 HildonAppPrivate *priv;
733 g_return_if_fail (container != NULL);
734 g_return_if_fail (GTK_IS_WIDGET (child));
736 priv = HILDON_APP_GET_PRIVATE (container);
737 bin = GTK_BIN (container);
738 app = HILDON_APP (bin);
740 /* Make sure that child is found in the list */
741 if (g_list_find (priv->children, child) == NULL)
744 priv->children = g_list_remove (priv->children, child);
746 if (HILDON_IS_APPVIEW (child))
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 */
753 g_signal_handlers_disconnect_by_func (G_OBJECT (child),
754 (gpointer)hildon_app_construct_title, app);
756 if (priv->autoregistration)
757 hildon_app_unregister_view (app, HILDON_APPVIEW (child));
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);
764 if (bin->child == child)
767 gtk_widget_queue_resize (GTK_WIDGET (bin));
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.
778 * It returns FALSE in order to get called only once.
781 hildon_app_escape_timeout(gpointer app)
783 HildonAppPrivate *priv;
786 GDK_THREADS_ENTER ();
788 g_assert(GTK_WIDGET_REALIZED(app));
790 priv = HILDON_APP_GET_PRIVATE(app);
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);
799 priv->escape_timeout = 0;
801 GDK_THREADS_LEAVE ();
807 * Looks for the visible window to whom the point of coordinates (co,by)
808 * belongs. Search recursively all the children of the window.
810 * This functionality is only needed for scrollbar remote control.
812 static GdkWindow *find_window (GdkWindow *window, gint by, gint co)
815 GList *children = gdk_window_peek_children (window);
817 /* If the window has no children, then the coordinates must match it */
821 if (!(child = (GdkWindow *)children->data))
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)
831 gint x, width, y, height;
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
839 if (x < co && x + width > co && y < by && y + height > by)
840 return find_window (child, by, co);
843 /* If the window has no more children, then it's the one we're looking for */
844 if (!(children = g_list_next (children)))
847 } while ( (child = children->data));
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).
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).
862 hildon_app_button (GtkWidget *widget, GdkEventButton *event)
864 HildonAppPrivate *priv = NULL;
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;
870 if (!GTK_WIDGET_REALIZED(widget))
875 priv = HILDON_APP_GET_PRIVATE (widget);
877 if (!priv->scroll_control)
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
889 if (HILDON_IS_APPVIEW(GTK_BIN(widget)->child))
891 GtkBin *avbin = GTK_BIN(GTK_BIN(widget)->child);
892 if (GTK_IS_SCROLLED_WINDOW(avbin->child))
894 GtkScrolledWindow *win;
895 win = GTK_SCROLLED_WINDOW(avbin->child);
897 if (GTK_WIDGET_VISIBLE(win->vscrollbar))
899 /* Calculate the distance between the AppView's right border and
900 * the scrollbars center
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);
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
913 if (event->x > widget->allocation.width - sensitive_border)
915 GdkWindow *window = NULL;
916 gint co = widget->allocation.width - sensitive_border;
918 /* We now need to know in which window the _modified_ coordinates are */
919 if ((window = find_window (widget->window, event->y, co)))
921 GdkEventButton nevent;
923 if (window == widget->window)
926 /* Build a new event and associate the proper window to it */
929 nevent.window = window;
930 g_object_ref (nevent.window);
931 gtk_main_do_event ((GdkEvent*)&nevent);
938 * Performs the initialisation of the widget.
941 hildon_app_init (HildonApp *self)
943 HildonAppPrivate *priv;
945 priv = HILDON_APP_GET_PRIVATE(self);
948 priv->title = g_strdup("");
949 #ifndef HILDON_DISABLE_DEPRECATED
950 priv->zoom = HILDON_ZOOM_MEDIUM;
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;
962 priv->active_menu_id = 0;
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);
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);
974 gtk_widget_set_events (GTK_WIDGET(self), GDK_BUTTON_PRESS_MASK |
975 GDK_BUTTON_RELEASE_MASK |
976 GDK_POINTER_MOTION_MASK);
979 /*public functions */
984 * Creates a new #HildonApp
986 * Returns: pointer to a new #HildonApp structure
989 hildon_app_new (void)
991 return GTK_WIDGET(g_object_new(HILDON_TYPE_APP, NULL));
995 * hildon_app_new_with_appview:
996 * @appview : a #HildonAppView
998 * Creates an app, and sets it's initial appview.
1000 * Returns: pointer to a new #HildonApp structure
1003 hildon_app_new_with_appview (HildonAppView *appview)
1007 g_return_val_if_fail (HILDON_IS_APPVIEW (appview), NULL);
1009 app = hildon_app_new ();
1011 hildon_app_set_appview(HILDON_APP(app), appview);
1017 * hildon_app_get_appview:
1018 * @self : a #HildonApp
1020 * Gets the currently shown appview.
1022 * Returns: the currently shown appview in this HildonApp.
1023 * If no appview is currently set for this HildonApp,
1027 hildon_app_get_appview (HildonApp *self)
1031 g_return_val_if_fail (HILDON_IS_APP (self), NULL);
1032 bin = GTK_BIN (self);
1033 if (HILDON_IS_APPVIEW (bin->child))
1035 return HILDON_APPVIEW (bin->child);
1042 * hildon_app_set_appview:
1043 * @self : a #HildonApp
1044 * @appview : a #HildonAppView
1046 * Sets (switches to) appview.
1049 hildon_app_set_appview (HildonApp *app, HildonAppView *view)
1051 HildonAppPrivate *priv;
1053 GtkWidget *widget; /*(view to be set)*/
1056 g_return_if_fail (HILDON_IS_APP (app));
1057 g_return_if_fail (HILDON_IS_APPVIEW (view));
1059 bin = GTK_BIN (app);
1060 priv = HILDON_APP_GET_PRIVATE (app);
1061 widget = GTK_WIDGET (view);
1063 if (widget == bin->child)
1066 /* Make old appview dissapear */
1069 gtk_widget_hide (bin->child);
1070 g_signal_emit_by_name (bin->child, "switched_from", NULL);
1072 if (priv->active_menu_id > 0)
1074 if (priv->uim != NULL)
1076 gtk_ui_manager_remove_ui (priv->uim,
1077 priv->active_menu_id);
1079 priv->active_menu_id = 0;
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);
1089 bin->child = widget;
1091 gtk_widget_show (widget);
1093 /* UI manager support, merge menu for activated view */
1094 g_object_get (G_OBJECT (view),
1095 "menu-ui", &menu_ui,
1098 if (menu_ui && priv->uim)
1101 priv->active_menu_id =
1102 gtk_ui_manager_add_ui_from_string (priv->uim, menu_ui, -1, NULL);
1104 gtk_ui_manager_ensure_update (priv->uim);
1110 g_signal_emit_by_name (widget, "switched_to", NULL);
1112 /* Inform task navigator about changed view */
1113 hildon_app_notify_view_changed (app, view);
1115 /* Update title to show currently activated view */
1116 hildon_app_construct_title (app);
1117 gtk_widget_child_focus (widget, GTK_DIR_TAB_FORWARD);
1121 * hildon_app_set_title:
1122 * @self : a #HildonApp
1123 * @newtitle : the new title assigned to the application
1125 * Sets title of the application.
1128 hildon_app_set_title (HildonApp *self, const gchar *newtitle)
1130 HildonAppPrivate *priv;
1133 g_return_if_fail(HILDON_IS_APP(self));
1135 priv = HILDON_APP_GET_PRIVATE(self);
1136 oldstr = priv->title;
1140 priv->title = g_strdup(newtitle);
1141 g_strstrip(priv->title);
1144 priv->title = g_strdup("");
1149 hildon_app_construct_title(self);
1153 * hildon_app_get_title:
1154 * @self : a #HildonApp
1156 * Gets the title of the application.
1158 * Returns: the title currently assigned to the application. This
1159 * value is not to be freed or modified by the calling application
1162 hildon_app_get_title (HildonApp *self)
1164 HildonAppPrivate *priv;
1166 g_return_val_if_fail (HILDON_IS_APP(self), NULL);
1167 priv = HILDON_APP_GET_PRIVATE(self);
1171 /* FIXME: Zoom is deprecated, remove */
1173 * hildon_app_set_zoom:
1174 * @self : a #HildonApp
1175 * @newzoom: the zoom level of type #HildonZoomLevel to be assigned to an
1178 * Sets the zoom level. Warning! This function is deprecated and
1179 * should not be used. It's lecacy stuff from ancient specs.
1182 hildon_app_set_zoom (HildonApp *self, HildonZoomLevel newzoom)
1184 HildonAppPrivate *priv;
1186 g_return_if_fail(HILDON_IS_APP(self));
1188 priv = HILDON_APP_GET_PRIVATE(self);
1190 if (newzoom != priv->zoom)
1192 if (newzoom < HILDON_ZOOM_SMALL)
1194 newzoom = HILDON_ZOOM_SMALL;
1195 gtk_infoprint(GTK_WINDOW(self), _("ckct_ib_min_zoom_level_reached"));
1197 else if (newzoom > HILDON_ZOOM_LARGE) {
1198 newzoom = HILDON_ZOOM_LARGE;
1199 gtk_infoprint(GTK_WINDOW(self), _("ckct_ib_max_zoom_level_reached"));
1201 priv->zoom = newzoom;
1206 * hildon_app_get_zoom:
1207 * @self : a #HildonApp
1209 * Gets the zoom level. Warning! This function is deprecated and
1210 * should not be used. It's lecacy stuff from ancient specifications.
1212 * Returns: the zoom level of the Hildon application. The
1213 * returned zoom level is of type #HildonZoomLevel.
1216 hildon_app_get_zoom (HildonApp *self)
1218 HildonAppPrivate *priv;
1220 g_return_val_if_fail(HILDON_IS_APP(self), HILDON_ZOOM_MEDIUM);
1221 priv = HILDON_APP_GET_PRIVATE(self);
1226 * hildon_app_get_default_font:
1227 * @self : a #HildonApp
1229 * Gets default font. Warning! This function is deprecated and should
1230 * not be used. It's legacy stuff from ancient version of specification.
1232 * Returns: pointer to PangoFontDescription for the default,
1235 PangoFontDescription *
1236 hildon_app_get_default_font (HildonApp *self)
1238 PangoFontDescription *font_desc = NULL;
1239 GtkStyle *fontstyle = NULL;
1241 g_return_val_if_fail(HILDON_IS_APP(self), NULL);
1244 gtk_rc_get_style_by_paths (gtk_widget_get_settings
1245 (GTK_WIDGET(self)), NULL, NULL,
1246 gtk_widget_get_type());
1250 g_print("WARNING : default font not found. "
1251 "Defaulting to swissa 19\n");
1252 font_desc = pango_font_description_from_string("swissa 19");
1256 font_desc = pango_font_description_copy(fontstyle->font_desc);
1262 * hildon_app_get_zoom_font:
1263 * @self : a #HildonApp
1265 * Gets the description of the default font. Warning! This function
1266 * is deprecated and should not be used. It's legacy stuff from
1269 * Returns: pointer to PangoFontDescription for the default,
1272 PangoFontDescription *
1273 hildon_app_get_zoom_font (HildonApp *self)
1275 HildonAppPrivate *priv;
1276 PangoFontDescription *font_desc = NULL;
1277 gchar *style_name = 0;
1278 GtkStyle *fontstyle = NULL;
1280 g_return_val_if_fail(HILDON_IS_APP(self), NULL);
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");
1291 g_warning("Invalid Zoom Value\n");
1292 style_name = g_strdup("");
1296 gtk_rc_get_style_by_paths (gtk_widget_get_settings
1297 (GTK_WIDGET(self)), style_name, NULL,
1299 g_free (style_name);
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");
1313 font_desc = pango_font_description_copy(fontstyle->font_desc);
1318 /* FIXME: Zoom is deprecated, remove the code above */
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
1327 * Sets the two part title.
1330 hildon_app_set_two_part_title (HildonApp *self, gboolean istwoparttitle)
1332 HildonAppPrivate *priv;
1333 g_return_if_fail(HILDON_IS_APP(self));
1334 priv = HILDON_APP_GET_PRIVATE(self);
1336 if (istwoparttitle != priv->twoparttitle)
1338 priv->twoparttitle = istwoparttitle;
1339 hildon_app_construct_title(self);
1344 * hildon_app_get_two_part_title:
1345 * @self : a #HildonApp
1347 * Gets the 'twopart' represention of the title inside #HildonApp.
1349 * Returns: a boolean indicating wheter title shown has both
1350 * application, and application view title separated by a triangle.
1353 hildon_app_get_two_part_title (HildonApp *self)
1355 HildonAppPrivate *priv;
1357 g_return_val_if_fail(HILDON_IS_APP(self), FALSE);
1358 priv = HILDON_APP_GET_PRIVATE(self);
1359 return priv->twoparttitle;
1363 /* private functions */
1367 * Generates and sends an event of type _NET_SHOWING_DESKTOP to the XServer,
1368 * which will be then caught by the window Manager.
1371 hildon_app_switch_to_desktop (void)
1375 Atom showing_desktop = XInternAtom(GDK_DISPLAY(),
1376 "_NET_SHOWING_DESKTOP", False);
1377 memset(&ev, 0, sizeof(ev));
1378 ev.xclient.type = ClientMessage;
1379 gdk_error_trap_push();
1380 ev.xclient.window = GDK_ROOT_WINDOW();
1381 ev.xclient.message_type = showing_desktop;
1382 ev.xclient.format = 32;
1383 ev.xclient.data.l[0] = 1;
1384 gdk_error_trap_push();
1385 XSendEvent(GDK_DISPLAY(), GDK_ROOT_WINDOW(), False,
1386 SubstructureRedirectMask, &ev);
1387 gdk_error_trap_pop();
1391 * Handles the key press of the Escape, Increase and Decrease keys. Other keys
1392 * are handled by the parent GtkWidgetClass.
1395 hildon_app_key_press (GtkWidget *widget, GdkEventKey *keyevent)
1397 HildonApp *app = HILDON_APP (widget);
1398 HildonAppView *appview;
1399 HildonAppPrivate *priv = HILDON_APP_GET_PRIVATE(app);
1401 if (HILDON_IS_APPVIEW(GTK_BIN (app)->child))
1403 appview = HILDON_APPVIEW (GTK_BIN (app)->child);
1410 if (keyevent->keyval == GDK_Escape && priv->escape_timeout == 0)
1412 /* Call hildon_app_escape_timeout every 1500ms until it returns FALSE
1413 * and store the relative GSource id. Since hildon_app_escape_timeout
1414 * can only return FALSE, the call will occurr only once.
1416 priv->escape_timeout = g_timeout_add(1500, hildon_app_escape_timeout, app);
1419 /* FIXME: Handling +/- keys here is not usefull. Applications
1420 can equally easily handle the keypress themselves. */
1421 else if (HILDON_KEYEVENT_IS_INCREASE_KEY (keyevent))
1423 _hildon_appview_increase_button_state_changed (appview,
1426 else if (HILDON_KEYEVENT_IS_DECREASE_KEY (keyevent))
1428 _hildon_appview_decrease_button_state_changed (appview,
1432 /* Activate default bindings and let the widget with focus to handle key */
1433 return GTK_WIDGET_CLASS (parent_class)->key_press_event (widget, keyevent);
1437 * Handles the key release event for the Escape, Toolbar and Fullscreen keys.
1440 hildon_app_key_release (GtkWidget *widget, GdkEventKey *keyevent)
1442 HildonApp *app = HILDON_APP (widget);
1443 HildonAppView *appview;
1444 HildonAppPrivate *priv = HILDON_APP_GET_PRIVATE(app);
1446 if (HILDON_IS_APPVIEW(GTK_BIN (app)->child))
1448 appview = HILDON_APPVIEW (GTK_BIN (app)->child);
1455 if (keyevent->keyval == GDK_Escape)
1458 * This will prevent the hildon_app_escape_timeout from being called.
1459 * See hildon_app_escape_timeout and hildon_app_remove_timeout for more.
1461 hildon_app_remove_timeout(priv);
1463 else if (HILDON_KEYEVENT_IS_TOOLBAR_KEY (keyevent))
1465 g_signal_emit_by_name(G_OBJECT(appview),
1466 "toolbar-toggle-request");
1468 else if (HILDON_KEYEVENT_IS_FULLSCREEN_KEY (keyevent))
1470 /* Emit the fullscreen_state_change directly, it'll save one step */
1471 if (hildon_appview_get_fullscreen_key_allowed (appview))
1473 gboolean fullscreen;
1475 fullscreen = hildon_appview_get_fullscreen(appview);
1476 g_signal_emit_by_name(G_OBJECT(appview),
1477 "fullscreen_state_change",
1482 /* FIXME: Should the event be marked as handled if any of the three
1483 above cases took an action */
1485 /* Activate default bindings and let the widget with focus to handle key */
1486 return GTK_WIDGET_CLASS (parent_class)->key_release_event (widget, keyevent);
1490 * Handles the MENU and HOME key presses.
1493 hildon_app_key_snooper (GtkWidget *widget, GdkEventKey *keyevent, HildonApp *app)
1495 /* FIXME: Using normal keypress handler would be better choise. All
1496 keyevents come to window anyway, so we would get the same
1497 keys in that way as well, but we wouldn't need to struggle
1498 with grabs (modal dialogs etc). */
1500 /* Menu key handling is done here */
1501 if ( HILDON_KEYEVENT_IS_MENU_KEY (keyevent) ) {
1502 HildonAppView *appview;
1503 HildonAppPrivate *priv;
1504 GtkWidget *toplevel;
1506 /* Don't act on modal dialogs */
1507 toplevel = gtk_widget_get_toplevel (widget);
1508 if (GTK_IS_DIALOG (toplevel)
1509 && gtk_window_get_modal (GTK_WINDOW (toplevel)))
1514 appview = HILDON_APPVIEW (GTK_BIN(app)->child);
1515 priv = HILDON_APP_GET_PRIVATE(app);
1517 if ( keyevent->type == GDK_KEY_PRESS ) {
1518 /* Toggle menu on press, avoid key repeat */
1519 if ( priv->lastmenuclick == 0 ){
1520 priv->lastmenuclick = 1;
1521 if (_hildon_appview_toggle_menu(appview,
1522 gtk_get_current_event_time()))
1527 } else if ( keyevent->type == GDK_KEY_RELEASE ) {
1528 /* We got release, so next press is really a new press,
1530 if ( priv->lastmenuclick == 1 ) {
1531 priv->lastmenuclick = 0;
1535 /* Unknown key event */
1539 /* don't stop the key event so that it reaches GTK where it
1540 closes all existing menus that might be open */
1544 /* Home key handling is done here. */
1545 if ((keyevent->type == GDK_KEY_RELEASE) &&
1546 HILDON_KEYEVENT_IS_HOME_KEY (keyevent))
1548 hildon_app_switch_to_desktop();
1557 * Returns the message_type of the Atom registered with a certain name.
1560 xclient_message_type_check(XClientMessageEvent *cm, const gchar *name)
1562 return cm->message_type == XInternAtom(GDK_DISPLAY(), name, FALSE);
1566 * Returns the GtkWidget associated to a certain Window.
1569 hildon_app_xwindow_lookup_widget(Window xwindow)
1574 window = gdk_xid_table_lookup(xwindow);
1578 gdk_window_get_user_data(window, &widget);
1583 * Let's search a actual main window using tranciency hints.
1584 * Note that there can be several levels of menus/dialogs above
1585 * the actual main window.
1587 static Window get_active_main_window(Window window)
1589 Window parent_window;
1592 gdk_error_trap_push ();
1594 while (XGetTransientForHint(GDK_DISPLAY(), window, &parent_window))
1596 /* The limit > TRANSIENCY_MAXITER ensures that we can't be stuck
1597 here forever if we have circular transiencies for some reason.
1598 Use of _MB_CURRENT_APP_WINDOW might be more elegant... */
1600 if (!parent_window || parent_window == GDK_ROOT_WINDOW() ||
1601 parent_window == window || limit > TRANSIENCY_MAXITER)
1607 window = parent_window;
1612 if (gdk_error_trap_pop ())
1619 * Filters every GDK event first.
1621 static GdkFilterReturn
1622 hildon_app_event_filter (GdkXEvent *xevent, GdkEvent *event, gpointer data)
1625 HildonApp *app = data;
1626 HildonAppPrivate *priv;
1627 HildonAppView *appview = NULL;
1629 XAnyEvent *eventti = xevent;
1631 if (HILDON_IS_APPVIEW (GTK_BIN (app)->child))
1633 appview = HILDON_APPVIEW (GTK_BIN (app)->child);
1636 g_return_val_if_fail (app, GDK_FILTER_CONTINUE);
1637 g_return_val_if_fail (HILDON_IS_APP(app), GDK_FILTER_CONTINUE);
1639 priv = HILDON_APP_GET_PRIVATE(app);
1640 if (eventti->type == ClientMessage)
1642 XClientMessageEvent *cm = xevent;
1644 /* Check if a message indicating a click on titlebar has been
1645 received. Don't open it if mouse is grabbed (eg. modal dialog
1647 _MB_GRAB_TRANSFER is emitted by MatchBox, and signals that a button
1648 has just been released. */
1649 if (xclient_message_type_check(cm, "_MB_GRAB_TRANSFER") &&
1650 HILDON_IS_APPVIEW(appview) &&
1651 gtk_grab_get_current() == NULL &&
1652 !_hildon_appview_menu_visible(appview))
1654 _hildon_appview_toggle_menu(appview, cm->data.l[0]);
1655 return GDK_FILTER_REMOVE;
1657 /* IM_CLOSE is input method specific hack that is really questionable */
1658 else if (xclient_message_type_check(cm, "_HILDON_IM_CLOSE"))
1660 g_signal_emit_by_name(app, "im_close", NULL);
1661 return GDK_FILTER_REMOVE;
1663 /* Task user changed the view through task navigator? */
1664 else if (xclient_message_type_check(cm, "_NET_ACTIVE_WINDOW"))
1666 unsigned long view_id = cm->window;
1667 gpointer view_ptr = find_view(app, view_id);
1669 /* When getting a _NET_ACTIVE_WINDOW signal from the WM we need
1670 * to bring the application to the front */
1671 if (!priv->is_topmost)
1672 g_signal_emit_by_name (G_OBJECT(app), "topmost_status_acquire");
1674 if (HILDON_IS_APPVIEW(view_ptr))
1675 /* Sets the current view to the "window" that the _NET_ACTIVE_WINDOW
1677 hildon_app_set_appview(app, (HILDON_APPVIEW(view_ptr)));
1679 /* there was no view, so we have to switch to an actual application */
1680 g_signal_emit_by_name (G_OBJECT(app), "switch_to", view_ptr);
1682 /* FIXME: This is a hack. This was once just gtk_window_present, but
1683 was changed into this at some day!! */
1684 if (GTK_WIDGET(app)->window)
1686 mb_util_window_activate(GDK_DISPLAY(),
1687 GDK_WINDOW_XID(GTK_WIDGET(app)->window));
1690 /* FIXME: IM hack */
1691 else if (xclient_message_type_check(cm, "_HILDON_IM_CLIPBOARD_COPY"))
1693 Window xwindow = cm->data.l[0];
1694 GtkWidget *widget = hildon_app_xwindow_lookup_widget(xwindow);
1696 g_signal_emit_by_name (G_OBJECT(app), "clipboard_copy", widget);
1698 /* FIXME: IM hack */
1699 else if (xclient_message_type_check(cm, "_HILDON_IM_CLIPBOARD_CUT"))
1701 Window xwindow = cm->data.l[0];
1702 GtkWidget *widget = hildon_app_xwindow_lookup_widget(xwindow);
1704 g_signal_emit_by_name (G_OBJECT(app), "clipboard_cut", widget);
1706 /* FIXME: IM hack */
1707 else if (xclient_message_type_check(cm, "_HILDON_IM_CLIPBOARD_PASTE"))
1709 Window xwindow = cm->data.l[0];
1710 GtkWidget *widget = hildon_app_xwindow_lookup_widget(xwindow);
1712 g_signal_emit_by_name (G_OBJECT(app), "clipboard_paste", widget);
1716 if (eventti->type == ButtonPress)
1719 /* FIXME: This is mysterious bugfix related to problems to open the
1720 application menu (bugzilla N#3204) */
1721 XButtonEvent *bev = (XButtonEvent *)xevent;
1723 if (HILDON_IS_APPVIEW(appview) &&
1724 _hildon_appview_menu_visible(appview) &&
1725 !hildon_appview_get_fullscreen(appview))
1729 if ( (x >= MENUAREA_LEFT_LIMIT) && (x <= MENUAREA_RIGHT_LIMIT) &&
1730 (y >= MENUAREA_TOP_LIMIT) && (y <= MENUAREA_BOTTOM_LIMIT))
1732 _hildon_appview_toggle_menu(appview, bev->time);
1733 return GDK_FILTER_CONTINUE;
1738 /* FIXME: as above */
1739 if (eventti->type == ButtonRelease)
1741 if (HILDON_IS_APPVIEW(appview) &&
1742 _hildon_appview_menu_visible(appview) &&
1743 !hildon_appview_get_fullscreen(appview))
1745 XButtonEvent *bev = (XButtonEvent *)xevent;
1748 if ( (x >= MENUAREA_LEFT_LIMIT) && (x < MENUAREA_RIGHT_LIMIT) &&
1749 (y >= MENUAREA_TOP_LIMIT) && (y <= MENUAREA_BOTTOM_LIMIT))
1751 return GDK_FILTER_REMOVE;
1754 return GDK_FILTER_CONTINUE;
1757 /* Application stacking order changed */
1758 if (eventti->type == PropertyNotify)
1760 Atom active_app_atom =
1761 XInternAtom (GDK_DISPLAY(), "_MB_CURRENT_APP_WINDOW", False);
1762 XPropertyEvent *prop = xevent;
1764 if ((prop->atom == active_app_atom)
1765 && (prop->window == GDK_ROOT_WINDOW()))
1771 unsigned long extra;
1776 unsigned char *char_pointer;
1781 status = XGetWindowProperty(GDK_DISPLAY(), GDK_ROOT_WINDOW(),
1782 active_app_atom, 0L, 16L,
1783 0, XA_WINDOW, &realtype, &format,
1784 &n, &extra, &win.char_pointer);
1785 if (!(status == Success && realtype == XA_WINDOW && format == 32
1786 && n == 1 && win.win != NULL))
1788 if (win.win != NULL)
1789 XFree(win.char_pointer);
1790 return GDK_FILTER_CONTINUE;
1793 my_window = GDK_WINDOW_XID(GTK_WIDGET(app)->window);
1795 /* Are we the topmost one? */
1796 if (win.win[0] == my_window ||
1797 get_active_main_window(win.win[0]) == my_window)
1799 if (!priv->is_topmost)
1800 g_signal_emit_by_name (G_OBJECT(app),
1801 "topmost_status_acquire");
1803 else if (priv->is_topmost)
1805 GtkWidget *focus = gtk_window_get_focus(GTK_WINDOW(app));
1807 /* FIXME: IM hack, IM-module should do this in response to
1808 topmost_status_lose (emission hook?) */
1809 if (GTK_IS_ENTRY(focus))
1810 gtk_im_context_focus_out(GTK_ENTRY(focus)->im_context);
1811 if (GTK_IS_TEXT_VIEW(focus))
1812 gtk_im_context_focus_out(GTK_TEXT_VIEW(focus)->im_context);
1814 g_signal_emit_by_name (app, "topmost_status_lose");
1817 if (win.win != NULL)
1818 XFree(win.char_pointer);
1822 return GDK_FILTER_CONTINUE;
1826 * Sets the GTK Window title to the application's title, or
1827 * combined appview/app title, if two part title is asked.
1830 hildon_app_construct_title (HildonApp *self)
1832 g_return_if_fail (HILDON_IS_APP (self));
1834 if (GTK_WIDGET_REALIZED(self))
1836 HildonAppPrivate *priv;
1838 gchar *concatenated_title = NULL;
1839 HildonAppView *appview;
1841 priv = HILDON_APP_GET_PRIVATE (self);
1842 appview = hildon_app_get_appview(self);
1844 /* FIXME: The subname property is legacy stuff no longer supported by
1845 Matchbox. However, it is still set for the convenience of
1846 the Task Navigator. */
1847 subname = gdk_atom_intern("_MB_WIN_SUB_NAME", FALSE);
1849 if (!appview || !hildon_app_get_two_part_title(self) ||
1850 g_utf8_strlen(hildon_appview_get_title(appview), -1) < 1 )
1852 /* Set an invisible dummy value if there is no appview title */
1853 gdk_property_change (GTK_WIDGET(self)->window, subname,
1854 gdk_atom_intern ("UTF8_STRING", FALSE),
1855 8, GDK_PROP_MODE_REPLACE, (guchar *) " \0", 1);
1856 gtk_window_set_title (GTK_WINDOW(self), priv->title);
1860 gdk_property_change (GTK_WIDGET(self)->window, subname,
1861 gdk_atom_intern ("UTF8_STRING", FALSE),
1862 8, GDK_PROP_MODE_REPLACE,
1863 (guchar *)hildon_appview_get_title(appview),
1864 strlen(hildon_appview_get_title (appview)));
1865 concatenated_title = g_strjoin(TITLE_DELIMITER, priv->title,
1866 hildon_appview_get_title(appview), NULL);
1867 /* priv->title should always be non-null, but check anyway */
1868 if (concatenated_title != NULL)
1870 gtk_window_set_title (GTK_WINDOW(self), concatenated_title);
1871 g_free(concatenated_title);
1878 * Callback function to the topmost_status_acquire signal emitted by
1879 * hildon_app_event_filter function. See it for more details.
1882 hildon_app_real_topmost_status_acquire (HildonApp *self)
1884 HildonAppPrivate *priv;
1885 g_return_if_fail (HILDON_IS_APP (self));
1886 priv = HILDON_APP_GET_PRIVATE (self);
1888 /* FIXME: What is the logic not to update topmost status now? */
1889 if (!GTK_BIN (self)->child)
1892 priv->is_topmost = TRUE;
1896 * Callback function to the topmost_status_lose signal emitted by
1897 * hildon_app_event_filter function. See it for more details.
1900 hildon_app_real_topmost_status_lose (HildonApp *self)
1902 HildonAppPrivate *priv;
1903 g_return_if_fail (HILDON_IS_APP (self));
1904 priv = HILDON_APP_GET_PRIVATE (self);
1906 /* FIXME: What is the logic not to update topmost status now? */
1907 if (!GTK_BIN (self)->child)
1910 priv->is_topmost = FALSE;
1914 hildon_app_real_switch_to (HildonApp *self)
1916 g_return_if_fail (HILDON_IS_APP (self));
1917 /* Do we have to do anything here? */
1922 * hildon_app_set_autoregistration
1923 * @self : a #HildonApp
1924 * @auto_reg : whether the (app)view autoregistration should be active
1926 * Controls the autoregistration/unregistration of (app)views.
1929 void hildon_app_set_autoregistration(HildonApp *self, gboolean auto_reg)
1931 HildonAppPrivate *priv;
1932 g_return_if_fail (HILDON_IS_APP (self));
1934 priv = HILDON_APP_GET_PRIVATE (self);
1935 priv->autoregistration = auto_reg;
1940 * hildon_app_register_view:
1941 * @self : a #HildonApp
1942 * @view_ptr : pointer to the view instance to be registered
1944 * Registers a new view. For appviews, this can be done automatically
1945 * if autoregistration is set.
1948 void hildon_app_register_view(HildonApp *self, gpointer view_ptr)
1950 HildonAppPrivate *priv;
1951 view_item *view_item_inst;
1953 g_return_if_fail (HILDON_IS_APP (self) || view_ptr != NULL);
1955 priv = HILDON_APP_GET_PRIVATE (self);
1957 if (hildon_app_find_view_id(self, view_ptr) == 0)
1959 /* The pointer to the view was unique, so add it to the list */
1960 view_item_inst = g_malloc(sizeof(view_item));
1961 view_item_inst->view_id = priv->view_id_counter;
1962 view_item_inst->view_ptr = view_ptr;
1964 priv->view_id_counter++;
1967 g_slist_append(priv->view_ids, view_item_inst);
1969 /* Update the list of views */
1970 if (GTK_WIDGET_REALIZED(self))
1971 hildon_app_apply_client_list(self);
1977 * hildon_app_register_view_with_id:
1978 * @self : a #HildonApp
1979 * @view_ptr : pointer to the view instance to be registered
1980 * @view_id : the ID of the view
1982 * Registers a new view. Allows the application to specify any ID.
1984 * Returns: TRUE if the view registration succeeded, FALSE otherwise.
1985 * The probable cause of failure is that view with that ID
1989 gboolean hildon_app_register_view_with_id(HildonApp *self,
1991 unsigned long view_id)
1993 view_item *view_item_inst;
1994 HildonAppPrivate *priv;
1995 GSList *list_ptr = NULL;
1997 g_return_val_if_fail (HILDON_IS_APP (self), FALSE);
1998 g_return_val_if_fail (view_ptr, FALSE);
2000 priv = HILDON_APP_GET_PRIVATE (self);
2002 list_ptr = priv->view_ids;
2004 /* Check that the view is not already registered */
2007 if ( (gpointer)((view_item *)list_ptr->data)->view_ptr == view_ptr
2008 && (unsigned long)((view_item *)list_ptr->data)->view_id == view_id)
2012 list_ptr = list_ptr->next;
2015 /* The pointer to the view was unique, so add it to the list */
2016 view_item_inst = g_malloc(sizeof(view_item));
2017 view_item_inst->view_id = view_id;
2018 view_item_inst->view_ptr = view_ptr;
2021 g_slist_append(priv->view_ids, view_item_inst);
2023 priv->view_id_counter++;
2025 /* Finally, update the _NET_CLIENT_LIST property */
2026 if (GTK_WIDGET_REALIZED(self))
2027 hildon_app_apply_client_list(self);
2033 * hildon_app_unregister_view:
2034 * @self : a #HildonApp
2035 * @view_ptr : pointer to the view instance to be unregistered
2037 * Unregisters a view from HildonApp. Done usually when a view is
2038 * destroyed. For appviews, this is can be automatically
2039 * if autoregistration is set.
2041 void hildon_app_unregister_view(HildonApp *self, gpointer view_ptr)
2043 HildonAppPrivate *priv = NULL;
2044 GSList *list_ptr = NULL;
2046 g_return_if_fail (HILDON_IS_APP (self));
2047 g_return_if_fail (view_ptr != NULL);
2049 priv = HILDON_APP_GET_PRIVATE (self);
2051 /* Search the view from the list */
2052 list_ptr = priv->view_ids;
2056 if ( (gpointer)((view_item *)list_ptr->data)->view_ptr == view_ptr)
2058 /* Found the view, kick it off */
2059 g_free (list_ptr->data);
2060 priv->view_ids = g_slist_delete_link(priv->view_ids, list_ptr);
2063 list_ptr = list_ptr->next;
2066 if (GTK_WIDGET_REALIZED(self))
2067 hildon_app_apply_client_list(self);
2072 * hildon_app_unregister_view_with_id:
2073 * @self: a #HildonApp
2074 * @view_id: the ID of the view that should be unregistered
2076 * Unregisters a view with specified ID, if it exists.
2078 void hildon_app_unregister_view_with_id(HildonApp *self,
2079 unsigned long view_id)
2081 HildonAppPrivate *priv;
2082 GSList *list_ptr = NULL;
2084 g_return_if_fail (HILDON_IS_APP (self));
2086 priv = HILDON_APP_GET_PRIVATE (self);
2088 /* Search the view from the list */
2089 list_ptr = priv->view_ids;
2093 if ( (unsigned long)((view_item *)list_ptr->data)->view_id == view_id)
2095 /* Found view with given id, kick it off */
2096 g_free (list_ptr->data);
2097 priv->view_ids = g_slist_delete_link(priv->view_ids, list_ptr);
2100 list_ptr = list_ptr->next;
2103 /* Update client list to reflect new situation. If we are not
2104 realized, then nobody knows about us anyway... */
2105 if (GTK_WIDGET_REALIZED(self))
2106 hildon_app_apply_client_list(self);
2111 * hildon_app_notify_view_changed:
2112 * @self : a #HildonApp
2113 * @view_ptr : pointer to the view that is switched to
2115 * Updates the X property that contains the currently active view
2117 void hildon_app_notify_view_changed(HildonApp *self, gpointer view_ptr)
2119 g_return_if_fail (HILDON_IS_APP (self));
2120 g_return_if_fail (view_ptr != NULL);
2122 /* We need GdkWindow before we can send X messages */
2123 if (GTK_WIDGET_REALIZED(self))
2125 gulong id = hildon_app_find_view_id(self, view_ptr);
2126 Atom active_view = XInternAtom (GDK_DISPLAY(),
2127 "_NET_ACTIVE_WINDOW", False);
2130 /* Set _NET_ACTIVE_WINDOW for our own toplevel to contain view id */
2131 XChangeProperty(GDK_DISPLAY(), GDK_WINDOW_XID(GTK_WIDGET(self)->window),
2132 active_view, XA_WINDOW, 32, PropModeReplace,
2133 (unsigned char *)&id, 1);
2134 XFlush(GDK_DISPLAY());
2141 * hildon_app_find_view_id:
2142 * @self : a #HildonApp
2143 * @view_ptr : pointer to the view whose ID we want to acquire
2145 * Returns: the ID of the view, or 0 if not found
2147 * Allows mapping of view pointer to its view ID. If NULL is passed
2148 * as the view pointer, returns the ID of the current view.
2150 unsigned long hildon_app_find_view_id(HildonApp *self, gpointer view_ptr)
2152 HildonAppPrivate *priv;
2155 priv = HILDON_APP_GET_PRIVATE (self);
2157 /* If no view is given, find the ID for the currently visible view */
2159 view_ptr = GTK_BIN (self)->child;
2163 /* Iterate through list and search for given view pointer */
2164 for (iter = priv->view_ids; iter; iter = iter->next)
2166 if ( (gpointer)((view_item *)iter->data)->view_ptr == view_ptr)
2167 return (unsigned long)((view_item *)iter->data)->view_id;
2174 * hildon_app_set_killable:
2175 * @self : a #HildonApp
2176 * @killability : truth value indicating whether the app can be killed
2178 * Updates information about whether the application can be killed or not by
2179 * Task Navigator (i.e. whether its statesave is up to date)
2181 void hildon_app_set_killable(HildonApp *self, gboolean killability)
2183 HildonAppPrivate *priv = HILDON_APP_GET_PRIVATE (self);
2184 g_return_if_fail (HILDON_IS_APP (self) );
2186 if (killability != priv->killable)
2188 priv->killable = killability;
2190 /* If we have a window, then we can actually set this
2191 property. Otherwise we wait until we are realized */
2192 if (GTK_WIDGET_REALIZED(self))
2193 hildon_app_apply_killable(self);
2199 * hildon_app_set_ui_manager:
2200 * @self : #HildonApp
2201 * @uim : #GtkUIManager to be set
2203 * Sets the #GtkUIManager assigned to the #HildonApp.
2204 * If @uim is NULL, unsets the current ui manager.
2205 * The @HildonApp holds a reference to the ui manager until
2206 * the @HildonApp is destroyed or unset.
2208 void hildon_app_set_ui_manager(HildonApp *self, GtkUIManager *uim)
2210 HildonAppPrivate *priv;
2212 g_return_if_fail(self && HILDON_IS_APP(self));
2214 priv = HILDON_APP_GET_PRIVATE (self);
2216 /* Release old ui-manager object if such exists */
2217 if (priv->uim != NULL)
2219 g_object_unref (G_OBJECT (priv->uim));
2224 /* If we got new ui-manager (it's perfectly valid not
2225 to give one), acquire reference to it */
2226 if (priv->uim != NULL)
2228 g_object_ref (G_OBJECT (uim));
2231 g_object_notify (G_OBJECT(self), "ui-manager");
2235 * hildon_app_get_ui_manager:
2236 * @self : #HildonApp
2238 * Gets the #GtkUIManager assigned to the #HildonApp.
2240 * Returns: the #GtkUIManager assigned to this application
2241 * or null if no manager is assigned
2243 GtkUIManager *hildon_app_get_ui_manager(HildonApp *self)
2245 HildonAppPrivate *priv;
2247 g_return_val_if_fail(self && HILDON_IS_APP(self), NULL);
2249 priv = HILDON_APP_GET_PRIVATE (self);
2255 * Search for a view with the given id within HildonApp.
2256 * Returns a pointer to the found view, or NULL if not found.
2258 static gpointer find_view(HildonApp *self, unsigned long view_id)
2260 HildonAppPrivate *priv;
2263 priv = HILDON_APP_GET_PRIVATE (self);
2265 /* Iterate through the list of view ids and search given id */
2266 for (iter = priv->view_ids; iter; iter = iter->next)
2268 if ( (unsigned long)((view_item *)iter->data)->view_id == view_id)
2269 return (gpointer)((view_item *)iter->data)->view_ptr;