]> Pileus Git - ~andy/gtk/blob - gtk/gtkapplication.c
GtkApplication: add menu API
[~andy/gtk] / gtk / gtkapplication.c
1 /*
2  * Copyright © 2010 Codethink Limited
3  *
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.
8  *
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.
13  *
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.
18  *
19  * Author: Ryan Lortie <desrt@desrt.ca>
20  */
21
22 #include "config.h"
23
24 #include "gtkapplication.h"
25
26 #include <stdlib.h>
27 #ifdef HAVE_UNISTD_H
28 #include <unistd.h>
29 #endif
30 #include <string.h>
31
32 #include "gtkapplicationprivate.h"
33 #include "gtkmarshalers.h"
34 #include "gtkmain.h"
35 #include "gtkaccelmapprivate.h"
36 #include "gactionmuxer.h"
37
38 #ifdef GDK_WINDOWING_QUARTZ
39 #include "gtkquartz-menu.h"
40 #import <Cocoa/Cocoa.h>
41 #endif
42
43 #include <gdk/gdk.h>
44 #ifdef GDK_WINDOWING_X11
45 #include <gdk/x11/gdkx.h>
46 #endif
47
48 /**
49  * SECTION:gtkapplication
50  * @title: GtkApplication
51  * @short_description: Application class
52  *
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.
56  *
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.
61  *
62  * While GtkApplication works fine with plain #GtkWindows, it is recommended
63  * to use it together with #GtkApplicationWindow.
64  *
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.
73  *
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.
81  *
82  * <figure label="Menu integration in OS X">
83  * <graphic fileref="bloatpad-osx.png" format="PNG"/>
84  * </figure>
85  *
86  * <figure label="Menu integration in GNOME">
87  * <graphic fileref="bloatpad-gnome.png" format="PNG"/>
88  * </figure>
89  *
90  * <example id="gtkapplication"><title>A simple application</title>
91  * <programlisting>
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>
94  * </xi:include>
95  * </programlisting>
96  * </example>
97  */
98
99 enum {
100   WINDOW_ADDED,
101   WINDOW_REMOVED,
102   LAST_SIGNAL
103 };
104
105 static guint gtk_application_signals[LAST_SIGNAL];
106
107 G_DEFINE_TYPE (GtkApplication, gtk_application, G_TYPE_APPLICATION)
108
109 struct _GtkApplicationPrivate
110 {
111   GList *windows;
112
113 #ifdef GDK_WINDOWING_X11
114   GDBusConnection *session;
115   gchar *window_prefix;
116   guint next_id;
117 #endif
118
119 #ifdef GDK_WINDOWING_QUARTZ
120   GActionMuxer *muxer;
121   GMenu *combined;
122 #endif
123 };
124
125 #ifdef GDK_WINDOWING_X11
126 static void
127 gtk_application_window_added_x11 (GtkApplication *application,
128                                   GtkWindow      *window)
129 {
130   if (application->priv->session == NULL)
131     return;
132
133   if (GTK_IS_APPLICATION_WINDOW (window))
134     {
135       GtkApplicationWindow *app_window = GTK_APPLICATION_WINDOW (window);
136       gboolean success;
137
138       /* GtkApplicationWindow associates with us when it is first created,
139        * so surely it's not realized yet...
140        */
141       g_assert (!gtk_widget_get_realized (GTK_WIDGET (window)));
142
143       do
144         {
145           gchar *window_path;
146           guint window_id;
147
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);
152         }
153       while (!success);
154     }
155 }
156
157 static void
158 gtk_application_window_removed_x11 (GtkApplication *application,
159                                     GtkWindow      *window)
160 {
161   if (application->priv->session == NULL)
162     return;
163
164   if (GTK_IS_APPLICATION_WINDOW (window))
165     gtk_application_window_unpublish (GTK_APPLICATION_WINDOW (window));
166 }
167
168 static gchar *
169 window_prefix_from_appid (const gchar *appid)
170 {
171   gchar *appid_path, *iter;
172
173   appid_path = g_strconcat ("/", appid, "/windows/", NULL);
174   for (iter = appid_path; *iter; iter++)
175     {
176       if (*iter == '.')
177         *iter = '/';
178
179       if (*iter == '-')
180         *iter = '_';
181     }
182
183   return appid_path;
184 }
185
186 static void
187 gtk_application_startup_x11 (GtkApplication *application)
188 {
189   const gchar *application_id;
190
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);
194 }
195
196 static void
197 gtk_application_shutdown_x11 (GtkApplication *application)
198 {
199   g_free (application->priv->window_prefix);
200   application->priv->window_prefix = NULL;
201   if (application->priv->session)
202     {
203       g_object_unref (application->priv->session);
204       application->priv->session = NULL;
205     }
206 }
207 #endif
208
209 #ifdef GDK_WINDOWING_QUARTZ
210 static void
211 gtk_application_menu_changed_quartz (GObject    *object,
212                                      GParamSpec *pspec,
213                                      gpointer    user_data)
214 {
215   GtkApplication *application = GTK_APPLICATION (object);
216   GMenu *combined;
217
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));
221
222   gtk_quartz_set_main_menu (G_MENU_MODEL (combined), G_ACTION_OBSERVABLE (application->priv->muxer));
223 }
224
225 static void
226 gtk_application_startup_quartz (GtkApplication *application)
227 {
228   [NSApp finishLaunching];
229
230   application->priv->muxer = g_action_muxer_new ();
231   g_action_muxer_insert (application->priv->muxer, "app", G_ACTION_GROUP (application));
232
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);
236 }
237
238 static void
239 gtk_application_shutdown_quartz (GtkApplication *application)
240 {
241   g_signal_handlers_disconnect_by_func (application, gtk_application_menu_changed_quartz, NULL);
242
243   g_object_unref (application->priv->muxer);
244   application->priv->muxer = NULL;
245 }
246
247 static void
248 gtk_application_focus_changed (GtkApplication *application,
249                                GtkWindow      *window)
250 {
251   if (G_IS_ACTION_GROUP (window))
252     g_action_muxer_insert (application->priv->muxer, "win", G_ACTION_GROUP (window));
253   else
254     g_action_muxer_remove (application->priv->muxer, "win");
255 }
256 #endif
257
258 static gboolean
259 gtk_application_focus_in_event_cb (GtkWindow      *window,
260                                    GdkEventFocus  *event,
261                                    GtkApplication *application)
262 {
263   GtkApplicationPrivate *priv = application->priv;
264   GList *link;
265
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)
269     {
270       priv->windows = g_list_remove_link (priv->windows, link);
271       priv->windows = g_list_concat (link, priv->windows);
272     }
273
274 #ifdef GDK_WINDOWING_QUARTZ
275   gtk_application_focus_changed (application, window);
276 #endif
277
278   return FALSE;
279 }
280
281 static void
282 gtk_application_startup (GApplication *application)
283 {
284   G_APPLICATION_CLASS (gtk_application_parent_class)
285     ->startup (application);
286
287   gtk_init (0, 0);
288
289 #ifdef GDK_WINDOWING_X11
290   gtk_application_startup_x11 (GTK_APPLICATION (application));
291 #endif
292
293 #ifdef GDK_WINDOWING_QUARTZ
294   gtk_application_startup_quartz (GTK_APPLICATION (application));
295 #endif
296 }
297
298 static void
299 gtk_application_shutdown (GApplication *application)
300 {
301 #ifdef GDK_WINDOWING_X11
302   gtk_application_shutdown_x11 (GTK_APPLICATION (application));
303 #endif
304
305 #ifdef GDK_WINDOWING_QUARTZ
306   gtk_application_shutdown_quartz (GTK_APPLICATION (application));
307 #endif
308
309   G_APPLICATION_CLASS (gtk_application_parent_class)
310     ->shutdown (application);
311 }
312
313 static void
314 gtk_application_add_platform_data (GApplication    *application,
315                                    GVariantBuilder *builder)
316 {
317   const gchar *startup_id;
318
319   startup_id = getenv ("DESKTOP_STARTUP_ID");
320   
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));
324 }
325
326 static void
327 gtk_application_before_emit (GApplication *application,
328                              GVariant     *platform_data)
329 {
330   GVariantIter iter;
331   const gchar *key;
332   GVariant *value;
333
334   gdk_threads_enter ();
335
336   g_variant_iter_init (&iter, platform_data);
337   while (g_variant_iter_loop (&iter, "{&sv}", &key, &value))
338     {
339 #ifdef GDK_WINDOWING_X11
340       if (strcmp (key, "desktop-startup-id") == 0)
341         {
342           GdkDisplay *display;
343           const gchar *id;
344
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);
349        }
350 #endif
351     }
352 }
353
354 static void
355 gtk_application_after_emit (GApplication *application,
356                             GVariant     *platform_data)
357 {
358   gdk_notify_startup_complete ();
359
360   gdk_threads_leave ();
361 }
362
363 static void
364 gtk_application_init (GtkApplication *application)
365 {
366   application->priv = G_TYPE_INSTANCE_GET_PRIVATE (application,
367                                                    GTK_TYPE_APPLICATION,
368                                                    GtkApplicationPrivate);
369 }
370
371 static void
372 gtk_application_window_added (GtkApplication *application,
373                               GtkWindow      *window)
374 {
375   GtkApplicationPrivate *priv = application->priv;
376
377   priv->windows = g_list_prepend (priv->windows, window);
378   gtk_window_set_application (window, application);
379   g_application_hold (G_APPLICATION (application));
380
381   g_signal_connect (window, "focus-in-event",
382                     G_CALLBACK (gtk_application_focus_in_event_cb),
383                     application);
384
385 #ifdef GDK_WINDOWING_X11
386   gtk_application_window_added_x11 (application, window);
387 #endif
388 }
389
390 static void
391 gtk_application_window_removed (GtkApplication *application,
392                                 GtkWindow      *window)
393 {
394   GtkApplicationPrivate *priv = application->priv;
395
396 #ifdef GDK_WINDOWING_X11
397   gtk_application_window_removed_x11 (application, window);
398 #endif
399
400   g_signal_handlers_disconnect_by_func (window,
401                                         gtk_application_focus_in_event_cb,
402                                         application);
403
404   g_application_release (G_APPLICATION (application));
405   priv->windows = g_list_remove (priv->windows, window);
406   gtk_window_set_application (window, NULL);
407 }
408
409 static void
410 extract_accel_from_menu_item (GMenuModel     *model,
411                               gint            item,
412                               GtkApplication *app)
413 {
414   GMenuAttributeIter *iter;
415   const gchar *key;
416   GVariant *value;
417   const gchar *accel = NULL;
418   const gchar *action = NULL;
419   GVariant *target = NULL;
420
421   iter = g_menu_model_iterate_item_attributes (model, item);
422   while (g_menu_attribute_iter_get_next (iter, &key, &value))
423     {
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);
431     }
432   g_object_unref (iter);
433
434   if (accel && action)
435     gtk_application_add_accelerator (app, accel, action, target);
436
437   if (target)
438     g_variant_unref (target);
439 }
440
441 static void
442 extract_accels_from_menu (GMenuModel     *model,
443                           GtkApplication *app)
444 {
445   gint i;
446   GMenuLinkIter *iter;
447   const gchar *key;
448   GMenuModel *m;
449
450   for (i = 0; i < g_menu_model_get_n_items (model); i++)
451     {
452       extract_accel_from_menu_item (model, i, app);
453
454       iter = g_menu_model_iterate_item_links (model, i);
455       while (g_menu_link_iter_get_next (iter, &key, &m))
456         {
457           extract_accels_from_menu (m, app);
458           g_object_unref (m);
459         }
460       g_object_unref (iter);
461     }
462 }
463
464 static void
465 gtk_application_notify (GObject    *object,
466                         GParamSpec *pspec)
467 {
468   if (strcmp (pspec->name, "app-menu") == 0 ||
469       strcmp (pspec->name, "menubar") == 0)
470     {
471       GMenuModel *model;
472       g_object_get (object, pspec->name, &model, NULL);
473       if (model)
474         {
475           extract_accels_from_menu (model, GTK_APPLICATION (object));
476           g_object_unref (model);
477         }
478     }
479
480   if (G_OBJECT_CLASS (gtk_application_parent_class)->notify)
481     G_OBJECT_CLASS (gtk_application_parent_class)->notify (object, pspec);
482 }
483
484 static void
485 gtk_application_class_init (GtkApplicationClass *class)
486 {
487   GObjectClass *object_class = G_OBJECT_CLASS (class);
488   GApplicationClass *application_class = G_APPLICATION_CLASS (class);
489
490   object_class->notify = gtk_application_notify;
491
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;
497
498   class->window_added = gtk_application_window_added;
499   class->window_removed = gtk_application_window_removed;
500
501   g_type_class_add_private (class, sizeof (GtkApplicationPrivate));
502
503   /**
504    * GtkApplication::window-added:
505    * @application: the #GtkApplication which emitted the signal
506    * @window: the newly-added #GtkWindow
507    *
508    * Emitted when a #GtkWindow is added to @application through
509    * gtk_application_add_window().
510    *
511    * Since: 3.2
512    */
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),
516                   NULL, NULL,
517                   g_cclosure_marshal_VOID__OBJECT,
518                   G_TYPE_NONE, 1, GTK_TYPE_WINDOW);
519
520   /**
521    * GtkApplication::window-removed:
522    * @application: the #GtkApplication which emitted the signal
523    * @window: the #GtkWindow that is being removed
524    *
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().
528    *
529    * Since: 3.2
530    */
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),
534                   NULL, NULL,
535                   g_cclosure_marshal_VOID__OBJECT,
536                   G_TYPE_NONE, 1, GTK_TYPE_WINDOW);
537 }
538
539 /**
540  * gtk_application_new:
541  * @application_id: the application id
542  * @flags: the application flags
543  *
544  * Creates a new #GtkApplication instance.
545  *
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.
548  *
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
555  * instance.
556  *
557  * The application id must be valid. See g_application_id_is_valid().
558  *
559  * Returns: a new #GtkApplication instance
560  */
561 GtkApplication *
562 gtk_application_new (const gchar       *application_id,
563                      GApplicationFlags  flags)
564 {
565   g_return_val_if_fail (g_application_id_is_valid (application_id), NULL);
566
567   g_type_init ();
568
569   return g_object_new (GTK_TYPE_APPLICATION,
570                        "application-id", application_id,
571                        "flags", flags,
572                        NULL);
573 }
574
575 /**
576  * gtk_application_add_window:
577  * @application: a #GtkApplication
578  * @window: a #GtkWindow
579  *
580  * Adds a window from @application.
581  *
582  * This call is equivalent to setting the #GtkWindow:application
583  * property of @window to @application.
584  *
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().
588  *
589  * GTK+ will keep the application running as long as it has
590  * any windows.
591  *
592  * Since: 3.0
593  **/
594 void
595 gtk_application_add_window (GtkApplication *application,
596                             GtkWindow      *window)
597 {
598   g_return_if_fail (GTK_IS_APPLICATION (application));
599
600   if (!g_list_find (application->priv->windows, window))
601     g_signal_emit (application,
602                    gtk_application_signals[WINDOW_ADDED], 0, window);
603 }
604
605 /**
606  * gtk_application_remove_window:
607  * @application: a #GtkApplication
608  * @window: a #GtkWindow
609  *
610  * Remove a window from @application.
611  *
612  * If @window belongs to @application then this call is equivalent to
613  * setting the #GtkWindow:application property of @window to
614  * %NULL.
615  *
616  * The application may stop running as a result of a call to this
617  * function.
618  *
619  * Since: 3.0
620  **/
621 void
622 gtk_application_remove_window (GtkApplication *application,
623                                GtkWindow      *window)
624 {
625   g_return_if_fail (GTK_IS_APPLICATION (application));
626
627   if (g_list_find (application->priv->windows, window))
628     g_signal_emit (application,
629                    gtk_application_signals[WINDOW_REMOVED], 0, window);
630 }
631
632 /**
633  * gtk_application_get_windows:
634  * @application: a #GtkApplication
635  *
636  * Gets a list of the #GtkWindows associated with @application.
637  *
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.)
641  *
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
644  * deletion.
645  *
646  * Returns: (element-type GtkWindow) (transfer none): a #GList of #GtkWindow
647  *
648  * Since: 3.0
649  **/
650 GList *
651 gtk_application_get_windows (GtkApplication *application)
652 {
653   g_return_val_if_fail (GTK_IS_APPLICATION (application), NULL);
654
655   return application->priv->windows;
656 }
657
658 /**
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
665  *
666  * Installs an accelerator that will cause the named action
667  * to be activated when the key combination specificed by @accelerator
668  * is pressed.
669  *
670  * @accelerator must be a string that can be parsed by
671  * gtk_accelerator_parse(), e.g. "<Primary>q" or "<Control><Alt>p".
672  *
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.
677  *
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.
682  *
683  * Since: 3.4
684  */
685 void
686 gtk_application_add_accelerator (GtkApplication *application,
687                                  const gchar    *accelerator,
688                                  const gchar    *action_name,
689                                  GVariant       *parameter)
690 {
691   gchar *accel_path;
692   guint accel_key;
693   GdkModifierType accel_mods;
694
695   g_return_if_fail (GTK_IS_APPLICATION (application));
696
697   /* Call this here, since gtk_init() is only getting called in startup() */
698   _gtk_accel_map_init ();
699
700   gtk_accelerator_parse (accelerator, &accel_key, &accel_mods);
701
702   if (accel_key == 0)
703     {
704       g_warning ("Failed to parse accelerator: '%s'\n", accelerator);
705       return;
706     }
707
708   accel_path = _gtk_accel_path_for_action (action_name, parameter);
709
710   if (gtk_accel_map_lookup_entry (accel_path, NULL))
711     gtk_accel_map_change_entry (accel_path, accel_key, accel_mods, TRUE);
712   else
713     gtk_accel_map_add_entry (accel_path, accel_key, accel_mods);
714
715   g_free (accel_path);
716 }
717
718 /**
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
724  *
725  * Removes an accelerator that has been previously added
726  * with gtk_application_add_accelerator().
727  *
728  * Since: 3.4
729  */
730 void
731 gtk_application_remove_accelerator (GtkApplication *application,
732                                     const gchar    *action_name,
733                                     GVariant       *parameter)
734 {
735   gchar *accel_path;
736
737   g_return_if_fail (GTK_IS_APPLICATION (application));
738
739   accel_path = _gtk_accel_path_for_action (action_name, parameter);
740
741   if (!gtk_accel_map_lookup_entry (accel_path, NULL))
742     {
743       g_warning ("No accelerator found for '%s'\n", accel_path);
744       g_free (accel_path);
745       return;
746     }
747
748   gtk_accel_map_change_entry (accel_path, 0, 0, FALSE);
749   g_free (accel_path);
750 }
751
752 /**
753  * gtk_application_set_app_menu:
754  * @application: a #GtkApplication
755  * @app_menu: (allow-none): a #GMenuModel, or %NULL
756  *
757  * Sets or unsets the application menu for @application.
758  *
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
763  * "Print".
764  *
765  * If supported, the application menu will be rendered by the desktop
766  * environment.
767  *
768  * Since: 3.4
769  */
770 void
771 gtk_application_set_app_menu (GtkApplication *application,
772                               GMenuModel     *app_menu)
773 {
774   g_object_set (application, "app-menu", app_menu, NULL);
775 }
776
777 /**
778  * gtk_application_get_app_menu:
779  * @application: a #GtkApplication
780  *
781  * Returns the menu model that has been set with
782  * g_application_set_app_menu().
783  *
784  * Returns: the application menu of @application
785  *
786  * Since: 3.4
787  */
788 GMenuModel *
789 gtk_application_get_app_menu (GtkApplication *application)
790 {
791   GMenuModel *app_menu;
792
793   g_object_get (application, "app-menu", &app_menu, NULL);
794   g_object_unref (app_menu);
795
796   return app_menu;
797 }
798
799 /**
800  * gtk_application_set_menubar:
801  * @application: a #GtkApplication
802  * @menubar: (allow-none): a #GMenuModel, or %NULL
803  *
804  * Sets or unsets the menubar for windows of @application.
805  *
806  * This is a menubar in the traditional sense.
807  *
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.
815  *
816  * Since: 3.4
817  */
818 void
819 gtk_application_set_menubar (GtkApplication *application,
820                              GMenuModel     *menubar)
821 {
822   g_object_set (application, "menubar", menubar, NULL);
823 }
824
825 /**
826  * gtk_application_get_menubar:
827  * @application: a #GtkApplication
828  *
829  * Returns the menu model that has been set with
830  * g_application_set_menubar().
831  *
832  * Returns: the menubar for windows of @application
833  *
834  * Since: 3.4
835  */
836 GMenuModel *
837 gtk_application_get_menubar (GtkApplication *application)
838 {
839   GMenuModel *menubar;
840
841   g_object_get (application, "menubar", &menubar, NULL);
842   g_object_unref (menubar);
843
844   return menubar;
845 }