2 # Purpose:      XRC editor, main module 
   3 # Author:       Roman Rolinsky <rolinsky@mema.ucl.ac.be> 
   9 xrced -- Simple resource editor for XRC format used by wxWindows/wxPython 
  14     xrced [ -h ] [ -v ] [ XRC-file ] 
  18     -h          output short usage info and exit 
  20     -v          output version info and exit 
  25 import os
, sys
, getopt
, re
, traceback
 
  28 from tree 
import *                      # imports xxx which imports params 
  31 # Cleanup recursive import sideeffects, otherwise we can't create undoMan 
  33 undo
.ParamPage 
= ParamPage
 
  34 undoMan 
= g
.undoMan 
= UndoManager() 
  36 # Set application path for loading resources 
  37 if __name__ 
== '__main__': 
  38     basePath 
= os
.path
.dirname(sys
.argv
[0]) 
  40     basePath 
= os
.path
.dirname(__file__
) 
  42 # 1 adds CMD command to Help menu 
  46 <HTML><H2>Welcome to XRC<font color="blue">ed</font></H2><H3><font color="green">DON'T PANIC :)</font></H3> 
  47 Read this note before clicking on anything!<P> 
  48 To start select tree root, then popup menu with your right mouse button, 
  49 select "Append Child", and then any command.<P> 
  50 Or just press one of the buttons on the tools palette.<P> 
  51 Enter XML ID, change properties, create children.<P> 
  52 To test your interface select Test command (View menu).<P> 
  53 Consult README file for the details.</HTML> 
  56 defaultIDs 
= {xxxPanel
:'PANEL', xxxDialog
:'DIALOG', xxxFrame
:'FRAME', 
  57               xxxMenuBar
:'MENUBAR', xxxMenu
:'MENU', xxxToolBar
:'TOOLBAR'} 
  59 ################################################################################ 
  61 # ScrolledMessageDialog - modified from wxPython lib to set fixed-width font 
  62 class ScrolledMessageDialog(wxDialog
): 
  63     def __init__(self
, parent
, msg
, caption
, pos 
= wxDefaultPosition
, size 
= (500,300)): 
  64         from wxPython
.lib
.layoutf 
import Layoutf
 
  65         wxDialog
.__init
__(self
, parent
, -1, caption
, pos
, size
) 
  66         text 
= wxTextCtrl(self
, -1, msg
, wxDefaultPosition
, 
  67                              wxDefaultSize
, wxTE_MULTILINE | wxTE_READONLY
) 
  68         text
.SetFont(g
.modernFont()) 
  70         # !!! possible bug - GetTextExtent without font returns sysfont dims 
  71         w
, h 
= dc
.GetFullTextExtent(' ', g
.modernFont())[:2] 
  72         ok 
= wxButton(self
, wxID_OK
, "OK") 
  73         text
.SetConstraints(Layoutf('t=t5#1;b=t5#2;l=l5#1;r=r5#1', (self
,ok
))) 
  74         text
.SetSize((w 
* 80 + 30, h 
* 40)) 
  76         ok
.SetConstraints(Layoutf('b=b5#1;x%w50#1;w!80;h!25', (self
,))) 
  77         self
.SetAutoLayout(True) 
  79         self
.CenterOnScreen(wxBOTH
) 
  81 ################################################################################ 
  83 # Event handler for using during location 
  84 class Locator(wxEvtHandler
): 
  85     def ProcessEvent(self
, evt
): 
  89     def __init__(self
, pos
, size
): 
  90         wxFrame
.__init
__(self
, None, -1, '', pos
, size
) 
  92         frame 
= g
.frame 
= self
 
  93         bar 
= self
.CreateStatusBar(2) 
  94         bar
.SetStatusWidths([-1, 40]) 
  95         self
.SetIcon(images
.getIconIcon()) 
 101         menuBar 
= wxMenuBar() 
 104         menu
.Append(wxID_NEW
, '&New\tCtrl-N', 'New file') 
 105         menu
.AppendSeparator() 
 106         menu
.Append(wxID_OPEN
, '&Open...\tCtrl-O', 'Open XRC file') 
 107         self
.recentMenu 
= wxMenu() 
 108         self
.AppendRecent(self
.recentMenu
) 
 109         menu
.AppendMenu(-1, 'Open Recent', self
.recentMenu
, 'Open a recent file') 
 110         menu
.AppendSeparator() 
 111         menu
.Append(wxID_SAVE
, '&Save\tCtrl-S', 'Save XRC file') 
 112         menu
.Append(wxID_SAVEAS
, 'Save &As...', 'Save XRC file under different name') 
 113         menu
.AppendSeparator() 
 114         menu
.Append(wxID_EXIT
, '&Quit\tCtrl-Q', 'Exit application') 
 116         menuBar
.Append(menu
, '&File') 
 119         menu
.Append(wxID_UNDO
, '&Undo\tCtrl-Z', 'Undo') 
 120         menu
.Append(wxID_REDO
, '&Redo\tCtrl-Y', 'Redo') 
 121         menu
.AppendSeparator() 
 122         menu
.Append(wxID_CUT
, 'Cut\tCtrl-X', 'Cut to the clipboard') 
 123         menu
.Append(wxID_COPY
, '&Copy\tCtrl-C', 'Copy to the clipboard') 
 124         menu
.Append(wxID_PASTE
, '&Paste\tCtrl-V', 'Paste from the clipboard') 
 125         self
.ID_DELETE 
= wxNewId() 
 126         menu
.Append(self
.ID_DELETE
, '&Delete\tCtrl-D', 'Delete object') 
 127 #        menu.AppendSeparator() 
 128         self
.ID_LOCATE 
= wxNewId() 
 129         menu
.Append(self
.ID_LOCATE
, '&Locate\tCtrl-L', 'Locate control in test window and select it') 
 130         menuBar
.Append(menu
, '&Edit') 
 133         self
.ID_EMBED_PANEL 
= wxNewId() 
 134         menu
.Append(self
.ID_EMBED_PANEL
, '&Embed Panel', 
 135                     'Toggle embedding properties panel in the main window', True) 
 136         menu
