| /******************************************************************************* |
| * Copyright (c) 2000, 2016 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: |
| * IBM Corporation - initial API and implementation |
| *******************************************************************************/ |
| package org.eclipse.swt.examples.accessibility; |
| |
| 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.accessibility.AccessibleTableAdapter; |
| import org.eclipse.swt.accessibility.AccessibleTableEvent; |
| import org.eclipse.swt.events.SelectionEvent; |
| import org.eclipse.swt.events.SelectionListener; |
| import org.eclipse.swt.graphics.Color; |
| import org.eclipse.swt.graphics.Font; |
| import org.eclipse.swt.graphics.GC; |
| import org.eclipse.swt.graphics.Image; |
| import org.eclipse.swt.graphics.ImageData; |
| import org.eclipse.swt.graphics.PaletteData; |
| 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.Control; |
| import org.eclipse.swt.widgets.Display; |
| import org.eclipse.swt.widgets.Event; |
| import org.eclipse.swt.widgets.Label; |
| import org.eclipse.swt.widgets.Listener; |
| import org.eclipse.swt.widgets.Menu; |
| import org.eclipse.swt.widgets.ScrollBar; |
| import org.eclipse.swt.widgets.Shell; |
| import org.eclipse.swt.widgets.Tracker; |
| import org.eclipse.swt.widgets.TypedListener; |
| import org.eclipse.swt.widgets.Widget; |
| |
| /** |
| * Instances of this class implement a selectable user interface |
| * object that displays a list of images and strings and issues |
| * notification when selected. |
| * <p> |
| * The item children that may be added to instances of this class |
| * must be of type <code>TableItem</code>. |
| * </p><p> |
| * Style <code>VIRTUAL</code> is used to create a <code>Table</code> whose |
| * <code>TableItem</code>s are to be populated by the client on an on-demand basis |
| * instead of up-front. This can provide significant performance improvements for |
| * tables that are very large or for which <code>TableItem</code> population is |
| * expensive (for example, retrieving values from an external source). |
| * </p><p> |
| * Here is an example of using a <code>Table</code> with style <code>VIRTUAL</code>: |
| * <code><pre> |
| * final Table table = new Table (parent, SWT.VIRTUAL | SWT.BORDER); |
| * table.setItemCount (1000000); |
| * table.addListener (SWT.SetData, new Listener () { |
| * public void handleEvent (Event event) { |
| * TableItem item = (TableItem) event.item; |
| * int index = table.indexOf (item); |
| * item.setText ("Item " + index); |
| * System.out.println (item.getText ()); |
| * } |
| * }); |
| * </pre></code> |
| * </p><p> |
| * Note that although this class is a subclass of <code>Composite</code>, |
| * it does not normally make sense to add <code>Control</code> children to |
| * it, or set a layout on it, unless implementing something like a cell |
| * editor. |
| * </p><p> |
| * <dl> |
| * <dt><b>Styles:</b></dt> |
| * <dd>SINGLE, MULTI, CHECK, FULL_SELECTION, HIDE_SELECTION, VIRTUAL, NO_SCROLL</dd> |
| * <dt><b>Events:</b></dt> |
| * <dd>Selection, DefaultSelection, SetData, MeasureItem, EraseItem, PaintItem</dd> |
| * </dl> |
| * </p><p> |
| * Note: Only one of the styles SINGLE, and MULTI may be specified. |
| * </p><p> |
| * IMPORTANT: This class is <em>not</em> intended to be subclassed. |
| * </p> |
| * |
| * @see <a href="http://www.eclipse.org/swt/snippets/#table">Table, TableItem, TableColumn snippets</a> |
| * @see <a href="http://www.eclipse.org/swt/examples.php">SWT Example: ControlExample</a> |
| * @see <a href="http://www.eclipse.org/swt/">Sample code and further information</a> |
| * @noextend This class is not intended to be subclassed by clients. |
| */ |
| public class CTable extends Composite { |
| Canvas header; |
| CTableColumn[] columns = new CTableColumn [0]; |
| CTableColumn[] orderedColumns; |
| CTableItem[] items = new CTableItem [0]; |
| CTableItem[] selectedItems = new CTableItem [0]; |
| CTableItem focusItem, anchorItem, lastClickedItem; |
| Event lastSelectionEvent; |
| boolean linesVisible, ignoreKey, ignoreDispose, customHeightSet; |
| int itemsCount = 0; |
| int topIndex = 0, horizontalOffset = 0; |
| int fontHeight = 0, imageHeight = 0, itemHeight = 0; |
| int col0ImageWidth = 0; |
| int headerImageHeight = 0; |
| CTableColumn resizeColumn; |
| int resizeColumnX = -1; |
| int drawCount = 0; |
| CTableColumn sortColumn; |
| int sortDirection = SWT.NONE; |
| |
| /* column header tooltip */ |
| Listener toolTipListener; |
| Shell toolTipShell; |
| Label toolTipLabel; |
| |
| Rectangle arrowBounds, checkboxBounds, clientArea; |
| |
| static final int MARGIN_IMAGE = 3; |
| static final int MARGIN_CELL = 1; |
| static final int SIZE_HORIZONTALSCROLL = 5; |
| static final int TOLLERANCE_COLUMNRESIZE = 2; |
| static final int WIDTH_HEADER_SHADOW = 2; |
| static final int WIDTH_CELL_HIGHLIGHT = 1; |
| static final int [] toolTipEvents = new int[] {SWT.MouseExit, SWT.MouseHover, SWT.MouseMove, SWT.MouseDown}; |
| static final String ELLIPSIS = "..."; //$NON-NLS-1$ |
| static final String ID_UNCHECKED = "UNCHECKED"; //$NON-NLS-1$ |
| static final String ID_GRAYUNCHECKED = "GRAYUNCHECKED"; //$NON-NLS-1$ |
| static final String ID_CHECKMARK = "CHECKMARK"; //$NON-NLS-1$ |
| static final String ID_ARROWUP = "ARROWUP"; //$NON-NLS-1$ |
| static final String ID_ARROWDOWN = "ARROWDOWN"; //$NON-NLS-1$ |
| |
| Display display; |
| |
| //TEMPORARY CODE |
| boolean hasFocus; |
| @Override |
| public boolean isFocusControl() { |
| return hasFocus; |
| } |
| |
| /** |
| * Constructs a new instance of this class given its parent |
| * and a style value describing its behavior and appearance. |
| * <p> |
| * The style value is either one of the style constants defined in |
| * class <code>SWT</code> which is applicable to instances of this |
| * class, or must be built by <em>bitwise OR</em>'ing together |
| * (that is, using the <code>int</code> "|" operator) two or more |
| * of those <code>SWT</code> style constants. The class description |
| * lists the style constants that are applicable to the class. |
| * Style bits are also inherited from superclasses. |
| * </p> |
| * |
| * @param parent a composite control which will be the parent of the new instance (cannot be null) |
| * @param style the style of control to construct |
| * |
| * @exception IllegalArgumentException <ul> |
| * <li>ERROR_NULL_ARGUMENT - if the parent is null</li> |
| * </ul> |
| * @exception SWTException <ul> |
| * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the parent</li> |
| * <li>ERROR_INVALID_SUBCLASS - if this class is not an allowed subclass</li> |
| * </ul> |
| * |
| * @see SWT#SINGLE |
| * @see SWT#MULTI |
| * @see SWT#CHECK |
| * @see SWT#FULL_SELECTION |
| * @see SWT#HIDE_SELECTION |
| * @see SWT#VIRTUAL |
| * @see SWT#NO_SCROLL |
| * @see Widget#checkSubclass |
| * @see Widget#getStyle |
| */ |
| public CTable (Composite parent, int style) { |
| super (parent, checkStyle (style)); |
| this.display = parent.getDisplay (); |
| setForeground (null); /* set foreground and background to chosen default colors */ |
| setBackground (null); |
| GC gc = new GC (this); |
| fontHeight = gc.getFontMetrics ().getHeight (); |
| gc.dispose (); |
| itemHeight = fontHeight + (2 * getCellPadding ()); |
| initImages (display); |
| checkboxBounds = getUncheckedImage ().getBounds (); |
| arrowBounds = getArrowDownImage ().getBounds (); |
| clientArea = getClientArea (); |
| |
| Listener listener = event -> handleEvents (event); |
| addListener (SWT.Paint, listener); |
| addListener (SWT.MouseDown, listener); |
| addListener (SWT.MouseUp, listener); |
| addListener (SWT.MouseDoubleClick, listener); |
| addListener (SWT.Dispose, listener); |
| addListener (SWT.Resize, listener); |
| addListener (SWT.KeyDown, listener); |
| addListener (SWT.FocusOut, listener); |
| addListener (SWT.FocusIn, listener); |
| addListener (SWT.Traverse, listener); |
| |
| initAccessibility (); |
| |
| header = new Canvas (this, SWT.NO_REDRAW_RESIZE | SWT.NO_FOCUS); |
| header.setVisible (false); |
| header.setBounds (0, 0, 0, fontHeight + 2 * getHeaderPadding ()); |
| header.addListener (SWT.Paint, listener); |
| header.addListener (SWT.MouseDown, listener); |
| header.addListener (SWT.MouseUp, listener); |
| header.addListener (SWT.MouseHover, listener); |
| header.addListener (SWT.MouseDoubleClick, listener); |
| header.addListener (SWT.MouseMove, listener); |
| header.addListener (SWT.MouseExit, listener); |
| header.addListener (SWT.MenuDetect, listener); |
| |
| toolTipListener = event -> { |
| switch (event.type) { |
| case SWT.MouseHover: |
| case SWT.MouseMove: |
| if (headerUpdateToolTip (event.x)) break; |
| // FALL THROUGH |
| case SWT.MouseExit: |
| case SWT.MouseDown: |
| headerHideToolTip (); |
| break; |
| } |
| }; |
| |
| ScrollBar hBar = getHorizontalBar (); |
| if (hBar != null) { |
| hBar.setValues (0, 0, 1, 1, 1, 1); |
| hBar.setVisible (false); |
| hBar.addListener (SWT.Selection, listener); |
| } |
| ScrollBar vBar = getVerticalBar (); |
| if (vBar != null) { |
| vBar.setValues (0, 0, 1, 1, 1, 1); |
| vBar.setVisible (false); |
| vBar.addListener (SWT.Selection, listener); |
| } |
| } |
| /** |
| * Adds the listener to the collection of listeners who will |
| * be notified when the user changes the receiver's selection, by sending |
| * it one of the messages defined in the <code>SelectionListener</code> |
| * interface. |
| * <p> |
| * When <code>widgetSelected</code> is called, the item field of the event object is valid. |
| * If the receiver has the <code>SWT.CHECK</code> style and the check selection changes, |
| * the event object detail field contains the value <code>SWT.CHECK</code>. |
| * <code>widgetDefaultSelected</code> is typically called when an item is double-clicked. |
| * The item field of the event object is valid for default selection, but the detail field is not used. |
| * </p> |
| * |
| * @param listener the listener which should be notified when the user changes the receiver's selection |
| * |
| * @exception IllegalArgumentException <ul> |
| * <li>ERROR_NULL_ARGUMENT - if the listener is null</li> |
| * </ul> |
| * @exception SWTException <ul> |
| * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> |
| * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> |
| * </ul> |
| * |
| * @see SelectionListener |
| * @see #removeSelectionListener |
| * @see SelectionEvent |
| */ |
| public void addSelectionListener (SelectionListener listener) { |
| checkWidget (); |
| if (listener == null) SWT.error (SWT.ERROR_NULL_ARGUMENT); |
| TypedListener typedListener = new TypedListener (listener); |
| addListener (SWT.Selection, typedListener); |
| addListener (SWT.DefaultSelection, typedListener); |
| } |
| boolean checkData (CTableItem item, boolean redraw) { |
| if (item.cached) return true; |
| if ((getStyle () & SWT.VIRTUAL) != 0) { |
| item.cached = true; |
| Event event = new Event (); |
| event.item = item; |
| event.index = indexOf (item); |
| notifyListeners (SWT.SetData, event); |
| if (isDisposed () || item.isDisposed ()) return false; |
| if (redraw) redrawItem (item.index, false); |
| } |
| return true; |
| } |
| static int checkStyle (int style) { |
| /* |
| * Feature in Windows. Even when WS_HSCROLL or |
| * WS_VSCROLL is not specified, Windows creates |
| * trees and tables with scroll bars. The fix |
| * is to set H_SCROLL and V_SCROLL. |
| * |
| * NOTE: This code appears on all platforms so that |
| * applications have consistent scroll bar behavior. |
| */ |
| if ((style & SWT.NO_SCROLL) == 0) { |
| style |= SWT.H_SCROLL | SWT.V_SCROLL; |
| } |
| style |= SWT.NO_REDRAW_RESIZE | SWT.NO_BACKGROUND | SWT.DOUBLE_BUFFERED; |
| //TEMPORARY CODE |
| style |= SWT.FULL_SELECTION; |
| return checkBits (style, SWT.SINGLE, SWT.MULTI, 0, 0, 0, 0); |
| } |
| static int checkBits (int style, int int0, int int1, int int2, int int3, int int4, int int5) { |
| int mask = int0 | int1 | int2 | int3 | int4 | int5; |
| if ((style & mask) == 0) style |= int0; |
| if ((style & int0) != 0) style = (style & ~mask) | int0; |
| if ((style & int1) != 0) style = (style & ~mask) | int1; |
| if ((style & int2) != 0) style = (style & ~mask) | int2; |
| if ((style & int3) != 0) style = (style & ~mask) | int3; |
| if ((style & int4) != 0) style = (style & ~mask) | int4; |
| if ((style & int5) != 0) style = (style & ~mask) | int5; |
| return style; |
| } |
| /** |
| * 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 |
| * |
| * @exception IllegalArgumentException <ul> |
| * <li>ERROR_INVALID_RANGE - if the index is not between 0 and the number of elements in the list minus 1 (inclusive)</li> |
| * </ul> |
| * @exception SWTException <ul> |
| * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> |
| * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> |
| * </ul> |
| * |
| * @see SWT#VIRTUAL |
| * @see SWT#SetData |
| * |
| * @since 3.0 |
| */ |
| public void clear (int index) { |
| checkWidget (); |
| if (!(0 <= index && index < itemsCount)) SWT.error (SWT.ERROR_INVALID_RANGE); |
| Rectangle bounds = items [index].getBounds (false); |
| int oldRightX = bounds.x + bounds.width; |
| items [index].clear (); |
| if (columns.length == 0) updateHorizontalBar (0, -oldRightX); |
| redrawItem (index, false); |
| } |
| /** |
| * Removes the items from 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 |
| * |
| * @exception IllegalArgumentException <ul> |
| * <li>ERROR_INVALID_RANGE - if either the start or end are not between 0 and the number of elements in the list minus 1 (inclusive)</li> |
| * </ul> |
| * @exception SWTException <ul> |
| * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> |
| * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> |
| * </ul> |
| * |
| * @see SWT#VIRTUAL |
| * @see SWT#SetData |
| * |
| * @since 3.0 |
| */ |
| public void clear (int start, int end) { |
| checkWidget (); |
| if (start > end) return; |
| if (!(0 <= start && start <= end && end < itemsCount)) { |
| SWT.error (SWT.ERROR_INVALID_RANGE); |
| } |
| for (int i = start; i <= end; i++) { |
| items [i].clear (); |
| } |
| updateHorizontalBar (); |
| redrawItems (start, end, false); |
| } |
| /** |
| * 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 |
| * |
| * @exception IllegalArgumentException <ul> |
| * <li>ERROR_INVALID_RANGE - if the index is not between 0 and the number of elements in the list minus 1 (inclusive)</li> |
| * <li>ERROR_NULL_ARGUMENT - if the indices array is null</li> |
| * </ul> |
| * @exception SWTException <ul> |
| * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> |
| * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> |
| * </ul> |
| * |
| * @see SWT#VIRTUAL |
| * @see SWT#SetData |
| * |
| * @since 3.0 |
| */ |
| public void clear (int [] indices) { |
| checkWidget (); |
| if (indices == null) SWT.error (SWT.ERROR_NULL_ARGUMENT); |
| if (indices.length == 0) return; |
| for (int i = 0; i < indices.length; i++) { |
| if (!(0 <= indices [i] && indices [i] < itemsCount)) { |
| SWT.error (SWT.ERROR_INVALID_RANGE); |
| } |
| } |
| |
| for (int index : indices) { |
| items [index].clear (); |
| } |
| updateHorizontalBar (); |
| for (int index : indices) { |
| redrawItem (index, false); |
| } |
| } |
| /** |
| * 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. |
| * |
| * @exception SWTException <ul> |
| * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> |
| * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> |
| * </ul> |
| * |
| * @see SWT#VIRTUAL |
| * @see SWT#SetData |
| * |
| * @since 3.0 |
| */ |
| public void clearAll () { |
| checkWidget (); |
| clear (0, itemsCount - 1); |
| } |
| /* |
| * Returns the ORDERED index of the column that the specified x falls within, |
| * or -1 if the x lies to the right of the last column. |
| */ |
| int computeColumnIntersect (int x, int startColumn) { |
| CTableColumn[] orderedColumns = getOrderedColumns (); |
| if (orderedColumns.length - 1 < startColumn) return -1; |
| int rightX = orderedColumns [startColumn].getX (); |
| for (int i = startColumn; i < orderedColumns.length; i++) { |
| rightX += orderedColumns [i].width; |
| if (x < rightX) return i; |
| } |
| return -1; |
| } |
| @Override |
| public Point computeSize (int wHint, int hHint, boolean changed) { |
| checkWidget (); |
| int width = 0, height = 0; |
| if (wHint != SWT.DEFAULT) { |
| width = wHint; |
| } else { |
| if (columns.length == 0) { |
| for (int i = 0; i < itemsCount; i++) { |
| Rectangle itemBounds = items [i].getBounds (false); |
| width = Math.max (width, itemBounds.x + itemBounds.width); |
| } |
| } else { |
| CTableColumn[] orderedColumns = getOrderedColumns (); |
| CTableColumn lastColumn = orderedColumns [orderedColumns.length - 1]; |
| width = lastColumn.getX () + lastColumn.width; |
| } |
| } |
| if (hHint != SWT.DEFAULT) { |
| height = hHint; |
| } else { |
| height = getHeaderHeight () + itemsCount * itemHeight; |
| } |
| Rectangle result = computeTrim (0, 0, width, height); |
| return new Point (result.width, result.height); |
| } |
| void createItem (CTableColumn column, int index) { |
| CTableColumn[] newColumns = new CTableColumn [columns.length + 1]; |
| System.arraycopy (columns, 0, newColumns, 0, index); |
| newColumns [index] = column; |
| System.arraycopy (columns, index, newColumns, index + 1, columns.length - index); |
| columns = newColumns; |
| |
| if (orderedColumns != null) { |
| int insertIndex = 0; |
| if (index > 0) { |
| insertIndex = columns [index - 1].getOrderIndex () + 1; |
| } |
| CTableColumn[] newOrderedColumns = new CTableColumn [orderedColumns.length + 1]; |
| System.arraycopy (orderedColumns, 0, newOrderedColumns, 0, insertIndex); |
| newOrderedColumns [insertIndex] = column; |
| System.arraycopy ( |
| orderedColumns, |
| insertIndex, |
| newOrderedColumns, |
| insertIndex + 1, |
| orderedColumns.length - insertIndex); |
| orderedColumns = newOrderedColumns; |
| } |
| |
| /* allow all items to update their internal structures accordingly */ |
| for (int i = 0; i < itemsCount; i++) { |
| items [i].addColumn (column); |
| } |
| |
| /* existing items become hidden when going from 0 to 1 column (0 width) */ |
| if (columns.length == 1 && itemsCount > 0) { |
| redrawFromItemDownwards (topIndex); |
| } else { |
| /* checkboxes become hidden when creating a column with index == orderedIndex == 0 (0 width) */ |
| if (itemsCount > 0 && (getStyle () & SWT.CHECK) != 0 && index == 0 && column.getOrderIndex () == 0) { |
| redrawFromItemDownwards (topIndex); |
| } |
| } |
| |
| /* Columns were added, so notify the accessible. */ |
| int[] eventData = new int[5]; |
| eventData[0] = ACC.INSERT; |
| eventData[1] = 0; |
| eventData[2] = 0; |
| eventData[3] = index; |
| eventData[4] = 1; |
| getAccessible().sendEvent(ACC.EVENT_TABLE_CHANGED, eventData); |
| } |
| void createItem (CTableItem item) { |
| int index = item.index; |
| if (itemsCount == items.length) { |
| int grow = drawCount <= 0 ? 4 : Math.max (4, items.length * 3 / 2); |
| CTableItem[] newItems = new CTableItem [items.length + grow]; |
| System.arraycopy (items, 0, newItems, 0, items.length); |
| items = newItems; |
| } |
| if (index != itemsCount) { |
| /* new item is not at end of list, so shift other items right to create space for it */ |
| System.arraycopy (items, index, items, index + 1, itemsCount - index); |
| } |
| items [index] = item; |
| itemsCount++; |
| |
| /* update the index for items bumped down by this new item */ |
| for (int i = index + 1; i < itemsCount; i++) { |
| items [i].index = i; |
| } |
| |
| /* Rows were added, so notify the accessible. */ |
| int[] eventData = new int[5]; |
| eventData[0] = ACC.INSERT; |
| eventData[1] = index; |
| eventData[2] = 1; |
| eventData[3] = 0; |
| eventData[4] = 0; |
| getAccessible().sendEvent(ACC.EVENT_TABLE_CHANGED, eventData); |
| |
| /* update scrollbars */ |
| updateVerticalBar (); |
| Rectangle bounds = item.getBounds (false); |
| int rightX = bounds.x + bounds.width; |
| updateHorizontalBar (rightX, rightX); |
| /* |
| * If new item is above viewport then adjust topIndex and the vertical |
| * scrollbar so that the current viewport items will not change. |
| */ |
| if (item.index < topIndex) { |
| topIndex++; |
| ScrollBar vBar = getVerticalBar (); |
| if (vBar != null) vBar.setSelection (topIndex); |
| return; |
| } |
| /* |
| * If this is the first item and the receiver has focus then its boundary |
| * focus ring must be removed. |
| */ |
| if (itemsCount == 1 && isFocusControl ()) { |
| focusItem = item; |
| redraw (); |
| return; |
| } |
| if (item.isInViewport ()) { |
| redrawFromItemDownwards (index); |
| } |
| } |
| /** |
| * Deselects the item at the given zero-relative index in the receiver. |
| * If the item at the index was already deselected, it remains |
| * deselected. Indices that are out of range are ignored. |
| * |
| * @param index the index of the item to deselect |
| * |
| * @exception SWTException <ul> |
| * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> |
| * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> |
| * </ul> |
| */ |
| public void deselect (int index) { |
| checkWidget (); |
| if (!(0 <= index && index < itemsCount)) return; |
| CTableItem item = items [index]; |
| int selectIndex = getSelectionIndex (item); |
| if (selectIndex == -1) return; |
| |
| CTableItem[] newSelectedItems = new CTableItem [selectedItems.length - 1]; |
| System.arraycopy (selectedItems, 0, newSelectedItems, 0, selectIndex); |
| System.arraycopy (selectedItems, selectIndex + 1, newSelectedItems, selectIndex, newSelectedItems.length - selectIndex); |
| selectedItems = newSelectedItems; |
| |
| if (isFocusControl () || (getStyle () & SWT.HIDE_SELECTION) == 0) { |
| redrawItem (item.index, false); |
| } |
| getAccessible().selectionChanged(); |
| } |
| /** |
| * Deselects the items at the given zero-relative indices in the receiver. |
| * If the item at the given zero-relative index in the receiver |
| * is selected, it is deselected. If the item at the index |
| * was not selected, it remains deselected. The range of the |
| * indices is inclusive. Indices that are out of range are ignored. |
| * |
| * @param start the start index of the items to deselect |
| * @param end the end index of the items to deselect |
| * |
| * @exception SWTException <ul> |
| * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> |
| * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> |
| * </ul> |
| */ |
| public void deselect (int start, int end) { |
| checkWidget (); |
| if (start == 0 && end == itemsCount - 1) { |
| deselectAll (); |
| } else { |
| start = Math.max (start, 0); |
| end = Math.min (end, itemsCount - 1); |
| for (int i = start; i <= end; i++) { |
| deselect (i); |
| } |
| } |
| } |
| /** |
| * Deselects the items at the given zero-relative indices in the receiver. |
| * If the item at the given zero-relative index in the receiver |
| * is selected, it is deselected. If the item at the index |
| * was not selected, it remains deselected. Indices that are out |
| * of range and duplicate indices are ignored. |
| * |
| * @param indices the array of indices for the items to deselect |
| * |
| * @exception IllegalArgumentException <ul> |
| * <li>ERROR_NULL_ARGUMENT - if the set of indices is null</li> |
| * </ul> |
| * @exception SWTException <ul> |
| * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> |
| * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> |
| * </ul> |
| */ |
| public void deselect (int [] indices) { |
| checkWidget (); |
| if (indices == null) SWT.error (SWT.ERROR_NULL_ARGUMENT); |
| if (indices.length == 0) return; |
| for (int index : indices) { |
| deselect (index); |
| } |
| } |
| /** |
| * Deselects all selected items in the receiver. |
| * |
| * @exception SWTException <ul> |
| * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> |
| * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> |
| * </ul> |
| */ |
| public void deselectAll () { |
| checkWidget (); |
| CTableItem[] oldSelection = selectedItems; |
| selectedItems = new CTableItem [0]; |
| if (isFocusControl () || (getStyle () & SWT.HIDE_SELECTION) == 0) { |
| for (CTableItem element : oldSelection) { |
| redrawItem (element.index, true); |
| } |
| } |
| for (CTableItem element : oldSelection) { |
| element.getAccessible(getAccessible(), 0).selectionChanged(); |
| } |
| if (oldSelection.length > 0) getAccessible().selectionChanged(); |
| } |
| void deselectItem (CTableItem item) { |
| int index = getSelectionIndex (item); |
| if (index == -1) return; |
| CTableItem[] newSelectedItems = new CTableItem [selectedItems.length - 1]; |
| System.arraycopy (selectedItems, 0, newSelectedItems, 0, index); |
| System.arraycopy ( |
| selectedItems, |
| index + 1, |
| newSelectedItems, |
| index, |
| newSelectedItems.length - index); |
| selectedItems = newSelectedItems; |
| item.getAccessible(getAccessible(), 0).selectionChanged(); |
| } |
| void destroyItem (CTableColumn column) { |
| headerHideToolTip (); |
| int index = column.getIndex (); |
| int orderedIndex = column.getOrderIndex (); |
| |
| CTableColumn[] newColumns = new CTableColumn [columns.length - 1]; |
| System.arraycopy (columns, 0, newColumns, 0, index); |
| System.arraycopy (columns, index + 1, newColumns, index, newColumns.length - index); |
| columns = newColumns; |
| |
| if (orderedColumns != null) { |
| if (columns.length < 2) { |
| orderedColumns = null; |
| } else { |
| int removeIndex = column.getOrderIndex (); |
| CTableColumn[] newOrderedColumns = new CTableColumn [orderedColumns.length - 1]; |
| System.arraycopy (orderedColumns, 0, newOrderedColumns, 0, removeIndex); |
| System.arraycopy ( |
| orderedColumns, |
| removeIndex + 1, |
| newOrderedColumns, |
| removeIndex, |
| newOrderedColumns.length - removeIndex); |
| orderedColumns = newOrderedColumns; |
| } |
| } |
| |
| /* ensure that column 0 always has left-alignment */ |
| if (index == 0 && columns.length > 0) { |
| int style = columns [0].getStyle (); |
| style |= SWT.LEFT; |
| style &= ~(SWT.CENTER | SWT.RIGHT); |
| columns [0].setStyle (style); |
| } |
| |
| /* allow all items to update their internal structures accordingly */ |
| for (int i = 0; i < itemsCount; i++) { |
| items [i].removeColumn (column, index); |
| } |
| |
| /* update horizontal scrollbar */ |
| int lastColumnIndex = columns.length - 1; |
| if (lastColumnIndex < 0) { /* no more columns */ |
| updateHorizontalBar (); |
| } else { |
| int newWidth = 0; |
| for (CTableColumn column2 : columns) { |
| newWidth += column2.width; |
| } |
| ScrollBar hBar = getHorizontalBar (); |
| if (hBar != null) { |
| hBar.setMaximum (newWidth); |
| hBar.setVisible (clientArea.width < newWidth); |
| } |
| int selection = hBar.getSelection (); |
| if (selection != horizontalOffset) { |
| horizontalOffset = selection; |
| redraw (); |
| if (header.isVisible () && drawCount <= 0) header.redraw (); |
| } |
| } |
| CTableColumn[] orderedColumns = getOrderedColumns (); |
| for (int i = orderedIndex; i < orderedColumns.length; i++) { |
| if (!orderedColumns [i].isDisposed ()) { |
| orderedColumns [i].notifyListeners (SWT.Move, new Event ()); |
| } |
| } |
| |
| int[] eventData = new int[5]; |
| eventData[0] = ACC.DELETE; |
| eventData[1] = 0; |
| eventData[2] = 0; |
| eventData[3] = index; |
| eventData[4] = 1; |
| getAccessible().sendEvent(ACC.EVENT_TABLE_CHANGED, eventData); |
| |
| if (sortColumn == column) { |
| sortColumn = null; |
| } |
| } |
| /* |
| * Allows the Table to update internal structures it has that may contain the |
| * item being destroyed. |
| */ |
| void destroyItem (CTableItem item) { |
| if (item == focusItem) reassignFocus (); |
| |
| int index = item.index; |
| Rectangle bounds = item.getBounds (false); |
| int rightX = bounds.x + bounds.width; |
| |
| if (index != itemsCount - 1) { |
| /* item is not at end of items list, so must shift items left to reclaim its slot */ |
| System.arraycopy (items, index + 1, items, index, itemsCount - index - 1); |
| items [itemsCount - 1] = null; |
| } else { |
| items [index] = null; /* last item, so no array copy needed */ |
| } |
| itemsCount--; |
| |
| if (drawCount <= 0 && items.length - itemsCount == 4) { |
| /* shrink the items array */ |
| CTableItem[] newItems = new CTableItem [itemsCount]; |
| System.arraycopy (items, 0, newItems, 0, newItems.length); |
| items = newItems; |
| } |
| |
| /* update the index on affected items */ |
| for (int i = index; i < itemsCount; i++) { |
| items [i].index = i; |
| } |
| item.index = -1; |
| |
| int oldTopIndex = topIndex; |
| updateVerticalBar (); |
| updateHorizontalBar (0, -rightX); |
| /* |
| * If destroyed item is above viewport then adjust topIndex and the vertical |
| * scrollbar so that the current viewport items will not change. |
| */ |
| if (index < topIndex) { |
| topIndex = oldTopIndex - 1; |
| ScrollBar vBar = getVerticalBar (); |
| if (vBar != null) vBar.setSelection (topIndex); |
| } |
| |
| /* selectedItems array */ |
| if (item.isSelected ()) { |
| int selectionIndex = getSelectionIndex (item); |
| CTableItem[] newSelectedItems = new CTableItem [selectedItems.length - 1]; |
| System.arraycopy (selectedItems, 0, newSelectedItems, 0, selectionIndex); |
| System.arraycopy ( |
| selectedItems, |
| selectionIndex + 1, |
| newSelectedItems, |
| selectionIndex, |
| newSelectedItems.length - selectionIndex); |
| selectedItems = newSelectedItems; |
| } |
| if (item == anchorItem) anchorItem = null; |
| if (item == lastClickedItem) lastClickedItem = null; |
| /* |
| * If this was the last item and the receiver has focus then its boundary |
| * focus ring must be redrawn. |
| */ |
| if (itemsCount == 0 && isFocusControl ()) { |
| redraw (); |
| } |
| |
| int[] eventData = new int[5]; |
| eventData[0] = ACC.DELETE; |
| eventData[1] = index; |
| eventData[2] = 1; |
| eventData[3] = 0; |
| eventData[4] = 0; |
| getAccessible().sendEvent(ACC.EVENT_TABLE_CHANGED, eventData); |
| } |
| Image getArrowDownImage () { |
| return (Image) display.getData (ID_ARROWDOWN); |
| } |
| Image getArrowUpImage () { |
| return (Image) display.getData (ID_ARROWUP); |
| } |
| int getCellPadding () { |
| return MARGIN_CELL + WIDTH_CELL_HIGHLIGHT; |
| } |
| Image getCheckmarkImage () { |
| return (Image) display.getData (ID_CHECKMARK); |
| } |
| @Override |
| public Control[] getChildren () { |
| checkWidget (); |
| Control[] controls = super.getChildren (); |
| if (header == null) return controls; |
| Control[] result = new Control [controls.length - 1]; |
| /* remove the Header from the returned set of children */ |
| int index = 0; |
| for (Control control : controls) { |
| if (control != header) { |
| result [index++] = control; |
| } |
| } |
| return result; |
| } |
| /** |
| * Returns the column at the given, zero-relative index in the |
| * receiver. Throws an exception if the index is out of range. |
| * Columns are returned in the order that they were created. |
| * If no <code>TableColumn</code>s were created by the programmer, |
| * this method will throw <code>ERROR_INVALID_RANGE</code> despite |
| * the fact that a single column of data may be visible in the table. |
| * This occurs when the programmer uses the table like a list, adding |
| * items but never creating a column. |
| * |
| * @param index the index of the column to return |
| * @return the column at the given index |
| * |
| * @exception IllegalArgumentException <ul> |
| * <li>ERROR_INVALID_RANGE - if the index is not between 0 and the number of elements in the list minus 1 (inclusive)</li> |
| * </ul> |
| * @exception SWTException <ul> |
| * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> |
| * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> |
| * </ul> |
| * |
| * @see CTable#getColumnOrder() |
| * @see CTable#setColumnOrder(int[]) |
| * @see CTableColumn#getMoveable() |
| * @see CTableColumn#setMoveable(boolean) |
| * @see SWT#Move |
| */ |
| public CTableColumn getColumn (int index) { |
| checkWidget (); |
| if (!(0 <= index && index < columns.length)) SWT.error (SWT.ERROR_INVALID_RANGE); |
| return columns [index]; |
| } |
| /** |
| * Returns the number of columns contained in the receiver. |
| * If no <code>TableColumn</code>s were created by the programmer, |
| * this value is zero, despite the fact that visually, one column |
| * of items may be visible. This occurs when the programmer uses |
| * the table like a list, adding items but never creating a column. |
| * |
| * @return the number of columns |
| * |
| * @exception SWTException <ul> |
| * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> |
| * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> |
| * </ul> |
| */ |
| public int getColumnCount () { |
| checkWidget (); |
| return columns.length; |
| } |
| /** |
| * 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 |
| * |
| * @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 CTable#setColumnOrder(int[]) |
| * @see CTableColumn#getMoveable() |
| * @see CTableColumn#setMoveable(boolean) |
| * @see SWT#Move |
| * |
| * @since 3.1 |
| */ |
| public int[] getColumnOrder () { |
| checkWidget (); |
| int[] result = new int [columns.length]; |
| if (orderedColumns != null) { |
| for (int i = 0; i < result.length; i++) { |
| result [i] = orderedColumns [i].getIndex (); |
| } |
| } else { |
| for (int i = 0; i < columns.length; i++) { |
| result [i] = i; |
| } |
| } |
| return result; |
| } |
| /** |
| * Returns an array of <code>TableColumn</code>s which are the |
| * columns in the receiver. Columns are returned in the order |
| * that they were created. If no <code>TableColumn</code>s were |
| * created by the programmer, the array is empty, despite the fact |
| * that visually, one column of items may be visible. This occurs |
| * when the programmer uses the table like a list, adding items but |
| * never creating a column. |
| * <p> |
| * Note: This is not the actual structure used by the receiver |
| * to maintain its list of items, so modifying the array will |
| * not affect the receiver. |
| * </p> |
| * |
| * @return the items in the receiver |
| * |
| * @exception SWTException <ul> |
| * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> |
| * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> |
| * </ul> |
| * |
| * @see CTable#getColumnOrder() |
| * @see CTable#setColumnOrder(int[]) |
| * @see CTableColumn#getMoveable() |
| * @see CTableColumn#setMoveable(boolean) |
| * @see SWT#Move |
| */ |
| public CTableColumn[] getColumns () { |
| checkWidget (); |
| CTableColumn[] result = new CTableColumn [columns.length]; |
| System.arraycopy (columns, 0, result, 0, columns.length); |
| return result; |
| } |
| Image getGrayUncheckedImage () { |
| return (Image) display.getData (ID_GRAYUNCHECKED); |
| } |
| /** |
| * Returns the width in pixels of a grid line. |
| * |
| * @return the width of a grid line in pixels |
| * |
| * @exception SWTException <ul> |
| * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> |
| * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> |
| * </ul> |
| */ |
| public int getGridLineWidth () { |
| checkWidget (); |
| return 1; |
| } |
| /** |
| * Returns the height of the receiver's header |
| * |
| * @return the height of the header or zero if the header is not visible |
| * |
| * @exception SWTException <ul> |
| * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> |
| * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> |
| * </ul> |
| * |
| * @since 2.0 |
| */ |
| public int getHeaderHeight () { |
| checkWidget (); |
| if (!header.getVisible ()) return 0; |
| return header.getSize ().y; |
| } |
| int getHeaderPadding () { |
| return MARGIN_CELL + WIDTH_HEADER_SHADOW; |
| } |
| /** |
| * Returns <code>true</code> if the receiver's header is visible, |
| * and <code>false</code> otherwise. |
| * <p> |
| * If one of the receiver's ancestors is not visible or some |
| * other condition makes the receiver not visible, this method |
| * may still indicate that it is considered visible even though |
| * it may not actually be showing. |
| * </p> |
| * |
| * @return the receiver's header's visibility state |
| * |
| * @exception SWTException <ul> |
| * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> |
| * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> |
| * </ul> |
| */ |
| public boolean getHeaderVisible () { |
| checkWidget (); |
| return header.getVisible (); |
| } |
| /** |
| * Returns the item at the given, zero-relative index in the |
| * receiver. Throws an exception if the index is out of range. |
| * |
| * @param index the index of the item to return |
| * @return the item at the given index |
| * |
| * @exception IllegalArgumentException <ul> |
| * <li>ERROR_INVALID_RANGE - if the index is not between 0 and the number of elements in the list minus 1 (inclusive)</li> |
| * </ul> |
| * @exception SWTException <ul> |
| * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> |
| * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> |
| * </ul> |
| */ |
| public CTableItem getItem (int index) { |
| checkWidget (); |
| if (!(0 <= index && index < itemsCount)) SWT.error (SWT.ERROR_INVALID_RANGE); |
| return items [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. |
| * <p> |
| * The item that is returned represents an item that could be selected by the user. |
| * For example, if selection only occurs in items in the first column, then null is |
| * returned if the point is outside of the item. |
| * Note that the SWT.FULL_SELECTION style hint, which specifies the selection policy, |
| * determines the extent of the selection. |
| * </p> |
| * |
| * @param point the point used to locate the item |
| * @return the item at the given point, or null if the point is not in a selectable item |
| * |
| * @exception IllegalArgumentException <ul> |
| * <li>ERROR_NULL_ARGUMENT - if the point is null</li> |
| * </ul> |
| * @exception SWTException <ul> |
| * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> |
| * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> |
| * </ul> |
| */ |
| public CTableItem getItem (Point point) { |
| checkWidget (); |
| if (point == null) SWT.error (SWT.ERROR_NULL_ARGUMENT); |
| int index = (point.y - getHeaderHeight ()) / itemHeight + topIndex; |
| if (!(0 <= index && index < itemsCount)) return null; /* below the last item */ |
| CTableItem result = items [index]; |
| if (!result.getHitBounds ().contains (point)) return null; /* considers the x value */ |
| return result; |
| } |
| /** |
| * Returns the number of items contained in the receiver. |
| * |
| * @return the number of items |
| * |
| * @exception SWTException <ul> |
| * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> |
| * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> |
| * </ul> |
| */ |
| public int getItemCount () { |
| checkWidget (); |
| return itemsCount; |
| } |
| /** |
| * Returns the height of the area which would be used to |
| * display <em>one</em> of the items in the receiver. |
| * |
| * @return the height of one item |
| * |
| * @exception SWTException <ul> |
| * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> |
| * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> |
| * </ul> |
| */ |
| public int getItemHeight () { |
| checkWidget (); |
| return itemHeight; |
| } |
| /** |
| * Returns a (possibly empty) array of <code>TableItem</code>s which |
| * are the items in the receiver. |
| * <p> |
| * Note: This is not the actual structure used by the receiver |
| * to maintain its list of items, so modifying the array will |
| * not affect the receiver. |
| * </p> |
| * |
| * @return the items in the receiver |
| * |
| * @exception SWTException <ul> |
| * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> |
| * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> |
| * </ul> |
| */ |
| public CTableItem[] getItems () { |
| checkWidget (); |
| CTableItem[] result = new CTableItem [itemsCount]; |
| System.arraycopy (items, 0, result, 0, itemsCount); |
| return result; |
| } |
| /* |
| * Returns the current y-coordinate that the specified item should have. |
| */ |
| int getItemY (CTableItem item) { |
| return (item.index - topIndex) * itemHeight + getHeaderHeight (); |
| } |
| /** |
| * Returns <code>true</code> if the receiver's lines are visible, |
| * and <code>false</code> otherwise. Note that some platforms draw |
| * grid lines while others may draw alternating row colors. |
| * <p> |
| * If one of the receiver's ancestors is not visible or some |
| * other condition makes the receiver not visible, this method |
| * may still indicate that it is considered visible even though |
| * it may not actually be showing. |
| * </p> |
| * |
| * @return the visibility state of the lines |
| * |
| * @exception SWTException <ul> |
| * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> |
| * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> |
| * </ul> |
| */ |
| public boolean getLinesVisible () { |
| checkWidget (); |
| return linesVisible; |
| } |
| CTableColumn[] getOrderedColumns () { |
| if (orderedColumns != null) return orderedColumns; |
| return columns; |
| } |
| /** |
| * Returns an array of <code>TableItem</code>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> |
| * @return an array representing the selection |
| * |
| * @exception SWTException <ul> |
| * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> |
| * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> |
| * </ul> |
| */ |
| public CTableItem[] getSelection () { |
| checkWidget (); |
| CTableItem[] result = new CTableItem [selectedItems.length]; |
| System.arraycopy (selectedItems, 0, result, 0, selectedItems.length); |
| sortAscent (result); |
| return result; |
| } |
| /** |
| * Returns the number of selected items contained in the receiver. |
| * |
| * @return the number of selected items |
| * |
| * @exception SWTException <ul> |
| * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> |
| * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> |
| * </ul> |
| */ |
| public int getSelectionCount () { |
| checkWidget (); |
| return selectedItems.length; |
| } |
| /** |
| * Returns the zero-relative index of the item which is currently |
| * selected in the receiver, or -1 if no item is selected. |
| * |
| * @return the index of the selected item |
| * |
| * @exception SWTException <ul> |
| * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> |
| * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> |
| * </ul> |
| */ |
| public int getSelectionIndex () { |
| checkWidget (); |
| if (selectedItems.length == 0) return -1; |
| return selectedItems [0].index; |
| } |
| /* |
| * Returns the index of the argument in the receiver's array of currently- |
| * selected items, or -1 if the item is not currently selected. |
| */ |
| int getSelectionIndex (CTableItem item) { |
| for (int i = 0; i < selectedItems.length; i++) { |
| if (selectedItems [i] == item) return i; |
| } |
| return -1; |
| } |
| /** |
| * 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> |
| * @return the array of indices of the selected items |
| * |
| * @exception SWTException <ul> |
| * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> |
| * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> |
| * </ul> |
| */ |
| public int [] getSelectionIndices () { |
| checkWidget (); |
| int[] result = new int [selectedItems.length]; |
| for (int i = 0; i < selectedItems.length; i++) { |
| result [i] = selectedItems [i].index; |
| } |
| sortAscent (result); |
| return result; |
| } |
| /** |
| * Returns the column which shows the sort indicator for |
| * the receiver. The value may be null if no column shows |
| * the sort indicator. |
| * |
| * @return the sort indicator |
| * |
| * @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 #setSortColumn(CTableColumn) |
| * |
| * @since 3.2 |
| */ |
| public CTableColumn getSortColumn () { |
| checkWidget (); |
| return sortColumn; |
| } |
| /** |
| * Returns the direction of the sort indicator for the receiver. |
| * The value will be one of <code>UP</code>, <code>DOWN</code> |
| * or <code>NONE</code>. |
| * |
| * @return the sort direction |
| * |
| * @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 #setSortDirection(int) |
| * |
| * @since 3.2 |
| */ |
| public int getSortDirection () { |
| checkWidget (); |
| return sortDirection; |
| } |
| /** |
| * Returns the zero-relative index of the item which is currently |
| * at the top of the receiver. This index can change when items are |
| * scrolled or new items are added or removed. |
| * |
| * @return the index of the top item |
| * |
| * @exception SWTException <ul> |
| * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> |
| * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> |
| * </ul> |
| */ |
| public int getTopIndex () { |
| checkWidget (); |
| return topIndex; |
| } |
| Image getUncheckedImage () { |
| return (Image) display.getData (ID_UNCHECKED); |
| } |
| void handleEvents (Event event) { |
| switch (event.type) { |
| case SWT.Paint: |
| if (event.widget == header) { |
| headerOnPaint (event); |
| } else { |
| onPaint (event); |
| } |
| break; |
| case SWT.MenuDetect: { |
| notifyListeners (SWT.MenuDetect, event); |
| break; |
| } |
| case SWT.MouseDown: |
| if (event.widget == header) { |
| headerOnMouseDown (event); |
| } else { |
| onMouseDown (event); |
| } |
| break; |
| case SWT.MouseUp: |
| if (event.widget == header) { |
| headerOnMouseUp (event); |
| } else { |
| onMouseUp (event); |
| } |
| break; |
| case SWT.MouseHover: |
| headerOnMouseHover (event); break; |
| case SWT.MouseMove: |
| headerOnMouseMove (event); break; |
| case SWT.MouseDoubleClick: |
| if (event.widget == header) { |
| headerOnMouseDoubleClick (event); |
| } else { |
| onMouseDoubleClick (event); |
| } |
| break; |
| case SWT.MouseExit: |
| headerOnMouseExit (); break; |
| case SWT.Dispose: |
| onDispose (event); break; |
| case SWT.KeyDown: |
| onKeyDown (event); break; |
| case SWT.Resize: |
| onResize (event); break; |
| case SWT.Selection: |
| if (event.widget == getHorizontalBar ()) { |
| onScrollHorizontal (event); |
| } |
| if (event.widget == getVerticalBar ()) { |
| onScrollVertical (event); |
| } |
| break; |
| case SWT.FocusOut: |
| onFocusOut (); break; |
| case SWT.FocusIn: |
| onFocusIn (); break; |
| case SWT.Traverse: |
| switch (event.detail) { |
| case SWT.TRAVERSE_ESCAPE: |
| case SWT.TRAVERSE_RETURN: |
| case SWT.TRAVERSE_TAB_NEXT: |
| case SWT.TRAVERSE_TAB_PREVIOUS: |
| case SWT.TRAVERSE_PAGE_NEXT: |
| case SWT.TRAVERSE_PAGE_PREVIOUS: |
| event.doit = true; |
| break; |
| } |
| break; |
| } |
| } |
| String headerGetToolTip (int x) { |
| if (resizeColumn != null) return null; |
| int orderedIndex = computeColumnIntersect (x, 0); |
| if (orderedIndex == -1) return null; |
| CTableColumn[] orderedColumns = getOrderedColumns (); |
| CTableColumn column = orderedColumns [orderedIndex]; |
| if (column.toolTipText == null) return null; |
| |
| /* no tooltip should appear if the hover is at a column resize opportunity */ |
| int columnX = column.getX (); |
| if (orderedIndex > 0 && orderedColumns [orderedIndex - 1].resizable) { |
| /* left column bound is resizable */ |
| if (x - columnX <= TOLLERANCE_COLUMNRESIZE) return null; |
| } |
| if (column.resizable) { |
| /* right column bound is resizable */ |
| int columnRightX = columnX + column.width; |
| if (columnRightX - x <= TOLLERANCE_COLUMNRESIZE) return null; |
| } |
| return removeMnemonics (column.toolTipText); |
| } |
| void headerHideToolTip() { |
| if (toolTipShell == null) return; |
| for (int toolTipEvent : toolTipEvents) { |
| header.removeListener (toolTipEvent, toolTipListener); |
| } |
| toolTipShell.dispose (); |
| toolTipShell = null; |
| toolTipLabel = null; |
| } |
| void headerOnMouseDoubleClick (Event event) { |
| if (!isFocusControl ()) setFocus (); |
| if (columns.length == 0) return; |
| CTableColumn[] orderedColumns = getOrderedColumns (); |
| int x = -horizontalOffset; |
| for (int i = 0; i < orderedColumns.length; i++) { |
| CTableColumn column = orderedColumns [i]; |
| x += column.width; |
| if (event.x < x) { |
| /* found the clicked column */ |
| CTableColumn packColumn = null; |
| if (x - event.x <= TOLLERANCE_COLUMNRESIZE) { |
| /* clicked on column bound for this column */ |
| packColumn = column; |
| } else { |
| if (i > 0 && event.x - column.getX () <= TOLLERANCE_COLUMNRESIZE) { |
| /* clicked on column bound that applies to previous column */ |
| packColumn = orderedColumns [i - 1]; |
| } |
| } |
| if (packColumn != null) { |
| packColumn.pack (); |
| resizeColumn = null; |
| if (Math.abs (packColumn.getX () + packColumn.width - event.x) > TOLLERANCE_COLUMNRESIZE) { |
| /* column separator has relocated away from pointer location */ |
| setCursor (null); |
| } |
| return; |
| } |
| /* did not click on column separator, so just fire column event */ |
| Event newEvent = new Event (); |
| newEvent.widget = column; |
| column.notifyListeners (SWT.DefaultSelection, newEvent); |
| return; |
| } |
| } |
| } |
| void headerOnMouseDown (Event event) { |
| if (event.button != 1) return; |
| CTableColumn[] orderedColumns = getOrderedColumns (); |
| int x = -horizontalOffset; |
| for (CTableColumn column : orderedColumns) { |
| x += column.width; |
| /* if close to a resizable column separator line then begin column resize */ |
| if (column.resizable && Math.abs (x - event.x) <= TOLLERANCE_COLUMNRESIZE) { |
| resizeColumn = column; |
| resizeColumnX = x; |
| return; |
| } |
| /* |
| * If within column but not near separator line then start column drag |
| * if column is moveable, or just fire column Selection otherwise. |
| */ |
| if (event.x < x) { |
| if (column.moveable) { |
| /* open tracker on the dragged column's header cell */ |
| int columnX = column.getX (); |
| int pointerOffset = event.x - columnX; |
| headerHideToolTip (); |
| Tracker tracker = new Tracker (this, SWT.NONE); |
| tracker.setRectangles (new Rectangle[] { |
| new Rectangle (columnX, 0, column.width, getHeaderHeight ()) |
| }); |
| if (!tracker.open ()) return; /* cancelled */ |
| /* determine which column was dragged onto */ |
| Rectangle result = tracker.getRectangles () [0]; |
| int pointerX = result.x + pointerOffset; |
| if (pointerX < 0) return; /* dragged too far left */ |
| x = -horizontalOffset; |
| for (int destIndex = 0; destIndex < orderedColumns.length; destIndex++) { |
| CTableColumn destColumn = orderedColumns [destIndex]; |
| x += destColumn.width; |
| if (pointerX < x) { |
| int oldIndex = column.getOrderIndex (); |
| if (destIndex == oldIndex) { /* dragged onto self */ |
| Event newEvent = new Event (); |
| newEvent.widget = column; |
| column.notifyListeners (SWT.Selection, newEvent); |
| return; |
| } |
| int leftmostIndex = Math.min (destIndex, oldIndex); |
| int[] oldOrder = getColumnOrder (); |
| int[] newOrder = new int [oldOrder.length]; |
| System.arraycopy (oldOrder, 0, newOrder, 0, leftmostIndex); |
| if (leftmostIndex == oldIndex) { |
| /* column moving to the right */ |
| System.arraycopy (oldOrder, oldIndex + 1, newOrder, oldIndex, destIndex - oldIndex); |
| } else { |
| /* column moving to the left */ |
| System.arraycopy (oldOrder, destIndex, newOrder, destIndex + 1, oldIndex - destIndex); |
| } |
| newOrder [destIndex] = oldOrder [oldIndex]; |
| int rightmostIndex = Math.max (destIndex, oldIndex); |
| System.arraycopy ( |
| oldOrder, |
| rightmostIndex + 1, |
| newOrder, |
| rightmostIndex + 1, |
| newOrder.length - rightmostIndex - 1); |
| setColumnOrder (newOrder); |
| return; |
| } |
| } |
| return; /* dragged too far right */ |
| } |
| /* column is not moveable */ |
| Event newEvent = new Event (); |
| newEvent.widget = column; |
| column.notifyListeners (SWT.Selection, newEvent); |
| return; |
| } |
| } |
| } |
| void headerOnMouseExit () { |
| if (resizeColumn != null) return; |
| setCursor (null); /* ensure that a column resize cursor does not escape */ |
| } |
| void headerOnMouseHover (Event event) { |
| headerShowToolTip (event.x); |
| } |
| void headerOnMouseMove (Event event) { |
| if (resizeColumn == null) { |
| /* not currently resizing a column */ |
| for (CTableColumn column : columns) { |
| int x = column.getX () + column.width; |
| if (Math.abs (x - event.x) <= TOLLERANCE_COLUMNRESIZE) { |
| if (column.resizable) { |
| setCursor (display.getSystemCursor (SWT.CURSOR_SIZEWE)); |
| } else { |
| setCursor (null); |
| } |
| return; |
| } |
| } |
| setCursor (null); |
| return; |
| } |
| |
| /* currently resizing a column */ |
| |
| /* don't allow the resize x to move left of the column's x position */ |
| if (event.x <= resizeColumn.getX ()) return; |
| |
| /* redraw the resizing line at its new location */ |
| GC gc = new GC (this); |
| gc.setForeground (display.getSystemColor (SWT.COLOR_BLACK)); |
| int lineHeight = clientArea.height; |
| redraw (resizeColumnX - 1, 0, 1, lineHeight, false); |
| resizeColumnX = event.x; |
| gc.drawLine (resizeColumnX - 1, 0, resizeColumnX - 1, lineHeight); |
| gc.dispose (); |
| } |
| void headerOnMouseUp (Event event) { |
| if (resizeColumn == null) return; /* not resizing a column */ |
| |
| /* remove the resize line */ |
| GC gc = new GC (this); |
| redraw (resizeColumnX - 1, 0, 1, clientArea.height, false); |
| gc.dispose (); |
| |
| int newWidth = resizeColumnX - resizeColumn.getX (); |
| if (newWidth != resizeColumn.width) { |
| setCursor (null); |
| updateColumnWidth (resizeColumn, newWidth); |
| } |
| resizeColumnX = -1; |
| resizeColumn = null; |
| } |
| void headerOnPaint (Event event) { |
| CTableColumn[] orderedColumns = getOrderedColumns (); |
| int numColumns = orderedColumns.length; |
| GC gc = event.gc; |
| Rectangle clipping = gc.getClipping (); |
| int startColumn = -1, endColumn = -1; |
| if (numColumns > 0) { |
| startColumn = computeColumnIntersect (clipping.x, 0); |
| if (startColumn != -1) { /* the clip x is within a column's bounds */ |
| endColumn = computeColumnIntersect (clipping.x + clipping.width, startColumn); |
| if (endColumn == -1) endColumn = numColumns - 1; |
| } |
| } else { |
| startColumn = endColumn = 0; |
| } |
| |
| /* paint the column header shadow that spans the full header width */ |
| Point headerSize = header.getSize (); |
| headerPaintHShadows (gc, 0, 0, headerSize.x, headerSize.y); |
| |
| /* if all damage is to the right of the last column then finished */ |
| if (startColumn == -1) return; |
| |
| /* paint each of the column headers */ |
| if (numColumns == 0) return; /* no headers to paint */ |
| for (int i = startColumn; i <= endColumn; i++) { |
| headerPaintVShadows (gc, orderedColumns [i].getX (), 0, orderedColumns [i].width, headerSize.y); |
| orderedColumns [i].paint (gc); |
| } |
| } |
| void headerPaintHShadows (GC gc, int x, int y, int width, int height) { |
| gc.setClipping (x, y, width, height); |
| int endX = x + width; |
| gc.setForeground (display.getSystemColor (SWT.COLOR_WIDGET_HIGHLIGHT_SHADOW)); |
| gc.drawLine (x, y, endX, y); /* highlight shadow */ |
| gc.setForeground (display.getSystemColor (SWT.COLOR_WIDGET_NORMAL_SHADOW)); |
| gc.drawLine (x, height - 2, endX, height - 2); /* lowlight shadow */ |
| gc.setForeground (display.getSystemColor (SWT.COLOR_WIDGET_DARK_SHADOW)); |
| gc.drawLine (x, height - 1, endX, height - 1); /* outer shadow */ |
| } |
| void headerPaintVShadows (GC gc, int x, int y, int width, int height) { |
| gc.setClipping (x, y, width, height); |
| int endX = x + width; |
| gc.setForeground (display.getSystemColor (SWT.COLOR_WIDGET_HIGHLIGHT_SHADOW)); |
| gc.drawLine (x, y, x, y + height - 1); /* highlight shadow */ |
| gc.setForeground (display.getSystemColor (SWT.COLOR_WIDGET_NORMAL_SHADOW)); |
| gc.drawLine (endX - 2, y + 1, endX - 2, height - 2); /* light inner shadow */ |
| gc.setForeground (display.getSystemColor (SWT.COLOR_WIDGET_DARK_SHADOW)); |
| gc.drawLine (endX - 1, y, endX - 1, height - 1); /* dark outer shadow */ |
| } |
| void headerShowToolTip (int x) { |
| String tooltip = headerGetToolTip (x); |
| if (tooltip == null || tooltip.length () == 0) return; |
| |
| if (toolTipShell == null) { |
| toolTipShell = new Shell (getShell (), SWT.ON_TOP | SWT.TOOL); |
| toolTipLabel = new Label (toolTipShell, SWT.CENTER); |
| Display display = toolTipShell.getDisplay (); |
| toolTipLabel.setForeground (display.getSystemColor (SWT.COLOR_INFO_FOREGROUND)); |
| toolTipLabel.setBackground (display.getSystemColor (SWT.COLOR_INFO_BACKGROUND)); |
| for (int toolTipEvent : toolTipEvents) { |
| header.addListener (toolTipEvent, toolTipListener); |
| } |
| } |
| if (headerUpdateToolTip (x)) { |
| toolTipShell.setVisible (true); |
| } else { |
| headerHideToolTip (); |
| } |
| } |
| boolean headerUpdateToolTip (int x) { |
| String tooltip = headerGetToolTip (x); |
| if (tooltip == null || tooltip.length () == 0) return false; |
| if (tooltip.equals (toolTipLabel.getText ())) return true; |
| |
| toolTipLabel.setText (tooltip); |
| CTableColumn column = getOrderedColumns () [computeColumnIntersect (x, 0)]; |
| toolTipShell.setData (Integer.valueOf (column.getIndex ())); |
| Point labelSize = toolTipLabel.computeSize (SWT.DEFAULT, SWT.DEFAULT, true); |
| labelSize.x += 2; labelSize.y += 2; |
| toolTipLabel.setSize (labelSize); |
| toolTipShell.pack (); |
| /* |
| * On some platforms, there is a minimum size for a shell |
| * which may be greater than the label size. |
| * To avoid having the background of the tip shell showing |
| * around the label, force the label to fill the entire client area. |
| */ |
| Rectangle area = toolTipShell.getClientArea (); |
| toolTipLabel.setSize (area.width, area.height); |
| |
| /* Position the tooltip and ensure it's not located off the screen */ |
| Point cursorLocation = getDisplay ().getCursorLocation (); |
| int cursorHeight = 21; /* assuming cursor is 21x21 */ |
| Point size = toolTipShell.getSize (); |
| Rectangle rect = getMonitor ().getBounds (); |
| Point pt = new Point (cursorLocation.x, cursorLocation.y + cursorHeight + 2); |
| pt.x = Math.max (pt.x, rect.x); |
| if (pt.x + size.x > rect.x + rect.width) pt.x = rect.x + rect.width - size.x; |
| if (pt.y + size.y > rect.y + rect.height) pt.y = cursorLocation.y - 2 - size.y; |
| toolTipShell.setLocation (pt); |
| return true; |
| } |
| /** |
| * Searches the receiver's list starting at the first column |
| * (index 0) until a column is found that is equal to the |
| * argument, and returns the index of that column. If no column |
| * is found, returns -1. |
| * |
| * @param column the search column |
| * @return the index of the column |
| * |
| * @exception IllegalArgumentException <ul> |
| * <li>ERROR_NULL_ARGUMENT - if the column is null</li> |
| * </ul> |
| * @exception SWTException <ul> |
| * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> |
| * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> |
| * </ul> |
| */ |
| public int indexOf (CTableColumn column) { |
| checkWidget (); |
| if (column == null) SWT.error (SWT.ERROR_NULL_ARGUMENT); |
| if (column.parent != this) return -1; |
| return column.getIndex (); |
| } |
| /** |
| * Searches the receiver's list starting at the first item |
| * (index 0) until an item is found that is equal to the |
| * argument, and returns the index of that item. If no item |
| * is found, returns -1. |
| * |
| * @param item the search item |
| * @return the index of the item |
| * |
| * @exception IllegalArgumentException <ul> |
| * <li>ERROR_NULL_ARGUMENT - if the item is null</li> |
| * </ul> |
| * @exception SWTException <ul> |
| * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> |
| * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> |
| * </ul> |
| */ |
| public int indexOf (CTableItem item) { |
| checkWidget (); |
| if (item == null) SWT.error (SWT.ERROR_NULL_ARGUMENT); |
| if (item.parent != this) return -1; |
| return item.index; |
| } |
| |
| void initAccessibility () { |
| // TODO: does this all work if CTable is virtual? |
| final Accessible accessibleTable = getAccessible(); |
| accessibleTable.addAccessibleListener(new AccessibleAdapter() { |
| @Override |
| public void getName(AccessibleEvent e) { |
| /* CTables take their name from the preceding Label, if any. */ |
| Control[] siblings = getParent().getChildren(); |
| for (int i = 0; i < siblings.length; i++) { |
| if (i != 0 && siblings[i] == CTable.this) { |
| Control control = siblings[i-1]; |
| if (control instanceof Label) { |
| e.result = ((Label) control).getText(); |
| } |
| } |
| } |
| } |
| @Override |
| public void getHelp(AccessibleEvent e) { |
| /* A CTable's toolTip text (if any) can be used as its help text. */ |
| e.result = getToolTipText(); |
| } |
| }); |
| accessibleTable.addAccessibleControlListener(new AccessibleControlAdapter() { |
| /* Child IDs are assigned as follows: |
| * - column header ids (if any) are numbered from 0 to columnCount - 1 |
| * - cell ids are numbered in row-major order (starting from columnCount if there are columns) |
| * Accessibles are returned in getChild. |
| */ |
| @Override |
| public void getChild(AccessibleControlEvent e) { |
| int childID = e.childID; |
| if (childID == ACC.CHILDID_CHILD_AT_INDEX) childID = e.detail; // childID == index |
| if (columns.length > 0 && 0 <= childID && childID < columns.length) { // header cell |
| CTableColumn column = columns [childID]; |
| e.accessible = column.getAccessible(accessibleTable); |
| } else { // item cell |
| int columnCount = columns.length > 0 ? columns.length : 1; |
| if (columns.length > 0) childID -= columnCount; |
| if (0 <= childID && childID < itemsCount * columnCount) { |
| int rowIndex = childID / columnCount; |
| int columnIndex = childID - rowIndex * columnCount; |
| e.accessible = items[rowIndex].getAccessible (accessibleTable, columnIndex); |
| } |
| } |
| } |
| @Override |
| public void getChildAtPoint(AccessibleControlEvent e) { |
| Point point = toControl(e.x, e.y); |
| if (columns.length > 0 && point.y < getHeaderHeight ()) { // header cell |
| int columnIndex = computeColumnIntersect (point.x, 0); |
| if (columnIndex != -1) { |
| CTableColumn column = columns [columnIndex]; |
| e.accessible = column.getAccessible (accessibleTable); |
| } |
| } else { // item cell |
| int columnIndex = columns.length > 0 ? computeColumnIntersect (point.x, 0) : 0; |
| if (columnIndex != -1) { |
| int rowIndex = (point.y - getHeaderHeight ()) / itemHeight + topIndex; |
| if (0 <= rowIndex && rowIndex < itemsCount) { |
| if (items [rowIndex].getHitBounds ().contains (point)) { /* considers the x value */ |
| e.accessible = items[rowIndex].getAccessible (accessibleTable, columnIndex); |
| } |
| } |
| } |
| } |
| } |
| @Override |
| public void getChildCount(AccessibleControlEvent e) { |
| e.detail = columns.length > 0 ? columns.length + itemsCount * columns.length : itemsCount; |
| } |
| @Override |
| public void getChildren(AccessibleControlEvent e) { |
| int childIdCount = columns.length > 0 ? columns.length + itemsCount * columns.length : itemsCount; |
| Object[] children = new Object[childIdCount]; |
| for (int i = 0; i < childIdCount; i++) { |
| children[i] = Integer.valueOf(i); |
| } |
| e.children = children; |
| } |
| @Override |
| public void getFocus(AccessibleControlEvent e) { |
| e.childID = isFocusControl() ? ACC.CHILDID_SELF : ACC.CHILDID_NONE; |
| } |
| @Override |
| public void getLocation(AccessibleControlEvent e) { |
| Rectangle location = null; |
| Point pt = null; |
| int childID = e.childID; |
| if (childID == ACC.CHILDID_SELF) { // table |
| location = getBounds(); |
| pt = getParent().toDisplay(location.x, location.y); |
| } else if (columns.length > 0 && 0 <= childID && childID < columns.length) { // header cell |
| CTableColumn column = columns [childID]; |
| location = new Rectangle (column.getX (), 0, column.getWidth(), getHeaderHeight()); |
| pt = toDisplay(location.x, location.y); |
| } else { // item cell |
| int columnCount = columns.length > 0 ? columns.length : 1; |
| if (columns.length > 0) childID -= columnCount; |
| if (0 <= childID && childID < itemsCount * columnCount) { |
| int rowIndex = childID / columnCount; |
| int columnIndex = childID - rowIndex * columnCount; |
| location = items[rowIndex].getBounds(columnIndex); |
| pt = toDisplay(location.x, location.y); |
| } |
| } |
| if (location != null && pt != null) { |
| e.x = pt.x; |
| e.y = pt.y; |
| e.width = location.width; |
| e.height = location.height; |
| } |
| } |
| @Override |
| public void getRole(AccessibleControlEvent e) { |
| e.detail = e.childID == ACC.CHILDID_SELF ? ACC.ROLE_TABLE : ACC.ROLE_TABLECELL; |
| } |
| @Override |
| public void getSelection(AccessibleControlEvent e) { |
| int columnCount = columns.length > 0 ? columns.length : 1; |
| int [] selectionIndices = getSelectionIndices(); |
| Object[] selectedChildren = new Object[selectionIndices.length * columnCount]; |
| for (int i = 0; i < selectionIndices.length; i++) { |
| int row = selectionIndices[i]; |
| for (int col = 0; col < columnCount; col++) { |
| selectedChildren[i] = Integer.valueOf(row * columnCount + col); |
| } |
| } |
| e.children = selectedChildren; |
| } |
| @Override |
| public void getState(AccessibleControlEvent e) { |
| int state = ACC.STATE_NORMAL; |
| int childID = e.childID; |
| if (childID == ACC.CHILDID_SELF) { // table |
| state |= ACC.STATE_FOCUSABLE; |
| if (isFocusControl()) { |
| state |= ACC.STATE_FOCUSED; |
| } |
| } else if (columns.length > 0 && 0 <= childID && childID < columns.length) { // header cell |
| /* CTable does not support header cell focus or selection. */ |
| state |= ACC.STATE_SIZEABLE; |
| } else { // item cell |
| int columnCount = columns.length > 0 ? columns.length : 1; |
| if (columns.length > 0) childID -= columnCount; |
| if (0 <= childID && childID < itemsCount * columnCount) { |
| /* CTable does not support cell selection (only row selection). */ |
| int rowIndex = childID / columnCount; |
| state |= ACC.STATE_SELECTABLE; |
| if (isFocusControl()) { |
| state |= ACC.STATE_FOCUSABLE; |
| } |
| if (items[rowIndex].isSelected()) { |
| state |= ACC.STATE_SELECTED; |
| if (isFocusControl()) { |
| state |= ACC.STATE_FOCUSED; |
| } |
| } |
| } |
| } |
| e.detail = state; |
| } |
| }); |
| accessibleTable.addAccessibleTableListener(new AccessibleTableAdapter() { |
| @Override |
| public void deselectColumn(AccessibleTableEvent e) { |
| /* CTable does not support column selection. */ |
| } |
| @Override |
| public void deselectRow(AccessibleTableEvent e) { |
| deselect(e.row); |
| e.result = ACC.OK; |
| } |
| @Override |
| public void getCaption(AccessibleTableEvent e) { |
| // TODO: What is a caption? How does it differ from name? Should app supply? |
| e.result = "This is the Custom Table's Test Caption"; |
| } |
| @Override |
| public void getCell(AccessibleTableEvent e) { |
| int index = e.row; |
| if (0 <= index && index < itemsCount) { |
| CTableItem row = items [index]; |
| index = e.column; |
| if (columns.length == 0 || 0 <= index && index < columns.length) { |
| e.accessible = row.getAccessible (accessibleTable, index); |
| } |
| } |
| } |
| @Override |
| public void getColumnCount(AccessibleTableEvent e) { |
| int columnCount = columns.length > 0 ? columns.length : 1; |
| e.count = columnCount; |
| } |
| @Override |
| public void getColumnDescription(AccessibleTableEvent e) { |
| // TODO: What is a description? How does it differ from name? Should app supply? |
| e.result = "This is the Custom Table's Test Description for column " + e.column; |
| } |
| // public void getColumnHeader(AccessibleTableEvent e) { |
| // e.accessible = header.getAccessible(); |
| // } |
| @Override |
| public void getColumnHeaderCells(AccessibleTableEvent e) { |
| if (columns.length == 0) { |
| /* The CTable is being used as a list, and there are no headers. */ |
| e.accessibles = null; |
| } else { |
| Accessible[] accessibles = new Accessible[columns.length]; |
| for (int i = 0; i < columns.length; i++) { |
| CTableColumn column = columns [i]; |
| accessibles[i] = column.getAccessible (accessibleTable); |
| } |
| e.accessibles = accessibles; |
| } |
| } |
| @Override |
| public void getRowCount(AccessibleTableEvent e) { |
| e.count = itemsCount; |
| } |
| @Override |
| public void getRowDescription(AccessibleTableEvent e) { |
| // TODO: What is a description? How does it differ from name? Should app supply? |
| e.result = "This is the Custom Table's Test Description for row " + e.row; |
| } |
| @Override |
| public void getRowHeader(AccessibleTableEvent e) { |
| /* CTable does not support row headers. */ |
| } |
| @Override |
| public void getSelectedCellCount(AccessibleTableEvent e) { |
| int columnCount = columns.length > 0 ? columns.length : 1; |
| e.count = selectedItems.length * columnCount; |
| } |
| @Override |
| public void getSelectedCells(AccessibleTableEvent e) { |
| int columnCount = columns.length > 0 ? columns.length : 1; |
| Accessible[] accessibles = new Accessible[selectedItems.length * columnCount]; |
| for (int r = 0; r < selectedItems.length; r++) { |
| CTableItem row = selectedItems [r]; |
| for (int c = 0; c < columnCount; c++) |
| accessibles[r+c] = row.getAccessible (accessibleTable, c); |
| } |
| e.accessibles = accessibles; |
| } |
| @Override |
| public void getSelectedColumnCount(AccessibleTableEvent e) { |
| e.count = 0; /* CTable does not support column selection. */ |
| } |
| @Override |
| public void getSelectedColumns(AccessibleTableEvent e) { |
| /* CTable does not support column selection. */ |
| } |
| @Override |
| public void getSelectedRowCount(AccessibleTableEvent e) { |
| e.count = selectedItems.length; |
| } |
| @Override |
| public void getSelectedRows(AccessibleTableEvent e) { |
| int[] selectedIndices = new int[selectedItems.length]; |
| for (int i = 0; i < selectedItems.length; i++) { |
| selectedIndices[i] = selectedItems [i].index; |
| } |
| e.selected = selectedIndices; |
| } |
| @Override |
| public void getSummary(AccessibleTableEvent e) { |
| // TODO: What is a summary? How does it differ from name? Should app supply? |
| e.result = "This is the Custom Table's Summary"; |
| } |
| @Override |
| public void isColumnSelected(AccessibleTableEvent e) { |
| e.isSelected = false; /* CTable does not support column selection. */ |
| } |
| @Override |
| public void isRowSelected(AccessibleTableEvent e) { |
| e.isSelected = isSelected(e.row); |
| } |
| @Override |
| public void selectColumn(AccessibleTableEvent e) { |
| /* CTable does not support column selection. */ |
| } |
| @Override |
| public void selectRow(AccessibleTableEvent e) { |
| select(e.row); |
| e.result = ACC.OK; |
| } |
| @Override |
| public void setSelectedColumn(AccessibleTableEvent e) { |
| /* CTable does not support column selection. */ |
| } |
| @Override |
| public void setSelectedRow(AccessibleTableEvent e) { |
| setSelection(e.row); |
| e.result = ACC.OK; |
| } |
| }); |
| } |
| |
| static void initImages (final Display display) { |
| PaletteData arrowPalette = new PaletteData (new RGB (0, 0, 0), new RGB (255, 255, 255)); |
| if (display.getData (ID_ARROWDOWN) == null) { |
| ImageData arrowDown = new ImageData ( |
| 7, 4, 1, |
| arrowPalette, 1, |
| new byte[] {0x00, (byte)0x83, (byte)0xC7, (byte)0xEF}); |
| arrowDown.transparentPixel = 0x1; /* use white for transparency */ |
| display.setData (ID_ARROWDOWN, new Image (display, arrowDown)); |
| } |
| if (display.getData (ID_ARROWUP) == null) { |
| ImageData arrowUp = new ImageData ( |
| 7, 4, 1, |
| arrowPalette, 1, |
| new byte[] {(byte)0xEF, (byte)0xC7, (byte)0x83, 0x00}); |
| arrowUp.transparentPixel = 0x1; /* use white for transparency */ |
| display.setData (ID_ARROWUP, new Image (display, arrowUp)); |
| } |
| |
| PaletteData checkMarkPalette = new PaletteData (new RGB (0, 0, 0), new RGB (252, 3, 251)); |
| byte[] checkbox = new byte[] {0, 0, 127, -64, 127, -64, 127, -64, 127, -64, 127, -64, 127, -64, 127, -64, 127, -64, 127, -64, 0, 0}; |
| ImageData checkmark = new ImageData (7, 7, 1, checkMarkPalette, 1, new byte[] {-4, -8, 112, 34, 6, -114, -34}); |
| checkmark.transparentPixel = 1; |
| if (display.getData (ID_CHECKMARK) == null) { |
| display.setData (ID_CHECKMARK, new Image (display, checkmark)); |
| } |
| |
| if (display.getData (ID_UNCHECKED) == null) { |
| PaletteData uncheckedPalette = new PaletteData (new RGB (128, 128, 128), new RGB (255, 255, 255)); |
| ImageData unchecked = new ImageData (11, 11, 1, uncheckedPalette, 2, checkbox); |
| display.setData (ID_UNCHECKED, new Image (display, unchecked)); |
| } |
| |
| if (display.getData (ID_GRAYUNCHECKED) == null) { |
| PaletteData grayUncheckedPalette = new PaletteData (new RGB (128, 128, 128), new RGB (192, 192, 192)); |
| ImageData grayUnchecked = new ImageData (11, 11, 1, grayUncheckedPalette, 2, checkbox); |
| display.setData (ID_GRAYUNCHECKED, new Image (display, grayUnchecked)); |
| } |
| |
| display.disposeExec (() -> { |
| Image unchecked = (Image) display.getData (ID_UNCHECKED); |
| if (unchecked != null) unchecked.dispose (); |
| Image grayUnchecked = (Image) display.getData (ID_GRAYUNCHECKED); |
| if (grayUnchecked != null) grayUnchecked.dispose (); |
| Image checkmark1 = (Image) display.getData (ID_CHECKMARK); |
| if (checkmark1 != null) checkmark1.dispose (); |
| Image arrowDown = (Image) display.getData (ID_ARROWDOWN); |
| if (arrowDown != null) arrowDown.dispose (); |
| Image arrowUp = (Image) display.getData (ID_ARROWUP); |
| if (arrowUp != null) arrowUp.dispose (); |
| |
| display.setData (ID_UNCHECKED, null); |
| display.setData (ID_GRAYUNCHECKED, null); |
| display.setData (ID_CHECKMARK, null); |
| display.setData (ID_ARROWDOWN, null); |
| display.setData (ID_ARROWUP, null); |
| }); |
| } |
| /** |
| * Returns <code>true</code> if the item is selected, |
| * and <code>false</code> otherwise. Indices out of |
| * range are ignored. |
| * |
| * @param index the index of the item |
| * @return the selection state of the item at the index |
| * |
| * @exception SWTException <ul> |
| * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> |
| * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> |
| * </ul> |
| */ |
| public boolean isSelected (int index) { |
| checkWidget (); |
| if (!(0 <= index && index < itemsCount)) return false; |
| return items [index].isSelected (); |
| } |
| @Override |
| public void notifyListeners (int eventType, Event event) { |
| super.notifyListeners(eventType, event); |
| if (eventType == SWT.Selection && event.detail != SWT.CHECK) getAccessible().selectionChanged(); |
| } |
| void onArrowDown (int stateMask) { |
| if ((stateMask & (SWT.SHIFT | SWT.CTRL)) == 0) { |
| /* Down Arrow with no modifiers */ |
| int newFocusIndex = focusItem.index + 1; |
| if (newFocusIndex == itemsCount) return; /* at bottom */ |
| selectItem (items [newFocusIndex], false); |
| setFocusItem (items [newFocusIndex], true); |
| redrawItem (newFocusIndex, true); |
| showItem (items [newFocusIndex]); |
| Event newEvent = new Event (); |
| newEvent.item = items [newFocusIndex]; |
| notifyListeners (SWT.Selection, newEvent); |
| return; |
| } |
| if ((getStyle () & SWT.SINGLE) != 0) { |
| if ((stateMask & SWT.CTRL) != 0) { |
| /* CTRL+Down Arrow, CTRL+Shift+Down Arrow */ |
| int visibleItemCount = (clientArea.height - getHeaderHeight ()) / itemHeight; |
| if (itemsCount <= topIndex + visibleItemCount) return; /* at bottom */ |
| update (); |
| topIndex++; |
| ScrollBar vBar = getVerticalBar (); |
| if (vBar != null) vBar.setSelection (topIndex); |
| GC gc = new GC (this); |
| gc.copyArea ( |
| 0, 0, |
| clientArea.width, clientArea.height, |
| 0, -itemHeight); |
| gc.dispose (); |
| return; |
| } |
| /* Shift+Down Arrow */ |
| int newFocusIndex = focusItem.index + 1; |
| if (newFocusIndex == itemsCount) return; /* at bottom */ |
| selectItem (items [newFocusIndex], false); |
| setFocusItem (items [newFocusIndex], true); |
| redrawItem (newFocusIndex, true); |
| showItem (items [newFocusIndex]); |
| Event newEvent = new Event (); |
| newEvent.item = items [newFocusIndex]; |
| notifyListeners (SWT.Selection, newEvent); |
| return; |
| } |
| /* SWT.MULTI */ |
| if ((stateMask & SWT.CTRL) != 0) { |
| if ((stateMask & SWT.SHIFT) != 0) { |
| /* CTRL+Shift+Down Arrow */ |
| int visibleItemCount = (clientArea.height - getHeaderHeight ()) / itemHeight; |
| if (itemsCount <= topIndex + visibleItemCount) return; /* at bottom */ |
| update (); |
| topIndex++; |
| ScrollBar vBar = getVerticalBar (); |
| if (vBar != null) vBar.setSelection (topIndex); |
| GC gc = new GC (this); |
| gc.copyArea ( |
| 0, 0, |
| clientArea.width, clientArea.height, |
| 0, -itemHeight); |
| gc.dispose (); |
| return; |
| } |
| /* CTRL+Down Arrow */ |
| int focusIndex = focusItem.index; |
| if (focusIndex == itemsCount - 1) return; /* at bottom */ |
| CTableItem newFocusItem = items [focusIndex + 1]; |
| setFocusItem (newFocusItem, true); |
| redrawItem (newFocusItem.index, true); |
| showItem (newFocusItem); |
| return; |
| } |
| /* Shift+Down Arrow */ |
| int newFocusIndex = focusItem.index + 1; |
| if (newFocusIndex == itemsCount) return; /* at bottom */ |
| if (anchorItem == null) anchorItem = focusItem; |
| if (focusItem.index < anchorItem.index) { |
| deselectItem (focusItem); |
| redrawItem (focusItem.index, true); |
| } |
| selectItem (items [newFocusIndex], true); |
| setFocusItem (items [newFocusIndex], true); |
| redrawItem (newFocusIndex, true); |
| showItem (items [newFocusIndex]); |
| Event newEvent = new Event (); |
| newEvent.item = items [newFocusIndex]; |
| notifyListeners (SWT.Selection, newEvent); |
| } |
| void onArrowLeft (int stateMask) { |
| if (horizontalOffset == 0) return; |
| int newSelection = Math.max (0, horizontalOffset - SIZE_HORIZONTALSCROLL); |
| update (); |
| GC gc = new GC (this); |
| gc.copyArea ( |
| 0, 0, |
| clientArea.width, clientArea.height, |
| horizontalOffset - newSelection, 0); |
| gc.dispose (); |
| if (header.getVisible ()) { |
| header.update (); |
| Rectangle headerClientArea = header.getClientArea (); |
| gc = new GC (header); |
| gc.copyArea ( |
| 0, 0, |
| headerClientArea.width, headerClientArea.height, |
| horizontalOffset - newSelection, 0); |
| gc.dispose(); |
| } |
| horizontalOffset = newSelection; |
| ScrollBar hBar = getHorizontalBar (); |
| if (hBar != null) hBar.setSelection (horizontalOffset); |
| } |
| void onArrowRight (int stateMask) { |
| ScrollBar hBar = getHorizontalBar (); |
| if (hBar == null) return; |
| int maximum = hBar.getMaximum (); |
| int clientWidth = clientArea.width; |
| if ((horizontalOffset + clientArea.width) == maximum) return; |
| if (maximum <= clientWidth) return; |
| int newSelection = Math.min (horizontalOffset + SIZE_HORIZONTALSCROLL, maximum - clientWidth); |
| update (); |
| GC gc = new GC (this); |
| gc.copyArea ( |
| 0, 0, |
| clientArea.width, clientArea.height, |
| horizontalOffset - newSelection, 0); |
| gc.dispose (); |
| if (header.getVisible ()) { |
| Rectangle headerClientArea = header.getClientArea (); |
| header.update (); |
| gc = new GC (header); |
| gc.copyArea ( |
| 0, 0, |
| headerClientArea.width, headerClientArea.height, |
| horizontalOffset - newSelection, 0); |
| gc.dispose(); |
| } |
| horizontalOffset = newSelection; |
| hBar.setSelection (horizontalOffset); |
| } |
| void onArrowUp (int stateMask) { |
| if ((stateMask & (SWT.SHIFT | SWT.CTRL)) == 0) { |
| /* Up Arrow with no modifiers */ |
| int newFocusIndex = focusItem.index - 1; |
| if (newFocusIndex < 0) return; /* at top */ |
| CTableItem item = items [newFocusIndex]; |
| selectItem (item, false); |
| setFocusItem (item, true); |
| redrawItem (newFocusIndex, true); |
| showItem (item); |
| Event newEvent = new Event (); |
| newEvent.item = item; |
| notifyListeners (SWT.Selection, newEvent); |
| return; |
| } |
| if ((getStyle () & SWT.SINGLE) != 0) { |
| if ((stateMask & SWT.CTRL) != 0) { |
| /* CTRL+Up Arrow, CTRL+Shift+Up Arrow */ |
| if (topIndex == 0) return; /* at top */ |
| update (); |
| topIndex--; |
| ScrollBar vBar = getVerticalBar (); |
| if (vBar != null) vBar.setSelection (topIndex); |
| GC gc = new GC (this); |
| gc.copyArea ( |
| 0, 0, |
| clientArea.width, clientArea.height, |
| 0, itemHeight); |
| gc.dispose (); |
| return; |
| } |
| /* Shift+Up Arrow */ |
| int newFocusIndex = focusItem.index - 1; |
| if (newFocusIndex < 0) return; /* at top */ |
| CTableItem item = items [newFocusIndex]; |
| selectItem (item, false); |
| setFocusItem (item, true); |
| redrawItem (newFocusIndex, true); |
| showItem (item); |
| Event newEvent = new Event (); |
| newEvent.item = item; |
| notifyListeners (SWT.Selection, newEvent); |
| return; |
| } |
| /* SWT.MULTI */ |
| if ((stateMask & SWT.CTRL) != 0) { |
| if ((stateMask & SWT.SHIFT) != 0) { |
| /* CTRL+Shift+Up Arrow */ |
| if (topIndex == 0) return; /* at top */ |
| update (); |
| topIndex--; |
| ScrollBar vBar = getVerticalBar (); |
| if (vBar != null) vBar.setSelection (topIndex); |
| GC gc = new GC (this); |
| gc.copyArea ( |
| 0, 0, |
| clientArea.width, clientArea.height, |
| 0, itemHeight); |
| gc.dispose (); |
| return; |
| } |
| /* CTRL+Up Arrow */ |
| int focusIndex = focusItem.index; |
| if (focusIndex == 0) return; /* at top */ |
| CTableItem newFocusItem = items [focusIndex - 1]; |
| setFocusItem (newFocusItem, true); |
| showItem (newFocusItem); |
| redrawItem (newFocusItem.index, true); |
| return; |
| } |
| /* Shift+Up Arrow */ |
| int newFocusIndex = focusItem.index - 1; |
| if (newFocusIndex < 0) return; /* at top */ |
| if (anchorItem == null) anchorItem = focusItem; |
| if (anchorItem.index < focusItem.index) { |
| deselectItem (focusItem); |
| redrawItem (focusItem.index, true); |
| } |
| CTableItem item = items [newFocusIndex]; |
| selectItem (item, true); |
| setFocusItem (item, true); |
| redrawItem (newFocusIndex, true); |
| showItem (item); |
| Event newEvent = new Event (); |
| newEvent.item = item; |
| notifyListeners (SWT.Selection, newEvent); |
| } |
| void onCR () { |
| if (focusItem == null) return; |
| Event event = new Event (); |
| event.item = focusItem; |
| notifyListeners (SWT.DefaultSelection, event); |
| } |
| void onDispose (Event event) { |
| if (isDisposed ()) return; |
| if (ignoreDispose) return; |
| ignoreDispose = true; |
| notifyListeners(SWT.Dispose, event); |
| event.type = SWT.None; |
| for (int i = 0; i < itemsCount; i++) { |
| items [i].dispose (false); |
| } |
| for (CTableColumn column : columns) { |
| column.dispose (false); |
| } |
| if (toolTipShell != null) { |
| toolTipShell.dispose (); |
| toolTipShell = null; |
| toolTipLabel = null; |
| } |
| toolTipListener = null; |
| itemsCount = topIndex = horizontalOffset = 0; |
| items = selectedItems = null; |
| columns = orderedColumns = null; |
| focusItem = anchorItem = lastClickedItem = null; |
| lastSelectionEvent = null; |
| header = null; |
| resizeColumn = sortColumn = null; |
| } |
| void onEnd (int stateMask) { |
| int lastAvailableIndex = itemsCount - 1; |
| if ((stateMask & (SWT.CTRL | SWT.SHIFT)) == 0) { |
| /* End with no modifiers */ |
| if (focusItem.index == lastAvailableIndex) return; /* at bottom */ |
| CTableItem item = items [lastAvailableIndex]; |
| selectItem (item, false); |
| setFocusItem (item, true); |
| redrawItem (lastAvailableIndex, true); |
| showItem (item); |
| Event newEvent = new Event (); |
| newEvent.item = item; |
| notifyListeners (SWT.Selection, newEvent); |
| return; |
| } |
| if ((getStyle () & SWT.SINGLE) != 0) { |
| if ((stateMask & SWT.CTRL) != 0) { |
| /* CTRL+End, CTRL+Shift+End */ |
| int visibleItemCount = (clientArea.height - getHeaderHeight ()) / itemHeight; |
| setTopIndex (itemsCount - visibleItemCount); |
| return; |
| } |
| /* Shift+End */ |
| if (focusItem.index == lastAvailableIndex) return; /* at bottom */ |
| CTableItem item = items [lastAvailableIndex]; |
| selectItem (item, false); |
| setFocusItem (item, true); |
| redrawItem (lastAvailableIndex, true); |
| showItem (item); |
| Event newEvent = new Event (); |
| newEvent.item = item; |
| notifyListeners (SWT.Selection, newEvent); |
| return; |
| } |
| /* SWT.MULTI */ |
| if ((stateMask & SWT.CTRL) != 0) { |
| if ((stateMask & SWT.SHIFT) != 0) { |
| /* CTRL+Shift+End */ |
| showItem (items [lastAvailableIndex]); |
| return; |
| } |
| /* CTRL+End */ |
| if (focusItem.index == lastAvailableIndex) return; /* at bottom */ |
| CTableItem item = items [lastAvailableIndex]; |
| setFocusItem (item, true); |
| showItem (item); |
| redrawItem (item.index, true); |
| return; |
| } |
| /* Shift+End */ |
| if (anchorItem == null) anchorItem = focusItem; |
| CTableItem selectedItem = items [lastAvailableIndex]; |
| if (selectedItem == focusItem && selectedItem.isSelected ()) return; |
| int anchorIndex = anchorItem.index; |
| int selectIndex = selectedItem.index; |
| CTableItem[] newSelection = new CTableItem [selectIndex - anchorIndex + 1]; |
| int writeIndex = 0; |
| for (int i = anchorIndex; i <= selectIndex; i++) { |
| newSelection [writeIndex++] = items [i]; |
| } |
| setSelection (newSelection, false); |
| setFocusItem (selectedItem, true); |
| redrawItems (anchorIndex, selectIndex, true); |
| showItem (selectedItem); |
| Event newEvent = new Event (); |
| newEvent.item = selectedItem; |
| notifyListeners (SWT.Selection, newEvent); |
| } |
| void onFocusIn () { |
| hasFocus = true; |
| if (itemsCount == 0) { |
| redraw (); |
| return; |
| } |
| if ((getStyle () & (SWT.HIDE_SELECTION | SWT.MULTI)) == (SWT.HIDE_SELECTION | SWT.MULTI)) { |
| for (CTableItem selectedItem : selectedItems) { |
| redrawItem (selectedItem.index, true); |
| } |
| } |
| if (focusItem != null) { |
| redrawItem (focusItem.index, true); |
| return; |
| } |
| /* an initial focus item must be selected */ |
| CTableItem initialFocus; |
| if (selectedItems.length > 0) { |
| initialFocus = selectedItems [0]; |
| } else { |
| initialFocus = items [topIndex]; |
| } |
| setFocusItem (initialFocus, false); |
| redrawItem (initialFocus.index, true); |
| return; |
| } |
| void onFocusOut () { |
| hasFocus = false; |
| if (itemsCount == 0) { |
| redraw (); |
| return; |
| } |
| if (focusItem != null) { |
| redrawItem (focusItem.index, true); |
| } |
| if ((getStyle () & (SWT.HIDE_SELECTION | SWT.MULTI)) == (SWT.HIDE_SELECTION | SWT.MULTI)) { |
| for (CTableItem selectedItem : selectedItems) { |
| redrawItem (selectedItem.index, true); |
| } |
| } |
| } |
| void onHome (int stateMask) { |
| if ((stateMask & (SWT.CTRL | SWT.SHIFT)) == 0) { |
| /* Home with no modifiers */ |
| if (focusItem.index == 0) return; /* at top */ |
| CTableItem item = items [0]; |
| selectItem (item, false); |
| setFocusItem (item, true); |
| redrawItem (0, true); |
| showItem (item); |
| Event newEvent = new Event (); |
| newEvent.item = item; |
| notifyListeners (SWT.Selection, newEvent); |
| return; |
| } |
| if ((getStyle () & SWT.SINGLE) != 0) { |
| if ((stateMask & SWT.CTRL) != 0) { |
| /* CTRL+Home, CTRL+Shift+Home */ |
| setTopIndex (0); |
| return; |
| } |
| /* Shift+Home */ |
| if (focusItem.index == 0) return; /* at top */ |
| CTableItem item = items [0]; |
| selectItem (item, false); |
| setFocusItem (item, true); |
| redrawItem (0, true); |
| showItem (item); |
| Event newEvent = new Event (); |
| newEvent.item = item; |
| notifyListeners (SWT.Selection, newEvent); |
| return; |
| } |
| /* SWT.MULTI */ |
| if ((stateMask & SWT.CTRL) != 0) { |
| if ((stateMask & SWT.SHIFT) != 0) { |
| /* CTRL+Shift+Home */ |
| setTopIndex (0); |
| return; |
| } |
| /* CTRL+Home */ |
| if (focusItem.index == 0) return; /* at top */ |
| CTableItem item = items [0]; |
| setFocusItem (item, true); |
| showItem (item); |
| redrawItem (item.index, true); |
| return; |
| } |
| /* Shift+Home */ |
| if (anchorItem == null) anchorItem = focusItem; |
| CTableItem selectedItem = items [0]; |
| if (selectedItem == focusItem && selectedItem.isSelected ()) return; |
| int anchorIndex = anchorItem.index; |
| int selectIndex = selectedItem.index; |
| CTableItem[] newSelection = new CTableItem [anchorIndex + 1]; |
| int writeIndex = 0; |
| for (int i = anchorIndex; i >= 0; i--) { |
| newSelection [writeIndex++] = items [i]; |
| } |
| setSelection (newSelection, false); |
| setFocusItem (selectedItem, true); |
| redrawItems (anchorIndex, selectIndex, true); |
| showItem (selectedItem); |
| Event newEvent = new Event (); |
| newEvent.item = selectedItem; |
| notifyListeners (SWT.Selection, newEvent); |
| } |
| void onKeyDown (Event event) { |
| if (ignoreKey) { |
| ignoreKey = false; |
| return; |
| } |
| ignoreKey = true; |
| notifyListeners (event.type, event); |
| event.type = SWT.None; |
| if (!event.doit) return; |
| if (focusItem == null) return; |
| if ((event.stateMask & SWT.SHIFT) == 0 && event.keyCode != SWT.SHIFT) { |
| anchorItem = null; |
| } |
| switch (event.keyCode) { |
| case SWT.ARROW_UP: |
| onArrowUp (event.stateMask); |
| return; |
| case SWT.ARROW_DOWN: |
| onArrowDown (event.stateMask); |
| return; |
| case SWT.ARROW_LEFT: |
| onArrowLeft (event.stateMask); |
| return; |
| case SWT.ARROW_RIGHT: |
| onArrowRight (event.stateMask); |
| return; |
| case SWT.PAGE_UP: |
| onPageUp (event.stateMask); |
| return; |
| case SWT.PAGE_DOWN: |
| onPageDown (event.stateMask); |
| return; |
| case SWT.HOME: |
| onHome (event.stateMask); |
| return; |
| case SWT.END: |
| onEnd (event.stateMask); |
| return; |
| } |
| if (event.character == ' ') { |
| onSpace (); |
| return; |
| } |
| if (event.character == SWT.CR) { |
| onCR (); |
| return; |
| } |
| if ((event.stateMask & SWT.CTRL) != 0) return; |
| |
| int initialIndex = focusItem.index; |
| char character = Character.toLowerCase (event.character); |
| /* check available items from current focus item to bottom */ |
| for (int i = initialIndex + 1; i < itemsCount; i++) { |
| CTableItem item = items [i]; |
| String text = item.getText (0, false); |
| if (text.length () > 0) { |
| if (Character.toLowerCase (text.charAt (0)) == character) { |
| selectItem (item, false); |
| setFocusItem (item, true); |
| redrawItem (i, true); |
| showItem (item); |
| Event newEvent = new Event (); |
| newEvent.item = item; |
| notifyListeners (SWT.Selection, newEvent); |
| return; |
| } |
| } |
| } |
| /* check available items from top to current focus item */ |
| for (int i = 0; i < initialIndex; i++) { |
| CTableItem item = items [i]; |
| String text = item.getText (0, false); |
| if (text.length () > 0) { |
| if (Character.toLowerCase (text.charAt (0)) == character) { |
| selectItem (item, false); |
| setFocusItem (item, true); |
| redrawItem (i, true); |
| showItem (item); |
| Event newEvent = new Event (); |
| newEvent.item = item; |
| notifyListeners (SWT.Selection, newEvent); |
| return; |
| } |
| } |
| } |
| } |
| void onMouseDoubleClick (Event event) { |
| if (!isFocusControl ()) setFocus (); |
| int index = (event.y - getHeaderHeight ()) / itemHeight + topIndex; |
| if (!(0 <= index && index < itemsCount)) return; /* not on an available item */ |
| CTableItem selectedItem = items [index]; |
| |
| /* |
| * If the two clicks of the double click did not occur over the same item then do not |
| * consider this to be a default selection. |
| */ |
| if (selectedItem != lastClickedItem) return; |
| |
| if (!selectedItem.getHitBounds ().contains (event.x, event.y)) return; /* considers x */ |
| |
| Event newEvent = new Event (); |
| newEvent.item = selectedItem; |
| notifyListeners (SWT.DefaultSelection, newEvent); |
| } |
| void onMouseDown (Event event) { |
| if (!isFocusControl ()) forceFocus (); |
| int index = (event.y - getHeaderHeight ()) / itemHeight + topIndex; |
| if (!(0 <= index && index < itemsCount)) return; /* not on an available item */ |
| CTableItem selectedItem = items [index]; |
| |
| /* if click was in checkbox */ |
| if ((getStyle () & SWT.CHECK) != 0 && selectedItem.getCheckboxBounds ().contains (event.x, event.y)) { |
| if (event.button != 1) return; |
| selectedItem.setChecked (!selectedItem.checked); |
| Event newEvent = new Event (); |
| newEvent.item = selectedItem; |
| newEvent.detail = SWT.CHECK; |
| notifyListeners (SWT.Selection, newEvent); |
| return; |
| } |
| |
| if (!selectedItem.getHitBounds ().contains (event.x, event.y)) return; |
| |
| if ((event.stateMask & SWT.SHIFT) == 0 && event.keyCode != SWT.SHIFT) anchorItem = null; |
| |
| boolean sendSelection = true; |
| /* Detect when this is the second click of a DefaultSelection and don't fire Selection */ |
| if (lastSelectionEvent != null && lastSelectionEvent.item == selectedItem) { |
| if (event.time - lastSelectionEvent.time <= display.getDoubleClickTime ()) { |
| sendSelection = false; |
| } else { |
| lastSelectionEvent = event; |
| event.item = selectedItem; |
| } |
| } else { |
| lastSelectionEvent = event; |
| event.item = selectedItem; |
| } |
| |
| if ((getStyle () & SWT.SINGLE) != 0) { |
| if (!selectedItem.isSelected ()) { |
| if (event.button == 1) { |
| selectItem (selectedItem, false); |
| setFocusItem (selectedItem, true); |
| redrawItem (selectedItem.index, true); |
| if (sendSelection) { |
| Event newEvent = new Event (); |
| newEvent.item = selectedItem; |
| notifyListeners (SWT.Selection, newEvent); |
| } |
| return; |
| } |
| if ((event.stateMask & (SWT.CTRL | SWT.SHIFT)) == 0) { |
| selectItem (selectedItem, false); |
| setFocusItem (selectedItem, true); |
| redrawItem (selectedItem.index, true); |
| if (sendSelection) { |
| Event newEvent = new Event (); |
| newEvent.item = selectedItem; |
| notifyListeners (SWT.Selection, newEvent); |
| } |
| return; |
| } |
| } |
| /* item is selected */ |
| if (event.button == 1) { |
| /* fire a selection event, though the selection did not change */ |
| if (sendSelection) { |
| Event newEvent = new Event (); |
| newEvent.item = selectedItem; |
| notifyListeners (SWT.Selection, newEvent); |
| } |
| return; |
| } |
| } |
| /* SWT.MULTI */ |
| if (!selectedItem.isSelected ()) { |
| if (event.button == 1) { |
| if ((event.stateMask & (SWT.CTRL | SWT.SHIFT)) == SWT.SHIFT) { |
| if (anchorItem == null) anchorItem = focusItem; |
| int anchorIndex = anchorItem.index; |
| int selectIndex = selectedItem.index; |
| CTableItem[] newSelection = new CTableItem [Math.abs (anchorIndex - selectIndex) + 1]; |
| int step = anchorIndex < selectIndex ? 1 : -1; |
| int writeIndex = 0; |
| for (int i = anchorIndex; i != selectIndex; i += step) { |
| newSelection [writeIndex++] = items [i]; |
| } |
| newSelection [writeIndex] = items [selectIndex]; |
| setSelection (newSelection, false); |
| setFocusItem (selectedItem, true); |
| redrawItems ( |
| Math.min (anchorIndex, selectIndex), |
| Math.max (anchorIndex, selectIndex), |
| true); |
| if (sendSelection) { |
| Event newEvent = new Event (); |
| newEvent.item = selectedItem; |
| notifyListeners (SWT.Selection, newEvent); |
| } |
| return; |
| } |
| selectItem (selectedItem, (event.stateMask & SWT.CTRL) != 0); |
| setFocusItem (selectedItem, true); |
| redrawItem (selectedItem.index, true); |
| if (sendSelection) { |
| Event newEvent = new Event (); |
| newEvent.item = selectedItem; |
| notifyListeners (SWT.Selection, newEvent); |
| } |
| return; |
| } |
| /* button 3 */ |
| if ((event.stateMask & (SWT.CTRL | SWT.SHIFT)) == 0) { |
| selectItem (selectedItem, false); |
| setFocusItem (selectedItem, true); |
| redrawItem (selectedItem.index, true); |
| if (sendSelection) { |
| Event newEvent = new Event (); |
| newEvent.item = selectedItem; |
| notifyListeners (SWT.Selection, newEvent); |
| } |
| return; |
| } |
| } |
| /* item is selected */ |
| if (event.button != 1) return; |
| if ((event.stateMask & SWT.CTRL) != 0) { |
| removeSelectedItem (getSelectionIndex (selectedItem)); |
| setFocusItem (selectedItem, true); |
| redrawItem (selectedItem.index, true); |
| if (sendSelection) { |
| Event newEvent = new Event (); |
| newEvent.item = selectedItem; |
| notifyListeners (SWT.Selection, newEvent); |
| } |
| return; |
| } |
| if ((event.stateMask & SWT.SHIFT) != 0) { |
| if (anchorItem == null) anchorItem = focusItem; |
| int anchorIndex = anchorItem.index; |
| int selectIndex = selectedItem.index; |
| CTableItem[] newSelection = new CTableItem [Math.abs (anchorIndex - selectIndex) + 1]; |
| int step = anchorIndex < selectIndex ? 1 : -1; |
| int writeIndex = 0; |
| for (int i = anchorIndex; i != selectIndex; i += step) { |
| newSelection [writeIndex++] = items [i]; |
| } |
| newSelection [writeIndex] = items [selectIndex]; |
| setSelection (newSelection, false); |
| setFocusItem (selectedItem, true); |
| redrawItems ( |
| Math.min (anchorIndex, selectIndex), |
| Math.max (anchorIndex, selectIndex), |
| true); |
| if (sendSelection) { |
| Event newEvent = new Event (); |
| newEvent.item = selectedItem; |
| notifyListeners (SWT.Selection, newEvent); |
| } |
| return; |
| } |
| selectItem (selectedItem, false); |
| setFocusItem (selectedItem, true); |
| redrawItem (selectedItem.index, true); |
| if (sendSelection) { |
| Event newEvent = new Event (); |
| newEvent.item = selectedItem; |
| notifyListeners (SWT.Selection, newEvent); |
| } |
| } |
| void onMouseUp (Event event) { |
| int index = (event.y - getHeaderHeight ()) / itemHeight + topIndex; |
| if (!(0 <= index && index < itemsCount)) return; /* not on an available item */ |
| lastClickedItem = items [index]; |
| } |
| void onPageDown (int stateMask) { |
| int visibleItemCount = (clientArea.height - getHeaderHeight ()) / itemHeight; |
| if ((stateMask & (SWT.CTRL | SWT.SHIFT)) == 0) { |
| /* PageDown with no modifiers */ |
| int newFocusIndex = focusItem.index + visibleItemCount - 1; |
| newFocusIndex = Math.min (newFocusIndex, itemsCount - 1); |
| if (newFocusIndex == focusItem.index) return; |
| CTableItem item = items [newFocusIndex]; |
| selectItem (item, false); |
| setFocusItem (item, true); |
| showItem (item); |
| redrawItem (item.index, true); |
| return; |
| } |
| if ((stateMask & (SWT.CTRL | SWT.SHIFT)) == (SWT.CTRL | SWT.SHIFT)) { |
| /* CTRL+Shift+PageDown */ |
| int newTopIndex = topIndex + visibleItemCount; |
| newTopIndex = Math.min (newTopIndex, itemsCount - visibleItemCount); |
| if (newTopIndex == topIndex) return; |
| setTopIndex (newTopIndex); |
| return; |
| } |
| if ((getStyle () & SWT.SINGLE) != 0) { |
| if ((stateMask & SWT.SHIFT) != 0) { |
| /* Shift+PageDown */ |
| int newFocusIndex = focusItem.index + visibleItemCount - 1; |
| newFocusIndex = Math.min (newFocusIndex, itemsCount - 1); |
| if (newFocusIndex == focusItem.index) return; |
| CTableItem item = items [newFocusIndex]; |
| selectItem (item, false); |
| setFocusItem (item, true); |
| showItem (item); |
| redrawItem (item.index, true); |
| return; |
| } |
| /* CTRL+PageDown */ |
| int newTopIndex = topIndex + visibleItemCount; |
| newTopIndex = Math.min (newTopIndex, itemsCount - visibleItemCount); |
| if (newTopIndex == topIndex) return; |
| setTopIndex (newTopIndex); |
| return; |
| } |
| /* SWT.MULTI */ |
| if ((stateMask & SWT.CTRL) != 0) { |
| /* CTRL+PageDown */ |
| int bottomIndex = Math.min (topIndex + visibleItemCount - 1, itemsCount - 1); |
| if (focusItem.index != bottomIndex) { |
| /* move focus to bottom item in viewport */ |
| setFocusItem (items [bottomIndex], true); |
| redrawItem (bottomIndex, true); |
| } else { |
| /* at bottom of viewport, so set focus to bottom item one page down */ |
| int newFocusIndex = Math.min (itemsCount - 1, bottomIndex + visibleItemCount); |
| if (newFocusIndex == focusItem.index) return; |
| setFocusItem (items [newFocusIndex], true); |
| showItem (items [newFocusIndex]); |
| redrawItem (newFocusIndex, true); |
| } |
| return; |
| } |
| /* Shift+PageDown */ |
| if (anchorItem == null) anchorItem = focusItem; |
| int anchorIndex = anchorItem.index; |
| int bottomIndex = Math.min (topIndex + visibleItemCount - 1, itemsCount - 1); |
| int selectIndex; |
| if (focusItem.index != bottomIndex) { |
| /* select from focus to bottom item in viewport */ |
| selectIndex = bottomIndex; |
| } else { |
| /* already at bottom of viewport, so select to bottom of one page down */ |
| selectIndex = Math.min (itemsCount - 1, bottomIndex + visibleItemCount); |
| if (selectIndex == focusItem.index && focusItem.isSelected ()) return; |
| } |
| CTableItem selectedItem = items [selectIndex]; |
| CTableItem[] newSelection = new CTableItem [Math.abs (anchorIndex - selectIndex) + 1]; |
| int step = anchorIndex < selectIndex ? 1 : -1; |
| int writeIndex = 0; |
| for (int i = anchorIndex; i != selectIndex; i += step) { |
| newSelection [writeIndex++] = items [i]; |
| } |
| newSelection [writeIndex] = items [selectIndex]; |
| setSelection (newSelection, false); |
| setFocusItem (selectedItem, true); |
| showItem (selectedItem); |
| Event newEvent = new Event (); |
| newEvent.item = selectedItem; |
| notifyListeners (SWT.Selection, newEvent); |
| } |
| void onPageUp (int stateMask) { |
| int visibleItemCount = (clientArea.height - getHeaderHeight ()) / itemHeight; |
| if ((stateMask & (SWT.CTRL | SWT.SHIFT)) == 0) { |
| /* PageUp with no modifiers */ |
| int newFocusIndex = Math.max (0, focusItem.index - visibleItemCount + 1); |
| if (newFocusIndex == focusItem.index) return; |
| CTableItem item = items [newFocusIndex]; |
| selectItem (item, false); |
| setFocusItem (item, true); |
| showItem (item); |
| redrawItem (item.index, true); |
| return; |
| } |
| if ((stateMask & (SWT.CTRL | SWT.SHIFT)) == (SWT.CTRL | SWT.SHIFT)) { |
| /* CTRL+Shift+PageUp */ |
| int newTopIndex = Math.max (0, topIndex - visibleItemCount); |
| if (newTopIndex == topIndex) return; |
| setTopIndex (newTopIndex); |
| return; |
| } |
| if ((getStyle () & SWT.SINGLE) != 0) { |
| if ((stateMask & SWT.SHIFT) != 0) { |
| /* Shift+PageUp */ |
| int newFocusIndex = Math.max (0, focusItem.index - visibleItemCount + 1); |
| if (newFocusIndex == focusItem.index) return; |
| CTableItem item = items [newFocusIndex]; |
| selectItem (item, false); |
| setFocusItem (item, true); |
| showItem (item); |
| redrawItem (item.index, true); |
| return; |
| } |
| /* CTRL+PageUp */ |
| int newTopIndex = Math.max (0, topIndex - visibleItemCount); |
| if (newTopIndex == topIndex) return; |
| setTopIndex (newTopIndex); |
| return; |
| } |
| /* SWT.MULTI */ |
| if ((stateMask & SWT.CTRL) != 0) { |
| /* CTRL+PageUp */ |
| if (focusItem.index != topIndex) { |
| /* move focus to top item in viewport */ |
| setFocusItem (items [topIndex], true); |
| redrawItem (topIndex, true); |
| } else { |
| /* at top of viewport, so set focus to top item one page up */ |
| int newFocusIndex = Math.max (0, focusItem.index - visibleItemCount); |
| if (newFocusIndex == focusItem.index) return; |
| setFocusItem (items [newFocusIndex], true); |
| showItem (items [newFocusIndex]); |
| redrawItem (newFocusIndex, true); |
| } |
| return; |
| } |
| /* Shift+PageUp */ |
| if (anchorItem == null) anchorItem = focusItem; |
| int anchorIndex = anchorItem.index; |
| int selectIndex; |
| if (focusItem.index != topIndex) { |
| /* select from focus to top item in viewport */ |
| selectIndex = topIndex; |
| } else { |
| /* already at top of viewport, so select to top of one page up */ |
| selectIndex = Math.max (0, topIndex - visibleItemCount); |
| if (selectIndex == focusItem.index && focusItem.isSelected ()) return; |
| } |
| CTableItem selectedItem = items [selectIndex]; |
| CTableItem[] newSelection = new CTableItem [Math.abs (anchorIndex - selectIndex) + 1]; |
| int step = anchorIndex < selectIndex ? 1 : -1; |
| int writeIndex = 0; |
| for (int i = anchorIndex; i != selectIndex; i += step) { |
| newSelection [writeIndex++] = items [i]; |
| } |
| newSelection [writeIndex] = items [selectIndex]; |
| setSelection (newSelection, false); |
| setFocusItem (selectedItem, true); |
| showItem (selectedItem); |
| Event newEvent = new Event (); |
| newEvent.item = selectedItem; |
| notifyListeners (SWT.Selection, newEvent); |
| } |
| void onPaint (Event event) { |
| CTableColumn[] orderedColumns = getOrderedColumns (); |
| GC gc = event.gc; |
| Rectangle clipping = gc.getClipping (); |
| int headerHeight = getHeaderHeight (); |
| int numColumns = orderedColumns.length; |
| int startColumn = -1, endColumn = -1; |
| if (numColumns > 0) { |
| startColumn = computeColumnIntersect (clipping.x, 0); |
| if (startColumn != -1) { /* the clip x is within a column's bounds */ |
| endColumn = computeColumnIntersect (clipping.x + clipping.width, startColumn); |
| if (endColumn == -1) endColumn = numColumns - 1; |
| } |
| } else { |
| startColumn = endColumn = 0; |
| } |
| |
| /* Determine the items to be painted */ |
| int startIndex = (clipping.y - headerHeight) / itemHeight + topIndex; |
| int endIndex = -1; |
| if (startIndex < itemsCount) { |
| endIndex = startIndex + (int)Math.ceil((float)clipping.height / itemHeight); |
| } |
| startIndex = Math.max (0, startIndex); |
| endIndex = Math.min (endIndex, itemsCount - 1); |
| |
| /* fill background not handled by items */ |
| gc.setBackground (getBackground ()); |
| gc.setClipping (clipping); |
| int bottomY = endIndex >= 0 ? getItemY (items [endIndex]) + itemHeight : 0; |
| int fillHeight = Math.max (0, clientArea.height - bottomY); |
| if (fillHeight > 0) { /* space below bottom item */ |
| gc.fillRectangle (0, bottomY, clientArea.width, fillHeight); |
| //drawBackground (gc, 0, bottomY, clientArea.width, fillHeight); |
| } |
| if (columns.length > 0) { |
| CTableColumn column = orderedColumns [orderedColumns.length - 1]; /* last column */ |
| int rightX = column.getX () + column.width; |
| if (rightX < clientArea.width) { |
| gc.fillRectangle (rightX, 0, clientArea.width - rightX, clientArea.height - fillHeight); |
| //drawBackground (gc, rightX, 0, clientArea.width - rightX, clientArea.height - fillHeight); |
| } |
| } |
| |
| /* paint the items */ |
| boolean noFocusDraw = false; |
| int[] lineDash = gc.getLineDash (); |
| int lineWidth = gc.getLineWidth (); |
| for (int i = startIndex; i <= Math.min (endIndex, itemsCount - 1); i++) { |
| CTableItem item = items [i]; |
| if (!item.isDisposed ()) { /* ensure that item was not disposed in a callback */ |
| if (startColumn == -1) { |
| /* indicates that region to paint is to the right of the last column */ |
| noFocusDraw = item.paint (gc, null, true) || noFocusDraw; |
| } else { |
| if (numColumns == 0) { |
| noFocusDraw = item.paint (gc, null, false) || noFocusDraw; |
| } else { |
| for (int j = startColumn; j <= Math.min (endColumn, columns.length - 1); j++) { |
| if (!item.isDisposed ()) { /* ensure that item was not disposed in a callback */ |
| noFocusDraw = item.paint (gc, orderedColumns [j], false) || noFocusDraw; |
| } |
| if (isDisposed () || gc.isDisposed ()) return; /* ensure that receiver was not disposed in a callback */ |
| } |
| } |
| } |
| } |
| if (isDisposed () || gc.isDisposed ()) return; /* ensure that receiver was not disposed in a callback */ |
| } |
| |
| /* repaint grid lines */ |
| gc.setClipping(clipping); |
| gc.setLineWidth (lineWidth); |
| if (linesVisible) { |
| gc.setForeground (display.getSystemColor (SWT.COLOR_WIDGET_LIGHT_SHADOW)); |
| gc.setLineDash (lineDash); |
| if (numColumns > 0 && startColumn != -1) { |
| /* vertical column lines */ |
| for (int i = startColumn; i <= endColumn; i++) { |
| int x = orderedColumns [i].getX () + orderedColumns [i].width - 1; |
| gc.drawLine (x, clipping.y, x, clipping.y + clipping.height); |
| } |
| } |
| /* horizontal item lines */ |
| bottomY = clipping.y + clipping.height; |
| int rightX = clipping.x + clipping.width; |
| int y = (clipping.y - headerHeight) / itemHeight * itemHeight + headerHeight; |
| while (y <= bottomY) { |
| gc.drawLine (clipping.x, y, rightX, y); |
| y += itemHeight; |
| } |
| } |
| |
| /* paint focus rectangle */ |
| if (!noFocusDraw && isFocusControl ()) { |
| if (focusItem != null) { |
| Rectangle focusBounds = focusItem.getFocusBounds (); |
| if (focusBounds.width > 0) { |
| gc.setForeground (display.getSystemColor (SWT.COLOR_BLACK)); |
| gc.setClipping (focusBounds); |
| if (focusItem.isSelected ()) { |
| gc.setLineDash (new int[] {2, 2}); |
| } else { |
| gc.setLineDash (new int[] {1, 1}); |
| } |
| gc.drawFocus (focusBounds.x, focusBounds.y, focusBounds.width, focusBounds.height); |
| } |
| } else { |
| /* no items, so draw focus border around Table */ |
| int y = headerHeight + 1; |
| int width = Math.max (0, clientArea.width - 2); |
| int height = Math.max (0, clientArea.height - headerHeight - 2); |
| gc.setForeground (display.getSystemColor (SWT.COLOR_BLACK)); |
| gc.setClipping (1, y, width, height); |
| gc.setLineDash (new int[] {1, 1}); |
| gc.drawFocus (1, y, width, height); |
| } |
| } |
| } |
| void onResize (Event event) { |
| clientArea = getClientArea (); |
| /* vertical scrollbar */ |
| ScrollBar vBar = getVerticalBar (); |
| if (vBar != null) { |
| int clientHeight = (clientArea.height - getHeaderHeight ()) / itemHeight; |
| int thumb = Math.min (clientHeight, itemsCount); |
| vBar.setThumb (thumb); |
| vBar.setPageIncrement (thumb); |
| int index = vBar.getSelection (); |
| if (index != topIndex) { |
| topIndex = index; |
| redraw (); |
| } |
| boolean visible = clientHeight < itemsCount; |
| if (visible != vBar.getVisible ()) { |
| vBar.setVisible (visible); |
| clientArea = getClientArea (); |
| } |
| } |
| |
| /* horizontal scrollbar */ |
| ScrollBar hBar = getHorizontalBar (); |
| if (hBar != null) { |
| int hBarMaximum = hBar.getMaximum (); |
| int thumb = Math.min (clientArea.width, hBarMaximum); |
| hBar.setThumb (thumb); |
| hBar.setPageIncrement (thumb); |
| horizontalOffset = hBar.getSelection (); |
| boolean visible = clientArea.width < hBarMaximum; |
| if (visible != hBar.getVisible ()) { |
| hBar.setVisible (visible); |
| clientArea = getClientArea (); |
| } |
| } |
| |
| /* header */ |
| int headerHeight = Math.max (fontHeight, headerImageHeight) + 2 * getHeaderPadding (); |
| header.setSize (clientArea.width, headerHeight); |
| |
| /* if this is the focus control but there are no items then the boundary focus ring must be repainted */ |
| if (itemsCount == 0 && isFocusControl ()) redraw (); |
| } |
| void onScrollHorizontal (Event event) { |
| ScrollBar hBar = getHorizontalBar (); |
| if (hBar == null) return; |
| int newSelection = hBar.getSelection (); |
| update (); |
| if (itemsCount > 0) { |
| GC gc = new GC (this); |
| gc.copyArea ( |
| 0, 0, |
| clientArea.width, clientArea.height, |
| horizontalOffset - newSelection, 0); |
| gc.dispose (); |
| } else { |
| redraw (); /* ensure that static focus rectangle updates properly */ |
| } |
| |
| if (drawCount <= 0 && header.isVisible ()) { |
| header.update (); |
| Rectangle headerClientArea = header.getClientArea (); |
| GC gc = new GC (header); |
| gc.copyArea ( |
| 0, 0, |
| headerClientArea.width, headerClientArea.height, |
| horizontalOffset - newSelection, 0); |
| gc.dispose (); |
| } |
| horizontalOffset = newSelection; |
| } |
| void onScrollVertical (Event event) { |
| ScrollBar vBar = getVerticalBar (); |
| if (vBar == null) return; |
| int newSelection = vBar.getSelection (); |
| update (); |
| GC gc = new GC (this); |
| gc.copyArea ( |
| 0, 0, |
| clientArea.width, clientArea.height, |
| 0, (topIndex - newSelection) * itemHeight); |
| gc.dispose (); |
| topIndex = newSelection; |
| } |
| void onSpace () { |
| if (focusItem == null) return; |
| if (!focusItem.isSelected ()) { |
| selectItem (focusItem, (getStyle () & SWT.MULTI) != 0); |
| redrawItem (focusItem.index, true); |
| } |
| if ((getStyle () & SWT.CHECK) != 0) { |
| focusItem.setChecked (!focusItem.checked); |
| } |
| showItem (focusItem); |
| Event event = new Event (); |
| event.item = focusItem; |
| notifyListeners (SWT.Selection, event); |
| if ((getStyle () & SWT.CHECK) == 0) return; |
| |
| /* SWT.CHECK */ |
| event = new Event (); |
| event.item = focusItem; |
| event.detail = SWT.CHECK; |
| notifyListeners (SWT.Selection, event); |
| } |
| /* |
| * The current focus item is about to become unavailable, so reassign focus. |
| */ |
| void reassignFocus () { |
| if (focusItem == null) return; |
| |
| /* |
| * reassign to the previous root-level item if there is one, or the next |
| * root-level item otherwise |
| */ |
| int index = focusItem.index; |
| if (index != 0) { |
| index--; |
| } else { |
| index++; |
| } |
| if (index < itemsCount) { |
| CTableItem item = items [index]; |
| setFocusItem (item, false); |
| showItem (item); |
| } else { |
| setFocusItem (null, false); /* no items left */ |
| } |
| } |
| @Override |
| public void redraw () { |
| checkWidget (); |
| if (drawCount <= 0) super.redraw (); |
| } |
| @Override |
| public void redraw (int x, int y, int width, int height, boolean all) { |
| checkWidget (); |
| if (drawCount <= 0) super.redraw (x, y, width, height, all); |
| } |
| /* |
| * Redraws from the specified index down to the last available item inclusive. Note |
| * that the redraw bounds do not extend beyond the current last item, so clients |
| * that reduce the number of available items should use #redrawItems(int,int) instead |
| * to ensure that redrawing extends down to the previous bottom item boundary. |
| */ |
| void redrawFromItemDownwards (int index) { |
| redrawItems (index, itemsCount - 1, false); |
| } |
| /* |
| * Redraws the table item at the specified index. It is valid for this index to reside |
| * beyond the last available item. |
| */ |
| void redrawItem (int itemIndex, boolean focusBoundsOnly) { |
| if (itemIndex < itemsCount && !items [itemIndex].isInViewport ()) return; |
| redrawItems (itemIndex, itemIndex, focusBoundsOnly); |
| } |
| /* |
| * Redraws the table between the start and end item indices inclusive. It is valid |
| * for the end index value to extend beyond the last available item. |
| */ |
| void redrawItems (int startIndex, int endIndex, boolean focusBoundsOnly) { |
| if (drawCount > 0) return; |
| |
| int startY = (startIndex - topIndex) * itemHeight + getHeaderHeight (); |
| int height = (endIndex - startIndex + 1) * itemHeight; |
| if (focusBoundsOnly) { |
| boolean custom = isListening (SWT.EraseItem) || isListening (SWT.PaintItem); |
| if (!custom && columns.length > 0) { |
| CTableColumn lastColumn; |
| if ((getStyle () & SWT.FULL_SELECTION) != 0) { |
| CTableColumn[] orderedColumns = getOrderedColumns (); |
| lastColumn = orderedColumns [orderedColumns.length - 1]; |
| } else { |
| lastColumn = columns [0]; |
| } |
| int rightX = lastColumn.getX () + lastColumn.getWidth (); |
| if (rightX <= 0) return; /* focus column(s) not visible */ |
| } |
| endIndex = Math.min (endIndex, itemsCount - 1); |
| for (int i = startIndex; i <= endIndex; i++) { |
| CTableItem item = items [i]; |
| if (item.isInViewport ()) { |
| /* if custom painting is being done then repaint the full item */ |
| if (custom) { |
| redraw (0, getItemY (item), clientArea.width, itemHeight, false); |
| } else { |
| /* repaint the item's focus bounds */ |
| Rectangle bounds = item.getFocusBounds (); |
| redraw (bounds.x, startY, bounds.width, height, false); |
| } |
| } |
| } |
| } else { |
| redraw (0, startY, clientArea.width, height, false); |
| } |
| } |
| /** |
| * Removes the item from the receiver at the given |
| * zero-relative index. |
| * |
| * @param index the index for the item |
| * |
| * @exception IllegalArgumentException <ul> |
| * <li>ERROR_INVALID_RANGE - if the index is not between 0 and the number of elements in the list minus 1 (inclusive)</li> |
| * </ul> |
| * @exception SWTException <ul> |
| * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> |
| * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> |
| * </ul> |
| */ |
| public void remove (int index) { |
| checkWidget (); |
| if (!(0 <= index && index < itemsCount)) SWT.error (SWT.ERROR_INVALID_RANGE); |
| items [index].dispose (); |
| int[] eventData = new int[5]; |
| eventData[0] = ACC.DELETE; |
| eventData[1] = index; |
| eventData[2] = 1; |
| eventData[3] = 0; |
| eventData[4] = 0; |
| getAccessible().sendEvent(ACC.EVENT_TABLE_CHANGED, eventData); |
| } |
| /** |
| * Removes the items from the receiver which are |
| * between the given zero-relative start and end |
| * indices (inclusive). |
| * |
| * @param start the start of the range |
| * @param end the end of the range |
| * |
| * @exception IllegalArgumentException <ul> |
| * <li>ERROR_INVALID_RANGE - if either the start or end are not between 0 and the number of elements in the list minus 1 (inclusive)</li> |
| * </ul> |
| * @exception SWTException <ul> |
| * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> |
| * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> |
| * </ul> |
| */ |
| public void remove (int start, int end) { |
| checkWidget (); |
| if (start > end) return; |
| if (!(0 <= start && start <= end && end < itemsCount)) { |
| SWT.error (SWT.ERROR_INVALID_RANGE); |
| } |
| if (start == 0 && end == itemsCount - 1) { |
| removeAll (); |
| } else { |
| for (int i = end; i >= start; i--) { |
| items [i].dispose (); |
| } |
| |
| int[] eventData = new int[5]; |
| eventData[0] = ACC.DELETE; |
| eventData[1] = start; |
| eventData[2] = end - start + 1; |
| eventData[3] = 0; |
| eventData[4] = 0; |
| getAccessible().sendEvent(ACC.EVENT_TABLE_CHANGED, eventData); |
| |
| } |
| } |
| /** |
| * Removes the items from the receiver's list at the given |
| * zero-relative indices. |
| * |
| * @param indices the array of indices of the items |
| * |
| * @exception IllegalArgumentException <ul> |
| * <li>ERROR_INVALID_RANGE - if the index is not between 0 and the number of elements in the list minus 1 (inclusive)</li> |
| * <li>ERROR_NULL_ARGUMENT - if the indices array is null</li> |
| * </ul> |
| * @exception SWTException <ul> |
| * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> |
| * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> |
| * </ul> |
| */ |
| public void remove (int [] indices) { |
| checkWidget (); |
| if (indices == null) SWT.error (SWT.ERROR_NULL_ARGUMENT); |
| if (indices.length == 0) return; |
| int [] newIndices = new int [indices.length]; |
| System.arraycopy (indices, 0, newIndices, 0, indices.length); |
| sortDescent (newIndices); |
| int start = newIndices [newIndices.length - 1], end = newIndices [0]; |
| if (!(0 <= start && start <= end && end < itemsCount)) { |
| SWT.error (SWT.ERROR_INVALID_RANGE); |
| } |
| int lastRemovedIndex = -1; |
| int[] eventData = new int[5]; |
| for (int newIndice : newIndices) { |
| if (newIndice != lastRemovedIndex) { |
| items [newIndice].dispose (); |
| eventData[0] = ACC.DELETE; |
| eventData[1] = newIndice; |
| eventData[2] = 1; |
| eventData[3] = 0; |
| eventData[4] = 0; |
| getAccessible().sendEvent(ACC.EVENT_TABLE_CHANGED, eventData); |
| lastRemovedIndex = newIndice; |
| } |
| } |
| } |
| /** |
| * Removes all of the items from the receiver. |
| * |
| * @exception SWTException <ul> |
| * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> |
| * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> |
| * </ul> |
| */ |
| public void removeAll () { |
| checkWidget (); |
| if (itemsCount == 0) return; |
| setRedraw (false); |
| |
| setFocusItem (null, false); |
| for (int i = 0; i < itemsCount; i++) { |
| items [i].dispose (false); |
| } |
| items = new CTableItem [0]; |
| selectedItems = new CTableItem [0]; |
| int oldCount = itemsCount; |
| itemsCount = topIndex = 0; |
| anchorItem = lastClickedItem = null; |
| lastSelectionEvent = null; |
| |
| int[] eventData = new int[5]; |
| eventData[0] = ACC.DELETE; |
| eventData[1] = 0; |
| eventData[2] = oldCount; |
| eventData[3] = 0; |
| eventData[4] = 0; |
| getAccessible().sendEvent(ACC.EVENT_TABLE_CHANGED, eventData); |
| |
| ScrollBar vBar = getVerticalBar (); |
| if (vBar != null) { |
| vBar.setMaximum (1); |
| vBar.setVisible (false); |
| } |
| if (columns.length == 0) { |
| horizontalOffset = 0; |
| ScrollBar hBar = getHorizontalBar (); |
| if (hBar != null) { |
| hBar.setMaximum (1); |
| hBar.setVisible (false); |
| } |
| } |
| |
| setRedraw (true); |
| } |
| String removeMnemonics (String string) { |
| /* removes single ampersands and preserves double-ampersands */ |
| char [] chars = new char [string.length ()]; |
| string.getChars (0, chars.length, chars, 0); |
| int i = 0, j = 0; |
| for ( ; i < chars.length; i++, j++) { |
| if (chars[i] == '&') { |
| if (++i == chars.length) break; |
| if (chars[i] == '&') { |
| chars[j++] = chars[i - 1]; |
| } |
| } |
| chars[j] = chars[i]; |
| } |
| if (i == j) return string; |
| return new String (chars, 0, j); |
| } |
| void removeSelectedItem (int index) { |
| CTableItem[] newSelectedItems = new CTableItem [selectedItems.length - 1]; |
| System.arraycopy (selectedItems, 0, newSelectedItems, 0, index); |
| System.arraycopy (selectedItems, index + 1, newSelectedItems, index, newSelectedItems.length - index); |
| selectedItems = newSelectedItems; |
| } |
| /** |
| * Removes the listener from the collection of listeners who will |
| * be notified when the user changes the receiver's selection. |
| * |
| * @param listener the listener which should no longer be notified |
| * |
| * @exception IllegalArgumentException <ul> |
| * <li>ERROR_NULL_ARGUMENT - if the listener is null</li> |
| * </ul> |
| * @exception SWTException <ul> |
| * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> |
| * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> |
| * </ul> |
| * |
| * @see SelectionListener |
| * @see #addSelectionListener(SelectionListener) |
| */ |
| public void removeSelectionListener (SelectionListener listener) { |
| checkWidget (); |
| if (listener == null) SWT.error (SWT.ERROR_NULL_ARGUMENT); |
| removeListener (SWT.Selection, listener); |
| removeListener (SWT.DefaultSelection, 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. |
| * |
| * @param index the index of the item to select |
| * |
| * @exception SWTException <ul> |
| * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> |
| * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> |
| * </ul> |
| */ |
| public void select (int index) { |
| checkWidget (); |
| if (!(0 <= index && index < itemsCount)) return; |
| selectItem (items [index], (getStyle () & SWT.MULTI) != 0); |
| if (isFocusControl () || (getStyle () & SWT.HIDE_SELECTION) == 0) { |
| redrawItem (index, false); |
| } |
| getAccessible().selectionChanged(); |
| } |
| /** |
| * 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> |
| * |
| * @param start the start of the range |
| * @param end the end of the range |
| * |
| * @exception SWTException <ul> |
| * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> |
| * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> |
| * </ul> |
| * |
| * @see CTable#setSelection(int,int) |
| */ |
| public void select (int start, int end) { |
| checkWidget (); |
| if (end < 0 || start > end || ((getStyle () & SWT.SINGLE) != 0 && start != end)) return; |
| if (itemsCount == 0 || start >= itemsCount) return; |
| start = Math.max (start, 0); |
| end = Math.min (end, itemsCount - 1); |
| for (int i = start; i <= end; i++) { |
| selectItem (items [i], (getStyle () & SWT.MULTI) != 0); |
| } |
| if (isFocusControl () || (getStyle () & SWT.HIDE_SELECTION) == 0) { |
| redrawItems (start, end, false); |
| } |
| getAccessible().selectionChanged(); |
| } |
| /** |
| * 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> |
| * |
| * @param indices the array of indices for the items to select |
| * |
| * @exception IllegalArgumentException <ul> |
| * <li>ERROR_NULL_ARGUMENT - if the array of indices is null</li> |
| * </ul> |
| * @exception SWTException <ul> |
| * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> |
| * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> |
| * </ul> |
| * |
| * @see CTable#setSelection(int[]) |
| */ |
| public void select (int [] indices) { |
| checkWidget (); |
| if (indices == null) SWT.error (SWT.ERROR_NULL_ARGUMENT); |
| if (indices.length == 0 || ((getStyle () & SWT.SINGLE) != 0 && indices.length > 1)) return; |
| for (int index : indices) { |
| if (0 <= index && index < itemsCount) { |
| selectItem (items [index], (getStyle () & SWT.MULTI) != 0); |
| } |
| } |
| if (isFocusControl () || (getStyle () & SWT.HIDE_SELECTION) == 0) { |
| for (int index : indices) { |
| if (0 <= index && index < itemsCount) { |
| redrawItem (index, false); |
| } |
| } |
| } |
| getAccessible().selectionChanged(); |
| } |
| /** |
| * Selects all of the items in the receiver. |
| * <p> |
| * If the receiver is single-select, do nothing. |
| * </p> |
| * |
| * @exception SWTException <ul> |
| * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> |
| * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> |
| * </ul> |
| */ |
| public void selectAll () { |
| checkWidget (); |
| if ((getStyle () & SWT.SINGLE) != 0) return; |
| selectedItems = new CTableItem [itemsCount]; |
| System.arraycopy (items, 0, selectedItems, 0, itemsCount); |
| if (isFocusControl () || (getStyle () & SWT.HIDE_SELECTION) == 0) { |
| redraw (); |
| } |
| for (CTableItem selectedItem : selectedItems) { |
| selectedItem.getAccessible(getAccessible(), 0).selectionChanged(); |
| } |
| getAccessible().selectionChanged(); |
| } |
| void selectItem (CTableItem item, boolean addToSelection) { |
| CTableItem[] oldSelectedItems = selectedItems; |
| if (!addToSelection || (getStyle () & SWT.SINGLE) != 0) { |
| selectedItems = new CTableItem[] {item}; |
| if (isFocusControl () || (getStyle () & SWT.HIDE_SELECTION) == 0) { |
| for (CTableItem oldSelectedItem : oldSelectedItems) { |
| if (oldSelectedItem != item) { |
| redrawItem (oldSelectedItem.index, true); |
| } |
| } |
| } |
| for (CTableItem oldSelectedItem : oldSelectedItems) { |
| oldSelectedItem.getAccessible(getAccessible(), 0).selectionChanged(); |
| } |
| } else { |
| if (item.isSelected ()) return; |
| selectedItems = new CTableItem [selectedItems.length + 1]; |
| System.arraycopy (oldSelectedItems, 0, selectedItems, 0, oldSelectedItems.length); |
| selectedItems [selectedItems.length - 1] = item; |
| } |
| |
| item.getAccessible(getAccessible(), 0).selectionChanged(); |
| getAccessible().selectionChanged(); |
| } |
| @Override |
| public void setBackground (Color color) { |
| checkWidget (); |
| if (color == null) color = display.getSystemColor (SWT.COLOR_LIST_BACKGROUND); |
| super.setBackground (color); |
| } |
| @Override |
| public void setForeground (Color color) { |
| checkWidget (); |
| if (color == null) color = display.getSystemColor (SWT.COLOR_LIST_FOREGROUND); |
| super.setForeground (color); |
| } |
| /** |
| * 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 |
| * |
| * @exception SWTException <ul> |
| * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> |
| * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> |
| * </ul> |
| * @exception IllegalArgumentException <ul> |
| * <li>ERROR_NULL_ARGUMENT - if the item order is null</li> |
| * <li>ERROR_INVALID_ARGUMENT - if the item order is not the same length as the number of items</li> |
| * </ul> |
| * |
| * @see CTable#getColumnOrder() |
| * @see CTableColumn#getMoveable() |
| * @see CTableColumn#setMoveable(boolean) |
| * @see SWT#Move |
| * |
| * @since 3.1 |
| */ |
| public void setColumnOrder (int [] order) { |
| checkWidget (); |
| if (order == null) SWT.error (SWT.ERROR_NULL_ARGUMENT); |
| if (columns.length == 0) { |
| if (order.length != 0) SWT.error (SWT.ERROR_INVALID_ARGUMENT); |
| return; |
| } |
| if (order.length != columns.length) SWT.error (SWT.ERROR_INVALID_ARGUMENT); |
| boolean reorder = false; |
| boolean [] seen = new boolean [columns.length]; |
| int[] oldOrder = getColumnOrder (); |
| for (int i = 0; i < order.length; i++) { |
| int index = order [i]; |
| if (index < 0 || index >= columns.length) SWT.error (SWT.ERROR_INVALID_RANGE); |
| if (seen [index]) SWT.error (SWT.ERROR_INVALID_ARGUMENT); |
| seen [index] = true; |
| if (index != oldOrder [i]) reorder = true; |
| } |
| if (!reorder) return; |
| |
| headerHideToolTip (); |
| int[] oldX = new int [columns.length]; |
| for (int i = 0; i < columns.length; i++) { |
| oldX [i] = columns [i].getX (); |
| } |
| orderedColumns = new CTableColumn [order.length]; |
| for (int i = 0; i < order.length; i++) { |
| orderedColumns [i] = columns [order [i]]; |
| } |
| for (CTableColumn orderedColumn : orderedColumns) { |
| CTableColumn column = orderedColumn; |
| if (!column.isDisposed () && column.getX () != oldX [column.getIndex ()]) { |
| column.notifyListeners (SWT.Move, new Event ()); |
| } |
| } |
| |
| redraw (); |
| if (drawCount <= 0 && header.isVisible ()) header.redraw (); |
| } |
| void setFocusItem (CTableItem item, boolean redrawOldFocus) { |
| if (item == focusItem) return; |
| CTableItem oldFocusItem = focusItem; |
| if (oldFocusItem != null) oldFocusItem.getAccessible(getAccessible(), 0).setFocus(ACC.CHILDID_SELF); |
| focusItem = item; |
| if (redrawOldFocus && oldFocusItem != null) { |
| redrawItem (oldFocusItem.index, true); |
| } |
| if (focusItem != null) focusItem.getAccessible(getAccessible(), 0).setFocus(ACC.CHILDID_SELF); |
| } |
| @Override |
| public void setFont (Font value) { |
| checkWidget (); |
| Font oldFont = getFont (); |
| super.setFont (value); |
| Font font = getFont (); |
| if (font.equals (oldFont)) return; |
| |
| GC gc = new GC (this); |
| |
| /* recompute the receiver's cached font height and item height values */ |
| fontHeight = gc.getFontMetrics ().getHeight (); |
| setItemHeight (Math.max (fontHeight, imageHeight) + 2 * getCellPadding ()); |
| Point headerSize = header.getSize (); |
| int newHeaderHeight = Math.max (fontHeight, headerImageHeight) + 2 * getHeaderPadding (); |
| if (headerSize.y != newHeaderHeight) { |
| header.setSize (headerSize.x, newHeaderHeight); |
| } |
| header.setFont (font); |
| |
| /* |
| * Notify all columns and items of the font change so that elements that |
| * use the receiver's font can recompute their cached string widths. |
| */ |
| for (CTableColumn column : columns) { |
| column.updateFont (gc); |
| } |
| for (int i = 0; i < itemsCount; i++) { |
| items [i].updateFont (gc); |
| } |
| |
| gc.dispose (); |
| |
| if (drawCount <= 0 && header.isVisible ()) header.redraw (); |
| |
| /* update scrollbars */ |
| if (columns.length == 0) updateHorizontalBar (); |
| ScrollBar vBar = getVerticalBar (); |
| if (vBar != null) { |
| int thumb = (clientArea.height - getHeaderHeight ()) / itemHeight; |
| vBar.setThumb (thumb); |
| vBar.setPageIncrement (thumb); |
| topIndex = vBar.getSelection (); |
| vBar.setVisible (thumb < vBar.getMaximum ()); |
| } |
| redraw (); |
| } |
| void setHeaderImageHeight (int value) { |
| headerImageHeight = value; |
| Point headerSize = header.getSize (); |
| int newHeaderHeight = Math.max (fontHeight, headerImageHeight) + 2 * getHeaderPadding (); |
| if (headerSize.y != newHeaderHeight) { |
| header.setSize (headerSize.x, newHeaderHeight); |
| } |
| } |
| /** |
| * Marks the receiver's header as visible if the argument is <code>true</code>, |
| * and marks it invisible otherwise. |
| * <p> |
| * If one of the receiver's ancestors is not visible or some |
| * other condition makes the receiver not visible, marking |
| * it visible may not actually cause it to be displayed. |
| * </p> |
| * |
| * @param show the new visibility state |
| * |
| * @exception SWTException <ul> |
| * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> |
| * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> |
| * </ul> |
| */ |
| public void setHeaderVisible (boolean value) { |
| checkWidget (); |
| if (header.getVisible () == value) return; /* no change */ |
| headerHideToolTip (); |
| header.setVisible (value); |
| updateVerticalBar (); |
| redraw (); |
| } |
| void setImageHeight (int value) { |
| imageHeight = value; |
| setItemHeight (Math.max (fontHeight, imageHeight) + 2 * getCellPadding ()); |
| } |
| /** |
| * Sets the number of items contained in the receiver. |
| * |
| * @param count the number of items |
| * |
| * @exception SWTException <ul> |
| * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> |
| * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> |
| * </ul> |
| * |
| * @since 3.0 |
| */ |
| public void setItemCount (int count) { |
| checkWidget (); |
| count = Math.max (0, count); |
| if (count == itemsCount) return; |
| int oldCount = itemsCount; |
| int redrawStart, redrawEnd; |
| |
| /* if the new item count is less than the current count then remove all excess items from the end */ |
| if (count < itemsCount) { |
| redrawStart = count; |
| redrawEnd = itemsCount - 1; |
| for (int i = count; i < itemsCount; i++) { |
| items [i].dispose (false); |
| } |
| |
| int newSelectedCount = 0; |
| for (int i = 0; i < selectedItems.length; i++) { |
| if (!selectedItems [i].isDisposed ()) newSelectedCount++; |
| } |
| if (newSelectedCount != selectedItems.length) { |
| /* one or more selected items have been disposed */ |
| CTableItem[] newSelectedItems = new CTableItem [newSelectedCount]; |
| int pos = 0; |
| for (CTableItem selectedItem : selectedItems) { |
| CTableItem item = selectedItem; |
| if (!item.isDisposed ()) { |
| newSelectedItems [pos++] = item; |
| } |
| } |
| selectedItems = newSelectedItems; |
| } |
| |
| if (anchorItem != null && anchorItem.isDisposed ()) anchorItem = null; |
| if (lastClickedItem != null && lastClickedItem.isDisposed ()) lastClickedItem = null; |
| if (focusItem != null && focusItem.isDisposed ()) { |
| CTableItem newFocusItem = count > 0 ? items [count - 1] : null; |
| setFocusItem (newFocusItem, false); |
| } |
| int[] eventData = new int[5]; |
| eventData[0] = ACC.DELETE; |
| eventData[1] = redrawStart; |
| eventData[2] = redrawEnd - redrawStart; |
| eventData[3] = 0; |
| eventData[4] = 0; |
| getAccessible().sendEvent(ACC.EVENT_TABLE_CHANGED, eventData); |
| |
| itemsCount = count; |
| if (columns.length == 0) updateHorizontalBar (); |
| } else { |
| redrawStart = itemsCount; |
| redrawEnd = count - 1; |
| CTableItem[] newItems = new CTableItem [count]; |
| System.arraycopy (items, 0, newItems, 0, itemsCount); |
| items = newItems; |
| for (int i = itemsCount; i < count; i++) { |
| items [i] = new CTableItem (this, SWT.NONE, i, false); |
| itemsCount++; |
| } |
| |
| int[] eventData = new int[5]; |
| eventData[0] = ACC.INSERT; |
| eventData[1] = redrawStart; |
| eventData[2] = count; |
| eventData[3] = 0; |
| eventData[4] = 0; |
| getAccessible().sendEvent(ACC.EVENT_TABLE_CHANGED, eventData); |
| if (oldCount == 0) focusItem = items [0]; |
| } |
| |
| updateVerticalBar (); |
| /* |
| * If this is the focus control and the item count is going from 0->!0 or !0->0 then the |
| * receiver must be redrawn to ensure that its boundary focus ring is updated. |
| */ |
| if ((oldCount == 0 || itemsCount == 0) && isFocusControl ()) { |
| redraw (); |
| return; |
| } |
| redrawItems (redrawStart, redrawEnd, false); |
| } |
| boolean setItemHeight (int value) { |
| boolean update = !customHeightSet || itemHeight < value; |
| if (update) itemHeight = value; |
| return update; |
| } |
| /** |
| * Marks the receiver's lines as visible if the argument is <code>true</code>, |
| * and marks it invisible otherwise. Note that some platforms draw grid lines |
| * while others may draw alternating row colors. |
| * <p> |
| * If one of the receiver's ancestors is not visible or some |
| * other condition makes the receiver not visible, marking |
| * it visible may not actually cause it to be displayed. |
| * </p> |
| * |
| * @param show the new visibility state |
| * |
| * @exception SWTException <ul> |
| * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> |
| * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> |
| * </ul> |
| */ |
| public void setLinesVisible (boolean value) { |
| checkWidget (); |
| if (linesVisible == value) return; /* no change */ |
| linesVisible = value; |
| redraw (); |
| } |
| @Override |
| public void setMenu (Menu menu) { |
| super.setMenu (menu); |
| header.setMenu (menu); |
| } |
| @Override |
| public void setRedraw (boolean value) { |
| checkWidget(); |
| if (value) { |
| if (--drawCount == 0) { |
| if (items.length - itemsCount > 3) { |
| CTableItem[] newItems = new CTableItem [itemsCount]; |
| System.arraycopy (items, 0, newItems, 0, itemsCount); |
| items = newItems; |
| } |
| updateVerticalBar (); |
| updateHorizontalBar (); |
| } |
| } else { |
| drawCount++; |
| } |
| super.setRedraw (value); |
| header.setRedraw (value); |
| } |
| /** |
| * Sets the receiver's selection to the given item. |
| * The current selection is cleared before the new item is selected. |
| * <p> |
| * If the item is not in the receiver, then it is ignored. |
| * </p> |
| * |
| * @param item the item to select |
| * |
| * @exception IllegalArgumentException <ul> |
| * <li>ERROR_NULL_ARGUMENT - if the item is null</li> |
| * <li>ERROR_INVALID_ARGUMENT - if the item has been disposed</li> |
| * </ul> |
| * @exception SWTException <ul> |
| * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> |
| * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> |
| * </ul> |
| * |
| * @since 3.2 |
| */ |
| public void setSelection (CTableItem item) { |
| checkWidget (); |
| if (item == null) SWT.error (SWT.ERROR_NULL_ARGUMENT); |
| setSelection (new CTableItem[] {item}, true); |
| } |
| /** |
| * 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. |
| * </p> |
| * |
| * @param items the array of items |
| * |
| * @exception IllegalArgumentException <ul> |
| * <li>ERROR_NULL_ARGUMENT - if the array of items is null</li> |
| * <li>ERROR_INVALID_ARGUMENT - if one of the items has been disposed</li> |
| * </ul> |
| * @exception SWTException <ul> |
| * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> |
| * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> |
| * </ul> |
| * |
| * @see CTable#deselectAll() |
| * @see CTable#select(int[]) |
| * @see CTable#setSelection(int[]) |
| */ |
| public void setSelection (CTableItem[] items) { |
| checkWidget (); |
| if (items == null) SWT.error (SWT.ERROR_NULL_ARGUMENT); |
| setSelection (items, true); |
| } |
| void setSelection (CTableItem[] items, boolean updateViewport) { |
| if (items.length == 0 || ((getStyle () & SWT.SINGLE) != 0 && items.length > 1)) { |
| deselectAll (); |
| return; |
| } |
| CTableItem[] oldSelection = selectedItems; |
| |
| /* remove null and duplicate items */ |
| int index = 0; |
| selectedItems = new CTableItem [items.length]; /* assume all valid items */ |
| for (CTableItem item2 : items) { |
| CTableItem item = item2; |
| if (item != null && item.parent == this && !item.isSelected ()) { |
| selectedItems [index++] = item; |
| } |
| } |
| if (index != items.length) { |
| /* an invalid item was provided so resize the array accordingly */ |
| CTableItem[] temp = new CTableItem [index]; |
| System.arraycopy (selectedItems, 0, temp, 0, index); |
| selectedItems = temp; |
| } |
| if (selectedItems.length == 0) { /* no valid items */ |
| deselectAll (); |
| return; |
| } |
| |
| boolean tableSelectionChanged = false; |
| if (isFocusControl () || (getStyle () & SWT.HIDE_SELECTION) == 0) { |
| for (int i = 0; i < oldSelection.length; i++) { |
| if (!oldSelection [i].isSelected ()) { |
| redrawItem (oldSelection [i].index, true); |
| oldSelection[i].getAccessible(getAccessible(), 0).selectionChanged(); |
| tableSelectionChanged = true; |
| } |
| } |
| for (CTableItem selectedItem : selectedItems) { |
| redrawItem (selectedItem.index, true); |
| selectedItem.getAccessible(getAccessible(), 0).selectionChanged(); |
| tableSelectionChanged = true; |
| } |
| } |
| if (updateViewport) { |
| showItem (selectedItems [0]); |
| setFocusItem (selectedItems [0], true); |
| } |
| |
| if (tableSelectionChanged) getAccessible().selectionChanged(); |
| } |
| /** |
| * Sets the column used by the sort indicator for the receiver. A null |
| * value will clear the sort indicator. The current sort column is cleared |
| * before the new column is set. |
| * |
| * @param column the column used by the sort indicator or <code>null</code> |
| * |
| * @exception IllegalArgumentException <ul> |
| * <li>ERROR_INVALID_ARGUMENT - if the column is disposed</li> |
| * </ul> |
| * @exception SWTException <ul> |
| * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> |
| * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> |
| * </ul> |
| * |
| * @since 3.2 |
| */ |
| public void setSortColumn (CTableColumn column) { |
| checkWidget (); |
| if (column != null && column.isDisposed ()) SWT.error (SWT.ERROR_INVALID_ARGUMENT); |
| if (column == sortColumn) return; |
| if (sortColumn != null && !sortColumn.isDisposed ()) { |
| sortColumn.setSortDirection (SWT.NONE); |
| } |
| sortColumn = column; |
| if (sortColumn != null) { |
| sortColumn.setSortDirection (sortDirection); |
| } |
| } |
| /** |
| * Sets the direction of the sort indicator for the receiver. The value |
| * can be one of <code>UP</code>, <code>DOWN</code> or <code>NONE</code>. |
| * |
| * @param direction the direction of the sort indicator |
| * |
| * @exception SWTException <ul> |
| * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> |
| * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> |
| * </ul> |
| * |
| * @since 3.2 |
| */ |
| public void setSortDirection (int direction) { |
| checkWidget (); |
| if (direction != SWT.UP && direction != SWT.DOWN && direction != SWT.NONE) return; |
| sortDirection = direction; |
| if (sortColumn == null || sortColumn.isDisposed ()) return; |
| sortColumn.setSortDirection (sortDirection); |
| } |
| /** |
| * Selects the item at the given zero-relative index in the receiver. |
| * The current selection is first cleared, then the new item is selected. |
| * |
| * @param index the index of the item to select |
| * |
| * @exception SWTException <ul> |
| * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> |
| * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> |
| * </ul> |
| * |
| * @see CTable#deselectAll() |
| * @see CTable#select(int) |
| */ |
| public void setSelection (int index) { |
| checkWidget (); |
| deselectAll (); |
| if (!(0 <= index && index < itemsCount)) return; |
| selectItem (items [index], false); |
| setFocusItem (items [index], true); |
| redrawItem (index, true); |
| showSelection (); |
| getAccessible().selectionChanged(); |
| } |
| /** |
| * 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> |
| * |
| * @param start the start index of the items to select |
| * @param end the end index of the items to select |
| * |
| * @exception SWTException <ul> |
| * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> |
| * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> |
| * </ul> |
| * |
| * @see CTable#deselectAll() |
| * @see CTable#select(int,int) |
| */ |
| public void setSelection (int start, int end) { |
| checkWidget (); |
| deselectAll (); |
| if (end < 0 || start > end || ((getStyle () & SWT.SINGLE) != 0 && start != end)) return; |
| if (itemsCount == 0 || start >= itemsCount) return; |
| start = Math.max (0, start); |
| end = Math.min (end, itemsCount - 1); |
| select (start, end); |
| setFocusItem (items [start], true); |
| showSelection (); |
| } |
| /** |
| * 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> |
| * |
| * @param indices the indices of the items to select |
| * |
| * @exception IllegalArgumentException <ul> |
| * <li>ERROR_NULL_ARGUMENT - if the array of indices is null</li> |
| * </ul> |
| * @exception SWTException <ul> |
| * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> |
| * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> |
| * </ul> |
| * |
| * @see CTable#deselectAll() |
| * @see CTable#select(int[]) |
| */ |
| public void setSelection (int [] indices) { |
| checkWidget (); |
| if (indices == null) SWT.error (SWT.ERROR_NULL_ARGUMENT); |
| deselectAll (); |
| int length = indices.length; |
| if (length == 0 || ((getStyle () & SWT.SINGLE) != 0 && length > 1)) return; |
| select (indices); |
| int focusIndex = -1; |
| for (int i = 0; i < indices.length && focusIndex == -1; i++) { |
| if (0 <= indices [i] && indices [i] < itemsCount) { |
| focusIndex = indices [i]; |
| } |
| } |
| if (focusIndex != -1) setFocusItem (items [focusIndex], true); |
| showSelection (); |
| } |
| /** |
| * Sets the zero-relative index of the item which is currently |
| * at the top of the receiver. This index can change when items |
| * are scrolled or new items are added and removed. |
| * |
| * @param index the index of the top item |
| * |
| * @exception SWTException <ul> |
| * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> |
| * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> |
| * </ul> |
| */ |
| public void setTopIndex (int index) { |
| checkWidget (); |
| if (!(0 <= index && index < itemsCount)) return; |
| int visibleItemCount = (clientArea.height - getHeaderHeight ()) / itemHeight; |
| if (itemsCount <= visibleItemCount) return; |
| index = Math.min (index, itemsCount - visibleItemCount); |
| if (index == topIndex) return; |
| |
| update (); |
| int change = topIndex - index; |
| topIndex = index; |
| ScrollBar vBar = getVerticalBar (); |
| if (vBar != null) vBar.setSelection (topIndex); |
| if (drawCount <= 0) { |
| GC gc = new GC (this); |
| gc.copyArea (0, 0, clientArea.width, clientArea.height, 0, change * itemHeight); |
| gc.dispose (); |
| } |
| } |
| /** |
| * Shows the column. If the column is already showing in the receiver, |
| * this method simply returns. Otherwise, the columns are scrolled until |
| * the column is visible. |
| * |
| * @param column the column to be shown |
| * |
| * @exception IllegalArgumentException <ul> |
| * <li>ERROR_NULL_ARGUMENT - if the column is null</li> |
| * <li>ERROR_INVALID_ARGUMENT - if the 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> |
| * |
| * @since 3.0 |
| */ |
| public void showColumn (CTableColumn column) { |
| checkWidget (); |
| if (column == null) SWT.error (SWT.ERROR_NULL_ARGUMENT); |
| if (column.isDisposed ()) SWT.error(SWT.ERROR_INVALID_ARGUMENT); |
| if (column.parent != this) return; |
| |
| int x = column.getX (); |
| int rightX = x + column.width; |
| if (0 <= x && rightX <= clientArea.width) return; /* column is fully visible */ |
| |
| headerHideToolTip (); |
| int absX = 0; /* the X of the column irrespective of the horizontal scroll */ |
| CTableColumn[] orderedColumns = getOrderedColumns (); |
| for (int i = 0; i < column.getOrderIndex (); i++) { |
| absX += orderedColumns [i].width; |
| } |
| if (x < clientArea.x) { /* column is to left of viewport */ |
| horizontalOffset = absX; |
| } else { |
| horizontalOffset = absX + column.width - clientArea.width; |
| } |
| ScrollBar hBar = getHorizontalBar (); |
| if (hBar != null) hBar.setSelection (horizontalOffset); |
| redraw (); |
| if (drawCount <= 0 && header.isVisible ()) header.redraw (); |
| } |
| /** |
| * Shows the item. If the item is already showing in the receiver, |
| * this method simply returns. Otherwise, the items are scrolled until |
| * the item is visible. |
| * |
| * @param item the item to be shown |
| * |
| * @exception IllegalArgumentException <ul> |
| * <li>ERROR_NULL_ARGUMENT - if the item is null</li> |
| * <li>ERROR_INVALID_ARGUMENT - if the item has been disposed</li> |
| * </ul> |
| * @exception SWTException <ul> |
| * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> |
| * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> |
| * </ul> |
| * |
| * @see CTable#showSelection() |
| */ |
| public void showItem (CTableItem item) { |
| checkWidget (); |
| if (item == null) SWT.error (SWT.ERROR_NULL_ARGUMENT); |
| if (item.isDisposed ()) SWT.error (SWT.ERROR_INVALID_ARGUMENT); |
| if (item.parent != this) return; |
| |
| int index = item.index; |
| int visibleItemCount = (clientArea.height - getHeaderHeight ()) / itemHeight; |
| /* nothing to do if item is already in viewport */ |
| if (topIndex <= index && index < topIndex + visibleItemCount) return; |
| |
| if (index <= topIndex) { |
| /* item is above current viewport, so show on top */ |
| setTopIndex (item.index); |
| } else { |
| /* item is below current viewport, so show on bottom */ |
| visibleItemCount = Math.max (visibleItemCount, 1); /* item to show should be top item */ |
| setTopIndex (Math.min (index - visibleItemCount + 1, itemsCount - 1)); |
| } |
| } |
| /** |
| * Shows the selection. If the selection is already showing in the receiver, |
| * this method simply returns. Otherwise, the items are scrolled until |
| * the selection is visible. |
| * |
| * @exception SWTException <ul> |
| * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> |
| * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> |
| * </ul> |
| * |
| * @see CTable#showItem(CTableItem) |
| */ |
| public void showSelection () { |
| checkWidget (); |
| if (selectedItems.length == 0) return; |
| showItem (selectedItems [0]); |
| } |
| void sortDescent (int [] items) { |
| /* Shell Sort from K&R, pg 108 */ |
| int length = items.length; |
| for (int gap = length / 2; gap > 0; gap /= 2) { |
| for (int i = gap; i < length; i++) { |
| for (int j = i - gap; j >= 0; j -= gap) { |
| if (items [j] <= items [j + gap]) { |
| int swap = items [j]; |
| items [j] = items [j + gap]; |
| items [j + gap] = swap; |
| } |
| } |
| } |
| } |
| } |
| void sortAscent (int [] items) { |
| /* Shell Sort from K&R, pg 108 */ |
| int length = items.length; |
| for (int gap = length / 2; gap > 0; gap /= 2) { |
| for (int i = gap; i < length; i++) { |
| for (int j = i - gap; j >= 0; j -= gap) { |
| if (items [j] >= items [j + gap]) { |
| int swap = items [j]; |
| items [j] = items [j + gap]; |
| items [j + gap] = swap; |
| } |
| } |
| } |
| } |
| } |
| void sortAscent (CTableItem [] items) { |
| /* Shell Sort from K&R, pg 108 */ |
| int length = items.length; |
| for (int gap = length / 2; gap > 0; gap /= 2) { |
| for (int i = gap; i < length; i++) { |
| for (int j = i - gap; j >= 0; j -= gap) { |
| if (items [j].index >= items [j + gap].index) { |
| CTableItem swap = items [j]; |
| items [j] = items [j + gap]; |
| items [j + gap] = swap; |
| } |
| } |
| } |
| } |
| } |
| void updateColumnWidth (CTableColumn column, int width) { |
| headerHideToolTip (); |
| int oldWidth = column.width; |
| int columnX = column.getX (); |
| int x = columnX + oldWidth - 1; /* -1 ensures that grid line is included */ |
| |
| update (); |
| GC gc = new GC (this); |
| gc.copyArea (x, 0, clientArea.width - x, clientArea.height, columnX + width - 1, 0); /* dest x -1 offsets x's -1 above */ |
| if (width > oldWidth) { |
| /* column width grew */ |
| int change = width - oldWidth + 1; /* +1 offsets x's -1 above */ |
| /* -1/+1 below ensure that right bound of selection redraws correctly in column */ |
| redraw (x - 1, 0, change + 1, clientArea.height, false); |
| } else { |
| int change = oldWidth - width + 1; /* +1 offsets x's -1 above */ |
| redraw (clientArea.width - change, 0, change, clientArea.height, false); |
| } |
| /* the focus box must be repainted because its stipple may become shifted as a result of its new width */ |
| if (focusItem != null) redrawItem (focusItem.index, true); |
| |
| GC headerGC = new GC (header); |
| if (drawCount <= 0 && header.getVisible ()) { |
| Rectangle headerBounds = header.getClientArea (); |
| header.update (); |
| x -= 1; /* -1 ensures that full header column separator is included */ |
| headerGC.copyArea (x, 0, headerBounds.width - x, headerBounds.height, columnX + width - 2, 0); /* dest x -2 offsets x's -1s above */ |
| if (width > oldWidth) { |
| /* column width grew */ |
| int change = width - oldWidth + 2; /* +2 offsets x's -1s above */ |
| header.redraw (x, 0, change, headerBounds.height, false); |
| } else { |
| int change = oldWidth - width + 2; /* +2 offsets x's -1s above */ |
| header.redraw (headerBounds.width - change, 0, change, headerBounds.height, false); |
| } |
| } |
| |
| column.width = width; |
| |
| /* |
| * Notify column and all items of column width change so that display labels |
| * can be recomputed if needed. |
| */ |
| column.updateWidth (headerGC); |
| headerGC.dispose (); |
| for (int i = 0; i < itemsCount; i++) { |
| items [i].updateColumnWidth (column, gc); |
| } |
| gc.dispose (); |
| |
| int maximum = 0; |
| for (CTableColumn column2 : columns) { |
| maximum += column2.width; |
| } |
| ScrollBar hBar = getHorizontalBar (); |
| if (hBar != null) { |
| hBar.setMaximum (Math.max (1, maximum)); /* setting a value of 0 here is ignored */ |
| if (hBar.getThumb () != clientArea.width) { |
| hBar.setThumb (clientArea.width); |
| hBar.setPageIncrement (clientArea.width); |
| } |
| int oldHorizontalOffset = horizontalOffset; /* hBar.setVisible() can modify horizontalOffset */ |
| hBar.setVisible (clientArea.width < maximum); |
| int selection = hBar.getSelection (); |
| if (selection != oldHorizontalOffset) { |
| horizontalOffset = selection; |
| redraw (); |
| if (drawCount <= 0 && header.getVisible ()) header.redraw (); |
| } |
| } |
| |
| column.notifyListeners (SWT.Resize, new Event ()); |
| CTableColumn[] orderedColumns = getOrderedColumns (); |
| for (int i = column.getOrderIndex () + 1; i < orderedColumns.length; i++) { |
| if (!orderedColumns [i].isDisposed ()) { |
| orderedColumns [i].notifyListeners (SWT.Move, new Event ()); |
| } |
| } |
| |
| if (itemsCount == 0) redraw (); /* ensure that static focus rectangle updates properly */ |
| } |
| /* |
| * This is a naive implementation that computes the value from scratch. |
| */ |
| void updateHorizontalBar () { |
| if (drawCount > 0) return; |
| ScrollBar hBar = getHorizontalBar (); |
| if (hBar == null) return; |
| |
| int maxX = 0; |
| if (columns.length > 0) { |
| for (CTableColumn column : columns) { |
| maxX += column.width; |
| } |
| } else { |
| for (int i = 0; i < itemsCount; i++) { |
| Rectangle itemBounds = items [i].getCellBounds (0); |
| maxX = Math.max (maxX, itemBounds.x + itemBounds.width + horizontalOffset); |
| } |
| } |
| |
| int clientWidth = clientArea.width; |
| if (maxX != hBar.getMaximum ()) { |
| hBar.setMaximum (Math.max (1, maxX)); /* setting a value of 0 here is ignored */ |
| } |
| int thumb = Math.min (clientWidth, maxX); |
| if (thumb != hBar.getThumb ()) { |
| hBar.setThumb (thumb); |
| hBar.setPageIncrement (thumb); |
| } |
| hBar.setVisible (clientWidth < maxX); |
| |
| /* reclaim any space now left on the right */ |
| if (maxX < horizontalOffset + thumb) { |
| horizontalOffset = maxX - thumb; |
| hBar.setSelection (horizontalOffset); |
| redraw (); |
| } else { |
| int selection = hBar.getSelection (); |
| if (selection != horizontalOffset) { |
| horizontalOffset = selection; |
| redraw (); |
| } |
| } |
| } |
| /* |
| * Update the horizontal bar, if needed, in response to an item change (eg.- created, |
| * disposed, expanded, etc.). newRightX is the new rightmost X value of the item, |
| * and rightXchange is the change that led to the item's rightmost X value becoming |
| * newRightX (so oldRightX + rightXchange = newRightX) |
| */ |
| void updateHorizontalBar (int newRightX, int rightXchange) { |
| if (drawCount > 0) return; |
| ScrollBar hBar = getHorizontalBar (); |
| if (hBar == null) return; |
| |
| newRightX += horizontalOffset; |
| int barMaximum = hBar.getMaximum (); |
| if (newRightX > barMaximum) { /* item has extended beyond previous maximum */ |
| hBar.setMaximum (newRightX); |
| int clientAreaWidth = clientArea.width; |
| int thumb = Math.min (newRightX, clientAreaWidth); |
| hBar.setThumb (thumb); |
| hBar.setPageIncrement (thumb); |
| hBar.setVisible (clientAreaWidth <= newRightX); |
| return; |
| } |
| |
| int previousRightX = newRightX - rightXchange; |
| if (previousRightX != barMaximum) { |
| /* this was not the rightmost item, so just check for client width change */ |
| int clientAreaWidth = clientArea.width; |
| int thumb = Math.min (barMaximum, clientAreaWidth); |
| hBar.setThumb (thumb); |
| hBar.setPageIncrement (thumb); |
| hBar.setVisible (clientAreaWidth <= barMaximum); |
| return; |
| } |
| updateHorizontalBar (); /* must search for the new rightmost item */ |
| } |
| void updateVerticalBar () { |
| if (drawCount > 0) return; |
| ScrollBar vBar = getVerticalBar (); |
| if (vBar == null) return; |
| |
| int pageSize = (clientArea.height - getHeaderHeight ()) / itemHeight; |
| int maximum = Math.max (1, itemsCount); /* setting a value of 0 here is ignored */ |
| if (maximum != vBar.getMaximum ()) { |
| vBar.setMaximum (maximum); |
| } |
| int thumb = Math.min (pageSize, maximum); |
| if (thumb != vBar.getThumb ()) { |
| vBar.setThumb (thumb); |
| vBar.setPageIncrement (thumb); |
| } |
| vBar.setVisible (pageSize < maximum); |
| |
| /* reclaim any space now left on the bottom */ |
| if (maximum < topIndex + thumb) { |
| topIndex = maximum - thumb; |
| vBar.setSelection (topIndex); |
| redraw (); |
| } else { |
| int selection = vBar.getSelection (); |
| if (selection != topIndex) { |
| topIndex = selection; |
| redraw (); |
| } |
| } |
| } |
| } |