TableTree.java Source code

Java tutorial

Introduction

Here is the source code for TableTree.java

Source

/*
    
 * (c) Copyright IBM Corp. 2000, 2001.
    
 * All Rights Reserved
    
 */

import org.eclipse.swt.SWT;
import org.eclipse.swt.SWTError;
import org.eclipse.swt.events.SelectionListener;
import org.eclipse.swt.events.TreeListener;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.graphics.Font;
import org.eclipse.swt.graphics.GC;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.graphics.ImageData;
import org.eclipse.swt.graphics.PaletteData;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.graphics.RGB;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.swt.widgets.Composite;
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.Menu;
import org.eclipse.swt.widgets.Table;
import org.eclipse.swt.widgets.TableItem;
import org.eclipse.swt.widgets.TypedListener;

/**
 * 
 * A TableTree is a selectable user interface object
 * 
 * that displays a hierarchy of items, and issues
 * 
 * notification when an item is selected.
 * 
 * A TableTree may be single or multi select.
 * 
 * <p>
 * 
 * The item children that may be added to instances of this class
 * 
 * must be of type <code>TableTreeItem</code>.
 * 
 * </p>
 * <p>
 * 
 * Note that although this class is a subclass of <code>Composite</code>,
 * 
 * it does not make sense to add <code>Control</code> children to it,
 * 
 * or set a layout on it.
 * 
 * </p>
 * <p>
 * 
 * <dl>
 * 
 * <dt><b>Styles: </b>
 * <dd>SINGLE, MULTI, CHECK, FULL_SELECTION
 * 
 * <dt><b>Events: </b>
 * <dd>Selection, DefaultSelection, Collapse, Expand
 * 
 * </dl>
 *  
 */

public class TableTree extends Composite {

    Table table;

    TableTreeItem[] items = EMPTY_ITEMS;

    Image plusImage, minusImage, sizeImage;

    /*
     * 
     * TableTreeItems are not treated as children but rather as items.
     * 
     * When the TableTree is disposed, all children are disposed because
     * 
     * TableTree inherits this behaviour from Composite. The items
     * 
     * must be disposed separately. Because TableTree is not part of
     * 
     * the org.eclipse.swt.widgets package, the method releaseWidget can
     * 
     * not be overriden (this is how items are disposed of in Table and Tree).
     * 
     * Instead, the items are disposed of in response to the dispose event on
     * the
     * 
     * TableTree. The "inDispose" flag is used to distinguish between disposing
     * 
     * one TableTreeItem (e.g. when removing an entry from the TableTree) and
     * 
     * disposing the entire TableTree.
     *  
     */

    boolean inDispose = false;

    static final TableTreeItem[] EMPTY_ITEMS = new TableTreeItem[0];

    static final String[] EMPTY_TEXTS = new String[0];

    static final Image[] EMPTY_IMAGES = new Image[0];

    /**
     * 
     * Creates a new instance of the widget.
     * 
     * 
     * 
     * @param parent
     *            a composite widget
     * 
     * @param style
     *            the bitwise OR'ing of widget styles
     *  
     */

    public TableTree(Composite parent, int style) {

        super(parent, SWT.NONE);

        table = new Table(this, style);

        setBackground(table.getBackground());

        setForeground(table.getForeground());

        setFont(table.getFont());

        table.addListener(SWT.MouseDown, new Listener() {

            public void handleEvent(Event e) {

                onMouseDown(e);

            }

        });

        table.addListener(SWT.Selection, new Listener() {

            public void handleEvent(Event e) {

                onSelection(e);

            }

        });

        table.addListener(SWT.DefaultSelection, new Listener() {

            public void handleEvent(Event e) {

                onSelection(e);

            }

        });

        addListener(SWT.Dispose, new Listener() {

            public void handleEvent(Event e) {

                onDispose();

            }

        });

        addListener(SWT.Resize, new Listener() {

            public void handleEvent(Event e) {

                onResize();

            }

        });

        addListener(SWT.FocusIn, new Listener() {

            public void handleEvent(Event e) {

                onFocusIn();

            }

        });

    }

    int addItem(TableTreeItem item, int index) {

        if (index < 0 || index > items.length)
            throw new SWTError(SWT.ERROR_INVALID_ARGUMENT);

        TableTreeItem[] newItems = new TableTreeItem[items.length + 1];

        System.arraycopy(items, 0, newItems, 0, index);

        newItems[index] = item;

        System.arraycopy(items, index, newItems, index + 1, items.length - index);

        items = newItems;

        /* Return the index in the table where this table should be inserted */

        if (index == items.length - 1)

            return table.getItemCount();

        else

            return table.indexOf(items[index + 1].tableItem);

    }

    /**
     * 
     * Adds the listener to receive selection events.
     * 
     * <p>
     * 
     * 
     * 
     * @param listener
     *            the selection listener
     * 
     * 
     * 
     * @exception SWTError
     *                <ul>
     * 
     * <li>ERROR_THREAD_INVALID_ACCESS when called from the wrong thread
     * 
     * <li>ERROR_WIDGET_DISPOSED when the widget has been disposed
     * 
     * <li>ERROR_NULL_ARGUMENT when listener is null
     * 
     * </ul>
     *  
     */

    public void addSelectionListener(SelectionListener listener) {

        if (listener == null)
            throw new SWTError(SWT.ERROR_NULL_ARGUMENT);

        TypedListener typedListener = new TypedListener(listener);

        addListener(SWT.Selection, typedListener);

        addListener(SWT.DefaultSelection, typedListener);

    }