.Check(self
.ID_EMBED_PANEL
, conf
.embedPanel
) 
 137         self
.ID_SHOW_TOOLS 
= wxNewId() 
 138         menu
.Append(self
.ID_SHOW_TOOLS
, 'Show &Tools', 'Toggle tools', True) 
 139         menu
.Check(self
.ID_SHOW_TOOLS
, conf
.showTools
) 
 140         menu
.AppendSeparator() 
 141         self
.ID_TEST 
= wxNewId() 
 142         menu
.Append(self
.ID_TEST
, '&Test\tF5', 'Show test window') 
 143         self
.ID_REFRESH 
= wxNewId() 
 144         menu
.Append(self
.ID_REFRESH
, '&Refresh\tCtrl-R', 'Refresh test window') 
 145         self
.ID_AUTO_REFRESH 
= wxNewId() 
 146         menu
.Append(self
.ID_AUTO_REFRESH
, '&Auto-refresh\tCtrl-A', 
 147                     'Toggle auto-refresh mode', True) 
 148         menu
.Check(self
.ID_AUTO_REFRESH
, conf
.autoRefresh
) 
 149         menuBar
.Append(menu
, '&View') 
 152         menu
.Append(wxID_ABOUT
, '&About...', 'About XCRed') 
 153         self
.ID_README 
= wxNewId() 
 154         menu
.Append(self
.ID_README
, '&Readme...', 'View the README file') 
 156             self
.ID_DEBUG_CMD 
= wxNewId() 
 157             menu
.Append(self
.ID_DEBUG_CMD
, 'CMD', 'Python command line') 
 158             EVT_MENU(self
, self
.ID_DEBUG_CMD
, self
.OnDebugCMD
) 
 159         menuBar
.Append(menu
, '&Help') 
 161         self
.menuBar 
= menuBar
 
 162         self
.SetMenuBar(menuBar
) 
 165         tb 
= self
.CreateToolBar(wxTB_HORIZONTAL | wxNO_BORDER | wxTB_FLAT
) 
 166         tb
.SetToolBitmapSize((24, 23)) 
 167         tb
.AddSimpleTool(wxID_NEW
, images
.getNewBitmap(), 'New', 'New file') 
 168         tb
.AddSimpleTool(wxID_OPEN
, images
.getOpenBitmap(), 'Open', 'Open file') 
 169         tb
.AddSimpleTool(wxID_SAVE
, images
.getSaveBitmap(), 'Save', 'Save file') 
 170         tb
.AddControl(wxStaticLine(tb
, -1, size
=(-1,23), style
=wxLI_VERTICAL
)) 
 171         tb
.AddSimpleTool(wxID_UNDO
, images
.getUndoBitmap(), 'Undo', 'Undo') 
 172         tb
.AddSimpleTool(wxID_REDO
, images
.getRedoBitmap(), 'Redo', 'Redo') 
 173         tb
.AddControl(wxStaticLine(tb
, -1, size
=(-1,23), style
=wxLI_VERTICAL
)) 
 174         tb
.AddSimpleTool(wxID_CUT
, images
.getCutBitmap(), 'Cut', 'Cut') 
 175         tb
.AddSimpleTool(wxID_COPY
, images
.getCopyBitmap(), 'Copy', 'Copy') 
 176         tb
.AddSimpleTool(wxID_PASTE
, images
.getPasteBitmap(), 'Paste', 'Paste') 
 177         tb
.AddControl(wxStaticLine(tb
, -1, size
=(-1,23), style
=wxLI_VERTICAL
)) 
 178         tb
.AddSimpleTool(self
.ID_TEST
, images
.getTestBitmap(), 'Test', 'Test window') 
 179         tb
.AddSimpleTool(self
.ID_REFRESH
, images
.getRefreshBitmap(), 
 180                          'Refresh', 'Refresh view') 
 181         tb
.AddSimpleTool(self
.ID_AUTO_REFRESH
, images
.getAutoRefreshBitmap(), 
 182                          'Auto-refresh', 'Toggle auto-refresh mode', True) 
 183         if wxPlatform 
== '__WXGTK__': 
 184             tb
.AddSeparator()   # otherwise auto-refresh sticks in status line 
 185         tb
.ToggleTool(self
.ID_AUTO_REFRESH
, conf
.autoRefresh
) 
 188         self
.minWidth 
= tb
.GetSize()[0] # minimal width is the size of toolbar 
 191         EVT_MENU(self
, wxID_NEW
, self
.OnNew
) 
 192         EVT_MENU(self
, wxID_OPEN
, self
.OnOpen
) 
 193         EVT_MENU(self
, wxID_SAVE
, self
.OnSaveOrSaveAs
) 
 194         EVT_MENU(self
, wxID_SAVEAS
, self
.OnSaveOrSaveAs
) 
 195         EVT_MENU(self
, wxID_EXIT
, self
.OnExit
) 
 197         EVT_MENU(self
, wxID_UNDO
, self
.OnUndo
) 
 198         EVT_MENU(self
, wxID_REDO
, self
.OnRedo
) 
 199         EVT_MENU(self
, wxID_CUT
, self
.OnCutDelete
) 
 200         EVT_MENU(self
, wxID_COPY
, self
.OnCopy
) 
 201         EVT_MENU(self
, wxID_PASTE
, self
.OnPaste
) 
 202         EVT_MENU(self
, self
.ID_DELETE
, self
.OnCutDelete
) 
 203         EVT_MENU(self
, self
.ID_LOCATE
, self
.OnLocate
) 
 205         EVT_MENU(self
, self
.ID_EMBED_PANEL
, self
.OnEmbedPanel
) 
 206         EVT_MENU(self
, self
.ID_SHOW_TOOLS
, self
.OnShowTools
) 
 207         EVT_MENU(self
, self
.ID_TEST
, self
.OnTest
) 
 208         EVT_MENU(self
, self
.ID_REFRESH
, self
.OnRefresh
) 
 209         EVT_MENU(self
, self
.ID_AUTO_REFRESH
, self
.OnAutoRefresh
) 
 211         EVT_MENU(self
, wxID_ABOUT
, self
.OnAbout
) 
 212         EVT_MENU(self
, self
.ID_README
, self
.OnReadme
) 
 215         EVT_UPDATE_UI(self
, wxID_CUT
, self
.OnUpdateUI
) 
 216         EVT_UPDATE_UI(self
, wxID_COPY
, self
.OnUpdateUI
) 
 217         EVT_UPDATE_UI(self
, wxID_PASTE
, self
.OnUpdateUI
) 
 218         EVT_UPDATE_UI(self
, wxID_UNDO
, self
.OnUpdateUI
) 
 219         EVT_UPDATE_UI(self
, wxID_REDO
, self
.OnUpdateUI
) 
 220         EVT_UPDATE_UI(self
, self
.ID_DELETE
, self
.OnUpdateUI
) 
 221         EVT_UPDATE_UI(self
, self
.ID_TEST
, self
.OnUpdateUI
) 
 222         EVT_UPDATE_UI(self
, self
.ID_REFRESH
, self
.OnUpdateUI
) 
 225         sizer 
