package org.eclipse.nebula.widgets.grid;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
import java.util.Vector;
import org.eclipse.nebula.widgets.grid.internal.DefaultBottomLeftRenderer;
import org.eclipse.nebula.widgets.grid.internal.DefaultColumnGroupHeaderRenderer;
import org.eclipse.nebula.widgets.grid.internal.DefaultDropPointRenderer;
import org.eclipse.nebula.widgets.grid.internal.DefaultEmptyCellRenderer;
import org.eclipse.nebula.widgets.grid.internal.DefaultEmptyColumnFooterRenderer;
import org.eclipse.nebula.widgets.grid.internal.DefaultEmptyColumnHeaderRenderer;
import org.eclipse.nebula.widgets.grid.internal.DefaultEmptyRowHeaderRenderer;
import org.eclipse.nebula.widgets.grid.internal.DefaultFocusRenderer;
import org.eclipse.nebula.widgets.grid.internal.DefaultInsertMarkRenderer;
import org.eclipse.nebula.widgets.grid.internal.DefaultRowHeaderRenderer;
import org.eclipse.nebula.widgets.grid.internal.DefaultTopLeftRenderer;
import org.eclipse.nebula.widgets.grid.internal.GridToolTip;
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.swt.SWT;
import org.eclipse.swt.SWTException;
import org.eclipse.swt.accessibility.ACC;
import org.eclipse.swt.accessibility.Accessible;
import org.eclipse.swt.accessibility.AccessibleAdapter;
import org.eclipse.swt.accessibility.AccessibleControlAdapter;
import org.eclipse.swt.accessibility.AccessibleControlEvent;
import org.eclipse.swt.accessibility.AccessibleEvent;
import org.eclipse.swt.dnd.DND;
import org.eclipse.swt.dnd.DropTargetEvent;
import org.eclipse.swt.dnd.DropTargetListener;
import org.eclipse.swt.events.FocusEvent;
import org.eclipse.swt.events.FocusListener;
import org.eclipse.swt.events.MouseEvent;
import org.eclipse.swt.events.MouseListener;
import org.eclipse.swt.events.MouseMoveListener;
import org.eclipse.swt.events.MouseTrackListener;
import org.eclipse.swt.events.PaintEvent;
import org.eclipse.swt.events.PaintListener;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.events.SelectionListener;
import org.eclipse.swt.events.TraverseEvent;
import org.eclipse.swt.events.TraverseListener;
import org.eclipse.swt.events.TreeEvent;
import org.eclipse.swt.events.TreeListener;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.graphics.Font;
import org.eclipse.swt.graphics.GC;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.graphics.RGB;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.swt.widgets.Canvas;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Event;
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>
*
* @author chris.gross@us.ibm.com
*/
public class Grid extends Canvas
{
//TODO: figure out better way to allow renderers to trigger events
//TODO: scroll as necessary when performing drag select (current strategy ok)
//TODO: need to refactor the way the range select remembers older selection
//TODO: remember why i decided i needed to refactor the way the range select remembers older selection
//TODO: need to alter how column drag selection works to allow selection of spanned cells
//TODO: JAVADOC!
//TODO: column freezing
//TODO: Performance - need to cache top index
/**
* Object holding the visible range
*/
public static class GridVisibleRange {
private GridItem[] items = new GridItem[0];
private GridColumn[] columns = new GridColumn[0];
/**
* @return the current items shown
*/
public GridItem[] getItems() {
return items;
}
/**
* @return the current columns shown
*/
public GridColumn[] getColumns() {
return columns;
}
}
/**
* Accessibility default action for column headers and column group headers.
*/
private static final String ACC_COLUMN_DEFAULT_ACTION = "Click";
/**
* Accessibility default action for items.
*/
private static final String ACC_ITEM_DEFAULT_ACTION = "Double Click";
/**
* Accessibility expand action for tree items.
*/
private static final String ACC_ITEM_ACTION_EXPAND = "Expand";
/**
* Accessibility collapse action for tree items.
*/
private static final String ACC_ITEM_ACTION_COLLAPSE = "Collapse";
/**
* Accessibility name for the column group header toggle button.
*/
private static final String ACC_TOGGLE_BUTTON_NAME = "Toggle Button";
/**
* Alpha blending value used when drawing the dragged column header.
*/
private static final int COLUMN_DRAG_ALPHA = 128;
/**
* Number of pixels below the header to draw the drop point.
*/
private static final int DROP_POINT_LOWER_OFFSET = 3;
/**
* Horizontal scrolling increment, in pixels.
*/
private static final int HORZ_SCROLL_INCREMENT = 5;
/**
* The area to the left and right of the column boundary/resizer that is
* still considered the resizer area. This prevents a user from having to be
* *exactly* over the resizer.
*/
private static final int COLUMN_RESIZER_THRESHOLD = 4;
/**
* @see #COLUMN_RESIZER_THRESHOLD
*/
private static final int ROW_RESIZER_THRESHOLD = 3;
/**
* The minimum width of a column header.
*/
private static final int MIN_COLUMN_HEADER_WIDTH = 20;
/**
* The minimum height of a row header.
*/
private static final int MIN_ROW_HEADER_HEIGHT = 10;
/**
* The number used when sizing the row header (i.e. size it for '1000')
* initially.
*/
// private static final int INITIAL_ROW_HEADER_SIZING_VALUE = 1000;
/**
* The factor to multiply the current row header sizing value by when
* determining the next sizing value. Used for performance reasons.
*/
// private static final int ROW_HEADER_SIZING_MULTIPLIER = 10;
/**
* Tracks whether the scroll values are correct. If not they will be
* recomputed in onPaint. This allows us to get a free ride on top of the
* OS's paint event merging to assure that we don't perform this expensive
* operation when unnecessary.
*/
private boolean scrollValuesObsolete = false;
/**
* All items in the table, not just root items.
*/
private List items = new ArrayList();
/**
* All root items.
*/
private List rootItems = new ArrayList();
/**
* List of selected items.
*/
private List selectedItems = new ArrayList();
/**
* Reference to the item in focus.
*/
private GridItem focusItem;
private boolean cellSelectionEnabled = false;
private List selectedCells = new ArrayList();
private List selectedCellsBeforeRangeSelect = new ArrayList();
private boolean cellDragSelectionOccuring = false;
private boolean cellRowDragSelectionOccuring = false;
private boolean cellColumnDragSelectionOccuring = false;
private boolean cellDragCTRL = false;
private boolean followupCellSelectionEventOwed = false;
private boolean cellSelectedOnLastMouseDown;
private boolean cellRowSelectedOnLastMouseDown;
private boolean cellColumnSelectedOnLastMouseDown;
private GridColumn shiftSelectionAnchorColumn;
private GridColumn focusColumn;
private List selectedColumns = new ArrayList();
/**
* This is the column that the user last navigated to, but may not be the focusColumn because
* that column may be spanned in the current row. This is only used in situations where the user
* has used the keyboard to navigate up or down in the table and the focusColumn has switched to
* a new column because the intended column (was maintained in this var) was spanned. The table
* will attempt to set focus back to the intended column during subsequent up/down navigations.
*/
private GridColumn intendedFocusColumn;
/**
* List of table columns in creation/index order.
*/
private List columns = new ArrayList();
/**
* List of the table columns in the order they are displayed.
*/
private List displayOrderedColumns = new ArrayList();
private GridColumnGroup[] columnGroups = new GridColumnGroup[0];
/**
* Renderer to paint the top left area when both column and row headers are
* shown.
*/
private IRenderer topLeftRenderer = new DefaultTopLeftRenderer();
/**
* Renderer to paint the bottom left area when row headers and column footers are shown
*/
private IRenderer bottomLeftRenderer = new DefaultBottomLeftRenderer();
/**
* Renderer used to paint row headers.
*/
private IRenderer rowHeaderRenderer = new DefaultRowHeaderRenderer();
/**
* Renderer used to paint empty column headers, used when the columns don't
* fill the horz space.
*/
private IRenderer emptyColumnHeaderRenderer = new DefaultEmptyColumnHeaderRenderer();
/**
* Renderer used to paint empty column footers, used when the columns don't
* fill the horz space.
*/
private IRenderer emptyColumnFooterRenderer = new DefaultEmptyColumnFooterRenderer();
/**
* Renderer used to paint empty cells to fill horz and vert space.
*/
private GridCellRenderer emptyCellRenderer = new DefaultEmptyCellRenderer();
/**
* Renderer used to paint empty row headers when the rows don't fill the
* vertical space.
*/
private IRenderer emptyRowHeaderRenderer = new DefaultEmptyRowHeaderRenderer();
/**
* Renderers the UI affordance identifying where the dragged column will be
* dropped.
*/
private IRenderer dropPointRenderer = new DefaultDropPointRenderer();
/**
* Renderer used to paint on top of an already painted row to denote focus.
*/
private IRenderer focusRenderer = new DefaultFocusRenderer();
/**
* Are row headers visible?
*/
private boolean rowHeaderVisible = false;
/**
* Are column headers visible?
*/
private boolean columnHeadersVisible = false;
/**
* Are column footers visible?
*/
private boolean columnFootersVisible = false;
/**
* Type of selection behavior. Valid values are SWT.SINGLE and SWT.MULTI.
*/
private int selectionType = SWT.SINGLE;
/**
* True if selection highlighting is enabled.
*/
private boolean selectionEnabled = true;
/**
* Default height of items. This value is used
* for <code>GridItem</code>s with a height
* of -1.
*/
private int itemHeight = 1;
private boolean userModifiedItemHeight = false;
/**
* Width of each row header.
*/
private int rowHeaderWidth = 0;
/**
* The row header width is variable. The row header width gets larger as
* more rows are added to the table to ensure that the row header has enough
* room to display the longest string of numbers that display in the row
* header. This determination of how wide to make the row header is rather
* slow and therefore is only done at every 1000 items (or so). This
* variable remembers how many items were last computed and therefore when
* the number of items is greater than this value, we need to recalculate
* the row header width. See newItem().
*/
// private int lastRowHeaderWidthCalculationAt = 0;
/**
* Height of each column header.
*/
private int headerHeight = 0;
/**
* Height of each column footer
*/
private int footerHeight = 0;
/**
* True if mouse is hover on a column boundary and can resize the column.
*/
boolean hoveringOnColumnResizer = false;
/**
* Reference to the column being resized.
*/
private GridColumn columnBeingResized;
/**
* Are this <code>Grid</code>'s rows resizeable?
*/
private boolean rowsResizeable = false;
/**
* Is the user currently resizing a column?
*/
private boolean resizingColumn = false;
/**
* The mouse X position when the user starts the resize.
*/
private int resizingStartX = 0;
/**
* The width of the column when the user starts the resize. This, together
* with the resizingStartX determines the current width during resize.
*/
private int resizingColumnStartWidth = 0;
private boolean hoveringOnRowResizer = false;
private GridItem rowBeingResized;
private boolean resizingRow = false;
private int resizingStartY;
private int resizingRowStartHeight;
/**
* Reference to the column whose header is currently in a pushed state.
*/
private GridColumn columnBeingPushed;
/**
* Is the user currently pushing a column header?
*/
private boolean pushingColumn = false;
/**
* Is the user currently pushing a column header and hovering over that same
* header?
*/
private boolean pushingAndHovering = false;
/**
* X position of the mouse when the user first pushes a column header.
*/
private int startHeaderPushX = 0;
/**
* X position of the mouse when the user has initiated a drag. This is
* different than startHeaderPushX because the mouse is allowed some
* 'wiggle-room' until the header is put into drag mode.
*/
private int startHeaderDragX = 0;
/**
* The current X position of the mouse during a header drag.
*/
private int currentHeaderDragX = 0;
/**
* Are we currently dragging a column header?
*/
private boolean draggingColumn = false;
private GridColumn dragDropBeforeColumn = null;
private GridColumn dragDropAfterColumn = null;
/**
* True if the current dragDropPoint is a valid drop point for the dragged
* column. This is false if the column groups are involved and a column is
* being dropped into or out of its column group.
*/
private boolean dragDropPointValid = true;
/**
* Reference to the currently item that the mouse is currently hovering
* over.
*/
private GridItem hoveringItem;
/**
* Reference to the column that the mouse is currently hovering over.
* Includes the header and all cells (all rows) in this column.
*/
private GridColumn hoveringColumn;
private GridColumn hoveringColumnHeader;
private GridColumnGroup hoverColumnGroupHeader;
/**
* String-based detail of what is being hovered over in a cell. This allows
* a renderer to differentiate between hovering over different parts of the
* cell. For example, hovering over a checkbox in the cell or hovering over
* a tree node in the cell. The table does nothing with this string except
* to set it back in the renderer when its painted. The renderer sets this
* during its notify method (InternalWidget.HOVER) and the table pulls it
* back and maintains it so it can be set back when the cell is painted. The
* renderer determines what the hover detail means and how it affects
* painting.
*/
private String hoveringDetail = "";
/**
* True if the mouse is hovering of a cell's text.
*/
private boolean hoveringOverText = false;
/**
* Are the grid lines visible?
*/
private boolean linesVisible = true;
/**
* Are tree lines visible?
*/
private boolean treeLinesVisible = true;
/**
* Grid line color.
*/
private Color lineColor;
/**
* Vertical scrollbar proxy.
* <p>
* Note:
* <ul>
* <li>{@link Grid#getTopIndex()} is the only method allowed to call vScroll.getSelection()
* (except #updateScrollbars() of course)</li>
* <li>{@link Grid#setTopIndex(int)} is the only method allowed to call vScroll.setSelection(int)</li>
* </ul>
*/
private IScrollBarProxy vScroll;
/**
* Horizontal scrollbar proxy.
*/
private IScrollBarProxy hScroll;
/**
* The number of GridItems whose visible = true. Maintained for
* performance reasons (rather than iterating over all items).
*/
private int currentVisibleItems = 0;
/**
* Item selected when a multiple selection using shift+click first occurs.
* This item anchors all further shift+click selections.
*/
private GridItem shiftSelectionAnchorItem;
private boolean columnScrolling = false;
private int groupHeaderHeight;
private Color cellHeaderSelectionBackground;
/**
* Dispose listener. This listener is removed during the dispose event to allow re-firing of
* the event.
*/
private Listener disposeListener;
/**
* The inplace tooltip.
*/
private GridToolTip inplaceToolTip;
private GC sizingGC;
private Color backgroundColor;
/**
* True if the widget is being disposed. When true, events are not fired.
*/
private boolean disposing = false;
/**
* True if there is at least one tree node. This is used by accessibility and various
* places for optimization.
*/
private boolean isTree = false;
/**
* True if there is at least one <code>GridItem</code> with an individual height.
* This value is only set to true in {@link GridItem#setHeight(int,boolean)}
* and it is never reset to false.
*/
boolean hasDifferingHeights = false;
/**
* True if three is at least one cell spanning columns. This is used in various places for
* optimizatoin.
*/
private boolean hasSpanning = false;
/**
* Index of first visible item. The value must never be read directly. It is cached and
* updated when appropriate. #getTopIndex should be called for every client (even internal
* callers). A value of -1 indicates that the value is old and will be recomputed.
*
* @see #bottomIndex
*/
int topIndex = -1;
/**
* Index of last visible item. The value must never be read directly. It is cached and
* updated when appropriate. #getBottomIndex() should be called for every client (even internal
* callers). A value of -1 indicates that the value is old and will be recomputed.
* <p>
* Note that the item with this index is often only partly visible; maybe only
* a single line of pixels is visible. In extreme cases, bottomIndex may be the
* same as topIndex.
*
* @see #topIndex
*/
int bottomIndex = -1;
/**
* Index of the first visible column. A value of -1 indicates that the value is old and will be recomputed.
*/
int startColumnIndex = -1;
/**
* Index of the the last visible column. A value of -1 indicates that the value is old and will be recomputed.
*/
int endColumnIndex = -1;
/**
* True if the last visible item is completely visible. The value must never be read directly. It is cached and
* updated when appropriate. #isShown() should be called for every client (even internal
* callers).
*
* @see #bottomIndex
*/
private boolean bottomIndexShownCompletely = false;
/**
* Tooltip text - overriden because we have cell specific tooltips
*/
private String toolTipText = null;
/**
* Flag that is set to true as soon as one image is set on any one item.
* This is used to mimic Table behavior that resizes the rows on the first image added.
* See imageSetOnItem.
*/
private boolean firstImageSet = false;
/**
* Mouse capture flag. Used for inplace tooltips. This flag must be used to ensure that
* we don't setCapture(false) in situations where we didn't do setCapture(true). The OS (SWT?)
* will automatically capture the mouse for us during a drag operation.
*/
private boolean inplaceTooltipCapture;
/**
* This is the tooltip text currently used. This could be the tooltip text for the currently
* hovered cell, or the general grid tooltip. See handleCellHover.
*/
private String displayedToolTipText;
/**
* The height of the area at the top and bottom of the
* visible grid area in which scrolling is initiated
* while dragging over this Grid.
*/
private static final int DRAG_SCROLL_AREA_HEIGHT = 12;
/**
* Threshold for the selection border used for drag n drop
* in mode (!{@link #dragOnFullSelection}}.
*/
private static final int SELECTION_DRAG_BORDER_THRESHOLD = 2;
private boolean hoveringOnSelectionDragArea = false;
private GridItem insertMarkItem = null;
private GridColumn insertMarkColumn = null;
private boolean insertMarkBefore = false;
private IRenderer insertMarkRenderer = new DefaultInsertMarkRenderer();
private boolean sizeOnEveryItemImageChange;
private boolean autoHeight = false;
private boolean autoWidth = true;
private boolean wordWrapRowHeader = false;
/**
* A range of rows in a <code>Grid</code>.
* <p>
* A row in this sense exists only for visible items
* (i.e. items with {@link GridItem#isVisible()} == true).
* Therefore, the items at 'startIndex' and 'endIndex'
* are always visible.
*
* @see Grid#getRowRange(int, int, boolean, boolean)
*/
private static class RowRange {
/** index of first item in range */
public int startIndex;
/** index of last item in range */
public int endIndex;
/** number of rows (i.e. <em>visible</em> items) in this range */
public int rows;
/** height in pixels of this range (including horizontal separator between rows) */
public int height;
}
/**
* Filters out unnecessary styles, adds mandatory styles and generally
* manages the style to pass to the super class.
*
* @param style user specified style.
* @return style to pass to the super 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 newStyle = style & mask;
newStyle |= SWT.DOUBLE_BUFFERED;
return newStyle;
}
/**
* 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));
// initialize drag & drop support
setData("DEFAULT_DRAG_SOURCE_EFFECT", new GridDragSourceEffect(this));
setData("DEFAULT_DROP_TARGET_EFFECT", new GridDropTargetEffect(this));
sizingGC = new GC(this);
topLeftRenderer.setDisplay(getDisplay());
bottomLeftRenderer.setDisplay(getDisplay());
rowHeaderRenderer.setDisplay(getDisplay());
emptyColumnHeaderRenderer.setDisplay(getDisplay());
emptyColumnFooterRenderer.setDisplay(getDisplay());
emptyCellRenderer.setDisplay(getDisplay());
dropPointRenderer.setDisplay(getDisplay());
focusRenderer.setDisplay(getDisplay());
emptyRowHeaderRenderer.setDisplay(getDisplay());
insertMarkRenderer.setDisplay(getDisplay());
setForeground(getDisplay().getSystemColor(SWT.COLOR_LIST_FOREGROUND));
setLineColor(getDisplay().getSystemColor(SWT.COLOR_WIDGET_BACKGROUND));
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();
}
scrollValuesObsolete = true;
initListeners();
initAccessible();
itemHeight = sizingGC.getFontMetrics().getHeight() + 2;
RGB sel = getDisplay().getSystemColor(SWT.COLOR_LIST_SELECTION).getRGB();
RGB white = getDisplay().getSystemColor(SWT.COLOR_WHITE).getRGB();
RGB cellSel = blend(sel,white,50);
cellHeaderSelectionBackground = new Color(getDisplay(),cellSel);
setDragDetect(false);
}
/**
* {@inheritDoc}
*/
public Color getBackground()
{
checkWidget();
if (backgroundColor == null)
return getDisplay().getSystemColor(SWT.COLOR_LIST_BACKGROUND);
return backgroundColor;
}
/**
* {@inheritDoc}
*/
public void setBackground(Color color)
{
checkWidget();
backgroundColor = color;
redraw();
}
/**
* Returns the background color of column and row headers when a cell in
* the row or header is selected.
*
* @return cell header selection background color
* @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 Color getCellHeaderSelectionBackground()
{
checkWidget();
return cellHeaderSelectionBackground;
}
/**
* Sets the background color of column and row headers displayed when a cell in
* the row or header is selected.
*
* @param cellSelectionBackground color 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 setCellHeaderSelectionBackground(Color cellSelectionBackground)
{
checkWidget();
this.cellHeaderSelectionBackground = cellSelectionBackground;
}
/**
* 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);
}
addListener(SWT.Selection, new TypedListener(listener));
addListener(SWT.DefaultSelection, new TypedListener(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);
}
addListener(SWT.Expand, new TypedListener(listener));
addListener(SWT.Collapse, new TypedListener(listener));
}
/**
* {@inheritDoc}
*/
public Point computeSize(int wHint, int hHint, boolean changed)
{
checkWidget();
Point prefSize = null;
if (wHint == SWT.DEFAULT || hHint == SWT.DEFAULT)
{
prefSize = getTableSize();
prefSize.x += 2 * getBorderWidth();
prefSize.y += 2 * getBorderWidth();
}
int x = 0;
int y = 0;
if (wHint == SWT.DEFAULT)
{
x += prefSize.x;
if (getVerticalBar() != null)
{
x += getVerticalBar().getSize().x;
}
}
else
{
x = wHint;
}
if (hHint == SWT.DEFAULT)
{
y += prefSize.y;
if (getHorizontalBar() != null)
{
y += getHorizontalBar().getSize().y;
}
}
else
{
y = hHint;
}
return new Point(x, y);
}
/**
* 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 (index < 0 || index > items.size() - 1)
{
return;
}
GridItem item = (GridItem)items.get(index);
if (!cellSelectionEnabled)
{
if (selectedItems.contains(item))
{
selectedItems.remove(item);
}
}
else
{
deselectCells(getCells(item));
}
redraw();
}
/**
* 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 i = start; i <= end; i++)
{
if (i < 0)
{
continue;
}
if (i > items.size() - 1)
{
break;
}
GridItem item = (GridItem)items.get(i);
if (!cellSelectionEnabled)
{
if (selectedItems.contains(item))
{
selectedItems.remove(item);
}
}
else
{
deselectCells(getCells(item));
}
}
redraw();
}
/**
* 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++)
{
int j = indices[i];
if (j >= 0 && j < items.size())
{
GridItem item = (GridItem)items.get(j);
if (!cellSelectionEnabled)
{
if (selectedItems.contains(item))
{
selectedItems.remove(item);
}
}
else
{
deselectCells(getCells(item));
}
}
}
redraw();
}
/**
* 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();
if (!cellSelectionEnabled)
{
selectedItems.clear();
redraw();
}
else
{
deselectAllCells();
}
}
/**
* 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 (GridColumn)columns.get(index);
}
/**
* Returns the column at the given point in the receiver or null if no such
* column exists. The point is in the coordinate system of the receiver.
*
* @param point the point used to locate the column
* @return the column 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 GridColumn getColumn(Point point)
{
return getColumn(null, point);
}
/**
* Returns the column at the given point and a known item in the receiver or null if no such
* column exists. The point is in the coordinate system of the receiver.
*
* @param item a known GridItem
* @param point the point used to locate the column
* @return the column 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>
*/
private GridColumn getColumn(GridItem item, Point point)
{
checkWidget();
if (point == null)
{
SWT.error(SWT.ERROR_NULL_ARGUMENT);
}
GridColumn overThis = null;
int x2 = 0;
if (rowHeaderVisible)
{
if (point.x <= rowHeaderWidth)
{
return null;
}
x2 += rowHeaderWidth;
}
x2 -= getHScrollSelectionInPixels();
for (Iterator columnIterator = displayOrderedColumns.iterator(); columnIterator.hasNext(); )
{
GridColumn column = (GridColumn) columnIterator.next();
if (!column.isVisible())
{
continue;
}
if (point.x >= x2 && point.x < x2 + column.getWidth())
{
overThis = column;
break;
}
x2 += column.getWidth();
}
if (overThis == null)
{
return null;
}
if (hasSpanning)
{
// special logic for column spanning
if(item == null) {
item = getItem(point);
}
if (item != null)
{
int displayColIndex = displayOrderedColumns.indexOf(overThis);
// track back all previous columns and check their spanning
for (int i = 0; i < displayColIndex; i++)
{
if (!((GridColumn)displayOrderedColumns.get(i)).isVisible())
{
continue;
}
int colIndex = indexOf((GridColumn)displayOrderedColumns.get(i));
int span = item.getColumnSpan(colIndex);
if (i + span >= displayColIndex)
{
overThis = (GridColumn)displayOrderedColumns.get(i);
break;
}
}
}
}
return overThis;
}
/**
* 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 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[] order = new int[columns.size()];
int i = 0;
for (Iterator colIterator = displayOrderedColumns.iterator(); colIterator.hasNext(); )
{
GridColumn col = (GridColumn) colIterator.next();
order[i] = columns.indexOf(col);
i++;
}
return order;
}
/**
* 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.length;
}
/**
* 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();
GridColumnGroup[] newArray = new GridColumnGroup[columnGroups.length];
System.arraycopy (columnGroups, 0, newArray, 0, columnGroups.length);
return newArray;
}
/**
* 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.length)
SWT.error(SWT.ERROR_INVALID_RANGE);
return columnGroups[index];
}
/**
* 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.length != 0)
{
GridColumnGroup currentGroup = null;
int colsInGroup = 0;
for (int i = 0; i < order.length; i++)
{
GridColumn col = getColumn(order[i]);
if (currentGroup != null)
{
if (col.getColumnGroup() != currentGroup && colsInGroup > 0 )
{
SWT.error(SWT.ERROR_INVALID_ARGUMENT);
}
else
{
colsInGroup--;
if (colsInGroup <= 0)
{
currentGroup = null;
}
}
}
else if (col.getColumnGroup() != null)
{
currentGroup = col.getColumnGroup();
colsInGroup = currentGroup.getColumns().length - 1;
}
}
}
GridColumn[] cols = getColumns();
displayOrderedColumns.clear();
for (int i = 0; i < order.length; i++)
{
displayOrderedColumns.add(cols[order[i]]);
}
}
/**
* 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 (GridColumn[])columns.toArray(new GridColumn[columns.size()]);
}
/**
* Returns the empty cell renderer.
*
* @return Returns the emptyCellRenderer.
* @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 GridCellRenderer getEmptyCellRenderer()
{
checkWidget();
return emptyCellRenderer;
}
/**
* Returns the empty column header renderer.
*
* @return Returns the emptyColumnHeaderRenderer.
* @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 IRenderer getEmptyColumnHeaderRenderer()
{
checkWidget();
return emptyColumnHeaderRenderer;
}
/**
* Returns the empty column footer renderer.
*
* @return Returns the emptyColumnFooterRenderer.
* @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 IRenderer getEmptyColumnFooterRenderer() {
checkWidget();
return emptyColumnFooterRenderer;
}
/**
* Returns the empty row header renderer.
*
* @return Returns the emptyRowHeaderRenderer.
* @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 IRenderer getEmptyRowHeaderRenderer()
{
checkWidget();
return emptyRowHeaderRenderer;
}
/**
* Returns the externally managed horizontal scrollbar.
*
* @return the external horizontal scrollbar.
* @see #setHorizontalScrollBarProxy(IScrollBarProxy)
* @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>
*/
protected IScrollBarProxy getHorizontalScrollBarProxy()
{
checkWidget();
return hScroll;
}
/**
* Returns the externally managed vertical scrollbar.
*
* @return the external vertical scrollbar.
* @see #setlVerticalScrollBarProxy(IScrollBarProxy)
* @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>
*/
protected IScrollBarProxy getVerticalScrollBarProxy()
{
checkWidget();
return vScroll;
}
/**
* Gets the focus renderer.
*
* @return Returns the focusRenderer.
* @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 IRenderer getFocusRenderer()
{
checkWidget();
return focusRenderer;
}
/**
* 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();
return headerHeight;
}
/**
* 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();
return 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();
return groupHeaderHeight;
}
/**
* 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 {@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 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 (GridItem)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);
if (point.x < 0 || point.x > getClientArea().width) return null;
Point p = new Point(point.x, point.y);
int y2=0;
if (columnHeadersVisible)
{
if (p.y <= headerHeight)
{
return null;
}
y2 += headerHeight;
}
GridItem itemToReturn = null;
int row=getTopIndex();
while(row<items.size() && y2<=getClientArea().height)
{
GridItem currItem = (GridItem)items.get(row);
if (currItem.isVisible())
{
int currItemHeight = currItem.getHeight();
if (p.y >= y2 && p.y < y2+currItemHeight+1)
{
itemToReturn = currItem;
break;
}
y2 += currItemHeight +1;
}
row++;
}
if (hasSpanning)
{
if (itemToReturn != null)
{
int itemIndex = this.getIndexOfItem(itemToReturn);
GridColumn gridColumn = getColumn(itemToReturn, point);
int displayColIndex = displayOrderedColumns.indexOf(gridColumn);
// track back all previous columns and check their spanning
for (int i = 0; i < itemIndex; i++)
{
GridItem gridItem = this.getItem(i);
if (gridItem.isVisible() == false)
{
continue;
}
int span = gridItem.getRowSpan(displayColIndex);
if (i + span >= itemIndex)
{
itemToReturn = gridItem;
break;
}
}
}
}
return itemToReturn;
}
/**
* 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 getItems().length;
}
/**
* Returns the default height of the items
* in this <code>Grid</code>. See {@link #setItemHeight(int)}
* for details.
*
* <p>IMPORTANT: The Grid's items need not all have the
* height returned by this method, because an
* item's height may have been changed by calling
* {@link GridItem#setHeight(int)}.
*
* @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();
return itemHeight;
}
/**
* 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()
* @see GridItem#setHeight(int)
*/
public void setItemHeight(int height)
{
checkWidget();
if (height < 1)
SWT.error(SWT.ERROR_INVALID_ARGUMENT);
itemHeight = height;
userModifiedItemHeight = true;
for(int cnt=0;cnt<items.size();cnt++)
((GridItem)items.get(cnt)).setHeight(height);
hasDifferingHeights=false;
setScrollValuesObsolete();
redraw();
}
/**
* Returns true if the rows are resizable.
*
* @return the row resizeable 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>
* @see #setRowsResizeable(boolean)
*/
public boolean getRowsResizeable() {
checkWidget();
return rowsResizeable;
}
/**
* Sets the rows resizeable state of this <code>Grid</code>.
* The default is 'false'.
* <p>
* If a row in a <code>Grid</code> is resizeable,
* then the user can interactively change its height
* by dragging the border of the row header.
* <p>
* Note that for rows to be resizable the row headers must be visible.
*
* @param rowsResizeable true if this <code>Grid</code>'s rows should be resizable
* @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 #setRowHeaderVisible(boolean)
*/
public void setRowsResizeable(boolean rowsResizeable) {
checkWidget();
this.rowsResizeable=rowsResizeable;
}
/**
* 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 (GridItem[])items.toArray(new GridItem[items.size()]);
}
/**
*
* @param item
* @return t
*/
public int getIndexOfItem(GridItem item)
{
checkWidget();
return items.indexOf(item);
}
/**
* Returns the line color.
*
* @return Returns the lineColor.
* @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 Color getLineColor()
{
checkWidget();
return lineColor;
}
/**
* 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;
}
/**
* Returns true if the tree lines are visible.
*
* @return Returns the treeLinesVisible.
* @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 getTreeLinesVisible() {
checkWidget();
return treeLinesVisible;
}
/**
* 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();
int index = items.indexOf(item);
if (items.size() == index + 1)
{
return null;
}
GridItem nextItem = (GridItem)items.get(index + 1);
while (!nextItem.isVisible())
{
index++;
if (items.size() == index + 1)
{
return null;
}
nextItem = (GridItem)items.get(index + 1);
}
return nextItem;
}
/**
* 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();
int index = 0;
if (item == null)
{
index = items.size();
}
else
{
index = items.indexOf(item);
if (index == 0)
{
return null;
}
}
GridItem prevItem = (GridItem)items.get(index - 1);
while (!prevItem.isVisible())
{
index--;
if (index == 0)
{
return null;
}
prevItem = (GridItem)items.get(index - 1);
}
return prevItem;
}
/**
* 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();
int index = displayOrderedColumns.indexOf(column);
if (index == 0)
return null;
index --;
GridColumn previous = (GridColumn)displayOrderedColumns.get(index);
while (!previous.isVisible())
{
if (index == 0)
return null;
index --;
previous = (GridColumn)displayOrderedColumns.get(index);
}
return previous;
}
/**
* 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();
int index = displayOrderedColumns.indexOf(column);
if (index == displayOrderedColumns.size() - 1)
return null;
index ++;
GridColumn next = (GridColumn)displayOrderedColumns.get(index);
while (!next.isVisible())
{
if (index == displayOrderedColumns.size() - 1)
return null;
index ++;
next = (GridColumn)displayOrderedColumns.get(index);
}
return next;
}
/**
* 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 (GridItem[])rootItems.toArray(new GridItem[rootItems.size()]);
}
/**
* TODO: asl;fj
* @param index
* @return asdf
*/
public GridItem getRootItem(int index)
{
checkWidget();
if (index < 0 || index >= rootItems.size())
{
SWT.error(SWT.ERROR_INVALID_RANGE);
}
return (GridItem)rootItems.get(index);
}
/**
* Gets the row header renderer.
*
* @return Returns the rowHeaderRenderer.
* @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 IRenderer getRowHeaderRenderer()
{
checkWidget();
return rowHeaderRenderer;
}
/**
* 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();
if (!cellSelectionEnabled)
{
return (GridItem[])selectedItems.toArray(new GridItem[selectedItems.size()]);
}
else
{
Vector items = new Vector();
int itemCount = getItemCount();
for (Iterator iter = selectedCells.iterator(); iter.hasNext();)
{
Point cell = (Point)iter.next();
if (cell.y >= 0 && cell.y < itemCount) {
GridItem item = getItem(cell.y);
if (!items.contains(item))
items.add(item);
}
}
return (GridItem[])items.toArray(new GridItem[]{});
}
}
/**
* 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();
if (!cellSelectionEnabled)
{
return selectedItems.size();
}
else
{
Vector items = new Vector();
for (Iterator iter = selectedCells.iterator(); iter.hasNext();)
{
Point cell = (Point)iter.next();
GridItem item = getItem(cell.y);
if (!items.contains(item))
items.add(item);
}
return items.size();
}
}
/**
* Returns the number of selected cells contained in the receiver.
*
* @return the number of selected cells
* @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 getCellSelectionCount()
{
checkWidget();
return selectedCells.size();
}
/**
* 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();
if (!cellSelectionEnabled)
{
if (selectedItems.size() == 0)
{
return -1;
}
return items.indexOf(selectedItems.get(0));
}
else
{
if (selectedCells.size() == 0)
return -1;
return ((Point)selectedCells.get(0)).y;
}
}
/**
* 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();
if (!cellSelectionEnabled)
{
int[] indices = new int[selectedItems.size()];
int i = 0;
for (Iterator itemIterator = selectedItems.iterator(); itemIterator.hasNext(); )
{
GridItem item = (GridItem) itemIterator.next();
indices[i] = items.indexOf(item);
i++;
}
return indices;
}
else
{
Vector selectedRows = new Vector();
for (Iterator iter = selectedCells.iterator(); iter.hasNext();)
{
Point cell = (Point)iter.next();
GridItem item = getItem(cell.y);
if (!selectedRows.contains(item))
selectedRows.add(item);
}
int[] indices = new int[selectedRows.size()];
int i = 0;
for (Iterator itemIterator = selectedRows.iterator(); itemIterator.hasNext(); )
{
GridItem item = (GridItem) itemIterator.next();
indices[i] = items.indexOf(item);
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
* @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)
return topIndex;
if (!vScroll.getVisible())
{
topIndex = 0;
}
else
{
// figure out first visible row and last visible row
int firstVisibleIndex = vScroll.getSelection();
if (isTree)
{
Iterator itemsIter = items.iterator();
int row = firstVisibleIndex + 1;
while (row > 0 && itemsIter.hasNext())
{
GridItem item = (GridItem)itemsIter.next();
if (item.isVisible())
{
row--;
if (row == 0)
{
firstVisibleIndex = items.indexOf(item);
}
}
}
}
topIndex = firstVisibleIndex;
/*
* MOPR here lies more potential for increasing performance
* for the case (isTree || hasDifferingHeights)
* the topIndex could be derived from the previous value
* depending on a delta of the vScroll.getSelection()
* instead of being calculated completely anew
*/
}
return topIndex;
}
/**
* Returns the zero-relative index of the item which is currently at the bottom
* of the receiver. This index can change when items are scrolled, expanded
* or collapsed or new items are added or removed.
* <p>
* Note that the item with this index is often only partly visible; maybe only
* a single line of pixels is visible. Use {@link #isShown(GridItem)} to find
* out.
* <p>
* In extreme cases, getBottomIndex() may return the same value as
* {@link #getTopIndex()}.
*
* @return the index of the bottom 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>
*/
int getBottomIndex() {
checkWidget();
if (bottomIndex != -1)
return bottomIndex;
if (items.size() == 0)
{
bottomIndex = 0;
}
else if (getVisibleGridHeight()<1)
{
bottomIndex = getTopIndex();
}
else
{
RowRange range = getRowRange(getTopIndex(),getVisibleGridHeight(),false,false);
bottomIndex = range.endIndex;
bottomIndexShownCompletely = range.height <= getVisibleGridHeight();
}
return bottomIndex;
}
/**
* Returns a {@link RowRange} ranging from
* the grid item at startIndex to that at endIndex.
* <p>
* This is primarily used to measure the height
* in pixel of such a range and to count the number
* of visible grid items within the range.
*
* @param startIndex index of the first item in the range or -1 to the first visible item in this grid
* @param endIndex index of the last item in the range or -1 to use the last visible item in this grid
* @return
*/
private RowRange getRowRange(int startIndex, int endIndex) {
// parameter preparation
if (startIndex == -1)
{
// search frist visible item
do startIndex++; while (startIndex < items.size() && !((GridItem)items.get(startIndex)).isVisible());
if (startIndex == items.size()) return null;
}
if (endIndex == -1)
{
// search last visible item
endIndex = items.size();
do endIndex--; while (endIndex >= 0 && !((GridItem)items.get(endIndex)).isVisible());
if (endIndex == -1) return null;
}
// fail fast
if (startIndex<0 || endIndex<0 || startIndex>=items.size() || endIndex>=items.size()
|| endIndex < startIndex
|| ((GridItem)items.get(startIndex)).isVisible()==false
|| ((GridItem)items.get(endIndex)).isVisible()==false)
SWT.error(SWT.ERROR_INVALID_ARGUMENT);
RowRange range = new RowRange();
range.startIndex = startIndex;
range.endIndex = endIndex;
if(isTree || hasDifferingHeights)
{
for (int idx=startIndex ; idx<=endIndex ; idx++ )
{
GridItem currItem = (GridItem)items.get(idx);
if(currItem.isVisible())
{
if (range.rows>0)
range.height++; // height of horizontal row separator
range.height += currItem.getHeight();
range.rows++;
}
}
}
else
{
range.rows = range.endIndex - range.startIndex + 1;
range.height = ( getItemHeight() + 1 ) * range.rows - 1;
}
return range;
}
/**
* This method can be used to build a range of grid rows
* that is allowed to span a certain height in pixels.
* <p>
* It returns a {@link RowRange} that contains information
* about the range, especially the index of the last
* element in the range (or if inverse == true, then the
* index of the first element).
* <p>
* Note: Even if 'forceEndCompletelyInside' is set to
* true, the last item will not lie completely within
* the availableHeight, if (height of item at startIndex < availableHeight).
*
* @param startIndex index of the first (if inverse==false) or
* last (if inverse==true) item in the range
* @param availableHeight height in pixels
* @param forceEndCompletelyInside if true, the last item in the range will lie completely
* within the availableHeight, otherwise it may lie partly outside this range
* @param inverse if true, then the first item in the range will be searched, not the last
* @return range of grid rows
* @see RowRange
*/
private RowRange getRowRange(int startIndex, int availableHeight,
boolean forceEndCompletelyInside, boolean inverse) {
// parameter preparation
if (startIndex == -1)
{
if(!inverse)
{
// search frist visible item
do startIndex++; while (startIndex < items.size() && !((GridItem)items.get(startIndex)).isVisible());
if (startIndex == items.size()) return null;
}
else
{
// search last visible item
startIndex = items.size();
do startIndex--; while (startIndex >= 0 && !((GridItem)items.get(startIndex)).isVisible());
if (startIndex == -1) return null;
}
}
// fail fast
if (startIndex < 0 || startIndex >= items.size()
|| ((GridItem)items.get(startIndex)).isVisible() == false)
SWT.error(SWT.ERROR_INVALID_ARGUMENT);
RowRange range = new RowRange();
if (availableHeight <= 0)
{
// special case: empty range
range.startIndex = startIndex;
range.endIndex = startIndex;
range.rows = 0;
range.height = 0;
return range;
}
if (isTree || hasDifferingHeights)
{
int otherIndex = startIndex; // tentative end index
int consumedItems = 0;
int consumedHeight = 0;
// consume height for startEnd (note: no separator pixel added here)
consumedItems++;
consumedHeight += ((GridItem)items.get(otherIndex)).getHeight();
// note: we use "+2" in next line, because we only try to add another row if there
// is room for the separator line + at least one pixel row for the additional item
while (consumedHeight+2 <= availableHeight)
{
// STEP 1:
// try to find a visible item we can add
int nextIndex = otherIndex;
GridItem nextItem;
do
{
if (!inverse)
nextIndex++;
else
nextIndex--;
if (nextIndex >= 0 && nextIndex < items.size())
nextItem = (GridItem)items.get(nextIndex);
else
nextItem = null;
}
while (nextItem != null && !nextItem.isVisible());
if (nextItem == null)
{
// no visible item found
break;
}
if (forceEndCompletelyInside)
{
// must lie completely within the allowed height
if(!(consumedHeight + 1 + nextItem.getHeight() <= availableHeight))
break;
}
// we found one !!
// STEP 2:
// Consume height for this item
consumedItems++;
consumedHeight += 1; // height of separator line
consumedHeight += nextItem.getHeight();
// STEP 3:
// make this item it the current guess for the other end
otherIndex = nextIndex;
}
range.startIndex = !inverse ? startIndex : otherIndex;
range.endIndex = !inverse ? otherIndex : startIndex;
range.rows = consumedItems;
range.height = consumedHeight;
}
else
{
int availableRows = ( availableHeight + 1 ) / ( getItemHeight() + 1 );
if ((( getItemHeight() + 1 ) * range.rows - 1) + 1 < availableHeight)
{
// not all available space used yet
// - so add another row if it need not be completely within availableHeight
if (!forceEndCompletelyInside)
availableRows++;
}
int otherIndex = startIndex + ((availableRows - 1) * (!inverse ? 1 : -1));
if (otherIndex<0) otherIndex = 0;
if (otherIndex>=items.size()) otherIndex = items.size() - 1 ;
range.startIndex = !inverse ? startIndex : otherIndex;
range.endIndex = !inverse ? otherIndex : startIndex;
range.rows = range.endIndex - range.startIndex + 1;
range.height = ( getItemHeight() + 1 ) * range.rows - 1;
}
return range;
}
/**
* Returns the height of the plain grid in pixels.
* <p>
* This includes all rows for visible items (i.e. items that return true
* on {@link GridItem#isVisible()} ; not only those currently visible on
* screen) and the 1 pixel separator between rows.
* <p>
* This does <em>not</em> include the height of the column headers.
*
* @return height of plain grid
*/
int getGridHeight() {
RowRange range = getRowRange(-1,-1);
return range != null ? range.height : 0;
/*
* MOPR currently this method is only used in #getTableSize() ;
* if it will be used for more important things in the future
* (e.g. the max value for vScroll.setValues() when doing pixel-by-pixel
* vertical scrolling) then this value should at least be cached or
* even updated incrementally when grid items are added/removed or
* expaned/collapsed (similar as #currentVisibleItems).
* (this is only necessary in the case (isTree || hasDifferingHeights))
*/
}
/**
* Returns the height of the on-screen area that is available
* for showing the grid's rows, i.e. the client area of the
* scrollable minus the height of the column headers (if shown).
*
* @return height of visible grid in pixels
*/
int getVisibleGridHeight() {
return getClientArea().height - (columnHeadersVisible ? headerHeight : 0) - (columnFootersVisible ? footerHeight : 0);
}
/**
* Returns the height of the screen area that is available for showing the grid columns
* @return
*/
int getVisibleGridWidth() {
return getClientArea().width - ( rowHeaderVisible ? rowHeaderWidth : 0 );
}
/**
* Gets the top left renderer.
*
* @return Returns the topLeftRenderer.
* @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 IRenderer getTopLeftRenderer()
{
checkWidget();
return topLeftRenderer;
}
/**
* Gets the bottom left renderer.
*
* @return Returns the bottomLeftRenderer.
* @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 IRenderer getBottomLeftRenderer()
{
checkWidget();
return bottomLeftRenderer;
}
/**
* 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);
if (column.getParent() != this) return -1;
return columns.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
* @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);
if (item.getParent() != this) return -1;
return items.indexOf(item);
}
/**
* Returns {@code true} if the receiver's row header is visible, and
* {@code false} otherwise.
* <p>
*
* @return the receiver's row 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 isRowHeaderVisible()
{
checkWidget();
return rowHeaderVisible;
}
/**
* 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();
if (index < 0 || index >= items.size()) return false;
if (!cellSelectionEnabled)
{
return isSelected((GridItem)items.get(index));
}
else
{
for (Iterator iter = selectedCells.iterator(); iter.hasNext();)
{
Point cell = (Point)iter.next();
if (cell.y == index) return true;
}
return false;
}
}
/**
* 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 (!cellSelectionEnabled)
{
return selectedItems.contains(item);
}
else
{
int index = indexOf(item);
if (index == -1) return false;
for (Iterator iter = selectedCells.iterator(); iter.hasNext();)
{
Point cell = (Point)iter.next();
if (cell.y == index) return true;
}
return false;
}
}
/**
* Returns true if the given cell is selected.
*
* @param cell cell
* @return true if the cell is selected.
* @throws IllegalArgumentException
* <ul>
* <li>ERROR_NULL_ARGUMENT - if the cell 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 isCellSelected(Point cell)
{
checkWidget();
if (cell == null)
SWT.error(SWT.ERROR_NULL_ARGUMENT);
return selectedCells.contains(cell);
}
/**
* 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);
}
GridItem item = (GridItem)items.get(index);
item.dispose();
redraw();
}
/**
* 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);
}
GridItem item = (GridItem)items.get(i);
item.dispose();
}
redraw();
}
/**
* 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 j = indices[i];
if (j < items.size() && j >= 0)
{
removeThese[i] = (GridItem)items.get(j);
}
else
{
SWT.error(SWT.ERROR_INVALID_RANGE);
}
}
for (int i = 0; i < removeThese.length; i++)
{
GridItem item = removeThese[i];
item.dispose();
}
redraw();
}
/**
* 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)
{
((GridItem)items.get(0)).dispose();
}
deselectAll();
redraw();
}
/**
* 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();
removeListener(SWT.Selection, listener);
removeListener(SWT.DefaultSelection, listener);
}
/**
* 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();
removeListener(SWT.Expand, listener);
removeListener(SWT.Collapse, listener);
}
/**
* Selects the item at the given zero-relative index in the receiver. If the
* item at the index was already selected, it remains selected. Indices that
* are out of range are ignored.
* <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) return;
if (index < 0 || index >= items.size()) return;
GridItem item = (GridItem)items.get(index);
if (!cellSelectionEnabled)
{
if (selectionType == SWT.MULTI && selectedItems.contains(item)) return;
if (selectionType == SWT.SINGLE) selectedItems.clear();
selectedItems.add(item);
}
else
{
selectCells(getCells(item));
}
redraw();
}
/**
* 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) return;
if (selectionType == SWT.SINGLE && start != end) return;
if (!cellSelectionEnabled)
{
if (selectionType == SWT.SINGLE) selectedItems.clear();
}
for (int i = start; i <= end; i++)
{
if (i < 0)
{
continue;
}
if (i > items.size() - 1)
{
break;
}
GridItem item = (GridItem)items.get(i);
if (!cellSelectionEnabled)
{
if (!selectedItems.contains(item))
selectedItems.add(item);
}
else
{
selectCells(getCells(item));
}
}
redraw();
}
/**
* 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) return;
if (selectionType == SWT.SINGLE && indices.length > 1) return;
if (!cellSelectionEnabled)
if (selectionType == SWT.SINGLE) selectedItems.clear();
for (int i = 0; i < indices.length; i++)
{
int j = indices[i];
if (j >= 0 && j < items.size())
{
GridItem item = (GridItem)items.get(j);
if (!cellSelectionEnabled)
{
if (!selectedItems.contains(item))
selectedItems.add(item);
}
else
{
selectCells(getCells(item));
}
}
}
redraw();
}
/**
* 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) return;
if (selectionType == SWT.SINGLE) return;
if (cellSelectionEnabled)
{
selectAllCells();
return;
}
selectedItems.clear();
selectedItems.addAll(items);
redraw();
}
/**
* Sets the empty cell renderer.
*
* @param emptyCellRenderer The emptyCellRenderer 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 setEmptyCellRenderer(GridCellRenderer emptyCellRenderer)
{
checkWidget();
emptyCellRenderer.setDisplay(getDisplay());
this.emptyCellRenderer = emptyCellRenderer;
}
/**
* Sets the empty column header renderer.
*
* @param emptyColumnHeaderRenderer The emptyColumnHeaderRenderer 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 setEmptyColumnHeaderRenderer(IRenderer emptyColumnHeaderRenderer)
{
checkWidget();
emptyColumnHeaderRenderer.setDisplay(getDisplay());
this.emptyColumnHeaderRenderer = emptyColumnHeaderRenderer;
}
/**
* Sets the empty column footer renderer.
*
* @param emptyColumnFooterRenderer The emptyColumnFooterRenderer 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 setEmptyColumnFooterRenderer(IRenderer emptyColumnFooterRenderer)
{
checkWidget();
emptyColumnFooterRenderer.setDisplay(getDisplay());
this.emptyColumnFooterRenderer = emptyColumnFooterRenderer;
}
/**
* Sets the empty row header renderer.
*
* @param emptyRowHeaderRenderer The emptyRowHeaderRenderer 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 setEmptyRowHeaderRenderer(IRenderer emptyRowHeaderRenderer)
{
checkWidget();
emptyRowHeaderRenderer.setDisplay(getDisplay());
this.emptyRowHeaderRenderer = emptyRowHeaderRenderer;
}
/**
* Sets the external horizontal scrollbar. Allows the scrolling to be
* managed externally from the table. This functionality is only intended
* when SWT.H_SCROLL is not given.
* <p>
* Using this feature, a ScrollBar could be instantiated outside the table,
* wrapped in IScrollBar and thus be 'connected' to the table.
*
* @param scroll The horizontal scrollbar 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>
*/
protected void setHorizontalScrollBarProxy(IScrollBarProxy scroll)
{
checkWidget();
if (getHorizontalBar() != null)
{
return;
}
hScroll = scroll;
hScroll.addSelectionListener(new SelectionListener() {
public void widgetSelected(SelectionEvent e) {
onScrollSelection();
}
public void widgetDefaultSelected(SelectionEvent e) {
}
});
}
/**
* Sets the external vertical scrollbar. Allows the scrolling to be managed
* externally from the table. This functionality is only intended when
* SWT.V_SCROLL is not given.
* <p>
* Using this feature, a ScrollBar could be instantiated outside the table,
* wrapped in IScrollBar and thus be 'connected' to the table.
*
* @param scroll
* The vertical scrollbar 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>
*/
protected void setlVerticalScrollBarProxy(IScrollBarProxy scroll)
{
checkWidget();
if (getVerticalBar() != null)
{
return;
}
vScroll = scroll;
vScroll.addSelectionListener(new SelectionListener() {
public void widgetSelected(SelectionEvent e) {
onScrollSelection();
}
public void widgetDefaultSelected(SelectionEvent e) {
}
});
}
/**
* Sets the focus renderer.
*
* @param focusRenderer The focusRenderer 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 setFocusRenderer(IRenderer focusRenderer)
{
checkWidget();
this.focusRenderer = focusRenderer;
}
/**
* 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();
this.columnHeadersVisible = show;
redraw();
}
/**
* 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();
this.columnFootersVisible = show;
redraw();
}
/**
* Sets the line color.
*
* @param lineColor The lineColor 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 setLineColor(Color lineColor)
{
checkWidget();
this.lineColor = lineColor;
}
/**
* Sets the line visibility.
*
* @param linesVisible Te 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;
redraw();
}
/**
* Sets the tree line visibility.
*
* @param treeLinesVisible
* @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 setTreeLinesVisible(boolean treeLinesVisible) {
checkWidget();
this.treeLinesVisible = treeLinesVisible;
redraw();
}
/**
* Sets the row header renderer.
*
* @param rowHeaderRenderer The rowHeaderRenderer 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 setRowHeaderRenderer(IRenderer rowHeaderRenderer)
{
checkWidget();
rowHeaderRenderer.setDisplay(getDisplay());
this.rowHeaderRenderer = rowHeaderRenderer;
}
/**
* Marks the receiver's row header as visible if the argument is
* {@code true}, and marks it invisible otherwise. When row headers are
* visible, horizontal scrolling is always done by column rather than by
* pixel.
*
* @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 setRowHeaderVisible(boolean show)
{
checkWidget();
this.rowHeaderVisible = show;
setColumnScrolling(true);
if (show && isAutoWidth())
{
rowHeaderWidth = 1;
for (Iterator iter = items.iterator(); iter.hasNext();)
{
GridItem iterItem = (GridItem)iter.next();
rowHeaderWidth = Math.max(rowHeaderWidth,rowHeaderRenderer.computeSize(sizingGC, SWT.DEFAULT,SWT.DEFAULT,iterItem).x);
}
}
redraw();
}
/**
* 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) return;
if (index >= 0 && index < items.size())
{
if (!cellSelectionEnabled)
{
selectedItems.clear();
selectedItems.add((GridItem)items.get(index));
redraw();
}
else
{
selectedCells.clear();
selectCells(getCells((GridItem)items.get(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) return;
if (selectionType == SWT.SINGLE && start != end) return;
if (!cellSelectionEnabled)
{
selectedItems.clear();
}
else
{
selectedCells.clear();
}
for (int i = start; i <= end; i++)
{
if (i < 0)
{
continue;
}
if (i > items.size() - 1)
{
break;
}
GridItem item = (GridItem)items.get(i);
if (!cellSelectionEnabled)
{
selectedItems.add(item);
}
else
{
selectCells(getCells(item));
}
}
redraw();
}
/**
* 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 (!selectionEnabled) return;
if (selectionType == SWT.SINGLE && indices.length > 1) return;
if (!cellSelectionEnabled)
{
selectedItems.clear();
}
else
{
selectedCells.clear();
}
for (int i = 0; i < indices.length; i++)
{
int j = indices[i];
if (j < 0)
{
continue;
}
if (j > items.size() - 1)
{
break;
}
GridItem item = (GridItem)items.get(j);
if (!cellSelectionEnabled)
{
selectedItems.add(item);
}
else
{
selectCells(getCells(item));
}
}
redraw();
}
/**
* 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 (!selectionEnabled) return;
if (_items == null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
if (selectionType == SWT.SINGLE && _items.length > 1) return;
if (!cellSelectionEnabled)
{
selectedItems.clear();
}
else
{
selectedCells.clear();
}
for (int i = 0; i < _items.length; i++)
{
GridItem item = _items[i];
if (item == null) continue;
if (item.isDisposed()) SWT.error(SWT.ERROR_INVALID_ARGUMENT);
if (item.getParent() != this) continue;
if (!cellSelectionEnabled)
{
selectedItems.add(item);
}
else
{
selectCells(getCells(item));
}
}
redraw();
}
/**
* 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 (index < 0 || index >= items.size())
{
return;
}
GridItem item = (GridItem)items.get(index);
if (!item.isVisible())
{
return;
}
if (!vScroll.getVisible())
{
return;
}
int vScrollAmount = 0;
for (int i = 0; i < index; i++)
{
if (((GridItem)items.get(i)).isVisible())
{
vScrollAmount++;
}
}
vScroll.setSelection(vScrollAmount);
topIndex = -1;
bottomIndex = -1;
redraw();
}
/**
* Sets the top left renderer.
*
* @param topLeftRenderer The topLeftRenderer 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 setTopLeftRenderer(IRenderer topLeftRenderer)
{
checkWidget();
topLeftRenderer.setDisplay(getDisplay());
this.topLeftRenderer = topLeftRenderer;
}
/**
* Sets the bottom left renderer.
*
* @param bottomLeftRenderer The topLeftRenderer 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 setBottomLeftRenderer(IRenderer bottomLeftRenderer)
{
checkWidget();
bottomLeftRenderer.setDisplay(getDisplay());
this.bottomLeftRenderer = bottomLeftRenderer;
}
/**
* 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 col 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 col)
{
checkWidget();
if (!col.isVisible())
{
GridColumnGroup group = col.getColumnGroup();
group.setExpanded(!group.getExpanded());
if (group.getExpanded())
{
group.notifyListeners(SWT.Expand,new Event());
}
else
{
group.notifyListeners(SWT.Collapse,new Event());
}
}
if (!hScroll.getVisible())
{
return;
}
int x = getColumnHeaderXPosition(col);
int firstVisibleX = 0;
if (rowHeaderVisible)
{
firstVisibleX = rowHeaderWidth;
}
// if its visible just return
if (x >= firstVisibleX
&& (x + col.getWidth()) <= (firstVisibleX + (getClientArea().width - firstVisibleX)))
{
return;
}
if (!getColumnScrolling())
{
if (x < firstVisibleX)
{
hScroll.setSelection(getHScrollSelectionInPixels() - (firstVisibleX - x));
}
else
{
if (col.getWidth() > getClientArea().width - firstVisibleX)
{
hScroll.setSelection(getHScrollSelectionInPixels() + (x - firstVisibleX));
}
else
{
x -= getClientArea().width - firstVisibleX - col.getWidth();
hScroll.setSelection(getHScrollSelectionInPixels() + (x - firstVisibleX));
}
}
}
else
{
if (x < firstVisibleX || col.getWidth() > getClientArea().width - firstVisibleX)
{
int sel = displayOrderedColumns.indexOf(col);
hScroll.setSelection(sel);
}
else
{
int availableWidth = getClientArea().width - firstVisibleX - col.getWidth();
GridColumn prevCol = getPreviousVisibleColumn(col);
GridColumn currentScrollTo = col;
while (true)
{
if (prevCol == null || prevCol.getWidth() > availableWidth)
{
int sel = displayOrderedColumns.indexOf(currentScrollTo);
hScroll.setSelection(sel);
break;
}
else
{
availableWidth -= prevCol.getWidth();
currentScrollTo = prevCol;
prevCol = getPreviousVisibleColumn(prevCol);
}
}
}
}
redraw();
}
/**
* Returns true if 'item' is currently being <em>completely</em>
* shown in this <code>Grid</code>'s visible on-screen area.
*
* <p>Here, "completely" only refers to the item's height, not its
* width. This means this method returns true also if some cells
* are horizontally scrolled away.
*
* @param item
* @return true if 'item' is 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>
*/
boolean isShown(GridItem item)
{
checkWidget();
if(!item.isVisible())
return false;
int itemIndex = items.indexOf(item);
if (itemIndex == -1)
SWT.error(SWT.ERROR_INVALID_ARGUMENT);
int firstVisibleIndex = getTopIndex();
int lastVisibleIndex = getBottomIndex();
return (itemIndex >= firstVisibleIndex && itemIndex < lastVisibleIndex)
||
(itemIndex == lastVisibleIndex && bottomIndexShownCompletely);
}
/**
* 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();
updateScrollbars();
// if no items are visible on screen then abort
if (getVisibleGridHeight()<1)
{
return;
}
// if its visible just return
if (isShown(item))
{
return;
}
if (!item.isVisible())
{
GridItem parent = item.getParentItem();
do
{
if (!parent.isExpanded())
{
parent.setExpanded(true);
parent.fireEvent(SWT.Expand);
}
parent = parent.getParentItem();
}
while (parent != null);
}
int newTopIndex = items.indexOf(item);
if (newTopIndex >= getBottomIndex())
{
RowRange range = getRowRange(newTopIndex,getVisibleGridHeight(),true,true); // note: inverse==true
newTopIndex = range.startIndex; // note: use startIndex because of inverse==true
}
setTopIndex(newTopIndex);
}
/**
* 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();
if (scrollValuesObsolete)
updateScrollbars();
GridItem item = null;
if (!cellSelectionEnabled)
{
if (selectedItems.size() == 0)
{
return;
}
item = (GridItem)selectedItems.get(0);
showItem(item);
}
else
{
if (selectedCells.size() == 0) return;
Point cell = (Point)selectedCells.get(0);
item = getItem(cell.y);
showItem(item);
GridColumn col = getColumn(cell.x);
showColumn(col);
}
}
/**
* 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();
redraw();
}
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;
}
/**
* Computes and sets the height of the header row. This method will ask for
* the preferred size of all the column headers and use the max.
*
* @param gc GC for font metrics, etc.
*/
private void computeHeaderHeight(GC gc)
{
int colHeaderHeight = 0;
for (Iterator columnsIterator = columns.iterator(); columnsIterator.hasNext(); )
{
GridColumn column = (GridColumn) columnsIterator.next();
colHeaderHeight = Math
.max(column.getHeaderRenderer().computeSize(gc, column.getWidth(), SWT.DEFAULT,
column).y, colHeaderHeight);
}
int groupHeight = 0;
for (int groupIndex = 0; groupIndex < columnGroups.length; groupIndex++)
{
GridColumnGroup group = (GridColumnGroup) columnGroups[groupIndex];
groupHeight = Math.max(group.getHeaderRenderer().computeSize(gc, SWT.DEFAULT,
SWT.DEFAULT, group).y,
groupHeight);
}
headerHeight = colHeaderHeight + groupHeight;
groupHeaderHeight = groupHeight;
}
private void computeFooterHeight(GC gc)
{
int colFooterHeight = 0;
for (Iterator columnsIterator = columns.iterator(); columnsIterator.hasNext(); )
{
GridColumn column = (GridColumn) columnsIterator.next();
colFooterHeight = Math
.max(column.getFooterRenderer().computeSize(gc, column.getWidth(), SWT.DEFAULT,
column).y, colFooterHeight);
}
footerHeight = colFooterHeight;
}
/**
* Returns the computed default item height. Currently this method just gets the
* preferred size of all the cells in the given row and returns that (it is
* then used as the height of all rows with items having a height of -1).
*
* @param item item to use for sizing
* @param gc GC used to perform font metrics,etc.
* @return the row height
*/
private int computeItemHeight(GridItem item, GC gc)
{
int height = 1;
if (columns.size() == 0 || items.size() == 0)
{
return height;
}
for (Iterator columnsIterator = columns.iterator(); columnsIterator.hasNext(); )
{
GridColumn column = (GridColumn) columnsIterator.next();
column.getCellRenderer().setColumn(indexOf(column));
height = Math.max(height, column.getCellRenderer().computeSize(gc, SWT.DEFAULT,
SWT.DEFAULT,
item).y);
}
if( rowHeaderVisible && rowHeaderRenderer != null ) {
height = Math.max(height, rowHeaderRenderer.computeSize(gc, SWT.DEFAULT,
SWT.DEFAULT, item).y);
}
return height <= 0 ? 16 : height;
}
/**
* Returns the x position of the given column. Takes into account scroll
* position.
*
* @param column given column
* @return x position
*/
private int getColumnHeaderXPosition(GridColumn column)
{
if (!column.isVisible())
{
return -1;
}
int x = 0;
x -= getHScrollSelectionInPixels();
if (rowHeaderVisible)
{
x += rowHeaderWidth;
}
for (Iterator column2Iterator = displayOrderedColumns.iterator(); column2Iterator.hasNext(); )
{
GridColumn column2 = (GridColumn) column2Iterator.next();
if (!column2.isVisible())
{
continue;
}
if (column2 == column)
{
break;
}
x += column2.getWidth();
}
return x;
}
/**
* Returns the hscroll selection in pixels. This method abstracts away the
* differences between column by column scrolling and pixel based scrolling.
*
* @return the horizontal scroll selection in pixels
*/
private int getHScrollSelectionInPixels()
{
int selection = hScroll.getSelection();
if (columnScrolling)
{
int pixels = 0;
for (int i = 0; i < selection; i++)
{
pixels += ((GridColumn)displayOrderedColumns.get(i)).getWidth();
}
selection = pixels;
}
return selection;
}
/**
* Returns the size of the preferred size of the inner table.
*
* @return the preferred size of the table.
*/
private Point getTableSize()
{
int x = 0;
int y = 0;
if (columnHeadersVisible)
{
y += headerHeight;
}
if(columnFootersVisible) {
y += footerHeight;
}
y += getGridHeight();
if (rowHeaderVisible)
{
x += rowHeaderWidth;
}
for (Iterator columnIterator = columns.iterator(); columnIterator.hasNext(); )
{
GridColumn column = (GridColumn) columnIterator.next();
if (column.isVisible())
{
x += column.getWidth();
}
}
return new Point(x, y);
}
/**
* Manages the header column dragging and calculates the drop point,
* triggers a redraw.
*
* @param x mouse x
* @return true if this event has been consumed.
*/
private boolean handleColumnDragging(int x)
{
GridColumn local_dragDropBeforeColumn = null;
GridColumn local_dragDropAfterColumn = null;
int x2 = 1;
if (rowHeaderVisible)
{
x2 += rowHeaderWidth + 1;
}
x2 -= getHScrollSelectionInPixels();
int i = 0;
GridColumn previousVisibleCol = null;
boolean nextVisibleColumnIsBeforeCol = false;
GridColumn firstVisibleCol = null;
GridColumn lastVisibleCol = null;
if (x < x2)
{
for (Iterator columnIterator = displayOrderedColumns.iterator(); columnIterator.hasNext(); )
{
GridColumn column = (GridColumn) columnIterator.next();
if (!column.isVisible())
{
continue;
}
local_dragDropBeforeColumn = column;
break;
}
local_dragDropAfterColumn = null;
}
else
{
for (Iterator columnIterator = displayOrderedColumns.iterator(); columnIterator.hasNext(); )
{
GridColumn column = (GridColumn) columnIterator.next();
if (!column.isVisible())
{
continue;
}
i++;
if (firstVisibleCol == null)
{
firstVisibleCol = column;
}
lastVisibleCol = column;
if (nextVisibleColumnIsBeforeCol)
{
local_dragDropBeforeColumn = column;
nextVisibleColumnIsBeforeCol = false;
}
if (x >= x2 && x <= (x2 + column.getWidth()))
{
if (x <= (x2 + column.getWidth() / 2))
{
local_dragDropBeforeColumn = column;
local_dragDropAfterColumn = previousVisibleCol;
}
else
{
local_dragDropAfterColumn = column;
// the next visible column is the before col
nextVisibleColumnIsBeforeCol = true;
}
}
x2 += column.getWidth();
previousVisibleCol = column;
}
if (local_dragDropBeforeColumn == null)
{
local_dragDropAfterColumn = lastVisibleCol;
}
}
currentHeaderDragX = x;
if (local_dragDropBeforeColumn != dragDropBeforeColumn
|| (dragDropBeforeColumn == null && dragDropAfterColumn == null))
{
dragDropPointValid = true;
// Determine if valid drop point
if (columnGroups.length != 0)
{
if (columnBeingPushed.getColumnGroup() == null)
{
if (local_dragDropBeforeColumn != null
&& local_dragDropAfterColumn != null
&& local_dragDropBeforeColumn.getColumnGroup() != null
&& local_dragDropBeforeColumn.getColumnGroup() == local_dragDropAfterColumn
.getColumnGroup())
{
// Dont move a column w/o a group in between two columns
// in the same group
dragDropPointValid = false;
}
}
else
{
if (!(local_dragDropBeforeColumn != null && local_dragDropBeforeColumn
.getColumnGroup() == columnBeingPushed.getColumnGroup())
&& !(local_dragDropAfterColumn != null && local_dragDropAfterColumn
.getColumnGroup() == columnBeingPushed.getColumnGroup()))
{
// Dont move a column with a group
dragDropPointValid = false;
}
}
}
else
{
dragDropPointValid = true;
}
}
dragDropBeforeColumn = local_dragDropBeforeColumn;
dragDropAfterColumn = local_dragDropAfterColumn;
Rectangle clientArea = getClientArea();
redraw(clientArea.x,clientArea.y,clientArea.width,clientArea.height,false);
return true;
}
/**
* Handles the moving of columns after a column is dropped.
*/
private void handleColumnDrop()
{
draggingColumn = false;
if ((dragDropBeforeColumn != columnBeingPushed && dragDropAfterColumn != columnBeingPushed)
&& (columnGroups.length == 0 || dragDropPointValid))
{
int notifyFrom = displayOrderedColumns.indexOf(columnBeingPushed);
int notifyTo = notifyFrom;
displayOrderedColumns.remove(columnBeingPushed);
if (dragDropBeforeColumn == null)
{
notifyTo = displayOrderedColumns.size();
displayOrderedColumns.add(columnBeingPushed);
}
else if (dragDropAfterColumn == null)
{
displayOrderedColumns.add(0, columnBeingPushed);
notifyFrom = 0;
}
else
{
int insertAtIndex = 0;
if (columnGroups.length != 0)
{
// ensure that we aren't putting this column into a group,
// this is possible if
// there are invisible columns between the after and before
// cols
if (dragDropBeforeColumn.getColumnGroup() == columnBeingPushed.getColumnGroup())
{
insertAtIndex = displayOrderedColumns.indexOf(dragDropBeforeColumn);
}
else if (dragDropAfterColumn.getColumnGroup() == columnBeingPushed
.getColumnGroup())
{
insertAtIndex = displayOrderedColumns.indexOf(dragDropAfterColumn) + 1;
}
else
{
if (dragDropBeforeColumn.getColumnGroup() == null)
{
insertAtIndex = displayOrderedColumns.indexOf(dragDropBeforeColumn);
}
else
{
GridColumnGroup beforeGroup = dragDropBeforeColumn.getColumnGroup();
insertAtIndex = displayOrderedColumns.indexOf(dragDropBeforeColumn);
while (insertAtIndex > 0
&& ((GridColumn)displayOrderedColumns.get(insertAtIndex -1)).getColumnGroup() == beforeGroup)
{
insertAtIndex--;
}
}
}
}
else
{
insertAtIndex = displayOrderedColumns.indexOf(dragDropBeforeColumn);
}
displayOrderedColumns.add(insertAtIndex, columnBeingPushed);
notifyFrom = Math.min(notifyFrom, insertAtIndex);
notifyTo = Math.max(notifyTo, insertAtIndex);
}
for (int i = notifyFrom; i <= notifyTo; i++)
{
((GridColumn)displayOrderedColumns.get(i)).fireMoved();
}
}
redraw();
}
/**
* Determines if the mouse is pushing the header but has since move out of
* the header bounds and therefore should be drawn unpushed. Also initiates
* a column header drag when appropriate.
*
* @param x mouse x
* @param y mouse y
* @return true if this event has been consumed.
*/
private boolean handleColumnHeaderHoverWhilePushing(int x, int y)
{
GridColumn overThis = overColumnHeader(x, y);
if ((overThis == columnBeingPushed) != pushingAndHovering)
{
pushingAndHovering = (overThis == columnBeingPushed);
redraw();
}
if (columnBeingPushed.getMoveable())
{
if (pushingAndHovering && Math.abs(startHeaderPushX - x) > 3)
{
// stop pushing
pushingColumn = false;
columnBeingPushed.getHeaderRenderer().setMouseDown(false);
columnBeingPushed.getHeaderRenderer().setHover(false);
// now dragging
draggingColumn = true;
columnBeingPushed.getHeaderRenderer().setMouseDown(false);
startHeaderDragX = x;
dragDropAfterColumn = null;
dragDropBeforeColumn = null;
dragDropPointValid = true;
handleColumnDragging(x);
}
}
return true;
}
/**
* Determines if a column group header has been clicked and forwards the
* event to the header renderer.
*
* @param x mouse x
* @param y mouse y
* @return true if this event has been consumed.
*/
private boolean handleColumnGroupHeaderClick(int x, int y)
{
if (!columnHeadersVisible)
{
return false;
}
GridColumnGroup overThis = overColumnGroupHeader(x, y);
if (overThis == null)
{
return false;
}
int headerX = 0;
if (rowHeaderVisible)
{
headerX += rowHeaderWidth;
}
int width = 0;
boolean firstCol = false;
for (Iterator colIterator = displayOrderedColumns.iterator(); colIterator.hasNext(); )
{
GridColumn col = (GridColumn) colIterator.next();
if (col.getColumnGroup() == overThis && col.isVisible())
{
firstCol = true;
width += col.getWidth();
}
if (!firstCol && col.isVisible())
{
headerX += col.getWidth();
}
}
overThis.getHeaderRenderer().setBounds(headerX - getHScrollSelectionInPixels(), 0, width,
groupHeaderHeight);
return overThis.getHeaderRenderer()
.notify(IInternalWidget.LeftMouseButtonDown, new Point(x, y), overThis);
}
/**
* Determines if a column header has been clicked, updates the renderer
* state and triggers a redraw if necesary.
*
* @param x mouse x
* @param y mouse y
* @return true if this event has been consumed.
*/
private boolean handleColumnHeaderPush(int x, int y)
{
if (!columnHeadersVisible)
{
return false;
}
GridColumn overThis = overColumnHeader(x, y);
if (overThis == null)
{
return false;
}
columnBeingPushed = overThis;
// draw pushed
columnBeingPushed.getHeaderRenderer().setMouseDown(true);
columnBeingPushed.getHeaderRenderer().setHover(true);
pushingAndHovering = true;
redraw();
startHeaderPushX = x;
pushingColumn = true;
setCapture(true);
return true;
}
private boolean handleColumnFooterPush(int x, int y)
{
if(!columnFootersVisible) {
return false;
}
GridColumn overThis = overColumnFooter(x, y);
if (overThis == null)
{
return false;
}
return true;
}
/**
* Sets the new width of the column being resized and fires the appropriate
* listeners.
*
* @param x mouse x
*/
private void handleColumnResizerDragging(int x)
{
int newWidth = resizingColumnStartWidth + (x - resizingStartX);
if (newWidth < MIN_COLUMN_HEADER_WIDTH)
{
newWidth = MIN_COLUMN_HEADER_WIDTH;
}
if (columnScrolling)
{
int maxWidth = getClientArea().width;
if (rowHeaderVisible)
maxWidth -= rowHeaderWidth;
if (newWidth > maxWidth)
newWidth = maxWidth;
}
if (newWidth == columnBeingResized.getWidth())
{
return;
}
columnBeingResized.setWidth(newWidth,false);
scrollValuesObsolete = true;
Rectangle clientArea = getClientArea();
redraw(clientArea.x,clientArea.y,clientArea.width,clientArea.height,false);
columnBeingResized.fireResized();
for (int index = displayOrderedColumns.indexOf(columnBeingResized) + 1; index < displayOrderedColumns.size(); index ++)
{
GridColumn col = (GridColumn)displayOrderedColumns.get(index);
if (col.isVisible()) col.fireMoved();
}
}
/**
* Sets the new height of the item of the row being resized and fires the appropriate
* listeners.
*
* @param x mouse x
*/
private void handleRowResizerDragging(int y)
{
int newHeight = resizingRowStartHeight + (y - resizingStartY);
if (newHeight < MIN_ROW_HEADER_HEIGHT)
{
newHeight = MIN_ROW_HEADER_HEIGHT;
}
if (newHeight > getClientArea().height)
{
newHeight = getClientArea().height;
}
if (newHeight == rowBeingResized.getHeight())
{
return;
}
Event e = new Event();
e.item = rowBeingResized;
e.widget = this;
e.detail = newHeight;
rowBeingResized.notifyListeners(SWT.Resize, e);
if (e.doit == false)
return;
newHeight = e.detail;
if (newHeight < MIN_ROW_HEADER_HEIGHT)
{
newHeight = MIN_ROW_HEADER_HEIGHT;
}
if (newHeight > getClientArea().height)
{
newHeight = getClientArea().height;
}
rowBeingResized.setHeight(newHeight);
scrollValuesObsolete = true;
Rectangle clientArea = getClientArea();
redraw(clientArea.x,clientArea.y,clientArea.width,clientArea.height,false);
}
/**
* Determines if the mouse is hovering on a column resizer and changes the
* pointer and sets field appropriately.
*
* @param x mouse x
* @param y mouse y
* @return true if this event has been consumed.
*/
private boolean handleHoverOnColumnResizer(int x, int y)
{
boolean over = false;
if (y <= headerHeight)
{
int x2 = 0;
if (rowHeaderVisible)
{
x2 += rowHeaderWidth;
}
x2 -= getHScrollSelectionInPixels();
for (Iterator columnIterator = displayOrderedColumns.iterator(); columnIterator.hasNext(); )
{
GridColumn column = (GridColumn) columnIterator.next();
if (!column.isVisible())
{
continue;
}
x2 += column.getWidth();
if (x2 >= (x - COLUMN_RESIZER_THRESHOLD) && x2 <= (x + COLUMN_RESIZER_THRESHOLD))
{
if (column.getResizeable())
{
if (column.getColumnGroup() != null && y <= groupHeaderHeight)
{
// if this is not the last column
if (column != column.getColumnGroup().getLastVisibleColumn())
{
break;
}
}
over = true;
columnBeingResized = column;
}
break;
}
}
}
if (over != hoveringOnColumnResizer)
{
if (over)
{
setCursor(getDisplay().getSystemCursor(SWT.CURSOR_SIZEWE));
}
else
{
columnBeingResized = null;
setCursor(null);
}
hoveringOnColumnResizer = over;
}
return over;
}
/**
* Determines if the mouse is hovering on a row resizer and changes the
* pointer and sets field appropriately.
*
* @param x mouse x
* @param y mouse y
* @return true if this event has been consumed.
*/
private boolean handleHoverOnRowResizer(int x, int y)
{
rowBeingResized = null;
boolean over = false;
if (x <= rowHeaderWidth)
{
int y2 = 0;
if (columnHeadersVisible)
{
y2 += headerHeight;
}
int row=getTopIndex();
while(row<items.size() && y2<=getClientArea().height)
{
GridItem currItem = (GridItem)items.get(row);
if (currItem.isVisible())
{
y2 += currItem.getHeight() +1;
if (y2 >= (y - ROW_RESIZER_THRESHOLD) && y2 <= (y + ROW_RESIZER_THRESHOLD))
{
// if (currItem.isResizeable())
{
over = true;
rowBeingResized = currItem;
}
// do not brake here, because in case of overlapping
// row resizers we need to find the last one
}
else
{
if(rowBeingResized != null)
{
// we have passed all (overlapping) row resizers, so break
break;
}
}
}
row++;
}
}
if (over != hoveringOnRowResizer)
{
if (over)
{
setCursor(getDisplay().getSystemCursor(SWT.CURSOR_SIZENS));
}
else
{
rowBeingResized = null;
setCursor(null);
}
hoveringOnRowResizer = over;
}
return over;
}
/**
* Returns the cell at the given point in the receiver or null if no such
* cell exists. The point is in the coordinate system of the receiver.
*
* @param point the point used to locate the item
* @return the cell 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 Point getCell(Point point)
{
checkWidget();
if (point == null)
SWT.error(SWT.ERROR_NULL_ARGUMENT);
if (point.x < 0 || point.x > getClientArea().width) return null;
GridItem item = getItem(point);
GridColumn column = getColumn(point);
if (item!=null && column!=null)
{
return new Point(columns.indexOf(column),items.indexOf(item));
}
else
{
return null;
}
}
/**
* Paints.
*
* @param e paint event
*/
private void onPaint(PaintEvent e)
{
int insertMarkPosX1 = -1; // we will populate these values while drawing the cells
int insertMarkPosX2 = -1;
int insertMarkPosY = -1;
boolean insertMarkPosFound = false;
GridCellSpanManager cellSpanManager = new GridCellSpanManager();
e.gc.setBackground(getBackground());
this.drawBackground(e.gc,0,0,getSize().x,getSize().y);
if (scrollValuesObsolete)
{
updateScrollbars();
scrollValuesObsolete = false;
}
int x = 0;
int y = 0;
if (columnHeadersVisible)
{
paintHeader(e.gc);
y += headerHeight;
}
int availableHeight = getClientArea().height-y;
int visibleRows = availableHeight / getItemHeight() + 1;
if (items.size()>0 && availableHeight>0)
{
RowRange range = getRowRange(getTopIndex(),availableHeight,false,false);
if (range.height >= availableHeight)
visibleRows = range.rows;
else
visibleRows = range.rows + (availableHeight-range.height) / getItemHeight() + 1;
}
int firstVisibleIndex = getTopIndex();
int firstItemToDraw = firstVisibleIndex;
if(hasSpanning) {
// We need to find the first Item to draw. An earlier item can row-span the first visible item.
for(int rowIndex = 0; rowIndex < firstVisibleIndex; rowIndex++)
{
GridItem itemForRow = (GridItem)items.get(rowIndex);
int colIndex = 0;
int maxRowSpanForItem = 0;
for (Iterator columnIterator = displayOrderedColumns.iterator(); columnIterator.hasNext(); )
{
GridColumn column = (GridColumn) columnIterator.next();
if (!column.isVisible())
{
colIndex++;
continue;
}
int rowSpan = itemForRow.getRowSpan(colIndex);
maxRowSpanForItem = rowSpan > maxRowSpanForItem ? rowSpan : maxRowSpanForItem;
colIndex++;
}
if(rowIndex + maxRowSpanForItem >= firstVisibleIndex) {
firstItemToDraw = rowIndex;
break;
} else {
rowIndex += maxRowSpanForItem;
}
}
for(int rowIndex = firstItemToDraw; rowIndex < firstVisibleIndex; rowIndex++)
{
GridItem itemForRow = (GridItem)items.get(rowIndex);
y = y - itemForRow.getHeight() - 1;
}
}
int row = firstItemToDraw;
for (int i = 0; i < visibleRows + (firstVisibleIndex - firstItemToDraw); i++)
{
x = 0;
x -= getHScrollSelectionInPixels();
// get the item to draw
GridItem item = null;
if (row < items.size())
{
item = (GridItem)items.get(row);
while (!item.isVisible() && row < items.size() - 1)
{
row++;
item = (GridItem)items.get(row);
}
}
if (item != null && !item.isVisible())
{
item = null;
}
if (item != null)
{
boolean cellInRowSelected = false;
if (rowHeaderVisible)
{
// row header is actually painted later
x += rowHeaderWidth;
}
int focusY = y;
int colIndex = 0;
// draw regular cells for each column
for (Iterator columnIterator = displayOrderedColumns.iterator(); columnIterator.hasNext(); )
{
GridColumn column = (GridColumn) columnIterator.next();
boolean skipCell = cellSpanManager.skipCell(colIndex, row);
int indexOfColumn = indexOf(column);
if (!column.isVisible())
{
colIndex++;
if(skipCell)
{
cellSpanManager.consumeCell(colIndex, row);
}
continue;
}
int width = item.getCellSize(indexOfColumn).x;
if(skipCell == false)
{
int nrRowsToSpan = item.getRowSpan(indexOfColumn);
int nrColumnsToSpan = item.getColumnSpan(indexOfColumn);
if(nrRowsToSpan > 0 || nrColumnsToSpan > 0)
{
cellSpanManager.addCellSpanInfo(colIndex, row, nrColumnsToSpan, nrRowsToSpan);
}
if (x + width >= 0 && x < getClientArea().width )
{
Point sizeOfColumn = item.getCellSize(indexOfColumn);
column.getCellRenderer().setBounds(x, y, width, sizeOfColumn.y);
int cellInHeaderDelta = columnHeadersVisible ? headerHeight - y : 0;
if(cellInHeaderDelta > 0)
{
e.gc.setClipping(new Rectangle(x -1,y + cellInHeaderDelta, width +1, sizeOfColumn.y + 2 - cellInHeaderDelta));
}
else
{
e.gc.setClipping(new Rectangle(x -1,y -1,width +1, sizeOfColumn.y + 2));
}
column.getCellRenderer().setRow(i + 1);
column.getCellRenderer().setSelected(selectedItems.contains(item));
column.getCellRenderer().setFocus(this.isFocusControl());
column.getCellRenderer().setRowFocus(focusItem == item);
column.getCellRenderer().setCellFocus(cellSelectionEnabled && focusItem == item && focusColumn == column);
column.getCellRenderer().setRowHover(hoveringItem == item);
column.getCellRenderer().setColumnHover(hoveringColumn == column);
column.getCellRenderer().setColumn(indexOfColumn);
if (selectedCells.contains(new Point(indexOfColumn,row)))
{
column.getCellRenderer().setCellSelected(true);
cellInRowSelected = true;
}
else
{
column.getCellRenderer().setCellSelected(false);
}
if (hoveringItem == item && hoveringColumn == column)
{
column.getCellRenderer().setHoverDetail(hoveringDetail);
}
else
{
column.getCellRenderer().setHoverDetail("");
}
column.getCellRenderer().paint(e.gc, item);
e.gc.setClipping((Rectangle)null);
// collect the insertMark position
if (!insertMarkPosFound && insertMarkItem == item && (insertMarkColumn == null || insertMarkColumn == column))
{
// y-pos
insertMarkPosY = y - 1;
if (!insertMarkBefore)
insertMarkPosY += item.getHeight() + 1;
// x1-pos
insertMarkPosX1 = x;
if (column.isTree())
{
insertMarkPosX1 += Math.min(
width,
column.getCellRenderer().getTextBounds(item, false).x);
}
// x2-pos
if (insertMarkColumn == null)
{
insertMarkPosX2 = getClientArea().x + getClientArea().width;
}
else
{
insertMarkPosX2 = x + width;
}
insertMarkPosFound = true;
}
}
}
else
{
cellSpanManager.consumeCell(colIndex, row);
}
x += column.getWidth();
colIndex++;
}
if (x < getClientArea().width)
{
// insertMarkPos needs correction
if(insertMarkPosFound && insertMarkColumn == null)
insertMarkPosX2 = x;
emptyCellRenderer.setSelected(selectedItems.contains(item));
emptyCellRenderer.setFocus(this.isFocusControl());
emptyCellRenderer.setRow(i + 1);
emptyCellRenderer.setBounds(x, y, getClientArea().width - x + 1, item.getHeight());
emptyCellRenderer.setColumn(getColumnCount());
emptyCellRenderer.paint(e.gc, item);
}
x = 0;
if (rowHeaderVisible)
{
if (!cellSelectionEnabled)
{
rowHeaderRenderer.setSelected(selectedItems.contains(item));
}
else
{
rowHeaderRenderer.setSelected(cellInRowSelected);
}
if(y >= headerHeight)
{
rowHeaderRenderer.setBounds(0, y, rowHeaderWidth, item.getHeight() + 1);
rowHeaderRenderer.paint(e.gc, item);
}
x += rowHeaderWidth;
}
// focus
if (isFocusControl() && !cellSelectionEnabled)
{
if (item == focusItem)
{
if (focusRenderer != null)
{
int focusX = 0;
if (rowHeaderVisible)
{
focusX = rowHeaderWidth;
}
focusRenderer
.setBounds(focusX, focusY - 1, getClientArea().width - focusX - 1,
item.getHeight() + 1);
focusRenderer.paint(e.gc, item);
}
}
}
y += item.getHeight() + 1;
}
else
{
if (rowHeaderVisible)
{
//row header is actually painted later
x += rowHeaderWidth;
}
emptyCellRenderer.setBounds(x, y, getClientArea().width - x, getItemHeight());
emptyCellRenderer.setFocus(false);
emptyCellRenderer.setSelected(false);
emptyCellRenderer.setRow(i + 1);
for (Iterator columnIterator = displayOrderedColumns.iterator(); columnIterator.hasNext(); )
{
GridColumn column = (GridColumn) columnIterator.next();
if (column.isVisible())
{
emptyCellRenderer.setBounds(x, y, column.getWidth(), getItemHeight());
emptyCellRenderer.setColumn(indexOf(column));
emptyCellRenderer.paint(e.gc, this);
x += column.getWidth();
}
}
if (x < getClientArea().width)
{
emptyCellRenderer.setBounds(x, y, getClientArea().width - x + 1, getItemHeight());
emptyCellRenderer.setColumn(getColumnCount());
emptyCellRenderer.paint(e.gc, this);
}
x = 0;
if (rowHeaderVisible)
{
emptyRowHeaderRenderer.setBounds(x, y, rowHeaderWidth, getItemHeight() + 1);
emptyRowHeaderRenderer.paint(e.gc, this);
x += rowHeaderWidth;
}
y += getItemHeight() + 1;
}
row++;
}
// draw drop point
if (draggingColumn)
{
if ((dragDropAfterColumn != null || dragDropBeforeColumn != null)
&& (dragDropAfterColumn != columnBeingPushed && dragDropBeforeColumn != columnBeingPushed)
&& dragDropPointValid)
{
if (dragDropBeforeColumn != null)
{
x = getColumnHeaderXPosition(dragDropBeforeColumn);
}
else
{
x = getColumnHeaderXPosition(dragDropAfterColumn)
+ dragDropAfterColumn.getWidth();
}
Point size = dropPointRenderer.computeSize(e.gc, SWT.DEFAULT, SWT.DEFAULT, null);
x -= size.x / 2;
if (x < 0)
{
x = 0;
}
dropPointRenderer.setBounds(x - 1, headerHeight + DROP_POINT_LOWER_OFFSET, size.x,
size.y);
dropPointRenderer.paint(e.gc, null);
}
}
// draw insertion mark
if (insertMarkPosFound)
{
e.gc.setClipping(
rowHeaderVisible ? rowHeaderWidth : 0,
columnHeadersVisible ? headerHeight : 0,
getClientArea().width,
getClientArea().height);
insertMarkRenderer.paint(e.gc, new Rectangle(insertMarkPosX1, insertMarkPosY, insertMarkPosX2 - insertMarkPosX1, 0));
}
if (columnFootersVisible)
{
paintFooter(e.gc);
}
}
/**
* Returns a column reference if the x,y coordinates are over a column
* header (header only).
*
* @param x mouse x
* @param y mouse y
* @return column reference which mouse is over, or null.
*/
private GridColumn overColumnHeader(int x, int y)
{
GridColumn col = null;
if (y <= headerHeight && y > 0)
{
col = getColumn(new Point(x, y));
if (col != null && col.getColumnGroup() != null)
{
if (y <= groupHeaderHeight)
{
return null;
}
}
}
return col;
}
/**
* Returns a column reference if the x,y coordinates are over a column
* header (header only).
*
* @param x mouse x
* @param y mouse y
* @return column reference which mouse is over, or null.
*/
private GridColumn overColumnFooter(int x, int y)
{
GridColumn col = null;
if (y >= getClientArea().height - footerHeight )
{
col = getColumn(new Point(x, y));
}
return col;
}
/**
* Returns a column group reference if the x,y coordinates are over a column
* group header (header only).
*
* @param x mouse x
* @param y mouse y
* @return column group reference which mouse is over, or null.
*/
private GridColumnGroup overColumnGroupHeader(int x, int y)
{
GridColumnGroup group = null;
if (y <= groupHeaderHeight && y > 0)
{
GridColumn col = getColumn(new Point(x, y));
if (col != null)
{
group = col.getColumnGroup();
}
}
return group;
}
/**
* Paints the header.
*
* @param gc gc from paint event
*/
private void paintHeader(GC gc)
{
int x = 0;
int y = 0;
x -= getHScrollSelectionInPixels();
if (rowHeaderVisible)
{
// paint left corner
// topLeftRenderer.setBounds(0, y, rowHeaderWidth, headerHeight);
// topLeftRenderer.paint(gc, null);
x += rowHeaderWidth;
}
GridColumnGroup previousPaintedGroup = null;
for (Iterator columnIterator = displayOrderedColumns.iterator(); columnIterator.hasNext(); )
{
if (x > getClientArea().width)
break;
GridColumn column = (GridColumn) columnIterator.next();
int height = 0;
if (!column.isVisible())
{
continue;
}
if (column.getColumnGroup() != null)
{
if (column.getColumnGroup() != previousPaintedGroup)
{
int width = column.getWidth();
GridColumn nextCol = null;
if (displayOrderedColumns.indexOf(column) + 1 < displayOrderedColumns.size())
{
nextCol = (GridColumn)displayOrderedColumns
.get(displayOrderedColumns.indexOf(column) + 1);
}
while (nextCol != null && nextCol.getColumnGroup() == column.getColumnGroup())
{
if ((nextCol.getColumnGroup().getExpanded() && !nextCol.isDetail())
|| (!nextCol.getColumnGroup().getExpanded() && !nextCol.isSummary()))
{
}
else if( nextCol.isVisible() )
{
width += nextCol.getWidth();
}
if (displayOrderedColumns.indexOf(nextCol) + 1 < displayOrderedColumns
.size())
{
nextCol = (GridColumn)displayOrderedColumns.get(displayOrderedColumns
.indexOf(nextCol) + 1);
}
else
{
nextCol = null;
}
}
boolean selected = true;
for (int i = 0; i < column.getColumnGroup().getColumns().length; i++)
{
GridColumn col = column.getColumnGroup().getColumns()[i];
if (col.isVisible() && (column.getMoveable() || !selectedColumns.contains(col)))
{
selected = false;
break;
}
}
column.getColumnGroup().getHeaderRenderer().setSelected(selected);
column.getColumnGroup().getHeaderRenderer()
.setHover(hoverColumnGroupHeader == column.getColumnGroup());
column.getColumnGroup().getHeaderRenderer().setHoverDetail(hoveringDetail);
column.getColumnGroup().getHeaderRenderer().setBounds(x, 0, width,
groupHeaderHeight);
column.getColumnGroup().getHeaderRenderer().paint(gc, column.getColumnGroup());
previousPaintedGroup = column.getColumnGroup();
}
height = headerHeight - groupHeaderHeight;
y = groupHeaderHeight;
}
else
{
height = headerHeight;
y = 0;
}
if (pushingColumn)
{
column.getHeaderRenderer().setHover(
columnBeingPushed == column
&& pushingAndHovering);
}
else
{
column.getHeaderRenderer().setHover(hoveringColumnHeader == column);
}
column.getHeaderRenderer().setHoverDetail(hoveringDetail);
column.getHeaderRenderer().setBounds(x, y, column.getWidth(), height);
if (cellSelectionEnabled)
column.getHeaderRenderer().setSelected(selectedColumns.contains(column));
if (x + column.getWidth() >= 0)
{
column.getHeaderRenderer().paint(gc, column);
}
x += column.getWidth();
}
if (x < getClientArea().width)
{
emptyColumnHeaderRenderer.setBounds(x, 0, getClientArea().width - x, headerHeight);
emptyColumnHeaderRenderer.paint(gc, null);
}
x = 0;
if (rowHeaderVisible)
{
// paint left corner
topLeftRenderer.setBounds(0, 0, rowHeaderWidth, headerHeight);
topLeftRenderer.paint(gc, this);
x += rowHeaderWidth;
}
if (draggingColumn)
{
gc.setAlpha(COLUMN_DRAG_ALPHA);
columnBeingPushed.getHeaderRenderer().setSelected(false);
int height = 0;
if (columnBeingPushed.getColumnGroup() != null)
{
height = headerHeight - groupHeaderHeight;
y = groupHeaderHeight;
}
else
{
height = headerHeight;
y = 0;
}
columnBeingPushed.getHeaderRenderer()
.setBounds(
getColumnHeaderXPosition(columnBeingPushed)
+ (currentHeaderDragX - startHeaderDragX), y,
columnBeingPushed.getWidth(), height);
columnBeingPushed.getHeaderRenderer().paint(gc, columnBeingPushed);
columnBeingPushed.getHeaderRenderer().setSelected(false);
gc.setAlpha(-1);
gc.setAdvanced(false);
}
}
private void paintFooter(GC gc) {
int x = 0;
int y = 0;
x -= getHScrollSelectionInPixels();
if (rowHeaderVisible)
{
// paint left corner
// topLeftRenderer.setBounds(0, y, rowHeaderWidth, headerHeight);
// topLeftRenderer.paint(gc, null);
x += rowHeaderWidth;
}
for (Iterator columnIterator = displayOrderedColumns.iterator(); columnIterator.hasNext(); ) {
if (x > getClientArea().width)
break;
GridColumn column = (GridColumn) columnIterator.next();
int height = 0;
if (!column.isVisible())
{
continue;
}
height = footerHeight;
y = getClientArea().height - height;
column.getFooterRenderer().setBounds(x, y, column.getWidth(), height);
if (x + column.getWidth() >= 0)
{
column.getFooterRenderer().paint(gc, column);
}
x += column.getWidth();
}
if (x < getClientArea().width)
{
emptyColumnFooterRenderer.setBounds(x, getClientArea().height - footerHeight, getClientArea().width - x, footerHeight);
emptyColumnFooterRenderer.paint(gc, null);
}
if (rowHeaderVisible)
{
// paint left corner
bottomLeftRenderer.setBounds(0, getClientArea().height-footerHeight, rowHeaderWidth, footerHeight);
bottomLeftRenderer.paint(gc, this);
x += rowHeaderWidth;
}
}
/**
* Manages the state of the scrollbars when new items are added or the
* bounds are changed.
*/
private void updateScrollbars()
{
Point preferredSize = getTableSize();
Rectangle clientArea = getClientArea();
// First, figure out if the scrollbars should be visible and turn them
// on right away
// this will allow the computations further down to accommodate the
// correct client
// area
// Turn the scrollbars on if necessary and do it all over again if
// necessary. This ensures
// that if a scrollbar is turned on/off, the other scrollbar's
// visibility may be affected (more
// area may have been added/removed.
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);
}
// get the clientArea again with the now visible/invisible
// scrollbars
clientArea = getClientArea();
}
// if the scrollbar is visible set its values
if (vScroll.getVisible())
{
int max = currentVisibleItems;
int thumb = 1;
if(!hasDifferingHeights)
{
// in this case, the number of visible rows on screen is constant,
// so use this as thumb
thumb = ( getVisibleGridHeight() + 1 ) / ( getItemHeight() + 1 );
}
else
{
// in this case, the number of visible rows on screen is variable,
// so we have to use 1 as thumb and decrease max by the number of
// rows on the last page
if(getVisibleGridHeight()>=1) {
RowRange range = getRowRange(-1,getVisibleGridHeight(),true,true);
max -= range.rows - 1;
}
}
// if possible, remember selection, if selection is too large, just
// make it the max you can
int selection = Math.min(vScroll.getSelection(), max);
vScroll.setValues(selection, 0, max, thumb, 1, thumb);
}
// if the scrollbar is visible set its values
if (hScroll.getVisible())
{
if (!columnScrolling)
{
// horizontal scrolling works pixel by pixel
int hiddenArea = preferredSize.x - clientArea.width + 1;
// if possible, remember selection, if selection is too large,
// just
// make it the max you can
int selection = Math.min(hScroll.getSelection(), hiddenArea - 1);
hScroll.setValues(selection, 0, hiddenArea + clientArea.width - 1, clientArea.width,
HORZ_SCROLL_INCREMENT, clientArea.width);
}
else
{
// horizontal scrolling is column by column
int hiddenArea = preferredSize.x - clientArea.width + 1;
int max = 0;
int i = 0;
while (hiddenArea > 0 && i < getColumnCount())
{
GridColumn col = (GridColumn)displayOrderedColumns.get(i);
i++;
if (col.isVisible())
{
hiddenArea -= col.getWidth();
max++;
}
}
max++;
// max should never be greater than the number of visible cols
int visCols = 0;
for (Iterator iter = columns.iterator(); iter.hasNext();)
{
GridColumn element = (GridColumn)iter.next();
if (element.isVisible())
{
visCols++;
}
}
max = Math.min(visCols, max);
// if possible, remember selection, if selection is too large,
// just
// make it the max you can
int selection = Math.min(hScroll.getSelection(), max);
hScroll.setValues(selection, 0, max, 1, 1, 1);
}
}
}
/**
* Adds/removes items from the selected items list based on the
* selection/deselection of the given item.
*
* @param item item being selected/unselected
* @param stateMask key state during selection
*
* @return selection event that needs to be fired or null
*/
private Event updateSelection(GridItem item, int stateMask)
{
if (!selectionEnabled)
{
return null;
}
Event selectionEvent = null;
if (selectionType == SWT.SINGLE)
{
if (selectedItems.contains(item))
{
// Deselect when pressing CTRL
if ((stateMask & SWT.MOD1) == SWT.MOD1)
{
selectedItems.clear();
}
}
else
{
selectedItems.clear();
selectedItems.add(item);
}
Rectangle clientArea = getClientArea();
redraw(clientArea.x,clientArea.y,clientArea.width,clientArea.height,false);
selectionEvent = new Event();
selectionEvent.item = item;
}
else if (selectionType == SWT.MULTI)
{
boolean shift = false;
boolean ctrl = false;
if ((stateMask & SWT.MOD2) == SWT.MOD2)
{
shift = true;
}
if ((stateMask & SWT.MOD1) == SWT.MOD1)
{
ctrl = true;
}
if (!shift && !ctrl)
{
if (selectedItems.size() == 1 && selectedItems.contains(item)) return null;
selectedItems.clear();
selectedItems.add(item);
Rectangle clientArea = getClientArea();
redraw(clientArea.x,clientArea.y,clientArea.width,clientArea.height,false);
shiftSelectionAnchorItem = null;
selectionEvent = new Event();
selectionEvent.item = item;
}
else if (shift)
{
if (shiftSelectionAnchorItem == null)
{
shiftSelectionAnchorItem = focusItem;
}
// if (shiftSelectionAnchorItem == item)
// {
// return;
// }
boolean maintainAnchorSelection = false;
if (!ctrl)
{
if (selectedItems.contains(shiftSelectionAnchorItem))
{
maintainAnchorSelection = true;
}
selectedItems.clear();
}
int anchorIndex = items.indexOf(shiftSelectionAnchorItem);
int itemIndex = items.indexOf(item);
int min = 0;
int max = 0;
if (anchorIndex < itemIndex)
{
if (maintainAnchorSelection)
{
min = anchorIndex;
}
else
{
min = anchorIndex + 1;
}
max = itemIndex;
}
else
{
if (maintainAnchorSelection)
{
max = anchorIndex;
}
else
{
max = anchorIndex - 1;
}
min = itemIndex;
}
for (int i = min; i <= max; i++)
{
if (!selectedItems.contains(items.get(i)) && ((GridItem)items.get(i)).isVisible())
{
selectedItems.add((GridItem)items.get(i));
}
}
Rectangle clientArea = getClientArea();
redraw(clientArea.x,clientArea.y,clientArea.width,clientArea.height,false);
selectionEvent = new Event();
}
else if (ctrl)
{
if (selectedItems.contains(item))
{
selectedItems.remove(item);
}
else
{
selectedItems.add(item);
}
Rectangle clientArea = getClientArea();
redraw(clientArea.x,clientArea.y,clientArea.width,clientArea.height,false);
shiftSelectionAnchorItem = null;
selectionEvent = new Event();
selectionEvent.item = item;
}
}
Rectangle clientArea = getClientArea();
redraw(clientArea.x,clientArea.y,clientArea.width,clientArea.height,false);
return selectionEvent;
}
/**
* Updates cell selection.
*
* @param newCell newly clicked, navigated to cell.
* @param stateMask statemask during preceeding mouse or key event.
* @param dragging true if the user is dragging.
* @param reverseDuplicateSelections true if the user is reversing selection rather than adding to.
*
* @return selection event that will need to be fired or null.
*/
private Event updateCellSelection(Point newCell, int stateMask, boolean dragging, boolean reverseDuplicateSelections)
{
Vector v = new Vector();
v.add(newCell);
return updateCellSelection(v, stateMask, dragging, reverseDuplicateSelections);
}
/**
* Updates cell selection.
*
* @param newCell newly clicked, navigated to cells.
* @param stateMask statemask during preceeding mouse or key event.
* @param dragging true if the user is dragging.
* @param reverseDuplicateSelections true if the user is reversing selection rather than adding to.
*
* @return selection event that will need to be fired or null.
*/
private Event updateCellSelection(Vector newCells, int stateMask, boolean dragging, boolean reverseDuplicateSelections)
{
boolean shift = false;
boolean ctrl = false;
if ((stateMask & SWT.MOD2) == SWT.MOD2)
{
shift = true;
}
else
{
shiftSelectionAnchorColumn = null;
shiftSelectionAnchorItem = null;
}
if ((stateMask & SWT.MOD1) == SWT.MOD1)
{
ctrl = true;
}
if (!shift && !ctrl)
{
if (newCells.equals(selectedCells)) return null;
selectedCells.clear();
for (int i = 0; i < newCells.size(); i++)
{
addToCellSelection((Point)newCells.get(i));
}
}
else if (shift)
{
Point newCell = (Point)newCells.get(0); //shift selection should only occur with one
//cell, ignoring others
if ((focusColumn == null) || (focusItem == null))
{
return null;
}
shiftSelectionAnchorColumn = getColumn(newCell.x);
shiftSelectionAnchorItem = getItem(newCell.y);
if (ctrl)
{
selectedCells.clear();
selectedCells.addAll(selectedCellsBeforeRangeSelect);
}
else
{
selectedCells.clear();
}
GridColumn currentColumn = focusColumn;
GridItem currentItem = focusItem;
GridColumn endColumn = getColumn(newCell.x);
GridItem endItem = getItem(newCell.y);
Point newRange = getSelectionRange(currentItem,currentColumn,endItem,endColumn);
currentColumn = getColumn(newRange.x);
endColumn = getColumn(newRange.y);
GridColumn startCol = currentColumn;
if (indexOf(currentItem) > indexOf(endItem))
{
GridItem temp = currentItem;
currentItem = endItem;
endItem = temp;
}
boolean firstLoop = true;
do
{
if (!firstLoop)
{
currentItem = getNextVisibleItem(currentItem);
}
firstLoop = false;
boolean firstLoop2 = true;
currentColumn = startCol;
do
{
if (!firstLoop2)
{
int index = displayOrderedColumns.indexOf(currentColumn) + 1;
if (index < displayOrderedColumns.size())
{
currentColumn = getVisibleColumn_DegradeRight(currentItem,(GridColumn)displayOrderedColumns.get(index));
}
else
{
currentColumn = null;
}
if (currentColumn!= null)
if (displayOrderedColumns.indexOf(currentColumn) > displayOrderedColumns.indexOf(endColumn))
currentColumn = null;
}
firstLoop2 = false;
if (currentColumn != null)
{
Point cell = new Point(indexOf(currentColumn),indexOf(currentItem));
addToCellSelection(cell);
}
} while (currentColumn != endColumn && currentColumn != null);
} while (currentItem != endItem);
}
else if (ctrl)
{
boolean reverse = reverseDuplicateSelections;
if (!selectedCells.containsAll(newCells))
reverse = false;
if (dragging)
{
selectedCells.clear();
selectedCells.addAll(selectedCellsBeforeRangeSelect);
}
if (reverse)
{
selectedCells.removeAll(newCells);
}
else
{
for (int i = 0; i < newCells.size(); i++)
{
addToCellSelection((Point)newCells.get(i));
}
}
}
updateColumnSelection();
Event e = new Event();
if (dragging)
{
e.detail = SWT.DRAG;
followupCellSelectionEventOwed = true;
}
Rectangle clientArea = getClientArea();
redraw(clientArea.x,clientArea.y,clientArea.width,clientArea.height,false);
return e;
}
private void addToCellSelection(Point newCell)
{
if (newCell.x < 0 || newCell.x >= columns.size())
return;
if (newCell.y < 0 || newCell.y >= items.size())
return;
if (getColumn(newCell.x).getCellSelectionEnabled())
{
Iterator it = selectedCells.iterator();
boolean found = false;
while( it.hasNext() ) {
Point p = (Point) it.next();
if( newCell.equals(p) ) {
found = true;
break;
}
}
if( ! found ) {
selectedCells.add(newCell);
}
}
}
void updateColumnSelection()
{
//Update the list of which columns have all their cells selected
selectedColumns.clear();
for (Iterator iter = selectedCells.iterator(); iter.hasNext();)
{
Point cell = (Point)iter.next();
GridColumn col = getColumn(cell.x);
selectedColumns.add(col);
}
}
/**
* Initialize all listeners.
*/
private void initListeners()
{
disposeListener = new Listener()
{
public void handleEvent(Event e)
{
onDispose(e);
}
};
addListener(SWT.Dispose,disposeListener);
addPaintListener(new PaintListener()
{
public void paintControl(PaintEvent e)
{
onPaint(e);
}
});
addListener(SWT.Resize, new Listener()
{
public void handleEvent(Event e)
{
onResize();
}
});
if (getVerticalBar() != null)
{
getVerticalBar().addListener(SWT.Selection, new Listener()
{
public void handleEvent(Event e)
{
onScrollSelection();
}
});
}
if (getHorizontalBar() != null)
{
getHorizontalBar().addListener(SWT.Selection, new Listener()
{
public void handleEvent(Event e)
{
onScrollSelection();
}
});
}
addListener(SWT.KeyDown, new Listener()
{
public void handleEvent(Event e)
{
onKeyDown(e);
}
});
addTraverseListener(new TraverseListener()
{
public void keyTraversed(TraverseEvent e)
{
e.doit = true;
}
});
addMouseListener(new MouseListener()
{
public void mouseDoubleClick(MouseEvent e)
{
onMouseDoubleClick(e);
}
public void mouseDown(MouseEvent e)
{
onMouseDown(e);
}
public void mouseUp(MouseEvent e)
{
onMouseUp(e);
}
});
addMouseMoveListener(new MouseMoveListener()
{
public void mouseMove(MouseEvent e)
{
onMouseMove(e);
}
});
addMouseTrackListener(new MouseTrackListener()
{
public void mouseEnter(MouseEvent e)
{
}
public void mouseExit(MouseEvent e)
{
onMouseExit(e);
}
public void mouseHover(MouseEvent e)
{
}
});
addFocusListener(new FocusListener()
{
public void focusGained(FocusEvent e)
{
onFocusIn();
redraw();
}
public void focusLost(FocusEvent e)
{
redraw();
}
});
// Special code to reflect mouse wheel events if using an external
// scroller
addListener(SWT.MouseWheel, new Listener()
{
public void handleEvent(Event e)
{
onMouseWheel(e);
}
});
}
private void onFocusIn()
{
if (!items.isEmpty() && focusItem == null)
{
focusItem = (GridItem) items.get(0);
}
}
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.Dispose, disposeListener);
notifyListeners(SWT.Dispose, event);
event.type = SWT.None;
disposing = true;
cellHeaderSelectionBackground.dispose();
for (Iterator iterator = items.iterator(); iterator.hasNext();)
{
GridItem item = (GridItem)iterator.next();
item.dispose();
}
for (int i = 0; i < columnGroups.length; i++)
{
columnGroups[i].dispose();
}
for (Iterator iterator = columns.iterator(); iterator.hasNext();)
{
GridColumn col = (GridColumn)iterator.next();
col.dispose();
}
sizingGC.dispose();
}
/**
* Mouse wheel event handler.
*
* @param e event
*/
private void onMouseWheel(Event e)
{
if (vScroll.getVisible())
{
vScroll.handleMouseWheel(e);
if (getVerticalBar() == null)
e.doit = false;
}
else if (hScroll.getVisible())
{
hScroll.handleMouseWheel(e);
if (getHorizontalBar() == null)
e.doit = false;
}
}
/**
* Mouse down event handler.
*
* @param e event
*/
private void onMouseDown(MouseEvent e)
{
// for some reason, SWT prefers the children to get focus if
// there are any children
// the setFocus method on Composite will not set focus to the
// Composite if one of its
// children can get focus instead. This only affects the table
// when an editor is open
// and therefore the table has a child. The solution is to
// forceFocus()
if ((getStyle() & SWT.NO_FOCUS) != SWT.NO_FOCUS)
{
forceFocus();
}
hideToolTip();
//if populated will be fired at end of method.
Event selectionEvent = null;
cellSelectedOnLastMouseDown = false;
cellRowSelectedOnLastMouseDown = false;
cellColumnSelectedOnLastMouseDown = false;
if (hoveringOnColumnResizer)
{
if (e.button == 1)
{
resizingColumn = true;
resizingStartX = e.x;
resizingColumnStartWidth = columnBeingResized.getWidth();
}
return;
}
if (rowsResizeable && hoveringOnRowResizer)
{
if (e.button == 1)
{
resizingRow = true;
resizingStartY = e.y;
resizingRowStartHeight = rowBeingResized.getHeight();
}
return;
}
if (e.button == 1 && handleColumnHeaderPush(e.x, e.y))
{
return;
}
if (e.button == 1 && handleColumnGroupHeaderClick(e.x, e.y))
{
return;
}
if(e.button == 1 && handleColumnFooterPush(e.x,e.y)) {
return;
}
GridItem item = getItem(new Point(e.x, e.y));
if (e.button == 1 && item != null && handleCellClick(item,e.x, e.y))
{
return;
}
if (isListening(SWT.DragDetect))
{
if ((cellSelectionEnabled && hoveringOnSelectionDragArea) ||
(!cellSelectionEnabled && item != null && selectedItems.contains(item)))
{
if(dragDetect(e))
{
return;
}
}
}
if (item != null)
{
if (cellSelectionEnabled)
{
GridColumn col = getColumn(new Point(e.x, e.y));
boolean isSelectedCell = false;
if (col != null)
isSelectedCell = selectedCells.contains(new Point(indexOf(col),indexOf(item)));
if (e.button == 1 || (e.button == 3 && col != null && !isSelectedCell))
{
if (col != null)
{
selectionEvent = updateCellSelection(new Point(indexOf(col),indexOf(item)), e.stateMask, false, true);
cellSelectedOnLastMouseDown = (getCellSelectionCount() > 0);
if (e.stateMask != SWT.MOD2)
{
focusColumn = col;
focusItem = item;
}
//showColumn(col);
showItem(item);
redraw();
}
else if (rowHeaderVisible)
{
if (e.x <= rowHeaderWidth)
{
boolean shift = ((e.stateMask & SWT.MOD2) != 0);
boolean ctrl = false;
if (!shift)
{
ctrl = ((e.stateMask & SWT.MOD1) != 0);
}
Vector cells = new Vector();
if (shift)
{
getCells(item,focusItem,cells);
}
else
{
getCells(item,cells);
}
int newStateMask = SWT.NONE;
if (ctrl) newStateMask = SWT.MOD1;
selectionEvent = updateCellSelection(cells, newStateMask, shift, ctrl);
cellRowSelectedOnLastMouseDown = (getCellSelectionCount() > 0);
if (!shift)
{
//set focus back to the first visible column
focusColumn = getColumn(new Point(rowHeaderWidth + 1,e.y));
focusItem = item;
}
showItem(item);
redraw();
}
}
intendedFocusColumn = focusColumn;
}
}
else
{
if (e.button == 2 || e.button > 3)
{
return;
}
if (e.button == 3 && selectionType == SWT.MULTI)
{
if ((e.stateMask & SWT.MOD2) == SWT.MOD2)
{
return;
}
if ((e.stateMask & SWT.MOD1) == SWT.MOD1)
{
return;
}
if (selectedItems.contains(item))
{
return;
}
}
selectionEvent = updateSelection(item, e.stateMask);
focusItem = item;
showItem(item);
redraw();
}
}
else if (e.button == 1 && rowHeaderVisible && e.x <= rowHeaderWidth && e.y < headerHeight)
{
// Nothing to select
if(items.size() == 0) {
return;
}
if (cellSelectionEnabled)
{
//click on the top left corner means select everything
selectionEvent = selectAllCellsInternal();
focusColumn = getColumn(new Point(rowHeaderWidth + 1,1));
}
else
{
//click on the top left corner means select everything
selectionEvent = selectAllRowsInternal();
}
focusItem = getItem(getTopIndex());
}
else if (cellSelectionEnabled && e.button == 1 && columnHeadersVisible && e.y <= headerHeight)
{
//column cell selection
GridColumn col = getColumn(new Point(e.x,e.y));
if (col == null) return;
if (getItemCount() == 0)
return;
Vector cells = new Vector();
GridColumnGroup group = col.getColumnGroup();
if (group != null && e.y < groupHeaderHeight) {
getCells(group, cells);
} else {
getCells(col, cells);
}
selectionEvent = updateCellSelection(cells, e.stateMask, false, true);
cellColumnSelectedOnLastMouseDown = (getCellSelectionCount() > 0);
GridItem newFocusItem = getItem(0);
while (newFocusItem != null && getSpanningColumn(newFocusItem, col) != null)
{
newFocusItem = getNextVisibleItem(newFocusItem);
}
if (newFocusItem != null)
{
focusColumn = col;
focusItem = newFocusItem;
}
showColumn(col);
redraw();
}
if (selectionEvent != null)
{
selectionEvent.stateMask = e.stateMask;
selectionEvent.button = e.button;
selectionEvent.item = item;
selectionEvent.x = e.x;
selectionEvent.y = e.y;
notifyListeners(SWT.Selection, selectionEvent);
if (!cellSelectionEnabled)
{
if (isListening(SWT.DragDetect))
{
dragDetect(e);
}
}
}
}
/**
* Mouse double click event handler.
*
* @param e event
*/
private void onMouseDoubleClick(MouseEvent e)
{
if (e.button == 1)
{
if (hoveringOnColumnResizer)
{
columnBeingResized.pack();
columnBeingResized.fireResized();
for (int index = displayOrderedColumns.indexOf(columnBeingResized) + 1; index < displayOrderedColumns.size(); index ++)
{
GridColumn col = (GridColumn)displayOrderedColumns.get(index);
if (col.isVisible()) col.fireMoved();
}
resizingColumn = false;
handleHoverOnColumnResizer(e.x, e.y);
return;
}
else if (rowsResizeable && hoveringOnRowResizer) {
List sel = Arrays.asList(getSelection());
if(sel.contains(rowBeingResized))
{
// the user double-clicked a row resizer of a selected row
// so update all selected rows
for(int cnt=0;cnt<sel.size();cnt++)
((GridItem)sel.get(cnt)).pack();
redraw();
}
else
{
// otherwise only update the row the user double-clicked
rowBeingResized.pack();
}
resizingRow = false;
handleHoverOnRowResizer(e.x, e.y);
return;
}
GridItem item = getItem(new Point(e.x, e.y));
if (item != null)
{
if (isListening(SWT.DefaultSelection))
{
Event newEvent = new Event();
newEvent.item = item;
notifyListeners(SWT.DefaultSelection, newEvent);
}
else if (item.getItemCount() > 0)
{
item.setExpanded(!item.isExpanded());
if (item.isExpanded())
{
item.fireEvent(SWT.Expand);
}
else
{
item.fireEvent(SWT.Collapse);
}
}
}
}
}
/**
* Mouse up handler.
*
* @param e event
*/
private void onMouseUp(MouseEvent e)
{
cellSelectedOnLastMouseDown = false;
if (resizingColumn)
{
resizingColumn = false;
handleHoverOnColumnResizer(e.x, e.y); // resets cursor if
// necessary
return;
}
if (resizingRow)
{
resizingRow = false;
handleHoverOnRowResizer(e.x, e.y); // resets cursor if
// necessary
return;
}
if (pushingColumn)
{
pushingColumn = false;
columnBeingPushed.getHeaderRenderer().setMouseDown(false);
columnBeingPushed.getHeaderRenderer().setHover(false);
redraw();
if (pushingAndHovering)
{
columnBeingPushed.fireListeners();
}
setCapture(false);
return;
}
if (draggingColumn)
{
handleColumnDrop();
return;
}
if (cellDragSelectionOccuring || cellRowDragSelectionOccuring || cellColumnDragSelectionOccuring)
{
cellDragSelectionOccuring = false;
cellRowDragSelectionOccuring = false;
cellColumnDragSelectionOccuring = false;
setCursor(null);
if (followupCellSelectionEventOwed)
{
Event se = new Event();
se.button = e.button;
se.item = getItem(new Point(e.x, e.y));
se.stateMask = e.stateMask;
se.x = e.x;
se.y = e.y;
notifyListeners(SWT.Selection, se);
followupCellSelectionEventOwed = false;
}
}
}
/**
* Mouse move event handler.
*
* @param e event
*/
private void onMouseMove(MouseEvent e)
{
//check to see if the mouse is outside the grid
//this should only happen when the mouse is captured for inplace
//tooltips - see bug 203364
if (inplaceTooltipCapture && (e.x < 0 || e.y < 0 || e.x >= getBounds().width || e.y >= getBounds().height))
{
setCapture(false);
inplaceTooltipCapture = false;
return; //a mouseexit event should occur immediately
}
//if populated will be fired at end of method.
Event selectionEvent = null;
if ((e.stateMask & SWT.BUTTON1) == 0)
{
handleHovering(e.x, e.y);
}
else
{
if (draggingColumn)
{
handleColumnDragging(e.x);
return;
}
if (resizingColumn)
{
handleColumnResizerDragging(e.x);
return;
}
if (resizingRow)
{
handleRowResizerDragging(e.y);
return;
}
if (pushingColumn)
{
handleColumnHeaderHoverWhilePushing(e.x, e.y);
return;
}
if (cellSelectionEnabled)
{
if (!cellDragSelectionOccuring && cellSelectedOnLastMouseDown)
{
cellDragSelectionOccuring = true;
//XXX: make this user definable
setCursor(getDisplay().getSystemCursor(SWT.CURSOR_CROSS));
cellDragCTRL = ((e.stateMask & SWT.MOD1) != 0);
if (cellDragCTRL)
{
selectedCellsBeforeRangeSelect.clear();
selectedCellsBeforeRangeSelect.addAll(selectedCells);
}
}
if (!cellRowDragSelectionOccuring && cellRowSelectedOnLastMouseDown)
{
cellRowDragSelectionOccuring = true;
setCursor(getDisplay().getSystemCursor(SWT.CURSOR_CROSS));
cellDragCTRL = ((e.stateMask & SWT.MOD1) != 0);
if (cellDragCTRL)
{
selectedCellsBeforeRangeSelect.clear();
selectedCellsBeforeRangeSelect.addAll(selectedCells);
}
}
if (!cellColumnDragSelectionOccuring && cellColumnSelectedOnLastMouseDown)
{
cellColumnDragSelectionOccuring = true;
setCursor(getDisplay().getSystemCursor(SWT.CURSOR_CROSS));
cellDragCTRL = ((e.stateMask & SWT.MOD1) != 0);
if (cellDragCTRL)
{
selectedCellsBeforeRangeSelect.clear();
selectedCellsBeforeRangeSelect.addAll(selectedCells);
}
}
int ctrlFlag = (cellDragCTRL ? SWT.MOD1 : SWT.NONE);
if (cellDragSelectionOccuring && handleCellHover(e.x, e.y))
{
GridColumn intentColumn = hoveringColumn;
GridItem intentItem = hoveringItem;
if (hoveringItem == null)
{
if (e.y > headerHeight)
{
//then we must be hovering way to the bottom
intentItem = getPreviousVisibleItem(null);
}
else
{
intentItem = (GridItem)items.get(0);
}
}
if (hoveringColumn == null)
{
if (e.x > rowHeaderWidth)
{
//then we must be hovering way to the right
intentColumn = getVisibleColumn_DegradeLeft(intentItem,(GridColumn)displayOrderedColumns.get(displayOrderedColumns.size() - 1));
}
else
{
GridColumn firstCol = (GridColumn)displayOrderedColumns.get(0);
if (!firstCol.isVisible())
{
firstCol = getNextVisibleColumn(firstCol);
}
intentColumn = firstCol;
}
}
showColumn(intentColumn);
showItem(intentItem);
selectionEvent = updateCellSelection(new Point(indexOf(intentColumn),indexOf(intentItem)),ctrlFlag | SWT.MOD2, true, false);
}
if (cellRowDragSelectionOccuring && handleCellHover(e.x, e.y))
{
GridItem intentItem = hoveringItem;
if (hoveringItem == null)
{
if (e.y > headerHeight)
{
//then we must be hovering way to the bottom
intentItem = getPreviousVisibleItem(null);
}
else
{
if (getTopIndex() > 0)
{
intentItem = getPreviousVisibleItem((GridItem)items.get(getTopIndex()));
}
else
{
intentItem = (GridItem)items.get(0);
}
}
}
Vector cells = new Vector();
getCells(intentItem,focusItem,cells);
showItem(intentItem);
selectionEvent = updateCellSelection(cells,ctrlFlag, true, false);
}
if (cellColumnDragSelectionOccuring && handleCellHover(e.x, e.y))
{
GridColumn intentCol = hoveringColumn;
if (intentCol == null)
{
if (e.y < rowHeaderWidth)
{
//TODO: get the first col to the left
}
else
{
//TODO: get the first col to the right
}
}
if (intentCol == null) return; //temporary
GridColumn iterCol = intentCol;
Vector newSelected = new Vector();
boolean decreasing = (displayOrderedColumns.indexOf(iterCol) > displayOrderedColumns.indexOf(focusColumn));
do
{
getCells(iterCol, newSelected);
if (iterCol == focusColumn)
{
break;
}
if (decreasing)
{
iterCol = getPreviousVisibleColumn(iterCol);
}
else
{
iterCol = getNextVisibleColumn(iterCol);
}
} while (true);
selectionEvent = updateCellSelection(newSelected, ctrlFlag, true, false);
}
}
}
if (selectionEvent != null)
{
selectionEvent.stateMask = e.stateMask;
selectionEvent.button = e.button;
selectionEvent.item = getItem(new Point(e.x, e.y));
selectionEvent.x = e.x;
selectionEvent.y = e.y;
notifyListeners(SWT.Selection, selectionEvent);
}
}
/**
* Handles the assignment of the correct values to the hover* field
* variables that let the painting code now what to paint as hovered.
*
* @param x mouse x coordinate
* @param y mouse y coordinate
*/
private void handleHovering(int x, int y)
{
// TODO: need to clean up and refactor hover code
handleCellHover(x, y);
// Is this Grid a DragSource ??
if (cellSelectionEnabled && getData("DragSource") != null) {
if (handleHoverOnSelectionDragArea(x, y))
{
return;
}
}
if (columnHeadersVisible)
{
if (handleHoverOnColumnResizer(x, y))
{
// if (hoveringItem != null || !hoveringDetail.equals("") || hoveringColumn != null
// || hoveringColumnHeader != null || hoverColumnGroupHeader != null)
// {
// hoveringItem = null;
// hoveringDetail = "";
// hoveringColumn = null;
// hoveringColumnHeader = null;
// hoverColumnGroupHeader = null;
//
// Rectangle clientArea = getClientArea();
// redraw(clientArea.x,clientArea.y,clientArea.width,clientArea.height,false);
// }
return;
}
}
if (rowsResizeable && rowHeaderVisible)
{
if (handleHoverOnRowResizer(x, y))
{
return;
}
}
// handleCellHover(x, y);
}
/**
* Refreshes the hover* variables according to the mouse location and
* current state of the table. This is useful is some method call, caused
* the state of the table to change and therefore the hover effects may have
* become out of date.
*/
protected void refreshHoverState()
{
Point p = getDisplay().map(null, this, getDisplay().getCursorLocation());
handleHovering(p.x, p.y);
}
/**
* Mouse exit event handler.
*
* @param e event
*/
private void onMouseExit(MouseEvent e)
{
hoveringItem = null;
hoveringDetail = "";
hoveringColumn = null;
hoveringOverText = false;
hideToolTip();
redraw();
}
/**
* Key down event handler.
*
* @param e event
*/
private void onKeyDown(Event e)
{
if (focusColumn == null || focusColumn.isDisposed())
{
if (columns.size() == 0)
return;
focusColumn = getColumn(0);
intendedFocusColumn = focusColumn;
}
if (e.character == '\r' && focusItem != null)
{
Event newEvent = new Event();
newEvent.item = focusItem;
notifyListeners(SWT.DefaultSelection, newEvent);
return;
}
int attemptExpandCollapse = 0;
if ((e.character == '-' || (!cellSelectionEnabled && e.keyCode == SWT.ARROW_LEFT)) && focusItem != null && focusItem.isExpanded())
{
attemptExpandCollapse = SWT.Collapse;
}
else if ((e.character == '+' || (!cellSelectionEnabled && e.keyCode == SWT.ARROW_RIGHT)) && focusItem != null && !focusItem.isExpanded())
{
attemptExpandCollapse = SWT.Expand;
}
if (attemptExpandCollapse != 0 && focusItem != null && focusItem.hasChildren())
{
int performExpandCollapse = 0;
if (cellSelectionEnabled && focusColumn != null && focusColumn.isTree())
{
performExpandCollapse = attemptExpandCollapse;
}
else if (!cellSelectionEnabled)
{
performExpandCollapse = attemptExpandCollapse;
}
if (performExpandCollapse == SWT.Expand)
{
focusItem.setExpanded(true);
focusItem.fireEvent(SWT.Expand);
return;
}
if (performExpandCollapse == SWT.Collapse)
{
focusItem.setExpanded(false);
focusItem.fireEvent(SWT.Collapse);
return;
}
}
if (e.character == ' ')
{
handleSpaceBarDown(e);
}
GridItem newSelection = null;
GridColumn newColumnFocus = null;
//These two variables are used because the key navigation when the shift key is down is
//based, not off the focus item/column, but rather off the implied focus (i.e. where the
//keyboard has extended focus to).
GridItem impliedFocusItem = focusItem;
GridColumn impliedFocusColumn = focusColumn;
if (cellSelectionEnabled && e.stateMask == SWT.MOD2)
{
if (shiftSelectionAnchorColumn != null)
{
impliedFocusItem = shiftSelectionAnchorItem;
impliedFocusColumn = shiftSelectionAnchorColumn;
}
}
switch (e.keyCode)
{
case SWT.ARROW_RIGHT :
if (cellSelectionEnabled)
{
if (impliedFocusItem != null && impliedFocusColumn != null)
{
newSelection = impliedFocusItem;
int index = displayOrderedColumns.indexOf(impliedFocusColumn);
int jumpAhead = impliedFocusItem.getColumnSpan(indexOf(impliedFocusColumn));
jumpAhead ++;
while (jumpAhead > 0)
{
index ++;
if (index < displayOrderedColumns.size())
{
if (((GridColumn)displayOrderedColumns.get(index)).isVisible())
jumpAhead --;
}
else
{
break;
}
}
if (index < displayOrderedColumns.size())
{
newColumnFocus = (GridColumn)displayOrderedColumns.get(index);
}
else
{
newColumnFocus = impliedFocusColumn;
}
}
intendedFocusColumn = newColumnFocus;
}
else
{
if (impliedFocusItem != null && impliedFocusItem.hasChildren())
{
newSelection = impliedFocusItem.getItem(0);
}
}
break;
case SWT.ARROW_LEFT :
if (cellSelectionEnabled)
{
if (impliedFocusItem != null && impliedFocusColumn != null)
{
newSelection = impliedFocusItem;
int index = displayOrderedColumns.indexOf(impliedFocusColumn);
if (index != 0)
{
newColumnFocus = (GridColumn)displayOrderedColumns.get(index -1);
newColumnFocus = getVisibleColumn_DegradeLeft(impliedFocusItem, newColumnFocus);
}
else
{
newColumnFocus = impliedFocusColumn;
}
}
intendedFocusColumn = newColumnFocus;
}
else
{
if (impliedFocusItem != null && impliedFocusItem.getParentItem() != null)
{
newSelection = impliedFocusItem.getParentItem();
}
}
break;
case SWT.ARROW_UP :
if (impliedFocusItem != null)
{
newSelection = getPreviousVisibleItem(impliedFocusItem);
}
if (impliedFocusColumn != null)
{
if (newSelection != null)
{
newColumnFocus = getVisibleColumn_DegradeLeft(newSelection, intendedFocusColumn);
}
else
{
newColumnFocus = impliedFocusColumn;
}
}
break;
case SWT.ARROW_DOWN :
if (impliedFocusItem != null)
{
newSelection = getNextVisibleItem(impliedFocusItem);
}
else
{
if (items.size() > 0)
{
newSelection = (GridItem)items.get(0);
}
}
if (impliedFocusColumn != null)
{
if (newSelection != null)
{
newColumnFocus = getVisibleColumn_DegradeLeft(newSelection, intendedFocusColumn);
}
else
{
newColumnFocus = impliedFocusColumn;
}
}
break;
case SWT.HOME :
if (!cellSelectionEnabled)
{
if (items.size() > 0)
{
newSelection = (GridItem)items.get(0);
}
}
else
{
newSelection = impliedFocusItem;
newColumnFocus = getVisibleColumn_DegradeRight(newSelection,(GridColumn)displayOrderedColumns.get(0));
}
break;
case SWT.END :
if (!cellSelectionEnabled)
{
if (items.size() > 0)
{
newSelection = getPreviousVisibleItem(null);
}
}
else
{
newSelection = impliedFocusItem;
newColumnFocus = getVisibleColumn_DegradeLeft(newSelection,(GridColumn)displayOrderedColumns.get(displayOrderedColumns.size() - 1));
}
break;
case SWT.PAGE_UP :
int topIndex = getTopIndex();
newSelection = (GridItem)items.get(topIndex);
if (focusItem == newSelection)
{
RowRange range = getRowRange(getTopIndex(),getVisibleGridHeight(),false,true);
newSelection = (GridItem)items.get(range.startIndex);
}
newColumnFocus = focusColumn;
break;
case SWT.PAGE_DOWN :
int bottomIndex = getBottomIndex();
newSelection = (GridItem)items.get(bottomIndex);
if(!isShown(newSelection))
{
// the item at bottom index is not shown completely
GridItem tmpItem = getPreviousVisibleItem(newSelection);
if(tmpItem!=null)
newSelection = tmpItem;
}
if (focusItem == newSelection)
{
RowRange range = getRowRange(getBottomIndex(),getVisibleGridHeight(),true,false);
newSelection = (GridItem)items.get(range.endIndex);
}
newColumnFocus = focusColumn;
break;
default :
break;
}
if (newSelection == null)
{
return;
}
if (cellSelectionEnabled)
{
if (e.stateMask != SWT.MOD2)
focusColumn = newColumnFocus;
showColumn(newColumnFocus);
if (e.stateMask != SWT.MOD2)
focusItem = newSelection;
showItem(newSelection);
if (e.stateMask != SWT.MOD1)
{
Event selEvent = updateCellSelection(new Point(indexOf(newColumnFocus),indexOf(newSelection)),e.stateMask, false, false);
if (selEvent != null)
{
selEvent.stateMask = e.stateMask;
selEvent.character = e.character;
selEvent.keyCode = e.keyCode;
notifyListeners(SWT.Selection, selEvent);
}
}
redraw();
}
else
{
Event selectionEvent = null;
if (selectionType == SWT.SINGLE || e.stateMask != SWT.MOD1)
{
selectionEvent = updateSelection(newSelection, e.stateMask);
if (selectionEvent != null)
{
selectionEvent.stateMask = e.stateMask;
selectionEvent.character = e.character;
selectionEvent.keyCode = e.keyCode;
}
}
focusItem = newSelection;
showItem(newSelection);
redraw();
if (selectionEvent != null)
notifyListeners(SWT.Selection, selectionEvent);
}
}
private void handleSpaceBarDown(Event event)
{
if (focusItem == null)
return;
if (selectionEnabled && !cellSelectionEnabled && !selectedItems.contains(focusItem))
{
selectedItems.add(focusItem);
redraw();
Event e = new Event();
e.item = focusItem;
e.stateMask = event.stateMask;
e.character = event.character;
e.keyCode = event.keyCode;
notifyListeners(SWT.Selection, e);
}
if (!cellSelectionEnabled)
{
boolean checkFirstCol = false;
boolean first = true;
for (Iterator iter = columns.iterator(); iter.hasNext();)
{
GridColumn col = (GridColumn)iter.next();
if (first)
{
if (!col.isCheck()) break;
first = false;
checkFirstCol = true;
}
else
{
if (col.isCheck())
{
checkFirstCol = false;
break;
}
}
}
if (checkFirstCol)
{
focusItem.setChecked(!focusItem.getChecked());
redraw();
focusItem.fireCheckEvent(0);
}
}
}
/**
* Resize event handler.
*/
private void onResize()
{
//CGross 1/2/08 - I don't really want to be doing this....
//I shouldn't be changing something you user configured...
//leaving out for now
// if (columnScrolling)
// {
// int maxWidth = getClientArea().width;
// if (rowHeaderVisible)
// maxWidth -= rowHeaderWidth;
//
// for (Iterator cols = columns.iterator(); cols.hasNext();) {
// GridColumn col = (GridColumn) cols.next();
// if (col.getWidth() > maxWidth)
// col.setWidth(maxWidth);
// }
// }
scrollValuesObsolete = true;
topIndex = -1;
bottomIndex = -1;
}
/**
* Scrollbar selection event handler.
*/
private void onScrollSelection()
{
topIndex = -1;
bottomIndex = -1;
refreshHoverState();
redraw(getClientArea().x, getClientArea().y, getClientArea().width, getClientArea().height,
false);
}
/**
* Returns the intersection of the given column and given item.
*
* @param column column
* @param item item
* @return x,y of top left corner of the cell
*/
Point getOrigin(GridColumn column, GridItem item)
{
int x = 0;
if (rowHeaderVisible)
{
x += rowHeaderWidth;
}
x -= getHScrollSelectionInPixels();
for (Iterator colIterIterator = displayOrderedColumns.iterator(); colIterIterator.hasNext(); )
{
GridColumn colIter = (GridColumn) colIterIterator.next();
if (colIter == column)
{
break;
}
if (colIter.isVisible())
{
x += colIter.getWidth();
}
}
int y = 0;
if (item != null)
{
if (columnHeadersVisible)
{
y += headerHeight;
}
int currIndex=getTopIndex();
int itemIndex=items.indexOf(item);
if (itemIndex == -1)
{
SWT.error(SWT.ERROR_INVALID_ARGUMENT);
}
while(currIndex!=itemIndex)
{
if(currIndex<itemIndex)
{
GridItem currItem = (GridItem)items.get(currIndex);
if(currItem.isVisible())
{
y += currItem.getHeight() + 1;
}
currIndex++;
}
else if(currIndex>itemIndex)
{
currIndex--;
GridItem currItem = (GridItem)items.get(currIndex);
if(currItem.isVisible())
{
y -= currItem.getHeight() + 1;
}
}
}
}
else
{
if (column.getColumnGroup() != null)
{
y += groupHeaderHeight;
}
}
return new Point(x, y);
}
/**
* Determines (which cell/if a cell) has been clicked (mouse down really)
* and notifies the appropriate renderer. Returns true when a cell has
* responded to this event in some way and prevents the event from
* triggering an action further down the chain (like a selection).
*
* @param item item clicked
* @param x mouse x
* @param y mouse y
* @return true if this event has been consumed.
*/
private boolean handleCellClick(GridItem item, int x, int y)
{
// if(!isTree)
// return false;
GridColumn col = getColumn(new Point(x, y));
if (col == null)
{
return false;
}
col.getCellRenderer().setBounds(item.getBounds(indexOf(col)));
return col.getCellRenderer().notify(IInternalWidget.LeftMouseButtonDown, new Point(x, y), item);
}
/**
* Sets the hovering variables (hoverItem,hoveringColumn) as well as
* hoverDetail by talking to the cell renderers. Triggers a redraw if
* necessary.
*
* @param x mouse x
* @param y mouse y
* @return true if a new section of the table is now being hovered
*/
private boolean handleCellHover(int x, int y)
{
String detail = "";
boolean overText = false;
final GridColumn col = getColumn(new Point(x, y));
final GridItem item = getItem(new Point(x, y));
GridColumnGroup hoverColGroup = null;
GridColumn hoverColHeader = null;
if (col != null)
{
if (item != null)
{
if( y < getClientArea().height - footerHeight ) {
col.getCellRenderer().setBounds(item.getBounds(columns.indexOf(col)));
if (col.getCellRenderer().notify(IInternalWidget.MouseMove, new Point(x, y), item))
{
detail = col.getCellRenderer().getHoverDetail();
}
Rectangle textBounds = col.getCellRenderer().getTextBounds(item,false);
if (textBounds != null)
{
Point p = new Point(x - col.getCellRenderer().getBounds().x, y - col.getCellRenderer().getBounds().y);
overText = textBounds.contains(p);
}
}
}
else
{
if (y < headerHeight)
{
if (columnGroups.length != 0 && y < groupHeaderHeight
&& col.getColumnGroup() != null)
{
hoverColGroup = col.getColumnGroup();
hoverColGroup.getHeaderRenderer().setBounds(hoverColGroup.getBounds());
if (hoverColGroup.getHeaderRenderer()
.notify(IInternalWidget.MouseMove, new Point(x, y), hoverColGroup))
{
detail = hoverColGroup.getHeaderRenderer().getHoverDetail();
}
Rectangle textBounds = hoverColGroup.getHeaderRenderer().getTextBounds(hoverColGroup,false);
if (textBounds != null)
{
Point p = new Point(x - hoverColGroup.getHeaderRenderer().getBounds().x, y - hoverColGroup.getHeaderRenderer().getBounds().y);
overText = textBounds.contains(p);
}
}
else
{
// on col header
hoverColHeader = col;
col.getHeaderRenderer().setBounds(col.getBounds());
if (col.getHeaderRenderer().notify(IInternalWidget.MouseMove, new Point(x, y),
col))
{
detail = col.getHeaderRenderer().getHoverDetail();
}
Rectangle textBounds = col.getHeaderRenderer().getTextBounds(col,false);
if (textBounds != null)
{
Point p = new Point(x - col.getHeaderRenderer().getBounds().x, y - col.getHeaderRenderer().getBounds().y);
overText = textBounds.contains(p);
}
}
}
}
}
boolean hoverChange = false;
if (hoveringItem != item || !hoveringDetail.equals(detail) || hoveringColumn != col
|| hoverColGroup != hoverColumnGroupHeader || hoverColHeader != hoveringColumnHeader)
{
hoveringItem = item;
hoveringDetail = detail;
hoveringColumn = col;
hoveringColumnHeader = hoverColHeader;
hoverColumnGroupHeader = hoverColGroup;
Rectangle clientArea = getClientArea();
redraw(clientArea.x,clientArea.y,clientArea.width,clientArea.height,false);
hoverChange = true;
}
//do inplace toolTip stuff
if (hoverChange || hoveringOverText != overText)
{
hoveringOverText = overText;
if (overText){
Rectangle cellBounds = null;
Rectangle textBounds = null;
Rectangle preferredTextBounds = null;
if (hoveringItem != null && hoveringItem.getToolTipText(indexOf(col)) == null && //no inplace tooltips when regular tooltip
!col.getWordWrap()) //dont show inplace tooltips for cells with wordwrap
{
cellBounds = col.getCellRenderer().getBounds();
if (cellBounds.x + cellBounds.width > getSize().x)
{
cellBounds.width = getSize().x - cellBounds.x;
}
textBounds = col.getCellRenderer().getTextBounds(item,false);
preferredTextBounds = col.getCellRenderer().getTextBounds(item,true);
}
else if (hoveringColumnHeader != null && hoveringColumnHeader.getHeaderTooltip() == null) //no inplace tooltips when regular tooltip
{
cellBounds = hoveringColumnHeader.getHeaderRenderer().getBounds();
if (cellBounds.x + cellBounds.width > getSize().x)
{
cellBounds.width = getSize().x - cellBounds.x;
}
textBounds = hoveringColumnHeader.getHeaderRenderer().getTextBounds(col,false);
preferredTextBounds = hoveringColumnHeader.getHeaderRenderer().getTextBounds(col,true);
}
else if (hoverColumnGroupHeader != null)
{
cellBounds = hoverColumnGroupHeader.getHeaderRenderer().getBounds();
if (cellBounds.x + cellBounds.width > getSize().x)
{
cellBounds.width = getSize().x - cellBounds.x;
}
textBounds = hoverColumnGroupHeader.getHeaderRenderer().getTextBounds(hoverColumnGroupHeader,false);
preferredTextBounds = hoverColumnGroupHeader.getHeaderRenderer().getTextBounds(hoverColumnGroupHeader,true);
}
//if we are truncated
if (textBounds != null && textBounds.width < preferredTextBounds.width)
{
showToolTip(item,col, hoverColumnGroupHeader, new Point(cellBounds.x + textBounds.x,cellBounds.y +
textBounds.y));
//the following 2 lines are done here rather than in showToolTip to allow
//that method to be overridden yet still capture the mouse.
setCapture(true);
inplaceTooltipCapture = true;
}
}
else
{
hideToolTip();
}
}
//do normal cell specific tooltip stuff
if (hoverChange)
{
String newTip = null;
if ((hoveringItem != null) && (hoveringColumn != null)) {
// get cell specific tooltip
newTip = hoveringItem.getToolTipText(indexOf(hoveringColumn));
} else if ((hoveringColumn != null) && (hoveringColumnHeader != null)) {
// get column header specific tooltip
newTip = hoveringColumn.getHeaderTooltip();
}
if (newTip == null) { // no cell or column header specific tooltip then use base Grid tooltip
newTip = getToolTipText();
}
//Avoid unnecessarily resetting tooltip - this will cause the tooltip to jump around
if (newTip != null && !newTip.equals(displayedToolTipText))
{
updateToolTipText(newTip);
}
else if(newTip == null && displayedToolTipText != null)
{
updateToolTipText(null);
}
displayedToolTipText = newTip;
}
return hoverChange;
}
/**
* Sets the tooltip for the whole Grid to the given text. This method is made available
* for subclasses to override, when a subclass wants to display a different than the standard
* SWT/OS tooltip. Generally, those subclasses would override this event and use this tooltip
* text in their own tooltip or just override this method to prevent the SWT/OS tooltip from
* displaying.
*
* @param text
*/
protected void updateToolTipText(String text)
{
super.setToolTipText(text);
}
/**
* Marks the scroll values obsolete so they will be recalculated.
*/
protected void setScrollValuesObsolete()
{
this.scrollValuesObsolete = true;
redraw();
}
/**
* Inserts a new column into the table.
*
* @param column new column
* @param index index to insert new column
* @return current number of columns
*/
int newColumn(GridColumn column, int index)
{
if (index == -1)
{
columns.add(column);
displayOrderedColumns.add(column);
}
else
{
columns.add(index, column);
displayOrderedColumns.add(index, column);
for (int i = 0; i < columns.size(); i++) {
((GridColumn) columns.get(i)).setColumnIndex(i);
}
}
computeHeaderHeight(sizingGC);
computeFooterHeight(sizingGC);
updatePrimaryCheckColumn();
for (Iterator iterator = items.iterator(); iterator.hasNext();)
{
GridItem item = (GridItem)iterator.next();
item.columnAdded(index);
}
scrollValuesObsolete = true;
redraw();
return columns.size() - 1;
}
/**
* Removes the given column from the table.
*
* @param column column to remove
*/
void removeColumn(GridColumn column)
{
boolean selectionModified = false;
int index = indexOf(column);
if (cellSelectionEnabled)
{
Vector removeSelectedCells = new Vector();
for (Iterator iterator = selectedCells.iterator(); iterator.hasNext();)
{
Point cell = (Point)iterator.next();
if (cell.x == index)
{
removeSelectedCells.add(cell);
}
}
if (removeSelectedCells.size() > 0)
{
selectedCells.removeAll(removeSelectedCells);
selectionModified = true;
}
for (Iterator iterator = selectedCells.iterator(); iterator.hasNext();)
{
Point cell = (Point)iterator.next();
if (cell.x >= index)
{
cell.x--;
selectionModified = true;
}
}
}
columns.remove(column);
displayOrderedColumns.remove(column);
updatePrimaryCheckColumn();
scrollValuesObsolete = true;
redraw();
for (Iterator iterator = items.iterator(); iterator.hasNext();)
{
GridItem item = (GridItem)iterator.next();
item.columnRemoved(index);
}
int i = 0;
for (Iterator iterator = columns.iterator(); iterator.hasNext();)
{
GridColumn col = (GridColumn)iterator.next();
col.setColumnIndex(i);
i++;
}
if (selectionModified && !disposing)
{
updateColumnSelection();
}
}
/**
* Manages the setting of the checkbox column when the SWT.CHECK style was given to the
* table. This method will ensure that the first column of the table always has a checkbox
* when SWT.CHECK is given to the table.
*/
private void updatePrimaryCheckColumn()
{
if ((getStyle() & SWT.CHECK) == SWT.CHECK)
{
boolean firstCol = true;
for (Iterator iter = columns.iterator(); iter.hasNext();)
{
GridColumn col = (GridColumn)iter.next();
col.setTableCheck(firstCol);
firstCol = false;
}
}
}
void newRootItem(GridItem item, int index)
{
if (index == -1 || index >= rootItems.size())
{
rootItems.add(item);
}
else
{
rootItems.add(index,item);
}
}
void removeRootItem(GridItem item)
{
rootItems.remove(item);
}
/**
* Creates the new item at the given index. Only called from GridItem
* constructor.
*
* @param item new item
* @param index index to insert the item at
* @return the index where the item was insert
*/
int newItem(GridItem item, int index, boolean root)
{
int row = 0;
if (!isTree)
{
if (item.getParentItem() != null)
{
isTree = true;
}
}
//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())
{
index = -1;
}
else
{
index = items.indexOf(rootItems.get(index));
}
}
else if (!root)
{
if (index >= item.getParentItem().getItems().length || index == -1)
{
GridItem rightMostDescendent = item.getParentItem();
while (rightMostDescendent.getItems().length > 0)
{
rightMostDescendent = rightMostDescendent.getItems()[rightMostDescendent
.getItems().length - 1];
}
index = indexOf(rightMostDescendent) + 1;
}
else
{
index = indexOf(item.getParentItem().getItems()[index]);
}
}
if (index == -1)
{
items.add(item);
row = items.size() - 1;
}
else
{
items.add(index, item);
row = index;
}
if (items.size() == 1 && !userModifiedItemHeight)
itemHeight = computeItemHeight(item,sizingGC);
item.initializeHeight(itemHeight);
if (isRowHeaderVisible() && isAutoWidth())
{
rowHeaderWidth = Math.max(rowHeaderWidth,rowHeaderRenderer
.computeSize(sizingGC, SWT.DEFAULT, SWT.DEFAULT, item).x);
}
scrollValuesObsolete = true;
topIndex = -1;
bottomIndex = -1;
currentVisibleItems++;
redraw();
return row;
}
/**
* Removes the given item from the table. This method is only called from
* the item's dispose method.
*
* @param item item to remove
*/
void removeItem(GridItem item)
{
Point[] cells = getCells(item);
boolean selectionModified = false;
items.remove(item);
if (disposing)
return;
if (selectedItems.remove(item))
selectionModified = true;
for (int i = 0; i < cells.length; i++)
{
if (selectedCells.remove(cells[i]))
selectionModified = true;
}
if (focusItem == item)
{
focusItem = null;
}
scrollValuesObsolete = true;
topIndex = -1;
bottomIndex = -1;
if (item.isVisible())
{
currentVisibleItems--;
}
if (selectionModified && !disposing)
{
updateColumnSelection();
}
redraw();
// Need to update the scrollbars see see 375327
updateScrollbars();
}
/**
* Creates the given column group at the given index. This method is only
* called from the {@code GridColumnGroup}'s constructor.
*
* @param group group to add.
*/
void newColumnGroup(GridColumnGroup group)
{
GridColumnGroup[] newColumnGroups = new GridColumnGroup[columnGroups.length + 1];
System.arraycopy(columnGroups, 0, newColumnGroups, 0, columnGroups.length);
newColumnGroups[newColumnGroups.length - 1] = group;
columnGroups = newColumnGroups;
// if we just added the first col group, then we need to up the row
// height
if (columnGroups.length == 1)
{
computeHeaderHeight(sizingGC);
}
scrollValuesObsolete = true;
redraw();
}
/**
* Removes the given column group from the table. This method is only called
* from the {@code GridColumnGroup}'s dispose method.
*
* @param group group to remove.
*/
void removeColumnGroup(GridColumnGroup group)
{
GridColumnGroup[] newColumnGroups = new GridColumnGroup[columnGroups.length - 1];
int newIndex = 0;
for (int i = 0; i < columnGroups.length; i++)
{
if (columnGroups[i] != group)
{
newColumnGroups[newIndex] = columnGroups[i];
newIndex++;
}
}
columnGroups = newColumnGroups;
if (columnGroups.length == 0)
{
computeHeaderHeight(sizingGC);
}
scrollValuesObsolete = true;
redraw();
}
/**
* Updates the cached number of visible items by the given amount.
*
* @param amount amount to update cached total
*/
void updateVisibleItems(int amount)
{
currentVisibleItems += amount;
}
/**
* 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;
}
/**
* Returns the current cell in focus. If cell selection is disabled, this method returns null.
*
* @return cell in focus or {@code null}. x represents the column and y the row the cell is in
* @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 Point getFocusCell()
{
checkWidget();
if (!cellSelectionEnabled) return null;
int x = -1;
int y = -1;
if (focusColumn != null)
x = indexOf(focusColumn);
if (focusItem != null)
y = indexOf(focusItem);
return new Point(x,y);
}
/**
* 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();
//TODO: check and make sure this item is valid for focus
if (item == null || item.isDisposed() || item.getParent() != this)
{
SWT.error(SWT.ERROR_INVALID_ARGUMENT);
}
focusItem = item;
}
/**
* Sets the focused item to the given column. Column focus is only applicable when cell
* selection is enabled.
*
* @param column column 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 setFocusColumn(GridColumn column)
{
checkWidget();
//TODO: check and make sure this item is valid for focus
if (column == null || column.isDisposed() || column.getParent() != this || !column.isVisible())
{
SWT.error(SWT.ERROR_INVALID_ARGUMENT);
}
focusColumn = column;
intendedFocusColumn = column;
}
/**
* Returns an array of the columns in their display order.
*
* @return columns in display order
*/
GridColumn[] getColumnsInOrder()
{
checkWidget();
return (GridColumn[])displayOrderedColumns.toArray(new GridColumn[columns.size()]);
}
/**
* Returns true if the table is set to horizontally scroll column-by-column
* rather than pixel-by-pixel.
*
* @return true if the table is scrolled horizontally by column
* @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 getColumnScrolling()
{
checkWidget();
return columnScrolling;
}
/**
* Sets the table scrolling method to either scroll column-by-column (true)
* or pixel-by-pixel (false).
*
* @param columnScrolling true to horizontally scroll by column, false to
* scroll by pixel
* @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 setColumnScrolling(boolean columnScrolling)
{
checkWidget();
if (rowHeaderVisible && !columnScrolling)
{
return;
}
this.columnScrolling = columnScrolling;
scrollValuesObsolete = true;
redraw();
}
/**
* Returns the first visible column that is not spanned by any other column that is either the
* given column or any of the columns displaying to the left of the given column. If the
* given column and subsequent columns to the right are either not visible or spanned, this
* method will return null.
*
* @param item
* @param col
* @return
*/
private GridColumn getVisibleColumn_DegradeLeft(GridItem item, GridColumn col)
{
int index = displayOrderedColumns.indexOf(col);
GridColumn prevCol = col;
int i = 0;
while (!prevCol.isVisible())
{
i ++;
if (index - i < 0)
return null;
prevCol = (GridColumn)displayOrderedColumns.get(index - i);
}
index = displayOrderedColumns.indexOf(prevCol);
for (int j = 0; j < index; j++)
{
GridColumn tempCol = (GridColumn)displayOrderedColumns.get(j);
if (!tempCol.isVisible())
{
continue;
}
if (item.getColumnSpan(indexOf(tempCol)) >= index - j)
{
prevCol = tempCol;
break;
}
}
return prevCol;
}
/**
* Returns the first visible column that is not spanned by any other column that is either the
* given column or any of the columns displaying to the right of the given column. If the
* given column and subsequent columns to the right are either not visible or spanned, this
* method will return null.
*
* @param item
* @param col
* @return
*/
private GridColumn getVisibleColumn_DegradeRight(GridItem item, GridColumn col)
{
int index = displayOrderedColumns.indexOf(col);
int i = 0;
GridColumn nextCol = col;
while (!nextCol.isVisible())
{
i ++;
if (index + i == displayOrderedColumns.size())
return null;
nextCol = (GridColumn)displayOrderedColumns.get(index + i);
}
index = displayOrderedColumns.indexOf(nextCol);
int startIndex = index;
while (index > 0)
{
index --;
GridColumn prevCol = (GridColumn)displayOrderedColumns.get(index);
if (item.getColumnSpan(indexOf(prevCol)) >= startIndex - index)
{
if (startIndex == displayOrderedColumns.size() - 1)
{
return null;
}
else
{
return getVisibleColumn_DegradeRight(item, (GridColumn)displayOrderedColumns.get(startIndex + 1));
}
}
}
return nextCol;
}
/**
* Returns true if the cells are selectable in the reciever.
*
* @return cell selection enablement status.
* @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 getCellSelectionEnabled()
{
checkWidget();
return cellSelectionEnabled;
}
/**
* Sets whether cells are selectable in the receiver.
*
* @param cellSelection the cellSelection 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 setCellSelectionEnabled(boolean cellSelection)
{
checkWidget();
if (!cellSelection)
{
selectedCells.clear();
redraw();
}
else
{
selectedItems.clear();
redraw();
}
this.cellSelectionEnabled = cellSelection;
}
/**
* @return <code>true</code> if cell selection is enabled
*/
public boolean isCellSelectionEnabled() {
return cellSelectionEnabled;
}
/**
* Deselects the given cell in the receiver. If the given cell is already
* deselected it remains deselected. Invalid cells are ignored.
*
* @param cell cell to deselect.
* @throws IllegalArgumentException
* <ul>
* <li>ERROR_NULL_ARGUMENT - if the cell 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 deselectCell(Point cell)
{
checkWidget();
if (cell == null)
SWT.error(SWT.ERROR_NULL_ARGUMENT);
selectedCells.remove(cell);
updateColumnSelection();
redraw();
}
/**
* Deselects the given cells. Invalid cells are ignored.
*
* @param cells the cells to deselect.
*
* @throws IllegalArgumentException
* <ul>
* <li>ERROR_NULL_ARGUMENT - if the set of cells or any cell 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 deselectCells(Point[] cells)
{
checkWidget();
if (cells == null)
SWT.error(SWT.ERROR_NULL_ARGUMENT);
for (int i = 0; i < cells.length; i++)
{
if (cells[i] == null)
SWT.error(SWT.ERROR_NULL_ARGUMENT);
}
for (int i = 0; i < cells.length; i++)
{
selectedCells.remove(cells[i]);
}
updateColumnSelection();
redraw();
}
/**
* Deselects all selected cells 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 void deselectAllCells()
{
checkWidget();
selectedCells.clear();
updateColumnSelection();
redraw();
}
/**
* Selects the given cell. Invalid cells are ignored.
*
* @param cell point whose x values is a column index and y value is an item index
* @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 void selectCell(Point cell)
{
checkWidget();
if (!cellSelectionEnabled) return;
if (cell == null)
SWT.error(SWT.ERROR_NULL_ARGUMENT);
addToCellSelection(cell);
updateColumnSelection();
redraw();
}
/**
* Selects the given cells. Invalid cells are ignored.
*
* @param cells an arry of points whose x value is a column index and y value is an item index
* @throws IllegalArgumentException
* <ul>
* <li>ERROR_NULL_ARGUMENT - if the set of cells or an individual cell 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 selectCells(Point[] cells)
{
checkWidget();
if (!cellSelectionEnabled) return;
if (cells == null)
SWT.error(SWT.ERROR_NULL_ARGUMENT);
for (int i = 0; i < cells.length; i++)
{
if (cells[i] == null)
SWT.error(SWT.ERROR_NULL_ARGUMENT);
}
for (int i = 0; i < cells.length; i++)
{
addToCellSelection(cells[i]);
}
updateColumnSelection();
redraw();
}
/**
* Selects all cells 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 void selectAllCells()
{
checkWidget();
selectAllCellsInternal();
}
/**
* Selects all cells in the receiver.
*
* @return An Event object
*
* @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>
*/
private Event selectAllCellsInternal()
{
if (!cellSelectionEnabled)
return selectAllRowsInternal();
if (columns.size() == 0)
return null;
if(items.size() == 0)
return null;
int index = 0;
GridColumn column = (GridColumn)displayOrderedColumns.get(index);
while (!column.isVisible())
{
index ++;
if (index >= columns.size())
return null;
column = (GridColumn)displayOrderedColumns.get(index);
}
GridColumn oldFocusColumn = focusColumn;
GridItem oldFocusItem = focusItem;
focusColumn = column;
focusItem = (GridItem)items.get(0);
GridItem lastItem = getPreviousVisibleItem(null);
GridColumn lastCol = getVisibleColumn_DegradeLeft(lastItem,(GridColumn)displayOrderedColumns.get(displayOrderedColumns.size() -1));
Event event = updateCellSelection(new Point(indexOf(lastCol),indexOf(lastItem)),SWT.MOD2, true, false);
focusColumn = oldFocusColumn;
focusItem = oldFocusItem;
updateColumnSelection();
redraw();
return event;
}
/**
* Selects rows in the receiver.
*
* @return An Event object
*
* @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>
*/
private Event selectAllRowsInternal()
{
if (cellSelectionEnabled)
return selectAllCellsInternal();
if (SWT.MULTI != selectionType)
return null;
if(items.size() == 0)
return null;
deselectAll();
GridItem oldFocusItem = focusItem;
GridItem firstItem = getItem(getTopIndex());
GridItem lastItem = getPreviousVisibleItem(null);
setFocusItem(firstItem);
updateSelection(firstItem, SWT.NONE);
Event event = updateSelection(lastItem, SWT.MOD2);
setFocusItem(oldFocusItem);
redraw();
return event;
}
/**
* Selects all cells in the given column in the receiver.
*
* @param col
*
* @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 selectColumn(int col) {
checkWidget();
Vector cells = new Vector();
getCells(getColumn(col), cells);
selectCells((Point[])cells.toArray(new Point[0]));
}
/**
* Selects all cells in the given column group in the receiver.
*
* @param colGroup the column group
*
* @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 selectColumnGroup(int colGroup) {
selectColumnGroup(getColumnGroup(colGroup));
}
/**
* Selects all cells in the given column group in the receiver.
*
* @param colGroup the column group
*
* @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 selectColumnGroup(GridColumnGroup colGroup) {
checkWidget();
Vector cells = new Vector();
getCells(colGroup, cells);
selectCells((Point[])cells.toArray(new Point[0]));
}
/**
* Selects the selection to the given cell. The existing selection is cleared before
* selecting the given cell.
*
* @param cell point whose x values is a column index and y value is an item index
* @throws IllegalArgumentException
* <ul>
* <li>ERROR_NULL_ARGUMENT - if the item is null</li>
* <li>ERROR_INVALID_ARGUMENT - if the cell is invalid</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 setCellSelection(Point cell)
{
checkWidget();
if (!cellSelectionEnabled) return;
if (cell == null)
SWT.error(SWT.ERROR_NULL_ARGUMENT);
if (!isValidCell(cell))
SWT.error(SWT.ERROR_INVALID_ARGUMENT);
selectedCells.clear();
addToCellSelection(cell);
updateColumnSelection();
redraw();
}
/**
* Selects the selection to the given set of cell. The existing selection is cleared before
* selecting the given cells.
*
* @param cells point array whose x values is a column index and y value is an item index
* @throws IllegalArgumentException
* <ul>
* <li>ERROR_NULL_ARGUMENT - if the cell array or an individual cell is null</li>
* <li>ERROR_INVALID_ARGUMENT - if the a cell is invalid</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 setCellSelection(Point[] cells)
{
checkWidget();
if (!cellSelectionEnabled) return;
if (cells == null)
SWT.error(SWT.ERROR_NULL_ARGUMENT);
for (int i = 0; i < cells.length; i++)
{
if (cells[i] == null)
SWT.error(SWT.ERROR_NULL_ARGUMENT);
if (!isValidCell(cells[i]))
SWT.error(SWT.ERROR_INVALID_ARGUMENT);
}
selectedCells.clear();
for (int i = 0; i < cells.length; i++)
{
addToCellSelection(cells[i]);
}
updateColumnSelection();
redraw();
}
/**
* Returns an array of cells that are currently selected in the
* receiver. The order of the items is unspecified. An empty array indicates
* that no items are selected.
* <p>
* Note: This is not the actual structure used by the receiver to maintain
* its selection, so modifying the array will not affect the receiver.
* </p>
*
* @return an array representing the cell 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 Point[] getCellSelection()
{
checkWidget();
return (Point[])selectedCells.toArray(new Point[selectedCells.size()]);
}
GridColumn getFocusColumn()
{
return focusColumn;
}
void updateColumnFocus()
{
if (!focusColumn.isVisible())
{
int index = displayOrderedColumns.indexOf(focusColumn);
if (index > 0)
{
GridColumn prev = (GridColumn)displayOrderedColumns.get(index - 1);
prev = getVisibleColumn_DegradeLeft(focusItem,prev);
if (prev == null)
{
prev = getVisibleColumn_DegradeRight(focusItem,focusColumn);
}
focusColumn = prev;
}
else
{
focusColumn = getVisibleColumn_DegradeRight(focusItem,focusColumn);
}
}
}
private void getCells(GridColumn col, Vector cells)
{
int colIndex = indexOf(col);
int columnAtPosition = 0;
for (Iterator iter = displayOrderedColumns.iterator(); iter.hasNext();)
{
GridColumn nextCol = (GridColumn)iter.next();
if (!nextCol.isVisible()) continue;
if (nextCol == col) break;
columnAtPosition ++;
}
GridItem item = null;
if (getItemCount() > 0)
item = getItem(0);
while (item != null)
{
//is cell spanned
int position = -1;
boolean spanned = false;
for (Iterator iter = displayOrderedColumns.iterator(); iter.hasNext();)
{
GridColumn nextCol = (GridColumn)iter.next();
if (!nextCol.isVisible()) continue;
if (nextCol == col) break;
int span = item.getColumnSpan(indexOf(nextCol));
if (position + span >= columnAtPosition){
spanned = true;
break;
}
}
if (!spanned && item.getColumnSpan(colIndex) == 0)
{
cells.add(new Point(colIndex,indexOf(item)));
}
item = getNextVisibleItem(item);
}
}
private void getCells(GridColumnGroup colGroup, Vector cells)
{
GridColumn[] cols = colGroup.getColumns();
for (int i = 0; i < cols.length; i++) {
getCells(cols[i], cells);
}
}
private void getCells(GridItem item, Vector cells)
{
int itemIndex = indexOf(item);
int span = 0;
for (Iterator iter = displayOrderedColumns.iterator(); iter.hasNext();)
{
GridColumn nextCol = (GridColumn)iter.next();
if (span > 0)
{
span --;
continue;
}
if (!nextCol.isVisible()) continue;
span = item.getColumnSpan(indexOf(nextCol));
cells.add(new Point(indexOf(nextCol),itemIndex));
}
}
private Point[] getCells(GridItem item)
{
Vector cells = new Vector();
int itemIndex = indexOf(item);
int span = 0;
for (Iterator iter = displayOrderedColumns.iterator(); iter.hasNext();)
{
GridColumn nextCol = (GridColumn)iter.next();
if (span > 0)
{
span --;
continue;
}
if (!nextCol.isVisible()) continue;
span = item.getColumnSpan(indexOf(nextCol));
cells.add(new Point(indexOf(nextCol),itemIndex));
}
return (Point[])cells.toArray(new Point[]{});
}
private void getCells(GridItem fromItem, GridItem toItem, Vector cells)
{
boolean descending = (indexOf(fromItem) < indexOf(toItem));
GridItem iterItem = toItem;
do
{
getCells(iterItem,cells);
if (iterItem == fromItem) break;
if (descending)
{
iterItem = getPreviousVisibleItem(iterItem);
}
else
{
iterItem = getNextVisibleItem(iterItem);
}
} while (true);
}
private int blend(int v1, int v2, int ratio) {
return (ratio*v1 + (100-ratio)*v2)/100;
}
private RGB blend(RGB c1, RGB c2, int ratio) {
int r = blend(c1.red, c2.red, ratio);
int g = blend(c1.green, c2.green, ratio);
int b = blend(c1.blue, c2.blue, ratio);
return new RGB(r, g, b);
}
/**
* Returns a point whose x and y values are the to and from column indexes of the new selection
* range inclusive of all spanned columns.
*
* @param fromItem
* @param fromColumn
* @param toItem
* @param toColumn
* @return
*/
private Point getSelectionRange(GridItem fromItem, GridColumn fromColumn, GridItem toItem, GridColumn toColumn)
{
if (displayOrderedColumns.indexOf(fromColumn) > displayOrderedColumns.indexOf(toColumn))
{
GridColumn temp = fromColumn;
fromColumn = toColumn;
toColumn = temp;
}
if (indexOf(fromItem) > indexOf(toItem))
{
GridItem temp = fromItem;
fromItem = toItem;
toItem = temp;
}
boolean firstTime = true;
GridItem iterItem = fromItem;
int fromIndex = indexOf(fromColumn);
int toIndex = indexOf(toColumn);
do
{
if (!firstTime)
{
iterItem = getNextVisibleItem(iterItem);
}
else
{
firstTime = false;
}
Point cols = getRowSelectionRange(iterItem, fromColumn, toColumn);
//check and see if column spanning means that the range increased
if (cols.x != fromIndex || cols.y != toIndex)
{
GridColumn newFrom = getColumn(cols.x);
GridColumn newTo = getColumn(cols.y);
//Unfortunately we have to start all over again from the top with the new range
return getSelectionRange(fromItem, newFrom, toItem, newTo);
}
} while (iterItem != toItem);
return new Point(indexOf(fromColumn),indexOf(toColumn));
}
/**
* Returns a point whose x and y value are the to and from column indexes of the new selection
* range inclusive of all spanned columns.
*
* @param item
* @param fromColumn
* @param toColumn
* @return
*/
private Point getRowSelectionRange(GridItem item, GridColumn fromColumn, GridColumn toColumn)
{
int newFrom = indexOf(fromColumn);
int newTo = indexOf(toColumn);
int span = 0;
int spanningColIndex = -1;
boolean spanningBeyondToCol = false;
for (Iterator iter = displayOrderedColumns.iterator(); iter.hasNext();)
{
GridColumn col = (GridColumn)iter.next();
if (!col.isVisible())
{
if (span > 0) span --;
continue;
}
if (span > 0)
{
if (col == fromColumn)
{
newFrom = spanningColIndex;
}
else if (col == toColumn && span > 1)
{
spanningBeyondToCol = true;
}
span --;
if (spanningBeyondToCol && span == 0)
{
newTo = indexOf(col);
break;
}
}
else
{
int index = indexOf(col);
span = item.getColumnSpan(index);
if (span > 0) spanningColIndex = index;
if (col == toColumn && span > 0)
spanningBeyondToCol = true;
}
if (col == toColumn && !spanningBeyondToCol)
break;
}
return new Point(newFrom,newTo);
}
/**
* Returns the column which is spanning the given column for the given item or null if it is not
* being spanned.
*
* @param item
* @param column
* @return
*/
private GridColumn getSpanningColumn(GridItem item, GridColumn column)
{
int span = 0;
GridColumn spanningCol = null;
for (Iterator iter = displayOrderedColumns.iterator(); iter.hasNext();)
{
GridColumn col = (GridColumn)iter.next();
if (col == column)
{
return spanningCol;
}
if (span > 0)
{
span --;
if (span == 0) spanningCol = null;
}
else
{
int index = indexOf(col);
span = item.getColumnSpan(index);
if (span > 0) spanningCol = col;
}
}
return spanningCol;
}
/**
* Returns true if the given cell's x and y values are valid column and
* item indexes respectively.
*
* @param cell
* @return
*/
private boolean isValidCell(Point cell)
{
if (cell.x < 0 || cell.x >= columns.size())
return false;
if (cell.y < 0 || cell.y >= items.size())
return false;
return true;
}
/**
* Shows the inplace tooltip for the given item and column. The location is the x and y origin
* of the text in the cell.
* <p>
* This method may be overriden to provide their own custom tooltips.
*
* @param item the item currently hovered over or null.
* @param column the column currently hovered over or null.
* @param group the group currently hovered over or null.
* @param location the x,y origin of the text in the hovered object.
*/
protected void showToolTip(GridItem item, GridColumn column, GridColumnGroup group, Point location)
{
if (inplaceToolTip == null)
{
inplaceToolTip = new GridToolTip(this);
}
if (group != null)
{
inplaceToolTip.setFont(getFont());
inplaceToolTip.setText(group.getText());
}
else if (item != null)
{
inplaceToolTip.setFont(item.getFont(item.getParent().indexOf(column)));
inplaceToolTip.setText(item.getText(item.getParent().indexOf(column)));
}
else if (column != null)
{
inplaceToolTip.setFont(getFont());
inplaceToolTip.setText(column.getText());
}
Point p = getDisplay().map(this, null, location);
inplaceToolTip.setLocation(p);
inplaceToolTip.setVisible(true);
}
/**
* Hides the inplace tooltip.
* <p>
* This method must be overriden when showToolTip is overriden. Subclasses must
* call super when overriding this method.
*/
protected void hideToolTip()
{
if (inplaceToolTip != null)
{
inplaceToolTip.setVisible(false);
}
if (inplaceTooltipCapture)
{
setCapture(false);
inplaceTooltipCapture = false;
}
}
void recalculateRowHeaderHeight(GridItem item,int oldHeight, int newHeight) {
checkWidget();
if( newHeight > itemHeight ) {
itemHeight = newHeight;
userModifiedItemHeight = false;
hasDifferingHeights=false;
itemHeight = computeItemHeight((GridItem) items.get(0), sizingGC);
for(int cnt=0;cnt<items.size();cnt++)
((GridItem)items.get(cnt)).setHeight(itemHeight);
setScrollValuesObsolete();
redraw();
}
}
void recalculateRowHeaderWidth(GridItem item,int oldWidth, int newWidth)
{
if (!isAutoWidth())
return;
if (newWidth > rowHeaderWidth)
{
rowHeaderWidth = newWidth;
}
else if (newWidth < rowHeaderWidth && oldWidth == rowHeaderWidth)
{
//if the changed width is smaller, and the previous width of that rows header was equal
//to the current row header width then its possible that we may need to make the new
//row header width smaller, but to do that we need to ask all the rows all over again
for (Iterator iter = items.iterator(); iter.hasNext();)
{
GridItem iterItem = (GridItem)iter.next();
newWidth = Math.max(newWidth,rowHeaderRenderer.computeSize(sizingGC, SWT.DEFAULT,SWT.DEFAULT,iterItem).x);
}
rowHeaderWidth = newWidth;
}
redraw();
}
/**
* {@inheritDoc}
*/
public void setFont(Font font)
{
super.setFont(font);
sizingGC.setFont(font);
}
/**
* Returns the row header width or 0 if row headers are not visible.
*
* @return the width of the row 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 getItemHeaderWidth()
{
checkWidget();
if (!rowHeaderVisible)
return 0;
return rowHeaderWidth;
}
/**
* Sets the row header width to the specified value. This automatically disables the auto width feature of the grid.
* @param width the width of the row header
* @see #getItemHeaderWidth()
* @see #setAutoWidth(boolean)
*/
public void setItemHeaderWidth(int width)
{
checkWidget();
rowHeaderWidth = width;
setAutoWidth(false);
redraw();
}
/**
* 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();
setRedraw(false);
if (count < 0)
count = 0;
if (count < items.size())
{
//TODO delete and clear items if necessary
}
while (count > items.size())
{
new GridItem(this,SWT.NONE);
}
setRedraw(true);
}
/**
* Initialize accessibility.
*/
private void initAccessible()
{
final Accessible accessible = getAccessible();
accessible.addAccessibleListener(new AccessibleAdapter()
{
public void getDescription(AccessibleEvent e)
{
int childID = e.childID;
if (childID >= 0 && childID < items.size())
{
String descrption = "";
for (int i = 0; i < columns.size(); i++)
{
if (i != 0)
{
descrption += ((GridColumn)columns.get(i)).getText() + " : ";
descrption += ((GridItem)items.get(childID)).getText(i) + " ";
}
}
e.result = descrption;
}
}
public void getName(AccessibleEvent e)
{
int childID = e.childID;
if (childID >= 0 && childID < items.size())
{
// Name of the items
e.result = ((GridItem)items.get(childID)).getText();
}
else if (childID >= items.size() && childID < items.size() + columns.size())
{
// Name of the column headers
e.result = ((GridColumn)columns.get(childID - items.size())).getText();
}
else if (childID >= items.size() + columns.size()
&& childID < items.size() + columns.size() + columnGroups.length)
{
// Name of the column group headers
e.result = ((GridColumnGroup)columnGroups[childID - items.size()
- columns.size()]).getText();
}
else if (childID >= items.size() + columns.size() + columnGroups.length
&& childID < items.size() + columns.size() + columnGroups.length
+ columnGroups.length)
{
// Name of the toggle button for column group headers
e.result = ACC_TOGGLE_BUTTON_NAME;
}
}
});
accessible.addAccessibleControlListener(new AccessibleControlAdapter()
{
public void getChildAtPoint(AccessibleControlEvent e)
{
Point location = toControl(e.x, e.y);
e.childID = ACC.CHILDID_SELF;
// Grid Items
GridItem item = getItem(location);
if (item != null)
{
for (int i = 0; i < getItems().length; i++)
{
if (item.equals(getItem(i)))
{
e.childID = i;
return;
}
}
}
else
{
// Column Headers
GridColumn column = overColumnHeader(location.x, location.y);
if (column != null)
{
for (int i = 0; i < getColumns().length; i++)
{
if (column.equals(getColumn(i)))
{
e.childID = getItems().length + i;
return;
}
}
}
else
{
// Column Group headers
GridColumnGroup columnGroup = overColumnGroupHeader(location.x, location.y);
if (columnGroup != null)
{
for (int i = 0; i < getColumnGroups().length; i++)
{
if (columnGroup.equals(getColumnGroup(i)))
{
Rectangle toggle = ((DefaultColumnGroupHeaderRenderer)columnGroup
.getHeaderRenderer()).getToggleBounds();
if (toggle.contains(location.x, location.y))
{
// Toggle button for column group
// header
e.childID = getItems().length + getColumns().length
+ getColumnGroups().length + i;
}
else
{
// Column Group header
e.childID = getItems().length + getColumns().length + i;
}
return;
}
}
}
}
}
}
public void getChildCount(AccessibleControlEvent e)
{
if (e.childID == ACC.CHILDID_SELF)
{
int length = items.size();
if (isTree)
{
// Child count for parent. Here if the item parent
// is not an other item,
// it is consider as children of Grid
for (int i = 0; i < items.size(); i++)
{
if (((GridItem)items.get(i)).getParentItem() != null)
{
length--;
}
}
}
e.detail = length;
}
}
public void getChildren(AccessibleControlEvent e)
{
if (e.childID == ACC.CHILDID_SELF)
{
int length = items.size();
if (isTree)
{
for (int i = 0; i < items.size(); i++)
{
if (((GridItem)items.get(i)).getParentItem() != null)
{
length--;
}
}
Object[] children = new Object[length];
int j = 0;
for (int i = 0; i < items.size(); i++)
{
if (((GridItem)items.get(i)).getParentItem() == null)
{
children[j] = new Integer(i);
j++;
}
}
e.children = children;
}
else
{
Object[] children = new Object[length];
for (int i = 0; i < items.size(); i++)
{
children[i] = new Integer(i);
}
e.children = children;
}
}
}
public void getDefaultAction(AccessibleControlEvent e)
{
int childID = e.childID;
if (childID >= 0 && childID < items.size())
{
if (getItem(childID).hasChildren())
{
// Action of tree items
if (getItem(childID).isExpanded())
{
e.result = ACC_ITEM_ACTION_COLLAPSE;
}
else
{
e.result = ACC_ITEM_ACTION_EXPAND;
}
}
else
{
// action of default items
e.result = ACC_ITEM_DEFAULT_ACTION;
}
}
else if (childID >= items.size()
&& childID < items.size() + columns.size() + columnGroups.length)
{
// action of column and column group header
e.result = ACC_COLUMN_DEFAULT_ACTION;
}
else if (childID >= items.size() + columns.size() + columnGroups.length
&& childID < items.size() + columns.size() + columnGroups.length
+ columnGroups.length)
{
// action of toggle button of column group header
e.result = SWT.getMessage("SWT_Press");
}
}
public void getLocation(AccessibleControlEvent e)
{
// location of parent
Rectangle location = getBounds();
location.x = 0;
location.y = 0;
int childID = e.childID;
if (childID >= 0 && childID < items.size())
{
// location of items
GridItem item = getItem(childID);
if (item != null)
{
Point p = getOrigin((GridColumn)columns.get(0), item);
location.y = p.y;
location.height = item.getHeight();
}
}
else if (childID >= items.size() && childID < items.size() + columns.size())
{
// location of columns headers
GridColumn column = getColumn(childID - items.size());
if (column != null)
{
location.x = getColumnHeaderXPosition(column);
if (column.getColumnGroup() == null)
{
location.y = 0;
}
else
{
location.y = groupHeaderHeight;
}
location.height = headerHeight;
location.width = column.getWidth();
}
}
else if (childID >= items.size() + columns.size()
&& childID < items.size() + columns.size() + columnGroups.length)
{
// location of column group header
GridColumnGroup columnGroup = getColumnGroup(childID - items.size()
- columns.size());
if (columnGroup != null)
{
location.y = 0;
location.height = groupHeaderHeight;
location.x = getColumnHeaderXPosition(columnGroup.getFirstVisibleColumn());
int width = 0;
for (int i = 0; i < columnGroup.getColumns().length; i++)
{
if (columnGroup.getColumns()[i].isVisible())
{
width += columnGroup.getColumns()[i].getWidth();
}
}
location.width = width;
}
}
else if (childID >= items.size() + columns.size() + columnGroups.length
&& childID < items.size() + columns.size() + columnGroups.length
+ columnGroups.length)
{
// location of toggle button of column group header
GridColumnGroup columnGroup = getColumnGroup(childID - items.size()
- columns.size()
- columnGroups.length);
location = ((DefaultColumnGroupHeaderRenderer)columnGroup.getHeaderRenderer())
.getToggleBounds();
}
if (location != null)
{
Point pt = toDisplay(location.x, location.y);
e.x = pt.x;
e.y = pt.y;
e.width = location.width;
e.height = location.height;
}
}
public void getRole(AccessibleControlEvent e)
{
int childID = e.childID;
if (childID >= 0 && childID < items.size())
{
// role of items
if (isTree)
{
e.detail = ACC.ROLE_TREEITEM;
}
else
{
e.detail = ACC.ROLE_LISTITEM;
}
}
else if (childID >= items.size()
&& childID < items.size() + columns.size() + columnGroups.length)
{
// role of columns headers and column group headers
e.detail = ACC.ROLE_TABLECOLUMNHEADER;
}
else if (childID >= items.size() + columns.size() + columnGroups.length
&& childID < items.size() + columns.size() + columnGroups.length
+ columnGroups.length)
{
// role of toggle button of column group headers
e.detail = ACC.ROLE_PUSHBUTTON;
}
else if (childID == ACC.CHILDID_SELF)
{
// role of parent
if (isTree)
{
e.detail = ACC.ROLE_TREE;
}
else
{
e.detail = ACC.ROLE_TABLE;
}
}
}
public void getSelection(AccessibleControlEvent e)
{
e.childID = ACC.CHILDID_NONE;
if (selectedItems.size() == 1)
{
// Single selection
e.childID = indexOf(((GridItem)selectedItems.get(0)));
}
else if (selectedItems.size() > 1)
{
// multiple selection
e.childID = ACC.CHILDID_MULTIPLE;
int length = selectedItems.size();
Object[] children = new Object[length];
for (int i = 0; i < length; i++)
{
GridItem item = (GridItem)selectedItems.get(i);
children[i] = new Integer(indexOf(item));
}
e.children = children;
}
}
public void getState(AccessibleControlEvent e)
{
int childID = e.childID;
if (childID >= 0 && childID < items.size())
{
// state of items
e.detail = ACC.STATE_SELECTABLE;
if (getDisplay().getActiveShell() == getParent().getShell())
{
e.detail |= ACC.STATE_FOCUSABLE;
}
if (selectedItems.contains(getItem(childID)))
{
e.detail |= ACC.STATE_SELECTED;
if (getDisplay().getActiveShell() == getParent().getShell())
{
e.detail |= ACC.STATE_FOCUSED;
}
}
if (getItem(childID).getChecked())
{
e.detail |= ACC.STATE_CHECKED;
}
// only for tree type items
if (getItem(childID).hasChildren())
{
if (getItem(childID).isExpanded())
{
e.detail |= ACC.STATE_EXPANDED;
}
else
{
e.detail |= ACC.STATE_COLLAPSED;
}
}
if (!getItem(childID).isVisible())
{
e.detail |= ACC.STATE_INVISIBLE;
}
}
else if (childID >= items.size()
&& childID < items.size() + columns.size() + columnGroups.length)
{
// state of column headers and column group headers
e.detail = ACC.STATE_READONLY;
}
else if (childID >= items.size() + columns.size() + columnGroups.length
&& childID < items.size() + columns.size() + columnGroups.length
+ columnGroups.length)
{
// state of toggle button of column group headers
if (getColumnGroup(
childID - items.size() - columns.size()
- columnGroups.length).getExpanded())
{
e.detail = ACC.STATE_EXPANDED;
}
else
{
e.detail = ACC.STATE_COLLAPSED;
}
}
}
public void getValue(AccessibleControlEvent e)
{
int childID = e.childID;
if (childID >= 0 && childID < items.size())
{
// value for tree items
if (isTree)
{
e.result = "" + getItem(childID).getLevel();
}
}
}
});
addListener(SWT.Selection, new Listener()
{
public void handleEvent(Event event)
{
if (selectedItems.size() > 0)
{
accessible.setFocus(items.indexOf(selectedItems.get(selectedItems.size() - 1)));
}
}
});
addTreeListener(new TreeListener()
{
public void treeCollapsed(TreeEvent e)
{
if (getFocusItem() != null)
{
accessible.setFocus(items.indexOf(getFocusItem()));
}
}
public void treeExpanded(TreeEvent e)
{
if (getFocusItem() != null)
{
accessible.setFocus(items.indexOf(getFocusItem()));
}
}
});
}
/**
* @return the disposing
*/
boolean isDisposing()
{
return disposing;
}
/**
* @param hasSpanning the hasSpanning to set
*/
void setHasSpanning(boolean hasSpanning)
{
this.hasSpanning = hasSpanning;
}
/**
* Returns the receiver's tool tip text, or null if it has
* not been set.
*
* @return the receiver's tool tip text
*
* @exception SWTException <ul>
* <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
* <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
* </ul>
*/
public String getToolTipText() {
checkWidget();
return toolTipText;
}
/**
* Sets the receiver's tool tip text to the argument, which
* may be null indicating that no tool tip text should be shown.
*
* @param string the new tool tip text (or null)
*
* @exception SWTException <ul>
* <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
* <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
* </ul>
*/
public void setToolTipText(String string) {
checkWidget();
toolTipText = string;
}
/**
* Updates the row height when the first image is set on an item.
* @param column the column the image is change
* @param item item which images has just been set on.
*/
void imageSetOnItem(int column, GridItem item)
{
if( sizeOnEveryItemImageChange ) {
if( item == null || item.getImage(column) == null )
return;
int height = item.getImage(column).getBounds().height;
//FIXME Needs better algorithm
if( height + 20 > getItemHeight() ) {
height = computeItemHeight(item,sizingGC);
setItemHeight(height);
}
} else {
if (firstImageSet || userModifiedItemHeight) return;
int height = computeItemHeight(item,sizingGC);
setItemHeight(height);
firstImageSet = true;
}
}
/**
* Determines if the mouse is hovering on the selection drag area and changes the
* pointer and sets field appropriately.
* <p>
* Note: The 'selection drag area' is that part of the selection,
* on which a drag event can be initiated. This is either the border
* of the selection (i.e. a cell border between a slected and a non-selected
* cell) or the complete selection (i.e. anywhere on a selected cell).
* What area serves as drag area is determined by {@link #setDragOnFullSelection(boolean)}.
*
* @param x
* @param y
* @return
* @see #setDragOnFullSelection(boolean)
*/
private boolean handleHoverOnSelectionDragArea(int x, int y)
{
boolean over = false;
// Point inSelection = null;
if ( (!rowHeaderVisible || x > rowHeaderWidth-SELECTION_DRAG_BORDER_THRESHOLD)
&& (!columnHeadersVisible || y > headerHeight-SELECTION_DRAG_BORDER_THRESHOLD) )
{
// not on a header
// if(!dragOnFullSelection)
// {
// // drag area is the border of the selection
//
// if(cellSelectionEnabled)
// {
// Point neP = new Point( x-SELECTION_DRAG_BORDER_THRESHOLD, y-SELECTION_DRAG_BORDER_THRESHOLD );
// Point ne = getCell(neP);
// Point nwP = new Point( x+SELECTION_DRAG_BORDER_THRESHOLD, y-SELECTION_DRAG_BORDER_THRESHOLD );
// Point nw = getCell(nwP);
// Point swP = new Point( x+SELECTION_DRAG_BORDER_THRESHOLD, y+SELECTION_DRAG_BORDER_THRESHOLD );
// Point sw = getCell(swP);
// Point seP = new Point( x-SELECTION_DRAG_BORDER_THRESHOLD, y+SELECTION_DRAG_BORDER_THRESHOLD );
// Point se = getCell(seP);
//
// boolean neSel = ne != null && isCellSelected(ne);
// boolean nwSel = nw != null && isCellSelected(nw);
// boolean swSel = sw != null && isCellSelected(sw);
// boolean seSel = se != null && isCellSelected(se);
//
// over = (neSel || nwSel || swSel || seSel) && (!neSel || !nwSel || !swSel || !seSel);
//// inSelection = neSel ? neP : nwSel ? nwP : swSel ? swP : seSel ? seP : null;
// }
// else
// {
// Point nP = new Point( x, y-SELECTION_DRAG_BORDER_THRESHOLD );
// GridItem n = getItem(nP);
// Point sP = new Point( x, y+SELECTION_DRAG_BORDER_THRESHOLD );
// GridItem s = getItem(sP);
//
// boolean nSel = n != null && isSelected(n);
// boolean sSel = s != null && isSelected(s);
//
// over = nSel != sSel;
//// inSelection = nSel ? nP : sSel ? sP : null;
// }
// }
// else
// {
// drag area is the entire selection
if(cellSelectionEnabled)
{
Point p = new Point(x,y);
Point cell = getCell(p);
over = cell !=null && isCellSelected(cell);
// inSelection = over ? p : null;
}
else
{
Point p = new Point(x,y);
GridItem item = getItem(p);
over = item != null && isSelected(item);
// inSelection = over ? p : null;
}
}
// }
if (over != hoveringOnSelectionDragArea)
{
// if (over)
// {
// // use drag cursor only in border mode
// if (!dragOnFullSelection)
// setCursor(getDisplay().getSystemCursor(SWT.CURSOR_SIZEALL));
//// potentialDragStart = inSelection;
// }
// else
// {
// setCursor(null);
//// potentialDragStart = null;
// }
hoveringOnSelectionDragArea = over;
}
return over;
}
/**
* Display a mark indicating the point at which an item will be inserted.
* This is used as a visual hint to show where a dragged item will be
* inserted when dropped on the grid. This method should not be called
* directly, instead {@link DND#FEEDBACK_INSERT_BEFORE} or
* {@link DND#FEEDBACK_INSERT_AFTER} should be set in
* {@link DropTargetEvent#feedback} from within a {@link DropTargetListener}.
*
* @param item the insert item. Null will clear the insertion mark.
* @param column the column of the cell. Null will make the insertion mark span all columns.
* @param before true places the insert mark above 'item'. false places
* the insert mark below 'item'.
*
* @exception IllegalArgumentException <ul>
* <li>ERROR_INVALID_ARGUMENT - if the item or column has been disposed</li>
* </ul>
* @exception SWTException <ul>
* <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
* <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
* </ul>
*/
void setInsertMark (GridItem item, GridColumn column, boolean before) {
checkWidget ();
if (item != null) {
if (item.isDisposed())
SWT.error(SWT.ERROR_INVALID_ARGUMENT);
}
if (column != null) {
if (column.isDisposed())
SWT.error(SWT.ERROR_INVALID_ARGUMENT);
}
insertMarkItem = item;
insertMarkColumn = column;
insertMarkBefore = before;
redraw();
}
/**
* A helper method for {@link GridDropTargetEffect#dragOver(DropTargetEvent)}.
*
* @param point
* @return true if point is near the top or bottom border of the visible grid area
*/
boolean isInDragScrollArea(Point point) {
int rhw = rowHeaderVisible ? rowHeaderWidth : 0;
int chh = columnHeadersVisible ? headerHeight : 0;
Rectangle top = new Rectangle(rhw, chh, getClientArea().width - rhw, DRAG_SCROLL_AREA_HEIGHT);
Rectangle bottom = new Rectangle(rhw, getClientArea().height - DRAG_SCROLL_AREA_HEIGHT, getClientArea().width - rhw, DRAG_SCROLL_AREA_HEIGHT);
return top.contains(point) || bottom.contains(point);
}
/**
* 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 SWTException <ul>
* <li>ERROR_WIDGET_DISPOSED - 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);
}
GridItem item = getItem(index);
item.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 SWTException <ul>
* <li>ERROR_WIDGET_DISPOSED - 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) return;
int count = items.size();
if (!(0 <= start && start <= end && end < count)) {
SWT.error(SWT.ERROR_INVALID_RANGE);
}
for (int i=start; i<=end; i++) {
GridItem item = (GridItem)items.get(i);
item.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 SWTException <ul>
* <li>ERROR_WIDGET_DISPOSED - 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) return;
int count = items.size();
for (int i=0; i<indices.length; i++) {
if (!(0 <= indices[i] && indices[i] < count)) {
SWT.error(SWT.ERROR_INVALID_RANGE);
}
}
for (int i=0; i<indices.length; i++) {
GridItem item = (GridItem)items.get(indices[i]);
item.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 SWTException <ul>
* <li>ERROR_WIDGET_DISPOSED - 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();
if (items.size() > 0)
clear(0, items.size()-1, allChildren);
}
/**
* Recalculate the height of the header
*/
public void recalculateHeader() {
int previous = getHeaderHeight();
computeHeaderHeight(sizingGC);
if( previous != getHeaderHeight() ) {
scrollValuesObsolete = true;
redraw();
}
}
/**
* Query the grid for all currently visible rows and columns
* <p>
* <b>This support is provisional and may change</b>
* </p>
* @return all currently visible rows and columns
*/
public GridVisibleRange getVisibleRange() {
//FIXME I think we should remember the topIndex in the onPaint-method
int topIndex = getTopIndex();
int bottomIndex = getBottomIndex();
int startColumnIndex = getStartColumnIndex();
int endColumnIndex = getEndColumnIndex();
GridVisibleRange range = new GridVisibleRange();
range.items = new GridItem[0];
range.columns = new GridColumn[0];
if( topIndex <= bottomIndex ) {
if( items.size() > 0 ) {
range.items = new GridItem[bottomIndex - topIndex + 1];
for( int i = topIndex; i <= bottomIndex; i++ ) {
range.items[i - topIndex] = (GridItem) items.get(i);
}
}
}
if( startColumnIndex <= endColumnIndex ) {
if( displayOrderedColumns.size() > 0 ) {
ArrayList cols = new ArrayList();
for( int i = startColumnIndex; i <= endColumnIndex; i++ ) {
GridColumn col = (GridColumn) displayOrderedColumns.get(i);
if( col.isVisible() ) {
cols.add(col);
}
}
range.columns = new GridColumn[cols.size()];
cols.toArray(range.columns);
}
}
return range;
}
int getStartColumnIndex() {
checkWidget();
if( startColumnIndex != -1 ) {
return startColumnIndex;
}
if( !hScroll.getVisible() ) {
startColumnIndex = 0;
}
startColumnIndex = hScroll.getSelection();
return startColumnIndex;
}
int getEndColumnIndex() {
checkWidget();
if( endColumnIndex != -1 ) {
return endColumnIndex;
}
if( displayOrderedColumns.size() == 0 ) {
endColumnIndex = 0;
} else if( getVisibleGridWidth() < 1 ) {
endColumnIndex = getStartColumnIndex();
} else {
int x = 0;
x -= getHScrollSelectionInPixels();
if (rowHeaderVisible)
{
//row header is actually painted later
x += rowHeaderWidth;
}
int startIndex = getStartColumnIndex();
GridColumn[] columns = new GridColumn[displayOrderedColumns.size()];
displayOrderedColumns.toArray(columns);
for (int i = startIndex; i < columns.length; i++)
{
endColumnIndex = i;
GridColumn column = columns[i];
if (column.isVisible())
{
x += column.getWidth();
}
if (x > getClientArea().width) {
break;
}
}
}
endColumnIndex = Math.max(0, endColumnIndex);
return endColumnIndex;
}
void setSizeOnEveryItemImageChange(boolean sizeOnEveryItemImageChange) {
this.sizeOnEveryItemImageChange = sizeOnEveryItemImageChange;
}
/**
* Returns the width of the row headers.
*
* @return width 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 getRowHeaderWidth() {
checkWidget();
return rowHeaderWidth;
}
/**
* Sets the value of the auto-height feature. When enabled, this feature resizes the height of rows to
* reflect the content of cells with word-wrapping enabled. Cell word-wrapping is enabled via the GridColumn.setWordWrap(boolean) method.
* If column headers have word-wrapping enabled, this feature will also resize the height of the column headers as necessary.
* @param enabled Set to true to enable this feature, false (default) otherwise.
*/
public void setAutoHeight(boolean enabled)
{
if (autoHeight == enabled)
return;
checkWidget();
autoHeight = enabled;
setRowsResizeable(false); // turn of resizing of row height since it conflicts with this property
redraw();
}
/**
* Returns the value of the auto-height feature, which resizes row heights and column header heights based on word-wrapped content.
* @return Returns whether or not the auto-height feature is enabled.
* @see #setAutoHeight(boolean)
*/
public boolean isAutoHeight()
{
return autoHeight;
}
/**
* Sets the value of the auto-width feature. When enabled, this feature resizes the width of the row headers to
* reflect the content of row headers.
* @param enabled Set to true to enable this feature, false (default) otherwise.
* @see #isAutoWidth()
*/
public void setAutoWidth(boolean enabled)
{
if (autoWidth == enabled)
return;
checkWidget();
autoWidth = enabled;
redraw();
}
/**
* Returns the value of the auto-height feature, which resizes row header width based on content.
* @return Returns whether or not the auto-width feature is enabled.
* @see #setAutoWidth(boolean)
*/
public boolean isAutoWidth()
{
return autoWidth;
}
/**
* Sets the value of the word-wrap feature for row headers. When enabled, this feature will word-wrap the contents of row headers.
* @param enabled Set to true to enable this feature, false (default) otherwise.
* @see #isWordWrapHeader()
*/
public void setWordWrapHeader(boolean enabled)
{
if (wordWrapRowHeader == enabled)
return;
checkWidget();
wordWrapRowHeader = enabled;
redraw();
}
/**
* Returns the value of the row header word-wrap feature, which word-wraps the content of row headers.
* @return Returns whether or not the row header word-wrap feature is enabled.
* @see #setWordWrapHeader(boolean)
*/
public boolean isWordWrapHeader()
{
return wordWrapRowHeader;
}
}