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
.AppendSeparator() 
 100         menu
.Append(wxID_OPEN
, '&Open...\tCtrl-O', 'Open XRC file') 
 101         self
.recentMenu 
= wxMenu() 
 102         self
.AppendRecent(self
.recentMenu
) 
 103         menu
.AppendMenu(-1, 'Open Recent', self
.recentMenu
, 'Open a recent file') 
 104         menu
.AppendSeparator() 
 105         menu
.Append(wxID_SAVE
, '&Save\tCtrl-S', 'Save XRC file') 
 106         menu
.Append(wxID_SAVEAS
, 'Save &As...', 'Save XRC file under different name') 
 107         menu
.AppendSeparator() 
 108         menu
.Append(wxID_EXIT
, '&Quit\tCtrl-Q', 'Exit application') 
 110         menuBar
.Append(menu
, '&File') 
 113         menu
.Append(wxID_UNDO
, '&Undo\tCtrl-Z', 'Undo') 
 114         menu
.Append(wxID_REDO
, '&Redo\tCtrl-Y', 'Redo') 
 115         menu
.AppendSeparator() 
 116         menu
.Append(wxID_CUT
, 'Cut\tCtrl-X', 'Cut to the clipboard') 
 117         menu
.Append(wxID_COPY
, '&Copy\tCtrl-C', 'Copy to the clipboard') 
 118         menu
.Append(wxID_PASTE
, '&Paste\tCtrl-V', 'Paste from the clipboard') 
 119         self
.ID_DELETE 
= wxNewId() 
 120         menu
.Append(self
.ID_DELETE
, '&Delete\tCtrl-D', 'Delete object') 
 121 #        menu.AppendSeparator() 
 122         ID_SELECT 
= wxNewId() 
 123 #        menu.Append(ID_SELECT, '&Select', 'Select object') 
 124         menuBar
.Append(menu
, '&Edit') 
 127         self
.ID_EMBED_PANEL 
= wxNewId() 
 128         menu
.Append(self
.ID_EMBED_PANEL
, '&Embed Panel', 
 129                     'Toggle embedding properties panel in the main window', True) 
 130         menu
.Check(self
.ID_EMBED_PANEL
, conf
.embedPanel
) 
 131         self
.ID_SHOW_TOOLS 
= wxNewId() 
 132         menu
.Append(self
.ID_SHOW_TOOLS
, 'Show &Tools', 'Toggle tools', True) 
 133         menu
.Check(self
.ID_SHOW_TOOLS
, conf
.showTools
) 
 134         menu
.AppendSeparator() 
 135         self
.ID_TEST 
= wxNewId() 
 136         menu
.Append(self
.ID_TEST
, '&Test\tF5', 'Test window') 
 137         self
.ID_REFRESH 
= wxNewId() 
 138         menu
.Append(self
.ID_REFRESH
, '&Refresh\tCtrl-R', 'Refresh test window') 
 139         self
.ID_AUTO_REFRESH 
= wxNewId() 
 140         menu
.Append(self
.ID_AUTO_REFRESH
, '&Auto-refresh\tCtrl-A', 
 141                     'Toggle auto-refresh mode', True) 
 142         menu
.Check(self
.ID_AUTO_REFRESH
, conf
.autoRefresh
) 
 143         menuBar
.Append(menu
, '&View') 
 146         menu
.Append(wxID_ABOUT
, '&About...', 'About XCRed') 
 147         self
.ID_README 
= wxNewId() 
 148         menu
.Append(self
.ID_README
, '&Readme...', 'View the README file') 
 150             self
.ID_DEBUG_CMD 
= wxNewId() 
 151             menu
.Append(self
.ID_DEBUG_CMD
, 'CMD', 'Python command line') 
 152             EVT_MENU(self
, self
.ID_DEBUG_CMD
, self
.OnDebugCMD
) 
 153         menuBar
.Append(menu
, '&Help') 
 155         self
.menuBar 
= menuBar
 
 156         self
.SetMenuBar(menuBar
) 
 159         tb 
= self
.CreateToolBar(wxTB_HORIZONTAL | wxNO_BORDER | wxTB_FLAT
) 
 160         tb
.SetToolBitmapSize((24, 23)) 
 161         tb
.AddSimpleTool(wxID_NEW
, images
.getNewBitmap(), 'New', 'New file') 
 162         tb
.AddSimpleTool(wxID_OPEN
, images
.getOpenBitmap(), 'Open', 'Open file') 
 163         tb
.AddSimpleTool(wxID_SAVE
, images
.getSaveBitmap(), 'Save', 'Save file') 
 164         tb
.AddControl(wxStaticLine(tb
, -1, size
=(-1,23), style
=wxLI_VERTICAL
)) 
 165         tb
.AddSimpleTool(wxID_UNDO
, images
.getUndoBitmap(), 'Undo', 'Undo') 
 166         tb
.AddSimpleTool(wxID_REDO
, images
.getRedoBitmap(), 'Redo', 'Redo') 
 167         tb
.AddControl(wxStaticLine(tb
, -1, size
=(-1,23), style
=wxLI_VERTICAL
)) 
 168         tb
.AddSimpleTool(wxID_CUT
, images
.getCutBitmap(), 'Cut', 'Cut') 
 169         tb
.AddSimpleTool(wxID_COPY
, images
.getCopyBitmap(), 'Copy', 'Copy') 
 170         tb
