org.neuro4j.studio.debug.ui.views.InternalTreeModelViewer.java Source code

Java tutorial

Introduction

Here is the source code for org.neuro4j.studio.debug.ui.views.InternalTreeModelViewer.java

Source

/*
 * Copyright (c) 2013-2014, Neuro4j.org
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.neuro4j.studio.debug.ui.views;

import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;

import org.eclipse.debug.internal.core.IInternalDebugCoreConstants;
import org.eclipse.debug.internal.ui.viewers.model.IInternalTreeModelViewer;
import org.eclipse.debug.internal.ui.viewers.model.ILabelUpdateListener;
import org.eclipse.debug.internal.ui.viewers.model.ITreeModelContentProvider;
import org.eclipse.debug.internal.ui.viewers.model.ITreeModelLabelProvider;
import org.eclipse.debug.internal.ui.viewers.model.ViewerAdapterService;
import org.eclipse.debug.internal.ui.viewers.model.provisional.ICheckUpdate;
import org.eclipse.debug.internal.ui.viewers.model.provisional.IColumnPresentation;
import org.eclipse.debug.internal.ui.viewers.model.provisional.IColumnPresentation2;
import org.eclipse.debug.internal.ui.viewers.model.provisional.IColumnPresentationFactory;
import org.eclipse.debug.internal.ui.viewers.model.provisional.IElementEditor;
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.ITreeModelViewer;
import org.eclipse.debug.internal.ui.viewers.model.provisional.IViewerUpdateListener;
import org.eclipse.debug.internal.ui.viewers.model.provisional.ModelDelta;
import org.eclipse.debug.internal.ui.viewers.model.provisional.PresentationContext;
import org.eclipse.jdt.internal.debug.ui.variables.JavaVariableColumnPresentation;
import org.eclipse.jface.resource.ImageDescriptor;
import org.eclipse.jface.viewers.CellEditor;
import org.eclipse.jface.viewers.IBasicPropertyConstants;
import org.eclipse.jface.viewers.ICellModifier;
import org.eclipse.jface.viewers.IContentProvider;
import org.eclipse.jface.viewers.ILazyTreePathContentProvider;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.jface.viewers.TreePath;
import org.eclipse.jface.viewers.TreeSelection;
import org.eclipse.jface.viewers.TreeViewer;
import org.eclipse.jface.viewers.ViewerLabel;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.ControlEvent;
import org.eclipse.swt.events.ControlListener;
import org.eclipse.swt.events.DisposeEvent;
import org.eclipse.swt.events.PaintEvent;
import org.eclipse.swt.events.PaintListener;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.events.TreeEvent;
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.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Event;
import org.eclipse.swt.widgets.Item;
import org.eclipse.swt.widgets.Listener;
import org.eclipse.swt.widgets.Tree;
import org.eclipse.swt.widgets.TreeColumn;
import org.eclipse.swt.widgets.TreeItem;
import org.eclipse.swt.widgets.Widget;
import org.eclipse.ui.IMemento;

/**
 * A tree viewer that displays a model.
 * 
 * @since 3.3
 */