= wxBoxSizer(wxVERTICAL
) 
 226         sizer
.Add(wxStaticLine(self
, -1), 0, wxEXPAND
) 
 227         # Horizontal sizer for toolbar and splitter 
 228         self
.toolsSizer 
= sizer1 
= wxBoxSizer() 
 229         splitter 
= wxSplitterWindow(self
, -1, style
=wxSP_3DSASH
) 
 230         self
.splitter 
= splitter
 
 231         splitter
.SetMinimumPaneSize(100) 
 234         g
.tree 
= tree 
= XML_Tree(splitter
, -1) 
 236         # Init pull-down menu data 
 238         g
.pullDownMenu 
= pullDownMenu 
= PullDownMenu(self
) 
 240         # Vertical toolbar for GUI buttons 
 241         g
.tools 
= tools 
= Tools(self
) 
 242         tools
.Show(conf
.showTools
) 
 243         if conf
.showTools
: sizer1
.Add(tools
, 0, wxEXPAND
) 
 245         tree
.RegisterKeyEvents() 
 247         # !!! frame styles are broken 
 248         # Miniframe for not embedded mode 
 249         miniFrame 
= wxFrame(self
, -1, 'Properties Panel', 
 250                             (conf
.panelX
, conf
.panelY
), 
 251                             (conf
.panelWidth
, conf
.panelHeight
)) 
 252         self
.miniFrame 
= miniFrame
 
 253         sizer2 
= wxBoxSizer() 
 254         miniFrame
.SetAutoLayout(True) 
 255         miniFrame
.SetSizer(sizer2
) 
 256         EVT_CLOSE(self
.miniFrame
, self
.OnCloseMiniFrame
) 
 257         # Create panel for parameters 
 260             panel 
= Panel(splitter
) 
 261             # Set plitter windows 
 262             splitter
.SplitVertically(tree
, panel
, conf
.sashPos
) 
 264             panel 
= Panel(miniFrame
) 
 265             sizer2
.Add(panel
, 1, wxEXPAND
) 
 267             splitter
.Initialize(tree
) 
 268         sizer1
.Add(splitter
, 1, wxEXPAND
) 
 269         sizer
.Add(sizer1
, 1, wxEXPAND
) 
 270         self
.SetAutoLayout(True) 
 274         self
.clipboard 
= None 
 278         EVT_IDLE(self
, self
.OnIdle
) 
 279         EVT_CLOSE(self
, self
.OnCloseWindow
) 
 280         EVT_KEY_DOWN(self
, tools
.OnKeyDown
) 
 281         EVT_KEY_UP(self
, tools
.OnKeyUp
) 
 283     def AppendRecent(self
, menu
): 
 284         # add recently used files to the menu 
 285         for id,name 
in conf
.recentfiles
.iteritems(): 
 287             EVT_MENU(self
,id,self
.OnRecentFile
) 
 290     def OnRecentFile(self
,evt
): 
 291         # open recently used file 
 292         if not self
.AskSave(): return 
 295             path
=conf
.recentfiles
[evt
.GetId()] 
 297                 self
.SetStatusText('Data loaded') 
 299                 self
.SetStatusText('Failed') 
 301             self
.SetStatusText('No such file') 
 304     def OnNew(self
, evt
): 
 305         if not self
.AskSave(): return 
 308     def OnOpen(self
, evt
): 
 309         if not self
.AskSave(): return 
 310         dlg 
= wxFileDialog(self
, 'Open', os
.path
.dirname(self
.dataFile
), 
 311                            '', '*.xrc', wxOPEN | wxCHANGE_DIR
) 
 312         if dlg
.ShowModal() == wxID_OK
: 
 314             self
.SetStatusText('Loading...') 
 318                 self
.SetStatusText('Data loaded') 
 320                 self
.SetStatusText('Failed') 
 321             self
.SaveRecent(path
) 
 325     def OnSaveOrSaveAs(self
, evt
): 
 326         if evt
.GetId() == wxID_SAVEAS 
or not self
.dataFile
: 
 327             if self
.dataFile
: defaultName 
= '' 
 328             else: defaultName 
= 'UNTITLED.xrc' 
 329             dirname 
= os
.path
.dirname(self
.dataFile
) 
 330             dlg 
= wxFileDialog(self
, 'Save As', dirname
, defaultName
, '*.xrc', 
 331                                wxSAVE | wxOVERWRITE_PROMPT | wxCHANGE_DIR
) 
 332             if dlg
.ShowModal() == wxID_OK
: 
 340         self
.SetStatusText('Saving...') 
 346             self
.SetStatusText('Data saved') 
 347             self
.SaveRecent(path
) 
 349             self
.SetStatusText('Failed') 
 352     def SaveRecent(self
,path
): 
 353         # append to recently used files 
 354         if path 
not in conf
.recentfiles
.values(): 
 356             self
.recentMenu
.Append(newid
, path
) 
 357             EVT_MENU(self
, newid
, self
.OnRecentFile
) 
 358             conf
