+static void
+gtk_application_window_update_menubar (GtkApplicationWindow *window)
+{
+ gboolean should_have_menubar;
+ gboolean have_menubar;
+
+ have_menubar = window->priv->menubar != NULL;
+
+ should_have_menubar = window->priv->show_menubar &&
+ (g_menu_model_get_n_items (G_MENU_MODEL (window->priv->app_menu_section)) ||
+ g_menu_model_get_n_items (G_MENU_MODEL (window->priv->menubar_section)));
+
+ if (have_menubar && !should_have_menubar)
+ {
+ gtk_widget_unparent (window->priv->menubar);
+ window->priv->menubar = NULL;
+
+ gtk_widget_queue_resize (GTK_WIDGET (window));
+ }
+
+ if (!have_menubar && should_have_menubar)
+ {
+ GMenu *combined;
+
+ combined = g_menu_new ();
+ g_menu_append_section (combined, NULL, G_MENU_MODEL (window->priv->app_menu_section));
+ g_menu_append_section (combined, NULL, G_MENU_MODEL (window->priv->menubar_section));
+
+ window->priv->menubar = gtk_menu_bar_new_from_model (G_MENU_MODEL (combined));
+ gtk_widget_set_parent (window->priv->menubar, GTK_WIDGET (window));
+ gtk_widget_show_all (window->priv->menubar);
+ g_object_unref (combined);
+
+ gtk_widget_queue_resize (GTK_WIDGET (window));
+ }
+}
+
+static gchar *
+gtk_application_window_get_app_desktop_name (void)
+{
+ gchar *retval = NULL;
+
+#ifdef HAVE_GIO_UNIX
+ GDesktopAppInfo *app_info;
+ const gchar *app_name = NULL;
+ gchar *desktop_file;
+
+ desktop_file = g_strconcat (g_get_prgname (), ".desktop", NULL);
+ app_info = g_desktop_app_info_new (desktop_file);
+ g_free (desktop_file);
+
+ if (app_info != NULL)
+ app_name = g_app_info_get_name (G_APP_INFO (app_info));
+
+ if (app_name != NULL)
+ retval = g_strdup (app_name);
+
+ g_clear_object (&app_info);
+#endif /* HAVE_GIO_UNIX */
+
+ return retval;
+}
+
+static void
+gtk_application_window_update_shell_shows_app_menu (GtkApplicationWindow *window,
+ GtkSettings *settings)
+{
+ gboolean shown_by_shell;
+
+ g_object_get (settings, "gtk-shell-shows-app-menu", &shown_by_shell, NULL);
+
+ if (shown_by_shell)
+ {
+ /* the shell shows it, so don't show it locally */
+ if (g_menu_model_get_n_items (G_MENU_MODEL (window->priv->app_menu_section)) != 0)
+ g_menu_remove (window->priv->app_menu_section, 0);
+ }
+ else
+ {
+ /* the shell does not show it, so make sure we show it */
+ if (g_menu_model_get_n_items (G_MENU_MODEL (window->priv->app_menu_section)) == 0)
+ {
+ GMenuModel *app_menu;
+
+ app_menu = gtk_application_get_app_menu (gtk_window_get_application (GTK_WINDOW (window)));
+
+ if (app_menu != NULL)
+ {
+ const gchar *app_name;
+ gchar *name;
+
+ app_name = g_get_application_name ();
+ if (app_name != g_get_prgname ())
+ {
+ /* the app has set its application name, use it */
+ name = g_strdup (app_name);
+ }
+ else
+ {
+ /* get the name from .desktop file */
+ name = gtk_application_window_get_app_desktop_name ();
+ if (name == NULL)
+ name = g_strdup (_("Application"));
+ }
+
+ g_menu_append_submenu (window->priv->app_menu_section, name, app_menu);
+ g_free (name);
+ }
+ }
+ }
+}
+
+static void
+gtk_application_window_update_shell_shows_menubar (GtkApplicationWindow *window,
+ GtkSettings *settings)
+{
+ gboolean shown_by_shell;
+
+ g_object_get (settings, "gtk-shell-shows-menubar", &shown_by_shell, NULL);
+
+ if (shown_by_shell)
+ {
+ /* the shell shows it, so don't show it locally */
+ if (g_menu_model_get_n_items (G_MENU_MODEL (window->priv->menubar_section)) != 0)
+ g_menu_remove (window->priv->menubar_section, 0);
+ }
+ else
+ {
+ /* the shell does not show it, so make sure we show it */
+ if (g_menu_model_get_n_items (G_MENU_MODEL (window->priv->menubar_section)) == 0)
+ {
+ GMenuModel *menubar;
+
+ menubar = gtk_application_get_menubar (gtk_window_get_application (GTK_WINDOW (window)));
+
+ if (menubar != NULL)
+ g_menu_append_section (window->priv->menubar_section, NULL, menubar);
+ }
+ }
+}
+
+typedef struct {
+ GClosure closure;
+ gchar *action_name;
+ GVariant *parameter;
+} AccelClosure;
+
+static void
+accel_activate (GClosure *closure,
+ GValue *return_value,
+ guint n_param_values,
+ const GValue *param_values,
+ gpointer invocation_hint,
+ gpointer marshal_data)
+{
+ AccelClosure *aclosure = (AccelClosure*)closure;
+ GActionGroup *actions;
+
+ actions = G_ACTION_GROUP (closure->data);
+ if (g_action_group_get_action_enabled (actions, aclosure->action_name))
+ {
+ g_action_group_activate_action (actions, aclosure->action_name, aclosure->parameter);
+
+ /* we handled the accelerator */
+ g_value_set_boolean (return_value, TRUE);
+ }
+}
+
+static void
+free_accel_closures (GtkApplicationWindow *window)
+{
+ GSList *l;
+
+ for (l = window->priv->accel_closures; l; l = l->next)
+ {
+ AccelClosure *closure = l->data;
+
+ gtk_accel_group_disconnect (window->priv->accels, &closure->closure);
+
+ g_object_unref (closure->closure.data);
+ if (closure->parameter)
+ g_variant_unref (closure->parameter);
+ g_free (closure->action_name);
+ g_closure_invalidate (&closure->closure);
+ g_closure_unref (&closure->closure);
+ }
+ g_slist_free (window->priv->accel_closures);
+ window->priv->accel_closures = NULL;
+}
+
+/* Hack. We iterate over the accel map instead of the actions,
+ * in order to pull the parameters out of accel map entries
+ */
+static void
+add_accel_closure (gpointer data,
+ const gchar *accel_path,
+ guint accel_key,
+ GdkModifierType accel_mods,
+ gboolean changed)
+{
+ GtkApplicationWindow *window = data;
+ GActionGroup *actions;
+ const gchar *path;
+ const gchar *p;
+ gchar *action_name;
+ GVariant *parameter;
+ AccelClosure *closure;
+
+ if (accel_key == 0)
+ return;
+
+ if (!g_str_has_prefix (accel_path, "<GAction>/"))
+ return;
+
+ path = accel_path + strlen ("<GAction>/");
+ p = strchr (path, '/');
+ if (p)
+ {
+ action_name = g_strndup (path, p - path);
+ parameter = g_variant_parse (NULL, p + 1, NULL, NULL, NULL);
+ if (!parameter)
+ g_warning ("Failed to parse parameter from '%s'\n", accel_path);
+ }
+ else
+ {
+ action_name = g_strdup (path);
+ parameter = NULL;
+ }
+
+ actions = G_ACTION_GROUP (_gtk_widget_get_action_muxer (GTK_WIDGET (window)));
+ if (g_action_group_has_action (actions, action_name))
+ {
+ closure = (AccelClosure*) g_closure_new_object (sizeof (AccelClosure), g_object_ref (actions));
+ g_closure_set_marshal (&closure->closure, accel_activate);
+
+ closure->action_name = g_strdup (action_name);
+ closure->parameter = parameter ? g_variant_ref_sink (parameter) : NULL;
+
+ window->priv->accel_closures = g_slist_prepend (window->priv->accel_closures, g_closure_ref (&closure->closure));
+ g_closure_sink (&closure->closure);
+
+ gtk_accel_group_connect_by_path (window->priv->accels, accel_path, &closure->closure);
+ }
+ else if (parameter)
+ {
+ g_variant_unref (parameter);
+ }
+
+ g_free (action_name);
+}
+
+static void
+gtk_application_window_update_accels (GtkApplicationWindow *window)
+{
+ free_accel_closures (window);
+
+ gtk_accel_map_foreach (window, add_accel_closure);
+}
+
+static void
+gtk_application_window_shell_shows_app_menu_changed (GObject *object,
+ GParamSpec *pspec,
+ gpointer user_data)
+{
+ GtkApplicationWindow *window = user_data;
+
+ gtk_application_window_update_shell_shows_app_menu (window, GTK_SETTINGS (object));
+ gtk_application_window_update_menubar (window);
+}
+
+static void
+gtk_application_window_shell_shows_menubar_changed (GObject *object,
+ GParamSpec *pspec,
+ gpointer user_data)
+{
+ GtkApplicationWindow *window = user_data;
+
+ gtk_application_window_update_shell_shows_menubar (window, GTK_SETTINGS (object));
+ gtk_application_window_update_menubar (window);
+}
+
+static gchar **
+gtk_application_window_list_actions (GActionGroup *group)
+{
+ GtkApplicationWindow *window = GTK_APPLICATION_WINDOW (group);
+
+ return g_action_group_list_actions (G_ACTION_GROUP (window->priv->actions));
+}
+
+static gboolean
+gtk_application_window_query_action (GActionGroup *group,
+ const gchar *action_name,
+ gboolean *enabled,
+ const GVariantType **parameter_type,
+ const GVariantType **state_type,
+ GVariant **state_hint,
+ GVariant **state)
+{
+ GtkApplicationWindow *window = GTK_APPLICATION_WINDOW (group);
+
+ return g_action_group_query_action (G_ACTION_GROUP (window->priv->actions),
+ action_name, enabled, parameter_type, state_type, state_hint, state);
+}
+
+static void
+gtk_application_window_activate_action (GActionGroup *group,
+ const gchar *action_name,
+ GVariant *parameter)
+{
+ GtkApplicationWindow *window = GTK_APPLICATION_WINDOW (group);
+
+ return g_action_group_activate_action (G_ACTION_GROUP (window->priv->actions), action_name, parameter);
+}
+
+static void
+gtk_application_window_change_action_state (GActionGroup *group,
+ const gchar *action_name,
+ GVariant *state)
+{
+ GtkApplicationWindow *window = GTK_APPLICATION_WINDOW (group);
+
+ return g_action_group_change_action_state (G_ACTION_GROUP (window->priv->actions), action_name, state);
+}
+
+static GAction *
+gtk_application_window_lookup_action (GActionMap *action_map,
+ const gchar *action_name)
+{
+ GtkApplicationWindow *window = GTK_APPLICATION_WINDOW (action_map);
+
+ return g_action_map_lookup_action (G_ACTION_MAP (window->priv->actions), action_name);
+}
+
+static void
+gtk_application_window_add_action (GActionMap *action_map,
+ GAction *action)
+{
+ GtkApplicationWindow *window = GTK_APPLICATION_WINDOW (action_map);
+
+ g_action_map_add_action (G_ACTION_MAP (window->priv->actions), action);
+}
+
+static void
+gtk_application_window_remove_action (GActionMap *action_map,
+ const gchar *action_name)
+{
+ GtkApplicationWindow *window = GTK_APPLICATION_WINDOW (action_map);
+
+ g_action_map_remove_action (G_ACTION_MAP (window->priv->actions), action_name);
+}
+
+static void
+gtk_application_window_group_iface_init (GActionGroupInterface *iface)
+{
+ iface->list_actions = gtk_application_window_list_actions;
+ iface->query_action = gtk_application_window_query_action;
+ iface->activate_action = gtk_application_window_activate_action;
+ iface->change_action_state = gtk_application_window_change_action_state;
+}
+
+static void
+gtk_application_window_map_iface_init (GActionMapInterface *iface)
+{
+ iface->lookup_action = gtk_application_window_lookup_action;
+ iface->add_action = gtk_application_window_add_action;
+ iface->remove_action = gtk_application_window_remove_action;
+}
+
+G_DEFINE_TYPE_WITH_CODE (GtkApplicationWindow, gtk_application_window, GTK_TYPE_WINDOW,
+ G_IMPLEMENT_INTERFACE (G_TYPE_ACTION_GROUP, gtk_application_window_group_iface_init)
+ G_IMPLEMENT_INTERFACE (G_TYPE_ACTION_MAP, gtk_application_window_map_iface_init))