root/trunk/ide/ClassDesignerTreeSheet.py

Revision 3938, 12.7 kB (checked in by ed, 9 months ago)

Fixed a problem in which the tree selection was being returned as a single object instead of a tuple.

  • Property svn:eol-style set to native
Line 
1 # -*- coding: utf-8 -*-
2 import dabo
3 dabo.ui.loadUI("wx")
4 import dabo.dEvents as dEvents
5 from dabo.dLocalize import _
6 from dabo.ui import dKeys
7 from ClassDesignerComponents import LayoutPanel
8 from ClassDesignerComponents import LayoutBasePanel
9 from ClassDesignerComponents import LayoutSpacerPanel
10 from ClassDesignerComponents import LayoutSizer
11 from ClassDesignerComponents import LayoutGridSizer
12 from ClassDesignerComponents import NoSizerBasePanel
13 from DragHandle import DragHandle
14 import ClassDesignerMenu
15 from MenuBarPanel import MenuBarPanel
16 from MenuPanel import MenuPanel
17 from MenuDesignerComponents import SeparatorPanel
18 dui = dabo.ui
19 from dabo.ui import makeProxyProperty
20
21
22 class TreeSheet(dui.dPanel):
23     def _initProperties(self):
24         self.tree = None
25         return super(TreeSheet, self)._initProperties()
26        
27    
28     def _constructed(self):
29         return hasattr(self, "tree") and isinstance(self.tree, dui.dTreeView)
30        
31        
32     def afterInit(self):
33         self._slotCaption = _("Empty Sizer Slot")
34         self._spacerCaption = _("Spacer")
35         self.tree = dui.dTreeView(self, ShowButtons=True)
36         plat = self.Application.Platform
37         if plat == "Mac":
38             self.tree.FontSize -= 3
39         elif plat == "Win":
40             self.tree.FontSize += 1
41         else:
42             self.tree.FontSize -= 1
43
44         self.tree.bindEvent(dEvents.TreeSelection, self.onTreeSel)
45         self.tree.bindEvent(dEvents.TreeItemContextMenu,
46                 self.onTreeContextMenu)
47         self.tree.bindEvent(dEvents.MouseLeftDoubleClick, self.onTreeAction)
48         self.tree.bindKey("enter", self.onTreeAction)
49         self.tree.bindKey("numpad_enter", self.onTreeAction)
50 #       self.tree.bindEvent(dEvents.TreeBeginDrag, self.onTreeBeginDrag)
51 #       self.tree.bindEvent(dEvents.TreeEndDrag, self.onTreeEndDrag)
52         self.Sizer = dui.dSizer("v")
53         self.Sizer.append1x(self.tree)
54         # Flag for determining if the user or the app is selecting
55         self._inAppSelection = False
56    
57    
58     def onTreeAction(self, evt):
59         self.Form.hideTree()
60
61    
62 #   def onTreeBeginDrag(self, evt):
63 #       print "BEGIN DRAG"
64 #       print "ALLOWED?",evt._uiEvent.IsAllowed()
65 #       print evt.EventData
66 #       print evt.selectedCaption
67 #   
68 #   
69 #   def onTreeEndDrag(self, evt):
70 #       print "End DRAG"
71 #       print evt.EventData
72 #       print evt.selectedCaption
73
74        
75     def onTreeSel(self, evt):
76         if self._inAppSelection:
77             # Otherwise, this would be infinite recursion
78             return
79         dui.callAfter(self.Controller.treeSelect)
80
81
82     def onTreeContextMenu(self, evt):
83         evt.stop()
84         try:
85             obj = self.tree.find(evt.itemID)[0].Object
86             # See if there is a context menu for this object
87             menu = self.Controller.getTreeContextMenu(obj)
88             if menu:
89                 dabo.ui.callAfter(self.showContextMenu, menu)
90         except: pass
91        
92
93     def expandAll(self):
94         self.tree.expandAll()
95        
96    
97     def collapseAll(self):
98         self.tree.collapseAll()
99        
100        
101     def getSelection(self):
102         if self.MultipleSelect:
103             nds = self.tree.Selection
104             ret = []
105             for nd in nds:
106                 ob = nd.Object
107                 if ob not in ret:
108                     ret.append(ob)
109         else:
110             ret = self.tree.Selection.Object
111         return ret
112    
113    
114     def select(self, ctls):
115         """Iterate through the nodes, and set their Selected status
116         to match if they are in the current selection of controls.
117         """
118         if self._inAppSelection:
119             return
120         if not isinstance(ctls, (tuple, list)):
121             ctls = [ctls]
122         self._inAppSelection = True
123         selNodes = [nn for nn in self.tree.nodes
124                 if nn.Object in ctls]
125         self.tree.Selection = selNodes
126         self._inAppSelection = False
127        
128        
129     def priorObj(self):
130         """Return the next node up from the current selection"""
131         ret = None
132         nx = self.tree.priorNode()
133         if nx is not None:
134             ret = nx._object
135         return ret
136        
137        
138     def nextObj(self):
139         """Return the next node down from the current selection"""
140         ret = None
141         nx = self.tree.nextNode()
142         if nx is not None:
143             ret = nx._object
144         return ret
145        
146        
147     def getNodeFor(self, obj):
148         """Return the node whose Object property is the passed object."""
149         return self.tree.nodeForObject(obj)
150    
151    
152     def updateDisplay(self, frm):
153         """Constructs the tree for the form's layout."""
154         sel = self.tree.Selection
155         if sel:
156             if self.MultipleSelect:
157                 selObjs = [nn.Object for nn in sel]
158         # Preserve the expand/collapse state if possible.
159         expState = [(nn.Object, nn.Expanded) for nn in self.tree.nodes]
160        
161         self.tree.clear()
162         topObj = frm.getObjectHierarchy()[0][1]
163         self.recurseLayout(topObj, None)
164         self.tree.expandAll()
165         if sel:
166             if self.MultipleSelect:
167                 self.select(selObjs)
168             else:
169                 self.select(sel)
170         # Restore the expand/collapse state if possible.
171         for obj, expand in expState:
172             nn = self.tree.nodeForObject(obj)
173             if nn:
174                 nn.Expanded = expand
175        
176    
177     def updateNames(self, frm):
178         """Refreshes the object names without changing the layout."""
179         sel = self.tree.Selection
180         if not isinstance(sel, (list, tuple)):
181             sel = (sel, )
182         for nd in sel:
183             obj = nd.Object
184             nd.Caption = self._getDisplayName(obj)
185            
186            
187            
188     def _getDisplayName(self, obj):
189         """Create the name displayed on the tree for a given object."""
190         ret = str(obj)
191         if isinstance(obj, (dui.dSizer, dui.dBorderSizer, dui.dGridSizer)):
192             ornt = obj.Orientation
193             if ornt in ("r", "c"):
194                 ornt = {"r":"Row", "c":"Column"}[ornt]
195                 ret = _("Grid Sizer")
196             else:
197                 if isinstance(obj, dui.dBorderSizer):
198                     itmCap = obj.Caption
199                     if itmCap:
200                         ret = _("BorderSizer ('%s'): %s") % (itmCap, ornt)
201                     else:
202                         ret = _("BorderSizer: %s") % ornt
203                 else:
204                     ret = _("Sizer: %s") % ornt
205
206         elif isinstance(obj, LayoutSpacerPanel):
207             ret = self._slotCaption
208             if isinstance(obj.ControllingSizer, LayoutGridSizer):
209                 # Add the row,col info to the caption
210                 r, c = obj.ControllingSizer.getGridPos(obj)
211                 ret = "%s r:%s, c:%s" % (self._slotCaption, r, c)
212             else:
213                 ret = "%s - (%s)" % (self._spacerCaption, obj.Spacing)
214    
215         elif isinstance(obj, SeparatorPanel):
216             return " (Separator) "
217         else:
218             if hasattr(obj, "TreeDisplayCaption"):
219                 dsp = obj.TreeDisplayCaption
220                 if isinstance(dsp[1], type):
221                     dsp = (dsp[0], self._getClassName(dsp[1]))
222             elif isinstance(obj, dui.dColumn):
223                 dsp = "Column", obj.DataField
224             elif isinstance(obj, dui.dialogs.Wizard):
225                 dsp = "Wizard", obj.Caption
226             elif hasattr(obj, "Name"):
227                 dsp = (obj.Name, self._getClassName(obj._baseClass))
228             else:
229                 dsp = ("", self._getClassName(obj.__class__))
230             try:
231                 if isinstance(obj.ControllingSizer, LayoutGridSizer):
232                     r, c = obj.ControllingSizer.getGridPos(obj)
233                     dsp = ("%s r:%s, c:%s" % (dsp[0], r, c), dsp[1])
234             except: pass
235             ret = "%s (%s)" % dsp
236         return ret
237        
238    
239     def _getClassName(self, cls):
240         """Takes a string representation of the form:
241             <class 'dabo.ui.uiwx.dTextBox.dTextBox'>
242         and returns just the actual class name (i.e., in this
243         case, 'dTextBox').
244         """
245         ret = str(cls)
246         if ret.startswith("<class 'dabo."):
247             # Just include the class name
248             ret = ret.split("'")[1].split(".")[-1]
249         return ret
250        
251    
252     def onTreeItemContextMenu(self, evt):
253         print evt.itemID
254        
255        
256     def recurseLayout(self, itm, node, noDisplay=False, sz=None):
257         ## Is this good to do? Or am I masking problems?
258         if itm is None:
259             return
260
261         if isinstance(itm, (dui.dSizer, dui.dBorderSizer, dui.dGridSizer)):
262             if isinstance(itm.Parent, self.Controller.getFormClass()):
263                 noDisplay = True
264             if noDisplay:
265                 childNode = node
266             else:
267                 cap = self._getDisplayName(itm)
268                 childNode = node.appendChild(cap)
269                 childNode.Object = itm
270             if isinstance(itm, dui.dGridSizer):
271                 # Grid Sizer children are in the order they are added;
272                 # instead, get items into r,c order
273                 kids = [itm.getItemByRowCol(rr, cc, False)
274                         for rr in range(itm._rows)
275                         for cc in range(itm._cols)]
276             else:
277                 kids = itm.Children
278             for kid in kids:
279                 self.recurseLayout(kid, childNode, noDisplay=noDisplay, sz=itm)
280
281         elif isinstance(itm, (dabo.ui.dSizer.SizerItem,
282                 dabo.ui.dSizer.GridSizerItem)):
283             if itm.IsWindow():
284                 recurse = True
285                 noDisplay = False
286                 win = itm.GetWindow()
287                
288                 if isinstance(win, LayoutSpacerPanel):
289                     cap = self._getDisplayName(win)
290                     childNode = node.appendChild(cap)
291                     childNode.Object = win
292
293                 elif isinstance(win, LayoutPanel):
294                     cap = self._getDisplayName(win)
295                     sz = win.Sizer
296                     if sz is None:
297                         # Empty slot; display it in the tree.
298                         childNode = node.appendChild(cap)               
299                         childNode.Object = win
300                         recurse = False
301                     if isinstance(sz, LayoutSizer) and sz.SlotCount == 0:
302                         # Empty slot; display it in the tree.
303                         childNode = node.appendChild(cap)               
304                         childNode.Object = win
305                     else:
306                         childNode = node
307                     if hasattr(win, "_hideInTree"):
308                         noDisplay = win._hideInTree
309                 elif isinstance(win, LayoutBasePanel):
310                     childNode = node
311                     noDisplay = True
312                 else:
313                     # A non-ClassDesigner control
314                     childNode = node
315                 if recurse:
316                     self.recurseLayout(win, childNode, noDisplay=noDisplay)
317                    
318             elif itm.IsSizer():
319                 sz = itm.GetSizer()
320                 childNode = node
321                 self.recurseLayout(sz, childNode)
322         else:
323             # Not a sizer; see if it an empty slot, an actual control,
324             # a sub-sizer, the form's Status Bar, or some other child
325             # form such as the PropSheet.
326             if isinstance(itm, (dui.dStatusBar, dabo.ui.nativeScrollBar,
327                     DragHandle)):
328                 # ignore
329                 return
330             elif isinstance(itm, (dui.dForm, dui.dToolForm,
331                     dui.dDialog)) and node is not None:
332                 # This is a child form; ignore it
333                 return
334             elif itm.__module__.startswith("wx"):
335                 # A native wx control; skip it
336                 return
337             elif isinstance(itm, LayoutPanel) and not isinstance(itm.Parent, dui.dialogs.WizardPage):
338                 if itm.Sizer:
339                     if itm.Sizer.Children:
340                         self.recurseLayout(itm.Sizer, node, noDisplay=noDisplay)
341                         hasSizer = True
342                 return
343             elif isinstance(itm, NoSizerBasePanel):
344                 self._recurseChildren(itm.Children, node, noDisplay=False)
345
346             elif isinstance(itm, LayoutBasePanel):
347                 return self.recurseLayout(itm.Sizer, node, noDisplay=False)
348                
349             cap = self._getDisplayName(itm)
350             if hasattr(itm, "_hideInTree"):
351                 if itm._hideInTree:
352                     # Don't continue to drill into object
353                     return             
354             if noDisplay:
355                 childNode = node
356             else:
357                 if node is None:
358                     self.tree.clear()
359                     childNode = self.tree.setRootNode(cap)
360                 else:
361                     childNode = node.appendChild(cap)
362                 childNode.Object = itm
363
364             if not isinstance(itm, (SeparatorPanel, MenuPanel, MenuBarPanel)):
365                 if hasattr(itm, "Sizer") and itm.Sizer:
366                     if isinstance(itm, dui.dialogs.WizardPage):
367                         self._recurseChildren(itm.Children, childNode, noDisplay)
368                         return
369                     if not isinstance(itm, (dui.dPageFrameNoTabs, dui.dRadioList,
370                             dui.dialogs.Wizard, dui.dialogs.WizardPage)):
371                         self.recurseLayout(itm.Sizer, childNode, noDisplay=noDisplay)
372             if isinstance(itm, dui.dGrid):
373                 children = itm.Columns
374             elif isinstance(itm, dui.dTreeView):
375                 # Can change this to BaseNode property post-0.7
376                 children = itm.BaseNodes
377             elif isinstance(itm, (dui.dComboBox, dui.dSpinner,
378                     dui.dListControl, dui.dRadioList)):
379                 # These compound controls don't need their parts listed
380                 children = None
381             elif isinstance(itm, SeparatorPanel):
382                 children = None
383             else:
384                 try:
385                     children = itm.Children
386                 except:
387                     children = None
388             if children:
389                 self._recurseChildren(children, childNode, noDisplay)
390            
391            
392     def _recurseChildren(self, children, childNode, noDisplay):
393         for chil in children:
394             if chil is self:
395                 continue
396             # BorderSizers add dBox instances to the parent object. They
397             # mark these with a '_belongsToBorderSizer' property. We
398             # want to skip them here.
399             if isinstance(chil, dui.dBox):
400                 if hasattr(chil, "_belongsToBorderSizer"):
401                     # Skip it
402                     continue
403             # See if it has already been added via recursive calls
404             chilNode = self.getNodeFor(chil)
405             if chilNode is None:
406                 # Child item wasn't already listed by the sizer
407                 noDisplay = False
408                 self.recurseLayout(chil, childNode, noDisplay=noDisplay)
409
410
411     def _getController(self):
412         try:
413             return self._controller
414         except AttributeError:
415             self._controller = self.Application
416             return self._controller
417
418     def _setController(self, val):
419         if self._constructed():
420             self._controller = val
421         else:
422             self._properties["Controller"] = val
423
424
425 #   def _getMultipleSelect(self):
426 #       try:
427 #           ret = self._multipleSelect
428 #       except AttributeError:
429 #           ret = self._multipleSelect = True
430 #       return ret
431 #
432 #   def _setMultipleSelect(self, val):
433 #       if self._constructed():
434 #           self._multipleSelect = val
435 #           try:
436 #               self.tree.MultipleSelect = val
437 #           except AttributeError:
438 #               # tree isn't constructed yet
439 #               dabo.ui.setAfter(self.tree, "MultipleSelect", val)
440 #       else:
441 #           self._properties["MultipleSelect"] = val
442
443
444     Controller = property(_getController, _setController, None,
445             _("Object to which this one reports events  (object (varies))"))
446
447 #   MultipleSelect = property(_getMultipleSelect, _setMultipleSelect, None,
448 #           _("Determines if the tree supports multiple selection  (bool)"))
449    
450     _proxyDict = {}
451     MultipleSelect = makeProxyProperty(_proxyDict, "MultipleSelect", "tree", )
Note: See TracBrowser for help on using the browser.