Package library :: Package pyjamas :: Package ui :: Module Tree
[hide private]
[frames] | no frames]

Source Code for Module library.pyjamas.ui.Tree

  1  # Copyright 2006 James Tauber and contributors 
  2  # Copyright (C) 2009 Luke Kenneth Casson Leighton <lkcl@lkcl.net> 
  3  # Copyright (C) 2012 Robert Peters <robertpeters@winterlionsoftware.com> 
  4  # 
  5  # Licensed under the Apache License, Version 2.0 (the "License"); 
  6  # you may not use this file except in compliance with the License. 
  7  # You may obtain a copy of the License at 
  8  # 
  9  #     http://www.apache.org/licenses/LICENSE-2.0 
 10  # 
 11  # Unless required by applicable law or agreed to in writing, software 
 12  # distributed under the License is distributed on an "AS IS" BASIS, 
 13  # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
 14  # See the License for the specific language governing permissions and 
 15  # limitations under the License. 
 16  from pyjamas import DOM 
 17  from pyjamas import Factory 
 18  from __pyjamas__ import console 
 19  from sets import Set 
 20  import pygwt 
 21   
 22  from pyjamas.ui.Widget import Widget 
 23  from pyjamas.ui import Event 
 24  from pyjamas.ui import Focus 
 25  from pyjamas.ui.TreeItem import RootTreeItem, TreeItem 
 26  from pyjamas.ui import MouseListener 
 27  from pyjamas.ui import KeyboardListener 
 28  from pyjamas.ui import FocusListener 
 29   