public class InternalTreeModelViewer extends TreeViewer
        implements IInternalTreeModelViewer, org.eclipse.debug.internal.ui.viewers.model.ITreeModelViewer {
    static final TreePath EMPTY_TREE_PATH = new TreePath(new Object[0]);
    private IPresentationContext fContext;

    /**
     * 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 fVisibleColumns = new HashMap();

    /**
     * Map of column id's to persisted sizes
     */
    private Map fColumnSizes = new HashMap();

    /**
     * Map of column presentation id's to an array of integers representing the column order
     * for that presentation, or <code>null</code> if default.
     */
    private Map fColumnOrder = 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 fShowColumns = new HashMap();

    /**
     * Item's tree path cache
     */
    private static final String TREE_PATH_KEY = "TREE_PATH_KEY"; //$NON-NLS-1$

    /**
     * Memento type for column sizes. Sizes are keyed by column presentation id
     */
    private static final String COLUMN_SIZES = "COLUMN_SIZES"; //$NON-NLS-1$
    /**
     * Memento type for the column order for a presentation context.
     * A memento is created for each column presentation
     */
    private static final String COLUMN_ORDER = "COLUMN_ORDER"; //$NON-NLS-1$   
    /**
     * 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$       

    /**
     * True while performing an insert... we allow insert with filters
     */
    private boolean fInserting = false;

    /**
     * Whether to notify the content provider when an element is unmapped
     */
    private boolean fNotifyUnmap = true;

    /**
     * Persist column sizes when they change.
     * 
     * @since 3.2
     */
    class ColumnListener implements ControlListener {
        /*
         * (non-Javadoc)
         * 
         * @see org.eclipse.swt.events.ControlListener#controlMoved(org.eclipse.swt.events.ControlEvent)
         */
        public void controlMoved(ControlEvent e) {
            persistColumnOrder();
        }

        /*
         * (non-Javadoc)
         * 
         * @see org.eclipse.swt.events.ControlListener#controlResized(org.eclipse.swt.events.ControlEvent)
         */
        public void controlResized(ControlEvent e) {
            persistColumnSizes();
        }
    }

    private ColumnListener fListener = new ColumnListener();

    /**
     * Proxy to cell modifier/editor support
     */
    class CellModifierProxy implements ICellModifier {

        private ICellModifier fModifier;

        /*
         * (non-Javadoc)
         * 
         * @see org.eclipse.jface.viewers.ICellModifier#canModify(java.lang.Object, java.lang.String)
         */
        public boolean canModify(Object element, String property) {
            IElementEditor editor = ViewerAdapterService.getElementEditor(element);
            if (editor != null) {
                fModifier = editor.getCellModifier(getPresentationContext(), element);
                if (fModifier != null) {
                    if (fModifier.canModify(element, property)) {
                        // install cell editor
                        CellEditor cellEditor = editor.getCellEditor(getPresentationContext(), property, element,
                                (Composite) getControl());
                        if (cellEditor != null) {
                            disposeCellEditors();
                            CellEditor[] newEditors = new CellEditor[getVisibleColumns().length];
                            for (int i = 0; i < newEditors.length; i++) {
                                newEditors[i] = cellEditor;
                            }
                            setCellEditors(newEditors);
                            return true;
                        }
                    }
                }
            }
            return false;
        }

        /*
         * (non-Javadoc)
         * 
         * @see org.eclipse.jface.viewers.ICellModifier#getValue(java.lang.Object, java.lang.String)
         */
        public Object getValue(Object element, String property) {
            if (fModifier != null) {
                return fModifier.getValue(element, property);
            }
            return null;
        }

        /*
         * (non-Javadoc)
         * 
         * @see org.eclipse.jface.viewers.ICellModifier#modify(java.lang.Object, java.lang.String, java.lang.Object)
         */
        public void modify(Object element, String property, Object value) {
            if (fModifier != null) {
                if (element instanceof Item) {
                    element = ((Item) element).getData();
                }
                fModifier.modify(element, property, value);
            }
        }

        /**
         * Clears client's column editor and cell editors
         */
        protected void clear() {
            fModifier = null;
            disposeCellEditors();
            setCellEditors(null);
        }

        /**
         * Disposes current cell editors
         */
        protected void disposeCellEditors() {
            CellEditor[] cellEditors = getCellEditors();
            if (cellEditors != null) {
                for (int i = 0; i < cellEditors.length; i++) {
                    CellEditor editor = cellEditors[i];
                    if (editor != null) {
                        editor.dispose();
                    }
                }
            }
        }

    }

    private CellModifierProxy fCellModifier;

    /**
     * @param parent
     *        the parent composite
     * @param style
     *        the widget style bits
     * @param context
     *        the presentation context
     */
    public InternalTreeModelViewer(Composite parent, int style, IPresentationContext context) {
        super(parent, style);
        if ((style & SWT.VIRTUAL) == 0) {
            throw new IllegalArgumentException("style must include SWT.VIRTUAL"); //$NON-NLS-1$
        }
        setUseHashlookup(true);
        fCellModifier = new CellModifierProxy();
        fContext = context;
        setContentProvider(createContentProvider());
        setLabelProvider(createLabelProvider());

        // A pop-up viewer is transient and does not automatically expand
        // and select elements up when requested by the model
        if ((style & SWT.POP_UP) != 0) {
            ((ITreeModelContentProvider) getContentProvider())
                    .setModelDeltaMask(~ITreeModelContentProvider.CONTROL_MODEL_DELTA_FLAGS);
        }
        if ((style & SWT.CHECK) != 0) {
            context.setProperty(ICheckUpdate.PROP_CHECK, Boolean.TRUE);
        }
    }

    /**
     * @return content provider for this tree viewer
     */
    protected ITreeModelContentProvider createContentProvider() {
        return new TreeModelContentProvider();
    }

    /**
     * @return label provider for this tree viewer
     */
    protected ITreeModelLabelProvider createLabelProvider() {
        return new MyTreeModelLabelProvider(this);
    }

    /*
     * (non-Javadoc)
     * 
     * Workaround for bug 159461: when an item is cleared it's label is cleared. To avoid
     * flashing, restore its label to its previous value.
     * 
     * @see org.eclipse.jface.viewers.TreeViewer#hookControl(org.eclipse.swt.widgets.Control)
     */
    protected void hookControl(Control control) {
        Tree treeControl = (Tree) control;
        treeControl.addListener(SWT.SetData, new Listener() {
            public void handleEvent(Event event) {
                // to avoid flash, reset previous label data
                TreeItem item = (TreeItem) event.item;
                preserveItem(item);
            }
        });
        super.hookControl(control);
    }

    /**
     * @param item
     *        the item
     */
    private void preserveItem(TreeItem item) {
        Object[] labels = (Object[]) item.getData(PREV_LABEL_KEY);
        if (labels != null) {
            for (int i = 0; i < labels.length; i++) {
                if (labels[i] != null) {
                    item.setText(i, (String) labels[i]);
                }
            }
        }
        Object[] images = (Object[]) item.getData(PREV_IMAGE_KEY);
        if (images != null) {
            for (int i = 0; i < images.length; i++) {
                item.setImage(i, (Image) images[i]);
            }
        }
        Object[] fonts = (Object[]) item.getData(PREV_FONT_KEY);
        if (fonts != null) {
            for (int i = 0; i < fonts.length; i++) {
                item.setFont(i, (Font) fonts[i]);
            }
        }
        Object[] foregrounds = (Object[]) item.getData(PREV_FOREGROUND_KEY);
        if (foregrounds != null) {
            for (int i = 0; i < foregrounds.length; i++) {
                item.setForeground(i, (Color) foregrounds[i]);
            }
        }
        Object[] backgrounds = (Object[]) item.getData(PREV_BACKGROUND_KEY);
        if (backgrounds != null) {
            for (int i = 0; i < backgrounds.length; i++) {
                item.setBackground(i, (Color) backgrounds[i]);
            }
        }
        Boolean checked = (Boolean) item.getData(PREV_CHECKED_KEY);
        if (checked != null) {
            item.setChecked(checked.booleanValue());
        }
        Boolean grayed = (Boolean) item.getData(PREV_GRAYED_KEY);
        if (grayed != null) {
            item.setGrayed(grayed.booleanValue());
        }
    }

    /*
     * (non-Javadoc)
     * 
     * @see org.eclipse.jface.viewers.StructuredViewer#handleInvalidSelection
     * 
     * Override the default handler for invalid selection to allow model
     * selection policy to select the new selection.
     */
    protected 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)) {
                    setSelectionToWidget(selection, false);
                    newSelection = getSelection();
                } else {
                    break;
                }
            }
        }
        super.handleInvalidSelection(selection, newSelection);
    }

    /*
     * (non-Javadoc)
     * 
     * @see org.eclipse.jface.viewers.ContentViewer#handleDispose(org.eclipse.swt.events.DisposeEvent)
     */
    protected void handleDispose(DisposeEvent event) {
        if (fColumnPresentation != null) {
            fColumnPresentation.dispose();
        }
        fCellModifier.clear();

        super.handleDispose(event);
    }

    /**
     * Returns this viewer's presentation context.
     * 
     * @return presentation context
     */
    public IPresentationContext getPresentationContext() {
        return fContext;
    }

    protected void unmapElement(Object element, Widget widget) {
        if (fNotifyUnmap) {
            // TODO: should we update the filter with the "new non-identical element"?
            ((ITreeModelContentProvider) getContentProvider()).unmapPath((TreePath) widget.getData(TREE_PATH_KEY));
        }
        super.unmapElement(element, widget);
    }

    protected void associate(Object element, Item item) {
        // see AbstractTreeViewer.associate(...)
        Object data = item.getData();
        if (data != null && data != element && equals(data, element)) {
            // elements are equal but not identical
            // -> being removed from map, but should not change filters
            try {
                fNotifyUnmap = false;
                super.associate(element, item);
            } finally {
                fNotifyUnmap = true;
            }
        } else {
            super.associate(element, item);
        }
    }

    /*
     * (non-Javadoc)
     * 
     * We need tree paths when disposed/unmapped in any order so cache the tree path.
     * 
     * @see org.eclipse.jface.viewers.TreeViewer#mapElement(java.lang.Object, org.eclipse.swt.widgets.Widget)
     */
    protected void mapElement(Object element, Widget widget) {
        super.mapElement(element, widget);
        if (widget instanceof Item) {
            widget.setData(TREE_PATH_KEY, getTreePathFromItem((Item) widget));
        } else {
            widget.setData(TREE_PATH_KEY, EMPTY_TREE_PATH);
        }
    }

    /*
     * (non-Javadoc)
     * 
     * Override because we allow inserting with filters present.
     * 
     * @see org.eclipse.jface.viewers.AbstractTreeViewer#insert(java.lang.Object, java.lang.Object, int)
     */
    public void insert(Object parentElementOrTreePath, Object element, int position) {
        try {
            fInserting = true;
            super.insert(parentElementOrTreePath, element, position);
        } finally {
            fInserting = false;
        }
    }

    /*
     * (non-Javadoc)
     * 
     * Override because we allow inserting with filters present.
     * 
     * @see org.eclipse.jface.viewers.StructuredViewer#hasFilters()
     */
    protected boolean hasFilters() {
        if (fInserting) {
            return false;
        }
        return super.hasFilters();
    }

    protected void unmapAllElements() {
        // Do nothing when called from StructuredViewer.setInput(), to avoid
        // clearing elements before viewer state is saved.
        // Bug 326917
        if (getControl().isDisposed()) {
            unmapAllElements();
        }
    }

    /*
     * (non-Javadoc)
     * 
     * @see org.eclipse.jface.viewers.AbstractTreeViewer#inputChanged(java.lang.Object, java.lang.Object)
     */
    protected void inputChanged(Object input, Object oldInput) {
        fCellModifier.clear();
        // Clear items map now that ITreeModelContentProvider.inputChanged() was already called.
        // Bug 326917
        super.unmapAllElements();
        ((ITreeModelContentProvider) getContentProvider()).postInputChanged(this, oldInput, input);
        super.inputChanged(input, oldInput);

        resetColumns(input);
    }

    /**
     * Configures the columns for the given viewer input.
     * 
     * @param input
     *        the viewer input
     */
    protected 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);
            }

            fColumnPresentation = new JavaVariableColumnPresentation();

            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();
    }

    /**
     * Resets any persisted column size for the given columns
     * 
     * @param columnIds
     *        the identifiers of the columns to reset
     */
    public void resetColumnSizes(String[] columnIds) {
        for (int i = 0; i < columnIds.length; i++) {
            fColumnSizes.remove(columnIds[i]);
        }
    }

    /**
     * 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) {
            fColumnOrder.remove(presentation.getId());
            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();
        }
    }

    protected void internalRefresh(Object element, boolean updateLabels) {
        ITreeModelContentProvider contentProvider = (ITreeModelContentProvider) getContentProvider();

        if (element == null) {
            internalRefresh(getControl(), getRoot(), true, updateLabels);
            contentProvider.preserveState(TreePath.EMPTY);
        } else {
            Widget[] items = findItems(element);
            if (items.length != 0) {
                for (int i = 0; i < items.length; i++) {
                    if (items[i] instanceof TreeItem) {
                        contentProvider.preserveState(getTreePathFromItem((TreeItem) items[i]));
                    } else {
                        contentProvider.preserveState(TreePath.EMPTY);
                    }
                }
            }
        }
        super.internalRefresh(element, updateLabels);
    }

    /**
     * Refreshes the columns in the view, based on the viewer input.
     */
    protected void refreshColumns() {
        configureColumns();
        refresh();
    }

    /**
     * Returns whether columns are being displayed currently.
     * 
     * @return if columns are being shown
     */
    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 = (Boolean) fShowColumns.get(columnPresentationId);
        if (bool == null) {
            return true;
        }
        return bool.booleanValue();
    }

    /**
     * Creates new columns for the given presentation.
     * 
     * TODO: does this need to be asynchronous?
     * 
     * @param presentation
     *        the column presentation to build from
     */
    protected void buildColumns(IColumnPresentation presentation) {
        // dispose current columns, persisting their weights
        Tree tree = getTree();
        final TreeColumn[] columns = tree.getColumns();
        String[] visibleColumnIds = getVisibleColumns();
        // remove all listeners before disposing - see bug 223233
        for (int i = 0; i < columns.length; i++) {
            columns[i].removeControlListener(fListener);
        }
        for (int i = 0; i < columns.length; i++) {
            columns[i].dispose();
        }
        PresentationContext presentationContext = (PresentationContext) getPresentationContext();
        if (presentation != null) {
            for (int i = 0; i < visibleColumnIds.length; i++) {
                String id = visibleColumnIds[i];
                String header = presentation.getHeader(id);
                // TODO: allow client to specify style
                TreeColumn column = new TreeColumn(tree, SWT.LEFT, i);
                column.setMoveable(true);
                column.setText(header);
                column.setWidth(1);
                ImageDescriptor image = presentation.getImageDescriptor(id);
                if (image != null) {
                    column.setImage(((ITreeModelLabelProvider) getLabelProvider()).getImage(image));
                }
                column.setData(id);
            }
            int[] order = (int[]) fColumnOrder.get(presentation.getId());
            if (order != null) {
                tree.setColumnOrder(order);
            }
            tree.setHeaderVisible(true);
            tree.setLinesVisible(true);
            presentationContext.setColumns(visibleColumnIds);
            setColumnProperties(visibleColumnIds);
            setCellModifier(fCellModifier);
        } else {
            tree.setHeaderVisible(false);
            tree.setLinesVisible(false);
            presentationContext.setColumns(null);
            setCellModifier(null);
            setColumnProperties(null);
        }

        int treeWidgetWidth = tree.getSize().x;
        int avg = treeWidgetWidth;
        if (visibleColumnIds != null)
            avg /= visibleColumnIds.length;

        if (avg == 0) {
            tree.addPaintListener(new PaintListener() {
                public void paintControl(PaintEvent e) {
                    Tree tree2 = getTree();
                    String[] visibleColumns = getVisibleColumns();
                    if (visibleColumns != null) {
                        int treeWidgetWidth1 = tree2.getSize().x;
                        int avg1 = treeWidgetWidth1 / visibleColumns.length;
                        initColumns(avg1, treeWidgetWidth1, visibleColumns);
                    }
                    tree2.removePaintListener(this);
                }
            });
        } else {
            initColumns(avg, treeWidgetWidth, visibleColumnIds);
        }
    }

    private void initColumns(int widthHint, int treeWidgetWidth, String[] visibleColumnIds) {
        TreeColumn[] columns = getTree().getColumns();
        for (int i = 0; i < columns.length; i++) {
            TreeColumn treeColumn = columns[i];
            Object colData = treeColumn.getData();
            String columnId = colData instanceof String ? (String) colData : null;
            Integer width = (Integer) fColumnSizes.get(colData);
            if (width == null) {
                int ans = getInitialColumnWidth(columnId, treeWidgetWidth, visibleColumnIds);
                if (ans == -1) {
                    treeColumn.setWidth(widthHint);
                } else {
                    treeColumn.setWidth(ans);
                }
            } else {
                treeColumn.setWidth(width.intValue());
            }
            treeColumn.addControlListener(fListener);
        }
    }

    /**
     * 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;
    }

    /**
     * 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>
     */
    public String[] getVisibleColumns() {
        if (isShowColumns()) {
            IColumnPresentation presentation = getColumnPresentation();
            if (presentation != null) {
                String[] columns = (String[]) fVisibleColumns.get(presentation.getId());
                if (columns == null) {
                    return presentation.getInitialColumns();
                } else {
                    String[] available = presentation.getAvailableColumns();
                    for (int i = 0; i < columns.length; i++) {
                        boolean columnAvailable = false;
                        for (int j = 0; j < available.length; j++) {
                            if (columns[i].equals(available[j]))
                                columnAvailable = true;
                        }

                        if (!columnAvailable || presentation.getHeader(columns[i]) == null) {
                            // We found a column ID which is not in current list of available column IDs.
                            // Or the presentation cannot return a header title for the given column.
                            // Clear out saved column data for given column presentation.
                            fVisibleColumns.remove(presentation.getId());
                            fColumnOrder.remove(presentation.getId());
                            fColumnSizes.remove(presentation.getId());
                            return presentation.getInitialColumns();
                        }
                    }
                }
                return columns;
            }
        }
        return null;
    }

    /**
     * Returns initial column width of a given column, or -1
     * 
     * @param columnId
     *        column Id
     * @param treeWidgetWidth
     *        tree widget width
     * @param visibleColumnIds
     *        visible columns
     * 
     * @return column width
     */
    public int getInitialColumnWidth(String columnId, int treeWidgetWidth, String[] visibleColumnIds) {
        if (isShowColumns()) {
            IColumnPresentation presentation = getColumnPresentation();
            if (presentation instanceof IColumnPresentation2) {
                int ans = ((IColumnPresentation2) presentation).getInitialColumnWidth(columnId, treeWidgetWidth,
                        visibleColumnIds);
                return ans;
            }
        }
        return -1;
    }

    /**
     * Persists column sizes in cache
     */
    protected void persistColumnSizes() {
        Tree tree = getTree();
        TreeColumn[] columns = tree.getColumns();
        for (int i = 0; i < columns.length; i++) {
            TreeColumn treeColumn = columns[i];
            Object id = treeColumn.getData();
            fColumnSizes.put(id, new Integer(treeColumn.getWidth()));
        }
    }

    /**
     * Persists column ordering
     */
    protected void persistColumnOrder() {
        IColumnPresentation presentation = getColumnPresentation();
        if (presentation != null) {
            Tree tree = getTree();
            int[] order = tree.getColumnOrder();
            if (order.length > 0) {
                for (int i = 0; i < order.length; i++) {
                    if (i != order[i]) {
                        // non default order
                        fColumnOrder.put(presentation.getId(), order);
                        return;
                    }
                }
            }
            // default order
            fColumnOrder.remove(presentation.getId());
        }
    }

    /**
     * Save viewer state into the given memento.
     * 
     * @param memento
     *        the {@link IMemento} to save to
     */
    public void saveState(IMemento memento) {
        if (!fColumnSizes.isEmpty()) {
            Iterator iterator = fColumnSizes.entrySet().iterator();
            while (iterator.hasNext()) {
                Map.Entry entry = (Entry) iterator.next();
                IMemento sizes = memento.createChild(COLUMN_SIZES, (String) entry.getKey());
                sizes.putInteger(SIZE, ((Integer) entry.getValue()).intValue());
            }
        }
        if (!fShowColumns.isEmpty()) {
            Iterator iterator = fShowColumns.entrySet().iterator();
            while (iterator.hasNext()) {
                Map.Entry entry = (Entry) iterator.next();
                IMemento sizes = memento.createChild(SHOW_COLUMNS, (String) entry.getKey());
                sizes.putString(SHOW_COLUMNS, ((Boolean) entry.getValue()).toString());
            }
        }
        if (!fVisibleColumns.isEmpty()) {
            Iterator iterator = fVisibleColumns.entrySet().iterator();
            while (iterator.hasNext()) {
                Map.Entry entry = (Entry) iterator.next();
                String id = (String) entry.getKey();
                IMemento visible = memento.createChild(VISIBLE_COLUMNS, id);
                String[] columns = (String[]) entry.getValue();
                visible.putInteger(SIZE, columns.length);
                for (int i = 0; i < columns.length; i++) {
                    visible.putString(COLUMN + Integer.toString(i), columns[i]);
                }
            }
        }
        if (!fColumnOrder.isEmpty()) {
            Iterator iterator = fColumnOrder.entrySet().iterator();
            while (iterator.hasNext()) {
                Map.Entry entry = (Entry) iterator.next();
                String id = (String) entry.getKey();
                IMemento orderMemento = memento.createChild(COLUMN_ORDER, id);
                int[] order = (int[]) entry.getValue();
                orderMemento.putInteger(SIZE, order.length);
                for (int i = 0; i < order.length; i++) {
                    orderMemento.putInteger(COLUMN + Integer.toString(i), order[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
     *        the {@link IMemento} to read from
     */
    public void initState(IMemento memento) {
        IMemento[] mementos = memento.getChildren(COLUMN_SIZES);
        for (int i = 0; i < mementos.length; i++) {
            IMemento child = mementos[i];
            String id = child.getID();
            Integer size = child.getInteger(SIZE);
            if (size != null) {
                fColumnSizes.put(id, size);
            }
        }
        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);
            }
        }
        mementos = memento.getChildren(COLUMN_ORDER);
        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();
                int[] order = new int[length];
                for (int j = 0; j < length; j++) {
                    order[j] = child.getInteger(COLUMN + Integer.toString(j)).intValue();
                }
                fColumnOrder.put(id, order);
            }
        }
        // restore presentation context properties
        // save presentation context properties
        IPresentationContext context = getPresentationContext();
        if (context instanceof PresentationContext) {
            PresentationContext pc = (PresentationContext) context;
            pc.initProperties(memento);
        }
    }

    /**
     * Returns whether the candidate selection should override the current
     * selection.
     * 
     * @param current
     *        the current selection
     * @param candidate
     *        the candidate for the new selection
     * @return if the current selection should be replaced with the candidate selection
     */
    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());
    }

    /*
     * (non-Javadoc)
     * 
     * Consider selection policy
     * 
     * @see org.eclipse.jface.viewers.StructuredViewer#setSelection(org.eclipse.jface.viewers.ISelection, boolean)
     */
    public void setSelection(ISelection selection, boolean reveal) {
        if (!overrideSelection(getSelection(), selection)) {
            return;
        }
        super.setSelection(selection, reveal);
    }

    /*
     * (non-Javadoc)
     * 
     * @see
     * org.eclipse.debug.internal.ui.viewers.model.ITreeModelViewer#setSelection(org.eclipse.jface.viewers.ISelection,
     * boolean, boolean)
     */
    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)
     */
    public boolean trySelection(ISelection selection, boolean reveal, boolean force) {
        if (force || overrideSelection(getSelection(), selection)) {
            super.setSelection(selection, reveal);
            return true;
        }
        return false;
    }

    /**
     * Registers the specified listener for view update notifications.
     * 
     * @param listener
     *        listener
     */
    public void addViewerUpdateListener(IViewerUpdateListener listener) {
        ((ITreeModelContentProvider) getContentProvider()).addViewerUpdateListener(listener);
    }

    /**
     * Removes the specified listener from update notifications.
     * 
     * @param listener
     *        listener
     */
    public void removeViewerUpdateListener(IViewerUpdateListener listener) {
        ITreeModelContentProvider cp = (ITreeModelContentProvider) getContentProvider();
        if (cp != null) {
            cp.removeViewerUpdateListener(listener);
        }
    }

    /**
     * Registers the given listener for model delta notification.
     * 
     * @param listener
     *        model delta listener
     */
    public void addModelChangedListener(IModelChangedListener listener) {
        ((ITreeModelContentProvider) getContentProvider()).addModelChangedListener(listener);
    }

    /**
     * Unregisters the given listener from model delta notification.
     * 
     * @param listener
     *        model delta listener
     */
    public void removeModelChangedListener(IModelChangedListener listener) {
        ITreeModelContentProvider cp = (ITreeModelContentProvider) getContentProvider();
        if (cp != null) {
            cp.removeModelChangedListener(listener);
        }
    }

    public void addStateUpdateListener(IStateUpdateListener listener) {
        ((ITreeModelContentProvider) getContentProvider()).addStateUpdateListener(listener);
    }

    public void removeStateUpdateListener(IStateUpdateListener listener) {
        ITreeModelContentProvider cp = (ITreeModelContentProvider) getContentProvider();
        if (cp != null) {
            cp.removeStateUpdateListener(listener);
        }
    }

    /*
     * (non-Javadoc) Method declared in AbstractTreeViewer.
     */
    protected void doUpdateItem(final Item item, Object element) {
        if (!(item instanceof TreeItem)) {
            return;
        }
        TreeItem treeItem = (TreeItem) item;
        if (treeItem.isDisposed()) {
            unmapElement(element, treeItem);
            return;
        }

        if (!((ITreeModelLabelProvider) getLabelProvider()).update(getTreePathFromItem(item))) {
            if (element instanceof String) {
                item.setData(PREV_LABEL_KEY, new String[] { (String) element });
            }
        }

        // As it is possible for user code to run the event
        // loop check here.
        if (item.isDisposed()) {
            unmapElement(element, item);
        }
    }

    public void addLabelUpdateListener(ILabelUpdateListener listener) {
        ((ITreeModelLabelProvider) getLabelProvider()).addLabelUpdateListener(listener);
    }

    public void removeLabelUpdateListener(ILabelUpdateListener listener) {
        if (!getControl().isDisposed()) {
            ((ITreeModelLabelProvider) getLabelProvider()).removeLabelUpdateListener(listener);
        }
    }

    /**
     * Returns the item for the element at the given tree path or <code>null</code> if none.
     * 
     * @param path
     *        tree path
     * @return item or <code>null</code>
     */
    public Widget findItem(TreePath path) {
        if (path.getSegmentCount() == 0) {
            return getTree();
        }
        Widget[] items = super.findItems(path.getLastSegment());
        if (items.length == 1) {
            return items[0];
        }
        for (int i = 0; i < items.length; i++) {
            if (getTreePathFromItem((Item) items[i]).equals(path)) {
                return items[i];
            }
        }
        return null;
    }

    public Item[] getChildren(Widget widget) {
        return super.getChildren(widget);
    }

    /**
     * Returns the tree path for the given item.
     * 
     * @param item
     *        the item to compute the {@link TreePath} for
     * @return {@link TreePath}
     */
    protected TreePath getTreePathFromItem(Item item) {
        return super.getTreePathFromItem(item);
    }

    // **************************************************************************
    // These methods were copied from TreeViewer as a workaround for bug 183463:
    // Expanded nodes in tree viewer flash on refresh

    /*
     * (non-Javadoc)
     * 
     * workaround for bug 183463
     * 
     * @see org.eclipse.jface.viewers.AbstractTreeViewer#internalRefreshStruct(org.eclipse.swt.widgets.Widget,
     * java.lang.Object, boolean)
     */
    protected void internalRefreshStruct(Widget widget, Object element, boolean updateLabels) {
        // clear all starting with the given widget
        if (widget instanceof Tree) {
            ((Tree) widget).clearAll(true);
        } else if (widget instanceof TreeItem) {
            ((TreeItem) widget).clearAll(true);
        }
        int index = 0;
        Widget parent = null;
        if (widget instanceof TreeItem) {
            TreeItem treeItem = (TreeItem) widget;
            parent = treeItem.getParentItem();
            if (parent == null) {
                parent = treeItem.getParent();
            }
            if (parent instanceof Tree) {
                index = ((Tree) parent).indexOf(treeItem);
            } else {
                index = ((TreeItem) parent).indexOf(treeItem);
            }
        }
        virtualRefreshExpandedItems(parent, widget, element, index);
    }

    /**
     * Traverses the visible (expanded) part of the tree and updates child
     * counts.
     * <p>
     * workaround for bug 183463
     * </p>
     * 
     * @param parent
     *        the parent of the widget, or <code>null</code> if the widget is the tree
     * @param widget
     *        the parent widget
     * @param element
     *        the underlying object
     * @param index
     *        the index of the widget in the children array of its parent, or 0 if the widget is the tree
     */
    private void virtualRefreshExpandedItems(Widget parent, Widget widget, Object element, int index) {
        if (widget instanceof Tree) {
            if (element == null) {
                ((Tree) widget).setItemCount(0);
                return;
            }
            virtualLazyUpdateChildCount(widget, getChildren(widget).length);
        } else if (((TreeItem) widget).getExpanded()) {
            // prevent SetData callback
            preserveItem((TreeItem) widget);
            //((TreeItem)widget).setText(" "); //$NON-NLS-1$
            virtualLazyUpdateWidget(parent, index);
        } else {
            return;
        }
        Item[] items = getChildren(widget);
        for (int i = 0; i < items.length; i++) {
            Item item = items[i];
            Object data = item.getData();
            virtualRefreshExpandedItems(widget, item, data, i);
        }
    }

    /**
     * workaround for bug 183463
     * 
     * Update the child count
     * 
     * @param widget
     *        the widget
     * @param currentChildCount
     *        the current child count
     */
    private void virtualLazyUpdateChildCount(Widget widget, int currentChildCount) {
        TreePath treePath;
        if (widget instanceof Item) {
            treePath = getTreePathFromItem((Item) widget);
        } else {
            treePath = TreePath.EMPTY;
        }
        ((ILazyTreePathContentProvider) getContentProvider()).updateChildCount(treePath, currentChildCount);
    }

    /**
     * Update the widget at index.
     * <p>
     * workaround for bug 183463
     * </p>
     * 
     * @param widget
     *        the widget
     * @param index
     *        the index to update
     */
    private void virtualLazyUpdateWidget(Widget widget, int index) {
        TreePath treePath;
        if (widget instanceof Item) {
            if (widget.getData() == null) {
                // we need to materialize the parent first
                // see bug 167668
                // however, that would be too risky
                // see bug 182782 and bug 182598
                // so we just ignore this call altogether
                // and don't do this: virtualMaterializeItem((TreeItem) widget);
                return;
            }
            treePath = getTreePathFromItem((Item) widget);
        } else {
            treePath = TreePath.EMPTY;
        }
        ((ILazyTreePathContentProvider) getContentProvider()).updateElement(treePath, index);
    }

    // **************************************************************************
    // Another couple of methods copied from TreeViewer to workaround the UI bug 266189.
    //
    protected void createChildren(Widget widget) {
        Object element = widget.getData();
        if (element == null && widget instanceof TreeItem) {
            // parent has not been materialized
            virtualMaterializeItem((TreeItem) widget);
            // try getting the element now that updateElement was called
            element = widget.getData();
        }
        if (element == null) {
            // give up because the parent is still not materialized
            return;
        }
        Item[] children = getChildren(widget);
        if (children.length == 1 && children[0].getData() == null) {
            // found a dummy node
            virtualLazyUpdateChildCount(widget, children.length);
            children = getChildren(widget);
        }
        // DO NOT touch all children
        return;
    }

    private void virtualMaterializeItem(TreeItem treeItem) {
        if (treeItem.getData() != null) {
            // already materialized
            return;
        }

        int index;
        Widget parent = treeItem.getParentItem();
        if (parent == null) {
            parent = treeItem.getParent();
        }
        Object parentElement = parent.getData();
        if (parentElement != null) {
            if (parent instanceof Tree) {
                index = ((Tree) parent).indexOf(treeItem);
            } else {
                index = ((TreeItem) parent).indexOf(treeItem);
            }
            virtualLazyUpdateWidget(parent, index);
        }
    }

    /**
     * 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
     */
    public void autoExpand(TreePath elementPath) {
        int level = getAutoExpandLevel();
        if (level > 0 || level == ITreeModelViewer.ALL_LEVELS) {
            if (level == ITreeModelViewer.ALL_LEVELS || level >= elementPath.getSegmentCount()) {
                expandToLevel(elementPath, 1);
            }
        }
    }

    public int findElementIndex(TreePath parentPath, Object element) {
        Widget parentItem = findItem(parentPath);
        if (parentItem != null) {
            Item[] children = getChildren(parentItem);
            for (int i = 0; i < children.length; i++) {
                Item item = children[i];
                Object data = item.getData();
                if ((element != null && element.equals(data)) || (element == null && data == null)) {
                    return i;
                }
            }
        }
        return -1;
    }

    public boolean getElementChildrenRealized(TreePath parentPath) {
        Widget parentItem = findItem(parentPath);
        if (parentItem != null) {
            Item[] children = getChildren(parentItem);
            for (int i = 0; i < children.length; i++) {
                if (children[i].getData() == null) {
                    return false;
                }
            }
        }
        return true;
    }

    public Display getDisplay() {
        Control control = getControl();
        if (control != null) {
            return control.getDisplay();
        }
        return null;
    }

    protected static final String[] STATE_PROPERTIES = new String[] { IBasicPropertyConstants.P_TEXT,
            IBasicPropertyConstants.P_IMAGE };

    public void update(Object element) {
        update(element, STATE_PROPERTIES);
    }

    /**
     * Label data cache keys
     * TODO: workaround for bug 159461
     */
    static String PREV_LABEL_KEY = "PREV_LABEL_KEY"; //$NON-NLS-1$
    static String PREV_IMAGE_KEY = "PREV_IMAGE_KEY"; //$NON-NLS-1$
    static String PREV_FONT_KEY = "PREV_FONT_KEY"; //$NON-NLS-1$
    static String PREV_FOREGROUND_KEY = "PREV_FOREGROUND_KEY"; //$NON-NLS-1$
    static String PREV_BACKGROUND_KEY = "PREV_BACKGROUND_KEY"; //$NON-NLS-1$
    static String PREV_CHECKED_KEY = "PREV_CHECKED_KEY"; //$NON-NLS-1$
    static String PREV_GRAYED_KEY = "PREV_GRAYED_KEY"; //$NON-NLS-1$

    public void setElementData(TreePath path, int numColumns, String[] labels, ImageDescriptor[] imageDescriptors,
            FontData[] fontDatas, RGB[] _foregrounds, RGB[] _backgrounds) {
        Widget widget = findItem(path);
        String[] columnIds = getVisibleColumns();

        if (widget != null && widget instanceof TreeItem && !widget.isDisposed()) {
            TreeItem item = (TreeItem) widget;
            /*
             * Object data = item.getData();
             * int itemCount = item.getItemCount();
             * item.clearAll(false);
             * item.setData(data);
             * item.setItemCount(itemCount);
             */

            for (int i = 0; i < numColumns; i++) {
                // text might be null if the launch has been terminated
                item.setText(i, (labels[i] == null ? IInternalDebugCoreConstants.EMPTY_STRING : labels[i]));
            }
            item.setData(PREV_LABEL_KEY, labels);

            if (imageDescriptors == null) {
                for (int i = 0; i < numColumns; i++) {
                    item.setImage(i, null);
                }
                item.setData(PREV_IMAGE_KEY, null);
            } else {
                Image[] images = new Image[imageDescriptors.length];
                for (int i = 0; i < imageDescriptors.length; i++) {
                    images[i] = ((ITreeModelLabelProvider) getLabelProvider()).getImage(imageDescriptors[i]);
                }
                if (columnIds == null) {
                    item.setImage(images[0]);
                } else {
                    item.setImage(images);
                }
                item.setData(PREV_IMAGE_KEY, images);
            }

            if (_foregrounds == null) {
                for (int i = 0; i < numColumns; i++) {
                    item.setForeground(i, null);
                }
                item.setData(PREV_FOREGROUND_KEY, null);
            } else {
                Color[] foregrounds = new Color[_foregrounds.length];
                for (int i = 0; i < foregrounds.length; i++) {
                    foregrounds[i] = ((ITreeModelLabelProvider) getLabelProvider()).getColor(_foregrounds[i]);
                }
                if (columnIds == null) {
                    item.setForeground(0, foregrounds[0]);
                } else {
                    for (int i = 0; i < foregrounds.length; i++) {
                        item.setForeground(i, foregrounds[i]);
                    }
                }
                item.setData(PREV_FOREGROUND_KEY, foregrounds);
            }

            if (_backgrounds == null) {
                for (int i = 0; i < numColumns; i++) {
                    item.setBackground(i, null);
                }
                item.setData(PREV_BACKGROUND_KEY, null);
            } else {
                Color[] backgrounds = new Color[_backgrounds.length];
                for (int i = 0; i < backgrounds.length; i++) {
                    backgrounds[i] = ((ITreeModelLabelProvider) getLabelProvider()).getColor(_backgrounds[i]);
                }
                if (columnIds == null) {
                    item.setBackground(0, backgrounds[0]);
                } else {
                    for (int i = 0; i < backgrounds.length; i++) {
                        item.setBackground(i, backgrounds[i]);
                    }
                }
                item.setData(PREV_BACKGROUND_KEY, backgrounds);
            }

            if (fontDatas == null) {
                for (int i = 0; i < numColumns; i++) {
                    item.setFont(i, null);
                }
                item.setData(PREV_FONT_KEY, null);
            } else {
                Font[] fonts = new Font[fontDatas.length];
                for (int i = 0; i < fontDatas.length; i++) {
                    fonts[i] = ((ITreeModelLabelProvider) getLabelProvider()).getFont(fontDatas[i]);
                }
                if (columnIds == null) {
                    item.setFont(0, fonts[0]);
                } else {
                    for (int i = 0; i < fonts.length; i++) {
                        item.setFont(i, fonts[i]);
                    }
                }
                item.setData(PREV_FONT_KEY, fonts);
            }
        }
    }

    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;
        }
        TreeItem item = (TreeItem) findItem(path);

        if (item != null) {
            ViewerLabel label = new ViewerLabel(item.getText(columnIdx), item.getImage(columnIdx));
            label.setFont(item.getFont(columnIdx));
            label.setBackground(item.getBackground(columnIdx));
            label.setForeground(item.getForeground(columnIdx));
            return label;
        }
        return null;
    }

    public void reveal(TreePath path, int index) {
        Widget item = findItem(path);
        TreeItem[] children = null;
        if (item instanceof TreeItem) {
            children = ((TreeItem) item).getItems();
        } else if (item instanceof Tree) {
            children = ((Tree) item).getItems();
        }
        if (children != null && index < children.length) {
            getTree().setTopItem(children[index]);
        }
    }

    public int getChildCount(TreePath path) {
        if (path.getSegmentCount() == 0) {
            return ((Tree) getControl()).getItemCount();
        } else {
            Widget[] items = internalFindItems(path);
            if (items.length > 0) {
                if (items[0] instanceof TreeItem) {
                    return ((TreeItem) items[0]).getItemCount();
                }
            }
        }
        return -1;
    }

    public Object getChildElement(TreePath path, int index) {
        TreeItem childItem = null;
        if (path.getSegmentCount() == 0) {
            Tree tree = (Tree) getControl();
            try {
                childItem = tree.getItem(index);
            } catch (IllegalArgumentException e) {
            }
        } else {
            try {
                Widget[] items = internalFindItems(path);
                if (items.length > 0) {
                    if (items[0] instanceof TreeItem) {
                        childItem = ((TreeItem) items[0]).getItem(index);
                    }
                }
            } catch (IllegalArgumentException e) {
            }
        }
        if (childItem != null) {
            return childItem.getData();
        }
        return null;
    }

    public TreePath getTopElementPath() {
        TreeItem topItem = ((Tree) getControl()).getTopItem();
        if (topItem != null && topItem.getData() != null) {
            return getTreePathFromItem(topItem);
        }
        return null;
    }

    public boolean saveElementState(TreePath path, ModelDelta delta, int flagsToSave) {
        Tree tree = (Tree) getControl();
        TreeItem[] selection = tree.getSelection();
        Set set = new HashSet();
        for (int i = 0; i < selection.length; i++) {
            set.add(selection[i]);
        }

        TreeItem[] items = null;
        Widget w = internalGetWidgetToSelect(path);
        if (w instanceof Tree) {
            delta.setChildCount(
                    ((ITreeModelContentProvider) getContentProvider()).viewToModelCount(path, tree.getItemCount()));
            if ((flagsToSave & IModelDelta.EXPAND) != 0) {
                delta.setFlags(delta.getFlags() | IModelDelta.EXPAND);
            }
            items = tree.getItems();
        } else if (w instanceof TreeItem) {
            TreeItem item = (TreeItem) w;
            if (item.getExpanded()) {
                int itemCount = item.getData() != null ? item.getItemCount() : -1;
                delta.setChildCount(
                        ((ITreeModelContentProvider) getContentProvider()).viewToModelCount(path, itemCount));
                if ((flagsToSave & IModelDelta.EXPAND) != 0) {
                    delta.setFlags(delta.getFlags() | IModelDelta.EXPAND);
                }
            } else if ((flagsToSave & IModelDelta.COLLAPSE) != 0) {
                delta.setFlags(delta.getFlags() | IModelDelta.COLLAPSE);
            }

            if (set.contains(item) && (flagsToSave & IModelDelta.SELECT) != 0) {
                delta.setFlags(delta.getFlags() | IModelDelta.SELECT);
            }
            items = ((TreeItem) w).getItems();
        }
        if (items != null && items.length != 0) {
            for (int i = 0; i < items.length; i++) {
                doSaveElementState(path, delta, items[i], set, i, flagsToSave);
            }
            return true;
        } else {
            return false;
        }
    }

    private void doSaveElementState(TreePath parentPath, ModelDelta delta, TreeItem item, Collection set, int index,
            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) {
                flags = flags | IModelDelta.COLLAPSE;
            }
            if (selected && (flagsToSave & IModelDelta.SELECT) != 0) {
                flags = flags | IModelDelta.SELECT;
            }
            if (expanded || flags != IModelDelta.NO_CHANGE) {
                int modelIndex = ((ITreeModelContentProvider) getContentProvider()).viewToModelIndex(parentPath,
                        index);
                TreePath elementPath = parentPath.createChildPath(element);
                ModelDelta childDelta = delta.addNode(element, modelIndex, flags, -1);
                if (expanded) {
                    // Only get the item count if the item is expanded. Getting
                    // item count triggers an update of the element (bug 335734).
                    int itemCount = item.getItemCount();
                    int numChildren = ((ITreeModelContentProvider) getContentProvider())
                            .viewToModelCount(elementPath, itemCount);
                    childDelta.setChildCount(numChildren);
                    TreeItem[] items = item.getItems();
                    for (int i = 0; i < items.length; i++) {
                        doSaveElementState(elementPath, childDelta, items[i], set, i, flagsToSave);
                    }
                }
            }
        }
    }

    public void updateViewer(IModelDelta delta) {
        ((ITreeModelContentProvider) getContentProvider()).updateModel(delta,
                ITreeModelContentProvider.UPDATE_MODEL_DELTA_FLAGS);
    }

    /*
     * (non-Javadoc)
     * 
     * @see
     * org.eclipse.debug.internal.ui.viewers.model.ITreeModelCheckProvider#setElementChecked(org.eclipse.jface.viewers
     * .TreePath, boolean, boolean)
     */
    public void setElementChecked(TreePath path, boolean checked, boolean grayed) {
        Widget widget = findItem(path);

        if (widget != null && widget instanceof TreeItem && !widget.isDisposed()) {
            TreeItem item = (TreeItem) widget;

            item.setChecked(checked);
            item.setGrayed(grayed);

            item.setData(PREV_CHECKED_KEY, checked ? Boolean.TRUE : Boolean.FALSE);
            item.setData(PREV_GRAYED_KEY, grayed ? Boolean.TRUE : Boolean.FALSE);
        }
    }

    public boolean getElementChecked(TreePath path) {
        Widget widget = findItem(path);

        if (widget != null && widget instanceof TreeItem && !widget.isDisposed()) {
            TreeItem item = (TreeItem) widget;

            return item.getChecked();
        }
        return false;
    }

    /**
     * Retrieves the element's check box grayed state.
     * 
     * @param path
     *        the path of the element to set grayed
     * @return grayed
     */
    public boolean getElementGrayed(TreePath path) {
        Widget widget = findItem(path);

        if (widget != null && widget instanceof TreeItem && !widget.isDisposed()) {
            TreeItem item = (TreeItem) widget;

            return item.getGrayed();
        }
        return false;
    }

    public boolean getHasChildren(Object elementOrTreePath) {
        if (elementOrTreePath instanceof TreePath && ((TreePath) elementOrTreePath).getSegmentCount() == 0) {
            return getTree().getItemCount() > 0;
        }

        Widget[] items = internalFindItems(elementOrTreePath);
        if (items != null && items.length > 0) {
            if (items[0] instanceof TreeItem) {
                return ((TreeItem) items[0]).getItemCount() > 0;
            } else {
                return ((Tree) items[0]).getItemCount() > 0;
            }
        }

        return false;
    }

    public TreePath[] getElementPaths(Object element) {
        Widget[] items = internalFindItems(element);
        TreePath[] paths = new TreePath[items.length];
        for (int i = 0; i < items.length; i++) {
            if (items[i] instanceof Tree) {
                paths[i] = TreePath.EMPTY;
            } else {
                paths[i] = getTreePathFromItem((Item) items[i]);
            }
        }
        return paths;
    }

    /*
     * (non-Javadoc)
     * 
     * @see org.eclipse.jface.viewers.StructuredViewer#handleSelect(org.eclipse.swt.events.SelectionEvent)
     */
    protected void handleSelect(SelectionEvent event) {
        super.handleSelect(event);

        TreeItem item = (TreeItem) event.item;
        if (item != null) { // item can be null when de-selected (bug 296703)
            Object element = item.getData();
            IContentProvider contentProvider = getContentProvider();
            if (element != null && contentProvider instanceof TreeModelContentProvider) {
                TreePath path = getTreePathFromItem(item);

                if (event.detail == SWT.CHECK) {
                    boolean checked = item.getChecked();
                    boolean accepted = ((ITreeModelContentProvider) contentProvider).setChecked(path, checked);

                    // if the listen rejects the change or there is not ICheckboxModelProxy, than revert the check state
                    if (!accepted) {
                        item.setChecked(!checked);
                    } else {
                        item.setData(PREV_CHECKED_KEY, new Boolean(checked));
                    }
                } else {
                    ((TreeModelContentProvider) contentProvider).cancelRestore(path,
                            IModelDelta.SELECT | IModelDelta.REVEAL);
                }
            }
        }
    }

    protected void handleTreeExpand(TreeEvent event) {
        super.handleTreeExpand(event);
        IContentProvider contentProvider = getContentProvider();
        if (contentProvider instanceof TreeModelContentProvider && event.item.getData() != null) {
            TreePath path = getTreePathFromItem((TreeItem) event.item);
            ((TreeModelContentProvider) contentProvider).cancelRestore(path, IModelDelta.COLLAPSE);
        }
    }

    protected void handleTreeCollapse(TreeEvent event) {
        super.handleTreeCollapse(event);
        IContentProvider contentProvider = getContentProvider();
        if (contentProvider instanceof TreeModelContentProvider && event.item.getData() != null) {
            TreePath path = getTreePathFromItem((TreeItem) event.item);
            ((TreeModelContentProvider) contentProvider).cancelRestore(path, IModelDelta.EXPAND);
        }
    }

    /*
     * (non-Javadoc)
     * 
     * @see org.eclipse.debug.internal.ui.viewers.model.ITreeModelContentProviderTarget#clearSelectionQuiet()
     */
    public void clearSelectionQuiet() {
        getTree().setSelection(new TreeItem[0]);
    }
}