org.eclipse.wst.common.snippets.internal.util.AccessibleTableViewer.java Source code

Java tutorial

Introduction

Here is the source code for org.eclipse.wst.common.snippets.internal.util.AccessibleTableViewer.java

Source

/*******************************************************************************
 * Copyright (c) 2004, 2005 IBM Corporation and others.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *     IBM Corporation - initial API and implementation
 *******************************************************************************/
package org.eclipse.wst.common.snippets.internal.util;

import java.util.ArrayList;
import java.util.List;

import org.eclipse.jface.util.Assert;
import org.eclipse.jface.viewers.CellEditor;
import org.eclipse.jface.viewers.IBaseLabelProvider;
import org.eclipse.jface.viewers.ICellModifier;
import org.eclipse.jface.viewers.ILabelProvider;
import org.eclipse.jface.viewers.ITableLabelProvider;
import org.eclipse.jface.viewers.StructuredSelection;
import org.eclipse.jface.viewers.StructuredViewer;
import org.eclipse.jface.viewers.ViewerSorter;
import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.TableEditor;
import org.eclipse.swt.events.KeyAdapter;
import org.eclipse.swt.events.KeyEvent;
import org.eclipse.swt.events.MouseAdapter;
import org.eclipse.swt.events.MouseEvent;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.events.SelectionListener;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Item;
import org.eclipse.swt.widgets.Table;
import org.eclipse.swt.widgets.TableItem;
import org.eclipse.swt.widgets.Widget;

/**
 * AccessibleTableViewer 
 * 
 * A concrete viewer based on a SWT <code>Table</code> control.
 * <p>
 * This class is not intended to be subclassed outside the viewer framework.
 * It is designed to be instantiated with a pre-existing SWT table control and
 * configured with a domain-specific content provider, table label provider,
 * element filter (optional), and element sorter (optional).
 * </p>
 * <p>
 * Label providers for table viewers must implement either the
 * <code>ITableLabelProvider</code> or the <code>ILabelProvider</code>
 * interface (see <code>TableViewer.setLabelProvider</code> for more
 * details).
 * </p>
 */
public class AccessibleTableViewer extends StructuredViewer {

    /**
     * This viewer's table control.
     */
    protected Table fTable;

    /**
     * This viewer's table editor.
     */
    protected TableEditor fTableEditor;

    /**
     * Internal table viewer implementation.
     */
    protected TableViewerImpl fTableViewerImpl;

    /**
     * Creates a table viewer on a newly-created table control under the given
     * parent. The table control is created using the SWT style bits
     * <code>MULTI, H_SCROLL, V_SCROLL,</code> and <code>BORDER</code>.
     * The viewer has no input, no content provider, a default label provider,
     * no sorter, and no filters. The table has no columns.
     * 
     * @param parent
     *            the parent control
     */
    public AccessibleTableViewer(Composite parent) {
        this(parent, SWT.MULTI | SWT.H_SCROLL | SWT.V_SCROLL | SWT.BORDER);
    }

    /**
     * Creates a table viewer on a newly-created table control under the given
     * parent. The table control is created using the given style bits. The
     * viewer has no input, no content provider, a default label provider, no
     * sorter, and no filters. The table has no columns.
     * 
     * @param parent
     *            the parent control
     * @param style
     *            SWT style bits
     */
    public AccessibleTableViewer(Composite parent, int style) {
        this(new Table(parent, style));
    }

    /**
     * Creates a table viewer on the given table control. The viewer has no
     * input, no content provider, a default label provider, no sorter, and no
     * filters.
     * 
     * @param table
     *            the table control
     */
    public AccessibleTableViewer(Table table) {
        fTable = table;
        addKeyListener();
        hookControl(table);
        fTableEditor = new TableEditor(table);
        initTableViewerImpl();
    }

    /**
     * Adds the given element to this table viewer. If this viewer does not
     * have a sorter, the element is added at the end; otherwise the element
     * is inserted at the appropriate position.
     * <p>
     * This method should be called (by the content provider) when a single
     * element has been added to the model, in order to cause the viewer to
     * accurately reflect the model. This method only affects the viewer, not
     * the model. Note that there is another method for efficiently processing
     * the simultaneous addition of multiple elements.
     * </p>
     * 
     * @param element
     *            the element to add
     */
    public void add(Object element) {
        add(new Object[] { element });
    }

