build: Remove remote menu support
It is now unused. https://gitlab.gnome.org/GNOME/gnome-shell/issues/624
This commit is contained in:
parent
ce6ab7e121
commit
996369a22d
@ -82,7 +82,6 @@
|
||||
<file>ui/panelMenu.js</file>
|
||||
<file>ui/pointerWatcher.js</file>
|
||||
<file>ui/popupMenu.js</file>
|
||||
<file>ui/remoteMenu.js</file>
|
||||
<file>ui/remoteSearch.js</file>
|
||||
<file>ui/runDialog.js</file>
|
||||
<file>ui/screenShield.js</file>
|
||||
|
@ -1,199 +0,0 @@
|
||||
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
|
||||
|
||||
const Atk = imports.gi.Atk;
|
||||
const GLib = imports.gi.GLib;
|
||||
const GObject = imports.gi.GObject;
|
||||
const Gio = imports.gi.Gio;
|
||||
const Shell = imports.gi.Shell;
|
||||
const ShellMenu = imports.gi.ShellMenu;
|
||||
const St = imports.gi.St;
|
||||
|
||||
const PopupMenu = imports.ui.popupMenu;
|
||||
|
||||
function stripMnemonics(label) {
|
||||
if (!label)
|
||||
return '';
|
||||
|
||||
// remove all underscores that are not followed by another underscore
|
||||
return label.replace(/_([^_])/, '$1');
|
||||
}
|
||||
|
||||
function _insertItem(menu, trackerItem, position) {
|
||||
let mapper;
|
||||
|
||||
if (trackerItem.get_is_separator())
|
||||
mapper = new RemoteMenuSeparatorItemMapper(trackerItem);
|
||||
else if (trackerItem.get_has_submenu())
|
||||
mapper = new RemoteMenuSubmenuItemMapper(trackerItem);
|
||||
else
|
||||
mapper = new RemoteMenuItemMapper(trackerItem);
|
||||
|
||||
let item = mapper.menuItem;
|
||||
menu.addMenuItem(item, position);
|
||||
}
|
||||
|
||||
function _removeItem(menu, position) {
|
||||
let items = menu._getMenuItems();
|
||||
items[position].destroy();
|
||||
}
|
||||
|
||||
var RemoteMenuSeparatorItemMapper = class {
|
||||
constructor(trackerItem) {
|
||||
this._trackerItem = trackerItem;
|
||||
this.menuItem = new PopupMenu.PopupSeparatorMenuItem();
|
||||
this._trackerItem.connect('notify::label', this._updateLabel.bind(this));
|
||||
this._updateLabel();
|
||||
|
||||
this.menuItem.connect('destroy', () => {
|
||||
trackerItem.run_dispose();
|
||||
});
|
||||
}
|
||||
|
||||
_updateLabel() {
|
||||
this.menuItem.label.text = stripMnemonics(this._trackerItem.label);
|
||||
}
|
||||
};
|
||||
|
||||
var RequestSubMenu = class extends PopupMenu.PopupSubMenuMenuItem {
|
||||
constructor() {
|
||||
super('');
|
||||
this._requestOpen = false;
|
||||
}
|
||||
|
||||
_setOpenState(open) {
|
||||
this.emit('request-open', open);
|
||||
this._requestOpen = open;
|
||||
}
|
||||
|
||||
_getOpenState() {
|
||||
return this._requestOpen;
|
||||
}
|
||||
};
|
||||
|
||||
var RemoteMenuSubmenuItemMapper = class {
|
||||
constructor(trackerItem) {
|
||||
this._trackerItem = trackerItem;
|
||||
this.menuItem = new RequestSubMenu();
|
||||
this._trackerItem.connect('notify::label', this._updateLabel.bind(this));
|
||||
this._updateLabel();
|
||||
|
||||
this._tracker = Shell.MenuTracker.new_for_item_submenu(this._trackerItem,
|
||||
_insertItem.bind(null, this.menuItem.menu),
|
||||
_removeItem.bind(null, this.menuItem.menu));
|
||||
|
||||
this.menuItem.connect('request-open', (menu, open) => {
|
||||
this._trackerItem.request_submenu_shown(open);
|
||||
});
|
||||
|
||||
this._trackerItem.connect('notify::submenu-shown', () => {
|
||||
this.menuItem.setSubmenuShown(this._trackerItem.get_submenu_shown());
|
||||
});
|
||||
|
||||
this.menuItem.connect('destroy', () => {
|
||||
trackerItem.run_dispose();
|
||||
});
|
||||
}
|
||||
|
||||
destroy() {
|
||||
this._tracker.destroy();
|
||||
}
|
||||
|
||||
_updateLabel() {
|
||||
this.menuItem.label.text = stripMnemonics(this._trackerItem.label);
|
||||
}
|
||||
};
|
||||
|
||||
var RemoteMenuItemMapper = class {
|
||||
constructor(trackerItem) {
|
||||
this._trackerItem = trackerItem;
|
||||
|
||||
this.menuItem = new PopupMenu.PopupBaseMenuItem();
|
||||
this._icon = new St.Icon({ style_class: 'popup-menu-icon' });
|
||||
this.menuItem.actor.add_child(this._icon);
|
||||
|
||||
this._label = new St.Label();
|
||||
this.menuItem.actor.add_child(this._label);
|
||||
this.menuItem.actor.label_actor = this._label;
|
||||
|
||||
this.menuItem.connect('activate', () => {
|
||||
this._trackerItem.activated();
|
||||
});
|
||||
|
||||
this._trackerItem.bind_property('visible', this.menuItem.actor, 'visible', GObject.BindingFlags.SYNC_CREATE);
|
||||
|
||||
this._trackerItem.connect('notify::icon', this._updateIcon.bind(this));
|
||||
this._trackerItem.connect('notify::label', this._updateLabel.bind(this));
|
||||
this._trackerItem.connect('notify::sensitive', this._updateSensitivity.bind(this));
|
||||
this._trackerItem.connect('notify::role', this._updateRole.bind(this));
|
||||
this._trackerItem.connect('notify::toggled', this._updateDecoration.bind(this));
|
||||
|
||||
this._updateIcon();
|
||||
this._updateLabel();
|
||||
this._updateSensitivity();
|
||||
this._updateRole();
|
||||
|
||||
this.menuItem.connect('destroy', () => {
|
||||
trackerItem.run_dispose();
|
||||
});
|
||||
}
|
||||
|
||||
_updateIcon() {
|
||||
this._icon.gicon = this._trackerItem.icon;
|
||||
this._icon.visible = (this._icon.gicon != null);
|
||||
}
|
||||
|
||||
_updateLabel() {
|
||||
this._label.text = stripMnemonics(this._trackerItem.label);
|
||||
}
|
||||
|
||||
_updateSensitivity() {
|
||||
this.menuItem.setSensitive(this._trackerItem.sensitive);
|
||||
}
|
||||
|
||||
_updateDecoration() {
|
||||
let ornamentForRole = {};
|
||||
ornamentForRole[ShellMenu.MenuTrackerItemRole.RADIO] = PopupMenu.Ornament.DOT;
|
||||
ornamentForRole[ShellMenu.MenuTrackerItemRole.CHECK] = PopupMenu.Ornament.CHECK;
|
||||
|
||||
let ornament = PopupMenu.Ornament.NONE;
|
||||
if (this._trackerItem.toggled)
|
||||
ornament = ornamentForRole[this._trackerItem.role];
|
||||
|
||||
this.menuItem.setOrnament(ornament);
|
||||
}
|
||||
|
||||
_updateRole() {
|
||||
let a11yRoles = {};
|
||||
a11yRoles[ShellMenu.MenuTrackerItemRole.NORMAL] = Atk.Role.MENU_ITEM;
|
||||
a11yRoles[ShellMenu.MenuTrackerItemRole.RADIO] = Atk.Role.RADIO_MENU_ITEM;
|
||||
a11yRoles[ShellMenu.MenuTrackerItemRole.CHECK] = Atk.Role.CHECK_MENU_ITEM;
|
||||
|
||||
let a11yRole = a11yRoles[this._trackerItem.role];
|
||||
this.menuItem.actor.accessible_role = a11yRole;
|
||||
|
||||
this._updateDecoration();
|
||||
}
|
||||
};
|
||||
|
||||
var RemoteMenu = class extends PopupMenu.PopupMenu {
|
||||
constructor(sourceActor, model, actionGroup) {
|
||||
super(sourceActor, 0.0, St.Side.TOP);
|
||||
|
||||
this._model = model;
|
||||
this._actionGroup = actionGroup;
|
||||
this._tracker = Shell.MenuTracker.new(this._actionGroup,
|
||||
this._model,
|
||||
null, /* action namespace */
|
||||
_insertItem.bind(null, this),
|
||||
_removeItem.bind(null, this));
|
||||
}
|
||||
|
||||
get actionGroup() {
|
||||
return this._actionGroup;
|
||||
}
|
||||
|
||||
destroy() {
|
||||
this._tracker.destroy();
|
||||
super.destroy();
|
||||
}
|
||||
};
|
@ -1,596 +0,0 @@
|
||||
/*
|
||||
* Copyright © 2013 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Author: Ryan Lortie <desrt@desrt.ca>
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include "gtkmenutracker.h"
|
||||
|
||||
/**
|
||||
* SECTION:gtkmenutracker
|
||||
* @Title: GtkMenuTracker
|
||||
* @Short_description: A helper class for interpreting #GMenuModel
|
||||
*
|
||||
* #GtkMenuTracker is a simple object to ease implementations of #GMenuModel.
|
||||
* Given a #GtkActionObservable (usually a #GActionMuxer) along with a
|
||||
* #GMenuModel, it will tell you which menu items to create and where to place
|
||||
* them. If a menu item is removed, it will tell you the position of the menu
|
||||
* item to remove.
|
||||
*
|
||||
* Using #GtkMenuTracker is fairly simple. The only guarantee you must make
|
||||
* to #GtkMenuTracker is that you must obey all insert signals and track the
|
||||
* position of items that #GtkMenuTracker gives you. That is, #GtkMenuTracker
|
||||
* expects positions of all the latter items to change when it calls your
|
||||
* insertion callback with an early position, as it may ask you to remove
|
||||
* an item with a readjusted position later.
|
||||
*
|
||||
* #GtkMenuTracker will give you a #GtkMenuTrackerItem in your callback. You
|
||||
* must hold onto this object until a remove signal is emitted. This item
|
||||
* represents a single menu item, which can be one of three classes: normal item,
|
||||
* separator, or submenu.
|
||||
*
|
||||
* Certain properties on the #GtkMenuTrackerItem are mutable, and you must
|
||||
* listen for changes in the item. For more details, see the documentation
|
||||
* for #GtkMenuTrackerItem along with https://live.gnome.org/GApplication/GMenuModel.
|
||||
*
|
||||
* The idea of @with_separators is for special cases where menu models may
|
||||
* be tracked in places where separators are not available, like in toplevel
|
||||
* "File", "Edit" menu bars. Ignoring separator items is wrong, as #GtkMenuTracker
|
||||
* expects the position to change, so we must tell #GtkMenuTracker to ignore
|
||||
* separators itself.
|
||||
*/
|
||||
|
||||
typedef struct _GtkMenuTrackerSection GtkMenuTrackerSection;
|
||||
|
||||
struct _GtkMenuTracker
|
||||
{
|
||||
GtkActionObservable *observable;
|
||||
GtkMenuTrackerInsertFunc insert_func;
|
||||
GtkMenuTrackerRemoveFunc remove_func;
|
||||
gpointer user_data;
|
||||
|
||||
GtkMenuTrackerSection *toplevel;
|
||||
};
|
||||
|
||||
struct _GtkMenuTrackerSection
|
||||
{
|
||||
gpointer model; /* may be a GtkMenuTrackerItem or a GMenuModel */
|
||||
GSList *items;
|
||||
gchar *action_namespace;
|
||||
|
||||
guint separator_label : 1;
|
||||
guint with_separators : 1;
|
||||
guint has_separator : 1;
|
||||
guint is_fake : 1;
|
||||
|
||||
gulong handler;
|
||||
};
|
||||
|
||||
static GtkMenuTrackerSection * gtk_menu_tracker_section_new (GtkMenuTracker *tracker,
|
||||
GMenuModel *model,
|
||||
gboolean with_separators,
|
||||
gboolean separator_label,
|
||||
gint offset,
|
||||
const gchar *action_namespace);
|
||||
static void gtk_menu_tracker_section_free (GtkMenuTrackerSection *section);
|
||||
|
||||
static GtkMenuTrackerSection *
|
||||
gtk_menu_tracker_section_find_model (GtkMenuTrackerSection *section,
|
||||
gpointer model,
|
||||
gint *offset)
|
||||
{
|
||||
GSList *item;
|
||||
|
||||
if (section->has_separator)
|
||||
(*offset)++;
|
||||
|
||||
if (section->model == model)
|
||||
return section;
|
||||
|
||||
for (item = section->items; item; item = item->next)
|
||||
{
|
||||
GtkMenuTrackerSection *subsection = item->data;
|
||||
|
||||
if (subsection)
|
||||
{
|
||||
GtkMenuTrackerSection *found_section;
|
||||
|
||||
found_section = gtk_menu_tracker_section_find_model (subsection, model, offset);
|
||||
|
||||
if (found_section)
|
||||
return found_section;
|
||||
}
|
||||
else
|
||||
(*offset)++;
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/* this is responsible for syncing the showing of a separator for a
|
||||
* single subsection (and its children).
|
||||
*
|
||||
* we only ever show separators if we have _actual_ children (ie: we do
|
||||
* not show a separator if the section contains only empty child
|
||||
* sections). it's difficult to determine this on-the-fly, so we have
|
||||
* this separate function to come back later and figure it out.
|
||||
*
|
||||
* 'section' is that section.
|
||||
*
|
||||
* 'tracker' is passed in so that we can emit callbacks when we decide
|
||||
* to add/remove separators.
|
||||
*
|
||||
* 'offset' is passed in so we know which position to emit in our
|
||||
* callbacks. ie: if we add a separator right at the top of this
|
||||
* section then we would emit it with this offset. deeper inside, we
|
||||
* adjust accordingly.
|
||||
*
|
||||
* could_have_separator is true in two situations:
|
||||
*
|
||||
* - our parent section had with_separators defined and there are items
|
||||
* before us (ie: we should add a separator if we have content in
|
||||
* order to divide us from the items above)
|
||||
*
|
||||
* - if we had a 'label' attribute set for this section
|
||||
*
|
||||
* parent_model and parent_index are passed in so that we can give them
|
||||
* to the insertion callback so that it can see the label (and anything
|
||||
* else that happens to be defined on the section).
|
||||
*
|
||||
* we iterate each item in ourselves. for subsections, we recursively
|
||||
* run ourselves to sync separators. after we are done, we notice if we
|
||||
* have any items in us or if we are completely empty and sync if our
|
||||
* separator is shown or not.
|
||||
*/
|
||||
static gint
|
||||
gtk_menu_tracker_section_sync_separators (GtkMenuTrackerSection *section,
|
||||
GtkMenuTracker *tracker,
|
||||
gint offset,
|
||||
gboolean could_have_separator,
|
||||
GMenuModel *parent_model,
|
||||
gint parent_index)
|
||||
{
|
||||
gboolean should_have_separator;
|
||||
gint n_items = 0;
|
||||
GSList *item;
|
||||
gint i = 0;
|
||||
|
||||
for (item = section->items; item; item = item->next)
|
||||
{
|
||||
GtkMenuTrackerSection *subsection = item->data;
|
||||
|
||||
if (subsection)
|
||||
{
|
||||
gboolean section_could_have_separator;
|
||||
|
||||
section_could_have_separator = (section->with_separators && n_items > 0) || subsection->separator_label;
|
||||
|
||||
/* Only pass the parent_model and parent_index in case they may be used to create the separator. */
|
||||
n_items += gtk_menu_tracker_section_sync_separators (subsection, tracker, offset + n_items,
|
||||
section_could_have_separator,
|
||||
section_could_have_separator ? section->model : NULL,
|
||||
section_could_have_separator ? i : 0);
|
||||
}
|
||||
else
|
||||
n_items++;
|
||||
|
||||
i++;
|
||||
}
|
||||
|
||||
should_have_separator = !section->is_fake && could_have_separator && n_items != 0;
|
||||
|
||||
if (should_have_separator > section->has_separator)
|
||||
{
|
||||
/* Add a separator */
|
||||
GtkMenuTrackerItem *menuitem;
|
||||
|
||||
menuitem = _gtk_menu_tracker_item_new (tracker->observable, parent_model, parent_index, NULL, TRUE);
|
||||
(* tracker->insert_func) (menuitem, offset, tracker->user_data);
|
||||
g_object_unref (menuitem);
|
||||
|
||||
section->has_separator = TRUE;
|
||||
}
|
||||
else if (should_have_separator < section->has_separator)
|
||||
{
|
||||
/* Remove a separator */
|
||||
(* tracker->remove_func) (offset, tracker->user_data);
|
||||
section->has_separator = FALSE;
|
||||
}
|
||||
|
||||
n_items += section->has_separator;
|
||||
|
||||
return n_items;
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_menu_tracker_item_visibility_changed (GtkMenuTrackerItem *item,
|
||||
gboolean is_now_visible,
|
||||
gpointer user_data)
|
||||
{
|
||||
GtkMenuTracker *tracker = user_data;
|
||||
GtkMenuTrackerSection *section;
|
||||
gboolean was_visible;
|
||||
gint offset = 0;
|
||||
|
||||
/* remember: the item is our model */
|
||||
section = gtk_menu_tracker_section_find_model (tracker->toplevel, item, &offset);
|
||||
|
||||
was_visible = section->items != NULL;
|
||||
|
||||
if (is_now_visible == was_visible)
|
||||
return;
|
||||
|
||||
if (is_now_visible)
|
||||
{
|
||||
section->items = g_slist_prepend (NULL, NULL);
|
||||
(* tracker->insert_func) (section->model, offset, tracker->user_data);
|
||||
}
|
||||
else
|
||||
{
|
||||
section->items = g_slist_delete_link (section->items, section->items);
|
||||
(* tracker->remove_func) (offset, tracker->user_data);
|
||||
}
|
||||
|
||||
gtk_menu_tracker_section_sync_separators (tracker->toplevel, tracker, 0, FALSE, NULL, 0);
|
||||
}
|
||||
|
||||
static gint
|
||||
gtk_menu_tracker_section_measure (GtkMenuTrackerSection *section)
|
||||
{
|
||||
GSList *item;
|
||||
gint n_items;
|
||||
|
||||
if (section == NULL)
|
||||
return 1;
|
||||
|
||||
n_items = 0;
|
||||
|
||||
if (section->has_separator)
|
||||
n_items++;
|
||||
|
||||
for (item = section->items; item; item = item->next)
|
||||
n_items += gtk_menu_tracker_section_measure (item->data);
|
||||
|
||||
return n_items;
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_menu_tracker_remove_items (GtkMenuTracker *tracker,
|
||||
GSList **change_point,
|
||||
gint offset,
|
||||
gint n_items)
|
||||
{
|
||||
gint i;
|
||||
|
||||
for (i = 0; i < n_items; i++)
|
||||
{
|
||||
GtkMenuTrackerSection *subsection;
|
||||
gint n;
|
||||
|
||||
subsection = (*change_point)->data;
|
||||
*change_point = g_slist_delete_link (*change_point, *change_point);
|
||||
|
||||
n = gtk_menu_tracker_section_measure (subsection);
|
||||
gtk_menu_tracker_section_free (subsection);
|
||||
|
||||
while (n--)
|
||||
(* tracker->remove_func) (offset, tracker->user_data);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_menu_tracker_add_items (GtkMenuTracker *tracker,
|
||||
GtkMenuTrackerSection *section,
|
||||
GSList **change_point,
|
||||
gint offset,
|
||||
GMenuModel *model,
|
||||
gint position,
|
||||
gint n_items)
|
||||
{
|
||||
while (n_items--)
|
||||
{
|
||||
GMenuModel *submenu;
|
||||
|
||||
submenu = g_menu_model_get_item_link (model, position + n_items, G_MENU_LINK_SECTION);
|
||||
g_assert (submenu != model);
|
||||
if (submenu != NULL)
|
||||
{
|
||||
GtkMenuTrackerSection *subsection;
|
||||
gchar *action_namespace = NULL;
|
||||
gboolean has_label;
|
||||
|
||||
has_label = g_menu_model_get_item_attribute (model, position + n_items,
|
||||
G_MENU_ATTRIBUTE_LABEL, "s", NULL);
|
||||
|
||||
g_menu_model_get_item_attribute (model, position + n_items,
|
||||
G_MENU_ATTRIBUTE_ACTION_NAMESPACE, "s", &action_namespace);
|
||||
|
||||
if (section->action_namespace)
|
||||
{
|
||||
gchar *namespace;
|
||||
|
||||
namespace = g_strjoin (".", section->action_namespace, action_namespace, NULL);
|
||||
subsection = gtk_menu_tracker_section_new (tracker, submenu, FALSE, has_label, offset, namespace);
|
||||
g_free (namespace);
|
||||
}
|
||||
else
|
||||
subsection = gtk_menu_tracker_section_new (tracker, submenu, FALSE, has_label, offset, action_namespace);
|
||||
|
||||
*change_point = g_slist_prepend (*change_point, subsection);
|
||||
g_free (action_namespace);
|
||||
g_object_unref (submenu);
|
||||
}
|
||||
else
|
||||
{
|
||||
GtkMenuTrackerItem *item;
|
||||
|
||||
item = _gtk_menu_tracker_item_new (tracker->observable, model, position + n_items,
|
||||
section->action_namespace, FALSE);
|
||||
|
||||
/* In the case that the item may disappear we handle that by
|
||||
* treating the item that we just created as being its own
|
||||
* subsection. This happens as so:
|
||||
*
|
||||
* - the subsection is created without the possibility of
|
||||
* showing a separator
|
||||
*
|
||||
* - the subsection will have either 0 or 1 item in it at all
|
||||
* times: either the shown item or not (in the case it is
|
||||
* hidden)
|
||||
*
|
||||
* - the created item acts as the "model" for this section
|
||||
* and we use its "visiblity-changed" signal in the same
|
||||
* way that we use the "items-changed" signal from a real
|
||||
* GMenuModel
|
||||
*
|
||||
* We almost never use the '->model' stored in the section for
|
||||
* anything other than lookups and for dropped the ref and
|
||||
* disconnecting the signal when we destroy the menu, and we
|
||||
* need to do exactly those things in this case as well.
|
||||
*
|
||||
* The only other thing that '->model' is used for is in the
|
||||
* case that we want to show a separator, but we will never do
|
||||
* that because separators are not shown for this fake section.
|
||||
*/
|
||||
if (_gtk_menu_tracker_item_may_disappear (item))
|
||||
{
|
||||
GtkMenuTrackerSection *fake_section;
|
||||
|
||||
fake_section = g_slice_new0 (GtkMenuTrackerSection);
|
||||
fake_section->is_fake = TRUE;
|
||||
fake_section->model = g_object_ref (item);
|
||||
fake_section->handler = g_signal_connect (item, "visibility-changed",
|
||||
G_CALLBACK (gtk_menu_tracker_item_visibility_changed),
|
||||
tracker);
|
||||
*change_point = g_slist_prepend (*change_point, fake_section);
|
||||
|
||||
if (_gtk_menu_tracker_item_is_visible (item))
|
||||
{
|
||||
(* tracker->insert_func) (item, offset, tracker->user_data);
|
||||
fake_section->items = g_slist_prepend (NULL, NULL);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/* In the normal case, we store NULL in the linked list.
|
||||
* The measurement and lookup code count NULL always as
|
||||
* exactly 1: an item that will always be there.
|
||||
*/
|
||||
(* tracker->insert_func) (item, offset, tracker->user_data);
|
||||
*change_point = g_slist_prepend (*change_point, NULL);
|
||||
}
|
||||
|
||||
g_object_unref (item);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_menu_tracker_model_changed (GMenuModel *model,
|
||||
gint position,
|
||||
gint removed,
|
||||
gint added,
|
||||
gpointer user_data)
|
||||
{
|
||||
GtkMenuTracker *tracker = user_data;
|
||||
GtkMenuTrackerSection *section;
|
||||
GSList **change_point;
|
||||
gint offset = 0;
|
||||
gint i;
|
||||
|
||||
/* First find which section the changed model corresponds to, and the
|
||||
* position of that section within the overall menu.
|
||||
*/
|
||||
section = gtk_menu_tracker_section_find_model (tracker->toplevel, model, &offset);
|
||||
|
||||
/* Next, seek through that section to the change point. This gives us
|
||||
* the correct GSList** to make the change to and also finds the final
|
||||
* offset at which we will make the changes (by measuring the number
|
||||
* of items within each item of the section before the change point).
|
||||
*/
|
||||
change_point = §ion->items;
|
||||
for (i = 0; i < position; i++)
|
||||
{
|
||||
offset += gtk_menu_tracker_section_measure ((*change_point)->data);
|
||||
change_point = &(*change_point)->next;
|
||||
}
|
||||
|
||||
/* We remove items in order and add items in reverse order. This
|
||||
* means that the offset used for all inserts and removes caused by a
|
||||
* single change will be the same.
|
||||
*
|
||||
* This also has a performance advantage: GtkMenuShell stores the
|
||||
* menu items in a linked list. In the case where we are creating a
|
||||
* menu for the first time, adding the items in reverse order means
|
||||
* that we only ever insert at index zero, prepending the list. This
|
||||
* means that we can populate in O(n) time instead of O(n^2) that we
|
||||
* would do by appending.
|
||||
*/
|
||||
gtk_menu_tracker_remove_items (tracker, change_point, offset, removed);
|
||||
gtk_menu_tracker_add_items (tracker, section, change_point, offset, model, position, added);
|
||||
|
||||
/* The offsets for insertion/removal of separators will be all over
|
||||
* the place, however...
|
||||
*/
|
||||
gtk_menu_tracker_section_sync_separators (tracker->toplevel, tracker, 0, FALSE, NULL, 0);
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_menu_tracker_section_free (GtkMenuTrackerSection *section)
|
||||
{
|
||||
if (section == NULL)
|
||||
return;
|
||||
|
||||
g_signal_handler_disconnect (section->model, section->handler);
|
||||
g_slist_free_full (section->items, (GDestroyNotify) gtk_menu_tracker_section_free);
|
||||
g_free (section->action_namespace);
|
||||
g_object_unref (section->model);
|
||||
g_slice_free (GtkMenuTrackerSection, section);
|
||||
}
|
||||
|
||||
static GtkMenuTrackerSection *
|
||||
gtk_menu_tracker_section_new (GtkMenuTracker *tracker,
|
||||
GMenuModel *model,
|
||||
gboolean with_separators,
|
||||
gboolean separator_label,
|
||||
gint offset,
|
||||
const gchar *action_namespace)
|
||||
{
|
||||
GtkMenuTrackerSection *section;
|
||||
|
||||
section = g_slice_new0 (GtkMenuTrackerSection);
|
||||
section->model = g_object_ref (model);
|
||||
section->with_separators = with_separators;
|
||||
section->action_namespace = g_strdup (action_namespace);
|
||||
section->separator_label = separator_label;
|
||||
|
||||
gtk_menu_tracker_add_items (tracker, section, §ion->items, offset, model, 0, g_menu_model_get_n_items (model));
|
||||
section->handler = g_signal_connect (model, "items-changed", G_CALLBACK (gtk_menu_tracker_model_changed), tracker);
|
||||
|
||||
return section;
|
||||
}
|
||||
|
||||
/*< private >
|
||||
* gtk_menu_tracker_new:
|
||||
* @model: the model to flatten
|
||||
* @with_separators: if the toplevel should have separators (ie: TRUE
|
||||
* for menus, FALSE for menubars)
|
||||
* @action_namespace: the passed-in action namespace
|
||||
* @insert_func: insert callback
|
||||
* @remove_func: remove callback
|
||||
* @user_data user data for callbacks
|
||||
*
|
||||
* Creates a GtkMenuTracker for @model, holding a ref on @model for as
|
||||
* long as the tracker is alive.
|
||||
*
|
||||
* This flattens out the model, merging sections and inserting
|
||||
* separators where appropriate. It monitors for changes and performs
|
||||
* updates on the fly. It also handles action_namespace for subsections
|
||||
* (but you will need to handle it yourself for submenus).
|
||||
*
|
||||
* When the tracker is first created, @insert_func will be called many
|
||||
* times to populate the menu with the initial contents of @model
|
||||
* (unless it is empty), before gtk_menu_tracker_new() returns. For
|
||||
* this reason, the menu that is using the tracker ought to be empty
|
||||
* when it creates the tracker.
|
||||
*
|
||||
* Future changes to @model will result in more calls to @insert_func
|
||||
* and @remove_func.
|
||||
*
|
||||
* The position argument to both functions is the linear 0-based
|
||||
* position in the menu at which the item in question should be inserted
|
||||
* or removed.
|
||||
*
|
||||
* For @insert_func, @model and @item_index are used to get the
|
||||
* information about the menu item to insert. @action_namespace is the
|
||||
* action namespace that actions referred to from that item should place
|
||||
* themselves in. Note that if the item is a submenu and the
|
||||
* "action-namespace" attribute is defined on the item, it will _not_ be
|
||||
* applied to the @action_namespace argument as it is meant for the
|
||||
* items inside of the submenu, not the submenu item itself.
|
||||
*
|
||||
* @is_separator is set to %TRUE in case the item being added is a
|
||||
* separator. @model and @item_index will still be meaningfully set in
|
||||
* this case -- to the section menu item corresponding to the separator.
|
||||
* This is useful if the section specifies a label, for example. If
|
||||
* there is an "action-namespace" attribute on this menu item then it
|
||||
* should be ignored by the consumer because #GtkMenuTracker has already
|
||||
* handled it.
|
||||
*
|
||||
* When using #GtkMenuTracker there is no need to hold onto @model or
|
||||
* monitor it for changes. The model will be unreffed when
|
||||
* gtk_menu_tracker_free() is called.
|
||||
*/
|
||||
GtkMenuTracker *
|
||||
gtk_menu_tracker_new (GtkActionObservable *observable,
|
||||
GMenuModel *model,
|
||||
gboolean with_separators,
|
||||
const gchar *action_namespace,
|
||||
GtkMenuTrackerInsertFunc insert_func,
|
||||
GtkMenuTrackerRemoveFunc remove_func,
|
||||
gpointer user_data)
|
||||
{
|
||||
GtkMenuTracker *tracker;
|
||||
|
||||
tracker = g_slice_new (GtkMenuTracker);
|
||||
tracker->observable = g_object_ref (observable);
|
||||
tracker->insert_func = insert_func;
|
||||
tracker->remove_func = remove_func;
|
||||
tracker->user_data = user_data;
|
||||
|
||||
tracker->toplevel = gtk_menu_tracker_section_new (tracker, model, with_separators, FALSE, 0, action_namespace);
|
||||
gtk_menu_tracker_section_sync_separators (tracker->toplevel, tracker, 0, FALSE, NULL, 0);
|
||||
|
||||
return tracker;
|
||||
}
|
||||
|
||||
GtkMenuTracker *
|
||||
gtk_menu_tracker_new_for_item_submenu (GtkMenuTrackerItem *item,
|
||||
GtkMenuTrackerInsertFunc insert_func,
|
||||
GtkMenuTrackerRemoveFunc remove_func,
|
||||
gpointer user_data)
|
||||
{
|
||||
GtkMenuTracker *tracker;
|
||||
GMenuModel *submenu;
|
||||
gchar *namespace;
|
||||
|
||||
submenu = _gtk_menu_tracker_item_get_submenu (item);
|
||||
namespace = _gtk_menu_tracker_item_get_submenu_namespace (item);
|
||||
|
||||
tracker = gtk_menu_tracker_new (_gtk_menu_tracker_item_get_observable (item), submenu,
|
||||
TRUE, namespace, insert_func, remove_func, user_data);
|
||||
|
||||
g_object_unref (submenu);
|
||||
g_free (namespace);
|
||||
|
||||
return tracker;
|
||||
}
|
||||
|
||||
/*< private >
|
||||
* gtk_menu_tracker_free:
|
||||
* @tracker: a #GtkMenuTracker
|
||||
*
|
||||
* Frees the tracker, ...
|
||||
*/
|
||||
void
|
||||
gtk_menu_tracker_free (GtkMenuTracker *tracker)
|
||||
{
|
||||
gtk_menu_tracker_section_free (tracker->toplevel);
|
||||
g_object_unref (tracker->observable);
|
||||
g_slice_free (GtkMenuTracker, tracker);
|
||||
}
|
@ -1,50 +0,0 @@
|
||||
/*
|
||||
* Copyright © 2013 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Author: Ryan Lortie <desrt@desrt.ca>
|
||||
*/
|
||||
|
||||
#ifndef __GTK_MENU_TRACKER_H__
|
||||
#define __GTK_MENU_TRACKER_H__
|
||||
|
||||
#include "gtkmenutrackeritem.h"
|
||||
|
||||
typedef struct _GtkMenuTracker GtkMenuTracker;
|
||||
|
||||
typedef void (* GtkMenuTrackerInsertFunc) (GtkMenuTrackerItem *item,
|
||||
gint position,
|
||||
gpointer user_data);
|
||||
|
||||
typedef void (* GtkMenuTrackerRemoveFunc) (gint position,
|
||||
gpointer user_data);
|
||||
|
||||
|
||||
GtkMenuTracker * gtk_menu_tracker_new (GtkActionObservable *observer,
|
||||
GMenuModel *model,
|
||||
gboolean with_separators,
|
||||
const gchar *action_namespace,
|
||||
GtkMenuTrackerInsertFunc insert_func,
|
||||
GtkMenuTrackerRemoveFunc remove_func,
|
||||
gpointer user_data);
|
||||
|
||||
GtkMenuTracker * gtk_menu_tracker_new_for_item_submenu (GtkMenuTrackerItem *item,
|
||||
GtkMenuTrackerInsertFunc insert_func,
|
||||
GtkMenuTrackerRemoveFunc remove_func,
|
||||
gpointer user_data);
|
||||
|
||||
void gtk_menu_tracker_free (GtkMenuTracker *tracker);
|
||||
|
||||
#endif /* __GTK_MENU_TRACKER_H__ */
|
@ -1,894 +0,0 @@
|
||||
/*
|
||||
* Copyright © 2013 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Author: Ryan Lortie <desrt@desrt.ca>
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include "gtkmenutrackeritem.h"
|
||||
#include "gtkactionmuxer.h"
|
||||
|
||||
#include "gtkactionmuxer.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
/**
|
||||
* SECTION:gtkmenutrackeritem
|
||||
* @Title: GtkMenuTrackerItem
|
||||
* @Short_description: Small helper for model menu items
|
||||
*
|
||||
* A #GtkMenuTrackerItem is a small helper class used by #GtkMenuTracker to
|
||||
* represent menu items. It has one of three classes: normal item, separator,
|
||||
* or submenu.
|
||||
*
|
||||
* If an item is one of the non-normal classes (submenu, separator), only the
|
||||
* label of the item needs to be respected. Otherwise, all the properties
|
||||
* of the item contribute to the item's appearance and state.
|
||||
*
|
||||
* Implementing the appearance of the menu item is up to toolkits, and certain
|
||||
* toolkits may choose to ignore certain properties, like icon or accel. The
|
||||
* role of the item determines its accessibility role, along with its
|
||||
* decoration if the GtkMenuTrackerItem::toggled property is true. As an
|
||||
* example, if the item has the role %GTK_MENU_TRACKER_ITEM_ROLE_CHECK and
|
||||
* GtkMenuTrackerItem::toggled is %FALSE, its accessible role should be that of
|
||||
* a check menu item, and no decoration should be drawn. But if
|
||||
* GtkMenuTrackerItem::toggled is %TRUE, a checkmark should be drawn.
|
||||
*
|
||||
* All properties except for the two class-determining properties,
|
||||
* GtkMenuTrackerItem::is-separator and GtkMenuTrackerItem::has-submenu are
|
||||
* allowed to change, so listen to the notify signals to update your item's
|
||||
* appearance. When using a GObject library, this can conveniently be done
|
||||
* with g_object_bind_property() and #GBinding, and this is how this is
|
||||
* implemented in GTK+; the appearance side is implemented in #GtkModelMenuItem.
|
||||
*
|
||||
* When an item is clicked, simply call gtk_menu_tracker_item_activated() in
|
||||
* response. The #GtkMenuTrackerItem will take care of everything related to
|
||||
* activating the item and will itself update the state of all items in
|
||||
* response.
|
||||
*
|
||||
* Submenus are a special case of menu item. When an item is a submenu, you
|
||||
* should create a submenu for it with gtk_menu_tracker_new_item_for_submenu(),
|
||||
* and apply the same menu tracking logic you would for a toplevel menu.
|
||||
* Applications using submenus may want to lazily build their submenus in
|
||||
* response to the user clicking on it, as building a submenu may be expensive.
|
||||
*
|
||||
* Thus, the submenu has two special controls -- the submenu's visibility
|
||||
* should be controlled by the GtkMenuTrackerItem::submenu-shown property,
|
||||
* and if a user clicks on the submenu, do not immediately show the menu,
|
||||
* but call gtk_menu_tracker_item_request_submenu_shown() and wait for the
|
||||
* GtkMenuTrackerItem::submenu-shown property to update. If the user navigates,
|
||||
* the application may want to be notified so it can cancel the expensive
|
||||
* operation that it was using to build the submenu. Thus,
|
||||
* gtk_menu_tracker_item_request_submenu_shown() takes a boolean parameter.
|
||||
* Use %TRUE when the user wants to open the submenu, and %FALSE when the
|
||||
* user wants to close the submenu.
|
||||
*/
|
||||
|
||||
typedef GObjectClass GtkMenuTrackerItemClass;
|
||||
|
||||
struct _GtkMenuTrackerItem
|
||||
{
|
||||
GObject parent_instance;
|
||||
|
||||
GtkActionObservable *observable;
|
||||
gchar *action_namespace;
|
||||
gchar *action_and_target;
|
||||
GMenuItem *item;
|
||||
GtkMenuTrackerItemRole role : 4;
|
||||
guint is_separator : 1;
|
||||
guint can_activate : 1;
|
||||
guint sensitive : 1;
|
||||
guint toggled : 1;
|
||||
guint submenu_shown : 1;
|
||||
guint submenu_requested : 1;
|
||||
guint hidden_when : 2;
|
||||
guint is_visible : 1;
|
||||
};
|
||||
|
||||
#define HIDDEN_NEVER 0
|
||||
#define HIDDEN_WHEN_MISSING 1
|
||||
#define HIDDEN_WHEN_DISABLED 2
|
||||
|
||||
enum {
|
||||
PROP_0,
|
||||
PROP_IS_SEPARATOR,
|
||||
PROP_HAS_SUBMENU,
|
||||
PROP_LABEL,
|
||||
PROP_ICON,
|
||||
PROP_SENSITIVE,
|
||||
PROP_VISIBLE,
|
||||
PROP_ROLE,
|
||||
PROP_TOGGLED,
|
||||
PROP_ACCEL,
|
||||
PROP_SUBMENU_SHOWN,
|
||||
N_PROPS
|
||||
};
|
||||
|
||||
static GParamSpec *gtk_menu_tracker_item_pspecs[N_PROPS];
|
||||
static guint gtk_menu_tracker_visibility_changed_signal;
|
||||
|
||||
static void gtk_menu_tracker_item_init_observer_iface (GtkActionObserverInterface *iface);
|
||||
G_DEFINE_TYPE_WITH_CODE (GtkMenuTrackerItem, gtk_menu_tracker_item, G_TYPE_OBJECT,
|
||||
G_IMPLEMENT_INTERFACE (GTK_TYPE_ACTION_OBSERVER, gtk_menu_tracker_item_init_observer_iface))
|
||||
|
||||
GType
|
||||
gtk_menu_tracker_item_role_get_type (void)
|
||||
{
|
||||
static gsize gtk_menu_tracker_item_role_type;
|
||||
|
||||
if (g_once_init_enter (>k_menu_tracker_item_role_type))
|
||||
{
|
||||
static const GEnumValue values[] = {
|
||||
{ GTK_MENU_TRACKER_ITEM_ROLE_NORMAL, "GTK_MENU_TRACKER_ITEM_ROLE_NORMAL", "normal" },
|
||||
{ GTK_MENU_TRACKER_ITEM_ROLE_CHECK, "GTK_MENU_TRACKER_ITEM_ROLE_CHECK", "check" },
|
||||
{ GTK_MENU_TRACKER_ITEM_ROLE_RADIO, "GTK_MENU_TRACKER_ITEM_ROLE_RADIO", "radio" },
|
||||
{ 0, NULL, NULL }
|
||||
};
|
||||
GType type;
|
||||
|
||||
type = g_enum_register_static ("GtkMenuTrackerItemRole", values);
|
||||
|
||||
g_once_init_leave (>k_menu_tracker_item_role_type, type);
|
||||
}
|
||||
|
||||
return gtk_menu_tracker_item_role_type;
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_menu_tracker_item_get_property (GObject *object,
|
||||
guint prop_id,
|
||||
GValue *value,
|
||||
GParamSpec *pspec)
|
||||
{
|
||||
GtkMenuTrackerItem *self = GTK_MENU_TRACKER_ITEM (object);
|
||||
|
||||
switch (prop_id)
|
||||
{
|
||||
case PROP_IS_SEPARATOR:
|
||||
g_value_set_boolean (value, gtk_menu_tracker_item_get_is_separator (self));
|
||||
break;
|
||||
case PROP_HAS_SUBMENU:
|
||||
g_value_set_boolean (value, gtk_menu_tracker_item_get_has_submenu (self));
|
||||
break;
|
||||
case PROP_LABEL:
|
||||
g_value_set_string (value, gtk_menu_tracker_item_get_label (self));
|
||||
break;
|
||||
case PROP_ICON:
|
||||
g_value_set_object (value, gtk_menu_tracker_item_get_icon (self));
|
||||
break;
|
||||
case PROP_SENSITIVE:
|
||||
g_value_set_boolean (value, gtk_menu_tracker_item_get_sensitive (self));
|
||||
break;
|
||||
case PROP_VISIBLE:
|
||||
g_value_set_boolean (value, gtk_menu_tracker_item_get_visible (self));
|
||||
break;
|
||||
case PROP_ROLE:
|
||||
g_value_set_enum (value, gtk_menu_tracker_item_get_role (self));
|
||||
break;
|
||||
case PROP_TOGGLED:
|
||||
g_value_set_boolean (value, gtk_menu_tracker_item_get_toggled (self));
|
||||
break;
|
||||
case PROP_ACCEL:
|
||||
g_value_set_string (value, gtk_menu_tracker_item_get_accel (self));
|
||||
break;
|
||||
case PROP_SUBMENU_SHOWN:
|
||||
g_value_set_boolean (value, gtk_menu_tracker_item_get_submenu_shown (self));
|
||||
break;
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_menu_tracker_item_finalize (GObject *object)
|
||||
{
|
||||
GtkMenuTrackerItem *self = GTK_MENU_TRACKER_ITEM (object);
|
||||
|
||||
g_free (self->action_namespace);
|
||||
g_free (self->action_and_target);
|
||||
|
||||
if (self->observable)
|
||||
g_object_unref (self->observable);
|
||||
|
||||
g_object_unref (self->item);
|
||||
|
||||
G_OBJECT_CLASS (gtk_menu_tracker_item_parent_class)->finalize (object);
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_menu_tracker_item_init (GtkMenuTrackerItem * self)
|
||||
{
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_menu_tracker_item_class_init (GtkMenuTrackerItemClass *class)
|
||||
{
|
||||
class->get_property = gtk_menu_tracker_item_get_property;
|
||||
class->finalize = gtk_menu_tracker_item_finalize;
|
||||
|
||||
gtk_menu_tracker_item_pspecs[PROP_IS_SEPARATOR] =
|
||||
g_param_spec_boolean ("is-separator", "", "", FALSE, G_PARAM_STATIC_STRINGS | G_PARAM_READABLE);
|
||||
gtk_menu_tracker_item_pspecs[PROP_HAS_SUBMENU] =
|
||||
g_param_spec_boolean ("has-submenu", "", "", FALSE, G_PARAM_STATIC_STRINGS | G_PARAM_READABLE);
|
||||
gtk_menu_tracker_item_pspecs[PROP_LABEL] =
|
||||
g_param_spec_string ("label", "", "", NULL, G_PARAM_STATIC_STRINGS | G_PARAM_READABLE);
|
||||
gtk_menu_tracker_item_pspecs[PROP_ICON] =
|
||||
g_param_spec_object ("icon", "", "", G_TYPE_ICON, G_PARAM_STATIC_STRINGS | G_PARAM_READABLE);
|
||||
gtk_menu_tracker_item_pspecs[PROP_SENSITIVE] =
|
||||
g_param_spec_boolean ("sensitive", "", "", FALSE, G_PARAM_STATIC_STRINGS | G_PARAM_READABLE);
|
||||
gtk_menu_tracker_item_pspecs[PROP_VISIBLE] =
|
||||
g_param_spec_boolean ("visible", "", "", FALSE, G_PARAM_STATIC_STRINGS | G_PARAM_READABLE);
|
||||
gtk_menu_tracker_item_pspecs[PROP_ROLE] =
|
||||
g_param_spec_enum ("role", "", "",
|
||||
GTK_TYPE_MENU_TRACKER_ITEM_ROLE, GTK_MENU_TRACKER_ITEM_ROLE_NORMAL,
|
||||
G_PARAM_STATIC_STRINGS | G_PARAM_READABLE);
|
||||
gtk_menu_tracker_item_pspecs[PROP_TOGGLED] =
|
||||
g_param_spec_boolean ("toggled", "", "", FALSE, G_PARAM_STATIC_STRINGS | G_PARAM_READABLE);
|
||||
gtk_menu_tracker_item_pspecs[PROP_ACCEL] =
|
||||
g_param_spec_string ("accel", "", "", NULL, G_PARAM_STATIC_STRINGS | G_PARAM_READABLE);
|
||||
gtk_menu_tracker_item_pspecs[PROP_SUBMENU_SHOWN] =
|
||||
g_param_spec_boolean ("submenu-shown", "", "", FALSE, G_PARAM_STATIC_STRINGS | G_PARAM_READABLE);
|
||||
|
||||
g_object_class_install_properties (class, N_PROPS, gtk_menu_tracker_item_pspecs);
|
||||
|
||||
gtk_menu_tracker_visibility_changed_signal = g_signal_new ("visibility-changed", GTK_TYPE_MENU_TRACKER_ITEM,
|
||||
G_SIGNAL_RUN_FIRST, 0, NULL, NULL,
|
||||
g_cclosure_marshal_VOID__BOOLEAN, G_TYPE_NONE,
|
||||
1, G_TYPE_BOOLEAN);
|
||||
}
|
||||
|
||||
/* This syncs up the visibility for the hidden-when='' case. We call it
|
||||
* from the action observer functions on changes to the action group and
|
||||
* on initialisation (via the action observer functions that are invoked
|
||||
* at that time).
|
||||
*/
|
||||
static void
|
||||
gtk_menu_tracker_item_update_visibility (GtkMenuTrackerItem *self)
|
||||
{
|
||||
gboolean visible;
|
||||
|
||||
switch (self->hidden_when)
|
||||
{
|
||||
case HIDDEN_NEVER:
|
||||
visible = TRUE;
|
||||
break;
|
||||
|
||||
case HIDDEN_WHEN_MISSING:
|
||||
visible = self->can_activate;
|
||||
break;
|
||||
|
||||
case HIDDEN_WHEN_DISABLED:
|
||||
visible = self->sensitive;
|
||||
break;
|
||||
|
||||
default:
|
||||
g_assert_not_reached ();
|
||||
}
|
||||
|
||||
if (visible != self->is_visible)
|
||||
{
|
||||
self->is_visible = visible;
|
||||
g_signal_emit (self, gtk_menu_tracker_visibility_changed_signal, 0, visible);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_menu_tracker_item_action_added (GtkActionObserver *observer,
|
||||
GtkActionObservable *observable,
|
||||
const gchar *action_name,
|
||||
const GVariantType *parameter_type,
|
||||
gboolean enabled,
|
||||
GVariant *state)
|
||||
{
|
||||
GtkMenuTrackerItem *self = GTK_MENU_TRACKER_ITEM (observer);
|
||||
GVariant *action_target;
|
||||
|
||||
action_target = g_menu_item_get_attribute_value (self->item, G_MENU_ATTRIBUTE_TARGET, NULL);
|
||||
|
||||
self->can_activate = (action_target == NULL && parameter_type == NULL) ||
|
||||
(action_target != NULL && parameter_type != NULL &&
|
||||
g_variant_is_of_type (action_target, parameter_type));
|
||||
|
||||
if (!self->can_activate)
|
||||
{
|
||||
if (action_target)
|
||||
g_variant_unref (action_target);
|
||||
return;
|
||||
}
|
||||
|
||||
self->sensitive = enabled;
|
||||
|
||||
if (action_target != NULL && state != NULL)
|
||||
{
|
||||
self->toggled = g_variant_equal (state, action_target);
|
||||
self->role = GTK_MENU_TRACKER_ITEM_ROLE_RADIO;
|
||||
}
|
||||
|
||||
else if (state != NULL && g_variant_is_of_type (state, G_VARIANT_TYPE_BOOLEAN))
|
||||
{
|
||||
self->toggled = g_variant_get_boolean (state);
|
||||
self->role = GTK_MENU_TRACKER_ITEM_ROLE_CHECK;
|
||||
}
|
||||
|
||||
g_object_freeze_notify (G_OBJECT (self));
|
||||
|
||||
if (self->sensitive)
|
||||
g_object_notify_by_pspec (G_OBJECT (self), gtk_menu_tracker_item_pspecs[PROP_SENSITIVE]);
|
||||
|
||||
if (self->toggled)
|
||||
g_object_notify_by_pspec (G_OBJECT (self), gtk_menu_tracker_item_pspecs[PROP_TOGGLED]);
|
||||
|
||||
if (self->role != GTK_MENU_TRACKER_ITEM_ROLE_NORMAL)
|
||||
g_object_notify_by_pspec (G_OBJECT (self), gtk_menu_tracker_item_pspecs[PROP_ROLE]);
|
||||
|
||||
g_object_thaw_notify (G_OBJECT (self));
|
||||
|
||||
if (action_target)
|
||||
g_variant_unref (action_target);
|
||||
|
||||
/* In case of hidden-when='', we want to Wait until after refreshing
|
||||
* all of the properties to emit the signal that will cause the
|
||||
* tracker to expose us (to prevent too much thrashing).
|
||||
*/
|
||||
gtk_menu_tracker_item_update_visibility (self);
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_menu_tracker_item_action_enabled_changed (GtkActionObserver *observer,
|
||||
GtkActionObservable *observable,
|
||||
const gchar *action_name,
|
||||
gboolean enabled)
|
||||
{
|
||||
GtkMenuTrackerItem *self = GTK_MENU_TRACKER_ITEM (observer);
|
||||
|
||||
if (!self->can_activate)
|
||||
return;
|
||||
|
||||
if (self->sensitive == enabled)
|
||||
return;
|
||||
|
||||
self->sensitive = enabled;
|
||||
|
||||
g_object_notify_by_pspec (G_OBJECT (self), gtk_menu_tracker_item_pspecs[PROP_SENSITIVE]);
|
||||
|
||||
gtk_menu_tracker_item_update_visibility (self);
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_menu_tracker_item_action_state_changed (GtkActionObserver *observer,
|
||||
GtkActionObservable *observable,
|
||||
const gchar *action_name,
|
||||
GVariant *state)
|
||||
{
|
||||
GtkMenuTrackerItem *self = GTK_MENU_TRACKER_ITEM (observer);
|
||||
GVariant *action_target;
|
||||
gboolean was_toggled;
|
||||
|
||||
if (!self->can_activate)
|
||||
return;
|
||||
|
||||
action_target = g_menu_item_get_attribute_value (self->item, G_MENU_ATTRIBUTE_TARGET, NULL);
|
||||
was_toggled = self->toggled;
|
||||
|
||||
if (action_target)
|
||||
{
|
||||
self->toggled = g_variant_equal (state, action_target);
|
||||
g_variant_unref (action_target);
|
||||
}
|
||||
|
||||
else if (g_variant_is_of_type (state, G_VARIANT_TYPE_BOOLEAN))
|
||||
self->toggled = g_variant_get_boolean (state);
|
||||
|
||||
else
|
||||
self->toggled = FALSE;
|
||||
|
||||
if (self->toggled != was_toggled)
|
||||
g_object_notify_by_pspec (G_OBJECT (self), gtk_menu_tracker_item_pspecs[PROP_TOGGLED]);
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_menu_tracker_item_action_removed (GtkActionObserver *observer,
|
||||
GtkActionObservable *observable,
|
||||
const gchar *action_name)
|
||||
{
|
||||
GtkMenuTrackerItem *self = GTK_MENU_TRACKER_ITEM (observer);
|
||||
gboolean was_sensitive, was_toggled;
|
||||
GtkMenuTrackerItemRole old_role;
|
||||
|
||||
if (!self->can_activate)
|
||||
return;
|
||||
|
||||
was_sensitive = self->sensitive;
|
||||
was_toggled = self->toggled;
|
||||
old_role = self->role;
|
||||
|
||||
self->can_activate = FALSE;
|
||||
self->sensitive = FALSE;
|
||||
self->toggled = FALSE;
|
||||
self->role = GTK_MENU_TRACKER_ITEM_ROLE_NORMAL;
|
||||
|
||||
/* Backwards from adding: we want to remove ourselves from the menu
|
||||
* -before- thrashing the properties.
|
||||
*/
|
||||
gtk_menu_tracker_item_update_visibility (self);
|
||||
|
||||
g_object_freeze_notify (G_OBJECT (self));
|
||||
|
||||
if (was_sensitive)
|
||||
g_object_notify_by_pspec (G_OBJECT (self), gtk_menu_tracker_item_pspecs[PROP_SENSITIVE]);
|
||||
|
||||
if (was_toggled)
|
||||
g_object_notify_by_pspec (G_OBJECT (self), gtk_menu_tracker_item_pspecs[PROP_TOGGLED]);
|
||||
|
||||
if (old_role != GTK_MENU_TRACKER_ITEM_ROLE_NORMAL)
|
||||
g_object_notify_by_pspec (G_OBJECT (self), gtk_menu_tracker_item_pspecs[PROP_ROLE]);
|
||||
|
||||
g_object_thaw_notify (G_OBJECT (self));
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_menu_tracker_item_primary_accel_changed (GtkActionObserver *observer,
|
||||
GtkActionObservable *observable,
|
||||
const gchar *action_name,
|
||||
const gchar *action_and_target)
|
||||
{
|
||||
GtkMenuTrackerItem *self = GTK_MENU_TRACKER_ITEM (observer);
|
||||
|
||||
if (g_str_equal (action_and_target, self->action_and_target))
|
||||
g_object_notify_by_pspec (G_OBJECT (self), gtk_menu_tracker_item_pspecs[PROP_ACCEL]);
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_menu_tracker_item_init_observer_iface (GtkActionObserverInterface *iface)
|
||||
{
|
||||
iface->action_added = gtk_menu_tracker_item_action_added;
|
||||
iface->action_enabled_changed = gtk_menu_tracker_item_action_enabled_changed;
|
||||
iface->action_state_changed = gtk_menu_tracker_item_action_state_changed;
|
||||
iface->action_removed = gtk_menu_tracker_item_action_removed;
|
||||
iface->primary_accel_changed = gtk_menu_tracker_item_primary_accel_changed;
|
||||
}
|
||||
|
||||
GtkMenuTrackerItem *
|
||||
_gtk_menu_tracker_item_new (GtkActionObservable *observable,
|
||||
GMenuModel *model,
|
||||
gint item_index,
|
||||
const gchar *action_namespace,
|
||||
gboolean is_separator)
|
||||
{
|
||||
GtkMenuTrackerItem *self;
|
||||
const gchar *action_name;
|
||||
const gchar *hidden_when;
|
||||
|
||||
g_return_val_if_fail (GTK_IS_ACTION_OBSERVABLE (observable), NULL);
|
||||
g_return_val_if_fail (G_IS_MENU_MODEL (model), NULL);
|
||||
|
||||
self = g_object_new (GTK_TYPE_MENU_TRACKER_ITEM, NULL);
|
||||
self->item = g_menu_item_new_from_model (model, item_index);
|
||||
self->action_namespace = g_strdup (action_namespace);
|
||||
self->observable = g_object_ref (observable);
|
||||
self->is_separator = is_separator;
|
||||
|
||||
if (!is_separator && g_menu_item_get_attribute (self->item, "hidden-when", "&s", &hidden_when))
|
||||
{
|
||||
if (g_str_equal (hidden_when, "action-disabled"))
|
||||
self->hidden_when = HIDDEN_WHEN_DISABLED;
|
||||
else if (g_str_equal (hidden_when, "action-missing"))
|
||||
self->hidden_when = HIDDEN_WHEN_MISSING;
|
||||
|
||||
/* Ignore other values -- this code may be running in context of a
|
||||
* desktop shell or the like and should not spew criticals due to
|
||||
* application bugs...
|
||||
*
|
||||
* Note: if we just set a hidden-when state, but don't get the
|
||||
* action_name below then our visibility will be FALSE forever.
|
||||
* That's to be expected since the action is missing...
|
||||
*/
|
||||
}
|
||||
|
||||
if (!is_separator && g_menu_item_get_attribute (self->item, "action", "&s", &action_name))
|
||||
{
|
||||
GActionGroup *group = G_ACTION_GROUP (observable);
|
||||
const GVariantType *parameter_type;
|
||||
GVariant *target;
|
||||
gboolean enabled;
|
||||
GVariant *state;
|
||||
gboolean found;
|
||||
|
||||
target = g_menu_item_get_attribute_value (self->item, "target", NULL);
|
||||
|
||||
self->action_and_target = gtk_print_action_and_target (action_namespace, action_name, target);
|
||||
|
||||
if (target)
|
||||
g_variant_unref (target);
|
||||
|
||||
action_name = strrchr (self->action_and_target, '|') + 1;
|
||||
|
||||
state = NULL;
|
||||
|
||||
gtk_action_observable_register_observer (self->observable, action_name, GTK_ACTION_OBSERVER (self));
|
||||
found = g_action_group_query_action (group, action_name, &enabled, ¶meter_type, NULL, NULL, &state);
|
||||
|
||||
if (found)
|
||||
gtk_menu_tracker_item_action_added (GTK_ACTION_OBSERVER (self), observable, NULL, parameter_type, enabled, state);
|
||||
else
|
||||
gtk_menu_tracker_item_action_removed (GTK_ACTION_OBSERVER (self), observable, NULL);
|
||||
|
||||
if (state)
|
||||
g_variant_unref (state);
|
||||
}
|
||||
else
|
||||
self->sensitive = TRUE;
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
GtkActionObservable *
|
||||
_gtk_menu_tracker_item_get_observable (GtkMenuTrackerItem *self)
|
||||
{
|
||||
return self->observable;
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_menu_tracker_item_get_is_separator:
|
||||
* @self: A #GtkMenuTrackerItem instance
|
||||
*
|
||||
* Returns whether the menu item is a separator. If so, only
|
||||
* certain properties may need to be obeyed. See the documentation
|
||||
* for #GtkMenuTrackerItem.
|
||||
*/
|
||||
gboolean
|
||||
gtk_menu_tracker_item_get_is_separator (GtkMenuTrackerItem *self)
|
||||
{
|
||||
return self->is_separator;
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_menu_tracker_item_get_has_submenu:
|
||||
* @self: A #GtkMenuTrackerItem instance
|
||||
*
|
||||
* Returns whether the menu item has a submenu. If so, only
|
||||
* certain properties may need to be obeyed. See the documentation
|
||||
* for #GtkMenuTrackerItem.
|
||||
*/
|
||||
gboolean
|
||||
gtk_menu_tracker_item_get_has_submenu (GtkMenuTrackerItem *self)
|
||||
{
|
||||
GMenuModel *link;
|
||||
|
||||
link = g_menu_item_get_link (self->item, G_MENU_LINK_SUBMENU);
|
||||
|
||||
if (link)
|
||||
{
|
||||
g_object_unref (link);
|
||||
return TRUE;
|
||||
}
|
||||
else
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
const gchar *
|
||||
gtk_menu_tracker_item_get_label (GtkMenuTrackerItem *self)
|
||||
{
|
||||
const gchar *label = NULL;
|
||||
|
||||
g_menu_item_get_attribute (self->item, G_MENU_ATTRIBUTE_LABEL, "&s", &label);
|
||||
|
||||
return label;
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_menu_tracker_item_get_icon:
|
||||
*
|
||||
* Returns: (transfer full):
|
||||
*/
|
||||
GIcon *
|
||||
gtk_menu_tracker_item_get_icon (GtkMenuTrackerItem *self)
|
||||
{
|
||||
GVariant *icon_data;
|
||||
GIcon *icon;
|
||||
|
||||
icon_data = g_menu_item_get_attribute_value (self->item, "icon", NULL);
|
||||
|
||||
if (icon_data == NULL)
|
||||
return NULL;
|
||||
|
||||
icon = g_icon_deserialize (icon_data);
|
||||
g_variant_unref (icon_data);
|
||||
|
||||
return icon;
|
||||
}
|
||||
|
||||
gboolean
|
||||
gtk_menu_tracker_item_get_sensitive (GtkMenuTrackerItem *self)
|
||||
{
|
||||
return self->sensitive;
|
||||
}
|
||||
|
||||
gboolean
|
||||
gtk_menu_tracker_item_get_visible (GtkMenuTrackerItem *self)
|
||||
{
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
GtkMenuTrackerItemRole
|
||||
gtk_menu_tracker_item_get_role (GtkMenuTrackerItem *self)
|
||||
{
|
||||
return self->role;
|
||||
}
|
||||
|
||||
gboolean
|
||||
gtk_menu_tracker_item_get_toggled (GtkMenuTrackerItem *self)
|
||||
{
|
||||
return self->toggled;
|
||||
}
|
||||
|
||||
const gchar *
|
||||
gtk_menu_tracker_item_get_accel (GtkMenuTrackerItem *self)
|
||||
{
|
||||
const gchar *accel;
|
||||
|
||||
if (!self->action_and_target)
|
||||
return NULL;
|
||||
|
||||
if (g_menu_item_get_attribute (self->item, "accel", "&s", &accel))
|
||||
return accel;
|
||||
|
||||
if (!GTK_IS_ACTION_MUXER (self->observable))
|
||||
return NULL;
|
||||
|
||||
return gtk_action_muxer_get_primary_accel (GTK_ACTION_MUXER (self->observable), self->action_and_target);
|
||||
}
|
||||
|
||||
GMenuModel *
|
||||
_gtk_menu_tracker_item_get_submenu (GtkMenuTrackerItem *self)
|
||||
{
|
||||
return g_menu_item_get_link (self->item, "submenu");
|
||||
}
|
||||
|
||||
gchar *
|
||||
_gtk_menu_tracker_item_get_submenu_namespace (GtkMenuTrackerItem *self)
|
||||
{
|
||||
const gchar *namespace;
|
||||
|
||||
if (g_menu_item_get_attribute (self->item, "action-namespace", "&s", &namespace))
|
||||
{
|
||||
if (self->action_namespace)
|
||||
return g_strjoin (".", self->action_namespace, namespace, NULL);
|
||||
else
|
||||
return g_strdup (namespace);
|
||||
}
|
||||
else
|
||||
return g_strdup (self->action_namespace);
|
||||
}
|
||||
|
||||
gboolean
|
||||
gtk_menu_tracker_item_get_should_request_show (GtkMenuTrackerItem *self)
|
||||
{
|
||||
return g_menu_item_get_attribute (self->item, "submenu-action", "&s", NULL);
|
||||
}
|
||||
|
||||
gboolean
|
||||
gtk_menu_tracker_item_get_submenu_shown (GtkMenuTrackerItem *self)
|
||||
{
|
||||
return self->submenu_shown;
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_menu_tracker_item_set_submenu_shown (GtkMenuTrackerItem *self,
|
||||
gboolean submenu_shown)
|
||||
{
|
||||
if (submenu_shown == self->submenu_shown)
|
||||
return;
|
||||
|
||||
self->submenu_shown = submenu_shown;
|
||||
g_object_notify_by_pspec (G_OBJECT (self), gtk_menu_tracker_item_pspecs[PROP_SUBMENU_SHOWN]);
|
||||
}
|
||||
|
||||
void
|
||||
gtk_menu_tracker_item_activated (GtkMenuTrackerItem *self)
|
||||
{
|
||||
const gchar *action_name;
|
||||
GVariant *action_target;
|
||||
|
||||
g_return_if_fail (GTK_IS_MENU_TRACKER_ITEM (self));
|
||||
|
||||
if (!self->can_activate)
|
||||
return;
|
||||
|
||||
action_name = strrchr (self->action_and_target, '|') + 1;
|
||||
action_target = g_menu_item_get_attribute_value (self->item, G_MENU_ATTRIBUTE_TARGET, NULL);
|
||||
|
||||
g_action_group_activate_action (G_ACTION_GROUP (self->observable), action_name, action_target);
|
||||
|
||||
if (action_target)
|
||||
g_variant_unref (action_target);
|
||||
}
|
||||
|
||||
typedef struct
|
||||
{
|
||||
GtkMenuTrackerItem *item;
|
||||
gchar *submenu_action;
|
||||
gboolean first_time;
|
||||
} GtkMenuTrackerOpener;
|
||||
|
||||
static void
|
||||
gtk_menu_tracker_opener_update (GtkMenuTrackerOpener *opener)
|
||||
{
|
||||
GActionGroup *group = G_ACTION_GROUP (opener->item->observable);
|
||||
gboolean is_open = TRUE;
|
||||
|
||||
/* We consider the menu as being "open" if the action does not exist
|
||||
* or if there is another problem (no state, wrong state type, etc.).
|
||||
* If the action exists, with the correct state then we consider it
|
||||
* open if we have ever seen this state equal to TRUE.
|
||||
*
|
||||
* In the event that we see the state equal to FALSE, we force it back
|
||||
* to TRUE. We do not signal that the menu was closed because this is
|
||||
* likely to create UI thrashing.
|
||||
*
|
||||
* The only way the menu can have a true-to-false submenu-shown
|
||||
* transition is if the user calls _request_submenu_shown (FALSE).
|
||||
* That is handled in _free() below.
|
||||
*/
|
||||
|
||||
if (g_action_group_has_action (group, opener->submenu_action))
|
||||
{
|
||||
GVariant *state = g_action_group_get_action_state (group, opener->submenu_action);
|
||||
|
||||
if (state)
|
||||
{
|
||||
if (g_variant_is_of_type (state, G_VARIANT_TYPE_BOOLEAN))
|
||||
is_open = g_variant_get_boolean (state);
|
||||
g_variant_unref (state);
|
||||
}
|
||||
}
|
||||
|
||||
/* If it is already open, signal that.
|
||||
*
|
||||
* If it is not open, ask it to open.
|
||||
*/
|
||||
if (is_open)
|
||||
gtk_menu_tracker_item_set_submenu_shown (opener->item, TRUE);
|
||||
|
||||
if (!is_open || opener->first_time)
|
||||
{
|
||||
g_action_group_change_action_state (group, opener->submenu_action, g_variant_new_boolean (TRUE));
|
||||
opener->first_time = FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_menu_tracker_opener_added (GActionGroup *group,
|
||||
const gchar *action_name,
|
||||
gpointer user_data)
|
||||
{
|
||||
GtkMenuTrackerOpener *opener = user_data;
|
||||
|
||||
if (g_str_equal (action_name, opener->submenu_action))
|
||||
gtk_menu_tracker_opener_update (opener);
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_menu_tracker_opener_removed (GActionGroup *action_group,
|
||||
const gchar *action_name,
|
||||
gpointer user_data)
|
||||
{
|
||||
GtkMenuTrackerOpener *opener = user_data;
|
||||
|
||||
if (g_str_equal (action_name, opener->submenu_action))
|
||||
gtk_menu_tracker_opener_update (opener);
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_menu_tracker_opener_changed (GActionGroup *action_group,
|
||||
const gchar *action_name,
|
||||
GVariant *new_state,
|
||||
gpointer user_data)
|
||||
{
|
||||
GtkMenuTrackerOpener *opener = user_data;
|
||||
|
||||
if (g_str_equal (action_name, opener->submenu_action))
|
||||
gtk_menu_tracker_opener_update (opener);
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_menu_tracker_opener_free (gpointer data)
|
||||
{
|
||||
GtkMenuTrackerOpener *opener = data;
|
||||
|
||||
g_signal_handlers_disconnect_by_func (opener->item->observable, gtk_menu_tracker_opener_added, opener);
|
||||
g_signal_handlers_disconnect_by_func (opener->item->observable, gtk_menu_tracker_opener_removed, opener);
|
||||
g_signal_handlers_disconnect_by_func (opener->item->observable, gtk_menu_tracker_opener_changed, opener);
|
||||
|
||||
g_action_group_change_action_state (G_ACTION_GROUP (opener->item->observable),
|
||||
opener->submenu_action,
|
||||
g_variant_new_boolean (FALSE));
|
||||
|
||||
gtk_menu_tracker_item_set_submenu_shown (opener->item, FALSE);
|
||||
|
||||
g_free (opener->submenu_action);
|
||||
|
||||
g_slice_free (GtkMenuTrackerOpener, opener);
|
||||
}
|
||||
|
||||
static GtkMenuTrackerOpener *
|
||||
gtk_menu_tracker_opener_new (GtkMenuTrackerItem *item,
|
||||
const gchar *submenu_action)
|
||||
{
|
||||
GtkMenuTrackerOpener *opener;
|
||||
|
||||
opener = g_slice_new (GtkMenuTrackerOpener);
|
||||
opener->first_time = TRUE;
|
||||
opener->item = item;
|
||||
|
||||
if (item->action_namespace)
|
||||
opener->submenu_action = g_strjoin (".", item->action_namespace, submenu_action, NULL);
|
||||
else
|
||||
opener->submenu_action = g_strdup (submenu_action);
|
||||
|
||||
g_signal_connect (item->observable, "action-added", G_CALLBACK (gtk_menu_tracker_opener_added), opener);
|
||||
g_signal_connect (item->observable, "action-removed", G_CALLBACK (gtk_menu_tracker_opener_removed), opener);
|
||||
g_signal_connect (item->observable, "action-state-changed", G_CALLBACK (gtk_menu_tracker_opener_changed), opener);
|
||||
|
||||
gtk_menu_tracker_opener_update (opener);
|
||||
|
||||
return opener;
|
||||
}
|
||||
|
||||
void
|
||||
gtk_menu_tracker_item_request_submenu_shown (GtkMenuTrackerItem *self,
|
||||
gboolean shown)
|
||||
{
|
||||
const gchar *submenu_action;
|
||||
gboolean has_submenu_action;
|
||||
|
||||
if (shown == self->submenu_requested)
|
||||
return;
|
||||
|
||||
has_submenu_action = g_menu_item_get_attribute (self->item, "submenu-action", "&s", &submenu_action);
|
||||
|
||||
self->submenu_requested = shown;
|
||||
|
||||
/* If we have a submenu action, start a submenu opener and wait
|
||||
* for the reply from the client. Otherwise, simply open the
|
||||
* submenu immediately.
|
||||
*/
|
||||
if (has_submenu_action)
|
||||
{
|
||||
if (shown)
|
||||
g_object_set_data_full (G_OBJECT (self), "submenu-opener",
|
||||
gtk_menu_tracker_opener_new (self, submenu_action),
|
||||
gtk_menu_tracker_opener_free);
|
||||
else
|
||||
g_object_set_data (G_OBJECT (self), "submenu-opener", NULL);
|
||||
}
|
||||
else
|
||||
gtk_menu_tracker_item_set_submenu_shown (self, shown);
|
||||
}
|
||||
|
||||
gboolean
|
||||
_gtk_menu_tracker_item_is_visible (GtkMenuTrackerItem *self)
|
||||
{
|
||||
return self->is_visible;
|
||||
}
|
||||
|
||||
gboolean
|
||||
_gtk_menu_tracker_item_may_disappear (GtkMenuTrackerItem *self)
|
||||
{
|
||||
return self->hidden_when != HIDDEN_NEVER;
|
||||
}
|
@ -1,88 +0,0 @@
|
||||
/*
|
||||
* Copyright © 2011, 2013 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Author: Ryan Lortie <desrt@desrt.ca>
|
||||
*/
|
||||
|
||||
#ifndef __GTK_MENU_TRACKER_ITEM_H__
|
||||
#define __GTK_MENU_TRACKER_ITEM_H__
|
||||
|
||||
#include "gtkactionobservable.h"
|
||||
|
||||
#define GTK_TYPE_MENU_TRACKER_ITEM (gtk_menu_tracker_item_get_type ())
|
||||
#define GTK_MENU_TRACKER_ITEM(inst) (G_TYPE_CHECK_INSTANCE_CAST ((inst), \
|
||||
GTK_TYPE_MENU_TRACKER_ITEM, GtkMenuTrackerItem))
|
||||
#define GTK_IS_MENU_TRACKER_ITEM(inst) (G_TYPE_CHECK_INSTANCE_TYPE ((inst), \
|
||||
GTK_TYPE_MENU_TRACKER_ITEM))
|
||||
|
||||
typedef struct _GtkMenuTrackerItem GtkMenuTrackerItem;
|
||||
|
||||
#define GTK_TYPE_MENU_TRACKER_ITEM_ROLE (gtk_menu_tracker_item_role_get_type ())
|
||||
|
||||
typedef enum {
|
||||
GTK_MENU_TRACKER_ITEM_ROLE_NORMAL,
|
||||
GTK_MENU_TRACKER_ITEM_ROLE_CHECK,
|
||||
GTK_MENU_TRACKER_ITEM_ROLE_RADIO,
|
||||
} GtkMenuTrackerItemRole;
|
||||
|
||||
GType gtk_menu_tracker_item_get_type (void) G_GNUC_CONST;
|
||||
|
||||
GType gtk_menu_tracker_item_role_get_type (void) G_GNUC_CONST;
|
||||
|
||||
GtkMenuTrackerItem * _gtk_menu_tracker_item_new (GtkActionObservable *observable,
|
||||
GMenuModel *model,
|
||||
gint item_index,
|
||||
const gchar *action_namespace,
|
||||
gboolean is_separator);
|
||||
|
||||
GtkActionObservable * _gtk_menu_tracker_item_get_observable (GtkMenuTrackerItem *self);
|
||||
|
||||
gboolean gtk_menu_tracker_item_get_is_separator (GtkMenuTrackerItem *self);
|
||||
|
||||
gboolean gtk_menu_tracker_item_get_has_submenu (GtkMenuTrackerItem *self);
|
||||
|
||||
const gchar * gtk_menu_tracker_item_get_label (GtkMenuTrackerItem *self);
|
||||
|
||||
GIcon * gtk_menu_tracker_item_get_icon (GtkMenuTrackerItem *self);
|
||||
|
||||
gboolean gtk_menu_tracker_item_get_sensitive (GtkMenuTrackerItem *self);
|
||||
|
||||
gboolean gtk_menu_tracker_item_get_visible (GtkMenuTrackerItem *self);
|
||||
|
||||
GtkMenuTrackerItemRole gtk_menu_tracker_item_get_role (GtkMenuTrackerItem *self);
|
||||
|
||||
gboolean gtk_menu_tracker_item_get_toggled (GtkMenuTrackerItem *self);
|
||||
|
||||
const gchar * gtk_menu_tracker_item_get_accel (GtkMenuTrackerItem *self);
|
||||
|
||||
GMenuModel * _gtk_menu_tracker_item_get_submenu (GtkMenuTrackerItem *self);
|
||||
|
||||
gchar * _gtk_menu_tracker_item_get_submenu_namespace (GtkMenuTrackerItem *self);
|
||||
|
||||
gboolean _gtk_menu_tracker_item_may_disappear (GtkMenuTrackerItem *self);
|
||||
|
||||
gboolean _gtk_menu_tracker_item_is_visible (GtkMenuTrackerItem *self);
|
||||
|
||||
gboolean gtk_menu_tracker_item_get_should_request_show (GtkMenuTrackerItem *self);
|
||||
|
||||
void gtk_menu_tracker_item_activated (GtkMenuTrackerItem *self);
|
||||
|
||||
void gtk_menu_tracker_item_request_submenu_shown (GtkMenuTrackerItem *self,
|
||||
gboolean shown);
|
||||
|
||||
gboolean gtk_menu_tracker_item_get_submenu_shown (GtkMenuTrackerItem *self);
|
||||
|
||||
#endif
|
@ -60,24 +60,17 @@ gnome_shell_deps += recorder_deps
|
||||
tools_cflags = '-DLOCALEDIR="@0@"'.format(localedir)
|
||||
tools_deps = [gio_dep, gjs_dep]
|
||||
|
||||
libshell_menu_gir_sources = [
|
||||
libshell_menu_sources = [
|
||||
'gtkactionmuxer.h',
|
||||
'gtkactionmuxer.c',
|
||||
'gtkactionobservable.h',
|
||||
'gtkactionobservable.c',
|
||||
'gtkactionobserver.h',
|
||||
'gtkactionobserver.c',
|
||||
'gtkmenutrackeritem.c',
|
||||
'gtkmenutrackeritem.h'
|
||||
]
|
||||
|
||||
libshell_menu_no_gir_sources= [
|
||||
'gtkmenutracker.c',
|
||||
'gtkmenutracker.h'
|
||||
'gtkactionobserver.c'
|
||||
]
|
||||
|
||||
libshell_menu = library('gnome-shell-menu',
|
||||
sources: libshell_menu_gir_sources + libshell_menu_no_gir_sources,
|
||||
sources: libshell_menu_sources,
|
||||
dependencies: [gio_dep, clutter_dep],
|
||||
include_directories: conf_inc,
|
||||
build_rpath: mutter_typelibdir,
|
||||
@ -87,7 +80,7 @@ libshell_menu = library('gnome-shell-menu',
|
||||
)
|
||||
|
||||
libshell_menu_gir = gnome.generate_gir(libshell_menu,
|
||||
sources: libshell_menu_gir_sources,
|
||||
sources: libshell_menu_sources,
|
||||
nsversion: '0.1',
|
||||
namespace: 'ShellMenu',
|
||||
identifier_prefix: 'Gtk',
|
||||
@ -148,8 +141,6 @@ libshell_sources = [
|
||||
'shell-invert-lightness-effect.c',
|
||||
'shell-keyring-prompt.c',
|
||||
'shell-keyring-prompt.h',
|
||||
'shell-menu-tracker.c',
|
||||
'shell-menu-tracker.h',
|
||||
'shell-mount-operation.c',
|
||||
'shell-perf-log.c',
|
||||
'shell-polkit-authentication-agent.c',
|
||||
|
@ -1,166 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2013 Red Hat
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as
|
||||
* published by the Free Software Foundation; either version 2 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program 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
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Written by:
|
||||
* Jasper St. Pierre <jstpierre@mecheye.net>
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include "shell-menu-tracker.h"
|
||||
#include "gtkmenutracker.h"
|
||||
|
||||
/**
|
||||
* SECTION:shell-menu-tracker
|
||||
* @short_description: a simple wrapper around #GtkMenuTracker
|
||||
* to make it bindable.
|
||||
*/
|
||||
|
||||
struct _ShellMenuTracker
|
||||
{
|
||||
guint ref_count;
|
||||
|
||||
GtkMenuTracker *tracker;
|
||||
|
||||
ShellMenuTrackerInsertFunc insert_func;
|
||||
gpointer insert_user_data;
|
||||
GDestroyNotify insert_notify;
|
||||
ShellMenuTrackerRemoveFunc remove_func;
|
||||
gpointer remove_user_data;
|
||||
GDestroyNotify remove_notify;
|
||||
};
|
||||
|
||||
static void
|
||||
shell_menu_tracker_insert_func (GtkMenuTrackerItem *item,
|
||||
gint position,
|
||||
gpointer user_data)
|
||||
{
|
||||
ShellMenuTracker *tracker = (ShellMenuTracker *) user_data;
|
||||
tracker->insert_func (item, position, tracker->insert_user_data);
|
||||
}
|
||||
|
||||
static void
|
||||
shell_menu_tracker_remove_func (gint position,
|
||||
gpointer user_data)
|
||||
{
|
||||
ShellMenuTracker *tracker = (ShellMenuTracker *) user_data;
|
||||
tracker->remove_func (position, tracker->remove_user_data);
|
||||
}
|
||||
|
||||
/**
|
||||
* shell_menu_tracker_new:
|
||||
* @observable:
|
||||
* @model:
|
||||
* @action_namespace: (nullable):
|
||||
* @insert_func:
|
||||
* @insert_user_data:
|
||||
* @insert_notify:
|
||||
* @remove_func:
|
||||
* @remove_user_data:
|
||||
* @remove_notify:
|
||||
*/
|
||||
ShellMenuTracker *
|
||||
shell_menu_tracker_new (GtkActionObservable *observable,
|
||||
GMenuModel *model,
|
||||
const gchar *action_namespace,
|
||||
ShellMenuTrackerInsertFunc insert_func,
|
||||
gpointer insert_user_data,
|
||||
GDestroyNotify insert_notify,
|
||||
ShellMenuTrackerRemoveFunc remove_func,
|
||||
gpointer remove_user_data,
|
||||
GDestroyNotify remove_notify)
|
||||
{
|
||||
ShellMenuTracker *tracker = g_slice_new0 (ShellMenuTracker);
|
||||
|
||||
tracker->ref_count = 1;
|
||||
tracker->insert_func = insert_func;
|
||||
tracker->insert_user_data = insert_user_data;
|
||||
tracker->insert_notify = insert_notify;
|
||||
tracker->remove_func = remove_func;
|
||||
tracker->remove_user_data = remove_user_data;
|
||||
tracker->remove_notify = remove_notify;
|
||||
|
||||
tracker->tracker = gtk_menu_tracker_new (observable,
|
||||
model,
|
||||
TRUE, /* with separators */
|
||||
action_namespace,
|
||||
shell_menu_tracker_insert_func,
|
||||
shell_menu_tracker_remove_func,
|
||||
tracker);
|
||||
|
||||
return tracker;
|
||||
}
|
||||
|
||||
ShellMenuTracker *
|
||||
shell_menu_tracker_new_for_item_submenu (GtkMenuTrackerItem *item,
|
||||
ShellMenuTrackerInsertFunc insert_func,
|
||||
gpointer insert_user_data,
|
||||
GDestroyNotify insert_notify,
|
||||
ShellMenuTrackerRemoveFunc remove_func,
|
||||
gpointer remove_user_data,
|
||||
GDestroyNotify remove_notify)
|
||||
{
|
||||
ShellMenuTracker *tracker = g_slice_new0 (ShellMenuTracker);
|
||||
|
||||
tracker->ref_count = 1;
|
||||
tracker->insert_func = insert_func;
|
||||
tracker->insert_user_data = insert_user_data;
|
||||
tracker->insert_notify = insert_notify;
|
||||
tracker->remove_func = remove_func;
|
||||
tracker->remove_user_data = remove_user_data;
|
||||
tracker->remove_notify = remove_notify;
|
||||
|
||||
tracker->tracker = gtk_menu_tracker_new_for_item_submenu (item,
|
||||
shell_menu_tracker_insert_func,
|
||||
shell_menu_tracker_remove_func,
|
||||
tracker);
|
||||
|
||||
return tracker;
|
||||
}
|
||||
|
||||
ShellMenuTracker *
|
||||
shell_menu_tracker_ref (ShellMenuTracker *tracker)
|
||||
{
|
||||
tracker->ref_count++;
|
||||
return tracker;
|
||||
}
|
||||
|
||||
void
|
||||
shell_menu_tracker_unref (ShellMenuTracker *tracker)
|
||||
{
|
||||
if (tracker->ref_count-- <= 0)
|
||||
{
|
||||
shell_menu_tracker_destroy (tracker);
|
||||
g_slice_free (ShellMenuTracker, tracker);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
shell_menu_tracker_destroy (ShellMenuTracker *tracker)
|
||||
{
|
||||
if (tracker->tracker != NULL)
|
||||
{
|
||||
gtk_menu_tracker_free (tracker->tracker);
|
||||
tracker->tracker = NULL;
|
||||
tracker->insert_notify (tracker->insert_user_data);
|
||||
tracker->remove_notify (tracker->remove_user_data);
|
||||
}
|
||||
}
|
||||
|
||||
G_DEFINE_BOXED_TYPE(ShellMenuTracker,
|
||||
shell_menu_tracker,
|
||||
shell_menu_tracker_ref,
|
||||
shell_menu_tracker_unref)
|
@ -1,60 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2013 Red Hat
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as
|
||||
* published by the Free Software Foundation; either version 2 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program 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
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Written by:
|
||||
* Jasper St. Pierre <jstpierre@mecheye.net>
|
||||
*/
|
||||
|
||||
#ifndef __SHELL_MENU_TRACKER_H__
|
||||
#define __SHELL_MENU_TRACKER_H__
|
||||
|
||||
#include <gio/gio.h>
|
||||
|
||||
#include "gtkmenutrackeritem.h"
|
||||
|
||||
typedef struct _ShellMenuTracker ShellMenuTracker;
|
||||
|
||||
GType shell_menu_tracker_get_type (void) G_GNUC_CONST;
|
||||
|
||||
typedef void (* ShellMenuTrackerInsertFunc) (GtkMenuTrackerItem *item,
|
||||
gint position,
|
||||
gpointer user_data);
|
||||
|
||||
typedef void (* ShellMenuTrackerRemoveFunc) (gint position,
|
||||
gpointer user_data);
|
||||
|
||||
ShellMenuTracker * shell_menu_tracker_new (GtkActionObservable *observable,
|
||||
GMenuModel *model,
|
||||
const gchar *action_namespace,
|
||||
ShellMenuTrackerInsertFunc insert_func,
|
||||
gpointer insert_user_data,
|
||||
GDestroyNotify insert_notify,
|
||||
ShellMenuTrackerRemoveFunc remove_func,
|
||||
gpointer remove_user_data,
|
||||
GDestroyNotify remove_notify);
|
||||
ShellMenuTracker * shell_menu_tracker_new_for_item_submenu (GtkMenuTrackerItem *item,
|
||||
ShellMenuTrackerInsertFunc insert_func,
|
||||
gpointer insert_user_data,
|
||||
GDestroyNotify insert_notify,
|
||||
ShellMenuTrackerRemoveFunc remove_func,
|
||||
gpointer remove_user_data,
|
||||
GDestroyNotify remove_notify);
|
||||
|
||||
ShellMenuTracker * shell_menu_tracker_ref (ShellMenuTracker *tracker);
|
||||
void shell_menu_tracker_unref (ShellMenuTracker *tracker);
|
||||
void shell_menu_tracker_destroy (ShellMenuTracker *tracker);
|
||||
|
||||
#endif /* __SHELL_MENU_TRACKER_H__ */
|
Loading…
Reference in New Issue
Block a user