All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.eclipse.jface.viewers.StructuredViewer Maven / Gradle / Ivy

/*******************************************************************************
 * Copyright (c) 2000, 2019 IBM Corporation and others.
 *
 * This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License 2.0
 * which accompanies this distribution, and is available at
 * https://www.eclipse.org/legal/epl-2.0/
 *
 * SPDX-License-Identifier: EPL-2.0
 *
 * Contributors:
 *     IBM Corporation - initial API and implementation
 *     Tom Schindl - bug 151205
 *     Lars Vogel  - Bug 402439, 475689
 *     Thorsten Maack  - Bug 482163
 *     Jan-Ove Weichel  - Bug 481490
 *     Alexander Fedorov  - Bug 548314
 *******************************************************************************/
package org.eclipse.jface.viewers;

import static org.eclipse.swt.events.SelectionListener.widgetSelectedAdapter;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;

import org.eclipse.core.runtime.Assert;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.ListenerList;
import org.eclipse.core.runtime.Status;
import org.eclipse.jface.internal.InternalPolicy;
import org.eclipse.jface.util.OpenStrategy;
import org.eclipse.jface.util.Policy;
import org.eclipse.jface.util.SafeRunnable;
import org.eclipse.swt.SWT;
import org.eclipse.swt.dnd.DragSource;
import org.eclipse.swt.dnd.DragSourceListener;
import org.eclipse.swt.dnd.DropTarget;
import org.eclipse.swt.dnd.DropTargetListener;
import org.eclipse.swt.dnd.Transfer;
import org.eclipse.swt.events.DisposeEvent;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.events.SelectionListener;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.graphics.Font;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Item;
import org.eclipse.swt.widgets.TableItem;
import org.eclipse.swt.widgets.TreeItem;
import org.eclipse.swt.widgets.Widget;

