/*******************************************************************************
 * Copyright (c) 2000, 2004 IBM Corporation and others.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Common Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/cpl-v10.html
 * 
 * Contributors:
 *     IBM Corporation - initial API and implementation
 *******************************************************************************/
package org.eclipse.swt.widgets;

 
import org.eclipse.swt.events.*;
import org.eclipse.swt.graphics.*;
import org.eclipse.swt.*;
import java.util.Enumeration;
import java.util.Vector;
 
/** 
 * Instances of this class implement a selectable user interface
 * object that displays a list of images and strings and issue
 * notificiation when selected.
 * <p>
 * The item children that may be added to instances of this class
 * must be of type <code>TableItem</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</dd>
 * </dl>
 * <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>
 */
public class Table extends SelectableItemWidget {
	private static final int COLUMN_RESIZE_OFFSET = 7;	// offset from the start and end of each 
														// column at which the resize cursor is displayed 
														// if the mouse is in the column header
	static final String DOT_STRING = "...";				// used to indicate truncated item labels

	private Header tableHeader;
	private Vector items;
	private Vector columns;
	private boolean drawGridLines = false;
	private boolean firstColumnImage = false;			// true if any item in the first column has an image
	private int columnResizeX;							// last position of the cursor in a column resize operation
	private Cursor columnResizeCursor;					// cursor displayed when a column resize is in progress. 
														// Need to keep reference to the cursor in order to dispose it.
	private boolean isColumnResizeCursor = false;		// set to true if the column resize cursor is active														
	private TableColumn resizeColumn;					// column that is currently being resized
	private TableColumn fillColumn;						// column used to fill up space that is not used 
														// by user defined columns
	private TableColumn defaultColumn;					// Default column that is created as soon as the table is created.
														// Fix for 1FUSJY5
	private int fontHeight;								// font height, avoid use GC.stringExtend for each pain
	
