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 |