move from using the test textbox to using the actual textbox in the editable tree, and properly round-trip the user's selections.
authorDaniel Brooks <db48x@db48x.net>
Thu, 17 Jul 2008 22:09:32 -0500
changeset 64880 0880d891168a073be2675f46bc86138b7a3f0b68
parent 64879 3a694ee89c4976dc93af8bae3d36891f6cad07f0
child 64881 c707f4463387be5ef7756be00a0a80872af2cbba
push id1
push userclegnitto@mozilla.com
push dateTue, 12 Apr 2011 01:19:02 +0000
treeherdermozilla-aurora@0cfe6840e0a4 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
move from using the test textbox to using the actual textbox in the editable tree, and properly round-trip the user's selections.
mobile/chrome/content/browser.xul
mobile/chrome/content/shortcuts.js
--- a/mobile/chrome/content/browser.xul
+++ b/mobile/chrome/content/browser.xul
@@ -135,17 +135,17 @@
     <!-- misc -->
     <key id="key_menu" keycode="VK_F4" command="cmd_menu"/>
     <key id="key_fullscreen" keycode="VK_F6" command="cmd_fullscreen"/>
     <key id="key_addons" key="E" command="cmd_addons" modifiers="accel"/>
     <key id="key_downloads" key="J" command="cmd_downloads" modifiers="control"/>
 
     <key id="key_find" key="&findOnCmd.commandkey;" command="cmd_find" modifiers="accel"/>
     <key id="key_findAgain" key="&findAgainCmd.commandkey;" command="cmd_findAgain" modifiers="accel"/>
-    <key id="key_findPrevious" key="&findAgainCmd.commandkey;" command="cmd_findPrevious" modifiers="accel,shift"/>
+    <key id="key_findPrevious" key="&findAgainCmd.commandkey;" command="cmd_findPrevious" modifiers="accel shift"/>
     <key keycode="&findAgainCmd.commandkey2;" command="cmd_findAgain"/>
     <key keycode="&findAgainCmd.commandkey2;"  command="cmd_findPrevious" modifiers="shift"/>
   </keyset>
 
   <popupset id="mainPopupSet">
     <panel type="autocomplete-richlistbox" id="popup_autocomplete"
            noautofocus="true" onpopupshowing="BrowserUI.sizeAutocompletePopup()">
       <hbox id="autocomplete_navbuttons" align="center" flex="1"
@@ -280,17 +280,16 @@
       <tree id="shortcuts" flex="1" seltype="text" hidecolumnpicker="true" editable="true">
         <treecols>
           <treecol id="name" flex="2" hideheader="true"/>
           <treecol id="value" flex="1" hideheader="true" editable="true" primary="true"/>
         </treecols>
         <treechildren id="shortcuts-children"/>
       </tree>
       <hbox pack="end">
-        <textbox id="test"/>
         <button label="Dismiss" oncommand="Shortcuts.dismiss();"/>
       </hbox>
     </vbox>
   </stack>
 
   <vbox id="findpanel-placeholder" sizetopopup="always">
     <panel id="findpanel" onpopupshown="Browser.doFind()">
       <findbar id="findbar"/>
