]>
git.saurik.com Git - wxWidgets.git/blob - wxPython/wx/tools/XRCed/xrced.py
23cce5040b76369fbb12b5c15448179b7c9e4a43
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)
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():
537 data
= wx
.CustomDataObject('XRCED')
538 if wx
.TheClipboard
.IsSupported(data
.GetFormat()):
540 success
= wx
.TheClipboard
.GetData(data
)
542 # there is a problem if XRCED_node is in clipboard
543 # but previous SetData was for XRCED
545 if not success
: # try other format
546 data
= wx
.CustomDataObject('XRCED_node')
547 if wx
.TheClipboard
.IsSupported(data
.GetFormat()):
548 success_node
= wx
.TheClipboard
.GetData(data
)
550 wx
.TheClipboard
.Close()
552 if not success
and not success_node
:
554 "There is no data in the clipboard in the required format",
558 xml
= cPickle
.loads(data
.GetData()) # xml representation of element
560 elem
= minidom
.parseString(xml
).childNodes
[0]
562 elem
= g
.tree
.dom
.createComment(xml
)
564 # Tempopary xxx object to test things
565 xxx
= MakeXXXFromDOM(parent
, elem
)
567 # Check compatibility
568 if not self
.ItemsAreCompatible(parent
, xxx
.treeObject()): return
570 # Check parent and child relationships.
571 # If parent is sizer or notebook, child is of wrong class or
572 # parent is normal window, child is child container then detach child.
573 isChildContainer
= isinstance(xxx
, xxxChildContainer
)
574 parentIsBook
= parent
.__class
__ in [xxxNotebook
, xxxChoicebook
, xxxListbook
]
575 if isChildContainer
and \
576 ((parent
.isSizer
and not isinstance(xxx
, xxxSizerItem
)) or \
577 (parentIsBook
and not isinstance(xxx
, xxxPage
)) or \
578 not (parent
.isSizer
or parentIsBook
)):
579 elem
.removeChild(xxx
.child
.node
) # detach child
580 elem
.unlink() # delete child container
581 elem
= xxx
.child
.node
# replace
582 # This may help garbage collection
583 xxx
.child
.parent
= None
584 isChildContainer
= False
585 # Parent is sizer or notebook, child is not child container
586 if parent
.isSizer
and not isChildContainer
and not isinstance(xxx
, xxxSpacer
):
587 # Create sizer item element
588 sizerItemElem
= MakeEmptyDOM(parent
.itemTag
)
589 sizerItemElem
.appendChild(elem
)
591 elif isinstance(parent
, xxxNotebook
) and not isChildContainer
:
592 pageElem
= MakeEmptyDOM('notebookpage')
593 pageElem
.appendChild(elem
)
595 elif isinstance(parent
, xxxChoicebook
) and not isChildContainer
:
596 pageElem
= MakeEmptyDOM('choicebookpage')
597 pageElem
.appendChild(elem
)
599 elif isinstance(parent
, xxxListbook
) and not isChildContainer
:
600 pageElem
= MakeEmptyDOM('listbookpage')
601 pageElem
.appendChild(elem
)
603 # Insert new node, register undo
604 newItem
= tree
.InsertNode(parentLeaf
, parent
, elem
, nextItem
)
605 undoMan
.RegisterUndo(UndoPasteCreate(parentLeaf
, parent
, newItem
, selected
))
606 # Scroll to show new item (!!! redundant?)
607 tree
.EnsureVisible(newItem
)
608 tree
.SelectItem(newItem
)
609 if not tree
.IsVisible(newItem
):
610 tree
.ScrollTo(newItem
)
613 if g
.testWin
and tree
.IsHighlatable(newItem
):
615 tree
.needUpdate
= True
616 tree
.pendingHighLight
= newItem
618 tree
.pendingHighLight
= None
620 self
.SetStatusText('Pasted')
623 def ItemsAreCompatible(self
, parent
, child
):
624 # Check compatibility
626 # Comments are always compatible
627 if child
.__class
__ == xxxComment
:
630 if child
.__class
__ in [xxxDialog
, xxxFrame
, xxxWizard
]:
632 if parent
.__class
__ != xxxMainNode
: error
= True
633 elif child
.__class
__ == xxxMenuBar
:
634 # Menubar can be put in frame or dialog
635 if parent
.__class
__ not in [xxxMainNode
, xxxFrame
, xxxDialog
]: error
= True
636 elif child
.__class
__ == xxxToolBar
:
637 # Toolbar can be top-level of child of panel or frame
638 if parent
.__class
__ not in [xxxMainNode
, xxxPanel
, xxxFrame
] and \
639 not parent
.isSizer
: error
= True
640 elif child
.__class
__ == xxxPanel
and parent
.__class
__ == xxxMainNode
:
642 elif child
.__class
__ == xxxSpacer
:
643 if not parent
.isSizer
: error
= True
644 elif child
.__class
__ == xxxSeparator
:
645 if not parent
.__class
__ in [xxxMenu
, xxxToolBar
]: error
= True
646 elif child
.__class
__ == xxxTool
:
647 if parent
.__class
__ != xxxToolBar
: error
= True
648 elif child
.__class
__ == xxxMenu
:
649 if not parent
.__class
__ in [xxxMainNode
, xxxMenuBar
, xxxMenu
]: error
= True
650 elif child
.__class
__ == xxxMenuItem
:
651 if not parent
.__class
__ in [xxxMenuBar
, xxxMenu
]: error
= True
652 elif child
.isSizer
and parent
.__class
__ in [xxxNotebook
, xxxChoicebook
, xxxListbook
]:
654 else: # normal controls can be almost anywhere
655 if parent
.__class
__ == xxxMainNode
or \
656 parent
.__class
__ in [xxxMenuBar
, xxxMenu
]: error
= True
658 if parent
.__class
__ == xxxMainNode
: parentClass
= 'root'
659 else: parentClass
= parent
.className
660 wx
.LogError('Incompatible parent/child: parent is %s, child is %s!' %
661 (parentClass
, child
.className
))
665 def OnMoveUp(self
, evt
):
666 selected
= tree
.selection
667 if not selected
: return
669 index
= tree
.ItemIndex(selected
)
670 if index
== 0: return # No previous sibling found
673 self
.lastOp
= 'MOVEUP'
674 status
= 'Moved before previous sibling'
679 parent
= tree
.GetItemParent(selected
)
680 elem
= tree
.RemoveLeaf(selected
)
681 nextItem
= tree
.GetFirstChild(parent
)[0]
682 for i
in range(index
- 1): nextItem
= tree
.GetNextSibling(nextItem
)
683 selected
= tree
.InsertNode(parent
, tree
.GetPyData(parent
).treeObject(), elem
, nextItem
)
684 newIndex
= tree
.ItemIndex(selected
)
685 tree
.SelectItem(selected
)
687 undoMan
.RegisterUndo(UndoMove(parent
, index
, parent
, newIndex
))
690 self
.SetStatusText(status
)
694 def OnMoveDown(self
, evt
):
695 selected
= tree
.selection
696 if not selected
: return
698 index
= tree
.ItemIndex(selected
)
699 next
= tree
.GetNextSibling(selected
)
703 self
.lastOp
= 'MOVEDOWN'
704 status
= 'Moved after next sibling'
709 parent
= tree
.GetItemParent(selected
)
710 elem
= tree
.RemoveLeaf(selected
)
711 nextItem
= tree
.GetFirstChild(parent
)[0]
712 for i
in range(index
+ 1): nextItem
= tree
.GetNextSibling(nextItem
)
713 selected
= tree
.InsertNode(parent
, tree
.GetPyData(parent
).treeObject(), elem
, nextItem
)
714 newIndex
= tree
.ItemIndex(selected
)
715 tree
.SelectItem(selected
)
717 undoMan
.RegisterUndo(UndoMove(parent
, index
, parent
, newIndex
))
720 self
.SetStatusText(status
)
724 def OnMoveLeft(self
, evt
):
725 selected
= tree
.selection
726 if not selected
: return
728 oldParent
= tree
.GetItemParent(selected
)
729 if not oldParent
: return
730 pparent
= tree
.GetItemParent(oldParent
)
731 if not pparent
: return
733 # Check compatibility
734 if not self
.ItemsAreCompatible(tree
.GetPyData(pparent
).treeObject(), tree
.GetPyData(selected
).treeObject()): return
737 self
.lastOp
= 'MOVELEFT'
738 status
= 'Made next sibling of parent'
740 oldIndex
= tree
.ItemIndex(selected
)
741 elem
= tree
.RemoveLeaf(selected
)
742 nextItem
= tree
.GetFirstChild(pparent
)[0]
743 parentIndex
= tree
.ItemIndex(oldParent
)
744 for i
in range(parentIndex
+ 1): nextItem
= tree
.GetNextSibling(nextItem
)
746 # Check parent and child relationships.
747 # If parent is sizer or notebook, child is of wrong class or
748 # parent is normal window, child is child container then detach child.
749 parent
= tree
.GetPyData(pparent
).treeObject()
750 xxx
= MakeXXXFromDOM(parent
, elem
)
751 isChildContainer
= isinstance(xxx
, xxxChildContainer
)
752 if isChildContainer
and \
753 ((parent
.isSizer
and not isinstance(xxx
, xxxSizerItem
)) or \
754 (isinstance(parent
, xxxNotebook
) and not isinstance(xxx
, xxxNotebookPage
)) or \
755 not (parent
.isSizer
or isinstance(parent
, xxxNotebook
))):
756 elem
.removeChild(xxx
.child
.node
) # detach child
757 elem
.unlink() # delete child container
758 elem
= xxx
.child
.node
# replace
759 # This may help garbage collection
760 xxx
.child
.parent
= None
761 isChildContainer
= False
762 # Parent is sizer or notebook, child is not child container
763 if parent
.isSizer
and not isChildContainer
and not isinstance(xxx
, xxxSpacer
):
764 # Create sizer item element
765 sizerItemElem
= MakeEmptyDOM('sizeritem')
766 sizerItemElem
.appendChild(elem
)
768 elif isinstance(parent
, xxxNotebook
) and not isChildContainer
:
769 pageElem
= MakeEmptyDOM('notebookpage')
770 pageElem
.appendChild(elem
)
773 selected
= tree
.InsertNode(pparent
, tree
.GetPyData(pparent
).treeObject(), elem
, nextItem
)
774 newIndex
= tree
.ItemIndex(selected
)
775 tree
.SelectItem(selected
)
777 undoMan
.RegisterUndo(UndoMove(oldParent
, oldIndex
, pparent
, newIndex
))
780 self
.SetStatusText(status
)
782 def OnMoveRight(self
, evt
):
783 selected
= tree
.selection
784 if not selected
: return
786 oldParent
= tree
.GetItemParent(selected
)
787 if not oldParent
: return
789 newParent
= tree
.GetPrevSibling(selected
)
790 if not newParent
: return
792 parent
= tree
.GetPyData(newParent
).treeObject()
794 # Check compatibility
795 if not self
.ItemsAreCompatible(parent
, tree
.GetPyData(selected
).treeObject()): return
798 self
.lastOp
= 'MOVERIGHT'
799 status
= 'Made last child of previous sibling'
801 oldIndex
= tree
.ItemIndex(selected
)
802 elem
= tree
.RemoveLeaf(selected
)
804 # Check parent and child relationships.
805 # If parent is sizer or notebook, child is of wrong class or
806 # parent is normal window, child is child container then detach child.
807 xxx
= MakeXXXFromDOM(parent
, elem
)
808 isChildContainer
= isinstance(xxx
, xxxChildContainer
)
809 if isChildContainer
and \
810 ((parent
.isSizer
and not isinstance(xxx
, xxxSizerItem
)) or \
811 (isinstance(parent
, xxxNotebook
) and not isinstance(xxx
, xxxNotebookPage
)) or \
812 not (parent
.isSizer
or isinstance(parent
, xxxNotebook
))):
813 elem
.removeChild(xxx
.child
.node
) # detach child
814 elem
.unlink() # delete child container
815 elem
= xxx
.child
.node
# replace
816 # This may help garbage collection
817 xxx
.child
.parent
= None
818 isChildContainer
= False
819 # Parent is sizer or notebook, child is not child container
820 if parent
.isSizer
and not isChildContainer
and not isinstance(xxx
, xxxSpacer
):
821 # Create sizer item element
822 sizerItemElem
= MakeEmptyDOM('sizeritem')
823 sizerItemElem
.appendChild(elem
)
825 elif isinstance(parent
, xxxNotebook
) and not isChildContainer
:
826 pageElem
= MakeEmptyDOM('notebookpage')
827 pageElem
.appendChild(elem
)
830 selected
= tree
.InsertNode(newParent
, tree
.GetPyData(newParent
).treeObject(), elem
, wx
.TreeItemId())
832 newIndex
= tree
.ItemIndex(selected
)
833 tree
.SelectItem(selected
)
835 undoMan
.RegisterUndo(UndoMove(oldParent
, oldIndex
, newParent
, newIndex
))
838 self
.SetStatusText(status
)
841 def OnCutDelete(self
, evt
):
842 selected
= tree
.selection
843 if not selected
: return # key pressed event
845 if evt
.GetId() == wx
.ID_CUT
:
847 status
= 'Removed to clipboard'
849 self
.lastOp
= 'DELETE'
853 # If deleting top-level item, delete testWin
854 if selected
== g
.testWin
.item
:
858 # Remove highlight, update testWin
859 if g
.testWin
.highLight
:
860 g
.testWin
.highLight
.Remove()
861 tree
.needUpdate
= True
864 index
= tree
.ItemFullIndex(selected
)
865 xxx
= tree
.GetPyData(selected
)
866 parent
= tree
.GetPyData(tree
.GetItemParent(selected
)).treeObject()
867 elem
= tree
.RemoveLeaf(selected
)
868 undoMan
.RegisterUndo(UndoCutDelete(index
, parent
, elem
))
869 if evt
.GetId() == wx
.ID_CUT
:
870 if wx
.TheClipboard
.Open():
872 data
= wx
.CustomDataObject('XRCED')
874 s
= elem
.toxml(encoding
=expat
.native_encoding
)
876 data
= wx
.CustomDataObject('XRCED_node')
878 data
.SetData(cPickle
.dumps(s
))
879 wx
.TheClipboard
.SetData(data
)
880 wx
.TheClipboard
.Close()
882 wx
.MessageBox("Unable to open the clipboard", "Error")
883 tree
.pendingHighLight
= None
885 tree
.selection
= None
890 self
.SetStatusText(status
)
892 def OnSubclass(self
, evt
):
893 selected
= tree
.selection
894 xxx
= tree
.GetPyData(selected
).treeObject()
896 subclass
= xxx
.subclass
897 dlg
= wx
.TextEntryDialog(self
, 'Subclass:', defaultValue
=subclass
)
898 if dlg
.ShowModal() == wx
.ID_OK
:
899 subclass
= dlg
.GetValue()
901 elem
.setAttribute('subclass', subclass
)
902 elif elem
.hasAttribute('subclass'):
903 elem
.removeAttribute('subclass')
905 xxx
.subclass
= elem
.getAttribute('subclass')
906 tree
.SetItemText(selected
, xxx
.treeName())
907 panel
.pages
[0].box
.SetLabel(xxx
.panelName())
910 def OnEmbedPanel(self
, evt
):
911 conf
.embedPanel
= evt
.IsChecked()
913 # Remember last dimentions
914 conf
.panelX
, conf
.panelY
= self
.miniFrame
.GetPosition()
915 conf
.panelWidth
, conf
.panelHeight
= self
.miniFrame
.GetSize()
916 size
= self
.GetSize()
917 pos
= self
.GetPosition()
918 sizePanel
= panel
.GetSize()
919 panel
.Reparent(self
.splitter
)
920 self
.miniFrame
.GetSizer().Remove(panel
)
922 self
.SetDimensions(pos
.x
, pos
.y
, size
.width
+ sizePanel
.width
, size
.height
)
923 self
.splitter
.SplitVertically(tree
, panel
, conf
.sashPos
)
924 self
.miniFrame
.Show(False)
926 conf
.sashPos
= self
.splitter
.GetSashPosition()
927 pos
= self
.GetPosition()
928 size
= self
.GetSize()
929 sizePanel
= panel
.GetSize()
930 self
.splitter
.Unsplit(panel
)
931 sizer
= self
.miniFrame
.GetSizer()
932 panel
.Reparent(self
.miniFrame
)
934 sizer
.Add(panel
, 1, wx
.EXPAND
)
935 self
.miniFrame
.Show(True)
936 self
.miniFrame
.SetDimensions(conf
.panelX
, conf
.panelY
,
937 conf
.panelWidth
, conf
.panelHeight
)
938 self
.miniFrame
.Layout()
940 self
.SetDimensions(pos
.x
, pos
.y
,
941 max(size
.width
- sizePanel
.width
, self
.minWidth
), size
.height
)
943 def OnShowTools(self
, evt
):
944 conf
.showTools
= evt
.IsChecked()
945 g
.tools
.Show(conf
.showTools
)
947 self
.toolsSizer
.Prepend(g
.tools
, 0, wx
.EXPAND
)
949 self
.toolsSizer
.Remove(g
.tools
)
950 self
.toolsSizer
.Layout()
952 def OnTest(self
, evt
):
953 if not tree
.selection
: return # key pressed event
954 tree
.ShowTestWindow(tree
.selection
)
956 def OnTestHide(self
, evt
):
957 tree
.CloseTestWindow()
959 # Find object by relative position
960 def FindObject(self
, item
, obj
):
961 # We simply perform depth-first traversal, sinse it's too much
962 # hassle to deal with all sizer/window combinations
963 w
= tree
.FindNodeObject(item
)
964 if w
== obj
or isinstance(w
, wx
.GBSizerItem
) and w
.GetWindow() == obj
:
966 if tree
.ItemHasChildren(item
):
967 child
= tree
.GetFirstChild(item
)[0]
969 found
= self
.FindObject(child
, obj
)
970 if found
: return found
971 child
= tree
.GetNextSibling(child
)
974 def OnTestWinLeftDown(self
, evt
):
975 pos
= evt
.GetPosition()
976 self
.SetHandler(g
.testWin
)
977 g
.testWin
.Disconnect(wx
.ID_ANY
, wx
.ID_ANY
, wx
.wxEVT_LEFT_DOWN
)
978 item
= self
.FindObject(g
.testWin
.item
, evt
.GetEventObject())
980 tree
.EnsureVisible(item
)
981 tree
.SelectItem(item
)
982 self
.tb
.ToggleTool(self
.ID_TOOL_LOCATE
, False)
984 self
.SetStatusText('Selected %s' % tree
.GetItemText(item
))
986 self
.SetStatusText('Locate failed!')
988 def SetHandler(self
, w
, h
=None):
991 w
.SetCursor(wx
.CROSS_CURSOR
)
994 w
.SetCursor(wx
.NullCursor
)
995 for ch
in w
.GetChildren():
996 self
.SetHandler(ch
, h
)
998 def OnLocate(self
, evt
):
1000 if evt
.GetId() == self
.ID_LOCATE
or \
1001 evt
.GetId() == self
.ID_TOOL_LOCATE
and evt
.IsChecked():
1002 self
.SetHandler(g
.testWin
, g
.testWin
)
1003 g
.testWin
.Connect(wx
.ID_ANY
, wx
.ID_ANY
, wx
.wxEVT_LEFT_DOWN
, self
.OnTestWinLeftDown
)
1004 if evt
.GetId() == self
.ID_LOCATE
:
1005 self
.tb
.ToggleTool(self
.ID_TOOL_LOCATE
, True)
1006 elif evt
.GetId() == self
.ID_TOOL_LOCATE
and not evt
.IsChecked():
1007 self
.SetHandler(g
.testWin
, None)
1008 g
.testWin
.Disconnect(wx
.ID_ANY
, wx
.ID_ANY
, wx
.wxEVT_LEFT_DOWN
)
1009 self
.SetStatusText('Click somewhere in your test window now')
1011 def OnRefresh(self
, evt
):
1012 # If modified, apply first
1013 selection
= tree
.selection
1015 xxx
= tree
.GetPyData(selection
)
1016 if xxx
and panel
.IsModified():
1017 tree
.Apply(xxx
, selection
)
1020 tree
.CreateTestWin(g
.testWin
.item
)
1021 panel
.modified
= False
1022 tree
.needUpdate
= False
1024 def OnAutoRefresh(self
, evt
):
1025 conf
.autoRefresh
= evt
.IsChecked()
1026 self
.menuBar
.Check(self
.ID_AUTO_REFRESH
, conf
.autoRefresh
)
1027 self
.tb
.ToggleTool(self
.ID_AUTO_REFRESH
, conf
.autoRefresh
)
1029 def OnAbout(self
, evt
):
1033 (c) Roman Rolinsky <rollrom@users.sourceforge.net>
1034 Homepage: http://xrced.sourceforge.net\
1036 dlg
= wx
.MessageDialog(self
, str, 'About XRCed', wx
.OK | wx
.CENTRE
)
1040 def OnReadme(self
, evt
):
1041 text
= open(os
.path
.join(basePath
, 'README.txt'), 'r').read()
1042 dlg
= ScrolledMessageDialog(self
, text
, "XRCed README")
1046 # Simple emulation of python command line
1047 def OnDebugCMD(self
, evt
):
1050 exec raw_input('C:\> ')
1055 (etype
, value
, tb
) =sys
.exc_info()
1056 tblist
=traceback
.extract_tb(tb
)[1:]
1057 msg
=' '.join(traceback
.format_exception_only(etype
, value
)
1058 +traceback
.format_list(tblist
))
1061 def OnCreate(self
, evt
):
1062 selected
= tree
.selection
1063 if tree
.ctrl
: appendChild
= False
1064 else: appendChild
= not tree
.NeedInsert(selected
)
1065 xxx
= tree
.GetPyData(selected
)
1069 # If has previous item, insert after it, else append to parent
1071 parentLeaf
= tree
.GetItemParent(selected
)
1073 # If has next item, insert, else append to parent
1074 nextItem
= tree
.GetNextSibling(selected
)
1075 parentLeaf
= tree
.GetItemParent(selected
)
1076 # Expanded container (must have children)
1077 elif tree
.shift
and tree
.IsExpanded(selected
) \
1078 and tree
.GetChildrenCount(selected
, False):
1079 nextItem
= tree
.GetFirstChild(selected
)[0]
1080 parentLeaf
= selected
1082 nextItem
= wx
.TreeItemId()
1083 parentLeaf
= selected
1084 parent
= tree
.GetPyData(parentLeaf
)
1085 if parent
.hasChild
: parent
= parent
.child
1087 # Create object_ref?
1088 if evt
.GetId() == ID_NEW
.REF
:
1089 ref
= wx
.GetTextFromUser('Create reference to:', 'Create reference')
1091 xxx
= MakeEmptyRefXXX(parent
, ref
)
1092 elif evt
.GetId() == ID_NEW
.COMMENT
:
1093 xxx
= MakeEmptyCommentXXX(parent
)
1095 # Create empty element
1096 if evt
.GetId() >= ID_NEW
.CUSTOM
:
1097 className
= pullDownMenu
.customMap
[evt
.GetId()]
1099 className
= pullDownMenu
.createMap
[evt
.GetId()]
1100 xxx
= MakeEmptyXXX(parent
, className
)
1102 # Insert new node, register undo
1103 if xxx
.isElement
: # true object
1104 # Set default name for top-level windows
1105 if parent
.__class
__ == xxxMainNode
:
1106 cl
= xxx
.treeObject().__class
__
1107 frame
.maxIDs
[cl
] += 1
1108 xxx
.setTreeName('%s%d' % (defaultIDs
[cl
], frame
.maxIDs
[cl
]))
1109 # And for some other standard controls
1110 elif parent
.__class
__ == xxxStdDialogButtonSizer
:
1111 xxx
.setTreeName(pullDownMenu
.stdButtonIDs
[evt
.GetId()][0])
1112 # We can even set label
1113 obj
= xxx
.treeObject()
1114 elem
= g
.tree
.dom
.createElement('label')
1115 elem
.appendChild(g
.tree
.dom
.createTextNode(pullDownMenu
.stdButtonIDs
[evt
.GetId()][1]))
1116 obj
.params
['label'] = xxxParam(elem
)
1117 xxx
.treeObject().node
.appendChild(elem
)
1119 newItem
= tree
.InsertNode(parentLeaf
, parent
, xxx
.node
, nextItem
)
1120 else: # comment node
1121 newItem
= tree
.InsertNode(parentLeaf
, parent
, xxx
.node
, nextItem
)
1122 undoMan
.RegisterUndo(UndoPasteCreate(parentLeaf
, parent
, newItem
, selected
))
1123 tree
.EnsureVisible(newItem
)
1124 tree
.SelectItem(newItem
)
1125 if not tree
.IsVisible(newItem
):
1126 tree
.ScrollTo(newItem
)
1129 if xxx
.isElement
and g
.testWin
and tree
.IsHighlatable(newItem
):
1130 if conf
.autoRefresh
:
1131 tree
.needUpdate
= True
1132 tree
.pendingHighLight
= newItem
1134 tree
.pendingHighLight
= None
1136 if not xxx
.isElement
:
1137 tree
.EditLabel(newItem
)
1140 # Replace one object with another
1141 def OnReplace(self
, evt
):
1142 selected
= tree
.selection
1143 xxx
= tree
.GetPyData(selected
).treeObject()
1145 parent
= elem
.parentNode
1146 undoMan
.RegisterUndo(UndoReplace(selected
))
1148 className
= pullDownMenu
.createMap
[evt
.GetId() - 1000]
1150 # Create temporary empty node (with default values)
1151 dummy
= MakeEmptyDOM(className
)
1152 if className
== 'spacer' and xxx
.className
!= 'spacer':
1154 elif xxx
.className
== 'spacer' and className
!= 'spacer':
1155 klass
= xxxSizerItem
1157 klass
= xxxDict
[className
]
1158 # Remove non-compatible children
1159 if tree
.ItemHasChildren(selected
) and not klass
.hasChildren
:
1160 tree
.DeleteChildren(selected
)
1161 nodes
= elem
.childNodes
[:]
1164 if node
.nodeType
!= minidom
.Node
.ELEMENT_NODE
: continue
1168 if not klass
.hasChildren
: remove
= True
1169 elif tag
not in klass
.allParams
and \
1170 (not klass
.hasStyle
or tag
not in klass
.styles
):
1175 elem
.removeChild(node
)
1178 # Remove sizeritem child if spacer
1179 if className
== 'spacer' and xxx
.className
!= 'spacer':
1180 sizeritem
= elem
.parentNode
1181 assert sizeritem
.getAttribute('class') == 'sizeritem'
1182 sizeritem
.removeChild(elem
)
1185 tree
.GetPyData(selected
).hasChild
= False
1186 elif xxx
.className
== 'spacer' and className
!= 'spacer':
1187 # Create sizeritem element
1188 assert xxx
.parent
.isSizer
1189 elem
.setAttribute('class', 'sizeritem')
1190 node
= MakeEmptyDOM(className
)
1191 elem
.appendChild(node
)
1192 # Replace to point to new object
1193 xxx
= xxxSizerItem(xxx
.parent
, elem
)
1195 tree
.SetPyData(selected
, xxx
)
1198 # Copy parameters present in dummy but not in elem
1199 for node
in dummy
.childNodes
:
1200 if node
.tagName
not in tags
: elem
.appendChild(node
.cloneNode(True))
1204 elem
.setAttribute('class', className
)
1205 if elem
.hasAttribute('subclass'):
1206 elem
.removeAttribute('subclass') # clear subclassing
1207 # Re-create xxx element
1208 xxx
= MakeXXXFromDOM(xxx
.parent
, elem
)
1209 # Remove incompatible style flags
1210 if 'style' in xxx
.params
:
1211 styles
= map(string
.strip
, xxx
.params
['style'].value().split('|'))
1212 newStyles
= [s
for s
in styles
if s
in klass
.winStyles
or s
in genericStyles
]
1213 if newStyles
!= styles
:
1215 value
= reduce(lambda a
,b
: a
+'|'+b
, newStyles
)
1218 xxx
.params
['style'].update(value
)
1220 # Update parent in child objects
1221 if tree
.ItemHasChildren(selected
):
1222 i
, cookie
= tree
.GetFirstChild(selected
)
1224 x
= tree
.GetPyData(i
)
1226 if x
.hasChild
: x
.child
.parent
= xxx
1227 i
, cookie
= tree
.GetNextChild(selected
, cookie
)
1230 if tree
.GetPyData(selected
).hasChild
: # child container
1231 container
= tree
.GetPyData(selected
)
1232 container
.resetChild(xxx
)
1235 tree
.SetPyData(selected
, xxx
)
1236 tree
.SetItemText(selected
, xxx
.treeName())
1237 tree
.SetItemImage(selected
, xxx
.treeImage())
1239 # Set default name for top-level windows
1240 if parent
.__class
__ == xxxMainNode
:
1241 cl
= xxx
.treeObject().__class
__
1242 frame
.maxIDs
[cl
] += 1
1243 xxx
.setTreeName('%s%d' % (defaultIDs
[cl
], frame
.maxIDs
[cl
]))
1246 g
.panel
.SetData(xxx
)
1250 #undoMan.RegisterUndo(UndoPasteCreate(parentLeaf, parent, newItem, selected))
1252 if g
.testWin
and tree
.IsHighlatable(selected
):
1253 if conf
.autoRefresh
:
1254 tree
.needUpdate
= True
1255 tree
.pendingHighLight
= selected
1257 tree
.pendingHighLight
= None
1261 # Expand/collapse subtree
1262 def OnExpand(self
, evt
):
1263 if tree
.selection
: tree
.ExpandAll(tree
.selection
)
1264 else: tree
.ExpandAll(tree
.root
)
1265 def OnCollapse(self
, evt
):
1266 if tree
.selection
: tree
.CollapseAll(tree
.selection
)
1267 else: tree
.CollapseAll(tree
.root
)
1269 def OnPullDownHighlight(self
, evt
):
1270 menuId
= evt
.GetMenuId()
1272 menu
= evt
.GetEventObject()
1273 help = menu
.GetHelpString(menuId
)
1274 self
.SetStatusText(help)
1276 self
.SetStatusText('')
1278 def OnUpdateUI(self
, evt
):
1279 if evt
.GetId() in [wx
.ID_CUT
, wx
.ID_COPY
, self
.ID_DELETE
]:
1280 evt
.Enable(tree
.selection
is not None and tree
.selection
!= tree
.root
)
1281 elif evt
.GetId() == wx
.ID_SAVE
:
1282 evt
.Enable(self
.modified
)
1283 elif evt
.GetId() in [wx
.ID_PASTE
, self
.ID_TOOL_PASTE
]:
1284 evt
.Enable(tree
.selection
is not None)
1285 elif evt
.GetId() == self
.ID_TEST
:
1286 evt
.Enable(tree
.selection
is not None and tree
.selection
!= tree
.root
)
1287 elif evt
.GetId() in [self
.ID_LOCATE
, self
.ID_TOOL_LOCATE
]:
1288 evt
.Enable(g
.testWin
is not None)
1289 elif evt
.GetId() == wx
.ID_UNDO
: evt
.Enable(undoMan
.CanUndo())
1290 elif evt
.GetId() == wx
.ID_REDO
: evt
.Enable(undoMan
.CanRedo())
1292 def OnIdle(self
, evt
):
1293 if self
.inIdle
: return # Recursive call protection
1297 if conf
.autoRefresh
:
1299 self
.SetStatusText('Refreshing test window...')
1301 tree
.CreateTestWin(g
.testWin
.item
)
1302 self
.SetStatusText('')
1303 tree
.needUpdate
= False
1304 elif tree
.pendingHighLight
:
1306 tree
.HighLight(tree
.pendingHighLight
)
1308 # Remove highlight if any problem
1309 if g
.testWin
.highLight
:
1310 g
.testWin
.highLight
.Remove()
1311 tree
.pendingHighLight
= None
1318 # We don't let close panel window
1319 def OnCloseMiniFrame(self
, evt
):
1322 def OnIconize(self
, evt
):
1324 conf
.x
, conf
.y
= self
.GetPosition()
1325 conf
.width
, conf
.height
= self
.GetSize()
1327 conf
.sashPos
= self
.splitter
.GetSashPosition()
1329 conf
.panelX
, conf
.panelY
= self
.miniFrame
.GetPosition()
1330 conf
.panelWidth
, conf
.panelHeight
= self
.miniFrame
.GetSize()
1331 self
.miniFrame
.Iconize()
1333 if not conf
.embedPanel
:
1334 self
.miniFrame
.Iconize(False)
1337 def OnCloseWindow(self
, evt
):
1338 if not self
.AskSave(): return
1339 if g
.testWin
: g
.testWin
.Destroy()
1340 if not panel
.GetPageCount() == 2:
1341 panel
.page2
.Destroy()
1343 # If we don't do this, page does not get destroyed (a bug?)
1345 if not self
.IsIconized():
1346 conf
.x
, conf
.y
= self
.GetPosition()
1347 conf
.width
, conf
.height
= self
.GetSize()
1349 conf
.sashPos
= self
.splitter
.GetSashPosition()
1351 conf
.panelX
, conf
.panelY
= self
.miniFrame
.GetPosition()
1352 conf
.panelWidth
, conf
.panelHeight
= self
.miniFrame
.GetSize()
1355 def CreateLocalConf(self
, path
):
1356 name
= os
.path
.splitext(path
)[0]
1358 return wx
.FileConfig(localFilename
=name
)
1362 conf
.localconf
= None
1364 self
.SetModified(False)
1370 # Numbers for new controls
1372 for cl
in [xxxPanel
, xxxDialog
, xxxFrame
,
1373 xxxMenuBar
, xxxMenu
, xxxToolBar
,
1374 xxxWizard
, xxxBitmap
, xxxIcon
]:
1376 # Restore handlers, menu, etc. to initial
1377 setHandlers(self
.handlers
[:])
1378 g
.pullDownMenu
.custom
= self
.custom
[:]
1379 # Remove modules imported from comment directives
1380 map(sys
.modules
.pop
, [m
for m
in sys
.modules
if m
not in self
.modules
])
1382 def SetModified(self
, state
=True):
1383 self
.modified
= state
1384 name
= os
.path
.basename(self
.dataFile
)
1385 if not name
: name
= defaultName
1387 self
.SetTitle(progname
+ ': ' + name
+ ' *')
1389 self
.SetTitle(progname
+ ': ' + name
)
1391 def Open(self
, path
):
1392 if not os
.path
.exists(path
):
1393 wx
.LogError('File does not exists: %s' % path
)
1395 # Try to read the file
1399 dom
= minidom
.parse(f
)
1401 # Set encoding global variable and default encoding
1403 g
.currentEncoding
= dom
.encoding
1404 wx
.SetDefaultPyEncoding(g
.currentEncoding
.encode())
1406 g
.currentEncoding
= ''
1408 self
.dataFile
= path
= os
.path
.abspath(path
)
1409 dir = os
.path
.dirname(path
)
1410 if dir: os
.chdir(dir)
1411 # Allow importing modules from the same directory
1412 sys
.path
= sys_path
+ [dir]
1414 self
.SetTitle(progname
+ ': ' + os
.path
.basename(path
))
1415 conf
.localconf
= self
.CreateLocalConf(self
.dataFile
)
1417 # Nice exception printing
1418 inf
= sys
.exc_info()
1419 wx
.LogError(traceback
.format_exception(inf
[0], inf
[1], None)[-1])
1420 wx
.LogError('Error reading file: %s' % path
)
1425 def Indent(self
, node
, indent
= 0):
1426 if node
.nodeType
== minidom
.Node
.COMMENT_NODE
:
1427 text
= self
.domCopy
.createTextNode('\n' + ' ' * indent
)
1428 node
.parentNode
.insertBefore(text
, node
)
1429 return # no children
1430 # Copy child list because it will change soon
1431 children
= node
.childNodes
[:]
1432 # Main node doesn't need to be indented
1434 text
= self
.domCopy
.createTextNode('\n' + ' ' * indent
)
1435 node
.parentNode
.insertBefore(text
, node
)
1437 # Append newline after last child, except for text nodes
1438 if children
[-1].nodeType
== minidom
.Node
.ELEMENT_NODE
:
1439 text
= self
.domCopy
.createTextNode('\n' + ' ' * indent
)
1440 node
.appendChild(text
)
1441 # Indent children which are elements
1443 if n
.nodeType
== minidom
.Node
.ELEMENT_NODE
or \
1444 n
.nodeType
== minidom
.Node
.COMMENT_NODE
:
1445 self
.Indent(n
, indent
+ 2)
1447 def Save(self
, path
):
1451 if tree
.selection
and panel
.IsModified():
1452 self
.OnRefresh(wx
.CommandEvent())
1453 if g
.currentEncoding
:
1454 f
= codecs
.open(path
, 'wt', g
.currentEncoding
)
1456 f
= codecs
.open(path
, 'wt')
1457 # Make temporary copy for formatting it
1458 # !!! We can't clone dom node, it works only once
1459 #self.domCopy = tree.dom.cloneNode(True)
1460 self
.domCopy
= MyDocument()
1461 mainNode
= self
.domCopy
.appendChild(tree
.mainNode
.cloneNode(True))
1462 # Remove first child (test element)
1463 testElem
= mainNode
.firstChild
1464 mainNode
.removeChild(testElem
)
1466 self
.Indent(mainNode
)
1467 self
.domCopy
.writexml(f
, encoding
= g
.currentEncoding
)
1469 self
.domCopy
.unlink()
1471 self
.SetModified(False)
1472 panel
.SetModified(False)
1473 conf
.localconf
.Flush()
1475 inf
= sys
.exc_info()
1476 wx
.LogError(traceback
.format_exception(inf
[0], inf
[1], None)[-1])
1477 wx
.LogError('Error writing file: %s' % path
)
1481 if not (self
.modified
or panel
.IsModified()): return True
1482 flags
= wx
.ICON_EXCLAMATION | wx
.YES_NO | wx
.CANCEL | wx
.CENTRE
1483 dlg
= wx
.MessageDialog( self
, 'File is modified. Save before exit?',
1484 'Save before too late?', flags
)
1485 say
= dlg
.ShowModal()
1488 if say
== wx
.ID_YES
:
1489 self
.OnSaveOrSaveAs(wx
.CommandEvent(wx
.ID_SAVE
))
1490 # If save was successful, modified flag is unset
1491 if not self
.modified
: return True
1492 elif say
== wx
.ID_NO
:
1493 self
.SetModified(False)
1494 panel
.SetModified(False)
1501 ################################################################################
1503 class PythonOptions(wx
.Dialog
):
1505 def __init__(self
, parent
, cfg
, dataFile
):
1506 pre
= wx
.PreDialog()
1507 g
.frame
.res
.LoadOnDialog(pre
, parent
, "PYTHON_OPTIONS")
1508 self
.PostCreate(pre
)
1511 self
.dataFile
= dataFile
1513 self
.AutoGenerateCB
= xrc
.XRCCTRL(self
, "AutoGenerateCB")
1514 self
.EmbedCB
= xrc
.XRCCTRL(self
, "EmbedCB")
1515 self
.GettextCB
= xrc
.XRCCTRL(self
, "GettextCB")
1516 self
.MakeXRSFileCB
= xrc
.XRCCTRL(self
, "MakeXRSFileCB")
1517 self
.FileNameTC
= xrc
.XRCCTRL(self
, "FileNameTC")
1518 self
.BrowseBtn
= xrc
.XRCCTRL(self
, "BrowseBtn")
1519 self
.GenerateBtn
= xrc
.XRCCTRL(self
, "GenerateBtn")
1520 self
.SaveOptsBtn
= xrc
.XRCCTRL(self
, "SaveOptsBtn")
1522 self
.Bind(wx
.EVT_BUTTON
, self
.OnBrowse
, self
.BrowseBtn
)
1523 self
.Bind(wx
.EVT_BUTTON
, self
.OnGenerate
, self
.GenerateBtn
)
1524 self
.Bind(wx
.EVT_BUTTON
, self
.OnSaveOpts
, self
.SaveOptsBtn
)
1526 if self
.cfg
.Read("filename", "") != "":
1527 self
.FileNameTC
.SetValue(self
.cfg
.Read("filename"))
1529 name
= os
.path
.splitext(os
.path
.split(dataFile
)[1])[0]
1531 self
.FileNameTC
.SetValue(name
)
1532 self
.AutoGenerateCB
.SetValue(self
.cfg
.ReadBool("autogenerate", False))
1533 self
.EmbedCB
.SetValue(self
.cfg
.ReadBool("embedResource", False))
1534 self
.MakeXRSFileCB
.SetValue(self
.cfg
.ReadBool("makeXRS", False))
1535 self
.GettextCB
.SetValue(self
.cfg
.ReadBool("genGettext", False))
1538 def OnBrowse(self
, evt
):
1539 path
= self
.FileNameTC
.GetValue()
1540 dirname
= os
.path
.abspath(os
.path
.dirname(path
))
1541 name
= os
.path
.split(path
)[1]
1542 dlg
= wx
.FileDialog(self
, 'Save As', dirname
, name
, '*.py',
1543 wx
.SAVE | wx
.OVERWRITE_PROMPT
)
1544 if dlg
.ShowModal() == wx
.ID_OK
:
1545 path
= dlg
.GetPath()
1546 self
.FileNameTC
.SetValue(path
)
1550 def OnGenerate(self
, evt
):
1551 pypath
= self
.FileNameTC
.GetValue()
1552 embed
= self
.EmbedCB
.GetValue()
1553 genGettext
= self
.GettextCB
.GetValue()
1554 frame
.GeneratePython(self
.dataFile
, pypath
, embed
, genGettext
)
1558 def OnSaveOpts(self
, evt
=None):
1559 self
.cfg
.Write("filename", self
.FileNameTC
.GetValue())
1560 self
.cfg
.WriteBool("autogenerate", self
.AutoGenerateCB
.GetValue())
1561 self
.cfg
.WriteBool("embedResource", self
.EmbedCB
.GetValue())
1562 self
.cfg
.WriteBool("makeXRS", self
.MakeXRSFileCB
.GetValue())
1563 self
.cfg
.WriteBool("genGettext", self
.GettextCB
.GetValue())
1565 self
.EndModal(wx
.ID_OK
)
1568 ################################################################################
1571 print >> sys
.stderr
, 'usage: xrced [-dhiv] [file]'
1576 if wx
.VERSION
[:3] < MinWxVersion
:
1578 This version of XRCed may not work correctly on your version of wxWidgets. \
1579 Please upgrade wxWidgets to %d.%d.%d or higher.''' % MinWxVersion
)
1581 # Process comand-line
1584 opts
, args
= getopt
.getopt(sys
.argv
[1:], 'dhiv')
1592 print 'XRCed version', version
1595 except getopt
.GetoptError
:
1596 if wx
.Platform
!= '__WXMAC__': # macs have some extra parameters
1597 print >> sys
.stderr
, 'Unknown option'
1601 self
.SetAppName('xrced')
1604 conf
= g
.conf
= wx
.Config(style
= wx
.CONFIG_USE_LOCAL_FILE
)
1605 conf
.localconf
= None
1606 conf
.autoRefresh
= conf
.ReadInt('autorefresh', True)
1607 pos
= conf
.ReadInt('x', -1), conf
.ReadInt('y', -1)
1608 size
= conf
.ReadInt('width', 800), conf
.ReadInt('height', 600)
1609 conf
.embedPanel
= conf
.ReadInt('embedPanel', True)
1610 conf
.showTools
= conf
.ReadInt('showTools', True)
1611 conf
.sashPos
= conf
.ReadInt('sashPos', 200)
1612 # read recently used files
1613 recentfiles
=conf
.Read('recentFiles','')
1616 for fil
in recentfiles
.split('|'):
1617 conf
.recentfiles
[wx
.NewId()]=fil
1618 if not conf
.embedPanel
:
1619 conf
.panelX
= conf
.ReadInt('panelX', -1)
1620 conf
.panelY
= conf
.ReadInt('panelY', -1)
1622 conf
.panelX
= conf
.panelY
= -1
1623 conf
.panelWidth
= conf
.ReadInt('panelWidth', 200)
1624 conf
.panelHeight
= conf
.ReadInt('panelHeight', 200)
1625 conf
.panic
= not conf
.HasEntry('nopanic')
1627 wx
.FileSystem
.AddHandler(wx
.MemoryFSHandler())
1629 frame
= Frame(pos
, size
)
1633 plugins
= os
.getenv('XRCEDPATH')
1637 for dir in plugins
.split(':'):
1638 if os
.path
.isdir(dir):
1640 dir = os
.path
.abspath(os
.path
.normpath(dir))
1641 sys
.path
= sys_path
+ [os
.path
.dirname(dir)]
1644 __import__(os
.path
.basename(dir), globals(), locals(), ['*'])
1646 print traceback
.print_exc()
1649 # Store important data
1650 frame
.handlers
= getHandlers()[:]
1651 frame
.custom
= g
.pullDownMenu
.custom
[:]
1652 frame
.modules
= set(sys
.modules
.keys())
1657 # Load file after showing
1660 frame
.open = frame
.Open(args
[0])
1668 wc
.WriteInt('autorefresh', conf
.autoRefresh
)
1669 wc
.WriteInt('x', conf
.x
)
1670 wc
.WriteInt('y', conf
.y
)
1671 wc
.WriteInt('width', conf
.width
)
1672 wc
.WriteInt('height', conf
.height
)
1673 wc
.WriteInt('embedPanel', conf
.embedPanel
)
1674 wc
.WriteInt('showTools', conf
.showTools
)
1675 if not conf
.embedPanel
:
1676 wc
.WriteInt('panelX', conf
.panelX
)
1677 wc
.WriteInt('panelY', conf
.panelY
)
1678 wc
.WriteInt('sashPos', conf
.sashPos
)
1679 wc
.WriteInt('panelWidth', conf
.panelWidth
)
1680 wc
.WriteInt('panelHeight', conf
.panelHeight
)
1681 wc
.WriteInt('nopanic', True)
1682 wc
.Write('recentFiles', '|'.join(conf
.recentfiles
.values()[-5:]))
1686 app
= App(0, useBestVisual
=False)
1687 #app.SetAssertMode(wx.PYAPP_ASSERT_LOG)
1693 if __name__
== '__main__':