1 /*******************************************************************************
2 * Copyright (c) 2000, 2016 IBM Corporation and others.
3 *
4 * This program and the accompanying materials
5 * are made available under the terms of the Eclipse Public License 2.0
6 * which accompanies this distribution, and is available at
7 * https://www.eclipse.org/legal/epl-2.0/
8 *
9 * SPDX-License-Identifier: EPL-2.0
10 *
11 * Contributors:
12 * IBM Corporation - initial API and implementation
13 *******************************************************************************/
14 package org.eclipse.swt.examples.accessibility;
15
16 import org.eclipse.swt.SWT;
17 import org.eclipse.swt.SWTException;
18 import org.eclipse.swt.accessibility.ACC;
19 import org.eclipse.swt.accessibility.Accessible;
20 import org.eclipse.swt.accessibility.AccessibleAdapter;
21 import org.eclipse.swt.accessibility.AccessibleControlAdapter;
22 import org.eclipse.swt.accessibility.AccessibleControlEvent;
23 import org.eclipse.swt.accessibility.AccessibleEvent;
24 import org.eclipse.swt.accessibility.AccessibleTableAdapter;
25 import org.eclipse.swt.accessibility.AccessibleTableEvent;
26 import org.eclipse.swt.events.SelectionEvent;
27 import org.eclipse.swt.events.SelectionListener;
28 import org.eclipse.swt.graphics.Color;
29 import org.eclipse.swt.graphics.Font;
30 import org.eclipse.swt.graphics.GC;
31 import org.eclipse.swt.graphics.Image;
32 import org.eclipse.swt.graphics.ImageData;
33 import org.eclipse.swt.graphics.PaletteData;
34 import org.eclipse.swt.graphics.Point;
35 import org.eclipse.swt.graphics.RGB;
36 import org.eclipse.swt.graphics.Rectangle;
37 import org.eclipse.swt.widgets.Canvas;
38 import org.eclipse.swt.widgets.Composite;
39 import org.eclipse.swt.widgets.Control;
40 import org.eclipse.swt.widgets.Display;
41 import org.eclipse.swt.widgets.Event;
42 import org.eclipse.swt.widgets.Label;
43 import org.eclipse.swt.widgets.Listener;
44 import org.eclipse.swt.widgets.Menu;
45 import org.eclipse.swt.widgets.ScrollBar;
46 import org.eclipse.swt.widgets.Shell;
47 import org.eclipse.swt.widgets.Tracker;
48 import org.eclipse.swt.widgets.TypedListener;
49 import org.eclipse.swt.widgets.Widget;
50
51 /**
52 * Instances of this class implement a selectable user interface
53 * object that displays a list of images and strings and issues
54 * notification when selected.
55 * <p>
56 * The item children that may be added to instances of this class
57 * must be of type <code>TableItem</code>.
58 * </p><p>
59 * Style <code>VIRTUAL</code> is used to create a <code>Table</code> whose
60 * <code>TableItem</code>s are to be populated by the client on an on-demand basis
61 * instead of up-front. This can provide significant performance improvements for
62 * tables that are very large or for which <code>TableItem</code> population is
63 * expensive (for example, retrieving values from an external source).
64 * </p><p>
65 * Here is an example of using a <code>Table</code> with style <code>VIRTUAL</code>:
66 * <code><pre>
67 * final Table table = new Table (parent, SWT.VIRTUAL | SWT.BORDER);
68 * table.setItemCount (1000000);
69 * table.addListener (SWT.SetData, new Listener () {
70 * public void handleEvent (Event event) {
71 * TableItem item = (TableItem) event.item;
72 * int index = table.indexOf (item);
73 * item.setText ("Item " + index);
74 * System.out.println (item.getText ());
75 * }
76 * });
77 * </pre></code>
78 * </p><p>
79 * Note that although this class is a subclass of <code>Composite</code>,
80 * it does not normally make sense to add <code>Control</code> children to
81 * it, or set a layout on it, unless implementing something like a cell
82 * editor.
83 * </p><p>
84 * <dl>
85 * <dt><b>Styles:</b></dt>
86 * <dd>SINGLE, MULTI, CHECK, FULL_SELECTION, HIDE_SELECTION, VIRTUAL, NO_SCROLL</dd>
87 * <dt><b>Events:</b></dt>
88 * <dd>Selection, DefaultSelection, SetData, MeasureItem, EraseItem, PaintItem</dd>
89 * </dl>
90 * </p><p>
91 * Note: Only one of the styles SINGLE, and MULTI may be specified.
92 * </p><p>
93 * IMPORTANT: This class is <em>not</em> intended to be subclassed.
94 * </p>
95 *
96 * @see <a href="http://www.eclipse.org/swt/snippets/#table">Table, TableItem, TableColumn snippets</a>
97 * @see <a href="http://www.eclipse.org/swt/examples.php">SWT Example: ControlExample</a>
98 * @see <a href="http://www.eclipse.org/swt/">Sample code and further information</a>
99 * @noextend This class is not intended to be subclassed by clients.
100 */
101 public class CTable extends Composite {
102 Canvas header;
103 CTableColumn[] columns = new CTableColumn [0];
104 CTableColumn[] orderedColumns;
105 CTableItem[] items = new CTableItem [0];
106 CTableItem[] selectedItems = new CTableItem [0];
107 CTableItem focusItem, anchorItem, lastClickedItem;
108 Event lastSelectionEvent;
109 boolean linesVisible, ignoreKey, ignoreDispose, customHeightSet;
110 int itemsCount = 0;
111 int topIndex = 0, horizontalOffset = 0;
112 int fontHeight = 0, imageHeight = 0, itemHeight = 0;
113 int col0ImageWidth = 0;
114 int headerImageHeight = 0;
115 CTableColumn resizeColumn;
116 int resizeColumnX = -1;
117 int drawCount = 0;
118 CTableColumn sortColumn;
119 int sortDirection = SWT.NONE;
120
121 /* column header tooltip */
122 Listener toolTipListener;
123 Shell toolTipShell;
124 Label toolTipLabel;
125
126 Rectangle arrowBounds, checkboxBounds, clientArea;
127
128 static final int MARGIN_IMAGE = 3;
129 static final int MARGIN_CELL = 1;
130 static final int SIZE_HORIZONTALSCROLL = 5;
131 static final int TOLLERANCE_COLUMNRESIZE = 2;
132 static final int WIDTH_HEADER_SHADOW = 2;
133 static final int WIDTH_CELL_HIGHLIGHT = 1;
134 static final int [] toolTipEvents = new int[] {SWT.MouseExit, SWT.MouseHover, SWT.MouseMove, SWT.MouseDown};
135 static final String ELLIPSIS = "..."; //$NON-NLS-1$
136 static final String ID_UNCHECKED = "UNCHECKED"; //$NON-NLS-1$
137 static final String ID_GRAYUNCHECKED = "GRAYUNCHECKED"; //$NON-NLS-1$
138 static final String ID_CHECKMARK = "CHECKMARK"; //$NON-NLS-1$
139 static final String ID_ARROWUP = "ARROWUP"; //$NON-NLS-1$
140 static final String ID_ARROWDOWN = "ARROWDOWN"; //$NON-NLS-1$
141
142 Display display;
143
144 //TEMPORARY CODE
145 boolean hasFocus;
146 @Override
isFocusControl()147 public boolean isFocusControl() {
148 return hasFocus;
149 }
150
151 /**
152 * Constructs a new instance of this class given its parent
153 * and a style value describing its behavior and appearance.
154 * <p>
155 * The style value is either one of the style constants defined in
156 * class <code>SWT</code> which is applicable to instances of this
157 * class, or must be built by <em>bitwise OR</em>'ing together
158 * (that is, using the <code>int</code> "|" operator) two or more
159 * of those <code>SWT</code> style constants. The class description
160 * lists the style constants that are applicable to the class.
161 * Style bits are also inherited from superclasses.
162 * </p>
163 *
164 * @param parent a composite control which will be the parent of the new instance (cannot be null)
165 * @param style the style of control to construct
166 *
167 * @exception IllegalArgumentException <ul>
168 * <li>ERROR_NULL_ARGUMENT - if the parent is null</li>
169 * </ul>
170 * @exception SWTException <ul>
171 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the parent</li>
172 * <li>ERROR_INVALID_SUBCLASS - if this class is not an allowed subclass</li>
173 * </ul>
174 *
175 * @see SWT#SINGLE
176 * @see SWT#MULTI
177 * @see SWT#CHECK
178 * @see SWT#FULL_SELECTION
179 * @see SWT#HIDE_SELECTION
180 * @see SWT#VIRTUAL
181 * @see SWT#NO_SCROLL
182 * @see Widget#checkSubclass
183 * @see Widget#getStyle
184 */
CTable(Composite parent, int style)185 public CTable (Composite parent, int style) {
186 super (parent, checkStyle (style));
187 this.display = parent.getDisplay ();
188 setForeground (null); /* set foreground and background to chosen default colors */
189 setBackground (null);
190 GC gc = new GC (this);
191 fontHeight = gc.getFontMetrics ().getHeight ();
192 gc.dispose ();
193 itemHeight = fontHeight + (2 * getCellPadding ());
194 initImages (display);
195 checkboxBounds = getUncheckedImage ().getBounds ();
196 arrowBounds = getArrowDownImage ().getBounds ();
197 clientArea = getClientArea ();
198
199 Listener listener = event -> handleEvents (event);
200 addListener (SWT.Paint, listener);
201 addListener (SWT.MouseDown, listener);
202 addListener (SWT.MouseUp, listener);
203 addListener (SWT.MouseDoubleClick, listener);
204 addListener (SWT.Dispose, listener);
205 addListener (SWT.Resize, listener);
206 addListener (SWT.KeyDown, listener);
207 addListener (SWT.FocusOut, listener);
208 addListener (SWT.FocusIn, listener);
209 addListener (SWT.Traverse, listener);
210
211 initAccessibility ();
212
213 header = new Canvas (this, SWT.NO_REDRAW_RESIZE | SWT.NO_FOCUS);
214 header.setVisible (false);
215 header.setBounds (0, 0, 0, fontHeight + 2 * getHeaderPadding ());
216 header.addListener (SWT.Paint, listener);
217 header.addListener (SWT.MouseDown, listener);
218 header.addListener (SWT.MouseUp, listener);
219 header.addListener (SWT.MouseHover, listener);
220 header.addListener (SWT.MouseDoubleClick, listener);
221 header.addListener (SWT.MouseMove, listener);
222 header.addListener (SWT.MouseExit, listener);
223 header.addListener (SWT.MenuDetect, listener);
224
225 toolTipListener = event -> {
226 switch (event.type) {
227 case SWT.MouseHover:
228 case SWT.MouseMove:
229 if (headerUpdateToolTip (event.x)) break;
230 // FALL THROUGH
231 case SWT.MouseExit:
232 case SWT.MouseDown:
233 headerHideToolTip ();
234 break;
235 }
236 };
237
238 ScrollBar hBar = getHorizontalBar ();
239 if (hBar != null) {
240 hBar.setValues (0, 0, 1, 1, 1, 1);
241 hBar.setVisible (false);
242 hBar.addListener (SWT.Selection, listener);
243 }
244 ScrollBar vBar = getVerticalBar ();
245 if (vBar != null) {
246 vBar.setValues (0, 0, 1, 1, 1, 1);
247 vBar.setVisible (false);
248 vBar.addListener (SWT.Selection, listener);
249 }
250 }
251 /**
252 * Adds the listener to the collection of listeners who will
253 * be notified when the user changes the receiver's selection, by sending
254 * it one of the messages defined in the <code>SelectionListener</code>
255 * interface.
256 * <p>
257 * When <code>widgetSelected</code> is called, the item field of the event object is valid.
258 * If the receiver has the <code>SWT.CHECK</code> style and the check selection changes,
259 * the event object detail field contains the value <code>SWT.CHECK</code>.
260 * <code>widgetDefaultSelected</code> is typically called when an item is double-clicked.
261 * The item field of the event object is valid for default selection, but the detail field is not used.
262 * </p>
263 *
264 * @param listener the listener which should be notified when the user changes the receiver's selection
265 *
266 * @exception IllegalArgumentException <ul>
267 * <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
268 * </ul>
269 * @exception SWTException <ul>
270 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
271 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
272 * </ul>
273 *
274 * @see SelectionListener
275 * @see #removeSelectionListener
276 * @see SelectionEvent
277 */
addSelectionListener(SelectionListener listener)278 public void addSelectionListener (SelectionListener listener) {
279 checkWidget ();
280 if (listener == null) SWT.error (SWT.ERROR_NULL_ARGUMENT);
281 TypedListener typedListener = new TypedListener (listener);
282 addListener (SWT.Selection, typedListener);
283 addListener (SWT.DefaultSelection, typedListener);
284 }
checkData(CTableItem item, boolean redraw)285 boolean checkData (CTableItem item, boolean redraw) {
286 if (item.cached) return true;
287 if ((getStyle () & SWT.VIRTUAL) != 0) {
288 item.cached = true;
289 Event event = new Event ();
290 event.item = item;
291 event.index = indexOf (item);
292 notifyListeners (SWT.SetData, event);
293 if (isDisposed () || item.isDisposed ()) return false;
294 if (redraw) redrawItem (item.index, false);
295 }
296 return true;
297 }
checkStyle(int style)298 static int checkStyle (int style) {
299 /*
300 * Feature in Windows. Even when WS_HSCROLL or
301 * WS_VSCROLL is not specified, Windows creates
302 * trees and tables with scroll bars. The fix
303 * is to set H_SCROLL and V_SCROLL.
304 *
305 * NOTE: This code appears on all platforms so that
306 * applications have consistent scroll bar behavior.
307 */
308 if ((style & SWT.NO_SCROLL) == 0) {
309 style |= SWT.H_SCROLL | SWT.V_SCROLL;
310 }
311 style |= SWT.NO_REDRAW_RESIZE | SWT.NO_BACKGROUND | SWT.DOUBLE_BUFFERED;
312 //TEMPORARY CODE
313 style |= SWT.FULL_SELECTION;
314 return checkBits (style, SWT.SINGLE, SWT.MULTI, 0, 0, 0, 0);
315 }
checkBits(int style, int int0, int int1, int int2, int int3, int int4, int int5)316 static int checkBits (int style, int int0, int int1, int int2, int int3, int int4, int int5) {
317 int mask = int0 | int1 | int2 | int3 | int4 | int5;
318 if ((style & mask) == 0) style |= int0;
319 if ((style & int0) != 0) style = (style & ~mask) | int0;
320 if ((style & int1) != 0) style = (style & ~mask) | int1;
321 if ((style & int2) != 0) style = (style & ~mask) | int2;
322 if ((style & int3) != 0) style = (style & ~mask) | int3;
323 if ((style & int4) != 0) style = (style & ~mask) | int4;
324 if ((style & int5) != 0) style = (style & ~mask) | int5;
325 return style;
326 }
327 /**
328 * Clears the item at the given zero-relative index in the receiver.
329 * The text, icon and other attributes of the item are set to the default
330 * value. If the table was created with the <code>SWT.VIRTUAL</code> style,
331 * these attributes are requested again as needed.
332 *
333 * @param index the index of the item to clear
334 *
335 * @exception IllegalArgumentException <ul>
336 * <li>ERROR_INVALID_RANGE - if the index is not between 0 and the number of elements in the list minus 1 (inclusive)</li>
337 * </ul>
338 * @exception SWTException <ul>
339 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
340 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
341 * </ul>
342 *
343 * @see SWT#VIRTUAL
344 * @see SWT#SetData
345 *
346 * @since 3.0
347 */
clear(int index)348 public void clear (int index) {
349 checkWidget ();
350 if (!(0 <= index && index < itemsCount)) SWT.error (SWT.ERROR_INVALID_RANGE);
351 Rectangle bounds = items [index].getBounds (false);
352 int oldRightX = bounds.x + bounds.width;
353 items [index].clear ();
354 if (columns.length == 0) updateHorizontalBar (0, -oldRightX);
355 redrawItem (index, false);
356 }
357 /**
358 * Removes the items from the receiver which are between the given
359 * zero-relative start and end indices (inclusive). The text, icon
360 * and other attributes of the items are set to their default values.
361 * If the table was created with the <code>SWT.VIRTUAL</code> style,
362 * these attributes are requested again as needed.
363 *
364 * @param start the start index of the item to clear
365 * @param end the end index of the item to clear
366 *
367 * @exception IllegalArgumentException <ul>
368 * <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>
369 * </ul>
370 * @exception SWTException <ul>
371 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
372 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
373 * </ul>
374 *
375 * @see SWT#VIRTUAL
376 * @see SWT#SetData
377 *
378 * @since 3.0
379 */
clear(int start, int end)380 public void clear (int start, int end) {
381 checkWidget ();
382 if (start > end) return;
383 if (!(0 <= start && start <= end && end < itemsCount)) {
384 SWT.error (SWT.ERROR_INVALID_RANGE);
385 }
386 for (int i = start; i <= end; i++) {
387 items [i].clear ();
388 }
389 updateHorizontalBar ();
390 redrawItems (start, end, false);
391 }
392 /**
393 * Clears the items at the given zero-relative indices in the receiver.
394 * The text, icon and other attributes of the items are set to their default
395 * values. If the table was created with the <code>SWT.VIRTUAL</code> style,
396 * these attributes are requested again as needed.
397 *
398 * @param indices the array of indices of the items
399 *
400 * @exception IllegalArgumentException <ul>
401 * <li>ERROR_INVALID_RANGE - if the index is not between 0 and the number of elements in the list minus 1 (inclusive)</li>
402 * <li>ERROR_NULL_ARGUMENT - if the indices array is null</li>
403 * </ul>
404 * @exception SWTException <ul>
405 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
406 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
407 * </ul>
408 *
409 * @see SWT#VIRTUAL
410 * @see SWT#SetData
411 *
412 * @since 3.0
413 */
clear(int [] indices)414 public void clear (int [] indices) {
415 checkWidget ();
416 if (indices == null) SWT.error (SWT.ERROR_NULL_ARGUMENT);
417 if (indices.length == 0) return;
418 for (int i = 0; i < indices.length; i++) {
419 if (!(0 <= indices [i] && indices [i] < itemsCount)) {
420 SWT.error (SWT.ERROR_INVALID_RANGE);
421 }
422 }
423
424 for (int index : indices) {
425 items [index].clear ();
426 }
427 updateHorizontalBar ();
428 for (int index : indices) {
429 redrawItem (index, false);
430 }
431 }
432 /**
433 * Clears all the items in the receiver. The text, icon and other
434 * attributes of the items are set to their default values. If the
435 * table was created with the <code>SWT.VIRTUAL</code> style, these
436 * attributes are requested again as needed.
437 *
438 * @exception SWTException <ul>
439 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
440 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
441 * </ul>
442 *
443 * @see SWT#VIRTUAL
444 * @see SWT#SetData
445 *
446 * @since 3.0
447 */
clearAll()448 public void clearAll () {
449 checkWidget ();
450 clear (0, itemsCount - 1);
451 }
452 /*
453 * Returns the ORDERED index of the column that the specified x falls within,
454 * or -1 if the x lies to the right of the last column.
455 */
computeColumnIntersect(int x, int startColumn)456 int computeColumnIntersect (int x, int startColumn) {
457 CTableColumn[] orderedColumns = getOrderedColumns ();
458 if (orderedColumns.length - 1 < startColumn) return -1;
459 int rightX = orderedColumns [startColumn].getX ();
460 for (int i = startColumn; i < orderedColumns.length; i++) {
461 rightX += orderedColumns [i].width;
462 if (x < rightX) return i;
463 }
464 return -1;
465 }
466 @Override
computeSize(int wHint, int hHint, boolean changed)467 public Point computeSize (int wHint, int hHint, boolean changed) {
468 checkWidget ();
469 int width = 0, height = 0;
470 if (wHint != SWT.DEFAULT) {
471 width = wHint;
472 } else {
473 if (columns.length == 0) {
474 for (int i = 0; i < itemsCount; i++) {
475 Rectangle itemBounds = items [i].getBounds (false);
476 width = Math.max (width, itemBounds.x + itemBounds.width);
477 }
478 } else {
479 CTableColumn[] orderedColumns = getOrderedColumns ();
480 CTableColumn lastColumn = orderedColumns [orderedColumns.length - 1];
481 width = lastColumn.getX () + lastColumn.width;
482 }
483 }
484 if (hHint != SWT.DEFAULT) {
485 height = hHint;
486 } else {
487 height = getHeaderHeight () + itemsCount * itemHeight;
488 }
489 Rectangle result = computeTrim (0, 0, width, height);
490 return new Point (result.width, result.height);
491 }
createItem(CTableColumn column, int index)492 void createItem (CTableColumn column, int index) {
493 CTableColumn[] newColumns = new CTableColumn [columns.length + 1];
494 System.arraycopy (columns, 0, newColumns, 0, index);
495 newColumns [index] = column;
496 System.arraycopy (columns, index, newColumns, index + 1, columns.length - index);
497 columns = newColumns;
498
499 if (orderedColumns != null) {
500 int insertIndex = 0;
501 if (index > 0) {
502 insertIndex = columns [index - 1].getOrderIndex () + 1;
503 }
504 CTableColumn[] newOrderedColumns = new CTableColumn [orderedColumns.length + 1];
505 System.arraycopy (orderedColumns, 0, newOrderedColumns, 0, insertIndex);
506 newOrderedColumns [insertIndex] = column;
507 System.arraycopy (
508 orderedColumns,
509 insertIndex,
510 newOrderedColumns,
511 insertIndex + 1,
512 orderedColumns.length - insertIndex);
513 orderedColumns = newOrderedColumns;
514 }
515
516 /* allow all items to update their internal structures accordingly */
517 for (int i = 0; i < itemsCount; i++) {
518 items [i].addColumn (column);
519 }
520
521 /* existing items become hidden when going from 0 to 1 column (0 width) */
522 if (columns.length == 1 && itemsCount > 0) {
523 redrawFromItemDownwards (topIndex);
524 } else {
525 /* checkboxes become hidden when creating a column with index == orderedIndex == 0 (0 width) */
526 if (itemsCount > 0 && (getStyle () & SWT.CHECK) != 0 && index == 0 && column.getOrderIndex () == 0) {
527 redrawFromItemDownwards (topIndex);
528 }
529 }
530
531 /* Columns were added, so notify the accessible. */
532 int[] eventData = new int[5];
533 eventData[0] = ACC.INSERT;
534 eventData[1] = 0;
535 eventData[2] = 0;
536 eventData[3] = index;
537 eventData[4] = 1;
538 getAccessible().sendEvent(ACC.EVENT_TABLE_CHANGED, eventData);
539 }
createItem(CTableItem item)540 void createItem (CTableItem item) {
541 int index = item.index;
542 if (itemsCount == items.length) {
543 int grow = drawCount <= 0 ? 4 : Math.max (4, items.length * 3 / 2);
544 CTableItem[] newItems = new CTableItem [items.length + grow];
545 System.arraycopy (items, 0, newItems, 0, items.length);
546 items = newItems;
547 }
548 if (index != itemsCount) {
549 /* new item is not at end of list, so shift other items right to create space for it */
550 System.arraycopy (items, index, items, index + 1, itemsCount - index);
551 }
552 items [index] = item;
553 itemsCount++;
554
555 /* update the index for items bumped down by this new item */
556 for (int i = index + 1; i < itemsCount; i++) {
557 items [i].index = i;
558 }
559
560 /* Rows were added, so notify the accessible. */
561 int[] eventData = new int[5];
562 eventData[0] = ACC.INSERT;
563 eventData[1] = index;
564 eventData[2] = 1;
565 eventData[3] = 0;
566 eventData[4] = 0;
567 getAccessible().sendEvent(ACC.EVENT_TABLE_CHANGED, eventData);
568
569 /* update scrollbars */
570 updateVerticalBar ();
571 Rectangle bounds = item.getBounds (false);
572 int rightX = bounds.x + bounds.width;
573 updateHorizontalBar (rightX, rightX);
574 /*
575 * If new item is above viewport then adjust topIndex and the vertical
576 * scrollbar so that the current viewport items will not change.
577 */
578 if (item.index < topIndex) {
579 topIndex++;
580 ScrollBar vBar = getVerticalBar ();
581 if (vBar != null) vBar.setSelection (topIndex);
582 return;
583 }
584 /*
585 * If this is the first item and the receiver has focus then its boundary
586 * focus ring must be removed.
587 */
588 if (itemsCount == 1 && isFocusControl ()) {
589 focusItem = item;
590 redraw ();
591 return;
592 }
593 if (item.isInViewport ()) {
594 redrawFromItemDownwards (index);
595 }
596 }
597 /**
598 * Deselects the item at the given zero-relative index in the receiver.
599 * If the item at the index was already deselected, it remains
600 * deselected. Indices that are out of range are ignored.
601 *
602 * @param index the index of the item to deselect
603 *
604 * @exception SWTException <ul>
605 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
606 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
607 * </ul>
608 */
deselect(int index)609 public void deselect (int index) {
610 checkWidget ();
611 if (!(0 <= index && index < itemsCount)) return;
612 CTableItem item = items [index];
613 int selectIndex = getSelectionIndex (item);
614 if (selectIndex == -1) return;
615
616 CTableItem[] newSelectedItems = new CTableItem [selectedItems.length - 1];
617 System.arraycopy (selectedItems, 0, newSelectedItems, 0, selectIndex);
618 System.arraycopy (selectedItems, selectIndex + 1, newSelectedItems, selectIndex, newSelectedItems.length - selectIndex);
619 selectedItems = newSelectedItems;
620
621 if (isFocusControl () || (getStyle () & SWT.HIDE_SELECTION) == 0) {
622 redrawItem (item.index, false);
623 }
624 getAccessible().selectionChanged();
625 }
626 /**
627 * Deselects the items at the given zero-relative indices in the receiver.
628 * If the item at the given zero-relative index in the receiver
629 * is selected, it is deselected. If the item at the index
630 * was not selected, it remains deselected. The range of the
631 * indices is inclusive. Indices that are out of range are ignored.
632 *
633 * @param start the start index of the items to deselect
634 * @param end the end index of the items to deselect
635 *
636 * @exception SWTException <ul>
637 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
638 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
639 * </ul>
640 */
deselect(int start, int end)641 public void deselect (int start, int end) {
642 checkWidget ();
643 if (start == 0 && end == itemsCount - 1) {
644 deselectAll ();
645 } else {
646 start = Math.max (start, 0);
647 end = Math.min (end, itemsCount - 1);
648 for (int i = start; i <= end; i++) {
649 deselect (i);
650 }
651 }
652 }
653 /**
654 * Deselects the items at the given zero-relative indices in the receiver.
655 * If the item at the given zero-relative index in the receiver
656 * is selected, it is deselected. If the item at the index
657 * was not selected, it remains deselected. Indices that are out
658 * of range and duplicate indices are ignored.
659 *
660 * @param indices the array of indices for the items to deselect
661 *
662 * @exception IllegalArgumentException <ul>
663 * <li>ERROR_NULL_ARGUMENT - if the set of indices is null</li>
664 * </ul>
665 * @exception SWTException <ul>
666 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
667 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
668 * </ul>
669 */
deselect(int [] indices)670 public void deselect (int [] indices) {
671 checkWidget ();
672 if (indices == null) SWT.error (SWT.ERROR_NULL_ARGUMENT);
673 if (indices.length == 0) return;
674 for (int index : indices) {
675 deselect (index);
676 }
677 }
678 /**
679 * Deselects all selected items in the receiver.
680 *
681 * @exception SWTException <ul>
682 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
683 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
684 * </ul>
685 */
deselectAll()686 public void deselectAll () {
687 checkWidget ();
688 CTableItem[] oldSelection = selectedItems;
689 selectedItems = new CTableItem [0];
690 if (isFocusControl () || (getStyle () & SWT.HIDE_SELECTION) == 0) {
691 for (CTableItem element : oldSelection) {
692 redrawItem (element.index, true);
693 }
694 }
695 for (CTableItem element : oldSelection) {
696 element.getAccessible(getAccessible(), 0).selectionChanged();
697 }
698 if (oldSelection.length > 0) getAccessible().selectionChanged();
699 }
deselectItem(CTableItem item)700 void deselectItem (CTableItem item) {
701 int index = getSelectionIndex (item);
702 if (index == -1) return;
703 CTableItem[] newSelectedItems = new CTableItem [selectedItems.length - 1];
704 System.arraycopy (selectedItems, 0, newSelectedItems, 0, index);
705 System.arraycopy (
706 selectedItems,
707 index + 1,
708 newSelectedItems,
709 index,
710 newSelectedItems.length - index);
711 selectedItems = newSelectedItems;
712 item.getAccessible(getAccessible(), 0).selectionChanged();
713 }
destroyItem(CTableColumn column)714 void destroyItem (CTableColumn column) {
715 headerHideToolTip ();
716 int index = column.getIndex ();
717 int orderedIndex = column.getOrderIndex ();
718
719 CTableColumn[] newColumns = new CTableColumn [columns.length - 1];
720 System.arraycopy (columns, 0, newColumns, 0, index);
721 System.arraycopy (columns, index + 1, newColumns, index, newColumns.length - index);
722 columns = newColumns;
723
724 if (orderedColumns != null) {
725 if (columns.length < 2) {
726 orderedColumns = null;
727 } else {
728 int removeIndex = column.getOrderIndex ();
729 CTableColumn[] newOrderedColumns = new CTableColumn [orderedColumns.length - 1];
730 System.arraycopy (orderedColumns, 0, newOrderedColumns, 0, removeIndex);
731 System.arraycopy (
732 orderedColumns,
733 removeIndex + 1,
734 newOrderedColumns,
735 removeIndex,
736 newOrderedColumns.length - removeIndex);
737 orderedColumns = newOrderedColumns;
738 }
739 }
740
741 /* ensure that column 0 always has left-alignment */
742 if (index == 0 && columns.length > 0) {
743 int style = columns [0].getStyle ();
744 style |= SWT.LEFT;
745 style &= ~(SWT.CENTER | SWT.RIGHT);
746 columns [0].setStyle (style);
747 }
748
749 /* allow all items to update their internal structures accordingly */
750 for (int i = 0; i < itemsCount; i++) {
751 items [i].removeColumn (column, index);
752 }
753
754 /* update horizontal scrollbar */
755 int lastColumnIndex = columns.length - 1;
756 if (lastColumnIndex < 0) { /* no more columns */
757 updateHorizontalBar ();
758 } else {
759 int newWidth = 0;
760 for (CTableColumn column2 : columns) {
761 newWidth += column2.width;
762 }
763 ScrollBar hBar = getHorizontalBar ();
764 if (hBar != null) {
765 hBar.setMaximum (newWidth);
766 hBar.setVisible (clientArea.width < newWidth);
767 }
768 int selection = hBar.getSelection ();
769 if (selection != horizontalOffset) {
770 horizontalOffset = selection;
771 redraw ();
772 if (header.isVisible () && drawCount <= 0) header.redraw ();
773 }
774 }
775 CTableColumn[] orderedColumns = getOrderedColumns ();
776 for (int i = orderedIndex; i < orderedColumns.length; i++) {
777 if (!orderedColumns [i].isDisposed ()) {
778 orderedColumns [i].notifyListeners (SWT.Move, new Event ());
779 }
780 }
781
782 int[] eventData = new int[5];
783 eventData[0] = ACC.DELETE;
784 eventData[1] = 0;
785 eventData[2] = 0;
786 eventData[3] = index;
787 eventData[4] = 1;
788 getAccessible().sendEvent(ACC.EVENT_TABLE_CHANGED, eventData);
789
790 if (sortColumn == column) {
791 sortColumn = null;
792 }
793 }
794 /*
795 * Allows the Table to update internal structures it has that may contain the
796 * item being destroyed.
797 */
798 void destroyItem (CTableItem item) {
799 if (item == focusItem) reassignFocus ();
800
801 int index = item.index;
802 Rectangle bounds = item.getBounds (false);
803 int rightX = bounds.x + bounds.width;
804
805 if (index != itemsCount - 1) {
806 /* item is not at end of items list, so must shift items left to reclaim its slot */
807 System.arraycopy (items, index + 1, items, index, itemsCount - index - 1);
808 items [itemsCount - 1] = null;
809 } else {
810 items [index] = null; /* last item, so no array copy needed */
811 }
812 itemsCount--;
813
814 if (drawCount <= 0 && items.length - itemsCount == 4) {
815 /* shrink the items array */
816 CTableItem[] newItems = new CTableItem [itemsCount];
817 System.arraycopy (items, 0, newItems, 0, newItems.length);
818 items = newItems;
819 }
820
821 /* update the index on affected items */
822 for (int i = index; i < itemsCount; i++) {
823 items [i].index = i;
824 }
825 item.index = -1;
826
827 int oldTopIndex = topIndex;
828 updateVerticalBar ();
829 updateHorizontalBar (0, -rightX);
830 /*
831 * If destroyed item is above viewport then adjust topIndex and the vertical
832 * scrollbar so that the current viewport items will not change.
833 */
834 if (index < topIndex) {
835 topIndex = oldTopIndex - 1;
836 ScrollBar vBar = getVerticalBar ();
837 if (vBar != null) vBar.setSelection (topIndex);
838 }
839
840 /* selectedItems array */
841 if (item.isSelected ()) {
842 int selectionIndex = getSelectionIndex (item);
843 CTableItem[] newSelectedItems = new CTableItem [selectedItems.length - 1];
844 System.arraycopy (selectedItems, 0, newSelectedItems, 0, selectionIndex);
845 System.arraycopy (
846 selectedItems,
847 selectionIndex + 1,
848 newSelectedItems,
849 selectionIndex,
850 newSelectedItems.length - selectionIndex);
851 selectedItems = newSelectedItems;
852 }
853 if (item == anchorItem) anchorItem = null;
854 if (item == lastClickedItem) lastClickedItem = null;
855 /*
856 * If this was the last item and the receiver has focus then its boundary
857 * focus ring must be redrawn.
858 */
859 if (itemsCount == 0 && isFocusControl ()) {
860 redraw ();
861 }
862
863 int[] eventData = new int[5];
864 eventData[0] = ACC.DELETE;
865 eventData[1] = index;
866 eventData[2] = 1;
867 eventData[3] = 0;
868 eventData[4] = 0;
869 getAccessible().sendEvent(ACC.EVENT_TABLE_CHANGED, eventData);
870 }
871 Image getArrowDownImage () {
872 return (Image) display.getData (ID_ARROWDOWN);
873 }
874 Image getArrowUpImage () {
875 return (Image) display.getData (ID_ARROWUP);
876 }
877 int getCellPadding () {
878 return MARGIN_CELL + WIDTH_CELL_HIGHLIGHT;
879 }
880 Image getCheckmarkImage () {
881 return (Image) display.getData (ID_CHECKMARK);
882 }
883 @Override
884 public Control[] getChildren () {
885 checkWidget ();
886 Control[] controls = super.getChildren ();
887 if (header == null) return controls;
888 Control[] result = new Control [controls.length - 1];
889 /* remove the Header from the returned set of children */
890 int index = 0;
891 for (Control control : controls) {
892 if (control != header) {
893 result [index++] = control;
894 }
895 }
896 return result;
897 }
898 /**
899 * Returns the column at the given, zero-relative index in the
900 * receiver. Throws an exception if the index is out of range.
901 * Columns are returned in the order that they were created.
902 * If no <code>TableColumn</code>s were created by the programmer,
903 * this method will throw <code>ERROR_INVALID_RANGE</code> despite
904 * the fact that a single column of data may be visible in the table.
905 * This occurs when the programmer uses the table like a list, adding
906 * items but never creating a column.
907 *
908 * @param index the index of the column to return
909 * @return the column at the given index
910 *
911 * @exception IllegalArgumentException <ul>
912 * <li>ERROR_INVALID_RANGE - if the index is not between 0 and the number of elements in the list minus 1 (inclusive)</li>
913 * </ul>
914 * @exception SWTException <ul>
915 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
916 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
917 * </ul>
918 *
919 * @see CTable#getColumnOrder()
920 * @see CTable#setColumnOrder(int[])
921 * @see CTableColumn#getMoveable()
922 * @see CTableColumn#setMoveable(boolean)
923 * @see SWT#Move
924 */
925 public CTableColumn getColumn (int index) {
926 checkWidget ();
927 if (!(0 <= index && index < columns.length)) SWT.error (SWT.ERROR_INVALID_RANGE);
928 return columns [index];
929 }
930 /**
931 * Returns the number of columns contained in the receiver.
932 * If no <code>TableColumn</code>s were created by the programmer,
933 * this value is zero, despite the fact that visually, one column
934 * of items may be visible. This occurs when the programmer uses
935 * the table like a list, adding items but never creating a column.
936 *
937 * @return the number of columns
938 *
939 * @exception SWTException <ul>
940 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
941 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
942 * </ul>
943 */
944 public int getColumnCount () {
945 checkWidget ();
946 return columns.length;
947 }
948 /**
949 * Returns an array of zero-relative integers that map
950 * the creation order of the receiver's items to the
951 * order in which they are currently being displayed.
952 * <p>
953 * Specifically, the indices of the returned array represent
954 * the current visual order of the items, and the contents
955 * of the array represent the creation order of the items.
956 * </p><p>
957 * Note: This is not the actual structure used by the receiver
958 * to maintain its list of items, so modifying the array will
959 * not affect the receiver.
960 * </p>
961 *
962 * @return the current visual order of the receiver's items
963 *
964 * @exception SWTException <ul>
965 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
966 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
967 * </ul>
968 *
969 * @see CTable#setColumnOrder(int[])
970 * @see CTableColumn#getMoveable()
971 * @see CTableColumn#setMoveable(boolean)
972 * @see SWT#Move
973 *
974 * @since 3.1
975 */
976 public int[] getColumnOrder () {
977 checkWidget ();
978 int[] result = new int [columns.length];
979 if (orderedColumns != null) {
980 for (int i = 0; i < result.length; i++) {
981 result [i] = orderedColumns [i].getIndex ();
982 }
983 } else {
984 for (int i = 0; i < columns.length; i++) {
985 result [i] = i;
986 }
987 }
988 return result;
989 }
990 /**
991 * Returns an array of <code>TableColumn</code>s which are the
992 * columns in the receiver. Columns are returned in the order
993 * that they were created. If no <code>TableColumn</code>s were
994 * created by the programmer, the array is empty, despite the fact
995 * that visually, one column of items may be visible. This occurs
996 * when the programmer uses the table like a list, adding items but
997 * never creating a column.
998 * <p>
999 * Note: This is not the actual structure used by the receiver
1000 * to maintain its list of items, so modifying the array will
1001 * not affect the receiver.
1002 * </p>
1003 *
1004 * @return the items in the receiver
1005 *
1006 * @exception SWTException <ul>
1007 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
1008 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
1009 * </ul>
1010 *
1011 * @see CTable#getColumnOrder()
1012 * @see CTable#setColumnOrder(int[])
1013 * @see CTableColumn#getMoveable()
1014 * @see CTableColumn#setMoveable(boolean)
1015 * @see SWT#Move
1016 */
1017 public CTableColumn[] getColumns () {
1018 checkWidget ();
1019 CTableColumn[] result = new CTableColumn [columns.length];
1020 System.arraycopy (columns, 0, result, 0, columns.length);
1021 return result;
1022 }
1023 Image getGrayUncheckedImage () {
1024 return (Image) display.getData (ID_GRAYUNCHECKED);
1025 }
1026 /**
1027 * Returns the width in pixels of a grid line.
1028 *
1029 * @return the width of a grid line in pixels
1030 *
1031 * @exception SWTException <ul>
1032 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
1033 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
1034 * </ul>
1035 */
1036 public int getGridLineWidth () {
1037 checkWidget ();
1038 return 1;
1039 }
1040 /**
1041 * Returns the height of the receiver's header
1042 *
1043 * @return the height of the header or zero if the header is not visible
1044 *
1045 * @exception SWTException <ul>
1046 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
1047 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
1048 * </ul>
1049 *
1050 * @since 2.0
1051 */
1052 public int getHeaderHeight () {
1053 checkWidget ();
1054 if (!header.getVisible ()) return 0;
1055 return header.getSize ().y;
1056 }
1057 int getHeaderPadding () {
1058 return MARGIN_CELL + WIDTH_HEADER_SHADOW;
1059 }
1060 /**
1061 * Returns <code>true</code> if the receiver's header is visible,
1062 * and <code>false</code> otherwise.
1063 * <p>
1064 * If one of the receiver's ancestors is not visible or some
1065 * other condition makes the receiver not visible, this method
1066 * may still indicate that it is considered visible even though
1067 * it may not actually be showing.
1068 * </p>
1069 *
1070 * @return the receiver's header's visibility state
1071 *
1072 * @exception SWTException <ul>
1073 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
1074 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
1075 * </ul>
1076 */
1077 public boolean getHeaderVisible () {
1078 checkWidget ();
1079 return header.getVisible ();
1080 }
1081 /**
1082 * Returns the item at the given, zero-relative index in the
1083 * receiver. Throws an exception if the index is out of range.
1084 *
1085 * @param index the index of the item to return
1086 * @return the item at the given index
1087 *
1088 * @exception IllegalArgumentException <ul>
1089 * <li>ERROR_INVALID_RANGE - if the index is not between 0 and the number of elements in the list minus 1 (inclusive)</li>
1090 * </ul>
1091 * @exception SWTException <ul>
1092 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
1093 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
1094 * </ul>
1095 */
1096 public CTableItem getItem (int index) {
1097 checkWidget ();
1098 if (!(0 <= index && index < itemsCount)) SWT.error (SWT.ERROR_INVALID_RANGE);
1099 return items [index];
1100 }
1101 /**
1102 * Returns the item at the given point in the receiver
1103 * or null if no such item exists. The point is in the
1104 * coordinate system of the receiver.
1105 * <p>
1106 * The item that is returned represents an item that could be selected by the user.
1107 * For example, if selection only occurs in items in the first column, then null is
1108 * returned if the point is outside of the item.
1109 * Note that the SWT.FULL_SELECTION style hint, which specifies the selection policy,
1110 * determines the extent of the selection.
1111 * </p>
1112 *
1113 * @param point the point used to locate the item
1114 * @return the item at the given point, or null if the point is not in a selectable item
1115 *
1116 * @exception IllegalArgumentException <ul>
1117 * <li>ERROR_NULL_ARGUMENT - if the point is null</li>
1118 * </ul>
1119 * @exception SWTException <ul>
1120 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
1121 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
1122 * </ul>
1123 */
1124 public CTableItem getItem (Point point) {
1125 checkWidget ();
1126 if (point == null) SWT.error (SWT.ERROR_NULL_ARGUMENT);
1127 int index = (point.y - getHeaderHeight ()) / itemHeight + topIndex;
1128 if (!(0 <= index && index < itemsCount)) return null; /* below the last item */
1129 CTableItem result = items [index];
1130 if (!result.getHitBounds ().contains (point)) return null; /* considers the x value */
1131 return result;
1132 }
1133 /**
1134 * Returns the number of items contained in the receiver.
1135 *
1136 * @return the number of items
1137 *
1138 * @exception SWTException <ul>
1139 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
1140 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
1141 * </ul>
1142 */
1143 public int getItemCount () {
1144 checkWidget ();
1145 return itemsCount;
1146 }
1147 /**
1148 * Returns the height of the area which would be used to
1149 * display <em>one</em> of the items in the receiver.
1150 *
1151 * @return the height of one item
1152 *
1153 * @exception SWTException <ul>
1154 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
1155 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
1156 * </ul>
1157 */
1158 public int getItemHeight () {
1159 checkWidget ();
1160 return itemHeight;
1161 }
1162 /**
1163 * Returns a (possibly empty) array of <code>TableItem</code>s which
1164 * are the items in the receiver.
1165 * <p>
1166 * Note: This is not the actual structure used by the receiver
1167 * to maintain its list of items, so modifying the array will
1168 * not affect the receiver.
1169 * </p>
1170 *
1171 * @return the items in the receiver
1172 *
1173 * @exception SWTException <ul>
1174 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
1175 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
1176 * </ul>
1177 */
1178 public CTableItem[] getItems () {
1179 checkWidget ();
1180 CTableItem[] result = new CTableItem [itemsCount];
1181 System.arraycopy (items, 0, result, 0, itemsCount);
1182 return result;
1183 }
1184 /*
1185 * Returns the current y-coordinate that the specified item should have.
1186 */
1187 int getItemY (CTableItem item) {
1188 return (item.index - topIndex) * itemHeight + getHeaderHeight ();
1189 }
1190 /**
1191 * Returns <code>true</code> if the receiver's lines are visible,
1192 * and <code>false</code> otherwise. Note that some platforms draw
1193 * grid lines while others may draw alternating row colors.
1194 * <p>
1195 * If one of the receiver's ancestors is not visible or some
1196 * other condition makes the receiver not visible, this method
1197 * may still indicate that it is considered visible even though
1198 * it may not actually be showing.
1199 * </p>
1200 *
1201 * @return the visibility state of the lines
1202 *
1203 * @exception SWTException <ul>
1204 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
1205 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
1206 * </ul>
1207 */
1208 public boolean getLinesVisible () {
1209 checkWidget ();
1210 return linesVisible;
1211 }
1212 CTableColumn[] getOrderedColumns () {
1213 if (orderedColumns != null) return orderedColumns;
1214 return columns;
1215 }
1216 /**
1217 * Returns an array of <code>TableItem</code>s that are currently
1218 * selected in the receiver. The order of the items is unspecified.
1219 * An empty array indicates that no items are selected.
1220 * <p>
1221 * Note: This is not the actual structure used by the receiver
1222 * to maintain its selection, so modifying the array will
1223 * not affect the receiver.
1224 * </p>
1225 * @return an array representing the selection
1226 *
1227 * @exception SWTException <ul>
1228 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
1229 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
1230 * </ul>
1231 */
1232 public CTableItem[] getSelection () {
1233 checkWidget ();
1234 CTableItem[] result = new CTableItem [selectedItems.length];
1235 System.arraycopy (selectedItems, 0, result, 0, selectedItems.length);
1236 sortAscent (result);
1237 return result;
1238 }
1239 /**
1240 * Returns the number of selected items contained in the receiver.
1241 *
1242 * @return the number of selected items
1243 *
1244 * @exception SWTException <ul>
1245 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
1246 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
1247 * </ul>
1248 */
1249 public int getSelectionCount () {
1250 checkWidget ();
1251 return selectedItems.length;
1252 }
1253 /**
1254 * Returns the zero-relative index of the item which is currently
1255 * selected in the receiver, or -1 if no item is selected.
1256 *
1257 * @return the index of the selected item
1258 *
1259 * @exception SWTException <ul>
1260 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
1261 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
1262 * </ul>
1263 */
1264 public int getSelectionIndex () {
1265 checkWidget ();
1266 if (selectedItems.length == 0) return -1;
1267 return selectedItems [0].index;
1268 }
1269 /*
1270 * Returns the index of the argument in the receiver's array of currently-
1271 * selected items, or -1 if the item is not currently selected.
1272 */
1273 int getSelectionIndex (CTableItem item) {
1274 for (int i = 0; i < selectedItems.length; i++) {
1275 if (selectedItems [i] == item) return i;
1276 }
1277 return -1;
1278 }
1279 /**
1280 * Returns the zero-relative indices of the items which are currently
1281 * selected in the receiver. The order of the indices is unspecified.
1282 * The array is empty if no items are selected.
1283 * <p>
1284 * Note: This is not the actual structure used by the receiver
1285 * to maintain its selection, so modifying the array will
1286 * not affect the receiver.
1287 * </p>
1288 * @return the array of indices of the selected items
1289 *
1290 * @exception SWTException <ul>
1291 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
1292 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
1293 * </ul>
1294 */
1295 public int [] getSelectionIndices () {
1296 checkWidget ();
1297 int[] result = new int [selectedItems.length];
1298 for (int i = 0; i < selectedItems.length; i++) {
1299 result [i] = selectedItems [i].index;
1300 }
1301 sortAscent (result);
1302 return result;
1303 }
1304 /**
1305 * Returns the column which shows the sort indicator for
1306 * the receiver. The value may be null if no column shows
1307 * the sort indicator.
1308 *
1309 * @return the sort indicator
1310 *
1311 * @exception SWTException <ul>
1312 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
1313 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
1314 * </ul>
1315 *
1316 * @see #setSortColumn(CTableColumn)
1317 *
1318 * @since 3.2
1319 */
1320 public CTableColumn getSortColumn () {
1321 checkWidget ();
1322 return sortColumn;
1323 }
1324 /**
1325 * Returns the direction of the sort indicator for the receiver.
1326 * The value will be one of <code>UP</code>, <code>DOWN</code>
1327 * or <code>NONE</code>.
1328 *
1329 * @return the sort direction
1330 *
1331 * @exception SWTException <ul>
1332 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
1333 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
1334 * </ul>
1335 *
1336 * @see #setSortDirection(int)
1337 *
1338 * @since 3.2
1339 */
1340 public int getSortDirection () {
1341 checkWidget ();
1342 return sortDirection;
1343 }
1344 /**
1345 * Returns the zero-relative index of the item which is currently
1346 * at the top of the receiver. This index can change when items are
1347 * scrolled or new items are added or removed.
1348 *
1349 * @return the index of the top item
1350 *
1351 * @exception SWTException <ul>
1352 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
1353 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
1354 * </ul>
1355 */
1356 public int getTopIndex () {
1357 checkWidget ();
1358 return topIndex;
1359 }
1360 Image getUncheckedImage () {
1361 return (Image) display.getData (ID_UNCHECKED);
1362 }
1363 void handleEvents (Event event) {
1364 switch (event.type) {
1365 case SWT.Paint:
1366 if (event.widget == header) {
1367 headerOnPaint (event);
1368 } else {
1369 onPaint (event);
1370 }
1371 break;
1372 case SWT.MenuDetect: {
1373 notifyListeners (SWT.MenuDetect, event);
1374 break;
1375 }
1376 case SWT.MouseDown:
1377 if (event.widget == header) {
1378 headerOnMouseDown (event);
1379 } else {
1380 onMouseDown (event);
1381 }
1382 break;
1383 case SWT.MouseUp:
1384 if (event.widget == header) {
1385 headerOnMouseUp (event);
1386 } else {
1387 onMouseUp (event);
1388 }
1389 break;
1390 case SWT.MouseHover:
1391 headerOnMouseHover (event); break;
1392 case SWT.MouseMove:
1393 headerOnMouseMove (event); break;
1394 case SWT.MouseDoubleClick:
1395 if (event.widget == header) {
1396 headerOnMouseDoubleClick (event);
1397 } else {
1398 onMouseDoubleClick (event);
1399 }
1400 break;
1401 case SWT.MouseExit:
1402 headerOnMouseExit (); break;
1403 case SWT.Dispose:
1404 onDispose (event); break;
1405 case SWT.KeyDown:
1406 onKeyDown (event); break;
1407 case SWT.Resize:
1408 onResize (event); break;
1409 case SWT.Selection:
1410 if (event.widget == getHorizontalBar ()) {
1411 onScrollHorizontal (event);
1412 }
1413 if (event.widget == getVerticalBar ()) {
1414 onScrollVertical (event);
1415 }
1416 break;
1417 case SWT.FocusOut:
1418 onFocusOut (); break;
1419 case SWT.FocusIn:
1420 onFocusIn (); break;
1421 case SWT.Traverse:
1422 switch (event.detail) {
1423 case SWT.TRAVERSE_ESCAPE:
1424 case SWT.TRAVERSE_RETURN:
1425 case SWT.TRAVERSE_TAB_NEXT:
1426 case SWT.TRAVERSE_TAB_PREVIOUS:
1427 case SWT.TRAVERSE_PAGE_NEXT:
1428 case SWT.TRAVERSE_PAGE_PREVIOUS:
1429 event.doit = true;
1430 break;
1431 }
1432 break;
1433 }
1434 }
1435 String headerGetToolTip (int x) {
1436 if (resizeColumn != null) return null;
1437 int orderedIndex = computeColumnIntersect (x, 0);
1438 if (orderedIndex == -1) return null;
1439 CTableColumn[] orderedColumns = getOrderedColumns ();
1440 CTableColumn column = orderedColumns [orderedIndex];
1441 if (column.toolTipText == null) return null;
1442
1443 /* no tooltip should appear if the hover is at a column resize opportunity */
1444 int columnX = column.getX ();
1445 if (orderedIndex > 0 && orderedColumns [orderedIndex - 1].resizable) {
1446 /* left column bound is resizable */
1447 if (x - columnX <= TOLLERANCE_COLUMNRESIZE) return null;
1448 }
1449 if (column.resizable) {
1450 /* right column bound is resizable */
1451 int columnRightX = columnX + column.width;
1452 if (columnRightX - x <= TOLLERANCE_COLUMNRESIZE) return null;
1453 }
1454 return removeMnemonics (column.toolTipText);
1455 }
1456 void headerHideToolTip() {
1457 if (toolTipShell == null) return;
1458 for (int toolTipEvent : toolTipEvents) {
1459 header.removeListener (toolTipEvent, toolTipListener);
1460 }
1461 toolTipShell.dispose ();
1462 toolTipShell = null;
1463 toolTipLabel = null;
1464 }
1465 void headerOnMouseDoubleClick (Event event) {
1466 if (!isFocusControl ()) setFocus ();
1467 if (columns.length == 0) return;
1468 CTableColumn[] orderedColumns = getOrderedColumns ();
1469 int x = -horizontalOffset;
1470 for (int i = 0; i < orderedColumns.length; i++) {
1471 CTableColumn column = orderedColumns [i];
1472 x += column.width;
1473 if (event.x < x) {
1474 /* found the clicked column */
1475 CTableColumn packColumn = null;
1476 if (x - event.x <= TOLLERANCE_COLUMNRESIZE) {
1477 /* clicked on column bound for this column */
1478 packColumn = column;
1479 } else {
1480 if (i > 0 && event.x - column.getX () <= TOLLERANCE_COLUMNRESIZE) {
1481 /* clicked on column bound that applies to previous column */
1482 packColumn = orderedColumns [i - 1];
1483 }
1484 }
1485 if (packColumn != null) {
1486 packColumn.pack ();
1487 resizeColumn = null;
1488 if (Math.abs (packColumn.getX () + packColumn.width - event.x) > TOLLERANCE_COLUMNRESIZE) {
1489 /* column separator has relocated away from pointer location */
1490 setCursor (null);
1491 }
1492 return;
1493 }
1494 /* did not click on column separator, so just fire column event */
1495 Event newEvent = new Event ();
1496 newEvent.widget = column;
1497 column.notifyListeners (SWT.DefaultSelection, newEvent);
1498 return;
1499 }
1500 }
1501 }
1502 void headerOnMouseDown (Event event) {
1503 if (event.button != 1) return;
1504 CTableColumn[] orderedColumns = getOrderedColumns ();
1505 int x = -horizontalOffset;
1506 for (CTableColumn column : orderedColumns) {
1507 x += column.width;
1508 /* if close to a resizable column separator line then begin column resize */
1509 if (column.resizable && Math.abs (x - event.x) <= TOLLERANCE_COLUMNRESIZE) {
1510 resizeColumn = column;
1511 resizeColumnX = x;
1512 return;
1513 }
1514 /*
1515 * If within column but not near separator line then start column drag
1516 * if column is moveable, or just fire column Selection otherwise.
1517 */
1518 if (event.x < x) {
1519 if (column.moveable) {
1520 /* open tracker on the dragged column's header cell */
1521 int columnX = column.getX ();
1522 int pointerOffset = event.x - columnX;
1523 headerHideToolTip ();
1524 Tracker tracker = new Tracker (this, SWT.NONE);
1525 tracker.setRectangles (new Rectangle[] {
1526 new Rectangle (columnX, 0, column.width, getHeaderHeight ())
1527 });
1528 if (!tracker.open ()) return; /* cancelled */
1529 /* determine which column was dragged onto */
1530 Rectangle result = tracker.getRectangles () [0];
1531 int pointerX = result.x + pointerOffset;
1532 if (pointerX < 0) return; /* dragged too far left */
1533 x = -horizontalOffset;
1534 for (int destIndex = 0; destIndex < orderedColumns.length; destIndex++) {
1535 CTableColumn destColumn = orderedColumns [destIndex];
1536 x += destColumn.width;
1537 if (pointerX < x) {
1538 int oldIndex = column.getOrderIndex ();
1539 if (destIndex == oldIndex) { /* dragged onto self */
1540 Event newEvent = new Event ();
1541 newEvent.widget = column;
1542 column.notifyListeners (SWT.Selection, newEvent);
1543 return;
1544 }
1545 int leftmostIndex = Math.min (destIndex, oldIndex);
1546 int[] oldOrder = getColumnOrder ();
1547 int[] newOrder = new int [oldOrder.length];
1548 System.arraycopy (oldOrder, 0, newOrder, 0, leftmostIndex);
1549 if (leftmostIndex == oldIndex) {
1550 /* column moving to the right */
1551 System.arraycopy (oldOrder, oldIndex + 1, newOrder, oldIndex, destIndex - oldIndex);
1552 } else {
1553 /* column moving to the left */
1554 System.arraycopy (oldOrder, destIndex, newOrder, destIndex + 1, oldIndex - destIndex);
1555 }
1556 newOrder [destIndex] = oldOrder [oldIndex];
1557 int rightmostIndex = Math.max (destIndex, oldIndex);
1558 System.arraycopy (
1559 oldOrder,
1560 rightmostIndex + 1,
1561 newOrder,
1562 rightmostIndex + 1,
1563 newOrder.length - rightmostIndex - 1);
1564 setColumnOrder (newOrder);
1565 return;
1566 }
1567 }
1568 return; /* dragged too far right */
1569 }
1570 /* column is not moveable */
1571 Event newEvent = new Event ();
1572 newEvent.widget = column;
1573 column.notifyListeners (SWT.Selection, newEvent);
1574 return;
1575 }
1576 }
1577 }
1578 void headerOnMouseExit () {
1579 if (resizeColumn != null) return;
1580 setCursor (null); /* ensure that a column resize cursor does not escape */
1581 }
1582 void headerOnMouseHover (Event event) {
1583 headerShowToolTip (event.x);
1584 }
1585 void headerOnMouseMove (Event event) {
1586 if (resizeColumn == null) {
1587 /* not currently resizing a column */
1588 for (CTableColumn column : columns) {
1589 int x = column.getX () + column.width;
1590 if (Math.abs (x - event.x) <= TOLLERANCE_COLUMNRESIZE) {
1591 if (column.resizable) {
1592 setCursor (display.getSystemCursor (SWT.CURSOR_SIZEWE));
1593 } else {
1594 setCursor (null);
1595 }
1596 return;
1597 }
1598 }
1599 setCursor (null);
1600 return;
1601 }
1602
1603 /* currently resizing a column */
1604
1605 /* don't allow the resize x to move left of the column's x position */
1606 if (event.x <= resizeColumn.getX ()) return;
1607
1608 /* redraw the resizing line at its new location */
1609 GC gc = new GC (this);
1610 gc.setForeground (display.getSystemColor (SWT.COLOR_BLACK));
1611 int lineHeight = clientArea.height;
1612 redraw (resizeColumnX - 1, 0, 1, lineHeight, false);
1613 resizeColumnX = event.x;
1614 gc.drawLine (resizeColumnX - 1, 0, resizeColumnX - 1, lineHeight);
1615 gc.dispose ();
1616 }
1617 void headerOnMouseUp (Event event) {
1618 if (resizeColumn == null) return; /* not resizing a column */
1619
1620 /* remove the resize line */
1621 GC gc = new GC (this);
1622 redraw (resizeColumnX - 1, 0, 1, clientArea.height, false);
1623 gc.dispose ();
1624
1625 int newWidth = resizeColumnX - resizeColumn.getX ();
1626 if (newWidth != resizeColumn.width) {
1627 setCursor (null);
1628 updateColumnWidth (resizeColumn, newWidth);
1629 }
1630 resizeColumnX = -1;
1631 resizeColumn = null;
1632 }
1633 void headerOnPaint (Event event) {
1634 CTableColumn[] orderedColumns = getOrderedColumns ();
1635 int numColumns = orderedColumns.length;
1636 GC gc = event.gc;
1637 Rectangle clipping = gc.getClipping ();
1638 int startColumn = -1, endColumn = -1;
1639 if (numColumns > 0) {
1640 startColumn = computeColumnIntersect (clipping.x, 0);
1641 if (startColumn != -1) { /* the clip x is within a column's bounds */
1642 endColumn = computeColumnIntersect (clipping.x + clipping.width, startColumn);
1643 if (endColumn == -1) endColumn = numColumns - 1;
1644 }
1645 } else {
1646 startColumn = endColumn = 0;
1647 }
1648
1649 /* paint the column header shadow that spans the full header width */
1650 Point headerSize = header.getSize ();
1651 headerPaintHShadows (gc, 0, 0, headerSize.x, headerSize.y);
1652
1653 /* if all damage is to the right of the last column then finished */
1654 if (startColumn == -1) return;
1655
1656 /* paint each of the column headers */
1657 if (numColumns == 0) return; /* no headers to paint */
1658 for (int i = startColumn; i <= endColumn; i++) {
1659 headerPaintVShadows (gc, orderedColumns [i].getX (), 0, orderedColumns [i].width, headerSize.y);
1660 orderedColumns [i].paint (gc);
1661 }
1662 }
1663 void headerPaintHShadows (GC gc, int x, int y, int width, int height) {
1664 gc.setClipping (x, y, width, height);
1665 int endX = x + width;
1666 gc.setForeground (display.getSystemColor (SWT.COLOR_WIDGET_HIGHLIGHT_SHADOW));
1667 gc.drawLine (x, y, endX, y); /* highlight shadow */
1668 gc.setForeground (display.getSystemColor (SWT.COLOR_WIDGET_NORMAL_SHADOW));
1669 gc.drawLine (x, height - 2, endX, height - 2); /* lowlight shadow */
1670 gc.setForeground (display.getSystemColor (SWT.COLOR_WIDGET_DARK_SHADOW));
1671 gc.drawLine (x, height - 1, endX, height - 1); /* outer shadow */
1672 }
1673 void headerPaintVShadows (GC gc, int x, int y, int width, int height) {
1674 gc.setClipping (x, y, width, height);
1675 int endX = x + width;
1676 gc.setForeground (display.getSystemColor (SWT.COLOR_WIDGET_HIGHLIGHT_SHADOW));
1677 gc.drawLine (x, y, x, y + height - 1); /* highlight shadow */
1678 gc.setForeground (display.getSystemColor (SWT.COLOR_WIDGET_NORMAL_SHADOW));
1679 gc.drawLine (endX - 2, y + 1, endX - 2, height - 2); /* light inner shadow */
1680 gc.setForeground (display.getSystemColor (SWT.COLOR_WIDGET_DARK_SHADOW));
1681 gc.drawLine (endX - 1, y, endX - 1, height - 1); /* dark outer shadow */
1682 }
1683 void headerShowToolTip (int x) {
1684 String tooltip = headerGetToolTip (x);
1685 if (tooltip == null || tooltip.length () == 0) return;
1686
1687 if (toolTipShell == null) {
1688 toolTipShell = new Shell (getShell (), SWT.ON_TOP | SWT.TOOL);
1689 toolTipLabel = new Label (toolTipShell, SWT.CENTER);
1690 Display display = toolTipShell.getDisplay ();
1691 toolTipLabel.setForeground (display.getSystemColor (SWT.COLOR_INFO_FOREGROUND));
1692 toolTipLabel.setBackground (display.getSystemColor (SWT.COLOR_INFO_BACKGROUND));
1693 for (int toolTipEvent : toolTipEvents) {
1694 header.addListener (toolTipEvent, toolTipListener);
1695 }
1696 }
1697 if (headerUpdateToolTip (x)) {
1698 toolTipShell.setVisible (true);
1699 } else {
1700 headerHideToolTip ();
1701 }
1702 }
1703 boolean headerUpdateToolTip (int x) {
1704 String tooltip = headerGetToolTip (x);
1705 if (tooltip == null || tooltip.length () == 0) return false;
1706 if (tooltip.equals (toolTipLabel.getText ())) return true;
1707
1708 toolTipLabel.setText (tooltip);
1709 CTableColumn column = getOrderedColumns () [computeColumnIntersect (x, 0)];
1710 toolTipShell.setData (Integer.valueOf (column.getIndex ()));
1711 Point labelSize = toolTipLabel.computeSize (SWT.DEFAULT, SWT.DEFAULT, true);
1712 labelSize.x += 2; labelSize.y += 2;
1713 toolTipLabel.setSize (labelSize);
1714 toolTipShell.pack ();
1715 /*
1716 * On some platforms, there is a minimum size for a shell
1717 * which may be greater than the label size.
1718 * To avoid having the background of the tip shell showing
1719 * around the label, force the label to fill the entire client area.
1720 */
1721 Rectangle area = toolTipShell.getClientArea ();
1722 toolTipLabel.setSize (area.width, area.height);
1723
1724 /* Position the tooltip and ensure it's not located off the screen */
1725 Point cursorLocation = getDisplay ().getCursorLocation ();
1726 int cursorHeight = 21; /* assuming cursor is 21x21 */
1727 Point size = toolTipShell.getSize ();
1728 Rectangle rect = getMonitor ().getBounds ();
1729 Point pt = new Point (cursorLocation.x, cursorLocation.y + cursorHeight + 2);
1730 pt.x = Math.max (pt.x, rect.x);
1731 if (pt.x + size.x > rect.x + rect.width) pt.x = rect.x + rect.width - size.x;
1732 if (pt.y + size.y > rect.y + rect.height) pt.y = cursorLocation.y - 2 - size.y;
1733 toolTipShell.setLocation (pt);
1734 return true;
1735 }
1736 /**
1737 * Searches the receiver's list starting at the first column
1738 * (index 0) until a column is found that is equal to the
1739 * argument, and returns the index of that column. If no column
1740 * is found, returns -1.
1741 *
1742 * @param column the search column
1743 * @return the index of the column
1744 *
1745 * @exception IllegalArgumentException <ul>
1746 * <li>ERROR_NULL_ARGUMENT - if the column is null</li>
1747 * </ul>
1748 * @exception SWTException <ul>
1749 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
1750 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
1751 * </ul>
1752 */
1753 public int indexOf (CTableColumn column) {
1754 checkWidget ();
1755 if (column == null) SWT.error (SWT.ERROR_NULL_ARGUMENT);
1756 if (column.parent != this) return -1;
1757 return column.getIndex ();
1758 }
1759 /**
1760 * Searches the receiver's list starting at the first item
1761 * (index 0) until an item is found that is equal to the
1762 * argument, and returns the index of that item. If no item
1763 * is found, returns -1.
1764 *
1765 * @param item the search item
1766 * @return the index of the item
1767 *
1768 * @exception IllegalArgumentException <ul>
1769 * <li>ERROR_NULL_ARGUMENT - if the item is null</li>
1770 * </ul>
1771 * @exception SWTException <ul>
1772 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
1773 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
1774 * </ul>
1775 */
1776 public int indexOf (CTableItem item) {
1777 checkWidget ();
1778 if (item == null) SWT.error (SWT.ERROR_NULL_ARGUMENT);
1779 if (item.parent != this) return -1;
1780 return item.index;
1781 }
1782
1783 void initAccessibility () {
1784 // TODO: does this all work if CTable is virtual?
1785 final Accessible accessibleTable = getAccessible();
1786 accessibleTable.addAccessibleListener(new AccessibleAdapter() {
1787 @Override
1788 public void getName(AccessibleEvent e) {
1789 /* CTables take their name from the preceding Label, if any. */
1790 Control[] siblings = getParent().getChildren();
1791 for (int i = 0; i < siblings.length; i++) {
1792 if (i != 0 && siblings[i] == CTable.this) {
1793 Control control = siblings[i-1];
1794 if (control instanceof Label) {
1795 e.result = ((Label) control).getText();
1796 }
1797 }
1798 }
1799 }
1800 @Override
1801 public void getHelp(AccessibleEvent e) {
1802 /* A CTable's toolTip text (if any) can be used as its help text. */
1803 e.result = getToolTipText();
1804 }
1805 });
1806 accessibleTable.addAccessibleControlListener(new AccessibleControlAdapter() {
1807 /* Child IDs are assigned as follows:
1808 * - column header ids (if any) are numbered from 0 to columnCount - 1
1809 * - cell ids are numbered in row-major order (starting from columnCount if there are columns)
1810 * Accessibles are returned in getChild.
1811 */
1812 @Override
1813 public void getChild(AccessibleControlEvent e) {
1814 int childID = e.childID;
1815 if (childID == ACC.CHILDID_CHILD_AT_INDEX) childID = e.detail; // childID == index
1816 if (columns.length > 0 && 0 <= childID && childID < columns.length) { // header cell
1817 CTableColumn column = columns [childID];
1818 e.accessible = column.getAccessible(accessibleTable);
1819 } else { // item cell
1820 int columnCount = columns.length > 0 ? columns.length : 1;
1821 if (columns.length > 0) childID -= columnCount;
1822 if (0 <= childID && childID < itemsCount * columnCount) {
1823 int rowIndex = childID / columnCount;
1824 int columnIndex = childID - rowIndex * columnCount;
1825 e.accessible = items[rowIndex].getAccessible (accessibleTable, columnIndex);
1826 }
1827 }
1828 }
1829 @Override
1830 public void getChildAtPoint(AccessibleControlEvent e) {
1831 Point point = toControl(e.x, e.y);
1832 if (columns.length > 0 && point.y < getHeaderHeight ()) { // header cell
1833 int columnIndex = computeColumnIntersect (point.x, 0);
1834 if (columnIndex != -1) {
1835 CTableColumn column = columns [columnIndex];
1836 e.accessible = column.getAccessible (accessibleTable);
1837 }
1838 } else { // item cell
1839 int columnIndex = columns.length > 0 ? computeColumnIntersect (point.x, 0) : 0;
1840 if (columnIndex != -1) {
1841 int rowIndex = (point.y - getHeaderHeight ()) / itemHeight + topIndex;
1842 if (0 <= rowIndex && rowIndex < itemsCount) {
1843 if (items [rowIndex].getHitBounds ().contains (point)) { /* considers the x value */
1844 e.accessible = items[rowIndex].getAccessible (accessibleTable, columnIndex);
1845 }
1846 }
1847 }
1848 }
1849 }
1850 @Override
1851 public void getChildCount(AccessibleControlEvent e) {
1852 e.detail = columns.length > 0 ? columns.length + itemsCount * columns.length : itemsCount;
1853 }
1854 @Override
1855 public void getChildren(AccessibleControlEvent e) {
1856 int childIdCount = columns.length > 0 ? columns.length + itemsCount * columns.length : itemsCount;
1857 Object[] children = new Object[childIdCount];
1858 for (int i = 0; i < childIdCount; i++) {
1859 children[i] = Integer.valueOf(i);
1860 }
1861 e.children = children;
1862 }
1863 @Override
1864 public void getFocus(AccessibleControlEvent e) {
1865 e.childID = isFocusControl() ? ACC.CHILDID_SELF : ACC.CHILDID_NONE;
1866 }
1867 @Override
1868 public void getLocation(AccessibleControlEvent e) {
1869 Rectangle location = null;
1870 Point pt = null;
1871 int childID = e.childID;
1872 if (childID == ACC.CHILDID_SELF) { // table
1873 location = getBounds();
1874 pt = getParent().toDisplay(location.x, location.y);
1875 } else if (columns.length > 0 && 0 <= childID && childID < columns.length) { // header cell
1876 CTableColumn column = columns [childID];
1877 location = new Rectangle (column.getX (), 0, column.getWidth(), getHeaderHeight());
1878 pt = toDisplay(location.x, location.y);
1879 } else { // item cell
1880 int columnCount = columns.length > 0 ? columns.length : 1;
1881 if (columns.length > 0) childID -= columnCount;
1882 if (0 <= childID && childID < itemsCount * columnCount) {
1883 int rowIndex = childID / columnCount;
1884 int columnIndex = childID - rowIndex * columnCount;
1885 location = items[rowIndex].getBounds(columnIndex);
1886 pt = toDisplay(location.x, location.y);
1887 }
1888 }
1889 if (location != null && pt != null) {
1890 e.x = pt.x;
1891 e.y = pt.y;
1892 e.width = location.width;
1893 e.height = location.height;
1894 }
1895 }
1896 @Override
1897 public void getRole(AccessibleControlEvent e) {
1898 e.detail = e.childID == ACC.CHILDID_SELF ? ACC.ROLE_TABLE : ACC.ROLE_TABLECELL;
1899 }
1900 @Override
1901 public void getSelection(AccessibleControlEvent e) {
1902 int columnCount = columns.length > 0 ? columns.length : 1;
1903 int [] selectionIndices = getSelectionIndices();
1904 Object[] selectedChildren = new Object[selectionIndices.length * columnCount];
1905 for (int i = 0; i < selectionIndices.length; i++) {
1906 int row = selectionIndices[i];
1907 for (int col = 0; col < columnCount; col++) {
1908 selectedChildren[i] = Integer.valueOf(row * columnCount + col);
1909 }
1910 }
1911 e.children = selectedChildren;
1912 }
1913 @Override
1914 public void getState(AccessibleControlEvent e) {
1915 int state = ACC.STATE_NORMAL;
1916 int childID = e.childID;
1917 if (childID == ACC.CHILDID_SELF) { // table
1918 state |= ACC.STATE_FOCUSABLE;
1919 if (isFocusControl()) {
1920 state |= ACC.STATE_FOCUSED;
1921 }
1922 } else if (columns.length > 0 && 0 <= childID && childID < columns.length) { // header cell
1923 /* CTable does not support header cell focus or selection. */
1924 state |= ACC.STATE_SIZEABLE;
1925 } else { // item cell
1926 int columnCount = columns.length > 0 ? columns.length : 1;
1927 if (columns.length > 0) childID -= columnCount;
1928 if (0 <= childID && childID < itemsCount * columnCount) {
1929 /* CTable does not support cell selection (only row selection). */
1930 int rowIndex = childID / columnCount;
1931 state |= ACC.STATE_SELECTABLE;
1932 if (isFocusControl()) {
1933 state |= ACC.STATE_FOCUSABLE;
1934 }
1935 if (items[rowIndex].isSelected()) {
1936 state |= ACC.STATE_SELECTED;
1937 if (isFocusControl()) {
1938 state |= ACC.STATE_FOCUSED;
1939 }
1940 }
1941 }
1942 }
1943 e.detail = state;
1944 }
1945 });
1946 accessibleTable.addAccessibleTableListener(new AccessibleTableAdapter() {
1947 @Override
1948 public void deselectColumn(AccessibleTableEvent e) {
1949 /* CTable does not support column selection. */
1950 }
1951 @Override
1952 public void deselectRow(AccessibleTableEvent e) {
1953 deselect(e.row);
1954 e.result = ACC.OK;
1955 }
1956 @Override
1957 public void getCaption(AccessibleTableEvent e) {
1958 // TODO: What is a caption? How does it differ from name? Should app supply?
1959 e.result = "This is the Custom Table's Test Caption";
1960 }
1961 @Override
1962 public void getCell(AccessibleTableEvent e) {
1963 int index = e.row;
1964 if (0 <= index && index < itemsCount) {
1965 CTableItem row = items [index];
1966 index = e.column;
1967 if (columns.length == 0 || 0 <= index && index < columns.length) {
1968 e.accessible = row.getAccessible (accessibleTable, index);
1969 }
1970 }
1971 }
1972 @Override
1973 public void getColumnCount(AccessibleTableEvent e) {
1974 int columnCount = columns.length > 0 ? columns.length : 1;
1975 e.count = columnCount;
1976 }
1977 @Override
1978 public void getColumnDescription(AccessibleTableEvent e) {
1979 // TODO: What is a description? How does it differ from name? Should app supply?
1980 e.result = "This is the Custom Table's Test Description for column " + e.column;
1981 }
1982 // public void getColumnHeader(AccessibleTableEvent e) {
1983 // e.accessible = header.getAccessible();
1984 // }
1985 @Override
1986 public void getColumnHeaderCells(AccessibleTableEvent e) {
1987 if (columns.length == 0) {
1988 /* The CTable is being used as a list, and there are no headers. */
1989 e.accessibles = null;
1990 } else {
1991 Accessible[] accessibles = new Accessible[columns.length];
1992 for (int i = 0; i < columns.length; i++) {
1993 CTableColumn column = columns [i];
1994 accessibles[i] = column.getAccessible (accessibleTable);
1995 }
1996 e.accessibles = accessibles;
1997 }
1998 }
1999 @Override
2000 public void getRowCount(AccessibleTableEvent e) {
2001 e.count = itemsCount;
2002 }
2003 @Override
2004 public void getRowDescription(AccessibleTableEvent e) {
2005 // TODO: What is a description? How does it differ from name? Should app supply?
2006 e.result = "This is the Custom Table's Test Description for row " + e.row;
2007 }
2008 @Override
2009 public void getRowHeader(AccessibleTableEvent e) {
2010 /* CTable does not support row headers. */
2011 }
2012 @Override
2013 public void getSelectedCellCount(AccessibleTableEvent e) {
2014 int columnCount = columns.length > 0 ? columns.length : 1;
2015 e.count = selectedItems.length * columnCount;
2016 }
2017 @Override
2018 public void getSelectedCells(AccessibleTableEvent e) {
2019 int columnCount = columns.length > 0 ? columns.length : 1;
2020 Accessible[] accessibles = new Accessible[selectedItems.length * columnCount];
2021 for (int r = 0; r < selectedItems.length; r++) {
2022 CTableItem row = selectedItems [r];
2023 for (int c = 0; c < columnCount; c++)
2024 accessibles[r+c] = row.getAccessible (accessibleTable, c);
2025 }
2026 e.accessibles = accessibles;
2027 }
2028 @Override
2029 public void getSelectedColumnCount(AccessibleTableEvent e) {
2030 e.count = 0; /* CTable does not support column selection. */
2031 }
2032 @Override
2033 public void getSelectedColumns(AccessibleTableEvent e) {
2034 /* CTable does not support column selection. */
2035 }
2036 @Override
2037 public void getSelectedRowCount(AccessibleTableEvent e) {
2038 e.count = selectedItems.length;
2039 }
2040 @Override
2041 public void getSelectedRows(AccessibleTableEvent e) {
2042 int[] selectedIndices = new int[selectedItems.length];
2043 for (int i = 0; i < selectedItems.length; i++) {
2044 selectedIndices[i] = selectedItems [i].index;
2045 }
2046 e.selected = selectedIndices;
2047 }
2048 @Override
2049 public void getSummary(AccessibleTableEvent e) {
2050 // TODO: What is a summary? How does it differ from name? Should app supply?
2051 e.result = "This is the Custom Table's Summary";
2052 }
2053 @Override
2054 public void isColumnSelected(AccessibleTableEvent e) {
2055 e.isSelected = false; /* CTable does not support column selection. */
2056 }
2057 @Override
2058 public void isRowSelected(AccessibleTableEvent e) {
2059 e.isSelected = isSelected(e.row);
2060 }
2061 @Override
2062 public void selectColumn(AccessibleTableEvent e) {
2063 /* CTable does not support column selection. */
2064 }
2065 @Override
2066 public void selectRow(AccessibleTableEvent e) {
2067 select(e.row);
2068 e.result = ACC.OK;
2069 }
2070 @Override
2071 public void setSelectedColumn(AccessibleTableEvent e) {
2072 /* CTable does not support column selection. */
2073 }
2074 @Override
2075 public void setSelectedRow(AccessibleTableEvent e) {
2076 setSelection(e.row);
2077 e.result = ACC.OK;
2078 }
2079 });
2080 }
2081
2082 static void initImages (final Display display) {
2083 PaletteData arrowPalette = new PaletteData (new RGB (0, 0, 0), new RGB (255, 255, 255));
2084 if (display.getData (ID_ARROWDOWN) == null) {
2085 ImageData arrowDown = new ImageData (
2086 7, 4, 1,
2087 arrowPalette, 1,
2088 new byte[] {0x00, (byte)0x83, (byte)0xC7, (byte)0xEF});
2089 arrowDown.transparentPixel = 0x1; /* use white for transparency */
2090 display.setData (ID_ARROWDOWN, new Image (display, arrowDown));
2091 }
2092 if (display.getData (ID_ARROWUP) == null) {
2093 ImageData arrowUp = new ImageData (
2094 7, 4, 1,
2095 arrowPalette, 1,
2096 new byte[] {(byte)0xEF, (byte)0xC7, (byte)0x83, 0x00});
2097 arrowUp.transparentPixel = 0x1; /* use white for transparency */
2098 display.setData (ID_ARROWUP, new Image (display, arrowUp));
2099 }
2100
2101 PaletteData checkMarkPalette = new PaletteData (new RGB (0, 0, 0), new RGB (252, 3, 251));
2102 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};
2103 ImageData checkmark = new ImageData (7, 7, 1, checkMarkPalette, 1, new byte[] {-4, -8, 112, 34, 6, -114, -34});
2104 checkmark.transparentPixel = 1;
2105 if (display.getData (ID_CHECKMARK) == null) {
2106 display.setData (ID_CHECKMARK, new Image (display, checkmark));
2107 }
2108
2109 if (display.getData (ID_UNCHECKED) == null) {
2110 PaletteData uncheckedPalette = new PaletteData (new RGB (128, 128, 128), new RGB (255, 255, 255));
2111 ImageData unchecked = new ImageData (11, 11, 1, uncheckedPalette, 2, checkbox);
2112 display.setData (ID_UNCHECKED, new Image (display, unchecked));
2113 }
2114
2115 if (display.getData (ID_GRAYUNCHECKED) == null) {
2116 PaletteData grayUncheckedPalette = new PaletteData (new RGB (128, 128, 128), new RGB (192, 192, 192));
2117 ImageData grayUnchecked = new ImageData (11, 11, 1, grayUncheckedPalette, 2, checkbox);
2118 display.setData (ID_GRAYUNCHECKED, new Image (display, grayUnchecked));
2119 }
2120
2121 display.disposeExec (() -> {
2122 Image unchecked = (Image) display.getData (ID_UNCHECKED);
2123 if (unchecked != null) unchecked.dispose ();
2124 Image grayUnchecked = (Image) display.getData (ID_GRAYUNCHECKED);
2125 if (grayUnchecked != null) grayUnchecked.dispose ();
2126 Image checkmark1 = (Image) display.getData (ID_CHECKMARK);
2127 if (checkmark1 != null) checkmark1.dispose ();
2128 Image arrowDown = (Image) display.getData (ID_ARROWDOWN);
2129 if (arrowDown != null) arrowDown.dispose ();
2130 Image arrowUp = (Image) display.getData (ID_ARROWUP);
2131 if (arrowUp != null) arrowUp.dispose ();
2132
2133 display.setData (ID_UNCHECKED, null);
2134 display.setData (ID_GRAYUNCHECKED, null);
2135 display.setData (ID_CHECKMARK, null);
2136 display.setData (ID_ARROWDOWN, null);
2137 display.setData (ID_ARROWUP, null);
2138 });
2139 }
2140 /**
2141 * Returns <code>true</code> if the item is selected,
2142 * and <code>false</code> otherwise. Indices out of
2143 * range are ignored.
2144 *
2145 * @param index the index of the item
2146 * @return the selection state of the item at the index
2147 *
2148 * @exception SWTException <ul>
2149 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
2150 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
2151 * </ul>
2152 */
2153 public boolean isSelected (int index) {
2154 checkWidget ();
2155 if (!(0 <= index && index < itemsCount)) return false;
2156 return items [index].isSelected ();
2157 }
2158 @Override
2159 public void notifyListeners (int eventType, Event event) {
2160 super.notifyListeners(eventType, event);
2161 if (eventType == SWT.Selection && event.detail != SWT.CHECK) getAccessible().selectionChanged();
2162 }
2163 void onArrowDown (int stateMask) {
2164 if ((stateMask & (SWT.SHIFT | SWT.CTRL)) == 0) {
2165 /* Down Arrow with no modifiers */
2166 int newFocusIndex = focusItem.index + 1;
2167 if (newFocusIndex == itemsCount) return; /* at bottom */
2168 selectItem (items [newFocusIndex], false);
2169 setFocusItem (items [newFocusIndex], true);
2170 redrawItem (newFocusIndex, true);
2171 showItem (items [newFocusIndex]);
2172 Event newEvent = new Event ();
2173 newEvent.item = items [newFocusIndex];
2174 notifyListeners (SWT.Selection, newEvent);
2175 return;
2176 }
2177 if ((getStyle () & SWT.SINGLE) != 0) {
2178 if ((stateMask & SWT.CTRL) != 0) {
2179 /* CTRL+Down Arrow, CTRL+Shift+Down Arrow */
2180 int visibleItemCount = (clientArea.height - getHeaderHeight ()) / itemHeight;
2181 if (itemsCount <= topIndex + visibleItemCount) return; /* at bottom */
2182 update ();
2183 topIndex++;
2184 ScrollBar vBar = getVerticalBar ();
2185 if (vBar != null) vBar.setSelection (topIndex);
2186 GC gc = new GC (this);
2187 gc.copyArea (
2188 0, 0,
2189 clientArea.width, clientArea.height,
2190 0, -itemHeight);
2191 gc.dispose ();
2192 return;
2193 }
2194 /* Shift+Down Arrow */
2195 int newFocusIndex = focusItem.index + 1;
2196 if (newFocusIndex == itemsCount) return; /* at bottom */
2197 selectItem (items [newFocusIndex], false);
2198 setFocusItem (items [newFocusIndex], true);
2199 redrawItem (newFocusIndex, true);
2200 showItem (items [newFocusIndex]);
2201 Event newEvent = new Event ();
2202 newEvent.item = items [newFocusIndex];
2203 notifyListeners (SWT.Selection, newEvent);
2204 return;
2205 }
2206 /* SWT.MULTI */
2207 if ((stateMask & SWT.CTRL) != 0) {
2208 if ((stateMask & SWT.SHIFT) != 0) {
2209 /* CTRL+Shift+Down Arrow */
2210 int visibleItemCount = (clientArea.height - getHeaderHeight ()) / itemHeight;
2211 if (itemsCount <= topIndex + visibleItemCount) return; /* at bottom */
2212 update ();
2213 topIndex++;
2214 ScrollBar vBar = getVerticalBar ();
2215 if (vBar != null) vBar.setSelection (topIndex);
2216 GC gc = new GC (this);
2217 gc.copyArea (
2218 0, 0,
2219 clientArea.width, clientArea.height,
2220 0, -itemHeight);
2221 gc.dispose ();
2222 return;
2223 }
2224 /* CTRL+Down Arrow */
2225 int focusIndex = focusItem.index;
2226 if (focusIndex == itemsCount - 1) return; /* at bottom */
2227 CTableItem newFocusItem = items [focusIndex + 1];
2228 setFocusItem (newFocusItem, true);
2229 redrawItem (newFocusItem.index, true);
2230 showItem (newFocusItem);
2231 return;
2232 }
2233 /* Shift+Down Arrow */
2234 int newFocusIndex = focusItem.index + 1;
2235 if (newFocusIndex == itemsCount) return; /* at bottom */
2236 if (anchorItem == null) anchorItem = focusItem;
2237 if (focusItem.index < anchorItem.index) {
2238 deselectItem (focusItem);
2239 redrawItem (focusItem.index, true);
2240 }
2241 selectItem (items [newFocusIndex], true);
2242 setFocusItem (items [newFocusIndex], true);
2243 redrawItem (newFocusIndex, true);
2244 showItem (items [newFocusIndex]);
2245 Event newEvent = new Event ();
2246 newEvent.item = items [newFocusIndex];
2247 notifyListeners (SWT.Selection, newEvent);
2248 }
2249 void onArrowLeft (int stateMask) {
2250 if (horizontalOffset == 0) return;
2251 int newSelection = Math.max (0, horizontalOffset - SIZE_HORIZONTALSCROLL);
2252 update ();
2253 GC gc = new GC (this);
2254 gc.copyArea (
2255 0, 0,
2256 clientArea.width, clientArea.height,
2257 horizontalOffset - newSelection, 0);
2258 gc.dispose ();
2259 if (header.getVisible ()) {
2260 header.update ();
2261 Rectangle headerClientArea = header.getClientArea ();
2262 gc = new GC (header);
2263 gc.copyArea (
2264 0, 0,
2265 headerClientArea.width, headerClientArea.height,
2266 horizontalOffset - newSelection, 0);
2267 gc.dispose();
2268 }
2269 horizontalOffset = newSelection;
2270 ScrollBar hBar = getHorizontalBar ();
2271 if (hBar != null) hBar.setSelection (horizontalOffset);
2272 }
2273 void onArrowRight (int stateMask) {
2274 ScrollBar hBar = getHorizontalBar ();
2275 if (hBar == null) return;
2276 int maximum = hBar.getMaximum ();
2277 int clientWidth = clientArea.width;
2278 if ((horizontalOffset + clientArea.width) == maximum) return;
2279 if (maximum <= clientWidth) return;
2280 int newSelection = Math.min (horizontalOffset + SIZE_HORIZONTALSCROLL, maximum - clientWidth);
2281 update ();
2282 GC gc = new GC (this);
2283 gc.copyArea (
2284 0, 0,
2285 clientArea.width, clientArea.height,
2286 horizontalOffset - newSelection, 0);
2287 gc.dispose ();
2288 if (header.getVisible ()) {
2289 Rectangle headerClientArea = header.getClientArea ();
2290 header.update ();
2291 gc = new GC (header);
2292 gc.copyArea (
2293 0, 0,
2294 headerClientArea.width, headerClientArea.height,
2295 horizontalOffset - newSelection, 0);
2296 gc.dispose();
2297 }
2298 horizontalOffset = newSelection;
2299 hBar.setSelection (horizontalOffset);
2300 }
2301 void onArrowUp (int stateMask) {
2302 if ((stateMask & (SWT.SHIFT | SWT.CTRL)) == 0) {
2303 /* Up Arrow with no modifiers */
2304 int newFocusIndex = focusItem.index - 1;
2305 if (newFocusIndex < 0) return; /* at top */
2306 CTableItem item = items [newFocusIndex];
2307 selectItem (item, false);
2308 setFocusItem (item, true);
2309 redrawItem (newFocusIndex, true);
2310 showItem (item);
2311 Event newEvent = new Event ();
2312 newEvent.item = item;
2313 notifyListeners (SWT.Selection, newEvent);
2314 return;
2315 }
2316 if ((getStyle () & SWT.SINGLE) != 0) {
2317 if ((stateMask & SWT.CTRL) != 0) {
2318 /* CTRL+Up Arrow, CTRL+Shift+Up Arrow */
2319 if (topIndex == 0) return; /* at top */
2320 update ();
2321 topIndex--;
2322 ScrollBar vBar = getVerticalBar ();
2323 if (vBar != null) vBar.setSelection (topIndex);
2324 GC gc = new GC (this);
2325 gc.copyArea (
2326 0, 0,
2327 clientArea.width, clientArea.height,
2328 0, itemHeight);
2329 gc.dispose ();
2330 return;
2331 }
2332 /* Shift+Up Arrow */
2333 int newFocusIndex = focusItem.index - 1;
2334 if (newFocusIndex < 0) return; /* at top */
2335 CTableItem item = items [newFocusIndex];
2336 selectItem (item, false);
2337 setFocusItem (item, true);
2338 redrawItem (newFocusIndex, true);
2339 showItem (item);
2340 Event newEvent = new Event ();
2341 newEvent.item = item;
2342 notifyListeners (SWT.Selection, newEvent);
2343 return;
2344 }
2345 /* SWT.MULTI */
2346 if ((stateMask & SWT.CTRL) != 0) {
2347 if ((stateMask & SWT.SHIFT) != 0) {
2348 /* CTRL+Shift+Up Arrow */
2349 if (topIndex == 0) return; /* at top */
2350 update ();
2351 topIndex--;
2352 ScrollBar vBar = getVerticalBar ();
2353 if (vBar != null) vBar.setSelection (topIndex);
2354 GC gc = new GC (this);
2355 gc.copyArea (
2356 0, 0,
2357 clientArea.width, clientArea.height,
2358 0, itemHeight);
2359 gc.dispose ();
2360 return;
2361 }
2362 /* CTRL+Up Arrow */
2363 int focusIndex = focusItem.index;
2364 if (focusIndex == 0) return; /* at top */
2365 CTableItem newFocusItem = items [focusIndex - 1];
2366 setFocusItem (newFocusItem, true);
2367 showItem (newFocusItem);
2368 redrawItem (newFocusItem.index, true);
2369 return;
2370 }
2371 /* Shift+Up Arrow */
2372 int newFocusIndex = focusItem.index - 1;
2373 if (newFocusIndex < 0) return; /* at top */
2374 if (anchorItem == null) anchorItem = focusItem;
2375 if (anchorItem.index < focusItem.index) {
2376 deselectItem (focusItem);
2377 redrawItem (focusItem.index, true);
2378 }
2379 CTableItem item = items [newFocusIndex];
2380 selectItem (item, true);
2381 setFocusItem (item, true);
2382 redrawItem (newFocusIndex, true);
2383 showItem (item);
2384 Event newEvent = new Event ();
2385 newEvent.item = item;
2386 notifyListeners (SWT.Selection, newEvent);
2387 }
2388 void onCR () {
2389 if (focusItem == null) return;
2390 Event event = new Event ();
2391 event.item = focusItem;
2392 notifyListeners (SWT.DefaultSelection, event);
2393 }
2394 void onDispose (Event event) {
2395 if (isDisposed ()) return;
2396 if (ignoreDispose) return;
2397 ignoreDispose = true;
2398 notifyListeners(SWT.Dispose, event);
2399 event.type = SWT.None;
2400 for (int i = 0; i < itemsCount; i++) {
2401 items [i].dispose (false);
2402 }
2403 for (CTableColumn column : columns) {
2404 column.dispose (false);
2405 }
2406 if (toolTipShell != null) {
2407 toolTipShell.dispose ();
2408 toolTipShell = null;
2409 toolTipLabel = null;
2410 }
2411 toolTipListener = null;
2412 itemsCount = topIndex = horizontalOffset = 0;
2413 items = selectedItems = null;
2414 columns = orderedColumns = null;
2415 focusItem = anchorItem = lastClickedItem = null;
2416 lastSelectionEvent = null;
2417 header = null;
2418 resizeColumn = sortColumn = null;
2419 }
2420 void onEnd (int stateMask) {
2421 int lastAvailableIndex = itemsCount - 1;
2422 if ((stateMask & (SWT.CTRL | SWT.SHIFT)) == 0) {
2423 /* End with no modifiers */
2424 if (focusItem.index == lastAvailableIndex) return; /* at bottom */
2425 CTableItem item = items [lastAvailableIndex];
2426 selectItem (item, false);
2427 setFocusItem (item, true);
2428 redrawItem (lastAvailableIndex, true);
2429 showItem (item);
2430 Event newEvent = new Event ();
2431 newEvent.item = item;
2432 notifyListeners (SWT.Selection, newEvent);
2433 return;
2434 }
2435 if ((getStyle () & SWT.SINGLE) != 0) {
2436 if ((stateMask & SWT.CTRL) != 0) {
2437 /* CTRL+End, CTRL+Shift+End */
2438 int visibleItemCount = (clientArea.height - getHeaderHeight ()) / itemHeight;
2439 setTopIndex (itemsCount - visibleItemCount);
2440 return;
2441 }
2442 /* Shift+End */
2443 if (focusItem.index == lastAvailableIndex) return; /* at bottom */
2444 CTableItem item = items [lastAvailableIndex];
2445 selectItem (item, false);
2446 setFocusItem (item, true);
2447 redrawItem (lastAvailableIndex, true);
2448 showItem (item);
2449 Event newEvent = new Event ();
2450 newEvent.item = item;
2451 notifyListeners (SWT.Selection, newEvent);
2452 return;
2453 }
2454 /* SWT.MULTI */
2455 if ((stateMask & SWT.CTRL) != 0) {
2456 if ((stateMask & SWT.SHIFT) != 0) {
2457 /* CTRL+Shift+End */
2458 showItem (items [lastAvailableIndex]);
2459 return;
2460 }
2461 /* CTRL+End */
2462 if (focusItem.index == lastAvailableIndex) return; /* at bottom */
2463 CTableItem item = items [lastAvailableIndex];
2464 setFocusItem (item, true);
2465 showItem (item);
2466 redrawItem (item.index, true);
2467 return;
2468 }
2469 /* Shift+End */
2470 if (anchorItem == null) anchorItem = focusItem;
2471 CTableItem selectedItem = items [lastAvailableIndex];
2472 if (selectedItem == focusItem && selectedItem.isSelected ()) return;
2473 int anchorIndex = anchorItem.index;
2474 int selectIndex = selectedItem.index;
2475 CTableItem[] newSelection = new CTableItem [selectIndex - anchorIndex + 1];
2476 int writeIndex = 0;
2477 for (int i = anchorIndex; i <= selectIndex; i++) {
2478 newSelection [writeIndex++] = items [i];
2479 }
2480 setSelection (newSelection, false);
2481 setFocusItem (selectedItem, true);
2482 redrawItems (anchorIndex, selectIndex, true);
2483 showItem (selectedItem);
2484 Event newEvent = new Event ();
2485 newEvent.item = selectedItem;
2486 notifyListeners (SWT.Selection, newEvent);
2487 }
2488 void onFocusIn () {
2489 hasFocus = true;
2490 if (itemsCount == 0) {
2491 redraw ();
2492 return;
2493 }
2494 if ((getStyle () & (SWT.HIDE_SELECTION | SWT.MULTI)) == (SWT.HIDE_SELECTION | SWT.MULTI)) {
2495 for (CTableItem selectedItem : selectedItems) {
2496 redrawItem (selectedItem.index, true);
2497 }
2498 }
2499 if (focusItem != null) {
2500 redrawItem (focusItem.index, true);
2501 return;
2502 }
2503 /* an initial focus item must be selected */
2504 CTableItem initialFocus;
2505 if (selectedItems.length > 0) {
2506 initialFocus = selectedItems [0];
2507 } else {
2508 initialFocus = items [topIndex];
2509 }
2510 setFocusItem (initialFocus, false);
2511 redrawItem (initialFocus.index, true);
2512 return;
2513 }
2514 void onFocusOut () {
2515 hasFocus = false;
2516 if (itemsCount == 0) {
2517 redraw ();
2518 return;
2519 }
2520 if (focusItem != null) {
2521 redrawItem (focusItem.index, true);
2522 }
2523 if ((getStyle () & (SWT.HIDE_SELECTION | SWT.MULTI)) == (SWT.HIDE_SELECTION | SWT.MULTI)) {
2524 for (CTableItem selectedItem : selectedItems) {
2525 redrawItem (selectedItem.index, true);
2526 }
2527 }
2528 }
2529 void onHome (int stateMask) {
2530 if ((stateMask & (SWT.CTRL | SWT.SHIFT)) == 0) {
2531 /* Home with no modifiers */
2532 if (focusItem.index == 0) return; /* at top */
2533 CTableItem item = items [0];
2534 selectItem (item, false);
2535 setFocusItem (item, true);
2536 redrawItem (0, true);
2537 showItem (item);
2538 Event newEvent = new Event ();
2539 newEvent.item = item;
2540 notifyListeners (SWT.Selection, newEvent);
2541 return;
2542 }
2543 if ((getStyle () & SWT.SINGLE) != 0) {
2544 if ((stateMask & SWT.CTRL) != 0) {
2545 /* CTRL+Home, CTRL+Shift+Home */
2546 setTopIndex (0);
2547 return;
2548 }
2549 /* Shift+Home */
2550 if (focusItem.index == 0) return; /* at top */
2551 CTableItem item = items [0];
2552 selectItem (item, false);
2553 setFocusItem (item, true);
2554 redrawItem (0, true);
2555 showItem (item);
2556 Event newEvent = new Event ();
2557 newEvent.item = item;
2558 notifyListeners (SWT.Selection, newEvent);
2559 return;
2560 }
2561 /* SWT.MULTI */
2562 if ((stateMask & SWT.CTRL) != 0) {
2563 if ((stateMask & SWT.SHIFT) != 0) {
2564 /* CTRL+Shift+Home */
2565 setTopIndex (0);
2566 return;
2567 }
2568 /* CTRL+Home */
2569 if (focusItem.index == 0) return; /* at top */
2570 CTableItem item = items [0];
2571 setFocusItem (item, true);
2572 showItem (item);
2573 redrawItem (item.index, true);
2574 return;
2575 }
2576 /* Shift+Home */
2577 if (anchorItem == null) anchorItem = focusItem;
2578 CTableItem selectedItem = items [0];
2579 if (selectedItem == focusItem && selectedItem.isSelected ()) return;
2580 int anchorIndex = anchorItem.index;
2581 int selectIndex = selectedItem.index;
2582 CTableItem[] newSelection = new CTableItem [anchorIndex + 1];
2583 int writeIndex = 0;
2584 for (int i = anchorIndex; i >= 0; i--) {
2585 newSelection [writeIndex++] = items [i];
2586 }
2587 setSelection (newSelection, false);
2588 setFocusItem (selectedItem, true);
2589 redrawItems (anchorIndex, selectIndex, true);
2590 showItem (selectedItem);
2591 Event newEvent = new Event ();
2592 newEvent.item = selectedItem;
2593 notifyListeners (SWT.Selection, newEvent);
2594 }
2595 void onKeyDown (Event event) {
2596 if (ignoreKey) {
2597 ignoreKey = false;
2598 return;
2599 }
2600 ignoreKey = true;
2601 notifyListeners (event.type, event);
2602 event.type = SWT.None;
2603 if (!event.doit) return;
2604 if (focusItem == null) return;
2605 if ((event.stateMask & SWT.SHIFT) == 0 && event.keyCode != SWT.SHIFT) {
2606 anchorItem = null;
2607 }
2608 switch (event.keyCode) {
2609 case SWT.ARROW_UP:
2610 onArrowUp (event.stateMask);
2611 return;
2612 case SWT.ARROW_DOWN:
2613 onArrowDown (event.stateMask);
2614 return;
2615 case SWT.ARROW_LEFT:
2616 onArrowLeft (event.stateMask);
2617 return;
2618 case SWT.ARROW_RIGHT:
2619 onArrowRight (event.stateMask);
2620 return;
2621 case SWT.PAGE_UP:
2622 onPageUp (event.stateMask);
2623 return;
2624 case SWT.PAGE_DOWN:
2625 onPageDown (event.stateMask);
2626 return;
2627 case SWT.HOME:
2628 onHome (event.stateMask);
2629 return;
2630 case SWT.END:
2631 onEnd (event.stateMask);
2632 return;
2633 }
2634 if (event.character == ' ') {
2635 onSpace ();
2636 return;
2637 }
2638 if (event.character == SWT.CR) {
2639 onCR ();
2640 return;
2641 }
2642 if ((event.stateMask & SWT.CTRL) != 0) return;
2643
2644 int initialIndex = focusItem.index;
2645 char character = Character.toLowerCase (event.character);
2646 /* check available items from current focus item to bottom */
2647 for (int i = initialIndex + 1; i < itemsCount; i++) {
2648 CTableItem item = items [i];
2649 String text = item.getText (0, false);
2650 if (text.length () > 0) {
2651 if (Character.toLowerCase (text.charAt (0)) == character) {
2652 selectItem (item, false);
2653 setFocusItem (item, true);
2654 redrawItem (i, true);
2655 showItem (item);
2656 Event newEvent = new Event ();
2657 newEvent.item = item;
2658 notifyListeners (SWT.Selection, newEvent);
2659 return;
2660 }
2661 }
2662 }
2663 /* check available items from top to current focus item */
2664 for (int i = 0; i < initialIndex; i++) {
2665 CTableItem item = items [i];
2666 String text = item.getText (0, false);
2667 if (text.length () > 0) {
2668 if (Character.toLowerCase (text.charAt (0)) == character) {
2669 selectItem (item, false);
2670 setFocusItem (item, true);
2671 redrawItem (i, true);
2672 showItem (item);
2673 Event newEvent = new Event ();
2674 newEvent.item = item;
2675 notifyListeners (SWT.Selection, newEvent);
2676 return;
2677 }
2678 }
2679 }
2680 }
2681 void onMouseDoubleClick (Event event) {
2682 if (!isFocusControl ()) setFocus ();
2683 int index = (event.y - getHeaderHeight ()) / itemHeight + topIndex;
2684 if (!(0 <= index && index < itemsCount)) return; /* not on an available item */
2685 CTableItem selectedItem = items [index];
2686
2687 /*
2688 * If the two clicks of the double click did not occur over the same item then do not
2689 * consider this to be a default selection.
2690 */
2691 if (selectedItem != lastClickedItem) return;
2692
2693 if (!selectedItem.getHitBounds ().contains (event.x, event.y)) return; /* considers x */
2694
2695 Event newEvent = new Event ();
2696 newEvent.item = selectedItem;
2697 notifyListeners (SWT.DefaultSelection, newEvent);
2698 }
2699 void onMouseDown (Event event) {
2700 if (!isFocusControl ()) forceFocus ();
2701 int index = (event.y - getHeaderHeight ()) / itemHeight + topIndex;
2702 if (!(0 <= index && index < itemsCount)) return; /* not on an available item */
2703 CTableItem selectedItem = items [index];
2704
2705 /* if click was in checkbox */
2706 if ((getStyle () & SWT.CHECK) != 0 && selectedItem.getCheckboxBounds ().contains (event.x, event.y)) {
2707 if (event.button != 1) return;
2708 selectedItem.setChecked (!selectedItem.checked);
2709 Event newEvent = new Event ();
2710 newEvent.item = selectedItem;
2711 newEvent.detail = SWT.CHECK;
2712 notifyListeners (SWT.Selection, newEvent);
2713 return;
2714 }
2715
2716 if (!selectedItem.getHitBounds ().contains (event.x, event.y)) return;
2717
2718 if ((event.stateMask & SWT.SHIFT) == 0 && event.keyCode != SWT.SHIFT) anchorItem = null;
2719
2720 boolean sendSelection = true;
2721 /* Detect when this is the second click of a DefaultSelection and don't fire Selection */
2722 if (lastSelectionEvent != null && lastSelectionEvent.item == selectedItem) {
2723 if (event.time - lastSelectionEvent.time <= display.getDoubleClickTime ()) {
2724 sendSelection = false;
2725 } else {
2726 lastSelectionEvent = event;
2727 event.item = selectedItem;
2728 }
2729 } else {
2730 lastSelectionEvent = event;
2731 event.item = selectedItem;
2732 }
2733
2734 if ((getStyle () & SWT.SINGLE) != 0) {
2735 if (!selectedItem.isSelected ()) {
2736 if (event.button == 1) {
2737 selectItem (selectedItem, false);
2738 setFocusItem (selectedItem, true);
2739 redrawItem (selectedItem.index, true);
2740 if (sendSelection) {
2741 Event newEvent = new Event ();
2742 newEvent.item = selectedItem;
2743 notifyListeners (SWT.Selection, newEvent);
2744 }
2745 return;
2746 }
2747 if ((event.stateMask & (SWT.CTRL | SWT.SHIFT)) == 0) {
2748 selectItem (selectedItem, false);
2749 setFocusItem (selectedItem, true);
2750 redrawItem (selectedItem.index, true);
2751 if (sendSelection) {
2752 Event newEvent = new Event ();
2753 newEvent.item = selectedItem;
2754 notifyListeners (SWT.Selection, newEvent);
2755 }
2756 return;
2757 }
2758 }
2759 /* item is selected */
2760 if (event.button == 1) {
2761 /* fire a selection event, though the selection did not change */
2762 if (sendSelection) {
2763 Event newEvent = new Event ();
2764 newEvent.item = selectedItem;
2765 notifyListeners (SWT.Selection, newEvent);
2766 }
2767 return;
2768 }
2769 }
2770 /* SWT.MULTI */
2771 if (!selectedItem.isSelected ()) {
2772 if (event.button == 1) {
2773 if ((event.stateMask & (SWT.CTRL | SWT.SHIFT)) == SWT.SHIFT) {
2774 if (anchorItem == null) anchorItem = focusItem;
2775 int anchorIndex = anchorItem.index;
2776 int selectIndex = selectedItem.index;
2777 CTableItem[] newSelection = new CTableItem [Math.abs (anchorIndex - selectIndex) + 1];
2778 int step = anchorIndex < selectIndex ? 1 : -1;
2779 int writeIndex = 0;
2780 for (int i = anchorIndex; i != selectIndex; i += step) {
2781 newSelection [writeIndex++] = items [i];
2782 }
2783 newSelection [writeIndex] = items [selectIndex];
2784 setSelection (newSelection, false);
2785 setFocusItem (selectedItem, true);
2786 redrawItems (
2787 Math.min (anchorIndex, selectIndex),
2788 Math.max (anchorIndex, selectIndex),
2789 true);
2790 if (sendSelection) {
2791 Event newEvent = new Event ();
2792 newEvent.item = selectedItem;
2793 notifyListeners (SWT.Selection, newEvent);
2794 }
2795 return;
2796 }
2797 selectItem (selectedItem, (event.stateMask & SWT.CTRL) != 0);
2798 setFocusItem (selectedItem, true);
2799 redrawItem (selectedItem.index, true);
2800 if (sendSelection) {
2801 Event newEvent = new Event ();
2802 newEvent.item = selectedItem;
2803 notifyListeners (SWT.Selection, newEvent);
2804 }
2805 return;
2806 }
2807 /* button 3 */
2808 if ((event.stateMask & (SWT.CTRL | SWT.SHIFT)) == 0) {
2809 selectItem (selectedItem, false);
2810 setFocusItem (selectedItem, true);
2811 redrawItem (selectedItem.index, true);
2812 if (sendSelection) {
2813 Event newEvent = new Event ();
2814 newEvent.item = selectedItem;
2815 notifyListeners (SWT.Selection, newEvent);
2816 }
2817 return;
2818 }
2819 }
2820 /* item is selected */
2821 if (event.button != 1) return;
2822 if ((event.stateMask & SWT.CTRL) != 0) {
2823 removeSelectedItem (getSelectionIndex (selectedItem));
2824 setFocusItem (selectedItem, true);
2825 redrawItem (selectedItem.index, true);
2826 if (sendSelection) {
2827 Event newEvent = new Event ();
2828 newEvent.item = selectedItem;
2829 notifyListeners (SWT.Selection, newEvent);
2830 }
2831 return;
2832 }
2833 if ((event.stateMask & SWT.SHIFT) != 0) {
2834 if (anchorItem == null) anchorItem = focusItem;
2835 int anchorIndex = anchorItem.index;
2836 int selectIndex = selectedItem.index;
2837 CTableItem[] newSelection = new CTableItem [Math.abs (anchorIndex - selectIndex) + 1];
2838 int step = anchorIndex < selectIndex ? 1 : -1;
2839 int writeIndex = 0;
2840 for (int i = anchorIndex; i != selectIndex; i += step) {
2841 newSelection [writeIndex++] = items [i];
2842 }
2843 newSelection [writeIndex] = items [selectIndex];
2844 setSelection (newSelection, false);
2845 setFocusItem (selectedItem, true);
2846 redrawItems (
2847 Math.min (anchorIndex, selectIndex),
2848 Math.max (anchorIndex, selectIndex),
2849 true);
2850 if (sendSelection) {
2851 Event newEvent = new Event ();
2852 newEvent.item = selectedItem;
2853 notifyListeners (SWT.Selection, newEvent);
2854 }
2855 return;
2856 }
2857 selectItem (selectedItem, false);
2858 setFocusItem (selectedItem, true);
2859 redrawItem (selectedItem.index, true);
2860 if (sendSelection) {
2861 Event newEvent = new Event ();
2862 newEvent.item = selectedItem;
2863 notifyListeners (SWT.Selection, newEvent);
2864 }
2865 }
2866 void onMouseUp (Event event) {
2867 int index = (event.y - getHeaderHeight ()) / itemHeight + topIndex;
2868 if (!(0 <= index && index < itemsCount)) return; /* not on an available item */
2869 lastClickedItem = items [index];
2870 }
2871 void onPageDown (int stateMask) {
2872 int visibleItemCount = (clientArea.height - getHeaderHeight ()) / itemHeight;
2873 if ((stateMask & (SWT.CTRL | SWT.SHIFT)) == 0) {
2874 /* PageDown with no modifiers */
2875 int newFocusIndex = focusItem.index + visibleItemCount - 1;
2876 newFocusIndex = Math.min (newFocusIndex, itemsCount - 1);
2877 if (newFocusIndex == focusItem.index) return;
2878 CTableItem item = items [newFocusIndex];
2879 selectItem (item, false);
2880 setFocusItem (item, true);
2881 showItem (item);
2882 redrawItem (item.index, true);
2883 return;
2884 }
2885 if ((stateMask & (SWT.CTRL | SWT.SHIFT)) == (SWT.CTRL | SWT.SHIFT)) {
2886 /* CTRL+Shift+PageDown */
2887 int newTopIndex = topIndex + visibleItemCount;
2888 newTopIndex = Math.min (newTopIndex, itemsCount - visibleItemCount);
2889 if (newTopIndex == topIndex) return;
2890 setTopIndex (newTopIndex);
2891 return;
2892 }
2893 if ((getStyle () & SWT.SINGLE) != 0) {
2894 if ((stateMask & SWT.SHIFT) != 0) {
2895 /* Shift+PageDown */
2896 int newFocusIndex = focusItem.index + visibleItemCount - 1;
2897 newFocusIndex = Math.min (newFocusIndex, itemsCount - 1);
2898 if (newFocusIndex == focusItem.index) return;
2899 CTableItem item = items [newFocusIndex];
2900 selectItem (item, false);
2901 setFocusItem (item, true);
2902 showItem (item);
2903 redrawItem (item.index, true);
2904 return;
2905 }
2906 /* CTRL+PageDown */
2907 int newTopIndex = topIndex + visibleItemCount;
2908 newTopIndex = Math.min (newTopIndex, itemsCount - visibleItemCount);
2909 if (newTopIndex == topIndex) return;
2910 setTopIndex (newTopIndex);
2911 return;
2912 }
2913 /* SWT.MULTI */
2914 if ((stateMask & SWT.CTRL) != 0) {
2915 /* CTRL+PageDown */
2916 int bottomIndex = Math.min (topIndex + visibleItemCount - 1, itemsCount - 1);
2917 if (focusItem.index != bottomIndex) {
2918 /* move focus to bottom item in viewport */
2919 setFocusItem (items [bottomIndex], true);
2920 redrawItem (bottomIndex, true);
2921 } else {
2922 /* at bottom of viewport, so set focus to bottom item one page down */
2923 int newFocusIndex = Math.min (itemsCount - 1, bottomIndex + visibleItemCount);
2924 if (newFocusIndex == focusItem.index) return;
2925 setFocusItem (items [newFocusIndex], true);
2926 showItem (items [newFocusIndex]);
2927 redrawItem (newFocusIndex, true);
2928 }
2929 return;
2930 }
2931 /* Shift+PageDown */
2932 if (anchorItem == null) anchorItem = focusItem;
2933 int anchorIndex = anchorItem.index;
2934 int bottomIndex = Math.min (topIndex + visibleItemCount - 1, itemsCount - 1);
2935 int selectIndex;
2936 if (focusItem.index != bottomIndex) {
2937 /* select from focus to bottom item in viewport */
2938 selectIndex = bottomIndex;
2939 } else {
2940 /* already at bottom of viewport, so select to bottom of one page down */
2941 selectIndex = Math.min (itemsCount - 1, bottomIndex + visibleItemCount);
2942 if (selectIndex == focusItem.index && focusItem.isSelected ()) return;
2943 }
2944 CTableItem selectedItem = items [selectIndex];
2945 CTableItem[] newSelection = new CTableItem [Math.abs (anchorIndex - selectIndex) + 1];
2946 int step = anchorIndex < selectIndex ? 1 : -1;
2947 int writeIndex = 0;
2948 for (int i = anchorIndex; i != selectIndex; i += step) {
2949 newSelection [writeIndex++] = items [i];
2950 }
2951 newSelection [writeIndex] = items [selectIndex];
2952 setSelection (newSelection, false);
2953 setFocusItem (selectedItem, true);
2954 showItem (selectedItem);
2955 Event newEvent = new Event ();
2956 newEvent.item = selectedItem;
2957 notifyListeners (SWT.Selection, newEvent);
2958 }
2959 void onPageUp (int stateMask) {
2960 int visibleItemCount = (clientArea.height - getHeaderHeight ()) / itemHeight;
2961 if ((stateMask & (SWT.CTRL | SWT.SHIFT)) == 0) {
2962 /* PageUp with no modifiers */
2963 int newFocusIndex = Math.max (0, focusItem.index - visibleItemCount + 1);
2964 if (newFocusIndex == focusItem.index) return;
2965 CTableItem item = items [newFocusIndex];
2966 selectItem (item, false);
2967 setFocusItem (item, true);
2968 showItem (item);
2969 redrawItem (item.index, true);
2970 return;
2971 }
2972 if ((stateMask & (SWT.CTRL | SWT.SHIFT)) == (SWT.CTRL | SWT.SHIFT)) {
2973 /* CTRL+Shift+PageUp */
2974 int newTopIndex = Math.max (0, topIndex - visibleItemCount);
2975 if (newTopIndex == topIndex) return;
2976 setTopIndex (newTopIndex);
2977 return;
2978 }
2979 if ((getStyle () & SWT.SINGLE) != 0) {
2980 if ((stateMask & SWT.SHIFT) != 0) {
2981 /* Shift+PageUp */
2982 int newFocusIndex = Math.max (0, focusItem.index - visibleItemCount + 1);
2983 if (newFocusIndex == focusItem.index) return;
2984 CTableItem item = items [newFocusIndex];
2985 selectItem (item, false);
2986 setFocusItem (item, true);
2987 showItem (item);
2988 redrawItem (item.index, true);
2989 return;
2990 }
2991 /* CTRL+PageUp */
2992 int newTopIndex = Math.max (0, topIndex - visibleItemCount);
2993 if (newTopIndex == topIndex) return;
2994 setTopIndex (newTopIndex);
2995 return;
2996 }
2997 /* SWT.MULTI */
2998 if ((stateMask & SWT.CTRL) != 0) {
2999 /* CTRL+PageUp */
3000 if (focusItem.index != topIndex) {
3001 /* move focus to top item in viewport */
3002 setFocusItem (items [topIndex], true);
3003 redrawItem (topIndex, true);
3004 } else {
3005 /* at top of viewport, so set focus to top item one page up */
3006 int newFocusIndex = Math.max (0, focusItem.index - visibleItemCount);
3007 if (newFocusIndex == focusItem.index) return;
3008 setFocusItem (items [newFocusIndex], true);
3009 showItem (items [newFocusIndex]);
3010 redrawItem (newFocusIndex, true);
3011 }
3012 return;
3013 }
3014 /* Shift+PageUp */
3015 if (anchorItem == null) anchorItem = focusItem;
3016 int anchorIndex = anchorItem.index;
3017 int selectIndex;
3018 if (focusItem.index != topIndex) {
3019 /* select from focus to top item in viewport */
3020 selectIndex = topIndex;
3021 } else {
3022 /* already at top of viewport, so select to top of one page up */
3023 selectIndex = Math.max (0, topIndex - visibleItemCount);
3024 if (selectIndex == focusItem.index && focusItem.isSelected ()) return;
3025 }
3026 CTableItem selectedItem = items [selectIndex];
3027 CTableItem[] newSelection = new CTableItem [Math.abs (anchorIndex - selectIndex) + 1];
3028 int step = anchorIndex < selectIndex ? 1 : -1;
3029 int writeIndex = 0;
3030 for (int i = anchorIndex; i != selectIndex; i += step) {
3031 newSelection [writeIndex++] = items [i];
3032 }
3033 newSelection [writeIndex] = items [selectIndex];
3034 setSelection (newSelection, false);
3035 setFocusItem (selectedItem, true);
3036 showItem (selectedItem);
3037 Event newEvent = new Event ();
3038 newEvent.item = selectedItem;
3039 notifyListeners (SWT.Selection, newEvent);
3040 }
3041 void onPaint (Event event) {
3042 CTableColumn[] orderedColumns = getOrderedColumns ();
3043 GC gc = event.gc;
3044 Rectangle clipping = gc.getClipping ();
3045 int headerHeight = getHeaderHeight ();
3046 int numColumns = orderedColumns.length;
3047 int startColumn = -1, endColumn = -1;
3048 if (numColumns > 0) {
3049 startColumn = computeColumnIntersect (clipping.x, 0);
3050 if (startColumn != -1) { /* the clip x is within a column's bounds */
3051 endColumn = computeColumnIntersect (clipping.x + clipping.width, startColumn);
3052 if (endColumn == -1) endColumn = numColumns - 1;
3053 }
3054 } else {
3055 startColumn = endColumn = 0;
3056 }
3057
3058 /* Determine the items to be painted */
3059 int startIndex = (clipping.y - headerHeight) / itemHeight + topIndex;
3060 int endIndex = -1;
3061 if (startIndex < itemsCount) {
3062 endIndex = startIndex + (int)Math.ceil((float)clipping.height / itemHeight);
3063 }
3064 startIndex = Math.max (0, startIndex);
3065 endIndex = Math.min (endIndex, itemsCount - 1);
3066
3067 /* fill background not handled by items */
3068 gc.setBackground (getBackground ());
3069 gc.setClipping (clipping);
3070 int bottomY = endIndex >= 0 ? getItemY (items [endIndex]) + itemHeight : 0;
3071 int fillHeight = Math.max (0, clientArea.height - bottomY);
3072 if (fillHeight > 0) { /* space below bottom item */
3073 gc.fillRectangle (0, bottomY, clientArea.width, fillHeight);
3074 //drawBackground (gc, 0, bottomY, clientArea.width, fillHeight);
3075 }
3076 if (columns.length > 0) {
3077 CTableColumn column = orderedColumns [orderedColumns.length - 1]; /* last column */
3078 int rightX = column.getX () + column.width;
3079 if (rightX < clientArea.width) {
3080 gc.fillRectangle (rightX, 0, clientArea.width - rightX, clientArea.height - fillHeight);
3081 //drawBackground (gc, rightX, 0, clientArea.width - rightX, clientArea.height - fillHeight);
3082 }
3083 }
3084
3085 /* paint the items */
3086 boolean noFocusDraw = false;
3087 int[] lineDash = gc.getLineDash ();
3088 int lineWidth = gc.getLineWidth ();
3089 for (int i = startIndex; i <= Math.min (endIndex, itemsCount - 1); i++) {
3090 CTableItem item = items [i];
3091 if (!item.isDisposed ()) { /* ensure that item was not disposed in a callback */
3092 if (startColumn == -1) {
3093 /* indicates that region to paint is to the right of the last column */
3094 noFocusDraw = item.paint (gc, null, true) || noFocusDraw;
3095 } else {
3096 if (numColumns == 0) {
3097 noFocusDraw = item.paint (gc, null, false) || noFocusDraw;
3098 } else {
3099 for (int j = startColumn; j <= Math.min (endColumn, columns.length - 1); j++) {
3100 if (!item.isDisposed ()) { /* ensure that item was not disposed in a callback */
3101 noFocusDraw = item.paint (gc, orderedColumns [j], false) || noFocusDraw;
3102 }
3103 if (isDisposed () || gc.isDisposed ()) return; /* ensure that receiver was not disposed in a callback */
3104 }
3105 }
3106 }
3107 }
3108 if (isDisposed () || gc.isDisposed ()) return; /* ensure that receiver was not disposed in a callback */
3109 }
3110
3111 /* repaint grid lines */
3112 gc.setClipping(clipping);
3113 gc.setLineWidth (lineWidth);
3114 if (linesVisible) {
3115 gc.setForeground (display.getSystemColor (SWT.COLOR_WIDGET_LIGHT_SHADOW));
3116 gc.setLineDash (lineDash);
3117 if (numColumns > 0 && startColumn != -1) {
3118 /* vertical column lines */
3119 for (int i = startColumn; i <= endColumn; i++) {
3120 int x = orderedColumns [i].getX () + orderedColumns [i].width - 1;
3121 gc.drawLine (x, clipping.y, x, clipping.y + clipping.height);
3122 }
3123 }
3124 /* horizontal item lines */
3125 bottomY = clipping.y + clipping.height;
3126 int rightX = clipping.x + clipping.width;
3127 int y = (clipping.y - headerHeight) / itemHeight * itemHeight + headerHeight;
3128 while (y <= bottomY) {
3129 gc.drawLine (clipping.x, y, rightX, y);
3130 y += itemHeight;
3131 }
3132 }
3133
3134 /* paint focus rectangle */
3135 if (!noFocusDraw && isFocusControl ()) {
3136 if (focusItem != null) {
3137 Rectangle focusBounds = focusItem.getFocusBounds ();
3138 if (focusBounds.width > 0) {
3139 gc.setForeground (display.getSystemColor (SWT.COLOR_BLACK));
3140 gc.setClipping (focusBounds);
3141 if (focusItem.isSelected ()) {
3142 gc.setLineDash (new int[] {2, 2});
3143 } else {
3144 gc.setLineDash (new int[] {1, 1});
3145 }
3146 gc.drawFocus (focusBounds.x, focusBounds.y, focusBounds.width, focusBounds.height);
3147 }
3148 } else {
3149 /* no items, so draw focus border around Table */
3150 int y = headerHeight + 1;
3151 int width = Math.max (0, clientArea.width - 2);
3152 int height = Math.max (0, clientArea.height - headerHeight - 2);
3153 gc.setForeground (display.getSystemColor (SWT.COLOR_BLACK));
3154 gc.setClipping (1, y, width, height);
3155 gc.setLineDash (new int[] {1, 1});
3156 gc.drawFocus (1, y, width, height);
3157 }
3158 }
3159 }
3160 void onResize (Event event) {
3161 clientArea = getClientArea ();
3162 /* vertical scrollbar */
3163 ScrollBar vBar = getVerticalBar ();
3164 if (vBar != null) {
3165 int clientHeight = (clientArea.height - getHeaderHeight ()) / itemHeight;
3166 int thumb = Math.min (clientHeight, itemsCount);
3167 vBar.setThumb (thumb);
3168 vBar.setPageIncrement (thumb);
3169 int index = vBar.getSelection ();
3170 if (index != topIndex) {
3171 topIndex = index;
3172 redraw ();
3173 }
3174 boolean visible = clientHeight < itemsCount;
3175 if (visible != vBar.getVisible ()) {
3176 vBar.setVisible (visible);
3177 clientArea = getClientArea ();
3178 }
3179 }
3180
3181 /* horizontal scrollbar */
3182 ScrollBar hBar = getHorizontalBar ();
3183 if (hBar != null) {
3184 int hBarMaximum = hBar.getMaximum ();
3185 int thumb = Math.min (clientArea.width, hBarMaximum);
3186 hBar.setThumb (thumb);
3187 hBar.setPageIncrement (thumb);
3188 horizontalOffset = hBar.getSelection ();
3189 boolean visible = clientArea.width < hBarMaximum;
3190 if (visible != hBar.getVisible ()) {
3191 hBar.setVisible (visible);
3192 clientArea = getClientArea ();
3193 }
3194 }
3195
3196 /* header */
3197 int headerHeight = Math.max (fontHeight, headerImageHeight) + 2 * getHeaderPadding ();
3198 header.setSize (clientArea.width, headerHeight);
3199
3200 /* if this is the focus control but there are no items then the boundary focus ring must be repainted */
3201 if (itemsCount == 0 && isFocusControl ()) redraw ();
3202 }
3203 void onScrollHorizontal (Event event) {
3204 ScrollBar hBar = getHorizontalBar ();
3205 if (hBar == null) return;
3206 int newSelection = hBar.getSelection ();
3207 update ();
3208 if (itemsCount > 0) {
3209 GC gc = new GC (this);
3210 gc.copyArea (
3211 0, 0,
3212 clientArea.width, clientArea.height,
3213 horizontalOffset - newSelection, 0);
3214 gc.dispose ();
3215 } else {
3216 redraw (); /* ensure that static focus rectangle updates properly */
3217 }
3218
3219 if (drawCount <= 0 && header.isVisible ()) {
3220 header.update ();
3221 Rectangle headerClientArea = header.getClientArea ();
3222 GC gc = new GC (header);
3223 gc.copyArea (
3224 0, 0,
3225 headerClientArea.width, headerClientArea.height,
3226 horizontalOffset - newSelection, 0);
3227 gc.dispose ();
3228 }
3229 horizontalOffset = newSelection;
3230 }
3231 void onScrollVertical (Event event) {
3232 ScrollBar vBar = getVerticalBar ();
3233 if (vBar == null) return;
3234 int newSelection = vBar.getSelection ();
3235 update ();
3236 GC gc = new GC (this);
3237 gc.copyArea (
3238 0, 0,
3239 clientArea.width, clientArea.height,
3240 0, (topIndex - newSelection) * itemHeight);
3241 gc.dispose ();
3242 topIndex = newSelection;
3243 }
3244 void onSpace () {
3245 if (focusItem == null) return;
3246 if (!focusItem.isSelected ()) {
3247 selectItem (focusItem, (getStyle () & SWT.MULTI) != 0);
3248 redrawItem (focusItem.index, true);
3249 }
3250 if ((getStyle () & SWT.CHECK) != 0) {
3251 focusItem.setChecked (!focusItem.checked);
3252 }
3253 showItem (focusItem);
3254 Event event = new Event ();
3255 event.item = focusItem;
3256 notifyListeners (SWT.Selection, event);
3257 if ((getStyle () & SWT.CHECK) == 0) return;
3258
3259 /* SWT.CHECK */
3260 event = new Event ();
3261 event.item = focusItem;
3262 event.detail = SWT.CHECK;
3263 notifyListeners (SWT.Selection, event);
3264 }
3265 /*
3266 * The current focus item is about to become unavailable, so reassign focus.
3267 */
3268 void reassignFocus () {
3269 if (focusItem == null) return;
3270
3271 /*
3272 * reassign to the previous root-level item if there is one, or the next
3273 * root-level item otherwise
3274 */
3275 int index = focusItem.index;
3276 if (index != 0) {
3277 index--;
3278 } else {
3279 index++;
3280 }
3281 if (index < itemsCount) {
3282 CTableItem item = items [index];
3283 setFocusItem (item, false);
3284 showItem (item);
3285 } else {
3286 setFocusItem (null, false); /* no items left */
3287 }
3288 }
3289 @Override
3290 public void redraw () {
3291 checkWidget ();
3292 if (drawCount <= 0) super.redraw ();
3293 }
3294 @Override
3295 public void redraw (int x, int y, int width, int height, boolean all) {
3296 checkWidget ();
3297 if (drawCount <= 0) super.redraw (x, y, width, height, all);
3298 }
3299 /*
3300 * Redraws from the specified index down to the last available item inclusive. Note
3301 * that the redraw bounds do not extend beyond the current last item, so clients
3302 * that reduce the number of available items should use #redrawItems(int,int) instead
3303 * to ensure that redrawing extends down to the previous bottom item boundary.
3304 */
3305 void redrawFromItemDownwards (int index) {
3306 redrawItems (index, itemsCount - 1, false);
3307 }
3308 /*
3309 * Redraws the table item at the specified index. It is valid for this index to reside
3310 * beyond the last available item.
3311 */
3312 void redrawItem (int itemIndex, boolean focusBoundsOnly) {
3313 if (itemIndex < itemsCount && !items [itemIndex].isInViewport ()) return;
3314 redrawItems (itemIndex, itemIndex, focusBoundsOnly);
3315 }
3316 /*
3317 * Redraws the table between the start and end item indices inclusive. It is valid
3318 * for the end index value to extend beyond the last available item.
3319 */
3320 void redrawItems (int startIndex, int endIndex, boolean focusBoundsOnly) {
3321 if (drawCount > 0) return;
3322
3323 int startY = (startIndex - topIndex) * itemHeight + getHeaderHeight ();
3324 int height = (endIndex - startIndex + 1) * itemHeight;
3325 if (focusBoundsOnly) {
3326 boolean custom = isListening (SWT.EraseItem) || isListening (SWT.PaintItem);
3327 if (!custom && columns.length > 0) {
3328 CTableColumn lastColumn;
3329 if ((getStyle () & SWT.FULL_SELECTION) != 0) {
3330 CTableColumn[] orderedColumns = getOrderedColumns ();
3331 lastColumn = orderedColumns [orderedColumns.length - 1];
3332 } else {
3333 lastColumn = columns [0];
3334 }
3335 int rightX = lastColumn.getX () + lastColumn.getWidth ();
3336 if (rightX <= 0) return; /* focus column(s) not visible */
3337 }
3338 endIndex = Math.min (endIndex, itemsCount - 1);
3339 for (int i = startIndex; i <= endIndex; i++) {
3340 CTableItem item = items [i];
3341 if (item.isInViewport ()) {
3342 /* if custom painting is being done then repaint the full item */
3343 if (custom) {
3344 redraw (0, getItemY (item), clientArea.width, itemHeight, false);
3345 } else {
3346 /* repaint the item's focus bounds */
3347 Rectangle bounds = item.getFocusBounds ();
3348 redraw (bounds.x, startY, bounds.width, height, false);
3349 }
3350 }
3351 }
3352 } else {
3353 redraw (0, startY, clientArea.width, height, false);
3354 }
3355 }
3356 /**
3357 * Removes the item from the receiver at the given
3358 * zero-relative index.
3359 *
3360 * @param index the index for the item
3361 *
3362 * @exception IllegalArgumentException <ul>
3363 * <li>ERROR_INVALID_RANGE - if the index is not between 0 and the number of elements in the list minus 1 (inclusive)</li>
3364 * </ul>
3365 * @exception SWTException <ul>
3366 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
3367 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
3368 * </ul>
3369 */
3370 public void remove (int index) {
3371 checkWidget ();
3372 if (!(0 <= index && index < itemsCount)) SWT.error (SWT.ERROR_INVALID_RANGE);
3373 items [index].dispose ();
3374 int[] eventData = new int[5];
3375 eventData[0] = ACC.DELETE;
3376 eventData[1] = index;
3377 eventData[2] = 1;
3378 eventData[3] = 0;
3379 eventData[4] = 0;
3380 getAccessible().sendEvent(ACC.EVENT_TABLE_CHANGED, eventData);
3381 }
3382 /**
3383 * Removes the items from the receiver which are
3384 * between the given zero-relative start and end
3385 * indices (inclusive).
3386 *
3387 * @param start the start of the range
3388 * @param end the end of the range
3389 *
3390 * @exception IllegalArgumentException <ul>
3391 * <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>
3392 * </ul>
3393 * @exception SWTException <ul>
3394 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
3395 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
3396 * </ul>
3397 */
3398 public void remove (int start, int end) {
3399 checkWidget ();
3400 if (start > end) return;
3401 if (!(0 <= start && start <= end && end < itemsCount)) {
3402 SWT.error (SWT.ERROR_INVALID_RANGE);
3403 }
3404 if (start == 0 && end == itemsCount - 1) {
3405 removeAll ();
3406 } else {
3407 for (int i = end; i >= start; i--) {
3408 items [i].dispose ();
3409 }
3410
3411 int[] eventData = new int[5];
3412 eventData[0] = ACC.DELETE;
3413 eventData[1] = start;
3414 eventData[2] = end - start + 1;
3415 eventData[3] = 0;
3416 eventData[4] = 0;
3417 getAccessible().sendEvent(ACC.EVENT_TABLE_CHANGED, eventData);
3418
3419 }
3420 }
3421 /**
3422 * Removes the items from the receiver's list at the given
3423 * zero-relative indices.
3424 *
3425 * @param indices the array of indices of the items
3426 *
3427 * @exception IllegalArgumentException <ul>
3428 * <li>ERROR_INVALID_RANGE - if the index is not between 0 and the number of elements in the list minus 1 (inclusive)</li>
3429 * <li>ERROR_NULL_ARGUMENT - if the indices array is null</li>
3430 * </ul>
3431 * @exception SWTException <ul>
3432 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
3433 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
3434 * </ul>
3435 */
3436 public void remove (int [] indices) {
3437 checkWidget ();
3438 if (indices == null) SWT.error (SWT.ERROR_NULL_ARGUMENT);
3439 if (indices.length == 0) return;
3440 int [] newIndices = new int [indices.length];
3441 System.arraycopy (indices, 0, newIndices, 0, indices.length);
3442 sortDescent (newIndices);
3443 int start = newIndices [newIndices.length - 1], end = newIndices [0];
3444 if (!(0 <= start && start <= end && end < itemsCount)) {
3445 SWT.error (SWT.ERROR_INVALID_RANGE);
3446 }
3447 int lastRemovedIndex = -1;
3448 int[] eventData = new int[5];
3449 for (int newIndice : newIndices) {
3450 if (newIndice != lastRemovedIndex) {
3451 items [newIndice].dispose ();
3452 eventData[0] = ACC.DELETE;
3453 eventData[1] = newIndice;
3454 eventData[2] = 1;
3455 eventData[3] = 0;
3456 eventData[4] = 0;
3457 getAccessible().sendEvent(ACC.EVENT_TABLE_CHANGED, eventData);
3458 lastRemovedIndex = newIndice;
3459 }
3460 }
3461 }
3462 /**
3463 * Removes all of the items from the receiver.
3464 *
3465 * @exception SWTException <ul>
3466 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
3467 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
3468 * </ul>
3469 */
3470 public void removeAll () {
3471 checkWidget ();
3472 if (itemsCount == 0) return;
3473 setRedraw (false);
3474
3475 setFocusItem (null, false);
3476 for (int i = 0; i < itemsCount; i++) {
3477 items [i].dispose (false);
3478 }
3479 items = new CTableItem [0];
3480 selectedItems = new CTableItem [0];
3481 int oldCount = itemsCount;
3482 itemsCount = topIndex = 0;
3483 anchorItem = lastClickedItem = null;
3484 lastSelectionEvent = null;
3485
3486 int[] eventData = new int[5];
3487 eventData[0] = ACC.DELETE;
3488 eventData[1] = 0;
3489 eventData[2] = oldCount;
3490 eventData[3] = 0;
3491 eventData[4] = 0;
3492 getAccessible().sendEvent(ACC.EVENT_TABLE_CHANGED, eventData);
3493
3494 ScrollBar vBar = getVerticalBar ();
3495 if (vBar != null) {
3496 vBar.setMaximum (1);
3497 vBar.setVisible (false);
3498 }
3499 if (columns.length == 0) {
3500 horizontalOffset = 0;
3501 ScrollBar hBar = getHorizontalBar ();
3502 if (hBar != null) {
3503 hBar.setMaximum (1);
3504 hBar.setVisible (false);
3505 }
3506 }
3507
3508 setRedraw (true);
3509 }
3510 String removeMnemonics (String string) {
3511 /* removes single ampersands and preserves double-ampersands */
3512 char [] chars = new char [string.length ()];
3513 string.getChars (0, chars.length, chars, 0);
3514 int i = 0, j = 0;
3515 for ( ; i < chars.length; i++, j++) {
3516 if (chars[i] == '&') {
3517 if (++i == chars.length) break;
3518 if (chars[i] == '&') {
3519 chars[j++] = chars[i - 1];
3520 }
3521 }
3522 chars[j] = chars[i];
3523 }
3524 if (i == j) return string;
3525 return new String (chars, 0, j);
3526 }
3527 void removeSelectedItem (int index) {
3528 CTableItem[] newSelectedItems = new CTableItem [selectedItems.length - 1];
3529 System.arraycopy (selectedItems, 0, newSelectedItems, 0, index);
3530 System.arraycopy (selectedItems, index + 1, newSelectedItems, index, newSelectedItems.length - index);
3531 selectedItems = newSelectedItems;
3532 }
3533 /**
3534 * Removes the listener from the collection of listeners who will
3535 * be notified when the user changes the receiver's selection.
3536 *
3537 * @param listener the listener which should no longer be notified
3538 *
3539 * @exception IllegalArgumentException <ul>
3540 * <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
3541 * </ul>
3542 * @exception SWTException <ul>
3543 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
3544 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
3545 * </ul>
3546 *
3547 * @see SelectionListener
3548 * @see #addSelectionListener(SelectionListener)
3549 */
3550 public void removeSelectionListener (SelectionListener listener) {
3551 checkWidget ();
3552 if (listener == null) SWT.error (SWT.ERROR_NULL_ARGUMENT);
3553 removeListener (SWT.Selection, listener);
3554 removeListener (SWT.DefaultSelection, listener);
3555 }
3556 /**
3557 * Selects the item at the given zero-relative index in the receiver.
3558 * If the item at the index was already selected, it remains
3559 * selected. Indices that are out of range are ignored.
3560 *
3561 * @param index the index of the item to select
3562 *
3563 * @exception SWTException <ul>
3564 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
3565 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
3566 * </ul>
3567 */
3568 public void select (int index) {
3569 checkWidget ();
3570 if (!(0 <= index && index < itemsCount)) return;
3571 selectItem (items [index], (getStyle () & SWT.MULTI) != 0);
3572 if (isFocusControl () || (getStyle () & SWT.HIDE_SELECTION) == 0) {
3573 redrawItem (index, false);
3574 }
3575 getAccessible().selectionChanged();
3576 }
3577 /**
3578 * Selects the items in the range specified by the given zero-relative
3579 * indices in the receiver. The range of indices is inclusive.
3580 * The current selection is not cleared before the new items are selected.
3581 * <p>
3582 * If an item in the given range is not selected, it is selected.
3583 * If an item in the given range was already selected, it remains selected.
3584 * Indices that are out of range are ignored and no items will be selected
3585 * if start is greater than end.
3586 * If the receiver is single-select and there is more than one item in the
3587 * given range, then all indices are ignored.
3588 * </p>
3589 *
3590 * @param start the start of the range
3591 * @param end the end of the range
3592 *
3593 * @exception SWTException <ul>
3594 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
3595 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
3596 * </ul>
3597 *
3598 * @see CTable#setSelection(int,int)
3599 */
3600 public void select (int start, int end) {
3601 checkWidget ();
3602 if (end < 0 || start > end || ((getStyle () & SWT.SINGLE) != 0 && start != end)) return;
3603 if (itemsCount == 0 || start >= itemsCount) return;
3604 start = Math.max (start, 0);
3605 end = Math.min (end, itemsCount - 1);
3606 for (int i = start; i <= end; i++) {
3607 selectItem (items [i], (getStyle () & SWT.MULTI) != 0);
3608 }
3609 if (isFocusControl () || (getStyle () & SWT.HIDE_SELECTION) == 0) {
3610 redrawItems (start, end, false);
3611 }
3612 getAccessible().selectionChanged();
3613 }
3614 /**
3615 * Selects the items at the given zero-relative indices in the receiver.
3616 * The current selection is not cleared before the new items are selected.
3617 * <p>
3618 * If the item at a given index is not selected, it is selected.
3619 * If the item at a given index was already selected, it remains selected.
3620 * Indices that are out of range and duplicate indices are ignored.
3621 * If the receiver is single-select and multiple indices are specified,
3622 * then all indices are ignored.
3623 * </p>
3624 *
3625 * @param indices the array of indices for the items to select
3626 *
3627 * @exception IllegalArgumentException <ul>
3628 * <li>ERROR_NULL_ARGUMENT - if the array of indices is null</li>
3629 * </ul>
3630 * @exception SWTException <ul>
3631 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
3632 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
3633 * </ul>
3634 *
3635 * @see CTable#setSelection(int[])
3636 */
3637 public void select (int [] indices) {
3638 checkWidget ();
3639 if (indices == null) SWT.error (SWT.ERROR_NULL_ARGUMENT);
3640 if (indices.length == 0 || ((getStyle () & SWT.SINGLE) != 0 && indices.length > 1)) return;
3641 for (int index : indices) {
3642 if (0 <= index && index < itemsCount) {
3643 selectItem (items [index], (getStyle () & SWT.MULTI) != 0);
3644 }
3645 }
3646 if (isFocusControl () || (getStyle () & SWT.HIDE_SELECTION) == 0) {
3647 for (int index : indices) {
3648 if (0 <= index && index < itemsCount) {
3649 redrawItem (index, false);
3650 }
3651 }
3652 }
3653 getAccessible().selectionChanged();
3654 }
3655 /**
3656 * Selects all of the items in the receiver.
3657 * <p>
3658 * If the receiver is single-select, do nothing.
3659 * </p>
3660 *
3661 * @exception SWTException <ul>
3662 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
3663 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
3664 * </ul>
3665 */
3666 public void selectAll () {
3667 checkWidget ();
3668 if ((getStyle () & SWT.SINGLE) != 0) return;
3669 selectedItems = new CTableItem [itemsCount];
3670 System.arraycopy (items, 0, selectedItems, 0, itemsCount);
3671 if (isFocusControl () || (getStyle () & SWT.HIDE_SELECTION) == 0) {
3672 redraw ();
3673 }
3674 for (CTableItem selectedItem : selectedItems) {
3675 selectedItem.getAccessible(getAccessible(), 0).selectionChanged();
3676 }
3677 getAccessible().selectionChanged();
3678 }
3679 void selectItem (CTableItem item, boolean addToSelection) {
3680 CTableItem[] oldSelectedItems = selectedItems;
3681 if (!addToSelection || (getStyle () & SWT.SINGLE) != 0) {
3682 selectedItems = new CTableItem[] {item};
3683 if (isFocusControl () || (getStyle () & SWT.HIDE_SELECTION) == 0) {
3684 for (CTableItem oldSelectedItem : oldSelectedItems) {
3685 if (oldSelectedItem != item) {
3686 redrawItem (oldSelectedItem.index, true);
3687 }
3688 }
3689 }
3690 for (CTableItem oldSelectedItem : oldSelectedItems) {
3691 oldSelectedItem.getAccessible(getAccessible(), 0).selectionChanged();
3692 }
3693 } else {
3694 if (item.isSelected ()) return;
3695 selectedItems = new CTableItem [selectedItems.length + 1];
3696 System.arraycopy (oldSelectedItems, 0, selectedItems, 0, oldSelectedItems.length);
3697 selectedItems [selectedItems.length - 1] = item;
3698 }
3699
3700 item.getAccessible(getAccessible(), 0).selectionChanged();
3701 getAccessible().selectionChanged();
3702 }
3703 @Override
3704 public void setBackground (Color color) {
3705 checkWidget ();
3706 if (color == null) color = display.getSystemColor (SWT.COLOR_LIST_BACKGROUND);
3707 super.setBackground (color);
3708 }
3709 @Override
3710 public void setForeground (Color color) {
3711 checkWidget ();
3712 if (color == null) color = display.getSystemColor (SWT.COLOR_LIST_FOREGROUND);
3713 super.setForeground (color);
3714 }
3715 /**
3716 * Sets the order that the items in the receiver should
3717 * be displayed in to the given argument which is described
3718 * in terms of the zero-relative ordering of when the items
3719 * were added.
3720 *
3721 * @param order the new order to display the items
3722 *
3723 * @exception SWTException <ul>
3724 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
3725 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
3726 * </ul>
3727 * @exception IllegalArgumentException <ul>
3728 * <li>ERROR_NULL_ARGUMENT - if the item order is null</li>
3729 * <li>ERROR_INVALID_ARGUMENT - if the item order is not the same length as the number of items</li>
3730 * </ul>
3731 *
3732 * @see CTable#getColumnOrder()
3733 * @see CTableColumn#getMoveable()
3734 * @see CTableColumn#setMoveable(boolean)
3735 * @see SWT#Move
3736 *
3737 * @since 3.1
3738 */
3739 public void setColumnOrder (int [] order) {
3740 checkWidget ();
3741 if (order == null) SWT.error (SWT.ERROR_NULL_ARGUMENT);
3742 if (columns.length == 0) {
3743 if (order.length != 0) SWT.error (SWT.ERROR_INVALID_ARGUMENT);
3744 return;
3745 }
3746 if (order.length != columns.length) SWT.error (SWT.ERROR_INVALID_ARGUMENT);
3747 boolean reorder = false;
3748 boolean [] seen = new boolean [columns.length];
3749 int[] oldOrder = getColumnOrder ();
3750 for (int i = 0; i < order.length; i++) {
3751 int index = order [i];
3752 if (index < 0 || index >= columns.length) SWT.error (SWT.ERROR_INVALID_RANGE);
3753 if (seen [index]) SWT.error (SWT.ERROR_INVALID_ARGUMENT);
3754 seen [index] = true;
3755 if (index != oldOrder [i]) reorder = true;
3756 }
3757 if (!reorder) return;
3758
3759 headerHideToolTip ();
3760 int[] oldX = new int [columns.length];
3761 for (int i = 0; i < columns.length; i++) {
3762 oldX [i] = columns [i].getX ();
3763 }
3764 orderedColumns = new CTableColumn [order.length];
3765 for (int i = 0; i < order.length; i++) {
3766 orderedColumns [i] = columns [order [i]];
3767 }
3768 for (CTableColumn orderedColumn : orderedColumns) {
3769 CTableColumn column = orderedColumn;
3770 if (!column.isDisposed () && column.getX () != oldX [column.getIndex ()]) {
3771 column.notifyListeners (SWT.Move, new Event ());
3772 }
3773 }
3774
3775 redraw ();
3776 if (drawCount <= 0 && header.isVisible ()) header.redraw ();
3777 }
3778 void setFocusItem (CTableItem item, boolean redrawOldFocus) {
3779 if (item == focusItem) return;
3780 CTableItem oldFocusItem = focusItem;
3781 if (oldFocusItem != null) oldFocusItem.getAccessible(getAccessible(), 0).setFocus(ACC.CHILDID_SELF);
3782 focusItem = item;
3783 if (redrawOldFocus && oldFocusItem != null) {
3784 redrawItem (oldFocusItem.index, true);
3785 }
3786 if (focusItem != null) focusItem.getAccessible(getAccessible(), 0).setFocus(ACC.CHILDID_SELF);
3787 }
3788 @Override
3789 public void setFont (Font value) {
3790 checkWidget ();
3791 Font oldFont = getFont ();
3792 super.setFont (value);
3793 Font font = getFont ();
3794 if (font.equals (oldFont)) return;
3795
3796 GC gc = new GC (this);
3797
3798 /* recompute the receiver's cached font height and item height values */
3799 fontHeight = gc.getFontMetrics ().getHeight ();
3800 setItemHeight (Math.max (fontHeight, imageHeight) + 2 * getCellPadding ());
3801 Point headerSize = header.getSize ();
3802 int newHeaderHeight = Math.max (fontHeight, headerImageHeight) + 2 * getHeaderPadding ();
3803 if (headerSize.y != newHeaderHeight) {
3804 header.setSize (headerSize.x, newHeaderHeight);
3805 }
3806 header.setFont (font);
3807
3808 /*
3809 * Notify all columns and items of the font change so that elements that
3810 * use the receiver's font can recompute their cached string widths.
3811 */
3812 for (CTableColumn column : columns) {
3813 column.updateFont (gc);
3814 }
3815 for (int i = 0; i < itemsCount; i++) {
3816 items [i].updateFont (gc);
3817 }
3818
3819 gc.dispose ();
3820
3821 if (drawCount <= 0 && header.isVisible ()) header.redraw ();
3822
3823 /* update scrollbars */
3824 if (columns.length == 0) updateHorizontalBar ();
3825 ScrollBar vBar = getVerticalBar ();
3826 if (vBar != null) {
3827 int thumb = (clientArea.height - getHeaderHeight ()) / itemHeight;
3828 vBar.setThumb (thumb);
3829 vBar.setPageIncrement (thumb);
3830 topIndex = vBar.getSelection ();
3831 vBar.setVisible (thumb < vBar.getMaximum ());
3832 }
3833 redraw ();
3834 }
3835 void setHeaderImageHeight (int value) {
3836 headerImageHeight = value;
3837 Point headerSize = header.getSize ();
3838 int newHeaderHeight = Math.max (fontHeight, headerImageHeight) + 2 * getHeaderPadding ();
3839 if (headerSize.y != newHeaderHeight) {
3840 header.setSize (headerSize.x, newHeaderHeight);
3841 }
3842 }
3843 /**
3844 * Marks the receiver's header as visible if the argument is <code>true</code>,
3845 * and marks it invisible otherwise.
3846 * <p>
3847 * If one of the receiver's ancestors is not visible or some
3848 * other condition makes the receiver not visible, marking
3849 * it visible may not actually cause it to be displayed.
3850 * </p>
3851 *
3852 * @param show the new visibility state
3853 *
3854 * @exception SWTException <ul>
3855 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
3856 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
3857 * </ul>
3858 */
3859 public void setHeaderVisible (boolean value) {
3860 checkWidget ();
3861 if (header.getVisible () == value) return; /* no change */
3862 headerHideToolTip ();
3863 header.setVisible (value);
3864 updateVerticalBar ();
3865 redraw ();
3866 }
3867 void setImageHeight (int value) {
3868 imageHeight = value;
3869 setItemHeight (Math.max (fontHeight, imageHeight) + 2 * getCellPadding ());
3870 }
3871 /**
3872 * Sets the number of items contained in the receiver.
3873 *
3874 * @param count the number of items
3875 *
3876 * @exception SWTException <ul>
3877 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
3878 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
3879 * </ul>
3880 *
3881 * @since 3.0
3882 */
3883 public void setItemCount (int count) {
3884 checkWidget ();
3885 count = Math.max (0, count);
3886 if (count == itemsCount) return;
3887 int oldCount = itemsCount;
3888 int redrawStart, redrawEnd;
3889
3890 /* if the new item count is less than the current count then remove all excess items from the end */
3891 if (count < itemsCount) {
3892 redrawStart = count;
3893 redrawEnd = itemsCount - 1;
3894 for (int i = count; i < itemsCount; i++) {
3895 items [i].dispose (false);
3896 }
3897
3898 int newSelectedCount = 0;
3899 for (CTableItem selectedItem : selectedItems) {
3900 if (!selectedItem.isDisposed ()) newSelectedCount++;
3901 }
3902 if (newSelectedCount != selectedItems.length) {
3903 /* one or more selected items have been disposed */
3904 CTableItem[] newSelectedItems = new CTableItem [newSelectedCount];
3905 int pos = 0;
3906 for (CTableItem selectedItem : selectedItems) {
3907 CTableItem item = selectedItem;
3908 if (!item.isDisposed ()) {
3909 newSelectedItems [pos++] = item;
3910 }
3911 }
3912 selectedItems = newSelectedItems;
3913 }
3914
3915 if (anchorItem != null && anchorItem.isDisposed ()) anchorItem = null;
3916 if (lastClickedItem != null && lastClickedItem.isDisposed ()) lastClickedItem = null;
3917 if (focusItem != null && focusItem.isDisposed ()) {
3918 CTableItem newFocusItem = count > 0 ? items [count - 1] : null;
3919 setFocusItem (newFocusItem, false);
3920 }
3921 int[] eventData = new int[5];
3922 eventData[0] = ACC.DELETE;
3923 eventData[1] = redrawStart;
3924 eventData[2] = redrawEnd - redrawStart;
3925 eventData[3] = 0;
3926 eventData[4] = 0;
3927 getAccessible().sendEvent(ACC.EVENT_TABLE_CHANGED, eventData);
3928
3929 itemsCount = count;
3930 if (columns.length == 0) updateHorizontalBar ();
3931 } else {
3932 redrawStart = itemsCount;
3933 redrawEnd = count - 1;
3934 CTableItem[] newItems = new CTableItem [count];
3935 System.arraycopy (items, 0, newItems, 0, itemsCount);
3936 items = newItems;
3937 for (int i = itemsCount; i < count; i++) {
3938 items [i] = new CTableItem (this, SWT.NONE, i, false);
3939 itemsCount++;
3940 }
3941
3942 int[] eventData = new int[5];
3943 eventData[0] = ACC.INSERT;
3944 eventData[1] = redrawStart;
3945 eventData[2] = count;
3946 eventData[3] = 0;
3947 eventData[4] = 0;
3948 getAccessible().sendEvent(ACC.EVENT_TABLE_CHANGED, eventData);
3949 if (oldCount == 0) focusItem = items [0];
3950 }
3951
3952 updateVerticalBar ();
3953 /*
3954 * If this is the focus control and the item count is going from 0->!0 or !0->0 then the
3955 * receiver must be redrawn to ensure that its boundary focus ring is updated.
3956 */
3957 if ((oldCount == 0 || itemsCount == 0) && isFocusControl ()) {
3958 redraw ();
3959 return;
3960 }
3961 redrawItems (redrawStart, redrawEnd, false);
3962 }
3963 boolean setItemHeight (int value) {
3964 boolean update = !customHeightSet || itemHeight < value;
3965 if (update) itemHeight = value;
3966 return update;
3967 }
3968 /**
3969 * Marks the receiver's lines as visible if the argument is <code>true</code>,
3970 * and marks it invisible otherwise. Note that some platforms draw grid lines
3971 * while others may draw alternating row colors.
3972 * <p>
3973 * If one of the receiver's ancestors is not visible or some
3974 * other condition makes the receiver not visible, marking
3975 * it visible may not actually cause it to be displayed.
3976 * </p>
3977 *
3978 * @param show the new visibility state
3979 *
3980 * @exception SWTException <ul>
3981 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
3982 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
3983 * </ul>
3984 */
3985 public void setLinesVisible (boolean value) {
3986 checkWidget ();
3987 if (linesVisible == value) return; /* no change */
3988 linesVisible = value;
3989 redraw ();
3990 }
3991 @Override
3992 public void setMenu (Menu menu) {
3993 super.setMenu (menu);
3994 header.setMenu (menu);
3995 }
3996 @Override
3997 public void setRedraw (boolean value) {
3998 checkWidget();
3999 if (value) {
4000 if (--drawCount == 0) {
4001 if (items.length - itemsCount > 3) {
4002 CTableItem[] newItems = new CTableItem [itemsCount];
4003 System.arraycopy (items, 0, newItems, 0, itemsCount);
4004 items = newItems;
4005 }
4006 updateVerticalBar ();
4007 updateHorizontalBar ();
4008 }
4009 } else {
4010 drawCount++;
4011 }
4012 super.setRedraw (value);
4013 header.setRedraw (value);
4014 }
4015 /**
4016 * Sets the receiver's selection to the given item.
4017 * The current selection is cleared before the new item is selected.
4018 * <p>
4019 * If the item is not in the receiver, then it is ignored.
4020 * </p>
4021 *
4022 * @param item the item to select
4023 *
4024 * @exception IllegalArgumentException <ul>
4025 * <li>ERROR_NULL_ARGUMENT - if the item is null</li>
4026 * <li>ERROR_INVALID_ARGUMENT - if the item has been disposed</li>
4027 * </ul>
4028 * @exception SWTException <ul>
4029 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
4030 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
4031 * </ul>
4032 *
4033 * @since 3.2
4034 */
4035 public void setSelection (CTableItem item) {
4036 checkWidget ();
4037 if (item == null) SWT.error (SWT.ERROR_NULL_ARGUMENT);
4038 setSelection (new CTableItem[] {item}, true);
4039 }
4040 /**
4041 * Sets the receiver's selection to be the given array of items.
4042 * The current selection is cleared before the new items are selected.
4043 * <p>
4044 * Items that are not in the receiver are ignored.
4045 * If the receiver is single-select and multiple items are specified,
4046 * then all items are ignored.
4047 * </p>
4048 *
4049 * @param items the array of items
4050 *
4051 * @exception IllegalArgumentException <ul>
4052 * <li>ERROR_NULL_ARGUMENT - if the array of items is null</li>
4053 * <li>ERROR_INVALID_ARGUMENT - if one of the items has been disposed</li>
4054 * </ul>
4055 * @exception SWTException <ul>
4056 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
4057 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
4058 * </ul>
4059 *
4060 * @see CTable#deselectAll()
4061 * @see CTable#select(int[])
4062 * @see CTable#setSelection(int[])
4063 */
4064 public void setSelection (CTableItem[] items) {
4065 checkWidget ();
4066 if (items == null) SWT.error (SWT.ERROR_NULL_ARGUMENT);
4067 setSelection (items, true);
4068 }
4069 void setSelection (CTableItem[] items, boolean updateViewport) {
4070 if (items.length == 0 || ((getStyle () & SWT.SINGLE) != 0 && items.length > 1)) {
4071 deselectAll ();
4072 return;
4073 }
4074 CTableItem[] oldSelection = selectedItems;
4075
4076 /* remove null and duplicate items */
4077 int index = 0;
4078 selectedItems = new CTableItem [items.length]; /* assume all valid items */
4079 for (CTableItem item2 : items) {
4080 CTableItem item = item2;
4081 if (item != null && item.parent == this && !item.isSelected ()) {
4082 selectedItems [index++] = item;
4083 }
4084 }
4085 if (index != items.length) {
4086 /* an invalid item was provided so resize the array accordingly */
4087 CTableItem[] temp = new CTableItem [index];
4088 System.arraycopy (selectedItems, 0, temp, 0, index);
4089 selectedItems = temp;
4090 }
4091 if (selectedItems.length == 0) { /* no valid items */
4092 deselectAll ();
4093 return;
4094 }
4095
4096 boolean tableSelectionChanged = false;
4097 if (isFocusControl () || (getStyle () & SWT.HIDE_SELECTION) == 0) {
4098 for (CTableItem item : oldSelection) {
4099 if (!item.isSelected ()) {
4100 redrawItem (item.index, true);
4101 item.getAccessible(getAccessible(), 0).selectionChanged();
4102 tableSelectionChanged = true;
4103 }
4104 }
4105 for (CTableItem selectedItem : selectedItems) {
4106 redrawItem (selectedItem.index, true);
4107 selectedItem.getAccessible(getAccessible(), 0).selectionChanged();
4108 tableSelectionChanged = true;
4109 }
4110 }
4111 if (updateViewport) {
4112 showItem (selectedItems [0]);
4113 setFocusItem (selectedItems [0], true);
4114 }
4115
4116 if (tableSelectionChanged) getAccessible().selectionChanged();
4117 }
4118 /**
4119 * Sets the column used by the sort indicator for the receiver. A null
4120 * value will clear the sort indicator. The current sort column is cleared
4121 * before the new column is set.
4122 *
4123 * @param column the column used by the sort indicator or <code>null</code>
4124 *
4125 * @exception IllegalArgumentException <ul>
4126 * <li>ERROR_INVALID_ARGUMENT - if the column is disposed</li>
4127 * </ul>
4128 * @exception SWTException <ul>
4129 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
4130 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
4131 * </ul>
4132 *
4133 * @since 3.2
4134 */
4135 public void setSortColumn (CTableColumn column) {
4136 checkWidget ();
4137 if (column != null && column.isDisposed ()) SWT.error (SWT.ERROR_INVALID_ARGUMENT);
4138 if (column == sortColumn) return;
4139 if (sortColumn != null && !sortColumn.isDisposed ()) {
4140 sortColumn.setSortDirection (SWT.NONE);
4141 }
4142 sortColumn = column;
4143 if (sortColumn != null) {
4144 sortColumn.setSortDirection (sortDirection);
4145 }
4146 }
4147 /**
4148 * Sets the direction of the sort indicator for the receiver. The value
4149 * can be one of <code>UP</code>, <code>DOWN</code> or <code>NONE</code>.
4150 *
4151 * @param direction the direction of the sort indicator
4152 *
4153 * @exception SWTException <ul>
4154 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
4155 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
4156 * </ul>
4157 *
4158 * @since 3.2
4159 */
4160 public void setSortDirection (int direction) {
4161 checkWidget ();
4162 if (direction != SWT.UP && direction != SWT.DOWN && direction != SWT.NONE) return;
4163 sortDirection = direction;
4164 if (sortColumn == null || sortColumn.isDisposed ()) return;
4165 sortColumn.setSortDirection (sortDirection);
4166 }
4167 /**
4168 * Selects the item at the given zero-relative index in the receiver.
4169 * The current selection is first cleared, then the new item is selected.
4170 *
4171 * @param index the index of the item to select
4172 *
4173 * @exception SWTException <ul>
4174 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
4175 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
4176 * </ul>
4177 *
4178 * @see CTable#deselectAll()
4179 * @see CTable#select(int)
4180 */
4181 public void setSelection (int index) {
4182 checkWidget ();
4183 deselectAll ();
4184 if (!(0 <= index && index < itemsCount)) return;
4185 selectItem (items [index], false);
4186 setFocusItem (items [index], true);
4187 redrawItem (index, true);
4188 showSelection ();
4189 getAccessible().selectionChanged();
4190 }
4191 /**
4192 * Selects the items in the range specified by the given zero-relative
4193 * indices in the receiver. The range of indices is inclusive.
4194 * The current selection is cleared before the new items are selected.
4195 * <p>
4196 * Indices that are out of range are ignored and no items will be selected
4197 * if start is greater than end.
4198 * If the receiver is single-select and there is more than one item in the
4199 * given range, then all indices are ignored.
4200 * </p>
4201 *
4202 * @param start the start index of the items to select
4203 * @param end the end index of the items to select
4204 *
4205 * @exception SWTException <ul>
4206 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
4207 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
4208 * </ul>
4209 *
4210 * @see CTable#deselectAll()
4211 * @see CTable#select(int,int)
4212 */
4213 public void setSelection (int start, int end) {
4214 checkWidget ();
4215 deselectAll ();
4216 if (end < 0 || start > end || ((getStyle () & SWT.SINGLE) != 0 && start != end)) return;
4217 if (itemsCount == 0 || start >= itemsCount) return;
4218 start = Math.max (0, start);
4219 end = Math.min (end, itemsCount - 1);
4220 select (start, end);
4221 setFocusItem (items [start], true);
4222 showSelection ();
4223 }
4224 /**
4225 * Selects the items at the given zero-relative indices in the receiver.
4226 * The current selection is cleared before the new items are selected.
4227 * <p>
4228 * Indices that are out of range and duplicate indices are ignored.
4229 * If the receiver is single-select and multiple indices are specified,
4230 * then all indices are ignored.
4231 * </p>
4232 *
4233 * @param indices the indices of the items to select
4234 *
4235 * @exception IllegalArgumentException <ul>
4236 * <li>ERROR_NULL_ARGUMENT - if the array of indices is null</li>
4237 * </ul>
4238 * @exception SWTException <ul>
4239 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
4240 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
4241 * </ul>
4242 *
4243 * @see CTable#deselectAll()
4244 * @see CTable#select(int[])
4245 */
4246 public void setSelection (int [] indices) {
4247 checkWidget ();
4248 if (indices == null) SWT.error (SWT.ERROR_NULL_ARGUMENT);
4249 deselectAll ();
4250 int length = indices.length;
4251 if (length == 0 || ((getStyle () & SWT.SINGLE) != 0 && length > 1)) return;
4252 select (indices);
4253 int focusIndex = -1;
4254 for (int i = 0; i < indices.length && focusIndex == -1; i++) {
4255 if (0 <= indices [i] && indices [i] < itemsCount) {
4256 focusIndex = indices [i];
4257 }
4258 }
4259 if (focusIndex != -1) setFocusItem (items [focusIndex], true);
4260 showSelection ();
4261 }
4262 /**
4263 * Sets the zero-relative index of the item which is currently
4264 * at the top of the receiver. This index can change when items
4265 * are scrolled or new items are added and removed.
4266 *
4267 * @param index the index of the top item
4268 *
4269 * @exception SWTException <ul>
4270 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
4271 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
4272 * </ul>
4273 */
4274 public void setTopIndex (int index) {
4275 checkWidget ();
4276 if (!(0 <= index && index < itemsCount)) return;
4277 int visibleItemCount = (clientArea.height - getHeaderHeight ()) / itemHeight;
4278 if (itemsCount <= visibleItemCount) return;
4279 index = Math.min (index, itemsCount - visibleItemCount);
4280 if (index == topIndex) return;
4281
4282 update ();
4283 int change = topIndex - index;
4284 topIndex = index;
4285 ScrollBar vBar = getVerticalBar ();
4286 if (vBar != null) vBar.setSelection (topIndex);
4287 if (drawCount <= 0) {
4288 GC gc = new GC (this);
4289 gc.copyArea (0, 0, clientArea.width, clientArea.height, 0, change * itemHeight);
4290 gc.dispose ();
4291 }
4292 }
4293 /**
4294 * Shows the column. If the column is already showing in the receiver,
4295 * this method simply returns. Otherwise, the columns are scrolled until
4296 * the column is visible.
4297 *
4298 * @param column the column to be shown
4299 *
4300 * @exception IllegalArgumentException <ul>
4301 * <li>ERROR_NULL_ARGUMENT - if the column is null</li>
4302 * <li>ERROR_INVALID_ARGUMENT - if the column has been disposed</li>
4303 * </ul>
4304 * @exception SWTException <ul>
4305 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
4306 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
4307 * </ul>
4308 *
4309 * @since 3.0
4310 */
4311 public void showColumn (CTableColumn column) {
4312 checkWidget ();
4313 if (column == null) SWT.error (SWT.ERROR_NULL_ARGUMENT);
4314 if (column.isDisposed ()) SWT.error(SWT.ERROR_INVALID_ARGUMENT);
4315 if (column.parent != this) return;
4316
4317 int x = column.getX ();
4318 int rightX = x + column.width;
4319 if (0 <= x && rightX <= clientArea.width) return; /* column is fully visible */
4320
4321 headerHideToolTip ();
4322 int absX = 0; /* the X of the column irrespective of the horizontal scroll */
4323 CTableColumn[] orderedColumns = getOrderedColumns ();
4324 for (int i = 0; i < column.getOrderIndex (); i++) {
4325 absX += orderedColumns [i].width;
4326 }
4327 if (x < clientArea.x) { /* column is to left of viewport */
4328 horizontalOffset = absX;
4329 } else {
4330 horizontalOffset = absX + column.width - clientArea.width;
4331 }
4332 ScrollBar hBar = getHorizontalBar ();
4333 if (hBar != null) hBar.setSelection (horizontalOffset);
4334 redraw ();
4335 if (drawCount <= 0 && header.isVisible ()) header.redraw ();
4336 }
4337 /**
4338 * Shows the item. If the item is already showing in the receiver,
4339 * this method simply returns. Otherwise, the items are scrolled until
4340 * the item is visible.
4341 *
4342 * @param item the item to be shown
4343 *
4344 * @exception IllegalArgumentException <ul>
4345 * <li>ERROR_NULL_ARGUMENT - if the item is null</li>
4346 * <li>ERROR_INVALID_ARGUMENT - if the item has been disposed</li>
4347 * </ul>
4348 * @exception SWTException <ul>
4349 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
4350 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
4351 * </ul>
4352 *
4353 * @see CTable#showSelection()
4354 */
4355 public void showItem (CTableItem item) {
4356 checkWidget ();
4357 if (item == null) SWT.error (SWT.ERROR_NULL_ARGUMENT);
4358 if (item.isDisposed ()) SWT.error (SWT.ERROR_INVALID_ARGUMENT);
4359 if (item.parent != this) return;
4360
4361 int index = item.index;
4362 int visibleItemCount = (clientArea.height - getHeaderHeight ()) / itemHeight;
4363 /* nothing to do if item is already in viewport */
4364 if (topIndex <= index && index < topIndex + visibleItemCount) return;
4365
4366 if (index <= topIndex) {
4367 /* item is above current viewport, so show on top */
4368 setTopIndex (item.index);
4369 } else {
4370 /* item is below current viewport, so show on bottom */
4371 visibleItemCount = Math.max (visibleItemCount, 1); /* item to show should be top item */
4372 setTopIndex (Math.min (index - visibleItemCount + 1, itemsCount - 1));
4373 }
4374 }
4375 /**
4376 * Shows the selection. If the selection is already showing in the receiver,
4377 * this method simply returns. Otherwise, the items are scrolled until
4378 * the selection is visible.
4379 *
4380 * @exception SWTException <ul>
4381 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
4382 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
4383 * </ul>
4384 *
4385 * @see CTable#showItem(CTableItem)
4386 */
4387 public void showSelection () {
4388 checkWidget ();
4389 if (selectedItems.length == 0) return;
4390 showItem (selectedItems [0]);
4391 }
4392 void sortDescent (int [] items) {
4393 /* Shell Sort from K&R, pg 108 */
4394 int length = items.length;
4395 for (int gap = length / 2; gap > 0; gap /= 2) {
4396 for (int i = gap; i < length; i++) {
4397 for (int j = i - gap; j >= 0; j -= gap) {
4398 if (items [j] <= items [j + gap]) {
4399 int swap = items [j];
4400 items [j] = items [j + gap];
4401 items [j + gap] = swap;
4402 }
4403 }
4404 }
4405 }
4406 }
4407 void sortAscent (int [] items) {
4408 /* Shell Sort from K&R, pg 108 */
4409 int length = items.length;
4410 for (int gap = length / 2; gap > 0; gap /= 2) {
4411 for (int i = gap; i < length; i++) {
4412 for (int j = i - gap; j >= 0; j -= gap) {
4413 if (items [j] >= items [j + gap]) {
4414 int swap = items [j];
4415 items [j] = items [j + gap];
4416 items [j + gap] = swap;
4417 }
4418 }
4419 }
4420 }
4421 }
4422 void sortAscent (CTableItem [] items) {
4423 /* Shell Sort from K&R, pg 108 */
4424 int length = items.length;
4425 for (int gap = length / 2; gap > 0; gap /= 2) {
4426 for (int i = gap; i < length; i++) {
4427 for (int j = i - gap; j >= 0; j -= gap) {
4428 if (items [j].index >= items [j + gap].index) {
4429 CTableItem swap = items [j];
4430 items [j] = items [j + gap];
4431 items [j + gap] = swap;
4432 }
4433 }
4434 }
4435 }
4436 }
4437 void updateColumnWidth (CTableColumn column, int width) {
4438 headerHideToolTip ();
4439 int oldWidth = column.width;
4440 int columnX = column.getX ();
4441 int x = columnX + oldWidth - 1; /* -1 ensures that grid line is included */
4442
4443 update ();
4444 GC gc = new GC (this);
4445 gc.copyArea (x, 0, clientArea.width - x, clientArea.height, columnX + width - 1, 0); /* dest x -1 offsets x's -1 above */
4446 if (width > oldWidth) {
4447 /* column width grew */
4448 int change = width - oldWidth + 1; /* +1 offsets x's -1 above */
4449 /* -1/+1 below ensure that right bound of selection redraws correctly in column */
4450 redraw (x - 1, 0, change + 1, clientArea.height, false);
4451 } else {
4452 int change = oldWidth - width + 1; /* +1 offsets x's -1 above */
4453 redraw (clientArea.width - change, 0, change, clientArea.height, false);
4454 }
4455 /* the focus box must be repainted because its stipple may become shifted as a result of its new width */
4456 if (focusItem != null) redrawItem (focusItem.index, true);
4457
4458 GC headerGC = new GC (header);
4459 if (drawCount <= 0 && header.getVisible ()) {
4460 Rectangle headerBounds = header.getClientArea ();
4461 header.update ();
4462 x -= 1; /* -1 ensures that full header column separator is included */
4463 headerGC.copyArea (x, 0, headerBounds.width - x, headerBounds.height, columnX + width - 2, 0); /* dest x -2 offsets x's -1s above */
4464 if (width > oldWidth) {
4465 /* column width grew */
4466 int change = width - oldWidth + 2; /* +2 offsets x's -1s above */
4467 header.redraw (x, 0, change, headerBounds.height, false);
4468 } else {
4469 int change = oldWidth - width + 2; /* +2 offsets x's -1s above */
4470 header.redraw (headerBounds.width - change, 0, change, headerBounds.height, false);
4471 }
4472 }
4473
4474 column.width = width;
4475
4476 /*
4477 * Notify column and all items of column width change so that display labels
4478 * can be recomputed if needed.
4479 */
4480 column.updateWidth (headerGC);
4481 headerGC.dispose ();
4482 for (int i = 0; i < itemsCount; i++) {
4483 items [i].updateColumnWidth (column, gc);
4484 }
4485 gc.dispose ();
4486
4487 int maximum = 0;
4488 for (CTableColumn column2 : columns) {
4489 maximum += column2.width;
4490 }
4491 ScrollBar hBar = getHorizontalBar ();
4492 if (hBar != null) {
4493 hBar.setMaximum (Math.max (1, maximum)); /* setting a value of 0 here is ignored */
4494 if (hBar.getThumb () != clientArea.width) {
4495 hBar.setThumb (clientArea.width);
4496 hBar.setPageIncrement (clientArea.width);
4497 }
4498 int oldHorizontalOffset = horizontalOffset; /* hBar.setVisible() can modify horizontalOffset */
4499 hBar.setVisible (clientArea.width < maximum);
4500 int selection = hBar.getSelection ();
4501 if (selection != oldHorizontalOffset) {
4502 horizontalOffset = selection;
4503 redraw ();
4504 if (drawCount <= 0 && header.getVisible ()) header.redraw ();
4505 }
4506 }
4507
4508 column.notifyListeners (SWT.Resize, new Event ());
4509 CTableColumn[] orderedColumns = getOrderedColumns ();
4510 for (int i = column.getOrderIndex () + 1; i < orderedColumns.length; i++) {
4511 if (!orderedColumns [i].isDisposed ()) {
4512 orderedColumns [i].notifyListeners (SWT.Move, new Event ());
4513 }
4514 }
4515
4516 if (itemsCount == 0) redraw (); /* ensure that static focus rectangle updates properly */
4517 }
4518 /*
4519 * This is a naive implementation that computes the value from scratch.
4520 */
4521 void updateHorizontalBar () {
4522 if (drawCount > 0) return;
4523 ScrollBar hBar = getHorizontalBar ();
4524 if (hBar == null) return;
4525
4526 int maxX = 0;
4527 if (columns.length > 0) {
4528 for (CTableColumn column : columns) {
4529 maxX += column.width;
4530 }
4531 } else {
4532 for (int i = 0; i < itemsCount; i++) {
4533 Rectangle itemBounds = items [i].getCellBounds (0);
4534 maxX = Math.max (maxX, itemBounds.x + itemBounds.width + horizontalOffset);
4535 }
4536 }
4537
4538 int clientWidth = clientArea.width;
4539 if (maxX != hBar.getMaximum ()) {
4540 hBar.setMaximum (Math.max (1, maxX)); /* setting a value of 0 here is ignored */
4541 }
4542 int thumb = Math.min (clientWidth, maxX);
4543 if (thumb != hBar.getThumb ()) {
4544 hBar.setThumb (thumb);
4545 hBar.setPageIncrement (thumb);
4546 }
4547 hBar.setVisible (clientWidth < maxX);
4548
4549 /* reclaim any space now left on the right */
4550 if (maxX < horizontalOffset + thumb) {
4551 horizontalOffset = maxX - thumb;
4552 hBar.setSelection (horizontalOffset);
4553 redraw ();
4554 } else {
4555 int selection = hBar.getSelection ();
4556 if (selection != horizontalOffset) {
4557 horizontalOffset = selection;
4558 redraw ();
4559 }
4560 }
4561 }
4562 /*
4563 * Update the horizontal bar, if needed, in response to an item change (eg.- created,
4564 * disposed, expanded, etc.). newRightX is the new rightmost X value of the item,
4565 * and rightXchange is the change that led to the item's rightmost X value becoming
4566 * newRightX (so oldRightX + rightXchange = newRightX)
4567 */
4568 void updateHorizontalBar (int newRightX, int rightXchange) {
4569 if (drawCount > 0) return;
4570 ScrollBar hBar = getHorizontalBar ();
4571 if (hBar == null) return;
4572
4573 newRightX += horizontalOffset;
4574 int barMaximum = hBar.getMaximum ();
4575 if (newRightX > barMaximum) { /* item has extended beyond previous maximum */
4576 hBar.setMaximum (newRightX);
4577 int clientAreaWidth = clientArea.width;
4578 int thumb = Math.min (newRightX, clientAreaWidth);
4579 hBar.setThumb (thumb);
4580 hBar.setPageIncrement (thumb);
4581 hBar.setVisible (clientAreaWidth <= newRightX);
4582 return;
4583 }
4584
4585 int previousRightX = newRightX - rightXchange;
4586 if (previousRightX != barMaximum) {
4587 /* this was not the rightmost item, so just check for client width change */
4588 int clientAreaWidth = clientArea.width;
4589 int thumb = Math.min (barMaximum, clientAreaWidth);
4590 hBar.setThumb (thumb);
4591 hBar.setPageIncrement (thumb);
4592 hBar.setVisible (clientAreaWidth <= barMaximum);
4593 return;
4594 }
4595 updateHorizontalBar (); /* must search for the new rightmost item */
4596 }
4597 void updateVerticalBar () {
4598 if (drawCount > 0) return;
4599 ScrollBar vBar = getVerticalBar ();
4600 if (vBar == null) return;
4601
4602 int pageSize = (clientArea.height - getHeaderHeight ()) / itemHeight;
4603 int maximum = Math.max (1, itemsCount); /* setting a value of 0 here is ignored */
4604 if (maximum != vBar.getMaximum ()) {
4605 vBar.setMaximum (maximum);
4606 }
4607 int thumb = Math.min (pageSize, maximum);
4608 if (thumb != vBar.getThumb ()) {
4609 vBar.setThumb (thumb);
4610 vBar.setPageIncrement (thumb);
4611 }
4612 vBar.setVisible (pageSize < maximum);
4613
4614 /* reclaim any space now left on the bottom */
4615 if (maximum < topIndex + thumb) {
4616 topIndex = maximum - thumb;
4617 vBar.setSelection (topIndex);
4618 redraw ();
4619 } else {
4620 int selection = vBar.getSelection ();
4621 if (selection != topIndex) {
4622 topIndex = selection;
4623 redraw ();
4624 }
4625 }
4626 }
4627 }
4628