summaryrefslogtreecommitdiffstats
path: root/bundles/org.eclipse.swt/Eclipse SWT/emulated/treetable/org/eclipse/swt
diff options
context:
space:
mode:
authorSilenio Quarti <silenio>2009-07-01 14:50:54 +0000
committerSilenio Quarti <silenio>2009-07-01 14:50:54 +0000
commit093c579a4ffd9551acb901bba9617e7aa776989d (patch)
tree71cf23798b651ef92f188390841a8d130908fb11 /bundles/org.eclipse.swt/Eclipse SWT/emulated/treetable/org/eclipse/swt
parentf664d297f7bb009784868bf3fcf0b3e3bb9a646b (diff)
downloadeclipse.platform.swt-093c579a4ffd9551acb901bba9617e7aa776989d.tar.gz
eclipse.platform.swt-093c579a4ffd9551acb901bba9617e7aa776989d.tar.xz
eclipse.platform.swt-093c579a4ffd9551acb901bba9617e7aa776989d.zip
restore HEAD after accidental deletion by error in automated build script
Diffstat (limited to 'bundles/org.eclipse.swt/Eclipse SWT/emulated/treetable/org/eclipse/swt')
-rw-r--r--bundles/org.eclipse.swt/Eclipse SWT/emulated/treetable/org/eclipse/swt/widgets/Table.java4166
-rw-r--r--bundles/org.eclipse.swt/Eclipse SWT/emulated/treetable/org/eclipse/swt/widgets/TableColumn.java762
-rw-r--r--bundles/org.eclipse.swt/Eclipse SWT/emulated/treetable/org/eclipse/swt/widgets/TableItem.java2032
-rw-r--r--bundles/org.eclipse.swt/Eclipse SWT/emulated/treetable/org/eclipse/swt/widgets/Tree.java4295
-rw-r--r--bundles/org.eclipse.swt/Eclipse SWT/emulated/treetable/org/eclipse/swt/widgets/TreeColumn.java738
-rw-r--r--bundles/org.eclipse.swt/Eclipse SWT/emulated/treetable/org/eclipse/swt/widgets/TreeItem.java2959
6 files changed, 14952 insertions, 0 deletions
diff --git a/bundles/org.eclipse.swt/Eclipse SWT/emulated/treetable/org/eclipse/swt/widgets/Table.java b/bundles/org.eclipse.swt/Eclipse SWT/emulated/treetable/org/eclipse/swt/widgets/Table.java
new file mode 100644
index 0000000000..af445fdd61
--- /dev/null
+++ b/bundles/org.eclipse.swt/Eclipse SWT/emulated/treetable/org/eclipse/swt/widgets/Table.java
@@ -0,0 +1,4166 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2009 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.swt.widgets;
+
+import org.eclipse.swt.*;
+import org.eclipse.swt.events.*;
+import org.eclipse.swt.graphics.*;
+import org.eclipse.swt.internal.*;
+
+/**
+ * Instances of this class implement a selectable user interface
+ * object that displays a list of images and strings and issues
+ * notification when selected.
+ * <p>
+ * The item children that may be added to instances of this class
+ * must be of type <code>TableItem</code>.
+ * </p><p>
+ * Style <code>VIRTUAL</code> is used to create a <code>Table</code> whose
+ * <code>TableItem</code>s are to be populated by the client on an on-demand basis
+ * instead of up-front. This can provide significant performance improvements for
+ * tables that are very large or for which <code>TableItem</code> population is
+ * expensive (for example, retrieving values from an external source).
+ * </p><p>
+ * Here is an example of using a <code>Table</code> with style <code>VIRTUAL</code>:
+ * <code><pre>
+ * final Table table = new Table (parent, SWT.VIRTUAL | SWT.BORDER);
+ * table.setItemCount (1000000);
+ * table.addListener (SWT.SetData, new Listener () {
+ * public void handleEvent (Event event) {
+ * TableItem item = (TableItem) event.item;
+ * int index = table.indexOf (item);
+ * item.setText ("Item " + index);
+ * System.out.println (item.getText ());
+ * }
+ * });
+ * </pre></code>
+ * </p><p>
+ * Note that although this class is a subclass of <code>Composite</code>,
+ * it does not normally make sense to add <code>Control</code> children to
+ * it, or set a layout on it, unless implementing something like a cell
+ * editor.
+ * </p><p>
+ * <dl>
+ * <dt><b>Styles:</b></dt>
+ * <dd>SINGLE, MULTI, CHECK, FULL_SELECTION, HIDE_SELECTION, VIRTUAL, NO_SCROLL</dd>
+ * <dt><b>Events:</b></dt>
+ * <dd>Selection, DefaultSelection, SetData, MeasureItem, EraseItem, PaintItem</dd>
+ * </dl>
+ * </p><p>
+ * Note: Only one of the styles SINGLE, and MULTI may be specified.
+ * </p><p>
+ * IMPORTANT: This class is <em>not</em> intended to be subclassed.
+ * </p>
+ *
+ * @see <a href="http://www.eclipse.org/swt/snippets/#table">Table, TableItem, TableColumn snippets</a>
+ * @see <a href="http://www.eclipse.org/swt/examples.php">SWT Example: ControlExample</a>
+ * @see <a href="http://www.eclipse.org/swt/">Sample code and further information</a>
+ * @noextend This class is not intended to be subclassed by clients.
+ */
+public class Table extends Composite {
+ Canvas header;
+ TableColumn[] columns = new TableColumn [0];
+ TableColumn[] orderedColumns;
+ TableItem[] items = new TableItem [0];
+ TableItem[] selectedItems = new TableItem [0];
+ TableItem focusItem, anchorItem, lastClickedItem;
+ Event lastSelectionEvent;
+ boolean linesVisible, ignoreKey, ignoreDispose, customHeightSet;
+ int itemsCount = 0;
+ int topIndex = 0, horizontalOffset = 0;
+ int fontHeight = 0, imageHeight = 0, itemHeight = 0;
+ int col0ImageWidth = 0;
+ int headerImageHeight = 0;
+ TableColumn resizeColumn;
+ int resizeColumnX = -1;
+ int drawCount = 0;
+ TableColumn sortColumn;
+ int sortDirection = SWT.NONE;
+
+ /* column header tooltip */
+ Listener toolTipListener;
+ Shell toolTipShell;
+ Label toolTipLabel;
+
+ Rectangle arrowBounds, checkboxBounds, clientArea;
+
+ static final int MARGIN_IMAGE = 3;
+ static final int MARGIN_CELL = 1;
+ static final int SIZE_HORIZONTALSCROLL = 5;
+ static final int TOLLERANCE_COLUMNRESIZE = 2;
+ static final int WIDTH_HEADER_SHADOW = 2;
+ static final int WIDTH_CELL_HIGHLIGHT = 1;
+ static final int [] toolTipEvents = new int[] {SWT.MouseExit, SWT.MouseHover, SWT.MouseMove, SWT.MouseDown};
+ static final String ELLIPSIS = "..."; //$NON-NLS-1$
+ static final String ID_UNCHECKED = "UNCHECKED"; //$NON-NLS-1$
+ static final String ID_GRAYUNCHECKED = "GRAYUNCHECKED"; //$NON-NLS-1$
+ static final String ID_CHECKMARK = "CHECKMARK"; //$NON-NLS-1$
+ static final String ID_ARROWUP = "ARROWUP"; //$NON-NLS-1$
+ static final String ID_ARROWDOWN = "ARROWDOWN"; //$NON-NLS-1$
+
+//TEMPORARY CODE
+boolean hasFocus;
+public boolean isFocusControl() {
+ return hasFocus;
+}
+
+/**
+ * Constructs a new instance of this class given its parent
+ * and a style value describing its behavior and appearance.
+ * <p>
+ * The style value is either one of the style constants defined in
+ * class <code>SWT</code> which is applicable to instances of this
+ * class, or must be built by <em>bitwise OR</em>'ing together
+ * (that is, using the <code>int</code> "|" operator) two or more
+ * of those <code>SWT</code> style constants. The class description
+ * lists the style constants that are applicable to the class.
+ * Style bits are also inherited from superclasses.
+ * </p>
+ *
+ * @param parent a composite control which will be the parent of the new instance (cannot be null)
+ * @param style the style of control to construct
+ *
+ * @exception IllegalArgumentException <ul>
+ * <li>ERROR_NULL_ARGUMENT - if the parent is null</li>
+ * </ul>
+ * @exception SWTException <ul>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the parent</li>
+ * <li>ERROR_INVALID_SUBCLASS - if this class is not an allowed subclass</li>
+ * </ul>
+ *
+ * @see SWT#SINGLE
+ * @see SWT#MULTI
+ * @see SWT#CHECK
+ * @see SWT#FULL_SELECTION
+ * @see SWT#HIDE_SELECTION
+ * @see SWT#VIRTUAL
+ * @see SWT#NO_SCROLL
+ * @see Widget#checkSubclass
+ * @see Widget#getStyle
+ */
+public Table (Composite parent, int style) {
+ super (parent, checkStyle (style));
+ setForeground (null); /* set foreground and background to chosen default colors */
+ setBackground (null);
+ GC gc = new GC (this);
+ fontHeight = gc.getFontMetrics ().getHeight ();
+ gc.dispose ();
+ itemHeight = fontHeight + (2 * getCellPadding ());
+ initImages (display);
+ checkboxBounds = getUncheckedImage ().getBounds ();
+ arrowBounds = getArrowDownImage ().getBounds ();
+ clientArea = getClientArea ();
+
+ Listener listener = new Listener () {
+ public void handleEvent (Event event) {
+ handleEvents (event);
+ }
+ };
+ addListener (SWT.Paint, listener);
+ addListener (SWT.MouseDown, listener);
+ addListener (SWT.MouseUp, listener);
+ addListener (SWT.MouseDoubleClick, listener);
+ addListener (SWT.Dispose, listener);
+ addListener (SWT.Resize, listener);
+ addListener (SWT.KeyDown, listener);
+ addListener (SWT.FocusOut, listener);
+ addListener (SWT.FocusIn, listener);
+ addListener (SWT.Traverse, listener);
+
+ header = new Canvas (this, SWT.NO_REDRAW_RESIZE | SWT.NO_FOCUS);
+ header.setVisible (false);
+ header.setBounds (0, 0, 0, fontHeight + 2 * getHeaderPadding ());
+ header.addListener (SWT.Paint, listener);
+ header.addListener (SWT.MouseDown, listener);
+ header.addListener (SWT.MouseUp, listener);
+ header.addListener (SWT.MouseHover, listener);
+ header.addListener (SWT.MouseDoubleClick, listener);
+ header.addListener (SWT.MouseMove, listener);
+ header.addListener (SWT.MouseExit, listener);
+ header.addListener (SWT.MenuDetect, listener);
+
+ toolTipListener = new Listener () {
+ public void handleEvent (Event event) {
+ switch (event.type) {
+ case SWT.MouseHover:
+ case SWT.MouseMove:
+ if (headerUpdateToolTip (event.x)) break;
+ // FALL THROUGH
+ case SWT.MouseExit:
+ case SWT.MouseDown:
+ headerHideToolTip ();
+ break;
+ }
+ }
+ };
+
+ ScrollBar hBar = getHorizontalBar ();
+ if (hBar != null) {
+ hBar.setValues (0, 0, 1, 1, 1, 1);
+ hBar.setVisible (false);
+ hBar.addListener (SWT.Selection, listener);
+ }
+ ScrollBar vBar = getVerticalBar ();
+ if (vBar != null) {
+ vBar.setValues (0, 0, 1, 1, 1, 1);
+ vBar.setVisible (false);
+ vBar.addListener (SWT.Selection, listener);
+ }
+}
+/**
+ * Adds the listener to the collection of listeners who will
+ * be notified when the user changes the receiver's selection, by sending
+ * it one of the messages defined in the <code>SelectionListener</code>
+ * interface.
+ * <p>
+ * When <code>widgetSelected</code> is called, the item field of the event object is valid.
+ * If the receiver has the <code>SWT.CHECK</code> style and the check selection changes,
+ * the event object detail field contains the value <code>SWT.CHECK</code>.
+ * <code>widgetDefaultSelected</code> is typically called when an item is double-clicked.
+ * The item field of the event object is valid for default selection, but the detail field is not used.
+ * </p>
+ *
+ * @param listener the listener which should be notified when the user changes the receiver's selection
+ *
+ * @exception IllegalArgumentException <ul>
+ * <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
+ * </ul>
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @see SelectionListener
+ * @see #removeSelectionListener
+ * @see SelectionEvent
+ */
+public void addSelectionListener (SelectionListener listener) {
+ checkWidget ();
+ if (listener == null) error (SWT.ERROR_NULL_ARGUMENT);
+ TypedListener typedListener = new TypedListener (listener);
+ addListener (SWT.Selection, typedListener);
+ addListener (SWT.DefaultSelection, typedListener);
+}
+boolean checkData (TableItem item, boolean redraw) {
+ if (item.cached) return true;
+ if ((style & SWT.VIRTUAL) != 0) {
+ item.cached = true;
+ Event event = new Event ();
+ event.item = item;
+ event.index = indexOf (item);
+ sendEvent (SWT.SetData, event);
+ if (isDisposed () || item.isDisposed ()) return false;
+ if (redraw) redrawItem (item.index, false);
+ }
+ return true;
+}
+static int checkStyle (int style) {
+ /*
+ * Feature in Windows. Even when WS_HSCROLL or
+ * WS_VSCROLL is not specified, Windows creates
+ * trees and tables with scroll bars. The fix
+ * is to set H_SCROLL and V_SCROLL.
+ *
+ * NOTE: This code appears on all platforms so that
+ * applications have consistent scroll bar behavior.
+ */
+ if ((style & SWT.NO_SCROLL) == 0) {
+ style |= SWT.H_SCROLL | SWT.V_SCROLL;
+ }
+ style |= SWT.NO_REDRAW_RESIZE | SWT.NO_BACKGROUND | SWT.DOUBLE_BUFFERED;
+ //TEMPORARY CODE
+ style |= SWT.FULL_SELECTION;
+ return checkBits (style, SWT.SINGLE, SWT.MULTI, 0, 0, 0, 0);
+}
+protected void checkSubclass () {
+ if (!isValidSubclass ()) error (SWT.ERROR_INVALID_SUBCLASS);
+}
+/**
+ * Clears the item at the given zero-relative index in the receiver.
+ * The text, icon and other attributes of the item are set to the default
+ * value. If the table was created with the <code>SWT.VIRTUAL</code> style,
+ * these attributes are requested again as needed.
+ *
+ * @param index the index of the item to clear
+ *
+ * @exception IllegalArgumentException <ul>
+ * <li>ERROR_INVALID_RANGE - if the index is not between 0 and the number of elements in the list minus 1 (inclusive)</li>
+ * </ul>
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @see SWT#VIRTUAL
+ * @see SWT#SetData
+ *
+ * @since 3.0
+ */
+public void clear (int index) {
+ checkWidget ();
+ if (!(0 <= index && index < itemsCount)) error (SWT.ERROR_INVALID_RANGE);
+ Rectangle bounds = items [index].getBounds (false);
+ int oldRightX = bounds.x + bounds.width;
+ items [index].clear ();
+ if (columns.length == 0) updateHorizontalBar (0, -oldRightX);
+ redrawItem (index, false);
+}
+/**
+ * Removes the items from the receiver which are between the given
+ * zero-relative start and end indices (inclusive). The text, icon
+ * and other attributes of the items are set to their default values.
+ * If the table was created with the <code>SWT.VIRTUAL</code> style,
+ * these attributes are requested again as needed.
+ *
+ * @param start the start index of the item to clear
+ * @param end the end index of the item to clear
+ *
+ * @exception IllegalArgumentException <ul>
+ * <li>ERROR_INVALID_RANGE - if either the start or end are not between 0 and the number of elements in the list minus 1 (inclusive)</li>
+ * </ul>
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @see SWT#VIRTUAL
+ * @see SWT#SetData
+ *
+ * @since 3.0
+ */
+public void clear (int start, int end) {
+ checkWidget ();
+ if (start > end) return;
+ if (!(0 <= start && start <= end && end < itemsCount)) {
+ error (SWT.ERROR_INVALID_RANGE);
+ }
+ for (int i = start; i <= end; i++) {
+ items [i].clear ();
+ }
+ updateHorizontalBar ();
+ redrawItems (start, end, false);
+}
+/**
+ * Clears the items at the given zero-relative indices in the receiver.
+ * The text, icon and other attributes of the items are set to their default
+ * values. If the table was created with the <code>SWT.VIRTUAL</code> style,
+ * these attributes are requested again as needed.
+ *
+ * @param indices the array of indices of the items
+ *
+ * @exception IllegalArgumentException <ul>
+ * <li>ERROR_INVALID_RANGE - if the index is not between 0 and the number of elements in the list minus 1 (inclusive)</li>
+ * <li>ERROR_NULL_ARGUMENT - if the indices array is null</li>
+ * </ul>
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @see SWT#VIRTUAL
+ * @see SWT#SetData
+ *
+ * @since 3.0
+ */
+public void clear (int [] indices) {
+ checkWidget ();
+ if (indices == null) error (SWT.ERROR_NULL_ARGUMENT);
+ if (indices.length == 0) return;
+ for (int i = 0; i < indices.length; i++) {
+ if (!(0 <= indices [i] && indices [i] < itemsCount)) {
+ error (SWT.ERROR_INVALID_RANGE);
+ }
+ }
+
+ for (int i = 0; i < indices.length; i++) {
+ items [indices [i]].clear ();
+ }
+ updateHorizontalBar ();
+ for (int i = 0; i < indices.length; i++) {
+ redrawItem (indices [i], false);
+ }
+}
+/**
+ * Clears all the items in the receiver. The text, icon and other
+ * attributes of the items are set to their default values. If the
+ * table was created with the <code>SWT.VIRTUAL</code> style, these
+ * attributes are requested again as needed.
+ *
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @see SWT#VIRTUAL
+ * @see SWT#SetData
+ *
+ * @since 3.0
+ */
+public void clearAll () {
+ checkWidget ();
+ clear (0, itemsCount - 1);
+}
+/*
+ * Returns the ORDERED index of the column that the specified x falls within,
+ * or -1 if the x lies to the right of the last column.
+ */
+int computeColumnIntersect (int x, int startColumn) {
+ TableColumn[] orderedColumns = getOrderedColumns ();
+ if (orderedColumns.length - 1 < startColumn) return -1;
+ int rightX = orderedColumns [startColumn].getX ();
+ for (int i = startColumn; i < orderedColumns.length; i++) {
+ rightX += orderedColumns [i].width;
+ if (x < rightX) return i;
+ }
+ return -1;
+}
+public Point computeSize (int wHint, int hHint, boolean changed) {
+ checkWidget ();
+ int width = 0, height = 0;
+ if (wHint != SWT.DEFAULT) {
+ width = wHint;
+ } else {
+ if (columns.length == 0) {
+ for (int i = 0; i < itemsCount; i++) {
+ Rectangle itemBounds = items [i].getBounds (false);
+ width = Math.max (width, itemBounds.x + itemBounds.width);
+ }
+ } else {
+ TableColumn[] orderedColumns = getOrderedColumns ();
+ TableColumn lastColumn = orderedColumns [orderedColumns.length - 1];
+ width = lastColumn.getX () + lastColumn.width;
+ }
+ }
+ if (hHint != SWT.DEFAULT) {
+ height = hHint;
+ } else {
+ height = getHeaderHeight () + itemsCount * itemHeight;
+ }
+ Rectangle result = computeTrim (0, 0, width, height);
+ return new Point (result.width, result.height);
+}
+void createItem (TableColumn column, int index) {
+ TableColumn[] newColumns = new TableColumn [columns.length + 1];
+ System.arraycopy (columns, 0, newColumns, 0, index);
+ newColumns [index] = column;
+ System.arraycopy (columns, index, newColumns, index + 1, columns.length - index);
+ columns = newColumns;
+
+ if (orderedColumns != null) {
+ int insertIndex = 0;
+ if (index > 0) {
+ insertIndex = columns [index - 1].getOrderIndex () + 1;
+ }
+ TableColumn[] newOrderedColumns = new TableColumn [orderedColumns.length + 1];
+ System.arraycopy (orderedColumns, 0, newOrderedColumns, 0, insertIndex);
+ newOrderedColumns [insertIndex] = column;
+ System.arraycopy (
+ orderedColumns,
+ insertIndex,
+ newOrderedColumns,
+ insertIndex + 1,
+ orderedColumns.length - insertIndex);
+ orderedColumns = newOrderedColumns;
+ }
+
+ /* allow all items to update their internal structures accordingly */
+ for (int i = 0; i < itemsCount; i++) {
+ items [i].addColumn (column);
+ }
+
+ /* existing items become hidden when going from 0 to 1 column (0 width) */
+ if (columns.length == 1 && itemsCount > 0) {
+ redrawFromItemDownwards (topIndex);
+ } else {
+ /* checkboxes become hidden when creating a column with index == orderedIndex == 0 (0 width) */
+ if (itemsCount > 0 && (style & SWT.CHECK) != 0 && index == 0 && column.getOrderIndex () == 0) {
+ redrawFromItemDownwards (topIndex);
+ }
+ }
+}
+void createItem (TableItem item) {
+ int index = item.index;
+ if (itemsCount == items.length) {
+ int grow = drawCount <= 0 ? 4 : Math.max (4, items.length * 3 / 2);
+ TableItem[] newItems = new TableItem [items.length + grow];
+ System.arraycopy (items, 0, newItems, 0, items.length);
+ items = newItems;
+ }
+ if (index != itemsCount) {
+ /* new item is not at end of list, so shift other items right to create space for it */
+ System.arraycopy (items, index, items, index + 1, itemsCount - index);
+ }
+ items [index] = item;
+ itemsCount++;
+
+ /* update the index for items bumped down by this new item */
+ for (int i = index + 1; i < itemsCount; i++) {
+ items [i].index = i;
+ }
+
+ /* update scrollbars */
+ updateVerticalBar ();
+ Rectangle bounds = item.getBounds (false);
+ int rightX = bounds.x + bounds.width;
+ updateHorizontalBar (rightX, rightX);
+ /*
+ * If new item is above viewport then adjust topIndex and the vertical
+ * scrollbar so that the current viewport items will not change.
+ */
+ if (item.index < topIndex) {
+ topIndex++;
+ ScrollBar vBar = getVerticalBar ();
+ if (vBar != null) vBar.setSelection (topIndex);
+ return;
+ }
+ /*
+ * If this is the first item and the receiver has focus then its boundary
+ * focus ring must be removed.
+ */
+ if (itemsCount == 1 && isFocusControl ()) {
+ focusItem = item;
+ redraw ();
+ return;
+ }
+ if (item.isInViewport ()) {
+ redrawFromItemDownwards (index);
+ }
+}
+/**
+ * Deselects the item at the given zero-relative index in the receiver.
+ * If the item at the index was already deselected, it remains
+ * deselected. Indices that are out of range are ignored.
+ *
+ * @param index the index of the item to deselect
+ *
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public void deselect (int index) {
+ checkWidget ();
+ if (!(0 <= index && index < itemsCount)) return;
+ TableItem item = items [index];
+ int selectIndex = getSelectionIndex (item);
+ if (selectIndex == -1) return;
+
+ TableItem[] newSelectedItems = new TableItem [selectedItems.length - 1];
+ System.arraycopy (selectedItems, 0, newSelectedItems, 0, selectIndex);
+ System.arraycopy (selectedItems, selectIndex + 1, newSelectedItems, selectIndex, newSelectedItems.length - selectIndex);
+ selectedItems = newSelectedItems;
+
+ if (hasFocus () || (style & SWT.HIDE_SELECTION) == 0) {
+ redrawItem (item.index, false);
+ }
+}
+/**
+ * Deselects the items at the given zero-relative indices in the receiver.
+ * If the item at the given zero-relative index in the receiver
+ * is selected, it is deselected. If the item at the index
+ * was not selected, it remains deselected. The range of the
+ * indices is inclusive. Indices that are out of range are ignored.
+ *
+ * @param start the start index of the items to deselect
+ * @param end the end index of the items to deselect
+ *
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public void deselect (int start, int end) {
+ checkWidget ();
+ if (start == 0 && end == itemsCount - 1) {
+ deselectAll ();
+ } else {
+ start = Math.max (start, 0);
+ end = Math.min (end, itemsCount - 1);
+ for (int i = start; i <= end; i++) {
+ deselect (i);
+ }
+ }
+}
+/**
+ * Deselects the items at the given zero-relative indices in the receiver.
+ * If the item at the given zero-relative index in the receiver
+ * is selected, it is deselected. If the item at the index
+ * was not selected, it remains deselected. Indices that are out
+ * of range and duplicate indices are ignored.
+ *
+ * @param indices the array of indices for the items to deselect
+ *
+ * @exception IllegalArgumentException <ul>
+ * <li>ERROR_NULL_ARGUMENT - if the set of indices is null</li>
+ * </ul>
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public void deselect (int [] indices) {
+ checkWidget ();
+ if (indices == null) error (SWT.ERROR_NULL_ARGUMENT);
+ if (indices.length == 0) return;
+ for (int i = 0; i < indices.length; i++) {
+ deselect (indices [i]);
+ }
+}
+/**
+ * Deselects all selected items in the receiver.
+ *
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public void deselectAll () {
+ checkWidget ();
+ TableItem[] oldSelection = selectedItems;
+ selectedItems = new TableItem [0];
+ if (hasFocus () || (style & SWT.HIDE_SELECTION) == 0) {
+ for (int i = 0; i < oldSelection.length; i++) {
+ redrawItem (oldSelection [i].index, true);
+ }
+ }
+}
+void deselectItem (TableItem item) {
+ int index = getSelectionIndex (item);
+ if (index == -1) return;
+ TableItem[] newSelectedItems = new TableItem [selectedItems.length - 1];
+ System.arraycopy (selectedItems, 0, newSelectedItems, 0, index);
+ System.arraycopy (
+ selectedItems,
+ index + 1,
+ newSelectedItems,
+ index,
+ newSelectedItems.length - index);
+ selectedItems = newSelectedItems;
+}
+void destroyItem (TableColumn column) {
+ headerHideToolTip ();
+ int index = column.getIndex ();
+ int orderedIndex = column.getOrderIndex ();
+
+ TableColumn[] newColumns = new TableColumn [columns.length - 1];
+ System.arraycopy (columns, 0, newColumns, 0, index);
+ System.arraycopy (columns, index + 1, newColumns, index, newColumns.length - index);
+ columns = newColumns;
+
+ if (orderedColumns != null) {
+ if (columns.length < 2) {
+ orderedColumns = null;
+ } else {
+ int removeIndex = column.getOrderIndex ();
+ TableColumn[] newOrderedColumns = new TableColumn [orderedColumns.length - 1];
+ System.arraycopy (orderedColumns, 0, newOrderedColumns, 0, removeIndex);
+ System.arraycopy (
+ orderedColumns,
+ removeIndex + 1,
+ newOrderedColumns,
+ removeIndex,
+ newOrderedColumns.length - removeIndex);
+ orderedColumns = newOrderedColumns;
+ }
+ }
+
+ /* ensure that column 0 always has left-alignment */
+ if (index == 0 && columns.length > 0) {
+ columns [0].style |= SWT.LEFT;
+ columns [0].style &= ~(SWT.CENTER | SWT.RIGHT);
+ }
+
+ /* allow all items to update their internal structures accordingly */
+ for (int i = 0; i < itemsCount; i++) {
+ items [i].removeColumn (column, index);
+ }
+
+ /* update horizontal scrollbar */
+ int lastColumnIndex = columns.length - 1;
+ if (lastColumnIndex < 0) { /* no more columns */
+ updateHorizontalBar ();
+ } else {
+ int newWidth = 0;
+ for (int i = 0; i < columns.length; i++) {
+ newWidth += columns [i].width;
+ }
+ ScrollBar hBar = getHorizontalBar ();
+ if (hBar != null) {
+ hBar.setMaximum (newWidth);
+ hBar.setVisible (clientArea.width < newWidth);
+ }
+ int selection = hBar.getSelection ();
+ if (selection != horizontalOffset) {
+ horizontalOffset = selection;
+ redraw ();
+ if (header.isVisible () && drawCount <= 0) header.redraw ();
+ }
+ }
+ TableColumn[] orderedColumns = getOrderedColumns ();
+ for (int i = orderedIndex; i < orderedColumns.length; i++) {
+ if (!orderedColumns [i].isDisposed ()) {
+ orderedColumns [i].sendEvent (SWT.Move);
+ }
+ }
+
+ if (sortColumn == column) {
+ sortColumn = null;
+ }
+}
+/*
+ * Allows the Table to update internal structures it has that may contain the
+ * item being destroyed.
+ */
+void destroyItem (TableItem item) {
+ if (item == focusItem) reassignFocus ();
+
+ int index = item.index;
+ Rectangle bounds = item.getBounds (false);
+ int rightX = bounds.x + bounds.width;
+
+ if (index != itemsCount - 1) {
+ /* item is not at end of items list, so must shift items left to reclaim its slot */
+ System.arraycopy (items, index + 1, items, index, itemsCount - index - 1);
+ items [itemsCount - 1] = null;
+ } else {
+ items [index] = null; /* last item, so no array copy needed */
+ }
+ itemsCount--;
+
+ if (drawCount <= 0 && items.length - itemsCount == 4) {
+ /* shrink the items array */
+ TableItem[] newItems = new TableItem [itemsCount];
+ System.arraycopy (items, 0, newItems, 0, newItems.length);
+ items = newItems;
+ }
+
+ /* update the index on affected items */
+ for (int i = index; i < itemsCount; i++) {
+ items [i].index = i;
+ }
+ item.index = -1;
+
+ int oldTopIndex = topIndex;
+ updateVerticalBar ();
+ updateHorizontalBar (0, -rightX);
+ /*
+ * If destroyed item is above viewport then adjust topIndex and the vertical
+ * scrollbar so that the current viewport items will not change.
+ */
+ if (index < topIndex) {
+ topIndex = oldTopIndex - 1;
+ ScrollBar vBar = getVerticalBar ();
+ if (vBar != null) vBar.setSelection (topIndex);
+ }
+
+ /* selectedItems array */
+ if (item.isSelected ()) {
+ int selectionIndex = getSelectionIndex (item);
+ TableItem[] newSelectedItems = new TableItem [selectedItems.length - 1];
+ System.arraycopy (selectedItems, 0, newSelectedItems, 0, selectionIndex);
+ System.arraycopy (
+ selectedItems,
+ selectionIndex + 1,
+ newSelectedItems,
+ selectionIndex,
+ newSelectedItems.length - selectionIndex);
+ selectedItems = newSelectedItems;
+ }
+ if (item == anchorItem) anchorItem = null;
+ if (item == lastClickedItem) lastClickedItem = null;
+ /*
+ * If this was the last item and the receiver has focus then its boundary
+ * focus ring must be redrawn.
+ */
+ if (itemsCount == 0 && isFocusControl ()) {
+ redraw ();
+ return;
+ }
+}
+Image getArrowDownImage () {
+ return (Image) display.getData (ID_ARROWDOWN);
+}
+Image getArrowUpImage () {
+ return (Image) display.getData (ID_ARROWUP);
+}
+int getCellPadding () {
+ return MARGIN_CELL + WIDTH_CELL_HIGHLIGHT;
+}
+Image getCheckmarkImage () {
+ return (Image) display.getData (ID_CHECKMARK);
+}
+public Control[] getChildren () {
+ checkWidget ();
+ Control[] controls = _getChildren ();
+ if (header == null) return controls;
+ Control[] result = new Control [controls.length - 1];
+ /* remove the Header from the returned set of children */
+ int index = 0;
+ for (int i = 0; i < controls.length; i++) {
+ if (controls [i] != header) {
+ result [index++] = controls [i];
+ }
+ }
+ return result;
+}
+/**
+ * Returns the column at the given, zero-relative index in the
+ * receiver. Throws an exception if the index is out of range.
+ * Columns are returned in the order that they were created.
+ * If no <code>TableColumn</code>s were created by the programmer,
+ * this method will throw <code>ERROR_INVALID_RANGE</code> despite
+ * the fact that a single column of data may be visible in the table.
+ * This occurs when the programmer uses the table like a list, adding
+ * items but never creating a column.
+ *
+ * @param index the index of the column to return
+ * @return the column at the given index
+ *
+ * @exception IllegalArgumentException <ul>
+ * <li>ERROR_INVALID_RANGE - if the index is not between 0 and the number of elements in the list minus 1 (inclusive)</li>
+ * </ul>
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @see Table#getColumnOrder()
+ * @see Table#setColumnOrder(int[])
+ * @see TableColumn#getMoveable()
+ * @see TableColumn#setMoveable(boolean)
+ * @see SWT#Move
+ */
+public TableColumn getColumn (int index) {
+ checkWidget ();
+ if (!(0 <= index && index < columns.length)) error (SWT.ERROR_INVALID_RANGE);
+ return columns [index];
+}
+/**
+ * Returns the number of columns contained in the receiver.
+ * If no <code>TableColumn</code>s were created by the programmer,
+ * this value is zero, despite the fact that visually, one column
+ * of items may be visible. This occurs when the programmer uses
+ * the table like a list, adding items but never creating a column.
+ *
+ * @return the number of columns
+ *
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public int getColumnCount () {
+ checkWidget ();
+ return columns.length;
+}
+/**
+ * Returns an array of zero-relative integers that map
+ * the creation order of the receiver's items to the
+ * order in which they are currently being displayed.
+ * <p>
+ * Specifically, the indices of the returned array represent
+ * the current visual order of the items, and the contents
+ * of the array represent the creation order of the items.
+ * </p><p>
+ * Note: This is not the actual structure used by the receiver
+ * to maintain its list of items, so modifying the array will
+ * not affect the receiver.
+ * </p>
+ *
+ * @return the current visual order of the receiver's items
+ *
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @see Table#setColumnOrder(int[])
+ * @see TableColumn#getMoveable()
+ * @see TableColumn#setMoveable(boolean)
+ * @see SWT#Move
+ *
+ * @since 3.1
+ */
+public int[] getColumnOrder () {
+ checkWidget ();
+ int[] result = new int [columns.length];
+ if (orderedColumns != null) {
+ for (int i = 0; i < result.length; i++) {
+ result [i] = orderedColumns [i].getIndex ();
+ }
+ } else {
+ for (int i = 0; i < columns.length; i++) {
+ result [i] = i;
+ }
+ }
+ return result;
+}
+/**
+ * Returns an array of <code>TableColumn</code>s which are the
+ * columns in the receiver. Columns are returned in the order
+ * that they were created. If no <code>TableColumn</code>s were
+ * created by the programmer, the array is empty, despite the fact
+ * that visually, one column of items may be visible. This occurs
+ * when the programmer uses the table like a list, adding items but
+ * never creating a column.
+ * <p>
+ * Note: This is not the actual structure used by the receiver
+ * to maintain its list of items, so modifying the array will
+ * not affect the receiver.
+ * </p>
+ *
+ * @return the items in the receiver
+ *
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @see Table#getColumnOrder()
+ * @see Table#setColumnOrder(int[])
+ * @see TableColumn#getMoveable()
+ * @see TableColumn#setMoveable(boolean)
+ * @see SWT#Move
+ */
+public TableColumn[] getColumns () {
+ checkWidget ();
+ TableColumn[] result = new TableColumn [columns.length];
+ System.arraycopy (columns, 0, result, 0, columns.length);
+ return result;
+}
+Image getGrayUncheckedImage () {
+ return (Image) display.getData (ID_GRAYUNCHECKED);
+}
+/**
+ * Returns the width in pixels of a grid line.
+ *
+ * @return the width of a grid line in pixels
+ *
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public int getGridLineWidth () {
+ checkWidget ();
+ return 1;
+}
+/**
+ * Returns the height of the receiver's header
+ *
+ * @return the height of the header or zero if the header is not visible
+ *
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @since 2.0
+ */
+public int getHeaderHeight () {
+ checkWidget ();
+ if (!header.getVisible ()) return 0;
+ return header.getSize ().y;
+}
+int getHeaderPadding () {
+ return MARGIN_CELL + WIDTH_HEADER_SHADOW;
+}
+/**
+ * Returns <code>true</code> if the receiver's header is visible,
+ * and <code>false</code> otherwise.
+ * <p>
+ * If one of the receiver's ancestors is not visible or some
+ * other condition makes the receiver not visible, this method
+ * may still indicate that it is considered visible even though
+ * it may not actually be showing.
+ * </p>
+ *
+ * @return the receiver's header's visibility state
+ *
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public boolean getHeaderVisible () {
+ checkWidget ();
+ return header.getVisible ();
+}
+/**
+ * Returns the item at the given, zero-relative index in the
+ * receiver. Throws an exception if the index is out of range.
+ *
+ * @param index the index of the item to return
+ * @return the item at the given index
+ *
+ * @exception IllegalArgumentException <ul>
+ * <li>ERROR_INVALID_RANGE - if the index is not between 0 and the number of elements in the list minus 1 (inclusive)</li>
+ * </ul>
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public TableItem getItem (int index) {
+ checkWidget ();
+ if (!(0 <= index && index < itemsCount)) error (SWT.ERROR_INVALID_RANGE);
+ return items [index];
+}
+/**
+ * Returns the item at the given point in the receiver
+ * or null if no such item exists. The point is in the
+ * coordinate system of the receiver.
+ * <p>
+ * The item that is returned represents an item that could be selected by the user.
+ * For example, if selection only occurs in items in the first column, then null is
+ * returned if the point is outside of the item.
+ * Note that the SWT.FULL_SELECTION style hint, which specifies the selection policy,
+ * determines the extent of the selection.
+ * </p>
+ *
+ * @param point the point used to locate the item
+ * @return the item at the given point, or null if the point is not in a selectable item
+ *
+ * @exception IllegalArgumentException <ul>
+ * <li>ERROR_NULL_ARGUMENT - if the point is null</li>
+ * </ul>
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public TableItem getItem (Point point) {
+ checkWidget ();
+ if (point == null) error (SWT.ERROR_NULL_ARGUMENT);
+ int index = (point.y - getHeaderHeight ()) / itemHeight + topIndex;
+ if (!(0 <= index && index < itemsCount)) return null; /* below the last item */
+ TableItem result = items [index];
+ if (!result.getHitBounds ().contains (point)) return null; /* considers the x value */
+ return result;
+}
+/**
+ * Returns the number of items contained in the receiver.
+ *
+ * @return the number of items
+ *
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public int getItemCount () {
+ checkWidget ();
+ return itemsCount;
+}
+/**
+ * Returns the height of the area which would be used to
+ * display <em>one</em> of the items in the receiver.
+ *
+ * @return the height of one item
+ *
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public int getItemHeight () {
+ checkWidget ();
+ return itemHeight;
+}
+/**
+ * Returns a (possibly empty) array of <code>TableItem</code>s which
+ * are the items in the receiver.
+ * <p>
+ * Note: This is not the actual structure used by the receiver
+ * to maintain its list of items, so modifying the array will
+ * not affect the receiver.
+ * </p>
+ *
+ * @return the items in the receiver
+ *
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public TableItem[] getItems () {
+ checkWidget ();
+ TableItem[] result = new TableItem [itemsCount];
+ System.arraycopy (items, 0, result, 0, itemsCount);
+ return result;
+}
+/*
+ * Returns the current y-coordinate that the specified item should have.
+ */
+int getItemY (TableItem item) {
+ return (item.index - topIndex) * itemHeight + getHeaderHeight ();
+}
+/**
+ * Returns <code>true</code> if the receiver's lines are visible,
+ * and <code>false</code> otherwise. Note that some platforms draw
+ * grid lines while others may draw alternating row colors.
+ * <p>
+ * If one of the receiver's ancestors is not visible or some
+ * other condition makes the receiver not visible, this method
+ * may still indicate that it is considered visible even though
+ * it may not actually be showing.
+ * </p>
+ *
+ * @return the visibility state of the lines
+ *
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public boolean getLinesVisible () {
+ checkWidget ();
+ return linesVisible;
+}
+TableColumn[] getOrderedColumns () {
+ if (orderedColumns != null) return orderedColumns;
+ return columns;
+}
+/**
+ * Returns an array of <code>TableItem</code>s that are currently
+ * selected in the receiver. The order of the items is unspecified.
+ * An empty array indicates that no items are selected.
+ * <p>
+ * Note: This is not the actual structure used by the receiver
+ * to maintain its selection, so modifying the array will
+ * not affect the receiver.
+ * </p>
+ * @return an array representing the selection
+ *
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public TableItem[] getSelection () {
+ checkWidget ();
+ TableItem[] result = new TableItem [selectedItems.length];
+ System.arraycopy (selectedItems, 0, result, 0, selectedItems.length);
+ sortAscent (result);
+ return result;
+}
+/**
+ * Returns the number of selected items contained in the receiver.
+ *
+ * @return the number of selected items
+ *
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public int getSelectionCount () {
+ checkWidget ();
+ return selectedItems.length;
+}
+/**
+ * Returns the zero-relative index of the item which is currently
+ * selected in the receiver, or -1 if no item is selected.
+ *
+ * @return the index of the selected item
+ *
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public int getSelectionIndex () {
+ checkWidget ();
+ if (selectedItems.length == 0) return -1;
+ return selectedItems [0].index;
+}
+/*
+ * Returns the index of the argument in the receiver's array of currently-
+ * selected items, or -1 if the item is not currently selected.
+ */
+int getSelectionIndex (TableItem item) {
+ for (int i = 0; i < selectedItems.length; i++) {
+ if (selectedItems [i] == item) return i;
+ }
+ return -1;
+}
+/**
+ * Returns the zero-relative indices of the items which are currently
+ * selected in the receiver. The order of the indices is unspecified.
+ * The array is empty if no items are selected.
+ * <p>
+ * Note: This is not the actual structure used by the receiver
+ * to maintain its selection, so modifying the array will
+ * not affect the receiver.
+ * </p>
+ * @return the array of indices of the selected items
+ *
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public int [] getSelectionIndices () {
+ checkWidget ();
+ int[] result = new int [selectedItems.length];
+ for (int i = 0; i < selectedItems.length; i++) {
+ result [i] = selectedItems [i].index;
+ }
+ sortAscent (result);
+ return result;
+}
+/**
+ * Returns the column which shows the sort indicator for
+ * the receiver. The value may be null if no column shows
+ * the sort indicator.
+ *
+ * @return the sort indicator
+ *
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @see #setSortColumn(TableColumn)
+ *
+ * @since 3.2
+ */
+public TableColumn getSortColumn () {
+ checkWidget ();
+ return sortColumn;
+}
+/**
+ * Returns the direction of the sort indicator for the receiver.
+ * The value will be one of <code>UP</code>, <code>DOWN</code>
+ * or <code>NONE</code>.
+ *
+ * @return the sort direction
+ *
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @see #setSortDirection(int)
+ *
+ * @since 3.2
+ */
+public int getSortDirection () {
+ checkWidget ();
+ return sortDirection;
+}
+/**
+ * Returns the zero-relative index of the item which is currently
+ * at the top of the receiver. This index can change when items are
+ * scrolled or new items are added or removed.
+ *
+ * @return the index of the top item
+ *
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public int getTopIndex () {
+ checkWidget ();
+ return topIndex;
+}
+Image getUncheckedImage () {
+ return (Image) display.getData (ID_UNCHECKED);
+}
+void handleEvents (Event event) {
+ switch (event.type) {
+ case SWT.Paint:
+ if (event.widget == header) {
+ headerOnPaint (event);
+ } else {
+ onPaint (event);
+ }
+ break;
+ case SWT.MenuDetect: {
+ notifyListeners (SWT.MenuDetect, event);
+ break;
+ }
+ case SWT.MouseDown:
+ if (event.widget == header) {
+ headerOnMouseDown (event);
+ } else {
+ onMouseDown (event);
+ }
+ break;
+ case SWT.MouseUp:
+ if (event.widget == header) {
+ headerOnMouseUp (event);
+ } else {
+ onMouseUp (event);
+ }
+ break;
+ case SWT.MouseHover:
+ headerOnMouseHover (event); break;
+ case SWT.MouseMove:
+ headerOnMouseMove (event); break;
+ case SWT.MouseDoubleClick:
+ if (event.widget == header) {
+ headerOnMouseDoubleClick (event);
+ } else {
+ onMouseDoubleClick (event);
+ }
+ break;
+ case SWT.MouseExit:
+ headerOnMouseExit (); break;
+ case SWT.Dispose:
+ onDispose (event); break;
+ case SWT.KeyDown:
+ onKeyDown (event); break;
+ case SWT.Resize:
+ onResize (event); break;
+ case SWT.Selection:
+ if (event.widget == getHorizontalBar ()) {
+ onScrollHorizontal (event);
+ }
+ if (event.widget == getVerticalBar ()) {
+ onScrollVertical (event);
+ }
+ break;
+ case SWT.FocusOut:
+ onFocusOut (); break;
+ case SWT.FocusIn:
+ onFocusIn (); break;
+ case SWT.Traverse:
+ switch (event.detail) {
+ case SWT.TRAVERSE_ESCAPE:
+ case SWT.TRAVERSE_RETURN:
+ case SWT.TRAVERSE_TAB_NEXT:
+ case SWT.TRAVERSE_TAB_PREVIOUS:
+ case SWT.TRAVERSE_PAGE_NEXT:
+ case SWT.TRAVERSE_PAGE_PREVIOUS:
+ event.doit = true;
+ break;
+ }
+ break;
+ }
+}
+String headerGetToolTip (int x) {
+ if (resizeColumn != null) return null;
+ int orderedIndex = computeColumnIntersect (x, 0);
+ if (orderedIndex == -1) return null;
+ TableColumn[] orderedColumns = getOrderedColumns ();
+ TableColumn column = orderedColumns [orderedIndex];
+ if (column.toolTipText == null) return null;
+
+ /* no tooltip should appear if the hover is at a column resize opportunity */
+ int columnX = column.getX ();
+ if (orderedIndex > 0 && orderedColumns [orderedIndex - 1].resizable) {
+ /* left column bound is resizable */
+ if (x - columnX <= TOLLERANCE_COLUMNRESIZE) return null;
+ }
+ if (column.resizable) {
+ /* right column bound is resizable */
+ int columnRightX = columnX + column.width;
+ if (columnRightX - x <= TOLLERANCE_COLUMNRESIZE) return null;
+ }
+ return removeMnemonics (column.toolTipText);
+}
+void headerHideToolTip() {
+ if (toolTipShell == null) return;
+ for (int i = 0; i < toolTipEvents.length; i++) {
+ header.removeListener (toolTipEvents [i], toolTipListener);
+ }
+ toolTipShell.dispose ();
+ toolTipShell = null;
+ toolTipLabel = null;
+}
+void headerOnMouseDoubleClick (Event event) {
+ if (!isFocusControl ()) setFocus ();
+ if (columns.length == 0) return;
+ TableColumn[] orderedColumns = getOrderedColumns ();
+ int x = -horizontalOffset;
+ for (int i = 0; i < orderedColumns.length; i++) {
+ TableColumn column = orderedColumns [i];
+ x += column.width;
+ if (event.x < x) {
+ /* found the clicked column */
+ TableColumn packColumn = null;
+ if (x - event.x <= TOLLERANCE_COLUMNRESIZE) {
+ /* clicked on column bound for this column */
+ packColumn = column;
+ } else {
+ if (i > 0 && event.x - column.getX () <= TOLLERANCE_COLUMNRESIZE) {
+ /* clicked on column bound that applies to previous column */
+ packColumn = orderedColumns [i - 1];
+ }
+ }
+ if (packColumn != null) {
+ packColumn.pack ();
+ resizeColumn = null;
+ if (Math.abs (packColumn.getX () + packColumn.width - event.x) > TOLLERANCE_COLUMNRESIZE) {
+ /* column separator has relocated away from pointer location */
+ setCursor (null);
+ }
+ return;
+ }
+ /* did not click on column separator, so just fire column event */
+ Event newEvent = new Event ();
+ newEvent.widget = column;
+ column.postEvent (SWT.DefaultSelection, newEvent);
+ return;
+ }
+ }
+}
+void headerOnMouseDown (Event event) {
+ if (event.button != 1) return;
+ TableColumn[] orderedColumns = getOrderedColumns ();
+ int x = -horizontalOffset;
+ for (int i = 0; i < orderedColumns.length; i++) {
+ TableColumn column = orderedColumns [i];
+ x += column.width;
+ /* if close to a resizable column separator line then begin column resize */
+ if (column.resizable && Math.abs (x - event.x) <= TOLLERANCE_COLUMNRESIZE) {
+ resizeColumn = column;
+ resizeColumnX = x;
+ return;
+ }
+ /*
+ * If within column but not near separator line then start column drag
+ * if column is moveable, or just fire column Selection otherwise.
+ */
+ if (event.x < x) {
+ if (column.moveable) {
+ /* open tracker on the dragged column's header cell */
+ int columnX = column.getX ();
+ int pointerOffset = event.x - columnX;
+ headerHideToolTip ();
+ Tracker tracker = new Tracker (this, SWT.NONE);
+ tracker.setRectangles (new Rectangle[] {
+ new Rectangle (columnX, 0, column.width, getHeaderHeight ())
+ });
+ if (!tracker.open ()) return; /* cancelled */
+ /* determine which column was dragged onto */
+ Rectangle result = tracker.getRectangles () [0];
+ int pointerX = result.x + pointerOffset;
+ if (pointerX < 0) return; /* dragged too far left */
+ x = -horizontalOffset;
+ for (int destIndex = 0; destIndex < orderedColumns.length; destIndex++) {
+ TableColumn destColumn = orderedColumns [destIndex];
+ x += destColumn.width;
+ if (pointerX < x) {
+ int oldIndex = column.getOrderIndex ();
+ if (destIndex == oldIndex) { /* dragged onto self */
+ Event newEvent = new Event ();
+ newEvent.widget = column;
+ column.postEvent (SWT.Selection, newEvent);
+ return;
+ }
+ int leftmostIndex = Math.min (destIndex, oldIndex);
+ int[] oldOrder = getColumnOrder ();
+ int[] newOrder = new int [oldOrder.length];
+ System.arraycopy (oldOrder, 0, newOrder, 0, leftmostIndex);
+ if (leftmostIndex == oldIndex) {
+ /* column moving to the right */
+ System.arraycopy (oldOrder, oldIndex + 1, newOrder, oldIndex, destIndex - oldIndex);
+ } else {
+ /* column moving to the left */
+ System.arraycopy (oldOrder, destIndex, newOrder, destIndex + 1, oldIndex - destIndex);
+ }
+ newOrder [destIndex] = oldOrder [oldIndex];
+ int rightmostIndex = Math.max (destIndex, oldIndex);
+ System.arraycopy (
+ oldOrder,
+ rightmostIndex + 1,
+ newOrder,
+ rightmostIndex + 1,
+ newOrder.length - rightmostIndex - 1);
+ setColumnOrder (newOrder);
+ return;
+ }
+ }
+ return; /* dragged too far right */
+ }
+ /* column is not moveable */
+ Event newEvent = new Event ();
+ newEvent.widget = column;
+ column.postEvent (SWT.Selection, newEvent);
+ return;
+ }
+ }
+}
+void headerOnMouseExit () {
+ if (resizeColumn != null) return;
+ setCursor (null); /* ensure that a column resize cursor does not escape */
+}
+void headerOnMouseHover (Event event) {
+ headerShowToolTip (event.x);
+}
+void headerOnMouseMove (Event event) {
+ if (resizeColumn == null) {
+ /* not currently resizing a column */
+ for (int i = 0; i < columns.length; i++) {
+ TableColumn column = columns [i];
+ int x = column.getX () + column.width;
+ if (Math.abs (x - event.x) <= TOLLERANCE_COLUMNRESIZE) {
+ if (column.resizable) {
+ setCursor (display.getSystemCursor (SWT.CURSOR_SIZEWE));
+ } else {
+ setCursor (null);
+ }
+ return;
+ }
+ }
+ setCursor (null);
+ return;
+ }
+
+ /* currently resizing a column */
+
+ /* don't allow the resize x to move left of the column's x position */
+ if (event.x <= resizeColumn.getX ()) return;
+
+ /* redraw the resizing line at its new location */
+ GC gc = new GC (this);
+ gc.setForeground (display.getSystemColor (SWT.COLOR_BLACK));
+ int lineHeight = clientArea.height;
+ redraw (resizeColumnX - 1, 0, 1, lineHeight, false);
+ resizeColumnX = event.x;
+ gc.drawLine (resizeColumnX - 1, 0, resizeColumnX - 1, lineHeight);
+ gc.dispose ();
+}
+void headerOnMouseUp (Event event) {
+ if (resizeColumn == null) return; /* not resizing a column */
+
+ /* remove the resize line */
+ GC gc = new GC (this);
+ redraw (resizeColumnX - 1, 0, 1, clientArea.height, false);
+ gc.dispose ();
+
+ int newWidth = resizeColumnX - resizeColumn.getX ();
+ if (newWidth != resizeColumn.width) {
+ setCursor (null);
+ updateColumnWidth (resizeColumn, newWidth);
+ }
+ resizeColumnX = -1;
+ resizeColumn = null;
+}
+void headerOnPaint (Event event) {
+ TableColumn[] orderedColumns = getOrderedColumns ();
+ int numColumns = orderedColumns.length;
+ GC gc = event.gc;
+ Rectangle clipping = gc.getClipping ();
+ int startColumn = -1, endColumn = -1;
+ if (numColumns > 0) {
+ startColumn = computeColumnIntersect (clipping.x, 0);
+ if (startColumn != -1) { /* the clip x is within a column's bounds */
+ endColumn = computeColumnIntersect (clipping.x + clipping.width, startColumn);
+ if (endColumn == -1) endColumn = numColumns - 1;
+ }
+ } else {
+ startColumn = endColumn = 0;
+ }
+
+ /* paint the column header shadow that spans the full header width */
+ Point headerSize = header.getSize ();
+ headerPaintHShadows (gc, 0, 0, headerSize.x, headerSize.y);
+
+ /* if all damage is to the right of the last column then finished */
+ if (startColumn == -1) return;
+
+ /* paint each of the column headers */
+ if (numColumns == 0) return; /* no headers to paint */
+ for (int i = startColumn; i <= endColumn; i++) {
+ headerPaintVShadows (gc, orderedColumns [i].getX (), 0, orderedColumns [i].width, headerSize.y);
+ orderedColumns [i].paint (gc);
+ }
+}
+void headerPaintHShadows (GC gc, int x, int y, int width, int height) {
+ gc.setClipping (x, y, width, height);
+ int endX = x + width;
+ gc.setForeground (display.getSystemColor (SWT.COLOR_WIDGET_HIGHLIGHT_SHADOW));
+ gc.drawLine (x, y, endX, y); /* highlight shadow */
+ gc.setForeground (display.getSystemColor (SWT.COLOR_WIDGET_NORMAL_SHADOW));
+ gc.drawLine (x, height - 2, endX, height - 2); /* lowlight shadow */
+ gc.setForeground (display.getSystemColor (SWT.COLOR_WIDGET_DARK_SHADOW));
+ gc.drawLine (x, height - 1, endX, height - 1); /* outer shadow */
+}
+void headerPaintVShadows (GC gc, int x, int y, int width, int height) {
+ gc.setClipping (x, y, width, height);
+ int endX = x + width;
+ gc.setForeground (display.getSystemColor (SWT.COLOR_WIDGET_HIGHLIGHT_SHADOW));
+ gc.drawLine (x, y, x, y + height - 1); /* highlight shadow */
+ gc.setForeground (display.getSystemColor (SWT.COLOR_WIDGET_NORMAL_SHADOW));
+ gc.drawLine (endX - 2, y + 1, endX - 2, height - 2); /* light inner shadow */
+ gc.setForeground (display.getSystemColor (SWT.COLOR_WIDGET_DARK_SHADOW));
+ gc.drawLine (endX - 1, y, endX - 1, height - 1); /* dark outer shadow */
+}
+void headerShowToolTip (int x) {
+ String tooltip = headerGetToolTip (x);
+ if (tooltip == null || tooltip.length () == 0) return;
+
+ if (toolTipShell == null) {
+ toolTipShell = new Shell (getShell (), SWT.ON_TOP | SWT.TOOL);
+ toolTipLabel = new Label (toolTipShell, SWT.CENTER);
+ Display display = toolTipShell.getDisplay ();
+ toolTipLabel.setForeground (display.getSystemColor (SWT.COLOR_INFO_FOREGROUND));
+ toolTipLabel.setBackground (display.getSystemColor (SWT.COLOR_INFO_BACKGROUND));
+ for (int i = 0; i < toolTipEvents.length; i++) {
+ header.addListener (toolTipEvents [i], toolTipListener);
+ }
+ }
+ if (headerUpdateToolTip (x)) {
+ toolTipShell.setVisible (true);
+ } else {
+ headerHideToolTip ();
+ }
+}
+boolean headerUpdateToolTip (int x) {
+ String tooltip = headerGetToolTip (x);
+ if (tooltip == null || tooltip.length () == 0) return false;
+ if (tooltip.equals (toolTipLabel.getText ())) return true;
+
+ toolTipLabel.setText (tooltip);
+ TableColumn column = getOrderedColumns () [computeColumnIntersect (x, 0)];
+ toolTipShell.setData (new Integer (column.getIndex ()));
+ Point labelSize = toolTipLabel.computeSize (SWT.DEFAULT, SWT.DEFAULT, true);
+ labelSize.x += 2; labelSize.y += 2;
+ toolTipLabel.setSize (labelSize);
+ toolTipShell.pack ();
+ /*
+ * On some platforms, there is a minimum size for a shell
+ * which may be greater than the label size.
+ * To avoid having the background of the tip shell showing
+ * around the label, force the label to fill the entire client area.
+ */
+ Rectangle area = toolTipShell.getClientArea ();
+ toolTipLabel.setSize (area.width, area.height);
+
+ /* Position the tooltip and ensure it's not located off the screen */
+ Point cursorLocation = getDisplay ().getCursorLocation ();
+ int cursorHeight = 21; /* assuming cursor is 21x21 */
+ Point size = toolTipShell.getSize ();
+ Rectangle rect = getMonitor ().getBounds ();
+ Point pt = new Point (cursorLocation.x, cursorLocation.y + cursorHeight + 2);
+ pt.x = Math.max (pt.x, rect.x);
+ if (pt.x + size.x > rect.x + rect.width) pt.x = rect.x + rect.width - size.x;
+ if (pt.y + size.y > rect.y + rect.height) pt.y = cursorLocation.y - 2 - size.y;
+ toolTipShell.setLocation (pt);
+ return true;
+}
+/**
+ * Searches the receiver's list starting at the first column
+ * (index 0) until a column is found that is equal to the
+ * argument, and returns the index of that column. If no column
+ * is found, returns -1.
+ *
+ * @param column the search column
+ * @return the index of the column
+ *
+ * @exception IllegalArgumentException <ul>
+ * <li>ERROR_NULL_ARGUMENT - if the column is null</li>
+ * </ul>
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public int indexOf (TableColumn column) {
+ checkWidget ();
+ if (column == null) error (SWT.ERROR_NULL_ARGUMENT);
+ if (column.parent != this) return -1;
+ return column.getIndex ();
+}
+/**
+ * Searches the receiver's list starting at the first item
+ * (index 0) until an item is found that is equal to the
+ * argument, and returns the index of that item. If no item
+ * is found, returns -1.
+ *
+ * @param item the search item
+ * @return the index of the item
+ *
+ * @exception IllegalArgumentException <ul>
+ * <li>ERROR_NULL_ARGUMENT - if the item is null</li>
+ * </ul>
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public int indexOf (TableItem item) {
+ checkWidget ();
+ if (item == null) error (SWT.ERROR_NULL_ARGUMENT);
+ if (item.parent != this) return -1;
+ return item.index;
+}
+static void initImages (final Display display) {
+ PaletteData arrowPalette = new PaletteData (new RGB[] {
+ new RGB (0, 0, 0), new RGB (255, 255, 255)});
+ if (display.getData (ID_ARROWDOWN) == null) {
+ ImageData arrowDown = new ImageData (
+ 7, 4, 1,
+ arrowPalette, 1,
+ new byte[] {0x00, (byte)0x83, (byte)0xC7, (byte)0xEF});
+ arrowDown.transparentPixel = 0x1; /* use white for transparency */
+ display.setData (ID_ARROWDOWN, new Image (display, arrowDown));
+ }
+ if (display.getData (ID_ARROWUP) == null) {
+ ImageData arrowUp = new ImageData (
+ 7, 4, 1,
+ arrowPalette, 1,
+ new byte[] {(byte)0xEF, (byte)0xC7, (byte)0x83, 0x00});
+ arrowUp.transparentPixel = 0x1; /* use white for transparency */
+ display.setData (ID_ARROWUP, new Image (display, arrowUp));
+ }
+
+ PaletteData checkMarkPalette = new PaletteData (
+ new RGB[] {new RGB (0, 0, 0), new RGB (252, 3, 251)});
+ byte[] checkbox = new byte[] {0, 0, 127, -64, 127, -64, 127, -64, 127, -64, 127, -64, 127, -64, 127, -64, 127, -64, 127, -64, 0, 0};
+ ImageData checkmark = new ImageData (7, 7, 1, checkMarkPalette, 1, new byte[] {-4, -8, 112, 34, 6, -114, -34});
+ checkmark.transparentPixel = 1;
+ if (display.getData (ID_CHECKMARK) == null) {
+ display.setData (ID_CHECKMARK, new Image (display, checkmark));
+ }
+
+ if (display.getData (ID_UNCHECKED) == null) {
+ PaletteData uncheckedPalette = new PaletteData (
+ new RGB[] {new RGB (128, 128, 128), new RGB (255, 255, 255)});
+ ImageData unchecked = new ImageData (11, 11, 1, uncheckedPalette, 2, checkbox);
+ display.setData (ID_UNCHECKED, new Image (display, unchecked));
+ }
+
+ if (display.getData (ID_GRAYUNCHECKED) == null) {
+ PaletteData grayUncheckedPalette = new PaletteData (
+ new RGB[] {new RGB (128, 128, 128), new RGB (192, 192, 192)});
+ ImageData grayUnchecked = new ImageData (11, 11, 1, grayUncheckedPalette, 2, checkbox);
+ display.setData (ID_GRAYUNCHECKED, new Image (display, grayUnchecked));
+ }
+
+ display.disposeExec (new Runnable () {
+ public void run() {
+ Image unchecked = (Image) display.getData (ID_UNCHECKED);
+ if (unchecked != null) unchecked.dispose ();
+ Image grayUnchecked = (Image) display.getData (ID_GRAYUNCHECKED);
+ if (grayUnchecked != null) grayUnchecked.dispose ();
+ Image checkmark = (Image) display.getData (ID_CHECKMARK);
+ if (checkmark != null) checkmark.dispose ();
+ Image arrowDown = (Image) display.getData (ID_ARROWDOWN);
+ if (arrowDown != null) arrowDown.dispose ();
+ Image arrowUp = (Image) display.getData (ID_ARROWUP);
+ if (arrowUp != null) arrowUp.dispose ();
+
+ display.setData (ID_UNCHECKED, null);
+ display.setData (ID_GRAYUNCHECKED, null);
+ display.setData (ID_CHECKMARK, null);
+ display.setData (ID_ARROWDOWN, null);
+ display.setData (ID_ARROWUP, null);
+ }
+ });
+}
+/**
+ * Returns <code>true</code> if the item is selected,
+ * and <code>false</code> otherwise. Indices out of
+ * range are ignored.
+ *
+ * @param index the index of the item
+ * @return the selection state of the item at the index
+ *
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public boolean isSelected (int index) {
+ checkWidget ();
+ if (!(0 <= index && index < itemsCount)) return false;
+ return items [index].isSelected ();
+}
+void onArrowDown (int stateMask) {
+ if ((stateMask & (SWT.SHIFT | SWT.CTRL)) == 0) {
+ /* Down Arrow with no modifiers */
+ int newFocusIndex = focusItem.index + 1;
+ if (newFocusIndex == itemsCount) return; /* at bottom */
+ selectItem (items [newFocusIndex], false);
+ setFocusItem (items [newFocusIndex], true);
+ redrawItem (newFocusIndex, true);
+ showItem (items [newFocusIndex]);
+ Event newEvent = new Event ();
+ newEvent.item = items [newFocusIndex];
+ postEvent (SWT.Selection, newEvent);
+ return;
+ }
+ if ((style & SWT.SINGLE) != 0) {
+ if ((stateMask & SWT.CTRL) != 0) {
+ /* CTRL+Down Arrow, CTRL+Shift+Down Arrow */
+ int visibleItemCount = (clientArea.height - getHeaderHeight ()) / itemHeight;
+ if (itemsCount <= topIndex + visibleItemCount) return; /* at bottom */
+ update ();
+ topIndex++;
+ ScrollBar vBar = getVerticalBar ();
+ if (vBar != null) vBar.setSelection (topIndex);
+ GC gc = new GC (this);
+ gc.copyArea (
+ 0, 0,
+ clientArea.width, clientArea.height,
+ 0, -itemHeight);
+ gc.dispose ();
+ return;
+ }
+ /* Shift+Down Arrow */
+ int newFocusIndex = focusItem.index + 1;
+ if (newFocusIndex == itemsCount) return; /* at bottom */
+ selectItem (items [newFocusIndex], false);
+ setFocusItem (items [newFocusIndex], true);
+ redrawItem (newFocusIndex, true);
+ showItem (items [newFocusIndex]);
+ Event newEvent = new Event ();
+ newEvent.item = items [newFocusIndex];
+ postEvent (SWT.Selection, newEvent);
+ return;
+ }
+ /* SWT.MULTI */
+ if ((stateMask & SWT.CTRL) != 0) {
+ if ((stateMask & SWT.SHIFT) != 0) {
+ /* CTRL+Shift+Down Arrow */
+ int visibleItemCount = (clientArea.height - getHeaderHeight ()) / itemHeight;
+ if (itemsCount <= topIndex + visibleItemCount) return; /* at bottom */
+ update ();
+ topIndex++;
+ ScrollBar vBar = getVerticalBar ();
+ if (vBar != null) vBar.setSelection (topIndex);
+ GC gc = new GC (this);
+ gc.copyArea (
+ 0, 0,
+ clientArea.width, clientArea.height,
+ 0, -itemHeight);
+ gc.dispose ();
+ return;
+ }
+ /* CTRL+Down Arrow */
+ int focusIndex = focusItem.index;
+ if (focusIndex == itemsCount - 1) return; /* at bottom */
+ TableItem newFocusItem = items [focusIndex + 1];
+ setFocusItem (newFocusItem, true);
+ redrawItem (newFocusItem.index, true);
+ showItem (newFocusItem);
+ return;
+ }
+ /* Shift+Down Arrow */
+ int newFocusIndex = focusItem.index + 1;
+ if (newFocusIndex == itemsCount) return; /* at bottom */
+ if (anchorItem == null) anchorItem = focusItem;
+ if (focusItem.index < anchorItem.index) {
+ deselectItem (focusItem);
+ redrawItem (focusItem.index, true);
+ }
+ selectItem (items [newFocusIndex], true);
+ setFocusItem (items [newFocusIndex], true);
+ redrawItem (newFocusIndex, true);
+ showItem (items [newFocusIndex]);
+ Event newEvent = new Event ();
+ newEvent.item = items [newFocusIndex];
+ postEvent (SWT.Selection, newEvent);
+}
+void onArrowLeft (int stateMask) {
+ if (horizontalOffset == 0) return;
+ int newSelection = Math.max (0, horizontalOffset - SIZE_HORIZONTALSCROLL);
+ update ();
+ GC gc = new GC (this);
+ gc.copyArea (
+ 0, 0,
+ clientArea.width, clientArea.height,
+ horizontalOffset - newSelection, 0);
+ gc.dispose ();
+ if (header.getVisible ()) {
+ header.update ();
+ Rectangle headerClientArea = header.getClientArea ();
+ gc = new GC (header);
+ gc.copyArea (
+ 0, 0,
+ headerClientArea.width, headerClientArea.height,
+ horizontalOffset - newSelection, 0);
+ gc.dispose();
+ }
+ horizontalOffset = newSelection;
+ ScrollBar hBar = getHorizontalBar ();
+ if (hBar != null) hBar.setSelection (horizontalOffset);
+}
+void onArrowRight (int stateMask) {
+ ScrollBar hBar = getHorizontalBar ();
+ if (hBar == null) return;
+ int maximum = hBar.getMaximum ();
+ int clientWidth = clientArea.width;
+ if ((horizontalOffset + clientArea.width) == maximum) return;
+ if (maximum <= clientWidth) return;
+ int newSelection = Math.min (horizontalOffset + SIZE_HORIZONTALSCROLL, maximum - clientWidth);
+ update ();
+ GC gc = new GC (this);
+ gc.copyArea (
+ 0, 0,
+ clientArea.width, clientArea.height,
+ horizontalOffset - newSelection, 0);
+ gc.dispose ();
+ if (header.getVisible ()) {
+ Rectangle headerClientArea = header.getClientArea ();
+ header.update ();
+ gc = new GC (header);
+ gc.copyArea (
+ 0, 0,
+ headerClientArea.width, headerClientArea.height,
+ horizontalOffset - newSelection, 0);
+ gc.dispose();
+ }
+ horizontalOffset = newSelection;
+ hBar.setSelection (horizontalOffset);
+}
+void onArrowUp (int stateMask) {
+ if ((stateMask & (SWT.SHIFT | SWT.CTRL)) == 0) {
+ /* Up Arrow with no modifiers */
+ int newFocusIndex = focusItem.index - 1;
+ if (newFocusIndex < 0) return; /* at top */
+ TableItem item = items [newFocusIndex];
+ selectItem (item, false);
+ setFocusItem (item, true);
+ redrawItem (newFocusIndex, true);
+ showItem (item);
+ Event newEvent = new Event ();
+ newEvent.item = item;
+ postEvent (SWT.Selection, newEvent);
+ return;
+ }
+ if ((style & SWT.SINGLE) != 0) {
+ if ((stateMask & SWT.CTRL) != 0) {
+ /* CTRL+Up Arrow, CTRL+Shift+Up Arrow */
+ if (topIndex == 0) return; /* at top */
+ update ();
+ topIndex--;
+ ScrollBar vBar = getVerticalBar ();
+ if (vBar != null) vBar.setSelection (topIndex);
+ GC gc = new GC (this);
+ gc.copyArea (
+ 0, 0,
+ clientArea.width, clientArea.height,
+ 0, itemHeight);
+ gc.dispose ();
+ return;
+ }
+ /* Shift+Up Arrow */
+ int newFocusIndex = focusItem.index - 1;
+ if (newFocusIndex < 0) return; /* at top */
+ TableItem item = items [newFocusIndex];
+ selectItem (item, false);
+ setFocusItem (item, true);
+ redrawItem (newFocusIndex, true);
+ showItem (item);
+ Event newEvent = new Event ();
+ newEvent.item = item;
+ postEvent (SWT.Selection, newEvent);
+ return;
+ }
+ /* SWT.MULTI */
+ if ((stateMask & SWT.CTRL) != 0) {
+ if ((stateMask & SWT.SHIFT) != 0) {
+ /* CTRL+Shift+Up Arrow */
+ if (topIndex == 0) return; /* at top */
+ update ();
+ topIndex--;
+ ScrollBar vBar = getVerticalBar ();
+ if (vBar != null) vBar.setSelection (topIndex);
+ GC gc = new GC (this);
+ gc.copyArea (
+ 0, 0,
+ clientArea.width, clientArea.height,
+ 0, itemHeight);
+ gc.dispose ();
+ return;
+ }
+ /* CTRL+Up Arrow */
+ int focusIndex = focusItem.index;
+ if (focusIndex == 0) return; /* at top */
+ TableItem newFocusItem = items [focusIndex - 1];
+ setFocusItem (newFocusItem, true);
+ showItem (newFocusItem);
+ redrawItem (newFocusItem.index, true);
+ return;
+ }
+ /* Shift+Up Arrow */
+ int newFocusIndex = focusItem.index - 1;
+ if (newFocusIndex < 0) return; /* at top */
+ if (anchorItem == null) anchorItem = focusItem;
+ if (anchorItem.index < focusItem.index) {
+ deselectItem (focusItem);
+ redrawItem (focusItem.index, true);
+ }
+ TableItem item = items [newFocusIndex];
+ selectItem (item, true);
+ setFocusItem (item, true);
+ redrawItem (newFocusIndex, true);
+ showItem (item);
+ Event newEvent = new Event ();
+ newEvent.item = item;
+ postEvent (SWT.Selection, newEvent);
+}
+void onCR () {
+ if (focusItem == null) return;
+ Event event = new Event ();
+ event.item = focusItem;
+ postEvent (SWT.DefaultSelection, event);
+}
+void onDispose (Event event) {
+ if (isDisposed ()) return;
+ if (ignoreDispose) return;
+ ignoreDispose = true;
+ notifyListeners(SWT.Dispose, event);
+ event.type = SWT.None;
+ for (int i = 0; i < itemsCount; i++) {
+ items [i].dispose (false);
+ }
+ for (int i = 0; i < columns.length; i++) {
+ columns [i].dispose (false);
+ }
+ if (toolTipShell != null) {
+ toolTipShell.dispose ();
+ toolTipShell = null;
+ toolTipLabel = null;
+ }
+ toolTipListener = null;
+ itemsCount = topIndex = horizontalOffset = 0;
+ items = selectedItems = null;
+ columns = orderedColumns = null;
+ focusItem = anchorItem = lastClickedItem = null;
+ lastSelectionEvent = null;
+ header = null;
+ resizeColumn = sortColumn = null;
+}
+void onEnd (int stateMask) {
+ int lastAvailableIndex = itemsCount - 1;
+ if ((stateMask & (SWT.CTRL | SWT.SHIFT)) == 0) {
+ /* End with no modifiers */
+ if (focusItem.index == lastAvailableIndex) return; /* at bottom */
+ TableItem item = items [lastAvailableIndex];
+ selectItem (item, false);
+ setFocusItem (item, true);
+ redrawItem (lastAvailableIndex, true);
+ showItem (item);
+ Event newEvent = new Event ();
+ newEvent.item = item;
+ postEvent (SWT.Selection, newEvent);
+ return;
+ }
+ if ((style & SWT.SINGLE) != 0) {
+ if ((stateMask & SWT.CTRL) != 0) {
+ /* CTRL+End, CTRL+Shift+End */
+ int visibleItemCount = (clientArea.height - getHeaderHeight ()) / itemHeight;
+ setTopIndex (itemsCount - visibleItemCount);
+ return;
+ }
+ /* Shift+End */
+ if (focusItem.index == lastAvailableIndex) return; /* at bottom */
+ TableItem item = items [lastAvailableIndex];
+ selectItem (item, false);
+ setFocusItem (item, true);
+ redrawItem (lastAvailableIndex, true);
+ showItem (item);
+ Event newEvent = new Event ();
+ newEvent.item = item;
+ postEvent (SWT.Selection, newEvent);
+ return;
+ }
+ /* SWT.MULTI */
+ if ((stateMask & SWT.CTRL) != 0) {
+ if ((stateMask & SWT.SHIFT) != 0) {
+ /* CTRL+Shift+End */
+ showItem (items [lastAvailableIndex]);
+ return;
+ }
+ /* CTRL+End */
+ if (focusItem.index == lastAvailableIndex) return; /* at bottom */
+ TableItem item = items [lastAvailableIndex];
+ setFocusItem (item, true);
+ showItem (item);
+ redrawItem (item.index, true);
+ return;
+ }
+ /* Shift+End */
+ if (anchorItem == null) anchorItem = focusItem;
+ TableItem selectedItem = items [lastAvailableIndex];
+ if (selectedItem == focusItem && selectedItem.isSelected ()) return;
+ int anchorIndex = anchorItem.index;
+ int selectIndex = selectedItem.index;
+ TableItem[] newSelection = new TableItem [selectIndex - anchorIndex + 1];
+ int writeIndex = 0;
+ for (int i = anchorIndex; i <= selectIndex; i++) {
+ newSelection [writeIndex++] = items [i];
+ }
+ setSelection (newSelection, false);
+ setFocusItem (selectedItem, true);
+ redrawItems (anchorIndex, selectIndex, true);
+ showItem (selectedItem);
+ Event newEvent = new Event ();
+ newEvent.item = selectedItem;
+ postEvent (SWT.Selection, newEvent);
+}
+void onFocusIn () {
+ hasFocus = true;
+ if (itemsCount == 0) {
+ redraw ();
+ return;
+ }
+ if ((style & (SWT.HIDE_SELECTION | SWT.MULTI)) == (SWT.HIDE_SELECTION | SWT.MULTI)) {
+ for (int i = 0; i < selectedItems.length; i++) {
+ redrawItem (selectedItems [i].index, true);
+ }
+ }
+ if (focusItem != null) {
+ redrawItem (focusItem.index, true);
+ return;
+ }
+ /* an initial focus item must be selected */
+ TableItem initialFocus;
+ if (selectedItems.length > 0) {
+ initialFocus = selectedItems [0];
+ } else {
+ initialFocus = items [topIndex];
+ }
+ setFocusItem (initialFocus, false);
+ redrawItem (initialFocus.index, true);
+ return;
+}
+void onFocusOut () {
+ hasFocus = false;
+ if (itemsCount == 0) {
+ redraw ();
+ return;
+ }
+ if (focusItem != null) {
+ redrawItem (focusItem.index, true);
+ }
+ if ((style & (SWT.HIDE_SELECTION | SWT.MULTI)) == (SWT.HIDE_SELECTION | SWT.MULTI)) {
+ for (int i = 0; i < selectedItems.length; i++) {
+ redrawItem (selectedItems [i].index, true);
+ }
+ }
+}
+void onHome (int stateMask) {
+ if ((stateMask & (SWT.CTRL | SWT.SHIFT)) == 0) {
+ /* Home with no modifiers */
+ if (focusItem.index == 0) return; /* at top */
+ TableItem item = items [0];
+ selectItem (item, false);
+ setFocusItem (item, true);
+ redrawItem (0, true);
+ showItem (item);
+ Event newEvent = new Event ();
+ newEvent.item = item;
+ postEvent (SWT.Selection, newEvent);
+ return;
+ }
+ if ((style & SWT.SINGLE) != 0) {
+ if ((stateMask & SWT.CTRL) != 0) {
+ /* CTRL+Home, CTRL+Shift+Home */
+ setTopIndex (0);
+ return;
+ }
+ /* Shift+Home */
+ if (focusItem.index == 0) return; /* at top */
+ TableItem item = items [0];
+ selectItem (item, false);
+ setFocusItem (item, true);
+ redrawItem (0, true);
+ showItem (item);
+ Event newEvent = new Event ();
+ newEvent.item = item;
+ postEvent (SWT.Selection, newEvent);
+ return;
+ }
+ /* SWT.MULTI */
+ if ((stateMask & SWT.CTRL) != 0) {
+ if ((stateMask & SWT.SHIFT) != 0) {
+ /* CTRL+Shift+Home */
+ setTopIndex (0);
+ return;
+ }
+ /* CTRL+Home */
+ if (focusItem.index == 0) return; /* at top */
+ TableItem item = items [0];
+ setFocusItem (item, true);
+ showItem (item);
+ redrawItem (item.index, true);
+ return;
+ }
+ /* Shift+Home */
+ if (anchorItem == null) anchorItem = focusItem;
+ TableItem selectedItem = items [0];
+ if (selectedItem == focusItem && selectedItem.isSelected ()) return;
+ int anchorIndex = anchorItem.index;
+ int selectIndex = selectedItem.index;
+ TableItem[] newSelection = new TableItem [anchorIndex + 1];
+ int writeIndex = 0;
+ for (int i = anchorIndex; i >= 0; i--) {
+ newSelection [writeIndex++] = items [i];
+ }
+ setSelection (newSelection, false);
+ setFocusItem (selectedItem, true);
+ redrawItems (anchorIndex, selectIndex, true);
+ showItem (selectedItem);
+ Event newEvent = new Event ();
+ newEvent.item = selectedItem;
+ postEvent (SWT.Selection, newEvent);
+}
+void onKeyDown (Event event) {
+ if (ignoreKey) {
+ ignoreKey = false;
+ return;
+ }
+ ignoreKey = true;
+ notifyListeners (event.type, event);
+ event.type = SWT.None;
+ if (!event.doit) return;
+ if (focusItem == null) return;
+ if ((event.stateMask & SWT.SHIFT) == 0 && event.keyCode != SWT.SHIFT) {
+ anchorItem = null;
+ }
+ switch (event.keyCode) {
+ case SWT.ARROW_UP:
+ onArrowUp (event.stateMask);
+ return;
+ case SWT.ARROW_DOWN:
+ onArrowDown (event.stateMask);
+ return;
+ case SWT.ARROW_LEFT:
+ onArrowLeft (event.stateMask);
+ return;
+ case SWT.ARROW_RIGHT:
+ onArrowRight (event.stateMask);
+ return;
+ case SWT.PAGE_UP:
+ onPageUp (event.stateMask);
+ return;
+ case SWT.PAGE_DOWN:
+ onPageDown (event.stateMask);
+ return;
+ case SWT.HOME:
+ onHome (event.stateMask);
+ return;
+ case SWT.END:
+ onEnd (event.stateMask);
+ return;
+ }
+ if (event.character == ' ') {
+ onSpace ();
+ return;
+ }
+ if (event.character == SWT.CR) {
+ onCR ();
+ return;
+ }
+ if ((event.stateMask & SWT.CTRL) != 0) return;
+
+ int initialIndex = focusItem.index;
+ char character = Character.toLowerCase (event.character);
+ /* check available items from current focus item to bottom */
+ for (int i = initialIndex + 1; i < itemsCount; i++) {
+ TableItem item = items [i];
+ String text = item.getText (0, false);
+ if (text.length () > 0) {
+ if (Character.toLowerCase (text.charAt (0)) == character) {
+ selectItem (item, false);
+ setFocusItem (item, true);
+ redrawItem (i, true);
+ showItem (item);
+ Event newEvent = new Event ();
+ newEvent.item = item;
+ postEvent (SWT.Selection, newEvent);
+ return;
+ }
+ }
+ }
+ /* check available items from top to current focus item */
+ for (int i = 0; i < initialIndex; i++) {
+ TableItem item = items [i];
+ String text = item.getText (0, false);
+ if (text.length () > 0) {
+ if (Character.toLowerCase (text.charAt (0)) == character) {
+ selectItem (item, false);
+ setFocusItem (item, true);
+ redrawItem (i, true);
+ showItem (item);
+ Event newEvent = new Event ();
+ newEvent.item = item;
+ postEvent (SWT.Selection, newEvent);
+ return;
+ }
+ }
+ }
+}
+void onMouseDoubleClick (Event event) {
+ if (!isFocusControl ()) setFocus ();
+ int index = (event.y - getHeaderHeight ()) / itemHeight + topIndex;
+ if (!(0 <= index && index < itemsCount)) return; /* not on an available item */
+ TableItem selectedItem = items [index];
+
+ /*
+ * If the two clicks of the double click did not occur over the same item then do not
+ * consider this to be a default selection.
+ */
+ if (selectedItem != lastClickedItem) return;
+
+ if (!selectedItem.getHitBounds ().contains (event.x, event.y)) return; /* considers x */
+
+ Event newEvent = new Event ();
+ newEvent.item = selectedItem;
+ postEvent (SWT.DefaultSelection, newEvent);
+}
+void onMouseDown (Event event) {
+ if (!isFocusControl ()) forceFocus ();
+ int index = (event.y - getHeaderHeight ()) / itemHeight + topIndex;
+ if (!(0 <= index && index < itemsCount)) return; /* not on an available item */
+ TableItem selectedItem = items [index];
+
+ /* if click was in checkbox */
+ if ((style & SWT.CHECK) != 0 && selectedItem.getCheckboxBounds ().contains (event.x, event.y)) {
+ if (event.button != 1) return;
+ selectedItem.setChecked (!selectedItem.checked);
+ Event newEvent = new Event ();
+ newEvent.item = selectedItem;
+ newEvent.detail = SWT.CHECK;
+ postEvent (SWT.Selection, newEvent);
+ return;
+ }
+
+ if (!selectedItem.getHitBounds ().contains (event.x, event.y)) return;
+
+ if ((event.stateMask & SWT.SHIFT) == 0 && event.keyCode != SWT.SHIFT) anchorItem = null;
+
+ boolean sendSelection = true;
+ /* Detect when this is the second click of a DefaultSelection and don't fire Selection */
+ if (lastSelectionEvent != null && lastSelectionEvent.item == selectedItem) {
+ if (event.time - lastSelectionEvent.time <= display.getDoubleClickTime ()) {
+ sendSelection = false;
+ } else {
+ lastSelectionEvent = event;
+ event.item = selectedItem;
+ }
+ } else {
+ lastSelectionEvent = event;
+ event.item = selectedItem;
+ }
+
+ if ((style & SWT.SINGLE) != 0) {
+ if (!selectedItem.isSelected ()) {
+ if (event.button == 1) {
+ selectItem (selectedItem, false);
+ setFocusItem (selectedItem, true);
+ redrawItem (selectedItem.index, true);
+ if (sendSelection) {
+ Event newEvent = new Event ();
+ newEvent.item = selectedItem;
+ postEvent (SWT.Selection, newEvent);
+ }
+ return;
+ }
+ if ((event.stateMask & (SWT.CTRL | SWT.SHIFT)) == 0) {
+ selectItem (selectedItem, false);
+ setFocusItem (selectedItem, true);
+ redrawItem (selectedItem.index, true);
+ if (sendSelection) {
+ Event newEvent = new Event ();
+ newEvent.item = selectedItem;
+ postEvent (SWT.Selection, newEvent);
+ }
+ return;
+ }
+ }
+ /* item is selected */
+ if (event.button == 1) {
+ /* fire a selection event, though the selection did not change */
+ if (sendSelection) {
+ Event newEvent = new Event ();
+ newEvent.item = selectedItem;
+ postEvent (SWT.Selection, newEvent);
+ }
+ return;
+ }
+ }
+ /* SWT.MULTI */
+ if (!selectedItem.isSelected ()) {
+ if (event.button == 1) {
+ if ((event.stateMask & (SWT.CTRL | SWT.SHIFT)) == SWT.SHIFT) {
+ if (anchorItem == null) anchorItem = focusItem;
+ int anchorIndex = anchorItem.index;
+ int selectIndex = selectedItem.index;
+ TableItem[] newSelection = new TableItem [Math.abs (anchorIndex - selectIndex) + 1];
+ int step = anchorIndex < selectIndex ? 1 : -1;
+ int writeIndex = 0;
+ for (int i = anchorIndex; i != selectIndex; i += step) {
+ newSelection [writeIndex++] = items [i];
+ }
+ newSelection [writeIndex] = items [selectIndex];
+ setSelection (newSelection, false);
+ setFocusItem (selectedItem, true);
+ redrawItems (
+ Math.min (anchorIndex, selectIndex),
+ Math.max (anchorIndex, selectIndex),
+ true);
+ if (sendSelection) {
+ Event newEvent = new Event ();
+ newEvent.item = selectedItem;
+ postEvent (SWT.Selection, newEvent);
+ }
+ return;
+ }
+ selectItem (selectedItem, (event.stateMask & SWT.CTRL) != 0);
+ setFocusItem (selectedItem, true);
+ redrawItem (selectedItem.index, true);
+ if (sendSelection) {
+ Event newEvent = new Event ();
+ newEvent.item = selectedItem;
+ postEvent (SWT.Selection, newEvent);
+ }
+ return;
+ }
+ /* button 3 */
+ if ((event.stateMask & (SWT.CTRL | SWT.SHIFT)) == 0) {
+ selectItem (selectedItem, false);
+ setFocusItem (selectedItem, true);
+ redrawItem (selectedItem.index, true);
+ if (sendSelection) {
+ Event newEvent = new Event ();
+ newEvent.item = selectedItem;
+ postEvent (SWT.Selection, newEvent);
+ }
+ return;
+ }
+ }
+ /* item is selected */
+ if (event.button != 1) return;
+ if ((event.stateMask & SWT.CTRL) != 0) {
+ removeSelectedItem (getSelectionIndex (selectedItem));
+ setFocusItem (selectedItem, true);
+ redrawItem (selectedItem.index, true);
+ if (sendSelection) {
+ Event newEvent = new Event ();
+ newEvent.item = selectedItem;
+ postEvent (SWT.Selection, newEvent);
+ }
+ return;
+ }
+ if ((event.stateMask & SWT.SHIFT) != 0) {
+ if (anchorItem == null) anchorItem = focusItem;
+ int anchorIndex = anchorItem.index;
+ int selectIndex = selectedItem.index;
+ TableItem[] newSelection = new TableItem [Math.abs (anchorIndex - selectIndex) + 1];
+ int step = anchorIndex < selectIndex ? 1 : -1;
+ int writeIndex = 0;
+ for (int i = anchorIndex; i != selectIndex; i += step) {
+ newSelection [writeIndex++] = items [i];
+ }
+ newSelection [writeIndex] = items [selectIndex];
+ setSelection (newSelection, false);
+ setFocusItem (selectedItem, true);
+ redrawItems (
+ Math.min (anchorIndex, selectIndex),
+ Math.max (anchorIndex, selectIndex),
+ true);
+ if (sendSelection) {
+ Event newEvent = new Event ();
+ newEvent.item = selectedItem;
+ postEvent (SWT.Selection, newEvent);
+ }
+ return;
+ }
+ selectItem (selectedItem, false);
+ setFocusItem (selectedItem, true);
+ redrawItem (selectedItem.index, true);
+ if (sendSelection) {
+ Event newEvent = new Event ();
+ newEvent.item = selectedItem;
+ postEvent (SWT.Selection, newEvent);
+ }
+}
+void onMouseUp (Event event) {
+ int index = (event.y - getHeaderHeight ()) / itemHeight + topIndex;
+ if (!(0 <= index && index < itemsCount)) return; /* not on an available item */
+ lastClickedItem = items [index];
+}
+void onPageDown (int stateMask) {
+ int visibleItemCount = (clientArea.height - getHeaderHeight ()) / itemHeight;
+ if ((stateMask & (SWT.CTRL | SWT.SHIFT)) == 0) {
+ /* PageDown with no modifiers */
+ int newFocusIndex = focusItem.index + visibleItemCount - 1;
+ newFocusIndex = Math.min (newFocusIndex, itemsCount - 1);
+ if (newFocusIndex == focusItem.index) return;
+ TableItem item = items [newFocusIndex];
+ selectItem (item, false);
+ setFocusItem (item, true);
+ showItem (item);
+ redrawItem (item.index, true);
+ return;
+ }
+ if ((stateMask & (SWT.CTRL | SWT.SHIFT)) == (SWT.CTRL | SWT.SHIFT)) {
+ /* CTRL+Shift+PageDown */
+ int newTopIndex = topIndex + visibleItemCount;
+ newTopIndex = Math.min (newTopIndex, itemsCount - visibleItemCount);
+ if (newTopIndex == topIndex) return;
+ setTopIndex (newTopIndex);
+ return;
+ }
+ if ((style & SWT.SINGLE) != 0) {
+ if ((stateMask & SWT.SHIFT) != 0) {
+ /* Shift+PageDown */
+ int newFocusIndex = focusItem.index + visibleItemCount - 1;
+ newFocusIndex = Math.min (newFocusIndex, itemsCount - 1);
+ if (newFocusIndex == focusItem.index) return;
+ TableItem item = items [newFocusIndex];
+ selectItem (item, false);
+ setFocusItem (item, true);
+ showItem (item);
+ redrawItem (item.index, true);
+ return;
+ }
+ /* CTRL+PageDown */
+ int newTopIndex = topIndex + visibleItemCount;
+ newTopIndex = Math.min (newTopIndex, itemsCount - visibleItemCount);
+ if (newTopIndex == topIndex) return;
+ setTopIndex (newTopIndex);
+ return;
+ }
+ /* SWT.MULTI */
+ if ((stateMask & SWT.CTRL) != 0) {
+ /* CTRL+PageDown */
+ int bottomIndex = Math.min (topIndex + visibleItemCount - 1, itemsCount - 1);
+ if (focusItem.index != bottomIndex) {
+ /* move focus to bottom item in viewport */
+ setFocusItem (items [bottomIndex], true);
+ redrawItem (bottomIndex, true);
+ } else {
+ /* at bottom of viewport, so set focus to bottom item one page down */
+ int newFocusIndex = Math.min (itemsCount - 1, bottomIndex + visibleItemCount);
+ if (newFocusIndex == focusItem.index) return;
+ setFocusItem (items [newFocusIndex], true);
+ showItem (items [newFocusIndex]);
+ redrawItem (newFocusIndex, true);
+ }
+ return;
+ }
+ /* Shift+PageDown */
+ if (anchorItem == null) anchorItem = focusItem;
+ int anchorIndex = anchorItem.index;
+ int bottomIndex = Math.min (topIndex + visibleItemCount - 1, itemsCount - 1);
+ int selectIndex;
+ if (focusItem.index != bottomIndex) {
+ /* select from focus to bottom item in viewport */
+ selectIndex = bottomIndex;
+ } else {
+ /* already at bottom of viewport, so select to bottom of one page down */
+ selectIndex = Math.min (itemsCount - 1, bottomIndex + visibleItemCount);
+ if (selectIndex == focusItem.index && focusItem.isSelected ()) return;
+ }
+ TableItem selectedItem = items [selectIndex];
+ TableItem[] newSelection = new TableItem [Math.abs (anchorIndex - selectIndex) + 1];
+ int step = anchorIndex < selectIndex ? 1 : -1;
+ int writeIndex = 0;
+ for (int i = anchorIndex; i != selectIndex; i += step) {
+ newSelection [writeIndex++] = items [i];
+ }
+ newSelection [writeIndex] = items [selectIndex];
+ setSelection (newSelection, false);
+ setFocusItem (selectedItem, true);
+ showItem (selectedItem);
+ Event newEvent = new Event ();
+ newEvent.item = selectedItem;
+ postEvent (SWT.Selection, newEvent);
+}
+void onPageUp (int stateMask) {
+ int visibleItemCount = (clientArea.height - getHeaderHeight ()) / itemHeight;
+ if ((stateMask & (SWT.CTRL | SWT.SHIFT)) == 0) {
+ /* PageUp with no modifiers */
+ int newFocusIndex = Math.max (0, focusItem.index - visibleItemCount + 1);
+ if (newFocusIndex == focusItem.index) return;
+ TableItem item = items [newFocusIndex];
+ selectItem (item, false);
+ setFocusItem (item, true);
+ showItem (item);
+ redrawItem (item.index, true);
+ return;
+ }
+ if ((stateMask & (SWT.CTRL | SWT.SHIFT)) == (SWT.CTRL | SWT.SHIFT)) {
+ /* CTRL+Shift+PageUp */
+ int newTopIndex = Math.max (0, topIndex - visibleItemCount);
+ if (newTopIndex == topIndex) return;
+ setTopIndex (newTopIndex);
+ return;
+ }
+ if ((style & SWT.SINGLE) != 0) {
+ if ((stateMask & SWT.SHIFT) != 0) {
+ /* Shift+PageUp */
+ int newFocusIndex = Math.max (0, focusItem.index - visibleItemCount + 1);
+ if (newFocusIndex == focusItem.index) return;
+ TableItem item = items [newFocusIndex];
+ selectItem (item, false);
+ setFocusItem (item, true);
+ showItem (item);
+ redrawItem (item.index, true);
+ return;
+ }
+ /* CTRL+PageUp */
+ int newTopIndex = Math.max (0, topIndex - visibleItemCount);
+ if (newTopIndex == topIndex) return;
+ setTopIndex (newTopIndex);
+ return;
+ }
+ /* SWT.MULTI */
+ if ((stateMask & SWT.CTRL) != 0) {
+ /* CTRL+PageUp */
+ if (focusItem.index != topIndex) {
+ /* move focus to top item in viewport */
+ setFocusItem (items [topIndex], true);
+ redrawItem (topIndex, true);
+ } else {
+ /* at top of viewport, so set focus to top item one page up */
+ int newFocusIndex = Math.max (0, focusItem.index - visibleItemCount);
+ if (newFocusIndex == focusItem.index) return;
+ setFocusItem (items [newFocusIndex], true);
+ showItem (items [newFocusIndex]);
+ redrawItem (newFocusIndex, true);
+ }
+ return;
+ }
+ /* Shift+PageUp */
+ if (anchorItem == null) anchorItem = focusItem;
+ int anchorIndex = anchorItem.index;
+ int selectIndex;
+ if (focusItem.index != topIndex) {
+ /* select from focus to top item in viewport */
+ selectIndex = topIndex;
+ } else {
+ /* already at top of viewport, so select to top of one page up */
+ selectIndex = Math.max (0, topIndex - visibleItemCount);
+ if (selectIndex == focusItem.index && focusItem.isSelected ()) return;
+ }
+ TableItem selectedItem = items [selectIndex];
+ TableItem[] newSelection = new TableItem [Math.abs (anchorIndex - selectIndex) + 1];
+ int step = anchorIndex < selectIndex ? 1 : -1;
+ int writeIndex = 0;
+ for (int i = anchorIndex; i != selectIndex; i += step) {
+ newSelection [writeIndex++] = items [i];
+ }
+ newSelection [writeIndex] = items [selectIndex];
+ setSelection (newSelection, false);
+ setFocusItem (selectedItem, true);
+ showItem (selectedItem);
+ Event newEvent = new Event ();
+ newEvent.item = selectedItem;
+ postEvent (SWT.Selection, newEvent);
+}
+void onPaint (Event event) {
+ TableColumn[] orderedColumns = getOrderedColumns ();
+ GC gc = event.gc;
+ Rectangle clipping = gc.getClipping ();
+ int headerHeight = getHeaderHeight ();
+ int numColumns = orderedColumns.length;
+ int startColumn = -1, endColumn = -1;
+ if (numColumns > 0) {
+ startColumn = computeColumnIntersect (clipping.x, 0);
+ if (startColumn != -1) { /* the clip x is within a column's bounds */
+ endColumn = computeColumnIntersect (clipping.x + clipping.width, startColumn);
+ if (endColumn == -1) endColumn = numColumns - 1;
+ }
+ } else {
+ startColumn = endColumn = 0;
+ }
+
+ /* Determine the items to be painted */
+ int startIndex = (clipping.y - headerHeight) / itemHeight + topIndex;
+ int endIndex = -1;
+ if (startIndex < itemsCount) {
+ endIndex = startIndex + Compatibility.ceil (clipping.height, itemHeight);
+ }
+ startIndex = Math.max (0, startIndex);
+ endIndex = Math.min (endIndex, itemsCount - 1);
+
+ /* fill background not handled by items */
+ gc.setBackground (getBackground ());
+ gc.setClipping (clipping);
+ int bottomY = endIndex >= 0 ? getItemY (items [endIndex]) + itemHeight : 0;
+ int fillHeight = Math.max (0, clientArea.height - bottomY);
+ if (fillHeight > 0) { /* space below bottom item */
+ drawBackground (gc, 0, bottomY, clientArea.width, fillHeight);
+ }
+ if (columns.length > 0) {
+ TableColumn column = orderedColumns [orderedColumns.length - 1]; /* last column */
+ int rightX = column.getX () + column.width;
+ if (rightX < clientArea.width) {
+ drawBackground (gc, rightX, 0, clientArea.width - rightX, clientArea.height - fillHeight);
+ }
+ }
+
+ /* paint the items */
+ boolean noFocusDraw = false;
+ int[] lineDash = gc.getLineDash ();
+ int lineWidth = gc.getLineWidth ();
+ for (int i = startIndex; i <= Math.min (endIndex, itemsCount - 1); i++) {
+ TableItem item = items [i];
+ if (!item.isDisposed ()) { /* ensure that item was not disposed in a callback */
+ if (startColumn == -1) {
+ /* indicates that region to paint is to the right of the last column */
+ noFocusDraw = item.paint (gc, null, true) || noFocusDraw;
+ } else {
+ if (numColumns == 0) {
+ noFocusDraw = item.paint (gc, null, false) || noFocusDraw;
+ } else {
+ for (int j = startColumn; j <= Math.min (endColumn, columns.length - 1); j++) {
+ if (!item.isDisposed ()) { /* ensure that item was not disposed in a callback */
+ noFocusDraw = item.paint (gc, orderedColumns [j], false) || noFocusDraw;
+ }
+ if (isDisposed () || gc.isDisposed ()) return; /* ensure that receiver was not disposed in a callback */
+ }
+ }
+ }
+ }
+ if (isDisposed () || gc.isDisposed ()) return; /* ensure that receiver was not disposed in a callback */
+ }
+
+ /* repaint grid lines */
+ gc.setClipping(clipping);
+ gc.setLineWidth (lineWidth);
+ if (linesVisible) {
+ gc.setForeground (display.getSystemColor (SWT.COLOR_WIDGET_LIGHT_SHADOW));
+ gc.setLineDash (lineDash);
+ if (numColumns > 0 && startColumn != -1) {
+ /* vertical column lines */
+ for (int i = startColumn; i <= endColumn; i++) {
+ int x = orderedColumns [i].getX () + orderedColumns [i].width - 1;
+ gc.drawLine (x, clipping.y, x, clipping.y + clipping.height);
+ }
+ }
+ /* horizontal item lines */
+ bottomY = clipping.y + clipping.height;
+ int rightX = clipping.x + clipping.width;
+ int y = (clipping.y - headerHeight) / itemHeight * itemHeight + headerHeight;
+ while (y <= bottomY) {
+ gc.drawLine (clipping.x, y, rightX, y);
+ y += itemHeight;
+ }
+ }
+
+ /* paint focus rectangle */
+ if (!noFocusDraw && isFocusControl ()) {
+ if (focusItem != null) {
+ Rectangle focusBounds = focusItem.getFocusBounds ();
+ if (focusBounds.width > 0) {
+ gc.setForeground (display.getSystemColor (SWT.COLOR_BLACK));
+ gc.setClipping (focusBounds);
+ if (focusItem.isSelected ()) {
+ gc.setLineDash (new int[] {2, 2});
+ } else {
+ gc.setLineDash (new int[] {1, 1});
+ }
+ gc.drawFocus (focusBounds.x, focusBounds.y, focusBounds.width, focusBounds.height);
+ }
+ } else {
+ /* no items, so draw focus border around Table */
+ int y = headerHeight + 1;
+ int width = Math.max (0, clientArea.width - 2);
+ int height = Math.max (0, clientArea.height - headerHeight - 2);
+ gc.setForeground (display.getSystemColor (SWT.COLOR_BLACK));
+ gc.setClipping (1, y, width, height);
+ gc.setLineDash (new int[] {1, 1});
+ gc.drawFocus (1, y, width, height);
+ }
+ }
+}
+void onResize (Event event) {
+ clientArea = getClientArea ();
+ /* vertical scrollbar */
+ ScrollBar vBar = getVerticalBar ();
+ if (vBar != null) {
+ int clientHeight = (clientArea.height - getHeaderHeight ()) / itemHeight;
+ int thumb = Math.min (clientHeight, itemsCount);
+ vBar.setThumb (thumb);
+ vBar.setPageIncrement (thumb);
+ int index = vBar.getSelection ();
+ if (index != topIndex) {
+ topIndex = index;
+ redraw ();
+ }
+ boolean visible = clientHeight < itemsCount;
+ if (visible != vBar.getVisible ()) {
+ vBar.setVisible (visible);
+ clientArea = getClientArea ();
+ }
+ }
+
+ /* horizontal scrollbar */
+ ScrollBar hBar = getHorizontalBar ();
+ if (hBar != null) {
+ int hBarMaximum = hBar.getMaximum ();
+ int thumb = Math.min (clientArea.width, hBarMaximum);
+ hBar.setThumb (thumb);
+ hBar.setPageIncrement (thumb);
+ horizontalOffset = hBar.getSelection ();
+ boolean visible = clientArea.width < hBarMaximum;
+ if (visible != hBar.getVisible ()) {
+ hBar.setVisible (visible);
+ clientArea = getClientArea ();
+ }
+ }
+
+ /* header */
+ int headerHeight = Math.max (fontHeight, headerImageHeight) + 2 * getHeaderPadding ();
+ header.setSize (clientArea.width, headerHeight);
+
+ /* if this is the focus control but there are no items then the boundary focus ring must be repainted */
+ if (itemsCount == 0 && isFocusControl ()) redraw ();
+}
+void onScrollHorizontal (Event event) {
+ ScrollBar hBar = getHorizontalBar ();
+ if (hBar == null) return;
+ int newSelection = hBar.getSelection ();
+ update ();
+ if (itemsCount > 0) {
+ GC gc = new GC (this);
+ gc.copyArea (
+ 0, 0,
+ clientArea.width, clientArea.height,
+ horizontalOffset - newSelection, 0);
+ gc.dispose ();
+ } else {
+ redraw (); /* ensure that static focus rectangle updates properly */
+ }
+
+ if (drawCount <= 0 && header.isVisible ()) {
+ header.update ();
+ Rectangle headerClientArea = header.getClientArea ();
+ GC gc = new GC (header);
+ gc.copyArea (
+ 0, 0,
+ headerClientArea.width, headerClientArea.height,
+ horizontalOffset - newSelection, 0);
+ gc.dispose ();
+ }
+ horizontalOffset = newSelection;
+}
+void onScrollVertical (Event event) {
+ ScrollBar vBar = getVerticalBar ();
+ if (vBar == null) return;
+ int newSelection = vBar.getSelection ();
+ update ();
+ GC gc = new GC (this);
+ gc.copyArea (
+ 0, 0,
+ clientArea.width, clientArea.height,
+ 0, (topIndex - newSelection) * itemHeight);
+ gc.dispose ();
+ topIndex = newSelection;
+}
+void onSpace () {
+ if (focusItem == null) return;
+ if (!focusItem.isSelected ()) {
+ selectItem (focusItem, (style & SWT.MULTI) != 0);
+ redrawItem (focusItem.index, true);
+ }
+ if ((style & SWT.CHECK) != 0) {
+ focusItem.setChecked (!focusItem.checked);
+ }
+ showItem (focusItem);
+ Event event = new Event ();
+ event.item = focusItem;
+ postEvent (SWT.Selection, event);
+ if ((style & SWT.CHECK) == 0) return;
+
+ /* SWT.CHECK */
+ event = new Event ();
+ event.item = focusItem;
+ event.detail = SWT.CHECK;
+ postEvent (SWT.Selection, event);
+}
+/*
+ * The current focus item is about to become unavailable, so reassign focus.
+ */
+void reassignFocus () {
+ if (focusItem == null) return;
+
+ /*
+ * reassign to the previous root-level item if there is one, or the next
+ * root-level item otherwise
+ */
+ int index = focusItem.index;
+ if (index != 0) {
+ index--;
+ } else {
+ index++;
+ }
+ if (index < itemsCount) {
+ TableItem item = items [index];
+ setFocusItem (item, false);
+ showItem (item);
+ } else {
+ setFocusItem (null, false); /* no items left */
+ }
+}
+public void redraw () {
+ checkWidget ();
+ if (drawCount <= 0) super.redraw ();
+}
+public void redraw (int x, int y, int width, int height, boolean all) {
+ checkWidget ();
+ if (drawCount <= 0) super.redraw (x, y, width, height, all);
+}
+/*
+ * Redraws from the specified index down to the last available item inclusive. Note
+ * that the redraw bounds do not extend beyond the current last item, so clients
+ * that reduce the number of available items should use #redrawItems(int,int) instead
+ * to ensure that redrawing extends down to the previous bottom item boundary.
+ */
+void redrawFromItemDownwards (int index) {
+ redrawItems (index, itemsCount - 1, false);
+}
+/*
+ * Redraws the table item at the specified index. It is valid for this index to reside
+ * beyond the last available item.
+ */
+void redrawItem (int itemIndex, boolean focusBoundsOnly) {
+ if (itemIndex < itemsCount && !items [itemIndex].isInViewport ()) return;
+ redrawItems (itemIndex, itemIndex, focusBoundsOnly);
+}
+/*
+ * Redraws the table between the start and end item indices inclusive. It is valid
+ * for the end index value to extend beyond the last available item.
+ */
+void redrawItems (int startIndex, int endIndex, boolean focusBoundsOnly) {
+ if (drawCount > 0) return;
+
+ int startY = (startIndex - topIndex) * itemHeight + getHeaderHeight ();
+ int height = (endIndex - startIndex + 1) * itemHeight;
+ if (focusBoundsOnly) {
+ boolean custom = hooks (SWT.EraseItem) || hooks (SWT.PaintItem);
+ if (!custom && columns.length > 0) {
+ TableColumn lastColumn;
+ if ((style & SWT.FULL_SELECTION) != 0) {
+ TableColumn[] orderedColumns = getOrderedColumns ();
+ lastColumn = orderedColumns [orderedColumns.length - 1];
+ } else {
+ lastColumn = columns [0];
+ }
+ int rightX = lastColumn.getX () + lastColumn.getWidth ();
+ if (rightX <= 0) return; /* focus column(s) not visible */
+ }
+ endIndex = Math.min (endIndex, itemsCount - 1);
+ for (int i = startIndex; i <= endIndex; i++) {
+ TableItem item = items [i];
+ if (item.isInViewport ()) {
+ /* if custom painting is being done then repaint the full item */
+ if (custom) {
+ redraw (0, getItemY (item), clientArea.width, itemHeight, false);
+ } else {
+ /* repaint the item's focus bounds */
+ Rectangle bounds = item.getFocusBounds ();
+ redraw (bounds.x, startY, bounds.width, height, false);
+ }
+ }
+ }
+ } else {
+ redraw (0, startY, clientArea.width, height, false);
+ }
+}
+/**
+ * Removes the item from the receiver at the given
+ * zero-relative index.
+ *
+ * @param index the index for the item
+ *
+ * @exception IllegalArgumentException <ul>
+ * <li>ERROR_INVALID_RANGE - if the index is not between 0 and the number of elements in the list minus 1 (inclusive)</li>
+ * </ul>
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public void remove (int index) {
+ checkWidget ();
+ if (!(0 <= index && index < itemsCount)) error (SWT.ERROR_INVALID_RANGE);
+ items [index].dispose ();
+}
+/**
+ * Removes the items from the receiver which are
+ * between the given zero-relative start and end
+ * indices (inclusive).
+ *
+ * @param start the start of the range
+ * @param end the end of the range
+ *
+ * @exception IllegalArgumentException <ul>
+ * <li>ERROR_INVALID_RANGE - if either the start or end are not between 0 and the number of elements in the list minus 1 (inclusive)</li>
+ * </ul>
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public void remove (int start, int end) {
+ checkWidget ();
+ if (start > end) return;
+ if (!(0 <= start && start <= end && end < itemsCount)) {
+ error (SWT.ERROR_INVALID_RANGE);
+ }
+ if (start == 0 && end == itemsCount - 1) {
+ removeAll ();
+ } else {
+ for (int i = end; i >= start; i--) {
+ items [i].dispose ();
+ }
+ }
+}
+/**
+ * Removes the items from the receiver's list at the given
+ * zero-relative indices.
+ *
+ * @param indices the array of indices of the items
+ *
+ * @exception IllegalArgumentException <ul>
+ * <li>ERROR_INVALID_RANGE - if the index is not between 0 and the number of elements in the list minus 1 (inclusive)</li>
+ * <li>ERROR_NULL_ARGUMENT - if the indices array is null</li>
+ * </ul>
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public void remove (int [] indices) {
+ checkWidget ();
+ if (indices == null) error (SWT.ERROR_NULL_ARGUMENT);
+ if (indices.length == 0) return;
+ int [] newIndices = new int [indices.length];
+ System.arraycopy (indices, 0, newIndices, 0, indices.length);
+ sortDescent (newIndices);
+ int start = newIndices [newIndices.length - 1], end = newIndices [0];
+ if (!(0 <= start && start <= end && end < itemsCount)) {
+ error (SWT.ERROR_INVALID_RANGE);
+ }
+ int lastRemovedIndex = -1;
+ for (int i = 0; i < newIndices.length; i++) {
+ if (newIndices [i] != lastRemovedIndex) {
+ items [newIndices [i]].dispose ();
+ lastRemovedIndex = newIndices [i];
+ }
+ }
+}
+/**
+ * Removes all of the items from the receiver.
+ *
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public void removeAll () {
+ checkWidget ();
+ if (itemsCount == 0) return;
+ setRedraw (false);
+
+ setFocusItem (null, false);
+ for (int i = 0; i < itemsCount; i++) {
+ items [i].dispose (false);
+ }
+ items = new TableItem [0];
+ selectedItems = new TableItem [0];
+ itemsCount = topIndex = 0;
+ anchorItem = lastClickedItem = null;
+ lastSelectionEvent = null;
+ ScrollBar vBar = getVerticalBar ();
+ if (vBar != null) {
+ vBar.setMaximum (1);
+ vBar.setVisible (false);
+ }
+ if (columns.length == 0) {
+ horizontalOffset = 0;
+ ScrollBar hBar = getHorizontalBar ();
+ if (hBar != null) {
+ hBar.setMaximum (1);
+ hBar.setVisible (false);
+ }
+ }
+
+ setRedraw (true);
+}
+String removeMnemonics (String string) {
+ /* removes single ampersands and preserves double-ampersands */
+ char [] chars = new char [string.length ()];
+ string.getChars (0, chars.length, chars, 0);
+ int i = 0, j = 0;
+ for ( ; i < chars.length; i++, j++) {
+ if (chars[i] == '&') {
+ if (++i == chars.length) break;
+ if (chars[i] == '&') {
+ chars[j++] = chars[i - 1];
+ }
+ }
+ chars[j] = chars[i];
+ }
+ if (i == j) return string;
+ return new String (chars, 0, j);
+}
+void removeSelectedItem (int index) {
+ TableItem[] newSelectedItems = new TableItem [selectedItems.length - 1];
+ System.arraycopy (selectedItems, 0, newSelectedItems, 0, index);
+ System.arraycopy (selectedItems, index + 1, newSelectedItems, index, newSelectedItems.length - index);
+ selectedItems = newSelectedItems;
+}
+/**
+ * Removes the listener from the collection of listeners who will
+ * be notified when the user changes the receiver's selection.
+ *
+ * @param listener the listener which should no longer be notified
+ *
+ * @exception IllegalArgumentException <ul>
+ * <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
+ * </ul>
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @see SelectionListener
+ * @see #addSelectionListener(SelectionListener)
+ */
+public void removeSelectionListener (SelectionListener listener) {
+ checkWidget ();
+ if (listener == null) error (SWT.ERROR_NULL_ARGUMENT);
+ removeListener (SWT.Selection, listener);
+ removeListener (SWT.DefaultSelection, listener);
+}
+/**
+ * Selects the item at the given zero-relative index in the receiver.
+ * If the item at the index was already selected, it remains
+ * selected. Indices that are out of range are ignored.
+ *
+ * @param index the index of the item to select
+ *
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public void select (int index) {
+ checkWidget ();
+ if (!(0 <= index && index < itemsCount)) return;
+ selectItem (items [index], (style & SWT.MULTI) != 0);
+ if (hasFocus () || (style & SWT.HIDE_SELECTION) == 0) {
+ redrawItem (index, false);
+ }
+}
+/**
+ * Selects the items in the range specified by the given zero-relative
+ * indices in the receiver. The range of indices is inclusive.
+ * The current selection is not cleared before the new items are selected.
+ * <p>
+ * If an item in the given range is not selected, it is selected.
+ * If an item in the given range was already selected, it remains selected.
+ * Indices that are out of range are ignored and no items will be selected
+ * if start is greater than end.
+ * If the receiver is single-select and there is more than one item in the
+ * given range, then all indices are ignored.
+ * </p>
+ *
+ * @param start the start of the range
+ * @param end the end of the range
+ *
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @see Table#setSelection(int,int)
+ */
+public void select (int start, int end) {
+ checkWidget ();
+ if (end < 0 || start > end || ((style & SWT.SINGLE) != 0 && start != end)) return;
+ if (itemsCount == 0 || start >= itemsCount) return;
+ start = Math.max (start, 0);
+ end = Math.min (end, itemsCount - 1);
+ for (int i = start; i <= end; i++) {
+ selectItem (items [i], (style & SWT.MULTI) != 0);
+ }
+ if (hasFocus () || (style & SWT.HIDE_SELECTION) == 0) {
+ redrawItems (start, end, false);
+ }
+}
+/**
+ * Selects the items at the given zero-relative indices in the receiver.
+ * The current selection is not cleared before the new items are selected.
+ * <p>
+ * If the item at a given index is not selected, it is selected.
+ * If the item at a given index was already selected, it remains selected.
+ * Indices that are out of range and duplicate indices are ignored.
+ * If the receiver is single-select and multiple indices are specified,
+ * then all indices are ignored.
+ * </p>
+ *
+ * @param indices the array of indices for the items to select
+ *
+ * @exception IllegalArgumentException <ul>
+ * <li>ERROR_NULL_ARGUMENT - if the array of indices is null</li>
+ * </ul>
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @see Table#setSelection(int[])
+ */
+public void select (int [] indices) {
+ checkWidget ();
+ if (indices == null) error (SWT.ERROR_NULL_ARGUMENT);
+ if (indices.length == 0 || ((style & SWT.SINGLE) != 0 && indices.length > 1)) return;
+
+ for (int i = 0; i < indices.length; i++) {
+ if (0 <= indices [i] && indices [i] < itemsCount) {
+ selectItem (items [indices [i]], (style & SWT.MULTI) != 0);
+ }
+ }
+ if (hasFocus () || (style & SWT.HIDE_SELECTION) == 0) {
+ for (int i = 0; i < indices.length; i++) {
+ if (0 <= indices [i] && indices [i] < itemsCount) {
+ redrawItem (indices [i], false);
+ }
+ }
+ }
+}
+/**
+ * Selects all of the items in the receiver.
+ * <p>
+ * If the receiver is single-select, do nothing.
+ * </p>
+ *
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public void selectAll () {
+ checkWidget ();
+ if ((style & SWT.SINGLE) != 0) return;
+ selectedItems = new TableItem [itemsCount];
+ System.arraycopy (items, 0, selectedItems, 0, itemsCount);
+ if (hasFocus () || (style & SWT.HIDE_SELECTION) == 0) {
+ redraw ();
+ }
+}
+void selectItem (TableItem item, boolean addToSelection) {
+ TableItem[] oldSelectedItems = selectedItems;
+ if (!addToSelection || (style & SWT.SINGLE) != 0) {
+ selectedItems = new TableItem[] {item};
+ if (hasFocus () || (style & SWT.HIDE_SELECTION) == 0) {
+ for (int i = 0; i < oldSelectedItems.length; i++) {
+ if (oldSelectedItems [i] != item) {
+ redrawItem (oldSelectedItems [i].index, true);
+ }
+ }
+ }
+ } else {
+ if (item.isSelected ()) return;
+ selectedItems = new TableItem [selectedItems.length + 1];
+ System.arraycopy (oldSelectedItems, 0, selectedItems, 0, oldSelectedItems.length);
+ selectedItems [selectedItems.length - 1] = item;
+ }
+}
+public void setBackground (Color color) {
+ checkWidget ();
+ if (color == null) color = display.getSystemColor (SWT.COLOR_LIST_BACKGROUND);
+ super.setBackground (color);
+}
+public void setForeground (Color color) {
+ checkWidget ();
+ if (color == null) color = display.getSystemColor (SWT.COLOR_LIST_FOREGROUND);
+ super.setForeground (color);
+}
+/**
+ * Sets the order that the items in the receiver should
+ * be displayed in to the given argument which is described
+ * in terms of the zero-relative ordering of when the items
+ * were added.
+ *
+ * @param order the new order to display the items
+ *
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ * @exception IllegalArgumentException <ul>
+ * <li>ERROR_NULL_ARGUMENT - if the item order is null</li>
+ * <li>ERROR_INVALID_ARGUMENT - if the item order is not the same length as the number of items</li>
+ * </ul>
+ *
+ * @see Table#getColumnOrder()
+ * @see TableColumn#getMoveable()
+ * @see TableColumn#setMoveable(boolean)
+ * @see SWT#Move
+ *
+ * @since 3.1
+ */
+public void setColumnOrder (int [] order) {
+ checkWidget ();
+ if (order == null) error (SWT.ERROR_NULL_ARGUMENT);
+ if (columns.length == 0) {
+ if (order.length != 0) error (SWT.ERROR_INVALID_ARGUMENT);
+ return;
+ }
+ if (order.length != columns.length) error (SWT.ERROR_INVALID_ARGUMENT);
+ boolean reorder = false;
+ boolean [] seen = new boolean [columns.length];
+ int[] oldOrder = getColumnOrder ();
+ for (int i = 0; i < order.length; i++) {
+ int index = order [i];
+ if (index < 0 || index >= columns.length) error (SWT.ERROR_INVALID_RANGE);
+ if (seen [index]) error (SWT.ERROR_INVALID_ARGUMENT);
+ seen [index] = true;
+ if (index != oldOrder [i]) reorder = true;
+ }
+ if (!reorder) return;
+
+ headerHideToolTip ();
+ int[] oldX = new int [columns.length];
+ for (int i = 0; i < columns.length; i++) {
+ oldX [i] = columns [i].getX ();
+ }
+ orderedColumns = new TableColumn [order.length];
+ for (int i = 0; i < order.length; i++) {
+ orderedColumns [i] = columns [order [i]];
+ }
+ for (int i = 0; i < orderedColumns.length; i++) {
+ TableColumn column = orderedColumns [i];
+ if (!column.isDisposed () && column.getX () != oldX [column.getIndex ()]) {
+ column.sendEvent (SWT.Move);
+ }
+ }
+
+ redraw ();
+ if (drawCount <= 0 && header.isVisible ()) header.redraw ();
+}
+void setFocusItem (TableItem item, boolean redrawOldFocus) {
+ if (item == focusItem) return;
+ TableItem oldFocusItem = focusItem;
+ focusItem = item;
+ if (redrawOldFocus && oldFocusItem != null) {
+ redrawItem (oldFocusItem.index, true);
+ }
+}
+public void setFont (Font value) {
+ checkWidget ();
+ Font oldFont = getFont ();
+ super.setFont (value);
+ Font font = getFont ();
+ if (font.equals (oldFont)) return;
+
+ GC gc = new GC (this);
+
+ /* recompute the receiver's cached font height and item height values */
+ fontHeight = gc.getFontMetrics ().getHeight ();
+ setItemHeight (Math.max (fontHeight, imageHeight) + 2 * getCellPadding ());
+ Point headerSize = header.getSize ();
+ int newHeaderHeight = Math.max (fontHeight, headerImageHeight) + 2 * getHeaderPadding ();
+ if (headerSize.y != newHeaderHeight) {
+ header.setSize (headerSize.x, newHeaderHeight);
+ }
+ header.setFont (font);
+
+ /*
+ * Notify all columns and items of the font change so that elements that
+ * use the receiver's font can recompute their cached string widths.
+ */
+ for (int i = 0; i < columns.length; i++) {
+ columns [i].updateFont (gc);
+ }
+ for (int i = 0; i < itemsCount; i++) {
+ items [i].updateFont (gc);
+ }
+
+ gc.dispose ();
+
+ if (drawCount <= 0 && header.isVisible ()) header.redraw ();
+
+ /* update scrollbars */
+ if (columns.length == 0) updateHorizontalBar ();
+ ScrollBar vBar = getVerticalBar ();
+ if (vBar != null) {
+ int thumb = (clientArea.height - getHeaderHeight ()) / itemHeight;
+ vBar.setThumb (thumb);
+ vBar.setPageIncrement (thumb);
+ topIndex = vBar.getSelection ();
+ vBar.setVisible (thumb < vBar.getMaximum ());
+ }
+ redraw ();
+}
+void setHeaderImageHeight (int value) {
+ headerImageHeight = value;
+ Point headerSize = header.getSize ();
+ int newHeaderHeight = Math.max (fontHeight, headerImageHeight) + 2 * getHeaderPadding ();
+ if (headerSize.y != newHeaderHeight) {
+ header.setSize (headerSize.x, newHeaderHeight);
+ }
+}
+/**
+ * Marks the receiver's header as visible if the argument is <code>true</code>,
+ * and marks it invisible otherwise.
+ * <p>
+ * If one of the receiver's ancestors is not visible or some
+ * other condition makes the receiver not visible, marking
+ * it visible may not actually cause it to be displayed.
+ * </p>
+ *
+ * @param show the new visibility state
+ *
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public void setHeaderVisible (boolean value) {
+ checkWidget ();
+ if (header.getVisible () == value) return; /* no change */
+ headerHideToolTip ();
+ header.setVisible (value);
+ updateVerticalBar ();
+ redraw ();
+}
+void setImageHeight (int value) {
+ imageHeight = value;
+ setItemHeight (Math.max (fontHeight, imageHeight) + 2 * getCellPadding ());
+}
+/**
+ * Sets the number of items contained in the receiver.
+ *
+ * @param count the number of items
+ *
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @since 3.0
+ */
+public void setItemCount (int count) {
+ checkWidget ();
+ count = Math.max (0, count);
+ if (count == itemsCount) return;
+ int oldCount = itemsCount;
+ int redrawStart, redrawEnd;
+
+ /* if the new item count is less than the current count then remove all excess items from the end */
+ if (count < itemsCount) {
+ redrawStart = count;
+ redrawEnd = itemsCount - 1;
+ for (int i = count; i < itemsCount; i++) {
+ items [i].dispose (false);
+ }
+
+ int newSelectedCount = 0;
+ for (int i = 0; i < selectedItems.length; i++) {
+ if (!selectedItems [i].isDisposed ()) newSelectedCount++;
+ }
+ if (newSelectedCount != selectedItems.length) {
+ /* one or more selected items have been disposed */
+ TableItem[] newSelectedItems = new TableItem [newSelectedCount];
+ int pos = 0;
+ for (int i = 0; i < selectedItems.length; i++) {
+ TableItem item = selectedItems [i];
+ if (!item.isDisposed ()) {
+ newSelectedItems [pos++] = item;
+ }
+ }
+ selectedItems = newSelectedItems;
+ }
+
+ if (anchorItem != null && anchorItem.isDisposed ()) anchorItem = null;
+ if (lastClickedItem != null && lastClickedItem.isDisposed ()) lastClickedItem = null;
+ if (focusItem != null && focusItem.isDisposed ()) {
+ TableItem newFocusItem = count > 0 ? items [count - 1] : null;
+ setFocusItem (newFocusItem, false);
+ }
+ itemsCount = count;
+ if (columns.length == 0) updateHorizontalBar ();
+ } else {
+ redrawStart = itemsCount;
+ redrawEnd = count - 1;
+ TableItem[] newItems = new TableItem [count];
+ System.arraycopy (items, 0, newItems, 0, itemsCount);
+ items = newItems;
+ for (int i = itemsCount; i < count; i++) {
+ items [i] = new TableItem (this, SWT.NONE, i, false);
+ itemsCount++;
+ }
+ if (oldCount == 0) focusItem = items [0];
+ }
+
+ updateVerticalBar ();
+ /*
+ * If this is the focus control and the item count is going from 0->!0 or !0->0 then the
+ * receiver must be redrawn to ensure that its boundary focus ring is updated.
+ */
+ if ((oldCount == 0 || itemsCount == 0) && isFocusControl ()) {
+ redraw ();
+ return;
+ }
+ redrawItems (redrawStart, redrawEnd, false);
+}
+boolean setItemHeight (int value) {
+ boolean update = !customHeightSet || itemHeight < value;
+ if (update) itemHeight = value;
+ return update;
+}
+/**
+ * Marks the receiver's lines as visible if the argument is <code>true</code>,
+ * and marks it invisible otherwise. Note that some platforms draw grid lines
+ * while others may draw alternating row colors.
+ * <p>
+ * If one of the receiver's ancestors is not visible or some
+ * other condition makes the receiver not visible, marking
+ * it visible may not actually cause it to be displayed.
+ * </p>
+ *
+ * @param show the new visibility state
+ *
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public void setLinesVisible (boolean value) {
+ checkWidget ();
+ if (linesVisible == value) return; /* no change */
+ linesVisible = value;
+ redraw ();
+}
+public void setMenu (Menu menu) {
+ super.setMenu (menu);
+ header.setMenu (menu);
+}
+public void setRedraw (boolean value) {
+ checkWidget();
+ if (value) {
+ if (--drawCount == 0) {
+ if (items.length - itemsCount > 3) {
+ TableItem[] newItems = new TableItem [itemsCount];
+ System.arraycopy (items, 0, newItems, 0, itemsCount);
+ items = newItems;
+ }
+ updateVerticalBar ();
+ updateHorizontalBar ();
+ }
+ } else {
+ drawCount++;
+ }
+ super.setRedraw (value);
+ header.setRedraw (value);
+}
+/**
+ * Sets the receiver's selection to the given item.
+ * The current selection is cleared before the new item is selected.
+ * <p>
+ * If the item is not in the receiver, then it is ignored.
+ * </p>
+ *
+ * @param item the item to select
+ *
+ * @exception IllegalArgumentException <ul>
+ * <li>ERROR_NULL_ARGUMENT - if the item is null</li>
+ * <li>ERROR_INVALID_ARGUMENT - if the item has been disposed</li>
+ * </ul>
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @since 3.2
+ */
+public void setSelection (TableItem item) {
+ checkWidget ();
+ if (item == null) error (SWT.ERROR_NULL_ARGUMENT);
+ setSelection (new TableItem[] {item}, true);
+}
+/**
+ * Sets the receiver's selection to be the given array of items.
+ * The current selection is cleared before the new items are selected.
+ * <p>
+ * Items that are not in the receiver are ignored.
+ * If the receiver is single-select and multiple items are specified,
+ * then all items are ignored.
+ * </p>
+ *
+ * @param items the array of items
+ *
+ * @exception IllegalArgumentException <ul>
+ * <li>ERROR_NULL_ARGUMENT - if the array of items is null</li>
+ * <li>ERROR_INVALID_ARGUMENT - if one of the items has been disposed</li>
+ * </ul>
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @see Table#deselectAll()
+ * @see Table#select(int[])
+ * @see Table#setSelection(int[])
+ */
+public void setSelection (TableItem[] items) {
+ checkWidget ();
+ if (items == null) error (SWT.ERROR_NULL_ARGUMENT);
+ setSelection (items, true);
+}
+void setSelection (TableItem[] items, boolean updateViewport) {
+ if (items.length == 0 || ((style & SWT.SINGLE) != 0 && items.length > 1)) {
+ deselectAll ();
+ return;
+ }
+ TableItem[] oldSelection = selectedItems;
+
+ /* remove null and duplicate items */
+ int index = 0;
+ selectedItems = new TableItem [items.length]; /* assume all valid items */
+ for (int i = 0; i < items.length; i++) {
+ TableItem item = items [i];
+ if (item != null && item.parent == this && !item.isSelected ()) {
+ selectedItems [index++] = item;
+ }
+ }
+ if (index != items.length) {
+ /* an invalid item was provided so resize the array accordingly */
+ TableItem[] temp = new TableItem [index];
+ System.arraycopy (selectedItems, 0, temp, 0, index);
+ selectedItems = temp;
+ }
+ if (selectedItems.length == 0) { /* no valid items */
+ deselectAll ();
+ return;
+ }
+
+ if (hasFocus () || (style & SWT.HIDE_SELECTION) == 0) {
+ for (int i = 0; i < oldSelection.length; i++) {
+ if (!oldSelection [i].isSelected ()) {
+ redrawItem (oldSelection [i].index, true);
+ }
+ }
+ for (int i = 0; i < selectedItems.length; i++) {
+ redrawItem (selectedItems [i].index, true);
+ }
+ }
+ if (updateViewport) {
+ showItem (selectedItems [0]);
+ setFocusItem (selectedItems [0], true);
+ }
+}
+/**
+ * Sets the column used by the sort indicator for the receiver. A null
+ * value will clear the sort indicator. The current sort column is cleared
+ * before the new column is set.
+ *
+ * @param column the column used by the sort indicator or <code>null</code>
+ *
+ * @exception IllegalArgumentException <ul>
+ * <li>ERROR_INVALID_ARGUMENT - if the column is disposed</li>
+ * </ul>
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @since 3.2
+ */
+public void setSortColumn (TableColumn column) {
+ checkWidget ();
+ if (column != null && column.isDisposed ()) error (SWT.ERROR_INVALID_ARGUMENT);
+ if (column == sortColumn) return;
+ if (sortColumn != null && !sortColumn.isDisposed ()) {
+ sortColumn.setSortDirection (SWT.NONE);
+ }
+ sortColumn = column;
+ if (sortColumn != null) {
+ sortColumn.setSortDirection (sortDirection);
+ }
+}
+/**
+ * Sets the direction of the sort indicator for the receiver. The value
+ * can be one of <code>UP</code>, <code>DOWN</code> or <code>NONE</code>.
+ *
+ * @param direction the direction of the sort indicator
+ *
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @since 3.2
+ */
+public void setSortDirection (int direction) {
+ checkWidget ();
+ if (direction != SWT.UP && direction != SWT.DOWN && direction != SWT.NONE) return;
+ sortDirection = direction;
+ if (sortColumn == null || sortColumn.isDisposed ()) return;
+ sortColumn.setSortDirection (sortDirection);
+}
+/**
+ * Selects the item at the given zero-relative index in the receiver.
+ * The current selection is first cleared, then the new item is selected.
+ *
+ * @param index the index of the item to select
+ *
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @see Table#deselectAll()
+ * @see Table#select(int)
+ */
+public void setSelection (int index) {
+ checkWidget ();
+ deselectAll ();
+ if (!(0 <= index && index < itemsCount)) return;
+ selectItem (items [index], false);
+ setFocusItem (items [index], true);
+ redrawItem (index, true);
+ showSelection ();
+}
+/**
+ * Selects the items in the range specified by the given zero-relative
+ * indices in the receiver. The range of indices is inclusive.
+ * The current selection is cleared before the new items are selected.
+ * <p>
+ * Indices that are out of range are ignored and no items will be selected
+ * if start is greater than end.
+ * If the receiver is single-select and there is more than one item in the
+ * given range, then all indices are ignored.
+ * </p>
+ *
+ * @param start the start index of the items to select
+ * @param end the end index of the items to select
+ *
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @see Table#deselectAll()
+ * @see Table#select(int,int)
+ */
+public void setSelection (int start, int end) {
+ checkWidget ();
+ deselectAll ();
+ if (end < 0 || start > end || ((style & SWT.SINGLE) != 0 && start != end)) return;
+ if (itemsCount == 0 || start >= itemsCount) return;
+ start = Math.max (0, start);
+ end = Math.min (end, itemsCount - 1);
+ select (start, end);
+ setFocusItem (items [start], true);
+ showSelection ();
+}
+/**
+ * Selects the items at the given zero-relative indices in the receiver.
+ * The current selection is cleared before the new items are selected.
+ * <p>
+ * Indices that are out of range and duplicate indices are ignored.
+ * If the receiver is single-select and multiple indices are specified,
+ * then all indices are ignored.
+ * </p>
+ *
+ * @param indices the indices of the items to select
+ *
+ * @exception IllegalArgumentException <ul>
+ * <li>ERROR_NULL_ARGUMENT - if the array of indices is null</li>
+ * </ul>
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @see Table#deselectAll()
+ * @see Table#select(int[])
+ */
+public void setSelection (int [] indices) {
+ checkWidget ();
+ if (indices == null) error (SWT.ERROR_NULL_ARGUMENT);
+ deselectAll ();
+ int length = indices.length;
+ if (length == 0 || ((style & SWT.SINGLE) != 0 && length > 1)) return;
+ select (indices);
+ int focusIndex = -1;
+ for (int i = 0; i < indices.length && focusIndex == -1; i++) {
+ if (0 <= indices [i] && indices [i] < itemsCount) {
+ focusIndex = indices [i];
+ }
+ }
+ if (focusIndex != -1) setFocusItem (items [focusIndex], true);
+ showSelection ();
+}
+/**
+ * Sets the zero-relative index of the item which is currently
+ * at the top of the receiver. This index can change when items
+ * are scrolled or new items are added and removed.
+ *
+ * @param index the index of the top item
+ *
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public void setTopIndex (int index) {
+ checkWidget ();
+ if (!(0 <= index && index < itemsCount)) return;
+ int visibleItemCount = (clientArea.height - getHeaderHeight ()) / itemHeight;
+ if (itemsCount <= visibleItemCount) return;
+ index = Math.min (index, itemsCount - visibleItemCount);
+ if (index == topIndex) return;
+
+ update ();
+ int change = topIndex - index;
+ topIndex = index;
+ ScrollBar vBar = getVerticalBar ();
+ if (vBar != null) vBar.setSelection (topIndex);
+ if (drawCount <= 0) {
+ GC gc = new GC (this);
+ gc.copyArea (0, 0, clientArea.width, clientArea.height, 0, change * itemHeight);
+ gc.dispose ();
+ }
+}
+/**
+ * Shows the column. If the column is already showing in the receiver,
+ * this method simply returns. Otherwise, the columns are scrolled until
+ * the column is visible.
+ *
+ * @param column the column to be shown
+ *
+ * @exception IllegalArgumentException <ul>
+ * <li>ERROR_NULL_ARGUMENT - if the column is null</li>
+ * <li>ERROR_INVALID_ARGUMENT - if the column has been disposed</li>
+ * </ul>
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @since 3.0
+ */
+public void showColumn (TableColumn column) {
+ checkWidget ();
+ if (column == null) error (SWT.ERROR_NULL_ARGUMENT);
+ if (column.isDisposed ()) error(SWT.ERROR_INVALID_ARGUMENT);
+ if (column.parent != this) return;
+
+ int x = column.getX ();
+ int rightX = x + column.width;
+ if (0 <= x && rightX <= clientArea.width) return; /* column is fully visible */
+
+ headerHideToolTip ();
+ int absX = 0; /* the X of the column irrespective of the horizontal scroll */
+ TableColumn[] orderedColumns = getOrderedColumns ();
+ for (int i = 0; i < column.getOrderIndex (); i++) {
+ absX += orderedColumns [i].width;
+ }
+ if (x < clientArea.x) { /* column is to left of viewport */
+ horizontalOffset = absX;
+ } else {
+ horizontalOffset = absX + column.width - clientArea.width;
+ }
+ ScrollBar hBar = getHorizontalBar ();
+ if (hBar != null) hBar.setSelection (horizontalOffset);
+ redraw ();
+ if (drawCount <= 0 && header.isVisible ()) header.redraw ();
+}
+/**
+ * Shows the item. If the item is already showing in the receiver,
+ * this method simply returns. Otherwise, the items are scrolled until
+ * the item is visible.
+ *
+ * @param item the item to be shown
+ *
+ * @exception IllegalArgumentException <ul>
+ * <li>ERROR_NULL_ARGUMENT - if the item is null</li>
+ * <li>ERROR_INVALID_ARGUMENT - if the item has been disposed</li>
+ * </ul>
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @see Table#showSelection()
+ */
+public void showItem (TableItem item) {
+ checkWidget ();
+ if (item == null) error (SWT.ERROR_NULL_ARGUMENT);
+ if (item.isDisposed ()) error (SWT.ERROR_INVALID_ARGUMENT);
+ if (item.parent != this) return;
+
+ int index = item.index;
+ int visibleItemCount = (clientArea.height - getHeaderHeight ()) / itemHeight;
+ /* nothing to do if item is already in viewport */
+ if (topIndex <= index && index < topIndex + visibleItemCount) return;
+
+ if (index <= topIndex) {
+ /* item is above current viewport, so show on top */
+ setTopIndex (item.index);
+ } else {
+ /* item is below current viewport, so show on bottom */
+ visibleItemCount = Math.max (visibleItemCount, 1); /* item to show should be top item */
+ setTopIndex (Math.min (index - visibleItemCount + 1, itemsCount - 1));
+ }
+}
+/**
+ * Shows the selection. If the selection is already showing in the receiver,
+ * this method simply returns. Otherwise, the items are scrolled until
+ * the selection is visible.
+ *
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @see Table#showItem(TableItem)
+ */
+public void showSelection () {
+ checkWidget ();
+ if (selectedItems.length == 0) return;
+ showItem (selectedItems [0]);
+}
+void sortDescent (int [] items) {
+ /* Shell Sort from K&R, pg 108 */
+ int length = items.length;
+ for (int gap = length / 2; gap > 0; gap /= 2) {
+ for (int i = gap; i < length; i++) {
+ for (int j = i - gap; j >= 0; j -= gap) {
+ if (items [j] <= items [j + gap]) {
+ int swap = items [j];
+ items [j] = items [j + gap];
+ items [j + gap] = swap;
+ }
+ }
+ }
+ }
+}
+void sortAscent (int [] items) {
+ /* Shell Sort from K&R, pg 108 */
+ int length = items.length;
+ for (int gap = length / 2; gap > 0; gap /= 2) {
+ for (int i = gap; i < length; i++) {
+ for (int j = i - gap; j >= 0; j -= gap) {
+ if (items [j] >= items [j + gap]) {
+ int swap = items [j];
+ items [j] = items [j + gap];
+ items [j + gap] = swap;
+ }
+ }
+ }
+ }
+}
+void sortAscent (TableItem [] items) {
+ /* Shell Sort from K&R, pg 108 */
+ int length = items.length;
+ for (int gap = length / 2; gap > 0; gap /= 2) {
+ for (int i = gap; i < length; i++) {
+ for (int j = i - gap; j >= 0; j -= gap) {
+ if (items [j].index >= items [j + gap].index) {
+ TableItem swap = items [j];
+ items [j] = items [j + gap];
+ items [j + gap] = swap;
+ }
+ }
+ }
+ }
+}
+void updateColumnWidth (TableColumn column, int width) {
+ headerHideToolTip ();
+ int oldWidth = column.width;
+ int columnX = column.getX ();
+ int x = columnX + oldWidth - 1; /* -1 ensures that grid line is included */
+
+ update ();
+ GC gc = new GC (this);
+ gc.copyArea (x, 0, clientArea.width - x, clientArea.height, columnX + width - 1, 0); /* dest x -1 offsets x's -1 above */
+ if (width > oldWidth) {
+ /* column width grew */
+ int change = width - oldWidth + 1; /* +1 offsets x's -1 above */
+ /* -1/+1 below ensure that right bound of selection redraws correctly in column */
+ redraw (x - 1, 0, change + 1, clientArea.height, false);
+ } else {
+ int change = oldWidth - width + 1; /* +1 offsets x's -1 above */
+ redraw (clientArea.width - change, 0, change, clientArea.height, false);
+ }
+ /* the focus box must be repainted because its stipple may become shifted as a result of its new width */
+ if (focusItem != null) redrawItem (focusItem.index, true);
+
+ GC headerGC = new GC (header);
+ if (drawCount <= 0 && header.getVisible ()) {
+ Rectangle headerBounds = header.getClientArea ();
+ header.update ();
+ x -= 1; /* -1 ensures that full header column separator is included */
+ headerGC.copyArea (x, 0, headerBounds.width - x, headerBounds.height, columnX + width - 2, 0); /* dest x -2 offsets x's -1s above */
+ if (width > oldWidth) {
+ /* column width grew */
+ int change = width - oldWidth + 2; /* +2 offsets x's -1s above */
+ header.redraw (x, 0, change, headerBounds.height, false);
+ } else {
+ int change = oldWidth - width + 2; /* +2 offsets x's -1s above */
+ header.redraw (headerBounds.width - change, 0, change, headerBounds.height, false);
+ }
+ }
+
+ column.width = width;
+
+ /*
+ * Notify column and all items of column width change so that display labels
+ * can be recomputed if needed.
+ */
+ column.updateWidth (headerGC);
+ headerGC.dispose ();
+ for (int i = 0; i < itemsCount; i++) {
+ items [i].updateColumnWidth (column, gc);
+ }
+ gc.dispose ();
+
+ int maximum = 0;
+ for (int i = 0; i < columns.length; i++) {
+ maximum += columns [i].width;
+ }
+ ScrollBar hBar = getHorizontalBar ();
+ if (hBar != null) {
+ hBar.setMaximum (Math.max (1, maximum)); /* setting a value of 0 here is ignored */
+ if (hBar.getThumb () != clientArea.width) {
+ hBar.setThumb (clientArea.width);
+ hBar.setPageIncrement (clientArea.width);
+ }
+ int oldHorizontalOffset = horizontalOffset; /* hBar.setVisible() can modify horizontalOffset */
+ hBar.setVisible (clientArea.width < maximum);
+ int selection = hBar.getSelection ();
+ if (selection != oldHorizontalOffset) {
+ horizontalOffset = selection;
+ redraw ();
+ if (drawCount <= 0 && header.getVisible ()) header.redraw ();
+ }
+ }
+
+ column.sendEvent (SWT.Resize);
+ TableColumn[] orderedColumns = getOrderedColumns ();
+ for (int i = column.getOrderIndex () + 1; i < orderedColumns.length; i++) {
+ if (!orderedColumns [i].isDisposed ()) {
+ orderedColumns [i].sendEvent (SWT.Move);
+ }
+ }
+
+ if (itemsCount == 0) redraw (); /* ensure that static focus rectangle updates properly */
+}
+/*
+ * This is a naive implementation that computes the value from scratch.
+ */
+void updateHorizontalBar () {
+ if (drawCount > 0) return;
+ ScrollBar hBar = getHorizontalBar ();
+ if (hBar == null) return;
+
+ int maxX = 0;
+ if (columns.length > 0) {
+ for (int i = 0; i < columns.length; i++) {
+ maxX += columns [i].width;
+ }
+ } else {
+ for (int i = 0; i < itemsCount; i++) {
+ Rectangle itemBounds = items [i].getCellBounds (0);
+ maxX = Math.max (maxX, itemBounds.x + itemBounds.width + horizontalOffset);
+ }
+ }
+
+ int clientWidth = clientArea.width;
+ if (maxX != hBar.getMaximum ()) {
+ hBar.setMaximum (Math.max (1, maxX)); /* setting a value of 0 here is ignored */
+ }
+ int thumb = Math.min (clientWidth, maxX);
+ if (thumb != hBar.getThumb ()) {
+ hBar.setThumb (thumb);
+ hBar.setPageIncrement (thumb);
+ }
+ hBar.setVisible (clientWidth < maxX);
+
+ /* reclaim any space now left on the right */
+ if (maxX < horizontalOffset + thumb) {
+ horizontalOffset = maxX - thumb;
+ hBar.setSelection (horizontalOffset);
+ redraw ();
+ } else {
+ int selection = hBar.getSelection ();
+ if (selection != horizontalOffset) {
+ horizontalOffset = selection;
+ redraw ();
+ }
+ }
+}
+/*
+ * Update the horizontal bar, if needed, in response to an item change (eg.- created,
+ * disposed, expanded, etc.). newRightX is the new rightmost X value of the item,
+ * and rightXchange is the change that led to the item's rightmost X value becoming
+ * newRightX (so oldRightX + rightXchange = newRightX)
+ */
+void updateHorizontalBar (int newRightX, int rightXchange) {
+ if (drawCount > 0) return;
+ ScrollBar hBar = getHorizontalBar ();
+ if (hBar == null) return;
+
+ newRightX += horizontalOffset;
+ int barMaximum = hBar.getMaximum ();
+ if (newRightX > barMaximum) { /* item has extended beyond previous maximum */
+ hBar.setMaximum (newRightX);
+ int clientAreaWidth = clientArea.width;
+ int thumb = Math.min (newRightX, clientAreaWidth);
+ hBar.setThumb (thumb);
+ hBar.setPageIncrement (thumb);
+ hBar.setVisible (clientAreaWidth <= newRightX);
+ return;
+ }
+
+ int previousRightX = newRightX - rightXchange;
+ if (previousRightX != barMaximum) {
+ /* this was not the rightmost item, so just check for client width change */
+ int clientAreaWidth = clientArea.width;
+ int thumb = Math.min (barMaximum, clientAreaWidth);
+ hBar.setThumb (thumb);
+ hBar.setPageIncrement (thumb);
+ hBar.setVisible (clientAreaWidth <= barMaximum);
+ return;
+ }
+ updateHorizontalBar (); /* must search for the new rightmost item */
+}
+void updateVerticalBar () {
+ if (drawCount > 0) return;
+ ScrollBar vBar = getVerticalBar ();
+ if (vBar == null) return;
+
+ int pageSize = (clientArea.height - getHeaderHeight ()) / itemHeight;
+ int maximum = Math.max (1, itemsCount); /* setting a value of 0 here is ignored */
+ if (maximum != vBar.getMaximum ()) {
+ vBar.setMaximum (maximum);
+ }
+ int thumb = Math.min (pageSize, maximum);
+ if (thumb != vBar.getThumb ()) {
+ vBar.setThumb (thumb);
+ vBar.setPageIncrement (thumb);
+ }
+ vBar.setVisible (pageSize < maximum);
+
+ /* reclaim any space now left on the bottom */
+ if (maximum < topIndex + thumb) {
+ topIndex = maximum - thumb;
+ vBar.setSelection (topIndex);
+ redraw ();
+ } else {
+ int selection = vBar.getSelection ();
+ if (selection != topIndex) {
+ topIndex = selection;
+ redraw ();
+ }
+ }
+}
+}
diff --git a/bundles/org.eclipse.swt/Eclipse SWT/emulated/treetable/org/eclipse/swt/widgets/TableColumn.java b/bundles/org.eclipse.swt/Eclipse SWT/emulated/treetable/org/eclipse/swt/widgets/TableColumn.java
new file mode 100644
index 0000000000..7ef48a0fdd
--- /dev/null
+++ b/bundles/org.eclipse.swt/Eclipse SWT/emulated/treetable/org/eclipse/swt/widgets/TableColumn.java
@@ -0,0 +1,762 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2009 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.swt.widgets;
+
+import org.eclipse.swt.*;
+import org.eclipse.swt.events.*;
+import org.eclipse.swt.graphics.*;
+
+/**
+ * Instances of this class represent a column in a table widget.
+ * <p><dl>
+ * <dt><b>Styles:</b></dt>
+ * <dd>LEFT, RIGHT, CENTER</dd>
+ * <dt><b>Events:</b></dt>
+ * <dd> Move, Resize, Selection</dd>
+ * </dl>
+ * </p><p>
+ * Note: Only one of the styles LEFT, RIGHT and CENTER may be specified.
+ * </p><p>
+ * IMPORTANT: This class is <em>not</em> intended to be subclassed.
+ * </p>
+ *
+ * @see <a href="http://www.eclipse.org/swt/snippets/#table">Table, TableItem, TableColumn snippets</a>
+ * @see <a href="http://www.eclipse.org/swt/">Sample code and further information</a>
+ * @noextend This class is not intended to be subclassed by clients.
+ */
+public class TableColumn extends Item {
+ Table parent;
+ String displayText = "";
+ int width;
+ boolean moveable, resizable = true;
+ int sort = SWT.NONE;
+ String toolTipText;
+
+/**
+ * Constructs a new instance of this class given its parent
+ * (which must be a <code>Table</code>) and a style value
+ * describing its behavior and appearance. The item is added
+ * to the end of the items maintained by its parent.
+ * <p>
+ * The style value is either one of the style constants defined in
+ * class <code>SWT</code> which is applicable to instances of this
+ * class, or must be built by <em>bitwise OR</em>'ing together
+ * (that is, using the <code>int</code> "|" operator) two or more
+ * of those <code>SWT</code> style constants. The class description
+ * lists the style constants that are applicable to the class.
+ * Style bits are also inherited from superclasses.
+ * </p>
+ *
+ * @param parent a composite control which will be the parent of the new instance (cannot be null)
+ * @param style the style of control to construct
+ *
+ * @exception IllegalArgumentException <ul>
+ * <li>ERROR_NULL_ARGUMENT - if the parent is null</li>
+ * </ul>
+ * @exception SWTException <ul>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the parent</li>
+ * <li>ERROR_INVALID_SUBCLASS - if this class is not an allowed subclass</li>
+ * </ul>
+ *
+ * @see SWT#LEFT
+ * @see SWT#RIGHT
+ * @see SWT#CENTER
+ * @see Widget#checkSubclass
+ * @see Widget#getStyle
+ */
+public TableColumn (Table parent, int style) {
+ this (parent, style, checkNull (parent).columns.length);
+}
+/**
+ * Constructs a new instance of this class given its parent
+ * (which must be a <code>Table</code>), a style value
+ * describing its behavior and appearance, and the index
+ * at which to place it in the items maintained by its parent.
+ * <p>
+ * The style value is either one of the style constants defined in
+ * class <code>SWT</code> which is applicable to instances of this
+ * class, or must be built by <em>bitwise OR</em>'ing together
+ * (that is, using the <code>int</code> "|" operator) two or more
+ * of those <code>SWT</code> style constants. The class description
+ * lists the style constants that are applicable to the class.
+ * Style bits are also inherited from superclasses.
+ * </p>
+ * <p>
+ * Note that due to a restriction on some platforms, the first column
+ * is always left aligned.
+ * </p>
+ * @param parent a composite control which will be the parent of the new instance (cannot be null)
+ * @param style the style of control to construct
+ * @param index the zero-relative index to store the receiver in its parent
+ *
+ * @exception IllegalArgumentException <ul>
+ * <li>ERROR_NULL_ARGUMENT - if the parent is null</li>
+ * <li>ERROR_INVALID_RANGE - if the index is not between 0 and the number of elements in the parent (inclusive)</li>
+ * </ul>
+ * @exception SWTException <ul>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the parent</li>
+ * <li>ERROR_INVALID_SUBCLASS - if this class is not an allowed subclass</li>
+ * </ul>
+ *
+ * @see SWT#LEFT
+ * @see SWT#RIGHT
+ * @see SWT#CENTER
+ * @see Widget#checkSubclass
+ * @see Widget#getStyle
+ */
+public TableColumn (Table parent, int style, int index) {
+ super (parent, checkStyle (style), index);
+ if (!(0 <= index && index <= parent.columns.length)) error (SWT.ERROR_INVALID_RANGE);
+ this.parent = parent;
+ parent.createItem (this, index);
+}
+/**
+ * Adds the listener to the collection of listeners who will
+ * be notified when the control is moved or resized, by sending
+ * it one of the messages defined in the <code>ControlListener</code>
+ * interface.
+ *
+ * @param listener the listener which should be notified
+ *
+ * @exception IllegalArgumentException <ul>
+ * <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
+ * </ul>
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @see ControlListener
+ * @see #removeControlListener
+ */
+public void addControlListener (ControlListener listener) {
+ checkWidget ();
+ if (listener == null) error (SWT.ERROR_NULL_ARGUMENT);
+ TypedListener typedListener = new TypedListener (listener);
+ addListener (SWT.Resize, typedListener);
+ addListener (SWT.Move, typedListener);
+}
+/**
+ * Adds the listener to the collection of listeners who will
+ * be notified when the control is selected by the user, by sending
+ * it one of the messages defined in the <code>SelectionListener</code>
+ * interface.
+ * <p>
+ * <code>widgetSelected</code> is called when the column header is selected.
+ * <code>widgetDefaultSelected</code> is not called.
+ * </p>
+ *
+ * @param listener the listener which should be notified when the control is selected by the user
+ *
+ * @exception IllegalArgumentException <ul>
+ * <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
+ * </ul>
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @see SelectionListener
+ * @see #removeSelectionListener
+ * @see SelectionEvent
+ */
+public void addSelectionListener (SelectionListener listener) {
+ checkWidget ();
+ if (listener == null) error (SWT.ERROR_NULL_ARGUMENT);
+ TypedListener typedListener = new TypedListener (listener);
+ addListener (SWT.Selection, typedListener);
+ addListener (SWT.DefaultSelection, typedListener);
+}
+static Table checkNull (Table table) {
+ if (table == null) SWT.error (SWT.ERROR_NULL_ARGUMENT);
+ return table;
+}
+static int checkStyle (int style) {
+ return checkBits (style, SWT.LEFT, SWT.CENTER, SWT.RIGHT, 0, 0, 0);
+}
+protected void checkSubclass () {
+ if (!isValidSubclass ()) error (SWT.ERROR_INVALID_SUBCLASS);
+}
+void computeDisplayText (GC gc) {
+ int availableWidth = width - 2 * parent.getHeaderPadding ();
+ if (image != null) {
+ availableWidth -= image.getBounds ().width;
+ availableWidth -= Table.MARGIN_IMAGE;
+ }
+ if (sort != SWT.NONE) {
+ availableWidth -= parent.arrowBounds.width;
+ availableWidth -= Table.MARGIN_IMAGE;
+ }
+ String text = this.text;
+ int textWidth = gc.textExtent (text, SWT.DRAW_MNEMONIC).x;
+ if (textWidth <= availableWidth) {
+ displayText = text;
+ return;
+ }
+
+ /* Ellipsis will be needed, so subtract their width from the available text width */
+ int ellipsisWidth = gc.stringExtent (Table.ELLIPSIS).x;
+ availableWidth -= ellipsisWidth;
+ if (availableWidth <= 0) {
+ displayText = Table.ELLIPSIS;
+ return;
+ }
+
+ /* Make initial guess. */
+ int index = Math.min (availableWidth / gc.getFontMetrics ().getAverageCharWidth (), text.length ());
+ textWidth = gc.textExtent (text.substring (0, index), SWT.DRAW_MNEMONIC).x;
+
+ /* Initial guess is correct. */
+ if (availableWidth == textWidth) {
+ displayText = text.substring (0, index) + Table.ELLIPSIS;
+ return;
+ }
+
+ /* Initial guess is too high, so reduce until fit is found. */
+ if (availableWidth < textWidth) {
+ do {
+ index--;
+ if (index < 0) {
+ displayText = Table.ELLIPSIS;
+ return;
+ }
+ text = text.substring (0, index);
+ textWidth = gc.textExtent (text, SWT.DRAW_MNEMONIC).x;
+ } while (availableWidth < textWidth);
+ displayText = text + Table.ELLIPSIS;
+ return;
+ }
+
+ /* Initial guess is too low, so increase until overrun is found. */
+ while (textWidth < availableWidth) {
+ index++;
+ textWidth = gc.textExtent (text.substring (0, index), SWT.DRAW_MNEMONIC).x;
+ }
+ displayText = text.substring (0, index - 1) + Table.ELLIPSIS;
+}
+public void dispose () {
+ if (isDisposed ()) return;
+ Rectangle parentBounds = parent.clientArea;
+ int x = getX ();
+ int index = getIndex ();
+ int orderIndex = getOrderIndex ();
+ int nextColumnAlignment = parent.columns.length > 1 ? parent.columns [1].getAlignment () : SWT.LEFT;
+ Table parent = this.parent;
+ dispose (true);
+
+ int width = parentBounds.width - x;
+ parent.redraw (x, 0, width, parentBounds.height, false);
+ /*
+ * If column 0 was disposed then the new column 0 must be redrawn if it appears to the
+ * left of the disposed column in the column order AND one the following are true:
+ * - the parent has style CHECK, since these will now appear in the new column 0
+ * - the new column 0 had non-left alignment before the dispose, since the parent will have
+ * changed this to LEFT in the call to dispose(true)
+ */
+ if (index == 0 && ((parent.style & SWT.CHECK) != 0 || nextColumnAlignment != SWT.LEFT)) {
+ if (parent.columns.length > 0) {
+ TableColumn newColumn0 = parent.columns [0];
+ if (newColumn0.getOrderIndex () < orderIndex) {
+ int newColumn0x = newColumn0.getX ();
+ parent.redraw (newColumn0x, 0, newColumn0.width, parentBounds.height, false);
+ /* if the alignment changed then the header text must be repainted with its new alignment */
+ if (nextColumnAlignment != SWT.LEFT && parent.getHeaderVisible () && parent.drawCount <= 0) {
+ parent.header.redraw (newColumn0x, 0, newColumn0.width, parent.header.getClientArea ().height, false);
+ }
+ }
+ }
+ }
+ if (parent.drawCount <= 0 && parent.getHeaderVisible ()) {
+ parent.header.redraw (x, 0, width, parent.getHeaderHeight (), false);
+ }
+}
+void dispose (boolean notifyParent) {
+ super.dispose (); /* super is intentional here */
+ if (notifyParent) parent.destroyItem (this);
+ parent = null;
+}
+/**
+ * Returns a value which describes the position of the
+ * text or image in the receiver. The value will be one of
+ * <code>LEFT</code>, <code>RIGHT</code> or <code>CENTER</code>.
+ *
+ * @return the alignment
+ *
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public int getAlignment () {
+ checkWidget ();
+ if ((style & SWT.CENTER) != 0) return SWT.CENTER;
+ if ((style & SWT.RIGHT) != 0) return SWT.RIGHT;
+ return SWT.LEFT;
+}
+/*
+ * Returns the width of the header's content
+ * (image + text + sort arrow + internal margins)
+ */
+int getContentWidth (GC gc, boolean useDisplayText) {
+ int contentWidth = 0;
+ String text = useDisplayText ? displayText : this.text;
+ if (text.length () > 0) {
+ contentWidth += gc.textExtent (text, SWT.DRAW_MNEMONIC).x;
+ }
+ if (image != null) {
+ contentWidth += image.getBounds ().width;
+ if (text.length () > 0) contentWidth += Table.MARGIN_IMAGE;
+ }
+ if (sort != SWT.NONE) {
+ contentWidth += parent.arrowBounds.width;
+ if (text.length () > 0 || image != null) {
+ contentWidth += Table.MARGIN_IMAGE;
+ }
+ }
+ return contentWidth;
+}
+int getIndex () {
+ TableColumn[] columns = parent.columns;
+ for (int i = 0; i < columns.length; i++) {
+ if (columns [i] == this) return i;
+ }
+ return -1;
+}
+/**
+ * Gets the moveable attribute. A column that is
+ * not moveable cannot be reordered by the user
+ * by dragging the header but may be reordered
+ * by the programmer.
+ *
+ * @return the moveable attribute
+ *
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @see Table#getColumnOrder()
+ * @see Table#setColumnOrder(int[])
+ * @see TableColumn#setMoveable(boolean)
+ * @see SWT#Move
+ *
+ * @since 3.1
+ */
+public boolean getMoveable () {
+ checkWidget ();
+ return moveable;
+}
+int getOrderIndex () {
+ TableColumn[] orderedColumns = parent.orderedColumns;
+ if (orderedColumns == null) return getIndex ();
+ for (int i = 0; i < orderedColumns.length; i++) {
+ if (orderedColumns [i] == this) return i;
+ }
+ return -1;
+}
+/**
+ * Returns the receiver's parent, which must be a <code>Table</code>.
+ *
+ * @return the receiver's parent
+ *
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public Table getParent () {
+ checkWidget ();
+ return parent;
+}
+int getPreferredWidth () {
+ if (!parent.getHeaderVisible ()) return 0;
+ GC gc = new GC (parent);
+ int result = getContentWidth (gc, false);
+ gc.dispose ();
+ return result + 2 * parent.getHeaderPadding ();
+}
+/**
+ * Gets the resizable attribute. A column that is
+ * not resizable cannot be dragged by the user but
+ * may be resized by the programmer.
+ *
+ * @return the resizable attribute
+ *
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public boolean getResizable () {
+ checkWidget ();
+ return resizable;
+}
+/**
+ * Returns the receiver's tool tip text, or null if it has
+ * not been set.
+ *
+ * @return the receiver's tool tip text
+ *
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @since 3.2
+ */
+public String getToolTipText () {
+ checkWidget ();
+ return toolTipText;
+}
+/**
+ * Gets the width of the receiver.
+ *
+ * @return the width
+ *
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public int getWidth () {
+ checkWidget ();
+ return width;
+}
+int getX () {
+ TableColumn[] orderedColumns = parent.getOrderedColumns ();
+ int index = getOrderIndex ();
+ int result = -parent.horizontalOffset;
+ for (int i = 0; i < index; i++) {
+ result += orderedColumns [i].width;
+ }
+ return result;
+}
+/**
+ * Causes the receiver to be resized to its preferred size.
+ * For a composite, this involves computing the preferred size
+ * from its layout, if there is one.
+ *
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ */
+public void pack () {
+ checkWidget ();
+ TableItem[] items = parent.items;
+ int index = getIndex ();
+ int newWidth = getPreferredWidth ();
+ for (int i = 0; i < parent.itemsCount; i++) {
+ int width = items [i].getPreferredWidth (index);
+ /* ensure that receiver and parent were not disposed in a callback */
+ if (parent.isDisposed () || isDisposed ()) return;
+ if (!items [i].isDisposed ()) {
+ newWidth = Math.max (newWidth, width);
+ }
+ }
+ if (newWidth != width) parent.updateColumnWidth (this, newWidth);
+}
+void paint (GC gc) {
+ int padding = parent.getHeaderPadding ();
+
+ int x = getX ();
+ int startX = x + padding;
+ if ((style & SWT.LEFT) == 0) {
+ int contentWidth = getContentWidth (gc, true);
+ if ((style & SWT.RIGHT) != 0) {
+ startX = Math.max (startX, x + width - padding - contentWidth);
+ } else { /* SWT.CENTER */
+ startX = Math.max (startX, x + (width - contentWidth) / 2);
+ }
+ }
+ int headerHeight = parent.getHeaderHeight ();
+
+ /* restrict the clipping region to the header cell */
+ gc.setClipping (
+ x + padding,
+ padding,
+ width - 2 * padding,
+ headerHeight - 2 * padding);
+
+ if (image != null) {
+ Rectangle imageBounds = image.getBounds ();
+ int drawHeight = Math.min (imageBounds.height, headerHeight - 2 * padding);
+ gc.drawImage (
+ image,
+ 0, 0,
+ imageBounds.width, imageBounds.height,
+ startX, (headerHeight - drawHeight) / 2,
+ imageBounds.width, drawHeight);
+ startX += imageBounds.width + Table.MARGIN_IMAGE;
+ }
+ if (displayText.length () > 0) {
+ gc.setForeground (display.getSystemColor (SWT.COLOR_BLACK));
+ int fontHeight = parent.fontHeight;
+ gc.drawText (displayText, startX, (headerHeight - fontHeight) / 2, SWT.DRAW_MNEMONIC);
+ startX += gc.textExtent (displayText, SWT.DRAW_MNEMONIC).x + Table.MARGIN_IMAGE;
+ }
+ if (sort != SWT.NONE) {
+ Image image = sort == SWT.DOWN ? parent.getArrowDownImage () : parent.getArrowUpImage ();
+ int y = (headerHeight - parent.arrowBounds.height) / 2;
+ gc.drawImage (image, startX, y);
+ }
+}
+/**
+ * Removes the listener from the collection of listeners who will
+ * be notified when the control is moved or resized.
+ *
+ * @param listener the listener which should no longer be notified
+ *
+ * @exception IllegalArgumentException <ul>
+ * <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
+ * </ul>
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @see ControlListener
+ * @see #addControlListener
+ */
+public void removeControlListener (ControlListener listener) {
+ checkWidget ();
+ if (listener == null) error (SWT.ERROR_NULL_ARGUMENT);
+ if (eventTable == null) return;
+ eventTable.unhook (SWT.Move, listener);
+ eventTable.unhook (SWT.Resize, listener);
+}
+/**
+ * Removes the listener from the collection of listeners who will
+ * be notified when the control is selected by the user.
+ *
+ * @param listener the listener which should no longer be notified
+ *
+ * @exception IllegalArgumentException <ul>
+ * <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
+ * </ul>
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @see SelectionListener
+ * @see #addSelectionListener
+ */
+public void removeSelectionListener (SelectionListener listener) {
+ checkWidget ();
+ if (listener == null) error (SWT.ERROR_NULL_ARGUMENT);
+ removeListener (SWT.Selection, listener);
+ removeListener (SWT.DefaultSelection, listener);
+}
+/**
+ * Controls how text and images will be displayed in the receiver.
+ * The argument should be one of <code>LEFT</code>, <code>RIGHT</code>
+ * or <code>CENTER</code>.
+ * <p>
+ * Note that due to a restriction on some platforms, the first column
+ * is always left aligned.
+ * </p>
+ * @param alignment the new alignment
+ *
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public void setAlignment (int alignment) {
+ checkWidget ();
+ if ((alignment & (SWT.LEFT | SWT.RIGHT | SWT.CENTER)) == 0) return;
+ int index = getIndex ();
+ if (index == -1 || index == 0) return; /* column 0 can only have left-alignment */
+ alignment = checkBits (alignment, SWT.LEFT, SWT.CENTER, SWT.RIGHT, 0, 0, 0);
+ if ((style & alignment) != 0) return; /* same value */
+ style &= ~(SWT.LEFT | SWT.CENTER | SWT.RIGHT);
+ style |= alignment;
+ int x = getX ();
+ parent.redraw (x, 0, width, parent.clientArea.height, false);
+ if (parent.drawCount <= 0 && parent.getHeaderVisible ()) {
+ /* don't damage the header's drawn borders */
+ parent.header.redraw (x, 1, width - 2, parent.getHeaderHeight () - 3, false);
+ }
+}
+public void setImage (Image value) {
+ checkWidget ();
+ if (value == image) return;
+ if (value != null && value.equals (image)) return; /* same value */
+ super.setImage (value);
+
+ /* An image width change may affect the space available for the column's displayText. */
+ GC gc = new GC (parent);
+ computeDisplayText (gc);
+ gc.dispose ();
+
+ /*
+ * If this is the first image being put into the header then the header
+ * height may be adjusted, in which case a full redraw is needed.
+ */
+ if (parent.headerImageHeight == 0) {
+ int oldHeaderHeight = parent.getHeaderHeight ();
+ parent.setHeaderImageHeight (value.getBounds ().height);
+ if (oldHeaderHeight != parent.getHeaderHeight ()) {
+ /* parent header height changed */
+ if (parent.drawCount <= 0 && parent.getHeaderVisible ()) {
+ parent.header.redraw ();
+ }
+ parent.redraw ();
+ return;
+ }
+ }
+
+ if (parent.drawCount <= 0 && parent.getHeaderVisible ()) {
+ /* don't damage the header's drawn borders */
+ parent.header.redraw (getX (), 1, width - 2, parent.getHeaderHeight () - 3, false);
+ }
+}
+/**
+ * Sets the moveable attribute. A column that is
+ * moveable can be reordered by the user by dragging
+ * the header. A column that is not moveable cannot be
+ * dragged by the user but may be reordered
+ * by the programmer.
+ *
+ * @param moveable the moveable attribute
+ *
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @see Table#setColumnOrder(int[])
+ * @see Table#getColumnOrder()
+ * @see TableColumn#getMoveable()
+ * @see SWT#Move
+ *
+ * @since 3.1
+ */
+public void setMoveable (boolean moveable) {
+ checkWidget ();
+ this.moveable = moveable;
+}
+/**
+ * Sets the resizable attribute. A column that is
+ * resizable can be resized by the user dragging the
+ * edge of the header. A column that is not resizable
+ * cannot be dragged by the user but may be resized
+ * by the programmer.
+ *
+ * @param resizable the resize attribute
+ *
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public void setResizable (boolean value) {
+ checkWidget ();
+ resizable = value;
+}
+void setSortDirection (int value) {
+ if (value == sort) return;
+ boolean widthChange = value == SWT.NONE || sort == SWT.NONE;
+ sort = value;
+ if (widthChange) {
+ /*
+ * adding/removing the sort arrow decreases/increases the width that is
+ * available for the column's header text, so recompute the display text
+ */
+ GC gc = new GC (parent);
+ computeDisplayText (gc);
+ gc.dispose ();
+ }
+ if (parent.drawCount <= 0 && parent.getHeaderVisible ()) {
+ /* don't damage the header's drawn borders */
+ parent.header.redraw (getX (), 1, width - 2, parent.getHeaderHeight () - 3, false);
+ }
+}
+public void setText (String value) {
+ checkWidget ();
+ if (value == null) error (SWT.ERROR_NULL_ARGUMENT);
+ if (value.equals (text)) return; /* same value */
+ super.setText (value);
+ GC gc = new GC (parent);
+ computeDisplayText (gc);
+ gc.dispose ();
+ if (parent.drawCount <= 0 && parent.getHeaderVisible ()) {
+ /* don't damage the header's drawn borders */
+ parent.header.redraw (getX (), 1, width - 2, parent.getHeaderHeight () - 3, false);
+ }
+}
+/**
+ * Sets the receiver's tool tip text to the argument, which
+ * may be null indicating that the default tool tip for the
+ * control will be shown. For a control that has a default
+ * tool tip, such as the Tree control on Windows, setting
+ * the tool tip text to an empty string replaces the default,
+ * causing no tool tip text to be shown.
+ * <p>
+ * The mnemonic indicator (character '&amp;') is not displayed in a tool tip.
+ * To display a single '&amp;' in the tool tip, the character '&amp;' can be
+ * escaped by doubling it in the string.
+ * </p>
+ *
+ * @param string the new tool tip text (or null)
+ *
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @since 3.2
+ */
+public void setToolTipText (String string) {
+ checkWidget ();
+ if (toolTipText == string) return;
+ if (toolTipText != null && toolTipText.equals (string)) return;
+ toolTipText = string;
+ if (parent.toolTipShell == null) return; /* tooltip not currently showing */
+ if (((Integer) parent.toolTipShell.getData ()).intValue () != getIndex ()) return; /* tooltip showing for different column */
+ parent.headerUpdateToolTip (getX () + (width / 2)); /* update the tooltip text */
+}
+/**
+ * Sets the width of the receiver.
+ *
+ * @param width the new width
+ *
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public void setWidth (int value) {
+ checkWidget ();
+ if (value < 0) return;
+ if (width == value) return; /* same value */
+ parent.updateColumnWidth (this, value);
+}
+void updateFont (GC gc) {
+ computeDisplayText (gc);
+}
+/*
+ * Perform any internal changes necessary to reflect a changed width.
+ */
+void updateWidth (GC gc) {
+ String oldDisplayText = displayText;
+ computeDisplayText (gc);
+ /* the header must be damaged if the display text has changed or if the alignment is not LEFT */
+ if (parent.getHeaderVisible ()) {
+ if ((style & SWT.LEFT) == 0 || !oldDisplayText.equals (displayText)) {
+ int padding = parent.getHeaderPadding ();
+ parent.header.redraw (getX () + padding, 0, width - padding, parent.getHeaderHeight (), false);
+ }
+ }
+}
+}
diff --git a/bundles/org.eclipse.swt/Eclipse SWT/emulated/treetable/org/eclipse/swt/widgets/TableItem.java b/bundles/org.eclipse.swt/Eclipse SWT/emulated/treetable/org/eclipse/swt/widgets/TableItem.java
new file mode 100644
index 0000000000..e4c3476995
--- /dev/null
+++ b/bundles/org.eclipse.swt/Eclipse SWT/emulated/treetable/org/eclipse/swt/widgets/TableItem.java
@@ -0,0 +1,2032 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2009 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.swt.widgets;
+
+import org.eclipse.swt.*;
+import org.eclipse.swt.graphics.*;
+
+/**
+ * Instances of this class represent a selectable user interface object
+ * that represents an item in a table.
+ * <dl>
+ * <dt><b>Styles:</b></dt>
+ * <dd>(none)</dd>
+ * <dt><b>Events:</b></dt>
+ * <dd>(none)</dd>
+ * </dl>
+ * <p>
+ * IMPORTANT: This class is <em>not</em> intended to be subclassed.
+ * </p>
+ *
+ * @see <a href="http://www.eclipse.org/swt/snippets/#table">Table, TableItem, TableColumn snippets</a>
+ * @see <a href="http://www.eclipse.org/swt/">Sample code and further information</a>
+ * @noextend This class is not intended to be subclassed by clients.
+ */
+public class TableItem extends Item {
+ Table parent;
+ int index = -1;
+ boolean checked, grayed, cached;
+
+ String[] texts;
+ int[] textWidths = new int [1]; /* cached string measurements */
+ int customWidth = -1; /* width specified by Measure callback */
+ int fontHeight; /* cached item font height */
+ int[] fontHeights;
+ int imageIndent;
+ Image[] images;
+ Color foreground, background;
+ String[] displayTexts;
+ Color[] cellForegrounds, cellBackgrounds;
+ Font font;
+ Font[] cellFonts;
+
+ static final int MARGIN_TEXT = 3; /* the left and right margins within the text's space */
+
+/**
+ * Constructs a new instance of this class given its parent
+ * (which must be a <code>Table</code>) and a style value
+ * describing its behavior and appearance. The item is added
+ * to the end of the items maintained by its parent.
+ * <p>
+ * The style value is either one of the style constants defined in
+ * class <code>SWT</code> which is applicable to instances of this
+ * class, or must be built by <em>bitwise OR</em>'ing together
+ * (that is, using the <code>int</code> "|" operator) two or more
+ * of those <code>SWT</code> style constants. The class description
+ * lists the style constants that are applicable to the class.
+ * Style bits are also inherited from superclasses.
+ * </p>
+ *
+ * @param parent a composite control which will be the parent of the new instance (cannot be null)
+ * @param style the style of control to construct
+ *
+ * @exception IllegalArgumentException <ul>
+ * <li>ERROR_NULL_ARGUMENT - if the parent is null</li>
+ * </ul>
+ * @exception SWTException <ul>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the parent</li>
+ * <li>ERROR_INVALID_SUBCLASS - if this class is not an allowed subclass</li>
+ * </ul>
+ *
+ * @see SWT
+ * @see Widget#checkSubclass
+ * @see Widget#getStyle
+ */
+public TableItem (Table parent, int style) {
+ this (parent, style, checkNull (parent).itemsCount);
+}
+/**
+ * Constructs a new instance of this class given its parent
+ * (which must be a <code>Table</code>), a style value
+ * describing its behavior and appearance, and the index
+ * at which to place it in the items maintained by its parent.
+ * <p>
+ * The style value is either one of the style constants defined in
+ * class <code>SWT</code> which is applicable to instances of this
+ * class, or must be built by <em>bitwise OR</em>'ing together
+ * (that is, using the <code>int</code> "|" operator) two or more
+ * of those <code>SWT</code> style constants. The class description
+ * lists the style constants that are applicable to the class.
+ * Style bits are also inherited from superclasses.
+ * </p>
+ *
+ * @param parent a composite control which will be the parent of the new instance (cannot be null)
+ * @param style the style of control to construct
+ * @param index the zero-relative index to store the receiver in its parent
+ *
+ * @exception IllegalArgumentException <ul>
+ * <li>ERROR_NULL_ARGUMENT - if the parent is null</li>
+ * <li>ERROR_INVALID_RANGE - if the index is not between 0 and the number of elements in the parent (inclusive)</li>
+ * </ul>
+ * @exception SWTException <ul>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the parent</li>
+ * <li>ERROR_INVALID_SUBCLASS - if this class is not an allowed subclass</li>
+ * </ul>
+ *
+ * @see SWT
+ * @see Widget#checkSubclass
+ * @see Widget#getStyle
+ */
+public TableItem (Table parent, int style, int index) {
+ this (parent, style, index, true);
+}
+TableItem (Table parent, int style, int index, boolean notifyParent) {
+ super (parent, style);
+ int validItemIndex = parent.itemsCount;
+ if (!(0 <= index && index <= validItemIndex)) error (SWT.ERROR_INVALID_RANGE);
+ this.parent = parent;
+ this.index = index;
+ int columnCount = parent.columns.length;
+ if (columnCount > 0) {
+ displayTexts = new String [columnCount];
+ if (columnCount > 1) {
+ texts = new String [columnCount];
+ textWidths = new int [columnCount];
+ images = new Image [columnCount];
+ }
+ }
+ if (notifyParent) parent.createItem (this);
+}
+/*
+ * Updates internal structures in the receiver and its child items to handle the creation of a new column.
+ */
+void addColumn (TableColumn column) {
+ int index = column.getIndex ();
+ int columnCount = parent.columns.length;
+
+ if (columnCount > 1) {
+ if (columnCount == 2) {
+ texts = new String [2];
+ } else {
+ String[] newTexts = new String [columnCount];
+ System.arraycopy (texts, 0, newTexts, 0, index);
+ System.arraycopy (texts, index, newTexts, index + 1, columnCount - index - 1);
+ texts = newTexts;
+ }
+ if (index == 0) {
+ texts [1] = text;
+ text = ""; //$NON-NLS-1$
+ }
+
+ if (columnCount == 2) {
+ images = new Image [2];
+ } else {
+ Image[] newImages = new Image [columnCount];
+ System.arraycopy (images, 0, newImages, 0, index);
+ System.arraycopy (images, index, newImages, index + 1, columnCount - index - 1);
+ images = newImages;
+ }
+ if (index == 0) {
+ images [1] = image;
+ image = null;
+ }
+
+ int[] newTextWidths = new int [columnCount];
+ System.arraycopy (textWidths, 0, newTextWidths, 0, index);
+ System.arraycopy (textWidths, index, newTextWidths, index + 1, columnCount - index - 1);
+ textWidths = newTextWidths;
+ } else {
+ customWidth = -1; /* columnCount == 1 */
+ }
+
+ /*
+ * The length of displayTexts always matches the parent's column count, unless this
+ * count is zero, in which case displayTexts is null.
+ */
+ String[] newDisplayTexts = new String [columnCount];
+ if (columnCount > 1) {
+ System.arraycopy (displayTexts, 0, newDisplayTexts, 0, index);
+ System.arraycopy (displayTexts, index, newDisplayTexts, index + 1, columnCount - index - 1);
+ }
+ displayTexts = newDisplayTexts;
+
+ if (cellBackgrounds != null) {
+ Color[] newCellBackgrounds = new Color [columnCount];
+ System.arraycopy (cellBackgrounds, 0, newCellBackgrounds, 0, index);
+ System.arraycopy (cellBackgrounds, index, newCellBackgrounds, index + 1, columnCount - index - 1);
+ cellBackgrounds = newCellBackgrounds;
+ }
+ if (cellForegrounds != null) {
+ Color[] newCellForegrounds = new Color [columnCount];
+ System.arraycopy (cellForegrounds, 0, newCellForegrounds, 0, index);
+ System.arraycopy (cellForegrounds, index, newCellForegrounds, index + 1, columnCount - index - 1);
+ cellForegrounds = newCellForegrounds;
+ }
+ if (cellFonts != null) {
+ Font[] newCellFonts = new Font [columnCount];
+ System.arraycopy (cellFonts, 0, newCellFonts, 0, index);
+ System.arraycopy (cellFonts, index, newCellFonts, index + 1, columnCount - index - 1);
+ cellFonts = newCellFonts;
+
+ int[] newFontHeights = new int [columnCount];
+ System.arraycopy (fontHeights, 0, newFontHeights, 0, index);
+ System.arraycopy (fontHeights, index, newFontHeights, index + 1, columnCount - index - 1);
+ fontHeights = newFontHeights;
+ }
+
+ if (index == 0 && columnCount > 1) {
+ /*
+ * The new second column may have more width available to it than it did when it was
+ * the first column if checkboxes are being shown, so recompute its displayText if needed.
+ */
+ if ((parent.style & SWT.CHECK) != 0) {
+ GC gc = new GC (parent);
+ gc.setFont (getFont (1, false));
+ computeDisplayText (1, gc);
+ gc.dispose ();
+ }
+ }
+}
+static Table checkNull (Table table) {
+ if (table == null) SWT.error (SWT.ERROR_NULL_ARGUMENT);
+ return table;
+}
+protected void checkSubclass () {
+ if (!isValidSubclass ()) error (SWT.ERROR_INVALID_SUBCLASS);
+}
+void clear () {
+ checked = grayed = false;
+ texts = null;
+ textWidths = new int [1];
+ fontHeight = 0;
+ fontHeights = null;
+ images = null;
+ foreground = background = null;
+ displayTexts = null;
+ cellForegrounds = cellBackgrounds = null;
+ font = null;
+ cellFonts = null;
+ cached = false;
+ text = "";
+ image = null;
+
+ int columnCount = parent.columns.length;
+ if (columnCount > 0) {
+ displayTexts = new String [columnCount];
+ if (columnCount > 1) {
+ texts = new String [columnCount];
+ textWidths = new int [columnCount];
+ images = new Image [columnCount];
+ }
+ }
+}
+void computeDisplayText (int columnIndex, GC gc) {
+ if ((parent.style & SWT.VIRTUAL) != 0 && !cached) return; /* nothing to do */
+
+ int columnCount = parent.columns.length;
+ if (columnCount == 0) {
+ String text = getText (0, false);
+ textWidths [columnIndex] = gc.stringExtent (text).x;
+ return;
+ }
+
+ TableColumn column = parent.columns [columnIndex];
+ int availableWidth = column.width - 2 * parent.getCellPadding () - 2 * MARGIN_TEXT;
+ if (columnIndex == 0) {
+ availableWidth -= parent.col0ImageWidth;
+ if (parent.col0ImageWidth > 0) availableWidth -= Table.MARGIN_IMAGE;
+ if ((parent.style & SWT.CHECK) != 0) {
+ availableWidth -= parent.checkboxBounds.width;
+ availableWidth -= Table.MARGIN_IMAGE;
+ }
+ } else {
+ Image image = getImage (columnIndex, false);
+ if (image != null) {
+ availableWidth -= image.getBounds ().width;
+ availableWidth -= Table.MARGIN_IMAGE;
+ }
+ }
+
+ String text = getText (columnIndex, false);
+ int textWidth = gc.stringExtent (text).x;
+ if (textWidth <= availableWidth) {
+ displayTexts [columnIndex] = text;
+ textWidths [columnIndex] = textWidth;
+ return;
+ }
+
+ /* Ellipsis will be needed, so subtract their width from the available text width */
+ int ellipsisWidth = gc.stringExtent (Table.ELLIPSIS).x;
+ availableWidth -= ellipsisWidth;
+ if (availableWidth <= 0) {
+ displayTexts [columnIndex] = Table.ELLIPSIS;
+ textWidths [columnIndex] = ellipsisWidth;
+ return;
+ }
+
+ /* Make initial guess. */
+ int index = Math.min (availableWidth / gc.getFontMetrics ().getAverageCharWidth (), text.length ());
+ textWidth = gc.stringExtent (text.substring (0, index)).x;
+
+ /* Initial guess is correct. */
+ if (availableWidth == textWidth) {
+ displayTexts [columnIndex] = text.substring (0, index) + Table.ELLIPSIS;
+ textWidths [columnIndex] = textWidth + ellipsisWidth;
+ return;
+ }
+
+ /* Initial guess is too high, so reduce until fit is found. */
+ if (availableWidth < textWidth) {
+ do {
+ index--;
+ if (index < 0) {
+ displayTexts [columnIndex] = Table.ELLIPSIS;
+ textWidths [columnIndex] = ellipsisWidth;
+ return;
+ }
+ text = text.substring (0, index);
+ textWidth = gc.stringExtent (text).x;
+ } while (availableWidth < textWidth);
+ displayTexts [columnIndex] = text + Table.ELLIPSIS;
+ textWidths [columnIndex] = textWidth + ellipsisWidth;
+ return;
+ }
+
+ /* Initial guess is too low, so increase until overrun is found. */
+ int previousWidth = 0;
+ while (textWidth < availableWidth) {
+ index++;
+ previousWidth = textWidth;
+ textWidth = gc.stringExtent (text.substring (0, index)).x;
+ }
+ displayTexts [columnIndex] = text.substring (0, index - 1) + Table.ELLIPSIS;
+ textWidths [columnIndex] = previousWidth + ellipsisWidth;
+}
+void computeDisplayTexts (GC gc) {
+ if ((parent.style & SWT.VIRTUAL) != 0 && !cached) return; /* nothing to do */
+
+ int columnCount = parent.columns.length;
+ if (columnCount == 0) return;
+
+ for (int i = 0; i < columnCount; i++) {
+ gc.setFont (getFont (i, false));
+ computeDisplayText (i, gc);
+ }
+}
+/*
+ * Computes the cached text widths.
+ */
+void computeTextWidths (GC gc) {
+ if ((parent.style & SWT.VIRTUAL) != 0 && !cached) return; /* nothing to do */
+
+ int validColumnCount = Math.max (1, parent.columns.length);
+ textWidths = new int [validColumnCount];
+ for (int i = 0; i < textWidths.length; i++) {
+ String value = getDisplayText (i);
+ if (value != null) {
+ gc.setFont (getFont (i, false));
+ textWidths [i] = gc.stringExtent (value).x;
+ }
+ }
+}
+public void dispose () {
+ if (isDisposed ()) return;
+ Table parent = this.parent;
+ int startIndex = index;
+ int endIndex = parent.itemsCount - 1;
+ dispose (true);
+ parent.redrawItems (startIndex, endIndex, false);
+}
+void dispose (boolean notifyParent) {
+ if (isDisposed ()) return;
+ if (notifyParent) parent.destroyItem (this);
+ super.dispose (); /* super is intentional here */
+ background = foreground = null;
+ cellBackgrounds = cellForegrounds = null;
+ font = null;
+ cellFonts = null;
+ images = null;
+ texts = displayTexts = null;
+ textWidths = fontHeights = null;
+ parent = null;
+}
+/**
+ * Returns the receiver's background color.
+ *
+ * @return the background color
+ *
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @since 2.0
+ */
+public Color getBackground () {
+ checkWidget ();
+ if (!parent.checkData (this, true)) error (SWT.ERROR_WIDGET_DISPOSED);
+ if (background != null) return background;
+ return parent.getBackground ();
+}
+/**
+ * Returns the background color at the given column index in the receiver.
+ *
+ * @param index the column index
+ * @return the background color
+ *
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @since 3.0
+ */
+public Color getBackground (int columnIndex) {
+ checkWidget ();
+ if (!parent.checkData (this, true)) error (SWT.ERROR_WIDGET_DISPOSED);
+ int validColumnCount = Math.max (1, parent.columns.length);
+ if (!(0 <= columnIndex && columnIndex < validColumnCount)) return getBackground ();
+ if (cellBackgrounds == null || cellBackgrounds [columnIndex] == null) return getBackground ();
+ return cellBackgrounds [columnIndex];
+}
+/**
+ * Returns a rectangle describing the receiver's size and location
+ * relative to its parent.
+ *
+ * @return the receiver's bounding rectangle
+ *
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @since 3.2
+ */
+public Rectangle getBounds () {
+ checkWidget ();
+ return getBounds (true);
+}
+Rectangle getBounds (boolean checkData) {
+ if (checkData && !parent.checkData (this, true)) error (SWT.ERROR_WIDGET_DISPOSED);
+ int x = getTextX (0);
+ int width = textWidths [0] + 2 * MARGIN_TEXT;
+ if (parent.columns.length > 0) {
+ TableColumn column = parent.columns [0];
+ int right = column.getX () + column.width;
+ if (x + width > right) {
+ width = Math.max (0, right - x);
+ }
+ }
+ return new Rectangle (x, parent.getItemY (this), width, parent.itemHeight);
+}
+/**
+ * Returns a rectangle describing the receiver's size and location
+ * relative to its parent at a column in the table.
+ *
+ * @param index the index that specifies the column
+ * @return the receiver's bounding column rectangle
+ *
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public Rectangle getBounds (int columnIndex) {
+ checkWidget ();
+ if (!parent.checkData (this, true)) error (SWT.ERROR_WIDGET_DISPOSED);
+ TableColumn[] columns = parent.columns;
+ int columnCount = columns.length;
+ int validColumnCount = Math.max (1, columnCount);
+ if (!(0 <= columnIndex && columnIndex < validColumnCount)) {
+ return new Rectangle (0, 0, 0, 0);
+ }
+ /*
+ * If there are no columns then this is the bounds of the receiver's content.
+ */
+ if (columnCount == 0) {
+ int width = getContentWidth (0);
+ return new Rectangle (
+ getContentX (0),
+ parent.getItemY (this),
+ width,
+ parent.itemHeight - 1);
+ }
+
+ TableColumn column = columns [columnIndex];
+ if (columnIndex == 0) {
+ /*
+ * For column 0 this is bounds from the beginning of the content to the
+ * end of the column.
+ */
+ int x = getContentX (0);
+ int offset = x - column.getX ();
+ int width = Math.max (0, column.width - offset - 1); /* max is for columns with small widths */
+ return new Rectangle (x, parent.getItemY (this) + 1, width, parent.itemHeight - 1);
+ }
+ /*
+ * For columns > 0 this is the bounds of the table cell.
+ */
+ return new Rectangle (column.getX (), parent.getItemY (this) + 1, column.width, parent.itemHeight - 1);
+}
+/*
+ * Returns the full bounds of a cell in a table, regardless of its content.
+ */
+Rectangle getCellBounds (int columnIndex) {
+ int y = parent.getItemY (this);
+ if (parent.columns.length == 0) {
+ int width;
+ if (customWidth != -1) {
+ width = getContentX (0) + customWidth + parent.horizontalOffset;
+ } else {
+ int textPaintWidth = textWidths [0] + 2 * MARGIN_TEXT;
+ width = getTextX (0) + textPaintWidth + parent.horizontalOffset;
+ }
+ return new Rectangle (-parent.horizontalOffset, y, width, parent.itemHeight);
+ }
+ TableColumn column = parent.columns [columnIndex];
+ return new Rectangle (column.getX (), y, column.width, parent.itemHeight);
+}
+/*
+ * Returns the bounds of the receiver's checkbox, or null if the parent's style does not
+ * include SWT.CHECK.
+ */
+Rectangle getCheckboxBounds () {
+ if ((parent.getStyle () & SWT.CHECK) == 0) return null;
+ Rectangle result = parent.checkboxBounds;
+ if (parent.columns.length == 0) {
+ result.x = parent.getCellPadding () - parent.horizontalOffset;
+ } else {
+ result.x = parent.columns [0].getX () + parent.getCellPadding ();
+ }
+ result.y = parent.getItemY (this) + (parent.itemHeight - result.height) / 2;
+ return result;
+}
+/**
+ * Returns <code>true</code> if the receiver is checked,
+ * and false otherwise. When the parent does not have
+ * the <code>CHECK</code> style, return false.
+ *
+ * @return the checked state of the checkbox
+ *
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public boolean getChecked () {
+ checkWidget ();
+ if (!parent.checkData (this, true)) error (SWT.ERROR_WIDGET_DISPOSED);
+ return checked;
+}
+int getContentWidth (int columnIndex) {
+ int width = textWidths [columnIndex] + 2 * MARGIN_TEXT;
+ if (columnIndex == 0) {
+ width += parent.col0ImageWidth;
+ if (parent.col0ImageWidth > 0) width += Table.MARGIN_IMAGE;
+ } else {
+ Image image = getImage (columnIndex, false);
+ if (image != null) {
+ width += image.getBounds ().width + Table.MARGIN_IMAGE;
+ }
+ }
+ return width;
+}
+/*
+ * Returns the x value where the receiver's content (ie.- its image or text) begins
+ * for the specified column.
+ */
+int getContentX (int columnIndex) {
+ int minX = parent.getCellPadding ();
+ if (columnIndex == 0) {
+ Rectangle checkboxBounds = getCheckboxBounds ();
+ if (checkboxBounds != null) {
+ minX += checkboxBounds.width + Table.MARGIN_IMAGE;
+ }
+ }
+
+ if (parent.columns.length == 0) return minX - parent.horizontalOffset; /* free first column */
+
+ TableColumn column = parent.columns [columnIndex];
+ int columnX = column.getX ();
+ if ((column.style & SWT.LEFT) != 0) return columnX + minX;
+
+ /* column is not left-aligned */
+ int contentWidth = getContentWidth (columnIndex);
+ int contentX = 0;
+ if ((column.style & SWT.RIGHT) != 0) {
+ contentX = column.width - parent.getCellPadding () - contentWidth;
+ } else { /* SWT.CENTER */
+ contentX = (column.width - contentWidth) / 2;
+ }
+ return Math.max (columnX + minX, columnX + contentX);
+}
+String getDisplayText (int columnIndex) {
+ if (parent.columns.length == 0) return getText (0, false);
+ String result = displayTexts [columnIndex];
+ return result != null ? result : ""; //$NON-NLS-1$
+}
+/*
+ * Returns the bounds that should be used for drawing a focus rectangle on the receiver
+ */
+Rectangle getFocusBounds () {
+ int x = 0;
+ TableColumn[] columns = parent.columns;
+ int[] columnOrder = parent.getColumnOrder ();
+ if ((parent.style & SWT.FULL_SELECTION) != 0) {
+ int col0index = columnOrder.length == 0 ? 0 : columnOrder [0];
+ if (col0index == 0) {
+ x = getTextX (0);
+ } else {
+ x = -parent.horizontalOffset;
+ }
+ } else {
+ x = getTextX (0);
+ }
+
+ if (columns.length > 0) {
+ /* ensure that the focus x does not start beyond the right bound of column 0 */
+ int rightX = columns [0].getX () + columns [0].width;
+ x = Math.min (x, rightX - 1);
+ }
+
+ int width;
+ if (columns.length == 0) {
+ if (customWidth != -1) {
+ width = customWidth;
+ } else {
+ width = textWidths [0] + 2 * MARGIN_TEXT;
+ }
+ } else {
+ TableColumn column;
+ if ((parent.style & SWT.FULL_SELECTION) != 0) {
+ column = columns [columnOrder [columnOrder.length - 1]];
+ } else {
+ column = columns [0];
+ }
+ width = column.getX () + column.width - x - 1;
+ }
+ return new Rectangle (
+ x,
+ parent.getItemY (this) + (parent.linesVisible ? 1 : 0),
+ width,
+ parent.itemHeight - (parent.linesVisible ? 1 : 0));
+}
+/**
+ * Returns the font that the receiver will use to paint textual information for this item.
+ *
+ * @return the receiver's font
+ *
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @since 3.0
+ */
+public Font getFont () {
+ checkWidget ();
+ return getFont (true);
+}
+Font getFont (boolean checkData) {
+ if (checkData && !parent.checkData (this, true)) error (SWT.ERROR_WIDGET_DISPOSED);
+ if (font != null) return font;
+ return parent.getFont ();
+}
+/**
+ * Returns the font that the receiver will use to paint textual information
+ * for the specified cell in this item.
+ *
+ * @param index the column index
+ * @return the receiver's font
+ *
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @since 3.0
+ */
+public Font getFont (int columnIndex) {
+ checkWidget ();
+ return getFont (columnIndex, true);
+}
+Font getFont (int columnIndex, boolean checkData) {
+ if (checkData && !parent.checkData (this, true)) error (SWT.ERROR_WIDGET_DISPOSED);
+ int validColumnCount = Math.max (1, parent.columns.length);
+ if (!(0 <= columnIndex && columnIndex < validColumnCount)) return getFont (checkData);
+ if (cellFonts == null || cellFonts [columnIndex] == null) return getFont (checkData);
+ return cellFonts [columnIndex];
+}
+int getFontHeight () {
+ if (fontHeight != 0) return fontHeight;
+ return parent.fontHeight;
+}
+int getFontHeight (int columnIndex) {
+ if (fontHeights == null || fontHeights [columnIndex] == 0) return getFontHeight ();
+ return fontHeights [columnIndex];
+}
+/**
+ * Returns the foreground color that the receiver will use to draw.
+ *
+ * @return the receiver's foreground color
+ *
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @since 2.0
+ */
+public Color getForeground () {
+ checkWidget ();
+ if (!parent.checkData (this, true)) error (SWT.ERROR_WIDGET_DISPOSED);
+ if (foreground != null) return foreground;
+ return parent.getForeground ();
+}
+/**
+ *
+ * Returns the foreground color at the given column index in the receiver.
+ *
+ * @param index the column index
+ * @return the foreground color
+ *
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @since 3.0
+ */
+public Color getForeground (int columnIndex) {
+ checkWidget ();
+ if (!parent.checkData (this, true)) error (SWT.ERROR_WIDGET_DISPOSED);
+ int validColumnCount = Math.max (1, parent.columns.length);
+ if (!(0 <= columnIndex && columnIndex < validColumnCount)) return getForeground ();
+ if (cellForegrounds == null || cellForegrounds [columnIndex] == null) return getForeground ();
+ return cellForegrounds [columnIndex];
+}
+/**
+ * Returns <code>true</code> if the receiver is grayed,
+ * and false otherwise. When the parent does not have
+ * the <code>CHECK</code> style, return false.
+ *
+ * @return the grayed state of the checkbox
+ *
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public boolean getGrayed () {
+ checkWidget ();
+ if (!parent.checkData (this, true)) error (SWT.ERROR_WIDGET_DISPOSED);
+ return grayed;
+}
+/*
+ * Returns the bounds representing the clickable region that should select the receiver.
+ */
+Rectangle getHitBounds () {
+ int[] columnOrder = parent.getColumnOrder ();
+ int contentX = 0;
+ if ((parent.style & SWT.FULL_SELECTION) != 0) {
+ int col0index = columnOrder.length == 0 ? 0 : columnOrder [0];
+ if (col0index == 0) {
+ contentX = getContentX (0);
+ } else {
+ contentX = 0;
+ }
+ } else {
+ contentX = getContentX (0);
+ }
+
+ int width = 0;
+ TableColumn[] columns = parent.columns;
+ if (columns.length == 0) {
+ width = getContentWidth (0);
+ } else {
+ /*
+ * If there are columns then this spans from the beginning of the receiver's column 0
+ * image or text to the end of either column 0 or the last column (FULL_SELECTION).
+ */
+ TableColumn column;
+ if ((parent.style & SWT.FULL_SELECTION) != 0) {
+ column = columns [columnOrder [columnOrder.length - 1]];
+ } else {
+ column = columns [0];
+ }
+ width = column.getX () + column.width - contentX;
+ }
+ return new Rectangle (contentX, parent.getItemY (this), width, parent.itemHeight);
+}
+public Image getImage () {
+ checkWidget ();
+ if (!parent.checkData (this, true)) error (SWT.ERROR_WIDGET_DISPOSED);
+ return super.getImage ();
+}
+/**
+ * Returns the image stored at the given column index in the receiver,
+ * or null if the image has not been set or if the column does not exist.
+ *
+ * @param index the column index
+ * @return the image stored at the given column index in the receiver
+ *
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public Image getImage (int columnIndex) {
+ checkWidget ();
+ return getImage (columnIndex, true);
+}
+Image getImage (int columnIndex, boolean checkData) {
+ if (checkData && !parent.checkData (this, true)) error (SWT.ERROR_WIDGET_DISPOSED);
+ int validColumnCount = Math.max (1, parent.columns.length);
+ if (!(0 <= columnIndex && columnIndex < validColumnCount)) return null;
+ if (columnIndex == 0) return super.getImage (); /* super is intentional here */
+ return images [columnIndex];
+}
+/**
+ * Returns a rectangle describing the size and location
+ * relative to its parent of an image at a column in the
+ * table. An empty rectangle is returned if index exceeds
+ * the index of the table's last column.
+ *
+ * @param index the index that specifies the column
+ * @return the receiver's bounding image rectangle
+ *
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public Rectangle getImageBounds (int columnIndex) {
+ checkWidget ();
+ if (!parent.checkData (this, true)) error (SWT.ERROR_WIDGET_DISPOSED);
+ int validColumnCount = Math.max (1, parent.columns.length);
+ if (!(0 <= columnIndex && columnIndex < validColumnCount)) return new Rectangle (0,0,0,0);
+
+ int padding = parent.getCellPadding ();
+ int startX = getContentX (columnIndex);
+ int itemHeight = parent.itemHeight;
+ int imageSpaceY = itemHeight - 2 * padding;
+ int y = parent.getItemY (this);
+ Image image = getImage (columnIndex, false);
+ int drawWidth = 0;
+ if (columnIndex == 0) {
+ /* for column 0 all images have the same width */
+ drawWidth = parent.col0ImageWidth;
+ } else {
+ if (image != null) drawWidth = image.getBounds ().width;
+ }
+ return new Rectangle (startX, y + padding, drawWidth, imageSpaceY);
+}
+/**
+ * Gets the image indent.
+ *
+ * @return the indent
+ *
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public int getImageIndent () {
+ checkWidget();
+ if (!parent.checkData (this, true)) error (SWT.ERROR_WIDGET_DISPOSED);
+ return imageIndent; // TODO
+}
+String getNameText () {
+ if ((parent.style & SWT.VIRTUAL) != 0) {
+ if (!cached) return "*virtual*"; //$NON-NLS-1$
+ }
+ return super.getNameText ();
+}
+/**
+ * Returns the receiver's parent, which must be a <code>Table</code>.
+ *
+ * @return the receiver's parent
+ *
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public Table getParent () {
+ checkWidget ();
+ return parent;
+}
+/*
+ * Returns the receiver's ideal width for the specified columnIndex.
+ */
+int getPreferredWidth (int columnIndex) {
+ int width = 0;
+ GC gc = new GC (parent);
+ gc.setFont (getFont (columnIndex, false));
+ width += gc.stringExtent (getText (columnIndex, false)).x + 2 * MARGIN_TEXT;
+ if (columnIndex == 0) {
+ if (parent.col0ImageWidth > 0) {
+ width += parent.col0ImageWidth;
+ width += Table.MARGIN_IMAGE;
+ }
+ } else {
+ Image image = getImage (columnIndex, false);
+ if (image != null) {
+ width += image.getBounds ().width;
+ width += Table.MARGIN_IMAGE;
+ }
+ }
+
+ if (parent.hooks (SWT.MeasureItem)) {
+ Event event = new Event ();
+ event.item = this;
+ event.gc = gc;
+ event.index = columnIndex;
+ event.x = getContentX (columnIndex);
+ event.y = parent.getItemY (this);
+ event.width = width;
+ event.height = parent.itemHeight;
+ parent.sendEvent (SWT.MeasureItem, event);
+ if (parent.itemHeight != event.height) {
+ parent.customHeightSet = true;
+ boolean update = parent.setItemHeight (event.height + 2 * parent.getCellPadding ());
+ if (update) parent.redraw ();
+ }
+ width = event.width;
+ }
+
+ gc.dispose ();
+ if (columnIndex == 0 && (parent.style & SWT.CHECK) != 0) {
+ width += parent.checkboxBounds.width;
+ width += Table.MARGIN_IMAGE;
+ }
+ return width + 2 * parent.getCellPadding ();
+}
+public String getText () {
+ checkWidget ();
+ if (!parent.checkData (this, true)) error (SWT.ERROR_WIDGET_DISPOSED);
+ return super.getText ();
+}
+/**
+ * Returns the text stored at the given column index in the receiver,
+ * or empty string if the text has not been set.
+ *
+ * @param index the column index
+ * @return the text stored at the given column index in the receiver
+ *
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public String getText (int columnIndex) {
+ checkWidget ();
+ return getText (columnIndex, true);
+}
+String getText (int columnIndex, boolean checkData) {
+ if (checkData && !parent.checkData (this, true)) error (SWT.ERROR_WIDGET_DISPOSED);
+ int validColumnCount = Math.max (1, parent.columns.length);
+ if (!(0 <= columnIndex && columnIndex < validColumnCount)) return ""; //$NON-NLS-1$
+ if (columnIndex == 0) return super.getText (); /* super is intentional here */
+ if (texts [columnIndex] == null) return ""; //$NON-NLS-1$
+ return texts [columnIndex];
+}
+/**
+ * Returns a rectangle describing the size and location
+ * relative to its parent of the text at a column in the
+ * table. An empty rectangle is returned if index exceeds
+ * the index of the table's last column.
+ *
+ * @param index the index that specifies the column
+ * @return the receiver's bounding text rectangle
+ *
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @since 3.3
+ */
+public Rectangle getTextBounds (int columnIndex) {
+ checkWidget ();
+ if (!parent.checkData (this, true)) error (SWT.ERROR_WIDGET_DISPOSED);
+ TableColumn[] columns = parent.columns;
+ int columnCount = columns.length;
+ int validColumnCount = Math.max (1, columnCount);
+ if (!(0 <= columnIndex && columnIndex < validColumnCount)) {
+ return new Rectangle (0, 0, 0, 0);
+ }
+ /*
+ * If there are no columns then this is the bounds of the receiver's content,
+ * starting from the text.
+ */
+ if (columnCount == 0) {
+ int x = getTextX (0) + MARGIN_TEXT;
+ int width = Math.max (0, getContentX(0) + getContentWidth (0) - x);
+ return new Rectangle (
+ x,
+ parent.getItemY (this),
+ width,
+ parent.itemHeight - 1);
+ }
+
+ TableColumn column = columns [columnIndex];
+ if (columnIndex == 0) {
+ /*
+ * For column 0 this is bounds from the beginning of the content to the
+ * end of the column, starting from the text.
+ */
+ int x = getTextX (0) + MARGIN_TEXT;
+ int offset = x - column.getX ();
+ int width = Math.max (0, column.width - offset - 1); /* max is for columns with small widths */
+ return new Rectangle (x, parent.getItemY (this) + 1, width, parent.itemHeight - 1);
+ }
+ /*
+ * For columns > 0 this is the bounds of the table cell, starting from the text.
+ */
+ int x = getTextX (columnIndex) + MARGIN_TEXT;
+ int offset = x - column.getX ();
+ int width = Math.max (0, column.width - offset - MARGIN_TEXT);
+ return new Rectangle (x, parent.getItemY (this) + 1, width, parent.itemHeight - 1);
+}
+/*
+ * Returns the x value where the receiver's text begins.
+ */
+int getTextX (int columnIndex) {
+ int textX = getContentX (columnIndex);
+ if (columnIndex == 0) {
+ textX += parent.col0ImageWidth;
+ if (parent.col0ImageWidth > 0) textX += Table.MARGIN_IMAGE;
+ } else {
+ Image image = getImage (columnIndex, false);
+ if (image != null) {
+ textX += image.getBounds ().width + Table.MARGIN_IMAGE;
+ }
+ }
+ return textX;
+}
+/*
+ * Answers a boolean indicating whether the receiver's y is within the current
+ * viewport of the parent.
+ */
+boolean isInViewport () {
+ int topIndex = parent.topIndex;
+ if (index < topIndex) return false;
+ int visibleCount = parent.clientArea.height / parent.itemHeight;
+ return index <= topIndex + visibleCount;
+}
+boolean isSelected () {
+ return parent.getSelectionIndex (this) != -1;
+}
+/*
+ * The backgroundOnly argument indicates whether the item should only
+ * worry about painting its background color and selection.
+ *
+ * Returns a boolean indicating whether to abort drawing focus on the item.
+ * If the receiver is not the current focus item then this value is irrelevant.
+ */
+boolean paint (GC gc, TableColumn column, boolean backgroundOnly) {
+ if (!parent.checkData (this, true)) return false;
+ int columnIndex = 0, x = 0;
+ if (column != null) {
+ columnIndex = column.getIndex ();
+ x = column.getX ();
+ }
+
+ /*
+ * Capture GC attributes that will need to be restored later in the paint
+ * process to ensure that the item paints as intended without being affected
+ * by GC changes made in MeasureItem/EraseItem/PaintItem callbacks.
+ */
+ int oldAlpha = gc.getAlpha ();
+ boolean oldAdvanced = gc.getAdvanced ();
+ int oldAntialias = gc.getAntialias ();
+ Pattern oldBackgroundPattern = gc.getBackgroundPattern ();
+ Pattern oldForegroundPattern = gc.getForegroundPattern ();
+ int oldInterpolation = gc.getInterpolation ();
+ int oldTextAntialias = gc.getTextAntialias ();
+
+ if (parent.hooks (SWT.MeasureItem)) {
+ int contentWidth = getContentWidth (columnIndex);
+ int contentX = getContentX (columnIndex);
+ gc.setFont (getFont (columnIndex, false));
+ Event event = new Event ();
+ event.item = this;
+ event.gc = gc;
+ event.index = columnIndex;
+ event.x = contentX;
+ event.y = parent.getItemY (this);
+ event.width = contentWidth;
+ event.height = parent.itemHeight;
+ parent.sendEvent (SWT.MeasureItem, event);
+ event.gc = null;
+ if (gc.isDisposed ()) return false;
+ gc.setAlpha (oldAlpha);
+ gc.setAntialias (oldAntialias);
+ gc.setBackgroundPattern (oldBackgroundPattern);
+ gc.setForegroundPattern (oldForegroundPattern);
+ gc.setInterpolation (oldInterpolation);
+ gc.setTextAntialias (oldTextAntialias);
+ gc.setAdvanced (oldAdvanced);
+ if (isDisposed ()) return false;
+ if (parent.itemHeight != event.height) {
+ parent.customHeightSet = true;
+ boolean update = parent.setItemHeight (event.height + 2 * parent.getCellPadding ());
+ if (update) parent.redraw ();
+ }
+ if (parent.columns.length == 0) {
+ int change = event.width - (customWidth != -1 ? customWidth : contentWidth);
+ if (event.width != contentWidth || customWidth != -1) customWidth = event.width;
+ if (change != 0) { /* scrollbar may be affected since no columns */
+ parent.updateHorizontalBar (contentX + event.width, change);
+ // TODO what if clip is too small now?
+ }
+ }
+ }
+
+ /* if this cell is completely to the right of the client area then there's no need to paint it */
+ Rectangle clientArea = parent.clientArea;
+ if (clientArea.x + clientArea.width < x) return false;
+
+ Rectangle cellBounds = getCellBounds (columnIndex);
+ if (parent.linesVisible) {
+ cellBounds.y++;
+ cellBounds.height--;
+ }
+ int cellRightX = 0;
+ if (column != null) {
+ cellRightX = column.getX () + column.width;
+ } else {
+ cellRightX = cellBounds.x + cellBounds.width;
+ }
+
+ /* restrict the clipping region to the cell */
+ gc.setClipping (x, cellBounds.y, clientArea.width - x, cellBounds.height);
+
+ int y = parent.getItemY (this);
+ int itemHeight = parent.itemHeight;
+
+ /* draw the parent background color/image of this cell */
+ if (column == null) {
+ parent.drawBackground (gc, 0, y, clientArea.width, itemHeight);
+ } else {
+ int fillWidth = cellBounds.width;
+ if (parent.linesVisible) fillWidth--;
+ parent.drawBackground (gc, cellBounds.x, cellBounds.y, fillWidth, cellBounds.height);
+ }
+
+ boolean isSelected = isSelected ();
+ boolean isFocusItem = parent.focusItem == this && parent.isFocusControl ();
+ boolean drawBackground = true;
+ boolean drawForeground = true;
+ boolean drawSelection = isSelected;
+ boolean drawFocus = isFocusItem;
+ if (parent.hooks (SWT.EraseItem)) {
+ drawBackground = background != null || (cellBackgrounds != null && cellBackgrounds [columnIndex] != null);
+ gc.setFont (getFont (columnIndex, false));
+ if (isSelected && (columnIndex == 0 || (parent.style & SWT.FULL_SELECTION) != 0)) {
+ gc.setForeground (display.getSystemColor (SWT.COLOR_LIST_SELECTION_TEXT));
+ gc.setBackground (display.getSystemColor (SWT.COLOR_LIST_SELECTION));
+ } else {
+ gc.setForeground (getForeground (columnIndex));
+ gc.setBackground (getBackground (columnIndex));
+ }
+ Event event = new Event ();
+ event.item = this;
+ event.gc = gc;
+ event.index = columnIndex;
+ event.doit = true;
+ event.detail = SWT.FOREGROUND;
+ if (drawBackground) event.detail |= SWT.BACKGROUND;
+ if (isSelected) event.detail |= SWT.SELECTED;
+ if (isFocusItem) event.detail |= SWT.FOCUSED;
+ event.x = cellBounds.x;
+ event.y = cellBounds.y;
+ event.width = cellBounds.width;
+ event.height = cellBounds.height;
+ gc.setClipping (cellBounds);
+ parent.sendEvent (SWT.EraseItem, event);
+ event.gc = null;
+ if (gc.isDisposed ()) return false;
+ gc.setAlpha (oldAlpha);
+ gc.setAntialias (oldAntialias);
+ gc.setBackgroundPattern (oldBackgroundPattern);
+ gc.setClipping (cellBounds);
+ gc.setForegroundPattern (oldForegroundPattern);
+ gc.setInterpolation (oldInterpolation);
+ gc.setTextAntialias (oldTextAntialias);
+ gc.setAdvanced (oldAdvanced);
+ if (isDisposed ()) return false;
+ if (!event.doit) {
+ drawBackground = drawForeground = drawSelection = drawFocus = false;
+ } else {
+ drawBackground = drawBackground && (event.detail & SWT.BACKGROUND) != 0;
+ drawForeground = (event.detail & SWT.FOREGROUND) != 0;
+ drawSelection = isSelected && (event.detail & SWT.SELECTED) != 0;
+ drawFocus = isFocusItem && (event.detail & SWT.FOCUSED) != 0;
+ }
+ }
+
+ /* draw the cell's set background if appropriate */
+ if (drawBackground) {
+ gc.setBackground (getBackground (columnIndex));
+ if (columnIndex == 0 && (column == null || column.getOrderIndex () == 0)) {
+ Rectangle focusBounds = getFocusBounds ();
+ int fillWidth = 0;
+ if (column == null) {
+ fillWidth = focusBounds.width;
+ } else {
+ fillWidth = column.width - focusBounds.x;
+ if (parent.linesVisible) fillWidth--;
+ }
+ gc.fillRectangle (focusBounds.x, focusBounds.y, fillWidth, focusBounds.height);
+ } else {
+ int fillWidth = cellBounds.width;
+ gc.fillRectangle (cellBounds.x, cellBounds.y, fillWidth, cellBounds.height);
+ }
+ }
+
+ /* draw the selection bar if the receiver is selected */
+ if (drawSelection && (columnIndex == 0 || (parent.style & SWT.FULL_SELECTION) != 0)) {
+ if (parent.hasFocus () || (parent.style & SWT.HIDE_SELECTION) == 0) {
+ gc.setBackground (display.getSystemColor (SWT.COLOR_LIST_SELECTION));
+ if (columnIndex == 0) {
+ Rectangle focusBounds = getFocusBounds ();
+ int startX, fillWidth;
+ if (column == null || column.getOrderIndex () == 0 || (parent.style & SWT.FULL_SELECTION) == 0) {
+ startX = focusBounds.x + 1; /* space for left bound of focus rect */
+ } else {
+ startX = column.getX ();
+ }
+ if (column == null) {
+ fillWidth = focusBounds.width - 2;
+ } else {
+ fillWidth = column.getX () + column.width - startX;
+ if (column.getOrderIndex () == parent.columns.length - 1 || (parent.style & SWT.FULL_SELECTION) == 0) {
+ fillWidth -= 2; /* space for right bound of focus rect */
+ }
+ }
+ if (fillWidth > 0) {
+ gc.fillRectangle (startX, focusBounds.y + 1, fillWidth, focusBounds.height - 2);
+ }
+ } else {
+ int startX = column.getX ();
+ int fillWidth = column.width;
+ if (column.getOrderIndex () == 0) {
+ startX += 1; /* space for left bound of focus rect */
+ fillWidth -= 1;
+ }
+ if (column.getOrderIndex () == parent.columns.length - 1) {
+ fillWidth -= 2; /* space for right bound of focus rect */
+ }
+ if (fillWidth > 0) {
+ gc.fillRectangle (
+ column.getX (),
+ cellBounds.y + 1,
+ fillWidth,
+ cellBounds.height - 2);
+ }
+ }
+ }
+ }
+
+ if (backgroundOnly) return false;
+
+ /* Draw checkbox if drawing column 0 and parent has style SWT.CHECK */
+ if (columnIndex == 0 && (parent.style & SWT.CHECK) != 0) {
+ Image baseImage = grayed ? parent.getGrayUncheckedImage () : parent.getUncheckedImage ();
+ Rectangle checkboxBounds = getCheckboxBounds ();
+ gc.drawImage (baseImage, checkboxBounds.x, checkboxBounds.y);
+ /* Draw checkmark if item is checked */
+ if (checked) {
+ Image checkmarkImage = parent.getCheckmarkImage ();
+ Rectangle checkmarkBounds = checkmarkImage.getBounds ();
+ int xInset = (checkboxBounds.width - checkmarkBounds.width) / 2;
+ int yInset = (checkboxBounds.height - checkmarkBounds.height) / 2;
+ gc.drawImage (checkmarkImage, checkboxBounds.x + xInset, checkboxBounds.y + yInset);
+ }
+ }
+
+ if (drawForeground) {
+ Image image = getImage (columnIndex, false);
+ String text = getDisplayText (columnIndex);
+ Rectangle imageArea = getImageBounds (columnIndex);
+ int startX = imageArea.x;
+
+ /* while painting the cell's content restrict the clipping region */
+ int padding = parent.getCellPadding ();
+ gc.setClipping (
+ startX,
+ cellBounds.y + padding - (parent.linesVisible ? 1 : 0),
+ cellRightX - startX - padding,
+ cellBounds.height - 2 * (padding - (parent.linesVisible ? 1 : 0)));
+
+ /* draw the image */
+ if (image != null) {
+ Rectangle imageBounds = image.getBounds ();
+ gc.drawImage (
+ image,
+ 0, 0, /* source x, y */
+ imageBounds.width, imageBounds.height, /* source width, height */
+ imageArea.x, imageArea.y, /* dest x, y */
+ imageArea.width, imageArea.height); /* dest width, height */
+ }
+
+ /* draw the text */
+ if (text.length () > 0) {
+ gc.setFont (getFont (columnIndex, false));
+ int fontHeight = getFontHeight (columnIndex);
+ if (drawSelection && (columnIndex == 0 || (parent.style & SWT.FULL_SELECTION) != 0)) {
+ if (parent.hasFocus () || (parent.style & SWT.HIDE_SELECTION) == 0) {
+ gc.setForeground (display.getSystemColor (SWT.COLOR_LIST_SELECTION_TEXT));
+ }
+ } else {
+ if (!isSelected || drawSelection) {
+ gc.setForeground (getForeground (columnIndex));
+ }
+ }
+ x = getTextX (columnIndex) + MARGIN_TEXT;
+ gc.drawString (text, x, y + (itemHeight - fontHeight) / 2, true);
+ }
+ }
+
+ if (parent.hooks (SWT.PaintItem)) {
+ int contentWidth = getContentWidth (columnIndex);
+ int contentX = getContentX (columnIndex);
+ gc.setFont (getFont (columnIndex, false));
+ if (isSelected && (columnIndex == 0 || (parent.style & SWT.FULL_SELECTION) != 0)) {
+ gc.setForeground (display.getSystemColor (SWT.COLOR_LIST_SELECTION_TEXT));
+ gc.setBackground (display.getSystemColor (SWT.COLOR_LIST_SELECTION));
+ } else {
+ gc.setForeground (getForeground (columnIndex));
+ gc.setBackground (getBackground (columnIndex));
+ }
+ Event event = new Event ();
+ event.item = this;
+ event.gc = gc;
+ event.index = columnIndex;
+ if (isSelected) event.detail |= SWT.SELECTED;
+ if (drawFocus) event.detail |= SWT.FOCUSED;
+ event.x = contentX;
+ event.y = cellBounds.y;
+ event.width = contentWidth;
+ event.height = cellBounds.height;
+ gc.setClipping (cellBounds);
+ parent.sendEvent (SWT.PaintItem, event);
+ event.gc = null;
+ if (gc.isDisposed ()) return false;
+ gc.setAlpha (oldAlpha);
+ gc.setAntialias (oldAntialias);
+ gc.setBackgroundPattern (oldBackgroundPattern);
+ gc.setClipping (cellBounds);
+ gc.setForegroundPattern (oldForegroundPattern);
+ gc.setInterpolation (oldInterpolation);
+ gc.setTextAntialias (oldTextAntialias);
+ gc.setAdvanced (oldAdvanced);
+ drawFocus = isFocusItem && (event.detail & SWT.FOCUSED) != 0;
+ }
+
+ return isFocusItem && !drawFocus;
+}
+/*
+ * Redraw part of the receiver. If either EraseItem or PaintItem is hooked then
+ * only full cells should be damaged, so adjust accordingly. If neither of these
+ * events are hooked then the exact bounds given for damaging can be used.
+ */
+void redraw (int x, int y, int width, int height, int columnIndex) {
+ if (!parent.hooks (SWT.EraseItem) && !parent.hooks (SWT.PaintItem)) {
+ parent.redraw (x, y, width, height, false);
+ return;
+ }
+ Rectangle cellBounds = getCellBounds (columnIndex);
+ parent.redraw (cellBounds.x, cellBounds.y, cellBounds.width, cellBounds.height, false);
+}
+void redrawItem () {
+ parent.redraw (0, parent.getItemY (this), parent.clientArea.width, parent.itemHeight, false);
+}
+/*
+ * Updates internal structures in the receiver and its child items to handle the removal of a column.
+ */
+void removeColumn (TableColumn column, int index) {
+ int columnCount = parent.columns.length;
+
+ if (columnCount == 0) {
+ /* reverts to normal table when last column disposed */
+ cellBackgrounds = cellForegrounds = null;
+ displayTexts = null;
+ cellFonts = null;
+ fontHeights = null;
+ GC gc = new GC (parent);
+ computeTextWidths (gc);
+ gc.dispose ();
+ return;
+ }
+
+ String[] newTexts = new String [columnCount];
+ System.arraycopy (texts, 0, newTexts, 0, index);
+ System.arraycopy (texts, index + 1, newTexts, index, columnCount - index);
+ texts = newTexts;
+
+ Image[] newImages = new Image [columnCount];
+ System.arraycopy (images, 0, newImages, 0, index);
+ System.arraycopy (images, index + 1, newImages, index, columnCount - index);
+ images = newImages;
+
+ int[] newTextWidths = new int [columnCount];
+ System.arraycopy (textWidths, 0, newTextWidths, 0, index);
+ System.arraycopy (textWidths, index + 1, newTextWidths, index, columnCount - index);
+ textWidths = newTextWidths;
+
+ String[] newDisplayTexts = new String [columnCount];
+ System.arraycopy (displayTexts, 0, newDisplayTexts, 0, index);
+ System.arraycopy (displayTexts, index + 1, newDisplayTexts, index, columnCount - index);
+ displayTexts = newDisplayTexts;
+
+ if (cellBackgrounds != null) {
+ Color[] newCellBackgrounds = new Color [columnCount];
+ System.arraycopy (cellBackgrounds, 0, newCellBackgrounds, 0, index);
+ System.arraycopy (cellBackgrounds, index + 1, newCellBackgrounds, index, columnCount - index);
+ cellBackgrounds = newCellBackgrounds;
+ }
+ if (cellForegrounds != null) {
+ Color[] newCellForegrounds = new Color [columnCount];
+ System.arraycopy (cellForegrounds, 0, newCellForegrounds, 0, index);
+ System.arraycopy (cellForegrounds, index + 1, newCellForegrounds, index, columnCount - index);
+ cellForegrounds = newCellForegrounds;
+ }
+ if (cellFonts != null) {
+ Font[] newCellFonts = new Font [columnCount];
+ System.arraycopy (cellFonts, 0, newCellFonts, 0, index);
+ System.arraycopy (cellFonts, index + 1, newCellFonts, index, columnCount - index);
+ cellFonts = newCellFonts;
+
+ int[] newFontHeights = new int [columnCount];
+ System.arraycopy (fontHeights, 0, newFontHeights, 0, index);
+ System.arraycopy (fontHeights, index + 1, newFontHeights, index, columnCount - index);
+ fontHeights = newFontHeights;
+ }
+
+ if (index == 0) {
+ text = texts [0] != null ? texts [0] : ""; //$NON-NLS-1$
+ texts [0] = null;
+ image = images [0];
+ images [0] = null;
+ /*
+ * The new first column may not have as much width available to it as it did when it was
+ * the second column if checkboxes are being shown, so recompute its displayText if needed.
+ */
+ if ((parent.style & SWT.CHECK) != 0) {
+ GC gc = new GC (parent);
+ gc.setFont (getFont (0, false));
+ computeDisplayText (0, gc);
+ gc.dispose ();
+ }
+ }
+ if (columnCount < 2) {
+ texts = null;
+ images = null;
+ }
+}
+/**
+ * Sets the receiver's background color to the color specified
+ * by the argument, or to the default system color for the item
+ * if the argument is null.
+ *
+ * @param color the new color (or null)
+ *
+ * @exception IllegalArgumentException <ul>
+ * <li>ERROR_INVALID_ARGUMENT - if the argument has been disposed</li>
+ * </ul>
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @since 2.0
+ */
+public void setBackground (Color color) {
+ checkWidget ();
+ if (color != null && color.isDisposed ()) {
+ SWT.error (SWT.ERROR_INVALID_ARGUMENT);
+ }
+ Color oldColor = background;
+ if (oldColor == color) return;
+ background = color;
+ if (oldColor != null && oldColor.equals (color)) return;
+ if ((parent.style & SWT.VIRTUAL) != 0) cached = true;
+ redrawItem ();
+}
+/**
+ * Sets the background color at the given column index in the receiver
+ * to the color specified by the argument, or to the default system color for the item
+ * if the argument is null.
+ *
+ * @param index the column index
+ * @param color the new color (or null)
+ *
+ * @exception IllegalArgumentException <ul>
+ * <li>ERROR_INVALID_ARGUMENT - if the argument has been disposed</li>
+ * </ul>
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @since 3.0
+ */
+public void setBackground (int columnIndex, Color color) {
+ checkWidget ();
+ if (color != null && color.isDisposed ()) {
+ SWT.error (SWT.ERROR_INVALID_ARGUMENT);
+ }
+ int validColumnCount = Math.max (1, parent.columns.length);
+ if (!(0 <= columnIndex && columnIndex < validColumnCount)) return;
+ if (cellBackgrounds == null) {
+ if (color == null) return;
+ cellBackgrounds = new Color [validColumnCount];
+ }
+ Color oldColor = cellBackgrounds [columnIndex];
+ if (oldColor == color) return;
+ cellBackgrounds [columnIndex] = color;
+ if (oldColor != null && oldColor.equals (color)) return;
+ if ((parent.style & SWT.VIRTUAL) != 0) cached = true;
+
+ if (isInViewport ()) {
+ Rectangle bounds = getCellBounds (columnIndex);
+ parent.redraw (bounds.x, bounds.y, bounds.width, bounds.height, false);
+ }
+}
+/**
+ * Sets the checked state of the checkbox for this item. This state change
+ * only applies if the Table was created with the SWT.CHECK style.
+ *
+ * @param checked the new checked state of the checkbox
+ *
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public void setChecked (boolean value) {
+ checkWidget ();
+ if ((parent.getStyle () & SWT.CHECK) == 0) return;
+ if (checked == value) return;
+ checked = value;
+ if ((parent.style & SWT.VIRTUAL) != 0) cached = true;
+
+ if (isInViewport ()) {
+ if (parent.hooks (SWT.EraseItem) || parent.hooks (SWT.PaintItem)) {
+ redrawItem ();
+ } else {
+ Rectangle bounds = getCheckboxBounds ();
+ parent.redraw (bounds.x, bounds.y, bounds.width, bounds.height, false);
+ }
+ }
+}
+/**
+ * Sets the font that the receiver will use to paint textual information
+ * for this item to the font specified by the argument, or to the default font
+ * for that kind of control if the argument is null.
+ *
+ * @param font the new font (or null)
+ *
+ * @exception IllegalArgumentException <ul>
+ * <li>ERROR_INVALID_ARGUMENT - if the argument has been disposed</li>
+ * </ul>
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @since 3.0
+ */
+public void setFont (Font font) {
+ checkWidget ();
+ if (font != null && font.isDisposed ()) {
+ SWT.error (SWT.ERROR_INVALID_ARGUMENT);
+ }
+ Font oldFont = this.font;
+ if (oldFont == font) return;
+ this.font = font;
+ if (oldFont != null && oldFont.equals (font)) return;
+
+ Rectangle bounds = getBounds (false);
+ int oldRightX = bounds.x + bounds.width;
+ if ((parent.style & SWT.VIRTUAL) != 0) cached = true;
+
+ /* recompute cached values for string measurements */
+ GC gc = new GC (parent);
+ gc.setFont (getFont (false));
+ fontHeight = gc.getFontMetrics ().getHeight ();
+ computeDisplayTexts (gc);
+ computeTextWidths (gc);
+ gc.dispose ();
+
+ /* horizontal bar could be affected if table has no columns */
+ if (parent.columns.length == 0) {
+ bounds = getBounds (false);
+ int newRightX = bounds.x + bounds.width;
+ parent.updateHorizontalBar (newRightX, newRightX - oldRightX);
+ }
+ redrawItem ();
+}
+/**
+ * Sets the font that the receiver will use to paint textual information
+ * for the specified cell in this item to the font specified by the
+ * argument, or to the default font for that kind of control if the
+ * argument is null.
+ *
+ * @param index the column index
+ * @param font the new font (or null)
+ *
+ * @exception IllegalArgumentException <ul>
+ * <li>ERROR_INVALID_ARGUMENT - if the argument has been disposed</li>
+ * </ul>
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @since 3.0
+ */
+public void setFont (int columnIndex, Font font) {
+ checkWidget ();
+ if (font != null && font.isDisposed ()) {
+ SWT.error (SWT.ERROR_INVALID_ARGUMENT);
+ }
+
+ int validColumnCount = Math.max (1, parent.columns.length);
+ if (!(0 <= columnIndex && columnIndex < validColumnCount)) return;
+ if (cellFonts == null) {
+ if (font == null) return;
+ cellFonts = new Font [validColumnCount];
+ }
+ Font oldFont = cellFonts [columnIndex];
+ if (oldFont == font) return;
+ cellFonts [columnIndex] = font;
+ if (oldFont != null && oldFont.equals (font)) return;
+ if ((parent.style & SWT.VIRTUAL) != 0) cached = true;
+
+ /* recompute cached values for string measurements */
+ GC gc = new GC (parent);
+ gc.setFont (getFont (columnIndex, false));
+ if (fontHeights == null) fontHeights = new int [validColumnCount];
+ fontHeights [columnIndex] = gc.getFontMetrics ().getHeight ();
+ computeDisplayText (columnIndex, gc);
+ gc.dispose ();
+
+ if (isInViewport ()) {
+ Rectangle bounds = getCellBounds (columnIndex);
+ parent.redraw (bounds.x, bounds.y, bounds.width, bounds.height, false);
+ }
+}
+/**
+ * Sets the receiver's foreground color to the color specified
+ * by the argument, or to the default system color for the item
+ * if the argument is null.
+ *
+ * @param color the new color (or null)
+ *
+ * @exception IllegalArgumentException <ul>
+ * <li>ERROR_INVALID_ARGUMENT - if the argument has been disposed</li>
+ * </ul>
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @since 2.0
+ */
+public void setForeground (Color color) {
+ checkWidget ();
+ if (color != null && color.isDisposed ()) {
+ SWT.error (SWT.ERROR_INVALID_ARGUMENT);
+ }
+ Color oldColor = foreground;
+ if (oldColor == color) return;
+ foreground = color;
+ if (oldColor != null && oldColor.equals (color)) return;
+ if ((parent.style & SWT.VIRTUAL) != 0) cached = true;
+ redrawItem ();
+}
+/**
+ * Sets the foreground color at the given column index in the receiver
+ * to the color specified by the argument, or to the default system color for the item
+ * if the argument is null.
+ *
+ * @param index the column index
+ * @param color the new color (or null)
+ *
+ * @exception IllegalArgumentException <ul>
+ * <li>ERROR_INVALID_ARGUMENT - if the argument has been disposed</li>
+ * </ul>
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @since 3.0
+ */
+public void setForeground (int columnIndex, Color color) {
+ checkWidget ();
+ if (color != null && color.isDisposed ()) {
+ SWT.error (SWT.ERROR_INVALID_ARGUMENT);
+ }
+ int validColumnCount = Math.max (1, parent.columns.length);
+ if (!(0 <= columnIndex && columnIndex < validColumnCount)) return;
+ if (cellForegrounds == null) {
+ if (color == null) return;
+ cellForegrounds = new Color [validColumnCount];
+ }
+ Color oldColor = cellForegrounds [columnIndex];
+ if (oldColor == color) return;
+ cellForegrounds [columnIndex] = color;
+ if (oldColor != null && oldColor.equals (color)) return;
+ if ((parent.style & SWT.VIRTUAL) != 0) cached = true;
+
+ if (isInViewport ()) {
+ redraw (
+ getTextX (columnIndex),
+ parent.getItemY (this),
+ textWidths [columnIndex] + 2 * MARGIN_TEXT,
+ parent.itemHeight,
+ columnIndex);
+ }
+}
+/**
+ * Sets the grayed state of the checkbox for this item. This state change
+ * only applies if the Table was created with the SWT.CHECK style.
+ *
+ * @param grayed the new grayed state of the checkbox;
+ *
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public void setGrayed (boolean value) {
+ checkWidget ();
+ if ((parent.getStyle () & SWT.CHECK) == 0) return;
+ if (grayed == value) return;
+ grayed = value;
+ if ((parent.style & SWT.VIRTUAL) != 0) cached = true;
+
+ if (isInViewport ()) {
+ Rectangle bounds = getCheckboxBounds ();
+ parent.redraw (bounds.x, bounds.y, bounds.width, bounds.height, false);
+ }
+}
+public void setImage (Image value) {
+ checkWidget ();
+ setImage (0, value);
+}
+/**
+ * Sets the image for multiple columns in the table.
+ *
+ * @param images the array of new images
+ *
+ * @exception IllegalArgumentException <ul>
+ * <li>ERROR_NULL_ARGUMENT - if the array of images is null</li>
+ * <li>ERROR_INVALID_ARGUMENT - if one of the images has been disposed</li>
+ * </ul>
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public void setImage (Image[] value) {
+ checkWidget ();
+ if (value == null) error (SWT.ERROR_NULL_ARGUMENT);
+
+ // TODO make a smarter implementation of this
+ for (int i = 0; i < value.length; i++) {
+ if (value [i] != null) setImage (i, value [i]);
+ }
+}
+/**
+ * Sets the receiver's image at a column.
+ *
+ * @param index the column index
+ * @param image the new image
+ *
+ * @exception IllegalArgumentException <ul>
+ * <li>ERROR_INVALID_ARGUMENT - if the image has been disposed</li>
+ * </ul>
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public void setImage (int columnIndex, Image value) {
+ checkWidget ();
+ if (value != null && value.isDisposed ()) error (SWT.ERROR_INVALID_ARGUMENT);
+
+ TableColumn[] columns = parent.columns;
+ int validColumnCount = Math.max (1, columns.length);
+ if (!(0 <= columnIndex && columnIndex < validColumnCount)) return;
+ Image image = getImage (columnIndex, false);
+ if (value == image) return;
+ if (value != null && value.equals (image)) return;
+ if (columnIndex == 0) {
+ super.setImage (value);
+ } else {
+ images [columnIndex] = value;
+ }
+ if ((parent.style & SWT.VIRTUAL) != 0) cached = true;
+
+ /*
+ * An image width change may affect the space available for the item text, so
+ * recompute the displayText if there are columns.
+ */
+ if (columns.length > 0) {
+ GC gc = new GC (parent);
+ gc.setFont (getFont (columnIndex, false));
+ computeDisplayText (columnIndex, gc);
+ gc.dispose ();
+ }
+
+ if (value == null) {
+ redrawItem (); // TODO why the whole item?
+ return;
+ }
+
+ /*
+ * If this is the first image being put into the table then its item height
+ * may be adjusted, in which case a full redraw is needed.
+ */
+ if (parent.imageHeight == 0) {
+ int oldItemHeight = parent.itemHeight;
+ parent.setImageHeight (value.getBounds ().height);
+ if (oldItemHeight != parent.itemHeight) {
+ if (columnIndex == 0) {
+ parent.col0ImageWidth = value.getBounds ().width;
+ if (columns.length > 0) {
+ /*
+ * All column 0 cells will now have less room available for their texts,
+ * so all items must now recompute their column 0 displayTexts.
+ */
+ GC gc = new GC (parent);
+ TableItem[] rootItems = parent.items;
+ for (int i = 0; i < parent.itemsCount; i++) {
+ rootItems [i].updateColumnWidth (columns [0], gc);
+ }
+ gc.dispose ();
+ }
+ }
+ parent.redraw ();
+ return;
+ }
+ }
+
+ /*
+ * If this is the first image being put into column 0 then all cells
+ * in the column should also indent accordingly.
+ */
+ if (columnIndex == 0 && parent.col0ImageWidth == 0) {
+ parent.col0ImageWidth = value.getBounds ().width;
+ /* redraw the column */
+ if (columns.length == 0) {
+ parent.redraw ();
+ } else {
+ /*
+ * All column 0 cells will now have less room available for their texts,
+ * so all items must now recompute their column 0 displayTexts.
+ */
+ GC gc = new GC (parent);
+ TableItem[] rootItems = parent.items;
+ for (int i = 0; i < parent.itemsCount; i++) {
+ rootItems [i].updateColumnWidth (columns [0], gc);
+ }
+ gc.dispose ();
+ parent.redraw (
+ columns [0].getX (), 0,
+ columns [0].width,
+ parent.clientArea.height,
+ false);
+ }
+ return;
+ }
+ redrawItem (); // TODO why the whole item?
+}
+/**
+ * Sets the indent of the first column's image, expressed in terms of the image's width.
+ *
+ * @param indent the new indent
+ *
+ * </ul>
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @deprecated this functionality is not supported on most platforms
+ */
+public void setImageIndent (int indent) {
+ checkWidget();
+ if (indent < 0) return;
+ if (imageIndent == indent) return;
+ imageIndent = indent;
+ if ((parent.style & SWT.VIRTUAL) != 0) cached = true;
+}
+/**
+ * Sets the receiver's text at a column
+ *
+ * @param index the column index
+ * @param string the new text
+ *
+ * @exception IllegalArgumentException <ul>
+ * <li>ERROR_NULL_ARGUMENT - if the text is null</li>
+ * </ul>
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public void setText (int columnIndex, String value) {
+ checkWidget ();
+ if (value == null) error (SWT.ERROR_NULL_ARGUMENT);
+ int validColumnCount = Math.max (1, parent.columns.length);
+ if (!(0 <= columnIndex && columnIndex < validColumnCount)) return;
+ if (value.equals (getText (columnIndex, false))) return;
+ if (columnIndex == 0) {
+ super.setText (value);
+ } else {
+ texts [columnIndex] = value;
+ }
+ if ((parent.style & SWT.VIRTUAL) != 0) cached = true;
+
+ int oldWidth = textWidths [columnIndex];
+ GC gc = new GC (parent);
+ gc.setFont (getFont (columnIndex, false));
+ computeDisplayText (columnIndex, gc);
+ gc.dispose ();
+
+ if (parent.columns.length == 0) {
+ Rectangle bounds = getBounds (false);
+ int rightX = bounds.x + bounds.width;
+ parent.updateHorizontalBar (rightX, textWidths [columnIndex] - oldWidth);
+ }
+ if (isInViewport ()) {
+ redraw (
+ getTextX (columnIndex),
+ parent.getItemY (this),
+ Math.max (oldWidth, textWidths [columnIndex]) + 2 * MARGIN_TEXT,
+ parent.itemHeight,
+ columnIndex);
+ }
+}
+public void setText (String value) {
+ checkWidget ();
+ Rectangle bounds = getBounds (false);
+ int oldRightX = bounds.x + bounds.width;
+ setText (0, value);
+ /* horizontal bar could be affected if table has no columns */
+ if (parent.columns.length == 0) {
+ bounds = getBounds (false);
+ int newRightX = bounds.x + bounds.width;
+ parent.updateHorizontalBar (newRightX, newRightX - oldRightX);
+ }
+}
+/**
+ * Sets the text for multiple columns in the table.
+ *
+ * @param strings the array of new strings
+ *
+ * @exception IllegalArgumentException <ul>
+ * <li>ERROR_NULL_ARGUMENT - if the text is null</li>
+ * </ul>
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public void setText (String[] value) {
+ checkWidget ();
+ if (value == null) error (SWT.ERROR_NULL_ARGUMENT);
+ Rectangle bounds = getBounds (false);
+ int oldRightX = bounds.x + bounds.width;
+ // TODO make a smarter implementation of this
+ for (int i = 0; i < value.length; i++) {
+ if (value [i] != null) setText (i, value [i]);
+ }
+ /* horizontal bar could be affected if table has no columns */
+ if (parent.columns.length == 0) {
+ bounds = getBounds (false);
+ int newRightX = bounds.x + bounds.width;
+ parent.updateHorizontalBar (newRightX, newRightX - oldRightX);
+ }
+}
+/*
+ * Perform any internal changes necessary to reflect a changed column width.
+ */
+void updateColumnWidth (TableColumn column, GC gc) {
+ int columnIndex = column.getIndex ();
+ gc.setFont (getFont (columnIndex, false));
+ String oldDisplayText = displayTexts [columnIndex];
+ computeDisplayText (columnIndex, gc);
+
+ /* the cell must be damaged if there is custom drawing being done or if the alignment is not LEFT */
+ if (isInViewport ()) {
+ boolean columnIsLeft = (column.style & SWT.LEFT) != 0;
+ if (!columnIsLeft || parent.hooks (SWT.EraseItem) || parent.hooks (SWT.PaintItem)) {
+ Rectangle cellBounds = getCellBounds (columnIndex);
+ parent.redraw (cellBounds.x, cellBounds.y, cellBounds.width, cellBounds.height, false);
+ return;
+ }
+ /* if the display text has changed then the cell text must be damaged in order to repaint */
+ if (oldDisplayText == null || !oldDisplayText.equals (displayTexts [columnIndex])) {
+ Rectangle cellBounds = getCellBounds (columnIndex);
+ int textX = getTextX (columnIndex);
+ parent.redraw (textX, cellBounds.y, cellBounds.x + cellBounds.width - textX, cellBounds.height, false);
+ }
+ }
+}
+/*
+ * The parent's font has changed, so if this font was being used by the receiver then
+ * recompute its cached text sizes using the gc argument.
+ */
+void updateFont (GC gc) {
+ if (font == null) { /* receiver is using the Table's font */
+ computeDisplayTexts (gc);
+ computeTextWidths (gc);
+ }
+}
+}
diff --git a/bundles/org.eclipse.swt/Eclipse SWT/emulated/treetable/org/eclipse/swt/widgets/Tree.java b/bundles/org.eclipse.swt/Eclipse SWT/emulated/treetable/org/eclipse/swt/widgets/Tree.java
new file mode 100644
index 0000000000..1e65f8c530
--- /dev/null
+++ b/bundles/org.eclipse.swt/Eclipse SWT/emulated/treetable/org/eclipse/swt/widgets/Tree.java
@@ -0,0 +1,4295 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2009 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.swt.widgets;
+
+import org.eclipse.swt.*;
+import org.eclipse.swt.events.*;
+import org.eclipse.swt.graphics.*;
+import org.eclipse.swt.internal.*;
+
+/**
+ * Instances of this class provide a selectable user interface object
+ * that displays a hierarchy of items and issues notification when an
+ * item in the hierarchy is selected.
+ * <p>
+ * The item children that may be added to instances of this class
+ * must be of type <code>TreeItem</code>.
+ * </p><p>
+ * Style <code>VIRTUAL</code> is used to create a <code>Tree</code> whose
+ * <code>TreeItem</code>s are to be populated by the client on an on-demand basis
+ * instead of up-front. This can provide significant performance improvements for
+ * trees that are very large or for which <code>TreeItem</code> population is
+ * expensive (for example, retrieving values from an external source).
+ * </p><p>
+ * Here is an example of using a <code>Tree</code> with style <code>VIRTUAL</code>:
+ * <code><pre>
+ * final Tree tree = new Tree(parent, SWT.VIRTUAL | SWT.BORDER);
+ * tree.setItemCount(20);
+ * tree.addListener(SWT.SetData, new Listener() {
+ * public void handleEvent(Event event) {
+ * TreeItem item = (TreeItem)event.item;
+ * TreeItem parentItem = item.getParentItem();
+ * String text = null;
+ * if (parentItem == null) {
+ * text = "node " + tree.indexOf(item);
+ * } else {
+ * text = parentItem.getText() + " - " + parentItem.indexOf(item);
+ * }
+ * item.setText(text);
+ * System.out.println(text);
+ * item.setItemCount(10);
+ * }
+ * });
+ * </pre></code>
+ * </p><p>
+ * Note that although this class is a subclass of <code>Composite</code>,
+ * it does not normally make sense to add <code>Control</code> children to
+ * it, or set a layout on it, unless implementing something like a cell
+ * editor.
+ * </p><p>
+ * <dl>
+ * <dt><b>Styles:</b></dt>
+ * <dd>SINGLE, MULTI, CHECK, FULL_SELECTION, VIRTUAL, NO_SCROLL</dd>
+ * <dt><b>Events:</b></dt>
+ * <dd>Selection, DefaultSelection, Collapse, Expand, SetData, MeasureItem, EraseItem, PaintItem</dd>
+ * </dl>
+ * </p><p>
+ * Note: Only one of the styles SINGLE and MULTI may be specified.
+ * </p><p>
+ * IMPORTANT: This class is <em>not</em> intended to be subclassed.
+ * </p>
+ *
+ * @see <a href="http://www.eclipse.org/swt/snippets/#tree">Tree, TreeItem, TreeColumn snippets</a>
+ * @see <a href="http://www.eclipse.org/swt/examples.php">SWT Example: ControlExample</a>
+ * @see <a href="http://www.eclipse.org/swt/">Sample code and further information</a>
+ * @noextend This class is not intended to be subclassed by clients.
+ */
+public class Tree extends Composite {
+ Canvas header;
+ TreeColumn[] columns = new TreeColumn [0];
+ TreeColumn[] orderedColumns;
+ TreeItem[] items = NO_ITEMS;
+ TreeItem[] availableItems = NO_ITEMS;
+ TreeItem[] selectedItems = NO_ITEMS;
+ TreeItem focusItem, anchorItem, insertMarkItem;
+ TreeItem lastClickedItem;
+ Event lastSelectionEvent;
+ int availableItemsCount = 0;
+ boolean insertMarkPrecedes = false;
+ boolean linesVisible, ignoreKey, ignoreDispose, customHeightSet;
+ int topIndex = 0, horizontalOffset = 0;
+ int fontHeight = 0, imageHeight = 0, itemHeight = 0;
+ int headerImageHeight = 0, orderedCol0imageWidth = 0;
+ TreeColumn resizeColumn;
+ int resizeColumnX = -1;
+ int drawCount = 0;
+ boolean inExpand = false; /* for item creation within Expand callback */
+ TreeColumn sortColumn;
+ int sortDirection = SWT.NONE;
+
+ /* column header tooltip */
+ Listener toolTipListener;
+ Shell toolTipShell;
+ Label toolTipLabel;
+
+ Rectangle arrowBounds, expanderBounds, checkboxBounds, clientArea;
+
+ static final TreeItem[] NO_ITEMS = new TreeItem [0];
+
+ static final int MARGIN_IMAGE = 3;
+ static final int MARGIN_CELL = 1;
+ static final int SIZE_HORIZONTALSCROLL = 5;
+ static final int TOLLERANCE_COLUMNRESIZE = 2;
+ static final int WIDTH_HEADER_SHADOW = 2;
+ static final int WIDTH_CELL_HIGHLIGHT = 1;
+ static final int [] toolTipEvents = new int[] {SWT.MouseExit, SWT.MouseHover, SWT.MouseMove, SWT.MouseDown};
+ static final String ELLIPSIS = "..."; //$NON-NLS-1$
+ static final String ID_EXPANDED = "EXPANDED"; //$NON-NLS-1$
+ static final String ID_COLLAPSED = "COLLAPSED"; //$NON-NLS-1$
+ static final String ID_UNCHECKED = "UNCHECKED"; //$NON-NLS-1$
+ static final String ID_GRAYUNCHECKED = "GRAYUNCHECKED"; //$NON-NLS-1$
+ static final String ID_CHECKMARK = "CHECKMARK"; //$NON-NLS-1$
+ static final String ID_CONNECTOR_COLOR = "CONNECTOR_COLOR"; //$NON-NLS-1$
+ static final String ID_ARROWUP = "ARROWUP"; //$NON-NLS-1$
+ static final String ID_ARROWDOWN = "ARROWDOWN"; //$NON-NLS-1$
+
+// TEMPORARY CODE
+boolean hasFocus;
+
+public boolean isFocusControl() {
+ return hasFocus;
+}
+
+/**
+ * Constructs a new instance of this class given its parent
+ * and a style value describing its behavior and appearance.
+ * <p>
+ * The style value is either one of the style constants defined in
+ * class <code>SWT</code> which is applicable to instances of this
+ * class, or must be built by <em>bitwise OR</em>'ing together
+ * (that is, using the <code>int</code> "|" operator) two or more
+ * of those <code>SWT</code> style constants. The class description
+ * lists the style constants that are applicable to the class.
+ * Style bits are also inherited from superclasses.
+ * </p>
+ *
+ * @param parent a composite control which will be the parent of the new instance (cannot be null)
+ * @param style the style of control to construct
+ *
+ * @exception IllegalArgumentException <ul>
+ * <li>ERROR_NULL_ARGUMENT - if the parent is null</li>
+ * </ul>
+ * @exception SWTException <ul>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the parent</li>
+ * <li>ERROR_INVALID_SUBCLASS - if this class is not an allowed subclass</li>
+ * </ul>
+ *
+ * @see SWT#SINGLE
+ * @see SWT#MULTI
+ * @see SWT#CHECK
+ * @see SWT#FULL_SELECTION
+ * @see SWT#VIRTUAL
+ * @see SWT#NO_SCROLL
+ * @see Widget#checkSubclass
+ * @see Widget#getStyle
+ */
+public Tree (Composite parent, int style) {
+ super (parent, checkStyle (style));
+ setForeground (null); /* set foreground and background to chosen default colors */
+ setBackground (null);
+ GC gc = new GC (this);
+ fontHeight = gc.getFontMetrics ().getHeight ();
+ gc.dispose ();
+ itemHeight = fontHeight + (2 * getCellPadding ());
+ initImages (display);
+ expanderBounds = getExpandedImage ().getBounds ();
+ checkboxBounds = getUncheckedImage ().getBounds ();
+ arrowBounds = getArrowDownImage ().getBounds ();
+ clientArea = getClientArea ();
+
+ Listener listener = new Listener () {
+ public void handleEvent (Event event) {
+ handleEvents (event);
+ }
+ };
+ addListener (SWT.Paint, listener);
+ addListener (SWT.MouseDown, listener);
+ addListener (SWT.MouseUp, listener);
+ addListener (SWT.MouseDoubleClick, listener);
+ addListener (SWT.Dispose, listener);
+ addListener (SWT.Resize, listener);
+ addListener (SWT.KeyDown, listener);
+ addListener (SWT.FocusOut, listener);
+ addListener (SWT.FocusIn, listener);
+ addListener (SWT.Traverse, listener);
+
+ header = new Canvas (this, SWT.NO_REDRAW_RESIZE | SWT.NO_FOCUS);
+ header.setVisible (false);
+ header.setBounds (0, 0, 0, fontHeight + 2 * getHeaderPadding ());
+ header.addListener (SWT.Paint, listener);
+ header.addListener (SWT.MouseDown, listener);
+ header.addListener (SWT.MouseUp, listener);
+ header.addListener (SWT.MouseHover, listener);
+ header.addListener (SWT.MouseDoubleClick, listener);
+ header.addListener (SWT.MouseMove, listener);
+ header.addListener (SWT.MouseExit, listener);
+ header.addListener (SWT.MenuDetect, listener);
+
+ toolTipListener = new Listener () {
+ public void handleEvent (Event event) {
+ switch (event.type) {
+ case SWT.MouseHover:
+ case SWT.MouseMove:
+ if (headerUpdateToolTip (event.x)) break;
+ // FALL THROUGH
+ case SWT.MouseExit:
+ case SWT.MouseDown:
+ headerHideToolTip ();
+ break;
+ }
+ }
+ };
+
+ ScrollBar hBar = getHorizontalBar ();
+ if (hBar != null) {
+ hBar.setValues (0, 0, 1, 1, 1, 1);
+ hBar.setVisible (false);
+ hBar.addListener (SWT.Selection, listener);
+ }
+ ScrollBar vBar = getVerticalBar ();
+ if (vBar != null) {
+ vBar.setValues (0, 0, 1, 1, 1, 1);
+ vBar.setVisible (false);
+ vBar.addListener (SWT.Selection, listener);
+ }
+}
+/**
+ * Adds the listener to the collection of listeners who will
+ * be notified when the user changes the receiver's selection, by sending
+ * it one of the messages defined in the <code>SelectionListener</code>
+ * interface.
+ * <p>
+ * When <code>widgetSelected</code> is called, the item field of the event object is valid.
+ * If the receiver has the <code>SWT.CHECK</code> style and the check selection changes,
+ * the event object detail field contains the value <code>SWT.CHECK</code>.
+ * <code>widgetDefaultSelected</code> is typically called when an item is double-clicked.
+ * The item field of the event object is valid for default selection, but the detail field is not used.
+ * </p>
+ *
+ * @param listener the listener which should be notified when the user changes the receiver's selection
+ *
+ * @exception IllegalArgumentException <ul>
+ * <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
+ * </ul>
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @see SelectionListener
+ * @see #removeSelectionListener
+ * @see SelectionEvent
+ */
+public void addSelectionListener (SelectionListener listener) {
+ checkWidget ();
+ if (listener == null) error (SWT.ERROR_NULL_ARGUMENT);
+ TypedListener typedListener = new TypedListener (listener);
+ addListener (SWT.Selection, typedListener);
+ addListener (SWT.DefaultSelection, typedListener);
+}
+/**
+ * Adds the listener to the collection of listeners who will
+ * be notified when an item in the receiver is expanded or collapsed
+ * by sending it one of the messages defined in the <code>TreeListener</code>
+ * interface.
+ *
+ * @param listener the listener which should be notified
+ *
+ * @exception IllegalArgumentException <ul>
+ * <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
+ * </ul>
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @see TreeListener
+ * @see #removeTreeListener
+ */
+public void addTreeListener (TreeListener listener) {
+ checkWidget ();
+ if (listener == null) error (SWT.ERROR_NULL_ARGUMENT);
+ TypedListener typedListener = new TypedListener (listener);
+ addListener (SWT.Expand, typedListener);
+ addListener (SWT.Collapse, typedListener);
+}
+boolean checkData (TreeItem item, boolean redraw) {
+ if (item.cached) return true;
+ if ((style & SWT.VIRTUAL) != 0) {
+ item.cached = true;
+ Event event = new Event ();
+ TreeItem parentItem = item.getParentItem ();
+ event.item = item;
+ event.index = parentItem == null ? indexOf (item) : parentItem.indexOf (item);
+ sendEvent (SWT.SetData, event);
+ if (isDisposed () || item.isDisposed ()) return false;
+ if (redraw) redrawItem (item.availableIndex, false);
+ }
+ return true;
+}
+static int checkStyle (int style) {
+ /*
+ * Feature in Windows. Even when WS_HSCROLL or
+ * WS_VSCROLL is not specified, Windows creates
+ * trees and tables with scroll bars. The fix
+ * is to set H_SCROLL and V_SCROLL.
+ *
+ * NOTE: This code appears on all platforms so that
+ * applications have consistent scroll bar behavior.
+ */
+ if ((style & SWT.NO_SCROLL) == 0) {
+ style |= SWT.H_SCROLL | SWT.V_SCROLL;
+ }
+ style |= SWT.NO_REDRAW_RESIZE | SWT.NO_BACKGROUND | SWT.DOUBLE_BUFFERED;
+ //TEMPORARY CODE
+ style |= SWT.FULL_SELECTION;
+ return checkBits (style, SWT.SINGLE, SWT.MULTI, 0, 0, 0, 0);
+}
+protected void checkSubclass () {
+ if (!isValidSubclass ()) error (SWT.ERROR_INVALID_SUBCLASS);
+}
+/**
+ * Clears the item at the given zero-relative index in the receiver.
+ * The text, icon and other attributes of the item are set to the default
+ * value. If the tree was created with the <code>SWT.VIRTUAL</code> style,
+ * these attributes are requested again as needed.
+ *
+ * @param index the index of the item to clear
+ * @param all <code>true</code> if all child items of the indexed item should be
+ * cleared recursively, and <code>false</code> otherwise
+ *
+ * @exception IllegalArgumentException <ul>
+ * <li>ERROR_INVALID_RANGE - if the index is not between 0 and the number of elements in the list minus 1 (inclusive)</li>
+ * </ul>
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @see SWT#VIRTUAL
+ * @see SWT#SetData
+ *
+ * @since 3.2
+ */
+public void clear (int index, boolean recursive) {
+ checkWidget ();
+ if (!(0 <= index && index < items.length)) error (SWT.ERROR_INVALID_RANGE);
+ TreeItem item = items [index];
+
+ /* if there are no columns then the horizontal scrollbar may need adjusting */
+ TreeItem[] availableDescendents = null;
+ int oldRightX = 0;
+ if (columns.length == 0) {
+ if (recursive) {
+ availableDescendents = item.computeAvailableDescendents ();
+ for (int i = 0; i < availableDescendents.length; i++) {
+ Rectangle bounds = availableDescendents [i].getBounds (false);
+ oldRightX = Math.max (oldRightX, bounds.x + bounds.width);
+ }
+ } else {
+ Rectangle bounds = item.getBounds (false);
+ oldRightX = bounds.x + bounds.width;
+ }
+ }
+
+ /* clear the item(s) */
+ item.clear ();
+ if (recursive) {
+ item.clearAll (true, false);
+ }
+
+ /* adjust the horizontal scrollbar if needed */
+ if (columns.length == 0) {
+ int newRightX = 0;
+ if (recursive) {
+ for (int i = 0; i < availableDescendents.length; i++) {
+ Rectangle bounds = availableDescendents [i].getBounds (false);
+ newRightX = Math.max (newRightX, bounds.x + bounds.width);
+ }
+ } else {
+ Rectangle bounds = item.getBounds (false);
+ newRightX = bounds.x + bounds.width;
+ }
+ updateHorizontalBar (newRightX, newRightX - oldRightX);
+ }
+
+ /* redraw the item(s) */
+ if (recursive && item.expanded) {
+ int descendentCount = availableDescendents == null ?
+ item.computeAvailableDescendentCount () :
+ availableDescendents.length;
+ redrawItems (item.availableIndex, item.availableIndex + descendentCount - 1, false);
+ } else {
+ redrawItem (item.availableIndex, false);
+ }
+}
+/**
+ * Clears all the items in the receiver. The text, icon and other
+ * attributes of the items are set to their default values. If the
+ * tree was created with the <code>SWT.VIRTUAL</code> style, these
+ * attributes are requested again as needed.
+ *
+ * @param all <code>true</code> if all child items should be cleared
+ * recursively, and <code>false</code> otherwise
+ *
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @see SWT#VIRTUAL
+ * @see SWT#SetData
+ *
+ * @since 3.2
+ */
+public void clearAll (boolean recursive) {
+ checkWidget ();
+ if (items.length == 0) return;
+
+ /* if there are no columns then the horizontal scrollbar may need adjusting */
+ int oldRightX = 0;
+ if (columns.length == 0 && !recursive) {
+ for (int i = 0; i < items.length; i++) {
+ Rectangle bounds = items [i].getBounds (false);
+ oldRightX = Math.max (oldRightX, bounds.x + bounds.width);
+ }
+ }
+
+ /* clear the item(s) */
+ for (int i = 0; i < items.length; i++) {
+ items [i].clear ();
+ if (recursive) items [i].clearAll (true, false);
+ }
+
+ /* adjust the horizontal scrollbar if needed */
+ if (columns.length == 0) {
+ if (recursive) {
+ updateHorizontalBar (); /* recompute from scratch */
+ } else {
+ /*
+ * All cleared root items will have the same x and width values now,
+ * so just measure the first one as a sample.
+ */
+ Rectangle bounds = items [0].getBounds (false);
+ int newRightX = bounds.x + bounds.width;
+ updateHorizontalBar (newRightX, newRightX - oldRightX);
+ }
+ }
+
+ /* redraw the item(s) */
+ if (recursive) {
+ redrawItems (0, availableItemsCount - 1, false);
+ } else {
+ for (int i = 0; i < items.length; i++) {
+ redrawItem (items [i].availableIndex, false);
+ }
+ }
+}
+/*
+ * Returns the ORDERED index of the column that the specified x falls within,
+ * or -1 if the x lies to the right of the last column.
+ */
+int computeColumnIntersect (int x, int startColumn) {
+ TreeColumn[] orderedColumns = getOrderedColumns ();
+ if (orderedColumns.length - 1 < startColumn) return -1;
+ int rightX = orderedColumns [startColumn].getX ();
+ for (int i = startColumn; i < orderedColumns.length; i++) {
+ rightX += orderedColumns [i].width;
+ if (x < rightX) return i;
+ }
+ return -1;
+}
+public Point computeSize (int wHint, int hHint, boolean changed) {
+ checkWidget ();
+ int width = 0, height = 0;
+ if (wHint != SWT.DEFAULT) {
+ width = wHint;
+ } else {
+ if (columns.length == 0) {
+ for (int i = 0; i < availableItemsCount; i++) {
+ Rectangle itemBounds = availableItems [i].getBounds (false);
+ width = Math.max (width, itemBounds.x + itemBounds.width);
+ }
+ } else {
+ TreeColumn[] orderedColumns = getOrderedColumns ();
+ TreeColumn lastColumn = orderedColumns [orderedColumns.length - 1];
+ width = lastColumn.getX () + lastColumn.width;
+ }
+ }
+ if (hHint != SWT.DEFAULT) {
+ height = hHint;
+ } else {
+ height = getHeaderHeight () + availableItemsCount * itemHeight;
+ }
+ Rectangle result = computeTrim (0, 0, width, height);
+ return new Point (result.width, result.height);
+}
+void createItem (TreeColumn column, int index) {
+ TreeColumn[] newColumns = new TreeColumn [columns.length + 1];
+ System.arraycopy (columns, 0, newColumns, 0, index);
+ newColumns [index] = column;
+ System.arraycopy (columns, index, newColumns, index + 1, columns.length - index);
+ columns = newColumns;
+
+ if (orderedColumns != null) {
+ int insertIndex = 0;
+ if (index > 0) {
+ insertIndex = columns [index - 1].getOrderIndex () + 1;
+ }
+ TreeColumn[] newOrderedColumns = new TreeColumn [orderedColumns.length + 1];
+ System.arraycopy (orderedColumns, 0, newOrderedColumns, 0, insertIndex);
+ newOrderedColumns [insertIndex] = column;
+ System.arraycopy (
+ orderedColumns,
+ insertIndex,
+ newOrderedColumns,
+ insertIndex + 1,
+ orderedColumns.length - insertIndex);
+ orderedColumns = newOrderedColumns;
+ }
+
+ if (columns.length == 1) {
+ column.itemImageWidth = orderedCol0imageWidth;
+ } else {
+ if (column.getOrderIndex () == 0) orderedCol0imageWidth = 0;
+ }
+
+ /* allow all items to update their internal structures accordingly */
+ for (int i = 0; i < items.length; i++) {
+ items [i].addColumn (column);
+ }
+
+ /* existing items become hidden when going from 0 to 1 column (0 width) */
+ if (columns.length == 1 && availableItemsCount > 0) {
+ redrawFromItemDownwards (topIndex);
+ } else {
+ /* checkboxes become hidden when creating a column with index == ordered index == 0 (0 width) */
+ if (availableItemsCount > 0 && (style & SWT.CHECK) != 0 && index == 0 && column.getOrderIndex () == 0) {
+ redrawFromItemDownwards (topIndex);
+ }
+ }
+}
+void createItem (TreeItem item, int index) {
+ TreeItem[] newItems = new TreeItem [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;
+
+ /* determine the item's availability index */
+ int startIndex;
+ if (index == items.length - 1) {
+ startIndex = availableItemsCount; /* last item */
+ } else {
+ startIndex = items [index + 1].availableIndex;
+ }
+
+ if (availableItemsCount == availableItems.length) {
+ int grow = drawCount <= 0 ? 4 : Math.max (4, availableItems.length * 3 / 2);
+ TreeItem[] newAvailableItems = new TreeItem [availableItems.length + grow];
+ System.arraycopy (availableItems, 0, newAvailableItems, 0, availableItems.length);
+ availableItems = newAvailableItems;
+ }
+ if (startIndex != availableItemsCount) {
+ /* new item is not at end of list, so shift other items right to create space for it */
+ System.arraycopy (
+ availableItems,
+ startIndex,
+ availableItems,
+ startIndex + 1,
+ availableItemsCount - startIndex);
+ }
+ availableItems [startIndex] = item;
+ availableItemsCount++;
+
+ /* update the availableIndex for items bumped down by this new item */
+ for (int i = startIndex; i < availableItemsCount; i++) {
+ availableItems [i].availableIndex = i;
+ }
+
+ /* update scrollbars */
+ updateVerticalBar ();
+ Rectangle bounds = item.getBounds (false);
+ int rightX = bounds.x + bounds.width;
+ updateHorizontalBar (rightX, rightX);
+ /*
+ * If new item is above viewport then adjust topIndex and the vertical
+ * scrollbar so that the current viewport items will not change.
+ */
+ if (item.availableIndex < topIndex) {
+ topIndex++;
+ ScrollBar vBar = getVerticalBar ();
+ if (vBar != null) vBar.setSelection (topIndex);
+ return;
+ }
+ /*
+ * If this is the first item and the receiver has focus then its boundary
+ * focus ring must be removed.
+ */
+ if (availableItemsCount == 1 && isFocusControl ()) {
+ focusItem = item;
+ redraw ();
+ return;
+ }
+ int redrawIndex = index;
+ if (redrawIndex > 0 && item.isLastChild ()) redrawIndex--;
+ redrawFromItemDownwards (items [redrawIndex].availableIndex);
+}
+/**
+ * Deselects an item in the receiver. If the item was already
+ * deselected, it remains deselected.
+ *
+ * @param item the item to be deselected
+ *
+ * @exception IllegalArgumentException <ul>
+ * <li>ERROR_NULL_ARGUMENT - if the item is null</li>
+ * <li>ERROR_INVALID_ARGUMENT - if the item has been disposed</li>
+ * </ul>
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @since 3.4
+ */
+public void deselect (TreeItem item) {
+ checkWidget ();
+ if (item == null) error (SWT.ERROR_NULL_ARGUMENT);
+ if (item.isDisposed ()) error (SWT.ERROR_INVALID_ARGUMENT);
+ deselectItem (item);
+ redrawItem (item.availableIndex, true);
+}
+/**
+ * Deselects all selected items in the receiver.
+ *
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public void deselectAll () {
+ checkWidget ();
+ TreeItem[] oldSelection = selectedItems;
+ selectedItems = NO_ITEMS;
+ for (int i = 0; i < oldSelection.length; i++) {
+ redrawItem (oldSelection [i].availableIndex, true);
+ }
+}
+void deselectItem (TreeItem item) {
+ int index = getSelectionIndex (item);
+ if (index == -1) return;
+ TreeItem[] newSelectedItems = new TreeItem [selectedItems.length - 1];
+ System.arraycopy (selectedItems, 0, newSelectedItems, 0, index);
+ System.arraycopy (
+ selectedItems,
+ index + 1,
+ newSelectedItems,
+ index,
+ newSelectedItems.length - index);
+ selectedItems = newSelectedItems;
+}
+void destroyItem (TreeColumn column) {
+ headerHideToolTip ();
+ int index = column.getIndex ();
+ int orderedIndex = column.getOrderIndex ();
+
+ TreeColumn[] newColumns = new TreeColumn [columns.length - 1];
+ System.arraycopy (columns, 0, newColumns, 0, index);
+ System.arraycopy (columns, index + 1, newColumns, index, newColumns.length - index);
+ columns = newColumns;
+
+ if (orderedColumns != null) {
+ if (columns.length < 2) {
+ orderedColumns = null;
+ } else {
+ int removeIndex = column.getOrderIndex ();
+ TreeColumn[] newOrderedColumns = new TreeColumn [orderedColumns.length - 1];
+ System.arraycopy (orderedColumns, 0, newOrderedColumns, 0, removeIndex);
+ System.arraycopy (
+ orderedColumns,
+ removeIndex + 1,
+ newOrderedColumns,
+ removeIndex,
+ newOrderedColumns.length - removeIndex);
+ orderedColumns = newOrderedColumns;
+ }
+ }
+
+ if (orderedIndex == 0 && columns.length > 0) {
+ orderedCol0imageWidth = columns [getColumnOrder ()[0]].itemImageWidth;
+ }
+
+ /* allow all items to update their internal structures accordingly */
+ for (int i = 0; i < items.length; i++) {
+ items [i].removeColumn (column, index, orderedIndex);
+ }
+
+ /* update horizontal scrollbar */
+ int lastColumnIndex = columns.length - 1;
+ if (lastColumnIndex < 0) { /* no more columns */
+ updateHorizontalBar ();
+ } else {
+ int newWidth = 0;
+ for (int i = 0; i < columns.length; i++) {
+ newWidth += columns [i].width;
+ }
+ ScrollBar hBar = getHorizontalBar ();
+ if (hBar != null) {
+ hBar.setMaximum (newWidth);
+ hBar.setVisible (clientArea.width < newWidth);
+ int selection = hBar.getSelection ();
+ if (selection != horizontalOffset) {
+ horizontalOffset = selection;
+ redraw ();
+ if (header.isVisible () && drawCount <= 0) header.redraw ();
+ }
+ }
+ }
+ TreeColumn[] orderedColumns = getOrderedColumns ();
+ for (int i = orderedIndex; i < orderedColumns.length; i++) {
+ if (!orderedColumns [i].isDisposed ()) {
+ orderedColumns [i].sendEvent (SWT.Move);
+ }
+ }
+
+ if (sortColumn == column) {
+ sortColumn = null;
+ }
+}
+/*
+ * Allows the Tree to update internal structures it has that may contain the
+ * item being destroyed. The argument is not necessarily a root-level item.
+ */
+void destroyItem (TreeItem item) {
+ if (item == focusItem) reassignFocus ();
+
+ /* availableItems array */
+ int availableIndex = item.availableIndex;
+ if (availableIndex != -1) {
+ Rectangle bounds = item.getBounds (false);
+ int rightX = bounds.x + bounds.width;
+
+ if (availableIndex != availableItemsCount - 1) {
+ /* item is not at end of available items list, so must shift items left to reclaim its slot */
+ System.arraycopy (
+ availableItems,
+ availableIndex + 1,
+ availableItems,
+ availableIndex,
+ availableItemsCount - availableIndex - 1);
+ availableItems [availableItemsCount - 1] = null;
+ } else {
+ availableItems [availableIndex] = null; /* last item, so no array copy needed */
+ }
+ availableItemsCount--;
+
+ if (drawCount <= 0 && availableItems.length - availableItemsCount == 4) {
+ /* shrink the available items array */
+ TreeItem[] newAvailableItems = new TreeItem [availableItemsCount];
+ System.arraycopy (availableItems, 0, newAvailableItems, 0, newAvailableItems.length);
+ availableItems = newAvailableItems;
+ }
+
+ /* update the availableIndex on affected items */
+ for (int i = availableIndex; i < availableItemsCount; i++) {
+ availableItems [i].availableIndex = i;
+ }
+ item.availableIndex = -1;
+ int oldTopIndex = topIndex;
+ updateVerticalBar ();
+ updateHorizontalBar (0, -rightX);
+ /*
+ * If destroyed item is above viewport then adjust topIndex and the vertical
+ * scrollbar so that the current viewport items will not change.
+ */
+ if (availableIndex < topIndex) {
+ topIndex = oldTopIndex - 1;
+ ScrollBar vBar = getVerticalBar ();
+ if (vBar != null) vBar.setSelection (topIndex);
+ }
+ }
+ /* selectedItems array */
+ if (item.isSelected ()) {
+ int selectionIndex = getSelectionIndex (item);
+ TreeItem[] newSelectedItems = new TreeItem [selectedItems.length - 1];
+ System.arraycopy (selectedItems, 0, newSelectedItems, 0, selectionIndex);
+ System.arraycopy (
+ selectedItems,
+ selectionIndex + 1,
+ newSelectedItems,
+ selectionIndex,
+ newSelectedItems.length - selectionIndex);
+ selectedItems = newSelectedItems;
+ }
+ /* root-level items array */
+ if (item.depth == 0) {
+ int index = item.getIndex ();
+ TreeItem[] newItems = new TreeItem [items.length - 1];
+ System.arraycopy (items, 0, newItems, 0, index);
+ System.arraycopy (items, index + 1, newItems, index, newItems.length - index);
+ items = newItems;
+ }
+ if (item == anchorItem) anchorItem = null;
+ if (item == insertMarkItem) insertMarkItem = null;
+ if (item == lastClickedItem) lastClickedItem = null;
+ /*
+ * If this was the last item and the receiver has focus then its boundary
+ * focus ring must be redrawn.
+ */
+ if (availableItemsCount == 0 && isFocusControl ()) {
+ redraw ();
+ return;
+ }
+}
+Image getArrowDownImage () {
+ return (Image) display.getData (ID_ARROWDOWN);
+}
+Image getArrowUpImage () {
+ return (Image) display.getData (ID_ARROWUP);
+}
+int getCellPadding () {
+ return MARGIN_CELL + WIDTH_CELL_HIGHLIGHT;
+}
+Image getCheckmarkImage () {
+ return (Image) display.getData (ID_CHECKMARK);
+}
+public Control[] getChildren () {
+ checkWidget ();
+ Control[] controls = _getChildren ();
+ if (header == null) return controls;
+ Control[] result = new Control [controls.length - 1];
+ /* remove the Header from the returned set of children */
+ int index = 0;
+ for (int i = 0; i < controls.length; i++) {
+ if (controls [i] != header) {
+ result [index++] = controls [i];
+ }
+ }
+ return result;
+}
+Image getCollapsedImage () {
+ return (Image) display.getData (ID_COLLAPSED);
+}
+/**
+ * Returns the column at the given, zero-relative index in the
+ * receiver. Throws an exception if the index is out of range.
+ * Columns are returned in the order that they were created.
+ * If no <code>TreeColumn</code>s were created by the programmer,
+ * this method will throw <code>ERROR_INVALID_RANGE</code> despite
+ * the fact that a single column of data may be visible in the tree.
+ * This occurs when the programmer uses the tree like a list, adding
+ * items but never creating a column.
+ *
+ * @param index the index of the column to return
+ * @return the column at the given index
+ *
+ * @exception IllegalArgumentException <ul>
+ * <li>ERROR_INVALID_RANGE - if the index is not between 0 and the number of elements in the list minus 1 (inclusive)</li>
+ * </ul>
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @see Tree#getColumnOrder()
+ * @see Tree#setColumnOrder(int[])
+ * @see TreeColumn#getMoveable()
+ * @see TreeColumn#setMoveable(boolean)
+ * @see SWT#Move
+ *
+ * @since 3.1
+ */
+public TreeColumn getColumn (int index) {
+ checkWidget ();
+ if (!(0 <= index && index < columns.length)) error (SWT.ERROR_INVALID_RANGE);
+ return columns [index];
+}
+/**
+ * Returns the number of columns contained in the receiver.
+ * If no <code>TreeColumn</code>s were created by the programmer,
+ * this value is zero, despite the fact that visually, one column
+ * of items may be visible. This occurs when the programmer uses
+ * the tree like a list, adding items but never creating a column.
+ *
+ * @return the number of columns
+ *
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @since 3.1
+ */
+public int getColumnCount () {
+ checkWidget ();
+ return columns.length;
+}
+/**
+ * Returns an array of zero-relative integers that map
+ * the creation order of the receiver's items to the
+ * order in which they are currently being displayed.
+ * <p>
+ * Specifically, the indices of the returned array represent
+ * the current visual order of the items, and the contents
+ * of the array represent the creation order of the items.
+ * </p><p>
+ * Note: This is not the actual structure used by the receiver
+ * to maintain its list of items, so modifying the array will
+ * not affect the receiver.
+ * </p>
+ *
+ * @return the current visual order of the receiver's items
+ *
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @see Tree#setColumnOrder(int[])
+ * @see TreeColumn#getMoveable()
+ * @see TreeColumn#setMoveable(boolean)
+ * @see SWT#Move
+ *
+ * @since 3.2
+ */
+public int[] getColumnOrder () {
+ checkWidget ();
+ int[] result = new int [columns.length];
+ if (orderedColumns != null) {
+ for (int i = 0; i < result.length; i++) {
+ result [i] = orderedColumns [i].getIndex ();
+ }
+ } else {
+ for (int i = 0; i < columns.length; i++) {
+ result [i] = i;
+ }
+ }
+ return result;
+}
+/**
+ * Returns an array of <code>TreeColumn</code>s which are the
+ * columns in the receiver. Columns are returned in the order
+ * that they were created. If no <code>TreeColumn</code>s were
+ * created by the programmer, the array is empty, despite the fact
+ * that visually, one column of items may be visible. This occurs
+ * when the programmer uses the tree like a list, adding items but
+ * never creating a column.
+ * <p>
+ * Note: This is not the actual structure used by the receiver
+ * to maintain its list of items, so modifying the array will
+ * not affect the receiver.
+ * </p>
+ *
+ * @return the items in the receiver
+ *
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @see Tree#getColumnOrder()
+ * @see Tree#setColumnOrder(int[])
+ * @see TreeColumn#getMoveable()
+ * @see TreeColumn#setMoveable(boolean)
+ * @see SWT#Move
+ *
+ * @since 3.1
+ */
+public TreeColumn[] getColumns () {
+ checkWidget ();
+ TreeColumn[] result = new TreeColumn [columns.length];
+ System.arraycopy (columns, 0, result, 0, columns.length);
+ return result;
+}
+Color getConnectorColor () {
+ return (Color) display.getData (ID_CONNECTOR_COLOR);
+}
+Image getExpandedImage () {
+ return (Image) display.getData (ID_EXPANDED);
+}
+Image getGrayUncheckedImage () {
+ return (Image) display.getData (ID_GRAYUNCHECKED);
+}
+/**
+ * Returns the width in pixels of a grid line.
+ *
+ * @return the width of a grid line in pixels
+ *
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @since 3.1
+ */
+public int getGridLineWidth () {
+ checkWidget ();
+ return 1;
+}
+/**
+ * Returns the height of the receiver's header
+ *
+ * @return the height of the header or zero if the header is not visible
+ *
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @since 3.1
+ */
+public int getHeaderHeight () {
+ checkWidget ();
+ if (!header.getVisible ()) return 0;
+ return header.getSize ().y;
+}
+int getHeaderPadding () {
+ return MARGIN_CELL + WIDTH_HEADER_SHADOW;
+}
+/**
+ * Returns <code>true</code> if the receiver's header is visible,
+ * and <code>false</code> otherwise.
+ * <p>
+ * If one of the receiver's ancestors is not visible or some
+ * other condition makes the receiver not visible, this method
+ * may still indicate that it is considered visible even though
+ * it may not actually be showing.
+ * </p>
+ *
+ * @return the receiver's header's visibility state
+ *
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @since 3.1
+ */
+public boolean getHeaderVisible () {
+ checkWidget ();
+ return header.getVisible ();
+}
+/**
+ * Returns the item at the given point in the receiver
+ * or null if no such item exists. The point is in the
+ * coordinate system of the receiver.
+ * <p>
+ * The item that is returned represents an item that could be selected by the user.
+ * For example, if selection only occurs in items in the first column, then null is
+ * returned if the point is outside of the item.
+ * Note that the SWT.FULL_SELECTION style hint, which specifies the selection policy,
+ * determines the extent of the selection.
+ * </p>
+ *
+ * @param point the point used to locate the item
+ * @return the item at the given point, or null if the point is not in a selectable item
+ *
+ * @exception IllegalArgumentException <ul>
+ * <li>ERROR_NULL_ARGUMENT - if the point is null</li>
+ * </ul>
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public TreeItem getItem (Point point) {
+ checkWidget ();
+ if (point == null) error (SWT.ERROR_NULL_ARGUMENT);
+ int index = (point.y - getHeaderHeight ()) / itemHeight + topIndex;
+ if (!(0 <= index && index < availableItemsCount)) return null; /* below the last item */
+ TreeItem result = availableItems [index];
+ if (!result.getHitBounds ().contains (point)) return null; /* considers the x value */
+ return result;
+}
+/**
+ * Returns the item at the given, zero-relative index in the
+ * receiver. Throws an exception if the index is out of range.
+ *
+ * @param index the index of the item to return
+ * @return the item at the given index
+ *
+ * @exception IllegalArgumentException <ul>
+ * <li>ERROR_INVALID_RANGE - if the index is not between 0 and the number of elements in the list minus 1 (inclusive)</li>
+ * </ul>
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @since 3.1
+ */
+public TreeItem getItem (int index) {
+ checkWidget ();
+ if (!(0 <= index && index < items.length)) error (SWT.ERROR_INVALID_RANGE);
+ return items [index];
+}
+/**
+ * Returns the number of items contained in the receiver
+ * that are direct item children of the receiver. The
+ * number that is returned is the number of roots in the
+ * tree.
+ *
+ * @return the number of items
+ *
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public int getItemCount () {
+ checkWidget ();
+ return items.length;
+}
+/**
+ * Returns the height of the area which would be used to
+ * display <em>one</em> of the items in the tree.
+ *
+ * @return the height of one item
+ *
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public int getItemHeight () {
+ checkWidget ();
+ return itemHeight;
+}
+/**
+ * Returns a (possibly empty) array of items contained in the
+ * receiver that are direct item children of the receiver. These
+ * are the roots of the tree.
+ * <p>
+ * Note: This is not the actual structure used by the receiver
+ * to maintain its list of items, so modifying the array will
+ * not affect the receiver.
+ * </p>
+ *
+ * @return the items
+ *
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public TreeItem[] getItems () {
+ checkWidget ();
+ TreeItem[] result = new TreeItem [items.length];
+ System.arraycopy (items, 0, result, 0, items.length);
+ return result;
+}
+/*
+ * Returns the current y-coordinate that the specified item should have.
+ */
+int getItemY (TreeItem item) {
+ int index = item.availableIndex;
+ if (index == -1) return -1;
+ return (index - topIndex) * itemHeight + getHeaderHeight ();
+}
+/**
+ * Returns <code>true</code> if the receiver's lines are visible,
+ * and <code>false</code> otherwise. Note that some platforms draw
+ * grid lines while others may draw alternating row colors.
+ * <p>
+ * If one of the receiver's ancestors is not visible or some
+ * other condition makes the receiver not visible, this method
+ * may still indicate that it is considered visible even though
+ * it may not actually be showing.
+ * </p>
+ *
+ * @return the visibility state of the lines
+ *
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @since 3.1
+ */
+public boolean getLinesVisible () {
+ checkWidget ();
+ return linesVisible;
+}
+TreeColumn[] getOrderedColumns () {
+ if (orderedColumns != null) return orderedColumns;
+ return columns;
+}
+/**
+ * Returns the receiver's parent item, which must be a
+ * <code>TreeItem</code> or null when the receiver is a
+ * root.
+ *
+ * @return the receiver's parent item
+ *
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public TreeItem getParentItem () {
+ checkWidget ();
+ return null;
+}
+/**
+ * Returns an array of <code>TreeItem</code>s that are currently
+ * selected in the receiver. The order of the items is unspecified.
+ * An empty array indicates that no items are selected.
+ * <p>
+ * Note: This is not the actual structure used by the receiver
+ * to maintain its selection, so modifying the array will
+ * not affect the receiver.
+ * </p>
+ * @return an array representing the selection
+ *
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public TreeItem[] getSelection () {
+ checkWidget ();
+ int count = selectedItems.length;
+ TreeItem[] result = new TreeItem [count];
+ if (count > 0) {
+ if (count == 1) {
+ System.arraycopy (selectedItems, 0, result, 0, count);
+ } else {
+ getSelection (result, items, 0);
+ }
+ }
+ return result;
+}
+int getSelection (TreeItem[] result, TreeItem[] items, int index) {
+ for (int i = 0; i < items.length; i++) {
+ TreeItem item = items [i];
+ if (item.isSelected ()) result [index++] = item;
+ if (index == result.length) break;
+ index = getSelection (result, items [i].items, index);
+ if (index == result.length) break;
+ }
+ return index;
+}
+/**
+ * Returns the number of selected items contained in the receiver.
+ *
+ * @return the number of selected items
+ *
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public int getSelectionCount () {
+ checkWidget ();
+ return selectedItems.length;
+}
+/*
+ * Returns the index of the argument in the receiver's array of currently-
+ * selected items, or -1 if the item is not currently selected.
+ */
+int getSelectionIndex (TreeItem item) {
+ for (int i = 0; i < selectedItems.length; i++) {
+ if (selectedItems [i] == item) return i;
+ }
+ return -1;
+}
+/**
+ * Returns the column which shows the sort indicator for
+ * the receiver. The value may be null if no column shows
+ * the sort indicator.
+ *
+ * @return the sort indicator
+ *
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @see #setSortColumn(TreeColumn)
+ *
+ * @since 3.2
+ */
+public TreeColumn getSortColumn () {
+ checkWidget ();
+ return sortColumn;
+}
+/**
+ * Returns the direction of the sort indicator for the receiver.
+ * The value will be one of <code>UP</code>, <code>DOWN</code>
+ * or <code>NONE</code>.
+ *
+ * @return the sort direction
+ *
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @see #setSortDirection(int)
+ *
+ * @since 3.2
+ */
+public int getSortDirection () {
+ checkWidget ();
+ return sortDirection;
+}
+/**
+ * Returns the item which is currently at the top of the receiver.
+ * This item can change when items are expanded, collapsed, scrolled
+ * or new items are added or removed.
+ *
+ * @return the item at the top of the receiver
+ *
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @since 2.1
+ */
+public TreeItem getTopItem () {
+ checkWidget ();
+ if (availableItemsCount == 0) return null;
+ return availableItems [topIndex];
+}
+Image getUncheckedImage () {
+ return (Image) display.getData (ID_UNCHECKED);
+}
+void handleEvents (Event event) {
+ switch (event.type) {
+ case SWT.Paint:
+ if (event.widget == header) {
+ headerOnPaint (event);
+ } else {
+ onPaint (event);
+ }
+ break;
+ case SWT.MenuDetect: {
+ notifyListeners (SWT.MenuDetect, event);
+ break;
+ }
+ case SWT.MouseDown:
+ if (event.widget == header) {
+ headerOnMouseDown (event);
+ } else {
+ onMouseDown (event);
+ }
+ break;
+ case SWT.MouseUp:
+ if (event.widget == header) {
+ headerOnMouseUp (event);
+ } else {
+ onMouseUp (event);
+ }
+ break;
+ case SWT.MouseHover:
+ headerOnMouseHover (event); break;
+ case SWT.MouseMove:
+ headerOnMouseMove (event); break;
+ case SWT.MouseDoubleClick:
+ if (event.widget == header) {
+ headerOnMouseDoubleClick (event);
+ } else {
+ onMouseDoubleClick (event);
+ }
+ break;
+ case SWT.MouseExit:
+ headerOnMouseExit (); break;
+ case SWT.Dispose:
+ onDispose (event); break;
+ case SWT.KeyDown:
+ onKeyDown (event); break;
+ case SWT.Resize:
+ onResize (event); break;
+ case SWT.Selection:
+ if (event.widget == getHorizontalBar ()) {
+ onScrollHorizontal (event);
+ }
+ if (event.widget == getVerticalBar ()) {
+ onScrollVertical (event);
+ }
+ break;
+ case SWT.FocusOut:
+ onFocusOut (); break;
+ case SWT.FocusIn:
+ onFocusIn (); break;
+ case SWT.Traverse:
+ switch (event.detail) {
+ case SWT.TRAVERSE_ESCAPE:
+ case SWT.TRAVERSE_RETURN:
+ case SWT.TRAVERSE_TAB_NEXT:
+ case SWT.TRAVERSE_TAB_PREVIOUS:
+ case SWT.TRAVERSE_PAGE_NEXT:
+ case SWT.TRAVERSE_PAGE_PREVIOUS:
+ event.doit = true;
+ break;
+ }
+ break;
+ }
+}
+String headerGetToolTip (int x) {
+ if (resizeColumn != null) return null;
+ int orderedIndex = computeColumnIntersect (x, 0);
+ if (orderedIndex == -1) return null;
+ TreeColumn[] orderedColumns = getOrderedColumns ();
+ TreeColumn column = orderedColumns [orderedIndex];
+ if (column.toolTipText == null) return null;
+
+ /* no tooltip should appear if the hover is at a column resize opportunity */
+ int columnX = column.getX ();
+ if (orderedIndex > 0 && orderedColumns [orderedIndex - 1].resizable) {
+ /* left column bound is resizable */
+ if (x - columnX <= TOLLERANCE_COLUMNRESIZE) return null;
+ }
+ if (column.resizable) {
+ /* right column bound is resizable */
+ int columnRightX = columnX + column.width;
+ if (columnRightX - x <= TOLLERANCE_COLUMNRESIZE) return null;
+ }
+ return removeMnemonics (column.toolTipText);
+}
+void headerHideToolTip() {
+ if (toolTipShell == null) return;
+ for (int i = 0; i < toolTipEvents.length; i++) {
+ header.removeListener (toolTipEvents [i], toolTipListener);
+ }
+ toolTipShell.dispose ();
+ toolTipShell = null;
+ toolTipLabel = null;
+}
+void headerOnMouseDoubleClick (Event event) {
+ if (!isFocusControl ()) setFocus ();
+ if (columns.length == 0) return;
+ TreeColumn[] orderedColumns = getOrderedColumns ();
+ int x = -horizontalOffset;
+ for (int i = 0; i < orderedColumns.length; i++) {
+ TreeColumn column = orderedColumns [i];
+ x += column.width;
+ if (event.x < x) {
+ /* found the clicked column */
+ TreeColumn packColumn = null;
+ if (x - event.x <= TOLLERANCE_COLUMNRESIZE) {
+ /* clicked on column bound for this column */
+ packColumn = column;
+ } else {
+ if (i > 0 && event.x - column.getX () <= TOLLERANCE_COLUMNRESIZE) {
+ /* clicked on column bound that applies to previous column */
+ packColumn = orderedColumns [i - 1];
+ }
+ }
+ if (packColumn != null) {
+ packColumn.pack ();
+ resizeColumn = null;
+ if (Math.abs (packColumn.getX () + packColumn.width - event.x) > TOLLERANCE_COLUMNRESIZE) {
+ /* column separator has relocated away from pointer location */
+ setCursor (null);
+ }
+ return;
+ }
+ /* did not click on column separator, so just fire column event */
+ Event newEvent = new Event ();
+ newEvent.widget = column;
+ column.postEvent (SWT.DefaultSelection, newEvent);
+ return;
+ }
+ }
+}
+void headerOnMouseDown (Event event) {
+ if (event.button != 1) return;
+ TreeColumn[] orderedColumns = getOrderedColumns ();
+ int x = -horizontalOffset;
+ for (int i = 0; i < orderedColumns.length; i++) {
+ TreeColumn column = orderedColumns [i];
+ x += column.width;
+ /* if close to a resizable column separator line then begin column resize */
+ if (column.resizable && Math.abs (x - event.x) <= TOLLERANCE_COLUMNRESIZE) {
+ resizeColumn = column;
+ resizeColumnX = x;
+ return;
+ }
+ /*
+ * If within column but not near separator line then start column drag
+ * if column is moveable, or just fire column Selection otherwise.
+ */
+ if (event.x < x) {
+ if (column.moveable) {
+ /* open tracker on the dragged column's header cell */
+ int columnX = column.getX ();
+ int pointerOffset = event.x - columnX;
+ headerHideToolTip ();
+ Tracker tracker = new Tracker (this, SWT.NONE);
+ tracker.setRectangles (new Rectangle[] {
+ new Rectangle (columnX, 0, column.width, getHeaderHeight ())
+ });
+ if (!tracker.open ()) return; /* cancelled */
+ /* determine which column was dragged onto */
+ Rectangle result = tracker.getRectangles () [0];
+ int pointerX = result.x + pointerOffset;
+ if (pointerX < 0) return; /* dragged too far left */
+ x = -horizontalOffset;
+ for (int destIndex = 0; destIndex < orderedColumns.length; destIndex++) {
+ TreeColumn destColumn = orderedColumns [destIndex];
+ x += destColumn.width;
+ if (pointerX < x) {
+ int oldIndex = column.getOrderIndex ();
+ if (destIndex == oldIndex) { /* dragged onto self */
+ Event newEvent = new Event ();
+ newEvent.widget = column;
+ column.postEvent (SWT.Selection, newEvent);
+ return;
+ }
+ int leftmostIndex = Math.min (destIndex, oldIndex);
+ int[] oldOrder = getColumnOrder ();
+ int[] newOrder = new int [oldOrder.length];
+ System.arraycopy (oldOrder, 0, newOrder, 0, leftmostIndex);
+ if (leftmostIndex == oldIndex) {
+ /* column moving to the right */
+ System.arraycopy (oldOrder, oldIndex + 1, newOrder, oldIndex, destIndex - oldIndex);
+ } else {
+ /* column moving to the left */
+ System.arraycopy (oldOrder, destIndex, newOrder, destIndex + 1, oldIndex - destIndex);
+ }
+ newOrder [destIndex] = oldOrder [oldIndex];
+ int rightmostIndex = Math.max (destIndex, oldIndex);
+ System.arraycopy (
+ oldOrder,
+ rightmostIndex + 1,
+ newOrder,
+ rightmostIndex + 1,
+ newOrder.length - rightmostIndex - 1);
+ setColumnOrder (newOrder);
+ return;
+ }
+ }
+ return; /* dragged too far right */
+ }
+ /* column is not moveable */
+ Event newEvent = new Event ();
+ newEvent.widget = column;
+ column.postEvent (SWT.Selection, newEvent);
+ return;
+ }
+ }
+}
+void headerOnMouseExit () {
+ if (resizeColumn != null) return;
+ setCursor (null); /* ensure that a column resize cursor does not escape */
+}
+void headerOnMouseHover (Event event) {
+ headerShowToolTip (event.x);
+}
+void headerOnMouseMove (Event event) {
+ if (resizeColumn == null) {
+ /* not currently resizing a column */
+ for (int i = 0; i < columns.length; i++) {
+ TreeColumn column = columns [i];
+ int x = column.getX () + column.width;
+ if (Math.abs (x - event.x) <= TOLLERANCE_COLUMNRESIZE) {
+ if (column.resizable) {
+ setCursor (display.getSystemCursor (SWT.CURSOR_SIZEWE));
+ } else {
+ setCursor (null);
+ }
+ return;
+ }
+ }
+ setCursor (null);
+ return;
+ }
+
+ /* currently resizing a column */
+
+ /* don't allow the resize x to move left of the column's x position */
+ if (event.x <= resizeColumn.getX ()) return;
+
+ /* redraw the resizing line at its new location */
+ GC gc = new GC (this);
+ gc.setForeground (display.getSystemColor (SWT.COLOR_BLACK));
+ int lineHeight = clientArea.height;
+ redraw (resizeColumnX - 1, 0, 1, lineHeight, false);
+ resizeColumnX = event.x;
+ gc.drawLine (resizeColumnX - 1, 0, resizeColumnX - 1, lineHeight);
+ gc.dispose ();
+}
+void headerOnMouseUp (Event event) {
+ if (resizeColumn == null) return; /* not resizing a column */
+
+ /* remove the resize line */
+ GC gc = new GC (this);
+ redraw (resizeColumnX - 1, 0, 1, clientArea.height, false);
+ gc.dispose ();
+
+ int newWidth = resizeColumnX - resizeColumn.getX ();
+ if (newWidth != resizeColumn.width) {
+ setCursor (null);
+ updateColumnWidth (resizeColumn, newWidth);
+ }
+ resizeColumnX = -1;
+ resizeColumn = null;
+}
+void headerOnPaint (Event event) {
+ TreeColumn[] orderedColumns = getOrderedColumns ();
+ int numColumns = orderedColumns.length;
+ GC gc = event.gc;
+ Rectangle clipping = gc.getClipping ();
+ int startColumn = -1, endColumn = -1;
+ if (numColumns > 0) {
+ startColumn = computeColumnIntersect (clipping.x, 0);
+ if (startColumn != -1) { /* the clip x is within a column's bounds */
+ endColumn = computeColumnIntersect (clipping.x + clipping.width, startColumn);
+ if (endColumn == -1) endColumn = numColumns - 1;
+ }
+ } else {
+ startColumn = endColumn = 0;
+ }
+
+ /* paint the column header shadow that spans the full header width */
+ Point headerSize = header.getSize ();
+ headerPaintHShadows (gc, 0, 0, headerSize.x, headerSize.y);
+
+ /* if all damage is to the right of the last column then finished */
+ if (startColumn == -1) return;
+
+ /* paint each of the column headers */
+ if (numColumns == 0) return; /* no headers to paint */
+ for (int i = startColumn; i <= endColumn; i++) {
+ headerPaintVShadows (gc, orderedColumns [i].getX (), 0, orderedColumns [i].width, headerSize.y);
+ orderedColumns [i].paint (gc);
+ }
+}
+void headerPaintHShadows (GC gc, int x, int y, int width, int height) {
+ gc.setClipping (x, y, width, height);
+ int endX = x + width;
+ gc.setForeground (display.getSystemColor (SWT.COLOR_WIDGET_HIGHLIGHT_SHADOW));
+ gc.drawLine (x, y, endX, y); /* highlight shadow */
+ gc.setForeground (display.getSystemColor (SWT.COLOR_WIDGET_NORMAL_SHADOW));
+ gc.drawLine (x, height - 2, endX, height - 2); /* lowlight shadow */
+ gc.setForeground (display.getSystemColor (SWT.COLOR_WIDGET_DARK_SHADOW));
+ gc.drawLine (x, height - 1, endX, height - 1); /* outer shadow */
+}
+void headerPaintVShadows (GC gc, int x, int y, int width, int height) {
+ gc.setClipping (x, y, width, height);
+ int endX = x + width;
+ gc.setForeground (display.getSystemColor (SWT.COLOR_WIDGET_HIGHLIGHT_SHADOW));
+ gc.drawLine (x, y, x, y + height - 1); /* highlight shadow */
+ gc.setForeground (display.getSystemColor (SWT.COLOR_WIDGET_NORMAL_SHADOW));
+ gc.drawLine (endX - 2, y + 1, endX - 2, height - 2); /* light inner shadow */
+ gc.setForeground (display.getSystemColor (SWT.COLOR_WIDGET_DARK_SHADOW));
+ gc.drawLine (endX - 1, y, endX - 1, height - 1); /* dark outer shadow */
+}
+void headerShowToolTip (int x) {
+ String tooltip = headerGetToolTip (x);
+ if (tooltip == null || tooltip.length () == 0) return;
+
+ if (toolTipShell == null) {
+ toolTipShell = new Shell (getShell (), SWT.ON_TOP | SWT.TOOL);
+ toolTipLabel = new Label (toolTipShell, SWT.CENTER);
+ Display display = toolTipShell.getDisplay ();
+ toolTipLabel.setForeground (display.getSystemColor (SWT.COLOR_INFO_FOREGROUND));
+ toolTipLabel.setBackground (display.getSystemColor (SWT.COLOR_INFO_BACKGROUND));
+ for (int i = 0; i < toolTipEvents.length; i++) {
+ header.addListener (toolTipEvents [i], toolTipListener);
+ }
+ }
+ if (headerUpdateToolTip (x)) {
+ toolTipShell.setVisible (true);
+ } else {
+ headerHideToolTip ();
+ }
+}
+boolean headerUpdateToolTip (int x) {
+ String tooltip = headerGetToolTip (x);
+ if (tooltip == null || tooltip.length () == 0) return false;
+ if (tooltip.equals (toolTipLabel.getText ())) return true;
+
+ toolTipLabel.setText (tooltip);
+ TreeColumn column = getOrderedColumns () [computeColumnIntersect (x, 0)];
+ toolTipShell.setData (new Integer (column.getIndex ()));
+ Point labelSize = toolTipLabel.computeSize (SWT.DEFAULT, SWT.DEFAULT, true);
+ labelSize.x += 2; labelSize.y += 2;
+ toolTipLabel.setSize (labelSize);
+ toolTipShell.pack ();
+ /*
+ * On some platforms, there is a minimum size for a shell
+ * which may be greater than the label size.
+ * To avoid having the background of the tip shell showing
+ * around the label, force the label to fill the entire client area.
+ */
+ Rectangle area = toolTipShell.getClientArea ();
+ toolTipLabel.setSize (area.width, area.height);
+
+ /* Position the tooltip and ensure it's not located off the screen */
+ Point cursorLocation = getDisplay ().getCursorLocation ();
+ int cursorHeight = 21; /* assuming cursor is 21x21 */
+ Point size = toolTipShell.getSize ();
+ Rectangle rect = getMonitor ().getBounds ();
+ Point pt = new Point (cursorLocation.x, cursorLocation.y + cursorHeight + 2);
+ pt.x = Math.max (pt.x, rect.x);
+ if (pt.x + size.x > rect.x + rect.width) pt.x = rect.x + rect.width - size.x;
+ if (pt.y + size.y > rect.y + rect.height) pt.y = cursorLocation.y - 2 - size.y;
+ toolTipShell.setLocation (pt);
+ return true;
+}
+/**
+ * Searches the receiver's list starting at the first column
+ * (index 0) until a column is found that is equal to the
+ * argument, and returns the index of that column. If no column
+ * is found, returns -1.
+ *
+ * @param column the search column
+ * @return the index of the column
+ *
+ * @exception IllegalArgumentException <ul>
+ * <li>ERROR_NULL_ARGUMENT - if the column is null</li>
+ * </ul>
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @since 3.1
+ */
+public int indexOf (TreeColumn column) {
+ checkWidget ();
+ if (column == null) error (SWT.ERROR_NULL_ARGUMENT);
+ if (column.isDisposed ()) error (SWT.ERROR_INVALID_ARGUMENT);
+ return column.getIndex ();
+}
+/**
+ * Searches the receiver's list starting at the first item
+ * (index 0) until an item is found that is equal to the
+ * argument, and returns the index of that item. If no item
+ * is found, returns -1.
+ *
+ * @param item the search item
+ * @return the index of the item
+ *
+ * @exception IllegalArgumentException <ul>
+ * <li>ERROR_NULL_ARGUMENT - if the item is null</li>
+ * <li>ERROR_INVALID_ARGUMENT - if the item has been disposed</li>
+ * </ul>
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @since 3.1
+ */
+public int indexOf (TreeItem item) {
+ checkWidget ();
+ if (item == null) error (SWT.ERROR_NULL_ARGUMENT);
+ if (item.isDisposed ()) error (SWT.ERROR_INVALID_ARGUMENT);
+ if (item.parentItem != null || item.parent != this) return -1;
+ return item.getIndex ();
+}
+static void initImages (final Display display) {
+ PaletteData fourBit = new PaletteData (new RGB[] {
+ new RGB (0, 0, 0), new RGB (128, 0, 0), new RGB (0, 128, 0), new RGB (128, 128, 0),
+ new RGB (0, 0, 128), new RGB (128, 0, 128), new RGB (0, 128, 128), new RGB (128, 128, 128),
+ new RGB (192, 192, 192), new RGB (255, 0, 0), new RGB (0, 255, 0), new RGB (255, 255, 0),
+ new RGB (0, 0, 255), new RGB (255, 0, 255), new RGB (0, 255, 255), new RGB (255, 255, 255)});
+
+ if (display.getData (ID_EXPANDED) == null) {
+ ImageData expanded = new ImageData (
+ 9, 9, 4, /* width, height, depth */
+ fourBit, 4,
+ new byte[] {
+ 119, 119, 119, 119, 112, 0, 0, 0,
+ 127, -1, -1, -1, 112, 0, 0, 0,
+ 127, -1, -1, -1, 112, 0, 0, 0,
+ 127, -1, -1, -1, 112, 0, 0, 0,
+ 127, 0, 0, 15, 112, 0, 0, 0,
+ 127, -1, -1, -1, 112, 0, 0, 0,
+ 127, -1, -1, -1, 112, 0, 0, 0,
+ 127, -1, -1, -1, 112, 0, 0, 0,
+ 119, 119, 119, 119, 112, 0, 0, 0});
+ expanded.transparentPixel = 15; /* use white for transparency */
+ display.setData (ID_EXPANDED, new Image (display, expanded));
+ }
+
+ if (display.getData (ID_COLLAPSED) == null) {
+ ImageData collapsed = new ImageData (
+ 9, 9, 4, /* width, height, depth */
+ fourBit, 4,
+ new byte[] {
+ 119, 119, 119, 119, 112, 0, 0, 0,
+ 127, -1, -1, -1, 112, 0, 0, 0,
+ 127, -1, 15, -1, 112, 0, 0, 0,
+ 127, -1, 15, -1, 112, 0, 0, 0,
+ 127, 0, 0, 15, 112, 0, 0, 0,
+ 127, -1, 15, -1, 112, 0, 0, 0,
+ 127, -1, 15, -1, 112, 0, 0, 0,
+ 127, -1, -1, -1, 112, 0, 0, 0,
+ 119, 119, 119, 119, 112, 0, 0, 0});
+ collapsed.transparentPixel = 15; /* use white for transparency */
+ display.setData (ID_COLLAPSED, new Image (display, collapsed));
+ }
+
+ PaletteData arrowPalette = new PaletteData (new RGB[] {
+ new RGB (0, 0, 0), new RGB (255, 255, 255)});
+ if (display.getData (ID_ARROWDOWN) == null) {
+ ImageData arrowDown = new ImageData (
+ 7, 4, 1,
+ arrowPalette, 1,
+ new byte[] {0x00, (byte)0x83, (byte)0xC7, (byte)0xEF});
+ arrowDown.transparentPixel = 0x1; /* use white for transparency */
+ display.setData (ID_ARROWDOWN, new Image (display, arrowDown));
+ }
+ if (display.getData (ID_ARROWUP) == null) {
+ ImageData arrowUp = new ImageData (
+ 7, 4, 1,
+ arrowPalette, 1,
+ new byte[] {(byte)0xEF, (byte)0xC7, (byte)0x83, 0x00});
+ arrowUp.transparentPixel = 0x1; /* use white for transparency */
+ display.setData (ID_ARROWUP, new Image (display, arrowUp));
+ }
+
+ PaletteData checkMarkPalette = new PaletteData (
+ new RGB[] {new RGB (0, 0, 0), new RGB (252, 3, 251)});
+ byte[] checkbox = new byte[] {0, 0, 127, -64, 127, -64, 127, -64, 127, -64, 127, -64, 127, -64, 127, -64, 127, -64, 127, -64, 0, 0};
+ ImageData checkmark = new ImageData (7, 7, 1, checkMarkPalette, 1, new byte[] {-4, -8, 112, 34, 6, -114, -34});
+ checkmark.transparentPixel = 1;
+ if (display.getData (ID_CHECKMARK) == null) {
+ display.setData (ID_CHECKMARK, new Image (display, checkmark));
+ }
+
+ if (display.getData (ID_UNCHECKED) == null) {
+ PaletteData uncheckedPalette = new PaletteData (
+ new RGB[] {new RGB (128, 128, 128), new RGB (255, 255, 255)});
+ ImageData unchecked = new ImageData (11, 11, 1, uncheckedPalette, 2, checkbox);
+ display.setData (ID_UNCHECKED, new Image (display, unchecked));
+ }
+
+ if (display.getData (ID_GRAYUNCHECKED) == null) {
+ PaletteData grayUncheckedPalette = new PaletteData (
+ new RGB[] {new RGB (128, 128, 128), new RGB (192, 192, 192)});
+ ImageData grayUnchecked = new ImageData (11, 11, 1, grayUncheckedPalette, 2, checkbox);
+ display.setData (ID_GRAYUNCHECKED, new Image (display, grayUnchecked));
+ }
+
+ if (display.getData (ID_CONNECTOR_COLOR) == null) {
+ display.setData (ID_CONNECTOR_COLOR, new Color (display, 170, 170, 170));
+ }
+
+ display.disposeExec (new Runnable () {
+ public void run() {
+ Image expanded = (Image) display.getData (ID_EXPANDED);
+ if (expanded != null) expanded.dispose ();
+ Image collapsed = (Image) display.getData (ID_COLLAPSED);
+ if (collapsed != null) collapsed.dispose ();
+ Color connectorColor = (Color) display.getData (ID_CONNECTOR_COLOR);
+ if (connectorColor != null) connectorColor.dispose ();
+ Image unchecked = (Image) display.getData (ID_UNCHECKED);
+ if (unchecked != null) unchecked.dispose ();
+ Image grayUnchecked = (Image) display.getData (ID_GRAYUNCHECKED);
+ if (grayUnchecked != null) grayUnchecked.dispose ();
+ Image checkmark = (Image) display.getData (ID_CHECKMARK);
+ if (checkmark != null) checkmark.dispose ();
+ Image arrowDown = (Image) display.getData (ID_ARROWDOWN);
+ if (arrowDown != null) arrowDown.dispose ();
+ Image arrowUp = (Image) display.getData (ID_ARROWUP);
+ if (arrowUp != null) arrowUp.dispose ();
+
+ display.setData (ID_EXPANDED, null);
+ display.setData (ID_COLLAPSED, null);
+ display.setData (ID_CONNECTOR_COLOR, null);
+ display.setData (ID_UNCHECKED, null);
+ display.setData (ID_GRAYUNCHECKED, null);
+ display.setData (ID_CHECKMARK, null);
+ display.setData (ID_ARROWDOWN, null);
+ display.setData (ID_ARROWUP, null);
+ }
+ });
+}
+/*
+ * Important: Assumes that item just became available (ie.- was either created
+ * or the parent item was expanded) and the parent is available.
+ */
+void makeAvailable (TreeItem item) {
+ int parentItemCount = item.parentItem.items.length;
+ int index = 0;
+ if (parentItemCount == 1) { /* this is the only child of parentItem */
+ index = item.parentItem.availableIndex + 1;
+ } else {
+ /* determine this item's index in its parent */
+ int itemIndex = 0;
+ TreeItem[] items = item.parentItem.items;
+ for (int i = 0; i < items.length; i++) {
+ if (items [i] == item) {
+ itemIndex = i;
+ break;
+ }
+ }
+ if (itemIndex != parentItemCount - 1) { /* this is not the last child */
+ index = items [itemIndex + 1].availableIndex;
+ } else { /* this is the last child */
+ TreeItem previousItem = items [itemIndex - 1];
+ index = previousItem.availableIndex + previousItem.computeAvailableDescendentCount ();
+ }
+ }
+
+ if (availableItemsCount == availableItems.length) {
+ int grow = drawCount <= 0 ? 4 : Math.max (4, availableItems.length * 3 / 2);
+ TreeItem[] newAvailableItems = new TreeItem [availableItems.length + grow];
+ System.arraycopy (availableItems, 0, newAvailableItems, 0, availableItems.length);
+ availableItems = newAvailableItems;
+ }
+ if (index != availableItemsCount) {
+ /* new item is not at end of list, so shift other items right to create space for it */
+ System.arraycopy (availableItems, index, availableItems, index + 1, availableItemsCount - index);
+ }
+ availableItems [index] = item;
+ availableItemsCount++;
+
+ /* update availableIndex as needed */
+ for (int i = index; i < availableItemsCount; i++) {
+ availableItems [i].availableIndex = i;
+ }
+}
+
+/*
+ * Important: Assumes that item is available and its descendents have just become
+ * available (ie.- they were either created or the item was expanded).
+ */
+void makeDescendentsAvailable (TreeItem item, TreeItem[] descendents) {
+ int itemAvailableIndex = item.availableIndex;
+ TreeItem[] newAvailableItems = new TreeItem [availableItemsCount + descendents.length - 1];
+
+ System.arraycopy (availableItems, 0, newAvailableItems, 0, itemAvailableIndex);
+ System.arraycopy (descendents, 0, newAvailableItems, itemAvailableIndex, descendents.length);
+ int startIndex = itemAvailableIndex + 1;
+ System.arraycopy (
+ availableItems,
+ startIndex,
+ newAvailableItems,
+ itemAvailableIndex + descendents.length,
+ availableItemsCount - startIndex);
+ availableItems = newAvailableItems;
+ availableItemsCount = availableItems.length;
+
+ /* update availableIndex as needed */
+ for (int i = itemAvailableIndex; i < availableItemsCount; i++) {
+ availableItems [i].availableIndex = i;
+ }
+}
+
+/*
+ * Important: Assumes that item is available and its descendents have just become
+ * unavailable (ie.- they were either disposed or the item was collapsed).
+ */
+void makeDescendentsUnavailable (TreeItem item, TreeItem[] descendents) {
+ int descendentsLength = descendents.length;
+ TreeItem[] newAvailableItems = new TreeItem [availableItemsCount - descendentsLength + 1];
+
+ System.arraycopy (availableItems, 0, newAvailableItems, 0, item.availableIndex + 1);
+ int startIndex = item.availableIndex + descendentsLength;
+ System.arraycopy (
+ availableItems,
+ startIndex,
+ newAvailableItems,
+ item.availableIndex + 1,
+ availableItemsCount - startIndex);
+ availableItems = newAvailableItems;
+ availableItemsCount = availableItems.length;
+
+ /* update availableIndexes */
+ for (int i = 1; i < descendents.length; i++) {
+ /* skip the first descendent since this is the item being collapsed */
+ descendents [i].availableIndex = -1;
+ }
+ for (int i = item.availableIndex; i < availableItemsCount; i++) {
+ availableItems [i].availableIndex = i;
+ }
+
+ /* remove the selection from all descendents */
+ for (int i = selectedItems.length - 1; i >= 0; i--) {
+ if (selectedItems [i] != item && selectedItems [i].hasAncestor (item)) {
+ removeSelectedItem (i);
+ }
+ }
+
+ /* if the anchorItem is being hidden then clear it */
+ if (anchorItem != null && anchorItem != item && anchorItem.hasAncestor (item)) {
+ anchorItem = null;
+ }
+}
+void onArrowDown (int stateMask) {
+ if ((stateMask & (SWT.SHIFT | SWT.CTRL)) == 0) {
+ /* Down Arrow with no modifiers */
+ int newFocusIndex = focusItem.availableIndex + 1;
+ if (newFocusIndex == availableItemsCount) return; /* at bottom */
+ selectItem (availableItems [newFocusIndex], false);
+ setFocusItem (availableItems [newFocusIndex], true);
+ redrawItem (newFocusIndex, true);
+ showItem (availableItems [newFocusIndex]);
+ Event newEvent = new Event ();
+ newEvent.item = availableItems [newFocusIndex];
+ postEvent (SWT.Selection, newEvent);
+ return;
+ }
+ if ((style & SWT.SINGLE) != 0) {
+ if ((stateMask & SWT.CTRL) != 0) {
+ /* CTRL+Down Arrow, CTRL+Shift+Down Arrow */
+ int visibleItemCount = (clientArea.height - getHeaderHeight ()) / itemHeight;
+ if (availableItemsCount <= topIndex + visibleItemCount) return; /* at bottom */
+ update ();
+ topIndex++;
+ ScrollBar vBar = getVerticalBar ();
+ if (vBar != null) vBar.setSelection (topIndex);
+ GC gc = new GC (this);
+ gc.copyArea (
+ 0, 0,
+ clientArea.width, clientArea.height,
+ 0, -itemHeight);
+ gc.dispose ();
+ return;
+ }
+ /* Shift+Down Arrow */
+ int newFocusIndex = focusItem.availableIndex + 1;
+ if (newFocusIndex == availableItemsCount) return; /* at bottom */
+ selectItem (availableItems [newFocusIndex], false);
+ setFocusItem (availableItems [newFocusIndex], true);
+ redrawItem (newFocusIndex, true);
+ showItem (availableItems [newFocusIndex]);
+ Event newEvent = new Event ();
+ newEvent.item = availableItems [newFocusIndex];
+ postEvent (SWT.Selection, newEvent);
+ return;
+ }
+ /* SWT.MULTI */
+ if ((stateMask & SWT.CTRL) != 0) {
+ if ((stateMask & SWT.SHIFT) != 0) {
+ /* CTRL+Shift+Down Arrow */
+ int visibleItemCount = (clientArea.height - getHeaderHeight ()) / itemHeight;
+ if (availableItemsCount <= topIndex + visibleItemCount) return; /* at bottom */
+ update ();
+ topIndex++;
+ ScrollBar vBar = getVerticalBar ();
+ if (vBar != null) vBar.setSelection (topIndex);
+ GC gc = new GC (this);
+ gc.copyArea (
+ 0, 0,
+ clientArea.width, clientArea.height,
+ 0, -itemHeight);
+ gc.dispose ();
+ return;
+ }
+ /* CTRL+Down Arrow */
+ int focusIndex = focusItem.availableIndex;
+ if (focusIndex == availableItemsCount - 1) return; /* at bottom */
+ TreeItem newFocusItem = availableItems [focusIndex + 1];
+ setFocusItem (newFocusItem, true);
+ redrawItem (newFocusItem.availableIndex, true);
+ showItem (newFocusItem);
+ return;
+ }
+ /* Shift+Down Arrow */
+ int newFocusIndex = focusItem.availableIndex + 1;
+ if (newFocusIndex == availableItemsCount) return; /* at bottom */
+ if (anchorItem == null) anchorItem = focusItem;
+ if (focusItem.availableIndex < anchorItem.availableIndex) {
+ deselectItem (focusItem);
+ redrawItem (focusItem.availableIndex, true);
+ }
+ selectItem (availableItems [newFocusIndex], true);
+ setFocusItem (availableItems [newFocusIndex], true);
+ redrawItem (newFocusIndex, true);
+ showItem (availableItems [newFocusIndex]);
+ Event newEvent = new Event ();
+ newEvent.item = availableItems [newFocusIndex];
+ postEvent (SWT.Selection, newEvent);
+}
+void onArrowLeft (int stateMask) {
+ if ((stateMask & SWT.CTRL) != 0) {
+ /* CTRL+Left Arrow, CTRL+Shift+Left Arrow */
+ if (horizontalOffset == 0) return;
+ int newSelection = Math.max (0, horizontalOffset - SIZE_HORIZONTALSCROLL);
+ update ();
+ GC gc = new GC (this);
+ gc.copyArea (
+ 0, 0,
+ clientArea.width, clientArea.height,
+ horizontalOffset - newSelection, 0);
+ gc.dispose ();
+ if (header.getVisible ()) {
+ header.update ();
+ Rectangle headerClientArea = header.getClientArea ();
+ gc = new GC (header);
+ gc.copyArea (
+ 0, 0,
+ headerClientArea.width, headerClientArea.height,
+ horizontalOffset - newSelection, 0);
+ gc.dispose();
+ }
+ horizontalOffset = newSelection;
+ ScrollBar hBar = getHorizontalBar ();
+ if (hBar != null) hBar.setSelection (horizontalOffset);
+ return;
+ }
+ /* Left Arrow with no modifiers, Shift+Left Arrow */
+ if (focusItem.expanded) {
+ focusItem.setExpanded (false);
+ Event newEvent = new Event ();
+ newEvent.item = focusItem;
+ sendEvent (SWT.Collapse, newEvent);
+ return;
+ }
+ TreeItem parentItem = focusItem.parentItem;
+ if (parentItem == null) return;
+
+ selectItem (parentItem, false);
+ setFocusItem (parentItem, true);
+ redrawItem (parentItem.availableIndex, true);
+ showItem (parentItem);
+ Event newEvent = new Event ();
+ newEvent.item = parentItem;
+ postEvent (SWT.Selection, newEvent);
+}
+void onArrowRight (int stateMask) {
+ if ((stateMask & SWT.CTRL) != 0) {
+ /* CTRL+Right Arrow, CTRL+Shift+Right Arrow */
+ ScrollBar hBar = getHorizontalBar ();
+ if (hBar != null) {
+ int maximum = hBar.getMaximum ();
+ int clientWidth = clientArea.width;
+ if ((horizontalOffset + clientWidth) == maximum) return;
+ if (maximum <= clientWidth) return;
+ int newSelection = Math.min (horizontalOffset + SIZE_HORIZONTALSCROLL, maximum - clientWidth);
+ update ();
+ GC gc = new GC (this);
+ gc.copyArea (
+ 0, 0,
+ clientArea.width, clientArea.height,
+ horizontalOffset - newSelection, 0);
+ gc.dispose ();
+ if (header.getVisible ()) {
+ Rectangle headerClientArea = header.getClientArea ();
+ header.update ();
+ gc = new GC (header);
+ gc.copyArea (
+ 0, 0,
+ headerClientArea.width, headerClientArea.height,
+ horizontalOffset - newSelection, 0);
+ gc.dispose();
+ }
+ horizontalOffset = newSelection;
+ hBar.setSelection (horizontalOffset);
+ }
+ return;
+ }
+ /* Right Arrow with no modifiers, Shift+Right Arrow */
+ TreeItem[] children = focusItem.items;
+ if (children.length == 0) return;
+ if (!focusItem.expanded) {
+ focusItem.setExpanded (true);
+ Event newEvent = new Event ();
+ newEvent.item = focusItem;
+ inExpand = true;
+ sendEvent (SWT.Expand, newEvent);
+ inExpand = false;
+ if (isDisposed ()) return;
+ if (focusItem.items.length == 0) {
+ focusItem.expanded = false;
+ }
+ return;
+ }
+ selectItem (children [0], false);
+ setFocusItem (children [0], true);
+ redrawItem (children [0].availableIndex, true);
+ showItem (children [0]);
+ Event newEvent = new Event ();
+ newEvent.item = children [0];
+ postEvent (SWT.Selection, newEvent);
+}
+void onArrowUp (int stateMask) {
+ if ((stateMask & (SWT.SHIFT | SWT.CTRL)) == 0) {
+ /* Up Arrow with no modifiers */
+ int newFocusIndex = focusItem.availableIndex - 1;
+ if (newFocusIndex < 0) return; /* at top */
+ TreeItem item = availableItems [newFocusIndex];
+ selectItem (item, false);
+ setFocusItem (item, true);
+ redrawItem (newFocusIndex, true);
+ showItem (item);
+ Event newEvent = new Event ();
+ newEvent.item = item;
+ postEvent (SWT.Selection, newEvent);
+ return;
+ }
+ if ((style & SWT.SINGLE) != 0) {
+ if ((stateMask & SWT.CTRL) != 0) {
+ /* CTRL+Up Arrow, CTRL+Shift+Up Arrow */
+ if (topIndex == 0) return; /* at top */
+ update ();
+ topIndex--;
+ ScrollBar vBar = getVerticalBar ();
+ if (vBar != null) vBar.setSelection (topIndex);
+ GC gc = new GC (this);
+ gc.copyArea (
+ 0, 0,
+ clientArea.width, clientArea.height,
+ 0, itemHeight);
+ gc.dispose ();
+ return;
+ }
+ /* Shift+Up Arrow */
+ int newFocusIndex = focusItem.availableIndex - 1;
+ if (newFocusIndex < 0) return; /* at top */
+ TreeItem item = availableItems [newFocusIndex];
+ selectItem (item, false);
+ setFocusItem (item, true);
+ redrawItem (newFocusIndex, true);
+ showItem (item);
+ Event newEvent = new Event ();
+ newEvent.item = item;
+ postEvent (SWT.Selection, newEvent);
+ return;
+ }
+ /* SWT.MULTI */
+ if ((stateMask & SWT.CTRL) != 0) {
+ if ((stateMask & SWT.SHIFT) != 0) {
+ /* CTRL+Shift+Up Arrow */
+ if (topIndex == 0) return; /* at top */
+ update ();
+ topIndex--;
+ ScrollBar vBar = getVerticalBar ();
+ if (vBar != null) vBar.setSelection (topIndex);
+ GC gc = new GC (this);
+ gc.copyArea (
+ 0, 0,
+ clientArea.width, clientArea.height,
+ 0, itemHeight);
+ gc.dispose ();
+ return;
+ }
+ /* CTRL+Up Arrow */
+ int focusIndex = focusItem.availableIndex;
+ if (focusIndex == 0) return; /* at top */
+ TreeItem newFocusItem = availableItems [focusIndex - 1];
+ setFocusItem (newFocusItem, true);
+ showItem (newFocusItem);
+ redrawItem (newFocusItem.availableIndex, true);
+ return;
+ }
+ /* Shift+Up Arrow */
+ int newFocusIndex = focusItem.availableIndex - 1;
+ if (newFocusIndex < 0) return; /* at top */
+ if (anchorItem == null) anchorItem = focusItem;
+ if (anchorItem.availableIndex < focusItem.availableIndex) {
+ deselectItem (focusItem);
+ redrawItem (focusItem.availableIndex, true);
+ }
+ TreeItem item = availableItems [newFocusIndex];
+ selectItem (item, true);
+ setFocusItem (item, true);
+ redrawItem (newFocusIndex, true);
+ showItem (item);
+ Event newEvent = new Event ();
+ newEvent.item = item;
+ postEvent (SWT.Selection, newEvent);
+}
+void onCR () {
+ if (focusItem == null) return;
+ Event event = new Event ();
+ event.item = focusItem;
+ postEvent (SWT.DefaultSelection, event);
+}
+void onDispose (Event event) {
+ if (isDisposed ()) return;
+ if (ignoreDispose) return;
+ ignoreDispose = true;
+ notifyListeners(SWT.Dispose, event);
+ event.type = SWT.None;
+ for (int i = 0; i < items.length; i++) {
+ items [i].dispose (false);
+ }
+ for (int i = 0; i < columns.length; i++) {
+ columns [i].dispose (false);
+ }
+ if (toolTipShell != null) {
+ toolTipShell.dispose ();
+ toolTipShell = null;
+ toolTipLabel = null;
+ }
+ toolTipListener = null;
+ topIndex = availableItemsCount = horizontalOffset = 0;
+ availableItems = items = selectedItems = null;
+ columns = orderedColumns = null;
+ focusItem = anchorItem = insertMarkItem = lastClickedItem = null;
+ lastSelectionEvent = null;
+ header = null;
+ resizeColumn = sortColumn = null;
+ expanderBounds = null;
+}
+void onEnd (int stateMask) {
+ int lastAvailableIndex = availableItemsCount - 1;
+ if ((stateMask & (SWT.CTRL | SWT.SHIFT)) == 0) {
+ /* End with no modifiers */
+ if (focusItem.availableIndex == lastAvailableIndex) return; /* at bottom */
+ TreeItem item = availableItems [lastAvailableIndex];
+ selectItem (item, false);
+ setFocusItem (item, true);
+ redrawItem (lastAvailableIndex, true);
+ showItem (item);
+ Event newEvent = new Event ();
+ newEvent.item = item;
+ postEvent (SWT.Selection, newEvent);
+ return;
+ }
+ if ((style & SWT.SINGLE) != 0) {
+ if ((stateMask & SWT.CTRL) != 0) {
+ /* CTRL+End, CTRL+Shift+End */
+ int visibleItemCount = (clientArea.height - getHeaderHeight ()) / itemHeight;
+ setTopItem (availableItems [availableItemsCount - visibleItemCount]);
+ return;
+ }
+ /* Shift+End */
+ if (focusItem.availableIndex == lastAvailableIndex) return; /* at bottom */
+ TreeItem item = availableItems [lastAvailableIndex];
+ selectItem (item, false);
+ setFocusItem (item, true);
+ redrawItem (lastAvailableIndex, true);
+ showItem (item);
+ Event newEvent = new Event ();
+ newEvent.item = item;
+ postEvent (SWT.Selection, newEvent);
+ return;
+ }
+ /* SWT.MULTI */
+ if ((stateMask & SWT.CTRL) != 0) {
+ if ((stateMask & SWT.SHIFT) != 0) {
+ /* CTRL+Shift+End */
+ showItem (availableItems [lastAvailableIndex]);
+ return;
+ }
+ /* CTRL+End */
+ if (focusItem.availableIndex == lastAvailableIndex) return; /* at bottom */
+ TreeItem item = availableItems [lastAvailableIndex];
+ setFocusItem (item, true);
+ showItem (item);
+ redrawItem (item.availableIndex, true);
+ return;
+ }
+ /* Shift+End */
+ if (anchorItem == null) anchorItem = focusItem;
+ TreeItem selectedItem = availableItems [lastAvailableIndex];
+ if (selectedItem == focusItem && selectedItem.isSelected ()) return;
+ int anchorIndex = anchorItem.availableIndex;
+ int selectIndex = selectedItem.availableIndex;
+ TreeItem[] newSelection = new TreeItem [selectIndex - anchorIndex + 1];
+ int writeIndex = 0;
+ for (int i = anchorIndex; i <= selectIndex; i++) {
+ newSelection [writeIndex++] = availableItems [i];
+ }
+ setSelection (newSelection, false);
+ setFocusItem (selectedItem, true);
+ redrawItems (anchorIndex, selectIndex, true);
+ showItem (selectedItem);
+ Event newEvent = new Event ();
+ newEvent.item = selectedItem;
+ postEvent (SWT.Selection, newEvent);
+}
+void onFocusIn () {
+ hasFocus = true;
+
+ if (items.length == 0) {
+ redraw ();
+ return;
+ }
+ if (focusItem != null) {
+ redrawItem (focusItem.availableIndex, true);
+ return;
+ }
+ /* an initial focus item must be selected */
+ TreeItem initialFocus = null;
+ if (selectedItems.length > 0) {
+ for (int i = 0; i < selectedItems.length && initialFocus == null; i++) {
+ if (selectedItems [i].isAvailable ()) {
+ initialFocus = selectedItems [i];
+ }
+ }
+ if (initialFocus == null) {
+ /* none of the selected items are available */
+ initialFocus = availableItems [topIndex];
+ }
+ } else {
+ initialFocus = availableItems [topIndex];
+ }
+ setFocusItem (initialFocus, false);
+ redrawItem (initialFocus.availableIndex, true);
+ return;
+}
+void onFocusOut () {
+ hasFocus = false;
+
+ if (items.length == 0) {
+ redraw ();
+ return;
+ }
+
+ if (focusItem != null) {
+ redrawItem (focusItem.availableIndex, true);
+ }
+}
+void onHome (int stateMask) {
+ if ((stateMask & (SWT.CTRL | SWT.SHIFT)) == 0) {
+ /* Home with no modifiers */
+ if (focusItem.availableIndex == 0) return; /* at top */
+ TreeItem item = availableItems [0];
+ selectItem (item, false);
+ setFocusItem (item, true);
+ redrawItem (0, true);
+ showItem (item);
+ Event newEvent = new Event ();
+ newEvent.item = item;
+ postEvent (SWT.Selection, newEvent);
+ return;
+ }
+ if ((style & SWT.SINGLE) != 0) {
+ if ((stateMask & SWT.CTRL) != 0) {
+ /* CTRL+Home, CTRL+Shift+Home */
+ setTopItem (availableItems [0]);
+ return;
+ }
+ /* Shift+Home */
+ if (focusItem.availableIndex == 0) return; /* at top */
+ TreeItem item = availableItems [0];
+ selectItem (item, false);
+ setFocusItem (item, true);
+ redrawItem (0, true);
+ showItem (item);
+ Event newEvent = new Event ();
+ newEvent.item = item;
+ postEvent (SWT.Selection, newEvent);
+ return;
+ }
+ /* SWT.MULTI */
+ if ((stateMask & SWT.CTRL) != 0) {
+ if ((stateMask & SWT.SHIFT) != 0) {
+ /* CTRL+Shift+Home */
+ setTopItem (availableItems [0]);
+ return;
+ }
+ /* CTRL+Home */
+ if (focusItem.availableIndex == 0) return; /* at top */
+ TreeItem item = availableItems [0];
+ setFocusItem (item, true);
+ showItem (item);
+ redrawItem (item.availableIndex, true);
+ return;
+ }
+ /* Shift+Home */
+ if (anchorItem == null) anchorItem = focusItem;
+ TreeItem selectedItem = availableItems [0];
+ if (selectedItem == focusItem && selectedItem.isSelected ()) return;
+ int anchorIndex = anchorItem.availableIndex;
+ int selectIndex = selectedItem.availableIndex;
+ TreeItem[] newSelection = new TreeItem [anchorIndex + 1];
+ int writeIndex = 0;
+ for (int i = anchorIndex; i >= 0; i--) {
+ newSelection [writeIndex++] = availableItems [i];
+ }
+ setSelection (newSelection, false);
+ setFocusItem (selectedItem, true);
+ redrawItems (anchorIndex, selectIndex, true);
+ showItem (selectedItem);
+ Event newEvent = new Event ();
+ newEvent.item = selectedItem;
+ postEvent (SWT.Selection, newEvent);
+}
+void onKeyDown (Event event) {
+ if (ignoreKey) {
+ ignoreKey = false;
+ return;
+ }
+ ignoreKey = true;
+ notifyListeners (event.type, event);
+ event.type = SWT.None;
+ if (!event.doit) return;
+ if (focusItem == null) return;
+ if ((event.stateMask & SWT.SHIFT) == 0 && event.keyCode != SWT.SHIFT) {
+ anchorItem = null;
+ }
+ switch (event.keyCode) {
+ case SWT.ARROW_UP:
+ onArrowUp (event.stateMask);
+ return;
+ case SWT.ARROW_DOWN:
+ onArrowDown (event.stateMask);
+ return;
+ case SWT.ARROW_LEFT:
+ onArrowLeft (event.stateMask);
+ return;
+ case SWT.ARROW_RIGHT:
+ onArrowRight (event.stateMask);
+ return;
+ case SWT.PAGE_UP:
+ onPageUp (event.stateMask);
+ return;
+ case SWT.PAGE_DOWN:
+ onPageDown (event.stateMask);
+ return;
+ case SWT.HOME:
+ onHome (event.stateMask);
+ return;
+ case SWT.END:
+ onEnd (event.stateMask);
+ return;
+ }
+ if (event.character == ' ') {
+ onSpace ();
+ return;
+ }
+ if (event.character == SWT.CR) {
+ onCR ();
+ return;
+ }
+ if ((event.stateMask & SWT.CTRL) != 0) return;
+
+ int initialIndex = focusItem.availableIndex;
+ char character = Character.toLowerCase (event.character);
+ /* check available items from current focus item to bottom */
+ for (int i = initialIndex + 1; i < availableItemsCount; i++) {
+ TreeItem item = availableItems [i];
+ String text = item.getText (0, false);
+ if (text.length() > 0) {
+ if (Character.toLowerCase (text.charAt (0)) == character) {
+ selectItem (item, false);
+ setFocusItem (item, true);
+ redrawItem (i, true);
+ showItem (item);
+ Event newEvent = new Event ();
+ newEvent.item = item;
+ postEvent (SWT.Selection, newEvent);
+ return;
+ }
+ }
+ }
+ /* check available items from top to current focus item */
+ for (int i = 0; i < initialIndex; i++) {
+ TreeItem item = availableItems [i];
+ String text = item.getText (0, false);
+ if (text.length() > 0) {
+ if (Character.toLowerCase (text.charAt (0)) == character) {
+ selectItem (item, false);
+ setFocusItem (item, true);
+ redrawItem (i, true);
+ showItem (item);
+ Event newEvent = new Event ();
+ newEvent.item = item;
+ postEvent (SWT.Selection, newEvent);
+ return;
+ }
+ }
+ }
+}
+void onMouseDoubleClick (Event event) {
+ if (!isFocusControl ()) setFocus ();
+ int index = (event.y - getHeaderHeight ()) / itemHeight + topIndex;
+ if (!(0 <= index && index < availableItemsCount)) return; /* not on an available item */
+ TreeItem selectedItem = availableItems [index];
+
+ /*
+ * If the two clicks of the double click did not occur over the same item then do not
+ * consider this to be a default selection.
+ */
+ if (selectedItem != lastClickedItem) return;
+
+ /* if click was in expander box then don't fire event */
+ if (selectedItem.items.length > 0 && selectedItem.getExpanderBounds ().contains (event.x, event.y)) {
+ return;
+ }
+
+ if (!selectedItem.getHitBounds ().contains (event.x, event.y)) return; /* considers x */
+
+ Event newEvent = new Event ();
+ newEvent.item = selectedItem;
+ postEvent (SWT.DefaultSelection, newEvent);
+}
+void onMouseDown (Event event) {
+ if (!isFocusControl ()) forceFocus ();
+ int index = (event.y - getHeaderHeight ()) / itemHeight + topIndex;
+ if (!(0 <= index && index < availableItemsCount)) return; /* not on an available item */
+ TreeItem selectedItem = availableItems [index];
+
+ /* if click was in expander box */
+ if (selectedItem.items.length > 0 && selectedItem.getExpanderBounds ().contains (event.x, event.y)) {
+ if (event.button != 1) return;
+ boolean expand = !selectedItem.expanded;
+ selectedItem.setExpanded (expand);
+ Event newEvent = new Event ();
+ newEvent.item = selectedItem;
+ if (expand) {
+ inExpand = true;
+ sendEvent (SWT.Expand, newEvent);
+ inExpand = false;
+ if (isDisposed ()) return;
+ if (selectedItem.items.length == 0) {
+ selectedItem.expanded = false;
+ }
+ } else {
+ sendEvent (SWT.Collapse, newEvent);
+ }
+ return;
+ }
+ /* if click was in checkbox */
+ if ((style & SWT.CHECK) != 0 && selectedItem.getCheckboxBounds ().contains (event.x, event.y)) {
+ if (event.button != 1) return;
+ selectedItem.setChecked (!selectedItem.checked);
+ Event newEvent = new Event ();
+ newEvent.item = selectedItem;
+ newEvent.detail = SWT.CHECK;
+ postEvent (SWT.Selection, newEvent);
+ return;
+ }
+
+ if (!selectedItem.getHitBounds ().contains (event.x, event.y)) return;
+
+ if ((event.stateMask & SWT.SHIFT) == 0 && event.keyCode != SWT.SHIFT) anchorItem = null;
+
+ boolean sendSelection = true;
+ /* Detect when this is the second click of a DefaultSelection and don't fire Selection */
+ if (lastSelectionEvent != null && lastSelectionEvent.item == selectedItem) {
+ if (event.time - lastSelectionEvent.time <= display.getDoubleClickTime ()) {
+ sendSelection = false;
+ } else {
+ lastSelectionEvent = event;
+ event.item = selectedItem;
+ }
+ } else {
+ lastSelectionEvent = event;
+ event.item = selectedItem;
+ }
+
+ if ((style & SWT.SINGLE) != 0) {
+ if (!selectedItem.isSelected ()) {
+ if (event.button == 1) {
+ selectItem (selectedItem, false);
+ setFocusItem (selectedItem, true);
+ redrawItem (selectedItem.availableIndex, true);
+ if (sendSelection) {
+ Event newEvent = new Event ();
+ newEvent.item = selectedItem;
+ postEvent (SWT.Selection, newEvent);
+ }
+ return;
+ }
+ if ((event.stateMask & (SWT.CTRL | SWT.SHIFT)) == 0) {
+ selectItem (selectedItem, false);
+ setFocusItem (selectedItem, true);
+ redrawItem (selectedItem.availableIndex, true);
+ if (sendSelection) {
+ Event newEvent = new Event ();
+ newEvent.item = selectedItem;
+ postEvent (SWT.Selection, newEvent);
+ }
+ return;
+ }
+ }
+ /* item is selected */
+ if (event.button == 1) {
+ /* fire a selection event, though the selection did not change */
+ if (sendSelection) {
+ Event newEvent = new Event ();
+ newEvent.item = selectedItem;
+ postEvent (SWT.Selection, newEvent);
+ }
+ return;
+ }
+ }
+ /* SWT.MULTI */
+ if (!selectedItem.isSelected ()) {
+ if (event.button == 1) {
+ if ((event.stateMask & (SWT.CTRL | SWT.SHIFT)) == SWT.SHIFT) {
+ if (anchorItem == null) anchorItem = focusItem;
+ int anchorIndex = anchorItem.availableIndex;
+ int selectIndex = selectedItem.availableIndex;
+ TreeItem[] newSelection = new TreeItem [Math.abs (anchorIndex - selectIndex) + 1];
+ int step = anchorIndex < selectIndex ? 1 : -1;
+ int writeIndex = 0;
+ for (int i = anchorIndex; i != selectIndex; i += step) {
+ newSelection [writeIndex++] = availableItems [i];
+ }
+ newSelection [writeIndex] = availableItems [selectIndex];
+ setSelection (newSelection, false);
+ setFocusItem (selectedItem, true);
+ redrawItems (
+ Math.min (anchorIndex, selectIndex),
+ Math.max (anchorIndex, selectIndex),
+ true);
+ if (sendSelection) {
+ Event newEvent = new Event ();
+ newEvent.item = selectedItem;
+ postEvent (SWT.Selection, newEvent);
+ }
+ return;
+ }
+ selectItem (selectedItem, (event.stateMask & SWT.CTRL) != 0);
+ setFocusItem (selectedItem, true);
+ redrawItem (selectedItem.availableIndex, true);
+ if (sendSelection) {
+ Event newEvent = new Event ();
+ newEvent.item = selectedItem;
+ postEvent (SWT.Selection, newEvent);
+ }
+ return;
+ }
+ /* button 3 */
+ if ((event.stateMask & (SWT.CTRL | SWT.SHIFT)) == 0) {
+ selectItem (selectedItem, false);
+ setFocusItem (selectedItem, true);
+ redrawItem (selectedItem.availableIndex, true);
+ if (sendSelection) {
+ Event newEvent = new Event ();
+ newEvent.item = selectedItem;
+ postEvent (SWT.Selection, newEvent);
+ }
+ return;
+ }
+ }
+ /* item is selected */
+ if (event.button != 1) return;
+ if ((event.stateMask & SWT.CTRL) != 0) {
+ removeSelectedItem (getSelectionIndex (selectedItem));
+ setFocusItem (selectedItem, true);
+ redrawItem (selectedItem.availableIndex, true);
+ if (sendSelection) {
+ Event newEvent = new Event ();
+ newEvent.item = selectedItem;
+ postEvent (SWT.Selection, newEvent);
+ }
+ return;
+ }
+ if ((event.stateMask & SWT.SHIFT) != 0) {
+ if (anchorItem == null) anchorItem = focusItem;
+ int anchorIndex = anchorItem.availableIndex;
+ int selectIndex = selectedItem.availableIndex;
+ TreeItem[] newSelection = new TreeItem [Math.abs (anchorIndex - selectIndex) + 1];
+ int step = anchorIndex < selectIndex ? 1 : -1;
+ int writeIndex = 0;
+ for (int i = anchorIndex; i != selectIndex; i += step) {
+ newSelection [writeIndex++] = availableItems [i];
+ }
+ newSelection [writeIndex] = availableItems [selectIndex];
+ setSelection (newSelection, false);
+ setFocusItem (selectedItem, true);
+ redrawItems (
+ Math.min (anchorIndex, selectIndex),
+ Math.max (anchorIndex, selectIndex),
+ true);
+ if (sendSelection) {
+ Event newEvent = new Event ();
+ newEvent.item = selectedItem;
+ postEvent (SWT.Selection, newEvent);
+ }
+ return;
+ }
+ selectItem (selectedItem, false);
+ setFocusItem (selectedItem, true);
+ redrawItem (selectedItem.availableIndex, true);
+ if (sendSelection) {
+ Event newEvent = new Event ();
+ newEvent.item = selectedItem;
+ postEvent (SWT.Selection, newEvent);
+ }
+}
+void onMouseUp (Event event) {
+ int index = (event.y - getHeaderHeight ()) / itemHeight + topIndex;
+ if (!(0 <= index && index < availableItemsCount)) return; /* not on an available item */
+ lastClickedItem = availableItems [index];
+}
+void onPageDown (int stateMask) {
+ int visibleItemCount = (clientArea.height - getHeaderHeight ()) / itemHeight;
+ if ((stateMask & (SWT.CTRL | SWT.SHIFT)) == 0) {
+ /* PageDown with no modifiers */
+ int newFocusIndex = focusItem.availableIndex + visibleItemCount - 1;
+ newFocusIndex = Math.min (newFocusIndex, availableItemsCount - 1);
+ if (newFocusIndex == focusItem.availableIndex) return;
+ TreeItem item = availableItems [newFocusIndex];
+ selectItem (item, false);
+ setFocusItem (item, true);
+ showItem (item);
+ redrawItem (item.availableIndex, true);
+ return;
+ }
+ if ((stateMask & (SWT.CTRL | SWT.SHIFT)) == (SWT.CTRL | SWT.SHIFT)) {
+ /* CTRL+Shift+PageDown */
+ int newTopIndex = topIndex + visibleItemCount;
+ newTopIndex = Math.min (newTopIndex, availableItemsCount - visibleItemCount);
+ if (newTopIndex == topIndex) return;
+ setTopItem (availableItems [newTopIndex]);
+ return;
+ }
+ if ((style & SWT.SINGLE) != 0) {
+ if ((stateMask & SWT.SHIFT) != 0) {
+ /* Shift+PageDown */
+ int newFocusIndex = focusItem.availableIndex + visibleItemCount - 1;
+ newFocusIndex = Math.min (newFocusIndex, availableItemsCount - 1);
+ if (newFocusIndex == focusItem.availableIndex) return;
+ TreeItem item = availableItems [newFocusIndex];
+ selectItem (item, false);
+ setFocusItem (item, true);
+ showItem (item);
+ redrawItem (item.availableIndex, true);
+ return;
+ }
+ /* CTRL+PageDown */
+ int newTopIndex = topIndex + visibleItemCount;
+ newTopIndex = Math.min (newTopIndex, availableItemsCount - visibleItemCount);
+ if (newTopIndex == topIndex) return;
+ setTopItem (availableItems [newTopIndex]);
+ return;
+ }
+ /* SWT.MULTI */
+ if ((stateMask & SWT.CTRL) != 0) {
+ /* CTRL+PageDown */
+ int bottomIndex = Math.min (topIndex + visibleItemCount - 1, availableItemsCount - 1);
+ if (focusItem.availableIndex != bottomIndex) {
+ /* move focus to bottom item in viewport */
+ setFocusItem (availableItems [bottomIndex], true);
+ redrawItem (bottomIndex, true);
+ } else {
+ /* at bottom of viewport, so set focus to bottom item one page down */
+ int newFocusIndex = Math.min (availableItemsCount - 1, bottomIndex + visibleItemCount);
+ if (newFocusIndex == focusItem.availableIndex) return;
+ setFocusItem (availableItems [newFocusIndex], true);
+ showItem (availableItems [newFocusIndex]);
+ redrawItem (newFocusIndex, true);
+ }
+ return;
+ }
+ /* Shift+PageDown */
+ if (anchorItem == null) anchorItem = focusItem;
+ int anchorIndex = anchorItem.availableIndex;
+ int bottomIndex = Math.min (topIndex + visibleItemCount - 1, availableItemsCount - 1);
+ int selectIndex;
+ if (focusItem.availableIndex != bottomIndex) {
+ /* select from focus to bottom item in viewport */
+ selectIndex = bottomIndex;
+ } else {
+ /* already at bottom of viewport, so select to bottom of one page down */
+ selectIndex = Math.min (availableItemsCount - 1, bottomIndex + visibleItemCount);
+ if (selectIndex == focusItem.availableIndex && focusItem.isSelected ()) return;
+ }
+ TreeItem selectedItem = availableItems [selectIndex];
+ TreeItem[] newSelection = new TreeItem [Math.abs (anchorIndex - selectIndex) + 1];
+ int step = anchorIndex < selectIndex ? 1 : -1;
+ int writeIndex = 0;
+ for (int i = anchorIndex; i != selectIndex; i += step) {
+ newSelection [writeIndex++] = availableItems [i];
+ }
+ newSelection [writeIndex] = availableItems [selectIndex];
+ setSelection (newSelection, false);
+ setFocusItem (selectedItem, true);
+ showItem (selectedItem);
+ Event newEvent = new Event ();
+ newEvent.item = selectedItem;
+ postEvent (SWT.Selection, newEvent);
+}
+void onPageUp (int stateMask) {
+ int visibleItemCount = (clientArea.height - getHeaderHeight ()) / itemHeight;
+ if ((stateMask & (SWT.CTRL | SWT.SHIFT)) == 0) {
+ /* PageUp with no modifiers */
+ int newFocusIndex = Math.max (0, focusItem.availableIndex - visibleItemCount + 1);
+ if (newFocusIndex == focusItem.availableIndex) return;
+ TreeItem item = availableItems [newFocusIndex];
+ selectItem (item, false);
+ setFocusItem (item, true);
+ showItem (item);
+ redrawItem (item.availableIndex, true);
+ return;
+ }
+ if ((stateMask & (SWT.CTRL | SWT.SHIFT)) == (SWT.CTRL | SWT.SHIFT)) {
+ /* CTRL+Shift+PageUp */
+ int newTopIndex = Math.max (0, topIndex - visibleItemCount);
+ if (newTopIndex == topIndex) return;
+ setTopItem (availableItems [newTopIndex]);
+ return;
+ }
+ if ((style & SWT.SINGLE) != 0) {
+ if ((stateMask & SWT.SHIFT) != 0) {
+ /* Shift+PageUp */
+ int newFocusIndex = Math.max (0, focusItem.availableIndex - visibleItemCount + 1);
+ if (newFocusIndex == focusItem.availableIndex) return;
+ TreeItem item = availableItems [newFocusIndex];
+ selectItem (item, false);
+ setFocusItem (item, true);
+ showItem (item);
+ redrawItem (item.availableIndex, true);
+ return;
+ }
+ /* CTRL+PageUp */
+ int newTopIndex = Math.max (0, topIndex - visibleItemCount);
+ if (newTopIndex == topIndex) return;
+ setTopItem (availableItems [newTopIndex]);
+ return;
+ }
+ /* SWT.MULTI */
+ if ((stateMask & SWT.CTRL) != 0) {
+ /* CTRL+PageUp */
+ if (focusItem.availableIndex != topIndex) {
+ /* move focus to top item in viewport */
+ setFocusItem (availableItems [topIndex], true);
+ redrawItem (topIndex, true);
+ } else {
+ /* at top of viewport, so set focus to top item one page up */
+ int newFocusIndex = Math.max (0, focusItem.availableIndex - visibleItemCount);
+ if (newFocusIndex == focusItem.availableIndex) return;
+ setFocusItem (availableItems [newFocusIndex], true);
+ showItem (availableItems [newFocusIndex]);
+ redrawItem (newFocusIndex, true);
+ }
+ return;
+ }
+ /* Shift+PageUp */
+ if (anchorItem == null) anchorItem = focusItem;
+ int anchorIndex = anchorItem.availableIndex;
+ int selectIndex;
+ if (focusItem.availableIndex != topIndex) {
+ /* select from focus to top item in viewport */
+ selectIndex = topIndex;
+ } else {
+ /* already at top of viewport, so select to top of one page up */
+ selectIndex = Math.max (0, topIndex - visibleItemCount);
+ if (selectIndex == focusItem.availableIndex && focusItem.isSelected ()) return;
+ }
+ TreeItem selectedItem = availableItems [selectIndex];
+ TreeItem[] newSelection = new TreeItem [Math.abs (anchorIndex - selectIndex) + 1];
+ int step = anchorIndex < selectIndex ? 1 : -1;
+ int writeIndex = 0;
+ for (int i = anchorIndex; i != selectIndex; i += step) {
+ newSelection [writeIndex++] = availableItems [i];
+ }
+ newSelection [writeIndex] = availableItems [selectIndex];
+ setSelection (newSelection, false);
+ setFocusItem (selectedItem, true);
+ showItem (selectedItem);
+ Event newEvent = new Event ();
+ newEvent.item = selectedItem;
+ postEvent (SWT.Selection, newEvent);
+}
+void onPaint (Event event) {
+ TreeColumn[] orderedColumns = getOrderedColumns ();
+ GC gc = event.gc;
+ Rectangle clipping = gc.getClipping ();
+ int headerHeight = getHeaderHeight ();
+ int numColumns = orderedColumns.length;
+ int startColumn = -1, endColumn = -1;
+ if (numColumns > 0) {
+ startColumn = computeColumnIntersect (clipping.x, 0);
+ if (startColumn != -1) { /* the clip x is within a column's bounds */
+ endColumn = computeColumnIntersect (clipping.x + clipping.width, startColumn);
+ if (endColumn == -1) endColumn = numColumns - 1;
+ }
+ } else {
+ startColumn = endColumn = 0;
+ }
+
+ /* Determine the items to be painted */
+ int startIndex = (clipping.y - headerHeight) / itemHeight + topIndex;
+ int endIndex = -1;
+ if (startIndex < availableItemsCount) {
+ endIndex = startIndex + Compatibility.ceil (clipping.height, itemHeight);
+ }
+ startIndex = Math.max (0, startIndex);
+ endIndex = Math.min (endIndex, availableItemsCount - 1);
+
+ /* fill background not handled by items */
+ gc.setBackground (getBackground ());
+ gc.setClipping (clipping);
+ int bottomY = endIndex >= 0 ? getItemY (availableItems [endIndex]) + itemHeight : 0;
+ int fillHeight = Math.max (0, clientArea.height - bottomY);
+ if (fillHeight > 0) { /* space below bottom item */
+ drawBackground (gc, 0, bottomY, clientArea.width, fillHeight);
+ }
+ if (columns.length > 0) {
+ TreeColumn column = orderedColumns [orderedColumns.length - 1]; /* last column */
+ int rightX = column.getX () + column.width;
+ if (rightX < clientArea.width) {
+ drawBackground (gc, rightX, 0, clientArea.width - rightX, clientArea.height - fillHeight);
+ }
+ }
+
+ /* paint the items */
+ boolean noFocusDraw = false;
+ int[] lineDash = gc.getLineDash ();
+ int lineWidth = gc.getLineWidth ();
+ for (int i = startIndex; i <= Math.min (endIndex, availableItemsCount - 1); i++) {
+ TreeItem item = availableItems [i];
+ if (!item.isDisposed ()) { /* ensure that item was not disposed in a callback */
+ if (startColumn == -1) {
+ /* indicates that region to paint is to the right of the last column */
+ noFocusDraw = item.paint (gc, null, true) || noFocusDraw;
+ } else {
+ if (numColumns == 0) {
+ noFocusDraw = item.paint (gc, null, false) || noFocusDraw;
+ } else {
+ for (int j = startColumn; j <= Math.min (endColumn, columns.length - 1); j++) {
+ if (!item.isDisposed ()) { /* ensure that item was not disposed in a callback */
+ noFocusDraw = item.paint (gc, orderedColumns [j], false) || noFocusDraw;
+ }
+ if (isDisposed () || gc.isDisposed ()) return; /* ensure that receiver was not disposed in a callback */
+ }
+ }
+ }
+ }
+ if (isDisposed () || gc.isDisposed ()) return; /* ensure that receiver was not disposed in a callback */
+ }
+
+ /* repaint grid lines */
+ gc.setClipping(clipping);
+ gc.setLineWidth (lineWidth);
+ if (linesVisible) {
+ gc.setForeground (display.getSystemColor (SWT.COLOR_WIDGET_LIGHT_SHADOW));
+ gc.setLineDash (lineDash);
+ if (numColumns > 0 && startColumn != -1) {
+ /* vertical column lines */
+ for (int i = startColumn; i <= endColumn; i++) {
+ int x = orderedColumns [i].getX () + orderedColumns [i].width - 1;
+ gc.drawLine (x, clipping.y, x, clipping.y + clipping.height);
+ }
+ }
+ /* horizontal item lines */
+ bottomY = clipping.y + clipping.height;
+ int rightX = clipping.x + clipping.width;
+ int y = (clipping.y - headerHeight) / itemHeight * itemHeight + headerHeight;
+ while (y <= bottomY) {
+ gc.drawLine (clipping.x, y, rightX, y);
+ y += itemHeight;
+ }
+ }
+
+ /* draw focus rectangle */
+ if (!noFocusDraw && isFocusControl ()) {
+ if (focusItem != null) {
+ Rectangle focusBounds = focusItem.getFocusBounds ();
+ if (focusBounds.width > 0) {
+ gc.setForeground (display.getSystemColor (SWT.COLOR_BLACK));
+ gc.setClipping (focusBounds);
+ if (focusItem.isSelected ()) {
+ gc.setLineDash (new int[] {2, 2});
+ } else {
+ gc.setLineDash (new int[] {1, 1});
+ }
+ gc.drawFocus (focusBounds.x, focusBounds.y, focusBounds.width, focusBounds.height);
+ }
+ } else {
+ /* no items, so draw focus border around Tree */
+ int y = headerHeight + 1;
+ int width = Math.max (0, clientArea.width - 2);
+ int height = Math.max (0, clientArea.height - headerHeight - 2);
+ gc.setForeground (display.getSystemColor (SWT.COLOR_BLACK));
+ gc.setClipping (1, y, width, height);
+ gc.setLineDash (new int[] {1, 1});
+ gc.drawFocus (1, y, width, height);
+ }
+ }
+
+ /* draw insert mark */
+ if (insertMarkItem != null) {
+ Rectangle focusBounds = insertMarkItem.getFocusBounds ();
+ gc.setForeground (display.getSystemColor (SWT.COLOR_BLACK));
+ gc.setClipping (focusBounds);
+ gc.setLineDash (lineDash);
+ if (insertMarkPrecedes) {
+ gc.drawLine (focusBounds.x, focusBounds.y, focusBounds.x + focusBounds.width, focusBounds.y);
+ } else {
+ int y = focusBounds.y + focusBounds.height - 1;
+ gc.drawLine (focusBounds.x, y, focusBounds.x + focusBounds.width, y);
+ }
+ }
+}
+void onResize (Event event) {
+ clientArea = getClientArea ();
+ /* vertical scrollbar */
+ ScrollBar vBar = getVerticalBar ();
+ if (vBar != null) {
+ int clientHeight = (clientArea.height - getHeaderHeight ()) / itemHeight;
+ int thumb = Math.min (clientHeight, availableItemsCount);
+ vBar.setThumb (thumb);
+ vBar.setPageIncrement (thumb);
+ int index = vBar.getSelection ();
+ if (index != topIndex) {
+ topIndex = index;
+ redraw ();
+ }
+ boolean visible = clientHeight < availableItemsCount;
+ if (visible != vBar.getVisible ()) {
+ vBar.setVisible (visible);
+ clientArea = getClientArea ();
+ }
+ }
+
+ /* horizontal scrollbar */
+ ScrollBar hBar = getHorizontalBar ();
+ if (hBar != null) {
+ int hBarMaximum = hBar.getMaximum ();
+ int thumb = Math.min (clientArea.width, hBarMaximum);
+ hBar.setThumb (thumb);
+ hBar.setPageIncrement (thumb);
+ horizontalOffset = hBar.getSelection ();
+ boolean visible = clientArea.width < hBarMaximum;
+ if (visible != hBar.getVisible ()) {
+ hBar.setVisible (visible);
+ clientArea = getClientArea ();
+ }
+ }
+
+ /* header */
+ int headerHeight = Math.max (fontHeight, headerImageHeight) + 2 * getHeaderPadding ();
+ header.setSize (clientArea.width, headerHeight);
+
+ /* if this is the focus control but there are no items then the boundary focus ring must be repainted */
+ if (availableItemsCount == 0 && isFocusControl ()) redraw ();
+}
+void onScrollHorizontal (Event event) {
+ ScrollBar hBar = getHorizontalBar ();
+ if (hBar == null) return;
+ int newSelection = hBar.getSelection ();
+ update ();
+ if (availableItemsCount > 0) {
+ GC gc = new GC (this);
+ gc.copyArea (
+ 0, 0,
+ clientArea.width, clientArea.height,
+ horizontalOffset - newSelection, 0);
+ gc.dispose ();
+ } else {
+ redraw (); /* ensure that static focus rectangle updates properly */
+ }
+ if (drawCount <= 0 && header.isVisible ()) {
+ header.update ();
+ Rectangle headerClientArea = header.getClientArea ();
+ GC gc = new GC (header);
+ gc.copyArea (
+ 0, 0,
+ headerClientArea.width, headerClientArea.height,
+ horizontalOffset - newSelection, 0);
+ gc.dispose ();
+ }
+ horizontalOffset = newSelection;
+}
+void onScrollVertical (Event event) {
+ ScrollBar vBar = getVerticalBar ();
+ if (vBar != null) {
+ int newSelection = vBar.getSelection ();
+ update ();
+ GC gc = new GC (this);
+ gc.copyArea (
+ 0, 0,
+ clientArea.width, clientArea.height,
+ 0, (topIndex - newSelection) * itemHeight);
+ gc.dispose ();
+ topIndex = newSelection;
+ }
+}
+void onSpace () {
+ if (focusItem == null) return;
+ if (!focusItem.isSelected ()) {
+ selectItem (focusItem, (style & SWT.MULTI) != 0);
+ redrawItem (focusItem.availableIndex, true);
+ }
+ if ((style & SWT.CHECK) != 0) {
+ focusItem.setChecked (!focusItem.checked);
+ }
+ showItem (focusItem);
+ Event event = new Event ();
+ event.item = focusItem;
+ postEvent (SWT.Selection, event);
+ if ((style & SWT.CHECK) == 0) return;
+
+ /* SWT.CHECK */
+ event = new Event ();
+ event.item = focusItem;
+ event.detail = SWT.CHECK;
+ postEvent (SWT.Selection, event);
+}
+/*
+ * The current focus item is about to become unavailable, so reassign focus.
+ */
+void reassignFocus () {
+ if (focusItem == null) return;
+
+ /* reassign to current focus' parent item if it has one */
+ if (focusItem.parentItem != null) {
+ TreeItem item = focusItem.parentItem;
+ setFocusItem (item, false);
+ showItem (item);
+ return;
+ }
+
+ /*
+ * reassign to the previous root-level item if there is one, or the next
+ * root-level item otherwise
+ */
+ int index = focusItem.getIndex ();
+ if (index != 0) {
+ index--;
+ } else {
+ index++;
+ }
+ if (index < items.length) {
+ TreeItem item = items [index];
+ setFocusItem (item, false);
+ showItem (item);
+ } else {
+ setFocusItem (null, false); /* no items left */
+ }
+}
+public void redraw () {
+ checkWidget ();
+ if (drawCount <= 0) super.redraw ();
+}
+public void redraw (int x, int y, int width, int height, boolean all) {
+ checkWidget ();
+ if (drawCount <= 0) super.redraw (x, y, width, height, all);
+}
+/*
+ * Redraws from the specified index down to the last available item inclusive. Note
+ * that the redraw bounds do not extend beyond the current last item, so clients
+ * that reduce the number of available items should use #redrawItems(int,int) instead
+ * to ensure that redrawing extends down to the previous bottom item boundary.
+ */
+void redrawFromItemDownwards (int index) {
+ redrawItems (index, availableItemsCount - 1, false);
+}
+/*
+ * Redraws the tree item at the specified index. It is valid for this index to reside
+ * beyond the last available item.
+ */
+void redrawItem (int itemIndex, boolean focusBoundsOnly) {
+ if (itemIndex == -1) return;
+ if (itemIndex < availableItemsCount && !availableItems [itemIndex].isInViewport ()) return;
+ redrawItems (itemIndex, itemIndex, focusBoundsOnly);
+}
+/*
+ * Redraws the tree between the start and end item indices inclusive. It is valid
+ * for the end index value to extend beyond the last available item.
+ */
+void redrawItems (int startIndex, int endIndex, boolean focusBoundsOnly) {
+ if (drawCount > 0) return;
+
+ int startY = (startIndex - topIndex) * itemHeight + getHeaderHeight ();
+ int height = (endIndex - startIndex + 1) * itemHeight;
+ if (focusBoundsOnly) {
+ boolean custom = hooks (SWT.EraseItem) || hooks (SWT.PaintItem);
+ if (!custom && columns.length > 0) {
+ TreeColumn lastColumn;
+ if ((style & SWT.FULL_SELECTION) != 0) {
+ TreeColumn[] orderedColumns = getOrderedColumns ();
+ lastColumn = orderedColumns [orderedColumns.length - 1];
+ } else {
+ lastColumn = columns [0];
+ }
+ int rightX = lastColumn.getX () + lastColumn.getWidth ();
+ if (rightX <= 0) return; /* focus column(s) not visible */
+ }
+ endIndex = Math.min (endIndex, availableItemsCount - 1);
+ for (int i = startIndex; i <= endIndex; i++) {
+ TreeItem item = availableItems [i];
+ if (item.isInViewport ()) {
+ /* if custom painting is being done then repaint the full item */
+ if (custom) {
+ redraw (0, getItemY (item), clientArea.width, itemHeight, false);
+ } else {
+ /* repaint the item's focus bounds */
+ Rectangle bounds = item.getFocusBounds ();
+ redraw (bounds.x, startY, bounds.width, height, false);
+ }
+ }
+ }
+ } else {
+ redraw (0, startY, clientArea.width, height, false);
+ }
+}
+/**
+ * Removes all of the items from the receiver.
+ *
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public void removeAll () {
+ checkWidget ();
+ if (items.length == 0) return;
+ setRedraw (false);
+
+ setFocusItem (null, false);
+ for (int i = 0; i < items.length; i++) {
+ items [i].dispose (false);
+ }
+ items = availableItems = selectedItems = NO_ITEMS;
+ availableItemsCount = topIndex = 0;
+ anchorItem = lastClickedItem = insertMarkItem = null;
+ lastSelectionEvent = null;
+ inExpand = false;
+ ScrollBar vBar = getVerticalBar ();
+ if (vBar != null) {
+ vBar.setMaximum (1);
+ vBar.setVisible (false);
+ }
+ if (columns.length == 0) {
+ horizontalOffset = 0;
+ ScrollBar hBar = getHorizontalBar ();
+ if (hBar != null) {
+ hBar.setMaximum (1);
+ hBar.setVisible (false);
+ }
+ }
+
+ setRedraw (true);
+}
+String removeMnemonics (String string) {
+ /* removes single ampersands and preserves double-ampersands */
+ char [] chars = new char [string.length ()];
+ string.getChars (0, chars.length, chars, 0);
+ int i = 0, j = 0;
+ for ( ; i < chars.length; i++, j++) {
+ if (chars[i] == '&') {
+ if (++i == chars.length) break;
+ if (chars[i] == '&') {
+ chars[j++] = chars[i - 1];
+ }
+ }
+ chars[j] = chars[i];
+ }
+ if (i == j) return string;
+ return new String (chars, 0, j);
+}
+void removeSelectedItem (int index) {
+ TreeItem[] newSelectedItems = new TreeItem [selectedItems.length - 1];
+ System.arraycopy (selectedItems, 0, newSelectedItems, 0, index);
+ System.arraycopy (selectedItems, index + 1, newSelectedItems, index, newSelectedItems.length - index);
+ selectedItems = newSelectedItems;
+}
+/**
+ * Removes the listener from the collection of listeners who will
+ * be notified when the user changes the receiver's selection.
+ *
+ * @param listener the listener which should no longer be notified
+ *
+ * @exception IllegalArgumentException <ul>
+ * <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
+ * </ul>
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @see SelectionListener
+ * @see #addSelectionListener
+ */
+public void removeSelectionListener (SelectionListener listener) {
+ checkWidget ();
+ if (listener == null) error (SWT.ERROR_NULL_ARGUMENT);
+ removeListener (SWT.Selection, listener);
+ removeListener (SWT.DefaultSelection, listener);
+}
+/**
+ * Removes the listener from the collection of listeners who will
+ * be notified when items in the receiver are expanded or collapsed.
+ *
+ * @param listener the listener which should no longer be notified
+ *
+ * @exception IllegalArgumentException <ul>
+ * <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
+ * </ul>
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @see TreeListener
+ * @see #addTreeListener
+ */
+public void removeTreeListener (TreeListener listener) {
+ checkWidget ();
+ if (listener == null) error (SWT.ERROR_NULL_ARGUMENT);
+ removeListener (SWT.Expand, listener);
+ removeListener (SWT.Collapse, listener);
+}
+/**
+ * Selects an item in the receiver. If the item was already
+ * selected, it remains selected.
+ *
+ * @param item the item to be selected
+ *
+ * @exception IllegalArgumentException <ul>
+ * <li>ERROR_NULL_ARGUMENT - if the item is null</li>
+ * <li>ERROR_INVALID_ARGUMENT - if the item has been disposed</li>
+ * </ul>
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @since 3.4
+ */
+public void select (TreeItem item) {
+ checkWidget ();
+ if (item == null) error (SWT.ERROR_NULL_ARGUMENT);
+ if (item.isDisposed ()) error (SWT.ERROR_INVALID_ARGUMENT);
+ selectItem (item, (style & SWT.MULTI) != 0);
+ redrawItem (item.availableIndex, true);
+}
+/**
+ * Selects all of the items in the receiver.
+ * <p>
+ * If the receiver is single-select, do nothing.
+ * </p>
+ *
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public void selectAll () {
+ checkWidget ();
+ if ((style & SWT.SINGLE) != 0) return;
+ selectedItems = new TreeItem [availableItemsCount];
+ System.arraycopy (availableItems, 0, selectedItems, 0, availableItemsCount);
+ redraw ();
+}
+void selectItem (TreeItem item, boolean addToSelection) {
+ TreeItem[] oldSelectedItems = selectedItems;
+ if (!addToSelection || (style & SWT.SINGLE) != 0) {
+ selectedItems = new TreeItem[] {item};
+ for (int i = 0; i < oldSelectedItems.length; i++) {
+ if (oldSelectedItems [i] != item) {
+ redrawItem (oldSelectedItems [i].availableIndex, true);
+ }
+ }
+ } else {
+ if (item.isSelected ()) return;
+ selectedItems = new TreeItem [selectedItems.length + 1];
+ System.arraycopy (oldSelectedItems, 0, selectedItems, 0, oldSelectedItems.length);
+ selectedItems [selectedItems.length - 1] = item;
+ }
+}
+public void setBackground (Color color) {
+ checkWidget ();
+ if (color == null) color = display.getSystemColor (SWT.COLOR_LIST_BACKGROUND);
+ super.setBackground (color);
+}
+/**
+ * Sets the order that the items in the receiver should
+ * be displayed in to the given argument which is described
+ * in terms of the zero-relative ordering of when the items
+ * were added.
+ *
+ * @param order the new order to display the items
+ *
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ * @exception IllegalArgumentException <ul>
+ * <li>ERROR_NULL_ARGUMENT - if the item order is null</li>
+ * <li>ERROR_INVALID_ARGUMENT - if the item order is not the same length as the number of items</li>
+ * </ul>
+ *
+ * @see Tree#getColumnOrder()
+ * @see TreeColumn#getMoveable()
+ * @see TreeColumn#setMoveable(boolean)
+ * @see SWT#Move
+ *
+ * @since 3.2
+ */
+public void setColumnOrder (int [] order) {
+ checkWidget ();
+ if (order == null) error (SWT.ERROR_NULL_ARGUMENT);
+ if (columns.length == 0) {
+ if (order.length != 0) error (SWT.ERROR_INVALID_ARGUMENT);
+ return;
+ }
+ if (order.length != columns.length) error (SWT.ERROR_INVALID_ARGUMENT);
+ boolean reorder = false;
+ boolean [] seen = new boolean [columns.length];
+ int[] oldOrder = getColumnOrder ();
+ for (int i = 0; i < order.length; i++) {
+ int index = order [i];
+ if (index < 0 || index >= columns.length) error (SWT.ERROR_INVALID_RANGE);
+ if (seen [index]) error (SWT.ERROR_INVALID_ARGUMENT);
+ seen [index] = true;
+ if (index != oldOrder [i]) reorder = true;
+ }
+ if (!reorder) return;
+
+ headerHideToolTip ();
+ int[] oldX = new int [columns.length];
+ for (int i = 0; i < columns.length; i++) {
+ oldX [i] = columns [i].getX ();
+ }
+ orderedColumns = new TreeColumn [order.length];
+ for (int i = 0; i < order.length; i++) {
+ orderedColumns [i] = columns [order [i]];
+ }
+ /*
+ * If the first ordered column has changed then the old and new ordered column 0's
+ * have to recompute their display texts since they will now have just gained/lost
+ * space as a result of the hierarchy decorations that appear in ordered column 0.
+ */
+ if (oldOrder [0] != order [0]) {
+ orderedCol0imageWidth = columns [order [0]].itemImageWidth;
+ GC gc = new GC (this);
+ for (int i = 0; i < items.length; i++) {
+ items [i].updateColumnWidth (columns [oldOrder [0]], gc);
+ items [i].updateColumnWidth (columns [order [0]], gc);
+ }
+ gc.dispose();
+ }
+ for (int i = 0; i < orderedColumns.length; i++) {
+ TreeColumn column = orderedColumns [i];
+ if (!column.isDisposed () && column.getX () != oldX [column.getIndex ()]) {
+ column.sendEvent (SWT.Move);
+ }
+ }
+
+ redraw ();
+ if (drawCount <= 0 && header.isVisible ()) header.redraw ();
+}
+void setFocusItem (TreeItem item, boolean redrawOldFocus) {
+ if (item == focusItem) return;
+ TreeItem oldFocusItem = focusItem;
+ focusItem = item;
+ if (redrawOldFocus && oldFocusItem != null) {
+ redrawItem (oldFocusItem.availableIndex, true);
+ }
+}
+public void setFont (Font value) {
+ checkWidget ();
+ Font oldFont = getFont ();
+ super.setFont (value);
+ Font font = getFont ();
+ if (font.equals (oldFont)) return;
+
+ GC gc = new GC (this);
+
+ /* recompute the receiver's cached font height and item height values */
+ fontHeight = gc.getFontMetrics ().getHeight ();
+ setItemHeight (Math.max (fontHeight, imageHeight) + 2 * getCellPadding ());
+ Point headerSize = header.getSize ();
+ int newHeaderHeight = Math.max (fontHeight, headerImageHeight) + 2 * getHeaderPadding ();
+ if (headerSize.y != newHeaderHeight) {
+ header.setSize (headerSize.x, newHeaderHeight);
+ }
+ header.setFont (font);
+
+ /*
+ * Notify all columns and items of the font change so that elements that
+ * use the receiver's font can recompute their cached string widths.
+ */
+ for (int i = 0; i < columns.length; i++) {
+ columns [i].updateFont (gc);
+ }
+ for (int i = 0; i < items.length; i++) {
+ items [i].updateFont (gc);
+ }
+
+ gc.dispose ();
+
+ if (drawCount <= 0 && header.isVisible ()) header.redraw ();
+
+ /* update scrollbars */
+ if (columns.length == 0) updateHorizontalBar ();
+ ScrollBar vBar = getVerticalBar ();
+ if (vBar != null) {
+ int thumb = (clientArea.height - getHeaderHeight ()) / itemHeight;
+ if (vBar.getThumb () != thumb) {
+ vBar.setThumb (thumb);
+ vBar.setPageIncrement (thumb);
+ }
+ topIndex = vBar.getSelection ();
+ vBar.setVisible (thumb < vBar.getMaximum ());
+ }
+ redraw ();
+}
+public void setForeground (Color color) {
+ checkWidget ();
+ if (color == null) color = display.getSystemColor (SWT.COLOR_LIST_FOREGROUND);
+ super.setForeground (color);
+}
+void setHeaderImageHeight (int value) {
+ headerImageHeight = value;
+ Point headerSize = header.getSize ();
+ int newHeaderHeight = Math.max (fontHeight, headerImageHeight) + 2 * getHeaderPadding ();
+ if (headerSize.y != newHeaderHeight) {
+ header.setSize (headerSize.x, newHeaderHeight);
+ }
+}
+/**
+ * Marks the receiver's header as visible if the argument is <code>true</code>,
+ * and marks it invisible otherwise.
+ * <p>
+ * If one of the receiver's ancestors is not visible or some
+ * other condition makes the receiver not visible, marking
+ * it visible may not actually cause it to be displayed.
+ * </p>
+ *
+ * @param show the new visibility state
+ *
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @since 3.1
+ */
+public void setHeaderVisible (boolean value) {
+ checkWidget ();
+ if (header.getVisible () == value) return; /* no change */
+ headerHideToolTip ();
+ header.setVisible (value);
+ updateVerticalBar ();
+ redraw ();
+}
+void setImageHeight (int value) {
+ imageHeight = value;
+ setItemHeight (Math.max (fontHeight, imageHeight) + 2 * getCellPadding ());
+}
+/**
+ * Display a mark indicating the point at which an item will be inserted.
+ * The drop insert item has a visual hint to show where a dragged item
+ * will be inserted when dropped on the tree.
+ *
+ * @param item the insert item. Null will clear the insertion mark.
+ * @param before true places the insert mark above 'item'. false places
+ * the insert mark below 'item'.
+ *
+ * @exception IllegalArgumentException <ul>
+ * <li>ERROR_INVALID_ARGUMENT - if the item has been disposed</li>
+ * </ul>
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public void setInsertMark (TreeItem item, boolean before) {
+ checkWidget ();
+ if (item != null && item.isDisposed ()) error (SWT.ERROR_INVALID_ARGUMENT);
+ if (item != null && item.parent != this) return;
+ if (item == insertMarkItem && before == insertMarkPrecedes) return; /* no change */
+
+ TreeItem oldInsertItem = insertMarkItem;
+ insertMarkItem = item;
+ insertMarkPrecedes = before;
+ if (oldInsertItem != null && oldInsertItem.availableIndex != -1) {
+ redrawItem (oldInsertItem.availableIndex, true);
+ }
+ if (item != null && item != oldInsertItem && item.availableIndex != -1) {
+ redrawItem (item.availableIndex, true);
+ }
+}
+/**
+ * Sets the number of root-level items contained in the receiver.
+ *
+ * @param count the number of items
+ *
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @since 3.2
+ */
+public void setItemCount (int count) {
+ checkWidget ();
+ count = Math.max (0, count);
+ if (count == items.length) return;
+ int oldCount = availableItemsCount;
+ int redrawStart, redrawEnd;
+
+ /* if the new item count is less than the current count then remove all excess items from the end */
+ if (count < items.length) {
+ redrawStart = count > 0 ? items [count - 1].availableIndex : 0;
+ redrawEnd = availableItemsCount - 1;
+ availableItemsCount = items [count].availableIndex;
+ for (int i = count; i < items.length; i++) {
+ items [i].dispose (false);
+ }
+ if (count == 0) {
+ items = Tree.NO_ITEMS;
+ } else {
+ TreeItem[] newItems = new TreeItem [count];
+ System.arraycopy (items, 0, newItems, 0, count);
+ items = newItems;
+ }
+
+ int newSelectedCount = 0;
+ for (int i = 0; i < selectedItems.length; i++) {
+ if (!selectedItems [i].isDisposed ()) newSelectedCount++;
+ }
+ if (newSelectedCount != selectedItems.length) {
+ /* one or more selected items have been disposed */
+ TreeItem[] newSelectedItems = new TreeItem [newSelectedCount];
+ int pos = 0;
+ for (int i = 0; i < selectedItems.length; i++) {
+ TreeItem item = selectedItems [i];
+ if (!item.isDisposed ()) {
+ newSelectedItems [pos++] = item;
+ }
+ }
+ selectedItems = newSelectedItems;
+ }
+
+ if (insertMarkItem != null && insertMarkItem.isDisposed ()) insertMarkItem = null;
+ if (lastClickedItem != null && lastClickedItem.isDisposed ()) lastClickedItem = null;
+ if (anchorItem != null && anchorItem.isDisposed ()) anchorItem = null;
+ if (focusItem != null && focusItem.isDisposed ()) {
+ TreeItem newFocusItem = count > 0 ? items [count - 1] : null;
+ setFocusItem (newFocusItem, false);
+ }
+ if (columns.length == 0) updateHorizontalBar ();
+ } else {
+ int grow = count - items.length;
+ redrawStart = items.length == 0 ? 0 : items [items.length - 1].availableIndex;
+ redrawEnd = availableItemsCount + grow - 1;
+ TreeItem[] newItems = new TreeItem [count];
+ System.arraycopy (items, 0, newItems, 0, items.length);
+ items = newItems;
+ if (availableItems.length < availableItemsCount + grow) {
+ TreeItem[] newAvailableItems = new TreeItem [availableItemsCount + grow];
+ System.arraycopy (availableItems, 0, newAvailableItems, 0, availableItemsCount);
+ availableItems = newAvailableItems;
+ }
+ for (int i = items.length - grow; i < count; i++) {
+ TreeItem newItem = new TreeItem (this, SWT.NONE, i, false);
+ items [i] = newItem;
+ items [i].availableIndex = availableItemsCount;
+ availableItems [availableItemsCount++] = newItem;
+ }
+ if (oldCount == 0) focusItem = availableItems [0];
+ }
+
+ updateVerticalBar ();
+ /*
+ * If this is the focus control and the available item count is going from 0->!0 or !0->0
+ * then the receiver must be redrawn to ensure that its boundary focus ring is updated.
+ */
+ if ((oldCount == 0 || availableItemsCount == 0) && isFocusControl ()) {
+ redraw ();
+ return;
+ }
+ redrawItems (redrawStart, redrawEnd, false);
+}
+boolean setItemHeight (int value) {
+ boolean update = !customHeightSet || itemHeight < value;
+ if (update) itemHeight = value;
+ return update;
+}
+/**
+ * Marks the receiver's lines as visible if the argument is <code>true</code>,
+ * and marks it invisible otherwise. Note that some platforms draw
+ * grid lines while others may draw alternating row colors.
+ * <p>
+ * If one of the receiver's ancestors is not visible or some
+ * other condition makes the receiver not visible, marking
+ * it visible may not actually cause it to be displayed.
+ * </p>
+ *
+ * @param show the new visibility state
+ *
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @since 3.1
+ */
+public void setLinesVisible (boolean value) {
+ checkWidget ();
+ if (linesVisible == value) return; /* no change */
+ linesVisible = value;
+ redraw ();
+}
+public void setMenu (Menu menu) {
+ super.setMenu (menu);
+ header.setMenu (menu);
+}
+public void setRedraw (boolean value) {
+ checkWidget();
+ if (value) {
+ if (--drawCount == 0) {
+ if (availableItems.length - availableItemsCount > 3) {
+ TreeItem[] newAvailableItems = new TreeItem [availableItemsCount];
+ System.arraycopy (availableItems, 0, newAvailableItems, 0, availableItemsCount);
+ availableItems = newAvailableItems;
+ }
+ updateVerticalBar ();
+ updateHorizontalBar ();
+ }
+ } else {
+ drawCount++;
+ }
+ super.setRedraw (value);
+ header.setRedraw (value);
+}
+/**
+ * Sets the receiver's selection to the given item.
+ * The current selection is cleared before the new item is selected.
+ * <p>
+ * If the item is not in the receiver, then it is ignored.
+ * </p>
+ *
+ * @param item the item to select
+ *
+ * @exception IllegalArgumentException <ul>
+ * <li>ERROR_NULL_ARGUMENT - if the item is null</li>
+ * <li>ERROR_INVALID_ARGUMENT - if the item has been disposed</li>
+ * </ul>
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @since 3.2
+ */
+public void setSelection (TreeItem item) {
+ checkWidget ();
+ if (item == null) error (SWT.ERROR_NULL_ARGUMENT);
+ setSelection (new TreeItem [] {item}, true);
+}
+/**
+ * Sets the receiver's selection to be the given array of items.
+ * The current selection is cleared before the new items are selected.
+ * <p>
+ * Items that are not in the receiver are ignored.
+ * If the receiver is single-select and multiple items are specified,
+ * then all items are ignored.
+ * </p>
+ *
+ * @param items the array of items
+ *
+ * @exception IllegalArgumentException <ul>
+ * <li>ERROR_NULL_ARGUMENT - if the array of items is null</li>
+ * <li>ERROR_INVALID_ARGUMENT - if one of the items has been disposed</li>
+ * </ul>
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @see Tree#deselectAll()
+ */
+public void setSelection (TreeItem[] items) {
+ checkWidget ();
+ if (items == null) error (SWT.ERROR_NULL_ARGUMENT);
+ setSelection (items, true);
+}
+
+void setSelection (TreeItem[] items, boolean updateViewport) {
+ if (items.length == 0 || ((style & SWT.SINGLE) != 0 && items.length > 1)) {
+ deselectAll ();
+ return;
+ }
+ TreeItem[] oldSelection = selectedItems;
+
+ /* remove null and duplicate items */
+ int index = 0;
+ selectedItems = new TreeItem [items.length]; /* initially assume all valid items */
+ for (int i = 0; i < items.length; i++) {
+ TreeItem item = items [i];
+ if (item != null && item.parent == this && !item.isSelected ()) {
+ selectedItems [index++] = item;
+ }
+ }
+ if (index != items.length) {
+ /* an invalid item was provided so resize the array accordingly */
+ TreeItem[] temp = new TreeItem [index];
+ System.arraycopy (selectedItems, 0, temp, 0, index);
+ selectedItems = temp;
+ }
+ if (selectedItems.length == 0) { /* no valid items */
+ deselectAll ();
+ return;
+ }
+
+ for (int i = 0; i < oldSelection.length; i++) {
+ if (!oldSelection [i].isSelected ()) {
+ int availableIndex = oldSelection [i].availableIndex;
+ if (availableIndex != -1) {
+ redrawItem (availableIndex, true);
+ }
+ }
+ }
+ if (updateViewport) {
+ showItem (selectedItems [0]);
+ setFocusItem (selectedItems [0], true);
+ }
+ for (int i = 0; i < selectedItems.length; i++) {
+ int availableIndex = selectedItems [i].availableIndex;
+ if (availableIndex != -1) {
+ redrawItem (availableIndex, true);
+ }
+ }
+}
+/**
+ * Sets the column used by the sort indicator for the receiver. A null
+ * value will clear the sort indicator. The current sort column is cleared
+ * before the new column is set.
+ *
+ * @param column the column used by the sort indicator or <code>null</code>
+ *
+ * @exception IllegalArgumentException <ul>
+ * <li>ERROR_INVALID_ARGUMENT - if the column is disposed</li>
+ * </ul>
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @since 3.2
+ */
+public void setSortColumn (TreeColumn column) {
+ checkWidget ();
+ if (column != null && column.isDisposed ()) error (SWT.ERROR_INVALID_ARGUMENT);
+ if (column == sortColumn) return;
+ if (sortColumn != null && !sortColumn.isDisposed ()) {
+ sortColumn.setSortDirection (SWT.NONE);
+ }
+ sortColumn = column;
+ if (sortColumn != null) {
+ sortColumn.setSortDirection (sortDirection);
+ }
+}
+/**
+ * Sets the direction of the sort indicator for the receiver. The value
+ * can be one of <code>UP</code>, <code>DOWN</code> or <code>NONE</code>.
+ *
+ * @param direction the direction of the sort indicator
+ *
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @since 3.2
+ */
+public void setSortDirection (int direction) {
+ checkWidget ();
+ if (direction != SWT.UP && direction != SWT.DOWN && direction != SWT.NONE) return;
+ sortDirection = direction;
+ if (sortColumn == null || sortColumn.isDisposed ()) return;
+ sortColumn.setSortDirection (sortDirection);
+}
+/**
+ * Sets the item which is currently at the top of the receiver.
+ * This item can change when items are expanded, collapsed, scrolled
+ * or new items are added or removed.
+ *
+ * @param item the item to be shown
+ *
+ * @exception IllegalArgumentException <ul>
+ * <li>ERROR_NULL_ARGUMENT - if the item is null</li>
+ * <li>ERROR_INVALID_ARGUMENT - if the item has been disposed</li>
+ * </ul>
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @see Tree#getTopItem()
+ *
+ * @since 2.1
+ */
+public void setTopItem (TreeItem item) {
+ checkWidget ();
+ if (item == null) error (SWT.ERROR_NULL_ARGUMENT);
+ if (item.isDisposed ()) error (SWT.ERROR_INVALID_ARGUMENT);
+ if (item.parent != this) return;
+
+ /* item must be available */
+ if (!item.isAvailable ()) item.parentItem.expandAncestors ();
+
+ int visibleItemCount = (clientArea.height - getHeaderHeight ()) / itemHeight;
+ if (availableItemsCount < visibleItemCount) return;
+ int index = Math.min (item.availableIndex, availableItemsCount - visibleItemCount);
+ if (topIndex == index) return;
+
+ update ();
+ int change = topIndex - index;
+ topIndex = index;
+ ScrollBar vBar = getVerticalBar ();
+ if (vBar != null) vBar.setSelection (topIndex);
+ if (drawCount <= 0) {
+ GC gc = new GC (this);
+ gc.copyArea (0, 0, clientArea.width, clientArea.height, 0, change * itemHeight);
+ gc.dispose ();
+ }
+}
+/**
+ * Shows the column. If the column is already showing in the receiver,
+ * this method simply returns. Otherwise, the columns are scrolled until
+ * the column is visible.
+ *
+ * @param column the column to be shown
+ *
+ * @exception IllegalArgumentException <ul>
+ * <li>ERROR_NULL_ARGUMENT - if the item is null</li>
+ * <li>ERROR_INVALID_ARGUMENT - if the item has been disposed</li>
+ * </ul>
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @since 3.1
+ */
+public void showColumn (TreeColumn column) {
+ checkWidget ();
+ if (column == null) error (SWT.ERROR_NULL_ARGUMENT);
+ if (column.isDisposed ()) error(SWT.ERROR_INVALID_ARGUMENT);
+ if (column.parent != this) return;
+
+ int x = column.getX ();
+ int rightX = x + column.width;
+ if (0 <= x && rightX <= clientArea.width) return; /* column is fully visible */
+
+ headerHideToolTip ();
+ int absX = 0; /* the X of the column irrespective of the horizontal scroll */
+ TreeColumn[] orderedColumns = getOrderedColumns ();
+ for (int i = 0; i < column.getOrderIndex (); i++) {
+ absX += orderedColumns [i].width;
+ }
+ if (x < clientArea.x) { /* column is to left of viewport */
+ horizontalOffset = absX;
+ } else {
+ horizontalOffset = absX + column.width - clientArea.width;
+ }
+ ScrollBar hBar = getHorizontalBar ();
+ if (hBar != null) hBar.setSelection (horizontalOffset);
+ redraw ();
+ if (drawCount <= 0 && header.isVisible ()) header.redraw ();
+}
+/**
+ * Shows the item. If the item is already showing in the receiver,
+ * this method simply returns. Otherwise, the items are scrolled
+ * and expanded until the item is visible.
+ *
+ * @param item the item to be shown
+ *
+ * @exception IllegalArgumentException <ul>
+ * <li>ERROR_NULL_ARGUMENT - if the item is null</li>
+ * <li>ERROR_INVALID_ARGUMENT - if the item has been disposed</li>
+ * </ul>
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @see Tree#showSelection()
+ */
+public void showItem (TreeItem item) {
+ checkWidget ();
+ if (item == null) error (SWT.ERROR_NULL_ARGUMENT);
+ if (item.isDisposed ()) error (SWT.ERROR_INVALID_ARGUMENT);
+ if (item.parent != this) return;
+
+ /* item must be available */
+ if (!item.isAvailable ()) item.parentItem.expandAncestors ();
+
+ int index = item.availableIndex;
+ int visibleItemCount = (clientArea.height - getHeaderHeight ()) / itemHeight;
+ /* nothing to do if item is already in viewport */
+ if (topIndex <= index && index < topIndex + visibleItemCount) return;
+
+ if (index <= topIndex) {
+ /* item is above current viewport, so show on top */
+ setTopItem (item);
+ } else {
+ /* item is below current viewport, so show on bottom */
+ visibleItemCount = Math.max (visibleItemCount, 1); /* item to show should be top item */
+ setTopItem (availableItems [Math.min (index - visibleItemCount + 1, availableItemsCount - 1)]);
+ }
+}
+/**
+ * Shows the selection. If the selection is already showing in the receiver,
+ * this method simply returns. Otherwise, the items are scrolled until
+ * the selection is visible.
+ *
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @see Tree#showItem(TreeItem)
+ */
+public void showSelection () {
+ checkWidget ();
+ if (selectedItems.length == 0) return;
+ showItem (selectedItems [0]);
+}
+void updateColumnWidth (TreeColumn column, int width) {
+ headerHideToolTip ();
+ int oldWidth = column.width;
+ int columnX = column.getX ();
+ int x = columnX + oldWidth - 1; /* -1 ensures that grid line is included */
+
+ update ();
+ GC gc = new GC (this);
+ gc.copyArea (x, 0, clientArea.width - x, clientArea.height, columnX + width - 1, 0); /* dest x -1 offsets x's -1 above */
+ if (width > oldWidth) {
+ /* column width grew */
+ int change = width - oldWidth + 1; /* +1 offsets x's -1 above */
+ /* -1/+1 below ensure that right bound of selection redraws correctly in column */
+ redraw (x - 1, 0, change + 1, clientArea.height, false);
+ } else {
+ int change = oldWidth - width + 1; /* +1 offsets x's -1 above */
+ redraw (clientArea.width - change, 0, change, clientArea.height, false);
+ }
+ /* the focus box must be repainted because its stipple may become shifted as a result of its new width */
+ if (focusItem != null) redrawItem (focusItem.availableIndex, true);
+
+ GC headerGC = new GC (header);
+ if (drawCount <= 0 && header.getVisible ()) {
+ Rectangle headerBounds = header.getClientArea ();
+ header.update ();
+ x -= 1; /* -1 ensures that full header column separator is included */
+ headerGC.copyArea (x, 0, headerBounds.width - x, headerBounds.height, columnX + width - 2, 0); /* dest x -2 offsets x's -1s above */
+ if (width > oldWidth) {
+ /* column width grew */
+ int change = width - oldWidth + 2; /* +2 offsets x's -1s above */
+ header.redraw (x, 0, change, headerBounds.height, false);
+ } else {
+ int change = oldWidth - width + 2; /* +2 offsets x's -1s above */
+ header.redraw (headerBounds.width - change, 0, change, headerBounds.height, false);
+ }
+ }
+
+ column.width = width;
+
+ /*
+ * Notify column and all items of column width change so that display labels
+ * can be recomputed if needed.
+ */
+ column.updateWidth (headerGC);
+ headerGC.dispose ();
+ for (int i = 0; i < items.length; i++) {
+ items [i].updateColumnWidth (column, gc);
+ }
+ gc.dispose ();
+
+ int maximum = 0;
+ for (int i = 0; i < columns.length; i++) {
+ maximum += columns [i].width;
+ }
+ ScrollBar hBar = getHorizontalBar ();
+ if (hBar != null) {
+ hBar.setMaximum (Math.max (1, maximum)); /* setting a value of 0 here is ignored */
+ if (hBar.getThumb () != clientArea.width) {
+ hBar.setThumb (clientArea.width);
+ hBar.setPageIncrement (clientArea.width);
+ }
+ int oldHorizontalOffset = horizontalOffset; /* hBar.setVisible() can modify horizontalOffset */
+ hBar.setVisible (clientArea.width < maximum);
+ int selection = hBar.getSelection ();
+ if (selection != oldHorizontalOffset) {
+ horizontalOffset = selection;
+ redraw ();
+ if (drawCount <= 0 && header.getVisible ()) header.redraw ();
+ }
+ }
+
+ column.sendEvent (SWT.Resize);
+ TreeColumn[] orderedColumns = getOrderedColumns ();
+ for (int i = column.getOrderIndex () + 1; i < orderedColumns.length; i++) {
+ if (!orderedColumns [i].isDisposed ()) {
+ orderedColumns [i].sendEvent (SWT.Move);
+ }
+ }
+
+ if (availableItemsCount == 0) redraw (); /* ensure that static focus rectangle updates properly */
+}
+/*
+ * This is a naive implementation that computes the value from scratch.
+ */
+void updateHorizontalBar () {
+ if (drawCount > 0) return;
+ ScrollBar hBar = getHorizontalBar ();
+ if (hBar == null) return;
+
+ int maxX = 0;
+ if (columns.length > 0) {
+ for (int i = 0; i < columns.length; i++) {
+ maxX += columns [i].width;
+ }
+ } else {
+ for (int i = 0; i < availableItemsCount; i++) {
+ Rectangle itemBounds = availableItems [i].getCellBounds (0);
+ maxX = Math.max (maxX, itemBounds.x + itemBounds.width + horizontalOffset);
+ }
+ }
+
+ int clientWidth = clientArea.width;
+ if (maxX != hBar.getMaximum ()) {
+ hBar.setMaximum (Math.max (1, maxX)); /* setting a value of 0 here is ignored */
+ }
+ int thumb = Math.min (clientWidth, maxX);
+ if (thumb != hBar.getThumb ()) {
+ hBar.setThumb (thumb);
+ hBar.setPageIncrement (thumb);
+ }
+ hBar.setVisible (clientWidth < maxX);
+
+ /* reclaim any space now left on the right */
+ if (maxX < horizontalOffset + thumb) {
+ horizontalOffset = maxX - thumb;
+ hBar.setSelection (horizontalOffset);
+ redraw ();
+ } else {
+ int selection = hBar.getSelection ();
+ if (selection != horizontalOffset) {
+ horizontalOffset = selection;
+ redraw ();
+ }
+ }
+}
+/*
+ * Update the horizontal bar, if needed, in response to an item change (eg.- created,
+ * disposed, expanded, etc.). newRightX is the new rightmost X value of the item,
+ * and rightXchange is the change that led to the item's rightmost X value becoming
+ * newRightX (so oldRightX + rightXchange = newRightX)
+ */
+void updateHorizontalBar (int newRightX, int rightXchange) {
+ if (drawCount > 0) return;
+ ScrollBar hBar = getHorizontalBar ();
+ if (hBar == null) return;
+
+ newRightX += horizontalOffset;
+ int barMaximum = hBar.getMaximum ();
+ if (newRightX > barMaximum) { /* item has extended beyond previous maximum */
+ hBar.setMaximum (newRightX);
+ int clientAreaWidth = clientArea.width;
+ int thumb = Math.min (newRightX, clientAreaWidth);
+ if (hBar.getThumb () != thumb) {
+ hBar.setThumb (thumb);
+ hBar.setPageIncrement (thumb);
+ }
+ hBar.setVisible (clientAreaWidth <= newRightX);
+ return;
+ }
+
+ int previousRightX = newRightX - rightXchange;
+ if (previousRightX != barMaximum) {
+ /* this was not the rightmost item, so just check for client width change */
+ int clientAreaWidth = clientArea.width;
+ int thumb = Math.min (barMaximum, clientAreaWidth);
+ if (hBar.getThumb () != thumb) {
+ hBar.setThumb (thumb);
+ hBar.setPageIncrement (thumb);
+ }
+ hBar.setVisible (clientAreaWidth <= barMaximum);
+ return;
+ }
+ updateHorizontalBar (); /* must search for the new rightmost item */
+}
+void updateVerticalBar () {
+ if (drawCount > 0) return;
+ ScrollBar vBar = getVerticalBar ();
+ if (vBar == null) return;
+
+ int pageSize = (clientArea.height - getHeaderHeight ()) / itemHeight;
+ int maximum = Math.max (1, availableItemsCount);
+ if (maximum != vBar.getMaximum ()) {
+ vBar.setMaximum (maximum);
+ }
+ int thumb = Math.min (pageSize, maximum);
+ if (thumb != vBar.getThumb ()) {
+ vBar.setThumb (thumb);
+ vBar.setPageIncrement (thumb);
+ }
+ vBar.setVisible (pageSize < maximum);
+
+ /* reclaim any space now left on the bottom */
+ if (maximum < topIndex + thumb) {
+ topIndex = maximum - thumb;
+ vBar.setSelection (topIndex);
+ redraw ();
+ } else {
+ int selection = vBar.getSelection ();
+ if (selection != topIndex) {
+ topIndex = selection;
+ redraw ();
+ }
+ }
+}
+}
diff --git a/bundles/org.eclipse.swt/Eclipse SWT/emulated/treetable/org/eclipse/swt/widgets/TreeColumn.java b/bundles/org.eclipse.swt/Eclipse SWT/emulated/treetable/org/eclipse/swt/widgets/TreeColumn.java
new file mode 100644
index 0000000000..41d1715a87
--- /dev/null
+++ b/bundles/org.eclipse.swt/Eclipse SWT/emulated/treetable/org/eclipse/swt/widgets/TreeColumn.java
@@ -0,0 +1,738 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2009 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.swt.widgets;
+
+import org.eclipse.swt.*;
+import org.eclipse.swt.events.*;
+import org.eclipse.swt.graphics.*;
+
+/**
+ * Instances of this class represent a column in a tree widget.
+ * <p><dl>
+ * <dt><b>Styles:</b></dt>
+ * <dd>LEFT, RIGHT, CENTER</dd>
+ * <dt><b>Events:</b></dt>
+ * <dd> Move, Resize, Selection</dd>
+ * </dl>
+ * </p><p>
+ * Note: Only one of the styles LEFT, RIGHT and CENTER may be specified.
+ * </p><p>
+ * IMPORTANT: This class is <em>not</em> intended to be subclassed.
+ * </p>
+ *
+ * @see <a href="http://www.eclipse.org/swt/snippets/#tree">Tree, TreeItem, TreeColumn snippets</a>
+ * @see <a href="http://www.eclipse.org/swt/">Sample code and further information</a>
+ *
+ * @since 3.1
+ * @noextend This class is not intended to be subclassed by clients.
+ */
+public class TreeColumn extends Item {
+ Tree parent;
+ String displayText = "";
+ int width, itemImageWidth;
+ boolean moveable, resizable = true;
+ int sort = SWT.NONE;
+ String toolTipText;
+
+/**
+ * Constructs a new instance of this class given its parent
+ * (which must be a <code>Tree</code>) and a style value
+ * describing its behavior and appearance. The item is added
+ * to the end of the items maintained by its parent.
+ * <p>
+ * The style value is either one of the style constants defined in
+ * class <code>SWT</code> which is applicable to instances of this
+ * class, or must be built by <em>bitwise OR</em>'ing together
+ * (that is, using the <code>int</code> "|" operator) two or more
+ * of those <code>SWT</code> style constants. The class description
+ * lists the style constants that are applicable to the class.
+ * Style bits are also inherited from superclasses.
+ * </p>
+ *
+ * @param parent a composite control which will be the parent of the new instance (cannot be null)
+ * @param style the style of control to construct
+ *
+ * @exception IllegalArgumentException <ul>
+ * <li>ERROR_NULL_ARGUMENT - if the parent is null</li>
+ * </ul>
+ * @exception SWTException <ul>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the parent</li>
+ * <li>ERROR_INVALID_SUBCLASS - if this class is not an allowed subclass</li>
+ * </ul>
+ *
+ * @see SWT#LEFT
+ * @see SWT#RIGHT
+ * @see SWT#CENTER
+ * @see Widget#checkSubclass
+ * @see Widget#getStyle
+ */
+public TreeColumn (Tree parent, int style) {
+ this (parent, style, checkNull (parent).columns.length);
+}
+/**
+ * Constructs a new instance of this class given its parent
+ * (which must be a <code>Tree</code>), a style value
+ * describing its behavior and appearance, and the index
+ * at which to place it in the items maintained by its parent.
+ * <p>
+ * The style value is either one of the style constants defined in
+ * class <code>SWT</code> which is applicable to instances of this
+ * class, or must be built by <em>bitwise OR</em>'ing together
+ * (that is, using the <code>int</code> "|" operator) two or more
+ * of those <code>SWT</code> style constants. The class description
+ * lists the style constants that are applicable to the class.
+ * Style bits are also inherited from superclasses.
+ * </p>
+ * <p>
+ * Note that due to a restriction on some platforms, the first column
+ * is always left aligned.
+ * </p>
+ * @param parent a composite control which will be the parent of the new instance (cannot be null)
+ * @param style the style of control to construct
+ * @param index the zero-relative index to store the receiver in its parent
+ *
+ * @exception IllegalArgumentException <ul>
+ * <li>ERROR_NULL_ARGUMENT - if the parent is null</li>
+ * <li>ERROR_INVALID_RANGE - if the index is not between 0 and the number of elements in the parent (inclusive)</li>
+ * </ul>
+ * @exception SWTException <ul>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the parent</li>
+ * <li>ERROR_INVALID_SUBCLASS - if this class is not an allowed subclass</li>
+ * </ul>
+ *
+ * @see SWT#LEFT
+ * @see SWT#RIGHT
+ * @see SWT#CENTER
+ * @see Widget#checkSubclass
+ * @see Widget#getStyle
+ */
+public TreeColumn (Tree parent, int style, int index) {
+ super (parent, checkStyle (style), index);
+ if (!(0 <= index && index <= parent.columns.length)) error (SWT.ERROR_INVALID_RANGE);
+ this.parent = parent;
+ parent.createItem (this, index);
+}
+/**
+ * Adds the listener to the collection of listeners who will
+ * be notified when the control is moved or resized, by sending
+ * it one of the messages defined in the <code>ControlListener</code>
+ * interface.
+ *
+ * @param listener the listener which should be notified
+ *
+ * @exception IllegalArgumentException <ul>
+ * <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
+ * </ul>
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @see ControlListener
+ * @see #removeControlListener
+ */
+public void addControlListener (ControlListener listener) {
+ checkWidget ();
+ if (listener == null) error (SWT.ERROR_NULL_ARGUMENT);
+ TypedListener typedListener = new TypedListener (listener);
+ addListener (SWT.Resize, typedListener);
+ addListener (SWT.Move, typedListener);
+}
+/**
+ * Adds the listener to the collection of listeners who will
+ * be notified when the control is selected by the user, by sending
+ * it one of the messages defined in the <code>SelectionListener</code>
+ * interface.
+ * <p>
+ * <code>widgetSelected</code> is called when the column header is selected.
+ * <code>widgetDefaultSelected</code> is not called.
+ * </p>
+ *
+ * @param listener the listener which should be notified when the control is selected by the user
+ *
+ * @exception IllegalArgumentException <ul>
+ * <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
+ * </ul>
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @see SelectionListener
+ * @see #removeSelectionListener
+ * @see SelectionEvent
+ */
+public void addSelectionListener (SelectionListener listener) {
+ checkWidget ();
+ if (listener == null) error (SWT.ERROR_NULL_ARGUMENT);
+ TypedListener typedListener = new TypedListener (listener);
+ addListener (SWT.Selection, typedListener);
+ addListener (SWT.DefaultSelection, typedListener);
+}
+static Tree checkNull (Tree tree) {
+ if (tree == null) SWT.error (SWT.ERROR_NULL_ARGUMENT);
+ return tree;
+}
+static int checkStyle (int style) {
+ return checkBits (style, SWT.LEFT, SWT.CENTER, SWT.RIGHT, 0, 0, 0);
+}
+protected void checkSubclass () {
+ if (!isValidSubclass ()) error (SWT.ERROR_INVALID_SUBCLASS);
+}
+void computeDisplayText (GC gc) {
+ int availableWidth = width - 2 * parent.getHeaderPadding ();
+ if (image != null) {
+ availableWidth -= image.getBounds ().width;
+ availableWidth -= Tree.MARGIN_IMAGE;
+ }
+ if (sort != SWT.NONE) {
+ availableWidth -= parent.arrowBounds.width;
+ availableWidth -= Tree.MARGIN_IMAGE;
+ }
+ String text = this.text;
+ int textWidth = gc.textExtent (text, SWT.DRAW_MNEMONIC).x;
+ if (textWidth <= availableWidth) {
+ displayText = text;
+ return;
+ }
+
+ /* Ellipsis will be needed, so subtract their width from the available text width */
+ int ellipsisWidth = gc.stringExtent (Tree.ELLIPSIS).x;
+ availableWidth -= ellipsisWidth;
+ if (availableWidth <= 0) {
+ displayText = Tree.ELLIPSIS;
+ return;
+ }
+
+ /* Make initial guess. */
+ int index = Math.min (availableWidth / gc.getFontMetrics ().getAverageCharWidth (), text.length ());
+ textWidth = gc.textExtent (text.substring (0, index), SWT.DRAW_MNEMONIC).x;
+
+ /* Initial guess is correct. */
+ if (availableWidth == textWidth) {
+ displayText = text.substring (0, index) + Tree.ELLIPSIS;
+ return;
+ }
+
+ /* Initial guess is too high, so reduce until fit is found. */
+ if (availableWidth < textWidth) {
+ do {
+ index--;
+ if (index < 0) {
+ displayText = Tree.ELLIPSIS;
+ return;
+ }
+ text = text.substring (0, index);
+ textWidth = gc.textExtent (text, SWT.DRAW_MNEMONIC).x;
+ } while (availableWidth < textWidth);
+ displayText = text + Tree.ELLIPSIS;
+ return;
+ }
+
+ /* Initial guess is too low, so increase until overrun is found. */
+ while (textWidth < availableWidth) {
+ index++;
+ textWidth = gc.textExtent (text.substring (0, index), SWT.DRAW_MNEMONIC).x;
+ }
+ displayText = text.substring (0, index - 1) + Tree.ELLIPSIS;
+}
+public void dispose () {
+ if (isDisposed ()) return;
+ Rectangle parentBounds = parent.clientArea;
+ int x = getX ();
+ Tree parent = this.parent;
+ dispose (true);
+ int width = parentBounds.width - x;
+ parent.redraw (x, 0, width, parentBounds.height, false);
+ if (parent.drawCount <= 0 && parent.getHeaderVisible ()) {
+ parent.header.redraw (x, 0, width, parent.getHeaderHeight (), false);
+ }
+}
+void dispose (boolean notifyParent) {
+ super.dispose (); /* super is intentional here */
+ if (notifyParent) parent.destroyItem (this);
+ parent = null;
+}
+/**
+ * Returns a value which describes the position of the
+ * text or image in the receiver. The value will be one of
+ * <code>LEFT</code>, <code>RIGHT</code> or <code>CENTER</code>.
+ *
+ * @return the alignment
+ *
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public int getAlignment () {
+ checkWidget ();
+ if ((style & SWT.CENTER) != 0) return SWT.CENTER;
+ if ((style & SWT.RIGHT) != 0) return SWT.RIGHT;
+ return SWT.LEFT;
+}
+/*
+ * Returns the width of the header's content
+ * (image + text + sort arrow + internal margins)
+ */
+int getContentWidth (GC gc, boolean useDisplayText) {
+ int contentWidth = 0;
+ String text = useDisplayText ? displayText : this.text;
+ if (text.length () > 0) {
+ contentWidth += gc.textExtent (text, SWT.DRAW_MNEMONIC).x;
+ }
+ if (image != null) {
+ contentWidth += image.getBounds ().width;
+ if (text.length () > 0) contentWidth += Tree.MARGIN_IMAGE;
+ }
+ if (sort != SWT.NONE) {
+ contentWidth += parent.arrowBounds.width;
+ if (text.length () > 0 || image != null) {
+ contentWidth += Tree.MARGIN_IMAGE;
+ }
+ }
+ return contentWidth;
+}
+int getIndex () {
+ TreeColumn[] columns = parent.columns;
+ for (int i = 0; i < columns.length; i++) {
+ if (columns [i] == this) return i;
+ }
+ return -1;
+}
+/**
+ * Gets the moveable attribute. A column that is
+ * not moveable cannot be reordered by the user
+ * by dragging the header but may be reordered
+ * by the programmer.
+ *
+ * @return the moveable attribute
+ *
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @see Tree#getColumnOrder()
+ * @see Tree#setColumnOrder(int[])
+ * @see TreeColumn#setMoveable(boolean)
+ * @see SWT#Move
+ *
+ * @since 3.2
+ */
+public boolean getMoveable () {
+ checkWidget ();
+ return moveable;
+}
+int getOrderIndex () {
+ TreeColumn[] orderedColumns = parent.orderedColumns;
+ if (orderedColumns == null) return getIndex ();
+ for (int i = 0; i < orderedColumns.length; i++) {
+ if (orderedColumns [i] == this) return i;
+ }
+ return -1;
+}
+/**
+ * Returns the receiver's parent, which must be a <code>Tree</code>.
+ *
+ * @return the receiver's parent
+ *
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public Tree getParent () {
+ checkWidget ();
+ return parent;
+}
+int getPreferredWidth () {
+ if (!parent.getHeaderVisible ()) return 0;
+ GC gc = new GC (parent);
+ int result = getContentWidth (gc, false);
+ gc.dispose ();
+ return result + 2 * parent.getHeaderPadding ();
+}
+/**
+ * Gets the resizable attribute. A column that is
+ * not resizable cannot be dragged by the user but
+ * may be resized by the programmer.
+ *
+ * @return the resizable attribute
+ *
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public boolean getResizable () {
+ checkWidget ();
+ return resizable;
+}
+/**
+ * Returns the receiver's tool tip text, or null if it has
+ * not been set.
+ *
+ * @return the receiver's tool tip text
+ *
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @since 3.2
+ */
+public String getToolTipText () {
+ checkWidget ();
+ return toolTipText;
+}
+/**
+ * Gets the width of the receiver.
+ *
+ * @return the width
+ *
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public int getWidth () {
+ checkWidget ();
+ return width;
+}
+int getX () {
+ TreeColumn[] orderedColumns = parent.getOrderedColumns ();
+ int index = getOrderIndex ();
+ int result = -parent.horizontalOffset;
+ for (int i = 0; i < index; i++) {
+ result += orderedColumns [i].width;
+ }
+ return result;
+}
+void paint (GC gc) {
+ int padding = parent.getHeaderPadding ();
+
+ int x = getX ();
+ int startX = x + padding;
+ if (getOrderIndex () != 0 && (style & SWT.LEFT) == 0) {
+ int contentWidth = getContentWidth (gc, true);
+ if ((style & SWT.RIGHT) != 0) {
+ startX = Math.max (startX, x + width - padding - contentWidth);
+ } else { /* SWT.CENTER */
+ startX = Math.max (startX, x + (width - contentWidth) / 2);
+ }
+ }
+ int headerHeight = parent.getHeaderHeight ();
+
+ /* restrict the clipping region to the header cell */
+ gc.setClipping (
+ x + padding,
+ padding,
+ width - 2 * padding,
+ headerHeight - 2 * padding);
+
+ if (image != null) {
+ Rectangle imageBounds = image.getBounds ();
+ int drawHeight = Math.min (imageBounds.height, headerHeight - 2 * padding);
+ gc.drawImage (
+ image,
+ 0, 0,
+ imageBounds.width, imageBounds.height,
+ startX, (headerHeight - drawHeight) / 2,
+ imageBounds.width, drawHeight);
+ startX += imageBounds.width;
+ startX += Tree.MARGIN_IMAGE;
+ }
+ if (displayText.length () > 0) {
+ gc.setForeground (display.getSystemColor (SWT.COLOR_BLACK));
+ int fontHeight = parent.fontHeight;
+ gc.drawText (displayText, startX, (headerHeight - fontHeight) / 2, SWT.DRAW_MNEMONIC);
+ startX += gc.textExtent (displayText, SWT.DRAW_MNEMONIC).x + Tree.MARGIN_IMAGE;
+ }
+ if (sort != SWT.NONE) {
+ Image image = sort == SWT.DOWN ? parent.getArrowDownImage () : parent.getArrowUpImage ();
+ int y = (headerHeight - parent.arrowBounds.height) / 2;
+ gc.drawImage (image, startX, y);
+ }
+}
+/**
+ * Causes the receiver to be resized to its preferred size.
+ * For a composite, this involves computing the preferred size
+ * from its layout, if there is one.
+ *
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ */
+public void pack () {
+ checkWidget ();
+ TreeItem[] availableItems = parent.availableItems;
+ int index = getIndex ();
+ int newWidth = getPreferredWidth ();
+ for (int i = 0; i < parent.availableItemsCount; i++) {
+ int width = availableItems [i].getPreferredWidth (index);
+ /* ensure that receiver and parent were not disposed in a callback */
+ if (parent.isDisposed () || isDisposed ()) return;
+ if (!availableItems [i].isDisposed ()) {
+ newWidth = Math.max (newWidth, width);
+ }
+ }
+ if (newWidth != width) parent.updateColumnWidth (this, newWidth);
+}
+/**
+ * Removes the listener from the collection of listeners who will
+ * be notified when the control is moved or resized.
+ *
+ * @param listener the listener which should no longer be notified
+ *
+ * @exception IllegalArgumentException <ul>
+ * <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
+ * </ul>
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @see ControlListener
+ * @see #addControlListener
+ */
+public void removeControlListener (ControlListener listener) {
+ checkWidget ();
+ if (listener == null) error (SWT.ERROR_NULL_ARGUMENT);
+ if (eventTable == null) return;
+ eventTable.unhook (SWT.Move, listener);
+ eventTable.unhook (SWT.Resize, listener);
+}
+/**
+ * Removes the listener from the collection of listeners who will
+ * be notified when the control is selected by the user.
+ *
+ * @param listener the listener which should no longer be notified
+ *
+ * @exception IllegalArgumentException <ul>
+ * <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
+ * </ul>
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @see SelectionListener
+ * @see #addSelectionListener
+ */
+public void removeSelectionListener (SelectionListener listener) {
+ checkWidget ();
+ if (listener == null) error (SWT.ERROR_NULL_ARGUMENT);
+ removeListener (SWT.Selection, listener);
+ removeListener (SWT.DefaultSelection, listener);
+}
+/**
+ * Controls how text and images will be displayed in the receiver.
+ * The argument should be one of <code>LEFT</code>, <code>RIGHT</code>
+ * or <code>CENTER</code>.
+ * <p>
+ * Note that due to a restriction on some platforms, the first column
+ * is always left aligned.
+ * </p>
+ * @param alignment the new alignment
+ *
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public void setAlignment (int alignment) {
+ checkWidget ();
+ if ((alignment & (SWT.LEFT | SWT.RIGHT | SWT.CENTER)) == 0) return;
+ alignment = checkBits (alignment, SWT.LEFT, SWT.CENTER, SWT.RIGHT, 0, 0, 0);
+ if ((style & alignment) != 0) return; /* same value */
+ style &= ~(SWT.LEFT | SWT.CENTER | SWT.RIGHT);
+ style |= alignment;
+ if (getOrderIndex () == 0) return; /* no update needed since first ordered column appears left-aligned */
+ int x = getX ();
+ parent.redraw (x, 0, width, parent.clientArea.height, false);
+ if (parent.drawCount <= 0 && parent.getHeaderVisible ()) {
+ /* don't damage the header's drawn borders */
+ parent.header.redraw (x, 1, width - 2, parent.getHeaderHeight () - 3, false);
+ }
+}
+public void setImage (Image value) {
+ checkWidget ();
+ if (value == image) return;
+ if (value != null && value.equals (image)) return; /* same value */
+ super.setImage (value);
+
+ /* An image width change may affect the space available for the column's displayText. */
+ GC gc = new GC (parent);
+ computeDisplayText (gc);
+ gc.dispose ();
+
+ /*
+ * If this is the first image being put into the header then the header
+ * height may be adjusted, in which case a full redraw is needed.
+ */
+ if (parent.headerImageHeight == 0) {
+ int oldHeaderHeight = parent.getHeaderHeight ();
+ parent.setHeaderImageHeight (value.getBounds ().height);
+ if (oldHeaderHeight != parent.getHeaderHeight ()) {
+ /* parent header height changed */
+ if (parent.drawCount <= 0 && parent.getHeaderVisible ()) {
+ parent.header.redraw ();
+ }
+ parent.redraw ();
+ return;
+ }
+ }
+
+ if (parent.drawCount <= 0 && parent.getHeaderVisible ()) {
+ /* don't damage the header's drawn borders */
+ parent.header.redraw (getX (), 1, width - 2, parent.getHeaderHeight () - 3, false);
+ }
+}
+/**
+ * Sets the moveable attribute. A column that is
+ * moveable can be reordered by the user by dragging
+ * the header. A column that is not moveable cannot be
+ * dragged by the user but may be reordered
+ * by the programmer.
+ *
+ * @param moveable the moveable attribute
+ *
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @see Tree#setColumnOrder(int[])
+ * @see Tree#getColumnOrder()
+ * @see TreeColumn#getMoveable()
+ * @see SWT#Move
+ *
+ * @since 3.2
+ */
+public void setMoveable (boolean moveable) {
+ checkWidget ();
+ this.moveable = moveable;
+}
+/**
+ * Sets the resizable attribute. A column that is
+ * not resizable cannot be dragged by the user but
+ * may be resized by the programmer.
+ *
+ * @param resizable the resize attribute
+ *
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public void setResizable (boolean value) {
+ checkWidget ();
+ resizable = value;
+}
+void setSortDirection (int value) {
+ if (value == sort) return;
+ boolean widthChange = value == SWT.NONE || sort == SWT.NONE;
+ sort = value;
+ if (widthChange) {
+ /*
+ * adding/removing the sort arrow decreases/increases the width that is
+ * available for the column's header text, so recompute the display text
+ */
+ GC gc = new GC (parent);
+ computeDisplayText (gc);
+ gc.dispose ();
+ }
+ if (parent.drawCount <= 0 && parent.getHeaderVisible ()) {
+ /* don't damage the header's drawn borders */
+ parent.header.redraw (getX (), 1, width - 2, parent.getHeaderHeight () - 3, false);
+ }
+}
+public void setText (String value) {
+ checkWidget ();
+ if (value == null) error (SWT.ERROR_NULL_ARGUMENT);
+ if (value.equals (text)) return; /* same value */
+ super.setText (value);
+ GC gc = new GC (parent);
+ computeDisplayText (gc);
+ gc.dispose ();
+ if (parent.drawCount <= 0 && parent.getHeaderVisible ()) {
+ /* don't damage the header's drawn borders */
+ parent.header.redraw (getX (), 1, width - 2, parent.getHeaderHeight () - 3, false);
+ }
+}
+/**
+ * Sets the receiver's tool tip text to the argument, which
+ * may be null indicating that the default tool tip for the
+ * control will be shown. For a control that has a default
+ * tool tip, such as the Tree control on Windows, setting
+ * the tool tip text to an empty string replaces the default,
+ * causing no tool tip text to be shown.
+ * <p>
+ * The mnemonic indicator (character '&amp;') is not displayed in a tool tip.
+ * To display a single '&amp;' in the tool tip, the character '&amp;' can be
+ * escaped by doubling it in the string.
+ * </p>
+ *
+ * @param string the new tool tip text (or null)
+ *
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @since 3.2
+ */
+public void setToolTipText (String string) {
+ checkWidget ();
+ if (toolTipText == string) return;
+ if (toolTipText != null && toolTipText.equals (string)) return;
+ toolTipText = string;
+ if (parent.toolTipShell == null) return; /* tooltip not currently showing */
+ if (((Integer) parent.toolTipShell.getData ()).intValue () != getIndex ()) return; /* tooltip showing for different column */
+ parent.headerUpdateToolTip (getX () + (width / 2)); /* update the tooltip text */
+}
+/**
+ * Sets the width of the receiver.
+ *
+ * @param width the new width
+ *
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public void setWidth (int value) {
+ checkWidget ();
+ if (value < 0) return;
+ if (width == value) return; /* same value */
+ parent.updateColumnWidth (this, value);
+}
+/*
+ * Perform any internal changes necessary to reflect a changed width.
+ */
+void updateWidth (GC gc) {
+ String oldDisplayText = displayText;
+ computeDisplayText (gc);
+ /* the header must be damaged if the display text has changed or if the alignment is not LEFT */
+ if (parent.getHeaderVisible ()) {
+ if ((style & SWT.LEFT) == 0 || !oldDisplayText.equals (displayText)) {
+ int padding = parent.getHeaderPadding ();
+ parent.header.redraw (getX () + padding, 0, width - padding, parent.getHeaderHeight (), false);
+ }
+ }
+}
+void updateFont (GC gc) {
+ computeDisplayText (gc);
+}
+}
diff --git a/bundles/org.eclipse.swt/Eclipse SWT/emulated/treetable/org/eclipse/swt/widgets/TreeItem.java b/bundles/org.eclipse.swt/Eclipse SWT/emulated/treetable/org/eclipse/swt/widgets/TreeItem.java
new file mode 100644
index 0000000000..f762439f45
--- /dev/null
+++ b/bundles/org.eclipse.swt/Eclipse SWT/emulated/treetable/org/eclipse/swt/widgets/TreeItem.java
@@ -0,0 +1,2959 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2009 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.swt.widgets;
+
+import org.eclipse.swt.*;
+import org.eclipse.swt.graphics.*;
+import org.eclipse.swt.internal.Compatibility;
+
+/**
+ * Instances of this class represent a selectable user interface object
+ * that represents a hierarchy of tree items in a tree widget.
+ *
+ * <dl>
+ * <dt><b>Styles:</b></dt>
+ * <dd>(none)</dd>
+ * <dt><b>Events:</b></dt>
+ * <dd>(none)</dd>
+ * </dl>
+ * <p>
+ * IMPORTANT: This class is <em>not</em> intended to be subclassed.
+ * </p>
+ *
+ * @see <a href="http://www.eclipse.org/swt/snippets/#tree">Tree, TreeItem, TreeColumn snippets</a>
+ * @see <a href="http://www.eclipse.org/swt/">Sample code and further information</a>
+ * @noextend This class is not intended to be subclassed by clients.
+ */
+public class TreeItem extends Item {
+ Tree parent;
+ TreeItem parentItem;
+ TreeItem[] items = Tree.NO_ITEMS;
+ int availableIndex = -1; /* index in parent's flat list of available (though not necessarily within viewport) items */
+ int depth = 0; /* cached for performance, does not change after instantiation */
+ boolean checked, grayed, expanded, cached;
+
+ String[] texts;
+ int[] textWidths = new int [1]; /* cached string measurements */
+ int customWidth = -1; /* width specified by Measure callback */
+ int fontHeight; /* cached item font height */
+ int[] fontHeights;
+ Image[] images;
+ Color foreground, background;
+ String[] displayTexts;
+ Color[] cellForegrounds, cellBackgrounds;
+ Font font;
+ Font[] cellFonts;
+
+ static final int INDENT_HIERARCHY = 6; /* the margin between an item's expander and its checkbox or content */
+ static final int MARGIN_TEXT = 3; /* the left and right margins within the text's space */
+
+/**
+ * Constructs a new instance of this class given its parent
+ * (which must be a <code>Tree</code> or a <code>TreeItem</code>)
+ * and a style value describing its behavior and appearance.
+ * The item is added to the end of the items maintained by its parent.
+ * <p>
+ * The style value is either one of the style constants defined in
+ * class <code>SWT</code> which is applicable to instances of this
+ * class, or must be built by <em>bitwise OR</em>'ing together
+ * (that is, using the <code>int</code> "|" operator) two or more
+ * of those <code>SWT</code> style constants. The class description
+ * lists the style constants that are applicable to the class.
+ * Style bits are also inherited from superclasses.
+ * </p>
+ *
+ * @param parent a tree control which will be the parent of the new instance (cannot be null)
+ * @param style the style of control to construct
+ *
+ * @exception IllegalArgumentException <ul>
+ * <li>ERROR_NULL_ARGUMENT - if the parent is null</li>
+ * </ul>
+ * @exception SWTException <ul>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the parent</li>
+ * <li>ERROR_INVALID_SUBCLASS - if this class is not an allowed subclass</li>
+ * </ul>
+ *
+ * @see SWT
+ * @see Widget#checkSubclass
+ * @see Widget#getStyle
+ */
+public TreeItem (Tree parent, int style) {
+ this (parent, style, checkNull (parent).items.length);
+}
+/**
+ * Constructs a new instance of this class given its parent
+ * (which must be a <code>Tree</code> or a <code>TreeItem</code>),
+ * a style value describing its behavior and appearance, and the index
+ * at which to place it in the items maintained by its parent.
+ * <p>
+ * The style value is either one of the style constants defined in
+ * class <code>SWT</code> which is applicable to instances of this
+ * class, or must be built by <em>bitwise OR</em>'ing together
+ * (that is, using the <code>int</code> "|" operator) two or more
+ * of those <code>SWT</code> style constants. The class description
+ * lists the style constants that are applicable to the class.
+ * Style bits are also inherited from superclasses.
+ * </p>
+ *
+ * @param parent a tree control which will be the parent of the new instance (cannot be null)
+ * @param style the style of control to construct
+ * @param index the zero-relative index to store the receiver in its parent
+ *
+ * @exception IllegalArgumentException <ul>
+ * <li>ERROR_NULL_ARGUMENT - if the parent is null</li>
+ * <li>ERROR_INVALID_RANGE - if the index is not between 0 and the number of elements in the parent (inclusive)</li>
+ * </ul>
+ * @exception SWTException <ul>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the parent</li>
+ * <li>ERROR_INVALID_SUBCLASS - if this class is not an allowed subclass</li>
+ * </ul>
+ *
+ * @see SWT
+ * @see Widget#checkSubclass
+ * @see Widget#getStyle
+ */
+public TreeItem (Tree parent, int style, int index) {
+ this (parent, style, index, true);
+}
+/**
+ * Constructs a new instance of this class given its parent
+ * (which must be a <code>Tree</code> or a <code>TreeItem</code>)
+ * and a style value describing its behavior and appearance.
+ * The item is added to the end of the items maintained by its parent.
+ * <p>
+ * The style value is either one of the style constants defined in
+ * class <code>SWT</code> which is applicable to instances of this
+ * class, or must be built by <em>bitwise OR</em>'ing together
+ * (that is, using the <code>int</code> "|" operator) two or more
+ * of those <code>SWT</code> style constants. The class description
+ * lists the style constants that are applicable to the class.
+ * Style bits are also inherited from superclasses.
+ * </p>
+ *
+ * @param parentItem a tree control which will be the parent of the new instance (cannot be null)
+ * @param style the style of control to construct
+ *
+ * @exception IllegalArgumentException <ul>
+ * <li>ERROR_NULL_ARGUMENT - if the parent is null</li>
+ * </ul>
+ * @exception SWTException <ul>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the parent</li>
+ * <li>ERROR_INVALID_SUBCLASS - if this class is not an allowed subclass</li>
+ * </ul>
+ *
+ * @see SWT
+ * @see Widget#checkSubclass
+ * @see Widget#getStyle
+ */
+public TreeItem (TreeItem parentItem, int style) {
+ this (parentItem, style, checkNull (parentItem).items.length);
+}
+/**
+ * Constructs a new instance of this class given its parent
+ * (which must be a <code>Tree</code> or a <code>TreeItem</code>),
+ * a style value describing its behavior and appearance, and the index
+ * at which to place it in the items maintained by its parent.
+ * <p>
+ * The style value is either one of the style constants defined in
+ * class <code>SWT</code> which is applicable to instances of this
+ * class, or must be built by <em>bitwise OR</em>'ing together
+ * (that is, using the <code>int</code> "|" operator) two or more
+ * of those <code>SWT</code> style constants. The class description
+ * lists the style constants that are applicable to the class.
+ * Style bits are also inherited from superclasses.
+ * </p>
+ *
+ * @param parentItem a tree control which will be the parent of the new instance (cannot be null)
+ * @param style the style of control to construct
+ * @param index the zero-relative index to store the receiver in its parent
+ *
+ * @exception IllegalArgumentException <ul>
+ * <li>ERROR_NULL_ARGUMENT - if the parent is null</li>
+ * <li>ERROR_INVALID_RANGE - if the index is not between 0 and the number of elements in the parent (inclusive)</li>
+ * </ul>
+ * @exception SWTException <ul>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the parent</li>
+ * <li>ERROR_INVALID_SUBCLASS - if this class is not an allowed subclass</li>
+ * </ul>
+ *
+ * @see SWT
+ * @see Widget#checkSubclass
+ * @see Widget#getStyle
+ */
+public TreeItem (TreeItem parentItem, int style, int index) {
+ this (parentItem, style, index, true);
+}
+TreeItem (TreeItem parentItem, int style, int index, boolean notifyParent) {
+ super (parentItem, style);
+ this.parentItem = parentItem;
+ parent = parentItem.parent;
+ depth = parentItem.depth + 1;
+ int validItemIndex = parentItem.items.length;
+ if (!(0 <= index && index <= validItemIndex)) error (SWT.ERROR_INVALID_RANGE);
+ int columnCount = parent.columns.length;
+ if (columnCount > 0) {
+ displayTexts = new String [columnCount];
+ if (columnCount > 1) {
+ texts = new String [columnCount];
+ textWidths = new int [columnCount];
+ images = new Image [columnCount];
+ }
+ }
+ if (notifyParent) parentItem.addItem (this, index);
+}
+TreeItem (Tree parent, int style, int index, boolean notifyParent) {
+ super (parent, style);
+ int validItemIndex = parent.items.length;
+ if (!(0 <= index && index <= validItemIndex)) error (SWT.ERROR_INVALID_RANGE);
+ this.parent = parent;
+ int columnCount = parent.columns.length;
+ if (columnCount > 0) {
+ displayTexts = new String [columnCount];
+ if (columnCount > 1) {
+ texts = new String [columnCount];
+ textWidths = new int [columnCount];
+ images = new Image [columnCount];
+ }
+ }
+ if (notifyParent) parent.createItem (this, index);
+}
+/*
+ * Updates internal structures in the receiver and its child items to handle the creation of a new column.
+ */
+void addColumn (TreeColumn column) {
+ int index = column.getIndex ();
+ int columnCount = parent.columns.length;
+
+ if (columnCount > 1) {
+ if (columnCount == 2) {
+ texts = new String [2];
+ } else {
+ String[] newTexts = new String [columnCount];
+ System.arraycopy (texts, 0, newTexts, 0, index);
+ System.arraycopy (texts, index, newTexts, index + 1, columnCount - index - 1);
+ texts = newTexts;
+ }
+ if (index == 0) {
+ texts [1] = text;
+ text = ""; //$NON-NLS-1$
+ }
+
+ if (columnCount == 2) {
+ images = new Image [2];
+ } else {
+ Image[] newImages = new Image [columnCount];
+ System.arraycopy (images, 0, newImages, 0, index);
+ System.arraycopy (images, index, newImages, index + 1, columnCount - index - 1);
+ images = newImages;
+ }
+ if (index == 0) {
+ images [1] = image;
+ image = null;
+ }
+
+ int[] newTextWidths = new int [columnCount];
+ System.arraycopy (textWidths, 0, newTextWidths, 0, index);
+ System.arraycopy (textWidths, index, newTextWidths, index + 1, columnCount - index - 1);
+ textWidths = newTextWidths;
+ } else {
+ customWidth = -1; /* columnCount == 1 */
+ }
+
+ /*
+ * The length of displayTexts always matches the parent's column count, unless this
+ * count is zero, in which case displayTexts is null.
+ */
+ String[] newDisplayTexts = new String [columnCount];
+ if (columnCount > 1) {
+ System.arraycopy (displayTexts, 0, newDisplayTexts, 0, index);
+ System.arraycopy (displayTexts, index, newDisplayTexts, index + 1, columnCount - index - 1);
+ }
+ displayTexts = newDisplayTexts;
+
+ if (cellBackgrounds != null) {
+ Color[] newCellBackgrounds = new Color [columnCount];
+ System.arraycopy (cellBackgrounds, 0, newCellBackgrounds, 0, index);
+ System.arraycopy (cellBackgrounds, index, newCellBackgrounds, index + 1, columnCount - index - 1);
+ cellBackgrounds = newCellBackgrounds;
+ }
+ if (cellForegrounds != null) {
+ Color[] newCellForegrounds = new Color [columnCount];
+ System.arraycopy (cellForegrounds, 0, newCellForegrounds, 0, index);
+ System.arraycopy (cellForegrounds, index, newCellForegrounds, index + 1, columnCount - index - 1);
+ cellForegrounds = newCellForegrounds;
+ }
+ if (cellFonts != null) {
+ Font[] newCellFonts = new Font [columnCount];
+ System.arraycopy (cellFonts, 0, newCellFonts, 0, index);
+ System.arraycopy (cellFonts, index, newCellFonts, index + 1, columnCount - index - 1);
+ cellFonts = newCellFonts;
+
+ int[] newFontHeights = new int [columnCount];
+ System.arraycopy (fontHeights, 0, newFontHeights, 0, index);
+ System.arraycopy (fontHeights, index, newFontHeights, index + 1, columnCount - index - 1);
+ fontHeights = newFontHeights;
+ }
+
+ int orderedIndex = column.getOrderIndex ();
+ if (orderedIndex == 0 && columnCount > 1) {
+ /*
+ * The new second ordered column now has more space available to it than it did while
+ * it was the first ordered column since it no longer has to show hierarchy decorations,
+ * so recompute its displayText.
+ */
+ TreeColumn[] orderedColumns = parent.getOrderedColumns ();
+ int secondColumnIndex = orderedColumns [1].getIndex ();
+ GC gc = new GC (parent);
+ gc.setFont (getFont (secondColumnIndex, false));
+ computeDisplayText (secondColumnIndex, gc);
+ gc.dispose ();
+ }
+
+ /* notify all child items as well */
+ for (int i = 0; i < items.length; i++) {
+ items[i].addColumn (column);
+ }
+}
+/*
+ * Adds a child item to the receiver.
+ */
+void addItem (TreeItem item, int index) {
+ TreeItem[] newChildren = new TreeItem [items.length + 1];
+ System.arraycopy (items, 0, newChildren, 0, index);
+ newChildren [index] = item;
+ System.arraycopy (items, index, newChildren, index + 1, items.length - index);
+ items = newChildren;
+
+ if (!item.isAvailable ()) {
+ /* receiver will now need an expander box if this is its first child */
+ if (isInViewport () && items.length == 1) {
+ Rectangle bounds = getExpanderBounds ();
+ parent.redraw (bounds.x, bounds.y, bounds.width, bounds.height, false);
+ }
+ return;
+ }
+
+ /* item should be available immediately so update parent */
+ parent.makeAvailable (item);
+
+ /* update scrollbars */
+ Rectangle bounds = item.getBounds (false);
+ int rightX = bounds.x + bounds.width;
+ parent.updateHorizontalBar (rightX, rightX);
+ parent.updateVerticalBar ();
+ /*
+ * If new item is above viewport then adjust topIndex and the vertical scrollbar
+ * so that the current viewport items will not change.
+ */
+ if (item.availableIndex < parent.topIndex) {
+ parent.topIndex++;
+ ScrollBar vBar = parent.getVerticalBar ();
+ if (vBar != null) vBar.setSelection (parent.topIndex);
+ return;
+ }
+
+ parent.redrawFromItemDownwards (availableIndex);
+}
+static Tree checkNull (Tree tree) {
+ if (tree == null) SWT.error (SWT.ERROR_NULL_ARGUMENT);
+ return tree;
+}
+static TreeItem checkNull (TreeItem item) {
+ if (item == null) SWT.error (SWT.ERROR_NULL_ARGUMENT);
+ return item;
+}
+protected void checkSubclass () {
+ if (!isValidSubclass ()) error (SWT.ERROR_INVALID_SUBCLASS);
+}
+void clear () {
+ checked = grayed = false;
+ texts = null;
+ textWidths = new int [1];
+ fontHeight = 0;
+ fontHeights = null;
+ images = null;
+ foreground = background = null;
+ displayTexts = null;
+ cellForegrounds = cellBackgrounds = null;
+ font = null;
+ cellFonts = null;
+ cached = false;
+ text = "";
+ image = null;
+
+ int columnCount = parent.columns.length;
+ if (columnCount > 0) {
+ displayTexts = new String [columnCount];
+ if (columnCount > 1) {
+ texts = new String [columnCount];
+ textWidths = new int [columnCount];
+ images = new Image [columnCount];
+ }
+ }
+}
+/**
+ * Clears the item at the given zero-relative index in the receiver.
+ * The text, icon and other attributes of the item are set to the default
+ * value. If the tree was created with the <code>SWT.VIRTUAL</code> style,
+ * these attributes are requested again as needed.
+ *
+ * @param index the index of the item to clear
+ * @param all <code>true</code> if all child items of the indexed item should be
+ * cleared recursively, and <code>false</code> otherwise
+ *
+ * @exception IllegalArgumentException <ul>
+ * <li>ERROR_INVALID_RANGE - if the index is not between 0 and the number of elements in the list minus 1 (inclusive)</li>
+ * </ul>
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @see SWT#VIRTUAL
+ * @see SWT#SetData
+ *
+ * @since 3.2
+ */
+public void clear (int index, boolean recursive) {
+ checkWidget ();
+ if (!(0 <= index && index < items.length)) error (SWT.ERROR_INVALID_RANGE);
+ TreeItem item = items [index];
+
+ /* if there are no columns then the horizontal scrollbar may need adjusting */
+ TreeItem[] availableDescendents = null;
+ int oldRightX = 0;
+ if (item.availableIndex != -1 && parent.columns.length == 0) {
+ if (recursive) {
+ availableDescendents = item.computeAvailableDescendents ();
+ for (int i = 0; i < availableDescendents.length; i++) {
+ Rectangle bounds = availableDescendents [i].getBounds (false);
+ oldRightX = Math.max (oldRightX, bounds.x + bounds.width);
+ }
+ } else {
+ Rectangle bounds = item.getBounds (false);
+ oldRightX = bounds.x + bounds.width;
+ }
+ }
+
+ /* clear the item(s) */
+ item.clear ();
+ if (recursive) {
+ item.clearAll (true, false);
+ }
+ if (item.availableIndex == -1) return; /* no visual update needed */
+
+ /* adjust the horizontal scrollbar if needed */
+ if (parent.columns.length == 0) {
+ int newRightX = 0;
+ if (recursive) {
+ for (int i = 0; i < availableDescendents.length; i++) {
+ Rectangle bounds = availableDescendents [i].getBounds (false);
+ newRightX = Math.max (newRightX, bounds.x + bounds.width);
+ }
+ } else {
+ Rectangle bounds = item.getBounds (false);
+ newRightX = bounds.x + bounds.width;
+ }
+ parent.updateHorizontalBar (newRightX, newRightX - oldRightX);
+ }
+
+ /* redraw the item(s) */
+ if (recursive) {
+ int descendentCount = availableDescendents == null ?
+ item.computeAvailableDescendentCount () :
+ availableDescendents.length;
+ parent.redrawItems (item.availableIndex, item.availableIndex + descendentCount - 1, false);
+ } else {
+ parent.redrawItem (item.availableIndex, false);
+ }
+}
+/**
+ * Clears all the items in the receiver. The text, icon and other
+ * attributes of the items are set to their default values. If the
+ * tree was created with the <code>SWT.VIRTUAL</code> style, these
+ * attributes are requested again as needed.
+ *
+ * @param all <code>true</code> if all child items should be cleared
+ * recursively, and <code>false</code> otherwise
+ *
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @see SWT#VIRTUAL
+ * @see SWT#SetData
+ *
+ * @since 3.2
+ */
+public void clearAll (boolean recursive) {
+ clearAll (recursive, true);
+}
+void clearAll (boolean recursive, boolean doVisualUpdate) {
+ checkWidget ();
+ if (items.length == 0) return;
+
+ /* if there are no columns then the horizontal scrollbar may need adjusting */
+ TreeItem[] availableDescendents = null;
+ int oldRightX = 0;
+ if (doVisualUpdate && availableIndex != -1 && expanded && parent.columns.length == 0) {
+ if (recursive) {
+ availableDescendents = computeAvailableDescendents ();
+ /*
+ * i starts at 1 here because item 0 in availableDescendents
+ * will be the receiver, but this item is not being cleared.
+ */
+ for (int i = 1; i < availableDescendents.length; i++) {
+ Rectangle bounds = availableDescendents [i].getBounds (false);
+ oldRightX = Math.max (oldRightX, bounds.x + bounds.width);
+ }
+ } else {
+ for (int i = 0; i < items.length; i++) {
+ Rectangle bounds = items [i].getBounds (false);
+ oldRightX = Math.max (oldRightX, bounds.x + bounds.width);
+ }
+ }
+ }
+
+ /* clear the item(s) */
+ for (int i = 0; i < items.length; i++) {
+ items [i].clear ();
+ if (recursive) items [i].clearAll (true, false);
+ }
+
+ if (!doVisualUpdate || availableIndex == -1 || !expanded) return; /* no visual update needed */
+
+ /* adjust the horizontal scrollbar if needed */
+ if (parent.columns.length == 0) {
+ int newRightX = 0;
+ if (recursive) {
+ /*
+ * i starts at 1 here because item 0 in availableDescendents
+ * is the receiver, but this item was not cleared.
+ */
+ for (int i = 1; i < availableDescendents.length; i++) {
+ Rectangle bounds = availableDescendents [i].getBounds (false);
+ newRightX = Math.max (newRightX, bounds.x + bounds.width);
+ }
+ } else {
+ /*
+ * All cleared direct child items will have the same x and width
+ * values now, so just measure the first one as a sample.
+ */
+ Rectangle bounds = items [0].getBounds (false);
+ newRightX = bounds.x + bounds.width;
+ }
+ parent.updateHorizontalBar (newRightX, newRightX - oldRightX);
+ }
+
+ /* redraw the item(s) */
+ if (recursive) {
+ int startIndex = items [0].availableIndex;
+ TreeItem lastChild = items [items.length - 1];
+ int endIndex = lastChild.availableIndex + lastChild.computeAvailableDescendentCount () - 1;
+ parent.redrawItems (startIndex, endIndex, false);
+ } else {
+ for (int i = 0; i < items.length; i++) {
+ parent.redrawItem (items [i].availableIndex, false);
+ }
+ }
+}
+/*
+ * Returns a collection of all tree items descending from the receiver, including
+ * the receiver. The order of the items in this collection are receiver, child0tree,
+ * child1tree, ..., childNtree.
+ */
+TreeItem[] computeAllDescendents () {
+ int childCount = items.length;
+ TreeItem[][] childResults = new TreeItem [childCount][];
+ int count = 1; /* receiver */
+ for (int i = 0; i < childCount; i++) {
+ childResults [i] = items [i].computeAllDescendents ();
+ count += childResults [i].length;
+ }
+ TreeItem[] result = new TreeItem [count];
+ int index = 0;
+ result [index++] = this;
+ for (int i = 0; i < childCount; i++) {
+ System.arraycopy (childResults [i], 0, result, index, childResults [i].length);
+ index += childResults [i].length;
+ }
+ return result;
+}
+/*
+ * Returns the number of tree items descending from the receiver, including the
+ * receiver, that are currently available. It is assumed that the receiver is
+ * currently available.
+ */
+int computeAvailableDescendentCount () {
+ int result = 1; /* receiver */
+ if (!expanded) return result;
+ for (int i = 0; i < items.length; i++) {
+ result += items [i].computeAvailableDescendentCount ();
+ }
+ return result;
+}
+/*
+ * Returns a collection of the tree items descending from the receiver, including
+ * the receiver, that are currently available. It is assumed that the receiver is
+ * currently available. The order of the items in this collection are receiver,
+ * child0tree, child1tree, ..., childNtree.
+ */
+TreeItem[] computeAvailableDescendents () {
+ if (!expanded) return new TreeItem[] {this};
+ int childCount = items.length;
+ TreeItem[][] childResults = new TreeItem [childCount][];
+ int count = 1; /* receiver */
+ for (int i = 0; i < childCount; i++) {
+ childResults [i] = items [i].computeAvailableDescendents ();
+ count += childResults [i].length;
+ }
+ TreeItem[] result = new TreeItem [count];
+ int index = 0;
+ result [index++] = this;
+ for (int i = 0; i < childCount; i++) {
+ System.arraycopy (childResults [i], 0, result, index, childResults [i].length);
+ index += childResults [i].length;
+ }
+ return result;
+}
+void computeDisplayText (int columnIndex, GC gc) {
+ if ((parent.style & SWT.VIRTUAL) != 0 && !cached) return; /* nothing to do */
+
+ int columnCount = parent.columns.length;
+ if (columnCount == 0) {
+ String text = getText (0, false);
+ textWidths [columnIndex] = gc.stringExtent (text).x;
+ return;
+ }
+
+ int orderedIndex = parent.columns.length == 0 ? 0 : parent.columns [columnIndex].getOrderIndex ();
+ TreeColumn column = parent.columns [columnIndex];
+ int availableWidth;
+ if (orderedIndex == 0) {
+ /* ordered column 0 is always LEFT and must consider hierarchy decorations */
+ availableWidth = column.getX () + column.width - getTextX (columnIndex) - 2 * MARGIN_TEXT;
+ } else {
+ /* ordered columns > 0 may not be LEFT so cannot use getTextX (int) */
+ availableWidth = column.width - 2 * parent.getCellPadding () - 2 * MARGIN_TEXT;
+ if (images [columnIndex] != null) {
+ availableWidth -= images [columnIndex].getBounds ().width;
+ availableWidth -= Tree.MARGIN_IMAGE;
+ }
+ }
+ String text = getText (columnIndex, false);
+ int textWidth = gc.stringExtent (text).x;
+ if (textWidth <= availableWidth) {
+ displayTexts [columnIndex] = text;
+ textWidths [columnIndex] = textWidth;
+ return;
+ }
+
+ /* Ellipsis will be needed, so subtract their width from the available text width */
+ int ellipsisWidth = gc.stringExtent (Tree.ELLIPSIS).x;
+ availableWidth -= ellipsisWidth;
+ if (availableWidth <= 0) {
+ displayTexts [columnIndex] = Tree.ELLIPSIS;
+ textWidths [columnIndex] = ellipsisWidth;
+ return;
+ }
+
+ /* Make initial guess. */
+ int index = Math.min (availableWidth / gc.getFontMetrics ().getAverageCharWidth (), text.length ());
+ textWidth = gc.stringExtent (text.substring (0, index)).x;
+
+ /* Initial guess is correct. */
+ if (availableWidth == textWidth) {
+ displayTexts [columnIndex] = text.substring (0, index) + Tree.ELLIPSIS;
+ textWidths [columnIndex] = textWidth + ellipsisWidth;
+ return;
+ }
+
+ /* Initial guess is too high, so reduce until fit is found. */
+ if (availableWidth < textWidth) {
+ do {
+ index--;
+ if (index < 0) {
+ displayTexts [columnIndex] = Tree.ELLIPSIS;
+ textWidths [columnIndex] = ellipsisWidth;
+ return;
+ }
+ text = text.substring (0, index);
+ textWidth = gc.stringExtent (text).x;
+ } while (availableWidth < textWidth);
+ displayTexts [columnIndex] = text + Tree.ELLIPSIS;
+ textWidths [columnIndex] = textWidth + ellipsisWidth;
+ return;
+ }
+
+ /* Initial guess is too low, so increase until overrun is found. */
+ int previousWidth = 0;
+ while (textWidth < availableWidth) {
+ index++;
+ previousWidth = textWidth;
+ textWidth = gc.stringExtent (text.substring (0, index)).x;
+ }
+ displayTexts [columnIndex] = text.substring (0, index - 1) + Tree.ELLIPSIS;
+ textWidths [columnIndex] = previousWidth + ellipsisWidth;
+}
+void computeDisplayTexts (GC gc) {
+ if ((parent.style & SWT.VIRTUAL) != 0 && !cached) return; /* nothing to do */
+
+ int columnCount = parent.columns.length;
+ if (columnCount == 0) return;
+
+ for (int i = 0; i < columnCount; i++) {
+ gc.setFont (getFont (i, false));
+ computeDisplayText (i, gc);
+ }
+}
+/*
+ * Computes the cached text widths.
+ */
+void computeTextWidths (GC gc) {
+ if ((parent.style & SWT.VIRTUAL) != 0 && !cached) return; /* nothing to do */
+
+ int validColumnCount = Math.max (1, parent.columns.length);
+ textWidths = new int [validColumnCount];
+ for (int i = 0; i < textWidths.length; i++) {
+ String value = getDisplayText (i);
+ if (value != null) {
+ gc.setFont (getFont (i, false));
+ textWidths [i] = gc.stringExtent (value).x;
+ }
+ }
+}
+public void dispose () {
+ if (isDisposed ()) return;
+ int startIndex = -1, endIndex = -1;
+ Tree parent = this.parent;
+ int index = getIndex ();
+
+ /* determine the indices, if any, that will need to be visually updated */
+ if (isAvailable ()) {
+ if (isLastChild () && index > 0) {
+ /* vertical connector lines no longer needed for this item */
+ if (parentItem != null) {
+ startIndex = parentItem.items [index - 1].availableIndex;
+ } else {
+ startIndex = parent.items [index - 1].availableIndex;
+ }
+ } else {
+ startIndex = availableIndex;
+ }
+ endIndex = parent.availableItemsCount - 1;
+ }
+
+ /* for performance do this upfront for whole descendent chain */
+ TreeItem focusItem = parent.focusItem;
+ if (focusItem != null && focusItem.hasAncestor (this)) {
+ parent.setFocusItem (this, false);
+ parent.reassignFocus ();
+ focusItem = parent.focusItem;
+ if (focusItem != null) {
+ parent.redrawItem (focusItem.availableIndex, true);
+ }
+ }
+ if (parentItem != null) parentItem.removeItem (this, index);
+ dispose (true);
+ if (startIndex != -1) {
+ parent.redrawItems (startIndex, endIndex, false);
+ }
+}
+void dispose (boolean notifyParent) {
+ if (isDisposed ()) return;
+ for (int i = 0; i < items.length; i++) {
+ items [i].dispose (notifyParent);
+ }
+ if (notifyParent) parent.destroyItem (this);
+ super.dispose (); /* super is intentional here */
+ background = foreground = null;
+ cellBackgrounds = cellForegrounds = null;
+ font = null;
+ cellFonts = null;
+ images = null;
+ texts = displayTexts = null;
+ textWidths = fontHeights = null;
+ parent = null;
+ parentItem = null;
+ items = null;
+}
+/*
+ * Ensure that all ancestors of the receiver are expanded
+ */
+void expandAncestors () {
+ if (parentItem != null) parentItem.expandAncestors ();
+ setExpanded (true);
+ Event newEvent = new Event ();
+ newEvent.item = this;
+ parent.inExpand = true;
+ parent.sendEvent (SWT.Expand, newEvent);
+ parent.inExpand = false;
+ if (isDisposed ()) return;
+ if (items.length == 0) {
+ expanded = false;
+ }
+}
+/**
+ * Returns the receiver's background color.
+ *
+ * @return the background color
+ *
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @since 2.0
+ *
+ */
+public Color getBackground () {
+ checkWidget ();
+ if (!parent.checkData (this, true)) error (SWT.ERROR_WIDGET_DISPOSED);
+ if (background != null) return background;
+ return parent.getBackground ();
+}
+/**
+ * Returns the background color at the given column index in the receiver.
+ *
+ * @param index the column index
+ * @return the background color
+ *
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @since 3.1
+ */
+public Color getBackground (int columnIndex) {
+ checkWidget ();
+ if (!parent.checkData (this, true)) error (SWT.ERROR_WIDGET_DISPOSED);
+ int validColumnCount = Math.max (1, parent.columns.length);
+ if (!(0 <= columnIndex && columnIndex < validColumnCount)) return getBackground ();
+ if (cellBackgrounds == null || cellBackgrounds [columnIndex] == null) return getBackground ();
+ return cellBackgrounds [columnIndex];
+}
+/**
+ * Returns a rectangle describing the receiver's size and location
+ * relative to its parent.
+ *
+ * @return the receiver's bounding rectangle
+ *
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public Rectangle getBounds () {
+ checkWidget ();
+ return getBounds (true);
+}
+Rectangle getBounds (boolean checkData) {
+ if (checkData && !parent.checkData (this, true)) error (SWT.ERROR_WIDGET_DISPOSED);
+ if (!isAvailable ()) return new Rectangle (0, 0, 0, 0);
+ TreeColumn[] orderedColumns = parent.getOrderedColumns ();
+ int orderedCol0Index = orderedColumns.length == 0 ? 0 : orderedColumns [0].getIndex ();
+ int x = getTextX (orderedCol0Index);
+ int width = textWidths [orderedCol0Index] + 2 * MARGIN_TEXT;
+ if (orderedColumns.length > 0) {
+ TreeColumn column = orderedColumns [0];
+ int right = column.getX () + column.width;
+ if (x + width > right) {
+ width = Math.max (0, right - x);
+ }
+ }
+ return new Rectangle (x, parent.getItemY (this), width, parent.itemHeight);
+}
+/**
+ * Returns a rectangle describing the receiver's size and location
+ * relative to its parent at a column in the tree.
+ *
+ * @param index the index that specifies the column
+ * @return the receiver's bounding column rectangle
+ *
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @since 3.1
+ */
+public Rectangle getBounds (int columnIndex) {
+ checkWidget ();
+ if (!parent.checkData (this, true)) error (SWT.ERROR_WIDGET_DISPOSED);
+ if (!isAvailable ()) return new Rectangle (0, 0, 0, 0);
+ TreeColumn[] columns = parent.columns;
+ int columnCount = columns.length;
+ int validColumnCount = Math.max (1, columnCount);
+ if (!(0 <= columnIndex && columnIndex < validColumnCount)) {
+ return new Rectangle (0, 0, 0, 0);
+ }
+ /*
+ * If there are no columns then this is the bounds of the receiver's content.
+ */
+ if (columnCount == 0) {
+ return new Rectangle (
+ getContentX (0),
+ parent.getItemY (this),
+ getContentWidth (0),
+ parent.itemHeight - 1);
+ }
+
+ TreeColumn column = columns [columnIndex];
+ if (column.getOrderIndex () == 0) {
+ /*
+ * For ordered column 0 this is bounds from the beginning of the content to the
+ * end of the column.
+ */
+ int x = getContentX (columnIndex);
+ int offset = x - column.getX ();
+ int width = Math.max (0, column.width - offset - 1); /* max is for columns with small widths */
+ return new Rectangle (x, parent.getItemY (this) + 1, width, parent.itemHeight - 1);
+ }
+ /*
+ * For ordered columns > 0 this is the bounds of the tree cell.
+ */
+ return new Rectangle (column.getX (), parent.getItemY (this) + 1, column.width, parent.itemHeight - 1);
+}
+/*
+ * Returns the full bounds of a cell in a tree, regardless of its content.
+ */
+Rectangle getCellBounds (int columnIndex) {
+ int y = parent.getItemY (this);
+ if (parent.columns.length == 0) {
+ int width;
+ if (customWidth != -1) {
+ width = getContentX (0) + customWidth + parent.horizontalOffset;
+ } else {
+ int textPaintWidth = textWidths [0] + 2 * MARGIN_TEXT;
+ width = getTextX (0) + textPaintWidth + parent.horizontalOffset;
+ }
+ return new Rectangle (-parent.horizontalOffset, y, width, parent.itemHeight);
+ }
+ TreeColumn column = parent.columns [columnIndex];
+ return new Rectangle (column.getX (), y, column.width, parent.itemHeight);
+}
+/*
+ * Returns the bounds of the receiver's checkbox, or null if the parent's style does not
+ * include SWT.CHECK.
+ */
+Rectangle getCheckboxBounds () {
+ if ((parent.getStyle () & SWT.CHECK) == 0) return null;
+ int itemHeight = parent.itemHeight;
+ Rectangle result = parent.checkboxBounds;
+ Point[] hLinePoints = getHconnectorEndpoints ();
+ result.x = hLinePoints [1].x;
+ result.y = parent.getItemY (this) + (itemHeight - result.height) / 2;
+ return result;
+}
+/**
+ * Returns <code>true</code> if the receiver is checked,
+ * and false otherwise. When the parent does not have
+ * the <code>CHECK style, return false.
+ * <p>
+ *
+ * @return the checked state
+ *
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public boolean getChecked () {
+ checkWidget ();
+ if (!parent.checkData (this, true)) error (SWT.ERROR_WIDGET_DISPOSED);
+ return checked;
+}
+int getContentWidth (int columnIndex) {
+ int width = textWidths [columnIndex] + 2 * MARGIN_TEXT;
+ int orderedIndex = parent.columns.length == 0 ? 0 : parent.columns [columnIndex].getOrderIndex ();
+ if (orderedIndex == 0) {
+ width += parent.orderedCol0imageWidth;
+ if (parent.orderedCol0imageWidth > 0) width += Tree.MARGIN_IMAGE;
+ } else {
+ Image image = getImage (columnIndex, false);
+ if (image != null) {
+ width += image.getBounds ().width + Tree.MARGIN_IMAGE;
+ }
+ }
+ return width;
+}
+/*
+ * Returns the x value where the receiver's content (ie.- its image or text) begins
+ * for the specified column. For ordered columns > 0 this is dependent upon column
+ * alignment, and for ordered column 0 this is dependent upon the receiver's depth in
+ * the tree item hierarchy and the presence/absence of a checkbox.
+ */
+int getContentX (int columnIndex) {
+ int orderedIndex = parent.columns.length == 0 ? 0 : parent.columns [columnIndex].getOrderIndex ();
+ if (orderedIndex > 0) {
+ TreeColumn column = parent.columns [columnIndex];
+ int contentX = column.getX () + parent.getCellPadding ();
+ if ((column.style & SWT.LEFT) != 0) return contentX;
+
+ /* column is not left-aligned */
+ int contentWidth = getContentWidth (columnIndex);
+ if ((column.style & SWT.RIGHT) != 0) {
+ int padding = parent.getCellPadding ();
+ contentX = Math.max (contentX, column.getX () + column.width - padding - contentWidth);
+ } else { /* SWT.CENTER */
+ contentX = Math.max (contentX, column.getX () + (column.width - contentWidth) / 2);
+ }
+ return contentX;
+ }
+
+ /* ordered column 0 (always left-aligned) */
+ if ((parent.style & SWT.CHECK) != 0) {
+ Rectangle checkBounds = getCheckboxBounds ();
+ return checkBounds.x + checkBounds.width + Tree.MARGIN_IMAGE;
+ }
+
+ int contentX = parent.getCellPadding () - parent.horizontalOffset;
+ if (parentItem != null) {
+ int expanderWidth = parent.expanderBounds.width + INDENT_HIERARCHY;
+ contentX += expanderWidth * depth;
+ }
+ contentX += parent.expanderBounds.width;
+ return contentX + Tree.MARGIN_IMAGE + INDENT_HIERARCHY;
+}
+String getDisplayText (int columnIndex) {
+ if (parent.columns.length == 0) return getText (0, false);
+ String result = displayTexts [columnIndex];
+ return result != null ? result : ""; //$NON-NLS-1$
+}
+/**
+ * Returns <code>true</code> if the receiver is expanded,
+ * and false otherwise.
+ * <p>
+ *
+ * @return the expanded state
+ *
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public boolean getExpanded () {
+ checkWidget ();
+ if (!parent.checkData (this, true)) error (SWT.ERROR_WIDGET_DISPOSED);
+ return expanded;
+}
+/*
+ * Returns the bounds of the receiver's expander box, regardless of whether the
+ * receiver currently has children or not.
+ */
+Rectangle getExpanderBounds () {
+ int itemHeight = parent.itemHeight;
+ int x = parent.getCellPadding () - parent.horizontalOffset;
+ int y = parent.getItemY (this);
+ if (parentItem != null) {
+ int expanderWidth = parent.expanderBounds.width + INDENT_HIERARCHY;
+ x += expanderWidth * depth;
+ }
+ return new Rectangle (
+ x, y + (itemHeight - parent.expanderBounds.height) / 2,
+ parent.expanderBounds.width, parent.expanderBounds.height);
+}
+/*
+ * Returns the bounds that should be used for drawing a focus rectangle on the receiver
+ */
+Rectangle getFocusBounds () {
+ TreeColumn[] columns = parent.columns;
+ int orderedCol0index = columns.length == 0 ? 0 : parent.getOrderedColumns ()[0].getIndex ();
+ int x = getTextX (orderedCol0index);
+
+ int width;
+ if (columns.length > 0) {
+ /* ensure that the focus x does not start beyond the right bound of ordered column 0 */
+ int rightX = columns [orderedCol0index].getX () + columns [orderedCol0index].width;
+ x = Math.min (x, rightX - 1);
+
+ TreeColumn column;
+ if ((parent.style & SWT.FULL_SELECTION) != 0) {
+ int[] columnOrder = parent.getColumnOrder ();
+ column = columns [columnOrder [columnOrder.length - 1]]; /* last ordered column */
+ } else {
+ column = columns [orderedCol0index];
+ }
+ width = column.getX () + column.width - x - 1;
+ } else { /* no columns */
+ if (customWidth != -1) {
+ width = customWidth;
+ } else {
+ width = textWidths [0] + 2 * MARGIN_TEXT;
+ }
+ }
+
+ return new Rectangle (
+ x,
+ parent.getItemY (this) + (parent.linesVisible ? 1 : 0),
+ width,
+ parent.itemHeight - (parent.linesVisible ? 1 : 0));
+}
+/**
+ * Returns the font that the receiver will use to paint textual information for this item.
+ *
+ * @return the receiver's font
+ *
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @since 3.0
+ */
+public Font getFont () {
+ checkWidget ();
+ return getFont (true);
+}
+Font getFont (boolean checkData) {
+ if (checkData && !parent.checkData (this, true)) error (SWT.ERROR_WIDGET_DISPOSED);
+ if (font != null) return font;
+ return parent.getFont ();
+}
+/**
+ * Returns the font that the receiver will use to paint textual information
+ * for the specified cell in this item.
+ *
+ * @param index the column index
+ * @return the receiver's font
+ *
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @since 3.1
+ */
+public Font getFont (int columnIndex) {
+ checkWidget ();
+ return getFont (columnIndex, true);
+}
+Font getFont (int columnIndex, boolean checkData) {
+ if (checkData && !parent.checkData (this, true)) error (SWT.ERROR_WIDGET_DISPOSED);
+ int validColumnCount = Math.max (1, parent.columns.length);
+ if (!(0 <= columnIndex && columnIndex < validColumnCount)) return getFont (checkData);
+ if (cellFonts == null || cellFonts [columnIndex] == null) return getFont (checkData);
+ return cellFonts [columnIndex];
+}
+int getFontHeight () {
+ if (fontHeight != 0) return fontHeight;
+ return parent.fontHeight;
+}
+int getFontHeight (int columnIndex) {
+ if (fontHeights == null || fontHeights [columnIndex] == 0) return getFontHeight ();
+ return fontHeights [columnIndex];
+}
+/**
+ * Returns the foreground color that the receiver will use to draw.
+ *
+ * @return the receiver's foreground color
+ *
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @since 2.0
+ *
+ */
+public Color getForeground () {
+ checkWidget ();
+ if (!parent.checkData (this, true)) error (SWT.ERROR_WIDGET_DISPOSED);
+ if (foreground != null) return foreground;
+ return parent.getForeground ();
+}
+/**
+ *
+ * Returns the foreground color at the given column index in the receiver.
+ *
+ * @param index the column index
+ * @return the foreground color
+ *
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @since 3.1
+ */
+public Color getForeground (int columnIndex) {
+ checkWidget ();
+ if (!parent.checkData (this, true)) error (SWT.ERROR_WIDGET_DISPOSED);
+ int validColumnCount = Math.max (1, parent.columns.length);
+ if (!(0 <= columnIndex && columnIndex < validColumnCount)) return getForeground ();
+ if (cellForegrounds == null || cellForegrounds [columnIndex] == null) return getForeground ();
+ return cellForegrounds [columnIndex];
+}
+/**
+ * Returns <code>true</code> if the receiver is grayed,
+ * and false otherwise. When the parent does not have
+ * the <code>CHECK style, return false.
+ * <p>
+ *
+ * @return the grayed state of the checkbox
+ *
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public boolean getGrayed () {
+ checkWidget ();
+ if (!parent.checkData (this, true)) error (SWT.ERROR_WIDGET_DISPOSED);
+ return grayed;
+}
+/*
+ * Answers the start and end points of the horizontal connector line that is
+ * drawn between an item's expander box and its checkbox or content.
+ */
+Point[] getHconnectorEndpoints () {
+ Rectangle expanderBounds = getExpanderBounds ();
+ int x, width;
+ if (items.length == 0) { /* no child items, so no expander box */
+ x = expanderBounds.x + Compatibility.ceil (expanderBounds.width, 2);
+ width = Compatibility.floor (expanderBounds.width, 2) + INDENT_HIERARCHY;
+ } else { /* has child items */
+ x = expanderBounds.x + expanderBounds.width;
+ width = INDENT_HIERARCHY;
+ }
+ int y = expanderBounds.y + expanderBounds.height / 2;
+ return new Point[] {
+ new Point (x, y),
+ new Point (x + width, y)
+ };
+}
+/*
+ * Returns the bounds representing the clickable region that should select the receiver.
+ */
+Rectangle getHitBounds () {
+ int[] columnOrder = parent.getColumnOrder ();
+ int orderedCol0index = columnOrder.length == 0 ? 0 : parent.columns [columnOrder [0]].getIndex ();
+ int contentX = getContentX (orderedCol0index);
+ int width = 0;
+ TreeColumn[] columns = parent.columns;
+ if (columns.length == 0) {
+ width = getContentWidth (0);
+ } else {
+ /*
+ * If there are columns then this spans from the beginning of the receiver's column 0
+ * image or text to the end of either column 0 or the last column (FULL_SELECTION).
+ */
+ TreeColumn column;
+ if ((parent.style & SWT.FULL_SELECTION) != 0) {
+ column = columns [columnOrder [columnOrder.length - 1]]; /* last column */
+ } else {
+ column = columns [orderedCol0index];
+ }
+ width = column.getX () + column.width - contentX;
+ }
+ return new Rectangle (contentX, parent.getItemY (this), width, parent.itemHeight);
+}
+public Image getImage () {
+ checkWidget ();
+ if (!parent.checkData (this, true)) error (SWT.ERROR_WIDGET_DISPOSED);
+ return super.getImage ();
+}
+/**
+ * Returns the image stored at the given column index in the receiver,
+ * or null if the image has not been set or if the column does not exist.
+ *
+ * @param index the column index
+ * @return the image stored at the given column index in the receiver
+ *
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @since 3.1
+ */
+public Image getImage (int columnIndex) {
+ checkWidget ();
+ return getImage (columnIndex, true);
+}
+Image getImage (int columnIndex, boolean checkData) {
+ if (checkData && !parent.checkData (this, true)) error (SWT.ERROR_WIDGET_DISPOSED);
+ int validColumnCount = Math.max (1, parent.columns.length);
+ if (!(0 <= columnIndex && columnIndex < validColumnCount)) return null;
+ if (columnIndex == 0) return super.getImage (); /* super is intentional here */
+ return images [columnIndex];
+}
+/**
+ * Returns a rectangle describing the size and location
+ * relative to its parent of an image at a column in the
+ * tree.
+ *
+ * @param index the index that specifies the column
+ * @return the receiver's bounding image rectangle
+ *
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @since 3.1
+ */
+public Rectangle getImageBounds (int columnIndex) {
+ checkWidget ();
+ if (!parent.checkData (this, true)) error (SWT.ERROR_WIDGET_DISPOSED);
+ int validColumnCount = Math.max (1, parent.columns.length);
+ if (!(0 <= columnIndex && columnIndex < validColumnCount)) return new Rectangle (0,0,0,0);
+
+ int padding = parent.getCellPadding ();
+ int startX = getContentX (columnIndex);
+ int itemHeight = parent.itemHeight;
+ int imageSpaceY = itemHeight - 2 * padding;
+ int y = parent.getItemY (this);
+ int orderedIndex = parent.columns.length == 0 ? 0 : parent.columns [columnIndex].getOrderIndex ();
+ Image image = getImage (columnIndex, false);
+ int drawWidth = 0;
+ if (orderedIndex == 0) {
+ /* for ordered column 0 all images have the same width */
+ drawWidth = parent.orderedCol0imageWidth;
+ } else {
+ if (image != null) drawWidth = image.getBounds ().width;
+ }
+ return new Rectangle (startX, y + padding, drawWidth, imageSpaceY);
+}
+int getIndex () {
+ TreeItem[] items;
+ if (parentItem != null) {
+ items = parentItem.items;
+ } else {
+ items = parent.items;
+ }
+ for (int i = 0; i < items.length; i++) {
+ if (items [i] == this) return i;
+ }
+ return -1;
+}
+/**
+ * Returns the item at the given, zero-relative index in the
+ * receiver. Throws an exception if the index is out of range.
+ *
+ * @param index the index of the item to return
+ * @return the item at the given index
+ *
+ * @exception IllegalArgumentException <ul>
+ * <li>ERROR_INVALID_RANGE - if the index is not between 0 and the number of elements in the list minus 1 (inclusive)</li>
+ * </ul>
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @since 3.1
+ */
+public TreeItem getItem (int index) {
+ checkWidget ();
+ if (index < 0) error (SWT.ERROR_INVALID_RANGE);
+ if (!parent.checkData (this, true)) error (SWT.ERROR_WIDGET_DISPOSED);
+ if (index >= items.length) error (SWT.ERROR_INVALID_RANGE);
+ return items [index];
+}
+/**
+ * Returns the number of items contained in the receiver
+ * that are direct item children of the receiver.
+ *
+ * @return the number of items
+ *
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public int getItemCount () {
+ checkWidget ();
+ if (!parent.checkData (this, true)) error (SWT.ERROR_WIDGET_DISPOSED);
+ return items.length;
+}
+String getNameText () {
+ if ((parent.style & SWT.VIRTUAL) != 0) {
+ if (!cached) return "*virtual*"; //$NON-NLS-1$
+ }
+ return super.getNameText ();
+}
+/**
+ * Returns a (possibly empty) array of <code>TreeItem</code>s which
+ * are the direct item children of the receiver.
+ * <p>
+ * Note: This is not the actual structure used by the receiver
+ * to maintain its list of items, so modifying the array will
+ * not affect the receiver.
+ * </p>
+ *
+ * @return the receiver's items
+ *
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public TreeItem [] getItems () {
+ checkWidget ();
+ if (!parent.checkData (this, true)) error (SWT.ERROR_WIDGET_DISPOSED);
+ TreeItem[] result = new TreeItem [items.length];
+ System.arraycopy (items, 0, result, 0, items.length);
+ return result;
+}
+/**
+ * Returns the receiver's parent, which must be a <code>Tree</code>.
+ *
+ * @return the receiver's parent
+ *
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public Tree getParent () {
+ checkWidget ();
+ return parent;
+}
+/**
+ * Returns the receiver's parent item, which must be a
+ * <code>TreeItem</code> or null when the receiver is a
+ * root.
+ *
+ * @return the receiver's parent item
+ *
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public TreeItem getParentItem () {
+ checkWidget ();
+ return parentItem;
+}
+/*
+ * Returns the receiver's ideal width for the specified columnIndex.
+ */
+int getPreferredWidth (int columnIndex) {
+ int width = 0;
+ GC gc = new GC (parent);
+ gc.setFont (getFont (columnIndex, false));
+ width += gc.stringExtent (getText (columnIndex, false)).x + 2 * MARGIN_TEXT;
+ int orderedIndex = parent.columns.length == 0 ? 0 : parent.columns [columnIndex].getOrderIndex ();
+ if (orderedIndex == 0) {
+ if (parent.orderedCol0imageWidth > 0) {
+ width += parent.orderedCol0imageWidth;
+ width += Tree.MARGIN_IMAGE;
+ }
+ } else {
+ Image image = getImage (columnIndex, false);
+ if (image != null) {
+ width += image.getBounds ().width;
+ width += Tree.MARGIN_IMAGE;
+ }
+ }
+
+ if (parent.hooks (SWT.MeasureItem)) {
+ Event event = new Event ();
+ event.item = this;
+ event.gc = gc;
+ event.index = columnIndex;
+ event.x = getContentX (columnIndex);
+ event.y = parent.getItemY (this);
+ event.width = width;
+ event.height = parent.itemHeight;
+ parent.sendEvent (SWT.MeasureItem, event);
+ if (parent.itemHeight != event.height) {
+ parent.customHeightSet = true;
+ boolean update = parent.setItemHeight (event.height + 2 * parent.getCellPadding ());
+ if (update) parent.redraw ();
+ }
+ width = event.width;
+ }
+ gc.dispose ();
+
+ if (orderedIndex == 0) {
+ return getContentX (columnIndex) + parent.horizontalOffset + width + parent.getCellPadding (); /* right side cell pad */
+ }
+
+ return width + 2 * parent.getCellPadding ();
+}
+public String getText () {
+ checkWidget ();
+ if (!parent.checkData (this, true)) error (SWT.ERROR_WIDGET_DISPOSED);
+ return super.getText ();
+}
+/**
+ * Returns the text stored at the given column index in the receiver,
+ * or empty string if the text has not been set.
+ *
+ * @param index the column index
+ * @return the text stored at the given column index in the receiver
+ *
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @since 3.1
+ */
+public String getText (int columnIndex) {
+ checkWidget ();
+ return getText (columnIndex, true);
+}
+String getText (int columnIndex, boolean checkData) {
+ if (checkData && !parent.checkData (this, true)) error (SWT.ERROR_WIDGET_DISPOSED);
+ int validColumnCount = Math.max (1, parent.columns.length);
+ if (!(0 <= columnIndex && columnIndex < validColumnCount)) return ""; //$NON-NLS-1$
+ if (columnIndex == 0) return super.getText (); /* super is intentional here */
+ if (texts [columnIndex] == null) return ""; //$NON-NLS-1$
+ return texts [columnIndex];
+}
+/**
+ * Returns a rectangle describing the size and location
+ * relative to its parent of the text at a column in the
+ * tree.
+ *
+ * @param index the index that specifies the column
+ * @return the receiver's bounding text rectangle
+ *
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @since 3.3
+ */
+public Rectangle getTextBounds (int columnIndex) {
+ checkWidget ();
+ if (!parent.checkData (this, true)) error (SWT.ERROR_WIDGET_DISPOSED);
+ if (!isAvailable ()) return new Rectangle (0, 0, 0, 0);
+ TreeColumn[] columns = parent.columns;
+ int columnCount = columns.length;
+ int validColumnCount = Math.max (1, columnCount);
+ if (!(0 <= columnIndex && columnIndex < validColumnCount)) {
+ return new Rectangle (0, 0, 0, 0);
+ }
+ /*
+ * If there are no columns then this is the bounds of the receiver's content.
+ */
+ if (columnCount == 0) {
+ int x = getTextX (0) + MARGIN_TEXT;
+ int width = Math.max (0, getContentX(0) + getContentWidth (0) - x);
+ return new Rectangle (
+ x,
+ parent.getItemY (this),
+ width,
+ parent.itemHeight - 1);
+ }
+
+ TreeColumn column = columns [columnIndex];
+ if (column.getOrderIndex () == 0) {
+ /*
+ * For ordered column 0 this is bounds from the beginning of the content to the
+ * end of the column.
+ */
+ int x = getTextX (columnIndex) + MARGIN_TEXT;
+ int offset = x - column.getX ();
+ int width = Math.max (0, column.width - offset - 1); /* max is for columns with small widths */
+ return new Rectangle (x, parent.getItemY (this) + 1, width, parent.itemHeight - 1);
+ }
+ /*
+ * For ordered columns > 0 this is the bounds of the tree cell.
+ */
+ int x = getTextX (columnIndex) + MARGIN_TEXT;
+ int offset = x - column.getX ();
+ int width = Math.max (0, column.width - offset - MARGIN_TEXT);
+ return new Rectangle (x, parent.getItemY (this) + 1, width, parent.itemHeight - 1);
+}
+/*
+ * Returns the x value where the receiver's text begins.
+ */
+int getTextX (int columnIndex) {
+ int orderedIndex = parent.columns.length == 0 ? 0 : parent.columns [columnIndex].getOrderIndex ();
+ int textX = getContentX (columnIndex);
+ if (orderedIndex == 0) {
+ textX += parent.orderedCol0imageWidth;
+ if (parent.orderedCol0imageWidth > 0) textX += Tree.MARGIN_IMAGE;
+ } else {
+ Image image = getImage (columnIndex, false);
+ if (image != null) {
+ textX += image.getBounds ().width + Tree.MARGIN_IMAGE;
+ }
+ }
+ return textX;
+}
+/*
+ * Returns true if the receiver descends from (or is identical to) the item.
+ */
+boolean hasAncestor (TreeItem item) {
+ if (this == item) return true;
+ if (parentItem == null) return false;
+ return parentItem.hasAncestor (item);
+}
+/**
+ * Searches the receiver's list starting at the first item
+ * (index 0) until an item is found that is equal to the
+ * argument, and returns the index of that item. If no item
+ * is found, returns -1.
+ *
+ * @param item the search item
+ * @return the index of the item
+ *
+ * @exception IllegalArgumentException <ul>
+ * <li>ERROR_NULL_ARGUMENT - if the item is null</li>
+ * <li>ERROR_INVALID_ARGUMENT - if the item has been disposed</li>
+ * </ul>
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @since 3.1
+ */
+public int indexOf (TreeItem item) {
+ checkWidget ();
+ if (item == null) error (SWT.ERROR_NULL_ARGUMENT);
+ if (item.isDisposed ()) error (SWT.ERROR_INVALID_ARGUMENT);
+ if (item.parentItem != this) return -1;
+ return item.getIndex ();
+}
+/*
+ * Returns true if the receiver is currently available (though not necessary in the viewport).
+ */
+boolean isAvailable () {
+ if (parentItem == null) return true; /* root items are always available */
+ if (!parentItem.expanded) return false;
+ return parentItem.isAvailable ();
+}
+/*
+ * Answers a boolean indicating whether the receiver's y is within the current
+ * viewport of the parent.
+ */
+boolean isInViewport () {
+ if (availableIndex == -1) return false;
+ int topIndex = parent.topIndex;
+ if (availableIndex < topIndex) return false;
+ int visibleCount = parent.clientArea.height / parent.itemHeight;
+ return availableIndex <= topIndex + visibleCount;
+}
+/*
+ * Returns true if the receiver is the last child of its parent item, or of its parent
+ * if the receiver is a root item, and false otherwise.
+ */
+boolean isLastChild () {
+ if (parentItem != null) {
+ return getIndex () == parentItem.items.length - 1;
+ }
+ return getIndex () == parent.items.length - 1;
+}
+boolean isSelected () {
+ return parent.getSelectionIndex (this) != -1;
+}
+/*
+ * The backgroundOnly argument indicates whether the item should only
+ * worry about painting its background color and selection.
+ *
+ * Returns a boolean indicating whether to abort drawing focus on the item.
+ * If the receiver is not the current focus item then this value is irrelevant.
+ */
+boolean paint (GC gc, TreeColumn column, boolean backgroundOnly) {
+ if (!parent.checkData (this, true)) return false;
+ int columnIndex = 0, orderedIndex = 0, x = 0;
+ if (column != null) {
+ columnIndex = column.getIndex ();
+ orderedIndex = column.getOrderIndex ();
+ x = column.getX ();
+ }
+
+ /*
+ * Capture GC attributes that will need to be restored later in the paint
+ * process to ensure that the item paints as intended without being affected
+ * by GC changes made in MeasureItem/EraseItem/PaintItem callbacks.
+ */
+ int oldAlpha = gc.getAlpha ();
+ boolean oldAdvanced = gc.getAdvanced ();
+ int oldAntialias = gc.getAntialias ();
+ Pattern oldBackgroundPattern = gc.getBackgroundPattern ();
+ Pattern oldForegroundPattern = gc.getForegroundPattern ();
+ int oldInterpolation = gc.getInterpolation ();
+ int[] oldLineDash = gc.getLineDash ();
+ int oldLineWidth = gc.getLineWidth ();
+ int oldTextAntialias = gc.getTextAntialias ();
+
+ if (parent.hooks (SWT.MeasureItem)) {
+ int contentWidth = getContentWidth (columnIndex);
+ int contentX = getContentX (columnIndex);
+ gc.setFont (getFont (columnIndex, false));
+ Event event = new Event ();
+ event.item = this;
+ event.gc = gc;
+ event.index = columnIndex;
+ event.x = contentX;
+ event.y = parent.getItemY (this);
+ event.width = contentWidth;
+ event.height = parent.itemHeight;
+ parent.sendEvent (SWT.MeasureItem, event);
+ event.gc = null;
+ if (gc.isDisposed ()) return false;
+ gc.setAlpha (oldAlpha);
+ gc.setAntialias (oldAntialias);
+ gc.setBackgroundPattern (oldBackgroundPattern);
+ gc.setForegroundPattern (oldForegroundPattern);
+ gc.setInterpolation (oldInterpolation);
+ gc.setLineDash (oldLineDash);
+ gc.setLineWidth (oldLineWidth);
+ gc.setTextAntialias (oldTextAntialias);
+ gc.setAdvanced (oldAdvanced);
+ if (isDisposed ()) return false;
+ if (parent.itemHeight != event.height) {
+ parent.customHeightSet = true;
+ boolean update = parent.setItemHeight (event.height + 2 * parent.getCellPadding ());
+ if (update) parent.redraw ();
+ }
+ if (parent.columns.length == 0) {
+ int change = event.width - (customWidth != -1 ? customWidth : contentWidth);
+ if (event.width != contentWidth || customWidth != -1) customWidth = event.width;
+ if (change != 0) { /* scrollbar may be affected since no columns */
+ parent.updateHorizontalBar (contentX + event.width, change);
+ // TODO what if clip is too small now?
+ }
+ }
+ }
+
+ /* if this cell is completely to the right of the client area then there's no need to paint it */
+ Rectangle clientArea = parent.clientArea;
+ if (clientArea.x + clientArea.width < x) return false;
+
+ Rectangle cellBounds = getCellBounds (columnIndex);
+ if (parent.linesVisible) {
+ cellBounds.y++;
+ cellBounds.height--;
+ }
+ int cellRightX = 0;
+ if (column != null) {
+ cellRightX = column.getX () + column.width;
+ } else {
+ cellRightX = cellBounds.x + cellBounds.width;
+ }
+
+ /* restrict the clipping region to the cell */
+ gc.setClipping (x, cellBounds.y, clientArea.width - x, cellBounds.height);
+
+ int y = parent.getItemY (this);
+ int itemHeight = parent.itemHeight;
+
+ /* draw the parent background color/image of this cell */
+ if (column == null) {
+ parent.drawBackground (gc, 0, y, clientArea.width, itemHeight);
+ } else {
+ int fillWidth = cellBounds.width;
+ if (parent.linesVisible) fillWidth--;
+ parent.drawBackground (gc, cellBounds.x, cellBounds.y, fillWidth, cellBounds.height);
+ }
+
+ boolean isSelected = isSelected ();
+ boolean isFocusItem = parent.focusItem == this && parent.isFocusControl ();
+ boolean drawBackground = true;
+ boolean drawForeground = true;
+ boolean drawSelection = isSelected;
+ boolean drawFocus = isFocusItem;
+ if (parent.hooks (SWT.EraseItem)) {
+ drawBackground = background != null || (cellBackgrounds != null && cellBackgrounds [columnIndex] != null);
+ gc.setFont (getFont (columnIndex, false));
+ if (isSelected && (columnIndex == 0 || (parent.style & SWT.FULL_SELECTION) != 0)) {
+ gc.setForeground (display.getSystemColor (SWT.COLOR_LIST_SELECTION_TEXT));
+ gc.setBackground (display.getSystemColor (SWT.COLOR_LIST_SELECTION));
+ } else {
+ gc.setForeground (getForeground (columnIndex));
+ gc.setBackground (getBackground (columnIndex));
+ }
+ Event event = new Event ();
+ event.item = this;
+ event.gc = gc;
+ event.index = columnIndex;
+ event.doit = true;
+ event.detail = SWT.FOREGROUND;
+ if (drawBackground) event.detail |= SWT.BACKGROUND;
+ if (isSelected) event.detail |= SWT.SELECTED;
+ if (isFocusItem) event.detail |= SWT.FOCUSED;
+ event.x = cellBounds.x;
+ event.y = cellBounds.y;
+ event.width = cellBounds.width;
+ event.height = cellBounds.height;
+ gc.setClipping (cellBounds);
+ parent.sendEvent (SWT.EraseItem, event);
+ event.gc = null;
+ if (gc.isDisposed ()) return false;
+ gc.setAlpha (oldAlpha);
+ gc.setAntialias (oldAntialias);
+ gc.setBackgroundPattern (oldBackgroundPattern);
+ gc.setClipping (cellBounds);
+ gc.setForegroundPattern (oldForegroundPattern);
+ gc.setInterpolation (oldInterpolation);
+ gc.setLineDash (oldLineDash);
+ gc.setLineWidth (oldLineWidth);
+ gc.setTextAntialias (oldTextAntialias);
+ gc.setAdvanced (oldAdvanced);
+ if (isDisposed ()) return false;
+ if (!event.doit) {
+ drawBackground = drawForeground = drawSelection = drawFocus = false;
+ } else {
+ drawBackground = drawBackground && (event.detail & SWT.BACKGROUND) != 0;
+ drawForeground = (event.detail & SWT.FOREGROUND) != 0;
+ drawSelection = isSelected && (event.detail & SWT.SELECTED) != 0;
+ drawFocus = isFocusItem && (event.detail & SWT.FOCUSED) != 0;
+ }
+ }
+
+ /* draw the cell's set background if appropriate */
+ if (drawBackground) {
+ gc.setBackground (getBackground (columnIndex));
+ if (columnIndex == 0 && (column == null || column.getOrderIndex () == 0)) {
+ Rectangle focusBounds = getFocusBounds ();
+ int fillWidth = 0;
+ if (column == null) {
+ fillWidth = focusBounds.width;
+ } else {
+ fillWidth = column.width - focusBounds.x;
+ if (parent.linesVisible) fillWidth--;
+ }
+ gc.fillRectangle (focusBounds.x, focusBounds.y, fillWidth, focusBounds.height);
+ } else {
+ int fillWidth = cellBounds.width;
+ gc.fillRectangle (cellBounds.x, cellBounds.y, fillWidth, cellBounds.height);
+ }
+ }
+
+ /* draw the selection bar if the receiver is selected */
+ if (drawSelection && isSelected && (orderedIndex == 0 || (parent.style & SWT.FULL_SELECTION) != 0)) {
+ gc.setBackground (display.getSystemColor (SWT.COLOR_LIST_SELECTION));
+ if (orderedIndex == 0) {
+ Rectangle focusBounds = getFocusBounds ();
+ int fillWidth = focusBounds.width;
+ if (parent.columns.length < 2 || (parent.style & SWT.FULL_SELECTION) == 0) {
+ fillWidth -= 2; /* space for right bound of focus rect */
+ }
+ if (fillWidth > 0) {
+ gc.fillRectangle (focusBounds.x + 1, focusBounds.y + 1, fillWidth, focusBounds.height - 2);
+ }
+ } else {
+ int fillWidth = column.width;
+ int[] columnOrder = parent.getColumnOrder ();
+ if (columnIndex == columnOrder [columnOrder.length - 1]) {
+ fillWidth -= 2; /* space for right bound of focus rect */
+ }
+ if (fillWidth > 0) {
+ gc.fillRectangle (
+ column.getX (),
+ cellBounds.y + 1,
+ fillWidth,
+ cellBounds.height - 2);
+ }
+ }
+ }
+
+ if (backgroundOnly) return false;
+
+ /* Draw column 0 decorations */
+ if (orderedIndex == 0) {
+ gc.setClipping (cellBounds);
+
+ /* Draw hierarchy connector lines */
+ Rectangle expanderBounds = getExpanderBounds ();
+ Color oldForeground = gc.getForeground ();
+ gc.setForeground (parent.getConnectorColor ());
+
+ /* Draw vertical line above expander */
+ int lineX = expanderBounds.x + expanderBounds.width / 2;
+ int y2 = expanderBounds.y;
+ if (items.length == 0) {
+ y2 += expanderBounds.height / 2;
+ }
+ /* Do not draw this line iff this is the very first item in the tree */
+ if (parentItem != null || getIndex () != 0) {
+ gc.drawLine (lineX, y, lineX, y2);
+ }
+
+ /* Draw vertical line below expander if the receiver has lower siblings */
+ if (!isLastChild ()) {
+ if (items.length != 0) y2 += expanderBounds.height;
+ gc.drawLine (lineX, y2, lineX, y + itemHeight);
+ }
+
+ /* Draw horizontal line to right of expander */
+ Point[] endpoints = getHconnectorEndpoints ();
+ gc.drawLine (endpoints [0].x, endpoints [0].y, endpoints [1].x - Tree.MARGIN_IMAGE, endpoints [1].y);
+
+ /*
+ * Draw hierarchy lines that are needed by other items that are shown below
+ * this item but whose parents are shown above (ie.- lines to the left of
+ * this item's connector line).
+ */
+ TreeItem item = parentItem;
+ while (item != null) {
+ if (!item.isLastChild ()) {
+ Rectangle itemExpanderBounds = item.getExpanderBounds ();
+ lineX = itemExpanderBounds.x + itemExpanderBounds.width / 2;
+ gc.drawLine (lineX, y, lineX, y + itemHeight);
+ }
+ item = item.parentItem;
+ }
+
+ gc.setForeground (oldForeground);
+
+ /* Draw expand/collapse image if receiver has children */
+ if (items.length > 0) {
+ Image image = expanded ? parent.getExpandedImage () : parent.getCollapsedImage ();
+ gc.drawImage (image, expanderBounds.x, expanderBounds.y);
+ }
+
+ /* Draw checkbox if parent Tree has style SWT.CHECK */
+ if ((parent.style & SWT.CHECK) != 0) {
+ Image baseImage = grayed ? parent.getGrayUncheckedImage () : parent.getUncheckedImage ();
+ Rectangle checkboxBounds = getCheckboxBounds ();
+ gc.drawImage (baseImage, checkboxBounds.x, checkboxBounds.y);
+ /* Draw checkmark if item is checked */
+ if (checked) {
+ Image checkmarkImage = parent.getCheckmarkImage ();
+ Rectangle checkmarkBounds = checkmarkImage.getBounds ();
+ int xInset = (checkboxBounds.width - checkmarkBounds.width) / 2;
+ int yInset = (checkboxBounds.height - checkmarkBounds.height) / 2;
+ gc.drawImage (checkmarkImage, checkboxBounds.x + xInset, checkboxBounds.y + yInset);
+ }
+ }
+ }
+
+ if (drawForeground) {
+ Image image = getImage (columnIndex, false);
+ String text = getDisplayText (columnIndex);
+ Rectangle imageArea = getImageBounds (columnIndex);
+ int startX = imageArea.x;
+
+ /* while painting the cell's content restrict the clipping region */
+ int padding = parent.getCellPadding ();
+ gc.setClipping (
+ startX,
+ cellBounds.y + padding - (parent.linesVisible ? 1 : 0),
+ cellRightX - startX - padding,
+ cellBounds.height - 2 * (padding - (parent.linesVisible ? 1 : 0)));
+
+ /* draw the image */
+ if (image != null) {
+ Rectangle imageBounds = image.getBounds ();
+ gc.drawImage (
+ image,
+ 0, 0, /* source x, y */
+ imageBounds.width, imageBounds.height, /* source width, height */
+ imageArea.x, imageArea.y, /* dest x, y */
+ imageArea.width, imageArea.height); /* dest width, height */
+ }
+
+ /* draw the text */
+ if (text.length () > 0) {
+ gc.setFont (getFont (columnIndex, false));
+ int fontHeight = getFontHeight (columnIndex);
+ if (drawSelection && (orderedIndex == 0 || (parent.style & SWT.FULL_SELECTION) != 0)) {
+ gc.setForeground (display.getSystemColor (SWT.COLOR_LIST_SELECTION_TEXT));
+ } else {
+ if (!isSelected || drawSelection) {
+ gc.setForeground (getForeground (columnIndex));
+ }
+ }
+ x = getTextX (columnIndex) + MARGIN_TEXT;
+ gc.drawString (text, x, y + (itemHeight - fontHeight) / 2, true);
+ }
+ }
+
+ if (parent.hooks (SWT.PaintItem)) {
+ int contentWidth = getContentWidth (columnIndex);
+ int contentX = getContentX (columnIndex);
+ gc.setFont (getFont (columnIndex, false));
+ if (isSelected && (columnIndex == 0 || (parent.style & SWT.FULL_SELECTION) != 0)) {
+ gc.setForeground (display.getSystemColor (SWT.COLOR_LIST_SELECTION_TEXT));
+ gc.setBackground (display.getSystemColor (SWT.COLOR_LIST_SELECTION));
+ } else {
+ gc.setForeground (getForeground (columnIndex));
+ gc.setBackground (getBackground (columnIndex));
+ }
+ Event event = new Event ();
+ event.item = this;
+ event.gc = gc;
+ event.index = columnIndex;
+ if (isSelected) event.detail |= SWT.SELECTED;
+ if (drawFocus) event.detail |= SWT.FOCUSED;
+ event.x = contentX;
+ event.y = cellBounds.y;
+ event.width = contentWidth;
+ event.height = cellBounds.height;
+ gc.setClipping (cellBounds);
+ parent.sendEvent (SWT.PaintItem, event);
+ event.gc = null;
+ if (gc.isDisposed ()) return false;
+ gc.setAlpha (oldAlpha);
+ gc.setAntialias (oldAntialias);
+ gc.setBackgroundPattern (oldBackgroundPattern);
+ gc.setClipping (cellBounds);
+ gc.setForegroundPattern (oldForegroundPattern);
+ gc.setInterpolation (oldInterpolation);
+ gc.setLineDash (oldLineDash);
+ gc.setLineWidth (oldLineWidth);
+ gc.setTextAntialias (oldTextAntialias);
+ gc.setAdvanced (oldAdvanced);
+ drawFocus = isFocusItem && (event.detail & SWT.FOCUSED) != 0;
+ }
+
+ return isFocusItem && !drawFocus;
+}
+/*
+ * Redraw part of the receiver. If either EraseItem or PaintItem is hooked then
+ * only full cells should be damaged, so adjust accordingly. If neither of these
+ * events are hooked then the exact bounds given for damaging can be used.
+ */
+void redraw (int x, int y, int width, int height, int columnIndex) {
+ if (!parent.hooks (SWT.EraseItem) && !parent.hooks (SWT.PaintItem)) {
+ parent.redraw (x, y, width, height, false);
+ return;
+ }
+ Rectangle cellBounds = getCellBounds (columnIndex);
+ parent.redraw (cellBounds.x, cellBounds.y, cellBounds.width, cellBounds.height, false);
+}
+void redrawItem () {
+ if (!isAvailable ()) return;
+ parent.redraw (0, parent.getItemY (this), parent.clientArea.width, parent.itemHeight, false);
+}
+/*
+ * Updates internal structures in the receiver and its child items to handle the removal of a column.
+ */
+void removeColumn (TreeColumn column, int index, int orderedIndex) {
+ int columnCount = parent.columns.length;
+
+ if (columnCount == 0) {
+ /* reverts to normal tree when last column disposed */
+ cellBackgrounds = cellForegrounds = null;
+ displayTexts = null;
+ cellFonts = null;
+ fontHeights = null;
+ GC gc = new GC (parent);
+ computeTextWidths (gc);
+ gc.dispose ();
+ /* notify all child items as well */
+ for (int i = 0; i < items.length; i++) {
+ items [i].removeColumn (column, index, orderedIndex);
+ }
+ return;
+ }
+
+ String[] newTexts = new String [columnCount];
+ System.arraycopy (texts, 0, newTexts, 0, index);
+ System.arraycopy (texts, index + 1, newTexts, index, columnCount - index);
+ texts = newTexts;
+
+ Image[] newImages = new Image [columnCount];
+ System.arraycopy (images, 0, newImages, 0, index);
+ System.arraycopy (images, index + 1, newImages, index, columnCount - index);
+ images = newImages;
+
+ int[] newTextWidths = new int [columnCount];
+ System.arraycopy (textWidths, 0, newTextWidths, 0, index);
+ System.arraycopy (textWidths, index + 1, newTextWidths, index, columnCount - index);
+ textWidths = newTextWidths;
+
+ String[] newDisplayTexts = new String [columnCount];
+ System.arraycopy (displayTexts, 0, newDisplayTexts, 0, index);
+ System.arraycopy (displayTexts, index + 1, newDisplayTexts, index, columnCount - index);
+ displayTexts = newDisplayTexts;
+
+ if (cellBackgrounds != null) {
+ Color[] newCellBackgrounds = new Color [columnCount];
+ System.arraycopy (cellBackgrounds, 0, newCellBackgrounds, 0, index);
+ System.arraycopy (cellBackgrounds, index + 1, newCellBackgrounds, index, columnCount - index);
+ cellBackgrounds = newCellBackgrounds;
+ }
+ if (cellForegrounds != null) {
+ Color[] newCellForegrounds = new Color [columnCount];
+ System.arraycopy (cellForegrounds, 0, newCellForegrounds, 0, index);
+ System.arraycopy (cellForegrounds, index + 1, newCellForegrounds, index, columnCount - index);
+ cellForegrounds = newCellForegrounds;
+ }
+ if (cellFonts != null) {
+ Font[] newCellFonts = new Font [columnCount];
+ System.arraycopy (cellFonts, 0, newCellFonts, 0, index);
+ System.arraycopy (cellFonts, index + 1, newCellFonts, index, columnCount - index);
+ cellFonts = newCellFonts;
+
+ int[] newFontHeights = new int [columnCount];
+ System.arraycopy (fontHeights, 0, newFontHeights, 0, index);
+ System.arraycopy (fontHeights, index + 1, newFontHeights, index, columnCount - index);
+ fontHeights = newFontHeights;
+ }
+
+ if (index == 0) {
+ text = texts [0] != null ? texts [0] : ""; //$NON-NLS-1$
+ texts [0] = null;
+ image = images [0];
+ images [0] = null;
+ }
+
+ if (orderedIndex == 0) {
+ /*
+ * The new first ordered column will not have as much width available to it as it did when
+ * it was the second ordered column since it now has to show hierarchy decorations as well,
+ * so recompute its displayText.
+ */
+ int firstColumnIndex = parent.getOrderedColumns () [0].getIndex ();
+ GC gc = new GC (parent);
+ gc.setFont (getFont (firstColumnIndex, false));
+ computeDisplayText (firstColumnIndex, gc);
+ gc.dispose ();
+ }
+ if (columnCount < 2) {
+ texts = null;
+ images = null;
+ }
+
+ /* notify all child items as well */
+ for (int i = 0; i < items.length; i++) {
+ items [i].removeColumn (column, index, orderedIndex);
+ }
+}
+/**
+ * Removes all of the items from the receiver.
+ * <p>
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @since 3.1
+ */
+public void removeAll () {
+ checkWidget ();
+ if (items.length == 0) return;
+
+ int lastAvailableIndex = parent.availableItemsCount - 1;
+ /* for performance do this upfront for whole descendent chain */
+ TreeItem focusItem = parent.focusItem;
+ if (focusItem != null && focusItem.hasAncestor (this)) {
+ parent.setFocusItem (this, false);
+ }
+ while (items.length > 0) {
+ items [0].dispose (true);
+ removeItem (items [0], 0);
+ }
+ items = Tree.NO_ITEMS;
+ expanded = false;
+ if (isAvailable ()) {
+ parent.redrawItems (availableIndex, lastAvailableIndex, false);
+ }
+}
+/*
+ * Removes a child item from the receiver.
+ */
+void removeItem (TreeItem item, int index) {
+ if (isDisposed ()) return;
+ TreeItem[] newItems = new TreeItem [items.length - 1];
+ System.arraycopy (items, 0, newItems, 0, index);
+ System.arraycopy (items, index + 1, newItems, index, newItems.length - index);
+ items = newItems;
+ if (items.length == 0) {
+ items = Tree.NO_ITEMS;
+ /* condition below handles creation of item within Expand callback */
+ if (!parent.inExpand) {
+ expanded = false;
+ if (isInViewport ()) {
+ Rectangle bounds = getExpanderBounds (); /* expander box no longer needed */
+ parent.redraw (bounds.x, bounds.y, bounds.width, bounds.height, false);
+ }
+ }
+ return;
+ }
+}
+/**
+ * Sets the receiver's background color to the color specified
+ * by the argument, or to the default system color for the item
+ * if the argument is null.
+ *
+ * @param color the new color (or null)
+ *
+ * @exception IllegalArgumentException <ul>
+ * <li>ERROR_INVALID_ARGUMENT - if the argument has been disposed</li>
+ * </ul>
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @since 2.0
+ *
+ */
+public void setBackground (Color color) {
+ checkWidget ();
+ if (color != null && color.isDisposed ()) {
+ SWT.error (SWT.ERROR_INVALID_ARGUMENT);
+ }
+ Color oldColor = background;
+ if (oldColor == color) return;
+ background = color;
+ if (oldColor != null && oldColor.equals (color)) return;
+ if ((parent.style & SWT.VIRTUAL) != 0) cached = true;
+ redrawItem ();
+}
+/**
+ * Sets the background color at the given column index in the receiver
+ * to the color specified by the argument, or to the default system color for the item
+ * if the argument is null.
+ *
+ * @param index the column index
+ * @param color the new color (or null)
+ *
+ * @exception IllegalArgumentException <ul>
+ * <li>ERROR_INVALID_ARGUMENT - if the argument has been disposed</li>
+ * </ul>
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @since 3.1
+ *
+ */
+public void setBackground (int columnIndex, Color color) {
+ checkWidget ();
+ if (color != null && color.isDisposed ()) {
+ SWT.error (SWT.ERROR_INVALID_ARGUMENT);
+ }
+ int validColumnCount = Math.max (1, parent.columns.length);
+ if (!(0 <= columnIndex && columnIndex < validColumnCount)) return;
+ if (cellBackgrounds == null) {
+ if (color == null) return;
+ cellBackgrounds = new Color [validColumnCount];
+ }
+ Color oldColor = cellBackgrounds [columnIndex];
+ if (oldColor == color) return;
+ cellBackgrounds [columnIndex] = color;
+ if (oldColor != null && oldColor.equals (color)) return;
+ if ((parent.style & SWT.VIRTUAL) != 0) cached = true;
+ if (isInViewport ()) {
+ Rectangle bounds = getCellBounds (columnIndex);
+ parent.redraw (bounds.x, bounds.y, bounds.width, bounds.height, false);
+ }
+}
+/**
+ * Sets the checked state of the receiver.
+ * <p>
+ *
+ * @param checked the new checked state
+ *
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public void setChecked (boolean value) {
+ checkWidget ();
+ if ((parent.getStyle () & SWT.CHECK) == 0) return;
+ if (checked == value) return;
+ checked = value;
+ if ((parent.style & SWT.VIRTUAL) != 0) cached = true;
+ if (isInViewport ()) {
+ if (parent.hooks (SWT.EraseItem) || parent.hooks (SWT.PaintItem)) {
+ redrawItem ();
+ } else {
+ Rectangle bounds = getCheckboxBounds ();
+ parent.redraw (bounds.x, bounds.y, bounds.width, bounds.height, false);
+ }
+ }
+}
+/**
+ * Sets the expanded state of the receiver.
+ * <p>
+ *
+ * @param expanded the new expanded state
+ *
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public void setExpanded (boolean value) {
+ checkWidget ();
+ if (expanded == value) return;
+ if (items.length == 0) return;
+ if (parent.inExpand) return;
+ if ((parent.style & SWT.VIRTUAL) != 0) cached = true;
+ if (value) {
+ expanded = value;
+ if (availableIndex == -1) return;
+
+ TreeItem[] availableDescendents = computeAvailableDescendents ();
+ int descendentsCount = availableDescendents.length;
+ if (availableIndex != parent.availableItemsCount - 1) {
+ /* the receiver is not the last available item */
+ Rectangle clientArea = parent.clientArea;
+ int y = parent.getItemY (this) + parent.itemHeight;
+ if (0 < y && y < clientArea.height) {
+ if (parent.drawCount <= 0) {
+ parent.update ();
+ GC gc = new GC (parent);
+ gc.copyArea (
+ 0, y,
+ clientArea.width, clientArea.height - y,
+ 0, y + ((descendentsCount - 1) * parent.itemHeight));
+ gc.dispose ();
+ }
+ }
+ }
+
+ parent.makeDescendentsAvailable (this, availableDescendents);
+
+ /* update scrollbars */
+ int rightX = 0;
+ for (int i = 1; i < availableDescendents.length; i++) {
+ Rectangle bounds = availableDescendents [i].getBounds (false);
+ rightX = Math.max (rightX, bounds.x + bounds.width);
+ }
+ parent.updateHorizontalBar (rightX, rightX);
+ parent.updateVerticalBar ();
+ /*
+ * If new item is above viewport then adjust topIndex and the vertical scrollbar
+ * so that the current viewport items will not change.
+ */
+ if (availableIndex < parent.topIndex) {
+ parent.topIndex += descendentsCount - 1;
+ ScrollBar vBar = parent.getVerticalBar ();
+ if (vBar != null) vBar.setSelection (parent.topIndex);
+ return;
+ }
+
+ int redrawStart = availableIndex + 1;
+ int redrawEnd = redrawStart + descendentsCount - 2;
+ parent.redrawItems (redrawStart, redrawEnd, false);
+ } else {
+ TreeItem[] descendents = computeAvailableDescendents ();
+ expanded = value;
+ if (availableIndex == -1) return;
+ Rectangle clientArea = parent.clientArea;
+
+ int y = parent.getItemY (this) + parent.itemHeight;
+ int startY = y + (descendents.length - 1) * parent.itemHeight;
+ if (y < clientArea.height && 0 < startY) { /* determine whether any visual update is actually needed */
+ if (parent.drawCount <= 0) {
+ parent.update ();
+ GC gc = new GC (parent);
+ gc.copyArea (0, startY, clientArea.width, clientArea.height - startY, 0, y);
+ gc.dispose ();
+ int redrawY = y + Math.max (0, clientArea.height - startY);
+ parent.redraw (0, redrawY, clientArea.width, clientArea.height - redrawY, false);
+ }
+ }
+
+ parent.makeDescendentsUnavailable (this, descendents);
+
+ /*
+ * If all collapsed items are above the viewport then adjust topIndex and
+ * the vertical scrollbar so that the current viewport items will not change.
+ */
+ int bottomIndex = availableIndex + descendents.length - 1;
+ if (bottomIndex < parent.topIndex) {
+ parent.topIndex = parent.topIndex - descendents.length + 1;
+ ScrollBar vBar = parent.getVerticalBar ();
+ if (vBar != null) vBar.setSelection (parent.topIndex);
+ }
+
+ parent.updateHorizontalBar ();
+ parent.updateVerticalBar ();
+
+ /* move focus (and selection if SWT.SINGLE) to item if a descendent had focus */
+ TreeItem focusItem = parent.focusItem;
+ if (focusItem != null && focusItem != this && focusItem.hasAncestor (this)) {
+ parent.setFocusItem (this, false);
+ if ((parent.style & SWT.SINGLE) != 0) {
+ parent.selectItem (this, false);
+ }
+ /* Fire an event since the selection is being changed automatically */
+ Event newEvent = new Event ();
+ newEvent.item = this;
+ parent.sendEvent (SWT.Selection, newEvent);
+ if (isDisposed ()) return;
+ parent.showItem (this);
+ parent.redrawItem (availableIndex, true);
+ }
+ }
+ /* redraw the receiver's expander box */
+ if (isInViewport ()) {
+ Rectangle bounds = getExpanderBounds ();
+ parent.redraw (bounds.x, bounds.y, bounds.width, bounds.height, false);
+ }
+}
+/**
+ * Sets the font that the receiver will use to paint textual information
+ * for this item to the font specified by the argument, or to the default font
+ * for that kind of control if the argument is null.
+ *
+ * @param font the new font (or null)
+ *
+ * @exception IllegalArgumentException <ul>
+ * <li>ERROR_INVALID_ARGUMENT - if the argument has been disposed</li>
+ * </ul>
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @since 3.0
+ */
+public void setFont (Font font) {
+ checkWidget ();
+ if (font != null && font.isDisposed ()) {
+ SWT.error (SWT.ERROR_INVALID_ARGUMENT);
+ }
+ Font oldFont = this.font;
+ if (oldFont == font) return;
+ this.font = font;
+ if (oldFont != null && oldFont.equals (font)) return;
+
+ Rectangle bounds = getBounds (false);
+ int oldRightX = bounds.x + bounds.width;
+ if ((parent.style & SWT.VIRTUAL) != 0) cached = true;
+
+ /* recompute cached values for string measurements */
+ GC gc = new GC (parent);
+ gc.setFont (getFont (false));
+ fontHeight = gc.getFontMetrics ().getHeight ();
+ computeDisplayTexts (gc);
+ computeTextWidths (gc);
+ gc.dispose ();
+
+ /* horizontal bar could be affected if tree has no columns */
+ if (parent.columns.length == 0) {
+ bounds = getBounds (false);
+ int newRightX = bounds.x + bounds.width;
+ parent.updateHorizontalBar (newRightX, newRightX - oldRightX);
+ }
+ redrawItem ();
+}
+/**
+ * Sets the font that the receiver will use to paint textual information
+ * for the specified cell in this item to the font specified by the
+ * argument, or to the default font for that kind of control if the
+ * argument is null.
+ *
+ * @param index the column index
+ * @param font the new font (or null)
+ *
+ * @exception IllegalArgumentException <ul>
+ * <li>ERROR_INVALID_ARGUMENT - if the argument has been disposed</li>
+ * </ul>
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @since 3.1
+ */
+public void setFont (int columnIndex, Font font) {
+ checkWidget ();
+ if (font != null && font.isDisposed ()) {
+ SWT.error (SWT.ERROR_INVALID_ARGUMENT);
+ }
+
+ int validColumnCount = Math.max (1, parent.columns.length);
+ if (!(0 <= columnIndex && columnIndex < validColumnCount)) return;
+ if (cellFonts == null) {
+ if (font == null) return;
+ cellFonts = new Font [validColumnCount];
+ }
+ Font oldFont = cellFonts [columnIndex];
+ if (oldFont == font) return;
+ cellFonts [columnIndex] = font;
+ if (oldFont != null && oldFont.equals (font)) return;
+ if ((parent.style & SWT.VIRTUAL) != 0) cached = true;
+
+ /* recompute cached values for string measurements */
+ GC gc = new GC (parent);
+ gc.setFont (getFont (columnIndex, false));
+ if (fontHeights == null) fontHeights = new int [validColumnCount];
+ fontHeights [columnIndex] = gc.getFontMetrics ().getHeight ();
+ computeDisplayText (columnIndex, gc);
+ gc.dispose ();
+
+ if (isInViewport ()) {
+ Rectangle bounds = getCellBounds (columnIndex);
+ parent.redraw (bounds.x, bounds.y, bounds.width, bounds.height, false);
+ }
+}
+/**
+ * Sets the receiver's foreground color to the color specified
+ * by the argument, or to the default system color for the item
+ * if the argument is null.
+ *
+ * @param color the new color (or null)
+ *
+ * @since 2.0
+ *
+ * @exception IllegalArgumentException <ul>
+ * <li>ERROR_INVALID_ARGUMENT - if the argument has been disposed</li>
+ * </ul>
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @since 2.0
+ *
+ */
+public void setForeground (Color color) {
+ checkWidget ();
+ if (color != null && color.isDisposed ()) {
+ SWT.error (SWT.ERROR_INVALID_ARGUMENT);
+ }
+ Color oldColor = foreground;
+ if (oldColor == color) return;
+ foreground = color;
+ if (oldColor != null && oldColor.equals (color)) return;
+ if ((parent.style & SWT.VIRTUAL) != 0) cached = true;
+ redrawItem ();
+}
+/**
+ * Sets the foreground color at the given column index in the receiver
+ * to the color specified by the argument, or to the default system color for the item
+ * if the argument is null.
+ *
+ * @param index the column index
+ * @param color the new color (or null)
+ *
+ * @exception IllegalArgumentException <ul>
+ * <li>ERROR_INVALID_ARGUMENT - if the argument has been disposed</li>
+ * </ul>
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @since 3.1
+ *
+ */
+public void setForeground (int columnIndex, Color value) {
+ checkWidget ();
+ if (value != null && value.isDisposed ()) {
+ SWT.error (SWT.ERROR_INVALID_ARGUMENT);
+ }
+ int validColumnCount = Math.max (1, parent.columns.length);
+ if (!(0 <= columnIndex && columnIndex < validColumnCount)) return;
+ if (cellForegrounds == null) {
+ if (value == null) return;
+ cellForegrounds = new Color [validColumnCount];
+ }
+ Color oldColor = cellForegrounds [columnIndex];
+ if (oldColor == value) return;
+ cellForegrounds [columnIndex] = value;
+ if (oldColor != null && oldColor.equals (value)) return;
+ if ((parent.style & SWT.VIRTUAL) != 0) cached = true;
+ if (isInViewport ()) {
+ redraw (
+ getTextX (columnIndex),
+ parent.getItemY (this),
+ textWidths [columnIndex] + 2 * MARGIN_TEXT,
+ parent.itemHeight,
+ columnIndex);
+ }
+}
+/**
+ * Sets the grayed state of the checkbox for this item. This state change
+ * only applies if the Tree was created with the SWT.CHECK style.
+ *
+ * @param grayed the new grayed state of the checkbox
+ *
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ */
+public void setGrayed (boolean value) {
+ checkWidget ();
+ if ((parent.getStyle () & SWT.CHECK) == 0) return;
+ if (grayed == value) return;
+ grayed = value;
+ if ((parent.style & SWT.VIRTUAL) != 0) cached = true;
+ if (isInViewport ()) {
+ Rectangle bounds = getCheckboxBounds ();
+ parent.redraw (bounds.x, bounds.y, bounds.width, bounds.height, false);
+ }
+}
+public void setImage (Image value) {
+ checkWidget ();
+ setImage (0, value);
+}
+/**
+ * Sets the image for multiple columns in the tree.
+ *
+ * @param images the array of new images
+ *
+ * @exception IllegalArgumentException <ul>
+ * <li>ERROR_NULL_ARGUMENT - if the array of images is null</li>
+ * <li>ERROR_INVALID_ARGUMENT - if one of the images has been disposed</li>
+ * </ul>
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @since 3.1
+ */
+public void setImage (Image[] value) {
+ checkWidget ();
+ if (value == null) error (SWT.ERROR_NULL_ARGUMENT);
+
+ // TODO make a smarter implementation of this
+ for (int i = 0; i < value.length; i++) {
+ if (value [i] != null) setImage (i, value [i]);
+ }
+}
+/**
+ * Sets the receiver's image at a column.
+ *
+ * @param index the column index
+ * @param image the new image
+ *
+ * @exception IllegalArgumentException <ul>
+ * <li>ERROR_INVALID_ARGUMENT - if the image has been disposed</li>
+ * </ul>
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @since 3.1
+ */
+public void setImage (int columnIndex, Image value) {
+ checkWidget ();
+ if (value != null && value.isDisposed ()) error (SWT.ERROR_INVALID_ARGUMENT);
+
+ TreeColumn[] columns = parent.columns;
+ int validColumnCount = Math.max (1, columns.length);
+ if (!(0 <= columnIndex && columnIndex < validColumnCount)) return;
+ Image image = getImage (columnIndex, false);
+ if (value == image) return;
+ if (value != null && value.equals (image)) return;
+ if (columnIndex == 0) {
+ super.setImage (value);
+ } else {
+ images [columnIndex] = value;
+ }
+ if ((parent.style & SWT.VIRTUAL) != 0) cached = true;
+
+ /*
+ * An image width change may affect the space available for the item text, so
+ * recompute the displayText if there are columns.
+ */
+ if (columns.length > 0) {
+ GC gc = new GC (parent);
+ gc.setFont (getFont (columnIndex, false));
+ computeDisplayText (columnIndex, gc);
+ gc.dispose ();
+ }
+
+ if (value == null) {
+ redrawItem (); // TODO why the whole item?
+ return;
+ }
+
+ if (columns.length == 0) {
+ if (parent.imageHeight == 0) {
+ /* this is the first image being put into the parent Tree */
+ Rectangle bounds = value.getBounds ();
+ parent.orderedCol0imageWidth = bounds.width;
+ parent.setImageHeight (bounds.height);
+ parent.redrawItems (0, parent.availableItemsCount - 1, false);
+ } else {
+ redrawItem ();
+ }
+ return;
+ }
+
+ /* there are 1+ columns */
+ TreeColumn column = columns [columnIndex];
+ int orderedIndex = column.getOrderIndex ();
+ Rectangle bounds = value.getBounds ();
+ if (column.itemImageWidth == 0) column.itemImageWidth = bounds.width;
+
+ if (parent.imageHeight == 0) {
+ /* this is the first image being put into the parent Tree */
+ int oldItemHeight = parent.itemHeight;
+ parent.setImageHeight (bounds.height);
+
+ if (orderedIndex == 0) { /* the first ordered column */
+ parent.orderedCol0imageWidth = bounds.width;
+ /*
+ * All column 0 cells will now have less room available for their texts,
+ * so all items must now recompute their column 0 displayTexts.
+ */
+ TreeItem[] rootItems = parent.items;
+ GC gc = new GC (parent);
+ for (int i = 0; i < rootItems.length; i++) {
+ rootItems [i].updateColumnWidth (column, gc);
+ }
+ gc.dispose ();
+ if (oldItemHeight != parent.itemHeight) {
+ /* the item height grew as a result of the new image height, so redraw everything */
+ parent.redraw ();
+ } else {
+ /* redraw the column since all items should now have image space */
+ parent.redraw (column.getX (), 0, column.width, parent.clientArea.height, false);
+ }
+ } else { /* not the first ordered column */
+ if (oldItemHeight != parent.itemHeight) {
+ /* the item height grew as a result of the new image height, so redraw everything */
+ parent.redraw ();
+ } else {
+ redrawItem ();
+ }
+ }
+ return;
+ }
+
+ if (orderedIndex == 0 && parent.orderedCol0imageWidth == 0) {
+ /* this is the first image being put into the current ordered column 0 */
+ parent.orderedCol0imageWidth = bounds.width;
+ /*
+ * All column 0 cells will now have less room available for their texts,
+ * so all items must now recompute their column 0 displayTexts.
+ */
+ TreeItem[] rootItems = parent.items;
+ GC gc = new GC (parent);
+ for (int i = 0; i < rootItems.length; i++) {
+ rootItems [i].updateColumnWidth (column, gc);
+ }
+ gc.dispose ();
+ parent.redraw (column.getX (), 0, column.width, parent.clientArea.height, false);
+ return;
+ }
+
+ redrawItem (); // TODO why the whole item?
+}
+/**
+ * Sets the number of child items contained in the receiver.
+ *
+ * @param count the number of items
+ *
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @since 3.2
+ */
+public void setItemCount (int count) {
+ checkWidget ();
+ count = Math.max (0, count);
+ if (count == items.length) return;
+ int redrawStart, redrawEnd;
+
+ /* if the new item count is less than the current count then remove all excess items from the end */
+ if (count < items.length) {
+ redrawStart = count > 0 ? items [count - 1].availableIndex : availableIndex;
+ redrawEnd = parent.availableItemsCount - 1;
+ for (int i = count; i < items.length; i++) {
+ items [i].dispose (true);
+ }
+ if (count == 0) {
+ items = Tree.NO_ITEMS;
+ } else {
+ TreeItem[] newItems = new TreeItem [count];
+ System.arraycopy (items, 0, newItems, 0, count);
+ items = newItems;
+ }
+ if (count == 0) expanded = false;
+ } else {
+ int oldAvailableDescendentCount = computeAvailableDescendentCount ();
+ int grow = count - items.length;
+ redrawStart = items.length == 0 ? availableIndex : items [items.length - 1].availableIndex;
+ redrawEnd = expanded && isAvailable () ? parent.availableItemsCount + grow - 1: redrawStart;
+ TreeItem[] newItems = new TreeItem [count];
+ System.arraycopy (items, 0, newItems, 0, items.length);
+ items = newItems;
+ for (int i = items.length - grow; i < count; i++) {
+ items [i] = new TreeItem (this, SWT.NONE, i, false);
+ }
+
+ if (expanded && availableIndex != -1) {
+ /* expand the availableItems array if necessary */
+ if (parent.availableItems.length < parent.availableItemsCount + grow) {
+ TreeItem[] newAvailableItems = new TreeItem [parent.availableItemsCount + grow];
+ System.arraycopy (parent.availableItems, 0, newAvailableItems, 0, parent.availableItemsCount);
+ parent.availableItems = newAvailableItems;
+ }
+ TreeItem[] availableItems = parent.availableItems;
+ /* shift items right to create space for the new available items */
+ int dest = availableIndex + oldAvailableDescendentCount + grow;
+ System.arraycopy (
+ availableItems,
+ availableIndex + oldAvailableDescendentCount,
+ availableItems,
+ dest,
+ availableItems.length - dest);
+ parent.availableItemsCount += grow;
+ /* copy new items in */
+ System.arraycopy (
+ items,
+ items.length - grow,
+ availableItems,
+ availableIndex + oldAvailableDescendentCount,
+ grow);
+ /* update availableIndex for all affected items */
+ for (int i = availableIndex + oldAvailableDescendentCount; i < parent.availableItemsCount; i++) {
+ availableItems [i].availableIndex = i;
+ }
+ }
+ }
+
+ if ((parent.style & SWT.VIRTUAL) != 0) cached = true;
+ if (availableIndex != -1) {
+ if (expanded) parent.updateVerticalBar ();
+ parent.redrawItems (redrawStart, redrawEnd, false);
+ }
+}
+public void setText (String value) {
+ checkWidget ();
+ Rectangle bounds = getBounds (false);
+ int oldRightX = bounds.x + bounds.width;
+ setText (0, value);
+ /* horizontal bar could be affected if tree has no columns */
+ if (parent.columns.length == 0) {
+ bounds = getBounds (false);
+ int newRightX = bounds.x + bounds.width;
+ parent.updateHorizontalBar (newRightX, newRightX - oldRightX);
+ }
+}
+/**
+ * Sets the text for multiple columns in the tree.
+ *
+ * @param strings the array of new strings
+ *
+ * @exception IllegalArgumentException <ul>
+ * <li>ERROR_NULL_ARGUMENT - if the text is null</li>
+ * </ul>
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @since 3.1
+ */
+public void setText (String[] value) {
+ checkWidget ();
+ if (value == null) error (SWT.ERROR_NULL_ARGUMENT);
+ Rectangle bounds = getBounds (false);
+ int oldRightX = bounds.x + bounds.width;
+ // TODO make a smarter implementation of this
+ for (int i = 0; i < value.length; i++) {
+ if (value [i] != null) setText (i, value [i]);
+ }
+ /* horizontal bar could be affected if tree has no columns */
+ if (parent.columns.length == 0) {
+ bounds = getBounds (false);
+ int newRightX = bounds.x + bounds.width;
+ parent.updateHorizontalBar (newRightX, newRightX - oldRightX);
+ }
+}
+/**
+ * Sets the receiver's text at a column
+ *
+ * @param index the column index
+ * @param string the new text
+ *
+ * @exception IllegalArgumentException <ul>
+ * <li>ERROR_NULL_ARGUMENT - if the text is null</li>
+ * </ul>
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+ * </ul>
+ *
+ * @since 3.1
+ */
+public void setText (int columnIndex, String value) {
+ checkWidget ();
+ if (value == null) error (SWT.ERROR_NULL_ARGUMENT);
+ int validColumnCount = Math.max (1, parent.columns.length);
+ if (!(0 <= columnIndex && columnIndex < validColumnCount)) return;
+ if (value.equals (getText (columnIndex, false))) return;
+ if (columnIndex == 0) {
+ super.setText (value);
+ } else {
+ texts [columnIndex] = value;
+ }
+ if ((parent.style & SWT.VIRTUAL) != 0) cached = true;
+
+ int oldWidth = textWidths [columnIndex];
+ GC gc = new GC (parent);
+ gc.setFont (getFont (columnIndex, false));
+ computeDisplayText (columnIndex, gc);
+ gc.dispose ();
+ if (availableIndex == -1) return;
+ if (parent.columns.length == 0) {
+ Rectangle bounds = getBounds (false);
+ int rightX = bounds.x + bounds.width;
+ parent.updateHorizontalBar (rightX, textWidths [columnIndex] - oldWidth);
+ }
+ if (isInViewport ()) {
+ redraw (
+ getTextX (columnIndex),
+ parent.getItemY (this),
+ Math.max (oldWidth, textWidths [columnIndex]) + 2 * MARGIN_TEXT,
+ parent.itemHeight,
+ columnIndex);
+ }
+}
+/*
+ * Perform any internal changes necessary to reflect a changed column width.
+ */
+void updateColumnWidth (TreeColumn column, GC gc) {
+ int columnIndex = column.getIndex ();
+ gc.setFont (getFont (columnIndex, false));
+ String oldDisplayText = displayTexts [columnIndex];
+ computeDisplayText (columnIndex, gc);
+
+ /* the cell must be damaged if there is custom drawing being done or if the alignment is not LEFT */
+ if (isInViewport ()) {
+ boolean columnIsLeft = (column.style & SWT.LEFT) != 0;
+ if (!columnIsLeft || parent.hooks (SWT.EraseItem) || parent.hooks (SWT.PaintItem)) {
+ Rectangle cellBounds = getCellBounds (columnIndex);
+ parent.redraw (cellBounds.x, cellBounds.y, cellBounds.width, cellBounds.height, false);
+ } else {
+ /* if the display text has changed then the cell text must be damaged in order to repaint */
+ if (oldDisplayText == null || !oldDisplayText.equals (displayTexts [columnIndex])) {
+ Rectangle cellBounds = getCellBounds (columnIndex);
+ int textX = getTextX (columnIndex);
+ parent.redraw (textX, cellBounds.y, cellBounds.x + cellBounds.width - textX, cellBounds.height, false);
+ }
+ }
+ }
+
+ for (int i = 0; i < items.length; i++) {
+ items [i].updateColumnWidth (column, gc);
+ }
+}
+
+/*
+ * The parent's font has changed, so if this font was being used by the receiver then
+ * recompute its cached text sizes using the gc argument. Pass this notification on to
+ * all child items as well.
+ */
+void updateFont (GC gc) {
+ if (font == null) { /* receiver is using the Tree's font */
+ computeDisplayTexts (gc);
+ computeTextWidths (gc);
+ }
+ /* pass notification on to all children */
+ for (int i = 0; i < items.length; i++) {
+ items [i].updateFont (gc);
+ }
+}
+}