.AddSimpleTool(wxID_PASTE
, images
.getPasteBitmap(), 'Paste', 'Paste') 
 171         tb
.AddControl(wxStaticLine(tb
, -1, size
=(-1,23), style
=wxLI_VERTICAL
)) 
 172         tb
.AddSimpleTool(self
.ID_TEST
, images
.getTestBitmap(), 'Test', 'Test window') 
 173         tb
.AddSimpleTool(self
.ID_REFRESH
, images
.getRefreshBitmap(), 
 174                          'Refresh', 'Refresh view') 
 175         tb
.AddSimpleTool(self
.ID_AUTO_REFRESH
, images
.getAutoRefreshBitmap(), 
 176                          'Auto-refresh', 'Toggle auto-refresh mode', True) 
 177         if wxPlatform 
== '__WXGTK__': 
 178             tb
.AddSeparator()   # otherwise auto-refresh sticks in status line 
 179         tb
.ToggleTool(self
.ID_AUTO_REFRESH
, conf
.autoRefresh
) 
 182         self
.minWidth 
= tb
.GetSize()[0] # minimal width is the size of toolbar 
 185         EVT_MENU(self
, wxID_NEW
, self
.OnNew
) 
 186         EVT_MENU(self
, wxID_OPEN
, self
.OnOpen
) 
 187         EVT_MENU(self
, wxID_SAVE
, self
.OnSaveOrSaveAs
) 
 188         EVT_MENU(self
, wxID_SAVEAS
, self
.OnSaveOrSaveAs
) 
 189         EVT_MENU(self
, wxID_EXIT
, self
.OnExit
) 
 191         EVT_MENU(self
, wxID_UNDO
, self
.OnUndo
) 
 192         EVT_MENU(self
, wxID_REDO
, self
.OnRedo
) 
 193         EVT_MENU(self
, wxID_CUT
, self
.OnCutDelete
) 
 194         EVT_MENU(self
, wxID_COPY
, self
.OnCopy
) 
 195         EVT_MENU(self
, wxID_PASTE
, self
.OnPaste
) 
 196         EVT_MENU(self
, self
.ID_DELETE
, self
.OnCutDelete
) 
 197         EVT_MENU(self
, ID_SELECT
, self
.OnSelect
) 
 199         EVT_MENU(self
, self
.ID_EMBED_PANEL
, self
.OnEmbedPanel
) 
 200         EVT_MENU(self
, self
.ID_SHOW_TOOLS
, self
.OnShowTools
) 
 201         EVT_MENU(self
, self
.ID_TEST
, self
.OnTest
) 
 202         EVT_MENU(self
, self
.ID_REFRESH
, self
.OnRefresh
) 
 203         EVT_MENU(self
, self
.ID_AUTO_REFRESH
, self
.OnAutoRefresh
) 
 205         EVT_MENU(self
, wxID_ABOUT
, self
.OnAbout
) 
 206         EVT_MENU(self
, self
.ID_README
, self
.OnReadme
) 
 209         EVT_UPDATE_UI(self
, wxID_CUT
, self
.OnUpdateUI
) 
 210         EVT_UPDATE_UI(self
, wxID_COPY
, self
.OnUpdateUI
) 
 211         EVT_UPDATE_UI(self
, wxID_PASTE
, self
.OnUpdateUI
) 
 212         EVT_UPDATE_UI(self
, wxID_UNDO
, self
.OnUpdateUI
) 
 213         EVT_UPDATE_UI(self
, wxID_REDO
, self
.OnUpdateUI
) 
 214         EVT_UPDATE_UI(self
, self
.ID_DELETE
, self
.OnUpdateUI
) 
 215         EVT_UPDATE_UI(self
, self
.ID_TEST
, self
.OnUpdateUI
) 
 216         EVT_UPDATE_UI(self
, self
.ID_REFRESH
, self
.OnUpdateUI
) 
 219         sizer 
= wxBoxSizer(wxVERTICAL
) 
 220         sizer
.Add(wxStaticLine(self
, -1), 0, wxEXPAND
) 
 221         # Horizontal sizer for toolbar and splitter 
 222         self
.toolsSizer 
= sizer1 
= wxBoxSizer() 
 223         splitter 
= wxSplitterWindow(self
, -1, style
=wxSP_3DSASH
) 
 224         self
.splitter 
= splitter
 
 225         splitter
.SetMinimumPaneSize(100) 
 228         g
.tree 
= tree 
= XML_Tree(splitter
, -1) 
 230         # Init pull-down menu data 
 232         g
.pullDownMenu 
= pullDownMenu 
= PullDownMenu(self
) 
 234         # Vertical toolbar for GUI buttons 
 235         g
.tools 
= tools 
= Tools(self
) 
 236         tools
.Show(conf
.showTools
) 
 237         if conf
.showTools
: sizer1
.Add(tools
, 0, wxEXPAND
) 
 239         tree
.RegisterKeyEvents() 
 241         # !!! frame styles are broken 
 242         # Miniframe for not embedded mode 
 243         miniFrame 
= wxFrame(self
, -1, 'Properties Panel', 
 244                             (conf
.panelX
, conf
.panelY
), 
 245                             (conf
.panelWidth
, conf
.panelHeight
)) 
 246         self
.miniFrame 
= miniFrame
 
 247         sizer2 
= wxBoxSizer() 
 248         miniFrame
.SetAutoLayout(True) 
 249         miniFrame
