]>
Commit | Line | Data |
---|---|---|
d14a1e28 RD |
1 | # Name: panel.py |
2 | # Purpose: XRC editor, Panel class and related | |
3 | # Author: Roman Rolinsky <rolinsky@mema.ucl.ac.be> | |
4 | # Created: 02.12.2002 | |
5 | # RCS-ID: $Id$ | |
1fded56b | 6 | |
d14a1e28 RD |
7 | from xxx import * # xxx imports globals and params |
8 | from undo import * | |
29a41103 | 9 | from wx.html import HtmlWindow |
d14a1e28 RD |
10 | |
11 | # Properties panel containing notebook | |
29a41103 | 12 | class Panel(wx.Notebook): |
d14a1e28 | 13 | def __init__(self, parent, id = -1): |
29a41103 RD |
14 | if wx.Platform != '__WXMAC__': # some problems with this style on macs |
15 | wx.Notebook.__init__(self, parent, id, style=wx.NB_BOTTOM) | |
d14a1e28 | 16 | else: |
29a41103 | 17 | wx.Notebook.__init__(self, parent, id) |
d14a1e28 RD |
18 | global panel |
19 | g.panel = panel = self | |
20 | self.modified = False | |
21 | ||
c08eb1d3 | 22 | # Set common sizes |
d14a1e28 | 23 | import params |
c08eb1d3 RR |
24 | cTmp = wx.Button(self, -1, '') |
25 | params.buttonSize = (self.DLG_SZE(buttonSizeD)[0], cTmp.GetSize()[1]) | |
26 | cTmp.Destroy() | |
27 | cTmp = wx.TextCtrl(self, -1, '') | |
28 | params.textSize = cTmp.GetSize() | |
29 | cTmp.Destroy() | |
30 | cTmp = wx.CheckBox(self, -1, 'growablerows ') # this is the longest | |
31 | ParamPage.labelSize = cTmp.GetSize() | |
32 | cTmp.Destroy() | |
33 | del cTmp | |
d14a1e28 RD |
34 | |
35 | # List of child windows | |
36 | self.pages = [] | |
37 | # Create scrolled windows for pages | |
29a41103 RD |
38 | self.page1 = wx.ScrolledWindow(self, -1) |
39 | sizer = wx.BoxSizer() | |
40 | sizer.Add(wx.BoxSizer()) # dummy sizer | |
d14a1e28 RD |
41 | self.page1.SetAutoLayout(True) |
42 | self.page1.SetSizer(sizer) | |
43 | self.AddPage(self.page1, 'Properties') | |
44 | # Second page | |
29a41103 | 45 | self.page2 = wx.ScrolledWindow(self, -1) |
2871fade | 46 | self.page2.Hide() |
29a41103 RD |
47 | sizer = wx.BoxSizer() |
48 | sizer.Add(wx.BoxSizer()) # dummy sizer | |
d14a1e28 RD |
49 | self.page2.SetAutoLayout(True) |
50 | self.page2.SetSizer(sizer) | |
5cd66f07 RD |
51 | # Cache for already used panels |
52 | self.pageCache = {} # cached property panels | |
53 | self.stylePageCache = {} # cached style panels | |
a78714eb | 54 | |
d14a1e28 RD |
55 | # Delete child windows and recreate page sizer |
56 | def ResetPage(self, page): | |
57 | topSizer = page.GetSizer() | |
58 | sizer = topSizer.GetChildren()[0].GetSizer() | |
59 | for w in page.GetChildren(): | |
5cd66f07 RD |
60 | sizer.Detach(w) |
61 | if isinstance(w, ParamPage): | |
62 | if w.IsShown(): | |
63 | w.Hide() | |
64 | else: | |
65 | w.Destroy() | |
68ae5821 | 66 | topSizer.Remove(sizer) |
d14a1e28 | 67 | # Create new windows |
29a41103 | 68 | sizer = wx.BoxSizer(wx.VERTICAL) |
d14a1e28 RD |
69 | # Special case - resize html window |
70 | if g.conf.panic: | |
29a41103 | 71 | topSizer.Add(sizer, 1, wx.EXPAND) |
d14a1e28 | 72 | else: |
29a41103 | 73 | topSizer.Add(sizer, 0, wx.ALL, 5) |
d14a1e28 | 74 | return sizer |
a78714eb | 75 | |
d14a1e28 RD |
76 | def SetData(self, xxx): |
77 | self.pages = [] | |
78 | # First page | |
d14a1e28 RD |
79 | # Remove current objects and sizer |
80 | sizer = self.ResetPage(self.page1) | |
64b9ac75 | 81 | if not xxx or (not xxx.allParams and not xxx.hasName and not xxx.hasChild): |
d14a1e28 | 82 | if g.tree.selection: |
29a41103 | 83 | sizer.Add(wx.StaticText(self.page1, -1, 'This item has no properties.')) |
d14a1e28 RD |
84 | else: # nothing selected |
85 | # If first time, show some help | |
86 | if g.conf.panic: | |
29a41103 RD |
87 | html = HtmlWindow(self.page1, -1, wx.DefaultPosition, |
88 | wx.DefaultSize, wx.SUNKEN_BORDER) | |
d14a1e28 | 89 | html.SetPage(g.helpText) |
29a41103 | 90 | sizer.Add(html, 1, wx.EXPAND) |
d14a1e28 RD |
91 | g.conf.panic = False |
92 | else: | |
29a41103 | 93 | sizer.Add(wx.StaticText(self.page1, -1, 'Select a tree item.')) |
d14a1e28 RD |
94 | else: |
95 | g.currentXXX = xxx.treeObject() | |
a4c013b2 RR |
96 | # Normal or SizerItem page |
97 | isGBSizerItem = isinstance(xxx.parent, xxxGridBagSizer) | |
75aa1946 | 98 | cacheID = (xxx.panelName(), isGBSizerItem) |
5cd66f07 RD |
99 | try: |
100 | page = self.pageCache[cacheID] | |
101 | page.box.SetLabel(xxx.panelName()) | |
102 | page.Show() | |
103 | except KeyError: | |
104 | page = PropPage(self.page1, xxx.panelName(), xxx) | |
105 | self.pageCache[cacheID] = page | |
d14a1e28 RD |
106 | page.SetValues(xxx) |
107 | self.pages.append(page) | |
29a41103 | 108 | sizer.Add(page, 1, wx.EXPAND) |
d14a1e28 RD |
109 | if xxx.hasChild: |
110 | # Special label for child objects - they may have different GUI | |
75aa1946 | 111 | cacheID = (xxx.child.panelName(), xxx.__class__) |
5cd66f07 RD |
112 | try: |
113 | page = self.pageCache[cacheID] | |
114 | page.box.SetLabel(xxx.child.panelName()) | |
115 | page.Show() | |
116 | except KeyError: | |
117 | page = PropPage(self.page1, xxx.child.panelName(), xxx.child) | |
118 | self.pageCache[cacheID] = page | |
d14a1e28 RD |
119 | page.SetValues(xxx.child) |
120 | self.pages.append(page) | |
29a41103 | 121 | sizer.Add(page, 0, wx.EXPAND | wx.TOP, 5) |
d14a1e28 RD |
122 | self.page1.Layout() |
123 | size = self.page1.GetSizer().GetMinSize() | |
124 | self.page1.SetScrollbars(1, 1, size.width, size.height, 0, 0, True) | |
125 | ||
126 | # Second page | |
127 | # Create if does not exist | |
128 | if xxx and xxx.treeObject().hasStyle: | |
129 | xxx = xxx.treeObject() | |
130 | # Simplest case: set data if class is the same | |
131 | sizer = self.ResetPage(self.page2) | |
5cd66f07 RD |
132 | try: |
133 | page = self.stylePageCache[xxx.__class__] | |
134 | page.Show() | |
135 | except KeyError: | |
136 | page = StylePage(self.page2, xxx.className + ' style', xxx) | |
137 | self.stylePageCache[xxx.__class__] = page | |
d14a1e28 RD |
138 | page.SetValues(xxx) |
139 | self.pages.append(page) | |
29a41103 | 140 | sizer.Add(page, 0, wx.EXPAND) |
d14a1e28 RD |
141 | # Add page if not exists |
142 | if not self.GetPageCount() == 2: | |
143 | self.AddPage(self.page2, 'Style') | |
144 | self.page2.Layout() | |
1c7da10e RD |
145 | if 'wxGTK' in wx.PlatformInfo: |
146 | self.page2.Show(True) | |
d14a1e28 RD |
147 | size = self.page2.GetSizer().GetMinSize() |
148 | self.page2.SetScrollbars(1, 1, size.width, size.height, 0, 0, True) | |
149 | else: | |
150 | # Remove page if exists | |
151 | if self.GetPageCount() == 2: | |
152 | self.SetSelection(0) | |
153 | self.page1.Refresh() | |
154 | self.RemovePage(1) | |
155 | self.modified = False | |
5cd66f07 | 156 | |
d14a1e28 RD |
157 | def Clear(self): |
158 | self.SetData(None) | |
159 | self.modified = False | |
5cd66f07 | 160 | |
d14a1e28 RD |
161 | # If some parameter has changed |
162 | def IsModified(self): | |
163 | return self.modified | |
5cd66f07 | 164 | |
d14a1e28 RD |
165 | def SetModified(self, value): |
166 | # Register undo object when modifying first time | |
167 | if not self.modified and value: | |
168 | g.undoMan.RegisterUndo(UndoEdit()) | |
a023b456 | 169 | g.frame.SetModified() |
d14a1e28 | 170 | self.modified = value |
5cd66f07 | 171 | |
d14a1e28 RD |
172 | def Apply(self): |
173 | for p in self.pages: p.Apply() | |
174 | ||
175 | ################################################################################ | |
176 | ||
177 | # General class for notebook pages | |
29a41103 | 178 | class ParamPage(wx.Panel): |
c08eb1d3 | 179 | labelSize = None |
d14a1e28 | 180 | def __init__(self, parent, xxx): |
29a41103 | 181 | wx.Panel.__init__(self, parent, -1) |
d14a1e28 RD |
182 | self.xxx = xxx |
183 | # Register event handlers | |
184 | for id in paramIDs.values(): | |
29a41103 | 185 | wx.EVT_CHECKBOX(self, id, self.OnCheckParams) |
d14a1e28 RD |
186 | self.checks = {} |
187 | self.controls = {} # save python objects | |
188 | self.controlName = None | |
189 | ||
190 | def OnCheckParams(self, evt): | |
191 | xxx = self.xxx | |
192 | param = evt.GetEventObject().GetName() | |
193 | w = self.controls[param] | |
194 | w.Enable(True) | |
b372319f | 195 | objElem = xxx.node |
d14a1e28 RD |
196 | if evt.IsChecked(): |
197 | # Ad new text node in order of allParams | |
198 | w.SetValue('') # set empty (default) value | |
199 | w.SetModified() # mark as changed | |
200 | elem = g.tree.dom.createElement(param) | |
201 | # Some classes are special | |
202 | if param == 'font': | |
b372319f | 203 | xxx.params[param] = xxxParamFont(xxx.node, elem) |
d14a1e28 RD |
204 | elif param in xxxObject.bitmapTags: |
205 | xxx.params[param] = xxxParamBitmap(elem) | |
206 | else: | |
207 | xxx.params[param] = xxxParam(elem) | |
208 | # Find place to put new element: first present element after param | |
209 | found = False | |
a3c8bbc1 RD |
210 | if xxx.hasStyle: |
211 | paramStyles = xxx.allParams + xxx.styles | |
212 | else: | |
213 | paramStyles = xxx.allParams | |
d14a1e28 RD |
214 | for p in paramStyles[paramStyles.index(param) + 1:]: |
215 | # Content params don't have same type | |
216 | if xxx.params.has_key(p) and p != 'content': | |
217 | found = True | |
218 | break | |
219 | if found: | |
220 | nextTextElem = xxx.params[p].node | |
221 | objElem.insertBefore(elem, nextTextElem) | |
222 | else: | |
223 | objElem.appendChild(elem) | |
224 | else: | |
225 | # Remove parameter | |
226 | xxx.params[param].remove() | |
227 | del xxx.params[param] | |
228 | w.SetValue('') | |
7df24e78 | 229 | w.SetModified(False) # mark as not changed |
d14a1e28 RD |
230 | w.Enable(False) |
231 | # Set modified flag (provokes undo storing is necessary) | |
232 | panel.SetModified(True) | |
233 | def Apply(self): | |
234 | xxx = self.xxx | |
235 | if self.controlName: | |
236 | name = self.controlName.GetValue() | |
237 | if xxx.name != name: | |
238 | xxx.name = name | |
b372319f | 239 | xxx.node.setAttribute('name', name) |
d14a1e28 RD |
240 | for param, w in self.controls.items(): |
241 | if w.modified: | |
242 | paramObj = xxx.params[param] | |
243 | value = w.GetValue() | |
244 | if param in xxx.specials: | |
245 | xxx.setSpecial(param, value) | |
246 | else: | |
247 | paramObj.update(value) | |
538a0c0d | 248 | |
d14a1e28 RD |
249 | # Save current state |
250 | def SaveState(self): | |
251 | self.origChecks = map(lambda i: (i[0], i[1].GetValue()), self.checks.items()) | |
f2ccaa22 | 252 | self.origControls = map(lambda i: (i[0], i[1].GetValue(), i[1].enabled), |
d14a1e28 RD |
253 | self.controls.items()) |
254 | if self.controlName: | |
255 | self.origName = self.controlName.GetValue() | |
256 | # Return original values | |
257 | def GetState(self): | |
258 | if self.controlName: | |
259 | return (self.origChecks, self.origControls, self.origName) | |
260 | else: | |
261 | return (self.origChecks, self.origControls) | |
262 | # Set values from undo data | |
263 | def SetState(self, state): | |
264 | for k,v in state[0]: | |
265 | self.checks[k].SetValue(v) | |
266 | for k,v,e in state[1]: | |
267 | self.controls[k].SetValue(v) | |
268 | self.controls[k].Enable(e) | |
a023b456 RR |
269 | # Set all states to modified |
270 | if e and k in self.xxx.params: self.controls[k].modified = True | |
d14a1e28 RD |
271 | if self.controlName: |
272 | self.controlName.SetValue(state[2]) | |
273 | ||
274 | ################################################################################ | |
275 | ||
276 | # Panel for displaying properties | |
277 | class PropPage(ParamPage): | |
c08eb1d3 RR |
278 | renameDict = {'orient':'orientation', 'option':'proportion', |
279 | 'usenotebooksizer':'usesizer', 'dontattachtoframe':'dontattach', | |
280 | } | |
d14a1e28 RD |
281 | def __init__(self, parent, label, xxx): |
282 | ParamPage.__init__(self, parent, xxx) | |
29a41103 | 283 | self.box = wx.StaticBox(self, -1, label) |
289128a4 | 284 | self.box.SetFont(g.labelFont()) |
29a41103 | 285 | topSizer = wx.StaticBoxSizer(self.box, wx.VERTICAL) |
c08eb1d3 | 286 | sizer = wx.FlexGridSizer(len(xxx.allParams), 2, 1, 5) |
d14a1e28 RD |
287 | sizer.AddGrowableCol(1) |
288 | if xxx.hasName: | |
c08eb1d3 | 289 | label = wx.StaticText(self, -1, 'XML ID:', size=self.labelSize) |
d14a1e28 | 290 | control = ParamText(self, 'XML_name', 200) |
29a41103 RD |
291 | sizer.AddMany([ (label, 0, wx.ALIGN_CENTER_VERTICAL), |
292 | (control, 0, wx.ALIGN_CENTER_VERTICAL | wx.BOTTOM | wx.GROW, 10) ]) | |
d14a1e28 RD |
293 | self.controlName = control |
294 | for param in xxx.allParams: | |
295 | present = xxx.params.has_key(param) | |
c08eb1d3 | 296 | sParam = self.renameDict.get(param, param) |
d14a1e28 | 297 | if param in xxx.required: |
b372319f RR |
298 | if isinstance(xxx, xxxComment): |
299 | label = None | |
300 | else: | |
c08eb1d3 RR |
301 | label = wx.StaticText(self, paramIDs[param], sParam, |
302 | size = self.labelSize, name = param) | |
d14a1e28 | 303 | else: |
29a41103 | 304 | label = wx.CheckBox(self, paramIDs[param], sParam, |
c08eb1d3 | 305 | size = self.labelSize, name = param) |
d14a1e28 RD |
306 | self.checks[param] = label |
307 | try: | |
308 | typeClass = xxx.paramDict[param] | |
309 | except KeyError: | |
310 | try: | |
311 | # Standart type | |
312 | typeClass = paramDict[param] | |
313 | except KeyError: | |
314 | # Default | |
315 | typeClass = ParamText | |
316 | control = typeClass(self, param) | |
317 | control.Enable(present) | |
b372319f RR |
318 | # Comment has only one parameter |
319 | if isinstance(xxx, xxxComment): | |
fe295b0d RR |
320 | # Bind char event to check Enter key |
321 | control.text.Bind(wx.EVT_CHAR, self.OnEnter) | |
b372319f RR |
322 | sizer.Add(control, 0, wx.ALIGN_CENTER_VERTICAL | wx.GROW) |
323 | else: | |
324 | sizer.AddMany([ (label, 0, wx.ALIGN_CENTER_VERTICAL), | |
325 | (control, 0, wx.ALIGN_CENTER_VERTICAL | wx.GROW) ]) | |
d14a1e28 | 326 | self.controls[param] = control |
29a41103 | 327 | topSizer.Add(sizer, 1, wx.ALL | wx.EXPAND, 3) |
d14a1e28 RD |
328 | self.SetSizer(topSizer) |
329 | topSizer.Fit(self) | |
fe295b0d | 330 | |
d14a1e28 RD |
331 | def SetValues(self, xxx): |
332 | self.xxx = xxx | |
333 | self.origChecks = [] | |
334 | self.origControls = [] | |
335 | # Set values, checkboxes to False, disable defaults | |
336 | if xxx.hasName: | |
337 | self.controlName.SetValue(xxx.name) | |
338 | self.origName = xxx.name | |
339 | for param in xxx.allParams: | |
340 | w = self.controls[param] | |
341 | w.modified = False | |
342 | try: | |
343 | value = xxx.params[param].value() | |
344 | w.Enable(True) | |
345 | w.SetValue(value) | |
346 | if not param in xxx.required: | |
347 | self.checks[param].SetValue(True) | |
348 | self.origChecks.append((param, True)) | |
349 | self.origControls.append((param, value, True)) | |
350 | except KeyError: | |
ed650677 | 351 | # Optional param not present in xxx - set empty value |
d14a1e28 RD |
352 | self.checks[param].SetValue(False) |
353 | w.SetValue('') | |
354 | w.Enable(False) | |
355 | self.origChecks.append((param, False)) | |
356 | self.origControls.append((param, '', False)) | |
357 | ||
fe295b0d RR |
358 | # This is called only for comment now |
359 | def OnEnter(self, evt): | |
360 | if evt.GetKeyCode() == 13: | |
361 | g.tree.Apply(self.xxx, g.tree.selection) | |
362 | else: | |
363 | evt.Skip() | |
364 | ||
d14a1e28 RD |
365 | ################################################################################ |
366 | ||
367 | # Style notebook page | |
368 | class StylePage(ParamPage): | |
c08eb1d3 | 369 | renameDict = {'fg':'foreground', 'bg':'background'} |
d14a1e28 RD |
370 | def __init__(self, parent, label, xxx): |
371 | ParamPage.__init__(self, parent, xxx) | |
29a41103 | 372 | box = wx.StaticBox(self, -1, label) |
289128a4 | 373 | box.SetFont(g.labelFont()) |
29a41103 | 374 | topSizer = wx.StaticBoxSizer(box, wx.VERTICAL) |
c08eb1d3 | 375 | sizer = wx.FlexGridSizer(len(xxx.styles), 2, 1, 5) |
d14a1e28 RD |
376 | sizer.AddGrowableCol(1) |
377 | for param in xxx.styles: | |
378 | present = xxx.params.has_key(param) | |
c08eb1d3 | 379 | sParam = self.renameDict.get(param, param) |
29a41103 | 380 | check = wx.CheckBox(self, paramIDs[param], |
c08eb1d3 | 381 | sParam, size = self.labelSize, name = param) |
d14a1e28 RD |
382 | check.SetValue(present) |
383 | control = paramDict[param](self, name = param) | |
384 | control.Enable(present) | |
29a41103 RD |
385 | sizer.AddMany([ (check, 0, wx.ALIGN_CENTER_VERTICAL), |
386 | (control, 0, wx.ALIGN_CENTER_VERTICAL | wx.GROW) ]) | |
d14a1e28 RD |
387 | self.checks[param] = check |
388 | self.controls[param] = control | |
29a41103 | 389 | topSizer.Add(sizer, 1, wx.ALL | wx.EXPAND, 3) |
d14a1e28 RD |
390 | self.SetAutoLayout(True) |
391 | self.SetSizer(topSizer) | |
392 | topSizer.Fit(self) | |
fe295b0d | 393 | |
d14a1e28 RD |
394 | # Set data for a cahced page |
395 | def SetValues(self, xxx): | |
396 | self.xxx = xxx | |
397 | self.origChecks = [] | |
398 | self.origControls = [] | |
399 | for param in xxx.styles: | |
400 | present = xxx.params.has_key(param) | |
401 | check = self.checks[param] | |
402 | check.SetValue(present) | |
403 | w = self.controls[param] | |
404 | w.modified = False | |
405 | if present: | |
406 | value = xxx.params[param].value() | |
407 | else: | |
408 | value = '' | |
409 | w.SetValue(value) | |
410 | w.Enable(present) | |
411 | self.origChecks.append((param, present)) | |
412 | self.origControls.append((param, value, present)) | |
1fded56b | 413 |