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)) 
  75         ok
.SetConstraints(Layoutf('b=b5#1;x%w50#1;w!80;h!25', (self
,))) 
  76         self
.SetAutoLayout(True) 
  78         self
.CenterOnScreen(wxBOTH
) 
  80 ################################################################################ 
  83     def __init__(self
, pos
, size
): 
  84         wxFrame
.__init
__(self
, None, -1, '', pos
, size
) 
  86         frame 
= g
.frame 
= self
 
  87         bar 
= self
.CreateStatusBar(2) 
  88         bar
.SetStatusWidths([-1, 40]) 
  89         self
.SetIcon(images
.getIconIcon()) 
  98         menu
.Append(wxID_NEW
, '&New\tCtrl-N', 'New file') 
  99         menu
.Append(wxID_OPEN
, '&Open...\tCtrl-O', 'Open XRC file') 
 100         menu
.Append(wxID_SAVE
, '&Save\tCtrl-S', 'Save XRC file') 
 101         menu
.Append(wxID_SAVEAS
, 'Save &As...', 'Save XRC file under different name') 
 102         menu
.AppendSeparator() 
 103         menu
.Append(wxID_EXIT
, '&Quit\tCtrl-Q', 'Exit application') 
 104         menuBar
.Append(menu
, '&File') 
 107         menu
.Append(wxID_UNDO
, '&Undo\tCtrl-Z', 'Undo') 
 108         menu
.Append(wxID_REDO
, '&Redo\tCtrl-Y', 'Redo') 
 109         menu
.AppendSeparator() 
 110         menu
.Append(wxID_CUT
, 'Cut\tCtrl-X', 'Cut to the clipboard') 
 111         menu
.Append(wxID_COPY
, '&Copy\tCtrl-C', 'Copy to the clipboard') 
 112         menu
.Append(wxID_PASTE
, '&Paste\tCtrl-V', 'Paste from the clipboard') 
 113         self
.ID_DELETE 
= wxNewId() 
 114         menu
.Append(self
.ID_DELETE
, '&Delete\tCtrl-D', 'Delete object') 
 115 #        menu.AppendSeparator() 
 116         ID_SELECT 
= wxNewId() 
 117 #        menu.Append(ID_SELECT, '&Select', 'Select object') 
 118         menuBar
.Append(menu
, '&Edit') 
 121         self
.ID_EMBED_PANEL 
= wxNewId() 
 122         menu
.Append(self
.ID_EMBED_PANEL
, '&Embed Panel', 
 123                     'Toggle embedding properties panel in the main window', True) 
 124         menu
.Check(self
.ID_EMBED_PANEL
, conf
.embedPanel
) 
 125         self
.ID_SHOW_TOOLS 
= wxNewId() 
 126         menu
.Append(self
.ID_SHOW_TOOLS
, 'Show &Tools', 'Toggle tools', True) 
 127         menu
.Check(self
.ID_SHOW_TOOLS
, conf
.showTools
) 
 128         menu
.AppendSeparator() 
 129         self
.ID_TEST 
= wxNewId() 
 130         menu
.Append(self
.ID_TEST
, '&Test\tF5', 'Test window') 
 131         self
.ID_REFRESH 
= wxNewId() 
 132         menu
.Append(self
.ID_REFRESH
, '&Refresh\tCtrl-R', 'Refresh test window') 
 133         self
.ID_AUTO_REFRESH 
= wxNewId() 
 134         menu
.Append(self
.ID_AUTO_REFRESH
, '&Auto-refresh\tCtrl-A', 
 135                     'Toggle auto-refresh mode', True) 
 136         menu
.Check(self
.ID_AUTO_REFRESH
, conf
.autoRefresh
) 
 137         menuBar
.Append(menu
, '&View') 
 140         menu
.Append(wxID_ABOUT
, '&About...', 'About XCRed') 
 141         self
.ID_README 
= wxNewId() 
 142         menu
.Append(self
.ID_README
, '&Readme...', 'View the README file') 
 144             self
.ID_DEBUG_CMD 
= wxNewId() 
 145             menu
.Append(self
.ID_DEBUG_CMD
, 'CMD', 'Python command line') 
 146             EVT_MENU(self
, self
.ID_DEBUG_CMD
, self
.OnDebugCMD
) 
 147         menuBar
.Append(menu
, '&Help') 
 149         self
.menuBar 
= menuBar
 
 150         self
.SetMenuBar(menuBar
) 
 153         tb 
= self
.CreateToolBar(wxTB_HORIZONTAL | wxNO_BORDER | wxTB_FLAT
) 
 154         tb
.SetToolBitmapSize((24, 23)) 
 155         tb
.AddSimpleTool(wxID_NEW
, images
.getNewBitmap(), 'New', 'New file') 
 156         tb
.AddSimpleTool(wxID_OPEN
, images
.getOpenBitmap(), 'Open', 'Open file') 
 157         tb
.AddSimpleTool(wxID_SAVE
, images
.getSaveBitmap(), 'Save', 'Save file') 
 158         tb
.AddControl(wxStaticLine(tb
, -1, size
=(-1,23), style
=wxLI_VERTICAL
)) 
 159         tb
.AddSimpleTool(wxID_UNDO
, images
.getUndoBitmap(), 'Undo', 'Undo') 
 160         tb
