# Windows dialog .RC file parser, by Adam Walker.
# This module is part of the spambayes project, which is Copyright 2003
# The Python Software Foundation and is covered by the Python Software
# Foundation license.
__author__="Adam Walker"
import sys, os, shlex
import win32con
#import win32gui
import commctrl
_controlMap = {"DEFPUSHBUTTON":0x80,
"PUSHBUTTON":0x80,
"Button":0x80,
"GROUPBOX":0x80,
"Static":0x82,
"CTEXT":0x82,
"RTEXT":0x82,
"LTEXT":0x82,
"LISTBOX":0x83,
"SCROLLBAR":0x84,
"COMBOBOX":0x85,
"EDITTEXT":0x81,
}
_addDefaults = {"EDITTEXT":win32con.WS_BORDER,
"GROUPBOX":win32con.BS_GROUPBOX,
"LTEXT":win32con.SS_LEFT,
"DEFPUSHBUTTON":win32con.BS_DEFPUSHBUTTON,
"CTEXT":win32con.SS_CENTER,
"RTEXT":win32con.SS_RIGHT}
defaultControlStyle = win32con.WS_CHILD | win32con.WS_VISIBLE
class DialogDef:
name = ""
id = 0
style = 0
styleEx = None
caption = ""
font = "MS Sans Serif"
fontSize = 8
x = 0
y = 0
w = 0
h = 0
template = None
def __init__(self, n, i):
self.name = n
self.id = i
self.styles = []
self.stylesEx = []
self.controls = []
#print "dialog def for ",self.name, self.id
def createDialogTemplate(self):
t = None
self.template = [[self.caption, (self.x,self.y,self.w,self.h), self.style, self.styleEx, (self.fontSize, self.font)]]
# Add the controls
for control in self.controls:
self.template.append(control.createDialogTemplate())
return self.template
class ControlDef:
id = ""
controlType = ""
subType = ""
idNum = 0
style = defaultControlStyle
label = ""
x = 0
y = 0
w = 0
h = 0
def __init__(self):
self.styles = []
def toString(self):
s = "<Control id:"+self.id+" controlType:"+self.controlType+" subType:"+self.subType\
+" idNum:"+str(self.idNum)+" style:"+str(self.style)+" styles:"+str(self.styles)+" label:"+self.label\
+" x:"+str(self.x)+" y:"+str(self.y)+" w:"+str(self.w)+" h:"+str(self.h)+">"
return s
def createDialogTemplate(self):
ct = self.controlType
if "CONTROL"==ct:
ct = self.subType
if ct in _addDefaults:
self.style |= _addDefaults[ct]
if ct in _controlMap:
ct = _controlMap[ct]
t = [ct, self.label, self.idNum, (self.x, self.y, self.w, self.h), self.style]
#print t
return t
class RCParser:
next_id = 1001
dialogs = {}
_dialogs = {}
debugEnabled = False;
token = ""
def __init__(self):
self.ids = {"IDOK":1, "IDCANCEL":2, "IDC_STATIC": -1}
self.names = {1:"IDOK", 2:"IDCANCEL", -1:"IDC_STATIC"}
self.bitmaps = {}
def debug(self, *args):
if self.debugEnabled:
print args
def getToken(self):
self.token = self.lex.get_token()
self.debug("getToken returns:", self.token)
if self.token=="":
self.token = None
return self.token
def getCommaToken(self):
tok = self.getToken()
assert tok == ",", "Token '%s' should be a comma!" % tok
def loadDialogs(self, rcFileName):
"""
RCParser.loadDialogs(rcFileName) -> None
Load the dialog information into the parser. Dialog Definations can then be accessed
using the "dialogs" dictionary member (name->DialogDef). The "ids" member contains the dictionary of id->name.
The "names" member contains the dictionary of name->id
"""
hFileName = rcFileName[:-2]+"h"
try:
h = open(hFileName, "rU")
self.parseH(h)
h.close()
except OSError:
print "No .h file. ignoring."
f = open(rcFileName)
self.open(f)
self.getToken()
while self.token!=None:
self.parse()
self.getToken()
f.close()
def open(self, file):
self.lex = shlex.shlex(file)
self.lex.commenters = "//#"
def parseH(self, file):
lex = shlex.shlex(file)
lex.commenters = "//"
token = " "
while token is not None:
token = lex.get_token()
if token == "" or token is None:
token = None
else:
if token=='define':
n = lex.get_token()
i = int(lex.get_token())
self.ids[n] = i
if self.names.has_key(i):
# ignore AppStudio special ones.
if not n.startswith("_APS_"):
print "Duplicate id",i,"for",n,"is", self.names[i]
else:
self.names[i] = n
if self.next_id<=i:
self.next_id = i+1
def parse(self):
deep = 0
if self.token == None:
more == None
elif "BEGIN" == self.token:
deep = 1
while deep!=0:
self.getToken()
if "BEGIN" == self.token:
deep += 1
elif "END" == self.token:
deep -= 1
elif "IDD_" == self.token[:4]:
possibleDlgName = self.token
#print "possible dialog:", possibleDlgName
self.getToken()
if "DIALOG" == self.token or "DIALOGEX" == self.token:
self.dialog(possibleDlgName)
elif "IDB_" == self.token[:4]:
possibleBitmap = self.token
self.getToken()
if "BITMAP" == self.token:
self.getToken()
if self.token=="MOVEABLE":
self.getToken() # PURE
self.getToken() # bmpname
bmf = self.token[1:-1] # quotes
self.bitmaps[possibleBitmap] = bmf
print "BITMAP", possibleBitmap, bmf
#print win32gui.LoadImage(0, bmf, win32con.IMAGE_BITMAP,0,0,win32con.LR_DEFAULTCOLOR|win32con.LR_LOADFROMFILE)
def addId(self, id_name):
if id_name in self.ids:
id = self.ids[id_name]
else:
id = self.next_id
self.next_id += 1
self.ids[id_name] = id
self.names[id] = id_name
return id
def lang(self):
while self.token[0:4]=="LANG" or self.token[0:7]=="SUBLANG" or self.token==',':
self.getToken();
def dialog(self, name):
dlg = DialogDef(name,self.addId(name))
assert len(dlg.controls)==0
self._dialogs[name] = dlg
extras = []
self.getToken()
while not self.token.isdigit():
self.debug("extra", self.token)
extras.append(self.token)
self.getToken()
dlg.x = int(self.token)
self.getCommaToken()
self.getToken() # number
dlg.y = int(self.token)
self.getCommaToken()
self.getToken() # number
dlg.w = int(self.token)
self.getCommaToken()
self.getToken() # number
dlg.h = int(self.token)
self.getToken()
while not (self.token==None or self.token=="" or self.token=="END"):
if self.token=="STYLE":
self.dialogStyle(dlg)
elif self.token=="EXSTYLE":
self.dialogExStyle(dlg)
elif self.token=="CAPTION":
self.dialogCaption(dlg)
elif self.token=="FONT":
self.dialogFont(dlg)
elif self.token=="BEGIN":
self.controls(dlg)
else:
break
self.dialogs[name] = dlg.createDialogTemplate()
def dialogStyle(self, dlg):
dlg.style, dlg.styles = self.styles( [], win32con.WS_VISIBLE | win32con.DS_SETFONT)
def dialogExStyle(self, dlg):
self.getToken()
dlg.styleEx, dlg.stylesEx = self.styles( [], 0)
def styles(self, defaults, defaultStyle):
list = defaults
style = defaultStyle
if "STYLE"==self.token:
self.getToken()
i = 0
Not = False
while ((i%2==1 and ("|"==self.token or "NOT"==self.token)) or (i%2==0)) and not self.token==None:
Not = False;
if "NOT"==self.token:
Not = True
self.getToken()
i += 1
if self.token!="|":
if self.token in win32con.__dict__:
value = getattr(win32con,self.token)
else:
if self.token in commctrl.__dict__:
value = getattr(commctrl,self.token)
else:
value = 0
if Not:
list.append("NOT "+self.token)
self.debug("styles add Not",self.token, value)
style &= ~value
else:
list.append(self.token)
self.debug("styles add", self.token, value)
style |= value
self.getToken()
self.debug("style is ",style)
return style, list
def dialogCaption(self, dlg):
if "CAPTION"==self.token:
self.getToken()
self.token = self.token[1:-1]
self.debug("Caption is:",self.token)
dlg.caption = self.token
self.getToken()
def dialogFont(self, dlg):
if "FONT"==self.token:
self.getToken()
dlg.fontSize = int(self.token)
self.getCommaToken()
self.getToken() # Font name
dlg.font = self.token[1:-1] # it's quoted
self.getToken()
while "BEGIN"!=self.token:
self.getToken()
def controls(self, dlg):
if self.token=="BEGIN": self.getToken()
while self.token!="END":
control = ControlDef()
control.controlType = self.token;
#print self.token
self.getToken()
if self.token[0:1]=='"':
control.label = self.token[1:-1]
self.getCommaToken()
self.getToken()
elif self.token.isdigit():
control.label = self.token
self.getCommaToken()
self.getToken()
# msvc seems to occasionally replace "IDC_STATIC" with -1
if self.token=='-':
if self.getToken() != '1':
raise RuntimeError, \
"Negative literal in rc script (other than -1) - don't know what to do"
self.token = "IDC_STATIC"
control.id = self.token
control.idNum = self.addId(control.id)
self.getCommaToken()
if control.controlType == "CONTROL":
self.getToken()
control.subType = self.token[1:-1]
# Styles
self.getCommaToken()
self.getToken()
control.style, control.styles = self.styles([], defaultControlStyle)
#self.getToken() #,
# Rect
control.x = int(self.getToken())
self.getCommaToken()
control.y = int(self.getToken())
self.getCommaToken()
control.w = int(self.getToken())
self.getCommaToken()
self.getToken()
control.h = int(self.token)
self.getToken()
if self.token==",":
self.getToken()
control.style, control.styles = self.styles([], defaultControlStyle)
#print control.toString()
dlg.controls.append(control)
def ParseDialogs(rc_file):
rcp = RCParser()
try:
rcp.loadDialogs(rc_file)
except:
lex = getattr(rcp, "lex", None)
if lex:
print "ERROR parsing dialogs at line", lex.lineno
print "Next 10 tokens are:"
for i in range(10):
print lex.get_token(),
print
raise
return rcp
if __name__=='__main__':
rc_file = os.path.join(os.path.dirname(__file__), "dialogs.rc")
d = ParseDialogs(rc_file)
import pprint
for id, ddef in d.dialogs.items():
print "Dialog %s (%d controls)" % (id, len(ddef))
pprint.pprint(ddef)
print
syntax highlighted by Code2HTML, v. 0.9.1