/**
* GLOBAL HOTKEY CLASS
* Built on: 22 October 2008(revision 1)
* by Mattias Buelens
*
* This class allows script developers to easily
* create system-wide hotkeys.
* You can optionally include the Action Hook class
* so you can hook into specific parts of the class
* for even more functionality and possibilities.
*/
//Global hotkey message constants
var WM_HOTKEY = 0x312;
var MOD_ALT = 0x1;
var MOD_CONTROL = 0x2;
var MOD_SHIFT = 0x4;
var MOD_WIN = 0x8;
//Global object
var GlobalHotkey;
/* Atom class */
var Atom = function (sString) {
var nAtom = Atom.Add(sString);
return {
Get: function () { return Atom.Get(sString); },
GetName: function () { return Atom.GetName(nAtom); },
Delete: function () { return Atom.Delete(nAtom); }
};
};
//Adds a global atom to the table
Atom.Add = function (sString) {
return Interop.Call('kernel32', 'GlobalAddAtomW', sString);;
};
//Retrieves the value of the specified atom
Atom.Get = function (sString) {
return Interop.Call('kernel32', 'GlobalFindAtomW', sString);
};
//Retrieve the atom name based on a specified atom value
Atom.GetName = function (nAtom) {
var sBuffer = Interop.Allocate(512);
Interop.Call('kernel32', 'GlobalGetAtomNameW', 1*nAtom, sBuffer, 255);
return sBuffer.ReadString(0);
};
//Removes a specified atom from the table based on it's value
Atom.Delete = function (nAtom) {
Interop.Call('kernel32', 'GlobalDeleteAtom', (1*nAtom).toString(16) );
};
/* GlobalHotkey */
(function () {
/* Private object properties */
var oGlobalHotkeys = {};
var oRegistered = {};
var Subclass;
var bInitialized = false;
var bPaused = false;
/* Private functions */
//Initializes the hotkey hook
var InitializeHotkeyHook = function () {
GlobalHotkey.Actions.Do('BeforeInit', GlobalHotkey);
//Close the subclass window(if already created)
try {
UnregisterHotkeys();
Subclass.RegisterMessageNotification(WM_HOTKEY, false);
Subclass.Close(0);
} catch (e) {}
//Re-open the subclass window
Subclass = MsgPlus.CreateWnd(GlobalHotkey.Subclass.InterfacePath, GlobalHotkey.Subclass.WindowId, 0x3);
Subclass.RegisterMessageNotification(WM_HOTKEY, true);
RegisterHotkeys();
GlobalHotkey.Subclass.Registry.SetValue(Subclass.Handle);
GlobalHotkey.Actions.Do('AfterInit', GlobalHotkey);
};
//Registers the hotkeys
var RegisterHotkeys = function () {
GlobalHotkey.Actions.Do('BeforeRegister', GlobalHotkey);
hWndOldSubclass = GlobalHotkey.Subclass.Registry.GetValue();
var aFailedHotkeys =[];
var bSuccess = true;
for (var i in oGlobalHotkeys) {
var oHotkey = oGlobalHotkeys[i];
oHotkey.Update();
Interop.Call('user32', 'UnregisterHotKey', hWndOldSubclass, Atom.Get(oHotkey.Id) );
GlobalHotkey.Actions.Do('BeforeRegisterHotkey', oHotkey);
oHotkey.Update(true);
var nHotkey = LoByte(oHotkey.JoinedKey);
var nModifier = oHotkey.Modifier;
var nAtom = Atom.Add(oHotkey.Id);
var Result = Interop.Call('user32', 'RegisterHotKey', Subclass.Handle, nAtom, nModifier, nHotkey);
GlobalHotkey.Actions.Do('RegisterHotkey', oHotkey, (Result !== 0) );
if (Result === 0) {
aFailedHotkeys.push(oHotkey);
bSuccess = false;
} else {
oRegistered[(oHotkey.Id) ] = oHotkey;
Atom.Delete(nAtom);
}
}
if (bSuccess) {
GlobalHotkey.Actions.Do('AfterRegister', true);
} else {
GlobalHotkey.Actions.Do('AfterRegister', false, aFailedHotkeys);
}
return bSuccess;
};
//Unregisters all hotkeys
var UnregisterHotkeys = function () {
GlobalHotkey.Actions.Do('BeforeUnregister', GlobalHotkey);
if (typeof Subclass === 'object' && Subclass.Handle) {
Subclass.RegisterMessageNotification(WM_HOTKEY, false);
for (var i in oRegistered) {
var oHotkey = oRegistered[i];
var nAtom = Atom.Get(oHotkey.Id);
Interop.Call('user32', 'UnregisterHotKey', Subclass.Handle, nAtom);
Atom.Delete(nAtom);
}
}
oRegistered = {};
GlobalHotkey.Actions.Do('AfterUnregister', GlobalHotkey);
}
//Re-registers the hotkeys
var ReloadHotkeys = function () {
UnregisterHotkeys();
GlobalHotkey.Actions.Do('BeforeRegister', GlobalHotkey);
var aFailedHotkeys =[];
var bSuccess = true;
for (var i in oGlobalHotkeys) {
var oHotkey = oGlobalHotkeys[i];
oHotkey.Update();
GlobalHotkey.Actions.Do('BeforeRegisterHotkey', oHotkey);
oHotkey.Update(true);
var nHotkey = oHotkey.JoinedKey;
var nVirtualKey = oHotkey.KeyCode;
var nModifier = oHotkey.Modifier;
var nAtom = Atom.Add(oHotkey.Id);
var Result = Interop.Call('user32', 'RegisterHotKey', Subclass.Handle, nAtom, nModifier, LoByte(nHotkey) );
GlobalHotkey.Actions.Do('RegisterHotkey', oHotkey, (Result !== 0) );
if (Result === 0) {
aFailedHotkeys.push(oHotkey);
bSuccess = false;
} else {
oRegistered[(oHotkey.Id) ] = oHotkey;
Atom.Delete(nAtom);
}
}
Subclass.RegisterMessageNotification(WM_HOTKEY, true);
if (bSuccess) {
GlobalHotkey.Actions.Do('AfterRegister', true);
} else {
GlobalHotkey.Actions.Do('AfterRegister', false, aFailedHotkeys);
}
return bSuccess;
}
/* Helper functions */
//Parses the modifiers from a key code
var ParseKeyCode = function (nKeyCode, nModifier) {
var nOrigMod = (nModifier = (nModifier ? nModifier : 0) );
nModifier &= ~ (0x1000 | 0x800 | 0x400 | 0x200 | 0x100);
if ( (nKeyCode | nOrigMod) & 0x1000) { nKeyCode &= ~0x1000; nModifier |= MOD_WIN; } //Windows
if ( (nKeyCode | nOrigMod) & 0x800) { nKeyCode &= ~0x800; } //Extended
if ( (nKeyCode | nOrigMod) & 0x400) { nKeyCode &= ~0x400; nModifier |= MOD_ALT; } //Alt
if ( (nKeyCode | nOrigMod) & 0x200) { nKeyCode &= ~0x200; nModifier |= MOD_CONTROL } //Control
if ( (nKeyCode | nOrigMod) & 0x100) { nKeyCode &= ~0x100; nModifier |= MOD_SHIFT } //Shift
return {
Key: nKeyCode,
Mod: nModifier
};
};
//Applies the modifiers to a key code
var JoinKeyCode = function (nKeyCode, nModifier) {
if (!nModifier) return nKeyCode;
if (nModifier & MOD_WIN) { nKeyCode |= 0x1000; } //Windows
if (nModifier & MOD_ALT) { nKeyCode |= 0x400; } //Alt
if (nModifier & MOD_CONTROL) { nKeyCode |= 0x200; } //Control
if (nModifier & MOD_SHIFT) { nKeyCode |= 0x100; } //Shift
return nKeyCode;
};
//Creates a readable string based on the numerical value of the hotkey
function GetKeyName(uCode) {
var ScanCode = Interop.Call('user32', 'MapVirtualKeyW', LoByte(uCode), 0);
if (HiByte(uCode) & 8) ScanCode |= 0x100;
var Text = Interop.Allocate(512);
Interop.Call('user32', 'GetKeyNameTextW', ScanCode << 16, Text, 255);
var Prefix = (HiByte(uCode) & 2 ? 'Ctrl + ' : '') + (HiByte(uCode) & 1 ? 'Shift + ' : '') + (HiByte(uCode) & 4 ? 'Alt + ' : '');
return Prefix + Text.ReadString(0);
};
//Binds a function to an object - based on Function.bind from Prototype(www.prototypejs.org)
var BindFunction = function () {
var args = toArray(arguments);
var func = args.shift();
var obj = args.shift();
return function () {
return func.apply(obj, args.concat(toArray(arguments) ) );
}
};
//Converts any iterable object to an array - based on $A from Prototype(www.prototypejs.org)
var toArray = function (iterable) {
if (!iterable) return[];
if (iterable.toArray) return iterable.toArray();
var length = iterable.length, results = new Array(length);
while (length--) results[length] = iterable[length];
return results;
};
//Retrieves the lobyte from a value
function LoByte(w) { return w & 0xff; }
//Retrieves the hibyte from a value
function HiByte(w) { return w >> 8; }
//Creates a WORD value by concatenating the specified values.
function MakeWord(a,b) { return a | (b << 8); }
/* GlobalHotkey class */
GlobalHotkey = function (sId, nKeyCode, nModifier, fCallback, oScope) {
/* Private class properties */
sId = String(sId);
nKeyCode = 1*nKeyCode;
nModifier = 1*nModifier;
fCallback = (typeof fCallback === 'function')
? (typeof oScope === "undefined") ? fCallback : BindFunction (fCallback, this)
: (function () { });
var Joined = JoinKeyCode(nKeyCode, nModifier);
var Parsed = ParseKeyCode(Joined);
var KeyName = GetKeyName(Joined);
var Instance = {
/* Public class properties */
Id: sId,
KeyCode: Parsed.Key,
Modifier: Parsed.Mod,
Callback: fCallback,
KeyName: KeyName,
JoinedKey: Joined,
/* Public class functions */
Update: function (bNoUnregister) {
if (!bNoUnregister) {
Interop.Call('user32', 'UnregisterHotKey', Subclass.Handle, Atom.Add(sId) );
}
var Joined = JoinKeyCode(this.KeyCode, this.Modifier);
var Parsed = ParseKeyCode(Joined);
var KeyName = GetKeyName(Joined);
this.KeyCode = Parsed.Key;
this.Modifier = Parsed.Mod;
this.KeyName = KeyName;
this.JoinedKey = Joined;
},
Delete: function (bNoUnregister) {
if (!bNoUnregister) {
Interop.Call('user32', 'UnregisterHotKey', Subclass.Handle, Atom.Get(sId) );
}
delete oRegistered[this.Id];
delete oGlobalHotkeys[this.Id];
}
};
oGlobalHotkeys[sId] = Instance;
return Instance;
};
/* Public object properties */
//Subclass window information
GlobalHotkey.Subclass = {
InterfacePath: 'Subclass.xml',
WindowId: 'Subclass',
/*
* These functions are used to get and set the key
* value of the current subclass window. To avoid
* overloading this class with duplicate functions,
* I consider you already have such registry
* reading/writing functions in your script.
* GetValue should return the value of the subclass
* window handle key, SetValue should set that value
* according to the given parameter as a DWORD.
*/
Registry: {
GetValue: function () { return 0; },
SetValue: function (hWnd) { }
}
};
//Global hooks
GlobalHotkey.Actions = (function () {
try {
return new ActionHook('GlobalHotkeyClass');
} catch (e) {
return { Add: function () {}, Do: function () {} };
}
})();
/* Public object functions */
GlobalHotkey.Init = function () {
InitializeHotkeyHook();
bInitialized = true;
};
GlobalHotkey.Destroy = function () {
UnregisterHotkeys();
bInitialized = false;
};
GlobalHotkey.Reload = function () {
if (bInitialized) ReloadHotkeys();
else InitializeHotkeyHook();
bInitialized = true;
bPaused = false;
};
GlobalHotkey.Break = function (bNewState) {
bNewState = (typeof bNewState === "undefined") ? !bPaused : (bNewState == true);
if (bNewState) UnregisterHotkeys();
else InitializeHotkeyHook();
bPaused = bNewState;
};
GlobalHotkey.Add = function () {
var args = toArray(arguments);
return GlobalHotkey.apply(null, args);
};
GlobalHotkey.Get = function (sId) {
try {
return oGlobalHotkeys[sId];
} catch (e) {
return null;
}
};
GlobalHotkey.DeleteAll = function () {
oGlobalHotkeys = {};
};
GlobalHotkey.GetSubclass = function () {
return Subclass;
};
GlobalHotkey.IsInit = function () {
return(bInitialized == true);
};
GlobalHotkey.GetKeyName = function (nKeyCode, nModifier) {
var Joined = JoinKeyCode(nKeyCode, nModifier);
var Parsed = ParseKeyCode(Joined);
return GetKeyName(Joined);
};
/* Public object events */
GlobalHotkey.HandleNotification = function () {
var args = toArray( (arguments.length === 1) ? arguments[0]: arguments);
var pPlusWnd = args[0], nMessage = args[1], wParam = args[2], lParam = args[3];
if (nMessage === WM_HOTKEY) {
var sAtomName = Atom.GetName(wParam);
if (typeof oRegistered[sAtomName] === "object") {
var oHotkey = oRegistered[sAtomName];
oHotkey.Callback(oHotkey);
}
return true;
}
return false;
}
})();