OLD | NEW |
| (Empty) |
1 // Copyright 2014 The Chromium Authors. All rights reserved. | |
2 // Use of this source code is governed by a BSD-style license that can be | |
3 // found in the LICENSE file. | |
4 | |
5 /** | |
6 * @fileoverview | |
7 * Class managing the host's available keyboard layouts, allowing the user to | |
8 * select one that matches the local layout, or auto-selecting based on the | |
9 * current locale. | |
10 */ | |
11 | |
12 'use strict'; | |
13 | |
14 /** @suppress {duplicate} */ | |
15 var remoting = remoting || {}; | |
16 | |
17 /** | |
18 * @param {remoting.ContextMenuAdapter} adapter | |
19 * @constructor | |
20 */ | |
21 remoting.KeyboardLayoutsMenu = function(adapter) { | |
22 /** @private {remoting.ContextMenuAdapter} */ | |
23 this.adapter_ = adapter; | |
24 /** @private {remoting.SubmenuManager} */ | |
25 this.submenuManager_ = new remoting.SubmenuManager( | |
26 adapter, | |
27 chrome.i18n.getMessage(/*i18n-content*/'KEYBOARD_LAYOUTS_SUBMENU_TITLE'), | |
28 true); | |
29 /** @private {string} */ | |
30 this.currentLayout_ = ''; | |
31 | |
32 /** @private {function(string, string)} */ | |
33 this.sendExtensionMessage_ = base.doNothing; | |
34 | |
35 adapter.addListener(this.onContextMenu_.bind(this)); | |
36 }; | |
37 | |
38 /** | |
39 * @param {Array<string>} layouts The keyboard layouts available on the host, | |
40 * for example en-US, de-DE | |
41 * @param {string} currentLayout The layout currently active on the host. | |
42 */ | |
43 remoting.KeyboardLayoutsMenu.prototype.setLayouts = | |
44 function(layouts, currentLayout) { | |
45 this.submenuManager_.removeAll(); | |
46 this.currentLayout_ = ''; | |
47 for (var i = 0; i < layouts.length; ++i) { | |
48 this.submenuManager_.add(this.makeMenuId_(layouts[i]), layouts[i]); | |
49 } | |
50 // Pick a suitable default layout. | |
51 this.getBestLayout_(layouts, currentLayout, | |
52 this.setLayout_.bind(this, false)); | |
53 }; | |
54 | |
55 /** @param {function(string, string)} callback */ | |
56 remoting.KeyboardLayoutsMenu.prototype.setExtensionMessageSender = | |
57 function(callback) { | |
58 this.sendExtensionMessage_ = callback; | |
59 }; | |
60 | |
61 /** | |
62 * Notify the host that a new keyboard layout has been selected. | |
63 * | |
64 * @param {boolean} saveToLocalStorage If true, save the specified layout to | |
65 * local storage. | |
66 * @param {string} layout The new keyboard layout. | |
67 * @private | |
68 */ | |
69 remoting.KeyboardLayoutsMenu.prototype.setLayout_ = | |
70 function(saveToLocalStorage, layout) { | |
71 if (this.currentLayout_ != '') { | |
72 this.adapter_.updateCheckState( | |
73 this.makeMenuId_(this.currentLayout_), false); | |
74 } | |
75 this.adapter_.updateCheckState(this.makeMenuId_(layout), true); | |
76 this.currentLayout_ = layout; | |
77 | |
78 console.log("Setting the keyboard layout to '" + layout + "'"); | |
79 this.sendExtensionMessage_('setKeyboardLayout', | |
80 JSON.stringify({layout: layout})); | |
81 if (saveToLocalStorage) { | |
82 var params = {}; | |
83 params[remoting.KeyboardLayoutsMenu.KEY_] = layout; | |
84 chrome.storage.local.set(params); | |
85 } | |
86 }; | |
87 | |
88 /** | |
89 * Choose the best keyboard from the alternatives, based on the following | |
90 * algorithm: | |
91 * - Search local storage by for a preferred keyboard layout for the app; | |
92 * if it is found, prefer it over the current locale, falling back on the | |
93 * latter only if no match is found. | |
94 * - If the candidate layout matches one of the supported layouts, use it. | |
95 * - Otherwise, if the language portion of the candidate matches that of | |
96 * any of the supported layouts, use the first such layout (e.g, en-AU | |
97 * will match either en-US or en-GB, whichever appears first). | |
98 * - Otherwise, use the host's current layout. | |
99 * | |
100 * @param {Array<string>} layouts | |
101 * @param {string} currentHostLayout | |
102 * @param {function(string):void} onDone | |
103 * @private | |
104 */ | |
105 remoting.KeyboardLayoutsMenu.prototype.getBestLayout_ = | |
106 function(layouts, currentHostLayout, onDone) { | |
107 /** | |
108 * Extract the language id from a string that is either "language" (e.g. | |
109 * "de") or "language-region" (e.g. "en-US"). | |
110 * | |
111 * @param {string} layout | |
112 * @return {string} | |
113 */ | |
114 var getLanguage = function(layout) { | |
115 var languageAndRegion = layout.split('-'); | |
116 switch (languageAndRegion.length) { | |
117 case 1: | |
118 case 2: | |
119 return languageAndRegion[0]; | |
120 default: | |
121 return ''; | |
122 } | |
123 }; | |
124 | |
125 /** @param {Object<string>} storage */ | |
126 var chooseLayout = function(storage) { | |
127 var configuredLayout = storage[remoting.KeyboardLayoutsMenu.KEY_]; | |
128 var tryLayouts = [ chrome.i18n.getUILanguage() ]; | |
129 if (configuredLayout && typeof(configuredLayout) == 'string') { | |
130 tryLayouts.unshift(configuredLayout); | |
131 } | |
132 for (var i = 0; i < tryLayouts.length; ++i) { | |
133 if (layouts.indexOf(tryLayouts[i]) != -1) { | |
134 onDone(tryLayouts[i]); | |
135 return; | |
136 } | |
137 var language = getLanguage(tryLayouts[i]); | |
138 if (language) { | |
139 for (var j = 0; j < layouts.length; ++j) { | |
140 if (language == getLanguage(layouts[j])) { | |
141 onDone(layouts[j]); | |
142 return; | |
143 } | |
144 } | |
145 } | |
146 } | |
147 // Neither the stored layout nor UI locale was suitable. | |
148 onDone(currentHostLayout); | |
149 }; | |
150 | |
151 chrome.storage.local.get(remoting.KeyboardLayoutsMenu.KEY_, chooseLayout); | |
152 }; | |
153 | |
154 /** | |
155 * Create a menu id from the given keyboard layout. | |
156 * | |
157 * @param {string} layout Keyboard layout | |
158 * @return {string} | |
159 * @private | |
160 */ | |
161 remoting.KeyboardLayoutsMenu.prototype.makeMenuId_ = function(layout) { | |
162 return 'layout@' + layout; | |
163 }; | |
164 | |
165 /** | |
166 * Handle a click on the application's context menu. | |
167 * | |
168 * @param {OnClickData=} info | |
169 * @private | |
170 */ | |
171 remoting.KeyboardLayoutsMenu.prototype.onContextMenu_ = function(info) { | |
172 var menuItemId = info.menuItemId.toString(); | |
173 var components = menuItemId.split('@'); | |
174 if (components.length == 2 && | |
175 this.makeMenuId_(components[1]) === menuItemId) { | |
176 this.setLayout_(true, components[1]); | |
177 } | |
178 }; | |
179 | |
180 /** | |
181 * @type {string} | |
182 * @private | |
183 */ | |
184 remoting.KeyboardLayoutsMenu.KEY_ = 'preferred-keyboard-layout'; | |
OLD | NEW |