.AddSimpleTool(wxID_REDO
, images
.getRedoBitmap(), 'Redo', 'Redo') 
 161         tb
.AddControl(wxStaticLine(tb
, -1, size
=(-1,23), style
=wxLI_VERTICAL
)) 
 162         tb
.AddSimpleTool(wxID_CUT
, images
.getCutBitmap(), 'Cut', 'Cut') 
 163         tb
.AddSimpleTool(wxID_COPY
, images
.getCopyBitmap(), 'Copy', 'Copy') 
 164         tb
.AddSimpleTool(wxID_PASTE
, images
.getPasteBitmap(), 'Paste', 'Paste') 
 165         tb
.AddControl(wxStaticLine(tb
, -1, size
=(-1,23), style
=wxLI_VERTICAL
)) 
 166         tb
.AddSimpleTool(self
.ID_TEST
, images
.getTestBitmap(), 'Test', 'Test window') 
 167         tb
.AddSimpleTool(self
.ID_REFRESH
, images
.getRefreshBitmap(), 
 168                          'Refresh', 'Refresh view') 
 169         tb
.AddSimpleTool(self
.ID_AUTO_REFRESH
, images
.getAutoRefreshBitmap(), 
 170                          'Auto-refresh', 'Toggle auto-refresh mode', True) 
 171         if wxPlatform 
== '__WXGTK__': 
 172             tb
.AddSeparator()   # otherwise auto-refresh sticks in status line 
 173         tb
.ToggleTool(self
.ID_AUTO_REFRESH
, conf
.autoRefresh
) 
 176         self
.minWidth 
= tb
.GetSize()[0] # minimal width is the size of toolbar 
 179         EVT_MENU(self
, wxID_NEW
, self
.OnNew
) 
 180         EVT_MENU(self
, wxID_OPEN
, self
.OnOpen
) 
 181         EVT_MENU(self
, wxID_SAVE
, self
.OnSaveOrSaveAs
) 
 182         EVT_MENU(self
, wxID_SAVEAS
, self
.OnSaveOrSaveAs
) 
 183         EVT_MENU(self
, wxID_EXIT
, self
.OnExit
) 
 185         EVT_MENU(self
, wxID_UNDO
, self
.OnUndo
) 
 186         EVT_MENU(self
, wxID_REDO
, self
.OnRedo
) 
 187         EVT_MENU(self
, wxID_CUT
, self
.OnCutDelete
) 
 188         EVT_MENU(self
, wxID_COPY
, self
.OnCopy
) 
 189         EVT_MENU(self
, wxID_PASTE
, self
.OnPaste
) 
 190         EVT_MENU(self
, self
.ID_DELETE
, self
.OnCutDelete
) 
 191         EVT_MENU(self
, ID_SELECT
, self
.OnSelect
) 
 193         EVT_MENU(self
, self
.ID_EMBED_PANEL
, self
.OnEmbedPanel
) 
 194         EVT_MENU(self
, self
.ID_SHOW_TOOLS
, self
.OnShowTools
) 
 195         EVT_MENU(self
, self
.ID_TEST
, self
.OnTest
) 
 196         EVT_MENU(self
, self
.ID_REFRESH
, self
.OnRefresh
) 
 197         EVT_MENU(self
, self
.ID_AUTO_REFRESH
, self
.OnAutoRefresh
) 
 199         EVT_MENU(self
, wxID_ABOUT
, self
.OnAbout
) 
 200         EVT_MENU(self
, self
.ID_README
, self
.OnReadme
) 
 203         EVT_UPDATE_UI(self
, wxID_CUT
, self
.OnUpdateUI
) 
 204         EVT_UPDATE_UI(self
, wxID_COPY
, self
.OnUpdateUI
) 
 205         EVT_UPDATE_UI(self
, wxID_PASTE
, self
.OnUpdateUI
) 
 206         EVT_UPDATE_UI(self
, wxID_UNDO
, self
.OnUpdateUI
) 
 207         EVT_UPDATE_UI(self
, wxID_REDO
, self
.OnUpdateUI
) 
 208         EVT_UPDATE_UI(self
, self
.ID_DELETE
, self
.OnUpdateUI
) 
 209         EVT_UPDATE_UI(self
, self
.ID_TEST
, self
.OnUpdateUI
) 
 210         EVT_UPDATE_UI(self
, self
.ID_REFRESH
, self
.OnUpdateUI
) 
 213         sizer 
= wxBoxSizer(wxVERTICAL
) 
 214         sizer
.Add(wxStaticLine(self
, -1), 0, wxEXPAND
) 
 215         # Horizontal sizer for toolbar and splitter 
 216         self
.toolsSizer 
= sizer1 
= wxBoxSizer() 
 217         splitter 
= wxSplitterWindow(self
, -1, style
=wxSP_3DSASH
) 
 218         self
.splitter 
= splitter
 
 219         splitter
.SetMinimumPaneSize(100) 
 222         g
.tree 
= tree 
= XML_Tree(splitter
, -1) 
 224         # Init pull-down menu data 
 226         g
.pullDownMenu 
= pullDownMenu 
= PullDownMenu(self
) 
 228         # Vertical toolbar for GUI buttons 
 229         g
.tools 
= tools 
= Tools(self
) 
 230         tools
.Show(conf
.showTools
) 
 231         if conf
.showTools
: sizer1
.Add(tools
, 0, wxEXPAND
) 
 233         tree
