Java tutorial
/******************************************************************************* * Copyright (c) 2002, 2017 Innoopract Informationssysteme GmbH 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: * Innoopract Informationssysteme GmbH - initial API and implementation * EclipseSource - ongoing development ******************************************************************************/ package org.eclipse.swt.widgets; import static org.eclipse.rap.rwt.internal.textsize.TextSizeUtil.stringExtent; import static org.eclipse.swt.internal.widgets.MarkupUtil.isMarkupEnabledFor; import static org.eclipse.swt.internal.widgets.MarkupUtil.isToolTipMarkupEnabledFor; import static org.eclipse.swt.internal.widgets.MarkupValidator.isValidationDisabledFor; import org.eclipse.rap.rwt.RWT; import org.eclipse.rap.rwt.internal.lifecycle.ProcessActionRunner; import org.eclipse.rap.rwt.internal.lifecycle.WidgetLCA; import org.eclipse.rap.rwt.internal.textsize.TextSizeUtil; import org.eclipse.rap.rwt.internal.theme.CssBoxDimensions; import org.eclipse.rap.rwt.internal.theme.Size; import org.eclipse.rap.rwt.internal.theme.ThemeAdapter; import org.eclipse.rap.rwt.template.Template; import org.eclipse.rap.rwt.theme.BoxDimensions; import org.eclipse.swt.SWT; import org.eclipse.swt.SWTException; import org.eclipse.swt.events.SelectionEvent; import org.eclipse.swt.events.SelectionListener; import org.eclipse.swt.graphics.Font; import org.eclipse.swt.graphics.Image; import org.eclipse.swt.graphics.Point; import org.eclipse.swt.graphics.Rectangle; import org.eclipse.swt.internal.SerializableCompatibility; import org.eclipse.swt.internal.widgets.ICellToolTipAdapter; import org.eclipse.swt.internal.widgets.ICellToolTipProvider; import org.eclipse.swt.internal.widgets.IControlAdapter; import org.eclipse.swt.internal.widgets.IItemHolderAdapter; import org.eclipse.swt.internal.widgets.ITableAdapter; import org.eclipse.swt.internal.widgets.ItemHolder; import org.eclipse.swt.internal.widgets.MarkupValidator; import org.eclipse.swt.internal.widgets.tablekit.TableLCA; import org.eclipse.swt.internal.widgets.tablekit.TableThemeAdapter; /** * 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 make sense to add <code>Control</code> children to it, * or set a layout on it. * </p><p> * <dl> * <dt><b>Styles:</b></dt> * <dd>SINGLE, MULTI, CHECK, FULL_SELECTION, HIDE_SELECTION, VIRTUAL</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> * * @since 1.0 */ public class Table extends Composite { // handle the fact that we have two item types to deal with private final class CompositeItemHolder implements IItemHolderAdapter<Item> { @Override public void add(Item item) { if (item instanceof TableColumn) { columnHolder.add((TableColumn) item); } else { String msg = "Only TableColumns may be added to CompositeItemHolder"; throw new IllegalArgumentException(msg); } } @Override public void insert(Item item, int index) { if (item instanceof TableColumn) { columnHolder.insert((TableColumn) item, index); } else { String msg = "Only TableColumns may be inserted to CompositeItemHolder"; throw new IllegalArgumentException(msg); } } @Override public void remove(Item item) { if (item instanceof TableColumn) { columnHolder.remove((TableColumn) item); } else { String msg = "Only TableColumns may be removed from CompositeItemHolder"; throw new IllegalArgumentException(msg); } } @Override public Item[] getItems() { TableItem[] items = getCreatedItems(); Item[] columns = columnHolder.getItems(); Item[] result = new Item[columns.length + items.length]; System.arraycopy(columns, 0, result, 0, columns.length); System.arraycopy(items, 0, result, columns.length, items.length); return result; } } private final class TableAdapter implements ITableAdapter, ICellToolTipAdapter, SerializableCompatibility { private String toolTipText; private ICellToolTipProvider provider; @Override public int getCheckWidthWithMargin() { return Table.this.getCheckSize().x; } @Override public int getCheckLeft() { return getCheckBoxMargin().left; } @Override public int getCheckWidth() { return getThemeAdapter().getCheckBoxImageSize(Table.this).width; } @Override public int getItemImageWidth(int columnIndex) { int result = 0; if (hasColumnImages(columnIndex)) { result = getItemImageSize().x; } return result; } @Override public int getFocusIndex() { return focusIndex; } @Override public void setFocusIndex(int focusIndex) { Table.this.setFocusIndex(focusIndex); } @Override public int getColumnLeftOffset(int columnIndex) { return Table.this.getColumnLeftOffset(columnIndex); } @Override public int getColumnLeft(TableColumn column) { int index = Table.this.indexOf(column); return columnHolder.getItem(index).getLeft(); } @Override public int getLeftOffset() { return leftOffset; } @Override public void setLeftOffset(int leftOffset) { Table.this.leftOffset = leftOffset; } @Override public void checkData() { Table.this.checkData(); } @Override public void checkData(final int index) { if ((Table.this.style & SWT.VIRTUAL) != 0) { ProcessActionRunner.add(new Runnable() { @Override public void run() { if (index >= 0 && index < itemCount) { TableItem item = _getItem(index); if (!item.isDisposed()) { Table.this.checkData(item, index); } } } }); } } @Override public int getDefaultColumnWidth() { int result = 0; TableItem[] items = Table.this.getCachedItems(); for (int i = 0; i < items.length; i++) { result = Math.max(result, items[i].getPackWidth(0)); } return result; } @Override public boolean isItemVirtual(int index) { boolean result = false; if ((style & SWT.VIRTUAL) != 0) { TableItem item = items[index]; result = item == null || !item.cached; } return result; } @Override public TableItem[] getCachedItems() { return Table.this.getCachedItems(); } @Override public TableItem[] getCreatedItems() { return Table.this.getCreatedItems(); } @Override public TableItem getMeasureItem() { return Table.this.getMeasureItem(); } @Override public ICellToolTipProvider getCellToolTipProvider() { return provider; } @Override public void setCellToolTipProvider(ICellToolTipProvider provider) { this.provider = provider; } @Override public String getCellToolTipText() { return toolTipText; } @Override public void setCellToolTipText(String toolTipText) { if (toolTipText != null && isToolTipMarkupEnabledFor(Table.this) && !isValidationDisabledFor(Table.this)) { MarkupValidator.getInstance().validate(toolTipText); } this.toolTipText = toolTipText; } @Override public int getFixedColumns() { return Table.this.getFixedColumns(); } @Override public boolean isFixedColumn(TableColumn column) { return Table.this.isFixedColumn(Table.this.indexOf(column)); } } /** * <strong>IMPORTANT:</strong> This field is <em>not</em> part of the SWT * public API. It is marked public only so that it can be shared * within the packages provided by SWT. It should never be accessed from * application code. */ public static final String ALWAYS_HIDE_SELECTION = Table.class.getName() + "#alwaysHideSelection"; private static final int GRID_WIDTH = 1; private static final int[] EMPTY_SELECTION = new int[0]; private transient CompositeItemHolder itemHolder; private final ITableAdapter tableAdapter; private int customItemHeight; private int itemCount; private TableItem[] items; private final ItemHolder<TableColumn> columnHolder; private int[] columnImageCount; private int[] columnOrder; private int[] selection; private boolean linesVisible; private boolean headerVisible; private boolean hasVScrollBar; private boolean hasHScrollBar; private int topIndex; int leftOffset; private int focusIndex; private TableColumn sortColumn; private int sortDirection; private Point itemImageSize; private BoxDimensions bufferedCellPadding; private int bufferedCellSpacing; private int preloadedItems; /** * 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 Widget#checkSubclass * @see Widget#getStyle */ public Table(Composite parent, int style) { super(parent, checkStyle(style)); focusIndex = -1; sortDirection = SWT.NONE; tableAdapter = new TableAdapter(); columnHolder = new ItemHolder<>(TableColumn.class); setTableEmpty(); selection = EMPTY_SELECTION; customItemHeight = -1; bufferedCellSpacing = -1; } @Override void initState() { removeState( /* CANVAS | */ THEME_BACKGROUND); } @Override @SuppressWarnings("unchecked") public <T> T getAdapter(Class<T> adapter) { if (adapter == IItemHolderAdapter.class) { if (itemHolder == null) { itemHolder = new CompositeItemHolder(); } return (T) itemHolder; } if (adapter == ITableAdapter.class || adapter == ICellToolTipAdapter.class) { return (T) tableAdapter; } if (adapter == WidgetLCA.class) { return (T) TableLCA.INSTANCE; } return super.getAdapter(adapter); } @Override public void setData(String key, Object value) { if (RWT.CUSTOM_ITEM_HEIGHT.equals(key)) { setCustomItemHeight(value); } else if (RWT.PRELOADED_ITEMS.equals(key)) { setPreloadedItems(value); } if (!RWT.MARKUP_ENABLED.equals(key) || !isMarkupEnabledFor(this)) { super.setData(key, value); } } /////////////////////////// // Column handling methods /** * 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 columnHolder.size(); } /** * 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(); return columnHolder.getItems(); } /** * 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(); return columnHolder.getItem(index); } /** * 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 tableColumn the search column * @return the index of the column * * @exception IllegalArgumentException <ul> * <li>ERROR_NULL_ARGUMENT - if the string 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 tableColumn) { checkWidget(); if (tableColumn == null) { SWT.error(SWT.ERROR_NULL_ARGUMENT); } return columnHolder.indexOf(tableColumn); } /** * 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 */ public void setColumnOrder(int[] order) { checkWidget(); if (order == null) { SWT.error(SWT.ERROR_NULL_ARGUMENT); } int columnCount = columnHolder.size(); if (order.length != columnCount) { SWT.error(SWT.ERROR_INVALID_ARGUMENT); } if (columnCount > 0) { int[] oldOrder = new int[columnCount]; System.arraycopy(columnOrder, 0, oldOrder, 0, columnOrder.length); boolean reorder = false; boolean[] seen = new boolean[columnCount]; for (int i = 0; i < order.length; i++) { int index = order[i]; if (index < 0 || index >= columnCount) { SWT.error(SWT.ERROR_INVALID_RANGE); } if (seen[index]) { SWT.error(SWT.ERROR_INVALID_ARGUMENT); } seen[index] = true; if (index != oldOrder[i]) { reorder = true; } } if (reorder) { System.arraycopy(order, 0, columnOrder, 0, columnOrder.length); for (int i = 0; i < seen.length; i++) { if (oldOrder[i] != columnOrder[i]) { TableColumn column = columnHolder.getItem(columnOrder[i]); column.notifyListeners(SWT.Move, new Event()); } } } } } /** * 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 */ public int[] getColumnOrder() { checkWidget(); int[] result; if (columnHolder.size() == 0) { result = new int[0]; } else { result = new int[columnOrder.length]; System.arraycopy(columnOrder, 0, result, 0, columnOrder.length); } return result; } //////////////////////// // Item handling methods /** * 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> */ public void setItemCount(int count) { checkWidget(); int oldItemCount = itemCount; int newItemCount = Math.max(0, count); if (newItemCount != oldItemCount && !isInDispose()) { int deleteIndex = oldItemCount - 1; while (deleteIndex >= newItemCount) { TableItem item = items[deleteIndex]; if (item != null && !item.isDisposed()) { item.dispose(); } else { destroyItem(null, deleteIndex); } deleteIndex--; } int length = Math.max(4, (newItemCount + 3) / 4 * 4); TableItem[] newItems = new TableItem[length]; System.arraycopy(items, 0, newItems, 0, Math.min(newItemCount, itemCount)); items = newItems; if ((style & SWT.VIRTUAL) == 0) { for (int i = itemCount; i < newItemCount; i++) { items[i] = new TableItem(this, SWT.NONE, i, true); } } itemCount = newItemCount; adjustTopIndex(); if (focusIndex > itemCount - 1) { adjustFocusIndex(); } if ((style & SWT.VIRTUAL) != 0) { updateScrollBars(); } redraw(); } } /** * 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 itemCount; } /** * 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[itemCount]; if ((style & SWT.VIRTUAL) != 0) { for (int i = 0; i < itemCount; i++) { result[i] = _getItem(i); } } else { System.arraycopy(items, 0, result, 0, itemCount); } 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> */ public TableItem getItem(int index) { checkWidget(); if (index < 0 || index >= itemCount) { SWT.error(SWT.ERROR_INVALID_RANGE); } return _getItem(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) { SWT.error(SWT.ERROR_NULL_ARGUMENT); } TableItem result = null; int headerHeight = getHeaderHeight(); Rectangle itemArea = getClientArea(); itemArea.y += headerHeight; if (itemArea.contains(point)) { int itemHeight = getItemHeight(); int index = ((point.y - headerHeight) / itemHeight) - 1; if (point.y == headerHeight || point.y % itemHeight != 0) { index++; } index += topIndex; if (index >= 0 && index < itemCount) { result = _getItem(index); } } return result; } /** * 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 string 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) { SWT.error(SWT.ERROR_NULL_ARGUMENT); } return item.parent == this ? item.index : -1; } /** * 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(); while (itemCount > 0) { removeItem(itemCount - 1); } } /** * 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) { if (!(0 <= start && start <= end && end < itemCount)) { error(SWT.ERROR_INVALID_RANGE); } for (int i = end; i >= start; i--) { removeItem(i); } } } /** * 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 < itemCount)) { SWT.error(SWT.ERROR_ITEM_NOT_REMOVED); } removeItem(index); } /** * 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) { int[] sortedIndices = new int[indices.length]; System.arraycopy(indices, 0, sortedIndices, 0, indices.length); sort(sortedIndices); int start = sortedIndices[sortedIndices.length - 1]; int end = sortedIndices[0]; if (!(0 <= start && start <= end && end < itemCount)) { SWT.error(SWT.ERROR_INVALID_RANGE); } int lastValue = -1; for (int i = 0; i < sortedIndices.length; i++) { if (sortedIndices[i] != lastValue) { lastValue = sortedIndices[i]; removeItem(sortedIndices[i]); } } if (itemCount == 0) { setTableEmpty(); } } } /** * 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 */ public void clear(int index) { checkWidget(); if (index < 0 || index >= itemCount) { SWT.error(SWT.ERROR_INVALID_RANGE); } TableItem item = items[index]; if (item != null) { item.clear(); } } /** * 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 */ public void clear(int start, int end) { checkWidget(); if (start <= end) { if (!(0 <= start && start <= end && end < itemCount)) { SWT.error(SWT.ERROR_INVALID_RANGE); } if (start == 0 && end == itemCount - 1) { clearAll(); } else { for (int i = start; i <= end; i++) { TableItem item = items[i]; if (item != null) { item.clear(); } } } } } /** * 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 */ public void clearAll() { checkWidget(); for (int i = 0; i < itemCount; i++) { TableItem item = items[i]; if (item != null) { item.clear(); } } clearItemImageSize(); } /** * 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 */ public void clear(int[] indices) { checkWidget(); if (indices == null) { SWT.error(SWT.ERROR_NULL_ARGUMENT); } if (indices.length > 0) { for (int i = 0; i < indices.length; i++) { if (!(0 <= indices[i] && indices[i] < itemCount)) { SWT.error(SWT.ERROR_INVALID_RANGE); } } for (int i = 0; i < indices.length; i++) { TableItem item = items[indices[i]]; if (item != null) { item.clear(); } } } } ///////////////////////////// // Selection handling methods /** * 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(); int result = -1; int topSelectedIndex = -1; for (int i = 0; i < selection.length; i++) { if (focusIndex == selection[i]) { result = selection[i]; } if (topSelectedIndex == -1) { topSelectedIndex = selection[i]; } else { topSelectedIndex = Math.min(topSelectedIndex, selection[i]); } } if (result == -1) { result = topSelectedIndex; } return result; } /** * 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(); select(index); setFocusIndex(index); showSelection(); } /** * 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 selection.length; } /** * 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(); select(start, end); if (end >= 0 && start <= end && ((style & SWT.SINGLE) == 0 || start == end)) { setFocusIndex(Math.max(0, start)); } showSelection(); } /** * 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(); int length = selection.length; TableItem[] result = new TableItem[length]; for (int i = 0; i < selection.length; i++) { result[i] = _getItem(selection[i]); } return result; } /** * 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) { SWT.error(SWT.ERROR_NULL_ARGUMENT); } deselectAll(); select(indices); int length = indices.length; if (length != 0 && ((style & SWT.SINGLE) == 0 || length <= 1)) { setFocusIndex(indices[0]); } showSelection(); } /** * 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> */ public void setSelection(TableItem item) { checkWidget(); if (item == null) { error(SWT.ERROR_NULL_ARGUMENT); } setSelection(new TableItem[] { item }); } /** * 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) { SWT.error(SWT.ERROR_NULL_ARGUMENT); } int[] indices = new int[items.length]; for (int i = 0; i < items.length; i++) { indices[i] = indexOf(items[i]); } setSelection(indices); } /** * 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(); // TODO [rh] directly return a copy of the selection array TableItem[] currentSelection = getSelection(); int[] result = new int[currentSelection.length]; for (int i = 0; i < currentSelection.length; i++) { result[i] = indexOf(currentSelection[i]); } return result; } /** * 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 visibility 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(); boolean result = false; if (index >= 0 && index < itemCount) { for (int i = 0; !result && i < selection.length; i++) { result = selection[i] == index; } } return result; } /** * 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 (index >= 0 && index < itemCount) { if ((style & SWT.SINGLE) != 0) { selection = new int[] { index }; } else { if (!isSelected(index)) { int length = selection.length; int[] newSelection = new int[length + 1]; System.arraycopy(selection, 0, newSelection, 0, length); newSelection[length] = index; selection = newSelection; } } } } /** * 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)) { if (itemCount != 0 && start < itemCount) { int adjustedStart = Math.max(0, start); int adjustedEnd = Math.min(end, itemCount - 1); if (adjustedStart == 0 && adjustedEnd == itemCount - 1) { selectAll(); } else { for (int i = adjustedStart; i <= adjustedEnd; i++) { select(i); } } } } } /** * 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); } int length = indices.length; if (length != 0 && ((style & SWT.SINGLE) == 0 || length <= 1)) { for (int i = length - 1; i >= 0; --i) { select(indices[i]); } } } /** * 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> */ // TODO [rh] revise: a VIRTUAL table would resolve all its items when // selectAll is called. Compare how SWT handles this. public void selectAll() { checkWidget(); if ((style & SWT.SINGLE) == 0) { setSelection(getItems()); } } /** * 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(); removeFromSelection(index); } /** * 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 == itemCount - 1) { deselectAll(); } else { int actualStart = Math.max(0, start); for (int i = actualStart; i <= end; i++) { removeFromSelection(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); } for (int i = 0; i < indices.length; i++) { removeFromSelection(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(); selection = EMPTY_SELECTION; } ////////////////////////////////// // TopIndex and showItem/Selection /** * 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 topIndex 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 topIndex) { checkWidget(); if (this.topIndex != topIndex && topIndex >= 0 && topIndex < itemCount) { this.topIndex = topIndex; adjustTopIndex(); if ((style & SWT.VIRTUAL) != 0) { redraw(); } } } /** * 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; } /** * 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); } int itemIndex = indexOf(item); int itemCount = getVisibleItemCount(false); if (itemIndex < topIndex) { // Show item as top item setTopIndex(itemIndex); } else if (itemCount > 0 && itemIndex >= topIndex + itemCount) { // Show item as last item setTopIndex(itemIndex - itemCount + 1); } } /** * 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 1.3 */ public void showColumn(TableColumn column) { checkWidget(); if (column == null) { error(SWT.ERROR_NULL_ARGUMENT); } if (column.isDisposed()) { error(SWT.ERROR_INVALID_ARGUMENT); } if (column.getParent() == this) { int index = indexOf(column); if (0 <= index && index < getColumnCount()) { int leftColumnsWidth = 0; int columnWidth = column.getWidth(); int clientWidth = getClientArea().width; int[] columnOrder = getColumnOrder(); boolean found = false; for (int i = 0; i < columnOrder.length; i++) { if (index != columnOrder[i]) { int currentColumnWidth = getColumn(columnOrder[i]).getWidth(); if (!found) { if (isFixedColumn(columnOrder[i])) { clientWidth -= currentColumnWidth; } else { leftColumnsWidth += currentColumnWidth; } } } else { found = true; } } if (getColumnLeftOffset(index) > leftColumnsWidth) { leftOffset = leftColumnsWidth; } else if (leftOffset < leftColumnsWidth + columnWidth - clientWidth) { leftOffset = leftColumnsWidth + columnWidth - clientWidth; } } } } /** * 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(); int index = getSelectionIndex(); if (index != -1) { showItem(_getItem(index)); } } //////////////////// // Visual appearance /** * 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 headerVisible 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 headerVisible) { checkWidget(); boolean changed = headerVisible != this.headerVisible; this.headerVisible = headerVisible; if (changed) { updateScrollBars(); } } /** * 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 headerVisible; } /** * Returns <code>true</code> if the receiver's lines are 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 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; } /** * Marks the receiver's lines 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 linesVisible 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 linesVisible) { checkWidget(); this.linesVisible = linesVisible; } /** * 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> */ public void setSortColumn(TableColumn column) { checkWidget(); if (column != null && column.isDisposed()) { error(SWT.ERROR_INVALID_ARGUMENT); } sortColumn = column; } /** * 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) */ public TableColumn getSortColumn() { checkWidget(); return sortColumn; } /** * 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> */ public void setSortDirection(int direction) { checkWidget(); if ((direction & (SWT.UP | SWT.DOWN)) != 0 || direction == SWT.NONE) { sortDirection = direction; } } /** * 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) */ public int getSortDirection() { checkWidget(); return sortDirection; } /////////////////////////////////// // Dimensions and size calculations /** * Returns the height of the area which would be used to * display <em>one</em> of the items in the receiver's. * * @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(); int result = customItemHeight; if (result == -1) { int textHeight = TextSizeUtil.getCharHeight(getFont()); int imageHeight = getItemImageSize().y; BoxDimensions cellPadding = getCellPadding(); result = Math.max(imageHeight, textHeight) + cellPadding.top + cellPadding.bottom; if ((style & SWT.CHECK) != 0) { result = Math.max(getCheckSize().y, result); } } return result; } /** * 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> */ public int getHeaderHeight() { checkWidget(); int result = 0; if (headerVisible) { Font headerFont = getHeaderFont(); int textHeight = TextSizeUtil.getCharHeight(headerFont); int imageHeight = 0; for (int i = 0; i < columnHolder.size(); i++) { TableColumn column = columnHolder.getItem(i); if (column.getText().contains("\n")) { int columnTextHeight = TextSizeUtil.textExtent(headerFont, column.getText(), 0).y; textHeight = Math.max(textHeight, columnTextHeight); } Image image = column.getImage(); int height = image == null ? 0 : image.getBounds().height; if (height > imageHeight) { imageHeight = height; } } result = Math.max(textHeight, imageHeight); TableThemeAdapter themeAdapter = getThemeAdapter(); BoxDimensions headerPadding = themeAdapter.getHeaderPadding(this); result += themeAdapter.getHeaderBorderBottomWidth(this); result += headerPadding.top + headerPadding.bottom; } return result; } /** * 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 GRID_WIDTH; } ////////////////// // Selection event /** * Adds the listener to the collection of listeners who will * be notified when the receiver's selection changes, 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 <code>SWT.CHECK</code> style set 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 * * @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) { SWT.error(SWT.ERROR_NULL_ARGUMENT); } TypedListener typedListener = new TypedListener(listener); addListener(SWT.Selection, typedListener); addListener(SWT.DefaultSelection, typedListener); } /** * Removes the listener from the collection of listeners who will * be notified when the receiver's selection changes. * * @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) { SWT.error(SWT.ERROR_NULL_ARGUMENT); } removeListener(SWT.Selection, listener); removeListener(SWT.DefaultSelection, listener); } @Override public void setFont(Font font) { super.setFont(font); clearItemsTextWidths(); updateScrollBars(); } //////////////////// // Widget dimensions @Override public Point computeSize(int wHint, int hHint, boolean changed) { checkWidget(); int width = 0; int height = 0; if (columnHolder.size() > 0) { for (int i = 0; i < columnHolder.size(); i++) { width += columnHolder.getItem(i).getWidth(); } } else { width = getItemsPreferredWidth(0); } height += getHeaderHeight(); height += getItemCount() * getItemHeight(); if (width == 0) { width = DEFAULT_WIDTH; } if (height == 0) { height = DEFAULT_HEIGHT; } if (wHint != SWT.DEFAULT) { width = wHint; } if (hHint != SWT.DEFAULT) { height = hHint; } BoxDimensions border = getBorder(); width += border.left + border.right; height += border.top + border.bottom; if ((style & SWT.V_SCROLL) != 0) { width += getVerticalBar().getSize().x; } if ((style & SWT.H_SCROLL) != 0) { height += getHorizontalBar().getSize().y; } return new Point(width, height); } private void setCustomItemHeight(Object value) { if (value == null) { customItemHeight = -1; } else { if (!(value instanceof Integer)) { error(SWT.ERROR_INVALID_ARGUMENT); } int itemHeight = ((Integer) value).intValue(); if (itemHeight < 0) { error(SWT.ERROR_INVALID_RANGE); } customItemHeight = itemHeight; } } private void setPreloadedItems(Object value) { if (value == null) { preloadedItems = 0; } else { if (!(value instanceof Integer)) { error(SWT.ERROR_INVALID_ARGUMENT); } preloadedItems = ((Integer) value).intValue(); if (preloadedItems < 0) { error(SWT.ERROR_INVALID_RANGE); } } } final int getItemsPreferredWidth(int columnIndex) { // Mimic Windows behaviour that has a minimal width int width = getCheckSize(columnIndex).x + 12; // dont't access virtual items, they would get resolved unintentionally TableItem[] items = getCachedItems(); for (int i = 0; i < items.length; i++) { int checkWidth = items[i].getCheckWidth(columnIndex); int packWidth = items[i].getPackWidth(columnIndex); int itemWidth = checkWidth + packWidth; if (itemWidth > width) { width = itemWidth; } } return width; } private void clearItemsTextWidths() { TableItem[] items = getCreatedItems(); for (int i = 0; i < items.length; i++) { items[i].clearTextWidths(); } } @Override public void changed(Control[] changed) { clearItemsTextWidths(); super.changed(changed); } ///////////////////////////// // Create and destroy columns final void createColumn(TableColumn column, int index) { columnHolder.insert(column, index); if (columnOrder == null) { columnOrder = new int[] { index }; } else { int length = columnOrder.length; for (int i = index; i < length; i++) { columnOrder[i]++; } int[] newColumnOrder = new int[length + 1]; System.arraycopy(columnOrder, 0, newColumnOrder, 0, index); System.arraycopy(columnOrder, index, newColumnOrder, index + 1, length - index); columnOrder = newColumnOrder; columnOrder[index] = index; } if (columnImageCount == null) { columnImageCount = new int[] { 0 }; } else if (columnHolder.size() > 1) { int length = columnImageCount.length; int[] newColumnImageCount = new int[length + 1]; System.arraycopy(columnImageCount, 0, newColumnImageCount, 0, index); System.arraycopy(columnImageCount, index, newColumnImageCount, index + 1, length - index); columnImageCount = newColumnImageCount; } for (int i = 0; i < itemCount; i++) { if (items[i] != null) { items[i].shiftData(index); } } updateScrollBars(); } final void destroyColumn(TableColumn column) { if (!isInDispose()) { int index = indexOf(column); // Remove data from TableItems for (int i = 0; i < itemCount; i++) { if (items[i] != null) { items[i].removeData(index); } } // Reset sort column if necessary if (column == sortColumn) { sortColumn = null; } // Remove from column holder columnHolder.remove(column); // Remove from column order int length = columnOrder.length; int[] newColumnOrder = new int[length - 1]; int count = 0; for (int i = 0; i < length; i++) { if (columnOrder[i] != index) { int newOrder = columnOrder[i]; if (index < newOrder) { newOrder--; } newColumnOrder[count] = newOrder; count++; } } columnOrder = newColumnOrder; // Remove from columnImageCount if (columnImageCount.length == 1) { columnImageCount = null; } else { count = 0; int[] newColumnImageCount = new int[columnImageCount.length - 1]; for (int i = 0; i < columnImageCount.length; i++) { if (i != index) { newColumnImageCount[count] = columnImageCount[i]; count++; } } columnImageCount = newColumnImageCount; } updateScrollBars(); } } //////////////////////////// // Create and destroy items final void createItem(TableItem item, int index) { if (index < 0 || index > itemCount) { error(SWT.ERROR_INVALID_RANGE); } if (itemCount == items.length) { /* * Grow the array faster when redraw is off or the table is not visible. * When the table is painted, the items array is resized to be smaller to * reduce memory usage. */ boolean small = /* drawCount == 0 && */isVisible(); int length = small ? items.length + 4 : Math.max(4, items.length * 3 / 2); TableItem[] newItems = new TableItem[length]; System.arraycopy(items, 0, newItems, 0, items.length); items = newItems; } /* Insert the item */ System.arraycopy(items, index, items, index + 1, itemCount - index); items[index] = item; itemCount++; adjustItemIndices(index); // adjust the selection indices for (int i = 0; i < selection.length; i++) { if (selection[i] >= index) { selection[i] = selection[i] + 1; } } // advance focusIndex when an item is inserted before the focused item if (index <= focusIndex) { focusIndex++; } updateScrollBars(); } final void destroyItem(TableItem item, int index) { if (!isInDispose()) { removeFromSelection(index); adjustSelectionIdices(index); if (item != null) { int columnCount = Math.max(1, columnHolder.size()); for (int i = 0; i < columnCount; i++) { updateColumnImageCount(i, item.getImageInternal(i), null); } } itemCount--; if (item != null) { item.index = -1; } if (itemCount == 0) { setTableEmpty(); } else { System.arraycopy(items, index + 1, items, index, itemCount - index); items[itemCount] = null; adjustItemIndices(index); } adjustTopIndex(); if (index == focusIndex || focusIndex > itemCount - 1) { adjustFocusIndex(); } updateScrollBars(); if ((style & SWT.VIRTUAL) != 0) { redraw(); } } } //////////////// // Destroy table @Override void releaseChildren() { Item[] tableItems = new TableItem[items.length]; System.arraycopy(items, 0, tableItems, 0, items.length); for (int i = 0; i < tableItems.length; i++) { if (tableItems[i] != null) { tableItems[i].dispose(); items[i] = null; } } TableColumn[] tableColumns = columnHolder.getItems(); for (int i = 0; i < tableColumns.length; i++) { tableColumns[i].dispose(); columnHolder.remove(tableColumns[i]); } super.releaseChildren(); } /////////////////////////////////// // Helping methods - item retrieval private TableItem _getItem(int index) { if ((style & SWT.VIRTUAL) != 0 && items[index] == null) { items[index] = new TableItem(this, SWT.NONE, index, false); } return items[index]; } final TableItem[] getCachedItems() { TableItem[] result; if ((style & SWT.VIRTUAL) != 0) { int count = 0; for (int i = 0; i < itemCount; i++) { if (items[i] != null && items[i].cached) { count++; } } result = new TableItem[count]; count = 0; for (int i = 0; i < itemCount; i++) { if (items[i] != null && items[i].cached) { result[count] = items[i]; count++; } } } else { result = new TableItem[itemCount]; System.arraycopy(items, 0, result, 0, itemCount); } return result; } final TableItem[] getCreatedItems() { TableItem[] result; if ((style & SWT.VIRTUAL) != 0) { int count = 0; for (int i = 0; i < itemCount; i++) { if (items[i] != null) { count++; } } result = new TableItem[count]; count = 0; for (int i = 0; i < itemCount; i++) { if (items[i] != null) { result[count] = items[i]; count++; } } } else { result = new TableItem[itemCount]; System.arraycopy(items, 0, result, 0, itemCount); } return result; } /////////////////////////////////////////////// // Helping methods - resolving of virtual items private void checkData() { int visibleItemCount = getVisibleItemCount(true); int startIndex = Math.max(0, topIndex - preloadedItems); int endIndex = Math.min(itemCount, topIndex + visibleItemCount + preloadedItems); for (int index = startIndex; index < endIndex; index++) { checkData(_getItem(index), index); } } final boolean checkData(TableItem item, int index) { boolean result = true; boolean virtual = (style & SWT.VIRTUAL) != 0; if (virtual && !item.cached && index >= 0 && index < itemCount) { item.cached = true; Event event = new Event(); event.item = item; event.index = index; notifyListeners(SWT.SetData, event); // widget could be disposed at this point if (isDisposed() || item.isDisposed()) { result = false; } } return result; } //////////////////////////////////// // Helping methods - item image size final void updateColumnImageCount(int columnIndex, Image oldImage, Image newImage) { int delta = 0; if (oldImage == null && newImage != null) { delta = +1; } else if (oldImage != null && newImage == null) { delta = -1; } if (delta != 0) { if (columnImageCount == null) { int columnCount = Math.max(1, columnHolder.size()); columnImageCount = new int[columnCount]; } columnImageCount[columnIndex] += delta; } } final boolean hasColumnImages(int columnIndex) { return columnImageCount == null ? false : columnImageCount[columnIndex] > 0; } final void updateItemImageSize(Image image) { if (image != null && itemImageSize == null) { Rectangle imageBounds = image.getBounds(); itemImageSize = new Point(imageBounds.width, imageBounds.height); } } final Point getItemImageSize() { return itemImageSize == null ? new Point(0, 0) : itemImageSize; } final void clearItemImageSize() { itemImageSize = null; } BoxDimensions getCellPadding() { if (bufferedCellPadding == null) { bufferedCellPadding = getThemeAdapter().getCellPadding(this); } return bufferedCellPadding; } int getCellSpacing() { if (bufferedCellSpacing < 0) { bufferedCellSpacing = getThemeAdapter().getCellSpacing(_getParent()); } return bufferedCellSpacing; } /////////////////////////////////////// // Helping methods - dynamic scrollbars boolean hasVScrollBar() { return hasVScrollBar; } boolean hasHScrollBar() { return hasHScrollBar; } @Override int getVScrollBarWidth() { int result = 0; if (hasVScrollBar()) { result = getVerticalBar().getSize().x; } return result; } @Override int getHScrollBarHeight() { int result = 0; if (hasHScrollBar()) { result = getHorizontalBar().getSize().y; } return result; } void updateScrollBars() { if ((style & SWT.NO_SCROLL) == 0) { hasVScrollBar = false; hasHScrollBar = needsHScrollBar(); if (needsVScrollBar()) { hasVScrollBar = true; hasHScrollBar = needsHScrollBar(); } getHorizontalBar().setVisible(hasHScrollBar); getVerticalBar().setVisible(hasVScrollBar); } } //////////////////////////// // Helping methods - various @Override void notifyResize(Point oldSize) { if (!oldSize.equals(getSize()) && !TextSizeUtil.isTemporaryResize()) { if ((style & SWT.VIRTUAL) != 0) { checkData(); } clearItemsTextWidths(); updateScrollBars(); adjustTopIndex(); } super.notifyResize(oldSize); } final Point getCheckSize(int index) { Point result = new Point(0, 0); if (index == 0 && getColumnCount() == 0) { result = getCheckSize(); } else { int[] columnOrder = getColumnOrder(); if (columnOrder[0] == index) { result = getCheckSize(); } } return result; } final Point getCheckSize() { Point result = new Point(0, 0); if ((style & SWT.CHECK) != 0) { Size checkImageSize = getThemeAdapter().getCheckBoxImageSize(this); BoxDimensions margin = getCheckBoxMargin(); result.x = checkImageSize.width + margin.left + margin.right; result.y = checkImageSize.height + margin.top + margin.bottom; } return result; } final BoxDimensions getCheckBoxMargin() { TableThemeAdapter themeAdapter = getThemeAdapter(); BoxDimensions margin = themeAdapter.getCheckBoxMargin(this); if (!margin.equals(CssBoxDimensions.ZERO.dimensions)) { return margin; } int checkBoxWidth = themeAdapter.getCheckBoxWidth(this); int imageWidth = themeAdapter.getCheckBoxImageSize(this).width; int marginWidth = Math.max(0, checkBoxWidth - imageWidth); int marginLeft = Math.round(marginWidth / 2); return new BoxDimensions(0, marginLeft, 0, marginLeft); } /** * Returns the scroll-offset of the column, which is the leftOffset unless it is a fixed column. */ final int getColumnLeftOffset(int columnIndex) { int result = leftOffset; if (columnIndex >= 0) { result = isFixedColumn(columnIndex) ? 0 : leftOffset; } return result; } private boolean isFixedColumn(int index) { int[] columnOrder = getColumnOrder(); int visualIndex = -1; for (int i = 0; i < columnOrder.length && visualIndex == -1; i++) { if (index == columnOrder[i]) { visualIndex = i; } } return visualIndex < getFixedColumns(); } private int getFixedColumns() { Object fixedColumns = getData(RWT.FIXED_COLUMNS); if (fixedColumns instanceof Integer) { if (!(getData(RWT.ROW_TEMPLATE) instanceof Template)) { return ((Integer) fixedColumns).intValue(); } } return -1; } final int getVisibleItemCount(boolean includePartlyVisible) { int clientHeight = getClientArea().height - getHeaderHeight(); int result = 0; if (clientHeight >= 0) { int itemHeight = getItemHeight(); result = clientHeight / itemHeight; if (includePartlyVisible && clientHeight % itemHeight != 0) { result++; } } return result; } private void setFocusIndex(int index) { if (index >= 0 && index < itemCount) { focusIndex = index; } } private void removeItem(int index) { TableItem item = items[index]; if (item != null && !item.isDisposed()) { item.dispose(); } else { destroyItem(null, index); } } private void removeFromSelection(int index) { if (index >= 0 && index < itemCount) { boolean found = false; for (int i = 0; !found && i < selection.length; i++) { if (index == selection[i]) { int length = selection.length; int[] newSel = new int[length - 1]; System.arraycopy(selection, 0, newSel, 0, i); if (i < length - 1) { System.arraycopy(selection, i + 1, newSel, i, length - i - 1); } selection = newSel; found = true; } } } } private void adjustSelectionIdices(int removedIndex) { for (int i = 0; i < selection.length; i++) { if (selection[i] >= removedIndex) { selection[i] = selection[i] - 1; } } } private void adjustTopIndex() { int visibleItemCount = getVisibleItemCount(false); int correction = visibleItemCount == 0 ? 1 : 0; if (topIndex > itemCount - visibleItemCount - correction) { topIndex = Math.max(0, itemCount - visibleItemCount - correction); } } private void adjustFocusIndex() { // Must reset focusIndex before calling getSelectionIndex focusIndex = -1; focusIndex = getSelectionIndex(); } private void adjustItemIndices(int start) { for (int i = start; i < itemCount; i++) { if (items[i] != null) { items[i].index = i; } } } boolean isItemVisible(int index) { boolean result = false; int visibleItemCount = getVisibleItemCount(true); if (visibleItemCount > 0) { result = index >= topIndex && index < topIndex + visibleItemCount; } return result; } private static void sort(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; } } } } } private void setTableEmpty() { items = new TableItem[4]; clearItemImageSize(); } Font getHeaderFont() { IControlAdapter controlAdapter = getAdapter(IControlAdapter.class); Font result = controlAdapter.getUserFont(); if (result == null) { result = getThemeAdapter().getHeaderFont(this); } return result; } private static int checkStyle(int style) { int result = style; if ((style & SWT.NO_SCROLL) == 0) { result |= SWT.H_SCROLL | SWT.V_SCROLL; } return checkBits(result, SWT.SINGLE, SWT.MULTI, 0, 0, 0, 0); } boolean needsVScrollBar() { int availableHeight = getClientArea().height; int height = getHeaderHeight(); height += getItemCount() * getItemHeight(); return height > availableHeight; } boolean needsHScrollBar() { boolean result = false; int availableWidth = getClientArea().width; int columnCount = columnHolder.size(); if (columnCount > 0) { int totalWidth = 0; for (int i = 0; i < columnCount; i++) { TableColumn column = columnHolder.getItem(i); totalWidth += column.getWidth(); } result = totalWidth > availableWidth; } else { TableItem measureItem = getMeasureItem(); if (measureItem != null) { int itemWidth = measureItem.getBounds().width; result = itemWidth > availableWidth; } } return result; } TableItem getMeasureItem() { TableItem[] items = tableAdapter.getCachedItems(); TableItem result = null; if (columnHolder.size() == 0) { // Find item with longest text because the imaginary only column stretches // as wide as the longest item (images cannot differ in width) for (int i = 0; i < items.length; i++) { if (result == null) { result = items[i]; } else { result = max(result, items[i]); } } } else { // Take the first item if any if (items.length > 0) { result = items[0]; } } return result; } private TableItem max(TableItem item1, TableItem item2) { TableItem result; int item1TextWidth = getStringExtent(item1.getFont(), item1.getText(0)).x; int item2TextWidth = getStringExtent(item2.getFont(), item2.getText(0)).x; if (item1TextWidth > item2TextWidth) { result = item1; } else { result = item2; } return result; } Point getStringExtent(Font font, String text) { return stringExtent(font, text, isMarkupEnabledFor(this)); } TableThemeAdapter getThemeAdapter() { return (TableThemeAdapter) getAdapter(ThemeAdapter.class); } /////////////////// // Skinning support @Override void reskinChildren(int flags) { if (items != null) { for (int i = 0; i < items.length; i++) { TableItem item = items[i]; if (item != null) { item.reskin(flags); } } } TableColumn[] columns = getColumns(); if (columns != null) { for (int i = 0; i < columns.length; i++) { TableColumn column = columns[i]; if (!column.isDisposed()) { column.reskin(flags); } } } super.reskinChildren(flags); } }