UNPKG

@qooxdoo/framework

Version:

The JS Framework for Coders

419 lines (357 loc) 11.2 kB
/* ************************************************************************ qooxdoo - the new era of web development http://qooxdoo.org Copyright: 2004-2008 1&1 Internet AG, Germany, http://www.1und1.de License: MIT: https://opensource.org/licenses/MIT See the LICENSE file in the project's top-level directory for details. Authors: * Sebastian Werner (wpbasti) * Andreas Ecker (ecker) * Fabian Jakobs (fjakobs) ************************************************************************ */ /** * Shortcuts can be used to globally define keyboard shortcuts. * * NOTE: Instances of this class must be disposed of after use * */ qx.Class.define("qx.bom.Shortcut", { extend: qx.core.Object, implement: [qx.core.IDisposable], /* ***************************************************************************** CONSTRUCTOR ***************************************************************************** */ /** * Create a new instance of Command * * @param shortcut {String} shortcuts can be composed of optional modifier * keys Control, Alt, Shift, Meta and a non modifier key. * If no non modifier key is specified, the second parameter is evaluated. * The key must be separated by a <code>+</code> or <code>-</code> character. * Examples: Alt+F1, Control+C, Control+Alt+Delete */ construct(shortcut) { super(); this.__modifier = {}; this.__key = null; if (shortcut != null) { this.setShortcut(shortcut); } this.initEnabled(); }, /* ***************************************************************************** EVENTS ***************************************************************************** */ events: { /** * Fired when the command is executed. Sets the "data" property of the event to * the object that issued the command. */ execute: "qx.event.type.Data" }, /* ***************************************************************************** PROPERTIES ***************************************************************************** */ properties: { /** whether the command should be respected/enabled */ enabled: { init: true, check: "Boolean", event: "changeEnabled", apply: "_applyEnabled" }, /** The command shortcut */ shortcut: { check: "String", apply: "_applyShortcut", nullable: true }, /** * Whether the execute event should be fired repeatedly if the user keep * the keys pressed. */ autoRepeat: { check: "Boolean", init: false } }, /* ***************************************************************************** MEMBERS ***************************************************************************** */ /* eslint-disable @qooxdoo/qx/no-refs-in-members */ members: { __modifier: "", __key: "", /* --------------------------------------------------------------------------- USER METHODS --------------------------------------------------------------------------- */ /** * Fire the "execute" event on this shortcut. * * @param target {Object} Object which issued the execute event */ execute(target) { this.fireDataEvent("execute", target); }, /** * Key down event handler. * * @param event {qx.event.type.KeySequence} The key event object */ __onKeyDown(event) { if (this.getEnabled() && this.__matchesKeyEvent(event)) { if (!this.isAutoRepeat()) { this.execute(event.getTarget()); } event.stop(); } }, /** * Key press event handler. * * @param event {qx.event.type.KeySequence} The key event object */ __onKeyPress(event) { if (this.getEnabled() && this.__matchesKeyEvent(event)) { if (this.isAutoRepeat()) { this.execute(event.getTarget()); } event.stop(); } }, /* --------------------------------------------------------------------------- APPLY ROUTINES --------------------------------------------------------------------------- */ // property apply _applyEnabled(value, old) { if (value) { qx.event.Registration.addListener( document.documentElement, "keydown", this.__onKeyDown, this ); qx.event.Registration.addListener( document.documentElement, "keypress", this.__onKeyPress, this ); } else { qx.event.Registration.removeListener( document.documentElement, "keydown", this.__onKeyDown, this ); qx.event.Registration.removeListener( document.documentElement, "keypress", this.__onKeyPress, this ); } }, // property apply _applyShortcut(value, old) { if (value) { // do not allow whitespaces within shortcuts if (value.search(/[\s]+/) != -1) { var msg = "Whitespaces are not allowed within shortcuts"; this.error(msg); throw new Error(msg); } this.__modifier = { Control: false, Shift: false, Meta: false, Alt: false }; this.__key = null; // To support shortcuts with "+" and "-" as keys it is necessary // to split the given value in a different way to determine the // several keyIdentifiers var index; var a = []; while (value.length > 0 && index != -1) { // search for delimiters "+" and "-" index = value.search(/[-+]+/); // add identifiers - take value if no separator was found or // only one char is left (second part of shortcut) a.push( value.length == 1 || index == -1 ? value : value.substring(0, index) ); // extract the already detected identifier value = value.substring(index + 1); } var al = a.length; for (var i = 0; i < al; i++) { var identifier = this.__normalizeKeyIdentifier(a[i]); switch (identifier) { case "Control": case "Shift": case "Meta": case "Alt": this.__modifier[identifier] = true; break; case "Unidentified": var msg = "Not a valid key name for a shortcut: " + a[i]; this.error(msg); throw msg; default: if (this.__key) { var msg = "You can only specify one non modifier key!"; this.error(msg); throw msg; } this.__key = identifier; } } } return true; }, /* -------------------------------------------------------------------------- INTERNAL MATCHING LOGIC --------------------------------------------------------------------------- */ /** * Checks whether the given key event matches the shortcut's shortcut * * @param e {qx.event.type.KeySequence} the key event object * @return {Boolean} whether the shortcuts shortcut matches the key event */ __matchesKeyEvent(e) { var key = this.__key; if (!key) { // no shortcut defined. return false; } // for check special keys // and check if a shortcut is a single char and special keys are pressed if ( (!this.__modifier.Shift && e.isShiftPressed()) || (this.__modifier.Shift && !e.isShiftPressed()) || (!this.__modifier.Control && e.isCtrlPressed()) || (this.__modifier.Control && !e.isCtrlPressed()) || (!this.__modifier.Meta && e.isMetaPressed()) || (this.__modifier.Meta && !e.isMetaPressed()) || (!this.__modifier.Alt && e.isAltPressed()) || (this.__modifier.Alt && !e.isAltPressed()) ) { return false; } if (key == e.getKeyIdentifier()) { return true; } return false; }, /* --------------------------------------------------------------------------- COMPATIBILITY TO COMMAND --------------------------------------------------------------------------- */ /** * @lint ignoreReferenceField(__oldKeyNameToKeyIdentifierMap) */ __oldKeyNameToKeyIdentifierMap: { // all other keys are converted by converting the first letter to uppercase esc: "Escape", ctrl: "Control", print: "PrintScreen", del: "Delete", pageup: "PageUp", pagedown: "PageDown", numlock: "NumLock", numpad_0: "0", numpad_1: "1", numpad_2: "2", numpad_3: "3", numpad_4: "4", numpad_5: "5", numpad_6: "6", numpad_7: "7", numpad_8: "8", numpad_9: "9", numpad_divide: "/", numpad_multiply: "*", numpad_minus: "-", numpad_plus: "+" }, /** * Checks and normalizes the key identifier. * * @param keyName {String} name of the key. * @return {String} normalized keyIdentifier or "Unidentified" if a conversion was not possible */ __normalizeKeyIdentifier(keyName) { var kbUtil = qx.event.util.Keyboard; var keyIdentifier = "Unidentified"; if (kbUtil.isValidKeyIdentifier(keyName)) { return keyName; } if (keyName.length == 1 && keyName >= "a" && keyName <= "z") { return keyName.toUpperCase(); } keyName = keyName.toLowerCase(); var keyIdentifier = this.__oldKeyNameToKeyIdentifierMap[keyName] || qx.lang.String.firstUp(keyName); if (kbUtil.isValidKeyIdentifier(keyIdentifier)) { return keyIdentifier; } else { return "Unidentified"; } }, /* --------------------------------------------------------------------------- STRING CONVERSION --------------------------------------------------------------------------- */ /** * Returns the shortcut as string using the currently selected locale. * * @return {String} shortcut */ toString() { var key = this.__key; var str = []; for (var modifier in this.__modifier) { // this.__modifier holds a map with shortcut combination keys // like "Control", "Alt", "Meta" and "Shift" as keys with // Boolean values if (this.__modifier[modifier]) { str.push(qx.locale.Key.getKeyName("short", modifier)); } } if (key) { str.push(qx.locale.Key.getKeyName("short", key)); } return str.join("+"); } }, /* ***************************************************************************** DESTRUCTOR ***************************************************************************** */ destruct() { // this will remove the event listener this.setEnabled(false); this.__modifier = this.__key = null; } });