    /**
     * 
     * Adds the listener to receive tree events.
     * 
     * <p>
     * 
     * 
     * 
     * @param listener
     *            the tree listener
     * 
     * 
     * 
     * @exception SWTError
     *                <ul>
     * 
     * <li>ERROR_THREAD_INVALID_ACCESS when called from the wrong thread
     * 
     * <li>ERROR_WIDGET_DISPOSED when the widget has been disposed
     * 
     * <li>ERROR_NULL_ARGUMENT when listener is null
     * 
     * </ul>
     *  
     */

    public void addTreeListener(TreeListener listener) {

        if (listener == null)
            throw new SWTError(SWT.ERROR_NULL_ARGUMENT);

        TypedListener typedListener = new TypedListener(listener);

        addListener(SWT.Expand, typedListener);

        addListener(SWT.Collapse, typedListener);

    }

    /**
     * 
     * Computes the preferred size of the widget.
     * 
     * <p>
     * 
     * Calculate the preferred size of the widget based
     * 
     * on the current contents. The hint arguments allow
     * 
     * a specific client area width and/or height to be
     * 
     * requested. The hints may be honored depending on
     * 
     * the platform and the layout.
     * 
     * 
     * 
     * @param wHint
     *            the width hint (can be SWT.DEFAULT)
     * 
     * @param hHint
     *            the height hint (can be SWT.DEFAULT)
     * 
     * @return a point containing the preferred size of the widget including
     *         trim
     * 
     * 
     * 
     * @exception SWTError
     *                <ul>
     * 
     * <li>ERROR_THREAD_INVALID_ACCESS when called from the wrong thread</li>
     * 
     * <li>ERROR_WIDGET_DISPOSED when the widget has been disposed</li>
     * 
     * </ul>
     *  
     */

    public Point computeSize(int wHint, int hHint) {

        return table.computeSize(wHint, hHint, true);

    }

    /**
     * 
     * Computes the widget trim.
     * 
     * <p>
     * 
     * Trim is widget specific and may include scroll
     * 
     * bars and menu bar in addition to other trimmings
     * 
     * that are outside of the widget's client area.
     * 
     * 
     * 
     * @param x
     *            the x location of the client area
     * 
     * @param y
     *            the y location of the client area
     * 
     * @param width
     *            the width of the client area
     * 
     * @param height
     *            the height of the client area
     * 
     * @return a rectangle containing the trim of the widget.
     * 
     * 
     * 
     * @exception SWTError
     *                <ul>
     * 
     * <li>ERROR_THREAD_INVALID_ACCESS when called from the wrong thread</li>
     * 
     * <li>ERROR_WIDGET_DISPOSED when the widget has been disposed</li>
     * 
     * </ul>
     *  
     */

    public Rectangle computeTrim(int x, int y, int width, int height) {

        return table.computeTrim(x, y, width, height);

    }

    /**
     * 
     * Deselects all items.
     * 
     * <p>
     * 
     * If an item is selected, it is deselected.
     * 
     * If an item is not selected, it remains unselected.
     * 
     * 
     * 
     * @exception SWTError
     *                <ul>
     * 
     * <li>ERROR_THREAD_INVALID_ACCESS when called from the wrong thread
     * 
     * <li>ERROR_WIDGET_DISPOSED when the widget has been disposed
     * 
     * </ul>
     *  
     */

    public void deselectAll() {

        table.deselectAll();

    }

    /* Expand upward from the specified leaf item. */

    void expandItem(TableTreeItem item) {

        if (item == null || item.getExpanded())
            return;

        expandItem(item.parentItem);

        item.setExpanded(true);

        Event event = new Event();

        event.item = item;

        notifyListeners(SWT.Expand, event);

    }

    /**
     * 
     * Gets the number of items.
     * 
     * <p>
     * 
     * @return the number of items in the widget
     *  
     */

    public int getItemCount() {

        return items.length;

    }

    /**
     * 
     * Gets the height of one item.
     * 
     * <p>
     * 
     * This operation will fail if the height of
     * 
     * one item could not be queried from the OS.
     * 
     * 
     * 
     * @return the height of one item in the widget
     * 
     * 
     * 
     * @exception SWTError
     *                <ul>
     * 
     * <li>ERROR_THREAD_INVALID_ACCESS when called from the wrong thread
     * 
     * <li>ERROR_WIDGET_DISPOSED when the widget has been disposed
     * 
     * <li>ERROR_CANNOT_GET_ITEM_HEIGHT when the operation fails
     * 
     * </ul>
     *  
     */

    public int getItemHeight() {

        return table.getItemHeight();

    }

    /**
     * 
     * Gets the items.
     * 
     * <p>
     * 
     * @return the items in the widget
     * 
     * 
     *  
     */

    public TableTreeItem[] getItems() {

        TableTreeItem[] newItems = new TableTreeItem[items.length];

        System.arraycopy(items, 0, newItems, 0, items.length);

        return newItems;

    }

    /**
     * 
     * Gets the selected items.
     * 
     * <p>
     * 
     * This operation will fail if the selected
     * 
     * items cannot be queried from the OS.
     * 
     * 
     * 
     * @return the selected items in the widget
     * 
     * 
     * 
     * @exception SWTError
     *                <ul>
     * 
     * <li>ERROR_THREAD_INVALID_ACCESS when called from the wrong thread</li>
     * 
     * <li>ERROR_WIDGET_DISPOSED when the widget has been disposed</li>
     * 
     * <li>ERROR_CANNOT_GET_SELECTION when the operation fails</li>
     * 
     * </ul>
     *  
     */

    public TableTreeItem[] getSelection() {

        TableItem[] selection = table.getSelection();

        TableTreeItem[] result = new TableTreeItem[selection.length];

        for (int i = 0; i < selection.length; i++) {

            result[i] = (TableTreeItem) selection[i].getData();

        }

        return result;

    }

