diff options
Diffstat (limited to 'chromium/tools/gen_keyboard_overlay_data/gen_keyboard_overlay_data.py')
-rwxr-xr-x | chromium/tools/gen_keyboard_overlay_data/gen_keyboard_overlay_data.py | 529 |
1 files changed, 0 insertions, 529 deletions
diff --git a/chromium/tools/gen_keyboard_overlay_data/gen_keyboard_overlay_data.py b/chromium/tools/gen_keyboard_overlay_data/gen_keyboard_overlay_data.py deleted file mode 100755 index 8531241f280..00000000000 --- a/chromium/tools/gen_keyboard_overlay_data/gen_keyboard_overlay_data.py +++ /dev/null @@ -1,529 +0,0 @@ -#!/usr/bin/env python -# Copyright (c) 2012 The Chromium Authors. All rights reserved. -# Use of this source code is governed by a BSD-style license that can be -# found in the LICENSE file. - -"""Generate keyboard layout and hotkey data for the keyboard overlay. - -This script fetches data from the keyboard layout and hotkey data spreadsheet, -and output the data depending on the option. - - --cc: Rewrites a part of C++ code in - chrome/browser/chromeos/webui/keyboard_overlay_ui.cc - - --grd: Rewrites a part of grd messages in - chrome/app/generated_resources.grd - - --js: Rewrites the entire JavaScript code in - chrome/browser/resources/keyboard_overlay/keyboard_overlay_data.js - -These options can be specified at the same time. - -e.g. -python gen_keyboard_overlay_data.py --cc --grd --js - -The output directory of the generated files can be changed with --outdir. - -e.g. (This will generate tmp/keyboard_overlay.js) -python gen_keyboard_overlay_data.py --outdir=tmp --js -""" - -import cStringIO -import datetime -import gdata.spreadsheet.service -import getpass -import json -import optparse -import os -import re -import sys - -MODIFIER_SHIFT = 1 << 0 -MODIFIER_CTRL = 1 << 1 -MODIFIER_ALT = 1 << 2 - -KEYBOARD_GLYPH_SPREADSHEET_KEY = '0Ao3KldW9piwEdExLbGR6TmZ2RU9aUjFCMmVxWkVqVmc' -HOTKEY_SPREADSHEET_KEY = '0AqzoqbAMLyEPdE1RQXdodk1qVkFyTWtQbUxROVM1cXc' -CC_OUTDIR = 'chrome/browser/ui/webui/chromeos' -CC_FILENAME = 'keyboard_overlay_ui.cc' -GRD_OUTDIR = 'chrome/app' -GRD_FILENAME = 'chromeos_strings.grdp' -JS_OUTDIR = 'chrome/browser/resources/chromeos' -JS_FILENAME = 'keyboard_overlay_data.js' -CC_START = r'IDS_KEYBOARD_OVERLAY_INSTRUCTIONS_HIDE },' -CC_END = r'};' -GRD_START = r' <!-- BEGIN GENERATED KEYBOARD OVERLAY STRINGS -->' -GRD_END = r' <!-- END GENERATED KEYBOARD OVERLAY STRINGS -->' - -LABEL_MAP = { - 'glyph_arrow_down': 'down', - 'glyph_arrow_left': 'left', - 'glyph_arrow_right': 'right', - 'glyph_arrow_up': 'up', - 'glyph_back': 'back', - 'glyph_backspace': 'backspace', - 'glyph_brightness_down': 'bright down', - 'glyph_brightness_up': 'bright up', - 'glyph_enter': 'enter', - 'glyph_forward': 'forward', - 'glyph_fullscreen': 'full screen', - # Kana/Eisu key on Japanese keyboard - 'glyph_ime': u'\u304b\u306a\u0020\u002f\u0020\u82f1\u6570', - 'glyph_lock': 'lock', - 'glyph_overview': 'switch window', - 'glyph_power': 'power', - 'glyph_right': 'right', - 'glyph_reload': 'reload', - 'glyph_search': 'search', - 'glyph_shift': 'shift', - 'glyph_tab': 'tab', - 'glyph_tools': 'tools', - 'glyph_volume_down': 'vol. down', - 'glyph_volume_mute': 'mute', - 'glyph_volume_up': 'vol. up', -}; - -INPUT_METHOD_ID_TO_OVERLAY_ID = { - 'm17n:ar:kbd': 'ar', - 'm17n:fa:isiri': 'ar', - 'm17n:hi:itrans': 'hi', - 'm17n:th:kesmanee': 'th', - 'm17n:th:pattachote': 'th', - 'm17n:th:tis820': 'th', - 'm17n:vi:tcvn': 'vi', - 'm17n:vi:telex': 'vi', - 'm17n:vi:viqr': 'vi', - 'm17n:vi:vni': 'vi', - 'm17n:zh:cangjie': 'zh_TW', - 'm17n:zh:quick': 'zh_TW', - 'mozc': 'en_US', - 'mozc-chewing': 'zh_TW', - 'mozc-dv': 'en_US_dvorak', - 'mozc-hangul': 'ko', - 'mozc-jp': 'ja', - 'pinyin': 'zh_CN', - 'pinyin-dv': 'en_US_dvorak', - 'xkb:be::fra': 'fr', - 'xkb:be::ger': 'de', - 'xkb:be::nld': 'nl', - 'xkb:bg::bul': 'bg', - 'xkb:bg:phonetic:bul': 'bg', - 'xkb:br::por': 'pt_BR', - 'xkb:ca::fra': 'fr_CA', - 'xkb:ca:eng:eng': 'ca', - 'xkb:ch::ger': 'de', - 'xkb:ch:fr:fra': 'fr', - 'xkb:cz::cze': 'cs', - 'xkb:de::ger': 'de', - 'xkb:de:neo:ger': 'de_neo', - 'xkb:dk::dan': 'da', - 'xkb:ee::est': 'et', - 'xkb:es::spa': 'es', - 'xkb:es:cat:cat': 'ca', - 'xkb:fi::fin': 'fi', - 'xkb:fr::fra': 'fr', - 'xkb:gb:dvorak:eng': 'en_GB_dvorak', - 'xkb:gb:extd:eng': 'en_GB', - 'xkb:gr::gre': 'el', - 'xkb:hr::scr': 'hr', - 'xkb:hu::hun': 'hu', - 'xkb:il::heb': 'iw', - 'xkb:it::ita': 'it', - 'xkb:jp::jpn': 'ja', - 'xkb:kr:kr104:kor': 'ko', - 'xkb:latam::spa': 'es_419', - 'xkb:lt::lit': 'lt', - 'xkb:lv:apostrophe:lav': 'lv', - 'xkb:no::nob': 'no', - 'xkb:pl::pol': 'pl', - 'xkb:pt::por': 'pt_PT', - 'xkb:ro::rum': 'ro', - 'xkb:rs::srp': 'sr', - 'xkb:ru::rus': 'ru', - 'xkb:ru:phonetic:rus': 'ru', - 'xkb:se::swe': 'sv', - 'xkb:si::slv': 'sl', - 'xkb:sk::slo': 'sk', - 'xkb:tr::tur': 'tr', - 'xkb:ua::ukr': 'uk', - 'xkb:us::eng': 'en_US', - 'xkb:us:altgr-intl:eng': 'en_US_altgr_intl', - 'xkb:us:colemak:eng': 'en_US_colemak', - 'xkb:us:dvorak:eng': 'en_US_dvorak', - 'xkb:us:intl:eng': 'en_US_intl', - 'zinnia-japanese': 'ja', -} - -# The file was first generated in 2012 and we have a policy of not updating -# copyright dates. -COPYRIGHT_HEADER=\ -"""// Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -// This is a generated file but may contain local modifications. See -// src/tools/gen_keyboard_overlay_data/gen_keyboard_overlay_data.py --help -""" - -# A snippet for grd file -GRD_SNIPPET_TEMPLATE=""" <message name="%s" desc="%s"> - %s - </message> -""" - -# A snippet for C++ file -CC_SNIPPET_TEMPLATE=""" { "%s", %s }, -""" - - -def SplitBehavior(behavior): - """Splits the behavior to compose a message or i18n-content value. - - Examples: - 'Activate last tab' => ['Activate', 'last', 'tab'] - 'Close tab' => ['Close', 'tab'] - """ - return [x for x in re.split('[ ()"-.,]', behavior) if len(x) > 0] - - -def ToMessageName(behavior): - """Composes a message name for grd file. - - Examples: - 'Activate last tab' => IDS_KEYBOARD_OVERLAY_ACTIVATE_LAST_TAB - 'Close tab' => IDS_KEYBOARD_OVERLAY_CLOSE_TAB - """ - segments = [segment.upper() for segment in SplitBehavior(behavior)] - return 'IDS_KEYBOARD_OVERLAY_' + ('_'.join(segments)) - - -def ToMessageDesc(description): - """Composes a message description for grd file.""" - message_desc = 'The text in the keyboard overlay to explain the shortcut' - if description: - message_desc = '%s (%s).' % (message_desc, description) - else: - message_desc += '.' - return message_desc - - -def Toi18nContent(behavior): - """Composes a i18n-content value for HTML/JavaScript files. - - Examples: - 'Activate last tab' => keyboardOverlayActivateLastTab - 'Close tab' => keyboardOverlayCloseTab - """ - segments = [segment.lower() for segment in SplitBehavior(behavior)] - result = 'keyboardOverlay' - for segment in segments: - result += segment[0].upper() + segment[1:] - return result - - -def ToKeys(hotkey): - """Converts the action value to shortcut keys used from JavaScript. - - Examples: - 'Ctrl - 9' => '9<>CTRL' - 'Ctrl - Shift - Tab' => 'tab<>CTRL<>SHIFT' - """ - values = hotkey.split(' - ') - modifiers = sorted(value.upper() for value in values - if value in ['Shift', 'Ctrl', 'Alt', 'Search']) - keycode = [value.lower() for value in values - if value not in ['Shift', 'Ctrl', 'Alt', 'Search']] - # The keys which are highlighted even without modifier keys. - base_keys = ['backspace', 'power'] - if not modifiers and (keycode and keycode[0] not in base_keys): - return None - return '<>'.join(keycode + modifiers) - - -def ParseOptions(): - """Parses the input arguemnts and returns options.""" - # default_username = os.getusername() + '@google.com'; - default_username = '%s@google.com' % os.environ.get('USER') - parser = optparse.OptionParser() - parser.add_option('--key', dest='key', - help='The key of the spreadsheet (required).') - parser.add_option('--username', dest='username', - default=default_username, - help='Your user name (default: %s).' % default_username) - parser.add_option('--password', dest='password', - help='Your password.') - parser.add_option('--account_type', default='GOOGLE', dest='account_type', - help='Account type used for gdata login (default: GOOGLE)') - parser.add_option('--js', dest='js', default=False, action='store_true', - help='Output js file.') - parser.add_option('--grd', dest='grd', default=False, action='store_true', - help='Output resource file.') - parser.add_option('--cc', dest='cc', default=False, action='store_true', - help='Output cc file.') - parser.add_option('--outdir', dest='outdir', default=None, - help='Specify the directory files are generated.') - (options, unused_args) = parser.parse_args() - - if not options.username.endswith('google.com'): - print 'google.com account is necessary to use this script.' - sys.exit(-1) - - if (not (options.js or options.grd or options.cc)): - print 'Either --js, --grd, or --cc needs to be specified.' - sys.exit(-1) - - # Get the password from the terminal, if needed. - if not options.password: - options.password = getpass.getpass( - 'Application specific password for %s: ' % options.username) - return options - - -def InitClient(options): - """Initializes the spreadsheet client.""" - client = gdata.spreadsheet.service.SpreadsheetsService() - client.email = options.username - client.password = options.password - client.source = 'Spread Sheet' - client.account_type = options.account_type - print 'Logging in as %s (%s)' % (client.email, client.account_type) - client.ProgrammaticLogin() - return client - - -def PrintDiffs(message, lhs, rhs): - """Prints the differences between |lhs| and |rhs|.""" - dif = set(lhs).difference(rhs) - if dif: - print message, ', '.join(dif) - - -def FetchSpreadsheetFeeds(client, key, sheets, cols): - """Fetch feeds from the spreadsheet. - - Args: - client: A spreadsheet client to be used for fetching data. - key: A key string of the spreadsheet to be fetched. - sheets: A list of the sheet names to read data from. - cols: A list of columns to read data from. - """ - worksheets_feed = client.GetWorksheetsFeed(key) - print 'Fetching data from the worksheet: %s' % worksheets_feed.title.text - worksheets_data = {} - titles = [] - for entry in worksheets_feed.entry: - worksheet_id = entry.id.text.split('/')[-1] - list_feed = client.GetListFeed(key, worksheet_id) - list_data = [] - # Hack to deal with sheet names like 'sv (Copy of fl)' - title = list_feed.title.text.split('(')[0].strip() - titles.append(title) - if title not in sheets: - continue - print 'Reading data from the sheet: %s' % list_feed.title.text - for i, entry in enumerate(list_feed.entry): - line_data = {} - for k in entry.custom: - if (k not in cols) or (not entry.custom[k].text): - continue - line_data[k] = entry.custom[k].text - list_data.append(line_data) - worksheets_data[title] = list_data - PrintDiffs('Exist only on the spreadsheet: ', titles, sheets) - PrintDiffs('Specified but do not exist on the spreadsheet: ', sheets, titles) - return worksheets_data - - -def FetchKeyboardGlyphData(client): - """Fetches the keyboard glyph data from the spreadsheet.""" - glyph_cols = ['scancode', 'p0', 'p1', 'p2', 'p3', 'p4', 'p5', 'p6', 'p7', - 'p8', 'p9', 'label', 'format', 'notes'] - keyboard_glyph_data = FetchSpreadsheetFeeds( - client, KEYBOARD_GLYPH_SPREADSHEET_KEY, - INPUT_METHOD_ID_TO_OVERLAY_ID.values(), glyph_cols) - ret = {} - for lang in keyboard_glyph_data: - ret[lang] = {} - keys = {} - for line in keyboard_glyph_data[lang]: - scancode = line.get('scancode') - if (not scancode) and line.get('notes'): - ret[lang]['layoutName'] = line['notes'] - continue - del line['scancode'] - if 'notes' in line: - del line['notes'] - if 'label' in line: - line['label'] = LABEL_MAP.get(line['label'], line['label']) - keys[scancode] = line - # Add a label to space key - if '39' not in keys: - keys['39'] = {'label': 'space'} - ret[lang]['keys'] = keys - return ret - - -def FetchLayoutsData(client): - """Fetches the keyboard glyph data from the spreadsheet.""" - layout_names = ['U_layout', 'J_layout', 'E_layout', 'B_layout'] - cols = ['scancode', 'x', 'y', 'w', 'h'] - layouts = FetchSpreadsheetFeeds(client, KEYBOARD_GLYPH_SPREADSHEET_KEY, - layout_names, cols) - ret = {} - for layout_name, layout in layouts.items(): - ret[layout_name[0]] = [] - for row in layout: - line = [] - for col in cols: - value = row.get(col) - if not value: - line.append('') - else: - if col != 'scancode': - value = float(value) - line.append(value) - ret[layout_name[0]].append(line) - return ret - - -def FetchHotkeyData(client): - """Fetches the hotkey data from the spreadsheet.""" - hotkey_sheet = ['Cross Platform Behaviors'] - hotkey_cols = ['behavior', 'context', 'kind', 'actionctrlctrlcmdonmac', - 'chromeos', 'descriptionfortranslation'] - hotkey_data = FetchSpreadsheetFeeds(client, HOTKEY_SPREADSHEET_KEY, - hotkey_sheet, hotkey_cols) - action_to_id = {} - id_to_behavior = {} - # (behavior, action) - result = [] - for line in hotkey_data['Cross Platform Behaviors']: - if (not line.get('chromeos')) or (line.get('kind') != 'Key'): - continue - action = ToKeys(line['actionctrlctrlcmdonmac']) - if not action: - continue - behavior = line['behavior'].strip() - description = line.get('descriptionfortranslation') - result.append((behavior, action, description)) - return result - - -def UniqueBehaviors(hotkey_data): - """Retrieves a sorted list of unique behaviors from |hotkey_data|.""" - return sorted(set((behavior, description) for (behavior, _, description) - in hotkey_data), - cmp=lambda x, y: cmp(ToMessageName(x[0]), ToMessageName(y[0]))) - - -def GetPath(path_from_src): - """Returns the absolute path of the specified path.""" - path = os.path.join(os.path.dirname(__file__), '../..', path_from_src) - if not os.path.isfile(path): - print 'WARNING: %s does not exist. Maybe moved or renamed?' % path - return path - - -def OutputFile(outpath, snippet): - """Output the snippet into the specified path.""" - out = file(outpath, 'w') - out.write(COPYRIGHT_HEADER + '\n') - out.write(snippet) - print 'Output ' + os.path.normpath(outpath) - - -def RewriteFile(start, end, original_dir, original_filename, snippet, - outdir=None): - """Replaces a part of the specified file with snippet and outputs it.""" - original_path = GetPath(os.path.join(original_dir, original_filename)) - original = file(original_path, 'r') - original_content = original.read() - original.close() - if outdir: - outpath = os.path.join(outdir, original_filename) - else: - outpath = original_path - out = file(outpath, 'w') - rx = re.compile(r'%s\n.*?%s\n' % (re.escape(start), re.escape(end)), - re.DOTALL) - new_content = re.sub(rx, '%s\n%s%s\n' % (start, snippet, end), - original_content) - out.write(new_content) - out.close() - print 'Output ' + os.path.normpath(outpath) - - -def OutputJson(keyboard_glyph_data, hotkey_data, layouts, var_name, outdir): - """Outputs the keyboard overlay data as a JSON file.""" - action_to_id = {} - for (behavior, action, _) in hotkey_data: - i18nContent = Toi18nContent(behavior) - action_to_id[action] = i18nContent - data = {'keyboardGlyph': keyboard_glyph_data, - 'shortcut': action_to_id, - 'layouts': layouts, - 'inputMethodIdToOverlayId': INPUT_METHOD_ID_TO_OVERLAY_ID} - - if not outdir: - outdir = JS_OUTDIR - outpath = GetPath(os.path.join(outdir, JS_FILENAME)) - json_data = json.dumps(data, sort_keys=True, indent=2) - # Remove redundant spaces after ',' - json_data = json_data.replace(', \n', ',\n') - # Replace double quotes with single quotes to avoid lint warnings. - json_data = json_data.replace('\"', '\'') - snippet = 'var %s = %s;\n' % (var_name, json_data) - OutputFile(outpath, snippet) - - -def OutputGrd(hotkey_data, outdir): - """Outputs a part of messages in the grd file.""" - snippet = cStringIO.StringIO() - for (behavior, description) in UniqueBehaviors(hotkey_data): - # Do not generate message for 'Show wrench menu'. It is handled manually - # based on branding. - if behavior == 'Show wrench menu': - continue - snippet.write(GRD_SNIPPET_TEMPLATE % - (ToMessageName(behavior), ToMessageDesc(description), - behavior)) - - RewriteFile(GRD_START, GRD_END, GRD_OUTDIR, GRD_FILENAME, snippet.getvalue(), - outdir) - - -def OutputCC(hotkey_data, outdir): - """Outputs a part of code in the C++ file.""" - snippet = cStringIO.StringIO() - for (behavior, _) in UniqueBehaviors(hotkey_data): - message_name = ToMessageName(behavior) - output = CC_SNIPPET_TEMPLATE % (Toi18nContent(behavior), message_name) - # Break the line if the line is longer than 80 characters - if len(output) > 80: - output = output.replace(' ' + message_name, '\n %s' % message_name) - snippet.write(output) - - RewriteFile(CC_START, CC_END, CC_OUTDIR, CC_FILENAME, snippet.getvalue(), - outdir) - - -def main(): - options = ParseOptions() - client = InitClient(options) - hotkey_data = FetchHotkeyData(client) - - if options.js: - keyboard_glyph_data = FetchKeyboardGlyphData(client) - - if options.js: - layouts = FetchLayoutsData(client) - OutputJson(keyboard_glyph_data, hotkey_data, layouts, 'keyboardOverlayData', - options.outdir) - if options.grd: - OutputGrd(hotkey_data, options.outdir) - if options.cc: - OutputCC(hotkey_data, options.outdir) - - -if __name__ == '__main__': - main() |