From f0c20d947b3cc411c3c19d1ad1e507419d167fd7 Mon Sep 17 00:00:00 2001 From: Robin Dunn Date: Wed, 6 Mar 2002 21:41:19 +0000 Subject: [PATCH] A new version of XRCed from Roman Rolinsky with a few tweaks by me. git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@14470 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775 --- wxPython/tools/XRCed/CHANGES | 29 + wxPython/tools/XRCed/README | 56 +- wxPython/tools/XRCed/TODO | 30 +- wxPython/tools/XRCed/images.py | 46 +- wxPython/tools/XRCed/params.py | 285 ++++++-- wxPython/tools/XRCed/xrced.py | 1154 +++++++++++++++++++++----------- wxPython/tools/XRCed/xrced.sh | 1 + wxPython/tools/XRCed/xrced.xrc | 167 ++++- wxPython/tools/XRCed/xxx.py | 138 ++-- 9 files changed, 1311 insertions(+), 595 deletions(-) create mode 100644 wxPython/tools/XRCed/xrced.sh diff --git a/wxPython/tools/XRCed/CHANGES b/wxPython/tools/XRCed/CHANGES index 28061d36fe..eecd2c0ebe 100644 --- a/wxPython/tools/XRCed/CHANGES +++ b/wxPython/tools/XRCed/CHANGES @@ -1,3 +1,32 @@ +0.0.7 +----- + +Some command-line arguments. + +"Test window" command and toolbar button. + +New panel interphace (wxHTMLWindow is not used anymore). + +Toggling between embedded and detached panel. + +Cache for already used windows. + +Current top-level control is bold, if test window shown. + +Undo/redo broken. + +CheckListBox does not work unless wxXRC source fixed (in both wxPytnon and +wxWin): + +contrib/src/xrc/xmlrsall.cpp +45,46c45,46 +< AddHandler(new wxListCtrlXmlHandler); +< #if CHECKLISTBOX +--- +> AddHandler(new wxListCtrlXmlHandler); +> #if wxUSE_CHECKLISTBOX + + 0.0.6 ----- diff --git a/wxPython/tools/XRCed/README b/wxPython/tools/XRCed/README index ee7108f4e2..133ee2a2e5 100644 --- a/wxPython/tools/XRCed/README +++ b/wxPython/tools/XRCed/README @@ -7,53 +7,65 @@ Installation on UNIX -------------------- -XRCed was developed using Python 2.1.1. xml.dom.minidom module should be -available. XML support requires Expat package (http://expat.sourceforge.net), -and you have to uncomment expat lines in Modules/Setup file of Python source: +XRCed requires wxGTK and wxPython greater than 2.3.2, and Python 2.2 (it may +work with earlier version, but was not tested). -EXPAT_DIR=$(HOME)/expat -pyexpat pyexpat.c -I$(EXPAT_DIR)/xmlparse -L$(EXPAT_DIR) -lexpat +Of course wxGTK's XRC library (libwxxrc) must be installed. -wxPython version used was 2.3.1, which itself uses wxGTK 2.3.1. wxPython -should be modified to support some extra functions. To update it, go to -wxPython source directory and untar "wxPython-update.tgz" (included with -xrced) file there. Then recompile (run "b 21" for example) it and install as -usual (or you can set your environment to use it from the source tree). +Installation on Windows +----------------------- +Works with wxPython 2.3.2 for Python 2.2. Short manual ------------ +XRCed's idea is very straightforward: it is a visual tool for editing an XML +file conforming to XRC format. Every operation performed in XRCed has direct +correspondence to XML structure. So it is not really a usual point-and-click +GUI builder, but don't let that scare you. + To start xrced, change to the directory where you installed it and run -"python2.1 xrced.py". +"python2.2 xrced.py". + +On UNIX you can edit wrapper script "xrced.sh" to point to your installation +directory. To create an object, first you should select some object in the tree (or the -root item if it's empty) then press the right mouse button and select right -(in another sense now :) ) command. The pulldown menu is context-dependent on -the selected object. +root item if it's empty) then press the right mouse button and select an +appropriate command. The pulldown menu is context-dependent on the selected +object. XRCed tries to guess if new object should be added as a next sibling or a child of current object, depending on the possibility of the object to have child objects and expanded state (if tree item is collapsed, new object will be sibling). You can change this behavior to create siblings by pressing and -holding the Control key before clicking the mouse. +holding the Shift and Control keys before clicking the mouse. + +Pressed Control key while pressing right button makes next item a sibling of +selected item regardless of its expanded state. -Same applies for copy/paste, but at the moment Control key is ignored. +Pressed Shift key changes the place for inserting new child to be before +selected child, not after as by default. Panel on the right contains object properties. Properties which are optional -should be "checked" first. XMLID of the object is the textbox to the right of -the class name. +should be "checked" first. This panel can be made separate by unchecking +"Embed Panel" in View menu. All properties can be edited as text, and some are supplied with special editing controls. +The names of the properties are exactly as in XRC file, and it's usually not +hard to guess what they do. XML ID is the name of the window, and must be +present for top-level windows (though this is not enforced by XRCed). + To display the preview window double-click a top-level object (you should -assign an XMLID to it first). After that, if you select a child object, it +assign an XMLID to it first), press "Test" toolbar button or select command +from View menu, or press F5. After that, if you select a child object, it becomes highlighted, and if you change it, preview is updated when you select another item or press Ctrl-R (refresh). To turn off automatic update, toggle -"View->Auto-refresh" or toolbar auto-refresh button (to the right of refresh -button). - +"View->Auto-refresh" or toolbar auto-refresh button (to the right of the +refresh button). -------------------------------------------------------------------------------- diff --git a/wxPython/tools/XRCed/TODO b/wxPython/tools/XRCed/TODO index da2aaf4be5..4b9cc1b2b3 100644 --- a/wxPython/tools/XRCed/TODO +++ b/wxPython/tools/XRCed/TODO @@ -1,33 +1,29 @@ TODO for XRCed ============== -* Undo/Redo +- Undo/Redo -* menu - accel not displayed in preview +- menu - accel not displayed in preview -* tree icons ++ tree icons -* paste as sibling (toolbar toggle button) +- replace object with another, keeping children -* replace object with abother, keeping children ++ write tmp file for current dialog/panel/etc. -* write tmp file for current dialog/panel/etc. ++ XML indents -* XML indents +? select same notebook pages after update -* select same notebook pages after update +- put some default values in tree ctrl etc. -* put some default values in tree ctrl etc. - -* special (fast) update for some values: pos/size, value/content, sizeritem +- special (fast) update for some values: pos/size, value/content, sizeritem stuff (?), well, as much as possible -* highlighting with better method - -* import XRC/WXR files +- highlighting with better method -* disable some window creation when it's not valid +- import XRC/WXR files -* check for memory leaks from wx objects ++ disable some window creation when it's not valid -* style defaults for `hidde' and `focused' should be false +- check for memory leaks from wx objects diff --git a/wxPython/tools/XRCed/images.py b/wxPython/tools/XRCed/images.py index f2957d15a9..924c55d463 100644 --- a/wxPython/tools/XRCed/images.py +++ b/wxPython/tools/XRCed/images.py @@ -119,15 +119,34 @@ def getPasteBitmap(): def getPasteImage(): return wxImageFromBitmap(getPasteBitmap()) +#---------------------------------------------------------------------- +def getTestData(): + return cPickle.loads(zlib.decompress( +'x\xda\x95\xd2=\x0b\xc20\x10\x06\xe0\xbd\xbf\xe2\xc0\xa1N\xa1\x1f\n\xba*t\ +\xf4\x86.\xb7\x96\xe2d\xf1\xfc\xff\x93\xb9$\xcd5M\xa9xY\xf2\xf2\x10r$w\x9c>u\ +\xd1\x97\xcd\t\x9a\x16\xceP\x97\xc5\xd0\x97\x06F\xb8M\xc3\xf8r\x89l:\xb4\x95\ +,\x97Q\xf2\xb5\x92\xe52K\xee.\xdd\xbd\xf2\x19l~\xf0\xfb\x19\xc2v\xfd\x81Fj\ +\x1b\xcd\\9\x12\xd1\x8cD+$f\x0efw\xb4\x8b\xb8D\xb1\xa0nG\xbb\x88\x8a\xde,r@w\ +4A\x07\x8b\xa3\x01\xb5\x95\xd8V\x86Fm\x03M\xb4\x05\xaaF\xcb1\xb9R_h\xd5\x10f\ +\xc8\x1c\x15\xd3_\x89&\x8a+\x04$\xff\x14D\xe8-\x9d\x04\xcb\xa4\x94\xcd\x10\ +\xa2\xd2\xef\x013_\xba\x1d\xa3N' )) + +def getTestBitmap(): + return wxBitmapFromXPMData(getTestData()) + +def getTestImage(): + return wxImageFromBitmap(getTestBitmap()) + #---------------------------------------------------------------------- def getRefreshData(): return cPickle.loads(zlib.decompress( -'x\xda\xad\xd2\xbb\x0e\xc20\x0c\x05\xd0\xbd_q%\x860]\xf55\xc0\x0f0\xb2t\xf1\ -\x8a\x10\x1bj\xf9\xff\x89\xbc\x9a\x9a\xe2\xc2Rg\x89u\x14+vr|\xbe\x9ajpm\x8f\ -\xb6C\x87\xc6U\xb7\xc1\t\xee8\x9c\xeb\xb0b\x0e\x9f_\xa7\xf1\x11\x13\x06\xbc\ -\x9cj\x1f\x19\xed\xd8\r\x19b\x03\xbd\x88R\x85\x8c&XTc\xb2\xb0\x11\x13\x89_\ -\xc8\xb4\xd9(\xab\xeb~`"\x13\x91\xc9F\xd5\xee\x9e8w@\x1bY\xae\xfd]6\x9f\xb0&\ -4\x1f\xa5\x8dQY\xaa\x9a\x8f]\x86\xb1nED\x8a\xfd\xfdC|\x03\xab\xaaw\xdd' )) +'x\xda\xad\xd2\xbb\x0e\xc20\x0c\x05\xd0\xbd_q%\x860]\xf5\xc1\x00?\xc0\xc8\ +\xd2\xc5+Bl\xa8\xed\xffO\xcd\xab\xa9\x01\x17\t\x81\xb3\xc4:\x8a\x15;\xd9?\ +\xa6\xa6\xea]{@\xdb\xa1C\xe3\xaak\xef\x047\xecNuX1\x87\xcf/\xe3p\x8f\t\x03\ +\x9e\x8f\xb5\x8f\x8cv|\x83\x0c\xb1\x81^D\xa9BF\x13\xac\xaa1Y\xd8\x88\x89\xc4\ +\'d\xdal\x94\xd5u\x9f0\x91\x89\xc8d\xa3j\xf7\x9f\xb8t@\x1bY\xae\xfd^6\x9f\ +\xb0&\xb4\x1c\xa5\x8dQY\xaa\x9a\x8f]\x86\xf1\xda\x8a\x88\x14\xc3O\x1f\x8c3\ +\x1dNw\xdd' )) def getRefreshBitmap(): return wxBitmapFromXPMData(getRefreshData()) @@ -138,14 +157,13 @@ def getRefreshImage(): #---------------------------------------------------------------------- def getAutoRefreshData(): return cPickle.loads(zlib.decompress( -'x\xda\xad\xd21\x0e\xc20\x0c\x05\xd0\xbd\xa7\xb0\xc4\x10&+-\x1d\xca\x05\x18Y\ -\xba\xfc\x15!6\x84\xb9\xffD\x9c8%R\x92\x8a\x01gh\xacW9\xb2\x93\xe3\xf3=\x0e\ -\xab\x9bf\x9aN4\xd3\xe8\x86\xdb\xea@w:\x9c\xbd\xae\x98S\xc8\xaf\xf2z\xc4D\ -\x14\xbd_\x16\x9f\x905\xbf\x84\xcc\xe7\x9f\xdb\xf17d\x8d\x0e\x06A\xa1\x05r4\ -\xd0WKL\xa6\x1b4\x91i\x0f9m:eA\x12\x025*\x05\x03\x9b\x96\xad\x00\xf6i\xa1\ -\x85luk\xec\x94\xdd\xc5\xd4\x81\xb4\xcb\xf2\xa6\x10\xa9\xca\xdahbO\xa8\xd1\ -\x06\x84\xce\x95Q\x1e\x7f\xe7\xb2\xb3U\xad\xc0\x8e\xfb\xe9\r\xf1\x07\xdbD\ -\x86\x9f' )) +'x\xda\x95\xd2;\x0e\xc20\x0c\x06\xe0\xbd\xa7\xb0\xc4\x10&+-\x1d\xca\x05\x18Y\ +\xba\xfc+Bl\x08s\xff\x89<\x9c\x12\xc9I\xa5:Cc}\x95#;9\xbf\xbf\xe3\xb0\xbai\ +\xa6\xe9B3\x8dnx\xac\x0e\xf4\xa4\xd3\xd5\xc7\x95r\n\xf9]>\xaf\x94HD\xef\x97\ +\xc5g\xe4\x98\xdfB\xe6\xcb\xcf\xed8\x82\x1c\xa3\x83APi\x85\x9c\x0c\xf4\xd7\ +\x1a\xb3\xc5\r\x9a\xc8\xb4\x87\x9c7\x9d\xb2 \t\x01\x8b\x91\x82\x81U\xebV\x00\ +\xfd\xb4PC\xb6\xba\x16;ew1w \xed\xb2\xbc)DLY\x1dM\xea\t\x16u@\xe8\\\x19\x95\ +\xf1w.\xbb\x98i\x05z\xdc\xe17d\x90\x7f\x95\x07\x86\x9f' )) def getAutoRefreshBitmap(): return wxBitmapFromXPMData(getAutoRefreshData()) diff --git a/wxPython/tools/XRCed/params.py b/wxPython/tools/XRCed/params.py index 5ec0ac9516..89513a1fbf 100644 --- a/wxPython/tools/XRCed/params.py +++ b/wxPython/tools/XRCed/params.py @@ -23,8 +23,8 @@ genericStyles = ['wxSIMPLE_BORDER', 'wxDOUBLE_BORDER', # Class that can properly disable children class PPanel(wxPanel): - def __init__(self, parent, id, name): - wxPanel.__init__(self, parent, id, name=name) + def __init__(self, parent, name): + wxPanel.__init__(self, parent, -1, name=name) self.modified = self.freeze = false def Enable(self, value): # Something strange is going on with enable so we make sure... @@ -36,8 +36,8 @@ class PPanel(wxPanel): panel.SetModified(true) class ParamBinaryOr(PPanel): - def __init__(self, parent, id, size, name): - PPanel.__init__(self, parent, id, name) + def __init__(self, parent, name): + PPanel.__init__(self, parent, name) self.ID_TEXT_CTRL = wxNewId() self.ID_BUTTON_CHOICES = wxNewId() self.SetBackgroundColour(panel.GetBackgroundColour()) @@ -97,7 +97,10 @@ class ParamBinaryOr(PPanel): value.append(self.values[i]) # Add ignored flags value.extend(ignored) - self.SetValue(reduce(lambda a,b: a+'|'+b, value)) + if value: + self.SetValue(reduce(lambda a,b: a+'|'+b, value)) + else: + self.SetValue('') self.SetModified() dlg.Destroy() @@ -109,31 +112,31 @@ class ParamFlag(ParamBinaryOr): equal = {'wxALIGN_CENTER': 'wxALIGN_CENTRE', 'wxALIGN_CENTER_VERTICAL': 'wxALIGN_CENTRE_VERTICAL', 'wxALIGN_CENTER_HORIZONTAL': 'wxALIGN_CENTRE_HORIZONTAL'} - def __init__(self, parent, id, size, name): - ParamBinaryOr.__init__(self, parent, id, size, name) + def __init__(self, parent, name): + ParamBinaryOr.__init__(self, parent, name) class ParamStyle(ParamBinaryOr): equal = {'wxALIGN_CENTER': 'wxALIGN_CENTRE'} - def __init__(self, parent, id = -1, size = wxDefaultSize, name = ''): + def __init__(self, parent, name): self.values = currentXXX.winStyles + genericStyles - ParamBinaryOr.__init__(self, parent, id, size, name) + ParamBinaryOr.__init__(self, parent, name) class ParamNonGenericStyle(ParamBinaryOr): - def __init__(self, parent, id = -1, size = wxDefaultSize, name = ''): + def __init__(self, parent, name): self.values = currentXXX.winStyles - ParamBinaryOr.__init__(self, parent, id, size, name) + ParamBinaryOr.__init__(self, parent, name) class ParamExStyle(ParamBinaryOr): - def __init__(self, parent, id = -1, size = wxDefaultSize, name = ''): + def __init__(self, parent, name): if currentXXX: self.values = currentXXX.exStyles # constant at the moment else: self.values = [] - ParamBinaryOr.__init__(self, parent, id, size, name) + ParamBinaryOr.__init__(self, parent, name) class ParamColour(PPanel): - def __init__(self, parent, id = -1, size = wxDefaultSize, name = ''): - PPanel.__init__(self, parent, id, name) + def __init__(self, parent, name): + PPanel.__init__(self, parent, name) self.ID_TEXT_CTRL = wxNewId() self.ID_BUTTON = wxNewId() self.SetBackgroundColour(panel.GetBackgroundColour()) @@ -153,7 +156,6 @@ class ParamColour(PPanel): return self.value def SetValue(self, value): self.freeze = true - value = string.strip(value) if not value: value = '#FFFFFF' self.value = value self.text.SetValue(str(value)) # update text ctrl @@ -198,8 +200,8 @@ fontStylesXml2wx = ReverseMap(fontStylesWx2Xml) fontWeightsXml2wx = ReverseMap(fontWeightsWx2Xml) class ParamFont(PPanel): - def __init__(self, parent, id = -1, size = wxDefaultSize, name = ''): - PPanel.__init__(self, parent, id, name) + def __init__(self, parent, name): + PPanel.__init__(self, parent, name) self.ID_TEXT_CTRL = wxNewId() self.ID_BUTTON_SELECT = wxNewId() self.SetBackgroundColour(panel.GetBackgroundColour()) @@ -219,34 +221,56 @@ class ParamFont(PPanel): self.SetModified() self.textModified = true evt.Skip() + def _defaultValue(self): + return ['12', 'default', 'normal', 'normal', '0', '', ''] def GetValue(self): if self.textModified: # text has newer value - return eval(self.text.GetValue()) + try: + return eval(self.text.GetValue()) + except SyntaxError: + wxLogError('Syntax error in parameter value: ' + self.GetName()) + return self._defaultValue() return self.value def SetValue(self, value): self.freeze = true # disable other handlers - if not value: value = [''] * 7 + if not value: value = self._defaultValue() self.value = value self.text.SetValue(str(value)) # update text ctrl self.freeze = false def OnButtonSelect(self, evt): if self.textModified: # text has newer value - self.value = eval(self.text.GetValue()) + try: + self.value = eval(self.text.GetValue()) + except SyntaxError: + wxLogError('Syntax error in parameter value: ' + self.GetName()) + self.value = self._defaultValue() # Make initial font - try: size = int(self.value[0]) - except ValueError: size = 12 - try: family = fontFamiliesXml2wx[self.value[1]] - except KeyError: family = wxDEFAULT - try: style = fontStylesXml2wx[self.value[2]] - except KeyError: style = wxNORMAL - try: weight = fontWeightsXml2wx[self.value[3]] - except KeyError: weight = wxNORMAL - try: underlined = int(self.value[4]) - except ValueError: underlined = 0 - face = self.value[5] - mapper = wxFontMapper() + # Default values + size = 12 + family = wxDEFAULT + style = weight = wxNORMAL + underlined = 0 + face = '' enc = wxFONTENCODING_DEFAULT - if not self.value[6]: enc = mapper.CharsetToEncoding(self.value[6]) + # Fall back to default if exceptions + error = false + try: + try: size = int(self.value[0]) + except ValueError: error = true + try: family = fontFamiliesXml2wx[self.value[1]] + except KeyError: error = true + try: style = fontStylesXml2wx[self.value[2]] + except KeyError: error = true + try: weight = fontWeightsXml2wx[self.value[3]] + except KeyError: error = true + try: underlined = int(self.value[4]) + except ValueError: error = true + face = self.value[5] + mapper = wxFontMapper() + if not self.value[6]: enc = mapper.CharsetToEncoding(self.value[6]) + except IndexError: + error = true + if error: wxLogError('Invalid font specification') if enc == wxFONTENCODING_DEFAULT: enc = wxFONTENCODING_SYSTEM font = wxFont(size, family, style, weight, underlined, face, enc) data = wxFontData() @@ -254,11 +278,14 @@ class ParamFont(PPanel): dlg = wxFontDialog(self, data) if dlg.ShowModal() == wxID_OK: font = dlg.GetFontData().GetChosenFont() - value = [str(font.GetPointSize()), fontFamiliesWx2Xml[font.GetFamily()], - fontStylesWx2Xml[font.GetStyle()], - fontWeightsWx2Xml[font.GetWeight()], - str(font.GetUnderlined()), font.GetFaceName(), - wxFontMapper_GetEncodingName(font.GetEncoding())] + value = [str(font.GetPointSize()), + fontFamiliesWx2Xml.get(font.GetFamily(), "default"), + fontStylesWx2Xml.get(font.GetStyle(), "normal"), + fontWeightsWx2Xml.get(font.GetWeight(), "normal"), + str(font.GetUnderlined()), + font.GetFaceName(), + wxFontMapper_GetEncodingName(font.GetEncoding()) + ] # Add ignored flags self.SetValue(value) self.SetModified() @@ -268,8 +295,8 @@ class ParamFont(PPanel): ################################################################################ class ParamInt(PPanel): - def __init__(self, parent, id = -1, size = wxDefaultSize, name = ''): - PPanel.__init__(self, parent, id, name) + def __init__(self, parent, name): + PPanel.__init__(self, parent, name) self.ID_SPIN_CTRL = wxNewId() sizer = wxBoxSizer() self.spin = wxSpinCtrl(self, self.ID_SPIN_CTRL, size=wxSize(50,-1)) @@ -283,7 +310,6 @@ class ParamInt(PPanel): return str(self.spin.GetValue()) def SetValue(self, value): self.freeze = true - value = string.strip(value) if not value: value = 0 self.spin.SetValue(int(value)) self.freeze = false @@ -293,8 +319,8 @@ class ParamInt(PPanel): evt.Skip() class ParamText(PPanel): - def __init__(self, parent, id = -1, size = wxDefaultSize, name = '', textWidth=200): - PPanel.__init__(self, parent, id, name) + def __init__(self, parent, name, textWidth=200): + PPanel.__init__(self, parent, name) self.ID_TEXT_CTRL = wxNewId() # We use sizer even here to have the same size of text control sizer = wxBoxSizer() @@ -317,29 +343,25 @@ class ParamText(PPanel): evt.Skip() class ParamAccel(ParamText): - def __init__(self, parent, id = -1, size = wxDefaultSize, name = ''): - ParamText.__init__(self, parent, id, size, name, 50) + def __init__(self, parent, name): + ParamText.__init__(self, parent, name, 50) class ParamPosSize(ParamText): - def __init__(self, parent, id = -1, size = wxDefaultSize, name = ''): - ParamText.__init__(self, parent, id, size, name, 80) + def __init__(self, parent, name): + ParamText.__init__(self, parent, name, 80) -class ContentDialog(wxDialog): +class ContentDialog(wxDialogPtr): def __init__(self, parent, value): - #wxPreDialog(self) - # Use another constructor + # Is this normal??? w = frame.res.LoadDialog(parent, 'ID_DIALOG_CONTENT') - self.this = w.this - w.thisown = 0 + wxDialogPtr.__init__(self, w.this) self.thisown = 1 - #frame.res.LoadOnDialog(self, parent, 'ID_DIALOG_CONTENT') self.Center() self.list = self.FindWindowByName('ID_LIST') # Set list items for v in value: self.list.Append(v) self.SetAutoLayout(true) - # !!! self.SetSizer(sizer) self.GetSizer().Fit(self) # Callbacks self.ID_BUTTON_APPEND = XMLID('ID_BUTTON_APPEND') @@ -379,9 +401,69 @@ class ContentDialog(wxDialog): evt.Enable(self.list.GetSelection() != -1 and \ self.list.GetSelection() < self.list.Number() - 1) +class ContentCheckListDialog(wxDialogPtr): + def __init__(self, parent, value): + # Is this normal??? + w = frame.res.LoadDialog(parent, 'ID_DIALOG_CONTENT_CHECK_LIST') + wxDialogPtr.__init__(self, w.this) + self.thisown = 1 + self.Center() + self.list = self.FindWindowByName('ID_CHECK_LIST') + # Set list items + i = 0 + for v,ch in value: + self.list.Append(v) + self.list.Check(i, ch) + i += 1 + self.SetAutoLayout(true) + self.GetSizer().Fit(self) + # Callbacks + self.ID_BUTTON_APPEND = XMLID('ID_BUTTON_APPEND') + self.ID_BUTTON_REMOVE = XMLID('ID_BUTTON_REMOVE') + self.ID_BUTTON_UP = XMLID('ID_BUTTON_UP') + self.ID_BUTTON_DOWN = XMLID('ID_BUTTON_DOWN') + EVT_CHECKLISTBOX(self, self.list.GetId(), self.OnCheck) + EVT_BUTTON(self, self.ID_BUTTON_UP, self.OnButtonUp) + EVT_BUTTON(self, self.ID_BUTTON_DOWN, self.OnButtonDown) + EVT_BUTTON(self, self.ID_BUTTON_APPEND, self.OnButtonAppend) + EVT_BUTTON(self, self.ID_BUTTON_REMOVE, self.OnButtonRemove) + EVT_UPDATE_UI(self, self.ID_BUTTON_UP, self.OnUpdateUI) + EVT_UPDATE_UI(self, self.ID_BUTTON_DOWN, self.OnUpdateUI) + EVT_UPDATE_UI(self, self.ID_BUTTON_REMOVE, self.OnUpdateUI) + def OnCheck(self, evt): + # !!! Wrong wxGTK (wxMSW?) behavior: toggling selection if checking + self.list.Deselect(evt.GetSelection()) + def OnButtonUp(self, evt): + i = self.list.GetSelection() + str, ch = self.list.GetString(i), self.list.IsChecked(i) + self.list.Delete(i) + self.list.InsertItems([str], i-1) + self.list.Check(i-1, ch) + self.list.SetSelection(i-1) + def OnButtonDown(self, evt): + i = self.list.GetSelection() + str, ch = self.list.GetString(i), self.list.IsChecked(i) + self.list.Delete(i) + self.list.InsertItems([str], i+1) + self.list.Check(i+1, ch) + self.list.SetSelection(i+1) + def OnButtonAppend(self, evt): + str = wxGetTextFromUser('Enter new item:', 'Append', '', self) + self.list.Append(str) + def OnButtonRemove(self, evt): + self.list.Delete(self.list.GetSelection()) + def OnUpdateUI(self, evt): + if evt.GetId() == self.ID_BUTTON_REMOVE: + evt.Enable(self.list.GetSelection() != -1) + elif evt.GetId() == self.ID_BUTTON_UP: + evt.Enable(self.list.GetSelection() > 0) + elif evt.GetId() == self.ID_BUTTON_DOWN: + evt.Enable(self.list.GetSelection() != -1 and \ + self.list.GetSelection() < self.list.Number() - 1) + class ParamContent(PPanel): - def __init__(self, parent, id = -1, size = wxDefaultSize, name = ''): - PPanel.__init__(self, parent, id, name) + def __init__(self, parent, name): + PPanel.__init__(self, parent, name) self.ID_TEXT_CTRL = wxNewId() self.ID_BUTTON_EDIT = wxNewId() self.SetBackgroundColour(panel.GetBackgroundColour()) @@ -403,7 +485,11 @@ class ParamContent(PPanel): evt.Skip() def GetValue(self): if self.textModified: # text has newer value - return eval(self.text.GetValue()) + try: + return eval(self.text.GetValue()) + except SyntaxError: + wxLogError('Syntax error in parameter value: ' + self.GetName()) + return [] return self.value def SetValue(self, value): self.freeze = true @@ -412,7 +498,11 @@ class ParamContent(PPanel): self.freeze = false def OnButtonEdit(self, evt): if self.textModified: # text has newer value - self.value = eval(self.text.GetValue()) + try: + self.value = eval(self.text.GetValue()) + except SyntaxError: + wxLogError('Syntax error in parameter value: ' + self.GetName()) + self.value = [] dlg = ContentDialog(self, self.value) if dlg.ShowModal() == wxID_OK: value = [] @@ -424,11 +514,65 @@ class ParamContent(PPanel): self.textModified = false dlg.Destroy() +# CheckList content +class ParamContentCheckList(PPanel): + def __init__(self, parent, name): + PPanel.__init__(self, parent, name) + self.ID_TEXT_CTRL = wxNewId() + self.ID_BUTTON_EDIT = wxNewId() + self.SetBackgroundColour(panel.GetBackgroundColour()) + sizer = wxBoxSizer() + self.text = wxTextCtrl(self, self.ID_TEXT_CTRL, size=wxSize(200,-1)) + sizer.Add(self.text, 0, wxRIGHT | wxALIGN_CENTER_VERTICAL, 10) + self.button = wxButton(self, self.ID_BUTTON_EDIT, 'Edit...') + sizer.Add(self.button, 0, wxALIGN_CENTER_VERTICAL) + self.SetAutoLayout(true) + self.SetSizer(sizer) + sizer.Fit(self) + self.textModified = false + EVT_BUTTON(self, self.ID_BUTTON_EDIT, self.OnButtonEdit) + EVT_TEXT(self, self.ID_TEXT_CTRL, self.OnChange) + def OnChange(self, evt): + if self.freeze: return + self.SetModified() + self.textModified = true + evt.Skip() + def GetValue(self): + if self.textModified: # text has newer value + try: + return eval(self.text.GetValue()) + except SyntaxError: + wxLogError('Syntax error in parameter value: ' + self.GetName()) + return [] + return self.value + def SetValue(self, value): + self.freeze = true + self.value = value + self.text.SetValue(str(value)) # update text ctrl + self.freeze = false + def OnButtonEdit(self, evt): + if self.textModified: # text has newer value + try: + self.value = eval(self.text.GetValue()) + except SyntaxError: + wxLogError('Syntax error in parameter value: ' + self.GetName()) + self.value = [] + dlg = ContentCheckListDialog(self, self.value) + if dlg.ShowModal() == wxID_OK: + value = [] + for i in range(dlg.list.Number()): + value.append((dlg.list.GetString(i), dlg.list.IsChecked(i))) + # Add ignored flags + self.SetValue(value) + self.SetModified() + self.textModified = false + dlg.Destroy() + # Boxless radiobox class RadioBox(PPanel): def __init__(self, parent, id, choices, - pos=wxDefaultPosition, size=wxDefaultSize, name='radiobox'): - PPanel.__init__(self, parent, id, name) + pos=wxDefaultPosition, name='radiobox'): + PPanel.__init__(self, parent, name) self.SetBackgroundColour(panel.GetBackgroundColour()) self.choices = choices topSizer = wxBoxSizer() @@ -456,8 +600,8 @@ class RadioBox(PPanel): class ParamBool(RadioBox): values = {'yes': '1', 'no': '0'} seulav = {'1': 'yes', '0': 'no'} - def __init__(self, parent, id = -1, size = wxDefaultSize, name = ''): - RadioBox.__init__(self, parent, id, choices = self.values.keys(), name=name) + def __init__(self, parent, name): + RadioBox.__init__(self, parent, -1, choices = self.values.keys(), name=name) def GetValue(self): return self.values[self.GetStringSelection()] def SetValue(self, value): @@ -467,8 +611,8 @@ class ParamBool(RadioBox): class ParamOrient(RadioBox): values = {'horizontal': 'wxHORIZONTAL', 'vertical': 'wxVERTICAL'} seulav = {'wxHORIZONTAL': 'horizontal', 'wxVERTICAL': 'vertical'} - def __init__(self, parent, id = -1, size = wxDefaultSize, name = ''): - RadioBox.__init__(self, parent, id, choices = self.values.keys(), name=name) + def __init__(self, parent, name): + RadioBox.__init__(self, parent, -1, choices = self.values.keys(), name=name) def GetValue(self): return self.values[self.GetStringSelection()] def SetValue(self, value): @@ -476,8 +620,8 @@ class ParamOrient(RadioBox): self.SetStringSelection(self.seulav[value]) class ParamFile(PPanel): - def __init__(self, parent, id = -1, size = wxDefaultSize, name = ''): - PPanel.__init__(self, parent, id, name) + def __init__(self, parent, name): + PPanel.__init__(self, parent, name) self.ID_TEXT_CTRL = wxNewId() self.ID_BUTTON_BROWSE = wxNewId() self.SetBackgroundColour(panel.GetBackgroundColour()) @@ -499,7 +643,7 @@ class ParamFile(PPanel): evt.Skip() def GetValue(self): if self.textModified: # text has newer value - return eval(self.text.GetValue()) + return self.text.GetValue() return self.value def SetValue(self, value): self.freeze = true @@ -510,11 +654,12 @@ class ParamFile(PPanel): if self.textModified: # text has newer value self.value = self.text.GetValue() dlg = wxFileDialog(self, - defaultDir = os.path.abspath(os.path.dirname(self.value)), + defaultDir = os.path.dirname(self.value), defaultFile = os.path.basename(self.value)) if dlg.ShowModal() == wxID_OK: # Make relative - common = os.path.commonprefix([frame.dataFile, dlg.GetPath()]) + common = os.path.commonprefix([os.path.abspath(frame.dataFile), + dlg.GetPath()]) self.SetValue(dlg.GetPath()[len(common):]) self.SetModified() self.textModified = false diff --git a/wxPython/tools/XRCed/xrced.py b/wxPython/tools/XRCed/xrced.py index ff07aff810..175c022f55 100644 --- a/wxPython/tools/XRCed/xrced.py +++ b/wxPython/tools/XRCed/xrced.py @@ -7,25 +7,23 @@ from wxPython.wx import * from wxPython.xrc import * from wxPython.html import * -import wxPython.lib.wxpTag from xml.dom import minidom import os import tempfile +import getopt +# Icons import images -# String constants +# Constants -faceColour = wxSystemSettings_GetSystemColour(wxSYS_COLOUR_3DFACE) -# Background colour problem on wxGTK if wxGetOsVersion()[1] == 1: - bgcolor = (faceColour.Red()-1, faceColour.Green()-1, faceColour.Blue()-1) + labelFont = wxFont(12, wxDEFAULT, wxNORMAL, wxBOLD) else: - bgcolor = (faceColour.Red(), faceColour.Green(), faceColour.Blue()) -htmlHeader = '\n' % bgcolor -htmlFooter = '\n' + labelFont = wxFont(10, wxDEFAULT, wxNORMAL, wxBOLD) + progname = 'XRCed' -version = '0.0.6' +version = '0.0.7-1' # Local modules from xxx import * @@ -35,11 +33,19 @@ testWin = None testWinPos = wxDefaultPosition # 1 adds CMD command to Help menu -debug = 1 +debug = 0 + +helpText = """\ +