    /**
     * Adds the given elements to this table viewer. If this viewer does not
     * have a sorter, the elements are added at the end in the order given;
     * otherwise the elements are inserted at appropriate positions.
     * <p>
     * This method should be called (by the content provider) when elements
     * have been added to the model, in order to cause the viewer to
     * accurately reflect the model. This method only affects the viewer, not
     * the model.
     * </p>
     * 
     * @param elements
     *            the elements to add
     */
    public void add(Object[] elements) {
        Object[] filtered = filter(elements);
        for (int i = 0; i < filtered.length; i++) {
            Object element = filtered[i];
            int index = indexForElement(element);
            updateItem(new TableItem(getTable(), SWT.NONE, index), element);
        }
    }

    private void addKeyListener() {
        if (fTable != null && !fTable.isDisposed()) {
            fTable.addKeyListener(new KeyAdapter() {
                public void keyPressed(KeyEvent e) {
                    if (e.character == '\r' || e.character == ' ') {
                        TableItem[] selectedItems = fTable.getSelection();
                        if (selectedItems != null && selectedItems.length == 1) {
                            fTableViewerImpl.setTableItem(selectedItems[0]);
                            fTableViewerImpl.activateFirstCellEditor();
                        }
                    }
                }
            });
        }
    }

    /**
     * Cancels a currently active cell editor. All changes already done in the
     * cell editor are lost.
     */
    public void cancelEditing() {
        fTableViewerImpl.cancelEditing();
    }

    /*
     * (non-Javadoc) Method declared on StructuredViewer.
     */
    protected Widget doFindInputItem(Object element) {
        if (element.equals(getRoot()))
            return getTable();
        return null;
    }

    /*
     * (non-Javadoc) Method declared on StructuredViewer.
     */
    protected Widget doFindItem(Object element) {
        TableItem[] children = fTable.getItems();
        for (int i = 0; i < children.length; i++) {
            TableItem item = children[i];
            Object data = item.getData();
            if (data != null && data.equals(element))
                return item;
        }

        return null;
    }

    /*
     * (non-Javadoc) Method declared on StructuredViewer.
     */
    protected void doUpdateItem(Widget widget, Object element, boolean fullMap) {
        if (widget instanceof TableItem) {
            TableItem item = (TableItem) widget;

            // remember element we are showing
            if (fullMap) {
                associate(element, item);
            } else {
                item.setData(element);
                mapElement(element, item);
            }

            IBaseLabelProvider prov = getLabelProvider();
            ITableLabelProvider tprov = null;
            ILabelProvider lprov = null;
            if (prov instanceof ITableLabelProvider) {
                tprov = (ITableLabelProvider) prov;
            } else {
                lprov = (ILabelProvider) prov;
            }
            int columnCount = fTable.getColumnCount();
            // Also enter loop if no columns added. See 1G9WWGZ: JFUIF:WINNT -
            // TableViewer with 0 columns does not work
            for (int column = 0; column < columnCount || column == 0; column++) {
                // Similar code in TableTreeViewer.doUpdateItem()
                TableItem ti = item;
                String text = ""; //$NON-NLS-1$
                Image image = null;
                if (tprov != null) {
                    text = tprov.getColumnText(element, column);
                    image = tprov.getColumnImage(element, column);
                } else {
                    if (column == 0) {
                        text = lprov.getText(element);
                        image = lprov.getImage(element);
                    }
                }
                ti.setText(column, text);
                if (ti.getImage(column) != image) {
                    ti.setImage(column, image);
                }
            }
        }
    }

    /**
     * Starts editing the given element.
     * 
     * @param element
     *            the element
     * @param column
     *            the column number
     */
    public void editElement(Object element, int column) {
        fTableViewerImpl.editElement(element, column);
    }

    /**
     * Deactivates the currently active cell editor. All changes already done
     * in the cell editor are saved.
     */
    public void finishEditing() {
        fTableViewerImpl.applyEditorValue();
    }

    /**
     * Returns the cell editors of this table viewer.
     * 
     * @return the list of cell editors
     */
    public CellEditor[] getCellEditors() {
        return fTableViewerImpl.getCellEditors();
    }

    /**
     * Returns the cell modifier of this table viewer.
     * 
     * @return the cell modifier
     */
    public ICellModifier getCellModifier() {
        return fTableViewerImpl.getCellModifier();
    }

