]>
git.saurik.com Git - wxWidgets.git/blob - wxPython/wx/tools/XRCed/xrced.py
046bb763f4c8c94afe9ce5a7185d037955aecc8c
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 # 1 adds CMD command to Help menu
47 <HTML><H2>Welcome to XRC<font color="blue">ed</font></H2><H3><font color="green">DON'T PANIC :)</font></H3>
48 Read this note before clicking on anything!<P>
49 To start select tree root, then popup menu with your right mouse button,
50 select "Append Child", and then any command.<P>
51 Or just press one of the buttons on the tools palette.<P>
52 Enter XML ID, change properties, create children.<P>
53 To test your interface select Test command (View menu).<P>
54 Consult README file for the details.</HTML>
57 defaultIDs
= {xxxPanel
:'PANEL', xxxDialog
:'DIALOG', xxxFrame
:'FRAME',
58 xxxMenuBar
:'MENUBAR', xxxMenu
:'MENU', xxxToolBar
:'TOOLBAR',
59 xxxWizard
:'WIZARD', xxxBitmap
:'BITMAP', xxxIcon
:'ICON'}
61 defaultName
= 'UNTITLED.xrc'
63 ################################################################################
65 # ScrolledMessageDialog - modified from wxPython lib to set fixed-width font
66 class ScrolledMessageDialog(wx
.Dialog
):
67 def __init__(self
, parent
, msg
, caption
, pos
= wx
.DefaultPosition
, size
= (500,300)):
68 from wx
.lib
.layoutf
import Layoutf
69 wx
.Dialog
.__init
__(self
, parent
, -1, caption
, pos
, size
)
70 text
= wx
.TextCtrl(self
, -1, msg
, wx
.DefaultPosition
,
71 wx
.DefaultSize
, wx
.TE_MULTILINE | wx
.TE_READONLY
)
72 text
.SetFont(g
.modernFont())
73 dc
= wx
.WindowDC(text
)
74 # !!! possible bug - GetTextExtent without font returns sysfont dims
75 w
, h
= dc
.GetFullTextExtent(' ', g
.modernFont())[:2]
76 ok
= wx
.Button(self
, wx
.ID_OK
, "OK")
77 text
.SetConstraints(Layoutf('t=t5#1;b=t5#2;l=l5#1;r=r5#1', (self
,ok
)))
78 text
.SetSize((w
* 80 + 30, h
* 40))
80 ok
.SetConstraints(Layoutf('b=b5#1;x%w50#1;w!80;h!25', (self
,)))
81 self
.SetAutoLayout(True)
83 self
.CenterOnScreen(wx
.BOTH
)
85 ################################################################################
87 # Event handler for using during location
88 class Locator(wx
.EvtHandler
):
89 def ProcessEvent(self
, evt
):
92 class Frame(wx
.Frame
):
93 def __init__(self
, pos
, size
):
94 wx
.Frame
.__init
__(self
, None, -1, '', pos
, size
)
96 frame
= g
.frame
= self
97 bar
= self
.CreateStatusBar(2)
98 bar
.SetStatusWidths([-1, 40])
99 self
.SetIcon(images
.getIconIcon())
104 # Load our own resources
105 self
.res
= xrc
.XmlResource('')
106 # !!! Blocking of assert failure occurring in older unicode builds
108 quietlog
= wx
.LogNull()
109 self
.res
.Load(os
.path
.join(basePath
, 'xrced.xrc'))
110 except wx
._core
.PyAssertionError
:
111 print 'PyAssertionError was ignored'
114 menuBar
= wx
.MenuBar()
117 menu
.Append(wx
.ID_NEW
, '&New\tCtrl-N', 'New file')
118 menu
.AppendSeparator()
119 menu
.Append(wx
.ID_OPEN
, '&Open...\tCtrl-O', 'Open XRC file')
120 self
.recentMenu
= wx
.Menu()
121 self
.AppendRecent(self
.recentMenu
)
122 menu
.AppendMenu(-1, 'Open Recent', self
.recentMenu
, 'Open a recent file')
123 menu
.AppendSeparator()
124 menu
.Append(wx
.ID_SAVE
, '&Save\tCtrl-S', 'Save XRC file')
125 menu
.Append(wx
.ID_SAVEAS
, 'Save &As...', 'Save XRC file under different name')
126 self
.ID_GENERATE_PYTHON
= wx
.NewId()
127 menu
.Append(self
.ID_GENERATE_PYTHON
, '&Generate Python...',
128 'Generate a Python module that uses this XRC')
129 menu
.AppendSeparator()
130 menu
.Append(wx
.ID_EXIT
, '&Quit\tCtrl-Q', 'Exit application')
132 menuBar
.Append(menu
, '&File')
135 menu
.Append(wx
.ID_UNDO
, '&Undo\tCtrl-Z', 'Undo')
136 menu
.Append(wx
.ID_REDO
, '&Redo\tCtrl-Y', 'Redo')
137 menu
.AppendSeparator()
138 menu
.Append(wx
.ID_CUT
, 'Cut\tCtrl-X', 'Cut to the clipboard')
139 menu
.Append(wx
.ID_COPY
, '&Copy\tCtrl-C', 'Copy to the clipboard')
140 menu
.Append(wx
.ID_PASTE
, '&Paste\tCtrl-V', 'Paste from the clipboard')
141 self
.ID_DELETE
= wx
.NewId()
142 menu
.Append(self
.ID_DELETE
, '&Delete\tCtrl-D', 'Delete object')
143 menu
.AppendSeparator()
144 self
.ID_LOCATE
= wx
.NewId()
145 self
.ID_TOOL_LOCATE
= wx
.NewId()
146 self
.ID_TOOL_PASTE
= wx
.NewId()
147 menu
.Append(self
.ID_LOCATE
, '&Locate\tCtrl-L', 'Locate control in test window and select it')
148 menuBar
.Append(menu
, '&Edit')
151 self
.ID_EMBED_PANEL
= wx
.NewId()
152 menu
.Append(self
.ID_EMBED_PANEL
, '&Embed Panel',
153 'Toggle embedding properties panel in the main window', True)
154 menu
.Check(self
.ID_EMBED_PANEL
, conf
.embedPanel
)
155 self
.ID_SHOW_TOOLS
= wx
.NewId()
156 menu
.Append(self
.ID_SHOW_TOOLS
, 'Show &Tools', 'Toggle tools', True)
157 menu
.Check(self
.ID_SHOW_TOOLS
, conf
.showTools
)
158 menu
.AppendSeparator()
159 self
.ID_TEST
= wx
.NewId()
160 menu
.Append(self
.ID_TEST
, '&Test\tF5', 'Show test window')
161 self
.ID_REFRESH
= wx
.NewId()
162 menu
.Append(self
.ID_REFRESH
, '&Refresh\tCtrl-R', 'Refresh test window')
163 self
.ID_AUTO_REFRESH
= wx
.NewId()
164 menu
.Append(self
.ID_AUTO_REFRESH
, '&Auto-refresh\tCtrl-A',
165 'Toggle auto-refresh mode', True)
166 menu
.Check(self
.ID_AUTO_REFRESH
, conf
.autoRefresh
)
167 self
.ID_TEST_HIDE
= wx
.NewId()
168 menu
.Append(self
.ID_TEST_HIDE
, '&Hide\tF6', 'Close test window')
169 menuBar
.Append(menu
, '&View')
172 self
.ID_MOVEUP
= wx
.NewId()
173 menu
.Append(self
.ID_MOVEUP
, '&Up', 'Move before previous sibling')
174 self
.ID_MOVEDOWN
= wx
.NewId()
175 menu
.Append(self
.ID_MOVEDOWN
, '&Down', 'Move after next sibling')
176 self
.ID_MOVELEFT
= wx
.NewId()
177 menu
.Append(self
.ID_MOVELEFT
, '&Make sibling', 'Make sibling of parent')
178 self
.ID_MOVERIGHT
= wx
.NewId()
179 menu
.Append(self
.ID_MOVERIGHT
, '&Make child', 'Make child of previous sibling')
180 menuBar
.Append(menu
, '&Move')
183 menu
.Append(wx
.ID_ABOUT
, '&About...', 'About XCRed')
184 self
.ID_README
= wx
.NewId()
185 menu
.Append(self
.ID_README
, '&Readme...', 'View the README file')
187 self
.ID_DEBUG_CMD
= wx
.NewId()
188 menu
.Append(self
.ID_DEBUG_CMD
, 'CMD', 'Python command line')
189 wx
.EVT_MENU(self
, self
.ID_DEBUG_CMD
, self
.OnDebugCMD
)
190 menuBar
.Append(menu
, '&Help')
192 self
.menuBar
= menuBar
193 self
.SetMenuBar(menuBar
)
196 tb
= self
.CreateToolBar(wx
.TB_HORIZONTAL | wx
.NO_BORDER | wx
.TB_FLAT
)
197 tb
.SetToolBitmapSize((24,24))
198 new_bmp
= wx
.ArtProvider
.GetBitmap(wx
.ART_NORMAL_FILE
, wx
.ART_TOOLBAR
)
199 open_bmp
= wx
.ArtProvider
.GetBitmap(wx
.ART_FILE_OPEN
, wx
.ART_TOOLBAR
)
200 save_bmp
= wx
.ArtProvider
.GetBitmap(wx
.ART_FILE_SAVE
, wx
.ART_TOOLBAR
)
201 undo_bmp
= wx
.ArtProvider
.GetBitmap(wx
.ART_UNDO
, wx
.ART_TOOLBAR
)
202 redo_bmp
= wx
.ArtProvider
.GetBitmap(wx
.ART_REDO
, wx
.ART_TOOLBAR
)
203 cut_bmp
= wx
.ArtProvider
.GetBitmap(wx
.ART_CUT
, wx
.ART_TOOLBAR
)
204 copy_bmp
= wx
.ArtProvider
.GetBitmap(wx
.ART_COPY
, wx
.ART_TOOLBAR
)
205 paste_bmp
= wx
.ArtProvider
.GetBitmap(wx
.ART_PASTE
, wx
.ART_TOOLBAR
)
207 tb
.AddSimpleTool(wx
.ID_NEW
, new_bmp
, 'New', 'New file')
208 tb
.AddSimpleTool(wx
.ID_OPEN
, open_bmp
, 'Open', 'Open file')
209 tb
.AddSimpleTool(wx
.ID_SAVE
, save_bmp
, 'Save', 'Save file')
210 tb
.AddControl(wx
.StaticLine(tb
, -1, size
=(-1,23), style
=wx
.LI_VERTICAL
))
211 tb
.AddSimpleTool(wx
.ID_UNDO
, undo_bmp
, 'Undo', 'Undo')
212 tb
.AddSimpleTool(wx
.ID_REDO
, redo_bmp
, 'Redo', 'Redo')
213 tb
.AddControl(wx
.StaticLine(tb
, -1, size
=(-1,23), style
=wx
.LI_VERTICAL
))
214 tb
.AddSimpleTool(wx
.ID_CUT
, cut_bmp
, 'Cut', 'Cut')
215 tb
.AddSimpleTool(wx
.ID_COPY
, copy_bmp
, 'Copy', 'Copy')
216 tb
.AddSimpleTool(self
.ID_TOOL_PASTE
, paste_bmp
, 'Paste', 'Paste')
217 tb
.AddControl(wx
.StaticLine(tb
, -1, size
=(-1,23), style
=wx
.LI_VERTICAL
))
218 tb
.AddSimpleTool(self
.ID_TOOL_LOCATE
,
219 images
.getLocateBitmap(), #images.getLocateArmedBitmap(),
220 'Locate', 'Locate control in test window and select it', True)
221 tb
.AddControl(wx
.StaticLine(tb
, -1, size
=(-1,23), style
=wx
.LI_VERTICAL
))
222 tb
.AddSimpleTool(self
.ID_TEST
, images
.getTestBitmap(), 'Test', 'Test window')
223 tb
.AddSimpleTool(self
.ID_REFRESH
, images
.getRefreshBitmap(),
224 'Refresh', 'Refresh view')
225 tb
.AddSimpleTool(self
.ID_AUTO_REFRESH
, images
.getAutoRefreshBitmap(),
226 'Auto-refresh', 'Toggle auto-refresh mode', True)
227 tb
.AddControl(wx
.StaticLine(tb
, -1, size
=(-1,23), style
=wx
.LI_VERTICAL
))
228 tb
.AddSimpleTool(self
.ID_MOVEUP
, images
.getToolMoveUpBitmap(),
229 'Up', 'Move before previous sibling')
230 tb
.AddSimpleTool(self
.ID_MOVEDOWN
, images
.getToolMoveDownBitmap(),
231 'Down', 'Move after next sibling')
232 tb
.AddSimpleTool(self
.ID_MOVELEFT
, images
.getToolMoveLeftBitmap(),
233 'Make Sibling', 'Make sibling of parent')
234 tb
.AddSimpleTool(self
.ID_MOVERIGHT
, images
.getToolMoveRightBitmap(),
235 'Make Child', 'Make child of previous sibling')
236 # if wx.Platform == '__WXGTK__':
237 # tb.AddSeparator() # otherwise auto-refresh sticks in status line
238 tb
.ToggleTool(self
.ID_AUTO_REFRESH
, conf
.autoRefresh
)
242 self
.minWidth
= tb
.GetSize()[0] # minimal width is the size of toolbar
245 wx
.EVT_MENU(self
, wx
.ID_NEW
, self
.OnNew
)
246 wx
.EVT_MENU(self
, wx
.ID_OPEN
, self
.OnOpen
)
247 wx
.EVT_MENU(self
, wx
.ID_SAVE
, self
.OnSaveOrSaveAs
)
248 wx
.EVT_MENU(self
, wx
.ID_SAVEAS
, self
.OnSaveOrSaveAs
)
249 wx
.EVT_MENU(self
, self
.ID_GENERATE_PYTHON
, self
.OnGeneratePython
)
250 wx
.EVT_MENU(self
, wx
.ID_EXIT
, self
.OnExit
)
252 wx
.EVT_MENU(self
, wx
.ID_UNDO
, self
.OnUndo
)
253 wx
.EVT_MENU(self
, wx
.ID_REDO
, self
.OnRedo
)
254 wx
.EVT_MENU(self
, wx
.ID_CUT
, self
.OnCutDelete
)
255 wx
.EVT_MENU(self
, wx
.ID_COPY
, self
.OnCopy
)
256 wx
.EVT_MENU(self
, wx
.ID_PASTE
, self
.OnPaste
)
257 wx
.EVT_MENU(self
, self
.ID_TOOL_PASTE
, self
.OnPaste
)
258 wx
.EVT_MENU(self
, self
.ID_DELETE
, self
.OnCutDelete
)
259 wx
.EVT_MENU(self
, self
.ID_LOCATE
, self
.OnLocate
)
260 wx
.EVT_MENU(self
, self
.ID_TOOL_LOCATE
, self
.OnLocate
)
262 wx
.EVT_MENU(self
, self
.ID_EMBED_PANEL
, self
.OnEmbedPanel
)
263 wx
.EVT_MENU(self
, self
.ID_SHOW_TOOLS
, self
.OnShowTools
)
264 wx
.EVT_MENU(self
, self
.ID_TEST
, self
.OnTest
)
265 wx
.EVT_MENU(self
, self
.ID_REFRESH
, self
.OnRefresh
)
266 wx
.EVT_MENU(self
, self
.ID_AUTO_REFRESH
, self
.OnAutoRefresh
)
267 wx
.EVT_MENU(self
, self
.ID_TEST_HIDE
, self
.OnTestHide
)
269 wx
.EVT_MENU(self
, self
.ID_MOVEUP
, self
.OnMoveUp
)
270 wx
.EVT_MENU(self
, self
.ID_MOVEDOWN
, self
.OnMoveDown
)
271 wx
.EVT_MENU(self
, self
.ID_MOVELEFT
, self
.OnMoveLeft
)
272 wx
.EVT_MENU(self
, self
.ID_MOVERIGHT
, self
.OnMoveRight
)
274 wx
.EVT_MENU(self
, wx
.ID_ABOUT
, self
.OnAbout
)
275 wx
.EVT_MENU(self
, self
.ID_README
, self
.OnReadme
)
278 wx
.EVT_UPDATE_UI(self
, wx
.ID_SAVE
, self
.OnUpdateUI
)
279 wx
.EVT_UPDATE_UI(self
, wx
.ID_CUT
, self
.OnUpdateUI
)
280 wx
.EVT_UPDATE_UI(self
, wx
.ID_COPY
, self
.OnUpdateUI
)
281 wx
.EVT_UPDATE_UI(self
, wx
.ID_PASTE
, self
.OnUpdateUI
)
282 wx
.EVT_UPDATE_UI(self
, self
.ID_LOCATE
, self
.OnUpdateUI
)
283 wx
.EVT_UPDATE_UI(self
, self
.ID_TOOL_LOCATE
, self
.OnUpdateUI
)
284 wx
.EVT_UPDATE_UI(self
, self
.ID_TOOL_PASTE
, self
.OnUpdateUI
)
285 wx
.EVT_UPDATE_UI(self
, wx
.ID_UNDO
, self
.OnUpdateUI
)
286 wx
.EVT_UPDATE_UI(self
, wx
.ID_REDO
, self
.OnUpdateUI
)
287 wx
.EVT_UPDATE_UI(self
, self
.ID_DELETE
, self
.OnUpdateUI
)
288 wx
.EVT_UPDATE_UI(self
, self
.ID_TEST
, self
.OnUpdateUI
)
289 wx
.EVT_UPDATE_UI(self
, self
.ID_REFRESH
, self
.OnUpdateUI
)
292 sizer
= wx
.BoxSizer(wx
.VERTICAL
)
293 sizer
.Add(wx
.StaticLine(self
, -1), 0, wx
.EXPAND
)
294 # Horizontal sizer for toolbar and splitter
295 self
.toolsSizer
= sizer1
= wx
.BoxSizer()
296 splitter
= wx
.SplitterWindow(self
, -1, style
=wx
.SP_3DSASH
)
297 self
.splitter
= splitter
298 splitter
.SetMinimumPaneSize(100)
301 g
.tree
= tree
= XML_Tree(splitter
, -1)
303 # Init pull-down menu data
305 g
.pullDownMenu
= pullDownMenu
= PullDownMenu(self
)
307 # Vertical toolbar for GUI buttons
308 g
.tools
= tools
= Tools(self
)
309 tools
.Show(conf
.showTools
)
310 if conf
.showTools
: sizer1
.Add(tools
, 0, wx
.EXPAND
)
312 tree
.RegisterKeyEvents()
314 # Miniframe for split mode
315 miniFrame
= wx
.MiniFrame(self
, -1, 'Properties & Style',
316 (conf
.panelX
, conf
.panelY
),
317 (conf
.panelWidth
, conf
.panelHeight
))
318 self
.miniFrame
= miniFrame
319 sizer2
= wx
.BoxSizer()
320 miniFrame
.SetAutoLayout(True)
321 miniFrame
.SetSizer(sizer2
)
322 wx
.EVT_CLOSE(self
.miniFrame
, self
.OnCloseMiniFrame
)
323 # Create panel for parameters
326 panel
= Panel(splitter
)
327 # Set plitter windows
328 splitter
.SplitVertically(tree
, panel
, conf
.sashPos
)
330 panel
= Panel(miniFrame
)
331 sizer2
.Add(panel
, 1, wx
.EXPAND
)
333 splitter
.Initialize(tree
)
334 sizer1
.Add(splitter
, 1, wx
.EXPAND
)
335 sizer
.Add(sizer1
, 1, wx
.EXPAND
)
336 self
.SetAutoLayout(True)
343 wx
.EVT_IDLE(self
, self
.OnIdle
)
344 wx
.EVT_CLOSE(self
, self
.OnCloseWindow
)
345 wx
.EVT_KEY_DOWN(self
, tools
.OnKeyDown
)
346 wx
.EVT_KEY_UP(self
, tools
.OnKeyUp
)
347 wx
.EVT_ICONIZE(self
, self
.OnIconize
)
349 def AppendRecent(self
, menu
):
350 # add recently used files to the menu
351 for id,name
in conf
.recentfiles
.iteritems():
353 wx
.EVT_MENU(self
,id,self
.OnRecentFile
)
356 def OnRecentFile(self
,evt
):
357 # open recently used file
358 if not self
.AskSave(): return
361 path
=conf
.recentfiles
[evt
.GetId()]
363 self
.SetStatusText('Data loaded')
365 self
.SetStatusText('Failed')
367 self
.SetStatusText('No such file')
370 def OnNew(self
, evt
):
371 if not self
.AskSave(): return
374 def OnOpen(self
, evt
):
375 if not self
.AskSave(): return
376 dlg
= wx
.FileDialog(self
, 'Open', os
.path
.dirname(self
.dataFile
),
377 '', '*.xrc', wx
.OPEN | wx
.CHANGE_DIR
)
378 if dlg
.ShowModal() == wx
.ID_OK
:
380 self
.SetStatusText('Loading...')
384 self
.SetStatusText('Data loaded')
386 self
.SetStatusText('Failed')
387 self
.SaveRecent(path
)
392 def OnSaveOrSaveAs(self
, evt
):
393 if evt
.GetId() == wx
.ID_SAVEAS
or not self
.dataFile
:
394 if self
.dataFile
: name
= ''
395 else: name
= defaultName
396 dirname
= os
.path
.abspath(os
.path
.dirname(self
.dataFile
))
397 dlg
= wx
.FileDialog(self
, 'Save As', dirname
, name
, '*.xrc',
398 wx
.SAVE | wx
.OVERWRITE_PROMPT | wx
.CHANGE_DIR
)
399 if dlg
.ShowModal() == wx
.ID_OK
:
401 if isinstance(path
, unicode):
402 path
= path
.encode(sys
.getfilesystemencoding())
409 # if we already have a localconf then it needs to be
410 # copied to a new config with the new name
412 nc
= self
.CreateLocalConf(path
)
413 flag
, key
, idx
= lc
.GetFirstEntry()
415 nc
.Write(key
, lc
.Read(key
))
416 flag
, key
, idx
= lc
.GetNextEntry(idx
)
419 # otherwise create a new one
420 conf
.localconf
= self
.CreateLocalConf(path
)
423 self
.SetStatusText('Saving...')
427 tmpFile
,tmpName
= tempfile
.mkstemp(prefix
='xrced-')
429 self
.Save(tmpName
) # save temporary file first
430 shutil
.move(tmpName
, path
)
432 if conf
.localconf
.ReadBool("autogenerate", False):
433 pypath
= conf
.localconf
.Read("filename")
434 embed
= conf
.localconf
.ReadBool("embedResource", False)
435 genGettext
= conf
.localconf
.ReadBool("genGettext", False)
436 self
.GeneratePython(self
.dataFile
, pypath
, embed
, genGettext
)
438 self
.SetStatusText('Data saved')
439 self
.SaveRecent(path
)
441 self
.SetStatusText('Failed')
445 def SaveRecent(self
,path
):
446 # append to recently used files
447 if path
not in conf
.recentfiles
.values():
449 self
.recentMenu
.Append(newid
, path
)
450 wx
.EVT_MENU(self
, newid
, self
.OnRecentFile
)
451 conf
.recentfiles
[newid
] = path
453 def GeneratePython(self
, dataFile
, pypath
, embed
, genGettext
):
455 import wx
.tools
.pywxrc
456 rescomp
= wx
.tools
.pywxrc
.XmlResourceCompiler()
457 rescomp
.MakePythonModule([dataFile
], pypath
, embed
, genGettext
)
460 wx
.LogError(traceback
.format_exception(inf
[0], inf
[1], None)[-1])
461 wx
.LogError('Error generating python code : %s' % pypath
)
465 def OnGeneratePython(self
, evt
):
466 if self
.modified
or not conf
.localconf
:
467 wx
.MessageBox("Save the XRC file first!", "Error")
470 dlg
= PythonOptions(self
, conf
.localconf
, self
.dataFile
)
475 def OnExit(self
, evt
):
478 def OnUndo(self
, evt
):
479 # Extra check to not mess with idle updating
480 if undoMan
.CanUndo():
483 def OnRedo(self
, evt
):
484 if undoMan
.CanRedo():
487 def OnCopy(self
, evt
):
488 selected
= tree
.selection
489 if not selected
: return # key pressed event
490 xxx
= tree
.GetPyData(selected
)
491 if wx
.TheClipboard
.Open():
493 data
= wx
.CustomDataObject('XRCED')
494 # Set encoding in header
496 s
= xxx
.node
.toxml(encoding
=expat
.native_encoding
)
498 data
= wx
.CustomDataObject('XRCED_node')
500 data
.SetData(cPickle
.dumps(s
))
501 wx
.TheClipboard
.SetData(data
)
502 wx
.TheClipboard
.Close()
503 self
.SetStatusText('Copied')
505 wx
.MessageBox("Unable to open the clipboard", "Error")
507 def OnPaste(self
, evt
):
508 selected
= tree
.selection
509 if not selected
: return # key pressed event
510 # For pasting with Ctrl pressed
512 if evt
.GetId() == pullDownMenu
.ID_PASTE_SIBLING
: appendChild
= False
513 elif evt
.GetId() == self
.ID_TOOL_PASTE
:
514 if g
.tree
.ctrl
: appendChild
= False
515 else: appendChild
= not tree
.NeedInsert(selected
)
516 else: appendChild
= not tree
.NeedInsert(selected
)
517 xxx
= tree
.GetPyData(selected
)
519 # If has next item, insert, else append to parent
520 nextItem
= tree
.GetNextSibling(selected
)
521 parentLeaf
= tree
.GetItemParent(selected
)
522 # Expanded container (must have children)
523 elif tree
.IsExpanded(selected
) and tree
.GetChildrenCount(selected
, False):
524 # Insert as first child
525 nextItem
= tree
.GetFirstChild(selected
)[0]
526 parentLeaf
= selected
528 # No children or unexpanded item - appendChild stays True
529 nextItem
= wx
.TreeItemId() # no next item
530 parentLeaf
= selected
531 parent
= tree
.GetPyData(parentLeaf
).treeObject()
533 # Create a copy of clipboard pickled element
534 success
= success_node
= False
535 if wx
.TheClipboard
.Open():
536 data
= wx
.CustomDataObject('XRCED')
537 if wx
.TheClipboard
.IsSupported(data
.GetFormat()):
538 success
= wx
.TheClipboard
.GetData(data
)
539 if not success
: # try other format
540 data
= wx
.CustomDataObject('XRCED_node')
541 if wx
.TheClipboard
.IsSupported(data
.GetFormat()):
542 success_node
= wx
.TheClipboard
.GetData(data
)
543 wx
.TheClipboard
.Close()
545 if not success
and not success_node
:
547 "There is no data in the clipboard in the required format",
551 xml
= cPickle
.loads(data
.GetData()) # xml representation of element
553 elem
= minidom
.parseString(xml
).childNodes
[0]
555 elem
= g
.tree
.dom
.createComment(xml
)
557 # Tempopary xxx object to test things
558 xxx
= MakeXXXFromDOM(parent
, elem
)
560 # Check compatibility
561 if not self
.ItemsAreCompatible(parent
, xxx
.treeObject()): return
563 # Check parent and child relationships.
564 # If parent is sizer or notebook, child is of wrong class or
565 # parent is normal window, child is child container then detach child.
566 isChildContainer
= isinstance(xxx
, xxxChildContainer
)
567 parentIsBook
= parent
.__class
__ in [xxxNotebook
, xxxChoicebook
, xxxListbook
]
568 if isChildContainer
and \
569 ((parent
.isSizer
and not isinstance(xxx
, xxxSizerItem
)) or \
570 (parentIsBook
and not isinstance(xxx
, xxxPage
)) or \
571 not (parent
.isSizer
or parentIsBook
)):
572 elem
.removeChild(xxx
.child
.node
) # detach child
573 elem
.unlink() # delete child container
574 elem
= xxx
.child
.node
# replace
575 # This may help garbage collection
576 xxx
.child
.parent
= None
577 isChildContainer
= False
578 # Parent is sizer or notebook, child is not child container
579 if parent
.isSizer
and not isChildContainer
and not isinstance(xxx
, xxxSpacer
):
580 # Create sizer item element
581 sizerItemElem
= MakeEmptyDOM(parent
.itemTag
)
582 sizerItemElem
.appendChild(elem
)
584 elif isinstance(parent
, xxxNotebook
) and not isChildContainer
:
585 pageElem
= MakeEmptyDOM('notebookpage')
586 pageElem
.appendChild(elem
)
588 elif isinstance(parent
, xxxChoicebook
) and not isChildContainer
:
589 pageElem
= MakeEmptyDOM('choicebookpage')
590 pageElem
.appendChild(elem
)
592 elif isinstance(parent
, xxxListbook
) and not isChildContainer
:
593 pageElem
= MakeEmptyDOM('listbookpage')
594 pageElem
.appendChild(elem
)
596 # Insert new node, register undo
597 newItem
= tree
.InsertNode(parentLeaf
, parent
, elem
, nextItem
)
598 undoMan
.RegisterUndo(UndoPasteCreate(parentLeaf
, parent
, newItem
, selected
))
599 # Scroll to show new item (!!! redundant?)
600 tree
.EnsureVisible(newItem
)
601 tree
.SelectItem(newItem
)
602 if not tree
.IsVisible(newItem
):
603 tree
.ScrollTo(newItem
)
606 if g
.testWin
and tree
.IsHighlatable(newItem
):
608 tree
.needUpdate
= True
609 tree
.pendingHighLight
= newItem
611 tree
.pendingHighLight
= None
613 self
.SetStatusText('Pasted')
616 def ItemsAreCompatible(self
, parent
, child
):
617 # Check compatibility
619 # Comments are always compatible
620 if child
.__class
__ == xxxComment
:
623 if child
.__class
__ in [xxxDialog
, xxxFrame
, xxxWizard
]:
625 if parent
.__class
__ != xxxMainNode
: error
= True
626 elif child
.__class
__ == xxxMenuBar
:
627 # Menubar can be put in frame or dialog
628 if parent
.__class
__ not in [xxxMainNode
, xxxFrame
, xxxDialog
]: error
= True
629 elif child
.__class
__ == xxxToolBar
:
630 # Toolbar can be top-level of child of panel or frame
631 if parent
.__class
__ not in [xxxMainNode
, xxxPanel
, xxxFrame
] and \
632 not parent
.isSizer
: error
= True
633 elif child
.__class
__ == xxxPanel
and parent
.__class
__ == xxxMainNode
:
635 elif child
.__class
__ == xxxSpacer
:
636 if not parent
.isSizer
: error
= True
637 elif child
.__class
__ == xxxSeparator
:
638 if not parent
.__class
__ in [xxxMenu
, xxxToolBar
]: error
= True
639 elif child
.__class
__ == xxxTool
:
640 if parent
.__class
__ != xxxToolBar
: error
= True
641 elif child
.__class
__ == xxxMenu
:
642 if not parent
.__class
__ in [xxxMainNode
, xxxMenuBar
, xxxMenu
]: error
= True
643 elif child
.__class
__ == xxxMenuItem
:
644 if not parent
.__class
__ in [xxxMenuBar
, xxxMenu
]: error
= True
645 elif child
.isSizer
and parent
.__class
__ in [xxxNotebook
, xxxChoicebook
, xxxListbook
]:
647 else: # normal controls can be almost anywhere
648 if parent
.__class
__ == xxxMainNode
or \
649 parent
.__class
__ in [xxxMenuBar
, xxxMenu
]: error
= True
651 if parent
.__class
__ == xxxMainNode
: parentClass
= 'root'
652 else: parentClass
= parent
.className
653 wx
.LogError('Incompatible parent/child: parent is %s, child is %s!' %
654 (parentClass
, child
.className
))
658 def OnMoveUp(self
, evt
):
659 selected
= tree
.selection
660 if not selected
: return
662 index
= tree
.ItemIndex(selected
)
663 if index
== 0: return # No previous sibling found
666 self
.lastOp
= 'MOVEUP'
667 status
= 'Moved before previous sibling'
672 parent
= tree
.GetItemParent(selected
)
673 elem
= tree
.RemoveLeaf(selected
)
674 nextItem
= tree
.GetFirstChild(parent
)[0]
675 for i
in range(index
- 1): nextItem
= tree
.GetNextSibling(nextItem
)
676 selected
= tree
.InsertNode(parent
, tree
.GetPyData(parent
).treeObject(), elem
, nextItem
)
677 newIndex
= tree
.ItemIndex(selected
)
678 tree
.SelectItem(selected
)
680 undoMan
.RegisterUndo(UndoMove(parent
, index
, parent
, newIndex
))
683 self
.SetStatusText(status
)
687 def OnMoveDown(self
, evt
):
688 selected
= tree
.selection
689 if not selected
: return
691 index
= tree
.ItemIndex(selected
)
692 next
= tree
.GetNextSibling(selected
)
696 self
.lastOp
= 'MOVEDOWN'
697 status
= 'Moved after next sibling'
702 parent
= tree
.GetItemParent(selected
)
703 elem
= tree
.RemoveLeaf(selected
)
704 nextItem
= tree
.GetFirstChild(parent
)[0]
705 for i
in range(index
+ 1): nextItem
= tree
.GetNextSibling(nextItem
)
706 selected
= tree
.InsertNode(parent
, tree
.GetPyData(parent
).treeObject(), elem
, nextItem
)
707 newIndex
= tree
.ItemIndex(selected
)
708 tree
.SelectItem(selected
)
710 undoMan
.RegisterUndo(UndoMove(parent
, index
, parent
, newIndex
))
713 self
.SetStatusText(status
)
717 def OnMoveLeft(self
, evt
):
718 selected
= tree
.selection
719 if not selected
: return
721 oldParent
= tree
.GetItemParent(selected
)
722 if not oldParent
: return
723 pparent
= tree
.GetItemParent(oldParent
)
724 if not pparent
: return
726 # Check compatibility
727 if not self
.ItemsAreCompatible(tree
.GetPyData(pparent
).treeObject(), tree
.GetPyData(selected
).treeObject()): return
730 self
.lastOp
= 'MOVELEFT'
731 status
= 'Made next sibling of parent'
733 oldIndex
= tree
.ItemIndex(selected
)
734 elem
= tree
.RemoveLeaf(selected
)
735 nextItem
= tree
.GetFirstChild(pparent
)[0]
736 parentIndex
= tree
.ItemIndex(oldParent
)
737 for i
in range(parentIndex
+ 1): nextItem
= tree
.GetNextSibling(nextItem
)
739 # Check parent and child relationships.
740 # If parent is sizer or notebook, child is of wrong class or
741 # parent is normal window, child is child container then detach child.
742 parent
= tree
.GetPyData(pparent
).treeObject()
743 xxx
= MakeXXXFromDOM(parent
, elem
)
744 isChildContainer
= isinstance(xxx
, xxxChildContainer
)
745 if isChildContainer
and \
746 ((parent
.isSizer
and not isinstance(xxx
, xxxSizerItem
)) or \
747 (isinstance(parent
, xxxNotebook
) and not isinstance(xxx
, xxxNotebookPage
)) or \
748 not (parent
.isSizer
or isinstance(parent
, xxxNotebook
))):
749 elem
.removeChild(xxx
.child
.node
) # detach child
750 elem
.unlink() # delete child container
751 elem
= xxx
.child
.node
# replace
752 # This may help garbage collection
753 xxx
.child
.parent
= None
754 isChildContainer
= False
755 # Parent is sizer or notebook, child is not child container
756 if parent
.isSizer
and not isChildContainer
and not isinstance(xxx
, xxxSpacer
):
757 # Create sizer item element
758 sizerItemElem
= MakeEmptyDOM('sizeritem')
759 sizerItemElem
.appendChild(elem
)
761 elif isinstance(parent
, xxxNotebook
) and not isChildContainer
:
762 pageElem
= MakeEmptyDOM('notebookpage')
763 pageElem
.appendChild(elem
)
766 selected
= tree
.InsertNode(pparent
, tree
.GetPyData(pparent
).treeObject(), elem
, nextItem
)
767 newIndex
= tree
.ItemIndex(selected
)
768 tree
.SelectItem(selected
)
770 undoMan
.RegisterUndo(UndoMove(oldParent
, oldIndex
, pparent
, newIndex
))
773 self
.SetStatusText(status
)
775 def OnMoveRight(self
, evt
):
776 selected
= tree
.selection
777 if not selected
: return
779 oldParent
= tree
.GetItemParent(selected
)
780 if not oldParent
: return
782 newParent
= tree
.GetPrevSibling(selected
)
783 if not newParent
: return
785 parent
= tree
.GetPyData(newParent
).treeObject()
787 # Check compatibility
788 if not self
.ItemsAreCompatible(parent
, tree
.GetPyData(selected
).treeObject()): return
791 self
.lastOp
= 'MOVERIGHT'
792 status
= 'Made last child of previous sibling'
794 oldIndex
= tree
.ItemIndex(selected
)
795 elem
= tree
.RemoveLeaf(selected
)
797 # Check parent and child relationships.
798 # If parent is sizer or notebook, child is of wrong class or
799 # parent is normal window, child is child container then detach child.
800 xxx
= MakeXXXFromDOM(parent
, elem
)
801 isChildContainer
= isinstance(xxx
, xxxChildContainer
)
802 if isChildContainer
and \
803 ((parent
.isSizer
and not isinstance(xxx
, xxxSizerItem
)) or \
804 (isinstance(parent
, xxxNotebook
) and not isinstance(xxx
, xxxNotebookPage
)) or \
805 not (parent
.isSizer
or isinstance(parent
, xxxNotebook
))):
806 elem
.removeChild(xxx
.child
.node
) # detach child
807 elem
.unlink() # delete child container
808 elem
= xxx
.child
.node
# replace
809 # This may help garbage collection
810 xxx
.child
.parent
= None
811 isChildContainer
= False
812 # Parent is sizer or notebook, child is not child container
813 if parent
.isSizer
and not isChildContainer
and not isinstance(xxx
, xxxSpacer
):
814 # Create sizer item element
815 sizerItemElem
= MakeEmptyDOM('sizeritem')
816 sizerItemElem
.appendChild(elem
)
818 elif isinstance(parent
, xxxNotebook
) and not isChildContainer
:
819 pageElem
= MakeEmptyDOM('notebookpage')
820 pageElem
.appendChild(elem
)
823 selected
= tree
.InsertNode(newParent
, tree
.GetPyData(newParent
).treeObject(), elem
, wx
.TreeItemId())
825 newIndex
= tree
.ItemIndex(selected
)
826 tree
.SelectItem(selected
)
828 undoMan
.RegisterUndo(UndoMove(oldParent
, oldIndex
, newParent
, newIndex
))
831 self
.SetStatusText(status
)
834 def OnCutDelete(self
, evt
):
835 selected
= tree
.selection
836 if not selected
: return # key pressed event
838 if evt
.GetId() == wx
.ID_CUT
:
840 status
= 'Removed to clipboard'
842 self
.lastOp
= 'DELETE'
846 # If deleting top-level item, delete testWin
847 if selected
== g
.testWin
.item
:
851 # Remove highlight, update testWin
852 if g
.testWin
.highLight
:
853 g
.testWin
.highLight
.Remove()
854 tree
.needUpdate
= True
857 index
= tree
.ItemFullIndex(selected
)
858 xxx
= tree
.GetPyData(selected
)
859 parent
= tree
.GetPyData(tree
.GetItemParent(selected
)).treeObject()
860 elem
= tree
.RemoveLeaf(selected
)
861 undoMan
.RegisterUndo(UndoCutDelete(index
, parent
, elem
))
862 if evt
.GetId() == wx
.ID_CUT
:
863 if wx
.TheClipboard
.Open():
865 data
= wx
.CustomDataObject('XRCED')
867 s
= elem
.toxml(encoding
=expat
.native_encoding
)
869 data
= wx
.CustomDataObject('XRCED_node')
871 data
.SetData(cPickle
.dumps(s
))
872 wx
.TheClipboard
.SetData(data
)
873 wx
.TheClipboard
.Close()
875 wx
.MessageBox("Unable to open the clipboard", "Error")
876 tree
.pendingHighLight
= None
878 tree
.selection
= None
883 self
.SetStatusText(status
)
885 def OnSubclass(self
, evt
):
886 selected
= tree
.selection
887 xxx
= tree
.GetPyData(selected
).treeObject()
889 subclass
= xxx
.subclass
890 dlg
= wx
.TextEntryDialog(self
, 'Subclass:', defaultValue
=subclass
)
891 if dlg
.ShowModal() == wx
.ID_OK
:
892 subclass
= dlg
.GetValue()
894 elem
.setAttribute('subclass', subclass
)
895 elif elem
.hasAttribute('subclass'):
896 elem
.removeAttribute('subclass')
898 xxx
.subclass
= elem
.getAttribute('subclass')
899 tree
.SetItemText(selected
, xxx
.treeName())
900 panel
.pages
[0].box
.SetLabel(xxx
.panelName())
903 def OnEmbedPanel(self
, evt
):
904 conf
.embedPanel
= evt
.IsChecked()
906 # Remember last dimentions
907 conf
.panelX
, conf
.panelY
= self
.miniFrame
.GetPosition()
908 conf
.panelWidth
, conf
.panelHeight
= self
.miniFrame
.GetSize()
909 size
= self
.GetSize()
910 pos
= self
.GetPosition()
911 sizePanel
= panel
.GetSize()
912 panel
.Reparent(self
.splitter
)
913 self
.miniFrame
.GetSizer().Remove(panel
)
915 self
.SetDimensions(pos
.x
, pos
.y
, size
.width
+ sizePanel
.width
, size
.height
)
916 self
.splitter
.SplitVertically(tree
, panel
, conf
.sashPos
)
917 self
.miniFrame
.Show(False)
919 conf
.sashPos
= self
.splitter
.GetSashPosition()
920 pos
= self
.GetPosition()
921 size
= self
.GetSize()
922 sizePanel
= panel
.GetSize()
923 self
.splitter
.Unsplit(panel
)
924 sizer
= self
.miniFrame
.GetSizer()
925 panel
.Reparent(self
.miniFrame
)
927 sizer
.Add(panel
, 1, wx
.EXPAND
)
928 self
.miniFrame
.Show(True)
929 self
.miniFrame
.SetDimensions(conf
.panelX
, conf
.panelY
,
930 conf
.panelWidth
, conf
.panelHeight
)
931 self
.miniFrame
.Layout()
933 self
.SetDimensions(pos
.x
, pos
.y
,
934 max(size
.width
- sizePanel
.width
, self
.minWidth
), size
.height
)
936 def OnShowTools(self
, evt
):
937 conf
.showTools
= evt
.IsChecked()
938 g
.tools
.Show(conf
.showTools
)
940 self
.toolsSizer
.Prepend(g
.tools
, 0, wx
.EXPAND
)
942 self
.toolsSizer
.Remove(g
.tools
)
943 self
.toolsSizer
.Layout()
945 def OnTest(self
, evt
):
946 if not tree
.selection
: return # key pressed event
947 tree
.ShowTestWindow(tree
.selection
)
949 def OnTestHide(self
, evt
):
950 tree
.CloseTestWindow()
952 # Find object by relative position
953 def FindObject(self
, item
, obj
):
954 # We simply perform depth-first traversal, sinse it's too much
955 # hassle to deal with all sizer/window combinations
956 w
= tree
.FindNodeObject(item
)
957 if w
== obj
or isinstance(w
, wx
.GBSizerItem
) and w
.GetWindow() == obj
:
959 if tree
.ItemHasChildren(item
):
960 child
= tree
.GetFirstChild(item
)[0]
962 found
= self
.FindObject(child
, obj
)
963 if found
: return found
964 child
= tree
.GetNextSibling(child
)
967 def OnTestWinLeftDown(self
, evt
):
968 pos
= evt
.GetPosition()
969 self
.SetHandler(g
.testWin
)
970 g
.testWin
.Disconnect(wx
.ID_ANY
, wx
.ID_ANY
, wx
.wxEVT_LEFT_DOWN
)
971 item
= self
.FindObject(g
.testWin
.item
, evt
.GetEventObject())
973 tree
.EnsureVisible(item
)
974 tree
.SelectItem(item
)
975 self
.tb
.ToggleTool(self
.ID_TOOL_LOCATE
, False)
977 self
.SetStatusText('Selected %s' % tree
.GetItemText(item
))
979 self
.SetStatusText('Locate failed!')
981 def SetHandler(self
, w
, h
=None):
984 w
.SetCursor(wx
.CROSS_CURSOR
)
987 w
.SetCursor(wx
.NullCursor
)
988 for ch
in w
.GetChildren():
989 self
.SetHandler(ch
, h
)
991 def OnLocate(self
, evt
):
993 if evt
.GetId() == self
.ID_LOCATE
or \
994 evt
.GetId() == self
.ID_TOOL_LOCATE
and evt
.IsChecked():
995 self
.SetHandler(g
.testWin
, g
.testWin
)
996 g
.testWin
.Connect(wx
.ID_ANY
, wx
.ID_ANY
, wx
.wxEVT_LEFT_DOWN
, self
.OnTestWinLeftDown
)
997 if evt
.GetId() == self
.ID_LOCATE
:
998 self
.tb
.ToggleTool(self
.ID_TOOL_LOCATE
, True)
999 elif evt
.GetId() == self
.ID_TOOL_LOCATE
and not evt
.IsChecked():
1000 self
.SetHandler(g
.testWin
, None)
1001 g
.testWin
.Disconnect(wx
.ID_ANY
, wx
.ID_ANY
, wx
.wxEVT_LEFT_DOWN
)
1002 self
.SetStatusText('Click somewhere in your test window now')
1004 def OnRefresh(self
, evt
):
1005 # If modified, apply first
1006 selection
= tree
.selection
1008 xxx
= tree
.GetPyData(selection
)
1009 if xxx
and panel
.IsModified():
1010 tree
.Apply(xxx
, selection
)
1013 tree
.CreateTestWin(g
.testWin
.item
)
1014 panel
.modified
= False
1015 tree
.needUpdate
= False
1017 def OnAutoRefresh(self
, evt
):
1018 conf
.autoRefresh
= evt
.IsChecked()
1019 self
.menuBar
.Check(self
.ID_AUTO_REFRESH
, conf
.autoRefresh
)
1020 self
.tb
.ToggleTool(self
.ID_AUTO_REFRESH
, conf
.autoRefresh
)
1022 def OnAbout(self
, evt
):
1026 (c) Roman Rolinsky <rollrom@users.sourceforge.net>
1027 Homepage: http://xrced.sourceforge.net\
1029 dlg
= wx
.MessageDialog(self
, str, 'About XRCed', wx
.OK | wx
.CENTRE
)
1033 def OnReadme(self
, evt
):
1034 text
= open(os
.path
.join(basePath
, 'README.txt'), 'r').read()
1035 dlg
= ScrolledMessageDialog(self
, text
, "XRCed README")
1039 # Simple emulation of python command line
1040 def OnDebugCMD(self
, evt
):
1043 exec raw_input('C:\> ')
1048 (etype
, value
, tb
) =sys
.exc_info()
1049 tblist
=traceback
.extract_tb(tb
)[1:]
1050 msg
=' '.join(traceback
.format_exception_only(etype
, value
)
1051 +traceback
.format_list(tblist
))
1054 def OnCreate(self
, evt
):
1055 selected
= tree
.selection
1056 if tree
.ctrl
: appendChild
= False
1057 else: appendChild
= not tree
.NeedInsert(selected
)
1058 xxx
= tree
.GetPyData(selected
)
1062 # If has previous item, insert after it, else append to parent
1064 parentLeaf
= tree
.GetItemParent(selected
)
1066 # If has next item, insert, else append to parent
1067 nextItem
= tree
.GetNextSibling(selected
)
1068 parentLeaf
= tree
.GetItemParent(selected
)
1069 # Expanded container (must have children)
1070 elif tree
.shift
and tree
.IsExpanded(selected
) \
1071 and tree
.GetChildrenCount(selected
, False):
1072 nextItem
= tree
.GetFirstChild(selected
)[0]
1073 parentLeaf
= selected
1075 nextItem
= wx
.TreeItemId()
1076 parentLeaf
= selected
1077 parent
= tree
.GetPyData(parentLeaf
)
1078 if parent
.hasChild
: parent
= parent
.child
1080 # Create object_ref?
1081 if evt
.GetId() == ID_NEW
.REF
:
1082 ref
= wx
.GetTextFromUser('Create reference to:', 'Create reference')
1084 xxx
= MakeEmptyRefXXX(parent
, ref
)
1085 elif evt
.GetId() == ID_NEW
.COMMENT
:
1086 xxx
= MakeEmptyCommentXXX(parent
)
1088 # Create empty element
1089 className
= pullDownMenu
.createMap
[evt
.GetId()]
1090 xxx
= MakeEmptyXXX(parent
, className
)
1092 # Insert new node, register undo
1093 if xxx
.isElement
: # true object
1094 # Set default name for top-level windows
1095 if parent
.__class
__ == xxxMainNode
:
1096 cl
= xxx
.treeObject().__class
__
1097 frame
.maxIDs
[cl
] += 1
1098 xxx
.setTreeName('%s%d' % (defaultIDs
[cl
], frame
.maxIDs
[cl
]))
1099 # And for some other standard controls
1100 elif parent
.__class
__ == xxxStdDialogButtonSizer
:
1101 xxx
.setTreeName(pullDownMenu
.stdButtonIDs
[evt
.GetId()][0])
1102 # We can even set label
1103 obj
= xxx
.treeObject()
1104 elem
= g
.tree
.dom
.createElement('label')
1105 elem
.appendChild(g
.tree
.dom
.createTextNode(pullDownMenu
.stdButtonIDs
[evt
.GetId()][1]))
1106 obj
.params
['label'] = xxxParam(elem
)
1107 xxx
.treeObject().node
.appendChild(elem
)
1109 newItem
= tree
.InsertNode(parentLeaf
, parent
, xxx
.node
, nextItem
)
1110 else: # comment node
1111 newItem
= tree
.InsertNode(parentLeaf
, parent
, xxx
.node
, nextItem
)
1112 undoMan
.RegisterUndo(UndoPasteCreate(parentLeaf
, parent
, newItem
, selected
))
1113 tree
.EnsureVisible(newItem
)
1114 tree
.SelectItem(newItem
)
1115 if not tree
.IsVisible(newItem
):
1116 tree
.ScrollTo(newItem
)
1119 if xxx
.isElement
and g
.testWin
and tree
.IsHighlatable(newItem
):
1120 if conf
.autoRefresh
:
1121 tree
.needUpdate
= True
1122 tree
.pendingHighLight
= newItem
1124 tree
.pendingHighLight
= None
1126 if not xxx
.isElement
:
1127 tree
.EditLabel(newItem
)
1130 # Replace one object with another
1131 def OnReplace(self
, evt
):
1132 selected
= tree
.selection
1133 xxx
= tree
.GetPyData(selected
).treeObject()
1135 parent
= elem
.parentNode
1136 undoMan
.RegisterUndo(UndoReplace(selected
))
1138 className
= pullDownMenu
.createMap
[evt
.GetId() - 1000]
1140 # Create temporary empty node (with default values)
1141 dummy
= MakeEmptyDOM(className
)
1142 if className
== 'spacer' and xxx
.className
!= 'spacer':
1144 elif xxx
.className
== 'spacer' and className
!= 'spacer':
1145 klass
= xxxSizerItem
1147 klass
= xxxDict
[className
]
1148 # Remove non-compatible children
1149 if tree
.ItemHasChildren(selected
) and not klass
.hasChildren
:
1150 tree
.DeleteChildren(selected
)
1151 nodes
= elem
.childNodes
[:]
1154 if node
.nodeType
!= minidom
.Node
.ELEMENT_NODE
: continue
1158 if not klass
.hasChildren
: remove
= True
1159 elif tag
not in klass
.allParams
and \
1160 (not klass
.hasStyle
or tag
not in klass
.styles
):
1165 elem
.removeChild(node
)
1168 # Remove sizeritem child if spacer
1169 if className
== 'spacer' and xxx
.className
!= 'spacer':
1170 sizeritem
= elem
.parentNode
1171 assert sizeritem
.getAttribute('class') == 'sizeritem'
1172 sizeritem
.removeChild(elem
)
1175 tree
.GetPyData(selected
).hasChild
= False
1176 elif xxx
.className
== 'spacer' and className
!= 'spacer':
1177 # Create sizeritem element
1178 assert xxx
.parent
.isSizer
1179 elem
.setAttribute('class', 'sizeritem')
1180 node
= MakeEmptyDOM(className
)
1181 elem
.appendChild(node
)
1182 # Replace to point to new object
1183 xxx
= xxxSizerItem(xxx
.parent
, elem
)
1185 tree
.SetPyData(selected
, xxx
)
1188 # Copy parameters present in dummy but not in elem
1189 for node
in dummy
.childNodes
:
1190 if node
.tagName
not in tags
: elem
.appendChild(node
.cloneNode(True))
1194 elem
.setAttribute('class', className
)
1195 if elem
.hasAttribute('subclass'):
1196 elem
.removeAttribute('subclass') # clear subclassing
1197 # Re-create xxx element
1198 xxx
= MakeXXXFromDOM(xxx
.parent
, elem
)
1199 # Remove incompatible style flags
1200 if 'style' in xxx
.params
:
1201 styles
= map(string
.strip
, xxx
.params
['style'].value().split('|'))
1202 newStyles
= [s
for s
in styles
if s
in klass
.winStyles
or s
in genericStyles
]
1203 if newStyles
!= styles
:
1205 value
= reduce(lambda a
,b
: a
+'|'+b
, newStyles
)
1208 xxx
.params
['style'].update(value
)
1210 # Update parent in child objects
1211 if tree
.ItemHasChildren(selected
):
1212 i
, cookie
= tree
.GetFirstChild(selected
)
1214 x
= tree
.GetPyData(i
)
1216 if x
.hasChild
: x
.child
.parent
= xxx
1217 i
, cookie
= tree
.GetNextChild(selected
, cookie
)
1220 if tree
.GetPyData(selected
).hasChild
: # child container
1221 container
= tree
.GetPyData(selected
)
1222 container
.resetChild(xxx
)
1225 tree
.SetPyData(selected
, xxx
)
1226 tree
.SetItemText(selected
, xxx
.treeName())
1227 tree
.SetItemImage(selected
, xxx
.treeImage())
1229 # Set default name for top-level windows
1230 if parent
.__class
__ == xxxMainNode
:
1231 cl
= xxx
.treeObject().__class
__
1232 frame
.maxIDs
[cl
] += 1
1233 xxx
.setTreeName('%s%d' % (defaultIDs
[cl
], frame
.maxIDs
[cl
]))
1236 g
.panel
.SetData(xxx
)
1240 #undoMan.RegisterUndo(UndoPasteCreate(parentLeaf, parent, newItem, selected))
1242 if g
.testWin
and tree
.IsHighlatable(selected
):
1243 if conf
.autoRefresh
:
1244 tree
.needUpdate
= True
1245 tree
.pendingHighLight
= selected
1247 tree
.pendingHighLight
= None
1251 # Expand/collapse subtree
1252 def OnExpand(self
, evt
):
1253 if tree
.selection
: tree
.ExpandAll(tree
.selection
)
1254 else: tree
.ExpandAll(tree
.root
)
1255 def OnCollapse(self
, evt
):
1256 if tree
.selection
: tree
.CollapseAll(tree
.selection
)
1257 else: tree
.CollapseAll(tree
.root
)
1259 def OnPullDownHighlight(self
, evt
):
1260 menuId
= evt
.GetMenuId()
1262 menu
= evt
.GetEventObject()
1263 help = menu
.GetHelpString(menuId
)
1264 self
.SetStatusText(help)
1266 self
.SetStatusText('')
1268 def OnUpdateUI(self
, evt
):
1269 if evt
.GetId() in [wx
.ID_CUT
, wx
.ID_COPY
, self
.ID_DELETE
]:
1270 evt
.Enable(tree
.selection
is not None and tree
.selection
!= tree
.root
)
1271 elif evt
.GetId() == wx
.ID_SAVE
:
1272 evt
.Enable(self
.modified
)
1273 elif evt
.GetId() in [wx
.ID_PASTE
, self
.ID_TOOL_PASTE
]:
1274 evt
.Enable(tree
.selection
is not None)
1275 elif evt
.GetId() == self
.ID_TEST
:
1276 evt
.Enable(tree
.selection
is not None and tree
.selection
!= tree
.root
)
1277 elif evt
.GetId() in [self
.ID_LOCATE
, self
.ID_TOOL_LOCATE
]:
1278 evt
.Enable(g
.testWin
is not None)
1279 elif evt
.GetId() == wx
.ID_UNDO
: evt
.Enable(undoMan
.CanUndo())
1280 elif evt
.GetId() == wx
.ID_REDO
: evt
.Enable(undoMan
.CanRedo())
1282 def OnIdle(self
, evt
):
1283 if self
.inIdle
: return # Recursive call protection
1287 if conf
.autoRefresh
:
1289 self
.SetStatusText('Refreshing test window...')
1291 tree
.CreateTestWin(g
.testWin
.item
)
1292 self
.SetStatusText('')
1293 tree
.needUpdate
= False
1294 elif tree
.pendingHighLight
:
1296 tree
.HighLight(tree
.pendingHighLight
)
1298 # Remove highlight if any problem
1299 if g
.testWin
.highLight
:
1300 g
.testWin
.highLight
.Remove()
1301 tree
.pendingHighLight
= None
1308 # We don't let close panel window
1309 def OnCloseMiniFrame(self
, evt
):
1312 def OnIconize(self
, evt
):
1314 conf
.x
, conf
.y
= self
.GetPosition()
1315 conf
.width
, conf
.height
= self
.GetSize()
1317 conf
.sashPos
= self
.splitter
.GetSashPosition()
1319 conf
.panelX
, conf
.panelY
= self
.miniFrame
.GetPosition()
1320 conf
.panelWidth
, conf
.panelHeight
= self
.miniFrame
.GetSize()
1321 self
.miniFrame
.Iconize()
1323 if not conf
.embedPanel
:
1324 self
.miniFrame
.Iconize(False)
1327 def OnCloseWindow(self
, evt
):
1328 if not self
.AskSave(): return
1329 if g
.testWin
: g
.testWin
.Destroy()
1330 if not panel
.GetPageCount() == 2:
1331 panel
.page2
.Destroy()
1333 # If we don't do this, page does not get destroyed (a bug?)
1335 if not self
.IsIconized():
1336 conf
.x
, conf
.y
= self
.GetPosition()
1337 conf
.width
, conf
.height
= self
.GetSize()
1339 conf
.sashPos
= self
.splitter
.GetSashPosition()
1341 conf
.panelX
, conf
.panelY
= self
.miniFrame
.GetPosition()
1342 conf
.panelWidth
, conf
.panelHeight
= self
.miniFrame
.GetSize()
1346 def CreateLocalConf(self
, path
):
1347 name
= os
.path
.splitext(path
)[0]
1349 return wx
.FileConfig(localFilename
=name
)
1354 conf
.localconf
= None
1356 self
.SetModified(False)
1362 # Numbers for new controls
1364 for cl
in [xxxPanel
, xxxDialog
, xxxFrame
,
1365 xxxMenuBar
, xxxMenu
, xxxToolBar
,
1366 xxxWizard
, xxxBitmap
, xxxIcon
]:
1369 def SetModified(self
, state
=True):
1370 self
.modified
= state
1371 name
= os
.path
.basename(self
.dataFile
)
1372 if not name
: name
= defaultName
1374 self
.SetTitle(progname
+ ': ' + name
+ ' *')
1376 self
.SetTitle(progname
+ ': ' + name
)
1378 def Open(self
, path
):
1379 if not os
.path
.exists(path
):
1380 wx
.LogError('File does not exists: %s' % path
)
1382 # Try to read the file
1386 dom
= minidom
.parse(f
)
1388 # Set encoding global variable and default encoding
1390 g
.currentEncoding
= dom
.encoding
1391 wx
.SetDefaultPyEncoding(g
.currentEncoding
.encode())
1393 g
.currentEncoding
= ''
1395 self
.dataFile
= path
= os
.path
.abspath(path
)
1396 dir = os
.path
.dirname(path
)
1397 if dir: os
.chdir(dir)
1399 self
.SetTitle(progname
+ ': ' + os
.path
.basename(path
))
1400 conf
.localconf
= self
.CreateLocalConf(self
.dataFile
)
1402 # Nice exception printing
1403 inf
= sys
.exc_info()
1404 wx
.LogError(traceback
.format_exception(inf
[0], inf
[1], None)[-1])
1405 wx
.LogError('Error reading file: %s' % path
)
1410 def Indent(self
, node
, indent
= 0):
1411 if node
.nodeType
== minidom
.Node
.COMMENT_NODE
:
1412 text
= self
.domCopy
.createTextNode('\n' + ' ' * indent
)
1413 node
.parentNode
.insertBefore(text
, node
)
1414 return # no children
1415 # Copy child list because it will change soon
1416 children
= node
.childNodes
[:]
1417 # Main node doesn't need to be indented
1419 text
= self
.domCopy
.createTextNode('\n' + ' ' * indent
)
1420 node
.parentNode
.insertBefore(text
, node
)
1422 # Append newline after last child, except for text nodes
1423 if children
[-1].nodeType
== minidom
.Node
.ELEMENT_NODE
:
1424 text
= self
.domCopy
.createTextNode('\n' + ' ' * indent
)
1425 node
.appendChild(text
)
1426 # Indent children which are elements
1428 if n
.nodeType
== minidom
.Node
.ELEMENT_NODE
or \
1429 n
.nodeType
== minidom
.Node
.COMMENT_NODE
:
1430 self
.Indent(n
, indent
+ 2)
1432 def Save(self
, path
):
1436 if tree
.selection
and panel
.IsModified():
1437 self
.OnRefresh(wx
.CommandEvent())
1438 if g
.currentEncoding
:
1439 f
= codecs
.open(path
, 'wt', g
.currentEncoding
)
1441 f
= codecs
.open(path
, 'wt')
1442 # Make temporary copy for formatting it
1443 # !!! We can't clone dom node, it works only once
1444 #self.domCopy = tree.dom.cloneNode(True)
1445 self
.domCopy
= MyDocument()
1446 mainNode
= self
.domCopy
.appendChild(tree
.mainNode
.cloneNode(True))
1447 # Remove first child (test element)
1448 testElem
= mainNode
.firstChild
1449 mainNode
.removeChild(testElem
)
1451 self
.Indent(mainNode
)
1452 self
.domCopy
.writexml(f
, encoding
= g
.currentEncoding
)
1454 self
.domCopy
.unlink()
1456 self
.SetModified(False)
1457 panel
.SetModified(False)
1458 conf
.localconf
.Flush()
1460 inf
= sys
.exc_info()
1461 wx
.LogError(traceback
.format_exception(inf
[0], inf
[1], None)[-1])
1462 wx
.LogError('Error writing file: %s' % path
)
1466 if not (self
.modified
or panel
.IsModified()): return True
1467 flags
= wx
.ICON_EXCLAMATION | wx
.YES_NO | wx
.CANCEL | wx
.CENTRE
1468 dlg
= wx
.MessageDialog( self
, 'File is modified. Save before exit?',
1469 'Save before too late?', flags
)
1470 say
= dlg
.ShowModal()
1473 if say
== wx
.ID_YES
:
1474 self
.OnSaveOrSaveAs(wx
.CommandEvent(wx
.ID_SAVE
))
1475 # If save was successful, modified flag is unset
1476 if not self
.modified
: return True
1477 elif say
== wx
.ID_NO
:
1478 self
.SetModified(False)
1479 panel
.SetModified(False)
1486 ################################################################################
1488 class PythonOptions(wx
.Dialog
):
1490 def __init__(self
, parent
, cfg
, dataFile
):
1491 pre
= wx
.PreDialog()
1492 g
.frame
.res
.LoadOnDialog(pre
, parent
, "PYTHON_OPTIONS")
1493 self
.PostCreate(pre
)
1496 self
.dataFile
= dataFile
1498 self
.AutoGenerateCB
= xrc
.XRCCTRL(self
, "AutoGenerateCB")
1499 self
.EmbedCB
= xrc
.XRCCTRL(self
, "EmbedCB")
1500 self
.GettextCB
= xrc
.XRCCTRL(self
, "GettextCB")
1501 self
.MakeXRSFileCB
= xrc
.XRCCTRL(self
, "MakeXRSFileCB")
1502 self
.FileNameTC
= xrc
.XRCCTRL(self
, "FileNameTC")
1503 self
.BrowseBtn
= xrc
.XRCCTRL(self
, "BrowseBtn")
1504 self
.GenerateBtn
= xrc
.XRCCTRL(self
, "GenerateBtn")
1505 self
.SaveOptsBtn
= xrc
.XRCCTRL(self
, "SaveOptsBtn")
1507 self
.Bind(wx
.EVT_BUTTON
, self
.OnBrowse
, self
.BrowseBtn
)
1508 self
.Bind(wx
.EVT_BUTTON
, self
.OnGenerate
, self
.GenerateBtn
)
1509 self
.Bind(wx
.EVT_BUTTON
, self
.OnSaveOpts
, self
.SaveOptsBtn
)
1511 if self
.cfg
.Read("filename", "") != "":
1512 self
.FileNameTC
.SetValue(self
.cfg
.Read("filename"))
1514 name
= os
.path
.splitext(os
.path
.split(dataFile
)[1])[0]
1516 self
.FileNameTC
.SetValue(name
)
1517 self
.AutoGenerateCB
.SetValue(self
.cfg
.ReadBool("autogenerate", False))
1518 self
.EmbedCB
.SetValue(self
.cfg
.ReadBool("embedResource", False))
1519 self
.MakeXRSFileCB
.SetValue(self
.cfg
.ReadBool("makeXRS", False))
1520 self
.GettextCB
.SetValue(self
.cfg
.ReadBool("genGettext", False))
1523 def OnBrowse(self
, evt
):
1524 path
= self
.FileNameTC
.GetValue()
1525 dirname
= os
.path
.abspath(os
.path
.dirname(path
))
1526 name
= os
.path
.split(path
)[1]
1527 dlg
= wx
.FileDialog(self
, 'Save As', dirname
, name
, '*.py',
1528 wx
.SAVE | wx
.OVERWRITE_PROMPT
)
1529 if dlg
.ShowModal() == wx
.ID_OK
:
1530 path
= dlg
.GetPath()
1531 self
.FileNameTC
.SetValue(path
)
1535 def OnGenerate(self
, evt
):
1536 pypath
= self
.FileNameTC
.GetValue()
1537 embed
= self
.EmbedCB
.GetValue()
1538 genGettext
= self
.GettextCB
.GetValue()
1539 frame
.GeneratePython(self
.dataFile
, pypath
, embed
, genGettext
)
1543 def OnSaveOpts(self
, evt
=None):
1544 self
.cfg
.Write("filename", self
.FileNameTC
.GetValue())
1545 self
.cfg
.WriteBool("autogenerate", self
.AutoGenerateCB
.GetValue())
1546 self
.cfg
.WriteBool("embedResource", self
.EmbedCB
.GetValue())
1547 self
.cfg
.WriteBool("makeXRS", self
.MakeXRSFileCB
.GetValue())
1548 self
.cfg
.WriteBool("genGettext", self
.GettextCB
.GetValue())
1550 self
.EndModal(wx
.ID_OK
)
1553 ################################################################################
1556 print >> sys
.stderr
, 'usage: xrced [-dhiv] [file]'
1561 if wx
.VERSION
[:3] < MinWxVersion
:
1563 This version of XRCed may not work correctly on your version of wxWidgets. \
1564 Please upgrade wxWidgets to %d.%d.%d or higher.''' % MinWxVersion
)
1566 # Process comand-line
1569 opts
, args
= getopt
.getopt(sys
.argv
[1:], 'dhiv')
1577 print 'XRCed version', version
1580 except getopt
.GetoptError
:
1581 if wx
.Platform
!= '__WXMAC__': # macs have some extra parameters
1582 print >> sys
.stderr
, 'Unknown option'
1586 self
.SetAppName('xrced')
1589 conf
= g
.conf
= wx
.Config(style
= wx
.CONFIG_USE_LOCAL_FILE
)
1590 conf
.localconf
= None
1591 conf
.autoRefresh
= conf
.ReadInt('autorefresh', True)
1592 pos
= conf
.ReadInt('x', -1), conf
.ReadInt('y', -1)
1593 size
= conf
.ReadInt('width', 800), conf
.ReadInt('height', 600)
1594 conf
.embedPanel
= conf
.ReadInt('embedPanel', True)
1595 conf
.showTools
= conf
.ReadInt('showTools', True)
1596 conf
.sashPos
= conf
.ReadInt('sashPos', 200)
1597 # read recently used files
1598 recentfiles
=conf
.Read('recentFiles','')
1601 for fil
in recentfiles
.split('|'):
1602 conf
.recentfiles
[wx
.NewId()]=fil
1603 if not conf
.embedPanel
:
1604 conf
.panelX
= conf
.ReadInt('panelX', -1)
1605 conf
.panelY
= conf
.ReadInt('panelY', -1)
1607 conf
.panelX
= conf
.panelY
= -1
1608 conf
.panelWidth
= conf
.ReadInt('panelWidth', 200)
1609 conf
.panelHeight
= conf
.ReadInt('panelHeight', 200)
1610 conf
.panic
= not conf
.HasEntry('nopanic')
1612 wx
.FileSystem
.AddHandler(wx
.MemoryFSHandler())
1614 frame
= Frame(pos
, size
)
1617 # Load file after showing
1620 frame
.open = frame
.Open(args
[0])
1628 wc
.WriteInt('autorefresh', conf
.autoRefresh
)
1629 wc
.WriteInt('x', conf
.x
)
1630 wc
.WriteInt('y', conf
.y
)
1631 wc
.WriteInt('width', conf
.width
)
1632 wc
.WriteInt('height', conf
.height
)
1633 wc
.WriteInt('embedPanel', conf
.embedPanel
)
1634 wc
.WriteInt('showTools', conf
.showTools
)
1635 if not conf
.embedPanel
:
1636 wc
.WriteInt('panelX', conf
.panelX
)
1637 wc
.WriteInt('panelY', conf
.panelY
)
1638 wc
.WriteInt('sashPos', conf
.sashPos
)
1639 wc
.WriteInt('panelWidth', conf
.panelWidth
)
1640 wc
.WriteInt('panelHeight', conf
.panelHeight
)
1641 wc
.WriteInt('nopanic', True)
1642 wc
.Write('recentFiles', '|'.join(conf
.recentfiles
.values()[-5:]))
1646 app
= App(0, useBestVisual
=False)
1647 #app.SetAssertMode(wx.PYAPP_ASSERT_LOG)
1653 if __name__
== '__main__':