]>
Commit | Line | Data |
---|---|---|
09f3d4e6 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 | |
a40b5e84 | 5 | # RCS-ID: $Id$ |
09f3d4e6 RD |
6 | |
7 | from wxPython.wx import * | |
8 | from wxPython.xrc import * | |
c1beeb57 | 9 | from wxPython.html import wxHtmlWindow |
09f3d4e6 RD |
10 | from xml.dom import minidom |
11 | import os | |
f0c20d94 | 12 | import getopt |
09f3d4e6 | 13 | |
f0c20d94 | 14 | # Icons |
09f3d4e6 RD |
15 | import images |
16 | ||
f0c20d94 | 17 | # Constants |
a40b5e84 | 18 | |
18fc9fa3 RD |
19 | # Return code from wxGetOsVersion |
20 | wxGTK = 9 | |
21 | ||
22 | if wxGetOsVersion()[0] == wxGTK: | |
f0c20d94 | 23 | labelFont = wxFont(12, wxDEFAULT, wxNORMAL, wxBOLD) |
18fc9fa3 | 24 | modernFont = wxFont(12, wxMODERN, wxNORMAL, wxNORMAL) |
32ce09e2 | 25 | else: |
f0c20d94 | 26 | labelFont = wxFont(10, wxDEFAULT, wxNORMAL, wxBOLD) |
18fc9fa3 | 27 | modernFont = wxFont(10, wxMODERN, wxNORMAL, wxNORMAL) |
f0c20d94 | 28 | |
09f3d4e6 | 29 | progname = 'XRCed' |
c1beeb57 | 30 | version = '0.0.7-3' |
09f3d4e6 RD |
31 | |
32 | # Local modules | |
33 | from xxx import * | |
34 | ||
35 | # Globals | |
36 | testWin = None | |
37 | testWinPos = wxDefaultPosition | |
38 | ||
39 | # 1 adds CMD command to Help menu | |
f0c20d94 RD |
40 | debug = 0 |
41 | ||
42 | helpText = """\ | |
43 | <HTML><H2>Welcome to XRCed!</H2><H3><font color="green">DON'T PANIC :)</font></H3> | |
44 | To start select tree root, then popup menu with your right mouse button, | |
45 | select "Append Child", and then any command.<P> | |
46 | Enter XML ID, change properties, create children.<P> | |
47 | To test your interface select Test command (View menu).<P> | |
48 | Consult README file for the details.</HTML> | |
49 | """ | |
09f3d4e6 | 50 | |
f0c20d94 RD |
51 | defaultIDs = {xxxPanel:'PANEL', xxxDialog:'DIALOG', xxxFrame:'FRAME', |
52 | xxxMenuBar:'MENUBAR', xxxMenu:'MENU', xxxToolBar:'TOOLBAR'} | |
09f3d4e6 RD |
53 | |
54 | # Set menu to list items. | |
55 | # Each menu command is a tuple (id, label, help) | |
56 | # submenus are lists [id, label, help, submenu] | |
57 | # and separators are any other type | |
58 | def SetMenu(m, list): | |
59 | for l in list: | |
60 | if type(l) == types.TupleType: | |
61 | apply(m.Append, l) | |
62 | elif type(l) == types.ListType: | |
63 | subMenu = wxMenu() | |
64 | SetMenu(subMenu, l[2:]) | |
65 | m.AppendMenu(wxNewId(), l[0], subMenu, l[1]) | |
66 | else: # separator | |
67 | m.AppendSeparator() | |
68 | ||
a40b5e84 RD |
69 | ################################################################################ |
70 | ||
71 | # Properties panel containing notebook | |
72 | class Panel(wxNotebook): | |
73 | def __init__(self, parent, id = -1): | |
74 | wxNotebook.__init__(self, parent, id, style=wxNB_BOTTOM) | |
a40b5e84 | 75 | sys.modules['params'].panel = self |
f0c20d94 RD |
76 | # List of child windows |
77 | self.pages = [] | |
78 | # Create scrolled windows for pages | |
79 | self.page1 = wxScrolledWindow(self, -1) | |
80 | sizer = wxBoxSizer() | |
81 | sizer.Add(wxBoxSizer()) # dummy sizer | |
82 | self.page1.SetAutoLayout(true) | |
83 | self.page1.SetSizer(sizer) | |
a40b5e84 | 84 | self.AddPage(self.page1, 'Properties') |
f0c20d94 RD |
85 | # Second page |
86 | self.page2 = wxScrolledWindow(self, -1) | |
87 | sizer = wxBoxSizer() | |
88 | sizer.Add(wxBoxSizer()) # dummy sizer | |
89 | self.page2.SetAutoLayout(true) | |
90 | self.page2.SetSizer(sizer) | |
32ce09e2 | 91 | # Cache for already used panels |
f0c20d94 RD |
92 | self.pageCache = {} # cached property panels |
93 | self.stylePageCache = {} # cached style panels | |
94 | # Dummy parent window for cache pages | |
95 | self.cacheParent = wxFrame(None, -1, 'non visible') | |
96 | # Delete child windows and recreate page sizer | |
97 | def ResetPage(self, page): | |
98 | topSizer = page.GetSizer() | |
99 | sizer = topSizer.GetChildren()[0].GetSizer() | |
100 | for w in page.GetChildren(): | |
101 | sizer.RemoveWindow(w) | |
102 | if isinstance(w, ParamPage): | |
103 | # With SetParent, we wouldn't need this | |
104 | w.Reparent(self.cacheParent) | |
105 | else: | |
106 | w.Destroy() | |
107 | topSizer.RemoveSizer(sizer) | |
108 | # Create new windows | |
109 | sizer = wxBoxSizer(wxVERTICAL) | |
110 | # Special case - resize html window | |
111 | if conf.panic: | |
112 | topSizer.Add(sizer, 1, wxEXPAND) | |
113 | else: | |
114 | topSizer.Add(sizer, 0, wxALL, 5) | |
115 | return sizer | |
a40b5e84 | 116 | def SetData(self, xxx): |
f0c20d94 | 117 | self.pages = [] |
32ce09e2 | 118 | # First page |
f0c20d94 RD |
119 | # Set cached or new page |
120 | # Remove current objects and sizer | |
121 | sizer = self.ResetPage(self.page1) | |
122 | if not xxx or (not xxx.allParams and not xxx.hasName): | |
123 | if tree.selection: | |
124 | sizer.Add(wxStaticText(self.page1, -1, 'This item has no properties.')) | |
125 | else: # nothing selected | |
126 | # If first time, show some help | |
127 | if conf.panic: | |
128 | html = wxHtmlWindow(self.page1, -1, wxDefaultPosition, | |
129 | wxDefaultSize, wxSUNKEN_BORDER) | |
130 | html.SetPage(helpText) | |
131 | sizer.Add(html, 1, wxEXPAND) | |
132 | conf.panic = false | |
133 | else: | |
134 | sizer.Add(wxStaticText(self.page1, -1, 'Select a tree item.')) | |
135 | else: | |
136 | SetCurrentXXX(xxx.treeObject()) | |
137 | try: | |
138 | page = self.pageCache[xxx.__class__] | |
139 | page.Reparent(self.page1) | |
140 | except KeyError: | |
141 | page = PropPage(self.page1, xxx.className, xxx) | |
142 | self.pageCache[xxx.__class__] = page | |
143 | page.SetValues(xxx) | |
144 | self.pages.append(page) | |
145 | sizer.Add(page, 1, wxEXPAND) | |
146 | if xxx.hasChild: | |
147 | # Special label for child objects - they may have different GUI | |
148 | cacheID = (xxx.child.__class__, xxx.__class__) | |
149 | try: | |
150 | page = self.pageCache[cacheID] | |
151 | page.Reparent(self.page1) | |
152 | except KeyError: | |
153 | page = PropPage(self.page1, xxx.child.className, xxx.child) | |
154 | self.pageCache[cacheID] = page | |
155 | page.SetValues(xxx.child) | |
156 | self.pages.append(page) | |
157 | sizer.Add(page, 0, wxEXPAND | wxTOP, 5) | |
158 | self.page1.Layout() | |
159 | size = self.page1.GetSizer().GetMinSize() | |
160 | self.page1.SetScrollbars(1, 1, size.x, size.y, 0, 0, true) | |
161 | ||
162 | # Second page | |
163 | # Create if does not exist | |
a40b5e84 | 164 | if xxx and xxx.treeObject().hasStyle: |
f0c20d94 RD |
165 | xxx = xxx.treeObject() |
166 | # Simplest case: set data if class is the same | |
167 | sizer = self.ResetPage(self.page2) | |
168 | try: | |
169 | page = self.stylePageCache[xxx.__class__] | |
170 | page.Reparent(self.page2) | |
171 | except KeyError: | |
172 | page = StylePage(self.page2, xxx.className + ' style', xxx) | |
173 | self.stylePageCache[xxx.__class__] = page | |
174 | page.SetValues(xxx) | |
175 | self.pages.append(page) | |
176 | sizer.Add(page, 0, wxEXPAND) | |
177 | # Add page if not exists | |
178 | if not self.GetPageCount() == 2: | |
179 | self.AddPage(self.page2, 'Style') | |
180 | self.page2.Layout() | |
181 | size = self.page2.GetSizer().GetMinSize() | |
182 | self.page2.SetScrollbars(1, 1, size.x, size.y, 0, 0, true) | |
a40b5e84 | 183 | else: |
f0c20d94 RD |
184 | # Remove page if exists |
185 | if self.GetPageCount() == 2: | |
c1beeb57 RD |
186 | self.SetSelection(0) |
187 | self.page1.Refresh() | |
f0c20d94 | 188 | self.RemovePage(1) |
09f3d4e6 | 189 | def Clear(self): |
f0c20d94 RD |
190 | self.SetData(None) |
191 | # Check if some parameter on some page has changed | |
a40b5e84 | 192 | def IsModified(self): |
f0c20d94 RD |
193 | for p in self.pages: |
194 | if p.IsModified(): return true | |
195 | return false | |
196 | # Reset changed state | |
a40b5e84 | 197 | def SetModified(self, value): |
f0c20d94 RD |
198 | for p in self.pages: p.SetModified(value) |
199 | def Apply(self): | |
200 | for p in self.pages: p.Apply() | |
a40b5e84 RD |
201 | |
202 | ################################################################################ | |
203 | ||
204 | # General class for notebook pages | |
f0c20d94 RD |
205 | class ParamPage(wxPanel): |
206 | def __init__(self, parent, xxx): | |
207 | wxPanel.__init__(self, parent, -1) | |
208 | self.xxx = xxx | |
a40b5e84 RD |
209 | # Register event handlers |
210 | for id in paramIDs.values(): | |
211 | EVT_CHECKBOX(self, id, self.OnCheckParams) | |
f0c20d94 RD |
212 | self.modified = false |
213 | self.checks = {} | |
214 | self.controls = {} # save python objects | |
215 | self.controlName = None | |
09f3d4e6 | 216 | def OnCheckParams(self, evt): |
f0c20d94 RD |
217 | xxx = self.xxx |
218 | param = evt.GetEventObject().GetName() | |
219 | w = self.controls[param] | |
32ce09e2 | 220 | objElem = xxx.element |
09f3d4e6 RD |
221 | if evt.IsChecked(): |
222 | # Ad new text node in order of allParams | |
a40b5e84 | 223 | w.SetValue('') # set empty (default) value |
32ce09e2 RD |
224 | w.SetModified() # mark as changed |
225 | elem = tree.dom.createElement(param) | |
18fc9fa3 RD |
226 | # Some classes are special |
227 | if param == 'font': | |
228 | xxx.params[param] = xxxParamFont(xxx.element, elem) | |
229 | else: | |
230 | xxx.params[param] = xxxParam(elem) | |
09f3d4e6 RD |
231 | # Find place to put new element: first present element after param |
232 | found = false | |
a40b5e84 RD |
233 | paramStyles = xxx.allParams + xxx.styles |
234 | for p in paramStyles[paramStyles.index(param) + 1:]: | |
09f3d4e6 RD |
235 | # Content params don't have same type |
236 | if xxx.params.has_key(p) and p != 'content': | |
237 | found = true | |
238 | break | |
239 | if found: | |
32ce09e2 RD |
240 | nextTextElem = xxx.params[p].node |
241 | objElem.insertBefore(elem, nextTextElem) | |
09f3d4e6 | 242 | else: |
32ce09e2 | 243 | objElem.appendChild(elem) |
09f3d4e6 | 244 | else: |
e973de2e | 245 | # Remove parameter |
32ce09e2 | 246 | xxx.params[param].remove() |
09f3d4e6 RD |
247 | del xxx.params[param] |
248 | w.SetValue('') | |
f0c20d94 | 249 | w.modified = false # mark as not changed |
32ce09e2 RD |
250 | # Set modified flas |
251 | self.SetModified(true) | |
09f3d4e6 | 252 | w.Enable(evt.IsChecked()) |
f0c20d94 RD |
253 | # If some parameter has changed |
254 | def IsModified(self): | |
255 | return self.modified | |
256 | def SetModified(self, value): | |
257 | self.modified = value | |
258 | def Apply(self): | |
259 | xxx = self.xxx | |
260 | # !!! Save undo info | |
261 | # if xxx.undo: xxx.undo.unlink() | |
262 | # xxx.undo = xxx.element.cloneNode(false) | |
263 | if self.controlName: | |
264 | name = self.controlName.GetValue() | |
265 | if xxx.name != name: | |
266 | xxx.name = name | |
267 | xxx.element.setAttribute('name', name) | |
268 | for param, w in self.controls.items(): | |
269 | if w.modified: | |
270 | paramObj = xxx.params[param] | |
271 | value = w.GetValue() | |
272 | if param in xxx.specials: | |
273 | xxx.setSpecial(param, value) | |
274 | else: | |
275 | paramObj.update(value) | |
a40b5e84 RD |
276 | |
277 | ################################################################################ | |
278 | ||
f0c20d94 RD |
279 | # Panel for displaying properties |
280 | class PropPage(ParamPage): | |
281 | def __init__(self, parent, label, xxx): | |
282 | ParamPage.__init__(self, parent, xxx) | |
283 | box = wxStaticBox(self, -1, label) | |
284 | box.SetFont(labelFont) | |
285 | topSizer = wxStaticBoxSizer(box, wxVERTICAL) | |
286 | sizer = wxFlexGridSizer(len(xxx.allParams), 2, 1, 1) | |
287 | if xxx.hasName: | |
18fc9fa3 | 288 | label = wxStaticText(self, -1, 'XML ID:', size=(100,-1)) |
f0c20d94 RD |
289 | control = ParamText(self, name='XML_name') |
290 | sizer.AddMany([ (label, 0, wxALIGN_CENTER_VERTICAL), | |
291 | (control, 0, wxALIGN_CENTER_VERTICAL) ]) | |
292 | self.controlName = control | |
293 | for param in xxx.allParams: | |
294 | present = param in xxx.params | |
295 | if param in xxx.required: | |
296 | label = wxStaticText(self, paramIDs[param], param + ':', | |
18fc9fa3 | 297 | size = (100,-1), name = param) |
f0c20d94 RD |
298 | else: |
299 | # Notebook has one very loooooong parameter | |
300 | if param == 'usenotebooksizer': sParam = 'usesizer:' | |
301 | else: sParam = param + ':' | |
302 | label = wxCheckBox(self, paramIDs[param], sParam, | |
18fc9fa3 | 303 | size = (100,-1), name = param) |
f0c20d94 RD |
304 | self.checks[param] = label |
305 | try: | |
306 | typeClass = xxx.paramDict[param] | |
307 | except KeyError: | |
308 | try: | |
309 | # Standart type | |
310 | typeClass = paramDict[param] | |
311 | except KeyError: | |
312 | # Default | |
313 | typeClass = ParamText | |
314 | control = typeClass(self, param) | |
315 | control.Enable(present) | |
316 | sizer.AddMany([ (label, 0, wxALIGN_CENTER_VERTICAL), | |
317 | (control, 0, wxALIGN_CENTER_VERTICAL) ]) | |
318 | self.controls[param] = control | |
18fc9fa3 | 319 | topSizer.Add(sizer, 1, wxALL | wxEXPAND, 3) |
f0c20d94 RD |
320 | self.SetAutoLayout(true) |
321 | self.SetSizer(topSizer) | |
322 | topSizer.Fit(self) | |
32ce09e2 | 323 | def SetValues(self, xxx): |
f0c20d94 | 324 | self.xxx = xxx |
a40b5e84 | 325 | # Set values, checkboxes to false, disable defaults |
32ce09e2 | 326 | if xxx.hasName: |
f0c20d94 | 327 | self.controlName.SetValue(xxx.name) |
a40b5e84 | 328 | for param in xxx.allParams: |
f0c20d94 RD |
329 | w = self.controls[param] |
330 | w.modified = false | |
32ce09e2 RD |
331 | try: |
332 | value = xxx.params[param].value() | |
f0c20d94 RD |
333 | w.Enable(true) |
334 | w.SetValue(value) | |
a40b5e84 | 335 | if not param in xxx.required: |
f0c20d94 | 336 | self.checks[param].SetValue(true) |
32ce09e2 | 337 | except KeyError: |
f0c20d94 RD |
338 | self.checks[param].SetValue(false) |
339 | w.SetValue('') | |
340 | w.Enable(false) | |
341 | self.SetModified(false) | |
09f3d4e6 | 342 | |
a40b5e84 RD |
343 | ################################################################################ |
344 | ||
345 | # Style notebook page | |
f0c20d94 RD |
346 | class StylePage(ParamPage): |
347 | def __init__(self, parent, label, xxx): | |
348 | ParamPage.__init__(self, parent, xxx) | |
349 | box = wxStaticBox(self, -1, label) | |
350 | box.SetFont(labelFont) | |
351 | topSizer = wxStaticBoxSizer(box, wxVERTICAL) | |
32ce09e2 | 352 | sizer = wxFlexGridSizer(len(xxx.styles), 2, 1, 1) |
a40b5e84 RD |
353 | for param in xxx.styles: |
354 | present = param in xxx.params.keys() | |
355 | check = wxCheckBox(self, paramIDs[param], | |
18fc9fa3 | 356 | param + ':', size = (100,-1), name = param) |
a40b5e84 | 357 | check.SetValue(present) |
f0c20d94 | 358 | control = paramDict[param](self, name = param) |
a40b5e84 | 359 | control.Enable(present) |
32ce09e2 RD |
360 | sizer.AddMany([ (check, 0, wxALIGN_CENTER_VERTICAL), |
361 | (control, 0, wxALIGN_CENTER_VERTICAL) ]) | |
f0c20d94 | 362 | self.checks[param] = check |
a40b5e84 | 363 | self.controls[param] = control |
18fc9fa3 | 364 | topSizer.Add(sizer, 1, wxALL | wxEXPAND, 3) |
a40b5e84 RD |
365 | self.SetAutoLayout(true) |
366 | self.SetSizer(topSizer) | |
f0c20d94 | 367 | topSizer.Fit(self) |
32ce09e2 | 368 | # Set data for a cahced page |
f0c20d94 RD |
369 | def SetValues(self, xxx): |
370 | self.xxx = xxx | |
32ce09e2 RD |
371 | for param in xxx.styles: |
372 | present = param in xxx.params.keys() | |
f0c20d94 | 373 | check = self.checks[param] |
32ce09e2 | 374 | check.SetValue(present) |
c1beeb57 RD |
375 | w = self.controls[param] |
376 | w.modified = false | |
32ce09e2 | 377 | if present: |
c1beeb57 | 378 | w.SetValue(xxx.params[param].value()) |
32ce09e2 | 379 | else: |
c1beeb57 RD |
380 | w.SetValue('') |
381 | w.Enable(present) | |
f0c20d94 | 382 | self.SetModified(false) |
a40b5e84 RD |
383 | |
384 | ################################################################################ | |
385 | ||
09f3d4e6 RD |
386 | class HightLightBox: |
387 | def __init__(self, pos, size): | |
388 | w = testWin.panel | |
389 | l1 = wxWindow(w, -1, pos, wxSize(size.x, 2)) | |
390 | l1.SetBackgroundColour(wxRED) | |
391 | l2 = wxWindow(w, -1, pos, wxSize(2, size.y)) | |
392 | l2.SetBackgroundColour(wxRED) | |
393 | l3 = wxWindow(w, -1, wxPoint(pos.x + size.x - 2, pos.y), wxSize(2, size.y)) | |
394 | l3.SetBackgroundColour(wxRED) | |
395 | l4 = wxWindow(w, -1, wxPoint(pos.x, pos.y + size.y - 2), wxSize(size.x, 2)) | |
396 | l4.SetBackgroundColour(wxRED) | |
397 | self.lines = [l1, l2, l3, l4] | |
398 | # Move highlight to a new position | |
399 | def Replace(self, pos, size): | |
400 | self.lines[0].SetDimensions(pos.x, pos.y, size.x, 2, wxSIZE_ALLOW_MINUS_ONE) | |
401 | self.lines[1].SetDimensions(pos.x, pos.y, 2, size.y, wxSIZE_ALLOW_MINUS_ONE) | |
402 | self.lines[2].SetDimensions(pos.x + size.x - 2, pos.y, 2, size.y, | |
403 | wxSIZE_ALLOW_MINUS_ONE) | |
404 | self.lines[3].SetDimensions(pos.x, pos.y + size.y - 2, size.x, 2, | |
405 | wxSIZE_ALLOW_MINUS_ONE) | |
406 | # Remove it | |
407 | def Remove(self): | |
408 | map(wxWindow.Destroy, self.lines) | |
409 | testWin.highLight = None | |
410 | ||
a40b5e84 RD |
411 | ################################################################################ |
412 | ||
09f3d4e6 RD |
413 | class MemoryFile: |
414 | def __init__(self, name): | |
415 | self.name = name | |
416 | self.buffer = '' | |
417 | def write(self, data): | |
f0c20d94 | 418 | self.buffer += data.encode() |
09f3d4e6 | 419 | def close(self): |
f0c20d94 | 420 | wxMemoryFSHandler_AddFile(self.name, self.buffer) |
09f3d4e6 RD |
421 | |
422 | class XML_Tree(wxTreeCtrl): | |
423 | def __init__(self, parent, id): | |
f0c20d94 | 424 | wxTreeCtrl.__init__(self, parent, id, style = wxTR_HAS_BUTTONS) |
09f3d4e6 RD |
425 | self.SetBackgroundColour(wxColour(224, 248, 224)) |
426 | EVT_TREE_SEL_CHANGED(self, self.GetId(), self.OnSelChanged) | |
32ce09e2 | 427 | # One works on Linux, another on Windows |
c1beeb57 | 428 | if wxGetOsVersion()[0] == wxGTK: |
f0c20d94 RD |
429 | EVT_TREE_ITEM_ACTIVATED(self, self.GetId(), self.OnItemActivated) |
430 | else: | |
431 | EVT_LEFT_DCLICK(self, self.OnDClick) | |
09f3d4e6 | 432 | EVT_RIGHT_DOWN(self, self.OnRightDown) |
f0c20d94 | 433 | |
09f3d4e6 RD |
434 | self.needUpdate = false |
435 | self.pendingHighLight = None | |
18fc9fa3 | 436 | self.ctrl = self.shift = false |
09f3d4e6 RD |
437 | self.dom = None |
438 | # Create image list | |
439 | il = wxImageList(16, 16, true) | |
a40b5e84 RD |
440 | self.rootImage = il.AddIcon(wxIconFromXPMData(images.getTreeRootData())) |
441 | xxxObject.image = il.AddIcon(wxIconFromXPMData(images.getTreeDefaultData())) | |
442 | xxxPanel.image = il.AddIcon(wxIconFromXPMData(images.getTreePanelData())) | |
443 | xxxDialog.image = il.AddIcon(wxIconFromXPMData(images.getTreeDialogData())) | |
444 | xxxFrame.image = il.AddIcon(wxIconFromXPMData(images.getTreeFrameData())) | |
445 | xxxMenuBar.image = il.AddIcon(wxIconFromXPMData(images.getTreeMenuBarData())) | |
32ce09e2 | 446 | xxxToolBar.image = il.AddIcon(wxIconFromXPMData(images.getTreeToolBarData())) |
a40b5e84 RD |
447 | xxxMenu.image = il.AddIcon(wxIconFromXPMData(images.getTreeMenuData())) |
448 | xxxSizer.imageH = il.AddIcon(wxIconFromXPMData(images.getTreeSizerHData())) | |
449 | xxxSizer.imageV = il.AddIcon(wxIconFromXPMData(images.getTreeSizerVData())) | |
450 | xxxStaticBoxSizer.imageH = il.AddIcon(wxIconFromXPMData(images.getTreeStaticBoxSizerHData())) | |
451 | xxxStaticBoxSizer.imageV = il.AddIcon(wxIconFromXPMData(images.getTreeStaticBoxSizerVData())) | |
452 | xxxGridSizer.image = il.AddIcon(wxIconFromXPMData(images.getTreeSizerGridData())) | |
453 | xxxFlexGridSizer.image = il.AddIcon(wxIconFromXPMData(images.getTreeSizerFlexGridData())) | |
09f3d4e6 RD |
454 | self.il = il |
455 | self.SetImageList(il) | |
456 | ||
09f3d4e6 | 457 | def Unselect(self): |
f0c20d94 | 458 | self.selection = None |
09f3d4e6 | 459 | wxTreeCtrl.Unselect(self) |
e973de2e | 460 | |
09f3d4e6 RD |
461 | def ExpandAll(self, item): |
462 | if self.ItemHasChildren(item): | |
463 | self.Expand(item) | |
464 | i, cookie = self.GetFirstChild(item, 0) | |
465 | children = [] | |
466 | while i.IsOk(): | |
467 | children.append(i) | |
468 | i, cookie = self.GetNextChild(item, cookie) | |
469 | for i in children: | |
470 | self.ExpandAll(i) | |
f0c20d94 RD |
471 | def CollapseAll(self, item): |
472 | if self.ItemHasChildren(item): | |
473 | i, cookie = self.GetFirstChild(item, 0) | |
474 | children = [] | |
475 | while i.IsOk(): | |
476 | children.append(i) | |
477 | i, cookie = self.GetNextChild(item, cookie) | |
478 | for i in children: | |
479 | self.CollapseAll(i) | |
480 | self.Collapse(item) | |
09f3d4e6 | 481 | |
f0c20d94 RD |
482 | # Clear tree |
483 | def Clear(self): | |
484 | self.DeleteAllItems() | |
485 | # Add minimal structure | |
486 | if self.dom: self.dom.unlink() | |
487 | self.dom = minidom.Document() | |
488 | self.dummyNode = self.dom.createComment('dummy node') | |
489 | # Create main node | |
490 | self.mainNode = self.dom.createElement('resource') | |
491 | self.dom.appendChild(self.mainNode) | |
492 | xxx = xxxMainNode(None, self.mainNode) | |
493 | self.root = self.AddRoot('XML tree', self.rootImage, data=wxTreeItemData(xxx)) | |
494 | self.SetItemHasChildren(self.root) | |
495 | self.Expand(self.root) | |
496 | self.Unselect() | |
497 | ||
498 | # Clear old data and set new | |
09f3d4e6 | 499 | def SetData(self, dom): |
f0c20d94 RD |
500 | self.DeleteAllItems() |
501 | # Add minimal structure | |
502 | if self.dom: self.dom.unlink() | |
09f3d4e6 | 503 | self.dom = dom |
f0c20d94 | 504 | self.dummyNode = self.dom.createComment('dummy node') |
09f3d4e6 RD |
505 | # Find 'resource' child, add it's children |
506 | self.mainNode = dom.getElementsByTagName('resource')[0] | |
f0c20d94 RD |
507 | xxx = xxxMainNode(None, self.mainNode) |
508 | self.root = self.AddRoot('XML tree', self.rootImage, data=wxTreeItemData(xxx)) | |
509 | self.SetItemHasChildren(self.root) | |
09f3d4e6 RD |
510 | nodes = self.mainNode.childNodes[:] |
511 | for node in nodes: | |
512 | if IsObject(node): | |
f0c20d94 | 513 | self.AddNode(self.root, None, node) |
09f3d4e6 RD |
514 | else: |
515 | self.mainNode.removeChild(node) | |
516 | node.unlink() | |
f0c20d94 | 517 | self.Expand(self.root) |
09f3d4e6 RD |
518 | self.Unselect() |
519 | ||
520 | # Add tree item for given parent item if node is DOM element node with | |
521 | # 'object' tag. xxxParent is parent xxx object | |
522 | def AddNode(self, itemParent, xxxParent, node): | |
523 | # Set item data to current node | |
32ce09e2 RD |
524 | try: |
525 | xxx = MakeXXXFromDOM(xxxParent, node) | |
526 | except: | |
f0c20d94 RD |
527 | print 'ERROR: MakeXXXFromDom(%s, %s)' % (xxxParent, node) |
528 | raise | |
09f3d4e6 RD |
529 | treeObj = xxx.treeObject() |
530 | # Append tree item | |
531 | item = self.AppendItem(itemParent, treeObj.treeName(), | |
532 | image=treeObj.treeImage(), | |
533 | data=wxTreeItemData(xxx)) | |
534 | # Try to find children objects | |
535 | if treeObj.hasChildren: | |
536 | nodes = treeObj.element.childNodes[:] | |
537 | for n in nodes: | |
538 | if IsObject(n): | |
539 | self.AddNode(item, treeObj, n) | |
540 | elif n.nodeType != minidom.Node.ELEMENT_NODE: | |
541 | treeObj.element.removeChild(n) | |
542 | n.unlink() | |
09f3d4e6 RD |
543 | # Remove leaf of tree, return it's data object |
544 | def RemoveLeaf(self, leaf): | |
545 | xxx = self.GetPyData(leaf) | |
546 | node = xxx.element | |
547 | parent = node.parentNode | |
548 | parent.removeChild(node) | |
549 | self.Delete(leaf) | |
f0c20d94 RD |
550 | # Reset selection object |
551 | self.selection = None | |
09f3d4e6 | 552 | return node |
09f3d4e6 RD |
553 | # Find position relative to the top-level window |
554 | def FindNodePos(self, item): | |
18fc9fa3 RD |
555 | # Root at (0,0) |
556 | if item == testWin.item: return wxPoint(0, 0) | |
09f3d4e6 | 557 | itemParent = self.GetItemParent(item) |
f0c20d94 | 558 | # Select NB page |
09f3d4e6 | 559 | obj = self.FindNodeObject(item) |
f0c20d94 RD |
560 | if self.GetPyData(itemParent).treeObject().__class__ == xxxNotebook: |
561 | notebook = self.FindNodeObject(itemParent) | |
562 | # Find position | |
563 | for i in range(notebook.GetPageCount()): | |
564 | if notebook.GetPage(i) == obj: | |
565 | if notebook.GetSelection() != i: notebook.SetSelection(i) | |
566 | break | |
09f3d4e6 RD |
567 | # Find first ancestor which is a wxWindow (not a sizer) |
568 | winParent = itemParent | |
569 | while self.GetPyData(winParent).isSizer: | |
570 | winParent = self.GetItemParent(winParent) | |
571 | parentPos = self.FindNodePos(winParent) | |
f0c20d94 RD |
572 | # Position (-1,-1) is really (0,0) |
573 | pos = obj.GetPosition() | |
574 | if pos == (-1,-1): pos = (0,0) | |
575 | return parentPos + pos | |
09f3d4e6 RD |
576 | # Find window (or sizer) corresponding to a tree item. |
577 | def FindNodeObject(self, item): | |
18fc9fa3 | 578 | if item == testWin.item: return testWin.panel |
09f3d4e6 RD |
579 | itemParent = self.GetItemParent(item) |
580 | # If top-level, return testWin (or panel if wxFrame) | |
09f3d4e6 RD |
581 | xxx = self.GetPyData(item).treeObject() |
582 | parentWin = self.FindNodeObject(itemParent) | |
583 | # Top-level sizer? return window's sizer | |
584 | if xxx.isSizer and isinstance(parentWin, wxWindowPtr): | |
585 | return parentWin.GetSizer() | |
586 | # Otherwise get parent's object and it's child | |
587 | n = 0 # index of sibling | |
588 | prev = self.GetPrevSibling(item) | |
589 | while prev.IsOk(): | |
590 | prev = self.GetPrevSibling(prev) | |
591 | n += 1 | |
592 | child = parentWin.GetChildren()[n] | |
593 | # Return window or sizer for sizer items | |
594 | if child.GetClassName() == 'wxSizerItem': | |
595 | if child.IsWindow(): child = child.GetWindow() | |
596 | elif child.IsSizer(): | |
597 | child = child.GetSizer() | |
598 | # Test for notebook sizers | |
599 | if isinstance(child, wxNotebookSizerPtr): | |
600 | child = child.GetNotebook() | |
601 | return child | |
09f3d4e6 RD |
602 | def OnSelChanged(self, evt): |
603 | # Apply changes | |
f0c20d94 | 604 | # !!! problem with wxGTK - GetOldItem is Ok if nothing selected |
09f3d4e6 | 605 | #oldItem = evt.GetOldItem() |
f0c20d94 | 606 | status = '' |
09f3d4e6 | 607 | oldItem = self.selection |
f0c20d94 | 608 | if oldItem: |
09f3d4e6 RD |
609 | xxx = self.GetPyData(oldItem) |
610 | # If some data was modified, apply changes | |
f0c20d94 RD |
611 | if panel.IsModified(): |
612 | self.Apply(xxx, oldItem) | |
613 | #if conf.autoRefresh: | |
c1beeb57 RD |
614 | if testWin: |
615 | if testWin.highLight and not tree.IsHighlatable(oldItem): | |
09f3d4e6 RD |
616 | testWin.highLight.Remove() |
617 | self.needUpdate = true | |
f0c20d94 RD |
618 | status = 'Changes were applied' |
619 | frame.SetStatusText(status) | |
620 | # Generate view | |
621 | self.selection = evt.GetItem() | |
622 | if not self.selection.IsOk(): | |
623 | self.selection = None | |
624 | return | |
625 | xxx = self.GetPyData(self.selection) | |
a40b5e84 RD |
626 | # Update panel |
627 | panel.SetData(xxx) | |
09f3d4e6 RD |
628 | # Clear flag |
629 | panel.SetModified(false) | |
630 | # Hightlighting is done in OnIdle | |
f0c20d94 | 631 | tree.pendingHighLight = self.selection |
18fc9fa3 RD |
632 | # Check if item is in testWin subtree |
633 | def IsHighlatable(self, item): | |
634 | if item == testWin.item: return false | |
635 | while item != self.root: | |
09f3d4e6 | 636 | item = self.GetItemParent(item) |
18fc9fa3 RD |
637 | if item == testWin.item: return true |
638 | return false | |
09f3d4e6 RD |
639 | # Highlight selected item |
640 | def HighLight(self, item): | |
641 | self.pendingHighLight = None | |
642 | if not testWin or self.GetPyData(testWin.item).className \ | |
643 | not in ['wxDialog', 'wxPanel', 'wxFrame']: | |
644 | return | |
645 | # Top-level does not have highlight | |
f0c20d94 | 646 | if item == testWin.item or item == tree.root: |
09f3d4e6 RD |
647 | if testWin.highLight: testWin.highLight.Remove() |
648 | return | |
649 | # If a control from another window is selected, remove highlight | |
18fc9fa3 | 650 | if not self.IsHighlatable(item): |
f0c20d94 | 651 | if testWin.highLight: testWin.highLight.Remove() |
09f3d4e6 RD |
652 | return |
653 | # Get window/sizer object | |
654 | obj, pos = self.FindNodeObject(item), self.FindNodePos(item) | |
655 | size = obj.GetSize() | |
09f3d4e6 | 656 | # Highlight |
f0c20d94 RD |
657 | # Nagative positions are not working wuite well |
658 | if testWin.highLight: | |
09f3d4e6 | 659 | testWin.highLight.Replace(pos, size) |
f0c20d94 | 660 | else: |
09f3d4e6 RD |
661 | testWin.highLight = HightLightBox(pos, size) |
662 | testWin.highLight.item = item | |
f0c20d94 RD |
663 | def ShowTestWindow(self, item): |
664 | global testWin | |
09f3d4e6 | 665 | xxx = self.GetPyData(item) |
09f3d4e6 RD |
666 | if panel.IsModified(): |
667 | self.Apply(xxx, item) # apply changes | |
f0c20d94 RD |
668 | treeObj = xxx.treeObject() |
669 | if treeObj.className not in ['wxFrame', 'wxPanel', 'wxDialog', | |
670 | 'wxMenuBar', 'wxToolBar']: | |
671 | wxLogMessage('No view for this element (yet)') | |
672 | return | |
673 | if not treeObj.name: | |
674 | wxLogError("Can't display a noname element!") | |
675 | return | |
676 | # Show item in bold | |
677 | if testWin: | |
678 | self.SetItemBold(testWin.item, false) | |
679 | self.SetItemBold(item) | |
09f3d4e6 | 680 | self.CreateTestWin(item) |
f0c20d94 RD |
681 | # Double-click on Linux |
682 | def OnItemActivated(self, evt): | |
683 | if evt.GetItem() != self.root: | |
684 | self.ShowTestWindow(evt.GetItem()) | |
32ce09e2 RD |
685 | # Double-click on Windows |
686 | def OnDClick(self, evt): | |
f0c20d94 | 687 | item, flags = self.HitTest(evt.GetPosition()) |
32ce09e2 | 688 | if flags in [wxTREE_HITTEST_ONITEMBUTTON, wxTREE_HITTEST_ONITEMLABEL]: |
f0c20d94 | 689 | if item != self.root: self.ShowTestWindow(item) |
32ce09e2 RD |
690 | else: |
691 | evt.Skip() | |
09f3d4e6 | 692 | # (re)create test window |
f0c20d94 | 693 | def CreateTestWin(self, item): |
09f3d4e6 | 694 | global testWin |
18fc9fa3 | 695 | wxBeginBusyCursor() |
09f3d4e6 | 696 | # Create a window with this resource |
f0c20d94 | 697 | xxx = self.GetPyData(item).treeObject() |
09f3d4e6 RD |
698 | # Close old window, remember where it was |
699 | highLight = None | |
700 | if testWin: | |
701 | pos = testWin.GetPosition() | |
f0c20d94 | 702 | if item == testWin.item: |
09f3d4e6 RD |
703 | # Remember highlight if same top-level window |
704 | if testWin.highLight: | |
705 | highLight = testWin.highLight.item | |
706 | # !!! if 0 is removed, refresh is broken (notebook not deleted?) | |
18fc9fa3 | 707 | if xxx.className == 'wxPanel': |
09f3d4e6 RD |
708 | if testWin.highLight: |
709 | testWin.pendingHighLight = highLight | |
710 | testWin.highLight.Remove() | |
711 | testWin.panel.Destroy() | |
712 | testWin.panel = None | |
713 | else: | |
714 | testWin.Destroy() | |
715 | testWin = None | |
716 | else: | |
717 | testWin.Destroy() | |
718 | testWin = None | |
719 | else: | |
720 | pos = testWinPos | |
f0c20d94 RD |
721 | # Save in memory FS |
722 | memFile = MemoryFile('xxx.xrc') | |
09f3d4e6 RD |
723 | # Create partial XML file - faster for big files |
724 | ||
725 | dom = minidom.Document() | |
726 | mainNode = dom.createElement('resource') | |
727 | dom.appendChild(mainNode) | |
728 | ||
729 | # Remove temporarily from old parent | |
730 | elem = xxx.element | |
731 | parent = elem.parentNode | |
732 | next = elem.nextSibling | |
733 | parent.replaceChild(self.dummyNode, elem) | |
734 | # Append to new DOM, write it | |
735 | mainNode.appendChild(elem) | |
736 | dom.writexml(memFile) | |
737 | # Put back in place | |
738 | mainNode.removeChild(elem) | |
739 | dom.unlink() | |
740 | parent.replaceChild(elem, self.dummyNode) | |
09f3d4e6 RD |
741 | memFile.close() # write to wxMemoryFS |
742 | res = wxXmlResource('') | |
f0c20d94 | 743 | res.Load('memory:xxx.xrc') |
09f3d4e6 RD |
744 | if xxx.className == 'wxFrame': |
745 | # Create new frame | |
746 | testWin = wxPreFrame() | |
747 | res.LoadFrame(testWin, frame, xxx.name) | |
18fc9fa3 RD |
748 | # Create status bar |
749 | testWin.CreateStatusBar() | |
09f3d4e6 RD |
750 | testWin.panel = testWin |
751 | testWin.SetPosition(pos) | |
752 | testWin.Show(true) | |
753 | elif xxx.className == 'wxPanel': | |
754 | # Create new frame | |
755 | if not testWin: | |
756 | testWin = wxFrame(frame, -1, 'Panel: ' + xxx.name, pos=pos) | |
757 | testWin.panel = res.LoadPanel(testWin, xxx.name) | |
f0c20d94 | 758 | testWin.SetClientSize(testWin.panel.GetSize()) |
09f3d4e6 RD |
759 | testWin.Show(true) |
760 | elif xxx.className == 'wxDialog': | |
761 | # Create new frame | |
762 | testWin = res.LoadDialog(None, xxx.name) | |
763 | testWin.panel = testWin | |
f0c20d94 | 764 | testWin.Layout() |
09f3d4e6 RD |
765 | testWin.SetPosition(pos) |
766 | testWin.Show(true) | |
767 | elif xxx.className == 'wxMenuBar': | |
768 | testWin = wxFrame(frame, -1, 'MenuBar: ' + xxx.name, pos=pos) | |
18fc9fa3 | 769 | testWin.panel = None |
09f3d4e6 RD |
770 | # Set status bar to display help |
771 | testWin.CreateStatusBar() | |
772 | testWin.menuBar = res.LoadMenuBar(xxx.name) | |
773 | testWin.SetMenuBar(testWin.menuBar) | |
774 | testWin.Show(true) | |
f0c20d94 RD |
775 | elif xxx.className == 'wxToolBar': |
776 | testWin = wxFrame(frame, -1, 'ToolBar: ' + xxx.name, pos=pos) | |
18fc9fa3 | 777 | testWin.panel = None |
f0c20d94 RD |
778 | # Set status bar to display help |
779 | testWin.CreateStatusBar() | |
780 | testWin.toolBar = res.LoadToolBar(testWin, xxx.name) | |
781 | testWin.SetToolBar(testWin.toolBar) | |
782 | testWin.Show(true) | |
783 | wxMemoryFSHandler_RemoveFile('xxx.xrc') | |
784 | testWin.item = item | |
18fc9fa3 RD |
785 | EVT_CLOSE(testWin, self.OnCloseTestWin) |
786 | EVT_BUTTON(testWin, wxID_OK, self.OnCloseTestWin) | |
787 | EVT_BUTTON(testWin, wxID_CANCEL, self.OnCloseTestWin) | |
09f3d4e6 RD |
788 | testWin.highLight = None |
789 | if highLight and not tree.pendingHighLight: | |
790 | self.HighLight(highLight) | |
18fc9fa3 | 791 | wxEndBusyCursor() |
e973de2e | 792 | |
09f3d4e6 RD |
793 | def OnCloseTestWin(self, evt): |
794 | global testWin, testWinPos | |
f0c20d94 | 795 | self.SetItemBold(testWin.item, false) |
09f3d4e6 RD |
796 | testWinPos = testWin.GetPosition() |
797 | testWin.Destroy() | |
798 | testWin = None | |
18fc9fa3 RD |
799 | |
800 | # Return item index in parent | |
801 | def ItemIndex(self, parent, item): | |
802 | i = 0 | |
803 | it, cookie = self.GetFirstChild(parent, 0) | |
804 | while it != item: | |
805 | i += 1 | |
806 | it, cookie = self.GetNextChild(parent, cookie) | |
807 | return i | |
09f3d4e6 RD |
808 | |
809 | # True if next item should be inserted after current (vs. appended to it) | |
810 | def NeedInsert(self, item): | |
811 | xxx = self.GetPyData(item) | |
f0c20d94 | 812 | if item == self.root: return false # root item |
f0c20d94 | 813 | if xxx.hasChildren and not self.GetChildrenCount(item, false): |
09f3d4e6 | 814 | return false |
f0c20d94 | 815 | return not (self.IsExpanded(item) and self.GetChildrenCount(item, false)) |
32ce09e2 | 816 | |
09f3d4e6 RD |
817 | # Pull-down |
818 | def OnRightDown(self, evt): | |
f0c20d94 RD |
819 | # select this item |
820 | pt = evt.GetPosition(); | |
821 | item, flags = self.HitTest(pt) | |
822 | if item.Ok() and flags & wxTREE_HITTEST_ONITEM: | |
823 | self.SelectItem(item) | |
824 | ||
09f3d4e6 | 825 | # Setup menu |
a40b5e84 | 826 | menu = wxMenu() |
e973de2e | 827 | |
f0c20d94 RD |
828 | item = self.selection |
829 | if not item: | |
a40b5e84 | 830 | menu.Append(pullDownMenu.ID_EXPAND, 'Expand', 'Expand tree') |
f0c20d94 | 831 | menu.Append(pullDownMenu.ID_COLLAPSE, 'Collapse', 'Collapse tree') |
09f3d4e6 RD |
832 | else: |
833 | self.ctrl = evt.ControlDown() # save Ctrl state | |
32ce09e2 RD |
834 | self.shift = evt.ShiftDown() # and Shift too |
835 | m = wxMenu() # create menu | |
18fc9fa3 RD |
836 | if self.ctrl: |
837 | needInsert = true | |
838 | else: | |
839 | needInsert = self.NeedInsert(item) | |
840 | if item == self.root or needInsert and self.GetItemParent(item) == self.root: | |
09f3d4e6 RD |
841 | m.Append(pullDownMenu.ID_NEW_PANEL, 'Panel', 'Create panel') |
842 | m.Append(pullDownMenu.ID_NEW_DIALOG, 'Dialog', 'Create dialog') | |
843 | m.Append(pullDownMenu.ID_NEW_FRAME, 'Frame', 'Create frame') | |
844 | m.AppendSeparator() | |
32ce09e2 RD |
845 | m.Append(pullDownMenu.ID_NEW_TOOL_BAR, 'ToolBar', 'Create toolbar') |
846 | m.Append(pullDownMenu.ID_NEW_MENU_BAR, 'MenuBar', 'Create menubar') | |
09f3d4e6 RD |
847 | m.Append(pullDownMenu.ID_NEW_MENU, 'Menu', 'Create menu') |
848 | else: | |
f0c20d94 RD |
849 | xxx = self.GetPyData(item).treeObject() |
850 | # Check parent for possible child nodes if inserting sibling | |
851 | if needInsert: xxx = xxx.parent | |
09f3d4e6 RD |
852 | if xxx.__class__ == xxxMenuBar: |
853 | m.Append(pullDownMenu.ID_NEW_MENU, 'Menu', 'Create menu') | |
f0c20d94 RD |
854 | elif xxx.__class__ in [xxxToolBar, xxxTool] or \ |
855 | xxx.__class__ == xxxSeparator and xxx.parent.__class__ == xxxToolBar: | |
32ce09e2 | 856 | SetMenu(m, pullDownMenu.toolBarControls) |
09f3d4e6 RD |
857 | elif xxx.__class__ in [xxxMenu, xxxMenuItem]: |
858 | SetMenu(m, pullDownMenu.menuControls) | |
859 | else: | |
860 | SetMenu(m, pullDownMenu.controls) | |
f0c20d94 RD |
861 | if xxx.__class__ == xxxNotebook: |
862 | m.Enable(m.FindItem('sizer'), false) | |
863 | elif not (xxx.isSizer or xxx.parent and xxx.parent.isSizer): | |
09f3d4e6 RD |
864 | m.Enable(pullDownMenu.ID_NEW_SPACER, false) |
865 | # Select correct label for create menu | |
32ce09e2 RD |
866 | if not needInsert: |
867 | if self.shift: | |
868 | menu.AppendMenu(wxNewId(), 'Insert Child', m, | |
869 | 'Create child object as the first child') | |
870 | else: | |
871 | menu.AppendMenu(wxNewId(), 'Append Child', m, | |
872 | 'Create child object as the last child') | |
09f3d4e6 | 873 | else: |
32ce09e2 RD |
874 | if self.shift: |
875 | menu.AppendMenu(wxNewId(), 'Create Sibling', m, | |
876 | 'Create sibling before selected object') | |
09f3d4e6 | 877 | else: |
a40b5e84 | 878 | menu.AppendMenu(wxNewId(), 'Create Sibling', m, |
32ce09e2 | 879 | 'Create sibling after selected object') |
a40b5e84 | 880 | menu.AppendSeparator() |
f0c20d94 | 881 | # Not using standart IDs because we don't want to show shortcuts |
a40b5e84 RD |
882 | menu.Append(wxID_CUT, 'Cut', 'Cut to the clipboard') |
883 | menu.Append(wxID_COPY, 'Copy', 'Copy to the clipboard') | |
f0c20d94 | 884 | if self.ctrl and item != tree.root: |
18fc9fa3 | 885 | menu.Append(pullDownMenu.ID_PASTE_SIBLING, 'Paste Sibling', |
f0c20d94 RD |
886 | 'Paste from the clipboard as a sibling') |
887 | else: | |
888 | menu.Append(wxID_PASTE, 'Paste', 'Paste from the clipboard') | |
a40b5e84 RD |
889 | menu.Append(pullDownMenu.ID_DELETE, |
890 | 'Delete', 'Delete object') | |
f0c20d94 | 891 | if self.ItemHasChildren(item): |
a40b5e84 RD |
892 | menu.AppendSeparator() |
893 | menu.Append(pullDownMenu.ID_EXPAND, 'Expand', 'Expand subtree') | |
f0c20d94 | 894 | menu.Append(pullDownMenu.ID_COLLAPSE, 'Collapse', 'Collapse subtree') |
a40b5e84 RD |
895 | self.PopupMenu(menu, evt.GetPosition()) |
896 | menu.Destroy() | |
09f3d4e6 | 897 | |
09f3d4e6 RD |
898 | # Apply changes |
899 | def Apply(self, xxx, item): | |
f0c20d94 RD |
900 | panel.Apply() |
901 | # Update tree view | |
902 | xxx = xxx.treeObject() | |
903 | if xxx.hasName and self.GetItemText(item) != xxx.name: | |
904 | self.SetItemText(item, xxx.treeName()) | |
905 | # Change tree icon for sizers | |
906 | if isinstance(xxx, xxxBoxSizer): | |
907 | self.SetItemImage(item, xxx.treeImage()) | |
908 | # Set global modified state | |
909 | frame.modified = true | |
09f3d4e6 | 910 | |
a40b5e84 RD |
911 | class PullDownMenu: |
912 | ID_NEW_PANEL = wxNewId() | |
913 | ID_NEW_DIALOG = wxNewId() | |
914 | ID_NEW_FRAME = wxNewId() | |
32ce09e2 RD |
915 | ID_NEW_TOOL_BAR = wxNewId() |
916 | ID_NEW_TOOL = wxNewId() | |
a40b5e84 RD |
917 | ID_NEW_MENU_BAR = wxNewId() |
918 | ID_NEW_MENU = wxNewId() | |
919 | ||
920 | ID_NEW_STATIC_TEXT = wxNewId() | |
921 | ID_NEW_TEXT_CTRL = wxNewId() | |
922 | ||
923 | ID_NEW_BUTTON = wxNewId() | |
924 | ID_NEW_BITMAP_BUTTON = wxNewId() | |
925 | ID_NEW_RADIO_BUTTON = wxNewId() | |
926 | ID_NEW_SPIN_BUTTON = wxNewId() | |
927 | ||
928 | ID_NEW_STATIC_BOX = wxNewId() | |
929 | ID_NEW_CHECK_BOX = wxNewId() | |
930 | ID_NEW_RADIO_BOX = wxNewId() | |
931 | ID_NEW_COMBO_BOX = wxNewId() | |
932 | ID_NEW_LIST_BOX = wxNewId() | |
e973de2e | 933 | |
a40b5e84 | 934 | ID_NEW_STATIC_LINE = wxNewId() |
32ce09e2 | 935 | ID_NEW_STATIC_BITMAP = wxNewId() |
a40b5e84 RD |
936 | ID_NEW_CHOICE = wxNewId() |
937 | ID_NEW_SLIDER = wxNewId() | |
938 | ID_NEW_GAUGE = wxNewId() | |
939 | ID_NEW_SCROLL_BAR = wxNewId() | |
940 | ID_NEW_TREE_CTRL = wxNewId() | |
941 | ID_NEW_LIST_CTRL = wxNewId() | |
942 | ID_NEW_CHECK_LIST = wxNewId() | |
943 | ID_NEW_NOTEBOOK = wxNewId() | |
944 | ID_NEW_HTML_WINDOW = wxNewId() | |
945 | ID_NEW_CALENDAR = wxNewId() | |
e973de2e | 946 | |
a40b5e84 RD |
947 | ID_NEW_BOX_SIZER = wxNewId() |
948 | ID_NEW_STATIC_BOX_SIZER = wxNewId() | |
949 | ID_NEW_GRID_SIZER = wxNewId() | |
950 | ID_NEW_FLEX_GRID_SIZER = wxNewId() | |
951 | ID_NEW_SPACER = wxNewId() | |
32ce09e2 RD |
952 | ID_NEW_TOOL_BAR = wxNewId() |
953 | ID_NEW_TOOL = wxNewId() | |
a40b5e84 RD |
954 | ID_NEW_MENU = wxNewId() |
955 | ID_NEW_MENU_ITEM = wxNewId() | |
956 | ID_NEW_SEPARATOR = wxNewId() | |
957 | ID_NEW_LAST = wxNewId() | |
958 | ID_EXPAND = wxNewId() | |
f0c20d94 | 959 | ID_COLLAPSE = wxNewId() |
18fc9fa3 | 960 | ID_PASTE_SIBLING = wxNewId() |
a40b5e84 RD |
961 | |
962 | def __init__(self, parent): | |
963 | self.ID_DELETE = parent.ID_DELETE | |
964 | EVT_MENU_RANGE(parent, self.ID_NEW_PANEL, | |
965 | self.ID_NEW_LAST, parent.OnCreate) | |
f0c20d94 | 966 | EVT_MENU(parent, self.ID_COLLAPSE, parent.OnCollapse) |
a40b5e84 | 967 | EVT_MENU(parent, self.ID_EXPAND, parent.OnExpand) |
18fc9fa3 | 968 | EVT_MENU(parent, self.ID_PASTE_SIBLING, parent.OnPaste) |
a40b5e84 RD |
969 | # We connect to tree, but process in frame |
970 | EVT_MENU_HIGHLIGHT_ALL(tree, parent.OnPullDownHighlight) | |
971 | ||
32ce09e2 RD |
972 | ################################################################################ |
973 | ||
18fc9fa3 RD |
974 | # ScrolledMessageDialog - modified from wxPython lib to set fixed-width font |
975 | class ScrolledMessageDialog(wxDialog): | |
976 | def __init__(self, parent, msg, caption, pos = wxDefaultPosition, size = (500,300)): | |
977 | from wxPython.lib.layoutf import Layoutf | |
978 | wxDialog.__init__(self, parent, -1, caption, pos, size) | |
979 | text = wxTextCtrl(self, -1, msg, wxDefaultPosition, | |
980 | wxDefaultSize, wxTE_MULTILINE | wxTE_READONLY) | |
981 | text.SetFont(modernFont) | |
982 | dc = wxWindowDC(text) | |
983 | w, h = dc.GetTextExtent(' ') | |
984 | ok = wxButton(self, wxID_OK, "OK") | |
985 | text.SetConstraints(Layoutf('t=t5#1;b=t5#2;l=l5#1;r=r5#1', (self,ok))) | |
986 | text.SetSize((w * 80 + 30, h * 40)) | |
987 | ok.SetConstraints(Layoutf('b=b5#1;x%w50#1;w!80;h!25', (self,))) | |
988 | self.SetAutoLayout(TRUE) | |
989 | self.Fit() | |
990 | self.CenterOnScreen(wxBOTH) | |
991 | ||
992 | ################################################################################ | |
993 | ||
09f3d4e6 | 994 | class Frame(wxFrame): |
f0c20d94 RD |
995 | def __init__(self, pos, size): |
996 | global frame | |
997 | frame = self | |
998 | wxFrame.__init__(self, None, -1, '', pos, size) | |
09f3d4e6 | 999 | self.CreateStatusBar() |
f0c20d94 | 1000 | icon = wxIcon(os.path.join(sys.path[0], 'xrced.ico'), wxBITMAP_TYPE_ICO) |
e973de2e | 1001 | self.SetIcon(icon) |
09f3d4e6 | 1002 | |
c1beeb57 | 1003 | # Idle flag |
18fc9fa3 RD |
1004 | self.inIdle = false |
1005 | ||
09f3d4e6 RD |
1006 | # Make menus |
1007 | menuBar = wxMenuBar() | |
1008 | ||
1009 | menu = wxMenu() | |
1010 | menu.Append(wxID_NEW, '&New\tCtrl-N', 'New file') | |
1011 | menu.Append(wxID_OPEN, '&Open...\tCtrl-O', 'Open XRC file') | |
1012 | menu.Append(wxID_SAVE, '&Save\tCtrl-S', 'Save XRC file') | |
1013 | menu.Append(wxID_SAVEAS, 'Save &As...', 'Save XRC file under different name') | |
1014 | menu.AppendSeparator() | |
1015 | menu.Append(wxID_EXIT, '&Quit\tCtrl-Q', 'Exit application') | |
1016 | menuBar.Append(menu, '&File') | |
e973de2e | 1017 | |
09f3d4e6 RD |
1018 | menu = wxMenu() |
1019 | menu.Append(wxID_UNDO, '&Undo\tCtrl-Z', 'Undo') | |
f0c20d94 | 1020 | menu.Append(wxID_REDO, '&Redo\tCtrl-Y', 'Redo') |
09f3d4e6 RD |
1021 | menu.AppendSeparator() |
1022 | menu.Append(wxID_CUT, 'Cut\tCtrl-X', 'Cut to the clipboard') | |
1023 | menu.Append(wxID_COPY, '&Copy\tCtrl-C', 'Copy to the clipboard') | |
1024 | menu.Append(wxID_PASTE, '&Paste\tCtrl-V', 'Paste from the clipboard') | |
1025 | self.ID_DELETE = wxNewId() | |
1026 | menu.Append(self.ID_DELETE, '&Delete\tCtrl-D', 'Delete object') | |
1027 | menuBar.Append(menu, '&Edit') | |
e973de2e | 1028 | |
09f3d4e6 | 1029 | menu = wxMenu() |
f0c20d94 RD |
1030 | self.ID_EMBED_PANEL = wxNewId() |
1031 | menu.Append(self.ID_EMBED_PANEL, '&Embed Panel', | |
1032 | 'Toggle embedding properties panel in the main window', true) | |
1033 | menu.Check(self.ID_EMBED_PANEL, conf.embedPanel) | |
1034 | menu.AppendSeparator() | |
1035 | self.ID_TEST = wxNewId() | |
1036 | menu.Append(self.ID_TEST, '&Test\tF5', 'Test window') | |
09f3d4e6 | 1037 | self.ID_REFRESH = wxNewId() |
f0c20d94 | 1038 | menu.Append(self.ID_REFRESH, '&Refresh\tCtrl-R', 'Refresh test window') |
09f3d4e6 RD |
1039 | self.ID_AUTO_REFRESH = wxNewId() |
1040 | menu.Append(self.ID_AUTO_REFRESH, '&Auto-refresh\tCtrl-A', | |
1041 | 'Toggle auto-refresh mode', true) | |
1042 | menu.Check(self.ID_AUTO_REFRESH, conf.autoRefresh) | |
1043 | menuBar.Append(menu, '&View') | |
e973de2e | 1044 | |
09f3d4e6 | 1045 | menu = wxMenu() |
f0c20d94 RD |
1046 | menu.Append(wxID_ABOUT, '&About...', 'About XCRed') |
1047 | self.ID_README = wxNewId() | |
1048 | menu.Append(self.ID_README, '&Readme...', 'View the README file') | |
09f3d4e6 RD |
1049 | if debug: |
1050 | self.ID_DEBUG_CMD = wxNewId() | |
1051 | menu.Append(self.ID_DEBUG_CMD, 'CMD', 'Python command line') | |
1052 | EVT_MENU(self, self.ID_DEBUG_CMD, self.OnDebugCMD) | |
1053 | menuBar.Append(menu, '&Help') | |
1054 | ||
1055 | self.menuBar = menuBar | |
1056 | self.SetMenuBar(menuBar) | |
1057 | ||
1058 | # Create toolbar | |
a40b5e84 RD |
1059 | tb = self.CreateToolBar(wxTB_HORIZONTAL | wxNO_BORDER | wxTB_FLAT) |
1060 | tb.SetToolBitmapSize((24, 23)) | |
f60765b0 RD |
1061 | tb.AddSimpleTool(wxID_NEW, images.getNewBitmap(), 'New', 'New file') |
1062 | tb.AddSimpleTool(wxID_OPEN, images.getOpenBitmap(), 'Open', 'Open file') | |
1063 | tb.AddSimpleTool(wxID_SAVE, images.getSaveBitmap(), 'Save', 'Save file') | |
f0c20d94 | 1064 | tb.AddControl(wxStaticLine(tb, -1, size=(-1,23), style=wxLI_VERTICAL)) |
f60765b0 RD |
1065 | tb.AddSimpleTool(wxID_CUT, images.getCutBitmap(), 'Cut', 'Cut') |
1066 | tb.AddSimpleTool(wxID_COPY, images.getCopyBitmap(), 'Copy', 'Copy') | |
1067 | tb.AddSimpleTool(wxID_PASTE, images.getPasteBitmap(), 'Paste', 'Paste') | |
f0c20d94 | 1068 | tb.AddControl(wxStaticLine(tb, -1, size=(-1,23), style=wxLI_VERTICAL)) |
f60765b0 RD |
1069 | tb.AddSimpleTool(self.ID_TEST, images.getTestBitmap(), 'Test', 'Test window') |
1070 | tb.AddSimpleTool(self.ID_REFRESH, images.getRefreshBitmap(), | |
09f3d4e6 | 1071 | 'Refresh', 'Refresh view') |
f60765b0 | 1072 | tb.AddSimpleTool(self.ID_AUTO_REFRESH, images.getAutoRefreshBitmap(), |
09f3d4e6 | 1073 | 'Auto-refresh', 'Toggle auto-refresh mode', true) |
c1beeb57 | 1074 | if wxGetOsVersion()[0] == wxGTK: |
f0c20d94 | 1075 | tb.AddSeparator() # otherwise auto-refresh sticks in status line |
09f3d4e6 | 1076 | tb.ToggleTool(self.ID_AUTO_REFRESH, conf.autoRefresh) |
09f3d4e6 | 1077 | tb.Realize() |
a40b5e84 | 1078 | self.tb = tb |
c1beeb57 | 1079 | self.minWidth = tb.GetSize()[0] # minimal width is the size of toolbar |
09f3d4e6 RD |
1080 | |
1081 | # File | |
1082 | EVT_MENU(self, wxID_NEW, self.OnNew) | |
1083 | EVT_MENU(self, wxID_OPEN, self.OnOpen) | |
1084 | EVT_MENU(self, wxID_SAVE, self.OnSaveOrSaveAs) | |
1085 | EVT_MENU(self, wxID_SAVEAS, self.OnSaveOrSaveAs) | |
1086 | EVT_MENU(self, wxID_EXIT, self.OnExit) | |
1087 | # Edit | |
1088 | EVT_MENU(self, wxID_UNDO, self.OnUndo) | |
1089 | EVT_MENU(self, wxID_REDO, self.OnRedo) | |
1090 | EVT_MENU(self, wxID_CUT, self.OnCut) | |
1091 | EVT_MENU(self, wxID_COPY, self.OnCopy) | |
1092 | EVT_MENU(self, wxID_PASTE, self.OnPaste) | |
1093 | EVT_MENU(self, self.ID_DELETE, self.OnDelete) | |
1094 | # View | |
f0c20d94 RD |
1095 | EVT_MENU(self, self.ID_EMBED_PANEL, self.OnEmbedPanel) |
1096 | EVT_MENU(self, self.ID_TEST, self.OnTest) | |
09f3d4e6 RD |
1097 | EVT_MENU(self, self.ID_REFRESH, self.OnRefresh) |
1098 | EVT_MENU(self, self.ID_AUTO_REFRESH, self.OnAutoRefresh) | |
1099 | # Help | |
1100 | EVT_MENU(self, wxID_ABOUT, self.OnAbout) | |
f0c20d94 | 1101 | EVT_MENU(self, self.ID_README, self.OnReadme) |
09f3d4e6 RD |
1102 | |
1103 | # Update events | |
1104 | EVT_UPDATE_UI(self, wxID_CUT, self.OnUpdateUI) | |
1105 | EVT_UPDATE_UI(self, wxID_COPY, self.OnUpdateUI) | |
1106 | EVT_UPDATE_UI(self, wxID_PASTE, self.OnUpdateUI) | |
1107 | EVT_UPDATE_UI(self, self.ID_DELETE, self.OnUpdateUI) | |
f0c20d94 RD |
1108 | EVT_UPDATE_UI(self, self.ID_TEST, self.OnUpdateUI) |
1109 | EVT_UPDATE_UI(self, self.ID_REFRESH, self.OnUpdateUI) | |
09f3d4e6 RD |
1110 | |
1111 | # Build interface | |
a40b5e84 RD |
1112 | sizer = wxBoxSizer(wxVERTICAL) |
1113 | sizer.Add(wxStaticLine(self, -1), 0, wxEXPAND) | |
1114 | splitter = wxSplitterWindow(self, -1, style=wxSP_3DSASH) | |
32ce09e2 | 1115 | self.splitter = splitter |
a40b5e84 | 1116 | splitter.SetMinimumPaneSize(100) |
09f3d4e6 RD |
1117 | # Create tree |
1118 | global tree | |
1119 | tree = XML_Tree(splitter, -1) | |
1120 | sys.modules['xxx'].tree = tree | |
f0c20d94 RD |
1121 | # !!! frame styles are broken |
1122 | # Miniframe for not embedded mode | |
1123 | miniFrame = wxFrame(self, -1, 'Properties Panel', | |
1124 | (conf.panelX, conf.panelY), | |
1125 | (conf.panelWidth, conf.panelHeight)) | |
1126 | self.miniFrame = miniFrame | |
1127 | sizer2 = wxBoxSizer() | |
1128 | miniFrame.SetAutoLayout(true) | |
1129 | miniFrame.SetSizer(sizer2) | |
1130 | EVT_CLOSE(self.miniFrame, self.OnCloseMiniFrame) | |
09f3d4e6 RD |
1131 | # Create panel for parameters |
1132 | global panel | |
f0c20d94 RD |
1133 | if conf.embedPanel: |
1134 | panel = Panel(splitter) | |
1135 | # Set plitter windows | |
1136 | splitter.SplitVertically(tree, panel, conf.sashPos) | |
1137 | else: | |
1138 | panel = Panel(miniFrame) | |
1139 | sizer2.Add(panel, 1, wxEXPAND) | |
1140 | miniFrame.Show(true) | |
1141 | splitter.Initialize(tree) | |
a40b5e84 RD |
1142 | sizer.Add(splitter, 1, wxEXPAND) |
1143 | self.SetAutoLayout(true) | |
1144 | self.SetSizer(sizer) | |
09f3d4e6 RD |
1145 | |
1146 | # Init pull-down menu data | |
09f3d4e6 | 1147 | global pullDownMenu |
a40b5e84 | 1148 | pullDownMenu = PullDownMenu(self) |
09f3d4e6 RD |
1149 | # Mapping from IDs to element names |
1150 | self.createMap = { | |
1151 | pullDownMenu.ID_NEW_PANEL: 'wxPanel', | |
1152 | pullDownMenu.ID_NEW_DIALOG: 'wxDialog', | |
1153 | pullDownMenu.ID_NEW_FRAME: 'wxFrame', | |
32ce09e2 RD |
1154 | pullDownMenu.ID_NEW_TOOL_BAR: 'wxToolBar', |
1155 | pullDownMenu.ID_NEW_TOOL: 'tool', | |
09f3d4e6 RD |
1156 | pullDownMenu.ID_NEW_MENU_BAR: 'wxMenuBar', |
1157 | pullDownMenu.ID_NEW_MENU: 'wxMenu', | |
32ce09e2 RD |
1158 | pullDownMenu.ID_NEW_MENU_ITEM: 'wxMenuItem', |
1159 | pullDownMenu.ID_NEW_SEPARATOR: 'separator', | |
09f3d4e6 RD |
1160 | |
1161 | pullDownMenu.ID_NEW_STATIC_TEXT: 'wxStaticText', | |
1162 | pullDownMenu.ID_NEW_TEXT_CTRL: 'wxTextCtrl', | |
1163 | ||
1164 | pullDownMenu.ID_NEW_BUTTON: 'wxButton', | |
1165 | pullDownMenu.ID_NEW_BITMAP_BUTTON: 'wxBitmapButton', | |
1166 | pullDownMenu.ID_NEW_RADIO_BUTTON: 'wxRadioButton', | |
1167 | pullDownMenu.ID_NEW_SPIN_BUTTON: 'wxSpinButton', | |
e973de2e | 1168 | |
09f3d4e6 RD |
1169 | pullDownMenu.ID_NEW_STATIC_BOX: 'wxStaticBox', |
1170 | pullDownMenu.ID_NEW_CHECK_BOX: 'wxCheckBox', | |
1171 | pullDownMenu.ID_NEW_RADIO_BOX: 'wxRadioBox', | |
1172 | pullDownMenu.ID_NEW_COMBO_BOX: 'wxComboBox', | |
1173 | pullDownMenu.ID_NEW_LIST_BOX: 'wxListBox', | |
e973de2e | 1174 | |
09f3d4e6 | 1175 | pullDownMenu.ID_NEW_STATIC_LINE: 'wxStaticLine', |
32ce09e2 | 1176 | pullDownMenu.ID_NEW_STATIC_BITMAP: 'wxStaticBitmap', |
09f3d4e6 RD |
1177 | pullDownMenu.ID_NEW_CHOICE: 'wxChoice', |
1178 | pullDownMenu.ID_NEW_SLIDER: 'wxSlider', | |
1179 | pullDownMenu.ID_NEW_GAUGE: 'wxGauge', | |
1180 | pullDownMenu.ID_NEW_SCROLL_BAR: 'wxScrollBar', | |
1181 | pullDownMenu.ID_NEW_TREE_CTRL: 'wxTreeCtrl', | |
1182 | pullDownMenu.ID_NEW_LIST_CTRL: 'wxListCtrl', | |
1183 | pullDownMenu.ID_NEW_CHECK_LIST: 'wxCheckList', | |
1184 | pullDownMenu.ID_NEW_NOTEBOOK: 'wxNotebook', | |
1185 | pullDownMenu.ID_NEW_HTML_WINDOW: 'wxHtmlWindow', | |
1186 | pullDownMenu.ID_NEW_CALENDAR: 'wxCalendar', | |
e973de2e | 1187 | |
09f3d4e6 RD |
1188 | pullDownMenu.ID_NEW_BOX_SIZER: 'wxBoxSizer', |
1189 | pullDownMenu.ID_NEW_STATIC_BOX_SIZER: 'wxStaticBoxSizer', | |
1190 | pullDownMenu.ID_NEW_GRID_SIZER: 'wxGridSizer', | |
1191 | pullDownMenu.ID_NEW_FLEX_GRID_SIZER: 'wxFlexGridSizer', | |
1192 | pullDownMenu.ID_NEW_SPACER: 'spacer', | |
09f3d4e6 RD |
1193 | } |
1194 | pullDownMenu.controls = [ | |
1195 | ['control', 'Various controls', | |
1196 | (pullDownMenu.ID_NEW_STATIC_TEXT, 'Label', 'Create static label'), | |
1197 | (pullDownMenu.ID_NEW_STATIC_LINE, 'Line', 'Create static line'), | |
1198 | (pullDownMenu.ID_NEW_TEXT_CTRL, 'TextBox', 'Create text box control'), | |
1199 | (pullDownMenu.ID_NEW_CHOICE, 'Choice', 'Create choice control'), | |
1200 | (pullDownMenu.ID_NEW_SLIDER, 'Slider', 'Create slider control'), | |
1201 | (pullDownMenu.ID_NEW_GAUGE, 'Gauge', 'Create gauge control'), | |
1202 | (pullDownMenu.ID_NEW_SCROLL_BAR, 'ScrollBar', 'Create scroll bar'), | |
1203 | (pullDownMenu.ID_NEW_TREE_CTRL, 'TreeCtrl', 'Create tree control'), | |
1204 | (pullDownMenu.ID_NEW_LIST_CTRL, 'ListCtrl', 'Create list control'), | |
09f3d4e6 RD |
1205 | (pullDownMenu.ID_NEW_HTML_WINDOW, 'HtmlWindow', 'Create HTML window'), |
1206 | (pullDownMenu.ID_NEW_CALENDAR, 'Calendar', 'Create calendar control'), | |
1207 | (pullDownMenu.ID_NEW_PANEL, 'Panel', 'Create panel'), | |
1208 | (pullDownMenu.ID_NEW_NOTEBOOK, 'Notebook', 'Create notebook control'), | |
1209 | ], | |
1210 | ['button', 'Buttons', | |
1211 | (pullDownMenu.ID_NEW_BUTTON, 'Button', 'Create button'), | |
1212 | (pullDownMenu.ID_NEW_BITMAP_BUTTON, 'BitmapButton', 'Create bitmap button'), | |
1213 | (pullDownMenu.ID_NEW_RADIO_BUTTON, 'RadioButton', 'Create radio button'), | |
1214 | (pullDownMenu.ID_NEW_SPIN_BUTTON, 'SpinButton', 'Create spin button'), | |
1215 | ], | |
1216 | ['box', 'Boxes', | |
1217 | (pullDownMenu.ID_NEW_STATIC_BOX, 'StaticBox', 'Create static box'), | |
1218 | (pullDownMenu.ID_NEW_CHECK_BOX, 'CheckBox', 'Create check box'), | |
1219 | (pullDownMenu.ID_NEW_RADIO_BOX, 'RadioBox', 'Create radio box'), | |
1220 | (pullDownMenu.ID_NEW_COMBO_BOX, 'ComboBox', 'Create combo box'), | |
1221 | (pullDownMenu.ID_NEW_LIST_BOX, 'ListBox', 'Create list box'), | |
f0c20d94 RD |
1222 | (pullDownMenu.ID_NEW_CHECK_LIST, 'CheckListBox', |
1223 | 'Create check list control'), | |
09f3d4e6 RD |
1224 | ], |
1225 | ['sizer', 'Sizers', | |
1226 | (pullDownMenu.ID_NEW_BOX_SIZER, 'BoxSizer', 'Create box sizer'), | |
1227 | (pullDownMenu.ID_NEW_STATIC_BOX_SIZER, 'StaticBoxSizer', | |
1228 | 'Create static box sizer'), | |
1229 | (pullDownMenu.ID_NEW_GRID_SIZER, 'GridSizer', 'Create grid sizer'), | |
1230 | (pullDownMenu.ID_NEW_FLEX_GRID_SIZER, 'FlexGridSizer', | |
1231 | 'Create flexgrid sizer'), | |
1232 | (pullDownMenu.ID_NEW_SPACER, 'Spacer', 'Create spacer'), | |
1233 | ] | |
1234 | ] | |
1235 | pullDownMenu.menuControls = [ | |
1236 | (pullDownMenu.ID_NEW_MENU, 'Menu', 'Create menu'), | |
1237 | (pullDownMenu.ID_NEW_MENU_ITEM, 'MenuItem', 'Create menu item'), | |
1238 | (pullDownMenu.ID_NEW_SEPARATOR, 'Separator', 'Create separator'), | |
1239 | ] | |
32ce09e2 RD |
1240 | pullDownMenu.toolBarControls = [ |
1241 | (pullDownMenu.ID_NEW_TOOL, 'Tool', 'Create tool'), | |
1242 | (pullDownMenu.ID_NEW_SEPARATOR, 'Separator', 'Create separator'), | |
f0c20d94 RD |
1243 | ['control', 'Various controls', |
1244 | (pullDownMenu.ID_NEW_STATIC_TEXT, 'Label', 'Create static label'), | |
1245 | (pullDownMenu.ID_NEW_STATIC_LINE, 'Line', 'Create static line'), | |
1246 | (pullDownMenu.ID_NEW_TEXT_CTRL, 'TextBox', 'Create text box control'), | |
1247 | (pullDownMenu.ID_NEW_CHOICE, 'Choice', 'Create choice control'), | |
1248 | (pullDownMenu.ID_NEW_SLIDER, 'Slider', 'Create slider control'), | |
1249 | (pullDownMenu.ID_NEW_GAUGE, 'Gauge', 'Create gauge control'), | |
1250 | (pullDownMenu.ID_NEW_SCROLL_BAR, 'ScrollBar', 'Create scroll bar'), | |
1251 | (pullDownMenu.ID_NEW_LIST_CTRL, 'ListCtrl', 'Create list control'), | |
1252 | ], | |
1253 | ['button', 'Buttons', | |
1254 | (pullDownMenu.ID_NEW_BUTTON, 'Button', 'Create button'), | |
1255 | (pullDownMenu.ID_NEW_BITMAP_BUTTON, 'BitmapButton', 'Create bitmap button'), | |
1256 | (pullDownMenu.ID_NEW_RADIO_BUTTON, 'RadioButton', 'Create radio button'), | |
1257 | (pullDownMenu.ID_NEW_SPIN_BUTTON, 'SpinButton', 'Create spin button'), | |
1258 | ], | |
1259 | ['box', 'Boxes', | |
1260 | (pullDownMenu.ID_NEW_STATIC_BOX, 'StaticBox', 'Create static box'), | |
1261 | (pullDownMenu.ID_NEW_CHECK_BOX, 'CheckBox', 'Create check box'), | |
1262 | (pullDownMenu.ID_NEW_RADIO_BOX, 'RadioBox', 'Create radio box'), | |
1263 | (pullDownMenu.ID_NEW_COMBO_BOX, 'ComboBox', 'Create combo box'), | |
1264 | (pullDownMenu.ID_NEW_LIST_BOX, 'ListBox', 'Create list box'), | |
1265 | (pullDownMenu.ID_NEW_CHECK_LIST, 'CheckListBox', | |
1266 | 'Create check list control'), | |
1267 | ], | |
32ce09e2 | 1268 | ] |
e973de2e | 1269 | |
09f3d4e6 RD |
1270 | # Initialize |
1271 | self.Clear() | |
1272 | ||
1273 | # Other events | |
1274 | EVT_IDLE(self, self.OnIdle) | |
1275 | EVT_CLOSE(self, self.OnCloseWindow) | |
1276 | ||
1277 | def OnNew(self, evt): | |
1278 | self.Clear() | |
1279 | ||
1280 | def OnOpen(self, evt): | |
1281 | if not self.AskSave(): return | |
1282 | dlg = wxFileDialog(self, 'Open', os.path.dirname(self.dataFile), | |
1283 | '', '*.xrc', wxOPEN | wxCHANGE_DIR) | |
1284 | if dlg.ShowModal() == wxID_OK: | |
1285 | path = dlg.GetPath() | |
1286 | self.SetStatusText('Loading...') | |
1287 | wxYield() | |
1288 | wxBeginBusyCursor() | |
f0c20d94 RD |
1289 | try: |
1290 | self.Open(path) | |
1291 | self.SetStatusText('Data loaded') | |
1292 | except: | |
1293 | self.SetStatusText('Failed') | |
1294 | raise | |
09f3d4e6 | 1295 | wxEndBusyCursor() |
e973de2e | 1296 | dlg.Destroy() |
09f3d4e6 RD |
1297 | |
1298 | def OnSaveOrSaveAs(self, evt): | |
1299 | if evt.GetId() == wxID_SAVEAS or not self.dataFile: | |
1300 | if self.dataFile: defaultName = '' | |
1301 | else: defaultName = 'UNTITLED.xrc' | |
1302 | dlg = wxFileDialog(self, 'Save As', os.path.dirname(self.dataFile), | |
1303 | defaultName, '*.xrc', | |
1304 | wxSAVE | wxOVERWRITE_PROMPT | wxCHANGE_DIR) | |
a40b5e84 RD |
1305 | if dlg.ShowModal() == wxID_OK: |
1306 | path = dlg.GetPath() | |
1307 | dlg.Destroy() | |
1308 | else: | |
1309 | dlg.Destroy() | |
1310 | return | |
09f3d4e6 RD |
1311 | else: |
1312 | path = self.dataFile | |
1313 | self.SetStatusText('Saving...') | |
1314 | wxYield() | |
1315 | wxBeginBusyCursor() | |
f0c20d94 RD |
1316 | try: |
1317 | self.Save(path) | |
1318 | self.dataFile = path | |
1319 | self.SetStatusText('Data saved') | |
1320 | except IOError: | |
1321 | self.SetStatusText('Failed') | |
09f3d4e6 | 1322 | wxEndBusyCursor() |
09f3d4e6 RD |
1323 | |
1324 | def OnExit(self, evt): | |
1325 | self.Close() | |
1326 | ||
1327 | def OnUndo(self, evt): | |
1328 | print '*** being implemented' | |
f0c20d94 | 1329 | return |
09f3d4e6 RD |
1330 | print self.lastOp, self.undo |
1331 | if self.lastOp == 'DELETE': | |
1332 | parent, prev, elem = self.undo | |
1333 | if prev.IsOk(): | |
1334 | xxx = MakeXXXFromDOM(tree.GetPyData(parent).treeObject(), elem) | |
1335 | item = tree.InsertItem( parent, prev, xxx.treeObject().className, | |
1336 | data=wxTreeItemData(xxx) ) | |
e973de2e | 1337 | |
09f3d4e6 RD |
1338 | def OnRedo(self, evt): |
1339 | print '*** being implemented' | |
e973de2e | 1340 | |
09f3d4e6 | 1341 | def OnCut(self, evt): |
f0c20d94 | 1342 | selected = tree.selection |
18fc9fa3 | 1343 | if not selected: return # key pressed event |
09f3d4e6 RD |
1344 | # Undo info |
1345 | self.lastOp = 'CUT' | |
1346 | self.undo = [tree.GetItemParent(selected), tree.GetPrevSibling(selected)] | |
1347 | # Delete testWin? | |
1348 | global testWin | |
1349 | if testWin: | |
1350 | # If deleting top-level item, delete testWin | |
1351 | if selected == testWin.item: | |
1352 | testWin.Destroy() | |
1353 | testWin = None | |
1354 | else: | |
1355 | # Remove highlight, update testWin | |
18fc9fa3 | 1356 | if not tree.IsHighlatable(selected): |
09f3d4e6 RD |
1357 | if testWin.highLight: testWin.highLight.Remove() |
1358 | tree.needUpdate = true | |
1359 | self.clipboard = tree.RemoveLeaf(selected) | |
1360 | tree.pendingHighLight = None | |
1361 | tree.Unselect() | |
1362 | panel.Clear() | |
1363 | self.modified = true | |
f0c20d94 | 1364 | self.SetStatusText('Removed to clipboard') |
09f3d4e6 RD |
1365 | |
1366 | def OnCopy(self, evt): | |
f0c20d94 | 1367 | selected = tree.selection |
18fc9fa3 | 1368 | if not selected: return # key pressed event |
09f3d4e6 RD |
1369 | xxx = tree.GetPyData(selected) |
1370 | self.clipboard = xxx.element.cloneNode(true) | |
f0c20d94 | 1371 | self.SetStatusText('Copied') |
09f3d4e6 RD |
1372 | |
1373 | def OnPaste(self, evt): | |
f0c20d94 | 1374 | selected = tree.selection |
18fc9fa3 RD |
1375 | if not selected: return # key pressed event |
1376 | # For pasting with Ctrl pressed | |
1377 | if evt.GetId() == pullDownMenu.ID_PASTE_SIBLING: appendChild = false | |
1378 | else: appendChild = not tree.NeedInsert(selected) | |
09f3d4e6 RD |
1379 | xxx = tree.GetPyData(selected) |
1380 | if not appendChild: | |
1381 | # If has next item, insert, else append to parent | |
1382 | nextItem = tree.GetNextSibling(selected) | |
1383 | if nextItem.IsOk(): | |
1384 | # Insert before nextItem | |
1385 | parentLeaf = tree.GetItemParent(selected) | |
1386 | else: # last child: change selected to parent | |
1387 | appendChild = true | |
1388 | selected = tree.GetItemParent(selected) | |
1389 | # Expanded container (must have children) | |
f0c20d94 | 1390 | elif tree.IsExpanded(selected) and tree.GetChildrenCount(selected, false): |
09f3d4e6 RD |
1391 | appendChild = false |
1392 | nextItem = tree.GetFirstChild(selected, 0)[0] | |
1393 | parentLeaf = selected | |
1394 | # Parent should be tree element or None | |
1395 | if appendChild: | |
1396 | parent = tree.GetPyData(selected) | |
1397 | else: | |
1398 | parent = tree.GetPyData(parentLeaf) | |
f0c20d94 | 1399 | if parent.hasChild: parent = parent.child |
09f3d4e6 RD |
1400 | |
1401 | # Create a copy of clipboard element | |
1402 | elem = self.clipboard.cloneNode(true) | |
1403 | # Tempopary xxx object to test things | |
1404 | xxx = MakeXXXFromDOM(parent, elem) | |
f0c20d94 RD |
1405 | |
1406 | # Check compatibility | |
1407 | error = false | |
1408 | # Top-level | |
1409 | x = xxx.treeObject() | |
1410 | if x.__class__ in [xxxDialog, xxxFrame, xxxMenuBar, xxxToolBar]: | |
1411 | if parent.__class__ != xxxMainNode: error = true | |
18fc9fa3 RD |
1412 | elif x.__class__ == xxxPanel and parent.__class__ == xxxMainNode: |
1413 | pass | |
f0c20d94 RD |
1414 | elif x.__class__ == xxxSpacer: |
1415 | if not parent.isSizer: error = true | |
1416 | elif x.__class__ == xxxSeparator: | |
1417 | if not parent.__class__ in [xxxMenu, xxxToolBar]: error = true | |
1418 | elif x.__class__ == xxxTool: | |
1419 | if parent.__class__ != xxxToolBar: error = true | |
1420 | elif x.__class__ == xxxMenuItem: | |
1421 | if not parent.__class__ in [xxxMenuBar, xxxMenu]: error = true | |
1422 | elif x.isSizer and parent.__class__ == xxxNotebook: error = true | |
1423 | else: # normal controls can be almost anywhere | |
1424 | if parent.__class__ == xxxMainNode or \ | |
1425 | parent.__class__ in [xxxMenuBar, xxxMenu]: error = true | |
1426 | if error: | |
1427 | if parent.__class__ == xxxMainNode: parentClass = 'root' | |
1428 | else: parentClass = parent.className | |
1429 | wxLogError('Incompatible parent/child: parent is %s, child is %s!' % | |
1430 | (parentClass, x.className)) | |
1431 | return | |
1432 | ||
1433 | # Check parent and child relationships. | |
1434 | # If parent is sizer or notebook, child is of wrong class or | |
1435 | # parent is normal window, child is child container then detach child. | |
09f3d4e6 | 1436 | isChildContainer = isinstance(xxx, xxxChildContainer) |
f0c20d94 RD |
1437 | if isChildContainer and \ |
1438 | ((parent.isSizer and not isinstance(xxx, xxxSizerItem)) or \ | |
09f3d4e6 | 1439 | (isinstance(parent, xxxNotebook) and not isinstance(xxx, xxxNotebookPage)) or \ |
f0c20d94 RD |
1440 | not (parent.isSizer or isinstance(parent, xxxNotebook))): |
1441 | elem.removeChild(xxx.child.element) # detach child | |
1442 | elem.unlink() # delete child container | |
1443 | elem = xxx.child.element # replace | |
1444 | # This may help garbage collection | |
1445 | xxx.child.parent = None | |
1446 | isChildContainer = false | |
1447 | # Parent is sizer or notebook, child is not child container | |
1448 | if parent.isSizer and not isChildContainer and not isinstance(xxx, xxxSpacer): | |
1449 | # Create sizer item element | |
1450 | sizerItemElem = MakeEmptyDOM('sizeritem') | |
1451 | sizerItemElem.appendChild(elem) | |
1452 | elem = sizerItemElem | |
1453 | elif isinstance(parent, xxxNotebook) and not isChildContainer: | |
1454 | pageElem = MakeEmptyDOM('notebookpage') | |
1455 | pageElem.appendChild(elem) | |
1456 | elem = pageElem | |
09f3d4e6 RD |
1457 | xxx = MakeXXXFromDOM(parent, elem) |
1458 | # Figure out if we must append a new child or sibling | |
1459 | if appendChild: | |
f0c20d94 | 1460 | parent.element.appendChild(elem) |
09f3d4e6 RD |
1461 | newItem = tree.AppendItem(selected, xxx.treeName(), image=xxx.treeImage(), |
1462 | data=wxTreeItemData(xxx)) | |
1463 | else: | |
1464 | node = tree.GetPyData(nextItem).element | |
f0c20d94 | 1465 | parent.element.insertBefore(elem, node) |
09f3d4e6 | 1466 | # Inserting before is difficult, se we insert after or first child |
18fc9fa3 RD |
1467 | index = tree.ItemIndex(parentLeaf, nextItem) |
1468 | newItem = tree.InsertItemBefore(parentLeaf, index, | |
1469 | xxx.treeName(), image=xxx.treeImage()) | |
1470 | tree.SetPyData(newItem, xxx) | |
1471 | # newItem = tree.InsertItem(parentLeaf, selected, xxx.treeName(), | |
1472 | # image=xxx.treeImage(), data=wxTreeItemData(xxx)) | |
09f3d4e6 RD |
1473 | # Add children items |
1474 | if xxx.hasChildren: | |
1475 | treeObj = xxx.treeObject() | |
1476 | for n in treeObj.element.childNodes: | |
1477 | if IsObject(n): | |
1478 | tree.AddNode(newItem, treeObj, n) | |
1479 | # Scroll to show new item | |
1480 | tree.EnsureVisible(newItem) | |
1481 | tree.SelectItem(newItem) | |
1482 | if not tree.IsVisible(newItem): | |
1483 | tree.ScrollTo(newItem) | |
1484 | tree.Refresh() | |
1485 | # Update view? | |
18fc9fa3 | 1486 | if testWin and tree.IsHighlatable(newItem): |
09f3d4e6 RD |
1487 | if conf.autoRefresh: |
1488 | tree.needUpdate = true | |
1489 | tree.pendingHighLight = newItem | |
1490 | else: | |
1491 | tree.pendingHighLight = None | |
1492 | self.modified = true | |
f0c20d94 | 1493 | self.SetStatusText('Pasted') |
09f3d4e6 RD |
1494 | |
1495 | def OnDelete(self, evt): | |
f0c20d94 | 1496 | selected = tree.selection |
18fc9fa3 | 1497 | if not selected: return # key pressed event |
09f3d4e6 RD |
1498 | # Undo info |
1499 | self.lastOp = 'DELETE' | |
1500 | self.undo = [tree.GetItemParent(selected), tree.GetPrevSibling(selected)] | |
1501 | # Delete testWin? | |
1502 | global testWin | |
1503 | if testWin: | |
1504 | # If deleting top-level item, delete testWin | |
1505 | if selected == testWin.item: | |
1506 | testWin.Destroy() | |
1507 | testWin = None | |
1508 | else: | |
1509 | # Remove highlight, update testWin | |
18fc9fa3 | 1510 | if not tree.IsHighlatable(selected): |
09f3d4e6 RD |
1511 | if testWin.highLight: testWin.highLight.Remove() |
1512 | tree.needUpdate = true | |
1513 | xnode = tree.RemoveLeaf(selected) | |
f0c20d94 RD |
1514 | # !!! cloneNode is broken, or something is wrong |
1515 | # self.undo.append(xnode.cloneNode(true)) | |
09f3d4e6 RD |
1516 | xnode.unlink() |
1517 | tree.pendingHighLight = None | |
1518 | tree.Unselect() | |
1519 | panel.Clear() | |
1520 | self.modified = true | |
f0c20d94 RD |
1521 | self.SetStatusText('Deleted') |
1522 | ||
1523 | def OnEmbedPanel(self, evt): | |
1524 | conf.embedPanel = evt.IsChecked() | |
1525 | if conf.embedPanel: | |
1526 | # Remember last dimentions | |
c1beeb57 RD |
1527 | conf.panelX, conf.panelY = self.miniFrame.GetPosition() |
1528 | conf.panelWidth, conf.panelHeight = self.miniFrame.GetSize() | |
1529 | size = self.GetSize() | |
1530 | pos = self.GetPosition() | |
1531 | sizePanel = panel.GetSize() | |
f0c20d94 RD |
1532 | panel.Reparent(self.splitter) |
1533 | self.miniFrame.GetSizer().RemoveWindow(panel) | |
c1beeb57 RD |
1534 | wxYield() |
1535 | # Widen | |
1536 | self.SetDimensions(pos.x, pos.y, size.x + sizePanel.x, size.y) | |
1537 | self.splitter.SplitVertically(tree, panel, conf.sashPos) | |
f0c20d94 RD |
1538 | self.miniFrame.Show(false) |
1539 | else: | |
c1beeb57 RD |
1540 | conf.sashPos = self.splitter.GetSashPosition() |
1541 | pos = self.GetPosition() | |
1542 | size = self.GetSize() | |
1543 | sizePanel = panel.GetSize() | |
f0c20d94 RD |
1544 | self.splitter.Unsplit(panel) |
1545 | sizer = self.miniFrame.GetSizer() | |
1546 | panel.Reparent(self.miniFrame) | |
1547 | panel.Show(true) | |
1548 | sizer.Add(panel, 1, wxEXPAND) | |
1549 | self.miniFrame.Show(true) | |
c1beeb57 RD |
1550 | self.miniFrame.SetDimensions(conf.panelX, conf.panelY, |
1551 | conf.panelWidth, conf.panelHeight) | |
1552 | wxYield() | |
1553 | # Reduce width | |
1554 | self.SetDimensions(pos.x, pos.y, | |
1555 | max(size.x - sizePanel.x, self.minWidth), size.y) | |
f0c20d94 RD |
1556 | |
1557 | def OnTest(self, evt): | |
18fc9fa3 | 1558 | if not tree.selection: return # key pressed event |
f0c20d94 | 1559 | tree.ShowTestWindow(tree.selection) |
09f3d4e6 RD |
1560 | |
1561 | def OnRefresh(self, evt): | |
1562 | # If modified, apply first | |
f0c20d94 RD |
1563 | selection = tree.selection |
1564 | if selection: | |
09f3d4e6 RD |
1565 | xxx = tree.GetPyData(selection) |
1566 | if xxx and panel.IsModified(): | |
1567 | tree.Apply(xxx, selection) | |
1568 | if testWin: | |
1569 | # (re)create | |
1570 | tree.CreateTestWin(testWin.item) | |
1571 | tree.needUpdate = false | |
1572 | ||
1573 | def OnAutoRefresh(self, evt): | |
1574 | conf.autoRefresh = evt.IsChecked() | |
1575 | self.menuBar.Check(self.ID_AUTO_REFRESH, conf.autoRefresh) | |
1576 | self.tb.ToggleTool(self.ID_AUTO_REFRESH, conf.autoRefresh) | |
1577 | ||
1578 | def OnAbout(self, evt): | |
32ce09e2 RD |
1579 | str = '%s %s\n\nRoman Rolinsky <rolinsky@mema.ucl.ac.be>' % \ |
1580 | (progname, version) | |
1581 | dlg = wxMessageDialog(self, str, 'About ' + progname, wxOK | wxCENTRE) | |
1582 | dlg.ShowModal() | |
1583 | dlg.Destroy() | |
09f3d4e6 | 1584 | |
f0c20d94 | 1585 | def OnReadme(self, evt): |
f0c20d94 | 1586 | text = open(os.path.join(sys.path[0], 'README'), 'r').read() |
18fc9fa3 | 1587 | dlg = ScrolledMessageDialog(self, text, "XRCed README") |
f0c20d94 RD |
1588 | dlg.ShowModal() |
1589 | dlg.Destroy() | |
1590 | ||
1591 | ||
09f3d4e6 RD |
1592 | # Simple emulation of python command line |
1593 | def OnDebugCMD(self, evt): | |
f0c20d94 | 1594 | import traceback |
09f3d4e6 RD |
1595 | while 1: |
1596 | try: | |
1597 | exec raw_input('C:\> ') | |
1598 | except EOFError: | |
1599 | print '^D' | |
1600 | break | |
1601 | except: | |
1602 | (etype, value, tb) =sys.exc_info() | |
1603 | tblist =traceback.extract_tb(tb)[1:] | |
1604 | msg =string.join(traceback.format_exception_only(etype, value) | |
1605 | +traceback.format_list(tblist)) | |
1606 | print msg | |
1607 | ||
1608 | def OnCreate(self, evt): | |
f0c20d94 | 1609 | selected = tree.selection |
18fc9fa3 RD |
1610 | if tree.ctrl: appendChild = false |
1611 | else: appendChild = not tree.NeedInsert(selected) | |
09f3d4e6 RD |
1612 | xxx = tree.GetPyData(selected) |
1613 | if not appendChild: | |
f0c20d94 RD |
1614 | # If insert before |
1615 | if tree.shift: | |
1616 | # If has previous item, insert after it, else append to parent | |
1617 | nextItem = selected | |
18fc9fa3 | 1618 | parentLeaf = tree.GetItemParent(selected) |
f0c20d94 RD |
1619 | else: |
1620 | # If has next item, insert, else append to parent | |
1621 | nextItem = tree.GetNextSibling(selected) | |
1622 | if nextItem.IsOk(): | |
1623 | # Insert before nextItem | |
1624 | parentLeaf = tree.GetItemParent(selected) | |
1625 | else: # last child: change selected to parent | |
1626 | appendChild = true | |
1627 | selected = tree.GetItemParent(selected) | |
09f3d4e6 | 1628 | # Expanded container (must have children) |
18fc9fa3 RD |
1629 | elif tree.shift and tree.IsExpanded(selected) \ |
1630 | and tree.GetChildrenCount(selected, false): | |
1631 | appendChild = false | |
1632 | nextItem = tree.GetFirstChild(selected, 0)[0] | |
1633 | parentLeaf = selected | |
09f3d4e6 RD |
1634 | # Parent should be tree element or None |
1635 | if appendChild: | |
1636 | parent = tree.GetPyData(selected) | |
1637 | else: | |
1638 | parent = tree.GetPyData(parentLeaf) | |
f0c20d94 | 1639 | if parent.hasChild: parent = parent.child |
09f3d4e6 RD |
1640 | |
1641 | # Create element | |
1642 | className = self.createMap[evt.GetId()] | |
1643 | xxx = MakeEmptyXXX(parent, className) | |
f0c20d94 RD |
1644 | |
1645 | # Set default name for top-level windows | |
1646 | if parent.__class__ == xxxMainNode: | |
1647 | cl = xxx.treeObject().__class__ | |
1648 | frame.maxIDs[cl] += 1 | |
1649 | xxx.treeObject().name = '%s%d' % (defaultIDs[cl], frame.maxIDs[cl]) | |
1650 | xxx.treeObject().element.setAttribute('name', xxx.treeObject().name) | |
1651 | ||
09f3d4e6 RD |
1652 | # Figure out if we must append a new child or sibling |
1653 | elem = xxx.element | |
1654 | if appendChild: | |
09f3d4e6 | 1655 | # Insert newline for debug purposes |
f0c20d94 | 1656 | parent.element.appendChild(elem) |
09f3d4e6 RD |
1657 | newItem = tree.AppendItem(selected, xxx.treeName(), image=xxx.treeImage(), |
1658 | data=wxTreeItemData(xxx)) | |
1659 | else: | |
1660 | node = tree.GetPyData(nextItem).element | |
f0c20d94 | 1661 | parent.element.insertBefore(elem, node) |
18fc9fa3 RD |
1662 | # !!! There is a different behavious on Win and GTK |
1663 | # !!! On Win InsertItem(parent, parent, ...) inserts at the end. | |
1664 | index = tree.ItemIndex(parentLeaf, nextItem) | |
1665 | newItem = tree.InsertItemBefore(parentLeaf, index, | |
1666 | xxx.treeName(), image=xxx.treeImage()) | |
1667 | # data=wxTreeItemData(xxx)) # does not work | |
1668 | tree.SetPyData(newItem, xxx) | |
1669 | # newItem = tree.InsertItem(parentLeaf, selected, | |
1670 | # xxx.treeName(), image=xxx.treeImage(), | |
1671 | # data=wxTreeItemData(xxx)) | |
09f3d4e6 RD |
1672 | tree.EnsureVisible(newItem) |
1673 | tree.SelectItem(newItem) | |
1674 | if not tree.IsVisible(newItem): | |
1675 | tree.ScrollTo(newItem) | |
1676 | tree.Refresh() | |
1677 | # Update view? | |
18fc9fa3 | 1678 | if testWin and tree.IsHighlatable(newItem): |
09f3d4e6 RD |
1679 | if conf.autoRefresh: |
1680 | tree.needUpdate = true | |
1681 | tree.pendingHighLight = newItem | |
1682 | else: | |
1683 | tree.pendingHighLight = None | |
1684 | ||
f0c20d94 | 1685 | # Expand/collapse subtree |
09f3d4e6 | 1686 | def OnExpand(self, evt): |
f0c20d94 RD |
1687 | if tree.selection: tree.ExpandAll(tree.selection) |
1688 | else: tree.ExpandAll(tree.root) | |
1689 | def OnCollapse(self, evt): | |
1690 | if tree.selection: tree.CollapseAll(tree.selection) | |
1691 | else: tree.CollapseAll(tree.root) | |
09f3d4e6 RD |
1692 | |
1693 | def OnPullDownHighlight(self, evt): | |
1694 | menuId = evt.GetMenuId() | |
a40b5e84 RD |
1695 | if menuId != -1: |
1696 | menu = evt.GetEventObject() | |
1697 | help = menu.GetHelpString(menuId) | |
1698 | self.SetStatusText(help) | |
1699 | else: | |
1700 | self.SetStatusText('') | |
09f3d4e6 RD |
1701 | |
1702 | def OnUpdateUI(self, evt): | |
1703 | if evt.GetId() in [wxID_CUT, wxID_COPY, self.ID_DELETE]: | |
f0c20d94 | 1704 | evt.Enable(tree.selection != tree.root) |
09f3d4e6 | 1705 | elif evt.GetId() == wxID_PASTE: |
f0c20d94 RD |
1706 | evt.Enable((self.clipboard and tree.selection) != None) |
1707 | elif evt.GetId() == self.ID_TEST: | |
1708 | evt.Enable(tree.selection != tree.root) | |
09f3d4e6 RD |
1709 | |
1710 | def OnIdle(self, evt): | |
18fc9fa3 RD |
1711 | if self.inIdle: return # Recursive call protection |
1712 | self.inIdle = true | |
09f3d4e6 RD |
1713 | if tree.needUpdate: |
1714 | if conf.autoRefresh: | |
1715 | if testWin: | |
18fc9fa3 | 1716 | self.SetStatusText('Refreshing test window...') |
09f3d4e6 RD |
1717 | # (re)create |
1718 | tree.CreateTestWin(testWin.item) | |
18fc9fa3 RD |
1719 | wxYield() |
1720 | self.SetStatusText('') | |
09f3d4e6 RD |
1721 | tree.needUpdate = false |
1722 | elif tree.pendingHighLight: | |
1723 | tree.HighLight(tree.pendingHighLight) | |
32ce09e2 RD |
1724 | else: |
1725 | evt.Skip() | |
18fc9fa3 | 1726 | self.inIdle = false |
09f3d4e6 | 1727 | |
f0c20d94 RD |
1728 | # We don't let close panel window |
1729 | def OnCloseMiniFrame(self, evt): | |
1730 | return | |
1731 | ||
09f3d4e6 RD |
1732 | def OnCloseWindow(self, evt): |
1733 | if not self.AskSave(): return | |
1734 | if testWin: testWin.Destroy() | |
32ce09e2 | 1735 | # Destroy cached windows |
f0c20d94 | 1736 | panel.cacheParent.Destroy() |
f0c20d94 RD |
1737 | if not panel.GetPageCount() == 2: |
1738 | panel.page2.Destroy() | |
1739 | conf.x, conf.y = self.GetPosition() | |
09f3d4e6 | 1740 | conf.width, conf.height = self.GetSize() |
f0c20d94 RD |
1741 | if conf.embedPanel: |
1742 | conf.sashPos = self.splitter.GetSashPosition() | |
f0c20d94 | 1743 | else: |
f0c20d94 RD |
1744 | conf.panelX, conf.panelY = self.miniFrame.GetPosition() |
1745 | conf.panelWidth, conf.panelHeight = self.miniFrame.GetSize() | |
09f3d4e6 RD |
1746 | evt.Skip() |
1747 | ||
1748 | def Clear(self): | |
1749 | self.dataFile = '' | |
1750 | self.clipboard = None | |
1751 | self.modified = false | |
1752 | panel.SetModified(false) | |
09f3d4e6 | 1753 | tree.Clear() |
f0c20d94 | 1754 | panel.Clear() |
09f3d4e6 RD |
1755 | global testWin |
1756 | if testWin: | |
1757 | testWin.Destroy() | |
1758 | testWin = None | |
1759 | self.SetTitle(progname) | |
f0c20d94 RD |
1760 | # Numbers for new controls |
1761 | self.maxIDs = {} | |
1762 | self.maxIDs[xxxPanel] = self.maxIDs[xxxDialog] = self.maxIDs[xxxFrame] = \ | |
1763 | self.maxIDs[xxxMenuBar] = self.maxIDs[xxxMenu] = self.maxIDs[xxxToolBar] = 0 | |
09f3d4e6 RD |
1764 | |
1765 | def Open(self, path): | |
1766 | # Try to read the file | |
1767 | try: | |
1768 | open(path) | |
1769 | self.Clear() | |
1770 | # Build wx tree | |
1771 | dom = minidom.parse(path) | |
1772 | tree.SetData(dom) | |
1773 | self.dataFile = path | |
1774 | self.SetTitle(progname + ': ' + os.path.basename(path)) | |
1775 | except: | |
f0c20d94 | 1776 | wxLogError('Error reading file: %s' % path) |
a40b5e84 | 1777 | raise |
09f3d4e6 | 1778 | |
f0c20d94 RD |
1779 | def Indent(self, node, indent = 0): |
1780 | # Copy child list because it will change soon | |
1781 | children = node.childNodes[:] | |
1782 | # Main node doesn't need to be indented | |
1783 | if indent: | |
1784 | text = self.domCopy.createTextNode('\n' + ' ' * indent) | |
1785 | node.parentNode.insertBefore(text, node) | |
1786 | if children: | |
1787 | # Append newline after last child, except for text nodes | |
1788 | if children[-1].nodeType == minidom.Node.ELEMENT_NODE: | |
1789 | text = self.domCopy.createTextNode('\n' + ' ' * indent) | |
1790 | node.appendChild(text) | |
1791 | # Indent children which are elements | |
1792 | for n in children: | |
1793 | if n.nodeType == minidom.Node.ELEMENT_NODE: | |
1794 | self.Indent(n, indent + 2) | |
1795 | ||
09f3d4e6 RD |
1796 | def Save(self, path): |
1797 | try: | |
f0c20d94 | 1798 | # Apply changes |
a40b5e84 | 1799 | self.OnRefresh(wxCommandEvent()) |
f0c20d94 RD |
1800 | f = open(path, 'w') |
1801 | # Make temporary copy | |
c1beeb57 RD |
1802 | # !!! We can't clone dom node, it works only once |
1803 | #self.domCopy = tree.dom.cloneNode(true) | |
1804 | self.domCopy = minidom.Document() | |
1805 | mainNode = self.domCopy.appendChild(tree.mainNode.cloneNode(true)) | |
1806 | self.Indent(mainNode) | |
1807 | self.domCopy.writexml(f) | |
f0c20d94 | 1808 | f.close() |
c1beeb57 RD |
1809 | self.domCopy.unlink() |
1810 | self.domCopy = None | |
09f3d4e6 RD |
1811 | self.modified = false |
1812 | panel.SetModified(false) | |
1813 | except: | |
f0c20d94 | 1814 | wxLogError('Error writing file: %s' % path) |
a40b5e84 | 1815 | raise |
09f3d4e6 RD |
1816 | |
1817 | def AskSave(self): | |
1818 | if not (self.modified or panel.IsModified()): return true | |
1819 | flags = wxICON_EXCLAMATION | wxYES_NO | wxCANCEL | wxCENTRE | |
32ce09e2 RD |
1820 | dlg = wxMessageDialog( self, 'File is modified. Save before exit?', |
1821 | 'Save before too late?', flags ) | |
1822 | say = dlg.ShowModal() | |
1823 | dlg.Destroy() | |
09f3d4e6 RD |
1824 | if say == wxID_YES: |
1825 | self.OnSaveOrSaveAs(wxCommandEvent(wxID_SAVE)) | |
1826 | # If save was successful, modified flag is unset | |
1827 | if not self.modified: return true | |
1828 | elif say == wxID_NO: | |
1829 | self.modified = false | |
1830 | panel.SetModified(false) | |
1831 | return true | |
1832 | return false | |
1833 | ||
a40b5e84 RD |
1834 | ################################################################################ |
1835 | ||
f0c20d94 RD |
1836 | def usage(): |
1837 | print >> sys.stderr, 'usage: xrced [-dvh] [file]' | |
1838 | ||
09f3d4e6 RD |
1839 | class App(wxApp): |
1840 | def OnInit(self): | |
f0c20d94 RD |
1841 | global debug, verbose |
1842 | # Process comand-line | |
1843 | try: | |
1844 | opts, args = getopt.getopt(sys.argv[1:], 'dvh') | |
1845 | except getopt.GetoptError: | |
1846 | print >> sys.stderr, 'Unknown option' | |
1847 | usage() | |
1848 | sys.exit(1) | |
1849 | for o,a in opts: | |
1850 | if o == '-h': | |
1851 | usage() | |
1852 | sys.exit(0) | |
1853 | elif o == '-d': | |
1854 | debug = true | |
1855 | elif o == '-v': | |
1856 | print 'XRCed version', version | |
1857 | sys.exit(0) | |
1858 | ||
1859 | self.SetAppName('xrced') | |
09f3d4e6 RD |
1860 | # Settings |
1861 | global conf | |
f0c20d94 | 1862 | conf = wxConfig(style = wxCONFIG_USE_LOCAL_FILE) |
09f3d4e6 | 1863 | conf.autoRefresh = conf.ReadInt('autorefresh', true) |
f0c20d94 | 1864 | pos = conf.ReadInt('x', -1), conf.ReadInt('y', -1) |
09f3d4e6 | 1865 | size = conf.ReadInt('width', 800), conf.ReadInt('height', 600) |
f0c20d94 | 1866 | conf.embedPanel = conf.ReadInt('embedPanel', true) |
32ce09e2 | 1867 | conf.sashPos = conf.ReadInt('sashPos', 200) |
f0c20d94 RD |
1868 | if not conf.embedPanel: |
1869 | conf.panelX = conf.ReadInt('panelX', -1) | |
1870 | conf.panelY = conf.ReadInt('panelY', -1) | |
1871 | else: | |
1872 | conf.panelX = conf.panelY = -1 | |
1873 | conf.panelWidth = conf.ReadInt('panelWidth', 200) | |
1874 | conf.panelHeight = conf.ReadInt('panelHeight', 200) | |
1875 | conf.panic = not conf.HasEntry('nopanic') | |
09f3d4e6 RD |
1876 | # Add handlers |
1877 | wxFileSystem_AddHandler(wxMemoryFSHandler()) | |
1878 | wxInitAllImageHandlers() | |
1879 | # Create main frame | |
f0c20d94 RD |
1880 | frame = Frame(pos, size) |
1881 | frame.Show(true) | |
32ce09e2 | 1882 | # Load resources from XRC file (!!! should be transformed to .py later?) |
09f3d4e6 RD |
1883 | sys.modules['params'].frame = frame |
1884 | frame.res = wxXmlResource('') | |
1885 | frame.res.Load(os.path.join(sys.path[0], 'xrced.xrc')) | |
f0c20d94 RD |
1886 | |
1887 | # Load file after showing | |
1888 | if args: | |
1889 | conf.panic = false | |
1890 | frame.open = frame.Open(args[0]) | |
1891 | ||
09f3d4e6 RD |
1892 | return true |
1893 | ||
1894 | def OnExit(self): | |
1895 | # Write config | |
1896 | wc = wxConfigBase_Get() | |
1897 | wc.WriteInt('autorefresh', conf.autoRefresh) | |
f0c20d94 RD |
1898 | wc.WriteInt('x', conf.x) |
1899 | wc.WriteInt('y', conf.y) | |
09f3d4e6 RD |
1900 | wc.WriteInt('width', conf.width) |
1901 | wc.WriteInt('height', conf.height) | |
f0c20d94 RD |
1902 | wc.WriteInt('embedPanel', conf.embedPanel) |
1903 | if not conf.embedPanel: | |
1904 | wc.WriteInt('panelX', conf.panelX) | |
1905 | wc.WriteInt('panelY', conf.panelY) | |
32ce09e2 | 1906 | wc.WriteInt('sashPos', conf.sashPos) |
f0c20d94 RD |
1907 | wc.WriteInt('panelWidth', conf.panelWidth) |
1908 | wc.WriteInt('panelHeight', conf.panelHeight) | |
1909 | wc.WriteInt('nopanic', 1) | |
09f3d4e6 RD |
1910 | wc.Flush() |
1911 | ||
1912 | def main(): | |
f60765b0 | 1913 | app = App() |
09f3d4e6 RD |
1914 | app.MainLoop() |
1915 | app.OnExit() | |
1916 | ||
1917 | if __name__ == '__main__': | |
1918 | main() |