]>
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 * | |
75aa1946 | 31 | from params import genericStyles |
d14a1e28 RD |
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 | ||
c0d5ae74 RR |
43 | # Remember system path |
44 | sys_path = sys.path | |
45 | ||
d14a1e28 RD |
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> | |
c0d5ae74 | 57 | Consult README.txt file for the details.</HTML> |
d14a1e28 RD |
58 | """ |
59 | ||
60 | defaultIDs = {xxxPanel:'PANEL', xxxDialog:'DIALOG', xxxFrame:'FRAME', | |
64bce500 | 61 | xxxMenuBar:'MENUBAR', xxxMenu:'MENU', xxxToolBar:'TOOLBAR', |
306b6fe9 | 62 | xxxWizard:'WIZARD', xxxBitmap:'BITMAP', xxxIcon:'ICON'} |
d14a1e28 | 63 | |
6cb85701 RR |
64 | defaultName = 'UNTITLED.xrc' |
65 | ||
d14a1e28 RD |
66 | ################################################################################ |
67 | ||
68 | # ScrolledMessageDialog - modified from wxPython lib to set fixed-width font | |
29a41103 RD |
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) | |
289128a4 | 75 | text.SetFont(g.modernFont()) |
29a41103 | 76 | dc = wx.WindowDC(text) |
289128a4 | 77 | w, h = dc.GetFullTextExtent(' ', g.modernFont())[:2] |
29a41103 | 78 | ok = wx.Button(self, wx.ID_OK, "OK") |
c0d5ae74 | 79 | ok.SetDefault() |
d14a1e28 RD |
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)) | |
c0d5ae74 RR |
82 | text.ShowPosition(1) # scroll to the first line |
83 | ok.SetConstraints(Layoutf('b=b5#1;x%w50#1;w!80;h!35', (self,))) | |
d14a1e28 RD |
84 | self.SetAutoLayout(True) |
85 | self.Fit() | |
29a41103 | 86 | self.CenterOnScreen(wx.BOTH) |
d14a1e28 RD |
87 | |
88 | ################################################################################ | |
89 | ||
fd919451 | 90 | # Event handler for using during location |
29a41103 | 91 | class Locator(wx.EvtHandler): |
fd919451 RR |
92 | def ProcessEvent(self, evt): |
93 | print evt | |
94 | ||
fa64a0c3 RR |
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 | ||
8ecb322e RR |
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 | ||
29a41103 | 137 | class Frame(wx.Frame): |
d14a1e28 | 138 | def __init__(self, pos, size): |
8ecb322e | 139 | wx.Frame.__init__(self, None, -1, '', pos, size) |
d14a1e28 RD |
140 | global frame |
141 | frame = g.frame = self | |
142 | bar = self.CreateStatusBar(2) | |
143 | bar.SetStatusWidths([-1, 40]) | |
144 | self.SetIcon(images.getIconIcon()) | |
fa64a0c3 RR |
145 | try: |
146 | self.tbicon = TaskBarIcon(self) | |
147 | except: | |
148 | self.tbicon = None | |
d14a1e28 RD |
149 | |
150 | # Idle flag | |
151 | self.inIdle = False | |
152 | ||
64bce500 | 153 | # Load our own resources |
37867b24 | 154 | self.res = xrc.EmptyXmlResource() |
d6922577 | 155 | # !!! Blocking of assert failure occurring in older unicode builds |
64bce500 | 156 | try: |
f65bb0f8 | 157 | quietlog = wx.LogNull() |
64bce500 RR |
158 | self.res.Load(os.path.join(basePath, 'xrced.xrc')) |
159 | except wx._core.PyAssertionError: | |
160 | print 'PyAssertionError was ignored' | |
161 | ||
d14a1e28 | 162 | # Make menus |
29a41103 | 163 | menuBar = wx.MenuBar() |
d14a1e28 | 164 | |
29a41103 RD |
165 | menu = wx.Menu() |
166 | menu.Append(wx.ID_NEW, '&New\tCtrl-N', 'New file') | |
80389ff7 | 167 | menu.AppendSeparator() |
29a41103 | 168 | menu.Append(wx.ID_OPEN, '&Open...\tCtrl-O', 'Open XRC file') |
52cd0643 | 169 | |
29a41103 | 170 | self.recentMenu = wx.Menu() |
52cd0643 RD |
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 | ||
80389ff7 | 176 | menu.AppendSeparator() |
29a41103 RD |
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() | |
73b2a9a7 RD |
180 | menu.Append(self.ID_GENERATE_PYTHON, '&Generate Python...', |
181 | 'Generate a Python module that uses this XRC') | |
d14a1e28 | 182 | menu.AppendSeparator() |
5050b626 RR |
183 | self.ID_PREFS = wx.NewId() |
184 | menu.Append(self.ID_PREFS, 'Preferences...', 'Change XRCed settings') | |
185 | menu.AppendSeparator() | |
29a41103 | 186 | menu.Append(wx.ID_EXIT, '&Quit\tCtrl-Q', 'Exit application') |
80389ff7 | 187 | |
d14a1e28 RD |
188 | menuBar.Append(menu, '&File') |
189 | ||
29a41103 RD |
190 | menu = wx.Menu() |
191 | menu.Append(wx.ID_UNDO, '&Undo\tCtrl-Z', 'Undo') | |
192 | menu.Append(wx.ID_REDO, '&Redo\tCtrl-Y', 'Redo') | |
d14a1e28 | 193 | menu.AppendSeparator() |
29a41103 RD |
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() | |
d14a1e28 | 198 | menu.Append(self.ID_DELETE, '&Delete\tCtrl-D', 'Delete object') |
64bce500 | 199 | menu.AppendSeparator() |
29a41103 RD |
200 | self.ID_LOCATE = wx.NewId() |
201 | self.ID_TOOL_LOCATE = wx.NewId() | |
8ecb322e | 202 | self.ART_LOCATE = 'ART_LOCATE' |
29a41103 | 203 | self.ID_TOOL_PASTE = wx.NewId() |
fd919451 | 204 | menu.Append(self.ID_LOCATE, '&Locate\tCtrl-L', 'Locate control in test window and select it') |
d14a1e28 | 205 | menuBar.Append(menu, '&Edit') |
29a41103 RD |
206 | menu = wx.Menu() |
207 | self.ID_EMBED_PANEL = wx.NewId() | |
d14a1e28 RD |
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) | |
29a41103 | 211 | self.ID_SHOW_TOOLS = wx.NewId() |
d14a1e28 RD |
212 | menu.Append(self.ID_SHOW_TOOLS, 'Show &Tools', 'Toggle tools', True) |
213 | menu.Check(self.ID_SHOW_TOOLS, conf.showTools) | |
214 | menu.AppendSeparator() | |
29a41103 | 215 | self.ID_TEST = wx.NewId() |
8ecb322e | 216 | self.ART_TEST = 'ART_TEST' |
fd919451 | 217 | menu.Append(self.ID_TEST, '&Test\tF5', 'Show test window') |
29a41103 | 218 | self.ID_REFRESH = wx.NewId() |
8ecb322e | 219 | self.ART_REFRESH = 'ART_REFRESH' |
d14a1e28 | 220 | menu.Append(self.ID_REFRESH, '&Refresh\tCtrl-R', 'Refresh test window') |
29a41103 | 221 | self.ID_AUTO_REFRESH = wx.NewId() |
8ecb322e | 222 | self.ART_AUTO_REFRESH = 'ART_AUTO_REFRESH' |
5050b626 | 223 | menu.Append(self.ID_AUTO_REFRESH, '&Auto-refresh\tAlt-A', |
d14a1e28 RD |
224 | 'Toggle auto-refresh mode', True) |
225 | menu.Check(self.ID_AUTO_REFRESH, conf.autoRefresh) | |
29a41103 | 226 | self.ID_TEST_HIDE = wx.NewId() |
4ecdf8fc | 227 | menu.Append(self.ID_TEST_HIDE, '&Hide\tF6', 'Close test window') |
d14a1e28 RD |
228 | menuBar.Append(menu, '&View') |
229 | ||
75aa1946 RR |
230 | menu = wx.Menu() |
231 | self.ID_MOVEUP = wx.NewId() | |
8ecb322e | 232 | self.ART_MOVEUP = 'ART_MOVEUP' |
75aa1946 RR |
233 | menu.Append(self.ID_MOVEUP, '&Up', 'Move before previous sibling') |
234 | self.ID_MOVEDOWN = wx.NewId() | |
8ecb322e | 235 | self.ART_MOVEDOWN = 'ART_MOVEDOWN' |
75aa1946 RR |
236 | menu.Append(self.ID_MOVEDOWN, '&Down', 'Move after next sibling') |
237 | self.ID_MOVELEFT = wx.NewId() | |
8ecb322e | 238 | self.ART_MOVELEFT = 'ART_MOVELEFT' |
75aa1946 RR |
239 | menu.Append(self.ID_MOVELEFT, '&Make sibling', 'Make sibling of parent') |
240 | self.ID_MOVERIGHT = wx.NewId() | |
8ecb322e | 241 | self.ART_MOVERIGHT = 'ART_MOVERIGHT' |
75aa1946 RR |
242 | menu.Append(self.ID_MOVERIGHT, '&Make child', 'Make child of previous sibling') |
243 | menuBar.Append(menu, '&Move') | |
244 | ||
29a41103 RD |
245 | menu = wx.Menu() |
246 | menu.Append(wx.ID_ABOUT, '&About...', 'About XCRed') | |
247 | self.ID_README = wx.NewId() | |
c0d5ae74 | 248 | menu.Append(self.ID_README, '&Readme...\tF1', 'View the README file') |
d14a1e28 | 249 | if debug: |
29a41103 | 250 | self.ID_DEBUG_CMD = wx.NewId() |
d14a1e28 | 251 | menu.Append(self.ID_DEBUG_CMD, 'CMD', 'Python command line') |
29a41103 | 252 | wx.EVT_MENU(self, self.ID_DEBUG_CMD, self.OnDebugCMD) |
d14a1e28 RD |
253 | menuBar.Append(menu, '&Help') |
254 | ||
255 | self.menuBar = menuBar | |
256 | self.SetMenuBar(menuBar) | |
257 | ||
258 | # Create toolbar | |
29a41103 | 259 | tb = self.CreateToolBar(wx.TB_HORIZONTAL | wx.NO_BORDER | wx.TB_FLAT) |
2702a0c1 | 260 | if wx.Platform != '__WXMAC__': |
83295419 RR |
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 | |
690a4c0a | 268 | if wx.Platform in ['__WXMAC__', '__WXMSW__']: |
83295419 | 269 | tb.SetToolBitmapSize((26,26)) |
83295419 | 270 | else: |
fa64a0c3 | 271 | tb.SetToolBitmapSize((24,24)) |
8ecb322e RR |
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) | |
83295419 RR |
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() | |
8ecb322e RR |
291 | bmp = wx.ArtProvider.GetBitmap(self.ART_LOCATE, wx.ART_TOOLBAR) |
292 | tb.AddSimpleTool(self.ID_TOOL_LOCATE, bmp, | |
83295419 | 293 | 'Locate', 'Locate control in test window and select it', True) |
8ecb322e RR |
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, | |
d14a1e28 | 300 | 'Auto-refresh', 'Toggle auto-refresh mode', True) |
83295419 | 301 | tb.AddSeparator() |
8ecb322e RR |
302 | bmp = wx.ArtProvider.GetBitmap(self.ART_MOVEUP, wx.ART_TOOLBAR) |
303 | tb.AddSimpleTool(self.ID_MOVEUP, bmp, | |
75aa1946 | 304 | 'Up', 'Move before previous sibling') |
8ecb322e RR |
305 | bmp = wx.ArtProvider.GetBitmap(self.ART_MOVEDOWN, wx.ART_TOOLBAR) |
306 | tb.AddSimpleTool(self.ID_MOVEDOWN, bmp, | |
75aa1946 | 307 | 'Down', 'Move after next sibling') |
8ecb322e RR |
308 | bmp = wx.ArtProvider.GetBitmap(self.ART_MOVELEFT, wx.ART_TOOLBAR) |
309 | tb.AddSimpleTool(self.ID_MOVELEFT, bmp, | |
75aa1946 | 310 | 'Make Sibling', 'Make sibling of parent') |
8ecb322e RR |
311 | bmp = wx.ArtProvider.GetBitmap(self.ART_MOVERIGHT, wx.ART_TOOLBAR) |
312 | tb.AddSimpleTool(self.ID_MOVERIGHT, bmp, | |
75aa1946 | 313 | 'Make Child', 'Make child of previous sibling') |
d14a1e28 RD |
314 | tb.ToggleTool(self.ID_AUTO_REFRESH, conf.autoRefresh) |
315 | tb.Realize() | |
64bce500 | 316 | |
d14a1e28 RD |
317 | self.tb = tb |
318 | self.minWidth = tb.GetSize()[0] # minimal width is the size of toolbar | |
319 | ||
320 | # File | |
29a41103 RD |
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) | |
5050b626 | 326 | wx.EVT_MENU(self, self.ID_PREFS, self.OnPrefs) |
29a41103 | 327 | wx.EVT_MENU(self, wx.ID_EXIT, self.OnExit) |
d14a1e28 | 328 | # Edit |
29a41103 RD |
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) | |
d14a1e28 | 338 | # View |
29a41103 RD |
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) | |
75aa1946 RR |
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) | |
d14a1e28 | 350 | # Help |
29a41103 RD |
351 | wx.EVT_MENU(self, wx.ID_ABOUT, self.OnAbout) |
352 | wx.EVT_MENU(self, self.ID_README, self.OnReadme) | |
d14a1e28 RD |
353 | |
354 | # Update events | |
29a41103 RD |
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) | |
690a4c0a RR |
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) | |
29a41103 | 370 | wx.EVT_UPDATE_UI(self, self.ID_REFRESH, self.OnUpdateUI) |
d14a1e28 RD |
371 | |
372 | # Build interface | |
29a41103 | 373 | sizer = wx.BoxSizer(wx.VERTICAL) |
da7d0696 | 374 | #sizer.Add(wx.StaticLine(self, -1), 0, wx.EXPAND) |
d14a1e28 | 375 | # Horizontal sizer for toolbar and splitter |
29a41103 RD |
376 | self.toolsSizer = sizer1 = wx.BoxSizer() |
377 | splitter = wx.SplitterWindow(self, -1, style=wx.SP_3DSASH) | |
d14a1e28 RD |
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) | |
29a41103 | 391 | if conf.showTools: sizer1.Add(tools, 0, wx.EXPAND) |
d14a1e28 RD |
392 | |
393 | tree.RegisterKeyEvents() | |
394 | ||
7df24e78 RR |
395 | # Miniframe for split mode |
396 | miniFrame = wx.MiniFrame(self, -1, 'Properties & Style', | |
397 | (conf.panelX, conf.panelY), | |
398 | (conf.panelWidth, conf.panelHeight)) | |
d14a1e28 | 399 | self.miniFrame = miniFrame |
29a41103 | 400 | sizer2 = wx.BoxSizer() |
d14a1e28 | 401 | miniFrame.SetSizer(sizer2) |
d14a1e28 RD |
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) | |
29a41103 | 410 | sizer2.Add(panel, 1, wx.EXPAND) |
d14a1e28 RD |
411 | miniFrame.Show(True) |
412 | splitter.Initialize(tree) | |
fa64a0c3 RR |
413 | if wx.Platform == '__WXMAC__': |
414 | sizer1.Add(splitter, 1, wx.EXPAND|wx.RIGHT, 5) | |
415 | else: | |
416 | sizer1.Add(splitter, 1, wx.EXPAND) | |
29a41103 | 417 | sizer.Add(sizer1, 1, wx.EXPAND) |
d14a1e28 RD |
418 | self.SetAutoLayout(True) |
419 | self.SetSizer(sizer) | |
420 | ||
d14a1e28 | 421 | # Other events |
29a41103 RD |
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) | |
80389ff7 | 427 | |
80389ff7 RR |
428 | def OnRecentFile(self,evt): |
429 | # open recently used file | |
430 | if not self.AskSave(): return | |
29a41103 | 431 | wx.BeginBusyCursor() |
52cd0643 RD |
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 | ||
29a41103 | 444 | wx.EndBusyCursor() |
d14a1e28 RD |
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 | |
29a41103 RD |
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: | |
d14a1e28 RD |
455 | path = dlg.GetPath() |
456 | self.SetStatusText('Loading...') | |
29a41103 | 457 | wx.BeginBusyCursor() |
016f67ba RR |
458 | try: |
459 | if self.Open(path): | |
460 | self.SetStatusText('Data loaded') | |
52cd0643 | 461 | self.SaveRecent(path) |
016f67ba RR |
462 | else: |
463 | self.SetStatusText('Failed') | |
016f67ba | 464 | finally: |
29a41103 | 465 | wx.EndBusyCursor() |
d14a1e28 RD |
466 | dlg.Destroy() |
467 | ||
468 | def OnSaveOrSaveAs(self, evt): | |
29a41103 | 469 | if evt.GetId() == wx.ID_SAVEAS or not self.dataFile: |
6cb85701 RR |
470 | if self.dataFile: name = '' |
471 | else: name = defaultName | |
207ebbbd | 472 | dirname = os.path.abspath(os.path.dirname(self.dataFile)) |
29a41103 RD |
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: | |
d14a1e28 | 476 | path = dlg.GetPath() |
cc202d9b RD |
477 | if isinstance(path, unicode): |
478 | path = path.encode(sys.getfilesystemencoding()) | |
d14a1e28 RD |
479 | dlg.Destroy() |
480 | else: | |
481 | dlg.Destroy() | |
482 | return | |
73b2a9a7 RD |
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) | |
d14a1e28 RD |
497 | else: |
498 | path = self.dataFile | |
499 | self.SetStatusText('Saving...') | |
29a41103 | 500 | wx.BeginBusyCursor() |
d14a1e28 | 501 | try: |
016f67ba RR |
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 | |
52cd0643 | 508 | self.SetModified(False) |
73b2a9a7 RD |
509 | if conf.localconf.ReadBool("autogenerate", False): |
510 | pypath = conf.localconf.Read("filename") | |
511 | embed = conf.localconf.ReadBool("embedResource", False) | |
7e05216c RD |
512 | genGettext = conf.localconf.ReadBool("genGettext", False) |
513 | self.GeneratePython(self.dataFile, pypath, embed, genGettext) | |
73b2a9a7 | 514 | |
016f67ba RR |
515 | self.SetStatusText('Data saved') |
516 | self.SaveRecent(path) | |
517 | except IOError: | |
518 | self.SetStatusText('Failed') | |
519 | finally: | |
29a41103 | 520 | wx.EndBusyCursor() |
80389ff7 RR |
521 | |
522 | def SaveRecent(self,path): | |
523 | # append to recently used files | |
52cd0643 | 524 | g.fileHistory.AddFileToHistory(path) |
d14a1e28 | 525 | |
7e05216c | 526 | def GeneratePython(self, dataFile, pypath, embed, genGettext): |
73b2a9a7 RD |
527 | try: |
528 | import wx.tools.pywxrc | |
529 | rescomp = wx.tools.pywxrc.XmlResourceCompiler() | |
0fe9c329 | 530 | rescomp.MakePythonModule([dataFile], pypath, embed, genGettext) |
73b2a9a7 RD |
531 | except: |
532 | inf = sys.exc_info() | |
29a41103 RD |
533 | wx.LogError(traceback.format_exception(inf[0], inf[1], None)[-1]) |
534 | wx.LogError('Error generating python code : %s' % pypath) | |
73b2a9a7 RD |
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() | |
5050b626 RR |
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] | |
7058b794 | 556 | g.conf.allowExec = ('ask', 'yes', 'no')[dlg.radio_allow_exec.GetSelection()] |
5050b626 | 557 | dlg.Destroy() |
73b2a9a7 | 558 | |
d14a1e28 RD |
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() | |
a023b456 RR |
566 | g.panel.SetModified(False) |
567 | if not undoMan.CanUndo(): | |
568 | self.SetModified(False) | |
d14a1e28 RD |
569 | |
570 | def OnRedo(self, evt): | |
571 | if undoMan.CanRedo(): | |
572 | undoMan.Redo() | |
a023b456 | 573 | self.SetModified(True) |
d14a1e28 RD |
574 | |
575 | def OnCopy(self, evt): | |
576 | selected = tree.selection | |
577 | if not selected: return # key pressed event | |
578 | xxx = tree.GetPyData(selected) | |
f65bb0f8 | 579 | if wx.TheClipboard.Open(): |
b372319f RR |
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 | |
ebaaf8f6 | 588 | data.SetData(cPickle.dumps(s)) |
f65bb0f8 RD |
589 | wx.TheClipboard.SetData(data) |
590 | wx.TheClipboard.Close() | |
591 | self.SetStatusText('Copied') | |
592 | else: | |
593 | wx.MessageBox("Unable to open the clipboard", "Error") | |
d14a1e28 RD |
594 | |
595 | def OnPaste(self, evt): | |
596 | selected = tree.selection | |
597 | if not selected: return # key pressed event | |
598 | # For pasting with Ctrl pressed | |
016f67ba | 599 | appendChild = True |
d14a1e28 | 600 | if evt.GetId() == pullDownMenu.ID_PASTE_SIBLING: appendChild = False |
016f67ba RR |
601 | elif evt.GetId() == self.ID_TOOL_PASTE: |
602 | if g.tree.ctrl: appendChild = False | |
603 | else: appendChild = not tree.NeedInsert(selected) | |
d14a1e28 RD |
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 | |
a4c013b2 | 613 | nextItem = tree.GetFirstChild(selected)[0] |
d14a1e28 RD |
614 | parentLeaf = selected |
615 | else: | |
616 | # No children or unexpanded item - appendChild stays True | |
29a41103 | 617 | nextItem = wx.TreeItemId() # no next item |
d14a1e28 RD |
618 | parentLeaf = selected |
619 | parent = tree.GetPyData(parentLeaf).treeObject() | |
620 | ||
af52e185 | 621 | # Create a copy of clipboard pickled element |
b372319f | 622 | success = success_node = False |
f65bb0f8 | 623 | if wx.TheClipboard.Open(): |
c0d5ae74 RR |
624 | try: |
625 | data = wx.CustomDataObject('XRCED') | |
b372319f | 626 | if wx.TheClipboard.IsSupported(data.GetFormat()): |
c0d5ae74 RR |
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() | |
f65bb0f8 | 639 | |
b372319f | 640 | if not success and not success_node: |
f65bb0f8 RD |
641 | wx.MessageBox( |
642 | "There is no data in the clipboard in the required format", | |
643 | "Error") | |
af52e185 | 644 | return |
f65bb0f8 | 645 | |
1893848e | 646 | xml = cPickle.loads(data.GetData()) # xml representation of element |
b372319f RR |
647 | if success: |
648 | elem = minidom.parseString(xml).childNodes[0] | |
649 | else: | |
650 | elem = g.tree.dom.createComment(xml) | |
75aa1946 | 651 | |
d14a1e28 RD |
652 | # Tempopary xxx object to test things |
653 | xxx = MakeXXXFromDOM(parent, elem) | |
75aa1946 | 654 | |
d14a1e28 | 655 | # Check compatibility |
75aa1946 | 656 | if not self.ItemsAreCompatible(parent, xxx.treeObject()): return |
d14a1e28 RD |
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) | |
306b6fe9 | 662 | parentIsBook = parent.__class__ in [xxxNotebook, xxxChoicebook, xxxListbook] |
d14a1e28 RD |
663 | if isChildContainer and \ |
664 | ((parent.isSizer and not isinstance(xxx, xxxSizerItem)) or \ | |
306b6fe9 RR |
665 | (parentIsBook and not isinstance(xxx, xxxPage)) or \ |
666 | not (parent.isSizer or parentIsBook)): | |
b372319f | 667 | elem.removeChild(xxx.child.node) # detach child |
d14a1e28 | 668 | elem.unlink() # delete child container |
b372319f | 669 | elem = xxx.child.node # replace |
d14a1e28 RD |
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 | |
64b9ac75 | 676 | sizerItemElem = MakeEmptyDOM(parent.itemTag) |
d14a1e28 RD |
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 | |
306b6fe9 RR |
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 | |
d14a1e28 RD |
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 | |
6cb85701 | 707 | self.SetModified() |
d14a1e28 RD |
708 | self.SetStatusText('Pasted') |
709 | ||
75aa1946 RR |
710 | |
711 | def ItemsAreCompatible(self, parent, child): | |
712 | # Check compatibility | |
713 | error = False | |
b372319f RR |
714 | # Comments are always compatible |
715 | if child.__class__ == xxxComment: | |
716 | return True | |
75aa1946 RR |
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 | |
44271475 RR |
728 | elif not parent.hasChildren: |
729 | error = True | |
75aa1946 RR |
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 | ||
2fe11431 | 762 | # Remove highlight, update testWin |
09a68e88 | 763 | if g.testWin and g.testWin.highLight: |
2fe11431 RR |
764 | g.testWin.highLight.Remove() |
765 | tree.needUpdate = True | |
766 | ||
75aa1946 RR |
767 | # Undo info |
768 | self.lastOp = 'MOVEUP' | |
769 | status = 'Moved before previous sibling' | |
770 | ||
771 | # Prepare undo data | |
772 | panel.Apply() | |
2fe11431 | 773 | tree.UnselectAll() |
75aa1946 RR |
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 | ||
2fe11431 | 798 | # Remove highlight, update testWin |
09a68e88 | 799 | if g.testWin and g.testWin.highLight: |
2fe11431 RR |
800 | g.testWin.highLight.Remove() |
801 | tree.needUpdate = True | |
802 | ||
75aa1946 RR |
803 | # Undo info |
804 | self.lastOp = 'MOVEDOWN' | |
805 | status = 'Moved after next sibling' | |
806 | ||
807 | # Prepare undo data | |
808 | panel.Apply() | |
2fe11431 | 809 | tree.UnselectAll() |
75aa1946 RR |
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 | ||
09a68e88 | 838 | if g.testWin and g.testWin.highLight: |
2fe11431 | 839 | g.testWin.highLight.Remove() |
44271475 | 840 | tree.needUpdate = True |
2fe11431 | 841 | |
75aa1946 RR |
842 | # Undo info |
843 | self.lastOp = 'MOVELEFT' | |
844 | status = 'Made next sibling of parent' | |
845 | ||
44271475 RR |
846 | # Prepare undo data |
847 | panel.Apply() | |
848 | tree.UnselectAll() | |
849 | ||
75aa1946 RR |
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))): | |
b372319f | 866 | elem.removeChild(xxx.child.node) # detach child |
75aa1946 | 867 | elem.unlink() # delete child container |
b372319f | 868 | elem = xxx.child.node # replace |
75aa1946 RR |
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 | ||
2fe11431 | 907 | # Remove highlight, update testWin |
09a68e88 | 908 | if g.testWin and g.testWin.highLight: |
2fe11431 | 909 | g.testWin.highLight.Remove() |
44271475 RR |
910 | tree.needUpdate = True |
911 | ||
912 | # Prepare undo data | |
913 | panel.Apply() | |
914 | tree.UnselectAll() | |
2fe11431 | 915 | |
75aa1946 RR |
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))): | |
b372319f | 932 | elem.removeChild(xxx.child.node) # detach child |
75aa1946 | 933 | elem.unlink() # delete child container |
b372319f | 934 | elem = xxx.child.node # replace |
75aa1946 RR |
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) | |
44271475 | 952 | tree.Expand(selected) |
75aa1946 RR |
953 | tree.SelectItem(selected) |
954 | ||
955 | undoMan.RegisterUndo(UndoMove(oldParent, oldIndex, newParent, newIndex)) | |
2fe11431 | 956 | |
75aa1946 RR |
957 | self.modified = True |
958 | self.SetStatusText(status) | |
959 | ||
d14a1e28 RD |
960 | def OnCutDelete(self, evt): |
961 | selected = tree.selection | |
962 | if not selected: return # key pressed event | |
963 | # Undo info | |
29a41103 | 964 | if evt.GetId() == wx.ID_CUT: |
d14a1e28 RD |
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) | |
b372319f | 984 | xxx = tree.GetPyData(selected) |
d14a1e28 | 985 | parent = tree.GetPyData(tree.GetItemParent(selected)).treeObject() |
9e20825c | 986 | tree.UnselectAll() |
d14a1e28 RD |
987 | elem = tree.RemoveLeaf(selected) |
988 | undoMan.RegisterUndo(UndoCutDelete(index, parent, elem)) | |
29a41103 | 989 | if evt.GetId() == wx.ID_CUT: |
f65bb0f8 | 990 | if wx.TheClipboard.Open(): |
b372319f RR |
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 | |
ebaaf8f6 | 998 | data.SetData(cPickle.dumps(s)) |
f65bb0f8 RD |
999 | wx.TheClipboard.SetData(data) |
1000 | wx.TheClipboard.Close() | |
1001 | else: | |
1002 | wx.MessageBox("Unable to open the clipboard", "Error") | |
d14a1e28 | 1003 | tree.pendingHighLight = None |
6cb85701 | 1004 | # Update tools |
d14a1e28 | 1005 | panel.Clear() |
6cb85701 | 1006 | self.SetModified() |
d14a1e28 RD |
1007 | self.SetStatusText(status) |
1008 | ||
2481bf3c RR |
1009 | def OnSubclass(self, evt): |
1010 | selected = tree.selection | |
1011 | xxx = tree.GetPyData(selected).treeObject() | |
b372319f | 1012 | elem = xxx.node |
2481bf3c | 1013 | subclass = xxx.subclass |
29a41103 RD |
1014 | dlg = wx.TextEntryDialog(self, 'Subclass:', defaultValue=subclass) |
1015 | if dlg.ShowModal() == wx.ID_OK: | |
2481bf3c RR |
1016 | subclass = dlg.GetValue() |
1017 | if subclass: | |
1018 | elem.setAttribute('subclass', subclass) | |
2481bf3c RR |
1019 | elif elem.hasAttribute('subclass'): |
1020 | elem.removeAttribute('subclass') | |
6cb85701 | 1021 | self.SetModified() |
2481bf3c RR |
1022 | xxx.subclass = elem.getAttribute('subclass') |
1023 | tree.SetItemText(selected, xxx.treeName()) | |
1024 | panel.pages[0].box.SetLabel(xxx.panelName()) | |
1025 | dlg.Destroy() | |
1026 | ||
d14a1e28 RD |
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) | |
80389ff7 | 1037 | self.miniFrame.GetSizer().Remove(panel) |
d14a1e28 RD |
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) | |
29a41103 | 1051 | sizer.Add(panel, 1, wx.EXPAND) |
d14a1e28 RD |
1052 | self.miniFrame.Show(True) |
1053 | self.miniFrame.SetDimensions(conf.panelX, conf.panelY, | |
1054 | conf.panelWidth, conf.panelHeight) | |
7df24e78 | 1055 | self.miniFrame.Layout() |
d14a1e28 RD |
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: | |
29a41103 | 1064 | self.toolsSizer.Prepend(g.tools, 0, wx.EXPAND) |
d14a1e28 RD |
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 | ||
64bce500 RR |
1073 | def OnTestHide(self, evt): |
1074 | tree.CloseTestWindow() | |
1075 | ||
fd919451 RR |
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) | |
29a41103 | 1081 | if w == obj or isinstance(w, wx.GBSizerItem) and w.GetWindow() == obj: |
fd919451 RR |
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 | ||
da7d0696 | 1091 | # Click event after locate activated |
fd919451 | 1092 | def OnTestWinLeftDown(self, evt): |
da7d0696 | 1093 | # Restore normal event processing |
fd919451 | 1094 | self.SetHandler(g.testWin) |
29a41103 | 1095 | g.testWin.Disconnect(wx.ID_ANY, wx.ID_ANY, wx.wxEVT_LEFT_DOWN) |
fd919451 RR |
1096 | item = self.FindObject(g.testWin.item, evt.GetEventObject()) |
1097 | if item: | |
20002db0 | 1098 | tree.EnsureVisible(item) |
fd919451 | 1099 | tree.SelectItem(item) |
016f67ba | 1100 | self.tb.ToggleTool(self.ID_TOOL_LOCATE, False) |
64bce500 RR |
1101 | if item: |
1102 | self.SetStatusText('Selected %s' % tree.GetItemText(item)) | |
1103 | else: | |
1104 | self.SetStatusText('Locate failed!') | |
fd919451 RR |
1105 | |
1106 | def SetHandler(self, w, h=None): | |
1107 | if h: | |
1108 | w.SetEventHandler(h) | |
29a41103 | 1109 | w.SetCursor(wx.CROSS_CURSOR) |
fd919451 RR |
1110 | else: |
1111 | w.SetEventHandler(w) | |
29a41103 | 1112 | w.SetCursor(wx.NullCursor) |
fd919451 RR |
1113 | for ch in w.GetChildren(): |
1114 | self.SetHandler(ch, h) | |
1115 | ||
1116 | def OnLocate(self, evt): | |
1117 | if g.testWin: | |
64bce500 | 1118 | if evt.GetId() == self.ID_LOCATE or \ |
016f67ba | 1119 | evt.GetId() == self.ID_TOOL_LOCATE and evt.IsChecked(): |
64bce500 | 1120 | self.SetHandler(g.testWin, g.testWin) |
29a41103 | 1121 | g.testWin.Connect(wx.ID_ANY, wx.ID_ANY, wx.wxEVT_LEFT_DOWN, self.OnTestWinLeftDown) |
64bce500 | 1122 | if evt.GetId() == self.ID_LOCATE: |
016f67ba RR |
1123 | self.tb.ToggleTool(self.ID_TOOL_LOCATE, True) |
1124 | elif evt.GetId() == self.ID_TOOL_LOCATE and not evt.IsChecked(): | |
64bce500 | 1125 | self.SetHandler(g.testWin, None) |
29a41103 | 1126 | g.testWin.Disconnect(wx.ID_ANY, wx.ID_ANY, wx.wxEVT_LEFT_DOWN) |
64bce500 | 1127 | self.SetStatusText('Click somewhere in your test window now') |
fd919451 | 1128 | |
d14a1e28 RD |
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 | |
29a41103 | 1154 | dlg = wx.MessageDialog(self, str, 'About XRCed', wx.OK | wx.CENTRE) |
d14a1e28 RD |
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): | |
d14a1e28 RD |
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): | |
ed650677 RR |
1180 | # Ignore fake events generated while dragging |
1181 | if g.tools.drag: | |
1182 | g.tools.drag = False | |
1183 | return | |
d14a1e28 RD |
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): | |
a4c013b2 | 1201 | nextItem = tree.GetFirstChild(selected)[0] |
d14a1e28 RD |
1202 | parentLeaf = selected |
1203 | else: | |
29a41103 | 1204 | nextItem = wx.TreeItemId() |
d14a1e28 RD |
1205 | parentLeaf = selected |
1206 | parent = tree.GetPyData(parentLeaf) | |
1207 | if parent.hasChild: parent = parent.child | |
1208 | ||
da7d0696 RR |
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 | |
6cb85701 | 1214 | # Create object_ref? |
da7d0696 | 1215 | if id == ID_NEW.REF: |
29a41103 | 1216 | ref = wx.GetTextFromUser('Create reference to:', 'Create reference') |
6cb85701 RR |
1217 | if not ref: return |
1218 | xxx = MakeEmptyRefXXX(parent, ref) | |
da7d0696 | 1219 | elif id == ID_NEW.COMMENT: |
b372319f | 1220 | xxx = MakeEmptyCommentXXX(parent) |
6cb85701 RR |
1221 | else: |
1222 | # Create empty element | |
da7d0696 RR |
1223 | if id >= ID_NEW.CUSTOM: |
1224 | className = pullDownMenu.customMap[id] | |
c0d5ae74 | 1225 | else: |
da7d0696 | 1226 | className = pullDownMenu.createMap[id] |
6cb85701 | 1227 | xxx = MakeEmptyXXX(parent, className) |
d14a1e28 | 1228 | |
d14a1e28 | 1229 | # Insert new node, register undo |
b372319f RR |
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: | |
da7d0696 RR |
1238 | # ... we can even set automatically tree name |
1239 | xxx.setTreeName(pullDownMenu.stdButtonIDs[id][0]) | |
b372319f | 1240 | obj = xxx.treeObject() |
da7d0696 | 1241 | # ... and label |
b372319f | 1242 | elem = g.tree.dom.createElement('label') |
da7d0696 | 1243 | elem.appendChild(g.tree.dom.createTextNode(pullDownMenu.stdButtonIDs[id][1])) |
b372319f RR |
1244 | obj.params['label'] = xxxParam(elem) |
1245 | xxx.treeObject().node.appendChild(elem) | |
da7d0696 RR |
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) | |
d14a1e28 RD |
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? | |
b372319f | 1260 | if xxx.isElement and g.testWin and tree.IsHighlatable(newItem): |
d14a1e28 RD |
1261 | if conf.autoRefresh: |
1262 | tree.needUpdate = True | |
1263 | tree.pendingHighLight = newItem | |
1264 | else: | |
1265 | tree.pendingHighLight = None | |
1266 | tree.SetFocus() | |
fe295b0d RR |
1267 | if not xxx.isElement: |
1268 | tree.EditLabel(newItem) | |
6cb85701 | 1269 | self.SetModified() |
da7d0696 | 1270 | return xxx |
6cb85701 | 1271 | |
d14a1e28 RD |
1272 | # Replace one object with another |
1273 | def OnReplace(self, evt): | |
1274 | selected = tree.selection | |
1275 | xxx = tree.GetPyData(selected).treeObject() | |
b372319f | 1276 | elem = xxx.node |
d14a1e28 | 1277 | parent = elem.parentNode |
baba4aa5 | 1278 | undoMan.RegisterUndo(UndoReplace(selected)) |
d14a1e28 RD |
1279 | # New class |
1280 | className = pullDownMenu.createMap[evt.GetId() - 1000] | |
75aa1946 | 1281 | |
d14a1e28 RD |
1282 | # Create temporary empty node (with default values) |
1283 | dummy = MakeEmptyDOM(className) | |
baba4aa5 RR |
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] | |
d14a1e28 | 1290 | # Remove non-compatible children |
baba4aa5 | 1291 | if tree.ItemHasChildren(selected) and not klass.hasChildren: |
d14a1e28 RD |
1292 | tree.DeleteChildren(selected) |
1293 | nodes = elem.childNodes[:] | |
1294 | tags = [] | |
1295 | for node in nodes: | |
64bce500 | 1296 | if node.nodeType != minidom.Node.ELEMENT_NODE: continue |
d14a1e28 RD |
1297 | remove = False |
1298 | tag = node.tagName | |
1299 | if tag == 'object': | |
baba4aa5 RR |
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): | |
d14a1e28 RD |
1303 | remove = True |
1304 | else: | |
1305 | tags.append(tag) | |
1306 | if remove: | |
1307 | elem.removeChild(node) | |
1308 | node.unlink() | |
1309 | ||
baba4aa5 RR |
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 | |
75aa1946 | 1317 | tree.GetPyData(selected).hasChild = False |
baba4aa5 RR |
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)) | |
d14a1e28 | 1333 | dummy.unlink() |
baba4aa5 | 1334 | |
d14a1e28 | 1335 | # Change class name |
baba4aa5 | 1336 | elem.setAttribute('class', className) |
64bce500 RR |
1337 | if elem.hasAttribute('subclass'): |
1338 | elem.removeAttribute('subclass') # clear subclassing | |
d14a1e28 | 1339 | # Re-create xxx element |
baba4aa5 | 1340 | xxx = MakeXXXFromDOM(xxx.parent, elem) |
75aa1946 RR |
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 | ||
d14a1e28 RD |
1352 | # Update parent in child objects |
1353 | if tree.ItemHasChildren(selected): | |
a4c013b2 | 1354 | i, cookie = tree.GetFirstChild(selected) |
d14a1e28 RD |
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) | |
baba4aa5 | 1360 | |
d14a1e28 RD |
1361 | # Update tree |
1362 | if tree.GetPyData(selected).hasChild: # child container | |
1363 | container = tree.GetPyData(selected) | |
75aa1946 | 1364 | container.resetChild(xxx) |
baba4aa5 | 1365 | xxx = container |
d14a1e28 RD |
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 | |
64b9ac75 | 1375 | xxx.setTreeName('%s%d' % (defaultIDs[cl], frame.maxIDs[cl])) |
d14a1e28 RD |
1376 | |
1377 | # Update panel | |
1378 | g.panel.SetData(xxx) | |
1379 | # Update tools | |
1380 | g.tools.UpdateUI() | |
1381 | ||
57c48fc4 | 1382 | #undoMan.RegisterUndo(UndoPasteCreate(parentLeaf, parent, newItem, selected)) |
d14a1e28 RD |
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() | |
6cb85701 | 1391 | self.SetModified() |
d14a1e28 RD |
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() | |
fa64a0c3 RR |
1405 | try: |
1406 | help = menu.GetHelpString(menuId) | |
1407 | self.SetStatusText(help) | |
1408 | except: | |
1409 | self.SetStatusText('') | |
d14a1e28 RD |
1410 | else: |
1411 | self.SetStatusText('') | |
1412 | ||
1413 | def OnUpdateUI(self, evt): | |
29a41103 | 1414 | if evt.GetId() in [wx.ID_CUT, wx.ID_COPY, self.ID_DELETE]: |
d14a1e28 | 1415 | evt.Enable(tree.selection is not None and tree.selection != tree.root) |
29a41103 | 1416 | elif evt.GetId() == wx.ID_SAVE: |
6cb85701 | 1417 | evt.Enable(self.modified) |
29a41103 | 1418 | elif evt.GetId() in [wx.ID_PASTE, self.ID_TOOL_PASTE]: |
af52e185 | 1419 | evt.Enable(tree.selection is not None) |
690a4c0a RR |
1420 | elif evt.GetId() in [self.ID_TEST, |
1421 | self.ID_MOVEUP, self.ID_MOVEDOWN, | |
1422 | self.ID_MOVELEFT, self.ID_MOVERIGHT]: | |
d14a1e28 | 1423 | evt.Enable(tree.selection is not None and tree.selection != tree.root) |
690a4c0a RR |
1424 | elif evt.GetId() in [self.ID_LOCATE, self.ID_TOOL_LOCATE, |
1425 | self.ID_REFRESH]: | |
64bce500 | 1426 | evt.Enable(g.testWin is not None) |
29a41103 RD |
1427 | elif evt.GetId() == wx.ID_UNDO: evt.Enable(undoMan.CanUndo()) |
1428 | elif evt.GetId() == wx.ID_REDO: evt.Enable(undoMan.CanRedo()) | |
d14a1e28 RD |
1429 | |
1430 | def OnIdle(self, evt): | |
1431 | if self.inIdle: return # Recursive call protection | |
1432 | self.inIdle = True | |
fa64a0c3 | 1433 | #print 'onidle',tree.needUpdate,tree.pendingHighLight |
306b6fe9 RR |
1434 | try: |
1435 | if tree.needUpdate: | |
1436 | if conf.autoRefresh: | |
1437 | if g.testWin: | |
da7d0696 | 1438 | #self.SetStatusText('Refreshing test window...') |
306b6fe9 RR |
1439 | # (re)create |
1440 | tree.CreateTestWin(g.testWin.item) | |
da7d0696 | 1441 | #self.SetStatusText('') |
306b6fe9 RR |
1442 | tree.needUpdate = False |
1443 | elif tree.pendingHighLight: | |
1444 | try: | |
1445 | tree.HighLight(tree.pendingHighLight) | |
1446 | except: | |
1447 | # Remove highlight if any problem | |
09a68e88 | 1448 | if g.testWin and g.testWin.highLight: |
306b6fe9 RR |
1449 | g.testWin.highLight.Remove() |
1450 | tree.pendingHighLight = None | |
1451 | raise | |
1452 | else: | |
1453 | evt.Skip() | |
1454 | finally: | |
1455 | self.inIdle = False | |
d14a1e28 | 1456 | |
4483a6ed | 1457 | def OnIconize(self, evt): |
7df24e78 RR |
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() | |
2702a0c1 | 1466 | self.miniFrame.Show(False) |
4483a6ed | 1467 | else: |
7df24e78 | 1468 | if not conf.embedPanel: |
2702a0c1 | 1469 | self.miniFrame.Show(True) |
4483a6ed RR |
1470 | evt.Skip() |
1471 | ||
d14a1e28 RD |
1472 | def OnCloseWindow(self, evt): |
1473 | if not self.AskSave(): return | |
1474 | if g.testWin: g.testWin.Destroy() | |
d14a1e28 RD |
1475 | if not panel.GetPageCount() == 2: |
1476 | panel.page2.Destroy() | |
80389ff7 RR |
1477 | else: |
1478 | # If we don't do this, page does not get destroyed (a bug?) | |
1479 | panel.RemovePage(1) | |
3429f8d0 RD |
1480 | if not self.IsIconized(): |
1481 | conf.x, conf.y = self.GetPosition() | |
2702a0c1 RR |
1482 | if wx.Platform == '__WXMAC__': |
1483 | conf.width, conf.height = self.GetClientSize() | |
1484 | else: | |
1485 | conf.width, conf.height = self.GetSize() | |
3429f8d0 RD |
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() | |
d14a1e28 RD |
1491 | evt.Skip() |
1492 | ||
73b2a9a7 RD |
1493 | def CreateLocalConf(self, path): |
1494 | name = os.path.splitext(path)[0] | |
1495 | name += '.xcfg' | |
1496 | return wx.FileConfig(localFilename=name) | |
1497 | ||
d14a1e28 RD |
1498 | def Clear(self): |
1499 | self.dataFile = '' | |
73b2a9a7 | 1500 | conf.localconf = None |
d14a1e28 | 1501 | undoMan.Clear() |
6cb85701 | 1502 | self.SetModified(False) |
d14a1e28 RD |
1503 | tree.Clear() |
1504 | panel.Clear() | |
1505 | if g.testWin: | |
1506 | g.testWin.Destroy() | |
1507 | g.testWin = None | |
d14a1e28 RD |
1508 | # Numbers for new controls |
1509 | self.maxIDs = {} | |
306b6fe9 RR |
1510 | for cl in [xxxPanel, xxxDialog, xxxFrame, |
1511 | xxxMenuBar, xxxMenu, xxxToolBar, | |
1512 | xxxWizard, xxxBitmap, xxxIcon]: | |
1513 | self.maxIDs[cl] = 0 | |
8c64c153 RR |
1514 | # Restore handlers, menu, etc. to initial |
1515 | setHandlers(self.handlers[:]) | |
1516 | g.pullDownMenu.custom = self.custom[:] | |
1517 | # Remove modules imported from comment directives | |
aea9f6d5 | 1518 | map(sys.modules.pop, [m for m in sys.modules if m not in self.modules]) |
7058b794 RR |
1519 | xxxParamComment.locals = {} # clear local namespace |
1520 | xxxParamComment.allow = None # clear execution state | |
d14a1e28 | 1521 | |
6cb85701 RR |
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 | ||
d14a1e28 RD |
1531 | def Open(self, path): |
1532 | if not os.path.exists(path): | |
29a41103 | 1533 | wx.LogError('File does not exists: %s' % path) |
d14a1e28 RD |
1534 | return False |
1535 | # Try to read the file | |
1536 | try: | |
1537 | f = open(path) | |
1538 | self.Clear() | |
d14a1e28 | 1539 | dom = minidom.parse(f) |
d14a1e28 | 1540 | f.close() |
016f67ba RR |
1541 | # Set encoding global variable and default encoding |
1542 | if dom.encoding: | |
1543 | g.currentEncoding = dom.encoding | |
1544 | wx.SetDefaultPyEncoding(g.currentEncoding.encode()) | |
9a69d0aa RR |
1545 | else: |
1546 | g.currentEncoding = '' | |
d14a1e28 | 1547 | # Change dir |
fd919451 | 1548 | self.dataFile = path = os.path.abspath(path) |
d14a1e28 RD |
1549 | dir = os.path.dirname(path) |
1550 | if dir: os.chdir(dir) | |
c0d5ae74 RR |
1551 | # Allow importing modules from the same directory |
1552 | sys.path = sys_path + [dir] | |
d14a1e28 | 1553 | tree.SetData(dom) |
d14a1e28 | 1554 | self.SetTitle(progname + ': ' + os.path.basename(path)) |
73b2a9a7 | 1555 | conf.localconf = self.CreateLocalConf(self.dataFile) |
d14a1e28 RD |
1556 | except: |
1557 | # Nice exception printing | |
1558 | inf = sys.exc_info() | |
29a41103 RD |
1559 | wx.LogError(traceback.format_exception(inf[0], inf[1], None)[-1]) |
1560 | wx.LogError('Error reading file: %s' % path) | |
016f67ba | 1561 | if debug: raise |
d14a1e28 RD |
1562 | return False |
1563 | return True | |
1564 | ||
1565 | def Indent(self, node, indent = 0): | |
b372319f RR |
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 | |
d14a1e28 RD |
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: | |
b372319f RR |
1583 | if n.nodeType == minidom.Node.ELEMENT_NODE or \ |
1584 | n.nodeType == minidom.Node.COMMENT_NODE: | |
d14a1e28 RD |
1585 | self.Indent(n, indent + 2) |
1586 | ||
1587 | def Save(self, path): | |
1588 | try: | |
7353d818 | 1589 | import codecs |
d14a1e28 RD |
1590 | # Apply changes |
1591 | if tree.selection and panel.IsModified(): | |
29a41103 | 1592 | self.OnRefresh(wx.CommandEvent()) |
016f67ba | 1593 | if g.currentEncoding: |
9a69d0aa | 1594 | f = codecs.open(path, 'wt', g.currentEncoding) |
016f67ba | 1595 | else: |
9a69d0aa | 1596 | f = codecs.open(path, 'wt') |
d14a1e28 RD |
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)) | |
34b29ae7 RR |
1602 | # Remove first child (test element) |
1603 | testElem = mainNode.firstChild | |
1604 | mainNode.removeChild(testElem) | |
1605 | testElem.unlink() | |
d14a1e28 | 1606 | self.Indent(mainNode) |
7353d818 | 1607 | self.domCopy.writexml(f, encoding = g.currentEncoding) |
d14a1e28 RD |
1608 | f.close() |
1609 | self.domCopy.unlink() | |
1610 | self.domCopy = None | |
6cb85701 | 1611 | self.SetModified(False) |
d14a1e28 | 1612 | panel.SetModified(False) |
73b2a9a7 | 1613 | conf.localconf.Flush() |
d14a1e28 | 1614 | except: |
4eb5bfc6 | 1615 | inf = sys.exc_info() |
29a41103 RD |
1616 | wx.LogError(traceback.format_exception(inf[0], inf[1], None)[-1]) |
1617 | wx.LogError('Error writing file: %s' % path) | |
d14a1e28 | 1618 | raise |
73b2a9a7 | 1619 | |
d14a1e28 RD |
1620 | def AskSave(self): |
1621 | if not (self.modified or panel.IsModified()): return True | |
29a41103 RD |
1622 | flags = wx.ICON_EXCLAMATION | wx.YES_NO | wx.CANCEL | wx.CENTRE |
1623 | dlg = wx.MessageDialog( self, 'File is modified. Save before exit?', | |
d14a1e28 RD |
1624 | 'Save before too late?', flags ) |
1625 | say = dlg.ShowModal() | |
1626 | dlg.Destroy() | |
29a41103 RD |
1627 | wx.Yield() |
1628 | if say == wx.ID_YES: | |
1629 | self.OnSaveOrSaveAs(wx.CommandEvent(wx.ID_SAVE)) | |
d14a1e28 RD |
1630 | # If save was successful, modified flag is unset |
1631 | if not self.modified: return True | |
29a41103 | 1632 | elif say == wx.ID_NO: |
6cb85701 | 1633 | self.SetModified(False) |
d14a1e28 RD |
1634 | panel.SetModified(False) |
1635 | return True | |
1636 | return False | |
1637 | ||
d14a1e28 RD |
1638 | ################################################################################ |
1639 | ||
73b2a9a7 RD |
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 | ||
29a41103 RD |
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") | |
73b2a9a7 RD |
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: | |
93894584 | 1666 | name = os.path.splitext(os.path.split(dataFile)[1])[0] |
73b2a9a7 RD |
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] | |
29a41103 RD |
1679 | dlg = wx.FileDialog(self, 'Save As', dirname, name, '*.py', |
1680 | wx.SAVE | wx.OVERWRITE_PROMPT) | |
1681 | if dlg.ShowModal() == wx.ID_OK: | |
73b2a9a7 RD |
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() | |
7e05216c RD |
1690 | genGettext = self.GettextCB.GetValue() |
1691 | frame.GeneratePython(self.dataFile, pypath, embed, genGettext) | |
73b2a9a7 RD |
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 | ||
5050b626 RR |
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 | ||
cbfc9df6 RD |
1714 | ##xxx = sys.modules['xxx'] |
1715 | import xxx | |
5050b626 RR |
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 | ||
7058b794 RR |
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 | ||
5050b626 RR |
1762 | def OnCheck(self, evt): |
1763 | self.checkControls[evt.GetId()][0].Enable(evt.IsChecked()) | |
1764 | evt.Skip() | |
1765 | ||
73b2a9a7 RD |
1766 | ################################################################################ |
1767 | ||
c1dda21b RR |
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 | ||
d14a1e28 RD |
1780 | def usage(): |
1781 | print >> sys.stderr, 'usage: xrced [-dhiv] [file]' | |
1782 | ||
29a41103 | 1783 | class App(wx.App): |
d14a1e28 | 1784 | def OnInit(self): |
306b6fe9 | 1785 | # Check version |
29a41103 RD |
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) | |
d14a1e28 RD |
1790 | global debug |
1791 | # Process comand-line | |
364a2be0 | 1792 | opts = args = None |
d14a1e28 RD |
1793 | try: |
1794 | opts, args = getopt.getopt(sys.argv[1:], 'dhiv') | |
1c01bc8e RD |
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 | ||
d14a1e28 | 1805 | except getopt.GetoptError: |
29a41103 | 1806 | if wx.Platform != '__WXMAC__': # macs have some extra parameters |
d14a1e28 RD |
1807 | print >> sys.stderr, 'Unknown option' |
1808 | usage() | |
1809 | sys.exit(1) | |
d14a1e28 RD |
1810 | |
1811 | self.SetAppName('xrced') | |
1812 | # Settings | |
1813 | global conf | |
29a41103 | 1814 | conf = g.conf = wx.Config(style = wx.CONFIG_USE_LOCAL_FILE) |
73b2a9a7 | 1815 | conf.localconf = None |
d14a1e28 RD |
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) | |
52cd0643 | 1822 | |
80389ff7 | 1823 | # read recently used files |
52cd0643 RD |
1824 | g.fileHistory = wx.FileHistory() |
1825 | g.fileHistory.Load(conf) | |
1826 | ||
d14a1e28 RD |
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') | |
c1dda21b | 1835 | # Preferences |
7058b794 | 1836 | conf.allowExec = conf.Read('Prefs/allowExec', 'ask') |
c1dda21b | 1837 | p = 'Prefs/sizeritem_defaults_panel' |
cbfc9df6 | 1838 | import xxx |
c1dda21b | 1839 | if conf.HasEntry(p): |
cbfc9df6 RD |
1840 | ##sys.modules['xxx'].xxxSizerItem.defaults_panel = ReadDictFromString(conf.Read(p)) |
1841 | xxx.xxxSizerItem.defaults_panel = ReadDictFromString(conf.Read(p)) | |
c1dda21b RR |
1842 | p = 'Prefs/sizeritem_defaults_control' |
1843 | if conf.HasEntry(p): | |
cbfc9df6 RD |
1844 | ##sys.modules['xxx'].xxxSizerItem.defaults_control = ReadDictFromString(conf.Read(p)) |
1845 | xxx.xxxSizerItem.defaults_control = ReadDictFromString(conf.Read(p)) | |
1846 | ||
d14a1e28 | 1847 | # Add handlers |
29a41103 | 1848 | wx.FileSystem.AddHandler(wx.MemoryFSHandler()) |
8ecb322e RR |
1849 | self.toolArtProvider = ToolArtProvider() |
1850 | wx.ArtProvider.Push(self.toolArtProvider) | |
d14a1e28 RD |
1851 | # Create main frame |
1852 | frame = Frame(pos, size) | |
2702a0c1 RR |
1853 | # Mac does not set the correct size |
1854 | if wx.Platform == '__WXMAC__': | |
1855 | frame.SetClientSize(size) | |
d14a1e28 | 1856 | frame.Show(True) |
8c64c153 RR |
1857 | |
1858 | # Load plugins | |
1859 | plugins = os.getenv('XRCEDPATH') | |
1860 | if plugins: | |
1861 | cwd = os.getcwd() | |
1862 | try: | |
1863 | for dir in plugins.split(':'): | |
a023b456 RR |
1864 | if os.path.isdir(dir) and \ |
1865 | os.path.isfile(os.path.join(dir, '__init__.py')): | |
8c64c153 RR |
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[:] | |
da7d0696 | 1879 | frame.modules = sys.modules.copy() |
8c64c153 RR |
1880 | |
1881 | # Initialize | |
1882 | frame.Clear() | |
d14a1e28 RD |
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 | |
73b2a9a7 | 1894 | wc = conf |
d14a1e28 RD |
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) | |
7058b794 | 1908 | wc.WriteInt('nopanic', 1) |
52cd0643 | 1909 | g.fileHistory.Save(wc) |
c1dda21b | 1910 | # Preferences |
5050b626 | 1911 | wc.DeleteGroup('Prefs') |
7058b794 | 1912 | wc.Write('Prefs/allowExec', conf.allowExec) |
cbfc9df6 RD |
1913 | import xxx |
1914 | ##v = sys.modules['xxx'].xxxSizerItem.defaults_panel | |
1915 | v = xxx.xxxSizerItem.defaults_panel | |
c1dda21b | 1916 | if v: wc.Write('Prefs/sizeritem_defaults_panel', DictToString(v)) |
cbfc9df6 RD |
1917 | ###v = sys.modules['xxx'].xxxSizerItem.defaults_control |
1918 | v = xxx.xxxSizerItem.defaults_control | |
c1dda21b | 1919 | if v: wc.Write('Prefs/sizeritem_defaults_control', DictToString(v)) |
7058b794 | 1920 | |
d14a1e28 RD |
1921 | wc.Flush() |
1922 | ||
1923 | def main(): | |
1924 | app = App(0, useBestVisual=False) | |
29a41103 | 1925 | #app.SetAssertMode(wx.PYAPP_ASSERT_LOG) |
d14a1e28 RD |
1926 | app.MainLoop() |
1927 | app.OnExit() | |
1928 | global conf | |
1929 | del conf | |
1930 | ||
1931 | if __name__ == '__main__': | |
1932 | main() |