Compare commits
2 Commits
wip/menus-
...
wip/menus
Author | SHA1 | Date | |
---|---|---|---|
![]() |
3fd70e37bd | ||
![]() |
5580cfaf63 |
@@ -243,8 +243,6 @@ const AppMenuButton = new Lang.Class({
|
||||
|
||||
this._menuManager = menuManager;
|
||||
this._targetApp = null;
|
||||
this._appMenuNotifyId = 0;
|
||||
this._actionGroupNotifyId = 0;
|
||||
|
||||
let bin = new St.Bin({ name: 'appMenu' });
|
||||
this.actor.add_actor(bin);
|
||||
@@ -521,15 +519,6 @@ const AppMenuButton = new Lang.Class({
|
||||
this._iconBox.hide();
|
||||
this._label.setText('');
|
||||
|
||||
if (this._appMenuNotifyId)
|
||||
this._targetApp.disconnect(this._appMenuNotifyId);
|
||||
if (this._actionGroupNotifyId)
|
||||
this._targetApp.disconnect(this._actionGroupNotifyId);
|
||||
if (targetApp) {
|
||||
this._appMenuNotifyId = targetApp.connect('notify::menu', Lang.bind(this, this._sync));
|
||||
this._actionGroupNotifyId = targetApp.connect('notify::action-group', Lang.bind(this, this._sync));
|
||||
}
|
||||
|
||||
this._targetApp = targetApp;
|
||||
let icon = targetApp.get_faded_icon(2 * PANEL_ICON_SIZE);
|
||||
|
||||
|
@@ -1777,7 +1777,15 @@ const RemoteMenu = new Lang.Class({
|
||||
item = new PopupSwitchMenuItem(label, action.state.get_boolean());
|
||||
action.items.push(item);
|
||||
specificSignalId = item.connect('toggled', Lang.bind(this, function(item) {
|
||||
this.actionGroup.activate_action(action_id, null);
|
||||
this.actionGroup.change_action_state(action_id, GLib.Variant.new_boolean(item.state));
|
||||
}));
|
||||
break;
|
||||
case 'd':
|
||||
item = new PopupSliderMenuItem(label, action.state.get_double());
|
||||
action.items.push(item);
|
||||
// value-changed is emitted for each motion-event, maybe an idle is more appropriate here?
|
||||
specificSignalId = item.connect('value-changed', Lang.bind(this, function(item) {
|
||||
this.actionGroup.change_action_state(action_id, GLib.Variant.new_double(item.value));
|
||||
}));
|
||||
break;
|
||||
case 's':
|
||||
@@ -1786,7 +1794,7 @@ const RemoteMenu = new Lang.Class({
|
||||
action.items.push(item);
|
||||
item.setShowDot(action.state.deep_unpack() == item._remoteTarget);
|
||||
specificSignalId = item.connect('activate', Lang.bind(this, function(item) {
|
||||
this.actionGroup.activate_action(action_id, GLib.Variant.new_string(item._remoteTarget));
|
||||
this.actionGroup.change_action_state(action_id, GLib.Variant.new_string(item._remoteTarget));
|
||||
}));
|
||||
break;
|
||||
default:
|
||||
|
@@ -121,18 +121,9 @@ shell_public_headers_h = \
|
||||
shell-wm.h \
|
||||
shell-xfixes-cursor.h
|
||||
|
||||
shell_private_sources = \
|
||||
gactionmuxer.h \
|
||||
gactionmuxer.c \
|
||||
gactionobservable.h \
|
||||
gactionobservable.c \
|
||||
gactionobserver.h \
|
||||
gactionobserver.c
|
||||
|
||||
libgnome_shell_la_SOURCES = \
|
||||
$(shell_built_sources) \
|
||||
$(shell_public_headers_h) \
|
||||
$(shell_private_sources) \
|
||||
shell-app-private.h \
|
||||
shell-app-system-private.h \
|
||||
shell-embedded-window-private.h \
|
||||
@@ -166,12 +157,10 @@ libgnome_shell_la_SOURCES = \
|
||||
shell-util.c \
|
||||
shell-window-tracker.c \
|
||||
shell-wm.c \
|
||||
shell-xfixes-cursor.c \
|
||||
$(NULL)
|
||||
|
||||
shell-xfixes-cursor.c
|
||||
|
||||
libgnome_shell_la_gir_sources = \
|
||||
$(filter-out %-private.h $(shell_private_sources), $(shell_public_headers_h) $(libgnome_shell_la_SOURCES))
|
||||
$(filter-out %-private.h $(shell_recorder_non_gir_sources), $(shell_public_headers_h) $(libgnome_shell_la_SOURCES))
|
||||
|
||||
gnome_shell_real_SOURCES = \
|
||||
main.c
|
||||
@@ -186,16 +175,12 @@ shell_recorder_sources = \
|
||||
shell-recorder.h
|
||||
|
||||
# Custom element is an internal detail
|
||||
shell_recorder_non_gir_sources = \
|
||||
shell-recorder-src.c \
|
||||
shell-recorder-src.h
|
||||
|
||||
if BUILD_RECORDER
|
||||
libgnome_shell_la_SOURCES += $(shell_recorder_sources)
|
||||
|
||||
shell_recorder_private_sources = \
|
||||
shell-recorder-src.c \
|
||||
shell-recorder-src.h \
|
||||
$(NULL)
|
||||
|
||||
shell_private_sources += $(shell_recorder_private_sources)
|
||||
libgnome_shell_la_SOURCES += $(shell_recorder_sources) $(shell_recorder_non_gir_sources)
|
||||
|
||||
noinst_PROGRAMS += test-recorder
|
||||
|
||||
@@ -203,7 +188,7 @@ test_recorder_CPPFLAGS = $(TEST_SHELL_RECORDER_CFLAGS)
|
||||
test_recorder_LDADD = $(TEST_SHELL_RECORDER_LIBS)
|
||||
|
||||
test_recorder_SOURCES = \
|
||||
$(shell_recorder_sources) $(shell_recorder_private_sources) \
|
||||
$(shell_recorder_sources) $(shell_recorder_non_gir_sources) \
|
||||
test-recorder.c
|
||||
endif BUILD_RECORDER
|
||||
|
||||
|
@@ -1,533 +0,0 @@
|
||||
/*
|
||||
* Copyright © 2011 Canonical Limited
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the licence, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the
|
||||
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
||||
* Boston, MA 02111-1307, USA.
|
||||
*
|
||||
* Author: Ryan Lortie <desrt@desrt.ca>
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include "gactionmuxer.h"
|
||||
|
||||
#include "gactionobservable.h"
|
||||
#include "gactionobserver.h"
|
||||
|
||||
#include <clutter/clutter.h>
|
||||
|
||||
#include <string.h>
|
||||
|
||||
/*
|
||||
* SECTION:gactionmuxer
|
||||
* @short_description: Aggregate and monitor several action groups
|
||||
*
|
||||
* #GActionMuxer is a #GActionGroup and #GActionObservable that is
|
||||
* capable of containing other #GActionGroup instances.
|
||||
*
|
||||
* The typical use is aggregating all of the actions applicable to a
|
||||
* particular context into a single action group, with namespacing.
|
||||
*
|
||||
* Consider the case of two action groups -- one containing actions
|
||||
* applicable to an entire application (such as 'quit') and one
|
||||
* containing actions applicable to a particular window in the
|
||||
* application (such as 'fullscreen').
|
||||
*
|
||||
* In this case, each of these action groups could be added to a
|
||||
* #GActionMuxer with the prefixes "app" and "win", respectively. This
|
||||
* would expose the actions as "app.quit" and "win.fullscreen" on the
|
||||
* #GActionGroup interface presented by the #GActionMuxer.
|
||||
*
|
||||
* Activations and state change requests on the #GActionMuxer are wired
|
||||
* through to the underlying action group in the expected way.
|
||||
*
|
||||
* This class is typically only used at the site of "consumption" of
|
||||
* actions (eg: when displaying a menu that contains many actions on
|
||||
* different objects).
|
||||
*/
|
||||
|
||||
static void g_action_muxer_group_iface_init (GActionGroupInterface *iface);
|
||||
static void g_action_muxer_observable_iface_init (GActionObservableInterface *iface);
|
||||
|
||||
typedef GObjectClass GActionMuxerClass;
|
||||
|
||||
struct _GActionMuxer
|
||||
{
|
||||
GObject parent_instance;
|
||||
|
||||
GHashTable *actions;
|
||||
GHashTable *groups;
|
||||
};
|
||||
|
||||
G_DEFINE_TYPE_WITH_CODE (GActionMuxer, g_action_muxer, G_TYPE_OBJECT,
|
||||
G_IMPLEMENT_INTERFACE (G_TYPE_ACTION_GROUP, g_action_muxer_group_iface_init)
|
||||
G_IMPLEMENT_INTERFACE (G_TYPE_ACTION_OBSERVABLE, g_action_muxer_observable_iface_init))
|
||||
|
||||
typedef struct
|
||||
{
|
||||
GActionMuxer *muxer;
|
||||
GSList *watchers;
|
||||
gchar *fullname;
|
||||
} Action;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
GActionMuxer *muxer;
|
||||
GActionGroup *group;
|
||||
gchar *prefix;
|
||||
gulong handler_ids[4];
|
||||
} Group;
|
||||
|
||||
static gchar **
|
||||
g_action_muxer_list_actions (GActionGroup *action_group)
|
||||
{
|
||||
GActionMuxer *muxer = G_ACTION_MUXER (action_group);
|
||||
|
||||
return (gchar **) muxer->groups;
|
||||
}
|
||||
|
||||
static Group *
|
||||
g_action_muxer_find_group (GActionMuxer *muxer,
|
||||
const gchar **name)
|
||||
{
|
||||
const gchar *dot;
|
||||
gchar *prefix;
|
||||
Group *group;
|
||||
|
||||
dot = strchr (*name, '.');
|
||||
|
||||
if (!dot)
|
||||
return NULL;
|
||||
|
||||
prefix = g_strndup (*name, dot - *name);
|
||||
group = g_hash_table_lookup (muxer->groups, prefix);
|
||||
g_free (prefix);
|
||||
|
||||
*name = dot + 1;
|
||||
|
||||
return group;
|
||||
}
|
||||
|
||||
static Action *
|
||||
g_action_muxer_lookup_action (GActionMuxer *muxer,
|
||||
const gchar *prefix,
|
||||
const gchar *action_name,
|
||||
gchar **fullname)
|
||||
{
|
||||
Action *action;
|
||||
|
||||
*fullname = g_strconcat (prefix, ".", action_name, NULL);
|
||||
action = g_hash_table_lookup (muxer->actions, *fullname);
|
||||
|
||||
return action;
|
||||
}
|
||||
|
||||
static void
|
||||
g_action_muxer_action_enabled_changed (GActionGroup *action_group,
|
||||
const gchar *action_name,
|
||||
gboolean enabled,
|
||||
gpointer user_data)
|
||||
{
|
||||
Group *group = user_data;
|
||||
gchar *fullname;
|
||||
Action *action;
|
||||
GSList *node;
|
||||
|
||||
action = g_action_muxer_lookup_action (group->muxer, group->prefix, action_name, &fullname);
|
||||
for (node = action ? action->watchers : NULL; node; node = node->next)
|
||||
g_action_observer_action_enabled_changed (node->data, G_ACTION_OBSERVABLE (group->muxer), fullname, enabled);
|
||||
g_action_group_action_enabled_changed (G_ACTION_GROUP (group->muxer), fullname, enabled);
|
||||
g_free (fullname);
|
||||
}
|
||||
|
||||
static void
|
||||
g_action_muxer_action_state_changed (GActionGroup *action_group,
|
||||
const gchar *action_name,
|
||||
GVariant *state,
|
||||
gpointer user_data)
|
||||
{
|
||||
Group *group = user_data;
|
||||
gchar *fullname;
|
||||
Action *action;
|
||||
GSList *node;
|
||||
|
||||
action = g_action_muxer_lookup_action (group->muxer, group->prefix, action_name, &fullname);
|
||||
for (node = action ? action->watchers : NULL; node; node = node->next)
|
||||
g_action_observer_action_state_changed (node->data, G_ACTION_OBSERVABLE (group->muxer), fullname, state);
|
||||
g_action_group_action_state_changed (G_ACTION_GROUP (group->muxer), fullname, state);
|
||||
g_free (fullname);
|
||||
}
|
||||
|
||||
static void
|
||||
g_action_muxer_action_added (GActionGroup *action_group,
|
||||
const gchar *action_name,
|
||||
gpointer user_data)
|
||||
{
|
||||
const GVariantType *parameter_type;
|
||||
Group *group = user_data;
|
||||
gboolean enabled;
|
||||
GVariant *state;
|
||||
|
||||
if (g_action_group_query_action (group->group, action_name, &enabled, ¶meter_type, NULL, NULL, &state))
|
||||
{
|
||||
gchar *fullname;
|
||||
Action *action;
|
||||
GSList *node;
|
||||
|
||||
action = g_action_muxer_lookup_action (group->muxer, group->prefix, action_name, &fullname);
|
||||
|
||||
for (node = action ? action->watchers : NULL; node; node = node->next)
|
||||
g_action_observer_action_added (node->data,
|
||||
G_ACTION_OBSERVABLE (group->muxer),
|
||||
fullname, parameter_type, enabled, state);
|
||||
|
||||
g_action_group_action_added (G_ACTION_GROUP (group->muxer), fullname);
|
||||
|
||||
if (state)
|
||||
g_variant_unref (state);
|
||||
|
||||
g_free (fullname);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
g_action_muxer_action_removed (GActionGroup *action_group,
|
||||
const gchar *action_name,
|
||||
gpointer user_data)
|
||||
{
|
||||
Group *group = user_data;
|
||||
gchar *fullname;
|
||||
Action *action;
|
||||
GSList *node;
|
||||
|
||||
action = g_action_muxer_lookup_action (group->muxer, group->prefix, action_name, &fullname);
|
||||
for (node = action ? action->watchers : NULL; node; node = node->next)
|
||||
g_action_observer_action_removed (node->data, G_ACTION_OBSERVABLE (group->muxer), fullname);
|
||||
g_action_group_action_removed (G_ACTION_GROUP (group->muxer), fullname);
|
||||
g_free (fullname);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
g_action_muxer_query_action (GActionGroup *action_group,
|
||||
const gchar *action_name,
|
||||
gboolean *enabled,
|
||||
const GVariantType **parameter_type,
|
||||
const GVariantType **state_type,
|
||||
GVariant **state_hint,
|
||||
GVariant **state)
|
||||
{
|
||||
GActionMuxer *muxer = G_ACTION_MUXER (action_group);
|
||||
Group *group;
|
||||
|
||||
group = g_action_muxer_find_group (muxer, &action_name);
|
||||
|
||||
if (!group)
|
||||
return FALSE;
|
||||
|
||||
return g_action_group_query_action (group->group, action_name, enabled,
|
||||
parameter_type, state_type, state_hint, state);
|
||||
}
|
||||
|
||||
static GVariant *
|
||||
get_platform_data (void)
|
||||
{
|
||||
gchar time[32];
|
||||
GVariantBuilder *builder;
|
||||
GVariant *result;
|
||||
|
||||
g_snprintf (time, 32, "_TIME%d", clutter_get_current_event_time ());
|
||||
|
||||
builder = g_variant_builder_new (G_VARIANT_TYPE ("a{sv}"));
|
||||
|
||||
g_variant_builder_add (builder, "{sv}", "desktop-startup-id",
|
||||
g_variant_new_string (time));
|
||||
|
||||
result = g_variant_builder_end (builder);
|
||||
g_variant_builder_unref (builder);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static void
|
||||
g_action_muxer_activate_action (GActionGroup *action_group,
|
||||
const gchar *action_name,
|
||||
GVariant *parameter)
|
||||
{
|
||||
GActionMuxer *muxer = G_ACTION_MUXER (action_group);
|
||||
Group *group;
|
||||
|
||||
group = g_action_muxer_find_group (muxer, &action_name);
|
||||
|
||||
if (group)
|
||||
{
|
||||
if (G_IS_REMOTE_ACTION_GROUP (group->group))
|
||||
g_remote_action_group_activate_action_full (G_REMOTE_ACTION_GROUP (group->group),
|
||||
action_name,
|
||||
parameter,
|
||||
get_platform_data ());
|
||||
else
|
||||
g_action_group_activate_action (group->group, action_name, parameter);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
g_action_muxer_change_action_state (GActionGroup *action_group,
|
||||
const gchar *action_name,
|
||||
GVariant *state)
|
||||
{
|
||||
GActionMuxer *muxer = G_ACTION_MUXER (action_group);
|
||||
Group *group;
|
||||
|
||||
group = g_action_muxer_find_group (muxer, &action_name);
|
||||
|
||||
if (group)
|
||||
{
|
||||
if (G_IS_REMOTE_ACTION_GROUP (group->group))
|
||||
g_remote_action_group_change_action_state_full (G_REMOTE_ACTION_GROUP (group->group),
|
||||
action_name,
|
||||
state,
|
||||
get_platform_data ());
|
||||
else
|
||||
g_action_group_change_action_state (group->group, action_name, state);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
g_action_muxer_unregister_internal (Action *action,
|
||||
gpointer observer)
|
||||
{
|
||||
GActionMuxer *muxer = action->muxer;
|
||||
GSList **ptr;
|
||||
|
||||
for (ptr = &action->watchers; *ptr; ptr = &(*ptr)->next)
|
||||
if ((*ptr)->data == observer)
|
||||
{
|
||||
*ptr = g_slist_remove (*ptr, observer);
|
||||
|
||||
if (action->watchers == NULL)
|
||||
{
|
||||
g_hash_table_remove (muxer->actions, action->fullname);
|
||||
g_free (action->fullname);
|
||||
|
||||
g_slice_free (Action, action);
|
||||
|
||||
g_object_unref (muxer);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
g_action_muxer_weak_notify (gpointer data,
|
||||
GObject *where_the_object_was)
|
||||
{
|
||||
Action *action = data;
|
||||
|
||||
g_action_muxer_unregister_internal (action, where_the_object_was);
|
||||
}
|
||||
|
||||
static void
|
||||
g_action_muxer_register_observer (GActionObservable *observable,
|
||||
const gchar *name,
|
||||
GActionObserver *observer)
|
||||
{
|
||||
GActionMuxer *muxer = G_ACTION_MUXER (observable);
|
||||
Action *action;
|
||||
|
||||
action = g_hash_table_lookup (muxer->actions, name);
|
||||
|
||||
if (action == NULL)
|
||||
{
|
||||
action = g_slice_new (Action);
|
||||
action->muxer = g_object_ref (muxer);
|
||||
action->fullname = g_strdup (name);
|
||||
action->watchers = NULL;
|
||||
|
||||
g_hash_table_insert (muxer->actions, action->fullname, action);
|
||||
}
|
||||
|
||||
action->watchers = g_slist_prepend (action->watchers, observer);
|
||||
g_object_weak_ref (G_OBJECT (observer), g_action_muxer_weak_notify, action);
|
||||
}
|
||||
|
||||
static void
|
||||
g_action_muxer_unregister_observer (GActionObservable *observable,
|
||||
const gchar *name,
|
||||
GActionObserver *observer)
|
||||
{
|
||||
GActionMuxer *muxer = G_ACTION_MUXER (observable);
|
||||
Action *action;
|
||||
|
||||
action = g_hash_table_lookup (muxer->actions, name);
|
||||
g_object_weak_unref (G_OBJECT (observer), g_action_muxer_weak_notify, action);
|
||||
g_action_muxer_unregister_internal (action, observer);
|
||||
}
|
||||
|
||||
static void
|
||||
g_action_muxer_free_group (gpointer data)
|
||||
{
|
||||
Group *group = data;
|
||||
|
||||
g_object_unref (group->group);
|
||||
g_free (group->prefix);
|
||||
|
||||
g_slice_free (Group, group);
|
||||
}
|
||||
|
||||
static void
|
||||
g_action_muxer_finalize (GObject *object)
|
||||
{
|
||||
GActionMuxer *muxer = G_ACTION_MUXER (object);
|
||||
|
||||
g_assert_cmpint (g_hash_table_size (muxer->actions), ==, 0);
|
||||
g_hash_table_unref (muxer->actions);
|
||||
g_hash_table_unref (muxer->groups);
|
||||
|
||||
G_OBJECT_CLASS (g_action_muxer_parent_class)
|
||||
->finalize (object);
|
||||
}
|
||||
|
||||
static void
|
||||
g_action_muxer_init (GActionMuxer *muxer)
|
||||
{
|
||||
muxer->actions = g_hash_table_new (g_str_hash, g_str_equal);
|
||||
muxer->groups = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, g_action_muxer_free_group);
|
||||
}
|
||||
|
||||
static void
|
||||
g_action_muxer_observable_iface_init (GActionObservableInterface *iface)
|
||||
{
|
||||
iface->register_observer = g_action_muxer_register_observer;
|
||||
iface->unregister_observer = g_action_muxer_unregister_observer;
|
||||
}
|
||||
|
||||
static void
|
||||
g_action_muxer_group_iface_init (GActionGroupInterface *iface)
|
||||
{
|
||||
iface->list_actions = g_action_muxer_list_actions;
|
||||
iface->query_action = g_action_muxer_query_action;
|
||||
iface->activate_action = g_action_muxer_activate_action;
|
||||
iface->change_action_state = g_action_muxer_change_action_state;
|
||||
}
|
||||
|
||||
static void
|
||||
g_action_muxer_class_init (GObjectClass *class)
|
||||
{
|
||||
class->finalize = g_action_muxer_finalize;
|
||||
}
|
||||
|
||||
/*
|
||||
* g_action_muxer_insert:
|
||||
* @muxer: a #GActionMuxer
|
||||
* @prefix: the prefix string for the action group
|
||||
* @action_group: a #GActionGroup
|
||||
*
|
||||
* Adds the actions in @action_group to the list of actions provided by
|
||||
* @muxer. @prefix is prefixed to each action name, such that for each
|
||||
* action <varname>x</varname> in @action_group, there is an equivalent
|
||||
* action @prefix<literal>.</literal><varname>x</varname> in @muxer.
|
||||
*
|
||||
* For example, if @prefix is "<literal>app</literal>" and @action_group
|
||||
* contains an action called "<literal>quit</literal>", then @muxer will
|
||||
* now contain an action called "<literal>app.quit</literal>".
|
||||
*
|
||||
* If any #GActionObservers are registered for actions in the group,
|
||||
* "action_added" notifications will be emitted, as appropriate.
|
||||
*
|
||||
* @prefix must not contain a dot ('.').
|
||||
*/
|
||||
void
|
||||
g_action_muxer_insert (GActionMuxer *muxer,
|
||||
const gchar *prefix,
|
||||
GActionGroup *action_group)
|
||||
{
|
||||
gchar **actions;
|
||||
Group *group;
|
||||
gint i;
|
||||
|
||||
/* TODO: diff instead of ripout and replace */
|
||||
g_action_muxer_remove (muxer, prefix);
|
||||
|
||||
group = g_slice_new (Group);
|
||||
group->muxer = muxer;
|
||||
group->group = g_object_ref (action_group);
|
||||
group->prefix = g_strdup (prefix);
|
||||
|
||||
g_hash_table_insert (muxer->groups, group->prefix, group);
|
||||
|
||||
actions = g_action_group_list_actions (group->group);
|
||||
for (i = 0; actions[i]; i++)
|
||||
g_action_muxer_action_added (group->group, actions[i], group);
|
||||
g_strfreev (actions);
|
||||
|
||||
group->handler_ids[0] = g_signal_connect (group->group, "action-added",
|
||||
G_CALLBACK (g_action_muxer_action_added), group);
|
||||
group->handler_ids[1] = g_signal_connect (group->group, "action-removed",
|
||||
G_CALLBACK (g_action_muxer_action_removed), group);
|
||||
group->handler_ids[2] = g_signal_connect (group->group, "action-enabled-changed",
|
||||
G_CALLBACK (g_action_muxer_action_enabled_changed), group);
|
||||
group->handler_ids[3] = g_signal_connect (group->group, "action-state-changed",
|
||||
G_CALLBACK (g_action_muxer_action_state_changed), group);
|
||||
}
|
||||
|
||||
/*
|
||||
* g_action_muxer_remove:
|
||||
* @muxer: a #GActionMuxer
|
||||
* @prefix: the prefix of the action group to remove
|
||||
*
|
||||
* Removes a #GActionGroup from the #GActionMuxer.
|
||||
*
|
||||
* If any #GActionObservers are registered for actions in the group,
|
||||
* "action_removed" notifications will be emitted, as appropriate.
|
||||
*/
|
||||
void
|
||||
g_action_muxer_remove (GActionMuxer *muxer,
|
||||
const gchar *prefix)
|
||||
{
|
||||
Group *group;
|
||||
|
||||
group = g_hash_table_lookup (muxer->groups, prefix);
|
||||
|
||||
if (group != NULL)
|
||||
{
|
||||
gchar **actions;
|
||||
gint i;
|
||||
|
||||
g_hash_table_steal (muxer->groups, prefix);
|
||||
|
||||
actions = g_action_group_list_actions (group->group);
|
||||
for (i = 0; actions[i]; i++)
|
||||
g_action_muxer_action_removed (group->group, actions[i], group);
|
||||
g_strfreev (actions);
|
||||
|
||||
/* 'for loop' or 'four loop'? */
|
||||
for (i = 0; i < 4; i++)
|
||||
g_signal_handler_disconnect (group->group, group->handler_ids[i]);
|
||||
|
||||
g_action_muxer_free_group (group);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* g_action_muxer_new:
|
||||
*
|
||||
* Creates a new #GActionMuxer.
|
||||
*/
|
||||
GActionMuxer *
|
||||
g_action_muxer_new (void)
|
||||
{
|
||||
return g_object_new (G_TYPE_ACTION_MUXER, NULL);
|
||||
}
|
@@ -1,53 +0,0 @@
|
||||
/*
|
||||
* Copyright © 2011 Canonical Limited
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the licence, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the
|
||||
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
||||
* Boston, MA 02111-1307, USA.
|
||||
*
|
||||
* Author: Ryan Lortie <desrt@desrt.ca>
|
||||
*/
|
||||
|
||||
#ifndef __G_ACTION_MUXER_H__
|
||||
#define __G_ACTION_MUXER_H__
|
||||
|
||||
#include <gio/gio.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define G_TYPE_ACTION_MUXER (g_action_muxer_get_type ())
|
||||
#define G_ACTION_MUXER(inst) (G_TYPE_CHECK_INSTANCE_CAST ((inst), \
|
||||
G_TYPE_ACTION_MUXER, GActionMuxer))
|
||||
#define G_IS_ACTION_MUXER(inst) (G_TYPE_CHECK_INSTANCE_TYPE ((inst), \
|
||||
G_TYPE_ACTION_MUXER))
|
||||
|
||||
typedef struct _GActionMuxer GActionMuxer;
|
||||
|
||||
G_GNUC_INTERNAL
|
||||
GType g_action_muxer_get_type (void);
|
||||
G_GNUC_INTERNAL
|
||||
GActionMuxer * g_action_muxer_new (void);
|
||||
|
||||
G_GNUC_INTERNAL
|
||||
void g_action_muxer_insert (GActionMuxer *muxer,
|
||||
const gchar *prefix,
|
||||
GActionGroup *group);
|
||||
|
||||
G_GNUC_INTERNAL
|
||||
void g_action_muxer_remove (GActionMuxer *muxer,
|
||||
const gchar *prefix);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __G_ACTION_MUXER_H__ */
|
@@ -1,80 +0,0 @@
|
||||
/*
|
||||
* Copyright © 2011 Canonical Limited
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as
|
||||
* published by the Free Software Foundation; either version 2 of the
|
||||
* licence or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307,
|
||||
* USA.
|
||||
*
|
||||
* Authors: Ryan Lortie <desrt@desrt.ca>
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include "gactionobservable.h"
|
||||
|
||||
G_DEFINE_INTERFACE (GActionObservable, g_action_observable, G_TYPE_OBJECT)
|
||||
|
||||
/*
|
||||
* SECTION:gactionobserable
|
||||
* @short_description: an interface implemented by objects that report
|
||||
* changes to actions
|
||||
*/
|
||||
|
||||
void
|
||||
g_action_observable_default_init (GActionObservableInterface *iface)
|
||||
{
|
||||
}
|
||||
|
||||
/*
|
||||
* g_action_observable_register_observer:
|
||||
* @observable: a #GActionObservable
|
||||
* @action_name: the name of the action
|
||||
* @observer: the #GActionObserver to which the events will be reported
|
||||
*
|
||||
* Registers @observer as being interested in changes to @action_name on
|
||||
* @observable.
|
||||
*/
|
||||
void
|
||||
g_action_observable_register_observer (GActionObservable *observable,
|
||||
const gchar *action_name,
|
||||
GActionObserver *observer)
|
||||
{
|
||||
g_return_if_fail (G_IS_ACTION_OBSERVABLE (observable));
|
||||
|
||||
G_ACTION_OBSERVABLE_GET_IFACE (observable)
|
||||
->register_observer (observable, action_name, observer);
|
||||
}
|
||||
|
||||
/*
|
||||
* g_action_observable_unregister_observer:
|
||||
* @observable: a #GActionObservable
|
||||
* @action_name: the name of the action
|
||||
* @observer: the #GActionObserver to which the events will be reported
|
||||
*
|
||||
* Removes the registration of @observer as being interested in changes
|
||||
* to @action_name on @observable.
|
||||
*
|
||||
* If the observer was registered multiple times, it must be
|
||||
* unregistered an equal number of times.
|
||||
*/
|
||||
void
|
||||
g_action_observable_unregister_observer (GActionObservable *observable,
|
||||
const gchar *action_name,
|
||||
GActionObserver *observer)
|
||||
{
|
||||
g_return_if_fail (G_IS_ACTION_OBSERVABLE (observable));
|
||||
|
||||
G_ACTION_OBSERVABLE_GET_IFACE (observable)
|
||||
->unregister_observer (observable, action_name, observer);
|
||||
}
|
@@ -1,64 +0,0 @@
|
||||
/*
|
||||
* Copyright © 2011 Canonical Limited
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as
|
||||
* published by the Free Software Foundation; either version 2 of the
|
||||
* licence or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307,
|
||||
* USA.
|
||||
*
|
||||
* Authors: Ryan Lortie <desrt@desrt.ca>
|
||||
*/
|
||||
|
||||
#ifndef __G_ACTION_OBSERVABLE_H__
|
||||
#define __G_ACTION_OBSERVABLE_H__
|
||||
|
||||
#include "gactionobserver.h"
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define G_TYPE_ACTION_OBSERVABLE (g_action_observable_get_type ())
|
||||
#define G_ACTION_OBSERVABLE(inst) (G_TYPE_CHECK_INSTANCE_CAST ((inst), \
|
||||
G_TYPE_ACTION_OBSERVABLE, GActionObservable))
|
||||
#define G_IS_ACTION_OBSERVABLE(inst) (G_TYPE_CHECK_INSTANCE_TYPE ((inst), \
|
||||
G_TYPE_ACTION_OBSERVABLE))
|
||||
#define G_ACTION_OBSERVABLE_GET_IFACE(inst) (G_TYPE_INSTANCE_GET_INTERFACE ((inst), \
|
||||
G_TYPE_ACTION_OBSERVABLE, GActionObservableInterface))
|
||||
|
||||
typedef struct _GActionObservableInterface GActionObservableInterface;
|
||||
|
||||
struct _GActionObservableInterface
|
||||
{
|
||||
GTypeInterface g_iface;
|
||||
|
||||
void (* register_observer) (GActionObservable *observable,
|
||||
const gchar *action_name,
|
||||
GActionObserver *observer);
|
||||
void (* unregister_observer) (GActionObservable *observable,
|
||||
const gchar *action_name,
|
||||
GActionObserver *observer);
|
||||
};
|
||||
|
||||
G_GNUC_INTERNAL
|
||||
GType g_action_observable_get_type (void);
|
||||
G_GNUC_INTERNAL
|
||||
void g_action_observable_register_observer (GActionObservable *observable,
|
||||
const gchar *action_name,
|
||||
GActionObserver *observer);
|
||||
G_GNUC_INTERNAL
|
||||
void g_action_observable_unregister_observer (GActionObservable *observable,
|
||||
const gchar *action_name,
|
||||
GActionObserver *observer);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __G_ACTION_OBSERVABLE_H__ */
|
@@ -1,161 +0,0 @@
|
||||
/*
|
||||
* Copyright © 2011 Canonical Limited
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as
|
||||
* published by the Free Software Foundation; either version 2 of the
|
||||
* licence or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307,
|
||||
* USA.
|
||||
*
|
||||
* Authors: Ryan Lortie <desrt@desrt.ca>
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include "gactionobserver.h"
|
||||
|
||||
G_DEFINE_INTERFACE (GActionObserver, g_action_observer, G_TYPE_OBJECT)
|
||||
|
||||
/**
|
||||
* SECTION:gactionobserver
|
||||
* @short_description: an interface implemented by objects that are
|
||||
* interested in monitoring actions for changes
|
||||
*
|
||||
* GActionObserver is a simple interface allowing objects that wish to
|
||||
* be notified of changes to actions to be notified of those changes.
|
||||
*
|
||||
* It is also possible to monitor changes to action groups using
|
||||
* #GObject signals, but there are a number of reasons that this
|
||||
* approach could become problematic:
|
||||
*
|
||||
* - there are four separate signals that must be manually connected
|
||||
* and disconnected
|
||||
*
|
||||
* - when a large number of different observers wish to monitor a
|
||||
* (usually disjoint) set of actions within the same action group,
|
||||
* there is only one way to avoid having all notifications delivered
|
||||
* to all observers: signal detail. In order to use signal detail,
|
||||
* each action name must be quarked, which is not always practical.
|
||||
*
|
||||
* - even if quarking is acceptable, #GObject signal details are
|
||||
* implemented by scanning a linked list, so there is no real
|
||||
* decrease in complexity
|
||||
*/
|
||||
|
||||
void
|
||||
g_action_observer_default_init (GActionObserverInterface *class)
|
||||
{
|
||||
}
|
||||
|
||||
/*
|
||||
* g_action_observer_action_added:
|
||||
* @observer: a #GActionObserver
|
||||
* @observable: the source of the event
|
||||
* @action_name: the name of the action
|
||||
* @enabled: %TRUE if the action is now enabled
|
||||
* @parameter_type: the parameter type for action invocations, or %NULL
|
||||
* if no parameter is required
|
||||
* @state: the current state of the action, or %NULL if the action is
|
||||
* stateless
|
||||
*
|
||||
* This function is called when an action that the observer is
|
||||
* registered to receive events for is added.
|
||||
*
|
||||
* This function should only be called by objects with which the
|
||||
* observer has explicitly registered itself to receive events.
|
||||
*/
|
||||
void
|
||||
g_action_observer_action_added (GActionObserver *observer,
|
||||
GActionObservable *observable,
|
||||
const gchar *action_name,
|
||||
const GVariantType *parameter_type,
|
||||
gboolean enabled,
|
||||
GVariant *state)
|
||||
{
|
||||
g_return_if_fail (G_IS_ACTION_OBSERVER (observer));
|
||||
|
||||
G_ACTION_OBSERVER_GET_IFACE (observer)
|
||||
->action_added (observer, observable, action_name, parameter_type, enabled, state);
|
||||
}
|
||||
|
||||
/*
|
||||
* g_action_observer_action_enabled_changed:
|
||||
* @observer: a #GActionObserver
|
||||
* @observable: the source of the event
|
||||
* @action_name: the name of the action
|
||||
* @enabled: %TRUE if the action is now enabled
|
||||
*
|
||||
* This function is called when an action that the observer is
|
||||
* registered to receive events for becomes enabled or disabled.
|
||||
*
|
||||
* This function should only be called by objects with which the
|
||||
* observer has explicitly registered itself to receive events.
|
||||
*/
|
||||
void
|
||||
g_action_observer_action_enabled_changed (GActionObserver *observer,
|
||||
GActionObservable *observable,
|
||||
const gchar *action_name,
|
||||
gboolean enabled)
|
||||
{
|
||||
g_return_if_fail (G_IS_ACTION_OBSERVER (observer));
|
||||
|
||||
G_ACTION_OBSERVER_GET_IFACE (observer)
|
||||
->action_enabled_changed (observer, observable, action_name, enabled);
|
||||
}
|
||||
|
||||
/*
|
||||
* g_action_observer_action_state_changed:
|
||||
* @observer: a #GActionObserver
|
||||
* @observable: the source of the event
|
||||
* @action_name: the name of the action
|
||||
* @state: the new state of the action
|
||||
*
|
||||
* This function is called when an action that the observer is
|
||||
* registered to receive events for changes its state.
|
||||
*
|
||||
* This function should only be called by objects with which the
|
||||
* observer has explicitly registered itself to receive events.
|
||||
*/
|
||||
void
|
||||
g_action_observer_action_state_changed (GActionObserver *observer,
|
||||
GActionObservable *observable,
|
||||
const gchar *action_name,
|
||||
GVariant *state)
|
||||
{
|
||||
g_return_if_fail (G_IS_ACTION_OBSERVER (observer));
|
||||
|
||||
G_ACTION_OBSERVER_GET_IFACE (observer)
|
||||
->action_state_changed (observer, observable, action_name, state);
|
||||
}
|
||||
|
||||
/*
|
||||
* g_action_observer_action_removed:
|
||||
* @observer: a #GActionObserver
|
||||
* @observable: the source of the event
|
||||
* @action_name: the name of the action
|
||||
*
|
||||
* This function is called when an action that the observer is
|
||||
* registered to receive events for is removed.
|
||||
*
|
||||
* This function should only be called by objects with which the
|
||||
* observer has explicitly registered itself to receive events.
|
||||
*/
|
||||
void
|
||||
g_action_observer_action_removed (GActionObserver *observer,
|
||||
GActionObservable *observable,
|
||||
const gchar *action_name)
|
||||
{
|
||||
g_return_if_fail (G_IS_ACTION_OBSERVER (observer));
|
||||
|
||||
G_ACTION_OBSERVER_GET_IFACE (observer)
|
||||
->action_removed (observer, observable, action_name);
|
||||
}
|
@@ -1,90 +0,0 @@
|
||||
/*
|
||||
* Copyright © 2011 Canonical Limited
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as
|
||||
* published by the Free Software Foundation; either version 2 of the
|
||||
* licence or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307,
|
||||
* USA.
|
||||
*
|
||||
* Authors: Ryan Lortie <desrt@desrt.ca>
|
||||
*/
|
||||
|
||||
#ifndef __G_ACTION_OBSERVER_H__
|
||||
#define __G_ACTION_OBSERVER_H__
|
||||
|
||||
#include <gio/gio.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define G_TYPE_ACTION_OBSERVER (g_action_observer_get_type ())
|
||||
#define G_ACTION_OBSERVER(inst) (G_TYPE_CHECK_INSTANCE_CAST ((inst), \
|
||||
G_TYPE_ACTION_OBSERVER, GActionObserver))
|
||||
#define G_IS_ACTION_OBSERVER(inst) (G_TYPE_CHECK_INSTANCE_TYPE ((inst), \
|
||||
G_TYPE_ACTION_OBSERVER))
|
||||
#define G_ACTION_OBSERVER_GET_IFACE(inst) (G_TYPE_INSTANCE_GET_INTERFACE ((inst), \
|
||||
G_TYPE_ACTION_OBSERVER, GActionObserverInterface))
|
||||
|
||||
typedef struct _GActionObserverInterface GActionObserverInterface;
|
||||
typedef struct _GActionObservable GActionObservable;
|
||||
typedef struct _GActionObserver GActionObserver;
|
||||
|
||||
struct _GActionObserverInterface
|
||||
{
|
||||
GTypeInterface g_iface;
|
||||
|
||||
void (* action_added) (GActionObserver *observer,
|
||||
GActionObservable *observable,
|
||||
const gchar *action_name,
|
||||
const GVariantType *parameter_type,
|
||||
gboolean enabled,
|
||||
GVariant *state);
|
||||
void (* action_enabled_changed) (GActionObserver *observer,
|
||||
GActionObservable *observable,
|
||||
const gchar *action_name,
|
||||
gboolean enabled);
|
||||
void (* action_state_changed) (GActionObserver *observer,
|
||||
GActionObservable *observable,
|
||||
const gchar *action_name,
|
||||
GVariant *state);
|
||||
void (* action_removed) (GActionObserver *observer,
|
||||
GActionObservable *observable,
|
||||
const gchar *action_name);
|
||||
};
|
||||
|
||||
G_GNUC_INTERNAL
|
||||
GType g_action_observer_get_type (void);
|
||||
G_GNUC_INTERNAL
|
||||
void g_action_observer_action_added (GActionObserver *observer,
|
||||
GActionObservable *observable,
|
||||
const gchar *action_name,
|
||||
const GVariantType *parameter_type,
|
||||
gboolean enabled,
|
||||
GVariant *state);
|
||||
G_GNUC_INTERNAL
|
||||
void g_action_observer_action_enabled_changed (GActionObserver *observer,
|
||||
GActionObservable *observable,
|
||||
const gchar *action_name,
|
||||
gboolean enabled);
|
||||
G_GNUC_INTERNAL
|
||||
void g_action_observer_action_state_changed (GActionObserver *observer,
|
||||
GActionObservable *observable,
|
||||
const gchar *action_name,
|
||||
GVariant *state);
|
||||
G_GNUC_INTERNAL
|
||||
void g_action_observer_action_removed (GActionObserver *observer,
|
||||
GActionObservable *observable,
|
||||
const gchar *action_name);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __G_ACTION_OBSERVER_H__ */
|
@@ -27,6 +27,9 @@ void _shell_app_do_match (ShellApp *app,
|
||||
GSList **prefix_results,
|
||||
GSList **substring_results);
|
||||
|
||||
void _shell_app_set_dbus_name (ShellApp *app,
|
||||
const char *name);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __SHELL_APP_PRIVATE_H__ */
|
||||
|
177
src/shell-app.c
177
src/shell-app.c
@@ -15,7 +15,6 @@
|
||||
#include "shell-app-system-private.h"
|
||||
#include "shell-window-tracker-private.h"
|
||||
#include "st.h"
|
||||
#include "gactionmuxer.h"
|
||||
|
||||
typedef enum {
|
||||
MATCH_NONE,
|
||||
@@ -40,12 +39,10 @@ typedef struct {
|
||||
gboolean window_sort_stale : 1;
|
||||
|
||||
/* See GApplication documentation */
|
||||
gint name_watcher_id;
|
||||
guint name_watcher_id;
|
||||
gchar *dbus_name;
|
||||
GDBusProxy *app_proxy;
|
||||
GActionGroup *remote_actions;
|
||||
GMenuModel *remote_menu;
|
||||
GActionMuxer *muxer;
|
||||
GDBusActionGroup *remote_actions;
|
||||
GMenuProxy *remote_menu;
|
||||
GCancellable *dbus_cancellable;
|
||||
} ShellAppRunningState;
|
||||
|
||||
@@ -100,13 +97,6 @@ static guint shell_app_signals[LAST_SIGNAL] = { 0 };
|
||||
|
||||
static void create_running_state (ShellApp *app);
|
||||
static void unref_running_state (ShellAppRunningState *state);
|
||||
static void on_dbus_name_appeared (GDBusConnection *bus,
|
||||
const gchar *name,
|
||||
const gchar *name_owner,
|
||||
gpointer user_data);
|
||||
static void on_dbus_name_disappeared (GDBusConnection *bus,
|
||||
const gchar *name,
|
||||
gpointer user_data);
|
||||
|
||||
G_DEFINE_TYPE (ShellApp, shell_app, G_TYPE_OBJECT)
|
||||
|
||||
@@ -131,7 +121,7 @@ shell_app_get_property (GObject *gobject,
|
||||
break;
|
||||
case PROP_ACTION_GROUP:
|
||||
if (app->running_state)
|
||||
g_value_set_object (value, app->running_state->muxer);
|
||||
g_value_set_object (value, app->running_state->remote_actions);
|
||||
break;
|
||||
case PROP_MENU:
|
||||
if (app->running_state)
|
||||
@@ -557,31 +547,6 @@ shell_app_activate_window (ShellApp *app,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
shell_app_update_window_actions (ShellApp *app, MetaWindow *window)
|
||||
{
|
||||
const char *object_path;
|
||||
|
||||
object_path = meta_window_get_dbus_object_path (window);
|
||||
if (object_path != NULL)
|
||||
{
|
||||
GActionGroup *actions;
|
||||
|
||||
actions = g_object_get_data (G_OBJECT (window), "actions");
|
||||
if (actions == NULL)
|
||||
{
|
||||
actions = G_ACTION_GROUP (g_dbus_action_group_get (g_dbus_proxy_get_connection (app->running_state->app_proxy),
|
||||
meta_window_get_dbus_unique_name (window),
|
||||
object_path));
|
||||
g_object_set_data_full (G_OBJECT (window), "actions", actions, g_object_unref);
|
||||
}
|
||||
|
||||
g_action_muxer_insert (app->running_state->muxer, "win", actions);
|
||||
g_object_notify (G_OBJECT (app), "action-group");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* shell_app_activate:
|
||||
* @app: a #ShellApp
|
||||
@@ -972,34 +937,6 @@ shell_app_on_ws_switch (MetaScreen *screen,
|
||||
g_signal_emit (app, shell_app_signals[WINDOWS_CHANGED], 0);
|
||||
}
|
||||
|
||||
static void
|
||||
on_dbus_application_id_changed (MetaWindow *window,
|
||||
GParamSpec *pspec,
|
||||
gpointer user_data)
|
||||
{
|
||||
const char *appid;
|
||||
ShellApp *app = SHELL_APP (user_data);
|
||||
|
||||
/* Ignore changes in the appid after it's set, shouldn't happen */
|
||||
if (app->running_state->dbus_name != NULL)
|
||||
return;
|
||||
|
||||
appid = meta_window_get_dbus_application_id (window);
|
||||
|
||||
if (!appid)
|
||||
return;
|
||||
|
||||
g_assert (app->running_state != NULL);
|
||||
app->running_state->dbus_name = g_strdup (appid);
|
||||
app->running_state->name_watcher_id = g_bus_watch_name (G_BUS_TYPE_SESSION,
|
||||
appid,
|
||||
G_BUS_NAME_WATCHER_FLAGS_NONE,
|
||||
on_dbus_name_appeared,
|
||||
on_dbus_name_disappeared,
|
||||
g_object_ref (app),
|
||||
g_object_unref);
|
||||
}
|
||||
|
||||
void
|
||||
_shell_app_add_window (ShellApp *app,
|
||||
MetaWindow *window)
|
||||
@@ -1020,9 +957,6 @@ _shell_app_add_window (ShellApp *app,
|
||||
if (app->state != SHELL_APP_STATE_STARTING)
|
||||
shell_app_state_transition (app, SHELL_APP_STATE_RUNNING);
|
||||
|
||||
g_signal_connect (window, "notify::dbus-application-id", G_CALLBACK(on_dbus_application_id_changed), app);
|
||||
on_dbus_application_id_changed (window, NULL, app);
|
||||
|
||||
g_object_thaw_notify (G_OBJECT (app));
|
||||
|
||||
g_signal_emit (app, shell_app_signals[WINDOWS_CHANGED], 0);
|
||||
@@ -1039,7 +973,6 @@ _shell_app_remove_window (ShellApp *app,
|
||||
|
||||
g_signal_handlers_disconnect_by_func (window, G_CALLBACK(shell_app_on_unmanaged), app);
|
||||
g_signal_handlers_disconnect_by_func (window, G_CALLBACK(shell_app_on_user_time_changed), app);
|
||||
g_signal_handlers_disconnect_by_func (window, G_CALLBACK(on_dbus_application_id_changed), app);
|
||||
g_object_unref (window);
|
||||
app->running_state->windows = g_slist_remove (app->running_state->windows, window);
|
||||
|
||||
@@ -1050,24 +983,24 @@ _shell_app_remove_window (ShellApp *app,
|
||||
}
|
||||
|
||||
static void
|
||||
on_dbus_proxy_gotten (GObject *initable,
|
||||
GAsyncResult *result,
|
||||
gpointer user_data)
|
||||
on_action_group_acquired (GObject *object,
|
||||
GAsyncResult *result,
|
||||
gpointer user_data)
|
||||
{
|
||||
ShellApp *self = SHELL_APP (user_data);
|
||||
ShellAppRunningState *state = self->running_state;
|
||||
GError *error = NULL;
|
||||
GVariant *menu_property;
|
||||
char *object_path;
|
||||
|
||||
state->app_proxy = g_dbus_proxy_new_finish (result,
|
||||
&error);
|
||||
state->remote_actions = g_dbus_action_group_new_finish (result,
|
||||
&error);
|
||||
|
||||
if (error)
|
||||
{
|
||||
if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED) &&
|
||||
!g_error_matches (error, G_DBUS_ERROR, G_DBUS_ERROR_UNKNOWN_METHOD))
|
||||
{
|
||||
g_warning ("Unexpected error while creating application proxy: %s", error->message);
|
||||
g_warning ("Unexpected error while reading application actions: %s", error->message);
|
||||
}
|
||||
|
||||
g_clear_error (&error);
|
||||
@@ -1086,32 +1019,19 @@ on_dbus_proxy_gotten (GObject *initable,
|
||||
return;
|
||||
}
|
||||
|
||||
/* on to the second step, the primary action group */
|
||||
object_path = g_strconcat ("/", state->dbus_name, NULL);
|
||||
g_strdelimit (object_path, ".", '/');
|
||||
|
||||
state->remote_actions = (GActionGroup*)g_dbus_action_group_get (
|
||||
g_dbus_proxy_get_connection (state->app_proxy),
|
||||
g_dbus_proxy_get_name (state->app_proxy),
|
||||
g_dbus_proxy_get_object_path (state->app_proxy));
|
||||
state->muxer = g_action_muxer_new ();
|
||||
g_action_muxer_insert (state->muxer, "app", state->remote_actions);
|
||||
g_strfreev (g_action_group_list_actions (state->remote_actions));
|
||||
state->remote_menu = g_menu_proxy_get (g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, NULL),
|
||||
state->dbus_name,
|
||||
object_path);
|
||||
|
||||
g_object_notify (G_OBJECT (self), "dbus-id");
|
||||
g_object_notify (G_OBJECT (self), "action-group");
|
||||
g_object_notify (G_OBJECT (self), "menu");
|
||||
|
||||
menu_property = g_dbus_proxy_get_cached_property (state->app_proxy, "AppMenu");
|
||||
|
||||
if (menu_property && g_variant_n_children (menu_property) > 0)
|
||||
{
|
||||
const gchar *object_path;
|
||||
|
||||
g_variant_get_child (menu_property, 0, "&o", &object_path);
|
||||
|
||||
state->remote_menu = G_MENU_MODEL (g_dbus_menu_model_get (g_dbus_proxy_get_connection (state->app_proxy),
|
||||
state->dbus_name,
|
||||
object_path));
|
||||
|
||||
g_object_notify (G_OBJECT (self), "menu");
|
||||
}
|
||||
g_object_unref (self);
|
||||
g_free (object_path);
|
||||
}
|
||||
|
||||
static void
|
||||
@@ -1132,19 +1052,13 @@ on_dbus_name_appeared (GDBusConnection *bus,
|
||||
if (!state->dbus_cancellable)
|
||||
state->dbus_cancellable = g_cancellable_new ();
|
||||
|
||||
/* first step: the application proxy */
|
||||
|
||||
g_dbus_proxy_new (bus,
|
||||
G_DBUS_PROXY_FLAGS_NONE,
|
||||
NULL, /* interface info */
|
||||
name_owner,
|
||||
object_path,
|
||||
"org.gtk.Application",
|
||||
state->dbus_cancellable,
|
||||
on_dbus_proxy_gotten,
|
||||
g_object_ref (self));
|
||||
|
||||
g_object_notify (G_OBJECT (self), "dbus-id");
|
||||
g_dbus_action_group_new (bus,
|
||||
name,
|
||||
object_path,
|
||||
G_DBUS_ACTION_GROUP_FLAGS_NONE,
|
||||
state->dbus_cancellable,
|
||||
on_action_group_acquired,
|
||||
g_object_ref (self));
|
||||
|
||||
g_free (object_path);
|
||||
}
|
||||
@@ -1165,10 +1079,8 @@ on_dbus_name_disappeared (GDBusConnection *bus,
|
||||
g_clear_object (&state->dbus_cancellable);
|
||||
}
|
||||
|
||||
g_clear_object (&state->app_proxy);
|
||||
g_clear_object (&state->remote_actions);
|
||||
g_clear_object (&state->remote_menu);
|
||||
g_clear_object (&state->muxer);
|
||||
|
||||
g_free (state->dbus_name);
|
||||
state->dbus_name = NULL;
|
||||
@@ -1177,6 +1089,35 @@ on_dbus_name_disappeared (GDBusConnection *bus,
|
||||
state->name_watcher_id = 0;
|
||||
}
|
||||
|
||||
void
|
||||
_shell_app_set_dbus_name (ShellApp *app,
|
||||
const char *bus_name)
|
||||
{
|
||||
g_return_if_fail (app->running_state != NULL);
|
||||
|
||||
if (app->running_state->dbus_name != NULL)
|
||||
{
|
||||
/* already associating with another name
|
||||
(can only happen if you restart the shell in the
|
||||
middle of the session, in which case it will try
|
||||
all names seen on the bus; otherwise, it uses
|
||||
the Hello signal from GApplication and thus knows
|
||||
for sure which name is the right one)
|
||||
*/
|
||||
return;
|
||||
}
|
||||
|
||||
app->running_state->dbus_name = g_strdup (bus_name);
|
||||
app->running_state->name_watcher_id = g_bus_watch_name (G_BUS_TYPE_SESSION,
|
||||
bus_name,
|
||||
G_BUS_NAME_WATCHER_FLAGS_NONE,
|
||||
on_dbus_name_appeared,
|
||||
on_dbus_name_disappeared,
|
||||
g_object_ref (app),
|
||||
g_object_unref);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* shell_app_get_pids:
|
||||
* @app: a #ShellApp
|
||||
@@ -1411,10 +1352,8 @@ unref_running_state (ShellAppRunningState *state)
|
||||
g_object_unref (state->dbus_cancellable);
|
||||
}
|
||||
|
||||
g_clear_object (&state->app_proxy);
|
||||
g_clear_object (&state->remote_actions);
|
||||
g_clear_object (&state->remote_menu);
|
||||
g_clear_object (&state->muxer);
|
||||
g_free (state->dbus_name);
|
||||
|
||||
if (state->name_watcher_id)
|
||||
@@ -1688,7 +1627,7 @@ shell_app_class_init(ShellAppClass *klass)
|
||||
g_param_spec_object ("action-group",
|
||||
"Application Action Group",
|
||||
"The action group exported by the remote application",
|
||||
G_TYPE_ACTION_GROUP,
|
||||
G_TYPE_DBUS_ACTION_GROUP,
|
||||
G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
|
||||
/**
|
||||
* ShellApp:menu:
|
||||
@@ -1701,7 +1640,7 @@ shell_app_class_init(ShellAppClass *klass)
|
||||
g_param_spec_object ("menu",
|
||||
"Application Menu",
|
||||
"The primary menu exported by the remote application",
|
||||
G_TYPE_MENU_MODEL,
|
||||
G_TYPE_MENU_PROXY,
|
||||
G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
|
||||
|
||||
}
|
||||
|
@@ -83,8 +83,6 @@ int shell_app_compare_by_name (ShellApp *app, ShellApp *other);
|
||||
|
||||
int shell_app_compare (ShellApp *app, ShellApp *other);
|
||||
|
||||
void shell_app_update_window_actions (ShellApp *app, MetaWindow *window);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __SHELL_APP_H__ */
|
||||
|
@@ -49,6 +49,9 @@ struct _ShellWindowTracker
|
||||
/* <MetaWindow * window, ShellApp *app> */
|
||||
GHashTable *window_to_app;
|
||||
|
||||
/* <int, gchar *> */
|
||||
GHashTable *pid_to_dbus_connection;
|
||||
|
||||
/* <int, ShellApp *app> */
|
||||
GHashTable *launched_pid_to_app;
|
||||
};
|
||||
@@ -413,9 +416,6 @@ update_focus_app (ShellWindowTracker *self)
|
||||
new_focus_win = meta_display_get_focus_window (shell_global_get_display (shell_global_get ()));
|
||||
new_focus_app = new_focus_win ? shell_window_tracker_get_window_app (self, new_focus_win) : NULL;
|
||||
|
||||
if (new_focus_app)
|
||||
shell_app_update_window_actions (new_focus_app, new_focus_win);
|
||||
|
||||
set_focus_app (self, new_focus_app);
|
||||
}
|
||||
|
||||
@@ -439,6 +439,8 @@ track_window (ShellWindowTracker *self,
|
||||
MetaWindow *window)
|
||||
{
|
||||
ShellApp *app;
|
||||
GPid pid;
|
||||
gchar *dbus_name;
|
||||
|
||||
if (!shell_window_tracker_is_window_interesting (window))
|
||||
return;
|
||||
@@ -454,6 +456,15 @@ track_window (ShellWindowTracker *self,
|
||||
|
||||
_shell_app_add_window (app, window);
|
||||
|
||||
/* Try to associate this ShellApp with a GApplication id, if one exists */
|
||||
pid = meta_window_get_pid (window);
|
||||
dbus_name = g_hash_table_lookup (self->pid_to_dbus_connection, GINT_TO_POINTER ((int) pid));
|
||||
if (dbus_name != NULL)
|
||||
{
|
||||
_shell_app_set_dbus_name (app, dbus_name);
|
||||
g_hash_table_remove (self->pid_to_dbus_connection, GINT_TO_POINTER ((int) pid));
|
||||
}
|
||||
|
||||
g_signal_emit (self, signals[TRACKED_WINDOWS_CHANGED], 0);
|
||||
}
|
||||
|
||||
@@ -584,13 +595,161 @@ on_startup_sequence_changed (MetaScreen *screen,
|
||||
g_signal_emit (G_OBJECT (self), signals[STARTUP_SEQUENCE_CHANGED], 0, sequence);
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
ShellWindowTracker *tracker;
|
||||
gchar *bus_name;
|
||||
} LookupAppDBusData;
|
||||
|
||||
static void
|
||||
on_get_connection_unix_pid_reply (GObject *connection,
|
||||
GAsyncResult *result,
|
||||
gpointer user_data)
|
||||
{
|
||||
LookupAppDBusData *data = user_data;
|
||||
GError *error = NULL;
|
||||
GVariant *reply;
|
||||
guint32 pid;
|
||||
ShellApp *app;
|
||||
|
||||
reply = g_dbus_connection_call_finish (G_DBUS_CONNECTION (connection), result, &error);
|
||||
if (!reply)
|
||||
{
|
||||
if (!g_error_matches (error, G_DBUS_ERROR, G_DBUS_ERROR_NAME_HAS_NO_OWNER))
|
||||
g_warning ("%s\n", error->message);
|
||||
|
||||
g_clear_error (&error);
|
||||
goto out;
|
||||
}
|
||||
if (!g_variant_is_of_type (reply, G_VARIANT_TYPE ("(u)")))
|
||||
{
|
||||
g_variant_unref (reply);
|
||||
goto out;
|
||||
}
|
||||
g_variant_get (reply, "(u)", &pid);
|
||||
g_variant_unref (reply);
|
||||
|
||||
app = shell_window_tracker_get_app_from_pid (data->tracker, (int)pid);
|
||||
if (app)
|
||||
_shell_app_set_dbus_name (app, data->bus_name);
|
||||
else
|
||||
{
|
||||
g_hash_table_insert (data->tracker->pid_to_dbus_connection,
|
||||
GINT_TO_POINTER ((int) pid),
|
||||
data->bus_name);
|
||||
data->bus_name = NULL;
|
||||
}
|
||||
|
||||
out:
|
||||
g_object_unref (data->tracker);
|
||||
g_free (data->bus_name);
|
||||
g_slice_free (LookupAppDBusData, data);
|
||||
}
|
||||
|
||||
static void
|
||||
lookup_application_from_name (ShellWindowTracker *self,
|
||||
GDBusConnection *connection,
|
||||
const gchar *bus_name)
|
||||
{
|
||||
LookupAppDBusData *data;
|
||||
|
||||
data = g_slice_new0 (LookupAppDBusData);
|
||||
data->tracker = g_object_ref (self);
|
||||
data->bus_name = g_strdup (bus_name);
|
||||
|
||||
/*
|
||||
* TODO: Add something to GtkApplication so it definitely knows the .desktop file.
|
||||
*/
|
||||
g_dbus_connection_call (connection,
|
||||
"org.freedesktop.DBus",
|
||||
"/org/freedesktop/DBus",
|
||||
"org.freedesktop.DBus",
|
||||
"GetConnectionUnixProcessID",
|
||||
g_variant_new ("(s)", bus_name),
|
||||
NULL, 0, -1, NULL, on_get_connection_unix_pid_reply, data);
|
||||
}
|
||||
|
||||
static void
|
||||
on_application_signal (GDBusConnection *connection,
|
||||
const gchar *sender_name,
|
||||
const gchar *object_path,
|
||||
const gchar *interface_name,
|
||||
const gchar *signal_name,
|
||||
GVariant *parameters,
|
||||
gpointer user_data)
|
||||
{
|
||||
ShellWindowTracker *tracker = SHELL_WINDOW_TRACKER (user_data);
|
||||
gchar *bus_name = NULL;
|
||||
|
||||
g_variant_get (parameters, "(&s)", &bus_name);
|
||||
lookup_application_from_name (tracker, connection, bus_name);
|
||||
|
||||
g_variant_unref (parameters);
|
||||
}
|
||||
|
||||
static void
|
||||
on_list_names_end (GObject *object,
|
||||
GAsyncResult *result,
|
||||
gpointer user_data)
|
||||
{
|
||||
GDBusConnection *connection = G_DBUS_CONNECTION (object);
|
||||
ShellWindowTracker *self = SHELL_WINDOW_TRACKER (user_data);
|
||||
GError *error = NULL;
|
||||
GVariantIter iter;
|
||||
gchar *bus_name = NULL;
|
||||
|
||||
GVariant *res = g_dbus_connection_call_finish (connection, result, &error);
|
||||
|
||||
if (!res)
|
||||
{
|
||||
g_warning ("ListNames failed: %s", error->message);
|
||||
g_error_free (error);
|
||||
return;
|
||||
}
|
||||
|
||||
g_variant_iter_init (&iter, g_variant_get_child_value (res, 0));
|
||||
while (g_variant_iter_loop (&iter, "s", &bus_name))
|
||||
{
|
||||
if (bus_name[0] == ':')
|
||||
{
|
||||
/* unique name, uninteresting */
|
||||
continue;
|
||||
}
|
||||
|
||||
lookup_application_from_name (self, connection, bus_name);
|
||||
}
|
||||
|
||||
g_variant_unref (res);
|
||||
}
|
||||
|
||||
static void
|
||||
shell_window_tracker_init (ShellWindowTracker *self)
|
||||
{
|
||||
MetaScreen *screen;
|
||||
|
||||
self->window_to_app = g_hash_table_new_full (g_direct_hash, g_direct_equal,
|
||||
NULL, (GDestroyNotify) g_object_unref);
|
||||
g_dbus_connection_signal_subscribe (g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, NULL),
|
||||
NULL,
|
||||
"org.gtk.Application",
|
||||
"Hello",
|
||||
NULL,
|
||||
NULL,
|
||||
G_DBUS_SIGNAL_FLAGS_NONE,
|
||||
on_application_signal,
|
||||
self, NULL);
|
||||
|
||||
g_dbus_connection_call (g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, NULL),
|
||||
"org.freedesktop.DBus",
|
||||
"/org/freedesktop/DBus",
|
||||
"org.freedesktop.DBus",
|
||||
"ListNames",
|
||||
NULL, /* parameters */
|
||||
G_VARIANT_TYPE ("(as)"),
|
||||
G_DBUS_CALL_FLAGS_NONE,
|
||||
-1, /* timeout */
|
||||
NULL, /* cancellable */
|
||||
on_list_names_end, self);
|
||||
|
||||
self->window_to_app = g_hash_table_new_full (NULL, NULL, NULL, g_object_unref);
|
||||
self->pid_to_dbus_connection = g_hash_table_new_full (NULL, NULL, NULL, g_free);
|
||||
|
||||
self->launched_pid_to_app = g_hash_table_new_full (NULL, NULL, NULL, (GDestroyNotify) g_object_unref);
|
||||
|
||||
@@ -610,6 +769,7 @@ shell_window_tracker_finalize (GObject *object)
|
||||
|
||||
g_hash_table_destroy (self->window_to_app);
|
||||
g_hash_table_destroy (self->launched_pid_to_app);
|
||||
g_hash_table_destroy (self->pid_to_dbus_connection);
|
||||
|
||||
G_OBJECT_CLASS (shell_window_tracker_parent_class)->finalize(object);
|
||||
}
|
||||
|
@@ -13,13 +13,8 @@ function do_action_param(action, parameter) {
|
||||
print ("Action '" + action.name + "' invoked with parameter " + parameter.print(true));
|
||||
}
|
||||
|
||||
function do_action_toggle(action) {
|
||||
action.set_state(GLib.Variant.new('b', !action.state.deep_unpack()));
|
||||
print ("Toggled");
|
||||
}
|
||||
|
||||
function do_action_state_change(action) {
|
||||
print ("Action '" + action.name + "' has now state " + action.state.print(true));
|
||||
print ("Action '" + action.name + "' has now state '" + action.state.deep_unpack() + "'");
|
||||
}
|
||||
|
||||
function main() {
|
||||
@@ -30,44 +25,48 @@ function main() {
|
||||
print ("Activated");
|
||||
});
|
||||
|
||||
let action = new Gio.SimpleAction({ name: 'one' });
|
||||
action.connect('activate', do_action);
|
||||
app.add_action(action);
|
||||
let group = new Gio.SimpleActionGroup();
|
||||
|
||||
let action = new Gio.SimpleAction({ name: 'two' });
|
||||
let action = Gio.SimpleAction.new('one', null);
|
||||
action.connect('activate', do_action);
|
||||
app.add_action(action);
|
||||
group.insert(action);
|
||||
|
||||
let action = new Gio.SimpleAction({ name: 'toggle', state: GLib.Variant.new('b', false) });
|
||||
action.connect('activate', do_action_toggle);
|
||||
let action = Gio.SimpleAction.new('two', null);
|
||||
action.connect('activate', do_action);
|
||||
group.insert(action);
|
||||
|
||||
let action = Gio.SimpleAction.new_stateful('toggle', null, GLib.Variant.new('b', false));
|
||||
action.connect('activate', do_action);
|
||||
action.connect('notify::state', do_action_state_change);
|
||||
app.add_action(action);
|
||||
group.insert(action);
|
||||
|
||||
let action = new Gio.SimpleAction({ name: 'disable', enabled: false });
|
||||
let action = Gio.SimpleAction.new('disable', null);
|
||||
action.set_enabled(false);
|
||||
action.connect('activate', do_action);
|
||||
app.add_action(action);
|
||||
group.insert(action);
|
||||
|
||||
let action = new Gio.SimpleAction({ name: 'parameter-int', parameter_type: GLib.VariantType.new('u') });
|
||||
let action = Gio.SimpleAction.new('parameter-int', GLib.VariantType.new('u'));
|
||||
action.connect('activate', do_action_param);
|
||||
app.add_action(action);
|
||||
group.insert(action);
|
||||
|
||||
let action = new Gio.SimpleAction({ name: 'parameter-string', parameter_type: GLib.VariantType.new('s') });
|
||||
let action = Gio.SimpleAction.new('parameter-string', GLib.VariantType.new('s'));
|
||||
action.connect('activate', do_action_param);
|
||||
app.add_action(action);
|
||||
group.insert(action);
|
||||
|
||||
app.action_group = group;
|
||||
|
||||
let menu = new Gio.Menu();
|
||||
menu.append('An action', 'app.one');
|
||||
menu.append('An action', 'one');
|
||||
|
||||
let section = new Gio.Menu();
|
||||
section.append('Another action', 'app.two');
|
||||
section.append('Same as above', 'app.two');
|
||||
section.append('Another action', 'two');
|
||||
section.append('Same as above', 'two');
|
||||
menu.append_section(null, section);
|
||||
|
||||
// another section, to check separators
|
||||
section = new Gio.Menu();
|
||||
section.append('Checkbox', 'app.toggle');
|
||||
section.append('Disabled', 'app.disable');
|
||||
section.append('Checkbox', 'toggle');
|
||||
section.append('Disabled', 'disable');
|
||||
menu.append_section(null, section);
|
||||
|
||||
// empty sections or submenus should be invisible
|
||||
@@ -75,26 +74,22 @@ function main() {
|
||||
menu.append_submenu('Empty submenu', new Gio.Menu());
|
||||
|
||||
let submenu = new Gio.Menu();
|
||||
submenu.append('Open c:\\', 'app.parameter-string::c:\\');
|
||||
submenu.append('Open /home', 'app.parameter-string::/home');
|
||||
submenu.append('Open c:\\', 'parameter-string::c:\\');
|
||||
submenu.append('Open /home', 'parameter-string::/home');
|
||||
menu.append_submenu('Recent files', submenu);
|
||||
|
||||
let item = Gio.MenuItem.new('Say 42', null);
|
||||
item.set_action_and_target_value('app.parameter-int', GLib.Variant.new('u', 42));
|
||||
item.set_action_and_target_value('parameter-int', GLib.Variant.new('u', 42));
|
||||
menu.append_item(item);
|
||||
|
||||
let item = Gio.MenuItem.new('Say 43', null);
|
||||
item.set_action_and_target_value('app.parameter-int', GLib.Variant.new('u', 43));
|
||||
item.set_action_and_target_value('parameter-int', GLib.Variant.new('u', 43));
|
||||
menu.append_item(item);
|
||||
|
||||
app.set_app_menu(menu);
|
||||
app.menu = menu;
|
||||
|
||||
let window = null;
|
||||
|
||||
app.connect_after('startup', function(app) {
|
||||
window = new Gtk.ApplicationWindow({ title: "Test Application", application: app });
|
||||
});
|
||||
app.connect('activate', function(app) {
|
||||
app.connect('startup', function(app) {
|
||||
let window = new Gtk.Window({ title: "Test Application", application: app });
|
||||
window.present();
|
||||
});
|
||||
|
||||
|
Reference in New Issue
Block a user