GUI -- Fixed topo toolbar button synchronization with keystroke commands.
- Added toggleNoCb to the toggle button widget.
- Added _keyListener property to keyBindings structure, and the invocation of it from keyIn().
- Added keyListener to topoToolbar to synchronize button state.

Change-Id: Iffb1dc5d38f2d9010f5e246cfb81a6b3db30d4af
diff --git a/web/gui/src/main/webapp/app/fw/util/keys.js b/web/gui/src/main/webapp/app/fw/util/keys.js
index d22a335..d157414 100644
--- a/web/gui/src/main/webapp/app/fw/util/keys.js
+++ b/web/gui/src/main/webapp/app/fw/util/keys.js
@@ -76,6 +76,7 @@
             gk = kh.globalKeys[key],
             gcb = fs.isF(gk) || (fs.isA(gk) && fs.isF(gk[0])),
             vk = kh.viewKeys[key],
+            kl = fs.isF(kh.viewKeys._keyListener),
             vcb = fs.isF(vk) || (fs.isA(vk) && fs.isF(vk[0])) || fs.isF(kh.viewFn),
             token = getViewToken();
 
@@ -91,6 +92,9 @@
             if (vcb) {
                 vcb(token, key, keyCode, event);
             }
+            if (kl) {
+                kl(key);
+            }
         }
     }
 
diff --git a/web/gui/src/main/webapp/app/fw/widget/button.js b/web/gui/src/main/webapp/app/fw/widget/button.js
index 6b4ca28..c9980ee 100644
--- a/web/gui/src/main/webapp/app/fw/widget/button.js
+++ b/web/gui/src/main/webapp/app/fw/widget/button.js
@@ -92,10 +92,15 @@
         is.loadIcon(togDiv, gid, btnSize, true);
         togDiv.classed('selected', sel);
 
-        function _toggle(b) {
+        function _toggle(b, nocb) {
             sel = (b === undefined) ? !sel : !!b;
             togDiv.classed('selected', sel);
-            cbFnc(sel);
+            nocb || cbFnc(sel);
+        }
+
+        // toggle the button state without invoking the callback
+        function toggleNoCb() {
+            _toggle(undefined, true);
         }
 
         togDiv.on('click', _toggle);
@@ -104,7 +109,8 @@
             id: id,
             width: buttonWidth,
             selected: function () { return sel; },
-            toggle: _toggle
+            toggle: _toggle,
+            toggleNoCb: toggleNoCb
         }
     }
 
diff --git a/web/gui/src/main/webapp/app/view/topo/topo.js b/web/gui/src/main/webapp/app/view/topo/topo.js
index 5156fb0..bfcb15b 100644
--- a/web/gui/src/main/webapp/app/view/topo/topo.js
+++ b/web/gui/src/main/webapp/app/view/topo/topo.js
@@ -69,6 +69,8 @@
 
             esc: handleEscape,
 