.RegisterKeyEvents() 
 235         # !!! frame styles are broken 
 236         # Miniframe for not embedded mode 
 237         miniFrame 
= wxFrame(self
, -1, 'Properties Panel', 
 238                             (conf
.panelX
, conf
.panelY
), 
 239                             (conf
.panelWidth
, conf
.panelHeight
)) 
 240         self
.miniFrame 
= miniFrame
 
 241         sizer2 
= wxBoxSizer() 
 242         miniFrame
.SetAutoLayout(True) 
 243         miniFrame
.SetSizer(sizer2
) 
 244         EVT_CLOSE(self
.miniFrame
, self
.OnCloseMiniFrame
) 
 245         # Create panel for parameters 
 248             panel 
= Panel(splitter
) 
 249             # Set plitter windows 
 250             splitter
.SplitVertically(tree
, panel
, conf
.sashPos
) 
 252             panel 
= Panel(miniFrame
) 
 253             sizer2
.Add(panel
, 1, wxEXPAND
) 
 255             splitter
.Initialize(tree
) 
 256         sizer1
.Add(splitter
, 1, wxEXPAND
) 
 257         sizer
.Add(sizer1
, 1, wxEXPAND
) 
 258         self
.SetAutoLayout(True) 
 262         self
.clipboard 
= None 
 266         EVT_IDLE(self
, self
.OnIdle
) 
 267         EVT_CLOSE(self
, self
.OnCloseWindow
) 
 268         EVT_LEFT_DOWN(self
, self
.OnLeftDown
) 
 269         EVT_KEY_DOWN(self
, tools
.OnKeyDown
) 
 270         EVT_KEY_UP(self
, tools
.OnKeyUp
) 
 272     def OnNew(self
, evt
): 
 273         if not self
.AskSave(): return 
 276     def OnOpen(self
, evt
): 
 277         if not self
.AskSave(): return 
 278         dlg 
= wxFileDialog(self
, 'Open', os
.path
.dirname(self
.dataFile
), 
 279                            '', '*.xrc', wxOPEN | wxCHANGE_DIR
) 
 280         if dlg
.ShowModal() == wxID_OK
: 
 282             self
.SetStatusText('Loading...') 
 286                 self
.SetStatusText('Data loaded') 
 288                 self
.SetStatusText('Failed') 
 292     def OnSaveOrSaveAs(self
, evt
): 
 293         if evt
.GetId() == wxID_SAVEAS 
or not self
.dataFile
: 
 294             if self
.dataFile
: defaultName 
= '' 
 295             else: defaultName 
= 'UNTITLED.xrc' 
 296             dlg 
= wxFileDialog(self
, 'Save As', os
.path
.dirname(self
.dataFile
), 
 297                                defaultName
, '*.xrc', 
 298                                wxSAVE | wxOVERWRITE_PROMPT | wxCHANGE_DIR
) 
 299             if dlg
.ShowModal() == wxID_OK
: 
 307         self
.SetStatusText('Saving...') 
 313             self
.SetStatusText('Data saved') 
 315             self
.SetStatusText('Failed') 
 318     def OnExit(self
, evt
): 
 321     def OnUndo(self
, evt
): 
 322         # Extra check to not mess with idle updating 
 323         if undoMan
.CanUndo(): 
 326     def OnRedo(self
, evt
): 
 327         if undoMan
.CanRedo(): 
 330     def OnCopy(self
, evt
): 
 331         selected 
= tree
.selection
 
 332         if not selected
: return         # key pressed event 
 333         xxx 
= tree
.GetPyData(selected
) 
 334         self
.clipboard 
= xxx
.element
.cloneNode(True) 
 335         self
.SetStatusText('Copied') 
 337     def OnPaste(self
, evt
): 
 338         selected 
= tree
.selection
 
 339         if not selected
: return         # key pressed event 
 340         # For pasting with Ctrl pressed 
 341         if evt
.GetId() == pullDownMenu
.ID_PASTE_SIBLING
: appendChild 
= False 
 342         else: appendChild 
= not tree
.NeedInsert(selected
) 
 343         xxx 
= tree
.GetPyData(selected
) 
 345             # If has next item, insert, else append to parent 
 346             nextItem 
= tree
.GetNextSibling(selected
) 
 347             parentLeaf 
= tree
.GetItemParent(selected
) 
 348         # Expanded container (must have children) 
 349         elif tree
.IsExpanded(selected
) and tree
.GetChildrenCount(selected
, False): 
 350             # Insert as first child 
 351             nextItem 
= tree
.GetFirstChild(selected
)[0] 
 352             parentLeaf 
= selected
 
 354             # No children or unexpanded item - appendChild stays True 
 355             nextItem 
= wxTreeItemId()   # no next item 
 356             parentLeaf 
= selected
 
 357         parent 
= tree
.GetPyData(parentLeaf
).treeObject() 
 359         # Create a copy of clipboard element 
 360         elem 
= self
.clipboard
.cloneNode(True) 
 361         # Tempopary xxx object to test things 
 362         xxx 
= MakeXXXFromDOM(parent
, elem
) 
 364         # Check compatibility 
 368         if x
.__class
__ in [xxxDialog
, xxxFrame
, xxxMenuBar
]: 
 370             if parent
.__class
__ != xxxMainNode
: error 
= True 
 371         elif x
