KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > eclipse > jface > viewers > StructuredViewer


1 /*******************************************************************************
2  * Copyright (c) 2000, 2007 IBM Corporation and others.
3  * All rights reserved. This program and the accompanying materials
4  * are made available under the terms of the Eclipse Public License v1.0
5  * which accompanies this distribution, and is available at
6  * http://www.eclipse.org/legal/epl-v10.html
7  *
8  * Contributors:
9  * IBM Corporation - initial API and implementation
10  * Tom Schindl - bug 151205
11  *******************************************************************************/

12 package org.eclipse.jface.viewers;
13
14 import java.util.ArrayList JavaDoc;
15 import java.util.Arrays JavaDoc;
16 import java.util.Iterator JavaDoc;
17 import java.util.List JavaDoc;
18
19 import org.eclipse.core.runtime.ListenerList;
20 import org.eclipse.core.runtime.Assert;
21 import org.eclipse.jface.util.IOpenEventListener;
22 import org.eclipse.jface.util.OpenStrategy;
23 import org.eclipse.jface.util.SafeRunnable;
24 import org.eclipse.swt.custom.TableTreeItem;
25 import org.eclipse.swt.dnd.DragSource;
26 import org.eclipse.swt.dnd.DragSourceListener;
27 import org.eclipse.swt.dnd.DropTarget;
28 import org.eclipse.swt.dnd.DropTargetListener;
29 import org.eclipse.swt.dnd.Transfer;
30 import org.eclipse.swt.events.DisposeEvent;
31 import org.eclipse.swt.events.SelectionAdapter;
32 import org.eclipse.swt.events.SelectionEvent;
33 import org.eclipse.swt.events.SelectionListener;
34 import org.eclipse.swt.graphics.Color;
35 import org.eclipse.swt.graphics.Font;
36 import org.eclipse.swt.widgets.Control;
37 import org.eclipse.swt.widgets.Item;
38 import org.eclipse.swt.widgets.TableItem;
39 import org.eclipse.swt.widgets.TreeItem;
40 import org.eclipse.swt.widgets.Widget;
41
42 /**
43  * Abstract base implementation for structure-oriented viewers (trees, lists,
44  * tables). Supports custom sorting, filtering, and rendering.
45  * <p>
46  * Any number of viewer filters can be added to this viewer (using
47  * <code>addFilter</code>). When the viewer receives an update, it asks each
48  * of its filters if it is out of date, and refilters elements as required.
49  * </p>
50  *
51  * @see ViewerFilter
52  * @see ViewerComparator
53  */

