1 /*******************************************************************************
2 * Copyright (c) 2000, 2018 IBM Corporation and others.
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/
9 * SPDX-License-Identifier: EPL-2.0
12 * IBM Corporation - initial API and implementation
13 * Roland Oldenburg <r.oldenburg@hsp-software.de> - Bug 292199
14 * Conrad Groth - Bug 384906
15 *******************************************************************************/
16 package org.eclipse.swt.widgets;
21 import org.eclipse.swt.*;
22 import org.eclipse.swt.events.*;
23 import org.eclipse.swt.graphics.*;
24 import org.eclipse.swt.internal.*;
25 import org.eclipse.swt.internal.win32.*;
28 * Instances of this class implement a selectable user interface
29 * object that displays a list of images and strings and issues
30 * notification when selected.
32 * The item children that may be added to instances of this class
33 * must be of type <code>TableItem</code>.
35 * Style <code>VIRTUAL</code> is used to create a <code>Table</code> whose
36 * <code>TableItem</code>s are to be populated by the client on an on-demand basis
37 * instead of up-front. This can provide significant performance improvements for
38 * tables that are very large or for which <code>TableItem</code> population is
39 * expensive (for example, retrieving values from an external source).
41 * Here is an example of using a <code>Table</code> with style <code>VIRTUAL</code>:</p>
43 * final Table table = new Table (parent, SWT.VIRTUAL | SWT.BORDER);
44 * table.setItemCount (1000000);
45 * table.addListener (SWT.SetData, new Listener () {
46 * public void handleEvent (Event event) {
47 * TableItem item = (TableItem) event.item;
48 * int index = table.indexOf (item);
49 * item.setText ("Item " + index);
50 * System.out.println (item.getText ());
55 * Note that although this class is a subclass of <code>Composite</code>,
56 * it does not normally make sense to add <code>Control</code> children to
57 * it, or set a layout on it, unless implementing something like a cell
61 * <dt><b>Styles:</b></dt>
62 * <dd>SINGLE, MULTI, CHECK, FULL_SELECTION, HIDE_SELECTION, VIRTUAL, NO_SCROLL</dd>
63 * <dt><b>Events:</b></dt>
64 * <dd>Selection, DefaultSelection, SetData, MeasureItem, EraseItem, PaintItem</dd>
67 * Note: Only one of the styles SINGLE, and MULTI may be specified.
69 * IMPORTANT: This class is <em>not</em> intended to be subclassed.
72 * @see <a href="http://www.eclipse.org/swt/snippets/#table">Table, TableItem, TableColumn snippets</a>
73 * @see <a href="http://www.eclipse.org/swt/examples.php">SWT Example: ControlExample</a>
74 * @see <a href="http://www.eclipse.org/swt/">Sample code and further information</a>
75 * @noextend This class is not intended to be subclassed by clients.
77 public class Table extends Composite {
80 TableColumn [] columns;
81 int columnCount, customCount, keyCount;
82 ImageList imageList, headerImageList;
83 TableItem currentItem;
84 TableColumn sortColumn;
86 boolean [] columnVisible;
87 long headerToolTipHandle, hwndHeader;
88 boolean ignoreCustomDraw, ignoreDrawForeground, ignoreDrawBackground, ignoreDrawFocus, ignoreDrawSelection, ignoreDrawHot;
89 boolean customDraw, dragStarted, explorerTheme, firstColumnImage, fixScrollWidth, tipRequested, wasSelected, wasResized, painted;
90 boolean ignoreActivate, ignoreSelect, ignoreShrink, ignoreResize, ignoreColumnMove, ignoreColumnResize, fullRowSelect, settingItemHeight;
91 boolean headerItemDragging;
92 int itemHeight, lastIndexOf, lastWidth, sortDirection, resizeCount, selectionForeground, hotIndex;
93 int headerBackground = -1;
94 int headerForeground = -1;
95 static /*final*/ long HeaderProc;
96 static final int INSET = 4;
97 static final int GRID_WIDTH = 1;
98 static final int SORT_WIDTH = 10;
99 static final int HEADER_MARGIN = 12;
100 static final int HEADER_EXTRA = 3;
101 static final int VISTA_EXTRA = 2;
102 static final int EXPLORER_EXTRA = 2;
103 static final int H_SCROLL_LIMIT = 32;
104 static final int V_SCROLL_LIMIT = 16;
105 static final int DRAG_IMAGE_SIZE = 301;
106 static boolean COMPRESS_ITEMS = true;
107 static final long TableProc;
108 static final TCHAR TableClass = new TCHAR (0, OS.WC_LISTVIEW, true);
109 static final TCHAR HeaderClass = new TCHAR (0, OS.WC_HEADER, true);
111 WNDCLASS lpWndClass = new WNDCLASS ();
112 OS.GetClassInfo (0, TableClass, lpWndClass);
113 TableProc = lpWndClass.lpfnWndProc;
114 OS.GetClassInfo (0, HeaderClass, lpWndClass);
115 HeaderProc = lpWndClass.lpfnWndProc;
119 * Constructs a new instance of this class given its parent
120 * and a style value describing its behavior and appearance.
122 * The style value is either one of the style constants defined in
123 * class <code>SWT</code> which is applicable to instances of this
124 * class, or must be built by <em>bitwise OR</em>'ing together
125 * (that is, using the <code>int</code> "|" operator) two or more
126 * of those <code>SWT</code> style constants. The class description
127 * lists the style constants that are applicable to the class.
128 * Style bits are also inherited from superclasses.
131 * @param parent a composite control which will be the parent of the new instance (cannot be null)
132 * @param style the style of control to construct
134 * @exception IllegalArgumentException <ul>
135 * <li>ERROR_NULL_ARGUMENT - if the parent is null</li>
137 * @exception SWTException <ul>
138 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the parent</li>
139 * <li>ERROR_INVALID_SUBCLASS - if this class is not an allowed subclass</li>
145 * @see SWT#FULL_SELECTION
146 * @see SWT#HIDE_SELECTION
149 * @see Widget#checkSubclass
150 * @see Widget#getStyle
152 public Table (Composite parent, int style) {
153 super (parent, checkStyle (style));
157 void _addListener (int eventType, Listener listener) {
158 super._addListener (eventType, listener);
160 case SWT.MeasureItem:
163 setCustomDraw (true);
164 setBackgroundTransparent (true);
169 boolean _checkGrow (int count) {
170 //TODO - code could be shared but it would mix keyed and non-keyed logic
172 if (count == items.length) {
174 * Grow the array faster when redraw is off or the
175 * table is not visible. When the table is painted,
176 * the items array is resized to be smaller to reduce
179 boolean small = getDrawing () && OS.IsWindowVisible (handle);
180 int length = small ? items.length + 4 : Math.max (4, items.length * 3 / 2);
181 TableItem [] newItems = new TableItem [length];
182 System.arraycopy (items, 0, newItems, 0, items.length);
186 //TODO - don't shrink when count is very small (ie. 2 or 4 elements)?
187 //TODO - why? if setItemCount(1000000) is used after a shrink, then we won't compress
188 //TODO - get rid of ignoreShrink?
189 if (!ignoreShrink && keyCount > count / 2) {
190 boolean small = getDrawing () && OS.IsWindowVisible (handle);
191 int length = small ? count + 4 : Math.max (4, count * 3 / 2);
192 TableItem [] newItems = new TableItem [length];
193 for (int i=0; i<keyCount; i++) {
194 newItems [keys [i]] = items [i];
201 //TODO - grow by page size or screen height?
202 //TODO - experiment to determine an optimal growth rate for keys
203 if (keyCount == keys.length) {
204 boolean small = getDrawing () && OS.IsWindowVisible (handle);
205 int length = small ? keys.length + 4 : Math.max (4, keys.length * 3 / 2);
206 int [] newKeys = new int [length];
207 System.arraycopy (keys, 0, newKeys, 0, keys.length);
209 TableItem [] newItems = new TableItem [length];
210 System.arraycopy (items, 0, newItems, 0, items.length);
218 void _checkShrink () {
219 //TODO - code could be shared but it would mix keyed and non-keyed logic
220 //TODO - move ignoreShrink test back to the caller
223 /* Resize the item array to match the item count */
224 int count = (int)OS.SendMessage (handle, OS.LVM_GETITEMCOUNT, 0, 0);
227 * Bug in Windows. Call to OS.LVM_GETITEMCOUNT unexpectedly returns zero,
228 * leading to a possible "ArrayIndexOutOfBoundsException: 4" in SWT table.
229 * So, double check for any existing living items in the table and fixing
230 * the count value. Refer bug 292199.
232 if (count == 0 && items.length > 4) {
233 while (count<items.length && items[count] != null && !items[count].isDisposed()) {
238 if (items.length > 4 && items.length - count > 3) {
239 int length = Math.max (4, (count + 3) / 4 * 4);
240 TableItem [] newItems = new TableItem [length];
241 System.arraycopy (items, 0, newItems, 0, count);
247 if (keys.length > 4 && keys.length - keyCount > 3) {
248 int length = Math.max (4, (keyCount + 3) / 4 * 4);
249 int [] newKeys = new int [length];
250 System.arraycopy (keys, 0, newKeys, 0, keyCount);
252 TableItem [] newItems = new TableItem [length];
253 System.arraycopy (items, 0, newItems, 0, keyCount);
260 void _clearItems () {
266 TableItem _getItem (int index) {
267 return _getItem (index, true);
270 //TODO - check senders who have count (watch methods that change the count)
271 TableItem _getItem (int index, boolean create) {
272 return _getItem (index, create, -1);
275 TableItem _getItem (int index, boolean create, int count) {
276 //TODO - code could be shared but it would mix keyed and non-keyed logic
278 if (index >= items.length) return null;
279 if ((style & SWT.VIRTUAL) == 0 || !create) return items [index];
280 if (items [index] != null) return items [index];
281 return items [index] = new TableItem (this, SWT.NONE, -1, false);
283 if ((style & SWT.VIRTUAL) == 0 || !create) {
284 if (keyCount == 0) return null;
285 if (index > keys [keyCount - 1]) return null;
287 int keyIndex = binarySearch (keys, 0, keyCount, index);
288 if ((style & SWT.VIRTUAL) == 0 || !create) {
289 return keyIndex < 0 ? null : items [keyIndex];
293 count = (int)OS.SendMessage (handle, OS.LVM_GETITEMCOUNT, 0, 0);
295 //TODO - _checkGrow() doesn't return a value, check keys == null instead
296 if (_checkGrow (count)) {
297 if (items [index] != null) return items [index];
298 return items [index] = new TableItem (this, SWT.NONE, -1, false);
300 keyIndex = -keyIndex - 1;
301 if (keyIndex < keyCount) {
302 System.arraycopy(keys, keyIndex, keys, keyIndex + 1, keyCount - keyIndex);
303 System.arraycopy(items, keyIndex, items, keyIndex + 1, keyCount - keyIndex);
306 keys [keyIndex] = index;
308 if (items [keyIndex] != null) return items [keyIndex];
310 return items [keyIndex] = new TableItem (this, SWT.NONE, -1, false);
314 void _getItems (TableItem [] result, int count) {
316 System.arraycopy (items, 0, result, 0, count);
318 /* NOTE: Null items will be in the array when keyCount != count */
319 for (int i=0; i<keyCount; i++) {
320 if (keys [i] >= count) break;
321 result [keys [i]] = items [keys [i]];
326 boolean _hasItems () {
327 return items != null;
331 items = new TableItem [4];
332 if (COMPRESS_ITEMS) {
333 if ((style & SWT.VIRTUAL) != 0) {
340 /* NOTE: The array has already been grown to have space for the new item */
341 void _insertItem (int index, TableItem item, int count) {
343 System.arraycopy (items, index, items, index + 1, count - index);
344 items [index] = item;
346 int keyIndex = binarySearch (keys, 0, keyCount, index);
347 if (keyIndex < 0) keyIndex = -keyIndex - 1;
348 System.arraycopy(keys, keyIndex, keys, keyIndex + 1, keyCount - keyIndex);
349 keys [keyIndex] = index;
350 System.arraycopy(items, keyIndex, items, keyIndex + 1, keyCount - keyIndex);
351 items [keyIndex] = item;
353 for (int i=keyIndex + 1; i<keyCount; i++) keys[i]++;
357 void _removeItem (int index, int count) {
359 System.arraycopy (items, index + 1, items, index, --count - index);
360 items [count] = null;
362 int keyIndex = binarySearch (keys, 0, keyCount, index);
364 keyIndex = -keyIndex - 1;
367 System.arraycopy (keys, keyIndex + 1, keys, keyIndex, keyCount - keyIndex);
369 System.arraycopy (items, keyIndex + 1, items, keyIndex, keyCount - keyIndex);
370 items [keyCount] = null;
372 for (int i=keyIndex; i<keyCount; i++) --keys[i];
376 /* NOTE: Removes from start to index - 1 */
377 void _removeItems (int start, int index, int count) {
379 System.arraycopy (items, index, items, start, count - index);
380 for (int i=count-(index-start); i<count; i++) items [i] = null;
383 int left = binarySearch (keys, 0, keyCount, start);
384 if (left < 0) left = -left - 1;
385 int right = binarySearch (keys, left, keyCount, end);
386 if (right < 0) right = -right - 1;
387 //TODO - optimize when left and right are the same
388 System.arraycopy (keys, right, keys, left, keyCount - right);
389 for (int i=keyCount-(right-left); i<keyCount; i++) keys [i] = 0;
390 System.arraycopy (items, right, items, left, keyCount - right);
391 for (int i=keyCount-(right-left); i<keyCount; i++) items [i] = null;
392 keyCount -= (right - left);
393 for (int i=left; i<keyCount; i++) keys[i] -= (right - left);
397 void _setItemCount (int count, int itemCount) {
399 int length = Math.max (4, (count + 3) / 4 * 4);
400 TableItem [] newItems = new TableItem [length];
401 System.arraycopy (items, 0, newItems, 0, Math.min (count, itemCount));
404 int index = Math.min (count, itemCount);
405 keyCount = binarySearch (keys, 0, keyCount, index);
406 if (keyCount < 0) keyCount = -keyCount - 1;
407 int length = Math.max (4, (keyCount + 3) / 4 * 4);
408 int [] newKeys = new int [length];
409 System.arraycopy (keys, 0, newKeys, 0, keyCount);
411 TableItem [] newItems = new TableItem [length];
412 System.arraycopy (items, 0, newItems, 0, keyCount);
418 * Adds the listener to the collection of listeners who will
419 * be notified when the user changes the receiver's selection, by sending
420 * it one of the messages defined in the <code>SelectionListener</code>
423 * When <code>widgetSelected</code> is called, the item field of the event object is valid.
424 * If the receiver has the <code>SWT.CHECK</code> style and the check selection changes,
425 * the event object detail field contains the value <code>SWT.CHECK</code>.
426 * <code>widgetDefaultSelected</code> is typically called when an item is double-clicked.
427 * The item field of the event object is valid for default selection, but the detail field is not used.
430 * @param listener the listener which should be notified when the user changes the receiver's selection
432 * @exception IllegalArgumentException <ul>
433 * <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
435 * @exception SWTException <ul>
436 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
437 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
440 * @see SelectionListener
441 * @see #removeSelectionListener
442 * @see SelectionEvent
444 public void addSelectionListener (SelectionListener listener) {
446 if (listener == null) error (SWT.ERROR_NULL_ARGUMENT);
447 TypedListener typedListener = new TypedListener (listener);
448 addListener (SWT.Selection,typedListener);
449 addListener (SWT.DefaultSelection,typedListener);
453 long callWindowProc (long hwnd, int msg, long wParam, long lParam) {
454 return callWindowProc (hwnd, msg, wParam, lParam, false);
457 long callWindowProc (long hwnd, int msg, long wParam, long lParam, boolean forceSelect) {
458 if (handle == 0) return 0;
459 if (hwndHeader != 0 && hwnd == hwndHeader) {
460 return OS.CallWindowProc (HeaderProc, hwnd, msg, wParam, lParam);
463 boolean checkSelection = false, checkActivate = false, redraw = false;
465 /* Keyboard messages */
467 * Feature in Windows. Windows sends LVN_ITEMACTIVATE from WM_KEYDOWN
468 * instead of WM_CHAR. This means that application code that expects
469 * to consume the key press and therefore avoid a SWT.DefaultSelection
470 * event will fail. The fix is to ignore LVN_ITEMACTIVATE when it is
471 * caused by WM_KEYDOWN and send SWT.DefaultSelection from WM_CHAR.
474 checkActivate = true;
480 case OS.WM_SYSKEYDOWN:
484 /* Scroll messages */
489 /* Resize messages */
490 case OS.WM_WINDOWPOSCHANGED:
491 redraw = findImageControl () != null && getDrawing () && OS.IsWindowVisible (handle);
494 * Feature in Windows. When LVM_SETBKCOLOR is used with CLR_NONE
495 * to make the background of the table transparent, drawing becomes
496 * slow. The fix is to temporarily clear CLR_NONE when redraw is
499 OS.DefWindowProc (handle, OS.WM_SETREDRAW, 0, 0);
500 OS.SendMessage (handle, OS.LVM_SETBKCOLOR, 0, 0xFFFFFF);
505 case OS.WM_LBUTTONDBLCLK:
506 case OS.WM_LBUTTONDOWN:
507 case OS.WM_LBUTTONUP:
508 case OS.WM_MBUTTONDBLCLK:
509 case OS.WM_MBUTTONDOWN:
510 case OS.WM_MBUTTONUP:
511 case OS.WM_MOUSEHOVER:
512 case OS.WM_MOUSELEAVE:
513 case OS.WM_MOUSEMOVE:
514 case OS.WM_MOUSEWHEEL:
515 case OS.WM_RBUTTONDBLCLK:
516 case OS.WM_RBUTTONDOWN:
517 case OS.WM_RBUTTONUP:
518 case OS.WM_XBUTTONDBLCLK:
519 case OS.WM_XBUTTONDOWN:
520 case OS.WM_XBUTTONUP:
521 checkSelection = true;
527 if (findImageControl () != null) {
528 topIndex = (int)OS.SendMessage (handle, OS.LVM_GETTOPINDEX, 0, 0);
532 boolean oldSelected = wasSelected;
533 if (checkSelection) wasSelected = false;
534 if (checkActivate) ignoreActivate = true;
537 * Bug in Windows. For some reason, when the WS_EX_COMPOSITED
538 * style is set in a parent of a table and the header is visible,
539 * Windows issues an endless stream of WM_PAINT messages. The
540 * fix is to call BeginPaint() and EndPaint() outside of WM_PAINT
541 * and pass the paint HDC in to the window proc.
543 boolean fixPaint = false;
544 if (msg == OS.WM_PAINT) {
545 int bits0 = OS.GetWindowLong (handle, OS.GWL_STYLE);
546 if ((bits0 & OS.LVS_NOCOLUMNHEADER) == 0) {
547 long hwndParent = OS.GetParent (handle), hwndOwner = 0;
548 while (hwndParent != 0) {
549 int bits1 = OS.GetWindowLong (hwndParent, OS.GWL_EXSTYLE);
550 if ((bits1 & OS.WS_EX_COMPOSITED) != 0) {
554 hwndOwner = OS.GetWindow (hwndParent, OS.GW_OWNER);
555 if (hwndOwner != 0) break;
556 hwndParent = OS.GetParent (hwndParent);
561 /* Remove the scroll bars that Windows keeps automatically adding */
562 boolean fixScroll = false;
563 if ((style & SWT.H_SCROLL) == 0 || (style & SWT.V_SCROLL) == 0) {
567 case OS.WM_WINDOWPOSCHANGING: {
568 int bits = OS.GetWindowLong (hwnd, OS.GWL_STYLE);
569 if ((style & SWT.H_SCROLL) == 0 && (bits & OS.WS_HSCROLL) != 0) {
571 bits &= ~OS.WS_HSCROLL;
573 if ((style & SWT.V_SCROLL) == 0 && (bits & OS.WS_VSCROLL) != 0) {
575 bits &= ~OS.WS_VSCROLL;
577 if (fixScroll) OS.SetWindowLong (handle, OS.GWL_STYLE, bits);
583 PAINTSTRUCT ps = new PAINTSTRUCT ();
584 long hDC = OS.BeginPaint (hwnd, ps);
585 code = OS.CallWindowProc (TableProc, hwnd, OS.WM_PAINT, hDC, lParam);
586 OS.EndPaint (hwnd, ps);
588 code = OS.CallWindowProc (TableProc, hwnd, msg, wParam, lParam);
591 int flags = OS.RDW_FRAME | OS.RDW_INVALIDATE;
592 OS.RedrawWindow (handle, null, 0, flags);
595 if (checkActivate) ignoreActivate = false;
596 if (checkSelection) {
597 if (wasSelected || forceSelect) {
598 Event event = new Event ();
599 int index = (int)OS.SendMessage (handle, OS.LVM_GETNEXTITEM, -1, OS.LVNI_FOCUSED);
600 if (index != -1) event.item = _getItem (index);
601 sendSelectionEvent (SWT.Selection, event, false);
603 wasSelected = oldSelected;
606 /* Keyboard messages */
612 case OS.WM_SYSKEYDOWN:
616 /* Scroll messages */
621 /* Resize messages */
622 case OS.WM_WINDOWPOSCHANGED:
624 OS.SendMessage (handle, OS.LVM_SETBKCOLOR, 0, OS.CLR_NONE);
625 OS.DefWindowProc (handle, OS.WM_SETREDRAW, 1, 0);
626 OS.InvalidateRect (handle, null, true);
627 long hwndHeader = OS.SendMessage (handle, OS.LVM_GETHEADER, 0, 0);
628 if (hwndHeader != 0) OS.InvalidateRect (hwndHeader, null, true);
633 case OS.WM_LBUTTONDBLCLK:
634 case OS.WM_LBUTTONDOWN:
635 case OS.WM_LBUTTONUP:
636 case OS.WM_MBUTTONDBLCLK:
637 case OS.WM_MBUTTONDOWN:
638 case OS.WM_MBUTTONUP:
639 case OS.WM_MOUSEHOVER:
640 case OS.WM_MOUSELEAVE:
641 case OS.WM_MOUSEMOVE:
642 case OS.WM_MOUSEWHEEL:
643 case OS.WM_RBUTTONDBLCLK:
644 case OS.WM_RBUTTONDOWN:
645 case OS.WM_RBUTTONUP:
646 case OS.WM_XBUTTONDBLCLK:
647 case OS.WM_XBUTTONDOWN:
648 case OS.WM_XBUTTONUP:
654 if (findImageControl () != null) {
655 if (topIndex != OS.SendMessage (handle, OS.LVM_GETTOPINDEX, 0, 0)) {
656 OS.InvalidateRect (handle, null, true);
669 static int checkStyle (int style) {
671 * Feature in Windows. Even when WS_HSCROLL or
672 * WS_VSCROLL is not specified, Windows creates
673 * trees and tables with scroll bars. The fix
674 * is to set H_SCROLL and V_SCROLL.
676 * NOTE: This code appears on all platforms so that
677 * applications have consistent scroll bar behavior.
679 if ((style & SWT.NO_SCROLL) == 0) {
680 style |= SWT.H_SCROLL | SWT.V_SCROLL;
682 return checkBits (style, SWT.SINGLE, SWT.MULTI, 0, 0, 0, 0);
685 LRESULT CDDS_ITEMPOSTPAINT (NMLVCUSTOMDRAW nmcd, long wParam, long lParam) {
687 if (explorerTheme && !ignoreCustomDraw) {
689 if (hooks (SWT.EraseItem) && nmcd.left != nmcd.right) {
690 OS.RestoreDC (hDC, -1);
694 * Bug in Windows. When the table has the extended style
695 * LVS_EX_FULLROWSELECT and LVM_SETBKCOLOR is used with
696 * CLR_NONE to make the table transparent, Windows fills
697 * a black rectangle around any column that contains an
698 * image. The fix is clear LVS_EX_FULLROWSELECT during
701 * NOTE: Since CDIS_FOCUS is cleared during custom draw,
702 * it is necessary to draw the focus rectangle after the
703 * item has been drawn.
705 if (!ignoreCustomDraw && !ignoreDrawFocus && nmcd.left != nmcd.right) {
706 if (OS.IsWindowVisible (handle) && OS.IsWindowEnabled (handle)) {
707 if (!explorerTheme && (style & SWT.FULL_SELECTION) != 0) {
708 if ((int)OS.SendMessage (handle, OS.LVM_GETBKCOLOR, 0, 0) == OS.CLR_NONE) {
709 int dwExStyle = (int)OS.SendMessage (handle, OS.LVM_GETEXTENDEDLISTVIEWSTYLE, 0, 0);
710 if ((dwExStyle & OS.LVS_EX_FULLROWSELECT) == 0) {
711 // if ((nmcd.uItemState & OS.CDIS_FOCUS) != 0) {
712 if (OS.SendMessage (handle, OS.LVM_GETNEXTITEM, -1, OS.LVNI_FOCUSED) == nmcd.dwItemSpec) {
713 if (handle == OS.GetFocus ()) {
714 int uiState = (int)OS.SendMessage (handle, OS.WM_QUERYUISTATE, 0, 0);
715 if ((uiState & OS.UISF_HIDEFOCUS) == 0) {
716 RECT rect = new RECT ();
717 rect.left = OS.LVIR_BOUNDS;
718 boolean oldIgnore = ignoreCustomDraw;
719 ignoreCustomDraw = true;
720 OS.SendMessage (handle, OS. LVM_GETITEMRECT, nmcd.dwItemSpec, rect);
721 long hwndHeader = OS.SendMessage (handle, OS.LVM_GETHEADER, 0, 0);
722 int index = (int)OS.SendMessage (hwndHeader, OS.HDM_ORDERTOINDEX, 0, 0);
723 RECT itemRect = new RECT ();
725 itemRect.left = OS.LVIR_LABEL;
726 OS.SendMessage (handle, OS. LVM_GETITEMRECT, index, itemRect);
728 itemRect.top = index;
729 itemRect.left = OS.LVIR_ICON;
730 OS.SendMessage (handle, OS. LVM_GETSUBITEMRECT, nmcd.dwItemSpec, itemRect);
732 ignoreCustomDraw = oldIgnore;
733 rect.left = itemRect.left;
734 OS.DrawFocusRect (nmcd.hdc, rect);
746 LRESULT CDDS_ITEMPREPAINT (NMLVCUSTOMDRAW nmcd, long wParam, long lParam) {
748 * Bug in Windows. When the table has the extended style
749 * LVS_EX_FULLROWSELECT and LVM_SETBKCOLOR is used with
750 * CLR_NONE to make the table transparent, Windows fills
751 * a black rectangle around any column that contains an
752 * image. The fix is clear LVS_EX_FULLROWSELECT during
755 * NOTE: It is also necessary to clear CDIS_FOCUS to stop
756 * the table from drawing the focus rectangle around the
757 * first item instead of the full row.
759 if (!ignoreCustomDraw) {
760 if (OS.IsWindowVisible (handle) && OS.IsWindowEnabled (handle)) {
761 if (!explorerTheme && (style & SWT.FULL_SELECTION) != 0) {
762 if ((int)OS.SendMessage (handle, OS.LVM_GETBKCOLOR, 0, 0) == OS.CLR_NONE) {
763 int dwExStyle = (int)OS.SendMessage (handle, OS.LVM_GETEXTENDEDLISTVIEWSTYLE, 0, 0);
764 if ((dwExStyle & OS.LVS_EX_FULLROWSELECT) == 0) {
765 if ((nmcd.uItemState & OS.CDIS_FOCUS) != 0) {
766 nmcd.uItemState &= ~OS.CDIS_FOCUS;
767 OS.MoveMemory (lParam, nmcd, NMLVCUSTOMDRAW.sizeof);
774 if (explorerTheme && !ignoreCustomDraw) {
775 hotIndex = (nmcd.uItemState & OS.CDIS_HOT) != 0 ? (int)nmcd.dwItemSpec : -1;
776 if (hooks (SWT.EraseItem) && nmcd.left != nmcd.right) {
777 OS.SaveDC (nmcd.hdc);
778 long hrgn = OS.CreateRectRgn (0, 0, 0, 0);
779 OS.SelectClipRgn (nmcd.hdc, hrgn);
780 OS.DeleteObject (hrgn);
783 return new LRESULT (OS.CDRF_NOTIFYSUBITEMDRAW | OS.CDRF_NOTIFYPOSTPAINT);
786 LRESULT CDDS_POSTPAINT (NMLVCUSTOMDRAW nmcd, long wParam, long lParam) {
787 if (ignoreCustomDraw) return null;
789 * Bug in Windows. When the table has the extended style
790 * LVS_EX_FULLROWSELECT and LVM_SETBKCOLOR is used with
791 * CLR_NONE to make the table transparent, Windows fills
792 * a black rectangle around any column that contains an
793 * image. The fix is clear LVS_EX_FULLROWSELECT during
796 if (--customCount == 0 && OS.IsWindowVisible (handle)) {
797 if (!explorerTheme && (style & SWT.FULL_SELECTION) != 0) {
798 if ((int)OS.SendMessage (handle, OS.LVM_GETBKCOLOR, 0, 0) == OS.CLR_NONE) {
799 int dwExStyle = (int)OS.SendMessage (handle, OS.LVM_GETEXTENDEDLISTVIEWSTYLE, 0, 0);
800 if ((dwExStyle & OS.LVS_EX_FULLROWSELECT) == 0) {
801 int bits = OS.LVS_EX_FULLROWSELECT;
803 * Feature in Windows. When LVM_SETEXTENDEDLISTVIEWSTYLE is
804 * used to set or clear the extended style bits and the table
805 * has a tooltip, the tooltip is hidden. The fix is to clear
806 * the tooltip before setting the bits and then reset it.
808 long hwndToolTip = OS.SendMessage (handle, OS.LVM_SETTOOLTIPS, 0, 0);
809 long rgn = OS.CreateRectRgn (0, 0, 0, 0);
810 int result = OS.GetUpdateRgn (handle, rgn, true);
811 OS.SendMessage (handle, OS.LVM_SETEXTENDEDLISTVIEWSTYLE, bits, bits);
812 OS.ValidateRect (handle, null);
813 if (result != OS.NULLREGION) OS.InvalidateRgn (handle, rgn, true);
814 OS.DeleteObject (rgn);
816 * Bug in Windows. Despite the documentation, LVM_SETTOOLTIPS
817 * uses WPARAM instead of LPARAM for the new tooltip The fix
818 * is to put the tooltip in both parameters.
820 hwndToolTip = OS.SendMessage (handle, OS.LVM_SETTOOLTIPS, hwndToolTip, hwndToolTip);
828 LRESULT CDDS_PREPAINT (NMLVCUSTOMDRAW nmcd, long wParam, long lParam) {
829 if (ignoreCustomDraw) {
830 return new LRESULT (OS.CDRF_NOTIFYITEMDRAW | OS.CDRF_NOTIFYPOSTPAINT);
833 * Bug in Windows. When the table has the extended style
834 * LVS_EX_FULLROWSELECT and LVM_SETBKCOLOR is used with
835 * CLR_NONE to make the table transparent, Windows fills
836 * a black rectangle around any column that contains an
837 * image. The fix is clear LVS_EX_FULLROWSELECT during
840 if (customCount++ == 0 && OS.IsWindowVisible (handle)) {
841 if (!explorerTheme && (style & SWT.FULL_SELECTION) != 0) {
842 if ((int)OS.SendMessage (handle, OS.LVM_GETBKCOLOR, 0, 0) == OS.CLR_NONE) {
843 int dwExStyle = (int)OS.SendMessage (handle, OS.LVM_GETEXTENDEDLISTVIEWSTYLE, 0, 0);
844 if ((dwExStyle & OS.LVS_EX_FULLROWSELECT) != 0) {
845 int bits = OS.LVS_EX_FULLROWSELECT;
847 * Feature in Windows. When LVM_SETEXTENDEDLISTVIEWSTYLE is
848 * used to set or clear the extended style bits and the table
849 * has a tooltip, the tooltip is hidden. The fix is to clear
850 * the tooltip before setting the bits and then reset it.
852 long hwndToolTip = OS.SendMessage (handle, OS.LVM_SETTOOLTIPS, 0, 0);
853 long rgn = OS.CreateRectRgn (0, 0, 0, 0);
854 int result = OS.GetUpdateRgn (handle, rgn, true);
855 OS.SendMessage (handle, OS.LVM_SETEXTENDEDLISTVIEWSTYLE, bits, 0);
856 OS.ValidateRect (handle, null);
857 if (result != OS.NULLREGION) OS.InvalidateRgn (handle, rgn, true);
858 OS.DeleteObject (rgn);
860 * Bug in Windows. Despite the documentation, LVM_SETTOOLTIPS
861 * uses WPARAM instead of LPARAM for the new tooltip The fix
862 * is to put the tooltip in both parameters.
864 hwndToolTip = OS.SendMessage (handle, OS.LVM_SETTOOLTIPS, hwndToolTip, hwndToolTip);
869 if (OS.IsWindowVisible (handle)) {
871 * Feature in Windows. On Vista using the explorer theme,
872 * Windows draws a vertical line to separate columns. When
873 * there is only a single column, the line looks strange.
874 * The fix is to draw the background using custom draw.
876 RECT rect = new RECT ();
877 OS.SetRect (rect, nmcd.left, nmcd.top, nmcd.right, nmcd.bottom);
878 if (explorerTheme && columnCount == 0) {
880 if (OS.IsWindowEnabled (handle) || findImageControl () != null) {
881 drawBackground (hDC, rect);
883 fillBackground (hDC, OS.GetSysColor (OS.COLOR_3DFACE), rect);
886 Control control = findBackgroundControl ();
887 if (control != null && control.backgroundImage != null) {
888 fillImageBackground (nmcd.hdc, control, rect, 0, 0);
890 final boolean enabled = OS.IsWindowEnabled (handle);
891 if (enabled && (int)OS.SendMessage (handle, OS.LVM_GETBKCOLOR, 0, 0) == OS.CLR_NONE || !enabled && hasCustomBackground()) {
892 if (control == null) control = this;
893 fillBackground (nmcd.hdc, control.getBackgroundPixel (), rect);
894 if (OS.IsAppThemed ()) {
895 if (sortColumn != null && sortDirection != SWT.NONE) {
896 int index = indexOf (sortColumn);
898 parent.forceResize ();
899 int clrSortBk = getSortColumnPixel ();
900 RECT columnRect = new RECT (), headerRect = new RECT ();
901 OS.GetClientRect (handle, columnRect);
902 long hwndHeader = OS.SendMessage (handle, OS.LVM_GETHEADER, 0, 0);
903 if (OS.SendMessage (hwndHeader, OS.HDM_GETITEMRECT, index, headerRect) != 0) {
904 OS.MapWindowPoints (hwndHeader, handle, headerRect, 2);
905 columnRect.left = headerRect.left;
906 columnRect.right = headerRect.right;
907 if (OS.IntersectRect(columnRect, columnRect, rect)) {
908 fillBackground (nmcd.hdc, clrSortBk, columnRect);
918 return new LRESULT (OS.CDRF_NOTIFYITEMDRAW | OS.CDRF_NOTIFYPOSTPAINT);
921 LRESULT CDDS_SUBITEMPOSTPAINT (NMLVCUSTOMDRAW nmcd, long wParam, long lParam) {
922 if (ignoreCustomDraw) return null;
923 if (nmcd.left == nmcd.right) return new LRESULT (OS.CDRF_DODEFAULT);
925 if (ignoreDrawForeground) OS.RestoreDC (hDC, -1);
926 if (OS.IsWindowVisible (handle)) {
928 * Feature in Windows. When there is a sort column, the sort column
929 * color draws on top of the background color for an item. The fix
930 * is to clear the sort column in CDDS_SUBITEMPREPAINT, and reset it
931 * in CDDS_SUBITEMPOSTPAINT.
933 * Update region is saved and restored around LVM_SETSELECTEDCOLUMN
934 * to prevent infinite WM_PAINT on Vista.
936 if ((int)OS.SendMessage (handle, OS.LVM_GETBKCOLOR, 0, 0) != OS.CLR_NONE) {
937 if ((sortDirection & (SWT.UP | SWT.DOWN)) != 0) {
938 if (sortColumn != null && !sortColumn.isDisposed ()) {
939 int oldColumn = (int)OS.SendMessage (handle, OS.LVM_GETSELECTEDCOLUMN, 0, 0);
940 if (oldColumn == -1) {
941 int newColumn = indexOf (sortColumn);
942 long rgn = OS.CreateRectRgn (0, 0, 0, 0);
943 int result = OS.GetUpdateRgn (handle, rgn, true);
944 OS.SendMessage (handle, OS.LVM_SETSELECTEDCOLUMN, newColumn, 0);
945 OS.ValidateRect (handle, null);
946 if (result != OS.NULLREGION) OS.InvalidateRgn (handle, rgn, true);
947 OS.DeleteObject (rgn);
952 if (hooks (SWT.PaintItem)) {
953 TableItem item = _getItem ((int)nmcd.dwItemSpec);
954 sendPaintItemEvent (item, nmcd);
955 //widget could be disposed at this point
957 if (!ignoreDrawFocus && focusRect != null) {
958 OS.SetTextColor (nmcd.hdc, 0);
959 OS.SetBkColor (nmcd.hdc, 0xFFFFFF);
960 OS.DrawFocusRect (nmcd.hdc, focusRect);
967 LRESULT CDDS_SUBITEMPREPAINT (NMLVCUSTOMDRAW nmcd, long wParam, long lParam) {
969 if (explorerTheme && !ignoreCustomDraw && hooks (SWT.EraseItem) && (nmcd.left != nmcd.right)) {
970 OS.RestoreDC (hDC, -1);
973 * Feature in Windows. When a new table item is inserted
974 * using LVM_INSERTITEM in a table that is transparent
975 * (ie. LVM_SETBKCOLOR has been called with CLR_NONE),
976 * TVM_INSERTITEM calls NM_CUSTOMDRAW before the new item
977 * has been added to the array. The fix is to check for
980 * NOTE: Force the item to be created if it does not exist.
982 TableItem item = _getItem ((int)nmcd.dwItemSpec);
983 if (item == null || item.isDisposed ()) return null;
984 long hFont = item.fontHandle (nmcd.iSubItem);
985 if (hFont != -1) OS.SelectObject (hDC, hFont);
986 if (ignoreCustomDraw || (nmcd.left == nmcd.right)) {
987 return new LRESULT (hFont == -1 ? OS.CDRF_DODEFAULT : OS.CDRF_NEWFONT);
989 int code = OS.CDRF_DODEFAULT;
990 selectionForeground = -1;
991 ignoreDrawForeground = ignoreDrawSelection = ignoreDrawFocus = ignoreDrawBackground = false;
992 if (OS.IsWindowVisible (handle)) {
993 Event measureEvent = null;
994 if (hooks (SWT.MeasureItem)) {
995 measureEvent = sendMeasureItemEvent (item, (int)nmcd.dwItemSpec, nmcd.iSubItem, nmcd.hdc);
996 if (isDisposed () || item.isDisposed ()) return null;
998 if (hooks (SWT.EraseItem)) {
999 sendEraseItemEvent (item, nmcd, lParam, measureEvent);
1000 if (isDisposed () || item.isDisposed ()) return null;
1001 code |= OS.CDRF_NOTIFYPOSTPAINT;
1003 if (ignoreDrawForeground || hooks (SWT.PaintItem)) code |= OS.CDRF_NOTIFYPOSTPAINT;
1005 int clrText = item.cellForeground != null ? item.cellForeground [nmcd.iSubItem] : -1;
1006 if (clrText == -1) clrText = item.foreground;
1007 int clrTextBk = item.cellBackground != null ? item.cellBackground [nmcd.iSubItem] : -1;
1008 if (clrTextBk == -1) clrTextBk = item.background;
1009 if (selectionForeground != -1) clrText = selectionForeground;
1011 * Bug in Windows. When the table has the extended style
1012 * LVS_EX_FULLROWSELECT and LVM_SETBKCOLOR is used with
1013 * CLR_NONE to make the table transparent, Windows draws
1014 * a black rectangle around any column that contains an
1015 * image. The fix is emulate LVS_EX_FULLROWSELECT by
1016 * drawing the selection.
1018 final boolean enabled = OS.IsWindowEnabled (handle);
1019 if (OS.IsWindowVisible (handle) && enabled) {
1020 if (!explorerTheme && !ignoreDrawSelection && (style & SWT.FULL_SELECTION) != 0) {
1021 int bits = (int)OS.SendMessage (handle, OS.LVM_GETEXTENDEDLISTVIEWSTYLE, 0, 0);
1022 if ((bits & OS.LVS_EX_FULLROWSELECT) == 0) {
1024 * Bug in Windows. For some reason, CDIS_SELECTED always set,
1025 * even for items that are not selected. The fix is to get
1026 * the selection state from the item.
1028 LVITEM lvItem = new LVITEM ();
1029 lvItem.mask = OS.LVIF_STATE;
1030 lvItem.stateMask = OS.LVIS_SELECTED;
1031 lvItem.iItem = (int)nmcd.dwItemSpec;
1032 long result = OS.SendMessage (handle, OS.LVM_GETITEM, 0, lvItem);
1033 if ((result != 0 && (lvItem.state & OS.LVIS_SELECTED) != 0)) {
1034 int clrSelection = -1;
1035 if (nmcd.iSubItem == 0) {
1036 if (OS.GetFocus () == handle || display.getHighContrast ()) {
1037 clrSelection = OS.GetSysColor (OS.COLOR_HIGHLIGHT);
1039 if ((style & SWT.HIDE_SELECTION) == 0) {
1040 clrSelection = OS.GetSysColor (OS.COLOR_3DFACE);
1044 if (OS.GetFocus () == handle || display.getHighContrast ()) {
1045 clrText = OS.GetSysColor (OS.COLOR_HIGHLIGHTTEXT);
1046 clrTextBk = clrSelection = OS.GetSysColor (OS.COLOR_HIGHLIGHT);
1048 if ((style & SWT.HIDE_SELECTION) == 0) {
1049 clrTextBk = clrSelection = OS.GetSysColor (OS.COLOR_3DFACE);
1053 if (clrSelection != -1) {
1054 RECT rect = item.getBounds ((int)nmcd.dwItemSpec, nmcd.iSubItem, true, nmcd.iSubItem != 0, true, false, hDC);
1055 fillBackground (hDC, clrSelection, rect);
1061 if (!ignoreDrawForeground) {
1063 * Bug in Windows. When the attributes are for one cell in a table,
1064 * Windows does not reset them for the next cell. As a result, all
1065 * subsequent cells are drawn using the previous font, foreground and
1066 * background colors. The fix is to set the all attributes when any
1067 * attribute could have changed.
1069 boolean hasAttributes = true;
1070 if (hFont == -1 && clrText == -1 && clrTextBk == -1) {
1071 if (item.cellForeground == null && item.cellBackground == null && item.cellFont == null) {
1072 int count = (int)OS.SendMessage (hwndHeader, OS.HDM_GETITEMCOUNT, 0, 0);
1073 if (count == 1) hasAttributes = false;
1076 if (hasAttributes) {
1077 if (hFont == -1) hFont = OS.SendMessage (handle, OS.WM_GETFONT, 0, 0);
1078 OS.SelectObject (hDC, hFont);
1080 nmcd.clrText = clrText == -1 ? getForegroundPixel () : clrText;
1081 if (clrTextBk == -1) {
1082 nmcd.clrTextBk = OS.CLR_NONE;
1083 if (selectionForeground == -1) {
1084 Control control = findBackgroundControl ();
1085 if (control == null) control = this;
1086 if (control.backgroundImage == null) {
1087 if ((int)OS.SendMessage (handle, OS.LVM_GETBKCOLOR, 0, 0) != OS.CLR_NONE) {
1088 nmcd.clrTextBk = control.getBackgroundPixel ();
1093 nmcd.clrTextBk = selectionForeground != -1 ? OS.CLR_NONE : clrTextBk;
1095 OS.MoveMemory (lParam, nmcd, NMLVCUSTOMDRAW.sizeof);
1097 code |= OS.CDRF_NEWFONT;
1101 * Feature in Windows. When there is a sort column, the sort column
1102 * color draws on top of the background color for an item. The fix
1103 * is to clear the sort column in CDDS_SUBITEMPREPAINT, and reset it
1104 * in CDDS_SUBITEMPOSTPAINT.
1106 * Update region is saved and restored around LVM_SETSELECTEDCOLUMN
1107 * to prevent infinite WM_PAINT on Vista.
1109 if ((enabled && clrTextBk != -1) || (!enabled && hasCustomBackground())) {
1110 int oldColumn = (int)OS.SendMessage (handle, OS.LVM_GETSELECTEDCOLUMN, 0, 0);
1111 if (oldColumn != -1 && oldColumn == nmcd.iSubItem) {
1112 long rgn = OS.CreateRectRgn (0, 0, 0, 0);
1113 int result = OS.GetUpdateRgn (handle, rgn, true);
1114 OS.SendMessage (handle, OS.LVM_SETSELECTEDCOLUMN, -1, 0);
1115 OS.ValidateRect (handle, null);
1116 if (result != OS.NULLREGION) OS.InvalidateRgn (handle, rgn, true);
1117 OS.DeleteObject (rgn);
1118 code |= OS.CDRF_NOTIFYPOSTPAINT;
1123 * Feature in Windows. When the table is disabled, it draws
1124 * with a gray background but does not gray the text. The fix
1125 * is to explicitly gray the text, but only, when it wasn't customized.
1127 nmcd.clrText = OS.GetSysColor (OS.COLOR_GRAYTEXT);
1128 if (findImageControl () != null || hasCustomBackground()) {
1129 nmcd.clrTextBk = OS.CLR_NONE;
1131 nmcd.uItemState &= ~OS.CDIS_SELECTED;
1132 OS.MoveMemory (lParam, nmcd, NMLVCUSTOMDRAW.sizeof);
1133 code |= OS.CDRF_NEWFONT;
1135 return new LRESULT (code);
1139 void checkBuffered () {
1140 super.checkBuffered ();
1141 style |= SWT.DOUBLE_BUFFERED;
1144 boolean checkData (TableItem item, boolean redraw) {
1145 if ((style & SWT.VIRTUAL) == 0) return true;
1146 return checkData (item, indexOf (item), redraw);
1149 boolean checkData (TableItem item, int index, boolean redraw) {
1150 if ((style & SWT.VIRTUAL) == 0) return true;
1153 Event event = new Event ();
1155 event.index = index;
1157 sendEvent (SWT.SetData, event);
1158 //widget could be disposed at this point
1160 if (isDisposed () || item.isDisposed ()) return false;
1162 if (!setScrollWidth (item, false)) {
1171 boolean checkHandle (long hwnd) {
1172 if (hwnd == handle) return true;
1173 return hwnd == OS.SendMessage (handle, OS.LVM_GETHEADER, 0, 0);
1177 protected void checkSubclass () {
1178 if (!isValidSubclass ()) error (SWT.ERROR_INVALID_SUBCLASS);
1182 * Clears the item at the given zero-relative index in the receiver.
1183 * The text, icon and other attributes of the item are set to the default
1184 * value. If the table was created with the <code>SWT.VIRTUAL</code> style,
1185 * these attributes are requested again as needed.
1187 * @param index the index of the item to clear
1189 * @exception IllegalArgumentException <ul>
1190 * <li>ERROR_INVALID_RANGE - if the index is not between 0 and the number of elements in the list minus 1 (inclusive)</li>
1192 * @exception SWTException <ul>
1193 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
1194 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
1202 public void clear (int index) {
1204 int count = (int)OS.SendMessage (handle, OS.LVM_GETITEMCOUNT, 0, 0);
1205 if (!(0 <= index && index < count)) error (SWT.ERROR_INVALID_RANGE);
1206 TableItem item = _getItem (index, false);
1208 if (item != currentItem) item.clear ();
1210 * Bug in Windows. Despite the fact that every item in the
1211 * table always has LPSTR_TEXTCALLBACK, Windows caches the
1212 * bounds for the selected items. This means that
1213 * when you change the string to be something else, Windows
1214 * correctly asks you for the new string but when the item
1215 * is selected, the selection draws using the bounds of the
1216 * previous item. The fix is to reset LPSTR_TEXTCALLBACK
1217 * even though it has not changed, causing Windows to flush
1220 if ((style & SWT.VIRTUAL) == 0 && item.cached) {
1221 LVITEM lvItem = new LVITEM ();
1222 lvItem.mask = OS.LVIF_TEXT | OS.LVIF_INDENT;
1223 lvItem.pszText = OS.LPSTR_TEXTCALLBACK;
1224 lvItem.iItem = index;
1225 OS.SendMessage (handle, OS.LVM_SETITEM, 0, lvItem);
1226 item.cached = false;
1228 if (currentItem == null && getDrawing () && OS.IsWindowVisible (handle)) {
1229 OS.SendMessage (handle, OS.LVM_REDRAWITEMS, index, index);
1231 setScrollWidth (item, false);
1236 * Removes the items from the receiver which are between the given
1237 * zero-relative start and end indices (inclusive). The text, icon
1238 * and other attributes of the items are set to their default values.
1239 * If the table was created with the <code>SWT.VIRTUAL</code> style,
1240 * these attributes are requested again as needed.
1242 * @param start the start index of the item to clear
1243 * @param end the end index of the item to clear
1245 * @exception IllegalArgumentException <ul>
1246 * <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>
1248 * @exception SWTException <ul>
1249 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
1250 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
1258 public void clear (int start, int end) {
1260 if (start > end) return;
1261 int count = (int)OS.SendMessage (handle, OS.LVM_GETITEMCOUNT, 0, 0);
1262 if (!(0 <= start && start <= end && end < count)) {
1263 error (SWT.ERROR_INVALID_RANGE);
1265 if (start == 0 && end == count - 1) {
1268 LVITEM lvItem = null;
1269 boolean cleared = false;
1270 for (int i=start; i<=end; i++) {
1271 TableItem item = _getItem (i, false);
1273 if (item != currentItem) {
1278 * Bug in Windows. Despite the fact that every item in the
1279 * table always has LPSTR_TEXTCALLBACK, Windows caches the
1280 * bounds for the selected items. This means that
1281 * when you change the string to be something else, Windows
1282 * correctly asks you for the new string but when the item
1283 * is selected, the selection draws using the bounds of the
1284 * previous item. The fix is to reset LPSTR_TEXTCALLBACK
1285 * even though it has not changed, causing Windows to flush
1288 if ((style & SWT.VIRTUAL) == 0 && item.cached) {
1289 if (lvItem == null) {
1290 lvItem = new LVITEM ();
1291 lvItem.mask = OS.LVIF_TEXT | OS.LVIF_INDENT;
1292 lvItem.pszText = OS.LPSTR_TEXTCALLBACK;
1295 OS.SendMessage (handle, OS.LVM_SETITEM, 0, lvItem);
1296 item.cached = false;
1301 if (currentItem == null && getDrawing () && OS.IsWindowVisible (handle)) {
1302 OS.SendMessage (handle, OS.LVM_REDRAWITEMS, start, end);
1304 TableItem item = start == end ? _getItem (start, false) : null;
1305 setScrollWidth (item, false);
1311 * Clears the items at the given zero-relative indices in the receiver.
1312 * The text, icon and other attributes of the items are set to their default
1313 * values. If the table was created with the <code>SWT.VIRTUAL</code> style,
1314 * these attributes are requested again as needed.
1316 * @param indices the array of indices of the items
1318 * @exception IllegalArgumentException <ul>
1319 * <li>ERROR_INVALID_RANGE - if the index is not between 0 and the number of elements in the list minus 1 (inclusive)</li>
1320 * <li>ERROR_NULL_ARGUMENT - if the indices array is null</li>
1322 * @exception SWTException <ul>
1323 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
1324 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
1332 public void clear (int [] indices) {
1334 if (indices == null) error (SWT.ERROR_NULL_ARGUMENT);
1335 if (indices.length == 0) return;
1336 int count = (int)OS.SendMessage (handle, OS.LVM_GETITEMCOUNT, 0, 0);
1337 for (int i=0; i<indices.length; i++) {
1338 if (!(0 <= indices [i] && indices [i] < count)) {
1339 error (SWT.ERROR_INVALID_RANGE);
1342 LVITEM lvItem = null;
1343 boolean cleared = false;
1344 for (int i=0; i<indices.length; i++) {
1345 int index = indices [i];
1346 TableItem item = _getItem (index, false);
1348 if (item != currentItem) {
1353 * Bug in Windows. Despite the fact that every item in the
1354 * table always has LPSTR_TEXTCALLBACK, Windows caches the
1355 * bounds for the selected items. This means that
1356 * when you change the string to be something else, Windows
1357 * correctly asks you for the new string but when the item
1358 * is selected, the selection draws using the bounds of the
1359 * previous item. The fix is to reset LPSTR_TEXTCALLBACK
1360 * even though it has not changed, causing Windows to flush
1363 if ((style & SWT.VIRTUAL) == 0 && item.cached) {
1364 if (lvItem == null) {
1365 lvItem = new LVITEM ();
1366 lvItem.mask = OS.LVIF_TEXT | OS.LVIF_INDENT;
1367 lvItem.pszText = OS.LPSTR_TEXTCALLBACK;
1370 OS.SendMessage (handle, OS.LVM_SETITEM, 0, lvItem);
1371 item.cached = false;
1373 if (currentItem == null && getDrawing () && OS.IsWindowVisible (handle)) {
1374 OS.SendMessage (handle, OS.LVM_REDRAWITEMS, index, index);
1378 if (cleared) setScrollWidth (null, false);
1382 * Clears all the items in the receiver. The text, icon and other
1383 * attributes of the items are set to their default values. If the
1384 * table was created with the <code>SWT.VIRTUAL</code> style, these
1385 * attributes are requested again as needed.
1387 * @exception SWTException <ul>
1388 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
1389 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
1397 public void clearAll () {
1399 LVITEM lvItem = null;
1400 boolean cleared = false;
1401 int count = (int)OS.SendMessage (handle, OS.LVM_GETITEMCOUNT, 0, 0);
1402 for (int i=0; i<count; i++) {
1403 TableItem item = _getItem (i, false);
1405 if (item != currentItem) {
1410 * Bug in Windows. Despite the fact that every item in the
1411 * table always has LPSTR_TEXTCALLBACK, Windows caches the
1412 * bounds for the selected items. This means that
1413 * when you change the string to be something else, Windows
1414 * correctly asks you for the new string but when the item
1415 * is selected, the selection draws using the bounds of the
1416 * previous item. The fix is to reset LPSTR_TEXTCALLBACK
1417 * even though it has not changed, causing Windows to flush
1420 if ((style & SWT.VIRTUAL) == 0 && item.cached) {
1421 if (lvItem == null) {
1422 lvItem = new LVITEM ();
1423 lvItem.mask = OS.LVIF_TEXT | OS.LVIF_INDENT;
1424 lvItem.pszText = OS.LPSTR_TEXTCALLBACK;
1427 OS.SendMessage (handle, OS.LVM_SETITEM, 0, lvItem);
1428 item.cached = false;
1433 if (currentItem == null && getDrawing () && OS.IsWindowVisible (handle)) {
1434 OS.SendMessage (handle, OS.LVM_REDRAWITEMS, 0, count - 1);
1436 setScrollWidth (null, false);
1440 @Override Point computeSizeInPixels (int wHint, int hHint, boolean changed) {
1441 if (fixScrollWidth) setScrollWidth (null, true);
1442 //This code is intentionally commented
1443 // if (itemHeight == -1 && hooks (SWT.MeasureItem)) {
1445 // TableItem item = items [i];
1446 // if (item != null) {
1447 // int hDC = OS.GetDC (handle);
1448 // int oldFont = 0, newFont = OS.SendMessage (handle, OS.WM_GETFONT, 0, 0);
1449 // if (newFont != 0) oldFont = OS.SelectObject (hDC, newFont);
1450 // int index = 0, count = Math.max (1, columnCount);
1451 // while (index < count) {
1452 // int hFont = item.cellFont != null ? item.cellFont [index] : -1;
1453 // if (hFont == -1) hFont = item.font;
1454 // if (hFont != -1) hFont = OS.SelectObject (hDC, hFont);
1455 // sendMeasureItemEvent (item, i, index, hDC);
1456 // if (hFont != -1) hFont = OS.SelectObject (hDC, hFont);
1457 // if (isDisposed () || item.isDisposed ()) break;
1460 // if (newFont != 0) OS.SelectObject (hDC, oldFont);
1461 // OS.ReleaseDC (handle, hDC);
1464 RECT rect = new RECT ();
1465 OS.GetWindowRect (hwndHeader, rect);
1466 int height = rect.bottom - rect.top;
1468 if (wHint != SWT.DEFAULT) {
1469 bits |= wHint & 0xFFFF;
1472 int count = (int)OS.SendMessage (hwndHeader, OS.HDM_GETITEMCOUNT, 0, 0);
1473 for (int i=0; i<count; i++) {
1474 width += OS.SendMessage (handle, OS.LVM_GETCOLUMNWIDTH, i, 0);
1476 bits |= width & 0xFFFF;
1478 long result = OS.SendMessage (handle, OS.LVM_APPROXIMATEVIEWRECT, -1, OS.MAKELPARAM (bits, 0xFFFF));
1479 int width = OS.LOWORD (result);
1480 long empty = OS.SendMessage (handle, OS.LVM_APPROXIMATEVIEWRECT, 0, 0);
1481 long oneItem = OS.SendMessage (handle, OS.LVM_APPROXIMATEVIEWRECT, 1, 0);
1482 int itemHeight = OS.HIWORD (oneItem) - OS.HIWORD (empty);
1483 height += (int)OS.SendMessage (handle, OS.LVM_GETITEMCOUNT, 0, 0) * itemHeight;
1484 if (width == 0) width = DEFAULT_WIDTH;
1485 if (height == 0) height = DEFAULT_HEIGHT;
1486 if (wHint != SWT.DEFAULT) width = wHint;
1487 if (hHint != SWT.DEFAULT) height = hHint;
1488 int border = getBorderWidthInPixels ();
1489 width += border * 2; height += border * 2;
1490 if ((style & SWT.V_SCROLL) != 0) {
1491 width += OS.GetSystemMetrics (OS.SM_CXVSCROLL);
1493 if ((style & SWT.H_SCROLL) != 0) {
1494 height += OS.GetSystemMetrics (OS.SM_CYHSCROLL);
1496 return new Point (width, height);
1500 void createHandle () {
1501 super.createHandle ();
1502 state &= ~(CANVAS | THEME_BACKGROUND);
1504 /* Use the Explorer theme */
1505 if (OS.IsAppThemed ()) {
1506 explorerTheme = true;
1507 OS.SetWindowTheme (handle, Display.EXPLORER, null);
1510 /* Get the header window handle */
1511 hwndHeader = OS.SendMessage (handle, OS.LVM_GETHEADER, 0, 0);
1514 * This code is intentionally commented. According to
1515 * the documentation, setting the default item size is
1516 * supposed to improve performance. By experimentation,
1517 * this does not seem to have much of an effect.
1519 // OS.SendMessage (handle, OS.LVM_SETITEMCOUNT, 1024 * 2, 0);
1521 /* Set the checkbox image list */
1522 if ((style & SWT.CHECK) != 0) {
1523 long empty = OS.SendMessage (handle, OS.LVM_APPROXIMATEVIEWRECT, 0, 0);
1524 long oneItem = OS.SendMessage (handle, OS.LVM_APPROXIMATEVIEWRECT, 1, 0);
1525 int width = OS.HIWORD (oneItem) - OS.HIWORD (empty), height = width;
1526 setCheckboxImageList (width, height, false);
1527 OS.SendMessage (handle, OS. LVM_SETCALLBACKMASK, OS.LVIS_STATEIMAGEMASK, 0);
1531 * Feature in Windows. When the control is created,
1532 * it does not use the default system font. A new HFONT
1533 * is created and destroyed when the control is destroyed.
1534 * This means that a program that queries the font from
1535 * this control, uses the font in another control and then
1536 * destroys this control will have the font unexpectedly
1537 * destroyed in the other control. The fix is to assign
1538 * the font ourselves each time the control is created.
1539 * The control will not destroy a font that it did not
1542 long hFont = OS.GetStockObject (OS.SYSTEM_FONT);
1543 OS.SendMessage (handle, OS.WM_SETFONT, hFont, 0);
1546 * Bug in Windows. When the first column is inserted
1547 * without setting the header text, Windows will never
1548 * allow the header text for the first column to be set.
1549 * The fix is to set the text to an empty string when
1550 * the column is inserted.
1552 LVCOLUMN lvColumn = new LVCOLUMN ();
1553 lvColumn.mask = OS.LVCF_TEXT | OS.LVCF_WIDTH;
1554 long hHeap = OS.GetProcessHeap ();
1555 long pszText = OS.HeapAlloc (hHeap, OS.HEAP_ZERO_MEMORY, TCHAR.sizeof);
1556 lvColumn.pszText = pszText;
1557 OS.SendMessage (handle, OS.LVM_INSERTCOLUMN, 0, lvColumn);
1558 OS.HeapFree (hHeap, 0, pszText);
1560 /* Set the extended style bits */
1561 int bits1 = OS.LVS_EX_LABELTIP | OS.LVS_EX_DOUBLEBUFFER;
1562 if ((style & SWT.FULL_SELECTION) != 0) bits1 |= OS.LVS_EX_FULLROWSELECT;
1563 OS.SendMessage (handle, OS.LVM_SETEXTENDEDLISTVIEWSTYLE, bits1, bits1);
1566 * Feature in Windows. Windows does not explicitly set the orientation of
1567 * the header. Instead, the orientation is inherited when WS_EX_LAYOUTRTL
1568 * is specified for the table. This means that when both WS_EX_LAYOUTRTL
1569 * and WS_EX_NOINHERITLAYOUT are specified for the table, the header will
1570 * not be oriented correctly. The fix is to explicitly set the orientation
1573 if ((style & SWT.RIGHT_TO_LEFT) != 0) {
1574 int bits2 = OS.GetWindowLong (hwndHeader, OS.GWL_EXSTYLE);
1575 OS.SetWindowLong (hwndHeader, OS.GWL_EXSTYLE, bits2 | OS.WS_EX_LAYOUTRTL);
1576 long hwndTooltop = OS.SendMessage (handle, OS.LVM_GETTOOLTIPS, 0, 0);
1577 int bits3 = OS.GetWindowLong (hwndTooltop, OS.GWL_EXSTYLE);
1578 OS.SetWindowLong (hwndTooltop, OS.GWL_EXSTYLE, bits3 | OS.WS_EX_LAYOUTRTL);
1583 int applyThemeBackground () {
1585 * Just inheriting the THEME_BACKGROUND doesn't turn complete Table
1586 * background transparent, TableItem background remains as-is.
1588 return -1; /* No Change */
1591 void createHeaderToolTips () {
1592 if (headerToolTipHandle != 0) return;
1594 if ((style & SWT.RIGHT_TO_LEFT) != 0) bits |= OS.WS_EX_LAYOUTRTL;
1595 headerToolTipHandle = OS.CreateWindowEx (
1597 new TCHAR (0, OS.TOOLTIPS_CLASS, true),
1600 OS.CW_USEDEFAULT, 0, OS.CW_USEDEFAULT, 0,
1603 OS.GetModuleHandle (null),
1605 if (headerToolTipHandle == 0) error (SWT.ERROR_NO_HANDLES);
1607 * Feature in Windows. Despite the fact that the
1608 * tool tip text contains \r\n, the tooltip will
1609 * not honour the new line unless TTM_SETMAXTIPWIDTH
1610 * is set. The fix is to set TTM_SETMAXTIPWIDTH to
1613 OS.SendMessage (headerToolTipHandle, OS.TTM_SETMAXTIPWIDTH, 0, 0x7FFF);
1616 void createItem (TableColumn column, int index) {
1617 if (!(0 <= index && index <= columnCount)) error (SWT.ERROR_INVALID_RANGE);
1618 int oldColumn = (int)OS.SendMessage (handle, OS.LVM_GETSELECTEDCOLUMN, 0, 0);
1619 if (oldColumn >= index) {
1620 OS.SendMessage (handle, OS.LVM_SETSELECTEDCOLUMN, oldColumn + 1, 0);
1622 if (columnCount == columns.length) {
1623 TableColumn [] newColumns = new TableColumn [columns.length + 4];
1624 System.arraycopy (columns, 0, newColumns, 0, columns.length);
1625 columns = newColumns;
1627 int itemCount = (int)OS.SendMessage (handle, OS.LVM_GETITEMCOUNT, 0, 0);
1628 for (int i=0; i<itemCount; i++) {
1629 TableItem item = _getItem (i, false);
1631 String [] strings = item.strings;
1632 if (strings != null) {
1633 String [] temp = new String [columnCount + 1];
1634 System.arraycopy (strings, 0, temp, 0, index);
1635 System.arraycopy (strings, index, temp, index + 1, columnCount - index);
1636 item.strings = temp;
1638 Image [] images = item.images;
1639 if (images != null) {
1640 Image [] temp = new Image [columnCount + 1];
1641 System.arraycopy (images, 0, temp, 0, index);
1642 System.arraycopy (images, index, temp, index + 1, columnCount - index);
1646 if (columnCount != 0) {
1647 if (strings == null) {
1648 item.strings = new String [columnCount + 1];
1649 item.strings [1] = item.text;
1651 item.text = ""; //$NON-NLS-1$
1652 if (images == null) {
1653 item.images = new Image [columnCount + 1];
1654 item.images [1] = item.image;
1659 if (item.cellBackground != null) {
1660 int [] cellBackground = item.cellBackground;
1661 int [] temp = new int [columnCount + 1];
1662 System.arraycopy (cellBackground, 0, temp, 0, index);
1663 System.arraycopy (cellBackground, index, temp, index + 1, columnCount - index);
1665 item.cellBackground = temp;
1667 if (item.cellForeground != null) {
1668 int [] cellForeground = item.cellForeground;
1669 int [] temp = new int [columnCount + 1];
1670 System.arraycopy (cellForeground, 0, temp, 0, index);
1671 System.arraycopy (cellForeground, index, temp, index + 1, columnCount - index);
1673 item.cellForeground = temp;
1675 if (item.cellFont != null) {
1676 Font [] cellFont = item.cellFont;
1677 Font [] temp = new Font [columnCount + 1];
1678 System.arraycopy (cellFont, 0, temp, 0, index);
1679 System.arraycopy (cellFont, index, temp, index + 1, columnCount - index);
1680 item.cellFont = temp;
1685 * Insert the column into the columns array before inserting
1686 * it into the widget so that the column will be present when
1687 * any callbacks are issued as a result of LVM_INSERTCOLUMN
1690 System.arraycopy (columns, index, columns, index + 1, columnCount++ - index);
1691 columns [index] = column;
1694 * Ensure that resize listeners for the table and for columns
1695 * within the table are not called. This can happen when the
1696 * first column is inserted into a table or when a new column
1697 * is inserted in the first position.
1699 ignoreColumnResize = true;
1701 if (columnCount > 1) {
1702 LVCOLUMN lvColumn = new LVCOLUMN ();
1703 lvColumn.mask = OS.LVCF_WIDTH;
1704 OS.SendMessage (handle, OS.LVM_INSERTCOLUMN, 1, lvColumn);
1705 OS.SendMessage (handle, OS.LVM_GETCOLUMN, 1, lvColumn);
1706 int width = lvColumn.cx;
1707 int cchTextMax = 1024;
1708 long hHeap = OS.GetProcessHeap ();
1709 int byteCount = cchTextMax * TCHAR.sizeof;
1710 long pszText = OS.HeapAlloc (hHeap, OS.HEAP_ZERO_MEMORY, byteCount);
1711 lvColumn.mask = OS.LVCF_TEXT | OS.LVCF_IMAGE | OS.LVCF_WIDTH | OS.LVCF_FMT;
1712 lvColumn.pszText = pszText;
1713 lvColumn.cchTextMax = cchTextMax;
1714 OS.SendMessage (handle, OS.LVM_GETCOLUMN, 0, lvColumn);
1715 OS.SendMessage (handle, OS.LVM_SETCOLUMN, 1, lvColumn);
1716 lvColumn.fmt = OS.LVCFMT_IMAGE;
1717 lvColumn.cx = width;
1718 lvColumn.iImage = OS.I_IMAGENONE;
1719 lvColumn.pszText = lvColumn.cchTextMax = 0;
1720 OS.SendMessage (handle, OS.LVM_SETCOLUMN, 0, lvColumn);
1721 lvColumn.mask = OS.LVCF_FMT;
1722 lvColumn.fmt = OS.LVCFMT_LEFT;
1723 OS.SendMessage (handle, OS.LVM_SETCOLUMN, 0, lvColumn);
1724 if (pszText != 0) OS.HeapFree (hHeap, 0, pszText);
1726 OS.SendMessage (handle, OS.LVM_SETCOLUMNWIDTH, 0, 0);
1729 * Bug in Windows. Despite the fact that every item in the
1730 * table always has LPSTR_TEXTCALLBACK, Windows caches the
1731 * bounds for the selected items. This means that
1732 * when you change the string to be something else, Windows
1733 * correctly asks you for the new string but when the item
1734 * is selected, the selection draws using the bounds of the
1735 * previous item. The fix is to reset LPSTR_TEXTCALLBACK
1736 * even though it has not changed, causing Windows to flush
1739 if ((style & SWT.VIRTUAL) == 0) {
1740 LVITEM lvItem = new LVITEM ();
1741 lvItem.mask = OS.LVIF_TEXT | OS.LVIF_IMAGE;
1742 lvItem.pszText = OS.LPSTR_TEXTCALLBACK;
1743 lvItem.iImage = OS.I_IMAGECALLBACK;
1744 for (int i=0; i<itemCount; i++) {
1746 OS.SendMessage (handle, OS.LVM_SETITEM, 0, lvItem);
1750 int fmt = OS.LVCFMT_LEFT;
1751 if ((column.style & SWT.CENTER) == SWT.CENTER) fmt = OS.LVCFMT_CENTER;
1752 if ((column.style & SWT.RIGHT) == SWT.RIGHT) fmt = OS.LVCFMT_RIGHT;
1753 LVCOLUMN lvColumn = new LVCOLUMN ();
1754 lvColumn.mask = OS.LVCF_WIDTH | OS.LVCF_FMT;
1756 OS.SendMessage (handle, OS.LVM_INSERTCOLUMN, index, lvColumn);
1758 ignoreColumnResize = false;
1760 /* Add the tool tip item for the header */
1761 if (headerToolTipHandle != 0) {
1762 RECT rect = new RECT ();
1763 if (OS.SendMessage (hwndHeader, OS.HDM_GETITEMRECT, index, rect) != 0) {
1764 TOOLINFO lpti = new TOOLINFO ();
1765 lpti.cbSize = TOOLINFO.sizeof;
1766 lpti.uFlags = OS.TTF_SUBCLASS;
1767 lpti.hwnd = hwndHeader;
1768 lpti.uId = column.id = display.nextToolTipId++;
1769 lpti.left = rect.left;
1770 lpti.top = rect.top;
1771 lpti.right = rect.right;
1772 lpti.bottom = rect.bottom;
1773 lpti.lpszText = OS.LPSTR_TEXTCALLBACK;
1774 OS.SendMessage (headerToolTipHandle, OS.TTM_ADDTOOL, 0, lpti);
1779 void createItem (TableItem item, int index) {
1780 int count = (int)OS.SendMessage (handle, OS.LVM_GETITEMCOUNT, 0, 0);
1781 if (!(0 <= index && index <= count)) error (SWT.ERROR_INVALID_RANGE);
1783 LVITEM lvItem = new LVITEM ();
1784 lvItem.mask = OS.LVIF_TEXT | OS.LVIF_IMAGE;
1785 lvItem.iItem = index;
1786 lvItem.pszText = OS.LPSTR_TEXTCALLBACK;
1788 * Bug in Windows. Despite the fact that the image list
1789 * index has never been set for the item, Windows always
1790 * assumes that the image index for the item is valid.
1791 * When an item is inserted, the image index is zero.
1792 * Therefore, when the first image is inserted and is
1793 * assigned image index zero, every item draws with this
1794 * image. The fix is to set the image index when the
1795 * the item is created.
1797 lvItem.iImage = OS.I_IMAGECALLBACK;
1799 /* Insert the item */
1800 setDeferResize (true);
1801 ignoreSelect = ignoreShrink = true;
1802 int result = (int)OS.SendMessage (handle, OS.LVM_INSERTITEM, 0, lvItem);
1803 ignoreSelect = ignoreShrink = false;
1804 if (result == -1) error (SWT.ERROR_ITEM_NOT_ADDED);
1805 _insertItem (index, item, count);
1806 setDeferResize (false);
1808 /* Resize to show the first item */
1809 if (count == 0) setScrollWidth (item, false);
1813 void createWidget () {
1814 super.createWidget ();
1815 itemHeight = hotIndex = -1;
1817 columns = new TableColumn [4];
1820 private boolean customHeaderDrawing() {
1821 return headerBackground != -1 || headerForeground != -1;
1825 int defaultBackground () {
1826 return OS.GetSysColor (OS.COLOR_WINDOW);
1830 void deregister () {
1831 super.deregister ();
1832 if (hwndHeader != 0) display.removeControl (hwndHeader);
1836 * Deselects the items at the given zero-relative indices in the receiver.
1837 * If the item at the given zero-relative index in the receiver
1838 * is selected, it is deselected. If the item at the index
1839 * was not selected, it remains deselected. Indices that are out
1840 * of range and duplicate indices are ignored.
1842 * @param indices the array of indices for the items to deselect
1844 * @exception IllegalArgumentException <ul>
1845 * <li>ERROR_NULL_ARGUMENT - if the set of indices is null</li>
1847 * @exception SWTException <ul>
1848 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
1849 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
1852 public void deselect (int [] indices) {
1854 if (indices == null) error (SWT.ERROR_NULL_ARGUMENT);
1855 if (indices.length == 0) return;
1856 LVITEM lvItem = new LVITEM ();
1857 lvItem.stateMask = OS.LVIS_SELECTED;
1858 for (int i=0; i<indices.length; i++) {
1860 * An index of -1 will apply the change to all
1861 * items. Ensure that indices are greater than -1.
1863 if (indices [i] >= 0) {
1864 ignoreSelect = true;
1865 OS.SendMessage (handle, OS.LVM_SETITEMSTATE, indices [i], lvItem);
1866 ignoreSelect = false;
1872 * Deselects the item at the given zero-relative index in the receiver.
1873 * If the item at the index was already deselected, it remains
1874 * deselected. Indices that are out of range are ignored.
1876 * @param index the index of the item to deselect
1878 * @exception SWTException <ul>
1879 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
1880 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
1883 public void deselect (int index) {
1886 * An index of -1 will apply the change to all
1887 * items. Ensure that index is greater than -1.
1889 if (index < 0) return;
1890 LVITEM lvItem = new LVITEM ();
1891 lvItem.stateMask = OS.LVIS_SELECTED;
1892 ignoreSelect = true;
1893 OS.SendMessage (handle, OS.LVM_SETITEMSTATE, index, lvItem);
1894 ignoreSelect = false;
1898 * Deselects the items at the given zero-relative indices in the receiver.
1899 * If the item at the given zero-relative index in the receiver
1900 * is selected, it is deselected. If the item at the index
1901 * was not selected, it remains deselected. The range of the
1902 * indices is inclusive. Indices that are out of range are ignored.
1904 * @param start the start index of the items to deselect
1905 * @param end the end index of the items to deselect
1907 * @exception SWTException <ul>
1908 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
1909 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
1912 public void deselect (int start, int end) {
1914 int count = (int)OS.SendMessage (handle, OS.LVM_GETITEMCOUNT, 0, 0);
1915 if (start == 0 && end == count - 1) {
1918 LVITEM lvItem = new LVITEM ();
1919 lvItem.stateMask = OS.LVIS_SELECTED;
1921 * An index of -1 will apply the change to all
1922 * items. Ensure that indices are greater than -1.
1924 start = Math.max (0, start);
1925 for (int i=start; i<=end; i++) {
1926 ignoreSelect = true;
1927 OS.SendMessage (handle, OS.LVM_SETITEMSTATE, i, lvItem);
1928 ignoreSelect = false;
1934 * Deselects all selected items in the receiver.
1936 * @exception SWTException <ul>
1937 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
1938 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
1941 public void deselectAll () {
1943 LVITEM lvItem = new LVITEM ();
1944 lvItem.mask = OS.LVIF_STATE;
1945 lvItem.stateMask = OS.LVIS_SELECTED;
1946 ignoreSelect = true;
1947 OS.SendMessage (handle, OS.LVM_SETITEMSTATE, -1, lvItem);
1948 ignoreSelect = false;
1951 void destroyItem (TableColumn column) {
1953 while (index < columnCount) {
1954 if (columns [index] == column) break;
1957 int oldColumn = (int)OS.SendMessage (handle, OS.LVM_GETSELECTEDCOLUMN, 0, 0);
1958 if (oldColumn == index) {
1959 OS.SendMessage (handle, OS.LVM_SETSELECTEDCOLUMN, -1, 0);
1961 if (oldColumn > index) {
1962 OS.SendMessage (handle, OS.LVM_SETSELECTEDCOLUMN, oldColumn - 1, 0);
1966 int [] oldOrder = new int [columnCount];
1967 OS.SendMessage (handle, OS.LVM_GETCOLUMNORDERARRAY, columnCount, oldOrder);
1968 while (orderIndex < columnCount) {
1969 if (oldOrder [orderIndex] == index) break;
1972 ignoreColumnResize = true;
1973 boolean first = false;
1977 * Changing the content of a column using LVM_SETCOLUMN causes
1978 * the table control to send paint events. At this point the
1979 * partially disposed column is still part of the table and
1980 * paint handler can try to access it. This can cause exceptions.
1981 * The fix is to turn redraw off.
1984 if (columnCount > 1) {
1986 int cchTextMax = 1024;
1987 long hHeap = OS.GetProcessHeap ();
1988 int byteCount = cchTextMax * TCHAR.sizeof;
1989 long pszText = OS.HeapAlloc (hHeap, OS.HEAP_ZERO_MEMORY, byteCount);
1990 LVCOLUMN lvColumn = new LVCOLUMN ();
1991 lvColumn.mask = OS.LVCF_TEXT | OS.LVCF_IMAGE | OS.LVCF_WIDTH | OS.LVCF_FMT;
1992 lvColumn.pszText = pszText;
1993 lvColumn.cchTextMax = cchTextMax;
1994 OS.SendMessage (handle, OS.LVM_GETCOLUMN, 1, lvColumn);
1995 lvColumn.fmt &= ~(OS.LVCFMT_CENTER | OS.LVCFMT_RIGHT);
1996 lvColumn.fmt |= OS.LVCFMT_LEFT;
1997 OS.SendMessage (handle, OS.LVM_SETCOLUMN, 0, lvColumn);
1998 if (pszText != 0) OS.HeapFree (hHeap, 0, pszText);
2000 long hHeap = OS.GetProcessHeap ();
2001 long pszText = OS.HeapAlloc (hHeap, OS.HEAP_ZERO_MEMORY, TCHAR.sizeof);
2002 LVCOLUMN lvColumn = new LVCOLUMN ();
2003 lvColumn.mask = OS.LVCF_TEXT | OS.LVCF_IMAGE | OS.LVCF_WIDTH | OS.LVCF_FMT;
2004 lvColumn.pszText = pszText;
2005 lvColumn.iImage = OS.I_IMAGENONE;
2006 lvColumn.fmt = OS.LVCFMT_LEFT;
2007 OS.SendMessage (handle, OS.LVM_SETCOLUMN, 0, lvColumn);
2008 if (pszText != 0) OS.HeapFree (hHeap, 0, pszText);
2009 HDITEM hdItem = new HDITEM ();
2010 hdItem.mask = OS.HDI_FORMAT;
2011 hdItem.fmt = OS.HDF_LEFT;
2012 long hwndHeader = OS.SendMessage (handle, OS.LVM_GETHEADER, 0, 0);
2013 OS.SendMessage (hwndHeader, OS.HDM_SETITEM, index, hdItem);
2017 * Bug in Windows. Despite the fact that every item in the
2018 * table always has LPSTR_TEXTCALLBACK, Windows caches the
2019 * bounds for the selected items. This means that
2020 * when you change the string to be something else, Windows
2021 * correctly asks you for the new string but when the item
2022 * is selected, the selection draws using the bounds of the
2023 * previous item. The fix is to reset LPSTR_TEXTCALLBACK
2024 * even though it has not changed, causing Windows to flush
2027 if ((style & SWT.VIRTUAL) == 0) {
2028 LVITEM lvItem = new LVITEM ();
2029 lvItem.mask = OS.LVIF_TEXT | OS.LVIF_IMAGE;
2030 lvItem.pszText = OS.LPSTR_TEXTCALLBACK;
2031 lvItem.iImage = OS.I_IMAGECALLBACK;
2032 int itemCount = (int)OS.SendMessage (handle, OS.LVM_GETITEMCOUNT, 0, 0);
2033 for (int i=0; i<itemCount; i++) {
2035 OS.SendMessage (handle, OS.LVM_SETITEM, 0, lvItem);
2039 if (columnCount > 1) {
2040 if (OS.SendMessage (handle, OS.LVM_DELETECOLUMN, index, 0) == 0) {
2041 error (SWT.ERROR_ITEM_NOT_REMOVED);
2044 if (first) index = 0;
2045 System.arraycopy (columns, index + 1, columns, index, --columnCount - index);
2046 columns [columnCount] = null;
2047 int itemCount = (int)OS.SendMessage (handle, OS.LVM_GETITEMCOUNT, 0, 0);
2048 for (int i=0; i<itemCount; i++) {
2049 TableItem item = _getItem (i, false);
2051 if (columnCount == 0) {
2052 item.strings = null;
2054 item.cellBackground = null;
2055 item.cellForeground = null;
2056 item.cellFont = null;
2058 if (item.strings != null) {
2059 String [] strings = item.strings;
2061 item.text = strings [1] != null ? strings [1] : ""; //$NON-NLS-1$
2063 String [] temp = new String [columnCount];
2064 System.arraycopy (strings, 0, temp, 0, index);
2065 System.arraycopy (strings, index + 1, temp, index, columnCount - index);
2066 item.strings = temp;
2068 if (index == 0) item.text = ""; //$NON-NLS-1$
2070 if (item.images != null) {
2071 Image [] images = item.images;
2072 if (index == 0) item.image = images [1];
2073 Image [] temp = new Image [columnCount];
2074 System.arraycopy (images, 0, temp, 0, index);
2075 System.arraycopy (images, index + 1, temp, index, columnCount - index);
2078 if (index == 0) item.image = null;
2080 if (item.cellBackground != null) {
2081 int [] cellBackground = item.cellBackground;
2082 int [] temp = new int [columnCount];
2083 System.arraycopy (cellBackground, 0, temp, 0, index);
2084 System.arraycopy (cellBackground, index + 1, temp, index, columnCount - index);
2085 item.cellBackground = temp;
2087 if (item.cellForeground != null) {
2088 int [] cellForeground = item.cellForeground;
2089 int [] temp = new int [columnCount];
2090 System.arraycopy (cellForeground, 0, temp, 0, index);
2091 System.arraycopy (cellForeground, index + 1, temp, index, columnCount - index);
2092 item.cellForeground = temp;
2094 if (item.cellFont != null) {
2095 Font [] cellFont = item.cellFont;
2096 Font [] temp = new Font [columnCount];
2097 System.arraycopy (cellFont, 0, temp, 0, index);
2098 System.arraycopy (cellFont, index + 1, temp, index, columnCount - index);
2099 item.cellFont = temp;
2104 if (columnCount == 0) setScrollWidth (null, true);
2106 ignoreColumnResize = false;
2107 if (columnCount != 0) {
2109 * Bug in Windows. When LVM_DELETECOLUMN is used to delete a
2110 * column zero when that column is both the first column in the
2111 * table and the first column in the column order array, Windows
2112 * incorrectly computes the new column order. For example, both
2113 * the orders {0, 3, 1, 2} and {0, 3, 2, 1} give a new column
2114 * order of {0, 2, 1}, while {0, 2, 1, 3} gives {0, 1, 2, 3}.
2115 * The fix is to compute the new order and compare it with the
2116 * order that Windows is using. If the two differ, the new order
2120 int oldIndex = oldOrder [orderIndex];
2121 int [] newOrder = new int [columnCount];
2122 for (int i=0; i<oldOrder.length; i++) {
2123 if (oldOrder [i] != oldIndex) {
2124 int newIndex = oldOrder [i] <= oldIndex ? oldOrder [i] : oldOrder [i] - 1;
2125 newOrder [count++] = newIndex;
2128 OS.SendMessage (handle, OS.LVM_GETCOLUMNORDERARRAY, columnCount, oldOrder);
2130 while (j < newOrder.length) {
2131 if (oldOrder [j] != newOrder [j]) break;
2134 if (j != newOrder.length) {
2135 OS.SendMessage (handle, OS.LVM_SETCOLUMNORDERARRAY, newOrder.length, newOrder);
2137 * Bug in Windows. When LVM_SETCOLUMNORDERARRAY is used to change
2138 * the column order, the header redraws correctly but the table does
2139 * not. The fix is to force a redraw.
2141 OS.InvalidateRect (handle, null, true);
2143 TableColumn [] newColumns = new TableColumn [columnCount - orderIndex];
2144 for (int i=orderIndex; i<newOrder.length; i++) {
2145 newColumns [i - orderIndex] = columns [newOrder [i]];
2146 newColumns [i - orderIndex].updateToolTip (newOrder [i]);
2148 for (int i=0; i<newColumns.length; i++) {
2149 if (!newColumns [i].isDisposed ()) {
2150 newColumns [i].sendEvent (SWT.Move);
2155 /* Remove the tool tip item for the header */
2156 if (headerToolTipHandle != 0) {
2157 TOOLINFO lpti = new TOOLINFO ();
2158 lpti.cbSize = TOOLINFO.sizeof;
2159 lpti.uId = column.id;
2160 lpti.hwnd = OS.SendMessage (handle, OS.LVM_GETHEADER, 0, 0);
2161 OS.SendMessage (headerToolTipHandle, OS.TTM_DELTOOL, 0, lpti);
2165 void destroyItem (TableItem item) {
2166 int count = (int)OS.SendMessage (handle, OS.LVM_GETITEMCOUNT, 0, 0);
2168 while (index < count) {
2169 if (_getItem (index, false) == item) break;
2172 if (index == count) return;
2173 setDeferResize (true);
2174 ignoreSelect = ignoreShrink = true;
2175 long code = OS.SendMessage (handle, OS.LVM_DELETEITEM, index, 0);
2176 ignoreSelect = ignoreShrink = false;
2177 if (code == 0) error (SWT.ERROR_ITEM_NOT_REMOVED);
2178 _removeItem (index, count);
2180 if (count == 0) setTableEmpty ();
2181 setDeferResize (false);
2184 void fixCheckboxImageList (boolean fixScroll) {
2186 * Bug in Windows. When the state image list is larger than the
2187 * image list, Windows incorrectly positions the state images. When
2188 * the table is scrolled, Windows draws garbage. The fix is to force
2189 * the state image list to be the same size as the image list.
2191 if ((style & SWT.CHECK) == 0) return;
2192 long hImageList = OS.SendMessage (handle, OS.LVM_GETIMAGELIST, OS.LVSIL_SMALL, 0);
2193 if (hImageList == 0) return;
2194 int [] cx = new int [1], cy = new int [1];
2195 OS.ImageList_GetIconSize (hImageList, cx, cy);
2196 long hStateList = OS.SendMessage (handle, OS.LVM_GETIMAGELIST, OS.LVSIL_STATE, 0);
2197 if (hStateList == 0) return;
2198 int [] stateCx = new int [1], stateCy = new int [1];
2199 OS.ImageList_GetIconSize (hStateList, stateCx, stateCy);
2200 if (cx [0] == stateCx [0] && cy [0] == stateCy [0]) return;
2201 setCheckboxImageList (cx [0], cy [0], fixScroll);
2204 void fixCheckboxImageListColor (boolean fixScroll) {
2205 if ((style & SWT.CHECK) == 0) return;
2206 long hStateList = OS.SendMessage (handle, OS.LVM_GETIMAGELIST, OS.LVSIL_STATE, 0);
2207 if (hStateList == 0) return;
2208 int [] cx = new int [1], cy = new int [1];
2209 OS.ImageList_GetIconSize (hStateList, cx, cy);
2210 setCheckboxImageList (cx [0], cy [0], fixScroll);
2214 * Returns the column at the given, zero-relative index in the
2215 * receiver. Throws an exception if the index is out of range.
2216 * Columns are returned in the order that they were created.
2217 * If no <code>TableColumn</code>s were created by the programmer,
2218 * this method will throw <code>ERROR_INVALID_RANGE</code> despite
2219 * the fact that a single column of data may be visible in the table.
2220 * This occurs when the programmer uses the table like a list, adding
2221 * items but never creating a column.
2223 * @param index the index of the column to return
2224 * @return the column at the given index
2226 * @exception IllegalArgumentException <ul>
2227 * <li>ERROR_INVALID_RANGE - if the index is not between 0 and the number of elements in the list minus 1 (inclusive)</li>
2229 * @exception SWTException <ul>
2230 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
2231 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
2234 * @see Table#getColumnOrder()
2235 * @see Table#setColumnOrder(int[])
2236 * @see TableColumn#getMoveable()
2237 * @see TableColumn#setMoveable(boolean)
2240 public TableColumn getColumn (int index) {
2242 if (!(0 <= index && index < columnCount)) error (SWT.ERROR_INVALID_RANGE);
2243 return columns [index];
2247 * Returns the number of columns contained in the receiver.
2248 * If no <code>TableColumn</code>s were created by the programmer,
2249 * this value is zero, despite the fact that visually, one column
2250 * of items may be visible. This occurs when the programmer uses
2251 * the table like a list, adding items but never creating a column.
2253 * @return the number of columns
2255 * @exception SWTException <ul>
2256 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
2257 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
2260 public int getColumnCount () {
2266 * Returns an array of zero-relative integers that map
2267 * the creation order of the receiver's items to the
2268 * order in which they are currently being displayed.
2270 * Specifically, the indices of the returned array represent
2271 * the current visual order of the items, and the contents
2272 * of the array represent the creation order of the items.
2274 * Note: This is not the actual structure used by the receiver
2275 * to maintain its list of items, so modifying the array will
2276 * not affect the receiver.
2279 * @return the current visual order of the receiver's items
2281 * @exception SWTException <ul>
2282 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
2283 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
2286 * @see Table#setColumnOrder(int[])
2287 * @see TableColumn#getMoveable()
2288 * @see TableColumn#setMoveable(boolean)
2293 public int[] getColumnOrder () {
2295 if (columnCount == 0) return new int [0];
2296 int [] order = new int [columnCount];
2297 OS.SendMessage (handle, OS.LVM_GETCOLUMNORDERARRAY, columnCount, order);
2302 * Returns an array of <code>TableColumn</code>s which are the
2303 * columns in the receiver. Columns are returned in the order
2304 * that they were created. If no <code>TableColumn</code>s were
2305 * created by the programmer, the array is empty, despite the fact
2306 * that visually, one column of items may be visible. This occurs
2307 * when the programmer uses the table like a list, adding items but
2308 * never creating a column.
2310 * Note: This is not the actual structure used by the receiver
2311 * to maintain its list of items, so modifying the array will
2312 * not affect the receiver.
2315 * @return the items in the receiver
2317 * @exception SWTException <ul>
2318 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
2319 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
2322 * @see Table#getColumnOrder()
2323 * @see Table#setColumnOrder(int[])
2324 * @see TableColumn#getMoveable()
2325 * @see TableColumn#setMoveable(boolean)
2328 public TableColumn [] getColumns () {
2330 TableColumn [] result = new TableColumn [columnCount];
2331 System.arraycopy (columns, 0, result, 0, columnCount);
2335 int getFocusIndex () {
2337 return (int)OS.SendMessage (handle, OS.LVM_GETNEXTITEM, -1, OS.LVNI_FOCUSED);
2341 * Returns the width in points of a grid line.
2343 * @return the width of a grid line in points
2345 * @exception SWTException <ul>
2346 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
2347 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
2350 public int getGridLineWidth () {
2352 return DPIUtil.autoScaleDown(getGridLineWidthInPixels());
2355 int getGridLineWidthInPixels () {
2360 * Returns the header background color.
2362 * @return the receiver's header background color.
2364 * @exception SWTException <ul>
2365 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
2366 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
2370 public Color getHeaderBackground () {
2372 return Color.win32_new (display, getHeaderBackgroundPixel());
2375 private int getHeaderBackgroundPixel() {
2376 return headerBackground != -1 ? headerBackground : defaultBackground();
2380 * Returns the header foreground color.
2382 * @return the receiver's header foreground color.
2384 * @exception SWTException <ul>
2385 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
2386 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
2390 public Color getHeaderForeground () {
2392 return Color.win32_new (display, getHeaderForegroundPixel());
2395 private int getHeaderForegroundPixel() {
2396 return headerForeground != -1 ? headerForeground : defaultForeground();
2400 * Returns the height of the receiver's header
2402 * @return the height of the header or zero if the header is not visible
2404 * @exception SWTException <ul>
2405 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
2406 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
2411 public int getHeaderHeight () {
2413 return DPIUtil.autoScaleDown(getHeaderHeightInPixels ());
2416 int getHeaderHeightInPixels () {
2417 if (hwndHeader == 0) return 0;
2418 RECT rect = new RECT ();
2419 OS.GetWindowRect (hwndHeader, rect);
2420 return rect.bottom - rect.top;
2424 * Returns <code>true</code> if the receiver's header is visible,
2425 * and <code>false</code> otherwise.
2427 * If one of the receiver's ancestors is not visible or some
2428 * other condition makes the receiver not visible, this method
2429 * may still indicate that it is considered visible even though
2430 * it may not actually be showing.
2433 * @return the receiver's header's visibility state
2435 * @exception SWTException <ul>
2436 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
2437 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
2440 public boolean getHeaderVisible () {
2442 int bits = OS.GetWindowLong (handle, OS.GWL_STYLE);
2443 return (bits & OS.LVS_NOCOLUMNHEADER) == 0;
2447 * Returns the item at the given, zero-relative index in the
2448 * receiver. Throws an exception if the index is out of range.
2450 * @param index the index of the item to return
2451 * @return the item at the given index
2453 * @exception IllegalArgumentException <ul>
2454 * <li>ERROR_INVALID_RANGE - if the index is not between 0 and the number of elements in the list minus 1 (inclusive)</li>
2456 * @exception SWTException <ul>
2457 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
2458 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
2461 public TableItem getItem (int index) {
2463 int count = (int)OS.SendMessage (handle, OS.LVM_GETITEMCOUNT, 0, 0);
2464 if (!(0 <= index && index < count)) error (SWT.ERROR_INVALID_RANGE);
2465 return _getItem (index);
2469 * Returns the item at the given point in the receiver
2470 * or null if no such item exists. The point is in the
2471 * coordinate system of the receiver.
2473 * The item that is returned represents an item that could be selected by the user.
2474 * For example, if selection only occurs in items in the first column, then null is
2475 * returned if the point is outside of the item.
2476 * Note that the SWT.FULL_SELECTION style hint, which specifies the selection policy,
2477 * determines the extent of the selection.
2480 * @param point the point used to locate the item
2481 * @return the item at the given point, or null if the point is not in a selectable item
2483 * @exception IllegalArgumentException <ul>
2484 * <li>ERROR_NULL_ARGUMENT - if the point is null</li>
2486 * @exception SWTException <ul>
2487 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
2488 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
2491 public TableItem getItem (Point point) {
2493 if (point == null) error (SWT.ERROR_NULL_ARGUMENT);
2494 return getItemInPixels (DPIUtil.autoScaleUp(point));
2497 TableItem getItemInPixels (Point point) {
2498 int count = (int)OS.SendMessage (handle, OS.LVM_GETITEMCOUNT, 0, 0);
2499 if (count == 0) return null;
2500 LVHITTESTINFO pinfo = new LVHITTESTINFO ();
2503 if ((style & SWT.FULL_SELECTION) == 0) {
2504 if (hooks (SWT.MeasureItem)) {
2506 * Bug in Windows. When LVM_SUBITEMHITTEST is used to hittest
2507 * a point that is above the table, instead of returning -1 to
2508 * indicate that the hittest failed, a negative index is returned.
2509 * The fix is to consider any value that is negative a failure.
2511 if (OS.SendMessage (handle, OS.LVM_SUBITEMHITTEST, 0, pinfo) < 0) {
2512 RECT rect = new RECT ();
2513 rect.left = OS.LVIR_ICON;
2514 ignoreCustomDraw = true;
2515 long code = OS.SendMessage (handle, OS.LVM_GETITEMRECT, 0, rect);
2516 ignoreCustomDraw = false;
2518 pinfo.x = rect.left;
2520 * Bug in Windows. When LVM_SUBITEMHITTEST is used to hittest
2521 * a point that is above the table, instead of returning -1 to
2522 * indicate that the hittest failed, a negative index is returned.
2523 * The fix is to consider any value that is negative a failure.
2525 OS.SendMessage (handle, OS.LVM_SUBITEMHITTEST, 0, pinfo);
2526 if (pinfo.iItem < 0) pinfo.iItem = -1;
2529 if (pinfo.iItem != -1 && pinfo.iSubItem == 0) {
2530 if (hitTestSelection (pinfo.iItem, pinfo.x, pinfo.y)) {
2531 return _getItem (pinfo.iItem);
2537 OS.SendMessage (handle, OS.LVM_HITTEST, 0, pinfo);
2538 if (pinfo.iItem != -1) {
2540 * Bug in Windows. When the point that is used by
2541 * LVM_HITTEST is inside the header, Windows returns
2542 * the first item in the table. The fix is to check
2543 * when LVM_HITTEST returns the first item and make
2544 * sure that when the point is within the header,
2545 * the first item is not returned.
2547 if (pinfo.iItem == 0) {
2548 int bits = OS.GetWindowLong (handle, OS.GWL_STYLE);
2549 if ((bits & OS.LVS_NOCOLUMNHEADER) == 0) {
2550 if (hwndHeader != 0) {
2551 RECT rect = new RECT ();
2552 OS.GetWindowRect (hwndHeader, rect);
2553 POINT pt = new POINT ();
2556 OS.MapWindowPoints (handle, 0, pt, 1);
2557 if (OS.PtInRect (rect, pt)) return null;
2561 return _getItem (pinfo.iItem);
2567 * Returns the number of items contained in the receiver.
2569 * @return the number of items
2571 * @exception SWTException <ul>
2572 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
2573 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
2576 public int getItemCount () {
2578 return (int)OS.SendMessage (handle, OS.LVM_GETITEMCOUNT, 0, 0);
2582 * Returns the height of the area which would be used to
2583 * display <em>one</em> of the items in the receiver.
2585 * @return the height of one item
2587 * @exception SWTException <ul>
2588 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
2589 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
2592 public int getItemHeight () {
2594 return DPIUtil.autoScaleDown(getItemHeightInPixels());
2597 int getItemHeightInPixels () {
2598 if (!painted && hooks (SWT.MeasureItem)) hitTestSelection (0, 0, 0);
2599 long empty = OS.SendMessage (handle, OS.LVM_APPROXIMATEVIEWRECT, 0, 0);
2600 long oneItem = OS.SendMessage (handle, OS.LVM_APPROXIMATEVIEWRECT, 1, 0);
2601 return OS.HIWORD (oneItem) - OS.HIWORD (empty);
2605 * Returns a (possibly empty) array of <code>TableItem</code>s which
2606 * are the items in the receiver.
2608 * Note: This is not the actual structure used by the receiver
2609 * to maintain its list of items, so modifying the array will
2610 * not affect the receiver.
2613 * @return the items in the receiver
2615 * @exception SWTException <ul>
2616 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
2617 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
2620 public TableItem [] getItems () {
2622 int count = (int)OS.SendMessage (handle, OS.LVM_GETITEMCOUNT, 0, 0);
2623 TableItem [] result = new TableItem [count];
2624 if ((style & SWT.VIRTUAL) != 0) {
2625 for (int i=0; i<count; i++) {
2626 result [i] = _getItem (i);
2629 _getItems (result, count);
2635 * Returns <code>true</code> if the receiver's lines are visible,
2636 * and <code>false</code> otherwise. Note that some platforms draw
2637 * grid lines while others may draw alternating row colors.
2639 * If one of the receiver's ancestors is not visible or some
2640 * other condition makes the receiver not visible, this method
2641 * may still indicate that it is considered visible even though
2642 * it may not actually be showing.
2645 * @return the visibility state of the lines
2647 * @exception SWTException <ul>
2648 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
2649 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
2652 public boolean getLinesVisible () {
2654 return _getLinesVisible();
2657 private boolean _getLinesVisible() {
2658 int bits = (int)OS.SendMessage (handle, OS.LVM_GETEXTENDEDLISTVIEWSTYLE, 0, 0);
2659 return (bits & OS.LVS_EX_GRIDLINES) != 0;
2663 * Returns an array of <code>TableItem</code>s that are currently
2664 * selected in the receiver. The order of the items is unspecified.
2665 * An empty array indicates that no items are selected.
2667 * Note: This is not the actual structure used by the receiver
2668 * to maintain its selection, so modifying the array will
2669 * not affect the receiver.
2671 * @return an array representing the selection
2673 * @exception SWTException <ul>
2674 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
2675 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
2678 public TableItem [] getSelection () {
2680 int i = -1, j = 0, count = (int)OS.SendMessage (handle, OS.LVM_GETSELECTEDCOUNT, 0, 0);
2681 TableItem [] result = new TableItem [count];
2682 while ((i = (int)OS.SendMessage (handle, OS.LVM_GETNEXTITEM, i, OS.LVNI_SELECTED)) != -1) {
2683 result [j++] = _getItem (i);
2689 * Returns the number of selected items contained in the receiver.
2691 * @return the number of selected items
2693 * @exception SWTException <ul>
2694 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
2695 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
2698 public int getSelectionCount () {
2700 return (int)OS.SendMessage (handle, OS.LVM_GETSELECTEDCOUNT, 0, 0);
2704 * Returns the zero-relative index of the item which is currently
2705 * selected in the receiver, or -1 if no item is selected.
2707 * @return the index of the selected item
2709 * @exception SWTException <ul>
2710 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
2711 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
2714 public int getSelectionIndex () {
2716 int focusIndex = (int)OS.SendMessage (handle, OS.LVM_GETNEXTITEM, -1, OS.LVNI_FOCUSED);
2717 int selectedIndex = (int)OS.SendMessage (handle, OS.LVM_GETNEXTITEM, -1, OS.LVNI_SELECTED);
2718 if (focusIndex == selectedIndex) return selectedIndex;
2720 while ((i = (int)OS.SendMessage (handle, OS.LVM_GETNEXTITEM, i, OS.LVNI_SELECTED)) != -1) {
2721 if (i == focusIndex) return i;
2723 return selectedIndex;
2727 * Returns the zero-relative indices of the items which are currently
2728 * selected in the receiver. The order of the indices is unspecified.
2729 * The array is empty if no items are selected.
2731 * Note: This is not the actual structure used by the receiver
2732 * to maintain its selection, so modifying the array will
2733 * not affect the receiver.
2735 * @return the array of indices of the selected items
2737 * @exception SWTException <ul>
2738 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
2739 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
2742 public int [] getSelectionIndices () {
2744 int i = -1, j = 0, count = (int)OS.SendMessage (handle, OS.LVM_GETSELECTEDCOUNT, 0, 0);
2745 int [] result = new int [count];
2746 while ((i = (int)OS.SendMessage (handle, OS.LVM_GETNEXTITEM, i, OS.LVNI_SELECTED)) != -1) {
2753 * Returns the column which shows the sort indicator for
2754 * the receiver. The value may be null if no column shows
2755 * the sort indicator.
2757 * @return the sort indicator
2759 * @exception SWTException <ul>
2760 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
2761 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
2764 * @see #setSortColumn(TableColumn)
2768 public TableColumn getSortColumn () {
2773 int getSortColumnPixel () {
2774 int pixel = OS.IsWindowEnabled (handle) || hasCustomBackground() ? getBackgroundPixel () : OS.GetSysColor (OS.COLOR_3DFACE);
2775 return getSlightlyDifferentBackgroundColor(pixel);
2779 * Returns the direction of the sort indicator for the receiver.
2780 * The value will be one of <code>UP</code>, <code>DOWN</code>
2781 * or <code>NONE</code>.
2783 * @return the sort direction
2785 * @exception SWTException <ul>
2786 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
2787 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
2790 * @see #setSortDirection(int)
2794 public int getSortDirection () {
2796 return sortDirection;
2800 * Returns the zero-relative index of the item which is currently
2801 * at the top of the receiver. This index can change when items are
2802 * scrolled or new items are added or removed.
2804 * @return the index of the top item
2806 * @exception SWTException <ul>
2807 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
2808 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
2811 public int getTopIndex () {
2814 * Bug in Windows. Under rare circumstances, LVM_GETTOPINDEX
2815 * can return a negative number. When this happens, the table
2816 * is displaying blank lines at the top of the controls. The
2817 * fix is to check for a negative number and return zero instead.
2819 return Math.max (0, (int)OS.SendMessage (handle, OS.LVM_GETTOPINDEX, 0, 0));
2822 boolean hasChildren () {
2823 long hwndChild = OS.GetWindow (handle, OS.GW_CHILD);
2824 while (hwndChild != 0) {
2825 if (hwndChild != hwndHeader) return true;
2826 hwndChild = OS.GetWindow (hwndChild, OS.GW_HWNDNEXT);
2831 boolean hitTestSelection (int index, int x, int y) {
2832 int count = (int)OS.SendMessage (handle, OS.LVM_GETITEMCOUNT, 0, 0);
2833 if (count == 0) return false;
2834 if (!hooks (SWT.MeasureItem)) return false;
2835 boolean result = false;
2836 if (0 <= index && index < count) {
2837 TableItem item = _getItem (index);
2838 long hDC = OS.GetDC (handle);
2839 long oldFont = 0, newFont = OS.SendMessage (handle, OS.WM_GETFONT, 0, 0);
2840 if (newFont != 0) oldFont = OS.SelectObject (hDC, newFont);
2841 long hFont = item.fontHandle (0);
2842 if (hFont != -1) hFont = OS.SelectObject (hDC, hFont);
2843 Event event = sendMeasureItemEvent (item, index, 0, hDC);
2844 if (event.getBoundsInPixels ().contains (x, y)) result = true;
2845 if (hFont != -1) hFont = OS.SelectObject (hDC, hFont);
2846 if (newFont != 0) OS.SelectObject (hDC, oldFont);
2847 OS.ReleaseDC (handle, hDC);
2848 // if (isDisposed () || item.isDisposed ()) return false;
2853 int imageIndex (Image image, int column) {
2854 if (image == null) return OS.I_IMAGENONE;
2856 firstColumnImage = true;
2858 setSubImagesVisible (true);
2860 if (imageList == null) {
2861 Rectangle bounds = image.getBoundsInPixels ();
2862 imageList = display.getImageList (style & SWT.RIGHT_TO_LEFT, bounds.width, bounds.height);
2863 int index = imageList.indexOf (image);
2864 if (index == -1) index = imageList.add (image);
2865 long hImageList = imageList.getHandle ();
2867 * Bug in Windows. Making any change to an item that
2868 * changes the item height of a table while the table
2869 * is scrolled can cause the lines to draw incorrectly.
2870 * This happens even when the lines are not currently
2871 * visible and are shown afterwards. The fix is to
2872 * save the top index, scroll to the top of the table
2873 * and then restore the original top index.
2875 int topIndex = getTopIndex ();
2876 if (topIndex != 0) {
2880 OS.SendMessage (handle, OS.LVM_SETIMAGELIST, OS.LVSIL_SMALL, hImageList);
2881 if (headerImageList != null) {
2882 long hHeaderImageList = headerImageList.getHandle ();
2883 OS.SendMessage (hwndHeader, OS.HDM_SETIMAGELIST, 0, hHeaderImageList);
2885 fixCheckboxImageList (false);
2886 setItemHeight (false);
2887 if (topIndex != 0) {
2888 setTopIndex (topIndex);
2893 int index = imageList.indexOf (image);
2894 if (index != -1) return index;
2895 return imageList.add (image);
2898 int imageIndexHeader (Image image) {
2899 if (image == null) return OS.I_IMAGENONE;
2900 if (headerImageList == null) {
2901 Rectangle bounds = image.getBoundsInPixels ();
2902 headerImageList = display.getImageList (style & SWT.RIGHT_TO_LEFT, bounds.width, bounds.height);
2903 int index = headerImageList.indexOf (image);
2904 if (index == -1) index = headerImageList.add (image);
2905 long hImageList = headerImageList.getHandle ();
2906 OS.SendMessage (hwndHeader, OS.HDM_SETIMAGELIST, 0, hImageList);
2909 int index = headerImageList.indexOf (image);
2910 if (index != -1) return index;
2911 return headerImageList.add (image);
2915 * Searches the receiver's list starting at the first column
2916 * (index 0) until a column is found that is equal to the
2917 * argument, and returns the index of that column. If no column
2918 * is found, returns -1.
2920 * @param column the search column
2921 * @return the index of the column
2923 * @exception IllegalArgumentException <ul>
2924 * <li>ERROR_NULL_ARGUMENT - if the column is null</li>
2926 * @exception SWTException <ul>
2927 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
2928 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
2931 public int indexOf (TableColumn column) {
2933 if (column == null) error (SWT.ERROR_NULL_ARGUMENT);
2934 for (int i=0; i<columnCount; i++) {
2935 if (columns [i] == column) return i;
2941 * Searches the receiver's list starting at the first item
2942 * (index 0) until an item is found that is equal to the
2943 * argument, and returns the index of that item. If no item
2944 * is found, returns -1.
2946 * @param item the search item
2947 * @return the index of the item
2949 * @exception IllegalArgumentException <ul>
2950 * <li>ERROR_NULL_ARGUMENT - if the item is null</li>
2952 * @exception SWTException <ul>
2953 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
2954 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
2957 public int indexOf (TableItem item) {
2959 if (item == null) error (SWT.ERROR_NULL_ARGUMENT);
2960 //TODO - find other loops that can be optimized
2962 int count = (int)OS.SendMessage (handle, OS.LVM_GETITEMCOUNT, 0, 0);
2963 if (1 <= lastIndexOf && lastIndexOf < count - 1) {
2964 if (_getItem (lastIndexOf, false) == item) return lastIndexOf;
2965 if (_getItem (lastIndexOf + 1, false) == item) return ++lastIndexOf;
2966 if (_getItem (lastIndexOf - 1, false) == item) return --lastIndexOf;
2968 if (lastIndexOf < count / 2) {
2969 for (int i=0; i<count; i++) {
2970 if (_getItem (i, false) == item) return lastIndexOf = i;
2973 for (int i=count - 1; i>=0; --i) {
2974 if (_getItem (i, false) == item) return lastIndexOf = i;
2978 for (int i=0; i<keyCount; i++) {
2979 if (items [i] == item) return keys [i];
2985 boolean isCustomToolTip () {
2986 return hooks (SWT.MeasureItem);
2989 boolean isOptimizedRedraw () {
2990 if ((style & SWT.H_SCROLL) == 0 || (style & SWT.V_SCROLL) == 0) return false;
2991 return !hasChildren () && !hooks (SWT.Paint) && !filters (SWT.Paint) && !customHeaderDrawing();
2995 * Returns <code>true</code> if the item is selected,
2996 * and <code>false</code> otherwise. Indices out of
2997 * range are ignored.
2999 * @param index the index of the item
3000 * @return the selection state of the item at the index
3002 * @exception SWTException <ul>
3003 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
3004 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
3007 public boolean isSelected (int index) {
3009 LVITEM lvItem = new LVITEM ();
3010 lvItem.mask = OS.LVIF_STATE;
3011 lvItem.stateMask = OS.LVIS_SELECTED;
3012 lvItem.iItem = index;
3013 long result = OS.SendMessage (handle, OS.LVM_GETITEM, 0, lvItem);
3014 return (result != 0) && ((lvItem.state & OS.LVIS_SELECTED) != 0);
3020 if (hwndHeader != 0) display.addControl (hwndHeader, this);
3024 void releaseChildren (boolean destroy) {
3026 int itemCount = (int)OS.SendMessage (handle, OS.LVM_GETITEMCOUNT, 0, 0);
3028 for (int i=0; i<itemCount; i++) {
3029 TableItem item = _getItem (i, false);
3030 if (item != null && !item.isDisposed ()) item.release (false);
3033 for (int i=0; i<keyCount; i++) {
3034 TableItem item = items [i];
3035 if (item != null && !item.isDisposed ()) item.release (false);
3040 if (columns != null) {
3041 for (int i=0; i<columnCount; i++) {
3042 TableColumn column = columns [i];
3043 if (!column.isDisposed ()) column.release (false);
3047 super.releaseChildren (destroy);
3051 void releaseWidget () {
3052 super.releaseWidget ();
3055 if (imageList != null) {
3056 OS.SendMessage (handle, OS.LVM_SETIMAGELIST, OS.LVSIL_SMALL, 0);
3057 display.releaseImageList (imageList);
3059 if (headerImageList != null) {
3060 OS.SendMessage (hwndHeader, OS.HDM_SETIMAGELIST, 0, 0);
3061 display.releaseImageList (headerImageList);
3063 imageList = headerImageList = null;
3064 long hStateList = OS.SendMessage (handle, OS.LVM_GETIMAGELIST, OS.LVSIL_STATE, 0);
3065 OS.SendMessage (handle, OS.LVM_SETIMAGELIST, OS.LVSIL_STATE, 0);
3066 if (hStateList != 0) OS.ImageList_Destroy (hStateList);
3067 if (headerToolTipHandle != 0) OS.DestroyWindow (headerToolTipHandle);
3068 headerToolTipHandle = 0;
3072 * Removes the items from the receiver's list at the given
3073 * zero-relative indices.
3075 * @param indices the array of indices of the items
3077 * @exception IllegalArgumentException <ul>
3078 * <li>ERROR_INVALID_RANGE - if the index is not between 0 and the number of elements in the list minus 1 (inclusive)</li>
3079 * <li>ERROR_NULL_ARGUMENT - if the indices array is null</li>
3081 * @exception SWTException <ul>
3082 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
3083 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
3086 public void remove (int [] indices) {
3088 if (indices == null) error (SWT.ERROR_NULL_ARGUMENT);
3089 if (indices.length == 0) return;
3090 int [] newIndices = new int [indices.length];
3091 System.arraycopy (indices, 0, newIndices, 0, indices.length);
3093 int start = newIndices [newIndices.length - 1], end = newIndices [0];
3094 int count = (int)OS.SendMessage (handle, OS.LVM_GETITEMCOUNT, 0, 0);
3095 if (!(0 <= start && start <= end && end < count)) {
3096 error (SWT.ERROR_INVALID_RANGE);
3098 setDeferResize (true);
3100 for (int i=0; i<newIndices.length; i++) {
3101 int index = newIndices [i];
3102 if (index != last) {
3103 TableItem item = _getItem (index, false);
3104 if (item != null && !item.isDisposed ()) item.release (false);
3105 ignoreSelect = ignoreShrink = true;
3106 long code = OS.SendMessage (handle, OS.LVM_DELETEITEM, index, 0);
3107 ignoreSelect = ignoreShrink = false;
3108 if (code == 0) error (SWT.ERROR_ITEM_NOT_REMOVED);
3109 _removeItem(index, count);
3114 if (count == 0) setTableEmpty ();
3115 setDeferResize (false);
3119 * Removes the item from the receiver at the given
3120 * zero-relative index.
3122 * @param index the index for the item
3124 * @exception IllegalArgumentException <ul>
3125 * <li>ERROR_INVALID_RANGE - if the index is not between 0 and the number of elements in the list minus 1 (inclusive)</li>
3127 * @exception SWTException <ul>
3128 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
3129 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
3132 public void remove (int index) {
3134 int count = (int)OS.SendMessage (handle, OS.LVM_GETITEMCOUNT, 0, 0);
3135 if (!(0 <= index && index < count)) error (SWT.ERROR_INVALID_RANGE);
3136 TableItem item = _getItem (index, false);
3137 if (item != null && !item.isDisposed ()) item.release (false);
3138 setDeferResize (true);
3139 ignoreSelect = ignoreShrink = true;
3140 long code = OS.SendMessage (handle, OS.LVM_DELETEITEM, index, 0);
3141 ignoreSelect = ignoreShrink = false;
3142 if (code == 0) error (SWT.ERROR_ITEM_NOT_REMOVED);
3143 _removeItem (index, count);
3145 if (count == 0) setTableEmpty ();
3146 setDeferResize (false);
3150 * Removes the items from the receiver which are
3151 * between the given zero-relative start and end
3152 * indices (inclusive).
3154 * @param start the start of the range
3155 * @param end the end of the range
3157 * @exception IllegalArgumentException <ul>
3158 * <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>
3160 * @exception SWTException <ul>
3161 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
3162 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
3165 public void remove (int start, int end) {
3167 if (start > end) return;
3168 int count = (int)OS.SendMessage (handle, OS.LVM_GETITEMCOUNT, 0, 0);
3169 if (!(0 <= start && start <= end && end < count)) {
3170 error (SWT.ERROR_INVALID_RANGE);
3172 if (start == 0 && end == count - 1) {
3175 setDeferResize (true);
3177 while (index <= end) {
3178 TableItem item = _getItem (index, false);
3179 if (item != null && !item.isDisposed ()) item.release (false);
3180 ignoreSelect = ignoreShrink = true;
3181 long code = OS.SendMessage (handle, OS.LVM_DELETEITEM, start, 0);
3182 ignoreSelect = ignoreShrink = false;
3183 if (code == 0) break;
3186 _removeItems (start, index, count);
3187 if (index <= end) error (SWT.ERROR_ITEM_NOT_REMOVED);
3189 * This code is intentionally commented. It is not necessary
3190 * to check for an empty table because removeAll() was called
3191 * when the start == 0 and end == count - 1.
3193 //if (count - index == 0) setTableEmpty ();
3194 setDeferResize (false);
3199 * Removes all of the items from the receiver.
3201 * @exception SWTException <ul>
3202 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
3203 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
3206 public void removeAll () {
3208 int itemCount = (int)OS.SendMessage (handle, OS.LVM_GETITEMCOUNT, 0, 0);
3209 for (int i=0; i<itemCount; i++) {
3210 TableItem item = _getItem (i, false);
3211 if (item != null && !item.isDisposed ()) item.release (false);
3213 setDeferResize (true);
3214 ignoreSelect = ignoreShrink = true;
3215 long code = OS.SendMessage (handle, OS.LVM_DELETEALLITEMS, 0, 0);
3216 ignoreSelect = ignoreShrink = false;
3217 if (code == 0) error (SWT.ERROR_ITEM_NOT_REMOVED);
3219 setDeferResize (false);
3223 * Removes the listener from the collection of listeners who will
3224 * be notified when the user changes the receiver's selection.
3226 * @param listener the listener which should no longer be notified
3228 * @exception IllegalArgumentException <ul>
3229 * <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
3231 * @exception SWTException <ul>
3232 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
3233 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
3236 * @see SelectionListener
3237 * @see #addSelectionListener(SelectionListener)
3239 public void removeSelectionListener(SelectionListener listener) {
3241 if (listener == null) error (SWT.ERROR_NULL_ARGUMENT);
3242 if (eventTable == null) return;
3243 eventTable.unhook (SWT.Selection, listener);
3244 eventTable.unhook (SWT.DefaultSelection,listener);
3248 * Selects the items at the given zero-relative indices in the receiver.
3249 * The current selection is not cleared before the new items are selected.
3251 * If the item at a given index is not selected, it is selected.
3252 * If the item at a given index was already selected, it remains selected.
3253 * Indices that are out of range and duplicate indices are ignored.
3254 * If the receiver is single-select and multiple indices are specified,
3255 * then all indices are ignored.
3258 * @param indices the array of indices for the items to select
3260 * @exception IllegalArgumentException <ul>
3261 * <li>ERROR_NULL_ARGUMENT - if the array of indices is null</li>
3263 * @exception SWTException <ul>
3264 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
3265 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
3268 * @see Table#setSelection(int[])
3270 public void select (int [] indices) {
3272 if (indices == null) error (SWT.ERROR_NULL_ARGUMENT);
3273 int length = indices.length;
3274 if (length == 0 || ((style & SWT.SINGLE) != 0 && length > 1)) return;
3275 LVITEM lvItem = new LVITEM ();
3276 lvItem.state = OS.LVIS_SELECTED;
3277 lvItem.stateMask = OS.LVIS_SELECTED;
3278 for (int i=length-1; i>=0; --i) {
3280 * An index of -1 will apply the change to all
3281 * items. Ensure that indices are greater than -1.
3283 if (indices [i] >= 0) {
3284 ignoreSelect = true;
3285 OS.SendMessage (handle, OS.LVM_SETITEMSTATE, indices [i], lvItem);
3286 ignoreSelect = false;
3292 void reskinChildren (int flags) {
3294 int itemCount = (int)OS.SendMessage (handle, OS.LVM_GETITEMCOUNT, 0, 0);
3295 for (int i=0; i<itemCount; i++) {
3296 TableItem item = _getItem (i, false);
3297 if (item != null) item.reskin (flags);
3300 if (columns != null) {
3301 for (int i=0; i<columnCount; i++) {
3302 TableColumn column = columns [i];
3303 if (!column.isDisposed ()) column.reskin (flags);
3306 super.reskinChildren (flags);
3310 * Selects the item at the given zero-relative index in the receiver.
3311 * If the item at the index was already selected, it remains
3312 * selected. Indices that are out of range are ignored.
3314 * @param index the index of the item to select
3316 * @exception SWTException <ul>
3317 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
3318 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
3321 public void select (int index) {
3324 * An index of -1 will apply the change to all
3325 * items. Ensure that index is greater than -1.
3327 if (index < 0) return;
3328 LVITEM lvItem = new LVITEM ();
3329 lvItem.state = OS.LVIS_SELECTED;
3330 lvItem.stateMask = OS.LVIS_SELECTED;
3331 ignoreSelect = true;
3332 OS.SendMessage (handle, OS.LVM_SETITEMSTATE, index, lvItem);
3333 ignoreSelect = false;
3337 * Selects the items in the range specified by the given zero-relative
3338 * indices in the receiver. The range of indices is inclusive.
3339 * The current selection is not cleared before the new items are selected.
3341 * If an item in the given range is not selected, it is selected.
3342 * If an item in the given range was already selected, it remains selected.
3343 * Indices that are out of range are ignored and no items will be selected
3344 * if start is greater than end.
3345 * If the receiver is single-select and there is more than one item in the
3346 * given range, then all indices are ignored.
3349 * @param start the start of the range
3350 * @param end the end of the range
3352 * @exception SWTException <ul>
3353 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
3354 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
3357 * @see Table#setSelection(int,int)
3359 public void select (int start, int end) {
3361 if (end < 0 || start > end || ((style & SWT.SINGLE) != 0 && start != end)) return;
3362 int count = (int)OS.SendMessage (handle, OS.LVM_GETITEMCOUNT, 0, 0);
3363 if (count == 0 || start >= count) return;
3364 start = Math.max (0, start);
3365 end = Math.min (end, count - 1);
3366 if (start == 0 && end == count - 1) {
3370 * An index of -1 will apply the change to all
3371 * items. Indices must be greater than -1.
3373 LVITEM lvItem = new LVITEM ();
3374 lvItem.state = OS.LVIS_SELECTED;
3375 lvItem.stateMask = OS.LVIS_SELECTED;
3376 for (int i=start; i<=end; i++) {
3377 ignoreSelect = true;
3378 OS.SendMessage (handle, OS.LVM_SETITEMSTATE, i, lvItem);
3379 ignoreSelect = false;
3385 * Selects all of the items in the receiver.
3387 * If the receiver is single-select, do nothing.
3390 * @exception SWTException <ul>
3391 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
3392 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
3395 public void selectAll () {
3397 if ((style & SWT.SINGLE) != 0) return;
3398 LVITEM lvItem = new LVITEM ();
3399 lvItem.mask = OS.LVIF_STATE;
3400 lvItem.state = OS.LVIS_SELECTED;
3401 lvItem.stateMask = OS.LVIS_SELECTED;
3402 ignoreSelect = true;
3403 OS.SendMessage (handle, OS.LVM_SETITEMSTATE, -1, lvItem);
3404 ignoreSelect = false;
3407 void sendEraseItemEvent (TableItem item, NMLVCUSTOMDRAW nmcd, long lParam, Event measureEvent) {
3408 long hDC = nmcd.hdc;
3409 int clrText = item.cellForeground != null ? item.cellForeground [nmcd.iSubItem] : -1;
3410 if (clrText == -1) clrText = item.foreground;
3412 if (OS.IsAppThemed ()) {
3413 if (sortColumn != null && sortDirection != SWT.NONE) {
3414 if (findImageControl () == null) {
3415 if (indexOf (sortColumn) == nmcd.iSubItem) {
3416 clrTextBk = getSortColumnPixel ();
3421 clrTextBk = item.cellBackground != null ? item.cellBackground [nmcd.iSubItem] : -1;
3422 if (clrTextBk == -1) clrTextBk = item.background;
3424 * Bug in Windows. For some reason, CDIS_SELECTED always set,
3425 * even for items that are not selected. The fix is to get
3426 * the selection state from the item.
3428 LVITEM lvItem = new LVITEM ();
3429 lvItem.mask = OS.LVIF_STATE;
3430 lvItem.stateMask = OS.LVIS_SELECTED;
3431 lvItem.iItem = (int)nmcd.dwItemSpec;
3432 long result = OS.SendMessage (handle, OS.LVM_GETITEM, 0, lvItem);
3433 boolean selected = (result != 0 && (lvItem.state & OS.LVIS_SELECTED) != 0);
3434 GCData data = new GCData ();
3435 data.device = display;
3436 int clrSelectionBk = -1;
3437 boolean drawSelected = false, drawBackground = false, drawHot = false, drawDrophilited = false;
3438 if (nmcd.iSubItem == 0 || (style & SWT.FULL_SELECTION) != 0) {
3439 drawHot = hotIndex == nmcd.dwItemSpec;
3440 drawDrophilited = (nmcd.uItemState & OS.CDIS_DROPHILITED) != 0;
3442 if (OS.IsWindowEnabled (handle)) {
3443 if (selected && (nmcd.iSubItem == 0 || (style & SWT.FULL_SELECTION) != 0)) {
3444 if (OS.GetFocus () == handle || display.getHighContrast ()) {
3445 drawSelected = true;
3446 data.foreground = OS.GetSysColor (OS.COLOR_HIGHLIGHTTEXT);
3447 data.background = clrSelectionBk = OS.GetSysColor (OS.COLOR_HIGHLIGHT);
3449 drawSelected = (style & SWT.HIDE_SELECTION) == 0;
3450 data.foreground = OS.GetTextColor (hDC);
3451 data.background = clrSelectionBk = OS.GetSysColor (OS.COLOR_3DFACE);
3453 if (explorerTheme) {
3454 data.foreground = clrText != -1 ? clrText : getForegroundPixel ();
3457 drawBackground = clrTextBk != -1;
3459 * Bug in Windows. When LVM_SETTEXTBKCOLOR, LVM_SETBKCOLOR
3460 * or LVM_SETTEXTCOLOR is used to set the background color of
3461 * the the text or the control, the color is not set in the HDC
3462 * that is provided in Custom Draw. The fix is to explicitly
3465 if (clrText == -1 || clrTextBk == -1) {
3466 Control control = findBackgroundControl ();
3467 if (control == null) control = this;
3468 if (clrText == -1) clrText = control.getForegroundPixel ();
3469 if (clrTextBk == -1) clrTextBk = control.getBackgroundPixel ();
3471 data.foreground = clrText != -1 ? clrText : OS.GetTextColor (hDC);
3472 data.background = clrTextBk != -1 ? clrTextBk : OS.GetBkColor (hDC);
3475 data.foreground = OS.GetSysColor (OS.COLOR_GRAYTEXT);
3476 data.background = OS.GetSysColor (OS.COLOR_3DFACE);
3477 if (selected) clrSelectionBk = data.background;
3479 data.font = item.getFont (nmcd.iSubItem);
3480 data.uiState = (int)OS.SendMessage (handle, OS.WM_QUERYUISTATE, 0, 0);
3481 int nSavedDC = OS.SaveDC (hDC);
3482 GC gc = GC.win32_new (hDC, data);
3483 RECT cellRect = item.getBounds ((int)nmcd.dwItemSpec, nmcd.iSubItem, true, true, true, true, hDC);
3484 Event event = new Event ();
3487 event.index = nmcd.iSubItem;
3488 event.detail |= SWT.FOREGROUND;
3489 // if ((nmcd.uItemState & OS.CDIS_FOCUS) != 0) {
3490 if (OS.SendMessage (handle, OS.LVM_GETNEXTITEM, -1, OS.LVNI_FOCUSED) == nmcd.dwItemSpec) {
3491 if (nmcd.iSubItem == 0 || (style & SWT.FULL_SELECTION) != 0) {
3492 if (handle == OS.GetFocus ()) {
3493 int uiState = (int)OS.SendMessage (handle, OS.WM_QUERYUISTATE, 0, 0);
3494 if ((uiState & OS.UISF_HIDEFOCUS) == 0) event.detail |= SWT.FOCUSED;
3498 boolean focused = (event.detail & SWT.FOCUSED) != 0;
3499 if (drawHot) event.detail |= SWT.HOT;
3500 if (drawSelected) event.detail |= SWT.SELECTED;
3501 if (drawBackground) event.detail |= SWT.BACKGROUND;
3502 Rectangle boundsInPixels = new Rectangle (cellRect.left, cellRect.top, cellRect.right - cellRect.left, cellRect.bottom - cellRect.top);
3503 event.setBoundsInPixels (boundsInPixels);
3504 gc.setClipping (DPIUtil.autoScaleDown(boundsInPixels));
3505 sendEvent (SWT.EraseItem, event);
3507 int clrSelectionText = data.foreground;
3509 OS.RestoreDC (hDC, nSavedDC);
3510 if (isDisposed () || item.isDisposed ()) return;
3512 ignoreDrawForeground = (event.detail & SWT.FOREGROUND) == 0;
3513 ignoreDrawBackground = (event.detail & SWT.BACKGROUND) == 0;
3514 ignoreDrawSelection = (event.detail & SWT.SELECTED) == 0;
3515 ignoreDrawFocus = (event.detail & SWT.FOCUSED) == 0;
3516 ignoreDrawHot = (event.detail & SWT.HOT) == 0;
3518 ignoreDrawForeground = ignoreDrawBackground = ignoreDrawSelection = ignoreDrawFocus = ignoreDrawHot = true;
3521 if (ignoreDrawSelection) {
3522 ignoreDrawHot = true;
3523 if (nmcd.iSubItem == 0 || (style & SWT.FULL_SELECTION) != 0) {
3524 selectionForeground = clrSelectionText;
3526 nmcd.uItemState &= ~OS.CDIS_SELECTED;
3527 OS.MoveMemory (lParam, nmcd, NMLVCUSTOMDRAW.sizeof);
3530 if (ignoreDrawSelection) {
3531 nmcd.uItemState |= OS.CDIS_SELECTED;
3532 OS.MoveMemory (lParam, nmcd, NMLVCUSTOMDRAW.sizeof);
3535 boolean firstColumn = nmcd.iSubItem == OS.SendMessage (hwndHeader, OS.HDM_ORDERTOINDEX, 0, 0);
3536 if (ignoreDrawForeground && ignoreDrawHot && !drawDrophilited) {
3537 if (!ignoreDrawBackground && drawBackground) {
3538 RECT backgroundRect = item.getBounds ((int)nmcd.dwItemSpec, nmcd.iSubItem, true, false, true, false, hDC);
3539 fillBackground (hDC, clrTextBk, backgroundRect);
3543 if (!ignoreDrawHot || !ignoreDrawSelection || !ignoreDrawFocus || drawDrophilited) {
3544 boolean fullText = (style & SWT.FULL_SELECTION) != 0 || !firstColumn;
3545 RECT textRect = item.getBounds ((int)nmcd.dwItemSpec, nmcd.iSubItem, true, false, fullText, false, hDC);
3546 if ((style & SWT.FULL_SELECTION) == 0) {
3547 if (measureEvent != null) {
3548 Rectangle boundInPixels = measureEvent.getBoundsInPixels();
3549 textRect.right = Math.min (cellRect.right, boundInPixels.x + boundInPixels.width);
3551 if (!ignoreDrawFocus) {
3552 nmcd.uItemState &= ~OS.CDIS_FOCUS;
3553 OS.MoveMemory (lParam, nmcd, NMLVCUSTOMDRAW.sizeof);
3554 focusRect = textRect;
3557 if (explorerTheme) {
3558 if (!ignoreDrawHot || drawDrophilited || (!ignoreDrawSelection && clrSelectionBk != -1)) {
3559 RECT pClipRect = new RECT ();
3560 OS.SetRect (pClipRect, nmcd.left, nmcd.top, nmcd.right, nmcd.bottom);
3561 RECT rect = new RECT ();
3562 OS.SetRect (rect, nmcd.left, nmcd.top, nmcd.right, nmcd.bottom);
3563 if ((style & SWT.FULL_SELECTION) != 0) {
3564 int count = (int)OS.SendMessage (hwndHeader, OS.HDM_GETITEMCOUNT, 0, 0);
3565 int index = (int)OS.SendMessage (hwndHeader, OS.HDM_ORDERTOINDEX, count - 1, 0);
3566 RECT headerRect = new RECT ();
3567 OS.SendMessage (hwndHeader, OS.HDM_GETITEMRECT, index, headerRect);
3568 OS.MapWindowPoints (hwndHeader, handle, headerRect, 2);
3569 rect.right = headerRect.right;
3570 index = (int)OS.SendMessage (hwndHeader, OS.HDM_ORDERTOINDEX, 0, 0);
3571 OS.SendMessage (hwndHeader, OS.HDM_GETITEMRECT, index, headerRect);
3572 OS.MapWindowPoints (hwndHeader, handle, headerRect, 2);
3573 rect.left = headerRect.left;
3574 pClipRect.left = cellRect.left;
3575 pClipRect.right += EXPLORER_EXTRA;
3577 rect.right += EXPLORER_EXTRA;
3578 pClipRect.right += EXPLORER_EXTRA;
3580 long hTheme = OS.OpenThemeData (handle, Display.LISTVIEW);
3581 int iStateId = selected ? OS.LISS_SELECTED : OS.LISS_HOT;
3582 if (OS.GetFocus () != handle && selected && !drawHot) iStateId = OS.LISS_SELECTEDNOTFOCUS;
3583 if (drawDrophilited) iStateId = OS.LISS_SELECTED;
3584 OS.DrawThemeBackground (hTheme, hDC, OS.LVP_LISTITEM, iStateId, rect, pClipRect);
3585 OS.CloseThemeData (hTheme);
3588 if (!ignoreDrawSelection && clrSelectionBk != -1) fillBackground (hDC, clrSelectionBk, textRect);
3591 if (focused && ignoreDrawFocus) {
3592 nmcd.uItemState &= ~OS.CDIS_FOCUS;
3593 OS.MoveMemory (lParam, nmcd, NMLVCUSTOMDRAW.sizeof);
3595 if (ignoreDrawForeground) {
3596 RECT clipRect = item.getBounds ((int)nmcd.dwItemSpec, nmcd.iSubItem, true, true, true, false, hDC);
3598 OS.SelectClipRgn (hDC, 0);
3599 OS.ExcludeClipRect (hDC, clipRect.left, clipRect.top, clipRect.right, clipRect.bottom);
3603 Event sendEraseItemEvent (TableItem item, NMTTCUSTOMDRAW nmcd, int column, RECT cellRect) {
3604 int nSavedDC = OS.SaveDC (nmcd.hdc);
3605 RECT insetRect = toolTipInset (cellRect);
3606 OS.SetWindowOrgEx (nmcd.hdc, insetRect.left, insetRect.top, null);
3607 GCData data = new GCData ();
3608 data.device = display;
3609 data.foreground = OS.GetTextColor (nmcd.hdc);
3610 data.background = OS.GetBkColor (nmcd.hdc);
3611 data.font = item.getFont (column);
3612 data.uiState = (int)OS.SendMessage (handle, OS.WM_QUERYUISTATE, 0, 0);
3613 GC gc = GC.win32_new (nmcd.hdc, data);
3614 Event event = new Event ();
3616 event.index = column;
3618 event.detail |= SWT.FOREGROUND;
3619 event.setBoundsInPixels(new Rectangle(cellRect.left, cellRect.top, cellRect.right - cellRect.left, cellRect.bottom - cellRect.top));
3620 //gc.setClipping (event.x, event.y, event.width, event.height);
3621 sendEvent (SWT.EraseItem, event);
3623 //int newTextClr = data.foreground;
3625 OS.RestoreDC (nmcd.hdc, nSavedDC);
3629 Event sendMeasureItemEvent (TableItem item, int row, int column, long hDC) {
3630 GCData data = new GCData ();
3631 data.device = display;
3632 data.font = item.getFont (column);
3633 int nSavedDC = OS.SaveDC (hDC);
3634 GC gc = GC.win32_new (hDC, data);
3635 RECT itemRect = item.getBounds (row, column, true, true, false, false, hDC);
3636 Event event = new Event ();
3639 event.index = column;
3640 event.setBoundsInPixels(new Rectangle(itemRect.left, itemRect.top, itemRect.right - itemRect.left, itemRect.bottom - itemRect.top));
3641 boolean drawSelected = false;
3642 if (OS.IsWindowEnabled (handle)) {
3643 LVITEM lvItem = new LVITEM ();
3644 lvItem.mask = OS.LVIF_STATE;
3645 lvItem.stateMask = OS.LVIS_SELECTED;
3646 lvItem.iItem = (int)row;
3647 long result = OS.SendMessage (handle, OS.LVM_GETITEM, 0, lvItem);
3648 boolean selected = (result != 0 && (lvItem.state & OS.LVIS_SELECTED) != 0);
3649 if (selected && (column == 0 || (style & SWT.FULL_SELECTION) != 0)) {
3650 if (OS.GetFocus () == handle || display.getHighContrast ()) {
3651 drawSelected = true;
3653 drawSelected = (style & SWT.HIDE_SELECTION) == 0;
3657 if (drawSelected) event.detail |= SWT.SELECTED;
3658 sendEvent (SWT.MeasureItem, event);
3661 OS.RestoreDC (hDC, nSavedDC);
3662 if (!isDisposed () && !item.isDisposed ()) {
3663 Rectangle boundsInPixels = event.getBoundsInPixels();
3664 if (columnCount == 0) {
3665 int width = (int)OS.SendMessage (handle, OS.LVM_GETCOLUMNWIDTH, 0, 0);
3666 if (boundsInPixels.x + boundsInPixels.width > width) setScrollWidth (boundsInPixels.x + boundsInPixels.width);
3668 long empty = OS.SendMessage (handle, OS.LVM_APPROXIMATEVIEWRECT, 0, 0);
3669 long oneItem = OS.SendMessage (handle, OS.LVM_APPROXIMATEVIEWRECT, 1, 0);
3670 int itemHeight = OS.HIWORD (oneItem) - OS.HIWORD (empty);
3672 * Possible recursion: when setItemHeight() is called during
3673 * SWT.MeasureItem event processing with a non-zero table-row
3674 * selection. Refer bug 400174 and 458786
3676 if (!settingItemHeight && boundsInPixels.height > itemHeight) {
3677 settingItemHeight = true;
3678 setItemHeight (boundsInPixels.height);
3679 settingItemHeight = false;
3685 LRESULT sendMouseDownEvent (int type, int button, int msg, long wParam, long lParam) {
3686 Display display = this.display;
3687 display.captureChanged = false;
3688 if (!sendMouseEvent (type, button, handle, msg, wParam, lParam)) {
3689 if (!display.captureChanged && !isDisposed ()) {
3690 if (OS.GetCapture () != handle) OS.SetCapture (handle);
3692 return LRESULT.ZERO;
3696 * Feature in Windows. Inside WM_LBUTTONDOWN and WM_RBUTTONDOWN,
3697 * the widget starts a modal loop to determine if the user wants
3698 * to begin a drag/drop operation or marque select. Unfortunately,
3699 * this modal loop eats the corresponding mouse up. The fix is to
3700 * detect the cases when the modal loop has eaten the mouse up and
3701 * issue a fake mouse up.
3703 * By observation, when the mouse is clicked anywhere but the check
3704 * box, the widget eats the mouse up. When the mouse is dragged,
3705 * the widget does not eat the mouse up.
3707 LVHITTESTINFO pinfo = new LVHITTESTINFO ();
3708 pinfo.x = OS.GET_X_LPARAM (lParam);
3709 pinfo.y = OS.GET_Y_LPARAM (lParam);
3710 OS.SendMessage (handle, OS.LVM_HITTEST, 0, pinfo);
3711 if ((style & SWT.FULL_SELECTION) == 0) {
3712 if (hooks (SWT.MeasureItem)) {
3714 * Bug in Windows. When LVM_SUBITEMHITTEST is used to hittest
3715 * a point that is above the table, instead of returning -1 to
3716 * indicate that the hittest failed, a negative index is returned.
3717 * The fix is to consider any value that is negative a failure.
3719 if (OS.SendMessage (handle, OS.LVM_SUBITEMHITTEST, 0, pinfo) < 0) {
3720 int count = (int)OS.SendMessage (handle, OS.LVM_GETITEMCOUNT, 0, 0);
3722 RECT rect = new RECT ();
3723 rect.left = OS.LVIR_ICON;
3724 ignoreCustomDraw = true;
3725 long code = OS.SendMessage (handle, OS.LVM_GETITEMRECT, 0, rect);
3726 ignoreCustomDraw = false;
3728 pinfo.x = rect.left;
3730 * Bug in Windows. When LVM_SUBITEMHITTEST is used to hittest
3731 * a point that is above the table, instead of returning -1 to
3732 * indicate that the hittest failed, a negative index is returned.
3733 * The fix is to consider any value that is negative a failure.
3735 OS.SendMessage (handle, OS.LVM_SUBITEMHITTEST, 0, pinfo);
3736 if (pinfo.iItem < 0) pinfo.iItem = -1;
3737 pinfo.flags &= ~(OS.LVHT_ONITEMICON | OS.LVHT_ONITEMLABEL);
3741 if (pinfo.iSubItem != 0) pinfo.iItem = -1;
3747 * Force the table to have focus so that when the user
3748 * reselects the focus item, the LVIS_FOCUSED state bits
3749 * for the item will be set. If the user did not click on
3750 * an item, then set focus to the table so that it will
3751 * come to the front and take focus in the work around
3754 OS.SetFocus (handle);
3757 * Feature in Windows. When the user selects outside of
3758 * a table item, Windows deselects all the items, even
3759 * when the table is multi-select. While not strictly
3760 * wrong, this is unexpected. The fix is to detect the
3761 * case and avoid calling the window proc.
3763 if ((style & SWT.SINGLE) != 0 || hooks (SWT.MouseDown) || hooks (SWT.MouseUp)) {
3764 if (pinfo.iItem == -1) {
3765 if (!display.captureChanged && !isDisposed ()) {
3766 if (OS.GetCapture () != handle) OS.SetCapture (handle);
3768 return LRESULT.ZERO;
3773 * Feature in Windows. When a table item is reselected
3774 * in a single-select table, Windows does not issue a
3775 * WM_NOTIFY because the item state has not changed.
3776 * This is strictly correct but is inconsistent with the
3777 * list widget and other widgets in Windows. The fix is
3778 * to detect the case when an item is reselected and mark
3781 boolean forceSelect = false;
3782 int count = (int)OS.SendMessage (handle, OS.LVM_GETSELECTEDCOUNT, 0, 0);
3783 if (count == 1 && pinfo.iItem != -1) {
3784 LVITEM lvItem = new LVITEM ();
3785 lvItem.mask = OS.LVIF_STATE;
3786 lvItem.stateMask = OS.LVIS_SELECTED;
3787 lvItem.iItem = pinfo.iItem;
3788 OS.SendMessage (handle, OS.LVM_GETITEM, 0, lvItem);
3789 if ((lvItem.state & OS.LVIS_SELECTED) != 0) {
3794 /* Determine whether the user has selected an item based on SWT.MeasureItem */
3795 fullRowSelect = false;
3796 if (pinfo.iItem != -1) {
3797 if ((style & SWT.FULL_SELECTION) == 0) {
3798 if (hooks (SWT.MeasureItem)) {
3799 fullRowSelect = hitTestSelection (pinfo.iItem, pinfo.x, pinfo.y);
3800 if (fullRowSelect) {
3801 int flags = OS.LVHT_ONITEMICON | OS.LVHT_ONITEMLABEL;
3802 if ((pinfo.flags & flags) != 0) fullRowSelect = false;
3809 * Feature in Windows. Inside WM_LBUTTONDOWN and WM_RBUTTONDOWN,
3810 * the widget starts a modal loop to determine if the user wants
3811 * to begin a drag/drop operation or marque select. This modal
3812 * loop eats mouse events until a drag is detected. The fix is
3813 * to avoid this behavior by only running the drag and drop when
3814 * the event is hooked and the mouse is over an item.
3816 boolean dragDetect = (state & DRAG_DETECT) != 0 && hooks (SWT.DragDetect);
3818 int flags = OS.LVHT_ONITEMICON | OS.LVHT_ONITEMLABEL;
3819 dragDetect = pinfo.iItem == -1 || (pinfo.flags & flags) == 0;
3820 if (fullRowSelect) dragDetect = true;
3824 * Temporarily set LVS_EX_FULLROWSELECT to allow drag and drop
3825 * and the mouse to manipulate items based on the results of
3826 * the SWT.MeasureItem event.
3828 if (fullRowSelect) {
3829 OS.UpdateWindow (handle);
3830 OS.DefWindowProc (handle, OS.WM_SETREDRAW, 0, 0);
3831 OS.SendMessage (handle, OS.LVM_SETEXTENDEDLISTVIEWSTYLE, OS.LVS_EX_FULLROWSELECT, OS.LVS_EX_FULLROWSELECT);
3833 dragStarted = false;
3834 display.dragCancelled = false;
3835 if (!dragDetect) display.runDragDrop = false;
3836 long code = callWindowProc (handle, msg, wParam, lParam, forceSelect);
3837 if (!dragDetect) display.runDragDrop = true;
3838 if (fullRowSelect) {
3839 fullRowSelect = false;
3840 OS.DefWindowProc (handle, OS.WM_SETREDRAW, 1, 0);
3841 OS.SendMessage (handle, OS.LVM_SETEXTENDEDLISTVIEWSTYLE, OS.LVS_EX_FULLROWSELECT, 0);
3844 if (dragStarted || display.dragCancelled) {
3845 if (!display.captureChanged && !isDisposed ()) {
3846 if (OS.GetCapture () != handle) OS.SetCapture (handle);
3849 int flags = OS.LVHT_ONITEMLABEL | OS.LVHT_ONITEMICON;
3850 boolean fakeMouseUp = (pinfo.flags & flags) != 0;
3851 if (!fakeMouseUp && (style & SWT.MULTI) != 0) {
3852 fakeMouseUp = (pinfo.flags & OS.LVHT_ONITEMSTATEICON) == 0;
3855 sendMouseEvent (SWT.MouseUp, button, handle, msg, wParam, lParam);
3858 return new LRESULT (code);
3861 void sendPaintItemEvent (TableItem item, NMLVCUSTOMDRAW nmcd) {
3862 long hDC = nmcd.hdc;
3863 GCData data = new GCData ();
3864 data.device = display;
3865 data.font = item.getFont (nmcd.iSubItem);
3867 * Bug in Windows. For some reason, CDIS_SELECTED always set,
3868 * even for items that are not selected. The fix is to get
3869 * the selection state from the item.
3871 LVITEM lvItem = new LVITEM ();
3872 lvItem.mask = OS.LVIF_STATE;
3873 lvItem.stateMask = OS.LVIS_SELECTED;
3874 lvItem.iItem = (int)nmcd.dwItemSpec;
3875 long result = OS.SendMessage (handle, OS.LVM_GETITEM, 0, lvItem);
3876 boolean selected = result != 0 && (lvItem.state & OS.LVIS_SELECTED) != 0;
3877 boolean drawSelected = false, drawBackground = false, drawHot = false;
3878 if (nmcd.iSubItem == 0 || (style & SWT.FULL_SELECTION) != 0) {
3879 drawHot = hotIndex == nmcd.dwItemSpec;
3881 if (OS.IsWindowEnabled (handle)) {
3882 if (selected && (nmcd.iSubItem == 0 || (style & SWT.FULL_SELECTION) != 0)) {
3883 if (OS.GetFocus () == handle || display.getHighContrast ()) {
3884 drawSelected = true;
3885 if (selectionForeground != -1) {
3886 data.foreground = selectionForeground;
3888 data.foreground = OS.GetSysColor (OS.COLOR_HIGHLIGHTTEXT);
3890 data.background = OS.GetSysColor (OS.COLOR_HIGHLIGHT);
3892 drawSelected = (style & SWT.HIDE_SELECTION) == 0;
3893 data.foreground = OS.GetTextColor (hDC);
3894 data.background = OS.GetSysColor (OS.COLOR_3DFACE);
3896 if (explorerTheme && selectionForeground == -1) {
3897 int clrText = item.cellForeground != null ? item.cellForeground [nmcd.iSubItem] : -1;
3898 if (clrText == -1) clrText = item.foreground;
3899 data.foreground = clrText != -1 ? clrText : getForegroundPixel ();
3902 int clrText = item.cellForeground != null ? item.cellForeground [nmcd.iSubItem] : -1;
3903 if (clrText == -1) clrText = item.foreground;
3904 int clrTextBk = item.cellBackground != null ? item.cellBackground [nmcd.iSubItem] : -1;
3905 if (clrTextBk == -1) clrTextBk = item.background;
3906 drawBackground = clrTextBk != -1;
3908 * Bug in Windows. When LVM_SETTEXTBKCOLOR, LVM_SETBKCOLOR
3909 * or LVM_SETTEXTCOLOR is used to set the background color of
3910 * the the text or the control, the color is not set in the HDC
3911 * that is provided in Custom Draw. The fix is to explicitly
3914 if (clrText == -1 || clrTextBk == -1) {
3915 Control control = findBackgroundControl ();
3916 if (control == null) control = this;
3917 if (clrText == -1) clrText = control.getForegroundPixel ();
3918 if (clrTextBk == -1) clrTextBk = control.getBackgroundPixel ();
3920 data.foreground = clrText != -1 ? clrText : OS.GetTextColor (hDC);
3921 data.background = clrTextBk != -1 ? clrTextBk : OS.GetBkColor (hDC);
3924 data.foreground = OS.GetSysColor (OS.COLOR_GRAYTEXT);
3925 data.background = OS.GetSysColor (OS.COLOR_3DFACE);
3927 data.uiState = (int)OS.SendMessage (handle, OS.WM_QUERYUISTATE, 0, 0);
3928 int nSavedDC = OS.SaveDC (hDC);
3929 GC gc = GC.win32_new (hDC, data);
3930 RECT itemRect = item.getBounds ((int)nmcd.dwItemSpec, nmcd.iSubItem, true, true, false, false, hDC);
3931 Event event = new Event ();
3934 event.index = nmcd.iSubItem;
3935 event.detail |= SWT.FOREGROUND;
3936 // if ((nmcd.uItemState & OS.CDIS_FOCUS) != 0) {
3937 if (OS.SendMessage (handle, OS.LVM_GETNEXTITEM, -1, OS.LVNI_FOCUSED) == nmcd.dwItemSpec) {
3938 if (nmcd.iSubItem == 0 || (style & SWT.FULL_SELECTION) != 0) {
3939 if (handle == OS.GetFocus ()) {
3940 int uiState = (int)OS.SendMessage (handle, OS.WM_QUERYUISTATE, 0, 0);
3941 if ((uiState & OS.UISF_HIDEFOCUS) == 0) event.detail |= SWT.FOCUSED;
3945 if (drawHot) event.detail |= SWT.HOT;
3946 if (drawSelected) event.detail |= SWT.SELECTED;
3947 if (drawBackground) event.detail |= SWT.BACKGROUND;
3948 event.setBoundsInPixels(new Rectangle(itemRect.left, itemRect.top, itemRect.right - itemRect.left, itemRect.bottom - itemRect.top));
3949 RECT cellRect = item.getBounds ((int)nmcd.dwItemSpec, nmcd.iSubItem, true, true, true, true, hDC);
3950 int cellWidth = cellRect.right - cellRect.left;
3951 int cellHeight = cellRect.bottom - cellRect.top;
3952 gc.setClipping (DPIUtil.autoScaleDown(new Rectangle (cellRect.left, cellRect.top, cellWidth, cellHeight)));
3953 sendEvent (SWT.PaintItem, event);
3954 if (data.focusDrawn) focusRect = null;
3957 OS.RestoreDC (hDC, nSavedDC);
3960 Event sendPaintItemEvent (TableItem item, NMTTCUSTOMDRAW nmcd, int column, RECT itemRect) {
3961 int nSavedDC = OS.SaveDC (nmcd.hdc);
3962 RECT insetRect = toolTipInset (itemRect);
3963 OS.SetWindowOrgEx (nmcd.hdc, insetRect.left, insetRect.top, null);
3964 GCData data = new GCData ();
3965 data.device = display;
3966 data.font = item.getFont (column);
3967 data.foreground = OS.GetTextColor (nmcd.hdc);
3968 data.background = OS.GetBkColor (nmcd.hdc);
3969 data.uiState = (int)OS.SendMessage (handle, OS.WM_QUERYUISTATE, 0, 0);
3970 GC gc = GC.win32_new (nmcd.hdc, data);
3971 Event event = new Event ();
3973 event.index = column;
3975 event.detail |= SWT.FOREGROUND;
3976 event.setBoundsInPixels(new Rectangle(itemRect.left, itemRect.top, itemRect.right - itemRect.left, itemRect.bottom - itemRect.top));
3977 //gc.setClipping (cellRect.left, cellRect.top, cellWidth, cellHeight);
3978 sendEvent (SWT.PaintItem, event);
3981 OS.RestoreDC (nmcd.hdc, nSavedDC);
3986 void setBackgroundImage (long hBitmap) {
3987 super.setBackgroundImage (hBitmap);
3989 setBackgroundTransparent (true);
3991 if (!hooks (SWT.MeasureItem) && !hooks (SWT.EraseItem) && !hooks (SWT.PaintItem)) {
3992 setBackgroundTransparent (false);
3998 void setBackgroundPixel (int newPixel) {
3999 int oldPixel = (int)OS.SendMessage (handle, OS.LVM_GETBKCOLOR, 0, 0);
4000 if (oldPixel != OS.CLR_NONE) {
4001 if (findImageControl () != null) return;
4002 if (newPixel == -1) newPixel = defaultBackground ();
4003 if (oldPixel != newPixel) {
4004 OS.SendMessage (handle, OS.LVM_SETBKCOLOR, 0, newPixel);
4005 OS.SendMessage (handle, OS.LVM_SETTEXTBKCOLOR, 0, newPixel);
4006 if ((style & SWT.CHECK) != 0) fixCheckboxImageListColor (true);
4010 * Feature in Windows. When the background color is changed,
4011 * the table does not redraw until the next WM_PAINT. The fix
4012 * is to force a redraw.
4014 OS.InvalidateRect (handle, null, true);
4017 void setBackgroundTransparent (boolean transparent) {
4019 * Bug in Windows. When the table has the extended style
4020 * LVS_EX_FULLROWSELECT and LVM_SETBKCOLOR is used with
4021 * CLR_NONE to make the table transparent, Windows draws
4022 * a black rectangle around the first column. The fix is
4023 * clear LVS_EX_FULLROWSELECT.
4025 * Feature in Windows. When LVM_SETBKCOLOR is used with
4026 * CLR_NONE and LVM_SETSELECTEDCOLUMN is used to select
4027 * a column, Windows fills the column with the selection
4028 * color, drawing on top of the background image and any
4029 * other custom drawing. The fix is to clear the selected
4032 int oldPixel = (int)OS.SendMessage (handle, OS.LVM_GETBKCOLOR, 0, 0);
4034 if (oldPixel != OS.CLR_NONE) {
4036 * Bug in Windows. When the background color is changed,
4037 * the table does not redraw until the next WM_PAINT. The
4038 * fix is to force a redraw.
4040 OS.SendMessage (handle, OS.LVM_SETBKCOLOR, 0, OS.CLR_NONE);
4041 OS.SendMessage (handle, OS.LVM_SETTEXTBKCOLOR, 0, OS.CLR_NONE);
4042 OS.InvalidateRect (handle, null, true);
4044 /* Clear LVS_EX_FULLROWSELECT */
4045 if (!explorerTheme && (style & SWT.FULL_SELECTION) != 0) {
4046 int bits = OS.LVS_EX_FULLROWSELECT;
4047 OS.SendMessage (handle, OS.LVM_SETEXTENDEDLISTVIEWSTYLE, bits, 0);
4050 /* Clear LVM_SETSELECTEDCOLUMN */
4051 if ((sortDirection & (SWT.UP | SWT.DOWN)) != 0) {
4052 if (sortColumn != null && !sortColumn.isDisposed ()) {
4053 OS.SendMessage (handle, OS.LVM_SETSELECTEDCOLUMN, -1, 0);
4055 * Bug in Windows. When LVM_SETSELECTEDCOLUMN is set, Windows
4056 * does not redraw either the new or the previous selected column.
4057 * The fix is to force a redraw.
4059 OS.InvalidateRect (handle, null, true);
4064 if (oldPixel == OS.CLR_NONE) {
4065 Control control = findBackgroundControl ();
4066 if (control == null) control = this;
4067 if (control.backgroundImage == null) {
4068 int newPixel = control.getBackgroundPixel ();
4069 OS.SendMessage (handle, OS.LVM_SETBKCOLOR, 0, newPixel);
4070 OS.SendMessage (handle, OS.LVM_SETTEXTBKCOLOR, 0, newPixel);
4071 if ((style & SWT.CHECK) != 0) fixCheckboxImageListColor (true);
4072 OS.InvalidateRect (handle, null, true);
4075 /* Set LVS_EX_FULLROWSELECT */
4076 if (!explorerTheme && (style & SWT.FULL_SELECTION) != 0) {
4077 if (!hooks (SWT.EraseItem) && !hooks (SWT.PaintItem)) {
4078 int bits = OS.LVS_EX_FULLROWSELECT;
4079 OS.SendMessage (handle, OS.LVM_SETEXTENDEDLISTVIEWSTYLE, bits, bits);
4083 /* Set LVM_SETSELECTEDCOLUMN */
4084 if ((sortDirection & (SWT.UP | SWT.DOWN)) != 0) {
4085 if (sortColumn != null && !sortColumn.isDisposed ()) {
4086 int column = indexOf (sortColumn);
4088 OS.SendMessage (handle, OS.LVM_SETSELECTEDCOLUMN, column, 0);
4090 * Bug in Windows. When LVM_SETSELECTEDCOLUMN is set, Windows
4091 * does not redraw either the new or the previous selected column.
4092 * The fix is to force a redraw.
4094 OS.InvalidateRect (handle, null, true);
4103 void setBoundsInPixels (int x, int y, int width, int height, int flags, boolean defer) {
4105 * Bug in Windows. If the table column widths are adjusted
4106 * in WM_SIZE or WM_POSITIONCHANGED using LVM_SETCOLUMNWIDTH
4107 * blank lines may be inserted at the top of the table. A
4108 * call to LVM_GETTOPINDEX will return a negative number (this
4109 * is an impossible result). Once the blank lines appear,
4110 * there seems to be no way to get rid of them, other than
4111 * destroying and recreating the table. The fix is to send
4112 * the resize notification after the size has been changed in
4113 * the operating system.
4115 * NOTE: This does not fix the case when the user is resizing
4116 * columns dynamically. There is no fix for this case at this
4119 setDeferResize (true);
4120 super.setBoundsInPixels (x, y, width, height, flags, false);
4121 setDeferResize (false);
4125 * Sets the order that the items in the receiver should
4126 * be displayed in to the given argument which is described
4127 * in terms of the zero-relative ordering of when the items
4130 * @param order the new order to display the items
4132 * @exception SWTException <ul>
4133 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
4134 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
4136 * @exception IllegalArgumentException <ul>
4137 * <li>ERROR_NULL_ARGUMENT - if the item order is null</li>
4138 * <li>ERROR_INVALID_ARGUMENT - if the item order is not the same length as the number of items</li>
4141 * @see Table#getColumnOrder()
4142 * @see TableColumn#getMoveable()
4143 * @see TableColumn#setMoveable(boolean)
4148 public void setColumnOrder (int [] order) {
4150 if (order == null) error (SWT.ERROR_NULL_ARGUMENT);
4151 if (columnCount == 0) {
4152 if (order.length != 0) error (SWT.ERROR_INVALID_ARGUMENT);
4155 if (order.length != columnCount) error (SWT.ERROR_INVALID_ARGUMENT);
4156 int [] oldOrder = new int [columnCount];
4157 OS.SendMessage (handle, OS.LVM_GETCOLUMNORDERARRAY, columnCount, oldOrder);
4158 boolean reorder = false;
4159 boolean [] seen = new boolean [columnCount];
4160 for (int i=0; i<order.length; i++) {
4161 int index = order [i];
4162 if (index < 0 || index >= columnCount) error (SWT.ERROR_INVALID_RANGE);
4163 if (seen [index]) error (SWT.ERROR_INVALID_ARGUMENT);
4164 seen [index] = true;
4165 if (index != oldOrder [i]) reorder = true;
4168 RECT [] oldRects = new RECT [columnCount];
4169 for (int i=0; i<columnCount; i++) {
4170 oldRects [i] = new RECT ();
4171 OS.SendMessage (hwndHeader, OS.HDM_GETITEMRECT, i, oldRects [i]);
4173 OS.SendMessage (handle, OS.LVM_SETCOLUMNORDERARRAY, order.length, order);
4175 * Bug in Windows. When LVM_SETCOLUMNORDERARRAY is used to change
4176 * the column order, the header redraws correctly but the table does
4177 * not. The fix is to force a redraw.
4179 OS.InvalidateRect (handle, null, true);
4180 TableColumn[] newColumns = new TableColumn [columnCount];
4181 System.arraycopy (columns, 0, newColumns, 0, columnCount);
4182 RECT newRect = new RECT ();
4183 for (int i=0; i<columnCount; i++) {
4184 TableColumn column = newColumns [i];
4185 if (!column.isDisposed ()) {
4186 OS.SendMessage (hwndHeader, OS.HDM_GETITEMRECT, i, newRect);
4187 if (newRect.left != oldRects [i].left) {
4188 column.updateToolTip (i);
4189 column.sendEvent (SWT.Move);
4196 void setCustomDraw (boolean customDraw) {
4197 if (this.customDraw == customDraw) return;
4198 if (!this.customDraw && customDraw && currentItem != null) {
4199 OS.InvalidateRect (handle, null, true);
4201 this.customDraw = customDraw;
4204 void setDeferResize (boolean defer) {
4206 if (resizeCount++ == 0) {
4209 * Feature in Windows. When LVM_SETBKCOLOR is used with CLR_NONE
4210 * to make the background of the table transparent, drawing becomes
4211 * slow. The fix is to temporarily clear CLR_NONE when redraw is
4214 if (hooks (SWT.MeasureItem) || hooks (SWT.EraseItem) || hooks (SWT.PaintItem)) {
4215 if (drawCount++ == 0 && OS.IsWindowVisible (handle)) {
4216 OS.DefWindowProc (handle, OS.WM_SETREDRAW, 0, 0);
4217 OS.SendMessage (handle, OS.LVM_SETBKCOLOR, 0, 0xFFFFFF);
4222 if (--resizeCount == 0) {
4223 if (hooks (SWT.MeasureItem) || hooks (SWT.EraseItem) || hooks (SWT.PaintItem)) {
4224 if (--drawCount == 0 /*&& OS.IsWindowVisible (handle)*/) {
4225 OS.SendMessage (handle, OS.LVM_SETBKCOLOR, 0, OS.CLR_NONE);
4226 OS.DefWindowProc (handle, OS.WM_SETREDRAW, 1, 0);
4227 int flags = OS.RDW_ERASE | OS.RDW_FRAME | OS.RDW_INVALIDATE | OS.RDW_ALLCHILDREN;
4228 OS.RedrawWindow (handle, null, 0, flags);
4233 setResizeChildren (false);
4234 sendEvent (SWT.Resize);
4235 if (isDisposed ()) return;
4236 if (layout != null) {
4237 markLayout (false, false);
4238 updateLayout (false, false);
4240 setResizeChildren (true);
4246 void setCheckboxImageList (int width, int height, boolean fixScroll) {
4247 if ((style & SWT.CHECK) == 0) return;
4248 int count = 8, flags = OS.ILC_COLOR32;
4249 if ((style & SWT.RIGHT_TO_LEFT) != 0) flags |= OS.ILC_MIRROR;
4250 if (!OS.IsAppThemed ()) flags |= OS.ILC_MASK;
4251 long hStateList = OS.ImageList_Create (width, height, flags, count, count);
4252 long hDC = OS.GetDC (handle);
4253 long memDC = OS.CreateCompatibleDC (hDC);
4254 long hBitmap = OS.CreateCompatibleBitmap (hDC, width * count, height);
4255 long hOldBitmap = OS.SelectObject (memDC, hBitmap);
4256 RECT rect = new RECT ();
4257 OS.SetRect (rect, 0, 0, width * count, height);
4259 if (OS.IsAppThemed ()) {
4260 Control control = findBackgroundControl ();
4261 if (control == null) control = this;
4262 clrBackground = control.getBackgroundPixel ();
4264 clrBackground = 0x020000FF;
4265 if ((clrBackground & 0xFFFFFF) == OS.GetSysColor (OS.COLOR_WINDOW)) {
4266 clrBackground = 0x0200FF00;
4269 long hBrush = OS.CreateSolidBrush (clrBackground);
4270 OS.FillRect (memDC, rect, hBrush);
4271 OS.DeleteObject (hBrush);
4272 long oldFont = OS.SelectObject (hDC, defaultFont ());
4273 TEXTMETRIC tm = new TEXTMETRIC ();
4274 OS.GetTextMetrics (hDC, tm);
4275 OS.SelectObject (hDC, oldFont);
4276 int itemWidth = Math.min (tm.tmHeight, width);
4277 int itemHeight = Math.min (tm.tmHeight, height);
4278 if (OS.IsAppThemed()) {
4280 * Feature in Windows. DrawThemeBackground stretches the checkbox
4281 * bitmap to fill the provided rectangle. To avoid stretching
4282 * artifacts, limit the rectangle to actual checkbox bitmap size.
4284 SIZE size = new SIZE();
4285 OS.GetThemePartSize(display.hButtonTheme(), memDC, OS.BP_CHECKBOX, 0, null, OS.TS_TRUE, size);
4286 itemWidth = Math.min (size.cx, itemWidth);
4287 itemHeight = Math.min (size.cy, itemHeight);
4289 int left = (width - itemWidth) / 2, top = (height - itemHeight) / 2;
4290 OS.SetRect (rect, left, top, left + itemWidth, top + itemHeight);
4291 if (OS.IsAppThemed ()) {
4292 long hTheme = display.hButtonTheme ();
4293 OS.DrawThemeBackground (hTheme, memDC, OS.BP_CHECKBOX, OS.CBS_UNCHECKEDNORMAL, rect, null);
4294 rect.left += width; rect.right += width;
4295 OS.DrawThemeBackground (hTheme, memDC, OS.BP_CHECKBOX, OS.CBS_CHECKEDNORMAL, rect, null);
4296 rect.left += width; rect.right += width;
4297 OS.DrawThemeBackground (hTheme, memDC, OS.BP_CHECKBOX, OS.CBS_UNCHECKEDNORMAL, rect, null);
4298 rect.left += width; rect.right += width;
4299 OS.DrawThemeBackground (hTheme, memDC, OS.BP_CHECKBOX, OS.CBS_MIXEDNORMAL, rect, null);
4300 rect.left += width; rect.right += width;
4301 OS.DrawThemeBackground (hTheme, memDC, OS.BP_CHECKBOX, OS.CBS_UNCHECKEDDISABLED, rect, null);
4302 rect.left += width; rect.right += width;
4303 OS.DrawThemeBackground (hTheme, memDC, OS.BP_CHECKBOX, OS.CBS_CHECKEDDISABLED, rect, null);
4304 rect.left += width; rect.right += width;
4305 OS.DrawThemeBackground (hTheme, memDC, OS.BP_CHECKBOX, OS.CBS_UNCHECKEDDISABLED, rect, null);
4306 rect.left += width; rect.right += width;
4307 OS.DrawThemeBackground (hTheme, memDC, OS.BP_CHECKBOX, OS.CBS_MIXEDDISABLED, rect, null);
4309 OS.DrawFrameControl (memDC, rect, OS.DFC_BUTTON, OS.DFCS_BUTTONCHECK | OS.DFCS_FLAT);
4310 rect.left += width; rect.right += width;
4311 OS.DrawFrameControl (memDC, rect, OS.DFC_BUTTON, OS.DFCS_BUTTONCHECK | OS.DFCS_CHECKED | OS.DFCS_FLAT);
4312 rect.left += width; rect.right += width;
4313 OS.DrawFrameControl (memDC, rect, OS.DFC_BUTTON, OS.DFCS_BUTTONCHECK | OS.DFCS_INACTIVE | OS.DFCS_FLAT);
4314 rect.left += width; rect.right += width;
4315 OS.DrawFrameControl (memDC, rect, OS.DFC_BUTTON, OS.DFCS_BUTTONCHECK | OS.DFCS_CHECKED | OS.DFCS_INACTIVE | OS.DFCS_FLAT);
4316 rect.left += width; rect.right += width;
4317 OS.DrawFrameControl (memDC, rect, OS.DFC_BUTTON, OS.DFCS_BUTTONCHECK | OS.DFCS_FLAT);
4318 rect.left += width; rect.right += width;
4319 OS.DrawFrameControl (memDC, rect, OS.DFC_BUTTON, OS.DFCS_BUTTONCHECK | OS.DFCS_CHECKED | OS.DFCS_FLAT);
4320 rect.left += width; rect.right += width;
4321 OS.DrawFrameControl (memDC, rect, OS.DFC_BUTTON, OS.DFCS_BUTTONCHECK | OS.DFCS_INACTIVE | OS.DFCS_FLAT);
4322 rect.left += width; rect.right += width;
4323 OS.DrawFrameControl (memDC, rect, OS.DFC_BUTTON, OS.DFCS_BUTTONCHECK | OS.DFCS_CHECKED | OS.DFCS_INACTIVE | OS.DFCS_FLAT);
4325 OS.SelectObject (memDC, hOldBitmap);
4326 OS.DeleteDC (memDC);
4327 OS.ReleaseDC (handle, hDC);
4328 if (OS.IsAppThemed ()) {
4329 OS.ImageList_Add (hStateList, hBitmap, 0);
4331 OS.ImageList_AddMasked (hStateList, hBitmap, clrBackground);
4333 OS.DeleteObject (hBitmap);
4335 * Bug in Windows. Making any change to an item that
4336 * changes the item height of a table while the table
4337 * is scrolled can cause the lines to draw incorrectly.
4338 * This happens even when the lines are not currently
4339 * visible and are shown afterwards. The fix is to
4340 * save the top index, scroll to the top of the table
4341 * and then restore the original top index.
4343 int topIndex = getTopIndex ();
4344 if (fixScroll && topIndex != 0) {
4348 long hOldStateList = OS.SendMessage (handle, OS.LVM_GETIMAGELIST, OS.LVSIL_STATE, 0);
4349 OS.SendMessage (handle, OS.LVM_SETIMAGELIST, OS.LVSIL_STATE, hStateList);
4350 if (hOldStateList != 0) OS.ImageList_Destroy (hOldStateList);
4352 * Bug in Windows. Setting the LVSIL_STATE state image list
4353 * when the table already has a LVSIL_SMALL image list causes
4354 * pixel corruption of the images. The fix is to reset the
4355 * LVSIL_SMALL image list.
4357 long hImageList = OS.SendMessage (handle, OS.LVM_GETIMAGELIST, OS.LVSIL_SMALL, 0);
4358 OS.SendMessage (handle, OS.LVM_SETIMAGELIST, OS.LVSIL_SMALL, hImageList);
4359 if (fixScroll && topIndex != 0) {
4360 setTopIndex (topIndex);
4365 void setFocusIndex (int index) {
4368 * An index of -1 will apply the change to all
4369 * items. Ensure that index is greater than -1.
4371 if (index < 0) return;
4372 LVITEM lvItem = new LVITEM ();
4373 lvItem.state = OS.LVIS_FOCUSED;
4374 lvItem.stateMask = OS.LVIS_FOCUSED;
4375 ignoreSelect = true;
4376 OS.SendMessage (handle, OS.LVM_SETITEMSTATE, index, lvItem);
4377 ignoreSelect = false;
4378 OS.SendMessage (handle, OS.LVM_SETSELECTIONMARK, 0, index);
4382 public void setFont (Font font) {
4385 * Bug in Windows. Making any change to an item that
4386 * changes the item height of a table while the table
4387 * is scrolled can cause the lines to draw incorrectly.
4388 * This happens even when the lines are not currently
4389 * visible and are shown afterwards. The fix is to
4390 * save the top index, scroll to the top of the table
4391 * and then restore the original top index.
4393 int topIndex = getTopIndex ();
4394 if (topIndex != 0) {
4398 if (itemHeight != -1) {
4399 int bits = OS.GetWindowLong (handle, OS.GWL_STYLE);
4400 OS.SetWindowLong (handle, OS.GWL_STYLE, bits | OS.LVS_OWNERDRAWFIXED);
4402 super.setFont (font);
4403 if (itemHeight != -1) {
4404 int bits = OS.GetWindowLong (handle, OS.GWL_STYLE);
4405 OS.SetWindowLong (handle, OS.GWL_STYLE, bits & ~OS.LVS_OWNERDRAWFIXED);
4407 setScrollWidth (null, true);
4408 if (topIndex != 0) {
4409 setTopIndex (topIndex);
4414 * Bug in Windows. Setting the font will cause the table
4415 * to be redrawn but not the column headers. The fix is
4416 * to force a redraw of the column headers.
4418 OS.InvalidateRect (hwndHeader, null, true);
4422 void setForegroundPixel (int pixel) {
4424 * The Windows table control uses CLR_DEFAULT to indicate
4425 * that it is using the default foreground color. This
4428 if (pixel == -1) pixel = OS.CLR_DEFAULT;
4429 OS.SendMessage (handle, OS.LVM_SETTEXTCOLOR, 0, pixel);
4432 * Feature in Windows. When the foreground color is
4433 * changed, the table does not redraw until the next
4434 * WM_PAINT. The fix is to force a redraw.
4436 OS.InvalidateRect (handle, null, true);
4437 OS.InvalidateRect (hwndHeader, null, true);
4441 * Sets the header background color to the color specified
4442 * by the argument, or to the default system color if the argument is null.
4444 * Note: This operation is a <em>HINT</em> and is not supported on all platforms. If
4445 * the native header has a 3D look and feel (e.g. Windows 7), this method
4446 * will cause the header to look FLAT irrespective of the state of the table style.
4448 * @param color the new color (or null)
4450 * @exception IllegalArgumentException <ul>
4451 * <li>ERROR_INVALID_ARGUMENT - if the argument has been disposed</li>
4453 * @exception SWTException <ul>
4454 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
4455 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
4459 public void setHeaderBackground (Color color) {
4462 if (color != null) {
4463 if (color.isDisposed()) error(SWT.ERROR_INVALID_ARGUMENT);
4464 pixel = color.handle;
4466 if (pixel == headerBackground) return;
4467 headerBackground = pixel;
4468 if (getHeaderVisible()) {
4469 OS.InvalidateRect (hwndHeader, null, true);
4474 * Sets the header foreground color to the color specified
4475 * by the argument, or to the default system color if the argument is null.
4477 * Note: This operation is a <em>HINT</em> and is not supported on all platforms. If
4478 * the native header has a 3D look and feel (e.g. Windows 7), this method
4479 * will cause the header to look FLAT irrespective of the state of the table style.
4481 * @param color the new color (or null)
4483 * @exception IllegalArgumentException <ul>
4484 * <li>ERROR_INVALID_ARGUMENT - if the argument has been disposed</li>
4486 * @exception SWTException <ul>
4487 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
4488 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
4492 public void setHeaderForeground (Color color) {
4495 if (color != null) {
4496 if (color.isDisposed()) error(SWT.ERROR_INVALID_ARGUMENT);
4497 pixel = color.handle;
4499 if (pixel == headerForeground) return;
4500 headerForeground = pixel;
4501 if (getHeaderVisible()) {
4502 OS.InvalidateRect (hwndHeader, null, true);
4507 * Marks the receiver's header as visible if the argument is <code>true</code>,
4508 * and marks it invisible otherwise.
4510 * If one of the receiver's ancestors is not visible or some
4511 * other condition makes the receiver not visible, marking
4512 * it visible may not actually cause it to be displayed.
4515 * @param show the new visibility state
4517 * @exception SWTException <ul>
4518 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
4519 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
4522 public void setHeaderVisible (boolean show) {
4524 int newBits = OS.GetWindowLong (handle, OS.GWL_STYLE);
4525 newBits &= ~OS.LVS_NOCOLUMNHEADER;
4526 if (!show) newBits |= OS.LVS_NOCOLUMNHEADER;
4528 * Feature in Windows. Setting or clearing LVS_NOCOLUMNHEADER
4529 * causes the table to scroll to the beginning. The fix is to
4530 * save and restore the top index causing the table to scroll
4531 * to the new location.
4533 int oldIndex = getTopIndex ();
4534 OS.SetWindowLong (handle, OS.GWL_STYLE, newBits);
4537 * Bug in Windows. Making any change to an item that
4538 * changes the item height of a table while the table
4539 * is scrolled can cause the lines to draw incorrectly.
4540 * This happens even when the lines are not currently
4541 * visible and are shown afterwards. The fix is to
4542 * save the top index, scroll to the top of the table
4543 * and then restore the original top index.
4545 int newIndex = getTopIndex ();
4546 if (newIndex != 0) {
4550 setTopIndex (oldIndex);
4551 if (newIndex != 0) {
4554 updateHeaderToolTips ();
4558 * Sets the number of items contained in the receiver.
4560 * @param count the number of items
4562 * @exception SWTException <ul>
4563 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
4564 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
4569 public void setItemCount (int count) {
4571 count = Math.max (0, count);
4572 int itemCount = (int)OS.SendMessage (handle, OS.LVM_GETITEMCOUNT, 0, 0);
4573 if (count == itemCount) return;
4574 setDeferResize (true);
4575 boolean isVirtual = (style & SWT.VIRTUAL) != 0;
4576 if (!isVirtual) setRedraw (false);
4578 while (index < itemCount) {
4579 TableItem item = _getItem (index, false);
4580 if (item != null && !item.isDisposed ()) item.release (false);
4582 ignoreSelect = ignoreShrink = true;
4583 long code = OS.SendMessage (handle, OS.LVM_DELETEITEM, count, 0);
4584 ignoreSelect = ignoreShrink = false;
4585 if (code == 0) break;
4589 if (index < itemCount) error (SWT.ERROR_ITEM_NOT_REMOVED);
4590 _setItemCount (count, itemCount);
4592 int flags = OS.LVSICF_NOINVALIDATEALL | OS.LVSICF_NOSCROLL;
4593 OS.SendMessage (handle, OS.LVM_SETITEMCOUNT, count, flags);
4595 * Bug in Windows. When a virtual table contains items and
4596 * LVM_SETITEMCOUNT is used to set the new item count to zero,
4597 * Windows does not redraw the table. Note that simply not
4598 * specifying LVSICF_NOINVALIDATEALL or LVSICF_NOSCROLL does
4599 * correct the problem. The fix is to force a redraw.
4601 if (count == 0 && itemCount != 0) {
4602 OS.InvalidateRect (handle, null, true);
4605 for (int i=itemCount; i<count; i++) {
4606 new TableItem (this, SWT.NONE, i, true);
4609 if (!isVirtual) setRedraw (true);
4610 if (itemCount == 0) setScrollWidth (null, false);
4611 setDeferResize (false);
4614 void setItemHeight (boolean fixScroll) {
4616 * Bug in Windows. Making any change to an item that
4617 * changes the item height of a table while the table
4618 * is scrolled can cause the lines to draw incorrectly.
4619 * This happens even when the lines are not currently
4620 * visible and are shown afterwards. The fix is to
4621 * save the top index, scroll to the top of the table
4622 * and then restore the original top index.
4625 int topIndex = getTopIndex ();
4626 if (fixScroll && topIndex != 0) {
4630 if (itemHeight == -1) {
4632 * Feature in Windows. Windows has no API to restore the
4633 * defualt item height for a table. The fix is to use
4634 * WM_SETFONT which recomputes and assigns the default item
4637 long hFont = OS.SendMessage (handle, OS.WM_GETFONT, 0, 0);
4638 OS.SendMessage (handle, OS.WM_SETFONT, hFont, 0);
4641 * Feature in Windows. Window has no API to set the item
4642 * height for a table. The fix is to set temporarily set
4643 * LVS_OWNERDRAWFIXED then resize the table, causing a
4644 * WM_MEASUREITEM to be sent, then clear LVS_OWNERDRAWFIXED.
4647 RECT rect = new RECT ();
4648 OS.GetWindowRect (handle, rect);
4649 int width = rect.right - rect.left, height = rect.bottom - rect.top;
4650 int bits = OS.GetWindowLong (handle, OS.GWL_STYLE);
4651 OS.SetWindowLong (handle, OS.GWL_STYLE, bits | OS.LVS_OWNERDRAWFIXED);
4652 int flags = OS.SWP_NOACTIVATE | OS.SWP_NOMOVE | OS.SWP_NOREDRAW | OS.SWP_NOZORDER;
4653 ignoreResize = true;
4654 OS.SetWindowPos (handle, 0 , 0, 0, width, height + 1, flags);
4655 OS.SetWindowPos (handle, 0 , 0, 0, width, height, flags);
4656 ignoreResize = false;
4657 OS.SetWindowLong (handle, OS.GWL_STYLE, bits);
4659 if (fixScroll && topIndex != 0) {
4660 setTopIndex (topIndex);
4666 * Sets the height of the area which would be used to
4667 * display <em>one</em> of the items in the table.
4669 * @param itemHeight the height of one item
4671 * @exception SWTException <ul>
4672 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
4673 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
4678 /*public*/ void setItemHeight (int itemHeight) {
4680 if (itemHeight < -1) error (SWT.ERROR_INVALID_ARGUMENT);
4681 this.itemHeight = itemHeight;
4682 setItemHeight (true);
4683 setScrollWidth (null, true);
4687 * Marks the receiver's lines as visible if the argument is <code>true</code>,
4688 * and marks it invisible otherwise. Note that some platforms draw grid lines
4689 * while others may draw alternating row colors.
4691 * If one of the receiver's ancestors is not visible or some
4692 * other condition makes the receiver not visible, marking
4693 * it visible may not actually cause it to be displayed.
4696 * @param show the new visibility state
4698 * @exception SWTException <ul>
4699 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
4700 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
4703 public void setLinesVisible (boolean show) {
4705 int newBits = show ? OS.LVS_EX_GRIDLINES : 0;
4706 OS.SendMessage (handle, OS.LVM_SETEXTENDEDLISTVIEWSTYLE, OS.LVS_EX_GRIDLINES, newBits);
4707 OS.InvalidateRect (hwndHeader, null, true);
4711 public void setRedraw (boolean redraw) {
4714 * Feature in Windows. When WM_SETREDRAW is used to turn
4715 * off drawing in a widget, it clears the WS_VISIBLE bits
4716 * and then sets them when redraw is turned back on. This
4717 * means that WM_SETREDRAW will make a widget unexpectedly
4718 * visible. The fix is to track the visibility state while
4719 * drawing is turned off and restore it when drawing is turned
4722 if (drawCount == 0) {
4723 int bits = OS.GetWindowLong (handle, OS.GWL_STYLE);
4724 if ((bits & OS.WS_VISIBLE) == 0) state |= HIDDEN;
4727 if (--drawCount == 0) {
4729 * When many items are added to a table, it is faster to
4730 * temporarily unsubclass the window proc so that messages
4731 * are dispatched directly to the table.
4733 * NOTE: This is optimization somewhat dangerous because any
4734 * operation can occur when redraw is turned off, even operations
4735 * where the table must be subclassed in order to have the correct
4736 * behavior or work around a Windows bug.
4738 * This code is intentionally commented.
4742 /* Set the width of the horizontal scroll bar */
4743 setScrollWidth (null, true);
4746 * Bug in Windows. For some reason, when WM_SETREDRAW is used
4747 * to turn redraw back on this may result in a WM_SIZE. If the
4748 * table column widths are adjusted in WM_SIZE, blank lines may
4749 * be inserted at the top of the widget. A call to LVM_GETTOPINDEX
4750 * will return a negative number (this is an impossible result).
4751 * The fix is to send the resize notification after the size has
4752 * been changed in the operating system.
4754 setDeferResize (true);
4755 OS.SendMessage (handle, OS.WM_SETREDRAW, 1, 0);
4756 if (hwndHeader != 0) OS.SendMessage (hwndHeader, OS.WM_SETREDRAW, 1, 0);
4757 if ((state & HIDDEN) != 0) {
4759 OS.ShowWindow (handle, OS.SW_HIDE);
4761 int flags = OS.RDW_ERASE | OS.RDW_FRAME | OS.RDW_INVALIDATE | OS.RDW_ALLCHILDREN;
4762 OS.RedrawWindow (handle, null, 0, flags);
4764 setDeferResize (false);
4767 if (drawCount++ == 0) {
4768 OS.SendMessage (handle, OS.WM_SETREDRAW, 0, 0);
4769 if (hwndHeader != 0) OS.SendMessage (hwndHeader, OS.WM_SETREDRAW, 0, 0);
4772 * When many items are added to a table, it is faster to
4773 * temporarily unsubclass the window proc so that messages
4774 * are dispatched directly to the table.
4776 * NOTE: This is optimization somewhat dangerous because any
4777 * operation can occur when redraw is turned off, even operations
4778 * where the table must be subclassed in order to have the correct
4779 * behavior or work around a Windows bug.
4781 * This code is intentionally commented.
4788 void setScrollWidth (int width) {
4789 if (width != (int)OS.SendMessage (handle, OS.LVM_GETCOLUMNWIDTH, 0, 0)) {
4791 * Feature in Windows. When LVM_SETCOLUMNWIDTH is sent,
4792 * Windows draws right away instead of queuing a WM_PAINT.
4793 * This can cause recursive calls when called from paint
4794 * or from messages that are retrieving the item data,
4795 * such as WM_NOTIFY, causing a stack overflow. The fix
4796 * is to turn off redraw and queue a repaint, collapsing
4797 * the recursive calls.
4799 boolean redraw = false;
4800 if (hooks (SWT.MeasureItem)) {
4801 redraw = getDrawing () && OS.IsWindowVisible (handle);
4803 if (redraw) OS.DefWindowProc (handle, OS.WM_SETREDRAW, 0, 0);
4804 OS.SendMessage (handle, OS.LVM_SETCOLUMNWIDTH, 0, width);
4806 OS.DefWindowProc (handle, OS.WM_SETREDRAW, 1, 0);
4807 int flags = OS.RDW_ERASE | OS.RDW_FRAME | OS.RDW_INVALIDATE | OS.RDW_ALLCHILDREN;
4808 OS.RedrawWindow (handle, null, 0, flags);
4813 boolean setScrollWidth (TableItem item, boolean force) {
4814 if (currentItem != null) {
4815 if (currentItem != item) fixScrollWidth = true;
4818 if (!force && (!getDrawing () || !OS.IsWindowVisible (handle))) {
4819 fixScrollWidth = true;
4822 fixScrollWidth = false;
4824 * NOTE: It is much faster to measure the strings and compute the
4825 * width of the scroll bar in non-virtual table rather than using
4826 * LVM_SETCOLUMNWIDTH with LVSCW_AUTOSIZE.
4828 if (columnCount == 0) {
4829 int newWidth = 0, imageIndent = 0, index = 0;
4830 int itemCount = (int)OS.SendMessage (handle, OS.LVM_GETITEMCOUNT, 0, 0);
4831 while (index < itemCount) {
4832 String string = null;
4836 imageIndent = Math.max (imageIndent, item.imageIndent);
4837 hFont = item.fontHandle (0);
4839 TableItem tableItem = _getItem (index, false);
4840 if (tableItem != null) {
4841 string = tableItem.text;
4842 imageIndent = Math.max (imageIndent, tableItem.imageIndent);
4843 hFont = tableItem.fontHandle (0);
4846 if (string != null && string.length () != 0) {
4848 long hDC = OS.GetDC (handle);
4849 long oldFont = OS.SelectObject (hDC, hFont);
4850 int flags = OS.DT_CALCRECT | OS.DT_SINGLELINE | OS.DT_NOPREFIX;
4851 char [] buffer = string.toCharArray ();
4852 RECT rect = new RECT ();
4853 OS.DrawText (hDC, buffer, buffer.length, rect, flags);
4854 OS.SelectObject (hDC, oldFont);
4855 OS.ReleaseDC (handle, hDC);
4856 newWidth = Math.max (newWidth, rect.right - rect.left);
4858 TCHAR buffer = new TCHAR (getCodePage (), string, true);
4859 newWidth = Math.max (newWidth, (int)OS.SendMessage (handle, OS.LVM_GETSTRINGWIDTH, 0, buffer));
4862 if (item != null) break;
4866 * Bug in Windows. When the width of the first column is
4867 * small but not zero, Windows draws '...' outside of the
4868 * bounds of the text. This is strange, but only causes
4869 * problems when the item is selected. In this case, Windows
4870 * clears the '...' but doesn't redraw it when the item is
4871 * deselected, causing pixel corruption. The fix is to ensure
4872 * that the column is at least wide enough to draw a single
4875 if (newWidth == 0) {
4876 char [] buffer = {' ', '\0'};
4877 newWidth = Math.max (newWidth, (int)OS.SendMessage (handle, OS.LVM_GETSTRINGWIDTH, 0, buffer));
4879 long hStateList = OS.SendMessage (handle, OS.LVM_GETIMAGELIST, OS.LVSIL_STATE, 0);
4880 if (hStateList != 0) {
4881 int [] cx = new int [1], cy = new int [1];
4882 OS.ImageList_GetIconSize (hStateList, cx, cy);
4883 newWidth += cx [0] + INSET;
4885 long hImageList = OS.SendMessage (handle, OS.LVM_GETIMAGELIST, OS.LVSIL_SMALL, 0);
4886 if (hImageList != 0) {
4887 int [] cx = new int [1], cy = new int [1];
4888 OS.ImageList_GetIconSize (hImageList, cx, cy);
4889 newWidth += (imageIndent + 1) * cx [0];
4892 * Bug in Windows. When LVM_SETIMAGELIST is used to remove the
4893 * image list by setting it to NULL, the item width and height
4894 * is not changed and space is reserved for icons despite the
4895 * fact that there are none. The fix is to set the image list
4896 * to be very small before setting it to NULL. This causes
4897 * Windows to reserve the smallest possible space when an image
4898 * list is removed. In this case, the scroll width must be one
4903 newWidth += INSET * 2 + VISTA_EXTRA;
4904 int oldWidth = (int)OS.SendMessage (handle, OS.LVM_GETCOLUMNWIDTH, 0, 0);
4905 if (newWidth > oldWidth) {
4906 setScrollWidth (newWidth);
4914 * Selects the items at the given zero-relative indices in the receiver.
4915 * The current selection is cleared before the new items are selected,
4916 * and if necessary the receiver is scrolled to make the new selection visible.
4918 * Indices that are out of range and duplicate indices are ignored.
4919 * If the receiver is single-select and multiple indices are specified,
4920 * then all indices are ignored.
4923 * @param indices the indices of the items to select
4925 * @exception IllegalArgumentException <ul>
4926 * <li>ERROR_NULL_ARGUMENT - if the array of indices is null</li>
4928 * @exception SWTException <ul>
4929 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
4930 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
4933 * @see Table#deselectAll()
4934 * @see Table#select(int[])
4936 public void setSelection (int [] indices) {
4938 if (indices == null) error (SWT.ERROR_NULL_ARGUMENT);
4940 int length = indices.length;
4941 if (length == 0 || ((style & SWT.SINGLE) != 0 && length > 1)) return;
4943 int focusIndex = indices [0];
4944 if (focusIndex != -1) setFocusIndex (focusIndex);
4949 * Sets the receiver's selection to the given item.
4950 * The current selection is cleared before the new item is selected,
4951 * and if necessary the receiver is scrolled to make the new selection visible.
4953 * If the item is not in the receiver, then it is ignored.
4956 * @param item the item to select
4958 * @exception IllegalArgumentException <ul>
4959 * <li>ERROR_NULL_ARGUMENT - if the item is null</li>
4960 * <li>ERROR_INVALID_ARGUMENT - if the item has been disposed</li>
4962 * @exception SWTException <ul>
4963 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
4964 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
4969 public void setSelection (TableItem item) {
4971 if (item == null) error (SWT.ERROR_NULL_ARGUMENT);
4972 setSelection (new TableItem [] {item});
4976 * Sets the receiver's selection to be the given array of items.
4977 * The current selection is cleared before the new items are selected,
4978 * and if necessary the receiver is scrolled to make the new selection visible.
4980 * Items that are not in the receiver are ignored.
4981 * If the receiver is single-select and multiple items are specified,
4982 * then all items are ignored.
4985 * @param items the array of items
4987 * @exception IllegalArgumentException <ul>
4988 * <li>ERROR_NULL_ARGUMENT - if the array of items is null</li>
4989 * <li>ERROR_INVALID_ARGUMENT - if one of the items has been disposed</li>
4991 * @exception SWTException <ul>
4992 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
4993 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
4996 * @see Table#deselectAll()
4997 * @see Table#select(int[])
4998 * @see Table#setSelection(int[])
5000 public void setSelection (TableItem [] items) {
5002 if (items == null) error (SWT.ERROR_NULL_ARGUMENT);
5004 int length = items.length;
5005 if (length == 0 || ((style & SWT.SINGLE) != 0 && length > 1)) return;
5006 int focusIndex = -1;
5007 for (int i=length-1; i>=0; --i) {
5008 int index = indexOf (items [i]);
5010 select (focusIndex = index);
5013 if (focusIndex != -1) setFocusIndex (focusIndex);
5018 * Selects the item at the given zero-relative index in the receiver.
5019 * The current selection is first cleared, then the new item is selected,
5020 * and if necessary the receiver is scrolled to make the new selection visible.
5022 * @param index the index of the item to select
5024 * @exception SWTException <ul>
5025 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
5026 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
5029 * @see Table#deselectAll()
5030 * @see Table#select(int)
5032 public void setSelection (int index) {
5036 if (index != -1) setFocusIndex (index);
5041 * Selects the items in the range specified by the given zero-relative
5042 * indices in the receiver. The range of indices is inclusive.
5043 * The current selection is cleared before the new items are selected,
5044 * and if necessary the receiver is scrolled to make the new selection visible.
5046 * Indices that are out of range are ignored and no items will be selected
5047 * if start is greater than end.
5048 * If the receiver is single-select and there is more than one item in the
5049 * given range, then all indices are ignored.
5052 * @param start the start index of the items to select
5053 * @param end the end index of the items to select
5055 * @exception SWTException <ul>
5056 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
5057 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
5060 * @see Table#deselectAll()
5061 * @see Table#select(int,int)
5063 public void setSelection (int start, int end) {
5066 if (end < 0 || start > end || ((style & SWT.SINGLE) != 0 && start != end)) return;
5067 int count = (int)OS.SendMessage (handle, OS.LVM_GETITEMCOUNT, 0, 0);
5068 if (count == 0 || start >= count) return;
5069 start = Math.max (0, start);
5070 end = Math.min (end, count - 1);
5071 select (start, end);
5072 setFocusIndex (start);
5077 * Sets the column used by the sort indicator for the receiver. A null
5078 * value will clear the sort indicator. The current sort column is cleared
5079 * before the new column is set.
5081 * @param column the column used by the sort indicator or <code>null</code>
5083 * @exception IllegalArgumentException <ul>
5084 * <li>ERROR_INVALID_ARGUMENT - if the column is disposed</li>
5086 * @exception SWTException <ul>
5087 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
5088 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
5093 public void setSortColumn (TableColumn column) {
5095 if (column != null && column.isDisposed ()) error (SWT.ERROR_INVALID_ARGUMENT);
5096 if (sortColumn != null && !sortColumn.isDisposed ()) {
5097 sortColumn.setSortDirection (SWT.NONE);
5099 sortColumn = column;
5100 if (sortColumn != null && sortDirection != SWT.NONE) {
5101 sortColumn.setSortDirection (sortDirection);
5106 * Sets the direction of the sort indicator for the receiver. The value
5107 * can be one of <code>UP</code>, <code>DOWN</code> or <code>NONE</code>.
5109 * @param direction the direction of the sort indicator
5111 * @exception SWTException <ul>
5112 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
5113 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
5118 public void setSortDirection (int direction) {
5120 if ((direction & (SWT.UP | SWT.DOWN)) == 0 && direction != SWT.NONE) return;
5121 sortDirection = direction;
5122 if (sortColumn != null && !sortColumn.isDisposed ()) {
5123 sortColumn.setSortDirection (direction);
5127 void setSubImagesVisible (boolean visible) {
5128 int dwExStyle = (int)OS.SendMessage (handle, OS.LVM_GETEXTENDEDLISTVIEWSTYLE, 0, 0);
5129 if ((dwExStyle & OS.LVS_EX_SUBITEMIMAGES) != 0 == visible) return;
5130 int bits = visible ? OS.LVS_EX_SUBITEMIMAGES : 0;
5131 OS.SendMessage (handle, OS.LVM_SETEXTENDEDLISTVIEWSTYLE, OS.LVS_EX_SUBITEMIMAGES, bits);
5134 void setTableEmpty () {
5135 if (imageList != null) {
5137 * Bug in Windows. When LVM_SETIMAGELIST is used to remove the
5138 * image list by setting it to NULL, the item width and height
5139 * is not changed and space is reserved for icons despite the
5140 * fact that there are none. The fix is to set the image list
5141 * to be very small before setting it to NULL. This causes
5142 * Windows to reserve the smallest possible space when an image
5145 long hImageList = OS.ImageList_Create (1, 1, 0, 0, 0);
5146 OS.SendMessage (handle, OS.LVM_SETIMAGELIST, OS.LVSIL_SMALL, hImageList);
5147 OS.SendMessage (handle, OS.LVM_SETIMAGELIST, OS.LVSIL_SMALL, 0);
5148 if (headerImageList != null) {
5149 long hHeaderImageList = headerImageList.getHandle ();
5150 long hwndHeader = OS.SendMessage (handle, OS.LVM_GETHEADER, 0, 0);
5151 OS.SendMessage (hwndHeader, OS.HDM_SETIMAGELIST, 0, hHeaderImageList);
5153 OS.ImageList_Destroy (hImageList);
5154 display.releaseImageList (imageList);
5156 if (itemHeight != -1) setItemHeight (false);
5158 if (!hooks (SWT.MeasureItem) && !hooks (SWT.EraseItem) && !hooks (SWT.PaintItem)) {
5159 Control control = findBackgroundControl ();
5160 if (control == null) control = this;
5161 if (control.backgroundImage == null) {
5162 setCustomDraw (false);
5163 setBackgroundTransparent (false);
5167 if (columnCount == 0) {
5168 OS.SendMessage (handle, OS.LVM_SETCOLUMNWIDTH, 0, 0);
5169 setScrollWidth (null, false);
5174 * Sets the zero-relative index of the item which is currently
5175 * at the top of the receiver. This index can change when items
5176 * are scrolled or new items are added and removed.
5178 * @param index the index of the top item
5180 * @exception SWTException <ul>
5181 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
5182 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
5185 public void setTopIndex (int index) {
5187 int topIndex = (int)OS.SendMessage (handle, OS.LVM_GETTOPINDEX, 0, 0);
5188 if (index == topIndex) return;
5189 if (!painted && hooks (SWT.MeasureItem)) hitTestSelection (index, 0, 0);
5192 * Bug in Windows. For some reason, LVM_SCROLL refuses to
5193 * scroll a table vertically when the width and height of
5194 * the table is smaller than a certain size. The values
5195 * that seem to cause the problem are width=68 and height=6
5196 * but there is no guarantee that these values cause the
5197 * failure on different machines or on different versions
5198 * of Windows. It may depend on the font and any number
5199 * of other factors. For example, setting the font to
5200 * anything but the default sometimes fixes the problem.
5201 * The fix is to use LVM_GETCOUNTPERPAGE to detect the
5202 * case when the number of visible items is zero and
5203 * use LVM_ENSUREVISIBLE to scroll the table to make the
5208 * Bug in Windows. When the table header is visible and
5209 * there is not enough space to show a single table item,
5210 * LVM_GETCOUNTPERPAGE can return a negative number instead
5211 * of zero. The fix is to test for negative or zero.
5213 if (OS.SendMessage (handle, OS.LVM_GETCOUNTPERPAGE, 0, 0) <= 0) {
5215 * Bug in Windows. For some reason, LVM_ENSUREVISIBLE can
5216 * scroll one item more or one item less when there is not
5217 * enough space to show a single table item. The fix is
5218 * to detect the case and call LVM_ENSUREVISIBLE again with
5219 * the same arguments. It seems that once LVM_ENSUREVISIBLE
5220 * has scrolled into the general area, it is able to scroll
5221 * to the exact item.
5223 OS.SendMessage (handle, OS.LVM_ENSUREVISIBLE, index, 1);
5224 if (index != OS.SendMessage (handle, OS.LVM_GETTOPINDEX, 0, 0)) {
5225 OS.SendMessage (handle, OS.LVM_ENSUREVISIBLE, index, 1);
5230 /* Use LVM_SCROLL to scroll the table */
5231 RECT rect = new RECT ();
5232 rect.left = OS.LVIR_BOUNDS;
5233 ignoreCustomDraw = true;
5234 OS.SendMessage (handle, OS.LVM_GETITEMRECT, 0, rect);
5235 ignoreCustomDraw = false;
5236 int dy = (index - topIndex) * (rect.bottom - rect.top);
5237 OS.SendMessage (handle, OS.LVM_SCROLL, 0, dy);
5241 * Shows the column. If the column is already showing in the receiver,
5242 * this method simply returns. Otherwise, the columns are scrolled until
5243 * the column is visible.
5245 * @param column the column to be shown
5247 * @exception IllegalArgumentException <ul>
5248 * <li>ERROR_NULL_ARGUMENT - if the column is null</li>
5249 * <li>ERROR_INVALID_ARGUMENT - if the column has been disposed</li>
5251 * @exception SWTException <ul>
5252 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
5253 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
5258 public void showColumn (TableColumn column) {
5260 if (column == null) error (SWT.ERROR_NULL_ARGUMENT);
5261 if (column.isDisposed()) error(SWT.ERROR_INVALID_ARGUMENT);
5262 if (column.parent != this) return;
5263 int index = indexOf (column);
5264 if (!(0 <= index && index < columnCount)) return;
5266 * Feature in Windows. Calling LVM_GETSUBITEMRECT with -1 for the
5267 * row number gives the bounds of the item that would be above the
5268 * first row in the table. This is undocumented and does not work
5269 * for the first column. In this case, to get the bounds of the
5270 * first column, get the bounds of the second column and subtract
5271 * the width of the first. The left edge of the second column is
5272 * also used as the right edge of the first.
5274 RECT itemRect = new RECT ();
5275 itemRect.left = OS.LVIR_BOUNDS;
5278 ignoreCustomDraw = true;
5279 OS.SendMessage (handle, OS.LVM_GETSUBITEMRECT, -1, itemRect);
5280 ignoreCustomDraw = false;
5281 itemRect.right = itemRect.left;
5282 int width = (int)OS.SendMessage (handle, OS.LVM_GETCOLUMNWIDTH, 0, 0);
5283 itemRect.left = itemRect.right - width;
5285 itemRect.top = index;
5286 ignoreCustomDraw = true;
5287 OS.SendMessage (handle, OS.LVM_GETSUBITEMRECT, -1, itemRect);
5288 ignoreCustomDraw = false;
5291 * Bug in Windows. When a table that is drawing grid lines
5292 * is slowly scrolled horizontally to the left, the table does
5293 * not redraw the newly exposed vertical grid lines. The fix
5294 * is to save the old scroll position, call the window proc,
5295 * get the new scroll position and redraw the new area.
5298 if (_getLinesVisible()) {
5299 SCROLLINFO info = new SCROLLINFO ();
5300 info.cbSize = SCROLLINFO.sizeof;
5301 info.fMask = OS.SIF_POS;
5302 OS.GetScrollInfo (handle, OS.SB_HORZ, info);
5305 RECT rect = new RECT ();
5306 OS.GetClientRect (handle, rect);
5307 if (itemRect.left < rect.left) {
5308 int dx = itemRect.left - rect.left;
5309 OS.SendMessage (handle, OS.LVM_SCROLL, dx, 0);
5311 int width = Math.min (rect.right - rect.left, itemRect.right - itemRect.left);
5312 if (itemRect.left + width > rect.right) {
5313 int dx = itemRect.left + width - rect.right;
5314 OS.SendMessage (handle, OS.LVM_SCROLL, dx, 0);
5318 * Bug in Windows. When a table that is drawing grid lines
5319 * is slowly scrolled horizontally to the left, the table does
5320 * not redraw the newly exposed vertical grid lines. The fix
5321 * is to save the old scroll position, call the window proc,
5322 * get the new scroll position and redraw the new area.
5324 if (_getLinesVisible()) {
5325 SCROLLINFO info = new SCROLLINFO ();
5326 info.cbSize = SCROLLINFO.sizeof;
5327 info.fMask = OS.SIF_POS;
5328 OS.GetScrollInfo (handle, OS.SB_HORZ, info);
5329 int newPos = info.nPos;
5330 if (newPos < oldPos) {
5331 rect.right = oldPos - newPos + GRID_WIDTH;
5332 OS.InvalidateRect (handle, rect, true);
5337 void showItem (int index) {
5338 if (!painted && hooks (SWT.MeasureItem)) hitTestSelection (index, 0, 0);
5340 * Bug in Windows. For some reason, when there is insufficient space
5341 * to show an item, LVM_ENSUREVISIBLE causes blank lines to be
5342 * inserted at the top of the widget. A call to LVM_GETTOPINDEX will
5343 * return a negative number (this is an impossible result). The fix
5344 * is to use LVM_GETCOUNTPERPAGE to detect the case when the number
5345 * of visible items is zero and use LVM_ENSUREVISIBLE with the
5346 * fPartialOK flag set to true to scroll the table.
5348 long counterPage = OS.SendMessage (handle, OS.LVM_GETCOUNTPERPAGE, 0, 0);
5349 if (counterPage <= 0) {
5351 * Bug in Windows. For some reason, LVM_ENSUREVISIBLE can
5352 * scroll one item more or one item less when there is not
5353 * enough space to show a single table item. The fix is
5354 * to detect the case and call LVM_ENSUREVISIBLE again with
5355 * the same arguments. It seems that once LVM_ENSUREVISIBLE
5356 * has scrolled into the general area, it is able to scroll
5357 * to the exact item.
5359 OS.SendMessage (handle, OS.LVM_ENSUREVISIBLE, index, 1);
5360 if (index != OS.SendMessage (handle, OS.LVM_GETTOPINDEX, 0, 0)) {
5361 OS.SendMessage (handle, OS.LVM_ENSUREVISIBLE, index, 1);
5365 * Bug in Windows Vista and onwards: For some reason,
5366 * LVM_ENSUREVISIBLE command scrolls the table to the leftmost
5367 * column even if the item is already visible, refer Bug 334234
5369 long topIndex = OS.SendMessage (handle, OS.LVM_GETTOPINDEX, 0, 0);
5370 if (topIndex > index || index >= topIndex + counterPage ) {
5371 OS.SendMessage (handle, OS.LVM_ENSUREVISIBLE, index, 0);
5377 * Shows the item. If the item is already showing in the receiver,
5378 * this method simply returns. Otherwise, the items are scrolled until
5379 * the item is visible.
5381 * @param item the item to be shown
5383 * @exception IllegalArgumentException <ul>
5384 * <li>ERROR_NULL_ARGUMENT - if the item is null</li>
5385 * <li>ERROR_INVALID_ARGUMENT - if the item has been disposed</li>
5387 * @exception SWTException <ul>
5388 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
5389 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
5392 * @see Table#showSelection()
5394 public void showItem (TableItem item) {
5396 if (item == null) error (SWT.ERROR_NULL_ARGUMENT);
5397 if (item.isDisposed()) error(SWT.ERROR_INVALID_ARGUMENT);
5398 int index = indexOf (item);
5399 if (index != -1) showItem (index);
5403 * Shows the selection. If the selection is already showing in the receiver,
5404 * this method simply returns. Otherwise, the items are scrolled until
5405 * the selection is visible.
5407 * @exception SWTException <ul>
5408 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
5409 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
5412 * @see Table#showItem(TableItem)
5414 public void showSelection () {
5416 int index = (int)OS.SendMessage (handle, OS.LVM_GETNEXTITEM, -1, OS.LVNI_SELECTED);
5419 * Bug in Windows. For some reason, when a table had vertically
5420 * scrolled down, followed by clearAll or clear items call such that
5421 * vertical scroll bar is no more visible/needed, even then top of
5422 * the table is not visible. Fix is to make sure on show selection
5423 * table gets vertically scrolled back to the top, refer bug 442275
5425 * Make sure above fix is only applied to the active shell, see bug 450391.
5427 if (display.getActiveShell() == getShell() && (style & SWT.NO_SCROLL) == 0
5428 && (verticalBar == null || !verticalBar.isVisible())) {
5436 /*public*/ void sort () {
5438 // if ((style & SWT.VIRTUAL) != 0) return;
5439 // int itemCount = OS.SendMessage (handle, OS.LVM_GETITEMCOUNT, 0, 0);
5440 // if (itemCount == 0 || itemCount == 1) return;
5441 // Comparator comparator = new Comparator () {
5442 // int index = sortColumn == null ? 0 : indexOf (sortColumn);
5443 // public int compare (Object object1, Object object2) {
5444 // TableItem item1 = (TableItem) object1, item2 = (TableItem) object2;
5445 // if (sortDirection == SWT.UP || sortDirection == SWT.NONE) {
5446 // return item1.getText (index).compareTo (item2.getText (index));
5448 // return item2.getText (index).compareTo (item1.getText (index));
5452 // Arrays.sort (items, 0, itemCount, comparator);
5459 if (HeaderProc != 0) {
5460 OS.SetWindowLongPtr (hwndHeader, OS.GWLP_WNDPROC, display.windowProc);
5464 RECT toolTipInset (RECT rect) {
5465 RECT insetRect = new RECT ();
5466 OS.SetRect (insetRect, rect.left - 1, rect.top - 1, rect.right + 1, rect.bottom + 1);
5470 RECT toolTipRect (RECT rect) {
5471 RECT toolRect = new RECT ();
5472 OS.SetRect (toolRect, rect.left - 1, rect.top - 1, rect.right + 1, rect.bottom + 1);
5477 String toolTipText (NMTTDISPINFO hdr) {
5478 long hwndToolTip = OS.SendMessage (handle, OS.LVM_GETTOOLTIPS, 0, 0);
5479 if (hwndToolTip == hdr.hwndFrom && toolTipText != null) return ""; //$NON-NLS-1$
5480 if (headerToolTipHandle == hdr.hwndFrom) {
5481 for (int i=0; i<columnCount; i++) {
5482 TableColumn column = columns [i];
5483 if (column.id == hdr.idFrom) return column.toolTipText;
5486 return super.toolTipText (hdr);
5490 void unsubclass () {
5491 super.unsubclass ();
5492 if (HeaderProc != 0) {
5493 OS.SetWindowLongPtr (hwndHeader, OS.GWLP_WNDPROC, HeaderProc);
5498 void update (boolean all) {
5501 * When there are many columns in a table, scrolling performance
5502 * can be improved by temporarily unsubclassing the window proc
5503 * so that internal messages are dispatched directly to the table.
5504 * If the application expects to see a paint event or has a child
5505 * whose font, foreground or background color might be needed,
5506 * the window proc cannot be unsubclassed.
5508 * NOTE: The header tooltip can subclass the header proc so the
5509 * current proc must be restored or header tooltips stop working.
5511 long oldHeaderProc = 0, oldTableProc = 0;
5512 boolean fixSubclass = isOptimizedRedraw ();
5514 oldTableProc = OS.SetWindowLongPtr (handle, OS.GWLP_WNDPROC, TableProc);
5515 oldHeaderProc = OS.SetWindowLongPtr (hwndHeader, OS.GWLP_WNDPROC, HeaderProc);
5519 OS.SetWindowLongPtr (handle, OS.GWLP_WNDPROC, oldTableProc);
5520 OS.SetWindowLongPtr (hwndHeader, OS.GWLP_WNDPROC, oldHeaderProc);
5524 void updateHeaderToolTips () {
5525 if (headerToolTipHandle == 0) return;
5526 long hwndHeader = OS.SendMessage (handle, OS.LVM_GETHEADER, 0, 0);
5527 RECT rect = new RECT ();
5528 TOOLINFO lpti = new TOOLINFO ();
5529 lpti.cbSize = TOOLINFO.sizeof;
5530 lpti.uFlags = OS.TTF_SUBCLASS;
5531 lpti.hwnd = hwndHeader;
5532 lpti.lpszText = OS.LPSTR_TEXTCALLBACK;
5533 for (int i=0; i<columnCount; i++) {
5534 TableColumn column = columns [i];
5535 if (OS.SendMessage (hwndHeader, OS.HDM_GETITEMRECT, i, rect) != 0) {
5536 lpti.uId = column.id = display.nextToolTipId++;
5537 lpti.left = rect.left;
5538 lpti.top = rect.top;
5539 lpti.right = rect.right;
5540 lpti.bottom = rect.bottom;
5541 OS.SendMessage (headerToolTipHandle, OS.TTM_ADDTOOL, 0, lpti);
5547 void updateMenuLocation (Event event) {
5548 Rectangle clientArea = getClientAreaInPixels ();
5549 int x = clientArea.x, y = clientArea.y;
5550 int focusIndex = getFocusIndex ();
5551 if (focusIndex != -1) {
5552 TableItem focusItem = getItem (focusIndex);
5553 Rectangle bounds = focusItem.getBoundsInPixels (0);
5554 if (focusItem.text != null && focusItem.text.length () != 0) {
5555 bounds = focusItem.getBoundsInPixels ();
5557 x = Math.max (x, bounds.x + bounds.width / 2);
5558 x = Math.min (x, clientArea.x + clientArea.width);
5559 y = Math.max (y, bounds.y + bounds.height);
5560 y = Math.min (y, clientArea.y + clientArea.height);
5562 Point pt = toDisplayInPixels (x, y);
5563 event.setLocationInPixels(pt.x, pt.y);
5566 void updateMoveable () {
5568 while (index < columnCount) {
5569 if (columns [index].moveable) break;
5572 int newBits = index < columnCount ? OS.LVS_EX_HEADERDRAGDROP : 0;
5573 OS.SendMessage (handle, OS.LVM_SETEXTENDEDLISTVIEWSTYLE, OS.LVS_EX_HEADERDRAGDROP, newBits);
5577 void updateOrientation () {
5578 super.updateOrientation ();
5579 long hwndHeader = OS.SendMessage (handle, OS.LVM_GETHEADER, 0, 0);
5580 if (hwndHeader != 0) {
5581 int bits = OS.GetWindowLong (hwndHeader, OS.GWL_EXSTYLE);
5582 if ((style & SWT.RIGHT_TO_LEFT) != 0) {
5583 bits |= OS.WS_EX_LAYOUTRTL;
5585 bits &= ~OS.WS_EX_LAYOUTRTL;
5587 bits &= ~OS.WS_EX_RTLREADING;
5588 OS.SetWindowLong (hwndHeader, OS.GWL_EXSTYLE, bits);
5589 OS.InvalidateRect (hwndHeader, null, true);
5590 RECT rect = new RECT ();
5591 OS.GetWindowRect (handle, rect);
5592 int width = rect.right - rect.left, height = rect.bottom - rect.top;
5593 OS.SetWindowPos (handle, 0, 0, 0, width - 1, height - 1, OS.SWP_NOMOVE | OS.SWP_NOZORDER);
5594 OS.SetWindowPos (handle, 0, 0, 0, width, height, OS.SWP_NOMOVE | OS.SWP_NOZORDER);
5596 if ((style & SWT.CHECK) != 0) fixCheckboxImageListColor (false);
5597 if (imageList != null) {
5598 Point size = imageList.getImageSize ();
5599 display.releaseImageList (imageList);
5600 imageList = display.getImageList (style & SWT.RIGHT_TO_LEFT, size.x, size.y);
5601 int count = (int)OS.SendMessage (handle, OS.LVM_GETITEMCOUNT, 0, 0);
5602 for (int i = 0; i < count; i++) {
5603 TableItem item = _getItem (i, false);
5605 Image image = item.image;
5606 if (image != null) {
5607 int index = imageList.indexOf (image);
5608 if (index == -1) imageList.add (image);
5612 long hImageList = imageList.getHandle ();
5613 OS.SendMessage (handle, OS.LVM_SETIMAGELIST, OS.LVSIL_SMALL, hImageList);
5615 if (hwndHeader != 0) {
5616 if (headerImageList != null) {
5617 Point size = headerImageList.getImageSize ();
5618 display.releaseImageList (headerImageList);
5619 headerImageList = display.getImageList (style & SWT.RIGHT_TO_LEFT, size.x, size.y);
5620 if (columns != null) {
5621 for (int i = 0; i < columns.length; i++) {
5622 TableColumn column = columns [i];
5623 if (column != null) {
5624 Image image = column.image;
5625 if (image != null) {
5626 LVCOLUMN lvColumn = new LVCOLUMN ();
5627 lvColumn.mask = OS.LVCF_FMT;
5628 OS.SendMessage (hwndHeader, OS.LVM_GETCOLUMN, i, lvColumn);
5629 if ((lvColumn.fmt & OS.LVCFMT_IMAGE) != 0) {
5630 int index = headerImageList.indexOf (image);
5631 if (index == -1) headerImageList.add (image);
5632 lvColumn.iImage = index;
5633 lvColumn.mask = OS.LVCF_IMAGE;
5634 OS.SendMessage (hwndHeader, OS.LVM_SETCOLUMN, i, lvColumn);
5640 long hHeaderImageList = headerImageList.getHandle ();
5641 OS.SendMessage (hwndHeader, OS.HDM_SETIMAGELIST, 0, hHeaderImageList);
5647 boolean updateTextDirection(int textDirection) {
5648 if (super.updateTextDirection(textDirection)) {
5649 if (textDirection == AUTO_TEXT_DIRECTION || (state & HAS_AUTO_DIRECTION) != 0) {
5650 for (int i = 0, n = items.length; i < n; i++) {
5651 if (items[i] != null) {
5652 items[i].updateTextDirection(textDirection == AUTO_TEXT_DIRECTION ? AUTO_TEXT_DIRECTION : style & SWT.FLIP_TEXT_DIRECTION);
5656 OS.InvalidateRect (handle, null, true);
5663 int widgetStyle () {
5664 int bits = super.widgetStyle () | OS.LVS_SHAREIMAGELISTS;
5665 if ((style & SWT.HIDE_SELECTION) == 0) bits |= OS.LVS_SHOWSELALWAYS;
5666 if ((style & SWT.SINGLE) != 0) bits |= OS.LVS_SINGLESEL;
5668 * This code is intentionally commented. In the future,
5669 * the FLAT bit may be used to make the header flat and
5670 * unresponsive to mouse clicks.
5672 // if ((style & SWT.FLAT) != 0) bits |= OS.LVS_NOSORTHEADER;
5673 bits |= OS.LVS_REPORT | OS.LVS_NOCOLUMNHEADER;
5674 if ((style & SWT.VIRTUAL) != 0) bits |= OS.LVS_OWNERDATA;
5679 TCHAR windowClass () {
5684 long windowProc () {
5689 long windowProc (long hwnd, int msg, long wParam, long lParam) {
5690 if (handle == 0) return 0;
5691 if (hwnd != handle) {
5693 case OS.WM_CONTEXTMENU: {
5694 LRESULT result = wmContextMenu (hwnd, wParam, lParam);
5695 if (result != null) return result.value;
5698 case OS.WM_MOUSELEAVE: {
5700 * Bug in Windows. On XP, when a tooltip is hidden
5701 * due to a time out or mouse press, the tooltip
5702 * remains active although no longer visible and
5703 * won't show again until another tooltip becomes
5704 * active. The fix is to reset the tooltip bounds.
5706 updateHeaderToolTips ();
5707 updateHeaderToolTips ();
5710 case OS.WM_NOTIFY: {
5711 NMHDR hdr = new NMHDR ();
5712 OS.MoveMemory (hdr, lParam, NMHDR.sizeof);
5716 case OS.TTN_GETDISPINFO:
5717 return OS.SendMessage (handle, msg, wParam, lParam);
5721 case OS.WM_SETCURSOR: {
5722 if (wParam == hwnd) {
5723 int hitTest = (short) OS.LOWORD (lParam);
5724 if (hitTest == OS.HTCLIENT) {
5725 HDHITTESTINFO pinfo = new HDHITTESTINFO ();
5726 int pos = OS.GetMessagePos ();
5727 POINT pt = new POINT ();
5728 OS.POINTSTOPOINT (pt, pos);
5729 OS.ScreenToClient (hwnd, pt);
5732 long hwndHeader = OS.SendMessage (handle, OS.LVM_GETHEADER, 0, 0);
5733 int index = (int)OS.SendMessage (hwndHeader, OS.HDM_HITTEST, 0, pinfo);
5734 if (0 <= index && index < columnCount && !columns [index].resizable) {
5735 if ((pinfo.flags & (OS.HHT_ONDIVIDER | OS.HHT_ONDIVOPEN)) != 0) {
5736 OS.SetCursor (OS.LoadCursor (0, OS.IDC_ARROW));
5745 return callWindowProc (hwnd, msg, wParam, lParam);
5747 if (msg == Display.DI_GETDRAGIMAGE) {
5749 * Bug in Windows. On Vista, for some reason, DI_GETDRAGIMAGE
5750 * returns an image that does not contain strings.
5752 * Bug in Windows. For custom draw control the window origin the
5755 * The fix for both cases is to create the image using PrintWindow().
5757 int topIndex = (int)OS.SendMessage (handle, OS.LVM_GETTOPINDEX, 0, 0);
5758 int selection = (int)OS.SendMessage (handle, OS.LVM_GETNEXTITEM, topIndex - 1, OS.LVNI_SELECTED);
5759 if (selection == -1) return 0;
5760 POINT mousePos = new POINT ();
5761 OS.POINTSTOPOINT (mousePos, OS.GetMessagePos ());
5762 OS.MapWindowPoints(0, handle, mousePos, 1);
5763 RECT clientRect = new RECT ();
5764 OS.GetClientRect (handle, clientRect);
5765 TableItem item = _getItem (selection);
5766 RECT rect = item.getBounds (selection, 0, true, true, true);
5767 if ((style & SWT.FULL_SELECTION) != 0) {
5768 int width = DRAG_IMAGE_SIZE;
5769 rect.left = Math.max (clientRect.left, mousePos.x - width / 2);
5770 if (clientRect.right > rect.left + width) {
5771 rect.right = rect.left + width;
5773 rect.right = clientRect.right;
5774 rect.left = Math.max (clientRect.left, rect.right - width);
5777 long hRgn = OS.CreateRectRgn (rect.left, rect.top, rect.right, rect.bottom);
5778 while ((selection = (int)OS.SendMessage (handle, OS.LVM_GETNEXTITEM, selection, OS.LVNI_SELECTED)) != -1) {
5779 if (rect.bottom - rect.top > DRAG_IMAGE_SIZE) break;
5780 if (rect.bottom > clientRect.bottom) break;
5781 RECT itemRect = item.getBounds (selection, 0, true, true, true);
5782 long rectRgn = OS.CreateRectRgn (rect.left, itemRect.top, rect.right, itemRect.bottom);
5783 OS.CombineRgn (hRgn, hRgn, rectRgn, OS.RGN_OR);
5784 OS.DeleteObject (rectRgn);
5785 rect.bottom = itemRect.bottom;
5787 OS.GetRgnBox (hRgn, rect);
5789 /* Create resources */
5790 long hdc = OS.GetDC (handle);
5791 long memHdc = OS.CreateCompatibleDC (hdc);
5792 BITMAPINFOHEADER bmiHeader = new BITMAPINFOHEADER ();
5793 bmiHeader.biSize = BITMAPINFOHEADER.sizeof;
5794 bmiHeader.biWidth = rect.right - rect.left;
5795 bmiHeader.biHeight = -(rect.bottom - rect.top);
5796 bmiHeader.biPlanes = 1;
5797 bmiHeader.biBitCount = 32;
5798 bmiHeader.biCompression = OS.BI_RGB;
5799 byte [] bmi = new byte [BITMAPINFOHEADER.sizeof];
5800 OS.MoveMemory (bmi, bmiHeader, BITMAPINFOHEADER.sizeof);
5801 long [] pBits = new long [1];
5802 long memDib = OS.CreateDIBSection (0, bmi, OS.DIB_RGB_COLORS, pBits, 0, 0);
5803 if (memDib == 0) error (SWT.ERROR_NO_HANDLES);
5804 long oldMemBitmap = OS.SelectObject (memHdc, memDib);
5805 int colorKey = 0x0000FD;
5806 POINT pt = new POINT();
5807 OS.SetWindowOrgEx (memHdc, rect.left, rect.top, pt);
5808 OS.FillRect (memHdc, rect, findBrush (colorKey, OS.BS_SOLID));
5809 OS.OffsetRgn (hRgn, -rect.left, -rect.top);
5810 OS.SelectClipRgn (memHdc, hRgn);
5811 OS.PrintWindow (handle, memHdc, 0);
5812 OS.SetWindowOrgEx (memHdc, pt.x, pt.y, null);
5813 OS.SelectObject (memHdc, oldMemBitmap);
5814 OS.DeleteDC (memHdc);
5815 OS.ReleaseDC (0, hdc);
5816 OS.DeleteObject (hRgn);
5818 SHDRAGIMAGE shdi = new SHDRAGIMAGE ();
5819 shdi.hbmpDragImage = memDib;
5820 shdi.crColorKey = colorKey;
5821 shdi.sizeDragImage.cx = bmiHeader.biWidth;
5822 shdi.sizeDragImage.cy = -bmiHeader.biHeight;
5823 shdi.ptOffset.x = mousePos.x - rect.left;
5824 shdi.ptOffset.y = mousePos.y - rect.top;
5825 if ((style & SWT.MIRRORED) != 0) {
5826 shdi.ptOffset.x = shdi.sizeDragImage.cx - shdi.ptOffset.x;
5828 OS.MoveMemory (lParam, shdi, SHDRAGIMAGE.sizeof);
5831 return super.windowProc (hwnd, msg, wParam, lParam);
5835 LRESULT WM_CHAR (long wParam, long lParam) {
5836 LRESULT result = super.WM_CHAR (wParam, lParam);
5837 if (result != null) return result;
5838 switch ((int)wParam) {
5840 if ((style & SWT.CHECK) != 0) {
5841 int index = (int)OS.SendMessage (handle, OS.LVM_GETNEXTITEM, -1, OS.LVNI_FOCUSED);
5843 TableItem item = _getItem (index);
5844 item.setChecked (!item.getChecked (), true);
5845 OS.NotifyWinEvent (OS.EVENT_OBJECT_FOCUS, handle, OS.OBJID_CLIENT, index + 1);
5849 * NOTE: Call the window proc with WM_KEYDOWN rather than WM_CHAR
5850 * so that the key that was ignored during WM_KEYDOWN is processed.
5851 * This allows the application to cancel an operation that is normally
5852 * performed in WM_KEYDOWN from WM_CHAR.
5854 long code = callWindowProc (handle, OS.WM_KEYDOWN, wParam, lParam);
5855 return new LRESULT (code);
5858 * Feature in Windows. Windows sends LVN_ITEMACTIVATE from WM_KEYDOWN
5859 * instead of WM_CHAR. This means that application code that expects
5860 * to consume the key press and therefore avoid a SWT.DefaultSelection
5861 * event will fail. The fix is to ignore LVN_ITEMACTIVATE when it is
5862 * caused by WM_KEYDOWN and send SWT.DefaultSelection from WM_CHAR.
5864 int index = (int)OS.SendMessage (handle, OS.LVM_GETNEXTITEM, -1, OS.LVNI_FOCUSED);
5866 Event event = new Event ();
5867 event.item = _getItem (index);
5868 sendSelectionEvent (SWT.DefaultSelection, event, false);
5870 return LRESULT.ZERO;
5876 LRESULT WM_CONTEXTMENU (long wParam, long lParam) {
5878 * Feature in Windows. For some reason, when the right
5879 * mouse button is pressed over an item, Windows sends
5880 * a WM_CONTEXTMENU from WM_RBUTTONDOWN, instead of from
5881 * WM_RBUTTONUP. This causes two context menus requests
5882 * to be sent. The fix is to ignore WM_CONTEXTMENU on
5885 * NOTE: This only happens when dragging is disabled.
5886 * When the table is detecting drag, the WM_CONTEXTMENU
5887 * is not sent WM_RBUTTONUP.
5889 if (!display.runDragDrop) return LRESULT.ZERO;
5890 return super.WM_CONTEXTMENU (wParam, lParam);
5894 LRESULT WM_ERASEBKGND (long wParam, long lParam) {
5895 LRESULT result = super.WM_ERASEBKGND (wParam, lParam);
5896 if (findImageControl () != null) return LRESULT.ONE;
5901 LRESULT WM_GETOBJECT (long wParam, long lParam) {
5903 * Ensure that there is an accessible object created for this
5904 * control because support for checked item accessibility is
5905 * temporarily implemented in the accessibility package.
5907 if ((style & SWT.CHECK) != 0) {
5908 if (accessible == null) accessible = new_Accessible (this);
5910 return super.WM_GETOBJECT (wParam, lParam);
5914 LRESULT WM_KEYDOWN (long wParam, long lParam) {
5915 LRESULT result = super.WM_KEYDOWN (wParam, lParam);
5916 if (result != null) return result;
5917 switch ((int)wParam) {
5920 * Ensure that the window proc does not process VK_SPACE
5921 * so that it can be handled in WM_CHAR. This allows the
5922 * application to cancel an operation that is normally
5923 * performed in WM_KEYDOWN from WM_CHAR.
5925 return LRESULT.ZERO;
5927 if (OS.GetKeyState (OS.VK_CONTROL) < 0) {
5929 while (index < columnCount) {
5930 if (!columns [index].getResizable ()) break;
5933 if (index != columnCount || hooks (SWT.MeasureItem)) {
5934 TableColumn [] newColumns = new TableColumn [columnCount];
5935 System.arraycopy (columns, 0, newColumns, 0, columnCount);
5936 for (int i=0; i<newColumns.length; i++) {
5937 TableColumn column = newColumns [i];
5938 if (!column.isDisposed () && column.getResizable ()) {
5942 return LRESULT.ZERO;
5951 * When there are many columns in a table, scrolling performance
5952 * can be improved by temporarily unsubclassing the window proc
5953 * so that internal messages are dispatched directly to the table.
5954 * If the application expects to see a paint event, the window
5955 * proc cannot be unsubclassed or the event will not be seen.
5957 * NOTE: The header tooltip can subclass the header proc so the
5958 * current proc must be restored or header tooltips stop working.
5960 long oldHeaderProc = 0, oldTableProc = 0;
5961 long hwndHeader = OS.SendMessage (handle, OS.LVM_GETHEADER, 0, 0);
5962 boolean fixSubclass = isOptimizedRedraw ();
5964 oldTableProc = OS.SetWindowLongPtr (handle, OS.GWLP_WNDPROC, TableProc);
5965 oldHeaderProc = OS.SetWindowLongPtr (hwndHeader, OS.GWLP_WNDPROC, HeaderProc);
5967 long code = callWindowProc (handle, OS.WM_KEYDOWN, wParam, lParam);
5968 result = code == 0 ? LRESULT.ZERO : new LRESULT (code);
5970 OS.SetWindowLongPtr (handle, OS.GWLP_WNDPROC, oldTableProc);
5971 OS.SetWindowLongPtr (hwndHeader, OS.GWLP_WNDPROC, oldHeaderProc);
5976 OS.SendMessage (handle, OS.WM_CHANGEUISTATE, OS.UIS_INITIALIZE, 0);
5983 LRESULT WM_KILLFOCUS (long wParam, long lParam) {
5984 LRESULT result = super.WM_KILLFOCUS (wParam, lParam);
5986 * Bug in Windows. When focus is lost, Windows does not
5987 * redraw the selection properly, leaving the image and
5988 * check box appearing selected. The fix is to redraw
5991 if (imageList != null || (style & SWT.CHECK) != 0) {
5992 OS.InvalidateRect (handle, null, false);
5998 LRESULT WM_LBUTTONDBLCLK (long wParam, long lParam) {
6001 * Feature in Windows. When the user selects outside of
6002 * a table item, Windows deselects all the items, even
6003 * when the table is multi-select. While not strictly
6004 * wrong, this is unexpected. The fix is to detect the
6005 * case and avoid calling the window proc.
6007 LVHITTESTINFO pinfo = new LVHITTESTINFO ();
6008 pinfo.x = OS.GET_X_LPARAM (lParam);
6009 pinfo.y = OS.GET_Y_LPARAM (lParam);
6010 int index = (int)OS.SendMessage (handle, OS.LVM_HITTEST, 0, pinfo);
6011 Display display = this.display;
6012 display.captureChanged = false;
6013 sendMouseEvent (SWT.MouseDown, 1, handle, OS.WM_LBUTTONDOWN, wParam, lParam);
6014 if (!sendMouseEvent (SWT.MouseDoubleClick, 1, handle, OS.WM_LBUTTONDBLCLK, wParam, lParam)) {
6015 if (!display.captureChanged && !isDisposed ()) {
6016 if (OS.GetCapture () != handle) OS.SetCapture (handle);
6018 return LRESULT.ZERO;
6020 if (pinfo.iItem != -1) callWindowProc (handle, OS.WM_LBUTTONDBLCLK, wParam, lParam);
6021 if (!display.captureChanged && !isDisposed ()) {
6022 if (OS.GetCapture () != handle) OS.SetCapture (handle);
6025 /* Look for check/uncheck */
6026 if ((style & SWT.CHECK) != 0) {
6028 * Note that when the table has LVS_EX_FULLROWSELECT and the
6029 * user clicks anywhere on a row except on the check box, all
6030 * of the bits are set. The hit test flags are LVHT_ONITEM.
6031 * This means that a bit test for LVHT_ONITEMSTATEICON is not
6032 * the correct way to determine that the user has selected
6033 * the check box, equality is needed.
6035 if (index != -1 && pinfo.flags == OS.LVHT_ONITEMSTATEICON) {
6036 TableItem item = _getItem (index);
6037 if (item != null && !item.isDisposed ()) {
6038 item.setChecked (!item.getChecked (), true);
6039 OS.NotifyWinEvent (OS.EVENT_OBJECT_FOCUS, handle, OS.OBJID_CLIENT, index + 1);
6043 return LRESULT.ZERO;
6047 LRESULT WM_LBUTTONDOWN (long wParam, long lParam) {
6049 * Feature in Windows. For some reason, capturing
6050 * the mouse after processing the mouse event for the
6051 * widget interferes with the normal mouse processing
6052 * for the widget. The fix is to avoid the automatic
6055 LRESULT result = sendMouseDownEvent (SWT.MouseDown, 1, OS.WM_LBUTTONDOWN, wParam, lParam);
6056 if (result == LRESULT.ZERO) return result;
6058 /* Look for check/uncheck */
6059 if ((style & SWT.CHECK) != 0) {
6060 LVHITTESTINFO pinfo = new LVHITTESTINFO ();
6061 pinfo.x = OS.GET_X_LPARAM (lParam);
6062 pinfo.y = OS.GET_Y_LPARAM (lParam);
6064 * Note that when the table has LVS_EX_FULLROWSELECT and the
6065 * user clicks anywhere on a row except on the check box, all
6066 * of the bits are set. The hit test flags are LVHT_ONITEM.
6067 * This means that a bit test for LVHT_ONITEMSTATEICON is not
6068 * the correct way to determine that the user has selected
6069 * the check box, equality is needed.
6071 int index = (int)OS.SendMessage (handle, OS.LVM_HITTEST, 0, pinfo);
6072 if (index != -1 && pinfo.flags == OS.LVHT_ONITEMSTATEICON) {
6073 TableItem item = _getItem (index);
6074 if (item != null && !item.isDisposed ()) {
6075 item.setChecked (!item.getChecked (), true);
6076 OS.NotifyWinEvent (OS.EVENT_OBJECT_FOCUS, handle, OS.OBJID_CLIENT, index + 1);
6084 LRESULT WM_MOUSEHOVER (long wParam, long lParam) {
6086 * Feature in Windows. Despite the fact that hot
6087 * tracking is not enabled, the hot tracking code
6088 * in WM_MOUSEHOVER is executed causing the item
6089 * under the cursor to be selected. The fix is to
6090 * avoid calling the window proc.
6092 LRESULT result = super.WM_MOUSEHOVER (wParam, lParam);
6093 int bits = (int)OS.SendMessage (handle, OS.LVM_GETEXTENDEDLISTVIEWSTYLE, 0, 0);
6094 int mask = OS.LVS_EX_ONECLICKACTIVATE | OS.LVS_EX_TRACKSELECT | OS.LVS_EX_TWOCLICKACTIVATE;
6095 if ((bits & mask) != 0) return result;
6096 return LRESULT.ZERO;
6100 LRESULT WM_PAINT (long wParam, long lParam) {
6101 if ((state & DISPOSE_SENT) != 0) return LRESULT.ZERO;
6104 if (fixScrollWidth) setScrollWidth (null, true);
6105 return super.WM_PAINT (wParam, lParam);
6109 LRESULT WM_RBUTTONDBLCLK (long wParam, long lParam) {
6111 * Feature in Windows. When the user selects outside of
6112 * a table item, Windows deselects all the items, even
6113 * when the table is multi-select. While not strictly
6114 * wrong, this is unexpected. The fix is to detect the
6115 * case and avoid calling the window proc.
6117 LVHITTESTINFO pinfo = new LVHITTESTINFO ();
6118 pinfo.x = OS.GET_X_LPARAM (lParam);
6119 pinfo.y = OS.GET_Y_LPARAM (lParam);
6120 OS.SendMessage (handle, OS.LVM_HITTEST, 0, pinfo);
6121 Display display = this.display;
6122 display.captureChanged = false;
6123 sendMouseEvent (SWT.MouseDown, 3, handle, OS.WM_RBUTTONDOWN, wParam, lParam);
6124 if (sendMouseEvent (SWT.MouseDoubleClick, 3, handle, OS.WM_RBUTTONDBLCLK, wParam, lParam)) {
6125 if (pinfo.iItem != -1) callWindowProc (handle, OS.WM_RBUTTONDBLCLK, wParam, lParam);
6127 if (!display.captureChanged && !isDisposed ()) {
6128 if (OS.GetCapture () != handle) OS.SetCapture (handle);
6130 return LRESULT.ZERO;
6134 LRESULT WM_RBUTTONDOWN (long wParam, long lParam) {
6136 * Feature in Windows. For some reason, capturing
6137 * the mouse after processing the mouse event for the
6138 * widget interferes with the normal mouse processing
6139 * for the widget. The fix is to avoid the automatic
6142 return sendMouseDownEvent (SWT.MouseDown, 3, OS.WM_RBUTTONDOWN, wParam, lParam);
6146 LRESULT WM_SETFOCUS (long wParam, long lParam) {
6147 LRESULT result = super.WM_SETFOCUS (wParam, lParam);
6149 * Bug in Windows. When focus is gained after the
6150 * selection has been changed using LVM_SETITEMSTATE,
6151 * Windows redraws the selected text but does not
6152 * redraw the image or the check box, leaving them
6153 * appearing unselected. The fix is to redraw
6156 if (imageList != null || (style & SWT.CHECK) != 0) {
6157 OS.InvalidateRect (handle, null, false);
6161 * Bug in Windows. For some reason, the table does
6162 * not set the default focus rectangle to be the first
6163 * item in the table when it gets focus and there is
6164 * no selected item. The fix to make the first item
6165 * be the focus item.
6167 int count = (int)OS.SendMessage (handle, OS.LVM_GETITEMCOUNT, 0, 0);
6168 if (count == 0) return result;
6169 int index = (int)OS.SendMessage (handle, OS.LVM_GETNEXTITEM, -1, OS.LVNI_FOCUSED);
6171 LVITEM lvItem = new LVITEM ();
6172 lvItem.state = OS.LVIS_FOCUSED;
6173 lvItem.stateMask = OS.LVIS_FOCUSED;
6174 ignoreSelect = true;
6175 OS.SendMessage (handle, OS.LVM_SETITEMSTATE, 0, lvItem);
6176 ignoreSelect = false;
6182 LRESULT WM_SETFONT (long wParam, long lParam) {
6183 LRESULT result = super.WM_SETFONT (wParam, lParam);
6184 if (result != null) return result;
6187 * Bug in Windows. When a header has a sort indicator
6188 * triangle, Windows resizes the indicator based on the
6189 * size of the n-1th font. The fix is to always make
6190 * the n-1th font be the default. This makes the sort
6191 * indicator always be the default size.
6193 * NOTE: The table window proc sets the actual font in
6194 * the header so that all that is necessary here is to
6195 * set the default first.
6197 OS.SendMessage (hwndHeader, OS.WM_SETFONT, 0, lParam);
6199 if (headerToolTipHandle != 0) {
6200 OS.SendMessage (headerToolTipHandle, OS.WM_SETFONT, wParam, lParam);
6206 LRESULT WM_SETREDRAW (long wParam, long lParam) {
6207 LRESULT result = super.WM_SETREDRAW (wParam, lParam);
6208 if (result != null) return result;
6210 * Feature in Windows. When LVM_SETBKCOLOR is used with CLR_NONE
6211 * to make the background of the table transparent, drawing becomes
6212 * slow. The fix is to temporarily clear CLR_NONE when redraw is
6216 if ((int)OS.SendMessage (handle, OS.LVM_GETBKCOLOR, 0, 0) != OS.CLR_NONE) {
6217 if (hooks (SWT.MeasureItem) || hooks (SWT.EraseItem) || hooks (SWT.PaintItem)) {
6218 OS.SendMessage (handle, OS.LVM_SETBKCOLOR, 0, OS.CLR_NONE);
6223 * Bug in Windows. When WM_SETREDRAW is used to turn off
6224 * redraw for a list, table or tree, the background of the
6225 * control is drawn. The fix is to call DefWindowProc(),
6226 * which stops all graphics output to the control.
6228 OS.DefWindowProc (handle, OS.WM_SETREDRAW, wParam, lParam);
6229 long code = callWindowProc (handle, OS.WM_SETREDRAW, wParam, lParam);
6231 if ((int)OS.SendMessage (handle, OS.LVM_GETBKCOLOR, 0, 0) == OS.CLR_NONE) {
6232 OS.SendMessage (handle, OS.LVM_SETBKCOLOR, 0, 0xFFFFFF);
6235 return code == 0 ? LRESULT.ZERO : new LRESULT (code);
6239 LRESULT WM_SIZE (long wParam, long lParam) {
6240 if (ignoreResize) return null;
6241 if (hooks (SWT.EraseItem) || hooks (SWT.PaintItem)) {
6242 OS.InvalidateRect (handle, null, true);
6244 if (resizeCount != 0) {
6248 return super.WM_SIZE (wParam, lParam);
6252 LRESULT WM_SYSCOLORCHANGE (long wParam, long lParam) {
6253 LRESULT result = super.WM_SYSCOLORCHANGE (wParam, lParam);
6254 if (result != null) return result;
6255 if (findBackgroundControl () == null) {
6256 setBackgroundPixel (defaultBackground ());
6258 int oldPixel = (int)OS.SendMessage (handle, OS.LVM_GETBKCOLOR, 0, 0);
6259 if (oldPixel != OS.CLR_NONE) {
6260 if (findImageControl () == null) {
6261 if ((style & SWT.CHECK) != 0) fixCheckboxImageListColor (true);
6269 LRESULT WM_HSCROLL (long wParam, long lParam) {
6271 * Bug in Windows. When a table that is drawing grid lines
6272 * is slowly scrolled horizontally to the left, the table does
6273 * not redraw the newly exposed vertical grid lines. The fix
6274 * is to save the old scroll position, call the window proc,
6275 * get the new scroll position and redraw the new area.
6278 if (_getLinesVisible()) {
6279 SCROLLINFO info = new SCROLLINFO ();
6280 info.cbSize = SCROLLINFO.sizeof;
6281 info.fMask = OS.SIF_POS;
6282 OS.GetScrollInfo (handle, OS.SB_HORZ, info);
6287 * Feature in Windows. When there are many columns in a table,
6288 * scrolling performance can be improved by unsubclassing the
6289 * window proc so that internal messages are dispatched directly
6290 * to the table. If the application expects to see a paint event
6291 * or has a child whose font, foreground or background color might
6292 * be needed, the window proc cannot be unsubclassed
6294 * NOTE: The header tooltip can subclass the header proc so the
6295 * current proc must be restored or header tooltips stop working.
6297 long oldHeaderProc = 0, oldTableProc = 0;
6298 long hwndHeader = OS.SendMessage (handle, OS.LVM_GETHEADER, 0, 0);
6299 boolean fixSubclass = isOptimizedRedraw ();
6301 oldTableProc = OS.SetWindowLongPtr (handle, OS.GWLP_WNDPROC, TableProc);
6302 oldHeaderProc = OS.SetWindowLongPtr (hwndHeader, OS.GWLP_WNDPROC, HeaderProc);
6306 * Feature in Windows. For some reason, when the table window
6307 * proc processes WM_HSCROLL or WM_VSCROLL when there are many
6308 * columns in the table, scrolling is slow and the table does
6309 * not keep up with the position of the scroll bar. The fix
6310 * is to turn off redraw, scroll, turn redraw back on and redraw
6311 * the entire table. Strangly, redrawing the entire table is
6314 boolean fixScroll = false;
6315 if (OS.LOWORD (wParam) != OS.SB_ENDSCROLL) {
6316 if (columnCount > H_SCROLL_LIMIT) {
6317 int rowCount = (int)OS.SendMessage (handle, OS.LVM_GETCOUNTPERPAGE, 0, 0);
6318 if (rowCount > V_SCROLL_LIMIT) fixScroll = getDrawing () && OS.IsWindowVisible (handle);
6321 if (fixScroll) OS.DefWindowProc (handle, OS.WM_SETREDRAW, 0, 0);
6322 LRESULT result = super.WM_HSCROLL (wParam, lParam);
6324 OS.DefWindowProc (handle, OS.WM_SETREDRAW, 1, 0);
6325 int flags = OS.RDW_ERASE | OS.RDW_FRAME | OS.RDW_INVALIDATE | OS.RDW_ALLCHILDREN;
6326 OS.RedrawWindow (handle, null, 0, flags);
6328 * Feature in Windows. On Vista only, it is faster to
6329 * compute and answer the data for the visible columns
6330 * of a table when scrolling, rather than just return
6331 * the data for each column when asked.
6333 if (OS.WIN32_VERSION >= OS.VERSION (6, 0)) {
6334 RECT headerRect = new RECT (), rect = new RECT ();
6335 OS.GetClientRect (handle, rect);
6336 boolean [] visible = new boolean [columnCount];
6337 for (int i=0; i<columnCount; i++) {
6340 headerRect.left = OS.LVIR_BOUNDS;
6341 if (OS.SendMessage (handle, OS.LVM_GETSUBITEMRECT, 0, headerRect) != 0) {
6342 headerRect.top = rect.top;
6343 headerRect.bottom = rect.bottom;
6344 visible [i] = OS.IntersectRect(headerRect, rect, headerRect);
6348 columnVisible = visible;
6349 OS.UpdateWindow (handle);
6351 columnVisible = null;
6357 OS.SetWindowLongPtr (handle, OS.GWLP_WNDPROC, oldTableProc);
6358 OS.SetWindowLongPtr (hwndHeader, OS.GWLP_WNDPROC, oldHeaderProc);
6362 * Bug in Windows. When a table that is drawing grid lines
6363 * is slowly scrolled horizontally to the left, the table does
6364 * not redraw the newly exposed vertical grid lines. The fix
6365 * is to save the old scroll position, call the window proc,
6366 * get the new scroll position and redraw the new area.
6368 if (_getLinesVisible()) {
6369 SCROLLINFO info = new SCROLLINFO ();
6370 info.cbSize = SCROLLINFO.sizeof;
6371 info.fMask = OS.SIF_POS;
6372 OS.GetScrollInfo (handle, OS.SB_HORZ, info);
6373 int newPos = info.nPos;
6374 if (newPos < oldPos) {
6375 RECT rect = new RECT ();
6376 OS.GetClientRect (handle, rect);
6377 rect.right = oldPos - newPos + GRID_WIDTH;
6378 OS.InvalidateRect (handle, rect, true);
6385 LRESULT WM_VSCROLL (long wParam, long lParam) {
6387 * When there are many columns in a table, scrolling performance
6388 * can be improved by temporarily unsubclassing the window proc
6389 * so that internal messages are dispatched directly to the table.
6390 * If the application expects to see a paint event or has a child
6391 * whose font, foreground or background color might be needed,
6392 * the window proc cannot be unsubclassed.
6394 * NOTE: The header tooltip can subclass the header proc so the
6395 * current proc must be restored or header tooltips stop working.
6397 long oldHeaderProc = 0, oldTableProc = 0;
6398 long hwndHeader = OS.SendMessage (handle, OS.LVM_GETHEADER, 0, 0);
6399 boolean fixSubclass = isOptimizedRedraw ();
6401 oldTableProc = OS.SetWindowLongPtr (handle, OS.GWLP_WNDPROC, TableProc);
6402 oldHeaderProc = OS.SetWindowLongPtr (hwndHeader, OS.GWLP_WNDPROC, HeaderProc);
6406 * Feature in Windows. For some reason, when the table window
6407 * proc processes WM_HSCROLL or WM_VSCROLL when there are many
6408 * columns in the table, scrolling is slow and the table does
6409 * not keep up with the position of the scroll bar. The fix
6410 * is to turn off redraw, scroll, turn redraw back on and redraw
6411 * the entire table. Strangly, redrawing the entire table is
6414 boolean fixScroll = false;
6415 if (OS.LOWORD (wParam) != OS.SB_ENDSCROLL) {
6416 if (columnCount > H_SCROLL_LIMIT) {
6417 int rowCount = (int)OS.SendMessage (handle, OS.LVM_GETCOUNTPERPAGE, 0, 0);
6418 if (rowCount > V_SCROLL_LIMIT) fixScroll = getDrawing () && OS.IsWindowVisible (handle);
6421 if (fixScroll) OS.DefWindowProc (handle, OS.WM_SETREDRAW, 0, 0);
6422 LRESULT result = super.WM_VSCROLL (wParam, lParam);
6424 OS.DefWindowProc (handle, OS.WM_SETREDRAW, 1, 0);
6425 int flags = OS.RDW_ERASE | OS.RDW_FRAME | OS.RDW_INVALIDATE | OS.RDW_ALLCHILDREN;
6426 OS.RedrawWindow (handle, null, 0, flags);
6428 * Feature in Windows. On Vista only, it is faster to
6429 * compute and answer the data for the visible columns
6430 * of a table when scrolling, rather than just return
6431 * the data for each column when asked.
6433 if (OS.WIN32_VERSION >= OS.VERSION (6, 0)) {
6434 RECT headerRect = new RECT (), rect = new RECT ();
6435 OS.GetClientRect (handle, rect);
6436 boolean [] visible = new boolean [columnCount];
6437 for (int i=0; i<columnCount; i++) {
6440 headerRect.left = OS.LVIR_BOUNDS;
6441 if (OS.SendMessage (handle, OS.LVM_GETSUBITEMRECT, 0, headerRect) != 0) {
6442 headerRect.top = rect.top;
6443 headerRect.bottom = rect.bottom;
6444 visible [i] = OS.IntersectRect(headerRect, rect, headerRect);
6448 columnVisible = visible;
6449 OS.UpdateWindow (handle);
6451 columnVisible = null;
6457 OS.SetWindowLongPtr (handle, OS.GWLP_WNDPROC, oldTableProc);
6458 OS.SetWindowLongPtr (hwndHeader, OS.GWLP_WNDPROC, oldHeaderProc);
6462 * Bug in Windows. When a table is drawing grid lines and the
6463 * user scrolls vertically up or down by a line or a page, the
6464 * table does not redraw the grid lines for newly exposed items.
6465 * The fix is to invalidate the items.
6467 if (_getLinesVisible()) {
6468 int code = OS.LOWORD (wParam);
6470 case OS.SB_ENDSCROLL:
6471 case OS.SB_THUMBPOSITION:
6472 case OS.SB_THUMBTRACK:
6476 case OS.SB_LINEDOWN:
6478 RECT rect = new RECT ();
6479 OS.GetWindowRect (hwndHeader, rect);
6480 int headerHeight = rect.bottom - rect.top;
6481 RECT clientRect = new RECT ();
6482 OS.GetClientRect (handle, clientRect);
6483 clientRect.top += headerHeight;
6484 long empty = OS.SendMessage (handle, OS.LVM_APPROXIMATEVIEWRECT, 0, 0);
6485 long oneItem = OS.SendMessage (handle, OS.LVM_APPROXIMATEVIEWRECT, 1, 0);
6486 int itemHeight = OS.HIWORD (oneItem) - OS.HIWORD (empty);
6487 if (code == OS.SB_LINEDOWN) {
6488 clientRect.top = clientRect.bottom - itemHeight - GRID_WIDTH;
6490 clientRect.bottom = clientRect.top + itemHeight + GRID_WIDTH;
6492 OS.InvalidateRect (handle, clientRect, true);
6494 case OS.SB_PAGEDOWN:
6496 OS.InvalidateRect (handle, null, true);
6504 LRESULT wmMeasureChild (long wParam, long lParam) {
6505 MEASUREITEMSTRUCT struct = new MEASUREITEMSTRUCT ();
6506 OS.MoveMemory (struct, lParam, MEASUREITEMSTRUCT.sizeof);
6507 if (itemHeight == -1) {
6508 long empty = OS.SendMessage (handle, OS.LVM_APPROXIMATEVIEWRECT, 0, 0);
6509 long oneItem = OS.SendMessage (handle, OS.LVM_APPROXIMATEVIEWRECT, 1, 0);
6510 struct.itemHeight = OS.HIWORD (oneItem) - OS.HIWORD (empty);
6512 struct.itemHeight = itemHeight;
6514 OS.MoveMemory (lParam, struct, MEASUREITEMSTRUCT.sizeof);
6519 LRESULT wmNotify (NMHDR hdr, long wParam, long lParam) {
6520 long hwndToolTip = OS.SendMessage (handle, OS.LVM_GETTOOLTIPS, 0, 0);
6521 if (hdr.hwndFrom == hwndToolTip) {
6522 LRESULT result = wmNotifyToolTip (hdr, wParam, lParam);
6523 if (result != null) return result;
6525 if (hdr.hwndFrom == hwndHeader) {
6526 LRESULT result = wmNotifyHeader (hdr, wParam, lParam);
6527 if (result != null) return result;
6529 return super.wmNotify (hdr, wParam, lParam);
6533 LRESULT wmNotifyChild (NMHDR hdr, long wParam, long lParam) {
6535 case OS.LVN_ODFINDITEM: {
6536 if ((style & SWT.VIRTUAL) != 0) return new LRESULT (-1);
6539 case OS.LVN_ODSTATECHANGED: {
6540 if ((style & SWT.VIRTUAL) != 0) {
6541 if (!ignoreSelect) {
6542 NMLVODSTATECHANGE lpStateChange = new NMLVODSTATECHANGE ();
6543 OS.MoveMemory (lpStateChange, lParam, NMLVODSTATECHANGE.sizeof);
6544 boolean oldSelected = (lpStateChange.uOldState & OS.LVIS_SELECTED) != 0;
6545 boolean newSelected = (lpStateChange.uNewState & OS.LVIS_SELECTED) != 0;
6546 if (oldSelected != newSelected) wasSelected = true;
6551 case OS.LVN_GETDISPINFO: {
6552 // if (drawCount != 0 || !OS.IsWindowVisible (handle)) break;
6553 NMLVDISPINFO plvfi = new NMLVDISPINFO ();
6554 OS.MoveMemory (plvfi, lParam, NMLVDISPINFO.sizeof);
6556 if (columnVisible != null && !columnVisible [plvfi.iSubItem]) {
6561 * Feature in Windows. When a new table item is inserted
6562 * using LVM_INSERTITEM in a table that is transparent
6563 * (ie. LVM_SETBKCOLOR has been called with CLR_NONE),
6564 * TVM_INSERTITEM calls LVN_GETDISPINFO before the item
6565 * has been added to the array. The fix is to check for
6568 * NOTE: Force the item to be created if it does not exist.
6570 TableItem item = _getItem (plvfi.iItem);
6571 if (item == null) break;
6574 * Feature in Windows. On Vista, the list view expects the item array
6575 * to be up to date when a LVM_DELETEITEM message is being processed.
6577 * Also, when the table is virtual, do not allow the application to
6578 * provide data for a new item that becomes visible until the item has
6579 * been removed from the items array. Because arbitrary application
6580 * code can run during the callback, the items array might be accessed
6581 * in an inconsistent state.
6583 * On both cases, Rather than answering the data right away, queue a
6588 * Feature in Windows Vista and newer. Using LVM_REDRAWITEMS causes LVN_GETDISPINFO
6589 * to be sent before the method returns. For this reason, LVM_REDRAWITEMS
6590 * can never be used from a LVN_GETDISPINFO handler. The fix is to
6591 * InvalidateRect() passing the bounds for the entire item.
6593 if (OS.WIN32_VERSION >= OS.VERSION (6, 0)) {
6594 RECT rect = new RECT ();
6595 rect.left = OS.LVIR_BOUNDS;
6596 ignoreCustomDraw = true;
6597 long code = OS.SendMessage (handle, OS. LVM_GETITEMRECT, plvfi.iItem, rect);
6598 ignoreCustomDraw = false;
6599 if (code != 0) OS.InvalidateRect (handle, rect, true);
6602 if ((style & SWT.VIRTUAL) != 0 && !item.cached) {
6603 OS.SendMessage (handle, OS.LVM_REDRAWITEMS, plvfi.iItem, plvfi.iItem);
6610 * The cached flag is used by both virtual and non-virtual
6611 * tables to indicate that Windows has asked at least once
6615 if ((style & SWT.VIRTUAL) != 0) {
6616 lastIndexOf = plvfi.iItem;
6617 if (!checkData (item, lastIndexOf, false)) break;
6618 TableItem newItem = fixScrollWidth ? null : item;
6619 if (setScrollWidth (newItem, true)) {
6620 OS.InvalidateRect (handle, null, true);
6625 if ((plvfi.mask & OS.LVIF_TEXT) != 0) {
6626 String string = null;
6627 if (plvfi.iSubItem == 0) {
6630 String [] strings = item.strings;
6631 if (strings != null && plvfi.iSubItem < strings.length) string = strings [plvfi.iSubItem];
6633 if (string != null) {
6635 * Bug in Windows. When pszText points to a zero length
6636 * NULL terminated string, Windows correctly draws the
6637 * empty string but the cache of the bounds for the item
6638 * is not reset. This means that when the text for the
6639 * item is set and then reset to an empty string, the
6640 * selection draws using the bounds of the previous text.
6641 * The fix is to use a space rather than an empty string
6642 * when anything but a tool tip is requested (to avoid
6643 * a tool tip that is a single space).
6645 * NOTE: This is only a problem for items in the first
6646 * column. Assigning NULL to other columns stops Windows
6647 * from drawing the selection when LVS_EX_FULLROWSELECT
6650 int length = Math.min (string.length (), Math.max (0, plvfi.cchTextMax - 1));
6651 if (!tipRequested && plvfi.iSubItem == 0 && length == 0) {
6652 string = " "; //$NON-NLS-1$
6655 if (length > 1 && (state & HAS_AUTO_DIRECTION) != 0) {
6656 switch (BidiUtil.resolveTextDirection(string)) {
6657 case SWT.LEFT_TO_RIGHT:
6658 string = LRE + string;
6661 case SWT.RIGHT_TO_LEFT:
6662 string = RLE + string;
6667 char [] buffer = display.tableBuffer;
6668 if (buffer == null || plvfi.cchTextMax > buffer.length) {
6669 buffer = display.tableBuffer = new char [plvfi.cchTextMax];
6671 string.getChars (0, length, buffer, 0);
6674 * Bug in Windows. The tooltip is only displayed up to
6675 * the first line delimiter. The fix is to remove all
6676 * line delimiter characters.
6679 for (int i = 0; i < length; i++) {
6680 switch (buffer [i]) {
6686 if (shift != 0) buffer [i - shift] = buffer [i];
6691 buffer [length++] = 0;
6692 OS.MoveMemory (plvfi.pszText, buffer, length * 2);
6695 boolean move = false;
6696 if ((plvfi.mask & OS.LVIF_IMAGE) != 0) {
6698 if (plvfi.iSubItem == 0) {
6701 Image [] images = item.images;
6702 if (images != null && plvfi.iSubItem < images.length) image = images [plvfi.iSubItem];
6704 if (image != null) {
6705 plvfi.iImage = imageIndex (image, plvfi.iSubItem);
6709 if ((plvfi.mask & OS.LVIF_STATE) != 0) {
6710 if (plvfi.iSubItem == 0) {
6712 if (item.checked) state++;
6713 if (item.grayed) state +=2;
6714 if (!OS.IsWindowEnabled (handle)) state += 4;
6715 plvfi.state = state << 12;
6716 plvfi.stateMask = OS.LVIS_STATEIMAGEMASK;
6720 if ((plvfi.mask & OS.LVIF_INDENT) != 0) {
6721 if (plvfi.iSubItem == 0) {
6722 plvfi.iIndent = item.imageIndent;
6726 if (move) OS.MoveMemory (lParam, plvfi, NMLVDISPINFO.sizeof);
6729 case OS.NM_CUSTOMDRAW: {
6730 long hwndHeader = OS.SendMessage (handle, OS.LVM_GETHEADER, 0, 0);
6731 if (hdr.hwndFrom == hwndHeader) break;
6732 if (!customDraw && findImageControl () == null) {
6734 * Feature in Windows. When the table is disabled, it draws
6735 * with a gray background but does not gray the text. The fix
6736 * is to explicitly gray the text using Custom Draw.
6738 if (OS.IsWindowEnabled (handle)) {
6740 * Feature in Windows. On Vista using the explorer theme,
6741 * Windows draws a vertical line to separate columns. When
6742 * there is only a single column, the line looks strange.
6743 * The fix is to draw the background using custom draw.
6745 if (!explorerTheme || columnCount != 0) break;
6748 NMLVCUSTOMDRAW nmcd = new NMLVCUSTOMDRAW ();
6749 OS.MoveMemory (nmcd, lParam, NMLVCUSTOMDRAW.sizeof);
6750 switch (nmcd.dwDrawStage) {
6751 case OS.CDDS_PREPAINT: return CDDS_PREPAINT (nmcd, wParam, lParam);
6752 case OS.CDDS_ITEMPREPAINT: return CDDS_ITEMPREPAINT (nmcd, wParam, lParam);
6753 case OS.CDDS_ITEMPOSTPAINT: return CDDS_ITEMPOSTPAINT (nmcd, wParam, lParam);
6754 case OS.CDDS_SUBITEMPREPAINT: return CDDS_SUBITEMPREPAINT (nmcd, wParam, lParam);
6755 case OS.CDDS_SUBITEMPOSTPAINT: return CDDS_SUBITEMPOSTPAINT (nmcd, wParam, lParam);
6756 case OS.CDDS_POSTPAINT: return CDDS_POSTPAINT (nmcd, wParam, lParam);
6760 case OS.LVN_MARQUEEBEGIN: {
6761 if ((style & SWT.SINGLE) != 0) return LRESULT.ONE;
6762 if (hooks (SWT.MouseDown) || hooks (SWT.MouseUp)) {
6765 if ((style & SWT.RIGHT_TO_LEFT) != 0) {
6766 if (findImageControl () != null) return LRESULT.ONE;
6770 case OS.LVN_BEGINDRAG:
6771 case OS.LVN_BEGINRDRAG: {
6772 if (OS.GetKeyState (OS.VK_LBUTTON) >= 0) break;
6774 if (hdr.code == OS.LVN_BEGINDRAG) {
6775 int pos = OS.GetMessagePos ();
6776 POINT pt = new POINT ();
6777 OS.POINTSTOPOINT (pt, pos);
6778 OS.ScreenToClient (handle, pt);
6779 sendDragEvent (1, pt.x, pt.y);
6783 case OS.LVN_COLUMNCLICK: {
6784 NMLISTVIEW pnmlv = new NMLISTVIEW ();
6785 OS.MoveMemory(pnmlv, lParam, NMLISTVIEW.sizeof);
6786 TableColumn column = columns [pnmlv.iSubItem];
6787 if (column != null) {
6788 column.sendSelectionEvent (SWT.Selection);
6792 case OS.LVN_ITEMACTIVATE: {
6793 if (ignoreActivate) break;
6794 NMLISTVIEW pnmlv = new NMLISTVIEW ();
6795 OS.MoveMemory(pnmlv, lParam, NMLISTVIEW.sizeof);
6796 if (pnmlv.iItem != -1) {
6797 Event event = new Event ();
6798 event.item = _getItem (pnmlv.iItem);
6799 sendSelectionEvent (SWT.DefaultSelection, event, false);
6803 case OS.LVN_ITEMCHANGED: {
6804 if (fullRowSelect) {
6805 fullRowSelect = false;
6806 OS.DefWindowProc (handle, OS.WM_SETREDRAW, 1, 0);
6807 OS.SendMessage (handle, OS.LVM_SETEXTENDEDLISTVIEWSTYLE, OS.LVS_EX_FULLROWSELECT, 0);
6809 if (!ignoreSelect) {
6810 NMLISTVIEW pnmlv = new NMLISTVIEW ();
6811 OS.MoveMemory (pnmlv, lParam, NMLISTVIEW.sizeof);
6812 if ((pnmlv.uChanged & OS.LVIF_STATE) != 0) {
6813 if (pnmlv.iItem == -1) {
6816 boolean oldSelected = (pnmlv.uOldState & OS.LVIS_SELECTED) != 0;
6817 boolean newSelected = (pnmlv.uNewState & OS.LVIS_SELECTED) != 0;
6818 if (oldSelected != newSelected) wasSelected = true;
6822 if (hooks (SWT.EraseItem) || hooks (SWT.PaintItem)) {
6823 long hwndHeader = OS.SendMessage (handle, OS.LVM_GETHEADER, 0, 0);
6824 int count = (int)OS.SendMessage (hwndHeader, OS.HDM_GETITEMCOUNT, 0, 0);
6827 RECT rect = new RECT ();
6828 OS.GetClientRect (handle, rect);
6829 NMLISTVIEW pnmlv = new NMLISTVIEW ();
6830 OS.MoveMemory (pnmlv, lParam, NMLISTVIEW.sizeof);
6831 if (pnmlv.iItem != -1) {
6832 RECT itemRect = new RECT ();
6833 itemRect.left = OS.LVIR_BOUNDS;
6834 ignoreCustomDraw = true;
6835 OS.SendMessage (handle, OS. LVM_GETITEMRECT, pnmlv.iItem, itemRect);
6836 ignoreCustomDraw = false;
6837 RECT headerRect = new RECT ();
6838 int index = (int)OS.SendMessage (hwndHeader, OS.HDM_ORDERTOINDEX, count - 1, 0);
6839 OS.SendMessage (hwndHeader, OS.HDM_GETITEMRECT, index, headerRect);
6840 OS.MapWindowPoints (hwndHeader, handle, headerRect, 2);
6841 rect.left = headerRect.right;
6842 rect.top = itemRect.top;
6843 rect.bottom = itemRect.bottom;
6844 OS.InvalidateRect (handle, rect, true);
6851 return super.wmNotifyChild (hdr, wParam, lParam);
6854 LRESULT wmNotifyHeader (NMHDR hdr, long wParam, long lParam) {
6856 * Feature in Windows. On NT, the automatically created
6857 * header control is created as a UNICODE window, not an
6858 * ANSI window despite the fact that the parent is created
6859 * as an ANSI window. This means that it sends UNICODE
6860 * notification messages to the parent window on NT for
6861 * no good reason. The data and size in the NMHEADER and
6862 * HDITEM structs is identical between the platforms so no
6863 * different message is actually necessary. Despite this,
6864 * Windows sends different messages. The fix is to look
6865 * for both messages, despite the platform. This works
6866 * because only one will be sent on either platform, never
6870 case OS.HDN_BEGINTRACK:
6871 case OS.HDN_DIVIDERDBLCLICK: {
6872 if (columnCount == 0) return LRESULT.ONE;
6873 NMHEADER phdn = new NMHEADER ();
6874 OS.MoveMemory (phdn, lParam, NMHEADER.sizeof);
6875 TableColumn column = columns [phdn.iItem];
6876 if (column != null && !column.getResizable ()) {
6879 ignoreColumnMove = true;
6880 if (hdr.code == OS.HDN_DIVIDERDBLCLICK) {
6881 if (column != null && hooks (SWT.MeasureItem)) {
6888 case OS.NM_CUSTOMDRAW: {
6889 NMCUSTOMDRAW nmcd = new NMCUSTOMDRAW();
6890 OS.MoveMemory(nmcd, lParam, NMCUSTOMDRAW.sizeof);
6891 switch (nmcd.dwDrawStage) {
6892 case OS.CDDS_PREPAINT: {
6893 /* Drawing here will be deleted by further drawing steps, even with OS.CDRF_SKIPDEFAULT.
6894 Changing the TextColor and returning OS.CDRF_NEWFONT has no effect. */
6895 return new LRESULT (customHeaderDrawing() ? OS.CDRF_NOTIFYITEMDRAW | OS.CDRF_NOTIFYPOSTPAINT : OS.CDRF_DODEFAULT);
6897 case OS.CDDS_ITEMPREPAINT: {
6899 RECT rect = new RECT();
6900 OS.SetRect(rect, nmcd.left, nmcd.top, nmcd.right, nmcd.bottom);
6901 int pixel = getHeaderBackgroundPixel();
6902 if ((nmcd.uItemState & OS.CDIS_SELECTED) != 0) {
6903 pixel = getDifferentColor(pixel);
6904 } else if (columns[(int) nmcd.dwItemSpec] == sortColumn && sortDirection != SWT.NONE) {
6905 pixel = getSlightlyDifferentColor(pixel);
6907 long brush = OS.CreateSolidBrush(pixel);
6908 OS.FillRect(nmcd.hdc, rect, brush);
6909 OS.DeleteObject(brush);
6911 return new LRESULT(OS.CDRF_SKIPDEFAULT); // if we got here, we will paint everything ourself
6913 case OS.CDDS_POSTPAINT: {
6914 // get the cursor position
6915 POINT cursorPos = new POINT();
6916 OS.GetCursorPos(cursorPos);
6917 OS.MapWindowPoints(0, hwndHeader, cursorPos, 1);
6919 // drawing all cells
6920 int highlightedHeaderDividerX = -1;
6921 int lastColumnRight = -1;
6922 RECT [] rects = new RECT [columnCount];
6923 for (int i=0; i<columnCount; i++) {
6924 rects [i] = new RECT ();
6925 OS.SendMessage (hwndHeader, OS.HDM_GETITEMRECT, i, rects [i]);
6926 if (rects[i].right > lastColumnRight) {
6927 lastColumnRight = rects[i].right;
6930 if (columns[i] == sortColumn && sortDirection != SWT.NONE) {
6931 // the display.getSortImage looks terrible after scaling up.
6932 long pen = OS.CreatePen (OS.PS_SOLID, 1, getHeaderForegroundPixel());
6933 long oldPen = OS.SelectObject (nmcd.hdc, pen);
6934 int center = rects[i].left + (rects[i].right - rects[i].left) / 2;
6936 * Sort indicator size needs to scale as per the Native Windows OS DPI level
6937 * when header is custom drawn. For more details refer bug 537097.
6939 int leg = DPIUtil.autoScaleUpUsingNativeDPI(3);
6940 if (sortDirection == SWT.UP) {
6941 OS.Polyline(nmcd.hdc, new int[] {center-leg, 1+leg, center+1, 0}, 2);
6942 OS.Polyline(nmcd.hdc, new int[] {center+leg, 1+leg, center-1, 0}, 2);
6943 } else if (sortDirection == SWT.DOWN) {
6944 OS.Polyline(nmcd.hdc, new int[] {center-leg, 1, center+1, 1+leg+1}, 2);
6945 OS.Polyline(nmcd.hdc, new int[] {center+leg, 1, center-1, 1+leg+1}, 2);
6947 OS.SelectObject (nmcd.hdc, oldPen);
6948 OS.DeleteObject (pen);
6951 /* Windows 7 and 10 always draw a nearly invisible vertical line between the columns, even if lines are disabled.
6952 This line uses no fixed color constant, but calculates it from the background color.
6953 The method getSlightlyDifferentColor gives us a color, that is near enough to the windows algorithm. */
6954 long pen = OS.CreatePen (OS.PS_SOLID, getGridLineWidthInPixels(), getSlightlyDifferentColor(getHeaderBackgroundPixel()));
6955 long oldPen = OS.SelectObject (nmcd.hdc, pen);
6956 int alignmentCorrection = _getLinesVisible () ? 0 : 1;
6957 OS.Polyline(nmcd.hdc, new int[] {rects[i].right-alignmentCorrection, rects[i].top, rects[i].right-alignmentCorrection, rects[i].bottom}, 2);
6958 OS.SelectObject (nmcd.hdc, oldPen);
6959 OS.DeleteObject (pen);
6961 pen = OS.CreatePen (OS.PS_SOLID, getGridLineWidthInPixels(), OS.GetSysColor(OS.COLOR_3DFACE));
6962 oldPen = OS.SelectObject (nmcd.hdc, pen);
6963 /* To differentiate headers, always draw header column separator. */
6964 OS.Polyline(nmcd.hdc, new int[] {rects[i].right - alignmentCorrection, rects[i].top, rects[i].right - alignmentCorrection, rects[i].bottom}, 2);
6965 /* To differentiate header & content area, always draw the line separator between header & first row. */
6966 if (i == 0) OS.Polyline(nmcd.hdc, new int[] {nmcd.left, nmcd.bottom-1, nmcd.right+1, nmcd.bottom-1}, 2);
6967 OS.SelectObject (nmcd.hdc, oldPen);
6968 OS.DeleteObject (pen);
6970 if (headerItemDragging && highlightedHeaderDividerX == -1) {
6971 int distanceToLeftBorder = cursorPos.x - rects[i].left;
6972 int distanceToRightBorder = rects[i].right - cursorPos.x;
6973 if (distanceToLeftBorder >= 0 && distanceToRightBorder >= 0) {
6974 // the cursor is in the current rectangle
6975 highlightedHeaderDividerX = distanceToLeftBorder <= distanceToRightBorder ? rects[i].left-1 : rects[i].right;
6979 int x = rects[i].left + INSET + 2;
6980 if (columns[i].image != null) {
6981 GCData data = new GCData();
6982 data.device = display;
6983 GC gc = GC.win32_new (nmcd.hdc, data);
6984 int y = Math.max (0, (nmcd.bottom - columns[i].image.getBoundsInPixels().height) / 2);
6985 gc.drawImage (columns[i].image, DPIUtil.autoScaleDown(x), DPIUtil.autoScaleDown(y));
6986 x += columns[i].image.getBoundsInPixels().width + 12;
6990 if (columns[i].text != null) {
6991 int flags = OS.DT_NOPREFIX | OS.DT_SINGLELINE | OS.DT_VCENTER;
6992 if ((columns[i].style & SWT.CENTER) != 0) flags |= OS.DT_CENTER;
6993 if ((columns[i].style & SWT.RIGHT) != 0) flags |= OS.DT_RIGHT;
6994 char [] buffer = columns[i].text.toCharArray ();
6995 OS.SetBkMode(nmcd.hdc, OS.TRANSPARENT);
6996 OS.SetTextColor(nmcd.hdc, getHeaderForegroundPixel());
6997 RECT textRect = new RECT();
6999 textRect.top = rects[i].top;
7000 textRect.right = rects[i].right - (x - rects[i].left);
7001 textRect.bottom = rects[i].bottom;
7002 OS.DrawText (nmcd.hdc, buffer, buffer.length, textRect, flags);
7006 if (lastColumnRight < nmcd.right) {
7007 // draw background of the 'no column' area
7008 RECT rect = new RECT();
7009 lastColumnRight += _getLinesVisible() ? 1 : 0;
7010 OS.SetRect(rect, lastColumnRight, nmcd.top, nmcd.right, nmcd.bottom-1);
7011 long brush = OS.CreateSolidBrush(getHeaderBackgroundPixel());
7012 OS.FillRect(nmcd.hdc, rect, brush);
7013 OS.DeleteObject(brush);
7016 // always draw the highlighted border at the end, to avoid overdrawing by other borders.
7017 if (highlightedHeaderDividerX != -1) {
7018 long pen = OS.CreatePen (OS.PS_SOLID, 4, OS.GetSysColor(OS.COLOR_HIGHLIGHT));
7019 long oldPen = OS.SelectObject (nmcd.hdc, pen);
7020 OS.Polyline(nmcd.hdc, new int[] {highlightedHeaderDividerX, nmcd.top, highlightedHeaderDividerX, nmcd.bottom}, 2);
7021 OS.SelectObject (nmcd.hdc, oldPen);
7022 OS.DeleteObject (pen);
7025 return new LRESULT(OS.CDRF_DODEFAULT);
7030 case OS.NM_RELEASEDCAPTURE: {
7031 if (!ignoreColumnMove) {
7032 for (int i=0; i<columnCount; i++) {
7033 TableColumn column = columns [i];
7034 column.updateToolTip (i);
7037 ignoreColumnMove = false;
7040 case OS.HDN_BEGINDRAG: {
7041 if (ignoreColumnMove) return LRESULT.ONE;
7042 int bits = (int)OS.SendMessage (handle, OS.LVM_GETEXTENDEDLISTVIEWSTYLE, 0, 0);
7043 if ((bits & OS.LVS_EX_HEADERDRAGDROP) != 0) {
7044 if (columnCount == 0) return LRESULT.ONE;
7045 NMHEADER phdn = new NMHEADER ();
7046 OS.MoveMemory (phdn, lParam, NMHEADER.sizeof);
7047 if (phdn.iItem != -1) {
7048 TableColumn column = columns [phdn.iItem];
7049 if (column != null && !column.getMoveable ()) {
7050 ignoreColumnMove = true;
7054 headerItemDragging = true;
7058 case OS.HDN_ENDDRAG: {
7059 headerItemDragging = false;
7060 int bits = (int)OS.SendMessage (handle, OS.LVM_GETEXTENDEDLISTVIEWSTYLE, 0, 0);
7061 if ((bits & OS.LVS_EX_HEADERDRAGDROP) == 0) break;
7062 NMHEADER phdn = new NMHEADER ();
7063 OS.MoveMemory (phdn, lParam, NMHEADER.sizeof);
7064 if (phdn.iItem != -1 && phdn.pitem != 0) {
7065 HDITEM pitem = new HDITEM ();
7066 OS.MoveMemory (pitem, phdn.pitem, HDITEM.sizeof);
7067 if ((pitem.mask & OS.HDI_ORDER) != 0 && pitem.iOrder != -1) {
7068 if (columnCount == 0) break;
7069 int [] order = new int [columnCount];
7070 OS.SendMessage (handle, OS.LVM_GETCOLUMNORDERARRAY, columnCount, order);
7072 while (index < order.length) {
7073 if (order [index] == phdn.iItem) break;
7076 if (index == order.length) index = 0;
7077 if (index == pitem.iOrder) break;
7078 int start = Math.min (index, pitem.iOrder);
7079 int end = Math.max (index, pitem.iOrder);
7080 ignoreColumnMove = false;
7081 for (int i=start; i<=end; i++) {
7082 TableColumn column = columns [order [i]];
7083 if (!column.isDisposed ()) {
7084 column.postEvent (SWT.Move);
7091 case OS.HDN_ITEMCHANGED: {
7093 * Bug in Windows. When a table has the LVS_EX_GRIDLINES extended
7094 * style and the user drags any column over the first column in the
7095 * table, making the size become zero, when the user drags a column
7096 * such that the size of the first column becomes non-zero, the grid
7097 * lines are not redrawn. The fix is to detect the case and force
7098 * a redraw of the first column.
7100 int width = (int)OS.SendMessage (handle, OS.LVM_GETCOLUMNWIDTH, 0, 0);
7101 if (lastWidth == 0 && width > 0) {
7102 if (_getLinesVisible()) {
7103 RECT rect = new RECT ();
7104 OS.GetClientRect (handle, rect);
7105 rect.right = rect.left + width;
7106 OS.InvalidateRect (handle, rect, true);
7110 if (!ignoreColumnResize) {
7111 NMHEADER phdn = new NMHEADER ();
7112 OS.MoveMemory (phdn, lParam, NMHEADER.sizeof);
7113 if (phdn.pitem != 0) {
7114 HDITEM pitem = new HDITEM ();
7115 OS.MoveMemory (pitem, phdn.pitem, HDITEM.sizeof);
7116 if ((pitem.mask & OS.HDI_WIDTH) != 0) {
7117 TableColumn column = columns [phdn.iItem];
7118 if (column != null) {
7119 column.updateToolTip (phdn.iItem);
7120 column.sendEvent (SWT.Resize);
7121 if (isDisposed ()) return LRESULT.ZERO;
7123 * It is possible (but unlikely), that application
7124 * code could have disposed the column in the move
7125 * event. If this happens, process the move event
7126 * for those columns that have not been destroyed.
7128 TableColumn [] newColumns = new TableColumn [columnCount];
7129 System.arraycopy (columns, 0, newColumns, 0, columnCount);
7130 int [] order = new int [columnCount];
7131 OS.SendMessage (handle, OS.LVM_GETCOLUMNORDERARRAY, columnCount, order);
7132 boolean moved = false;
7133 for (int i=0; i<columnCount; i++) {
7134 TableColumn nextColumn = newColumns [order [i]];
7135 if (moved && !nextColumn.isDisposed ()) {
7136 nextColumn.updateToolTip (order [i]);
7137 nextColumn.sendEvent (SWT.Move);
7139 if (nextColumn == column) moved = true;
7147 case OS.HDN_ITEMDBLCLICK: {
7148 NMHEADER phdn = new NMHEADER ();
7149 OS.MoveMemory (phdn, lParam, NMHEADER.sizeof);
7150 TableColumn column = columns [phdn.iItem];
7151 if (column != null) {
7152 column.sendSelectionEvent (SWT.DefaultSelection);
7160 LRESULT wmNotifyToolTip (NMHDR hdr, long wParam, long lParam) {
7162 case OS.NM_CUSTOMDRAW: {
7163 if (toolTipText != null) break;
7164 if (isCustomToolTip ()) {
7165 NMTTCUSTOMDRAW nmcd = new NMTTCUSTOMDRAW ();
7166 OS.MoveMemory (nmcd, lParam, NMTTCUSTOMDRAW.sizeof);
7167 return wmNotifyToolTip (nmcd, lParam);
7171 case OS.TTN_GETDISPINFO:
7173 LRESULT result = super.wmNotify (hdr, wParam, lParam);
7174 if (result != null) return result;
7175 if (hdr.code != OS.TTN_SHOW) tipRequested = true;
7176 long code = callWindowProc (handle, OS.WM_NOTIFY, wParam, lParam);
7177 if (hdr.code != OS.TTN_SHOW) tipRequested = false;
7178 if (toolTipText != null) break;
7179 if (isCustomToolTip ()) {
7180 LVHITTESTINFO pinfo = new LVHITTESTINFO ();
7181 int pos = OS.GetMessagePos ();
7182 POINT pt = new POINT();
7183 OS.POINTSTOPOINT (pt, pos);
7184 OS.ScreenToClient (handle, pt);
7188 * Bug in Windows. When LVM_SUBITEMHITTEST is used to hittest
7189 * a point that is above the table, instead of returning -1 to
7190 * indicate that the hittest failed, a negative index is returned.
7191 * The fix is to consider any value that is negative a failure.
7193 if (OS.SendMessage (handle, OS.LVM_SUBITEMHITTEST, 0, pinfo) >= 0) {
7194 TableItem item = _getItem (pinfo.iItem);
7195 long hDC = OS.GetDC (handle);
7196 long oldFont = 0, newFont = OS.SendMessage (handle, OS.WM_GETFONT, 0, 0);
7197 if (newFont != 0) oldFont = OS.SelectObject (hDC, newFont);
7198 long hFont = item.fontHandle (pinfo.iSubItem);
7199 if (hFont != -1) hFont = OS.SelectObject (hDC, hFont);
7200 Event event = sendMeasureItemEvent (item, pinfo.iItem, pinfo.iSubItem, hDC);
7201 if (!isDisposed () && !item.isDisposed ()) {
7202 RECT itemRect = new RECT ();
7203 Rectangle boundsInPixels = event.getBoundsInPixels();
7204 OS.SetRect (itemRect, boundsInPixels.x, boundsInPixels.y, boundsInPixels.x + boundsInPixels.width, boundsInPixels.y + boundsInPixels.height);
7205 if (hdr.code == OS.TTN_SHOW) {
7206 RECT toolRect = toolTipRect (itemRect);
7207 OS.MapWindowPoints (handle, 0, toolRect, 2);
7208 long hwndToolTip = OS.SendMessage (handle, OS.LVM_GETTOOLTIPS, 0, 0);
7209 int flags = OS.SWP_NOACTIVATE | OS.SWP_NOZORDER;
7210 int width = toolRect.right - toolRect.left, height = toolRect.bottom - toolRect.top;
7211 OS.SetWindowPos (hwndToolTip, 0, toolRect.left , toolRect.top, width, height, flags);
7213 NMTTDISPINFO lpnmtdi = new NMTTDISPINFO ();
7214 OS.MoveMemory (lpnmtdi, lParam, NMTTDISPINFO.sizeof);
7215 if (lpnmtdi.lpszText != 0) {
7216 OS.MoveMemory (lpnmtdi.lpszText, new char [1], 2);
7217 OS.MoveMemory (lParam, lpnmtdi, NMTTDISPINFO.sizeof);
7219 RECT cellRect = item.getBounds (pinfo.iItem, pinfo.iSubItem, true, true, true, true, hDC);
7220 RECT clientRect = new RECT ();
7221 OS.GetClientRect (handle, clientRect);
7222 if (itemRect.right > cellRect.right || itemRect.right > clientRect.right) {
7224 String string = " ";
7225 // String string = null;
7226 // if (pinfo.iSubItem == 0) {
7227 // string = item.text;
7229 // String [] strings = item.strings;
7230 // if (strings != null) string = strings [pinfo.iSubItem];
7232 if (string != null) {
7233 Shell shell = getShell ();
7234 char [] chars = new char [string.length () + 1];
7235 string.getChars (0, string.length (), chars, 0);
7236 shell.setToolTipText (lpnmtdi, chars);
7237 OS.MoveMemory (lParam, lpnmtdi, NMTTDISPINFO.sizeof);
7242 if (hFont != -1) hFont = OS.SelectObject (hDC, hFont);
7243 if (newFont != 0) OS.SelectObject (hDC, oldFont);
7244 OS.ReleaseDC (handle, hDC);
7247 return new LRESULT (code);
7253 LRESULT wmNotifyToolTip (NMTTCUSTOMDRAW nmcd, long lParam) {
7254 switch (nmcd.dwDrawStage) {
7255 case OS.CDDS_PREPAINT: {
7256 if (isCustomToolTip ()) {
7258 // nmcd.uDrawFlags |= OS.DT_CALCRECT;
7259 // OS.MoveMemory (lParam, nmcd, NMTTCUSTOMDRAW.sizeof);
7260 return new LRESULT (OS.CDRF_NOTIFYPOSTPAINT | OS.CDRF_NEWFONT);
7264 case OS.CDDS_POSTPAINT: {
7265 LVHITTESTINFO pinfo = new LVHITTESTINFO ();
7266 int pos = OS.GetMessagePos ();
7267 POINT pt = new POINT();
7268 OS.POINTSTOPOINT (pt, pos);
7269 OS.ScreenToClient (handle, pt);
7273 * Bug in Windows. When LVM_SUBITEMHITTEST is used to hittest
7274 * a point that is above the table, instead of returning -1 to
7275 * indicate that the hittest failed, a negative index is returned.
7276 * The fix is to consider any value that is negative a failure.
7278 if (OS.SendMessage (handle, OS.LVM_SUBITEMHITTEST, 0, pinfo) >= 0) {
7279 TableItem item = _getItem (pinfo.iItem);
7280 long hDC = OS.GetDC (handle);
7281 long hFont = item.fontHandle (pinfo.iSubItem);
7282 if (hFont == -1) hFont = OS.SendMessage (handle, OS.WM_GETFONT, 0, 0);
7283 long oldFont = OS.SelectObject (hDC, hFont);
7284 boolean drawForeground = true;
7285 RECT cellRect = item.getBounds (pinfo.iItem, pinfo.iSubItem, true, true, false, false, hDC);
7286 if (hooks (SWT.EraseItem)) {
7287 Event event = sendEraseItemEvent (item, nmcd, pinfo.iSubItem, cellRect);
7288 if (isDisposed () || item.isDisposed ()) break;
7290 drawForeground = (event.detail & SWT.FOREGROUND) != 0;
7292 drawForeground = false;
7295 if (drawForeground) {
7296 int nSavedDC = OS.SaveDC (nmcd.hdc);
7297 int gridWidth = getLinesVisible () ? Table.GRID_WIDTH : 0;
7298 RECT insetRect = toolTipInset (cellRect);
7299 OS.SetWindowOrgEx (nmcd.hdc, insetRect.left, insetRect.top, null);
7300 GCData data = new GCData ();
7301 data.device = display;
7302 data.foreground = OS.GetTextColor (nmcd.hdc);
7303 data.background = OS.GetBkColor (nmcd.hdc);
7304 data.font = Font.win32_new (display, hFont);
7305 GC gc = GC.win32_new (nmcd.hdc, data);
7306 int x = cellRect.left;
7307 if (pinfo.iSubItem != 0) x -= gridWidth;
7308 Image image = item.getImage (pinfo.iSubItem);
7309 if (image != null) {
7310 Rectangle rect = image.getBoundsInPixels ();
7311 RECT imageRect = item.getBounds (pinfo.iItem, pinfo.iSubItem, false, true, false, false, hDC);
7312 Point size = imageList == null ? new Point (rect.width, rect.height) : imageList.getImageSize ();
7313 int y = imageRect.top + Math.max (0, (imageRect.bottom - imageRect.top - size.y) / 2);
7314 rect = DPIUtil.autoScaleDown(rect);
7315 gc.drawImage (image, rect.x, rect.y, rect.width, rect.height, DPIUtil.autoScaleDown(x), DPIUtil.autoScaleDown(y), DPIUtil.autoScaleDown(size.x), DPIUtil.autoScaleDown(size.y));
7316 x += size.x + INSET + (pinfo.iSubItem == 0 ? -2 : 4);
7320 String string = item.getText (pinfo.iSubItem);
7321 if (string != null) {
7322 int flags = OS.DT_NOPREFIX | OS.DT_SINGLELINE | OS.DT_VCENTER;
7323 TableColumn column = columns != null ? columns [pinfo.iSubItem] : null;
7324 if (column != null) {
7325 if ((column.style & SWT.CENTER) != 0) flags |= OS.DT_CENTER;
7326 if ((column.style & SWT.RIGHT) != 0) flags |= OS.DT_RIGHT;
7328 char [] buffer = string.toCharArray ();
7329 RECT textRect = new RECT ();
7330 OS.SetRect (textRect, x, cellRect.top, cellRect.right, cellRect.bottom);
7331 OS.DrawText (nmcd.hdc, buffer, buffer.length, textRect, flags);
7334 OS.RestoreDC (nmcd.hdc, nSavedDC);
7336 if (hooks (SWT.PaintItem)) {
7337 RECT itemRect = item.getBounds (pinfo.iItem, pinfo.iSubItem, true, true, false, false, hDC);
7338 sendPaintItemEvent (item, nmcd, pinfo.iSubItem, itemRect);
7340 OS.SelectObject (hDC, oldFont);
7341 OS.ReleaseDC (handle, hDC);