    /**
     * 
     * Gets the number of selected items.
     * 
     * <p>
     * 
     * This operation will fail if the number of selected
     * 
     * items cannot be queried from the OS.
     * 
     * 
     * 
     * @return the number of selected items in the widget
     * 
     * 
     * 
     * @exception SWTError
     *                <ul>
     * 
     * <li>ERROR_THREAD_INVALID_ACCESS when called from the wrong thread</li>
     * 
     * <li>ERROR_WIDGET_DISPOSED when the widget has been disposed</li>
     * 
     * <li>ERROR_CANNOT_GET_COUNT when the operation fails</li>
     * 
     * </ul>
     *  
     */

    public int getSelectionCount() {

        return table.getSelectionCount();

    }

    /**
     * 
     * Returns the underlying Table control.
     * 
     * 
     * 
     * @return the underlying Table control
     *  
     */

    public Table getTable() {

        return table;

    }

    void createImages() {

        int itemHeight = sizeImage.getBounds().height;

        // Calculate border around image.

        // At least 9 pixels are needed to draw the image

        // Leave at least a 6 pixel border.

        int indent = Math.min(6, (itemHeight - 9) / 2);

        indent = Math.max(0, indent);

        int size = Math.max(10, itemHeight - 2 * indent);

        size = ((size + 1) / 2) * 2; // size must be an even number

        int midpoint = indent + size / 2;

        Color foreground = getForeground();

        Color plusMinus = getDisplay().getSystemColor(SWT.COLOR_WIDGET_NORMAL_SHADOW);

        Color background = getBackground();

        /* Plus image */

        PaletteData palette = new PaletteData(
                new RGB[] { foreground.getRGB(), background.getRGB(), plusMinus.getRGB() });

        ImageData imageData = new ImageData(itemHeight, itemHeight, 4, palette);

        imageData.transparentPixel = 1;

        plusImage = new Image(getDisplay(), imageData);

        GC gc = new GC(plusImage);

        gc.setBackground(background);

        gc.fillRectangle(0, 0, itemHeight, itemHeight);

        gc.setForeground(plusMinus);

        gc.drawRectangle(indent, indent, size, size);

        gc.setForeground(foreground);

        gc.drawLine(midpoint, indent + 2, midpoint, indent + size - 2);

        gc.drawLine(indent + 2, midpoint, indent + size - 2, midpoint);

        gc.dispose();

        /* Minus image */

        palette = new PaletteData(new RGB[] { foreground.getRGB(), background.getRGB(), plusMinus.getRGB() });

        imageData = new ImageData(itemHeight, itemHeight, 4, palette);

        imageData.transparentPixel = 1;

        minusImage = new Image(getDisplay(), imageData);

        gc = new GC(minusImage);

        gc.setBackground(background);

        gc.fillRectangle(0, 0, itemHeight, itemHeight);

        gc.setForeground(plusMinus);

        gc.drawRectangle(indent, indent, size, size);

        gc.setForeground(foreground);

        gc.drawLine(indent + 2, midpoint, indent + size - 2, midpoint);

        gc.dispose();

    }

    Image getPlusImage() {

        if (plusImage == null)
            createImages();

        return plusImage;

    }

    Image getMinusImage() {

        if (minusImage == null)
            createImages();

        return minusImage;

    }

    /**
     * 
     * Gets the index of an item.
     * 
     * 
     * 
     * <p>
     * The widget is searched starting at 0 until an
     * 
     * item is found that is equal to the search item.
     * 
     * If no item is found, -1 is returned. Indexing
     * 
     * is zero based. This index is relative to the parent only.
     * 
     * 
     * 
     * @param item
     *            the search item
     * 
     * @return the index of the item or -1
     * 
     * 
     *  
     */

    public int indexOf(TableTreeItem item) {

        for (int i = 0; i < items.length; i++) {

            if (item == items[i])
                return i;

        }

        return -1;

    }

    void onDispose() {

        inDispose = true;

        for (int i = 0; i < items.length; i++) {

            items[i].dispose();

        }

        inDispose = false;

        if (plusImage != null)
            plusImage.dispose();

        if (minusImage != null)
            minusImage.dispose();

        if (sizeImage != null)
            sizeImage.dispose();

        plusImage = minusImage = sizeImage = null;

    }

    void onResize() {

        Rectangle area = getClientArea();

        table.setBounds(0, 0, area.width, area.height);

    }

    void onSelection(Event e) {

        Event event = new Event();

        TableItem tableItem = (TableItem) e.item;

        TableTreeItem item = getItem(tableItem);

        event.item = item;

        if (e.type == SWT.Selection

                && e.detail == SWT.CHECK

                && item != null) {

            event.detail = SWT.CHECK;

            item.checked = tableItem.getChecked();

        }

        notifyListeners(e.type, event);

    }

    public TableTreeItem getItem(Point point) {

        TableItem item = table.getItem(point);

        if (item == null)
            return null;

        return getItem(item);

    }

    TableTreeItem getItem(TableItem tableItem) {

        if (tableItem == null)
            return null;

        for (int i = 0; i < items.length; i++) {

            TableTreeItem item = items[i].getItem(tableItem);

            if (item != null)
                return item;

        }

        return null;

    }

    void onFocusIn() {

        table.setFocus();

    }

    void onMouseDown(Event event) {

        /* If user clicked on the [+] or [-], expand or collapse the tree. */

        TableItem[] items = table.getItems();

        for (int i = 0; i < items.length; i++) {

            Rectangle rect = items[i].getImageBounds(0);

            if (rect.contains(event.x, event.y)) {

                TableTreeItem item = (TableTreeItem) items[i].getData();

                event = new Event();

                event.item = item;

                item.setExpanded(!item.getExpanded());

                if (item.getExpanded()) {

                    notifyListeners(SWT.Expand, event);

                } else {

                    notifyListeners(SWT.Collapse, event);

                }

                return;

            }

        }

    }

