| #!/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. |
| |
| '''The 'grit resize' tool. |
| ''' |
| |
| import getopt |
| import os |
| |
| from grit import grd_reader |
| from grit import pseudo |
| from grit import util |
| from grit.format import rc |
| from grit.format import rc_header |
| from grit.node import include |
| from grit.tool import interface |
| |
| |
| # Template for the .vcproj file, with a couple of [[REPLACEABLE]] parts. |
| PROJECT_TEMPLATE = '''\ |
| <?xml version="1.0" encoding="Windows-1252"?> |
| <VisualStudioProject |
| ProjectType="Visual C++" |
| Version="7.10" |
| Name="[[DIALOG_NAME]]" |
| ProjectGUID="[[PROJECT_GUID]]" |
| Keyword="Win32Proj"> |
| <Platforms> |
| <Platform |
| Name="Win32"/> |
| </Platforms> |
| <Configurations> |
| <Configuration |
| Name="Debug|Win32" |
| OutputDirectory="Debug" |
| IntermediateDirectory="Debug" |
| ConfigurationType="1" |
| CharacterSet="2"> |
| </Configuration> |
| </Configurations> |
| <References> |
| </References> |
| <Files> |
| <Filter |
| Name="Resource Files" |
| Filter="rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx" |
| UniqueIdentifier="{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}"> |
| <File |
| RelativePath=".\[[DIALOG_NAME]].rc"> |
| </File> |
| </Filter> |
| </Files> |
| <Globals> |
| </Globals> |
| </VisualStudioProject>''' |
| |
| |
| # Template for the .rc file with a couple of [[REPLACEABLE]] parts. |
| # TODO(joi) Improve this (and the resource.h template) to allow saving and then |
| # reopening of the RC file in Visual Studio. Currently you can only open it |
| # once and change it, then after you close it you won't be able to reopen it. |
| RC_TEMPLATE = '''\ |
| // This file is automatically generated by GRIT and intended for editing |
| // the layout of the dialogs contained in it. Do not edit anything but the |
| // dialogs. Any changes made to translateable portions of the dialogs will |
| // be ignored by GRIT. |
| |
| #include "resource.h" |
| #include <winresrc.h> |
| #ifdef IDC_STATIC |
| #undef IDC_STATIC |
| #endif |
| #define IDC_STATIC (-1) |
| |
| LANGUAGE LANG_NEUTRAL, SUBLANG_NEUTRAL |
| |
| #pragma code_page([[CODEPAGE_NUM]]) |
| |
| [[INCLUDES]] |
| |
| [[DIALOGS]] |
| ''' |
| |
| |
| # Template for the resource.h file with a couple of [[REPLACEABLE]] parts. |
| HEADER_TEMPLATE = '''\ |
| // This file is automatically generated by GRIT. Do not edit. |
| |
| #pragma once |
| |
| // Edit commands |
| #define ID_EDIT_CLEAR 0xE120 |
| #define ID_EDIT_CLEAR_ALL 0xE121 |
| #define ID_EDIT_COPY 0xE122 |
| #define ID_EDIT_CUT 0xE123 |
| #define ID_EDIT_FIND 0xE124 |
| #define ID_EDIT_PASTE 0xE125 |
| #define ID_EDIT_PASTE_LINK 0xE126 |
| #define ID_EDIT_PASTE_SPECIAL 0xE127 |
| #define ID_EDIT_REPEAT 0xE128 |
| #define ID_EDIT_REPLACE 0xE129 |
| #define ID_EDIT_SELECT_ALL 0xE12A |
| #define ID_EDIT_UNDO 0xE12B |
| #define ID_EDIT_REDO 0xE12C |
| |
| |
| [[DEFINES]] |
| ''' |
| |
| |
| class ResizeDialog(interface.Tool): |
| '''Generates an RC file, header and Visual Studio project that you can use |
| with Visual Studio's GUI resource editor to modify the layout of dialogs for |
| the language of your choice. You then use the RC file, after you resize the |
| dialog, for the language or languages of your choice, using the <skeleton> child |
| of the <structure> node for the dialog. The translateable bits of the dialog |
| will be ignored when you use the <skeleton> node (GRIT will instead use the |
| translateable bits from the original dialog) but the layout changes you make |
| will be used. Note that your layout changes must preserve the order of the |
| translateable elements in the RC file. |
| |
| Usage: grit resize [-f BASEFOLDER] [-l LANG] [-e RCENCODING] DIALOGID* |
| |
| Arguments: |
| DIALOGID The 'name' attribute of a dialog to output for resizing. Zero |
| or more of these parameters can be used. If none are |
| specified, all dialogs from the input .grd file are output. |
| |
| Options: |
| |
| -f BASEFOLDER The project will be created in a subfolder of BASEFOLDER. |
| The name of the subfolder will be the first DIALOGID you |
| specify. Defaults to '.' |
| |
| -l LANG Specifies that the RC file should contain a dialog translated |
| into the language LANG. The default is a cp1252-representable |
| pseudotranslation, because Visual Studio's GUI RC editor only |
| supports single-byte encodings. |
| |
| -c CODEPAGE Code page number to indicate to the RC compiler the encoding |
| of the RC file, default is something reasonable for the |
| language you selected (but this does not work for every single |
| language). See details on codepages below. NOTE that you do |
| not need to specify the codepage unless the tool complains |
| that it's not sure which codepage to use. See the following |
| page for codepage numbers supported by Windows: |
| http://www.microsoft.com/globaldev/reference/wincp.mspx |
| |
| -D NAME[=VAL] Specify a C-preprocessor-like define NAME with optional |
| value VAL (defaults to 1) which will be used to control |
| conditional inclusion of resources. |
| |
| |
| IMPORTANT NOTE: For now, the tool outputs a UTF-8 encoded file for any language |
| that can not be represented in cp1252 (i.e. anything other than Western |
| European languages). You will need to open this file in a text editor and |
| save it using the codepage indicated in the #pragma code_page(XXXX) command |
| near the top of the file, before you open it in Visual Studio. |
| |
| ''' |
| |
| # TODO(joi) It would be cool to have this tool note the Perforce revision |
| # of the original RC file somewhere, such that the <skeleton> node could warn |
| # if the original RC file gets updated without the skeleton file being updated. |
| |
| # TODO(joi) Would be cool to have option to add the files to Perforce |
| |
| def __init__(self): |
| self.lang = pseudo.PSEUDO_LANG |
| self.defines = {} |
| self.base_folder = '.' |
| self.codepage_number = 1252 |
| self.codepage_number_specified_explicitly = False |
| |
| def SetLanguage(self, lang): |
| '''Sets the language code to output things in. |
| ''' |
| self.lang = lang |
| if not self.codepage_number_specified_explicitly: |
| self.codepage_number = util.LanguageToCodepage(lang) |
| |
| def GetEncoding(self): |
| if self.codepage_number == 1200: |
| return 'utf_16' |
| if self.codepage_number == 65001: |
| return 'utf_8' |
| return 'cp%d' % self.codepage_number |
| |
| def ShortDescription(self): |
| return 'Generate a file where you can resize a given dialog.' |
| |
| def Run(self, opts, args): |
| self.SetOptions(opts) |
| |
| own_opts, args = getopt.getopt(args, 'l:f:c:D:') |
| for key, val in own_opts: |
| if key == '-l': |
| self.SetLanguage(val) |
| if key == '-f': |
| self.base_folder = val |
| if key == '-c': |
| self.codepage_number = int(val) |
| self.codepage_number_specified_explicitly = True |
| if key == '-D': |
| name, val = util.ParseDefine(val) |
| self.defines[name] = val |
| |
| res_tree = grd_reader.Parse(opts.input, debug=opts.extra_verbose) |
| res_tree.OnlyTheseTranslations([self.lang]) |
| res_tree.RunGatherers() |
| |
| # Dialog IDs are either explicitly listed, or we output all dialogs from the |
| # .grd file |
| dialog_ids = args |
| if not len(dialog_ids): |
| for node in res_tree: |
| if node.name == 'structure' and node.attrs['type'] == 'dialog': |
| dialog_ids.append(node.attrs['name']) |
| |
| self.Process(res_tree, dialog_ids) |
| |
| def Process(self, grd, dialog_ids): |
| '''Outputs an RC file and header file for the dialog 'dialog_id' stored in |
| resource tree 'grd', to self.base_folder, as discussed in this class's |
| documentation. |
| |
| Arguments: |
| grd: grd = grd_reader.Parse(...); grd.RunGatherers() |
| dialog_ids: ['IDD_MYDIALOG', 'IDD_OTHERDIALOG'] |
| ''' |
| grd.SetOutputLanguage(self.lang) |
| grd.SetDefines(self.defines) |
| |
| project_name = dialog_ids[0] |
| |
| dir_path = os.path.join(self.base_folder, project_name) |
| if not os.path.isdir(dir_path): |
| os.mkdir(dir_path) |
| |
| # If this fails then we're not on Windows (or you don't have the required |
| # win32all Python libraries installed), so what are you doing mucking |
| # about with RC files anyway? :) |
| import pythoncom |
| |
| # Create the .vcproj file |
| project_text = PROJECT_TEMPLATE.replace( |
| '[[PROJECT_GUID]]', str(pythoncom.CreateGuid()) |
| ).replace('[[DIALOG_NAME]]', project_name) |
| fname = os.path.join(dir_path, '%s.vcproj' % project_name) |
| self.WriteFile(fname, project_text) |
| print "Wrote %s" % fname |
| |
| # Create the .rc file |
| # Output all <include> nodes since the dialogs might depend on them (e.g. |
| # for icons and bitmaps). |
| include_items = [] |
| for node in grd.ActiveDescendants(): |
| if isinstance(node, include.IncludeNode): |
| include_items.append(rc.FormatInclude(node, self.lang, '.')) |
| rc_text = RC_TEMPLATE.replace('[[CODEPAGE_NUM]]', |
| str(self.codepage_number)) |
| rc_text = rc_text.replace('[[INCLUDES]]', ''.join(include_items)) |
| |
| # Then output the dialogs we have been asked to output. |
| dialogs = [] |
| for dialog_id in dialog_ids: |
| node = grd.GetNodeById(dialog_id) |
| assert node.name == 'structure' and node.attrs['type'] == 'dialog' |
| # TODO(joi) Add exception handling for better error reporting |
| dialogs.append(rc.FormatStructure(node, self.lang, '.')) |
| rc_text = rc_text.replace('[[DIALOGS]]', ''.join(dialogs)) |
| |
| fname = os.path.join(dir_path, '%s.rc' % project_name) |
| self.WriteFile(fname, rc_text, self.GetEncoding()) |
| print "Wrote %s" % fname |
| |
| # Create the resource.h file |
| header_defines = ''.join(rc_header.FormatDefines(grd)) |
| header_text = HEADER_TEMPLATE.replace('[[DEFINES]]', header_defines) |
| fname = os.path.join(dir_path, 'resource.h') |
| self.WriteFile(fname, header_text) |
| print "Wrote %s" % fname |
| |
| def WriteFile(self, filename, contents, encoding='cp1252'): |
| with open(filename, 'wb') as f: |
| writer = util.WrapOutputStream(f, encoding) |
| writer.write(contents) |