]> git.saurik.com Git - wxWidgets.git/blob - wxPython/wx/tools/XRCed/xrced.py
editing comments by editing tree label
[wxWidgets.git] / wxPython / wx / tools / XRCed / xrced.py
1 # Name: xrced.py
2 # Purpose: XRC editor, main module
3 # Author: Roman Rolinsky <rolinsky@mema.ucl.ac.be>
4 # Created: 20.08.2001
5 # RCS-ID: $Id$
6
7 """
8
9 xrced -- Simple resource editor for XRC format used by wxWidgets/wxPython
10 GUI toolkit.
11
12 Usage:
13
14 xrced [ -h ] [ -v ] [ XRC-file ]
15
16 Options:
17
18 -h output short usage info and exit
19
20 -v output version info and exit
21 """
22
23 from globals import *
24 import os, sys, getopt, re, traceback, tempfile, shutil, cPickle
25 from xml.parsers import expat
26
27 # Local modules
28 from tree import * # imports xxx which imports params
29 from panel import *
30 from tools import *
31 from params import genericStyles
32 # Cleanup recursive import sideeffects, otherwise we can't create undoMan
33 import undo
34 undo.ParamPage = ParamPage
35 undoMan = g.undoMan = UndoManager()
36
37 # Set application path for loading resources
38 if __name__ == '__main__':
39 basePath = os.path.dirname(sys.argv[0])
40 else:
41 basePath = os.path.dirname(__file__)
42
43 # 1 adds CMD command to Help menu
44 debug = 0
45
46 g.helpText = """\
47 <HTML><H2>Welcome to XRC<font color="blue">ed</font></H2><H3><font color="green">DON'T PANIC :)</font></H3>
48 Read this note before clicking on anything!<P>
49 To start select tree root, then popup menu with your right mouse button,
50 select "Append Child", and then any command.<P>
51 Or just press one of the buttons on the tools palette.<P>
52 Enter XML ID, change properties, create children.<P>
53 To test your interface select Test command (View menu).<P>
54 Consult README file for the details.</HTML>
55 """
56
57 defaultIDs = {xxxPanel:'PANEL', xxxDialog:'DIALOG', xxxFrame:'FRAME',
58 xxxMenuBar:'MENUBAR', xxxMenu:'MENU', xxxToolBar:'TOOLBAR',
59 xxxWizard:'WIZARD', xxxBitmap:'BITMAP', xxxIcon:'ICON'}
60
61 defaultName = 'UNTITLED.xrc'
62
63 ################################################################################
64
65 # ScrolledMessageDialog - modified from wxPython lib to set fixed-width font
66 class ScrolledMessageDialog(wx.Dialog):
67 def __init__(self, parent, msg, caption, pos = wx.DefaultPosition, size = (500,300)):
68 from wx.lib.layoutf import Layoutf
69 wx.Dialog.__init__(self, parent, -1, caption, pos, size)
70 text = wx.TextCtrl(self, -1, msg, wx.DefaultPosition,
71 wx.DefaultSize, wx.TE_MULTILINE | wx.TE_READONLY)
72 text.SetFont(g.modernFont())
73 dc = wx.WindowDC(text)
74 # !!! possible bug - GetTextExtent without font returns sysfont dims
75 w, h = dc.GetFullTextExtent(' ', g.modernFont())[:2]
76 ok = wx.Button(self, wx.ID_OK, "OK")
77 text.SetConstraints(Layoutf('t=t5#1;b=t5#2;l=l5#1;r=r5#1', (self,ok)))
78 text.SetSize((w * 80 + 30, h * 40))
79 text.ShowPosition(1)
80 ok.SetConstraints(Layoutf('b=b5#1;x%w50#1;w!80;h!25', (self,)))
81 self.SetAutoLayout(True)
82 self.Fit()
83 self.CenterOnScreen(wx.BOTH)
84
85 ################################################################################
86
87 # Event handler for using during location
88 class Locator(wx.EvtHandler):
89 def ProcessEvent(self, evt):
90 print evt
91
92 class Frame(wx.Frame):
93 def __init__(self, pos, size):
94 wx.Frame.__init__(self, None, -1, '', pos, size)
95 global frame
96 frame = g.frame = self
97 bar = self.CreateStatusBar(2)
98 bar.SetStatusWidths([-1, 40])
99 self.SetIcon(images.getIconIcon())
100
101 # Idle flag
102 self.inIdle = False
103
104 # Load our own resources
105 self.res = xrc.XmlResource('')
106 # !!! Blocking of assert failure occurring in older unicode builds
107 try:
108 quietlog = wx.LogNull()
109 self.res.Load(os.path.join(basePath, 'xrced.xrc'))
110 except wx._core.PyAssertionError:
111 print 'PyAssertionError was ignored'
112
113 # Make menus
114 menuBar = wx.MenuBar()
115
116 menu = wx.Menu()
117 menu.Append(wx.ID_NEW, '&New\tCtrl-N', 'New file')
118 menu.AppendSeparator()
119 menu.Append(wx.ID_OPEN, '&Open...\tCtrl-O', 'Open XRC file')
120 self.recentMenu = wx.Menu()
121 self.AppendRecent(self.recentMenu)
122 menu.AppendMenu(-1, 'Open Recent', self.recentMenu, 'Open a recent file')
123 menu.AppendSeparator()
124 menu.Append(wx.ID_SAVE, '&Save\tCtrl-S', 'Save XRC file')
125 menu.Append(wx.ID_SAVEAS, 'Save &As...', 'Save XRC file under different name')
126 self.ID_GENERATE_PYTHON = wx.NewId()
127 menu.Append(self.ID_GENERATE_PYTHON, '&Generate Python...',
128 'Generate a Python module that uses this XRC')
129 menu.AppendSeparator()
130 menu.Append(wx.ID_EXIT, '&Quit\tCtrl-Q', 'Exit application')
131
132 menuBar.Append(menu, '&File')
133
134 menu = wx.Menu()
135 menu.Append(wx.ID_UNDO, '&Undo\tCtrl-Z', 'Undo')
136 menu.Append(wx.ID_REDO, '&Redo\tCtrl-Y', 'Redo')
137 menu.AppendSeparator()
138 menu.Append(wx.ID_CUT, 'Cut\tCtrl-X', 'Cut to the clipboard')
139 menu.Append(wx.ID_COPY, '&Copy\tCtrl-C', 'Copy to the clipboard')
140 menu.Append(wx.ID_PASTE, '&Paste\tCtrl-V', 'Paste from the clipboard')
141 self.ID_DELETE = wx.NewId()
142 menu.Append(self.ID_DELETE, '&Delete\tCtrl-D', 'Delete object')
143 menu.AppendSeparator()
144 self.ID_LOCATE = wx.NewId()
145 self.ID_TOOL_LOCATE = wx.NewId()
146 self.ID_TOOL_PASTE = wx.NewId()
147 menu.Append(self.ID_LOCATE, '&Locate\tCtrl-L', 'Locate control in test window and select it')
148 menuBar.Append(menu, '&Edit')
149
150 menu = wx.Menu()
151 self.ID_EMBED_PANEL = wx.NewId()
152 menu.Append(self.ID_EMBED_PANEL, '&Embed Panel',
153 'Toggle embedding properties panel in the main window', True)
154 menu.Check(self.ID_EMBED_PANEL, conf.embedPanel)
155 self.ID_SHOW_TOOLS = wx.NewId()
156 menu.Append(self.ID_SHOW_TOOLS, 'Show &Tools', 'Toggle tools', True)
157 menu.Check(self.ID_SHOW_TOOLS, conf.showTools)
158 menu.AppendSeparator()
159 self.ID_TEST = wx.NewId()
160 menu.Append(self.ID_TEST, '&Test\tF5', 'Show test window')
161 self.ID_REFRESH = wx.NewId()
162 menu.Append(self.ID_REFRESH, '&Refresh\tCtrl-R', 'Refresh test window')
163 self.ID_AUTO_REFRESH = wx.NewId()
164 menu.Append(self.ID_AUTO_REFRESH, '&Auto-refresh\tCtrl-A',
165 'Toggle auto-refresh mode', True)
166 menu.Check(self.ID_AUTO_REFRESH, conf.autoRefresh)
167 self.ID_TEST_HIDE = wx.NewId()
168 menu.Append(self.ID_TEST_HIDE, '&Hide\tF6', 'Close test window')
169 menuBar.Append(menu, '&View')
170
171 menu = wx.Menu()
172 self.ID_MOVEUP = wx.NewId()
173 menu.Append(self.ID_MOVEUP, '&Up', 'Move before previous sibling')
174 self.ID_MOVEDOWN = wx.NewId()
175 menu.Append(self.ID_MOVEDOWN, '&Down', 'Move after next sibling')
176 self.ID_MOVELEFT = wx.NewId()
177 menu.Append(self.ID_MOVELEFT, '&Make sibling', 'Make sibling of parent')
178 self.ID_MOVERIGHT = wx.NewId()
179 menu.Append(self.ID_MOVERIGHT, '&Make child', 'Make child of previous sibling')
180 menuBar.Append(menu, '&Move')
181
182 menu = wx.Menu()
183 menu.Append(wx.ID_ABOUT, '&About...', 'About XCRed')
184 self.ID_README = wx.NewId()
185 menu.Append(self.ID_README, '&Readme...', 'View the README file')
186 if debug:
187 self.ID_DEBUG_CMD = wx.NewId()
188 menu.Append(self.ID_DEBUG_CMD, 'CMD', 'Python command line')
189 wx.EVT_MENU(self, self.ID_DEBUG_CMD, self.OnDebugCMD)
190 menuBar.Append(menu, '&Help')
191
192 self.menuBar = menuBar
193 self.SetMenuBar(menuBar)
194
195 # Create toolbar
196 tb = self.CreateToolBar(wx.TB_HORIZONTAL | wx.NO_BORDER | wx.TB_FLAT)
197 tb.SetToolBitmapSize((24,24))
198 new_bmp = wx.ArtProvider.GetBitmap(wx.ART_NORMAL_FILE, wx.ART_TOOLBAR)
199 open_bmp = wx.ArtProvider.GetBitmap(wx.ART_FILE_OPEN, wx.ART_TOOLBAR)
200 save_bmp = wx.ArtProvider.GetBitmap(wx.ART_FILE_SAVE, wx.ART_TOOLBAR)
201 undo_bmp = wx.ArtProvider.GetBitmap(wx.ART_UNDO, wx.ART_TOOLBAR)
202 redo_bmp = wx.ArtProvider.GetBitmap(wx.ART_REDO, wx.ART_TOOLBAR)
203 cut_bmp = wx.ArtProvider.GetBitmap(wx.ART_CUT, wx.ART_TOOLBAR)
204 copy_bmp = wx.ArtProvider.GetBitmap(wx.ART_COPY, wx.ART_TOOLBAR)
205 paste_bmp= wx.ArtProvider.GetBitmap(wx.ART_PASTE, wx.ART_TOOLBAR)
206
207 tb.AddSimpleTool(wx.ID_NEW, new_bmp, 'New', 'New file')
208 tb.AddSimpleTool(wx.ID_OPEN, open_bmp, 'Open', 'Open file')
209 tb.AddSimpleTool(wx.ID_SAVE, save_bmp, 'Save', 'Save file')
210 tb.AddControl(wx.StaticLine(tb, -1, size=(-1,23), style=wx.LI_VERTICAL))
211 tb.AddSimpleTool(wx.ID_UNDO, undo_bmp, 'Undo', 'Undo')
212 tb.AddSimpleTool(wx.ID_REDO, redo_bmp, 'Redo', 'Redo')
213 tb.AddControl(wx.StaticLine(tb, -1, size=(-1,23), style=wx.LI_VERTICAL))
214 tb.AddSimpleTool(wx.ID_CUT, cut_bmp, 'Cut', 'Cut')
215 tb.AddSimpleTool(wx.ID_COPY, copy_bmp, 'Copy', 'Copy')
216 tb.AddSimpleTool(self.ID_TOOL_PASTE, paste_bmp, 'Paste', 'Paste')
217 tb.AddControl(wx.StaticLine(tb, -1, size=(-1,23), style=wx.LI_VERTICAL))
218 tb.AddSimpleTool(self.ID_TOOL_LOCATE,
219 images.getLocateBitmap(), #images.getLocateArmedBitmap(),
220 'Locate', 'Locate control in test window and select it', True)
221 tb.AddControl(wx.StaticLine(tb, -1, size=(-1,23), style=wx.LI_VERTICAL))
222 tb.AddSimpleTool(self.ID_TEST, images.getTestBitmap(), 'Test', 'Test window')
223 tb.AddSimpleTool(self.ID_REFRESH, images.getRefreshBitmap(),
224 'Refresh', 'Refresh view')
225 tb.AddSimpleTool(self.ID_AUTO_REFRESH, images.getAutoRefreshBitmap(),
226 'Auto-refresh', 'Toggle auto-refresh mode', True)
227 tb.AddControl(wx.StaticLine(tb, -1, size=(-1,23), style=wx.LI_VERTICAL))
228 tb.AddSimpleTool(self.ID_MOVEUP, images.getToolMoveUpBitmap(),
229 'Up', 'Move before previous sibling')
230 tb.AddSimpleTool(self.ID_MOVEDOWN, images.getToolMoveDownBitmap(),
231 'Down', 'Move after next sibling')
232 tb.AddSimpleTool(self.ID_MOVELEFT, images.getToolMoveLeftBitmap(),
233 'Make Sibling', 'Make sibling of parent')
234 tb.AddSimpleTool(self.ID_MOVERIGHT, images.getToolMoveRightBitmap(),
235 'Make Child', 'Make child of previous sibling')
236 # if wx.Platform == '__WXGTK__':
237 # tb.AddSeparator() # otherwise auto-refresh sticks in status line
238 tb.ToggleTool(self.ID_AUTO_REFRESH, conf.autoRefresh)
239 tb.Realize()
240
241 self.tb = tb
242 self.minWidth = tb.GetSize()[0] # minimal width is the size of toolbar
243
244 # File
245 wx.EVT_MENU(self, wx.ID_NEW, self.OnNew)
246 wx.EVT_MENU(self, wx.ID_OPEN, self.OnOpen)
247 wx.EVT_MENU(self, wx.ID_SAVE, self.OnSaveOrSaveAs)
248 wx.EVT_MENU(self, wx.ID_SAVEAS, self.OnSaveOrSaveAs)
249 wx.EVT_MENU(self, self.ID_GENERATE_PYTHON, self.OnGeneratePython)
250 wx.EVT_MENU(self, wx.ID_EXIT, self.OnExit)
251 # Edit
252 wx.EVT_MENU(self, wx.ID_UNDO, self.OnUndo)
253 wx.EVT_MENU(self, wx.ID_REDO, self.OnRedo)
254 wx.EVT_MENU(self, wx.ID_CUT, self.OnCutDelete)
255 wx.EVT_MENU(self, wx.ID_COPY, self.OnCopy)
256 wx.EVT_MENU(self, wx.ID_PASTE, self.OnPaste)
257 wx.EVT_MENU(self, self.ID_TOOL_PASTE, self.OnPaste)
258 wx.EVT_MENU(self, self.ID_DELETE, self.OnCutDelete)
259 wx.EVT_MENU(self, self.ID_LOCATE, self.OnLocate)
260 wx.EVT_MENU(self, self.ID_TOOL_LOCATE, self.OnLocate)
261 # View
262 wx.EVT_MENU(self, self.ID_EMBED_PANEL, self.OnEmbedPanel)
263 wx.EVT_MENU(self, self.ID_SHOW_TOOLS, self.OnShowTools)
264 wx.EVT_MENU(self, self.ID_TEST, self.OnTest)
265 wx.EVT_MENU(self, self.ID_REFRESH, self.OnRefresh)
266 wx.EVT_MENU(self, self.ID_AUTO_REFRESH, self.OnAutoRefresh)
267 wx.EVT_MENU(self, self.ID_TEST_HIDE, self.OnTestHide)
268 # Move
269 wx.EVT_MENU(self, self.ID_MOVEUP, self.OnMoveUp)
270 wx.EVT_MENU(self, self.ID_MOVEDOWN, self.OnMoveDown)
271 wx.EVT_MENU(self, self.ID_MOVELEFT, self.OnMoveLeft)
272 wx.EVT_MENU(self, self.ID_MOVERIGHT, self.OnMoveRight)
273 # Help
274 wx.EVT_MENU(self, wx.ID_ABOUT, self.OnAbout)
275 wx.EVT_MENU(self, self.ID_README, self.OnReadme)
276
277 # Update events
278 wx.EVT_UPDATE_UI(self, wx.ID_SAVE, self.OnUpdateUI)
279 wx.EVT_UPDATE_UI(self, wx.ID_CUT, self.OnUpdateUI)
280 wx.EVT_UPDATE_UI(self, wx.ID_COPY, self.OnUpdateUI)
281 wx.EVT_UPDATE_UI(self, wx.ID_PASTE, self.OnUpdateUI)
282 wx.EVT_UPDATE_UI(self, self.ID_LOCATE, self.OnUpdateUI)
283 wx.EVT_UPDATE_UI(self, self.ID_TOOL_LOCATE, self.OnUpdateUI)
284 wx.EVT_UPDATE_UI(self, self.ID_TOOL_PASTE, self.OnUpdateUI)
285 wx.EVT_UPDATE_UI(self, wx.ID_UNDO, self.OnUpdateUI)
286 wx.EVT_UPDATE_UI(self, wx.ID_REDO, self.OnUpdateUI)
287 wx.EVT_UPDATE_UI(self, self.ID_DELETE, self.OnUpdateUI)
288 wx.EVT_UPDATE_UI(self, self.ID_TEST, self.OnUpdateUI)
289 wx.EVT_UPDATE_UI(self, self.ID_REFRESH, self.OnUpdateUI)
290
291 # Build interface
292 sizer = wx.BoxSizer(wx.VERTICAL)
293 sizer.Add(wx.StaticLine(self, -1), 0, wx.EXPAND)
294 # Horizontal sizer for toolbar and splitter
295 self.toolsSizer = sizer1 = wx.BoxSizer()
296 splitter = wx.SplitterWindow(self, -1, style=wx.SP_3DSASH)
297 self.splitter = splitter
298 splitter.SetMinimumPaneSize(100)
299 # Create tree
300 global tree
301 g.tree = tree = XML_Tree(splitter, -1)
302
303 # Init pull-down menu data
304 global pullDownMenu
305 g.pullDownMenu = pullDownMenu = PullDownMenu(self)
306
307 # Vertical toolbar for GUI buttons
308 g.tools = tools = Tools(self)
309 tools.Show(conf.showTools)
310 if conf.showTools: sizer1.Add(tools, 0, wx.EXPAND)
311
312 tree.RegisterKeyEvents()
313
314 # Miniframe for split mode
315 miniFrame = wx.MiniFrame(self, -1, 'Properties & Style',
316 (conf.panelX, conf.panelY),
317 (conf.panelWidth, conf.panelHeight))
318 self.miniFrame = miniFrame
319 sizer2 = wx.BoxSizer()
320 miniFrame.SetAutoLayout(True)
321 miniFrame.SetSizer(sizer2)
322 wx.EVT_CLOSE(self.miniFrame, self.OnCloseMiniFrame)
323 # Create panel for parameters
324 global panel
325 if conf.embedPanel:
326 panel = Panel(splitter)
327 # Set plitter windows
328 splitter.SplitVertically(tree, panel, conf.sashPos)
329 else:
330 panel = Panel(miniFrame)
331 sizer2.Add(panel, 1, wx.EXPAND)
332 miniFrame.Show(True)
333 splitter.Initialize(tree)
334 sizer1.Add(splitter, 1, wx.EXPAND)
335 sizer.Add(sizer1, 1, wx.EXPAND)
336 self.SetAutoLayout(True)
337 self.SetSizer(sizer)
338
339 # Initialize
340 self.Clear()
341
342 # Other events
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)
348
349 def AppendRecent(self, menu):
350 # add recently used files to the menu
351 for id,name in conf.recentfiles.iteritems():
352 menu.Append(id,name)
353 wx.EVT_MENU(self,id,self.OnRecentFile)
354 return
355
356 def OnRecentFile(self,evt):
357 # open recently used file
358 if not self.AskSave(): return
359 wx.BeginBusyCursor()
360 try:
361 path=conf.recentfiles[evt.GetId()]
362 if self.Open(path):
363 self.SetStatusText('Data loaded')
364 else:
365 self.SetStatusText('Failed')
366 except KeyError:
367 self.SetStatusText('No such file')
368 wx.EndBusyCursor()
369
370 def OnNew(self, evt):
371 if not self.AskSave(): return
372 self.Clear()
373
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:
379 path = dlg.GetPath()
380 self.SetStatusText('Loading...')
381 wx.BeginBusyCursor()
382 try:
383 if self.Open(path):
384 self.SetStatusText('Data loaded')
385 else:
386 self.SetStatusText('Failed')
387 self.SaveRecent(path)
388 finally:
389 wx.EndBusyCursor()
390 dlg.Destroy()
391
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:
400 path = dlg.GetPath()
401 if isinstance(path, unicode):
402 path = path.encode(sys.getfilesystemencoding())
403 dlg.Destroy()
404 else:
405 dlg.Destroy()
406 return
407
408 if conf.localconf:
409 # if we already have a localconf then it needs to be
410 # copied to a new config with the new name
411 lc = conf.localconf
412 nc = self.CreateLocalConf(path)
413 flag, key, idx = lc.GetFirstEntry()
414 while flag:
415 nc.Write(key, lc.Read(key))
416 flag, key, idx = lc.GetNextEntry(idx)
417 conf.localconf = nc
418 else:
419 # otherwise create a new one
420 conf.localconf = self.CreateLocalConf(path)
421 else:
422 path = self.dataFile
423 self.SetStatusText('Saving...')
424 wx.BeginBusyCursor()
425 try:
426 try:
427 tmpFile,tmpName = tempfile.mkstemp(prefix='xrced-')
428 os.close(tmpFile)
429 self.Save(tmpName) # save temporary file first
430 shutil.move(tmpName, path)
431 self.dataFile = 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)
437
438 self.SetStatusText('Data saved')
439 self.SaveRecent(path)
440 except IOError:
441 self.SetStatusText('Failed')
442 finally:
443 wx.EndBusyCursor()
444
445 def SaveRecent(self,path):
446 # append to recently used files
447 if path not in conf.recentfiles.values():
448 newid = wx.NewId()
449 self.recentMenu.Append(newid, path)
450 wx.EVT_MENU(self, newid, self.OnRecentFile)
451 conf.recentfiles[newid] = path
452
453 def GeneratePython(self, dataFile, pypath, embed, genGettext):
454 try:
455 import wx.tools.pywxrc
456 rescomp = wx.tools.pywxrc.XmlResourceCompiler()
457 rescomp.MakePythonModule([dataFile], pypath, embed, genGettext)
458 except:
459 inf = sys.exc_info()
460 wx.LogError(traceback.format_exception(inf[0], inf[1], None)[-1])
461 wx.LogError('Error generating python code : %s' % pypath)
462 raise
463
464
465 def OnGeneratePython(self, evt):
466 if self.modified or not conf.localconf:
467 wx.MessageBox("Save the XRC file first!", "Error")
468 return
469
470 dlg = PythonOptions(self, conf.localconf, self.dataFile)
471 dlg.ShowModal()
472 dlg.Destroy()
473
474
475 def OnExit(self, evt):
476 self.Close()
477
478 def OnUndo(self, evt):
479 # Extra check to not mess with idle updating
480 if undoMan.CanUndo():
481 undoMan.Undo()
482
483 def OnRedo(self, evt):
484 if undoMan.CanRedo():
485 undoMan.Redo()
486
487 def OnCopy(self, evt):
488 selected = tree.selection
489 if not selected: return # key pressed event
490 xxx = tree.GetPyData(selected)
491 if wx.TheClipboard.Open():
492 if xxx.isElement:
493 data = wx.CustomDataObject('XRCED')
494 # Set encoding in header
495 # (False,True)
496 s = xxx.node.toxml(encoding=expat.native_encoding)
497 else:
498 data = wx.CustomDataObject('XRCED_node')
499 s = xxx.node.data
500 data.SetData(cPickle.dumps(s))
501 wx.TheClipboard.SetData(data)
502 wx.TheClipboard.Close()
503 self.SetStatusText('Copied')
504 else:
505 wx.MessageBox("Unable to open the clipboard", "Error")
506
507 def OnPaste(self, evt):
508 selected = tree.selection
509 if not selected: return # key pressed event
510 # For pasting with Ctrl pressed
511 appendChild = True
512 if evt.GetId() == pullDownMenu.ID_PASTE_SIBLING: appendChild = False
513 elif evt.GetId() == self.ID_TOOL_PASTE:
514 if g.tree.ctrl: appendChild = False
515 else: appendChild = not tree.NeedInsert(selected)
516 else: appendChild = not tree.NeedInsert(selected)
517 xxx = tree.GetPyData(selected)
518 if not appendChild:
519 # If has next item, insert, else append to parent
520 nextItem = tree.GetNextSibling(selected)
521 parentLeaf = tree.GetItemParent(selected)
522 # Expanded container (must have children)
523 elif tree.IsExpanded(selected) and tree.GetChildrenCount(selected, False):
524 # Insert as first child
525 nextItem = tree.GetFirstChild(selected)[0]
526 parentLeaf = selected
527 else:
528 # No children or unexpanded item - appendChild stays True
529 nextItem = wx.TreeItemId() # no next item
530 parentLeaf = selected
531 parent = tree.GetPyData(parentLeaf).treeObject()
532
533 # Create a copy of clipboard pickled element
534 success = success_node = False
535 if wx.TheClipboard.Open():
536 data = wx.CustomDataObject('XRCED')
537 if wx.TheClipboard.IsSupported(data.GetFormat()):
538 success = wx.TheClipboard.GetData(data)
539 if not success: # try other format
540 data = wx.CustomDataObject('XRCED_node')
541 if wx.TheClipboard.IsSupported(data.GetFormat()):
542 success_node = wx.TheClipboard.GetData(data)
543 wx.TheClipboard.Close()
544
545 if not success and not success_node:
546 wx.MessageBox(
547 "There is no data in the clipboard in the required format",
548 "Error")
549 return
550
551 xml = cPickle.loads(data.GetData()) # xml representation of element
552 if success:
553 elem = minidom.parseString(xml).childNodes[0]
554 else:
555 elem = g.tree.dom.createComment(xml)
556
557 # Tempopary xxx object to test things
558 xxx = MakeXXXFromDOM(parent, elem)
559
560 # Check compatibility
561 if not self.ItemsAreCompatible(parent, xxx.treeObject()): return
562
563 # Check parent and child relationships.
564 # If parent is sizer or notebook, child is of wrong class or
565 # parent is normal window, child is child container then detach child.
566 isChildContainer = isinstance(xxx, xxxChildContainer)
567 parentIsBook = parent.__class__ in [xxxNotebook, xxxChoicebook, xxxListbook]
568 if isChildContainer and \
569 ((parent.isSizer and not isinstance(xxx, xxxSizerItem)) or \
570 (parentIsBook and not isinstance(xxx, xxxPage)) or \
571 not (parent.isSizer or parentIsBook)):
572 elem.removeChild(xxx.child.node) # detach child
573 elem.unlink() # delete child container
574 elem = xxx.child.node # replace
575 # This may help garbage collection
576 xxx.child.parent = None
577 isChildContainer = False
578 # Parent is sizer or notebook, child is not child container
579 if parent.isSizer and not isChildContainer and not isinstance(xxx, xxxSpacer):
580 # Create sizer item element
581 sizerItemElem = MakeEmptyDOM(parent.itemTag)
582 sizerItemElem.appendChild(elem)
583 elem = sizerItemElem
584 elif isinstance(parent, xxxNotebook) and not isChildContainer:
585 pageElem = MakeEmptyDOM('notebookpage')
586 pageElem.appendChild(elem)
587 elem = pageElem
588 elif isinstance(parent, xxxChoicebook) and not isChildContainer:
589 pageElem = MakeEmptyDOM('choicebookpage')
590 pageElem.appendChild(elem)
591 elem = pageElem
592 elif isinstance(parent, xxxListbook) and not isChildContainer:
593 pageElem = MakeEmptyDOM('listbookpage')
594 pageElem.appendChild(elem)
595 elem = pageElem
596 # Insert new node, register undo
597 newItem = tree.InsertNode(parentLeaf, parent, elem, nextItem)
598 undoMan.RegisterUndo(UndoPasteCreate(parentLeaf, parent, newItem, selected))
599 # Scroll to show new item (!!! redundant?)
600 tree.EnsureVisible(newItem)
601 tree.SelectItem(newItem)
602 if not tree.IsVisible(newItem):
603 tree.ScrollTo(newItem)
604 tree.Refresh()
605 # Update view?
606 if g.testWin and tree.IsHighlatable(newItem):
607 if conf.autoRefresh:
608 tree.needUpdate = True
609 tree.pendingHighLight = newItem
610 else:
611 tree.pendingHighLight = None
612 self.SetModified()
613 self.SetStatusText('Pasted')
614
615
616 def ItemsAreCompatible(self, parent, child):
617 # Check compatibility
618 error = False
619 # Comments are always compatible
620 if child.__class__ == xxxComment:
621 return True
622 # Top-level
623 if child.__class__ in [xxxDialog, xxxFrame, xxxWizard]:
624 # Top-level classes
625 if parent.__class__ != xxxMainNode: error = True
626 elif child.__class__ == xxxMenuBar:
627 # Menubar can be put in frame or dialog
628 if parent.__class__ not in [xxxMainNode, xxxFrame, xxxDialog]: error = True
629 elif child.__class__ == xxxToolBar:
630 # Toolbar can be top-level of child of panel or frame
631 if parent.__class__ not in [xxxMainNode, xxxPanel, xxxFrame] and \
632 not parent.isSizer: error = True
633 elif child.__class__ == xxxPanel and parent.__class__ == xxxMainNode:
634 pass
635 elif child.__class__ == xxxSpacer:
636 if not parent.isSizer: error = True
637 elif child.__class__ == xxxSeparator:
638 if not parent.__class__ in [xxxMenu, xxxToolBar]: error = True
639 elif child.__class__ == xxxTool:
640 if parent.__class__ != xxxToolBar: error = True
641 elif child.__class__ == xxxMenu:
642 if not parent.__class__ in [xxxMainNode, xxxMenuBar, xxxMenu]: error = True
643 elif child.__class__ == xxxMenuItem:
644 if not parent.__class__ in [xxxMenuBar, xxxMenu]: error = True
645 elif child.isSizer and parent.__class__ in [xxxNotebook, xxxChoicebook, xxxListbook]:
646 error = True
647 else: # normal controls can be almost anywhere
648 if parent.__class__ == xxxMainNode or \
649 parent.__class__ in [xxxMenuBar, xxxMenu]: error = True
650 if error:
651 if parent.__class__ == xxxMainNode: parentClass = 'root'
652 else: parentClass = parent.className
653 wx.LogError('Incompatible parent/child: parent is %s, child is %s!' %
654 (parentClass, child.className))
655 return False
656 return True
657
658 def OnMoveUp(self, evt):
659 selected = tree.selection
660 if not selected: return
661
662 index = tree.ItemIndex(selected)
663 if index == 0: return # No previous sibling found
664
665 # Undo info
666 self.lastOp = 'MOVEUP'
667 status = 'Moved before previous sibling'
668
669 # Prepare undo data
670 panel.Apply()
671
672 parent = tree.GetItemParent(selected)
673 elem = tree.RemoveLeaf(selected)
674 nextItem = tree.GetFirstChild(parent)[0]
675 for i in range(index - 1): nextItem = tree.GetNextSibling(nextItem)
676 selected = tree.InsertNode(parent, tree.GetPyData(parent).treeObject(), elem, nextItem)
677 newIndex = tree.ItemIndex(selected)
678 tree.SelectItem(selected)
679
680 undoMan.RegisterUndo(UndoMove(parent, index, parent, newIndex))
681
682 self.modified = True
683 self.SetStatusText(status)
684
685 return
686
687 def OnMoveDown(self, evt):
688 selected = tree.selection
689 if not selected: return
690
691 index = tree.ItemIndex(selected)
692 next = tree.GetNextSibling(selected)
693 if not next: return
694
695 # Undo info
696 self.lastOp = 'MOVEDOWN'
697 status = 'Moved after next sibling'
698
699 # Prepare undo data
700 panel.Apply()
701
702 parent = tree.GetItemParent(selected)
703 elem = tree.RemoveLeaf(selected)
704 nextItem = tree.GetFirstChild(parent)[0]
705 for i in range(index + 1): nextItem = tree.GetNextSibling(nextItem)
706 selected = tree.InsertNode(parent, tree.GetPyData(parent).treeObject(), elem, nextItem)
707 newIndex = tree.ItemIndex(selected)
708 tree.SelectItem(selected)
709
710 undoMan.RegisterUndo(UndoMove(parent, index, parent, newIndex))
711
712 self.modified = True
713 self.SetStatusText(status)
714
715 return
716
717 def OnMoveLeft(self, evt):
718 selected = tree.selection
719 if not selected: return
720
721 oldParent = tree.GetItemParent(selected)
722 if not oldParent: return
723 pparent = tree.GetItemParent(oldParent)
724 if not pparent: return
725
726 # Check compatibility
727 if not self.ItemsAreCompatible(tree.GetPyData(pparent).treeObject(), tree.GetPyData(selected).treeObject()): return
728
729 # Undo info
730 self.lastOp = 'MOVELEFT'
731 status = 'Made next sibling of parent'
732
733 oldIndex = tree.ItemIndex(selected)
734 elem = tree.RemoveLeaf(selected)
735 nextItem = tree.GetFirstChild(pparent)[0]
736 parentIndex = tree.ItemIndex(oldParent)
737 for i in range(parentIndex + 1): nextItem = tree.GetNextSibling(nextItem)
738
739 # Check parent and child relationships.
740 # If parent is sizer or notebook, child is of wrong class or
741 # parent is normal window, child is child container then detach child.
742 parent = tree.GetPyData(pparent).treeObject()
743 xxx = MakeXXXFromDOM(parent, elem)
744 isChildContainer = isinstance(xxx, xxxChildContainer)
745 if isChildContainer and \
746 ((parent.isSizer and not isinstance(xxx, xxxSizerItem)) or \
747 (isinstance(parent, xxxNotebook) and not isinstance(xxx, xxxNotebookPage)) or \
748 not (parent.isSizer or isinstance(parent, xxxNotebook))):
749 elem.removeChild(xxx.child.node) # detach child
750 elem.unlink() # delete child container
751 elem = xxx.child.node # replace
752 # This may help garbage collection
753 xxx.child.parent = None
754 isChildContainer = False
755 # Parent is sizer or notebook, child is not child container
756 if parent.isSizer and not isChildContainer and not isinstance(xxx, xxxSpacer):
757 # Create sizer item element
758 sizerItemElem = MakeEmptyDOM('sizeritem')
759 sizerItemElem.appendChild(elem)
760 elem = sizerItemElem
761 elif isinstance(parent, xxxNotebook) and not isChildContainer:
762 pageElem = MakeEmptyDOM('notebookpage')
763 pageElem.appendChild(elem)
764 elem = pageElem
765
766 selected = tree.InsertNode(pparent, tree.GetPyData(pparent).treeObject(), elem, nextItem)
767 newIndex = tree.ItemIndex(selected)
768 tree.SelectItem(selected)
769
770 undoMan.RegisterUndo(UndoMove(oldParent, oldIndex, pparent, newIndex))
771
772 self.modified = True
773 self.SetStatusText(status)
774
775 def OnMoveRight(self, evt):
776 selected = tree.selection
777 if not selected: return
778
779 oldParent = tree.GetItemParent(selected)
780 if not oldParent: return
781
782 newParent = tree.GetPrevSibling(selected)
783 if not newParent: return
784
785 parent = tree.GetPyData(newParent).treeObject()
786
787 # Check compatibility
788 if not self.ItemsAreCompatible(parent, tree.GetPyData(selected).treeObject()): return
789
790 # Undo info
791 self.lastOp = 'MOVERIGHT'
792 status = 'Made last child of previous sibling'
793
794 oldIndex = tree.ItemIndex(selected)
795 elem = tree.RemoveLeaf(selected)
796
797 # Check parent and child relationships.
798 # If parent is sizer or notebook, child is of wrong class or
799 # parent is normal window, child is child container then detach child.
800 xxx = MakeXXXFromDOM(parent, elem)
801 isChildContainer = isinstance(xxx, xxxChildContainer)
802 if isChildContainer and \
803 ((parent.isSizer and not isinstance(xxx, xxxSizerItem)) or \
804 (isinstance(parent, xxxNotebook) and not isinstance(xxx, xxxNotebookPage)) or \
805 not (parent.isSizer or isinstance(parent, xxxNotebook))):
806 elem.removeChild(xxx.child.node) # detach child
807 elem.unlink() # delete child container
808 elem = xxx.child.node # replace
809 # This may help garbage collection
810 xxx.child.parent = None
811 isChildContainer = False
812 # Parent is sizer or notebook, child is not child container
813 if parent.isSizer and not isChildContainer and not isinstance(xxx, xxxSpacer):
814 # Create sizer item element
815 sizerItemElem = MakeEmptyDOM('sizeritem')
816 sizerItemElem.appendChild(elem)
817 elem = sizerItemElem
818 elif isinstance(parent, xxxNotebook) and not isChildContainer:
819 pageElem = MakeEmptyDOM('notebookpage')
820 pageElem.appendChild(elem)
821 elem = pageElem
822
823 selected = tree.InsertNode(newParent, tree.GetPyData(newParent).treeObject(), elem, wx.TreeItemId())
824
825 newIndex = tree.ItemIndex(selected)
826 tree.SelectItem(selected)
827
828 undoMan.RegisterUndo(UndoMove(oldParent, oldIndex, newParent, newIndex))
829
830 self.modified = True
831 self.SetStatusText(status)
832
833
834 def OnCutDelete(self, evt):
835 selected = tree.selection
836 if not selected: return # key pressed event
837 # Undo info
838 if evt.GetId() == wx.ID_CUT:
839 self.lastOp = 'CUT'
840 status = 'Removed to clipboard'
841 else:
842 self.lastOp = 'DELETE'
843 status = 'Deleted'
844 # Delete testWin?
845 if g.testWin:
846 # If deleting top-level item, delete testWin
847 if selected == g.testWin.item:
848 g.testWin.Destroy()
849 g.testWin = None
850 else:
851 # Remove highlight, update testWin
852 if g.testWin.highLight:
853 g.testWin.highLight.Remove()
854 tree.needUpdate = True
855 # Prepare undo data
856 panel.Apply()
857 index = tree.ItemFullIndex(selected)
858 xxx = tree.GetPyData(selected)
859 parent = tree.GetPyData(tree.GetItemParent(selected)).treeObject()
860 elem = tree.RemoveLeaf(selected)
861 undoMan.RegisterUndo(UndoCutDelete(index, parent, elem))
862 if evt.GetId() == wx.ID_CUT:
863 if wx.TheClipboard.Open():
864 if xxx.isElement:
865 data = wx.CustomDataObject('XRCED')
866 # (False, True)
867 s = elem.toxml(encoding=expat.native_encoding)
868 else:
869 data = wx.CustomDataObject('XRCED_node')
870 s = xxx.node.data
871 data.SetData(cPickle.dumps(s))
872 wx.TheClipboard.SetData(data)
873 wx.TheClipboard.Close()
874 else:
875 wx.MessageBox("Unable to open the clipboard", "Error")
876 tree.pendingHighLight = None
877 tree.UnselectAll()
878 tree.selection = None
879 # Update tools
880 g.tools.UpdateUI()
881 panel.Clear()
882 self.SetModified()
883 self.SetStatusText(status)
884
885 def OnSubclass(self, evt):
886 selected = tree.selection
887 xxx = tree.GetPyData(selected).treeObject()
888 elem = xxx.node
889 subclass = xxx.subclass
890 dlg = wx.TextEntryDialog(self, 'Subclass:', defaultValue=subclass)
891 if dlg.ShowModal() == wx.ID_OK:
892 subclass = dlg.GetValue()
893 if subclass:
894 elem.setAttribute('subclass', subclass)
895 elif elem.hasAttribute('subclass'):
896 elem.removeAttribute('subclass')
897 self.SetModified()
898 xxx.subclass = elem.getAttribute('subclass')
899 tree.SetItemText(selected, xxx.treeName())
900 panel.pages[0].box.SetLabel(xxx.panelName())
901 dlg.Destroy()
902
903 def OnEmbedPanel(self, evt):
904 conf.embedPanel = evt.IsChecked()
905 if conf.embedPanel:
906 # Remember last dimentions
907 conf.panelX, conf.panelY = self.miniFrame.GetPosition()
908 conf.panelWidth, conf.panelHeight = self.miniFrame.GetSize()
909 size = self.GetSize()
910 pos = self.GetPosition()
911 sizePanel = panel.GetSize()
912 panel.Reparent(self.splitter)
913 self.miniFrame.GetSizer().Remove(panel)
914 # Widen
915 self.SetDimensions(pos.x, pos.y, size.width + sizePanel.width, size.height)
916 self.splitter.SplitVertically(tree, panel, conf.sashPos)
917 self.miniFrame.Show(False)
918 else:
919 conf.sashPos = self.splitter.GetSashPosition()
920 pos = self.GetPosition()
921 size = self.GetSize()
922 sizePanel = panel.GetSize()
923 self.splitter.Unsplit(panel)
924 sizer = self.miniFrame.GetSizer()
925 panel.Reparent(self.miniFrame)
926 panel.Show(True)
927 sizer.Add(panel, 1, wx.EXPAND)
928 self.miniFrame.Show(True)
929 self.miniFrame.SetDimensions(conf.panelX, conf.panelY,
930 conf.panelWidth, conf.panelHeight)
931 self.miniFrame.Layout()
932 # Reduce width
933 self.SetDimensions(pos.x, pos.y,
934 max(size.width - sizePanel.width, self.minWidth), size.height)
935
936 def OnShowTools(self, evt):
937 conf.showTools = evt.IsChecked()
938 g.tools.Show(conf.showTools)
939 if conf.showTools:
940 self.toolsSizer.Prepend(g.tools, 0, wx.EXPAND)
941 else:
942 self.toolsSizer.Remove(g.tools)
943 self.toolsSizer.Layout()
944
945 def OnTest(self, evt):
946 if not tree.selection: return # key pressed event
947 tree.ShowTestWindow(tree.selection)
948
949 def OnTestHide(self, evt):
950 tree.CloseTestWindow()
951
952 # Find object by relative position
953 def FindObject(self, item, obj):
954 # We simply perform depth-first traversal, sinse it's too much
955 # hassle to deal with all sizer/window combinations
956 w = tree.FindNodeObject(item)
957 if w == obj or isinstance(w, wx.GBSizerItem) and w.GetWindow() == obj:
958 return item
959 if tree.ItemHasChildren(item):
960 child = tree.GetFirstChild(item)[0]
961 while child:
962 found = self.FindObject(child, obj)
963 if found: return found
964 child = tree.GetNextSibling(child)
965 return None
966
967 def OnTestWinLeftDown(self, evt):
968 pos = evt.GetPosition()
969 self.SetHandler(g.testWin)
970 g.testWin.Disconnect(wx.ID_ANY, wx.ID_ANY, wx.wxEVT_LEFT_DOWN)
971 item = self.FindObject(g.testWin.item, evt.GetEventObject())
972 if item:
973 tree.EnsureVisible(item)
974 tree.SelectItem(item)
975 self.tb.ToggleTool(self.ID_TOOL_LOCATE, False)
976 if item:
977 self.SetStatusText('Selected %s' % tree.GetItemText(item))
978 else:
979 self.SetStatusText('Locate failed!')
980
981 def SetHandler(self, w, h=None):
982 if h:
983 w.SetEventHandler(h)
984 w.SetCursor(wx.CROSS_CURSOR)
985 else:
986 w.SetEventHandler(w)
987 w.SetCursor(wx.NullCursor)
988 for ch in w.GetChildren():
989 self.SetHandler(ch, h)
990
991 def OnLocate(self, evt):
992 if g.testWin:
993 if evt.GetId() == self.ID_LOCATE or \
994 evt.GetId() == self.ID_TOOL_LOCATE and evt.IsChecked():
995 self.SetHandler(g.testWin, g.testWin)
996 g.testWin.Connect(wx.ID_ANY, wx.ID_ANY, wx.wxEVT_LEFT_DOWN, self.OnTestWinLeftDown)
997 if evt.GetId() == self.ID_LOCATE:
998 self.tb.ToggleTool(self.ID_TOOL_LOCATE, True)
999 elif evt.GetId() == self.ID_TOOL_LOCATE and not evt.IsChecked():
1000 self.SetHandler(g.testWin, None)
1001 g.testWin.Disconnect(wx.ID_ANY, wx.ID_ANY, wx.wxEVT_LEFT_DOWN)
1002 self.SetStatusText('Click somewhere in your test window now')
1003
1004 def OnRefresh(self, evt):
1005 # If modified, apply first
1006 selection = tree.selection
1007 if selection:
1008 xxx = tree.GetPyData(selection)
1009 if xxx and panel.IsModified():
1010 tree.Apply(xxx, selection)
1011 if g.testWin:
1012 # (re)create
1013 tree.CreateTestWin(g.testWin.item)
1014 panel.modified = False
1015 tree.needUpdate = False
1016
1017 def OnAutoRefresh(self, evt):
1018 conf.autoRefresh = evt.IsChecked()
1019 self.menuBar.Check(self.ID_AUTO_REFRESH, conf.autoRefresh)
1020 self.tb.ToggleTool(self.ID_AUTO_REFRESH, conf.autoRefresh)
1021
1022 def OnAbout(self, evt):
1023 str = '''\
1024 XRCed version %s
1025
1026 (c) Roman Rolinsky <rollrom@users.sourceforge.net>
1027 Homepage: http://xrced.sourceforge.net\
1028 ''' % version
1029 dlg = wx.MessageDialog(self, str, 'About XRCed', wx.OK | wx.CENTRE)
1030 dlg.ShowModal()
1031 dlg.Destroy()
1032
1033 def OnReadme(self, evt):
1034 text = open(os.path.join(basePath, 'README.txt'), 'r').read()
1035 dlg = ScrolledMessageDialog(self, text, "XRCed README")
1036 dlg.ShowModal()
1037 dlg.Destroy()
1038
1039 # Simple emulation of python command line
1040 def OnDebugCMD(self, evt):
1041 while 1:
1042 try:
1043 exec raw_input('C:\> ')
1044 except EOFError:
1045 print '^D'
1046 break
1047 except:
1048 (etype, value, tb) =sys.exc_info()
1049 tblist =traceback.extract_tb(tb)[1:]
1050 msg =' '.join(traceback.format_exception_only(etype, value)
1051 +traceback.format_list(tblist))
1052 print msg
1053
1054 def OnCreate(self, evt):
1055 selected = tree.selection
1056 if tree.ctrl: appendChild = False
1057 else: appendChild = not tree.NeedInsert(selected)
1058 xxx = tree.GetPyData(selected)
1059 if not appendChild:
1060 # If insert before
1061 if tree.shift:
1062 # If has previous item, insert after it, else append to parent
1063 nextItem = selected
1064 parentLeaf = tree.GetItemParent(selected)
1065 else:
1066 # If has next item, insert, else append to parent
1067 nextItem = tree.GetNextSibling(selected)
1068 parentLeaf = tree.GetItemParent(selected)
1069 # Expanded container (must have children)
1070 elif tree.shift and tree.IsExpanded(selected) \
1071 and tree.GetChildrenCount(selected, False):
1072 nextItem = tree.GetFirstChild(selected)[0]
1073 parentLeaf = selected
1074 else:
1075 nextItem = wx.TreeItemId()
1076 parentLeaf = selected
1077 parent = tree.GetPyData(parentLeaf)
1078 if parent.hasChild: parent = parent.child
1079
1080 # Create object_ref?
1081 if evt.GetId() == ID_NEW.REF:
1082 ref = wx.GetTextFromUser('Create reference to:', 'Create reference')
1083 if not ref: return
1084 xxx = MakeEmptyRefXXX(parent, ref)
1085 elif evt.GetId() == ID_NEW.COMMENT:
1086 xxx = MakeEmptyCommentXXX(parent)
1087 else:
1088 # Create empty element
1089 className = pullDownMenu.createMap[evt.GetId()]
1090 xxx = MakeEmptyXXX(parent, className)
1091
1092 # Insert new node, register undo
1093 if xxx.isElement: # true object
1094 # Set default name for top-level windows
1095 if parent.__class__ == xxxMainNode:
1096 cl = xxx.treeObject().__class__
1097 frame.maxIDs[cl] += 1
1098 xxx.setTreeName('%s%d' % (defaultIDs[cl], frame.maxIDs[cl]))
1099 # And for some other standard controls
1100 elif parent.__class__ == xxxStdDialogButtonSizer:
1101 xxx.setTreeName(pullDownMenu.stdButtonIDs[evt.GetId()][0])
1102 # We can even set label
1103 obj = xxx.treeObject()
1104 elem = g.tree.dom.createElement('label')
1105 elem.appendChild(g.tree.dom.createTextNode(pullDownMenu.stdButtonIDs[evt.GetId()][1]))
1106 obj.params['label'] = xxxParam(elem)
1107 xxx.treeObject().node.appendChild(elem)
1108
1109 newItem = tree.InsertNode(parentLeaf, parent, xxx.node, nextItem)
1110 else: # comment node
1111 newItem = tree.InsertNode(parentLeaf, parent, xxx.node, nextItem)
1112 undoMan.RegisterUndo(UndoPasteCreate(parentLeaf, parent, newItem, selected))
1113 tree.EnsureVisible(newItem)
1114 tree.SelectItem(newItem)
1115 if not tree.IsVisible(newItem):
1116 tree.ScrollTo(newItem)
1117 tree.Refresh()
1118 # Update view?
1119 if xxx.isElement and g.testWin and tree.IsHighlatable(newItem):
1120 if conf.autoRefresh:
1121 tree.needUpdate = True
1122 tree.pendingHighLight = newItem
1123 else:
1124 tree.pendingHighLight = None
1125 tree.SetFocus()
1126 if not xxx.isElement:
1127 tree.EditLabel(newItem)
1128 self.SetModified()
1129
1130 # Replace one object with another
1131 def OnReplace(self, evt):
1132 selected = tree.selection
1133 xxx = tree.GetPyData(selected).treeObject()
1134 elem = xxx.node
1135 parent = elem.parentNode
1136 undoMan.RegisterUndo(UndoReplace(selected))
1137 # New class
1138 className = pullDownMenu.createMap[evt.GetId() - 1000]
1139
1140 # Create temporary empty node (with default values)
1141 dummy = MakeEmptyDOM(className)
1142 if className == 'spacer' and xxx.className != 'spacer':
1143 klass = xxxSpacer
1144 elif xxx.className == 'spacer' and className != 'spacer':
1145 klass = xxxSizerItem
1146 else:
1147 klass = xxxDict[className]
1148 # Remove non-compatible children
1149 if tree.ItemHasChildren(selected) and not klass.hasChildren:
1150 tree.DeleteChildren(selected)
1151 nodes = elem.childNodes[:]
1152 tags = []
1153 for node in nodes:
1154 if node.nodeType != minidom.Node.ELEMENT_NODE: continue
1155 remove = False
1156 tag = node.tagName
1157 if tag == 'object':
1158 if not klass.hasChildren: remove = True
1159 elif tag not in klass.allParams and \
1160 (not klass.hasStyle or tag not in klass.styles):
1161 remove = True
1162 else:
1163 tags.append(tag)
1164 if remove:
1165 elem.removeChild(node)
1166 node.unlink()
1167
1168 # Remove sizeritem child if spacer
1169 if className == 'spacer' and xxx.className != 'spacer':
1170 sizeritem = elem.parentNode
1171 assert sizeritem.getAttribute('class') == 'sizeritem'
1172 sizeritem.removeChild(elem)
1173 elem.unlink()
1174 elem = sizeritem
1175 tree.GetPyData(selected).hasChild = False
1176 elif xxx.className == 'spacer' and className != 'spacer':
1177 # Create sizeritem element
1178 assert xxx.parent.isSizer
1179 elem.setAttribute('class', 'sizeritem')
1180 node = MakeEmptyDOM(className)
1181 elem.appendChild(node)
1182 # Replace to point to new object
1183 xxx = xxxSizerItem(xxx.parent, elem)
1184 elem = node
1185 tree.SetPyData(selected, xxx)
1186 xxx = xxx.child
1187 else:
1188 # Copy parameters present in dummy but not in elem
1189 for node in dummy.childNodes:
1190 if node.tagName not in tags: elem.appendChild(node.cloneNode(True))
1191 dummy.unlink()
1192
1193 # Change class name
1194 elem.setAttribute('class', className)
1195 if elem.hasAttribute('subclass'):
1196 elem.removeAttribute('subclass') # clear subclassing
1197 # Re-create xxx element
1198 xxx = MakeXXXFromDOM(xxx.parent, elem)
1199 # Remove incompatible style flags
1200 if 'style' in xxx.params:
1201 styles = map(string.strip, xxx.params['style'].value().split('|'))
1202 newStyles = [s for s in styles if s in klass.winStyles or s in genericStyles]
1203 if newStyles != styles:
1204 if newStyles:
1205 value = reduce(lambda a,b: a+'|'+b, newStyles)
1206 else:
1207 value = ''
1208 xxx.params['style'].update(value)
1209
1210 # Update parent in child objects
1211 if tree.ItemHasChildren(selected):
1212 i, cookie = tree.GetFirstChild(selected)
1213 while i.IsOk():
1214 x = tree.GetPyData(i)
1215 x.parent = xxx
1216 if x.hasChild: x.child.parent = xxx
1217 i, cookie = tree.GetNextChild(selected, cookie)
1218
1219 # Update tree
1220 if tree.GetPyData(selected).hasChild: # child container
1221 container = tree.GetPyData(selected)
1222 container.resetChild(xxx)
1223 xxx = container
1224 else:
1225 tree.SetPyData(selected, xxx)
1226 tree.SetItemText(selected, xxx.treeName())
1227 tree.SetItemImage(selected, xxx.treeImage())
1228
1229 # Set default name for top-level windows
1230 if parent.__class__ == xxxMainNode:
1231 cl = xxx.treeObject().__class__
1232 frame.maxIDs[cl] += 1
1233 xxx.setTreeName('%s%d' % (defaultIDs[cl], frame.maxIDs[cl]))
1234
1235 # Update panel
1236 g.panel.SetData(xxx)
1237 # Update tools
1238 g.tools.UpdateUI()
1239
1240 #undoMan.RegisterUndo(UndoPasteCreate(parentLeaf, parent, newItem, selected))
1241 # Update view?
1242 if g.testWin and tree.IsHighlatable(selected):
1243 if conf.autoRefresh:
1244 tree.needUpdate = True
1245 tree.pendingHighLight = selected
1246 else:
1247 tree.pendingHighLight = None
1248 tree.SetFocus()
1249 self.SetModified()
1250
1251 # Expand/collapse subtree
1252 def OnExpand(self, evt):
1253 if tree.selection: tree.ExpandAll(tree.selection)
1254 else: tree.ExpandAll(tree.root)
1255 def OnCollapse(self, evt):
1256 if tree.selection: tree.CollapseAll(tree.selection)
1257 else: tree.CollapseAll(tree.root)
1258
1259 def OnPullDownHighlight(self, evt):
1260 menuId = evt.GetMenuId()
1261 if menuId != -1:
1262 menu = evt.GetEventObject()
1263 help = menu.GetHelpString(menuId)
1264 self.SetStatusText(help)
1265 else:
1266 self.SetStatusText('')
1267
1268 def OnUpdateUI(self, evt):
1269 if evt.GetId() in [wx.ID_CUT, wx.ID_COPY, self.ID_DELETE]:
1270 evt.Enable(tree.selection is not None and tree.selection != tree.root)
1271 elif evt.GetId() == wx.ID_SAVE:
1272 evt.Enable(self.modified)
1273 elif evt.GetId() in [wx.ID_PASTE, self.ID_TOOL_PASTE]:
1274 evt.Enable(tree.selection is not None)
1275 elif evt.GetId() == self.ID_TEST:
1276 evt.Enable(tree.selection is not None and tree.selection != tree.root)
1277 elif evt.GetId() in [self.ID_LOCATE, self.ID_TOOL_LOCATE]:
1278 evt.Enable(g.testWin is not None)
1279 elif evt.GetId() == wx.ID_UNDO: evt.Enable(undoMan.CanUndo())
1280 elif evt.GetId() == wx.ID_REDO: evt.Enable(undoMan.CanRedo())
1281
1282 def OnIdle(self, evt):
1283 if self.inIdle: return # Recursive call protection
1284 self.inIdle = True
1285 try:
1286 if tree.needUpdate:
1287 if conf.autoRefresh:
1288 if g.testWin:
1289 self.SetStatusText('Refreshing test window...')
1290 # (re)create
1291 tree.CreateTestWin(g.testWin.item)
1292 self.SetStatusText('')
1293 tree.needUpdate = False
1294 elif tree.pendingHighLight:
1295 try:
1296 tree.HighLight(tree.pendingHighLight)
1297 except:
1298 # Remove highlight if any problem
1299 if g.testWin.highLight:
1300 g.testWin.highLight.Remove()
1301 tree.pendingHighLight = None
1302 raise
1303 else:
1304 evt.Skip()
1305 finally:
1306 self.inIdle = False
1307
1308 # We don't let close panel window
1309 def OnCloseMiniFrame(self, evt):
1310 return
1311
1312 def OnIconize(self, evt):
1313 if evt.Iconized():
1314 conf.x, conf.y = self.GetPosition()
1315 conf.width, conf.height = self.GetSize()
1316 if conf.embedPanel:
1317 conf.sashPos = self.splitter.GetSashPosition()
1318 else:
1319 conf.panelX, conf.panelY = self.miniFrame.GetPosition()
1320 conf.panelWidth, conf.panelHeight = self.miniFrame.GetSize()
1321 self.miniFrame.Iconize()
1322 else:
1323 if not conf.embedPanel:
1324 self.miniFrame.Iconize(False)
1325 evt.Skip()
1326
1327 def OnCloseWindow(self, evt):
1328 if not self.AskSave(): return
1329 if g.testWin: g.testWin.Destroy()
1330 if not panel.GetPageCount() == 2:
1331 panel.page2.Destroy()
1332 else:
1333 # If we don't do this, page does not get destroyed (a bug?)
1334 panel.RemovePage(1)
1335 if not self.IsIconized():
1336 conf.x, conf.y = self.GetPosition()
1337 conf.width, conf.height = self.GetSize()
1338 if conf.embedPanel:
1339 conf.sashPos = self.splitter.GetSashPosition()
1340 else:
1341 conf.panelX, conf.panelY = self.miniFrame.GetPosition()
1342 conf.panelWidth, conf.panelHeight = self.miniFrame.GetSize()
1343 evt.Skip()
1344
1345
1346 def CreateLocalConf(self, path):
1347 name = os.path.splitext(path)[0]
1348 name += '.xcfg'
1349 return wx.FileConfig(localFilename=name)
1350
1351
1352 def Clear(self):
1353 self.dataFile = ''
1354 conf.localconf = None
1355 undoMan.Clear()
1356 self.SetModified(False)
1357 tree.Clear()
1358 panel.Clear()
1359 if g.testWin:
1360 g.testWin.Destroy()
1361 g.testWin = None
1362 # Numbers for new controls
1363 self.maxIDs = {}
1364 for cl in [xxxPanel, xxxDialog, xxxFrame,
1365 xxxMenuBar, xxxMenu, xxxToolBar,
1366 xxxWizard, xxxBitmap, xxxIcon]:
1367 self.maxIDs[cl] = 0
1368
1369 def SetModified(self, state=True):
1370 self.modified = state
1371 name = os.path.basename(self.dataFile)
1372 if not name: name = defaultName
1373 if state:
1374 self.SetTitle(progname + ': ' + name + ' *')
1375 else:
1376 self.SetTitle(progname + ': ' + name)
1377
1378 def Open(self, path):
1379 if not os.path.exists(path):
1380 wx.LogError('File does not exists: %s' % path)
1381 return False
1382 # Try to read the file
1383 try:
1384 f = open(path)
1385 self.Clear()
1386 dom = minidom.parse(f)
1387 f.close()
1388 # Set encoding global variable and default encoding
1389 if dom.encoding:
1390 g.currentEncoding = dom.encoding
1391 wx.SetDefaultPyEncoding(g.currentEncoding.encode())
1392 else:
1393 g.currentEncoding = ''
1394 # Change dir
1395 self.dataFile = path = os.path.abspath(path)
1396 dir = os.path.dirname(path)
1397 if dir: os.chdir(dir)
1398 tree.SetData(dom)
1399 self.SetTitle(progname + ': ' + os.path.basename(path))
1400 conf.localconf = self.CreateLocalConf(self.dataFile)
1401 except:
1402 # Nice exception printing
1403 inf = sys.exc_info()
1404 wx.LogError(traceback.format_exception(inf[0], inf[1], None)[-1])
1405 wx.LogError('Error reading file: %s' % path)
1406 if debug: raise
1407 return False
1408 return True
1409
1410 def Indent(self, node, indent = 0):
1411 if node.nodeType == minidom.Node.COMMENT_NODE:
1412 text = self.domCopy.createTextNode('\n' + ' ' * indent)
1413 node.parentNode.insertBefore(text, node)
1414 return # no children
1415 # Copy child list because it will change soon
1416 children = node.childNodes[:]
1417 # Main node doesn't need to be indented
1418 if indent:
1419 text = self.domCopy.createTextNode('\n' + ' ' * indent)
1420 node.parentNode.insertBefore(text, node)
1421 if children:
1422 # Append newline after last child, except for text nodes
1423 if children[-1].nodeType == minidom.Node.ELEMENT_NODE:
1424 text = self.domCopy.createTextNode('\n' + ' ' * indent)
1425 node.appendChild(text)
1426 # Indent children which are elements
1427 for n in children:
1428 if n.nodeType == minidom.Node.ELEMENT_NODE or \
1429 n.nodeType == minidom.Node.COMMENT_NODE:
1430 self.Indent(n, indent + 2)
1431
1432 def Save(self, path):
1433 try:
1434 import codecs
1435 # Apply changes
1436 if tree.selection and panel.IsModified():
1437 self.OnRefresh(wx.CommandEvent())
1438 if g.currentEncoding:
1439 f = codecs.open(path, 'wt', g.currentEncoding)
1440 else:
1441 f = codecs.open(path, 'wt')
1442 # Make temporary copy for formatting it
1443 # !!! We can't clone dom node, it works only once
1444 #self.domCopy = tree.dom.cloneNode(True)
1445 self.domCopy = MyDocument()
1446 mainNode = self.domCopy.appendChild(tree.mainNode.cloneNode(True))
1447 # Remove first child (test element)
1448 testElem = mainNode.firstChild
1449 mainNode.removeChild(testElem)
1450 testElem.unlink()
1451 self.Indent(mainNode)
1452 self.domCopy.writexml(f, encoding = g.currentEncoding)
1453 f.close()
1454 self.domCopy.unlink()
1455 self.domCopy = None
1456 self.SetModified(False)
1457 panel.SetModified(False)
1458 conf.localconf.Flush()
1459 except:
1460 inf = sys.exc_info()
1461 wx.LogError(traceback.format_exception(inf[0], inf[1], None)[-1])
1462 wx.LogError('Error writing file: %s' % path)
1463 raise
1464
1465 def AskSave(self):
1466 if not (self.modified or panel.IsModified()): return True
1467 flags = wx.ICON_EXCLAMATION | wx.YES_NO | wx.CANCEL | wx.CENTRE
1468 dlg = wx.MessageDialog( self, 'File is modified. Save before exit?',
1469 'Save before too late?', flags )
1470 say = dlg.ShowModal()
1471 dlg.Destroy()
1472 wx.Yield()
1473 if say == wx.ID_YES:
1474 self.OnSaveOrSaveAs(wx.CommandEvent(wx.ID_SAVE))
1475 # If save was successful, modified flag is unset
1476 if not self.modified: return True
1477 elif say == wx.ID_NO:
1478 self.SetModified(False)
1479 panel.SetModified(False)
1480 return True
1481 return False
1482
1483 def SaveUndo(self):
1484 pass # !!!
1485
1486 ################################################################################
1487
1488 class PythonOptions(wx.Dialog):
1489
1490 def __init__(self, parent, cfg, dataFile):
1491 pre = wx.PreDialog()
1492 g.frame.res.LoadOnDialog(pre, parent, "PYTHON_OPTIONS")
1493 self.PostCreate(pre)
1494
1495 self.cfg = cfg
1496 self.dataFile = dataFile
1497
1498 self.AutoGenerateCB = xrc.XRCCTRL(self, "AutoGenerateCB")
1499 self.EmbedCB = xrc.XRCCTRL(self, "EmbedCB")
1500 self.GettextCB = xrc.XRCCTRL(self, "GettextCB")
1501 self.MakeXRSFileCB = xrc.XRCCTRL(self, "MakeXRSFileCB")
1502 self.FileNameTC = xrc.XRCCTRL(self, "FileNameTC")
1503 self.BrowseBtn = xrc.XRCCTRL(self, "BrowseBtn")
1504 self.GenerateBtn = xrc.XRCCTRL(self, "GenerateBtn")
1505 self.SaveOptsBtn = xrc.XRCCTRL(self, "SaveOptsBtn")
1506
1507 self.Bind(wx.EVT_BUTTON, self.OnBrowse, self.BrowseBtn)
1508 self.Bind(wx.EVT_BUTTON, self.OnGenerate, self.GenerateBtn)
1509 self.Bind(wx.EVT_BUTTON, self.OnSaveOpts, self.SaveOptsBtn)
1510
1511 if self.cfg.Read("filename", "") != "":
1512 self.FileNameTC.SetValue(self.cfg.Read("filename"))
1513 else:
1514 name = os.path.splitext(os.path.split(dataFile)[1])[0]
1515 name += '_xrc.py'
1516 self.FileNameTC.SetValue(name)
1517 self.AutoGenerateCB.SetValue(self.cfg.ReadBool("autogenerate", False))
1518 self.EmbedCB.SetValue(self.cfg.ReadBool("embedResource", False))
1519 self.MakeXRSFileCB.SetValue(self.cfg.ReadBool("makeXRS", False))
1520 self.GettextCB.SetValue(self.cfg.ReadBool("genGettext", False))
1521
1522
1523 def OnBrowse(self, evt):
1524 path = self.FileNameTC.GetValue()
1525 dirname = os.path.abspath(os.path.dirname(path))
1526 name = os.path.split(path)[1]
1527 dlg = wx.FileDialog(self, 'Save As', dirname, name, '*.py',
1528 wx.SAVE | wx.OVERWRITE_PROMPT)
1529 if dlg.ShowModal() == wx.ID_OK:
1530 path = dlg.GetPath()
1531 self.FileNameTC.SetValue(path)
1532 dlg.Destroy()
1533
1534
1535 def OnGenerate(self, evt):
1536 pypath = self.FileNameTC.GetValue()
1537 embed = self.EmbedCB.GetValue()
1538 genGettext = self.GettextCB.GetValue()
1539 frame.GeneratePython(self.dataFile, pypath, embed, genGettext)
1540 self.OnSaveOpts()
1541
1542
1543 def OnSaveOpts(self, evt=None):
1544 self.cfg.Write("filename", self.FileNameTC.GetValue())
1545 self.cfg.WriteBool("autogenerate", self.AutoGenerateCB.GetValue())
1546 self.cfg.WriteBool("embedResource", self.EmbedCB.GetValue())
1547 self.cfg.WriteBool("makeXRS", self.MakeXRSFileCB.GetValue())
1548 self.cfg.WriteBool("genGettext", self.GettextCB.GetValue())
1549
1550 self.EndModal(wx.ID_OK)
1551
1552
1553 ################################################################################
1554
1555 def usage():
1556 print >> sys.stderr, 'usage: xrced [-dhiv] [file]'
1557
1558 class App(wx.App):
1559 def OnInit(self):
1560 # Check version
1561 if wx.VERSION[:3] < MinWxVersion:
1562 wx.LogWarning('''\
1563 This version of XRCed may not work correctly on your version of wxWidgets. \
1564 Please upgrade wxWidgets to %d.%d.%d or higher.''' % MinWxVersion)
1565 global debug
1566 # Process comand-line
1567 opts = args = None
1568 try:
1569 opts, args = getopt.getopt(sys.argv[1:], 'dhiv')
1570 for o,a in opts:
1571 if o == '-h':
1572 usage()
1573 sys.exit(0)
1574 elif o == '-d':
1575 debug = True
1576 elif o == '-v':
1577 print 'XRCed version', version
1578 sys.exit(0)
1579
1580 except getopt.GetoptError:
1581 if wx.Platform != '__WXMAC__': # macs have some extra parameters
1582 print >> sys.stderr, 'Unknown option'
1583 usage()
1584 sys.exit(1)
1585
1586 self.SetAppName('xrced')
1587 # Settings
1588 global conf
1589 conf = g.conf = wx.Config(style = wx.CONFIG_USE_LOCAL_FILE)
1590 conf.localconf = None
1591 conf.autoRefresh = conf.ReadInt('autorefresh', True)
1592 pos = conf.ReadInt('x', -1), conf.ReadInt('y', -1)
1593 size = conf.ReadInt('width', 800), conf.ReadInt('height', 600)
1594 conf.embedPanel = conf.ReadInt('embedPanel', True)
1595 conf.showTools = conf.ReadInt('showTools', True)
1596 conf.sashPos = conf.ReadInt('sashPos', 200)
1597 # read recently used files
1598 recentfiles=conf.Read('recentFiles','')
1599 conf.recentfiles={}
1600 if recentfiles:
1601 for fil in recentfiles.split('|'):
1602 conf.recentfiles[wx.NewId()]=fil
1603 if not conf.embedPanel:
1604 conf.panelX = conf.ReadInt('panelX', -1)
1605 conf.panelY = conf.ReadInt('panelY', -1)
1606 else:
1607 conf.panelX = conf.panelY = -1
1608 conf.panelWidth = conf.ReadInt('panelWidth', 200)
1609 conf.panelHeight = conf.ReadInt('panelHeight', 200)
1610 conf.panic = not conf.HasEntry('nopanic')
1611 # Add handlers
1612 wx.FileSystem.AddHandler(wx.MemoryFSHandler())
1613 # Create main frame
1614 frame = Frame(pos, size)
1615 frame.Show(True)
1616
1617 # Load file after showing
1618 if args:
1619 conf.panic = False
1620 frame.open = frame.Open(args[0])
1621
1622 return True
1623
1624 def OnExit(self):
1625 # Write config
1626 global conf
1627 wc = conf
1628 wc.WriteInt('autorefresh', conf.autoRefresh)
1629 wc.WriteInt('x', conf.x)
1630 wc.WriteInt('y', conf.y)
1631 wc.WriteInt('width', conf.width)
1632 wc.WriteInt('height', conf.height)
1633 wc.WriteInt('embedPanel', conf.embedPanel)
1634 wc.WriteInt('showTools', conf.showTools)
1635 if not conf.embedPanel:
1636 wc.WriteInt('panelX', conf.panelX)
1637 wc.WriteInt('panelY', conf.panelY)
1638 wc.WriteInt('sashPos', conf.sashPos)
1639 wc.WriteInt('panelWidth', conf.panelWidth)
1640 wc.WriteInt('panelHeight', conf.panelHeight)
1641 wc.WriteInt('nopanic', True)
1642 wc.Write('recentFiles', '|'.join(conf.recentfiles.values()[-5:]))
1643 wc.Flush()
1644
1645 def main():
1646 app = App(0, useBestVisual=False)
1647 #app.SetAssertMode(wx.PYAPP_ASSERT_LOG)
1648 app.MainLoop()
1649 app.OnExit()
1650 global conf
1651 del conf
1652
1653 if __name__ == '__main__':
1654 main()