07053c3df7
GrabHelper already takes care of putting the key focus back on the widget that had it when the grab was established. ShellEntry's close() has recently become harmful since it's now called from the menu's destroy() method and that is called in the entry's destroy handler which means that this._entry might no longer be valid at close() time. https://bugzilla.gnome.org/show_bug.cgi?id=697560
176 lines
5.5 KiB
JavaScript
176 lines
5.5 KiB
JavaScript
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
|
|
|
|
const Clutter = imports.gi.Clutter;
|
|
const Gtk = imports.gi.Gtk;
|
|
const Lang = imports.lang;
|
|
const St = imports.gi.St;
|
|
|
|
const BoxPointer = imports.ui.boxpointer;
|
|
const Main = imports.ui.main;
|
|
const Params = imports.misc.params;
|
|
const PopupMenu = imports.ui.popupMenu;
|
|
|
|
const EntryMenu = new Lang.Class({
|
|
Name: 'ShellEntryMenu',
|
|
Extends: PopupMenu.PopupMenu,
|
|
|
|
_init: function(entry, params) {
|
|
params = Params.parse (params, { isPassword: false });
|
|
|
|
this.parent(entry, 0, St.Side.TOP);
|
|
|
|
this.actor.add_style_class_name('entry-context-menu');
|
|
|
|
this._entry = entry;
|
|
this._clipboard = St.Clipboard.get_default();
|
|
|
|
// Populate menu
|
|
let item;
|
|
item = new PopupMenu.PopupMenuItem(_("Copy"));
|
|
item.connect('activate', Lang.bind(this, this._onCopyActivated));
|
|
this.addMenuItem(item);
|
|
this._copyItem = item;
|
|
|
|
item = new PopupMenu.PopupMenuItem(_("Paste"));
|
|
item.connect('activate', Lang.bind(this, this._onPasteActivated));
|
|
this.addMenuItem(item);
|
|
this._pasteItem = item;
|
|
|
|
this._passwordItem = null;
|
|
if (params.isPassword)
|
|
this._makePasswordItem();
|
|
|
|
Main.uiGroup.add_actor(this.actor);
|
|
this.actor.hide();
|
|
},
|
|
|
|
_makePasswordItem: function() {
|
|
let item = new PopupMenu.PopupMenuItem('');
|
|
item.connect('activate', Lang.bind(this,
|
|
this._onPasswordActivated));
|
|
this.addMenuItem(item);
|
|
this._passwordItem = item;
|
|
},
|
|
|
|
get isPassword() {
|
|
return this._passwordItem != null;
|
|
},
|
|
|
|
set isPassword(v) {
|
|
if (v == this.isPassword)
|
|
return;
|
|
|
|
if (v)
|
|
this._makePasswordItem();
|
|
else {
|
|
this._passwordItem.destroy();
|
|
this._passwordItem = null;
|
|
}
|
|
},
|
|
|
|
open: function(animate) {
|
|
this._updatePasteItem();
|
|
this._updateCopyItem();
|
|
if (this._passwordItem)
|
|
this._updatePasswordItem();
|
|
|
|
this.parent(animate);
|
|
this._entry.add_style_pseudo_class('focus');
|
|
|
|
let direction = Gtk.DirectionType.TAB_FORWARD;
|
|
if (!this.actor.navigate_focus(null, direction, false))
|
|
this.actor.grab_key_focus();
|
|
},
|
|
|
|
_updateCopyItem: function() {
|
|
let selection = this._entry.clutter_text.get_selection();
|
|
this._copyItem.setSensitive(!this._entry.clutter_text.password_char &&
|
|
selection && selection != '');
|
|
},
|
|
|
|
_updatePasteItem: function() {
|
|
this._clipboard.get_text(St.ClipboardType.CLIPBOARD, Lang.bind(this,
|
|
function(clipboard, text) {
|
|
this._pasteItem.setSensitive(text && text != '');
|
|
}));
|
|
},
|
|
|
|
_updatePasswordItem: function() {
|
|
let textHidden = (this._entry.clutter_text.password_char);
|
|
if (textHidden)
|
|
this._passwordItem.label.set_text(_("Show Text"));
|
|
else
|
|
this._passwordItem.label.set_text(_("Hide Text"));
|
|
},
|
|
|
|
_onCopyActivated: function() {
|
|
let selection = this._entry.clutter_text.get_selection();
|
|
this._clipboard.set_text(St.ClipboardType.CLIPBOARD, selection);
|
|
},
|
|
|
|
_onPasteActivated: function() {
|
|
this._clipboard.get_text(St.ClipboardType.CLIPBOARD, Lang.bind(this,
|
|
function(clipboard, text) {
|
|
if (!text)
|
|
return;
|
|
this._entry.clutter_text.delete_selection();
|
|
let pos = this._entry.clutter_text.get_cursor_position();
|
|
this._entry.clutter_text.insert_text(text, pos);
|
|
}));
|
|
},
|
|
|
|
_onPasswordActivated: function() {
|
|
let visible = !!(this._entry.clutter_text.password_char);
|
|
this._entry.clutter_text.set_password_char(visible ? '' : '\u25cf');
|
|
}
|
|
});
|
|
|
|
function _setMenuAlignment(entry, stageX) {
|
|
let [success, entryX, entryY] = entry.transform_stage_point(stageX, 0);
|
|
if (success)
|
|
entry.menu.setSourceAlignment(entryX / entry.width);
|
|
};
|
|
|
|
function _onButtonPressEvent(actor, event, entry) {
|
|
if (entry.menu.isOpen) {
|
|
entry.menu.close(BoxPointer.PopupAnimation.FULL);
|
|
return true;
|
|
} else if (event.get_button() == 3) {
|
|
let [stageX, stageY] = event.get_coords();
|
|
_setMenuAlignment(entry, stageX);
|
|
entry.menu.open(BoxPointer.PopupAnimation.FULL);
|
|
return true;
|
|
}
|
|
return false;
|
|
};
|
|
|
|
function _onPopup(actor, entry) {
|
|
let [success, textX, textY, lineHeight] = entry.clutter_text.position_to_coords(-1);
|
|
if (success)
|
|
entry.menu.setSourceAlignment(textX / entry.width);
|
|
entry.menu.open(BoxPointer.PopupAnimation.FULL);
|
|
};
|
|
|
|
function addContextMenu(entry, params) {
|
|
if (entry.menu)
|
|
return;
|
|
|
|
entry.menu = new EntryMenu(entry, params);
|
|
entry._menuManager = new PopupMenu.PopupMenuManager({ actor: entry });
|
|
entry._menuManager.addMenu(entry.menu);
|
|
|
|
// Add an event handler to both the entry and its clutter_text; the former
|
|
// so padding is included in the clickable area, the latter because the
|
|
// event processing of ClutterText prevents event-bubbling.
|
|
entry.clutter_text.connect('button-press-event', Lang.bind(null, _onButtonPressEvent, entry));
|
|
entry.connect('button-press-event', Lang.bind(null, _onButtonPressEvent, entry));
|
|
|
|
entry.connect('popup-menu', Lang.bind(null, _onPopup, entry));
|
|
|
|
entry.connect('destroy', function() {
|
|
entry.menu.destroy();
|
|
entry.menu = null;
|
|
entry._menuManager = null;
|
|
});
|
|
}
|