]> gerrit.simantics Code Review - simantics/platform.git/blob - bundles/org.eclipse.swt.win32.win32.x86_64/src/org/eclipse/swt/widgets/Table.java
Remove invalid SHA-256-Digests
[simantics/platform.git] / bundles / org.eclipse.swt.win32.win32.x86_64 / src / org / eclipse / swt / widgets / Table.java
1 /*******************************************************************************
2  * Copyright (c) 2000, 2018 IBM Corporation and others.
3  *
4  * This program and the accompanying materials
5  * are made available under the terms of the Eclipse Public License 2.0
6  * which accompanies this distribution, and is available at
7  * https://www.eclipse.org/legal/epl-2.0/
8  *
9  * SPDX-License-Identifier: EPL-2.0
10  *
11  * Contributors:
12  *     IBM Corporation - initial API and implementation
13  *     Roland Oldenburg <r.oldenburg@hsp-software.de> - Bug 292199
14  *     Conrad Groth - Bug 384906
15  *******************************************************************************/
16 package org.eclipse.swt.widgets;
17
18
19 //import java.util.*;
20
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.*;
26
27 /**
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.
31  * <p>
32  * The item children that may be added to instances of this class
33  * must be of type <code>TableItem</code>.
34  * </p><p>
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).
40  * </p><p>
41  * Here is an example of using a <code>Table</code> with style <code>VIRTUAL</code>:</p>
42  * <pre><code>
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 ());
51  *      }
52  *  });
53  * </code></pre>
54  * <p>
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
58  * editor.
59  * </p>
60  * <dl>
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>
65  * </dl>
66  * <p>
67  * Note: Only one of the styles SINGLE, and MULTI may be specified.
68  * </p><p>
69  * IMPORTANT: This class is <em>not</em> intended to be subclassed.
70  * </p>
71  *
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.
76  */
77 public class Table extends Composite {
78         TableItem [] items;
79         int [] keys;
80         TableColumn [] columns;
81         int columnCount, customCount, keyCount;
82         ImageList imageList, headerImageList;
83         TableItem currentItem;
84         TableColumn sortColumn;
85         RECT focusRect;
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);
110         static {
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;
116         }
117
118 /**
119  * Constructs a new instance of this class given its parent
120  * and a style value describing its behavior and appearance.
121  * <p>
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.
129  * </p>
130  *
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
133  *
134  * @exception IllegalArgumentException <ul>
135  *    <li>ERROR_NULL_ARGUMENT - if the parent is null</li>
136  * </ul>
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>
140  * </ul>
141  *
142  * @see SWT#SINGLE
143  * @see SWT#MULTI
144  * @see SWT#CHECK
145  * @see SWT#FULL_SELECTION
146  * @see SWT#HIDE_SELECTION
147  * @see SWT#VIRTUAL
148  * @see SWT#NO_SCROLL
149  * @see Widget#checkSubclass
150  * @see Widget#getStyle
151  */
152 public Table (Composite parent, int style) {
153         super (parent, checkStyle (style));
154 }
155
156 @Override
157 void _addListener (int eventType, Listener listener) {
158         super._addListener (eventType, listener);
159         switch (eventType) {
160                 case SWT.MeasureItem:
161                 case SWT.EraseItem:
162                 case SWT.PaintItem:
163                         setCustomDraw (true);
164                         setBackgroundTransparent (true);
165                         break;
166         }
167 }
168
169 boolean _checkGrow (int count) {
170         //TODO - code could be shared but it would mix keyed and non-keyed logic
171         if (keys == null) {
172                 if (count == items.length) {
173                         /*
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
177                         * memory usage.
178                         */
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);
183                         items = newItems;
184                 }
185         } else {
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];
195                         }
196                         items = newItems;
197                         keys = null;
198                         keyCount = 0;
199                         return true;
200                 } else {
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);
208                                 keys = newKeys;
209                                 TableItem [] newItems = new TableItem [length];
210                                 System.arraycopy (items, 0, newItems, 0, items.length);
211                                 items = newItems;
212                         }
213                 }
214         }
215         return false;
216 }
217
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
221         if (keys == null) {
222                 if (!ignoreShrink) {
223                         /* Resize the item array to match the item count */
224                         int count = (int)OS.SendMessage (handle, OS.LVM_GETITEMCOUNT, 0, 0);
225
226                         /*
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.
231                         */
232                         if (count == 0 && items.length > 4) {
233                                 while (count<items.length && items[count] != null && !items[count].isDisposed()) {
234                                         count++;
235                                 }
236                         }
237
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);
242                                 items = newItems;
243                         }
244                 }
245         } else {
246                 if (!ignoreShrink) {
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);
251                                 keys = newKeys;
252                                 TableItem [] newItems = new TableItem [length];
253                                 System.arraycopy (items, 0, newItems, 0, keyCount);
254                                 items = newItems;
255                         }
256                 }
257         }
258 }
259
260 void _clearItems () {
261         items = null;
262         keys = null;
263         keyCount = 0;
264 }
265
266 TableItem _getItem (int index) {
267         return _getItem (index, true);
268 }
269
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);
273 }
274
275 TableItem _getItem (int index, boolean create, int count) {
276         //TODO - code could be shared but it would mix keyed and non-keyed logic
277         if (keys == null) {
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);
282         } else {
283                 if ((style & SWT.VIRTUAL) == 0 || !create) {
284                         if (keyCount == 0) return null;
285                         if (index > keys [keyCount - 1]) return null;
286                 }
287                 int keyIndex = binarySearch (keys, 0, keyCount, index);
288                 if ((style & SWT.VIRTUAL) == 0 || !create) {
289                         return keyIndex < 0 ? null : items [keyIndex];
290                 }
291                 if (keyIndex < 0) {
292                         if (count == -1) {
293                                 count = (int)OS.SendMessage (handle, OS.LVM_GETITEMCOUNT, 0, 0);
294                         }
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);
299                         }
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);
304                         }
305                         keyCount++;
306                         keys [keyIndex] = index;
307                 } else {
308                         if (items [keyIndex] != null) return items [keyIndex];
309                 }
310                 return items [keyIndex] = new TableItem (this, SWT.NONE, -1, false);
311         }
312 }
313
314 void _getItems (TableItem [] result, int count) {
315         if (keys == null) {
316                 System.arraycopy (items, 0, result, 0, count);
317         } else {
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]];
322                 }
323         }
324 }
325
326 boolean _hasItems () {
327         return items != null;
328 }
329
330 void _initItems () {
331         items = new TableItem [4];
332         if (COMPRESS_ITEMS) {
333                 if ((style & SWT.VIRTUAL) != 0) {
334                         keyCount = 0;
335                         keys = new int [4];
336                 }
337         }
338 }
339
340 /* NOTE: The array has already been grown to have space for the new item */
341 void _insertItem (int index, TableItem item, int count) {
342         if (keys == null) {
343                 System.arraycopy (items, index, items, index + 1, count - index);
344                 items [index] = item;
345         } else {
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;
352                 keyCount++;
353                 for (int i=keyIndex + 1; i<keyCount; i++) keys[i]++;
354         }
355 }
356
357 void _removeItem (int index, int count) {
358         if (keys == null) {
359                 System.arraycopy (items, index + 1, items, index, --count - index);
360                 items [count] = null;
361         } else {
362                 int keyIndex = binarySearch (keys, 0, keyCount, index);
363                 if (keyIndex < 0) {
364                         keyIndex = -keyIndex - 1;
365                 } else {
366                         --keyCount;
367                         System.arraycopy (keys, keyIndex + 1, keys, keyIndex, keyCount - keyIndex);
368                         keys [keyCount] = 0;
369                         System.arraycopy (items, keyIndex + 1, items, keyIndex, keyCount - keyIndex);
370                         items [keyCount] = null;
371                 }
372                 for (int i=keyIndex; i<keyCount; i++) --keys[i];
373         }
374 }
375
376 /* NOTE: Removes from start to index - 1 */
377 void _removeItems (int start, int index, int count) {
378         if (keys == null) {
379                 System.arraycopy (items, index, items, start, count - index);
380                 for (int i=count-(index-start); i<count; i++) items [i] = null;
381         } else {
382                 int end = index;
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);
394         }
395 }
396
397 void _setItemCount (int count, int itemCount) {
398         if (keys == null) {
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));
402                 items = newItems;
403         } else {
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);
410                 keys = newKeys;
411                 TableItem [] newItems = new TableItem [length];
412                 System.arraycopy (items, 0, newItems, 0, keyCount);
413                 items = newItems;
414         }
415 }
416
417 /**
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>
421  * interface.
422  * <p>
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.
428  * </p>
429  *
430  * @param listener the listener which should be notified when the user changes the receiver's selection
431  *
432  * @exception IllegalArgumentException <ul>
433  *    <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
434  * </ul>
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>
438  * </ul>
439  *
440  * @see SelectionListener
441  * @see #removeSelectionListener
442  * @see SelectionEvent
443  */
444 public void addSelectionListener (SelectionListener listener) {
445         checkWidget ();
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);
450 }
451
452 @Override
453 long callWindowProc (long hwnd, int msg, long wParam, long lParam) {
454         return callWindowProc (hwnd, msg, wParam, lParam, false);
455 }
456
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);
461         }
462         int topIndex = 0;
463         boolean checkSelection = false, checkActivate = false, redraw = false;
464         switch (msg) {
465                 /* Keyboard messages */
466                 /*
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.
472                 */
473                 case OS.WM_KEYDOWN:
474                         checkActivate = true;
475                         //FALL THROUGH
476                 case OS.WM_CHAR:
477                 case OS.WM_IME_CHAR:
478                 case OS.WM_KEYUP:
479                 case OS.WM_SYSCHAR:
480                 case OS.WM_SYSKEYDOWN:
481                 case OS.WM_SYSKEYUP:
482                         //FALL THROUGH
483
484                 /* Scroll messages */
485                 case OS.WM_HSCROLL:
486                 case OS.WM_VSCROLL:
487                         //FALL THROUGH
488
489                 /* Resize messages */
490                 case OS.WM_WINDOWPOSCHANGED:
491                         redraw = findImageControl () != null && getDrawing () && OS.IsWindowVisible (handle);
492                         if (redraw) {
493                                 /*
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
497                                 * turned off.
498                                 */
499                                 OS.DefWindowProc (handle, OS.WM_SETREDRAW, 0, 0);
500                                 OS.SendMessage (handle, OS.LVM_SETBKCOLOR, 0, 0xFFFFFF);
501                         }
502                         //FALL THROUGH
503
504                 /* Mouse messages */
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;
522                         //FALL THROUGH
523
524                 /* Other messages */
525                 case OS.WM_SETFONT:
526                 case OS.WM_TIMER: {
527                         if (findImageControl () != null) {
528                                 topIndex = (int)OS.SendMessage (handle, OS.LVM_GETTOPINDEX, 0, 0);
529                         }
530                 }
531         }
532         boolean oldSelected = wasSelected;
533         if (checkSelection) wasSelected = false;
534         if (checkActivate) ignoreActivate = true;
535
536         /*
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.
542         */
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) {
551                                         fixPaint = true;
552                                         break;
553                                 }
554                                 hwndOwner = OS.GetWindow (hwndParent, OS.GW_OWNER);
555                                 if (hwndOwner != 0) break;
556                                 hwndParent = OS.GetParent (hwndParent);
557                         }
558                 }
559         }
560
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) {
564                 switch (msg) {
565                         case OS.WM_PAINT:
566                         case OS.WM_NCPAINT:
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) {
570                                         fixScroll = true;
571                                         bits &= ~OS.WS_HSCROLL;
572                                 }
573                                 if ((style & SWT.V_SCROLL) == 0 && (bits & OS.WS_VSCROLL) != 0) {
574                                         fixScroll = true;
575                                         bits &= ~OS.WS_VSCROLL;
576                                 }
577                                 if (fixScroll) OS.SetWindowLong (handle, OS.GWL_STYLE, bits);
578                         }
579                 }
580         }
581         long code = 0;
582         if (fixPaint) {
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);
587         } else {
588                 code = OS.CallWindowProc (TableProc, hwnd, msg, wParam, lParam);
589         }
590         if (fixScroll) {
591                 int flags = OS.RDW_FRAME | OS.RDW_INVALIDATE;
592                 OS.RedrawWindow (handle, null, 0, flags);
593         }
594
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);
602                 }
603                 wasSelected = oldSelected;
604         }
605         switch (msg) {
606                 /* Keyboard messages */
607                 case OS.WM_KEYDOWN:
608                 case OS.WM_CHAR:
609                 case OS.WM_IME_CHAR:
610                 case OS.WM_KEYUP:
611                 case OS.WM_SYSCHAR:
612                 case OS.WM_SYSKEYDOWN:
613                 case OS.WM_SYSKEYUP:
614                         //FALL THROUGH
615
616                 /* Scroll messages */
617                 case OS.WM_HSCROLL:
618                 case OS.WM_VSCROLL:
619                         //FALL THROUGH
620
621                 /* Resize messages */
622                 case OS.WM_WINDOWPOSCHANGED:
623                         if (redraw) {
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);
629                         }
630                         //FALL THROUGH
631
632                 /* Mouse messages */
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:
649                         //FALL THROUGH
650
651                 /* Other messages */
652                 case OS.WM_SETFONT:
653                 case OS.WM_TIMER: {
654                         if (findImageControl () != null) {
655                                 if (topIndex != OS.SendMessage (handle, OS.LVM_GETTOPINDEX, 0, 0)) {
656                                         OS.InvalidateRect (handle, null, true);
657                                 }
658                         }
659                         break;
660                 }
661
662                 case OS.WM_PAINT:
663                         painted = true;
664                         break;
665         }
666         return code;
667 }
668
669 static int checkStyle (int style) {
670         /*
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.
675         *
676         * NOTE: This code appears on all platforms so that
677         * applications have consistent scroll bar behavior.
678         */
679         if ((style & SWT.NO_SCROLL) == 0) {
680                 style |= SWT.H_SCROLL | SWT.V_SCROLL;
681         }
682         return checkBits (style, SWT.SINGLE, SWT.MULTI, 0, 0, 0, 0);
683 }
684
685 LRESULT CDDS_ITEMPOSTPAINT (NMLVCUSTOMDRAW nmcd, long wParam, long lParam) {
686         long hDC = nmcd.hdc;
687         if (explorerTheme && !ignoreCustomDraw) {
688                 hotIndex = -1;
689                 if (hooks (SWT.EraseItem) && nmcd.left != nmcd.right) {
690                         OS.RestoreDC (hDC, -1);
691                 }
692         }
693         /*
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
699         * custom draw.
700         *
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.
704         */
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 ();
724                                                                         if (index == 0) {
725                                                                                 itemRect.left = OS.LVIR_LABEL;
726                                                                                 OS.SendMessage (handle, OS. LVM_GETITEMRECT, index, itemRect);
727                                                                         } else {
728                                                                                 itemRect.top = index;
729                                                                                 itemRect.left = OS.LVIR_ICON;
730                                                                                 OS.SendMessage (handle, OS. LVM_GETSUBITEMRECT, nmcd.dwItemSpec, itemRect);
731                                                                         }
732                                                                         ignoreCustomDraw = oldIgnore;
733                                                                         rect.left = itemRect.left;
734                                                                         OS.DrawFocusRect (nmcd.hdc, rect);
735                                                                 }
736                                                         }
737                                                 }
738                                         }
739                                 }
740                         }
741                 }
742         }
743         return null;
744 }
745
746 LRESULT CDDS_ITEMPREPAINT (NMLVCUSTOMDRAW nmcd, long wParam, long lParam) {
747         /*
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
753         * custom draw.
754         *
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.
758         */
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);
768                                                 }
769                                         }
770                                 }
771                         }
772                 }
773         }
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);
781                 }
782         }
783         return new LRESULT (OS.CDRF_NOTIFYSUBITEMDRAW | OS.CDRF_NOTIFYPOSTPAINT);
784 }
785
786 LRESULT CDDS_POSTPAINT (NMLVCUSTOMDRAW nmcd, long wParam, long lParam) {
787         if (ignoreCustomDraw) return null;
788         /*
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
794         * custom draw.
795         */
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;
802                                         /*
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.
807                                         */
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);
815                                         /*
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.
819                                         */
820                                         hwndToolTip = OS.SendMessage (handle, OS.LVM_SETTOOLTIPS, hwndToolTip, hwndToolTip);
821                                 }
822                         }
823                 }
824         }
825         return null;
826 }
827
828 LRESULT CDDS_PREPAINT (NMLVCUSTOMDRAW nmcd, long wParam, long lParam) {
829         if (ignoreCustomDraw) {
830                 return new LRESULT (OS.CDRF_NOTIFYITEMDRAW | OS.CDRF_NOTIFYPOSTPAINT);
831         }
832         /*
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
838         * custom draw.
839         */
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;
846                                         /*
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.
851                                         */
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);
859                                         /*
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.
863                                         */
864                                         hwndToolTip = OS.SendMessage (handle, OS.LVM_SETTOOLTIPS, hwndToolTip, hwndToolTip);
865                                 }
866                         }
867                 }
868         }
869         if (OS.IsWindowVisible (handle)) {
870                 /*
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.
875                 */
876                 RECT rect = new RECT ();
877                 OS.SetRect (rect, nmcd.left, nmcd.top, nmcd.right, nmcd.bottom);
878                 if (explorerTheme && columnCount == 0) {
879                         long hDC = nmcd.hdc;
880                         if (OS.IsWindowEnabled (handle) || findImageControl () != null) {
881                                 drawBackground (hDC, rect);
882                         } else {
883                                 fillBackground (hDC, OS.GetSysColor (OS.COLOR_3DFACE), rect);
884                         }
885                 } else {
886                         Control control = findBackgroundControl ();
887                         if (control != null && control.backgroundImage != null) {
888                                 fillImageBackground (nmcd.hdc, control, rect, 0, 0);
889                         } else {
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);
897                                                         if (index != -1) {
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);
909                                                                         }
910                                                                 }
911                                                         }
912                                                 }
913                                         }
914                                 }
915                         }
916                 }
917         }
918         return new LRESULT (OS.CDRF_NOTIFYITEMDRAW | OS.CDRF_NOTIFYPOSTPAINT);
919 }
920
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);
924         long hDC = nmcd.hdc;
925         if (ignoreDrawForeground) OS.RestoreDC (hDC, -1);
926         if (OS.IsWindowVisible (handle)) {
927                 /*
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.
932                 *
933                 * Update region is saved and restored around LVM_SETSELECTEDCOLUMN
934                 * to prevent infinite WM_PAINT on Vista.
935                 */
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);
948                                         }
949                                 }
950                         }
951                 }
952                 if (hooks (SWT.PaintItem)) {
953                         TableItem item = _getItem ((int)nmcd.dwItemSpec);
954                         sendPaintItemEvent (item, nmcd);
955                         //widget could be disposed at this point
956                 }
957                 if (!ignoreDrawFocus && focusRect != null) {
958                         OS.SetTextColor (nmcd.hdc, 0);
959                         OS.SetBkColor (nmcd.hdc, 0xFFFFFF);
960                         OS.DrawFocusRect (nmcd.hdc, focusRect);
961                         focusRect = null;
962                 }
963         }
964         return null;
965 }
966
967 LRESULT CDDS_SUBITEMPREPAINT (NMLVCUSTOMDRAW nmcd, long wParam, long lParam) {
968         long hDC = nmcd.hdc;
969         if (explorerTheme && !ignoreCustomDraw && hooks (SWT.EraseItem) && (nmcd.left != nmcd.right)) {
970                 OS.RestoreDC (hDC, -1);
971         }
972         /*
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
978         * null.
979         *
980         * NOTE: Force the item to be created if it does not exist.
981         */
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);
988         }
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;
997                 }
998                 if (hooks (SWT.EraseItem)) {
999                         sendEraseItemEvent (item, nmcd, lParam, measureEvent);
1000                         if (isDisposed () || item.isDisposed ()) return null;
1001                         code |= OS.CDRF_NOTIFYPOSTPAINT;
1002                 }
1003                 if (ignoreDrawForeground || hooks (SWT.PaintItem)) code |= OS.CDRF_NOTIFYPOSTPAINT;
1004         }
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;
1010         /*
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.
1017         */
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) {
1023                                 /*
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.
1027                                 */
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);
1038                                                 } else {
1039                                                         if ((style & SWT.HIDE_SELECTION) == 0) {
1040                                                                 clrSelection = OS.GetSysColor (OS.COLOR_3DFACE);
1041                                                         }
1042                                                 }
1043                                         } else {
1044                                                 if (OS.GetFocus () == handle || display.getHighContrast ()) {
1045                                                         clrText = OS.GetSysColor (OS.COLOR_HIGHLIGHTTEXT);
1046                                                         clrTextBk = clrSelection = OS.GetSysColor (OS.COLOR_HIGHLIGHT);
1047                                                 } else {
1048                                                         if ((style & SWT.HIDE_SELECTION) == 0) {
1049                                                                 clrTextBk = clrSelection = OS.GetSysColor (OS.COLOR_3DFACE);
1050                                                         }
1051                                                 }
1052                                         }
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);
1056                                         }
1057                                 }
1058                         }
1059                 }
1060         }
1061         if (!ignoreDrawForeground) {
1062                 /*
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.
1068                 */
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;
1074                         }
1075                 }
1076                 if (hasAttributes) {
1077                         if (hFont == -1) hFont = OS.SendMessage (handle, OS.WM_GETFONT, 0, 0);
1078                         OS.SelectObject (hDC, hFont);
1079                         if (enabled) {
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 ();
1089                                                         }
1090                                                 }
1091                                         }
1092                                 } else {
1093                                         nmcd.clrTextBk = selectionForeground != -1 ? OS.CLR_NONE : clrTextBk;
1094                                 }
1095                                 OS.MoveMemory (lParam, nmcd, NMLVCUSTOMDRAW.sizeof);
1096                         }
1097                         code |= OS.CDRF_NEWFONT;
1098                 }
1099         }
1100         /*
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.
1105         *
1106         * Update region is saved and restored around LVM_SETSELECTEDCOLUMN
1107         * to prevent infinite WM_PAINT on Vista.
1108         */
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;
1119                 }
1120         }
1121         if (!enabled) {
1122                 /*
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.
1126                 */
1127                 nmcd.clrText = OS.GetSysColor (OS.COLOR_GRAYTEXT);
1128                 if (findImageControl () != null || hasCustomBackground()) {
1129                         nmcd.clrTextBk = OS.CLR_NONE;
1130                 }
1131                 nmcd.uItemState &= ~OS.CDIS_SELECTED;
1132                 OS.MoveMemory (lParam, nmcd, NMLVCUSTOMDRAW.sizeof);
1133                 code |= OS.CDRF_NEWFONT;
1134         }
1135         return new LRESULT (code);
1136 }
1137
1138 @Override
1139 void checkBuffered () {
1140         super.checkBuffered ();
1141         style |= SWT.DOUBLE_BUFFERED;
1142 }
1143
1144 boolean checkData (TableItem item, boolean redraw) {
1145         if ((style & SWT.VIRTUAL) == 0) return true;
1146         return checkData (item, indexOf (item), redraw);
1147 }
1148
1149 boolean checkData (TableItem item, int index, boolean redraw) {
1150         if ((style & SWT.VIRTUAL) == 0) return true;
1151         if (!item.cached) {
1152                 item.cached = true;
1153                 Event event = new Event ();
1154                 event.item = item;
1155                 event.index = index;
1156                 currentItem = item;
1157                 sendEvent (SWT.SetData, event);
1158                 //widget could be disposed at this point
1159                 currentItem = null;
1160                 if (isDisposed () || item.isDisposed ()) return false;
1161                 if (redraw) {
1162                         if (!setScrollWidth (item, false)) {
1163                                 item.redraw ();
1164                         }
1165                 }
1166         }
1167         return true;
1168 }
1169
1170 @Override
1171 boolean checkHandle (long hwnd) {
1172         if (hwnd == handle) return true;
1173         return hwnd == OS.SendMessage (handle, OS.LVM_GETHEADER, 0, 0);
1174 }
1175
1176 @Override
1177 protected void checkSubclass () {
1178         if (!isValidSubclass ()) error (SWT.ERROR_INVALID_SUBCLASS);
1179 }
1180
1181 /**
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.
1186  *
1187  * @param index the index of the item to clear
1188  *
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>
1191  * </ul>
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>
1195  * </ul>
1196  *
1197  * @see SWT#VIRTUAL
1198  * @see SWT#SetData
1199  *
1200  * @since 3.0
1201  */
1202 public void clear (int index) {
1203         checkWidget ();
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);
1207         if (item != null) {
1208                 if (item != currentItem) item.clear ();
1209                 /*
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
1218                 * cached bounds.
1219                 */
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;
1227                 }
1228                 if (currentItem == null && getDrawing () && OS.IsWindowVisible (handle)) {
1229                         OS.SendMessage (handle, OS.LVM_REDRAWITEMS, index, index);
1230                 }
1231                 setScrollWidth (item, false);
1232         }
1233 }
1234
1235 /**
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.
1241  *
1242  * @param start the start index of the item to clear
1243  * @param end the end index of the item to clear
1244  *
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>
1247  * </ul>
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>
1251  * </ul>
1252  *
1253  * @see SWT#VIRTUAL
1254  * @see SWT#SetData
1255  *
1256  * @since 3.0
1257  */
1258 public void clear (int start, int end) {
1259         checkWidget ();
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);
1264         }
1265         if (start == 0 && end == count - 1) {
1266                 clearAll ();
1267         } else {
1268                 LVITEM lvItem = null;
1269                 boolean cleared = false;
1270                 for (int i=start; i<=end; i++) {
1271                         TableItem item = _getItem (i, false);
1272                         if (item != null) {
1273                                 if (item != currentItem) {
1274                                         cleared = true;
1275                                         item.clear ();
1276                                 }
1277                                 /*
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
1286                                 * cached bounds.
1287                                 */
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;
1293                                         }
1294                                         lvItem.iItem = i;
1295                                         OS.SendMessage (handle, OS.LVM_SETITEM, 0, lvItem);
1296                                         item.cached = false;
1297                                 }
1298                         }
1299                 }
1300                 if (cleared) {
1301                         if (currentItem == null && getDrawing () && OS.IsWindowVisible (handle)) {
1302                                 OS.SendMessage (handle, OS.LVM_REDRAWITEMS, start, end);
1303                         }
1304                         TableItem item = start == end ? _getItem (start, false) : null;
1305                         setScrollWidth (item, false);
1306                 }
1307         }
1308 }
1309
1310 /**
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.
1315  *
1316  * @param indices the array of indices of the items
1317  *
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>
1321  * </ul>
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>
1325  * </ul>
1326  *
1327  * @see SWT#VIRTUAL
1328  * @see SWT#SetData
1329  *
1330  * @since 3.0
1331  */
1332 public void clear (int [] indices) {
1333         checkWidget ();
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);
1340                 }
1341         }
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);
1347                 if (item != null) {
1348                         if (item != currentItem) {
1349                                 cleared = true;
1350                                 item.clear ();
1351                         }
1352                         /*
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
1361                         * cached bounds.
1362                         */
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;
1368                                 }
1369                                 lvItem.iItem = i;
1370                                 OS.SendMessage (handle, OS.LVM_SETITEM, 0, lvItem);
1371                                 item.cached = false;
1372                         }
1373                         if (currentItem == null && getDrawing () && OS.IsWindowVisible (handle)) {
1374                                 OS.SendMessage (handle, OS.LVM_REDRAWITEMS, index, index);
1375                         }
1376                 }
1377         }
1378         if (cleared) setScrollWidth (null, false);
1379 }
1380
1381 /**
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.
1386  *
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>
1390  * </ul>
1391  *
1392  * @see SWT#VIRTUAL
1393  * @see SWT#SetData
1394  *
1395  * @since 3.0
1396  */
1397 public void clearAll () {
1398         checkWidget ();
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);
1404                 if (item != null) {
1405                         if (item != currentItem) {
1406                                 cleared = true;
1407                                 item.clear ();
1408                         }
1409                         /*
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
1418                         * cached bounds.
1419                         */
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;
1425                                 }
1426                                 lvItem.iItem = i;
1427                                 OS.SendMessage (handle, OS.LVM_SETITEM, 0, lvItem);
1428                                 item.cached = false;
1429                         }
1430                 }
1431         }
1432         if (cleared) {
1433                 if (currentItem == null && getDrawing () && OS.IsWindowVisible (handle)) {
1434                         OS.SendMessage (handle, OS.LVM_REDRAWITEMS, 0, count - 1);
1435                 }
1436                 setScrollWidth (null, false);
1437         }
1438 }
1439
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)) {
1444 //              int i = 0;
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;
1458 //                              index++;
1459 //                      }
1460 //                      if (newFont != 0) OS.SelectObject (hDC, oldFont);
1461 //                      OS.ReleaseDC (handle, hDC);
1462 //              }
1463 //      }
1464         RECT rect = new RECT ();
1465         OS.GetWindowRect (hwndHeader, rect);
1466         int height = rect.bottom - rect.top;
1467         int bits = 0;
1468         if (wHint != SWT.DEFAULT) {
1469                 bits |= wHint & 0xFFFF;
1470         } else {
1471                 int width = 0;
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);
1475                 }
1476                 bits |= width & 0xFFFF;
1477         }
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);
1492         }
1493         if ((style & SWT.H_SCROLL) != 0) {
1494                 height += OS.GetSystemMetrics (OS.SM_CYHSCROLL);
1495         }
1496         return new Point (width, height);
1497 }
1498
1499 @Override
1500 void createHandle () {
1501         super.createHandle ();
1502         state &= ~(CANVAS | THEME_BACKGROUND);
1503
1504         /* Use the Explorer theme */
1505         if (OS.IsAppThemed ()) {
1506                 explorerTheme = true;
1507                 OS.SetWindowTheme (handle, Display.EXPLORER, null);
1508         }
1509
1510         /* Get the header window handle */
1511         hwndHeader = OS.SendMessage (handle, OS.LVM_GETHEADER, 0, 0);
1512
1513         /*
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.
1518         */
1519 //      OS.SendMessage (handle, OS.LVM_SETITEMCOUNT, 1024 * 2, 0);
1520
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);
1528         }
1529
1530         /*
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
1540         * create.
1541         */
1542         long hFont = OS.GetStockObject (OS.SYSTEM_FONT);
1543         OS.SendMessage (handle, OS.WM_SETFONT, hFont, 0);
1544
1545         /*
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.
1551         */
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);
1559
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);
1564
1565         /*
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
1571         * for the header.
1572         */
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);
1579         }
1580 }
1581
1582 @Override
1583 int applyThemeBackground () {
1584         /*
1585          * Just inheriting the THEME_BACKGROUND doesn't turn complete Table
1586          * background transparent, TableItem background remains as-is.
1587          */
1588         return -1; /* No Change */
1589 }
1590
1591 void createHeaderToolTips () {
1592         if (headerToolTipHandle != 0) return;
1593         int bits = 0;
1594         if ((style & SWT.RIGHT_TO_LEFT) != 0) bits |= OS.WS_EX_LAYOUTRTL;
1595         headerToolTipHandle = OS.CreateWindowEx (
1596                 bits,
1597                 new TCHAR (0, OS.TOOLTIPS_CLASS, true),
1598                 null,
1599                 OS.TTS_NOPREFIX,
1600                 OS.CW_USEDEFAULT, 0, OS.CW_USEDEFAULT, 0,
1601                 handle,
1602                 0,
1603                 OS.GetModuleHandle (null),
1604                 null);
1605         if (headerToolTipHandle == 0) error (SWT.ERROR_NO_HANDLES);
1606         /*
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
1611         * a large value.
1612         */
1613         OS.SendMessage (headerToolTipHandle, OS.TTM_SETMAXTIPWIDTH, 0, 0x7FFF);
1614 }
1615
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);
1621         }
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;
1626         }
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);
1630                 if (item != null) {
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;
1637                         }
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);
1643                                 item.images = temp;
1644                         }
1645                         if (index == 0) {
1646                                 if (columnCount != 0) {
1647                                         if (strings == null) {
1648                                                 item.strings = new String [columnCount + 1];
1649                                                 item.strings [1] = item.text;
1650                                         }
1651                                         item.text = ""; //$NON-NLS-1$
1652                                         if (images == null) {
1653                                                 item.images = new Image [columnCount + 1];
1654                                                 item.images [1] = item.image;
1655                                         }
1656                                         item.image = null;
1657                                 }
1658                         }
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);
1664                                 temp [index] = -1;
1665                                 item.cellBackground = temp;
1666                         }
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);
1672                                 temp [index] = -1;
1673                                 item.cellForeground = temp;
1674                         }
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;
1681                         }
1682                 }
1683         }
1684         /*
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
1688         * or LVM_SETCOLUMN.
1689         */
1690         System.arraycopy (columns, index, columns, index + 1, columnCount++ - index);
1691         columns [index] = column;
1692
1693         /*
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.
1698         */
1699         ignoreColumnResize = true;
1700         if (index == 0) {
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);
1725                 } else {
1726                         OS.SendMessage (handle, OS.LVM_SETCOLUMNWIDTH, 0, 0);
1727                 }
1728                 /*
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
1737                 * cached bounds.
1738                 */
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++) {
1745                                 lvItem.iItem = i;
1746                                 OS.SendMessage (handle, OS.LVM_SETITEM, 0, lvItem);
1747                         }
1748                 }
1749         } else {
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;
1755                 lvColumn.fmt = fmt;
1756                 OS.SendMessage (handle, OS.LVM_INSERTCOLUMN, index, lvColumn);
1757         }
1758         ignoreColumnResize = false;
1759
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);
1775                 }
1776         }
1777 }
1778
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);
1782         _checkGrow (count);
1783         LVITEM lvItem = new LVITEM ();
1784         lvItem.mask = OS.LVIF_TEXT | OS.LVIF_IMAGE;
1785         lvItem.iItem = index;
1786         lvItem.pszText = OS.LPSTR_TEXTCALLBACK;
1787         /*
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.
1796         */
1797         lvItem.iImage = OS.I_IMAGECALLBACK;
1798
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);
1807
1808         /* Resize to show the first item */
1809         if (count == 0) setScrollWidth (item, false);
1810 }
1811
1812 @Override
1813 void createWidget () {
1814         super.createWidget ();
1815         itemHeight = hotIndex = -1;
1816         _initItems ();
1817         columns = new TableColumn [4];
1818 }
1819
1820 private boolean customHeaderDrawing() {
1821         return headerBackground != -1 || headerForeground != -1;
1822 }
1823
1824 @Override
1825 int defaultBackground () {
1826         return OS.GetSysColor (OS.COLOR_WINDOW);
1827 }
1828
1829 @Override
1830 void deregister () {
1831         super.deregister ();
1832         if (hwndHeader != 0) display.removeControl (hwndHeader);
1833 }
1834
1835 /**
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.
1841  *
1842  * @param indices the array of indices for the items to deselect
1843  *
1844  * @exception IllegalArgumentException <ul>
1845  *    <li>ERROR_NULL_ARGUMENT - if the set of indices is null</li>
1846  * </ul>
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>
1850  * </ul>
1851  */
1852 public void deselect (int [] indices) {
1853         checkWidget ();
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++) {
1859                 /*
1860                 * An index of -1 will apply the change to all
1861                 * items.  Ensure that indices are greater than -1.
1862                 */
1863                 if (indices [i] >= 0) {
1864                         ignoreSelect = true;
1865                         OS.SendMessage (handle, OS.LVM_SETITEMSTATE, indices [i], lvItem);
1866                         ignoreSelect = false;
1867                 }
1868         }
1869 }
1870
1871 /**
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.
1875  *
1876  * @param index the index of the item to deselect
1877  *
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>
1881  * </ul>
1882  */
1883 public void deselect (int index) {
1884         checkWidget ();
1885         /*
1886         * An index of -1 will apply the change to all
1887         * items.  Ensure that index is greater than -1.
1888         */
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;
1895 }
1896
1897 /**
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.
1903  *
1904  * @param start the start index of the items to deselect
1905  * @param end the end index of the items to deselect
1906  *
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>
1910  * </ul>
1911  */
1912 public void deselect (int start, int end) {
1913         checkWidget ();
1914         int count = (int)OS.SendMessage (handle, OS.LVM_GETITEMCOUNT, 0, 0);
1915         if (start == 0 && end == count - 1) {
1916                 deselectAll ();
1917         } else {
1918                 LVITEM lvItem = new LVITEM ();
1919                 lvItem.stateMask = OS.LVIS_SELECTED;
1920                 /*
1921                 * An index of -1 will apply the change to all
1922                 * items.  Ensure that indices are greater than -1.
1923                 */
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;
1929                 }
1930         }
1931 }
1932
1933 /**
1934  * Deselects all selected items in the receiver.
1935  *
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>
1939  * </ul>
1940  */
1941 public void deselectAll () {
1942         checkWidget ();
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;
1949 }
1950
1951 void destroyItem (TableColumn column) {
1952         int index = 0;
1953         while (index < columnCount) {
1954                 if (columns [index] == column) break;
1955                 index++;
1956         }
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);
1960         } else {
1961                 if (oldColumn > index) {
1962                         OS.SendMessage (handle, OS.LVM_SETSELECTEDCOLUMN, oldColumn - 1, 0);
1963                 }
1964         }
1965         int orderIndex = 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;
1970                 orderIndex++;
1971         }
1972         ignoreColumnResize = true;
1973         boolean first = false;
1974         if (index == 0) {
1975                 first = true;
1976                 /*
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.
1982                 */
1983                 setRedraw (false);
1984                 if (columnCount > 1) {
1985                         index = 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);
1999                 } else {
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);
2014                 }
2015                 setRedraw (true);
2016                 /*
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
2025                 * cached bounds.
2026                 */
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++) {
2034                                 lvItem.iItem = i;
2035                                 OS.SendMessage (handle, OS.LVM_SETITEM, 0, lvItem);
2036                         }
2037                 }
2038         }
2039         if (columnCount > 1) {
2040                 if (OS.SendMessage (handle, OS.LVM_DELETECOLUMN, index, 0) == 0) {
2041                         error (SWT.ERROR_ITEM_NOT_REMOVED);
2042                 }
2043         }
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);
2050                 if (item != null) {
2051                         if (columnCount == 0) {
2052                                 item.strings = null;
2053                                 item.images = null;
2054                                 item.cellBackground = null;
2055                                 item.cellForeground = null;
2056                                 item.cellFont = null;
2057                         } else {
2058                                 if (item.strings != null) {
2059                                         String [] strings = item.strings;
2060                                         if (index == 0) {
2061                                                 item.text = strings [1] != null ? strings [1] : ""; //$NON-NLS-1$
2062                                         }
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;
2067                                 } else {
2068                                         if (index == 0) item.text = ""; //$NON-NLS-1$
2069                                 }
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);
2076                                         item.images = temp;
2077                                 } else {
2078                                         if (index == 0) item.image = null;
2079                                 }
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;
2086                                 }
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;
2093                                 }
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;
2100                                 }
2101                         }
2102                 }
2103         }
2104         if (columnCount == 0) setScrollWidth (null, true);
2105         updateMoveable ();
2106         ignoreColumnResize = false;
2107         if (columnCount != 0) {
2108                 /*
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
2117                 * is used.
2118                 */
2119                 int count = 0;
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;
2126                         }
2127                 }
2128                 OS.SendMessage (handle, OS.LVM_GETCOLUMNORDERARRAY, columnCount, oldOrder);
2129                 int j = 0;
2130                 while (j < newOrder.length) {
2131                         if (oldOrder [j] != newOrder [j]) break;
2132                         j++;
2133                 }
2134                 if (j != newOrder.length) {
2135                         OS.SendMessage (handle, OS.LVM_SETCOLUMNORDERARRAY, newOrder.length, newOrder);
2136                         /*
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.
2140                         */
2141                         OS.InvalidateRect (handle, null, true);
2142                 }
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]);
2147                 }
2148                 for (int i=0; i<newColumns.length; i++) {
2149                         if (!newColumns [i].isDisposed ()) {
2150                                 newColumns [i].sendEvent (SWT.Move);
2151                         }
2152                 }
2153         }
2154
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);
2162         }
2163 }
2164
2165 void destroyItem (TableItem item) {
2166         int count = (int)OS.SendMessage (handle, OS.LVM_GETITEMCOUNT, 0, 0);
2167         int index = 0;
2168         while (index < count) {
2169                 if (_getItem (index, false) == item) break;
2170                 index++;
2171         }
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);
2179         --count;
2180         if (count == 0) setTableEmpty ();
2181         setDeferResize (false);
2182 }
2183
2184 void fixCheckboxImageList (boolean fixScroll) {
2185         /*
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.
2190         */
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);
2202 }
2203
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);
2211 }
2212
2213 /**
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.
2222  *
2223  * @param index the index of the column to return
2224  * @return the column at the given index
2225  *
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>
2228  * </ul>
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>
2232  * </ul>
2233  *
2234  * @see Table#getColumnOrder()
2235  * @see Table#setColumnOrder(int[])
2236  * @see TableColumn#getMoveable()
2237  * @see TableColumn#setMoveable(boolean)
2238  * @see SWT#Move
2239  */
2240 public TableColumn getColumn (int index) {
2241         checkWidget ();
2242         if (!(0 <= index && index < columnCount)) error (SWT.ERROR_INVALID_RANGE);
2243         return columns [index];
2244 }
2245
2246 /**
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.
2252  *
2253  * @return the number of columns
2254  *
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>
2258  * </ul>
2259  */
2260 public int getColumnCount () {
2261         checkWidget ();
2262         return columnCount;
2263 }
2264
2265 /**
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.
2269  * <p>
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.
2273  * </p><p>
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.
2277  * </p>
2278  *
2279  * @return the current visual order of the receiver's items
2280  *
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>
2284  * </ul>
2285  *
2286  * @see Table#setColumnOrder(int[])
2287  * @see TableColumn#getMoveable()
2288  * @see TableColumn#setMoveable(boolean)
2289  * @see SWT#Move
2290  *
2291  * @since 3.1
2292  */
2293 public int[] getColumnOrder () {
2294         checkWidget ();
2295         if (columnCount == 0) return new int [0];
2296         int [] order = new int [columnCount];
2297         OS.SendMessage (handle, OS.LVM_GETCOLUMNORDERARRAY, columnCount, order);
2298         return order;
2299 }
2300
2301 /**
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.
2309  * <p>
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.
2313  * </p>
2314  *
2315  * @return the items in the receiver
2316  *
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>
2320  * </ul>
2321  *
2322  * @see Table#getColumnOrder()
2323  * @see Table#setColumnOrder(int[])
2324  * @see TableColumn#getMoveable()
2325  * @see TableColumn#setMoveable(boolean)
2326  * @see SWT#Move
2327  */
2328 public TableColumn [] getColumns () {
2329         checkWidget ();
2330         TableColumn [] result = new TableColumn [columnCount];
2331         System.arraycopy (columns, 0, result, 0, columnCount);
2332         return result;
2333 }
2334
2335 int getFocusIndex () {
2336 //      checkWidget ();
2337         return (int)OS.SendMessage (handle, OS.LVM_GETNEXTITEM, -1, OS.LVNI_FOCUSED);
2338 }
2339
2340 /**
2341  * Returns the width in points of a grid line.
2342  *
2343  * @return the width of a grid line in points
2344  *
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>
2348  * </ul>
2349  */
2350 public int getGridLineWidth () {
2351         checkWidget ();
2352         return DPIUtil.autoScaleDown(getGridLineWidthInPixels());
2353 }
2354
2355 int getGridLineWidthInPixels () {
2356         return GRID_WIDTH;
2357 }
2358
2359 /**
2360  * Returns the header background color.
2361  *
2362  * @return the receiver's header background color.
2363  *
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>
2367  * </ul>
2368  * @since 3.106
2369  */
2370 public Color getHeaderBackground () {
2371         checkWidget ();
2372         return Color.win32_new (display, getHeaderBackgroundPixel());
2373 }
2374
2375 private int getHeaderBackgroundPixel() {
2376         return headerBackground != -1 ? headerBackground : defaultBackground();
2377 }
2378
2379 /**
2380  * Returns the header foreground color.
2381  *
2382  * @return the receiver's header foreground color.
2383  *
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>
2387  * </ul>
2388  * @since 3.106
2389  */
2390 public Color getHeaderForeground () {
2391         checkWidget ();
2392         return Color.win32_new (display, getHeaderForegroundPixel());
2393 }
2394
2395 private int getHeaderForegroundPixel() {
2396         return headerForeground != -1 ? headerForeground : defaultForeground();
2397 }
2398
2399 /**
2400  * Returns the height of the receiver's header
2401  *
2402  * @return the height of the header or zero if the header is not visible
2403  *
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>
2407  * </ul>
2408  *
2409  * @since 2.0
2410  */
2411 public int getHeaderHeight () {
2412         checkWidget ();
2413         return DPIUtil.autoScaleDown(getHeaderHeightInPixels ());
2414 }
2415
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;
2421 }
2422
2423 /**
2424  * Returns <code>true</code> if the receiver's header is visible,
2425  * and <code>false</code> otherwise.
2426  * <p>
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.
2431  * </p>
2432  *
2433  * @return the receiver's header's visibility state
2434  *
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>
2438  * </ul>
2439  */
2440 public boolean getHeaderVisible () {
2441         checkWidget ();
2442         int bits = OS.GetWindowLong (handle, OS.GWL_STYLE);
2443         return (bits & OS.LVS_NOCOLUMNHEADER) == 0;
2444 }
2445
2446 /**
2447  * Returns the item at the given, zero-relative index in the
2448  * receiver. Throws an exception if the index is out of range.
2449  *
2450  * @param index the index of the item to return
2451  * @return the item at the given index
2452  *
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>
2455  * </ul>
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>
2459  * </ul>
2460  */
2461 public TableItem getItem (int index) {
2462         checkWidget ();
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);
2466 }
2467
2468 /**
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.
2472  * <p>
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.
2478  * </p>
2479  *
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
2482  *
2483  * @exception IllegalArgumentException <ul>
2484  *    <li>ERROR_NULL_ARGUMENT - if the point is null</li>
2485  * </ul>
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>
2489  * </ul>
2490  */
2491 public TableItem getItem (Point point) {
2492         checkWidget ();
2493         if (point == null) error (SWT.ERROR_NULL_ARGUMENT);
2494         return getItemInPixels (DPIUtil.autoScaleUp(point));
2495 }
2496
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 ();
2501         pinfo.x = point.x;
2502         pinfo.y = point.y;
2503         if ((style & SWT.FULL_SELECTION) == 0) {
2504                 if (hooks (SWT.MeasureItem)) {
2505                         /*
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.
2510                         */
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;
2517                                 if (code != 0) {
2518                                         pinfo.x = rect.left;
2519                                         /*
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.
2524                                         */
2525                                         OS.SendMessage (handle, OS.LVM_SUBITEMHITTEST, 0, pinfo);
2526                                         if (pinfo.iItem < 0) pinfo.iItem = -1;
2527                                 }
2528                         }
2529                         if (pinfo.iItem != -1 && pinfo.iSubItem == 0) {
2530                                 if (hitTestSelection (pinfo.iItem, pinfo.x, pinfo.y)) {
2531                                         return _getItem (pinfo.iItem);
2532                                 }
2533                         }
2534                         return null;
2535                 }
2536         }
2537         OS.SendMessage (handle, OS.LVM_HITTEST, 0, pinfo);
2538         if (pinfo.iItem != -1) {
2539                 /*
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.
2546                 */
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 ();
2554                                         pt.x = pinfo.x;
2555                                         pt.y = pinfo.y;
2556                                         OS.MapWindowPoints (handle, 0, pt, 1);
2557                                         if (OS.PtInRect (rect, pt)) return null;
2558                                 }
2559                         }
2560                 }
2561                 return _getItem (pinfo.iItem);
2562         }
2563         return null;
2564 }
2565
2566 /**
2567  * Returns the number of items contained in the receiver.
2568  *
2569  * @return the number of items
2570  *
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>
2574  * </ul>
2575  */
2576 public int getItemCount () {
2577         checkWidget ();
2578         return (int)OS.SendMessage (handle, OS.LVM_GETITEMCOUNT, 0, 0);
2579 }
2580
2581 /**
2582  * Returns the height of the area which would be used to
2583  * display <em>one</em> of the items in the receiver.
2584  *
2585  * @return the height of one item
2586  *
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>
2590  * </ul>
2591  */
2592 public int getItemHeight () {
2593         checkWidget ();
2594         return DPIUtil.autoScaleDown(getItemHeightInPixels());
2595 }
2596
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);
2602 }
2603
2604 /**
2605  * Returns a (possibly empty) array of <code>TableItem</code>s which
2606  * are the items in the receiver.
2607  * <p>
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.
2611  * </p>
2612  *
2613  * @return the items in the receiver
2614  *
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>
2618  * </ul>
2619  */
2620 public TableItem [] getItems () {
2621         checkWidget ();
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);
2627                 }
2628         } else {
2629                 _getItems (result, count);
2630         }
2631         return result;
2632 }
2633
2634 /**
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.
2638  * <p>
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.
2643  * </p>
2644  *
2645  * @return the visibility state of the lines
2646  *
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>
2650  * </ul>
2651  */
2652 public boolean getLinesVisible () {
2653         checkWidget ();
2654         return _getLinesVisible();
2655 }
2656
2657 private boolean _getLinesVisible() {
2658         int bits = (int)OS.SendMessage (handle, OS.LVM_GETEXTENDEDLISTVIEWSTYLE, 0, 0);
2659         return (bits & OS.LVS_EX_GRIDLINES) != 0;
2660 }
2661
2662 /**
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.
2666  * <p>
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.
2670  * </p>
2671  * @return an array representing the selection
2672  *
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>
2676  * </ul>
2677  */
2678 public TableItem [] getSelection () {
2679         checkWidget ();
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);
2684         }
2685         return result;
2686 }
2687
2688 /**
2689  * Returns the number of selected items contained in the receiver.
2690  *
2691  * @return the number of selected items
2692  *
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>
2696  * </ul>
2697  */
2698 public int getSelectionCount () {
2699         checkWidget ();
2700         return (int)OS.SendMessage (handle, OS.LVM_GETSELECTEDCOUNT, 0, 0);
2701 }
2702
2703 /**
2704  * Returns the zero-relative index of the item which is currently
2705  * selected in the receiver, or -1 if no item is selected.
2706  *
2707  * @return the index of the selected item
2708  *
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>
2712  * </ul>
2713  */
2714 public int getSelectionIndex () {
2715         checkWidget ();
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;
2719         int i = -1;
2720         while ((i = (int)OS.SendMessage (handle, OS.LVM_GETNEXTITEM, i, OS.LVNI_SELECTED)) != -1) {
2721                 if (i == focusIndex) return i;
2722         }
2723         return selectedIndex;
2724 }
2725
2726 /**
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.
2730  * <p>
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.
2734  * </p>
2735  * @return the array of indices of the selected items
2736  *
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>
2740  * </ul>
2741  */
2742 public int [] getSelectionIndices () {
2743         checkWidget ();
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) {
2747                 result [j++] = i;
2748         }
2749         return result;
2750 }
2751
2752 /**
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.
2756  *
2757  * @return the sort indicator
2758  *
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>
2762  * </ul>
2763  *
2764  * @see #setSortColumn(TableColumn)
2765  *
2766  * @since 3.2
2767  */
2768 public TableColumn getSortColumn () {
2769         checkWidget ();
2770         return sortColumn;
2771 }
2772
2773 int getSortColumnPixel () {
2774         int pixel = OS.IsWindowEnabled (handle) || hasCustomBackground() ? getBackgroundPixel () : OS.GetSysColor (OS.COLOR_3DFACE);
2775         return getSlightlyDifferentBackgroundColor(pixel);
2776 }
2777
2778 /**
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>.
2782  *
2783  * @return the sort direction
2784  *
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>
2788  * </ul>
2789  *
2790  * @see #setSortDirection(int)
2791  *
2792  * @since 3.2
2793  */
2794 public int getSortDirection () {
2795         checkWidget ();
2796         return sortDirection;
2797 }
2798
2799 /**
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.
2803  *
2804  * @return the index of the top item
2805  *
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>
2809  * </ul>
2810  */
2811 public int getTopIndex () {
2812         checkWidget ();
2813         /*
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.
2818         */
2819         return Math.max (0, (int)OS.SendMessage (handle, OS.LVM_GETTOPINDEX, 0, 0));
2820 }
2821
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);
2827         }
2828         return false;
2829 }
2830
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;
2849         }
2850         return result;
2851 }
2852
2853 int imageIndex (Image image, int column) {
2854         if (image == null) return OS.I_IMAGENONE;
2855         if (column == 0) {
2856                 firstColumnImage = true;
2857         } else {
2858                 setSubImagesVisible (true);
2859         }
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 ();
2866                 /*
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.
2874                 */
2875                 int topIndex = getTopIndex ();
2876                 if (topIndex != 0) {
2877                         setRedraw (false);
2878                         setTopIndex (0);
2879                 }
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);
2884                 }
2885                 fixCheckboxImageList (false);
2886                 setItemHeight (false);
2887                 if (topIndex != 0) {
2888                         setTopIndex (topIndex);
2889                         setRedraw (true);
2890                 }
2891                 return index;
2892         }
2893         int index = imageList.indexOf (image);
2894         if (index != -1) return index;
2895         return imageList.add (image);
2896 }
2897
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);
2907                 return index;
2908         }
2909         int index = headerImageList.indexOf (image);
2910         if (index != -1) return index;
2911         return headerImageList.add (image);
2912 }
2913
2914 /**
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.
2919  *
2920  * @param column the search column
2921  * @return the index of the column
2922  *
2923  * @exception IllegalArgumentException <ul>
2924  *    <li>ERROR_NULL_ARGUMENT - if the column is null</li>
2925  * </ul>
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>
2929  * </ul>
2930  */
2931 public int indexOf (TableColumn column) {
2932         checkWidget ();
2933         if (column == null) error (SWT.ERROR_NULL_ARGUMENT);
2934         for (int i=0; i<columnCount; i++) {
2935                 if (columns [i] == column) return i;
2936         }
2937         return -1;
2938 }
2939
2940 /**
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.
2945  *
2946  * @param item the search item
2947  * @return the index of the item
2948  *
2949  * @exception IllegalArgumentException <ul>
2950  *    <li>ERROR_NULL_ARGUMENT - if the item is null</li>
2951  * </ul>
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>
2955  * </ul>
2956  */
2957 public int indexOf (TableItem item) {
2958         checkWidget ();
2959         if (item == null) error (SWT.ERROR_NULL_ARGUMENT);
2960         //TODO - find other loops that can be optimized
2961         if (keys == null) {
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;
2967                 }
2968                 if (lastIndexOf < count / 2) {
2969                         for (int i=0; i<count; i++) {
2970                                 if (_getItem (i, false) == item) return lastIndexOf = i;
2971                         }
2972                 } else {
2973                         for (int i=count - 1; i>=0; --i) {
2974                                 if (_getItem (i, false) == item) return lastIndexOf = i;
2975                         }
2976                 }
2977         } else {
2978                 for (int i=0; i<keyCount; i++) {
2979                         if (items [i] == item) return keys [i];
2980                 }
2981         }
2982         return -1;
2983 }
2984
2985 boolean isCustomToolTip () {
2986         return hooks (SWT.MeasureItem);
2987 }
2988
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();
2992 }
2993
2994 /**
2995  * Returns <code>true</code> if the item is selected,
2996  * and <code>false</code> otherwise.  Indices out of
2997  * range are ignored.
2998  *
2999  * @param index the index of the item
3000  * @return the selection state of the item at the index
3001  *
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>
3005  * </ul>
3006  */
3007 public boolean isSelected (int index) {
3008         checkWidget ();
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);
3015 }
3016
3017 @Override
3018 void register () {
3019         super.register ();
3020         if (hwndHeader != 0) display.addControl (hwndHeader, this);
3021 }
3022
3023 @Override
3024 void releaseChildren (boolean destroy) {
3025         if (_hasItems ()) {
3026                 int itemCount = (int)OS.SendMessage (handle, OS.LVM_GETITEMCOUNT, 0, 0);
3027                 if (keys == null) {
3028                         for (int i=0; i<itemCount; i++) {
3029                                 TableItem item = _getItem (i, false);
3030                                 if (item != null && !item.isDisposed ()) item.release (false);
3031                         }
3032                 } else {
3033                         for (int i=0; i<keyCount; i++) {
3034                                 TableItem item = items [i];
3035                                 if (item != null && !item.isDisposed ()) item.release (false);
3036                         }
3037                 }
3038                 _clearItems ();
3039         }
3040         if (columns != null) {
3041                 for (int i=0; i<columnCount; i++) {
3042                         TableColumn column = columns [i];
3043                         if (!column.isDisposed ()) column.release (false);
3044                 }
3045                 columns = null;
3046         }
3047         super.releaseChildren (destroy);
3048 }
3049
3050 @Override
3051 void releaseWidget () {
3052         super.releaseWidget ();
3053         customDraw = false;
3054         currentItem = null;
3055         if (imageList != null) {
3056                 OS.SendMessage (handle, OS.LVM_SETIMAGELIST, OS.LVSIL_SMALL, 0);
3057                 display.releaseImageList (imageList);
3058         }
3059         if (headerImageList != null) {
3060                 OS.SendMessage (hwndHeader, OS.HDM_SETIMAGELIST, 0, 0);
3061                 display.releaseImageList (headerImageList);
3062         }
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;
3069 }
3070
3071 /**
3072  * Removes the items from the receiver's list at the given
3073  * zero-relative indices.
3074  *
3075  * @param indices the array of indices of the items
3076  *
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>
3080  * </ul>
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>
3084  * </ul>
3085  */
3086 public void remove (int [] indices) {
3087         checkWidget ();
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);
3092         sort (newIndices);
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);
3097         }
3098         setDeferResize (true);
3099         int last = -1;
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);
3110                         --count;
3111                         last = index;
3112                 }
3113         }
3114         if (count == 0) setTableEmpty ();
3115         setDeferResize (false);
3116 }
3117
3118 /**
3119  * Removes the item from the receiver at the given
3120  * zero-relative index.
3121  *
3122  * @param index the index for the item
3123  *
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>
3126  * </ul>
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>
3130  * </ul>
3131  */
3132 public void remove (int index) {
3133         checkWidget ();
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);
3144         --count;
3145         if (count == 0) setTableEmpty ();
3146         setDeferResize (false);
3147 }
3148
3149 /**
3150  * Removes the items from the receiver which are
3151  * between the given zero-relative start and end
3152  * indices (inclusive).
3153  *
3154  * @param start the start of the range
3155  * @param end the end of the range
3156  *
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>
3159  * </ul>
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>
3163  * </ul>
3164  */
3165 public void remove (int start, int end) {
3166         checkWidget ();
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);
3171         }
3172         if (start == 0 && end == count - 1) {
3173                 removeAll ();
3174         } else {
3175                 setDeferResize (true);
3176                 int index = start;
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;
3184                         index++;
3185                 }
3186                 _removeItems (start, index, count);
3187                 if (index <= end) error (SWT.ERROR_ITEM_NOT_REMOVED);
3188                 /*
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.
3192                 */
3193                 //if (count - index == 0) setTableEmpty ();
3194                 setDeferResize (false);
3195         }
3196 }
3197
3198 /**
3199  * Removes all of the items from the receiver.
3200  *
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>
3204  * </ul>
3205  */
3206 public void removeAll () {
3207         checkWidget ();
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);
3212         }
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);
3218         setTableEmpty ();
3219         setDeferResize (false);
3220 }
3221
3222 /**
3223  * Removes the listener from the collection of listeners who will
3224  * be notified when the user changes the receiver's selection.
3225  *
3226  * @param listener the listener which should no longer be notified
3227  *
3228  * @exception IllegalArgumentException <ul>
3229  *    <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
3230  * </ul>
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>
3234  * </ul>
3235  *
3236  * @see SelectionListener
3237  * @see #addSelectionListener(SelectionListener)
3238  */
3239 public void removeSelectionListener(SelectionListener listener) {
3240         checkWidget ();
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);
3245 }
3246
3247 /**
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.
3250  * <p>
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.
3256  * </p>
3257  *
3258  * @param indices the array of indices for the items to select
3259  *
3260  * @exception IllegalArgumentException <ul>
3261  *    <li>ERROR_NULL_ARGUMENT - if the array of indices is null</li>
3262  * </ul>
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>
3266  * </ul>
3267  *
3268  * @see Table#setSelection(int[])
3269  */
3270 public void select (int [] indices) {
3271         checkWidget ();
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) {
3279                 /*
3280                 * An index of -1 will apply the change to all
3281                 * items.  Ensure that indices are greater than -1.
3282                 */
3283                 if (indices [i] >= 0) {
3284                         ignoreSelect = true;
3285                         OS.SendMessage (handle, OS.LVM_SETITEMSTATE, indices [i], lvItem);
3286                         ignoreSelect = false;
3287                 }
3288         }
3289 }
3290
3291 @Override
3292 void reskinChildren (int flags) {
3293         if (_hasItems ()) {
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);
3298                 }
3299         }
3300         if (columns != null) {
3301                 for (int i=0; i<columnCount; i++) {
3302                         TableColumn column = columns [i];
3303                         if (!column.isDisposed ()) column.reskin (flags);
3304                 }
3305         }
3306         super.reskinChildren (flags);
3307 }
3308
3309 /**
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.
3313  *
3314  * @param index the index of the item to select
3315  *
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>
3319  * </ul>
3320  */
3321 public void select (int index) {
3322         checkWidget ();
3323         /*
3324         * An index of -1 will apply the change to all
3325         * items.  Ensure that index is greater than -1.
3326         */
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;
3334 }
3335
3336 /**
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.
3340  * <p>
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.
3347  * </p>
3348  *
3349  * @param start the start of the range
3350  * @param end the end of the range
3351  *
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>
3355  * </ul>
3356  *
3357  * @see Table#setSelection(int,int)
3358  */
3359 public void select (int start, int end) {
3360         checkWidget ();
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) {
3367                 selectAll ();
3368         } else {
3369                 /*
3370                 * An index of -1 will apply the change to all
3371                 * items.  Indices must be greater than -1.
3372                 */
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;
3380                 }
3381         }
3382 }
3383
3384 /**
3385  * Selects all of the items in the receiver.
3386  * <p>
3387  * If the receiver is single-select, do nothing.
3388  * </p>
3389  *
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>
3393  * </ul>
3394  */
3395 public void selectAll () {
3396         checkWidget ();
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;
3405 }
3406
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;
3411         int clrTextBk = -1;
3412         if (OS.IsAppThemed ()) {
3413                 if (sortColumn != null && sortDirection != SWT.NONE) {
3414                         if (findImageControl () == null) {
3415                                 if (indexOf (sortColumn) == nmcd.iSubItem) {
3416                                         clrTextBk = getSortColumnPixel ();
3417                                 }
3418                         }
3419                 }
3420         }
3421         clrTextBk = item.cellBackground != null ? item.cellBackground [nmcd.iSubItem] : -1;
3422         if (clrTextBk == -1) clrTextBk = item.background;
3423         /*
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.
3427         */
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;
3441         }
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);
3448                         } else {
3449                                 drawSelected = (style & SWT.HIDE_SELECTION) == 0;
3450                                 data.foreground = OS.GetTextColor (hDC);
3451                                 data.background = clrSelectionBk = OS.GetSysColor (OS.COLOR_3DFACE);
3452                         }
3453                         if (explorerTheme) {
3454                                 data.foreground = clrText != -1 ? clrText : getForegroundPixel ();
3455                         }
3456                 } else {
3457                         drawBackground = clrTextBk != -1;
3458                         /*
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
3463                         * set the color.
3464                         */
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 ();
3470                         }
3471                         data.foreground = clrText != -1 ? clrText : OS.GetTextColor (hDC);
3472                         data.background = clrTextBk != -1 ? clrTextBk : OS.GetBkColor (hDC);
3473                 }
3474         } else {
3475                 data.foreground = OS.GetSysColor (OS.COLOR_GRAYTEXT);
3476                 data.background = OS.GetSysColor (OS.COLOR_3DFACE);
3477                 if (selected) clrSelectionBk = data.background;
3478         }
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 ();
3485         event.item = item;
3486         event.gc = gc;
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;
3495                         }
3496                 }
3497         }
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);
3506         event.gc = null;
3507         int clrSelectionText = data.foreground;
3508         gc.dispose ();
3509         OS.RestoreDC (hDC, nSavedDC);
3510         if (isDisposed () || item.isDisposed ()) return;
3511         if (event.doit) {
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;
3517         } else {
3518                 ignoreDrawForeground = ignoreDrawBackground = ignoreDrawSelection = ignoreDrawFocus = ignoreDrawHot = true;
3519         }
3520         if (drawSelected) {
3521                 if (ignoreDrawSelection) {
3522                         ignoreDrawHot = true;
3523                         if (nmcd.iSubItem == 0 || (style & SWT.FULL_SELECTION) != 0) {
3524                                 selectionForeground = clrSelectionText;
3525                         }
3526                         nmcd.uItemState &= ~OS.CDIS_SELECTED;
3527                         OS.MoveMemory (lParam, nmcd, NMLVCUSTOMDRAW.sizeof);
3528                 }
3529         } else {
3530                 if (ignoreDrawSelection) {
3531                         nmcd.uItemState |= OS.CDIS_SELECTED;
3532                         OS.MoveMemory (lParam, nmcd, NMLVCUSTOMDRAW.sizeof);
3533                 }
3534         }
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);
3540                 }
3541         }
3542         focusRect = null;
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);
3550                         }
3551                         if (!ignoreDrawFocus) {
3552                                 nmcd.uItemState &= ~OS.CDIS_FOCUS;
3553                                 OS.MoveMemory (lParam, nmcd, NMLVCUSTOMDRAW.sizeof);
3554                                 focusRect = textRect;
3555                         }
3556                 }
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;
3576                                 } else {
3577                                         rect.right += EXPLORER_EXTRA;
3578                                         pClipRect.right += EXPLORER_EXTRA;
3579                                 }
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);
3586                         }
3587                 } else {
3588                         if (!ignoreDrawSelection && clrSelectionBk != -1) fillBackground (hDC, clrSelectionBk, textRect);
3589                 }
3590         }
3591         if (focused && ignoreDrawFocus) {
3592                 nmcd.uItemState &= ~OS.CDIS_FOCUS;
3593                 OS.MoveMemory (lParam, nmcd, NMLVCUSTOMDRAW.sizeof);
3594         }
3595         if (ignoreDrawForeground) {
3596                 RECT clipRect = item.getBounds ((int)nmcd.dwItemSpec, nmcd.iSubItem, true, true, true, false, hDC);
3597                 OS.SaveDC (hDC);
3598                 OS.SelectClipRgn (hDC, 0);
3599                 OS.ExcludeClipRect (hDC, clipRect.left, clipRect.top, clipRect.right, clipRect.bottom);
3600         }
3601 }
3602
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 ();
3615         event.item = item;
3616         event.index = column;
3617         event.gc = gc;
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);
3622         event.gc = null;
3623         //int newTextClr = data.foreground;
3624         gc.dispose ();
3625         OS.RestoreDC (nmcd.hdc, nSavedDC);
3626         return event;
3627 }
3628
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 ();
3637         event.item = item;
3638         event.gc = gc;
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;
3652                         } else {
3653                                 drawSelected = (style & SWT.HIDE_SELECTION) == 0;
3654                         }
3655                 }
3656         }
3657         if (drawSelected) event.detail |= SWT.SELECTED;
3658         sendEvent (SWT.MeasureItem, event);
3659         event.gc = null;
3660         gc.dispose ();
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);
3667                 }
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);
3671                 /*
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
3675                  */
3676                 if (!settingItemHeight && boundsInPixels.height > itemHeight) {
3677                         settingItemHeight = true;
3678                         setItemHeight (boundsInPixels.height);
3679                         settingItemHeight = false;
3680                 }
3681         }
3682         return event;
3683 }
3684
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);
3691                 }
3692                 return LRESULT.ZERO;
3693         }
3694
3695         /*
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.
3702         *
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.
3706         */
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)) {
3713                         /*
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.
3718                         */
3719                         if (OS.SendMessage (handle, OS.LVM_SUBITEMHITTEST, 0, pinfo) < 0) {
3720                                 int count = (int)OS.SendMessage (handle, OS.LVM_GETITEMCOUNT, 0, 0);
3721                                 if (count != 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;
3727                                         if (code != 0) {
3728                                                 pinfo.x = rect.left;
3729                                                 /*
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.
3734                                                 */
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);
3738                                         }
3739                                 }
3740                         } else {
3741                                 if (pinfo.iSubItem != 0) pinfo.iItem = -1;
3742                         }
3743                 }
3744         }
3745
3746         /*
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
3752         * below.
3753         */
3754         OS.SetFocus (handle);
3755
3756         /*
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.
3762         */
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);
3767                         }
3768                         return LRESULT.ZERO;
3769                 }
3770         }
3771
3772         /*
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
3779         * it as selected.
3780         */
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) {
3790                         forceSelect = true;
3791                 }
3792         }
3793
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;
3803                                 }
3804                         }
3805                 }
3806         }
3807
3808         /*
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.
3815         */
3816         boolean dragDetect = (state & DRAG_DETECT) != 0 && hooks (SWT.DragDetect);
3817         if (!dragDetect) {
3818                 int flags = OS.LVHT_ONITEMICON | OS.LVHT_ONITEMLABEL;
3819                 dragDetect = pinfo.iItem == -1 || (pinfo.flags & flags) == 0;
3820                 if (fullRowSelect) dragDetect = true;
3821         }
3822
3823         /*
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.
3827         */
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);
3832         }
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);
3842         }
3843
3844         if (dragStarted || display.dragCancelled) {
3845                 if (!display.captureChanged && !isDisposed ()) {
3846                         if (OS.GetCapture () != handle) OS.SetCapture (handle);
3847                 }
3848         } else {
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;
3853                 }
3854                 if (fakeMouseUp) {
3855                         sendMouseEvent (SWT.MouseUp, button, handle, msg, wParam, lParam);
3856                 }
3857         }
3858         return new LRESULT (code);
3859 }
3860
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);
3866         /*
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.
3870         */
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;
3880         }
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;
3887                                 } else {
3888                                         data.foreground = OS.GetSysColor (OS.COLOR_HIGHLIGHTTEXT);
3889                                 }
3890                                 data.background = OS.GetSysColor (OS.COLOR_HIGHLIGHT);
3891                         } else {
3892                                 drawSelected = (style & SWT.HIDE_SELECTION) == 0;
3893                                 data.foreground = OS.GetTextColor (hDC);
3894                                 data.background = OS.GetSysColor (OS.COLOR_3DFACE);
3895                         }
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 ();
3900                         }
3901                 } else {
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;
3907                         /*
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
3912                         * set the color.
3913                         */
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 ();
3919                         }
3920                         data.foreground = clrText != -1 ? clrText : OS.GetTextColor (hDC);
3921                         data.background = clrTextBk != -1 ? clrTextBk : OS.GetBkColor (hDC);
3922                 }
3923         } else {
3924                 data.foreground = OS.GetSysColor (OS.COLOR_GRAYTEXT);
3925                 data.background = OS.GetSysColor (OS.COLOR_3DFACE);
3926         }
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 ();
3932         event.item = item;
3933         event.gc = gc;
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;
3942                         }
3943                 }
3944         }
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;
3955         event.gc = null;
3956         gc.dispose ();
3957         OS.RestoreDC (hDC, nSavedDC);
3958 }
3959
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 ();
3972         event.item = item;
3973         event.index = column;
3974         event.gc = gc;
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);
3979         event.gc = null;
3980         gc.dispose ();
3981         OS.RestoreDC (nmcd.hdc, nSavedDC);
3982         return event;
3983 }
3984
3985 @Override
3986 void setBackgroundImage (long hBitmap) {
3987         super.setBackgroundImage (hBitmap);
3988         if (hBitmap != 0) {
3989                 setBackgroundTransparent (true);
3990         } else {
3991                 if (!hooks (SWT.MeasureItem) && !hooks (SWT.EraseItem) && !hooks (SWT.PaintItem)) {
3992                         setBackgroundTransparent (false);
3993                 }
3994         }
3995 }
3996
3997 @Override
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);
4007                 }
4008         }
4009         /*
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.
4013         */
4014         OS.InvalidateRect (handle, null, true);
4015 }
4016
4017 void setBackgroundTransparent (boolean transparent) {
4018         /*
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.
4024         *
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
4030         * column.
4031         */
4032         int oldPixel = (int)OS.SendMessage (handle, OS.LVM_GETBKCOLOR, 0, 0);
4033         if (transparent) {
4034                 if (oldPixel != OS.CLR_NONE) {
4035                         /*
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.
4039                         */
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);
4043
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);
4048                         }
4049
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);
4054                                         /*
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.
4058                                         */
4059                                         OS.InvalidateRect (handle, null, true);
4060                                 }
4061                         }
4062                 }
4063         } else {
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);
4073                         }
4074
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);
4080                                 }
4081                         }
4082
4083                         /* Set LVM_SETSELECTEDCOLUMN */
4084                         if ((sortDirection & (SWT.UP | SWT.DOWN)) != 0) {
4085                                 if (sortColumn != null && !sortColumn.isDisposed ()) {
4086                                         int column = indexOf (sortColumn);
4087                                         if (column != -1) {
4088                                                 OS.SendMessage (handle, OS.LVM_SETSELECTEDCOLUMN, column, 0);
4089                                                 /*
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.
4093                                                 */
4094                                                 OS.InvalidateRect (handle, null, true);
4095                                         }
4096                                 }
4097                         }
4098                 }
4099         }
4100 }
4101
4102 @Override
4103 void setBoundsInPixels (int x, int y, int width, int height, int flags, boolean defer) {
4104         /*
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.
4114         *
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
4117         * time.
4118         */
4119         setDeferResize (true);
4120         super.setBoundsInPixels (x, y, width, height, flags, false);
4121         setDeferResize (false);
4122 }
4123
4124 /**
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
4128  * were added.
4129  *
4130  * @param order the new order to display the items
4131  *
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>
4135  * </ul>
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>
4139  * </ul>
4140  *
4141  * @see Table#getColumnOrder()
4142  * @see TableColumn#getMoveable()
4143  * @see TableColumn#setMoveable(boolean)
4144  * @see SWT#Move
4145  *
4146  * @since 3.1
4147  */
4148 public void setColumnOrder (int [] order) {
4149         checkWidget ();
4150         if (order == null) error (SWT.ERROR_NULL_ARGUMENT);
4151         if (columnCount == 0) {
4152                 if (order.length != 0) error (SWT.ERROR_INVALID_ARGUMENT);
4153                 return;
4154         }
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;
4166         }
4167         if (reorder) {
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]);
4172                 }
4173                 OS.SendMessage (handle, OS.LVM_SETCOLUMNORDERARRAY, order.length, order);
4174                 /*
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.
4178                 */
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);
4190                                 }
4191                         }
4192                 }
4193         }
4194 }
4195
4196 void setCustomDraw (boolean customDraw) {
4197         if (this.customDraw == customDraw) return;
4198         if (!this.customDraw && customDraw && currentItem != null) {
4199                 OS.InvalidateRect (handle, null, true);
4200         }
4201         this.customDraw = customDraw;
4202 }
4203
4204 void setDeferResize (boolean defer) {
4205         if (defer) {
4206                 if (resizeCount++ == 0) {
4207                         wasResized = false;
4208                         /*
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
4212                         * turned off.
4213                         */
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);
4218                                 }
4219                         }
4220                 }
4221         } else {
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);
4229                                 }
4230                         }
4231                         if (wasResized) {
4232                                 wasResized = false;
4233                                 setResizeChildren (false);
4234                                 sendEvent (SWT.Resize);
4235                                 if (isDisposed ()) return;
4236                                 if (layout != null) {
4237                                         markLayout (false, false);
4238                                         updateLayout (false, false);
4239                                 }
4240                                 setResizeChildren (true);
4241                         }
4242                 }
4243         }
4244 }
4245
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);
4258         int clrBackground;
4259         if (OS.IsAppThemed ()) {
4260                 Control control = findBackgroundControl ();
4261                 if (control == null) control = this;
4262                 clrBackground = control.getBackgroundPixel ();
4263         } else {
4264                 clrBackground = 0x020000FF;
4265                 if ((clrBackground & 0xFFFFFF) == OS.GetSysColor (OS.COLOR_WINDOW)) {
4266                         clrBackground = 0x0200FF00;
4267                 }
4268         }
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()) {
4279                 /*
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.
4283                  */
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);
4288         }
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);
4308         } else {
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);
4324         }
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);
4330         } else {
4331                 OS.ImageList_AddMasked (hStateList, hBitmap, clrBackground);
4332         }
4333         OS.DeleteObject (hBitmap);
4334         /*
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.
4342         */
4343         int topIndex = getTopIndex ();
4344         if (fixScroll && topIndex != 0) {
4345                 setRedraw (false);
4346                 setTopIndex (0);
4347         }
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);
4351         /*
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.
4356         */
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);
4361                 setRedraw (true);
4362         }
4363 }
4364
4365 void setFocusIndex (int index) {
4366 //      checkWidget ();
4367         /*
4368         * An index of -1 will apply the change to all
4369         * items.  Ensure that index is greater than -1.
4370         */
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);
4379 }
4380
4381 @Override
4382 public void setFont (Font font) {
4383         checkWidget ();
4384         /*
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.
4392         */
4393         int topIndex = getTopIndex ();
4394         if (topIndex != 0) {
4395                 setRedraw (false);
4396                 setTopIndex (0);
4397         }
4398         if (itemHeight != -1) {
4399                 int bits = OS.GetWindowLong (handle, OS.GWL_STYLE);
4400                 OS.SetWindowLong (handle, OS.GWL_STYLE, bits | OS.LVS_OWNERDRAWFIXED);
4401         }
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);
4406         }
4407         setScrollWidth (null, true);
4408         if (topIndex != 0) {
4409                 setTopIndex (topIndex);
4410                 setRedraw (true);
4411         }
4412
4413         /*
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.
4417         */
4418         OS.InvalidateRect (hwndHeader, null, true);
4419 }
4420
4421 @Override
4422 void setForegroundPixel (int pixel) {
4423         /*
4424         * The Windows table control uses CLR_DEFAULT to indicate
4425         * that it is using the default foreground color.  This
4426         * is undocumented.
4427         */
4428         if (pixel == -1) pixel = OS.CLR_DEFAULT;
4429         OS.SendMessage (handle, OS.LVM_SETTEXTCOLOR, 0, pixel);
4430
4431         /*
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.
4435         */
4436         OS.InvalidateRect (handle, null, true);
4437         OS.InvalidateRect (hwndHeader, null, true);
4438 }
4439
4440 /**
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.
4443  * <p>
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.
4447  * </p>
4448  * @param color the new color (or null)
4449  *
4450  * @exception IllegalArgumentException <ul>
4451  *    <li>ERROR_INVALID_ARGUMENT - if the argument has been disposed</li>
4452  * </ul>
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>
4456  * </ul>
4457  * @since 3.106
4458  */
4459 public void setHeaderBackground (Color color) {
4460         checkWidget ();
4461         int pixel = -1;
4462         if (color != null) {
4463                 if (color.isDisposed()) error(SWT.ERROR_INVALID_ARGUMENT);
4464                 pixel = color.handle;
4465         }
4466         if (pixel == headerBackground) return;
4467         headerBackground = pixel;
4468         if (getHeaderVisible()) {
4469                 OS.InvalidateRect (hwndHeader, null, true);
4470         }
4471 }
4472
4473 /**
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.
4476  * <p>
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.
4480  * </p>
4481  * @param color the new color (or null)
4482  *
4483  * @exception IllegalArgumentException <ul>
4484  *    <li>ERROR_INVALID_ARGUMENT - if the argument has been disposed</li>
4485  * </ul>
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>
4489  * </ul>
4490  * @since 3.106
4491  */
4492 public void setHeaderForeground (Color color) {
4493         checkWidget ();
4494         int pixel = -1;
4495         if (color != null) {
4496                 if (color.isDisposed()) error(SWT.ERROR_INVALID_ARGUMENT);
4497                 pixel = color.handle;
4498         }
4499         if (pixel == headerForeground) return;
4500         headerForeground = pixel;
4501         if (getHeaderVisible()) {
4502                 OS.InvalidateRect (hwndHeader, null, true);
4503         }
4504 }
4505
4506 /**
4507  * Marks the receiver's header as visible if the argument is <code>true</code>,
4508  * and marks it invisible otherwise.
4509  * <p>
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.
4513  * </p>
4514  *
4515  * @param show the new visibility state
4516  *
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>
4520  * </ul>
4521  */
4522 public void setHeaderVisible (boolean show) {
4523         checkWidget ();
4524         int newBits = OS.GetWindowLong (handle, OS.GWL_STYLE);
4525         newBits &= ~OS.LVS_NOCOLUMNHEADER;
4526         if (!show) newBits |= OS.LVS_NOCOLUMNHEADER;
4527         /*
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.
4532         */
4533         int oldIndex = getTopIndex ();
4534         OS.SetWindowLong (handle, OS.GWL_STYLE, newBits);
4535
4536         /*
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.
4544         */
4545         int newIndex = getTopIndex ();
4546         if (newIndex != 0) {
4547                 setRedraw (false);
4548                 setTopIndex (0);
4549         }
4550         setTopIndex (oldIndex);
4551         if (newIndex != 0) {
4552                 setRedraw (true);
4553         }
4554         updateHeaderToolTips ();
4555 }
4556
4557 /**
4558  * Sets the number of items contained in the receiver.
4559  *
4560  * @param count the number of items
4561  *
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>
4565  * </ul>
4566  *
4567  * @since 3.0
4568  */
4569 public void setItemCount (int count) {
4570         checkWidget ();
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);
4577         int index = count;
4578         while (index < itemCount) {
4579                 TableItem item = _getItem (index, false);
4580                 if (item != null && !item.isDisposed ()) item.release (false);
4581                 if (!isVirtual) {
4582                         ignoreSelect = ignoreShrink = true;
4583                         long code = OS.SendMessage (handle, OS.LVM_DELETEITEM, count, 0);
4584                         ignoreSelect = ignoreShrink = false;
4585                         if (code == 0) break;
4586                 }
4587                 index++;
4588         }
4589         if (index < itemCount) error (SWT.ERROR_ITEM_NOT_REMOVED);
4590         _setItemCount (count, itemCount);
4591         if (isVirtual) {
4592                 int flags = OS.LVSICF_NOINVALIDATEALL | OS.LVSICF_NOSCROLL;
4593                 OS.SendMessage (handle, OS.LVM_SETITEMCOUNT, count, flags);
4594                 /*
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.
4600                 */
4601                 if (count == 0 && itemCount != 0) {
4602                         OS.InvalidateRect (handle, null, true);
4603                 }
4604         } else {
4605                 for (int i=itemCount; i<count; i++) {
4606                         new TableItem (this, SWT.NONE, i, true);
4607                 }
4608         }
4609         if (!isVirtual) setRedraw (true);
4610         if (itemCount == 0) setScrollWidth (null, false);
4611         setDeferResize (false);
4612 }
4613
4614 void setItemHeight (boolean fixScroll) {
4615         /*
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.
4623         *
4624         */
4625         int topIndex = getTopIndex ();
4626         if (fixScroll && topIndex != 0) {
4627                 setRedraw (false);
4628                 setTopIndex (0);
4629         }
4630         if (itemHeight == -1) {
4631                 /*
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
4635                 * height.
4636                 */
4637                 long hFont = OS.SendMessage (handle, OS.WM_GETFONT, 0, 0);
4638                 OS.SendMessage (handle, OS.WM_SETFONT, hFont, 0);
4639         } else {
4640                 /*
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.
4645                 */
4646                 forceResize ();
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);
4658         }
4659         if (fixScroll && topIndex != 0) {
4660                 setTopIndex (topIndex);
4661                 setRedraw (true);
4662         }
4663 }
4664
4665 /**
4666  * Sets the height of the area which would be used to
4667  * display <em>one</em> of the items in the table.
4668  *
4669  * @param itemHeight the height of one item
4670  *
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>
4674  * </ul>
4675  *
4676  * @since 3.2
4677  */
4678 /*public*/ void setItemHeight (int itemHeight) {
4679         checkWidget ();
4680         if (itemHeight < -1) error (SWT.ERROR_INVALID_ARGUMENT);
4681         this.itemHeight = itemHeight;
4682         setItemHeight (true);
4683         setScrollWidth (null, true);
4684 }
4685
4686 /**
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.
4690  * <p>
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.
4694  * </p>
4695  *
4696  * @param show the new visibility state
4697  *
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>
4701  * </ul>
4702  */
4703 public void setLinesVisible (boolean show) {
4704         checkWidget ();
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);
4708 }
4709
4710 @Override
4711 public void setRedraw (boolean redraw) {
4712         checkWidget ();
4713         /*
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
4720          * back on.
4721          */
4722         if (drawCount == 0) {
4723                 int bits = OS.GetWindowLong (handle, OS.GWL_STYLE);
4724                 if ((bits & OS.WS_VISIBLE) == 0) state |= HIDDEN;
4725         }
4726         if (redraw) {
4727                 if (--drawCount == 0) {
4728                         /*
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.
4732                         *
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.
4737                         *
4738                         * This code is intentionally commented.
4739                         */
4740 //                      subclass ();
4741
4742                         /* Set the width of the horizontal scroll bar */
4743                         setScrollWidth (null, true);
4744
4745                         /*
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.
4753                         */
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) {
4758                                 state &= ~HIDDEN;
4759                                 OS.ShowWindow (handle, OS.SW_HIDE);
4760                         } else {
4761                                 int flags = OS.RDW_ERASE | OS.RDW_FRAME | OS.RDW_INVALIDATE | OS.RDW_ALLCHILDREN;
4762                                 OS.RedrawWindow (handle, null, 0, flags);
4763                         }
4764                         setDeferResize (false);
4765                 }
4766         } else {
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);
4770
4771                         /*
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.
4775                         *
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.
4780                         *
4781                         * This code is intentionally commented.
4782                         */
4783 //                      unsubclass ();
4784                 }
4785         }
4786 }
4787
4788 void setScrollWidth (int width) {
4789         if (width != (int)OS.SendMessage (handle, OS.LVM_GETCOLUMNWIDTH, 0, 0)) {
4790                 /*
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.
4798                 */
4799                 boolean redraw = false;
4800                 if (hooks (SWT.MeasureItem)) {
4801                         redraw = getDrawing () && OS.IsWindowVisible (handle);
4802                 }
4803                 if (redraw) OS.DefWindowProc (handle, OS.WM_SETREDRAW, 0, 0);
4804                 OS.SendMessage (handle, OS.LVM_SETCOLUMNWIDTH, 0, width);
4805                 if (redraw) {
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);
4809                 }
4810         }
4811 }
4812
4813 boolean setScrollWidth (TableItem item, boolean force) {
4814         if (currentItem != null) {
4815                 if (currentItem != item) fixScrollWidth = true;
4816                 return false;
4817         }
4818         if (!force && (!getDrawing () || !OS.IsWindowVisible (handle))) {
4819                 fixScrollWidth = true;
4820                 return false;
4821         }
4822         fixScrollWidth = false;
4823         /*
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.
4827         */
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;
4833                         long hFont = -1;
4834                         if (item != null) {
4835                                 string = item.text;
4836                                 imageIndent = Math.max (imageIndent, item.imageIndent);
4837                                 hFont = item.fontHandle (0);
4838                         } else {
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);
4844                                 }
4845                         }
4846                         if (string != null && string.length () != 0) {
4847                                 if (hFont != -1) {
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);
4857                                 } else {
4858                                         TCHAR buffer = new TCHAR (getCodePage (), string, true);
4859                                         newWidth = Math.max (newWidth, (int)OS.SendMessage (handle, OS.LVM_GETSTRINGWIDTH, 0, buffer));
4860                                 }
4861                         }
4862                         if (item != null) break;
4863                         index++;
4864                 }
4865                 /*
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
4873                 * space.
4874                 */
4875                 if (newWidth == 0) {
4876                         char [] buffer = {' ', '\0'};
4877                         newWidth = Math.max (newWidth, (int)OS.SendMessage (handle, OS.LVM_GETSTRINGWIDTH, 0, buffer));
4878                 }
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;
4884                 }
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];
4890                 } else {
4891                         /*
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
4899                         * pixel larger.
4900                         */
4901                         newWidth++;
4902                 }
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);
4907                         return true;
4908                 }
4909         }
4910         return false;
4911 }
4912
4913 /**
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.
4917  * <p>
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.
4921  * </p>
4922  *
4923  * @param indices the indices of the items to select
4924  *
4925  * @exception IllegalArgumentException <ul>
4926  *    <li>ERROR_NULL_ARGUMENT - if the array of indices is null</li>
4927  * </ul>
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>
4931  * </ul>
4932  *
4933  * @see Table#deselectAll()
4934  * @see Table#select(int[])
4935  */
4936 public void setSelection (int [] indices) {
4937         checkWidget ();
4938         if (indices == null) error (SWT.ERROR_NULL_ARGUMENT);
4939         deselectAll ();
4940         int length = indices.length;
4941         if (length == 0 || ((style & SWT.SINGLE) != 0 && length > 1)) return;
4942         select (indices);
4943         int focusIndex = indices [0];
4944         if (focusIndex != -1) setFocusIndex (focusIndex);
4945         showSelection ();
4946 }
4947
4948 /**
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.
4952  * <p>
4953  * If the item is not in the receiver, then it is ignored.
4954  * </p>
4955  *
4956  * @param item the item to select
4957  *
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>
4961  * </ul>
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>
4965  * </ul>
4966  *
4967  * @since 3.2
4968  */
4969 public void setSelection (TableItem  item) {
4970         checkWidget ();
4971         if (item == null) error (SWT.ERROR_NULL_ARGUMENT);
4972         setSelection (new TableItem [] {item});
4973 }
4974
4975 /**
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.
4979  * <p>
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.
4983  * </p>
4984  *
4985  * @param items the array of items
4986  *
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>
4990  * </ul>
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>
4994  * </ul>
4995  *
4996  * @see Table#deselectAll()
4997  * @see Table#select(int[])
4998  * @see Table#setSelection(int[])
4999  */
5000 public void setSelection (TableItem [] items) {
5001         checkWidget ();
5002         if (items == null) error (SWT.ERROR_NULL_ARGUMENT);
5003         deselectAll ();
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]);
5009                 if (index != -1) {
5010                         select (focusIndex = index);
5011                 }
5012         }
5013         if (focusIndex != -1) setFocusIndex (focusIndex);
5014         showSelection ();
5015 }
5016
5017 /**
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.
5021  *
5022  * @param index the index of the item to select
5023  *
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>
5027  * </ul>
5028  *
5029  * @see Table#deselectAll()
5030  * @see Table#select(int)
5031  */
5032 public void setSelection (int index) {
5033         checkWidget ();
5034         deselectAll ();
5035         select (index);
5036         if (index != -1) setFocusIndex (index);
5037         showSelection ();
5038 }
5039
5040 /**
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.
5045  * <p>
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.
5050  * </p>
5051  *
5052  * @param start the start index of the items to select
5053  * @param end the end index of the items to select
5054  *
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>
5058  * </ul>
5059  *
5060  * @see Table#deselectAll()
5061  * @see Table#select(int,int)
5062  */
5063 public void setSelection (int start, int end) {
5064         checkWidget ();
5065         deselectAll ();
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);
5073         showSelection ();
5074 }
5075
5076 /**
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.
5080  *
5081  * @param column the column used by the sort indicator or <code>null</code>
5082  *
5083  * @exception IllegalArgumentException <ul>
5084  *    <li>ERROR_INVALID_ARGUMENT - if the column is disposed</li>
5085  * </ul>
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>
5089  * </ul>
5090  *
5091  * @since 3.2
5092  */
5093 public void setSortColumn (TableColumn column) {
5094         checkWidget ();
5095         if (column != null && column.isDisposed ()) error (SWT.ERROR_INVALID_ARGUMENT);
5096         if (sortColumn != null && !sortColumn.isDisposed ()) {
5097                 sortColumn.setSortDirection (SWT.NONE);
5098         }
5099         sortColumn = column;
5100         if (sortColumn != null && sortDirection != SWT.NONE) {
5101                 sortColumn.setSortDirection (sortDirection);
5102         }
5103 }
5104
5105 /**
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>.
5108  *
5109  * @param direction the direction of the sort indicator
5110  *
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>
5114  * </ul>
5115  *
5116  * @since 3.2
5117  */
5118 public void setSortDirection (int direction) {
5119         checkWidget ();
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);
5124         }
5125 }
5126
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);
5132 }
5133
5134 void setTableEmpty () {
5135         if (imageList != null) {
5136                 /*
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
5143                 * list is removed.
5144                 */
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);
5152                 }
5153                 OS.ImageList_Destroy (hImageList);
5154                 display.releaseImageList (imageList);
5155                 imageList = null;
5156                 if (itemHeight != -1) setItemHeight (false);
5157         }
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);
5164                 }
5165         }
5166         _initItems ();
5167         if (columnCount == 0) {
5168                 OS.SendMessage (handle, OS.LVM_SETCOLUMNWIDTH, 0, 0);
5169                 setScrollWidth (null, false);
5170         }
5171 }
5172
5173 /**
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.
5177  *
5178  * @param index the index of the top item
5179  *
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>
5183  * </ul>
5184  */
5185 public void setTopIndex (int index) {
5186         checkWidget ();
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);
5190
5191         /*
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
5204         * index visible.
5205         */
5206
5207         /*
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.
5212         */
5213         if (OS.SendMessage (handle, OS.LVM_GETCOUNTPERPAGE, 0, 0) <= 0) {
5214                 /*
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.
5222                 */
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);
5226                 }
5227                 return;
5228         }
5229
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);
5238 }
5239
5240 /**
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.
5244  *
5245  * @param column the column to be shown
5246  *
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>
5250  * </ul>
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>
5254  * </ul>
5255  *
5256  * @since 3.0
5257  */
5258 public void showColumn (TableColumn column) {
5259         checkWidget ();
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;
5265         /*
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.
5273         */
5274         RECT itemRect = new RECT ();
5275         itemRect.left = OS.LVIR_BOUNDS;
5276         if (index == 0) {
5277                 itemRect.top = 1;
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;
5284         } else {
5285                 itemRect.top = index;
5286                 ignoreCustomDraw = true;
5287                 OS.SendMessage (handle, OS.LVM_GETSUBITEMRECT, -1, itemRect);
5288                 ignoreCustomDraw = false;
5289         }
5290         /*
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.
5296         */
5297         int oldPos = 0;
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);
5303                 oldPos = info.nPos;
5304         }
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);
5310         } else {
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);
5315                 }
5316         }
5317         /*
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.
5323         */
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);
5333                 }
5334         }
5335 }
5336
5337 void showItem (int index) {
5338         if (!painted && hooks (SWT.MeasureItem)) hitTestSelection (index, 0, 0);
5339         /*
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.
5347         */
5348         long counterPage = OS.SendMessage (handle, OS.LVM_GETCOUNTPERPAGE, 0, 0);
5349         if (counterPage <= 0) {
5350                 /*
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.
5358                 */
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);
5362                 }
5363         } else {
5364                 /*
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
5368                  */
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);
5372                 }
5373         }
5374 }
5375
5376 /**
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.
5380  *
5381  * @param item the item to be shown
5382  *
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>
5386  * </ul>
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>
5390  * </ul>
5391  *
5392  * @see Table#showSelection()
5393  */
5394 public void showItem (TableItem item) {
5395         checkWidget ();
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);
5400 }
5401
5402 /**
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.
5406  *
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>
5410  * </ul>
5411  *
5412  * @see Table#showItem(TableItem)
5413  */
5414 public void showSelection () {
5415         checkWidget ();
5416         int index = (int)OS.SendMessage (handle, OS.LVM_GETNEXTITEM, -1, OS.LVNI_SELECTED);
5417         if (index != -1) {
5418                 /*
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
5424                  *
5425                  * Make sure above fix is only applied to the active shell, see bug 450391.
5426                  */
5427                 if (display.getActiveShell() == getShell() && (style & SWT.NO_SCROLL) == 0
5428                                         && (verticalBar == null || !verticalBar.isVisible())) {
5429                         showItem (0);
5430                 } else {
5431                         showItem (index);
5432                 }
5433         }
5434 }
5435
5436 /*public*/ void sort () {
5437         checkWidget ();
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));
5447 //                      } else {
5448 //                              return item2.getText (index).compareTo (item1.getText (index));
5449 //                      }
5450 //              }
5451 //      };
5452 //      Arrays.sort (items, 0, itemCount, comparator);
5453 //      redraw ();
5454 }
5455
5456 @Override
5457 void subclass () {
5458         super.subclass ();
5459         if (HeaderProc != 0) {
5460                 OS.SetWindowLongPtr (hwndHeader, OS.GWLP_WNDPROC, display.windowProc);
5461         }
5462 }
5463
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);
5467         return insetRect;
5468 }
5469
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);
5473         return toolRect;
5474 }
5475
5476 @Override
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;
5484                 }
5485         }
5486         return super.toolTipText (hdr);
5487 }
5488
5489 @Override
5490 void unsubclass () {
5491         super.unsubclass ();
5492         if (HeaderProc != 0) {
5493                 OS.SetWindowLongPtr (hwndHeader, OS.GWLP_WNDPROC, HeaderProc);
5494         }
5495 }
5496
5497 @Override
5498 void update (boolean all) {
5499 //      checkWidget ();
5500         /*
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.
5507         *
5508         * NOTE: The header tooltip can subclass the header proc so the
5509         * current proc must be restored or header tooltips stop working.
5510         */
5511         long oldHeaderProc = 0, oldTableProc = 0;
5512         boolean fixSubclass = isOptimizedRedraw ();
5513         if (fixSubclass) {
5514                 oldTableProc = OS.SetWindowLongPtr (handle, OS.GWLP_WNDPROC, TableProc);
5515                 oldHeaderProc = OS.SetWindowLongPtr (hwndHeader, OS.GWLP_WNDPROC, HeaderProc);
5516         }
5517         super.update (all);
5518         if (fixSubclass) {
5519                 OS.SetWindowLongPtr (handle, OS.GWLP_WNDPROC, oldTableProc);
5520                 OS.SetWindowLongPtr (hwndHeader, OS.GWLP_WNDPROC, oldHeaderProc);
5521         }
5522 }
5523
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);
5542                 }
5543         }
5544 }
5545
5546 @Override
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 ();
5556                 }
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);
5561         }
5562         Point pt = toDisplayInPixels (x, y);
5563         event.setLocationInPixels(pt.x, pt.y);
5564 }
5565
5566 void updateMoveable () {
5567         int index = 0;
5568         while (index < columnCount) {
5569                 if (columns [index].moveable) break;
5570                 index++;
5571         }
5572         int newBits = index < columnCount ? OS.LVS_EX_HEADERDRAGDROP : 0;
5573         OS.SendMessage (handle, OS.LVM_SETEXTENDEDLISTVIEWSTYLE, OS.LVS_EX_HEADERDRAGDROP, newBits);
5574 }
5575
5576 @Override
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;
5584                 } else {
5585                         bits &= ~OS.WS_EX_LAYOUTRTL;
5586                 }
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);
5595         }
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);
5604                         if (item != null) {
5605                                 Image image = item.image;
5606                                 if (image != null) {
5607                                         int index = imageList.indexOf (image);
5608                                         if (index == -1) imageList.add (image);
5609                                 }
5610                         }
5611                 }
5612                 long hImageList = imageList.getHandle ();
5613                 OS.SendMessage (handle, OS.LVM_SETIMAGELIST, OS.LVSIL_SMALL, hImageList);
5614         }
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);
5635                                                         }
5636                                                 }
5637                                         }
5638                                 }
5639                         }
5640                         long hHeaderImageList = headerImageList.getHandle ();
5641                         OS.SendMessage (hwndHeader, OS.HDM_SETIMAGELIST, 0, hHeaderImageList);
5642                 }
5643         }
5644 }
5645
5646 @Override
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);
5653                                 }
5654                         }
5655                 }
5656                 OS.InvalidateRect (handle, null, true);
5657                 return true;
5658         }
5659         return false;
5660 }
5661
5662 @Override
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;
5667         /*
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.
5671         */
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;
5675         return bits;
5676 }
5677
5678 @Override
5679 TCHAR windowClass () {
5680         return TableClass;
5681 }
5682
5683 @Override
5684 long windowProc () {
5685         return TableProc;
5686 }
5687
5688 @Override
5689 long windowProc (long hwnd, int msg, long wParam, long lParam) {
5690         if (handle == 0) return 0;
5691         if (hwnd != handle) {
5692                 switch (msg) {
5693                         case OS.WM_CONTEXTMENU: {
5694                                 LRESULT result = wmContextMenu (hwnd, wParam, lParam);
5695                                 if (result != null) return result.value;
5696                                 break;
5697                         }
5698                         case OS.WM_MOUSELEAVE: {
5699                                 /*
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.
5705                                 */
5706                                 updateHeaderToolTips ();
5707                                 updateHeaderToolTips ();
5708                                 break;
5709                         }
5710                         case OS.WM_NOTIFY: {
5711                                 NMHDR hdr = new NMHDR ();
5712                                 OS.MoveMemory (hdr, lParam, NMHDR.sizeof);
5713                                 switch (hdr.code) {
5714                                         case OS.TTN_SHOW:
5715                                         case OS.TTN_POP:
5716                                         case OS.TTN_GETDISPINFO:
5717                                                 return OS.SendMessage (handle, msg, wParam, lParam);
5718                                 }
5719                                 break;
5720                         }
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);
5730                                                 pinfo.x = pt.x;
5731                                                 pinfo.y = pt.y;
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));
5737                                                                 return 1;
5738                                                         }
5739                                                 }
5740                                         }
5741                                 }
5742                                 break;
5743                         }
5744                 }
5745                 return callWindowProc (hwnd, msg, wParam, lParam);
5746         }
5747         if (msg == Display.DI_GETDRAGIMAGE) {
5748                 /*
5749                 * Bug in Windows.  On Vista, for some reason, DI_GETDRAGIMAGE
5750                 * returns an image that does not contain strings.
5751                 *
5752                 * Bug in Windows. For custom draw control the window origin the
5753                 * in HDC is wrong.
5754                 *
5755                 * The fix for both cases is to create the image using PrintWindow().
5756                 */
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;
5772                         } else {
5773                                 rect.right = clientRect.right;
5774                                 rect.left = Math.max (clientRect.left, rect.right - width);
5775                         }
5776                 }
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;
5786                 }
5787                 OS.GetRgnBox (hRgn, rect);
5788
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);
5817
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;
5827                 }
5828                 OS.MoveMemory (lParam, shdi, SHDRAGIMAGE.sizeof);
5829                 return 1;
5830         }
5831         return super.windowProc (hwnd, msg, wParam, lParam);
5832 }
5833
5834 @Override
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) {
5839                 case ' ':
5840                         if ((style & SWT.CHECK) != 0) {
5841                                 int index = (int)OS.SendMessage (handle, OS.LVM_GETNEXTITEM, -1, OS.LVNI_FOCUSED);
5842                                 if (index != -1) {
5843                                         TableItem item = _getItem (index);
5844                                         item.setChecked (!item.getChecked (), true);
5845                                         OS.NotifyWinEvent (OS.EVENT_OBJECT_FOCUS, handle, OS.OBJID_CLIENT, index + 1);
5846                                 }
5847                         }
5848                         /*
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.
5853                         */
5854                         long code = callWindowProc (handle, OS.WM_KEYDOWN, wParam, lParam);
5855                         return new LRESULT (code);
5856                 case SWT.CR:
5857                         /*
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.
5863                         */
5864                         int index = (int)OS.SendMessage (handle, OS.LVM_GETNEXTITEM, -1, OS.LVNI_FOCUSED);
5865                         if (index != -1) {
5866                                 Event event = new Event ();
5867                                 event.item = _getItem (index);
5868                                 sendSelectionEvent (SWT.DefaultSelection, event, false);
5869                         }
5870                         return LRESULT.ZERO;
5871         }
5872         return result;
5873 }
5874
5875 @Override
5876 LRESULT WM_CONTEXTMENU (long wParam, long lParam) {
5877         /*
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
5883         * mouse down.
5884         *
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.
5888         */
5889         if (!display.runDragDrop) return LRESULT.ZERO;
5890         return super.WM_CONTEXTMENU (wParam, lParam);
5891 }
5892
5893 @Override
5894 LRESULT WM_ERASEBKGND (long wParam, long lParam) {
5895         LRESULT result = super.WM_ERASEBKGND (wParam, lParam);
5896         if (findImageControl () != null) return LRESULT.ONE;
5897         return result;
5898 }
5899
5900 @Override
5901 LRESULT WM_GETOBJECT (long wParam, long lParam) {
5902         /*
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.
5906         */
5907         if ((style & SWT.CHECK) != 0) {
5908                 if (accessible == null) accessible = new_Accessible (this);
5909         }
5910         return super.WM_GETOBJECT (wParam, lParam);
5911 }
5912
5913 @Override
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) {
5918                 case OS.VK_SPACE:
5919                         /*
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.
5924                         */
5925                         return LRESULT.ZERO;
5926                 case OS.VK_ADD:
5927                         if (OS.GetKeyState (OS.VK_CONTROL) < 0) {
5928                                 int index = 0;
5929                                 while (index < columnCount) {
5930                                         if (!columns [index].getResizable ()) break;
5931                                         index++;
5932                                 }
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 ()) {
5939                                                         column.pack ();
5940                                                 }
5941                                         }
5942                                         return LRESULT.ZERO;
5943                                 }
5944                         }
5945                         break;
5946                 case OS.VK_PRIOR:
5947                 case OS.VK_NEXT:
5948                 case OS.VK_HOME:
5949                 case OS.VK_END:
5950                         /*
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.
5956                         *
5957                         * NOTE: The header tooltip can subclass the header proc so the
5958                         * current proc must be restored or header tooltips stop working.
5959                         */
5960                         long oldHeaderProc = 0, oldTableProc = 0;
5961                         long hwndHeader = OS.SendMessage (handle, OS.LVM_GETHEADER, 0, 0);
5962                         boolean fixSubclass = isOptimizedRedraw ();
5963                         if (fixSubclass) {
5964                                 oldTableProc = OS.SetWindowLongPtr (handle, OS.GWLP_WNDPROC, TableProc);
5965                                 oldHeaderProc = OS.SetWindowLongPtr (hwndHeader, OS.GWLP_WNDPROC, HeaderProc);
5966                         }
5967                         long code = callWindowProc (handle, OS.WM_KEYDOWN, wParam, lParam);
5968                         result = code == 0 ? LRESULT.ZERO : new LRESULT (code);
5969                         if (fixSubclass) {
5970                                 OS.SetWindowLongPtr (handle, OS.GWLP_WNDPROC, oldTableProc);
5971                                 OS.SetWindowLongPtr (hwndHeader, OS.GWLP_WNDPROC, oldHeaderProc);
5972                         }
5973                         //FALL THROUGH
5974                 case OS.VK_UP:
5975                 case OS.VK_DOWN:
5976                         OS.SendMessage (handle, OS.WM_CHANGEUISTATE, OS.UIS_INITIALIZE, 0);
5977                         break;
5978         }
5979         return result;
5980 }
5981
5982 @Override
5983 LRESULT WM_KILLFOCUS (long wParam, long lParam) {
5984         LRESULT result = super.WM_KILLFOCUS (wParam, lParam);
5985         /*
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
5989         * the table.
5990         */
5991         if (imageList != null || (style & SWT.CHECK) != 0) {
5992                 OS.InvalidateRect (handle, null, false);
5993         }
5994         return result;
5995 }
5996
5997 @Override
5998 LRESULT WM_LBUTTONDBLCLK (long wParam, long lParam) {
5999
6000         /*
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.
6006         */
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);
6017                 }
6018                 return LRESULT.ZERO;
6019         }
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);
6023         }
6024
6025         /* Look for check/uncheck */
6026         if ((style & SWT.CHECK) != 0) {
6027                 /*
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.
6034                 */
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);
6040                         }
6041                 }
6042         }
6043         return LRESULT.ZERO;
6044 }
6045
6046 @Override
6047 LRESULT WM_LBUTTONDOWN (long wParam, long lParam) {
6048         /*
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
6053         * mouse capture.
6054         */
6055         LRESULT result = sendMouseDownEvent (SWT.MouseDown, 1, OS.WM_LBUTTONDOWN, wParam, lParam);
6056         if (result == LRESULT.ZERO) return result;
6057
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);
6063                 /*
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.
6070                 */
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);
6077                         }
6078                 }
6079         }
6080         return result;
6081 }
6082
6083 @Override
6084 LRESULT WM_MOUSEHOVER (long wParam, long lParam) {
6085         /*
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.
6091         */
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;
6097 }
6098
6099 @Override
6100 LRESULT WM_PAINT (long wParam, long lParam) {
6101         if ((state & DISPOSE_SENT) != 0) return LRESULT.ZERO;
6102
6103         _checkShrink();
6104         if (fixScrollWidth) setScrollWidth (null, true);
6105         return super.WM_PAINT (wParam, lParam);
6106 }
6107
6108 @Override
6109 LRESULT WM_RBUTTONDBLCLK (long wParam, long lParam) {
6110         /*
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.
6116         */
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);
6126         }
6127         if (!display.captureChanged && !isDisposed ()) {
6128                 if (OS.GetCapture () != handle) OS.SetCapture (handle);
6129         }
6130         return LRESULT.ZERO;
6131 }
6132
6133 @Override
6134 LRESULT WM_RBUTTONDOWN (long wParam, long lParam) {
6135         /*
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
6140         * mouse capture.
6141         */
6142         return sendMouseDownEvent (SWT.MouseDown, 3, OS.WM_RBUTTONDOWN, wParam, lParam);
6143 }
6144
6145 @Override
6146 LRESULT WM_SETFOCUS (long wParam, long lParam) {
6147         LRESULT result = super.WM_SETFOCUS (wParam, lParam);
6148         /*
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
6154         * the table.
6155         */
6156         if (imageList != null || (style & SWT.CHECK) != 0) {
6157                 OS.InvalidateRect (handle, null, false);
6158         }
6159
6160         /*
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.
6166         */
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);
6170         if (index == -1) {
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;
6177         }
6178         return result;
6179 }
6180
6181 @Override
6182 LRESULT WM_SETFONT (long wParam, long lParam) {
6183         LRESULT result = super.WM_SETFONT (wParam, lParam);
6184         if (result != null) return result;
6185
6186         /*
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.
6192         *
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.
6196         */
6197         OS.SendMessage (hwndHeader, OS.WM_SETFONT, 0, lParam);
6198
6199         if (headerToolTipHandle != 0) {
6200                 OS.SendMessage (headerToolTipHandle, OS.WM_SETFONT, wParam, lParam);
6201         }
6202         return result;
6203 }
6204
6205 @Override
6206 LRESULT WM_SETREDRAW (long wParam, long lParam) {
6207         LRESULT result = super.WM_SETREDRAW (wParam, lParam);
6208         if (result != null) return result;
6209         /*
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
6213         * turned off.
6214         */
6215         if (wParam == 1) {
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);
6219                         }
6220                 }
6221         }
6222         /*
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.
6227         */
6228         OS.DefWindowProc (handle, OS.WM_SETREDRAW, wParam, lParam);
6229         long code = callWindowProc (handle, OS.WM_SETREDRAW, wParam, lParam);
6230         if (wParam == 0) {
6231                 if ((int)OS.SendMessage (handle, OS.LVM_GETBKCOLOR, 0, 0) == OS.CLR_NONE) {
6232                         OS.SendMessage (handle, OS.LVM_SETBKCOLOR, 0, 0xFFFFFF);
6233                 }
6234         }
6235         return code == 0 ? LRESULT.ZERO : new LRESULT (code);
6236 }
6237
6238 @Override
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);
6243         }
6244         if (resizeCount != 0) {
6245                 wasResized = true;
6246                 return null;
6247         }
6248         return super.WM_SIZE (wParam, lParam);
6249 }
6250
6251 @Override
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 ());
6257         } else {
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);
6262                         }
6263                 }
6264         }
6265         return result;
6266 }
6267
6268 @Override
6269 LRESULT WM_HSCROLL (long wParam, long lParam) {
6270         /*
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.
6276         */
6277         int oldPos = 0;
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);
6283                 oldPos = info.nPos;
6284         }
6285
6286         /*
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
6293         *
6294         * NOTE: The header tooltip can subclass the header proc so the
6295         * current proc must be restored or header tooltips stop working.
6296         */
6297         long oldHeaderProc = 0, oldTableProc = 0;
6298         long hwndHeader = OS.SendMessage (handle, OS.LVM_GETHEADER, 0, 0);
6299         boolean fixSubclass = isOptimizedRedraw ();
6300         if (fixSubclass) {
6301                 oldTableProc = OS.SetWindowLongPtr (handle, OS.GWLP_WNDPROC, TableProc);
6302                 oldHeaderProc = OS.SetWindowLongPtr (hwndHeader, OS.GWLP_WNDPROC, HeaderProc);
6303         }
6304
6305         /*
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
6312         * faster.
6313         */
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);
6319                 }
6320         }
6321         if (fixScroll) OS.DefWindowProc (handle, OS.WM_SETREDRAW, 0, 0);
6322         LRESULT result = super.WM_HSCROLL (wParam, lParam);
6323         if (fixScroll) {
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);
6327                 /*
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.
6332                 */
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++) {
6338                                 visible [i] = true;
6339                                 headerRect.top = 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);
6345                                 }
6346                         }
6347                         try {
6348                                 columnVisible = visible;
6349                                 OS.UpdateWindow (handle);
6350                         } finally {
6351                                 columnVisible = null;
6352                         }
6353                 }
6354         }
6355
6356         if (fixSubclass) {
6357                 OS.SetWindowLongPtr (handle, OS.GWLP_WNDPROC, oldTableProc);
6358                 OS.SetWindowLongPtr (hwndHeader, OS.GWLP_WNDPROC, oldHeaderProc);
6359         }
6360
6361         /*
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.
6367         */
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);
6379                 }
6380         }
6381         return result;
6382 }
6383
6384 @Override
6385 LRESULT WM_VSCROLL (long wParam, long lParam) {
6386         /*
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.
6393         *
6394         * NOTE: The header tooltip can subclass the header proc so the
6395         * current proc must be restored or header tooltips stop working.
6396         */
6397         long oldHeaderProc = 0, oldTableProc = 0;
6398         long hwndHeader = OS.SendMessage (handle, OS.LVM_GETHEADER, 0, 0);
6399         boolean fixSubclass = isOptimizedRedraw ();
6400         if (fixSubclass) {
6401                 oldTableProc = OS.SetWindowLongPtr (handle, OS.GWLP_WNDPROC, TableProc);
6402                 oldHeaderProc = OS.SetWindowLongPtr (hwndHeader, OS.GWLP_WNDPROC, HeaderProc);
6403         }
6404
6405         /*
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
6412         * faster.
6413         */
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);
6419                 }
6420         }
6421         if (fixScroll) OS.DefWindowProc (handle, OS.WM_SETREDRAW, 0, 0);
6422         LRESULT result = super.WM_VSCROLL (wParam, lParam);
6423         if (fixScroll) {
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);
6427                 /*
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.
6432                 */
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++) {
6438                                 visible [i] = true;
6439                                 headerRect.top = 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);
6445                                 }
6446                         }
6447                         try {
6448                                 columnVisible = visible;
6449                                 OS.UpdateWindow (handle);
6450                         } finally {
6451                                 columnVisible = null;
6452                         }
6453                 }
6454         }
6455
6456         if (fixSubclass) {
6457                 OS.SetWindowLongPtr (handle, OS.GWLP_WNDPROC, oldTableProc);
6458                 OS.SetWindowLongPtr (hwndHeader, OS.GWLP_WNDPROC, oldHeaderProc);
6459         }
6460
6461         /*
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.
6466         */
6467         if (_getLinesVisible()) {
6468                 int code = OS.LOWORD (wParam);
6469                 switch (code) {
6470                         case OS.SB_ENDSCROLL:
6471                         case OS.SB_THUMBPOSITION:
6472                         case OS.SB_THUMBTRACK:
6473                         case OS.SB_TOP:
6474                         case OS.SB_BOTTOM:
6475                                 break;
6476                         case OS.SB_LINEDOWN:
6477                         case OS.SB_LINEUP:
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;
6489                                 } else {
6490                                         clientRect.bottom = clientRect.top + itemHeight + GRID_WIDTH;
6491                                 }
6492                                 OS.InvalidateRect (handle, clientRect, true);
6493                                 break;
6494                         case OS.SB_PAGEDOWN:
6495                         case OS.SB_PAGEUP:
6496                                 OS.InvalidateRect (handle, null, true);
6497                                 break;
6498                 }
6499         }
6500         return result;
6501 }
6502
6503 @Override
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);
6511         } else {
6512                 struct.itemHeight = itemHeight;
6513         }
6514         OS.MoveMemory (lParam, struct, MEASUREITEMSTRUCT.sizeof);
6515         return null;
6516 }
6517
6518 @Override
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;
6524         }
6525         if (hdr.hwndFrom == hwndHeader) {
6526                 LRESULT result = wmNotifyHeader (hdr, wParam, lParam);
6527                 if (result != null) return result;
6528         }
6529         return super.wmNotify (hdr, wParam, lParam);
6530 }
6531
6532 @Override
6533 LRESULT wmNotifyChild (NMHDR hdr, long wParam, long lParam) {
6534         switch (hdr.code) {
6535                 case OS.LVN_ODFINDITEM: {
6536                         if ((style & SWT.VIRTUAL) != 0) return new LRESULT (-1);
6537                         break;
6538                 }
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;
6547                                 }
6548                         }
6549                         break;
6550                 }
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);
6555
6556                         if (columnVisible != null && !columnVisible [plvfi.iSubItem]) {
6557                                 break;
6558                         }
6559
6560                         /*
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
6566                         * null.
6567                         *
6568                         * NOTE: Force the item to be created if it does not exist.
6569                         */
6570                         TableItem item = _getItem (plvfi.iItem);
6571                         if (item == null) break;
6572
6573                         /*
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.
6576                         *
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.
6582                         *
6583                         * On both cases, Rather than answering the data right away, queue a
6584                         * redraw for later.
6585                         */
6586                         if (ignoreShrink) {
6587                                 /*
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.
6592                                 */
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);
6600                                         break;
6601                                 } else {
6602                                         if ((style & SWT.VIRTUAL) != 0 && !item.cached) {
6603                                                 OS.SendMessage (handle, OS.LVM_REDRAWITEMS, plvfi.iItem, plvfi.iItem);
6604                                                 break;
6605                                         }
6606                                 }
6607                         }
6608
6609                         /*
6610                         * The cached flag is used by both virtual and non-virtual
6611                         * tables to indicate that Windows has asked at least once
6612                         * for a table item.
6613                         */
6614                         if (!item.cached) {
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);
6621                                         }
6622                                 }
6623                                 item.cached = true;
6624                         }
6625                         if ((plvfi.mask & OS.LVIF_TEXT) != 0) {
6626                                 String string = null;
6627                                 if (plvfi.iSubItem == 0) {
6628                                         string = item.text;
6629                                 } else {
6630                                         String [] strings  = item.strings;
6631                                         if (strings != null && plvfi.iSubItem < strings.length) string = strings [plvfi.iSubItem];
6632                                 }
6633                                 if (string != null) {
6634                                         /*
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).
6644                                         *
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
6648                                         * is set.
6649                                         */
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$
6653                                                 length = 1;
6654                                         }
6655                                         if (length > 1 && (state & HAS_AUTO_DIRECTION) != 0) {
6656                                                 switch (BidiUtil.resolveTextDirection(string)) {
6657                                                         case SWT.LEFT_TO_RIGHT:
6658                                                                 string = LRE + string;
6659                                                                 length++;
6660                                                                 break;
6661                                                         case SWT.RIGHT_TO_LEFT:
6662                                                                 string = RLE + string;
6663                                                                 length++;
6664                                                                 break;
6665                                                 }
6666                                         }
6667                                         char [] buffer = display.tableBuffer;
6668                                         if (buffer == null || plvfi.cchTextMax > buffer.length) {
6669                                                 buffer = display.tableBuffer = new char [plvfi.cchTextMax];
6670                                         }
6671                                         string.getChars (0, length, buffer, 0);
6672                                         if (tipRequested) {
6673                                                 /*
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.
6677                                                  */
6678                                                 int shift = 0;
6679                                                 for (int i = 0; i < length; i++) {
6680                                                         switch (buffer [i]) {
6681                                                                 case '\r':
6682                                                                 case '\n':
6683                                                                         shift++;
6684                                                                         break;
6685                                                                 default:
6686                                                                         if (shift != 0) buffer [i - shift] = buffer [i];
6687                                                         }
6688                                                 }
6689                                                 length -= shift;
6690                                         }
6691                                         buffer [length++] = 0;
6692                                         OS.MoveMemory (plvfi.pszText, buffer, length * 2);
6693                                 }
6694                         }
6695                         boolean move = false;
6696                         if ((plvfi.mask & OS.LVIF_IMAGE) != 0) {
6697                                 Image image = null;
6698                                 if (plvfi.iSubItem == 0) {
6699                                         image = item.image;
6700                                 } else {
6701                                         Image [] images = item.images;
6702                                         if (images != null && plvfi.iSubItem < images.length) image = images [plvfi.iSubItem];
6703                                 }
6704                                 if (image != null) {
6705                                         plvfi.iImage = imageIndex (image, plvfi.iSubItem);
6706                                         move = true;
6707                                 }
6708                         }
6709                         if ((plvfi.mask & OS.LVIF_STATE) != 0) {
6710                                 if (plvfi.iSubItem == 0) {
6711                                         int state = 1;
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;
6717                                         move = true;
6718                                 }
6719                         }
6720                         if ((plvfi.mask & OS.LVIF_INDENT) != 0) {
6721                                 if (plvfi.iSubItem == 0) {
6722                                         plvfi.iIndent = item.imageIndent;
6723                                         move = true;
6724                                 }
6725                         }
6726                         if (move) OS.MoveMemory (lParam, plvfi, NMLVDISPINFO.sizeof);
6727                         break;
6728                 }
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) {
6733                                 /*
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.
6737                                 */
6738                                 if (OS.IsWindowEnabled (handle)) {
6739                                         /*
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.
6744                                         */
6745                                         if (!explorerTheme || columnCount != 0) break;
6746                                 }
6747                         }
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);
6757                         }
6758                         break;
6759                 }
6760                 case OS.LVN_MARQUEEBEGIN: {
6761                         if ((style & SWT.SINGLE) != 0) return LRESULT.ONE;
6762                         if (hooks (SWT.MouseDown) || hooks (SWT.MouseUp)) {
6763                                 return LRESULT.ONE;
6764                         }
6765                         if ((style & SWT.RIGHT_TO_LEFT) != 0) {
6766                                 if (findImageControl () != null) return LRESULT.ONE;
6767                         }
6768                         break;
6769                 }
6770                 case OS.LVN_BEGINDRAG:
6771                 case OS.LVN_BEGINRDRAG: {
6772                         if (OS.GetKeyState (OS.VK_LBUTTON) >= 0) break;
6773                         dragStarted = true;
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);
6780                         }
6781                         break;
6782                 }
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);
6789                         }
6790                         break;
6791                 }
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);
6800                         }
6801                         break;
6802                 }
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);
6808                         }
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) {
6814                                                 wasSelected = true;
6815                                         } else {
6816                                                 boolean oldSelected = (pnmlv.uOldState & OS.LVIS_SELECTED) != 0;
6817                                                 boolean newSelected = (pnmlv.uNewState & OS.LVIS_SELECTED) != 0;
6818                                                 if (oldSelected != newSelected) wasSelected = true;
6819                                         }
6820                                 }
6821                         }
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);
6825                                 if (count != 0) {
6826                                         forceResize ();
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);
6845                                         }
6846                                 }
6847                         }
6848                         break;
6849                 }
6850         }
6851         return super.wmNotifyChild (hdr, wParam, lParam);
6852 }
6853
6854 LRESULT wmNotifyHeader (NMHDR hdr, long wParam, long lParam) {
6855         /*
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
6867         * both.
6868         */
6869         switch (hdr.code) {
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 ()) {
6877                                 return LRESULT.ONE;
6878                         }
6879                         ignoreColumnMove = true;
6880                         if (hdr.code == OS.HDN_DIVIDERDBLCLICK) {
6881                                 if (column != null && hooks (SWT.MeasureItem)) {
6882                                         column.pack ();
6883                                         return LRESULT.ONE;
6884                                 }
6885                         }
6886                         break;
6887                 }
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);
6896                                 }
6897                                 case OS.CDDS_ITEMPREPAINT: {
6898                                         // draw background
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);
6906                                         }
6907                                         long brush = OS.CreateSolidBrush(pixel);
6908                                         OS.FillRect(nmcd.hdc, rect, brush);
6909                                         OS.DeleteObject(brush);
6910
6911                                         return new LRESULT(OS.CDRF_SKIPDEFAULT); // if we got here, we will paint everything ourself
6912                                 }
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);
6918
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;
6928                                                 }
6929
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;
6935                                                         /*
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.
6938                                                          */
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);
6946                                                         }
6947                                                         OS.SelectObject (nmcd.hdc, oldPen);
6948                                                         OS.DeleteObject (pen);
6949                                                 }
6950
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);
6960
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);
6969
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;
6976                                                         }
6977                                                 }
6978
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;
6987                                                         gc.dispose ();
6988                                                 }
6989
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();
6998                                                         textRect.left = x;
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);
7003                                                 }
7004                                         }
7005
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);
7014                                         }
7015
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);
7023                                         }
7024
7025                                         return new LRESULT(OS.CDRF_DODEFAULT);
7026                                 }
7027                         }
7028                         break;
7029                 }
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);
7035                                 }
7036                         }
7037                         ignoreColumnMove = false;
7038                         break;
7039                 }
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;
7051                                                 return LRESULT.ONE;
7052                                         }
7053                                 }
7054                                 headerItemDragging = true;
7055                         }
7056                         break;
7057                 }
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);
7071                                         int index = 0;
7072                                         while (index < order.length) {
7073                                                 if (order [index] == phdn.iItem) break;
7074                                                 index++;
7075                                         }
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);
7085                                                 }
7086                                         }
7087                                 }
7088                         }
7089                         break;
7090                 }
7091                 case OS.HDN_ITEMCHANGED: {
7092                         /*
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.
7099                         */
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);
7107                                 }
7108                         }
7109                         lastWidth = width;
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;
7122                                                         /*
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.
7127                                                         */
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);
7138                                                                 }
7139                                                                 if (nextColumn == column) moved = true;
7140                                                         }
7141                                                 }
7142                                         }
7143                                 }
7144                         }
7145                         break;
7146                 }
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);
7153                         }
7154                         break;
7155                 }
7156         }
7157         return null;
7158 }
7159
7160 LRESULT wmNotifyToolTip (NMHDR hdr, long wParam, long lParam) {
7161         switch (hdr.code) {
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);
7168                         }
7169                         break;
7170                 }
7171                 case OS.TTN_GETDISPINFO:
7172                 case OS.TTN_SHOW: {
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);
7185                                 pinfo.x = pt.x;
7186                                 pinfo.y = pt.y;
7187                                 /*
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.
7192                                 */
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);
7212                                                 } else {
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);
7218                                                         }
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) {
7223                                                                 //TEMPORARY CODE
7224                                                                 String string = " ";
7225 //                                                              String string = null;
7226 //                                                              if (pinfo.iSubItem == 0) {
7227 //                                                                      string = item.text;
7228 //                                                              } else {
7229 //                                                                      String [] strings  = item.strings;
7230 //                                                                      if (strings != null) string = strings [pinfo.iSubItem];
7231 //                                                              }
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);
7238                                                                 }
7239                                                         }
7240                                                 }
7241                                         }
7242                                         if (hFont != -1) hFont = OS.SelectObject (hDC, hFont);
7243                                         if (newFont != 0) OS.SelectObject (hDC, oldFont);
7244                                         OS.ReleaseDC (handle, hDC);
7245                                 }
7246                         }
7247                         return new LRESULT (code);
7248                 }
7249         }
7250         return null;
7251 }
7252
7253 LRESULT wmNotifyToolTip (NMTTCUSTOMDRAW nmcd, long lParam) {
7254         switch (nmcd.dwDrawStage) {
7255                 case OS.CDDS_PREPAINT: {
7256                         if (isCustomToolTip ()) {
7257                                 //TEMPORARY CODE
7258 //                              nmcd.uDrawFlags |= OS.DT_CALCRECT;
7259 //                              OS.MoveMemory (lParam, nmcd, NMTTCUSTOMDRAW.sizeof);
7260                                 return new LRESULT (OS.CDRF_NOTIFYPOSTPAINT | OS.CDRF_NEWFONT);
7261                         }
7262                         break;
7263                 }
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);
7270                         pinfo.x = pt.x;
7271                         pinfo.y = pt.y;
7272                         /*
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.
7277                         */
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;
7289                                         if (event.doit) {
7290                                                 drawForeground = (event.detail & SWT.FOREGROUND) != 0;
7291                                         } else {
7292                                                 drawForeground = false;
7293                                         }
7294                                 }
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);
7317                                         } else {
7318                                                 x += INSET + 2;
7319                                         }
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;
7327                                                 }
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);
7332                                         }
7333                                         gc.dispose ();
7334                                         OS.RestoreDC (nmcd.hdc, nSavedDC);
7335                                 }
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);
7339                                 }
7340                                 OS.SelectObject (hDC, oldFont);
7341                                 OS.ReleaseDC (handle, hDC);
7342                         }
7343                 }
7344         }
7345         return null;
7346 }
7347 }