.__class
__ == xxxToolBar
: 
 372             # Toolbar can be top-level of child of panel or frame 
 373             if parent
.__class
__ not in [xxxMainNode
, xxxPanel
, xxxFrame
]: error 
= True 
 374         elif x
.__class
__ == xxxPanel 
and parent
.__class
__ == xxxMainNode
: 
 376         elif x
.__class
__ == xxxSpacer
: 
 377             if not parent
.isSizer
: error 
= True 
 378         elif x
.__class
__ == xxxSeparator
: 
 379             if not parent
.__class
__ in [xxxMenu
, xxxToolBar
]: error 
= True 
 380         elif x
.__class
__ == xxxTool
: 
 381             if parent
.__class
__ != xxxToolBar
: error 
= True 
 382         elif x
.__class
__ == xxxMenu
: 
 383             if not parent
.__class
__ in [xxxMainNode
, xxxMenuBar
, xxxMenu
]: error 
= True 
 384         elif x
.__class
__ == xxxMenuItem
: 
 385             if not parent
.__class
__ in [xxxMenuBar
, xxxMenu
]: error 
= True 
 386         elif x
.isSizer 
and parent
.__class
__ == xxxNotebook
: error 
= True 
 387         else:                           # normal controls can be almost anywhere 
 388             if parent
.__class
__ == xxxMainNode 
or \
 
 389                parent
.__class
__ in [xxxMenuBar
, xxxMenu
]: error 
= True 
 391             if parent
.__class
__ == xxxMainNode
: parentClass 
= 'root' 
 392             else: parentClass 
= parent
.className
 
 393             wxLogError('Incompatible parent/child: parent is %s, child is %s!' % 
 394                        (parentClass
, x
.className
)) 
 397         # Check parent and child relationships. 
 398         # If parent is sizer or notebook, child is of wrong class or 
 399         # parent is normal window, child is child container then detach child. 
 400         isChildContainer 
= isinstance(xxx
, xxxChildContainer
) 
 401         if isChildContainer 
and \
 
 402            ((parent
.isSizer 
and not isinstance(xxx
, xxxSizerItem
)) or \
 
 403             (isinstance(parent
, xxxNotebook
) and not isinstance(xxx
, xxxNotebookPage
)) or \
 
 404            not (parent
.isSizer 
or isinstance(parent
, xxxNotebook
))): 
 405             elem
.removeChild(xxx
.child
.element
) # detach child 
 406             elem
.unlink()           # delete child container 
 407             elem 
= xxx
.child
.element 
# replace 
 408             # This may help garbage collection 
 409             xxx
.child
.parent 
= None 
 410             isChildContainer 
= False 
 411         # Parent is sizer or notebook, child is not child container 
 412         if parent
.isSizer 
and not isChildContainer 
and not isinstance(xxx
, xxxSpacer
): 
 413             # Create sizer item element 
 414             sizerItemElem 
= MakeEmptyDOM('sizeritem') 
 415             sizerItemElem
.appendChild(elem
) 
 417         elif isinstance(parent
, xxxNotebook
) and not isChildContainer
: 
 418             pageElem 
= MakeEmptyDOM('notebookpage') 
 419             pageElem
.appendChild(elem
) 
 421         # Insert new node, register undo 
 422         newItem 
= tree
.InsertNode(parentLeaf
, parent
, elem
, nextItem
) 
 423         undoMan
.RegisterUndo(UndoPasteCreate(parentLeaf
, parent
, newItem
, selected
)) 
 424         # Scroll to show new item (!!! redundant?) 
 425         tree
.EnsureVisible(newItem
) 
 426         tree
.SelectItem(newItem
) 
 427         if not tree
.IsVisible(newItem
): 
 428             tree
.ScrollTo(newItem
) 
 431         if g
.testWin 
and tree
.IsHighlatable(newItem
): 
 433                 tree
.needUpdate 
= True 
 434                 tree
.pendingHighLight 
= newItem
 
 436                 tree
.pendingHighLight 
= None 
 438         self
.SetStatusText('Pasted') 
 440     def OnCutDelete(self
, evt
): 
 441         selected 
= tree
.selection
 
 442         if not selected
: return         # key pressed event 
 444         if evt
.GetId() == wxID_CUT
: 
 446             status 
= 'Removed to clipboard' 
 448             self
.lastOp 
= 'DELETE' 
 452             # If deleting top-level item, delete testWin 
 453             if selected 
== g
.testWin
.item
: 
 457                 # Remove highlight, update testWin 
 458                 if g
.testWin
.highLight
: 
 459                     g
.testWin
.highLight
.Remove() 
 460                 tree
.needUpdate 
= True 
 463         index 
= tree
.ItemFullIndex(selected
) 
 464         parent 
= tree
.GetPyData(tree
.GetItemParent(selected
)).treeObject() 
 465         elem 
= tree
.RemoveLeaf(selected
) 
 466         undoMan
.RegisterUndo(UndoCutDelete(index
, parent
, elem
)) 
 467         if evt
.GetId() == wxID_CUT
: 
 468             if self
.clipboard
: self
.clipboard
.unlink() 
 469             self
.clipboard 
= elem
.cloneNode(True) 
 470         tree
.pendingHighLight 
= None 
 474         self
.SetStatusText(status
) 
 476     def OnSubclass(self
, evt
): 
 477         selected 