    /**
     * 
     * Removes all items.
     * 
     * <p>
     * 
     * This operation will fail when an item
     * 
     * could not be removed in the OS.
     * 
     * 
     * 
     * @exception SWTError
     *                <ul>
     * 
     * <li>ERROR_THREAD_INVALID_ACCESS when called from the wrong thread
     * 
     * <li>ERROR_WIDGET_DISPOSED when the widget has been disposed
     * 
     * <li>ERROR_ITEM_NOT_REMOVED when the operation fails
     * 
     * </ul>
     *  
     */

    public void removeAll() {

        setRedraw(false);

        for (int i = items.length - 1; i >= 0; i--) {

            items[i].dispose();

        }

        items = EMPTY_ITEMS;

        setRedraw(true);

    }

    void removeItem(TableTreeItem item) {

        int index = 0;

        while (index < items.length && items[index] != item)
            index++;

        if (index == items.length)
            return;

        TableTreeItem[] newItems = new TableTreeItem[items.length - 1];

        System.arraycopy(items, 0, newItems, 0, index);

        System.arraycopy(items, index + 1, newItems, index, items.length - index - 1);

        items = newItems;

    }

    /**
     * 
     * Removes the listener.
     * 
     * <p>
     * 
     * 
     * 
     * @param listener
     *            the listener
     * 
     * 
     * 
     * @exception SWTError
     *                <ul>
     * 
     * <li>ERROR_THREAD_INVALID_ACCESS when called from the wrong thread
     * 
     * <li>ERROR_WIDGET_DISPOSED when the widget has been disposed
     * 
     * <li>ERROR_NULL_ARGUMENT when listener is null
     * 
     * </ul>
     *  
     */

    public void removeSelectionListener(SelectionListener listener) {

        if (listener == null)
            throw new SWTError(SWT.ERROR_NULL_ARGUMENT);

        removeListener(SWT.Selection, listener);

        removeListener(SWT.DefaultSelection, listener);

    }

    /**
     * 
     * Removes the listener.
     * 
     * 
     * 
     * @param listener
     *            the listener
     * 
     * 
     * 
     * @exception SWTError
     *                <ul>
     * 
     * <li>ERROR_THREAD_INVALID_ACCESS when called from the wrong thread
     * 
     * <li>ERROR_WIDGET_DISPOSED when the widget has been disposed
     * 
     * <li>ERROR_NULL_ARGUMENT when listener is null
     * 
     * </ul>
     *  
     */

    public void removeTreeListener(TreeListener listener) {

        if (listener == null)
            throw new SWTError(SWT.ERROR_NULL_ARGUMENT);

        removeListener(SWT.Expand, listener);

        removeListener(SWT.Collapse, listener);

    }

    /**
     * 
     * Selects all items.
     * 
     * <p>
     * 
     * If an item is not selected, it is selected.
     * 
     * If an item is selected, it remains selected.
     * 
     * 
     * 
     * @exception SWTError
     *                <ul>
     * 
     * <li>ERROR_THREAD_INVALID_ACCESS when called from the wrong thread
     * 
     * <li>ERROR_WIDGET_DISPOSED when the widget has been disposed
     * 
     * </ul>
     *  
     */

    public void selectAll() {

        table.selectAll();

    }

    /**
     * 
     * Sets the widget background color.
     * 
     * <p>
     * 
     * When new color is null, the background reverts
     * 
     * to the default system color for the widget.
     * 
     * 
     * 
     * @param color
     *            the new color (or null)
     * 
     * 
     * 
     * @exception SWTError
     *                <ul>
     * 
     * <li>ERROR_THREAD_INVALID_ACCESS when called from the wrong thread</li>
     * 
     * <li>ERROR_WIDGET_DISPOSED when the widget has been disposed</li>
     * 
     * </ul>
     *  
     */

    public void setBackground(Color color) {

        super.setBackground(color);

        table.setBackground(color);

        if (sizeImage != null) {

            GC gc = new GC(sizeImage);

            gc.setBackground(getBackground());

            Rectangle size = sizeImage.getBounds();

            gc.fillRectangle(size);

            gc.dispose();

        }

    }

    /**
     * 
     * Sets the enabled state.
     * 
     * <p>
     * 
     * A disabled widget is typically not selectable from
     * 
     * the user interface and draws with an inactive or
     * 
     * grayed look.
     * 
     * 
     * 
     * @param enabled
     *            the new enabled state
     * 
     * 
     * 
     * @exception SWTError
     *                <ul>
     * 
     * <li>ERROR_THREAD_INVALID_ACCESS when called from the wrong thread</li>
     * 
     * <li>ERROR_WIDGET_DISPOSED when the widget has been disposed</li>
     * 
     * </ul>
     *  
     */

    public void setEnabled(boolean enabled) {

        super.setEnabled(enabled);

        table.setEnabled(enabled);

    }

    /**
     * 
     * Sets the widget font.
     * 
     * <p>
     * 
     * When new font is null, the font reverts
     * 
     * to the default system font for the widget.
     * 
     * 
     * 
     * @param font
     *            the new font (or null)
     * 
     * 
     * 
     * @exception SWTError
     *                <ul>
     * 
     * <li>ERROR_THREAD_INVALID_ACCESS when called from the wrong thread</li>
     * 
     * <li>ERROR_WIDGET_DISPOSED when the widget has been disposed</li>
     * 
     * </ul>
     *  
     */

    public void setFont(Font font) {

        super.setFont(font);

        table.setFont(font);

    }

    /**
     * 
     * Gets the widget foreground color.
     * 
     * <p>
     * 
     * @return the widget foreground color
     * 
     * 
     * 
     * @exception SWTError
     *                <ul>
     * 
     * <li>ERROR_THREAD_INVALID_ACCESS when called from the wrong thread</li>
     * 
     * <li>ERROR_WIDGET_DISPOSED when the widget has been disposed</li>
     * 
     * </ul>
     *  
     */