30 -class Tree(Widget):
31 - def __init__(self, **ka):
32 ka['StyleName'] = ka.get('StyleName', "gwt-Tree") 33 34 self.root = None 35 self.childWidgets = Set() 36 self.curSelection = None 37 self.focusable = None 38 self.focusListeners = [] 39 self.mouseListeners = [] 40 self.images = ka.pop('Images', False) 41 self.imageBase = pygwt.getImageBaseURL(self.images) 42 self.keyboardListeners = [] 43 self.listeners = [] 44 self.lastEventType = "" 45 46 element = ka.pop('Element', None) or DOM.createDiv() 47 self.setElement(element) 48 DOM.setStyleAttribute(self.getElement(), "position", "relative") 49 self.focusable = Focus.createFocusable() 50 # Hide focus outline in Mozilla/Webkit/Opera 51 DOM.setStyleAttribute(self.focusable, "outline", "0px") 52 # Hide focus outline in IE 6/7 53 DOM.setElemAttribute(self.focusable, "hideFocus", "true"); 54 55 DOM.setStyleAttribute(self.focusable, "fontSize", "0") 56 DOM.setStyleAttribute(self.focusable, "position", "absolute") 57 DOM.setIntStyleAttribute(self.focusable, "zIndex", -1) 58 DOM.appendChild(self.getElement(), self.focusable) 59 60 self.root = RootTreeItem() 61 self.root.setTree(self) 62 63 Widget.__init__(self, **ka) 64 65 self.sinkEvents(Event.ONMOUSEDOWN | Event.ONCLICK | Event.KEYEVENTS) 66 DOM.sinkEvents(self.focusable, Event.FOCUSEVENTS)
67
68 - def add(self, widget):
69 self.addItem(widget)
70
71 - def addFocusListener(self, listener):
72 self.focusListeners.append(listener)
73
74 - def addItem(self, item):
75 return self.insertItem(item)
76
77 - def insertItem(self, item, index=None):
78 if isinstance(item, basestring): 79 item = TreeItem(item) 80 81 ret = self.root.addItem(item) 82 if index is None: 83 DOM.appendChild(self.getElement(), item.getElement()) 84 else: 85 DOM.insertChild(self.getElement(), item.getElement(), index) 86 87 return ret
88
89 - def addKeyboardListener(self, listener):
90 self.keyboardListeners.append(listener)
91
92 - def addMouseListener(self, listener):
93 self.mouseListeners.append(listener)
94
95 - def addTreeListener(self, listener):
96 self.listeners.append(listener)
97
98 - def clear(self):
99 size = self.root.getChildCount() 100 for i in range(size, 0, -1): 101 self.root.getChild(i-1).remove()
102
104 if self.curSelection is None: 105 return 106 107 parent = self.curSelection.getParentItem() 108 while parent is not None: 109 parent.setState(True) 110 parent = parent.getParentItem()
111
112 - def getImageBase(self):
113 return self.imageBase
114
115 - def getImages(self):
116 return self.images
117
118 - def getItem(self, index):
119 return self.root.getChild(index)
120
121 - def getItemCount(self):
122 return self.root.getChildCount()
123
124 - def getSelectedItem(self):
125 return self.curSelection
126
127 - def getTabIndex(self):
128 return Focus.getTabIndex(self.focusable)
129
130 - def __iter__(self):
131 return self.root.__iter__()
132
133 - def onBrowserEvent(self, event):
134 etype = DOM.eventGetType(event) 135 136 if etype == "click": 137 e = DOM.eventGetTarget(event) 138 if not self.shouldTreeDelegateFocusToElement(e) and \ 139 self.curSelection is not None: 140 self.setFocus(True) 141 elif etype in MouseListener.MOUSE_EVENTS: 142 if etype == "mousedown": 143 self.elementClicked(self.root, DOM.eventGetTarget(event)) 144 MouseListener.fireMouseEvent(self.mouseListeners, self, event) 145 elif etype == "blur" or etype == "focus": 146 FocusListener.fireFocusEvent(self.focusListeners, self, event) 147 elif etype == "keydown": 148 if self.curSelection is None: 149 if self.root.getChildCount() > 0: 150 self.onSelection(self.root.getChild(0), True) 151 Widget.onBrowserEvent(self, event) 152 return 153 154 if self.lastEventType == "keydown": 155 return 156 157 keycode = DOM.eventGetKeyCode(event) 158 if keycode == KeyboardListener.KEY_UP: 159 self.moveSelectionUp(self.curSelection, True) 160 DOM.eventPreventDefault(event) 161 elif keycode == KeyboardListener.KEY_DOWN: 162 self.moveSelectionDown(self.curSelection, True) 163 DOM.eventPreventDefault(event) 164 elif keycode == KeyboardListener.KEY_LEFT: 165 if self.curSelection.getState(): 166 self.curSelection.setState(False) 167 DOM.eventPreventDefault(event) 168 elif keycode == KeyboardListener.KEY_RIGHT: 169 if not self.curSelection.getState(): 170 self.curSelection.setState(True) 171 DOM.eventPreventDefault(event) 172 elif etype == "keyup": 173 if DOM.eventGetKeyCode(event) == KeyboardListener.KEY_TAB: 174 chain = [] 175 self.collectElementChain(chain, self.getElement(), 176 DOM.eventGetTarget(event)) 177 item = self.findItemByChain(chain, 0, self.root) 178 if item != self.getSelectedItem(): 179 self.setSelectedItem(item, True) 180 elif etype == "keypress": 181 KeyboardListener.fireKeyboardEvent(self.keyboardListeners, 182 self, event) 183 184 Widget.onBrowserEvent(self, event) 185 self.lastEventType = etype
186
187 - def remove(self, widget):
188 raise Exception("Widgets should never be directly removed from a tree")
189
190 - def removeFocusListener(self, listener):
191 self.focusListeners.remove(listener)
192
193 - def removeItem(self, item):
194 self.root.removeItem(item) 195 DOM.removeChild(self.getElement(), item.getElement())
196
197 - def removeItems(self):
198 while self.getItemCount() > 0: 199 self.removeItem(self.getItem(0))
200
201 - def removeKeyboardListener(self, listener):
202 self.keyboardListeners.remove(listener)
203
204 - def removeTreeListener(self, listener):
205 self.listeners.remove(listener)
206
207 - def setAccessKey(self, key):
208 Focus.setAccessKey(self.focusable, key)
209
210 - def setFocus(self, focus):
211 if focus: 212 Focus.focus(self.focusable) 213 else: 214 Focus.blur(self.focusable)
215
216 - def setImageBase(self, baseUrl):
217 self.imageBase = baseUrl 218 self.root.updateStateRecursive()
219
220 - def setImages(self, images):
221 if self.images == images: 222 return 223 self.images = images 224 self.setImageBase(pygwt.getImageBaseURL(self.images))
225
226 - def setSelectedItem(self, item, fireEvents=True):
227 if item is None: 228 if self.curSelection is None: 229 return 230 self.curSelection.setSelected(False) 231 self.curSelection = None 232 return 233 234 self.onSelection(item, fireEvents)
235
236 - def setTabIndex(self, index):
237 Focus.setTabIndex(self.focusable, index)
238
239 - def treeItemIterator(self):
240 accum = [] 241 self.root.addTreeItems(accum) 242 return accum.__iter__()
243
244 - def collectElementChain(self, chain, hRoot, hElem):
245 if (hElem is None) or DOM.compare(hElem, hRoot): 246 return 247 248 self.collectElementChain(chain, hRoot, DOM.getParent(hElem)) 249 chain.append(hElem)
250
251 - def elementClicked(self, root, hElem):
252 chain = [] 253 self.collectElementChain(chain, self.getElement(), hElem) 254 255 item = self.findItemByChain(chain, 0, root) 256 if item is not None: 257 if DOM.compare(item.getImageElement(), hElem): 258 item.setState(not item.getState(), True) 259 return True 260 elif DOM.isOrHasChild(item.getElement(), hElem): 261 self.onSelection(item, True) 262 return True 263 264 return False
265
266 - def findDeepestOpenChild(self, item):
267 if not item.getState(): 268 return item 269 return self.findDeepestOpenChild(item.getChild(item.getChildCount()-1))
270
271 - def findItemByChain(self, chain, idx, root):
272 if idx == len(chain): 273 return root 274 275 hCurElem = chain[idx] 276 for i in range(root.getChildCount()): 277 child = root.getChild(i) 278 if DOM.compare(child.getElement(), hCurElem): 279 retItem = self.findItemByChain(chain, idx + 1, root.getChild(i)) 280 if retItem is None: 281 return child 282 return retItem 283 284 return self.findItemByChain(chain, idx + 1, root)
285
286 - def moveFocus(self, selection):
287 focusableWidget = selection.getFocusableWidget() 288 if focusableWidget is not None: 289 focusableWidget.setFocus(True) 290 DOM.scrollIntoView(focusableWidget.getElement()) 291 else: 292 selectedElem = selection.getContentElem() 293 containerLeft = self.getAbsoluteLeft() 294 containerTop = self.getAbsoluteTop() 295 296 left = DOM.getAbsoluteLeft(selectedElem) - containerLeft 297 top = DOM.getAbsoluteTop(selectedElem) - containerTop 298 width = DOM.getIntAttribute(selectedElem, "offsetWidth") 299 height = DOM.getIntAttribute(selectedElem, "offsetHeight") 300 301 DOM.setIntStyleAttribute(self.focusable, "left", "%spx" % left) 302 DOM.setIntStyleAttribute(self.focusable, "top", "%spx" % top) 303 DOM.setIntStyleAttribute(self.focusable, "width", "%spx" % width) 304 DOM.setIntStyleAttribute(self.focusable, "height", "%spx" % height) 305 306 DOM.scrollIntoView(self.focusable) 307 Focus.focus(self.focusable)
308
309 - def moveSelectionDown(self, sel, dig):
310 if sel == self.root: 311 return 312 313 parent = sel.getParentItem() 314 if parent is None: 315 parent = self.root 316 idx = parent.getChildIndex(sel) 317 318 if not dig or not sel.getState(): 319 if idx < parent.getChildCount() - 1: 320 self.onSelection(parent.getChild(idx + 1), True) 321 else: 322 self.moveSelectionDown(parent, False) 323 elif sel.getChildCount() > 0: 324 self.onSelection(sel.getChild(0), True)
325
326 - def moveSelectionUp(self, sel, climb):
327 parent = sel.getParentItem() 328 if parent is None: 329 parent = self.root 330 idx = parent.getChildIndex(sel) 331 332 if idx > 0: 333 sibling = parent.getChild(idx - 1) 334 self.onSelection(self.findDeepestOpenChild(sibling), True) 335 else: 336 self.onSelection(parent, True)
337
338 - def onSelection(self, item, fireEvents):
339 if item == self.root: 340 return 341 342 if fireEvents and len(self.listeners): 343 for listener in self.listeners: 344 onBefore = getattr(listener, "onBeforeTreeItemSelected", None) 345 if onBefore is not None: 346 if not onBefore(item): 347 return 348 349 if self.curSelection is not None: 350 self.curSelection.setSelected(False) 351 352 self.curSelection = item 353 354 if self.curSelection is not None: 355 self.moveFocus(self.curSelection) 356 self.curSelection.setSelected(True) 357 if fireEvents and len(self.listeners): 358 for listener in self.listeners: 359 listener.onTreeItemSelected(item)
360
361 - def doAttachChildren(self):
362 for child in self: 363 child.onAttach() 364 DOM.setEventListener(self.focusable, self);
365
366 - def doDetachChildren(self):
367 for child in self: 368 child.onDetach() 369 DOM.setEventListener(self.focusable, None);
370
371 - def onLoad(self):
372 self.root.updateStateRecursive()
373
374 - def adopt(self, content):
375 self.childWidgets.add(content) 376 content.treeSetParent(self)
377
378 - def disown(self, item):
379 self.childWidgets.remove(item) 380 item.treeSetParent(None)
381
382 - def fireStateChanged(self, item):
383 for listener in self.listeners: 384 if hasattr(listener, "onTreeItemStateChanged"): 385 listener.onTreeItemStateChanged(item)
386
387 - def getChildWidgets(self):
388 return self.childWidgets
389
390 - def shouldTreeDelegateFocusToElement(self, elem):
391 name = str(elem.nodeName) 392 name = name.lower() 393 return name == 'select' or \ 394 name == 'input' or \ 395 name == 'checkbox'
396 397 Factory.registerClass('pyjamas.ui.Tree', 'Tree', Tree) 398