2 * This file is part of hildon-libs
4 * Copyright (C) 2005, 2006 Nokia Corporation, all rights reserved.
6 * Contact: Michael Dominic Kostrzewa <michael.kostrzewa@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; version 2.1 of
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>
54 #include <gtk/gtktogglebutton.h>
55 #include <gtk/gtkcombobox.h>
60 #include <libmb/mbutil.h>
66 #define TITLE_DELIMITER " - "
69 * 'Magic' values for the titlebar menu area limits
71 #define MENUAREA_LEFT_LIMIT 80
72 #define MENUAREA_RIGHT_LIMIT MENUAREA_LEFT_LIMIT + 307
73 #define MENUAREA_TOP_LIMIT 0
74 #define MENUAREA_BOTTOM_LIMIT 39
76 #define KILLABLE "CANKILL"
78 #define _(String) dgettext(PACKAGE, String)
80 #define HILDON_APP_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), \
81 HILDON_TYPE_APP, HildonAppPrivate));
83 static GtkWindowClass *parent_class;
84 static guint app_signals[HILDON_APP_LAST_SIGNAL] = { 0 };
86 typedef struct _HildonAppPrivate HildonAppPrivate;
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 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 * Handles the key press of the Escape, Increase and Decrease keys. Other keys
1368 * are handled by the parent GtkWidgetClass.
1371 hildon_app_key_press (GtkWidget *widget, GdkEventKey *keyevent)
1373 HildonApp *app = HILDON_APP (widget);
1374 HildonAppView *appview;
1375 HildonAppPrivate *priv = HILDON_APP_GET_PRIVATE(app);
1377 if (HILDON_IS_APPVIEW(GTK_BIN (app)->child))
1379 appview = HILDON_APPVIEW (GTK_BIN (app)->child);
1386 if (keyevent->keyval == GDK_Escape && priv->escape_timeout == 0)
1388 /* Call hildon_app_escape_timeout every 1500ms until it returns FALSE
1389 * and store the relative GSource id. Since hildon_app_escape_timeout
1390 * can only return FALSE, the call will occurr only once.
1392 priv->escape_timeout = g_timeout_add(1500, hildon_app_escape_timeout, app);
1395 /* FIXME: Handling +/- keys here is not usefull. Applications
1396 can equally easily handle the keypress themselves. */
1397 else if (HILDON_KEYEVENT_IS_INCREASE_KEY (keyevent))
1399 _hildon_appview_increase_button_state_changed (appview,
1402 else if (HILDON_KEYEVENT_IS_DECREASE_KEY (keyevent))
1404 _hildon_appview_decrease_button_state_changed (appview,
1408 /* Activate default bindings and let the widget with focus to handle key */
1409 return GTK_WIDGET_CLASS (parent_class)->key_press_event (widget, keyevent);
1413 * Handles the key release event for the Escape, Toolbar and Fullscreen keys.
1416 hildon_app_key_release (GtkWidget *widget, GdkEventKey *keyevent)
1418 HildonApp *app = HILDON_APP (widget);
1419 HildonAppView *appview;
1420 HildonAppPrivate *priv = HILDON_APP_GET_PRIVATE(app);
1422 if (HILDON_IS_APPVIEW(GTK_BIN (app)->child))
1424 appview = HILDON_APPVIEW (GTK_BIN (app)->child);
1431 if (keyevent->keyval == GDK_Escape)
1434 * This will prevent the hildon_app_escape_timeout from being called.
1435 * See hildon_app_escape_timeout and hildon_app_remove_timeout for more.
1437 hildon_app_remove_timeout(priv);
1439 else if (HILDON_KEYEVENT_IS_TOOLBAR_KEY (keyevent))
1441 g_signal_emit_by_name(G_OBJECT(appview),
1442 "toolbar-toggle-request");
1444 else if (HILDON_KEYEVENT_IS_FULLSCREEN_KEY (keyevent))
1446 /* Emit the fullscreen_state_change directly, it'll save one step */
1447 if (hildon_appview_get_fullscreen_key_allowed (appview))
1449 gboolean fullscreen;
1451 fullscreen = hildon_appview_get_fullscreen(appview);
1452 g_signal_emit_by_name(G_OBJECT(appview),
1453 "fullscreen_state_change",
1458 /* FIXME: Should the event be marked as handled if any of the three
1459 above cases took an action */
1461 /* Activate default bindings and let the widget with focus to handle key */
1462 return GTK_WIDGET_CLASS (parent_class)->key_release_event (widget, keyevent);
1466 * Handles the MENU key presses.
1469 hildon_app_key_snooper (GtkWidget *widget, GdkEventKey *keyevent, HildonApp *app)
1471 /* FIXME: Using normal keypress handler would be better choise. All
1472 keyevents come to window anyway, so we would get the same
1473 keys in that way as well, but we wouldn't need to struggle
1474 with grabs (modal dialogs etc). */
1476 /* Menu key handling is done here */
1477 if ( HILDON_KEYEVENT_IS_MENU_KEY (keyevent) ) {
1478 HildonAppView *appview;
1479 HildonAppPrivate *priv;
1480 GtkWidget *toplevel;
1481 GtkWidget *focus = NULL;
1483 /* Don't act on modal dialogs */
1484 toplevel = gtk_widget_get_toplevel (widget);
1485 focus = gtk_window_get_focus(GTK_WINDOW(app));
1487 /* Don't act when comboboxes are active, if a togglebutton
1488 (combobox) has the focus and it is active, we deactivate the
1489 combobox and do nothing */
1490 if (GTK_IS_TOGGLE_BUTTON (focus))
1492 GtkWidget *parent = gtk_widget_get_parent (focus);
1494 if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (focus)) &&
1495 GTK_IS_COMBO_BOX (parent))
1497 gtk_combo_box_popdown (GTK_COMBO_BOX (parent));
1502 /* Don't act when a GtkWindow of a combobox is selected, this
1503 can happen in some applications that change the properties of
1504 the widget focus attribute, WARNING: we are using the name of
1505 the hildon combobox widget to identify the window
1506 (gtkcombobox.c, HILDON_COMBO_BOX_POPUP), if it changes we
1507 must change this name */
1508 if (GTK_IS_WINDOW (widget) &&
1509 !g_ascii_strcasecmp("hildon-combobox-window", gtk_widget_get_name (widget)))
1514 if (GTK_IS_DIALOG (toplevel)
1515 && gtk_window_get_modal (GTK_WINDOW (toplevel)))
1520 appview = HILDON_APPVIEW (GTK_BIN(app)->child);
1521 priv = HILDON_APP_GET_PRIVATE(app);
1523 if ( keyevent->type == GDK_KEY_PRESS ) {
1524 /* Toggle menu on press, avoid key repeat */
1525 if ( priv->lastmenuclick == 0 ){
1526 priv->lastmenuclick = 1;
1527 if (_hildon_appview_toggle_menu(appview,
1528 gtk_get_current_event_time()))
1533 } else if ( keyevent->type == GDK_KEY_RELEASE ) {
1534 /* We got release, so next press is really a new press,
1536 if ( priv->lastmenuclick == 1 ) {
1537 priv->lastmenuclick = 0;
1541 /* Unknown key event */
1545 /* don't stop the key event so that it reaches GTK where it
1546 closes all existing menus that might be open */
1554 * Returns the message_type of the Atom registered with a certain name.
1557 xclient_message_type_check(XClientMessageEvent *cm, const gchar *name)
1559 return cm->message_type == XInternAtom(GDK_DISPLAY(), name, FALSE);
1563 * Returns the GtkWidget associated to a certain Window.
1566 hildon_app_xwindow_lookup_widget(Window xwindow)
1571 window = gdk_xid_table_lookup(xwindow);
1575 gdk_window_get_user_data(window, &widget);
1580 * Let's search a actual main window using tranciency hints.
1581 * Note that there can be several levels of menus/dialogs above
1582 * the actual main window.
1584 static Window get_active_main_window(Window window)
1586 Window parent_window;
1589 gdk_error_trap_push ();
1591 while (XGetTransientForHint(GDK_DISPLAY(), window, &parent_window))
1593 /* The limit > TRANSIENCY_MAXITER ensures that we can't be stuck
1594 here forever if we have circular transiencies for some reason.
1595 Use of _MB_CURRENT_APP_WINDOW might be more elegant... */
1597 if (!parent_window || parent_window == GDK_ROOT_WINDOW() ||
1598 parent_window == window || limit > TRANSIENCY_MAXITER)
1604 window = parent_window;
1609 if (gdk_error_trap_pop ())
1616 * Filters every GDK event first.
1618 static GdkFilterReturn
1619 hildon_app_event_filter (GdkXEvent *xevent, GdkEvent *event, gpointer data)
1622 HildonApp *app = data;
1623 HildonAppPrivate *priv;
1624 HildonAppView *appview = NULL;
1626 XAnyEvent *eventti = xevent;
1628 if (HILDON_IS_APPVIEW (GTK_BIN (app)->child))
1630 appview = HILDON_APPVIEW (GTK_BIN (app)->child);
1633 g_return_val_if_fail (app, GDK_FILTER_CONTINUE);
1634 g_return_val_if_fail (HILDON_IS_APP(app), GDK_FILTER_CONTINUE);
1636 priv = HILDON_APP_GET_PRIVATE(app);
1637 if (eventti->type == ClientMessage)
1639 XClientMessageEvent *cm = xevent;
1641 /* Check if a message indicating a click on titlebar has been
1642 received. Don't open it if mouse is grabbed (eg. modal dialog
1644 _MB_GRAB_TRANSFER is emitted by MatchBox, and signals that a button
1645 has just been released. */
1646 if (xclient_message_type_check(cm, "_MB_GRAB_TRANSFER") &&
1647 HILDON_IS_APPVIEW(appview) &&
1648 gtk_grab_get_current() == NULL &&
1649 !_hildon_appview_menu_visible(appview))
1651 _hildon_appview_toggle_menu(appview, cm->data.l[0]);
1652 return GDK_FILTER_REMOVE;
1654 /* IM_CLOSE is input method specific hack that is really questionable */
1655 else if (xclient_message_type_check(cm, "_HILDON_IM_CLOSE"))
1657 g_signal_emit_by_name(app, "im_close", NULL);
1658 return GDK_FILTER_REMOVE;
1660 /* Task user changed the view through task navigator? */
1661 else if (xclient_message_type_check(cm, "_NET_ACTIVE_WINDOW"))
1663 unsigned long view_id = cm->window;
1664 gpointer view_ptr = find_view(app, view_id);
1666 /* When getting a _NET_ACTIVE_WINDOW signal from the WM we need
1667 * to bring the application to the front */
1668 if (!priv->is_topmost)
1669 g_signal_emit_by_name (G_OBJECT(app), "topmost_status_acquire");
1671 if (HILDON_IS_APPVIEW(view_ptr))
1672 /* Sets the current view to the "window" that the _NET_ACTIVE_WINDOW
1674 hildon_app_set_appview(app, (HILDON_APPVIEW(view_ptr)));
1676 /* there was no view, so we have to switch to an actual application */
1677 g_signal_emit_by_name (G_OBJECT(app), "switch_to", view_ptr);
1679 /* FIXME: This is a hack. This was once just gtk_window_present, but
1680 was changed into this at some day!! */
1681 if (GTK_WIDGET(app)->window)
1683 mb_util_window_activate(GDK_DISPLAY(),
1684 GDK_WINDOW_XID(GTK_WIDGET(app)->window));
1687 /* FIXME: IM hack */
1688 else if (xclient_message_type_check(cm, "_HILDON_IM_CLIPBOARD_COPY"))
1690 Window xwindow = cm->data.l[0];
1691 GtkWidget *widget = hildon_app_xwindow_lookup_widget(xwindow);
1693 g_signal_emit_by_name (G_OBJECT(app), "clipboard_copy", widget);
1695 /* FIXME: IM hack */
1696 else if (xclient_message_type_check(cm, "_HILDON_IM_CLIPBOARD_CUT"))
1698 Window xwindow = cm->data.l[0];
1699 GtkWidget *widget = hildon_app_xwindow_lookup_widget(xwindow);
1701 g_signal_emit_by_name (G_OBJECT(app), "clipboard_cut", widget);
1703 /* FIXME: IM hack */
1704 else if (xclient_message_type_check(cm, "_HILDON_IM_CLIPBOARD_PASTE"))
1706 Window xwindow = cm->data.l[0];
1707 GtkWidget *widget = hildon_app_xwindow_lookup_widget(xwindow);
1709 g_signal_emit_by_name (G_OBJECT(app), "clipboard_paste", widget);
1713 if (eventti->type == ButtonPress)
1716 /* FIXME: This is mysterious bugfix related to problems to open the
1717 application menu (bugzilla N#3204) */
1718 XButtonEvent *bev = (XButtonEvent *)xevent;
1720 if (HILDON_IS_APPVIEW(appview) &&
1721 _hildon_appview_menu_visible(appview) &&
1722 !hildon_appview_get_fullscreen(appview))
1726 if ( (x >= MENUAREA_LEFT_LIMIT) && (x <= MENUAREA_RIGHT_LIMIT) &&
1727 (y >= MENUAREA_TOP_LIMIT) && (y <= MENUAREA_BOTTOM_LIMIT))
1729 _hildon_appview_toggle_menu(appview, bev->time);
1730 return GDK_FILTER_CONTINUE;
1735 /* FIXME: as above */
1736 if (eventti->type == ButtonRelease)
1738 if (HILDON_IS_APPVIEW(appview) &&
1739 _hildon_appview_menu_visible(appview) &&
1740 !hildon_appview_get_fullscreen(appview))
1742 XButtonEvent *bev = (XButtonEvent *)xevent;
1745 if ( (x >= MENUAREA_LEFT_LIMIT) && (x < MENUAREA_RIGHT_LIMIT) &&
1746 (y >= MENUAREA_TOP_LIMIT) && (y <= MENUAREA_BOTTOM_LIMIT))
1748 return GDK_FILTER_REMOVE;
1751 return GDK_FILTER_CONTINUE;
1754 /* Application stacking order changed */
1755 if (eventti->type == PropertyNotify)
1757 Atom active_app_atom =
1758 XInternAtom (GDK_DISPLAY(), "_MB_CURRENT_APP_WINDOW", False);
1759 XPropertyEvent *prop = xevent;
1761 if ((prop->atom == active_app_atom)
1762 && (prop->window == GDK_ROOT_WINDOW()))
1768 unsigned long extra;
1773 unsigned char *char_pointer;
1778 status = XGetWindowProperty(GDK_DISPLAY(), GDK_ROOT_WINDOW(),
1779 active_app_atom, 0L, 16L,
1780 0, XA_WINDOW, &realtype, &format,
1781 &n, &extra, &win.char_pointer);
1782 if (!(status == Success && realtype == XA_WINDOW && format == 32
1783 && n == 1 && win.win != NULL))
1785 if (win.win != NULL)
1786 XFree(win.char_pointer);
1787 return GDK_FILTER_CONTINUE;
1790 my_window = GDK_WINDOW_XID(GTK_WIDGET(app)->window);
1792 /* Are we the topmost one? */
1793 if (win.win[0] == my_window ||
1794 get_active_main_window(win.win[0]) == my_window)
1796 if (!priv->is_topmost)
1797 g_signal_emit_by_name (G_OBJECT(app),
1798 "topmost_status_acquire");
1800 else if (priv->is_topmost)
1802 GtkWidget *focus = gtk_window_get_focus(GTK_WINDOW(app));
1804 /* FIXME: IM hack, IM-module should do this in response to
1805 topmost_status_lose (emission hook?) */
1806 if (GTK_IS_ENTRY(focus))
1807 gtk_im_context_focus_out(GTK_ENTRY(focus)->im_context);
1808 if (GTK_IS_TEXT_VIEW(focus))
1809 gtk_im_context_focus_out(GTK_TEXT_VIEW(focus)->im_context);
1811 g_signal_emit_by_name (app, "topmost_status_lose");
1814 if (win.win != NULL)
1815 XFree(win.char_pointer);
1819 return GDK_FILTER_CONTINUE;
1823 * Sets the GTK Window title to the application's title, or
1824 * combined appview/app title, if two part title is asked.
1827 hildon_app_construct_title (HildonApp *self)
1829 g_return_if_fail (HILDON_IS_APP (self));
1831 if (GTK_WIDGET_REALIZED(self))
1833 HildonAppPrivate *priv;
1835 gchar *concatenated_title = NULL;
1836 HildonAppView *appview;
1838 priv = HILDON_APP_GET_PRIVATE (self);
1839 appview = hildon_app_get_appview(self);
1841 /* FIXME: The subname property is legacy stuff no longer supported by
1842 Matchbox. However, it is still set for the convenience of
1843 the Task Navigator. */
1844 subname = gdk_atom_intern("_MB_WIN_SUB_NAME", FALSE);
1846 if (!appview || !hildon_app_get_two_part_title(self) ||
1847 g_utf8_strlen(hildon_appview_get_title(appview), -1) < 1 )
1849 /* Set an invisible dummy value if there is no appview title */
1850 gdk_property_change (GTK_WIDGET(self)->window, subname,
1851 gdk_atom_intern ("UTF8_STRING", FALSE),
1852 8, GDK_PROP_MODE_REPLACE, (guchar *) " \0", 1);
1853 gtk_window_set_title (GTK_WINDOW(self), priv->title);
1857 gdk_property_change (GTK_WIDGET(self)->window, subname,
1858 gdk_atom_intern ("UTF8_STRING", FALSE),
1859 8, GDK_PROP_MODE_REPLACE,
1860 (guchar *)hildon_appview_get_title(appview),
1861 strlen(hildon_appview_get_title (appview)));
1862 concatenated_title = g_strjoin(TITLE_DELIMITER, priv->title,
1863 hildon_appview_get_title(appview), NULL);
1864 /* priv->title should always be non-null, but check anyway */
1865 if (concatenated_title != NULL)
1867 gtk_window_set_title (GTK_WINDOW(self), concatenated_title);
1868 g_free(concatenated_title);
1875 * Callback function to the topmost_status_acquire signal emitted by
1876 * hildon_app_event_filter function. See it for more details.
1879 hildon_app_real_topmost_status_acquire (HildonApp *self)
1881 HildonAppPrivate *priv;
1882 g_return_if_fail (HILDON_IS_APP (self));
1883 priv = HILDON_APP_GET_PRIVATE (self);
1885 /* FIXME: What is the logic not to update topmost status now? */
1886 if (!GTK_BIN (self)->child)
1889 priv->is_topmost = TRUE;
1893 * Callback function to the topmost_status_lose signal emitted by
1894 * hildon_app_event_filter function. See it for more details.
1897 hildon_app_real_topmost_status_lose (HildonApp *self)
1899 HildonAppPrivate *priv;
1900 g_return_if_fail (HILDON_IS_APP (self));
1901 priv = HILDON_APP_GET_PRIVATE (self);
1903 /* FIXME: What is the logic not to update topmost status now? */
1904 if (!GTK_BIN (self)->child)
1907 priv->is_topmost = FALSE;
1911 hildon_app_real_switch_to (HildonApp *self)
1913 g_return_if_fail (HILDON_IS_APP (self));
1914 /* Do we have to do anything here? */
1919 * hildon_app_set_autoregistration
1920 * @self : a #HildonApp
1921 * @auto_reg : whether the (app)view autoregistration should be active
1923 * Controls the autoregistration/unregistration of (app)views.
1926 void hildon_app_set_autoregistration(HildonApp *self, gboolean auto_reg)
1928 HildonAppPrivate *priv;
1929 g_return_if_fail (HILDON_IS_APP (self));
1931 priv = HILDON_APP_GET_PRIVATE (self);
1932 priv->autoregistration = auto_reg;
1937 * hildon_app_register_view:
1938 * @self : a #HildonApp
1939 * @view_ptr : pointer to the view instance to be registered
1941 * Registers a new view. For appviews, this can be done automatically
1942 * if autoregistration is set.
1945 void hildon_app_register_view(HildonApp *self, gpointer view_ptr)
1947 HildonAppPrivate *priv;
1948 view_item *view_item_inst;
1950 g_return_if_fail (HILDON_IS_APP (self) || view_ptr != NULL);
1952 priv = HILDON_APP_GET_PRIVATE (self);
1954 if (hildon_app_find_view_id(self, view_ptr) == 0)
1956 /* The pointer to the view was unique, so add it to the list */
1957 view_item_inst = g_malloc(sizeof(view_item));
1958 view_item_inst->view_id = priv->view_id_counter;
1959 view_item_inst->view_ptr = view_ptr;
1961 priv->view_id_counter++;
1964 g_slist_append(priv->view_ids, view_item_inst);
1966 /* Update the list of views */
1967 if (GTK_WIDGET_REALIZED(self))
1968 hildon_app_apply_client_list(self);
1974 * hildon_app_register_view_with_id:
1975 * @self : a #HildonApp
1976 * @view_ptr : pointer to the view instance to be registered
1977 * @view_id : the ID of the view
1979 * Registers a new view. Allows the application to specify any ID.
1981 * Returns: TRUE if the view registration succeeded, FALSE otherwise.
1982 * The probable cause of failure is that view with that ID
1986 gboolean hildon_app_register_view_with_id(HildonApp *self,
1988 unsigned long view_id)
1990 view_item *view_item_inst;
1991 HildonAppPrivate *priv;
1992 GSList *list_ptr = NULL;
1994 g_return_val_if_fail (HILDON_IS_APP (self), FALSE);
1995 g_return_val_if_fail (view_ptr, FALSE);
1997 priv = HILDON_APP_GET_PRIVATE (self);
1999 list_ptr = priv->view_ids;
2001 /* Check that the view is not already registered */
2004 if ( (gpointer)((view_item *)list_ptr->data)->view_ptr == view_ptr
2005 && (unsigned long)((view_item *)list_ptr->data)->view_id == view_id)
2009 list_ptr = list_ptr->next;
2012 /* The pointer to the view was unique, so add it to the list */
2013 view_item_inst = g_malloc(sizeof(view_item));
2014 view_item_inst->view_id = view_id;
2015 view_item_inst->view_ptr = view_ptr;
2018 g_slist_append(priv->view_ids, view_item_inst);
2020 priv->view_id_counter++;
2022 /* Finally, update the _NET_CLIENT_LIST property */
2023 if (GTK_WIDGET_REALIZED(self))
2024 hildon_app_apply_client_list(self);
2030 * hildon_app_unregister_view:
2031 * @self : a #HildonApp
2032 * @view_ptr : pointer to the view instance to be unregistered
2034 * Unregisters a view from HildonApp. Done usually when a view is
2035 * destroyed. For appviews, this is can be automatically
2036 * if autoregistration is set.
2038 void hildon_app_unregister_view(HildonApp *self, gpointer view_ptr)
2040 HildonAppPrivate *priv = NULL;
2041 GSList *list_ptr = NULL;
2043 g_return_if_fail (HILDON_IS_APP (self));
2044 g_return_if_fail (view_ptr != NULL);
2046 priv = HILDON_APP_GET_PRIVATE (self);
2048 /* Search the view from the list */
2049 list_ptr = priv->view_ids;
2053 if ( (gpointer)((view_item *)list_ptr->data)->view_ptr == view_ptr)
2055 /* Found the view, kick it off */
2056 g_free (list_ptr->data);
2057 priv->view_ids = g_slist_delete_link(priv->view_ids, list_ptr);
2060 list_ptr = list_ptr->next;
2063 if (GTK_WIDGET_REALIZED(self))
2064 hildon_app_apply_client_list(self);
2069 * hildon_app_unregister_view_with_id:
2070 * @self: a #HildonApp
2071 * @view_id: the ID of the view that should be unregistered
2073 * Unregisters a view with specified ID, if it exists.
2075 void hildon_app_unregister_view_with_id(HildonApp *self,
2076 unsigned long view_id)
2078 HildonAppPrivate *priv;
2079 GSList *list_ptr = NULL;
2081 g_return_if_fail (HILDON_IS_APP (self));
2083 priv = HILDON_APP_GET_PRIVATE (self);
2085 /* Search the view from the list */
2086 list_ptr = priv->view_ids;
2090 if ( (unsigned long)((view_item *)list_ptr->data)->view_id == view_id)
2092 /* Found view with given id, kick it off */
2093 g_free (list_ptr->data);
2094 priv->view_ids = g_slist_delete_link(priv->view_ids, list_ptr);
2097 list_ptr = list_ptr->next;
2100 /* Update client list to reflect new situation. If we are not
2101 realized, then nobody knows about us anyway... */
2102 if (GTK_WIDGET_REALIZED(self))
2103 hildon_app_apply_client_list(self);
2108 * hildon_app_notify_view_changed:
2109 * @self : a #HildonApp
2110 * @view_ptr : pointer to the view that is switched to
2112 * Updates the X property that contains the currently active view
2114 void hildon_app_notify_view_changed(HildonApp *self, gpointer view_ptr)
2116 g_return_if_fail (HILDON_IS_APP (self));
2117 g_return_if_fail (view_ptr != NULL);
2119 /* We need GdkWindow before we can send X messages */
2120 if (GTK_WIDGET_REALIZED(self))
2122 gulong id = hildon_app_find_view_id(self, view_ptr);
2123 Atom active_view = XInternAtom (GDK_DISPLAY(),
2124 "_NET_ACTIVE_WINDOW", False);
2127 /* Set _NET_ACTIVE_WINDOW for our own toplevel to contain view id */
2128 XChangeProperty(GDK_DISPLAY(), GDK_WINDOW_XID(GTK_WIDGET(self)->window),
2129 active_view, XA_WINDOW, 32, PropModeReplace,
2130 (unsigned char *)&id, 1);
2131 XFlush(GDK_DISPLAY());
2138 * hildon_app_find_view_id:
2139 * @self : a #HildonApp
2140 * @view_ptr : pointer to the view whose ID we want to acquire
2142 * Returns: the ID of the view, or 0 if not found
2144 * Allows mapping of view pointer to its view ID. If NULL is passed
2145 * as the view pointer, returns the ID of the current view.
2147 unsigned long hildon_app_find_view_id(HildonApp *self, gpointer view_ptr)
2149 HildonAppPrivate *priv;
2152 priv = HILDON_APP_GET_PRIVATE (self);
2154 /* If no view is given, find the ID for the currently visible view */
2156 view_ptr = GTK_BIN (self)->child;
2160 /* Iterate through list and search for given view pointer */
2161 for (iter = priv->view_ids; iter; iter = iter->next)
2163 if ( (gpointer)((view_item *)iter->data)->view_ptr == view_ptr)
2164 return (unsigned long)((view_item *)iter->data)->view_id;
2171 * hildon_app_set_killable:
2172 * @self : a #HildonApp
2173 * @killability : truth value indicating whether the app can be killed
2175 * Updates information about whether the application can be killed or not by
2176 * Task Navigator (i.e. whether its statesave is up to date)
2178 void hildon_app_set_killable(HildonApp *self, gboolean killability)
2180 HildonAppPrivate *priv = HILDON_APP_GET_PRIVATE (self);
2181 g_return_if_fail (HILDON_IS_APP (self) );
2183 if (killability != priv->killable)
2185 priv->killable = killability;
2187 /* If we have a window, then we can actually set this
2188 property. Otherwise we wait until we are realized */
2189 if (GTK_WIDGET_REALIZED(self))
2190 hildon_app_apply_killable(self);
2196 * hildon_app_set_ui_manager:
2197 * @self : #HildonApp
2198 * @uim : #GtkUIManager to be set
2200 * Sets the #GtkUIManager assigned to the #HildonApp.
2201 * If @uim is NULL, unsets the current ui manager.
2202 * The @HildonApp holds a reference to the ui manager until
2203 * the @HildonApp is destroyed or unset.
2205 void hildon_app_set_ui_manager(HildonApp *self, GtkUIManager *uim)
2207 HildonAppPrivate *priv;
2209 g_return_if_fail(self && HILDON_IS_APP(self));
2211 priv = HILDON_APP_GET_PRIVATE (self);
2213 /* Release old ui-manager object if such exists */
2214 if (priv->uim != NULL)
2216 g_object_unref (G_OBJECT (priv->uim));
2221 /* If we got new ui-manager (it's perfectly valid not
2222 to give one), acquire reference to it */
2223 if (priv->uim != NULL)
2225 g_object_ref (G_OBJECT (uim));
2228 g_object_notify (G_OBJECT(self), "ui-manager");
2232 * hildon_app_get_ui_manager:
2233 * @self : #HildonApp
2235 * Gets the #GtkUIManager assigned to the #HildonApp.
2237 * Returns: the #GtkUIManager assigned to this application
2238 * or null if no manager is assigned
2240 GtkUIManager *hildon_app_get_ui_manager(HildonApp *self)
2242 HildonAppPrivate *priv;
2244 g_return_val_if_fail(self && HILDON_IS_APP(self), NULL);
2246 priv = HILDON_APP_GET_PRIVATE (self);
2252 * Search for a view with the given id within HildonApp.
2253 * Returns a pointer to the found view, or NULL if not found.
2255 static gpointer find_view(HildonApp *self, unsigned long view_id)
2257 HildonAppPrivate *priv;
2260 priv = HILDON_APP_GET_PRIVATE (self);
2262 /* Iterate through the list of view ids and search given id */
2263 for (iter = priv->view_ids; iter; iter = iter->next)
2265 if ( (unsigned long)((view_item *)iter->data)->view_id == view_id)
2266 return (gpointer)((view_item *)iter->data)->view_ptr;