.SetSizer(sizer2
) 
 250         EVT_CLOSE(self
.miniFrame
, self
.OnCloseMiniFrame
) 
 251         # Create panel for parameters 
 254             panel 
= Panel(splitter
) 
 255             # Set plitter windows 
 256             splitter
.SplitVertically(tree
, panel
, conf
.sashPos
) 
 258             panel 
= Panel(miniFrame
) 
 259             sizer2
.Add(panel
, 1, wxEXPAND
) 
 261             splitter
.Initialize(tree
) 
 262         sizer1
.Add(splitter
, 1, wxEXPAND
) 
 263         sizer
.Add(sizer1
, 1, wxEXPAND
) 
 264         self
.SetAutoLayout(True) 
 268         self
.clipboard 
= None 
 272         EVT_IDLE(self
, self
.OnIdle
) 
 273         EVT_CLOSE(self
, self
.OnCloseWindow
) 
 274         EVT_LEFT_DOWN(self
, self
.OnLeftDown
) 
 275         EVT_KEY_DOWN(self
, tools
.OnKeyDown
) 
 276         EVT_KEY_UP(self
, tools
.OnKeyUp
) 
 278     def AppendRecent(self
, menu
): 
 279         # add recently used files to the menu 
 280         for id,name 
in conf
.recentfiles
.iteritems(): 
 282             EVT_MENU(self
,id,self
.OnRecentFile
) 
 285     def OnRecentFile(self
,evt
): 
 286         # open recently used file 
 287         if not self
.AskSave(): return 
 290             path
=conf
.recentfiles
[evt
.GetId()] 
 292                 self
.SetStatusText('Data loaded') 
 294                 self
.SetStatusText('Failed') 
 296             self
.SetStatusText('No such file') 
 299     def OnNew(self
, evt
): 
 300         if not self
.AskSave(): return 
 303     def OnOpen(self
, evt
): 
 304         if not self
.AskSave(): return 
 305         dlg 
= wxFileDialog(self
, 'Open', os
.path
.dirname(self
.dataFile
), 
 306                            '', '*.xrc', wxOPEN | wxCHANGE_DIR
) 
 307         if dlg
.ShowModal() == wxID_OK
: 
 309             self
.SetStatusText('Loading...') 
 313                 self
.SetStatusText('Data loaded') 
 315                 self
.SetStatusText('Failed') 
 316             self
.SaveRecent(path
) 
 320     def OnSaveOrSaveAs(self
, evt
): 
 321         if evt
.GetId() == wxID_SAVEAS 
or not self
.dataFile
: 
 322             if self
.dataFile
: defaultName 
= '' 
 323             else: defaultName 
= 'UNTITLED.xrc' 
 324             dlg 
= wxFileDialog(self
, 'Save As', os
.path
.dirname(self
.dataFile
), 
 325                                defaultName
, '*.xrc', 
 326                                wxSAVE | wxOVERWRITE_PROMPT | wxCHANGE_DIR
) 
 327             if dlg
.ShowModal() == wxID_OK
: 
 335         self
.SetStatusText('Saving...') 
 341             self
.SetStatusText('Data saved') 
 342             self
.SaveRecent(path
) 
 344             self
.SetStatusText('Failed') 
 347     def SaveRecent(self
,path
): 
 348         # append to recently used files 
 349         if path 
not in conf
.recentfiles
.values(): 
 351             self
.recentMenu
.Append(newid
, path
) 
 352             EVT_MENU(self
, newid
, self
.OnRecentFile
) 
 353             conf
.recentfiles
[newid
] = path
 
 355     def OnExit(self
, evt
): 
 358     def OnUndo(self
, evt
): 
 359         # Extra check to not mess with idle updating 
 360         if undoMan
.CanUndo(): 
 363     def OnRedo(self
, evt
): 
 364         if undoMan
.CanRedo(): 
 367     def OnCopy(self
, evt
): 
 368         selected 
= tree
.selection
 
 369         if not selected
: return         # key pressed event 
 370         xxx 
= tree
.GetPyData(selected
) 
 371         self
.clipboard 
= xxx
.element
.cloneNode(True) 
 372         self
.SetStatusText('Copied') 
 374     def OnPaste(self
, evt
): 
 375         selected 
= tree
.selection
 
 376         if not selected
: return         # key pressed event 
 377         # For pasting with Ctrl pressed 
 378         if evt
.GetId() == pullDownMenu
.ID_PASTE_SIBLING
: appendChild 
= False 
 379         else: appendChild 
= not tree
.NeedInsert(selected
) 
 380         xxx 
= tree
.GetPyData(selected
) 
 382             # If has next item, insert, else append to parent 
 383             nextItem 
= tree
.GetNextSibling(selected
) 
 384             parentLeaf 
= tree
.GetItemParent(selected
) 
 385         # Expanded container (must have children) 
 386         elif tree
.IsExpanded(selected
) and tree
.GetChildrenCount(selected
, False): 
 387             # Insert as first child 
 388             nextItem 
= tree
.GetFirstChild(selected
)[0] 
 389             parentLeaf 
= selected
 
 391             # No children or unexpanded item - appendChild stays True 
 392             nextItem 
= wxTreeItemId()   # no next item 
 393             parentLeaf 
= selected
 
 394         parent 
= tree
.GetPyData(parentLeaf
).treeObject() 
 396         # Create a copy of clipboard element 
 397         elem 
= self
.clipboard
.cloneNode(True) 
 398         # Tempopary xxx object to test things 
 399         xxx 
