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