54 public abstract class StructuredViewer extends ContentViewer implements IPostSelectionProvider {
55
56     /**
57      * A map from the viewer's model elements to SWT widgets. (key type:
58      * <code>Object</code>, value type: <code>Widget</code>, or <code>Widget[]</code>).
59      * <code>null</code> means that the element map is disabled.
60      */

61     private CustomHashtable elementMap;
62
63     /**
64      * The comparer to use for comparing elements, or <code>null</code> to use
65      * the default <code>equals</code> and <code>hashCode</code> methods on
66      * the element itself.
67      */

68     private IElementComparer comparer;
69
70     /**
71      * This viewer's comparator used for sorting. <code>null</code> means there is no comparator.
72      */

73     private ViewerComparator sorter;
74
75     /**
76      * This viewer's filters (element type: <code>ViewerFilter</code>).
77      * <code>null</code> means there are no filters.
78      */

79     private List JavaDoc filters;
80
81     /**
82      * Indicates whether a selection change is in progress on this viewer.
83      *
84      * @see #setSelection(ISelection, boolean)
85      */

86     private boolean inChange;
87
88     /**
89      * Used while a selection change is in progress on this viewer to indicates
90      * whether the selection should be restored.
91      *
92      * @see #setSelection(ISelection, boolean)
93      */

94     private boolean restoreSelection;
95
96     /**
97      * List of double-click state listeners (element type:
98      * <code>IDoubleClickListener</code>).
99      *
100      * @see #fireDoubleClick
101      */

102     private ListenerList doubleClickListeners = new ListenerList();
103
104     /**
105      * List of open listeners (element type:
106      * <code>ISelectionActivateListener</code>).
107      *
108      * @see #fireOpen
109      */

110     private ListenerList openListeners = new ListenerList();
111
112     /**
113      * List of post selection listeners (element type:
114      * <code>ISelectionActivateListener</code>).
115      *
116      * @see #firePostSelectionChanged
117      */

118     private ListenerList postSelectionChangedListeners = new ListenerList();
119
120     /**
121      * The colorAndFontCollector is an object used by viewers that
122      * support the IColorProvider, the IFontProvider and/or the
123      * IViewerLabelProvider for color and font updates.
124      * Initialize it to have no color or font providing
125      * initially.
126      * @since 3.1
127      */

128     private ColorAndFontCollector colorAndFontCollector = new ColorAndFontCollector();
129     
130     /**
131      * Empty array of widgets.
132      */

133     private static Widget[] NO_WIDGETS = new Widget[0];
134
135     /**
136      * The ColorAndFontCollector is a helper class for viewers
137      * that have color and font support ad optionally decorators.
138      * @see IColorDecorator
139      * @see IFontDecorator
140      * @see IColorProvider
141      * @see IFontProvider
142      * @see IDecoration
143      */

144     protected class ColorAndFontCollectorWithProviders extends ColorAndFontCollector{
145
146         IColorProvider colorProvider;
147
148         IFontProvider fontProvider;
149         
150         /**
151          * Create a new instance of the receiver using the supplied
152          * label provider. If it is an IColorProvider or IFontProvider
153          * set these values up.
154          * @param provider IBaseLabelProvider
155          * @see IColorProvider
156          * @see IFontProvider
157          */

158         public ColorAndFontCollectorWithProviders(IBaseLabelProvider provider) {
159             super();
160             if (provider instanceof IColorProvider) {
161                 colorProvider = (IColorProvider) provider;
162             }
163             if (provider instanceof IFontProvider) {
164                 fontProvider = (IFontProvider) provider;
165             }
166         }
167         
168     
169         /* (non-Javadoc)
170          * @see org.eclipse.jface.viewers.StructuredViewer.ColorAndFontManager#setFontsAndColors(java.lang.Object)
171          */

172         public void setFontsAndColors(Object JavaDoc element){
173             
174             if(fontProvider != null){
175                 if(font == null) {
176                     font = fontProvider.getFont(element);
177                 }
178             }
179             
180             if(colorProvider == null) {
181                 return;
182             }
183             //Set the colors if they are not set yet
184
if(background == null) {
185                 background = colorProvider.getBackground(element);
186             }
187             
188             if(foreground == null) {
189                 foreground = colorProvider.getForeground(element);
190             }
191         }
192         
193         /**
194          * Apply the fonts and colors to the control if
195          * required.
196          * @param control
197          */

198         public void applyFontsAndColors(TableItem control) {
199             
200             if(colorProvider == null){
201                 if(usedDecorators){
202                     //If there is no provider only apply set values
203
if(background != null) {
204                         control.setBackground(background);
205                     }
206                 
207                     if(foreground != null) {
208                         control.setForeground(foreground);
209                     }
210                 }
211             }
212             else{
213                 //Always set the value if there is a provider
214
control.setBackground(background);
215                 control.setForeground(foreground);
216             }
217             
218             if(fontProvider == null){
219                 if(usedDecorators && font != null) {
220                     control.setFont(font);
221                 }
222             } else {
223                 control.setFont(font);
224             }
225             
226             clear();
227         }
228         
229
230     
231         /* (non-Javadoc)
232          * @see org.eclipse.jface.viewers.StructuredViewer.ColorAndFontManager#applyFontsAndColors(org.eclipse.swt.widgets.TreeItem)
233          */

234         public void applyFontsAndColors(TreeItem control) {
235             
236             if(colorProvider == null){
237                 if(usedDecorators){
238                     //If there is no provider only apply set values
239
if(background != null) {
240                         control.setBackground(background);
241                     }
242                 
243                     if(foreground != null) {
244                         control.setForeground(foreground);
245                     }
246                 }
247             }
248             else{
249                 //Always set the value if there is a provider
250
control.setBackground(background);
251                 control.setForeground(foreground);
252             }
253             
254             if(fontProvider == null){
255                 if(usedDecorators && font != null) {
256                     control.setFont(font);
257                 }
258             } else {
259                 control.setFont(font);
260             }
261             
262             clear();
263         }
264         
265     
266         /* (non-Javadoc)
267          * @see org.eclipse.jface.viewers.StructuredViewer.ColorAndFontManager#applyFontsAndColors(org.eclipse.swt.custom.TableTreeItem)
268          */

269         public void applyFontsAndColors(TableTreeItem control) {
270             
271             if(colorProvider == null){
272                 if(usedDecorators){
273                     //If there is no provider only apply set values
274
if(background != null) {
275                         control.setBackground(background);
276                     }
277                 
278                     if(foreground != null) {
279                         control.setForeground(foreground);
280                     }
281                 }
282             }
283             else{
284                 //Always set the value if there is a provider
285
control.setBackground(background);
286                 control.setForeground(foreground);
287             }
288             
289             if(fontProvider == null){
290                 if(usedDecorators && font != null) {
291                     control.setFont(font);
292                 }
293             } else {
294                 control.setFont(font);
295             }
296             
297             clear();
298         }
299         
300         
301     }
302     
303     /**
304      * The ColorAndFontManager collects fonts and colors without a
305      * a color or font provider.
306      *
307      */

308     protected class ColorAndFontCollector {
309
310         Color foreground = null;
311
312         Color background = null;
313
314         Font font = null;
315
316         boolean usedDecorators = false;
317
318         /**
319          * Create a new instance of the receiver with
320          * no color and font provider.
321          */

322         public ColorAndFontCollector(){
323             super();
324         }
325         
326
327         /**
328          * Clear all of the results.
329          */

330         public void clear() {
331             foreground = null;
332             background = null;
333             font = null;
334             usedDecorators = false;
335         }
336
337         
338         /**
339          * Set the initial fonts and colors for the element from the
340          * content providers.
341          * @param element Object
342          */

343         public void setFontsAndColors(Object JavaDoc element){
344             //Do nothing if there are no providers
345
}
346
347         /**
348          * Set that decorators were applied.
349          */

350         public void setUsedDecorators() {
351             this.usedDecorators = true;
352         }
353
354         /**
355          * Apply the fonts and colors to the control if
356          * required.
357          * @param control
358          */

359         public void applyFontsAndColors(TableItem control) {
360             
361             if(usedDecorators){
362                 //If there is no provider only apply set values
363
if(background != null) {
364                     control.setBackground(background);
365                 }
366             
367                 if(foreground != null) {
368                     control.setForeground(foreground);
369                 }
370         
371                 if(font != null) {
372                     control.setFont(font);
373                 }
374             }
375             clear();
376         }
377         
378         /**
379          * Apply the fonts and colors to the control if
380          * required.
381          * @param control
382          */

383         public void applyFontsAndColors(TreeItem control) {
384             if(usedDecorators){
385                 //If there is no provider only apply set values
386
if(background != null) {
387                     control.setBackground(background);
388                 }
389             
390                 if(foreground != null) {
391                     control.setForeground(foreground);
392                 }
393         
394                 if(font != null) {
395                     control.setFont(font);
396                 }
397             }
398             clear();
399         }
400         
401         /**
402          * Apply the fonts and colors to the control if
403          * required.
404          * @param control
405          */

406         public void applyFontsAndColors(TableTreeItem control) {
407             if(usedDecorators){
408                 //If there is no provider only apply set values
409
if(background != null) {
410                     control.setBackground(background);
411                 }
412             
413                 if(foreground != null) {
414                     control.setForeground(foreground);
415                 }
416         
417                 if(font != null) {
418                     control.setFont(font);
419                 }
420             }
421             clear();
422         }
423         
424         /**
425          * Set the background color.
426          * @param background
427          */

428         public void setBackground(Color background) {
429             this.background = background;
430         }
431         /**
432          * Set the font.
433          * @param font
434          */

435         public void setFont(Font font) {
436             this.font = font;
437         }
438         /**
439          * Set the foreground color.
440          * @param foreground
441          */

442         public void setForeground(Color foreground) {
443             this.foreground = foreground;
444         }
445     
446         
447     }
448
449     /**
450      * The safe runnable used to update an item.
451      */

452     class UpdateItemSafeRunnable extends SafeRunnable {
453         private Widget widget;
454
455         private Object JavaDoc element;
456
457         private boolean fullMap;
458
459         UpdateItemSafeRunnable(Widget widget, Object JavaDoc element, boolean fullMap) {
460             this.widget = widget;
461             this.element = element;
462             this.fullMap = fullMap;
463         }
464
465         public void run() {
466             doUpdateItem(widget, element, fullMap);
467         }
468     }
469     
470     /**
471      * Creates a structured element viewer. The viewer has no input, no content
472      * provider, a default label provider, no sorter, and no filters.
473      */

474     protected StructuredViewer() {
475         // do nothing
476
}
477
478     /**
479      * Adds a listener for double-clicks in this viewer. Has no effect if an
480      * identical listener is already registered.
481      *
482      * @param listener
483      * a double-click listener
484      */

485     public void addDoubleClickListener(IDoubleClickListener listener) {
486         doubleClickListeners.add(listener);
487     }
488
489     /**
490      * Adds a listener for selection-open in this viewer. Has no effect if an
491      * identical listener is already registered.
492      *
493      * @param listener
494      * a double-click listener
495      */

496     public void addOpenListener(IOpenListener listener) {
497         openListeners.add(listener);
498     }
499
500     /*
501      * (non-Javadoc) Method declared on IPostSelectionProvider.
502      */

503     public void addPostSelectionChangedListener(ISelectionChangedListener listener) {
504         postSelectionChangedListeners.add(listener);
505     }
506
507     /**
508      * Adds support for dragging items out of this viewer via a user
509      * drag-and-drop operation.
510      *
511      * @param operations
512      * a bitwise OR of the supported drag and drop operation types (
513      * <code>DROP_COPY</code>,<code>DROP_LINK</code>, and
514      * <code>DROP_MOVE</code>)
515      * @param transferTypes
516      * the transfer types that are supported by the drag operation
517      * @param listener
518      * the callback that will be invoked to set the drag data and to
519      * cleanup after the drag and drop operation finishes
520      * @see org.eclipse.swt.dnd.DND
521      */

522     public void addDragSupport(int operations, Transfer[] transferTypes, DragSourceListener listener) {
523
524         Control myControl = getControl();
525         final DragSource dragSource = new DragSource(myControl, operations);
526         dragSource.setTransfer(transferTypes);
527         dragSource.addDragListener(listener);
528     }
529
530     /**
531      * Adds support for dropping items into this viewer via a user drag-and-drop
532      * operation.
533      *
534      * @param operations
535      * a bitwise OR of the supported drag and drop operation types (
536      * <code>DROP_COPY</code>,<code>DROP_LINK</code>, and
537      * <code>DROP_MOVE</code>)
538      * @param transferTypes
539      * the transfer types that are supported by the drop operation
540      * @param listener
541      * the callback that will be invoked after the drag and drop
542      * operation finishes
543      * @see org.eclipse.swt.dnd.DND
544      */

545     public void addDropSupport(int operations, Transfer[] transferTypes,
546             final DropTargetListener listener) {
547         Control control = getControl();
548         DropTarget dropTarget = new DropTarget(control, operations);
549         dropTarget.setTransfer(transferTypes);
550         dropTarget.addDropListener(listener);
551     }
552
553     /**
554      * Adds the given filter to this viewer, and triggers refiltering and
555      * resorting of the elements. If you want to add more than one filter
556      * consider using {@link StructuredViewer#setFilters(ViewerFilter[])}.
557      *
558      * @param filter
559      * a viewer filter
560      * @see StructuredViewer#setFilters(ViewerFilter[])
561      */

562     public void addFilter(ViewerFilter filter) {
563         if (filters == null) {
564             filters = new ArrayList JavaDoc();
565         }
566         filters.add(filter);
567         refresh();
568     }
569
570     /**
571      * Asserts that the given array of elements is itself non- <code>null</code>
572      * and contains no <code>null</code> elements.
573      *
574      * @param elements
575      * the array to check
576      */

577     protected void assertElementsNotNull(Object JavaDoc[] elements) {
578         Assert.isNotNull(elements);
579         for (int i = 0, n = elements.length; i < n; ++i) {
580             Assert.isNotNull(elements[i]);
581         }
582     }
583
584     /**
585      * Associates the given element with the given widget. Sets the given item's
586      * data to be the element, and maps the element to the item in the element
587      * map (if enabled).
588      *
589      * @param element
590      * the element
591      * @param item
592      * the widget
593      */

594     protected void associate(Object JavaDoc element, Item item) {
595         Object JavaDoc data = item.getData();
596         if (data != element) {
597             if (data != null) {
598                 disassociate(item);
599             }
600             item.setData(element);
601         }
602         // Always map the element, even if data == element,
603
// since unmapAllElements() can leave the map inconsistent
604
// See bug 2741 for details.
605
mapElement(element, item);
606     }
607
608     /**
609      * Disassociates the given SWT item from its corresponding element. Sets the
610      * item's data to <code>null</code> and removes the element from the
611      * element map (if enabled).
612      *
613      * @param item
614      * the widget
615      */

616     protected void disassociate(Item item) {
617         Object JavaDoc element = item.getData();
618         Assert.isNotNull(element);
619         //Clear the map before we clear the data
620
unmapElement(element, item);
621         item.setData(null);
622     }
623
624     /**
625      * Returns the widget in this viewer's control which represents the given
626      * element if it is the viewer's input.
627      * <p>
628      * This method is internal to the framework; subclassers should not call
629      * this method.
630      * </p>
631      *
632      * @param element
633      * @return the corresponding widget, or <code>null</code> if none
634      */

635     protected abstract Widget doFindInputItem(Object JavaDoc element);
636
637     /**
638      * Returns the widget in this viewer's control which represent the given
639      * element. This method searches all the children of the input element.
640      * <p>
641      * This method is internal to the framework; subclassers should not call
642      * this method.
643      * </p>
644      *
645      * @param element
646      * @return the corresponding widget, or <code>null</code> if none
647      */

648     protected abstract Widget doFindItem(Object JavaDoc element);
649
650     /**
651      * Copies the attributes of the given element into the given SWT item. The
652      * element map is updated according to the value of <code>fullMap</code>.
653      * If <code>fullMap</code> is <code>true</code> then the current mapping
654      * from element to widgets is removed and the new mapping is added. If
655      * full map is <code>false</code> then only the new map gets installed.
656      * Installing only the new map is necessary in cases where only the order of
657      * elements changes but not the set of elements.
658      * <p>
659      * This method is internal to the framework; subclassers should not call
660      * this method.
661      * </p>
662      *
663      * @param item
664      * @param element element
665      * @param fullMap
666      * <code>true</code> if mappings are added and removed, and
667      * <code>false</code> if only the new map gets installed
668      */

669     protected abstract void doUpdateItem(Widget item, Object JavaDoc element, boolean fullMap);
670
671     /**
672      * Compares two elements for equality. Uses the element comparer if one has
673      * been set, otherwise uses the default <code>equals</code> method on the
674      * elements themselves.
675      *
676      * @param elementA
677      * the first element
678      * @param elementB
679      * the second element
680      * @return whether elementA is equal to elementB
681      */

682     protected boolean equals(Object JavaDoc elementA, Object JavaDoc elementB) {
683         if (comparer == null) {
684             return elementA == null ? elementB == null : elementA.equals(elementB);
685         } else {
686             return elementA == null ? elementB == null : comparer.equals(elementA, elementB);
687         }
688     }
689
690     /**
691      * Returns the result of running the given elements through the filters.
692      *
693      * @param elements
694      * the elements to filter
695      * @return only the elements which all filters accept
696      */

697     protected Object JavaDoc[] filter(Object JavaDoc[] elements) {
698         if (filters != null) {
699             ArrayList JavaDoc filtered = new ArrayList JavaDoc(elements.length);
700             Object JavaDoc root = getRoot();
701             for (int i = 0; i < elements.length; i++) {
702                 boolean add = true;
703                 for (int j = 0; j < filters.size(); j++) {
704                     add = ((ViewerFilter) filters.get(j)).select(this, root, elements[i]);
705                     if (!add) {
706                         break;
707                     }
708                 }
709                 if (add) {
710                     filtered.add(elements[i]);
711                 }
712             }
713             return filtered.toArray();
714         }
715         return elements;
716     }
717
718     /**
719      * Finds the widget which represents the given element.
720      * <p>
721      * The default implementation of this method tries first to find the widget
722      * for the given element assuming that it is the viewer's input; this is
723      * done by calling <code>doFindInputItem</code>. If it is not found
724      * there, it is looked up in the internal element map provided that this
725      * feature has been enabled. If the element map is disabled, the widget is
726      * found via <code>doFindInputItem</code>.
727      * </p>
728      *
729      * @param element
730      * the element
731      * @return the corresponding widget, or <code>null</code> if none
732      */

733     protected final Widget findItem(Object JavaDoc element) {
734         Widget[] result = findItems(element);
735         return result.length == 0 ? null : result[0];
736     }
737
738     /**
739      * Finds the widgets which represent the given element. The returned array
740      * must not be changed by clients; it might change upon calling other
741      * methods on this viewer.
742      * <p>
743      * This method was introduced to support multiple equal elements in a viewer
744      * (@see {@link AbstractTreeViewer}). Multiple equal elements are only
745      * supported if the element map is enabled by calling
746      * {@link #setUseHashlookup(boolean)} and passing <code>true</code>.
747      * </p>
748      * <p>
749      * The default implementation of this method tries first to find the widget
750      * for the given element assuming that it is the viewer's input; this is
751      * done by calling <code>doFindInputItem</code>. If it is not found
752      * there, the widgets are looked up in the internal element map provided
753      * that this feature has been enabled. If the element map is disabled, the
754      * widget is found via <code>doFindInputItem</code>.
755      * </p>
756      *
757      * @param element
758      * the element
759      * @return the corresponding widgets
760      *
761      * @since 3.2
762      */

763     protected final Widget[] findItems(Object JavaDoc element) {
764         Widget result = doFindInputItem(element);
765         if (result != null) {
766             return new Widget[] { result };
767         }
768         // if we have an element map use it, otherwise search for the item.
769
if (usingElementMap()) {
770             Object JavaDoc widgetOrWidgets = elementMap.get(element);
771             if (widgetOrWidgets==null) {
772                 return NO_WIDGETS;
773             } else if (widgetOrWidgets instanceof Widget) {
774                 return new Widget[] {(Widget) widgetOrWidgets};
775             } else {
776                 return (Widget[])widgetOrWidgets;
777             }
778         }
779         result = doFindItem(element);
780         return result == null ? NO_WIDGETS : new Widget[] { result };
781     }
782
783     /**
784      * Notifies any double-click listeners that a double-click has been
785      * received. Only listeners registered at the time this method is called are
786      * notified.
787      *
788      * @param event
789      * a double-click event
790      *
791      * @see IDoubleClickListener#doubleClick
792      */

793     protected void fireDoubleClick(final DoubleClickEvent event) {
794         Object JavaDoc[] listeners = doubleClickListeners.getListeners();
795         for (int i = 0; i < listeners.length; ++i) {
796             final IDoubleClickListener l = (IDoubleClickListener) listeners[i];
797             SafeRunnable.run(new SafeRunnable() {
798                 public void run() {
799                     l.doubleClick(event);
800                 }
801             });
802         }
803     }
804
805     /**
806      * Notifies any open event listeners that a open event has been received.
807      * Only listeners registered at the time this method is called are notified.
808      *
809      * @param event
810      * a double-click event
811      *
812      * @see IOpenListener#open(OpenEvent)
813      */

814     protected void fireOpen(final OpenEvent event) {
815         Object JavaDoc[] listeners = openListeners.getListeners();
816         for (int i = 0; i < listeners.length; ++i) {
817             final IOpenListener l = (IOpenListener) listeners[i];
818             SafeRunnable.run(new SafeRunnable() {
819                 public void run() {
820                     l.open(event);
821                 }
822             });
823         }
824     }
825
826     /**
827      * Notifies any post selection listeners that a post selection event has
828      * been received. Only listeners registered at the time this method is
829      * called are notified.
830      *
831      * @param event
832      * a selection changed event
833      *
834      * @see #addPostSelectionChangedListener(ISelectionChangedListener)
835      */

836     protected void firePostSelectionChanged(final SelectionChangedEvent event) {
837         Object JavaDoc[] listeners = postSelectionChangedListeners.getListeners();
838         for (int i = 0; i < listeners.length; ++i) {
839             final ISelectionChangedListener l = (ISelectionChangedListener) listeners[i];
840             SafeRunnable.run(new SafeRunnable() {
841                 public void run() {
842                     l.selectionChanged(event);
843                 }
844             });
845         }
846     }
847
848     /**
849      * Returns the comparer to use for comparing elements, or
850      * <code>null</code> if none has been set. If specified,
851      * the viewer uses this to compare and hash elements rather
852      * than the elements' own equals and hashCode methods.
853      *
854      * @return the comparer to use for comparing elements or
855      * <code>null</code>
856      */

857     public IElementComparer getComparer() {
858         return comparer;
859     }
860
861     /**
862      * Returns the filtered array of children of the given element. The
863      * resulting array must not be modified, as it may come directly from the
864      * model's internal state.
865      *
866      * @param parent
867      * the parent element
868      * @return a filtered array of child elements
869      */

870     protected Object JavaDoc[] getFilteredChildren(Object JavaDoc parent) {
871         Object JavaDoc[] result = getRawChildren(parent);
872         if (filters != null) {
873             for (Iterator JavaDoc iter = filters.iterator(); iter.hasNext();) {
874                 ViewerFilter f = (ViewerFilter) iter.next();
875                 result = f.filter(this, parent, result);
876             }
877         }
878         return result;
879     }
880
881     /**
882      * Returns this viewer's filters.
883      *
884      * @return an array of viewer filters
885      * @see StructuredViewer#setFilters(ViewerFilter[])
886      */

887     public ViewerFilter[] getFilters() {
888         if (filters == null) {
889             return new ViewerFilter[0];
890         }
891         ViewerFilter[] result = new ViewerFilter[filters.size()];
892         filters.toArray(result);
893         return result;
894     }
895
896     /**
897      * Returns the item at the given display-relative coordinates, or
898      * <code>null</code> if there is no item at that location or
899      * the underlying SWT-Control is not made up of {@link Item}
900      * (e.g {@link ListViewer})
901      * <p>
902      * The default implementation of this method returns <code>null</code>.
903      * </p>
904      *
905      * @param x
906      * horizontal coordinate
907      * @param y
908      * vertical coordinate
909      * @return the item, or <code>null</code> if there is no item at the given
910      * coordinates
911      * @deprecated This method is deprecated in 3.3 in favor of {@link ColumnViewer#getItemAt(org.eclipse.swt.graphics.Point)}.
912      * Viewers who are not subclasses of {@link ColumnViewer} should consider using a
913      * widget relative implementation like {@link ColumnViewer#getItemAt(org.eclipse.swt.graphics.Point)}.
914      *
915      */

916     protected Item getItem(int x, int y) {
917         return null;
918     }
919
920     /**
921      * Returns the children of the given parent without sorting and filtering
922      * them. The resulting array must not be modified, as it may come directly
923      * from the model's internal state.
924      * <p>
925      * Returns an empty array if the given parent is <code>null</code>.
926      * </p>
927      *
928      * @param parent
929      * the parent element
930      * @return the child elements
931      */

932     protected Object JavaDoc[] getRawChildren(Object JavaDoc parent) {
933         Object JavaDoc[] result = null;
934         if (parent != null) {
935             IStructuredContentProvider cp = (IStructuredContentProvider) getContentProvider();
936             if (cp != null) {
937                 result = cp.getElements(parent);
938                 assertElementsNotNull(result);
939             }
940         }
941         return (result != null) ? result : new Object JavaDoc[0];
942     }
943
944     /**
945      * Returns the root element.
946      * <p>
947      * The default implementation of this framework method forwards to
948      * <code>getInput</code>. Override if the root element is different from
949      * the viewer's input element.
950      * </p>
951      *
952      * @return the root element, or <code>null</code> if none
953      */

954     protected Object JavaDoc getRoot() {
955         return getInput();
956     }
957
958     /**
959      * The <code>StructuredViewer</code> implementation of this method returns
960      * the result as an <code>IStructuredSelection</code>.
961      * <p>
962      * Subclasses do not typically override this method, but implement
963      * <code>getSelectionFromWidget(List)</code> instead.
964      * <p>
965      * @return ISelection
966      */

967     public ISelection getSelection() {
968         Control control = getControl();
969         if (control == null || control.isDisposed()) {
970             return StructuredSelection.EMPTY;
971         }
972         List JavaDoc list = getSelectionFromWidget();
973         return new StructuredSelection(list);
974     }
975
976     /**
977      * Retrieves the selection, as a <code>List</code>, from the underlying
978      * widget.
979      *
980      * @return the list of selected elements
981      */

982     protected abstract List JavaDoc getSelectionFromWidget();
983
984     /**
985      * Returns the sorted and filtered set of children of the given element. The
986      * resulting array must not be modified, as it may come directly from the
987      * model's internal state.
988      *
989      * @param parent
990      * the parent element
991      * @return a sorted and filtered array of child elements
992      */

993     protected Object JavaDoc[] getSortedChildren(Object JavaDoc parent) {
994         Object JavaDoc[] result = getFilteredChildren(parent);
995         if (sorter != null) {
996             // be sure we're not modifying the original array from the model
997
result = (Object JavaDoc[]) result.clone();
998             sorter.sort(this, result);
999         }
1000        return result;
1001    }
1002
1003    /**
1004     * Returns this viewer's sorter, or <code>null</code> if it does not have
1005     * one. If this viewer has a comparator that was set via
1006     * <code>setComparator(ViewerComparator)</code> then this method will return
1007     * <code>null</code> if the comparator is not an instance of ViewerSorter.
1008     * <p>
1009     * It is recommended to use <code>getComparator()</code> instead.
1010     * </p>
1011     *
1012     * @return a viewer sorter, or <code>null</code> if none or if the comparator is
1013     * not an instance of ViewerSorter
1014     */

1015    public ViewerSorter getSorter() {
1016        if (sorter instanceof ViewerSorter)
1017            return (ViewerSorter)sorter;
1018        return null;
1019    }
1020
1021    /**
1022     * Return this viewer's comparator used to sort elements.
1023     * This method should be used instead of <code>getSorter()</code>.
1024     *
1025     * @return a viewer comparator, or <code>null</code> if none
1026     *
1027     * @since 3.2
1028     */

1029    public ViewerComparator getComparator(){
1030        return sorter;
1031    }
1032    
1033    /**
1034     * Handles a double-click select event from the widget.
1035     * <p>
1036     * This method is internal to the framework; subclassers should not call
1037     * this method.
1038     * </p>
1039     *
1040     * @param event
1041     * the SWT selection event
1042     */

1043    protected void handleDoubleSelect(SelectionEvent event) {
1044        // This method is reimplemented in AbstractTreeViewer to fix bug 108102.
1045

1046        // handle case where an earlier selection listener disposed the control.
1047
Control control = getControl();
1048        if (control != null && !control.isDisposed()) {
1049            // If the double-clicked element can be obtained from the event, use it
1050
// otherwise get it from the control. Some controls like List do
1051
// not have the notion of item.
1052
// For details, see bug 90161 [Navigator] DefaultSelecting folders shouldn't always expand first one
1053
ISelection selection;
1054            if (event.item != null && event.item.getData() != null) {
1055                selection = new StructuredSelection(event.item.getData());
1056            }
1057            else {
1058                selection = getSelection();
1059                updateSelection(selection);
1060            }
1061            fireDoubleClick(new DoubleClickEvent(this, selection));
1062        }
1063    }
1064
1065    /**
1066     * Handles an open event from the OpenStrategy.
1067     * <p>
1068     * This method is internal to the framework; subclassers should not call
1069     * this method.
1070     * </p>
1071     *
1072     * @param event
1073     * the SWT selection event
1074     */

1075    protected void handleOpen(SelectionEvent event) {
1076        Control control = getControl();
1077        if (control != null && !control.isDisposed()) {
1078            ISelection selection = getSelection();
1079            fireOpen(new OpenEvent(this, selection));
1080        }
1081    }
1082
1083    /**
1084     * Handles an invalid selection.
1085     * <p>
1086     * This framework method is called if a model change picked up by a viewer
1087     * results in an invalid selection. For instance if an element contained in
1088     * the selection has been removed from the viewer, the viewer is free to
1089     * either remove the element from the selection or to pick another element
1090     * as its new selection. The default implementation of this method calls
1091     * <code>updateSelection</code>. Subclasses may override it to implement
1092     * a different strategy for picking a new selection when the old selection
1093     * becomes invalid.
1094     * </p>
1095     *
1096     * @param invalidSelection
1097     * the selection before the viewer was updated
1098     * @param newSelection
1099     * the selection after the update, or <code>null</code> if none
1100     */

1101    protected void handleInvalidSelection(ISelection invalidSelection, ISelection newSelection) {
1102        updateSelection(newSelection);
1103        SelectionChangedEvent event = new SelectionChangedEvent(this, newSelection);
1104        firePostSelectionChanged(event);
1105    }
1106
1107    /**
1108     * The <code>StructuredViewer</code> implementation of this
1109     * <code>ContentViewer</code> method calls <code>update</code> if the
1110     * event specifies that the label of a given element has changed, otherwise
1111     * it calls super. Subclasses may reimplement or extend.
1112     * </p>
1113     * @param event the event that generated this update
1114     */

1115    protected void handleLabelProviderChanged(LabelProviderChangedEvent event) {
1116        Object JavaDoc[] elements = event.getElements();
1117        if (elements != null) {
1118            update(elements, null);
1119        } else {
1120            super.handleLabelProviderChanged(event);
1121        }
1122    }
1123
1124    /**
1125     * Handles a select event from the widget.
1126     * <p>
1127     * This method is internal to the framework; subclassers should not call
1128     * this method.
1129     * </p>
1130     *
1131     * @param event
1132     * the SWT selection event
1133     */

1134    protected void handleSelect(SelectionEvent event) {
1135        // handle case where an earlier selection listener disposed the control.
1136
Control control = getControl();
1137        if (control != null && !control.isDisposed()) {
1138            updateSelection(getSelection());
1139        }
1140    }
1141
1142    /**
1143     * Handles a post select event from the widget.
1144     * <p>
1145     * This method is internal to the framework; subclassers should not call
1146     * this method.
1147     * </p>
1148     *
1149     * @param e the SWT selection event
1150     */

1151    protected void handlePostSelect(SelectionEvent e) {
1152        SelectionChangedEvent event = new SelectionChangedEvent(this, getSelection());
1153        firePostSelectionChanged(event);
1154    }
1155
1156    /*
1157     * (non-Javadoc) Method declared on Viewer.
1158     */

1159    protected void hookControl(Control control) {
1160        super.hookControl(control);
1161        OpenStrategy handler = new OpenStrategy(control);
1162        handler.addSelectionListener(new SelectionListener() {
1163            public void widgetSelected(SelectionEvent e) {
1164                // On Windows, selection events may happen during a refresh.
1165
// Ignore these events if we are currently in preservingSelection().
1166
// See bug 184441.
1167
if (!inChange) {
1168                    handleSelect(e);
1169                }
1170            }
1171
1172            public void widgetDefaultSelected(SelectionEvent e) {
1173                handleDoubleSelect(e);
1174            }
1175        });
1176        handler.addPostSelectionListener(new SelectionAdapter() {
1177            public void widgetSelected(SelectionEvent e) {
1178                handlePostSelect(e);
1179            }
1180        });
1181        handler.addOpenListener(new IOpenEventListener() {
1182            public void handleOpen(SelectionEvent e) {
1183                StructuredViewer.this.handleOpen(e);
1184            }
1185        });
1186    }
1187
1188    /**
1189     * Returns whether this viewer has any filters.
1190     * @return boolean
1191     */

1192    protected boolean hasFilters() {
1193        return filters != null && filters.size() > 0;
1194    }
1195
1196    /**
1197     * Refreshes this viewer starting at the given element.
1198     *
1199     * @param element
1200     * the element
1201     */

1202    protected abstract void internalRefresh(Object JavaDoc element);
1203
1204    /**
1205     * Refreshes this viewer starting at the given element. Labels are updated
1206     * as described in <code>refresh(boolean updateLabels)</code>.
1207     * <p>
1208     * The default implementation simply calls
1209     * <code>internalRefresh(element)</code>, ignoring
1210     * <code>updateLabels</code>.
1211     * <p>
1212     * If this method is overridden to do the actual refresh, then
1213     * <code>internalRefresh(Object element)</code> should simply call
1214     * <code>internalRefresh(element, true)</code>.
1215     *
1216     * @param element
1217     * the element
1218     * @param updateLabels
1219     * <code>true</code> to update labels for existing elements,
1220     * <code>false</code> to only update labels as needed, assuming
1221     * that labels for existing elements are unchanged.
1222     *
1223     * @since 2.0
1224     */

1225    protected void internalRefresh(Object JavaDoc element, boolean updateLabels) {
1226        internalRefresh(element);
1227    }
1228
1229    /**
1230     * Adds the element item pair to the element map.
1231     * <p>
1232     * This method is internal to the framework; subclassers should not call
1233     * this method.
1234     * </p>
1235     *
1236     * @param element
1237     * the element
1238     * @param item
1239     * the corresponding widget
1240     */

1241    protected void mapElement(Object JavaDoc element, Widget item) {
1242        if (elementMap != null) {
1243            Object JavaDoc widgetOrWidgets = elementMap.get(element);
1244            if (widgetOrWidgets == null) {
1245                elementMap.put(element, item);
1246            } else if (widgetOrWidgets instanceof Widget) {
1247                if (widgetOrWidgets != item) {
1248                    elementMap.put(element, new Widget[] {
1249                            (Widget) widgetOrWidgets, item });
1250                }
1251            } else {
1252                Widget[] widgets = (Widget[]) widgetOrWidgets;
1253                int indexOfItem = Arrays.asList(widgets).indexOf(item);
1254                if (indexOfItem == -1) {
1255                    int length = widgets.length;
1256                    System.arraycopy(widgets, 0,
1257                            widgets = new Widget[length + 1], 0, length);
1258                    widgets[length] = item;
1259                    elementMap.put(element, widgets);
1260                }
1261            }
1262        }
1263    }
1264
1265    /**
1266     * Determines whether a change to the given property of the given element
1267     * would require refiltering and/or resorting.
1268     * <p>
1269     * This method is internal to the framework; subclassers should not call
1270     * this method.
1271     * </p>
1272     *
1273     * @param element
1274     * the element
1275     * @param property
1276     * the property
1277     * @return <code>true</code> if refiltering is required, and
1278     * <code>false</code> otherwise
1279     */

1280    protected boolean needsRefilter(Object JavaDoc element, String JavaDoc property) {
1281        if (sorter != null && sorter.isSorterProperty(element, property)) {
1282            return true;
1283        }
1284
1285        if (filters != null) {
1286            for (int i = 0, n = filters.size(); i < n; ++i) {
1287                ViewerFilter filter = (ViewerFilter) filters.get(i);
1288                if (filter.isFilterProperty(element, property)) {
1289                    return true;
1290                }
1291            }
1292        }
1293        return false;
1294    }
1295
1296    /**
1297     * Returns a new hashtable using the given capacity and this viewer's element comparer.
1298     *
1299     * @param capacity the initial capacity of the hashtable
1300     * @return a new hashtable
1301     *
1302     * @since 3.0
1303     */

1304    CustomHashtable newHashtable(int capacity) {
1305        return new CustomHashtable(capacity, getComparer());
1306    }
1307
1308    /**
1309     * Attempts to preserves the current selection across a run of the given
1310     * code.
1311     * <p>
1312     * The default implementation of this method:
1313     * <ul>
1314     * <li>discovers the old selection (via <code>getSelection</code>)</li>
1315     * <li>runs the given runnable</li>
1316     * <li>attempts to restore the old selection (using
1317     * <code>setSelectionToWidget</code></li>
1318     * <li>rediscovers the resulting selection (via <code>getSelection</code>)
1319     * </li>
1320     * <li>calls <code>handleInvalidSelection</code> if the selection did not
1321     * take</li>
1322     * <li>calls <code>postUpdateHook</code></li>
1323     * </ul>
1324     * </p>
1325     *
1326     * @param updateCode
1327     * the code to run
1328     */

1329    protected void preservingSelection(Runnable JavaDoc updateCode) {
1330        preservingSelection(updateCode, false);
1331    }
1332
1333    /**
1334     * Attempts to preserves the current selection across a run of the given
1335     * code, with a best effort to avoid scrolling if <code>reveal</code> is false,
1336     * or to reveal the selection if <code>reveal</code> is true.
1337     * <p>
1338     * The default implementation of this method:
1339     * <ul>
1340     * <li>discovers the old selection (via <code>getSelection</code>)</li>
1341     * <li>runs the given runnable</li>
1342     * <li>attempts to restore the old selection (using
1343     * <code>setSelectionToWidget</code></li>
1344     * <li>rediscovers the resulting selection (via <code>getSelection</code>)
1345     * </li>
1346     * <li>calls <code>handleInvalidSelection</code> if the selection did not
1347     * take</li>
1348     * <li>calls <code>postUpdateHook</code></li>
1349     * </ul>
1350     * </p>
1351     *
1352     * @param updateCode
1353     * the code to run
1354     * @param reveal
1355     * <code>true</code> if the selection should be made visible,
1356     * <code>false</code> if scrolling should be avoided
1357     * @since 3.3
1358     */

1359    void preservingSelection(Runnable JavaDoc updateCode, boolean reveal) {
1360
1361        ISelection oldSelection = null;
1362        try {
1363            // preserve selection
1364
oldSelection = getSelection();
1365            inChange = restoreSelection = true;
1366
1367            // perform the update
1368
updateCode.run();
1369
1370        } finally {
1371            inChange = false;
1372
1373            // restore selection
1374
if (restoreSelection) {
1375                setSelectionToWidget(oldSelection, reveal);
1376            }
1377
1378            // send out notification if old and new differ
1379
ISelection newSelection = getSelection();
1380            if (!newSelection.equals(oldSelection)) {
1381                handleInvalidSelection(oldSelection, newSelection);
1382            }
1383        }
1384    }
1385    
1386    /*
1387     * Non-Javadoc. Method declared on Viewer.
1388     */

1389    public void refresh() {
1390        refresh(getRoot());
1391    }
1392
1393    /**
1394     * Refreshes this viewer with information freshly obtained from this
1395     * viewer's model. If <code>updateLabels</code> is <code>true</code>
1396     * then labels for otherwise unaffected elements are updated as well.
1397     * Otherwise, it assumes labels for existing elements are unchanged, and
1398     * labels are only obtained as needed (for example, for new elements).
1399     * <p>
1400     * Calling <code>refresh(true)</code> has the same effect as
1401     * <code>refresh()</code>.
1402     * <p>
1403     * Note that the implementation may still obtain labels for existing
1404     * elements even if <code>updateLabels</code> is false. The intent is
1405     * simply to allow optimization where possible.
1406     *
1407     * @param updateLabels
1408     * <code>true</code> to update labels for existing elements,
1409     * <code>false</code> to only update labels as needed, assuming
1410     * that labels for existing elements are unchanged.
1411     *
1412     * @since 2.0
1413     */

1414    public void refresh(boolean updateLabels) {
1415        refresh(getRoot(), updateLabels);
1416    }
1417
1418    /**
1419     * Refreshes this viewer starting with the given element.
1420     * <p>
1421     * Unlike the <code>update</code> methods, this handles structural changes
1422     * to the given element (e.g. addition or removal of children). If only the
1423     * given element needs updating, it is more efficient to use the
1424     * <code>update</code> methods.
1425     * </p>
1426     *
1427     * @param element
1428     * the element
1429     */

1430    public void refresh(final Object JavaDoc element) {
1431        preservingSelection(new Runnable JavaDoc() {
1432            public void run() {
1433                internalRefresh(element);
1434            }
1435        });
1436    }
1437
1438    /**
1439     * Refreshes this viewer starting with the given element. Labels are updated
1440     * as described in <code>refresh(boolean updateLabels)</code>.
1441     * <p>
1442     * Unlike the <code>update</code> methods, this handles structural changes
1443     * to the given element (e.g. addition or removal of children). If only the
1444     * given element needs updating, it is more efficient to use the
1445     * <code>update</code> methods.
1446     * </p>
1447     *
1448     * @param element
1449     * the element
1450     * @param updateLabels
1451     * <code>true</code> to update labels for existing elements,
1452     * <code>false</code> to only update labels as needed, assuming
1453     * that labels for existing elements are unchanged.
1454     *
1455     * @since 2.0
1456     */

1457    public void refresh(final Object JavaDoc element, final boolean updateLabels) {
1458        preservingSelection(new Runnable JavaDoc() {
1459            public void run() {
1460                internalRefresh(element, updateLabels);
1461            }
1462        });
1463    }
1464
1465    /**
1466     *
1467     * Refreshes the given TableItem with the given element. Calls
1468     * <code>doUpdateItem(..., false)</code>.
1469     * <p>
1470     * This method is internal to the framework; subclassers should not call
1471     * this method.
1472     * </p>
1473     * @param widget
1474     * the widget
1475     * @param element
1476     * the element
1477     */

1478    protected final void refreshItem(Widget widget, Object JavaDoc element) {
1479        SafeRunnable.run(new UpdateItemSafeRunnable(widget, element, true));
1480    }
1481
1482    /**
1483     * Removes the given open listener from this viewer. Has no affect if an
1484     * identical listener is not registered.
1485     *
1486     * @param listener
1487     * a double-click listener
1488     */

1489    public void removeOpenListener(IOpenListener listener) {
1490        openListeners.remove(listener);
1491    }
1492
1493    /*
1494     * (non-Javadoc) Method declared on IPostSelectionProvider.
1495     */

1496    public void removePostSelectionChangedListener(ISelectionChangedListener listener) {
1497        postSelectionChangedListeners.remove(listener);
1498    }
1499
1500    /**
1501     * Removes the given double-click listener from this viewer. Has no affect
1502     * if an identical listener is not registered.
1503     *
1504     * @param listener
1505     * a double-click listener
1506     */

1507    public void removeDoubleClickListener(IDoubleClickListener listener) {
1508        doubleClickListeners.remove(listener);
1509    }
1510
1511    /**
1512     * Removes the given filter from this viewer, and triggers refiltering and
1513     * resorting of the elements if required. Has no effect if the identical
1514     * filter is not registered. If you want to remove more than one filter
1515     * consider using {@link StructuredViewer#setFilters(ViewerFilter[])}.
1516     *
1517     * @param filter
1518     * a viewer filter
1519     * @see StructuredViewer#setFilters(ViewerFilter[])
1520     */

1521    public void removeFilter(ViewerFilter filter) {
1522        Assert.isNotNull(filter);
1523        if (filters != null) {
1524            // Note: can't use List.remove(Object). Use identity comparison
1525
// instead.
1526
for (Iterator JavaDoc i = filters.iterator(); i.hasNext();) {
1527                Object JavaDoc o = i.next();
1528                if (o == filter) {
1529                    i.remove();
1530                    refresh();
1531                    if (filters.size() == 0) {
1532                        filters = null;
1533                    }
1534                    return;
1535                }
1536            }
1537        }
1538    }
1539
1540    /**
1541     * Sets the filters, replacing any previous filters, and triggers
1542     * refiltering and resorting of the elements.
1543     *
1544     * @param filters
1545     * an array of viewer filters
1546     * @since 3.3
1547     */

1548    public void setFilters(ViewerFilter[] filters) {
1549        if (filters.length == 0) {
1550            resetFilters();
1551        } else {
1552            this.filters = new ArrayList JavaDoc(Arrays.asList(filters));
1553            refresh();
1554        }
1555    }
1556    
1557    /**
1558     * Discards this viewer's filters and triggers refiltering and resorting of
1559     * the elements.
1560     */

1561    public void resetFilters() {
1562        if (filters != null) {
1563            filters = null;
1564            refresh();
1565        }
1566    }
1567
1568    /**
1569     * Ensures that the given element is visible, scrolling the viewer if
1570     * necessary. The selection is unchanged.
1571     *
1572     * @param element
1573     * the element to reveal
1574     */

1575    public abstract void reveal(Object JavaDoc element);
1576
1577    /*
1578     * (non-Javadoc)
1579     * @see org.eclipse.jface.viewers.ContentViewer#setContentProvider(org.eclipse.jface.viewers.IContentProvider)
1580     */

1581    public void setContentProvider(IContentProvider provider) {
1582        assertContentProviderType(provider);
1583        super.setContentProvider(provider);
1584    }
1585
1586    /**
1587     * Assert that the content provider is of one of the
1588     * supported types.
1589     * @param provider
1590     */

1591    protected void assertContentProviderType(IContentProvider provider) {
1592        Assert.isTrue(provider instanceof IStructuredContentProvider);
1593    }
1594
1595    /*
1596     * (non-Javadoc)
1597     * @see org.eclipse.jface.viewers.Viewer#setInput(java.lang.Object)
1598     */

1599    public final void setInput(Object JavaDoc input) {
1600
1601        try {
1602            // fInChange= true;
1603

1604            unmapAllElements();
1605
1606            super.setInput(input);
1607
1608        } finally {
1609            // fInChange= false;
1610
}
1611    }
1612
1613    /*
1614     * (non-Javadoc)
1615     * @see org.eclipse.jface.viewers.Viewer#setSelection(org.eclipse.jface.viewers.ISelection, boolean)
1616     */

1617    public void setSelection(ISelection selection, boolean reveal) {
1618        /**
1619         * <p>
1620         * If the new selection differs from the current selection the hook
1621         * <code>updateSelection</code> is called.
1622         * </p>
1623         * <p>
1624         * If <code>setSelection</code> is called from within
1625         * <code>preserveSelection</code>, the call to
1626         * <code>updateSelection</code> is delayed until the end of
1627         * <code>preserveSelection</code>.
1628         * </p>
1629         * <p>
1630         * Subclasses do not typically override this method, but implement
1631         * <code>setSelectionToWidget</code> instead.
1632         * </p>
1633         */

1634        Control control = getControl();
1635        if (control == null || control.isDisposed()) {
1636            return;
1637        }
1638        if (!inChange) {
1639            setSelectionToWidget(selection, reveal);
1640            ISelection sel = getSelection();
1641            updateSelection(sel);
1642            firePostSelectionChanged(new SelectionChangedEvent(this, sel));
1643        } else {
1644            restoreSelection = false;
1645            setSelectionToWidget(selection, reveal);
1646        }
1647    }
1648
1649    /**
1650     * Parlays the given list of selected elements into selections on this
1651     * viewer's control.
1652     * <p>
1653     * Subclasses should override to set their selection based on the given list
1654     * of elements.
1655     * </p>
1656     *
1657     * @param l
1658     * list of selected elements (element type: <code>Object</code>)
1659     * or <code>null</code> if the selection is to be cleared
1660     * @param reveal
1661     * <code>true</code> if the selection is to be made visible,
1662     * and <code>false</code> otherwise
1663     */

1664    protected abstract void setSelectionToWidget(List JavaDoc l, boolean reveal);
1665
1666    /**
1667     * Converts the selection to a <code>List</code> and calls
1668     * <code>setSelectionToWidget(List, boolean)</code>. The selection is
1669     * expected to be an <code>IStructuredSelection</code> of elements. If
1670     * not, the selection is cleared.
1671     * <p>
1672     * Subclasses do not typically override this method, but implement
1673     * <code>setSelectionToWidget(List, boolean)</code> instead.
1674     *
1675     * @param selection
1676     * an IStructuredSelection of elements
1677     * @param reveal
1678     * <code>true</code> to reveal the first element in the
1679     * selection, or <code>false</code> otherwise
1680     */

1681    protected void setSelectionToWidget(ISelection selection, boolean reveal) {
1682        if (selection instanceof IStructuredSelection) {
1683            setSelectionToWidget(((IStructuredSelection) selection).toList(), reveal);
1684        } else {
1685            setSelectionToWidget((List JavaDoc) null, reveal);
1686        }
1687    }
1688
1689    /**
1690     * Sets this viewer's sorter and triggers refiltering and resorting of this
1691     * viewer's element. Passing <code>null</code> turns sorting off.
1692     * <p>
1693     * It is recommended to use <code>setComparator()</code> instead.
1694     * </p>
1695     *
1696     * @param sorter
1697     * a viewer sorter, or <code>null</code> if none
1698     */

1699    public void setSorter(ViewerSorter sorter) {
1700        if (this.sorter != sorter) {
1701            this.sorter = sorter;
1702            refresh();
1703        }
1704    }
1705
1706    /**
1707     * Sets this viewer's comparator to be used for sorting elements, and triggers refiltering and
1708     * resorting of this viewer's element. <code>null</code> turns sorting off.
1709     * To get the viewer's comparator, call <code>getComparator()</code>.
1710     * <p>
1711     * IMPORTANT: This method was introduced in 3.2. If a reference to this viewer object
1712     * is passed to clients who call <code>getSorter()<code>, null may be returned from
1713     * from that method even though the viewer is sorting its elements using the
1714     * viewer's comparator.
1715     * </p>
1716     *
1717     * @param comparator a viewer comparator, or <code>null</code> if none
1718     *
1719     * @since 3.2
1720     */

1721    public void setComparator(ViewerComparator comparator){
1722        if (this.sorter != comparator){
1723            this.sorter = comparator;
1724            refresh();
1725        }
1726    }
1727    
1728    /**
1729     * Configures whether this structured viewer uses an internal hash table to
1730     * speeds up the mapping between elements and SWT items. This must be called
1731     * before the viewer is given an input (via <code>setInput</code>).
1732     *
1733     * @param enable
1734     * <code>true</code> to enable hash lookup, and
1735     * <code>false</code> to disable it
1736     */

1737    public void setUseHashlookup(boolean enable) {
1738        Assert.isTrue(getInput() == null,
1739                "Can only enable the hash look up before input has been set");//$NON-NLS-1$
1740
if (enable) {
1741            elementMap = newHashtable(CustomHashtable.DEFAULT_CAPACITY);
1742        } else {
1743            elementMap = null;
1744        }
1745    }
1746
1747    /**
1748     * Sets the comparer to use for comparing elements, or <code>null</code>
1749     * to use the default <code>equals</code> and <code>hashCode</code>
1750     * methods on the elements themselves.
1751     *
1752     * @param comparer
1753     * the comparer to use for comparing elements or
1754     * <code>null</code>
1755     */

1756    public void setComparer(IElementComparer comparer) {
1757        this.comparer = comparer;
1758        if (elementMap != null) {
1759            elementMap = new CustomHashtable(elementMap, comparer);
1760        }
1761    }
1762
1763    /**
1764     * Hook for testing.
1765     * @param element
1766     * @return Widget
1767     */

1768    public Widget testFindItem(Object JavaDoc element) {
1769        return findItem(element);
1770    }
1771
1772    /**
1773     * Hook for testing.
1774     * @param element
1775     * @return Widget[]
1776     * @since 3.2
1777     */

1778    public Widget[] testFindItems(Object JavaDoc element) {
1779        return findItems(element);
1780    }
1781    
1782    /**
1783     * Removes all elements from the map.
1784     * <p>
1785     * This method is internal to the framework; subclassers should not call
1786     * this method.
1787     * </p>
1788     */

1789    protected void unmapAllElements() {
1790        if (elementMap != null) {
1791            elementMap = newHashtable(CustomHashtable.DEFAULT_CAPACITY);
1792        }
1793    }
1794
1795    /**
1796     * Removes the given element from the internal element to widget map. Does
1797     * nothing if mapping is disabled. If mapping is enabled, the given element
1798     * must be present.
1799     * <p>
1800     * This method is internal to the framework; subclassers should not call
1801     * this method.
1802     * </p>
1803     *
1804     * @param element
1805     * the element
1806     */

1807    protected void unmapElement(Object JavaDoc element) {
1808        if (elementMap != null) {
1809            elementMap.remove(element);
1810        }
1811    }
1812
1813    /**
1814     * Removes the given association from the internal element to widget map.
1815     * Does nothing if mapping is disabled, or if the given element does not map
1816     * to the given item.
1817     * <p>
1818     * This method is internal to the framework; subclassers should not call
1819     * this method.
1820     * </p>
1821     *
1822     * @param element
1823     * the element
1824     * @param item the item to unmap
1825     * @since 2.0
1826     */

1827    protected void unmapElement(Object JavaDoc element, Widget item) {
1828        // double-check that the element actually maps to the given item before
1829
// unmapping it
1830
if (elementMap != null) {
1831            Object JavaDoc widgetOrWidgets = elementMap.get(element);
1832            if (widgetOrWidgets == null) {
1833                // item was not mapped, return
1834
return;
1835            } else if (widgetOrWidgets instanceof Widget) {
1836                if (item == widgetOrWidgets) {
1837                    elementMap.remove(element);
1838                }
1839            } else {
1840                Widget[] widgets = (Widget[]) widgetOrWidgets;
1841                int indexOfItem = Arrays.asList(widgets).indexOf(item);
1842                if (indexOfItem == -1) {
1843                    return;
1844                }
1845                int length = widgets.length;
1846                if (indexOfItem == 0) {
1847                    if(length == 1) {
1848                        elementMap.remove(element);
1849                    } else {
1850                        Widget[] updatedWidgets = new Widget[length - 1];
1851                        System.arraycopy(widgets, 1, updatedWidgets, 0, length -1 );
1852                        elementMap.put(element, updatedWidgets);
1853                    }
1854                } else {
1855                    Widget[] updatedWidgets = new Widget[length - 1];
1856                    System.arraycopy(widgets, 0, updatedWidgets, 0, indexOfItem);
1857                    System.arraycopy(widgets, indexOfItem + 1, updatedWidgets, indexOfItem, length - indexOfItem - 1);
1858                    elementMap.put(element, updatedWidgets);
1859                }
1860            }
1861        }
1862    }
1863
1864    /**
1865     * Updates the given elements' presentation when one or more of their
1866     * properties change. Only the given elements are updated.
1867     * <p>
1868     * This does not handle structural changes (e.g. addition or removal of
1869     * elements), and does not update any other related elements (e.g. child
1870     * elements). To handle structural changes, use the <code>refresh</code>
1871     * methods instead.
1872     * </p>
1873     * <p>
1874     * This should be called when an element has changed in the model, in order
1875     * to have the viewer accurately reflect the model. This method only affects
1876     * the viewer, not the model.
1877     * </p>
1878     * <p>
1879     * Specifying which properties are affected may allow the viewer to optimize
1880     * the update. For example, if the label provider is not affected by changes
1881     * to any of these properties, an update may not actually be required.
1882     * Specifying <code>properties</code> as <code>null</code> forces a full
1883     * update of the given elements.
1884     * </p>
1885     * <p>
1886     * If the viewer has a sorter which is affected by a change to one of the
1887     * properties, the elements' positions are updated to maintain the sort
1888     * order. Note that resorting does not happen if <code>properties</code>
1889     * is <code>null</code>.
1890     * </p>
1891     * <p>
1892     * If the viewer has a filter which is affected by a change to one of the
1893     * properties, elements may appear or disappear if the change affects
1894     * whether or not they are filtered out.
1895     * </p>
1896     *
1897     * @param elements
1898     * the elements
1899     * @param properties
1900     * the properties that have changed, or <code>null</code> to
1901     * indicate unknown
1902     */

1903    public void update(Object JavaDoc[] elements, String JavaDoc[] properties) {
1904        for (int i = 0; i < elements.length; ++i) {
1905            update(elements[i], properties);
1906        }
1907    }
1908
1909    /**
1910     * Updates the given element's presentation when one or more of its
1911     * properties changes. Only the given element is updated.
1912     * <p>
1913     * This does not handle structural changes (e.g. addition or removal of
1914     * elements), and does not update any other related elements (e.g. child
1915     * elements). To handle structural changes, use the <code>refresh</code>
1916     * methods instead.
1917     * </p>
1918     * <p>
1919     * This should be called when an element has changed in the model, in order
1920     * to have the viewer accurately reflect the model. This method only affects
1921     * the viewer, not the model.
1922     * </p>
1923     * <p>
1924     * Specifying which properties are affected may allow the viewer to optimize
1925     * the update. For example, if the label provider is not affected by changes
1926     * to any of these properties, an update may not actually be required.
1927     * Specifying <code>properties</code> as <code>null</code> forces a full
1928     * update of the element.
1929     * </p>
1930     * <p>
1931     * If the viewer has a sorter which is affected by a change to one of the
1932     * properties, the element's position is updated to maintain the sort order.
1933     * Note that resorting does not happen if <code>properties</code> is
1934     * <code>null</code>.
1935     * </p>
1936     * <p>
1937     * If the viewer has a filter which is affected by a change to one of the
1938     * properties, the element may appear or disappear if the change affects
1939     * whether or not the element is filtered out.
1940     * </p>
1941     *
1942     * @param element
1943     * the element
1944     * @param properties
1945     * the properties that have changed, or <code>null</code> to
1946     * indicate unknown
1947     */

1948    public void update(Object JavaDoc element, String JavaDoc[] properties) {
1949        Assert.isNotNull(element);
1950        Widget[] items = findItems(element);
1951
1952        for (int i = 0; i < items.length; i++) {
1953            internalUpdate(items[i], element, properties);
1954        }
1955    }
1956
1957    /**
1958     * Updates the given element's presentation when one or more of its
1959     * properties changes. Only the given element is updated.
1960     * <p>
1961     * EXPERIMENTAL. Not to be used except by JDT.
1962     * This method was added to support JDT's explorations
1963     * into grouping by working sets, which requires viewers to support multiple
1964     * equal elements. See bug 76482 for more details. This support will
1965     * likely be removed in Eclipse 3.3 in favor of proper support for
1966     * multiple equal elements (which was implemented for AbtractTreeViewer in 3.2).
1967     * </p>
1968     * @param widget
1969     * the widget for the element
1970     * @param element
1971     * the element
1972     * @param properties
1973     * the properties that have changed, or <code>null</code> to
1974     * indicate unknown
1975     */

1976    protected void internalUpdate(Widget widget, Object JavaDoc element, String JavaDoc[] properties) {
1977        boolean needsRefilter = false;
1978        if (properties != null) {
1979            for (int i = 0; i < properties.length; ++i) {
1980                needsRefilter = needsRefilter(element, properties[i]);
1981                if (needsRefilter) {
1982                    break;
1983                }
1984            }
1985        }
1986        if (needsRefilter) {
1987            preservingSelection(new Runnable JavaDoc() {
1988                public void run() {
1989                    internalRefresh(getRoot());
1990                }
1991            });
1992            return;
1993        }
1994
1995        boolean needsUpdate;
1996        if (properties == null) {
1997            needsUpdate = true;
1998        } else {
1999            needsUpdate = false;
2000            IBaseLabelProvider labelProvider = getLabelProvider();
2001            for (int i = 0; i < properties.length; ++i) {
2002                needsUpdate = labelProvider.isLabelProperty(element, properties[i]);
2003                if (needsUpdate) {
2004                    break;
2005                }
2006            }
2007        }
2008        if (needsUpdate) {
2009            updateItem(widget, element);
2010        }
2011    }
2012
2013    /**
2014     * Copies attributes of the given element into the given widget.
2015     * <p>
2016     * This method is internal to the framework; subclassers should not call
2017     * this method. Calls <code>doUpdateItem(widget, element, true)</code>.
2018     * </p>
2019     *
2020     * @param widget
2021     * the widget
2022     * @param element
2023     * the element
2024     */

2025    protected final void updateItem(Widget widget, Object JavaDoc element) {
2026        SafeRunnable.run(new UpdateItemSafeRunnable(widget, element, true));
2027    }
2028
2029    /**
2030     * Updates the selection of this viewer.
2031     * <p>
2032     * This framework method should be called when the selection in the viewer
2033     * widget changes.
2034     * </p>
2035     * <p>
2036     * The default implementation of this method notifies all selection change
2037     * listeners recorded in an internal state variable. Overriding this method
2038     * is generally not required; however, if overriding in a subclass,
2039     * <code>super.updateSelection</code> must be invoked.
2040     * </p>
2041     *
2042     * @param selection
2043     * the selection, or <code>null</code> if none
2044     */

2045    protected void updateSelection(ISelection selection) {
2046        SelectionChangedEvent event = new SelectionChangedEvent(this, selection);
2047        fireSelectionChanged(event);
2048    }
2049
2050    /**
2051     * Returns whether this structured viewer is configured to use an internal
2052     * map to speed up the mapping between elements and SWT items.
2053     * <p>
2054     * The default implementation of this framework method checks whether the
2055     * internal map has been initialized.
2056     * </p>
2057     *
2058     * @return <code>true</code> if the element map is enabled, and
2059     * <code>false</code> if disabled
2060     */

2061    protected boolean usingElementMap() {
2062        return elementMap != null;
2063    }
2064
2065    /* (non-Javadoc)
2066     * @see org.eclipse.jface.viewers.ContentViewer#setLabelProvider(org.eclipse.jface.viewers.IBaseLabelProvider)
2067     */

2068    public void setLabelProvider(IBaseLabelProvider labelProvider) {
2069        if (labelProvider instanceof IColorProvider || labelProvider instanceof IFontProvider) {
2070            colorAndFontCollector = new ColorAndFontCollectorWithProviders(labelProvider);
2071        } else {
2072            colorAndFontCollector = new ColorAndFontCollector();
2073        }
2074        super.setLabelProvider(labelProvider);
2075        
2076    }
2077    
2078    /**
2079     * Build a label up for the element using the supplied label provider.
2080     * @param updateLabel The ViewerLabel to collect the result in
2081     * @param element The element being decorated.
2082     */

2083    protected void buildLabel(ViewerLabel updateLabel, Object JavaDoc element){
2084
2085        if (getLabelProvider() instanceof IViewerLabelProvider) {
2086            IViewerLabelProvider itemProvider = (IViewerLabelProvider) getLabelProvider();
2087            itemProvider.updateLabel(updateLabel, element);
2088                    
2089            colorAndFontCollector.setUsedDecorators();
2090            
2091            if(updateLabel.hasNewBackground()) {
2092                colorAndFontCollector.setBackground(updateLabel.getBackground());
2093            }
2094            
2095            if(updateLabel.hasNewForeground()) {
2096                colorAndFontCollector.setForeground(updateLabel.getForeground());
2097            }
2098            
2099            if(updateLabel.hasNewFont()) {
2100                colorAndFontCollector.setFont(updateLabel.getFont());
2101            }
2102            return;
2103
2104        }
2105        
2106        if(getLabelProvider() instanceof ILabelProvider){
2107            ILabelProvider labelProvider = (ILabelProvider) getLabelProvider();
2108            updateLabel.setText(labelProvider.getText(element));
2109            updateLabel.setImage(labelProvider.getImage(element));
2110        }
2111    
2112    }
2113    
2114    /**
2115     * Build a label up for the element using the supplied label provider.
2116     * @param updateLabel The ViewerLabel to collect the result in
2117     * @param element The element being decorated.
2118     * @param labelProvider ILabelProvider the labelProvider for the receiver.
2119     */

2120    void buildLabel(ViewerLabel updateLabel, Object JavaDoc element,IViewerLabelProvider labelProvider){
2121
2122            labelProvider.updateLabel(updateLabel, element);
2123                    
2124            colorAndFontCollector.setUsedDecorators();
2125            
2126            if(updateLabel.hasNewBackground()) {
2127                colorAndFontCollector.setBackground(updateLabel.getBackground());
2128            }
2129            
2130            if(updateLabel.hasNewForeground()) {
2131                colorAndFontCollector.setForeground(updateLabel.getForeground());
2132            }
2133            
2134            if(updateLabel.hasNewFont()) {
2135                colorAndFontCollector.setFont(updateLabel.getFont());
2136            }
2137    
2138    }
2139    
2140    /**
2141     * Build a label up for the element using the supplied label provider.
2142     * @param updateLabel The ViewerLabel to collect the result in
2143     * @param elementPath The path of the element being decorated.
2144     * @param labelProvider ILabelProvider the labelProvider for the receiver.
2145     */

2146    void buildLabel(ViewerLabel updateLabel, TreePath elementPath,ITreePathLabelProvider labelProvider){
2147
2148            labelProvider.updateLabel(updateLabel, elementPath);
2149                    
2150            colorAndFontCollector.setUsedDecorators();
2151            
2152            if(updateLabel.hasNewBackground()) {
2153                colorAndFontCollector.setBackground(updateLabel.getBackground());
2154            }
2155            
2156            if(updateLabel.hasNewForeground()) {
2157                colorAndFontCollector.setForeground(updateLabel.getForeground());
2158            }
2159            
2160            if(updateLabel.hasNewFont()) {
2161                colorAndFontCollector.setFont(updateLabel.getFont());
2162            }
2163    
2164    }
2165    
2166    /**
2167     * Build a label up for the element using the supplied label provider.
2168     * @param updateLabel The ViewerLabel to collect the result in
2169     * @param element The element being decorated.
2170     * @param labelProvider ILabelProvider the labelProvider for the receiver.
2171     */

2172    void buildLabel(ViewerLabel updateLabel, Object JavaDoc element,ILabelProvider labelProvider){
2173            updateLabel.setText(labelProvider.getText(element));
2174            updateLabel.setImage(labelProvider.getImage(element));
2175    }
2176
2177    /**
2178     * Get the ColorAndFontCollector for the receiver.
2179     * @return ColorAndFontCollector
2180     * @since 3.1
2181     */

2182    protected ColorAndFontCollector getColorAndFontCollector() {
2183        return colorAndFontCollector;
2184    }
2185    
2186    protected void handleDispose(DisposeEvent event) {
2187        super.handleDispose(event);
2188        sorter = null;
2189        comparer = null;
2190        if (filters != null)
2191            filters.clear();
2192        elementMap = newHashtable(1);
2193        openListeners.clear();
2194        doubleClickListeners.clear();
2195        colorAndFontCollector.clear();
2196        postSelectionChangedListeners.clear();
2197    }
2198
2199}
2200
Popular Tags