= tree
.selection
 
 478         xxx 
= tree
.GetPyData(selected
).treeObject() 
 480         subclass 
= xxx
.subclass
 
 481         dlg 
= wxTextEntryDialog(self
, 'Subclass:', defaultValue
=subclass
) 
 482         if dlg
.ShowModal() == wxID_OK
: 
 483             subclass 
= dlg
.GetValue() 
 485                 elem
.setAttribute('subclass', subclass
) 
 487             elif elem
.hasAttribute('subclass'): 
 488                 elem
.removeAttribute('subclass') 
 490             xxx
.subclass 
= elem
.getAttribute('subclass') 
 491             tree
.SetItemText(selected
, xxx
.treeName()) 
 492             panel
.pages
[0].box
.SetLabel(xxx
.panelName()) 
 495     def OnSelect(self
, evt
): 
 496         print >> sys
.stderr
, 'Xperimental function!' 
 498         self
.SetCursor(wxCROSS_CURSOR
) 
 501     def OnLeftDown(self
, evt
): 
 502         pos 
= evt
.GetPosition() 
 503         self
.SetCursor(wxNullCursor
) 
 506     def OnEmbedPanel(self
, evt
): 
 507         conf
.embedPanel 
= evt
.IsChecked() 
 509             # Remember last dimentions 
 510             conf
.panelX
, conf
.panelY 
= self
.miniFrame
.GetPosition() 
 511             conf
.panelWidth
, conf
.panelHeight 
= self
.miniFrame
.GetSize() 
 512             size 
= self
.GetSize() 
 513             pos 
= self
.GetPosition() 
 514             sizePanel 
= panel
.GetSize() 
 515             panel
.Reparent(self
.splitter
) 
 516             self
.miniFrame
.GetSizer().RemoveWindow(panel
) 
 519             self
.SetDimensions(pos
.x
, pos
.y
, size
.width 
+ sizePanel
.width
, size
.height
) 
 520             self
.splitter
.SplitVertically(tree
, panel
, conf
.sashPos
) 
 521             self
.miniFrame
.Show(False) 
 523             conf
.sashPos 
= self
.splitter
.GetSashPosition() 
 524             pos 
= self
.GetPosition() 
 525             size 
= self
.GetSize() 
 526             sizePanel 
= panel
.GetSize() 
 527             self
.splitter
.Unsplit(panel
) 
 528             sizer 
= self
.miniFrame
.GetSizer() 
 529             panel
.Reparent(self
.miniFrame
) 
 531             sizer
.Add(panel
, 1, wxEXPAND
) 
 532             self
.miniFrame
.Show(True) 
 533             self
.miniFrame
.SetDimensions(conf
.panelX
, conf
.panelY
, 
 534                                          conf
.panelWidth
, conf
.panelHeight
) 
 537             self
.SetDimensions(pos
.x
, pos
.y
, 
 538                                max(size
.width 
- sizePanel
.width
, self
.minWidth
), size
.height
) 
 540     def OnShowTools(self
, evt
): 
 541         conf
.showTools 
= evt
.IsChecked() 
 542         g
.tools
.Show(conf
.showTools
) 
 544             self
.toolsSizer
.Prepend(g
.tools
, 0, wxEXPAND
) 
 546             self
.toolsSizer
.Remove(g
.tools
) 
 547         self
.toolsSizer
.Layout() 
 549     def OnTest(self
, evt
): 
 550         if not tree
.selection
: return   # key pressed event 
 551         tree
.ShowTestWindow(tree
.selection
) 
 553     def OnRefresh(self
, evt
): 
 554         # If modified, apply first 
 555         selection 
= tree
.selection
 
 557             xxx 
= tree
.GetPyData(selection
) 
 558             if xxx 
and panel
.IsModified(): 
 559                 tree
.Apply(xxx
, selection
) 
 562             tree
.CreateTestWin(g
.testWin
.item
) 
 563         panel
.modified 
= False 
 564         tree
.needUpdate 
= False 
 566     def OnAutoRefresh(self
, evt
): 
 567         conf
.autoRefresh 
= evt
.IsChecked() 
 568         self
.menuBar
.Check(self
.ID_AUTO_REFRESH
, conf
.autoRefresh
) 
 569         self
.tb
.ToggleTool(self
.ID_AUTO_REFRESH
, conf
.autoRefresh
) 
 571     def OnAbout(self
, evt
): 
 575 (c) Roman Rolinsky <rollrom@users.sourceforge.net> 
 576 Homepage: http://xrced.sourceforge.net\ 
 578         dlg 
= wxMessageDialog(self
, str, 'About XRCed', wxOK | wxCENTRE
) 
 582     def OnReadme(self
, evt
): 
 583         text 
= open(os
.path
.join(basePath
, 'README.txt'), 'r').read() 
 584         dlg 
= ScrolledMessageDialog(self
, text
, "XRCed README") 
 588     # Simple emulation of python command line 
 589     def OnDebugCMD(self
, evt
): 
 593                 exec raw_input('C:\> ') 
 598                 (etype
, value
, tb
) =sys
.exc_info() 
 599                 tblist 
=traceback
.extract_tb(tb
)[1:] 
 600                 msg 
=' '.join(traceback
.format_exception_only(etype
, value
) 
 601                         +traceback
.format_list(tblist
)) 
 604     def OnCreate(self
, evt
): 
 605         selected 