Welcome to XRCed!

DON'T PANIC :)

+To start select tree root, then popup menu with your right mouse button, +select "Append Child", and then any command.

+Enter XML ID, change properties, create children.

+To test your interface select Test command (View menu).

+Consult README file for the details. +""" -if debug: - import traceback - import time +defaultIDs = {xxxPanel:'PANEL', xxxDialog:'DIALOG', xxxFrame:'FRAME', + xxxMenuBar:'MENUBAR', xxxMenu:'MENU', xxxToolBar:'TOOLBAR'} # Set menu to list items. # Each menu command is a tuple (id, label, help) @@ -63,78 +69,148 @@ class Panel(wxNotebook): def __init__(self, parent, id = -1): wxNotebook.__init__(self, parent, id, style=wxNB_BOTTOM) sys.modules['params'].panel = self - self.page1 = HtmlPage(self) + # List of child windows + self.pages = [] + # Create scrolled windows for pages + self.page1 = wxScrolledWindow(self, -1) + sizer = wxBoxSizer() + sizer.Add(wxBoxSizer()) # dummy sizer + self.page1.SetAutoLayout(true) + self.page1.SetSizer(sizer) self.AddPage(self.page1, 'Properties') - self.page2 = None + # Second page + self.page2 = wxScrolledWindow(self, -1) + sizer = wxBoxSizer() + sizer.Add(wxBoxSizer()) # dummy sizer + self.page2.SetAutoLayout(true) + self.page2.SetSizer(sizer) # Cache for already used panels - self.styleCache = {} + self.pageCache = {} # cached property panels + self.stylePageCache = {} # cached style panels + # Dummy parent window for cache pages + self.cacheParent = wxFrame(None, -1, 'non visible') + # Delete child windows and recreate page sizer + def ResetPage(self, page): + topSizer = page.GetSizer() + sizer = topSizer.GetChildren()[0].GetSizer() + for w in page.GetChildren(): + sizer.RemoveWindow(w) + if isinstance(w, ParamPage): + # With SetParent, we wouldn't need this + w.Reparent(self.cacheParent) + else: + w.Destroy() + topSizer.RemoveSizer(sizer) + # Create new windows + sizer = wxBoxSizer(wxVERTICAL) + # Special case - resize html window + if conf.panic: + topSizer.Add(sizer, 1, wxEXPAND) + else: + topSizer.Add(sizer, 0, wxALL, 5) + return sizer def SetData(self, xxx): + self.pages = [] # First page - self.page1.SetPageData(xxx) - # Replace/remove style page - curPage = self.GetSelection() - if self.page2: - # If style page for a same class, don't need to recreate - if xxx and self.page2.cacheId == xxx.treeObject().__class__: - self.page2.SetData(xxx.treeObject()) - return - # Else remove and then add if needed - self.RemovePage(1) - if not self.styleCache.has_key(self.page2.cacheId): - self.styleCache[self.page2.cacheId] = self.page2 - self.Freeze() + # Set cached or new page + # Remove current objects and sizer + sizer = self.ResetPage(self.page1) + if not xxx or (not xxx.allParams and not xxx.hasName): + if tree.selection: + sizer.Add(wxStaticText(self.page1, -1, 'This item has no properties.')) + else: # nothing selected + # If first time, show some help + if conf.panic: + html = wxHtmlWindow(self.page1, -1, wxDefaultPosition, + wxDefaultSize, wxSUNKEN_BORDER) + html.SetPage(helpText) + sizer.Add(html, 1, wxEXPAND) + conf.panic = false + else: + sizer.Add(wxStaticText(self.page1, -1, 'Select a tree item.')) + else: + SetCurrentXXX(xxx.treeObject()) + try: + page = self.pageCache[xxx.__class__] + page.Reparent(self.page1) + except KeyError: + page = PropPage(self.page1, xxx.className, xxx) + self.pageCache[xxx.__class__] = page + page.SetValues(xxx) + self.pages.append(page) + sizer.Add(page, 1, wxEXPAND) + if xxx.hasChild: + # Special label for child objects - they may have different GUI + cacheID = (xxx.child.__class__, xxx.__class__) + try: + page = self.pageCache[cacheID] + page.Reparent(self.page1) + except KeyError: + page = PropPage(self.page1, xxx.child.className, xxx.child) + self.pageCache[cacheID] = page + page.SetValues(xxx.child) + self.pages.append(page) + sizer.Add(page, 0, wxEXPAND | wxTOP, 5) + self.page1.Layout() + size = self.page1.GetSizer().GetMinSize() + self.page1.SetScrollbars(1, 1, size.x, size.y, 0, 0, true) + + # Second page + # Create if does not exist if xxx and xxx.treeObject().hasStyle: - cacheId = xxx.treeObject().__class__ - if self.styleCache.has_key(cacheId): - self.page2 = self.styleCache[cacheId] - self.page2.SetData(xxx.treeObject()) - else: - self.page2 = StylePage(self, xxx.treeObject()) - self.AddPage(self.page2, 'Style') + xxx = xxx.treeObject() + # Simplest case: set data if class is the same + sizer = self.ResetPage(self.page2) + try: + page = self.stylePageCache[xxx.__class__] + page.Reparent(self.page2) + except KeyError: + page = StylePage(self.page2, xxx.className + ' style', xxx) + self.stylePageCache[xxx.__class__] = page + page.SetValues(xxx) + self.pages.append(page) + sizer.Add(page, 0, wxEXPAND) + # Add page if not exists + if not self.GetPageCount() == 2: + self.AddPage(self.page2, 'Style') + self.page2.Layout() + size = self.page2.GetSizer().GetMinSize() + self.page2.SetScrollbars(1, 1, size.x, size.y, 0, 0, true) else: - self.page2 = None - # Keep page selected - if self.page2 and curPage == 1: - self.SetSelection(curPage) - self.Thaw() + # Remove page if exists + if self.GetPageCount() == 2: + self.RemovePage(1) def Clear(self): - self.page1.Clear() - if self.page2: - self.RemovePage(1) - self.page2.Destroy() - self.page2 = None - # If some parameter on some page has changed + self.SetData(None) + # Check if some parameter on some page has changed def IsModified(self): - return self.page1.IsModified() or self.page2 and self.page2.IsModified() + for p in self.pages: + if p.IsModified(): return true + return false + # Reset changed state def SetModified(self, value): - self.page1.SetModified(value) - if self.page2: - self.page2.SetModified(value) + for p in self.pages: p.SetModified(value) + def Apply(self): + for p in self.pages: p.Apply() ################################################################################ # General class for notebook pages -class ParamPage: - def __init__(self): +class ParamPage(wxPanel): + def __init__(self, parent, xxx): + wxPanel.__init__(self, parent, -1) + self.xxx = xxx # Register event handlers for id in paramIDs.values(): EVT_CHECKBOX(self, id, self.OnCheckParams) + self.modified = false + self.checks = {} + self.controls = {} # save python objects + self.controlName = None def OnCheckParams(self, evt): - selected = tree.GetSelection() - xxx = tree.GetPyData(selected) - winName = evt.GetEventObject().GetName() - sizerParam = false - if winName[0] == '_': - param = winName[7:] - sizerParam = true - else: - if xxx.hasChild: xxx = xxx.child - param = winName[6:] - # Set current object - if sizerParam: - w = self.FindWindowByName('_data_' + param) - else: - w = self.FindWindowByName('data_' + param) + xxx = self.xxx + param = evt.GetEventObject().GetName() + w = self.controls[param] objElem = xxx.element if evt.IsChecked(): # Ad new text node in order of allParams @@ -160,108 +236,131 @@ class ParamPage: xxx.params[param].remove() del xxx.params[param] w.SetValue('') + w.modified = false # mark as not changed # Set modified flas self.SetModified(true) w.Enable(evt.IsChecked()) - + # If some parameter has changed + def IsModified(self): + return self.modified + def SetModified(self, value): + self.modified = value + def Apply(self): + xxx = self.xxx + # !!! Save undo info +# if xxx.undo: xxx.undo.unlink() +# xxx.undo = xxx.element.cloneNode(false) + if self.controlName: + name = self.controlName.GetValue() + if xxx.name != name: + xxx.name = name + xxx.element.setAttribute('name', name) + for param, w in self.controls.items(): + if w.modified: + paramObj = xxx.params[param] + value = w.GetValue() + if param in xxx.specials: + xxx.setSpecial(param, value) + else: + paramObj.update(value) ################################################################################ -# Properties panel notebook page -class HtmlPage(wxHtmlWindow, ParamPage): - def __init__(self, parent, id = -1): - wxHtmlWindow.__init__(self, parent, id) - ParamPage.__init__(self) - self.SetBorders(5) - if wxGetOsVersion()[1] == 1: - self.SetFonts('', '', [8, 10, 12, 14, 16, 19, 24]) - else: - self.SetFonts("", "", [7, 8, 10, 12, 16, 22, 30]) - self.modified = false - # Previous type - self.xxxClass = self.xxxChildClass = None - def Clear(self): - self.SetPage(htmlHeader + 'select a tree item on the left' + htmlFooter) - def SetPageData(self, xxx): - if not xxx: - self.SetPage(htmlHeader + 'this item has no properties' + htmlFooter) - return - self.Freeze() # doesn't seem to help - # Don't change interface if same class - compare = false - if self.xxxClass and self.xxxClass == xxx.__class__: - compare = true # a little weird code - if xxx.hasChild: - if self.xxxChildClass != xxx.child.__class__: - compare = false - if not compare: # not same - self.SetPage(htmlHeader + xxx.generateHtml() + htmlFooter) - self.xxxClass = xxx.__class__ - if xxx.hasChild: self.xxxChildClass = xxx.child.__class__ - self.SetValues(xxx) - if xxx.hasChild: - self.SetValues(xxx.child) - self.Thaw() +# Panel for displaying properties +class PropPage(ParamPage): + def __init__(self, parent, label, xxx): + ParamPage.__init__(self, parent, xxx) + box = wxStaticBox(self, -1, label) + box.SetFont(labelFont) + topSizer = wxStaticBoxSizer(box, wxVERTICAL) + sizer = wxFlexGridSizer(len(xxx.allParams), 2, 1, 1) + if xxx.hasName: + label = wxStaticText(self, -1, 'XML ID:', size=(80,-1)) + control = ParamText(self, name='XML_name') + sizer.AddMany([ (label, 0, wxALIGN_CENTER_VERTICAL), + (control, 0, wxALIGN_CENTER_VERTICAL) ]) + self.controlName = control + for param in xxx.allParams: + present = param in xxx.params + if param in xxx.required: + label = wxStaticText(self, paramIDs[param], param + ':', + size = (80,-1), name = param) + else: + # Notebook has one very loooooong parameter + if param == 'usenotebooksizer': sParam = 'usesizer:' + else: sParam = param + ':' + label = wxCheckBox(self, paramIDs[param], sParam, + size = (80,-1), name = param) + self.checks[param] = label + try: + typeClass = xxx.paramDict[param] + except KeyError: + try: + # Standart type + typeClass = paramDict[param] + except KeyError: + # Default + typeClass = ParamText + control = typeClass(self, param) + control.Enable(present) + sizer.AddMany([ (label, 0, wxALIGN_CENTER_VERTICAL), + (control, 0, wxALIGN_CENTER_VERTICAL) ]) + self.controls[param] = control + topSizer.Add(sizer, 1, wxALL | wxEXPAND, 5) + self.SetAutoLayout(true) + self.SetSizer(topSizer) + topSizer.Fit(self) def SetValues(self, xxx): + self.xxx = xxx # Set values, checkboxes to false, disable defaults - if xxx.hasChild: prefix = '_' - else: prefix = '' if xxx.hasName: - self.FindWindowByName('data_name').SetValue(xxx.name) + self.controlName.SetValue(xxx.name) for param in xxx.allParams: + w = self.controls[param] + w.modified = false try: value = xxx.params[param].value() - self.FindWindowByName(prefix + 'data_' + param).SetValue(value) + w.Enable(true) + w.SetValue(value) if not param in xxx.required: - self.FindWindowByName(prefix + 'check_' + param).SetValue(true) + self.checks[param].SetValue(true) except KeyError: - self.FindWindowByName(prefix + 'data_' + param).Enable(false) - # If some parameter has changed - def IsModified(self): - return self.modified - def SetModified(self, value): - self.modified = value + self.checks[param].SetValue(false) + w.SetValue('') + w.Enable(false) + self.SetModified(false) ################################################################################ # Style notebook page -class StylePage(wxPanel, ParamPage): - def __init__(self, parent, xxx): - wxPanel.__init__(self, parent, -1) - ParamPage.__init__(self) - self.cacheId = xxx.__class__ - if wxGetOsVersion()[1] == 1: - self.SetFont(wxFont(12, wxDEFAULT, wxNORMAL, wxNORMAL)) - else: - self.SetFont(wxFont(10, wxDEFAULT, wxNORMAL, wxNORMAL)) - topSizer = wxBoxSizer(wxVERTICAL) +class StylePage(ParamPage): + def __init__(self, parent, label, xxx): + ParamPage.__init__(self, parent, xxx) + box = wxStaticBox(self, -1, label) + box.SetFont(labelFont) + topSizer = wxStaticBoxSizer(box, wxVERTICAL) sizer = wxFlexGridSizer(len(xxx.styles), 2, 1, 1) - self.controls = {} # save python objects for param in xxx.styles: present = param in xxx.params.keys() check = wxCheckBox(self, paramIDs[param], - param + ':', name = 'check_' + param) + param + ':', size = (80,-1), name = param) check.SetValue(present) - control = paramDict[param](self, name = 'data_' + param) - if present: - control.SetValue(xxx.params[param].value()) - else: - control.SetValue('') + control = paramDict[param](self, name = param) control.Enable(present) sizer.AddMany([ (check, 0, wxALIGN_CENTER_VERTICAL), (control, 0, wxALIGN_CENTER_VERTICAL) ]) + self.checks[param] = check self.controls[param] = control - topSizer.Add(sizer, 1, wxALL, 5) + topSizer.Add(sizer, 1, wxALL | wxEXPAND, 5) self.SetAutoLayout(true) self.SetSizer(topSizer) - - self.modified = false - + topSizer.Fit(self) # Set data for a cahced page - def SetData(self, xxx): + def SetValues(self, xxx): + self.xxx = xxx for param in xxx.styles: present = param in xxx.params.keys() - check = self.FindWindowByName('check_' + param) + check = self.checks[param] check.SetValue(present) control = self.controls[param] if present: @@ -269,13 +368,7 @@ class StylePage(wxPanel, ParamPage): else: control.SetValue('') control.Enable(present) - self.modified = false - - # If some parameter has changed - def IsModified(self): - return self.modified - def SetModified(self, value): - self.modified = value + self.SetModified(false) ################################################################################ @@ -311,24 +404,22 @@ class MemoryFile: self.name = name self.buffer = '' def write(self, data): - self.buffer = self.buffer + data.encode() + self.buffer += data.encode() def close(self): - f = open(self.name, 'w') - f.write(self.buffer) - f.close() - # !!! memory FS will work someday - #self.file = wxMemoryFSHandler_AddFile(self.name, self.buffer) + wxMemoryFSHandler_AddFile(self.name, self.buffer) class XML_Tree(wxTreeCtrl): def __init__(self, parent, id): - wxTreeCtrl.__init__(self, parent, id, - style=wxTR_HAS_BUTTONS | wxTR_LINES_AT_ROOT) + wxTreeCtrl.__init__(self, parent, id, style = wxTR_HAS_BUTTONS) self.SetBackgroundColour(wxColour(224, 248, 224)) EVT_TREE_SEL_CHANGED(self, self.GetId(), self.OnSelChanged) # One works on Linux, another on Windows - EVT_TREE_ITEM_ACTIVATED(self, self.GetId(), self.OnItemActivated) - EVT_LEFT_DCLICK(self, self.OnDClick) + if wxGetOsVersion()[1] == 1: + EVT_TREE_ITEM_ACTIVATED(self, self.GetId(), self.OnItemActivated) + else: + EVT_LEFT_DCLICK(self, self.OnDClick) EVT_RIGHT_DOWN(self, self.OnRightDown) + self.needUpdate = false self.pendingHighLight = None self.ctrl = false @@ -352,12 +443,9 @@ class XML_Tree(wxTreeCtrl): self.il = il self.SetImageList(il) - # !!! temporary solution for GetOldItem problem def Unselect(self): - self.selection = wxTreeItemId() + self.selection = None wxTreeCtrl.Unselect(self) - def GetSelection(self): - return self.selection def ExpandAll(self, item): if self.ItemHasChildren(item): @@ -369,18 +457,53 @@ class XML_Tree(wxTreeCtrl): i, cookie = self.GetNextChild(item, cookie) for i in children: self.ExpandAll(i) + def CollapseAll(self, item): + if self.ItemHasChildren(item): + i, cookie = self.GetFirstChild(item, 0) + children = [] + while i.IsOk(): + children.append(i) + i, cookie = self.GetNextChild(item, cookie) + for i in children: + self.CollapseAll(i) + self.Collapse(item) + # Clear tree + def Clear(self): + self.DeleteAllItems() + # Add minimal structure + if self.dom: self.dom.unlink() + self.dom = minidom.Document() + self.dummyNode = self.dom.createComment('dummy node') + # Create main node + self.mainNode = self.dom.createElement('resource') + self.dom.appendChild(self.mainNode) + xxx = xxxMainNode(None, self.mainNode) + self.root = self.AddRoot('XML tree', self.rootImage, data=wxTreeItemData(xxx)) + self.SetItemHasChildren(self.root) + self.Expand(self.root) + self.Unselect() + + # Clear old data and set new def SetData(self, dom): + self.DeleteAllItems() + # Add minimal structure + if self.dom: self.dom.unlink() self.dom = dom + self.dummyNode = self.dom.createComment('dummy node') # Find 'resource' child, add it's children self.mainNode = dom.getElementsByTagName('resource')[0] + xxx = xxxMainNode(None, self.mainNode) + self.root = self.AddRoot('XML tree', self.rootImage, data=wxTreeItemData(xxx)) + self.SetItemHasChildren(self.root) nodes = self.mainNode.childNodes[:] for node in nodes: if IsObject(node): - self.AddNode(self.GetRootItem(), None, node) + self.AddNode(self.root, None, node) else: self.mainNode.removeChild(node) node.unlink() + self.Expand(self.root) self.Unselect() # Add tree item for given parent item if node is DOM element node with @@ -390,7 +513,9 @@ class XML_Tree(wxTreeCtrl): try: xxx = MakeXXXFromDOM(xxxParent, node) except: - return + print 'ERROR: MakeXXXFromDom(%s, %s)' % (xxxParent, node) + raise +# return treeObj = xxx.treeObject() # Append tree item item = self.AppendItem(itemParent, treeObj.treeName(), @@ -405,8 +530,6 @@ class XML_Tree(wxTreeCtrl): elif n.nodeType != minidom.Node.ELEMENT_NODE: treeObj.element.removeChild(n) n.unlink() - - # Remove leaf of tree, return it's data object def RemoveLeaf(self, leaf): xxx = self.GetPyData(leaf) @@ -414,30 +537,37 @@ class XML_Tree(wxTreeCtrl): parent = node.parentNode parent.removeChild(node) self.Delete(leaf) - # Update view? - #if testWin and self.GetItemAncestor(leaf) == testWin.item: - # if testWin.highLight: - # testWin.highLight.Remove() - # self.needUpdate = true + # Reset selection object + self.selection = None return node - # Find position relative to the top-level window def FindNodePos(self, item): itemParent = self.GetItemParent(item) - if itemParent == self.GetRootItem(): return wxPoint(0, 0) + # Select NB page obj = self.FindNodeObject(item) + if self.GetPyData(itemParent).treeObject().__class__ == xxxNotebook: + notebook = self.FindNodeObject(itemParent) + # Find position + for i in range(notebook.GetPageCount()): + if notebook.GetPage(i) == obj: + if notebook.GetSelection() != i: notebook.SetSelection(i) + break + # Root at (0,0) + if itemParent == self.root: return wxPoint(0, 0) # Find first ancestor which is a wxWindow (not a sizer) winParent = itemParent while self.GetPyData(winParent).isSizer: winParent = self.GetItemParent(winParent) parentPos = self.FindNodePos(winParent) - return parentPos + obj.GetPosition() - + # Position (-1,-1) is really (0,0) + pos = obj.GetPosition() + if pos == (-1,-1): pos = (0,0) + return parentPos + pos # Find window (or sizer) corresponding to a tree item. def FindNodeObject(self, item): itemParent = self.GetItemParent(item) # If top-level, return testWin (or panel if wxFrame) - if itemParent == self.GetRootItem(): return testWin.panel + if itemParent == self.root: return testWin.panel xxx = self.GetPyData(item).treeObject() parentWin = self.FindNodeObject(itemParent) # Top-level sizer? return window's sizer @@ -459,43 +589,41 @@ class XML_Tree(wxTreeCtrl): if isinstance(child, wxNotebookSizerPtr): child = child.GetNotebook() return child - def OnSelChanged(self, evt): # Apply changes - # !!! problem with wxGTK + # !!! problem with wxGTK - GetOldItem is Ok if nothing selected #oldItem = evt.GetOldItem() + status = '' oldItem = self.selection - if oldItem.IsOk(): + if oldItem: xxx = self.GetPyData(oldItem) # If some data was modified, apply changes - if xxx: - if panel.IsModified(): - self.Apply(xxx, oldItem) - #if conf.autoRefresh: - if testWin and testWin.highLight: + if panel.IsModified(): + self.Apply(xxx, oldItem) + #if conf.autoRefresh: + if testWin and tree.GetItemAncestor(oldItem) == testWin.item: + if testWin.highLight: testWin.highLight.Remove() self.needUpdate = true - # Generate HTML view - item = evt.GetItem() - self.selection = item # !!! fix - xxx = self.GetPyData(item) + status = 'Changes were applied' + frame.SetStatusText(status) + # Generate view + self.selection = evt.GetItem() + if not self.selection.IsOk(): + self.selection = None + return + xxx = self.GetPyData(self.selection) # Update panel panel.SetData(xxx) - # Remove highlight? - if not xxx and testWin and testWin.highLight: - testWin.highLight.Remove() - return # Clear flag panel.SetModified(false) # Hightlighting is done in OnIdle - tree.pendingHighLight = item - + tree.pendingHighLight = self.selection # Find top-level parent def GetItemAncestor(self, item): - while self.GetItemParent(item) != self.GetRootItem(): + while self.GetItemParent(item) != self.root: item = self.GetItemParent(item) return item - # Highlight selected item def HighLight(self, item): self.pendingHighLight = None @@ -503,72 +631,62 @@ class XML_Tree(wxTreeCtrl): not in ['wxDialog', 'wxPanel', 'wxFrame']: return # Top-level does not have highlight - if item == testWin.item or item == tree.GetRootItem(): + if item == testWin.item or item == tree.root: if testWin.highLight: testWin.highLight.Remove() return # If a control from another window is selected, remove highlight - if self.GetItemAncestor(item) != testWin.item and testWin.highLight: - testWin.highLight.Remove() + if self.GetItemAncestor(item) != testWin.item: + if testWin.highLight: testWin.highLight.Remove() return # Get window/sizer object obj, pos = self.FindNodeObject(item), self.FindNodePos(item) size = obj.GetSize() - # For notebook, select item's page. - # For children of page, nothing happens (too much work) - if isinstance(self.GetPyData(item).parent, xxxNotebook): - notebook = self.FindNodeObject(self.GetItemParent(item)) - # Find position - n = 0 - prev = self.GetPrevSibling(item) - while prev.IsOk(): - n += 1 - prev = self.GetPrevSibling(prev) - notebook.SetSelection(n) # Highlight - try: # finally I use exceptions + # Nagative positions are not working wuite well + if testWin.highLight: testWin.highLight.Replace(pos, size) - except AttributeError: + else: testWin.highLight = HightLightBox(pos, size) testWin.highLight.item = item - - # Double-click on Linux - def OnItemActivated(self, evt): - item = evt.GetItem() + def ShowTestWindow(self, item): + global testWin xxx = self.GetPyData(item) - if not xxx: return # if root selected, do nothing if panel.IsModified(): self.Apply(xxx, item) # apply changes + treeObj = xxx.treeObject() + if treeObj.className not in ['wxFrame', 'wxPanel', 'wxDialog', + 'wxMenuBar', 'wxToolBar']: + wxLogMessage('No view for this element (yet)') + return + if not treeObj.name: + wxLogError("Can't display a noname element!") + return + # Show item in bold + if testWin: + self.SetItemBold(testWin.item, false) + self.SetItemBold(item) self.CreateTestWin(item) - + # Double-click on Linux + def OnItemActivated(self, evt): + if evt.GetItem() != self.root: + self.ShowTestWindow(evt.GetItem()) # Double-click on Windows def OnDClick(self, evt): - id, flags = self.HitTest(evt.GetPosition()) + item, flags = self.HitTest(evt.GetPosition()) if flags in [wxTREE_HITTEST_ONITEMBUTTON, wxTREE_HITTEST_ONITEMLABEL]: - # !!! can't create a wxTreeItemId from int - item = self.selection # assume item already selected - xxx = self.GetPyData(item) - if not xxx: return # if root selected, do nothing - if panel.IsModified(): - self.Apply(xxx, item) # apply changes - self.CreateTestWin(item) + if item != self.root: self.ShowTestWindow(item) else: evt.Skip() - # (re)create test window - def CreateTestWin(self, node): + def CreateTestWin(self, item): global testWin # Create a window with this resource - xxx = self.GetPyData(node).treeObject() - if not xxx: return # if root selected, do nothing - # If noname element, display error - if not xxx.hasName or not xxx.name: - wxLogError("Can't display a noname element") - return + xxx = self.GetPyData(item).treeObject() # Close old window, remember where it was highLight = None if testWin: pos = testWin.GetPosition() - if node == testWin.item: + if item == testWin.item: # Remember highlight if same top-level window if testWin.highLight: highLight = testWin.highLight.item @@ -587,9 +705,8 @@ class XML_Tree(wxTreeCtrl): testWin = None else: pos = testWinPos - # Save in temporary file before activating - memFile = MemoryFile(tempfile.mktemp('xrc')) - #memFile = MemoryFile('core.xrc') # to write debug file + # Save in memory FS + memFile = MemoryFile('xxx.xrc') # Create partial XML file - faster for big files dom = minidom.Document() @@ -608,10 +725,9 @@ class XML_Tree(wxTreeCtrl): mainNode.removeChild(elem) dom.unlink() parent.replaceChild(elem, self.dummyNode) - memFile.close() # write to wxMemoryFS res = wxXmlResource('') - res.Load(memFile.name) + res.Load('memory:xxx.xrc') if xxx.className == 'wxFrame': # Create new frame testWin = wxPreFrame() @@ -624,12 +740,13 @@ class XML_Tree(wxTreeCtrl): if not testWin: testWin = wxFrame(frame, -1, 'Panel: ' + xxx.name, pos=pos) testWin.panel = res.LoadPanel(testWin, xxx.name) - testWin.SetSize(testWin.panel.GetSize()) + testWin.SetClientSize(testWin.panel.GetSize()) testWin.Show(true) elif xxx.className == 'wxDialog': # Create new frame testWin = res.LoadDialog(None, xxx.name) testWin.panel = testWin + testWin.Layout() testWin.SetPosition(pos) testWin.Show(true) elif xxx.className == 'wxMenuBar': @@ -639,11 +756,15 @@ class XML_Tree(wxTreeCtrl): testWin.menuBar = res.LoadMenuBar(xxx.name) testWin.SetMenuBar(testWin.menuBar) testWin.Show(true) - else: - wxLogMessage('No view for this element yet') - return - os.unlink(memFile.name) # remove tmp file - testWin.item = node + elif xxx.className == 'wxToolBar': + testWin = wxFrame(frame, -1, 'ToolBar: ' + xxx.name, pos=pos) + # Set status bar to display help + testWin.CreateStatusBar() + testWin.toolBar = res.LoadToolBar(testWin, xxx.name) + testWin.SetToolBar(testWin.toolBar) + testWin.Show(true) + wxMemoryFSHandler_RemoveFile('xxx.xrc') + testWin.item = item testWin.Connect(testWin.GetId(), -1, wxEVT_CLOSE_WINDOW, self.OnCloseTestWin) testWin.highLight = None if highLight and not tree.pendingHighLight: @@ -651,6 +772,7 @@ class XML_Tree(wxTreeCtrl): def OnCloseTestWin(self, evt): global testWin, testWinPos + self.SetItemBold(testWin.item, false) testWinPos = testWin.GetPosition() testWin.Destroy() testWin = None @@ -659,28 +781,35 @@ class XML_Tree(wxTreeCtrl): # True if next item should be inserted after current (vs. appended to it) def NeedInsert(self, item): xxx = self.GetPyData(item) - if not xxx: return false # root item + if item == self.root: return false # root item if self.ctrl: return true # if Ctrl pressed, always insert - if xxx.hasChildren and not self.ItemHasChildren(item): + if xxx.hasChildren and not self.GetChildrenCount(item, false): return false - return not (self.IsExpanded(item) and self.ItemHasChildren(item)) + return not (self.IsExpanded(item) and self.GetChildrenCount(item, false)) # Pull-down def OnRightDown(self, evt): + # select this item + pt = evt.GetPosition(); + item, flags = self.HitTest(pt) + if item.Ok() and flags & wxTREE_HITTEST_ONITEM: + self.SelectItem(item) + # Setup menu menu = wxMenu() - item = self.GetSelection() - if not item.IsOk(): + item = self.selection + if not item: menu.Append(pullDownMenu.ID_EXPAND, 'Expand', 'Expand tree') + menu.Append(pullDownMenu.ID_COLLAPSE, 'Collapse', 'Collapse tree') else: self.ctrl = evt.ControlDown() # save Ctrl state self.shift = evt.ShiftDown() # and Shift too m = wxMenu() # create menu needInsert = false - if item != self.GetRootItem(): needInsert = self.NeedInsert(item) - if item == self.GetRootItem() or \ - self.GetItemParent(item) == self.GetRootItem() and needInsert: + if item != self.root: needInsert = self.NeedInsert(item) + if item == self.root or \ + self.GetItemParent(item) == self.root and needInsert: m.Append(pullDownMenu.ID_NEW_PANEL, 'Panel', 'Create panel') m.Append(pullDownMenu.ID_NEW_DIALOG, 'Dialog', 'Create dialog') m.Append(pullDownMenu.ID_NEW_FRAME, 'Frame', 'Create frame') @@ -689,17 +818,21 @@ class XML_Tree(wxTreeCtrl): m.Append(pullDownMenu.ID_NEW_MENU_BAR, 'MenuBar', 'Create menubar') m.Append(pullDownMenu.ID_NEW_MENU, 'Menu', 'Create menu') else: - xxx = self.GetPyData(item) + xxx = self.GetPyData(item).treeObject() + # Check parent for possible child nodes if inserting sibling + if needInsert: xxx = xxx.parent if xxx.__class__ == xxxMenuBar: m.Append(pullDownMenu.ID_NEW_MENU, 'Menu', 'Create menu') - elif xxx.__class__ in [xxxToolBar, xxxTool]: + elif xxx.__class__ in [xxxToolBar, xxxTool] or \ + xxx.__class__ == xxxSeparator and xxx.parent.__class__ == xxxToolBar: SetMenu(m, pullDownMenu.toolBarControls) elif xxx.__class__ in [xxxMenu, xxxMenuItem]: SetMenu(m, pullDownMenu.menuControls) else: SetMenu(m, pullDownMenu.controls) - if not (xxx.isSizer or \ - xxx.parent and xxx.parent.isSizer): + if xxx.__class__ == xxxNotebook: + m.Enable(m.FindItem('sizer'), false) + elif not (xxx.isSizer or xxx.parent and xxx.parent.isSizer): m.Enable(pullDownMenu.ID_NEW_SPACER, false) # Select correct label for create menu if not needInsert: @@ -717,60 +850,35 @@ class XML_Tree(wxTreeCtrl): menu.AppendMenu(wxNewId(), 'Create Sibling', m, 'Create sibling after selected object') menu.AppendSeparator() + # Not using standart IDs because we don't want to show shortcuts menu.Append(wxID_CUT, 'Cut', 'Cut to the clipboard') menu.Append(wxID_COPY, 'Copy', 'Copy to the clipboard') - menu.Append(wxID_PASTE, 'Paste', 'Paste from the clipboard') + if self.ctrl and item != tree.root: + menu.Append(wxID_PASTE, 'Paste Sibling', + 'Paste from the clipboard as a sibling') + else: + menu.Append(wxID_PASTE, 'Paste', 'Paste from the clipboard') menu.Append(pullDownMenu.ID_DELETE, 'Delete', 'Delete object') - if item.IsOk() and self.ItemHasChildren(item): + if self.ItemHasChildren(item): menu.AppendSeparator() menu.Append(pullDownMenu.ID_EXPAND, 'Expand', 'Expand subtree') + menu.Append(pullDownMenu.ID_COLLAPSE, 'Collapse', 'Collapse subtree') self.PopupMenu(menu, evt.GetPosition()) menu.Destroy() - # Clear tree - def Clear(self): - self.DeleteAllItems() - # Add minimal structure - root = self.AddRoot('XML tree', self.rootImage) - self.Unselect() - if self.dom: self.dom.unlink() - self.dom = minidom.Document() - self.dummyNode = self.dom.createComment('dummy node') - # Create main node - self.mainNode = self.dom.createElement('resource') - self.dom.appendChild(self.mainNode) - # Apply changes def Apply(self, xxx, item): - if not xxx: return - # !!! Save undo info - if xxx.undo: xxx.undo.unlink() - xxx.undo = xxx.element.cloneNode(false) - if xxx.hasName: - name = panel.page1.FindWindowByName('data_name').GetValue() - if xxx.name != name: - xxx.name = name - xxx.element.setAttribute('name', name) - self.SetItemText(item, xxx.treeName()) - if xxx.hasChild: prefix = '_' - else: prefix = '' - for param, paramObj in xxx.params.items(): - paramWin = panel.FindWindowByName(prefix + 'data_' + param) - if not paramWin.modified: continue - value = paramWin.GetValue() - if param in xxx.specials: - xxx.setSpecial(param, value) - else: - paramObj.update(value) - if xxx.hasChild: - self.Apply(xxx.child, item) - else: - # Change tree icon for sizers - if isinstance(xxx, xxxBoxSizer): - self.SetItemImage(item, xxx.treeImage()) - # Set global modified state - frame.modified = true + panel.Apply() + # Update tree view + xxx = xxx.treeObject() + if xxx.hasName and self.GetItemText(item) != xxx.name: + self.SetItemText(item, xxx.treeName()) + # Change tree icon for sizers + if isinstance(xxx, xxxBoxSizer): + self.SetItemImage(item, xxx.treeImage()) + # Set global modified state + frame.modified = true class PullDownMenu: ID_NEW_PANEL = wxNewId() @@ -820,11 +928,13 @@ class PullDownMenu: ID_NEW_SEPARATOR = wxNewId() ID_NEW_LAST = wxNewId() ID_EXPAND = wxNewId() + ID_COLLAPSE = wxNewId() def __init__(self, parent): self.ID_DELETE = parent.ID_DELETE EVT_MENU_RANGE(parent, self.ID_NEW_PANEL, self.ID_NEW_LAST, parent.OnCreate) + EVT_MENU(parent, self.ID_COLLAPSE, parent.OnCollapse) EVT_MENU(parent, self.ID_EXPAND, parent.OnExpand) # We connect to tree, but process in frame EVT_MENU_HIGHLIGHT_ALL(tree, parent.OnPullDownHighlight) @@ -832,13 +942,22 @@ class PullDownMenu: ################################################################################ class Frame(wxFrame): - def __init__(self, size): - wxFrame.__init__(self, None, -1, '', size=size) + def __init__(self, pos, size): + global frame + frame = self + wxFrame.__init__(self, None, -1, '', pos, size) self.CreateStatusBar() + #self.SetIcon(wxIconFromXPMData(images.getIconData())) #icon = wxIconFromXPMData(images.getIconData()) - icon = wxIcon(os.path.join(sys.path[0], "xrced.ico"), wxBITMAP_TYPE_ICO) + icon = wxIcon(os.path.join(sys.path[0], 'xrced.ico'), wxBITMAP_TYPE_ICO) self.SetIcon(icon) + # Defaults + self.sashPos = 100 + self.panelX = self.panelY = -1 + self.panelWidth = 300 + self.panelHeight = 200 + # Make menus menuBar = wxMenuBar() @@ -853,7 +972,7 @@ class Frame(wxFrame): menu = wxMenu() menu.Append(wxID_UNDO, '&Undo\tCtrl-Z', 'Undo') - menu.Append(wxID_REDO, '&Redo\tCtrl-R', 'Redo') + menu.Append(wxID_REDO, '&Redo\tCtrl-Y', 'Redo') menu.AppendSeparator() menu.Append(wxID_CUT, 'Cut\tCtrl-X', 'Cut to the clipboard') menu.Append(wxID_COPY, '&Copy\tCtrl-C', 'Copy to the clipboard') @@ -863,8 +982,15 @@ class Frame(wxFrame): menuBar.Append(menu, '&Edit') menu = wxMenu() + self.ID_EMBED_PANEL = wxNewId() + menu.Append(self.ID_EMBED_PANEL, '&Embed Panel', + 'Toggle embedding properties panel in the main window', true) + menu.Check(self.ID_EMBED_PANEL, conf.embedPanel) + menu.AppendSeparator() + self.ID_TEST = wxNewId() + menu.Append(self.ID_TEST, '&Test\tF5', 'Test window') self.ID_REFRESH = wxNewId() - menu.Append(self.ID_REFRESH, '&Refresh\tCtrl-R', 'Refresh view') + menu.Append(self.ID_REFRESH, '&Refresh\tCtrl-R', 'Refresh test window') self.ID_AUTO_REFRESH = wxNewId() menu.Append(self.ID_AUTO_REFRESH, '&Auto-refresh\tCtrl-A', 'Toggle auto-refresh mode', true) @@ -872,7 +998,9 @@ class Frame(wxFrame): menuBar.Append(menu, '&View') menu = wxMenu() - menu.Append(wxID_ABOUT, 'About...', 'About XCRed') + menu.Append(wxID_ABOUT, '&About...', 'About XCRed') + self.ID_README = wxNewId() + menu.Append(self.ID_README, '&Readme...', 'View the README file') if debug: self.ID_DEBUG_CMD = wxNewId() menu.Append(self.ID_DEBUG_CMD, 'CMD', 'Python command line') @@ -888,15 +1016,18 @@ class Frame(wxFrame): tb.AddSimpleTool(wxID_NEW, images.getNewBitmap(), 'New', 'New file') tb.AddSimpleTool(wxID_OPEN, images.getOpenBitmap(), 'Open', 'Open file') tb.AddSimpleTool(wxID_SAVE, images.getSaveBitmap(), 'Save', 'Save file') - tb.AddSeparator() + tb.AddControl(wxStaticLine(tb, -1, size=(-1,23), style=wxLI_VERTICAL)) tb.AddSimpleTool(wxID_CUT, images.getCutBitmap(), 'Cut', 'Cut') tb.AddSimpleTool(wxID_COPY, images.getCopyBitmap(), 'Copy', 'Copy') tb.AddSimpleTool(wxID_PASTE, images.getPasteBitmap(), 'Paste', 'Paste') - tb.AddSeparator() + tb.AddControl(wxStaticLine(tb, -1, size=(-1,23), style=wxLI_VERTICAL)) + tb.AddSimpleTool(self.ID_TEST, images.getTestBitmap(), 'Test', 'Test window') tb.AddSimpleTool(self.ID_REFRESH, images.getRefreshBitmap(), 'Refresh', 'Refresh view') tb.AddSimpleTool(self.ID_AUTO_REFRESH, images.getAutoRefreshBitmap(), 'Auto-refresh', 'Toggle auto-refresh mode', true) + if wxGetOsVersion()[1] == 1: + tb.AddSeparator() # otherwise auto-refresh sticks in status line tb.ToggleTool(self.ID_AUTO_REFRESH, conf.autoRefresh) tb.Realize() self.tb = tb @@ -915,16 +1046,21 @@ class Frame(wxFrame): EVT_MENU(self, wxID_PASTE, self.OnPaste) EVT_MENU(self, self.ID_DELETE, self.OnDelete) # View + EVT_MENU(self, self.ID_EMBED_PANEL, self.OnEmbedPanel) + EVT_MENU(self, self.ID_TEST, self.OnTest) EVT_MENU(self, self.ID_REFRESH, self.OnRefresh) EVT_MENU(self, self.ID_AUTO_REFRESH, self.OnAutoRefresh) # Help EVT_MENU(self, wxID_ABOUT, self.OnAbout) + EVT_MENU(self, self.ID_README, self.OnReadme) # Update events EVT_UPDATE_UI(self, wxID_CUT, self.OnUpdateUI) EVT_UPDATE_UI(self, wxID_COPY, self.OnUpdateUI) EVT_UPDATE_UI(self, wxID_PASTE, self.OnUpdateUI) EVT_UPDATE_UI(self, self.ID_DELETE, self.OnUpdateUI) + EVT_UPDATE_UI(self, self.ID_TEST, self.OnUpdateUI) + EVT_UPDATE_UI(self, self.ID_REFRESH, self.OnUpdateUI) # Build interface sizer = wxBoxSizer(wxVERTICAL) @@ -936,11 +1072,27 @@ class Frame(wxFrame): global tree tree = XML_Tree(splitter, -1) sys.modules['xxx'].tree = tree + # !!! frame styles are broken + # Miniframe for not embedded mode + miniFrame = wxFrame(self, -1, 'Properties Panel', + (conf.panelX, conf.panelY), + (conf.panelWidth, conf.panelHeight)) + self.miniFrame = miniFrame + sizer2 = wxBoxSizer() + miniFrame.SetAutoLayout(true) + miniFrame.SetSizer(sizer2) + EVT_CLOSE(self.miniFrame, self.OnCloseMiniFrame) # Create panel for parameters global panel - panel = Panel(splitter) - # Set plitter windows - splitter.SplitVertically(tree, panel, conf.sashPos) + if conf.embedPanel: + panel = Panel(splitter) + # Set plitter windows + splitter.SplitVertically(tree, panel, conf.sashPos) + else: + panel = Panel(miniFrame) + sizer2.Add(panel, 1, wxEXPAND) + miniFrame.Show(true) + splitter.Initialize(tree) sizer.Add(splitter, 1, wxEXPAND) self.SetAutoLayout(true) self.SetSizer(sizer) @@ -1004,7 +1156,6 @@ class Frame(wxFrame): (pullDownMenu.ID_NEW_SCROLL_BAR, 'ScrollBar', 'Create scroll bar'), (pullDownMenu.ID_NEW_TREE_CTRL, 'TreeCtrl', 'Create tree control'), (pullDownMenu.ID_NEW_LIST_CTRL, 'ListCtrl', 'Create list control'), - (pullDownMenu.ID_NEW_CHECK_LIST, 'CheckList', 'Create check list control'), (pullDownMenu.ID_NEW_HTML_WINDOW, 'HtmlWindow', 'Create HTML window'), (pullDownMenu.ID_NEW_CALENDAR, 'Calendar', 'Create calendar control'), (pullDownMenu.ID_NEW_PANEL, 'Panel', 'Create panel'), @@ -1022,6 +1173,8 @@ class Frame(wxFrame): (pullDownMenu.ID_NEW_RADIO_BOX, 'RadioBox', 'Create radio box'), (pullDownMenu.ID_NEW_COMBO_BOX, 'ComboBox', 'Create combo box'), (pullDownMenu.ID_NEW_LIST_BOX, 'ListBox', 'Create list box'), + (pullDownMenu.ID_NEW_CHECK_LIST, 'CheckListBox', + 'Create check list control'), ], ['sizer', 'Sizers', (pullDownMenu.ID_NEW_BOX_SIZER, 'BoxSizer', 'Create box sizer'), @@ -1041,6 +1194,31 @@ class Frame(wxFrame): pullDownMenu.toolBarControls = [ (pullDownMenu.ID_NEW_TOOL, 'Tool', 'Create tool'), (pullDownMenu.ID_NEW_SEPARATOR, 'Separator', 'Create separator'), + ['control', 'Various controls', + (pullDownMenu.ID_NEW_STATIC_TEXT, 'Label', 'Create static label'), + (pullDownMenu.ID_NEW_STATIC_LINE, 'Line', 'Create static line'), + (pullDownMenu.ID_NEW_TEXT_CTRL, 'TextBox', 'Create text box control'), + (pullDownMenu.ID_NEW_CHOICE, 'Choice', 'Create choice control'), + (pullDownMenu.ID_NEW_SLIDER, 'Slider', 'Create slider control'), + (pullDownMenu.ID_NEW_GAUGE, 'Gauge', 'Create gauge control'), + (pullDownMenu.ID_NEW_SCROLL_BAR, 'ScrollBar', 'Create scroll bar'), + (pullDownMenu.ID_NEW_LIST_CTRL, 'ListCtrl', 'Create list control'), + ], + ['button', 'Buttons', + (pullDownMenu.ID_NEW_BUTTON, 'Button', 'Create button'), + (pullDownMenu.ID_NEW_BITMAP_BUTTON, 'BitmapButton', 'Create bitmap button'), + (pullDownMenu.ID_NEW_RADIO_BUTTON, 'RadioButton', 'Create radio button'), + (pullDownMenu.ID_NEW_SPIN_BUTTON, 'SpinButton', 'Create spin button'), + ], + ['box', 'Boxes', + (pullDownMenu.ID_NEW_STATIC_BOX, 'StaticBox', 'Create static box'), + (pullDownMenu.ID_NEW_CHECK_BOX, 'CheckBox', 'Create check box'), + (pullDownMenu.ID_NEW_RADIO_BOX, 'RadioBox', 'Create radio box'), + (pullDownMenu.ID_NEW_COMBO_BOX, 'ComboBox', 'Create combo box'), + (pullDownMenu.ID_NEW_LIST_BOX, 'ListBox', 'Create list box'), + (pullDownMenu.ID_NEW_CHECK_LIST, 'CheckListBox', + 'Create check list control'), + ], ] # Initialize @@ -1062,9 +1240,13 @@ class Frame(wxFrame): self.SetStatusText('Loading...') wxYield() wxBeginBusyCursor() - self.Open(path) + try: + self.Open(path) + self.SetStatusText('Data loaded') + except: + self.SetStatusText('Failed') + raise wxEndBusyCursor() - self.SetStatusText('Ready') dlg.Destroy() def OnSaveOrSaveAs(self, evt): @@ -1085,16 +1267,20 @@ class Frame(wxFrame): self.SetStatusText('Saving...') wxYield() wxBeginBusyCursor() - self.Save(path) - self.dataFile = path + try: + self.Save(path) + self.dataFile = path + self.SetStatusText('Data saved') + except IOError: + self.SetStatusText('Failed') wxEndBusyCursor() - self.SetStatusText('Ready') def OnExit(self, evt): self.Close() def OnUndo(self, evt): print '*** being implemented' + return print self.lastOp, self.undo if self.lastOp == 'DELETE': parent, prev, elem = self.undo @@ -1107,7 +1293,7 @@ class Frame(wxFrame): print '*** being implemented' def OnCut(self, evt): - selected = tree.GetSelection() + selected = tree.selection # Undo info self.lastOp = 'CUT' self.undo = [tree.GetItemParent(selected), tree.GetPrevSibling(selected)] @@ -1128,14 +1314,16 @@ class Frame(wxFrame): tree.Unselect() panel.Clear() self.modified = true + self.SetStatusText('Removed to clipboard') def OnCopy(self, evt): - selected = tree.GetSelection() + selected = tree.selection xxx = tree.GetPyData(selected) self.clipboard = xxx.element.cloneNode(true) + self.SetStatusText('Copied') def OnPaste(self, evt): - selected = tree.GetSelection() + selected = tree.selection appendChild = not tree.NeedInsert(selected) xxx = tree.GetPyData(selected) if not appendChild: @@ -1148,7 +1336,7 @@ class Frame(wxFrame): appendChild = true selected = tree.GetItemParent(selected) # Expanded container (must have children) - elif tree.IsExpanded(selected) and tree.ItemHasChildren(selected): + elif tree.IsExpanded(selected) and tree.GetChildrenCount(selected, false): appendChild = false nextItem = tree.GetFirstChild(selected, 0)[0] parentLeaf = selected @@ -1157,56 +1345,71 @@ class Frame(wxFrame): parent = tree.GetPyData(selected) else: parent = tree.GetPyData(parentLeaf) - if parent and parent.hasChild: parent = parent.child + if parent.hasChild: parent = parent.child # Create a copy of clipboard element elem = self.clipboard.cloneNode(true) # Tempopary xxx object to test things xxx = MakeXXXFromDOM(parent, elem) - className = xxx.treeObject().className - # Check parent and child relationships - # Parent is sizer or notebook, child is of wrong class or - # parent is normal window, child is child container: detach child + + # Check compatibility + error = false + # Top-level + x = xxx.treeObject() + if x.__class__ in [xxxDialog, xxxFrame, xxxMenuBar, xxxToolBar]: + if parent.__class__ != xxxMainNode: error = true + elif x.__class__ == xxxSpacer: + if not parent.isSizer: error = true + elif x.__class__ == xxxSeparator: + if not parent.__class__ in [xxxMenu, xxxToolBar]: error = true + elif x.__class__ == xxxTool: + if parent.__class__ != xxxToolBar: error = true + elif x.__class__ == xxxMenuItem: + if not parent.__class__ in [xxxMenuBar, xxxMenu]: error = true + elif x.isSizer and parent.__class__ == xxxNotebook: error = true + else: # normal controls can be almost anywhere + if parent.__class__ == xxxMainNode or \ + parent.__class__ in [xxxMenuBar, xxxMenu]: error = true + if error: + if parent.__class__ == xxxMainNode: parentClass = 'root' + else: parentClass = parent.className + wxLogError('Incompatible parent/child: parent is %s, child is %s!' % + (parentClass, x.className)) + return + + # Check parent and child relationships. + # If parent is sizer or notebook, child is of wrong class or + # parent is normal window, child is child container then detach child. isChildContainer = isinstance(xxx, xxxChildContainer) - if not parent and isChildContainer or \ - (parent.isSizer and not isinstance(xxx, xxxSizerItem)) or \ + if isChildContainer and \ + ((parent.isSizer and not isinstance(xxx, xxxSizerItem)) or \ (isinstance(parent, xxxNotebook) and not isinstance(xxx, xxxNotebookPage)) or \ - (not parent.isSizer and not isinstance(parent, xxxNotebook) and \ - isChildContainer): - if isChildContainer: - elem.removeChild(xxx.child.element) # detach child - elem.unlink() # delete child container - elem = xxx.child.element # replace - # This should help garbage collection (!!! maybe not needed?) - xxx.child.parent = None - xxx.child = None - if parent: - # Parent is sizer or notebook, child is not child container - if parent.isSizer and not isChildContainer and \ - not isinstance(xxx, xxxSpacer): - # Create sizer item element - sizerItemElem = MakeEmptyDOM('sizeritem') - sizerItemElem.appendChild(elem) - elem = sizerItemElem - elif isinstance(parent, xxxNotebook) and not isChildContainer: - pageElem = MakeEmptyDOM('notebookpage') - pageElem.appendChild(elem) - elem = pageElem + not (parent.isSizer or isinstance(parent, xxxNotebook))): + elem.removeChild(xxx.child.element) # detach child + elem.unlink() # delete child container + elem = xxx.child.element # replace + # This may help garbage collection + xxx.child.parent = None + isChildContainer = false + # Parent is sizer or notebook, child is not child container + if parent.isSizer and not isChildContainer and not isinstance(xxx, xxxSpacer): + # Create sizer item element + sizerItemElem = MakeEmptyDOM('sizeritem') + sizerItemElem.appendChild(elem) + elem = sizerItemElem + elif isinstance(parent, xxxNotebook) and not isChildContainer: + pageElem = MakeEmptyDOM('notebookpage') + pageElem.appendChild(elem) + elem = pageElem xxx = MakeXXXFromDOM(parent, elem) # Figure out if we must append a new child or sibling if appendChild: - if parent: node = parent.element - else: node = tree.mainNode - node.appendChild(elem) + parent.element.appendChild(elem) newItem = tree.AppendItem(selected, xxx.treeName(), image=xxx.treeImage(), data=wxTreeItemData(xxx)) else: node = tree.GetPyData(nextItem).element - if parent: - elemParent = parent.element - else: - elemParent = tree.mainNode - elemParent.insertBefore(elem, node) + parent.element.insertBefore(elem, node) # Inserting before is difficult, se we insert after or first child newItem = tree.InsertItem(parentLeaf, selected, xxx.treeName(), image=xxx.treeImage(), data=wxTreeItemData(xxx)) @@ -1230,9 +1433,10 @@ class Frame(wxFrame): else: tree.pendingHighLight = None self.modified = true + self.SetStatusText('Pasted') def OnDelete(self, evt): - selected = tree.GetSelection() + selected = tree.selection # Undo info self.lastOp = 'DELETE' self.undo = [tree.GetItemParent(selected), tree.GetPrevSibling(selected)] @@ -1249,17 +1453,41 @@ class Frame(wxFrame): if testWin.highLight: testWin.highLight.Remove() tree.needUpdate = true xnode = tree.RemoveLeaf(selected) - self.undo.append(xnode.cloneNode(true)) + # !!! cloneNode is broken, or something is wrong +# self.undo.append(xnode.cloneNode(true)) xnode.unlink() tree.pendingHighLight = None tree.Unselect() panel.Clear() self.modified = true + self.SetStatusText('Deleted') + + def OnEmbedPanel(self, evt): + conf.embedPanel = evt.IsChecked() + if conf.embedPanel: + # Remember last dimentions + self.panelWidth, self.panelHeight = panel.GetSize() + panel.Reparent(self.splitter) + self.miniFrame.GetSizer().RemoveWindow(panel) + self.splitter.SplitVertically(tree, panel, self.sashPos) + self.miniFrame.Show(false) + else: + self.sashPos = self.splitter.GetSashPosition() + self.splitter.Unsplit(panel) + sizer = self.miniFrame.GetSizer() + panel.Reparent(self.miniFrame) + panel.Show(true) + sizer.Add(panel, 1, wxEXPAND) + self.miniFrame.Show(true) + self.miniFrame.SetSize((self.panelWidth, self.panelHeight)) + + def OnTest(self, evt): + tree.ShowTestWindow(tree.selection) def OnRefresh(self, evt): # If modified, apply first - selection = tree.GetSelection() - if selection.IsOk(): + selection = tree.selection + if selection: xxx = tree.GetPyData(selection) if xxx and panel.IsModified(): tree.Apply(xxx, selection) @@ -1280,8 +1508,17 @@ class Frame(wxFrame): dlg.ShowModal() dlg.Destroy() + def OnReadme(self, evt): + from wxPython.lib.dialogs import wxScrolledMessageDialog + text = open(os.path.join(sys.path[0], 'README'), 'r').read() + dlg = wxScrolledMessageDialog(self, text, "XRCed README") + dlg.ShowModal() + dlg.Destroy() + + # Simple emulation of python command line def OnDebugCMD(self, evt): + import traceback while 1: try: exec raw_input('C:\> ') @@ -1296,52 +1533,65 @@ class Frame(wxFrame): print msg def OnCreate(self, evt): - selected = tree.GetSelection() + selected = tree.selection appendChild = not tree.NeedInsert(selected) xxx = tree.GetPyData(selected) if not appendChild: - # If has next item, insert, else append to parent - nextItem = tree.GetNextSibling(selected) - if nextItem.IsOk(): - # Insert before nextItem - parentLeaf = tree.GetItemParent(selected) - else: # last child: change selected to parent - appendChild = true - selected = tree.GetItemParent(selected) + # If insert before + if tree.shift: + # If has previous item, insert after it, else append to parent + nextItem = selected + selected = tree.GetPrevSibling(selected) + if selected: + # Insert before nextItem + parentLeaf = tree.GetItemParent(selected) + else: # last child: change selected to parent + parentLeaf = selected = tree.GetItemParent(nextItem) + else: + # If has next item, insert, else append to parent + nextItem = tree.GetNextSibling(selected) + if nextItem.IsOk(): + # Insert before nextItem + parentLeaf = tree.GetItemParent(selected) + else: # last child: change selected to parent + appendChild = true + selected = tree.GetItemParent(selected) # Expanded container (must have children) - elif tree.IsExpanded(selected) and tree.ItemHasChildren(selected): - appendChild = false - nextItem = tree.GetFirstChild(selected, 0)[0] - parentLeaf = selected + else: + # Can't use HasChildren because root always has + if tree.shift and tree.IsExpanded(selected) \ + and tree.GetChildrenCount(selected, false): + appendChild = false + nextItem = tree.GetFirstChild(selected, 0)[0] + parentLeaf = selected # Parent should be tree element or None if appendChild: parent = tree.GetPyData(selected) else: parent = tree.GetPyData(parentLeaf) - if parent and parent.hasChild: parent = parent.child + if parent.hasChild: parent = parent.child # Create element className = self.createMap[evt.GetId()] xxx = MakeEmptyXXX(parent, className) + + # Set default name for top-level windows + if parent.__class__ == xxxMainNode: + cl = xxx.treeObject().__class__ + frame.maxIDs[cl] += 1 + xxx.treeObject().name = '%s%d' % (defaultIDs[cl], frame.maxIDs[cl]) + xxx.treeObject().element.setAttribute('name', xxx.treeObject().name) + # Figure out if we must append a new child or sibling elem = xxx.element if appendChild: - if parent: node = parent.element - else: node = tree.mainNode # Insert newline for debug purposes - node.appendChild(tree.dom.createTextNode('\n')) - node.appendChild(elem) + parent.element.appendChild(elem) newItem = tree.AppendItem(selected, xxx.treeName(), image=xxx.treeImage(), data=wxTreeItemData(xxx)) else: node = tree.GetPyData(nextItem).element - if parent: - elemParent = parent.element - else: - elemParent = tree.mainNode - elemParent.insertBefore(tree.dom.createTextNode('\n'), node) - elemParent.insertBefore(elem, node) - # Inserting before is difficult, se we insert after or first child + parent.element.insertBefore(elem, node) newItem = tree.InsertItem(parentLeaf, selected, xxx.treeName(), image=xxx.treeImage(), data=wxTreeItemData(xxx)) @@ -1358,11 +1608,13 @@ class Frame(wxFrame): else: tree.pendingHighLight = None + # Expand/collapse subtree def OnExpand(self, evt): - if tree.GetSelection().IsOk(): - tree.ExpandAll(tree.GetSelection()) - else: - tree.ExpandAll(tree.GetRootItem()) + if tree.selection: tree.ExpandAll(tree.selection) + else: tree.ExpandAll(tree.root) + def OnCollapse(self, evt): + if tree.selection: tree.CollapseAll(tree.selection) + else: tree.CollapseAll(tree.root) def OnPullDownHighlight(self, evt): menuId = evt.GetMenuId() @@ -1375,14 +1627,13 @@ class Frame(wxFrame): def OnUpdateUI(self, evt): if evt.GetId() in [wxID_CUT, wxID_COPY, self.ID_DELETE]: - enable = tree.GetSelection().IsOk() and \ - tree.GetSelection() != tree.GetRootItem() - evt.Enable(enable) + evt.Enable(tree.selection != tree.root) elif evt.GetId() == wxID_PASTE: - enable = tree.GetSelection().IsOk() and \ - tree.GetSelection() != tree.GetRootItem() and \ - self.clipboard != None - evt.Enable(enable) + evt.Enable((self.clipboard and tree.selection) != None) + elif evt.GetId() == self.ID_TEST: + evt.Enable(tree.selection != tree.root) + elif evt.GetId() == self.ID_REFRESH: + evt.Enable(testWin != None) def OnIdle(self, evt): if tree.needUpdate: @@ -1396,13 +1647,27 @@ class Frame(wxFrame): else: evt.Skip() + # We don't let close panel window + def OnCloseMiniFrame(self, evt): + return + def OnCloseWindow(self, evt): if not self.AskSave(): return if testWin: testWin.Destroy() # Destroy cached windows - for w in panel.styleCache.values(): w.Destroy() + panel.cacheParent.Destroy() +# for w in panel.styleCache.values(): w.Destroy() + if not panel.GetPageCount() == 2: + panel.page2.Destroy() + conf.x, conf.y = self.GetPosition() conf.width, conf.height = self.GetSize() - conf.sashPos = self.splitter.GetSashPosition() + if conf.embedPanel: + conf.sashPos = self.splitter.GetSashPosition() + conf.panelWidth, conf.panelHeight = self.panelWidth, self.panelHeight + else: + conf.sashPos = self.sashPos + conf.panelX, conf.panelY = self.miniFrame.GetPosition() + conf.panelWidth, conf.panelHeight = self.miniFrame.GetSize() evt.Skip() def Clear(self): @@ -1410,13 +1675,17 @@ class Frame(wxFrame): self.clipboard = None self.modified = false panel.SetModified(false) - panel.Clear() tree.Clear() + panel.Clear() global testWin if testWin: testWin.Destroy() testWin = None self.SetTitle(progname) + # Numbers for new controls + self.maxIDs = {} + self.maxIDs[xxxPanel] = self.maxIDs[xxxDialog] = self.maxIDs[xxxFrame] = \ + self.maxIDs[xxxMenuBar] = self.maxIDs[xxxMenu] = self.maxIDs[xxxToolBar] = 0 def Open(self, path): # Try to read the file @@ -1429,19 +1698,42 @@ class Frame(wxFrame): self.dataFile = path self.SetTitle(progname + ': ' + os.path.basename(path)) except: - wxLogError('Error reading file: ' + path) + wxLogError('Error reading file: %s' % path) raise + def Indent(self, node, indent = 0): + # Copy child list because it will change soon + children = node.childNodes[:] + # Main node doesn't need to be indented + if indent: + text = self.domCopy.createTextNode('\n' + ' ' * indent) + node.parentNode.insertBefore(text, node) + if children: + # Append newline after last child, except for text nodes + if children[-1].nodeType == minidom.Node.ELEMENT_NODE: + text = self.domCopy.createTextNode('\n' + ' ' * indent) + node.appendChild(text) + # Indent children which are elements + for n in children: + if n.nodeType == minidom.Node.ELEMENT_NODE: + self.Indent(n, indent + 2) + def Save(self, path): try: + # Apply changes self.OnRefresh(wxCommandEvent()) - memFile = MemoryFile(path) - tree.dom.writexml(memFile) - memFile.close() + f = open(path, 'w') + # Make temporary copy + self.domCopy = domCopy = tree.dom.cloneNode(true) + self.Indent(domCopy.getElementsByTagName('resource')[0]) + domCopy.writexml(f) +# domCopy.unlink() + self.domCopy = None + f.close() self.modified = false panel.SetModified(false) except: - wxLogError('Error writing file: ' + path) + wxLogError('Error writing file: %s' % path) raise def AskSave(self): @@ -1463,36 +1755,80 @@ class Frame(wxFrame): ################################################################################ +def usage(): + print >> sys.stderr, 'usage: xrced [-dvh] [file]' + class App(wxApp): def OnInit(self): - self.SetAppName("xrced") + global debug, verbose + # Process comand-line + try: + opts, args = getopt.getopt(sys.argv[1:], 'dvh') + except getopt.GetoptError: + print >> sys.stderr, 'Unknown option' + usage() + sys.exit(1) + for o,a in opts: + if o == '-h': + usage() + sys.exit(0) + elif o == '-d': + debug = true + elif o == '-v': + print 'XRCed version', version + sys.exit(0) + + self.SetAppName('xrced') # Settings global conf - # !!! wxConfigBase_Get doesn't seem to work - conf = wxConfig(style=wxCONFIG_USE_LOCAL_FILE) + conf = wxConfig(style = wxCONFIG_USE_LOCAL_FILE) conf.autoRefresh = conf.ReadInt('autorefresh', true) + pos = conf.ReadInt('x', -1), conf.ReadInt('y', -1) size = conf.ReadInt('width', 800), conf.ReadInt('height', 600) + conf.embedPanel = conf.ReadInt('embedPanel', true) conf.sashPos = conf.ReadInt('sashPos', 200) + if not conf.embedPanel: + conf.panelX = conf.ReadInt('panelX', -1) + conf.panelY = conf.ReadInt('panelY', -1) + else: + conf.panelX = conf.panelY = -1 + conf.panelWidth = conf.ReadInt('panelWidth', 200) + conf.panelHeight = conf.ReadInt('panelHeight', 200) + conf.panic = not conf.HasEntry('nopanic') # Add handlers wxFileSystem_AddHandler(wxMemoryFSHandler()) wxInitAllImageHandlers() # Create main frame - global frame - frame = self.frame = Frame(size) - self.frame.Show(true) + frame = Frame(pos, size) + frame.Show(true) # Load resources from XRC file (!!! should be transformed to .py later?) sys.modules['params'].frame = frame frame.res = wxXmlResource('') frame.res.Load(os.path.join(sys.path[0], 'xrced.xrc')) + + # Load file after showing + if args: + conf.panic = false + frame.open = frame.Open(args[0]) + return true def OnExit(self): # Write config wc = wxConfigBase_Get() wc.WriteInt('autorefresh', conf.autoRefresh) + wc.WriteInt('x', conf.x) + wc.WriteInt('y', conf.y) wc.WriteInt('width', conf.width) wc.WriteInt('height', conf.height) + wc.WriteInt('embedPanel', conf.embedPanel) + if not conf.embedPanel: + wc.WriteInt('panelX', conf.panelX) + wc.WriteInt('panelY', conf.panelY) wc.WriteInt('sashPos', conf.sashPos) + wc.WriteInt('panelWidth', conf.panelWidth) + wc.WriteInt('panelHeight', conf.panelHeight) + wc.WriteInt('nopanic', 1) wc.Flush() def main(): diff --git a/wxPython/tools/XRCed/xrced.sh b/wxPython/tools/XRCed/xrced.sh new file mode 100644 index 0000000000..70f983bb14 --- /dev/null +++ b/wxPython/tools/XRCed/xrced.sh @@ -0,0 +1 @@ +python2.2 YOUR_PATH_TO_XRCED/xrced.py $* diff --git a/wxPython/tools/XRCed/xrced.xrc b/wxPython/tools/XRCed/xrced.xrc index ee21945bfb..1a9be61a7d 100644 --- a/wxPython/tools/XRCed/xrced.xrc +++ b/wxPython/tools/XRCed/xrced.xrc @@ -1,2 +1,167 @@ -Content250,300wxVERTICALwxHORIZONTALwxTOP|wxBOTTOM|wxLEFT|wxEXPAND5wxVERTICALwxBOTTOM510,20wxBOTTOM5wxALL|wxEXPAND5wxEXPANDwxEXPANDwxHORIZONTAL1wxRIGHT10wxALL|wxALIGN_CENTRE_HORIZONTAL10 \ No newline at end of file + + + Content + 250,300 + + wxVERTICAL + + + wxHORIZONTAL + + + + + + wxTOP|wxBOTTOM|wxLEFT|wxEXPAND + 5 + + + + wxVERTICAL + + + + + wxBOTTOM + 5 + + + + + + + + 10,20 + + + + + + + wxBOTTOM + 5 + + + + + + + + wxALL|wxEXPAND + 5 + + + + wxEXPAND + + + + wxEXPAND + + + + wxHORIZONTAL + + + + 1 + + wxRIGHT + 10 + + + + + + + + wxALL|wxALIGN_CENTRE_HORIZONTAL + 10 + + + + + + Content + 250,300 + + wxVERTICAL + + + wxHORIZONTAL + + + + + + wxTOP|wxBOTTOM|wxLEFT|wxEXPAND + 5 + + + + wxVERTICAL + + + + + wxBOTTOM + 5 + + + + + + + + 10,20 + + + + + + + wxBOTTOM + 5 + + + + + + + + wxALL|wxEXPAND + 5 + + + + wxEXPAND + + + + wxEXPAND + + + + wxHORIZONTAL + + + + 1 + + wxRIGHT + 10 + + + + + + + + wxALL|wxALIGN_CENTRE_HORIZONTAL + 10 + + + + + \ No newline at end of file diff --git a/wxPython/tools/XRCed/xxx.py b/wxPython/tools/XRCed/xxx.py index 47d8b0a03f..0435ea5737 100644 --- a/wxPython/tools/XRCed/xxx.py +++ b/wxPython/tools/XRCed/xxx.py @@ -51,7 +51,7 @@ class xxxParamContent: text = n.childNodes[0] # first child must be text node assert text.nodeType == minidom.Node.TEXT_NODE l.append(text) - data.append(text.data) + data.append(str(text.data)) else: # remove other node.removeChild(n) n.unlink() @@ -61,7 +61,8 @@ class xxxParamContent: def update(self, value): # If number if items is not the same, recreate children if len(value) != len(self.l): # remove first if number of items has changed - for n in self.node.childNodes: + childNodes = self.node.childNodes[:] + for n in childNodes: self.node.removeChild(n) l = [] for str in value: @@ -70,11 +71,59 @@ class xxxParamContent: itemElem.appendChild(itemText) self.node.appendChild(itemElem) l.append(itemText) + self.l = l else: for i in range(len(value)): self.l[i].data = value[i] self.data = value +# Content parameter for checklist +class xxxParamContentCheckList: + def __init__(self, node): + self.node = node + data, l = [], [] # data is needed to quicker value retrieval + nodes = node.childNodes[:] # make a copy of the child list + for n in nodes: + if n.nodeType == minidom.Node.ELEMENT_NODE: + assert n.tagName == 'item', 'bad content content' + checked = n.getAttribute('checked') + if not n.hasChildNodes(): + # If does not have child nodes, create empty text node + text = tree.dom.createTextNode('') + node.appendChild(text) + else: + # !!! normalize? + text = n.childNodes[0] # first child must be text node + assert text.nodeType == minidom.Node.TEXT_NODE + l.append((text, n)) + data.append((str(text.data), int(checked))) + else: # remove other + node.removeChild(n) + n.unlink() + self.l, self.data = l, data + def value(self): + return self.data + def update(self, value): + # If number if items is not the same, recreate children + if len(value) != len(self.l): # remove first if number of items has changed + childNodes = self.node.childNodes[:] + for n in childNodes: + self.node.removeChild(n) + l = [] + for (s,ch) in value: + itemElem = tree.dom.createElement('item') + itemElem.setAttribute('checked', str(ch)) + itemText = tree.dom.createTextNode(s) + itemElem.appendChild(itemText) + self.node.appendChild(itemElem) + l.append((itemText, itemElem)) + self.l = l +# else: +# for i in range(len(value)): +# self.l[i][0].data = value[i] +# self.l[i][1].setAttributedata = value[i] + self.data = value + ################################################################################ # Classes to interface DOM objects @@ -84,6 +133,7 @@ class xxxObject: hasStyle = true # almost everyone hasName = true # has name attribute? isSizer = hasChild = false + allParams = None # Some nodes have no parameters # Style parameters (all optional) styles = ['fg', 'bg', 'font', 'enabled', 'focused', 'hidden', 'tooltip'] # Special parameters @@ -121,7 +171,10 @@ class xxxObject: elif tag in self.specials: self.special(tag, node) elif tag == 'content': - self.params[tag] = xxxParamContent(node) + if self.className == 'wxCheckList': + self.params[tag] = xxxParamContentCheckList(node) + else: + self.params[tag] = xxxParamContent(node) elif tag == 'font': # has children self.params[tag] = xxxParamFont(self, node) else: # simple parameter @@ -130,52 +183,6 @@ class xxxObject: # Remove all other nodes element.removeChild(node) node.unlink() - # Generate HTML - def generateHtml(self, prefix=''): - SetCurrentXXX(self) - html = '' % self.className - # Has id (name) attribute - if self.hasName: - html += """\ -""" - html += '
\ -%s - -

' - html += '\n' - # Add required parameters - for param in self.allParams: - # Add checkbox or just text - if param in self.required: - html += '' % param - else: # optional parameter - html += """\ - -""" % (paramIDs[param], prefix + 'check_' + param, param) - # Get parameter type - try: - # Local or overriden type - typeClass = self.paramDict[param].__name__ - except KeyError: - try: - # Standart type - typeClass = paramDict[param].__name__ - except KeyError: - # Default - typeClass = 'ParamText' - html += """\ - -""" % (typeClass, -1, prefix + 'data_' + param) - html += '
%s:
- - - - -%s: - - -
\n' - return html # Returns real tree object def treeObject(self): if self.hasChild: return self.child @@ -229,8 +236,12 @@ class xxxParamFont(xxxParam): class xxxContainer(xxxObject): hasChildren = true +# Special class for root node +class xxxMainNode(xxxContainer): + hasStyle = hasName = false + ################################################################################ -# Top-level windwos +# Top-level windwows class xxxPanel(xxxContainer): allParams = ['pos', 'size', 'style'] @@ -347,9 +358,6 @@ class xxxListCtrl(xxxObject): 'wxLC_USER_TEXT', 'wxLC_EDIT_LABELS', 'wxLC_NO_HEADER', 'wxLC_SINGLE_SEL', 'wxLC_SORT_ASCENDING', 'wxLC_SORT_DESCENDING'] -# !!! temporary -xxxCheckList = xxxListCtrl - class xxxTreeCtrl(xxxObject): allParams = ['pos', 'size', 'style'] winStyles = ['wxTR_HAS_BUTTONS', 'wxTR_NO_LINES', 'wxTR_LINES_AT_ROOT', @@ -422,6 +430,15 @@ class xxxListBox(xxxObject): winStyles = ['wxLB_SINGLE', 'wxLB_MULTIPLE', 'wxLB_EXTENDED', 'wxLB_HSCROLL', 'wxLB_ALWAYS_SB', 'wxLB_NEEDED_SB', 'wxLB_SORT'] +class xxxCheckList(xxxObject): + allParams = ['content', 'pos', 'size', 'style'] + required = ['content'] + winStyles = ['wxLC_LIST', 'wxLC_REPORT', 'wxLC_ICON', 'wxLC_SMALL_ICON', + 'wxLC_ALIGN_TOP', 'wxLC_ALIGN_LEFT', 'wxLC_AUTOARRANGE', + 'wxLC_USER_TEXT', 'wxLC_EDIT_LABELS', 'wxLC_NO_HEADER', + 'wxLC_SINGLE_SEL', 'wxLC_SORT_ASCENDING', 'wxLC_SORT_DESCENDING'] + paramDict = {'content': ParamContentCheckList} + ################################################################################ # Sizers @@ -507,16 +524,13 @@ class xxxChildContainer(xxxObject): element.removeChild(node) node.unlink() assert 0, 'no child found' - def generateHtml(self): - return xxxObject.generateHtml(self, '_') + '


\n' + \ - self.child.generateHtml() class xxxSizerItem(xxxChildContainer): allParams = ['option', 'flag', 'border'] paramDict = {'option': ParamInt} def __init__(self, parent, element): xxxChildContainer.__init__(self, parent, element) - # Remove pos parameter - unnecessary for sizeritems + # Remove pos parameter - not needed for sizeritems if 'pos' in self.child.allParams: self.child.allParams = self.child.allParams[:] self.child.allParams.remove('pos') @@ -556,7 +570,6 @@ class xxxMenuItem(xxxObject): class xxxSeparator(xxxObject): hasName = hasStyle = false - allParams = [] ################################################################################ @@ -616,9 +629,10 @@ paramIDs = {'fg': wxNewId(), 'bg': wxNewId(), 'exstyle': wxNewId(), 'font': wxNe 'tooltip': wxNewId() } for cl in xxxDict.values(): - for param in cl.allParams + cl.paramDict.keys(): - if not paramIDs.has_key(param): - paramIDs[param] = wxNewId() + if cl.allParams: + for param in cl.allParams + cl.paramDict.keys(): + if not paramIDs.has_key(param): + paramIDs[param] = wxNewId() ################################################################################ # Helper functions @@ -633,7 +647,7 @@ def MakeXXXFromDOM(parent, element): return xxxDict[element.getAttribute('class')](parent, element) except KeyError: # Verify that it's not recursive exception - if element.getAttribute('class') not in xxxDict.keys(): + if element.getAttribute('class') not in xxxDict: print 'ERROR: unknown class:', element.getAttribute('class') raise -- 2.45.2