Appendix A: TableTree and TableTreeItem

This appendix contains the source code for the TableTree and TableTreeItem classes.

TableTree

package org.eclipse.swt.custom;

 

/*

 * (c) Copyright IBM Corp. 2000, 2001.

 * All Rights Reserved

 */

 

import java.util.Enumeration;

import java.util.Vector;

import org.eclipse.swt.*;

import org.eclipse.swt.events.*;

import org.eclipse.swt.graphics.*;

import org.eclipse.swt.widgets.*;

 

/**

 * 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

package org.eclipse.swt.custom;

 

/*

 * (c) Copyright IBM Corp. 2000, 2001.

 * All Rights Reserved

 */

 

import org.eclipse.swt.*;

import org.eclipse.swt.events.*;

import org.eclipse.swt.graphics.*;

import org.eclipse.swt.widgets.*;

 

/**

 * A TableTreeItem is a selectable user interface object

 * that represents an item in a heirarchy of items in a

 * TableTree.

 */

public 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;

      }

}

}