/******************************************************************************* * Copyright (c) 2006 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * chris.gross@us.ibm.com - initial API and implementation * Chuck.Mastrandrea@sas.com - wordwrapping in bug 222280 * smcduff@hotmail.com - wordwrapping in bug 222280 * Claes Rosell<claes.rosell@solme.se> - rowspan in bug 272384 * Marco Maccaferri<macca@maccasoft.com> - fixed arrow scrolling in bug 294767 * higerinbeijing@gmail.com . fixed selectionEvent.item in bug 286617 * balarkrishnan@yahoo.com - fix in bug 298684 * Enrico Schnepel<enrico.schnepel@randomice.net> - new API in 238729, bugfix in 294952, 322114 * Benjamin Bortfeldt<bbortfeldt@gmail.com> - new tooltip support in 300797 * Thomas Halm <thha@fernbach.com> - bugfix in 315397 * Justin Dolezy <justin@neckdiagrams.com> - bugfix in 316598 * Cosmin Ghita <cghita@ansis.eu> - bugfix in 323687 * Pinard-Legry Guilhaume <guilhaume_pl@yahoo.fr> - bugfix in 267057 * Thorsten Schenkel <thorsten.schenkel@compeople.de> - bugfix in 356803 *******************************************************************************/ 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; /** * Getter for draggingColumn. * @return the draggingColumn */ public boolean isDraggingColumn() { return draggingColumn; } 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. */ protected int startColumnIndex = -1; /** * Index of the the last visible column. A value of -1 indicates that the value is old and will be recomputed. */ protected 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; } x += 50; 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 */ protected 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. */ protected 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. */ 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, y); } } return true; } /** * handle column dragging. * @param x * @param y * @return true if this event has been consumed. */ protected boolean handleColumnDragging(int x, int y) { return handleColumnDragging(x); } /** * 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()); 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 // MOD sizhaoliu TDQ-6140 topLeftRenderer.setBounds(0, y, getBounds().width, headerHeight); // 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) { paintDraggingColumn(gc, getColumnHeaderXPosition(columnBeingPushed) - startHeaderDragX, COLUMN_DRAG_ALPHA); } } /** * paint dragging column. * MOD sizhaoliu TDQ-6010 extract this method so that it can be customized. * @param gc * @param offset * @param alpha */ protected void paintDraggingColumn(GC gc, int offset, int alpha) { int y = 0; gc.setAlpha(alpha); columnBeingPushed.getHeaderRenderer().setSelected(true); int height = 0; if (columnBeingPushed.getColumnGroup() != null) { height = headerHeight - groupHeaderHeight; y = groupHeaderHeight; } else { height = headerHeight; y = 0; } columnBeingPushed.getHeaderRenderer() .setBounds(currentHeaderDragX - columnBeingPushed.getWidth()/2 + offset , 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, e.y); 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; // MOD sizhaoliu TDQ-6010 init visible column index so that they are recalculated startColumnIndex = -1; endColumnIndex = -1; } /** * Scrollbar selection event handler. */ private void onScrollSelection() { topIndex = -1; bottomIndex = -1; // MOD sizhaoliu TDQ-6010 init visible column index so that they are recalculated startColumnIndex = -1; endColumnIndex = -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; // MOD sizhaoliu TDQ-6010 remove the following lines to correctly calculate the endColumnIndex // 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; } }