= tree
.selection
 
 606         if tree
.ctrl
: appendChild 
= False 
 607         else: appendChild 
= not tree
.NeedInsert(selected
) 
 608         xxx 
= tree
.GetPyData(selected
) 
 612                 # If has previous item, insert after it, else append to parent 
 614                 parentLeaf 
= tree
.GetItemParent(selected
) 
 616                 # If has next item, insert, else append to parent 
 617                 nextItem 
= tree
.GetNextSibling(selected
) 
 618                 parentLeaf 
= tree
.GetItemParent(selected
) 
 619         # Expanded container (must have children) 
 620         elif tree
.shift 
and tree
.IsExpanded(selected
) \
 
 621            and tree
.GetChildrenCount(selected
, False): 
 622             nextItem 
= tree
.GetFirstChild(selected
)[0] 
 623             parentLeaf 
= selected
 
 625             nextItem 
= wxTreeItemId() 
 626             parentLeaf 
= selected
 
 627         parent 
= tree
.GetPyData(parentLeaf
) 
 628         if parent
.hasChild
: parent 
= parent
.child
 
 631         className 
= pullDownMenu
.createMap
[evt
.GetId()] 
 632         xxx 
= MakeEmptyXXX(parent
, className
) 
 634         # Set default name for top-level windows 
 635         if parent
.__class
__ == xxxMainNode
: 
 636             cl 
= xxx
.treeObject().__class
__ 
 637             frame
.maxIDs
[cl
] += 1 
 638             xxx
.treeObject().name 
= '%s%d' % (defaultIDs
[cl
], frame
.maxIDs
[cl
]) 
 639             xxx
.treeObject().element
.setAttribute('name', xxx
.treeObject().name
) 
 641         # Insert new node, register undo 
 643         newItem 
= tree
.InsertNode(parentLeaf
, parent
, elem
, nextItem
) 
 644         undoMan
.RegisterUndo(UndoPasteCreate(parentLeaf
, parent
, newItem
, selected
)) 
 645         tree
.EnsureVisible(newItem
) 
 646         tree
.SelectItem(newItem
) 
 647         if not tree
.IsVisible(newItem
): 
 648             tree
.ScrollTo(newItem
) 
 651         if g
.testWin 
and tree
.IsHighlatable(newItem
): 
 653                 tree
.needUpdate 
= True 
 654                 tree
.pendingHighLight 
= newItem
 
 656                 tree
.pendingHighLight 
= None 
 660     # Replace one object with another 
 661     def OnReplace(self
, evt
): 
 662         selected 
= tree
.selection
 
 663         xxx 
= tree
.GetPyData(selected
).treeObject() 
 665         parent 
= elem
.parentNode
 
 666         parentXXX 
= xxx
.parent
 
 668         className 
= pullDownMenu
.createMap
[evt
.GetId() - 1000] 
 669         # Create temporary empty node (with default values) 
 670         dummy 
= MakeEmptyDOM(className
) 
 671         xxxClass 
= xxxDict
[className
] 
 672         # Remove non-compatible children 
 673         if tree
.ItemHasChildren(selected
) and not xxxClass
.hasChildren
: 
 674             tree
.DeleteChildren(selected
) 
 675         nodes 
= elem
.childNodes
[:] 
 681                 if not xxxClass
.hasChildren
: 
 683             elif tag 
not in xxxClass
.allParams 
and \
 
 684                      (not xxxClass
.hasStyle 
or tag 
not in xxxClass
.styles
): 
 689                 elem
.removeChild(node
) 
 692         # Copy parameters present in dummy but not in elem 
 693         for node 
in dummy
.childNodes
: 
 696                 elem
.appendChild(node
.cloneNode(True)) 
 699         elem
.setAttribute('class', className
)         
 700         # Re-create xxx element 
 701         xxx 
= MakeXXXFromDOM(parentXXX
, elem
) 
 702         # Update parent in child objects 
 703         if tree
.ItemHasChildren(selected
): 
 704             i
, cookie 
= tree
.GetFirstChild(selected
) 
 706                 x 
= tree
.GetPyData(i
) 
 708                 if x
.hasChild
: x
.child
.parent 
= xxx
 
 709                 i
, cookie 
= tree
.GetNextChild(selected
, cookie
) 
 712         if tree
.GetPyData(selected
).hasChild
: # child container 
 713             container 
= tree
.GetPyData(selected
) 
 714             container
.child 
= xxx
 
 715             container
.hasChildren 
= xxx
.hasChildren
 
 716             container
.isSizer 
= xxx
.isSizer
 
 718             tree
.SetPyData(selected
, xxx
) 
 719         tree
.SetItemText(selected
, xxx
.treeName()) 
 720         tree
.SetItemImage(selected
, xxx
.treeImage()) 
 722         # Set default name for top-level windows 
 723         if parent
.__class
__ == xxxMainNode
: 
 724             cl 
= xxx
.treeObject().__class
__ 
 725             frame
.maxIDs
[cl
] += 1 
 726             xxx
.treeObject().name 
= '%s%d' % (defaultIDs
[cl
], frame
.maxIDs
[cl
]) 
 727             xxx
.treeObject().element
.setAttribute('name', xxx
.treeObject().name
) 
 734         #undoMan.RegisterUndo(UndoPasteCreate(parentLeaf, parent, newItem, selected)) 
 736         if g
