@qooxdoo/framework
Version:
The JS Framework for Coders
445 lines (357 loc) • 11.4 kB
JavaScript
/* ************************************************************************
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 : function(shortcut)
{
this.base(arguments);
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
*****************************************************************************
*/
members :
{
__modifier : "",
__key : "",
/*
---------------------------------------------------------------------------
USER METHODS
---------------------------------------------------------------------------
*/
/**
* Fire the "execute" event on this shortcut.
*
* @param target {Object} Object which issued the execute event
*/
execute : function(target) {
this.fireDataEvent("execute", target);
},
/**
* Key down event handler.
*
* @param event {qx.event.type.KeySequence} The key event object
*/
__onKeyDown : function(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 : function(event)
{
if (this.getEnabled() && this.__matchesKeyEvent(event))
{
if (this.isAutoRepeat()) {
this.execute(event.getTarget());
}
event.stop();
}
},
/*
---------------------------------------------------------------------------
APPLY ROUTINES
---------------------------------------------------------------------------
*/
// property apply
_applyEnabled : function(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 : function(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 : function(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 : function(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 : function()
{
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 : function()
{
// this will remove the event listener
this.setEnabled(false);
this.__modifier = this.__key = null;
}
});