.recentfiles
[newid
] = path
 
 360     def OnExit(self
, evt
): 
 363     def OnUndo(self
, evt
): 
 364         # Extra check to not mess with idle updating 
 365         if undoMan
.CanUndo(): 
 368     def OnRedo(self
, evt
): 
 369         if undoMan
.CanRedo(): 
 372     def OnCopy(self
, evt
): 
 373         selected 
= tree
.selection
 
 374         if not selected
: return         # key pressed event 
 375         xxx 
= tree
.GetPyData(selected
) 
 376         self
.clipboard 
= xxx
.element
.cloneNode(True) 
 377         self
.SetStatusText('Copied') 
 379     def OnPaste(self
, evt
): 
 380         selected 
= tree
.selection
 
 381         if not selected
: return         # key pressed event 
 382         # For pasting with Ctrl pressed 
 383         if evt
.GetId() == pullDownMenu
.ID_PASTE_SIBLING
: appendChild 
= False 
 384         else: appendChild 
= not tree
.NeedInsert(selected
) 
 385         xxx 
= tree
.GetPyData(selected
) 
 387             # If has next item, insert, else append to parent 
 388             nextItem 
= tree
.GetNextSibling(selected
) 
 389             parentLeaf 
= tree
.GetItemParent(selected
) 
 390         # Expanded container (must have children) 
 391         elif tree
.IsExpanded(selected
) and tree
.GetChildrenCount(selected
, False): 
 392             # Insert as first child 
 393             nextItem 
= tree
.GetFirstChild(selected
)[0] 
 394             parentLeaf 
= selected
 
 396             # No children or unexpanded item - appendChild stays True 
 397             nextItem 
= wxTreeItemId()   # no next item 
 398             parentLeaf 
= selected
 
 399         parent 
= tree
.GetPyData(parentLeaf
).treeObject() 
 401         # Create a copy of clipboard element 
 402         elem 
= self
.clipboard
.cloneNode(True) 
 403         # Tempopary xxx object to test things 
 404         xxx 
= MakeXXXFromDOM(parent
, elem
) 
 406         # Check compatibility 
 410         if x
.__class
__ in [xxxDialog
, xxxFrame
, xxxMenuBar
]: 
 412             if parent
.__class
__ != xxxMainNode
: error 
= True 
 413         elif x
.__class
__ == xxxToolBar
: 
 414             # Toolbar can be top-level of child of panel or frame 
 415             if parent
.__class
__ not in [xxxMainNode
, xxxPanel
, xxxFrame
]: error 
= True 
 416         elif x
.__class
__ == xxxPanel 
and parent
.__class
__ == xxxMainNode
: 
 418         elif x
.__class
__ == xxxSpacer
: 
 419             if not parent
.isSizer
: error 
= True 
 420         elif x
.__class
__ == xxxSeparator
: 
 421             if not parent
.__class
__ in [xxxMenu
, xxxToolBar
]: error 
= True 
 422         elif x
.__class
__ == xxxTool
: 
 423             if parent
.__class
__ != xxxToolBar
: error 
= True 
 424         elif x
.__class
__ == xxxMenu
: 
 425             if not parent
.__class
__ in [xxxMainNode
, xxxMenuBar
, xxxMenu
]: error 
= True 
 426         elif x
.__class
__ == xxxMenuItem
: 
 427             if not parent
.__class
__ in [xxxMenuBar
, xxxMenu
]: error 
= True 
 428         elif x
.isSizer 
and parent
.__class
__ == xxxNotebook
: error 
= True 
 429         else:                           # normal controls can be almost anywhere 
 430             if parent
.__class
__ == xxxMainNode 
or \
 
 431                parent
.__class
__ in [xxxMenuBar
, xxxMenu
]: error 
= True 
 433             if parent
.__class
__ == xxxMainNode
: parentClass 
= 'root' 
 434             else: parentClass 
= parent
.className
 
 435             wxLogError('Incompatible parent/child: parent is %s, child is %s!' % 
 436                        (parentClass
, x
.className
)) 
 439         # Check parent and child relationships. 
 440         # If parent is sizer or notebook, child is of wrong class or 
 441         # parent is normal window, child is child container then detach child. 
 442         isChildContainer 
= isinstance(xxx
, xxxChildContainer
) 
 443         if isChildContainer 
and \
 
 444            ((parent
.isSizer 
and not isinstance(xxx
, xxxSizerItem
)) or \
 
 445             (isinstance(parent
, xxxNotebook
) and not isinstance(xxx
, xxxNotebookPage
)) or \
 
 446            not (parent
.isSizer 
or isinstance(parent
, xxxNotebook
))): 
 447             elem
.removeChild(xxx
.child
.element
) # detach child 
 448             elem
.unlink()           # delete child container 
 449             elem 
= xxx
.child
.element 
# replace 
 450             # This may help garbage collection 
 451             xxx
.child
.parent 
= None 
 452             isChildContainer 
= False 
 453         # Parent is sizer or notebook, child is not child container 
 454         if parent
.isSizer 
and not isChildContainer 
and not isinstance(xxx
, xxxSpacer
): 
 455             # Create sizer item element 
 456             sizerItemElem 
= MakeEmptyDOM('sizeritem') 
 457             sizerItemElem
.appendChild(elem
) 
 459         elif isinstance(parent
, xxxNotebook
) and not isChildContainer
: 
 460             pageElem 
= MakeEmptyDOM('notebookpage') 
 461             pageElem
.appendChild(elem
) 
 463         # Insert new node, register undo 
 464         newItem 
= tree
.InsertNode(parentLeaf
, parent
, elem
, nextItem
) 
 465         undoMan
.RegisterUndo(UndoPasteCreate(parentLeaf
, parent
, newItem
, selected
)) 
 466         # Scroll to show new item (!!! redundant?) 
 467         tree
.EnsureVisible(newItem
) 
 468         tree
.SelectItem(newItem
) 
 469         if not tree
.IsVisible(newItem
): 
 470             tree
.ScrollTo(newItem
) 
 473         if g