.testWin 
and tree
.IsHighlatable(selected
): 
 738                 tree
.needUpdate 
= True 
 739                 tree
.pendingHighLight 
= selected
 
 741                 tree
.pendingHighLight 
= None 
 745     # Expand/collapse subtree 
 746     def OnExpand(self
, evt
): 
 747         if tree
.selection
: tree
.ExpandAll(tree
.selection
) 
 748         else: tree
.ExpandAll(tree
.root
) 
 749     def OnCollapse(self
, evt
): 
 750         if tree
.selection
: tree
.CollapseAll(tree
.selection
) 
 751         else: tree
.CollapseAll(tree
.root
) 
 753     def OnPullDownHighlight(self
, evt
): 
 754         menuId 
= evt
.GetMenuId() 
 756             menu 
= evt
.GetEventObject() 
 757             help = menu
.GetHelpString(menuId
) 
 758             self
.SetStatusText(help) 
 760             self
.SetStatusText('') 
 762     def OnUpdateUI(self
, evt
): 
 763         if evt
.GetId() in [wxID_CUT
, wxID_COPY
, self
.ID_DELETE
]: 
 764             evt
.Enable(tree
.selection 
is not None and tree
.selection 
!= tree
.root
) 
 765         elif evt
.GetId() == wxID_PASTE
: 
 766             evt
.Enable((self
.clipboard 
and tree
.selection
) != None) 
 767         elif evt
.GetId() == self
.ID_TEST
: 
 768             evt
.Enable(tree
.selection 
is not None and tree
.selection 
!= tree
.root
) 
 769         elif evt
.GetId() == wxID_UNDO
:  evt
.Enable(undoMan
.CanUndo()) 
 770         elif evt
.GetId() == wxID_REDO
:  evt
.Enable(undoMan
.CanRedo()) 
 772     def OnIdle(self
, evt
): 
 773         if self
.inIdle
: return          # Recursive call protection 
 778                     self
.SetStatusText('Refreshing test window...') 
 780                     tree
.CreateTestWin(g
.testWin
.item
) 
 782                     self
.SetStatusText('') 
 783                 tree
.needUpdate 
= False 
 784         elif tree
.pendingHighLight
: 
 785             tree
.HighLight(tree
.pendingHighLight
) 
 790     # We don't let close panel window 
 791     def OnCloseMiniFrame(self
, evt
): 
 794     def OnCloseWindow(self
, evt
): 
 795         if not self
.AskSave(): return 
 796         if g
.testWin
: g
.testWin
.Destroy() 
 797         # Destroy cached windows 
 798         panel
.cacheParent
.Destroy() 
 799         if not panel
.GetPageCount() == 2: 
 800             panel
.page2
.Destroy() 
 801         conf
.x
, conf
.y 
= self
.GetPosition() 
 802         conf
.width
, conf
.height 
= self
.GetSize() 
 804             conf
.sashPos 
= self
.splitter
.GetSashPosition() 
 806             conf
.panelX
, conf
.panelY 
= self
.miniFrame
.GetPosition() 
 807             conf
.panelWidth
, conf
.panelHeight 
= self
.miniFrame
.GetSize() 
 813             self
.clipboard
.unlink() 
 814             self
.clipboard 
= None 
 816         self
.modified 
= False 
 822         self
.SetTitle(progname
) 
 823         # Numbers for new controls 
 825         self
.maxIDs
[xxxPanel
] = self
.maxIDs
[xxxDialog
] = self
.maxIDs
[xxxFrame
] = \
 
 826         self
.maxIDs
[xxxMenuBar
] = self
.maxIDs
[xxxMenu
] = self
.maxIDs
[xxxToolBar
] = 0 
 828     def Open(self
, path
): 
 829         if not os
.path
.exists(path
): 
 830             wxLogError('File does not exists: %s' % path
) 
 832         # Try to read the file 
 836             # Parse first line to get encoding (!! hack, I don't know a better way) 
 838             mo 
= re
.match(r
'^<\?xml ([^<>]* )?encoding="(?P<encd>[^<>].*)"\?>', line
) 
 841             dom 
= minidom
.parse(f
) 
 842             # Set encoding global variable and document encoding property 
 844                 dom
.encoding 
= g
.currentEncoding 
= mo
.group('encd') 
 845                 if dom
.encoding 
not in ['ascii', sys
.getdefaultencoding()]: 
 846                     wxLogWarning('Encoding is different from system default') 
 848                 g
.currentEncoding 
= 'ascii' 
 852             dir = os
.path
.dirname(path
) 
 853             if dir: os
.chdir(dir) 
 856             self
.SetTitle(progname 
+ ': ' + os
.path
.basename(path
)) 
 858             # Nice exception printing 
 860             wxLogError(traceback
.format_exception(inf
[0], inf
[1], None)[-1]) 
 861             wxLogError('Error reading file: %s' % path
) 
 865     def Indent(self
, node
, indent 
= 0): 
 866         # Copy child list because it will change soon 
 867         children 
= node
.childNodes
[:] 
 868         # Main node doesn't need to be indented 
 870             text 
= self
.domCopy
.createTextNode('\n' + ' ' * indent
) 
 871             node
.parentNode
.insertBefore(text
, node
) 
 873             # Append newline after last child, except for text nodes 
 874             if children
