package org.eclipse.swt.widgets; | |
/* | |
* (c) Copyright IBM Corp. 2000, 2001. | |
* All Rights Reserved | |
*/ | |
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</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 dotsWidth; // width of the static String dots (see above) | |
private int fontHeight; // font height, avoid use GC.stringExtend for each pain | |
/** | |
* 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 | |
* for all SWT widget classes should include a comment which | |
* describes the style constants which are applicable to the class. | |
* </p> | |
* | |
* @param parent a composite control which will be the parent of the new instance (cannot be null) | |
* @param style the style of control to construct | |
* | |
* @exception IllegalArgumentException <ul> | |
* <li>ERROR_NULL_ARGUMENT - if the parent is null</li> | |
* </ul> | |
* @exception SWTException <ul> | |
* <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the parent</li> | |
* <li>ERROR_INVALID_SUBCLASS - if this class is not an allowed subclass</li> | |
* </ul> | |
* | |
* @see SWT | |
* @see Widget#checkSubclass | |
* @see Widget#getStyle | |
*/ | |
public 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 | |
if (getColumnCount() > 1) { | |
insertColumnData(column); | |
} | |
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); | |
} | |
/** | |
* 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) { | |
int itemHeight = getItemHeight(); | |
int itemIndex; | |
TableItem hitItem; | |
TableColumn hitColumn = getColumnAtX (event.x); | |
Event columnDblClickEvent; | |
boolean isFullSelection = (getStyle () & SWT.FULL_SELECTION) != 0; | |
if (isFocusControl () == false) { | |
forceFocus (); | |
} | |
if (hitColumn != null) { | |
itemIndex = (event.y - getHeaderHeight()) / itemHeight + getTopIndex(); | |
hitItem = (TableItem) getVisibleItem(itemIndex); | |
if (hitItem != null && | |
(hitColumn.getIndex() == TableColumn.FIRST || isFullSelection)) { | |
if (hitItem.isSelectionHit(event.x) == true) { | |
columnDblClickEvent = new Event(); | |
columnDblClickEvent.item = hitItem; | |
notifyListeners(SWT.DefaultSelection, columnDblClickEvent); | |
} | |
} | |
else { | |
deselectAll(); | |
} | |
} | |
} | |
/** | |
* 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); | |
// only react to button one clicks. fixes bug 6770 | |
if (event.button != 1) { | |
return; | |
} | |
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) { | |
doCheckItem(hitItem); | |
} | |
} | |
else { | |
deselectAll(); | |
} | |
} | |
} | |
/** | |
* 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) { | |
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) { | |
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 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> | |
*/ | |
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; | |
redraw(getColumnResizeX(), 0, 1, lineHeight, false); | |
setColumnResizeX(xPosition); | |
gc.drawLine(xPosition, 0, xPosition, 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(getDisplay().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 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(getDisplay(), 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 width of the replacement String used to indicate | |
* truncated items. | |
* Cached to speed up calculation of truncated items. | |
* @param gc - GC used to measure the width of the replacement | |
* String | |
*/ | |
int getDotsWidth(GC gc) { | |
return dotsWidth; | |
} | |
/** | |
* 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. | |
* | |
* @exception SWTException <ul> | |
* <li>ERROR_WIDGET_DISPOSED - 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; | |
} | |
/** | |
* Answer the header height or 0 if the header is not visible. | |
*/ | |
int getHeaderHeight() { | |
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 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> | |
*/ | |
public TableItem getItem(Point point) { | |
int headerHeight = getHeaderHeight(); | |
TableColumn column = getColumnAtX(point.x); | |
TableItem item = null; | |
if (column != null && column.getIndex() != TableColumn.FILL && point.y - headerHeight > 0) { | |
int itemIndex = (point.y - headerHeight) / getItemHeight() + getTopIndex(); | |
item = (TableItem) getVisibleItem(itemIndex); | |
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() + getDisplay().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) { | |
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: | |
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()); | |
} | |
} | |
/** | |
* 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(DOT_STRING); | |
dotsWidth = extent.x; | |
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.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) { | |
if (widthChange > 0) { | |
redraw(resizeXPosition, 0, 1, getClientArea().height, false); // remove resize line | |
update(); // to avoid cheese caused by scrolling the resize line | |
} | |
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 = getDisplay().getSystemColor(SWT.COLOR_LIST_SELECTION); | |
Point fullSelectionExtent; | |
int paintXPosition; | |
int paintYPosition; | |
topPaintIndex += getTopIndex(); | |
bottomPaintIndex += getTopIndex(); | |
for (int i = topPaintIndex; i <= bottomPaintIndex; i++) { | |
paintItem = (TableItem) getVisibleItem(i); | |
paintXPosition = paintItem.getSelectionX(); | |
paintYPosition = getRedrawY(paintItem); | |
fullSelectionExtent = getFullSelectionExtent(paintItem); | |
gc.setBackground(paintItem.getBackground()); | |
gc.fillRectangle(paintXPosition, paintYPosition, fullSelectionExtent.x, fullSelectionExtent.y); | |
if (paintItem.isSelected() == true) { | |
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> | |
* </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(); | |
SelectableItem item; | |
int [] sortedIndices; | |
int last = -1; | |
if (indices == null) { | |
error(SWT.ERROR_NULL_ARGUMENT); | |
} | |
sortedIndices = new int[indices.length]; | |
System.arraycopy (indices, 0, sortedIndices, 0, indices.length); | |
sort(sortedIndices); // sort indices in descending order | |
for (int i = 0; i < sortedIndices.length; i++) { | |
if (sortedIndices[i] != last) { | |
item = getVisibleItem(sortedIndices[i]); | |
if (item != null) { | |
item.dispose(); | |
} | |
else { | |
error(SWT.ERROR_ITEM_NOT_REMOVED); | |
} | |
last = sortedIndices[i]; | |
} | |
} | |
} | |
/** | |
* Removes the item from the receiver at the given | |
* zero-relative index. | |
* | |
* @param index the index for the item | |
* | |
* @exception IllegalArgumentException <ul> | |
* <li>ERROR_INVALID_RANGE - if the index is not between 0 and the number of elements in the list minus 1 (inclusive)</li> | |
* </ul> | |
* @exception SWTException <ul> | |
* <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> | |
* <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> | |
* </ul> | |
* @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 { | |
error(SWT.ERROR_ITEM_NOT_REMOVED); | |
} | |
} | |
/** | |
* 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(); | |
SelectableItem item; | |
for (int i = end; i >= start; i--) { | |
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); | |
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() + 1, columnWidth * -1); | |
redraw(); | |
getHeader().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 | |
*/ | |
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. | |
* If the item at the given zero-relative index in the receiver | |
* is not selected, it is selected. If the item at the index | |
* was selected, it remains selected. Indices that are out | |
* of range and duplicate 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> | |
*/ | |
public void select(int indices[]) { | |
checkWidget(); | |
SelectableItem item = null; | |
int selectionCount; | |
if (indices == null) { | |
error(SWT.ERROR_NULL_ARGUMENT); | |
} | |
selectionCount = indices.length; | |
if (isMultiSelect() == false && selectionCount > 1) { | |
selectionCount = 1; | |
deselectAllExcept(getVisibleItem(indices[0])); | |
} | |
for (int i = selectionCount - 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 at the given zero-relative indices in the receiver. | |
* If the item at the index was already selected, it remains | |
* selected. The range of the indices is inclusive. Indices that are | |
* out of range 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> | |
*/ | |
public void select(int start, int end) { | |
checkWidget(); | |
SelectableItem item = null; | |
if (isMultiSelect() == false) { | |
if (start < 0 && end >= 0) { | |
start = 0; | |
} | |
end = start; | |
deselectAllExcept(getVisibleItem(end)); | |
} | |
// 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. | |
for (int i = end; i >= start; i--) { | |
item = getVisibleItem(i); | |
if (item != null) { | |
select(item); | |
} | |
} | |
if (item != null) { | |
setLastSelection(item, false); | |
} | |
} | |
/** | |
* Selects all 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 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(DOT_STRING); | |
dotsWidth = extent.x; | |
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 visible 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(); | |
} | |
} | |
/** | |
* 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 visible 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 selected is first cleared, then the new items are selected. | |
* | |
* @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(); | |
Vector keepSelected; | |
if (indices == null) { | |
error(SWT.ERROR_NULL_ARGUMENT); | |
} | |
keepSelected = new Vector(indices.length); | |
for (int i = 0; i < indices.length; i++) { | |
SelectableItem item = getVisibleItem(indices[i]); | |
if (item != null) { | |
keepSelected.addElement(item); | |
} | |
} | |
deselectAllExcept(keepSelected); | |
select(indices); | |
} | |
/** | |
* Sets the receiver's selection to be the given array of items. | |
* The current selected is first cleared, then the new items are | |
* selected. | |
* | |
* @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 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#deselectAll() | |
* @see Table#select(int) | |
*/ | |
public void setSelection(TableItem selectionItems[]) { | |
checkWidget(); | |
if (selectionItems == null) { | |
error(SWT.ERROR_NULL_ARGUMENT); | |
} | |
setSelectableSelection(selectionItems); | |
} | |
/** | |
* 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); | |
} | |
/** | |
* Selects the items at the given zero-relative indices in the receiver. | |
* The current selected if first cleared, then the new items are selected. | |
* | |
* @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(); | |
Vector keepSelected = new Vector(end - start + 1); | |
for (int i = start; i <= end; i++) { | |
SelectableItem item = getVisibleItem(i); | |
if (item != null) { | |
keepSelected.addElement(item); | |
} | |
} | |
deselectAllExcept(keepSelected); | |
select(start, end); | |
} | |
/** | |
* 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 - itemCountWhole; | |
} | |
super.setTopIndex(index, true); | |
} | |
/** | |
* 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 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 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 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 = getDotsWidth(gc); | |
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; | |
} | |
} |