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 9import getopt 10import os 11 12from grit import grd_reader 13from grit import pseudo 14from grit import util 15from grit.format import rc 16from grit.format import rc_header 17from grit.node import include 18from grit.tool import interface 19 20 21# Template for the .vcproj file, with a couple of [[REPLACEABLE]] parts. 22PROJECT_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. 64RC_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 77LANGUAGE 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. 88HEADER_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 113class ResizeDialog(interface.Tool): 114 '''Generates an RC file, header and Visual Studio project that you can use 115with Visual Studio's GUI resource editor to modify the layout of dialogs for 116the language of your choice. You then use the RC file, after you resize the 117dialog, for the language or languages of your choice, using the <skeleton> child 118of the <structure> node for the dialog. The translateable bits of the dialog 119will be ignored when you use the <skeleton> node (GRIT will instead use the 120translateable bits from the original dialog) but the layout changes you make 121will be used. Note that your layout changes must preserve the order of the 122translateable elements in the RC file. 123 124Usage: grit resize [-f BASEFOLDER] [-l LANG] [-e RCENCODING] DIALOGID* 125 126Arguments: 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 131Options: 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 156IMPORTANT NOTE: For now, the tool outputs a UTF-8 encoded file for any language 157that can not be represented in cp1252 (i.e. anything other than Western 158European languages). You will need to open this file in a text editor and 159save it using the codepage indicated in the #pragma code_page(XXXX) command 160near 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) 290