    public void setForeground(Color color) {

        super.setForeground(color);

        table.setForeground(color);

    }

    /**
     * 
     * Sets the pop up menu.
     * 
     * <p>
     * 
     * Every control has an optional pop up menu that is
     * 
     * displayed when the user requests a popup menu for
     * 
     * the control. The sequence of key strokes/button
     * 
     * presses/button releases that is used to request
     * 
     * a pop up menu is platform specific.
     * 
     * 
     * 
     * @param menu
     *            the new pop up menu
     * 
     * 
     * 
     * @exception SWTError
     *                <ul>
     * 
     * <li>ERROR_THREAD_INVALID_ACCESS when called from the wrong thread</li>
     * 
     * <li>ERROR_WIDGET_DISPOSED when the widget has been disposed</li>
     * 
     * <li>ERROR_MENU_NOT_POP_UP when the menu is not a POP_UP</li>
     * 
     * <li>ERROR_NO_COMMON_PARENT when the menu is not in the same widget tree
     * </li>
     * 
     * </ul>
     *  
     */

    public void setMenu(Menu menu) {

        super.setMenu(menu);

        table.setMenu(menu);

    }

    /**
     * 
     * Sets the selection.
     * 
     * <p>
     * 
     * @param items
     *            new selection
     * 
     * 
     * 
     * @exception SWTError
     *                <ul>
     * 
     * <li>ERROR_THREAD_INVALID_ACCESS when called from the wrong thread
     * 
     * <li>ERROR_WIDGET_DISPOSED when the widget has been disposed
     * 
     * <li>ERROR_NULL_ARGUMENT when items is null
     * 
     * </ul>
     *  
     */

    public void setSelection(TableTreeItem[] items) {

        TableItem[] tableItems = new TableItem[items.length];

        for (int i = 0; i < items.length; i++) {

            if (items[i] == null)
                throw new SWTError(SWT.ERROR_NULL_ARGUMENT);

            if (!items[i].getVisible())
                expandItem(items[i]);

            tableItems[i] = items[i].tableItem;

        }

        table.setSelection(tableItems);

    }

    /**
     * 
     * Sets the tool tip text.
     * 
     * <p>
     * 
     * @param string
     *            the new tool tip text (or null)
     * 
     * 
     * 
     * @exception SWTError
     *                <ul>
     * 
     * <li>ERROR_THREAD_INVALID_ACCESS when called from the wrong thread</li>
     * 
     * <li>ERROR_WIDGET_DISPOSED when the widget has been disposed</li>
     * 
     * </ul>
     *  
     */

    public void setToolTipText(String string) {

        super.setToolTipText(string);

        table.setToolTipText(string);

    }

    /**
     * 
     * Shows the item.
     * 
     * <p>
     * 
     * @param item
     *            the item to be shown
     * 
     * 
     * 
     * @exception SWTError
     *                <ul>
     * 
     * <li>ERROR_THREAD_INVALID_ACCESS when called from the wrong thread
     * 
     * <li>ERROR_WIDGET_DISPOSED when the widget has been disposed
     * 
     * <li>ERROR_NULL_ARGUMENT when item is null
     * 
     * </ul>
     *  
     */

    public void showItem(TableTreeItem item) {

        if (item == null)
            throw new SWTError(SWT.ERROR_NULL_ARGUMENT);

        if (!item.getVisible())
            expandItem(item);

        TableItem tableItem = item.tableItem;

        table.showItem(tableItem);

    }

    /**
     * 
     * Shows the selection.
     * 
     * <p>
     * 
     * If there is no selection or the selection
     * 
     * is already visible, this method does nothing.
     * 
     * If the selection is scrolled out of view,
     * 
     * the top index of the widget is changed such
     * 
     * that selection becomes visible.
     * 
     * 
     * 
     * @exception SWTError
     *                <ul>
     * 
     * <li>ERROR_THREAD_INVALID_ACCESS when called from the wrong thread
     * 
     * <li>ERROR_WIDGET_DISPOSED when the widget has been disposed
     * 
     * </ul>
     *  
     */

    public void showSelection() {

        table.showSelection();

    }

}

//TableTreeItem

/*
 * 
 * (c) Copyright IBM Corp. 2000, 2001.
 * 
 * All Rights Reserved
 *  
 */

/**
 * 
 * A TableTreeItem is a selectable user interface object
 * 
 * that represents an item in a heirarchy of items in a
 * 
 * TableTree.
 *  
 */

class TableTreeItem extends Item {

    TableItem tableItem;

    TableTree parent;

    TableTreeItem parentItem;

    TableTreeItem[] items = TableTree.EMPTY_ITEMS;

    String[] texts = TableTree.EMPTY_TEXTS;

    Image[] images = TableTree.EMPTY_IMAGES;

    boolean expanded;

    boolean checked;

    /**
     * 
     * Create a new instance of a root item.
     * 
     * 
     * 
     * @param parent
     *            the TableTree that contains this root item
     * 
     * @param style
     *            the bitwise OR'ing of widget styles
     *  
     */

    public TableTreeItem(TableTree parent, int style) {

        this(parent, style, parent.getItemCount());

    }

    /**
     * 
     * Create a new instance of a root item in the position
     * 
     * indicated by the specified index.
     * 
     * 
     * 
     * @param parent
     *            the TableTree that contains this root item
     * 
     * @param style
     *            the bitwise OR'ing of widget styles
     * 
     * @param index
     *            specifies the position of this item in the TableTree
     * 
     * relative to other root items
     *  
     */

    public TableTreeItem(TableTree parent, int style, int index) {

        this(parent, null, style, index);

    }

