Mega Code Archive

 
Categories / Java / SWT Jface Eclipse
 

SWT Table Tree

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