2 * Copyright © 2010 Codethink Limited
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the licence, or (at your option) any later version.
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library; if not, write to the
16 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17 * Boston, MA 02111-1307, USA.
19 * Author: Ryan Lortie <desrt@desrt.ca>
24 #include "gtkapplication.h"
32 #include "gtkapplicationprivate.h"
33 #include "gtkmarshalers.h"
35 #include "gtkaccelmapprivate.h"
36 #include "gactionmuxer.h"
38 #ifdef GDK_WINDOWING_QUARTZ
39 #include "gtkquartz-menu.h"
40 #import <Cocoa/Cocoa.h>
44 #ifdef GDK_WINDOWING_X11
45 #include <gdk/x11/gdkx.h>
49 * SECTION:gtkapplication
50 * @title: GtkApplication
51 * @short_description: Application class
53 * #GtkApplication is a class that handles many important aspects
54 * of a GTK+ application in a convenient fashion, without enforcing
55 * a one-size-fits-all application model.
57 * Currently, GtkApplication handles GTK+ initialization, application
58 * uniqueness, provides some basic scriptability and desktop shell integration
59 * by exporting actions and menus and manages a list of toplevel windows whose
60 * life-cycle is automatically tied to the life-cycle of your application.
62 * While GtkApplication works fine with plain #GtkWindows, it is recommended
63 * to use it together with #GtkApplicationWindow.
65 * When GDK threads are enabled, GtkApplication will acquire the GDK
66 * lock when invoking actions that arrive from other processes. The GDK
67 * lock is not touched for local action invocations. In order to have
68 * actions invoked in a predictable context it is therefore recommended
69 * that the GDK lock be held while invoking actions locally with
70 * g_action_group_activate_action(). The same applies to actions
71 * associated with #GtkApplicationWindow and to the 'activate' and
72 * 'open' #GApplication methods.
74 * To set an application menu on a GtkApplication, use
75 * g_application_set_app_menu(). The #GMenuModel that this function
76 * expects is usually constructed using #GtkBuilder, as seen in the
77 * following example. To set a menubar that will be automatically picked
78 * up by #GApplicationWindows, use g_application_set_menubar(). GTK+
79 * makes these menus appear as expected, depending on the platform
80 * the application is running on.
82 * <figure label="Menu integration in OS X">
83 * <graphic fileref="bloatpad-osx.png" format="PNG"/>
86 * <figure label="Menu integration in GNOME">
87 * <graphic fileref="bloatpad-gnome.png" format="PNG"/>
90 * <figure label="Menu integration in Xfce">
91 * <graphic fileref="bloatpad-xfce.png" format="PNG"/>
94 * <example id="gtkapplication"><title>A simple application</title>
96 * <xi:include xmlns:xi="http://www.w3.org/2001/XInclude" parse="text" href="../../../../examples/bloatpad.c">
97 * <xi:fallback>FIXME: MISSING XINCLUDE CONTENT</xi:fallback>
109 static guint gtk_application_signals[LAST_SIGNAL];
111 G_DEFINE_TYPE (GtkApplication, gtk_application, G_TYPE_APPLICATION)
113 struct _GtkApplicationPrivate
117 #ifdef GDK_WINDOWING_X11
118 GDBusConnection *session;
119 gchar *window_prefix;
123 #ifdef GDK_WINDOWING_QUARTZ
129 #ifdef GDK_WINDOWING_X11
131 gtk_application_window_added_x11 (GtkApplication *application,
134 if (application->priv->session == NULL)
137 if (GTK_IS_APPLICATION_WINDOW (window))
139 GtkApplicationWindow *app_window = GTK_APPLICATION_WINDOW (window);
142 /* GtkApplicationWindow associates with us when it is first created,
143 * so surely it's not realized yet...
145 g_assert (!gtk_widget_get_realized (GTK_WIDGET (window)));
152 window_id = application->priv->next_id++;
153 window_path = g_strdup_printf ("%s%d", application->priv->window_prefix, window_id);
154 success = gtk_application_window_publish (app_window, application->priv->session, window_path);
155 g_free (window_path);
162 gtk_application_window_removed_x11 (GtkApplication *application,
165 if (application->priv->session == NULL)
168 if (GTK_IS_APPLICATION_WINDOW (window))
169 gtk_application_window_unpublish (GTK_APPLICATION_WINDOW (window));
173 window_prefix_from_appid (const gchar *appid)
175 gchar *appid_path, *iter;
177 appid_path = g_strconcat ("/", appid, "/windows/", NULL);
178 for (iter = appid_path; *iter; iter++)
191 gtk_application_startup_x11 (GtkApplication *application)
193 const gchar *application_id;
195 application_id = g_application_get_application_id (G_APPLICATION (application));
196 application->priv->session = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, NULL);
197 application->priv->window_prefix = window_prefix_from_appid (application_id);
201 gtk_application_shutdown_x11 (GtkApplication *application)
203 g_free (application->priv->window_prefix);
204 application->priv->window_prefix = NULL;
205 if (application->priv->session)
207 g_object_unref (application->priv->session);
208 application->priv->session = NULL;
213 #ifdef GDK_WINDOWING_QUARTZ
215 gtk_application_menu_changed_quartz (GObject *object,
219 GtkApplication *application = GTK_APPLICATION (object);
222 combined = g_menu_new ();
223 g_menu_append_submenu (combined, "Application", g_application_get_app_menu (application));
224 g_menu_append_section (combined, NULL, gtk_application_get_menubar (application));
226 gtk_quartz_set_main_menu (G_MENU_MODEL (combined), G_ACTION_OBSERVABLE (application->priv->muxer));
230 gtk_application_startup_quartz (GtkApplication *application)
232 [NSApp finishLaunching];
234 application->priv->muxer = g_action_muxer_new ();
235 g_action_muxer_insert (application->priv->muxer, "app", G_ACTION_GROUP (application));
237 g_signal_connect (application, "notify::app-menu", G_CALLBACK (gtk_application_menu_changed_quartz), NULL);
238 g_signal_connect (application, "notify::menubar", G_CALLBACK (gtk_application_menu_changed_quartz), NULL);
239 gtk_application_menu_changed_quartz (G_OBJECT (application), NULL, NULL);
243 gtk_application_shutdown_quartz (GtkApplication *application)
245 g_signal_handlers_disconnect_by_func (application, gtk_application_menu_changed_quartz, NULL);
247 g_object_unref (application->priv->muxer);
248 application->priv->muxer = NULL;
252 gtk_application_focus_changed (GtkApplication *application,
255 if (G_IS_ACTION_GROUP (window))
256 g_action_muxer_insert (application->priv->muxer, "win", G_ACTION_GROUP (window));
258 g_action_muxer_remove (application->priv->muxer, "win");
263 gtk_application_focus_in_event_cb (GtkWindow *window,
264 GdkEventFocus *event,
265 GtkApplication *application)
267 GtkApplicationPrivate *priv = application->priv;
270 /* Keep the window list sorted by most-recently-focused. */
271 link = g_list_find (priv->windows, window);
272 if (link != NULL && link != priv->windows)
274 priv->windows = g_list_remove_link (priv->windows, link);
275 priv->windows = g_list_concat (link, priv->windows);
278 #ifdef GDK_WINDOWING_QUARTZ
279 gtk_application_focus_changed (application, window);
286 gtk_application_startup (GApplication *application)
288 G_APPLICATION_CLASS (gtk_application_parent_class)
289 ->startup (application);
293 #ifdef GDK_WINDOWING_X11
294 gtk_application_startup_x11 (GTK_APPLICATION (application));
297 #ifdef GDK_WINDOWING_QUARTZ
298 gtk_application_startup_quartz (GTK_APPLICATION (application));
303 gtk_application_shutdown (GApplication *application)
305 #ifdef GDK_WINDOWING_X11
306 gtk_application_shutdown_x11 (GTK_APPLICATION (application));
309 #ifdef GDK_WINDOWING_QUARTZ
310 gtk_application_shutdown_quartz (GTK_APPLICATION (application));
313 G_APPLICATION_CLASS (gtk_application_parent_class)
314 ->shutdown (application);
318 gtk_application_add_platform_data (GApplication *application,
319 GVariantBuilder *builder)
321 const gchar *startup_id;
323 startup_id = getenv ("DESKTOP_STARTUP_ID");
325 if (startup_id && g_utf8_validate (startup_id, -1, NULL))
326 g_variant_builder_add (builder, "{sv}", "desktop-startup-id",
327 g_variant_new_string (startup_id));
331 gtk_application_before_emit (GApplication *application,
332 GVariant *platform_data)
338 gdk_threads_enter ();
340 g_variant_iter_init (&iter, platform_data);
341 while (g_variant_iter_loop (&iter, "{&sv}", &key, &value))
343 #ifdef GDK_WINDOWING_X11
344 if (strcmp (key, "desktop-startup-id") == 0)
349 display = gdk_display_get_default ();
350 id = g_variant_get_string (value, NULL);
351 if (GDK_IS_X11_DISPLAY (display))
352 gdk_x11_display_set_startup_notification_id (display, id);
359 gtk_application_after_emit (GApplication *application,
360 GVariant *platform_data)
362 gdk_notify_startup_complete ();
364 gdk_threads_leave ();
368 gtk_application_init (GtkApplication *application)
370 application->priv = G_TYPE_INSTANCE_GET_PRIVATE (application,
371 GTK_TYPE_APPLICATION,
372 GtkApplicationPrivate);
376 gtk_application_window_added (GtkApplication *application,
379 GtkApplicationPrivate *priv = application->priv;
381 priv->windows = g_list_prepend (priv->windows, window);
382 gtk_window_set_application (window, application);
383 g_application_hold (G_APPLICATION (application));
385 g_signal_connect (window, "focus-in-event",
386 G_CALLBACK (gtk_application_focus_in_event_cb),
389 #ifdef GDK_WINDOWING_X11
390 gtk_application_window_added_x11 (application, window);
395 gtk_application_window_removed (GtkApplication *application,
398 GtkApplicationPrivate *priv = application->priv;
400 #ifdef GDK_WINDOWING_X11
401 gtk_application_window_removed_x11 (application, window);
404 g_signal_handlers_disconnect_by_func (window,
405 gtk_application_focus_in_event_cb,
408 g_application_release (G_APPLICATION (application));
409 priv->windows = g_list_remove (priv->windows, window);
410 gtk_window_set_application (window, NULL);
414 extract_accel_from_menu_item (GMenuModel *model,
418 GMenuAttributeIter *iter;
421 const gchar *accel = NULL;
422 const gchar *action = NULL;
423 GVariant *target = NULL;
425 iter = g_menu_model_iterate_item_attributes (model, item);
426 while (g_menu_attribute_iter_get_next (iter, &key, &value))
428 if (g_str_equal (key, "action") && g_variant_is_of_type (value, G_VARIANT_TYPE_STRING))
429 action = g_variant_get_string (value, NULL);
430 else if (g_str_equal (key, "accel") && g_variant_is_of_type (value, G_VARIANT_TYPE_STRING))
431 accel = g_variant_get_string (value, NULL);
432 else if (g_str_equal (key, "target"))
433 target = g_variant_ref (value);
434 g_variant_unref (value);
436 g_object_unref (iter);
439 gtk_application_add_accelerator (app, accel, action, target);
442 g_variant_unref (target);
446 extract_accels_from_menu (GMenuModel *model,
454 for (i = 0; i < g_menu_model_get_n_items (model); i++)
456 extract_accel_from_menu_item (model, i, app);
458 iter = g_menu_model_iterate_item_links (model, i);
459 while (g_menu_link_iter_get_next (iter, &key, &m))
461 extract_accels_from_menu (m, app);
464 g_object_unref (iter);
469 gtk_application_notify (GObject *object,
472 if (strcmp (pspec->name, "app-menu") == 0 ||
473 strcmp (pspec->name, "menubar") == 0)
476 g_object_get (object, pspec->name, &model, NULL);
479 extract_accels_from_menu (model, GTK_APPLICATION (object));
480 g_object_unref (model);
484 if (G_OBJECT_CLASS (gtk_application_parent_class)->notify)
485 G_OBJECT_CLASS (gtk_application_parent_class)->notify (object, pspec);
489 gtk_application_class_init (GtkApplicationClass *class)
491 GObjectClass *object_class = G_OBJECT_CLASS (class);
492 GApplicationClass *application_class = G_APPLICATION_CLASS (class);
494 object_class->notify = gtk_application_notify;
496 application_class->add_platform_data = gtk_application_add_platform_data;
497 application_class->before_emit = gtk_application_before_emit;
498 application_class->after_emit = gtk_application_after_emit;
499 application_class->startup = gtk_application_startup;
500 application_class->shutdown = gtk_application_shutdown;
502 class->window_added = gtk_application_window_added;
503 class->window_removed = gtk_application_window_removed;
505 g_type_class_add_private (class, sizeof (GtkApplicationPrivate));
508 * GtkApplication::window-added:
509 * @application: the #GtkApplication which emitted the signal
510 * @window: the newly-added #GtkWindow
512 * Emitted when a #GtkWindow is added to @application through
513 * gtk_application_add_window().
517 gtk_application_signals[WINDOW_ADDED] =
518 g_signal_new ("window-added", GTK_TYPE_APPLICATION, G_SIGNAL_RUN_FIRST,
519 G_STRUCT_OFFSET (GtkApplicationClass, window_added),
521 g_cclosure_marshal_VOID__OBJECT,
522 G_TYPE_NONE, 1, GTK_TYPE_WINDOW);
525 * GtkApplication::window-removed:
526 * @application: the #GtkApplication which emitted the signal
527 * @window: the #GtkWindow that is being removed
529 * Emitted when a #GtkWindow is removed from @application,
530 * either as a side-effect of being destroyed or explicitly
531 * through gtk_application_remove_window().
535 gtk_application_signals[WINDOW_REMOVED] =
536 g_signal_new ("window-removed", GTK_TYPE_APPLICATION, G_SIGNAL_RUN_FIRST,
537 G_STRUCT_OFFSET (GtkApplicationClass, window_removed),
539 g_cclosure_marshal_VOID__OBJECT,
540 G_TYPE_NONE, 1, GTK_TYPE_WINDOW);
544 * gtk_application_new:
545 * @application_id: the application id
546 * @flags: the application flags
548 * Creates a new #GtkApplication instance.
550 * This function calls g_type_init() for you. gtk_init() is called
551 * as soon as the application gets registered as the primary instance.
553 * Note that commandline arguments are not passed to gtk_init().
554 * All GTK+ functionality that is available via commandline arguments
555 * can also be achieved by setting suitable environment variables
556 * such as <envvar>G_DEBUG</envvar>, so this should not be a big
557 * problem. If you absolutely must support GTK+ commandline arguments,
558 * you can explicitly call gtk_init() before creating the application
561 * The application id must be valid. See g_application_id_is_valid().
563 * Returns: a new #GtkApplication instance
566 gtk_application_new (const gchar *application_id,
567 GApplicationFlags flags)
569 g_return_val_if_fail (g_application_id_is_valid (application_id), NULL);
573 return g_object_new (GTK_TYPE_APPLICATION,
574 "application-id", application_id,
580 * gtk_application_add_window:
581 * @application: a #GtkApplication
582 * @window: a #GtkWindow
584 * Adds a window from @application.
586 * This call is equivalent to setting the #GtkWindow:application
587 * property of @window to @application.
589 * Normally, the connection between the application and the window
590 * will remain until the window is destroyed, but you can explicitly
591 * remove it with gtk_application_remove_window().
593 * GTK+ will keep the application running as long as it has
599 gtk_application_add_window (GtkApplication *application,
602 g_return_if_fail (GTK_IS_APPLICATION (application));
604 if (!g_list_find (application->priv->windows, window))
605 g_signal_emit (application,
606 gtk_application_signals[WINDOW_ADDED], 0, window);
610 * gtk_application_remove_window:
611 * @application: a #GtkApplication
612 * @window: a #GtkWindow
614 * Remove a window from @application.
616 * If @window belongs to @application then this call is equivalent to
617 * setting the #GtkWindow:application property of @window to
620 * The application may stop running as a result of a call to this
626 gtk_application_remove_window (GtkApplication *application,
629 g_return_if_fail (GTK_IS_APPLICATION (application));
631 if (g_list_find (application->priv->windows, window))
632 g_signal_emit (application,
633 gtk_application_signals[WINDOW_REMOVED], 0, window);
637 * gtk_application_get_windows:
638 * @application: a #GtkApplication
640 * Gets a list of the #GtkWindows associated with @application.
642 * The list is sorted by most recently focused window, such that the first
643 * element is the currently focused window. (Useful for choosing a parent
644 * for a transient window.)
646 * The list that is returned should not be modified in any way. It will
647 * only remain valid until the next focus change or window creation or
650 * Returns: (element-type GtkWindow) (transfer none): a #GList of #GtkWindow
655 gtk_application_get_windows (GtkApplication *application)
657 g_return_val_if_fail (GTK_IS_APPLICATION (application), NULL);
659 return application->priv->windows;
663 * gtk_application_add_accelerator:
664 * @application: a #GtkApplication
665 * @accelerator: accelerator string
666 * @action_name: the name of the action to activate
667 * @parameter: (allow-none): parameter to pass when activating the action,
668 * or %NULL if the action does not accept an activation parameter
670 * Installs an accelerator that will cause the named action
671 * to be activated when the key combination specificed by @accelerator
674 * @accelerator must be a string that can be parsed by
675 * gtk_accelerator_parse(), e.g. "<Primary>q" or "<Control><Alt>p".
677 * @action_name must be the name of an action as it would be used
678 * in the app menu, i.e. actions that have been added to the application
679 * are referred to with an "app." prefix, and window-specific actions
680 * with a "win." prefix.
682 * GtkApplication also extracts accelerators out of 'accel' attributes
683 * in the #GMenuModels passed to g_application_set_app_menu() and
684 * g_application_set_menubar(), which is usually more convenient
685 * than calling this function for each accelerator.
690 gtk_application_add_accelerator (GtkApplication *application,
691 const gchar *accelerator,
692 const gchar *action_name,
697 GdkModifierType accel_mods;
699 g_return_if_fail (GTK_IS_APPLICATION (application));
701 /* Call this here, since gtk_init() is only getting called in startup() */
702 _gtk_accel_map_init ();
704 gtk_accelerator_parse (accelerator, &accel_key, &accel_mods);
708 g_warning ("Failed to parse accelerator: '%s'\n", accelerator);
712 accel_path = _gtk_accel_path_for_action (action_name, parameter);
714 if (gtk_accel_map_lookup_entry (accel_path, NULL))
715 gtk_accel_map_change_entry (accel_path, accel_key, accel_mods, TRUE);
717 gtk_accel_map_add_entry (accel_path, accel_key, accel_mods);
723 * gtk_application_remove_accelerator:
724 * @application: a #GtkApplication
725 * @action_name: the name of the action to activate
726 * @parameter: (allow-none): parameter to pass when activating the action,
727 * or %NULL if the action does not accept an activation parameter
729 * Removes an accelerator that has been previously added
730 * with gtk_application_add_accelerator().
735 gtk_application_remove_accelerator (GtkApplication *application,
736 const gchar *action_name,
741 g_return_if_fail (GTK_IS_APPLICATION (application));
743 accel_path = _gtk_accel_path_for_action (action_name, parameter);
745 if (!gtk_accel_map_lookup_entry (accel_path, NULL))
747 g_warning ("No accelerator found for '%s'\n", accel_path);
752 gtk_accel_map_change_entry (accel_path, 0, 0, FALSE);
757 * gtk_application_set_app_menu:
758 * @application: a #GtkApplication
759 * @model: (allow-none): a #GMenuModel, or %NULL
761 * Sets or unsets the application menu for @application.
763 * The application menu is a single menu containing items that typically
764 * impact the application as a whole, rather than acting on a specific
765 * window or document. For example, you would expect to see
766 * "Preferences" or "Quit" in an application menu, but not "Save" or
769 * If supported, the application menu will be rendered by the desktop
775 gtk_application_set_app_menu (GtkApplication *application,
778 g_object_set (application, "app-menu", model, NULL);
782 * gtk_application_get_app_menu:
783 * @application: a #GtkApplication
785 * Returns the menu model that has been set with
786 * g_application_set_app_menu().
788 * Returns: the application menu of @application
793 gtk_application_get_app_menu (GtkApplication *application)
795 GMenuModel *app_menu;
797 g_object_get (application, "app-menu", &app_menu, NULL);
798 g_object_unref (app_menu);
804 * gtk_application_set_menubar:
805 * @application: a #GtkApplication
806 * @model: (allow-none): a #GMenuModel, or %NULL
808 * Sets or unsets the menubar for windows of @application.
810 * This is a menubar in the traditional sense.
812 * Depending on the desktop environment, this may appear at the top of
813 * each window, or at the top of the screen. In some environments, if
814 * both the application menu and the menubar are set, the application
815 * menu will be presented as if it were the first item of the menubar.
816 * Other environments treat the two as completely separate -- for
817 * example, the application menu may be rendered by the desktop shell
818 * while the menubar (if set) remains in each individual window.
823 gtk_application_set_menubar (GtkApplication *application,
826 g_object_set (application, "menubar", model, NULL);
830 * gtk_application_get_menubar:
831 * @application: a #GtkApplication
833 * Returns the menu model that has been set with
834 * g_application_set_menubar().
836 * Returns: the menubar for windows of @application
841 gtk_application_get_menubar (GtkApplication *application)
845 g_object_get (application, "menubar", &menubar, NULL);
846 g_object_unref (menubar);