]>
git.saurik.com Git - wxWidgets.git/blob - wxPython/wx/tools/XRCed/xrced.py
2 # Purpose: XRC editor, main module
3 # Author: Roman Rolinsky <rolinsky@mema.ucl.ac.be>
9 xrced -- Simple resource editor for XRC format used by wxWidgets/wxPython
14 xrced [ -h ] [ -v ] [ XRC-file ]
18 -h output short usage info and exit
20 -v output version info and exit
24 import os
, sys
, getopt
, re
, traceback
, tempfile
, shutil
, cPickle
25 from xml
.parsers
import expat
28 from tree
import * # imports xxx which imports params
31 from params
import genericStyles
32 # Cleanup recursive import sideeffects, otherwise we can't create undoMan
34 undo
.ParamPage
= ParamPage
35 undoMan
= g
.undoMan
= UndoManager()
37 # Set application path for loading resources
38 if __name__
== '__main__':
39 basePath
= os
.path
.dirname(sys
.argv
[0])
41 basePath
= os
.path
.dirname(__file__
)
43 # Remember system path
46 # 1 adds CMD command to Help menu
50 <HTML><H2>Welcome to XRC<font color="blue">ed</font></H2><H3><font color="green">DON'T PANIC :)</font></H3>
51 Read this note before clicking on anything!<P>
52 To start select tree root, then popup menu with your right mouse button,
53 select "Append Child", and then any command.<P>
54 Or just press one of the buttons on the tools palette.<P>
55 Enter XML ID, change properties, create children.<P>
56 To test your interface select Test command (View menu).<P>
57 Consult README.txt file for the details.</HTML>
60 defaultIDs
= {xxxPanel
:'PANEL', xxxDialog
:'DIALOG', xxxFrame
:'FRAME',
61 xxxMenuBar
:'MENUBAR', xxxMenu
:'MENU', xxxToolBar
:'TOOLBAR',
62 xxxWizard
:'WIZARD', xxxBitmap
:'BITMAP', xxxIcon
:'ICON'}
64 defaultName
= 'UNTITLED.xrc'
66 ################################################################################
68 # ScrolledMessageDialog - modified from wxPython lib to set fixed-width font
69 class ScrolledMessageDialog(wx
.Dialog
):
70 def __init__(self
, parent
, msg
, caption
, pos
= wx
.DefaultPosition
, size
= (500,300)):
71 from wx
.lib
.layoutf
import Layoutf
72 wx
.Dialog
.__init
__(self
, parent
, -1, caption
, pos
, size
)
73 text
= wx
.TextCtrl(self
, -1, msg
, wx
.DefaultPosition
,
74 wx
.DefaultSize
, wx
.TE_MULTILINE | wx
.TE_READONLY
)
75 text
.SetFont(g
.modernFont())
76 dc
= wx
.WindowDC(text
)
77 w
, h
= dc
.GetFullTextExtent(' ', g
.modernFont())[:2]
78 ok
= wx
.Button(self
, wx
.ID_OK
, "OK")
80 text
.SetConstraints(Layoutf('t=t5#1;b=t5#2;l=l5#1;r=r5#1', (self
,ok
)))
81 text
.SetSize((w
* 80 + 30, h
* 40))
82 text
.ShowPosition(1) # scroll to the first line
83 ok
.SetConstraints(Layoutf('b=b5#1;x%w50#1;w!80;h!35', (self
,)))
84 self
.SetAutoLayout(True)
86 self
.CenterOnScreen(wx
.BOTH
)
88 ################################################################################
90 # Event handler for using during location
91 class Locator(wx
.EvtHandler
):
92 def ProcessEvent(self
, evt
):
95 class Frame(wx
.Frame
):
96 def __init__(self
, pos
, size
):
97 wx
.Frame
.__init
__(self
, None, -1, '', pos
, size
)
99 frame
= g
.frame
= self
100 bar
= self
.CreateStatusBar(2)
101 bar
.SetStatusWidths([-1, 40])
102 self
.SetIcon(images
.getIconIcon())
107 # Load our own resources
108 self
.res
= xrc
.XmlResource('')
109 # !!! Blocking of assert failure occurring in older unicode builds
111 quietlog
= wx
.LogNull()
112 self
.res
.Load(os
.path
.join(basePath
, 'xrced.xrc'))
113 except wx
._core
.PyAssertionError
:
114 print 'PyAssertionError was ignored'
117 menuBar
= wx
.MenuBar()
120 menu
.Append(wx
.ID_NEW
, '&New\tCtrl-N', 'New file')
121 menu
.AppendSeparator()
122 menu
.Append(wx
.ID_OPEN
, '&Open...\tCtrl-O', 'Open XRC file')
123 self
.recentMenu
= wx
.Menu()
124 self
.AppendRecent(self
.recentMenu
)
125 menu
.AppendMenu(-1, 'Open Recent', self
.recentMenu
, 'Open a recent file')
126 menu
.AppendSeparator()
127 menu
.Append(wx
.ID_SAVE
, '&Save\tCtrl-S', 'Save XRC file')
128 menu
.Append(wx
.ID_SAVEAS
, 'Save &As...', 'Save XRC file under different name')
129 self
.ID_GENERATE_PYTHON
= wx
.NewId()
130 menu
.Append(self
.ID_GENERATE_PYTHON
, '&Generate Python...',
131 'Generate a Python module that uses this XRC')
132 menu
.AppendSeparator()
133 menu
.Append(wx
.ID_EXIT
, '&Quit\tCtrl-Q', 'Exit application')
135 menuBar
.Append(menu
, '&File')
138 menu
.Append(wx
.ID_UNDO
, '&Undo\tCtrl-Z', 'Undo')
139 menu
.Append(wx
.ID_REDO
, '&Redo\tCtrl-Y', 'Redo')
140 menu
.AppendSeparator()
141 menu
.Append(wx
.ID_CUT
, 'Cut\tCtrl-X', 'Cut to the clipboard')
142 menu
.Append(wx
.ID_COPY
, '&Copy\tCtrl-C', 'Copy to the clipboard')
143 menu
.Append(wx
.ID_PASTE
, '&Paste\tCtrl-V', 'Paste from the clipboard')
144 self
.ID_DELETE
= wx
.NewId()
145 menu
.Append(self
.ID_DELETE
, '&Delete\tCtrl-D', 'Delete object')
146 menu
.AppendSeparator()
147 self
.ID_LOCATE
= wx
.NewId()
148 self
.ID_TOOL_LOCATE
= wx
.NewId()
149 self
.ID_TOOL_PASTE
= wx
.NewId()
150 menu
.Append(self
.ID_LOCATE
, '&Locate\tCtrl-L', 'Locate control in test window and select it')
151 menuBar
.Append(menu
, '&Edit')
154 self
.ID_EMBED_PANEL
= wx
.NewId()
155 menu
.Append(self
.ID_EMBED_PANEL
, '&Embed Panel',
156 'Toggle embedding properties panel in the main window', True)
157 menu
.Check(self
.ID_EMBED_PANEL
, conf
.embedPanel
)
158 self
.ID_SHOW_TOOLS
= wx
.NewId()
159 menu
.Append(self
.ID_SHOW_TOOLS
, 'Show &Tools', 'Toggle tools', True)
160 menu
.Check(self
.ID_SHOW_TOOLS
, conf
.showTools
)
161 menu
.AppendSeparator()
162 self
.ID_TEST
= wx
.NewId()
163 menu
.Append(self
.ID_TEST
, '&Test\tF5', 'Show test window')
164 self
.ID_REFRESH
= wx
.NewId()
165 menu
.Append(self
.ID_REFRESH
, '&Refresh\tCtrl-R', 'Refresh test window')
166 self
.ID_AUTO_REFRESH
= wx
.NewId()
167 menu
.Append(self
.ID_AUTO_REFRESH
, '&Auto-refresh\tCtrl-A',
168 'Toggle auto-refresh mode', True)
169 menu
.Check(self
.ID_AUTO_REFRESH
, conf
.autoRefresh
)
170 self
.ID_TEST_HIDE
= wx
.NewId()
171 menu
.Append(self
.ID_TEST_HIDE
, '&Hide\tF6', 'Close test window')
172 menuBar
.Append(menu
, '&View')
175 self
.ID_MOVEUP
= wx
.NewId()
176 menu
.Append(self
.ID_MOVEUP
, '&Up', 'Move before previous sibling')
177 self
.ID_MOVEDOWN
= wx
.NewId()
178 menu
.Append(self
.ID_MOVEDOWN
, '&Down', 'Move after next sibling')
179 self
.ID_MOVELEFT
= wx
.NewId()
180 menu
.Append(self
.ID_MOVELEFT
, '&Make sibling', 'Make sibling of parent')
181 self
.ID_MOVERIGHT
= wx
.NewId()
182 menu
.Append(self
.ID_MOVERIGHT
, '&Make child', 'Make child of previous sibling')
183 menuBar
.Append(menu
, '&Move')
186 menu
.Append(wx
.ID_ABOUT
, '&About...', 'About XCRed')
187 self
.ID_README
= wx
.NewId()
188 menu
.Append(self
.ID_README
, '&Readme...\tF1', 'View the README file')
190 self
.ID_DEBUG_CMD
= wx
.NewId()
191 menu
.Append(self
.ID_DEBUG_CMD
, 'CMD', 'Python command line')
192 wx
.EVT_MENU(self
, self
.ID_DEBUG_CMD
, self
.OnDebugCMD
)
193 menuBar
.Append(menu
, '&Help')
195 self
.menuBar
= menuBar
196 self
.SetMenuBar(menuBar
)
199 tb
= self
.CreateToolBar(wx
.TB_HORIZONTAL | wx
.NO_BORDER | wx
.TB_FLAT
)
200 tb
.SetToolBitmapSize((24,24))
201 new_bmp
= wx
.ArtProvider
.GetBitmap(wx
.ART_NORMAL_FILE
, wx
.ART_TOOLBAR
)
202 open_bmp
= wx
.ArtProvider
.GetBitmap(wx
.ART_FILE_OPEN
, wx
.ART_TOOLBAR
)
203 save_bmp
= wx
.ArtProvider
.GetBitmap(wx
.ART_FILE_SAVE
, wx
.ART_TOOLBAR
)
204 undo_bmp
= wx
.ArtProvider
.GetBitmap(wx
.ART_UNDO
, wx
.ART_TOOLBAR
)
205 redo_bmp
= wx
.ArtProvider
.GetBitmap(wx
.ART_REDO
, wx
.ART_TOOLBAR
)
206 cut_bmp
= wx
.ArtProvider
.GetBitmap(wx
.ART_CUT
, wx
.ART_TOOLBAR
)
207 copy_bmp
= wx
.ArtProvider
.GetBitmap(wx
.ART_COPY
, wx
.ART_TOOLBAR
)
208 paste_bmp
= wx
.ArtProvider
.GetBitmap(wx
.ART_PASTE
, wx
.ART_TOOLBAR
)
210 tb
.AddSimpleTool(wx
.ID_NEW
, new_bmp
, 'New', 'New file')
211 tb
.AddSimpleTool(wx
.ID_OPEN
, open_bmp
, 'Open', 'Open file')
212 tb
.AddSimpleTool(wx
.ID_SAVE
, save_bmp
, 'Save', 'Save file')
213 tb
.AddControl(wx
.StaticLine(tb
, -1, size
=(-1,23), style
=wx
.LI_VERTICAL
))
214 tb
.AddSimpleTool(wx
.ID_UNDO
, undo_bmp
, 'Undo', 'Undo')
215 tb
.AddSimpleTool(wx
.ID_REDO
, redo_bmp
, 'Redo', 'Redo')
216 tb
.AddControl(wx
.StaticLine(tb
, -1, size
=(-1,23), style
=wx
.LI_VERTICAL
))
217 tb
.AddSimpleTool(wx
.ID_CUT
, cut_bmp
, 'Cut', 'Cut')
218 tb
.AddSimpleTool(wx
.ID_COPY
, copy_bmp
, 'Copy', 'Copy')
219 tb
.AddSimpleTool(self
.ID_TOOL_PASTE
, paste_bmp
, 'Paste', 'Paste')
220 tb
.AddControl(wx
.StaticLine(tb
, -1, size
=(-1,23), style
=wx
.LI_VERTICAL
))
221 tb
.AddSimpleTool(self
.ID_TOOL_LOCATE
,
222 images
.getLocateBitmap(), #images.getLocateArmedBitmap(),
223 'Locate', 'Locate control in test window and select it', True)
224 tb
.AddControl(wx
.StaticLine(tb
, -1, size
=(-1,23), style
=wx
.LI_VERTICAL
))
225 tb
.AddSimpleTool(self
.ID_TEST
, images
.getTestBitmap(), 'Test', 'Test window')
226 tb
.AddSimpleTool(self
.ID_REFRESH
, images
.getRefreshBitmap(),
227 'Refresh', 'Refresh view')
228 tb
.AddSimpleTool(self
.ID_AUTO_REFRESH
, images
.getAutoRefreshBitmap(),
229 'Auto-refresh', 'Toggle auto-refresh mode', True)
230 tb
.AddControl(wx
.StaticLine(tb
, -1, size
=(-1,23), style
=wx
.LI_VERTICAL
))
231 tb
.AddSimpleTool(self
.ID_MOVEUP
, images
.getToolMoveUpBitmap(),
232 'Up', 'Move before previous sibling')
233 tb
.AddSimpleTool(self
.ID_MOVEDOWN
, images
.getToolMoveDownBitmap(),
234 'Down', 'Move after next sibling')
235 tb
.AddSimpleTool(self
.ID_MOVELEFT
, images
.getToolMoveLeftBitmap(),
236 'Make Sibling', 'Make sibling of parent')
237 tb
.AddSimpleTool(self
.ID_MOVERIGHT
, images
.getToolMoveRightBitmap(),
238 'Make Child', 'Make child of previous sibling')
239 # if wx.Platform == '__WXGTK__':
240 # tb.AddSeparator() # otherwise auto-refresh sticks in status line
241 tb
.ToggleTool(self
.ID_AUTO_REFRESH
, conf
.autoRefresh
)
245 self
.minWidth
= tb
.GetSize()[0] # minimal width is the size of toolbar
248 wx
.EVT_MENU(self
, wx
.ID_NEW
, self
.OnNew
)
249 wx
.EVT_MENU(self
, wx
.ID_OPEN
, self
.OnOpen
)
250 wx
.EVT_MENU(self
, wx
.ID_SAVE
, self
.OnSaveOrSaveAs
)
251 wx
.EVT_MENU(self
, wx
.ID_SAVEAS
, self
.OnSaveOrSaveAs
)
252 wx
.EVT_MENU(self
, self
.ID_GENERATE_PYTHON
, self
.OnGeneratePython
)
253 wx
.EVT_MENU(self
, wx
.ID_EXIT
, self
.OnExit
)
255 wx
.EVT_MENU(self
, wx
.ID_UNDO
, self
.OnUndo
)
256 wx
.EVT_MENU(self
, wx
.ID_REDO
, self
.OnRedo
)
257 wx
.EVT_MENU(self
, wx
.ID_CUT
, self
.OnCutDelete
)
258 wx
.EVT_MENU(self
, wx
.ID_COPY
, self
.OnCopy
)
259 wx
.EVT_MENU(self
, wx
.ID_PASTE
, self
.OnPaste
)
260 wx
.EVT_MENU(self
, self
.ID_TOOL_PASTE
, self
.OnPaste
)
261 wx
.EVT_MENU(self
, self
.ID_DELETE
, self
.OnCutDelete
)
262 wx
.EVT_MENU(self
, self
.ID_LOCATE
, self
.OnLocate
)
263 wx
.EVT_MENU(self
, self
.ID_TOOL_LOCATE
, self
.OnLocate
)
265 wx
.EVT_MENU(self
, self
.ID_EMBED_PANEL
, self
.OnEmbedPanel
)
266 wx
.EVT_MENU(self
, self
.ID_SHOW_TOOLS
, self
.OnShowTools
)
267 wx
.EVT_MENU(self
, self
.ID_TEST
, self
.OnTest
)
268 wx
.EVT_MENU(self
, self
.ID_REFRESH
, self
.OnRefresh
)
269 wx
.EVT_MENU(self
, self
.ID_AUTO_REFRESH
, self
.OnAutoRefresh
)
270 wx
.EVT_MENU(self
, self
.ID_TEST_HIDE
, self
.OnTestHide
)
272 wx
.EVT_MENU(self
, self
.ID_MOVEUP
, self
.OnMoveUp
)
273 wx
.EVT_MENU(self
, self
.ID_MOVEDOWN
, self
.OnMoveDown
)
274 wx
.EVT_MENU(self
, self
.ID_MOVELEFT
, self
.OnMoveLeft
)
275 wx
.EVT_MENU(self
, self
.ID_MOVERIGHT
, self
.OnMoveRight
)
277 wx
.EVT_MENU(self
, wx
.ID_ABOUT
, self
.OnAbout
)
278 wx
.EVT_MENU(self
, self
.ID_README
, self
.OnReadme
)
281 wx
.EVT_UPDATE_UI(self
, wx
.ID_SAVE
, self
.OnUpdateUI
)
282 wx
.EVT_UPDATE_UI(self
, wx
.ID_CUT
, self
.OnUpdateUI
)
283 wx
.EVT_UPDATE_UI(self
, wx
.ID_COPY
, self
.OnUpdateUI
)
284 wx
.EVT_UPDATE_UI(self
, wx
.ID_PASTE
, self
.OnUpdateUI
)
285 wx
.EVT_UPDATE_UI(self
, self
.ID_LOCATE
, self
.OnUpdateUI
)
286 wx
.EVT_UPDATE_UI(self
, self
.ID_TOOL_LOCATE
, self
.OnUpdateUI
)
287 wx
.EVT_UPDATE_UI(self
, self
.ID_TOOL_PASTE
, self
.OnUpdateUI
)
288 wx
.EVT_UPDATE_UI(self
, wx
.ID_UNDO
, self
.OnUpdateUI
)
289 wx
.EVT_UPDATE_UI(self
, wx
.ID_REDO
, self
.OnUpdateUI
)
290 wx
.EVT_UPDATE_UI(self
, self
.ID_DELETE
, self
.OnUpdateUI
)
291 wx
.EVT_UPDATE_UI(self
, self
.ID_TEST
, self
.OnUpdateUI
)
292 wx
.EVT_UPDATE_UI(self
, self
.ID_REFRESH
, self
.OnUpdateUI
)
295 sizer
= wx
.BoxSizer(wx
.VERTICAL
)
296 sizer
.Add(wx
.StaticLine(self
, -1), 0, wx
.EXPAND
)
297 # Horizontal sizer for toolbar and splitter
298 self
.toolsSizer
= sizer1
= wx
.BoxSizer()
299 splitter
= wx
.SplitterWindow(self
, -1, style
=wx
.SP_3DSASH
)
300 self
.splitter
= splitter
301 splitter
.SetMinimumPaneSize(100)
304 g
.tree
= tree
= XML_Tree(splitter
, -1)
306 # Init pull-down menu data
308 g
.pullDownMenu
= pullDownMenu
= PullDownMenu(self
)
310 # Vertical toolbar for GUI buttons
311 g
.tools
= tools
= Tools(self
)
312 tools
.Show(conf
.showTools
)
313 if conf
.showTools
: sizer1
.Add(tools
, 0, wx
.EXPAND
)
315 tree
.RegisterKeyEvents()
317 # Miniframe for split mode
318 miniFrame
= wx
.MiniFrame(self
, -1, 'Properties & Style',
319 (conf
.panelX
, conf
.panelY
),
320 (conf
.panelWidth
, conf
.panelHeight
))
321 self
.miniFrame
= miniFrame
322 sizer2
= wx
.BoxSizer()
323 miniFrame
.SetAutoLayout(True)
324 miniFrame
.SetSizer(sizer2
)
325 wx
.EVT_CLOSE(self
.miniFrame
, self
.OnCloseMiniFrame
)
326 # Create panel for parameters
329 panel
= Panel(splitter
)
330 # Set plitter windows
331 splitter
.SplitVertically(tree
, panel
, conf
.sashPos
)
333 panel
= Panel(miniFrame
)
334 sizer2
.Add(panel
, 1, wx
.EXPAND
)
336 splitter
.Initialize(tree
)
337 sizer1
.Add(splitter
, 1, wx
.EXPAND
)
338 sizer
.Add(sizer1
, 1, wx
.EXPAND
)
339 self
.SetAutoLayout(True)
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():
482 g
.panel
.SetModified(False)
483 if not undoMan
.CanUndo():
484 self
.SetModified(False)
486 def OnRedo(self
, evt
):
487 if undoMan
.CanRedo():
489 self
.SetModified(True)
491 def OnCopy(self
, evt
):
492 selected
= tree
.selection
493 if not selected
: return # key pressed event
494 xxx
= tree
.GetPyData(selected
)
495 if wx
.TheClipboard
.Open():
497 data
= wx
.CustomDataObject('XRCED')
498 # Set encoding in header
500 s
= xxx
.node
.toxml(encoding
=expat
.native_encoding
)
502 data
= wx
.CustomDataObject('XRCED_node')
504 data
.SetData(cPickle
.dumps(s
))
505 wx
.TheClipboard
.SetData(data
)
506 wx
.TheClipboard
.Close()
507 self
.SetStatusText('Copied')
509 wx
.MessageBox("Unable to open the clipboard", "Error")
511 def OnPaste(self
, evt
):
512 selected
= tree
.selection
513 if not selected
: return # key pressed event
514 # For pasting with Ctrl pressed
516 if evt
.GetId() == pullDownMenu
.ID_PASTE_SIBLING
: appendChild
= False
517 elif evt
.GetId() == self
.ID_TOOL_PASTE
:
518 if g
.tree
.ctrl
: appendChild
= False
519 else: appendChild
= not tree
.NeedInsert(selected
)
520 else: appendChild
= not tree
.NeedInsert(selected
)
521 xxx
= tree
.GetPyData(selected
)
523 # If has next item, insert, else append to parent
524 nextItem
= tree
.GetNextSibling(selected
)
525 parentLeaf
= tree
.GetItemParent(selected
)
526 # Expanded container (must have children)
527 elif tree
.IsExpanded(selected
) and tree
.GetChildrenCount(selected
, False):
528 # Insert as first child
529 nextItem
= tree
.GetFirstChild(selected
)[0]
530 parentLeaf
= selected
532 # No children or unexpanded item - appendChild stays True
533 nextItem
= wx
.TreeItemId() # no next item
534 parentLeaf
= selected
535 parent
= tree
.GetPyData(parentLeaf
).treeObject()
537 # Create a copy of clipboard pickled element
538 success
= success_node
= False
539 if wx
.TheClipboard
.Open():
541 data
= wx
.CustomDataObject('XRCED')
542 if wx
.TheClipboard
.IsSupported(data
.GetFormat()):
544 success
= wx
.TheClipboard
.GetData(data
)
546 # there is a problem if XRCED_node is in clipboard
547 # but previous SetData was for XRCED
549 if not success
: # try other format
550 data
= wx
.CustomDataObject('XRCED_node')
551 if wx
.TheClipboard
.IsSupported(data
.GetFormat()):
552 success_node
= wx
.TheClipboard
.GetData(data
)
554 wx
.TheClipboard
.Close()
556 if not success
and not success_node
:
558 "There is no data in the clipboard in the required format",
562 xml
= cPickle
.loads(data
.GetData()) # xml representation of element
564 elem
= minidom
.parseString(xml
).childNodes
[0]
566 elem
= g
.tree
.dom
.createComment(xml
)
568 # Tempopary xxx object to test things
569 xxx
= MakeXXXFromDOM(parent
, elem
)
571 # Check compatibility
572 if not self
.ItemsAreCompatible(parent
, xxx
.treeObject()): return
574 # Check parent and child relationships.
575 # If parent is sizer or notebook, child is of wrong class or
576 # parent is normal window, child is child container then detach child.
577 isChildContainer
= isinstance(xxx
, xxxChildContainer
)
578 parentIsBook
= parent
.__class
__ in [xxxNotebook
, xxxChoicebook
, xxxListbook
]
579 if isChildContainer
and \
580 ((parent
.isSizer
and not isinstance(xxx
, xxxSizerItem
)) or \
581 (parentIsBook
and not isinstance(xxx
, xxxPage
)) or \
582 not (parent
.isSizer
or parentIsBook
)):
583 elem
.removeChild(xxx
.child
.node
) # detach child
584 elem
.unlink() # delete child container
585 elem
= xxx
.child
.node
# replace
586 # This may help garbage collection
587 xxx
.child
.parent
= None
588 isChildContainer
= False
589 # Parent is sizer or notebook, child is not child container
590 if parent
.isSizer
and not isChildContainer
and not isinstance(xxx
, xxxSpacer
):
591 # Create sizer item element
592 sizerItemElem
= MakeEmptyDOM(parent
.itemTag
)
593 sizerItemElem
.appendChild(elem
)
595 elif isinstance(parent
, xxxNotebook
) and not isChildContainer
:
596 pageElem
= MakeEmptyDOM('notebookpage')
597 pageElem
.appendChild(elem
)
599 elif isinstance(parent
, xxxChoicebook
) and not isChildContainer
:
600 pageElem
= MakeEmptyDOM('choicebookpage')
601 pageElem
.appendChild(elem
)
603 elif isinstance(parent
, xxxListbook
) and not isChildContainer
:
604 pageElem
= MakeEmptyDOM('listbookpage')
605 pageElem
.appendChild(elem
)
607 # Insert new node, register undo
608 newItem
= tree
.InsertNode(parentLeaf
, parent
, elem
, nextItem
)
609 undoMan
.RegisterUndo(UndoPasteCreate(parentLeaf
, parent
, newItem
, selected
))
610 # Scroll to show new item (!!! redundant?)
611 tree
.EnsureVisible(newItem
)
612 tree
.SelectItem(newItem
)
613 if not tree
.IsVisible(newItem
):
614 tree
.ScrollTo(newItem
)
617 if g
.testWin
and tree
.IsHighlatable(newItem
):
619 tree
.needUpdate
= True
620 tree
.pendingHighLight
= newItem
622 tree
.pendingHighLight
= None
624 self
.SetStatusText('Pasted')
627 def ItemsAreCompatible(self
, parent
, child
):
628 # Check compatibility
630 # Comments are always compatible
631 if child
.__class
__ == xxxComment
:
634 if child
.__class
__ in [xxxDialog
, xxxFrame
, xxxWizard
]:
636 if parent
.__class
__ != xxxMainNode
: error
= True
637 elif child
.__class
__ == xxxMenuBar
:
638 # Menubar can be put in frame or dialog
639 if parent
.__class
__ not in [xxxMainNode
, xxxFrame
, xxxDialog
]: error
= True
640 elif child
.__class
__ == xxxToolBar
:
641 # Toolbar can be top-level of child of panel or frame
642 if parent
.__class
__ not in [xxxMainNode
, xxxPanel
, xxxFrame
] and \
643 not parent
.isSizer
: error
= True
644 elif child
.__class
__ == xxxPanel
and parent
.__class
__ == xxxMainNode
:
646 elif child
.__class
__ == xxxSpacer
:
647 if not parent
.isSizer
: error
= True
648 elif child
.__class
__ == xxxSeparator
:
649 if not parent
.__class
__ in [xxxMenu
, xxxToolBar
]: error
= True
650 elif child
.__class
__ == xxxTool
:
651 if parent
.__class
__ != xxxToolBar
: error
= True
652 elif child
.__class
__ == xxxMenu
:
653 if not parent
.__class
__ in [xxxMainNode
, xxxMenuBar
, xxxMenu
]: error
= True
654 elif child
.__class
__ == xxxMenuItem
:
655 if not parent
.__class
__ in [xxxMenuBar
, xxxMenu
]: error
= True
656 elif child
.isSizer
and parent
.__class
__ in [xxxNotebook
, xxxChoicebook
, xxxListbook
]:
658 else: # normal controls can be almost anywhere
659 if parent
.__class
__ == xxxMainNode
or \
660 parent
.__class
__ in [xxxMenuBar
, xxxMenu
]: error
= True
662 if parent
.__class
__ == xxxMainNode
: parentClass
= 'root'
663 else: parentClass
= parent
.className
664 wx
.LogError('Incompatible parent/child: parent is %s, child is %s!' %
665 (parentClass
, child
.className
))
669 def OnMoveUp(self
, evt
):
670 selected
= tree
.selection
671 if not selected
: return
673 index
= tree
.ItemIndex(selected
)
674 if index
== 0: return # No previous sibling found
677 self
.lastOp
= 'MOVEUP'
678 status
= 'Moved before previous sibling'
683 parent
= tree
.GetItemParent(selected
)
684 elem
= tree
.RemoveLeaf(selected
)
685 nextItem
= tree
.GetFirstChild(parent
)[0]
686 for i
in range(index
- 1): nextItem
= tree
.GetNextSibling(nextItem
)
687 selected
= tree
.InsertNode(parent
, tree
.GetPyData(parent
).treeObject(), elem
, nextItem
)
688 newIndex
= tree
.ItemIndex(selected
)
689 tree
.SelectItem(selected
)
691 undoMan
.RegisterUndo(UndoMove(parent
, index
, parent
, newIndex
))
694 self
.SetStatusText(status
)
698 def OnMoveDown(self
, evt
):
699 selected
= tree
.selection
700 if not selected
: return
702 index
= tree
.ItemIndex(selected
)
703 next
= tree
.GetNextSibling(selected
)
707 self
.lastOp
= 'MOVEDOWN'
708 status
= 'Moved after next sibling'
713 parent
= tree
.GetItemParent(selected
)
714 elem
= tree
.RemoveLeaf(selected
)
715 nextItem
= tree
.GetFirstChild(parent
)[0]
716 for i
in range(index
+ 1): nextItem
= tree
.GetNextSibling(nextItem
)
717 selected
= tree
.InsertNode(parent
, tree
.GetPyData(parent
).treeObject(), elem
, nextItem
)
718 newIndex
= tree
.ItemIndex(selected
)
719 tree
.SelectItem(selected
)
721 undoMan
.RegisterUndo(UndoMove(parent
, index
, parent
, newIndex
))
724 self
.SetStatusText(status
)
728 def OnMoveLeft(self
, evt
):
729 selected
= tree
.selection
730 if not selected
: return
732 oldParent
= tree
.GetItemParent(selected
)
733 if not oldParent
: return
734 pparent
= tree
.GetItemParent(oldParent
)
735 if not pparent
: return
737 # Check compatibility
738 if not self
.ItemsAreCompatible(tree
.GetPyData(pparent
).treeObject(), tree
.GetPyData(selected
).treeObject()): return
741 self
.lastOp
= 'MOVELEFT'
742 status
= 'Made next sibling of parent'
744 oldIndex
= tree
.ItemIndex(selected
)
745 elem
= tree
.RemoveLeaf(selected
)
746 nextItem
= tree
.GetFirstChild(pparent
)[0]
747 parentIndex
= tree
.ItemIndex(oldParent
)
748 for i
in range(parentIndex
+ 1): nextItem
= tree
.GetNextSibling(nextItem
)
750 # Check parent and child relationships.
751 # If parent is sizer or notebook, child is of wrong class or
752 # parent is normal window, child is child container then detach child.
753 parent
= tree
.GetPyData(pparent
).treeObject()
754 xxx
= MakeXXXFromDOM(parent
, elem
)
755 isChildContainer
= isinstance(xxx
, xxxChildContainer
)
756 if isChildContainer
and \
757 ((parent
.isSizer
and not isinstance(xxx
, xxxSizerItem
)) or \
758 (isinstance(parent
, xxxNotebook
) and not isinstance(xxx
, xxxNotebookPage
)) or \
759 not (parent
.isSizer
or isinstance(parent
, xxxNotebook
))):
760 elem
.removeChild(xxx
.child
.node
) # detach child
761 elem
.unlink() # delete child container
762 elem
= xxx
.child
.node
# replace
763 # This may help garbage collection
764 xxx
.child
.parent
= None
765 isChildContainer
= False
766 # Parent is sizer or notebook, child is not child container
767 if parent
.isSizer
and not isChildContainer
and not isinstance(xxx
, xxxSpacer
):
768 # Create sizer item element
769 sizerItemElem
= MakeEmptyDOM('sizeritem')
770 sizerItemElem
.appendChild(elem
)
772 elif isinstance(parent
, xxxNotebook
) and not isChildContainer
:
773 pageElem
= MakeEmptyDOM('notebookpage')
774 pageElem
.appendChild(elem
)
777 selected
= tree
.InsertNode(pparent
, tree
.GetPyData(pparent
).treeObject(), elem
, nextItem
)
778 newIndex
= tree
.ItemIndex(selected
)
779 tree
.SelectItem(selected
)
781 undoMan
.RegisterUndo(UndoMove(oldParent
, oldIndex
, pparent
, newIndex
))
784 self
.SetStatusText(status
)
786 def OnMoveRight(self
, evt
):
787 selected
= tree
.selection
788 if not selected
: return
790 oldParent
= tree
.GetItemParent(selected
)
791 if not oldParent
: return
793 newParent
= tree
.GetPrevSibling(selected
)
794 if not newParent
: return
796 parent
= tree
.GetPyData(newParent
).treeObject()
798 # Check compatibility
799 if not self
.ItemsAreCompatible(parent
, tree
.GetPyData(selected
).treeObject()): return
802 self
.lastOp
= 'MOVERIGHT'
803 status
= 'Made last child of previous sibling'
805 oldIndex
= tree
.ItemIndex(selected
)
806 elem
= tree
.RemoveLeaf(selected
)
808 # Check parent and child relationships.
809 # If parent is sizer or notebook, child is of wrong class or
810 # parent is normal window, child is child container then detach child.
811 xxx
= MakeXXXFromDOM(parent
, elem
)
812 isChildContainer
= isinstance(xxx
, xxxChildContainer
)
813 if isChildContainer
and \
814 ((parent
.isSizer
and not isinstance(xxx
, xxxSizerItem
)) or \
815 (isinstance(parent
, xxxNotebook
) and not isinstance(xxx
, xxxNotebookPage
)) or \
816 not (parent
.isSizer
or isinstance(parent
, xxxNotebook
))):
817 elem
.removeChild(xxx
.child
.node
) # detach child
818 elem
.unlink() # delete child container
819 elem
= xxx
.child
.node
# replace
820 # This may help garbage collection
821 xxx
.child
.parent
= None
822 isChildContainer
= False
823 # Parent is sizer or notebook, child is not child container
824 if parent
.isSizer
and not isChildContainer
and not isinstance(xxx
, xxxSpacer
):
825 # Create sizer item element
826 sizerItemElem
= MakeEmptyDOM('sizeritem')
827 sizerItemElem
.appendChild(elem
)
829 elif isinstance(parent
, xxxNotebook
) and not isChildContainer
:
830 pageElem
= MakeEmptyDOM('notebookpage')
831 pageElem
.appendChild(elem
)
834 selected
= tree
.InsertNode(newParent
, tree
.GetPyData(newParent
).treeObject(), elem
, wx
.TreeItemId())
836 newIndex
= tree
.ItemIndex(selected
)
837 tree
.SelectItem(selected
)
839 undoMan
.RegisterUndo(UndoMove(oldParent
, oldIndex
, newParent
, newIndex
))
842 self
.SetStatusText(status
)
845 def OnCutDelete(self
, evt
):
846 selected
= tree
.selection
847 if not selected
: return # key pressed event
849 if evt
.GetId() == wx
.ID_CUT
:
851 status
= 'Removed to clipboard'
853 self
.lastOp
= 'DELETE'
857 # If deleting top-level item, delete testWin
858 if selected
== g
.testWin
.item
:
862 # Remove highlight, update testWin
863 if g
.testWin
.highLight
:
864 g
.testWin
.highLight
.Remove()
865 tree
.needUpdate
= True
868 index
= tree
.ItemFullIndex(selected
)
869 xxx
= tree
.GetPyData(selected
)
870 parent
= tree
.GetPyData(tree
.GetItemParent(selected
)).treeObject()
871 elem
= tree
.RemoveLeaf(selected
)
872 undoMan
.RegisterUndo(UndoCutDelete(index
, parent
, elem
))
873 if evt
.GetId() == wx
.ID_CUT
:
874 if wx
.TheClipboard
.Open():
876 data
= wx
.CustomDataObject('XRCED')
878 s
= elem
.toxml(encoding
=expat
.native_encoding
)
880 data
= wx
.CustomDataObject('XRCED_node')
882 data
.SetData(cPickle
.dumps(s
))
883 wx
.TheClipboard
.SetData(data
)
884 wx
.TheClipboard
.Close()
886 wx
.MessageBox("Unable to open the clipboard", "Error")
887 tree
.pendingHighLight
= None
889 tree
.selection
= None
894 self
.SetStatusText(status
)
896 def OnSubclass(self
, evt
):
897 selected
= tree
.selection
898 xxx
= tree
.GetPyData(selected
).treeObject()
900 subclass
= xxx
.subclass
901 dlg
= wx
.TextEntryDialog(self
, 'Subclass:', defaultValue
=subclass
)
902 if dlg
.ShowModal() == wx
.ID_OK
:
903 subclass
= dlg
.GetValue()
905 elem
.setAttribute('subclass', subclass
)
906 elif elem
.hasAttribute('subclass'):
907 elem
.removeAttribute('subclass')
909 xxx
.subclass
= elem
.getAttribute('subclass')
910 tree
.SetItemText(selected
, xxx
.treeName())
911 panel
.pages
[0].box
.SetLabel(xxx
.panelName())
914 def OnEmbedPanel(self
, evt
):
915 conf
.embedPanel
= evt
.IsChecked()
917 # Remember last dimentions
918 conf
.panelX
, conf
.panelY
= self
.miniFrame
.GetPosition()
919 conf
.panelWidth
, conf
.panelHeight
= self
.miniFrame
.GetSize()
920 size
= self
.GetSize()
921 pos
= self
.GetPosition()
922 sizePanel
= panel
.GetSize()
923 panel
.Reparent(self
.splitter
)
924 self
.miniFrame
.GetSizer().Remove(panel
)
926 self
.SetDimensions(pos
.x
, pos
.y
, size
.width
+ sizePanel
.width
, size
.height
)
927 self
.splitter
.SplitVertically(tree
, panel
, conf
.sashPos
)
928 self
.miniFrame
.Show(False)
930 conf
.sashPos
= self
.splitter
.GetSashPosition()
931 pos
= self
.GetPosition()
932 size
= self
.GetSize()
933 sizePanel
= panel
.GetSize()
934 self
.splitter
.Unsplit(panel
)
935 sizer
= self
.miniFrame
.GetSizer()
936 panel
.Reparent(self
.miniFrame
)
938 sizer
.Add(panel
, 1, wx
.EXPAND
)
939 self
.miniFrame
.Show(True)
940 self
.miniFrame
.SetDimensions(conf
.panelX
, conf
.panelY
,
941 conf
.panelWidth
, conf
.panelHeight
)
942 self
.miniFrame
.Layout()
944 self
.SetDimensions(pos
.x
, pos
.y
,
945 max(size
.width
- sizePanel
.width
, self
.minWidth
), size
.height
)
947 def OnShowTools(self
, evt
):
948 conf
.showTools
= evt
.IsChecked()
949 g
.tools
.Show(conf
.showTools
)
951 self
.toolsSizer
.Prepend(g
.tools
, 0, wx
.EXPAND
)
953 self
.toolsSizer
.Remove(g
.tools
)
954 self
.toolsSizer
.Layout()
956 def OnTest(self
, evt
):
957 if not tree
.selection
: return # key pressed event
958 tree
.ShowTestWindow(tree
.selection
)
960 def OnTestHide(self
, evt
):
961 tree
.CloseTestWindow()
963 # Find object by relative position
964 def FindObject(self
, item
, obj
):
965 # We simply perform depth-first traversal, sinse it's too much
966 # hassle to deal with all sizer/window combinations
967 w
= tree
.FindNodeObject(item
)
968 if w
== obj
or isinstance(w
, wx
.GBSizerItem
) and w
.GetWindow() == obj
:
970 if tree
.ItemHasChildren(item
):
971 child
= tree
.GetFirstChild(item
)[0]
973 found
= self
.FindObject(child
, obj
)
974 if found
: return found
975 child
= tree
.GetNextSibling(child
)
978 def OnTestWinLeftDown(self
, evt
):
979 pos
= evt
.GetPosition()
980 self
.SetHandler(g
.testWin
)
981 g
.testWin
.Disconnect(wx
.ID_ANY
, wx
.ID_ANY
, wx
.wxEVT_LEFT_DOWN
)
982 item
= self
.FindObject(g
.testWin
.item
, evt
.GetEventObject())
984 tree
.EnsureVisible(item
)
985 tree
.SelectItem(item
)
986 self
.tb
.ToggleTool(self
.ID_TOOL_LOCATE
, False)
988 self
.SetStatusText('Selected %s' % tree
.GetItemText(item
))
990 self
.SetStatusText('Locate failed!')
992 def SetHandler(self
, w
, h
=None):
995 w
.SetCursor(wx
.CROSS_CURSOR
)
998 w
.SetCursor(wx
.NullCursor
)
999 for ch
in w
.GetChildren():
1000 self
.SetHandler(ch
, h
)
1002 def OnLocate(self
, evt
):
1004 if evt
.GetId() == self
.ID_LOCATE
or \
1005 evt
.GetId() == self
.ID_TOOL_LOCATE
and evt
.IsChecked():
1006 self
.SetHandler(g
.testWin
, g
.testWin
)
1007 g
.testWin
.Connect(wx
.ID_ANY
, wx
.ID_ANY
, wx
.wxEVT_LEFT_DOWN
, self
.OnTestWinLeftDown
)
1008 if evt
.GetId() == self
.ID_LOCATE
:
1009 self
.tb
.ToggleTool(self
.ID_TOOL_LOCATE
, True)
1010 elif evt
.GetId() == self
.ID_TOOL_LOCATE
and not evt
.IsChecked():
1011 self
.SetHandler(g
.testWin
, None)
1012 g
.testWin
.Disconnect(wx
.ID_ANY
, wx
.ID_ANY
, wx
.wxEVT_LEFT_DOWN
)
1013 self
.SetStatusText('Click somewhere in your test window now')
1015 def OnRefresh(self
, evt
):
1016 # If modified, apply first
1017 selection
= tree
.selection
1019 xxx
= tree
.GetPyData(selection
)
1020 if xxx
and panel
.IsModified():
1021 tree
.Apply(xxx
, selection
)
1024 tree
.CreateTestWin(g
.testWin
.item
)
1025 panel
.modified
= False
1026 tree
.needUpdate
= False
1028 def OnAutoRefresh(self
, evt
):
1029 conf
.autoRefresh
= evt
.IsChecked()
1030 self
.menuBar
.Check(self
.ID_AUTO_REFRESH
, conf
.autoRefresh
)
1031 self
.tb
.ToggleTool(self
.ID_AUTO_REFRESH
, conf
.autoRefresh
)
1033 def OnAbout(self
, evt
):
1037 (c) Roman Rolinsky <rollrom@users.sourceforge.net>
1038 Homepage: http://xrced.sourceforge.net\
1040 dlg
= wx
.MessageDialog(self
, str, 'About XRCed', wx
.OK | wx
.CENTRE
)
1044 def OnReadme(self
, evt
):
1045 text
= open(os
.path
.join(basePath
, 'README.txt'), 'r').read()
1046 dlg
= ScrolledMessageDialog(self
, text
, "XRCed README")
1050 # Simple emulation of python command line
1051 def OnDebugCMD(self
, evt
):
1054 exec raw_input('C:\> ')
1059 (etype
, value
, tb
) =sys
.exc_info()
1060 tblist
=traceback
.extract_tb(tb
)[1:]
1061 msg
=' '.join(traceback
.format_exception_only(etype
, value
)
1062 +traceback
.format_list(tblist
))
1065 def OnCreate(self
, evt
):
1066 selected
= tree
.selection
1067 if tree
.ctrl
: appendChild
= False
1068 else: appendChild
= not tree
.NeedInsert(selected
)
1069 xxx
= tree
.GetPyData(selected
)
1073 # If has previous item, insert after it, else append to parent
1075 parentLeaf
= tree
.GetItemParent(selected
)
1077 # If has next item, insert, else append to parent
1078 nextItem
= tree
.GetNextSibling(selected
)
1079 parentLeaf
= tree
.GetItemParent(selected
)
1080 # Expanded container (must have children)
1081 elif tree
.shift
and tree
.IsExpanded(selected
) \
1082 and tree
.GetChildrenCount(selected
, False):
1083 nextItem
= tree
.GetFirstChild(selected
)[0]
1084 parentLeaf
= selected
1086 nextItem
= wx
.TreeItemId()
1087 parentLeaf
= selected
1088 parent
= tree
.GetPyData(parentLeaf
)
1089 if parent
.hasChild
: parent
= parent
.child
1091 # Create object_ref?
1092 if evt
.GetId() == ID_NEW
.REF
:
1093 ref
= wx
.GetTextFromUser('Create reference to:', 'Create reference')
1095 xxx
= MakeEmptyRefXXX(parent
, ref
)
1096 elif evt
.GetId() == ID_NEW
.COMMENT
:
1097 xxx
= MakeEmptyCommentXXX(parent
)
1099 # Create empty element
1100 if evt
.GetId() >= ID_NEW
.CUSTOM
:
1101 className
= pullDownMenu
.customMap
[evt
.GetId()]
1103 className
= pullDownMenu
.createMap
[evt
.GetId()]
1104 xxx
= MakeEmptyXXX(parent
, className
)
1106 # Insert new node, register undo
1107 if xxx
.isElement
: # true object
1108 # Set default name for top-level windows
1109 if parent
.__class
__ == xxxMainNode
:
1110 cl
= xxx
.treeObject().__class
__
1111 frame
.maxIDs
[cl
] += 1
1112 xxx
.setTreeName('%s%d' % (defaultIDs
[cl
], frame
.maxIDs
[cl
]))
1113 # And for some other standard controls
1114 elif parent
.__class
__ == xxxStdDialogButtonSizer
:
1115 xxx
.setTreeName(pullDownMenu
.stdButtonIDs
[evt
.GetId()][0])
1116 # We can even set label
1117 obj
= xxx
.treeObject()
1118 elem
= g
.tree
.dom
.createElement('label')
1119 elem
.appendChild(g
.tree
.dom
.createTextNode(pullDownMenu
.stdButtonIDs
[evt
.GetId()][1]))
1120 obj
.params
['label'] = xxxParam(elem
)
1121 xxx
.treeObject().node
.appendChild(elem
)
1123 newItem
= tree
.InsertNode(parentLeaf
, parent
, xxx
.node
, nextItem
)
1124 else: # comment node
1125 newItem
= tree
.InsertNode(parentLeaf
, parent
, xxx
.node
, nextItem
)
1126 undoMan
.RegisterUndo(UndoPasteCreate(parentLeaf
, parent
, newItem
, selected
))
1127 tree
.EnsureVisible(newItem
)
1128 tree
.SelectItem(newItem
)
1129 if not tree
.IsVisible(newItem
):
1130 tree
.ScrollTo(newItem
)
1133 if xxx
.isElement
and g
.testWin
and tree
.IsHighlatable(newItem
):
1134 if conf
.autoRefresh
:
1135 tree
.needUpdate
= True
1136 tree
.pendingHighLight
= newItem
1138 tree
.pendingHighLight
= None
1140 if not xxx
.isElement
:
1141 tree
.EditLabel(newItem
)
1144 # Replace one object with another
1145 def OnReplace(self
, evt
):
1146 selected
= tree
.selection
1147 xxx
= tree
.GetPyData(selected
).treeObject()
1149 parent
= elem
.parentNode
1150 undoMan
.RegisterUndo(UndoReplace(selected
))
1152 className
= pullDownMenu
.createMap
[evt
.GetId() - 1000]
1154 # Create temporary empty node (with default values)
1155 dummy
= MakeEmptyDOM(className
)
1156 if className
== 'spacer' and xxx
.className
!= 'spacer':
1158 elif xxx
.className
== 'spacer' and className
!= 'spacer':
1159 klass
= xxxSizerItem
1161 klass
= xxxDict
[className
]
1162 # Remove non-compatible children
1163 if tree
.ItemHasChildren(selected
) and not klass
.hasChildren
:
1164 tree
.DeleteChildren(selected
)
1165 nodes
= elem
.childNodes
[:]
1168 if node
.nodeType
!= minidom
.Node
.ELEMENT_NODE
: continue
1172 if not klass
.hasChildren
: remove
= True
1173 elif tag
not in klass
.allParams
and \
1174 (not klass
.hasStyle
or tag
not in klass
.styles
):
1179 elem
.removeChild(node
)
1182 # Remove sizeritem child if spacer
1183 if className
== 'spacer' and xxx
.className
!= 'spacer':
1184 sizeritem
= elem
.parentNode
1185 assert sizeritem
.getAttribute('class') == 'sizeritem'
1186 sizeritem
.removeChild(elem
)
1189 tree
.GetPyData(selected
).hasChild
= False
1190 elif xxx
.className
== 'spacer' and className
!= 'spacer':
1191 # Create sizeritem element
1192 assert xxx
.parent
.isSizer
1193 elem
.setAttribute('class', 'sizeritem')
1194 node
= MakeEmptyDOM(className
)
1195 elem
.appendChild(node
)
1196 # Replace to point to new object
1197 xxx
= xxxSizerItem(xxx
.parent
, elem
)
1199 tree
.SetPyData(selected
, xxx
)
1202 # Copy parameters present in dummy but not in elem
1203 for node
in dummy
.childNodes
:
1204 if node
.tagName
not in tags
: elem
.appendChild(node
.cloneNode(True))
1208 elem
.setAttribute('class', className
)
1209 if elem
.hasAttribute('subclass'):
1210 elem
.removeAttribute('subclass') # clear subclassing
1211 # Re-create xxx element
1212 xxx
= MakeXXXFromDOM(xxx
.parent
, elem
)
1213 # Remove incompatible style flags
1214 if 'style' in xxx
.params
:
1215 styles
= map(string
.strip
, xxx
.params
['style'].value().split('|'))
1216 newStyles
= [s
for s
in styles
if s
in klass
.winStyles
or s
in genericStyles
]
1217 if newStyles
!= styles
:
1219 value
= reduce(lambda a
,b
: a
+'|'+b
, newStyles
)
1222 xxx
.params
['style'].update(value
)
1224 # Update parent in child objects
1225 if tree
.ItemHasChildren(selected
):
1226 i
, cookie
= tree
.GetFirstChild(selected
)
1228 x
= tree
.GetPyData(i
)
1230 if x
.hasChild
: x
.child
.parent
= xxx
1231 i
, cookie
= tree
.GetNextChild(selected
, cookie
)
1234 if tree
.GetPyData(selected
).hasChild
: # child container
1235 container
= tree
.GetPyData(selected
)
1236 container
.resetChild(xxx
)
1239 tree
.SetPyData(selected
, xxx
)
1240 tree
.SetItemText(selected
, xxx
.treeName())
1241 tree
.SetItemImage(selected
, xxx
.treeImage())
1243 # Set default name for top-level windows
1244 if parent
.__class
__ == xxxMainNode
:
1245 cl
= xxx
.treeObject().__class
__
1246 frame
.maxIDs
[cl
] += 1
1247 xxx
.setTreeName('%s%d' % (defaultIDs
[cl
], frame
.maxIDs
[cl
]))
1250 g
.panel
.SetData(xxx
)
1254 #undoMan.RegisterUndo(UndoPasteCreate(parentLeaf, parent, newItem, selected))
1256 if g
.testWin
and tree
.IsHighlatable(selected
):
1257 if conf
.autoRefresh
:
1258 tree
.needUpdate
= True
1259 tree
.pendingHighLight
= selected
1261 tree
.pendingHighLight
= None
1265 # Expand/collapse subtree
1266 def OnExpand(self
, evt
):
1267 if tree
.selection
: tree
.ExpandAll(tree
.selection
)
1268 else: tree
.ExpandAll(tree
.root
)
1269 def OnCollapse(self
, evt
):
1270 if tree
.selection
: tree
.CollapseAll(tree
.selection
)
1271 else: tree
.CollapseAll(tree
.root
)
1273 def OnPullDownHighlight(self
, evt
):
1274 menuId
= evt
.GetMenuId()
1276 menu
= evt
.GetEventObject()
1277 help = menu
.GetHelpString(menuId
)
1278 self
.SetStatusText(help)
1280 self
.SetStatusText('')
1282 def OnUpdateUI(self
, evt
):
1283 if evt
.GetId() in [wx
.ID_CUT
, wx
.ID_COPY
, self
.ID_DELETE
]:
1284 evt
.Enable(tree
.selection
is not None and tree
.selection
!= tree
.root
)
1285 elif evt
.GetId() == wx
.ID_SAVE
:
1286 evt
.Enable(self
.modified
)
1287 elif evt
.GetId() in [wx
.ID_PASTE
, self
.ID_TOOL_PASTE
]:
1288 evt
.Enable(tree
.selection
is not None)
1289 elif evt
.GetId() == self
.ID_TEST
:
1290 evt
.Enable(tree
.selection
is not None and tree
.selection
!= tree
.root
)
1291 elif evt
.GetId() in [self
.ID_LOCATE
, self
.ID_TOOL_LOCATE
]:
1292 evt
.Enable(g
.testWin
is not None)
1293 elif evt
.GetId() == wx
.ID_UNDO
: evt
.Enable(undoMan
.CanUndo())
1294 elif evt
.GetId() == wx
.ID_REDO
: evt
.Enable(undoMan
.CanRedo())
1296 def OnIdle(self
, evt
):
1297 if self
.inIdle
: return # Recursive call protection
1301 if conf
.autoRefresh
:
1303 self
.SetStatusText('Refreshing test window...')
1305 tree
.CreateTestWin(g
.testWin
.item
)
1306 self
.SetStatusText('')
1307 tree
.needUpdate
= False
1308 elif tree
.pendingHighLight
:
1310 tree
.HighLight(tree
.pendingHighLight
)
1312 # Remove highlight if any problem
1313 if g
.testWin
.highLight
:
1314 g
.testWin
.highLight
.Remove()
1315 tree
.pendingHighLight
= None
1322 # We don't let close panel window
1323 def OnCloseMiniFrame(self
, evt
):
1326 def OnIconize(self
, evt
):
1328 conf
.x
, conf
.y
= self
.GetPosition()
1329 conf
.width
, conf
.height
= self
.GetSize()
1331 conf
.sashPos
= self
.splitter
.GetSashPosition()
1333 conf
.panelX
, conf
.panelY
= self
.miniFrame
.GetPosition()
1334 conf
.panelWidth
, conf
.panelHeight
= self
.miniFrame
.GetSize()
1335 self
.miniFrame
.Iconize()
1337 if not conf
.embedPanel
:
1338 self
.miniFrame
.Iconize(False)
1341 def OnCloseWindow(self
, evt
):
1342 if not self
.AskSave(): return
1343 if g
.testWin
: g
.testWin
.Destroy()
1344 if not panel
.GetPageCount() == 2:
1345 panel
.page2
.Destroy()
1347 # If we don't do this, page does not get destroyed (a bug?)
1349 if not self
.IsIconized():
1350 conf
.x
, conf
.y
= self
.GetPosition()
1351 conf
.width
, conf
.height
= self
.GetSize()
1353 conf
.sashPos
= self
.splitter
.GetSashPosition()
1355 conf
.panelX
, conf
.panelY
= self
.miniFrame
.GetPosition()
1356 conf
.panelWidth
, conf
.panelHeight
= self
.miniFrame
.GetSize()
1359 def CreateLocalConf(self
, path
):
1360 name
= os
.path
.splitext(path
)[0]
1362 return wx
.FileConfig(localFilename
=name
)
1366 conf
.localconf
= None
1368 self
.SetModified(False)
1374 # Numbers for new controls
1376 for cl
in [xxxPanel
, xxxDialog
, xxxFrame
,
1377 xxxMenuBar
, xxxMenu
, xxxToolBar
,
1378 xxxWizard
, xxxBitmap
, xxxIcon
]:
1380 # Restore handlers, menu, etc. to initial
1381 setHandlers(self
.handlers
[:])
1382 g
.pullDownMenu
.custom
= self
.custom
[:]
1383 # Remove modules imported from comment directives
1384 map(sys
.modules
.pop
, [m
for m
in sys
.modules
if m
not in self
.modules
])
1386 def SetModified(self
, state
=True):
1387 self
.modified
= state
1388 name
= os
.path
.basename(self
.dataFile
)
1389 if not name
: name
= defaultName
1391 self
.SetTitle(progname
+ ': ' + name
+ ' *')
1393 self
.SetTitle(progname
+ ': ' + name
)
1395 def Open(self
, path
):
1396 if not os
.path
.exists(path
):
1397 wx
.LogError('File does not exists: %s' % path
)
1399 # Try to read the file
1403 dom
= minidom
.parse(f
)
1405 # Set encoding global variable and default encoding
1407 g
.currentEncoding
= dom
.encoding
1408 wx
.SetDefaultPyEncoding(g
.currentEncoding
.encode())
1410 g
.currentEncoding
= ''
1412 self
.dataFile
= path
= os
.path
.abspath(path
)
1413 dir = os
.path
.dirname(path
)
1414 if dir: os
.chdir(dir)
1415 # Allow importing modules from the same directory
1416 sys
.path
= sys_path
+ [dir]
1418 self
.SetTitle(progname
+ ': ' + os
.path
.basename(path
))
1419 conf
.localconf
= self
.CreateLocalConf(self
.dataFile
)
1421 # Nice exception printing
1422 inf
= sys
.exc_info()
1423 wx
.LogError(traceback
.format_exception(inf
[0], inf
[1], None)[-1])
1424 wx
.LogError('Error reading file: %s' % path
)
1429 def Indent(self
, node
, indent
= 0):
1430 if node
.nodeType
== minidom
.Node
.COMMENT_NODE
:
1431 text
= self
.domCopy
.createTextNode('\n' + ' ' * indent
)
1432 node
.parentNode
.insertBefore(text
, node
)
1433 return # no children
1434 # Copy child list because it will change soon
1435 children
= node
.childNodes
[:]
1436 # Main node doesn't need to be indented
1438 text
= self
.domCopy
.createTextNode('\n' + ' ' * indent
)
1439 node
.parentNode
.insertBefore(text
, node
)
1441 # Append newline after last child, except for text nodes
1442 if children
[-1].nodeType
== minidom
.Node
.ELEMENT_NODE
:
1443 text
= self
.domCopy
.createTextNode('\n' + ' ' * indent
)
1444 node
.appendChild(text
)
1445 # Indent children which are elements
1447 if n
.nodeType
== minidom
.Node
.ELEMENT_NODE
or \
1448 n
.nodeType
== minidom
.Node
.COMMENT_NODE
:
1449 self
.Indent(n
, indent
+ 2)
1451 def Save(self
, path
):
1455 if tree
.selection
and panel
.IsModified():
1456 self
.OnRefresh(wx
.CommandEvent())
1457 if g
.currentEncoding
:
1458 f
= codecs
.open(path
, 'wt', g
.currentEncoding
)
1460 f
= codecs
.open(path
, 'wt')
1461 # Make temporary copy for formatting it
1462 # !!! We can't clone dom node, it works only once
1463 #self.domCopy = tree.dom.cloneNode(True)
1464 self
.domCopy
= MyDocument()
1465 mainNode
= self
.domCopy
.appendChild(tree
.mainNode
.cloneNode(True))
1466 # Remove first child (test element)
1467 testElem
= mainNode
.firstChild
1468 mainNode
.removeChild(testElem
)
1470 self
.Indent(mainNode
)
1471 self
.domCopy
.writexml(f
, encoding
= g
.currentEncoding
)
1473 self
.domCopy
.unlink()
1475 self
.SetModified(False)
1476 panel
.SetModified(False)
1477 conf
.localconf
.Flush()
1479 inf
= sys
.exc_info()
1480 wx
.LogError(traceback
.format_exception(inf
[0], inf
[1], None)[-1])
1481 wx
.LogError('Error writing file: %s' % path
)
1485 if not (self
.modified
or panel
.IsModified()): return True
1486 flags
= wx
.ICON_EXCLAMATION | wx
.YES_NO | wx
.CANCEL | wx
.CENTRE
1487 dlg
= wx
.MessageDialog( self
, 'File is modified. Save before exit?',
1488 'Save before too late?', flags
)
1489 say
= dlg
.ShowModal()
1492 if say
== wx
.ID_YES
:
1493 self
.OnSaveOrSaveAs(wx
.CommandEvent(wx
.ID_SAVE
))
1494 # If save was successful, modified flag is unset
1495 if not self
.modified
: return True
1496 elif say
== wx
.ID_NO
:
1497 self
.SetModified(False)
1498 panel
.SetModified(False)
1505 ################################################################################
1507 class PythonOptions(wx
.Dialog
):
1509 def __init__(self
, parent
, cfg
, dataFile
):
1510 pre
= wx
.PreDialog()
1511 g
.frame
.res
.LoadOnDialog(pre
, parent
, "PYTHON_OPTIONS")
1512 self
.PostCreate(pre
)
1515 self
.dataFile
= dataFile
1517 self
.AutoGenerateCB
= xrc
.XRCCTRL(self
, "AutoGenerateCB")
1518 self
.EmbedCB
= xrc
.XRCCTRL(self
, "EmbedCB")
1519 self
.GettextCB
= xrc
.XRCCTRL(self
, "GettextCB")
1520 self
.MakeXRSFileCB
= xrc
.XRCCTRL(self
, "MakeXRSFileCB")
1521 self
.FileNameTC
= xrc
.XRCCTRL(self
, "FileNameTC")
1522 self
.BrowseBtn
= xrc
.XRCCTRL(self
, "BrowseBtn")
1523 self
.GenerateBtn
= xrc
.XRCCTRL(self
, "GenerateBtn")
1524 self
.SaveOptsBtn
= xrc
.XRCCTRL(self
, "SaveOptsBtn")
1526 self
.Bind(wx
.EVT_BUTTON
, self
.OnBrowse
, self
.BrowseBtn
)
1527 self
.Bind(wx
.EVT_BUTTON
, self
.OnGenerate
, self
.GenerateBtn
)
1528 self
.Bind(wx
.EVT_BUTTON
, self
.OnSaveOpts
, self
.SaveOptsBtn
)
1530 if self
.cfg
.Read("filename", "") != "":
1531 self
.FileNameTC
.SetValue(self
.cfg
.Read("filename"))
1533 name
= os
.path
.splitext(os
.path
.split(dataFile
)[1])[0]
1535 self
.FileNameTC
.SetValue(name
)
1536 self
.AutoGenerateCB
.SetValue(self
.cfg
.ReadBool("autogenerate", False))
1537 self
.EmbedCB
.SetValue(self
.cfg
.ReadBool("embedResource", False))
1538 self
.MakeXRSFileCB
.SetValue(self
.cfg
.ReadBool("makeXRS", False))
1539 self
.GettextCB
.SetValue(self
.cfg
.ReadBool("genGettext", False))
1542 def OnBrowse(self
, evt
):
1543 path
= self
.FileNameTC
.GetValue()
1544 dirname
= os
.path
.abspath(os
.path
.dirname(path
))
1545 name
= os
.path
.split(path
)[1]
1546 dlg
= wx
.FileDialog(self
, 'Save As', dirname
, name
, '*.py',
1547 wx
.SAVE | wx
.OVERWRITE_PROMPT
)
1548 if dlg
.ShowModal() == wx
.ID_OK
:
1549 path
= dlg
.GetPath()
1550 self
.FileNameTC
.SetValue(path
)
1554 def OnGenerate(self
, evt
):
1555 pypath
= self
.FileNameTC
.GetValue()
1556 embed
= self
.EmbedCB
.GetValue()
1557 genGettext
= self
.GettextCB
.GetValue()
1558 frame
.GeneratePython(self
.dataFile
, pypath
, embed
, genGettext
)
1562 def OnSaveOpts(self
, evt
=None):
1563 self
.cfg
.Write("filename", self
.FileNameTC
.GetValue())
1564 self
.cfg
.WriteBool("autogenerate", self
.AutoGenerateCB
.GetValue())
1565 self
.cfg
.WriteBool("embedResource", self
.EmbedCB
.GetValue())
1566 self
.cfg
.WriteBool("makeXRS", self
.MakeXRSFileCB
.GetValue())
1567 self
.cfg
.WriteBool("genGettext", self
.GettextCB
.GetValue())
1569 self
.EndModal(wx
.ID_OK
)
1572 ################################################################################
1575 print >> sys
.stderr
, 'usage: xrced [-dhiv] [file]'
1580 if wx
.VERSION
[:3] < MinWxVersion
:
1582 This version of XRCed may not work correctly on your version of wxWidgets. \
1583 Please upgrade wxWidgets to %d.%d.%d or higher.''' % MinWxVersion
)
1585 # Process comand-line
1588 opts
, args
= getopt
.getopt(sys
.argv
[1:], 'dhiv')
1596 print 'XRCed version', version
1599 except getopt
.GetoptError
:
1600 if wx
.Platform
!= '__WXMAC__': # macs have some extra parameters
1601 print >> sys
.stderr
, 'Unknown option'
1605 self
.SetAppName('xrced')
1608 conf
= g
.conf
= wx
.Config(style
= wx
.CONFIG_USE_LOCAL_FILE
)
1609 conf
.localconf
= None
1610 conf
.autoRefresh
= conf
.ReadInt('autorefresh', True)
1611 pos
= conf
.ReadInt('x', -1), conf
.ReadInt('y', -1)
1612 size
= conf
.ReadInt('width', 800), conf
.ReadInt('height', 600)
1613 conf
.embedPanel
= conf
.ReadInt('embedPanel', True)
1614 conf
.showTools
= conf
.ReadInt('showTools', True)
1615 conf
.sashPos
= conf
.ReadInt('sashPos', 200)
1616 # read recently used files
1617 recentfiles
=conf
.Read('recentFiles','')
1620 for fil
in recentfiles
.split('|'):
1621 conf
.recentfiles
[wx
.NewId()]=fil
1622 if not conf
.embedPanel
:
1623 conf
.panelX
= conf
.ReadInt('panelX', -1)
1624 conf
.panelY
= conf
.ReadInt('panelY', -1)
1626 conf
.panelX
= conf
.panelY
= -1
1627 conf
.panelWidth
= conf
.ReadInt('panelWidth', 200)
1628 conf
.panelHeight
= conf
.ReadInt('panelHeight', 200)
1629 conf
.panic
= not conf
.HasEntry('nopanic')
1631 wx
.FileSystem
.AddHandler(wx
.MemoryFSHandler())
1633 frame
= Frame(pos
, size
)
1637 plugins
= os
.getenv('XRCEDPATH')
1641 for dir in plugins
.split(':'):
1642 if os
.path
.isdir(dir) and \
1643 os
.path
.isfile(os
.path
.join(dir, '__init__.py')):
1645 dir = os
.path
.abspath(os
.path
.normpath(dir))
1646 sys
.path
= sys_path
+ [os
.path
.dirname(dir)]
1649 __import__(os
.path
.basename(dir), globals(), locals(), ['*'])
1651 print traceback
.print_exc()
1654 # Store important data
1655 frame
.handlers
= getHandlers()[:]
1656 frame
.custom
= g
.pullDownMenu
.custom
[:]
1657 frame
.modules
= set(sys
.modules
.keys())
1662 # Load file after showing
1665 frame
.open = frame
.Open(args
[0])
1673 wc
.WriteInt('autorefresh', conf
.autoRefresh
)
1674 wc
.WriteInt('x', conf
.x
)
1675 wc
.WriteInt('y', conf
.y
)
1676 wc
.WriteInt('width', conf
.width
)
1677 wc
.WriteInt('height', conf
.height
)
1678 wc
.WriteInt('embedPanel', conf
.embedPanel
)
1679 wc
.WriteInt('showTools', conf
.showTools
)
1680 if not conf
.embedPanel
:
1681 wc
.WriteInt('panelX', conf
.panelX
)
1682 wc
.WriteInt('panelY', conf
.panelY
)
1683 wc
.WriteInt('sashPos', conf
.sashPos
)
1684 wc
.WriteInt('panelWidth', conf
.panelWidth
)
1685 wc
.WriteInt('panelHeight', conf
.panelHeight
)
1686 wc
.WriteInt('nopanic', True)
1687 wc
.Write('recentFiles', '|'.join(conf
.recentfiles
.values()[-5:]))
1691 app
= App(0, useBestVisual
=False)
1692 #app.SetAssertMode(wx.PYAPP_ASSERT_LOG)
1698 if __name__
== '__main__':