.testWin 
and tree
.IsHighlatable(newItem
): 
 475                 tree
.needUpdate 
= True 
 476                 tree
.pendingHighLight 
= newItem
 
 478                 tree
.pendingHighLight 
= None 
 480         self
.SetStatusText('Pasted') 
 482     def OnCutDelete(self
, evt
): 
 483         selected 
= tree
.selection
 
 484         if not selected
: return         # key pressed event 
 486         if evt
.GetId() == wxID_CUT
: 
 488             status 
= 'Removed to clipboard' 
 490             self
.lastOp 
= 'DELETE' 
 494             # If deleting top-level item, delete testWin 
 495             if selected 
== g
.testWin
.item
: 
 499                 # Remove highlight, update testWin 
 500                 if g
.testWin
.highLight
: 
 501                     g
.testWin
.highLight
.Remove() 
 502                 tree
.needUpdate 
= True 
 505         index 
= tree
.ItemFullIndex(selected
) 
 506         parent 
= tree
.GetPyData(tree
.GetItemParent(selected
)).treeObject() 
 507         elem 
= tree
.RemoveLeaf(selected
) 
 508         undoMan
.RegisterUndo(UndoCutDelete(index
, parent
, elem
)) 
 509         if evt
.GetId() == wxID_CUT
: 
 510             if self
.clipboard
: self
.clipboard
.unlink() 
 511             self
.clipboard 
= elem
.cloneNode(True) 
 512         tree
.pendingHighLight 
= None 
 516         self
.SetStatusText(status
) 
 518     def OnSubclass(self
, evt
): 
 519         selected 
= tree
.selection
 
 520         xxx 
= tree
.GetPyData(selected
).treeObject() 
 522         subclass 
= xxx
.subclass
 
 523         dlg 
= wxTextEntryDialog(self
, 'Subclass:', defaultValue
=subclass
) 
 524         if dlg
.ShowModal() == wxID_OK
: 
 525             subclass 
= dlg
.GetValue() 
 527                 elem
.setAttribute('subclass', subclass
) 
 529             elif elem
.hasAttribute('subclass'): 
 530                 elem
.removeAttribute('subclass') 
 532             xxx
.subclass 
= elem
.getAttribute('subclass') 
 533             tree
.SetItemText(selected
, xxx
.treeName()) 
 534             panel
.pages
[0].box
.SetLabel(xxx
.panelName()) 
 537     def OnEmbedPanel(self
, evt
): 
 538         conf
.embedPanel 
= evt
.IsChecked() 
 540             # Remember last dimentions 
 541             conf
.panelX
, conf
.panelY 
= self
.miniFrame
.GetPosition() 
 542             conf
.panelWidth
, conf
.panelHeight 
= self
.miniFrame
.GetSize() 
 543             size 
= self
.GetSize() 
 544             pos 
= self
.GetPosition() 
 545             sizePanel 
= panel
.GetSize() 
 546             panel
.Reparent(self
.splitter
) 
 547             self
.miniFrame
.GetSizer().Remove(panel
) 
 550             self
.SetDimensions(pos
.x
, pos
.y
, size
.width 
+ sizePanel
.width
, size
.height
) 
 551             self
.splitter
.SplitVertically(tree
, panel
, conf
.sashPos
) 
 552             self
.miniFrame
.Show(False) 
 554             conf
.sashPos 
= self
.splitter
.GetSashPosition() 
 555             pos 
= self
.GetPosition() 
 556             size 
= self
.GetSize() 
 557             sizePanel 
= panel
.GetSize() 
 558             self
.splitter
.Unsplit(panel
) 
 559             sizer 
= self
.miniFrame
.GetSizer() 
 560             panel
.Reparent(self
.miniFrame
) 
 562             sizer
.Add(panel
, 1, wxEXPAND
) 
 563             self
.miniFrame
.Show(True) 
 564             self
.miniFrame
.SetDimensions(conf
.panelX
, conf
.panelY
, 
 565                                          conf
.panelWidth
, conf
.panelHeight
) 
 568             self
.SetDimensions(pos
.x
, pos
.y
, 
 569                                max(size
.width 
- sizePanel
.width
, self
.minWidth
), size
.height
) 
 571     def OnShowTools(self
, evt
): 
 572         conf
.showTools 
= evt
.IsChecked() 
 573         g
.tools
.Show(conf
.showTools
) 
 575             self
.toolsSizer
.Prepend(g
.tools
, 0, wxEXPAND
) 
 577             self
.toolsSizer
.Remove(g
.tools
) 
 578         self
.toolsSizer
.Layout() 
 580     def OnTest(self
, evt
): 
 581         if not tree
.selection
: return   # key pressed event 
 582         tree
.ShowTestWindow(tree
.selection
) 
 584     # Find object by relative position 
 585     def FindObject(self
, item
, obj
): 
 586         # We simply perform depth-first traversal, sinse it's too much 
 587         # hassle to deal with all sizer/window combinations 
 588         w 
= tree
.FindNodeObject(item
) 
 591         if tree
.ItemHasChildren(item
): 
 592             child 
= tree
.GetFirstChild(item
)[0] 
 594                 found 
= self
.FindObject(child
, obj
) 
 595                 if found
: return found
 
 596                 child 
= tree
.GetNextSibling(child
) 
 599     def OnTestWinLeftDown(self
, evt
): 
 600         pos 
= evt
.GetPosition() 
 601         self
.SetHandler(g
.testWin
) 
 602         g
.testWin
.Disconnect(wxID_ANY
, wxID_ANY
, wxEVT_LEFT_DOWN
) 
 603         item 
= self
.FindObject(g
.testWin
.item
, evt
.GetEventObject()) 
 605             tree
.SelectItem(item
) 
 607     def SetHandler(self
, w
, h
=None): 
 610             w
.SetCursor(wxCROSS_CURSOR
) 
 613             w
.SetCursor(wxNullCursor
) 
 614         for ch 
in w
.GetChildren(): 
 615             self
.SetHandler(ch
, h
) 
 617     def OnLocate(self
, evt
): 
 619             self