    /**
     * Returns the column properties of this table viewer. The properties must
     * correspond with the columns of the table control. They are used to
     * identify the column in a cell modifier.
     * 
     * @return the list of column properties
     */
    public Object[] getColumnProperties() {
        return fTableViewerImpl.getColumnProperties();
    }

    /*
     * (non-Javadoc) Method declared on Viewer.
     */
    public Control getControl() {
        return fTable;
    }

    /**
     * Returns the element with the given index from this table viewer.
     * Returns <code>null</code> if the index is out of range.
     * <p>
     * This method is internal to the framework.
     * </p>
     * 
     * @param index
     *            the zero-based index
     * @return the element at the given index, or <code>null</code> if the
     *         index is out of range
     */
    public Object getElementAt(int index) {
        if (index >= 0 && index < fTable.getItemCount()) {
            TableItem i = fTable.getItem(index);
            if (i != null)
                return i.getData();
        }
        return null;
    }

    /**
     * The table viewer implementation of this <code>Viewer</code> framework
     * method returns the label provider, which in the case of table viewers
     * will be an instance of either <code>ITableLabelProvider</code> or
     * <code>ILabelProvider</code>. If it is an
     * <code>ITableLabelProvider</code>, then it provides a separate label
     * text and image for each column. If it is an <code>ILabelProvider</code>,
     * then it provides only the label text and image for the first column,
     * and any remaining columns are blank.
     */
    public IBaseLabelProvider getLabelProvider() {
        return super.getLabelProvider();
    }

    /*
     * (non-Javadoc) Method declared on StructuredViewer.
     */
    protected List getSelectionFromWidget() {
        Widget[] items = fTable.getSelection();
        ArrayList list = new ArrayList(items.length);
        for (int i = 0; i < items.length; i++) {
            Widget item = items[i];
            Object e = item.getData();
            if (e != null)
                list.add(e);
        }
        return list;
    }

    /**
     * Returns this table viewer's table control.
     * 
     * @return the table control
     */
    public Table getTable() {
        return fTable;
    }

    /*
     * (non-Javadoc) Method declared on StructuredViewer.
     */
    protected void hookControl(Control control) {
        super.hookControl(control);
        Table tableControl = (Table) control;
        tableControl.addSelectionListener(new SelectionListener() {

            public void widgetDefaultSelected(SelectionEvent e) {
                handleDoubleSelect(e);
            }

            public void widgetSelected(SelectionEvent e) {
                handleSelect(e);
            }
        });
        tableControl.addMouseListener(new MouseAdapter() {

            public void mouseDoubleClick(MouseEvent e) {
                fTableViewerImpl.handleMouseDoubleClick(e);
            }

            public void mouseDown(MouseEvent e) {
                fTableViewerImpl.handleMouseDown(e);
            }
        });
    }

    /*
     * Returns the index where the item should be inserted.
     */
    protected int indexForElement(Object element) {
        ViewerSorter sorter = getSorter();
        if (sorter == null)
            return fTable.getItemCount();
        int count = fTable.getItemCount();
        int min = 0, max = count - 1;
        while (min <= max) {
            int mid = (min + max) / 2;
            Object data = fTable.getItem(mid).getData();
            int compare = sorter.compare(this, data, element);
            if (compare == 0) {
                // find first item > element
                while (compare == 0) {
                    ++mid;
                    if (mid >= count) {
                        break;
                    }
                    data = fTable.getItem(mid).getData();
                    compare = sorter.compare(this, data, element);
                }
                return mid;
            }
            if (compare < 0)
                min = mid + 1;
            else
                max = mid - 1;
        }
        return min;
    }

    /**
     * Initializes the table viewer implementation.
     */
    protected void initTableViewerImpl() {
        fTableViewerImpl = new TableViewerImpl(fTable) {
            Rectangle getBounds(Item item, int columnNumber) {
                return ((TableItem) item).getBounds(columnNumber);
            }

            int getColumnCount() {
                return getTable().getColumnCount();
            }

            Item[] getSelection() {
                return getTable().getSelection();
            }

            void setEditor(Control w, Item item, int columnNumber) {
                fTableEditor.setEditor(w, (TableItem) item, columnNumber);
            }

            void setLayoutData(CellEditor.LayoutData layoutData) {
                fTableEditor.grabHorizontal = layoutData.grabHorizontal;
                fTableEditor.horizontalAlignment = layoutData.horizontalAlignment;
                fTableEditor.minimumWidth = layoutData.minimumWidth;
            }

            void setSelection(StructuredSelection selection, boolean b) {
                AccessibleTableViewer.this.setSelection(selection, b);
            }

            void showSelection() {
                getTable().showSelection();
            }
        };
    }