= MakeXXXFromDOM(parent
, elem
) 
 401         # Check compatibility 
 405         if x
.__class
__ in [xxxDialog
, xxxFrame
, xxxMenuBar
]: 
 407             if parent
.__class
__ != xxxMainNode
: error 
= True 
 408         elif x
.__class
__ == xxxToolBar
: 
 409             # Toolbar can be top-level of child of panel or frame 
 410             if parent
.__class
__ not in [xxxMainNode
, xxxPanel
, xxxFrame
]: error 
= True 
 411         elif x
.__class
__ == xxxPanel 
and parent
.__class
__ == xxxMainNode
: 
 413         elif x
.__class
__ == xxxSpacer
: 
 414             if not parent
.isSizer
: error 
= True 
 415         elif x
.__class
__ == xxxSeparator
: 
 416             if not parent
.__class
__ in [xxxMenu
, xxxToolBar
]: error 
= True 
 417         elif x
.__class
__ == xxxTool
: 
 418             if parent
.__class
__ != xxxToolBar
: error 
= True 
 419         elif x
.__class
__ == xxxMenu
: 
 420             if not parent
.__class
__ in [xxxMainNode
, xxxMenuBar
, xxxMenu
]: error 
= True 
 421         elif x
.__class
__ == xxxMenuItem
: 
 422             if not parent
.__class
__ in [xxxMenuBar
, xxxMenu
]: error 
= True 
 423         elif x
.isSizer 
and parent
.__class
__ == xxxNotebook
: error 
= True 
 424         else:                           # normal controls can be almost anywhere 
 425             if parent
.__class
__ == xxxMainNode 
or \
 
 426                parent
.__class
__ in [xxxMenuBar
, xxxMenu
]: error 
= True 
 428             if parent
.__class
__ == xxxMainNode
: parentClass 
= 'root' 
 429             else: parentClass 
= parent
.className
 
 430             wxLogError('Incompatible parent/child: parent is %s, child is %s!' % 
 431                        (parentClass
, x
.className
)) 
 434         # Check parent and child relationships. 
 435         # If parent is sizer or notebook, child is of wrong class or 
 436         # parent is normal window, child is child container then detach child. 
 437         isChildContainer 
= isinstance(xxx
, xxxChildContainer
) 
 438         if isChildContainer 
and \
 
 439            ((parent
.isSizer 
and not isinstance(xxx
, xxxSizerItem
)) or \
 
 440             (isinstance(parent
, xxxNotebook
) and not isinstance(xxx
, xxxNotebookPage
)) or \
 
 441            not (parent
.isSizer 
or isinstance(parent
, xxxNotebook
))): 
 442             elem
.removeChild(xxx
.child
.element
) # detach child 
 443             elem
.unlink()           # delete child container 
 444             elem 
= xxx
.child
.element 
# replace 
 445             # This may help garbage collection 
 446             xxx
.child
.parent 
= None 
 447             isChildContainer 
= False 
 448         # Parent is sizer or notebook, child is not child container 
 449         if parent
.isSizer 
and not isChildContainer 
and not isinstance(xxx
, xxxSpacer
): 
 450             # Create sizer item element 
 451             sizerItemElem 
= MakeEmptyDOM('sizeritem') 
 452             sizerItemElem
.appendChild(elem
) 
 454         elif isinstance(parent
, xxxNotebook
) and not isChildContainer
: 
 455             pageElem 
= MakeEmptyDOM('notebookpage') 
 456             pageElem
.appendChild(elem
) 
 458         # Insert new node, register undo 
 459         newItem 
= tree
.InsertNode(parentLeaf
, parent
, elem
, nextItem
) 
 460         undoMan
.RegisterUndo(UndoPasteCreate(parentLeaf
, parent
, newItem
, selected
)) 
 461         # Scroll to show new item (!!! redundant?) 
 462         tree
.EnsureVisible(newItem
) 
 463         tree
.SelectItem(newItem
) 
 464         if not tree
.IsVisible(newItem
): 
 465             tree
.ScrollTo(newItem
) 
 468         if g
.testWin 
and tree
.IsHighlatable(newItem
): 
 470                 tree
.needUpdate 
= True 
 471                 tree
.pendingHighLight 
= newItem
 
 473                 tree
.pendingHighLight 
= None 
 475         self
.SetStatusText('Pasted') 
 477     def OnCutDelete(self
, evt
): 
 478         selected 
= tree
.selection
 
 479         if not selected
: return         # key pressed event 
 481         if evt
.GetId() == wxID_CUT
: 
 483             status 
= 'Removed to clipboard' 
 485             self
.lastOp 
= 'DELETE' 
 489             # If deleting top-level item, delete testWin 
 490             if selected 
== g
.testWin
.item
: 
 494                 # Remove highlight, update testWin 
 495                 if g
.testWin
.highLight
: 
 496                     g
.testWin
.highLight
.Remove() 
 497                 tree
.needUpdate 
= True 
 500         index 
= tree
.ItemFullIndex(selected
) 
 501         parent 
= tree
.GetPyData(tree
.GetItemParent(selected
)).treeObject() 
 502         elem 
= tree
.RemoveLeaf(selected
) 
 503         undoMan
.RegisterUndo(UndoCutDelete(index
, parent
, elem
)) 
 504         if evt
.GetId() == wxID_CUT
: 
 505             if self
