]>
git.saurik.com Git - wxWidgets.git/blob - wxPython/wx/tools/XRCed/xrced.py
c68015a2ee9db8c66ea4e1510c9e0def54a6793f
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)
342 # Save sys.modules names
343 self
.modules
= set(sys
.modules
.keys())
349 wx
.EVT_IDLE(self
, self
.OnIdle
)
350 wx
.EVT_CLOSE(self
, self
.OnCloseWindow
)
351 wx
.EVT_KEY_DOWN(self
, tools
.OnKeyDown
)
352 wx
.EVT_KEY_UP(self
, tools
.OnKeyUp
)
353 wx
.EVT_ICONIZE(self
, self
.OnIconize
)
355 def AppendRecent(self
, menu
):
356 # add recently used files to the menu
357 for id,name
in conf
.recentfiles
.iteritems():
359 wx
.EVT_MENU(self
,id,self
.OnRecentFile
)
362 def OnRecentFile(self
,evt
):
363 # open recently used file
364 if not self
.AskSave(): return
367 path
=conf
.recentfiles
[evt
.GetId()]
369 self
.SetStatusText('Data loaded')
371 self
.SetStatusText('Failed')
373 self
.SetStatusText('No such file')
376 def OnNew(self
, evt
):
377 if not self
.AskSave(): return
380 def OnOpen(self
, evt
):
381 if not self
.AskSave(): return
382 dlg
= wx
.FileDialog(self
, 'Open', os
.path
.dirname(self
.dataFile
),
383 '', '*.xrc', wx
.OPEN | wx
.CHANGE_DIR
)
384 if dlg
.ShowModal() == wx
.ID_OK
:
386 self
.SetStatusText('Loading...')
390 self
.SetStatusText('Data loaded')
392 self
.SetStatusText('Failed')
393 self
.SaveRecent(path
)
398 def OnSaveOrSaveAs(self
, evt
):
399 if evt
.GetId() == wx
.ID_SAVEAS
or not self
.dataFile
:
400 if self
.dataFile
: name
= ''
401 else: name
= defaultName
402 dirname
= os
.path
.abspath(os
.path
.dirname(self
.dataFile
))
403 dlg
= wx
.FileDialog(self
, 'Save As', dirname
, name
, '*.xrc',
404 wx
.SAVE | wx
.OVERWRITE_PROMPT | wx
.CHANGE_DIR
)
405 if dlg
.ShowModal() == wx
.ID_OK
:
407 if isinstance(path
, unicode):
408 path
= path
.encode(sys
.getfilesystemencoding())
415 # if we already have a localconf then it needs to be
416 # copied to a new config with the new name
418 nc
= self
.CreateLocalConf(path
)
419 flag
, key
, idx
= lc
.GetFirstEntry()
421 nc
.Write(key
, lc
.Read(key
))
422 flag
, key
, idx
= lc
.GetNextEntry(idx
)
425 # otherwise create a new one
426 conf
.localconf
= self
.CreateLocalConf(path
)
429 self
.SetStatusText('Saving...')
433 tmpFile
,tmpName
= tempfile
.mkstemp(prefix
='xrced-')
435 self
.Save(tmpName
) # save temporary file first
436 shutil
.move(tmpName
, path
)
438 if conf
.localconf
.ReadBool("autogenerate", False):
439 pypath
= conf
.localconf
.Read("filename")
440 embed
= conf
.localconf
.ReadBool("embedResource", False)
441 genGettext
= conf
.localconf
.ReadBool("genGettext", False)
442 self
.GeneratePython(self
.dataFile
, pypath
, embed
, genGettext
)
444 self
.SetStatusText('Data saved')
445 self
.SaveRecent(path
)
447 self
.SetStatusText('Failed')
451 def SaveRecent(self
,path
):
452 # append to recently used files
453 if path
not in conf
.recentfiles
.values():
455 self
.recentMenu
.Append(newid
, path
)
456 wx
.EVT_MENU(self
, newid
, self
.OnRecentFile
)
457 conf
.recentfiles
[newid
] = path
459 def GeneratePython(self
, dataFile
, pypath
, embed
, genGettext
):
461 import wx
.tools
.pywxrc
462 rescomp
= wx
.tools
.pywxrc
.XmlResourceCompiler()
463 rescomp
.MakePythonModule([dataFile
], pypath
, embed
, genGettext
)
466 wx
.LogError(traceback
.format_exception(inf
[0], inf
[1], None)[-1])
467 wx
.LogError('Error generating python code : %s' % pypath
)
471 def OnGeneratePython(self
, evt
):
472 if self
.modified
or not conf
.localconf
:
473 wx
.MessageBox("Save the XRC file first!", "Error")
476 dlg
= PythonOptions(self
, conf
.localconf
, self
.dataFile
)
481 def OnExit(self
, evt
):
484 def OnUndo(self
, evt
):
485 # Extra check to not mess with idle updating
486 if undoMan
.CanUndo():
489 def OnRedo(self
, evt
):
490 if undoMan
.CanRedo():
493 def OnCopy(self
, evt
):
494 selected
= tree
.selection
495 if not selected
: return # key pressed event
496 xxx
= tree
.GetPyData(selected
)
497 if wx
.TheClipboard
.Open():
499 data
= wx
.CustomDataObject('XRCED')
500 # Set encoding in header
502 s
= xxx
.node
.toxml(encoding
=expat
.native_encoding
)
504 data
= wx
.CustomDataObject('XRCED_node')
506 data
.SetData(cPickle
.dumps(s
))
507 wx
.TheClipboard
.SetData(data
)
508 wx
.TheClipboard
.Close()
509 self
.SetStatusText('Copied')
511 wx
.MessageBox("Unable to open the clipboard", "Error")
513 def OnPaste(self
, evt
):
514 selected
= tree
.selection
515 if not selected
: return # key pressed event
516 # For pasting with Ctrl pressed
518 if evt
.GetId() == pullDownMenu
.ID_PASTE_SIBLING
: appendChild
= False
519 elif evt
.GetId() == self
.ID_TOOL_PASTE
:
520 if g
.tree
.ctrl
: appendChild
= False
521 else: appendChild
= not tree
.NeedInsert(selected
)
522 else: appendChild
= not tree
.NeedInsert(selected
)
523 xxx
= tree
.GetPyData(selected
)
525 # If has next item, insert, else append to parent
526 nextItem
= tree
.GetNextSibling(selected
)
527 parentLeaf
= tree
.GetItemParent(selected
)
528 # Expanded container (must have children)
529 elif tree
.IsExpanded(selected
) and tree
.GetChildrenCount(selected
, False):
530 # Insert as first child
531 nextItem
= tree
.GetFirstChild(selected
)[0]
532 parentLeaf
= selected
534 # No children or unexpanded item - appendChild stays True
535 nextItem
= wx
.TreeItemId() # no next item
536 parentLeaf
= selected
537 parent
= tree
.GetPyData(parentLeaf
).treeObject()
539 # Create a copy of clipboard pickled element
540 success
= success_node
= False
541 if wx
.TheClipboard
.Open():
543 data
= wx
.CustomDataObject('XRCED')
544 if wx
.TheClipboard
.IsSupported(data
.GetFormat()):
546 success
= wx
.TheClipboard
.GetData(data
)
548 # there is a problem if XRCED_node is in clipboard
549 # but previous SetData was for XRCED
551 if not success
: # try other format
552 data
= wx
.CustomDataObject('XRCED_node')
553 if wx
.TheClipboard
.IsSupported(data
.GetFormat()):
554 success_node
= wx
.TheClipboard
.GetData(data
)
556 wx
.TheClipboard
.Close()
558 if not success
and not success_node
:
560 "There is no data in the clipboard in the required format",
564 xml
= cPickle
.loads(data
.GetData()) # xml representation of element
566 elem
= minidom
.parseString(xml
).childNodes
[0]
568 elem
= g
.tree
.dom
.createComment(xml
)
570 # Tempopary xxx object to test things
571 xxx
= MakeXXXFromDOM(parent
, elem
)
573 # Check compatibility
574 if not self
.ItemsAreCompatible(parent
, xxx
.treeObject()): return
576 # Check parent and child relationships.
577 # If parent is sizer or notebook, child is of wrong class or
578 # parent is normal window, child is child container then detach child.
579 isChildContainer
= isinstance(xxx
, xxxChildContainer
)
580 parentIsBook
= parent
.__class
__ in [xxxNotebook
, xxxChoicebook
, xxxListbook
]
581 if isChildContainer
and \
582 ((parent
.isSizer
and not isinstance(xxx
, xxxSizerItem
)) or \
583 (parentIsBook
and not isinstance(xxx
, xxxPage
)) or \
584 not (parent
.isSizer
or parentIsBook
)):
585 elem
.removeChild(xxx
.child
.node
) # detach child
586 elem
.unlink() # delete child container
587 elem
= xxx
.child
.node
# replace
588 # This may help garbage collection
589 xxx
.child
.parent
= None
590 isChildContainer
= False
591 # Parent is sizer or notebook, child is not child container
592 if parent
.isSizer
and not isChildContainer
and not isinstance(xxx
, xxxSpacer
):
593 # Create sizer item element
594 sizerItemElem
= MakeEmptyDOM(parent
.itemTag
)
595 sizerItemElem
.appendChild(elem
)
597 elif isinstance(parent
, xxxNotebook
) and not isChildContainer
:
598 pageElem
= MakeEmptyDOM('notebookpage')
599 pageElem
.appendChild(elem
)
601 elif isinstance(parent
, xxxChoicebook
) and not isChildContainer
:
602 pageElem
= MakeEmptyDOM('choicebookpage')
603 pageElem
.appendChild(elem
)
605 elif isinstance(parent
, xxxListbook
) and not isChildContainer
:
606 pageElem
= MakeEmptyDOM('listbookpage')
607 pageElem
.appendChild(elem
)
609 # Insert new node, register undo
610 newItem
= tree
.InsertNode(parentLeaf
, parent
, elem
, nextItem
)
611 undoMan
.RegisterUndo(UndoPasteCreate(parentLeaf
, parent
, newItem
, selected
))
612 # Scroll to show new item (!!! redundant?)
613 tree
.EnsureVisible(newItem
)
614 tree
.SelectItem(newItem
)
615 if not tree
.IsVisible(newItem
):
616 tree
.ScrollTo(newItem
)
619 if g
.testWin
and tree
.IsHighlatable(newItem
):
621 tree
.needUpdate
= True
622 tree
.pendingHighLight
= newItem
624 tree
.pendingHighLight
= None
626 self
.SetStatusText('Pasted')
629 def ItemsAreCompatible(self
, parent
, child
):
630 # Check compatibility
632 # Comments are always compatible
633 if child
.__class
__ == xxxComment
:
636 if child
.__class
__ in [xxxDialog
, xxxFrame
, xxxWizard
]:
638 if parent
.__class
__ != xxxMainNode
: error
= True
639 elif child
.__class
__ == xxxMenuBar
:
640 # Menubar can be put in frame or dialog
641 if parent
.__class
__ not in [xxxMainNode
, xxxFrame
, xxxDialog
]: error
= True
642 elif child
.__class
__ == xxxToolBar
:
643 # Toolbar can be top-level of child of panel or frame
644 if parent
.__class
__ not in [xxxMainNode
, xxxPanel
, xxxFrame
] and \
645 not parent
.isSizer
: error
= True
646 elif child
.__class
__ == xxxPanel
and parent
.__class
__ == xxxMainNode
:
648 elif child
.__class
__ == xxxSpacer
:
649 if not parent
.isSizer
: error
= True
650 elif child
.__class
__ == xxxSeparator
:
651 if not parent
.__class
__ in [xxxMenu
, xxxToolBar
]: error
= True
652 elif child
.__class
__ == xxxTool
:
653 if parent
.__class
__ != xxxToolBar
: error
= True
654 elif child
.__class
__ == xxxMenu
:
655 if not parent
.__class
__ in [xxxMainNode
, xxxMenuBar
, xxxMenu
]: error
= True
656 elif child
.__class
__ == xxxMenuItem
:
657 if not parent
.__class
__ in [xxxMenuBar
, xxxMenu
]: error
= True
658 elif child
.isSizer
and parent
.__class
__ in [xxxNotebook
, xxxChoicebook
, xxxListbook
]:
660 else: # normal controls can be almost anywhere
661 if parent
.__class
__ == xxxMainNode
or \
662 parent
.__class
__ in [xxxMenuBar
, xxxMenu
]: error
= True
664 if parent
.__class
__ == xxxMainNode
: parentClass
= 'root'
665 else: parentClass
= parent
.className
666 wx
.LogError('Incompatible parent/child: parent is %s, child is %s!' %
667 (parentClass
, child
.className
))
671 def OnMoveUp(self
, evt
):
672 selected
= tree
.selection
673 if not selected
: return
675 index
= tree
.ItemIndex(selected
)
676 if index
== 0: return # No previous sibling found
679 self
.lastOp
= 'MOVEUP'
680 status
= 'Moved before previous sibling'
685 parent
= tree
.GetItemParent(selected
)
686 elem
= tree
.RemoveLeaf(selected
)
687 nextItem
= tree
.GetFirstChild(parent
)[0]
688 for i
in range(index
- 1): nextItem
= tree
.GetNextSibling(nextItem
)
689 selected
= tree
.InsertNode(parent
, tree
.GetPyData(parent
).treeObject(), elem
, nextItem
)
690 newIndex
= tree
.ItemIndex(selected
)
691 tree
.SelectItem(selected
)
693 undoMan
.RegisterUndo(UndoMove(parent
, index
, parent
, newIndex
))
696 self
.SetStatusText(status
)
700 def OnMoveDown(self
, evt
):
701 selected
= tree
.selection
702 if not selected
: return
704 index
= tree
.ItemIndex(selected
)
705 next
= tree
.GetNextSibling(selected
)
709 self
.lastOp
= 'MOVEDOWN'
710 status
= 'Moved after next sibling'
715 parent
= tree
.GetItemParent(selected
)
716 elem
= tree
.RemoveLeaf(selected
)
717 nextItem
= tree
.GetFirstChild(parent
)[0]
718 for i
in range(index
+ 1): nextItem
= tree
.GetNextSibling(nextItem
)
719 selected
= tree
.InsertNode(parent
, tree
.GetPyData(parent
).treeObject(), elem
, nextItem
)
720 newIndex
= tree
.ItemIndex(selected
)
721 tree
.SelectItem(selected
)
723 undoMan
.RegisterUndo(UndoMove(parent
, index
, parent
, newIndex
))
726 self
.SetStatusText(status
)
730 def OnMoveLeft(self
, evt
):
731 selected
= tree
.selection
732 if not selected
: return
734 oldParent
= tree
.GetItemParent(selected
)
735 if not oldParent
: return
736 pparent
= tree
.GetItemParent(oldParent
)
737 if not pparent
: return
739 # Check compatibility
740 if not self
.ItemsAreCompatible(tree
.GetPyData(pparent
).treeObject(), tree
.GetPyData(selected
).treeObject()): return
743 self
.lastOp
= 'MOVELEFT'
744 status
= 'Made next sibling of parent'
746 oldIndex
= tree
.ItemIndex(selected
)
747 elem
= tree
.RemoveLeaf(selected
)
748 nextItem
= tree
.GetFirstChild(pparent
)[0]
749 parentIndex
= tree
.ItemIndex(oldParent
)
750 for i
in range(parentIndex
+ 1): nextItem
= tree
.GetNextSibling(nextItem
)
752 # Check parent and child relationships.
753 # If parent is sizer or notebook, child is of wrong class or
754 # parent is normal window, child is child container then detach child.
755 parent
= tree
.GetPyData(pparent
).treeObject()
756 xxx
= MakeXXXFromDOM(parent
, elem
)
757 isChildContainer
= isinstance(xxx
, xxxChildContainer
)
758 if isChildContainer
and \
759 ((parent
.isSizer
and not isinstance(xxx
, xxxSizerItem
)) or \
760 (isinstance(parent
, xxxNotebook
) and not isinstance(xxx
, xxxNotebookPage
)) or \
761 not (parent
.isSizer
or isinstance(parent
, xxxNotebook
))):
762 elem
.removeChild(xxx
.child
.node
) # detach child
763 elem
.unlink() # delete child container
764 elem
= xxx
.child
.node
# replace
765 # This may help garbage collection
766 xxx
.child
.parent
= None
767 isChildContainer
= False
768 # Parent is sizer or notebook, child is not child container
769 if parent
.isSizer
and not isChildContainer
and not isinstance(xxx
, xxxSpacer
):
770 # Create sizer item element
771 sizerItemElem
= MakeEmptyDOM('sizeritem')
772 sizerItemElem
.appendChild(elem
)
774 elif isinstance(parent
, xxxNotebook
) and not isChildContainer
:
775 pageElem
= MakeEmptyDOM('notebookpage')
776 pageElem
.appendChild(elem
)
779 selected
= tree
.InsertNode(pparent
, tree
.GetPyData(pparent
).treeObject(), elem
, nextItem
)
780 newIndex
= tree
.ItemIndex(selected
)
781 tree
.SelectItem(selected
)
783 undoMan
.RegisterUndo(UndoMove(oldParent
, oldIndex
, pparent
, newIndex
))
786 self
.SetStatusText(status
)
788 def OnMoveRight(self
, evt
):
789 selected
= tree
.selection
790 if not selected
: return
792 oldParent
= tree
.GetItemParent(selected
)
793 if not oldParent
: return
795 newParent
= tree
.GetPrevSibling(selected
)
796 if not newParent
: return
798 parent
= tree
.GetPyData(newParent
).treeObject()
800 # Check compatibility
801 if not self
.ItemsAreCompatible(parent
, tree
.GetPyData(selected
).treeObject()): return
804 self
.lastOp
= 'MOVERIGHT'
805 status
= 'Made last child of previous sibling'
807 oldIndex
= tree
.ItemIndex(selected
)
808 elem
= tree
.RemoveLeaf(selected
)
810 # Check parent and child relationships.
811 # If parent is sizer or notebook, child is of wrong class or
812 # parent is normal window, child is child container then detach child.
813 xxx
= MakeXXXFromDOM(parent
, elem
)
814 isChildContainer
= isinstance(xxx
, xxxChildContainer
)
815 if isChildContainer
and \
816 ((parent
.isSizer
and not isinstance(xxx
, xxxSizerItem
)) or \
817 (isinstance(parent
, xxxNotebook
) and not isinstance(xxx
, xxxNotebookPage
)) or \
818 not (parent
.isSizer
or isinstance(parent
, xxxNotebook
))):
819 elem
.removeChild(xxx
.child
.node
) # detach child
820 elem
.unlink() # delete child container
821 elem
= xxx
.child
.node
# replace
822 # This may help garbage collection
823 xxx
.child
.parent
= None
824 isChildContainer
= False
825 # Parent is sizer or notebook, child is not child container
826 if parent
.isSizer
and not isChildContainer
and not isinstance(xxx
, xxxSpacer
):
827 # Create sizer item element
828 sizerItemElem
= MakeEmptyDOM('sizeritem')
829 sizerItemElem
.appendChild(elem
)
831 elif isinstance(parent
, xxxNotebook
) and not isChildContainer
:
832 pageElem
= MakeEmptyDOM('notebookpage')
833 pageElem
.appendChild(elem
)
836 selected
= tree
.InsertNode(newParent
, tree
.GetPyData(newParent
).treeObject(), elem
, wx
.TreeItemId())
838 newIndex
= tree
.ItemIndex(selected
)
839 tree
.SelectItem(selected
)
841 undoMan
.RegisterUndo(UndoMove(oldParent
, oldIndex
, newParent
, newIndex
))
844 self
.SetStatusText(status
)
847 def OnCutDelete(self
, evt
):
848 selected
= tree
.selection
849 if not selected
: return # key pressed event
851 if evt
.GetId() == wx
.ID_CUT
:
853 status
= 'Removed to clipboard'
855 self
.lastOp
= 'DELETE'
859 # If deleting top-level item, delete testWin
860 if selected
== g
.testWin
.item
:
864 # Remove highlight, update testWin
865 if g
.testWin
.highLight
:
866 g
.testWin
.highLight
.Remove()
867 tree
.needUpdate
= True
870 index
= tree
.ItemFullIndex(selected
)
871 xxx
= tree
.GetPyData(selected
)
872 parent
= tree
.GetPyData(tree
.GetItemParent(selected
)).treeObject()
873 elem
= tree
.RemoveLeaf(selected
)
874 undoMan
.RegisterUndo(UndoCutDelete(index
, parent
, elem
))
875 if evt
.GetId() == wx
.ID_CUT
:
876 if wx
.TheClipboard
.Open():
878 data
= wx
.CustomDataObject('XRCED')
880 s
= elem
.toxml(encoding
=expat
.native_encoding
)
882 data
= wx
.CustomDataObject('XRCED_node')
884 data
.SetData(cPickle
.dumps(s
))
885 wx
.TheClipboard
.SetData(data
)
886 wx
.TheClipboard
.Close()
888 wx
.MessageBox("Unable to open the clipboard", "Error")
889 tree
.pendingHighLight
= None
891 tree
.selection
= None
896 self
.SetStatusText(status
)
898 def OnSubclass(self
, evt
):
899 selected
= tree
.selection
900 xxx
= tree
.GetPyData(selected
).treeObject()
902 subclass
= xxx
.subclass
903 dlg
= wx
.TextEntryDialog(self
, 'Subclass:', defaultValue
=subclass
)
904 if dlg
.ShowModal() == wx
.ID_OK
:
905 subclass
= dlg
.GetValue()
907 elem
.setAttribute('subclass', subclass
)
908 elif elem
.hasAttribute('subclass'):
909 elem
.removeAttribute('subclass')
911 xxx
.subclass
= elem
.getAttribute('subclass')
912 tree
.SetItemText(selected
, xxx
.treeName())
913 panel
.pages
[0].box
.SetLabel(xxx
.panelName())
916 def OnEmbedPanel(self
, evt
):
917 conf
.embedPanel
= evt
.IsChecked()
919 # Remember last dimentions
920 conf
.panelX
, conf
.panelY
= self
.miniFrame
.GetPosition()
921 conf
.panelWidth
, conf
.panelHeight
= self
.miniFrame
.GetSize()
922 size
= self
.GetSize()
923 pos
= self
.GetPosition()
924 sizePanel
= panel
.GetSize()
925 panel
.Reparent(self
.splitter
)
926 self
.miniFrame
.GetSizer().Remove(panel
)
928 self
.SetDimensions(pos
.x
, pos
.y
, size
.width
+ sizePanel
.width
, size
.height
)
929 self
.splitter
.SplitVertically(tree
, panel
, conf
.sashPos
)
930 self
.miniFrame
.Show(False)
932 conf
.sashPos
= self
.splitter
.GetSashPosition()
933 pos
= self
.GetPosition()
934 size
= self
.GetSize()
935 sizePanel
= panel
.GetSize()
936 self
.splitter
.Unsplit(panel
)
937 sizer
= self
.miniFrame
.GetSizer()
938 panel
.Reparent(self
.miniFrame
)
940 sizer
.Add(panel
, 1, wx
.EXPAND
)
941 self
.miniFrame
.Show(True)
942 self
.miniFrame
.SetDimensions(conf
.panelX
, conf
.panelY
,
943 conf
.panelWidth
, conf
.panelHeight
)
944 self
.miniFrame
.Layout()
946 self
.SetDimensions(pos
.x
, pos
.y
,
947 max(size
.width
- sizePanel
.width
, self
.minWidth
), size
.height
)
949 def OnShowTools(self
, evt
):
950 conf
.showTools
= evt
.IsChecked()
951 g
.tools
.Show(conf
.showTools
)
953 self
.toolsSizer
.Prepend(g
.tools
, 0, wx
.EXPAND
)
955 self
.toolsSizer
.Remove(g
.tools
)
956 self
.toolsSizer
.Layout()
958 def OnTest(self
, evt
):
959 if not tree
.selection
: return # key pressed event
960 tree
.ShowTestWindow(tree
.selection
)
962 def OnTestHide(self
, evt
):
963 tree
.CloseTestWindow()
965 # Find object by relative position
966 def FindObject(self
, item
, obj
):
967 # We simply perform depth-first traversal, sinse it's too much
968 # hassle to deal with all sizer/window combinations
969 w
= tree
.FindNodeObject(item
)
970 if w
== obj
or isinstance(w
, wx
.GBSizerItem
) and w
.GetWindow() == obj
:
972 if tree
.ItemHasChildren(item
):
973 child
= tree
.GetFirstChild(item
)[0]
975 found
= self
.FindObject(child
, obj
)
976 if found
: return found
977 child
= tree
.GetNextSibling(child
)
980 def OnTestWinLeftDown(self
, evt
):
981 pos
= evt
.GetPosition()
982 self
.SetHandler(g
.testWin
)
983 g
.testWin
.Disconnect(wx
.ID_ANY
, wx
.ID_ANY
, wx
.wxEVT_LEFT_DOWN
)
984 item
= self
.FindObject(g
.testWin
.item
, evt
.GetEventObject())
986 tree
.EnsureVisible(item
)
987 tree
.SelectItem(item
)
988 self
.tb
.ToggleTool(self
.ID_TOOL_LOCATE
, False)
990 self
.SetStatusText('Selected %s' % tree
.GetItemText(item
))
992 self
.SetStatusText('Locate failed!')
994 def SetHandler(self
, w
, h
=None):
997 w
.SetCursor(wx
.CROSS_CURSOR
)
1000 w
.SetCursor(wx
.NullCursor
)
1001 for ch
in w
.GetChildren():
1002 self
.SetHandler(ch
, h
)
1004 def OnLocate(self
, evt
):
1006 if evt
.GetId() == self
.ID_LOCATE
or \
1007 evt
.GetId() == self
.ID_TOOL_LOCATE
and evt
.IsChecked():
1008 self
.SetHandler(g
.testWin
, g
.testWin
)
1009 g
.testWin
.Connect(wx
.ID_ANY
, wx
.ID_ANY
, wx
.wxEVT_LEFT_DOWN
, self
.OnTestWinLeftDown
)
1010 if evt
.GetId() == self
.ID_LOCATE
:
1011 self
.tb
.ToggleTool(self
.ID_TOOL_LOCATE
, True)
1012 elif evt
.GetId() == self
.ID_TOOL_LOCATE
and not evt
.IsChecked():
1013 self
.SetHandler(g
.testWin
, None)
1014 g
.testWin
.Disconnect(wx
.ID_ANY
, wx
.ID_ANY
, wx
.wxEVT_LEFT_DOWN
)
1015 self
.SetStatusText('Click somewhere in your test window now')
1017 def OnRefresh(self
, evt
):
1018 # If modified, apply first
1019 selection
= tree
.selection
1021 xxx
= tree
.GetPyData(selection
)
1022 if xxx
and panel
.IsModified():
1023 tree
.Apply(xxx
, selection
)
1026 tree
.CreateTestWin(g
.testWin
.item
)
1027 panel
.modified
= False
1028 tree
.needUpdate
= False
1030 def OnAutoRefresh(self
, evt
):
1031 conf
.autoRefresh
= evt
.IsChecked()
1032 self
.menuBar
.Check(self
.ID_AUTO_REFRESH
, conf
.autoRefresh
)
1033 self
.tb
.ToggleTool(self
.ID_AUTO_REFRESH
, conf
.autoRefresh
)
1035 def OnAbout(self
, evt
):
1039 (c) Roman Rolinsky <rollrom@users.sourceforge.net>
1040 Homepage: http://xrced.sourceforge.net\
1042 dlg
= wx
.MessageDialog(self
, str, 'About XRCed', wx
.OK | wx
.CENTRE
)
1046 def OnReadme(self
, evt
):
1047 text
= open(os
.path
.join(basePath
, 'README.txt'), 'r').read()
1048 dlg
= ScrolledMessageDialog(self
, text
, "XRCed README")
1052 # Simple emulation of python command line
1053 def OnDebugCMD(self
, evt
):
1056 exec raw_input('C:\> ')
1061 (etype
, value
, tb
) =sys
.exc_info()
1062 tblist
=traceback
.extract_tb(tb
)[1:]
1063 msg
=' '.join(traceback
.format_exception_only(etype
, value
)
1064 +traceback
.format_list(tblist
))
1067 def OnCreate(self
, evt
):
1068 selected
= tree
.selection
1069 if tree
.ctrl
: appendChild
= False
1070 else: appendChild
= not tree
.NeedInsert(selected
)
1071 xxx
= tree
.GetPyData(selected
)
1075 # If has previous item, insert after it, else append to parent
1077 parentLeaf
= tree
.GetItemParent(selected
)
1079 # If has next item, insert, else append to parent
1080 nextItem
= tree
.GetNextSibling(selected
)
1081 parentLeaf
= tree
.GetItemParent(selected
)
1082 # Expanded container (must have children)
1083 elif tree
.shift
and tree
.IsExpanded(selected
) \
1084 and tree
.GetChildrenCount(selected
, False):
1085 nextItem
= tree
.GetFirstChild(selected
)[0]
1086 parentLeaf
= selected
1088 nextItem
= wx
.TreeItemId()
1089 parentLeaf
= selected
1090 parent
= tree
.GetPyData(parentLeaf
)
1091 if parent
.hasChild
: parent
= parent
.child
1093 # Create object_ref?
1094 if evt
.GetId() == ID_NEW
.REF
:
1095 ref
= wx
.GetTextFromUser('Create reference to:', 'Create reference')
1097 xxx
= MakeEmptyRefXXX(parent
, ref
)
1098 elif evt
.GetId() == ID_NEW
.COMMENT
:
1099 xxx
= MakeEmptyCommentXXX(parent
)
1101 # Create empty element
1102 if evt
.GetId() >= ID_NEW
.CUSTOM
:
1103 className
= pullDownMenu
.customMap
[evt
.GetId()]
1105 className
= pullDownMenu
.createMap
[evt
.GetId()]
1106 xxx
= MakeEmptyXXX(parent
, className
)
1108 # Insert new node, register undo
1109 if xxx
.isElement
: # true object
1110 # Set default name for top-level windows
1111 if parent
.__class
__ == xxxMainNode
:
1112 cl
= xxx
.treeObject().__class
__
1113 frame
.maxIDs
[cl
] += 1
1114 xxx
.setTreeName('%s%d' % (defaultIDs
[cl
], frame
.maxIDs
[cl
]))
1115 # And for some other standard controls
1116 elif parent
.__class
__ == xxxStdDialogButtonSizer
:
1117 xxx
.setTreeName(pullDownMenu
.stdButtonIDs
[evt
.GetId()][0])
1118 # We can even set label
1119 obj
= xxx
.treeObject()
1120 elem
= g
.tree
.dom
.createElement('label')
1121 elem
.appendChild(g
.tree
.dom
.createTextNode(pullDownMenu
.stdButtonIDs
[evt
.GetId()][1]))
1122 obj
.params
['label'] = xxxParam(elem
)
1123 xxx
.treeObject().node
.appendChild(elem
)
1125 newItem
= tree
.InsertNode(parentLeaf
, parent
, xxx
.node
, nextItem
)
1126 else: # comment node
1127 newItem
= tree
.InsertNode(parentLeaf
, parent
, xxx
.node
, nextItem
)
1128 undoMan
.RegisterUndo(UndoPasteCreate(parentLeaf
, parent
, newItem
, selected
))
1129 tree
.EnsureVisible(newItem
)
1130 tree
.SelectItem(newItem
)
1131 if not tree
.IsVisible(newItem
):
1132 tree
.ScrollTo(newItem
)
1135 if xxx
.isElement
and g
.testWin
and tree
.IsHighlatable(newItem
):
1136 if conf
.autoRefresh
:
1137 tree
.needUpdate
= True
1138 tree
.pendingHighLight
= newItem
1140 tree
.pendingHighLight
= None
1142 if not xxx
.isElement
:
1143 tree
.EditLabel(newItem
)
1146 # Replace one object with another
1147 def OnReplace(self
, evt
):
1148 selected
= tree
.selection
1149 xxx
= tree
.GetPyData(selected
).treeObject()
1151 parent
= elem
.parentNode
1152 undoMan
.RegisterUndo(UndoReplace(selected
))
1154 className
= pullDownMenu
.createMap
[evt
.GetId() - 1000]
1156 # Create temporary empty node (with default values)
1157 dummy
= MakeEmptyDOM(className
)
1158 if className
== 'spacer' and xxx
.className
!= 'spacer':
1160 elif xxx
.className
== 'spacer' and className
!= 'spacer':
1161 klass
= xxxSizerItem
1163 klass
= xxxDict
[className
]
1164 # Remove non-compatible children
1165 if tree
.ItemHasChildren(selected
) and not klass
.hasChildren
:
1166 tree
.DeleteChildren(selected
)
1167 nodes
= elem
.childNodes
[:]
1170 if node
.nodeType
!= minidom
.Node
.ELEMENT_NODE
: continue
1174 if not klass
.hasChildren
: remove
= True
1175 elif tag
not in klass
.allParams
and \
1176 (not klass
.hasStyle
or tag
not in klass
.styles
):
1181 elem
.removeChild(node
)
1184 # Remove sizeritem child if spacer
1185 if className
== 'spacer' and xxx
.className
!= 'spacer':
1186 sizeritem
= elem
.parentNode
1187 assert sizeritem
.getAttribute('class') == 'sizeritem'
1188 sizeritem
.removeChild(elem
)
1191 tree
.GetPyData(selected
).hasChild
= False
1192 elif xxx
.className
== 'spacer' and className
!= 'spacer':
1193 # Create sizeritem element
1194 assert xxx
.parent
.isSizer
1195 elem
.setAttribute('class', 'sizeritem')
1196 node
= MakeEmptyDOM(className
)
1197 elem
.appendChild(node
)
1198 # Replace to point to new object
1199 xxx
= xxxSizerItem(xxx
.parent
, elem
)
1201 tree
.SetPyData(selected
, xxx
)
1204 # Copy parameters present in dummy but not in elem
1205 for node
in dummy
.childNodes
:
1206 if node
.tagName
not in tags
: elem
.appendChild(node
.cloneNode(True))
1210 elem
.setAttribute('class', className
)
1211 if elem
.hasAttribute('subclass'):
1212 elem
.removeAttribute('subclass') # clear subclassing
1213 # Re-create xxx element
1214 xxx
= MakeXXXFromDOM(xxx
.parent
, elem
)
1215 # Remove incompatible style flags
1216 if 'style' in xxx
.params
:
1217 styles
= map(string
.strip
, xxx
.params
['style'].value().split('|'))
1218 newStyles
= [s
for s
in styles
if s
in klass
.winStyles
or s
in genericStyles
]
1219 if newStyles
!= styles
:
1221 value
= reduce(lambda a
,b
: a
+'|'+b
, newStyles
)
1224 xxx
.params
['style'].update(value
)
1226 # Update parent in child objects
1227 if tree
.ItemHasChildren(selected
):
1228 i
, cookie
= tree
.GetFirstChild(selected
)
1230 x
= tree
.GetPyData(i
)
1232 if x
.hasChild
: x
.child
.parent
= xxx
1233 i
, cookie
= tree
.GetNextChild(selected
, cookie
)
1236 if tree
.GetPyData(selected
).hasChild
: # child container
1237 container
= tree
.GetPyData(selected
)
1238 container
.resetChild(xxx
)
1241 tree
.SetPyData(selected
, xxx
)
1242 tree
.SetItemText(selected
, xxx
.treeName())
1243 tree
.SetItemImage(selected
, xxx
.treeImage())
1245 # Set default name for top-level windows
1246 if parent
.__class
__ == xxxMainNode
:
1247 cl
= xxx
.treeObject().__class
__
1248 frame
.maxIDs
[cl
] += 1
1249 xxx
.setTreeName('%s%d' % (defaultIDs
[cl
], frame
.maxIDs
[cl
]))
1252 g
.panel
.SetData(xxx
)
1256 #undoMan.RegisterUndo(UndoPasteCreate(parentLeaf, parent, newItem, selected))
1258 if g
.testWin
and tree
.IsHighlatable(selected
):
1259 if conf
.autoRefresh
:
1260 tree
.needUpdate
= True
1261 tree
.pendingHighLight
= selected
1263 tree
.pendingHighLight
= None
1267 # Expand/collapse subtree
1268 def OnExpand(self
, evt
):
1269 if tree
.selection
: tree
.ExpandAll(tree
.selection
)
1270 else: tree
.ExpandAll(tree
.root
)
1271 def OnCollapse(self
, evt
):
1272 if tree
.selection
: tree
.CollapseAll(tree
.selection
)
1273 else: tree
.CollapseAll(tree
.root
)
1275 def OnPullDownHighlight(self
, evt
):
1276 menuId
= evt
.GetMenuId()
1278 menu
= evt
.GetEventObject()
1279 help = menu
.GetHelpString(menuId
)
1280 self
.SetStatusText(help)
1282 self
.SetStatusText('')
1284 def OnUpdateUI(self
, evt
):
1285 if evt
.GetId() in [wx
.ID_CUT
, wx
.ID_COPY
, self
.ID_DELETE
]:
1286 evt
.Enable(tree
.selection
is not None and tree
.selection
!= tree
.root
)
1287 elif evt
.GetId() == wx
.ID_SAVE
:
1288 evt
.Enable(self
.modified
)
1289 elif evt
.GetId() in [wx
.ID_PASTE
, self
.ID_TOOL_PASTE
]:
1290 evt
.Enable(tree
.selection
is not None)
1291 elif evt
.GetId() == self
.ID_TEST
:
1292 evt
.Enable(tree
.selection
is not None and tree
.selection
!= tree
.root
)
1293 elif evt
.GetId() in [self
.ID_LOCATE
, self
.ID_TOOL_LOCATE
]:
1294 evt
.Enable(g
.testWin
is not None)
1295 elif evt
.GetId() == wx
.ID_UNDO
: evt
.Enable(undoMan
.CanUndo())
1296 elif evt
.GetId() == wx
.ID_REDO
: evt
.Enable(undoMan
.CanRedo())
1298 def OnIdle(self
, evt
):
1299 if self
.inIdle
: return # Recursive call protection
1303 if conf
.autoRefresh
:
1305 self
.SetStatusText('Refreshing test window...')
1307 tree
.CreateTestWin(g
.testWin
.item
)
1308 self
.SetStatusText('')
1309 tree
.needUpdate
= False
1310 elif tree
.pendingHighLight
:
1312 tree
.HighLight(tree
.pendingHighLight
)
1314 # Remove highlight if any problem
1315 if g
.testWin
.highLight
:
1316 g
.testWin
.highLight
.Remove()
1317 tree
.pendingHighLight
= None
1324 # We don't let close panel window
1325 def OnCloseMiniFrame(self
, evt
):
1328 def OnIconize(self
, evt
):
1330 conf
.x
, conf
.y
= self
.GetPosition()
1331 conf
.width
, conf
.height
= self
.GetSize()
1333 conf
.sashPos
= self
.splitter
.GetSashPosition()
1335 conf
.panelX
, conf
.panelY
= self
.miniFrame
.GetPosition()
1336 conf
.panelWidth
, conf
.panelHeight
= self
.miniFrame
.GetSize()
1337 self
.miniFrame
.Iconize()
1339 if not conf
.embedPanel
:
1340 self
.miniFrame
.Iconize(False)
1343 def OnCloseWindow(self
, evt
):
1344 if not self
.AskSave(): return
1345 if g
.testWin
: g
.testWin
.Destroy()
1346 if not panel
.GetPageCount() == 2:
1347 panel
.page2
.Destroy()
1349 # If we don't do this, page does not get destroyed (a bug?)
1351 if not self
.IsIconized():
1352 conf
.x
, conf
.y
= self
.GetPosition()
1353 conf
.width
, conf
.height
= self
.GetSize()
1355 conf
.sashPos
= self
.splitter
.GetSashPosition()
1357 conf
.panelX
, conf
.panelY
= self
.miniFrame
.GetPosition()
1358 conf
.panelWidth
, conf
.panelHeight
= self
.miniFrame
.GetSize()
1362 def CreateLocalConf(self
, path
):
1363 name
= os
.path
.splitext(path
)[0]
1365 return wx
.FileConfig(localFilename
=name
)
1370 conf
.localconf
= None
1372 self
.SetModified(False)
1378 # Numbers for new controls
1380 for cl
in [xxxPanel
, xxxDialog
, xxxFrame
,
1381 xxxMenuBar
, xxxMenu
, xxxToolBar
,
1382 xxxWizard
, xxxBitmap
, xxxIcon
]:
1386 g
.pullDownMenu
.clearCustom()
1387 # Delete modules imported from comment directives
1388 map(sys
.modules
.pop
, [m
for m
in sys
.modules
if m
not in self
.modules
])
1390 def SetModified(self
, state
=True):
1391 self
.modified
= state
1392 name
= os
.path
.basename(self
.dataFile
)
1393 if not name
: name
= defaultName
1395 self
.SetTitle(progname
+ ': ' + name
+ ' *')
1397 self
.SetTitle(progname
+ ': ' + name
)
1399 def Open(self
, path
):
1400 if not os
.path
.exists(path
):
1401 wx
.LogError('File does not exists: %s' % path
)
1403 # Try to read the file
1407 dom
= minidom
.parse(f
)
1409 # Set encoding global variable and default encoding
1411 g
.currentEncoding
= dom
.encoding
1412 wx
.SetDefaultPyEncoding(g
.currentEncoding
.encode())
1414 g
.currentEncoding
= ''
1416 self
.dataFile
= path
= os
.path
.abspath(path
)
1417 dir = os
.path
.dirname(path
)
1418 if dir: os
.chdir(dir)
1419 # Allow importing modules from the same directory
1420 sys
.path
= sys_path
+ [dir]
1422 self
.SetTitle(progname
+ ': ' + os
.path
.basename(path
))
1423 conf
.localconf
= self
.CreateLocalConf(self
.dataFile
)
1425 # Nice exception printing
1426 inf
= sys
.exc_info()
1427 wx
.LogError(traceback
.format_exception(inf
[0], inf
[1], None)[-1])
1428 wx
.LogError('Error reading file: %s' % path
)
1433 def Indent(self
, node
, indent
= 0):
1434 if node
.nodeType
== minidom
.Node
.COMMENT_NODE
:
1435 text
= self
.domCopy
.createTextNode('\n' + ' ' * indent
)
1436 node
.parentNode
.insertBefore(text
, node
)
1437 return # no children
1438 # Copy child list because it will change soon
1439 children
= node
.childNodes
[:]
1440 # Main node doesn't need to be indented
1442 text
= self
.domCopy
.createTextNode('\n' + ' ' * indent
)
1443 node
.parentNode
.insertBefore(text
, node
)
1445 # Append newline after last child, except for text nodes
1446 if children
[-1].nodeType
== minidom
.Node
.ELEMENT_NODE
:
1447 text
= self
.domCopy
.createTextNode('\n' + ' ' * indent
)
1448 node
.appendChild(text
)
1449 # Indent children which are elements
1451 if n
.nodeType
== minidom
.Node
.ELEMENT_NODE
or \
1452 n
.nodeType
== minidom
.Node
.COMMENT_NODE
:
1453 self
.Indent(n
, indent
+ 2)
1455 def Save(self
, path
):
1459 if tree
.selection
and panel
.IsModified():
1460 self
.OnRefresh(wx
.CommandEvent())
1461 if g
.currentEncoding
:
1462 f
= codecs
.open(path
, 'wt', g
.currentEncoding
)
1464 f
= codecs
.open(path
, 'wt')
1465 # Make temporary copy for formatting it
1466 # !!! We can't clone dom node, it works only once
1467 #self.domCopy = tree.dom.cloneNode(True)
1468 self
.domCopy
= MyDocument()
1469 mainNode
= self
.domCopy
.appendChild(tree
.mainNode
.cloneNode(True))
1470 # Remove first child (test element)
1471 testElem
= mainNode
.firstChild
1472 mainNode
.removeChild(testElem
)
1474 self
.Indent(mainNode
)
1475 self
.domCopy
.writexml(f
, encoding
= g
.currentEncoding
)
1477 self
.domCopy
.unlink()
1479 self
.SetModified(False)
1480 panel
.SetModified(False)
1481 conf
.localconf
.Flush()
1483 inf
= sys
.exc_info()
1484 wx
.LogError(traceback
.format_exception(inf
[0], inf
[1], None)[-1])
1485 wx
.LogError('Error writing file: %s' % path
)
1489 if not (self
.modified
or panel
.IsModified()): return True
1490 flags
= wx
.ICON_EXCLAMATION | wx
.YES_NO | wx
.CANCEL | wx
.CENTRE
1491 dlg
= wx
.MessageDialog( self
, 'File is modified. Save before exit?',
1492 'Save before too late?', flags
)
1493 say
= dlg
.ShowModal()
1496 if say
== wx
.ID_YES
:
1497 self
.OnSaveOrSaveAs(wx
.CommandEvent(wx
.ID_SAVE
))
1498 # If save was successful, modified flag is unset
1499 if not self
.modified
: return True
1500 elif say
== wx
.ID_NO
:
1501 self
.SetModified(False)
1502 panel
.SetModified(False)
1509 ################################################################################
1511 class PythonOptions(wx
.Dialog
):
1513 def __init__(self
, parent
, cfg
, dataFile
):
1514 pre
= wx
.PreDialog()
1515 g
.frame
.res
.LoadOnDialog(pre
, parent
, "PYTHON_OPTIONS")
1516 self
.PostCreate(pre
)
1519 self
.dataFile
= dataFile
1521 self
.AutoGenerateCB
= xrc
.XRCCTRL(self
, "AutoGenerateCB")
1522 self
.EmbedCB
= xrc
.XRCCTRL(self
, "EmbedCB")
1523 self
.GettextCB
= xrc
.XRCCTRL(self
, "GettextCB")
1524 self
.MakeXRSFileCB
= xrc
.XRCCTRL(self
, "MakeXRSFileCB")
1525 self
.FileNameTC
= xrc
.XRCCTRL(self
, "FileNameTC")
1526 self
.BrowseBtn
= xrc
.XRCCTRL(self
, "BrowseBtn")
1527 self
.GenerateBtn
= xrc
.XRCCTRL(self
, "GenerateBtn")
1528 self
.SaveOptsBtn
= xrc
.XRCCTRL(self
, "SaveOptsBtn")
1530 self
.Bind(wx
.EVT_BUTTON
, self
.OnBrowse
, self
.BrowseBtn
)
1531 self
.Bind(wx
.EVT_BUTTON
, self
.OnGenerate
, self
.GenerateBtn
)
1532 self
.Bind(wx
.EVT_BUTTON
, self
.OnSaveOpts
, self
.SaveOptsBtn
)
1534 if self
.cfg
.Read("filename", "") != "":
1535 self
.FileNameTC
.SetValue(self
.cfg
.Read("filename"))
1537 name
= os
.path
.splitext(os
.path
.split(dataFile
)[1])[0]
1539 self
.FileNameTC
.SetValue(name
)
1540 self
.AutoGenerateCB
.SetValue(self
.cfg
.ReadBool("autogenerate", False))
1541 self
.EmbedCB
.SetValue(self
.cfg
.ReadBool("embedResource", False))
1542 self
.MakeXRSFileCB
.SetValue(self
.cfg
.ReadBool("makeXRS", False))
1543 self
.GettextCB
.SetValue(self
.cfg
.ReadBool("genGettext", False))
1546 def OnBrowse(self
, evt
):
1547 path
= self
.FileNameTC
.GetValue()
1548 dirname
= os
.path
.abspath(os
.path
.dirname(path
))
1549 name
= os
.path
.split(path
)[1]
1550 dlg
= wx
.FileDialog(self
, 'Save As', dirname
, name
, '*.py',
1551 wx
.SAVE | wx
.OVERWRITE_PROMPT
)
1552 if dlg
.ShowModal() == wx
.ID_OK
:
1553 path
= dlg
.GetPath()
1554 self
.FileNameTC
.SetValue(path
)
1558 def OnGenerate(self
, evt
):
1559 pypath
= self
.FileNameTC
.GetValue()
1560 embed
= self
.EmbedCB
.GetValue()
1561 genGettext
= self
.GettextCB
.GetValue()
1562 frame
.GeneratePython(self
.dataFile
, pypath
, embed
, genGettext
)
1566 def OnSaveOpts(self
, evt
=None):
1567 self
.cfg
.Write("filename", self
.FileNameTC
.GetValue())
1568 self
.cfg
.WriteBool("autogenerate", self
.AutoGenerateCB
.GetValue())
1569 self
.cfg
.WriteBool("embedResource", self
.EmbedCB
.GetValue())
1570 self
.cfg
.WriteBool("makeXRS", self
.MakeXRSFileCB
.GetValue())
1571 self
.cfg
.WriteBool("genGettext", self
.GettextCB
.GetValue())
1573 self
.EndModal(wx
.ID_OK
)
1576 ################################################################################
1579 print >> sys
.stderr
, 'usage: xrced [-dhiv] [file]'
1584 if wx
.VERSION
[:3] < MinWxVersion
:
1586 This version of XRCed may not work correctly on your version of wxWidgets. \
1587 Please upgrade wxWidgets to %d.%d.%d or higher.''' % MinWxVersion
)
1589 # Process comand-line
1592 opts
, args
= getopt
.getopt(sys
.argv
[1:], 'dhiv')
1600 print 'XRCed version', version
1603 except getopt
.GetoptError
:
1604 if wx
.Platform
!= '__WXMAC__': # macs have some extra parameters
1605 print >> sys
.stderr
, 'Unknown option'
1609 self
.SetAppName('xrced')
1612 conf
= g
.conf
= wx
.Config(style
= wx
.CONFIG_USE_LOCAL_FILE
)
1613 conf
.localconf
= None
1614 conf
.autoRefresh
= conf
.ReadInt('autorefresh', True)
1615 pos
= conf
.ReadInt('x', -1), conf
.ReadInt('y', -1)
1616 size
= conf
.ReadInt('width', 800), conf
.ReadInt('height', 600)
1617 conf
.embedPanel
= conf
.ReadInt('embedPanel', True)
1618 conf
.showTools
= conf
.ReadInt('showTools', True)
1619 conf
.sashPos
= conf
.ReadInt('sashPos', 200)
1620 # read recently used files
1621 recentfiles
=conf
.Read('recentFiles','')
1624 for fil
in recentfiles
.split('|'):
1625 conf
.recentfiles
[wx
.NewId()]=fil
1626 if not conf
.embedPanel
:
1627 conf
.panelX
= conf
.ReadInt('panelX', -1)
1628 conf
.panelY
= conf
.ReadInt('panelY', -1)
1630 conf
.panelX
= conf
.panelY
= -1
1631 conf
.panelWidth
= conf
.ReadInt('panelWidth', 200)
1632 conf
.panelHeight
= conf
.ReadInt('panelHeight', 200)
1633 conf
.panic
= not conf
.HasEntry('nopanic')
1635 wx
.FileSystem
.AddHandler(wx
.MemoryFSHandler())
1637 frame
= Frame(pos
, size
)
1640 # Load file after showing
1643 frame
.open = frame
.Open(args
[0])
1651 wc
.WriteInt('autorefresh', conf
.autoRefresh
)
1652 wc
.WriteInt('x', conf
.x
)
1653 wc
.WriteInt('y', conf
.y
)
1654 wc
.WriteInt('width', conf
.width
)
1655 wc
.WriteInt('height', conf
.height
)
1656 wc
.WriteInt('embedPanel', conf
.embedPanel
)
1657 wc
.WriteInt('showTools', conf
.showTools
)
1658 if not conf
.embedPanel
:
1659 wc
.WriteInt('panelX', conf
.panelX
)
1660 wc
.WriteInt('panelY', conf
.panelY
)
1661 wc
.WriteInt('sashPos', conf
.sashPos
)
1662 wc
.WriteInt('panelWidth', conf
.panelWidth
)
1663 wc
.WriteInt('panelHeight', conf
.panelHeight
)
1664 wc
.WriteInt('nopanic', True)
1665 wc
.Write('recentFiles', '|'.join(conf
.recentfiles
.values()[-5:]))
1669 app
= App(0, useBestVisual
=False)
1670 #app.SetAssertMode(wx.PYAPP_ASSERT_LOG)
1676 if __name__
== '__main__':