blob: 5ff4f7f4fbf9c6f5c67e7c8b93ff557114dadce1 [file] [log] [blame]
Simon Hunt1eecfa22014-12-16 14:46:29 -08001/*
Simon Hunt8ead3a22015-01-06 11:00:15 -08002 * Copyright 2014,2015 Open Networking Laboratory
Simon Hunt1eecfa22014-12-16 14:46:29 -08003 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17/*
Simon Huntdc6362a2014-12-18 19:55:23 -080018 ONOS GUI -- Util -- Key Handler Service
Simon Hunt1eecfa22014-12-16 14:46:29 -080019 */
Simon Huntdc6362a2014-12-18 19:55:23 -080020(function () {
Simon Hunt1eecfa22014-12-16 14:46:29 -080021 'use strict';
22
23 // references to injected services
Simon Hunt9d286562015-03-09 13:53:50 -070024 var $log, fs, ts, ns, qhs;
Simon Hunt1eecfa22014-12-16 14:46:29 -080025
26 // internal state
Simon Hunt36fc15c2015-02-12 17:02:58 -080027 var enabled = true,
Simon Huntd5579252015-10-06 15:09:14 -070028 globalEnabled = true,
Simon Hunt36fc15c2015-02-12 17:02:58 -080029 keyHandler = {
Simon Hunt1eecfa22014-12-16 14:46:29 -080030 globalKeys: {},
31 maskedKeys: {},
32 viewKeys: {},
33 viewFn: null,
34 viewGestures: []
Yuta HIGUCHI4f39bcd2014-12-18 20:46:14 -080035 };
Simon Hunt1eecfa22014-12-16 14:46:29 -080036
Simon Hunt1eecfa22014-12-16 14:46:29 -080037 function whatKey(code) {
38 switch (code) {
39 case 13: return 'enter';
40 case 16: return 'shift';
41 case 17: return 'ctrl';
42 case 18: return 'alt';
43 case 27: return 'esc';
44 case 32: return 'space';
45 case 37: return 'leftArrow';
46 case 38: return 'upArrow';
47 case 39: return 'rightArrow';
48 case 40: return 'downArrow';
49 case 91: return 'cmdLeft';
50 case 93: return 'cmdRight';
51 case 187: return 'equals';
Simon Hunt90dcc3e2015-03-25 15:01:27 -070052 case 188: return 'comma';
Simon Hunt1eecfa22014-12-16 14:46:29 -080053 case 189: return 'dash';
Simon Hunt90dcc3e2015-03-25 15:01:27 -070054 case 190: return 'dot';
Simon Hunt1eecfa22014-12-16 14:46:29 -080055 case 191: return 'slash';
56 case 192: return 'backQuote';
57 case 220: return 'backSlash';
58 default:
59 if ((code >= 48 && code <= 57) ||
60 (code >= 65 && code <= 90)) {
61 return String.fromCharCode(code);
62 } else if (code >= 112 && code <= 123) {
63 return 'F' + (code - 111);
64 }
65 return '.';
66 }
67 }
68
69 function keyIn() {
70 var event = d3.event,
71 keyCode = event.keyCode,
72 key = whatKey(keyCode),
73 kh = keyHandler,
74 gk = kh.globalKeys[key],
Yuta HIGUCHI4f39bcd2014-12-18 20:46:14 -080075 gcb = fs.isF(gk) || (fs.isA(gk) && fs.isF(gk[0])),
Simon Hunt1eecfa22014-12-16 14:46:29 -080076 vk = kh.viewKeys[key],
Simon Hunt09060142015-03-18 20:23:32 -070077 kl = fs.isF(kh.viewKeys._keyListener),
Yuta HIGUCHI4f39bcd2014-12-18 20:46:14 -080078 vcb = fs.isF(vk) || (fs.isA(vk) && fs.isF(vk[0])) || fs.isF(kh.viewFn),
Simon Hunt1bf1e8c2015-04-08 10:12:43 -070079 token = 'keyev'; // indicate this was a key-pressed event
Simon Hunt1eecfa22014-12-16 14:46:29 -080080
Simon Huntdc6362a2014-12-18 19:55:23 -080081 d3.event.stopPropagation();
82
Simon Hunt36fc15c2015-02-12 17:02:58 -080083 if (enabled) {
84 // global callback?
85 if (gcb && gcb(token, key, keyCode, event)) {
86 // if the event was 'handled', we are done
87 return;
88 }
89 // otherwise, let the view callback have a shot
90 if (vcb) {
91 vcb(token, key, keyCode, event);
92 }
Simon Hunt09060142015-03-18 20:23:32 -070093 if (kl) {
94 kl(key);
95 }
Simon Hunt1eecfa22014-12-16 14:46:29 -080096 }
97 }
98
99 function setupGlobalKeys() {
Simon Hunt404f6b22015-01-21 14:00:56 -0800100 angular.extend(keyHandler, {
Simon Hunt1eecfa22014-12-16 14:46:29 -0800101 globalKeys: {
102 backSlash: [quickHelp, 'Show / hide Quick Help'],
103 slash: [quickHelp, 'Show / hide Quick Help'],
104 esc: [escapeKey, 'Dismiss dialog or cancel selections'],
105 T: [toggleTheme, "Toggle theme"]
106 },
107 globalFormat: ['backSlash', 'slash', 'esc', 'T'],
108
109 // Masked keys are global key handlers that always return true.
110 // That is, the view will never see the event for that key.
111 maskedKeys: {
112 slash: true,
113 backSlash: true,
114 T: true
115 }
116 });
117 }
118
119 function quickHelp(view, key, code, ev) {
Simon Huntd5579252015-10-06 15:09:14 -0700120 if (!globalEnabled) {
121 return false;
122 }
Simon Hunt639dc662015-02-18 14:19:20 -0800123 qhs.showQuickHelp(keyHandler);
Simon Hunt1eecfa22014-12-16 14:46:29 -0800124 return true;
125 }
126
Simon Hunta0eb0a82015-02-11 12:30:06 -0800127 // returns true if we 'consumed' the ESC keypress, false otherwise
Simon Hunt1eecfa22014-12-16 14:46:29 -0800128 function escapeKey(view, key, code, ev) {
Simon Hunt9d286562015-03-09 13:53:50 -0700129 return ns.hideIfShown() || qhs.hideQuickHelp();
Simon Hunt1eecfa22014-12-16 14:46:29 -0800130 }
131
132 function toggleTheme(view, key, code, ev) {
Simon Huntd5579252015-10-06 15:09:14 -0700133 if (!globalEnabled) {
134 return false;
135 }
Yuta HIGUCHI4f39bcd2014-12-18 20:46:14 -0800136 ts.toggleTheme();
Simon Hunt1eecfa22014-12-16 14:46:29 -0800137 return true;
138 }
139
Simon Hunt59df0b22014-12-17 10:32:25 -0800140 function setKeyBindings(keyArg) {
141 var viewKeys,
142 masked = [];
143
Yuta HIGUCHI4f39bcd2014-12-18 20:46:14 -0800144 if (fs.isF(keyArg)) {
Simon Hunt59df0b22014-12-17 10:32:25 -0800145 // set general key handler callback
146 keyHandler.viewFn = keyArg;
147 } else {
148 // set specific key filter map
149 viewKeys = d3.map(keyArg).keys();
150 viewKeys.forEach(function (key) {
151 if (keyHandler.maskedKeys[key]) {
Simon Huntaf322072014-12-18 13:23:40 -0800152 masked.push('setKeyBindings(): Key "' + key + '" is reserved');
Simon Hunt59df0b22014-12-17 10:32:25 -0800153 }
154 });
155
156 if (masked.length) {
Simon Huntaf322072014-12-18 13:23:40 -0800157 $log.warn(masked.join('\n'));
Simon Hunt59df0b22014-12-17 10:32:25 -0800158 }
159 keyHandler.viewKeys = keyArg;
160 }
161 }
162
163 function getKeyBindings() {
164 var gkeys = d3.map(keyHandler.globalKeys).keys(),
165 masked = d3.map(keyHandler.maskedKeys).keys(),
166 vkeys = d3.map(keyHandler.viewKeys).keys(),
Yuta HIGUCHI4f39bcd2014-12-18 20:46:14 -0800167 vfn = !!fs.isF(keyHandler.viewFn);
Simon Hunt59df0b22014-12-17 10:32:25 -0800168
169 return {
170 globalKeys: gkeys,
171 maskedKeys: masked,
172 viewKeys: vkeys,
173 viewFunction: vfn
174 };
175 }
176
Bri Prebilic Cole9dcaea52015-07-21 14:39:48 -0700177 function unbindKeys() {
178 keyHandler.viewKeys = {};
179 keyHandler.viewFn = null;
180 keyHandler.viewGestures = [];
181 }
182
Simon Hunt71892222015-09-29 13:39:40 -0700183 function checkNotGlobal(o) {
184 var oops = [];
185 if (fs.isO(o)) {
186 angular.forEach(o, function (val, key) {
187 if (keyHandler.globalKeys[key]) {
188 oops.push(key);
189 }
190 });
191 if (oops.length) {
192 $log.warn('Ignoring reserved global key(s):', oops.join(','));
193 oops.forEach(function (key) {
194 delete o[key];
195 });
196 }
197 }
198 }
199
Simon Huntdc6362a2014-12-18 19:55:23 -0800200 angular.module('onosUtil')
Simon Hunt639dc662015-02-18 14:19:20 -0800201 .factory('KeyService',
Simon Hunt9d286562015-03-09 13:53:50 -0700202 ['$log', 'FnService', 'ThemeService', 'NavService',
Simon Hunt639dc662015-02-18 14:19:20 -0800203
Simon Hunt9d286562015-03-09 13:53:50 -0700204 function (_$log_, _fs_, _ts_, _ns_) {
Yuta HIGUCHI4f39bcd2014-12-18 20:46:14 -0800205 $log = _$log_;
206 fs = _fs_;
207 ts = _ts_;
Simon Hunt9d286562015-03-09 13:53:50 -0700208 ns = _ns_;
Yuta HIGUCHI4f39bcd2014-12-18 20:46:14 -0800209
Simon Huntdc6362a2014-12-18 19:55:23 -0800210 return {
Simon Hunt639dc662015-02-18 14:19:20 -0800211 bindQhs: function (_qhs_) {
212 qhs = _qhs_;
213 },
Simon Huntdc6362a2014-12-18 19:55:23 -0800214 installOn: function (elem) {
215 elem.on('keydown', keyIn);
216 setupGlobalKeys();
217 },
Simon Huntdc6362a2014-12-18 19:55:23 -0800218 keyBindings: function (x) {
219 if (x === undefined) {
220 return getKeyBindings();
221 } else {
222 setKeyBindings(x);
223 }
224 },
Bri Prebilic Cole9dcaea52015-07-21 14:39:48 -0700225 unbindKeys: unbindKeys,
Simon Huntdc6362a2014-12-18 19:55:23 -0800226 gestureNotes: function (g) {
227 if (g === undefined) {
228 return keyHandler.viewGestures;
229 } else {
Yuta HIGUCHI4f39bcd2014-12-18 20:46:14 -0800230 keyHandler.viewGestures = fs.isA(g) || [];
Simon Huntdc6362a2014-12-18 19:55:23 -0800231 }
Simon Hunt36fc15c2015-02-12 17:02:58 -0800232 },
233 enableKeys: function (b) {
234 enabled = b;
Simon Hunt71892222015-09-29 13:39:40 -0700235 },
Simon Huntd5579252015-10-06 15:09:14 -0700236 enableGlobalKeys: function (b) {
237 globalEnabled = b;
238 },
Simon Hunt71892222015-09-29 13:39:40 -0700239 checkNotGlobal: checkNotGlobal
Simon Huntdc6362a2014-12-18 19:55:23 -0800240 };
Simon Hunt1eecfa22014-12-16 14:46:29 -0800241 }]);
242
Simon Huntdc6362a2014-12-18 19:55:23 -0800243}());