.clipboard
: self
.clipboard
.unlink() 
 506             self
.clipboard 
= elem
.cloneNode(True) 
 507         tree
.pendingHighLight 
= None 
 511         self
.SetStatusText(status
) 
 513     def OnSubclass(self
, evt
): 
 514         selected 
= tree
.selection
 
 515         xxx 
= tree
.GetPyData(selected
).treeObject() 
 517         subclass 
= xxx
.subclass
 
 518         dlg 
= wxTextEntryDialog(self
, 'Subclass:', defaultValue
=subclass
) 
 519         if dlg
.ShowModal() == wxID_OK
: 
 520             subclass 
= dlg
.GetValue() 
 522                 elem
.setAttribute('subclass', subclass
) 
 524             elif elem
.hasAttribute('subclass'): 
 525                 elem
.removeAttribute('subclass') 
 527             xxx
.subclass 
= elem
.getAttribute('subclass') 
 528             tree
.SetItemText(selected
, xxx
.treeName()) 
 529             panel
.pages
[0].box
.SetLabel(xxx
.panelName()) 
 532     def OnSelect(self
, evt
): 
 533         print >> sys
.stderr
, 'Xperimental function!' 
 535         self
.SetCursor(wxCROSS_CURSOR
) 
 538     def OnLeftDown(self
, evt
): 
 539         pos 
= evt
.GetPosition() 
 540         self
.SetCursor(wxNullCursor
) 
 543     def OnEmbedPanel(self
, evt
): 
 544         conf
.embedPanel 
= evt
.IsChecked() 
 546             # Remember last dimentions 
 547             conf
.panelX
, conf
.panelY 
= self
.miniFrame
.GetPosition() 
 548             conf
.panelWidth
, conf
.panelHeight 
= self
.miniFrame
.GetSize() 
 549             size 
= self
.GetSize() 
 550             pos 
= self
.GetPosition() 
 551             sizePanel 
= panel
.GetSize() 
 552             panel
.Reparent(self
.splitter
) 
 553             self
.miniFrame
.GetSizer().Remove(panel
) 
 556             self
.SetDimensions(pos
.x
, pos
.y
, size
.width 
+ sizePanel
.width
, size
.height
) 
 557             self
.splitter
.SplitVertically(tree
, panel
, conf
.sashPos
) 
 558             self
.miniFrame
.Show(False) 
 560             conf
.sashPos 
= self
.splitter
.GetSashPosition() 
 561             pos 
= self
.GetPosition() 
 562             size 
= self
.GetSize() 
 563             sizePanel 
= panel
.GetSize() 
 564             self
.splitter
.Unsplit(panel
) 
 565             sizer 
= self
.miniFrame
.GetSizer() 
 566             panel
.Reparent(self
.miniFrame
) 
 568             sizer
.Add(panel
, 1, wxEXPAND
) 
 569             self
.miniFrame
.Show(True) 
 570             self
.miniFrame
.SetDimensions(conf
.panelX
, conf
.panelY
, 
 571                                          conf
.panelWidth
, conf
.panelHeight
) 
 574             self
.SetDimensions(pos
.x
, pos
.y
, 
 575                                max(size
.width 
- sizePanel
.width
, self
.minWidth
), size
.height
) 
 577     def OnShowTools(self
, evt
): 
 578         conf
.showTools 
= evt
.IsChecked() 
 579         g
.tools
.Show(conf
.showTools
) 
 581             self
.toolsSizer
.Prepend(g
.tools
, 0, wxEXPAND
) 
 583             self
.toolsSizer
.Remove(g
.tools
) 
 584         self
.toolsSizer
.Layout() 
 586     def OnTest(self
, evt
): 
 587         if not tree
.selection
: return   # key pressed event 
 588         tree
.ShowTestWindow(tree
.selection
) 
 590     def OnRefresh(self
, evt
): 
 591         # If modified, apply first 
 592         selection 
= tree
.selection
 
 594             xxx 
= tree
.GetPyData(selection
) 
 595             if xxx 
and panel
.IsModified(): 
 596                 tree
.Apply(xxx
, selection
) 
 599             tree
.CreateTestWin(g
.testWin
.item
) 
 600         panel
.modified 
= False 
 601         tree
.needUpdate 
= False 
 603     def OnAutoRefresh(self
, evt
): 
 604         conf
.autoRefresh 
= evt
.IsChecked() 
 605         self
.menuBar
.Check(self
.ID_AUTO_REFRESH
, conf
.autoRefresh
) 
 606         self
.tb
.ToggleTool(self
.ID_AUTO_REFRESH
, conf
.autoRefresh
) 
 608     def OnAbout(self
, evt
): 
 612 (c) Roman Rolinsky <rollrom@users.sourceforge.net> 
 613 Homepage: http://xrced.sourceforge.net\ 
 615         dlg 
= wxMessageDialog(self
, str, 'About XRCed', wxOK | wxCENTRE
) 
 619     def OnReadme(self
, evt
): 
 620         text 
= open(os
.path
.join(basePath
, 'README.txt'), 'r').read() 
 621         dlg 
= ScrolledMessageDialog(self
, text
, "XRCed README") 
 625     # Simple emulation of python command line 
 626     def OnDebugCMD(self
, evt
): 
 630                 exec raw_input('C:\> ') 
 635                 (etype
, value
, tb
) =sys
.exc_info() 
 636                 tblist 
=traceback
.extract_tb(tb
)[1:] 
 637                 msg 