--- a/mobile/chrome/content/shortcuts.js
+++ b/mobile/chrome/content/shortcuts.js
@@ -51,16 +51,18 @@ function ShortcutEditor()
 {
     var prefsvc = Components.classes["@mozilla.org/preferences-service;1"]
                             .getService(Components.interfaces.nsIPrefService);
     var prefs = Components.classes["@mozilla.org/preferences-service;1"]
                           .getService(Components.interfaces.nsIPrefBranch2);
     var keyPrefs = prefsvc.getBranch("shortcut.");
     var keyCache;
 
+    var tree;
+
     // first, we need to be able to manipulate the keys and commands themselves
     function getCommandNames()
     {
         return Array.map(document.getElementsByTagNameNS(XUL_NS, "command"), function(c) { return c.getAttribute("id"); });
     }
 
     function getKeys()
     {
@@ -83,17 +85,17 @@ function ShortcutEditor()
         // TODO: This is a bit simplistic as yet. For example, we should match
         //       a key with an optional modifier even if that modifier isn't
         //       specified in our arguments. Also, we need to differentiate
         //       between a key element with an attribute that is an empty string
         //       and one without that attribute at all.
         var keys = document.getElementsByTagNameNS(XUL_NS, "key");
         var l = keys.length;
         for (var i = 0; i < l; i++)
-            if (keys[i].getAttribute("modifiers") == keySpec.modifiers &&
+            if (keys[i].getAttribute("modifiers") == getModifiersFromFlags(keySpec.modifiers) &&
                 keys[i].getAttribute("key") == keySpec.key &&
                 keys[i].getAttribute("keycode") == keySpec.keycode)
                 return keys[i];
     }
 
     function addKey(command, keySpec)
     {
         // generally adds a new key to the document that runs command,
@@ -105,75 +107,87 @@ function ShortcutEditor()
         var key = findKeyForCommand(command);
         if (keySpec.exists)
         {
             if (findCommandForKey(keySpec))
                 return null;
 
             if (key)
             {
-                key.setAttribute("modifiers") = keySpec.modifiers;
-                key.setAttribute("key") = keySpec.key;
-                key.setAttribute("keycode") = keySpec.keycode;
+                key.setAttribute("modifiers", getModifiersFromFlags(keySpec.modifiers));
+                key.setAttribute("key", keySpec.key);
+                key.setAttribute("keycode", keySpec.keycode);
             }
             else
             {
                 key = document.createElementNS(XUL_NS, "key");
-                key.setAttribute("modifiers") = keySpec.modifiers;
-                key.setAttribute("key") = keySpec.key;
-                key.setAttribute("keycode") = keySpec.keycode;
-                key.setAttribute("command") = command;
+                key.setAttribute("modifiers", getModifiersFromFlags(keySpec.modifiers));
+                key.setAttribute("key", keySpec.key);
+                key.setAttribute("keycode", keySpec.keycode);
+                key.setAttribute("command", command);
                 document.getElementById("mainKeyset").appendChild(k);
             }
 
-            return k;
+            return key;
         }
 
         if (key)
             key.parentNode.removeChild(key);
         return null;
     }
 
     function makeKeySpec(modifiers, key, keycode)
     {
         // TODO: make this check more specific, once key elements implement a unique interface
         if (modifiers instanceof Components.interfaces.nsIDOMElement)
             return {
                 exists: true,
                 modifiers: getFlagsForModifiers(modifiers.getAttribute("modifiers")),
-                key: modifiers.getAttribute("key"),
-                keycode: modifiers.getAttribute("keycode")
+                key: modifiers.getAttribute("key") || false,
+                keycode: modifiers.getAttribute("keycode") || false
             };
         if (modifiers instanceof Components.interfaces.nsIDOMKeyEvent)
             return {
                 exists: true,
                 modifiers: getEventModifiers(modifiers),
-                key: getEventKey(modifiers),
-                keycode: getEventKeyCode(modifiers)
+                key: getEventKey(modifiers) || false,
+                keycode: getEventKeyCode(modifiers) || false
             };
         return {
             exists: !!(modifiers || key || keycode),
             modifiers: getFlagsForModifiers(modifiers),
-            key: key,
-            keycode: keycode
+            key: key || false,
+            keycode: keycode || false
         };
     }
 
     var modifierFlags = { alt: 1, control: 2, meta: 4, shift: 8 };
     function getFlagsForModifiers(modifiers)
     {
         if (!modifiers)
             return 0;
 
         var result;
         for each (m in modifiers.split(" "))
             result |= modifierFlags[m];
         return result;
     }
 
+    function getModifiersFromFlags(flags)
+    {
+        var result = [], i = 1;
+        for each (m in ["alt", "control", "meta", "shift"])
+        {
+            if (flags & i)
+                result.push(m);
+            i += i;
+        }
+        return result.join(" ");
+    }
+
     function getEventModifiers(event)
     {
         var result, i = 1;
         for each (m in [event.altKey, event.ctrlKey, event.metaKey, event.shiftKey])
         {
             result |= (m && i);
             i += i;
         }
@@ -323,44 +337,69 @@ function ShortcutEditor()
         return accel.join(modifierSeparator);
     }
 
     // this listens to keyup events and converts them into the proper display name for the textbox
     function keyListener(event)
     {
         if (!event instanceof Components.interfaces.nsIDOMKeyEvent)
             return;
-        document.getElementById("test").value = getKeyName(makeKeySpec(event));
+
+        var keySpec = makeKeySpec(event);
+        this.value = getKeyName(keySpec);
+        tree.setAttribute("spec", JSON.toString(keySpec));
+        dump(tree.getAttribute("spec") +"\n");
         event.preventDefault();
     }
 
+    function modificationListener(event)
+    {
+        if (event.attrName == "label" && event.newValue != event.prevValue)
+        {
+            var keySpec = tree.getAttribute("spec");
+            tree.removeAttribute("spec");
+            var cell = event.relatedNode.ownerElement;
+            cell.setAttribute("value", keySpec);
+            var command = cell.previousSibling.getAttribute("value");
+            var keySpec = JSON.fromString(keySpec);
+            addKey(command, keySpec);
+            save(command, keySpec);
+        }
+    }
+
     // show the window
     this.edit = function()
     {
+        tree = document.getElementById("shortcuts");
+
         var nodes = document.getElementById("ui-stack").childNodes;
-        Array.forEach(nodes, function(n) { if (n.getAttribute("id") != "browser-container") { n.hidden = true; }});
+        Array.forEach(nodes, function(n) { if (n.getAttribute("id") != "browser-container") n.hidden = true; });
         document.getElementById("shortcuts-container").hidden = false;
         fillShortcutList();
 
-        document.getElementById("test").addEventListener("keypress", keyListener, true);
+        document.getAnonymousElementByAttribute(tree, "anonid", "input")
+                .addEventListener("keypress", keyListener, true);
+        tree.addEventListener("DOMAttrModified", modificationListener, true);
     };
 
     function hack()
     {
         // TODO: this is a hack, so I want to remove it. to do so, key elements
         // will have to respond to direct dom manipulation.
         Array.map(document.getElementsByTagNameNS(XUL_NS, "keyset"),
-                  function(e) { document.removeChild(e); return document.cloneNode(e, true); })
-             .forEach(function(e) { document.appendChild(e); });
+                  function(e) { return e.parentNode.removeChild(e); })
+             .forEach(function(e) { document.documentElement.appendChild(e); });
     }
 
     this.dismiss = function()
     {
-	hack();
-        document.getElementById("test").removeEventListener("keypress", keyListener, true);
+        hack();
+        document.getAnonymousElementByAttribute(tree, "anonid", "input")
+                .removeEventListener("keypress", keyListener, true);
+        tree.removeEventListener("DOMAttrModified", modificationListener, true);
         document.getElementById("shortcuts-container").hidden = true;
     };
 
     // also, updating the UI is helpful
     function fillShortcutList()
     {
         var commands = getCommandNames();
         var sb = document.getElementById("shortcut-bundles").childNodes;
@@ -391,17 +430,16 @@ function ShortcutEditor()
             for (var i = 0; i < l; i++)
                 try
                 {
                     return sb[i].getString(name);
                 }
                 catch (e) { }
         }
 
-        var tree = document.getElementById("shortcuts");
         var children = document.getElementById("shortcuts-children");
         tree.removeChild(children);
         children = document.createElementNS(XUL_NS, "treechildren");
         children.setAttribute("id", "shortcuts-children");
         tree.appendChild(children);
 
         commands.forEach(doAppend);
     }
@@ -419,18 +457,18 @@ function ShortcutEditor()
             return JSON.fromString(keyPrefs.getCharPref(command));
         }
         catch (ex)
         {
             return makeKeySpec();
         }
     }
 
-    // and of course, none of this would be any use unless at some point we made
-    // the proper changes to the document based on the user's choices.
-    function restore()
+    // and of course, none of this would be any use unless at some point we
+    // ensure that all the keys in the window match the user's choices
+    this.restore = function()
     {
-        getCommandNames().forEach(function(c) { addKey(cmd, load(cmd)); });
+        getCommandNames().forEach(function(cmd) { addKey(cmd, load(cmd)); });
         hack();
     }
 }
 
 var Shortcuts = new ShortcutEditor();