| /******************************************************************************* |
| * Copyright (c) 2009, 2018 Wind River Systems 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: |
| * Wind River Systems - initial API and implementation |
| * IBM Corporation - ongoing bug fixing |
| *******************************************************************************/ |
| package org.eclipse.debug.internal.ui.viewers.model; |
| |
| import java.util.ArrayList; |
| import java.util.Arrays; |
| import java.util.Collection; |
| import java.util.HashMap; |
| import java.util.HashSet; |
| import java.util.Iterator; |
| import java.util.LinkedHashMap; |
| import java.util.LinkedList; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.Map.Entry; |
| import java.util.Set; |
| |
| import org.eclipse.debug.internal.ui.viewers.model.provisional.IColumnPresentation; |
| import org.eclipse.debug.internal.ui.viewers.model.provisional.IColumnPresentationFactory; |
| import org.eclipse.debug.internal.ui.viewers.model.provisional.IModelChangedListener; |
| import org.eclipse.debug.internal.ui.viewers.model.provisional.IModelDelta; |
| import org.eclipse.debug.internal.ui.viewers.model.provisional.IModelSelectionPolicy; |
| import org.eclipse.debug.internal.ui.viewers.model.provisional.IPresentationContext; |
| import org.eclipse.debug.internal.ui.viewers.model.provisional.IStateUpdateListener; |
| import org.eclipse.debug.internal.ui.viewers.model.provisional.IViewerUpdateListener; |
| import org.eclipse.debug.internal.ui.viewers.model.provisional.IVirtualItemListener; |
| import org.eclipse.debug.internal.ui.viewers.model.provisional.IVirtualItemValidator; |
| import org.eclipse.debug.internal.ui.viewers.model.provisional.ModelDelta; |
| import org.eclipse.debug.internal.ui.viewers.model.provisional.PresentationContext; |
| import org.eclipse.debug.internal.ui.viewers.model.provisional.VirtualItem; |
| import org.eclipse.debug.internal.ui.viewers.model.provisional.VirtualItem.Index; |
| import org.eclipse.debug.internal.ui.viewers.model.provisional.VirtualTree; |
| import org.eclipse.jface.resource.ImageDescriptor; |
| import org.eclipse.jface.viewers.IContentProvider; |
| import org.eclipse.jface.viewers.ISelection; |
| import org.eclipse.jface.viewers.ITreeSelection; |
| import org.eclipse.jface.viewers.SelectionChangedEvent; |
| import org.eclipse.jface.viewers.TreePath; |
| import org.eclipse.jface.viewers.TreeSelection; |
| import org.eclipse.jface.viewers.Viewer; |
| import org.eclipse.jface.viewers.ViewerFilter; |
| import org.eclipse.jface.viewers.ViewerLabel; |
| import org.eclipse.swt.SWT; |
| import org.eclipse.swt.graphics.Color; |
| import org.eclipse.swt.graphics.Font; |
| import org.eclipse.swt.graphics.FontData; |
| import org.eclipse.swt.graphics.Image; |
| import org.eclipse.swt.graphics.RGB; |
| import org.eclipse.swt.widgets.Control; |
| import org.eclipse.swt.widgets.Display; |
| import org.eclipse.ui.IMemento; |
| |
| /** |
| * A tree model viewer without a UI component. |
| * @since 3.5 |
| */ |
| @SuppressWarnings("deprecation") |
| public class InternalVirtualTreeModelViewer extends Viewer |
| implements IVirtualItemListener, |
| org.eclipse.debug.internal.ui.viewers.model.ITreeModelViewer, |
| IInternalTreeModelViewer |
| { |
| |
| /** |
| * Memento type for the visible columns for a presentation context. |
| * A memento is created for each column presentation keyed by column number |
| */ |
| private static final String VISIBLE_COLUMNS = "VISIBLE_COLUMNS"; //$NON-NLS-1$ |
| |
| /** |
| * Memento type for whether columns are visible for a presentation context. |
| * Booleans are keyed by column presentation id |
| */ |
| private static final String SHOW_COLUMNS = "SHOW_COLUMNS"; //$NON-NLS-1$ |
| /** |
| * Memento key for the number of visible columns in a VISIBLE_COLUMNS memento |
| * or for the width of a column |
| */ |
| private static final String SIZE = "SIZE"; //$NON-NLS-1$ |
| /** |
| * Memento key prefix a visible column |
| */ |
| private static final String COLUMN = "COLUMN"; //$NON-NLS-1$ |
| |
| /** |
| * Item's tree path cache |
| */ |
| private static final String TREE_PATH_KEY = "TREE_PATH_KEY"; //$NON-NLS-1$ |
| |
| /** |
| * Viewer filters currently configured for viewer. |
| */ |
| private ViewerFilter[] fFilters = new ViewerFilter[0]; |
| |
| /** |
| * The display that this virtual tree viewer is associated with. It is used |
| * for access to the UI thread. |
| */ |
| private Display fDisplay; |
| |
| /** |
| * The object that allows the model to identify what this view |
| * is presenting. |
| */ |
| private IPresentationContext fContext; |
| |
| /** |
| * Input into the viewer. |
| */ |
| private Object fInput; |
| |
| /** |
| * The tree of items in this viewer. |
| */ |
| private VirtualTree fTree; |
| |
| /** |
| * Mapping of data elements in the tree to the items that hold them. The |
| * tree may contain the same element in several places, so the map values |
| * are lists. |
| */ |
| private Map<Object, List<VirtualItem>> fItemsMap = new HashMap<>(); |
| |
| /** |
| * Whether to notify the content provider when an element is unmapped. |
| * Used to suppress the notification during an associate operation. |
| */ |
| private boolean fNotifyUnmap = true; |
| |
| /** |
| * The label provider, must be the tree model provider. |
| */ |
| private TreeModelLabelProvider fLabelProvider; |
| |
| /** |
| * The content provider must be a tree model provider. |
| */ |
| private TreeModelContentProvider fContentProvider; |
| |
| /** |
| * Flag indicating whether the viewer is currently executing an operation |
| * at the end of which the selection will be restored. |
| */ |
| private boolean fPreservingSelecction; |
| |
| /** |
| * Flag indicating that the selection should not be restored at the end |
| * of a preserving-selection operation. |
| */ |
| private boolean fRestoreSelection; |
| |
| /** |
| * Level to which the tree should automatically expand elements. |
| * <code>-1<code> indicates that all levels shoudl be expanded. |
| */ |
| private int fAutoExpandToLevel = 0; |
| |
| /** |
| * Current column presentation or <code>null</code> |
| */ |
| private IColumnPresentation fColumnPresentation = null; |
| |
| /** |
| * Map of columns presentation id to its visible columns id's (String[]) |
| * When a columns presentation is not in the map, default settings are used. |
| */ |
| private Map<String, String[]> fVisibleColumns = new HashMap<>(); |
| |
| /** |
| * Map of column presentation id to whether columns should be displayed |
| * for that presentation (the user can toggle columns on/off when a |
| * presentation is optional. |
| */ |
| private Map<String, Boolean> fShowColumns = new HashMap<>(); |
| |
| /** |
| * Runnable for validating the virtual tree. It is scheduled to run in the |
| * UI thread whenever a tree validation is requested. |
| */ |
| private Runnable fValidateRunnable; |
| |
| public InternalVirtualTreeModelViewer(Display display, int style, IPresentationContext context, IVirtualItemValidator itemValidator) { |
| fDisplay = display; |
| fContext = context; |
| fTree = new VirtualTree(style, itemValidator); |
| fTree.addItemListener(this); |
| |
| fContentProvider = new TreeModelContentProvider(); |
| fLabelProvider = new TreeModelLabelProvider(this); |
| |
| if ((style & SWT.POP_UP) != 0) { |
| getContentProvider().setModelDeltaMask(~ITreeModelContentProvider.CONTROL_MODEL_DELTA_FLAGS); |
| } |
| } |
| |
| @Override |
| public Object getInput() { |
| return fInput; |
| } |
| |
| @Override |
| public Control getControl() { |
| // The virtual viewer does not have an SWT control associated with it. |
| // Fortunately this method is not used by the base Viewer class. |
| return null; |
| } |
| |
| @Override |
| public Display getDisplay() { |
| return fDisplay; |
| } |
| |
| @Override |
| public void setInput(Object input) { |
| Object oldInput = fInput; |
| getContentProvider().inputChanged(this, oldInput, input); |
| fItemsMap.clear(); |
| fTree.clearAll(); |
| fInput = input; |
| mapElement(fInput, getTree()); |
| getContentProvider().postInputChanged(this, oldInput , input); |
| fTree.setData(fInput); |
| fTree.setSelection(EMPTY_ITEMS_ARRAY); |
| inputChanged(fInput, oldInput); |
| refresh(); |
| } |
| |
| @Override |
| public void replace(Object parentElementOrTreePath, final int index, Object element) { |
| VirtualItem[] selectedItems = fTree.getSelection(); |
| TreeSelection selection = (TreeSelection) getSelection(); |
| VirtualItem[] itemsToDisassociate; |
| if (parentElementOrTreePath instanceof TreePath) { |
| TreePath elementPath = ((TreePath) parentElementOrTreePath).createChildPath(element); |
| itemsToDisassociate = findItems(elementPath); |
| } else { |
| itemsToDisassociate = findItems(element); |
| } |
| |
| VirtualItem[] parentItems = findItems(parentElementOrTreePath); |
| for (int i = 0; i < parentItems.length; i++) { |
| VirtualItem parentItem = parentItems[i]; |
| if (index < parentItem.getItemCount()) { |
| VirtualItem item = parentItem.getItem(new Index(index)); |
| selection = adjustSelectionForReplace(selectedItems, selection, item, element, parentItem.getData()); |
| // disassociate any different item that represents the |
| // same element under the same parent (the tree) |
| for (int j = 0; j < itemsToDisassociate.length; j++) { |
| VirtualItem itemToDisassociate = itemsToDisassociate[j]; |
| if (itemToDisassociate != item && itemsToDisassociate[j].getParent() == parentItem) { |
| disassociate(itemToDisassociate); |
| itemToDisassociate.getParent().clear(itemToDisassociate.getIndex()); |
| } |
| } |
| //Object oldData = item.getData(); |
| associate(element, item); |
| doUpdate(item); |
| VirtualItem[] children = item.getItems(); |
| for (int j = 0; j < children.length; j++) { |
| children[j].setNeedsDataUpdate(); |
| } |
| } |
| } |
| // Restore the selection if we are not already in a nested |
| // preservingSelection: |
| if (!fPreservingSelecction) { |
| internalSetSelection(selection, false); |
| // send out notification if old and new differ |
| ISelection newSelection = getSelection(); |
| if (!newSelection.equals(selection)) { |
| handleInvalidSelection(selection, newSelection); |
| } |
| } |
| validate(); |
| } |
| |
| public VirtualTree getTree() { |
| return fTree; |
| } |
| |
| @Override |
| public void insert(Object parentOrTreePath, Object element, int position) { |
| if (parentOrTreePath instanceof TreePath) { |
| VirtualItem parentItem = findItem((TreePath) parentOrTreePath); |
| if (parentItem != null) { |
| VirtualItem item = parentItem.addItem(position); |
| item.setData(element); |
| mapElement(element, item); |
| doUpdate(item); |
| } |
| } else { |
| // TODO: Implement insert() for element |
| } |
| validate(); |
| } |
| |
| @Override |
| public void remove(final Object parentOrTreePath, final int index) { |
| final List<TreePath> oldSelection = new LinkedList<>(Arrays.asList(((TreeSelection) getSelection()).getPaths())); |
| preservingSelection(() -> { |
| TreePath removedPath = null; |
| VirtualItem[] parentItems = findItems(parentOrTreePath); |
| for (int i = 0; i < parentItems.length; i++) { |
| VirtualItem parentItem = parentItems[i]; |
| if (parentItem.isDisposed()) { |
| continue; |
| } |
| |
| // Parent item is not expanded so just update its contents so that |
| // the plus sign gets refreshed. |
| if (!parentItem.getExpanded()) { |
| parentItem.setNeedsCountUpdate(); |
| parentItem.setItemCount(-1); |
| virtualLazyUpdateHasChildren(parentItem); |
| } |
| |
| if (index < parentItem.getItemCount()) { |
| VirtualItem item = parentItem.getItem(new VirtualItem.Index(index)); |
| |
| if (item.getData() != null) { |
| removedPath = getTreePathFromItem(item); |
| disassociate(item); |
| } |
| parentItem.remove(item.getIndex()); |
| } |
| } |
| |
| if (removedPath != null) { |
| boolean removed = false; |
| for (Iterator<TreePath> it = oldSelection.iterator(); it.hasNext();) { |
| TreePath path = it.next(); |
| if (path.startsWith(removedPath, null)) { |
| it.remove(); |
| removed = true; |
| } |
| } |
| if (removed) { |
| setSelection(new TreeSelection(oldSelection.toArray(new TreePath[oldSelection.size()])), false); |
| } |
| } |
| }); |
| } |
| |
| @Override |
| public void remove(Object elementOrPath) { |
| if (elementOrPath.equals(getInput()) || TreePath.EMPTY.equals(elementOrPath)) { |
| setInput(null); |
| return; |
| } |
| |
| VirtualItem[] items = findItems(elementOrPath); |
| if (items.length > 0) { |
| for (int j = 0; j < items.length; j++) { |
| disassociate(items[j]); |
| items[j].getParent().remove(items[j].getIndex()); |
| } |
| } |
| } |
| |
| private TreeSelection adjustSelectionForReplace(VirtualItem[] selectedItems, TreeSelection selection, |
| VirtualItem item, Object element, Object parentElement) |
| { |
| if (item.getData() != null || selectedItems.length == selection.size() || parentElement == null) { |
| // Don't do anything - we are not seeing an instance of bug 185673 |
| return selection; |
| } |
| for (int i = 0; i < selectedItems.length; i++) { |
| if (item == selectedItems[i]) { |
| // The current item was selected, but its data is null. |
| // The data will be replaced by the given element, so to keep |
| // it selected, we have to add it to the selection. |
| TreePath[] originalPaths = selection.getPaths(); |
| int length = originalPaths.length; |
| TreePath[] paths = new TreePath[length + 1]; |
| System.arraycopy(originalPaths, 0, paths, 0, length); |
| // set the element temporarily so that we can call getTreePathFromItem |
| item.setData(element); |
| paths[length] = getTreePathFromItem(item); |
| item.setData(null); |
| return new TreeSelection(paths, selection.getElementComparer()); |
| } |
| } |
| // The item was not selected, return the given selection |
| return selection; |
| } |
| |
| // private VirtualTreeSelection adjustSelectionForReplace(VirtualTreeSelection selection, VirtualItem item, |
| // Object element, Object parentElement) |
| // { |
| // if (selection.getItems().containsKey(item)) { |
| // if (item.getData() == null) { |
| // // The current item was selected, but its data is null. |
| // // The data will be replaced by the given element, so to keep |
| // // it selected, we have to add it to the selection. |
| // |
| // // set the element temporarily so that we can call getTreePathFromItem |
| // item.setData(element); |
| // TreePath path = getTreePathFromItem(item); |
| // item.setData(null); |
| // |
| // Map map = new LinkedHashMap(selection.getItems()); |
| // map.put(item, path); |
| // TreePath[] paths = new TreePath[selection.getPaths().length + 1]; |
| // int i = 0; |
| // for (Iterator itr = map.values().iterator(); itr.hasNext();) { |
| // TreePath nextPath = (TreePath)itr.next(); |
| // if (nextPath != null) { |
| // paths[i++] = nextPath; |
| // } |
| // } |
| // return new VirtualTreeSelection(map, paths); |
| // } else if (!item.getData().equals(element)) { |
| // // The current item was selected by the new element is |
| // // different than the previous element in the item. |
| // // Remove this item from selection. |
| // Map map = new LinkedHashMap(selection.getItems()); |
| // map.remove(item); |
| // TreePath[] paths = new TreePath[selection.getPaths().length - 1]; |
| // int i = 0; |
| // for (Iterator itr = map.values().iterator(); itr.hasNext();) { |
| // TreePath nextPath = (TreePath)itr.next(); |
| // if (nextPath != null) { |
| // paths[i++] = nextPath; |
| // } |
| // } |
| // return new VirtualTreeSelection(map, paths); |
| // } |
| // } |
| // if (item.getData() != null || selection.getItems().size() == selection.size() || parentElement == null) { |
| // // Don't do anything - we are not seeing an instance of bug 185673 |
| // return selection; |
| // } |
| // if (item.getData() == null && selection.getItems().containsKey(item)) { |
| // } |
| // // The item was not selected, return the given selection |
| // return selection; |
| // } |
| |
| |
| @Override |
| public void reveal(TreePath path, final int index) { |
| VirtualItem parentItem = findItem(path); |
| if (parentItem != null && parentItem.getItemCount() >= index) { |
| VirtualItem revealItem = parentItem.getItem(new Index(index)); |
| getTree().showItem(revealItem); |
| getTree().validate(); |
| } |
| // TODO: implement reveal() |
| } |
| |
| @Override |
| public int findElementIndex(TreePath parentPath, Object element) { |
| VirtualItem parentItem = findItem(parentPath); |
| if (parentItem != null) { |
| VirtualItem item = parentItem.findItem(element); |
| if (item != null) { |
| return item.getIndex().intValue(); |
| } |
| } |
| return -1; |
| } |
| |
| @Override |
| public boolean getElementChildrenRealized(TreePath parentPath) { |
| VirtualItem parentItem = findItem(parentPath); |
| if (parentItem != null) { |
| return !parentItem.childrenNeedDataUpdate(); |
| } |
| return true; |
| } |
| |
| |
| private ITreeModelLabelProvider getLabelProvider() { |
| return fLabelProvider; |
| } |
| |
| private ITreeModelContentProvider getContentProvider() { |
| return fContentProvider; |
| } |
| |
| public static int ALL_LEVELS = -1; |
| |
| @Override |
| public void refresh() { |
| refresh(fTree); |
| validate(); |
| } |
| |
| @Override |
| public void refresh(Object element) { |
| VirtualItem[] items = findItems(element); |
| for (int i = 0; i < items.length; i++) { |
| refresh(items[i]); |
| validate(); |
| } |
| } |
| |
| private void refresh(VirtualItem item) { |
| getContentProvider().preserveState(getTreePathFromItem(item)); |
| |
| if (!item.needsDataUpdate()) { |
| if (item.getParent() != null) { |
| item.setNeedsLabelUpdate(); |
| virtualLazyUpdateHasChildren(item); |
| } |
| |
| VirtualItem[] items = item.getItems(); |
| for (int i = 0; i < items.length; i++) { |
| items[i].setNeedsDataUpdate(); |
| } |
| } |
| refreshStruct(item); |
| } |
| |
| private void refreshStruct(VirtualItem item) { |
| boolean expanded = false; |
| if (item.getParent() == null) { |
| // root item |
| virtualLazyUpdateChildCount(item); |
| expanded = true; |
| } else { |
| if (item.getExpanded()) { |
| virtualLazyUpdateData(item); |
| expanded = true; |
| } |
| } |
| |
| VirtualItem[] items = item.getItems(); |
| for (int i = 0; i < items.length; i++) { |
| if (expanded) { |
| refreshStruct(items[i]); |
| } else { |
| item.clear(new VirtualItem.Index(i)); |
| } |
| } |
| } |
| |
| private void validate() { |
| if (fValidateRunnable == null) { |
| fValidateRunnable = () -> { |
| if (!fTree.isDisposed()) { |
| fValidateRunnable = null; |
| fTree.validate(); |
| } |
| }; |
| getDisplay().asyncExec(fValidateRunnable); |
| } |
| } |
| |
| @Override |
| protected void inputChanged(Object input, Object oldInput) { |
| resetColumns(input); |
| } |
| |
| @Override |
| public int getAutoExpandLevel() { |
| return fAutoExpandToLevel; |
| } |
| |
| @Override |
| public void setAutoExpandLevel(int level) { |
| fAutoExpandToLevel = level; |
| } |
| |
| public VirtualItem findItem(TreePath path) { |
| if (path.getSegmentCount() == 0) { |
| return fTree; |
| } |
| |
| List<VirtualItem> itemsList = fItemsMap.get(path.getLastSegment()); |
| if (itemsList != null) { |
| for (VirtualItem item : itemsList) { |
| if (path.equals(getTreePathFromItem(item))) { |
| return item; |
| } |
| } |
| } |
| |
| return null; |
| } |
| |
| static private final VirtualItem[] EMPTY_ITEMS_ARRAY = new VirtualItem[0]; |
| |
| public VirtualItem[] findItems(Object elementOrTreePath) { |
| Object element = elementOrTreePath; |
| if (elementOrTreePath instanceof TreePath) { |
| TreePath path = (TreePath)elementOrTreePath; |
| if (path.getSegmentCount() == 0) { |
| return new VirtualItem[] { getTree() }; |
| } |
| element = path.getLastSegment(); |
| } |
| List<VirtualItem> itemsList = fItemsMap.get(element); |
| if (itemsList == null) { |
| return EMPTY_ITEMS_ARRAY; |
| } else { |
| return itemsList.toArray(new VirtualItem[itemsList.size()]); |
| } |
| } |
| |
| @Override |
| public void setElementData(TreePath path, int numColumns, String[] labels, ImageDescriptor[] images, |
| FontData[] fontDatas, RGB[] foregrounds, RGB[] backgrounds) { |
| VirtualItem item = findItem(path); |
| if (item != null) { |
| item.setData(VirtualItem.LABEL_KEY, labels); |
| item.setData(VirtualItem.IMAGE_KEY, images); |
| item.setData(VirtualItem.FOREGROUND_KEY, foregrounds); |
| item.setData(VirtualItem.BACKGROUND_KEY, backgrounds); |
| item.setData(VirtualItem.FONT_KEY, fontDatas); |
| } |
| } |
| |
| @Override |
| public void setChildCount(final Object elementOrTreePath, final int count) { |
| preservingSelection(() -> { |
| VirtualItem[] items = findItems(elementOrTreePath); |
| for (int i = 0; i < items.length; i++) { |
| VirtualItem[] children = items[i].getItems(); |
| for (int j = 0; j < children.length; j++) { |
| if (children[j].getData() != null && children[j].getIndex().intValue() >= count) { |
| disassociate(children[j]); |
| } |
| } |
| |
| items[i].setItemCount(count); |
| } |
| }); |
| validate(); |
| } |
| |
| @Override |
| public void setHasChildren(final Object elementOrTreePath, final boolean hasChildren) { |
| preservingSelection(() -> { |
| VirtualItem[] items = findItems(elementOrTreePath); |
| for (int i = 0; i < items.length; i++) { |
| VirtualItem item = items[i]; |
| |
| if (!hasChildren) { |
| VirtualItem[] children = item.getItems(); |
| for (int j = 0; j < children.length; j++) { |
| if (children[j].getData() != null) { |
| disassociate(children[j]); |
| } |
| } |
| } |
| |
| item.setHasItems(hasChildren); |
| if (hasChildren) { |
| if (!item.getExpanded()) { |
| item.setItemCount(-1); |
| } else { |
| virtualLazyUpdateChildCount(item); |
| } |
| } |
| } |
| }); |
| } |
| |
| @Override |
| public boolean getHasChildren(Object elementOrTreePath) { |
| VirtualItem[] items = findItems(elementOrTreePath); |
| if (items.length > 0) { |
| return items[0].hasItems(); |
| } |
| return false; |
| } |
| |
| private void virtualLazyUpdateHasChildren(VirtualItem item) { |
| TreePath treePath; |
| treePath = getTreePathFromItem(item); |
| item.clearNeedsCountUpdate(); |
| getContentProvider().updateHasChildren(treePath); |
| } |
| |
| private void virtualLazyUpdateChildCount(VirtualItem item) { |
| item.clearNeedsCountUpdate(); |
| getContentProvider().updateChildCount(getTreePathFromItem(item), item.getItemCount()); |
| } |
| |
| private void virtualLazyUpdateData(VirtualItem item) { |
| item.clearNeedsDataUpdate(); |
| getContentProvider().updateElement(getTreePathFromItem(item.getParent()), item.getIndex().intValue()); |
| } |
| |
| private void virtualLazyUpdateLabel(VirtualItem item) { |
| item.clearNeedsLabelUpdate(); |
| if ( !getLabelProvider().update(getTreePathFromItem(item)) ) { |
| if (item.getData() instanceof String) { |
| item.setData(VirtualItem.LABEL_KEY, new String[] { (String)item.getData() } ); |
| } |
| } |
| } |
| |
| private TreePath getTreePathFromItem(VirtualItem item) { |
| List<Object> segments = new LinkedList<>(); |
| while (item.getParent() != null) { |
| segments.add(0, item.getData()); |
| item = item.getParent(); |
| } |
| return new TreePath(segments.toArray()); |
| } |
| |
| private void unmapElement(Object element, VirtualItem item) { |
| if (fNotifyUnmap) { |
| // TODO: should we update the filter with the "new non-identical element"? |
| IContentProvider provider = getContentProvider(); |
| if (provider instanceof TreeModelContentProvider) { |
| ((TreeModelContentProvider) provider).unmapPath((TreePath) item.getData(TREE_PATH_KEY)); |
| } |
| } |
| |
| List<VirtualItem> itemsList = fItemsMap.get(element); |
| if (itemsList != null) { |
| itemsList.remove(item); |
| if (itemsList.isEmpty()) { |
| fItemsMap.remove(element); |
| } |
| } |
| } |
| |
| private void mapElement(Object element, VirtualItem item) { |
| // Get the items set for given element, if it doesn't exist, create it. |
| // When retrieving the set, also remove it from the map, it will be |
| // re-inserted to make sure that the new instance of element is used |
| // in case the element has changed but the elment is equal to the old |
| // one. |
| List<VirtualItem> itemsList = fItemsMap.remove(element); |
| if (itemsList == null) { |
| itemsList = new ArrayList<>(1); |
| } |
| |
| if (!itemsList.contains(item)) { |
| itemsList.add(item); |
| } |
| |
| // Insert the set back into the map. |
| fItemsMap.put(element, itemsList); |
| |
| item.setData(TREE_PATH_KEY, getTreePathFromItem(item)); |
| } |
| |
| @Override |
| public void revealed(VirtualItem item) { |
| if (item.needsDataUpdate()) { |
| virtualLazyUpdateData(item); |
| } else if (item.getData() != null) { |
| if (item.needsLabelUpdate()) { |
| virtualLazyUpdateLabel(item); |
| } |
| if (item.needsCountUpdate() && item.getExpanded()) { |
| virtualLazyUpdateChildCount(item); |
| } |
| } |
| } |
| |
| @Override |
| public void disposed(VirtualItem item) { |
| if (!fTree.isDisposed()) { |
| Object data = item.getData(); |
| if (data != null) { |
| unmapElement(data, item); |
| } |
| } |
| } |
| |
| private void associate(Object element, VirtualItem item) { |
| Object data = item.getData(); |
| if (data != null && data != element && data.equals(element)) { |
| // elements are equal but not identical |
| // -> being removed from map, but should not change filters |
| try { |
| fNotifyUnmap = false; |
| doAssociate(element, item); |
| } finally { |
| fNotifyUnmap = true; |
| } |
| } else { |
| doAssociate(element, item); |
| } |
| |
| } |
| |
| private void doAssociate(Object element, VirtualItem item) { |
| Object data = item.getData(); |
| if (data != null && data != element && data.equals(element)) { |
| // workaround for PR 1FV62BT |
| // assumption: elements are equal but not identical |
| // -> remove from map but don't touch children |
| unmapElement(data, item); |
| item.setData(element); |
| mapElement(element, item); |
| } else { |
| // recursively disassociate all |
| if (data != element) { |
| if (data != null) { |
| unmapElement(element, item); |
| disassociate(item); |
| } |
| item.setData(element); |
| } |
| // Always map the element, even if data == element, |
| // since unmapAllElements() can leave the map inconsistent |
| // See bug 2741 for details. |
| mapElement(element, item); |
| } |
| } |
| |
| private void disassociate(VirtualItem item) { |
| unmapElement(item.getData(), item); |
| |
| // Clear the map before we clear the data |
| item.setData(null); |
| |
| // Disassociate the children |
| VirtualItem[] items = item.getItems(); |
| for (int i = 0; i < items.length; i++) { |
| if (items[i].getData() != null) { |
| disassociate(items[i]); |
| } |
| } |
| } |
| |
| @Override |
| public void setSelection(ISelection selection, boolean reveal) { |
| setSelection(selection, reveal, false); |
| } |
| |
| /* (non-Javadoc) |
| * @see org.eclipse.debug.internal.ui.viewers.model.ITreeModelViewer#setSelection(org.eclipse.jface.viewers.ISelection, boolean, boolean) |
| */ |
| @Override |
| public void setSelection(ISelection selection, boolean reveal, boolean force) { |
| trySelection(selection, reveal, force); |
| } |
| |
| /* (non-Javadoc) |
| * @see org.eclipse.debug.internal.ui.viewers.model.ITreeModelViewer#trySelection(org.eclipse.jface.viewers.ISelection, boolean, boolean) |
| */ |
| @Override |
| public boolean trySelection(ISelection selection, boolean reveal, boolean force) { |
| if (!force && !overrideSelection(getSelection(), selection)) { |
| return false; |
| } |
| |
| if (!fPreservingSelecction) { |
| internalSetSelection(selection, reveal); |
| fireSelectionChanged(new SelectionChangedEvent(this, selection)); |
| } else { |
| fRestoreSelection = false; |
| internalSetSelection(selection, reveal); |
| } |
| return true; |
| } |
| |
| private void internalSetSelection(ISelection selection, boolean reveal) { |
| if (selection instanceof ITreeSelection) { |
| TreePath[] paths = ((ITreeSelection) selection).getPaths(); |
| List<VirtualItem> newSelection = new ArrayList<>(paths.length); |
| for (int i = 0; i < paths.length; ++i) { |
| // Use internalExpand since item may not yet be created. See |
| // 1G6B1AR. |
| VirtualItem item = findItem(paths[i]); |
| if (item != null) { |
| newSelection.add(item); |
| } |
| } |
| fTree.setSelection(newSelection.toArray(new VirtualItem[newSelection.size()])); |
| |
| // Although setting the selection in the control should reveal it, |
| // setSelection may be a no-op if the selection is unchanged, |
| // so explicitly reveal items in the selection here. |
| // See bug 100565 for more details. |
| if (reveal && newSelection.size() > 0) { |
| // Iterate backwards so the first item in the list |
| // is the one guaranteed to be visible |
| for (int i = (newSelection.size() - 1); i >= 0; i--) { |
| fTree.showItem(newSelection.get(i)); |
| } |
| } |
| } else { |
| fTree.setSelection(EMPTY_ITEMS_ARRAY); |
| } |
| |
| // Make sure that the new selection is properly revealed. |
| validate(); |
| } |
| |
| @Override |
| public void update(Object element) { |
| VirtualItem[] items = findItems(element); |
| for (int i = 0; i < items.length; i++) { |
| doUpdate(items[i]); |
| } |
| } |
| |
| public void doUpdate(VirtualItem item) { |
| item.setNeedsLabelUpdate(); |
| validate(); |
| } |
| |
| @Override |
| public ISelection getSelection() { |
| if (fTree.isDisposed()) { |
| return TreeSelection.EMPTY; |
| } |
| VirtualItem[] items = fTree.getSelection(); |
| ArrayList<TreePath> list = new ArrayList<>(items.length); |
| Map<VirtualItem, TreePath> map = new LinkedHashMap<>(items.length * 4 / 3); |
| for (int i = 0; i < items.length; i++) { |
| TreePath path = null; |
| if (items[i].getData() != null) { |
| path = getTreePathFromItem(items[i]); |
| list.add(path); |
| } |
| map.put(items[i], path); |
| } |
| return new TreeSelection(list.toArray(new TreePath[list.size()])); |
| } |
| |
| private void preservingSelection(Runnable updateCode) { |
| |
| ISelection oldSelection = null; |
| try { |
| // preserve selection |
| oldSelection = getSelection(); |
| fPreservingSelecction = fRestoreSelection = true; |
| |
| // perform the update |
| updateCode.run(); |
| |
| } finally { |
| fPreservingSelecction = false; |
| |
| // restore selection |
| if (fRestoreSelection) { |
| internalSetSelection(oldSelection, false); |
| } |
| |
| // send out notification if old and new differ |
| ISelection newSelection = getSelection(); |
| if (!newSelection.equals(oldSelection)) { |
| handleInvalidSelection(oldSelection, newSelection); |
| } |
| } |
| } |
| |
| @Override |
| public void expandToLevel(Object elementOrTreePath, int level) { |
| VirtualItem[] items = findItems(elementOrTreePath); |
| if (items.length > 0) { |
| expandToLevel(items[0], level); |
| } |
| validate(); |
| } |
| |
| @Override |
| public void setExpandedState(Object elementOrTreePath, boolean expanded) { |
| VirtualItem[] items = findItems(elementOrTreePath); |
| for (int i = 0; i < items.length; i++) { |
| items[i].setExpanded(expanded); |
| } |
| validate(); |
| } |
| |
| @Override |
| public boolean getExpandedState(Object elementOrTreePath) { |
| VirtualItem[] items = findItems(elementOrTreePath); |
| if (items.length > 0) { |
| return items[0].getExpanded(); |
| } |
| return false; |
| } |
| |
| private void expandToLevel(VirtualItem item, int level) { |
| if (level == ALL_LEVELS || level > 0) { |
| if (!item.hasItems()) { |
| return; |
| } |
| |
| item.setExpanded(true); |
| |
| if (item.getData() == null) { |
| virtualLazyUpdateData(item); |
| // Cannot expand children if data is null. |
| return; |
| } |
| |
| if (level == ALL_LEVELS || level > 1) { |
| VirtualItem[] children = item.getItems(); |
| int newLevel = (level == ALL_LEVELS ? ALL_LEVELS |
| : level - 1); |
| for (int i = 0; i < children.length; i++) { |
| expandToLevel(children[i], newLevel); |
| } |
| } |
| } |
| } |
| |
| private void handleInvalidSelection(ISelection selection, ISelection newSelection) { |
| IModelSelectionPolicy selectionPolicy = ViewerAdapterService.getSelectionPolicy(selection, getPresentationContext()); |
| if (selectionPolicy != null) { |
| while (!selection.equals(newSelection)) { |
| ISelection temp = newSelection; |
| selection = selectionPolicy.replaceInvalidSelection(selection, newSelection); |
| if (selection == null) { |
| selection = TreeSelection.EMPTY; |
| } |
| if (!temp.equals(selection)) { |
| internalSetSelection(selection, false); |
| newSelection = getSelection(); |
| } else { |
| break; |
| } |
| } |
| } |
| |
| fireSelectionChanged(new SelectionChangedEvent(this, newSelection)); |
| } |
| |
| /** |
| * Returns whether the candidate selection should override the current |
| * selection. |
| * |
| * @param current Current selection in viewer |
| * @param candidate New potential selection requested by model. |
| * @return true if candidate selection should be set to viewer. |
| */ |
| @Override |
| public boolean overrideSelection(ISelection current, ISelection candidate) { |
| IModelSelectionPolicy selectionPolicy = ViewerAdapterService.getSelectionPolicy(current, getPresentationContext()); |
| if (selectionPolicy == null) { |
| return true; |
| } |
| if (selectionPolicy.contains(candidate, getPresentationContext())) { |
| return selectionPolicy.overrides(current, candidate, getPresentationContext()); |
| } |
| return !selectionPolicy.isSticky(current, getPresentationContext()); |
| } |
| |
| @Override |
| public ViewerFilter[] getFilters() { |
| return fFilters; |
| } |
| |
| @Override |
| public void addFilter(ViewerFilter filter) { |
| ViewerFilter[] newFilters = new ViewerFilter[fFilters.length + 1]; |
| System.arraycopy(fFilters, 0, newFilters, 0, fFilters.length); |
| newFilters[fFilters.length] = filter; |
| fFilters = newFilters; |
| } |
| |
| @Override |
| public void setFilters(ViewerFilter... filters) { |
| fFilters = filters; |
| } |
| |
| public void dispose() { |
| if (fColumnPresentation != null) { |
| fColumnPresentation.dispose(); |
| } |
| |
| if (fContentProvider != null) { |
| fContentProvider.dispose(); |
| fContentProvider = null; |
| } |
| if (fLabelProvider != null) { |
| fLabelProvider.dispose(); |
| fLabelProvider = null; |
| } |
| |
| fTree.removeItemListener(this); |
| fTree.dispose(); |
| } |
| |
| /** |
| * Returns this viewer's presentation context. |
| * |
| * @return presentation context |
| */ |
| @Override |
| public IPresentationContext getPresentationContext() { |
| return fContext; |
| } |
| |
| /** |
| * Configures the columns for the given viewer input. |
| * |
| * @param input new viewer input |
| */ |
| private void resetColumns(Object input) { |
| if (input != null) { |
| // only change columns if the input is non-null (persist when empty) |
| IColumnPresentationFactory factory = ViewerAdapterService.getColumnPresentationFactory(input); |
| PresentationContext context = (PresentationContext) getPresentationContext(); |
| String type = null; |
| if (factory != null) { |
| type = factory.getColumnPresentationId(context, input); |
| } |
| if (type != null && factory != null) { |
| if (fColumnPresentation != null) { |
| if (!fColumnPresentation.getId().equals(type)) { |
| // dispose old, create new |
| fColumnPresentation.dispose(); |
| fColumnPresentation = null; |
| } |
| } |
| if (fColumnPresentation == null) { |
| fColumnPresentation = factory.createColumnPresentation(context, input); |
| if (fColumnPresentation != null) { |
| fColumnPresentation.init(context); |
| configureColumns(); |
| } |
| } |
| } else { |
| if (fColumnPresentation != null) { |
| fColumnPresentation.dispose(); |
| fColumnPresentation = null; |
| configureColumns(); |
| } |
| } |
| } |
| } |
| |
| /** |
| * Configures the columns based on the current settings. |
| */ |
| protected void configureColumns() { |
| if (fColumnPresentation != null) { |
| IColumnPresentation build = null; |
| if (isShowColumns(fColumnPresentation.getId())) { |
| build = fColumnPresentation; |
| } |
| buildColumns(build); |
| } else { |
| // get rid of columns |
| buildColumns(null); |
| } |
| } |
| |
| /** |
| * Toggles columns on/off for the current column presentation, if any. |
| * |
| * @param show whether to show columns if the current input supports |
| * columns |
| */ |
| public void setShowColumns(boolean show) { |
| if (show) { |
| if (!isShowColumns()) { |
| fShowColumns.remove(fColumnPresentation.getId()); |
| } |
| } else { |
| if (isShowColumns()){ |
| fShowColumns.put(fColumnPresentation.getId(), Boolean.FALSE); |
| } |
| } |
| refreshColumns(); |
| } |
| |
| /** |
| * Refreshes the columns in the view, based on the viewer input. |
| */ |
| protected void refreshColumns() { |
| configureColumns(); |
| refresh(); |
| } |
| |
| /** |
| * @return Returns true if columns are being displayed currently. |
| */ |
| public boolean isShowColumns() { |
| if (fColumnPresentation != null) { |
| return isShowColumns(fColumnPresentation.getId()); |
| } |
| return false; |
| } |
| |
| /** |
| * Returns whether columns can be toggled on/off for the current input. |
| * |
| * @return whether columns can be toggled on/off for the current input |
| */ |
| public boolean canToggleColumns() { |
| return fColumnPresentation != null && fColumnPresentation.isOptional(); |
| } |
| |
| protected boolean isShowColumns(String columnPresentationId) { |
| Boolean bool = fShowColumns.get(columnPresentationId); |
| if (bool == null) { |
| return true; |
| } |
| return bool.booleanValue(); |
| } |
| |
| /** |
| * Creates new columns for the given presentation. |
| * |
| * @param presentation presentation context to build columns for. |
| */ |
| protected void buildColumns(IColumnPresentation presentation) { |
| PresentationContext presentationContext = (PresentationContext) getPresentationContext(); |
| if (presentation != null) { |
| presentationContext.setColumns(getVisibleColumns()); |
| } else { |
| presentationContext.setColumns(null); |
| } |
| } |
| |
| /** |
| * Returns identifiers of the visible columns in this viewer, or <code>null</code> |
| * if there is currently no column presentation. |
| * |
| * @return visible columns or <code>null</code> |
| */ |
| @Override |
| public String[] getVisibleColumns() { |
| if (isShowColumns()) { |
| IColumnPresentation presentation = getColumnPresentation(); |
| if (presentation != null) { |
| String[] columns = fVisibleColumns.get(presentation.getId()); |
| if (columns == null) { |
| return presentation.getInitialColumns(); |
| } |
| return columns; |
| } |
| } |
| return null; |
| } |
| |
| /** |
| * Sets the id's of visible columns, or <code>null</code> to set default columns. |
| * Only affects the current column presentation. |
| * |
| * @param ids visible columns |
| */ |
| public void setVisibleColumns(String[] ids) { |
| if (ids != null && ids.length == 0) { |
| ids = null; |
| } |
| IColumnPresentation presentation = getColumnPresentation(); |
| if (presentation != null) { |
| fVisibleColumns.remove(presentation.getId()); |
| if (ids != null) { |
| // put back in table if not default |
| String[] columns = presentation.getInitialColumns(); |
| if (columns.length == ids.length) { |
| for (int i = 0; i < columns.length; i++) { |
| if (!ids[i].equals(columns[i])) { |
| fVisibleColumns.put(presentation.getId(), ids); |
| break; |
| } |
| } |
| } else { |
| fVisibleColumns.put(presentation.getId(), ids); |
| } |
| } |
| PresentationContext presentationContext = (PresentationContext) getPresentationContext(); |
| presentationContext.setColumns(getVisibleColumns()); |
| refreshColumns(); |
| } |
| } |
| |
| /** |
| * Returns the current column presentation for this viewer, or <code>null</code> |
| * if none. |
| * |
| * @return column presentation or <code>null</code> |
| */ |
| public IColumnPresentation getColumnPresentation() { |
| return fColumnPresentation; |
| } |
| |
| /** |
| * Save viewer state into the given memento. |
| * |
| * @param memento Memento to write state to. |
| */ |
| public void saveState(IMemento memento) { |
| if (!fShowColumns.isEmpty()) { |
| for (Entry<String, Boolean> entry : fShowColumns.entrySet()) { |
| IMemento sizes = memento.createChild(SHOW_COLUMNS, entry.getKey()); |
| sizes.putString(SHOW_COLUMNS, entry.getValue().toString()); |
| } |
| } |
| if (!fVisibleColumns.isEmpty()) { |
| for (Entry<String, String[]> entry : fVisibleColumns.entrySet()) { |
| IMemento visible = memento.createChild(VISIBLE_COLUMNS, entry.getKey()); |
| String[] columns = entry.getValue(); |
| visible.putInteger(SIZE, columns.length); |
| for (int i = 0; i < columns.length; i++) { |
| visible.putString(COLUMN+Integer.toString(i), columns[i]); |
| } |
| } |
| } |
| // save presentation context properties |
| IPresentationContext context = getPresentationContext(); |
| if (context instanceof PresentationContext) { |
| PresentationContext pc = (PresentationContext) context; |
| pc.saveProperites(memento); |
| |
| } |
| } |
| |
| /** |
| * Initializes viewer state from the memento |
| * |
| * @param memento Memento to read state from. |
| */ |
| public void initState(IMemento memento) { |
| IMemento[] mementos = memento.getChildren(SHOW_COLUMNS); |
| for (int i = 0; i < mementos.length; i++) { |
| IMemento child = mementos[i]; |
| String id = child.getID(); |
| Boolean bool = Boolean.valueOf(child.getString(SHOW_COLUMNS)); |
| if (!bool.booleanValue()) { |
| fShowColumns.put(id, bool); |
| } |
| } |
| mementos = memento.getChildren(VISIBLE_COLUMNS); |
| for (int i = 0; i < mementos.length; i++) { |
| IMemento child = mementos[i]; |
| String id = child.getID(); |
| Integer integer = child.getInteger(SIZE); |
| if (integer != null) { |
| int length = integer.intValue(); |
| String[] columns = new String[length]; |
| for (int j = 0; j < length; j++) { |
| columns[j] = child.getString(COLUMN+Integer.toString(j)); |
| } |
| fVisibleColumns.put(id, columns); |
| } |
| } |
| // restore presentation context properties |
| // save presentation context properties |
| IPresentationContext context = getPresentationContext(); |
| if (context instanceof PresentationContext) { |
| PresentationContext pc = (PresentationContext) context; |
| pc.initProperties(memento); |
| } |
| } |
| |
| @Override |
| public void addViewerUpdateListener(IViewerUpdateListener listener) { |
| getContentProvider().addViewerUpdateListener(listener); |
| } |
| |
| @Override |
| public void removeViewerUpdateListener(IViewerUpdateListener listener) { |
| ITreeModelContentProvider cp = getContentProvider(); |
| if (cp != null) { |
| cp.removeViewerUpdateListener(listener); |
| } |
| } |
| |
| @Override |
| public void addModelChangedListener(IModelChangedListener listener) { |
| getContentProvider().addModelChangedListener(listener); |
| } |
| |
| @Override |
| public void removeModelChangedListener(IModelChangedListener listener) { |
| ITreeModelContentProvider cp = getContentProvider(); |
| if (cp != null) { |
| cp.removeModelChangedListener(listener); |
| } |
| } |
| |
| @Override |
| public void addStateUpdateListener(IStateUpdateListener listener) { |
| getContentProvider().addStateUpdateListener(listener); |
| } |
| |
| @Override |
| public void removeStateUpdateListener(IStateUpdateListener listener) { |
| ITreeModelContentProvider cp = getContentProvider(); |
| if (cp != null) { |
| cp.removeStateUpdateListener(listener); |
| } |
| } |
| |
| @Override |
| public void addLabelUpdateListener(ILabelUpdateListener listener) { |
| getLabelProvider().addLabelUpdateListener(listener); |
| } |
| |
| @Override |
| public void removeLabelUpdateListener(ILabelUpdateListener listener) { |
| getLabelProvider().removeLabelUpdateListener(listener); |
| } |
| |
| /** |
| * Performs auto expand on an element at the specified path if the auto expand |
| * level dictates the element should be expanded. |
| * |
| * @param elementPath tree path to element to consider for expansion |
| */ |
| @Override |
| public void autoExpand(TreePath elementPath) { |
| int level = getAutoExpandLevel(); |
| if (level > 0 || level == org.eclipse.debug.internal.ui.viewers.model.provisional.ITreeModelViewer.ALL_LEVELS) { |
| if (level == org.eclipse.debug.internal.ui.viewers.model.provisional.ITreeModelViewer.ALL_LEVELS || level > elementPath.getSegmentCount()) { |
| expandToLevel(elementPath, 1); |
| } |
| } |
| } |
| |
| @Override |
| public int getChildCount(TreePath path) { |
| int childCount = -1; |
| VirtualItem[] items = findItems(path); |
| if (items.length > 0) { |
| childCount = items[0].getItemCount(); |
| // Mimic the jface viewer behavior which returns 1 for child count |
| // for an item that has children but is not yet expanded. |
| // Return 0, if we do not know if the item has children. |
| if (childCount == -1) { |
| childCount = items[0].hasItems() ? 1 : 0; |
| } |
| } |
| return childCount; |
| } |
| |
| @Override |
| public Object getChildElement(TreePath path, int index) { |
| VirtualItem[] items = findItems(path); |
| if (items.length > 0) { |
| if (index < items[0].getItemCount()) { |
| return items[0].getItem(new VirtualItem.Index(index)).getData(); |
| } |
| } |
| return null; |
| } |
| |
| @Override |
| public TreePath getTopElementPath() { |
| return null; |
| } |
| |
| @Override |
| public boolean saveElementState(TreePath path, ModelDelta delta, int flagsToSave) { |
| VirtualTree tree = getTree(); |
| VirtualItem[] selection = tree.getSelection(); |
| Set<VirtualItem> set = new HashSet<>(); |
| for (int i = 0; i < selection.length; i++) { |
| set.add(selection[i]); |
| } |
| |
| VirtualItem[] items = null; |
| VirtualItem parent = findItem(path); |
| |
| if (parent != null) { |
| delta.setChildCount(((TreeModelContentProvider)getContentProvider()).viewToModelCount(path, parent.getItemCount())); |
| if (parent.getExpanded()) { |
| if ((flagsToSave & IModelDelta.EXPAND) != 0) { |
| delta.setFlags(delta.getFlags() | IModelDelta.EXPAND); |
| } |
| } else if ((flagsToSave & IModelDelta.COLLAPSE) != 0 && parent.hasItems()){ |
| delta.setFlags(delta.getFlags() | IModelDelta.COLLAPSE); |
| } |
| |
| if (set.contains(parent) && (flagsToSave & IModelDelta.SELECT) != 0) { |
| delta.setFlags(delta.getFlags() | IModelDelta.SELECT); |
| } |
| |
| items = parent.getItems(); |
| for (int i = 0; i < items.length; i++) { |
| doSaveElementState(path, delta, items[i], set, flagsToSave); |
| } |
| return true; |
| } else { |
| return false; |
| } |
| } |
| |
| private void doSaveElementState(TreePath parentPath, ModelDelta delta, VirtualItem item, Collection<VirtualItem> set, int flagsToSave) { |
| Object element = item.getData(); |
| if (element != null) { |
| boolean expanded = item.getExpanded(); |
| boolean selected = set.contains(item); |
| int flags = IModelDelta.NO_CHANGE; |
| if (expanded && (flagsToSave & IModelDelta.EXPAND) != 0) { |
| flags = flags | IModelDelta.EXPAND; |
| } |
| if (!expanded && (flagsToSave & IModelDelta.COLLAPSE) != 0 && item.hasItems()){ |
| flags = flags | IModelDelta.COLLAPSE; |
| } |
| if (selected && (flagsToSave & IModelDelta.SELECT) != 0) { |
| flags = flags | IModelDelta.SELECT; |
| } |
| if (expanded || flags != IModelDelta.NO_CHANGE) { |
| int modelIndex = ((TreeModelContentProvider)getContentProvider()).viewToModelIndex(parentPath, item.getIndex().intValue()); |
| TreePath elementPath = parentPath.createChildPath(element); |
| int numChildren = ((TreeModelContentProvider)getContentProvider()).viewToModelCount(elementPath, item.getItemCount()); |
| ModelDelta childDelta = delta.addNode(element, modelIndex, flags, numChildren); |
| if (expanded) { |
| VirtualItem[] items = item.getItems(); |
| for (int i = 0; i < items.length; i++) { |
| doSaveElementState(elementPath, childDelta, items[i], set, flagsToSave); |
| } |
| } |
| } |
| } |
| } |
| |
| @Override |
| public void updateViewer(IModelDelta delta) { |
| getContentProvider().updateModel(delta, ITreeModelContentProvider.ALL_MODEL_DELTA_FLAGS); |
| } |
| |
| @Override |
| public ViewerLabel getElementLabel(TreePath path, String columnId) { |
| if (path.getSegmentCount() == 0) { |
| return null; |
| } |
| |
| int columnIdx = -1; |
| String[] visibleColumns = getVisibleColumns(); |
| if (columnId != null && visibleColumns != null) { |
| int i = 0; |
| for (i = 0; i < visibleColumns.length; i++) { |
| if (columnId.equals(getVisibleColumns()[i])) { |
| columnIdx = i; |
| break; |
| } |
| } |
| if (i == visibleColumns.length) { |
| return null; |
| } |
| } else { |
| columnIdx = 0; |
| } |
| VirtualItem item = findItem(path); |
| |
| if (item != null) { |
| ViewerLabel label = new ViewerLabel(getText(item, columnIdx), getImage(item, columnIdx)); |
| label.setFont(getFont(item, columnIdx)); |
| label.setBackground(getBackground(item, columnIdx)); |
| label.setForeground(getForeground(item, columnIdx)); |
| return label; |
| } |
| return null; |
| } |
| |
| @Override |
| public TreePath[] getElementPaths(Object element) { |
| VirtualItem[] items = findItems(element); |
| TreePath[] paths = new TreePath[items.length]; |
| for (int i = 0; i < items.length; i++) { |
| paths[i] = getTreePathFromItem(items[i]); |
| } |
| return paths; |
| } |
| |
| |
| public String getText(VirtualItem item, int columnIdx) { |
| String[] texts = (String[])item.getData(VirtualItem.LABEL_KEY); |
| if (texts != null && texts.length > columnIdx) { |
| return texts[columnIdx]; |
| } |
| return null; |
| } |
| |
| public Image getImage(VirtualItem item, int columnIdx) { |
| ImageDescriptor[] imageDescriptors = (ImageDescriptor[]) item.getData(VirtualItem.IMAGE_KEY); |
| if (imageDescriptors != null && imageDescriptors.length > columnIdx) { |
| return getLabelProvider().getImage(imageDescriptors[columnIdx]); |
| } |
| return null; |
| } |
| |
| public Font getFont(VirtualItem item, int columnIdx) { |
| FontData[] fontDatas = (FontData[]) item.getData(VirtualItem.FONT_KEY); |
| if (fontDatas != null) { |
| return getLabelProvider().getFont(fontDatas[columnIdx]); |
| } |
| return null; |
| } |
| |
| public Color getForeground(VirtualItem item, int columnIdx) { |
| RGB[] rgbs = (RGB[]) item.getData(VirtualItem.FOREGROUND_KEY); |
| if (rgbs != null) { |
| return getLabelProvider().getColor(rgbs[columnIdx]); |
| } |
| return null; |
| } |
| |
| public Color getBackground(VirtualItem item, int columnIdx) { |
| RGB[] rgbs = (RGB[]) item.getData(VirtualItem.BACKGROUND_KEY); |
| if (rgbs != null) { |
| return getLabelProvider().getColor(rgbs[columnIdx]); |
| } |
| return null; |
| } |
| |
| /* (non-Javadoc) |
| * @see org.eclipse.debug.internal.ui.viewers.model.ITreeModelContentProviderTarget#clearSelectionQuiet() |
| */ |
| @Override |
| public void clearSelectionQuiet() { |
| getTree().setSelection(EMPTY_ITEMS_ARRAY); |
| } |
| |
| @Override |
| public boolean getElementChecked(TreePath path) { |
| // Not supported |
| return false; |
| } |
| |
| @Override |
| public boolean getElementGrayed(TreePath path) { |
| // Not supported |
| return false; |
| } |
| |
| @Override |
| public void setElementChecked(TreePath path, boolean checked, boolean grayed) { |
| // Not supported |
| } |
| |
| @Override |
| public String toString() { |
| return getTree().toString(); |
| } |
| } |