blob: aeab3e4eb2ca310ac9a24e268d559b0467ad9c5d [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2012, 2014 EclipseSource and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* EclipseSource - initial API and implementation
******************************************************************************/
package org.eclipse.nebula.widgets.grid;
import static org.eclipse.swt.internal.widgets.MarkupUtil.isMarkupEnabledFor;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import org.eclipse.nebula.widgets.grid.internal.IGridAdapter;
import org.eclipse.nebula.widgets.grid.internal.IScrollBarProxy;
import org.eclipse.nebula.widgets.grid.internal.NullScrollBarProxy;
import org.eclipse.nebula.widgets.grid.internal.ScrollBarProxyAdapter;
import org.eclipse.nebula.widgets.grid.internal.gridkit.GridThemeAdapter;
import org.eclipse.rap.rwt.RWT;
import org.eclipse.rap.rwt.internal.textsize.TextSizeUtil;
import org.eclipse.rap.rwt.internal.theme.IThemeAdapter;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.SelectionListener;
import org.eclipse.swt.events.TreeListener;
import org.eclipse.swt.graphics.Font;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.swt.internal.SerializableCompatibility;
import org.eclipse.swt.internal.widgets.ICellToolTipAdapter;
import org.eclipse.swt.internal.widgets.ICellToolTipProvider;
import org.eclipse.swt.internal.widgets.IItemHolderAdapter;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Event;
import org.eclipse.swt.widgets.Item;
import org.eclipse.swt.widgets.Listener;
import org.eclipse.swt.widgets.TypedListener;
/**
* <p>
* NOTE: THIS WIDGET AND ITS API ARE STILL UNDER DEVELOPMENT. THIS IS A PRE-RELEASE ALPHA
* VERSION. USERS SHOULD EXPECT API CHANGES IN FUTURE VERSIONS.
* </p>
* Instances of this class implement a selectable user interface object that
* displays a list of images and strings and issue notification when selected.
* <p>
* The item children that may be added to instances of this class must be of
* type {@code GridItem}.
* </p>
* <dl>
* <dt><b>Styles:</b></dt>
* <dd>SWT.SINGLE, SWT.MULTI, SWT.NO_FOCUS, SWT.CHECK, SWT.VIRTUAL</dd>
* <dt><b>Events:</b></dt>
* <dd>Selection, DefaultSelection</dd>
* </dl>
*/
@SuppressWarnings("restriction")
public class Grid extends Composite {
private static final int MIN_ITEM_HEIGHT = 16;
private static final int GRID_WIDTH = 1;
private List<GridItem> items = new ArrayList<GridItem>();
private List<GridItem> rootItems = new ArrayList<GridItem>();
private List<GridItem> selectedItems = new ArrayList<GridItem>();
private List<Point> selectedCells = new ArrayList<Point>();
private List<GridColumn> columns = new ArrayList<GridColumn>();
private List<GridColumn> displayOrderedColumns = new ArrayList<GridColumn>();
private List<GridColumnGroup> columnGroups = new ArrayList<GridColumnGroup>();
private GridItem focusItem;
private boolean isTree;
private boolean disposing;
private boolean columnHeadersVisible;
private boolean columnFootersVisible;
private boolean linesVisible = true;
private int currentVisibleItems;
private int selectionType = SWT.SINGLE;
private boolean selectionEnabled = true;
private boolean cellSelectionEnabled;
private int customItemHeight = -1;
private int groupHeaderHeight;
private Point itemImageSize;
private Listener resizeListener;
private Listener disposeListener;
private boolean isTemporaryResize;
private IScrollBarProxy vScroll;
private IScrollBarProxy hScroll;
private boolean scrollValuesObsolete;
private int topIndex = -1;
private int bottomIndex = -1;
private boolean bottomIndexShownCompletely;
private final IGridAdapter gridAdapter;
private transient CompositeItemHolder itemHolder;
boolean hasDifferingHeights;
LayoutCache layoutCache;
/**
* Constructs a new instance of this class given its parent and a style
* value describing its behavior and appearance.
* <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
* @throws IllegalArgumentException
* <ul>
* <li>ERROR_NULL_ARGUMENT - if the parent is null</li>
* </ul>
* @throws org.eclipse.swt.SWTException
* <ul>
* <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that
* created the parent</li>
* </ul>
* @see SWT#SINGLE
* @see SWT#MULTI
*/
public Grid( Composite parent, int style ) {
super( parent, checkStyle( style ) );
if( ( style & SWT.MULTI ) != 0 ) {
selectionType = SWT.MULTI;
}
if( getVerticalBar() != null ) {
getVerticalBar().setVisible( false );
vScroll = new ScrollBarProxyAdapter( getVerticalBar() );
} else {
vScroll = new NullScrollBarProxy();
}
if( getHorizontalBar() != null ) {
getHorizontalBar().setVisible( false );
hScroll = new ScrollBarProxyAdapter( getHorizontalBar() );
} else {
hScroll = new NullScrollBarProxy();
}
gridAdapter = new GridAdapter();
layoutCache = new LayoutCache();
initListeners();
}
/**
* {@inheritDoc}
*/
@Override
public Point computeSize( int wHint, int hHint, boolean changed ) {
checkWidget();
Point rreferredSize = null;
if( wHint == SWT.DEFAULT || hHint == SWT.DEFAULT ) {
rreferredSize = getTableSize();
rreferredSize.x += 2 * getBorderWidth();
rreferredSize.y += 2 * getBorderWidth();
}
int width = 0;
int height = 0;
if( wHint == SWT.DEFAULT ) {
width += rreferredSize.x;
if( getVerticalBar() != null ) {
width += getVerticalBar().getSize().x;
}
} else {
width = wHint;
}
if( hHint == SWT.DEFAULT ) {
height += rreferredSize.y;
if( getHorizontalBar() != null ) {
height += getHorizontalBar().getSize().y;
}
} else {
height = hHint;
}
return new Point( width, height );
}
/**
* 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} interface.
* <p>
* Cell selection events may have <code>Event.detail = SWT.DRAG</code> when the
* user is drag selecting multiple cells. A follow up selection event will be generated
* when the drag is complete.
*
* @param listener the listener which should be notified
* @throws IllegalArgumentException
* <ul>
* <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
* </ul>
* @throws org.eclipse.swt.SWTException
* <ul>
* <li>ERROR_WIDGET_DISPOSED - 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 addSelectionListener( SelectionListener listener ) {
checkWidget();
if( listener == null ) {
SWT.error( SWT.ERROR_NULL_ARGUMENT );
}
TypedListener typedListener = new TypedListener( listener );
addListener( SWT.Selection, typedListener );
addListener( SWT.DefaultSelection, typedListener );
}
/**
* Removes the listener from the collection of listeners who will be
* notified when the receiver's selection changes.
*
* @param listener the listener which should no longer be notified
* @see SelectionListener
* @see #addSelectionListener(SelectionListener)
* @throws org.eclipse.swt.SWTException
* <ul>
* <li>ERROR_WIDGET_DISPOSED - 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 removeSelectionListener( SelectionListener listener ) {
checkWidget();
if( listener == null ) {
SWT.error( SWT.ERROR_NULL_ARGUMENT );
}
removeListener( SWT.Selection, listener );
removeListener( SWT.DefaultSelection, listener );
}
/**
* Adds the listener to the collection of listeners who will be notified
* when the receiver's items changes, by sending it one of the messages
* defined in the {@code TreeListener} interface.
*
* @param listener the listener which should be notified
* @throws IllegalArgumentException
* <ul>
* <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
* </ul>
* @throws org.eclipse.swt.SWTException
* <ul>
* <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
* <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that
* created the receiver</li>
* </ul>
* @see TreeListener
* @see #removeTreeListener
* @see org.eclipse.swt.events.TreeEvent
*/
public void addTreeListener( TreeListener listener ) {
checkWidget();
if( listener == null ) {
SWT.error( SWT.ERROR_NULL_ARGUMENT );
}
TypedListener typedListener = new TypedListener( listener );
addListener( SWT.Expand, typedListener );
addListener( SWT.Collapse, typedListener );
}
/**
* Removes the listener from the collection of listeners who will be
* notified when the receiver's items changes.
*
* @param listener the listener which should no longer be notified
* @see TreeListener
* @see #addTreeListener(TreeListener)
* @throws org.eclipse.swt.SWTException
* <ul>
* <li>ERROR_WIDGET_DISPOSED - 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 removeTreeListener( TreeListener listener ) {
checkWidget();
if( listener == null ) {
SWT.error( SWT.ERROR_NULL_ARGUMENT );
}
removeListener( SWT.Expand, listener );
removeListener( SWT.Collapse, listener );
}
/**
* Sets the number of items contained in the receiver.
*
* @param count the number of items
*
* @exception org.eclipse.swt.SWTException
* <ul>
* <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
* <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
* </ul>
*/
public void setItemCount( int count ) {
checkWidget();
int itemCount = Math.max( 0, count );
while( itemCount < items.size() ) {
int flatIndex = items.size() - 1;
items.get( flatIndex ).dispose( flatIndex );
}
while( itemCount > items.size() ) {
new GridItem( this, null, SWT.NONE, -1 );
}
redraw();
}
/**
* Returns the number of items contained in the receiver.
*
* @return the number of items
* @throws org.eclipse.swt.SWTException
* <ul>
* <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
* <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that
* created the receiver</li>
* </ul>
*/
public int getItemCount() {
checkWidget();
return items.size();
}
/**
* Returns a (possibly empty) array of {@code GridItem}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
* @throws org.eclipse.swt.SWTException
* <ul>
* <li>ERROR_WIDGET_DISPOSED - 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 GridItem[] getItems() {
checkWidget();
return items.toArray( new GridItem[ items.size() ] );
}
/**
* 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
* @throws 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>
* @throws org.eclipse.swt.SWTException
* <ul>
* <li>ERROR_WIDGET_DISPOSED - 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 GridItem getItem( int index ) {
checkWidget();
if( index < 0 || index >= items.size() ) {
SWT.error( SWT.ERROR_INVALID_RANGE );
}
return items.get( 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
* @throws IllegalArgumentException
* <ul>
* <li>ERROR_NULL_ARGUMENT - if the point is null</li>
* </ul>
* @throws org.eclipse.swt.SWTException
* <ul>
* <li>ERROR_WIDGET_DISPOSED - 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 GridItem getItem( Point point ) {
checkWidget();
if( point == null ) {
SWT.error( SWT.ERROR_NULL_ARGUMENT );
}
GridItem result = null;
if( point.x >= 0 && point.x <= getClientArea().width ) {
Point p = new Point( point.x, point.y );
int y = 0;
if( columnHeadersVisible ) {
y += getHeaderHeight();
}
if( p.y > y ) {
int row = getTopIndex();
while( row < items.size() && y <= getClientArea().height && result == null ) {
GridItem currentItem = items.get( row );
if( currentItem.isVisible() ) {
int currentItemHeight = currentItem.getHeight();
if( p.y >= y && p.y < y + currentItemHeight ) {
result = currentItem;
}
y += currentItemHeight;
}
row++;
}
}
}
return result;
}
/**
* Searches the receiver's list starting at the first item (index 0) until
* an item is found that is equal to the argument, and returns the index of
* that item. If no item is found, returns -1.
*
* @param item the search item
* @return the index of the item
* @throws IllegalArgumentException
* <ul>
* <li>ERROR_NULL_ARGUMENT - if the item is null</li>
* </ul>
* @throws org.eclipse.swt.SWTException
* <ul>
* <li>ERROR_WIDGET_DISPOSED - 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( GridItem item ) {
checkWidget();
if( item == null ) {
SWT.error( SWT.ERROR_NULL_ARGUMENT );
}
return item.getParent() == this ? items.indexOf( item ) : -1;
}
/**
* Returns the number of root items contained in the receiver.
*
* @return the number of items
* @throws org.eclipse.swt.SWTException
* <ul>
* <li>ERROR_WIDGET_DISPOSED - 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 getRootItemCount() {
checkWidget();
return rootItems.size();
}
/**
* Returns a (possibly empty) array of {@code GridItem}s which are
* the root 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 root items in the receiver
* @throws org.eclipse.swt.SWTException
* <ul>
* <li>ERROR_WIDGET_DISPOSED - 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 GridItem[] getRootItems() {
checkWidget();
return rootItems.toArray( new GridItem[ rootItems.size() ] );
}
/**
* TODO: JavaDoc
* @param index
* @return the root item
*/
public GridItem getRootItem( int index ) {
checkWidget();
if( index < 0 || index >= rootItems.size() ) {
SWT.error( SWT.ERROR_INVALID_RANGE );
}
return rootItems.get( index );
}
/**
* Returns the next visible item in the table.
*
* @param item item
* @return next visible item or null
* @throws org.eclipse.swt.SWTException
* <ul>
* <li>ERROR_WIDGET_DISPOSED - 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 GridItem getNextVisibleItem( GridItem item ) {
checkWidget();
GridItem result = null;
int index = items.indexOf( item );
if( index != items.size() - 1 ) {
result = items.get( index + 1 );
while( result != null && !result.isVisible() ) {
index++;
if( index != items.size() - 1 ) {
result = items.get( index + 1 );
} else {
result = null;
}
}
}
return result;
}
/**
* Returns the previous visible item in the table. Passing null for the item
* will return the last visible item in the table.
*
* @param item item or null
* @return previous visible item or if item==null last visible item
* @throws org.eclipse.swt.SWTException
* <ul>
* <li>ERROR_WIDGET_DISPOSED - 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 GridItem getPreviousVisibleItem( GridItem item ) {
checkWidget();
GridItem result = null;
int index = 0;
if( item == null ) {
index = items.size();
} else {
index = items.indexOf( item );
}
if( index > 0 ) {
result = items.get( index - 1 );
while( result != null && !result.isVisible() ) {
index--;
if( index > 0 ) {
result = items.get( index - 1 );
} else {
result = null;
}
}
}
return result;
}
/**
* Returns the number of columns contained in the receiver. If no
* {@code GridColumn}s were created by the programmer, this value is
* zero, despite the fact that visually, one column of items may be visible.
* This occurs when the programmer uses the table like a list, adding items
* but never creating a column.
*
* @return the number of columns
* @throws org.eclipse.swt.SWTException
* <ul>
* <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
* <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that
* created the receiver</li>
* </ul>
*/
public int getColumnCount() {
checkWidget();
return columns.size();
}
/**
* Returns an array of {@code GridColumn}s which are the columns in the
* receiver. If no {@code GridColumn}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
* @throws org.eclipse.swt.SWTException
* <ul>
* <li>ERROR_WIDGET_DISPOSED - 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 GridColumn[] getColumns() {
checkWidget();
return columns.toArray( new GridColumn[ columns.size() ] );
}
/**
* 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 GridColumn}s were created by the programmer, this method will
* throw {@code ERROR_INVALID_RANGE} 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
* @throws 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>
* @throws org.eclipse.swt.SWTException
* <ul>
* <li>ERROR_WIDGET_DISPOSED - 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 GridColumn getColumn( int index ) {
checkWidget();
if( index < 0 || index > getColumnCount() - 1 ) {
SWT.error( SWT.ERROR_INVALID_RANGE );
}
return columns.get( index );
}
/**
* Searches the receiver's list starting at the first column (index 0) until
* a column is found that is equal to the argument, and returns the index of
* that column. If no column is found, returns -1.
*
* @param column the search column
* @return the index of the column
* @throws IllegalArgumentException
* <ul>
* <li>ERROR_NULL_ARGUMENT - if the column is null</li>
* </ul>
* @throws org.eclipse.swt.SWTException
* <ul>
* <li>ERROR_WIDGET_DISPOSED - 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( GridColumn column ) {
checkWidget();
if( column == null ) {
SWT.error( SWT.ERROR_NULL_ARGUMENT );
}
return column.getParent() == this ? columns.indexOf( column ): -1;
}
/**
* Sets the order that the items in the receiver should be displayed in to
* the given argument which is described in terms of the zero-relative
* ordering of when the items were added.
*
* @param order the new order to display the items
* @throws org.eclipse.swt.SWTException
* <ul>
* <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
* <li>ERROR_THREAD_INVALID_ACCESS -if not called from the thread that
* created the receiver</li>
* </ul>
* @throws IllegalArgumentException
* <ul>
* <li>ERROR_NULL_ARGUMENT - if the item order is null</li>
* <li>ERROR_INVALID_ARGUMENT - if the order is not the same length as the
* number of items, or if an item is listed twice, or if the order splits a
* column group</li>
* </ul>
*/
public void setColumnOrder( int[] order ) {
checkWidget();
if( order == null ) {
SWT.error( SWT.ERROR_NULL_ARGUMENT );
}
if( order.length != displayOrderedColumns.size() ) {
SWT.error( SWT.ERROR_INVALID_ARGUMENT );
}
boolean[] seen = new boolean[ displayOrderedColumns.size() ];
for( int i = 0; i < order.length; i++ ) {
if( order[ i ] < 0 || order[ i ] >= displayOrderedColumns.size() ) {
SWT.error( SWT.ERROR_INVALID_ARGUMENT );
}
if( seen[ order[ i ] ] ) {
SWT.error( SWT.ERROR_INVALID_ARGUMENT );
}
seen[ order[ i ] ] = true;
}
if( columnGroups.size() != 0 ) {
GridColumnGroup currentGroup = null;
int columnsInGroup = 0;
for( int i = 0; i < order.length; i++ ) {
GridColumn column = getColumn( order[ i ] );
if( currentGroup != null ) {
if( column.getColumnGroup() != currentGroup && columnsInGroup > 0 ) {
SWT.error( SWT.ERROR_INVALID_ARGUMENT );
} else {
columnsInGroup--;
if( columnsInGroup <= 0 ) {
currentGroup = null;
}
}
} else if( column.getColumnGroup() != null ) {
currentGroup = column.getColumnGroup();
columnsInGroup = currentGroup.getColumns().length - 1;
}
}
}
GridColumn[] columns = getColumns();
int[] oldOrder = getColumnOrder();
displayOrderedColumns.clear();
for( int i = 0; i < order.length; i++ ) {
displayOrderedColumns.add( columns[ order[ i ] ] );
}
for( int i = 0; i < order.length; i++ ) {
if( oldOrder[ i ] != order[ i ] ) {
columns[ order[ i ] ].fireMoved();
}
}
updatePrimaryCheckColumn();
}
/**
* Returns an array of zero-relative integers that map the creation order of
* the receiver's items to the order in which they are currently being
* displayed.
* <p>
* Specifically, the indices of the returned array represent the current
* visual order of the items, and the contents of the array represent the
* creation order of the items.
* </p>
* <p>
* Note: This is not the actual structure used by the receiver to maintain
* its list of items, so modifying the array will not affect the receiver.
* </p>
*
* @return the current visual order of the receiver's items
* @throws org.eclipse.swt.SWTException
* <ul>
* <li>ERROR_WIDGET_DISPOSED - 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[] getColumnOrder() {
checkWidget();
int[] result = new int[ columns.size() ];
for( int i = 0; i < result.length; i++ ) {
GridColumn column = displayOrderedColumns.get( i );
result[ i ] = columns.indexOf( column );
}
return result;
}
/**
* Returns the next visible column in the table.
*
* @param column column
* @return next visible column or null
* @throws org.eclipse.swt.SWTException
* <ul>
* <li>ERROR_WIDGET_DISPOSED - 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 GridColumn getNextVisibleColumn( GridColumn column ) {
checkWidget();
GridColumn result = null;
int index = displayOrderedColumns.indexOf( column );
if( index != displayOrderedColumns.size() - 1 ) {
result = displayOrderedColumns.get( index + 1 );
while( result != null && !result.isVisible() ) {
index++;
if( index != displayOrderedColumns.size() - 1 ) {
result = displayOrderedColumns.get( index + 1 );
} else {
result = null;
}
}
}
return result;
}
/**
* Returns the previous visible column in the table.
*
* @param column column
* @return previous visible column or null
* @throws org.eclipse.swt.SWTException
* <ul>
* <li>ERROR_WIDGET_DISPOSED - 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 GridColumn getPreviousVisibleColumn( GridColumn column ) {
checkWidget();
GridColumn result = null;
int index = 0;
if( column == null ) {
index = displayOrderedColumns.size();
} else {
index = displayOrderedColumns.indexOf( column );
}
if( index > 0 ) {
result = displayOrderedColumns.get( index - 1 );
while( result != null && !result.isVisible() ) {
index--;
if( index > 0 ) {
result = displayOrderedColumns.get( index - 1 );
} else {
result = null;
}
}
}
return result;
}
/**
* Returns the number of column groups contained in the receiver.
*
* @return the number of column groups
* @throws org.eclipse.swt.SWTException
* <ul>
* <li>ERROR_WIDGET_DISPOSED - 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 getColumnGroupCount() {
checkWidget();
return columnGroups.size();
}
/**
* Returns an array of {@code GridColumnGroup}s which are the column groups 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 column groups in the receiver
* @throws org.eclipse.swt.SWTException
* <ul>
* <li>ERROR_WIDGET_DISPOSED - 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 GridColumnGroup[] getColumnGroups() {
checkWidget();
return columnGroups.toArray( new GridColumnGroup[ columnGroups.size() ] );
}
/**
* Returns the column group 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 column group to return
* @return the column group at the given index
* @throws 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>
* @throws org.eclipse.swt.SWTException
* <ul>
* <li>ERROR_WIDGET_DISPOSED - 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 GridColumnGroup getColumnGroup( int index ) {
checkWidget();
if( index < 0 || index >= columnGroups.size() ) {
SWT.error( SWT.ERROR_INVALID_RANGE );
}
return columnGroups.get( index );
}
/**
* Clears the item at the given zero-relative index in the receiver.
* The text, icon and other attributes of the item are set to the default
* value. If the table was created with the <code>SWT.VIRTUAL</code> style,
* these attributes are requested again as needed.
*
* @param index the index of the item to clear
* @param allChildren <code>true</code> if all child items of the indexed item should be
* cleared recursively, and <code>false</code> otherwise
*
* @exception IllegalArgumentException <ul>
* <li>ERROR_INVALID_RANGE - if the index is not between 0 and the number of elements in the list minus 1 (inclusive)</li>
* </ul>
* @exception org.eclipse.swt.SWTException <ul>
* <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
* <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
* </ul>
*
* @see SWT#VIRTUAL
* @see SWT#SetData
*/
public void clear( int index, boolean allChildren ) {
checkWidget();
if( index < 0 || index >= items.size() ) {
SWT.error( SWT.ERROR_INVALID_RANGE );
}
items.get( index ).clear( allChildren );
redraw();
}
/**
* Clears the items in the receiver which are between the given
* zero-relative start and end indices (inclusive). The text, icon
* and other attributes of the items are set to their default values.
* If the table was created with the <code>SWT.VIRTUAL</code> style,
* these attributes are requested again as needed.
*
* @param start the start index of the item to clear
* @param end the end index of the item to clear
* @param allChildren <code>true</code> if all child items of the range of items should be
* cleared recursively, and <code>false</code> otherwise
*
* @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 org.eclipse.swt.SWTException <ul>
* <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
* <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
* </ul>
*
* @see SWT#VIRTUAL
* @see SWT#SetData
*/
public void clear( int start, int end, boolean allChildren ) {
checkWidget();
if( start <= end ) {
if( !( 0 <= start && start <= end && end < items.size() ) ) {
SWT.error( SWT.ERROR_INVALID_RANGE );
}
for( int i = start; i <= end; i++ ) {
items.get( i ).clear( allChildren );
}
redraw();
}
}
/**
* Clears the items at the given zero-relative indices in the receiver.
* The text, icon and other attributes of the items are set to their default
* values. If the table was created with the <code>SWT.VIRTUAL</code> style,
* these attributes are requested again as needed.
*
* @param indices the array of indices of the items
* @param allChildren <code>true</code> if all child items of the indexed items should be
* cleared recursively, and <code>false</code> otherwise
*
* @exception IllegalArgumentException <ul>
* <li>ERROR_INVALID_RANGE - if the index is not between 0 and the number of elements in the list minus 1 (inclusive)</li>
* <li>ERROR_NULL_ARGUMENT - if the indices array is null</li>
* </ul>
* @exception org.eclipse.swt.SWTException <ul>
* <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
* <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
* </ul>
*
* @see SWT#VIRTUAL
* @see SWT#SetData
*/
public void clear( int[] indices, boolean allChildren ) {
checkWidget();
if( indices == null ) {
SWT.error( SWT.ERROR_NULL_ARGUMENT );
}
if( indices.length > 0 ) {
for( int i = 0; i < indices.length; i++ ) {
if( !isValidItemIndex( indices[ i ] ) ) {
SWT.error( SWT.ERROR_INVALID_RANGE );
}
}
for( int i = 0; i < indices.length; i++ ) {
items.get( indices[ i ] ).clear( allChildren );
}
redraw();
}
}
/**
* Clears all the items in the receiver. The text, icon and other
* attributes of the items are set to their default values. If the
* table was created with the <code>SWT.VIRTUAL</code> style, these
* attributes are requested again as needed.
*
* @param allChildren <code>true</code> if all child items of each item should be
* cleared recursively, and <code>false</code> otherwise
*
* @exception org.eclipse.swt.SWTException <ul>
* <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
* <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
* </ul>
*
* @see SWT#VIRTUAL
* @see SWT#SetData
*/
public void clearAll( boolean allChildren ) {
checkWidget();
int itemsCount = items.size();
if( itemsCount > 0 ) {
// [if] Note: The parameter allChildren has no effect as all items (not only rootItems)
// are cleared
clear( 0, itemsCount - 1, allChildren );
itemImageSize = null;
setCellToolTipsEnabled( false );
layoutCache.invalidateItemHeight();
}
}
/**
* Enables selection highlighting if the argument is <code>true</code>.
*
* @param selectionEnabled the selection enabled state
*
* @throws org.eclipse.swt.SWTException
* <ul>
* <li>ERROR_WIDGET_DISPOSED - 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 setSelectionEnabled( boolean selectionEnabled ) {
checkWidget();
if( !selectionEnabled ) {
selectedItems.clear();
}
this.selectionEnabled = selectionEnabled;
}
/**
* Returns <code>true</code> if selection is enabled, false otherwise.
*
* @return the selection enabled state
*
* @throws org.eclipse.swt.SWTException
* <ul>
* <li>ERROR_WIDGET_DISPOSED - 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 getSelectionEnabled() {
checkWidget();
return selectionEnabled;
}
/**
* 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.
* <p>
* If cell selection is enabled, selects all cells at the given index.
*
* @param index the index of the item to select
* @throws org.eclipse.swt.SWTException
* <ul>
* <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
* <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that
* created the receiver</li>
* </ul>
*/
public void select( int index ) {
checkWidget();
if( selectionEnabled && isValidItemIndex( index ) ) {
if( !cellSelectionEnabled && selectionType == SWT.SINGLE ) {
selectedItems.clear();
}
internalSelect( index );
}
}
/**
* Selects the items in the range specified by the given zero-relative
* indices in the receiver. The range of indices is inclusive. The current
* selection is not cleared before the new items are selected.
* <p>
* If an item in the given range is not selected, it is selected. If an item
* in the given range was already selected, it remains selected. Indices
* that are out of range are ignored and no items will be selected if start
* is greater than end. If the receiver is single-select and there is more
* than one item in the given range, then all indices are ignored.
* <p>
* If cell selection is enabled, all cells within the given range are selected.
*
* @param start the start of the range
* @param end the end of the range
* @throws org.eclipse.swt.SWTException
* <ul>
* <li>ERROR_WIDGET_DISPOSED - 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 Grid#setSelection(int,int)
*/
public void select( int start, int end ) {
checkWidget();
if( selectionEnabled && !( selectionType == SWT.SINGLE && start != end ) ) {
if( !cellSelectionEnabled && selectionType == SWT.SINGLE ) {
selectedItems.clear();
}
for( int index = Math.max( 0, start ); index <= Math.min( items.size() - 1, end ); index++ ) {
internalSelect( index );
}
}
}
/**
* Selects the items at the given zero-relative indices in the receiver. The
* current selection is not cleared before the new items are selected.
* <p>
* If the item at a given index is not selected, it is selected. If the item
* at a given index was already selected, it remains selected. Indices that
* are out of range and duplicate indices are ignored. If the receiver is
* single-select and multiple indices are specified, then all indices are
* ignored.
* <p>
* If cell selection is enabled, all cells within the given indices are
* selected.
*
* @param indices the array of indices for the items to select
* @throws IllegalArgumentException
* <ul>
* <li>ERROR_NULL_ARGUMENT - if the array of indices is null</li>
* </ul>
* @throws org.eclipse.swt.SWTException
* <ul>
* <li>ERROR_WIDGET_DISPOSED - 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 Grid#setSelection(int[])
*/
public void select( int[] indices ) {
checkWidget();
if( indices == null ) {
SWT.error( SWT.ERROR_NULL_ARGUMENT );
}
if( selectionEnabled && !( selectionType == SWT.SINGLE && indices.length > 1 ) ) {
if( !cellSelectionEnabled && selectionType == SWT.SINGLE ) {
selectedItems.clear();
}
for( int i = 0; i < indices.length; i++ ) {
internalSelect( indices[ i ] );
}
}
}
/**
* Selects all of the items in the receiver.
* <p>
* If the receiver is single-select, do nothing. If cell selection is enabled,
* all cells are selected.
*
* @throws org.eclipse.swt.SWTException
* <ul>
* <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
* <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that
* created the receiver</li>
* </ul>
*/
public void selectAll() {
checkWidget();
if( selectionEnabled && selectionType != SWT.SINGLE ) {
if( cellSelectionEnabled ) {
// TODO: [if] Implement cell selection
// selectAllCells();
} else {
selectedItems.clear();
selectedItems.addAll( items );
}
}
}
/**
* 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.
* <p>
* If cell selection is enabled, all cells in the specified item are deselected.
*
* @param index the index of the item to deselect
* @throws org.eclipse.swt.SWTException
* <ul>
* <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
* <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that
* created the receiver</li>
* </ul>
*/
public void deselect( int index ) {
checkWidget();
if( isValidItemIndex( index ) ) {
internalDeselect( index );
}
}
/**
* Deselects the items at the given zero-relative indices in the receiver.
* If the item at the given zero-relative index in the receiver is selected,
* it is deselected. If the item at the index was not selected, it remains
* deselected. The range of the indices is inclusive. Indices that are out
* of range are ignored.
* <p>
* If cell selection is enabled, all cells in the given range are deselected.
*
* @param start the start index of the items to deselect
* @param end the end index of the items to deselect
* @throws org.eclipse.swt.SWTException
* <ul>
* <li>ERROR_WIDGET_DISPOSED - 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();
for( int index = Math.max( 0, start ); index <= Math.min( items.size() - 1, end ); index++ ) {
internalDeselect( index );
}
}
/**
* Deselects the items at the given zero-relative indices in the receiver.
* If the item at the given zero-relative index in the receiver is selected,
* it is deselected. If the item at the index was not selected, it remains
* deselected. Indices that are out of range and duplicate indices are
* ignored.
* <p>
* If cell selection is enabled, all cells in the given items are deselected.
*
* @param indices the array of indices for the items to deselect
* @throws IllegalArgumentException
* <ul>
* <li>ERROR_NULL_ARGUMENT - if the set of indices is null</li>
* </ul>
* @throws org.eclipse.swt.SWTException
* <ul>
* <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
* <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that
* created the receiver</li>
* </ul>
*/
public void deselect( int[] indices ) {
checkWidget();
if( indices == null ) {
SWT.error( SWT.ERROR_NULL_ARGUMENT );
}
for( int i = 0; i < indices.length; i++ ) {
internalDeselect( indices[ i ] );
}
}
/**
* Deselects all selected items in the receiver. If cell selection is enabled,
* all cells are deselected.
*
* @throws org.eclipse.swt.SWTException
* <ul>
* <li>ERROR_WIDGET_DISPOSED - 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();
internalDeselectAll();
}
/**
* Selects the item at the given zero-relative index in the receiver. The
* current selection is first cleared, then the new item is selected.
* <p>
* If cell selection is enabled, all cells within the item at the given index
* are selected.
*
* @param index the index of the item to select
* @throws org.eclipse.swt.SWTException
* <ul>
* <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
* <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that
* created the receiver</li>
* </ul>
*/
public void setSelection( int index ) {
checkWidget();
if( selectionEnabled && isValidItemIndex( index ) ) {
internalDeselectAll();
internalSelect( index );
}
}
/**
* Selects the items in the range specified by the given zero-relative
* indices in the receiver. The range of indices is inclusive. The current
* selection is cleared before the new items are selected.
* <p>
* Indices that are out of range are ignored and no items will be selected
* if start is greater than end. If the receiver is single-select and there
* is more than one item in the given range, then all indices are ignored.
* <p>
* If cell selection is enabled, all cells within the given range are selected.
*
* @param start the start index of the items to select
* @param end the end index of the items to select
* @throws org.eclipse.swt.SWTException
* <ul>
* <li>ERROR_WIDGET_DISPOSED - 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 Grid#deselectAll()
* @see Grid#select(int,int)
*/
public void setSelection( int start, int end ) {
checkWidget();
if( selectionEnabled && !( selectionType == SWT.SINGLE && start != end ) ) {
internalDeselectAll();
for( int index = Math.max( 0, start ); index <= Math.min( items.size() - 1, end ); index++ ) {
internalSelect( index );
}
}
}
/**
* Selects the items at the given zero-relative indices in the receiver. The
* current selection is cleared before the new items are selected.
* <p>
* Indices that are out of range and duplicate indices are ignored. If the
* receiver is single-select and multiple indices are specified, then all
* indices are ignored.
* <p>
* If cell selection is enabled, all cells within the given indices are selected.
*
* @param indices the indices of the items to select
* @throws IllegalArgumentException
* <ul>
* <li>ERROR_NULL_ARGUMENT - if the array of indices is null</li>
* </ul>
* @throws org.eclipse.swt.SWTException
* <ul>
* <li>ERROR_WIDGET_DISPOSED - 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 Grid#deselectAll()
* @see Grid#select(int[])
*/
public void setSelection( int[] indices ) {
checkWidget();
if( indices == null ) {
SWT.error( SWT.ERROR_NULL_ARGUMENT );
}
if( selectionEnabled && !( selectionType == SWT.SINGLE && indices.length > 1 ) ) {
internalDeselectAll();
for( int i = 0; i < indices.length; i++ ) {
internalSelect( indices[ i ] );
}
}
}
/**
* 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. If cell selection is enabled, all cells within the given items
* are selected.
*
* @param items the array of items
* @throws 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>
* @throws org.eclipse.swt.SWTException
* <ul>
* <li>ERROR_WIDGET_DISPOSED - 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 Grid#deselectAll()
* @see Grid#select(int[])
* @see Grid#setSelection(int[])
*/
public void setSelection( GridItem[] items ) {
checkWidget();
if( items == null ) {
SWT.error( SWT.ERROR_NULL_ARGUMENT );
}
if( selectionEnabled && !( selectionType == SWT.SINGLE && items.length > 1 ) ) {
internalDeselectAll();
for( GridItem item : items ) {
if( item != null ) {
if( item.isDisposed() ) {
SWT.error( SWT.ERROR_INVALID_ARGUMENT );
}
internalSelect( this.items.indexOf( item ) );
}
}
}
}
/**
* Returns a array of {@code GridItem}s that are currently selected in the
* receiver. The order of the items is unspecified. An empty array indicates
* that no items are selected.
* <p>
* Note: This is not the actual structure used by the receiver to maintain
* its selection, so modifying the array will not affect the receiver.
* <p>
* If cell selection is enabled, any items which contain at least one selected
* cell are returned.
*
* @return an array representing the selection
* @throws org.eclipse.swt.SWTException
* <ul>
* <li>ERROR_WIDGET_DISPOSED - 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 GridItem[] getSelection() {
checkWidget();
GridItem[] result = new GridItem[ 0 ];
if( cellSelectionEnabled ) {
// TODO: [if] Implement cell selection
// List<GridItem> items = new ArrayList<GridItem>();
// int itemCount = getItemCount();
// for( Iterator iterator = selectedCells.iterator(); iterator.hasNext(); ) {
// Point cell = ( Point )iterator.next();
// if( cell.y >= 0 && cell.y < itemCount ) {
// GridItem item = getItem( cell.y );
// if( !items.contains( item ) ) {
// items.add( item );
// }
// }
// }
// result = items.toArray( new GridItem[ 0 ] );
} else {
result = selectedItems.toArray( new GridItem[ selectedItems.size() ] );
}
return result;
}
/**
* Returns the number of selected items contained in the receiver. If cell selection
* is enabled, the number of items with at least one selected cell are returned.
*
* @return the number of selected items
* @throws org.eclipse.swt.SWTException
* <ul>
* <li>ERROR_WIDGET_DISPOSED - 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();
int result = 0;
if( cellSelectionEnabled ) {
// TODO: [if] Implement cell selection
// List<GridItem> items = new ArrayList<GridItem>();
// for( Iterator iterator = selectedCells.iterator(); iterator.hasNext(); ) {
// Point cell = ( Point )iterator.next();
// GridItem item = getItem( cell.y );
// if( !items.contains( item ) ) {
// items.add( item );
// }
// }
// result = items.size();
} else {
result = selectedItems.size();
}
return result;
}
/**
* Returns the zero-relative index of the item which is currently selected
* in the receiver, or -1 if no item is selected. If cell selection is enabled,
* returns the index of first item that contains at least one selected cell.
*
* @return the index of the selected item
* @throws org.eclipse.swt.SWTException
* <ul>
* <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
* <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that
* created the receiver</li>
* </ul>
*/
public int getSelectionIndex() {
checkWidget();
int result = -1;
if( cellSelectionEnabled ) {
if( selectedCells.size() != 0 ) {
result = selectedCells.get( 0 ).y;
}
} else {
if( selectedItems.size() != 0 ) {
result = items.indexOf( selectedItems.get( 0 ) );
}
}
return result;
}
/**
* Returns the zero-relative indices of the items which are currently
* selected in the receiver. The order of the indices is unspecified. The
* array is empty if no items are selected.
* <p>
* Note: This is not the actual structure used by the receiver to maintain
* its selection, so modifying the array will not affect the receiver.
* <p>
* If cell selection is enabled, returns the indices of any items which
* contain at least one selected cell.
*
* @return the array of indices of the selected items
* @throws org.eclipse.swt.SWTException
* <ul>
* <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
* <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that
* created the receiver</li>
* </ul>
*/
public int[] getSelectionIndices() {
checkWidget();
int[] result = new int[ 0 ];
if( cellSelectionEnabled ) {
List<GridItem> selectedRows = new ArrayList<GridItem>();
for( Point cell : selectedCells ) {
GridItem item = getItem( cell.y );
if( !selectedRows.contains( item ) ) {
selectedRows.add( item );
}
}
result = new int[ selectedRows.size() ];
for( int i = 0; i < result.length; i++ ) {
GridItem item = selectedRows.get( i );
result[ i ] = items.indexOf( item );
}
} else {
result = new int[ selectedItems.size() ];
for( int i = 0; i < result.length; i++ ) {
GridItem item = selectedItems.get( i );
result[ i ] = items.indexOf( item );
}
}
return result;
}
/**
* Returns {@code true} if the item is selected, and {@code false}
* otherwise. Indices out of range are ignored. If cell selection is
* enabled, returns true if the item at the given index contains at
* least one selected cell.
*
* @param index the index of the item
* @return the visibility state of the item at the index
* @throws org.eclipse.swt.SWTException
* <ul>
* <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
* <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that
* created the receiver</li>
* </ul>
*/
public boolean isSelected( int index ) {
checkWidget();
boolean result = false;
if( isValidItemIndex( index ) ) {
if( cellSelectionEnabled ) {
for( Point cell : selectedCells ) {
if( cell.y == index ) {
result = true;
}
}
} else {
result = isSelected( items.get( index ) );
}
}
return result;
}
/**
* Returns true if the given item is selected. If cell selection is enabled,
* returns true if the given item contains at least one selected cell.
*
* @param item item
* @return true if the item is selected.
* @throws IllegalArgumentException
* <ul>
* <li>ERROR_NULL_ARGUMENT - if the item is null</li>
* </ul>
* @throws org.eclipse.swt.SWTException
* <ul>
* <li>ERROR_WIDGET_DISPOSED - 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( GridItem item ) {
checkWidget();
if( item == null ) {
SWT.error( SWT.ERROR_NULL_ARGUMENT );
}
boolean result = false;
if( cellSelectionEnabled ) {
int index = items.indexOf( item );
if( index != -1 ) {
for( Point cell : selectedCells ) {
if( cell.y == index ) {
result = true;
}
}
}
} else {
result = selectedItems.contains( item );
}
return result;
}
/**
* Removes the item from the receiver at the given zero-relative index.
*
* @param index the index for the item
* @throws 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>
* @throws org.eclipse.swt.SWTException
* <ul>
* <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
* <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that
* created the receiver</li>
* </ul>
*/
public void remove( int index ) {
checkWidget();
if( index < 0 || index > items.size() - 1 ) {
SWT.error( SWT.ERROR_INVALID_RANGE );
}
items.get( index ).dispose( index );
}
/**
* 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
* @throws 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>
* @throws org.eclipse.swt.SWTException
* <ul>
* <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
* <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that
* created the receiver</li>
* </ul>
*/
public void remove( int start, int end ) {
checkWidget();
for( int i = end; i >= start; i-- ) {
if( i < 0 || i > items.size() - 1 ) {
SWT.error( SWT.ERROR_INVALID_RANGE );
}
items.get( i ).dispose( i );
}
}
/**
* Removes the items from the receiver's list at the given zero-relative
* indices.
*
* @param indices the array of indices of the items
* @throws 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>
* @throws org.eclipse.swt.SWTException
* <ul>
* <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
* <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that
* created the receiver</li>
* </ul>
*/
public void remove( int[] indices ) {
checkWidget();
if( indices == null ) {
SWT.error( SWT.ERROR_NULL_ARGUMENT );
}
GridItem[] removeThese = new GridItem[ indices.length ];
for( int i = 0; i < indices.length; i++ ) {
int index = indices[ i ];
if( isValidItemIndex( index ) ) {
removeThese[ i ] = items.get( index );
} else {
SWT.error( SWT.ERROR_INVALID_RANGE );
}
}
for( int i = 0; i < removeThese.length; i++ ) {
removeThese[ i ].dispose();
}
}
/**
* Removes all of the items from the receiver.
*
* @throws org.eclipse.swt.SWTException
* <ul>
* <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
* <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that
* created the receiver</li>
* </ul>
*/
public void removeAll() {
checkWidget();
while( items.size() > 0 ) {
int flatIndex = items.size() - 1;
items.get( flatIndex ).dispose( flatIndex );
}
}
/**
* Marks the receiver's header as visible if the argument is {@code true},
* and marks it invisible otherwise.
*
* @param show the new visibility state
* @throws org.eclipse.swt.SWTException
* <ul>
* <li>ERROR_WIDGET_DISPOSED - 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 show ) {
checkWidget();
if( columnHeadersVisible != show ) {
columnHeadersVisible = show;
layoutCache.invalidateHeaderHeight();
scheduleRedraw();
}
}
/**
* Returns {@code true} if the receiver's header is visible, and
* {@code false} otherwise.
*
* @return the receiver's header's visibility state
* @throws org.eclipse.swt.SWTException
* <ul>
* <li>ERROR_WIDGET_DISPOSED - 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 columnHeadersVisible;
}
/**
* Returns the height of the column headers. If this table has column
* groups, the returned value includes the height of group headers.
*
* @return height of the column header row
* @throws org.eclipse.swt.SWTException
* <ul>
* <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
* <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that
* created the receiver</li>
* </ul>
*/
public int getHeaderHeight() {
checkWidget();
if( !layoutCache.hasHeaderHeight() ) {
layoutCache.headerHeight = computeHeaderHeight();
}
return layoutCache.headerHeight;
}
/**
* Marks the receiver's footer as visible if the argument is {@code true},
* and marks it invisible otherwise.
*
* @param show the new visibility state
* @throws org.eclipse.swt.SWTException
* <ul>
* <li>ERROR_WIDGET_DISPOSED - 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 setFooterVisible( boolean show ) {
checkWidget();
if( columnFootersVisible != show ) {
columnFootersVisible = show;
layoutCache.invalidateFooterHeight();
scheduleRedraw();
}
}
/**
* Returns {@code true} if the receiver's footer is visible, and {@code false} otherwise
* @return the receiver's footer's visibility state
* @throws org.eclipse.swt.SWTException
* <ul>
* <li>ERROR_WIDGET_DISPOSED - 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 getFooterVisible() {
checkWidget();
return columnFootersVisible;
}
/**
* Returns the height of the column footers.
*
* @return height of the column footer row
* @throws org.eclipse.swt.SWTException
* <ul>
* <li>ERROR_WIDGET_DISPOSED - 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 getFooterHeight() {
checkWidget();
if( !layoutCache.hasFooterHeight() ) {
layoutCache.footerHeight = computeFooterHeight();
}
return layoutCache.footerHeight;
}
/**
* Returns the height of the column group headers.
*
* @return height of column group headers
* @throws org.eclipse.swt.SWTException
* <ul>
* <li>ERROR_WIDGET_DISPOSED - 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 getGroupHeaderHeight() {
checkWidget();
if( !layoutCache.hasHeaderHeight() ) {
layoutCache.headerHeight = computeHeaderHeight();
}
return groupHeaderHeight;
}
/**
* Sets the line visibility.
*
* @param linesVisible The linesVisible to set.
* @throws org.eclipse.swt.SWTException
* <ul>
* <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
* <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that
* created the receiver</li>
* </ul>
*/
public void setLinesVisible( boolean linesVisible ) {
checkWidget();
this.linesVisible = linesVisible;
}
/**
* Returns true if the lines are visible.
*
* @return Returns the linesVisible.
* @throws org.eclipse.swt.SWTException
* <ul>
* <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
* <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that
* created the receiver</li>
* </ul>
*/
public boolean getLinesVisible() {
checkWidget();
return linesVisible;
}
/**
* Sets the focused item to the given item.
*
* @param item item to focus.
* @throws IllegalArgumentException
* <ul>
* <li>ERROR_INVALID_ARGUMENT - if item is disposed</li>
* </ul>
* @throws org.eclipse.swt.SWTException
* <ul>
* <li>ERROR_WIDGET_DISPOSED - 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 setFocusItem( GridItem item ) {
checkWidget();
if( item == null || item.isDisposed() || item.getParent() != this || !item.isVisible() ) {
SWT.error( SWT.ERROR_INVALID_ARGUMENT );
}
focusItem = item;
}
/**
* Returns the current item in focus.
*
* @return item in focus or {@code null}.
* @throws org.eclipse.swt.SWTException
* <ul>
* <li>ERROR_WIDGET_DISPOSED - 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 GridItem getFocusItem() {
checkWidget();
return focusItem;
}
/**
* Sets the default height for this <code>Grid</code>'s items. When
* this method is called, all existing items are resized
* to the specified height and items created afterwards will be
* initially sized to this height.
* <p>
* As long as no default height was set by the client through this method,
* the preferred height of the first item in this <code>Grid</code> is
* used as a default for all items (and is returned by {@link #getItemHeight()}).
*
* @param height default height in pixels
* @throws IllegalArgumentException
* <ul>
* <li>ERROR_INVALID_ARGUMENT - if the height is < 1</li>
* </ul>
* @throws org.eclipse.swt.SWTException
* <ul>
* <li>ERROR_WIDGET_DISPOSED - 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 GridItem#getHeight()
*/
public void setItemHeight( int height ) {
checkWidget();
if( height < 1 ) {
SWT.error( SWT.ERROR_INVALID_ARGUMENT );
}
if( customItemHeight != height ) {
customItemHeight = height;
hasDifferingHeights = false;
scheduleRedraw();
}
}
/**
* Returns the default height of the items
* in this <code>Grid</code>. See {@link #setItemHeight(int)}
* for details.
*
* @return default height of items
* @throws org.eclipse.swt.SWTException
* <ul>
* <li>ERROR_WIDGET_DISPOSED - 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 #setItemHeight(int)
*/
public int getItemHeight() {
checkWidget();
if( customItemHeight == -1 ) {
if( !layoutCache.hasItemHeight() ) {
layoutCache.itemHeight = computeItemHeight();
}
return layoutCache.itemHeight;
}
return customItemHeight;
}
@Override
public void setFont( Font font ) {
super.setFont( font );
layoutCache.invalidateItemHeight();
scheduleRedraw();
}
/**
* 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
* @throws org.eclipse.swt.SWTException
* <ul>
* <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
* <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that
* created the receiver</li>
* </ul>
*/
public void setTopIndex( int index ) {
checkWidget();
if( isValidItemIndex( index ) ) {
updateScrollBars();
GridItem item = items.get( index );
if( item.isVisible() && vScroll.getVisible() ) {
int vScrollAmount = 0;
for( int i = 0; i < index; i++ ) {
if( items.get( i ).isVisible() ) {
vScrollAmount++;
}
}
vScroll.setSelection( vScrollAmount );
invalidateTopBottomIndex();
redraw();
}
}
}
/**
* Returns the zero-relative index of the item which is currently at the top
* of the receiver. This index can change when items are scrolled or new
* items are added or removed.
*
* @return the index of the top item
* @throws org.eclipse.swt.SWTException
* <ul>
* <li>ERROR_WIDGET_DISPOSED - 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();
if( topIndex == -1 ) {
updateScrollBars();
if( vScroll.getVisible() ) {
int firstVisibleIndex = vScroll.getSelection();
if( isTree ) {
Iterator<GridItem> iterator = items.iterator();
int row = firstVisibleIndex + 1;
while( row > 0 && iterator.hasNext() ) {
GridItem item = iterator.next();
if( item.isVisible() ) {
row--;
if( row == 0 ) {
firstVisibleIndex = items.indexOf( item );
}
}
}
}
topIndex = firstVisibleIndex;
} else {
topIndex = 0;
}
}
return topIndex;
}
/**
* Shows the item. If the item is already showing in the receiver, this
* method simply returns. Otherwise, the items are scrolled until the item
* is visible.
*
* @param item the item to be shown
* @throws org.eclipse.swt.SWTException
* <ul>
* <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
* <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that
* created the receiver</li>
* <li>ERROR_INVALID_ARGUMENT - if 'item' is not contained in the receiver</li>
* </ul>
*/
public void showItem( GridItem item ) {
checkWidget();
if( item == null ) {
SWT.error( SWT.ERROR_NULL_ARGUMENT );
}
if( item.isDisposed() ) {
SWT.error( SWT.ERROR_INVALID_ARGUMENT );
}
if( item.getParent() == this ) {
int visibleGridHeight = getVisibleGridHeight();
if( visibleGridHeight >= 1 ) {
updateScrollBars();
GridItem parent = item.getParentItem();
while( parent != null ) {
if( !parent.isExpanded() ) {
parent.setExpanded( true );
parent.fireEvent( SWT.Expand );
}
parent = parent.getParentItem();
}
if( !isShown( item ) ) {
setTopIndex( items.indexOf( item ) );
}
}
}
}
/**
* 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
* @throws org.eclipse.swt.SWTException
* <ul>
* <li>ERROR_WIDGET_DISPOSED - 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 showColumn( GridColumn column ) {
checkWidget();
if( column == null ) {
SWT.error( SWT.ERROR_NULL_ARGUMENT );
}
if( column.isDisposed() ) {
SWT.error( SWT.ERROR_INVALID_ARGUMENT );
}
if( column.getParent() == this ) {
updateScrollBars();
if( !column.isVisible() ) {
GridColumnGroup group = column.getColumnGroup();
group.setExpanded( !group.getExpanded() );
if( group.getExpanded() ) {
group.notifyListeners( SWT.Expand, new Event() );
} else {
group.notifyListeners( SWT.Collapse, new Event() );
}
}
if( hScroll.getVisible() ) {
int offset = hScroll.getSelection();
int x = getColumnHeaderXPosition( column );
if( x < 0 || x + column.getWidth() > getClientArea().width ) {
if( x >= 0 && column.getWidth() <= getClientArea().width ) {
x -= getClientArea().width - column.getWidth();
}
hScroll.setSelection( offset + x );
}
}
}
}
/**
* 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.
*
* @throws org.eclipse.swt.SWTException
* <ul>
* <li>ERROR_WIDGET_DISPOSED - 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 showSelection() {
checkWidget();
GridItem item = null;
if( cellSelectionEnabled ) {
if( selectedCells.size() != 0 ) {
Point cell = selectedCells.get( 0 );
item = getItem( cell.y );
showItem( item );
GridColumn column = getColumn( cell.x );
showColumn( column );
}
} else {
if( selectedItems.size() != 0 ) {
item = selectedItems.get( 0 );
showItem( item );
}
}
}
@Override
@SuppressWarnings("unchecked")
public <T> T getAdapter( Class<T> adapter ) {
T result;
if( adapter == IItemHolderAdapter.class ) {
if( itemHolder == null ) {
itemHolder = new CompositeItemHolder();
}
result = ( T )itemHolder;
} else if( adapter == IGridAdapter.class ) {
result = ( T )gridAdapter;
} else if( adapter == ICellToolTipAdapter.class ) {
result = ( T )gridAdapter;
} else {
result = super.getAdapter( adapter );
}
return result;
}
@Override
public void setData( String key, Object value ) {
if( !RWT.MARKUP_ENABLED.equals( key ) || !isMarkupEnabledFor( this ) ) {
super.setData( key, value );
}
}
int newItem( GridItem item, int index, boolean root ) {
int row = 0;
GridItem parentItem = item.getParentItem();
if( !isTree && parentItem != null ) {
isTree = true;
}
int flatIndex = index;
// Have to convert indexes, this method needs a flat index, the method is called with indexes
// that are relative to the level
if( root && index != -1 ) {
if( index >= rootItems.size() ) {
flatIndex = -1;
} else {
flatIndex = items.indexOf( rootItems.get( index ) );
}
} else if( !root ) {
if( index >= parentItem.getItemCount() || index == -1 ) {
GridItem rightMostDescendent = parentItem;
while( rightMostDescendent.hasChildren() ) {
int lastChildIndex = rightMostDescendent.getItemCount() - 1;
rightMostDescendent = rightMostDescendent.getItem( lastChildIndex );
}
flatIndex = items.indexOf( rightMostDescendent ) + 1;
} else {
flatIndex = items.indexOf( parentItem.getItem( index ) );
}
}
if( flatIndex == -1 ) {
items.add( item );
row = items.size() - 1;
} else {
items.add( flatIndex, item );
row = flatIndex;
}
updateVisibleItems( 1 );
scheduleRedraw();
return row;
}
void removeItem( int index ) {
GridItem item = items.remove( index );
if( !disposing ) {
selectedItems.remove (item );
// TODO: [if] Implement cell selection
// Point[] cells = getCells( item );
// for( int i = 0; i < cells.length; i++ ) {
// selectedCells.remove( cells[ i ] );
// }
if( focusItem == item ) {
focusItem = null;
}
if( item.isVisible() ) {
updateVisibleItems( -1 );
}
scheduleRedraw();
}
}
void newRootItem( GridItem item, int index ) {
if( index == -1 || index >= rootItems.size() ) {
rootItems.add( item );
item.index = rootItems.size() - 1;
} else {
rootItems.add( index, item );
item.index = index;
}
adjustItemIndices( item.index + 1 );
}
void removeRootItem( int index ) {
rootItems.remove( index );
adjustItemIndices( index );
}
private void adjustItemIndices( int start ) {
for( int i = start; i < rootItems.size(); i++ ) {
rootItems.get( i ).index = i;
}
}
int newColumn( GridColumn column, int index ) {
if( index == -1 ) {
columns.add( column );
displayOrderedColumns.add( column );
} else {
columns.add( index, column );
displayOrderedColumns.add( index, column );
}
updatePrimaryCheckColumn();
for( GridItem item : items ) {
item.columnAdded( index );
}
if( column.isCheck() ) {
layoutCache.invalidateItemHeight();
}
layoutCache.invalidateHeaderHeight();
layoutCache.invalidateFooterHeight();
scheduleRedraw();
return columns.size() - 1;
}
void removeColumn( GridColumn column ) {
int index = columns.indexOf( column );
columns.remove( index );
displayOrderedColumns.remove( column );
updatePrimaryCheckColumn();
for( GridItem item : items ) {
item.columnRemoved( index );
}
if( column.isCheck() ) {
layoutCache.invalidateItemHeight();
}
layoutCache.invalidateHeaderHeight();
layoutCache.invalidateFooterHeight();
scheduleRedraw();
}
void newColumnGroup( GridColumnGroup group ) {
columnGroups.add( group );
if( columnGroups.size() == 1 ) {
layoutCache.invalidateHeaderHeight();
}
scheduleRedraw();
}
void removeColumnGroup( GridColumnGroup group ) {
columnGroups.remove( group );
if( columnGroups.size() == 0 ) {
layoutCache.invalidateHeaderHeight();
}
scheduleRedraw();
}
boolean isDisposing() {
return disposing;
}
void updateVisibleItems( int amount ) {
currentVisibleItems += amount;
}
GridColumn[] getColumnsInOrder() {
checkWidget();
return displayOrderedColumns.toArray( new GridColumn[ columns.size() ] );
}
void imageSetOnItem( int column, GridItem item ) {
Image image = item.getImage( column );
if( image != null && itemImageSize == null ) {
Rectangle imageBounds = image.getBounds();
itemImageSize = new Point( imageBounds.width, imageBounds.height );
layoutCache.invalidateItemHeight();
scheduleRedraw();
}
}
int getMaxContentWidth( GridColumn column ) {
doRedraw();
return getMaxInnerWidth( getRootItems(), columns.indexOf( column ) );
}
int getBottomIndex() {
checkWidget();
if( bottomIndex == -1 ) {
int topIndex = getTopIndex();
int visibleGridHeight = getVisibleGridHeight();
if( items.size() == 0 ) {
bottomIndex = 0;
} else if( visibleGridHeight < 1 ) {
bottomIndex = topIndex;
} else {
RowRange range = getRowRange( topIndex, visibleGridHeight, false, false );
bottomIndex = range.endIndex;
bottomIndexShownCompletely = range.height <= visibleGridHeight;
}
}
return bottomIndex;
}
Point getOrigin( GridColumn column, GridItem item ) {
int x = column.getLeft() - hScroll.getSelection();
int y = 0;
if( item != null ) {
if( columnHeadersVisible ) {
y += getHeaderHeight();
}
int topIndex = getTopIndex();
int itemIndex = items.indexOf( item );
if( itemIndex == -1 ) {
SWT.error( SWT.ERROR_INVALID_ARGUMENT );
}
while( topIndex != itemIndex ) {
if( topIndex < itemIndex ) {
GridItem currentItem = items.get( topIndex );
if( currentItem.isVisible() ) {
y += currentItem.getHeight();
}
topIndex++;
} else if( topIndex > itemIndex ) {
topIndex--;
GridItem currentItem = items.get( topIndex );
if( currentItem.isVisible() ) {
y -= currentItem.getHeight();
}
}
}
}
return new Point( x, y );
}
boolean isShown( GridItem item ) {
checkWidget();
boolean result = false;
if( item.isVisible() ) {
int itemIndex = items.indexOf( item );
if( itemIndex == -1 ) {
SWT.error( SWT.ERROR_INVALID_ARGUMENT );
}
int firstVisibleIndex = getTopIndex();
int lastVisibleIndex = getBottomIndex();
result = ( itemIndex >= firstVisibleIndex && itemIndex < lastVisibleIndex )
|| ( itemIndex == lastVisibleIndex && bottomIndexShownCompletely );
}
return result;
}
private void doRedraw() {
if( isVirtual() ) {
for( int index = getTopIndex(); index <= getBottomIndex(); index++ ) {
GridItem item = items.get( index );
if( item.isVisible() ) {
item.ensureItemData();
item.handleVirtual();
}
}
}
updateScrollBars();
}
private GridItem[] getResolvedItems() {
if( isVirtual() ) {
List<GridItem> resolvedItems = new ArrayList<GridItem>();
for( GridItem item : items ) {
if( item.isResolved() ) {
resolvedItems.add( item );
}
}
return resolvedItems.toArray( new GridItem[ 0 ] );
}
return getItems();
}
boolean isVirtual() {
return ( getStyle() & SWT.VIRTUAL ) != 0;
}
void updateScrollBars() {
if( scrollValuesObsolete ) {
Point preferredSize = getTableSize();
Rectangle clientArea = getClientArea();
for( int doublePass = 1; doublePass <= 2; doublePass++ ) {
if( preferredSize.y > clientArea.height ) {
vScroll.setVisible( true );
} else {
vScroll.setVisible( false );
vScroll.setValues( 0, 0, 1, 1, 1, 1 );
}
if( preferredSize.x > clientArea.width ) {
hScroll.setVisible( true );
} else {
hScroll.setVisible( false );
hScroll.setValues( 0, 0, 1, 1, 1, 1 );
}
clientArea = getClientArea();
}
if( vScroll.getVisible() ) {
int max = currentVisibleItems;
int thumb = 1;
int visibleGridHeight = getVisibleGridHeight();
if( !hasDifferingHeights ) {
thumb = visibleGridHeight / getItemHeight();
} else if( visibleGridHeight >= 1 ) {
RowRange range = getRowRange( -1, visibleGridHeight, true, true );
max -= range.rows - 1;
}
int selection = Math.min( vScroll.getSelection(), max );
vScroll.setValues( selection, 0, max, thumb, 1, thumb );
}
if( hScroll.getVisible() ) {
int hiddenArea = preferredSize.x - clientArea.width;
int selection = Math.min( hScroll.getSelection(), hiddenArea );
hScroll.setValues( selection, 0, preferredSize.x, clientArea.width, 5, clientArea.width );
}
scrollValuesObsolete = false;
}
}
protected IScrollBarProxy getHorizontalScrollBarProxy() {
checkWidget();
return hScroll;
}
protected IScrollBarProxy getVerticalScrollBarProxy() {
checkWidget();
return vScroll;
}
private void initListeners() {
resizeListener = new Listener() {
public void handleEvent( Event event ) {
onResize();
}
};
addListener( SWT.Resize, resizeListener );
disposeListener = new Listener() {
public void handleEvent( Event event ) {
onDispose( event );
}
};
addListener( SWT.Dispose, disposeListener );
}
private void onResize() {
if( TextSizeUtil.isTemporaryResize() ) {
isTemporaryResize = true;
layoutCache.invalidateHeaderHeight();
layoutCache.invalidateFooterHeight();
layoutCache.invalidateItemHeight();
} else {
if( isTemporaryResize) {
isTemporaryResize = false;
repackColumns();
}
scheduleRedraw();
}
}
private void onDispose( Event event ) {
// We only want to dispose of our items and such *after* anybody else who may have been
// listening to the dispose has had a chance to do whatever.
removeListener( SWT.Resize, resizeListener );
removeListener( SWT.Dispose, disposeListener );
notifyListeners( SWT.Dispose, event );
event.type = SWT.None;
disposing = true;
for( GridItem item : items ) {
item.dispose();
}
for( GridColumn column : columns ) {
column.dispose();
}
for( GridColumnGroup group : columnGroups ) {
group.dispose();
}
}
void setCellToolTipsEnabled( boolean enabled ) {
setData( ICellToolTipProvider.ENABLE_CELL_TOOLTIP, Boolean.valueOf( enabled ) );
}
private Point getTableSize() {
int width = 0;
int height = 0;
if( columnHeadersVisible ) {
height += getHeaderHeight();
}
if( columnFootersVisible ) {
height += getFooterHeight();
}
height += getGridHeight();
for( GridColumn column : columns ) {
if( column.isVisible() ) {
width += column.getWidth();
}
}
return new Point( width, height );
}
private int getGridHeight() {
int result = 0;
if( hasDifferingHeights ) {
for( GridItem item : items ) {
if( item.isVisible() ) {
result += item.getHeight();
}
}
} else {
result = currentVisibleItems * getItemHeight();
}
return result;
}
private int getVisibleGridHeight() {
int headerHeight = columnHeadersVisible ? getHeaderHeight() : 0;
int footerHeight = columnFootersVisible ? getFooterHeight() : 0;
return getClientArea().height - headerHeight - footerHeight;
}
private static int getMaxInnerWidth( GridItem[] items, int index ) {
int maxInnerWidth = 0;
for( GridItem item : items ) {
if( item.isResolved() ) {
maxInnerWidth = Math.max( maxInnerWidth, item.getPreferredWidth( index ) );
if( item.isExpanded() ) {
int innerWidth = getMaxInnerWidth( item.getItems(), index );
maxInnerWidth = Math.max( maxInnerWidth, innerWidth );
}
}
}
return maxInnerWidth;
}
private void internalSelect( int index ) {
if( isValidItemIndex( index ) ) {
GridItem item = items.get( index );
if( cellSelectionEnabled ) {
// TODO: [if] Implement cell selection
// selectCells( getCells( item ) );
} else if( !selectedItems.contains( item ) ) {
selectedItems.add( item );
}
}
}
private void internalDeselect( int index ) {
if( isValidItemIndex( index ) ) {
GridItem item = items.get( index );
if( cellSelectionEnabled ) {
// TODO: [if] Implement cell selection
// deselectCells( getCells( item ) );
} else if( selectedItems.contains( item ) ) {
selectedItems.remove( item );
}
}
}
private void internalDeselectAll() {
if( cellSelectionEnabled ) {
// TODO: [if] Implement cell selection
// selectedCells.clear();
} else {
selectedItems.clear();
}
}
private void updatePrimaryCheckColumn() {
if( ( getStyle() & SWT.CHECK ) == SWT.CHECK ) {
boolean firstCol = true;
for( GridColumn column : displayOrderedColumns ) {
column.setTableCheck( firstCol );
firstCol = false;
}
}
}
private int computeItemHeight() {
int result = Math.max( getItemImageSize().y, TextSizeUtil.getCharHeight( getFont() ) );
if( hasCheckBoxes() ) {
result = Math.max( getCheckBoxImageOuterSize().y, result );
}
result += getCellPadding().height;
result += GRID_WIDTH;
result = Math.max( result, MIN_ITEM_HEIGHT );
return result;
}
private int computeHeaderHeight() {
int result = 0;
groupHeaderHeight = 0;
if( columnHeadersVisible ) {
int columnHeaderHeight = 0;
for( int i = 0; i < getColumnCount(); i++ ) {
GridColumn column = columns.get( i );
Font headerFont = column.getHeaderFont();
String headerText = column.getText();
Image headerImage = column.getImage();
int computedHeight = computeColumnHeight( headerFont, headerText, headerImage, 0 );
columnHeaderHeight = Math.max( columnHeaderHeight, computedHeight );
}
for( int i = 0; i < getColumnGroupCount(); i++ ) {
GridColumnGroup group = columnGroups.get( i );
Font groupFont = group.getHeaderFont();
String groupText = group.getText();
Image groupImage = group.getImage();
int chevronHeight = group.getChevronHeight();
int computedHeight = computeColumnHeight( groupFont, groupText, groupImage, chevronHeight );
groupHeaderHeight = Math.max( groupHeaderHeight, computedHeight );
}
result = columnHeaderHeight + groupHeaderHeight;
}
return result;
}
private int computeFooterHeight() {
int result = 0;
if( columnFootersVisible ) {
int columnFooterHeight = 0;
for( int i = 0; i < getColumnCount(); i++ ) {
GridColumn column = columns.get( i );
Font footerFont = column.getFooterFont();
String footerText = column.getFooterText();
Image footerImage = column.getFooterImage();
int computedHeight = computeColumnHeight( footerFont, footerText, footerImage, 0 );
columnFooterHeight= Math.max( columnFooterHeight, computedHeight );
}
result = columnFooterHeight;
}
return result;
}
private int computeColumnHeight( Font font, String text, Image image, int minHeight ) {
int result = minHeight;
int textHeight = 0;
if( text.contains( "\n" ) ) {
textHeight = TextSizeUtil.textExtent( font, text, 0 ).y;
} else {
textHeight = TextSizeUtil.getCharHeight( font );
}
result = Math.max( result, textHeight );
int imageHeight = image == null ? 0 : image.getBounds().height;
result = Math.max( result, imageHeight );
result += getHeaderPadding().height;
result += getThemeAdapter().getHeaderBorderBottomWidth( this );
return result;
}
private void repackColumns() {
for( int i = 0; i < getColumnCount(); i++ ) {
columns.get( i ).repack();
}
}
private int getColumnHeaderXPosition( GridColumn column ) {
int result = -1;
if( column.isVisible() ) {
result = column.getLeft() - hScroll.getSelection();
}
return result;
}
private int getCellLeft( int index ) {
return getColumn( index ).getLeft();
}
private int getCellWidth( int index ) {
GridColumn column = getColumn( index );
return column.isVisible() ? column.getWidth() : 0;
}
private int getCheckBoxOffset( int index ) {
int result = -1;
Rectangle padding = getCellPadding();
if( isColumnCentered( index )
&& !isTreeColumn( index )
&& !hasColumnImages( index )
&& !hasColumnTexts( index ) )
{
result = ( getCellWidth( index ) - getCheckBoxImageSize().x ) / 2;
result = Math.max( result, padding.x );
}
if( result == -1 ) {
result = getCheckBoxMargin().x;
if( !isTreeColumn( index ) ) {
result += padding.x;
}
}
return result;
}
private int getCheckBoxWidth( int index ) {
return getColumn( index ).isCheck() ? getCheckBoxImageSize().x : 0;
}
private int getImageOffset( int index ) {
int result = 0;
if( !isTreeColumn( index ) ) {
result += getCellPadding().x;
}
if( getColumn( index ).isCheck() ) {
result += getCheckBoxImageOuterSize().x;
}
return result;
}
private int getImageWidth( int index ) {
int result = 0;
if( hasColumnImages( index ) ) {
result = getItemImageSize().x;
int availableWidth = getCellWidth( index );
if( !isTreeColumn( index ) ) {
result -= getCellPadding().x;
}
availableWidth = Math.max( 0, availableWidth );
result = Math.min( result, availableWidth );
}
return result;
}
private int getTextOffset( int index ) {
int result = getImageOffset( index );
if( hasColumnImages( index ) ) {
result += getItemImageSize().x;
result += getCellSpacing();
}
return result;
}
private int getTextWidth( int index ) {
Rectangle padding = getCellPadding();
int rightPadding = padding.width - padding.x;
return Math.max( 0, getCellWidth( index ) - getTextOffset( index ) - rightPadding );
}
Point getItemImageSize() {
Point result = new Point( 0, 0 );
if( itemImageSize != null ) {
result.x = itemImageSize.x;
result.y = itemImageSize.y;
}
return result;
}
boolean hasColumnImages( int index ) {
return getColumn( index ).imageCount > 0;
}
boolean hasColumnTexts( int index ) {
return getColumn( index ).textCount > 0;
}
private boolean hasCheckBoxes() {
boolean result = ( getStyle() & SWT.CHECK ) != 0;
for( int i = 0; i < getColumnCount() && !result; i++ ) {
GridColumn column = columns.get( i );
if( column.isCheck() ) {
result = true;
}
}
return result;
}
Point getCheckBoxImageOuterSize() {
Point imageSize = getCheckBoxImageSize();
Rectangle margin = getCheckBoxMargin();
return new Point( imageSize.x + margin.width, imageSize.y + margin.height );
}
boolean isTreeColumn( int index ) {
boolean result = false;
if( isTree ) {
int columnCount = getColumnCount();
result = columnCount == 0 && index == 0 || columnCount > 0 && index == getColumnOrder()[ 0 ];
}
return result;
}
private boolean isColumnCentered( int index ) {
return getColumn( index ).getAlignment() == SWT.CENTER;
}
private Point getCheckBoxImageSize() {
if( !layoutCache.hasCheckBoxImageSize() ) {
layoutCache.checkBoxImageSize = getThemeAdapter().getCheckBoxImageSize( this );
}
return layoutCache.checkBoxImageSize;
}
private Rectangle getCheckBoxMargin() {
if( !layoutCache.hasCheckBoxMargin() ) {
layoutCache.checkBoxMargin = getThemeAdapter().getCheckBoxMargin( this );
}
return layoutCache.checkBoxMargin;
}
Rectangle getCellPadding() {
if( !layoutCache.hasCellPadding() ) {
layoutCache.cellPadding = getThemeAdapter().getCellPadding( this );
}
return layoutCache.cellPadding;
}
Rectangle getHeaderPadding() {
if( !layoutCache.hasHeaderPadding() ) {
layoutCache.headerPadding = getThemeAdapter().getHeaderPadding( this );
}
return layoutCache.headerPadding;
}
int getIndentationWidth() {
if( !layoutCache.hasIndentationWidth() ) {
layoutCache.indentationWidth = getThemeAdapter().getIndentationWidth( this );
}
return layoutCache.indentationWidth;
}
int getCellSpacing() {
if( !layoutCache.hasCellSpacing() ) {
layoutCache.cellSpacing = getThemeAdapter().getCellSpacing( this );
}
return layoutCache.cellSpacing;
}
private GridThemeAdapter getThemeAdapter() {
return ( GridThemeAdapter )getAdapter( IThemeAdapter.class );
}
private static int checkStyle( int style ) {
int mask = SWT.BORDER
| SWT.LEFT_TO_RIGHT
// | SWT.RIGHT_TO_LEFT
| SWT.H_SCROLL
| SWT.V_SCROLL
| SWT.SINGLE
| SWT.MULTI
| SWT.NO_FOCUS
| SWT.CHECK
| SWT.VIRTUAL;
int result = style & mask;
result |= SWT.DOUBLE_BUFFERED;
// TODO: [if] Remove it when cell selection is implemented
result |= SWT.FULL_SELECTION;
return result;
}
private RowRange getRowRange( int start,
int availableHeight,
boolean forceEndCompletelyInside,
boolean inverse )
{
RowRange result = new RowRange();
int startIndex = start;
if( startIndex == -1 ) {
if( inverse ) {
startIndex = items.size();
}
do {
startIndex += inverse ? -1 : 1;
} while( isValidItemIndex( startIndex ) && !items.get( startIndex ).isVisible() );
if( !isValidItemIndex( startIndex ) ) {
result = null;
}
}
if( result != null ) {
if( startIndex < 0 || startIndex >= items.size() || !items.get( startIndex ).isVisible() ) {
SWT.error( SWT.ERROR_INVALID_ARGUMENT );
}
if( availableHeight <= 0 ) {
result.startIndex = startIndex;
result.endIndex = startIndex;
result.rows = 0;
result.height = 0;
} else if( isTree || hasDifferingHeights ) {
int otherIndex = startIndex;
int consumedItems = 0;
int consumedHeight = 0;
consumedItems++;
consumedHeight += items.get( otherIndex ).getHeight();
boolean abort = false;
while( consumedHeight + 1 <= availableHeight && !abort ) {
int nextIndex = otherIndex;
GridItem nextItem;
do {
nextIndex += inverse ? -1 : 1;
if( isValidItemIndex( nextIndex ) ) {
nextItem = items.get( nextIndex );
} else {
nextItem = null;
}
} while( nextItem != null && !nextItem.isVisible() );
if( nextItem == null
|| forceEndCompletelyInside
&& !( consumedHeight + nextItem.getHeight() <= availableHeight ) )
{
abort = true;
} else {
consumedItems++;
consumedHeight += nextItem.getHeight();
otherIndex = nextIndex;
}
}
result.startIndex = !inverse ? startIndex : otherIndex;
result.endIndex = !inverse ? otherIndex : startIndex;
result.rows = consumedItems;
result.height = consumedHeight;
} else {
int availableRows = availableHeight / getItemHeight();
if( !forceEndCompletelyInside && availableRows * getItemHeight() < availableHeight ) {
availableRows++;
}
int otherIndex = startIndex + ( ( availableRows - 1 ) * ( inverse ? -1 : 1 ) );
otherIndex = Math.max( otherIndex, 0 );
otherIndex = Math.min( otherIndex, items.size() - 1 );
result.startIndex = !inverse ? startIndex : otherIndex;
result.endIndex = !inverse ? otherIndex : startIndex;
result.rows = result.endIndex - result.startIndex + 1;
result.height = getItemHeight() * result.rows;
}
}
return result;
}
private boolean isValidItemIndex( int index ) {
return index >= 0 && index < items.size();
}
int internalIndexOf( GridItem item ) {
return items.indexOf( item );
}
void scheduleRedraw() {
invalidateScrollBars();
invalidateTopBottomIndex();
redraw();
}
void invalidateTopBottomIndex() {
topIndex = -1;
bottomIndex = -1;
}
void invalidateScrollBars() {
scrollValuesObsolete = true;
}
////////////////
// Inner classes
private static class RowRange {
public int startIndex;
public int endIndex;
public int rows;
public int height;
}
private final class CompositeItemHolder implements IItemHolderAdapter<Item> {
public void add( Item item ) {
throw new UnsupportedOperationException();
}
public void insert( Item item, int index ) {
throw new UnsupportedOperationException();
}
public void remove( Item item ) {
throw new UnsupportedOperationException();
}
public Item[] getItems() {
GridItem[] items = getResolvedItems();
GridColumn[] columns = getColumns();
GridColumnGroup[] groups = getColumnGroups();
Item[] result = new Item[ columns.length + items.length + groups.length ];
System.arraycopy( groups, 0, result, 0, groups.length );
System.arraycopy( columns, 0, result, groups.length, columns.length );
System.arraycopy( items, 0, result, groups.length + columns.length, items.length );
return result;
}
}
private final class GridAdapter
implements IGridAdapter, ICellToolTipAdapter, SerializableCompatibility
{
private String toolTipText;
private ICellToolTipProvider provider;
public GridAdapter() {
provider = new CellToolTipProvider();
}
public void invalidateTopIndex() {
invalidateTopBottomIndex();
redraw();
}
public int getIndentationWidth() {
return Grid.this.getIndentationWidth();
}
public int getCellLeft( int index ) {
return Grid.this.getCellLeft( index );
}
public int getCellWidth( int index ) {
return Grid.this.getCellWidth( index );
}
public int getCheckBoxOffset( int index ) {
return Grid.this.getCheckBoxOffset( index );
}
public int getCheckBoxWidth( int index ) {
return Grid.this.getCheckBoxWidth( index );
}
public int getImageOffset( int index ) {
return Grid.this.getImageOffset( index );
}
public int getImageWidth( int index ) {
return Grid.this.getImageWidth( index );
}
public int getTextOffset( int index ) {
return Grid.this.getTextOffset( index );
}
public int getTextWidth( int index ) {
return Grid.this.getTextWidth( index );
}
public int getItemIndex( GridItem item ) {
return item.index;
}
public ICellToolTipProvider getCellToolTipProvider() {
return provider;
}
public void setCellToolTipProvider( ICellToolTipProvider provider ) {
this.provider = provider;
}
public String getCellToolTipText() {
return toolTipText;
}
public void setCellToolTipText( String toolTipText ) {
this.toolTipText = toolTipText;
}
public void doRedraw() {
Grid.this.doRedraw();
}
}
private final class CellToolTipProvider
implements ICellToolTipProvider, SerializableCompatibility
{
public void getToolTipText( Item item, int columnIndex ) {
String toolTipText = ( ( GridItem )item ).getToolTipText( columnIndex );
getAdapter( ICellToolTipAdapter.class ).setCellToolTipText( toolTipText );
}
}
static final class LayoutCache implements SerializableCompatibility {
private static final int UNKNOWN = -1;
int headerHeight = UNKNOWN;
int footerHeight = UNKNOWN;
int itemHeight = UNKNOWN;
int cellSpacing = UNKNOWN;
int indentationWidth = UNKNOWN;
Rectangle cellPadding;
Rectangle checkBoxMargin;
Point checkBoxImageSize;
Rectangle headerPadding;
public boolean hasHeaderPadding() {
return headerPadding != null;
}
public void invalidateHeaderPadding() {
headerPadding = null;
}
public boolean hasHeaderHeight() {
return headerHeight != UNKNOWN;
}
public void invalidateHeaderHeight() {
headerHeight = UNKNOWN;
}
public boolean hasFooterHeight() {
return footerHeight != UNKNOWN;
}
public void invalidateFooterHeight() {
footerHeight = UNKNOWN;
}
public boolean hasItemHeight() {
return itemHeight != UNKNOWN;
}
public void invalidateItemHeight() {
itemHeight = UNKNOWN;
}
public boolean hasCellSpacing() {
return cellSpacing != UNKNOWN;
}
public void invalidateCellSpacing() {
cellSpacing = UNKNOWN;
}
public boolean hasIndentationWidth() {
return indentationWidth != UNKNOWN;
}
public void invalidateIndentationWidth() {
indentationWidth = UNKNOWN;
}
public boolean hasCellPadding() {
return cellPadding != null;
}
public void invalidateCellPadding() {
cellPadding = null;
}
public boolean hasCheckBoxMargin() {
return checkBoxMargin != null;
}
public void invalidateCheckBoxMargin() {
checkBoxMargin = null;
}
public boolean hasCheckBoxImageSize() {
return checkBoxImageSize != null;
}
public void invalidateCheckBoxImageSize() {
checkBoxImageSize = null;
}
public void invalidateAll() {
invalidateHeaderPadding();
invalidateHeaderHeight();
invalidateFooterHeight();
invalidateItemHeight();
invalidateCellSpacing();
invalidateCellPadding();
invalidateCheckBoxMargin();
invalidateCheckBoxImageSize();
invalidateIndentationWidth();
}
}
}