=' '.join(traceback
.format_exception_only(etype
, value
) 
 638                         +traceback
.format_list(tblist
)) 
 641     def OnCreate(self
, evt
): 
 642         selected 
= tree
.selection
 
 643         if tree
.ctrl
: appendChild 
= False 
 644         else: appendChild 
= not tree
.NeedInsert(selected
) 
 645         xxx 
= tree
.GetPyData(selected
) 
 649                 # If has previous item, insert after it, else append to parent 
 651                 parentLeaf 
= tree
.GetItemParent(selected
) 
 653                 # If has next item, insert, else append to parent 
 654                 nextItem 
= tree
.GetNextSibling(selected
) 
 655                 parentLeaf 
= tree
.GetItemParent(selected
) 
 656         # Expanded container (must have children) 
 657         elif tree
.shift 
and tree
.IsExpanded(selected
) \
 
 658            and tree
.GetChildrenCount(selected
, False): 
 659             nextItem 
= tree
.GetFirstChild(selected
)[0] 
 660             parentLeaf 
= selected
 
 662             nextItem 
= wxTreeItemId() 
 663             parentLeaf 
= selected
 
 664         parent 
= tree
.GetPyData(parentLeaf
) 
 665         if parent
.hasChild
: parent 
= parent
.child
 
 668         className 
= pullDownMenu
.createMap
[evt
.GetId()] 
 669         xxx 
= MakeEmptyXXX(parent
, className
) 
 671         # Set default name for top-level windows 
 672         if parent
.__class
__ == xxxMainNode
: 
 673             cl 
= xxx
.treeObject().__class
__ 
 674             frame
.maxIDs
[cl
] += 1 
 675             xxx
.treeObject().name 
= '%s%d' % (defaultIDs
[cl
], frame
.maxIDs
[cl
]) 
 676             xxx
.treeObject().element
.setAttribute('name', xxx
.treeObject().name
) 
 678         # Insert new node, register undo 
 680         newItem 
= tree
.InsertNode(parentLeaf
, parent
, elem
, nextItem
) 
 681         undoMan
.RegisterUndo(UndoPasteCreate(parentLeaf
, parent
, newItem
, selected
)) 
 682         tree
.EnsureVisible(newItem
) 
 683         tree
.SelectItem(newItem
) 
 684         if not tree
.IsVisible(newItem
): 
 685             tree
.ScrollTo(newItem
) 
 688         if g
.testWin 
and tree
.IsHighlatable(newItem
): 
 690                 tree
.needUpdate 
= True 
 691                 tree
.pendingHighLight 
= newItem
 
 693                 tree
.pendingHighLight 
= None 
 697     # Replace one object with another 
 698     def OnReplace(self
, evt
): 
 699         selected 
= tree
.selection
 
 700         xxx 
= tree
.GetPyData(selected
).treeObject() 
 702         parent 
= elem
.parentNode
 
 703         parentXXX 
= xxx
.parent
 
 705         className 
= pullDownMenu
.createMap
[evt
.GetId() - 1000] 
 706         # Create temporary empty node (with default values) 
 707         dummy 
= MakeEmptyDOM(className
) 
 708         xxxClass 
= xxxDict
[className
] 
 709         # Remove non-compatible children 
 710         if tree
.ItemHasChildren(selected
) and not xxxClass
.hasChildren
: 
 711             tree
.DeleteChildren(selected
) 
 712         nodes 
= elem
.childNodes
[:] 
 718                 if not xxxClass
.hasChildren
: 
 720             elif tag 
not in xxxClass
.allParams 
and \
 
 721                      (not xxxClass
.hasStyle 
or tag 
not in xxxClass
.styles
): 
 726                 elem
.removeChild(node
) 
 729         # Copy parameters present in dummy but not in elem 
 730         for node 
in dummy
.childNodes
: 
 733                 elem
.appendChild(node
.cloneNode(True)) 
 736         elem
.setAttribute('class', className
)         
 737         # Re-create xxx element 
 738         xxx 
= MakeXXXFromDOM(parentXXX
, elem
) 
 739         # Update parent in child objects 
 740         if tree
.ItemHasChildren(selected
): 
 741             i
, cookie 
= tree
.GetFirstChild(selected
) 
 743                 x 
= tree
.GetPyData(i
) 
 745                 if x
.hasChild
: x
.child
.parent 
= xxx
 
 746                 i
, cookie 
= tree
.GetNextChild(selected
, cookie
) 
 749         if tree
.GetPyData(selected
).hasChild
: # child container 
 750             container 
= tree
.GetPyData(selected
) 
 751             container
.child 
= xxx
 
 752             container
.hasChildren 
= xxx
.hasChildren
 
 753             container
.isSizer 
= xxx
.isSizer
 
 755             tree
.SetPyData(selected
, xxx
) 
 756         tree
.SetItemText(selected
, xxx
.treeName()) 
 757         tree
.SetItemImage(selected
, xxx
.treeImage()) 
 759         # Set default name for top-level windows 
 760         if parent
.__class
__ == xxxMainNode
: 
 761             cl 
= xxx
.treeObject().__class
__ 
 762             frame
.maxIDs
[cl
] += 1 
 763             xxx
.treeObject().name 
= '%s%d' % (defaultIDs
[cl
], frame
.maxIDs
[cl
]) 
 764             xxx