/**
 * Abstract base implementation for structure-oriented viewers (trees, lists,
 * tables). Supports custom sorting, filtering, and rendering.
 * 

* Any number of viewer filters can be added to this viewer (using * addFilter). When the viewer receives an update, it asks each * of its filters if it is out of date, and refilters elements as required. *

* * @see ViewerFilter * @see ViewerComparator */ public abstract class StructuredViewer extends ContentViewer implements IPostSelectionProvider { /** * A map from the viewer's model elements to SWT widgets. (key type: * Object, value type: Widget, or Widget[]). * null means that the element map is disabled. */ private CustomHashtable elementMap; /** * The comparer to use for comparing elements, or null to use * the default equals and hashCode methods on * the element itself. */ private IElementComparer comparer; /** * This viewer's comparator used for sorting. null means there is no comparator. */ private ViewerComparator sorter; /** * This viewer's filters (element type: ViewerFilter). * null means there are no filters. */ private List filters; /** * Indicates whether the viewer should attempt to preserve the selection * across update operations. * * @see #setSelection(ISelection, boolean) */ private boolean preserveSelection = true; /** * Indicates whether a selection change is in progress on this viewer. * * @see #setSelection(ISelection, boolean) */ private boolean inChange; /** * Used while a selection change is in progress on this viewer to indicates * whether the selection should be restored. * * @see #setSelection(ISelection, boolean) */ private boolean restoreSelection; /** * List of double-click state listeners (element type: * IDoubleClickListener). * * @see #fireDoubleClick */ private ListenerList doubleClickListeners = new ListenerList<>(); /** * List of open listeners (element type: * ISelectionActivateListener). * * @see #fireOpen */ private ListenerList openListeners = new ListenerList<>(); /** * List of post selection listeners (element type: * ISelectionActivateListener). * * @see #firePostSelectionChanged */ private ListenerList postSelectionChangedListeners = new ListenerList<>(); /** * The colorAndFontCollector is an object used by viewers that * support the IColorProvider, the IFontProvider and/or the * IViewerLabelProvider for color and font updates. * Initialize it to have no color or font providing * initially. * @since 3.1 */ private ColorAndFontCollector colorAndFontCollector = new ColorAndFontCollector(); /** * Calls when associate() and disassociate() are called */ private StructuredViewerInternals.AssociateListener associateListener; /** * Empty array of widgets. */ private static Widget[] NO_WIDGETS = new Widget[0]; /** * The ColorAndFontCollector is a helper class for viewers * that have color and font support ad optionally decorators. * @see IColorDecorator * @see IFontDecorator * @see IColorProvider * @see IFontProvider * @see IDecoration */ protected class ColorAndFontCollectorWithProviders extends ColorAndFontCollector{ IColorProvider colorProvider; IFontProvider fontProvider; /** * Create a new instance of the receiver using the supplied * label provider. If it is an IColorProvider or IFontProvider * set these values up. * @param provider IBaseLabelProvider * @see IColorProvider * @see IFontProvider */ public ColorAndFontCollectorWithProviders(IBaseLabelProvider provider) { super(); if (provider instanceof IColorProvider) { colorProvider = (IColorProvider) provider; } if (provider instanceof IFontProvider) { fontProvider = (IFontProvider) provider; } } @Override public void setFontsAndColors(Object element){ if(fontProvider != null){ if(font == null) { font = fontProvider.getFont(element); } } if(colorProvider == null) { return; } //Set the colors if they are not set yet if(background == null) { background = colorProvider.getBackground(element); } if(foreground == null) { foreground = colorProvider.getForeground(element); } } @Override public void applyFontsAndColors(TableItem control) { if(colorProvider == null){ if(usedDecorators){ //If there is no provider only apply set values if(background != null) { control.setBackground(background); } if(foreground != null) { control.setForeground(foreground); } } } else{ //Always set the value if there is a provider control.setBackground(background); control.setForeground(foreground); } if(fontProvider == null){ if(usedDecorators && font != null) { control.setFont(font); } } else { control.setFont(font); } clear(); } @Override public void applyFontsAndColors(TreeItem control) { if(colorProvider == null){ if(usedDecorators){ //If there is no provider only apply set values if(background != null) { control.setBackground(background); } if(foreground != null) { control.setForeground(foreground); } } } else{ //Always set the value if there is a provider control.setBackground(background); control.setForeground(foreground); } if(fontProvider == null){ if(usedDecorators && font != null) { control.setFont(font); } } else { control.setFont(font); } clear(); } } /** * The ColorAndFontCollector collects fonts and colors without a * a color or font provider. */ protected class ColorAndFontCollector { Color foreground = null; Color background = null; Font font = null; boolean usedDecorators = false; /** * Create a new instance of the receiver with * no color and font provider. */ public ColorAndFontCollector(){ super(); } /** * Clear all of the results. */ public void clear() { foreground = null; background = null; font = null; usedDecorators = false; } /** * Set the initial fonts and colors for the element from the * content providers. * @param element Object */ public void setFontsAndColors(Object element){ //Do nothing if there are no providers } /** * Set that decorators were applied. */ public void setUsedDecorators() { this.usedDecorators = true; } /** * Apply the fonts and colors to the control if required. * * @param control control to apply font and color on */ public void applyFontsAndColors(TableItem control) { if(usedDecorators){ //If there is no provider only apply set values if(background != null) { control.setBackground(background); } if(foreground != null) { control.setForeground(foreground); } if(font != null) { control.setFont(font); } } clear(); } /** * Apply the fonts and colors to the control if required. * * @param control control to apply font and color on */ public void applyFontsAndColors(TreeItem control) { if(usedDecorators){ //If there is no provider only apply set values if(background != null) { control.setBackground(background); } if(foreground != null) { control.setForeground(foreground); } if(font != null) { control.setFont(font); } } clear(); } /** * Set the background color. * * @param background color to set */ public void setBackground(Color background) { this.background = background; } /** * Set the font. * * @param font font to set */ public void setFont(Font font) { this.font = font; } /** * Set the foreground color. * * @param foreground color to set */ public void setForeground(Color foreground) { this.foreground = foreground; } } /** * The safe runnable used to update an item. */ class UpdateItemSafeRunnable extends SafeRunnable { private Widget widget; private Object element; private boolean fullMap; UpdateItemSafeRunnable(Widget widget, Object element, boolean fullMap) { this.widget = widget; this.element = element; this.fullMap = fullMap; } @Override public void run() { doUpdateItem(widget, element, fullMap); } } /** * Creates a structured element viewer. The viewer has no input, no content * provider, a default label provider, no sorter, and no filters. */ protected StructuredViewer() { // do nothing } /** * Adds a listener for double-clicks in this viewer. Has no effect if an * identical listener is already registered. * * @param listener * a double-click listener */ public void addDoubleClickListener(IDoubleClickListener listener) { doubleClickListeners.add(listener); } /** * Adds a listener for selection-open in this viewer. Has no effect if an * identical listener is already registered. * * @param listener * an open listener */ public void addOpenListener(IOpenListener listener) { openListeners.add(listener); } @Override public void addPostSelectionChangedListener(ISelectionChangedListener listener) { postSelectionChangedListeners.add(listener); } /** * Adds support for dragging items out of this viewer via a user * drag-and-drop operation. * * @param operations * a bitwise OR of the supported drag and drop operation types ( * DROP_COPY,DROP_LINK, and * DROP_MOVE) * @param transferTypes * the transfer types that are supported by the drag operation * @param listener * the callback that will be invoked to set the drag data and to * cleanup after the drag and drop operation finishes * @see org.eclipse.swt.dnd.DND */ public void addDragSupport(int operations, Transfer[] transferTypes, DragSourceListener listener) { Control myControl = getControl(); final DragSource dragSource = new DragSource(myControl, operations); dragSource.setTransfer(transferTypes); dragSource.addDragListener(listener); } /** * Adds support for dropping items into this viewer via a user drag-and-drop * operation. * * @param operations * a bitwise OR of the supported drag and drop operation types ( * DROP_COPY,DROP_LINK, and * DROP_MOVE) * @param transferTypes * the transfer types that are supported by the drop operation * @param listener * the callback that will be invoked after the drag and drop * operation finishes * @see org.eclipse.swt.dnd.DND */ public void addDropSupport(int operations, Transfer[] transferTypes, final DropTargetListener listener) { Control control = getControl(); DropTarget dropTarget = new DropTarget(control, operations); dropTarget.setTransfer(transferTypes); dropTarget.addDropListener(listener); } /** * Adds the given filter to this viewer, and triggers refiltering and * resorting of the elements. If you want to add more than one filter * consider using {@link StructuredViewer#setFilters(ViewerFilter...)}. * * @param filter * a viewer filter * @see StructuredViewer#setFilters(ViewerFilter...) */ public void addFilter(ViewerFilter filter) { if (filters == null) { filters = new ArrayList<>(); } filters.add(filter); refresh(); } /** * Asserts that the given array of elements is itself non- null * and contains no null elements. * * @param elements * the array to check */ protected void assertElementsNotNull(Object... elements) { Assert.isNotNull(elements); for (Object element : elements) { Assert.isNotNull(element); } if (InternalPolicy.DEBUG_LOG_EQUAL_VIEWER_ELEMENTS && elements.length > 1) { CustomHashtable elementSet = newHashtable(elements.length * 2); for (Object element : elements) { Object old = elementSet.put(element, element); if (old != null) { String message = "Sibling elements in viewer must not be equal:\n " //$NON-NLS-1$ + old + ",\n " + element; //$NON-NLS-1$ Policy.getLog().log( new Status(IStatus.WARNING, Policy.JFACE, message, new RuntimeException())); return; } } } } /** * Associates the given element with the given widget. Sets the given item's * data to be the element, and maps the element to the item in the element * map (if enabled). * * @param element * the element * @param item * the widget */ protected void associate(Object element, Item item) { Object data = item.getData(); if (data != element) { if (data != null) { disassociate(item); } item.setData(element); mapElement(element, item); } else { // Always map the element, even if data == element, // since unmapAllElements() can leave the map inconsistent // See bug 2741 for details. mapElement(element, item); } if (associateListener != null) associateListener.associate(element, item); } /** * Disassociates the given SWT item from its corresponding element. Sets the * item's data to null and removes the element from the * element map (if enabled). * * @param item * the widget */ protected void disassociate(Item item) { if (associateListener != null) associateListener.disassociate(item); Object element = item.getData(); Assert.isNotNull(element); //Clear the map before we clear the data unmapElement(element, item); item.setData(null); } /** * Returns the widget in this viewer's control which represents the given * element if it is the viewer's input. *

* This method is internal to the framework; subclassers should not call this * method. *

* * @param element the element to find the representing widget for * @return the corresponding widget, or null if none */ protected abstract Widget doFindInputItem(Object element); /** * Returns the widget in this viewer's control which represent the given * element. This method searches all the children of the input element. *

* This method is internal to the framework; subclassers should not call this * method. *

* * @param element the element to find the representing widget for * @return the corresponding widget, or null if none */ protected abstract Widget doFindItem(Object element); /** * Copies the attributes of the given element into the given SWT item. The * element map is updated according to the value of fullMap. * If fullMap is true then the current mapping * from element to widgets is removed and the new mapping is added. If * full map is false then only the new map gets installed. * Installing only the new map is necessary in cases where only the order of * elements changes but not the set of elements. *

* This method is internal to the framework; subclassers should not call * this method. *

* * @param item widget item to update * @param element the element to represent * @param fullMap * true if mappings are added and removed, and * false if only the new map gets installed */ protected abstract void doUpdateItem(Widget item, Object element, boolean fullMap); /** * Compares two elements for equality. Uses the element comparer if one has * been set, otherwise uses the default equals method on the * elements themselves. * * @param elementA * the first element * @param elementB * the second element * @return whether elementA is equal to elementB */ protected boolean equals(Object elementA, Object elementB) { if (comparer == null) { return elementA == null ? elementB == null : elementA.equals(elementB); } return elementA == null ? elementB == null : comparer.equals(elementA, elementB); } /** * Returns the result of running the given elements through the filters. * * @param elements * the elements to filter * @return only the elements which all filters accept */ protected Object[] filter(Object[] elements) { if (filters != null) { List filtered = new ArrayList<>(elements.length); Object root = getRoot(); for (Object element : elements) { boolean add = true; for (ViewerFilter filter : filters) { add = filter.select(this, root, element); if (!add) { break; } } if (add) { filtered.add(element); } else if (associateListener != null) associateListener.filteredOut(element); } return filtered.toArray(); } return elements; } /** * Finds the widget which represents the given element. *

* The default implementation of this method tries first to find the widget * for the given element assuming that it is the viewer's input; this is * done by calling doFindInputItem. If it is not found * there, it is looked up in the internal element map provided that this * feature has been enabled. If the element map is disabled, the widget is * found via doFindInputItem. *

* * @param element * the element * @return the corresponding widget, or null if none */ protected final Widget findItem(Object element) { Widget[] result = findItems(element); return result.length == 0 ? null : result[0]; } /** * Finds the widgets which represent the given element. The returned array * must not be changed by clients; it might change upon calling other * methods on this viewer. *

* This method was introduced to support multiple equal elements in a viewer * (see {@link AbstractTreeViewer}). Multiple equal elements are only * supported if the element map is enabled by calling * {@link #setUseHashlookup(boolean)} and passing true. *

*

* The default implementation of this method tries first to find the widget * for the given element assuming that it is the viewer's input; this is * done by calling doFindInputItem. If it is not found * there, the widgets are looked up in the internal element map provided * that this feature has been enabled. If the element map is disabled, the * widget is found via doFindItem. *

* * @param element * the element * @return the corresponding widgets * * @since 3.2 */ protected final Widget[] findItems(Object element) { Widget result = doFindInputItem(element); if (result != null) { return new Widget[] { result }; } // if we have an element map use it, otherwise search for the item. if (usingElementMap()) { Object widgetOrWidgets = elementMap.get(element); if (widgetOrWidgets==null) { return NO_WIDGETS; } else if (widgetOrWidgets instanceof Widget) { return new Widget[] {(Widget) widgetOrWidgets}; } else { return (Widget[])widgetOrWidgets; } } result = doFindItem(element); return result == null ? NO_WIDGETS : new Widget[] { result }; } /** * Notifies any double-click listeners that a double-click has been * received. Only listeners registered at the time this method is called are * notified. * * @param event * a double-click event * * @see IDoubleClickListener#doubleClick */ protected void fireDoubleClick(final DoubleClickEvent event) { for (IDoubleClickListener l : doubleClickListeners) { SafeRunnable.run(new SafeRunnable() { @Override public void run() { l.doubleClick(event); } }); } } /** * Notifies any open event listeners that a open event has been received. * Only listeners registered at the time this method is called are notified. * * @param event * a double-click event * * @see IOpenListener#open(OpenEvent) */ protected void fireOpen(final OpenEvent event) { for (IOpenListener l : openListeners) { SafeRunnable.run(new SafeRunnable() { @Override public void run() { l.open(event); } }); } } /** * Notifies any post selection listeners that a post selection event has * been received. Only listeners registered at the time this method is * called are notified. * * @param event * a selection changed event * * @see #addPostSelectionChangedListener(ISelectionChangedListener) */ protected void firePostSelectionChanged(final SelectionChangedEvent event) { for (ISelectionChangedListener l : postSelectionChangedListeners) { SafeRunnable.run(new SafeRunnable() { @Override public void run() { l.selectionChanged(event); } }); } } /** * Returns the comparer to use for comparing elements, or * null if none has been set. If specified, * the viewer uses this to compare and hash elements rather * than the elements' own equals and hashCode methods. * * @return the comparer to use for comparing elements or * null */ public IElementComparer getComparer() { return comparer; } /** * Returns the filtered array of children of the given element. The * resulting array must not be modified, as it may come directly from the * model's internal state. * * @param parent * the parent element * @return a filtered array of child elements */ protected Object[] getFilteredChildren(Object parent) { Object[] result = getRawChildren(parent); if (result.length == 0) { return result; } if (filters != null) { for (ViewerFilter f : filters) { Object[] filteredResult = f.filter(this, parent, result); if (associateListener != null && filteredResult.length != result.length) { notifyFilteredOut(result, filteredResult); } result = filteredResult; } } return result; } /** * Notifies an AssociateListener of the elements that have been filtered out. */ private void notifyFilteredOut(Object[] rawResult, Object[] filteredResult) { int rawIndex = 0; int filteredIndex = 0; for (; filteredIndex < filteredResult.length; ) { if (rawResult[rawIndex] != filteredResult[filteredIndex]) { associateListener.filteredOut(rawResult[rawIndex++]); continue; } rawIndex++; filteredIndex++; } for (; rawIndex < rawResult.length; rawIndex++) { associateListener.filteredOut(rawResult[rawIndex]); } } /** * Returns this viewer's filters. * * @return an array of viewer filters * @see StructuredViewer#setFilters(ViewerFilter...) */ public ViewerFilter[] getFilters() { if (filters == null) { return new ViewerFilter[0]; } ViewerFilter[] result = new ViewerFilter[filters.size()]; filters.toArray(result); return result; } /** * Returns the item at the given display-relative coordinates, or * null if there is no item at that location or * the underlying SWT-Control is not made up of {@link Item} * (e.g {@link ListViewer}) *

* The default implementation of this method returns null. *

* * @param x * horizontal coordinate * @param y * vertical coordinate * @return the item, or null if there is no item at the given * coordinates * @deprecated This method is deprecated in 3.3 in favor of {@link ColumnViewer#getItemAt(org.eclipse.swt.graphics.Point)}. * Viewers who are not subclasses of {@link ColumnViewer} should consider using a * widget relative implementation like {@link ColumnViewer#getItemAt(org.eclipse.swt.graphics.Point)}. */ @Deprecated protected Item getItem(int x, int y) { return null; } /** * Returns the children of the given parent without sorting and filtering * them. The resulting array must not be modified, as it may come directly * from the model's internal state. *

* Returns an empty array if the given parent is null. *

* * @param parent * the parent element * @return the child elements */ protected Object[] getRawChildren(Object parent) { Object[] result = null; if (parent != null) { IStructuredContentProvider cp = (IStructuredContentProvider) getContentProvider(); if (cp != null) { result = cp.getElements(parent); assertElementsNotNull(result); } } return (result != null) ? result : new Object[0]; } /** * Returns the root element. *

* The default implementation of this framework method forwards to * getInput. Override if the root element is different from * the viewer's input element. *

* * @return the root element, or null if none */ protected Object getRoot() { return getInput(); } /** * The StructuredViewer implementation of this method returns * the result as an IStructuredSelection. *

* Call {@link #getStructuredSelection()} instead to get an instance of * IStructuredSelection directly. *

* Subclasses do not typically override this method, but implement * getSelectionFromWidget(List) instead. If they override this * method, they should return an IStructuredSelection as well. * * @return ISelection */ @Override public ISelection getSelection() { Control control = getControl(); if (control == null || control.isDisposed()) { return StructuredSelection.EMPTY; } List list = getSelectionFromWidget(); return new StructuredSelection(list, comparer); } /** * Returns the IStructuredSelection of this viewer. *

* Subclasses whose {@link #getSelection()} specifies to return a more * specific type should also override this method and return that type. *

* * @return IStructuredSelection * @throws ClassCastException * if the selection of the viewer is not an instance of * IStructuredSelection * @since 3.11 */ public IStructuredSelection getStructuredSelection() throws ClassCastException { ISelection selection = getSelection(); if (selection instanceof IStructuredSelection) { return (IStructuredSelection) selection; } throw new ClassCastException("StructuredViewer should return an instance of IStructuredSelection from its getSelection() method."); //$NON-NLS-1$ } /** * Retrieves the selection, as a List, from the underlying * widget. * * @return the list of selected elements */ @SuppressWarnings("rawtypes") protected abstract List getSelectionFromWidget(); /** * Returns the sorted and filtered set of children of the given element. The * resulting array must not be modified, as it may come directly from the * model's internal state. * * @param parent * the parent element * @return a sorted and filtered array of child elements */ protected Object[] getSortedChildren(Object parent) { Object[] result = getFilteredChildren(parent); if (sorter != null) { // be sure we're not modifying the original array from the model result = result.clone(); sorter.sort(this, result); } return result; } /** * Returns this viewer's sorter, or null if it does not have * one. If this viewer has a comparator that was set via * setComparator(ViewerComparator) then this method will return * null if the comparator is not an instance of ViewerSorter. *

* It is recommended to use getComparator() instead. *

* * @return a viewer sorter, or null if none or if the comparator is * not an instance of ViewerSorter */ public ViewerSorter getSorter() { if (sorter instanceof ViewerSorter) return (ViewerSorter)sorter; return null; } /** * Return this viewer's comparator used to sort elements. * This method should be used instead of getSorter(). * * @return a viewer comparator, or null if none * * @since 3.2 */ public ViewerComparator getComparator(){ return sorter; } /** * Handles a double-click select event from the widget. *

* This method is internal to the framework; subclassers should not call * this method. *

* * @param event * the SWT selection event */ protected void handleDoubleSelect(SelectionEvent event) { // This method is reimplemented in AbstractTreeViewer to fix bug 108102. // handle case where an earlier selection listener disposed the control. Control control = getControl(); if (control != null && !control.isDisposed()) { // If the double-clicked element can be obtained from the event, use it // otherwise get it from the control. Some controls like List do // not have the notion of item. // For details, see bug 90161 [Navigator] DefaultSelecting folders shouldn't always expand first one ISelection selection; if (event.item != null && event.item.getData() != null) { selection = new StructuredSelection(event.item.getData()); } else { selection = getSelection(); updateSelection(selection); } fireDoubleClick(new DoubleClickEvent(this, selection)); } } /** * Handles an open event from the OpenStrategy. *

* This method is internal to the framework; subclassers should not call * this method. *

* * @param event * the SWT selection event */ protected void handleOpen(SelectionEvent event) { Control control = getControl(); if (control != null && !control.isDisposed()) { ISelection selection = getSelection(); fireOpen(new OpenEvent(this, selection)); } } /** * Handles an invalid selection. *

* This framework method is called if a model change picked up by a viewer * results in an invalid selection. For instance if an element contained in * the selection has been removed from the viewer, the viewer is free to * either remove the element from the selection or to pick another element * as its new selection. The default implementation of this method calls * updateSelection. Subclasses may override it to implement * a different strategy for picking a new selection when the old selection * becomes invalid. *

* * @param invalidSelection * the selection before the viewer was updated * @param newSelection * the selection after the update, or null if none */ protected void handleInvalidSelection(ISelection invalidSelection, ISelection newSelection) { updateSelection(newSelection); SelectionChangedEvent event = new SelectionChangedEvent(this, newSelection); firePostSelectionChanged(event); } /** * The StructuredViewer implementation of this * ContentViewer method calls update if the event * specifies that the label of a given element has changed, otherwise it calls * super. Subclasses may reimplement or extend. * * @param event the event that generated this update */ @Override protected void handleLabelProviderChanged(LabelProviderChangedEvent event) { Object[] elements = event.getElements(); if (elements != null) { update(elements, null); } else { super.handleLabelProviderChanged(event); } } /** * Handles a select event from the widget. *

* This method is internal to the framework; subclassers should not call * this method. *

* * @param event * the SWT selection event */ protected void handleSelect(SelectionEvent event) { // handle case where an earlier selection listener disposed the control. Control control = getControl(); if (control != null && !control.isDisposed()) { updateSelection(getSelection()); } } /** * Handles a post select event from the widget. *

* This method is internal to the framework; subclassers should not call * this method. *

* * @param e the SWT selection event */ protected void handlePostSelect(SelectionEvent e) { SelectionChangedEvent event = new SelectionChangedEvent(this, getSelection()); firePostSelectionChanged(event); } @Override protected void hookControl(Control control) { super.hookControl(control); OpenStrategy handler = new OpenStrategy(control); handler.addSelectionListener(new SelectionListener() { @Override public void widgetSelected(SelectionEvent e) { // On Windows, selection events may happen during a refresh. // Ignore these events if we are currently in preservingSelection(). // See bug 184441. if (!inChange) { handleSelect(e); } } @Override public void widgetDefaultSelected(SelectionEvent e) { handleDoubleSelect(e); } }); handler.addPostSelectionListener(widgetSelectedAdapter(this::handlePostSelect)); handler.addOpenListener(StructuredViewer.this::handleOpen); } /** * Returns whether this viewer has any filters. * @return boolean */ protected boolean hasFilters() { return filters != null && filters.size() > 0; } /** * Refreshes this viewer starting at the given element. * * @param element * the element */ protected abstract void internalRefresh(Object element); /** * Refreshes this viewer starting at the given element. Labels are updated * as described in refresh(boolean updateLabels). *

* The default implementation simply calls * internalRefresh(element), ignoring * updateLabels. *

* If this method is overridden to do the actual refresh, then * internalRefresh(Object element) should simply call * internalRefresh(element, true). * * @param element * the element * @param updateLabels * true to update labels for existing elements, * false to only update labels as needed, assuming * that labels for existing elements are unchanged. * * @since 2.0 */ protected void internalRefresh(Object element, boolean updateLabels) { internalRefresh(element); } /** * Adds the element item pair to the element map. *

* This method is internal to the framework; subclassers should not call * this method. *

* * @param element * the element * @param item * the corresponding widget */ protected void mapElement(Object element, Widget item) { if (elementMap != null) { Object widgetOrWidgets = elementMap.get(element); if (widgetOrWidgets == null) { elementMap.put(element, item); } else if (widgetOrWidgets instanceof Widget) { if (widgetOrWidgets != item) { elementMap.put(element, new Widget[] { (Widget) widgetOrWidgets, item }); } } else { Widget[] widgets = (Widget[]) widgetOrWidgets; int indexOfItem = Arrays.asList(widgets).indexOf(item); if (indexOfItem == -1) { int length = widgets.length; System.arraycopy(widgets, 0, widgets = new Widget[length + 1], 0, length); widgets[length] = item; elementMap.put(element, widgets); } } } } /** * Determines whether a change to the given property of the given element * would require refiltering and/or resorting. *

* This method is internal to the framework; subclassers should not call * this method. *

* * @param element * the element * @param property * the property * @return true if refiltering is required, and * false otherwise */ protected boolean needsRefilter(Object element, String property) { if (sorter != null && sorter.isSorterProperty(element, property)) { return true; } if (filters != null) { for (ViewerFilter filter : filters) { if (filter.isFilterProperty(element, property)) { return true; } } } return false; } /** * Returns a new hashtable using the given capacity and this viewer's element comparer. * * @param capacity the initial capacity of the hashtable * @return a new hashtable * * @since 3.0 */ CustomHashtable newHashtable(int capacity) { return new CustomHashtable(capacity, getComparer()); } /** * Attempts to preserves the current selection across a run of the given code. * This method should not preserve the selection if {link * #getPreserveSelection()} returns false. *

* The default implementation of this method: *

*
    *
  • discovers the old selection (via getSelection)
  • *
  • runs the given runnable
  • *
  • attempts to restore the old selection (using * setSelectionToWidget
  • *
  • rediscovers the resulting selection (via getSelection)
  • *
  • calls handleInvalidSelection if the resulting selection is * different from the old selection
  • *
* * @param updateCode the code to run * * see #getPreserveSelection() */ protected void preservingSelection(Runnable updateCode) { preservingSelection(updateCode, false); } /** * Attempts to preserves the current selection across a run of the given * code, with a best effort to avoid scrolling if reveal is * false, or to reveal the selection if reveal is true. *

* The default implementation of this method: *

*
    *
  • discovers the old selection (via getSelection)
  • *
  • runs the given runnable
  • *
  • attempts to restore the old selection (using * setSelectionToWidget
  • *
  • rediscovers the resulting selection (via getSelection)
  • *
  • calls handleInvalidSelection if the selection did not * take
  • *
* * @param updateCode * the code to run * @param reveal * true if the selection should be made visible, * false if scrolling should be avoided * @since 3.3 */ void preservingSelection(Runnable updateCode, boolean reveal) { if (!preserveSelection) { return; } ISelection oldSelection = null; try { // preserve selection oldSelection = getSelection(); inChange = restoreSelection = true; // perform the update updateCode.run(); } finally { inChange = false; // restore selection if (restoreSelection) { setSelectionToWidget(oldSelection, reveal); } // send out notification if old and new differ ISelection newSelection = getSelection(); if (!newSelection.equals(oldSelection)) { handleInvalidSelection(oldSelection, newSelection); } } } @Override public void refresh() { Control control = getControl(); if (control != null) { control.setRedraw(false); } try { refresh(getRoot()); } finally { if (control != null) { control.setRedraw(true); } } } /** * Refreshes this viewer with information freshly obtained from this * viewer's model. If updateLabels is true * then labels for otherwise unaffected elements are updated as well. * Otherwise, it assumes labels for existing elements are unchanged, and * labels are only obtained as needed (for example, for new elements). *

* Calling refresh(true) has the same effect as * refresh(). *

* Note that the implementation may still obtain labels for existing * elements even if updateLabels is false. The intent is * simply to allow optimization where possible. * * @param updateLabels * true to update labels for existing elements, * false to only update labels as needed, assuming * that labels for existing elements are unchanged. * * @since 2.0 */ public void refresh(boolean updateLabels) { refresh(getRoot(), updateLabels); } /** * Refreshes this viewer starting with the given element. *

* Unlike the update methods, this handles structural changes * to the given element (e.g. addition or removal of children). If only the * given element needs updating, it is more efficient to use the * update methods. *

* * @param element * the element */ public void refresh(final Object element) { preservingSelection(() -> internalRefresh(element)); } /** * Refreshes this viewer starting with the given element. Labels are updated * as described in refresh(boolean updateLabels). *

* Unlike the update methods, this handles structural changes * to the given element (e.g. addition or removal of children). If only the * given element needs updating, it is more efficient to use the * update methods. *

* * @param element * the element * @param updateLabels * true to update labels for existing elements, * false to only update labels as needed, assuming * that labels for existing elements are unchanged. * * @since 2.0 */ public void refresh(final Object element, final boolean updateLabels) { preservingSelection(() -> internalRefresh(element, updateLabels)); } /** * * Refreshes the given item with the given element. Calls * doUpdateItem(..., false). *

* This method is internal to the framework; subclassers should not call * this method. *

* @param widget * the widget * @param element * the element */ protected final void refreshItem(Widget widget, Object element) { SafeRunnable.run(new UpdateItemSafeRunnable(widget, element, true)); } /** * Removes the given open listener from this viewer. Has no effect if an * identical listener is not registered. * * @param listener * an open listener */ public void removeOpenListener(IOpenListener listener) { openListeners.remove(listener); } @Override public void removePostSelectionChangedListener(ISelectionChangedListener listener) { postSelectionChangedListeners.remove(listener); } /** * Removes the given double-click listener from this viewer. Has no effect * if an identical listener is not registered. * * @param listener * a double-click listener */ public void removeDoubleClickListener(IDoubleClickListener listener) { doubleClickListeners.remove(listener); } /** * Removes the given filter from this viewer, and triggers refiltering and * resorting of the elements if required. Has no effect if the identical * filter is not registered. If you want to remove more than one filter * consider using {@link StructuredViewer#setFilters(ViewerFilter...)}. * * @param filter * a viewer filter * @see StructuredViewer#setFilters(ViewerFilter...) */ public void removeFilter(ViewerFilter filter) { Assert.isNotNull(filter); if (filters != null) { // Note: can't use List.remove(Object). Use identity comparison // instead. for (Iterator i = filters.iterator(); i.hasNext();) { Object o = i.next(); if (o == filter) { i.remove(); refresh(); if (filters.isEmpty()) { filters = null; } return; } } } } void setAssociateListener(StructuredViewerInternals.AssociateListener l) { associateListener = l; } /** * Sets the filters, replacing any previous filters, and triggers * refiltering and resorting of the elements. * * @param filters * an varargs of viewer filters * @since 3.3 */ public void setFilters(ViewerFilter... filters) { if (filters.length == 0) { resetFilters(); } else { this.filters = new ArrayList<>(Arrays.asList(filters)); refresh(false); } } /** * Discards this viewer's filters and triggers refiltering and resorting of * the elements. */ public void resetFilters() { if (filters != null) { filters = null; refresh(false); } } /** * Ensures that the given element is visible, scrolling the viewer if * necessary. The selection is unchanged. * * @param element * the element to reveal */ public abstract void reveal(Object element); /** * {@inheritDoc} *

* The StructuredViewer implementation of this method calls * {@link #assertContentProviderType(IContentProvider)} to validate the * content provider. For a StructuredViewer, the content * provider must implement {@link IStructuredContentProvider}. *

*/ @Override public void setContentProvider(IContentProvider provider) { assertContentProviderType(provider); super.setContentProvider(provider); } /** * Assert that the content provider is of one of the supported types. * * @param provider content provider to check */ protected void assertContentProviderType(IContentProvider provider) { Assert.isTrue(provider instanceof IStructuredContentProvider); } @Override public final void setInput(Object input) { Control control = getControl(); if (control == null || control.isDisposed()) { throw new IllegalStateException( "Need an underlying widget to be able to set the input." + //$NON-NLS-1$ "(Has the widget been disposed?)"); //$NON-NLS-1$ } unmapAllElements(); super.setInput(input); } @Override public void setSelection(ISelection selection, boolean reveal) { /** *

* If the new selection differs from the current selection the hook * updateSelection is called. *

*

* If setSelection is called from within * preserveSelection, the call to * updateSelection is delayed until the end of * preserveSelection. *

*

* Subclasses do not typically override this method, but implement * setSelectionToWidget instead. *

*/ Control control = getControl(); if (control == null || control.isDisposed()) { return; } if (!inChange) { setSelectionToWidget(selection, reveal); ISelection sel = getSelection(); updateSelection(sel); firePostSelectionChanged(new SelectionChangedEvent(this, sel)); } else { restoreSelection = false; setSelectionToWidget(selection, reveal); } } /** * Parlays the given list of selected elements into selections on this * viewer's control. *

* Subclasses should override to set their selection based on the given list * of elements. *

* * @param l * list of selected elements (element type: Object) * or null if the selection is to be cleared * @param reveal * true if the selection is to be made visible, * and false otherwise */ protected abstract void setSelectionToWidget(@SuppressWarnings("rawtypes") List l, boolean reveal); /** * Converts the selection to a List and calls * setSelectionToWidget(List, boolean). The selection is * expected to be an IStructuredSelection of elements. If * not, the selection is cleared. *

* Subclasses do not typically override this method, but implement * setSelectionToWidget(List, boolean) instead. * * @param selection * an IStructuredSelection of elements * @param reveal * true to reveal the first element in the * selection, or false otherwise */ protected void setSelectionToWidget(ISelection selection, boolean reveal) { if (selection instanceof IStructuredSelection) { setSelectionToWidget(((IStructuredSelection) selection).toList(), reveal); } else { setSelectionToWidget((List) null, reveal); } } /** * Sets this viewer's sorter and triggers refiltering and resorting of this * viewer's element. Passing null turns sorting off. * * @deprecated use setComparator() instead. * * @param sorter * a viewer sorter, or null if none */ @Deprecated public void setSorter(ViewerSorter sorter) { if (this.sorter != sorter) { this.sorter = sorter; refresh(); } } /** * Sets this viewer's comparator to be used for sorting elements, and triggers refiltering and * resorting of this viewer's element. null turns sorting off. * To get the viewer's comparator, call getComparator(). *

* IMPORTANT: This method was introduced in 3.2. If a reference to this viewer object * is passed to clients who call getSorter(), null may be returned from * from that method even though the viewer is sorting its elements using the * viewer's comparator. *

* * @param comparator a viewer comparator, or null if none * * @since 3.2 */ public void setComparator(ViewerComparator comparator){ if (this.sorter != comparator){ this.sorter = comparator; refresh(); } } /** * Configures whether this structured viewer uses an internal hash table to * speed up the mapping between elements and SWT items. This must be called * before the viewer is given an input (via setInput). *

* Note: enabling hash lookup requires from client code that elements managed by * the viewer properly implement {@link #hashCode()} and {@link #equals(Object)} * and that equal elements are not added to the same parent in the tree (each * equal element should have a different parent chain). *

* * @param enable true to enable hash lookup, and false * to disable it */ public void setUseHashlookup(boolean enable) { Assert.isTrue(getInput() == null, "Can only enable the hash look up before input has been set");//$NON-NLS-1$ if (enable) { elementMap = newHashtable(CustomHashtable.DEFAULT_CAPACITY); } else { elementMap = null; } } /** * Sets the comparer to use for comparing elements, or null * to use the default equals and hashCode * methods on the elements themselves. * * @param comparer * the comparer to use for comparing elements or * null */ public void setComparer(IElementComparer comparer) { this.comparer = comparer; if (elementMap != null) { elementMap = new CustomHashtable(elementMap, comparer); } } /** * NON-API - to be removed - see bug 200214 * Enable or disable the preserve selection behavior of this viewer. The * default is that the viewer attempts to preserve the selection across * update operations. This is an advanced option, to support clients that * manage the selection without relying on the viewer, or clients running * into performance problems when using viewers and {@link SWT#VIRTUAL}. * Note that this method has been introduced in 3.5 and that trying to * disable the selection behavior may not be possible for all subclasses of * StructuredViewer, or may cause program errors. This method * is supported for {@link TableViewer}, {@link TreeViewer}, * {@link ListViewer}, {@link CheckboxTableViewer}, * {@link CheckboxTreeViewer}, and {@link ComboViewer}, but no promises are * made for other subclasses of StructuredViewer, or subclasses of the * listed viewer classes. * * @param preserve * true if selection should be preserved, * false otherwise */ void setPreserveSelection(boolean preserve) { this.preserveSelection = preserve; } /** * NON-API - to be removed - see bug 200214 * Returns whether an attempt should be made to preserve selection across * update operations. To be used by subclasses that override * {@link #preservingSelection(Runnable)}. * * @return true if selection should be preserved, * false otherwise */ boolean getPreserveSelection() { return this.preserveSelection; } /** * Hook for testing. * * @param element the element * @return the corresponding widget, or null if none */ public Widget testFindItem(Object element) { return findItem(element); } /** * Hook for testing. * * @param element the element * @return the corresponding widgets * @since 3.2 */ public Widget[] testFindItems(Object element) { return findItems(element); } /** * Removes all elements from the map. *

* This method is internal to the framework; subclassers should not call * this method. *

*/ protected void unmapAllElements() { if (elementMap != null) { elementMap = newHashtable(CustomHashtable.DEFAULT_CAPACITY); } } /** * Removes the given element from the internal element to widget map. Does * nothing if mapping is disabled. If mapping is enabled, the given element * must be present. *

* This method is internal to the framework; subclassers should not call * this method. *

* * @param element * the element */ protected void unmapElement(Object element) { if (elementMap != null) { elementMap.remove(element); } } /** * Removes the given association from the internal element to widget map. * Does nothing if mapping is disabled, or if the given element does not map * to the given item. *

* This method is internal to the framework; subclassers should not call * this method. *

* * @param element * the element * @param item the item to unmap * @since 2.0 */ protected void unmapElement(Object element, Widget item) { // double-check that the element actually maps to the given item before // unmapping it if (elementMap != null) { Object widgetOrWidgets = elementMap.get(element); if (widgetOrWidgets == null) { // item was not mapped, return return; } else if (widgetOrWidgets instanceof Widget) { if (item == widgetOrWidgets) { elementMap.remove(element); } } else { Widget[] widgets = (Widget[]) widgetOrWidgets; int indexOfItem = Arrays.asList(widgets).indexOf(item); if (indexOfItem == -1) { return; } int length = widgets.length; if (indexOfItem == 0) { if(length == 1) { elementMap.remove(element); } else { Widget[] updatedWidgets = new Widget[length - 1]; System.arraycopy(widgets, 1, updatedWidgets, 0, length -1 ); elementMap.put(element, updatedWidgets); } } else { Widget[] updatedWidgets = new Widget[length - 1]; System.arraycopy(widgets, 0, updatedWidgets, 0, indexOfItem); System.arraycopy(widgets, indexOfItem + 1, updatedWidgets, indexOfItem, length - indexOfItem - 1); elementMap.put(element, updatedWidgets); } } } } // flag to indicate that a full refresh took place. See bug 102440. private boolean refreshOccurred; /** * Updates the given elements' presentation when one or more of their * properties change. Only the given elements are updated. *

* This does not handle structural changes (e.g. addition or removal of * elements), and does not update any other related elements (e.g. child * elements). To handle structural changes, use the refresh * methods instead. *

*

* This should be called when an element has changed in the model, in order * to have the viewer accurately reflect the model. This method only affects * the viewer, not the model. *

*

* Specifying which properties are affected may allow the viewer to optimize * the update. For example, if the label provider is not affected by changes * to any of these properties, an update may not actually be required. * Specifying properties as null forces a full * update of the given elements. *

*

* If the viewer has a sorter which is affected by a change to one of the * properties, the elements' positions are updated to maintain the sort * order. Note that resorting may not happen if properties * is null. *

*

* If the viewer has a filter which is affected by a change to one of the * properties, elements may appear or disappear if the change affects * whether or not they are filtered out. Note that resorting may not happen * if properties is null. *

* * @param elements * the elements * @param properties * the properties that have changed, or null to * indicate unknown */ public void update(Object[] elements, String[] properties) { boolean previousValue = refreshOccurred; refreshOccurred = false; try { for (Object element : elements) { update(element, properties); if (refreshOccurred) { return; } } } finally { refreshOccurred = previousValue; } } /** * Updates the given element's presentation when one or more of its * properties changes. Only the given element is updated. *

* This does not handle structural changes (e.g. addition or removal of * elements), and does not update any other related elements (e.g. child * elements). To handle structural changes, use the refresh * methods instead. *

*

* This should be called when an element has changed in the model, in order * to have the viewer accurately reflect the model. This method only affects * the viewer, not the model. *

*

* Specifying which properties are affected may allow the viewer to optimize * the update. For example, if the label provider is not affected by changes * to any of these properties, an update may not actually be required. * Specifying properties as null forces a full * update of the element. *

*

* If the viewer has a sorter which is affected by a change to one of the * properties, the element's position is updated to maintain the sort order. * Note that resorting may not happen if properties is * null. *

*

* If the viewer has a filter which is affected by a change to one of the * properties, the element may appear or disappear if the change affects * whether or not the element is filtered out. Note that filtering may not * happen if properties is null. *

* * @param element * the element * @param properties * the properties that have changed, or null to * indicate unknown */ public void update(Object element, String[] properties) { Assert.isNotNull(element); Widget[] items = findItems(element); boolean mayExitEarly = !refreshOccurred; for (Widget item : items) { internalUpdate(item, element, properties); if (mayExitEarly && refreshOccurred) { // detected a change from refreshOccurred==false to refreshOccurred==true return; } } } /** * Updates the given element's presentation when one or more of its * properties changes. Only the given element is updated. *

* EXPERIMENTAL. Not to be used except by JDT. * This method was added to support JDT's explorations * into grouping by working sets, which requires viewers to support multiple * equal elements. See bug 76482 for more details. This support will * likely be removed in Eclipse 3.3 in favor of proper support for * multiple equal elements (which was implemented for AbtractTreeViewer in 3.2). *

* @param widget * the widget for the element * @param element * the element * @param properties * the properties that have changed, or null to * indicate unknown */ protected void internalUpdate(Widget widget, Object element, String[] properties) { boolean needsRefilter = false; if (properties != null) { for (String property : properties) { needsRefilter = needsRefilter(element, property); if (needsRefilter) { break; } } } if (needsRefilter) { preservingSelection(() -> { internalRefresh(getRoot()); refreshOccurred = true; }); return; } boolean needsUpdate; if (properties == null) { needsUpdate = true; } else { needsUpdate = false; IBaseLabelProvider labelProvider = getLabelProvider(); for (String property : properties) { needsUpdate = labelProvider.isLabelProperty(element, property); if (needsUpdate) { break; } } } if (needsUpdate) { updateItem(widget, element); } } /** * Copies attributes of the given element into the given widget. *

* This method is internal to the framework; subclassers should not call * this method. Calls doUpdateItem(widget, element, true). *

* * @param widget * the widget * @param element * the element */ protected final void updateItem(Widget widget, Object element) { SafeRunnable.run(new UpdateItemSafeRunnable(widget, element, true)); } /** * Updates the selection of this viewer. *

* This framework method should be called when the selection in the viewer * widget changes. *

*

* The default implementation of this method notifies all selection change * listeners recorded in an internal state variable. Overriding this method * is generally not required; however, if overriding in a subclass, * super.updateSelection must be invoked. *

* * @param selection * the selection, or null if none */ protected void updateSelection(ISelection selection) { SelectionChangedEvent event = new SelectionChangedEvent(this, selection); fireSelectionChanged(event); } /** * Returns whether this structured viewer is configured to use an internal * map to speed up the mapping between elements and SWT items. *

* The default implementation of this framework method checks whether the * internal map has been initialized. *

* * @return true if the element map is enabled, and * false if disabled */ protected boolean usingElementMap() { return elementMap != null; } @Override public void setLabelProvider(IBaseLabelProvider labelProvider) { if (labelProvider instanceof IColorProvider || labelProvider instanceof IFontProvider) { colorAndFontCollector = new ColorAndFontCollectorWithProviders(labelProvider); } else { colorAndFontCollector = new ColorAndFontCollector(); } super.setLabelProvider(labelProvider); } /** * Build a label up for the element using the supplied label provider. * @param updateLabel The ViewerLabel to collect the result in * @param element The element being decorated. */ protected void buildLabel(ViewerLabel updateLabel, Object element){ if (getLabelProvider() instanceof IViewerLabelProvider) { IViewerLabelProvider itemProvider = (IViewerLabelProvider) getLabelProvider(); itemProvider.updateLabel(updateLabel, element); colorAndFontCollector.setUsedDecorators(); if(updateLabel.hasNewBackground()) { colorAndFontCollector.setBackground(updateLabel.getBackground()); } if(updateLabel.hasNewForeground()) { colorAndFontCollector.setForeground(updateLabel.getForeground()); } if(updateLabel.hasNewFont()) { colorAndFontCollector.setFont(updateLabel.getFont()); } return; } if(getLabelProvider() instanceof ILabelProvider){ ILabelProvider labelProvider = (ILabelProvider) getLabelProvider(); updateLabel.setText(labelProvider.getText(element)); updateLabel.setImage(labelProvider.getImage(element)); } } /** * Build a label up for the element using the supplied label provider. * @param updateLabel The ViewerLabel to collect the result in * @param element The element being decorated. * @param labelProvider ILabelProvider the labelProvider for the receiver. */ void buildLabel(ViewerLabel updateLabel, Object element,IViewerLabelProvider labelProvider){ labelProvider.updateLabel(updateLabel, element); colorAndFontCollector.setUsedDecorators(); if(updateLabel.hasNewBackground()) { colorAndFontCollector.setBackground(updateLabel.getBackground()); } if(updateLabel.hasNewForeground()) { colorAndFontCollector.setForeground(updateLabel.getForeground()); } if(updateLabel.hasNewFont()) { colorAndFontCollector.setFont(updateLabel.getFont()); } } /** * Build a label up for the element using the supplied label provider. * @param updateLabel The ViewerLabel to collect the result in * @param elementPath The path of the element being decorated. * @param labelProvider ILabelProvider the labelProvider for the receiver. */ void buildLabel(ViewerLabel updateLabel, TreePath elementPath,ITreePathLabelProvider labelProvider){ labelProvider.updateLabel(updateLabel, elementPath); colorAndFontCollector.setUsedDecorators(); if(updateLabel.hasNewBackground()) { colorAndFontCollector.setBackground(updateLabel.getBackground()); } if(updateLabel.hasNewForeground()) { colorAndFontCollector.setForeground(updateLabel.getForeground()); } if(updateLabel.hasNewFont()) { colorAndFontCollector.setFont(updateLabel.getFont()); } } /** * Build a label up for the element using the supplied label provider. * @param updateLabel The ViewerLabel to collect the result in * @param element The element being decorated. * @param labelProvider ILabelProvider the labelProvider for the receiver. */ void buildLabel(ViewerLabel updateLabel, Object element,ILabelProvider labelProvider){ updateLabel.setText(labelProvider.getText(element)); updateLabel.setImage(labelProvider.getImage(element)); } /** * Get the ColorAndFontCollector for the receiver. * @return ColorAndFontCollector * @since 3.1 */ protected ColorAndFontCollector getColorAndFontCollector() { return colorAndFontCollector; } @Override protected void handleDispose(DisposeEvent event) { super.handleDispose(event); sorter = null; comparer = null; if (filters != null) filters.clear(); elementMap = newHashtable(1); openListeners.clear(); doubleClickListeners.clear(); colorAndFontCollector.clear(); postSelectionChangedListeners.clear(); } }