]>
git.saurik.com Git - wxWidgets.git/blob - wxPython/wx/tools/XRCed/xrced.py
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 wxWidgets/wxPython
14 xrced [ -h ] [ -v ] [ XRC-file ]
18 -h output short usage info and exit
20 -v output version info and exit
24 import os
, sys
, getopt
, re
, traceback
, tempfile
, shutil
, cPickle
25 from xml
.parsers
import expat
28 from tree
import * # imports xxx which imports params
31 from params
import genericStyles
32 # Cleanup recursive import sideeffects, otherwise we can't create undoMan
34 undo
.ParamPage
= ParamPage
35 undoMan
= g
.undoMan
= UndoManager()
37 # Set application path for loading resources
38 if __name__
== '__main__':
39 basePath
= os
.path
.dirname(sys
.argv
[0])
41 basePath
= os
.path
.dirname(__file__
)
43 # Remember system path
46 # 1 adds CMD command to Help menu
50 <HTML><H2>Welcome to XRC<font color="blue">ed</font></H2><H3><font color="green">DON'T PANIC :)</font></H3>
51 Read this note before clicking on anything!<P>
52 To start select tree root, then popup menu with your right mouse button,
53 select "Append Child", and then any command.<P>
54 Or just press one of the buttons on the tools palette.<P>
55 Enter XML ID, change properties, create children.<P>
56 To test your interface select Test command (View menu).<P>
57 Consult README.txt file for the details.</HTML>
60 defaultIDs
= {xxxPanel
:'PANEL', xxxDialog
:'DIALOG', xxxFrame
:'FRAME',
61 xxxMenuBar
:'MENUBAR', xxxMenu
:'MENU', xxxToolBar
:'TOOLBAR',
62 xxxWizard
:'WIZARD', xxxBitmap
:'BITMAP', xxxIcon
:'ICON'}
64 defaultName
= 'UNTITLED.xrc'
66 ################################################################################
68 # ScrolledMessageDialog - modified from wxPython lib to set fixed-width font
69 class ScrolledMessageDialog(wx
.Dialog
):
70 def __init__(self
, parent
, msg
, caption
, pos
= wx
.DefaultPosition
, size
= (500,300)):
71 from wx
.lib
.layoutf
import Layoutf
72 wx
.Dialog
.__init
__(self
, parent
, -1, caption
, pos
, size
)
73 text
= wx
.TextCtrl(self
, -1, msg
, wx
.DefaultPosition
,
74 wx
.DefaultSize
, wx
.TE_MULTILINE | wx
.TE_READONLY
)
75 text
.SetFont(g
.modernFont())
76 dc
= wx
.WindowDC(text
)
77 w
, h
= dc
.GetFullTextExtent(' ', g
.modernFont())[:2]
78 ok
= wx
.Button(self
, wx
.ID_OK
, "OK")
80 text
.SetConstraints(Layoutf('t=t5#1;b=t5#2;l=l5#1;r=r5#1', (self
,ok
)))
81 text
.SetSize((w
* 80 + 30, h
* 40))
82 text
.ShowPosition(1) # scroll to the first line
83 ok
.SetConstraints(Layoutf('b=b5#1;x%w50#1;w!80;h!35', (self
,)))
84 self
.SetAutoLayout(True)
86 self
.CenterOnScreen(wx
.BOTH
)
88 ################################################################################
90 # Event handler for using during location
91 class Locator(wx
.EvtHandler
):
92 def ProcessEvent(self
, evt
):
95 class Frame(wx
.Frame
):
96 def __init__(self
, pos
, size
):
97 wx
.Frame
.__init
__(self
, None, -1, '', pos
, size
)
99 frame
= g
.frame
= self
100 bar
= self
.CreateStatusBar(2)
101 bar
.SetStatusWidths([-1, 40])
102 self
.SetIcon(images
.getIconIcon())
107 # Load our own resources
108 self
.res
= xrc
.XmlResource('')
109 # !!! Blocking of assert failure occurring in older unicode builds
111 quietlog
= wx
.LogNull()
112 self
.res
.Load(os
.path
.join(basePath
, 'xrced.xrc'))
113 except wx
._core
.PyAssertionError
:
114 print 'PyAssertionError was ignored'
117 menuBar
= wx
.MenuBar()
120 menu
.Append(wx
.ID_NEW
, '&New\tCtrl-N', 'New file')
121 menu
.AppendSeparator()
122 menu
.Append(wx
.ID_OPEN
, '&Open...\tCtrl-O', 'Open XRC file')
123 self
.recentMenu
= wx
.Menu()
124 self
.AppendRecent(self
.recentMenu
)
125 menu
.AppendMenu(-1, 'Open Recent', self
.recentMenu
, 'Open a recent file')
126 menu
.AppendSeparator()
127 menu
.Append(wx
.ID_SAVE
, '&Save\tCtrl-S', 'Save XRC file')
128 menu
.Append(wx
.ID_SAVEAS
, 'Save &As...', 'Save XRC file under different name')
129 self
.ID_GENERATE_PYTHON
= wx
.NewId()
130 menu
.Append(self
.ID_GENERATE_PYTHON
, '&Generate Python...',
131 'Generate a Python module that uses this XRC')
132 menu
.AppendSeparator()
133 menu
.Append(wx
.ID_EXIT
, '&Quit\tCtrl-Q', 'Exit application')
135 menuBar
.Append(menu
, '&File')
138 menu
.Append(wx
.ID_UNDO
, '&Undo\tCtrl-Z', 'Undo')
139 menu
.Append(wx
.ID_REDO
, '&Redo\tCtrl-Y', 'Redo')
140 menu
.AppendSeparator()
141 menu
.Append(wx
.ID_CUT
, 'Cut\tCtrl-X', 'Cut to the clipboard')
142 menu
.Append(wx
.ID_COPY
, '&Copy\tCtrl-C', 'Copy to the clipboard')
143 menu
.Append(wx
.ID_PASTE
, '&Paste\tCtrl-V', 'Paste from the clipboard')
144 self
.ID_DELETE
= wx
.NewId()
145 menu
.Append(self
.ID_DELETE
, '&Delete\tCtrl-D', 'Delete object')
146 menu
.AppendSeparator()
147 self
.ID_LOCATE
= wx
.NewId()
148 self
.ID_TOOL_LOCATE
= wx
.NewId()
149 self
.ID_TOOL_PASTE
= wx
.NewId()
150 menu
.Append(self
.ID_LOCATE
, '&Locate\tCtrl-L', 'Locate control in test window and select it')
151 menuBar
.Append(menu
, '&Edit')
154 self
.ID_EMBED_PANEL
= wx
.NewId()
155 menu
.Append(self
.ID_EMBED_PANEL
, '&Embed Panel',
156 'Toggle embedding properties panel in the main window', True)
157 menu
.Check(self
.ID_EMBED_PANEL
, conf
.embedPanel
)
158 self
.ID_SHOW_TOOLS
= wx
.NewId()
159 menu
.Append(self
.ID_SHOW_TOOLS
, 'Show &Tools', 'Toggle tools', True)
160 menu
.Check(self
.ID_SHOW_TOOLS
, conf
.showTools
)
161 menu
.AppendSeparator()
162 self
.ID_TEST
= wx
.NewId()
163 menu
.Append(self
.ID_TEST
, '&Test\tF5', 'Show test window')
164 self
.ID_REFRESH
= wx
.NewId()
165 menu
.Append(self
.ID_REFRESH
, '&Refresh\tCtrl-R', 'Refresh test window')
166 self
.ID_AUTO_REFRESH
= wx
.NewId()
167 menu
.Append(self
.ID_AUTO_REFRESH
, '&Auto-refresh\tCtrl-A',
168 'Toggle auto-refresh mode', True)
169 menu
.Check(self
.ID_AUTO_REFRESH
, conf
.autoRefresh
)
170 self
.ID_TEST_HIDE
= wx
.NewId()
171 menu
.Append(self
.ID_TEST_HIDE
, '&Hide\tF6', 'Close test window')
172 menuBar
.Append(menu
, '&View')
175 self
.ID_MOVEUP
= wx
.NewId()
176 menu
.Append(self
.ID_MOVEUP
, '&Up', 'Move before previous sibling')
177 self
.ID_MOVEDOWN
= wx
.NewId()
178 menu
.Append(self
.ID_MOVEDOWN
, '&Down', 'Move after next sibling')
179 self
.ID_MOVELEFT
= wx
.NewId()
180 menu
.Append(self
.ID_MOVELEFT
, '&Make sibling', 'Make sibling of parent')
181 self
.ID_MOVERIGHT
= wx
.NewId()
182 menu
.Append(self
.ID_MOVERIGHT
, '&Make child', 'Make child of previous sibling')
183 menuBar
.Append(menu
, '&Move')
186 menu
.Append(wx
.ID_ABOUT
, '&About...', 'About XCRed')
187 self
.ID_README
= wx
.NewId()
188 menu
.Append(self
.ID_README
, '&Readme...\tF1', 'View the README file')
190 self
.ID_DEBUG_CMD
= wx
.NewId()
191 menu
.Append(self
.ID_DEBUG_CMD
, 'CMD', 'Python command line')
192 wx
.EVT_MENU(self
, self
.ID_DEBUG_CMD
, self
.OnDebugCMD
)
193 menuBar
.Append(menu
, '&Help')
195 self
.menuBar
= menuBar
196 self
.SetMenuBar(menuBar
)
199 tb
= self
.CreateToolBar(wx
.TB_HORIZONTAL | wx
.NO_BORDER | wx
.TB_FLAT
)
200 tb
.SetToolBitmapSize((24,24))
201 new_bmp
= wx
.ArtProvider
.GetBitmap(wx
.ART_NORMAL_FILE
, wx
.ART_TOOLBAR
)
202 open_bmp
= wx
.ArtProvider
.GetBitmap(wx
.ART_FILE_OPEN
, wx
.ART_TOOLBAR
)
203 save_bmp
= wx
.ArtProvider
.GetBitmap(wx
.ART_FILE_SAVE
, wx
.ART_TOOLBAR
)
204 undo_bmp
= wx
.ArtProvider
.GetBitmap(wx
.ART_UNDO
, wx
.ART_TOOLBAR
)
205 redo_bmp
= wx
.ArtProvider
.GetBitmap(wx
.ART_REDO
, wx
.ART_TOOLBAR
)
206 cut_bmp
= wx
.ArtProvider
.GetBitmap(wx
.ART_CUT
, wx
.ART_TOOLBAR
)
207 copy_bmp
= wx
.ArtProvider
.GetBitmap(wx
.ART_COPY
, wx
.ART_TOOLBAR
)
208 paste_bmp
= wx
.ArtProvider
.GetBitmap(wx
.ART_PASTE
, wx
.ART_TOOLBAR
)
210 tb
.AddSimpleTool(wx
.ID_NEW
, new_bmp
, 'New', 'New file')
211 tb
.AddSimpleTool(wx
.ID_OPEN
, open_bmp
, 'Open', 'Open file')
212 tb
.AddSimpleTool(wx
.ID_SAVE
, save_bmp
, 'Save', 'Save file')
213 tb
.AddControl(wx
.StaticLine(tb
, -1, size
=(-1,23), style
=wx
.LI_VERTICAL
))
214 tb
.AddSimpleTool(wx
.ID_UNDO
, undo_bmp
, 'Undo', 'Undo')
215 tb
.AddSimpleTool(wx
.ID_REDO
, redo_bmp
, 'Redo', 'Redo')
216 tb
.AddControl(wx
.StaticLine(tb
, -1, size
=(-1,23), style
=wx
.LI_VERTICAL
))
217 tb
.AddSimpleTool(wx
.ID_CUT
, cut_bmp
, 'Cut', 'Cut')
218 tb
.AddSimpleTool(wx
.ID_COPY
, copy_bmp
, 'Copy', 'Copy')
219 tb
.AddSimpleTool(self
.ID_TOOL_PASTE
, paste_bmp
, 'Paste', 'Paste')
220 tb
.AddControl(wx
.StaticLine(tb
, -1, size
=(-1,23), style
=wx
.LI_VERTICAL
))
221 tb
.AddSimpleTool(self
.ID_TOOL_LOCATE
,
222 images
.getLocateBitmap(), #images.getLocateArmedBitmap(),
223 'Locate', 'Locate control in test window and select it', True)
224 tb
.AddControl(wx
.StaticLine(tb
, -1, size
=(-1,23), style
=wx
.LI_VERTICAL
))
225 tb
.AddSimpleTool(self
.ID_TEST
, images
.getTestBitmap(), 'Test', 'Test window')
226 tb
.AddSimpleTool(self
.ID_REFRESH
, images
.getRefreshBitmap(),
227 'Refresh', 'Refresh view')
228 tb
.AddSimpleTool(self
.ID_AUTO_REFRESH
, images
.getAutoRefreshBitmap(),
229 'Auto-refresh', 'Toggle auto-refresh mode', True)
230 tb
.AddControl(wx
.StaticLine(tb
, -1, size
=(-1,23), style
=wx
.LI_VERTICAL
))
231 tb
.AddSimpleTool(self
.ID_MOVEUP
, images
.getToolMoveUpBitmap(),
232 'Up', 'Move before previous sibling')
233 tb
.AddSimpleTool(self
.ID_MOVEDOWN
, images
.getToolMoveDownBitmap(),
234 'Down', 'Move after next sibling')
235 tb
.AddSimpleTool(self
.ID_MOVELEFT
, images
.getToolMoveLeftBitmap(),
236 'Make Sibling', 'Make sibling of parent')
237 tb
.AddSimpleTool(self
.ID_MOVERIGHT
, images
.getToolMoveRightBitmap(),
238 'Make Child', 'Make child of previous sibling')
239 # if wx.Platform == '__WXGTK__':
240 # tb.AddSeparator() # otherwise auto-refresh sticks in status line
241 tb
.ToggleTool(self
.ID_AUTO_REFRESH
, conf
.autoRefresh
)
245 self
.minWidth
= tb
.GetSize()[0] # minimal width is the size of toolbar
248 wx
.EVT_MENU(self
, wx
.ID_NEW
, self
.OnNew
)
249 wx
.EVT_MENU(self
, wx
.ID_OPEN
, self
.OnOpen
)
250 wx
.EVT_MENU(self
, wx
.ID_SAVE
, self
.OnSaveOrSaveAs
)
251 wx
.EVT_MENU(self
, wx
.ID_SAVEAS
, self
.OnSaveOrSaveAs
)
252 wx
.EVT_MENU(self
, self
.ID_GENERATE_PYTHON
, self
.OnGeneratePython
)
253 wx
.EVT_MENU(self
, wx
.ID_EXIT
, self
.OnExit
)
255 wx
.EVT_MENU(self
, wx
.ID_UNDO
, self
.OnUndo
)
256 wx
.EVT_MENU(self
, wx
.ID_REDO
, self
.OnRedo
)
257 wx
.EVT_MENU(self
, wx
.ID_CUT
, self
.OnCutDelete
)
258 wx
.EVT_MENU(self
, wx
.ID_COPY
, self
.OnCopy
)
259 wx
.EVT_MENU(self
, wx
.ID_PASTE
, self
.OnPaste
)
260 wx
.EVT_MENU(self
, self
.ID_TOOL_PASTE
, self
.OnPaste
)
261 wx
.EVT_MENU(self
, self
.ID_DELETE
, self
.OnCutDelete
)
262 wx
.EVT_MENU(self
, self
.ID_LOCATE
, self
.OnLocate
)
263 wx
.EVT_MENU(self
, self
.ID_TOOL_LOCATE
, self
.OnLocate
)
265 wx
.EVT_MENU(self
, self
.ID_EMBED_PANEL
, self
.OnEmbedPanel
)
266 wx
.EVT_MENU(self
, self
.ID_SHOW_TOOLS
, self
.OnShowTools
)
267 wx
.EVT_MENU(self
, self
.ID_TEST
, self
.OnTest
)
268 wx
.EVT_MENU(self
, self
.ID_REFRESH
, self
.OnRefresh
)
269 wx
.EVT_MENU(self
, self
.ID_AUTO_REFRESH
, self
.OnAutoRefresh
)
270 wx
.EVT_MENU(self
, self
.ID_TEST_HIDE
, self
.OnTestHide
)
272 wx
.EVT_MENU(self
, self
.ID_MOVEUP
, self
.OnMoveUp
)
273 wx
.EVT_MENU(self
, self
.ID_MOVEDOWN
, self
.OnMoveDown
)
274 wx
.EVT_MENU(self
, self
.ID_MOVELEFT
, self
.OnMoveLeft
)
275 wx
.EVT_MENU(self
, self
.ID_MOVERIGHT
, self
.OnMoveRight
)
277 wx
.EVT_MENU(self
, wx
.ID_ABOUT
, self
.OnAbout
)
278 wx
.EVT_MENU(self
, self
.ID_README
, self
.OnReadme
)
281 wx
.EVT_UPDATE_UI(self
, wx
.ID_SAVE
, self
.OnUpdateUI
)
282 wx
.EVT_UPDATE_UI(self
, wx
.ID_CUT
, self
.OnUpdateUI
)
283 wx
.EVT_UPDATE_UI(self
, wx
.ID_COPY
, self
.OnUpdateUI
)
284 wx
.EVT_UPDATE_UI(self
, wx
.ID_PASTE
, self
.OnUpdateUI
)
285 wx
.EVT_UPDATE_UI(self
, self
.ID_LOCATE
, self
.OnUpdateUI
)
286 wx
.EVT_UPDATE_UI(self
, self
.ID_TOOL_LOCATE
, self
.OnUpdateUI
)
287 wx
.EVT_UPDATE_UI(self
, self
.ID_TOOL_PASTE
, self
.OnUpdateUI
)
288 wx
.EVT_UPDATE_UI(self
, wx
.ID_UNDO
, self
.OnUpdateUI
)
289 wx
.EVT_UPDATE_UI(self
, wx
.ID_REDO
, self
.OnUpdateUI
)
290 wx
.EVT_UPDATE_UI(self
, self
.ID_DELETE
, self
.OnUpdateUI
)
291 wx
.EVT_UPDATE_UI(self
, self
.ID_TEST
, self
.OnUpdateUI
)
292 wx
.EVT_UPDATE_UI(self
, self
.ID_REFRESH
, self
.OnUpdateUI
)
295 sizer
= wx
.BoxSizer(wx
.VERTICAL
)
296 sizer
.Add(wx
.StaticLine(self
, -1), 0, wx
.EXPAND
)
297 # Horizontal sizer for toolbar and splitter
298 self
.toolsSizer
= sizer1
= wx
.BoxSizer()
299 splitter
= wx
.SplitterWindow(self
, -1, style
=wx
.SP_3DSASH
)
300 self
.splitter
= splitter
301 splitter
.SetMinimumPaneSize(100)
304 g
.tree
= tree
= XML_Tree(splitter
, -1)
306 # Init pull-down menu data
308 g
.pullDownMenu
= pullDownMenu
= PullDownMenu(self
)
310 # Vertical toolbar for GUI buttons
311 g
.tools
= tools
= Tools(self
)
312 tools
.Show(conf
.showTools
)
313 if conf
.showTools
: sizer1
.Add(tools
, 0, wx
.EXPAND
)
315 tree
.RegisterKeyEvents()
317 # Miniframe for split mode
318 miniFrame
= wx
.MiniFrame(self
, -1, 'Properties & Style',
319 (conf
.panelX
, conf
.panelY
),
320 (conf
.panelWidth
, conf
.panelHeight
))
321 self
.miniFrame
= miniFrame
322 sizer2
= wx
.BoxSizer()
323 miniFrame
.SetAutoLayout(True)
324 miniFrame
.SetSizer(sizer2
)
325 wx
.EVT_CLOSE(self
.miniFrame
, self
.OnCloseMiniFrame
)
326 # Create panel for parameters
329 panel
= Panel(splitter
)
330 # Set plitter windows
331 splitter
.SplitVertically(tree
, panel
, conf
.sashPos
)
333 panel
= Panel(miniFrame
)
334 sizer2
.Add(panel
, 1, wx
.EXPAND
)
336 splitter
.Initialize(tree
)
337 sizer1
.Add(splitter
, 1, wx
.EXPAND
)
338 sizer
.Add(sizer1
, 1, wx
.EXPAND
)
339 self
.SetAutoLayout(True)
346 wx
.EVT_IDLE(self
, self
.OnIdle
)
347 wx
.EVT_CLOSE(self
, self
.OnCloseWindow
)
348 wx
.EVT_KEY_DOWN(self
, tools
.OnKeyDown
)
349 wx
.EVT_KEY_UP(self
, tools
.OnKeyUp
)
350 wx
.EVT_ICONIZE(self
, self
.OnIconize
)
352 def AppendRecent(self
, menu
):
353 # add recently used files to the menu
354 for id,name
in conf
.recentfiles
.iteritems():
356 wx
.EVT_MENU(self
,id,self
.OnRecentFile
)
359 def OnRecentFile(self
,evt
):
360 # open recently used file
361 if not self
.AskSave(): return
364 path
=conf
.recentfiles
[evt
.GetId()]
366 self
.SetStatusText('Data loaded')
368 self
.SetStatusText('Failed')
370 self
.SetStatusText('No such file')
373 def OnNew(self
, evt
):
374 if not self
.AskSave(): return
377 def OnOpen(self
, evt
):
378 if not self
.AskSave(): return
379 dlg
= wx
.FileDialog(self
, 'Open', os
.path
.dirname(self
.dataFile
),
380 '', '*.xrc', wx
.OPEN | wx
.CHANGE_DIR
)
381 if dlg
.ShowModal() == wx
.ID_OK
:
383 self
.SetStatusText('Loading...')
387 self
.SetStatusText('Data loaded')
389 self
.SetStatusText('Failed')
390 self
.SaveRecent(path
)
395 def OnSaveOrSaveAs(self
, evt
):
396 if evt
.GetId() == wx
.ID_SAVEAS
or not self
.dataFile
:
397 if self
.dataFile
: name
= ''
398 else: name
= defaultName
399 dirname
= os
.path
.abspath(os
.path
.dirname(self
.dataFile
))
400 dlg
= wx
.FileDialog(self
, 'Save As', dirname
, name
, '*.xrc',
401 wx
.SAVE | wx
.OVERWRITE_PROMPT | wx
.CHANGE_DIR
)
402 if dlg
.ShowModal() == wx
.ID_OK
:
404 if isinstance(path
, unicode):
405 path
= path
.encode(sys
.getfilesystemencoding())
412 # if we already have a localconf then it needs to be
413 # copied to a new config with the new name
415 nc
= self
.CreateLocalConf(path
)
416 flag
, key
, idx
= lc
.GetFirstEntry()
418 nc
.Write(key
, lc
.Read(key
))
419 flag
, key
, idx
= lc
.GetNextEntry(idx
)
422 # otherwise create a new one
423 conf
.localconf
= self
.CreateLocalConf(path
)
426 self
.SetStatusText('Saving...')
430 tmpFile
,tmpName
= tempfile
.mkstemp(prefix
='xrced-')
432 self
.Save(tmpName
) # save temporary file first
433 shutil
.move(tmpName
, path
)
435 if conf
.localconf
.ReadBool("autogenerate", False):
436 pypath
= conf
.localconf
.Read("filename")
437 embed
= conf
.localconf
.ReadBool("embedResource", False)
438 genGettext
= conf
.localconf
.ReadBool("genGettext", False)
439 self
.GeneratePython(self
.dataFile
, pypath
, embed
, genGettext
)
441 self
.SetStatusText('Data saved')
442 self
.SaveRecent(path
)
444 self
.SetStatusText('Failed')
448 def SaveRecent(self
,path
):
449 # append to recently used files
450 if path
not in conf
.recentfiles
.values():
452 self
.recentMenu
.Append(newid
, path
)
453 wx
.EVT_MENU(self
, newid
, self
.OnRecentFile
)
454 conf
.recentfiles
[newid
] = path
456 def GeneratePython(self
, dataFile
, pypath
, embed
, genGettext
):
458 import wx
.tools
.pywxrc
459 rescomp
= wx
.tools
.pywxrc
.XmlResourceCompiler()
460 rescomp
.MakePythonModule([dataFile
], pypath
, embed
, genGettext
)
463 wx
.LogError(traceback
.format_exception(inf
[0], inf
[1], None)[-1])
464 wx
.LogError('Error generating python code : %s' % pypath
)
468 def OnGeneratePython(self
, evt
):
469 if self
.modified
or not conf
.localconf
:
470 wx
.MessageBox("Save the XRC file first!", "Error")
473 dlg
= PythonOptions(self
, conf
.localconf
, self
.dataFile
)
478 def OnExit(self
, evt
):
481 def OnUndo(self
, evt
):
482 # Extra check to not mess with idle updating
483 if undoMan
.CanUndo():
486 def OnRedo(self
, evt
):
487 if undoMan
.CanRedo():
490 def OnCopy(self
, evt
):
491 selected
= tree
.selection
492 if not selected
: return # key pressed event
493 xxx
= tree
.GetPyData(selected
)
494 if wx
.TheClipboard
.Open():
496 data
= wx
.CustomDataObject('XRCED')
497 # Set encoding in header
499 s
= xxx
.node
.toxml(encoding
=expat
.native_encoding
)
501 data
= wx
.CustomDataObject('XRCED_node')
503 data
.SetData(cPickle
.dumps(s
))
504 wx
.TheClipboard
.SetData(data
)
505 wx
.TheClipboard
.Close()
506 self
.SetStatusText('Copied')
508 wx
.MessageBox("Unable to open the clipboard", "Error")
510 def OnPaste(self
, evt
):
511 selected
= tree
.selection
512 if not selected
: return # key pressed event
513 # For pasting with Ctrl pressed
515 if evt
.GetId() == pullDownMenu
.ID_PASTE_SIBLING
: appendChild
= False
516 elif evt
.GetId() == self
.ID_TOOL_PASTE
:
517 if g
.tree
.ctrl
: appendChild
= False
518 else: appendChild
= not tree
.NeedInsert(selected
)
519 else: appendChild
= not tree
.NeedInsert(selected
)
520 xxx
= tree
.GetPyData(selected
)
522 # If has next item, insert, else append to parent
523 nextItem
= tree
.GetNextSibling(selected
)
524 parentLeaf
= tree
.GetItemParent(selected
)
525 # Expanded container (must have children)
526 elif tree
.IsExpanded(selected
) and tree
.GetChildrenCount(selected
, False):
527 # Insert as first child
528 nextItem
= tree
.GetFirstChild(selected
)[0]
529 parentLeaf
= selected
531 # No children or unexpanded item - appendChild stays True
532 nextItem
= wx
.TreeItemId() # no next item
533 parentLeaf
= selected
534 parent
= tree
.GetPyData(parentLeaf
).treeObject()
536 # Create a copy of clipboard pickled element
537 success
= success_node
= False
538 if wx
.TheClipboard
.Open():
540 data
= wx
.CustomDataObject('XRCED')
541 if wx
.TheClipboard
.IsSupported(data
.GetFormat()):
543 success
= wx
.TheClipboard
.GetData(data
)
545 # there is a problem if XRCED_node is in clipboard
546 # but previous SetData was for XRCED
548 if not success
: # try other format
549 data
= wx
.CustomDataObject('XRCED_node')
550 if wx
.TheClipboard
.IsSupported(data
.GetFormat()):
551 success_node
= wx
.TheClipboard
.GetData(data
)
553 wx
.TheClipboard
.Close()
555 if not success
and not success_node
:
557 "There is no data in the clipboard in the required format",
561 xml
= cPickle
.loads(data
.GetData()) # xml representation of element
563 elem
= minidom
.parseString(xml
).childNodes
[0]
565 elem
= g
.tree
.dom
.createComment(xml
)
567 # Tempopary xxx object to test things
568 xxx
= MakeXXXFromDOM(parent
, elem
)
570 # Check compatibility
571 if not self
.ItemsAreCompatible(parent
, xxx
.treeObject()): return
573 # Check parent and child relationships.
574 # If parent is sizer or notebook, child is of wrong class or
575 # parent is normal window, child is child container then detach child.
576 isChildContainer
= isinstance(xxx
, xxxChildContainer
)
577 parentIsBook
= parent
.__class
__ in [xxxNotebook
, xxxChoicebook
, xxxListbook
]
578 if isChildContainer
and \
579 ((parent
.isSizer
and not isinstance(xxx
, xxxSizerItem
)) or \
580 (parentIsBook
and not isinstance(xxx
, xxxPage
)) or \
581 not (parent
.isSizer
or parentIsBook
)):
582 elem
.removeChild(xxx
.child
.node
) # detach child
583 elem
.unlink() # delete child container
584 elem
= xxx
.child
.node
# replace
585 # This may help garbage collection
586 xxx
.child
.parent
= None
587 isChildContainer
= False
588 # Parent is sizer or notebook, child is not child container
589 if parent
.isSizer
and not isChildContainer
and not isinstance(xxx
, xxxSpacer
):
590 # Create sizer item element
591 sizerItemElem
= MakeEmptyDOM(parent
.itemTag
)
592 sizerItemElem
.appendChild(elem
)
594 elif isinstance(parent
, xxxNotebook
) and not isChildContainer
:
595 pageElem
= MakeEmptyDOM('notebookpage')
596 pageElem
.appendChild(elem
)
598 elif isinstance(parent
, xxxChoicebook
) and not isChildContainer
:
599 pageElem
= MakeEmptyDOM('choicebookpage')
600 pageElem
.appendChild(elem
)
602 elif isinstance(parent
, xxxListbook
) and not isChildContainer
:
603 pageElem
= MakeEmptyDOM('listbookpage')
604 pageElem
.appendChild(elem
)
606 # Insert new node, register undo
607 newItem
= tree
.InsertNode(parentLeaf
, parent
, elem
, nextItem
)
608 undoMan
.RegisterUndo(UndoPasteCreate(parentLeaf
, parent
, newItem
, selected
))
609 # Scroll to show new item (!!! redundant?)
610 tree
.EnsureVisible(newItem
)
611 tree
.SelectItem(newItem
)
612 if not tree
.IsVisible(newItem
):
613 tree
.ScrollTo(newItem
)
616 if g
.testWin
and tree
.IsHighlatable(newItem
):
618 tree
.needUpdate
= True
619 tree
.pendingHighLight
= newItem
621 tree
.pendingHighLight
= None
623 self
.SetStatusText('Pasted')
626 def ItemsAreCompatible(self
, parent
, child
):
627 # Check compatibility
629 # Comments are always compatible
630 if child
.__class
__ == xxxComment
:
633 if child
.__class
__ in [xxxDialog
, xxxFrame
, xxxWizard
]:
635 if parent
.__class
__ != xxxMainNode
: error
= True
636 elif child
.__class
__ == xxxMenuBar
:
637 # Menubar can be put in frame or dialog
638 if parent
.__class
__ not in [xxxMainNode
, xxxFrame
, xxxDialog
]: error
= True
639 elif child
.__class
__ == xxxToolBar
:
640 # Toolbar can be top-level of child of panel or frame
641 if parent
.__class
__ not in [xxxMainNode
, xxxPanel
, xxxFrame
] and \
642 not parent
.isSizer
: error
= True
643 elif child
.__class
__ == xxxPanel
and parent
.__class
__ == xxxMainNode
:
645 elif child
.__class
__ == xxxSpacer
:
646 if not parent
.isSizer
: error
= True
647 elif child
.__class
__ == xxxSeparator
:
648 if not parent
.__class
__ in [xxxMenu
, xxxToolBar
]: error
= True
649 elif child
.__class
__ == xxxTool
:
650 if parent
.__class
__ != xxxToolBar
: error
= True
651 elif child
.__class
__ == xxxMenu
:
652 if not parent
.__class
__ in [xxxMainNode
, xxxMenuBar
, xxxMenu
]: error
= True
653 elif child
.__class
__ == xxxMenuItem
:
654 if not parent
.__class
__ in [xxxMenuBar
, xxxMenu
]: error
= True
655 elif child
.isSizer
and parent
.__class
__ in [xxxNotebook
, xxxChoicebook
, xxxListbook
]:
657 else: # normal controls can be almost anywhere
658 if parent
.__class
__ == xxxMainNode
or \
659 parent
.__class
__ in [xxxMenuBar
, xxxMenu
]: error
= True
661 if parent
.__class
__ == xxxMainNode
: parentClass
= 'root'
662 else: parentClass
= parent
.className
663 wx
.LogError('Incompatible parent/child: parent is %s, child is %s!' %
664 (parentClass
, child
.className
))
668 def OnMoveUp(self
, evt
):
669 selected
= tree
.selection
670 if not selected
: return
672 index
= tree
.ItemIndex(selected
)
673 if index
== 0: return # No previous sibling found
676 self
.lastOp
= 'MOVEUP'
677 status
= 'Moved before previous sibling'
682 parent
= tree
.GetItemParent(selected
)
683 elem
= tree
.RemoveLeaf(selected
)
684 nextItem
= tree
.GetFirstChild(parent
)[0]
685 for i
in range(index
- 1): nextItem
= tree
.GetNextSibling(nextItem
)
686 selected
= tree
.InsertNode(parent
, tree
.GetPyData(parent
).treeObject(), elem
, nextItem
)
687 newIndex
= tree
.ItemIndex(selected
)
688 tree
.SelectItem(selected
)
690 undoMan
.RegisterUndo(UndoMove(parent
, index
, parent
, newIndex
))
693 self
.SetStatusText(status
)
697 def OnMoveDown(self
, evt
):
698 selected
= tree
.selection
699 if not selected
: return
701 index
= tree
.ItemIndex(selected
)
702 next
= tree
.GetNextSibling(selected
)
706 self
.lastOp
= 'MOVEDOWN'
707 status
= 'Moved after next sibling'
712 parent
= tree
.GetItemParent(selected
)
713 elem
= tree
.RemoveLeaf(selected
)
714 nextItem
= tree
.GetFirstChild(parent
)[0]
715 for i
in range(index
+ 1): nextItem
= tree
.GetNextSibling(nextItem
)
716 selected
= tree
.InsertNode(parent
, tree
.GetPyData(parent
).treeObject(), elem
, nextItem
)
717 newIndex
= tree
.ItemIndex(selected
)
718 tree
.SelectItem(selected
)
720 undoMan
.RegisterUndo(UndoMove(parent
, index
, parent
, newIndex
))
723 self
.SetStatusText(status
)
727 def OnMoveLeft(self
, evt
):
728 selected
= tree
.selection
729 if not selected
: return
731 oldParent
= tree
.GetItemParent(selected
)
732 if not oldParent
: return
733 pparent
= tree
.GetItemParent(oldParent
)
734 if not pparent
: return
736 # Check compatibility
737 if not self
.ItemsAreCompatible(tree
.GetPyData(pparent
).treeObject(), tree
.GetPyData(selected
).treeObject()): return
740 self
.lastOp
= 'MOVELEFT'
741 status
= 'Made next sibling of parent'
743 oldIndex
= tree
.ItemIndex(selected
)
744 elem
= tree
.RemoveLeaf(selected
)
745 nextItem
= tree
.GetFirstChild(pparent
)[0]
746 parentIndex
= tree
.ItemIndex(oldParent
)
747 for i
in range(parentIndex
+ 1): nextItem
= tree
.GetNextSibling(nextItem
)
749 # Check parent and child relationships.
750 # If parent is sizer or notebook, child is of wrong class or
751 # parent is normal window, child is child container then detach child.
752 parent
= tree
.GetPyData(pparent
).treeObject()
753 xxx
= MakeXXXFromDOM(parent
, elem
)
754 isChildContainer
= isinstance(xxx
, xxxChildContainer
)
755 if isChildContainer
and \
756 ((parent
.isSizer
and not isinstance(xxx
, xxxSizerItem
)) or \
757 (isinstance(parent
, xxxNotebook
) and not isinstance(xxx
, xxxNotebookPage
)) or \
758 not (parent
.isSizer
or isinstance(parent
, xxxNotebook
))):
759 elem
.removeChild(xxx
.child
.node
) # detach child
760 elem
.unlink() # delete child container
761 elem
= xxx
.child
.node
# replace
762 # This may help garbage collection
763 xxx
.child
.parent
= None
764 isChildContainer
= False
765 # Parent is sizer or notebook, child is not child container
766 if parent
.isSizer
and not isChildContainer
and not isinstance(xxx
, xxxSpacer
):
767 # Create sizer item element
768 sizerItemElem
= MakeEmptyDOM('sizeritem')
769 sizerItemElem
.appendChild(elem
)
771 elif isinstance(parent
, xxxNotebook
) and not isChildContainer
:
772 pageElem
= MakeEmptyDOM('notebookpage')
773 pageElem
.appendChild(elem
)
776 selected
= tree
.InsertNode(pparent
, tree
.GetPyData(pparent
).treeObject(), elem
, nextItem
)
777 newIndex
= tree
.ItemIndex(selected
)
778 tree
.SelectItem(selected
)
780 undoMan
.RegisterUndo(UndoMove(oldParent
, oldIndex
, pparent
, newIndex
))
783 self
.SetStatusText(status
)
785 def OnMoveRight(self
, evt
):
786 selected
= tree
.selection
787 if not selected
: return
789 oldParent
= tree
.GetItemParent(selected
)
790 if not oldParent
: return
792 newParent
= tree
.GetPrevSibling(selected
)
793 if not newParent
: return
795 parent
= tree
.GetPyData(newParent
).treeObject()
797 # Check compatibility
798 if not self
.ItemsAreCompatible(parent
, tree
.GetPyData(selected
).treeObject()): return
801 self
.lastOp
= 'MOVERIGHT'
802 status
= 'Made last child of previous sibling'
804 oldIndex
= tree
.ItemIndex(selected
)
805 elem
= tree
.RemoveLeaf(selected
)
807 # Check parent and child relationships.
808 # If parent is sizer or notebook, child is of wrong class or
809 # parent is normal window, child is child container then detach child.
810 xxx
= MakeXXXFromDOM(parent
, elem
)
811 isChildContainer
= isinstance(xxx
, xxxChildContainer
)
812 if isChildContainer
and \
813 ((parent
.isSizer
and not isinstance(xxx
, xxxSizerItem
)) or \
814 (isinstance(parent
, xxxNotebook
) and not isinstance(xxx
, xxxNotebookPage
)) or \
815 not (parent
.isSizer
or isinstance(parent
, xxxNotebook
))):
816 elem
.removeChild(xxx
.child
.node
) # detach child
817 elem
.unlink() # delete child container
818 elem
= xxx
.child
.node
# replace
819 # This may help garbage collection
820 xxx
.child
.parent
= None
821 isChildContainer
= False
822 # Parent is sizer or notebook, child is not child container
823 if parent
.isSizer
and not isChildContainer
and not isinstance(xxx
, xxxSpacer
):
824 # Create sizer item element
825 sizerItemElem
= MakeEmptyDOM('sizeritem')
826 sizerItemElem
.appendChild(elem
)
828 elif isinstance(parent
, xxxNotebook
) and not isChildContainer
:
829 pageElem
= MakeEmptyDOM('notebookpage')
830 pageElem
.appendChild(elem
)
833 selected
= tree
.InsertNode(newParent
, tree
.GetPyData(newParent
).treeObject(), elem
, wx
.TreeItemId())
835 newIndex
= tree
.ItemIndex(selected
)
836 tree
.SelectItem(selected
)
838 undoMan
.RegisterUndo(UndoMove(oldParent
, oldIndex
, newParent
, newIndex
))
841 self
.SetStatusText(status
)
844 def OnCutDelete(self
, evt
):
845 selected
= tree
.selection
846 if not selected
: return # key pressed event
848 if evt
.GetId() == wx
.ID_CUT
:
850 status
= 'Removed to clipboard'
852 self
.lastOp
= 'DELETE'
856 # If deleting top-level item, delete testWin
857 if selected
== g
.testWin
.item
:
861 # Remove highlight, update testWin
862 if g
.testWin
.highLight
:
863 g
.testWin
.highLight
.Remove()
864 tree
.needUpdate
= True
867 index
= tree
.ItemFullIndex(selected
)
868 xxx
= tree
.GetPyData(selected
)
869 parent
= tree
.GetPyData(tree
.GetItemParent(selected
)).treeObject()
870 elem
= tree
.RemoveLeaf(selected
)
871 undoMan
.RegisterUndo(UndoCutDelete(index
, parent
, elem
))
872 if evt
.GetId() == wx
.ID_CUT
:
873 if wx
.TheClipboard
.Open():
875 data
= wx
.CustomDataObject('XRCED')
877 s
= elem
.toxml(encoding
=expat
.native_encoding
)
879 data
= wx
.CustomDataObject('XRCED_node')
881 data
.SetData(cPickle
.dumps(s
))
882 wx
.TheClipboard
.SetData(data
)
883 wx
.TheClipboard
.Close()
885 wx
.MessageBox("Unable to open the clipboard", "Error")
886 tree
.pendingHighLight
= None
888 tree
.selection
= None
893 self
.SetStatusText(status
)
895 def OnSubclass(self
, evt
):
896 selected
= tree
.selection
897 xxx
= tree
.GetPyData(selected
).treeObject()
899 subclass
= xxx
.subclass
900 dlg
= wx
.TextEntryDialog(self
, 'Subclass:', defaultValue
=subclass
)
901 if dlg
.ShowModal() == wx
.ID_OK
:
902 subclass
= dlg
.GetValue()
904 elem
.setAttribute('subclass', subclass
)
905 elif elem
.hasAttribute('subclass'):
906 elem
.removeAttribute('subclass')
908 xxx
.subclass
= elem
.getAttribute('subclass')
909 tree
.SetItemText(selected
, xxx
.treeName())
910 panel
.pages
[0].box
.SetLabel(xxx
.panelName())
913 def OnEmbedPanel(self
, evt
):
914 conf
.embedPanel
= evt
.IsChecked()
916 # Remember last dimentions
917 conf
.panelX
, conf
.panelY
= self
.miniFrame
.GetPosition()
918 conf
.panelWidth
, conf
.panelHeight
= self
.miniFrame
.GetSize()
919 size
= self
.GetSize()
920 pos
= self
.GetPosition()
921 sizePanel
= panel
.GetSize()
922 panel
.Reparent(self
.splitter
)
923 self
.miniFrame
.GetSizer().Remove(panel
)
925 self
.SetDimensions(pos
.x
, pos
.y
, size
.width
+ sizePanel
.width
, size
.height
)
926 self
.splitter
.SplitVertically(tree
, panel
, conf
.sashPos
)
927 self
.miniFrame
.Show(False)
929 conf
.sashPos
= self
.splitter
.GetSashPosition()
930 pos
= self
.GetPosition()
931 size
= self
.GetSize()
932 sizePanel
= panel
.GetSize()
933 self
.splitter
.Unsplit(panel
)
934 sizer
= self
.miniFrame
.GetSizer()
935 panel
.Reparent(self
.miniFrame
)
937 sizer
.Add(panel
, 1, wx
.EXPAND
)
938 self
.miniFrame
.Show(True)
939 self
.miniFrame
.SetDimensions(conf
.panelX
, conf
.panelY
,
940 conf
.panelWidth
, conf
.panelHeight
)
941 self
.miniFrame
.Layout()
943 self
.SetDimensions(pos
.x
, pos
.y
,
944 max(size
.width
- sizePanel
.width
, self
.minWidth
), size
.height
)
946 def OnShowTools(self
, evt
):
947 conf
.showTools
= evt
.IsChecked()
948 g
.tools
.Show(conf
.showTools
)
950 self
.toolsSizer
.Prepend(g
.tools
, 0, wx
.EXPAND
)
952 self
.toolsSizer
.Remove(g
.tools
)
953 self
.toolsSizer
.Layout()
955 def OnTest(self
, evt
):
956 if not tree
.selection
: return # key pressed event
957 tree
.ShowTestWindow(tree
.selection
)
959 def OnTestHide(self
, evt
):
960 tree
.CloseTestWindow()
962 # Find object by relative position
963 def FindObject(self
, item
, obj
):
964 # We simply perform depth-first traversal, sinse it's too much
965 # hassle to deal with all sizer/window combinations
966 w
= tree
.FindNodeObject(item
)
967 if w
== obj
or isinstance(w
, wx
.GBSizerItem
) and w
.GetWindow() == obj
:
969 if tree
.ItemHasChildren(item
):
970 child
= tree
.GetFirstChild(item
)[0]
972 found
= self
.FindObject(child
, obj
)
973 if found
: return found
974 child
= tree
.GetNextSibling(child
)
977 def OnTestWinLeftDown(self
, evt
):
978 pos
= evt
.GetPosition()
979 self
.SetHandler(g
.testWin
)
980 g
.testWin
.Disconnect(wx
.ID_ANY
, wx
.ID_ANY
, wx
.wxEVT_LEFT_DOWN
)
981 item
= self
.FindObject(g
.testWin
.item
, evt
.GetEventObject())
983 tree
.EnsureVisible(item
)
984 tree
.SelectItem(item
)
985 self
.tb
.ToggleTool(self
.ID_TOOL_LOCATE
, False)
987 self
.SetStatusText('Selected %s' % tree
.GetItemText(item
))
989 self
.SetStatusText('Locate failed!')
991 def SetHandler(self
, w
, h
=None):
994 w
.SetCursor(wx
.CROSS_CURSOR
)
997 w
.SetCursor(wx
.NullCursor
)
998 for ch
in w
.GetChildren():
999 self
.SetHandler(ch
, h
)
1001 def OnLocate(self
, evt
):
1003 if evt
.GetId() == self
.ID_LOCATE
or \
1004 evt
.GetId() == self
.ID_TOOL_LOCATE
and evt
.IsChecked():
1005 self
.SetHandler(g
.testWin
, g
.testWin
)
1006 g
.testWin
.Connect(wx
.ID_ANY
, wx
.ID_ANY
, wx
.wxEVT_LEFT_DOWN
, self
.OnTestWinLeftDown
)
1007 if evt
.GetId() == self
.ID_LOCATE
:
1008 self
.tb
.ToggleTool(self
.ID_TOOL_LOCATE
, True)
1009 elif evt
.GetId() == self
.ID_TOOL_LOCATE
and not evt
.IsChecked():
1010 self
.SetHandler(g
.testWin
, None)
1011 g
.testWin
.Disconnect(wx
.ID_ANY
, wx
.ID_ANY
, wx
.wxEVT_LEFT_DOWN
)
1012 self
.SetStatusText('Click somewhere in your test window now')
1014 def OnRefresh(self
, evt
):
1015 # If modified, apply first
1016 selection
= tree
.selection
1018 xxx
= tree
.GetPyData(selection
)
1019 if xxx
and panel
.IsModified():
1020 tree
.Apply(xxx
, selection
)
1023 tree
.CreateTestWin(g
.testWin
.item
)
1024 panel
.modified
= False
1025 tree
.needUpdate
= False
1027 def OnAutoRefresh(self
, evt
):
1028 conf
.autoRefresh
= evt
.IsChecked()
1029 self
.menuBar
.Check(self
.ID_AUTO_REFRESH
, conf
.autoRefresh
)
1030 self
.tb
.ToggleTool(self
.ID_AUTO_REFRESH
, conf
.autoRefresh
)
1032 def OnAbout(self
, evt
):
1036 (c) Roman Rolinsky <rollrom@users.sourceforge.net>
1037 Homepage: http://xrced.sourceforge.net\
1039 dlg
= wx
.MessageDialog(self
, str, 'About XRCed', wx
.OK | wx
.CENTRE
)
1043 def OnReadme(self
, evt
):
1044 text
= open(os
.path
.join(basePath
, 'README.txt'), 'r').read()
1045 dlg
= ScrolledMessageDialog(self
, text
, "XRCed README")
1049 # Simple emulation of python command line
1050 def OnDebugCMD(self
, evt
):
1053 exec raw_input('C:\> ')
1058 (etype
, value
, tb
) =sys
.exc_info()
1059 tblist
=traceback
.extract_tb(tb
)[1:]
1060 msg
=' '.join(traceback
.format_exception_only(etype
, value
)
1061 +traceback
.format_list(tblist
))
1064 def OnCreate(self
, evt
):
1065 selected
= tree
.selection
1066 if tree
.ctrl
: appendChild
= False
1067 else: appendChild
= not tree
.NeedInsert(selected
)
1068 xxx
= tree
.GetPyData(selected
)
1072 # If has previous item, insert after it, else append to parent
1074 parentLeaf
= tree
.GetItemParent(selected
)
1076 # If has next item, insert, else append to parent
1077 nextItem
= tree
.GetNextSibling(selected
)
1078 parentLeaf
= tree
.GetItemParent(selected
)
1079 # Expanded container (must have children)
1080 elif tree
.shift
and tree
.IsExpanded(selected
) \
1081 and tree
.GetChildrenCount(selected
, False):
1082 nextItem
= tree
.GetFirstChild(selected
)[0]
1083 parentLeaf
= selected
1085 nextItem
= wx
.TreeItemId()
1086 parentLeaf
= selected
1087 parent
= tree
.GetPyData(parentLeaf
)
1088 if parent
.hasChild
: parent
= parent
.child
1090 # Create object_ref?
1091 if evt
.GetId() == ID_NEW
.REF
:
1092 ref
= wx
.GetTextFromUser('Create reference to:', 'Create reference')
1094 xxx
= MakeEmptyRefXXX(parent
, ref
)
1095 elif evt
.GetId() == ID_NEW
.COMMENT
:
1096 xxx
= MakeEmptyCommentXXX(parent
)
1098 # Create empty element
1099 if evt
.GetId() >= ID_NEW
.CUSTOM
:
1100 className
= pullDownMenu
.customMap
[evt
.GetId()]
1102 className
= pullDownMenu
.createMap
[evt
.GetId()]
1103 xxx
= MakeEmptyXXX(parent
, className
)
1105 # Insert new node, register undo
1106 if xxx
.isElement
: # true object
1107 # Set default name for top-level windows
1108 if parent
.__class
__ == xxxMainNode
:
1109 cl
= xxx
.treeObject().__class
__
1110 frame
.maxIDs
[cl
] += 1
1111 xxx
.setTreeName('%s%d' % (defaultIDs
[cl
], frame
.maxIDs
[cl
]))
1112 # And for some other standard controls
1113 elif parent
.__class
__ == xxxStdDialogButtonSizer
:
1114 xxx
.setTreeName(pullDownMenu
.stdButtonIDs
[evt
.GetId()][0])
1115 # We can even set label
1116 obj
= xxx
.treeObject()
1117 elem
= g
.tree
.dom
.createElement('label')
1118 elem
.appendChild(g
.tree
.dom
.createTextNode(pullDownMenu
.stdButtonIDs
[evt
.GetId()][1]))
1119 obj
.params
['label'] = xxxParam(elem
)
1120 xxx
.treeObject().node
.appendChild(elem
)
1122 newItem
= tree
.InsertNode(parentLeaf
, parent
, xxx
.node
, nextItem
)
1123 else: # comment node
1124 newItem
= tree
.InsertNode(parentLeaf
, parent
, xxx
.node
, nextItem
)
1125 undoMan
.RegisterUndo(UndoPasteCreate(parentLeaf
, parent
, newItem
, selected
))
1126 tree
.EnsureVisible(newItem
)
1127 tree
.SelectItem(newItem
)
1128 if not tree
.IsVisible(newItem
):
1129 tree
.ScrollTo(newItem
)
1132 if xxx
.isElement
and g
.testWin
and tree
.IsHighlatable(newItem
):
1133 if conf
.autoRefresh
:
1134 tree
.needUpdate
= True
1135 tree
.pendingHighLight
= newItem
1137 tree
.pendingHighLight
= None
1139 if not xxx
.isElement
:
1140 tree
.EditLabel(newItem
)
1143 # Replace one object with another
1144 def OnReplace(self
, evt
):
1145 selected
= tree
.selection
1146 xxx
= tree
.GetPyData(selected
).treeObject()
1148 parent
= elem
.parentNode
1149 undoMan
.RegisterUndo(UndoReplace(selected
))
1151 className
= pullDownMenu
.createMap
[evt
.GetId() - 1000]
1153 # Create temporary empty node (with default values)
1154 dummy
= MakeEmptyDOM(className
)
1155 if className
== 'spacer' and xxx
.className
!= 'spacer':
1157 elif xxx
.className
== 'spacer' and className
!= 'spacer':
1158 klass
= xxxSizerItem
1160 klass
= xxxDict
[className
]
1161 # Remove non-compatible children
1162 if tree
.ItemHasChildren(selected
) and not klass
.hasChildren
:
1163 tree
.DeleteChildren(selected
)
1164 nodes
= elem
.childNodes
[:]
1167 if node
.nodeType
!= minidom
.Node
.ELEMENT_NODE
: continue
1171 if not klass
.hasChildren
: remove
= True
1172 elif tag
not in klass
.allParams
and \
1173 (not klass
.hasStyle
or tag
not in klass
.styles
):
1178 elem
.removeChild(node
)
1181 # Remove sizeritem child if spacer
1182 if className
== 'spacer' and xxx
.className
!= 'spacer':
1183 sizeritem
= elem
.parentNode
1184 assert sizeritem
.getAttribute('class') == 'sizeritem'
1185 sizeritem
.removeChild(elem
)
1188 tree
.GetPyData(selected
).hasChild
= False
1189 elif xxx
.className
== 'spacer' and className
!= 'spacer':
1190 # Create sizeritem element
1191 assert xxx
.parent
.isSizer
1192 elem
.setAttribute('class', 'sizeritem')
1193 node
= MakeEmptyDOM(className
)
1194 elem
.appendChild(node
)
1195 # Replace to point to new object
1196 xxx
= xxxSizerItem(xxx
.parent
, elem
)
1198 tree
.SetPyData(selected
, xxx
)
1201 # Copy parameters present in dummy but not in elem
1202 for node
in dummy
.childNodes
:
1203 if node
.tagName
not in tags
: elem
.appendChild(node
.cloneNode(True))
1207 elem
.setAttribute('class', className
)
1208 if elem
.hasAttribute('subclass'):
1209 elem
.removeAttribute('subclass') # clear subclassing
1210 # Re-create xxx element
1211 xxx
= MakeXXXFromDOM(xxx
.parent
, elem
)
1212 # Remove incompatible style flags
1213 if 'style' in xxx
.params
:
1214 styles
= map(string
.strip
, xxx
.params
['style'].value().split('|'))
1215 newStyles
= [s
for s
in styles
if s
in klass
.winStyles
or s
in genericStyles
]
1216 if newStyles
!= styles
:
1218 value
= reduce(lambda a
,b
: a
+'|'+b
, newStyles
)
1221 xxx
.params
['style'].update(value
)
1223 # Update parent in child objects
1224 if tree
.ItemHasChildren(selected
):
1225 i
, cookie
= tree
.GetFirstChild(selected
)
1227 x
= tree
.GetPyData(i
)
1229 if x
.hasChild
: x
.child
.parent
= xxx
1230 i
, cookie
= tree
.GetNextChild(selected
, cookie
)
1233 if tree
.GetPyData(selected
).hasChild
: # child container
1234 container
= tree
.GetPyData(selected
)
1235 container
.resetChild(xxx
)
1238 tree
.SetPyData(selected
, xxx
)
1239 tree
.SetItemText(selected
, xxx
.treeName())
1240 tree
.SetItemImage(selected
, xxx
.treeImage())
1242 # Set default name for top-level windows
1243 if parent
.__class
__ == xxxMainNode
:
1244 cl
= xxx
.treeObject().__class
__
1245 frame
.maxIDs
[cl
] += 1
1246 xxx
.setTreeName('%s%d' % (defaultIDs
[cl
], frame
.maxIDs
[cl
]))
1249 g
.panel
.SetData(xxx
)
1253 #undoMan.RegisterUndo(UndoPasteCreate(parentLeaf, parent, newItem, selected))
1255 if g
.testWin
and tree
.IsHighlatable(selected
):
1256 if conf
.autoRefresh
:
1257 tree
.needUpdate
= True
1258 tree
.pendingHighLight
= selected
1260 tree
.pendingHighLight
= None
1264 # Expand/collapse subtree
1265 def OnExpand(self
, evt
):
1266 if tree
.selection
: tree
.ExpandAll(tree
.selection
)
1267 else: tree
.ExpandAll(tree
.root
)
1268 def OnCollapse(self
, evt
):
1269 if tree
.selection
: tree
.CollapseAll(tree
.selection
)
1270 else: tree
.CollapseAll(tree
.root
)
1272 def OnPullDownHighlight(self
, evt
):
1273 menuId
= evt
.GetMenuId()
1275 menu
= evt
.GetEventObject()
1276 help = menu
.GetHelpString(menuId
)
1277 self
.SetStatusText(help)
1279 self
.SetStatusText('')
1281 def OnUpdateUI(self
, evt
):
1282 if evt
.GetId() in [wx
.ID_CUT
, wx
.ID_COPY
, self
.ID_DELETE
]:
1283 evt
.Enable(tree
.selection
is not None and tree
.selection
!= tree
.root
)
1284 elif evt
.GetId() == wx
.ID_SAVE
:
1285 evt
.Enable(self
.modified
)
1286 elif evt
.GetId() in [wx
.ID_PASTE
, self
.ID_TOOL_PASTE
]:
1287 evt
.Enable(tree
.selection
is not None)
1288 elif evt
.GetId() == self
.ID_TEST
:
1289 evt
.Enable(tree
.selection
is not None and tree
.selection
!= tree
.root
)
1290 elif evt
.GetId() in [self
.ID_LOCATE
, self
.ID_TOOL_LOCATE
]:
1291 evt
.Enable(g
.testWin
is not None)
1292 elif evt
.GetId() == wx
.ID_UNDO
: evt
.Enable(undoMan
.CanUndo())
1293 elif evt
.GetId() == wx
.ID_REDO
: evt
.Enable(undoMan
.CanRedo())
1295 def OnIdle(self
, evt
):
1296 if self
.inIdle
: return # Recursive call protection
1300 if conf
.autoRefresh
:
1302 self
.SetStatusText('Refreshing test window...')
1304 tree
.CreateTestWin(g
.testWin
.item
)
1305 self
.SetStatusText('')
1306 tree
.needUpdate
= False
1307 elif tree
.pendingHighLight
:
1309 tree
.HighLight(tree
.pendingHighLight
)
1311 # Remove highlight if any problem
1312 if g
.testWin
.highLight
:
1313 g
.testWin
.highLight
.Remove()
1314 tree
.pendingHighLight
= None
1321 # We don't let close panel window
1322 def OnCloseMiniFrame(self
, evt
):
1325 def OnIconize(self
, evt
):
1327 conf
.x
, conf
.y
= self
.GetPosition()
1328 conf
.width
, conf
.height
= self
.GetSize()
1330 conf
.sashPos
= self
.splitter
.GetSashPosition()
1332 conf
.panelX
, conf
.panelY
= self
.miniFrame
.GetPosition()
1333 conf
.panelWidth
, conf
.panelHeight
= self
.miniFrame
.GetSize()
1334 self
.miniFrame
.Iconize()
1336 if not conf
.embedPanel
:
1337 self
.miniFrame
.Iconize(False)
1340 def OnCloseWindow(self
, evt
):
1341 if not self
.AskSave(): return
1342 if g
.testWin
: g
.testWin
.Destroy()
1343 if not panel
.GetPageCount() == 2:
1344 panel
.page2
.Destroy()
1346 # If we don't do this, page does not get destroyed (a bug?)
1348 if not self
.IsIconized():
1349 conf
.x
, conf
.y
= self
.GetPosition()
1350 conf
.width
, conf
.height
= self
.GetSize()
1352 conf
.sashPos
= self
.splitter
.GetSashPosition()
1354 conf
.panelX
, conf
.panelY
= self
.miniFrame
.GetPosition()
1355 conf
.panelWidth
, conf
.panelHeight
= self
.miniFrame
.GetSize()
1359 def CreateLocalConf(self
, path
):
1360 name
= os
.path
.splitext(path
)[0]
1362 return wx
.FileConfig(localFilename
=name
)
1367 conf
.localconf
= None
1369 self
.SetModified(False)
1375 # Numbers for new controls
1377 for cl
in [xxxPanel
, xxxDialog
, xxxFrame
,
1378 xxxMenuBar
, xxxMenu
, xxxToolBar
,
1379 xxxWizard
, xxxBitmap
, xxxIcon
]:
1383 g
.pullDownMenu
.clearCustom()
1385 def SetModified(self
, state
=True):
1386 self
.modified
= state
1387 name
= os
.path
.basename(self
.dataFile
)
1388 if not name
: name
= defaultName
1390 self
.SetTitle(progname
+ ': ' + name
+ ' *')
1392 self
.SetTitle(progname
+ ': ' + name
)
1394 def Open(self
, path
):
1395 if not os
.path
.exists(path
):
1396 wx
.LogError('File does not exists: %s' % path
)
1398 # Try to read the file
1402 dom
= minidom
.parse(f
)
1404 # Set encoding global variable and default encoding
1406 g
.currentEncoding
= dom
.encoding
1407 wx
.SetDefaultPyEncoding(g
.currentEncoding
.encode())
1409 g
.currentEncoding
= ''
1411 self
.dataFile
= path
= os
.path
.abspath(path
)
1412 dir = os
.path
.dirname(path
)
1413 if dir: os
.chdir(dir)
1414 # Allow importing modules from the same directory
1415 sys
.path
= sys_path
+ [dir]
1417 self
.SetTitle(progname
+ ': ' + os
.path
.basename(path
))
1418 conf
.localconf
= self
.CreateLocalConf(self
.dataFile
)
1420 # Nice exception printing
1421 inf
= sys
.exc_info()
1422 wx
.LogError(traceback
.format_exception(inf
[0], inf
[1], None)[-1])
1423 wx
.LogError('Error reading file: %s' % path
)
1428 def Indent(self
, node
, indent
= 0):
1429 if node
.nodeType
== minidom
.Node
.COMMENT_NODE
:
1430 text
= self
.domCopy
.createTextNode('\n' + ' ' * indent
)
1431 node
.parentNode
.insertBefore(text
, node
)
1432 return # no children
1433 # Copy child list because it will change soon
1434 children
= node
.childNodes
[:]
1435 # Main node doesn't need to be indented
1437 text
= self
.domCopy
.createTextNode('\n' + ' ' * indent
)
1438 node
.parentNode
.insertBefore(text
, node
)
1440 # Append newline after last child, except for text nodes
1441 if children
[-1].nodeType
== minidom
.Node
.ELEMENT_NODE
:
1442 text
= self
.domCopy
.createTextNode('\n' + ' ' * indent
)
1443 node
.appendChild(text
)
1444 # Indent children which are elements
1446 if n
.nodeType
== minidom
.Node
.ELEMENT_NODE
or \
1447 n
.nodeType
== minidom
.Node
.COMMENT_NODE
:
1448 self
.Indent(n
, indent
+ 2)
1450 def Save(self
, path
):
1454 if tree
.selection
and panel
.IsModified():
1455 self
.OnRefresh(wx
.CommandEvent())
1456 if g
.currentEncoding
:
1457 f
= codecs
.open(path
, 'wt', g
.currentEncoding
)
1459 f
= codecs
.open(path
, 'wt')
1460 # Make temporary copy for formatting it
1461 # !!! We can't clone dom node, it works only once
1462 #self.domCopy = tree.dom.cloneNode(True)
1463 self
.domCopy
= MyDocument()
1464 mainNode
= self
.domCopy
.appendChild(tree
.mainNode
.cloneNode(True))
1465 # Remove first child (test element)
1466 testElem
= mainNode
.firstChild
1467 mainNode
.removeChild(testElem
)
1469 self
.Indent(mainNode
)
1470 self
.domCopy
.writexml(f
, encoding
= g
.currentEncoding
)
1472 self
.domCopy
.unlink()
1474 self
.SetModified(False)
1475 panel
.SetModified(False)
1476 conf
.localconf
.Flush()
1478 inf
= sys
.exc_info()
1479 wx
.LogError(traceback
.format_exception(inf
[0], inf
[1], None)[-1])
1480 wx
.LogError('Error writing file: %s' % path
)
1484 if not (self
.modified
or panel
.IsModified()): return True
1485 flags
= wx
.ICON_EXCLAMATION | wx
.YES_NO | wx
.CANCEL | wx
.CENTRE
1486 dlg
= wx
.MessageDialog( self
, 'File is modified. Save before exit?',
1487 'Save before too late?', flags
)
1488 say
= dlg
.ShowModal()
1491 if say
== wx
.ID_YES
:
1492 self
.OnSaveOrSaveAs(wx
.CommandEvent(wx
.ID_SAVE
))
1493 # If save was successful, modified flag is unset
1494 if not self
.modified
: return True
1495 elif say
== wx
.ID_NO
:
1496 self
.SetModified(False)
1497 panel
.SetModified(False)
1504 ################################################################################
1506 class PythonOptions(wx
.Dialog
):
1508 def __init__(self
, parent
, cfg
, dataFile
):
1509 pre
= wx
.PreDialog()
1510 g
.frame
.res
.LoadOnDialog(pre
, parent
, "PYTHON_OPTIONS")
1511 self
.PostCreate(pre
)
1514 self
.dataFile
= dataFile
1516 self
.AutoGenerateCB
= xrc
.XRCCTRL(self
, "AutoGenerateCB")
1517 self
.EmbedCB
= xrc
.XRCCTRL(self
, "EmbedCB")
1518 self
.GettextCB
= xrc
.XRCCTRL(self
, "GettextCB")
1519 self
.MakeXRSFileCB
= xrc
.XRCCTRL(self
, "MakeXRSFileCB")
1520 self
.FileNameTC
= xrc
.XRCCTRL(self
, "FileNameTC")
1521 self
.BrowseBtn
= xrc
.XRCCTRL(self
, "BrowseBtn")
1522 self
.GenerateBtn
= xrc
.XRCCTRL(self
, "GenerateBtn")
1523 self
.SaveOptsBtn
= xrc
.XRCCTRL(self
, "SaveOptsBtn")
1525 self
.Bind(wx
.EVT_BUTTON
, self
.OnBrowse
, self
.BrowseBtn
)
1526 self
.Bind(wx
.EVT_BUTTON
, self
.OnGenerate
, self
.GenerateBtn
)
1527 self
.Bind(wx
.EVT_BUTTON
, self
.OnSaveOpts
, self
.SaveOptsBtn
)
1529 if self
.cfg
.Read("filename", "") != "":
1530 self
.FileNameTC
.SetValue(self
.cfg
.Read("filename"))
1532 name
= os
.path
.splitext(os
.path
.split(dataFile
)[1])[0]
1534 self
.FileNameTC
.SetValue(name
)
1535 self
.AutoGenerateCB
.SetValue(self
.cfg
.ReadBool("autogenerate", False))
1536 self
.EmbedCB
.SetValue(self
.cfg
.ReadBool("embedResource", False))
1537 self
.MakeXRSFileCB
.SetValue(self
.cfg
.ReadBool("makeXRS", False))
1538 self
.GettextCB
.SetValue(self
.cfg
.ReadBool("genGettext", False))
1541 def OnBrowse(self
, evt
):
1542 path
= self
.FileNameTC
.GetValue()
1543 dirname
= os
.path
.abspath(os
.path
.dirname(path
))
1544 name
= os
.path
.split(path
)[1]
1545 dlg
= wx
.FileDialog(self
, 'Save As', dirname
, name
, '*.py',
1546 wx
.SAVE | wx
.OVERWRITE_PROMPT
)
1547 if dlg
.ShowModal() == wx
.ID_OK
:
1548 path
= dlg
.GetPath()
1549 self
.FileNameTC
.SetValue(path
)
1553 def OnGenerate(self
, evt
):
1554 pypath
= self
.FileNameTC
.GetValue()
1555 embed
= self
.EmbedCB
.GetValue()
1556 genGettext
= self
.GettextCB
.GetValue()
1557 frame
.GeneratePython(self
.dataFile
, pypath
, embed
, genGettext
)
1561 def OnSaveOpts(self
, evt
=None):
1562 self
.cfg
.Write("filename", self
.FileNameTC
.GetValue())
1563 self
.cfg
.WriteBool("autogenerate", self
.AutoGenerateCB
.GetValue())
1564 self
.cfg
.WriteBool("embedResource", self
.EmbedCB
.GetValue())
1565 self
.cfg
.WriteBool("makeXRS", self
.MakeXRSFileCB
.GetValue())
1566 self
.cfg
.WriteBool("genGettext", self
.GettextCB
.GetValue())
1568 self
.EndModal(wx
.ID_OK
)
1571 ################################################################################
1574 print >> sys
.stderr
, 'usage: xrced [-dhiv] [file]'
1579 if wx
.VERSION
[:3] < MinWxVersion
:
1581 This version of XRCed may not work correctly on your version of wxWidgets. \
1582 Please upgrade wxWidgets to %d.%d.%d or higher.''' % MinWxVersion
)
1584 # Process comand-line
1587 opts
, args
= getopt
.getopt(sys
.argv
[1:], 'dhiv')
1595 print 'XRCed version', version
1598 except getopt
.GetoptError
:
1599 if wx
.Platform
!= '__WXMAC__': # macs have some extra parameters
1600 print >> sys
.stderr
, 'Unknown option'
1604 self
.SetAppName('xrced')
1607 conf
= g
.conf
= wx
.Config(style
= wx
.CONFIG_USE_LOCAL_FILE
)
1608 conf
.localconf
= None
1609 conf
.autoRefresh
= conf
.ReadInt('autorefresh', True)
1610 pos
= conf
.ReadInt('x', -1), conf
.ReadInt('y', -1)
1611 size
= conf
.ReadInt('width', 800), conf
.ReadInt('height', 600)
1612 conf
.embedPanel
= conf
.ReadInt('embedPanel', True)
1613 conf
.showTools
= conf
.ReadInt('showTools', True)
1614 conf
.sashPos
= conf
.ReadInt('sashPos', 200)
1615 # read recently used files
1616 recentfiles
=conf
.Read('recentFiles','')
1619 for fil
in recentfiles
.split('|'):
1620 conf
.recentfiles
[wx
.NewId()]=fil
1621 if not conf
.embedPanel
:
1622 conf
.panelX
= conf
.ReadInt('panelX', -1)
1623 conf
.panelY
= conf
.ReadInt('panelY', -1)
1625 conf
.panelX
= conf
.panelY
= -1
1626 conf
.panelWidth
= conf
.ReadInt('panelWidth', 200)
1627 conf
.panelHeight
= conf
.ReadInt('panelHeight', 200)
1628 conf
.panic
= not conf
.HasEntry('nopanic')
1630 wx
.FileSystem
.AddHandler(wx
.MemoryFSHandler())
1632 frame
= Frame(pos
, size
)
1635 # Load file after showing
1638 frame
.open = frame
.Open(args
[0])
1646 wc
.WriteInt('autorefresh', conf
.autoRefresh
)
1647 wc
.WriteInt('x', conf
.x
)
1648 wc
.WriteInt('y', conf
.y
)
1649 wc
.WriteInt('width', conf
.width
)
1650 wc
.WriteInt('height', conf
.height
)
1651 wc
.WriteInt('embedPanel', conf
.embedPanel
)
1652 wc
.WriteInt('showTools', conf
.showTools
)
1653 if not conf
.embedPanel
:
1654 wc
.WriteInt('panelX', conf
.panelX
)
1655 wc
.WriteInt('panelY', conf
.panelY
)
1656 wc
.WriteInt('sashPos', conf
.sashPos
)
1657 wc
.WriteInt('panelWidth', conf
.panelWidth
)
1658 wc
.WriteInt('panelHeight', conf
.panelHeight
)
1659 wc
.WriteInt('nopanic', True)
1660 wc
.Write('recentFiles', '|'.join(conf
.recentfiles
.values()[-5:]))
1664 app
= App(0, useBestVisual
=False)
1665 #app.SetAssertMode(wx.PYAPP_ASSERT_LOG)
1671 if __name__
== '__main__':