+            _keyListener: ttbs.keyListener,
+
             _helpFormat: [
                 ['O', 'I', 'D', '-', 'H', 'M', 'P', 'B' ],
                 ['X', 'Z', 'L', 'U', 'R' ],
diff --git a/web/gui/src/main/webapp/app/view/topo/topoToolbar.js b/web/gui/src/main/webapp/app/view/topo/topoToolbar.js
index dc9c6db..da08d5f 100644
--- a/web/gui/src/main/webapp/app/view/topo/topoToolbar.js
+++ b/web/gui/src/main/webapp/app/view/topo/topoToolbar.js
@@ -16,73 +16,79 @@
 
 /*
  ONOS GUI -- Topology Toolbar Module.
- Defines functions for manipulating the toolbar.
+ Functions for creating and interacting with the toolbar.
  */
 
 (function () {
 
     // injected references
-    var $log, tbs;
+    var $log, tbs, api;
 
-    var api, toolbar;
+    // internal state
+    var toolbar, keyData;
 
-    // buttons
-    var togSummary, togInstances, togDetails,
-        togHosts, togOffline, togPorts, togBackground;
+    // key to button mapping data
+    var k2b = {
+        O: { id: 'summary-tog', gid: 'unknown', isel: true},
+        I: { id: 'instance-tog', gid: 'uiAttached', isel: true },
+        D: { id: 'details-tog', gid: 'unknown', isel: true },
+
+        H: { id: 'hosts-tog', gid: 'endstation', isel: false },
+        M: { id: 'offline-tog', gid: 'switch', isel: true },
+        P: { id: 'ports-tog', gid: 'unknown', isel: true },
+        B: { id: 'bkgrnd-tog', gid: 'unknown', isel: true }
+    };
 
     function init(_api_) {
         api = _api_;
     }
 
-    // TODO: fix toggle and radio sets to be selected based on the current state
-    // current bug: first toggle button, then toggle with key, toggle button doesn't update appearance
-
-
-    function getActions() {
-        togSummary = api.getActionEntry('O');
-        togInstances = api.getActionEntry('I');
-        togDetails = api.getActionEntry('D');
-
-        togHosts = api.getActionEntry('H');
-        togOffline = api.getActionEntry('M');
-        togPorts = api.getActionEntry('P');
-        togBackground = api.getActionEntry('B');
+    function initKeyData() {
+        keyData = d3.map(k2b);
+        keyData.forEach(function(key, value) {
+            var data = api.getActionEntry(key);
+            value.cb = data[0];     // on-click callback
+            value.tt = data[1];     // tooltip
+        });
     }
 
-    function entryCallback(entry) {
-        return entry[0];
-    }
-
-    function entryToolTip(entry) {
-        return entry[1];
+    function addToggle(key) {
+        var v = keyData.get(key);
+        v.tog = toolbar.addToggle(v.id, v.gid, v.isel, v.cb, v.tt);
     }
 
     function addFirstRow() {
-        toolbar.addToggle('summary-tog', 'unknown', true,
-            entryCallback(togSummary), entryToolTip(togSummary));
-        toolbar.addToggle('instance-tog', 'uiAttached', true,
-            entryCallback(togInstances), entryToolTip(togInstances));
-        toolbar.addToggle('details-tog', 'unknown', true,
-            entryCallback(togDetails), entryToolTip(togDetails));
+        addToggle('O');
+        addToggle('I');
+        addToggle('D');
         toolbar.addSeparator();
 
-        toolbar.addToggle('hosts-tog', 'endstation', false,
-            entryCallback(togHosts), entryToolTip(togHosts));
-        toolbar.addToggle('offline-tog', 'switch', true,
-            entryCallback(togOffline), entryToolTip(togOffline));
-        toolbar.addToggle('ports-tog', 'unknown', true,
-            entryCallback(togPorts), entryToolTip(togPorts));
-        toolbar.addToggle('bkgrnd-tog', 'unknown', true,
-            entryCallback(togBackground), entryToolTip(togBackground));
+        addToggle('H');
+        addToggle('M');
+        addToggle('P');
+        addToggle('B');
     }
 
     function createToolbar() {
-        getActions();
+        initKeyData();
         toolbar = tbs.createToolbar('topo-tbar');
         addFirstRow();
         toolbar.show();
     }
 
+    // allows us to ensure the button states track key strokes
+    function keyListener(key) {
+        var v = keyData.get(key);
+
+        if (v) {
+            // we have a valid button mapping
+            if (v.tog) {
+                // it's a toggle button
+                v.tog.toggleNoCb();
+            }
+        }
+    }
+
     angular.module('ovTopo')
         .factory('TopoToolbarService', ['$log', 'ToolbarService',
 
@@ -92,7 +98,8 @@
 
             return {
                 init: init,
-                createToolbar: createToolbar
+                createToolbar: createToolbar,
+                keyListener: keyListener
             };
         }]);
 }());
\ No newline at end of file