.treeObject().element
.setAttribute('name', xxx
.treeObject().name
) 
 771         #undoMan.RegisterUndo(UndoPasteCreate(parentLeaf, parent, newItem, selected)) 
 773         if g
.testWin 
and tree
.IsHighlatable(selected
): 
 775                 tree
.needUpdate 
= True 
 776                 tree
.pendingHighLight 
= selected
 
 778                 tree
.pendingHighLight 
= None 
 782     # Expand/collapse subtree 
 783     def OnExpand(self
, evt
): 
 784         if tree
.selection
: tree
.ExpandAll(tree
.selection
) 
 785         else: tree
.ExpandAll(tree
.root
) 
 786     def OnCollapse(self
, evt
): 
 787         if tree
.selection
: tree
.CollapseAll(tree
.selection
) 
 788         else: tree
.CollapseAll(tree
.root
) 
 790     def OnPullDownHighlight(self
, evt
): 
 791         menuId 
= evt
.GetMenuId() 
 793             menu 
= evt
.GetEventObject() 
 794             help = menu
.GetHelpString(menuId
) 
 795             self
.SetStatusText(help) 
 797             self
.SetStatusText('') 
 799     def OnUpdateUI(self
, evt
): 
 800         if evt
.GetId() in [wxID_CUT
, wxID_COPY
, self
.ID_DELETE
]: 
 801             evt
.Enable(tree
.selection 
is not None and tree
.selection 
!= tree
.root
) 
 802         elif evt
.GetId() == wxID_PASTE
: 
 803             evt
.Enable((self
.clipboard 
and tree
.selection
) != None) 
 804         elif evt
.GetId() == self
.ID_TEST
: 
 805             evt
.Enable(tree
.selection 
is not None and tree
.selection 
!= tree
.root
) 
 806         elif evt
.GetId() == wxID_UNDO
:  evt
.Enable(undoMan
.CanUndo()) 
 807         elif evt
.GetId() == wxID_REDO
:  evt
.Enable(undoMan
.CanRedo()) 
 809     def OnIdle(self
, evt
): 
 810         if self
.inIdle
: return          # Recursive call protection 
 815                     self
.SetStatusText('Refreshing test window...') 
 817                     tree
.CreateTestWin(g
.testWin
.item
) 
 819                     self
.SetStatusText('') 
 820                 tree
.needUpdate 
= False 
 821         elif tree
.pendingHighLight
: 
 822             tree
.HighLight(tree
.pendingHighLight
) 
 827     # We don't let close panel window 
 828     def OnCloseMiniFrame(self
, evt
): 
 831     def OnCloseWindow(self
, evt
): 
 832         if not self
.AskSave(): return 
 833         if g
.testWin
: g
.testWin
.Destroy() 
 834         if not panel
.GetPageCount() == 2: 
 835             panel
.page2
.Destroy() 
 837             # If we don't do this, page does not get destroyed (a bug?) 
 839         if not self
.IsIconized(): 
 840             conf
.x
, conf
.y 
= self
.GetPosition() 
 841             conf
.width
, conf
.height 
= self
.GetSize() 
 843                 conf
.sashPos 
= self
.splitter
.GetSashPosition() 
 845                 conf
.panelX
, conf
.panelY 
= self
.miniFrame
.GetPosition() 
 846                 conf
.panelWidth
, conf
.panelHeight 
= self
.miniFrame
.GetSize() 
 852             self
.clipboard
.unlink() 
 853             self
.clipboard 
= None 
 855         self
.modified 
= False 
 861         self
.SetTitle(progname
) 
 862         # Numbers for new controls 
 864         self
.maxIDs
[xxxPanel
] = self
.maxIDs
[xxxDialog
] = self
.maxIDs
[xxxFrame
] = \
 
 865         self
.maxIDs
[xxxMenuBar
] = self
.maxIDs
[xxxMenu
] = self
.maxIDs
[xxxToolBar
] = 0 
 867     def Open(self
, path
): 
 868         if not os
.path
.exists(path
): 
 869             wxLogError('File does not exists: %s' % path
) 
 871         # Try to read the file 
 875             dom 
= minidom
.parse(f
) 
 877             # Set encoding global variable 
 878             if dom
.encoding
: g
.currentEncoding 
= dom
.encoding
 
 880             dir = os
.path
.dirname(path
) 
 881             if dir: os
.chdir(dir) 
 884             self
.SetTitle(progname 
+ ': ' + os
.path
.basename(path
)) 
 886             # Nice exception printing 
 888             wxLogError(traceback
.format_exception(inf
[0], inf
[1], None)[-1]) 
 889             wxLogError('Error reading file: %s' % path
) 
 893     def Indent(self
, node
, indent 
= 0): 
 894         # Copy child list because it will change soon 
 895         children 
= node
.childNodes
[:] 
 896         # Main node doesn't need to be indented 
 898             text 
= self
.domCopy
.createTextNode('\n' + ' ' * indent
) 
 899             node
.parentNode
.insertBefore(text
, node
) 
 901             # Append newline after last child, except for text nodes 
 902             if children
[-1].nodeType 
== minidom
.Node
.ELEMENT_NODE
: 
 903                 text 
= self
.domCopy
.createTextNode('\n' + ' ' * indent
) 
 904                 node
.appendChild(text
) 
 905             # Indent children which are elements 
 907                 if n
.nodeType 
== minidom
.Node
.ELEMENT_NODE
: 
 908                     self
.Indent(n
, indent 
+ 2) 
 910     def Save(self
, path
): 
 914             if tree
