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 * <example id="gtkapplication"><title>A simple application</title>
92 * <xi:include xmlns:xi="http://www.w3.org/2001/XInclude" parse="text" href="../../../../examples/bloatpad.c">
93 * <xi:fallback>FIXME: MISSING XINCLUDE CONTENT</xi:fallback>
105 static guint gtk_application_signals[LAST_SIGNAL];
107 G_DEFINE_TYPE (GtkApplication, gtk_application, G_TYPE_APPLICATION)
109 struct _GtkApplicationPrivate
113 #ifdef GDK_WINDOWING_X11
114 GDBusConnection *session;
115 gchar *window_prefix;
119 #ifdef GDK_WINDOWING_QUARTZ
125 #ifdef GDK_WINDOWING_X11
127 gtk_application_window_added_x11 (GtkApplication *application,
130 if (application->priv->session == NULL)
133 if (GTK_IS_APPLICATION_WINDOW (window))
135 GtkApplicationWindow *app_window = GTK_APPLICATION_WINDOW (window);
138 /* GtkApplicationWindow associates with us when it is first created,
139 * so surely it's not realized yet...
141 g_assert (!gtk_widget_get_realized (GTK_WIDGET (window)));
148 window_id = application->priv->next_id++;
149 window_path = g_strdup_printf ("%s%d", application->priv->window_prefix, window_id);
150 success = gtk_application_window_publish (app_window, application->priv->session, window_path);
151 g_free (window_path);
158 gtk_application_window_removed_x11 (GtkApplication *application,
161 if (application->priv->session == NULL)
164 if (GTK_IS_APPLICATION_WINDOW (window))
165 gtk_application_window_unpublish (GTK_APPLICATION_WINDOW (window));
169 window_prefix_from_appid (const gchar *appid)
171 gchar *appid_path, *iter;
173 appid_path = g_strconcat ("/", appid, "/windows/", NULL);
174 for (iter = appid_path; *iter; iter++)
187 gtk_application_startup_x11 (GtkApplication *application)
189 const gchar *application_id;
191 application_id = g_application_get_application_id (G_APPLICATION (application));
192 application->priv->session = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, NULL);
193 application->priv->window_prefix = window_prefix_from_appid (application_id);
197 gtk_application_shutdown_x11 (GtkApplication *application)
199 g_free (application->priv->window_prefix);
200 application->priv->window_prefix = NULL;
201 if (application->priv->session)
203 g_object_unref (application->priv->session);
204 application->priv->session = NULL;
209 #ifdef GDK_WINDOWING_QUARTZ
211 gtk_application_menu_changed_quartz (GObject *object,
215 GtkApplication *application = GTK_APPLICATION (object);
218 combined = g_menu_new ();
219 g_menu_append_submenu (combined, "Application", g_application_get_app_menu (application));
220 g_menu_append_section (combined, NULL, gtk_application_get_menubar (application));
222 gtk_quartz_set_main_menu (G_MENU_MODEL (combined), G_ACTION_OBSERVABLE (application->priv->muxer));
226 gtk_application_startup_quartz (GtkApplication *application)
228 [NSApp finishLaunching];
230 application->priv->muxer = g_action_muxer_new ();
231 g_action_muxer_insert (application->priv->muxer, "app", G_ACTION_GROUP (application));
233 g_signal_connect (application, "notify::app-menu", G_CALLBACK (gtk_application_menu_changed_quartz), NULL);
234 g_signal_connect (application, "notify::menubar", G_CALLBACK (gtk_application_menu_changed_quartz), NULL);
235 gtk_application_menu_changed_quartz (G_OBJECT (application), NULL, NULL);
239 gtk_application_shutdown_quartz (GtkApplication *application)
241 g_signal_handlers_disconnect_by_func (application, gtk_application_menu_changed_quartz, NULL);
243 g_object_unref (application->priv->muxer);
244 application->priv->muxer = NULL;
248 gtk_application_focus_changed (GtkApplication *application,
251 if (G_IS_ACTION_GROUP (window))
252 g_action_muxer_insert (application->priv->muxer, "win", G_ACTION_GROUP (window));
254 g_action_muxer_remove (application->priv->muxer, "win");
259 gtk_application_focus_in_event_cb (GtkWindow *window,
260 GdkEventFocus *event,
261 GtkApplication *application)
263 GtkApplicationPrivate *priv = application->priv;
266 /* Keep the window list sorted by most-recently-focused. */
267 link = g_list_find (priv->windows, window);
268 if (link != NULL && link != priv->windows)
270 priv->windows = g_list_remove_link (priv->windows, link);
271 priv->windows = g_list_concat (link, priv->windows);
274 #ifdef GDK_WINDOWING_QUARTZ
275 gtk_application_focus_changed (application, window);
282 gtk_application_startup (GApplication *application)
284 G_APPLICATION_CLASS (gtk_application_parent_class)
285 ->startup (application);
289 #ifdef GDK_WINDOWING_X11
290 gtk_application_startup_x11 (GTK_APPLICATION (application));
293 #ifdef GDK_WINDOWING_QUARTZ
294 gtk_application_startup_quartz (GTK_APPLICATION (application));
299 gtk_application_shutdown (GApplication *application)
301 #ifdef GDK_WINDOWING_X11
302 gtk_application_shutdown_x11 (GTK_APPLICATION (application));
305 #ifdef GDK_WINDOWING_QUARTZ
306 gtk_application_shutdown_quartz (GTK_APPLICATION (application));
309 G_APPLICATION_CLASS (gtk_application_parent_class)
310 ->shutdown (application);
314 gtk_application_add_platform_data (GApplication *application,
315 GVariantBuilder *builder)
317 const gchar *startup_id;
319 startup_id = getenv ("DESKTOP_STARTUP_ID");
321 if (startup_id && g_utf8_validate (startup_id, -1, NULL))
322 g_variant_builder_add (builder, "{sv}", "desktop-startup-id",
323 g_variant_new_string (startup_id));
327 gtk_application_before_emit (GApplication *application,
328 GVariant *platform_data)
334 gdk_threads_enter ();
336 g_variant_iter_init (&iter, platform_data);
337 while (g_variant_iter_loop (&iter, "{&sv}", &key, &value))
339 #ifdef GDK_WINDOWING_X11
340 if (strcmp (key, "desktop-startup-id") == 0)
345 display = gdk_display_get_default ();
346 id = g_variant_get_string (value, NULL);
347 if (GDK_IS_X11_DISPLAY (display))
348 gdk_x11_display_set_startup_notification_id (display, id);
355 gtk_application_after_emit (GApplication *application,
356 GVariant *platform_data)
358 gdk_notify_startup_complete ();
360 gdk_threads_leave ();
364 gtk_application_init (GtkApplication *application)
366 application->priv = G_TYPE_INSTANCE_GET_PRIVATE (application,
367 GTK_TYPE_APPLICATION,
368 GtkApplicationPrivate);
372 gtk_application_window_added (GtkApplication *application,
375 GtkApplicationPrivate *priv = application->priv;
377 priv->windows = g_list_prepend (priv->windows, window);
378 gtk_window_set_application (window, application);
379 g_application_hold (G_APPLICATION (application));
381 g_signal_connect (window, "focus-in-event",
382 G_CALLBACK (gtk_application_focus_in_event_cb),
385 #ifdef GDK_WINDOWING_X11
386 gtk_application_window_added_x11 (application, window);
391 gtk_application_window_removed (GtkApplication *application,
394 GtkApplicationPrivate *priv = application->priv;
396 #ifdef GDK_WINDOWING_X11
397 gtk_application_window_removed_x11 (application, window);
400 g_signal_handlers_disconnect_by_func (window,
401 gtk_application_focus_in_event_cb,
404 g_application_release (G_APPLICATION (application));
405 priv->windows = g_list_remove (priv->windows, window);
406 gtk_window_set_application (window, NULL);
410 extract_accel_from_menu_item (GMenuModel *model,
414 GMenuAttributeIter *iter;
417 const gchar *accel = NULL;
418 const gchar *action = NULL;
419 GVariant *target = NULL;
421 iter = g_menu_model_iterate_item_attributes (model, item);
422 while (g_menu_attribute_iter_get_next (iter, &key, &value))
424 if (g_str_equal (key, "action") && g_variant_is_of_type (value, G_VARIANT_TYPE_STRING))
425 action = g_variant_get_string (value, NULL);
426 else if (g_str_equal (key, "accel") && g_variant_is_of_type (value, G_VARIANT_TYPE_STRING))
427 accel = g_variant_get_string (value, NULL);
428 else if (g_str_equal (key, "target"))
429 target = g_variant_ref (value);
430 g_variant_unref (value);
432 g_object_unref (iter);
435 gtk_application_add_accelerator (app, accel, action, target);
438 g_variant_unref (target);
442 extract_accels_from_menu (GMenuModel *model,
450 for (i = 0; i < g_menu_model_get_n_items (model); i++)
452 extract_accel_from_menu_item (model, i, app);
454 iter = g_menu_model_iterate_item_links (model, i);
455 while (g_menu_link_iter_get_next (iter, &key, &m))
457 extract_accels_from_menu (m, app);
460 g_object_unref (iter);
465 gtk_application_notify (GObject *object,
468 if (strcmp (pspec->name, "app-menu") == 0 ||
469 strcmp (pspec->name, "menubar") == 0)
472 g_object_get (object, pspec->name, &model, NULL);
475 extract_accels_from_menu (model, GTK_APPLICATION (object));
476 g_object_unref (model);
480 if (G_OBJECT_CLASS (gtk_application_parent_class)->notify)
481 G_OBJECT_CLASS (gtk_application_parent_class)->notify (object, pspec);
485 gtk_application_class_init (GtkApplicationClass *class)
487 GObjectClass *object_class = G_OBJECT_CLASS (class);
488 GApplicationClass *application_class = G_APPLICATION_CLASS (class);
490 object_class->notify = gtk_application_notify;
492 application_class->add_platform_data = gtk_application_add_platform_data;
493 application_class->before_emit = gtk_application_before_emit;
494 application_class->after_emit = gtk_application_after_emit;
495 application_class->startup = gtk_application_startup;
496 application_class->shutdown = gtk_application_shutdown;
498 class->window_added = gtk_application_window_added;
499 class->window_removed = gtk_application_window_removed;
501 g_type_class_add_private (class, sizeof (GtkApplicationPrivate));
504 * GtkApplication::window-added:
505 * @application: the #GtkApplication which emitted the signal
506 * @window: the newly-added #GtkWindow
508 * Emitted when a #GtkWindow is added to @application through
509 * gtk_application_add_window().
513 gtk_application_signals[WINDOW_ADDED] =
514 g_signal_new ("window-added", GTK_TYPE_APPLICATION, G_SIGNAL_RUN_FIRST,
515 G_STRUCT_OFFSET (GtkApplicationClass, window_added),
517 g_cclosure_marshal_VOID__OBJECT,
518 G_TYPE_NONE, 1, GTK_TYPE_WINDOW);
521 * GtkApplication::window-removed:
522 * @application: the #GtkApplication which emitted the signal
523 * @window: the #GtkWindow that is being removed
525 * Emitted when a #GtkWindow is removed from @application,
526 * either as a side-effect of being destroyed or explicitly
527 * through gtk_application_remove_window().
531 gtk_application_signals[WINDOW_REMOVED] =
532 g_signal_new ("window-removed", GTK_TYPE_APPLICATION, G_SIGNAL_RUN_FIRST,
533 G_STRUCT_OFFSET (GtkApplicationClass, window_removed),
535 g_cclosure_marshal_VOID__OBJECT,
536 G_TYPE_NONE, 1, GTK_TYPE_WINDOW);
540 * gtk_application_new:
541 * @application_id: the application id
542 * @flags: the application flags
544 * Creates a new #GtkApplication instance.
546 * This function calls g_type_init() for you. gtk_init() is called
547 * as soon as the application gets registered as the primary instance.
549 * Note that commandline arguments are not passed to gtk_init().
550 * All GTK+ functionality that is available via commandline arguments
551 * can also be achieved by setting suitable environment variables
552 * such as <envvar>G_DEBUG</envvar>, so this should not be a big
553 * problem. If you absolutely must support GTK+ commandline arguments,
554 * you can explicitly call gtk_init() before creating the application
557 * The application id must be valid. See g_application_id_is_valid().
559 * Returns: a new #GtkApplication instance
562 gtk_application_new (const gchar *application_id,
563 GApplicationFlags flags)
565 g_return_val_if_fail (g_application_id_is_valid (application_id), NULL);
569 return g_object_new (GTK_TYPE_APPLICATION,
570 "application-id", application_id,
576 * gtk_application_add_window:
577 * @application: a #GtkApplication
578 * @window: a #GtkWindow
580 * Adds a window from @application.
582 * This call is equivalent to setting the #GtkWindow:application
583 * property of @window to @application.
585 * Normally, the connection between the application and the window
586 * will remain until the window is destroyed, but you can explicitly
587 * remove it with gtk_application_remove_window().
589 * GTK+ will keep the application running as long as it has
595 gtk_application_add_window (GtkApplication *application,
598 g_return_if_fail (GTK_IS_APPLICATION (application));
600 if (!g_list_find (application->priv->windows, window))
601 g_signal_emit (application,
602 gtk_application_signals[WINDOW_ADDED], 0, window);
606 * gtk_application_remove_window:
607 * @application: a #GtkApplication
608 * @window: a #GtkWindow
610 * Remove a window from @application.
612 * If @window belongs to @application then this call is equivalent to
613 * setting the #GtkWindow:application property of @window to
616 * The application may stop running as a result of a call to this
622 gtk_application_remove_window (GtkApplication *application,
625 g_return_if_fail (GTK_IS_APPLICATION (application));
627 if (g_list_find (application->priv->windows, window))
628 g_signal_emit (application,
629 gtk_application_signals[WINDOW_REMOVED], 0, window);
633 * gtk_application_get_windows:
634 * @application: a #GtkApplication
636 * Gets a list of the #GtkWindows associated with @application.
638 * The list is sorted by most recently focused window, such that the first
639 * element is the currently focused window. (Useful for choosing a parent
640 * for a transient window.)
642 * The list that is returned should not be modified in any way. It will
643 * only remain valid until the next focus change or window creation or
646 * Returns: (element-type GtkWindow) (transfer none): a #GList of #GtkWindow
651 gtk_application_get_windows (GtkApplication *application)
653 g_return_val_if_fail (GTK_IS_APPLICATION (application), NULL);
655 return application->priv->windows;
659 * gtk_application_add_accelerator:
660 * @application: a #GtkApplication
661 * @accelerator: accelerator string
662 * @action_name: the name of the action to activate
663 * @parameter: (allow-none): parameter to pass when activating the action,
664 * or %NULL if the action does not accept an activation parameter
666 * Installs an accelerator that will cause the named action
667 * to be activated when the key combination specificed by @accelerator
670 * @accelerator must be a string that can be parsed by
671 * gtk_accelerator_parse(), e.g. "<Primary>q" or "<Control><Alt>p".
673 * @action_name must be the name of an action as it would be used
674 * in the app menu, i.e. actions that have been added to the application
675 * are referred to with an "app." prefix, and window-specific actions
676 * with a "win." prefix.
678 * GtkApplication also extracts accelerators out of 'accel' attributes
679 * in the #GMenuModels passed to g_application_set_app_menu() and
680 * g_application_set_menubar(), which is usually more convenient
681 * than calling this function for each accelerator.
686 gtk_application_add_accelerator (GtkApplication *application,
687 const gchar *accelerator,
688 const gchar *action_name,
693 GdkModifierType accel_mods;
695 g_return_if_fail (GTK_IS_APPLICATION (application));
697 /* Call this here, since gtk_init() is only getting called in startup() */
698 _gtk_accel_map_init ();
700 gtk_accelerator_parse (accelerator, &accel_key, &accel_mods);
704 g_warning ("Failed to parse accelerator: '%s'\n", accelerator);
708 accel_path = _gtk_accel_path_for_action (action_name, parameter);
710 if (gtk_accel_map_lookup_entry (accel_path, NULL))
711 gtk_accel_map_change_entry (accel_path, accel_key, accel_mods, TRUE);
713 gtk_accel_map_add_entry (accel_path, accel_key, accel_mods);
719 * gtk_application_remove_accelerator:
720 * @application: a #GtkApplication
721 * @action_name: the name of the action to activate
722 * @parameter: (allow-none): parameter to pass when activating the action,
723 * or %NULL if the action does not accept an activation parameter
725 * Removes an accelerator that has been previously added
726 * with gtk_application_add_accelerator().
731 gtk_application_remove_accelerator (GtkApplication *application,
732 const gchar *action_name,
737 g_return_if_fail (GTK_IS_APPLICATION (application));
739 accel_path = _gtk_accel_path_for_action (action_name, parameter);
741 if (!gtk_accel_map_lookup_entry (accel_path, NULL))
743 g_warning ("No accelerator found for '%s'\n", accel_path);
748 gtk_accel_map_change_entry (accel_path, 0, 0, FALSE);
753 * gtk_application_set_app_menu:
754 * @application: a #GtkApplication
755 * @app_menu: (allow-none): a #GMenuModel, or %NULL
757 * Sets or unsets the application menu for @application.
759 * The application menu is a single menu containing items that typically
760 * impact the application as a whole, rather than acting on a specific
761 * window or document. For example, you would expect to see
762 * "Preferences" or "Quit" in an application menu, but not "Save" or
765 * If supported, the application menu will be rendered by the desktop
771 gtk_application_set_app_menu (GtkApplication *application,
772 GMenuModel *app_menu)
774 g_object_set (application, "app-menu", app_menu, NULL);
778 * gtk_application_get_app_menu:
779 * @application: a #GtkApplication
781 * Returns the menu model that has been set with
782 * g_application_set_app_menu().
784 * Returns: the application menu of @application
789 gtk_application_get_app_menu (GtkApplication *application)
791 GMenuModel *app_menu;
793 g_object_get (application, "app-menu", &app_menu, NULL);
794 g_object_unref (app_menu);
800 * gtk_application_set_menubar:
801 * @application: a #GtkApplication
802 * @menubar: (allow-none): a #GMenuModel, or %NULL
804 * Sets or unsets the menubar for windows of @application.
806 * This is a menubar in the traditional sense.
808 * Depending on the desktop environment, this may appear at the top of
809 * each window, or at the top of the screen. In some environments, if
810 * both the application menu and the menubar are set, the application
811 * menu will be presented as if it were the first item of the menubar.
812 * Other environments treat the two as completely separate -- for
813 * example, the application menu may be rendered by the desktop shell
814 * while the menubar (if set) remains in each individual window.
819 gtk_application_set_menubar (GtkApplication *application,
822 g_object_set (application, "menubar", menubar, NULL);
826 * gtk_application_get_menubar:
827 * @application: a #GtkApplication
829 * Returns the menu model that has been set with
830 * g_application_set_menubar().
832 * Returns: the menubar for windows of @application
837 gtk_application_get_menubar (GtkApplication *application)
841 g_object_get (application, "menubar", &menubar, NULL);
842 g_object_unref (menubar);