Subversion Repository Public Repository

litesoft

Diff Revisions 949 vs 950 for /trunk/Java/GWT/Client/src/org/litesoft/GWT/client/widgets/SizeableTree.java

Diff revisions: vs.
  @@ -1,796 +1,807 @@
1 - // This Source Code is in the Public Domain per: http://unlicense.org
2 - package org.litesoft.GWT.client.widgets;
3 -
4 - import org.litesoft.GWT.client.*;
5 - import org.litesoft.GWT.client.widgets.nonpublic.*;
6 - import org.litesoft.commonfoundation.typeutils.*;
7 - import org.litesoft.commonfoundation.typeutils.Objects;
8 - import org.litesoft.core.util.*;
9 -
10 - import com.google.gwt.event.dom.client.*;
11 - import com.google.gwt.event.logical.shared.*;
12 - import com.google.gwt.resources.client.*;
13 - import com.google.gwt.user.client.ui.*;
14 -
15 - import java.util.*;
16 -
17 - public class SizeableTree extends AbstractSizeableComposite implements TreeNode {
18 - public static final String FOLDER_OPEN_URL = "common/images/misc/folder_open_16x16.gif";
19 - public static final String FOLDER_CLOSED_URL = "common/images/misc/folder_closed_16x16.gif";
20 - public static final String FOLDER_LEAF_URL = "common/images/misc/TransparentSpacer_16x16.gif";
21 -
22 - protected final Tree mTree;
23 - protected SizeableScrollPanel mScrollPanel;
24 -
25 - private SizeableTree( Tree pTree ) {
26 - mTree = pTree;
27 - initWidget( mScrollPanel = new SizeableScrollPanel( mTree ) );
28 - LLstretchable();
29 - mTree.addSelectionHandler( new SelectionHandler<TreeItem>() {
30 - @Override
31 - public void onSelection( SelectionEvent<TreeItem> event ) {
32 - notifySelectionChange();
33 - }
34 - } );
35 - mTree.addOpenHandler( new OpenHandler<TreeItem>() {
36 - @Override
37 - public void onOpen( OpenEvent<TreeItem> event ) {
38 - notifyOpenCloseChange( true );
39 - }
40 - } );
41 - mTree.addCloseHandler( new CloseHandler<TreeItem>() {
42 - @Override
43 - public void onClose( CloseEvent<TreeItem> event ) {
44 - notifyOpenCloseChange( false );
45 - }
46 - } );
47 - }
48 -
49 - public SizeableTree() {
50 - this( new Tree() );
51 - }
52 -
53 - public SizeableTree( String pOpenImageURL, int pOpenWidth, int pOpenHeight, //
54 - String pClosedImageURL, int pClosedWidth, int pClosedHeight, //
55 - String pLeafImageURL, int pLeafWidth, int pLeafHeight, //
56 - boolean pUseLeafImage ) {
57 - this( new Tree( new TreeImageAdaptor( pOpenImageURL, pOpenWidth, pOpenHeight, pClosedImageURL, pClosedWidth, pClosedHeight, pLeafImageURL, pLeafWidth,
58 - pLeafHeight ), pUseLeafImage ) );
59 - }
60 -
61 - public static SizeableTree withFolders() {
62 - return new SizeableTree( FOLDER_OPEN_URL, 16, 16,//
63 - FOLDER_CLOSED_URL, 16, 16, //
64 - FOLDER_LEAF_URL, 16, 16, //
65 - false );
66 - }
67 -
68 - public SizeableTree style( String pStyleName ) {
69 - mTree.addStyleName( pStyleName );
70 - return this;
71 - }
72 -
73 - public boolean isEmpty() {
74 - return (0 == mTree.getItemCount());
75 - }
76 -
77 - /**
78 - * Get the Currently Selected TreeNode
79 - *
80 - * @return Selected Node or null if none Selected
81 - */
82 - public TreeNode getSelectedNode() {
83 - return (TreeNode) mTree.getSelectedItem();
84 - }
85 -
86 - /**
87 - * Get the Currently Selected UserObject
88 - *
89 - * @return UserObject for the currently selected "row"
90 - */
91 - public Object getSelected() {
92 - TreeNode zNode = getSelectedNode();
93 - return (zNode != null) ? zNode.getUserObject() : null;
94 - }
95 -
96 - /**
97 - * Set the Selected Node & return the UserObject actually Selected
98 - *
99 - * @param pUserObject to select
100 - *
101 - * @return pUserObject or null if it was not found!
102 - */
103 - public Object setSelected( Object pUserObject ) {
104 - MyTreeNode tn = getTreeNode( pUserObject );
105 - if ( tn != null ) {
106 - tn.setSelected( true );
107 - return pUserObject;
108 - }
109 - mTree.setSelectedItem( null, true );
110 - return null;
111 - }
112 -
113 - /**
114 - * Check if the currently selected is Open.
115 - *
116 - * @return null if no current selected, otherwise the open state of the currently selected node
117 - */
118 - public Boolean isSelectedOpen() {
119 - TreeNode tn = getSelectedNode();
120 - return (tn == null) ? null : tn.areAnyOpen();
121 - }
122 -
123 - /**
124 - * Ensures that the specified item is visible, by adjusting the panel's scroll position.
125 - *
126 - * @param pUserObject the item whose visibility is to be ensured
127 - */
128 - public void ensureVisible( Object pUserObject ) {
129 - MyTreeNode tn = getTreeNode( pUserObject );
130 - if ( tn != null ) {
131 - mScrollPanel.ensureVisible( tn );
132 - }
133 - }
134 -
135 - public SizeableTree setAnimationEnabled( boolean pEnable ) {
136 - mTree.setAnimationEnabled( pEnable );
137 - return this;
138 - }
139 -
140 - @Override
141 - public TreeNode addChild( Object pUserObject ) {
142 - if ( pUserObject == null ) {
143 - return null;
144 - }
145 - MyTreeNode tn = createMyTreeNode( pUserObject );
146 - mTree.addItem( tn );
147 - notifyOpenCloseChange( null );
148 - tn.addChildren();
149 - return tn;
150 - }
151 -
152 - @Override
153 - public boolean removeChild( Object pUserObject ) {
154 - if ( pUserObject != null ) {
155 - int zCount = mTree.getItemCount();
156 - for ( int i = 0; i < zCount; i++ ) {
157 - TreeItem zItem = mTree.getItem( i );
158 - if ( pUserObject == zItem.getUserObject() ) {
159 - return removeWithSelectMangementAndNotification( zItem );
160 - }
161 - }
162 - }
163 - return false;
164 - }
165 -
166 - @Override
167 - public boolean removeDescendant( Object pUserObject ) {
168 - MyTreeNode tn = getTreeNode( pUserObject );
169 - return (tn != null) && removeWithSelectMangementAndNotification( tn );
170 - }
171 -
172 - @Override
173 - public void clear() {
174 - mTree.removeItems();
175 - notifyOpenCloseChange( null );
176 - }
177 -
178 - @Override
179 - public boolean areAnyOpen() {
180 - int zChildCount = mTree.getItemCount(); // Top Level!
181 - for ( int i = 0; i < zChildCount; i++ ) {
182 - TreeItem zItem = mTree.getItem( i ); // Top Level!
183 - if ( zItem.getChildCount() != 0 ) {
184 - if ( zItem.getState() ) {
185 - return true;
186 - }
187 - }
188 - }
189 - return false;
190 - }
191 -
192 - @Override
193 - public boolean areAnyClosed() {
194 - int zChildCount = mTree.getItemCount(); // Top Level!
195 - for ( int i = 0; i < zChildCount; i++ ) {
196 - TreeItem zItem = mTree.getItem( i ); // Top Level!
197 - if ( zItem.getChildCount() != 0 ) {
198 - if ( ((TreeNode) zItem).areAnyClosed() ) {
199 - return true;
200 - }
201 - }
202 - }
203 - return false;
204 - }
205 -
206 - @Override
207 - public void openAll() {
208 - if ( childrenUpdateOpenClosedState( true ) ) {
209 - notifyOpenCloseChange( true );
210 - }
211 - }
212 -
213 - @Override
214 - public void closeAll() {
215 - if ( childrenUpdateOpenClosedState( false ) ) {
216 - notifyOpenCloseChange( false );
217 - }
218 - }
219 -
220 - /**
221 - * The Tree itself does NOT have a User Object
222 - *
223 - * @return null
224 - */
225 - @Override
226 - public Object getUserObject() {
227 - return null;
228 - }
229 -
230 - /**
231 - * @return iterator over Tree's UserObject(s) depth first
232 - */
233 - public Iterator<Object> iterator() {
234 - List<Object> collector = new ArrayList<Object>();
235 - int zCount = mTree.getItemCount(); // Top Level!
236 - for ( int i = 0; i < zCount; i++ ) {
237 - MyTreeNode tn = (MyTreeNode) mTree.getItem( i );
238 - tn.addToDepthFirst( collector );
239 - }
240 - return collector.iterator();
241 - }
242 -
243 - public SizeableTree connectOpenCloseButtons( Button pOpenButton, Button pCloseButton ) {
244 - if ( (pOpenButton != null) || (pCloseButton != null) ) {
245 - final OpenCloseAllHelper zHelper = new OpenCloseAllHelper( pOpenButton, pCloseButton );
246 - addTreeSelectionListener( zHelper );
247 - addTreeOpenCloseListener( zHelper );
248 - if ( pOpenButton != null ) {
249 - pOpenButton.addClickHandler( new ClickHandler() {
250 - @Override
251 - public void onClick( ClickEvent event ) {
252 - zHelper.processOpenAllRequest();
253 - }
254 - } );
255 - }
256 - if ( pCloseButton != null ) {
257 - pCloseButton.addClickHandler( new ClickHandler() {
258 - @Override
259 - public void onClick( ClickEvent event ) {
260 - zHelper.processCloseAllRequest();
261 - }
262 - } );
263 - }
264 - }
265 - return this;
266 - }
267 -
268 - public void addTreeSelectionListener( TreeSelectionListener pListener ) {
269 - if ( pListener != null ) {
270 - if ( mSelectionListeners == null ) {
271 - mSelectionListeners = new TreeSelectionListenerCollection();
272 - }
273 - mSelectionListeners.add( pListener );
274 - }
275 - }
276 -
277 - public void removeTreeSelectionListener( TreeSelectionListener pListener ) {
278 - if ( pListener != null ) {
279 - if ( mSelectionListeners != null ) {
280 - mSelectionListeners.remove( pListener );
281 - if ( mSelectionListeners.isEmpty() ) {
282 - mSelectionListeners = null;
283 - }
284 - }
285 - }
286 - }
287 -
288 - protected TreeSelectionListenerCollection mSelectionListeners = null;
289 -
290 - protected void notifySelectionChange() {
291 - if ( mSelectionListeners != null ) {
292 - mSelectionListeners.notify( getSelected() );
293 - }
294 - }
295 -
296 - public void addTreeOpenCloseListener( TreeOpenCloseListener pListener ) {
297 - if ( pListener != null ) {
298 - if ( mOpenCloseListeners == null ) {
299 - mOpenCloseListeners = new TreeOpenCloseListenerCollection();
300 - }
301 - mOpenCloseListeners.add( pListener );
302 - }
303 - }
304 -
305 - public void removeTreeOpenCloseListener( TreeOpenCloseListener pListener ) {
306 - if ( pListener != null ) {
307 - if ( mOpenCloseListeners != null ) {
308 - mOpenCloseListeners.remove( pListener );
309 - if ( mOpenCloseListeners.isEmpty() ) {
310 - mOpenCloseListeners = null;
311 - }
312 - }
313 - }
314 - }
315 -
316 - protected TreeOpenCloseListenerCollection mOpenCloseListeners = null;
317 -
318 - protected void notifyOpenCloseChange( Boolean pOpened ) {
319 - if ( mOpenCloseListeners != null ) {
320 - mOpenCloseListeners.openStateChanged( pOpened );
321 - }
322 - }
323 -
324 - private boolean childrenUpdateOpenClosedState( boolean pOpenAll ) {
325 - boolean changed = false;
326 - int zChildCount = mTree.getItemCount(); // Top Level!
327 - if ( pOpenAll ) {
328 - for ( int i = 0; i < zChildCount; i++ ) // Open from the Top Down
329 - {
330 - TreeItem zItem = mTree.getItem( i ); // Top Level!
331 - if ( zItem.getParentItem() == null ) {
332 - changed |= updateOpenClosedStateRecursively( zItem, pOpenAll );
333 - }
334 - }
335 - } else {
336 - for ( int i = zChildCount; 0 <= --i; ) // Close from the Bottom Up
337 - {
338 - TreeItem zItem = mTree.getItem( i ); // Top Level!
339 - if ( zItem.getParentItem() == null ) {
340 - changed |= updateOpenClosedStateRecursively( zItem, pOpenAll );
341 - }
342 - }
343 - }
344 - return changed;
345 - }
346 -
347 - private static boolean updateOpenClosedState( TreeItem pTreeItem, boolean pOpen ) {
348 - if ( (pTreeItem.getState() != pOpen) && (pTreeItem.getChildCount() != 0) ) {
349 - pTreeItem.setState( pOpen );
350 - return true;
351 - }
352 - return false;
353 - }
354 -
355 - private static boolean updateOpenClosedStateRecursively( TreeItem pTreeItem, boolean pOpenAll ) {
356 - boolean changed = false;
357 - int zChildCount = pTreeItem.getChildCount();
358 - if ( pOpenAll ) {
359 - changed = updateOpenClosedState( pTreeItem, pOpenAll ); // Open from the Top Down
360 - for ( int i = 0; i < zChildCount; i++ ) {
361 - changed |= updateOpenClosedStateRecursively( pTreeItem.getChild( i ), pOpenAll );
362 - }
363 - } else {
364 - for ( int i = zChildCount; 0 <= --i; ) // Close from the Bottom Up
365 - {
366 - changed |= updateOpenClosedStateRecursively( pTreeItem.getChild( i ), pOpenAll );
367 - }
368 - changed |= updateOpenClosedState( pTreeItem, pOpenAll );
369 - }
370 - return changed;
371 - }
372 -
373 - private MyTreeNode getTreeNode( Object pUserObject ) {
374 - if ( pUserObject != null ) {
375 - int zCount = mTree.getItemCount(); // Top Level!
376 - for ( int i = 0; i < zCount; i++ ) {
377 - MyTreeNode tn = (MyTreeNode) mTree.getItem( i ); // Top Level!
378 - if ( null != (tn = tn.getTreeNode( pUserObject )) ) {
379 - return tn;
380 - }
381 - }
382 - }
383 - return null;
384 - }
385 -
386 - private boolean removeWithSelectMangementAndNotification( TreeItem pTreeItem ) {
387 - Object currentlySelected = getSelected();
388 -
389 - removeWithoutSelectMangementAndNotification( pTreeItem );
390 -
391 - restoreSelection( currentlySelected );
392 - return true;
393 - }
394 -
395 - private void restoreSelection( Object pPreviouslySelected ) {
396 - Object restoredSelected = setSelected( pPreviouslySelected );
397 - if ( restoredSelected != pPreviouslySelected ) {
398 - notifySelectionChange();
399 - }
400 - notifyOpenCloseChange( null );
401 - }
402 -
403 - private void removeWithoutSelectMangementAndNotification( TreeItem pTreeItem ) {
404 - TreeItem zParentItem = pTreeItem.getParentItem();
405 - if ( zParentItem == null ) {
406 - mTree.removeItem( pTreeItem );
407 - } else {
408 - zParentItem.removeItem( pTreeItem );
409 - }
410 - }
411 -
412 - private HierarchicalNode.ChangeListener<HierarchicalNode> getChangeListener() {
413 - if ( mChangeListener == null ) {
414 - mChangeListener = new HierarchicalNode.ChangeListener<HierarchicalNode>() {
415 - @Override
416 - public void change( HierarchicalNode pNode, HierarchicalNode.Change pChange ) {
417 - if ( (pNode != null) && (pChange != null) ) {
418 - if ( HierarchicalNode.Change.Deleted == pChange ) {
419 - removeChild( pNode );
420 - } else {
421 - updateChildren( pNode );
422 - }
423 - }
424 - }
425 - };
426 - }
427 - return mChangeListener;
428 - }
429 -
430 - private HierarchicalNode.ChangeListener<HierarchicalNode> mChangeListener;
431 -
432 - private static class TreeImageAdaptor implements Tree.Resources {
433 - private ImageResource mOpen, mClosed, mLeaf;
434 -
435 - private TreeImageAdaptor( String pOpenImageURL, int pOpenWidth, int pOpenHeight, //
436 - String pClosedImageURL, int pClosedWidth, int pClosedHeight, //
437 - String pLeafImageURL, int pLeafWidth, int pLeafHeight ) {
438 - mOpen = new ImageResourceAdapter( "Open", pOpenImageURL, pOpenWidth, pOpenHeight );
439 - mClosed = new ImageResourceAdapter( "Closed", pClosedImageURL, pClosedWidth, pClosedHeight );
440 - mLeaf = new ImageResourceAdapter( "Leaf", pLeafImageURL, pLeafWidth, pLeafHeight );
441 - }
442 -
443 - @Override
444 - public ImageResource treeOpen() {
445 - return mOpen;
446 - }
447 -
448 - @Override
449 - public ImageResource treeClosed() {
450 - return mClosed;
451 - }
452 -
453 - @Override
454 - public ImageResource treeLeaf() {
455 - return mLeaf;
456 - }
457 - }
458 -
459 - private MyTreeNode createMyTreeNode( Object pUserObject ) {
460 - MyTreeNode tn;
461 - if ( pUserObject instanceof HasWidget ) {
462 - tn = new MyTreeNode( ((HasWidget) pUserObject).getWidget(), pUserObject );
463 - } else if ( pUserObject instanceof Widget ) {
464 - tn = new MyTreeNode( (Widget) pUserObject, pUserObject );
465 - } else {
466 - tn = new MyTreeNode( new Label( pUserObject.toString() ), pUserObject );
467 - }
468 - if ( pUserObject instanceof HierarchicalNode ) {
469 - //noinspection unchecked
470 - ((HierarchicalNode) pUserObject).addChangeListener( getChangeListener() );
471 - }
472 - return tn;
473 - }
474 -
475 - private class MyTreeNode extends TreeItem implements TreeNode {
476 - private MyTreeNode( Widget pWidget, Object pUserObject ) {
477 - super( pWidget );
478 - setUserObject( pUserObject );
479 - }
480 -
481 - public void addChildren() {
482 - Object zUserObject = getUserObject();
483 - if ( zUserObject instanceof HierarchicalNode ) {
484 - addChildren( getChildren( (HierarchicalNode) zUserObject ) );
485 - }
486 - }
487 -
488 - @Override
489 - public MyTreeNode addChild( Object pUserObject ) {
490 - if ( pUserObject == null ) {
491 - return null;
492 - }
493 - MyTreeNode tn = createMyTreeNode( pUserObject );
494 - addItem( tn );
495 - tn.addChildren();
496 - notifyOpenCloseChange( null );
497 - return tn;
498 - }
499 -
500 - @Override
501 - public boolean removeChild( Object pUserObject ) {
502 - if ( pUserObject != null ) {
503 - int zCount = getChildCount();
504 - if ( zCount != 0 ) {
505 - for ( int i = 0; i < zCount; i++ ) {
506 - TreeItem zItem = getChild( i );
507 - if ( (zItem != null) && (pUserObject == zItem.getUserObject()) ) {
508 - return removeWithSelectMangementAndNotification( zItem );
509 - }
510 - }
511 - }
512 - }
513 - return false;
514 - }
515 -
516 - @Override
517 - public boolean removeDescendant( Object pUserObject ) {
518 - if ( pUserObject != null ) {
519 - int zCount = getChildCount();
520 - if ( zCount != 0 ) {
521 - for ( int i = 0; i < zCount; i++ ) {
522 - MyTreeNode tn = (MyTreeNode) getChild( i );
523 - if ( pUserObject == tn.getUserObject() ) {
524 - return removeWithSelectMangementAndNotification( tn );
525 - }
526 - if ( tn.removeDescendant( pUserObject ) ) {
527 - return true;
528 - }
529 - }
530 - }
531 - }
532 - return false;
533 - }
534 -
535 - @Override
536 - public void clear() {
537 - removeItems();
538 - notifyOpenCloseChange( null );
539 - }
540 -
541 - @Override
542 - public boolean areAnyOpen() {
543 - return parentsOpen() && (getChildCount() != 0) && getState();
544 - }
545 -
546 - @Override
547 - public boolean areAnyClosed() {
548 - return parentsOpen() && LLareAnyClosed();
549 - }
550 -
551 - @Override
552 - public void openAll() {
553 - if ( updateOpenClosedStateRecursively( this, true ) ) {
554 - notifyOpenCloseChange( true );
555 - }
556 - }
557 -
558 - @Override
559 - public void closeAll() {
560 - if ( updateOpenClosedStateRecursively( this, false ) ) {
561 - notifyOpenCloseChange( false );
562 - }
563 - }
564 -
565 - private boolean areAnyChildrenClosed() {
566 - for ( int i = getChildCount(); 0 <= --i; ) {
567 - if ( ((MyTreeNode) getChild( i )).LLareAnyClosed() ) {
568 - return true;
569 - }
570 - }
571 - return false;
572 - }
573 -
574 - private boolean LLareAnyClosed() {
575 - return (getChildCount() != 0) && (!getState() || areAnyChildrenClosed());
576 - }
577 -
578 - private boolean parentsOpen() {
579 - MyTreeNode zParent = (MyTreeNode) getParentItem();
580 - return (zParent == null) || (zParent.getState() && zParent.parentsOpen());
581 - }
582 -
583 - private void addChildren( List<HierarchicalNode> pChildUserObjects ) {
584 - if ( pChildUserObjects != null ) {
585 - for ( HierarchicalNode zUserObject : pChildUserObjects ) {
586 - MyTreeNode tn = addChild( zUserObject );
587 - if ( tn != null ) {
588 - tn.addChildren();
589 - }
590 - }
591 - }
592 - }
593 -
594 - public void addToDepthFirst( List<Object> pCollector ) {
595 - pCollector.add( getUserObject() );
596 - int zCount = getChildCount();
597 - for ( int i = 0; i < zCount; i++ ) {
598 - MyTreeNode tn = (MyTreeNode) getChild( i );
599 - tn.addToDepthFirst( pCollector );
600 - }
601 - }
602 -
603 - public MyTreeNode getTreeNode( Object pUserObject ) {
604 - if ( pUserObject == getUserObject() ) {
605 - return this;
606 - }
607 - int zCount = getChildCount();
608 - for ( int i = 0; i < zCount; i++ ) {
609 - MyTreeNode tn = (MyTreeNode) getChild( i );
610 - if ( null != (tn = tn.getTreeNode( pUserObject )) ) {
611 - return tn;
612 - }
613 - }
614 - return null;
615 - }
616 - }
617 -
618 - @SuppressWarnings({"unchecked"})
619 - private static List<HierarchicalNode> getChildren( HierarchicalNode pUserObject ) {
620 - return (List<HierarchicalNode>) pUserObject.getChildren();
621 - }
622 -
623 - protected void updateChildren( HierarchicalNode pNode ) {
624 - MyTreeNode tn = getTreeNode( pNode );
625 - if ( tn != null ) {
626 - List<HierarchicalNode> zChildren = getChildren( pNode );
627 - if ( Lists.isNullOrEmpty( zChildren ) ) {
628 - Object currentlySelected = getSelected();
629 -
630 - tn.removeItems();
631 -
632 - restoreSelection( currentlySelected );
633 - return;
634 - }
635 - CurrentState cs = new CurrentState( tn );
636 - cs.dropChildrenAndAlignWith( zChildren, tn );
637 - }
638 - }
639 -
640 - private class CurrentState {
641 - private Object mUserObject;
642 - private boolean mSelected, mOpen;
643 - private List<CurrentState> mChildren = new ArrayList<CurrentState>();
644 -
645 - public CurrentState( MyTreeNode pTreeNode ) {
646 - mUserObject = pTreeNode.getUserObject();
647 - mSelected = pTreeNode.isSelected();
648 - mOpen = pTreeNode.getState(); // Open
649 - int zChildCount = pTreeNode.getChildCount();
650 - for ( int i = 0; i < zChildCount; i++ ) {
651 - mChildren.add( new CurrentState( (MyTreeNode) pTreeNode.getChild( i ) ) );
652 - }
653 - }
654 -
655 - public CurrentState( HierarchicalNode pNew ) {
656 - mUserObject = pNew;
657 - mSelected = mOpen = false;
658 - List<HierarchicalNode> zChildren = getChildren( pNew );
659 - if ( zChildren != null ) {
660 - for ( HierarchicalNode zChild : zChildren ) {
661 - mChildren.add( new CurrentState( zChild ) );
662 - }
663 - }
664 - }
665 -
666 - private void setStateOn( MyTreeNode pTreeNode ) {
667 - pTreeNode.setState( mOpen );
668 - pTreeNode.setSelected( mSelected );
669 - }
670 -
671 - public Object getUserObject() {
672 - return mUserObject;
673 - }
674 -
675 - private boolean hasSameUserObject( CurrentState them ) {
676 - return Objects.areEqual( this.getUserObject(), them.getUserObject() );
677 - }
678 -
679 - /**
680 - * Damages the mChildren state!
681 - *
682 - * @param pNewChildren -
683 - * @param pTreeNode -
684 - */
685 - public void dropChildrenAndAlignWith( List<HierarchicalNode> pNewChildren, MyTreeNode pTreeNode ) {
686 - List<CurrentState> zChildren = new ArrayList<CurrentState>( pNewChildren.size() );
687 - for ( HierarchicalNode zChild : pNewChildren ) {
688 - zChildren.add( new CurrentState( zChild ) );
689 - }
690 - for ( CurrentState zChild : mChildren ) {
691 - @SuppressWarnings({"SuspiciousMethodCalls"}) int at = pNewChildren.indexOf( zChild.getUserObject() );
692 - if ( at != -1 ) {
693 - zChildren.set( at, zChild );
694 - }
695 - }
696 -
697 - Object currentlySelected = getSelected();
698 - // Skip over the front of tree that is common && delete front of tree that is NOT in new Tree!
699 - int i = 0; // MyTreeNode Child Index
700 - while ( !zChildren.isEmpty() && !mChildren.isEmpty() && (i < pTreeNode.getChildCount()) ) {
701 - //noinspection SuspiciousMethodCalls
702 - if ( !pNewChildren.contains( mChildren.get( 0 ).getUserObject() ) ) {
703 - mChildren.remove( 0 );
704 - removeWithoutSelectMangementAndNotification( pTreeNode.getChild( i ) );
705 - continue;
706 - }
707 - while ( mChildren.get( 0 ).hasSameUserObject( zChildren.get( 0 ) ) ) {
708 - zChildren.remove( 0 );
709 - mChildren.remove( 0 );
710 - i++;
711 - }
712 - }
713 - mChildren.clear();
714 - while ( i < pTreeNode.getChildCount() ) {
715 - removeWithoutSelectMangementAndNotification( pTreeNode.getChild( i ) );
716 - }
717 - addTo( pTreeNode, zChildren );
718 -
719 - restoreSelection( currentlySelected );
720 - }
721 -
722 - private void addTo( MyTreeNode pTreeNode, List<CurrentState> zChildren ) {
723 - if ( zChildren != null ) {
724 - for ( CurrentState zChild : zChildren ) {
725 - MyTreeNode tn = createMyTreeNode( zChild.getUserObject() );
726 - pTreeNode.addItem( tn );
727 - zChild.setStateOn( tn );
728 - addTo( tn, zChild.mChildren );
729 - }
730 - }
731 - }
732 - }
733 -
734 - private static class NoOpEnableable implements Enableable {
735 - public static Enableable deNull( Enableable pEnableable ) {
736 - return (pEnableable != null) ? pEnableable : new NoOpEnableable();
737 - }
738 -
739 - @Override
740 - public boolean isEnabled() {
741 - return false;
742 - }
743 -
744 - @Override
745 - public void setEnabled( boolean pEnabled ) {
746 - }
747 - }
748 -
749 - private class OpenCloseAllHelper implements TreeSelectionListener,
750 - TreeOpenCloseListener {
751 - private Enableable mOpenAll, mCloseAll;
752 -
753 - public OpenCloseAllHelper( Enableable pOpenAll, Enableable pCloseAll ) {
754 - mOpenAll = NoOpEnableable.deNull( pOpenAll );
755 - mCloseAll = NoOpEnableable.deNull( pCloseAll );
756 - update();
757 - }
758 -
759 - private void setAllState( boolean pOpenAllEnabled, boolean pCloseAllEnabled ) {
760 - mOpenAll.setEnabled( pOpenAllEnabled );
761 - mCloseAll.setEnabled( pCloseAllEnabled );
762 - }
763 -
764 - @Override
765 - public void openStateChanged( Boolean pOpened ) {
766 - update();
767 - }
768 -
769 - @Override
770 - public void selected( Object pSelected ) {
771 - update();
772 - }
773 -
774 - private void update() {
775 - setAllState( areAnyClosed(), areAnyOpen() );
776 - }
777 -
778 - public void processOpenAllRequest() {
779 - TreeNode tn = getSelectedNode();
780 - if ( (tn != null) && tn.areAnyClosed() ) {
781 - tn.openAll();
782 - return;
783 - }
784 - openAll();
785 - }
786 -
787 - public void processCloseAllRequest() {
788 - TreeNode tn = getSelectedNode();
789 - if ( (tn != null) && tn.areAnyOpen() ) {
790 - tn.closeAll();
791 - return;
792 - }
793 - closeAll();
794 - }
795 - }
796 - }
1 + // This Source Code is in the Public Domain per: http://unlicense.org
2 + package org.litesoft.GWT.client.widgets;
3 +
4 + import org.litesoft.GWT.client.*;
5 + import org.litesoft.GWT.client.widgets.nonpublic.*;
6 + import org.litesoft.commonfoundation.base.*;
7 + import org.litesoft.commonfoundation.iterators.*;
8 + import org.litesoft.commonfoundation.typeutils.*;
9 + import org.litesoft.core.util.*;
10 +
11 + import com.google.gwt.event.dom.client.*;
12 + import com.google.gwt.event.logical.shared.*;
13 + import com.google.gwt.resources.client.*;
14 + import com.google.gwt.user.client.ui.*;
15 +
16 + import java.util.*;
17 +
18 + public class SizeableTree extends AbstractSizeableComposite implements TreeNode {
19 + public static final String FOLDER_OPEN_URL = "common/images/misc/folder_open_16x16.gif";
20 + public static final String FOLDER_CLOSED_URL = "common/images/misc/folder_closed_16x16.gif";
21 + public static final String FOLDER_LEAF_URL = "common/images/misc/TransparentSpacer_16x16.gif";
22 +
23 + protected final Tree mTree;
24 + protected SizeableScrollPanel mScrollPanel;
25 +
26 + private SizeableTree( Tree pTree ) {
27 + mTree = pTree;
28 + initWidget( mScrollPanel = new SizeableScrollPanel( mTree ) );
29 + LLstretchable();
30 + mTree.addSelectionHandler( new SelectionHandler<TreeItem>() {
31 + @Override
32 + public void onSelection( SelectionEvent<TreeItem> event ) {
33 + notifySelectionChange();
34 + }
35 + } );
36 + mTree.addOpenHandler( new OpenHandler<TreeItem>() {
37 + @Override
38 + public void onOpen( OpenEvent<TreeItem> event ) {
39 + notifyOpenCloseChange( true );
40 + }
41 + } );
42 + mTree.addCloseHandler( new CloseHandler<TreeItem>() {
43 + @Override
44 + public void onClose( CloseEvent<TreeItem> event ) {
45 + notifyOpenCloseChange( false );
46 + }
47 + } );
48 + }
49 +
50 + public SizeableTree() {
51 + this( new Tree() );
52 + }
53 +
54 + public SizeableTree( String pOpenImageURL, int pOpenWidth, int pOpenHeight, //
55 + String pClosedImageURL, int pClosedWidth, int pClosedHeight, //
56 + String pLeafImageURL, int pLeafWidth, int pLeafHeight, //
57 + boolean pUseLeafImage ) {
58 + this( new Tree( new TreeImageAdaptor( pOpenImageURL, pOpenWidth, pOpenHeight, pClosedImageURL, pClosedWidth, pClosedHeight, pLeafImageURL, pLeafWidth,
59 + pLeafHeight ), pUseLeafImage ) );
60 + }
61 +
62 + public static SizeableTree withFolders() {
63 + return new SizeableTree( FOLDER_OPEN_URL, 16, 16,//
64 + FOLDER_CLOSED_URL, 16, 16, //
65 + FOLDER_LEAF_URL, 16, 16, //
66 + false );
67 + }
68 +
69 + public SizeableTree style( String pStyleName ) {
70 + mTree.addStyleName( pStyleName );
71 + return this;
72 + }
73 +
74 + public boolean isEmpty() {
75 + return (0 == mTree.getItemCount());
76 + }
77 +
78 + /**
79 + * Get the Currently Selected TreeNode
80 + *
81 + * @return Selected Node or null if none Selected
82 + */
83 + public TreeNode getSelectedNode() {
84 + return (TreeNode) mTree.getSelectedItem();
85 + }
86 +
87 + /**
88 + * Get the Currently Selected UserObject
89 + *
90 + * @return UserObject for the currently selected "row"
91 + */
92 + public Object getSelected() {
93 + TreeNode zNode = getSelectedNode();
94 + return (zNode != null) ? zNode.getUserObject() : null;
95 + }
96 +
97 + /**
98 + * Set the Selected Node & return the UserObject actually Selected
99 + *
100 + * @param pUserObject to select
101 + *
102 + * @return pUserObject or null if it was not found!
103 + */
104 + public Object setSelected( Object pUserObject ) {
105 + MyTreeNode tn = getTreeNode( pUserObject );
106 + if ( tn != null ) {
107 + tn.setSelected( true );
108 + return pUserObject;
109 + }
110 + mTree.setSelectedItem( null, true );
111 + return null;
112 + }
113 +
114 + /**
115 + * Check if the currently selected is Open.
116 + *
117 + * @return null if no current selected, otherwise the open state of the currently selected node
118 + */
119 + public Boolean isSelectedOpen() {
120 + TreeNode tn = getSelectedNode();
121 + return (tn == null) ? null : tn.areAnyOpen();
122 + }
123 +
124 + /**
125 + * Ensures that the specified item is visible, by adjusting the panel's scroll position.
126 + *
127 + * @param pUserObject the item whose visibility is to be ensured
128 + */
129 + public void ensureVisible( Object pUserObject ) {
130 + MyTreeNode tn = getTreeNode( pUserObject );
131 + if ( tn != null ) {
132 + mScrollPanel.ensureVisible( tn );
133 + }
134 + }
135 +
136 + public SizeableTree setAnimationEnabled( boolean pEnable ) {
137 + mTree.setAnimationEnabled( pEnable );
138 + return this;
139 + }
140 +
141 + @Override
142 + public TreeNode addChild( Object pUserObject ) {
143 + if ( pUserObject == null ) {
144 + return null;
145 + }
146 + MyTreeNode tn = createMyTreeNode( pUserObject );
147 + mTree.addItem( tn );
148 + notifyOpenCloseChange( null );
149 + tn.addChildren();
150 + return tn;
151 + }
152 +
153 + @Override
154 + public boolean removeChild( Object pUserObject ) {
155 + if ( pUserObject != null ) {
156 + int zCount = mTree.getItemCount();
157 + for ( int i = 0; i < zCount; i++ ) {
158 + TreeItem zItem = mTree.getItem( i );
159 + if ( pUserObject == zItem.getUserObject() ) {
160 + return removeWithSelectMangementAndNotification( zItem );
161 + }
162 + }
163 + }
164 + return false;
165 + }
166 +
167 + @Override
168 + public boolean removeDescendant( Object pUserObject ) {
169 + MyTreeNode tn = getTreeNode( pUserObject );
170 + return (tn != null) && removeWithSelectMangementAndNotification( tn );
171 + }
172 +
173 + @Override
174 + public void clear() {
175 + mTree.removeItems();
176 + notifyOpenCloseChange( null );
177 + }
178 +
179 + @Override
180 + public boolean areAnyOpen() {
181 + int zChildCount = mTree.getItemCount(); // Top Level!
182 + for ( int i = 0; i < zChildCount; i++ ) {
183 + TreeItem zItem = mTree.getItem( i ); // Top Level!
184 + if ( zItem.getChildCount() != 0 ) {
185 + if ( zItem.getState() ) {
186 + return true;
187 + }
188 + }
189 + }
190 + return false;
191 + }
192 +
193 + @Override
194 + public boolean areAnyClosed() {
195 + int zChildCount = mTree.getItemCount(); // Top Level!
196 + for ( int i = 0; i < zChildCount; i++ ) {
197 + TreeItem zItem = mTree.getItem( i ); // Top Level!
198 + if ( zItem.getChildCount() != 0 ) {
199 + if ( ((TreeNode) zItem).areAnyClosed() ) {
200 + return true;
201 + }
202 + }
203 + }
204 + return false;
205 + }
206 +
207 + @Override
208 + public void openAll() {
209 + if ( childrenUpdateOpenClosedState( true ) ) {
210 + notifyOpenCloseChange( true );
211 + }
212 + }
213 +
214 + @Override
215 + public void closeAll() {
216 + if ( childrenUpdateOpenClosedState( false ) ) {
217 + notifyOpenCloseChange( false );
218 + }
219 + }
220 +
221 + /**
222 + * The Tree itself does NOT have a User Object
223 + *
224 + * @return null
225 + */
226 + @Override
227 + public Object getUserObject() {
228 + return null;
229 + }
230 +
231 + /**
232 + * @return iterator over Tree's UserObject(s) depth first
233 + */
234 + public Iterator<Object> iterator() {
235 + List<Object> collector = Lists.newArrayList();
236 + int zCount = mTree.getItemCount(); // Top Level!
237 + for ( int i = 0; i < zCount; i++ ) {
238 + MyTreeNode tn = (MyTreeNode) mTree.getItem( i );
239 + tn.addToDepthFirst( collector );
240 + }
241 + return collector.iterator();
242 + }
243 +
244 + public SizeableTree connectOpenCloseButtons( Button pOpenButton, Button pCloseButton ) {
245 + if ( (pOpenButton != null) || (pCloseButton != null) ) {
246 + final OpenCloseAllHelper zHelper = new OpenCloseAllHelper( pOpenButton, pCloseButton );
247 + addTreeSelectionListener( zHelper );
248 + addTreeOpenCloseListener( zHelper );
249 + if ( pOpenButton != null ) {
250 + pOpenButton.addClickHandler( new ClickHandler() {
251 + @Override
252 + public void onClick( ClickEvent event ) {
253 + zHelper.processOpenAllRequest();
254 + }
255 + } );
256 + }
257 + if ( pCloseButton != null ) {
258 + pCloseButton.addClickHandler( new ClickHandler() {
259 + @Override
260 + public void onClick( ClickEvent event ) {
261 + zHelper.processCloseAllRequest();
262 + }
263 + } );
264 + }
265 + }
266 + return this;
267 + }
268 +
269 + public void addTreeSelectionListener( TreeSelectionListener pListener ) {
270 + if ( pListener != null ) {
271 + if ( mSelectionListeners == null ) {
272 + mSelectionListeners = new TreeSelectionListenerCollection();
273 + }
274 + mSelectionListeners.add( pListener );
275 + }
276 + }
277 +
278 + public void removeTreeSelectionListener( TreeSelectionListener pListener ) {
279 + if ( pListener != null ) {
280 + if ( mSelectionListeners != null ) {
281 + mSelectionListeners.remove( pListener );
282 + if ( mSelectionListeners.isEmpty() ) {
283 + mSelectionListeners = null;
284 + }
285 + }
286 + }
287 + }
288 +
289 + protected TreeSelectionListenerCollection mSelectionListeners = null;
290 +
291 + protected void notifySelectionChange() {
292 + if ( mSelectionListeners != null ) {
293 + mSelectionListeners.notify( getSelected() );
294 + }
295 + }
296 +
297 + public void addTreeOpenCloseListener( TreeOpenCloseListener pListener ) {
298 + if ( pListener != null ) {
299 + if ( mOpenCloseListeners == null ) {
300 + mOpenCloseListeners = new TreeOpenCloseListenerCollection();
301 + }
302 + mOpenCloseListeners.add( pListener );
303 + }
304 + }
305 +
306 + public void removeTreeOpenCloseListener( TreeOpenCloseListener pListener ) {
307 + if ( pListener != null ) {
308 + if ( mOpenCloseListeners != null ) {
309 + mOpenCloseListeners.remove( pListener );
310 + if ( mOpenCloseListeners.isEmpty() ) {
311 + mOpenCloseListeners = null;
312 + }
313 + }
314 + }
315 + }
316 +
317 + protected TreeOpenCloseListenerCollection mOpenCloseListeners = null;
318 +
319 + protected void notifyOpenCloseChange( Boolean pOpened ) {
320 + if ( mOpenCloseListeners != null ) {
321 + mOpenCloseListeners.openStateChanged( pOpened );
322 + }
323 + }
324 +
325 + private boolean childrenUpdateOpenClosedState( boolean pOpenAll ) {
326 + return childrenUpdateOpenClosedState( pOpenAll, new ChildrenAccessor() {
327 + @Override
328 + public int getChildCount() {
329 + return mTree.getItemCount();
330 + }
331 +
332 + @Override
333 + public TreeItem getChild( int index ) {
334 + TreeItem zItem = mTree.getItem( index );
335 + return (zItem.getParentItem() == null) ? zItem : null;
336 + }
337 + } );
338 + }
339 +
340 + private static boolean childrenUpdateOpenClosedState( boolean pOpenAll, ChildrenAccessor pAccessor ) {
341 + boolean changed = false;
342 + // Open from the Top Down, Close from the Bottom Up
343 + for ( IntIterator zIT = IntIterator.from( 0 ).uptoExclusive( pAccessor.getChildCount() )
344 + .optionallyReverse( !pOpenAll ).build(); zIT.hasNext(); ) {
345 + TreeItem zItem = pAccessor.getChild( zIT.next() );
346 + if ( zItem != null ) {
347 + changed |= updateOpenClosedStateRecursively( zItem, pOpenAll );
348 + }
349 + }
350 + return changed;
351 + }
352 +
353 + private static boolean updateOpenClosedStateRecursively( final TreeItem pTreeItem, boolean pOpenAll ) {
354 + boolean changed = updateOpenClosedStateIfShould( pOpenAll, pTreeItem, pOpenAll ); // Open from the Top Down
355 + changed |= childrenUpdateOpenClosedState( pOpenAll, new ChildrenAccessor() {
356 + @Override
357 + public int getChildCount() {
358 + return pTreeItem.getChildCount();
359 + }
360 +
361 + @Override
362 + public TreeItem getChild( int index ) {
363 + return pTreeItem.getChild( index );
364 + }
365 + } );
366 + changed |= updateOpenClosedStateIfShould( !pOpenAll, pTreeItem, pOpenAll ); // Close from the Bottom Up
367 + return changed;
368 + }
369 +
370 + private static boolean updateOpenClosedStateIfShould( boolean pShould, TreeItem pTreeItem, boolean pOpen ) {
371 + if ( pShould && (pTreeItem.getState() != pOpen) && (pTreeItem.getChildCount() != 0) ) {
372 + pTreeItem.setState( pOpen );
373 + return true;
374 + }
375 + return false;
376 + }
377 +
378 + private MyTreeNode getTreeNode( Object pUserObject ) {
379 + if ( pUserObject != null ) {
380 + int zCount = mTree.getItemCount(); // Top Level!
381 + for ( int i = 0; i < zCount; i++ ) {
382 + MyTreeNode tn = (MyTreeNode) mTree.getItem( i ); // Top Level!
383 + if ( null != (tn = tn.getTreeNode( pUserObject )) ) {
384 + return tn;
385 + }
386 + }
387 + }
388 + return null;
389 + }
390 +
391 + private boolean removeWithSelectMangementAndNotification( TreeItem pTreeItem ) {
392 + Object currentlySelected = getSelected();
393 +
394 + removeWithoutSelectMangementAndNotification( pTreeItem );
395 +
396 + restoreSelection( currentlySelected );
397 + return true;
398 + }
399 +
400 + private void restoreSelection( Object pPreviouslySelected ) {
401 + Object restoredSelected = setSelected( pPreviouslySelected );
402 + if ( restoredSelected != pPreviouslySelected ) {
403 + notifySelectionChange();
404 + }
405 + notifyOpenCloseChange( null );
406 + }
407 +
408 + private void removeWithoutSelectMangementAndNotification( TreeItem pTreeItem ) {
409 + TreeItem zParentItem = pTreeItem.getParentItem();
410 + if ( zParentItem == null ) {
411 + mTree.removeItem( pTreeItem );
412 + } else {
413 + zParentItem.removeItem( pTreeItem );
414 + }
415 + }
416 +
417 + private HierarchicalNode.ChangeListener<HierarchicalNode> getChangeListener() {
418 + if ( mChangeListener == null ) {
419 + mChangeListener = new HierarchicalNode.ChangeListener<HierarchicalNode>() {
420 + @Override
421 + public void change( HierarchicalNode pNode, HierarchicalNode.Change pChange ) {
422 + if ( (pNode != null) && (pChange != null) ) {
423 + if ( HierarchicalNode.Change.Deleted == pChange ) {
424 + removeChild( pNode );
425 + } else {
426 + updateChildren( pNode );
427 + }
428 + }
429 + }
430 + };
431 + }
432 + return mChangeListener;
433 + }
434 +
435 + private HierarchicalNode.ChangeListener<HierarchicalNode> mChangeListener;
436 +
437 + private static class TreeImageAdaptor implements Tree.Resources {
438 + private ImageResource mOpen, mClosed, mLeaf;
439 +
440 + private TreeImageAdaptor( String pOpenImageURL, int pOpenWidth, int pOpenHeight, //
441 + String pClosedImageURL, int pClosedWidth, int pClosedHeight, //
442 + String pLeafImageURL, int pLeafWidth, int pLeafHeight ) {
443 + mOpen = new ImageResourceAdapter( "Open", pOpenImageURL, pOpenWidth, pOpenHeight );
444 + mClosed = new ImageResourceAdapter( "Closed", pClosedImageURL, pClosedWidth, pClosedHeight );
445 + mLeaf = new ImageResourceAdapter( "Leaf", pLeafImageURL, pLeafWidth, pLeafHeight );
446 + }
447 +
448 + @Override
449 + public ImageResource treeOpen() {
450 + return mOpen;
451 + }
452 +
453 + @Override
454 + public ImageResource treeClosed() {
455 + return mClosed;
456 + }
457 +
458 + @Override
459 + public ImageResource treeLeaf() {
460 + return mLeaf;
461 + }
462 + }
463 +
464 + private MyTreeNode createMyTreeNode( Object pUserObject ) {
465 + MyTreeNode tn;
466 + if ( pUserObject instanceof HasWidget ) {
467 + tn = new MyTreeNode( ((HasWidget) pUserObject).getWidget(), pUserObject );
468 + } else if ( pUserObject instanceof Widget ) {
469 + tn = new MyTreeNode( (Widget) pUserObject, pUserObject );
470 + } else {
471 + tn = new MyTreeNode( new Label( pUserObject.toString() ), pUserObject );
472 + }
473 + if ( pUserObject instanceof HierarchicalNode ) {
474 + //noinspection unchecked
475 + ((HierarchicalNode) pUserObject).addChangeListener( getChangeListener() );
476 + }
477 + return tn;
478 + }
479 +
480 + private class MyTreeNode extends TreeItem implements TreeNode {
481 + private MyTreeNode( Widget pWidget, Object pUserObject ) {
482 + super( pWidget );
483 + setUserObject( pUserObject );
484 + }
485 +
486 + public void addChildren() {
487 + Object zUserObject = getUserObject();
488 + if ( zUserObject instanceof HierarchicalNode ) {
489 + addChildren( getChildren( (HierarchicalNode) zUserObject ) );
490 + }
491 + }
492 +
493 + @Override
494 + public MyTreeNode addChild( Object pUserObject ) {
495 + if ( pUserObject == null ) {
496 + return null;
497 + }
498 + MyTreeNode tn = createMyTreeNode( pUserObject );
499 + addItem( tn );
500 + tn.addChildren();
501 + notifyOpenCloseChange( null );
502 + return tn;
503 + }
504 +
505 + @Override
506 + public boolean removeChild( Object pUserObject ) {
507 + if ( pUserObject != null ) {
508 + int zCount = getChildCount();
509 + if ( zCount != 0 ) {
510 + for ( int i = 0; i < zCount; i++ ) {
511 + TreeItem zItem = getChild( i );
512 + if ( (zItem != null) && (pUserObject == zItem.getUserObject()) ) {
513 + return removeWithSelectMangementAndNotification( zItem );
514 + }
515 + }
516 + }
517 + }
518 + return false;
519 + }
520 +
521 + @Override
522 + public boolean removeDescendant( Object pUserObject ) {
523 + if ( pUserObject != null ) {
524 + int zCount = getChildCount();
525 + if ( zCount != 0 ) {
526 + for ( int i = 0; i < zCount; i++ ) {
527 + MyTreeNode tn = (MyTreeNode) getChild( i );
528 + if ( pUserObject == tn.getUserObject() ) {
529 + return removeWithSelectMangementAndNotification( tn );
530 + }
531 + if ( tn.removeDescendant( pUserObject ) ) {
532 + return true;
533 + }
534 + }
535 + }
536 + }
537 + return false;
538 + }
539 +
540 + @Override
541 + public void clear() {
542 + removeItems();
543 + notifyOpenCloseChange( null );
544 + }
545 +
546 + @Override
547 + public boolean areAnyOpen() {
548 + return parentsOpen() && (getChildCount() != 0) && getState();
549 + }
550 +
551 + @Override
552 + public boolean areAnyClosed() {
553 + return parentsOpen() && LLareAnyClosed();
554 + }
555 +
556 + @Override
557 + public void openAll() {
558 + if ( updateOpenClosedStateRecursively( this, true ) ) {
559 + notifyOpenCloseChange( true );
560 + }
561 + }
562 +
563 + @Override
564 + public void closeAll() {
565 + if ( updateOpenClosedStateRecursively( this, false ) ) {
566 + notifyOpenCloseChange( false );
567 + }
568 + }
569 +
570 + private boolean areAnyChildrenClosed() {
571 + for ( int i = getChildCount(); 0 <= --i; ) {
572 + if ( ((MyTreeNode) getChild( i )).LLareAnyClosed() ) {
573 + return true;
574 + }
575 + }
576 + return false;
577 + }
578 +
579 + private boolean LLareAnyClosed() {
580 + return (getChildCount() != 0) && (!getState() || areAnyChildrenClosed());
581 + }
582 +
583 + private boolean parentsOpen() {
584 + MyTreeNode zParent = (MyTreeNode) getParentItem();
585 + return (zParent == null) || (zParent.getState() && zParent.parentsOpen());
586 + }
587 +
588 + private void addChildren( List<HierarchicalNode> pChildUserObjects ) {
589 + if ( pChildUserObjects != null ) {
590 + for ( HierarchicalNode zUserObject : pChildUserObjects ) {
591 + MyTreeNode tn = addChild( zUserObject );
592 + if ( tn != null ) {
593 + tn.addChildren();
594 + }
595 + }
596 + }
597 + }
598 +
599 + public void addToDepthFirst( List<Object> pCollector ) {
600 + pCollector.add( getUserObject() );
601 + int zCount = getChildCount();
602 + for ( int i = 0; i < zCount; i++ ) {
603 + MyTreeNode tn = (MyTreeNode) getChild( i );
604 + tn.addToDepthFirst( pCollector );
605 + }
606 + }
607 +
608 + public MyTreeNode getTreeNode( Object pUserObject ) {
609 + if ( pUserObject == getUserObject() ) {
610 + return this;
611 + }
612 + int zCount = getChildCount();
613 + for ( int i = 0; i < zCount; i++ ) {
614 + MyTreeNode tn = (MyTreeNode) getChild( i );
615 + if ( null != (tn = tn.getTreeNode( pUserObject )) ) {
616 + return tn;
617 + }
618 + }
619 + return null;
620 + }
621 + }
622 +
623 + @SuppressWarnings({"unchecked"})
624 + private static List<HierarchicalNode> getChildren( HierarchicalNode pUserObject ) {
625 + return (List<HierarchicalNode>) pUserObject.getChildren();
626 + }
627 +
628 + protected void updateChildren( HierarchicalNode pNode ) {
629 + MyTreeNode tn = getTreeNode( pNode );
630 + if ( tn != null ) {
631 + List<HierarchicalNode> zChildren = getChildren( pNode );
632 + if ( Currently.isNullOrEmpty( zChildren ) ) {
633 + Object currentlySelected = getSelected();
634 +
635 + tn.removeItems();
636 +
637 + restoreSelection( currentlySelected );
638 + return;
639 + }
640 + CurrentState cs = new CurrentState( tn );
641 + cs.dropChildrenAndAlignWith( zChildren, tn );
642 + }
643 + }
644 +
645 + private class CurrentState {
646 + private Object mUserObject;
647 + private boolean mSelected, mOpen;
648 + private List<CurrentState> mChildren = Lists.newArrayList();
649 +
650 + public CurrentState( MyTreeNode pTreeNode ) {
651 + mUserObject = pTreeNode.getUserObject();
652 + mSelected = pTreeNode.isSelected();
653 + mOpen = pTreeNode.getState(); // Open
654 + int zChildCount = pTreeNode.getChildCount();
655 + for ( int i = 0; i < zChildCount; i++ ) {
656 + mChildren.add( new CurrentState( (MyTreeNode) pTreeNode.getChild( i ) ) );
657 + }
658 + }
659 +
660 + public CurrentState( HierarchicalNode pNew ) {
661 + mUserObject = pNew;
662 + mSelected = mOpen = false;
663 + List<HierarchicalNode> zChildren = getChildren( pNew );
664 + if ( zChildren != null ) {
665 + for ( HierarchicalNode zChild : zChildren ) {
666 + mChildren.add( new CurrentState( zChild ) );
667 + }
668 + }
669 + }
670 +
671 + private void setStateOn( MyTreeNode pTreeNode ) {
672 + pTreeNode.setState( mOpen );
673 + pTreeNode.setSelected( mSelected );
674 + }
675 +
676 + public Object getUserObject() {
677 + return mUserObject;
678 + }
679 +
680 + private boolean hasSameUserObject( CurrentState them ) {
681 + return Currently.areEqual( this.getUserObject(), them.getUserObject() );
682 + }
683 +
684 + /**
685 + * Damages the mChildren state!
686 + *
687 + * @param pNewChildren -
688 + * @param pTreeNode -
689 + */
690 + public void dropChildrenAndAlignWith( List<HierarchicalNode> pNewChildren, MyTreeNode pTreeNode ) {
691 + List<CurrentState> zChildren = Lists.newArrayList( pNewChildren.size() );
692 + for ( HierarchicalNode zChild : pNewChildren ) {
693 + zChildren.add( new CurrentState( zChild ) );
694 + }
695 + for ( CurrentState zChild : mChildren ) {
696 + @SuppressWarnings({"SuspiciousMethodCalls"}) int at = pNewChildren.indexOf( zChild.getUserObject() );
697 + if ( at != -1 ) {
698 + zChildren.set( at, zChild );
699 + }
700 + }
701 +
702 + Object currentlySelected = getSelected();
703 + // Skip over the front of tree that is common && delete front of tree that is NOT in new Tree!
704 + int i = 0; // MyTreeNode Child Index
705 + while ( !zChildren.isEmpty() && !mChildren.isEmpty() && (i < pTreeNode.getChildCount()) ) {
706 + //noinspection SuspiciousMethodCalls
707 + if ( !pNewChildren.contains( mChildren.get( 0 ).getUserObject() ) ) {
708 + mChildren.remove( 0 );
709 + removeWithoutSelectMangementAndNotification( pTreeNode.getChild( i ) );
710 + continue;
711 + }
712 + while ( mChildren.get( 0 ).hasSameUserObject( zChildren.get( 0 ) ) ) {
713 + zChildren.remove( 0 );
714 + mChildren.remove( 0 );
715 + i++;
716 + }
717 + }
718 + mChildren.clear();
719 + while ( i < pTreeNode.getChildCount() ) {
720 + removeWithoutSelectMangementAndNotification( pTreeNode.getChild( i ) );
721 + }
722 + addTo( pTreeNode, zChildren );
723 +
724 + restoreSelection( currentlySelected );
725 + }
726 +
727 + private void addTo( MyTreeNode pTreeNode, List<CurrentState> zChildren ) {
728 + if ( zChildren != null ) {
729 + for ( CurrentState zChild : zChildren ) {
730 + MyTreeNode tn = createMyTreeNode( zChild.getUserObject() );
731 + pTreeNode.addItem( tn );
732 + zChild.setStateOn( tn );
733 + addTo( tn, zChild.mChildren );
734 + }
735 + }
736 + }
737 + }
738 +
739 + private static class NoOpEnableable implements Enableable {
740 + public static Enableable deNull( Enableable pEnableable ) {
741 + return (pEnableable != null) ? pEnableable : new NoOpEnableable();
742 + }
743 +
744 + @Override
745 + public boolean isEnabled() {
746 + return false;
747 + }
748 +
749 + @Override
750 + public void setEnabled( boolean pEnabled ) {
751 + }
752 + }
753 +
754 + private class OpenCloseAllHelper implements TreeSelectionListener,
755 + TreeOpenCloseListener {
756 + private Enableable mOpenAll, mCloseAll;
757 +
758 + public OpenCloseAllHelper( Enableable pOpenAll, Enableable pCloseAll ) {
759 + mOpenAll = NoOpEnableable.deNull( pOpenAll );
760 + mCloseAll = NoOpEnableable.deNull( pCloseAll );
761 + update();
762 + }
763 +
764 + private void setAllState( boolean pOpenAllEnabled, boolean pCloseAllEnabled ) {
765 + mOpenAll.setEnabled( pOpenAllEnabled );
766 + mCloseAll.setEnabled( pCloseAllEnabled );
767 + }
768 +
769 + @Override
770 + public void openStateChanged( Boolean pOpened ) {
771 + update();
772 + }
773 +
774 + @Override
775 + public void selected( Object pSelected ) {
776 + update();
777 + }
778 +
779 + private void update() {
780 + setAllState( areAnyClosed(), areAnyOpen() );
781 + }
782 +
783 + public void processOpenAllRequest() {
784 + TreeNode tn = getSelectedNode();
785 + if ( (tn != null) && tn.areAnyClosed() ) {
786 + tn.openAll();
787 + return;
788 + }
789 + openAll();
790 + }
791 +
792 + public void processCloseAllRequest() {
793 + TreeNode tn = getSelectedNode();
794 + if ( (tn != null) && tn.areAnyOpen() ) {
795 + tn.closeAll();
796 + return;
797 + }
798 + closeAll();
799 + }
800 + }
801 +
802 + private interface ChildrenAccessor {
803 + int getChildCount();
804 +
805 + TreeItem getChild( int index );
806 + }
807 + }