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.
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
14 /** @suppress {duplicate} */
15 var remoting = remoting || {};
18 * @param {remoting.ContextMenuAdapter} adapter
21 remoting.KeyboardLayoutsMenu = function(adapter) {
22 /** @private {remoting.ContextMenuAdapter} */
23 this.adapter_ = adapter;
24 /** @private {remoting.SubmenuManager} */
25 this.submenuManager_ = new remoting.SubmenuManager(
27 chrome.i18n.getMessage(/*i18n-content*/'KEYBOARD_LAYOUTS_SUBMENU_TITLE'),
29 /** @private {string} */
30 this.currentLayout_ = '';
32 /** @private {function(string, string)} */
33 this.sendExtensionMessage_ = base.doNothing;
35 adapter.addListener(this.onContextMenu_.bind(this));
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.
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]);
50 // Pick a suitable default layout.
51 this.getBestLayout_(layouts, currentLayout,
52 this.setLayout_.bind(this, false));
55 /** @param {function(string, string)} callback */
56 remoting.KeyboardLayoutsMenu.prototype.setExtensionMessageSender =
58 this.sendExtensionMessage_ = callback;
62 * Notify the host that a new keyboard layout has been selected.
64 * @param {boolean} saveToLocalStorage If true, save the specified layout to
66 * @param {string} layout The new keyboard layout.
69 remoting.KeyboardLayoutsMenu.prototype.setLayout_ =
70 function(saveToLocalStorage, layout) {
71 if (this.currentLayout_ != '') {
72 this.adapter_.updateCheckState(
73 this.makeMenuId_(this.currentLayout_), false);
75 this.adapter_.updateCheckState(this.makeMenuId_(layout), true);
76 this.currentLayout_ = layout;
78 console.log("Setting the keyboard layout to '" + layout + "'");
79 this.sendExtensionMessage_('setKeyboardLayout',
80 JSON.stringify({layout: layout}));
81 if (saveToLocalStorage) {
83 params[remoting.KeyboardLayoutsMenu.KEY_] = layout;
84 chrome.storage.local.set(params);
89 * Choose the best keyboard from the alternatives, based on the following
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.
100 * @param {Array<string>} layouts
101 * @param {string} currentHostLayout
102 * @param {function(string):void} onDone
105 remoting.KeyboardLayoutsMenu.prototype.getBestLayout_ =
106 function(layouts, currentHostLayout, onDone) {
108 * Extract the language id from a string that is either "language" (e.g.
109 * "de") or "language-region" (e.g. "en-US").
111 * @param {string} layout
114 var getLanguage = function(layout) {
115 var languageAndRegion = layout.split('-');
116 switch (languageAndRegion.length) {
119 return languageAndRegion[0];
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);
132 for (var i = 0; i < tryLayouts.length; ++i) {
133 if (layouts.indexOf(tryLayouts[i]) != -1) {
134 onDone(tryLayouts[i]);
137 var language = getLanguage(tryLayouts[i]);
139 for (var j = 0; j < layouts.length; ++j) {
140 if (language == getLanguage(layouts[j])) {
147 // Neither the stored layout nor UI locale was suitable.
148 onDone(currentHostLayout);
151 chrome.storage.local.get(remoting.KeyboardLayoutsMenu.KEY_, chooseLayout);
155 * Create a menu id from the given keyboard layout.
157 * @param {string} layout Keyboard layout
161 remoting.KeyboardLayoutsMenu.prototype.makeMenuId_ = function(layout) {
162 return 'layout@' + layout;
166 * Handle a click on the application's context menu.
168 * @param {OnClickData=} info
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]);
184 remoting.KeyboardLayoutsMenu.KEY_ = 'preferred-keyboard-layout';