    /**
     * 
     * Create a new instance of a sub item.
     * 
     * 
     * 
     * @param parent
     *            this item's parent in the hierarchy of TableTree items
     * 
     * @param style
     *            the bitwise OR'ing of widget styles
     *  
     */

    public TableTreeItem(TableTreeItem parent, int style) {

        this(parent, style, parent.getItemCount());

    }

    /**
     * 
     * Create a new instance of a sub item in the position
     * 
     * indicated by the specified index.
     * 
     * 
     * 
     * @param parent
     *            this item's parent in the hierarchy of TableTree items
     * 
     * @param style
     *            the bitwise OR'ing of widget styles
     * 
     * @param index
     *            specifies the position of this item in the TableTree
     * 
     * relative to other children of the same parent
     *  
     */

    public TableTreeItem(TableTreeItem parent, int style, int index) {

        this(parent.getParent(), parent, style, index);

    }

    TableTreeItem(TableTree parent, TableTreeItem parentItem, int style, int index) {

        super(parent, style);

        this.parent = parent;

        this.parentItem = parentItem;

        if (parentItem == null) {

            /* Root items are visible immediately */

            int tableIndex = parent.addItem(this, index);

            tableItem = new TableItem(parent.getTable(), style, tableIndex);

            tableItem.setData(this);

            addCheck();

            /*
             * 
             * Feature in the Table. The table uses the first image that
             * 
             * is inserted into the table to size the table rows. If the
             * 
             * user is allowed to insert the first image, this will cause
             * 
             * the +/- images to be scaled. The fix is to insert a dummy
             * 
             * image to force the size.
             *  
             */

            if (parent.sizeImage == null) {

                int itemHeight = parent.getItemHeight();

                parent.sizeImage = new Image(null, itemHeight, itemHeight);

                GC gc = new GC(parent.sizeImage);

                gc.setBackground(parent.getBackground());

                gc.fillRectangle(0, 0, itemHeight, itemHeight);

                gc.dispose();

                tableItem.setImage(0, parent.sizeImage);

            }

        } else {

            parentItem.addItem(this, index);

        }

    }

    void addCheck() {

        Table table = parent.getTable();

        if ((table.getStyle() & SWT.CHECK) == 0)
            return;

        tableItem.setChecked(checked);

    }

    void addItem(TableTreeItem item, int index) {

        if (item == null)
            throw new SWTError(SWT.ERROR_NULL_ARGUMENT);

        if (index < 0 || index > items.length)
            throw new SWTError(SWT.ERROR_INVALID_ARGUMENT);

        /* Now that item has a sub-node it must indicate that it can be expanded */

        if (items.length == 0 && index == 0) {

            if (tableItem != null) {

                Image image = expanded ? parent.getMinusImage() : parent.getPlusImage();

                tableItem.setImage(0, image);

            }

        }

        /* Put the item in the items list */

        TableTreeItem[] newItems = new TableTreeItem[items.length + 1];

        System.arraycopy(items, 0, newItems, 0, index);

        newItems[index] = item;

        System.arraycopy(items, index, newItems, index + 1, items.length - index);

        items = newItems;

        if (expanded)
            item.setVisible(true);

    }

    /**
     * 
     * Gets the widget bounds at the specified index.
     * 
     * <p>
     * 
     * @return the widget bounds at the specified index
     * 
     * 
     * 
     * @exception SWTError
     *                <ul>
     * 
     * <li>ERROR_THREAD_INVALID_ACCESS when called from the wrong thread</li>
     * 
     * <li>ERROR_WIDGET_DISPOSED when the widget has been disposed</li>
     * 
     * </ul>
     *  
     */

    public Rectangle getBounds(int index) {

        if (tableItem != null) {

            return tableItem.getBounds(index);

        } else {

            return new Rectangle(0, 0, 0, 0);

        }

    }

    /**
     * 
     * Gets the checked state.
     * 
     * <p>
     * 
     * @return the item checked state.
     * 
     * 
     * 
     * @exception SWTError
     *                <ul>
     * 
     * <li>ERROR_THREAD_INVALID_ACCESS when called from the wrong thread</li>
     * 
     * <li>ERROR_WIDGET_DISPOSED when the widget has been disposed</li>
     * 
     * </ul>
     *  
     */

    public boolean getChecked() {

        if (tableItem == null) {

            return checked;

        }

        return tableItem.getChecked();

    }

    /**
     * 
     * Gets the Display.
     * 
     * <p>
     * 
     * This method gets the Display that is associated
     * 
     * with the widget.
     * 
     * 
     * 
     * @return the widget data
     * 
     * 
     * 
     * @exception SWTError
     *                <ul>
     * 
     * <li>ERROR_THREAD_INVALID_ACCESS when called from the wrong thread</li>
     * 
     * <li>ERROR_WIDGET_DISPOSED when the widget has been disposed</li>
     * 
     * </ul>
     *  
     */

    public Display getDisplay() {

        TableTree parent = this.parent;

        if (parent == null)
            throw new SWTError(SWT.ERROR_WIDGET_DISPOSED);

        return parent.getDisplay();

    }

    /**
     * 
     * Gets the expanded state of the widget.
     * 
     * <p>
     * 
     * @return a boolean that is the expanded state of the widget
     *  
     */

    public boolean getExpanded() {

        return expanded;

    }

    /**
     * 
     * Gets the first image.
     * 
     * <p>
     * 
     * The image in column 0 is reserved for the [+] and [-]
     * 
     * images of the tree, therefore getImage(0) will return null.
     * 
     * 
     * 
     * @return the image at index 0
     *  
     */

    public Image getImage() {

        return getImage(0);

    }