.selection 
and panel
.IsModified(): 
 915                 self
.OnRefresh(wxCommandEvent()) 
 916             f 
= codecs
.open(path
, 'w', g
.currentEncoding
) 
 917             # Make temporary copy for formatting it 
 918             # !!! We can't clone dom node, it works only once 
 919             #self.domCopy = tree.dom.cloneNode(True) 
 920             self
.domCopy 
= MyDocument() 
 921             mainNode 
= self
.domCopy
.appendChild(tree
.mainNode
.cloneNode(True)) 
 922             self
.Indent(mainNode
) 
 923             self
.domCopy
.writexml(f
, encoding 
= g
.currentEncoding
) 
 925             self
.domCopy
.unlink() 
 927             self
.modified 
= False 
 928             panel
.SetModified(False) 
 930             wxLogError('Error writing file: %s' % path
) 
 934         if not (self
.modified 
or panel
.IsModified()): return True 
 935         flags 
= wxICON_EXCLAMATION | wxYES_NO | wxCANCEL | wxCENTRE
 
 936         dlg 
= wxMessageDialog( self
, 'File is modified. Save before exit?', 
 937                                'Save before too late?', flags 
) 
 938         say 
= dlg
.ShowModal() 
 941             self
.OnSaveOrSaveAs(wxCommandEvent(wxID_SAVE
)) 
 942             # If save was successful, modified flag is unset 
 943             if not self
.modified
: return True 
 945             self
.modified 
= False 
 946             panel
.SetModified(False) 
 953 ################################################################################ 
 956     print >> sys
.stderr
, 'usage: xrced [-dhiv] [file]' 
 961         # Process comand-line 
 964             opts
, args 
= getopt
.getopt(sys
.argv
[1:], 'dhiv') 
 972                     print 'XRCed version', version
 
 975         except getopt
.GetoptError
: 
 976             if wxPlatform 
!= '__WXMAC__': # macs have some extra parameters 
 977                 print >> sys
.stderr
, 'Unknown option' 
 981         self
.SetAppName('xrced') 
 984         conf 
= g
.conf 
= wxConfig(style 
= wxCONFIG_USE_LOCAL_FILE
) 
 985         conf
.autoRefresh 
= conf
.ReadInt('autorefresh', True) 
 986         pos 
= conf
.ReadInt('x', -1), conf
.ReadInt('y', -1) 
 987         size 
= conf
.ReadInt('width', 800), conf
.ReadInt('height', 600) 
 988         conf
.embedPanel 
= conf
.ReadInt('embedPanel', True) 
 989         conf
.showTools 
= conf
.ReadInt('showTools', True) 
 990         conf
.sashPos 
= conf
.ReadInt('sashPos', 200) 
 991         # read recently used files 
 992         recentfiles
=conf
.Read('recentFiles','') 
 995             for fil 
in recentfiles
.split('|'): 
 996                 conf
.recentfiles
[wxNewId()]=fil
 
 997         if not conf
.embedPanel
: 
 998             conf
.panelX 
= conf
.ReadInt('panelX', -1) 
 999             conf
.panelY 
= conf
.ReadInt('panelY', -1) 
1001             conf
.panelX 
= conf
.panelY 
= -1 
1002         conf
.panelWidth 
= conf
.ReadInt('panelWidth', 200) 
1003         conf
.panelHeight 
= conf
.ReadInt('panelHeight', 200) 
1004         conf
.panic 
= not conf
.HasEntry('nopanic') 
1006         wxFileSystem_AddHandler(wxMemoryFSHandler()) 
1007         wxInitAllImageHandlers() 
1009         frame 
= Frame(pos
, size
) 
1011         # Load resources from XRC file (!!! should be transformed to .py later?) 
1012         frame
.res 
= wxXmlResource('') 
1013         # !!! Temporary blocking of assert failure occuring in unicode build 
1015             frame
.res
.Load(os
.path
.join(basePath
, 'xrced.xrc')) 
1016         except wx
._core
.PyAssertionError
: 
1019         # Load file after showing 
1022             frame
.open = frame
.Open(args
[0]) 
1029         wc 
= wxConfigBase_Get() 
1030         wc
.WriteInt('autorefresh', conf
.autoRefresh
) 
1031         wc
.WriteInt('x', conf
.x
) 
1032         wc
.WriteInt('y', conf
.y
) 
1033         wc
.WriteInt('width', conf
.width
) 
1034         wc
.WriteInt('height', conf
.height
) 
1035         wc
.WriteInt('embedPanel', conf
.embedPanel
) 
1036         wc
.WriteInt('showTools', conf
.showTools
) 
1037         if not conf
.embedPanel
: 
1038             wc
.WriteInt('panelX', conf
.panelX
) 
1039             wc
.WriteInt('panelY', conf
.panelY
) 
1040         wc
.WriteInt('sashPos', conf
.sashPos
) 
1041         wc
.WriteInt('panelWidth', conf
.panelWidth
) 
1042         wc
.WriteInt('panelHeight', conf
.panelHeight
) 
1043         wc
.WriteInt('nopanic', True) 
1044         wc
.Write('recentFiles', '|'.join(conf
.recentfiles
.values()[-5:])) 
1048     app 
= App(0, useBestVisual
=False) 
1049     #app.SetAssertMode(wxPYAPP_ASSERT_LOG) 
1055 if __name__ 
== '__main__':