// -*- 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; var EntryMenu = new Lang.Class({ Name: 'ShellEntryMenu', Extends: PopupMenu.PopupMenu, _init(entry) { this.parent(entry, 0, St.Side.TOP); this._entry = entry; this._clipboard = St.Clipboard.get_default(); // Populate menu let item; item = new PopupMenu.PopupMenuItem(_("Copy")); item.connect('activate', this._onCopyActivated.bind(this)); this.addMenuItem(item); this._copyItem = item; item = new PopupMenu.PopupMenuItem(_("Paste")); item.connect('activate', this._onPasteActivated.bind(this)); this.addMenuItem(item); this._pasteItem = item; this._passwordItem = null; Main.uiGroup.add_actor(this.actor); this.actor.hide(); }, _makePasswordItem() { let item = new PopupMenu.PopupMenuItem(''); item.connect('activate', this._onPasswordActivated.bind(this)); this.addMenuItem(item); this._passwordItem = item; }, get isPassword() { return this._passwordItem != null; }, set isPassword(v) { if (v == this.isPassword) return; if (v) { this._makePasswordItem(); this._entry.input_purpose = Gtk.InputPurpose.PASSWORD; } else { this._passwordItem.destroy(); this._passwordItem = null; this._entry.input_purpose = Gtk.InputPurpose.FREE_FORM; } }, open(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() { let selection = this._entry.clutter_text.get_selection(); this._copyItem.setSensitive(!this._entry.clutter_text.password_char && selection && selection != ''); }, _updatePasteItem() { this._clipboard.get_text(St.ClipboardType.CLIPBOARD, (clipboard, text) => { this._pasteItem.setSensitive(text && text != ''); }); }, _updatePasswordItem() { 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() { let selection = this._entry.clutter_text.get_selection(); this._clipboard.set_text(St.ClipboardType.CLIPBOARD, selection); }, _onPasteActivated() { this._clipboard.get_text(St.ClipboardType.CLIPBOARD, (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() { 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 Clutter.EVENT_STOP; } else if (event.get_button() == 3) { let [stageX, stageY] = event.get_coords(); _setMenuAlignment(entry, stageX); entry.menu.open(BoxPointer.PopupAnimation.FULL); return Clutter.EVENT_STOP; } return Clutter.EVENT_PROPAGATE; }; 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; params = Params.parse (params, { isPassword: false }); entry.menu = new EntryMenu(entry); entry.menu.isPassword = params.isPassword; 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', (actor, event) => { _onButtonPressEvent(actor, event, entry); }); entry.connect('button-press-event', (actor, event) => { _onButtonPressEvent(actor, event, entry); }); entry.connect('popup-menu', actor => { _onPopup(actor, entry); }); entry.connect('destroy', () => { entry.menu.destroy(); entry.menu = null; entry._menuManager = null; }); }