.SetHandler(g
.testWin
, g
.testWin
) 
 620             g
.testWin
.Connect(wxID_ANY
, wxID_ANY
, wxEVT_LEFT_DOWN
, self
.OnTestWinLeftDown
) 
 622     def OnRefresh(self
, evt
): 
 623         # If modified, apply first 
 624         selection 
= tree
.selection
 
 626             xxx 
= tree
.GetPyData(selection
) 
 627             if xxx 
and panel
.IsModified(): 
 628                 tree
.Apply(xxx
, selection
) 
 631             tree
.CreateTestWin(g
.testWin
.item
) 
 632         panel
.modified 
= False 
 633         tree
.needUpdate 
= False 
 635     def OnAutoRefresh(self
, evt
): 
 636         conf
.autoRefresh 
= evt
.IsChecked() 
 637         self
.menuBar
.Check(self
.ID_AUTO_REFRESH
, conf
.autoRefresh
) 
 638         self
.tb
.ToggleTool(self
.ID_AUTO_REFRESH
, conf
.autoRefresh
) 
 640     def OnAbout(self
, evt
): 
 644 (c) Roman Rolinsky <rollrom@users.sourceforge.net> 
 645 Homepage: http://xrced.sourceforge.net\ 
 647         dlg 
= wxMessageDialog(self
, str, 'About XRCed', wxOK | wxCENTRE
) 
 651     def OnReadme(self
, evt
): 
 652         text 
= open(os
.path
.join(basePath
, 'README.txt'), 'r').read() 
 653         dlg 
= ScrolledMessageDialog(self
, text
, "XRCed README") 
 657     # Simple emulation of python command line 
 658     def OnDebugCMD(self
, evt
): 
 662                 exec raw_input('C:\> ') 
 667                 (etype
, value
, tb
) =sys
.exc_info() 
 668                 tblist 
=traceback
.extract_tb(tb
)[1:] 
 669                 msg 
=' '.join(traceback
.format_exception_only(etype
, value
) 
 670                         +traceback
.format_list(tblist
)) 
 673     def OnCreate(self
, evt
): 
 674         selected 
= tree
.selection
 
 675         if tree
.ctrl
: appendChild 
= False 
 676         else: appendChild 
= not tree
.NeedInsert(selected
) 
 677         xxx 
= tree
.GetPyData(selected
) 
 681                 # If has previous item, insert after it, else append to parent 
 683                 parentLeaf 
= tree
.GetItemParent(selected
) 
 685                 # If has next item, insert, else append to parent 
 686                 nextItem 
= tree
.GetNextSibling(selected
) 
 687                 parentLeaf 
= tree
.GetItemParent(selected
) 
 688         # Expanded container (must have children) 
 689         elif tree
.shift 
and tree
.IsExpanded(selected
) \
 
 690            and tree
.GetChildrenCount(selected
, False): 
 691             nextItem 
= tree
.GetFirstChild(selected
)[0] 
 692             parentLeaf 
= selected
 
 694             nextItem 
= wxTreeItemId() 
 695             parentLeaf 
= selected
 
 696         parent 
= tree
.GetPyData(parentLeaf
) 
 697         if parent
.hasChild
: parent 
= parent
.child
 
 700         className 
= pullDownMenu
.createMap
[evt
.GetId()] 
 701         xxx 
= MakeEmptyXXX(parent
, className
) 
 703         # Set default name for top-level windows 
 704         if parent
.__class
__ == xxxMainNode
: 
 705             cl 
= xxx
.treeObject().__class
__ 
 706             frame
.maxIDs
[cl
] += 1 
 707             xxx
.treeObject().name 
= '%s%d' % (defaultIDs
[cl
], frame
.maxIDs
[cl
]) 
 708             xxx
.treeObject().element
.setAttribute('name', xxx
.treeObject().name
) 
 710         # Insert new node, register undo 
 712         newItem 
= tree
.InsertNode(parentLeaf
, parent
, elem
, nextItem
) 
 713         undoMan
.RegisterUndo(UndoPasteCreate(parentLeaf
, parent
, newItem
, selected
)) 
 714         tree
.EnsureVisible(newItem
) 
 715         tree
.SelectItem(newItem
) 
 716         if not tree
.IsVisible(newItem
): 
 717             tree
.ScrollTo(newItem
) 
 720         if g
.testWin 
and tree
.IsHighlatable(newItem
): 
 722                 tree
.needUpdate 
= True 
 723                 tree
.pendingHighLight 
= newItem
 
 725                 tree
.pendingHighLight 
= None 
 729     # Replace one object with another 
 730     def OnReplace(self
, evt
): 
 731         selected 
= tree
.selection
 
 732         xxx 
= tree
.GetPyData(selected
).treeObject() 
 734         parent 
= elem
.parentNode
 
 735         parentXXX 
= xxx
.parent
 
 737         className 
= pullDownMenu
.createMap
[evt
.GetId() - 1000] 
 738         # Create temporary empty node (with default values) 
 739         dummy 
= MakeEmptyDOM(className
) 
 740         xxxClass 
= xxxDict
[className
] 
 741         # Remove non-compatible children 
 742         if tree
.ItemHasChildren(selected
) and not xxxClass
.hasChildren
: 
 743             tree
.DeleteChildren(selected
) 
 744         nodes 
= elem
.childNodes
[:] 
 750                 if not xxxClass
.hasChildren
: 
 752             elif tag 
not in xxxClass
.allParams 
and \
 
 753                      (not xxxClass
.hasStyle 
or tag 
not in xxxClass
.styles
): 
 758                 elem
.removeChild(node
) 
 761         # Copy parameters present in dummy but not in elem 
 762         for node 
in dummy
.childNodes
: 
 765                 elem
.appendChild(node
.cloneNode(True)) 
 768         elem
.setAttribute('class', className
)         
 769         # Re-create xxx element 
 770         xxx 
= MakeXXXFromDOM(parentXXX
, elem
) 
 771         # Update parent in child objects 
 772         if tree
.ItemHasChildren(selected
): 
 773             i
