1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 import logging
17
18 from muntjac.ui.tree \
19 import ExpandEvent, IExpandListener, CollapseEvent, ICollapseListener, \
20 COLLAPSE_METHOD, EXPAND_METHOD
21
22 from muntjac.ui.treetable.hierarchical_container_ordered_wrapper \
23 import HierarchicalContainerOrderedWrapper
24
25 from muntjac.data.container import IOrdered, IHierarchical
26 from muntjac.terminal.gwt.client.ui.v_tree_table import VTreeTable
27 from muntjac.ui.table import Table
28
29 from muntjac.data.util.container_hierarchical_wrapper \
30 import ContainerHierarchicalWrapper
31
32 from muntjac.ui.treetable.collapsible import ICollapsible
33 from muntjac.data.util.hierarchical_container import HierarchicalContainer
34
35
36 logger = logging.getLogger(__name__)
37
38
40 """TreeTable extends the L{Table} component so that it can also visualize
41 a hierarchy of its Items in a similar manner that {@link Tree} does. The
42 tree hierarchy is always displayed in the first actual column of the
43 TreeTable.
44
45 The TreeTable supports the usual {@link Table} features like lazy loading,
46 so it should be no problem to display lots of items at once. Only required
47 rows and some cache rows are sent to the client.
48
49 TreeTable supports standard L{IHierarchical} container interfaces, but
50 also a more fine tuned version - L{ICollapsible}. A container
51 implementing the L{ICollapsible} interface stores the collapsed/expanded
52 state internally and can this way scale better on the server side than with
53 standard Hierarchical implementations. Developer must however note that
54 L{ICollapsible} containers can not be shared among several users as they
55 share UI state in the container.
56 """
57
58 - def __init__(self, caption=None, dataSource=None):
59 """Creates a TreeTable instance with optional captions and data source.
60
61 @param caption:
62 the caption for the component
63 @param dataSource:
64 the dataSource that is used to list items in the component
65 """
66 if dataSource is None:
67 dataSource = HierarchicalContainer()
68
69 super(TreeTable, self).__init__(caption, dataSource)
70
71
72 self._cStrategy = None
73 self._focusedRowId = None
74 self._hierarchyColumnId = None
75
76
77
78
79
80
81
82
83 self._toggledItemId = None
84 self._animationsEnabled = None
85 self._clearFocusedRowPending = None
86
87
95
96
105
106
117
118
120 super(TreeTable, self).changeVariables(source, variables)
121 if 'toggleCollapsed' in variables:
122 obj = variables.get('toggleCollapsed')
123 itemId = self.itemIdMapper.get(obj)
124 self._toggledItemId = itemId
125 self.toggleChildVisibility(itemId)
126 if 'selectCollapsed' in variables:
127
128
129 if self.isSelectable():
130 self.select(itemId)
131 elif 'focusParent' in variables:
132 key = variables.get('focusParent')
133 refId = self.itemIdMapper.get(key)
134 itemId = self.getParent(refId)
135 self.focusParent(itemId)
136
137
161
162
164 self._focusedRowId = itemId
165 if self._focusedRowId is None:
166
167
168 self._clearFocusedRowPending = True
169 self.requestRepaint()
170
171
172 - def paintContent(self, target):
173
174
175
176 if self._focusedRowId is not None:
177 row = self.itemIdMapper.key(self._focusedRowId)
178 target.addAttribute('focusedRow', row)
179 self._focusedRowId = None
180 elif self._clearFocusedRowPending:
181
182
183 target.addAttribute('clearFocusPending', True)
184 self._clearFocusedRowPending = False
185
186 target.addAttribute('animate', self._animationsEnabled)
187 if self._hierarchyColumnId is not None:
188 visibleColumns2 = self.getVisibleColumns()
189 for i in range(len(visibleColumns2)):
190 obj = visibleColumns2[i]
191 if self._hierarchyColumnId == obj:
192 ahci = VTreeTable.ATTRIBUTE_HIERARCHY_COLUMN_INDEX
193 target.addAttribute(ahci, i)
194 break
195
196 super(TreeTable, self).paintContent(target)
197 self._toggledItemId = None
198
199
201 return self._toggledItemId is not None
202
203
205 return self.indexOfId(self._toggledItemId) + 1
206
207
211
212
214 count = 0
215
216
217
218 if (self.getContainerStrategy().isNodeOpen(itemId)
219 or (itemId == self._toggledItemId)):
220 children = hc.getChildren(itemId)
221 if children is not None:
222 count += len(children) if children is not None else 0
223 for idd in children:
224 count += self.countSubNodesRecursively(hc, idd)
225 return count
226
227
229 return self.indexOfId(self._toggledItemId)
230
231
234
235
238
239
251
252
255
256
259
260
263
264
272
273
280
281
284
285
288
289
292
293
296
297
300
301
304
305
308
309
312
313
316
317
323
324
327
328
331
332
335
336
340
341
344
345
347 """Sets the Item specified by given identifier collapsed or expanded.
348 If the Item is collapsed, its children is not displayed in for the
349 user.
350
351 @param itemId:
352 the identifier of the Item
353 @param collapsed:
354 true if the Item should be collapsed, false if expanded
355 """
356 if self.isCollapsed(itemId) != collapsed:
357 self.toggleChildVisibility(itemId)
358
359
361 """Checks if Item with given identifier is collapsed in the UI.
362
363 @param itemId:
364 the identifier of the checked Item
365 @return: true if the Item with given id is collapsed
366 @see: L{ICollapsible.isCollapsed}
367 """
368 return not self.getContainerStrategy().isNodeOpen(itemId)
369
370
372 """Explicitly sets the column in which the TreeTable visualizes the
373 hierarchy. If hierarchyColumnId is not set, the hierarchy is visualized
374 in the first visible column.
375 """
376 self._hierarchyColumnId = hierarchyColumnId
377
378
380 """@return: the identifier of column into which the hierarchy will be
381 visualized or null if the column is not explicitly defined.
382 """
383 return self._hierarchyColumnId
384
385
387 """Adds an expand/collapse listener.
388
389 @param listener:
390 the Listener to be added.
391 """
392 if (isinstance(listener, ICollapseListener) and
393 (iface is None or issubclass(iface, ICollapseListener))):
394 self.registerListener(CollapseEvent,
395 listener, COLLAPSE_METHOD)
396
397 if (isinstance(listener, IExpandListener) and
398 (iface is None or issubclass(iface, IExpandListener))):
399 self.registerListener(ExpandEvent,
400 listener, EXPAND_METHOD)
401
402 super(TreeTable, self).addListener(listener, iface)
403
404
405 - def addCallback(self, callback, eventType=None, *args):
406 if eventType is None:
407 eventType = callback._eventType
408
409 if issubclass(eventType, CollapseEvent):
410 self.registerCallback(CollapseEvent, callback, None, *args)
411
412 elif issubclass(eventType, ExpandEvent):
413 self.registerCallback(ExpandEvent, callback, None, *args)
414
415 else:
416 super(TreeTable, self).addCallback(callback, eventType, *args)
417
418
420 """Removes an expand or collapselistener.
421
422 @param listener:
423 the Listener to be removed.
424 """
425 if (isinstance(listener, ICollapseListener) and
426 (iface is None or issubclass(iface, ICollapseListener))):
427 self.withdrawListener(CollapseEvent,
428 listener, COLLAPSE_METHOD)
429
430 if (isinstance(listener, IExpandListener) and
431 (iface is None or issubclass(iface, IExpandListener))):
432 self.withdrawListener(ExpandEvent,
433 listener, EXPAND_METHOD)
434
435 super(TreeTable, self).removeListener(listener, iface)
436
437
450
451
453 """Emits an expand event.
454
455 @param itemId:
456 the item id.
457 """
458 evt = ExpandEvent(self, itemId)
459 self.fireEvent(evt)
460
461
463 """Emits a collapse event.
464
465 @param itemId:
466 the item id.
467 """
468 evt = CollapseEvent(self, itemId)
469 self.fireEvent(evt)
470
471
473 """@return true if animations are enabled"""
474 return self._animationsEnabled
475
476
478 """Animations can be enabled by passing true to this method. Currently
479 expanding rows slide in from the top and collapsing rows slide out the
480 same way. NOTE! not supported in Internet Explorer 6 or 7.
481
482 @param animationsEnabled
483 true or false whether to enable animations or not.
484 """
485 self._animationsEnabled = animationsEnabled
486 self.requestRepaint()
487
488
490
492 raise NotImplementedError
493
496
498 raise NotImplementedError
499
501 raise NotImplementedError
502
504 raise NotImplementedError
505
507 raise NotImplementedError
508
510 raise NotImplementedError
511
513 raise NotImplementedError
514
516 raise NotImplementedError
517
519 raise NotImplementedError
520
522 raise NotImplementedError
523
525 raise NotImplementedError
526
528 raise NotImplementedError
529
530
532
535
536
538 """Consider adding getDepth to L{ICollapsible}, might help
539 scalability with some container implementations.
540 """
541 depth = 0
542 hierarchicalContainer = self._treetable.getContainerDataSource()
543 while not hierarchicalContainer.isRoot(itemId):
544 depth += 1
545 itemId = hierarchicalContainer.getParent(itemId)
546 return depth
547
548
551
552
554 """This strategy is used if current container implements L{Collapsible}.
555
556 Open-collapsed logic diverted to container, otherwise use default
557 implementations.
558 """
559
562
563
566
567
570
571
574
575
578
579
582
583
587
588
592
593
596
597
600
601
604
605
607 """Strategy for Hierarchical but not Collapsible container like
608 L{HierarchicalContainer}.
609
610 Store collapsed/open states internally, fool Table to use preorder when
611 accessing items from container via Ordered/Indexed methods.
612 """
613
619
620
622 return itemId in self._openItems
623
624
627
628
631
632
634 if itemId is None:
635 return False
636 return itemId == self.lastItemId()
637
638
644
645
658
659
670
671
673 if itemId in self._openItems:
674 self._openItems.remove(itemId)
675 removed = True
676 else:
677 removed = False
678
679 if not removed:
680 self._openItems.add(itemId)
681 logger.debug('Item ' + itemId + ' is now expanded')
682 else:
683 logger.debug('Item ' + itemId + ' is now collapsed')
684
685 self.clearPreorderCache()
686
687
689 self._preOrder = None
690
691
703
704
713
714
716 try:
717 return self.getPreOrder().index(idd)
718 except ValueError:
719 return -1
720
721
724
725
730