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