, cookie 
= tree
.GetFirstChild(selected
) 
 775                 x 
= tree
.GetPyData(i
) 
 777                 if x
.hasChild
: x
.child
.parent 
= xxx
 
 778                 i
, cookie 
= tree
.GetNextChild(selected
, cookie
) 
 781         if tree
.GetPyData(selected
).hasChild
: # child container 
 782             container 
= tree
.GetPyData(selected
) 
 783             container
.child 
= xxx
 
 784             container
.hasChildren 
= xxx
.hasChildren
 
 785             container
.isSizer 
= xxx
.isSizer
 
 787             tree
.SetPyData(selected
, xxx
) 
 788         tree
.SetItemText(selected
, xxx
.treeName()) 
 789         tree
.SetItemImage(selected
, xxx
.treeImage()) 
 791         # Set default name for top-level windows 
 792         if parent
.__class
__ == xxxMainNode
: 
 793             cl 
= xxx
.treeObject().__class
__ 
 794             frame
.maxIDs
[cl
] += 1 
 795             xxx
.treeObject().name 
= '%s%d' % (defaultIDs
[cl
], frame
.maxIDs
[cl
]) 
 796             xxx
.treeObject().element
.setAttribute('name', xxx
.treeObject().name
) 
 803         #undoMan.RegisterUndo(UndoPasteCreate(parentLeaf, parent, newItem, selected)) 
 805         if g
.testWin 
and tree
.IsHighlatable(selected
): 
 807                 tree
.needUpdate 
= True 
 808                 tree
.pendingHighLight 
= selected
 
 810                 tree
.pendingHighLight 
= None 
 814     # Expand/collapse subtree 
 815     def OnExpand(self
, evt
): 
 816         if tree
.selection
: tree
.ExpandAll(tree
.selection
) 
 817         else: tree
.ExpandAll(tree
.root
) 
 818     def OnCollapse(self
, evt
): 
 819         if tree
.selection
: tree
.CollapseAll(tree
.selection
) 
 820         else: tree
.CollapseAll(tree
.root
) 
 822     def OnPullDownHighlight(self
, evt
): 
 823         menuId 
= evt
.GetMenuId() 
 825             menu 
= evt
.GetEventObject() 
 826             help = menu
.GetHelpString(menuId
) 
 827             self
.SetStatusText(help) 
 829             self
.SetStatusText('') 
 831     def OnUpdateUI(self
, evt
): 
 832         if evt
.GetId() in [wxID_CUT
, wxID_COPY
, self
.ID_DELETE
]: 
 833             evt
.Enable(tree
.selection 
is not None and tree
.selection 
!= tree
.root
) 
 834         elif evt
.GetId() == wxID_PASTE
: 
 835             evt
.Enable((self
.clipboard 
and tree
.selection
) != None) 
 836         elif evt
.GetId() == self
.ID_TEST
: 
 837             evt
.Enable(tree
.selection 
is not None and tree
.selection 
!= tree
.root
) 
 838         elif evt
.GetId() == wxID_UNDO
:  evt
.Enable(undoMan
.CanUndo()) 
 839         elif evt
.GetId() == wxID_REDO
:  evt
.Enable(undoMan
.CanRedo()) 
 841     def OnIdle(self
, evt
): 
 842         if self
.inIdle
: return          # Recursive call protection 
 847                     self
.SetStatusText('Refreshing test window...') 
 849                     tree
.CreateTestWin(g
.testWin
.item
) 
 851                     self
.SetStatusText('') 
 852                 tree
.needUpdate 
= False 
 853         elif tree
.pendingHighLight
: 
 854             tree
.HighLight(tree
.pendingHighLight
) 
 859     # We don't let close panel window 
 860     def OnCloseMiniFrame(self
, evt
): 
 863     def OnCloseWindow(self
, evt
): 
 864         if not self
.AskSave(): return 
 865         if g
.testWin
: g
.testWin
.Destroy() 
 866         if not panel
.GetPageCount() == 2: 
 867             panel
.page2
.Destroy() 
 869             # If we don't do this, page does not get destroyed (a bug?) 
 871         if not self
.IsIconized(): 
 872             conf
.x
, conf
.y 
= self
.GetPosition() 
 873             conf
.width
, conf
.height 
= self
.GetSize() 
 875                 conf
.sashPos 
= self
.splitter
.GetSashPosition() 
 877                 conf
.panelX
, conf
.panelY 
= self
.miniFrame
.GetPosition() 
 878                 conf
.panelWidth
, conf
.panelHeight 
= self
.miniFrame
.GetSize() 
 884             self
.clipboard
.unlink() 
 885             self
.clipboard 
= None 
 887         self
.modified 
= False 
 893         self
.SetTitle(progname
) 
 894         # Numbers for new controls 
 896         self
.maxIDs
[xxxPanel
] = self
.maxIDs
[xxxDialog
] = self
.maxIDs
[xxxFrame
] = \
 
 897         self
.maxIDs
[xxxMenuBar
] = self
.maxIDs
[xxxMenu
] = self
.maxIDs
[xxxToolBar
] = 0 
 899     def Open(self
, path
): 
 900         if not os
.path
.exists(path
): 
 901             wxLogError('File does not exists: %s' % path
) 
 903         # Try to read the file 
 907             dom 
= minidom
.parse(f
) 
 909             # Set encoding global variable 
 910             if dom
.encoding
: g
.currentEncoding 
= dom
.encoding
 
 912             self
.dataFile 
= path 
= os
.path
.abspath(path
) 
 913             dir = os
.path
.dirname(path
) 
 914             if dir: os
.chdir(dir) 
 916             self
.SetTitle(progname 
+ ': ' + os
.path
.basename(path
)) 
 918             # Nice exception printing 
 920             wxLogError(traceback
.format_exception(inf
[0], inf
[1], None)[-1]) 
 921             wxLogError('Error reading file: %s' % path
) 
 925     def Indent(self
, node
, indent 
= 0): 
 926         # Copy child list because it will change soon 
 927         children 
= node
.childNodes
[:] 
 928         # Main node doesn't need to be indented 
 930             text 
