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 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 frame
.res
.Load(os
.path
.join(basePath
, 'xrced.xrc'))
1015 # Load file after showing
1018 frame
.open = frame
.Open(args
[0])
1025 wc
= wxConfigBase_Get()
1026 wc
.WriteInt('autorefresh', conf
.autoRefresh
)
1027 wc
.WriteInt('x', conf
.x
)
1028 wc
.WriteInt('y', conf
.y
)
1029 wc
.WriteInt('width', conf
.width
)
1030 wc
.WriteInt('height', conf
.height
)
1031 wc
.WriteInt('embedPanel', conf
.embedPanel
)
1032 wc
.WriteInt('showTools', conf
.showTools
)
1033 if not conf
.embedPanel
:
1034 wc
.WriteInt('panelX', conf
.panelX
)
1035 wc
.WriteInt('panelY', conf
.panelY
)
1036 wc
.WriteInt('sashPos', conf
.sashPos
)
1037 wc
.WriteInt('panelWidth', conf
.panelWidth
)
1038 wc
.WriteInt('panelHeight', conf
.panelHeight
)
1039 wc
.WriteInt('nopanic', True)
1040 wc
.Write('recentFiles', '|'.join(conf
.recentfiles
.values()[-5:]))
1044 app
= App(0, useBestVisual
=False)
1045 #app.SetAssertMode(wxPYAPP_ASSERT_LOG)
1051 if __name__
== '__main__':