/* * This file is part of the Adblock Plus build tools, * Copyright (C) 2006-2014 Eyeo GmbH * * Adblock Plus is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * Adblock Plus is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Adblock Plus. If not, see . */ Cu.import("resource://gre/modules/Services.jsm"); let validModifiers = { ACCEL: null, CTRL: "control", CONTROL: "control", SHIFT: "shift", ALT: "alt", META: "meta", __proto__: null }; /** * Sets the correct value of validModifiers.ACCEL. */ function initAccelKey() { validModifiers.ACCEL = "control"; try { let accelKey = Services.prefs.getIntPref("ui.key.accelKey"); if (accelKey == Ci.nsIDOMKeyEvent.DOM_VK_CONTROL) validModifiers.ACCEL = "control"; else if (accelKey == Ci.nsIDOMKeyEvent.DOM_VK_ALT) validModifiers.ACCEL = "alt"; else if (accelKey == Ci.nsIDOMKeyEvent.DOM_VK_META) validModifiers.ACCEL = "meta"; } catch(e) { Cu.reportError(e); } } exports.KeySelector = KeySelector; /** * This class provides capabilities to find and use available keyboard shortcut * keys. * @param {ChromeWindow} window the window where to look up existing shortcut * keys * @constructor */ function KeySelector(window) { this._initExistingShortcuts(window); } KeySelector.prototype = { /** * Map listing existing shortcut keys as its keys. * @type Object */ _existingShortcuts: null, /** * Sets up _existingShortcuts property for a window. */ _initExistingShortcuts: function(/**ChromeWindow*/ window) { if (!validModifiers.ACCEL) initAccelKey(); this._existingShortcuts = {__proto__: null}; let keys = window.document.getElementsByTagName("key"); for (let i = 0; i < keys.length; i++) { let key = keys[i]; let keyData = { shift: false, meta: false, alt: false, control: false, char: null, code: null }; let keyChar = key.getAttribute("key"); if (keyChar && keyChar.length == 1) keyData.char = keyChar.toUpperCase(); let keyCode = key.getAttribute("keycode"); if (keyCode && "DOM_" + keyCode.toUpperCase() in Ci.nsIDOMKeyEvent) keyData.code = Ci.nsIDOMKeyEvent["DOM_" + keyCode.toUpperCase()]; if (!keyData.char && !keyData.code) continue; let keyModifiers = key.getAttribute("modifiers"); if (keyModifiers) for each (let modifier in keyModifiers.toUpperCase().match(/\w+/g)) if (modifier in validModifiers) keyData[validModifiers[modifier]] = true; let canonical = [keyData.shift, keyData.meta, keyData.alt, keyData.control, keyData.char || keyData.code].join(" "); this._existingShortcuts[canonical] = true; } }, /** * Selects a keyboard shortcut variant that isn't already taken, * parses it into an object. */ selectKey: function(/**String*/ variants) /**Object*/ { for each (let variant in variants.split(/\s*,\s*/)) { if (!variant) continue; let keyData = { shift: false, meta: false, alt: false, control: false, char: null, code: null, codeName: null }; for each (let part in variant.toUpperCase().split(/\s+/)) { if (part in validModifiers) keyData[validModifiers[part]] = true; else if (part.length == 1) keyData.char = part; else if ("DOM_VK_" + part in Ci.nsIDOMKeyEvent) { keyData.code = Ci.nsIDOMKeyEvent["DOM_VK_" + part]; keyData.codeName = "VK_" + part; } } if (!keyData.char && !keyData.code) continue; let canonical = [keyData.shift, keyData.meta, keyData.alt, keyData.control, keyData.char || keyData.code].join(" "); if (canonical in this._existingShortcuts) continue; return keyData; } return null; } }; /** * Creates the text representation for a key. * @static */ KeySelector.getTextForKey = function (/**Object*/ key) /**String*/ { if (!key) return null; if (!("text" in key)) { key.text = null; try { let stringBundle = Services.strings.createBundle("chrome://global-platform/locale/platformKeys.properties"); let parts = []; if (key.control) parts.push(stringBundle.GetStringFromName("VK_CONTROL")); if (key.alt) parts.push(stringBundle.GetStringFromName("VK_ALT")); if (key.meta) parts.push(stringBundle.GetStringFromName("VK_META")); if (key.shift) parts.push(stringBundle.GetStringFromName("VK_SHIFT")); if (key.char) parts.push(key.char.toUpperCase()); else { let stringBundle2 = Services.strings.createBundle("chrome://global/locale/keys.properties"); parts.push(stringBundle2.GetStringFromName(key.codeName)); } key.text = parts.join(stringBundle.GetStringFromName("MODIFIER_SEPARATOR")); } catch (e) { Cu.reportError(e); return null; } } return key.text; }; /** * Tests whether a keypress event matches the given key. * @static */ KeySelector.matchesKey = function(/**Event*/ event, /**Object*/ key) /**Boolean*/ { if (event.defaultPrevented || !key) return false; if (key.shift != event.shiftKey || key.alt != event.altKey) return false; if (key.meta != event.metaKey || key.control != event.ctrlKey) return false; if (key.char && event.charCode && String.fromCharCode(event.charCode).toUpperCase() == key.char) return true; if (key.code && event.keyCode && event.keyCode == key.code) return true; return false; };