= self
.domCopy
.createTextNode('\n' + ' ' * indent
) 
 931             node
.parentNode
.insertBefore(text
, node
) 
 933             # Append newline after last child, except for text nodes 
 934             if children
[-1].nodeType 
== minidom
.Node
.ELEMENT_NODE
: 
 935                 text 
= self
.domCopy
.createTextNode('\n' + ' ' * indent
) 
 936                 node
.appendChild(text
) 
 937             # Indent children which are elements 
 939                 if n
.nodeType 
== minidom
.Node
.ELEMENT_NODE
: 
 940                     self
.Indent(n
, indent 
+ 2) 
 942     def Save(self
, path
): 
 946             if tree
.selection 
and panel
.IsModified(): 
 947                 self
.OnRefresh(wxCommandEvent()) 
 948             f 
= codecs
.open(path
, 'w', g
.currentEncoding
) 
 949             # Make temporary copy for formatting it 
 950             # !!! We can't clone dom node, it works only once 
 951             #self.domCopy = tree.dom.cloneNode(True) 
 952             self
.domCopy 
= MyDocument() 
 953             mainNode 
= self
.domCopy
.appendChild(tree
.mainNode
.cloneNode(True)) 
 954             self
.Indent(mainNode
) 
 955             self
.domCopy
.writexml(f
, encoding 
= g
.currentEncoding
) 
 957             self
.domCopy
.unlink() 
 959             self
.modified 
= False 
 960             panel
.SetModified(False) 
 962             wxLogError('Error writing file: %s' % path
) 
 966         if not (self
.modified 
or panel
.IsModified()): return True 
 967         flags 
= wxICON_EXCLAMATION | wxYES_NO | wxCANCEL | wxCENTRE
 
 968         dlg 
= wxMessageDialog( self
, 'File is modified. Save before exit?', 
 969                                'Save before too late?', flags 
) 
 970         say 
= dlg
.ShowModal() 
 973             self
.OnSaveOrSaveAs(wxCommandEvent(wxID_SAVE
)) 
 974             # If save was successful, modified flag is unset 
 975             if not self
.modified
: return True 
 977             self
.modified 
= False 
 978             panel
.SetModified(False) 
 985 ################################################################################ 
 988     print >> sys
.stderr
, 'usage: xrced [-dhiv] [file]' 
 993         # Process comand-line 
 996             opts
, args 
= getopt
.getopt(sys
.argv
[1:], 'dhiv') 
1004                     print 'XRCed version', version
 
1007         except getopt
.GetoptError
: 
1008             if wxPlatform 
!= '__WXMAC__': # macs have some extra parameters 
1009                 print >> sys
.stderr
, 'Unknown option' 
1013         self
.SetAppName('xrced') 
1016         conf 
= g
.conf 
= wxConfig(style 
= wxCONFIG_USE_LOCAL_FILE
) 
1017         conf
.autoRefresh 
= conf
.ReadInt('autorefresh', True) 
1018         pos 
= conf
.ReadInt('x', -1), conf
.ReadInt('y', -1) 
1019         size 
= conf
.ReadInt('width', 800), conf
.ReadInt('height', 600) 
1020         conf
.embedPanel 
= conf
.ReadInt('embedPanel', True) 
1021         conf
.showTools 
= conf
.ReadInt('showTools', True) 
1022         conf
.sashPos 
= conf
.ReadInt('sashPos', 200) 
1023         # read recently used files 
1024         recentfiles
=conf
.Read('recentFiles','') 
1027             for fil 
in recentfiles
.split('|'): 
1028                 conf
.recentfiles
[wxNewId()]=fil
 
1029         if not conf
.embedPanel
: 
1030             conf
.panelX 
= conf
.ReadInt('panelX', -1) 
1031             conf
.panelY 
= conf
.ReadInt('panelY', -1) 
1033             conf
.panelX 
= conf
.panelY 
= -1 
1034         conf
.panelWidth 
= conf
.ReadInt('panelWidth', 200) 
1035         conf
.panelHeight 
= conf
.ReadInt('panelHeight', 200) 
1036         conf
.panic 
= not conf
.HasEntry('nopanic') 
1038         wxFileSystem_AddHandler(wxMemoryFSHandler()) 
1039         wxInitAllImageHandlers() 
1041         frame 
= Frame(pos
, size
) 
1043         # Load resources from XRC file (!!! should be transformed to .py later?) 
1044         frame
.res 
= wxXmlResource('') 
1045         # !!! Temporary blocking of assert failure occuring in unicode build 
1047             frame
.res
.Load(os
.path
.join(basePath
, 'xrced.xrc')) 
1048         except wx
._core
.PyAssertionError
: 
1051         # Load file after showing 
1054             frame
.open = frame
.Open(args
[0]) 
1061         wc 
= wxConfigBase_Get() 
1062         wc
.WriteInt('autorefresh', conf
.autoRefresh
) 
1063         wc
.WriteInt('x', conf
.x
) 
1064         wc
.WriteInt('y', conf
.y
) 
1065         wc
.WriteInt('width', conf
.width
) 
1066         wc
.WriteInt('height', conf
.height
) 
1067         wc
.WriteInt('embedPanel', conf
.embedPanel
) 
1068         wc
.WriteInt('showTools', conf
.showTools
) 
1069         if not conf
.embedPanel
: 
1070             wc
.WriteInt('panelX', conf
.panelX
) 
1071             wc
.WriteInt('panelY', conf
.panelY
) 
1072         wc
.WriteInt('sashPos', conf
.sashPos
) 
1073         wc
.WriteInt('panelWidth', conf
.panelWidth
) 
1074         wc
.WriteInt('panelHeight', conf
.panelHeight
) 
1075         wc
.WriteInt('nopanic', True) 
1076         wc
.Write('recentFiles', '|'.join(conf
.recentfiles
.values()[-5:])) 
1080     app 
= App(0, useBestVisual
=False) 
1081     #app.SetAssertMode(wxPYAPP_ASSERT_LOG) 
1087 if __name__ 
== '__main__':