    /**
     * 
     * Gets the image at the specified index.
     * 
     * <p>
     * 
     * Indexing is zero based. The image can be null.
     * 
     * The image in column 0 is reserved for the [+] and [-]
     * 
     * images of the tree, therefore getImage(0) will return null.
     * 
     * Return null if the index is out of range.
     * 
     * 
     * 
     * @param index
     *            the index of the image
     * 
     * @return the image at the specified index or null
     *  
     */

    public Image getImage(int index) {

        if (0 < index && index < images.length)
            return images[index];

        return null;

    }

    int getIndent() {

        if (parentItem == null)
            return 0;

        return parentItem.getIndent() + 1;

    }

    /**
     * 
     * Gets the number of sub items.
     * 
     * <p>
     * 
     * @return the number of sub items
     *  
     */

    public int getItemCount() {

        return items.length;

    }

    /**
     * 
     * Gets the sub items.
     * 
     * <p>
     * 
     * @return the sub items
     *  
     */

    public TableTreeItem[] getItems() {

        TableTreeItem[] newItems = new TableTreeItem[items.length];

        System.arraycopy(items, 0, newItems, 0, items.length);

        return newItems;

    }

    TableTreeItem getItem(TableItem tableItem) {

        if (tableItem == null)
            return null;

        if (this.tableItem == tableItem)
            return this;

        for (int i = 0; i < items.length; i++) {

            TableTreeItem item = items[i].getItem(tableItem);

            if (item != null)
                return item;

        }

        return null;

    }

    /**
     * 
     * Gets the parent.
     * 
     * <p>
     * 
     * @return the parent
     *  
     */

    public TableTree getParent() {

        return parent;

    }

    /**
     * 
     * Gets the parent item.
     * 
     * <p>
     * 
     * @return the parent item.
     *  
     */

    public TableTreeItem getParentItem() {

        return parentItem;

    }

    /**
     * 
     * Gets the first item text.
     * 
     * <p>
     * 
     * @return the item text at index 0, which can be null
     * 
     * 
     * 
     * @exception SWTError
     *                <ul>
     * 
     * <li>ERROR_THREAD_INVALID_ACCESS when called from the wrong thread</li>
     * 
     * <li>ERROR_WIDGET_DISPOSED when the widget has been disposed</li>
     * 
     * <li>ERROR_CANNOT_GET_TEXT when the operation fails</li>
     * 
     * </ul>
     *  
     */

    public String getText() {

        return getText(0);

    }

    /**
     * 
     * Gets the item text at the specified index.
     * 
     * <p>
     * 
     * Indexing is zero based.
     * 
     * 
     * 
     * This operation will fail when the index is out
     * 
     * of range or an item could not be queried from
     * 
     * the OS.
     * 
     * 
     * 
     * @param index
     *            the index of the item
     * 
     * @return the item text at the specified index, which can be null
     *  
     */

    public String getText(int index) {

        if (0 <= index && index < texts.length)
            return texts[index];

        return null;

    }

    boolean getVisible() {

        return tableItem != null;

    }

    /**
     * 
     * Gets the index of the specified item.
     * 
     * 
     * 
     * <p>
     * The widget is searched starting at 0 until an
     * 
     * item is found that is equal to the search item.
     * 
     * If no item is found, -1 is returned. Indexing
     * 
     * is zero based. This index is relative to the parent only.
     * 
     * 
     * 
     * @param item
     *            the search item
     * 
     * @return the index of the item or -1 if the item is not found
     * 
     * 
     *  
     */

    public int indexOf(TableTreeItem item) {

        for (int i = 0; i < items.length; i++) {

            if (items[i] == item)
                return i;

        }

        return -1;

    }

    int expandedIndexOf(TableTreeItem item) {

        int index = 0;

        for (int i = 0; i < items.length; i++) {

            if (items[i] == item)
                return index;

            if (items[i].expanded)
                index += items[i].visibleChildrenCount();

            index++;

        }

        return -1;

    }

    int visibleChildrenCount() {

        int count = 0;

        for (int i = 0; i < items.length; i++) {

            if (items[i].getVisible()) {

                count += 1 + items[i].visibleChildrenCount();

            }

        }

        return count;

    }

    public void dispose() {

        for (int i = items.length - 1; i >= 0; i--) {

            items[i].dispose();

        }

        super.dispose();

        if (!parent.inDispose) {

            if (parentItem != null) {

                parentItem.removeItem(this);

            } else {

                parent.removeItem(this);

            }

            if (tableItem != null)
                tableItem.dispose();

        }

        items = null;

        parentItem = null;

        parent = null;

        images = null;

        texts = null;

        tableItem = null;

    }

    void removeItem(TableTreeItem item) {

        int index = 0;

        while (index < items.length && items[index] != item)
            index++;

        if (index == items.length)
            return;

        TableTreeItem[] newItems = new TableTreeItem[items.length - 1];

        System.arraycopy(items, 0, newItems, 0, index);

        System.arraycopy(items, index + 1, newItems, index, items.length - index - 1);

        items = newItems;

        if (items.length == 0) {

            if (tableItem != null)
                tableItem.setImage(0, null);

        }

    }

    /**
     * 
     * Sets the checked state.
     * 
     * <p>
     * 
     * @param checked
     *            the new checked state.
     * 
     * 
     * 
     * @exception SWTError
     *                <ul>
     * 
     * <li>ERROR_THREAD_INVALID_ACCESS when called from the wrong thread</li>
     * 
     * <li>ERROR_WIDGET_DISPOSED when the widget has been disposed</li>
     * 
     * </ul>
     *  
     */

    public void setChecked(boolean checked) {

        if (tableItem != null) {

            tableItem.setChecked(checked);

        }

        this.checked = checked;

    }