	private boolean ignoreRedraw;

/**
 * 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 Widget#checkSubclass
 * @see Widget#getStyle
 */
public Table(Composite parent, int style) {
	// use NO_REDRAW_RESIZE to avoid flashing during widget resize redraw
	super(parent, checkStyle(style| SWT.NO_REDRAW_RESIZE));
}

/**
 * Add 'column' to the receiver.
 * @param column - new table column that should be added to 
 *	the receiver
 */
void addColumn(TableColumn column) {
	int index = column.getIndex();
	
	getColumnVector().insertElementAt(column, index);
	// has the column been inserted (vs. appended)?
	if (index < getColumnCount() - 1) {				
		reindexColumns(index + 1);
	}
	// is there more than one user created column?
	// There always is the data and visual of the default column
	// so we don't need to create those for the first user column
	int columnCount = getColumnCount();
	if (columnCount > 1) {
		insertColumnData(column);
		Enumeration items = getItemVector ().elements ();
		while (items.hasMoreElements()) {
			TableItem item = (TableItem)items.nextElement();
			Color [] cellBackground = item.cellBackground;
			if (cellBackground != null) {
				Color [] temp = new Color [columnCount];
				System.arraycopy (cellBackground, 0, temp, 0, index);
				System.arraycopy (cellBackground, index, temp, index+1, columnCount - index - 1);
				item.cellBackground = temp;
			}
			Color [] cellForeground = item.cellForeground;
			if (cellForeground != null) {
				Color [] temp = new Color [columnCount];
				System.arraycopy (cellForeground, 0, temp, 0, index);
				System.arraycopy (cellForeground, index, temp, index+1, columnCount - index - 1);
				item.cellForeground = temp;
			}
			Font [] cellFont = item.cellFont;
			if (cellFont != null) {
				Font [] temp = new Font [columnCount];
				System.arraycopy (cellFont, 0, temp, 0, index);
				System.arraycopy (cellFont, index, temp, index+1, columnCount - index - 1);
				item.cellFont = temp;
			}
		}
	
	}
	else {								// first user created column
		setContentWidth(0);				// pretend it's ground zero for column resizings
		redraw();						// redraw the table and header. The default column 
		getHeader().redraw();			// won't be drawn anymore, because there now is a user created table.
	}
	insertColumnVisual(column);
}
/**
 * Add 'item' to the receiver.
 * @param item - new table item that should be added to 
 *	the receiver
 * @param index - position the new item should be inserted at
 */
void addItem(TableItem item, int index) {
	Vector items = getItemVector();

	if (index < 0 || index > getItemCount()) {
		error(SWT.ERROR_INVALID_RANGE);
	}	
	addingItem(item, index);
	item.setIndex(index);
	if (index < items.size()) {
		for (int i = index; i < items.size(); i++) {
			TableItem anItem = (TableItem) items.elementAt(i);
			anItem.setIndex(anItem.getIndex() + 1);
		}
		items.insertElementAt(item, index);
	}
	else {
		items.addElement(item);
	}
	addedItem(item, index);
}
/**
 * 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 reciever 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) error (SWT.ERROR_NULL_ARGUMENT);
	TypedListener typedListener = new TypedListener(listener);
	addListener(SWT.Selection,typedListener);
	addListener(SWT.DefaultSelection,typedListener);
}
static int checkStyle (int style) {
	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 attribues of the item are set to the default
 * value.  If the table was created with the SWT.VIRTUAL 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 < getItemCount())) {
		error(SWT.ERROR_INVALID_RANGE);
	}
	TableItem item = (TableItem) getVisibleItem(index);
	item.clear();
	int y = getRedrawY(item);
	redraw(0, y, getClientArea().width, getItemHeight(), false);
}
/**
 * Removes the items from the receiver which are between the given
 * zero-relative start and end indices (inclusive).  The text, icon
 * and other attribues of the items are set to their default values.
 * If the table was created with the SWT.VIRTUAL 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 < getItemCount())) {
		error(SWT.ERROR_INVALID_RANGE);
	}
	if (start == 0 && end == getItemCount() - 1) {
		clearAll();
	} else {
		for (int i=start; i<=end; i++) {
			clear(i);
		}
	}
}
/**
 * Clears the items at the given zero-relative indices in the receiver.
 * The text, icon and other attribues of the items are set to their default
 * values.  If the table was created with the SWT.VIRTUAL 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] < getItemCount())) {
			error(SWT.ERROR_INVALID_RANGE);
		}
	}
	for (int i=0; i<indices.length; i++) {
		clear(indices [i]);
	}
}
/**
 * Clears all the items in the receiver. The text, icon and other
 * attribues of the items are set to their default values. If the
 * table was created with the SWT.VIRTUAL 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 ();
	for (int i=0; i<getItemCount(); i++) {
		TableItem item = (TableItem) getVisibleItem(i);
		item.clear();
	}
	redraw();
}
/**
 * The width of 'column' is about to change.
 * Adjust the position of all columns behind it.
 */
void columnChange(TableColumn column, Rectangle newBounds) {
	Rectangle columnBounds = column.getBounds();
	Rectangle clientArea = getClientArea();
	int oldXPosition = columnBounds.x + columnBounds.width; 
	int newXPosition = newBounds.x + newBounds.width;
	int widthChange = newBounds.width - columnBounds.width;
	int headerHeight = getHeaderHeight();
	int columnIndex = column.getIndex();

	if (widthChange != 0) {
		getHeader().widthChange(columnIndex, widthChange);
		if (columnIndex != TableColumn.FILL) {
			if (getLinesVisible() == true) {
				oldXPosition -= getGridLineWidth();						// include vertical grid line when scrolling resized column.
				newXPosition -= getGridLineWidth();
			}
			scroll(														// physically move all following columns
				newXPosition, headerHeight, 							// destination x, y
				oldXPosition, headerHeight, 							// source x, y
				clientArea.width, clientArea.height, true);
		}
		column.internalSetBounds(newBounds);
		if (columnIndex != TableColumn.FILL) {
			resetTableItems(columnIndex);
			moveColumns(columnIndex + 1, widthChange);					// logically move all following columns	(set their bounds)
			setContentWidth(getContentWidth() + widthChange);			// set the width of the receiver's content
			claimRightFreeSpace();
			resizeRedraw(column, columnBounds.width, newBounds.width);
		}
	}
}
/**
 * The mouse pointer was double clicked on the receiver.
 * Handle the event according to the position of the mouse click
 * and the modifier key that was pressed, if any.
 * @param event - the mouse event
 */
void columnMouseDoubleClick(Event event) {
	if (isFocusControl () == false) {
		forceFocus ();
	}
	if (getIgnoreDoubleClick()) return;	
	int itemHeight = getItemHeight();
	TableColumn hitColumn = getColumnAtX (event.x);
	boolean isFullSelection = (getStyle () & SWT.FULL_SELECTION) != 0;
	if (hitColumn != null) {
		int itemIndex = (event.y - getHeaderHeight()) / itemHeight + getTopIndex();
		TableItem hitItem = (TableItem) getVisibleItem(itemIndex);
		if (hitItem != null && 
			(hitColumn.getIndex() == TableColumn.FIRST || isFullSelection)) {
			if (hitItem.isSelectionHit(event.x) == true) {
				Event columnDblClickEvent = new Event();
				columnDblClickEvent.item = hitItem;
				postEvent(SWT.DefaultSelection, columnDblClickEvent);
			}
		}
	}
}
/**
 * The mouse pointer was pressed down on the receiver.
 * Handle the event according to the position of the mouse click
 * and the modifier key that was pressed, if any.
 * @param event - the mouse event
 */
void columnMouseDown(Event event) {
	int itemHeight = getItemHeight();
	int itemIndex;
	TableItem hitItem;
	TableColumn hitColumn = getColumnAtX (event.x);

	if (isFocusControl () == false) {
		forceFocus ();
	}
	if (hitColumn != null) {
		itemIndex = (event.y - getHeaderHeight()) / itemHeight + getTopIndex();
		hitItem = (TableItem) getVisibleItem(itemIndex);
		if (hitItem != null) {
			if (hitItem.isSelectionHit(event.x) == true) {
				doMouseSelect(hitItem, itemIndex, event.stateMask, event.button);
			}
			else 
			if (hitItem.isCheckHit(new Point(event.x, event.y)) == true) {
				// only react to button one clicks. fixes bug 6770
				if (event.button != 1) {
					return;
				}
				doCheckItem(hitItem);
			}
		}
	}
}
/**
 * The mouse pointer was moved over the receiver.
 * Reset the column resize cursor if it was active.
 * @param event - the mouse event
 */
void columnMouseMove(Event event) {
	if (isColumnResizeStarted() == false) {
		setColumnResizeCursor(false);
	}
}
public Point computeSize(int wHint, int hHint, boolean changed) {
	checkWidget();
	Point size = super.computeSize(wHint, hHint, changed);
	Point headerSize;
	GC gc;
	final int WidthCalculationCount = Math.min(getItemCount(), 50);		// calculate item width for the first couple of items only
	TableItem item;
	Image itemImage;
	String itemText;
	int width;
	int newItemWidth = 0;
		
	if (getHeaderVisible() == true && hHint == SWT.DEFAULT) {
		headerSize = getHeader().computeSize(SWT.DEFAULT, SWT.DEFAULT, false);
		size.y += headerSize.y;		
	}
	if (getContentWidth() == 0 && WidthCalculationCount > 0) {
		gc = new GC(this);
		for (int i = 0; i < WidthCalculationCount; i++) {
			item = getItem(i);
			if (item == null) {
				break;											// no more items
			}
			itemImage = item.getImage();
			itemText = item.getText();
			width = 0;
			if (itemImage != null) {
				width += itemImage.getBounds().width;
			}
			if (itemText != null) {
				gc.setFont(item.getFont());
				width += gc.stringExtent(itemText).x;
			}
			newItemWidth = Math.max(newItemWidth, width);
		}
		if (newItemWidth > 0) {
			size.x = newItemWidth;
		}
		gc.dispose();
	}
	return size;
}
/**
 * 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();
	SelectableItem item = null;
	
	if (indices == null) {
		error(SWT.ERROR_NULL_ARGUMENT);
	}
	for (int i = 0; i < indices.length; i++) {
		item = getVisibleItem(indices[i]);
		if (item != null) {
			deselect(item);
		}
	}
	if (item != null) {
		setLastSelection(item, false);
	}
}
/**
 * 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();
	SelectableItem item = getVisibleItem(index);

	if (item != null) {
		deselect(item);
		setLastSelection(item, 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();
	SelectableItem item = null;

	for (int i=start; i<=end; i++) {
		item = getVisibleItem(i);
		if (item != null) {
			deselect(item);
		}
	}
	if (item != null) {
		setLastSelection(item, false);
	}	
}
/**
 * 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();
	
	deselectAllExcept((SelectableItem) null);
}
/**
 * Free resources.
 */
void doDispose() {
	Vector items = getItemVector();
	
	super.doDispose();
	for (int i = items.size() - 1; i >= 0; i--) {
		((TableItem) items.elementAt(i)).dispose();
	}
	setItemVector(null);
	items = getColumnVector();
	while (items.size() > 0) {								// TableColumn objects are removed from vector during dispose()
		((TableColumn) items.lastElement()).dispose();
	}
	resizeColumn = null;
	fillColumn = null;
	defaultColumn = null;
	if (columnResizeCursor != null) {
		columnResizeCursor.dispose();
	}
}
/**
 * Draw a line tracking the current position of a column 
 * resize operation.
 * @param xPosition - x coordinate to draw the line at
 */
void drawColumnResizeLine(int xPosition) {
	GC gc = new GC(this);
	int lineHeight = getClientArea().height;
	int lineWidth = getGridLineWidth();
	
	redraw(getColumnResizeX() - lineWidth, 0, 1, lineHeight, false);
	setColumnResizeX(xPosition);
	gc.drawLine(xPosition - lineWidth, 0, xPosition - lineWidth, lineHeight);
	gc.dispose();
}
/**
 * Draw the grid lines for the receiver.
 * @param event - Paint event triggering the drawing operation.
 * @param drawColumns - The table columns for which the grid 
 *	lines should be drawn.
 */
void drawGridLines(Event event, Enumeration drawColumns) {
	GC gc = event.gc;
	Color oldForeground = getForeground();
	Rectangle columnBounds;
	TableColumn column;
	int lineWidth = getGridLineWidth();
	int itemHeight = getItemHeight();
	int headerHeight = getHeaderHeight();
	int lineXPosition;
	int lineYPosition = headerHeight + ((event.y-headerHeight) / itemHeight) * itemHeight;
	int lineYStopPosition = event.y + event.height;

	gc.setForeground(display.getSystemColor(SWT.COLOR_WIDGET_LIGHT_SHADOW));
	// Draw the horizontal lines	
	if (itemHeight > 0) {
		while (lineYPosition < lineYStopPosition) {
			gc.drawLine(
				event.x, lineYPosition + itemHeight - lineWidth, 
				event.x + event.width, lineYPosition + itemHeight - lineWidth);
			lineYPosition += itemHeight; 		
		}
	}
	// Draw the vertical lines at the right border of each column except the fill column
	while (drawColumns.hasMoreElements() == true) {
		column = (TableColumn) drawColumns.nextElement();
		if (column.getIndex() != TableColumn.FILL) {
			columnBounds = column.getBounds();
			lineXPosition = columnBounds.x + columnBounds.width - lineWidth;
			gc.drawLine(
				lineXPosition, event.y, 
				lineXPosition, event.y + event.height);
		}
	}
	gc.setForeground(oldForeground);
}

/**
 * If the receiver has input focus draw a rectangle enclosing 
 * the label of 'item' to indicate the input focus.
 * The rectangle is drawn in either the first column or in all columns 
 * for full row select. 
 * @param item - item for which the selection state should be drawn
 * @param gc - GC to draw on. 
 */
void drawSelectionFocus(TableItem item, GC gc) {
	Point extent = item.getSelectionExtent();
	Point position = new Point(
		item.getImageStopX(TableColumn.FIRST) + getHorizontalOffset(),
		getRedrawY(item));

	gc.drawFocus(position.x, position.y, extent.x, extent.y);
}

/**
 * Returns an array containing the receiver's children.
 * <p>
 * Note: This is not the actual structure used by the receiver
 * to maintain its list of children, so modifying the array will
 * not affect the receiver. 
 * </p>
 *
 * @return an array of children
 *
 * @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 Control [] getChildren() {
	checkWidget();
	Control[] controls = _getChildren();
	if (tableHeader == 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] != tableHeader) {
		 	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.
 * 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>
 */
public TableColumn getColumn(int index) {
	checkWidget();
	Vector columns = getColumnVector();
	
	if (columns == null) error(SWT.ERROR_CANNOT_GET_ITEM);
	if (index < 0 || index >= columns.size()) {
		error(SWT.ERROR_INVALID_RANGE);
	}
	
	return (TableColumn) columns.elementAt(index);
}
/**
 * Return the column located at 'xPosition' in the widget.
 * Return null if xPosition is outside the widget.
 * @param xPosition - position of the desired column
 */
TableColumn getColumnAtX(int xPosition) {
	Enumeration columns = internalGetColumnVector().elements();
	TableColumn column;
	TableColumn hitColumn = null;
	Rectangle bounds;

	while (columns.hasMoreElements() == true && hitColumn == null) {
		column = (TableColumn) columns.nextElement();
		bounds = column.getBounds();
		if ((xPosition >= bounds.x) && 
			(xPosition <= bounds.x + bounds.width)) {
			hitColumn = column;
		}
	}
	if (hitColumn == null) {
		column = getFillColumn();
		bounds = column.getBounds();
		if ((xPosition >= bounds.x) && 
			(xPosition <= bounds.x + bounds.width)) {
			hitColumn = column;
		}
	}
	return hitColumn;
}
/**
 * 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 is 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>
 * @exception SWTError <ul>
 *    <li>ERROR_CANNOT_GET_COUNT - if the operation fails because of an operating system failure</li>
 * </ul>
 */
public int getColumnCount() {
	checkWidget();
	Vector columns = getColumnVector();
	int count = 0;
	
	if (columns != null) {
		count = columns.size();
	}
	return count;
}
/** Replace CURSOR_SIZEWE with real column resize cursor 
 *	(no standard cursor-have to load from file)
 * Answer the cursor displayed during a column resize 
 * operation.
 * Lazy initialize the cursor since it may never be needed.
 */
Cursor getColumnResizeCursor() {
	if (columnResizeCursor == null) {
		columnResizeCursor = new Cursor(display, SWT.CURSOR_SIZEWE);
	}
	return columnResizeCursor;
}
/**
 * Answer the current position of the mouse cursor during
 * a column resize operation.
 */
int getColumnResizeX() {
	return columnResizeX;
}
/**
 * Returns an array of <code>TableColumn</code>s which are the
 * columns in the receiver. 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>
 */
public TableColumn [] getColumns() {
	checkWidget();
	Vector columns = getColumnVector();
	TableColumn columnArray[] = new TableColumn[columns.size()];

	columns.copyInto(columnArray);
	return columnArray;
}
/**
 * Answer a Vector containing all columns of receiver except 
 * the fill column to the right of all content columns.
 */
Vector getColumnVector() {
	return columns;
}
/**
 * Return the default column that is created as soon as the table 
 * is created.
 * Fix for 1FUSJY5
 */
TableColumn getDefaultColumn() {
	return defaultColumn;
}
/**
 * Answer the column used to occupy any space left to the 
 * right of all the user created columns.
 */
TableColumn getFillColumn() {
	return fillColumn;
}
/**
 * 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;
}
/**
 * Answer the table header widget.
 */
Header getHeader() {
	return tableHeader;
}
/**
 * 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();
	Header header = getHeader();
	int height = 0;
	
	if (header.getVisible() == true) {
		height = header.getBounds().height;
	}
	return height;
}
/**
 * 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 getHeader().getVisible();
}
/**
 * Answer the image extent of 'item'. Use the image of any column.
 */
Point getImageExtent(SelectableItem item) {
	Image image = null;
	Rectangle imageBounds;
	Point imageExtent = null;
	int columnCount = internalGetColumnCount();

	for (int i = 0; i < columnCount && image == null; i++) {
		image = ((TableItem) item).getImage(i);
	}		
	if (image != null) {
		imageBounds = image.getBounds();
		imageExtent = new Point(imageBounds.width, imageBounds.height);
	}
	return imageExtent;
}
/**
 * Answer the index of 'item' in the receiver.
 */
int getIndex(SelectableItem item) {
	int index = -1;
	
	if (item != null && item.getSelectableParent() == this) {
		index = ((TableItem) item).getIndex();
	}
	return index;
}
/**
 * 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 < getItemCount())) {
		error(SWT.ERROR_INVALID_RANGE);
	}		
	return (TableItem) getVisibleItem(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.
 *
 * @param point the point used to locate the item
 * @return the item at the given point
 *
 * @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);
	TableColumn column = getColumnAtX(point.x);	
	if ((style & SWT.FULL_SELECTION) == 0) {
		if (column != null && column.getIndex() != 0) {
			return null;
		}
	}
	TableItem item = null;
	int headerHeight = getHeaderHeight();
	if (column != null && column.getIndex() != TableColumn.FILL && point.y - headerHeight > 0) {
		int itemIndex = (point.y - headerHeight) / getItemHeight() + getTopIndex();
		item = (TableItem) getVisibleItem(itemIndex);
		if ((style & SWT.FULL_SELECTION) == 0) {
			if (item != null) {
				Point itemSize = item.getItemExtent(column);
				if (point.x - column.getBounds().x > itemSize.x) {
					item = null;
				}
			}
		}
	}
	return item;
}
/**
 * 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 getItemVector().size();
}
/**
 * Answer the number of items that can be displayed in the
 * client area of the receiver without truncating any items.
 */
int getItemCountWhole() {
	int clientAreaHeight = Math.max(0, getClientArea().height - getHeaderHeight());
	
	return clientAreaHeight / getItemHeight();
}
/**
 * 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();
	
	return super.getItemHeight();
}
/**
 * Answer the number of pixels that should be added to the item height.
 */
int getItemPadding() {
	return getGridLineWidth() + display.textHighlightThickness + 1;
}
/**
 * Returns an 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();
	Vector items = getItemVector();
	TableItem itemArray[] = new TableItem[items.size()];

	items.copyInto(itemArray);
	return itemArray;
}
/**
 * Answer all items of the receiver.
 */
Vector getItemVector() {
	return items;
}
/**
 * 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 drawGridLines;
}
/** 
 * Answer a Vector containing the columns that need repainting 
 * based on the 'paintArea'.
 * @param paintArea - invalidated rectangle that needs repainting
 */
Vector getPaintColumns(Rectangle paintArea) {
	Enumeration columns = internalGetColumnVector().elements();
	Vector paintColumns = new Vector();
	TableColumn column;
	Rectangle columnBounds;
	int paintAreaRightBorder = paintArea.x + paintArea.width;

	while (columns.hasMoreElements() == true) {
		column = (TableColumn) columns.nextElement();
		columnBounds = column.getBounds();
		if ((columnBounds.x + columnBounds.width >= paintArea.x) &&	// does the paintArea overlap the current column?
			(columnBounds.x <= paintAreaRightBorder)) {
			paintColumns.addElement(column);
		}
	}
	return paintColumns;
}
/** 
 * Return the width of the widest item in the column identified by 'columnIndex'
 * @param columnIndex - index of the column whose preferred width should be
 *	calculated
 */
int getPreferredColumnWidth(int columnIndex) {
	Enumeration tableItems = getItemVector().elements();
	TableItem tableItem;
	int width = 0;
	int headerWidth;
	
	if (columnIndex != TableColumn.FILL) {
		if ((parent.getStyle() & SWT.VIRTUAL) == 0) {
			while (tableItems.hasMoreElements() == true) {
				tableItem = (TableItem) tableItems.nextElement();
				width = Math.max(width, tableItem.getPreferredWidth(columnIndex));
			}
		}
		headerWidth = getHeader().getPreferredWidth(columnIndex);
		if (width < headerWidth) {
			width = headerWidth;
		}
	}
	return width;
}
/**
 * Answer the position in the receiver where 'item' is drawn
 * @return the y coordinate at which 'item' is drawn.
 *	Return -1 if 'item' is not an item of the receiver 
 */
int getRedrawY(SelectableItem item) {
	int redrawY = super.getRedrawY(item);

	if (redrawY != -1) {
		redrawY += getHeaderHeight();
	}
	return redrawY;
}
/**
 * Answer the column that is being resized or null if no 
 * resize operation is in progress.
 */
TableColumn getResizeColumn() {
	return resizeColumn;
}
/**
 * Return the positions at which the column identified by 'columnIndex' 
 * must be redrawn.
 * These positions may be different for each item since each item may 
 * have a different label
 * @param columnIndex - the column index
 * @param columnWidth - width of the column
 * @return the positions at which the column must be redrawn.
 *	Each item in the widget client area is represented by a slot in 
 * 	the array. The item at position 'topIndex' is the first item in 
 *	the array.
 */
int [] getResizeRedrawX(int columnIndex, int columnWidth) {
	int topIndex = getTopIndex();
	int bottomIndex = getBottomIndex();
	int resizeRedrawX[];
	TableItem item;

	bottomIndex = Math.min(bottomIndex, getItemCount());
	resizeRedrawX = new int[bottomIndex-topIndex+1];
	for (int i = topIndex; i < bottomIndex; i++) {
		item = (TableItem) getVisibleItem(i);
		resizeRedrawX[i-topIndex] = item.getDotStartX(columnIndex, columnWidth);
	}
	return resizeRedrawX;
}
/**
 * Returns an array of <code>TableItem</code>s that are currently
 * selected in the receiver. 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();
	Vector selectionVector = getSelectionVector();
	TableItem[] selectionArray = new TableItem[selectionVector.size()];

	selectionVector.copyInto(selectionArray);
	sort(selectionArray, 0, selectionArray.length);
	return selectionArray;
}

/**
 * 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 super.getSelectionCount();
}

int getFontHeight(){
	return fontHeight;
}
/**
 * Answer the size of the full row selection rectangle for 'item'.
 */
Point getFullSelectionExtent(TableItem item) {
	TableColumn lastColumn = (TableColumn) internalGetColumnVector().lastElement();
	Point selectionExtent = null;
	Rectangle columnBounds;
	int xPosition = item.getImageStopX(TableColumn.FIRST);
	int gridLineWidth = getGridLineWidth();

	if (lastColumn != null) {
		columnBounds = lastColumn.getBounds();
		selectionExtent = new Point(
			columnBounds.x - getHorizontalOffset() + columnBounds.width - xPosition - gridLineWidth, 
			getItemHeight());
		if (getLinesVisible() == true) {
			selectionExtent.y -= gridLineWidth;
		}	
	}
	return selectionExtent;
}
/**
 * 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 index = -1;
	
	if (getSelectionCount() > 0) {
		index = getIndex(getSelection()[0]);
	}
	return index;
}
/**
 * Returns the zero-relative indices of the items which are currently
 * selected in the receiver.  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();
	TableItem[] items = getSelection();
	int indices[] = new int[items.length];

	for (int i = 0; i < items.length; i++) {
		indices[i] = getIndex(items[i]);
	}	
	return indices;
}
/**
 * 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 super.getTopIndex();
}
/**
 * Answer the index of 'item' in the receiver.
 * Answer -1 if the item is not visible.
 * The returned index must refer to a visible item.
 * Note: 
 * 	Visible in this context does not neccessarily mean that the 
 * 	item is displayed on the screen. It only means that the item 
 * 	would be displayed if it is located inside the receiver's 
 * 	client area.
 *	Every item in a table widget should be visible.
 */
int getVisibleIndex(SelectableItem item) {
	return getIndex(item);
}
/**
 * Answer the SelectableItem located at 'itemIndex' in the receiver.
 * @param itemIndex - location of the SelectableItem object to return
 */
SelectableItem getVisibleItem(int itemIndex) {
	Vector items = getItemVector();
	TableItem item = null;
	
	if ((items != null) && (itemIndex >= 0) && (itemIndex < items.size())) {
		item = (TableItem) items.elementAt(itemIndex);
	}
	return item;
}
/**
 * Answer the y coordinate at which 'item' is drawn. 
 * @param item - SelectableItem for which the paint position 
 *	should be returned
 * @return the y coordinate at which 'item' is drawn.
 *	Return -1 if 'item' is null or outside the client area
 */
int getVisibleRedrawY(SelectableItem item) {
	int redrawY = -1;
	int index = getTopIndex();
	int bottomIndex = getBottomIndex();
	
	if (item == null) {
		return redrawY;
	}
	while (index < bottomIndex && item.equals(getVisibleItem(index)) == false) {
		index++;
	}
	if (index < bottomIndex) {
		redrawY = getRedrawY(item);
	}
	return redrawY;
}
/**
 * Handle the events the receiver is listening to.
 */
void handleEvents(Event event) {
	switch (event.type) {
		case SWT.MouseMove:
			if (event.widget == tableHeader) {
				headerMouseMove(event);
			}
			else {
				columnMouseMove(event);
			}
			break;
		case SWT.MouseDown:
			if (event.widget == tableHeader) {
				headerMouseDown(event);
			}
			else {
				columnMouseDown(event);
			}
			break;
		case SWT.MouseDoubleClick:
			if (event.widget == tableHeader) {
				headerMouseDoubleClick(event);
			} else {
				columnMouseDoubleClick(event);
			}
			break;
		case SWT.MouseUp:
			mouseUp(event);
			break;
		case SWT.Paint:
			paint(event);
			break;
		default:
			super.handleEvents(event);
	}		
}
/**
 * Answer true if any item in the first column has an image.
 * Answer false otherwise.
 */
boolean hasFirstColumnImage() {
	return firstColumnImage;
}
/**
 * The mouse pointer was pressed down on the receiver's header
 * widget. Start a column resize operation if apropriate.
 * @param event - the mouse event that occured over the header 
 *	widget
 */
void headerMouseDown(Event event) {
	TableColumn column = getColumnAtX(event.x);

	// only react to button one clicks. fixes bug 6770
	if (event.button != 1) {
		return;
	}
	if (isColumnResize(event) == true) {
		startColumnResize(event);
	}
	else
	if (column != null) {
		column.notifyListeners(SWT.Selection, new Event());
	}
}
void headerMouseDoubleClick(Event event) {
	if (event.button != 1) return;
	TableColumn column = getColumnAtX(event.x);
	if (column != null) {
		column.notifyListeners(SWT.DefaultSelection, new Event());
	}
}
/**
 * The mouse pointer was moved over the receiver's header widget.
 * If a column is currently being resized a vertical line indicating 
 * the new position of the resized column is drawn.
 * Otherwise, if no column resize operation is in progress, the 
 * column resize cursor is displayed when the mouse is near the border 
 * of a column.
 */
void headerMouseMove(Event event) {
	if (isColumnResizeStarted() == false) {				// only check whether cursor is in resize
		setColumnResizeCursor(isColumnResize(event));	// area if no resize operation is in progress
	}
	else 
	if (event.x >= getResizeColumn().getBounds().x) {
		drawColumnResizeLine(event.x);
		update();										// looks better if resize line is drawn immediately
	}
}
/**
 * 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 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 column) {
	checkWidget();

	if (column == null) {
		error(SWT.ERROR_NULL_ARGUMENT);
	}
	return internalGetColumnVector().indexOf(column);
}
/**
 * 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) {
		error(SWT.ERROR_NULL_ARGUMENT);
	}	
	return getIndex(item);
}
/**
 * Initialize the receiver. Create a header widget and an empty column.
 */
void initialize() {
	columns = new Vector();
	setItemVector(new Vector());
	GC gc = new GC(this);
	Point extent = gc.stringExtent("String");
	fontHeight = extent.y;
	gc.dispose();
	tableHeader = new Header(this);
	tableHeader.setVisible(false);					// SWT table header is invisible by default, too
	fillColumn = TableColumn.createFillColumn(this);
	setColumnPosition(fillColumn);
	defaultColumn = TableColumn.createDefaultColumn(this);	// Create the default column. Fix for 1FUSJY5	
	super.initialize();
}
/**
 * Insert the new column 'column' into the table data at position 
 * 'index'.
 */
void insertColumnData(TableColumn column) {
	Enumeration tableItems = getItemVector().elements();
	TableItem tableItem;
	
	while (tableItems.hasMoreElements() == true ) {
		tableItem = (TableItem) tableItems.nextElement();
		tableItem.insertColumn(column);
	}
}
/**
 * Insert the new column 'column'.
 * Set the position and move the following columns to the right.
 */
void insertColumnVisual(TableColumn column) {
	Rectangle columnBounds = column.getBounds();
	Rectangle previousColumnBounds;
	int index = column.getIndex();
		
	if (index > 0) {
		previousColumnBounds = getColumn(index - 1).getBounds();
		columnBounds.x = previousColumnBounds.x + previousColumnBounds.width;
	}
	else {
		columnBounds.x = 0;
	}
	column.setBounds(columnBounds);
	setColumnPosition(column);
}
/**
 * Set event listeners for the receiver.
 */
void installListeners() {
	Header tableHeader = getHeader();
	Listener listener = getListener();

	super.installListeners();	
	tableHeader.addListener(SWT.MouseMove, listener);
	tableHeader.addListener(SWT.MouseDown, listener);
	tableHeader.addListener(SWT.MouseDoubleClick, listener);
	tableHeader.addListener(SWT.MouseUp, listener);
	
	addListener(SWT.MouseMove, listener);
	addListener(SWT.MouseDown, listener);
	addListener(SWT.MouseDoubleClick, listener);
	addListener(SWT.MouseUp, listener);
	addListener(SWT.Paint, listener);
}
/**
 * Answer the TableColumn at 'index'.
 * If the user has not created any columns the default column is 
 * returned if index is 0.
 * Fix for 1FUSJY5 
 */
TableColumn internalGetColumn(int index) {
	Vector columns = internalGetColumnVector();
	
	if (columns == null) error(SWT.ERROR_CANNOT_GET_ITEM);
	if (index < 0 || index >= columns.size()) {
		error(SWT.ERROR_INVALID_RANGE);
	}
	
	return (TableColumn) columns.elementAt(index);

}
/**
 * Answer the number of columns in the receiver.
 * If the user has not created any columns, 1 is returned since there 
 * always is a default column.
 * Fix for 1FUSJY5
 */
int internalGetColumnCount() {
	Vector columns = internalGetColumnVector();
	int count = 0;
	
	if (columns != null) {
		count = columns.size();
	}
	return count;
}
/**
 * Return a Vector containing all columns of the receiver except 
 * the fill column to the right of all content columns.
 * Return a Vector containing the default column if the user has
 * not created any columns.
 * Fix for 1FUSJY5 
 */
Vector internalGetColumnVector() {
	Vector internalColumnVector;
	TableColumn defaultColumn;
	
	if (columns.isEmpty() == false) {
		internalColumnVector = columns;
	}
	else {
		internalColumnVector = new Vector(1);
		defaultColumn = getDefaultColumn();		
		if (defaultColumn != null) {
			internalColumnVector.addElement(defaultColumn);
		}
	}
	return internalColumnVector;
}
/**
 * Answer whether the mouse pointer is at a position that can
 * start a column resize operation. A column resize can be 
 * started if the mouse pointer is at either the left or right 
 * border of a column.
 * @param event - mouse event specifying the location of the 
 *	mouse pointer.
 */
boolean isColumnResize(Event event) {
	TableColumn hotColumn = getColumnAtX(event.x);
	if (hotColumn == null) return false;
	Rectangle bounds = hotColumn.getBounds();
	int hotColumnIndex = hotColumn.getIndex();
	int columnX = event.x - bounds.x;
	boolean isColumnResize = false;

	if (columnX <= COLUMN_RESIZE_OFFSET && 									// mouse over left side of column? and
		hotColumnIndex != TableColumn.FIRST) {								// it's not the first column)
		if (hotColumnIndex == TableColumn.FILL) {
			hotColumn = (TableColumn) internalGetColumnVector().lastElement();
		}
		else {
			hotColumn = internalGetColumn(hotColumnIndex - 1);
		}
		isColumnResize = hotColumn.getResizable();							// check whether left column can be resized
	}
	else
	if (columnX >= bounds.width - COLUMN_RESIZE_OFFSET && 					// mouse over right side of column and
		hotColumn != getFillColumn()) {										// column is a real one (not the right hand fill column)?
		isColumnResize = hotColumn.getResizable();							// check whether column under cursor can be resized
	}
	return isColumnResize;
}
/**
 * Answer whether a column of the receiver is being resized.
 */
boolean isColumnResizeStarted() {
	return (getResizeColumn() != 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 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();
	TableItem item = getItem(index);

	return (item != null && item.isSelected() == true);
}
/**
 * 'changedItem' has changed. Update the default column width.
 * @param changedItem - the item that has changed
 */
void itemChanged(SelectableItem changedItem, int repaintStartX, int repaintWidth) {
	// call super.itemChanged first to make sure that table image size is 
	// calculated if necessary. Fixes 1FYPBBG.
	super.itemChanged(changedItem, repaintStartX, repaintWidth);
	// remember if any item ever had an image in the first column.
	if (firstColumnImage == false && changedItem.getImage() != null) {
		firstColumnImage = true;
		redraw ();
	}
	setFirstColumnWidth((TableItem) changedItem);	
}
/**
 * A mouse button was released. 
 * Update the display if a column has been resized.
 * @param event - the mouse event for the button up action
 */
void mouseUp(Event event) {
	TableColumn resizeColumn = getResizeColumn();
	Rectangle oldColumnBounds;
	int resizeXPosition;
	int widthChange;
	if (isColumnResizeStarted() == true) {
		oldColumnBounds = resizeColumn.getBounds();
		resizeXPosition = getColumnResizeX();	
		widthChange = resizeXPosition - (oldColumnBounds.x + oldColumnBounds.width);
		if (widthChange >= 0) {
			redraw(resizeXPosition - getGridLineWidth(), 0, 1, getClientArea().height, false);		// remove resize line
			update();															// to avoid cheese caused by scrolling the resize line
		}
		if (widthChange != 0) {
			resizeColumn.setWidth(oldColumnBounds.width + widthChange);
		}
		setResizeColumn(null);
	}
}
/**
 * Adjust the position of all columns starting at 'startIndex'.
 * @param startIndex - index at which the column move should begin
 *	If this is the index of the fill column all columns are moved,
 * 	including the fill column
 * @param moveDistance - distance that the columns should be moved.
 *	< 0 = columns are going to be moved left.
 *	> 0 = columns are going to be moved right.
 */
void moveColumns(int startIndex, int moveDistance) {
	Vector columns = internalGetColumnVector();
	TableColumn moveColumn;
	Rectangle columnBounds;

	if (startIndex == TableColumn.FILL) {
		moveColumn = getFillColumn();
		columnBounds = moveColumn.getBounds();
		columnBounds.x += moveDistance;
		moveColumn.setBounds(columnBounds);
		startIndex = 0;					// continue with first data column
	}
	for (int i = startIndex; i < columns.size(); i++) {
		moveColumn = (TableColumn) columns.elementAt(i);
		columnBounds = moveColumn.getBounds();
		columnBounds.x += moveDistance;
		moveColumn.setBounds(columnBounds);
	}
}
/**
 * Adjust the y position of all columns including the fill column.
 */
void moveColumnsVertical() {
	Enumeration columns = internalGetColumnVector().elements();
	TableColumn column;

	setColumnPosition(getFillColumn());
	while (columns.hasMoreElements() == true) {
		column = (TableColumn) columns.nextElement();
		setColumnPosition(column);
	}
}
/** 
 * A paint event has occurred. Paint the invalidated items.
 * @param event - paint event specifying the invalidated area.
 */
void paint(Event event) {
	int visibleRange[];
	int headerHeight = getHeaderHeight();
	Vector paintColumns = getPaintColumns(event.getBounds());
	TableItem focusItem = null;
	
	if (paintColumns.size() > 0) {
		event.y -= headerHeight;
		visibleRange = getIndexRange(event.getBounds());
		event.y += headerHeight;
		// When the top index is > 0 and the receiver is resized 
		// higher so that the top index becomes 0 the invalidated 
		// rectangle doesn't start below the header widget but at 
		// y position 0. Subtraction of the header height (it is 
		// not above the receiver but on top) causes event.y and 
		// subsequently visibleRange[0] to be negative.
		// Hack to prevent visibleRange[0] from becoming negative.
		// Need to find out why the invalidated area starts at 0
		// in the first place.
		if (visibleRange[0] < 0) {
			visibleRange[0] = 0;
		}
		// 
		visibleRange[1] = Math.min(visibleRange[1], getItemCount()-1-getTopIndex());
		focusItem = paintItems(event, visibleRange[0], visibleRange[1], paintColumns);
	}
	if (getLinesVisible() == true) {
		drawGridLines(event, paintColumns.elements());
	}
	if (focusItem != null) {
		// draw focus on top of drawing grid lines so that focus rectangle 
		// is not obscured by grid. Fixes 1G5X20B
		drawSelectionFocus(focusItem, event.gc);	
	}
}

/**
 * Paint items of the receiver starting at index 'topPaintIndex' and 
 * ending at 'bottomPaintIndex'.
 * @param event - holds the GC to draw on and the clipping rectangle
 * @param topPaintIndex - index of the first item to draw
 * @param bottomPaintIndex - index of the last item to draw
 * @param paintColumns - the table columns that should be painted
 * @return the item that has focus if it was among the rendered items.
 *	null if the focus item was not rendered or if no item has focus (ie. 
 *	because the widget does not have focus)
 */
TableItem paintItems(Event event, int topPaintIndex, int bottomPaintIndex, Vector paintColumns) {
	Enumeration columns;
	TableColumn column;
	TableItem paintItem;
	TableItem focusItem = null;
	Point selectionExtent;
	GC gc = event.gc;
	Color selectionColor = display.getSystemColor(SWT.COLOR_LIST_SELECTION);
	int paintXPosition;
	int paintYPosition;
		
	topPaintIndex += getTopIndex();
	bottomPaintIndex += getTopIndex();

	if ((getStyle () & SWT.VIRTUAL) != 0) {
		for (int i = topPaintIndex; i <= bottomPaintIndex; i++) {
			paintItem = (TableItem) getVisibleItem(i);
			if (!paintItem.cached) {
				Event dataEvent = new Event();
				dataEvent.item = paintItem;
				ignoreRedraw = true;
				sendEvent(SWT.SetData, dataEvent);
				if (isDisposed()) return null;
				//widget could be disposed at this point
				ignoreRedraw = false;
				calculateItemHeight(paintItem);
				paintItem.cached = true;
			}
		}
	}
	
	for (int i = topPaintIndex; i <= bottomPaintIndex; i++) {
		paintItem = (TableItem) getVisibleItem(i);
		paintXPosition = paintItem.getSelectionX();
		paintYPosition = getRedrawY(paintItem);
				
		if (paintItem.isSelected() == true) {
			if ((style & SWT.HIDE_SELECTION) == 0 || isFocusControl()) {
				selectionExtent = paintItem.getSelectionExtent();
				gc.setBackground(selectionColor);
				gc.fillRectangle(paintXPosition, paintYPosition, selectionExtent.x, selectionExtent.y);
			}
		} 
		columns = paintColumns.elements(); 
		while (columns.hasMoreElements() == true) {
			column = (TableColumn) columns.nextElement();
			paintSubItem(event, paintItem, column, paintYPosition);
		}
		if (hasFocus(paintItem)) {
			focusItem = paintItem;
		}
	}
	return focusItem;
}

/**
 * Paint the table item 'paintItem' in 'column' at y position 
 * 'paintYPosition' of the receiver.
 * @param event - holds the GC to draw on and the clipping 
 *	rectangle.
 * @param paintItem - the item to draw
 * @param column - column to draw 'paintItem' in
 * @param paintYPosition - y position in the receiver to draw 
 *	'paintItem' at.
 */
void paintSubItem(Event event, TableItem paintItem, TableColumn column, int paintYPosition) {
	Rectangle columnBounds = column.getBounds();
	Point paintPosition;
	int gridLineWidth = getGridLineWidth();
	int itemDrawStopX = columnBounds.x + columnBounds.width - gridLineWidth;
	int clipX;
	
	if (event.x + event.width > itemDrawStopX) {	// does the invalidated area stretch past the current column's right border?
		clipX = Math.max(columnBounds.x, event.x);
		event.gc.setClipping(											// clip the drawing area
			clipX, event.y, 
			Math.max(0, itemDrawStopX - clipX), event.height);		
	}
	paintPosition = new Point(columnBounds.x, paintYPosition);
	paintItem.paint(event.gc, paintPosition, column);
	if (event.x + event.width > itemDrawStopX) {
		event.gc.setClipping(event.x, event.y, event.width, event.height); // restore original clip rectangle
	}
}
/**
 * Reindex all columns starting at 'startIndex'.
 * Reindexing is necessary when a new column has been inserted.
 */
void reindexColumns(int startIndex) {
	Vector columns = getColumnVector();
	TableColumn column;
	
	for (int i = startIndex; i < getColumnCount(); i++) {
		column = (TableColumn) columns.elementAt(i);
		column.setIndex(i);
	}
}
/**
 * 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>
 * @exception SWTError <ul>
 *    <li>ERROR_ITEM_NOT_REMOVED - if the operation fails because of an operating system failure</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);
	sort (newIndices);
	int start = newIndices [newIndices.length - 1], end = newIndices [0];
	int count = getItemCount();
	if (!(0 <= start && start <= end && end < count)) {
		error (SWT.ERROR_INVALID_RANGE);
	}
	int last = -1;
	for (int i = 0; i < newIndices.length; i++) {
		int index = newIndices[i];
		if (index != last) {
			SelectableItem item = getVisibleItem(index);
			if (item != null) {
				item.dispose();
			} else {
				error(SWT.ERROR_ITEM_NOT_REMOVED);				          
			}
			last = index;
		}
	}
}
public void redraw () {
	checkWidget();
	if (ignoreRedraw) return;
	super.redraw();
}
public void redraw (int x, int y, int width, int height, boolean all) {
	checkWidget();
	if (ignoreRedraw) return;
	super.redraw(x, y, width, height, all);
}
/**
 * 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>
 * @exception SWTError <ul>
 *    <li>ERROR_ITEM_NOT_REMOVED - if the operation fails because of an operating system failure</li>
 * </ul>
 */
public void remove(int index) {
	checkWidget();
	SelectableItem item = getVisibleItem(index);

	if (item != null) {
		item.dispose();
	}
	else {
		if (0 <= index && index < getItemVector().size()) {
			error(SWT.ERROR_ITEM_NOT_REMOVED);
		} 
		else {
			error(SWT.ERROR_INVALID_RANGE);
		}          
	}
}
/**
 * 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>
 * @exception SWTError <ul>
 *    <li>ERROR_ITEM_NOT_REMOVED - if the operation fails because of an operating system failure</li>
 * </ul>
 */
public void remove(int start, int end) {
	checkWidget();
	if (start > end) return;
	if (!(0 <= start && start <= end && end < getItemCount())) {
		error (SWT.ERROR_INVALID_RANGE);
	}
	for (int i = end; i >= start; i--) {
		SelectableItem item = getVisibleItem(i);
		if (item != null) {
			item.dispose();
		} else {
			error(SWT.ERROR_ITEM_NOT_REMOVED);
		}
	}
}
/**
 * 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>
 */
public void removeAll() {
	checkWidget();
	Vector items = getItemVector();

	setRedraw(false);
	setRemovingAll(true);
	for (int i = items.size() - 1; i >= 0; i--) {
		((TableItem) items.elementAt(i)).dispose();
	}
	setItemVector(new Vector());
	reset();
	calculateVerticalScrollbar();
	setRemovingAll(false);
	setRedraw(true);	
}
/**
 * Remove 'column' from the receiver.
 */
void removeColumn(TableColumn column) {
	int index = column.getIndex();
	int columnWidth = column.getWidth();
	int columnCount;

	if (isRemovingAll() == true) {
		getColumnVector().removeElementAt(index);
	}
	else {		
		getColumnVector().removeElementAt(index);
		columnCount = getColumnCount();
		// Never remove the data of the last user created column. 
		// SWT for Windows does the same.
		if (columnCount > 0) {
			removeColumnData(column);
			removeColumnVisual(column);		
			Enumeration items = getItemVector ().elements ();
			while (items.hasMoreElements()) {
				TableItem item = (TableItem)items.nextElement();
				Color [] cellBackground = item.cellBackground;
				if (cellBackground != null) {
					Color [] temp = new Color [columnCount];
					System.arraycopy (cellBackground, 0, temp, 0, index);
					System.arraycopy (cellBackground, index + 1, temp, index, columnCount - index);
					item.cellBackground = temp;
				}
				Color [] cellForeground = item.cellForeground;
				if (cellForeground != null) {
					Color [] temp = new Color [columnCount];
					System.arraycopy (cellForeground, 0, temp, 0, index);
					System.arraycopy (cellForeground, index + 1, temp, index, columnCount - index);
					item.cellForeground = temp;
				}
				Font [] cellFont = item.cellFont;
				if (cellFont != null) {
					Font [] temp = new Font [columnCount];
					System.arraycopy (cellFont, 0, temp, 0, index);
					System.arraycopy (cellFont, index + 1, temp, index, columnCount - index);
					item.cellFont = temp;
				}
			}		
		}
		else {
			redraw();
			getHeader().redraw();
		}
		if (index < columnCount) {					// is there a column after the removed one?
			reindexColumns(index);
		}
		// last user created column is about to be removed.
		if (columnCount == 0) {		
			TableColumn defaultColumn = getDefaultColumn();
			defaultColumn.pack();						// make sure the default column has the right size...
			setColumnPosition(defaultColumn);			// ...and is at the right position
		}
		// Fixes for 1G1L0UT
		// Reduce the content width by the width of the removed column
		setContentWidth(getContentWidth() - columnWidth);
		// claim free space
		claimRightFreeSpace();		
		//
	}
}
/**
 * Remove the column 'column' from the table data.
 */
void removeColumnData(TableColumn column) {
	Enumeration tableItems = getItemVector().elements();
	TableItem tableItem;

	while (tableItems.hasMoreElements() == true ) {
		tableItem = (TableItem) tableItems.nextElement();
		tableItem.removeColumn(column);
	}
}
/**
 * Remove the column 'column'.
 * Set the position of the following columns.
 */
void removeColumnVisual(TableColumn column) {
	int columnWidth = column.getWidth();
		
	// move following columns to the left
	moveColumns(column.getIndex(), columnWidth * -1);
	redraw();
	getHeader().redraw();
}
/**
 * 'item' has been removed from the receiver. 
 * Update the display and the scroll bars.
 */
void removedItem(SelectableItem item) {
	int oldHeight = getItemHeight();
	super.removedItem (item);
	if (getItemCount() == 0 && drawGridLines && oldHeight != getItemHeight()) {
		redraw();
	}
}
/** 
 * Remove 'item' from the receiver. 
 * @param item - item that should be removed from the receiver
 */
void removeItem(TableItem item) {
	if (isRemovingAll() == true) return;
	
	Vector items = getItemVector();
	int index = items.indexOf(item);
	if (index != -1) {		
		removingItem(item);				
		items.removeElementAt(index);
		for (int i = index; i < items.size(); i++) {
			TableItem anItem = (TableItem) items.elementAt(i);
			anItem.setIndex(anItem.getIndex() - 1);
		}		
		removedItem(item);		
	}
}
/**
 * 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) {
		error(SWT.ERROR_NULL_ARGUMENT);
	}
	removeListener(SWT.Selection, listener);
	removeListener(SWT.DefaultSelection, listener);
}
/** 
 * Reset cached data of column at 'columnIndex' for the items of the receiver. 
 * @param columnIndex - index of the column for which the item data should be 
 *	reset.
 */
void resetTableItems(int columnIndex) {
	Enumeration tableItems = getItemVector().elements();
	TableItem tableItem;

	while (tableItems.hasMoreElements() == true ) {
		tableItem = (TableItem) tableItems.nextElement();
		tableItem.reset(columnIndex);
	}
}
/**
 * The receiver has been resized. Resize the fill column 
 * and the header widget.
 */
void resize(Event event) {
	TableColumn fillColumn = getFillColumn();
	Rectangle fillColumnBounds;

	super.resize(event);
	// the x position may change in super.resize.
	// get the column bounds after calling super.resize. Fixes 1G7ALGG
	fillColumnBounds = fillColumn.getBounds();
	fillColumnBounds.width = Math.max(0, getClientArea().width - getContentWidth());
	fillColumn.setBounds(fillColumnBounds);
	resizeHeader();
}
/**
 * Resize the header widget to occupy the whole width of the
 * receiver.
 */
void resizeHeader() {
	Header tableHeader = getHeader();
	Point size = tableHeader.getSize();

	size.x = Math.max(getContentWidth(), getClientArea().width);
	tableHeader.setSize(size);
}
/**
 * Redraw 'column' after its width has been changed.
 * @param column - column whose width has changed.
 * @param oldColumnWidth - column width before resize
 * @param oldColumnWidth - column width after resize 
 */
void resizeRedraw(TableColumn column, int oldColumnWidth, int newColumnWidth) {
	Rectangle columnBounds = column.getBounds();
	int columnIndex = column.getIndex();
	int oldRedrawStartX[] = getResizeRedrawX(columnIndex, oldColumnWidth);
	int newRedrawStartX[] = getResizeRedrawX(columnIndex, newColumnWidth);
	int itemHeight = getItemHeight();
	int widthChange = newColumnWidth - oldColumnWidth;
	int topIndex = getTopIndex();

	for (int i = 0; i < newRedrawStartX.length; i++) {
		if (newRedrawStartX[i] != oldRedrawStartX[i]) {
			if (widthChange > 0) {
				newRedrawStartX[i] = oldRedrawStartX[i];
			}
			redraw(
				columnBounds.x + newRedrawStartX[i], columnBounds.y + itemHeight * (i + topIndex), 
				columnBounds.width - newRedrawStartX[i], itemHeight, false);
		}
	}
}
/**
 * Scroll horizontally by 'numPixel' pixel.
 * @param numPixel - the number of pixel to scroll
 *	< 0 = columns are going to be moved left.
 *	> 0 = columns are going to be moved right.
 */
void scrollHorizontal(int numPixel) {
	Rectangle clientArea = getClientArea();	

	scroll(
		numPixel, 0, 								// destination x, y
		0, 0, 										// source x, y
		clientArea.width, clientArea.height, true);
	getHeader().scroll(
		numPixel, 0, 								// destination x, y
		0, 0, 										// source x, y
		clientArea.width, clientArea.height, true);
	moveColumns(TableColumn.FILL, numPixel);
}
/**
 * Scroll vertically by 'scrollIndexCount' items.
 * @param scrollIndexCount - the number of items to scroll.
 *	scrollIndexCount > 0 = scroll up. scrollIndexCount < 0 = scroll down
 */
void scrollVertical(int scrollIndexCount) {
	int scrollAmount = scrollIndexCount * getItemHeight();
	int headerHeight = getHeaderHeight();
	int destY;
	int sourceY;
	boolean scrollUp = scrollIndexCount < 0;
	Rectangle clientArea = getClientArea();

	if (scrollIndexCount == 0) {
		return;
	}
	if (scrollUp == true) {
		destY = headerHeight - scrollAmount;
		sourceY = headerHeight;
	}
	else {
		destY = headerHeight;
		sourceY = destY + scrollAmount;
	}
	scroll(
		0, destY, 									// destination x, y
		0, sourceY,									// source x, y
		clientArea.width, clientArea.height, true);
}
/**
 * Scroll items down to make space for a new item added to 
 * the receiver at position 'index'.
 * @param index - position at which space for one new item
 *	should be made. This index is relative to the first item 
 *	of the receiver.
 */
void scrollVerticalAddingItem(int index) {
	int itemHeight = getItemHeight();
	int sourceY = getHeaderHeight();
	Rectangle clientArea = getClientArea();	

	if (index >= getTopIndex()) {
		sourceY += (index-getTopIndex()) * itemHeight;
	}
	scroll(
		0, sourceY + itemHeight, 				// destination x, y
		0, sourceY,								// source x, y
		clientArea.width, clientArea.height, true);
}
/**
 * Scroll the items below the item at position 'index' up 
 * so that they cover the removed item.
 * @param index - index of the removed item
 */
void scrollVerticalRemovedItem(int index) {
	int itemHeight = getItemHeight();
	int headerHeight = getHeaderHeight();
	int destY;
	Rectangle clientArea = getClientArea();		

	destY = Math.max(headerHeight, headerHeight + (index - getTopIndex()) * itemHeight);
	scroll(
		0, destY, 								// destination x, y
		0, destY + itemHeight,					// source x, y
		clientArea.width, clientArea.height, true);
}
/**
 * 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.
 *
 * @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)) return;
	if ((style & SWT.SINGLE) != 0) {
		deselectAllExcept(getVisibleItem(indices[0]));
	}
	SelectableItem item = null;
	for (int i = length - 1; i >= 0; --i) {
		item = getVisibleItem(indices[i]);
		if (item != null) {
			select(item);
		}
	}
	if (item != null) {
		setLastSelection(item, false);
	}
}
/**
 * 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();
	SelectableItem item = getVisibleItem(index);
	
	if (isMultiSelect() == false) {
		deselectAllExcept(getVisibleItem(index));
	}
	if (item != null) {
		select(item);
		setLastSelection(item, 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.
 *
 * @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;
	int count = getItemVector().size();
	if (count == 0 || start >= count) return;
	start = Math.max (0, start);
	end = Math.min (end, count - 1);
	if ((style & SWT.SINGLE) != 0) {
		deselectAllExcept(getVisibleItem(start));
	}
	// select in the same order as all the other selection and deslection methods.
	// Otherwise setLastSelection repeatedly changes the lastSelectedItem for repeated 
	// selections of the items, causing flash.
	SelectableItem item = null;
	for (int i = end; i >= start; i--) {
		item = getVisibleItem(i);
		if (item != null) {
			select(item);
		}
	}
	if (item != null) {
		setLastSelection(item, false);
	}
}
/**
 * Selects all of the items in the receiver.
 * <p>
 * If the receiver is single-select, do nothing.
 *
 * @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();
	Enumeration items = getItemVector().elements();
	TableItem item = null;

	if (isMultiSelect() == false) {
		return;
	}
	while (items.hasMoreElements() == true) {
		item = (TableItem) items.nextElement();
		select(item);
	}
	if (item != null) {
		setLastSelection(item, false);
	}
}
/**
 * Set the y position of 'column'.
 * @param column - the TableColumn that should be set to 
 *	a new y position.
 */
void setColumnPosition(TableColumn column) {
	Rectangle bounds = column.getBounds();

	bounds.y = getHeaderHeight() - getTopIndex() * getItemHeight();
	column.setBounds(bounds);	
}
/**
 * Change the cursor of the receiver.
 * @param isColumnResizeCursor - indicates whether the column 
 *	resize cursor or the regular cursor should be set.
 */
void setColumnResizeCursor(boolean isColumnResizeCursor) {
	if (isColumnResizeCursor != this.isColumnResizeCursor) {
		this.isColumnResizeCursor = isColumnResizeCursor;
		if (isColumnResizeCursor == true) {
			setCursor(getColumnResizeCursor());
		}
		else {
			setCursor(null);
		}
	}
}
/**
 * Set the current position of the resized column to 'xPosition'.
 * @param xPosition - the current position of the resized column
 */
void setColumnResizeX(int xPosition) {
	columnResizeX = xPosition;
}
/**
 * Set the width of the receiver's contents to 'newWidth'.
 * Content width is used to calculate the horizontal scrollbar.
 */
void setContentWidth(int newWidth) {
	TableColumn fillColumn = getFillColumn();
	Rectangle fillColumnBounds;
	int widthDiff = newWidth - getContentWidth();

	super.setContentWidth(newWidth);
	if (fillColumn != null) {
		fillColumnBounds = fillColumn.getBounds();
		fillColumnBounds.x += widthDiff;
		fillColumnBounds.width = Math.max(0, getClientArea().width - newWidth);
		fillColumn.setBounds(fillColumnBounds);
	}
}
/**
 * Set the width of the first column to fit 'item' if it is longer than 
 * the current column width.
 * Do nothing if the user has already set a width.
 */
void setFirstColumnWidth(TableItem item) {
	int newWidth;
	TableColumn column;

	if (internalGetColumnCount() > 0) {
		column = internalGetColumn(TableColumn.FIRST);		
		if (column.isDefaultWidth() == true) {
			newWidth = Math.max(column.getWidth(), item.getPreferredWidth(TableColumn.FIRST));
			column.setWidth(newWidth);
			column.setDefaultWidth(true);					// reset to true so that we know when the user has set 
															// the width instead of us setting a default width.
		}
	}
}
public void setFont(Font font) {
	checkWidget();
	int itemCount = getItemCount();

	if (font != null && font.equals(getFont()) == true) {
		return;
	}
	setRedraw(false);						// disable redraw because itemChanged() triggers undesired redraw	
	resetItemData();	
	super.setFont(font);
	
	GC gc = new GC(this);
	Point extent = gc.stringExtent("String");
	fontHeight = extent.y;
	gc.dispose();
	
	for (int i = 0; i < itemCount; i++) {
		itemChanged(getItem(i), 0, getClientArea().width);
	}
	setRedraw(true);						// re-enable redraw
	getHeader().setFont(font);
}
/**
 * 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 headerVisible) {
	checkWidget();
	if (headerVisible != getHeaderVisible()) {
		getHeader().setLocation(0, 0);
		getHeader().setVisible(headerVisible);
		// Windows resets scrolling so do we
		setTopIndex(0, true);
		moveColumnsVertical();
		resizeVerticalScrollbar();
		redraw();
	}
}
/**
 * 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);
	int itemCount = getItemCount ();
	if (count == itemCount) return;
	setRedraw (false);
	remove (count, itemCount - 1);
	for (int i = itemCount; i<count; i++) {
		new TableItem (this, SWT.NONE);
	}
	setRedraw (true);
}
/**
 * Set the vector that stores the items of the receiver 
 * to 'newVector'.
 * @param newVector - Vector to use for storing the items of 
 *	the receiver.
 */
void setItemVector(Vector newVector) {
	items = newVector;
}
/**
 * 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 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 drawGridLines) {
	checkWidget();
	if (this.drawGridLines != drawGridLines) {
		this.drawGridLines = drawGridLines;
		redraw();
	}
}
public void setRedraw(boolean redraw) {
	checkWidget();
	super.setRedraw(redraw);
	getHeader().setRedraw(redraw);
}
/**
 * Set the column that is being resized to 'column'. 
 * @param column - the TableColumn that is being resized. 
 * 	A null value indicates that no column resize operation is 
 *	in progress.
 */
void setResizeColumn(TableColumn column) {
	resizeColumn = column;
}
/**
 * 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.
 *
 * @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);
	int length = indices.length;
	if (length == 0 || ((style & SWT.SINGLE) != 0 && length > 1)) {
		deselectAll ();
		return;
	}
	if ((style & SWT.MULTI) != 0) {
		Vector keepSelected = new Vector(length);
		for (int i = 0; i < length; i++) {
			SelectableItem item = getVisibleItem(indices[i]);
			if (item != null) {
				keepSelected.addElement(item);
			}
		}
		deselectAllExcept(keepSelected);
	}
	select(indices);
	showSelection ();
}
/**
 * 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.
 *
 * @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);
	int length = items.length;
	if (length == 0 || ((style & SWT.SINGLE) != 0 && length > 1)) {
		deselectAll ();
		return;
	}
	setSelectableSelection(items);
}
/**
 * Selects the item at the given zero-relative index in the receiver. 
 * The current selected 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();
	deselectAllExcept(getVisibleItem(index));
	select(index);
	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.
 * 
 * @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 ();
	if (end < 0 || start > end || ((style & SWT.SINGLE) != 0 && start != end)) {
		deselectAll ();
		return;
	}
	int count = getItemVector().size();
	if (count == 0 || start >= count) {
		deselectAll ();
		return;
	}
	start = Math.max (0, start);
	end = Math.min (end, count - 1);
	if ((style & SWT.MULTI) != 0) {
		Vector keepSelected = new Vector();
		for (int i = start; i <= end; i++) {
			SelectableItem item = getVisibleItem(i);
			if (item != null) {
				keepSelected.addElement(item);
			}
		}
		deselectAllExcept(keepSelected);
	}
	select(start, end);
	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();
	int itemCount = getItemCount();
	int itemCountWhole = getItemCountWhole();
	if (index < 0 || itemCount == 0) return;
	if (index >= itemCount) index = itemCount - 1;		
	if (itemCount > itemCountWhole) {
		if (index + itemCountWhole <= itemCount) {
			setTopIndex(index, true);
		} else if (index > itemCount - itemCountWhole) {
			setTopIndex(itemCount - itemCountWhole, true);
		} else {
			showSelectableItem(index);
		}
	}
}
/**
 * Set the index of the first visible item in the receiver's client 
 * area to 'index'.
 * @param index - 0-based index of the first visible item in the 
 *	receiver's client area.
 * @param adjustScrollbar - true=set the position of the vertical 
 *	scroll bar to the new top index. 
 *	false=don't adjust the vertical scroll bar
 */
void setTopIndexNoScroll(int index, boolean adjustScrollbar) {
	super.setTopIndexNoScroll(index, adjustScrollbar);
	moveColumnsVertical();
}

/**
 * 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.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.getParent() != this) return;
	if (columns == null || columns.size() <= 1) return;
}

/**
 * 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);
	showSelectableItem(item);
}
/**
 * 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();
	super.showSelection();
}
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;
		   		}
	    	}
	    }
	}
}
/**
 * Start a column resize operation.
 * @param event - the mouse event that occured over the header 
 * 	widget
 */
void startColumnResize(Event event) {
	Vector columns = internalGetColumnVector();
	TableColumn hitColumn = getColumnAtX(event.x);
	Rectangle hitColumnBounds;
	int hitIndex = hitColumn.getIndex();

	if (hitColumn == getFillColumn()) {										// clicked on the fill column?
		hitColumn = (TableColumn) columns.lastElement();					// resize the last real column
	}
	else 
	if ((event.x - hitColumn.getBounds().x <= COLUMN_RESIZE_OFFSET) && 		// check if left side of a column was clicked
		(hitIndex > 0)) {													
		hitColumn = (TableColumn) columns.elementAt(hitIndex - 1);			// resize the preceding column
	}
	hitColumnBounds = hitColumn.getBounds();
	setColumnResizeX(hitColumnBounds.x + hitColumnBounds.width);
	setResizeColumn(hitColumn);
}
/**
 * Return 'text' after it has been checked to be no longer than 'maxWidth' 
 * when drawn on 'gc'.
 * If it is too long it will be truncated up to the last character.
 * @param text - the String that should be checked for length
 * @param maxWidth - maximum width of 'text'
 * @param gc - GC to use for String measurement
 */
String trimItemText(String text, int maxWidth, GC gc) {
	int textWidth;
	int dotsWidth;

	if (text != null && text.length() > 1) {
		textWidth = gc.stringExtent(text).x;
		if (textWidth > maxWidth) {
			dotsWidth = gc.stringExtent(Table.DOT_STRING).x;
			while (textWidth + dotsWidth > maxWidth && text.length() > 1) {
				text = text.substring(0, text.length() - 1);		// chop off one character at the end
				textWidth = gc.stringExtent(text).x;
			}
			text = text.concat(Table.DOT_STRING);
		}
	}
	return text;
}

}