    /*
     * (non-Javadoc) Method declared on Viewer.
     */
    protected void inputChanged(Object input, Object oldInput) {
        getControl().setRedraw(false);
        try {
            // refresh() attempts to preserve selection, which we want here
            refresh();
        } finally {
            getControl().setRedraw(true);
        }
    }

    /**
     * Inserts the given element into this table viewer at the given position.
     * If this viewer has a sorter, the position is ignored and the element is
     * inserted at the correct position in the sort order.
     * <p>
     * This method should be called (by the content provider) when elements
     * have been added to the model, in order to cause the viewer to
     * accurately reflect the model. This method only affects the viewer, not
     * the model.
     * </p>
     * 
     * @param element
     *            the element
     * @param position
     *            a 0-based position relative to the model, or -1 to indicate
     *            the last position
     */
    public void insert(Object element, int position) {
        fTableViewerImpl.applyEditorValue();
        if (getSorter() != null || hasFilters()) {
            add(element);
            return;
        }
        if (position == -1)
            position = fTable.getItemCount();
        updateItem(new TableItem(fTable, SWT.NONE, position), element);
    }

    /*
     * (non-Javadoc) Method declared on StructuredViewer.
     */
    protected void internalRefresh(Object element) {
        internalRefresh(element, true);
    }

    /*
     * (non-Javadoc) Method declared on StructuredViewer.
     */
    protected void internalRefresh(Object element, boolean updateLabels) {
        fTableViewerImpl.applyEditorValue();
        if (element == null || element.equals(getRoot())) {
            // the parent

            // in the code below, it is important to do all disassociates
            // before any associates, since a later disassociate can undo an
            // earlier associate
            // e.g. if (a, b) is replaced by (b, a), the disassociate of b to
            // item 1 could undo
            // the associate of b to item 0.

            Object[] children = getSortedChildren(getRoot());
            TableItem[] items = fTable.getItems();
            int min = Math.min(children.length, items.length);
            for (int i = 0; i < min; ++i) {
                if (children[i] != null && items[i] != null) {
                    // if the element is unchanged, update its label if
                    // appropriate
                    if (children[i].equals(items[i].getData())) {
                        if (updateLabels) {
                            updateItem(items[i], children[i]);
                        }
                    } else {
                        // updateItem does an associate(...), which can mess
                        // up
                        // the associations if the order of elements has
                        // changed.
                        // E.g. (a, b) -> (b, a) first replaces a->0 with
                        // b->0, then replaces b->1 with a->1, but this
                        // actually removes b->0.
                        // So, if the object associated with this item has
                        // changed,
                        // just disassociate it for now, and update it below.
                        disassociate(items[i]);
                    }
                }
            }

            // dispose of all items beyond the end of the current elements
            if (min < items.length) {
                for (int i = items.length; --i >= min;) {
                    disassociate(items[i]);
                }
                fTable.remove(min, items.length - 1);
            }

            // Workaround for 1GDGN4Q: ITPUI:WIN2000 - TableViewer icons get
            // scrunched
            if (fTable.getItemCount() == 0) {
                fTable.removeAll();
            }

            // Update items which were removed above
            for (int i = 0; i < min; ++i) {
                if (items[i].getData() == null) {
                    updateItem(items[i], children[i]);
                }
            }

            // add any remaining elements
            for (int i = min; i < children.length; ++i) {
                updateItem(new TableItem(fTable, SWT.NONE, i), children[i]);
            }
        } else {
            Widget w = findItem(element);
            if (w != null) {
                updateItem(w, element);
            }
        }
    }

    /**
     * Removes the given elements from this table viewer.
     * 
     * @param elements
     *            the elements to remove
     */
    private void internalRemove(final Object[] elements) {
        Object input = getInput();
        for (int i = 0; i < elements.length; ++i) {
            if (elements[i].equals(input)) {
                setInput(null);
                return;
            }
        }
        // use remove(int[]) rather than repeated TableItem.dispose() calls
        // to allow SWT to optimize multiple removals
        int[] indices = new int[elements.length];
        int count = 0;
        for (int i = 0; i < elements.length; ++i) {
            Widget w = findItem(elements[i]);
            if (w instanceof TableItem) {
                TableItem item = (TableItem) w;
                disassociate(item);
                indices[count++] = fTable.indexOf(item);
            }
        }
        if (count < indices.length) {
            System.arraycopy(indices, 0, indices = new int[count], 0, count);
        }
        fTable.remove(indices);

        // Workaround for 1GDGN4Q: ITPUI:WIN2000 - TableViewer icons get
        // scrunched
        if (fTable.getItemCount() == 0) {
            fTable.removeAll();
        }
    }