[-1].nodeType 
== minidom
.Node
.ELEMENT_NODE
: 
 875                 text 
= self
.domCopy
.createTextNode('\n' + ' ' * indent
) 
 876                 node
.appendChild(text
) 
 877             # Indent children which are elements 
 879                 if n
.nodeType 
== minidom
.Node
.ELEMENT_NODE
: 
 880                     self
.Indent(n
, indent 
+ 2) 
 882     def Save(self
, path
): 
 885             if tree
.selection 
and panel
.IsModified(): 
 886                 self
.OnRefresh(wxCommandEvent()) 
 888             # Make temporary copy for formatting it 
 889             # !!! We can't clone dom node, it works only once 
 890             #self.domCopy = tree.dom.cloneNode(True) 
 891             self
.domCopy 
= MyDocument() 
 892             mainNode 
= self
.domCopy
.appendChild(tree
.mainNode
.cloneNode(True)) 
 893             self
.Indent(mainNode
) 
 894             self
.domCopy
.writexml(f
, encoding
=tree
.rootObj
.params
['encoding'].value()) 
 896             self
.domCopy
.unlink() 
 898             self
.modified 
= False 
 899             panel
.SetModified(False) 
 901             wxLogError('Error writing file: %s' % path
) 
 905         if not (self
.modified 
or panel
.IsModified()): return True 
 906         flags 
= wxICON_EXCLAMATION | wxYES_NO | wxCANCEL | wxCENTRE
 
 907         dlg 
= wxMessageDialog( self
, 'File is modified. Save before exit?', 
 908                                'Save before too late?', flags 
) 
 909         say 
= dlg
.ShowModal() 
 912             self
.OnSaveOrSaveAs(wxCommandEvent(wxID_SAVE
)) 
 913             # If save was successful, modified flag is unset 
 914             if not self
.modified
: return True 
 916             self
.modified 
= False 
 917             panel
.SetModified(False) 
 924 ################################################################################ 
 927     print >> sys
.stderr
, 'usage: xrced [-dhiv] [file]' 
 932         # Process comand-line 
 935             opts
, args 
= getopt
.getopt(sys
.argv
[1:], 'dhiv') 
 943                     print 'XRCed version', version
 
 946         except getopt
.GetoptError
: 
 947             if wxPlatform 
!= '__WXMAC__': # macs have some extra parameters 
 948                 print >> sys
.stderr
, 'Unknown option' 
 952         self
.SetAppName('xrced') 
 955         conf 
= g
.conf 
= wxConfig(style 
= wxCONFIG_USE_LOCAL_FILE
) 
 956         conf
.autoRefresh 
= conf
.ReadInt('autorefresh', True) 
 957         pos 
= conf
.ReadInt('x', -1), conf
.ReadInt('y', -1) 
 958         size 
= conf
.ReadInt('width', 800), conf
.ReadInt('height', 600) 
 959         conf
.embedPanel 
= conf
.ReadInt('embedPanel', True) 
 960         conf
.showTools 
= conf
.ReadInt('showTools', True) 
 961         conf
.sashPos 
= conf
.ReadInt('sashPos', 200) 
 962         if not conf
.embedPanel
: 
 963             conf
.panelX 
= conf
.ReadInt('panelX', -1) 
 964             conf
.panelY 
= conf
.ReadInt('panelY', -1) 
 966             conf
.panelX 
= conf
.panelY 
= -1 
 967         conf
.panelWidth 
= conf
.ReadInt('panelWidth', 200) 
 968         conf
.panelHeight 
= conf
.ReadInt('panelHeight', 200) 
 969         conf
.panic 
= not conf
.HasEntry('nopanic') 
 971         wxFileSystem_AddHandler(wxMemoryFSHandler()) 
 972         wxInitAllImageHandlers() 
 974         frame 
= Frame(pos
, size
) 
 976         # Load resources from XRC file (!!! should be transformed to .py later?) 
 977         frame
.res 
= wxXmlResource('') 
 978         frame
.res
.Load(os
.path
.join(basePath
, 'xrced.xrc')) 
 980         # Load file after showing 
 983             frame
.open = frame
.Open(args
[0]) 
 990         wc 
= wxConfigBase_Get() 
 991         wc
.WriteInt('autorefresh', conf
.autoRefresh
) 
 992         wc
.WriteInt('x', conf
.x
) 
 993         wc
.WriteInt('y', conf
.y
) 
 994         wc
.WriteInt('width', conf
.width
) 
 995         wc
.WriteInt('height', conf
.height
) 
 996         wc
.WriteInt('embedPanel', conf
.embedPanel
) 
 997         wc
.WriteInt('showTools', conf
.showTools
) 
 998         if not conf
.embedPanel
: 
 999             wc
.WriteInt('panelX', conf
.panelX
) 
1000             wc
.WriteInt('panelY', conf
.panelY
) 
1001         wc
.WriteInt('sashPos', conf
.sashPos
) 
1002         wc
.WriteInt('panelWidth', conf
.panelWidth
) 
1003         wc
.WriteInt('panelHeight', conf
.panelHeight
) 
1004         wc
.WriteInt('nopanic', True) 
1008     app 
= App(0, useBestVisual
=False) 
1009     #app.SetAssertMode(wxPYAPP_ASSERT_LOG) 
1015 if __name__ 
== '__main__':