1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 """Used to select an item (or multiple items) from a hierarchical set of
17 items."""
18
19 from collections import deque
20
21 from muntjac.util import clsname, OrderedSet
22 from muntjac.terminal.key_mapper import KeyMapper
23 from muntjac.terminal.gwt.client.mouse_event_details import MouseEventDetails
24
25 from muntjac.data import container
26
27 from muntjac.event import action
28 from muntjac.event.data_bound_transferable import DataBoundTransferable
29 from muntjac.event.dd.acceptcriteria.target_detail_is import TargetDetailIs
30 from muntjac.event.dd.drop_target import IDropTarget
31 from muntjac.event.dd.drag_source import IDragSource
32
33 from muntjac.event.dd.acceptcriteria.server_side_criterion import \
34 ServerSideCriterion
35
36 from muntjac.event.dd.acceptcriteria.client_side_criterion import \
37 ClientSideCriterion
38
39 from muntjac.event.item_click_event import \
40 ItemClickEvent, IItemClickNotifier, IItemClickSource, ITEM_CLICK_METHOD,\
41 IItemClickListener
42
43 from muntjac.ui.component import Event as ComponentEvent
44
45 from muntjac.ui.abstract_select import \
46 AbstractSelect, MultiSelectMode, AbstractSelectTargetDetails
47
48 from muntjac.terminal.gwt.client.ui.v_tree import \
49 VTree
50
51 from muntjac.terminal.gwt.client.ui.dd.vertical_drop_location import \
52 VerticalDropLocation
53
54 from muntjac.data.util.container_hierarchical_wrapper import \
55 ContainerHierarchicalWrapper
56
57 from muntjac.data.util.indexed_container import IndexedContainer
58
59
60 -class Tree(AbstractSelect, container.IHierarchical, action.IContainer,
61 IItemClickSource, IItemClickNotifier, IDragSource, IDropTarget):
62 """Tree component. A Tree can be used to select an item (or multiple
63 items) from a hierarchical set of items.
64
65 @author: Vaadin Ltd.
66 @author: Richard Lincoln
67 @version: 1.1.2
68 """
69
70 CLIENT_WIDGET = None
71
72 - def __init__(self, caption=None, dataSource=None):
73 """Creates a new tree with caption and connect it to a IContainer.
74 """
75
76 self._expanded = set()
77
78
79 self._actionHandlers = None
80
81
82 self._actionMapper = None
83
84
85 self._selectable = True
86
87
88 self._partialUpdate = False
89
90
91 self._expandedItemId = None
92
93
94
95 self._initialPaint = True
96
97
98 self._itemDescriptionGenerator = None
99
100 self._dragMode = TreeDragMode.NONE
101 self._multiSelectMode = MultiSelectMode.DEFAULT
102
103 super(Tree, self).__init__()
104
105 if caption is not None:
106 self.setCaption(caption)
107
108 if dataSource is not None:
109 self.setContainerDataSource(dataSource)
110
111 self._itemStyleGenerator = None
112 self._dropHandler = None
113
114
115
117 """Check is an item is expanded
118
119 @param itemId:
120 the item id.
121 @return: true iff the item is expanded.
122 """
123 return itemId in self._expanded
124
125
126 - def expandItem(self, itemId, sendChildTree=None):
127 """Expands an item.
128
129 @param itemId:
130 the item id.
131 @param sendChildTree:
132 flag to indicate if client needs subtree or not (may
133 be cached)
134 @return: True iff the expand operation succeeded
135 """
136 if sendChildTree is None:
137 success = self.expandItem(itemId, True)
138 self.requestRepaint()
139 return success
140 else:
141
142 if self.isExpanded(itemId):
143 return True
144
145
146 if not self.areChildrenAllowed(itemId):
147 return False
148
149
150 self._expanded.add(itemId)
151
152 self._expandedItemId = itemId
153 if self._initialPaint:
154 self.requestRepaint()
155 elif sendChildTree:
156 self.requestPartialRepaint()
157
158 self.fireExpandEvent(itemId)
159
160 return True
161
162
166
167
171
172
174 """Expands the items recursively
175
176 Expands all the children recursively starting from an item.
177 Operation succeeds only if all expandable items are expanded.
178
179 @return: True iff the expand operation succeeded
180 """
181 result = True
182
183
184 todo = deque()
185 todo.append(startItemId)
186
187 while len(todo) > 0:
188 idd = todo.pop()
189 if (self.areChildrenAllowed(idd)
190 and not self.expandItem(idd, False)):
191 result = False
192 if self.hasChildren(idd):
193 for c in self.getChildren(idd):
194 todo.append(c)
195
196 self.requestRepaint()
197 return result
198
199
201 """Collapses an item.
202
203 @param itemId:
204 the item id.
205 @return: True iff the collapse operation succeeded
206 """
207
208 if not self.isExpanded(itemId):
209 return True
210
211
212 self._expanded.remove(itemId)
213 self.requestRepaint()
214 self.fireCollapseEvent(itemId)
215 return True
216
217
219 """Collapses the items recursively.
220
221 Collapse all the children recursively starting from an item.
222 Operation succeeds only if all expandable items are collapsed.
223
224 @return: True iff the collapse operation succeeded
225 """
226 result = True
227
228
229 todo = deque()
230 todo.append(startItemId)
231
232
233 while len(todo) > 0:
234 idd = todo.pop()
235 if self.areChildrenAllowed(idd) and not self.collapseItem(idd):
236 result = False
237 if self.hasChildren(idd):
238 for c in self.getChildren(idd):
239 todo.append(c)
240
241 return result
242
243
245 """Returns the current selectable state. Selectable determines if the
246 a node can be selected on the client side. Selectable does not affect
247 L{setValue} or L{select}.
248
249 The tree is selectable by default.
250
251 @return: the current selectable state.
252 """
253 return self._selectable
254
255
257 """Sets the selectable state. Selectable determines if the a node can
258 be selected on the client side. Selectable does not affect L{setValue}
259 or L{select}.
260
261 The tree is selectable by default.
262
263 @param selectable:
264 The new selectable state.
265 """
266 if self._selectable != selectable:
267 self._selectable = selectable
268 self.requestRepaint()
269
270
272 """Sets the behavior of the multiselect mode
273
274 @param mode:
275 The mode to set
276 """
277 if self._multiSelectMode != mode and mode is not None:
278 self._multiSelectMode = mode
279 self.requestRepaint()
280
281
283 """Returns the mode the multiselect is in. The mode controls
284 how multiselection can be done.
285
286 @return: The mode
287 """
288 return self._multiSelectMode
289
290
292 if 'clickedKey' in variables:
293 key = variables.get('clickedKey')
294
295 idd = self.itemIdMapper.get(key)
296 evt = variables.get('clickEvent')
297 details = MouseEventDetails.deSerialize(evt)
298 item = self.getItem(idd)
299 if item is not None:
300 event = ItemClickEvent(self, item, idd, None, details)
301 self.fireEvent(event)
302
303 if not self.isSelectable() and 'selected' in variables:
304
305
306 variables = dict(variables)
307 del variables['selected']
308
309
310 if 'collapse' in variables:
311 keys = variables.get('collapse')
312 for key in keys:
313 idd = self.itemIdMapper.get(key)
314 if idd is not None and self.isExpanded(idd):
315 self._expanded.remove(idd)
316 self.fireCollapseEvent(idd)
317
318
319 if 'expand' in variables:
320 sendChildTree = False
321 if 'requestChildTree' in variables:
322 sendChildTree = True
323
324 keys = variables.get('expand')
325 for key in keys:
326 idd = self.itemIdMapper.get(key)
327 if idd is not None:
328 self.expandItem(idd, sendChildTree)
329
330
331
332 if ('selected' in variables
333 and self.isMultiSelect()
334 and self._multiSelectMode == MultiSelectMode.DEFAULT):
335 self.handleSelectedItems(variables)
336 variables = dict(variables)
337 del variables['selected']
338
339
340 super(Tree, self).changeVariables(source, variables)
341
342
343 if 'action' in variables:
344 st = variables.get('action').split(',')
345 if len(st) == 2:
346 itemId = self.itemIdMapper.get(st[0].strip())
347 action = self._actionMapper.get(st[1].strip())
348 if (action is not None
349 and ((itemId is None) or self.containsId(itemId))
350 and self._actionHandlers is not None):
351 for ah in self._actionHandlers:
352 ah.handleAction(action, self, itemId)
353
354
381
382
383 - def paintContent(self, target):
384 """Paints any needed component-specific things to the given UIDL
385 stream.
386
387 @see: L{AbstractComponent.paintContent}
388 """
389 self._initialPaint = False
390
391 if self._partialUpdate:
392 target.addAttribute('partialUpdate', True)
393 target.addAttribute('rootKey',
394 self.itemIdMapper.key(self._expandedItemId))
395 else:
396 self.getCaptionChangeListener().clear()
397
398
399 if self.getTabIndex() > 0:
400 target.addAttribute('tabindex', self.getTabIndex())
401
402
403 if self.isSelectable():
404 if self.isMultiSelect():
405 target.addAttribute('selectmode', 'multi')
406 else:
407 target.addAttribute('selectmode', 'single')
408
409 if self.isMultiSelect():
410 try:
411 idx = MultiSelectMode.values().index(
412 self._multiSelectMode)
413 except ValueError:
414 idx = -1
415 target.addAttribute('multiselectmode', idx)
416 else:
417 target.addAttribute('selectmode', 'none')
418
419 if self.isNewItemsAllowed():
420 target.addAttribute('allownewitem', True)
421
422 if self.isNullSelectionAllowed():
423 target.addAttribute('nullselect', True)
424
425 if self._dragMode != TreeDragMode.NONE:
426 target.addAttribute('dragMode',
427 TreeDragMode.ordinal(self._dragMode))
428
429
430 actionSet = OrderedSet()
431
432
433 selectedKeys = list()
434 expandedKeys = list()
435
436
437 iteratorStack = deque()
438 if self._partialUpdate:
439 ids = self.getChildren(self._expandedItemId)
440 else:
441 ids = self.rootItemIds()
442
443 if ids is not None:
444 iteratorStack.append( iter(ids) )
445
446
447
448 if self._actionHandlers is not None:
449 keys = list()
450 for ah in self._actionHandlers:
451
452
453
454 aa = ah.getActions(None, self)
455 if aa is not None:
456 for ai in range(len(aa)):
457 akey = self._actionMapper.key(aa[ai])
458 actionSet.add(aa[ai])
459 keys.append(akey)
460
461 target.addAttribute('alb', keys)
462
463 while len(iteratorStack) > 0:
464
465
466 i = iteratorStack[-1]
467
468 try:
469
470 itemId = i.next()
471
472
473 isNode = self.areChildrenAllowed(itemId)
474 if isNode:
475 target.startTag('node')
476 else:
477 target.startTag('leaf')
478
479 if self._itemStyleGenerator is not None:
480 stylename = self._itemStyleGenerator.getStyle(itemId)
481 if stylename is not None:
482 target.addAttribute('style', stylename)
483
484 if self._itemDescriptionGenerator is not None:
485 description = self._itemDescriptionGenerator\
486 .generateDescription(self, itemId, None)
487 if description is not None and description != "":
488 target.addAttribute("descr", description)
489
490
491 target.addAttribute('caption', self.getItemCaption(itemId))
492 icon = self.getItemIcon(itemId)
493 if icon is not None:
494 target.addAttribute('icon', self.getItemIcon(itemId))
495
496 key = self.itemIdMapper.key(itemId)
497 target.addAttribute('key', key)
498 if self.isSelected(itemId):
499 target.addAttribute('selected', True)
500 selectedKeys.append(key)
501
502 if self.areChildrenAllowed(itemId) and self.isExpanded(itemId):
503 target.addAttribute('expanded', True)
504 expandedKeys.append(key)
505
506
507 self.getCaptionChangeListener().addNotifierForItem(itemId)
508
509
510 if self._actionHandlers is not None:
511 keys = list()
512 ahi = iter(self._actionHandlers)
513 while True:
514 try:
515 aa = ahi.next().getActions(itemId, self)
516 if aa is not None:
517 for ai in range(len(aa)):
518 akey = self._actionMapper.key(aa[ai])
519 actionSet.add(aa[ai])
520 keys.append(akey)
521 except StopIteration:
522 break
523 target.addAttribute('al', keys)
524
525
526 if (self.isExpanded(itemId)
527 and self.hasChildren(itemId)
528 and self.areChildrenAllowed(itemId)):
529 iteratorStack.append( iter(self.getChildren(itemId)) )
530 elif isNode:
531 target.endTag('node')
532 else:
533 target.endTag('leaf')
534
535
536 except StopIteration:
537
538 iteratorStack.pop()
539
540
541 if len(iteratorStack) > 0:
542 target.endTag('node')
543
544
545 if len(actionSet) > 0:
546 target.addVariable(self, 'action', '')
547 target.startTag('actions')
548 i = actionSet
549 for a in actionSet:
550 target.startTag('action')
551 if a.getCaption() is not None:
552 target.addAttribute('caption', a.getCaption())
553
554 if a.getIcon() is not None:
555 target.addAttribute('icon', a.getIcon())
556
557 target.addAttribute('key', self._actionMapper.key(a))
558 target.endTag('action')
559
560 target.endTag('actions')
561
562 if self._partialUpdate:
563 self._partialUpdate = False
564 else:
565
566 target.addVariable(self, 'selected', selectedKeys)
567
568
569 target.addVariable(self, 'expand', list())
570 target.addVariable(self, 'collapse', list())
571
572
573 target.addVariable(self, 'newitem', list())
574
575 if self._dropHandler is not None:
576 self._dropHandler.getAcceptCriterion().paint(target)
577
578
580 """Tests if the Item with given ID can have any children.
581
582 @see: L{IHierarchical.areChildrenAllowed}
583 """
584 return self.items.areChildrenAllowed(itemId)
585
586
588 """Gets the IDs of all Items that are children of the specified Item.
589
590 @see: L{IHierarchical.getChildren}
591 """
592 return self.items.getChildren(itemId)
593
594
596 """Gets the ID of the parent Item of the specified Item.
597
598 @see: L{IHierarchical.getParent}
599 """
600 if itemId is not None:
601 return self.items.getParent(itemId)
602 else:
603 return super(Tree, self).getParent()
604
605
607 """Tests if the Item specified with C{itemId} has child
608 Items.
609
610 @see: L{IHierarchical.hasChildren}
611 """
612 return self.items.hasChildren(itemId)
613
614
616 """Tests if the Item specified with C{itemId} is a root
617 Item.
618
619 @see: L{IHierarchical.isRoot}
620 """
621 return self.items.isRoot(itemId)
622
623
625 """Gets the IDs of all Items in the container that don't have a
626 parent.
627
628 @see: L{IHierarchical.rootItemIds}
629 """
630 return self.items.rootItemIds()
631
632
634 """Sets the given Item's capability to have children.
635
636 @see: L{IHierarchical.setChildrenAllowed}
637 """
638 success = self.items.setChildrenAllowed(itemId, areChildrenAllowed)
639 if success:
640 self.requestRepaint()
641
642 return success
643
644
645 - def setParent(self, itemId, newParentId=None):
646 if newParentId is not None:
647 success = self.items.setParent(itemId, newParentId)
648 if success:
649 self.requestRepaint()
650
651 return success
652 else:
653 parent = itemId
654 super(Tree, self).setParent(parent)
655
656
674
675
677 """Adds the expand/collapse listener.
678
679 @param listener:
680 the listener to be added.
681 """
682 if (isinstance(listener, ICollapseListener) and
683 (iface is None or issubclass(iface, ICollapseListener))):
684 self.registerListener(CollapseEvent,
685 listener, COLLAPSE_METHOD)
686
687 if (isinstance(listener, IExpandListener) and
688 (iface is None or issubclass(iface, IExpandListener))):
689 self.registerListener(ExpandEvent,
690 listener, EXPAND_METHOD)
691
692 if (isinstance(listener, IItemClickListener) and
693 (iface is None or issubclass(iface, IItemClickListener))):
694 self.registerListener(VTree.ITEM_CLICK_EVENT_ID,
695 ItemClickEvent, listener, ITEM_CLICK_METHOD)
696
697 super(Tree, self).addListener(listener, iface)
698
699
700 - def addCallback(self, callback, eventType=None, *args):
701 if eventType is None:
702 eventType = callback._eventType
703
704 if issubclass(eventType, CollapseEvent):
705 self.registerCallback(CollapseEvent, callback, None, *args)
706
707 elif issubclass(eventType, ExpandEvent):
708 self.registerCallback(ExpandEvent, callback, None, *args)
709
710 elif issubclass(eventType, ItemClickEvent):
711 self.registerCallback(ItemClickEvent, callback,
712 VTree.ITEM_CLICK_EVENT_ID, *args)
713
714 else:
715 super(Tree, self).addCallback(callback, eventType, *args)
716
717
719 """Removes the expand/collapse listener.
720
721 @param listener:
722 the listener to be removed.
723 """
724 if (isinstance(listener, ICollapseListener) and
725 (iface is None or issubclass(iface, ICollapseListener))):
726 self.withdrawListener(CollapseEvent,
727 listener, COLLAPSE_METHOD)
728
729 if (isinstance(listener, IExpandListener) and
730 (iface is None or issubclass(iface, IExpandListener))):
731 self.withdrawListener(ExpandEvent,
732 listener, EXPAND_METHOD)
733
734 if (isinstance(listener, IItemClickListener) and
735 (iface is None or issubclass(iface, IItemClickListener))):
736 self.withdrawListener(VTree.ITEM_CLICK_EVENT_ID,
737 ItemClickEvent, listener)
738
739 super(Tree, self).removeListener(listener, iface)
740
741
758
759
768
769
778
779
781 """Adds an action handler.
782
783 @see: L{IContainer.addActionHandler}
784 """
785 if actionHandler is not None:
786 if self._actionHandlers is None:
787 self._actionHandlers = list()
788 self._actionMapper = KeyMapper()
789
790 if actionHandler not in self._actionHandlers:
791 self._actionHandlers.append(actionHandler)
792 self.requestRepaint()
793
794
796 """Removes an action handler.
797
798 @see: L{IContainer.removeActionHandler}
799 """
800 if (self._actionHandlers is not None
801 and actionHandler in self._actionHandlers):
802 self._actionHandlers.remove(actionHandler)
803
804 if len(self._actionHandlers) > 0:
805 self._actionHandlers = None
806 self._actionMapper = None
807
808 self.requestRepaint()
809
810
812 """Removes all action handlers"""
813 self._actionHandlers = None
814 self._actionMapper = None
815 self.requestRepaint()
816
817
819 """Gets the visible item ids.
820
821 @see: L{Select.getVisibleItemIds}
822 """
823 visible = list()
824
825 iteratorStack = deque()
826 ids = self.rootItemIds()
827 if ids is not None:
828 iteratorStack.append(ids)
829 while len(iteratorStack) > 0:
830
831 i = iter( iteratorStack[-1] )
832
833
834 try:
835 itemId = i.next()
836 visible.append(itemId)
837
838 if self.isExpanded(itemId) and self.hasChildren(itemId):
839 iteratorStack.append( self.getChildren(itemId) )
840 except StopIteration:
841
842
843 iteratorStack.pop()
844
845 return visible
846
847
849 """Tree does not support C{setNullSelectionItemId}.
850
851 @see: L{AbstractSelect.setNullSelectionItemId}
852 """
853 if nullSelectionItemId is not None:
854 raise NotImplementedError
855
856
858 """Adding new items is not supported.
859
860 @raise NotImplementedError:
861 if set to true.
862 @see: L{Select.setNewItemsAllowed}
863 """
864 if allowNewOptions:
865 raise NotImplementedError
866
867
869 """Tree does not support lazy options loading mode. Setting this
870 true will throw NotImplementedError.
871
872 @see: L{Select.setLazyLoading}
873 """
874 if useLazyLoading:
875 raise NotImplementedError, \
876 'Lazy options loading is not supported by Tree.'
877
878
880 """Sets the L{IItemStyleGenerator} to be used with this tree.
881
882 @param itemStyleGenerator:
883 item style generator or null to remove generator
884 """
885 if self._itemStyleGenerator != itemStyleGenerator:
886 self._itemStyleGenerator = itemStyleGenerator
887 self.requestRepaint()
888
889
891 """@return: the current L{IItemStyleGenerator} for this tree.
892 C{None} if L{IItemStyleGenerator} is not set.
893 """
894 return self._itemStyleGenerator
895
896
899
900
902 return self._dropHandler
903
904
906 self._dropHandler = dropHandler
907
908
911
912
913 - def key(self, itemId):
914 """Helper API for L{TreeDropCriterion}
915 """
916 return self.itemIdMapper.key(itemId)
917
918
920 """Sets the drag mode that controls how Tree behaves as a
921 L{IDragSource}.
922 """
923 self._dragMode = dragMode
924 self.requestRepaint()
925
926
928 """@return: the drag mode that controls how Tree behaves as a
929 L{IDragSource}.
930
931 @see: L{TreeDragMode}
932 """
933 return self._dragMode
934
935
945
946
948 """Set the item description generator which generates tooltips for
949 the tree items
950
951 @param generator:
952 The generator to use or null to disable
953 """
954 if generator != self._itemDescriptionGenerator:
955 self._itemDescriptionGenerator = generator
956 self.requestRepaint()
957
958
960 """Get the item description generator which generates tooltips for
961 tree items.
962 """
963 return self._itemDescriptionGenerator
964
967 """Supported drag modes for Tree."""
968
969
970
971
972 NONE = 'NONE'
973
974
975
976 NODE = 'NODE'
977
978 _values = [NONE, NODE]
979
980 @classmethod
982 return cls._enum_values[:]
983
984 @classmethod
987
990 """Event to fired when a node is expanded. ExapandEvent is fired when a
991 node is to be expanded. it can me used to dynamically fill the sub-nodes
992 of the node.
993
994 @author: Vaadin Ltd.
995 @author: Richard Lincoln
996 @version: 1.1.2
997 """
998
999 - def __init__(self, source, expandedItemId):
1000 """New instance of options change event
1001
1002 @param source:
1003 the Source of the event.
1004 @param expandedItemId:
1005 """
1006 super(ExpandEvent, self).__init__(source)
1007 self._expandedItemId = expandedItemId
1008
1009
1011 """Node where the event occurred.
1012
1013 @return: the source of the event.
1014 """
1015 return self._expandedItemId
1016
1019 """Expand event listener.
1020
1021 @author: Vaadin Ltd.
1022 @author: Richard Lincoln
1023 @version: 1.1.2
1024 """
1025
1027 """A node has been expanded.
1028
1029 @param event:
1030 the expand event.
1031 """
1032 raise NotImplementedError
1033
1034
1035 EXPAND_METHOD = IExpandListener.nodeExpand
1039 """Collapse event
1040
1041 @author: Vaadin Ltd.
1042 @author: Richard Lincoln
1043 @version: 1.1.2
1044 """
1045
1046 - def __init__(self, source, collapsedItemId):
1047 """New instance of options change event.
1048
1049 @param source:
1050 the Source of the event.
1051 @param collapsedItemId:
1052 """
1053 super(CollapseEvent, self).__init__(source)
1054 self._collapsedItemId = collapsedItemId
1055
1056
1058 """Gets the collapsed item id.
1059
1060 @return: the collapsed item id.
1061 """
1062 return self._collapsedItemId
1063
1066 """Collapse event listener.
1067
1068 @author: Vaadin Ltd.
1069 @author: Richard Lincoln
1070 @version: 1.1.2
1071 """
1072
1074 """A node has been collapsed.
1075
1076 @param event:
1077 the collapse event.
1078 """
1079 raise NotImplementedError
1080
1081
1082 COLLAPSE_METHOD = ICollapseListener.nodeCollapse
1086 """IItemStyleGenerator can be used to add custom styles to tree items.
1087 The CSS class name that will be added to the cell content is
1088 C{v-tree-node-[style name]}.
1089 """
1090
1092 """Called by Tree when an item is painted.
1093
1094 @param itemId:
1095 The itemId of the item to be painted
1096 @return: The style name to add to this item. (the CSS class name
1097 will be v-tree-node-[style name]
1098 """
1099 raise NotImplementedError
1100
1103 """A L{TargetDetails} implementation with Tree specific api.
1104 """
1105
1106 - def __init__(self, rawVariables, tree):
1108
1109
1112
1113
1115 """If the event is on a node that can not have children (see
1116 L{Tree.areChildrenAllowed}), this method returns the
1117 parent item id of the target item (see L{getItemIdOver} ).
1118 The identifier of the parent node is also returned if the cursor is
1119 on the top part of node. Else this method returns the same as
1120 L{getItemIdOver}.
1121
1122 In other words this method returns the identifier of the "folder"
1123 into the drag operation is targeted.
1124
1125 If the method returns null, the current target is on a root node or
1126 on other undefined area over the tree component.
1127
1128 The default Tree implementation marks the targetted tree node with
1129 CSS classnames v-tree-node-dragfolder and
1130 v-tree-node-caption-dragfolder (for the caption element).
1131 """
1132 itemIdOver = self.getItemIdOver()
1133
1134 if (self.areChildrenAllowed(itemIdOver)
1135 and self.getDropLocation() == VerticalDropLocation.MIDDLE):
1136 return itemIdOver
1137
1138 return self.getParent(itemIdOver)
1139
1140
1142 """If drop is targeted into "folder node" (see L{getItemIdInto}), this
1143 method returns the item id of the node after
1144 the drag was targeted. This method is useful when implementing drop
1145 into specific location (between specific nodes) in tree.
1146
1147 @return: the id of the item after the user targets the drop or null if
1148 "target" is a first item in node list (or the first in root
1149 node list)
1150 """
1151 itemIdOver = self.getItemIdOver()
1152 itemIdInto2 = self.getItemIdInto()
1153
1154 if itemIdOver == itemIdInto2:
1155 return None
1156
1157 dropLocation = self.getDropLocation()
1158
1159 if VerticalDropLocation.TOP == dropLocation:
1160
1161 itemIdInto = self.getItemIdInto()
1162 if itemIdInto is not None:
1163
1164 children = self.getChildren(itemIdInto)
1165 else:
1166 children = self.rootItemIds()
1167 ref = None
1168 for obj in children:
1169 if obj == itemIdOver:
1170 return ref
1171 ref = obj
1172
1173 return itemIdOver
1174
1177 """Concrete implementation of L{DataBoundTransferable} for data
1178 transferred from a tree.
1179
1180 @see: L{DataBoundTransferable}.
1181 """
1182
1183 - def __init__(self, sourceComponent, rawVariables):
1185
1186
1189
1190
1193
1197 """Lazy loading accept criterion for Tree. Accepted target nodes are
1198 loaded from server once per drag and drop operation. Developer must
1199 override one method that decides accepted tree nodes for the whole Tree.
1200
1201 Initially pretty much no data is sent to client. On first required
1202 criterion check (per drag request) the client side data structure is
1203 initialized from server and no subsequent requests requests are needed
1204 during that drag and drop operation.
1205 """
1206
1208 self._tree = None
1209 self._allowedItemIds = None
1210
1211
1214
1215
1216 - def accept(self, dragEvent):
1221
1222
1224
1225
1226 arry = list(self._allowedItemIds)
1227 for i in range(len(arry)):
1228 key = self._tree.key(arry[i])
1229 arry[i] = key
1230 target.addAttribute('allowedIds', arry)
1231
1232
1235
1238 """A criterion that accepts L{Transferable} only directly on a tree
1239 node that can have children.
1240
1241 Class is singleton, use L{TargetItemAllowsChildren.get} to get the
1242 instance.
1243
1244 @see: Tree.setChildrenAllowed
1245 """
1246
1247 INSTANCE = None
1248
1249 @classmethod
1252
1253
1258
1259
1260 - def accept(self, dragEvent):
1275
1276 TargetItemAllowsChildren.INSTANCE = TargetItemAllowsChildren()
1280 """An accept criterion that checks the parent node (or parent hierarchy)
1281 for the item identifier given in constructor. If the parent is found,
1282 content is accepted. Criterion can be used to accepts drags on a specific
1283 sub tree only.
1284
1285 The root items is also consider to be valid target.
1286 """
1287
1288 - def __init__(self, rootId, depthToCheck=None):
1289 """Constructs a criteria that accepts the drag if the targeted Item
1290 is a descendant of Item identified by given id
1291
1292 Alternatively, constructs a criteria that accepts drops within given
1293 level below the subtree root identified by given id.
1294
1295 @param rootId:
1296 the item identifier of the parent node or the node to be
1297 sought for
1298 @param depthToCheck:
1299 the depth that tree is traversed upwards to seek for the
1300 parent, -1 means that the whole structure should be
1301 checked
1302 """
1303 self._rootId = rootId
1304 self._depthToCheck = -1
1305
1306 if depthToCheck is not None:
1307 self._depthToCheck = depthToCheck
1308
1309
1310 - def accept(self, dragEvent):
1311 try:
1312 eventDetails = dragEvent.getTargetDetails()
1313
1314 if eventDetails.getItemIdOver() is not None:
1315 itemId = eventDetails.getItemIdOver()
1316 i = 0
1317 while (itemId is not None
1318 and (self._depthToCheck == -1)
1319 or (i <= self._depthToCheck)):
1320 if itemId == self._rootId:
1321 return True
1322 itemId = self.getParent(itemId)
1323 i += 1
1324
1325 return False
1326 except Exception:
1327 return False
1328
1329
1330 - def paintContent(self, target):
1331 super(TargetInSubtree, self).paintContent(target)
1332 target.addAttribute('depth', self._depthToCheck)
1333 target.addAttribute('key', self.key(self._rootId))
1334