    /**
     * Returns whether there is an active cell editor.
     * 
     * @return <code>true</code> if there is an active cell editor, and
     *         <code>false</code> otherwise
     */
    public boolean isCellEditorActive() {
        return fTableViewerImpl.isCellEditorActive();
    }

    /**
     * Removes the given element from this table viewer. The selection is
     * updated if necessary.
     * <p>
     * This method should be called (by the content provider) when a single
     * element has been removed from the model, in order to cause the viewer
     * to accurately reflect the model. This method only affects the viewer,
     * not the model. Note that there is another method for efficiently
     * processing the simultaneous removal of multiple elements.
     * </p>
     * 
     * @param element
     *            the element
     */
    public void remove(Object element) {
        remove(new Object[] { element });
    }

    /**
     * Removes the given elements from this table viewer. The selection is
     * updated if required.
     * <p>
     * This method should be called (by the content provider) when elements
     * have been removed from the model, in order to cause the viewer to
     * accurately reflect the model. This method only affects the viewer, not
     * the model.
     * </p>
     * 
     * @param elements
     *            the elements to remove
     */
    public void remove(final Object[] elements) {
        preservingSelection(new Runnable() {
            public void run() {
                internalRemove(elements);
            }
        });
    }

    /*
     * Non-Javadoc. Method defined on StructuredViewer.
     */
    public void reveal(Object element) {
        Widget w = findItem(element);
        if (w instanceof TableItem)
            getTable().showItem((TableItem) w);
    }

    /**
     * Sets the cell editors of this table viewer.
     * 
     * @param editors
     *            the list of cell editors
     */
    public void setCellEditors(CellEditor[] editors) {
        fTableViewerImpl.setCellEditors(editors);
    }

    /**
     * Sets the cell modifier of this table viewer.
     * 
     * @param modifier
     *            the cell modifier
     */
    public void setCellModifier(ICellModifier modifier) {
        fTableViewerImpl.setCellModifier(modifier);
    }

    /**
     * Sets the column properties of this table viewer. The properties must
     * correspond with the columns of the table control. They are used to
     * identify the column in a cell modifier.
     * 
     * @param columnProperties
     *            the list of column properties
     */
    public void setColumnProperties(String[] columnProperties) {
        fTableViewerImpl.setColumnProperties(columnProperties);
    }

    /**
     * The table viewer implementation of this <code>Viewer</code> framework
     * method ensures that the given label provider is an instance of either
     * <code>ITableLabelProvider</code> or <code>ILabelProvider</code>.
     * If it is an <code>ITableLabelProvider</code>, then it provides a
     * separate label text and image for each column. If it is an
     * <code>ILabelProvider</code>, then it provides only the label text
     * and image for the first column, and any remaining columns are blank.
     */
    public void setLabelProvider(IBaseLabelProvider labelProvider) {
        Assert.isTrue(labelProvider instanceof ITableLabelProvider || labelProvider instanceof ILabelProvider);
        super.setLabelProvider(labelProvider);
    }

    /*
     * (non-Javadoc) Method declared on StructuredViewer.
     */
    protected void setSelectionToWidget(List list, boolean reveal) {
        if (list == null) {
            fTable.deselectAll();
            return;
        }
        int size = list.size();
        TableItem[] items = new TableItem[size];
        TableItem firstItem = null;
        int count = 0;
        for (int i = 0; i < size; ++i) {
            Object o = list.get(i);
            Widget w = findItem(o);
            if (w instanceof TableItem) {
                TableItem item = (TableItem) w;
                items[count++] = item;
                if (firstItem == null)
                    firstItem = item;
            }
        }
        if (count < size) {
            System.arraycopy(items, 0, items = new TableItem[count], 0, count);
        }
        fTable.setSelection(items);

        if (reveal && firstItem != null) {
            fTable.showItem(firstItem);
        }
    }

    public void setSingleClickCellSelect(boolean b) {
        fTableViewerImpl.setSingleClickCellSelect(b);
    }
}