| OLD | NEW |
| (Empty) |
| 1 #!/usr/bin/env python | |
| 2 # Copyright (c) 2012 The Chromium Authors. All rights reserved. | |
| 3 # Use of this source code is governed by a BSD-style license that can be | |
| 4 # found in the LICENSE file. | |
| 5 | |
| 6 '''The 'grit resize' tool. | |
| 7 ''' | |
| 8 | |
| 9 import getopt | |
| 10 import os | |
| 11 | |
| 12 from grit import grd_reader | |
| 13 from grit import pseudo | |
| 14 from grit import util | |
| 15 from grit.format import rc | |
| 16 from grit.format import rc_header | |
| 17 from grit.node import include | |
| 18 from grit.tool import interface | |
| 19 | |
| 20 | |
| 21 # Template for the .vcproj file, with a couple of [[REPLACEABLE]] parts. | |
| 22 PROJECT_TEMPLATE = '''\ | |
| 23 <?xml version="1.0" encoding="Windows-1252"?> | |
| 24 <VisualStudioProject | |
| 25 ProjectType="Visual C++" | |
| 26 Version="7.10" | |
| 27 Name="[[DIALOG_NAME]]" | |
| 28 ProjectGUID="[[PROJECT_GUID]]" | |
| 29 Keyword="Win32Proj"> | |
| 30 <Platforms> | |
| 31 <Platform | |
| 32 Name="Win32"/> | |
| 33 </Platforms> | |
| 34 <Configurations> | |
| 35 <Configuration | |
| 36 Name="Debug|Win32" | |
| 37 OutputDirectory="Debug" | |
| 38 IntermediateDirectory="Debug" | |
| 39 ConfigurationType="1" | |
| 40 CharacterSet="2"> | |
| 41 </Configuration> | |
| 42 </Configurations> | |
| 43 <References> | |
| 44 </References> | |
| 45 <Files> | |
| 46 <Filter | |
| 47 Name="Resource Files" | |
| 48 Filter="rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;
jpe;resx" | |
| 49 UniqueIdentifier="{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}
"> | |
| 50 <File | |
| 51 RelativePath=".\[[DIALOG_NAME]].rc"> | |
| 52 </File> | |
| 53 </Filter> | |
| 54 </Files> | |
| 55 <Globals> | |
| 56 </Globals> | |
| 57 </VisualStudioProject>''' | |
| 58 | |
| 59 | |
| 60 # Template for the .rc file with a couple of [[REPLACEABLE]] parts. | |
| 61 # TODO(joi) Improve this (and the resource.h template) to allow saving and then | |
| 62 # reopening of the RC file in Visual Studio. Currently you can only open it | |
| 63 # once and change it, then after you close it you won't be able to reopen it. | |
| 64 RC_TEMPLATE = '''\ | |
| 65 // This file is automatically generated by GRIT and intended for editing | |
| 66 // the layout of the dialogs contained in it. Do not edit anything but the | |
| 67 // dialogs. Any changes made to translateable portions of the dialogs will | |
| 68 // be ignored by GRIT. | |
| 69 | |
| 70 #include "resource.h" | |
| 71 #include <winresrc.h> | |
| 72 #ifdef IDC_STATIC | |
| 73 #undef IDC_STATIC | |
| 74 #endif | |
| 75 #define IDC_STATIC (-1) | |
| 76 | |
| 77 LANGUAGE LANG_NEUTRAL, SUBLANG_NEUTRAL | |
| 78 | |
| 79 #pragma code_page([[CODEPAGE_NUM]]) | |
| 80 | |
| 81 [[INCLUDES]] | |
| 82 | |
| 83 [[DIALOGS]] | |
| 84 ''' | |
| 85 | |
| 86 | |
| 87 # Template for the resource.h file with a couple of [[REPLACEABLE]] parts. | |
| 88 HEADER_TEMPLATE = '''\ | |
| 89 // This file is automatically generated by GRIT. Do not edit. | |
| 90 | |
| 91 #pragma once | |
| 92 | |
| 93 // Edit commands | |
| 94 #define ID_EDIT_CLEAR 0xE120 | |
| 95 #define ID_EDIT_CLEAR_ALL 0xE121 | |
| 96 #define ID_EDIT_COPY 0xE122 | |
| 97 #define ID_EDIT_CUT 0xE123 | |
| 98 #define ID_EDIT_FIND 0xE124 | |
| 99 #define ID_EDIT_PASTE 0xE125 | |
| 100 #define ID_EDIT_PASTE_LINK 0xE126 | |
| 101 #define ID_EDIT_PASTE_SPECIAL 0xE127 | |
| 102 #define ID_EDIT_REPEAT 0xE128 | |
| 103 #define ID_EDIT_REPLACE 0xE129 | |
| 104 #define ID_EDIT_SELECT_ALL 0xE12A | |
| 105 #define ID_EDIT_UNDO 0xE12B | |
| 106 #define ID_EDIT_REDO 0xE12C | |
| 107 | |
| 108 | |
| 109 [[DEFINES]] | |
| 110 ''' | |
| 111 | |
| 112 | |
| 113 class ResizeDialog(interface.Tool): | |
| 114 '''Generates an RC file, header and Visual Studio project that you can use | |
| 115 with Visual Studio's GUI resource editor to modify the layout of dialogs for | |
| 116 the language of your choice. You then use the RC file, after you resize the | |
| 117 dialog, for the language or languages of your choice, using the <skeleton> child | |
| 118 of the <structure> node for the dialog. The translateable bits of the dialog | |
| 119 will be ignored when you use the <skeleton> node (GRIT will instead use the | |
| 120 translateable bits from the original dialog) but the layout changes you make | |
| 121 will be used. Note that your layout changes must preserve the order of the | |
| 122 translateable elements in the RC file. | |
| 123 | |
| 124 Usage: grit resize [-f BASEFOLDER] [-l LANG] [-e RCENCODING] DIALOGID* | |
| 125 | |
| 126 Arguments: | |
| 127 DIALOGID The 'name' attribute of a dialog to output for resizing. Zero | |
| 128 or more of these parameters can be used. If none are | |
| 129 specified, all dialogs from the input .grd file are output. | |
| 130 | |
| 131 Options: | |
| 132 | |
| 133 -f BASEFOLDER The project will be created in a subfolder of BASEFOLDER. | |
| 134 The name of the subfolder will be the first DIALOGID you | |
| 135 specify. Defaults to '.' | |
| 136 | |
| 137 -l LANG Specifies that the RC file should contain a dialog translated | |
| 138 into the language LANG. The default is a cp1252-representable | |
| 139 pseudotranslation, because Visual Studio's GUI RC editor only | |
| 140 supports single-byte encodings. | |
| 141 | |
| 142 -c CODEPAGE Code page number to indicate to the RC compiler the encoding | |
| 143 of the RC file, default is something reasonable for the | |
| 144 language you selected (but this does not work for every single | |
| 145 language). See details on codepages below. NOTE that you do | |
| 146 not need to specify the codepage unless the tool complains | |
| 147 that it's not sure which codepage to use. See the following | |
| 148 page for codepage numbers supported by Windows: | |
| 149 http://www.microsoft.com/globaldev/reference/wincp.mspx | |
| 150 | |
| 151 -D NAME[=VAL] Specify a C-preprocessor-like define NAME with optional | |
| 152 value VAL (defaults to 1) which will be used to control | |
| 153 conditional inclusion of resources. | |
| 154 | |
| 155 | |
| 156 IMPORTANT NOTE: For now, the tool outputs a UTF-8 encoded file for any language | |
| 157 that can not be represented in cp1252 (i.e. anything other than Western | |
| 158 European languages). You will need to open this file in a text editor and | |
| 159 save it using the codepage indicated in the #pragma code_page(XXXX) command | |
| 160 near the top of the file, before you open it in Visual Studio. | |
| 161 | |
| 162 ''' | |
| 163 | |
| 164 # TODO(joi) It would be cool to have this tool note the Perforce revision | |
| 165 # of the original RC file somewhere, such that the <skeleton> node could warn | |
| 166 # if the original RC file gets updated without the skeleton file being updated
. | |
| 167 | |
| 168 # TODO(joi) Would be cool to have option to add the files to Perforce | |
| 169 | |
| 170 def __init__(self): | |
| 171 self.lang = pseudo.PSEUDO_LANG | |
| 172 self.defines = {} | |
| 173 self.base_folder = '.' | |
| 174 self.codepage_number = 1252 | |
| 175 self.codepage_number_specified_explicitly = False | |
| 176 | |
| 177 def SetLanguage(self, lang): | |
| 178 '''Sets the language code to output things in. | |
| 179 ''' | |
| 180 self.lang = lang | |
| 181 if not self.codepage_number_specified_explicitly: | |
| 182 self.codepage_number = util.LanguageToCodepage(lang) | |
| 183 | |
| 184 def GetEncoding(self): | |
| 185 if self.codepage_number == 1200: | |
| 186 return 'utf_16' | |
| 187 if self.codepage_number == 65001: | |
| 188 return 'utf_8' | |
| 189 return 'cp%d' % self.codepage_number | |
| 190 | |
| 191 def ShortDescription(self): | |
| 192 return 'Generate a file where you can resize a given dialog.' | |
| 193 | |
| 194 def Run(self, opts, args): | |
| 195 self.SetOptions(opts) | |
| 196 | |
| 197 own_opts, args = getopt.getopt(args, 'l:f:c:D:') | |
| 198 for key, val in own_opts: | |
| 199 if key == '-l': | |
| 200 self.SetLanguage(val) | |
| 201 if key == '-f': | |
| 202 self.base_folder = val | |
| 203 if key == '-c': | |
| 204 self.codepage_number = int(val) | |
| 205 self.codepage_number_specified_explicitly = True | |
| 206 if key == '-D': | |
| 207 name, val = util.ParseDefine(val) | |
| 208 self.defines[name] = val | |
| 209 | |
| 210 res_tree = grd_reader.Parse(opts.input, debug=opts.extra_verbose) | |
| 211 res_tree.OnlyTheseTranslations([self.lang]) | |
| 212 res_tree.RunGatherers() | |
| 213 | |
| 214 # Dialog IDs are either explicitly listed, or we output all dialogs from the | |
| 215 # .grd file | |
| 216 dialog_ids = args | |
| 217 if not len(dialog_ids): | |
| 218 for node in res_tree: | |
| 219 if node.name == 'structure' and node.attrs['type'] == 'dialog': | |
| 220 dialog_ids.append(node.attrs['name']) | |
| 221 | |
| 222 self.Process(res_tree, dialog_ids) | |
| 223 | |
| 224 def Process(self, grd, dialog_ids): | |
| 225 '''Outputs an RC file and header file for the dialog 'dialog_id' stored in | |
| 226 resource tree 'grd', to self.base_folder, as discussed in this class's | |
| 227 documentation. | |
| 228 | |
| 229 Arguments: | |
| 230 grd: grd = grd_reader.Parse(...); grd.RunGatherers() | |
| 231 dialog_ids: ['IDD_MYDIALOG', 'IDD_OTHERDIALOG'] | |
| 232 ''' | |
| 233 grd.SetOutputLanguage(self.lang) | |
| 234 grd.SetDefines(self.defines) | |
| 235 | |
| 236 project_name = dialog_ids[0] | |
| 237 | |
| 238 dir_path = os.path.join(self.base_folder, project_name) | |
| 239 if not os.path.isdir(dir_path): | |
| 240 os.mkdir(dir_path) | |
| 241 | |
| 242 # If this fails then we're not on Windows (or you don't have the required | |
| 243 # win32all Python libraries installed), so what are you doing mucking | |
| 244 # about with RC files anyway? :) | |
| 245 import pythoncom | |
| 246 | |
| 247 # Create the .vcproj file | |
| 248 project_text = PROJECT_TEMPLATE.replace( | |
| 249 '[[PROJECT_GUID]]', str(pythoncom.CreateGuid()) | |
| 250 ).replace('[[DIALOG_NAME]]', project_name) | |
| 251 fname = os.path.join(dir_path, '%s.vcproj' % project_name) | |
| 252 self.WriteFile(fname, project_text) | |
| 253 print "Wrote %s" % fname | |
| 254 | |
| 255 # Create the .rc file | |
| 256 # Output all <include> nodes since the dialogs might depend on them (e.g. | |
| 257 # for icons and bitmaps). | |
| 258 include_items = [] | |
| 259 for node in grd.ActiveDescendants(): | |
| 260 if isinstance(node, include.IncludeNode): | |
| 261 include_items.append(rc.FormatInclude(node, self.lang, '.')) | |
| 262 rc_text = RC_TEMPLATE.replace('[[CODEPAGE_NUM]]', | |
| 263 str(self.codepage_number)) | |
| 264 rc_text = rc_text.replace('[[INCLUDES]]', ''.join(include_items)) | |
| 265 | |
| 266 # Then output the dialogs we have been asked to output. | |
| 267 dialogs = [] | |
| 268 for dialog_id in dialog_ids: | |
| 269 node = grd.GetNodeById(dialog_id) | |
| 270 assert node.name == 'structure' and node.attrs['type'] == 'dialog' | |
| 271 # TODO(joi) Add exception handling for better error reporting | |
| 272 dialogs.append(rc.FormatStructure(node, self.lang, '.')) | |
| 273 rc_text = rc_text.replace('[[DIALOGS]]', ''.join(dialogs)) | |
| 274 | |
| 275 fname = os.path.join(dir_path, '%s.rc' % project_name) | |
| 276 self.WriteFile(fname, rc_text, self.GetEncoding()) | |
| 277 print "Wrote %s" % fname | |
| 278 | |
| 279 # Create the resource.h file | |
| 280 header_defines = ''.join(rc_header.FormatDefines(grd)) | |
| 281 header_text = HEADER_TEMPLATE.replace('[[DEFINES]]', header_defines) | |
| 282 fname = os.path.join(dir_path, 'resource.h') | |
| 283 self.WriteFile(fname, header_text) | |
| 284 print "Wrote %s" % fname | |
| 285 | |
| 286 def WriteFile(self, filename, contents, encoding='cp1252'): | |
| 287 with open(filename, 'wb') as f: | |
| 288 writer = util.WrapOutputStream(f, encoding) | |
| 289 writer.write(contents) | |
| OLD | NEW |