    /**
     * 
     * Sets the expanded state.
     * 
     * <p>
     * 
     * @param expanded
     *            the new expanded state.
     * 
     * 
     * 
     * @exception SWTError
     *                <ul>
     * 
     * <li>ERROR_THREAD_INVALID_ACCESS when called from the wrong thread</li>
     * 
     * <li>ERROR_WIDGET_DISPOSED when the widget has been disposed</li>
     * 
     * </ul>
     *  
     */

    public void setExpanded(boolean expanded) {

        if (items.length == 0)
            return;

        this.expanded = expanded;

        if (tableItem == null)
            return;

        parent.setRedraw(false);

        for (int i = 0; i < items.length; i++) {

            items[i].setVisible(expanded);

        }

        Image image = expanded ? parent.getMinusImage() : parent.getPlusImage();

        tableItem.setImage(0, image);

        parent.setRedraw(true);

    }

    /**
     * 
     * Sets the image at an index.
     * 
     * <p>
     * 
     * The image can be null.
     * 
     * The image in column 0 is reserved for the [+] and [-]
     * 
     * images of the tree, therefore do nothing if index is 0.
     * 
     * 
     * 
     * @param image
     *            the new image or null
     * 
     * 
     * 
     * @exception SWTError
     *                <ul>
     * 
     * <li>ERROR_THREAD_INVALID_ACCESS when called from the wrong thread</li>
     * 
     * <li>ERROR_WIDGET_DISPOSED when the widget has been disposed</li>
     * 
     * </ul>
     *  
     */

    public void setImage(int index, Image image) {

        int columnCount = Math.max(parent.getTable().getColumnCount(), 1);

        if (index <= 0 || index >= columnCount)
            return;

        if (images.length < columnCount) {

            Image[] newImages = new Image[columnCount];

            System.arraycopy(images, 0, newImages, 0, images.length);

            images = newImages;

        }

        images[index] = image;

        if (tableItem != null)
            tableItem.setImage(index, image);

    }

    /**
     * 
     * Sets the first image.
     * 
     * <p>
     * 
     * The image can be null.
     * 
     * The image in column 0 is reserved for the [+] and [-]
     * 
     * images of the tree, therefore do nothing.
     * 
     * 
     * 
     * @param image
     *            the new image or null
     *  
     */

    public void setImage(Image image) {

        setImage(0, image);

    }

    /**
     * 
     * Sets the widget text.
     * 
     * <p>
     * 
     * 
     * 
     * The widget text for an item is the label of the
     * 
     * item or the label of the text specified by a column
     * 
     * number.
     * 
     * 
     * 
     * @param index
     *            the column number
     * 
     * @param text
     *            the new text
     * 
     * 
     * 
     * @exception SWTError
     *                <ul>
     * 
     * <li>ERROR_THREAD_INVALID_ACCESS when called from the wrong thread</li>
     * 
     * <li>ERROR_WIDGET_DISPOSED when the widget has been disposed</li>
     * 
     * <li>ERROR_NULL_ARGUMENT when string is null</li>
     * 
     * </ul>
     *  
     */

    public void setText(int index, String text) {

        int columnCount = Math.max(parent.getTable().getColumnCount(), 1);

        if (index < 0 || index >= columnCount)
            return;

        if (texts.length < columnCount) {

            String[] newTexts = new String[columnCount];

            System.arraycopy(texts, 0, newTexts, 0, texts.length);

            texts = newTexts;

        }

        texts[index] = text;

        if (tableItem != null)
            tableItem.setText(index, text);

    }

    /**
     * 
     * Sets the widget text.
     * 
     * <p>
     * 
     * 
     * 
     * The widget text for an item is the label of the
     * 
     * item or the label of the text specified by a column
     * 
     * number.
     * 
     * 
     * 
     * @param index
     *            the column number
     * 
     * @param text
     *            the new text
     * 
     * 
     * 
     * @exception SWTError
     *                <ul>
     * 
     * <li>ERROR_THREAD_INVALID_ACCESS when called from the wrong thread</li>
     * 
     * <li>ERROR_WIDGET_DISPOSED when the widget has been disposed</li>
     * 
     * <li>ERROR_NULL_ARGUMENT when string is null</li>
     * 
     * </ul>
     *  
     */

    public void setText(String string) {

        setText(0, string);

    }

    void setVisible(boolean show) {

        if (parentItem == null)
            return; // this is a root and can not be toggled between visible and
                    // hidden

        if (getVisible() == show)
            return;

        if (show) {

            if (!parentItem.getVisible())
                return; // parentItem must already be visible

            // create underlying table item and set data in table item to stored
            // data

            Table table = parent.getTable();

            int parentIndex = table.indexOf(parentItem.tableItem);

            int index = parentItem.expandedIndexOf(this) + parentIndex + 1;

            if (index < 0)
                return;

            tableItem = new TableItem(table, getStyle(), index);

            tableItem.setData(this);

            tableItem.setImageIndent(getIndent());

            addCheck();

            // restore fields to item

            // ignore any images in the first column

            int columnCount = Math.max(table.getColumnCount(), 1);

            for (int i = 0; i < columnCount; i++) {

                if (i < texts.length && texts[i] != null)
                    setText(i, texts[i]);

                if (i < images.length && images[i] != null)
                    setImage(i, images[i]);

            }

            // display the children and the appropriate [+]/[-] symbol as
            // required

            if (items.length != 0) {

                if (expanded) {

                    tableItem.setImage(0, parent.getMinusImage());

                    for (int i = 0, length = items.length; i < length; i++) {

                        items[i].setVisible(true);

                    }

                } else {

                    tableItem.setImage(0, parent.getPlusImage());

                }

            }

        } else {

            for (int i = 0, length = items.length; i < length; i++) {

                items[i].setVisible(false);

            }

            // remove row from table

            tableItem.dispose();

            tableItem = null;

        }

    }

}