Changes:
[phpeclipse.git] / net.sourceforge.phpeclipse / src / net / sourceforge / phpdt / internal / ui / wizards / dialogfields / TreeListDialogField.java
1 /*******************************************************************************
2  * Copyright (c) 2000, 2003 IBM Corporation and others.
3  * All rights reserved. This program and the accompanying materials 
4  * are made available under the terms of the Common Public License v1.0
5  * which accompanies this distribution, and is available at
6  * http://www.eclipse.org/legal/cpl-v10.html
7  * 
8  * Contributors:
9  *     IBM Corporation - initial API and implementation
10  *******************************************************************************/
11 package net.sourceforge.phpdt.internal.ui.wizards.dialogfields;
12
13 import java.util.ArrayList;
14 import java.util.Iterator;
15 import java.util.List;
16
17 import net.sourceforge.phpdt.internal.ui.util.PixelConverter;
18 import net.sourceforge.phpdt.internal.ui.util.SWTUtil;
19
20 import org.eclipse.jface.util.Assert;
21 import org.eclipse.jface.viewers.DoubleClickEvent;
22 import org.eclipse.jface.viewers.IDoubleClickListener;
23 import org.eclipse.jface.viewers.ILabelProvider;
24 import org.eclipse.jface.viewers.ISelection;
25 import org.eclipse.jface.viewers.ISelectionChangedListener;
26 import org.eclipse.jface.viewers.IStructuredSelection;
27 import org.eclipse.jface.viewers.ITreeContentProvider;
28 import org.eclipse.jface.viewers.SelectionChangedEvent;
29 import org.eclipse.jface.viewers.StructuredSelection;
30 import org.eclipse.jface.viewers.TreeViewer;
31 import org.eclipse.jface.viewers.Viewer;
32 import org.eclipse.jface.viewers.ViewerSorter;
33 import org.eclipse.swt.SWT;
34 import org.eclipse.swt.events.KeyAdapter;
35 import org.eclipse.swt.events.KeyEvent;
36 import org.eclipse.swt.events.SelectionEvent;
37 import org.eclipse.swt.events.SelectionListener;
38 import org.eclipse.swt.layout.GridData;
39 import org.eclipse.swt.layout.GridLayout;
40 import org.eclipse.swt.widgets.Button;
41 import org.eclipse.swt.widgets.Composite;
42 import org.eclipse.swt.widgets.Control;
43 import org.eclipse.swt.widgets.Display;
44 import org.eclipse.swt.widgets.Label;
45 import org.eclipse.swt.widgets.Tree;
46
47
48 /**
49  * A list with a button bar.
50  * Typical buttons are 'Add', 'Remove', 'Up' and 'Down'.
51  * List model is independend of widget creation.
52  * DialogFields controls are: Label, List and Composite containing buttons.
53  */
54 public class TreeListDialogField extends DialogField {
55
56         protected TreeViewer fTree;
57         protected ILabelProvider fLabelProvider;
58         protected TreeViewerAdapter fTreeViewerAdapter;
59         protected List fElements;
60         protected ViewerSorter fViewerSorter;
61
62         protected String[] fButtonLabels;
63         private Button[] fButtonControls;
64
65         private boolean[] fButtonsEnabled;
66
67         private int fRemoveButtonIndex;
68         private int fUpButtonIndex;
69         private int fDownButtonIndex;
70
71         private Label fLastSeparator;
72
73         private Tree fTreeControl;
74         private Composite fButtonsControl;
75         private ISelection fSelectionWhenEnabled;
76
77         private ITreeListAdapter fTreeAdapter;
78
79         private Object fParentElement;
80         private int fTreeExpandLevel;
81
82         /**
83          * @param adapter Can be <code>null</code>.
84          */
85         public TreeListDialogField(ITreeListAdapter adapter, String[] buttonLabels, ILabelProvider lprovider) {
86                 super();
87                 fTreeAdapter= adapter;
88
89                 fLabelProvider= lprovider;
90                 fTreeViewerAdapter= new TreeViewerAdapter();
91                 fParentElement= this;
92
93                 fElements= new ArrayList(10);
94
95                 fButtonLabels= buttonLabels;
96                 if (fButtonLabels != null) {
97                         int nButtons= fButtonLabels.length;
98                         fButtonsEnabled= new boolean[nButtons];
99                         for (int i= 0; i < nButtons; i++) {
100                                 fButtonsEnabled[i]= true;
101                         }
102                 }
103
104                 fTree= null;
105                 fTreeControl= null;
106                 fButtonsControl= null;
107
108                 fRemoveButtonIndex= -1;
109                 fUpButtonIndex= -1;
110                 fDownButtonIndex= -1;
111                 
112                 fTreeExpandLevel= 0;
113         }
114
115         /**
116         * Sets the index of the 'remove' button in the button label array passed in
117         * the constructor. The behaviour of the button marked as the 'remove' button
118         * will then behandled internally. (enable state, button invocation
119         * behaviour)
120         */
121         public void setRemoveButtonIndex(int removeButtonIndex) {
122                 Assert.isTrue(removeButtonIndex < fButtonLabels.length);
123                 fRemoveButtonIndex= removeButtonIndex;
124         }
125
126         /**
127         * Sets the index of the 'up' button in the button label array passed in the
128         * constructor. The behaviour of the button marked as the 'up' button will
129         * then behandled internally.
130         * (enable state, button invocation behaviour)
131         */
132         public void setUpButtonIndex(int upButtonIndex) {
133                 Assert.isTrue(upButtonIndex < fButtonLabels.length);
134                 fUpButtonIndex= upButtonIndex;
135         }
136
137         /**
138         * Sets the index of the 'down' button in the button label array passed in
139         * the constructor. The behaviour of the button marked as the 'down' button
140         * will then be handled internally. (enable state, button invocation
141         * behaviour)
142         */
143         public void setDownButtonIndex(int downButtonIndex) {
144                 Assert.isTrue(downButtonIndex < fButtonLabels.length);
145                 fDownButtonIndex= downButtonIndex;
146         }
147
148         /**
149         * Sets the viewerSorter.
150         * @param viewerSorter The viewerSorter to set
151         */
152         public void setViewerSorter(ViewerSorter viewerSorter) {
153                 fViewerSorter= viewerSorter;
154         }
155         
156         /**
157         * Sets the viewerSorter.
158         * @param viewerSorter The viewerSorter to set
159         */
160         public void setTreeExpansionLevel(int level) {
161                 fTreeExpandLevel= level;
162                 if (fTree != null) {
163                         fTree.expandToLevel(level);
164                 }
165         }       
166
167         // ------ adapter communication
168
169         private void buttonPressed(int index) {
170                 if (!managedButtonPressed(index) && fTreeAdapter != null) {
171                         fTreeAdapter.customButtonPressed(this, index);
172                 }
173         }
174
175         /**
176         * Checks if the button pressed is handled internally
177         * @return Returns true if button has been handled.
178         */
179         protected boolean managedButtonPressed(int index) {
180                 if (index == fRemoveButtonIndex) {
181                         remove();
182                 } else if (index == fUpButtonIndex) {
183                         up();
184                 } else if (index == fDownButtonIndex) {
185                         down();
186                 } else {
187                         return false;
188                 }
189                 return true;
190         }
191
192         // ------ layout helpers
193
194         /*
195         * @see DialogField#doFillIntoGrid
196         */
197         public Control[] doFillIntoGrid(Composite parent, int nColumns) {
198                 PixelConverter converter= new PixelConverter(parent);
199
200                 assertEnoughColumns(nColumns);
201
202                 Label label= getLabelControl(parent);
203                 GridData gd= gridDataForLabel(1);
204                 gd.verticalAlignment= GridData.BEGINNING;
205                 label.setLayoutData(gd);
206
207                 Control list= getTreeControl(parent);
208                 gd= new GridData();
209                 gd.horizontalAlignment= GridData.FILL;
210                 gd.grabExcessHorizontalSpace= false;
211                 gd.verticalAlignment= GridData.FILL;
212                 gd.grabExcessVerticalSpace= true;
213                 gd.horizontalSpan= nColumns - 2;
214                 gd.widthHint= converter.convertWidthInCharsToPixels(50);
215                 gd.heightHint= converter.convertHeightInCharsToPixels(6);
216
217                 list.setLayoutData(gd);
218
219                 Composite buttons= getButtonBox(parent);
220                 gd= new GridData();
221                 gd.horizontalAlignment= GridData.FILL;
222                 gd.grabExcessHorizontalSpace= false;
223                 gd.verticalAlignment= GridData.FILL;
224                 gd.grabExcessVerticalSpace= true;
225                 gd.horizontalSpan= 1;
226                 buttons.setLayoutData(gd);
227
228                 return new Control[] { label, list, buttons };
229         }
230
231         /*
232         * @see DialogField#getNumberOfControls
233         */
234         public int getNumberOfControls() {
235                 return 3;
236         }
237
238         /**
239         * Sets the minimal width of the buttons. Must be called after widget creation.
240         */
241         public void setButtonsMinWidth(int minWidth) {
242                 if (fLastSeparator != null) {
243                         ((GridData) fLastSeparator.getLayoutData()).widthHint= minWidth;
244                 }
245         }
246
247         // ------ ui creation
248
249         /**
250         * Returns the tree control. When called the first time, the control will be
251         * created.
252         * @param The parent composite when called the first time, or <code>null</code>
253         * after.
254         */
255         public Control getTreeControl(Composite parent) {
256                 if (fTreeControl == null) {
257                         assertCompositeNotNull(parent);
258
259                         fTree= createTreeViewer(parent);
260
261                         fTreeControl= (Tree) fTree.getControl();
262                         fTreeControl.addKeyListener(new KeyAdapter() {
263                                 public void keyPressed(KeyEvent e) {
264                                         handleKeyPressed(e);
265                                 }
266                         });
267                         fTree.setAutoExpandLevel(99);
268                         fTree.setContentProvider(fTreeViewerAdapter);
269                         fTree.setLabelProvider(fLabelProvider);
270                         fTree.addSelectionChangedListener(fTreeViewerAdapter);
271                         fTree.addDoubleClickListener(fTreeViewerAdapter);
272
273                         fTree.setInput(fParentElement);
274                         fTree.expandToLevel(fTreeExpandLevel);
275
276                         if (fViewerSorter != null) {
277                                 fTree.setSorter(fViewerSorter);
278                         }
279
280                         fTreeControl.setEnabled(isEnabled());
281                         if (fSelectionWhenEnabled != null) {
282                                 postSetSelection(fSelectionWhenEnabled);
283                         }
284                 }
285                 return fTreeControl;
286         }
287
288         /**
289         * Returns the internally used table viewer.
290         */
291         public TreeViewer getTreeViewer() {
292                 return fTree;
293         }
294
295         /*
296         * Subclasses may override to specify a different style.
297         */
298         protected int getTreeStyle() {
299                 int style= SWT.BORDER | SWT.MULTI | SWT.H_SCROLL | SWT.V_SCROLL;
300                 return style;
301         }
302
303         protected TreeViewer createTreeViewer(Composite parent) {
304                 Tree tree= new Tree(parent, getTreeStyle());
305                 return new TreeViewer(tree);
306         }
307
308         protected Button createButton(Composite parent, String label, SelectionListener listener) {
309                 Button button= new Button(parent, SWT.PUSH);
310                 button.setText(label);
311                 button.addSelectionListener(listener);
312                 GridData gd= new GridData();
313                 gd.horizontalAlignment= GridData.FILL;
314                 gd.grabExcessHorizontalSpace= true;
315                 gd.verticalAlignment= GridData.BEGINNING;
316                 gd.heightHint= SWTUtil.getButtonHeigthHint(button);
317                 gd.widthHint= SWTUtil.getButtonWidthHint(button);
318
319                 button.setLayoutData(gd);
320                 return button;
321         }
322
323         private Label createSeparator(Composite parent) {
324                 Label separator= new Label(parent, SWT.NONE);
325                 separator.setVisible(false);
326                 GridData gd= new GridData();
327                 gd.horizontalAlignment= GridData.FILL;
328                 gd.verticalAlignment= GridData.BEGINNING;
329                 gd.heightHint= 4;
330                 separator.setLayoutData(gd);
331                 return separator;
332         }
333
334         /**
335         * Returns the composite containing the buttons. When called the first time, the control
336         * will be created.
337         * @param The parent composite when called the first time, or <code>null</code>
338         * after.
339         */
340         public Composite getButtonBox(Composite parent) {
341                 if (fButtonsControl == null) {
342                         assertCompositeNotNull(parent);
343
344                         SelectionListener listener= new SelectionListener() {
345                                 public void widgetDefaultSelected(SelectionEvent e) {
346                                         doButtonSelected(e);
347                                 }
348                                 public void widgetSelected(SelectionEvent e) {
349                                         doButtonSelected(e);
350                                 }
351                         };
352
353                         Composite contents= new Composite(parent, SWT.NULL);
354                         GridLayout layout= new GridLayout();
355                         layout.marginWidth= 0;
356                         layout.marginHeight= 0;
357                         contents.setLayout(layout);
358
359                         if (fButtonLabels != null) {
360                                 fButtonControls= new Button[fButtonLabels.length];
361                                 for (int i= 0; i < fButtonLabels.length; i++) {
362                                         String currLabel= fButtonLabels[i];
363                                         if (currLabel != null) {
364                                                 fButtonControls[i]= createButton(contents, currLabel, listener);
365                                                 fButtonControls[i].setEnabled(isEnabled() && fButtonsEnabled[i]);
366                                         } else {
367                                                 fButtonControls[i]= null;
368                                                 createSeparator(contents);
369                                         }
370                                 }
371                         }
372
373                         fLastSeparator= createSeparator(contents);
374
375                         updateButtonState();
376                         fButtonsControl= contents;
377                 }
378
379                 return fButtonsControl;
380         }
381
382         private void doButtonSelected(SelectionEvent e) {
383                 if (fButtonControls != null) {
384                         for (int i= 0; i < fButtonControls.length; i++) {
385                                 if (e.widget == fButtonControls[i]) {
386                                         buttonPressed(i);
387                                         return;
388                                 }
389                         }
390                 }
391         }
392
393         /**
394         * Handles key events in the table viewer. Specifically
395         * when the delete key is pressed.
396         */
397         protected void handleKeyPressed(KeyEvent event) {
398                 if (event.character == SWT.DEL && event.stateMask == 0) {
399                         if (fRemoveButtonIndex != -1 && isButtonEnabled(fTree.getSelection(), fRemoveButtonIndex)) {
400                                 managedButtonPressed(fRemoveButtonIndex);
401                                 return;
402                         }
403                 }
404                 fTreeAdapter.keyPressed(this, event);
405         }
406
407         // ------ enable / disable management
408
409         /*
410         * @see DialogField#dialogFieldChanged
411         */
412         public void dialogFieldChanged() {
413                 super.dialogFieldChanged();
414                 updateButtonState();
415         }
416
417         /*
418         * Updates the enable state of the all buttons
419         */
420         protected void updateButtonState() {
421                 if (fButtonControls != null) {
422                         ISelection sel= fTree.getSelection();
423                         for (int i= 0; i < fButtonControls.length; i++) {
424                                 Button button= fButtonControls[i];
425                                 if (isOkToUse(button)) {
426                                         button.setEnabled(isButtonEnabled(sel, i));
427                                 }
428                         }
429                 }
430         }
431         
432         
433         protected boolean containsAttributes(List selected) {
434                 for (int i= 0; i < selected.size(); i++) {
435                         if (!fElements.contains(selected.get(i))) {
436                                 return true;
437                         }
438                 }
439                 return false;
440         }
441         
442
443         protected boolean getManagedButtonState(ISelection sel, int index) {
444                 List selected= getSelectedElements();
445                 boolean hasAttributes= containsAttributes(selected);
446                 if (index == fRemoveButtonIndex) {
447                         return !selected.isEmpty() && !hasAttributes;
448                 } else if (index == fUpButtonIndex) {
449                         return !sel.isEmpty() && !hasAttributes && canMoveUp(selected);
450                 } else if (index == fDownButtonIndex) {
451                         return !sel.isEmpty() && !hasAttributes && canMoveDown(selected);
452                 }
453                 return true;
454         }
455
456         /*
457         * @see DialogField#updateEnableState
458         */
459         protected void updateEnableState() {
460                 super.updateEnableState();
461
462                 boolean enabled= isEnabled();
463                 if (isOkToUse(fTreeControl)) {
464                         if (!enabled) {
465                                 fSelectionWhenEnabled= fTree.getSelection();
466                                 selectElements(null);
467                         } else {
468                                 selectElements(fSelectionWhenEnabled);
469                                 fSelectionWhenEnabled= null;
470                         }
471                         fTreeControl.setEnabled(enabled);
472                 }
473                 updateButtonState();
474         }
475
476         /**
477         * Sets a button enabled or disabled.
478         */
479         public void enableButton(int index, boolean enable) {
480                 if (fButtonsEnabled != null && index < fButtonsEnabled.length) {
481                         fButtonsEnabled[index]= enable;
482                         updateButtonState();
483                 }
484         }
485
486         private boolean isButtonEnabled(ISelection sel, int index) {
487                 boolean extraState= getManagedButtonState(sel, index);
488                 return isEnabled() && extraState && fButtonsEnabled[index];
489         }
490
491         // ------ model access
492
493         /**
494         * Sets the elements shown in the list.
495         */
496         public void setElements(List elements) {
497                 fElements= new ArrayList(elements);
498                 refresh();
499                 if (fTree != null) {
500                         fTree.expandToLevel(fTreeExpandLevel);
501                 }
502                 dialogFieldChanged();
503         }
504
505         /**
506         * Gets the elements shown in the list.
507         * The list returned is a copy, so it can be modified by the user.
508         */
509         public List getElements() {
510                 return new ArrayList(fElements);
511         }
512
513         /**
514         * Gets the element shown at the given index.
515         */
516         public Object getElement(int index) {
517                 return fElements.get(index);
518         }
519         
520         /**
521         * Gets the index of an element in the list or -1 if element is not in list.
522     */
523         public int getIndexOfElement(Object elem) {
524                 return fElements.indexOf(elem);
525         }       
526
527         /**
528         * Replace an element.
529         */
530         public void replaceElement(Object oldElement, Object newElement) throws IllegalArgumentException {
531                 int idx= fElements.indexOf(oldElement);
532                 if (idx != -1) {
533                         fElements.set(idx, newElement);
534                         if (fTree != null) {
535                                 List selected= getSelectedElements();
536                                 if (selected.remove(oldElement)) {
537                                         selected.add(newElement);
538                                 }
539                                 boolean isExpanded= fTree.getExpandedState(oldElement);
540                                 fTree.remove(oldElement);
541                                 fTree.add(fParentElement, newElement);
542                                 if (isExpanded) {
543                                         fTree.expandToLevel(newElement, fTreeExpandLevel);
544                                 }
545                                 selectElements(new StructuredSelection(selected));
546                         }
547                         dialogFieldChanged();
548                 } else {
549                         throw new IllegalArgumentException();
550                 }
551         }
552
553         /**
554         * Adds an element at the end of the tree list.
555         */
556         public void addElement(Object element) {
557                 if (fElements.contains(element)) {
558                         return;
559                 }
560                 fElements.add(element);
561                 if (fTree != null) {
562                         fTree.add(fParentElement, element);
563                         fTree.expandToLevel(element, fTreeExpandLevel);
564                 }
565                 dialogFieldChanged();
566         }
567
568         /**
569         * Adds elements at the end of the tree list.
570         */
571         public void addElements(List elements) {
572                 int nElements= elements.size();
573
574                 if (nElements > 0) {
575                         // filter duplicated
576                         ArrayList elementsToAdd= new ArrayList(nElements);
577
578                         for (int i= 0; i < nElements; i++) {
579                                 Object elem= elements.get(i);
580                                 if (!fElements.contains(elem)) {
581                                         elementsToAdd.add(elem);
582                                 }
583                         }
584                         fElements.addAll(elementsToAdd);
585                         if (fTree != null) {
586                                 fTree.add(fParentElement, elementsToAdd.toArray());
587                                 for (int i= 0; i < elementsToAdd.size(); i++) {
588                                         fTree.expandToLevel(elementsToAdd.get(i), fTreeExpandLevel);
589                                 }
590                         }
591                         dialogFieldChanged();
592                 }
593         }
594
595         /**
596         * Adds an element at a position.
597         */
598         public void insertElementAt(Object element, int index) {
599                 if (fElements.contains(element)) {
600                         return;
601                 }
602                 fElements.add(index, element);
603                 if (fTree != null) {
604                         fTree.add(fParentElement, element);
605                         if (fTreeExpandLevel != -1) {
606                                 fTree.expandToLevel(element, fTreeExpandLevel);
607                         }
608                 }
609
610                 dialogFieldChanged();
611         }
612
613         /**
614         * Adds an element at a position.
615         */
616         public void removeAllElements() {
617                 if (fElements.size() > 0) {
618                         fElements.clear();
619                         refresh();
620                         dialogFieldChanged();
621                 }
622         }
623
624         /**
625         * Removes an element from the list.
626         */
627         public void removeElement(Object element) throws IllegalArgumentException {
628                 if (fElements.remove(element)) {
629                         if (fTree != null) {
630                                 fTree.remove(element);
631                         }
632                         dialogFieldChanged();
633                 } else {
634                         throw new IllegalArgumentException();
635                 }
636         }
637
638         /**
639         * Removes elements from the list.
640         */
641         public void removeElements(List elements) {
642                 if (elements.size() > 0) {
643                         fElements.removeAll(elements);
644                         if (fTree != null) {
645                                 fTree.remove(elements.toArray());
646                         }
647                         dialogFieldChanged();
648                 }
649         }
650
651         /**
652         * Gets the number of elements
653         */
654         public int getSize() {
655                 return fElements.size();
656         }
657
658         public void selectElements(ISelection selection) {
659                 fSelectionWhenEnabled= selection;
660                 if (fTree != null) {
661                         fTree.setSelection(selection, true);
662                 }
663         }
664
665         public void selectFirstElement() {
666                 Object element= null;
667                 if (fViewerSorter != null) {
668                         Object[] arr= fElements.toArray();
669                         fViewerSorter.sort(fTree, arr);
670                         if (arr.length > 0) {
671                                 element= arr[0];
672                         }
673                 } else {
674                         if (fElements.size() > 0) {
675                                 element= fElements.get(0);
676                         }
677                 }
678                 if (element != null) {
679                         selectElements(new StructuredSelection(element));
680                 }
681         }
682
683         public void postSetSelection(final ISelection selection) {
684                 if (isOkToUse(fTreeControl)) {
685                         Display d= fTreeControl.getDisplay();
686                         d.asyncExec(new Runnable() {
687                                 public void run() {
688                                         if (isOkToUse(fTreeControl)) {
689                                                 selectElements(selection);
690                                         }
691                                 }
692                         });
693                 }
694         }
695
696         /**
697         * Refreshes the tree.
698         */
699         public void refresh() {
700                 if (fTree != null) {
701                         fTree.refresh();
702                 }
703         }
704         
705         /**
706         * Refreshes the tree.
707         */
708         public void refresh(Object element) {
709                 if (fTree != null) {
710                         fTree.refresh(element);
711                 }
712         }       
713
714         // ------- list maintenance
715
716         private List moveUp(List elements, List move) {
717                 int nElements= elements.size();
718                 List res= new ArrayList(nElements);
719                 Object floating= null;
720                 for (int i= 0; i < nElements; i++) {
721                         Object curr= elements.get(i);
722                         if (move.contains(curr)) {
723                                 res.add(curr);
724                         } else {
725                                 if (floating != null) {
726                                         res.add(floating);
727                                 }
728                                 floating= curr;
729                         }
730                 }
731                 if (floating != null) {
732                         res.add(floating);
733                 }
734                 return res;
735         }
736
737         private void moveUp(List toMoveUp) {
738                 if (toMoveUp.size() > 0) {
739                         setElements(moveUp(fElements, toMoveUp));
740                         fTree.reveal(toMoveUp.get(0));
741                 }
742         }
743
744         private void moveDown(List toMoveDown) {
745                 if (toMoveDown.size() > 0) {
746                         setElements(reverse(moveUp(reverse(fElements), toMoveDown)));
747                         fTree.reveal(toMoveDown.get(toMoveDown.size() - 1));
748                 }
749         }
750
751         private List reverse(List p) {
752                 List reverse= new ArrayList(p.size());
753                 for (int i= p.size() - 1; i >= 0; i--) {
754                         reverse.add(p.get(i));
755                 }
756                 return reverse;
757         }
758
759         private void remove() {
760                 removeElements(getSelectedElements());
761         }
762
763         private void up() {
764                 moveUp(getSelectedElements());
765         }
766
767         private void down() {
768                 moveDown(getSelectedElements());
769         }
770
771         private boolean canMoveUp(List selectedElements) {
772                 if (isOkToUse(fTreeControl)) {
773                         int nSelected= selectedElements.size();
774                         int nElements= fElements.size();
775                         for (int i= 0; i < nElements && nSelected > 0; i++) {
776                                 if (!selectedElements.contains(fElements.get(i))) {
777                                         return true;
778                                 }
779                                 nSelected--;
780                         }
781                 }
782                 return false;
783         }
784
785         private boolean canMoveDown(List selectedElements) {
786                 if (isOkToUse(fTreeControl)) {
787                         int nSelected= selectedElements.size();                 
788                         for (int i= fElements.size() - 1; i >= 0 && nSelected > 0; i--) {
789                                 if (!selectedElements.contains(fElements.get(i))) {
790                                         return true;
791                                 }
792                                 nSelected--;                            
793                         }
794                 }
795                 return false;
796         }
797
798         /**
799         * Returns the selected elements.
800         */
801         public List getSelectedElements() {
802                 ArrayList result= new ArrayList();
803                 if (fTree != null) {
804                         ISelection selection= fTree.getSelection();
805                         if (selection instanceof IStructuredSelection) {
806                                 Iterator iter= ((IStructuredSelection)selection).iterator();
807                                 while (iter.hasNext()) {
808                                         result.add(iter.next());
809                                 }
810                         }
811                 }
812                 return result;
813         }
814         
815         public void expandElement(Object element, int level) {
816                 if (fTree != null) {
817                         fTree.expandToLevel(element, level);
818                 }
819         }
820         
821
822         // ------- TreeViewerAdapter
823
824         private class TreeViewerAdapter implements ITreeContentProvider, ISelectionChangedListener, IDoubleClickListener {
825
826                 private final Object[] NO_ELEMENTS= new Object[0];
827
828                 // ------- ITreeContentProvider Interface ------------
829
830                 public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
831                         // will never happen
832                 }
833
834                 public boolean isDeleted(Object element) {
835                         return false;
836                 }
837
838                 public void dispose() {
839                 }
840
841                 public Object[] getElements(Object obj) {
842                         return fElements.toArray();
843                 }
844                 
845                 public Object[] getChildren(Object element) {
846                         if (fTreeAdapter != null) {
847                                 return fTreeAdapter.getChildren(TreeListDialogField.this, element);
848                         }
849                         return NO_ELEMENTS;
850                 }
851
852                 public Object getParent(Object element) {
853                         if (!fElements.contains(element) && fTreeAdapter != null) {
854                                 return fTreeAdapter.getParent(TreeListDialogField.this, element);
855                         }
856                         return fParentElement;
857                 }
858
859                 public boolean hasChildren(Object element) {
860                         if (fTreeAdapter != null) {
861                                 return fTreeAdapter.hasChildren(TreeListDialogField.this, element);
862                         }
863                         return false;
864                 }               
865
866                 // ------- ISelectionChangedListener Interface ------------
867
868                 public void selectionChanged(SelectionChangedEvent event) {
869                         doListSelected(event);
870                 }
871                 
872                 /* (non-Javadoc)
873                  * @see org.eclipse.jface.viewers.IDoubleClickListener#doubleClick(org.eclipse.jface.viewers.DoubleClickEvent)
874                  */
875                 public void doubleClick(DoubleClickEvent event) {
876                         doDoubleClick(event);
877                 }               
878
879         }
880
881         protected void doListSelected(SelectionChangedEvent event) {
882                 updateButtonState();
883                 if (fTreeAdapter != null) {
884                         fTreeAdapter.selectionChanged(this);
885                 }
886         }
887         
888         protected void doDoubleClick(DoubleClickEvent event) {
889                 if (fTreeAdapter != null) {
890                         fTreeAdapter.doubleClicked(this);
891                 }
892         }
893
894         
895
896 }