]> git.saurik.com Git - wxWidgets.git/blob - wxPython/wx/tools/XRCed/xrced.py
fixed wxVsnprintf() to write as much as it can if the output buffer is too short
[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 not parent.hasChildren:
729 error = True
730 elif child.__class__ == xxxPanel and parent.__class__ == xxxMainNode:
731 pass
732 elif child.__class__ == xxxSpacer:
733 if not parent.isSizer: error = True
734 elif child.__class__ == xxxSeparator:
735 if not parent.__class__ in [xxxMenu, xxxToolBar]: error = True
736 elif child.__class__ == xxxTool:
737 if parent.__class__ != xxxToolBar: error = True
738 elif child.__class__ == xxxMenu:
739 if not parent.__class__ in [xxxMainNode, xxxMenuBar, xxxMenu]: error = True
740 elif child.__class__ == xxxMenuItem:
741 if not parent.__class__ in [xxxMenuBar, xxxMenu]: error = True
742 elif child.isSizer and parent.__class__ in [xxxNotebook, xxxChoicebook, xxxListbook]:
743 error = True
744 else: # normal controls can be almost anywhere
745 if parent.__class__ == xxxMainNode or \
746 parent.__class__ in [xxxMenuBar, xxxMenu]: error = True
747 if error:
748 if parent.__class__ == xxxMainNode: parentClass = 'root'
749 else: parentClass = parent.className
750 wx.LogError('Incompatible parent/child: parent is %s, child is %s!' %
751 (parentClass, child.className))
752 return False
753 return True
754
755 def OnMoveUp(self, evt):
756 selected = tree.selection
757 if not selected: return
758
759 index = tree.ItemIndex(selected)
760 if index == 0: return # No previous sibling found
761
762 # Remove highlight, update testWin
763 if g.testWin and g.testWin.highLight:
764 g.testWin.highLight.Remove()
765 tree.needUpdate = True
766
767 # Undo info
768 self.lastOp = 'MOVEUP'
769 status = 'Moved before previous sibling'
770
771 # Prepare undo data
772 panel.Apply()
773 tree.UnselectAll()
774
775 parent = tree.GetItemParent(selected)
776 elem = tree.RemoveLeaf(selected)
777 nextItem = tree.GetFirstChild(parent)[0]
778 for i in range(index - 1): nextItem = tree.GetNextSibling(nextItem)
779 selected = tree.InsertNode(parent, tree.GetPyData(parent).treeObject(), elem, nextItem)
780 newIndex = tree.ItemIndex(selected)
781 tree.SelectItem(selected)
782
783 undoMan.RegisterUndo(UndoMove(parent, index, parent, newIndex))
784
785 self.modified = True
786 self.SetStatusText(status)
787
788 return
789
790 def OnMoveDown(self, evt):
791 selected = tree.selection
792 if not selected: return
793
794 index = tree.ItemIndex(selected)
795 next = tree.GetNextSibling(selected)
796 if not next: return
797
798 # Remove highlight, update testWin
799 if g.testWin and g.testWin.highLight:
800 g.testWin.highLight.Remove()
801 tree.needUpdate = True
802
803 # Undo info
804 self.lastOp = 'MOVEDOWN'
805 status = 'Moved after next sibling'
806
807 # Prepare undo data
808 panel.Apply()
809 tree.UnselectAll()
810
811 parent = tree.GetItemParent(selected)
812 elem = tree.RemoveLeaf(selected)
813 nextItem = tree.GetFirstChild(parent)[0]
814 for i in range(index + 1): nextItem = tree.GetNextSibling(nextItem)
815 selected = tree.InsertNode(parent, tree.GetPyData(parent).treeObject(), elem, nextItem)
816 newIndex = tree.ItemIndex(selected)
817 tree.SelectItem(selected)
818
819 undoMan.RegisterUndo(UndoMove(parent, index, parent, newIndex))
820
821 self.modified = True
822 self.SetStatusText(status)
823
824 return
825
826 def OnMoveLeft(self, evt):
827 selected = tree.selection
828 if not selected: return
829
830 oldParent = tree.GetItemParent(selected)
831 if not oldParent: return
832 pparent = tree.GetItemParent(oldParent)
833 if not pparent: return
834
835 # Check compatibility
836 if not self.ItemsAreCompatible(tree.GetPyData(pparent).treeObject(), tree.GetPyData(selected).treeObject()): return
837
838 if g.testWin and g.testWin.highLight:
839 g.testWin.highLight.Remove()
840 tree.needUpdate = True
841
842 # Undo info
843 self.lastOp = 'MOVELEFT'
844 status = 'Made next sibling of parent'
845
846 # Prepare undo data
847 panel.Apply()
848 tree.UnselectAll()
849
850 oldIndex = tree.ItemIndex(selected)
851 elem = tree.RemoveLeaf(selected)
852 nextItem = tree.GetFirstChild(pparent)[0]
853 parentIndex = tree.ItemIndex(oldParent)
854 for i in range(parentIndex + 1): nextItem = tree.GetNextSibling(nextItem)
855
856 # Check parent and child relationships.
857 # If parent is sizer or notebook, child is of wrong class or
858 # parent is normal window, child is child container then detach child.
859 parent = tree.GetPyData(pparent).treeObject()
860 xxx = MakeXXXFromDOM(parent, elem)
861 isChildContainer = isinstance(xxx, xxxChildContainer)
862 if isChildContainer and \
863 ((parent.isSizer and not isinstance(xxx, xxxSizerItem)) or \
864 (isinstance(parent, xxxNotebook) and not isinstance(xxx, xxxNotebookPage)) or \
865 not (parent.isSizer or isinstance(parent, xxxNotebook))):
866 elem.removeChild(xxx.child.node) # detach child
867 elem.unlink() # delete child container
868 elem = xxx.child.node # replace
869 # This may help garbage collection
870 xxx.child.parent = None
871 isChildContainer = False
872 # Parent is sizer or notebook, child is not child container
873 if parent.isSizer and not isChildContainer and not isinstance(xxx, xxxSpacer):
874 # Create sizer item element
875 sizerItemElem = MakeEmptyDOM('sizeritem')
876 sizerItemElem.appendChild(elem)
877 elem = sizerItemElem
878 elif isinstance(parent, xxxNotebook) and not isChildContainer:
879 pageElem = MakeEmptyDOM('notebookpage')
880 pageElem.appendChild(elem)
881 elem = pageElem
882
883 selected = tree.InsertNode(pparent, tree.GetPyData(pparent).treeObject(), elem, nextItem)
884 newIndex = tree.ItemIndex(selected)
885 tree.SelectItem(selected)
886
887 undoMan.RegisterUndo(UndoMove(oldParent, oldIndex, pparent, newIndex))
888
889 self.modified = True
890 self.SetStatusText(status)
891
892 def OnMoveRight(self, evt):
893 selected = tree.selection
894 if not selected: return
895
896 oldParent = tree.GetItemParent(selected)
897 if not oldParent: return
898
899 newParent = tree.GetPrevSibling(selected)
900 if not newParent: return
901
902 parent = tree.GetPyData(newParent).treeObject()
903
904 # Check compatibility
905 if not self.ItemsAreCompatible(parent, tree.GetPyData(selected).treeObject()): return
906
907 # Remove highlight, update testWin
908 if g.testWin and g.testWin.highLight:
909 g.testWin.highLight.Remove()
910 tree.needUpdate = True
911
912 # Prepare undo data
913 panel.Apply()
914 tree.UnselectAll()
915
916 # Undo info
917 self.lastOp = 'MOVERIGHT'
918 status = 'Made last child of previous sibling'
919
920 oldIndex = tree.ItemIndex(selected)
921 elem = tree.RemoveLeaf(selected)
922
923 # Check parent and child relationships.
924 # If parent is sizer or notebook, child is of wrong class or
925 # parent is normal window, child is child container then detach child.
926 xxx = MakeXXXFromDOM(parent, elem)
927 isChildContainer = isinstance(xxx, xxxChildContainer)
928 if isChildContainer and \
929 ((parent.isSizer and not isinstance(xxx, xxxSizerItem)) or \
930 (isinstance(parent, xxxNotebook) and not isinstance(xxx, xxxNotebookPage)) or \
931 not (parent.isSizer or isinstance(parent, xxxNotebook))):
932 elem.removeChild(xxx.child.node) # detach child
933 elem.unlink() # delete child container
934 elem = xxx.child.node # replace
935 # This may help garbage collection
936 xxx.child.parent = None
937 isChildContainer = False
938 # Parent is sizer or notebook, child is not child container
939 if parent.isSizer and not isChildContainer and not isinstance(xxx, xxxSpacer):
940 # Create sizer item element
941 sizerItemElem = MakeEmptyDOM('sizeritem')
942 sizerItemElem.appendChild(elem)
943 elem = sizerItemElem
944 elif isinstance(parent, xxxNotebook) and not isChildContainer:
945 pageElem = MakeEmptyDOM('notebookpage')
946 pageElem.appendChild(elem)
947 elem = pageElem
948
949 selected = tree.InsertNode(newParent, tree.GetPyData(newParent).treeObject(), elem, wx.TreeItemId())
950
951 newIndex = tree.ItemIndex(selected)
952 tree.Expand(selected)
953 tree.SelectItem(selected)
954
955 undoMan.RegisterUndo(UndoMove(oldParent, oldIndex, newParent, newIndex))
956
957 self.modified = True
958 self.SetStatusText(status)
959
960 def OnCutDelete(self, evt):
961 selected = tree.selection
962 if not selected: return # key pressed event
963 # Undo info
964 if evt.GetId() == wx.ID_CUT:
965 self.lastOp = 'CUT'
966 status = 'Removed to clipboard'
967 else:
968 self.lastOp = 'DELETE'
969 status = 'Deleted'
970 # Delete testWin?
971 if g.testWin:
972 # If deleting top-level item, delete testWin
973 if selected == g.testWin.item:
974 g.testWin.Destroy()
975 g.testWin = None
976 else:
977 # Remove highlight, update testWin
978 if g.testWin.highLight:
979 g.testWin.highLight.Remove()
980 tree.needUpdate = True
981 # Prepare undo data
982 panel.Apply()
983 index = tree.ItemFullIndex(selected)
984 xxx = tree.GetPyData(selected)
985 parent = tree.GetPyData(tree.GetItemParent(selected)).treeObject()
986 tree.UnselectAll()
987 elem = tree.RemoveLeaf(selected)
988 undoMan.RegisterUndo(UndoCutDelete(index, parent, elem))
989 if evt.GetId() == wx.ID_CUT:
990 if wx.TheClipboard.Open():
991 if xxx.isElement:
992 data = wx.CustomDataObject('XRCED')
993 # (False, True)
994 s = elem.toxml(encoding=expat.native_encoding)
995 else:
996 data = wx.CustomDataObject('XRCED_node')
997 s = xxx.node.data
998 data.SetData(cPickle.dumps(s))
999 wx.TheClipboard.SetData(data)
1000 wx.TheClipboard.Close()
1001 else:
1002 wx.MessageBox("Unable to open the clipboard", "Error")
1003 tree.pendingHighLight = None
1004 # Update tools
1005 panel.Clear()
1006 self.SetModified()
1007 self.SetStatusText(status)
1008
1009 def OnSubclass(self, evt):
1010 selected = tree.selection
1011 xxx = tree.GetPyData(selected).treeObject()
1012 elem = xxx.node
1013 subclass = xxx.subclass
1014 dlg = wx.TextEntryDialog(self, 'Subclass:', defaultValue=subclass)
1015 if dlg.ShowModal() == wx.ID_OK:
1016 subclass = dlg.GetValue()
1017 if subclass:
1018 elem.setAttribute('subclass', subclass)
1019 elif elem.hasAttribute('subclass'):
1020 elem.removeAttribute('subclass')
1021 self.SetModified()
1022 xxx.subclass = elem.getAttribute('subclass')
1023 tree.SetItemText(selected, xxx.treeName())
1024 panel.pages[0].box.SetLabel(xxx.panelName())
1025 dlg.Destroy()
1026
1027 def OnEmbedPanel(self, evt):
1028 conf.embedPanel = evt.IsChecked()
1029 if conf.embedPanel:
1030 # Remember last dimentions
1031 conf.panelX, conf.panelY = self.miniFrame.GetPosition()
1032 conf.panelWidth, conf.panelHeight = self.miniFrame.GetSize()
1033 size = self.GetSize()
1034 pos = self.GetPosition()
1035 sizePanel = panel.GetSize()
1036 panel.Reparent(self.splitter)
1037 self.miniFrame.GetSizer().Remove(panel)
1038 # Widen
1039 self.SetDimensions(pos.x, pos.y, size.width + sizePanel.width, size.height)
1040 self.splitter.SplitVertically(tree, panel, conf.sashPos)
1041 self.miniFrame.Show(False)
1042 else:
1043 conf.sashPos = self.splitter.GetSashPosition()
1044 pos = self.GetPosition()
1045 size = self.GetSize()
1046 sizePanel = panel.GetSize()
1047 self.splitter.Unsplit(panel)
1048 sizer = self.miniFrame.GetSizer()
1049 panel.Reparent(self.miniFrame)
1050 panel.Show(True)
1051 sizer.Add(panel, 1, wx.EXPAND)
1052 self.miniFrame.Show(True)
1053 self.miniFrame.SetDimensions(conf.panelX, conf.panelY,
1054 conf.panelWidth, conf.panelHeight)
1055 self.miniFrame.Layout()
1056 # Reduce width
1057 self.SetDimensions(pos.x, pos.y,
1058 max(size.width - sizePanel.width, self.minWidth), size.height)
1059
1060 def OnShowTools(self, evt):
1061 conf.showTools = evt.IsChecked()
1062 g.tools.Show(conf.showTools)
1063 if conf.showTools:
1064 self.toolsSizer.Prepend(g.tools, 0, wx.EXPAND)
1065 else:
1066 self.toolsSizer.Remove(g.tools)
1067 self.toolsSizer.Layout()
1068
1069 def OnTest(self, evt):
1070 if not tree.selection: return # key pressed event
1071 tree.ShowTestWindow(tree.selection)
1072
1073 def OnTestHide(self, evt):
1074 tree.CloseTestWindow()
1075
1076 # Find object by relative position
1077 def FindObject(self, item, obj):
1078 # We simply perform depth-first traversal, sinse it's too much
1079 # hassle to deal with all sizer/window combinations
1080 w = tree.FindNodeObject(item)
1081 if w == obj or isinstance(w, wx.GBSizerItem) and w.GetWindow() == obj:
1082 return item
1083 if tree.ItemHasChildren(item):
1084 child = tree.GetFirstChild(item)[0]
1085 while child:
1086 found = self.FindObject(child, obj)
1087 if found: return found
1088 child = tree.GetNextSibling(child)
1089 return None
1090
1091 # Click event after locate activated
1092 def OnTestWinLeftDown(self, evt):
1093 # Restore normal event processing
1094 self.SetHandler(g.testWin)
1095 g.testWin.Disconnect(wx.ID_ANY, wx.ID_ANY, wx.wxEVT_LEFT_DOWN)
1096 item = self.FindObject(g.testWin.item, evt.GetEventObject())
1097 if item:
1098 tree.EnsureVisible(item)
1099 tree.SelectItem(item)
1100 self.tb.ToggleTool(self.ID_TOOL_LOCATE, False)
1101 if item:
1102 self.SetStatusText('Selected %s' % tree.GetItemText(item))
1103 else:
1104 self.SetStatusText('Locate failed!')
1105
1106 def SetHandler(self, w, h=None):
1107 if h:
1108 w.SetEventHandler(h)
1109 w.SetCursor(wx.CROSS_CURSOR)
1110 else:
1111 w.SetEventHandler(w)
1112 w.SetCursor(wx.NullCursor)
1113 for ch in w.GetChildren():
1114 self.SetHandler(ch, h)
1115
1116 def OnLocate(self, evt):
1117 if g.testWin:
1118 if evt.GetId() == self.ID_LOCATE or \
1119 evt.GetId() == self.ID_TOOL_LOCATE and evt.IsChecked():
1120 self.SetHandler(g.testWin, g.testWin)
1121 g.testWin.Connect(wx.ID_ANY, wx.ID_ANY, wx.wxEVT_LEFT_DOWN, self.OnTestWinLeftDown)
1122 if evt.GetId() == self.ID_LOCATE:
1123 self.tb.ToggleTool(self.ID_TOOL_LOCATE, True)
1124 elif evt.GetId() == self.ID_TOOL_LOCATE and not evt.IsChecked():
1125 self.SetHandler(g.testWin, None)
1126 g.testWin.Disconnect(wx.ID_ANY, wx.ID_ANY, wx.wxEVT_LEFT_DOWN)
1127 self.SetStatusText('Click somewhere in your test window now')
1128
1129 def OnRefresh(self, evt):
1130 # If modified, apply first
1131 selection = tree.selection
1132 if selection:
1133 xxx = tree.GetPyData(selection)
1134 if xxx and panel.IsModified():
1135 tree.Apply(xxx, selection)
1136 if g.testWin:
1137 # (re)create
1138 tree.CreateTestWin(g.testWin.item)
1139 panel.modified = False
1140 tree.needUpdate = False
1141
1142 def OnAutoRefresh(self, evt):
1143 conf.autoRefresh = evt.IsChecked()
1144 self.menuBar.Check(self.ID_AUTO_REFRESH, conf.autoRefresh)
1145 self.tb.ToggleTool(self.ID_AUTO_REFRESH, conf.autoRefresh)
1146
1147 def OnAbout(self, evt):
1148 str = '''\
1149 XRCed version %s
1150
1151 (c) Roman Rolinsky <rollrom@users.sourceforge.net>
1152 Homepage: http://xrced.sourceforge.net\
1153 ''' % version
1154 dlg = wx.MessageDialog(self, str, 'About XRCed', wx.OK | wx.CENTRE)
1155 dlg.ShowModal()
1156 dlg.Destroy()
1157
1158 def OnReadme(self, evt):
1159 text = open(os.path.join(basePath, 'README.txt'), 'r').read()
1160 dlg = ScrolledMessageDialog(self, text, "XRCed README")
1161 dlg.ShowModal()
1162 dlg.Destroy()
1163
1164 # Simple emulation of python command line
1165 def OnDebugCMD(self, evt):
1166 while 1:
1167 try:
1168 exec raw_input('C:\> ')
1169 except EOFError:
1170 print '^D'
1171 break
1172 except:
1173 (etype, value, tb) =sys.exc_info()
1174 tblist =traceback.extract_tb(tb)[1:]
1175 msg =' '.join(traceback.format_exception_only(etype, value)
1176 +traceback.format_list(tblist))
1177 print msg
1178
1179 def OnCreate(self, evt):
1180 # Ignore fake events generated while dragging
1181 if g.tools.drag:
1182 g.tools.drag = False
1183 return
1184 selected = tree.selection
1185 if tree.ctrl: appendChild = False
1186 else: appendChild = not tree.NeedInsert(selected)
1187 xxx = tree.GetPyData(selected)
1188 if not appendChild:
1189 # If insert before
1190 if tree.shift:
1191 # If has previous item, insert after it, else append to parent
1192 nextItem = selected
1193 parentLeaf = tree.GetItemParent(selected)
1194 else:
1195 # If has next item, insert, else append to parent
1196 nextItem = tree.GetNextSibling(selected)
1197 parentLeaf = tree.GetItemParent(selected)
1198 # Expanded container (must have children)
1199 elif tree.shift and tree.IsExpanded(selected) \
1200 and tree.GetChildrenCount(selected, False):
1201 nextItem = tree.GetFirstChild(selected)[0]
1202 parentLeaf = selected
1203 else:
1204 nextItem = wx.TreeItemId()
1205 parentLeaf = selected
1206 parent = tree.GetPyData(parentLeaf)
1207 if parent.hasChild: parent = parent.child
1208
1209 self.CreateXXX(parent, parentLeaf, nextItem, evt.GetId())
1210
1211 # Actual method to create object and add to XML and wx trees
1212 def CreateXXX(self, parent, parentLeaf, nextItem, id):
1213 selected = tree.selection
1214 # Create object_ref?
1215 if id == ID_NEW.REF:
1216 ref = wx.GetTextFromUser('Create reference to:', 'Create reference')
1217 if not ref: return
1218 xxx = MakeEmptyRefXXX(parent, ref)
1219 elif id == ID_NEW.COMMENT:
1220 xxx = MakeEmptyCommentXXX(parent)
1221 else:
1222 # Create empty element
1223 if id >= ID_NEW.CUSTOM:
1224 className = pullDownMenu.customMap[id]
1225 else:
1226 className = pullDownMenu.createMap[id]
1227 xxx = MakeEmptyXXX(parent, className)
1228
1229 # Insert new node, register undo
1230 if xxx.isElement: # true object
1231 # Set default name for top-level windows
1232 if parent.__class__ == xxxMainNode:
1233 cl = xxx.treeObject().__class__
1234 frame.maxIDs[cl] += 1
1235 xxx.setTreeName('%s%d' % (defaultIDs[cl], frame.maxIDs[cl]))
1236 # And for some other standard controls
1237 elif parent.__class__ == xxxStdDialogButtonSizer:
1238 # ... we can even set automatically tree name
1239 xxx.setTreeName(pullDownMenu.stdButtonIDs[id][0])
1240 obj = xxx.treeObject()
1241 # ... and label
1242 elem = g.tree.dom.createElement('label')
1243 elem.appendChild(g.tree.dom.createTextNode(pullDownMenu.stdButtonIDs[id][1]))
1244 obj.params['label'] = xxxParam(elem)
1245 xxx.treeObject().node.appendChild(elem)
1246 # Else, set label if exists to class name
1247 elif 'label' in xxx.treeObject().allParams:
1248 label = className
1249 if label[:2] == 'wx': label = label[2:]
1250 xxx.treeObject().set('label', label.upper())
1251 # For comment nodes, simply add node
1252 newItem = tree.InsertNode(parentLeaf, parent, xxx.node, nextItem)
1253 undoMan.RegisterUndo(UndoPasteCreate(parentLeaf, parent, newItem, selected))
1254 tree.EnsureVisible(newItem)
1255 tree.SelectItem(newItem)
1256 if not tree.IsVisible(newItem):
1257 tree.ScrollTo(newItem)
1258 tree.Refresh()
1259 # Update view?
1260 if xxx.isElement and g.testWin and tree.IsHighlatable(newItem):
1261 if conf.autoRefresh:
1262 tree.needUpdate = True
1263 tree.pendingHighLight = newItem
1264 else:
1265 tree.pendingHighLight = None
1266 tree.SetFocus()
1267 if not xxx.isElement:
1268 tree.EditLabel(newItem)
1269 self.SetModified()
1270 return xxx
1271
1272 # Replace one object with another
1273 def OnReplace(self, evt):
1274 selected = tree.selection
1275 xxx = tree.GetPyData(selected).treeObject()
1276 elem = xxx.node
1277 parent = elem.parentNode
1278 undoMan.RegisterUndo(UndoReplace(selected))
1279 # New class
1280 className = pullDownMenu.createMap[evt.GetId() - 1000]
1281
1282 # Create temporary empty node (with default values)
1283 dummy = MakeEmptyDOM(className)
1284 if className == 'spacer' and xxx.className != 'spacer':
1285 klass = xxxSpacer
1286 elif xxx.className == 'spacer' and className != 'spacer':
1287 klass = xxxSizerItem
1288 else:
1289 klass = xxxDict[className]
1290 # Remove non-compatible children
1291 if tree.ItemHasChildren(selected) and not klass.hasChildren:
1292 tree.DeleteChildren(selected)
1293 nodes = elem.childNodes[:]
1294 tags = []
1295 for node in nodes:
1296 if node.nodeType != minidom.Node.ELEMENT_NODE: continue
1297 remove = False
1298 tag = node.tagName
1299 if tag == 'object':
1300 if not klass.hasChildren: remove = True
1301 elif tag not in klass.allParams and \
1302 (not klass.hasStyle or tag not in klass.styles):
1303 remove = True
1304 else:
1305 tags.append(tag)
1306 if remove:
1307 elem.removeChild(node)
1308 node.unlink()
1309
1310 # Remove sizeritem child if spacer
1311 if className == 'spacer' and xxx.className != 'spacer':
1312 sizeritem = elem.parentNode
1313 assert sizeritem.getAttribute('class') == 'sizeritem'
1314 sizeritem.removeChild(elem)
1315 elem.unlink()
1316 elem = sizeritem
1317 tree.GetPyData(selected).hasChild = False
1318 elif xxx.className == 'spacer' and className != 'spacer':
1319 # Create sizeritem element
1320 assert xxx.parent.isSizer
1321 elem.setAttribute('class', 'sizeritem')
1322 node = MakeEmptyDOM(className)
1323 elem.appendChild(node)
1324 # Replace to point to new object
1325 xxx = xxxSizerItem(xxx.parent, elem)
1326 elem = node
1327 tree.SetPyData(selected, xxx)
1328 xxx = xxx.child
1329 else:
1330 # Copy parameters present in dummy but not in elem
1331 for node in dummy.childNodes:
1332 if node.tagName not in tags: elem.appendChild(node.cloneNode(True))
1333 dummy.unlink()
1334
1335 # Change class name
1336 elem.setAttribute('class', className)
1337 if elem.hasAttribute('subclass'):
1338 elem.removeAttribute('subclass') # clear subclassing
1339 # Re-create xxx element
1340 xxx = MakeXXXFromDOM(xxx.parent, elem)
1341 # Remove incompatible style flags
1342 if 'style' in xxx.params:
1343 styles = map(string.strip, xxx.params['style'].value().split('|'))
1344 newStyles = [s for s in styles if s in klass.winStyles or s in genericStyles]
1345 if newStyles != styles:
1346 if newStyles:
1347 value = reduce(lambda a,b: a+'|'+b, newStyles)
1348 else:
1349 value = ''
1350 xxx.params['style'].update(value)
1351
1352 # Update parent in child objects
1353 if tree.ItemHasChildren(selected):
1354 i, cookie = tree.GetFirstChild(selected)
1355 while i.IsOk():
1356 x = tree.GetPyData(i)
1357 x.parent = xxx
1358 if x.hasChild: x.child.parent = xxx
1359 i, cookie = tree.GetNextChild(selected, cookie)
1360
1361 # Update tree
1362 if tree.GetPyData(selected).hasChild: # child container
1363 container = tree.GetPyData(selected)
1364 container.resetChild(xxx)
1365 xxx = container
1366 else:
1367 tree.SetPyData(selected, xxx)
1368 tree.SetItemText(selected, xxx.treeName())
1369 tree.SetItemImage(selected, xxx.treeImage())
1370
1371 # Set default name for top-level windows
1372 if parent.__class__ == xxxMainNode:
1373 cl = xxx.treeObject().__class__
1374 frame.maxIDs[cl] += 1
1375 xxx.setTreeName('%s%d' % (defaultIDs[cl], frame.maxIDs[cl]))
1376
1377 # Update panel
1378 g.panel.SetData(xxx)
1379 # Update tools
1380 g.tools.UpdateUI()
1381
1382 #undoMan.RegisterUndo(UndoPasteCreate(parentLeaf, parent, newItem, selected))
1383 # Update view?
1384 if g.testWin and tree.IsHighlatable(selected):
1385 if conf.autoRefresh:
1386 tree.needUpdate = True
1387 tree.pendingHighLight = selected
1388 else:
1389 tree.pendingHighLight = None
1390 tree.SetFocus()
1391 self.SetModified()
1392
1393 # Expand/collapse subtree
1394 def OnExpand(self, evt):
1395 if tree.selection: tree.ExpandAll(tree.selection)
1396 else: tree.ExpandAll(tree.root)
1397 def OnCollapse(self, evt):
1398 if tree.selection: tree.CollapseAll(tree.selection)
1399 else: tree.CollapseAll(tree.root)
1400
1401 def OnPullDownHighlight(self, evt):
1402 menuId = evt.GetMenuId()
1403 if menuId != -1:
1404 menu = evt.GetEventObject()
1405 try:
1406 help = menu.GetHelpString(menuId)
1407 self.SetStatusText(help)
1408 except:
1409 self.SetStatusText('')
1410 else:
1411 self.SetStatusText('')
1412
1413 def OnUpdateUI(self, evt):
1414 if evt.GetId() in [wx.ID_CUT, wx.ID_COPY, self.ID_DELETE]:
1415 evt.Enable(tree.selection is not None and tree.selection != tree.root)
1416 elif evt.GetId() == wx.ID_SAVE:
1417 evt.Enable(self.modified)
1418 elif evt.GetId() in [wx.ID_PASTE, self.ID_TOOL_PASTE]:
1419 evt.Enable(tree.selection is not None)
1420 elif evt.GetId() in [self.ID_TEST,
1421 self.ID_MOVEUP, self.ID_MOVEDOWN,
1422 self.ID_MOVELEFT, self.ID_MOVERIGHT]:
1423 evt.Enable(tree.selection is not None and tree.selection != tree.root)
1424 elif evt.GetId() in [self.ID_LOCATE, self.ID_TOOL_LOCATE,
1425 self.ID_REFRESH]:
1426 evt.Enable(g.testWin is not None)
1427 elif evt.GetId() == wx.ID_UNDO: evt.Enable(undoMan.CanUndo())
1428 elif evt.GetId() == wx.ID_REDO: evt.Enable(undoMan.CanRedo())
1429
1430 def OnIdle(self, evt):
1431 if self.inIdle: return # Recursive call protection
1432 self.inIdle = True
1433 #print 'onidle',tree.needUpdate,tree.pendingHighLight
1434 try:
1435 if tree.needUpdate:
1436 if conf.autoRefresh:
1437 if g.testWin:
1438 #self.SetStatusText('Refreshing test window...')
1439 # (re)create
1440 tree.CreateTestWin(g.testWin.item)
1441 #self.SetStatusText('')
1442 tree.needUpdate = False
1443 elif tree.pendingHighLight:
1444 try:
1445 tree.HighLight(tree.pendingHighLight)
1446 except:
1447 # Remove highlight if any problem
1448 if g.testWin and g.testWin.highLight:
1449 g.testWin.highLight.Remove()
1450 tree.pendingHighLight = None
1451 raise
1452 else:
1453 evt.Skip()
1454 finally:
1455 self.inIdle = False
1456
1457 def OnIconize(self, evt):
1458 if evt.Iconized():
1459 conf.x, conf.y = self.GetPosition()
1460 conf.width, conf.height = self.GetSize()
1461 if conf.embedPanel:
1462 conf.sashPos = self.splitter.GetSashPosition()
1463 else:
1464 conf.panelX, conf.panelY = self.miniFrame.GetPosition()
1465 conf.panelWidth, conf.panelHeight = self.miniFrame.GetSize()
1466 self.miniFrame.Show(False)
1467 else:
1468 if not conf.embedPanel:
1469 self.miniFrame.Show(True)
1470 evt.Skip()
1471
1472 def OnCloseWindow(self, evt):
1473 if not self.AskSave(): return
1474 if g.testWin: g.testWin.Destroy()
1475 if not panel.GetPageCount() == 2:
1476 panel.page2.Destroy()
1477 else:
1478 # If we don't do this, page does not get destroyed (a bug?)
1479 panel.RemovePage(1)
1480 if not self.IsIconized():
1481 conf.x, conf.y = self.GetPosition()
1482 if wx.Platform == '__WXMAC__':
1483 conf.width, conf.height = self.GetClientSize()
1484 else:
1485 conf.width, conf.height = self.GetSize()
1486 if conf.embedPanel:
1487 conf.sashPos = self.splitter.GetSashPosition()
1488 else:
1489 conf.panelX, conf.panelY = self.miniFrame.GetPosition()
1490 conf.panelWidth, conf.panelHeight = self.miniFrame.GetSize()
1491 evt.Skip()
1492
1493 def CreateLocalConf(self, path):
1494 name = os.path.splitext(path)[0]
1495 name += '.xcfg'
1496 return wx.FileConfig(localFilename=name)
1497
1498 def Clear(self):
1499 self.dataFile = ''
1500 conf.localconf = None
1501 undoMan.Clear()
1502 self.SetModified(False)
1503 tree.Clear()
1504 panel.Clear()
1505 if g.testWin:
1506 g.testWin.Destroy()
1507 g.testWin = None
1508 # Numbers for new controls
1509 self.maxIDs = {}
1510 for cl in [xxxPanel, xxxDialog, xxxFrame,
1511 xxxMenuBar, xxxMenu, xxxToolBar,
1512 xxxWizard, xxxBitmap, xxxIcon]:
1513 self.maxIDs[cl] = 0
1514 # Restore handlers, menu, etc. to initial
1515 setHandlers(self.handlers[:])
1516 g.pullDownMenu.custom = self.custom[:]
1517 # Remove modules imported from comment directives
1518 map(sys.modules.pop, [m for m in sys.modules if m not in self.modules])
1519 xxxParamComment.locals = {} # clear local namespace
1520 xxxParamComment.allow = None # clear execution state
1521
1522 def SetModified(self, state=True):
1523 self.modified = state
1524 name = os.path.basename(self.dataFile)
1525 if not name: name = defaultName
1526 if state:
1527 self.SetTitle(progname + ': ' + name + ' *')
1528 else:
1529 self.SetTitle(progname + ': ' + name)
1530
1531 def Open(self, path):
1532 if not os.path.exists(path):
1533 wx.LogError('File does not exists: %s' % path)
1534 return False
1535 # Try to read the file
1536 try:
1537 f = open(path)
1538 self.Clear()
1539 dom = minidom.parse(f)
1540 f.close()
1541 # Set encoding global variable and default encoding
1542 if dom.encoding:
1543 g.currentEncoding = dom.encoding
1544 wx.SetDefaultPyEncoding(g.currentEncoding.encode())
1545 else:
1546 g.currentEncoding = ''
1547 # Change dir
1548 self.dataFile = path = os.path.abspath(path)
1549 dir = os.path.dirname(path)
1550 if dir: os.chdir(dir)
1551 # Allow importing modules from the same directory
1552 sys.path = sys_path + [dir]
1553 tree.SetData(dom)
1554 self.SetTitle(progname + ': ' + os.path.basename(path))
1555 conf.localconf = self.CreateLocalConf(self.dataFile)
1556 except:
1557 # Nice exception printing
1558 inf = sys.exc_info()
1559 wx.LogError(traceback.format_exception(inf[0], inf[1], None)[-1])
1560 wx.LogError('Error reading file: %s' % path)
1561 if debug: raise
1562 return False
1563 return True
1564
1565 def Indent(self, node, indent = 0):
1566 if node.nodeType == minidom.Node.COMMENT_NODE:
1567 text = self.domCopy.createTextNode('\n' + ' ' * indent)
1568 node.parentNode.insertBefore(text, node)
1569 return # no children
1570 # Copy child list because it will change soon
1571 children = node.childNodes[:]
1572 # Main node doesn't need to be indented
1573 if indent:
1574 text = self.domCopy.createTextNode('\n' + ' ' * indent)
1575 node.parentNode.insertBefore(text, node)
1576 if children:
1577 # Append newline after last child, except for text nodes
1578 if children[-1].nodeType == minidom.Node.ELEMENT_NODE:
1579 text = self.domCopy.createTextNode('\n' + ' ' * indent)
1580 node.appendChild(text)
1581 # Indent children which are elements
1582 for n in children:
1583 if n.nodeType == minidom.Node.ELEMENT_NODE or \
1584 n.nodeType == minidom.Node.COMMENT_NODE:
1585 self.Indent(n, indent + 2)
1586
1587 def Save(self, path):
1588 try:
1589 import codecs
1590 # Apply changes
1591 if tree.selection and panel.IsModified():
1592 self.OnRefresh(wx.CommandEvent())
1593 if g.currentEncoding:
1594 f = codecs.open(path, 'wt', g.currentEncoding)
1595 else:
1596 f = codecs.open(path, 'wt')
1597 # Make temporary copy for formatting it
1598 # !!! We can't clone dom node, it works only once
1599 #self.domCopy = tree.dom.cloneNode(True)
1600 self.domCopy = MyDocument()
1601 mainNode = self.domCopy.appendChild(tree.mainNode.cloneNode(True))
1602 # Remove first child (test element)
1603 testElem = mainNode.firstChild
1604 mainNode.removeChild(testElem)
1605 testElem.unlink()
1606 self.Indent(mainNode)
1607 self.domCopy.writexml(f, encoding = g.currentEncoding)
1608 f.close()
1609 self.domCopy.unlink()
1610 self.domCopy = None
1611 self.SetModified(False)
1612 panel.SetModified(False)
1613 conf.localconf.Flush()
1614 except:
1615 inf = sys.exc_info()
1616 wx.LogError(traceback.format_exception(inf[0], inf[1], None)[-1])
1617 wx.LogError('Error writing file: %s' % path)
1618 raise
1619
1620 def AskSave(self):
1621 if not (self.modified or panel.IsModified()): return True
1622 flags = wx.ICON_EXCLAMATION | wx.YES_NO | wx.CANCEL | wx.CENTRE
1623 dlg = wx.MessageDialog( self, 'File is modified. Save before exit?',
1624 'Save before too late?', flags )
1625 say = dlg.ShowModal()
1626 dlg.Destroy()
1627 wx.Yield()
1628 if say == wx.ID_YES:
1629 self.OnSaveOrSaveAs(wx.CommandEvent(wx.ID_SAVE))
1630 # If save was successful, modified flag is unset
1631 if not self.modified: return True
1632 elif say == wx.ID_NO:
1633 self.SetModified(False)
1634 panel.SetModified(False)
1635 return True
1636 return False
1637
1638 ################################################################################
1639
1640 class PythonOptions(wx.Dialog):
1641
1642 def __init__(self, parent, cfg, dataFile):
1643 pre = wx.PreDialog()
1644 g.frame.res.LoadOnDialog(pre, parent, "PYTHON_OPTIONS")
1645 self.PostCreate(pre)
1646
1647 self.cfg = cfg
1648 self.dataFile = dataFile
1649
1650 self.AutoGenerateCB = xrc.XRCCTRL(self, "AutoGenerateCB")
1651 self.EmbedCB = xrc.XRCCTRL(self, "EmbedCB")
1652 self.GettextCB = xrc.XRCCTRL(self, "GettextCB")
1653 self.MakeXRSFileCB = xrc.XRCCTRL(self, "MakeXRSFileCB")
1654 self.FileNameTC = xrc.XRCCTRL(self, "FileNameTC")
1655 self.BrowseBtn = xrc.XRCCTRL(self, "BrowseBtn")
1656 self.GenerateBtn = xrc.XRCCTRL(self, "GenerateBtn")
1657 self.SaveOptsBtn = xrc.XRCCTRL(self, "SaveOptsBtn")
1658
1659 self.Bind(wx.EVT_BUTTON, self.OnBrowse, self.BrowseBtn)
1660 self.Bind(wx.EVT_BUTTON, self.OnGenerate, self.GenerateBtn)
1661 self.Bind(wx.EVT_BUTTON, self.OnSaveOpts, self.SaveOptsBtn)
1662
1663 if self.cfg.Read("filename", "") != "":
1664 self.FileNameTC.SetValue(self.cfg.Read("filename"))
1665 else:
1666 name = os.path.splitext(os.path.split(dataFile)[1])[0]
1667 name += '_xrc.py'
1668 self.FileNameTC.SetValue(name)
1669 self.AutoGenerateCB.SetValue(self.cfg.ReadBool("autogenerate", False))
1670 self.EmbedCB.SetValue(self.cfg.ReadBool("embedResource", False))
1671 self.MakeXRSFileCB.SetValue(self.cfg.ReadBool("makeXRS", False))
1672 self.GettextCB.SetValue(self.cfg.ReadBool("genGettext", False))
1673
1674
1675 def OnBrowse(self, evt):
1676 path = self.FileNameTC.GetValue()
1677 dirname = os.path.abspath(os.path.dirname(path))
1678 name = os.path.split(path)[1]
1679 dlg = wx.FileDialog(self, 'Save As', dirname, name, '*.py',
1680 wx.SAVE | wx.OVERWRITE_PROMPT)
1681 if dlg.ShowModal() == wx.ID_OK:
1682 path = dlg.GetPath()
1683 self.FileNameTC.SetValue(path)
1684 dlg.Destroy()
1685
1686
1687 def OnGenerate(self, evt):
1688 pypath = self.FileNameTC.GetValue()
1689 embed = self.EmbedCB.GetValue()
1690 genGettext = self.GettextCB.GetValue()
1691 frame.GeneratePython(self.dataFile, pypath, embed, genGettext)
1692 self.OnSaveOpts()
1693
1694
1695 def OnSaveOpts(self, evt=None):
1696 self.cfg.Write("filename", self.FileNameTC.GetValue())
1697 self.cfg.WriteBool("autogenerate", self.AutoGenerateCB.GetValue())
1698 self.cfg.WriteBool("embedResource", self.EmbedCB.GetValue())
1699 self.cfg.WriteBool("makeXRS", self.MakeXRSFileCB.GetValue())
1700 self.cfg.WriteBool("genGettext", self.GettextCB.GetValue())
1701
1702 self.EndModal(wx.ID_OK)
1703
1704 ################################################################################
1705
1706 class PrefsDialog(wx.Dialog):
1707
1708 def __init__(self, parent):
1709 pre = wx.PreDialog()
1710 g.frame.res.LoadOnDialog(pre, parent, "DIALOG_PREFS")
1711 self.PostCreate(pre)
1712 self.checkControls = {} # map of check IDs to (control,dict,param)
1713
1714 ##xxx = sys.modules['xxx']
1715 import xxx
1716 d = xxx.xxxSizerItem.defaults_panel
1717
1718 self.check_proportion_panel = xrc.XRCCTRL(self, 'check_proportion_panel')
1719 id = self.check_proportion_panel.GetId()
1720 wx.EVT_CHECKBOX(self, id, self.OnCheck)
1721 self.checkControls[id] = (xrc.XRCCTRL(self, 'spin_proportion_panel'),
1722 d, 'option')
1723
1724 self.check_flag_panel = xrc.XRCCTRL(self, 'check_flag_panel')
1725 id = self.check_flag_panel.GetId()
1726 wx.EVT_CHECKBOX(self, id, self.OnCheck)
1727 self.checkControls[id] = (xrc.XRCCTRL(self, 'text_flag_panel'),
1728 d, 'flag')
1729
1730 d = xxx.xxxSizerItem.defaults_control
1731
1732 self.check_proportion_panel = xrc.XRCCTRL(self, 'check_proportion_control')
1733 id = self.check_proportion_panel.GetId()
1734 wx.EVT_CHECKBOX(self, id, self.OnCheck)
1735 self.checkControls[id] = (xrc.XRCCTRL(self, 'spin_proportion_control'),
1736 d, 'option')
1737
1738 self.check_flag_panel = xrc.XRCCTRL(self, 'check_flag_control')
1739 id = self.check_flag_panel.GetId()
1740 wx.EVT_CHECKBOX(self, id, self.OnCheck)
1741 self.checkControls[id] = (xrc.XRCCTRL(self, 'text_flag_control'),
1742 d, 'flag')
1743
1744 for id,cdp in self.checkControls.items():
1745 c,d,p = cdp
1746 try:
1747 if isinstance(c, wx.SpinCtrl):
1748 c.SetValue(int(d[p]))
1749 else:
1750 c.SetValue(d[p])
1751 self.FindWindowById(id).SetValue(True)
1752 except KeyError:
1753 c.Enable(False)
1754
1755 self.radio_allow_exec = xrc.XRCCTRL(self, 'radio_allow_exec')
1756 try:
1757 radio = {'ask': 0, 'yes':1, 'no':2}[g.conf.allowExec]
1758 except KeyError:
1759 radio = 0
1760 self.radio_allow_exec.SetSelection(radio)
1761
1762 def OnCheck(self, evt):
1763 self.checkControls[evt.GetId()][0].Enable(evt.IsChecked())
1764 evt.Skip()
1765
1766 ################################################################################
1767
1768 # Parse string in form var1=val1[,var2=val2]* as dictionary
1769 def ReadDictFromString(s):
1770 d = {}
1771 for vv in s.split(','):
1772 var,val = vv.split(':')
1773 d[var.strip()] = val
1774 return d
1775
1776 # Transform dictionary with strings into one string
1777 def DictToString(d):
1778 return ','.join(map(':'.join, d.items()))
1779
1780 def usage():
1781 print >> sys.stderr, 'usage: xrced [-dhiv] [file]'
1782
1783 class App(wx.App):
1784 def OnInit(self):
1785 # Check version
1786 if wx.VERSION[:3] < MinWxVersion:
1787 wx.LogWarning('''\
1788 This version of XRCed may not work correctly on your version of wxWidgets. \
1789 Please upgrade wxWidgets to %d.%d.%d or higher.''' % MinWxVersion)
1790 global debug
1791 # Process comand-line
1792 opts = args = None
1793 try:
1794 opts, args = getopt.getopt(sys.argv[1:], 'dhiv')
1795 for o,a in opts:
1796 if o == '-h':
1797 usage()
1798 sys.exit(0)
1799 elif o == '-d':
1800 debug = True
1801 elif o == '-v':
1802 print 'XRCed version', version
1803 sys.exit(0)
1804
1805 except getopt.GetoptError:
1806 if wx.Platform != '__WXMAC__': # macs have some extra parameters
1807 print >> sys.stderr, 'Unknown option'
1808 usage()
1809 sys.exit(1)
1810
1811 self.SetAppName('xrced')
1812 # Settings
1813 global conf
1814 conf = g.conf = wx.Config(style = wx.CONFIG_USE_LOCAL_FILE)
1815 conf.localconf = None
1816 conf.autoRefresh = conf.ReadInt('autorefresh', True)
1817 pos = conf.ReadInt('x', -1), conf.ReadInt('y', -1)
1818 size = conf.ReadInt('width', 800), conf.ReadInt('height', 600)
1819 conf.embedPanel = conf.ReadInt('embedPanel', True)
1820 conf.showTools = conf.ReadInt('showTools', True)
1821 conf.sashPos = conf.ReadInt('sashPos', 200)
1822
1823 # read recently used files
1824 g.fileHistory = wx.FileHistory()
1825 g.fileHistory.Load(conf)
1826
1827 if not conf.embedPanel:
1828 conf.panelX = conf.ReadInt('panelX', -1)
1829 conf.panelY = conf.ReadInt('panelY', -1)
1830 else:
1831 conf.panelX = conf.panelY = -1
1832 conf.panelWidth = conf.ReadInt('panelWidth', 200)
1833 conf.panelHeight = conf.ReadInt('panelHeight', 200)
1834 conf.panic = not conf.HasEntry('nopanic')
1835 # Preferences
1836 conf.allowExec = conf.Read('Prefs/allowExec', 'ask')
1837 p = 'Prefs/sizeritem_defaults_panel'
1838 import xxx
1839 if conf.HasEntry(p):
1840 ##sys.modules['xxx'].xxxSizerItem.defaults_panel = ReadDictFromString(conf.Read(p))
1841 xxx.xxxSizerItem.defaults_panel = ReadDictFromString(conf.Read(p))
1842 p = 'Prefs/sizeritem_defaults_control'
1843 if conf.HasEntry(p):
1844 ##sys.modules['xxx'].xxxSizerItem.defaults_control = ReadDictFromString(conf.Read(p))
1845 xxx.xxxSizerItem.defaults_control = ReadDictFromString(conf.Read(p))
1846
1847 # Add handlers
1848 wx.FileSystem.AddHandler(wx.MemoryFSHandler())
1849 self.toolArtProvider = ToolArtProvider()
1850 wx.ArtProvider.Push(self.toolArtProvider)
1851 # Create main frame
1852 frame = Frame(pos, size)
1853 # Mac does not set the correct size
1854 if wx.Platform == '__WXMAC__':
1855 frame.SetClientSize(size)
1856 frame.Show(True)
1857
1858 # Load plugins
1859 plugins = os.getenv('XRCEDPATH')
1860 if plugins:
1861 cwd = os.getcwd()
1862 try:
1863 for dir in plugins.split(':'):
1864 if os.path.isdir(dir) and \
1865 os.path.isfile(os.path.join(dir, '__init__.py')):
1866 # Normalize
1867 dir = os.path.abspath(os.path.normpath(dir))
1868 sys.path = sys_path + [os.path.dirname(dir)]
1869 try:
1870 os.chdir(dir)
1871 __import__(os.path.basename(dir), globals(), locals(), ['*'])
1872 except:
1873 print traceback.print_exc()
1874 finally:
1875 os.chdir(cwd)
1876 # Store important data
1877 frame.handlers = getHandlers()[:]
1878 frame.custom = g.pullDownMenu.custom[:]
1879 frame.modules = sys.modules.copy()
1880
1881 # Initialize
1882 frame.Clear()
1883
1884 # Load file after showing
1885 if args:
1886 conf.panic = False
1887 frame.open = frame.Open(args[0])
1888
1889 return True
1890
1891 def OnExit(self):
1892 # Write config
1893 global conf
1894 wc = conf
1895 wc.WriteInt('autorefresh', conf.autoRefresh)
1896 wc.WriteInt('x', conf.x)
1897 wc.WriteInt('y', conf.y)
1898 wc.WriteInt('width', conf.width)
1899 wc.WriteInt('height', conf.height)
1900 wc.WriteInt('embedPanel', conf.embedPanel)
1901 wc.WriteInt('showTools', conf.showTools)
1902 if not conf.embedPanel:
1903 wc.WriteInt('panelX', conf.panelX)
1904 wc.WriteInt('panelY', conf.panelY)
1905 wc.WriteInt('sashPos', conf.sashPos)
1906 wc.WriteInt('panelWidth', conf.panelWidth)
1907 wc.WriteInt('panelHeight', conf.panelHeight)
1908 wc.WriteInt('nopanic', 1)
1909 g.fileHistory.Save(wc)
1910 # Preferences
1911 wc.DeleteGroup('Prefs')
1912 wc.Write('Prefs/allowExec', conf.allowExec)
1913 import xxx
1914 ##v = sys.modules['xxx'].xxxSizerItem.defaults_panel
1915 v = xxx.xxxSizerItem.defaults_panel
1916 if v: wc.Write('Prefs/sizeritem_defaults_panel', DictToString(v))
1917 ###v = sys.modules['xxx'].xxxSizerItem.defaults_control
1918 v = xxx.xxxSizerItem.defaults_control
1919 if v: wc.Write('Prefs/sizeritem_defaults_control', DictToString(v))
1920
1921 wc.Flush()
1922
1923 def main():
1924 app = App(0, useBestVisual=False)
1925 #app.SetAssertMode(wx.PYAPP_ASSERT_LOG)
1926 app.MainLoop()
1927 app.OnExit()
1928 global conf
1929 del conf
1930
1931 if __name__ == '__main__':
1932 main()