KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > eclipse > swt > widgets > Table


1 /*******************************************************************************
2  * Copyright (c) 2000, 2007 IBM Corporation and others.
3  * All rights reserved. This program and the accompanying materials
4  * are made available under the terms of the Eclipse Public License v1.0
5  * which accompanies this distribution, and is available at
6  * http://www.eclipse.org/legal/epl-v10.html
7  *
8  * Contributors:
9  * IBM Corporation - initial API and implementation
10  *******************************************************************************/

11 package org.eclipse.swt.widgets;
12
13
14 //import java.util.*;
15

16 import org.eclipse.swt.internal.*;
17 import org.eclipse.swt.internal.win32.*;
18 import org.eclipse.swt.*;
19 import org.eclipse.swt.graphics.*;
20 import org.eclipse.swt.events.*;
21
22 /**
23  * Instances of this class implement a selectable user interface
24  * object that displays a list of images and strings and issues
25  * notification when selected.
26  * <p>
27  * The item children that may be added to instances of this class
28  * must be of type <code>TableItem</code>.
29  * </p><p>
30  * Style <code>VIRTUAL</code> is used to create a <code>Table</code> whose
31  * <code>TableItem</code>s are to be populated by the client on an on-demand basis
32  * instead of up-front. This can provide significant performance improvements for
33  * tables that are very large or for which <code>TableItem</code> population is
34  * expensive (for example, retrieving values from an external source).
35  * </p><p>
36  * Here is an example of using a <code>Table</code> with style <code>VIRTUAL</code>:
37  * <code><pre>
38  * final Table table = new Table (parent, SWT.VIRTUAL | SWT.BORDER);
39  * table.setItemCount (1000000);
40  * table.addListener (SWT.SetData, new Listener () {
41  * public void handleEvent (Event event) {
42  * TableItem item = (TableItem) event.item;
43  * int index = table.indexOf (item);
44  * item.setText ("Item " + index);
45  * System.out.println (item.getText ());
46  * }
47  * });
48  * </pre></code>
49  * </p><p>
50  * Note that although this class is a subclass of <code>Composite</code>,
51  * it does not make sense to add <code>Control</code> children to it,
52  * or set a layout on it.
53  * </p><p>
54  * <dl>
55  * <dt><b>Styles:</b></dt>
56  * <dd>SINGLE, MULTI, CHECK, FULL_SELECTION, HIDE_SELECTION, VIRTUAL</dd>
57  * <dt><b>Events:</b></dt>
58  * <dd>Selection, DefaultSelection, SetData, MeasureItem, EraseItem, PaintItem</dd>
59  * </dl>
60  * </p><p>
61  * Note: Only one of the styles SINGLE, and MULTI may be specified.
62  * </p><p>
63  * IMPORTANT: This class is <em>not</em> intended to be subclassed.
64  * </p>
65  */

66
67 public class Table extends Composite {
68     TableItem [] items;
69     TableColumn [] columns;
70     int columnCount, customCount;
71     ImageList imageList, headerImageList;
72     TableItem currentItem;
73     TableColumn sortColumn;
74     boolean ignoreCustomDraw, ignoreDrawForeground, ignoreDrawBackground, ignoreDrawFocus, ignoreDrawSelection, ignoreDrawHot;
75     boolean customDraw, dragStarted, explorerTheme, firstColumnImage, fixScrollWidth, tipRequested, wasSelected, wasResized;
76     boolean ignoreActivate, ignoreSelect, ignoreShrink, ignoreResize, ignoreColumnMove, ignoreColumnResize;
77     int headerToolTipHandle, itemHeight, lastIndexOf, lastWidth, sortDirection, resizeCount, selectionForeground, hotIndex;
78     static /*final*/ int HeaderProc;
79     static final int INSET = 4;
80     static final int GRID_WIDTH = 1;
81     static final int SORT_WIDTH = 10;
82     static final int HEADER_MARGIN = 12;
83     static final int HEADER_EXTRA = 3;
84     static final int VISTA_EXTRA = 2;
85     static final int EXPLORER_EXTRA = 2;
86     static final boolean EXPLORER_THEME = true;
87     static final int TableProc;
88     static final TCHAR TableClass = new TCHAR (0, OS.WC_LISTVIEW, true);
89     static {
90         WNDCLASS lpWndClass = new WNDCLASS ();
91         OS.GetClassInfo (0, TableClass, lpWndClass);
92         TableProc = lpWndClass.lpfnWndProc;
93     }
94
95 /**
96  * Constructs a new instance of this class given its parent
97  * and a style value describing its behavior and appearance.
98  * <p>
99  * The style value is either one of the style constants defined in
100  * class <code>SWT</code> which is applicable to instances of this
101  * class, or must be built by <em>bitwise OR</em>'ing together
102  * (that is, using the <code>int</code> "|" operator) two or more
103  * of those <code>SWT</code> style constants. The class description
104  * lists the style constants that are applicable to the class.
105  * Style bits are also inherited from superclasses.
106  * </p>
107  *
108  * @param parent a composite control which will be the parent of the new instance (cannot be null)
109  * @param style the style of control to construct
110  *
111  * @exception IllegalArgumentException <ul>
112  * <li>ERROR_NULL_ARGUMENT - if the parent is null</li>
113  * </ul>
114  * @exception SWTException <ul>
115  * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the parent</li>
116  * <li>ERROR_INVALID_SUBCLASS - if this class is not an allowed subclass</li>
117  * </ul>
118  *
119  * @see SWT#SINGLE
120  * @see SWT#MULTI
121  * @see SWT#CHECK
122  * @see SWT#FULL_SELECTION
123  * @see SWT#HIDE_SELECTION
124  * @see SWT#VIRTUAL
125  * @see Widget#checkSubclass
126  * @see Widget#getStyle
127  */

128 public Table (Composite parent, int style) {
129     super (parent, checkStyle (style));
130 }
131
132 void _addListener (int eventType, Listener listener) {
133     super._addListener (eventType, listener);
134     switch (eventType) {
135         case SWT.MeasureItem:
136         case SWT.EraseItem:
137         case SWT.PaintItem:
138             setCustomDraw (true);
139             setBackgroundTransparent (true);
140             if (OS.COMCTL32_MAJOR < 6) style |= SWT.DOUBLE_BUFFERED;
141             //TODO - LVS_EX_LABELTIP causes white rectangles (turn it off)
142
OS.SendMessage (handle, OS.LVM_SETEXTENDEDLISTVIEWSTYLE, OS.LVS_EX_LABELTIP, 0);
143             break;
144     }
145 }
146
147 TableItem _getItem (int index) {
148     if ((style & SWT.VIRTUAL) == 0) return items [index];
149     if (items [index] != null) return items [index];
150     return items [index] = new TableItem (this, SWT.NONE, -1, false);
151 }
152
153 /**
154  * Adds the listener to the collection of listeners who will
155  * be notified when the user changes the receiver's selection, by sending
156  * it one of the messages defined in the <code>SelectionListener</code>
157  * interface.
158  * <p>
159  * When <code>widgetSelected</code> is called, the item field of the event object is valid.
160  * If the receiver has the <code>SWT.CHECK</code> style and the check selection changes,
161  * the event object detail field contains the value <code>SWT.CHECK</code>.
162  * <code>widgetDefaultSelected</code> is typically called when an item is double-clicked.
163  * The item field of the event object is valid for default selection, but the detail field is not used.
164  * </p>
165  *
166  * @param listener the listener which should be notified when the user changes the receiver's selection
167  *
168  * @exception IllegalArgumentException <ul>
169  * <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
170  * </ul>
171  * @exception SWTException <ul>
172  * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
173  * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
174  * </ul>
175  *
176  * @see SelectionListener
177  * @see #removeSelectionListener
178  * @see SelectionEvent
179  */

180 public void addSelectionListener (SelectionListener listener) {
181     checkWidget ();
182     if (listener == null) error (SWT.ERROR_NULL_ARGUMENT);
183     TypedListener typedListener = new TypedListener (listener);
184     addListener (SWT.Selection,typedListener);
185     addListener (SWT.DefaultSelection,typedListener);
186 }
187
188 int callWindowProc (int hwnd, int msg, int wParam, int lParam) {
189     return callWindowProc (hwnd, msg, wParam, lParam, false);
190 }
191
192 int callWindowProc (int hwnd, int msg, int wParam, int lParam, boolean forceSelect) {
193     if (handle == 0) return 0;
194     if (handle != hwnd) {
195         return OS.CallWindowProc (HeaderProc, hwnd, msg, wParam, lParam);
196     }
197     int topIndex = 0;
198     boolean checkSelection = false, checkActivate = false, redraw = false;
199     switch (msg) {
200         /* Keyboard messages */
201         /*
202         * Feature in Windows. Windows sends LVN_ITEMACTIVATE from WM_KEYDOWN
203         * instead of WM_CHAR. This means that application code that expects
204         * to consume the key press and therefore avoid a SWT.DefaultSelection
205         * event will fail. The fix is to ignore LVN_ITEMACTIVATE when it is
206         * caused by WM_KEYDOWN and send SWT.DefaultSelection from WM_CHAR.
207         */

208         case OS.WM_KEYDOWN:
209             checkActivate = true;
210             //FALL THROUGH
211
case OS.WM_CHAR:
212         case OS.WM_IME_CHAR:
213         case OS.WM_KEYUP:
214         case OS.WM_SYSCHAR:
215         case OS.WM_SYSKEYDOWN:
216         case OS.WM_SYSKEYUP:
217             //FALL THROUGH
218

219         /* Scroll messages */
220         case OS.WM_HSCROLL:
221         case OS.WM_VSCROLL:
222             //FALL THROUGH
223

224         /* Resize messages */
225         case OS.WM_WINDOWPOSCHANGED:
226             redraw = findImageControl () != null && drawCount == 0 && OS.IsWindowVisible (handle);
227             if (redraw) OS.DefWindowProc (handle, OS.WM_SETREDRAW, 0, 0);
228             //FALL THROUGH
229

230         /* Mouse messages */
231         case OS.WM_LBUTTONDBLCLK:
232         case OS.WM_LBUTTONDOWN:
233         case OS.WM_LBUTTONUP:
234         case OS.WM_MBUTTONDBLCLK:
235         case OS.WM_MBUTTONDOWN:
236         case OS.WM_MBUTTONUP:
237         case OS.WM_MOUSEHOVER:
238         case OS.WM_MOUSELEAVE:
239         case OS.WM_MOUSEMOVE:
240         case OS.WM_MOUSEWHEEL:
241         case OS.WM_RBUTTONDBLCLK:
242         case OS.WM_RBUTTONDOWN:
243         case OS.WM_RBUTTONUP:
244         case OS.WM_XBUTTONDBLCLK:
245         case OS.WM_XBUTTONDOWN:
246         case OS.WM_XBUTTONUP:
247             checkSelection = true;
248             //FALL THROUGH
249

250         /* Other messages */
251         case OS.WM_SETFONT:
252         case OS.WM_TIMER: {
253             if (findImageControl () != null) {
254                 topIndex = OS.SendMessage (handle, OS.LVM_GETTOPINDEX, 0, 0);
255             }
256         }
257     }
258     boolean oldSelected = wasSelected;
259     if (checkSelection) wasSelected = false;
260     if (checkActivate) ignoreActivate = true;
261     int code = OS.CallWindowProc (TableProc, hwnd, msg, wParam, lParam);
262     if (checkActivate) ignoreActivate = false;
263     if (checkSelection) {
264         if (wasSelected || forceSelect) {
265             Event event = new Event ();
266             int index = OS.SendMessage (handle, OS.LVM_GETNEXTITEM, -1, OS.LVNI_FOCUSED);
267             if (index != -1) event.item = _getItem (index);
268             postEvent (SWT.Selection, event);
269         }
270         wasSelected = oldSelected;
271     }
272     switch (msg) {
273         /* Keyboard messages */
274         case OS.WM_KEYDOWN:
275         case OS.WM_CHAR:
276         case OS.WM_IME_CHAR:
277         case OS.WM_KEYUP:
278         case OS.WM_SYSCHAR:
279         case OS.WM_SYSKEYDOWN:
280         case OS.WM_SYSKEYUP:
281             //FALL THROUGH
282

283         /* Scroll messages */
284         case OS.WM_HSCROLL:
285         case OS.WM_VSCROLL:
286             //FALL THROUGH
287

288         /* Resize messages */
289         case OS.WM_WINDOWPOSCHANGED:
290             if (redraw) {
291                 OS.DefWindowProc (handle, OS.WM_SETREDRAW, 1, 0);
292                 OS.InvalidateRect (handle, null, true);
293                 int hwndHeader = OS.SendMessage (handle, OS.LVM_GETHEADER, 0, 0);
294                 if (hwndHeader != 0) OS.InvalidateRect (hwndHeader, null, true);
295             }
296             //FALL THROUGH
297

298         /* Mouse messages */
299         case OS.WM_LBUTTONDBLCLK:
300         case OS.WM_LBUTTONDOWN:
301         case OS.WM_LBUTTONUP:
302         case OS.WM_MBUTTONDBLCLK:
303         case OS.WM_MBUTTONDOWN:
304         case OS.WM_MBUTTONUP:
305         case OS.WM_MOUSEHOVER:
306         case OS.WM_MOUSELEAVE:
307         case OS.WM_MOUSEMOVE:
308         case OS.WM_MOUSEWHEEL:
309         case OS.WM_RBUTTONDBLCLK:
310         case OS.WM_RBUTTONDOWN:
311         case OS.WM_RBUTTONUP:
312         case OS.WM_XBUTTONDBLCLK:
313         case OS.WM_XBUTTONDOWN:
314         case OS.WM_XBUTTONUP:
315             //FALL THROUGH
316

317         /* Other messages */
318         case OS.WM_SETFONT:
319         case OS.WM_TIMER: {
320             if (findImageControl () != null) {
321                 if (topIndex != OS.SendMessage (handle, OS.LVM_GETTOPINDEX, 0, 0)) {
322                     OS.InvalidateRect (handle, null, true);
323                 }
324             }
325         }
326     }
327     return code;
328 }
329
330 static int checkStyle (int style) {
331     /*
332     * Feature in Windows. It is not possible to create
333     * a table that does not have scroll bars. Therefore,
334     * no matter what style bits are specified, set the
335     * H_SCROLL and V_SCROLL bits so that the SWT style
336     * will match the widget that Windows creates.
337     */

338     style |= SWT.H_SCROLL | SWT.V_SCROLL;
339     return checkBits (style, SWT.SINGLE, SWT.MULTI, 0, 0, 0, 0);
340 }
341
342 LRESULT CDDS_ITEMPOSTPAINT (NMLVCUSTOMDRAW nmcd, int wParam, int lParam) {
343     int hDC = nmcd.hdc;
344     if (explorerTheme && !ignoreCustomDraw) {
345         hotIndex = -1;
346         if (hooks (SWT.EraseItem) && nmcd.left != nmcd.right) {
347             OS.RestoreDC (hDC, -1);
348         }
349     }
350     /*
351     * Bug in Windows. When the table has the extended style
352     * LVS_EX_FULLROWSELECT and LVM_SETBKCOLOR is used with
353     * CLR_NONE to make the table transparent, Windows fills
354     * a black rectangle around any column that contains an
355     * image. The fix is clear LVS_EX_FULLROWSELECT during
356     * custom draw.
357     *
358     * NOTE: Since CDIS_FOCUS is cleared during custom draw,
359     * it is necessary to draw the focus rectangle after the
360     * item has been drawn.
361     */

362     if (!ignoreCustomDraw && !ignoreDrawFocus && nmcd.left != nmcd.right) {
363         if (OS.IsWindowVisible (handle) && OS.IsWindowEnabled (handle)) {
364             if (!explorerTheme && (style & SWT.FULL_SELECTION) != 0) {
365                 if (OS.SendMessage (handle, OS.LVM_GETBKCOLOR, 0, 0) == OS.CLR_NONE) {
366                     int dwExStyle = OS.SendMessage (handle, OS.LVM_GETEXTENDEDLISTVIEWSTYLE, 0, 0);
367                     if ((dwExStyle & OS.LVS_EX_FULLROWSELECT) == 0) {
368 // if ((nmcd.uItemState & OS.CDIS_FOCUS) != 0) {
369
if (OS.SendMessage (handle, OS.LVM_GETNEXTITEM, -1, OS.LVNI_FOCUSED) == nmcd.dwItemSpec) {
370                             if (handle == OS.GetFocus ()) {
371                                 int uiState = OS.SendMessage (handle, OS.WM_QUERYUISTATE, 0, 0);
372                                 if ((uiState & OS.UISF_HIDEFOCUS) == 0) {
373                                     RECT rect = new RECT ();
374                                     rect.left = OS.LVIR_BOUNDS;
375                                     boolean oldIgnore = ignoreCustomDraw;
376                                     ignoreCustomDraw = true;
377                                     OS.SendMessage (handle, OS. LVM_GETITEMRECT, nmcd.dwItemSpec, rect);
378                                     int hwndHeader = OS.SendMessage (handle, OS.LVM_GETHEADER, 0, 0);
379                                     int index = OS.SendMessage (hwndHeader, OS.HDM_ORDERTOINDEX, 0, 0);
380                                     RECT itemRect = new RECT ();
381                                     if (index == 0) {
382                                         itemRect.left = OS.LVIR_LABEL;
383                                         OS.SendMessage (handle, OS. LVM_GETITEMRECT, index, itemRect);
384                                     } else {
385                                         itemRect.top = index;
386                                         itemRect.left = OS.LVIR_ICON;
387                                         OS.SendMessage (handle, OS. LVM_GETSUBITEMRECT, nmcd.dwItemSpec, itemRect);
388                                     }
389                                     ignoreCustomDraw = oldIgnore;
390                                     rect.left = itemRect.left;
391                                     OS.DrawFocusRect (nmcd.hdc, rect);
392                                 }
393                             }
394                         }
395                     }
396                 }
397             }
398         }
399     }
400     return null;
401 }
402
403 LRESULT CDDS_ITEMPREPAINT (NMLVCUSTOMDRAW nmcd, int wParam, int lParam) {
404     /*
405     * Bug in Windows. When the table has the extended style
406     * LVS_EX_FULLROWSELECT and LVM_SETBKCOLOR is used with
407     * CLR_NONE to make the table transparent, Windows fills
408     * a black rectangle around any column that contains an
409     * image. The fix is clear LVS_EX_FULLROWSELECT during
410     * custom draw.
411     *
412     * NOTE: It is also necessary to clear CDIS_FOCUS to stop
413     * the table from drawing the focus rectangle around the
414     * first item instead of the full row.
415     */

416     if (!ignoreCustomDraw) {
417         if (OS.IsWindowVisible (handle) && OS.IsWindowEnabled (handle)) {
418             if (!explorerTheme && (style & SWT.FULL_SELECTION) != 0) {
419                 if (OS.SendMessage (handle, OS.LVM_GETBKCOLOR, 0, 0) == OS.CLR_NONE) {
420                     int dwExStyle = OS.SendMessage (handle, OS.LVM_GETEXTENDEDLISTVIEWSTYLE, 0, 0);
421                     if ((dwExStyle & OS.LVS_EX_FULLROWSELECT) == 0) {
422                         if ((nmcd.uItemState & OS.CDIS_FOCUS) != 0) {
423                             nmcd.uItemState &= ~OS.CDIS_FOCUS;
424                             OS.MoveMemory (lParam, nmcd, NMLVCUSTOMDRAW.sizeof);
425                         }
426                     }
427                 }
428             }
429         }
430     }
431     if (explorerTheme && !ignoreCustomDraw) {
432         hotIndex = (nmcd.uItemState & OS.CDIS_HOT) != 0 ? nmcd.dwItemSpec : -1;
433         if (hooks (SWT.EraseItem) && nmcd.left != nmcd.right) {
434             OS.SaveDC (nmcd.hdc);
435             int hrgn = OS.CreateRectRgn (0, 0, 0, 0);
436             OS.SelectClipRgn (nmcd.hdc, hrgn);
437             OS.DeleteObject (hrgn);
438         }
439     }
440     return new LRESULT (OS.CDRF_NOTIFYSUBITEMDRAW | OS.CDRF_NOTIFYPOSTPAINT);
441 }
442
443 LRESULT CDDS_POSTPAINT (NMLVCUSTOMDRAW nmcd, int wParam, int lParam) {
444     if (ignoreCustomDraw) return null;
445     /*
446     * Bug in Windows. When the table has the extended style
447     * LVS_EX_FULLROWSELECT and LVM_SETBKCOLOR is used with
448     * CLR_NONE to make the table transparent, Windows fills
449     * a black rectangle around any column that contains an
450     * image. The fix is clear LVS_EX_FULLROWSELECT during
451     * custom draw.
452     */

453     if (--customCount == 0 && OS.IsWindowVisible (handle)) {
454         if (!explorerTheme && (style & SWT.FULL_SELECTION) != 0) {
455             if (OS.SendMessage (handle, OS.LVM_GETBKCOLOR, 0, 0) == OS.CLR_NONE) {
456                 int dwExStyle = OS.SendMessage (handle, OS.LVM_GETEXTENDEDLISTVIEWSTYLE, 0, 0);
457                 if ((dwExStyle & OS.LVS_EX_FULLROWSELECT) == 0) {
458                     int bits = OS.LVS_EX_FULLROWSELECT;
459                     if (OS.IsWinCE) {
460                         RECT rect = new RECT ();
461                         boolean damaged = OS.GetUpdateRect (handle, rect, true);
462                         OS.SendMessage (handle, OS.LVM_SETEXTENDEDLISTVIEWSTYLE, bits, bits);
463                         OS.ValidateRect (handle, null);
464                         if (damaged) OS.InvalidateRect (handle, rect, true);
465                     } else {
466                         int rgn = OS.CreateRectRgn (0, 0, 0, 0);
467                         int result = OS.GetUpdateRgn (handle, rgn, true);
468                         OS.SendMessage (handle, OS.LVM_SETEXTENDEDLISTVIEWSTYLE, bits, bits);
469                         OS.ValidateRect (handle, null);
470                         if (result != OS.NULLREGION) OS.InvalidateRgn (handle, rgn, true);
471                         OS.DeleteObject (rgn);
472                     }
473                 }
474             }
475         }
476     }
477     return null;
478 }
479
480 LRESULT CDDS_PREPAINT (NMLVCUSTOMDRAW nmcd, int wParam, int lParam) {
481     if (ignoreCustomDraw) {
482         return new LRESULT (OS.CDRF_NOTIFYITEMDRAW | OS.CDRF_NOTIFYPOSTPAINT);
483     }
484     /*
485     * Bug in Windows. When the table has the extended style
486     * LVS_EX_FULLROWSELECT and LVM_SETBKCOLOR is used with
487     * CLR_NONE to make the table transparent, Windows fills
488     * a black rectangle around any column that contains an
489     * image. The fix is clear LVS_EX_FULLROWSELECT during
490     * custom draw.
491     */

492     if (customCount++ == 0 && OS.IsWindowVisible (handle)) {
493         if (!explorerTheme && (style & SWT.FULL_SELECTION) != 0) {
494             if (OS.SendMessage (handle, OS.LVM_GETBKCOLOR, 0, 0) == OS.CLR_NONE) {
495                 int dwExStyle = OS.SendMessage (handle, OS.LVM_GETEXTENDEDLISTVIEWSTYLE, 0, 0);
496                 if ((dwExStyle & OS.LVS_EX_FULLROWSELECT) != 0) {
497                     int bits = OS.LVS_EX_FULLROWSELECT;
498                     if (OS.IsWinCE) {
499                         RECT rect = new RECT ();
500                         boolean damaged = OS.GetUpdateRect (handle, rect, true);
501                         OS.SendMessage (handle, OS.LVM_SETEXTENDEDLISTVIEWSTYLE, bits, 0);
502                         OS.ValidateRect (handle, null);
503                         if (damaged) OS.InvalidateRect (handle, rect, true);
504                     } else {
505                         int rgn = OS.CreateRectRgn (0, 0, 0, 0);
506                         int result = OS.GetUpdateRgn (handle, rgn, true);
507                         OS.SendMessage (handle, OS.LVM_SETEXTENDEDLISTVIEWSTYLE, bits, 0);
508                         OS.ValidateRect (handle, null);
509                         if (result != OS.NULLREGION) OS.InvalidateRgn (handle, rgn, true);
510                         OS.DeleteObject (rgn);
511                     }
512                 }
513             }
514         }
515     }
516     if (OS.IsWindowVisible (handle)) {
517         boolean draw = true;
518         /*
519         * Feature in Windows. On Vista using the explorer theme,
520         * Windows draws a vertical line to separate columns. When
521         * there is only a single column, the line looks strange.
522         * The fix is to draw the background using custom draw.
523         */

524         if (explorerTheme && columnCount == 0) {
525             int hDC = nmcd.hdc;
526             RECT rect = new RECT ();
527             OS.SetRect (rect, nmcd.left, nmcd.top, nmcd.right, nmcd.bottom);
528             if (OS.IsWindowEnabled (handle) || findImageControl () != null) {
529                 drawBackground (hDC, rect);
530             } else {
531                 fillBackground (hDC, OS.GetSysColor (OS.COLOR_3DFACE), rect);
532             }
533             draw = false;
534         }
535         if (draw) {
536             Control control = findBackgroundControl ();
537             if (control != null && control.backgroundImage != null) {
538                 RECT rect = new RECT ();
539                 OS.SetRect (rect, nmcd.left, nmcd.top, nmcd.right, nmcd.bottom);
540                 fillImageBackground (nmcd.hdc, control, rect);
541             } else {
542                 if (OS.SendMessage (handle, OS.LVM_GETBKCOLOR, 0, 0) == OS.CLR_NONE) {
543                     if (OS.IsWindowEnabled (handle)) {
544                         RECT rect = new RECT ();
545                         OS.SetRect (rect, nmcd.left, nmcd.top, nmcd.right, nmcd.bottom);
546                         if (control == null) control = this;
547                         fillBackground (nmcd.hdc, control.getBackgroundPixel (), rect);
548                         if (OS.COMCTL32_MAJOR >= 6 && OS.IsAppThemed ()) {
549                             if (sortColumn != null && sortDirection != SWT.NONE) {
550                                 int index = indexOf (sortColumn);
551                                 if (index != -1) {
552                                     parent.forceResize ();
553                                     int clrSortBk = getSortColumnPixel ();
554                                     RECT columnRect = new RECT (), headerRect = new RECT ();
555                                     OS.GetClientRect (handle, columnRect);
556                                     int hwndHeader = OS.SendMessage (handle, OS.LVM_GETHEADER, 0, 0);
557                                     if (OS.SendMessage (hwndHeader, OS.HDM_GETITEMRECT, index, headerRect) != 0) {
558                                         OS.MapWindowPoints (hwndHeader, handle, headerRect, 2);
559                                         columnRect.left = headerRect.left;
560                                         columnRect.right = headerRect.right;
561                                         if (OS.IntersectRect(columnRect, columnRect, rect)) {
562                                             fillBackground (nmcd.hdc, clrSortBk, columnRect);
563                                         }
564                                     }
565                                 }
566                             }
567                         }
568                     }
569                 }
570             }
571         }
572     }
573     return new LRESULT (OS.CDRF_NOTIFYITEMDRAW | OS.CDRF_NOTIFYPOSTPAINT);
574 }
575
576 LRESULT CDDS_SUBITEMPOSTPAINT (NMLVCUSTOMDRAW nmcd, int wParam, int lParam) {
577     if (ignoreCustomDraw) return null;
578     if (nmcd.left == nmcd.right) return new LRESULT (OS.CDRF_DODEFAULT);
579     int hDC = nmcd.hdc;
580     if (ignoreDrawForeground) OS.RestoreDC (hDC, -1);
581     if (OS.IsWindowVisible (handle)) {
582         /*
583         * Feature in Windows. When there is a sort column, the sort column
584         * color draws on top of the background color for an item. The fix
585         * is to clear the sort column in CDDS_SUBITEMPREPAINT, and reset it
586         * in CDDS_SUBITEMPOSTPAINT.
587         */

588         if (OS.SendMessage (handle, OS.LVM_GETBKCOLOR, 0, 0) != OS.CLR_NONE) {
589             if ((sortDirection & (SWT.UP | SWT.DOWN)) != 0) {
590                 if (sortColumn != null && !sortColumn.isDisposed ()) {
591                     int oldColumn = OS.SendMessage (handle, OS.LVM_GETSELECTEDCOLUMN, 0, 0);
592                     if (oldColumn == -1) {
593                         int newColumn = indexOf (sortColumn);
594                         int result = 0, rgn = 0;
595                         if (!OS.IsWinCE && OS.WIN32_VERSION >= OS.VERSION (6, 0)) {
596                             rgn = OS.CreateRectRgn (0, 0, 0, 0);
597                             result = OS.GetUpdateRgn (handle, rgn, true);
598                         }
599                         OS.SendMessage (handle, OS.LVM_SETSELECTEDCOLUMN, newColumn, 0);
600                         if (!OS.IsWinCE && OS.WIN32_VERSION >= OS.VERSION (6, 0)) {
601                             OS.ValidateRect (handle, null);
602                             if (result != OS.NULLREGION) OS.InvalidateRgn (handle, rgn, true);
603                             OS.DeleteObject (rgn);
604                         }
605                     }
606                 }
607             }
608         }
609         if (hooks (SWT.PaintItem)) {
610             TableItem item = _getItem (nmcd.dwItemSpec);
611             sendPaintItemEvent (item, nmcd);
612             //widget could be disposed at this point
613
}
614     }
615     return null;
616 }
617
618 LRESULT CDDS_SUBITEMPREPAINT (NMLVCUSTOMDRAW nmcd, int wParam, int lParam) {
619     int hDC = nmcd.hdc;
620     if (explorerTheme && !ignoreCustomDraw && hooks (SWT.EraseItem) && (nmcd.left != nmcd.right)) {
621         OS.RestoreDC (hDC, -1);
622     }
623     /*
624     * Feature in Windows. When a new table item is inserted
625     * using LVM_INSERTITEM in a table that is transparent
626     * (ie. LVM_SETBKCOLOR has been called with CLR_NONE),
627     * TVM_INSERTITEM calls NM_CUSTOMDRAW before the new item
628     * has been added to the array. The fix is to check for
629     * null.
630     */

631     TableItem item = _getItem (nmcd.dwItemSpec);
632     if (item == null) return null;
633     int hFont = item.cellFont != null ? item.cellFont [nmcd.iSubItem] : -1;
634     if (hFont == -1) hFont = item.font;
635     if (hFont != -1) OS.SelectObject (hDC, hFont);
636     if (ignoreCustomDraw || (nmcd.left == nmcd.right)) {
637         return new LRESULT (hFont == -1 ? OS.CDRF_DODEFAULT : OS.CDRF_NEWFONT);
638     }
639     int code = OS.CDRF_DODEFAULT;
640     selectionForeground = -1;
641     ignoreDrawForeground = ignoreDrawSelection = ignoreDrawFocus = ignoreDrawBackground = false;
642     if (OS.IsWindowVisible (handle)) {
643         if (hooks (SWT.MeasureItem)) {
644             sendMeasureItemEvent (item, nmcd.dwItemSpec, nmcd.iSubItem, nmcd.hdc);
645             if (isDisposed () || item.isDisposed ()) return null;
646         }
647         if (hooks (SWT.EraseItem)) {
648             sendEraseItemEvent (item, nmcd, lParam);
649             if (isDisposed () || item.isDisposed ()) return null;
650             code |= OS.CDRF_NOTIFYPOSTPAINT;
651         }
652         if (ignoreDrawForeground || hooks (SWT.PaintItem)) code |= OS.CDRF_NOTIFYPOSTPAINT;
653     }
654     int clrText = item.cellForeground != null ? item.cellForeground [nmcd.iSubItem] : -1;
655     if (clrText == -1) clrText = item.foreground;
656     int clrTextBk = item.cellBackground != null ? item.cellBackground [nmcd.iSubItem] : -1;
657     if (clrTextBk == -1) clrTextBk = item.background;
658     if (selectionForeground != -1) clrText = selectionForeground;
659     /*
660     * Bug in Windows. When the table has the extended style
661     * LVS_EX_FULLROWSELECT and LVM_SETBKCOLOR is used with
662     * CLR_NONE to make the table transparent, Windows draws
663     * a black rectangle around any column that contains an
664     * image. The fix is emulate LVS_EX_FULLROWSELECT by
665     * drawing the selection.
666     */

667     if (OS.IsWindowVisible (handle) && OS.IsWindowEnabled (handle)) {
668         if (!explorerTheme && !ignoreDrawSelection && (style & SWT.FULL_SELECTION) != 0) {
669             int bits = OS.SendMessage (handle, OS.LVM_GETEXTENDEDLISTVIEWSTYLE, 0, 0);
670             if ((bits & OS.LVS_EX_FULLROWSELECT) == 0) {
671                 /*
672                 * Bug in Windows. For some reason, CDIS_SELECTED always set,
673                 * even for items that are not selected. The fix is to get
674                 * the selection state from the item.
675                 */

676                 LVITEM lvItem = new LVITEM ();
677                 lvItem.mask = OS.LVIF_STATE;
678                 lvItem.stateMask = OS.LVIS_SELECTED;
679                 lvItem.iItem = nmcd.dwItemSpec;
680                 int result = OS.SendMessage (handle, OS.LVM_GETITEM, 0, lvItem);
681                 if ((result != 0 && (lvItem.state & OS.LVIS_SELECTED) != 0)) {
682                     int clrSelection = -1;
683                     if (nmcd.iSubItem == 0) {
684                         if (OS.GetFocus () == handle) {
685                             clrSelection = OS.GetSysColor (OS.COLOR_HIGHLIGHT);
686                         } else {
687                             if ((style & SWT.HIDE_SELECTION) == 0) {
688                                 clrSelection = OS.GetSysColor (OS.COLOR_3DFACE);
689                             }
690                         }
691                     } else {
692                         if (OS.GetFocus () == handle) {
693                             clrText = OS.GetSysColor (OS.COLOR_HIGHLIGHTTEXT);
694                             clrTextBk = clrSelection = OS.GetSysColor (OS.COLOR_HIGHLIGHT);
695                         } else {
696                             if ((style & SWT.HIDE_SELECTION) == 0) {
697                                 clrTextBk = clrSelection = OS.GetSysColor (OS.COLOR_3DFACE);
698                             }
699                         }
700                     }
701                     if (clrSelection != -1) {
702                         RECT rect = item.getBounds (nmcd.dwItemSpec, nmcd.iSubItem, true, nmcd.iSubItem != 0, true, false, hDC);
703                         fillBackground (hDC, clrSelection, rect);
704                     }
705                 }
706             }
707         }
708     }
709     if (!ignoreDrawForeground) {
710         /*
711         * Bug in Windows. When the attributes are for one cell in a table,
712         * Windows does not reset them for the next cell. As a result, all
713         * subsequent cells are drawn using the previous font, foreground and
714         * background colors. The fix is to set the all attributes when any
715         * attribute could have changed.
716         */

717         boolean hasAttributes = true;
718         if (hFont == -1 && clrText == -1 && clrTextBk == -1) {
719             if (item.cellForeground == null && item.cellBackground == null && item.cellFont == null) {
720                 int hwndHeader = OS.SendMessage (handle, OS.LVM_GETHEADER, 0, 0);
721                 int count = OS.SendMessage (hwndHeader, OS.HDM_GETITEMCOUNT, 0, 0);
722                 if (count == 1) hasAttributes = false;
723             }
724         }
725         if (hasAttributes) {
726             if (hFont == -1) hFont = OS.SendMessage (handle, OS.WM_GETFONT, 0, 0);
727             OS.SelectObject (hDC, hFont);
728             if (OS.IsWindowEnabled (handle)) {
729                 nmcd.clrText = clrText == -1 ? getForegroundPixel () : clrText;
730                 if (clrTextBk == -1) {
731                     nmcd.clrTextBk = OS.CLR_NONE;
732                     if (selectionForeground == -1) {
733                         Control control = findBackgroundControl ();
734                         if (control == null) control = this;
735                         if (control.backgroundImage == null) {
736                             if (OS.SendMessage (handle, OS.LVM_GETBKCOLOR, 0, 0) != OS.CLR_NONE) {
737                                 nmcd.clrTextBk = control.getBackgroundPixel ();
738                             }
739                         }
740                     }
741                 } else {
742                     nmcd.clrTextBk = selectionForeground != -1 ? OS.CLR_NONE : clrTextBk;
743                 }
744                 OS.MoveMemory (lParam, nmcd, NMLVCUSTOMDRAW.sizeof);
745             }
746             code |= OS.CDRF_NEWFONT;
747         }
748     }
749     if (OS.IsWindowEnabled (handle)) {
750         /*
751         * Feature in Windows. When there is a sort column, the sort column
752         * color draws on top of the background color for an item. The fix
753         * is to clear the sort column in CDDS_SUBITEMPREPAINT, and reset it
754         * in CDDS_SUBITEMPOSTPAINT.
755         */

756         if (clrTextBk != -1) {
757             int oldColumn = OS.SendMessage (handle, OS.LVM_GETSELECTEDCOLUMN, 0, 0);
758             if (oldColumn != -1 && oldColumn == nmcd.iSubItem) {
759                 int result = 0, rgn = 0;
760                 if (!OS.IsWinCE && OS.WIN32_VERSION >= OS.VERSION (6, 0)) {
761                     rgn = OS.CreateRectRgn (0, 0, 0, 0);
762                     result = OS.GetUpdateRgn (handle, rgn, true);
763                 }
764                 OS.SendMessage (handle, OS.LVM_SETSELECTEDCOLUMN, -1, 0);
765                 if (!OS.IsWinCE && OS.WIN32_VERSION >= OS.VERSION (6, 0)) {
766                     OS.ValidateRect (handle, null);
767                     if (result != OS.NULLREGION) OS.InvalidateRgn (handle, rgn, true);
768                     OS.DeleteObject (rgn);
769                 }
770                 code |= OS.CDRF_NOTIFYPOSTPAINT;
771             }
772         }
773     } else {
774         /*
775         * Feature in Windows. When the table is disabled, it draws
776         * with a gray background but does not gray the text. The fix
777         * is to explicitly gray the text.
778         */

779         nmcd.clrText = OS.GetSysColor (OS.COLOR_GRAYTEXT);
780         if (findImageControl () != null) {
781             nmcd.clrTextBk = OS.CLR_NONE;
782         } else {
783             nmcd.clrTextBk = OS.GetSysColor (OS.COLOR_3DFACE);
784         }
785         nmcd.uItemState &= ~OS.CDIS_SELECTED;
786         OS.MoveMemory (lParam, nmcd, NMLVCUSTOMDRAW.sizeof);
787         code |= OS.CDRF_NEWFONT;
788     }
789     return new LRESULT (code);
790 }
791
792 void checkBuffered () {
793     super.checkBuffered ();
794     if (OS.COMCTL32_MAJOR >= 6) style |= SWT.DOUBLE_BUFFERED;
795     if ((style & SWT.VIRTUAL) != 0) style |= SWT.DOUBLE_BUFFERED;
796 }
797
798 boolean checkData (TableItem item, boolean redraw) {
799     if ((style & SWT.VIRTUAL) == 0) return true;
800     return checkData (item, indexOf (item), redraw);
801 }
802
803 boolean checkData (TableItem item, int index, boolean redraw) {
804     if ((style & SWT.VIRTUAL) == 0) return true;
805     if (!item.cached) {
806         item.cached = true;
807         Event event = new Event ();
808         event.item = item;
809         event.index = index;
810         currentItem = item;
811         sendEvent (SWT.SetData, event);
812         //widget could be disposed at this point
813
currentItem = null;
814         if (isDisposed () || item.isDisposed ()) return false;
815         if (redraw) {
816             if (!setScrollWidth (item, false)) {
817                 item.redraw ();
818             }
819         }
820     }
821     return true;
822 }
823
824 boolean checkHandle (int hwnd) {
825     if (hwnd == handle) return true;
826     return hwnd == OS.SendMessage (handle, OS.LVM_GETHEADER, 0, 0);
827 }
828
829 protected void checkSubclass () {
830     if (!isValidSubclass ()) error (SWT.ERROR_INVALID_SUBCLASS);
831 }
832
833 /**
834  * Clears the item at the given zero-relative index in the receiver.
835  * The text, icon and other attributes of the item are set to the default
836  * value. If the table was created with the <code>SWT.VIRTUAL</code> style,
837  * these attributes are requested again as needed.
838  *
839  * @param index the index of the item to clear
840  *
841  * @exception IllegalArgumentException <ul>
842  * <li>ERROR_INVALID_RANGE - if the index is not between 0 and the number of elements in the list minus 1 (inclusive)</li>
843  * </ul>
844  * @exception SWTException <ul>
845  * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
846  * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
847  * </ul>
848  *
849  * @see SWT#VIRTUAL
850  * @see SWT#SetData
851  *
852  * @since 3.0
853  */

854 public void clear (int index) {
855     checkWidget ();
856     int count = OS.SendMessage (handle, OS.LVM_GETITEMCOUNT, 0, 0);
857     if (!(0 <= index && index < count)) error (SWT.ERROR_INVALID_RANGE);
858     TableItem item = items [index];
859     if (item != null) {
860         if (item != currentItem) item.clear ();
861         /*
862         * Bug in Windows. Despite the fact that every item in the
863         * table always has LPSTR_TEXTCALLBACK, Windows caches the
864         * bounds for the selected items. This means that
865         * when you change the string to be something else, Windows
866         * correctly asks you for the new string but when the item
867         * is selected, the selection draws using the bounds of the
868         * previous item. The fix is to reset LPSTR_TEXTCALLBACK
869         * even though it has not changed, causing Windows to flush
870         * cached bounds.
871         */

872         if ((style & SWT.VIRTUAL) == 0 && item.cached) {
873             LVITEM lvItem = new LVITEM ();
874             lvItem.mask = OS.LVIF_TEXT | OS.LVIF_INDENT;
875             lvItem.pszText = OS.LPSTR_TEXTCALLBACK;
876             lvItem.iItem = index;
877             OS.SendMessage (handle, OS.LVM_SETITEM, 0, lvItem);
878             item.cached = false;
879         }
880         if (currentItem == null && drawCount == 0 && OS.IsWindowVisible (handle)) {
881             OS.SendMessage (handle, OS.LVM_REDRAWITEMS, index, index);
882         }
883         setScrollWidth (item, false);
884     }
885 }
886
887 /**
888  * Removes the items from the receiver which are between the given
889  * zero-relative start and end indices (inclusive). The text, icon
890  * and other attributes of the items are set to their default values.
891  * If the table was created with the <code>SWT.VIRTUAL</code> style,
892  * these attributes are requested again as needed.
893  *
894  * @param start the start index of the item to clear
895  * @param end the end index of the item to clear
896  *
897  * @exception IllegalArgumentException <ul>
898  * <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>
899  * </ul>
900  * @exception SWTException <ul>
901  * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
902  * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
903  * </ul>
904  *
905  * @see SWT#VIRTUAL
906  * @see SWT#SetData
907  *
908  * @since 3.0
909  */

910 public void clear (int start, int end) {
911     checkWidget ();
912     if (start > end) return;
913     int count = OS.SendMessage (handle, OS.LVM_GETITEMCOUNT, 0, 0);
914     if (!(0 <= start && start <= end && end < count)) {
915         error (SWT.ERROR_INVALID_RANGE);
916     }
917     if (start == 0 && end == count - 1) {
918         clearAll ();
919     } else {
920         LVITEM lvItem = null;
921         boolean cleared = false;
922         for (int i=start; i<=end; i++) {
923             TableItem item = items [i];
924             if (item != null) {
925                 if (item != currentItem) {
926                     cleared = true;
927                     item.clear ();
928                 }
929                 /*
930                 * Bug in Windows. Despite the fact that every item in the
931                 * table always has LPSTR_TEXTCALLBACK, Windows caches the
932                 * bounds for the selected items. This means that
933                 * when you change the string to be something else, Windows
934                 * correctly asks you for the new string but when the item
935                 * is selected, the selection draws using the bounds of the
936                 * previous item. The fix is to reset LPSTR_TEXTCALLBACK
937                 * even though it has not changed, causing Windows to flush
938                 * cached bounds.
939                 */

940                 if ((style & SWT.VIRTUAL) == 0 && item.cached) {
941                     if (lvItem == null) {
942                         lvItem = new LVITEM ();
943                         lvItem.mask = OS.LVIF_TEXT | OS.LVIF_INDENT;
944                         lvItem.pszText = OS.LPSTR_TEXTCALLBACK;
945                     }
946                     lvItem.iItem = i;
947                     OS.SendMessage (handle, OS.LVM_SETITEM, 0, lvItem);
948                     item.cached = false;
949                 }
950             }
951         }
952         if (cleared) {
953             if (currentItem == null && drawCount == 0 && OS.IsWindowVisible (handle)) {
954                 OS.SendMessage (handle, OS.LVM_REDRAWITEMS, start, end);
955             }
956             TableItem item = start == end ? items [start] : null;
957             setScrollWidth (item, false);
958         }
959     }
960 }
961
962 /**
963  * Clears the items at the given zero-relative indices in the receiver.
964  * The text, icon and other attributes of the items are set to their default
965  * values. If the table was created with the <code>SWT.VIRTUAL</code> style,
966  * these attributes are requested again as needed.
967  *
968  * @param indices the array of indices of the items
969  *
970  * @exception IllegalArgumentException <ul>
971  * <li>ERROR_INVALID_RANGE - if the index is not between 0 and the number of elements in the list minus 1 (inclusive)</li>
972  * <li>ERROR_NULL_ARGUMENT - if the indices array is null</li>
973  * </ul>
974  * @exception SWTException <ul>
975  * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
976  * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
977  * </ul>
978  *
979  * @see SWT#VIRTUAL
980  * @see SWT#SetData
981  *
982  * @since 3.0
983  */

984 public void clear (int [] indices) {
985     checkWidget ();
986     if (indices == null) error (SWT.ERROR_NULL_ARGUMENT);
987     if (indices.length == 0) return;
988     int count = OS.SendMessage (handle, OS.LVM_GETITEMCOUNT, 0, 0);
989     for (int i=0; i<indices.length; i++) {
990         if (!(0 <= indices [i] && indices [i] < count)) {
991             error (SWT.ERROR_INVALID_RANGE);
992         }
993     }
994     LVITEM lvItem = null;
995     boolean cleared = false;
996     for (int i=0; i<indices.length; i++) {
997         int index = indices [i];
998         TableItem item = items [index];
999         if (item != null) {
1000            if (item != currentItem) {
1001                cleared = true;
1002                item.clear ();
1003            }
1004            /*
1005            * Bug in Windows. Despite the fact that every item in the
1006            * table always has LPSTR_TEXTCALLBACK, Windows caches the
1007            * bounds for the selected items. This means that
1008            * when you change the string to be something else, Windows
1009            * correctly asks you for the new string but when the item
1010            * is selected, the selection draws using the bounds of the
1011            * previous item. The fix is to reset LPSTR_TEXTCALLBACK
1012            * even though it has not changed, causing Windows to flush
1013            * cached bounds.
1014            */

1015            if ((style & SWT.VIRTUAL) == 0 && item.cached) {
1016                if (lvItem == null) {
1017                    lvItem = new LVITEM ();
1018                    lvItem.mask = OS.LVIF_TEXT | OS.LVIF_INDENT;
1019                    lvItem.pszText = OS.LPSTR_TEXTCALLBACK;
1020                }
1021                lvItem.iItem = i;
1022                OS.SendMessage (handle, OS.LVM_SETITEM, 0, lvItem);
1023                item.cached = false;
1024            }
1025            if (currentItem == null && drawCount == 0 && OS.IsWindowVisible (handle)) {
1026                OS.SendMessage (handle, OS.LVM_REDRAWITEMS, index, index);
1027            }
1028        }
1029    }
1030    if (cleared) setScrollWidth (null, false);
1031}
1032
1033/**
1034 * Clears all the items in the receiver. The text, icon and other
1035 * attributes of the items are set to their default values. If the
1036 * table was created with the <code>SWT.VIRTUAL</code> style, these
1037 * attributes are requested again as needed.
1038 *
1039 * @exception SWTException <ul>
1040 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
1041 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
1042 * </ul>
1043 *
1044 * @see SWT#VIRTUAL
1045 * @see SWT#SetData
1046 *
1047 * @since 3.0
1048 */

1049public void clearAll () {
1050    checkWidget ();
1051    LVITEM lvItem = null;
1052    boolean cleared = false;
1053    int count = OS.SendMessage (handle, OS.LVM_GETITEMCOUNT, 0, 0);
1054    for (int i=0; i<count; i++) {
1055        TableItem item = items [i];
1056        if (item != null) {
1057            if (item != currentItem) {
1058                cleared = true;
1059                item.clear ();
1060            }
1061            /*
1062            * Bug in Windows. Despite the fact that every item in the
1063            * table always has LPSTR_TEXTCALLBACK, Windows caches the
1064            * bounds for the selected items. This means that
1065            * when you change the string to be something else, Windows
1066            * correctly asks you for the new string but when the item
1067            * is selected, the selection draws using the bounds of the
1068            * previous item. The fix is to reset LPSTR_TEXTCALLBACK
1069            * even though it has not changed, causing Windows to flush
1070            * cached bounds.
1071            */

1072            if ((style & SWT.VIRTUAL) == 0 && item.cached) {
1073                if (lvItem == null) {
1074                    lvItem = new LVITEM ();
1075                    lvItem.mask = OS.LVIF_TEXT | OS.LVIF_INDENT;
1076                    lvItem.pszText = OS.LPSTR_TEXTCALLBACK;
1077                }
1078                lvItem.iItem = i;
1079                OS.SendMessage (handle, OS.LVM_SETITEM, 0, lvItem);
1080                item.cached = false;
1081            }
1082        }
1083    }
1084    if (cleared) {
1085        if (currentItem == null && drawCount == 0 && OS.IsWindowVisible (handle)) {
1086            OS.SendMessage (handle, OS.LVM_REDRAWITEMS, 0, count - 1);
1087        }
1088        setScrollWidth (null, false);
1089    }
1090}
1091
1092public Point computeSize (int wHint, int hHint, boolean changed) {
1093    checkWidget ();
1094    if (fixScrollWidth) setScrollWidth (null, true);
1095    //This code is intentionally commented
1096
// if (itemHeight == -1 && hooks (SWT.MeasureItem)) {
1097
// int i = 0;
1098
// TableItem item = items [i];
1099
// if (item != null) {
1100
// int hDC = OS.GetDC (handle);
1101
// int oldFont = 0, newFont = OS.SendMessage (handle, OS.WM_GETFONT, 0, 0);
1102
// if (newFont != 0) oldFont = OS.SelectObject (hDC, newFont);
1103
// int index = 0, count = Math.max (1, columnCount);
1104
// while (index < count) {
1105
// int hFont = item.cellFont != null ? item.cellFont [index] : -1;
1106
// if (hFont == -1) hFont = item.font;
1107
// if (hFont != -1) hFont = OS.SelectObject (hDC, hFont);
1108
// sendMeasureItemEvent (item, i, index, hDC);
1109
// if (hFont != -1) hFont = OS.SelectObject (hDC, hFont);
1110
// if (isDisposed () || item.isDisposed ()) break;
1111
// index++;
1112
// }
1113
// if (newFont != 0) OS.SelectObject (hDC, oldFont);
1114
// OS.ReleaseDC (handle, hDC);
1115
// }
1116
// }
1117
int hwndHeader = OS.SendMessage (handle, OS.LVM_GETHEADER, 0, 0);
1118    RECT rect = new RECT ();
1119    OS.GetWindowRect (hwndHeader, rect);
1120    int height = rect.bottom - rect.top;
1121    int bits = 0;
1122    if (wHint != SWT.DEFAULT) {
1123        bits |= wHint & 0xFFFF;
1124    } else {
1125        int width = 0;
1126        int count = OS.SendMessage (hwndHeader, OS.HDM_GETITEMCOUNT, 0, 0);
1127        for (int i=0; i<count; i++) {
1128            width += OS.SendMessage (handle, OS.LVM_GETCOLUMNWIDTH, i, 0);
1129        }
1130        bits |= width & 0xFFFF;
1131    }
1132    int result = OS.SendMessage (handle, OS.LVM_APPROXIMATEVIEWRECT, -1, bits | 0xFFFF0000);
1133    int width = result & 0xFFFF;
1134    int empty = OS.SendMessage (handle, OS.LVM_APPROXIMATEVIEWRECT, 0, 0);
1135    int oneItem = OS.SendMessage (handle, OS.LVM_APPROXIMATEVIEWRECT, 1, 0);
1136    int itemHeight = (oneItem >> 16) - (empty >> 16);
1137    height += OS.SendMessage (handle, OS.LVM_GETITEMCOUNT, 0, 0) * itemHeight;
1138    if (width == 0) width = DEFAULT_WIDTH;
1139    if (height == 0) height = DEFAULT_HEIGHT;
1140    if (wHint != SWT.DEFAULT) width = wHint;
1141    if (hHint != SWT.DEFAULT) height = hHint;
1142    int border = getBorderWidth ();
1143    width += border * 2; height += border * 2;
1144    if ((style & SWT.V_SCROLL) != 0) {
1145        width += OS.GetSystemMetrics (OS.SM_CXVSCROLL);
1146    }
1147    if ((style & SWT.H_SCROLL) != 0) {
1148        height += OS.GetSystemMetrics (OS.SM_CYHSCROLL);
1149    }
1150    return new Point (width, height);
1151}
1152
1153void createHandle () {
1154    super.createHandle ();
1155    state &= ~(CANVAS | THEME_BACKGROUND);
1156    
1157    /* Use the Explorer theme */
1158    if (EXPLORER_THEME) {
1159        if (!OS.IsWinCE && OS.WIN32_VERSION >= OS.VERSION (6, 0)) {
1160            explorerTheme = true;
1161            OS.SetWindowTheme (handle, Display.EXPLORER, null);
1162        }
1163    }
1164    
1165    /* Get the header window proc */
1166    if (HeaderProc == 0) {
1167        int hwndHeader = OS.SendMessage (handle, OS.LVM_GETHEADER, 0, 0);
1168        HeaderProc = OS.GetWindowLong (hwndHeader, OS.GWL_WNDPROC);
1169    }
1170    
1171    /*
1172    * Feature in Windows. In version 5.8 of COMCTL32.DLL,
1173    * if the font is changed for an item, the bounds for the
1174    * item are not updated, causing the text to be clipped.
1175    * The fix is to detect the version of COMCTL32.DLL, and
1176    * if it is one of the versions with the problem, then
1177    * use version 5.00 of the control (a version that does
1178    * not have the problem). This is the recommended work
1179    * around from the MSDN.
1180    */

1181    if (!OS.IsWinCE) {
1182        if (OS.COMCTL32_MAJOR < 6) {
1183            OS.SendMessage (handle, OS.CCM_SETVERSION, 5, 0);
1184        }
1185    }
1186    
1187    /*
1188    * This code is intentionally commented. According to
1189    * the documentation, setting the default item size is
1190    * supposed to improve performance. By experimentation,
1191    * this does not seem to have much of an effect.
1192    */

1193// OS.SendMessage (handle, OS.LVM_SETITEMCOUNT, 1024 * 2, 0);
1194

1195    /* Set the checkbox image list */
1196    if ((style & SWT.CHECK) != 0) {
1197        int empty = OS.SendMessage (handle, OS.LVM_APPROXIMATEVIEWRECT, 0, 0);
1198        int oneItem = OS.SendMessage (handle, OS.LVM_APPROXIMATEVIEWRECT, 1, 0);
1199        int width = (oneItem >> 16) - (empty >> 16), height = width;
1200        setCheckboxImageList (width, height, false);
1201        OS.SendMessage (handle, OS. LVM_SETCALLBACKMASK, OS.LVIS_STATEIMAGEMASK, 0);
1202    }
1203
1204    /*
1205    * Feature in Windows. When the control is created,
1206    * it does not use the default system font. A new HFONT
1207    * is created and destroyed when the control is destroyed.
1208    * This means that a program that queries the font from
1209    * this control, uses the font in another control and then
1210    * destroys this control will have the font unexpectedly
1211    * destroyed in the other control. The fix is to assign
1212    * the font ourselves each time the control is created.
1213    * The control will not destroy a font that it did not
1214    * create.
1215    */

1216    int hFont = OS.GetStockObject (OS.SYSTEM_FONT);
1217    OS.SendMessage (handle, OS.WM_SETFONT, hFont, 0);
1218
1219    /*
1220    * Bug in Windows. When the first column is inserted
1221    * without setting the header text, Windows will never
1222    * allow the header text for the first column to be set.
1223    * The fix is to set the text to an empty string when
1224    * the column is inserted.
1225    */

1226    LVCOLUMN lvColumn = new LVCOLUMN ();
1227    lvColumn.mask = OS.LVCF_TEXT | OS.LVCF_WIDTH;
1228    int hHeap = OS.GetProcessHeap ();
1229    int pszText = OS.HeapAlloc (hHeap, OS.HEAP_ZERO_MEMORY, TCHAR.sizeof);
1230    lvColumn.pszText = pszText;
1231    OS.SendMessage (handle, OS.LVM_INSERTCOLUMN, 0, lvColumn);
1232    OS.HeapFree (hHeap, 0, pszText);
1233
1234    /* Set the extended style bits */
1235    int bits1 = OS.LVS_EX_LABELTIP;
1236    if ((style & SWT.FULL_SELECTION) != 0) bits1 |= OS.LVS_EX_FULLROWSELECT;
1237    if (OS.COMCTL32_MAJOR >= 6) bits1 |= OS.LVS_EX_DOUBLEBUFFER;
1238    OS.SendMessage (handle, OS.LVM_SETEXTENDEDLISTVIEWSTYLE, bits1, bits1);
1239    
1240    /*
1241    * Feature in Windows. Windows does not explicitly set the orientation of
1242    * the header. Instead, the orientation is inherited when WS_EX_LAYOUTRTL
1243    * is specified for the table. This means that when both WS_EX_LAYOUTRTL
1244    * and WS_EX_NOINHERITLAYOUT are specified for the table, the header will
1245    * not be oriented correctly. The fix is to explicitly set the orientation
1246    * for the header.
1247    *
1248    * NOTE: WS_EX_LAYOUTRTL is not supported on Windows NT.
1249    */

1250    if (OS.WIN32_VERSION < OS.VERSION (4, 10)) return;
1251    if ((style & SWT.RIGHT_TO_LEFT) != 0) {
1252        int hwndHeader = OS.SendMessage (handle, OS.LVM_GETHEADER, 0, 0);
1253        int bits2 = OS.GetWindowLong (hwndHeader, OS.GWL_EXSTYLE);
1254        OS.SetWindowLong (hwndHeader, OS.GWL_EXSTYLE, bits2 | OS.WS_EX_LAYOUTRTL);
1255    }
1256}
1257
1258void createHeaderToolTips () {
1259    if (OS.IsWinCE) return;
1260    if (headerToolTipHandle != 0) return;
1261    headerToolTipHandle = OS.CreateWindowEx (
1262        0,
1263        new TCHAR (0, OS.TOOLTIPS_CLASS, true),
1264        null,
1265        0,
1266        OS.CW_USEDEFAULT, 0, OS.CW_USEDEFAULT, 0,
1267        handle,
1268        0,
1269        OS.GetModuleHandle (null),
1270        null);
1271    if (headerToolTipHandle == 0) error (SWT.ERROR_NO_HANDLES);
1272    /*
1273    * Feature in Windows. Despite the fact that the
1274    * tool tip text contains \r\n, the tooltip will
1275    * not honour the new line unless TTM_SETMAXTIPWIDTH
1276    * is set. The fix is to set TTM_SETMAXTIPWIDTH to
1277    * a large value.
1278    */

1279    OS.SendMessage (headerToolTipHandle, OS.TTM_SETMAXTIPWIDTH, 0, 0x7FFF);
1280}
1281
1282void createItem (TableColumn column, int index) {
1283    if (!(0 <= index && index <= columnCount)) error (SWT.ERROR_INVALID_RANGE);
1284    int oldColumn = OS.SendMessage (handle, OS.LVM_GETSELECTEDCOLUMN, 0, 0);
1285    if (oldColumn >= index) {
1286        OS.SendMessage (handle, OS.LVM_SETSELECTEDCOLUMN, oldColumn + 1, 0);
1287    }
1288    if (columnCount == columns.length) {
1289        TableColumn [] newColumns = new TableColumn [columns.length + 4];
1290        System.arraycopy (columns, 0, newColumns, 0, columns.length);
1291        columns = newColumns;
1292    }
1293    int itemCount = OS.SendMessage (handle, OS.LVM_GETITEMCOUNT, 0, 0);
1294    for (int i=0; i<itemCount; i++) {
1295        TableItem item = items [i];
1296        if (item != null) {
1297            String JavaDoc [] strings = item.strings;
1298            if (strings != null) {
1299                String JavaDoc [] temp = new String JavaDoc [columnCount + 1];
1300                System.arraycopy (strings, 0, temp, 0, index);
1301                System.arraycopy (strings, index, temp, index + 1, columnCount - index);
1302                item.strings = temp;
1303            }
1304            Image [] images = item.images;
1305            if (images != null) {
1306                Image [] temp = new Image [columnCount + 1];
1307                System.arraycopy (images, 0, temp, 0, index);
1308                System.arraycopy (images, index, temp, index + 1, columnCount - index);
1309                item.images = temp;
1310            }
1311            if (index == 0) {
1312                if (columnCount != 0) {
1313                    if (strings == null) {
1314                        item.strings = new String JavaDoc [columnCount + 1];
1315                        item.strings [1] = item.text;
1316                    }
1317                    item.text = ""; //$NON-NLS-1$
1318
if (images == null) {
1319                        item.images = new Image [columnCount + 1];
1320                        item.images [1] = item.image;
1321                    }
1322                    item.image = null;
1323                }
1324            }
1325            if (item.cellBackground != null) {
1326                int [] cellBackground = item.cellBackground;
1327                int [] temp = new int [columnCount + 1];
1328                System.arraycopy (cellBackground, 0, temp, 0, index);
1329                System.arraycopy (cellBackground, index, temp, index + 1, columnCount - index);
1330                temp [index] = -1;
1331                item.cellBackground = temp;
1332            }
1333            if (item.cellForeground != null) {
1334                int [] cellForeground = item.cellForeground;
1335                int [] temp = new int [columnCount + 1];
1336                System.arraycopy (cellForeground, 0, temp, 0, index);
1337                System.arraycopy (cellForeground, index, temp, index + 1, columnCount - index);
1338                temp [index] = -1;
1339                item.cellForeground = temp;
1340            }
1341            if (item.cellFont != null) {
1342                int [] cellFont = item.cellFont;
1343                int [] temp = new int [columnCount + 1];
1344                System.arraycopy (cellFont, 0, temp, 0, index);
1345                System.arraycopy (cellFont, index, temp, index + 1, columnCount - index);
1346                temp [index] = -1;
1347                item.cellFont = temp;
1348            }
1349        }
1350    }
1351    /*
1352    * Insert the column into the columns array before inserting
1353    * it into the widget so that the column will be present when
1354    * any callbacks are issued as a result of LVM_INSERTCOLUMN
1355    * or LVM_SETCOLUMN.
1356    */

1357    System.arraycopy (columns, index, columns, index + 1, columnCount++ - index);
1358    columns [index] = column;
1359    
1360    /*
1361    * Ensure that resize listeners for the table and for columns
1362    * within the table are not called. This can happen when the
1363    * first column is inserted into a table or when a new column
1364    * is inserted in the first position.
1365    */

1366    ignoreColumnResize = true;
1367    if (index == 0) {
1368        if (columnCount > 1) {
1369            LVCOLUMN lvColumn = new LVCOLUMN ();
1370            lvColumn.mask = OS.LVCF_WIDTH;
1371            OS.SendMessage (handle, OS.LVM_INSERTCOLUMN, 1, lvColumn);
1372            OS.SendMessage (handle, OS.LVM_GETCOLUMN, 1, lvColumn);
1373            int width = lvColumn.cx;
1374            int cchTextMax = 1024;
1375            int hHeap = OS.GetProcessHeap ();
1376            int byteCount = cchTextMax * TCHAR.sizeof;
1377            int pszText = OS.HeapAlloc (hHeap, OS.HEAP_ZERO_MEMORY, byteCount);
1378            lvColumn.mask = OS.LVCF_TEXT | OS.LVCF_IMAGE | OS.LVCF_WIDTH | OS.LVCF_FMT;
1379            lvColumn.pszText = pszText;
1380            lvColumn.cchTextMax = cchTextMax;
1381            OS.SendMessage (handle, OS.LVM_GETCOLUMN, 0, lvColumn);
1382            OS.SendMessage (handle, OS.LVM_SETCOLUMN, 1, lvColumn);
1383            lvColumn.fmt = OS.LVCFMT_IMAGE;
1384            lvColumn.cx = width;
1385            lvColumn.iImage = OS.I_IMAGENONE;
1386            lvColumn.pszText = lvColumn.cchTextMax = 0;
1387            OS.SendMessage (handle, OS.LVM_SETCOLUMN, 0, lvColumn);
1388            lvColumn.mask = OS.LVCF_FMT;
1389            lvColumn.fmt = OS.LVCFMT_LEFT;
1390            OS.SendMessage (handle, OS.LVM_SETCOLUMN, 0, lvColumn);
1391            if (pszText != 0) OS.HeapFree (hHeap, 0, pszText);
1392        } else {
1393            OS.SendMessage (handle, OS.LVM_SETCOLUMNWIDTH, 0, 0);
1394        }
1395        if ((parent.style & SWT.VIRTUAL) == 0) {
1396            LVITEM lvItem = new LVITEM ();
1397            lvItem.mask = OS.LVIF_TEXT | OS.LVIF_IMAGE;
1398            lvItem.pszText = OS.LPSTR_TEXTCALLBACK;
1399            lvItem.iImage = OS.I_IMAGECALLBACK;
1400            for (int i=0; i<itemCount; i++) {
1401                lvItem.iItem = i;
1402                OS.SendMessage (handle, OS.LVM_SETITEM, 0, lvItem);
1403            }
1404        }
1405    } else {
1406        int fmt = OS.LVCFMT_LEFT;
1407        if ((column.style & SWT.CENTER) == SWT.CENTER) fmt = OS.LVCFMT_CENTER;
1408        if ((column.style & SWT.RIGHT) == SWT.RIGHT) fmt = OS.LVCFMT_RIGHT;
1409        LVCOLUMN lvColumn = new LVCOLUMN ();
1410        lvColumn.mask = OS.LVCF_WIDTH | OS.LVCF_FMT;
1411        lvColumn.fmt = fmt;
1412        OS.SendMessage (handle, OS.LVM_INSERTCOLUMN, index, lvColumn);
1413    }
1414    ignoreColumnResize = false;
1415    
1416    /* Add the tool tip item for the header */
1417    if (headerToolTipHandle != 0) {
1418        RECT rect = new RECT ();
1419        int hwndHeader = OS.SendMessage (handle, OS.LVM_GETHEADER, 0, 0);
1420        if (OS.SendMessage (hwndHeader, OS.HDM_GETITEMRECT, index, rect) != 0) {
1421            TOOLINFO lpti = new TOOLINFO ();
1422            lpti.cbSize = TOOLINFO.sizeof;
1423            lpti.uFlags = OS.TTF_SUBCLASS;
1424            lpti.hwnd = hwndHeader;
1425            lpti.uId = column.id = display.nextToolTipId++;
1426            lpti.left = rect.left;
1427            lpti.top = rect.top;
1428            lpti.right = rect.right;
1429            lpti.bottom = rect.bottom;
1430            lpti.lpszText = OS.LPSTR_TEXTCALLBACK;
1431            OS.SendMessage (headerToolTipHandle, OS.TTM_ADDTOOL, 0, lpti);
1432        }
1433    }
1434}
1435
1436void createItem (TableItem item, int index) {
1437    int count = OS.SendMessage (handle, OS.LVM_GETITEMCOUNT, 0, 0);
1438    if (!(0 <= index && index <= count)) error (SWT.ERROR_INVALID_RANGE);
1439    if (count == items.length) {
1440        /*
1441        * Grow the array faster when redraw is off or the
1442        * table is not visible. When the table is painted,
1443        * the items array is resized to be smaller to reduce
1444        * memory usage.
1445        */

1446        boolean small = drawCount == 0 && OS.IsWindowVisible (handle);
1447        int length = small ? items.length + 4 : Math.max (4, items.length * 3 / 2);
1448        TableItem [] newItems = new TableItem [length];
1449        System.arraycopy (items, 0, newItems, 0, items.length);
1450        items = newItems;
1451    }
1452    LVITEM lvItem = new LVITEM ();
1453    lvItem.mask = OS.LVIF_TEXT | OS.LVIF_IMAGE;
1454    lvItem.iItem = index;
1455    lvItem.pszText = OS.LPSTR_TEXTCALLBACK;
1456    /*
1457    * Bug in Windows. Despite the fact that the image list
1458    * index has never been set for the item, Windows always
1459    * assumes that the image index for the item is valid.
1460    * When an item is inserted, the image index is zero.
1461    * Therefore, when the first image is inserted and is
1462    * assigned image index zero, every item draws with this
1463    * image. The fix is to set the image index when the
1464    * the item is created.
1465    */

1466    lvItem.iImage = OS.I_IMAGECALLBACK;
1467
1468    /* Insert the item */
1469    setDeferResize (true);
1470    ignoreSelect = ignoreShrink = true;
1471    int result = OS.SendMessage (handle, OS.LVM_INSERTITEM, 0, lvItem);
1472    ignoreSelect = ignoreShrink = false;
1473    if (result == -1) error (SWT.ERROR_ITEM_NOT_ADDED);
1474    System.arraycopy (items, index, items, index + 1, count - index);
1475    items [index] = item;
1476    setDeferResize (false);
1477    
1478    /* Resize to show the first item */
1479    if (count == 0) setScrollWidth (item, false);
1480}
1481
1482void createWidget () {
1483    super.createWidget ();
1484    itemHeight = hotIndex = -1;
1485    items = new TableItem [4];
1486    columns = new TableColumn [4];
1487}
1488
1489int defaultBackground () {
1490    return OS.GetSysColor (OS.COLOR_WINDOW);
1491}
1492
1493void deregister () {
1494    super.deregister ();
1495    int hwndHeader = OS.SendMessage (handle, OS.LVM_GETHEADER, 0, 0);
1496    if (hwndHeader != 0) display.removeControl (hwndHeader);
1497}
1498
1499/**
1500 * Deselects the items at the given zero-relative indices in the receiver.
1501 * If the item at the given zero-relative index in the receiver
1502 * is selected, it is deselected. If the item at the index
1503 * was not selected, it remains deselected. Indices that are out
1504 * of range and duplicate indices are ignored.
1505 *
1506 * @param indices the array of indices for the items to deselect
1507 *
1508 * @exception IllegalArgumentException <ul>
1509 * <li>ERROR_NULL_ARGUMENT - if the set of indices is null</li>
1510 * </ul>
1511 * @exception SWTException <ul>
1512 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
1513 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
1514 * </ul>
1515 */

1516public void deselect (int [] indices) {
1517    checkWidget ();
1518    if (indices == null) error (SWT.ERROR_NULL_ARGUMENT);
1519    if (indices.length == 0) return;
1520    LVITEM lvItem = new LVITEM ();
1521    lvItem.stateMask = OS.LVIS_SELECTED;
1522    for (int i=0; i<indices.length; i++) {
1523        /*
1524        * An index of -1 will apply the change to all
1525        * items. Ensure that indices are greater than -1.
1526        */

1527        if (indices [i] >= 0) {
1528            ignoreSelect = true;
1529            OS.SendMessage (handle, OS.LVM_SETITEMSTATE, indices [i], lvItem);
1530            ignoreSelect = false;
1531        }
1532    }
1533}
1534
1535/**
1536 * Deselects the item at the given zero-relative index in the receiver.
1537 * If the item at the index was already deselected, it remains
1538 * deselected. Indices that are out of range are ignored.
1539 *
1540 * @param index the index of the item to deselect
1541 *
1542 * @exception SWTException <ul>
1543 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
1544 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
1545 * </ul>
1546 */

1547public void deselect (int index) {
1548    checkWidget ();
1549    /*
1550    * An index of -1 will apply the change to all
1551    * items. Ensure that index is greater than -1.
1552    */

1553    if (index < 0) return;
1554    LVITEM lvItem = new LVITEM ();
1555    lvItem.stateMask = OS.LVIS_SELECTED;
1556    ignoreSelect = true;
1557    OS.SendMessage (handle, OS.LVM_SETITEMSTATE, index, lvItem);
1558    ignoreSelect = false;
1559}
1560
1561/**
1562 * Deselects the items at the given zero-relative indices in the receiver.
1563 * If the item at the given zero-relative index in the receiver
1564 * is selected, it is deselected. If the item at the index
1565 * was not selected, it remains deselected. The range of the
1566 * indices is inclusive. Indices that are out of range are ignored.
1567 *
1568 * @param start the start index of the items to deselect
1569 * @param end the end index of the items to deselect
1570 *
1571 * @exception SWTException <ul>
1572 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
1573 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
1574 * </ul>
1575 */

1576public void deselect (int start, int end) {
1577    checkWidget ();
1578    int count = OS.SendMessage (handle, OS.LVM_GETITEMCOUNT, 0, 0);
1579    if (start == 0 && end == count - 1) {
1580        deselectAll ();
1581    } else {
1582        LVITEM lvItem = new LVITEM ();
1583        lvItem.stateMask = OS.LVIS_SELECTED;
1584        /*
1585        * An index of -1 will apply the change to all
1586        * items. Ensure that indices are greater than -1.
1587        */

1588        start = Math.max (0, start);
1589        for (int i=start; i<=end; i++) {
1590            ignoreSelect = true;
1591            OS.SendMessage (handle, OS.LVM_SETITEMSTATE, i, lvItem);
1592            ignoreSelect = false;
1593        }
1594    }
1595}
1596
1597/**
1598 * Deselects all selected items in the receiver.
1599 *
1600 * @exception SWTException <ul>
1601 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
1602 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
1603 * </ul>
1604 */

1605public void deselectAll () {
1606    checkWidget ();
1607    LVITEM lvItem = new LVITEM ();
1608    lvItem.mask = OS.LVIF_STATE;
1609    lvItem.stateMask = OS.LVIS_SELECTED;
1610    ignoreSelect = true;
1611    OS.SendMessage (handle, OS.LVM_SETITEMSTATE, -1, lvItem);
1612    ignoreSelect = false;
1613}
1614
1615void destroyItem (TableColumn column) {
1616    int index = 0;
1617    while (index < columnCount) {
1618        if (columns [index] == column) break;
1619        index++;
1620    }
1621    int oldColumn = OS.SendMessage (handle, OS.LVM_GETSELECTEDCOLUMN, 0, 0);
1622    if (oldColumn == index) {
1623        OS.SendMessage (handle, OS.LVM_SETSELECTEDCOLUMN, -1, 0);
1624    } else {
1625        if (oldColumn > index) {
1626            OS.SendMessage (handle, OS.LVM_SETSELECTEDCOLUMN, oldColumn - 1, 0);
1627        }
1628    }
1629    int orderIndex = 0;
1630    int [] oldOrder = new int [columnCount];
1631    OS.SendMessage (handle, OS.LVM_GETCOLUMNORDERARRAY, columnCount, oldOrder);
1632    while (orderIndex < columnCount) {
1633        if (oldOrder [orderIndex] == index) break;
1634        orderIndex++;
1635    }
1636    ignoreColumnResize = true;
1637    boolean first = false;
1638    if (index == 0) {
1639        first = true;
1640        if (columnCount > 1) {
1641            index = 1;
1642            int cchTextMax = 1024;
1643            int hHeap = OS.GetProcessHeap ();
1644            int byteCount = cchTextMax * TCHAR.sizeof;
1645            int pszText = OS.HeapAlloc (hHeap, OS.HEAP_ZERO_MEMORY, byteCount);
1646            LVCOLUMN lvColumn = new LVCOLUMN ();
1647            lvColumn.mask = OS.LVCF_TEXT | OS.LVCF_IMAGE | OS.LVCF_WIDTH | OS.LVCF_FMT;
1648            lvColumn.pszText = pszText;
1649            lvColumn.cchTextMax = cchTextMax;
1650            OS.SendMessage (handle, OS.LVM_GETCOLUMN, 1, lvColumn);
1651            lvColumn.fmt &= ~(OS.LVCFMT_CENTER | OS.LVCFMT_RIGHT);
1652            lvColumn.fmt |= OS.LVCFMT_LEFT;
1653            OS.SendMessage (handle, OS.LVM_SETCOLUMN, 0, lvColumn);
1654            if (pszText != 0) OS.HeapFree (hHeap, 0, pszText);
1655        } else {
1656            int hHeap = OS.GetProcessHeap ();
1657            int pszText = OS.HeapAlloc (hHeap, OS.HEAP_ZERO_MEMORY, TCHAR.sizeof);
1658            LVCOLUMN lvColumn = new LVCOLUMN ();
1659            lvColumn.mask = OS.LVCF_TEXT | OS.LVCF_IMAGE | OS.LVCF_WIDTH | OS.LVCF_FMT;
1660            lvColumn.pszText = pszText;
1661            lvColumn.iImage = OS.I_IMAGENONE;
1662            lvColumn.fmt = OS.LVCFMT_LEFT;
1663            OS.SendMessage (handle, OS.LVM_SETCOLUMN, 0, lvColumn);
1664            if (pszText != 0) OS.HeapFree (hHeap, 0, pszText);
1665            if (OS.COMCTL32_MAJOR >= 6) {
1666                HDITEM hdItem = new HDITEM ();
1667                hdItem.mask = OS.HDI_FORMAT;
1668                hdItem.fmt = OS.HDF_LEFT;
1669                int hwndHeader = OS.SendMessage (handle, OS.LVM_GETHEADER, 0, 0);
1670                OS.SendMessage (hwndHeader, OS.HDM_SETITEM, index, hdItem);
1671            }
1672        }
1673        if ((parent.style & SWT.VIRTUAL) == 0) {
1674            LVITEM lvItem = new LVITEM ();
1675            lvItem.mask = OS.LVIF_TEXT | OS.LVIF_IMAGE;
1676            lvItem.pszText = OS.LPSTR_TEXTCALLBACK;
1677            lvItem.iImage = OS.I_IMAGECALLBACK;
1678            int itemCount = OS.SendMessage (handle, OS.LVM_GETITEMCOUNT, 0, 0);
1679            for (int i=0; i<itemCount; i++) {
1680                lvItem.iItem = i;
1681                OS.SendMessage (handle, OS.LVM_SETITEM, 0, lvItem);
1682            }
1683        }
1684    }
1685    if (columnCount > 1) {
1686        if (OS.SendMessage (handle, OS.LVM_DELETECOLUMN, index, 0) == 0) {
1687            error (SWT.ERROR_ITEM_NOT_REMOVED);
1688        }
1689    }
1690    if (first) index = 0;
1691    System.arraycopy (columns, index + 1, columns, index, --columnCount - index);
1692    columns [columnCount] = null;
1693    int itemCount = OS.SendMessage (handle, OS.LVM_GETITEMCOUNT, 0, 0);
1694    for (int i=0; i<itemCount; i++) {
1695        TableItem item = items [i];
1696        if (item != null) {
1697            if (columnCount == 0) {
1698                item.strings = null;
1699                item.images = null;
1700                item.cellBackground = null;
1701                item.cellForeground = null;
1702                item.cellFont = null;
1703            } else {
1704                if (item.strings != null) {
1705                    String JavaDoc [] strings = item.strings;
1706                    if (index == 0) {
1707                        item.text = strings [1] != null ? strings [1] : ""; //$NON-NLS-1$
1708
}
1709                    String JavaDoc [] temp = new String JavaDoc [columnCount];
1710                    System.arraycopy (strings, 0, temp, 0, index);
1711                    System.arraycopy (strings, index + 1, temp, index, columnCount - index);
1712                    item.strings = temp;
1713                } else {
1714                    if (index == 0) item.text = ""; //$NON-NLS-1$
1715
}
1716                if (item.images != null) {
1717                    Image [] images = item.images;
1718                    if (index == 0) item.image = images [1];
1719                    Image [] temp = new Image [columnCount];
1720                    System.arraycopy (images, 0, temp, 0, index);
1721                    System.arraycopy (images, index + 1, temp, index, columnCount - index);
1722                    item.images = temp;
1723                } else {
1724                    if (index == 0) item.image = null;
1725                }
1726                if (item.cellBackground != null) {
1727                    int [] cellBackground = item.cellBackground;
1728                    int [] temp = new int [columnCount];
1729                    System.arraycopy (cellBackground, 0, temp, 0, index);
1730                    System.arraycopy (cellBackground, index + 1, temp, index, columnCount - index);
1731                    item.cellBackground = temp;
1732                }
1733                if (item.cellForeground != null) {
1734                    int [] cellForeground = item.cellForeground;
1735                    int [] temp = new int [columnCount];
1736                    System.arraycopy (cellForeground, 0, temp, 0, index);
1737                    System.arraycopy (cellForeground, index + 1, temp, index, columnCount - index);
1738                    item.cellForeground = temp;
1739                }
1740                if (item.cellFont != null) {
1741                    int [] cellFont = item.cellFont;
1742                    int [] temp = new int [columnCount];
1743                    System.arraycopy (cellFont, 0, temp, 0, index);
1744                    System.arraycopy (cellFont, index + 1, temp, index, columnCount - index);
1745                    item.cellFont = temp;
1746                }
1747            }
1748        }
1749    }
1750    if (columnCount == 0) setScrollWidth (null, true);
1751    updateMoveable ();
1752    ignoreColumnResize = false;
1753    if (columnCount != 0) {
1754        /*
1755        * Bug in Windows. When LVM_DELETECOLUMN is used to delete a
1756        * column zero when that column is both the first column in the
1757        * table and the first column in the column order array, Windows
1758        * incorrectly computes the new column order. For example, both
1759        * the orders {0, 3, 1, 2} and {0, 3, 2, 1} give a new column
1760        * order of {0, 2, 1}, while {0, 2, 1, 3} gives {0, 1, 2, 3}.
1761        * The fix is to compute the new order and compare it with the
1762        * order that Windows is using. If the two differ, the new order
1763        * is used.
1764        */

1765        int count = 0;
1766        int oldIndex = oldOrder [orderIndex];
1767        int [] newOrder = new int [columnCount];
1768        for (int i=0; i<oldOrder.length; i++) {
1769            if (oldOrder [i] != oldIndex) {
1770                int newIndex = oldOrder [i] <= oldIndex ? oldOrder [i] : oldOrder [i] - 1;
1771                newOrder [count++] = newIndex;
1772            }
1773        }
1774        OS.SendMessage (handle, OS.LVM_GETCOLUMNORDERARRAY, columnCount, oldOrder);
1775        int j = 0;
1776        while (j < newOrder.length) {
1777            if (oldOrder [j] != newOrder [j]) break;
1778            j++;
1779        }
1780        if (j != newOrder.length) {
1781            OS.SendMessage (handle, OS.LVM_SETCOLUMNORDERARRAY, newOrder.length, newOrder);
1782            /*
1783            * Bug in Windows. When LVM_SETCOLUMNORDERARRAY is used to change
1784            * the column order, the header redraws correctly but the table does
1785            * not. The fix is to force a redraw.
1786            */

1787            OS.InvalidateRect (handle, null, true);
1788        }
1789        TableColumn [] newColumns = new TableColumn [columnCount - orderIndex];
1790        for (int i=orderIndex; i<newOrder.length; i++) {
1791            newColumns [i - orderIndex] = columns [newOrder [i]];
1792            newColumns [i - orderIndex].updateToolTip (newOrder [i]);
1793        }
1794        for (int i=0; i<newColumns.length; i++) {
1795            if (!newColumns [i].isDisposed ()) {
1796                newColumns [i].sendEvent (SWT.Move);
1797            }
1798        }
1799    }
1800    
1801    /* Remove the tool tip item for the header */
1802    if (headerToolTipHandle != 0) {
1803        TOOLINFO lpti = new TOOLINFO ();
1804        lpti.cbSize = TOOLINFO.sizeof;
1805        lpti.uId = column.id;
1806        lpti.hwnd = OS.SendMessage (handle, OS.LVM_GETHEADER, 0, 0);
1807        OS.SendMessage (headerToolTipHandle, OS.TTM_DELTOOL, 0, lpti);
1808    }
1809}
1810
1811void destroyItem (TableItem item) {
1812    int count = OS.SendMessage (handle, OS.LVM_GETITEMCOUNT, 0, 0);
1813    int index = 0;
1814    while (index < count) {
1815        if (items [index] == item) break;
1816        index++;
1817    }
1818    if (index == count) return;
1819    setDeferResize (true);
1820    ignoreSelect = ignoreShrink = true;
1821    int code = OS.SendMessage (handle, OS.LVM_DELETEITEM, index, 0);
1822    ignoreSelect = ignoreShrink = false;
1823    if (code == 0) error (SWT.ERROR_ITEM_NOT_REMOVED);
1824    System.arraycopy (items, index + 1, items, index, --count - index);
1825    items [count] = null;
1826    if (count == 0) setTableEmpty ();
1827    setDeferResize (false);
1828}
1829
1830void fixCheckboxImageList (boolean fixScroll) {
1831    /*
1832    * Bug in Windows. When the state image list is larger than the
1833    * image list, Windows incorrectly positions the state images. When
1834    * the table is scrolled, Windows draws garbage. The fix is to force
1835    * the state image list to be the same size as the image list.
1836    */

1837    if ((style & SWT.CHECK) == 0) return;
1838    int hImageList = OS.SendMessage (handle, OS.LVM_GETIMAGELIST, OS.LVSIL_SMALL, 0);
1839    if (hImageList == 0) return;
1840    int [] cx = new int [1], cy = new int [1];
1841    OS.ImageList_GetIconSize (hImageList, cx, cy);
1842    int hStateList = OS.SendMessage (handle, OS.LVM_GETIMAGELIST, OS.LVSIL_STATE, 0);
1843    if (hStateList == 0) return;
1844    int [] stateCx = new int [1], stateCy = new int [1];
1845    OS.ImageList_GetIconSize (hStateList, stateCx, stateCy);
1846    if (cx [0] == stateCx [0] && cy [0] == stateCy [0]) return;
1847    setCheckboxImageList (cx [0], cy [0], fixScroll);
1848}
1849
1850void fixCheckboxImageListColor (boolean fixScroll) {
1851    if ((style & SWT.CHECK) == 0) return;
1852    int hStateList = OS.SendMessage (handle, OS.LVM_GETIMAGELIST, OS.LVSIL_STATE, 0);
1853    if (hStateList == 0) return;
1854    int [] cx = new int [1], cy = new int [1];
1855    OS.ImageList_GetIconSize (hStateList, cx, cy);
1856    setCheckboxImageList (cx [0], cy [0], fixScroll);
1857}
1858
1859void fixItemHeight (boolean fixScroll) {
1860    /*
1861    * Bug in Windows. When both a header and grid lines are
1862    * displayed, the grid lines do not take into account the
1863    * height of the header and draw in the wrong place. The
1864    * fix is to set the height of the table items to be the
1865    * height of the header so that the lines draw in the right
1866    * place. The height of a table item is the maximum of the
1867    * height of the font or the height of image list.
1868    *
1869    * NOTE: In version 5.80 of COMCTL32.DLL, the bug is fixed.
1870    */

1871    if (itemHeight != -1) return;
1872    if (OS.COMCTL32_VERSION >= OS.VERSION (5, 80)) return;
1873    int bits = OS.SendMessage (handle, OS.LVM_GETEXTENDEDLISTVIEWSTYLE, 0, 0);
1874    if ((bits & OS.LVS_EX_GRIDLINES) == 0) return;
1875    bits = OS.GetWindowLong (handle, OS.GWL_STYLE);
1876    if ((bits & OS.LVS_NOCOLUMNHEADER) != 0) return;
1877    /*
1878    * Bug in Windows. Making any change to an item that
1879    * changes the item height of a table while the table
1880    * is scrolled can cause the lines to draw incorrectly.
1881    * This happens even when the lines are not currently
1882    * visible and are shown afterwards. The fix is to
1883    * save the top index, scroll to the top of the table
1884    * and then restore the original top index.
1885    */

1886    int topIndex = getTopIndex ();
1887    if (fixScroll && topIndex != 0) {
1888        setRedraw (false);
1889        setTopIndex (0);
1890    }
1891    int hOldList = OS.SendMessage (handle, OS.LVM_GETIMAGELIST, OS.LVSIL_SMALL, 0);
1892    if (hOldList != 0) return;
1893    int hwndHeader = OS.SendMessage (handle, OS.LVM_GETHEADER, 0, 0);
1894    RECT rect = new RECT ();
1895    OS.GetWindowRect (hwndHeader, rect);
1896    int height = rect.bottom - rect.top - 1;
1897    int hImageList = OS.ImageList_Create (1, height, 0, 0, 0);
1898    OS.SendMessage (handle, OS.LVM_SETIMAGELIST, OS.LVSIL_SMALL, hImageList);
1899    fixCheckboxImageList (false);
1900    OS.SendMessage (handle, OS.LVM_SETIMAGELIST, OS.LVSIL_SMALL, 0);
1901    if (headerImageList != null) {
1902        int hHeaderImageList = headerImageList.getHandle ();
1903        OS.SendMessage (hwndHeader, OS.HDM_SETIMAGELIST, 0, hHeaderImageList);
1904    }
1905    OS.ImageList_Destroy (hImageList);
1906    if (fixScroll && topIndex != 0) {
1907        setTopIndex (topIndex);
1908        setRedraw (true);
1909    }
1910}
1911
1912/**
1913 * Returns the column at the given, zero-relative index in the
1914 * receiver. Throws an exception if the index is out of range.
1915 * Columns are returned in the order that they were created.
1916 * If no <code>TableColumn</code>s were created by the programmer,
1917 * this method will throw <code>ERROR_INVALID_RANGE</code> despite
1918 * the fact that a single column of data may be visible in the table.
1919 * This occurs when the programmer uses the table like a list, adding
1920 * items but never creating a column.
1921 *
1922 * @param index the index of the column to return
1923 * @return the column at the given index
1924 *
1925 * @exception IllegalArgumentException <ul>
1926 * <li>ERROR_INVALID_RANGE - if the index is not between 0 and the number of elements in the list minus 1 (inclusive)</li>
1927 * </ul>
1928 * @exception SWTException <ul>
1929 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
1930 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
1931 * </ul>
1932 *
1933 * @see Table#getColumnOrder()
1934 * @see Table#setColumnOrder(int[])
1935 * @see TableColumn#getMoveable()
1936 * @see TableColumn#setMoveable(boolean)
1937 * @see SWT#Move
1938 */

1939public TableColumn getColumn (int index) {
1940    checkWidget ();
1941    if (!(0 <= index && index < columnCount)) error (SWT.ERROR_INVALID_RANGE);
1942    return columns [index];
1943}
1944
1945/**
1946 * Returns the number of columns contained in the receiver.
1947 * If no <code>TableColumn</code>s were created by the programmer,
1948 * this value is zero, despite the fact that visually, one column
1949 * of items may be visible. This occurs when the programmer uses
1950 * the table like a list, adding items but never creating a column.
1951 *
1952 * @return the number of columns
1953 *
1954 * @exception SWTException <ul>
1955 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
1956 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
1957 * </ul>
1958 */

1959public int getColumnCount () {
1960    checkWidget ();
1961    return columnCount;
1962}
1963
1964/**
1965 * Returns an array of zero-relative integers that map
1966 * the creation order of the receiver's items to the
1967 * order in which they are currently being displayed.
1968 * <p>
1969 * Specifically, the indices of the returned array represent
1970 * the current visual order of the items, and the contents
1971 * of the array represent the creation order of the items.
1972 * </p><p>
1973 * Note: This is not the actual structure used by the receiver
1974 * to maintain its list of items, so modifying the array will
1975 * not affect the receiver.
1976 * </p>
1977 *
1978 * @return the current visual order of the receiver's items
1979 *
1980 * @exception SWTException <ul>
1981 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
1982 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
1983 * </ul>
1984 *
1985 * @see Table#setColumnOrder(int[])
1986 * @see TableColumn#getMoveable()
1987 * @see TableColumn#setMoveable(boolean)
1988 * @see SWT#Move
1989 *
1990 * @since 3.1
1991 */

1992public int[] getColumnOrder () {
1993    checkWidget ();
1994    if (columnCount == 0) return new int [0];
1995    int [] order = new int [columnCount];
1996    OS.SendMessage (handle, OS.LVM_GETCOLUMNORDERARRAY, columnCount, order);
1997    return order;
1998}
1999
2000/**
2001 * Returns an array of <code>TableColumn</code>s which are the
2002 * columns in the receiver. Columns are returned in the order
2003 * that they were created. If no <code>TableColumn</code>s were
2004 * created by the programmer, the array is empty, despite the fact
2005 * that visually, one column of items may be visible. This occurs
2006 * when the programmer uses the table like a list, adding items but
2007 * never creating a column.
2008 * <p>
2009 * Note: This is not the actual structure used by the receiver
2010 * to maintain its list of items, so modifying the array will
2011 * not affect the receiver.
2012 * </p>
2013 *
2014 * @return the items in the receiver
2015 *
2016 * @exception SWTException <ul>
2017 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
2018 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
2019 * </ul>
2020 *
2021 * @see Table#getColumnOrder()
2022 * @see Table#setColumnOrder(int[])
2023 * @see TableColumn#getMoveable()
2024 * @see TableColumn#setMoveable(boolean)
2025 * @see SWT#Move
2026 */

2027public TableColumn [] getColumns () {
2028    checkWidget ();
2029    TableColumn [] result = new TableColumn [columnCount];
2030    System.arraycopy (columns, 0, result, 0, columnCount);
2031    return result;
2032}
2033
2034int getFocusIndex () {
2035// checkWidget ();
2036
return OS.SendMessage (handle, OS.LVM_GETNEXTITEM, -1, OS.LVNI_FOCUSED);
2037}
2038
2039/**
2040 * Returns the width in pixels of a grid line.
2041 *
2042 * @return the width of a grid line in pixels
2043 *
2044 * @exception SWTException <ul>
2045 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
2046 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
2047 * </ul>
2048 */

2049public int getGridLineWidth () {
2050    checkWidget ();
2051    return GRID_WIDTH;
2052}
2053
2054/**
2055 * Returns the height of the receiver's header
2056 *
2057 * @return the height of the header or zero if the header is not visible
2058 *
2059 * @exception SWTException <ul>
2060 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
2061 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
2062 * </ul>
2063 *
2064 * @since 2.0
2065 */

2066public int getHeaderHeight () {
2067    checkWidget ();
2068    int hwndHeader = OS.SendMessage (handle, OS.LVM_GETHEADER, 0, 0);
2069    if (hwndHeader == 0) return 0;
2070    RECT rect = new RECT ();
2071    OS.GetWindowRect (hwndHeader, rect);
2072    return rect.bottom - rect.top;
2073}
2074
2075/**
2076 * Returns <code>true</code> if the receiver's header is visible,
2077 * and <code>false</code> otherwise.
2078 * <p>
2079 * If one of the receiver's ancestors is not visible or some
2080 * other condition makes the receiver not visible, this method
2081 * may still indicate that it is considered visible even though
2082 * it may not actually be showing.
2083 * </p>
2084 *
2085 * @return the receiver's header's visibility state
2086 *
2087 * @exception SWTException <ul>
2088 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
2089 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
2090 * </ul>
2091 */

2092public boolean getHeaderVisible () {
2093    checkWidget ();
2094    int bits = OS.GetWindowLong (handle, OS.GWL_STYLE);
2095    return (bits & OS.LVS_NOCOLUMNHEADER) == 0;
2096}
2097
2098/**
2099 * Returns the item at the given, zero-relative index in the
2100 * receiver. Throws an exception if the index is out of range.
2101 *
2102 * @param index the index of the item to return
2103 * @return the item at the given index
2104 *
2105 * @exception IllegalArgumentException <ul>
2106 * <li>ERROR_INVALID_RANGE - if the index is not between 0 and the number of elements in the list minus 1 (inclusive)</li>
2107 * </ul>
2108 * @exception SWTException <ul>
2109 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
2110 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
2111 * </ul>
2112 */

2113public TableItem getItem (int index) {
2114    checkWidget ();
2115    int count = OS.SendMessage (handle, OS.LVM_GETITEMCOUNT, 0, 0);
2116    if (!(0 <= index && index < count)) error (SWT.ERROR_INVALID_RANGE);
2117    return _getItem (index);
2118}
2119
2120/**
2121 * Returns the item at the given point in the receiver
2122 * or null if no such item exists. The point is in the
2123 * coordinate system of the receiver.
2124 * <p>
2125 * The item that is returned represents an item that could be selected by the user.
2126 * For example, if selection only occurs in items in the first column, then null is
2127 * returned if the point is outside of the item.
2128 * Note that the SWT.FULL_SELECTION style hint, which specifies the selection policy,
2129 * determines the extent of the selection.
2130 * </p>
2131 *
2132 * @param point the point used to locate the item
2133 * @return the item at the given point, or null if the point is not in a selectable item
2134 *
2135 * @exception IllegalArgumentException <ul>
2136 * <li>ERROR_NULL_ARGUMENT - if the point is null</li>
2137 * </ul>
2138 * @exception SWTException <ul>
2139 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
2140 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
2141 * </ul>
2142 */

2143public TableItem getItem (Point point) {
2144    checkWidget ();
2145    if (point == null) error (SWT.ERROR_NULL_ARGUMENT);
2146    LVHITTESTINFO pinfo = new LVHITTESTINFO ();
2147    pinfo.x = point.x; pinfo.y = point.y;
2148    OS.SendMessage (handle, OS.LVM_HITTEST, 0, pinfo);
2149    if (pinfo.iItem != -1) {
2150        /*
2151        * Bug in Windows. When the point that is used by
2152        * LVM_HITTEST is inside the header, Windows returns
2153        * the first item in the table. The fix is to check
2154        * when LVM_HITTEST returns the first item and make
2155        * sure that when the point is within the header,
2156        * the first item is not returned.
2157        */

2158        if (pinfo.iItem == 0) {
2159            int bits = OS.GetWindowLong (handle, OS.GWL_STYLE);
2160            if ((bits & OS.LVS_NOCOLUMNHEADER) == 0) {
2161                int hwndHeader = OS.SendMessage (handle, OS.LVM_GETHEADER, 0, 0);
2162                if (hwndHeader != 0) {
2163                    RECT rect = new RECT ();
2164                    OS.GetWindowRect (hwndHeader, rect);
2165                    POINT pt = new POINT ();
2166                    pt.x = pinfo.x;
2167                    pt.y = pinfo.y;
2168                    OS.MapWindowPoints (handle, 0, pt, 1);
2169                    if (OS.PtInRect (rect, pt)) return null;
2170                }
2171            }
2172        }
2173        return _getItem (pinfo.iItem);
2174    }
2175    return null;
2176}
2177
2178/**
2179 * Returns the number of items contained in the receiver.
2180 *
2181 * @return the number of items
2182 *
2183 * @exception SWTException <ul>
2184 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
2185 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
2186 * </ul>
2187 */

2188public int getItemCount () {
2189    checkWidget ();
2190    return OS.SendMessage (handle, OS.LVM_GETITEMCOUNT, 0, 0);
2191}
2192
2193/**
2194 * Returns the height of the area which would be used to
2195 * display <em>one</em> of the items in the receiver's.
2196 *
2197 * @return the height of one item
2198 *
2199 * @exception SWTException <ul>
2200 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
2201 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
2202 * </ul>
2203 */

2204public int getItemHeight () {
2205    checkWidget ();
2206    int empty = OS.SendMessage (handle, OS.LVM_APPROXIMATEVIEWRECT, 0, 0);
2207    int oneItem = OS.SendMessage (handle, OS.LVM_APPROXIMATEVIEWRECT, 1, 0);
2208    return (oneItem >> 16) - (empty >> 16);
2209}
2210
2211/**
2212 * Returns a (possibly empty) array of <code>TableItem</code>s which
2213 * are the items in the receiver.
2214 * <p>
2215 * Note: This is not the actual structure used by the receiver
2216 * to maintain its list of items, so modifying the array will
2217 * not affect the receiver.
2218 * </p>
2219 *
2220 * @return the items in the receiver
2221 *
2222 * @exception SWTException <ul>
2223 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
2224 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
2225 * </ul>
2226 */

2227public TableItem [] getItems () {
2228    checkWidget ();
2229    int count = OS.SendMessage (handle, OS.LVM_GETITEMCOUNT, 0, 0);
2230    TableItem [] result = new TableItem [count];
2231    if ((style & SWT.VIRTUAL) != 0) {
2232        for (int i=0; i<count; i++) {
2233            result [i] = _getItem (i);
2234        }
2235    } else {
2236        System.arraycopy (items, 0, result, 0, count);
2237    }
2238    return result;
2239}
2240
2241/**
2242 * Returns <code>true</code> if the receiver's lines are visible,
2243 * and <code>false</code> otherwise.
2244 * <p>
2245 * If one of the receiver's ancestors is not visible or some
2246 * other condition makes the receiver not visible, this method
2247 * may still indicate that it is considered visible even though
2248 * it may not actually be showing.
2249 * </p>
2250 *
2251 * @return the visibility state of the lines
2252 *
2253 * @exception SWTException <ul>
2254 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
2255 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
2256 * </ul>
2257 */

2258public boolean getLinesVisible () {
2259    checkWidget ();
2260    int bits = OS.SendMessage (handle, OS.LVM_GETEXTENDEDLISTVIEWSTYLE, 0, 0);
2261    return (bits & OS.LVS_EX_GRIDLINES) != 0;
2262}
2263
2264/**
2265 * Returns an array of <code>TableItem</code>s that are currently
2266 * selected in the receiver. The order of the items is unspecified.
2267 * An empty array indicates that no items are selected.
2268 * <p>
2269 * Note: This is not the actual structure used by the receiver
2270 * to maintain its selection, so modifying the array will
2271 * not affect the receiver.
2272 * </p>
2273 * @return an array representing the selection
2274 *
2275 * @exception SWTException <ul>
2276 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
2277 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
2278 * </ul>
2279 */

2280public TableItem [] getSelection () {
2281    checkWidget ();
2282    int i = -1, j = 0, count = OS.SendMessage (handle, OS.LVM_GETSELECTEDCOUNT, 0, 0);
2283    TableItem [] result = new TableItem [count];
2284    while ((i = OS.SendMessage (handle, OS.LVM_GETNEXTITEM, i, OS.LVNI_SELECTED)) != -1) {
2285        result [j++] = _getItem (i);
2286    }
2287    return result;
2288}
2289
2290/**
2291 * Returns the number of selected items contained in the receiver.
2292 *
2293 * @return the number of selected items
2294 *
2295 * @exception SWTException <ul>
2296 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
2297 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
2298 * </ul>
2299 */

2300public int getSelectionCount () {
2301    checkWidget ();
2302    return OS.SendMessage (handle, OS.LVM_GETSELECTEDCOUNT, 0, 0);
2303}
2304
2305/**
2306 * Returns the zero-relative index of the item which is currently
2307 * selected in the receiver, or -1 if no item is selected.
2308 *
2309 * @return the index of the selected item
2310 *
2311 * @exception SWTException <ul>
2312 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
2313 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
2314 * </ul>
2315 */

2316public int getSelectionIndex () {
2317    checkWidget ();
2318    int focusIndex = OS.SendMessage (handle, OS.LVM_GETNEXTITEM, -1, OS.LVNI_FOCUSED);
2319    int selectedIndex = OS.SendMessage (handle, OS.LVM_GETNEXTITEM, -1, OS.LVNI_SELECTED);
2320    if (focusIndex == selectedIndex) return selectedIndex;
2321    int i = -1;
2322    while ((i = OS.SendMessage (handle, OS.LVM_GETNEXTITEM, i, OS.LVNI_SELECTED)) != -1) {
2323        if (i == focusIndex) return i;
2324    }
2325    return selectedIndex;
2326}
2327
2328/**
2329 * Returns the zero-relative indices of the items which are currently
2330 * selected in the receiver. The order of the indices is unspecified.
2331 * The array is empty if no items are selected.
2332 * <p>
2333 * Note: This is not the actual structure used by the receiver
2334 * to maintain its selection, so modifying the array will
2335 * not affect the receiver.
2336 * </p>
2337 * @return the array of indices of the selected items
2338 *
2339 * @exception SWTException <ul>
2340 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
2341 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
2342 * </ul>
2343 */

2344public int [] getSelectionIndices () {
2345    checkWidget ();
2346    int i = -1, j = 0, count = OS.SendMessage (handle, OS.LVM_GETSELECTEDCOUNT, 0, 0);
2347    int [] result = new int [count];
2348    while ((i = OS.SendMessage (handle, OS.LVM_GETNEXTITEM, i, OS.LVNI_SELECTED)) != -1) {
2349        result [j++] = i;
2350    }
2351    return result;
2352}
2353
2354/**
2355 * Returns the column which shows the sort indicator for
2356 * the receiver. The value may be null if no column shows
2357 * the sort indicator.
2358 *
2359 * @return the sort indicator
2360 *
2361 * @exception SWTException <ul>
2362 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
2363 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
2364 * </ul>
2365 *
2366 * @see #setSortColumn(TableColumn)
2367 *
2368 * @since 3.2
2369 */

2370public TableColumn getSortColumn () {
2371    checkWidget ();
2372    return sortColumn;
2373}
2374
2375int getSortColumnPixel () {
2376    int pixel = OS.IsWindowEnabled (handle) ? getBackgroundPixel () : OS.GetSysColor (OS.COLOR_3DFACE);
2377    int red = pixel & 0xFF;
2378    int green = (pixel & 0xFF00) >> 8;
2379    int blue = (pixel & 0xFF0000) >> 16;
2380    if (red > 240 && green > 240 && blue > 240) {
2381        red -= 8;
2382        green -= 8;
2383        blue -= 8;
2384    } else {
2385        red = Math.min (0xFF, (red / 10) + red);
2386        green = Math.min (0xFF, (green / 10) + green);
2387        blue = Math.min (0xFF, (blue / 10) + blue);
2388    }
2389    return (red & 0xFF) | ((green & 0xFF) << 8) | ((blue & 0xFF) << 16);
2390}
2391
2392/**
2393 * Returns the direction of the sort indicator for the receiver.
2394 * The value will be one of <code>UP</code>, <code>DOWN</code>
2395 * or <code>NONE</code>.
2396 *
2397 * @return the sort direction
2398 *
2399 * @exception SWTException <ul>
2400 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
2401 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
2402 * </ul>
2403 *
2404 * @see #setSortDirection(int)
2405 *
2406 * @since 3.2
2407 */

2408public int getSortDirection () {
2409    checkWidget ();
2410    return sortDirection;
2411}
2412
2413/**
2414 * Returns the zero-relative index of the item which is currently
2415 * at the top of the receiver. This index can change when items are
2416 * scrolled or new items are added or removed.
2417 *
2418 * @return the index of the top item
2419 *
2420 * @exception SWTException <ul>
2421 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
2422 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
2423 * </ul>
2424 */

2425public int getTopIndex () {
2426    checkWidget ();
2427    /*
2428    * Bug in Windows. Under rare circumstances, LVM_GETTOPINDEX
2429    * can return a negative number. When this happens, the table
2430    * is displaying blank lines at the top of the controls. The
2431    * fix is to check for a negative number and return zero instead.
2432    */

2433    return Math.max (0, OS.SendMessage (handle, OS.LVM_GETTOPINDEX, 0, 0));
2434}
2435
2436boolean hasChildren () {
2437    int hwndHeader = OS.SendMessage (handle, OS.LVM_GETHEADER, 0, 0);
2438    int hwndChild = OS.GetWindow (handle, OS.GW_CHILD);
2439    while (hwndChild != 0) {
2440        if (hwndChild != hwndHeader) return true;
2441        hwndChild = OS.GetWindow (hwndChild, OS.GW_HWNDNEXT);
2442    }
2443    return false;
2444}
2445
2446int imageIndex (Image image, int column) {
2447    if (image == null) return OS.I_IMAGENONE;
2448    if (column == 0) {
2449        firstColumnImage = true;
2450    } else {
2451        setSubImagesVisible (true);
2452    }
2453    if (imageList == null) {
2454        Rectangle bounds = image.getBounds ();
2455        imageList = display.getImageList (style & SWT.RIGHT_TO_LEFT, bounds.width, bounds.height);
2456        int index = imageList.indexOf (image);
2457        if (index == -1) index = imageList.add (image);
2458        int hImageList = imageList.getHandle ();
2459        /*
2460        * Bug in Windows. Making any change to an item that
2461        * changes the item height of a table while the table
2462        * is scrolled can cause the lines to draw incorrectly.
2463        * This happens even when the lines are not currently
2464        * visible and are shown afterwards. The fix is to
2465        * save the top index, scroll to the top of the table
2466        * and then restore the original top index.
2467        */

2468        int topIndex = getTopIndex ();
2469        if (topIndex != 0) {
2470            setRedraw (false);
2471            setTopIndex (0);
2472        }
2473        OS.SendMessage (handle, OS.LVM_SETIMAGELIST, OS.LVSIL_SMALL, hImageList);
2474        if (headerImageList != null) {
2475            int hwndHeader = OS.SendMessage (handle, OS.LVM_GETHEADER, 0, 0);
2476            int hHeaderImageList = headerImageList.getHandle ();
2477            OS.SendMessage (hwndHeader, OS.HDM_SETIMAGELIST, 0, hHeaderImageList);
2478        }
2479        fixCheckboxImageList (false);
2480        if (itemHeight != -1) setItemHeight (false);
2481        if (topIndex != 0) {
2482            setTopIndex (topIndex);
2483            setRedraw (true);
2484        }
2485        return index;
2486    }
2487    int index = imageList.indexOf (image);
2488    if (index != -1) return index;
2489    return imageList.add (image);
2490}
2491
2492int imageIndexHeader (Image image) {
2493    if (image == null) return OS.I_IMAGENONE;
2494    if (headerImageList == null) {
2495        Rectangle bounds = image.getBounds ();
2496        headerImageList = display.getImageList (style & SWT.RIGHT_TO_LEFT, bounds.width, bounds.height);
2497        int index = headerImageList.indexOf (image);
2498        if (index == -1) index = headerImageList.add (image);
2499        int hImageList = headerImageList.getHandle ();
2500        int hwndHeader = OS.SendMessage (handle, OS.LVM_GETHEADER, 0, 0);
2501        OS.SendMessage (hwndHeader, OS.HDM_SETIMAGELIST, 0, hImageList);
2502        return index;
2503    }
2504    int index = headerImageList.indexOf (image);
2505    if (index != -1) return index;
2506    return headerImageList.add (image);
2507}
2508
2509/**
2510 * Searches the receiver's list starting at the first column
2511 * (index 0) until a column is found that is equal to the
2512 * argument, and returns the index of that column. If no column
2513 * is found, returns -1.
2514 *
2515 * @param column the search column
2516 * @return the index of the column
2517 *
2518 * @exception IllegalArgumentException <ul>
2519 * <li>ERROR_NULL_ARGUMENT - if the column is null</li>
2520 * </ul>
2521 * @exception SWTException <ul>
2522 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
2523 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
2524 * </ul>
2525 */

2526public int indexOf (TableColumn column) {
2527    checkWidget ();
2528    if (column == null) error (SWT.ERROR_NULL_ARGUMENT);
2529    for (int i=0; i<columnCount; i++) {
2530        if (columns [i] == column) return i;
2531    }
2532    return -1;
2533}
2534
2535/**
2536 * Searches the receiver's list starting at the first item
2537 * (index 0) until an item is found that is equal to the
2538 * argument, and returns the index of that item. If no item
2539 * is found, returns -1.
2540 *
2541 * @param item the search item
2542 * @return the index of the item
2543 *
2544 * @exception IllegalArgumentException <ul>
2545 * <li>ERROR_NULL_ARGUMENT - if the item is null</li>
2546 * </ul>
2547 * @exception SWTException <ul>
2548 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
2549 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
2550 * </ul>
2551 */

2552public int indexOf (TableItem item) {
2553    checkWidget ();
2554    if (item == null) error (SWT.ERROR_NULL_ARGUMENT);
2555    int count = OS.SendMessage (handle, OS.LVM_GETITEMCOUNT, 0, 0);
2556    if (1 <= lastIndexOf && lastIndexOf < count - 1) {
2557        if (items [lastIndexOf] == item) return lastIndexOf;
2558        if (items [lastIndexOf + 1] == item) return ++lastIndexOf;
2559        if (items [lastIndexOf - 1] == item) return --lastIndexOf;
2560    }
2561    if (lastIndexOf < count / 2) {
2562        for (int i=0; i<count; i++) {
2563            if (items [i] == item) return lastIndexOf = i;
2564        }
2565    } else {
2566        for (int i=count - 1; i>=0; --i) {
2567            if (items [i] == item) return lastIndexOf = i;
2568        }
2569    }
2570    return -1;
2571}
2572
2573/**
2574 * Returns <code>true</code> if the item is selected,
2575 * and <code>false</code> otherwise. Indices out of
2576 * range are ignored.
2577 *
2578 * @param index the index of the item
2579 * @return the selection state of the item at the index
2580 *
2581 * @exception SWTException <ul>
2582 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
2583 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
2584 * </ul>
2585 */

2586public boolean isSelected (int index) {
2587    checkWidget ();
2588    LVITEM lvItem = new LVITEM ();
2589    lvItem.mask = OS.LVIF_STATE;
2590    lvItem.stateMask = OS.LVIS_SELECTED;
2591    lvItem.iItem = index;
2592    int result = OS.SendMessage (handle, OS.LVM_GETITEM, 0, lvItem);
2593    return (result != 0) && ((lvItem.state & OS.LVIS_SELECTED) != 0);
2594}
2595
2596void register () {
2597    super.register ();
2598    int hwndHeader = OS.SendMessage (handle, OS.LVM_GETHEADER, 0, 0);
2599    if (hwndHeader != 0) display.addControl (hwndHeader, this);
2600}
2601
2602void releaseChildren (boolean destroy) {
2603    if (items != null) {
2604        int itemCount = OS.SendMessage (handle, OS.LVM_GETITEMCOUNT, 0, 0);
2605        /*
2606        * Feature in Windows 98. When there are a large number
2607        * of columns and items in a table (>1000) where each
2608        * of the subitems in the table has a string, it is much
2609        * faster to delete each item with LVM_DELETEITEM rather
2610        * than using LVM_DELETEALLITEMS. The fix is to detect
2611        * this case and delete the items, one by one. The fact
2612        * that the fix is only necessary on Windows 98 was
2613        * confirmed using version 5.81 of COMCTL32.DLL on both
2614        * Windows 98 and NT.
2615        *
2616        * NOTE: LVM_DELETEALLITEMS is also sent by the table
2617        * when the table is destroyed.
2618        */

2619        if (OS.IsWin95 && columnCount > 1) {
2620            /* Turn off redraw and resize events and leave them off */
2621            resizeCount = 1;
2622            OS.SendMessage (handle, OS.WM_SETREDRAW, 0, 0);
2623            for (int i=itemCount-1; i>=0; --i) {
2624                TableItem item = items [i];
2625                if (item != null && !item.isDisposed ()) item.release (false);
2626                ignoreSelect = ignoreShrink = true;
2627                OS.SendMessage (handle, OS.LVM_DELETEITEM, i, 0);
2628                ignoreSelect = ignoreShrink = false;
2629            }
2630        } else {
2631            for (int i=0; i<itemCount; i++) {
2632                TableItem item = items [i];
2633                if (item != null && !item.isDisposed ()) item.release (false);
2634            }
2635        }
2636        items = null;
2637    }
2638    if (columns != null) {
2639        for (int i=0; i<columnCount; i++) {
2640            TableColumn column = columns [i];
2641            if (!column.isDisposed ()) column.release (false);
2642        }
2643        columns = null;
2644    }
2645    super.releaseChildren (destroy);
2646}
2647    
2648void releaseWidget () {
2649    super.releaseWidget ();
2650    customDraw = false;
2651    currentItem = null;
2652    if (imageList != null) {
2653        OS.SendMessage (handle, OS.LVM_SETIMAGELIST, OS.LVSIL_SMALL, 0);
2654        display.releaseImageList (imageList);
2655    }
2656    if (headerImageList != null) {
2657        int hwndHeader = OS.SendMessage (handle, OS.LVM_GETHEADER, 0, 0);
2658        OS.SendMessage (hwndHeader, OS.HDM_SETIMAGELIST, 0, 0);
2659        display.releaseImageList (headerImageList);
2660    }
2661    imageList = headerImageList = null;
2662    int hStateList = OS.SendMessage (handle, OS.LVM_GETIMAGELIST, OS.LVSIL_STATE, 0);
2663    OS.SendMessage (handle, OS.LVM_SETIMAGELIST, OS.LVSIL_STATE, 0);
2664    if (hStateList != 0) OS.ImageList_Destroy (hStateList);
2665    if (headerToolTipHandle != 0) OS.DestroyWindow (headerToolTipHandle);
2666    headerToolTipHandle = 0;
2667}
2668
2669/**
2670 * Removes the items from the receiver's list at the given
2671 * zero-relative indices.
2672 *
2673 * @param indices the array of indices of the items
2674 *
2675 * @exception IllegalArgumentException <ul>
2676 * <li>ERROR_INVALID_RANGE - if the index is not between 0 and the number of elements in the list minus 1 (inclusive)</li>
2677 * <li>ERROR_NULL_ARGUMENT - if the indices array is null</li>
2678 * </ul>
2679 * @exception SWTException <ul>
2680 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
2681 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
2682 * </ul>
2683 */

2684public void remove (int [] indices) {
2685    checkWidget ();
2686    if (indices == null) error (SWT.ERROR_NULL_ARGUMENT);
2687    if (indices.length == 0) return;
2688    int [] newIndices = new int [indices.length];
2689    System.arraycopy (indices, 0, newIndices, 0, indices.length);
2690    sort (newIndices);
2691    int start = newIndices [newIndices.length - 1], end = newIndices [0];
2692    int count = OS.SendMessage (handle, OS.LVM_GETITEMCOUNT, 0, 0);
2693    if (!(0 <= start && start <= end && end < count)) {
2694        error (SWT.ERROR_INVALID_RANGE);
2695    }
2696    setDeferResize (true);
2697    int last = -1;
2698    for (int i=0; i<newIndices.length; i++) {
2699        int index = newIndices [i];
2700        if (index != last) {
2701            TableItem item = items [index];
2702            if (item != null && !item.isDisposed ()) item.release (false);
2703            ignoreSelect = ignoreShrink = true;
2704            int code = OS.SendMessage (handle, OS.LVM_DELETEITEM, index, 0);
2705            ignoreSelect = ignoreShrink = false;
2706            if (code == 0) error (SWT.ERROR_ITEM_NOT_REMOVED);
2707            System.arraycopy (items, index + 1, items, index, --count - index);
2708            items [count] = null;
2709            last = index;
2710        }
2711    }
2712    if (count == 0) setTableEmpty ();
2713    setDeferResize (false);
2714}
2715
2716/**
2717 * Removes the item from the receiver at the given
2718 * zero-relative index.
2719 *
2720 * @param index the index for the item
2721 *
2722 * @exception IllegalArgumentException <ul>
2723 * <li>ERROR_INVALID_RANGE - if the index is not between 0 and the number of elements in the list minus 1 (inclusive)</li>
2724 * </ul>
2725 * @exception SWTException <ul>
2726 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
2727 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
2728 * </ul>
2729 */

2730public void remove (int index) {
2731    checkWidget ();
2732    int count = OS.SendMessage (handle, OS.LVM_GETITEMCOUNT, 0, 0);
2733    if (!(0 <= index && index < count)) error (SWT.ERROR_INVALID_RANGE);
2734    TableItem item = items [index];
2735    if (item != null && !item.isDisposed ()) item.release (false);
2736    setDeferResize (true);
2737    ignoreSelect = ignoreShrink = true;
2738    int code = OS.SendMessage (handle, OS.LVM_DELETEITEM, index, 0);
2739    ignoreSelect = ignoreShrink = false;
2740    if (code == 0) error (SWT.ERROR_ITEM_NOT_REMOVED);
2741    System.arraycopy (items, index + 1, items, index, --count - index);
2742    items [count] = null;
2743    if (count == 0) setTableEmpty ();
2744    setDeferResize (false);
2745}
2746
2747/**
2748 * Removes the items from the receiver which are
2749 * between the given zero-relative start and end
2750 * indices (inclusive).
2751 *
2752 * @param start the start of the range
2753 * @param end the end of the range
2754 *
2755 * @exception IllegalArgumentException <ul>
2756 * <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>
2757 * </ul>
2758 * @exception SWTException <ul>
2759 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
2760 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
2761 * </ul>
2762 */

2763public void remove (int start, int end) {
2764    checkWidget ();
2765    if (start > end) return;
2766    int count = OS.SendMessage (handle, OS.LVM_GETITEMCOUNT, 0, 0);
2767    if (!(0 <= start && start <= end && end < count)) {
2768        error (SWT.ERROR_INVALID_RANGE);
2769    }
2770    if (start == 0 && end == count - 1) {
2771        removeAll ();
2772    } else {
2773        setDeferResize (true);
2774        int index = start;
2775        while (index <= end) {
2776            TableItem item = items [index];
2777            if (item != null && !item.isDisposed ()) item.release (false);
2778            ignoreSelect = ignoreShrink = true;
2779            int code = OS.SendMessage (handle, OS.LVM_DELETEITEM, start, 0);
2780            ignoreSelect = ignoreShrink = false;
2781            if (code == 0) break;
2782            index++;
2783        }
2784        System.arraycopy (items, index, items, start, count - index);
2785        for (int i=count-(index-start); i<count; i++) items [i] = null;
2786        if (index <= end) error (SWT.ERROR_ITEM_NOT_REMOVED);
2787        /*
2788        * This code is intentionally commented. It is not necessary
2789        * to check for an empty table because removeAll() was called
2790        * when the start == 0 and end == count - 1.
2791        */

2792        //if (count - index == 0) setTableEmpty ();
2793
setDeferResize (false);
2794    }
2795}
2796
2797/**
2798 * Removes all of the items from the receiver.
2799 *
2800 * @exception SWTException <ul>
2801 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
2802 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
2803 * </ul>
2804 */

2805public void removeAll () {
2806    checkWidget ();
2807    int itemCount = OS.SendMessage (handle, OS.LVM_GETITEMCOUNT, 0, 0);
2808    for (int i=0; i<itemCount; i++) {
2809        TableItem item = items [i];
2810        if (item != null && !item.isDisposed ()) item.release (false);
2811    }
2812    /*
2813    * Feature in Windows 98. When there are a large number
2814    * of columns and items in a table (>1000) where each
2815    * of the subitems in the table has a string, it is much
2816    * faster to delete each item with LVM_DELETEITEM rather
2817    * than using LVM_DELETEALLITEMS. The fix is to detect
2818    * this case and delete the items, one by one. The fact
2819    * that the fix is only necessary on Windows 98 was
2820    * confirmed using version 5.81 of COMCTL32.DLL on both
2821    * Windows 98 and NT.
2822    *
2823    * NOTE: LVM_DELETEALLITEMS is also sent by the table
2824    * when the table is destroyed.
2825    */

2826    setDeferResize (true);
2827    if (OS.IsWin95 && columnCount > 1) {
2828        boolean redraw = drawCount == 0 && OS.IsWindowVisible (handle);
2829        if (redraw) OS.SendMessage (handle, OS.WM_SETREDRAW, 0, 0);
2830        int index = itemCount - 1;
2831        while (index >= 0) {
2832            ignoreSelect = ignoreShrink = true;
2833            int code = OS.SendMessage (handle, OS.LVM_DELETEITEM, index, 0);
2834            ignoreSelect = ignoreShrink = false;
2835            if (code == 0) break;
2836            --index;
2837        }
2838        if (redraw) {
2839            OS.SendMessage (handle, OS.WM_SETREDRAW, 1, 0);
2840            /*
2841            * This code is intentionally commented. The window proc
2842            * for the table implements WM_SETREDRAW to invalidate
2843            * and erase the table so it is not necessary to do this
2844            * again.
2845            */

2846// int flags = OS.RDW_ERASE | OS.RDW_FRAME | OS.RDW_INVALIDATE;
2847
// OS.RedrawWindow (handle, null, 0, flags);
2848
}
2849        if (index != -1) error (SWT.ERROR_ITEM_NOT_REMOVED);
2850    } else {
2851        ignoreSelect = ignoreShrink = true;
2852        int code = OS.SendMessage (handle, OS.LVM_DELETEALLITEMS, 0, 0);
2853        ignoreSelect = ignoreShrink = false;
2854        if (code == 0) error (SWT.ERROR_ITEM_NOT_REMOVED);
2855    }
2856    setTableEmpty ();
2857    setDeferResize (false);
2858}
2859
2860/**
2861 * Removes the listener from the collection of listeners who will
2862 * be notified when the user changes the receiver's selection.
2863 *
2864 * @param listener the listener which should no longer be notified
2865 *
2866 * @exception IllegalArgumentException <ul>
2867 * <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
2868 * </ul>
2869 * @exception SWTException <ul>
2870 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
2871 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
2872 * </ul>
2873 *
2874 * @see SelectionListener
2875 * @see #addSelectionListener(SelectionListener)
2876 */

2877public void removeSelectionListener(SelectionListener listener) {
2878    checkWidget ();
2879    if (listener == null) error (SWT.ERROR_NULL_ARGUMENT);
2880    if (eventTable == null) return;
2881    eventTable.unhook (SWT.Selection, listener);
2882    eventTable.unhook (SWT.DefaultSelection,listener);
2883}
2884
2885/**
2886 * Selects the items at the given zero-relative indices in the receiver.
2887 * The current selection is not cleared before the new items are selected.
2888 * <p>
2889 * If the item at a given index is not selected, it is selected.
2890 * If the item at a given index was already selected, it remains selected.
2891 * Indices that are out of range and duplicate indices are ignored.
2892 * If the receiver is single-select and multiple indices are specified,
2893 * then all indices are ignored.
2894 * </p>
2895 *
2896 * @param indices the array of indices for the items to select
2897 *
2898 * @exception IllegalArgumentException <ul>
2899 * <li>ERROR_NULL_ARGUMENT - if the array of indices is null</li>
2900 * </ul>
2901 * @exception SWTException <ul>
2902 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
2903 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
2904 * </ul>
2905 *
2906 * @see Table#setSelection(int[])
2907 */

2908public void select (int [] indices) {
2909    checkWidget ();
2910    if (indices == null) error (SWT.ERROR_NULL_ARGUMENT);
2911    int length = indices.length;
2912    if (length == 0 || ((style & SWT.SINGLE) != 0 && length > 1)) return;
2913    LVITEM lvItem = new LVITEM ();
2914    lvItem.state = OS.LVIS_SELECTED;
2915    lvItem.stateMask = OS.LVIS_SELECTED;
2916    for (int i=length-1; i>=0; --i) {
2917        /*
2918        * An index of -1 will apply the change to all
2919        * items. Ensure that indices are greater than -1.
2920        */

2921        if (indices [i] >= 0) {
2922            ignoreSelect = true;
2923            OS.SendMessage (handle, OS.LVM_SETITEMSTATE, indices [i], lvItem);
2924            ignoreSelect = false;
2925        }
2926    }
2927}
2928
2929/**
2930 * Selects the item at the given zero-relative index in the receiver.
2931 * If the item at the index was already selected, it remains
2932 * selected. Indices that are out of range are ignored.
2933 *
2934 * @param index the index of the item to select
2935 *
2936 * @exception SWTException <ul>
2937 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
2938 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
2939 * </ul>
2940 */

2941public void select (int index) {
2942    checkWidget ();
2943    /*
2944    * An index of -1 will apply the change to all
2945    * items. Ensure that index is greater than -1.
2946    */

2947    if (index < 0) return;
2948    LVITEM lvItem = new LVITEM ();
2949    lvItem.state = OS.LVIS_SELECTED;
2950    lvItem.stateMask = OS.LVIS_SELECTED;
2951    ignoreSelect = true;
2952    OS.SendMessage (handle, OS.LVM_SETITEMSTATE, index, lvItem);
2953    ignoreSelect = false;
2954}
2955
2956/**
2957 * Selects the items in the range specified by the given zero-relative
2958 * indices in the receiver. The range of indices is inclusive.
2959 * The current selection is not cleared before the new items are selected.
2960 * <p>
2961 * If an item in the given range is not selected, it is selected.
2962 * If an item in the given range was already selected, it remains selected.
2963 * Indices that are out of range are ignored and no items will be selected
2964 * if start is greater than end.
2965 * If the receiver is single-select and there is more than one item in the
2966 * given range, then all indices are ignored.
2967 * </p>
2968 *
2969 * @param start the start of the range
2970 * @param end the end of the range
2971 *
2972 * @exception SWTException <ul>
2973 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
2974 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
2975 * </ul>
2976 *
2977 * @see Table#setSelection(int,int)
2978 */

2979public void select (int start, int end) {
2980    checkWidget ();
2981    if (end < 0 || start > end || ((style & SWT.SINGLE) != 0 && start != end)) return;
2982    int count = OS.SendMessage (handle, OS.LVM_GETITEMCOUNT, 0, 0);
2983    if (count == 0 || start >= count) return;
2984    start = Math.max (0, start);
2985    end = Math.min (end, count - 1);
2986    if (start == 0 && end == count - 1) {
2987        selectAll ();
2988    } else {
2989        /*
2990        * An index of -1 will apply the change to all
2991        * items. Indices must be greater than -1.
2992        */

2993        LVITEM lvItem = new LVITEM ();
2994        lvItem.state = OS.LVIS_SELECTED;
2995        lvItem.stateMask = OS.LVIS_SELECTED;
2996        for (int i=start; i<=end; i++) {
2997            ignoreSelect = true;
2998            OS.SendMessage (handle, OS.LVM_SETITEMSTATE, i, lvItem);
2999            ignoreSelect = false;
3000        }
3001    }
3002}
3003
3004/**
3005 * Selects all of the items in the receiver.
3006 * <p>
3007 * If the receiver is single-select, do nothing.
3008 * </p>
3009 *
3010 * @exception SWTException <ul>
3011 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
3012 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
3013 * </ul>
3014 */

3015public void selectAll () {
3016    checkWidget ();
3017    if ((style & SWT.SINGLE) != 0) return;
3018    LVITEM lvItem = new LVITEM ();
3019    lvItem.mask = OS.LVIF_STATE;
3020    lvItem.state = OS.LVIS_SELECTED;
3021    lvItem.stateMask = OS.LVIS_SELECTED;
3022    ignoreSelect = true;
3023    OS.SendMessage (handle, OS.LVM_SETITEMSTATE, -1, lvItem);
3024    ignoreSelect = false;
3025}
3026
3027void sendEraseItemEvent (TableItem item, NMLVCUSTOMDRAW nmcd, int lParam) {
3028    int hDC = nmcd.hdc;
3029    int hFont = item.cellFont != null ? item.cellFont [nmcd.iSubItem] : -1;
3030    if (hFont == -1) hFont = item.font;
3031    int clrText = item.cellForeground != null ? item.cellForeground [nmcd.iSubItem] : -1;
3032    if (clrText == -1) clrText = item.foreground;
3033    int clrTextBk = -1;
3034    if (OS.COMCTL32_MAJOR >= 6 && OS.IsAppThemed ()) {
3035        if (sortColumn != null && sortDirection != SWT.NONE) {
3036            if (findImageControl () == null) {
3037                if (indexOf (sortColumn) == nmcd.iSubItem) {
3038                    clrTextBk = getSortColumnPixel ();
3039                }
3040            }
3041        }
3042    }
3043    clrTextBk = item.cellBackground != null ? item.cellBackground [nmcd.iSubItem] : -1;
3044    if (clrTextBk == -1) clrTextBk = item.background;
3045    /*
3046    * Bug in Windows. For some reason, CDIS_SELECTED always set,
3047    * even for items that are not selected. The fix is to get
3048    * the selection state from the item.
3049    */

3050    LVITEM lvItem = new LVITEM ();
3051    lvItem.mask = OS.LVIF_STATE;
3052    lvItem.stateMask = OS.LVIS_SELECTED;
3053    lvItem.iItem = nmcd.dwItemSpec;
3054    int result = OS.SendMessage (handle, OS.LVM_GETITEM, 0, lvItem);
3055    boolean selected = (result != 0 && (lvItem.state & OS.LVIS_SELECTED) != 0);
3056    GCData data = new GCData ();
3057    data.device = display;
3058    int clrSelectionBk = -1;
3059    boolean drawSelected = false, drawBackground = false, drawHot = false;
3060    if (nmcd.iSubItem == 0 || (style & SWT.FULL_SELECTION) != 0) {
3061        drawHot = hotIndex == nmcd.dwItemSpec;
3062    }
3063    if (OS.IsWindowEnabled (handle)) {
3064        if (selected && (nmcd.iSubItem == 0 || (style & SWT.FULL_SELECTION) != 0)) {
3065            if (OS.GetFocus () == handle) {
3066                drawSelected = true;
3067                data.foreground = OS.GetSysColor (OS.COLOR_HIGHLIGHTTEXT);
3068                data.background = clrSelectionBk = OS.GetSysColor (OS.COLOR_HIGHLIGHT);
3069            } else {
3070                drawSelected = (style & SWT.HIDE_SELECTION) == 0;
3071                data.foreground = OS.GetTextColor (hDC);
3072                data.background = clrSelectionBk = OS.GetSysColor (OS.COLOR_3DFACE);
3073            }
3074            if (explorerTheme) {
3075                data.foreground = clrText != -1 ? clrText : getForegroundPixel ();
3076            }
3077        } else {
3078            drawBackground = clrTextBk != -1;
3079            /*
3080            * Bug in Windows. When LVM_SETTEXTBKCOLOR or LVM_SETBKCOLOR
3081            * is used to set the background color of the the text or the
3082            * control, the color is not set in the HDC that is provided
3083            * in Custom Draw. The fix is to explicitly set the background
3084            * color.
3085            */

3086            if (clrTextBk == -1) {
3087                Control control = findBackgroundControl ();
3088                if (control == null) control = this;
3089                clrTextBk = control.getBackgroundPixel ();
3090            }
3091            data.foreground = clrText != -1 ? clrText : OS.GetTextColor (hDC);
3092            data.background = clrTextBk != -1 ? clrTextBk : OS.GetBkColor (hDC);
3093        }
3094    } else {
3095        data.foreground = OS.GetSysColor (OS.COLOR_GRAYTEXT);
3096        data.background = OS.GetSysColor (OS.COLOR_3DFACE);
3097        if (selected) clrSelectionBk = data.background;
3098    }
3099    data.hFont = hFont;
3100    data.uiState = OS.SendMessage (handle, OS.WM_QUERYUISTATE, 0, 0);
3101    int nSavedDC = OS.SaveDC (hDC);
3102    GC gc = GC.win32_new (hDC, data);
3103    RECT cellRect = item.getBounds (nmcd.dwItemSpec, nmcd.iSubItem, true, true, true, true, hDC);
3104    Event event = new Event ();
3105    event.item = item;
3106    event.gc = gc;
3107    event.index = nmcd.iSubItem;
3108    event.detail |= SWT.FOREGROUND;
3109// if ((nmcd.uItemState & OS.CDIS_FOCUS) != 0) {
3110
if (OS.SendMessage (handle, OS.LVM_GETNEXTITEM, -1, OS.LVNI_FOCUSED) == nmcd.dwItemSpec) {
3111        if (nmcd.iSubItem == 0 || (style & SWT.FULL_SELECTION) != 0) {
3112            if (handle == OS.GetFocus ()) {
3113                int uiState = OS.SendMessage (handle, OS.WM_QUERYUISTATE, 0, 0);
3114                if ((uiState & OS.UISF_HIDEFOCUS) == 0) event.detail |= SWT.FOCUSED;
3115            }
3116        }
3117    }
3118    if (drawHot) event.detail |= SWT.HOT;
3119    if (drawSelected) event.detail |= SWT.SELECTED;
3120    if (drawBackground) event.detail |= SWT.BACKGROUND;
3121    event.x = cellRect.left;
3122    event.y = cellRect.top;
3123    event.width = cellRect.right - cellRect.left;
3124    event.height = cellRect.bottom - cellRect.top;
3125    gc.setClipping (event.x, event.y, event.width, event.height);
3126    sendEvent (SWT.EraseItem, event);
3127    event.gc = null;
3128    int clrSelectionText = data.foreground;
3129    gc.dispose ();
3130    OS.RestoreDC (hDC, nSavedDC);
3131    if (isDisposed () || item.isDisposed ()) return;
3132    if (event.doit) {
3133        ignoreDrawForeground = (event.detail & SWT.FOREGROUND) == 0;
3134        ignoreDrawBackground = (event.detail & SWT.BACKGROUND) == 0;
3135        ignoreDrawSelection = (event.detail & SWT.SELECTED) == 0;
3136        ignoreDrawFocus = (event.detail & SWT.FOCUSED) == 0;
3137        ignoreDrawHot = (event.detail & SWT.HOT) == 0;
3138    } else {
3139        ignoreDrawForeground = ignoreDrawBackground = ignoreDrawSelection = ignoreDrawFocus = ignoreDrawHot = true;
3140    }
3141    if (drawSelected) {
3142        if (ignoreDrawSelection) {
3143            ignoreDrawHot = true;
3144            if (nmcd.iSubItem == 0 || (style & SWT.FULL_SELECTION) != 0) {
3145                selectionForeground = clrSelectionText;
3146            }
3147            nmcd.uItemState &= ~OS.CDIS_SELECTED;
3148            OS.MoveMemory (lParam, nmcd, NMLVCUSTOMDRAW.sizeof);
3149        }
3150    } else {
3151        if (ignoreDrawSelection) {
3152            nmcd.uItemState |= OS.CDIS_SELECTED;
3153            OS.MoveMemory (lParam, nmcd, NMLVCUSTOMDRAW.sizeof);
3154        }
3155    }
3156    if (ignoreDrawFocus) {
3157        nmcd.uItemState &= ~OS.CDIS_FOCUS;
3158        OS.MoveMemory (lParam, nmcd, NMLVCUSTOMDRAW.sizeof);
3159    }
3160    int hwndHeader = OS.SendMessage (handle, OS.LVM_GETHEADER, 0, 0);
3161    boolean firstColumn = nmcd.iSubItem == OS.SendMessage (hwndHeader, OS.HDM_ORDERTOINDEX, 0, 0);
3162    if (ignoreDrawForeground && ignoreDrawHot) {
3163        if (!ignoreDrawBackground && drawBackground) {
3164            RECT backgroundRect = item.getBounds (nmcd.dwItemSpec, nmcd.iSubItem, true, false, true, false, hDC);
3165            fillBackground (hDC, clrTextBk, backgroundRect);
3166        }
3167    }
3168    if (!ignoreDrawHot || (!ignoreDrawSelection && clrSelectionBk != -1)) {
3169        if (explorerTheme) {
3170            boolean hot = drawHot;
3171            RECT pClipRect = new RECT ();
3172            OS.SetRect (pClipRect, nmcd.left, nmcd.top, nmcd.right, nmcd.bottom);
3173            RECT rect = new RECT ();
3174            OS.SetRect (rect, nmcd.left, nmcd.top, nmcd.right, nmcd.bottom);
3175            if ((style & SWT.FULL_SELECTION) != 0) {
3176                int count = OS.SendMessage (hwndHeader, OS.HDM_GETITEMCOUNT, 0, 0);
3177                int index = OS.SendMessage (hwndHeader, OS.HDM_ORDERTOINDEX, count - 1, 0);
3178                RECT headerRect = new RECT ();
3179                OS.SendMessage (hwndHeader, OS.HDM_GETITEMRECT, index, headerRect);
3180                OS.MapWindowPoints (hwndHeader, handle, headerRect, 2);
3181                rect.left = 0;
3182                rect.right = headerRect.right;
3183                pClipRect.left = cellRect.left;
3184                pClipRect.right += EXPLORER_EXTRA;
3185            } else {
3186                rect.right += EXPLORER_EXTRA;
3187                pClipRect.right += EXPLORER_EXTRA;
3188            }
3189            int hTheme = OS.OpenThemeData (handle, Display.LISTVIEW);
3190            int iStateId = selected ? OS.LISS_SELECTED : OS.LISS_HOT;
3191            if (OS.GetFocus () != handle && selected && !hot) iStateId = OS.LISS_SELECTEDNOTFOCUS;
3192            OS.DrawThemeBackground (hTheme, hDC, OS.LVP_LISTITEM, iStateId, rect, pClipRect);
3193            OS.CloseThemeData (hTheme);
3194        } else {
3195            boolean fullText = ((style & SWT.FULL_SELECTION) != 0 || !firstColumn);
3196            RECT textRect = item.getBounds (nmcd.dwItemSpec, nmcd.iSubItem, true, false, fullText, false, hDC);
3197            fillBackground (hDC, clrSelectionBk, textRect);
3198        }
3199    }
3200    if (ignoreDrawForeground) {
3201        RECT clipRect = item.getBounds (nmcd.dwItemSpec, nmcd.iSubItem, true, true, true, false, hDC);
3202        OS.SaveDC (hDC);
3203        OS.SelectClipRgn (hDC, 0);
3204        OS.ExcludeClipRect (hDC, clipRect.left, clipRect.top, clipRect.right, clipRect.bottom);
3205    }
3206}
3207
3208Event sendMeasureItemEvent (TableItem item, int row, int column, int hDC) {
3209    int hFont = item.cellFont != null ? item.cellFont [column] : -1;
3210    if (hFont == -1) hFont = item.font;
3211    GCData data = new GCData ();
3212    data.device = display;
3213    data.hFont = hFont;
3214    int nSavedDC = OS.SaveDC (hDC);
3215    GC gc = GC.win32_new (hDC, data);
3216    RECT itemRect = item.getBounds (row, column, true, true, false, false, hDC);
3217    Event event = new Event ();
3218    event.item = item;
3219    event.gc = gc;
3220    event.index = column;
3221    event.x = itemRect.left;
3222    event.y = itemRect.top;
3223    event.width = itemRect.right - itemRect.left;
3224    event.height = itemRect.bottom - itemRect.top;
3225    sendEvent (SWT.MeasureItem, event);
3226    event.gc = null;
3227    gc.dispose ();
3228    OS.RestoreDC (hDC, nSavedDC);
3229    if (!isDisposed () && !item.isDisposed ()) {
3230        if (columnCount == 0) {
3231            int width = OS.SendMessage (handle, OS.LVM_GETCOLUMNWIDTH, 0, 0);
3232            if (event.x + event.width > width) {
3233                OS.SendMessage (handle, OS.LVM_SETCOLUMNWIDTH, 0, event.x + event.width);
3234            }
3235        }
3236        if (event.height > getItemHeight ()) setItemHeight (event.height);
3237    }
3238    return event;
3239}
3240
3241LRESULT sendMouseDownEvent (int type, int button, int msg, int wParam, int lParam) {
3242    /*
3243    * Feature in Windows. Inside WM_LBUTTONDOWN and WM_RBUTTONDOWN,
3244    * the widget starts a modal loop to determine if the user wants
3245    * to begin a drag/drop operation or marque select. Unfortunately,
3246    * this modal loop eats the corresponding mouse up. The fix is to
3247    * detect the cases when the modal loop has eaten the mouse up and
3248    * issue a fake mouse up.
3249    *
3250    * By observation, when the mouse is clicked anywhere but the check
3251    * box, the widget eats the mouse up. When the mouse is dragged,
3252    * the widget does not eat the mouse up.
3253    */

3254    LVHITTESTINFO pinfo = new LVHITTESTINFO ();
3255    pinfo.x = (short) (lParam & 0xFFFF);
3256    pinfo.y = (short) (lParam >> 16);
3257    OS.SendMessage (handle, OS.LVM_HITTEST, 0, pinfo);
3258    Display display = this.display;
3259    display.captureChanged = false;
3260    if (!sendMouseEvent (type, button, handle, msg, wParam, lParam)) {
3261        if (!display.captureChanged && !isDisposed ()) {
3262            if (OS.GetCapture () != handle) OS.SetCapture (handle);
3263        }
3264        return LRESULT.ZERO;
3265    }
3266
3267    /*
3268    * Force the table to have focus so that when the user
3269    * reselects the focus item, the LVIS_FOCUSED state bits
3270    * for the item will be set. If the user did not click on
3271    * an item, then set focus to the table so that it will
3272    * come to the front and take focus in the work around
3273    * below.
3274    */

3275    OS.SetFocus (handle);
3276        
3277    /*
3278    * Feature in Windows. When the user selects outside of
3279    * a table item, Windows deselects all the items, even
3280    * when the table is multi-select. While not strictly
3281    * wrong, this is unexpected. The fix is to detect the
3282    * case and avoid calling the window proc.
3283    */

3284    if ((style & SWT.SINGLE) != 0 || hooks (SWT.MouseDown) || hooks (SWT.MouseUp)) {
3285        if (pinfo.iItem == -1) {
3286            if (!display.captureChanged && !isDisposed ()) {
3287                if (OS.GetCapture () != handle) OS.SetCapture (handle);
3288            }
3289            return LRESULT.ZERO;
3290        }
3291    }
3292    
3293    /*
3294    * Feature in Windows. When a table item is reselected
3295    * in a single-select table, Windows does not issue a
3296    * WM_NOTIFY because the item state has not changed.
3297    * This is strictly correct but is inconsistent with the
3298    * list widget and other widgets in Windows. The fix is
3299    * to detect the case when an item is reselected and mark
3300    * it as selected.
3301    */

3302    boolean forceSelect = false;
3303    int count = OS.SendMessage (handle, OS.LVM_GETSELECTEDCOUNT, 0, 0);
3304    if (count == 1 && pinfo.iItem != -1) {
3305        LVITEM lvItem = new LVITEM ();
3306        lvItem.mask = OS.LVIF_STATE;
3307        lvItem.stateMask = OS.LVIS_SELECTED;
3308        lvItem.iItem = pinfo.iItem;
3309        OS.SendMessage (handle, OS.LVM_GETITEM, 0, lvItem);
3310        if ((lvItem.state & OS.LVIS_SELECTED) != 0) {
3311            forceSelect = true;
3312        }
3313    }
3314    /*
3315    * Feature in Windows. Inside WM_LBUTTONDOWN and WM_RBUTTONDOWN,
3316    * the widget starts a modal loop to determine if the user wants
3317    * to begin a drag/drop operation or marque select. This modal
3318    * loop eats mouse events until a drag is detected. The fix is
3319    * to avoid this behavior by only running the drag and drop when
3320    * the event is hooked and the mouse is over an item.
3321    */

3322    boolean dragDetect = (state & DRAG_DETECT) != 0 && hooks (SWT.DragDetect);
3323    if (!dragDetect) {
3324        int flags = OS.LVHT_ONITEMICON | OS.LVHT_ONITEMLABEL;
3325        dragDetect = pinfo.iItem == -1 || (pinfo.flags & flags) == 0;
3326    }
3327    if (!dragDetect) display.runDragDrop = false;
3328    int code = callWindowProc (handle, msg, wParam, lParam, forceSelect);
3329    if (!dragDetect) display.runDragDrop = true;
3330    if (dragStarted || !dragDetect) {
3331        if (!display.captureChanged && !isDisposed ()) {
3332            if (OS.GetCapture () != handle) OS.SetCapture (handle);
3333        }
3334    } else {
3335        int flags = OS.LVHT_ONITEMLABEL | OS.LVHT_ONITEMICON;
3336        boolean fakeMouseUp = (pinfo.flags & flags) != 0;
3337        if (!fakeMouseUp && (style & SWT.MULTI) != 0) {
3338            fakeMouseUp = (pinfo.flags & OS.LVHT_ONITEMSTATEICON) == 0;
3339        }
3340        if (fakeMouseUp) {
3341            sendMouseEvent (SWT.MouseUp, button, handle, msg, wParam, lParam);
3342        }
3343    }
3344    dragStarted = false;
3345    return new LRESULT (code);
3346}
3347
3348void sendPaintItemEvent (TableItem item, NMLVCUSTOMDRAW nmcd) {
3349    int hDC = nmcd.hdc;
3350    GCData data = new GCData ();
3351    data.device = display;
3352    int hFont = item.cellFont != null ? item.cellFont [nmcd.iSubItem] : -1;
3353    if (hFont == -1) hFont = item.font;
3354    data.hFont = hFont;
3355    /*
3356    * Bug in Windows. For some reason, CDIS_SELECTED always set,
3357    * even for items that are not selected. The fix is to get
3358    * the selection state from the item.
3359    */

3360    LVITEM lvItem = new LVITEM ();
3361    lvItem.mask = OS.LVIF_STATE;
3362    lvItem.stateMask = OS.LVIS_SELECTED;
3363    lvItem.iItem = nmcd.dwItemSpec;
3364    int result = OS.SendMessage (handle, OS.LVM_GETITEM, 0, lvItem);
3365    boolean selected = result != 0 && (lvItem.state & OS.LVIS_SELECTED) != 0;
3366    boolean drawSelected = false, drawBackground = false, drawHot = false;
3367    if (nmcd.iSubItem == 0 || (style & SWT.FULL_SELECTION) != 0) {
3368        drawHot = hotIndex == nmcd.dwItemSpec;
3369    }
3370    if (OS.IsWindowEnabled (handle)) {
3371        if (selected && (nmcd.iSubItem == 0 || (style & SWT.FULL_SELECTION) != 0)) {
3372            if (OS.GetFocus () == handle) {
3373                drawSelected = true;
3374                if (selectionForeground != -1) {
3375                    data.foreground = selectionForeground;
3376                } else {
3377                    data.foreground = OS.GetSysColor (OS.COLOR_HIGHLIGHTTEXT);
3378                }
3379                data.background = OS.GetSysColor (OS.COLOR_HIGHLIGHT);
3380            } else {
3381                drawSelected = (style & SWT.HIDE_SELECTION) == 0;
3382                data.foreground = OS.GetTextColor (hDC);
3383                data.background = OS.GetSysColor (OS.COLOR_3DFACE);
3384            }
3385            if (explorerTheme && selectionForeground == -1) {
3386                int clrText = item.cellForeground != null ? item.cellForeground [nmcd.iSubItem] : -1;
3387                if (clrText == -1) clrText = item.foreground;
3388                data.foreground = clrText != -1 ? clrText : getForegroundPixel ();
3389            }
3390        } else {
3391            int clrText = item.cellForeground != null ? item.cellForeground [nmcd.iSubItem] : -1;
3392            if (clrText == -1) clrText = item.foreground;
3393            int clrTextBk = item.cellBackground != null ? item.cellBackground [nmcd.iSubItem] : -1;
3394            if (clrTextBk == -1) clrTextBk = item.background;
3395            drawBackground = clrTextBk != -1;
3396            /*
3397            * Bug in Windows. When LVM_SETTEXTBKCOLOR or LVM_SETBKCOLOR
3398            * is used to set the background color of the the text or the
3399            * control, the color is not set in the HDC that is provided
3400            * in Custom Draw. The fix is to explicitly set the background
3401            * color.
3402            */

3403            if (clrTextBk == -1) {
3404                Control control = findBackgroundControl ();
3405                if (control == null) control = this;
3406                clrTextBk = control.getBackgroundPixel ();
3407            }
3408            data.foreground = clrText != -1 ? clrText : OS.GetTextColor (hDC);
3409            data.background = clrTextBk != -1 ? clrTextBk : OS.GetBkColor (hDC);
3410        }
3411    } else {
3412        data.foreground = OS.GetSysColor (OS.COLOR_GRAYTEXT);
3413        data.background = OS.GetSysColor (OS.COLOR_3DFACE);
3414    }
3415    data.uiState = OS.SendMessage (handle, OS.WM_QUERYUISTATE, 0, 0);
3416    int nSavedDC = OS.SaveDC (hDC);
3417    GC gc = GC.win32_new (hDC, data);
3418    RECT itemRect = item.getBounds (nmcd.dwItemSpec, nmcd.iSubItem, true, true, false, false, hDC);
3419    Event event = new Event ();
3420    event.item = item;
3421    event.gc = gc;
3422    event.index = nmcd.iSubItem;
3423    event.detail |= SWT.FOREGROUND;
3424// if ((nmcd.uItemState & OS.CDIS_FOCUS) != 0) {
3425
if (OS.SendMessage (handle, OS.LVM_GETNEXTITEM, -1, OS.LVNI_FOCUSED) == nmcd.dwItemSpec) {
3426        if (nmcd.iSubItem == 0 || (style & SWT.FULL_SELECTION) != 0) {
3427            if (handle == OS.GetFocus ()) {
3428                int uiState = OS.SendMessage (handle, OS.WM_QUERYUISTATE, 0, 0);
3429                if ((uiState & OS.UISF_HIDEFOCUS) == 0) event.detail |= SWT.FOCUSED;
3430            }
3431        }
3432    }
3433    if (drawHot) event.detail |= SWT.HOT;
3434    if (drawSelected) event.detail |= SWT.SELECTED;
3435    if (drawBackground) event.detail |= SWT.BACKGROUND;
3436    event.x = itemRect.left;
3437    event.y = itemRect.top;
3438    event.width = itemRect.right - itemRect.left;
3439    event.height = itemRect.bottom - itemRect.top;
3440    RECT cellRect = item.getBounds (nmcd.dwItemSpec, nmcd.iSubItem, true, true, true, true, hDC);
3441    int cellWidth = cellRect.right - cellRect.left;
3442    int cellHeight = cellRect.bottom - cellRect.top;
3443    gc.setClipping (cellRect.left, cellRect.top, cellWidth, cellHeight);
3444    sendEvent (SWT.PaintItem, event);
3445    event.gc = null;
3446    gc.dispose ();
3447    OS.RestoreDC (hDC, nSavedDC);
3448}
3449
3450void setBackgroundImage (int hBitmap) {
3451    super.setBackgroundImage (hBitmap);
3452    if (!customDraw) setBackgroundTransparent (hBitmap != 0);
3453}
3454
3455void setBackgroundPixel (int newPixel) {
3456    if (!customDraw) {
3457        if (findImageControl () != null) return;
3458        if (newPixel == -1) newPixel = defaultBackground ();
3459        int oldPixel = OS.SendMessage (handle, OS.LVM_GETBKCOLOR, 0, 0);
3460        if (oldPixel != newPixel) {
3461            OS.SendMessage (handle, OS.LVM_SETBKCOLOR, 0, newPixel);
3462            OS.SendMessage (handle, OS.LVM_SETTEXTBKCOLOR, 0, newPixel);
3463            if ((style & SWT.CHECK) != 0) fixCheckboxImageListColor (true);
3464        }
3465    }
3466    /*
3467    * Feature in Windows. When the background color is changed,
3468    * the table does not redraw until the next WM_PAINT. The fix
3469    * is to force a redraw.
3470    */

3471    OS.InvalidateRect (handle, null, true);
3472}
3473
3474void setBackgroundTransparent (boolean transparent) {
3475    /*
3476    * Bug in Windows. When the table has the extended style
3477    * LVS_EX_FULLROWSELECT and LVM_SETBKCOLOR is used with
3478    * CLR_NONE to make the table transparent, Windows draws
3479    * a black rectangle around the first column. The fix is
3480    * clear LVS_EX_FULLROWSELECT.
3481    *
3482    * Feature in Windows. When LVM_SETBKCOLOR is used with
3483    * CLR_NONE and LVM_SETSELECTEDCOLUMN is used to select
3484    * a column, Windows fills the column with the selection
3485    * color, drawing on top of the background image and any
3486    * other custom drawing. The fix is to clear the selected
3487    * column.
3488    */

3489    int oldPixel = OS.SendMessage (handle, OS.LVM_GETBKCOLOR, 0, 0);
3490    if (transparent) {
3491        if (oldPixel != OS.CLR_NONE) {
3492            /*
3493            * Bug in Windows. When the background color is changed,
3494            * the table does not redraw until the next WM_PAINT. The
3495            * fix is to force a redraw.
3496            */

3497            OS.SendMessage (handle, OS.LVM_SETBKCOLOR, 0, OS.CLR_NONE);
3498            OS.SendMessage (handle, OS.LVM_SETTEXTBKCOLOR, 0, OS.CLR_NONE);
3499            OS.InvalidateRect (handle, null, true);
3500            
3501            /* Clear LVS_EX_FULLROWSELECT */
3502            if (!explorerTheme && (style & SWT.FULL_SELECTION) != 0) {
3503                int bits = OS.LVS_EX_FULLROWSELECT;
3504                OS.SendMessage (handle, OS.LVM_SETEXTENDEDLISTVIEWSTYLE, bits, 0);
3505            }
3506            
3507            /* Clear LVM_SETSELECTEDCOLUMN */
3508            if ((sortDirection & (SWT.UP | SWT.DOWN)) != 0) {
3509                if (sortColumn != null && !sortColumn.isDisposed ()) {
3510                    OS.SendMessage (handle, OS.LVM_SETSELECTEDCOLUMN, -1, 0);
3511                    /*
3512                    * Bug in Windows. When LVM_SETSELECTEDCOLUMN is set, Windows
3513                    * does not redraw either the new or the previous selected column.
3514                    * The fix is to force a redraw.
3515                    */

3516                    OS.InvalidateRect (handle, null, true);
3517                }
3518            }
3519        }
3520    } else {
3521        if (oldPixel == OS.CLR_NONE) {
3522            Control control = findBackgroundControl ();
3523            if (control == null) control = this;
3524            if (control.backgroundImage == null) {
3525                setBackgroundPixel (control.getBackgroundPixel ());
3526            }
3527            
3528            /* Set LVS_EX_FULLROWSELECT */
3529            if (!explorerTheme && (style & SWT.FULL_SELECTION) != 0) {
3530                if (!hooks (SWT.EraseItem) && !hooks (SWT.PaintItem)) {
3531                    int bits = OS.LVS_EX_FULLROWSELECT;
3532                    OS.SendMessage (handle, OS.LVM_SETEXTENDEDLISTVIEWSTYLE, bits, bits);
3533                }
3534            }
3535            
3536            /* Set LVM_SETSELECTEDCOLUMN */
3537            if ((sortDirection & (SWT.UP | SWT.DOWN)) != 0) {
3538                if (sortColumn != null && !sortColumn.isDisposed ()) {
3539                    int column = indexOf (sortColumn);
3540                    if (column != -1) {
3541                        OS.SendMessage (handle, OS.LVM_SETSELECTEDCOLUMN, column, 0);
3542                        /*
3543                        * Bug in Windows. When LVM_SETSELECTEDCOLUMN is set, Windows
3544                        * does not redraw either the new or the previous selected column.
3545                        * The fix is to force a redraw.
3546                        */

3547                        OS.InvalidateRect (handle, null, true);
3548                    }
3549                }
3550            }
3551        }
3552    }
3553}
3554
3555void setBounds (int x, int y, int width, int height, int flags, boolean defer) {
3556    /*
3557    * Bug in Windows. If the table column widths are adjusted
3558    * in WM_SIZE or WM_POSITIONCHANGED using LVM_SETCOLUMNWIDTH
3559    * blank lines may be inserted at the top of the table. A
3560    * call to LVM_GETTOPINDEX will return a negative number (this
3561    * is an impossible result). Once the blank lines appear,
3562    * there seems to be no way to get rid of them, other than
3563    * destroying and recreating the table. The fix is to send
3564    * the resize notification after the size has been changed in
3565    * the operating system.
3566    *
3567    * NOTE: This does not fix the case when the user is resizing
3568    * columns dynamically. There is no fix for this case at this
3569    * time.
3570    */

3571    setDeferResize (true);
3572    super.setBounds (x, y, width, height, flags, false);
3573    setDeferResize (false);
3574}
3575
3576/**
3577 * Sets the order that the items in the receiver should
3578 * be displayed in to the given argument which is described
3579 * in terms of the zero-relative ordering of when the items
3580 * were added.
3581 *
3582 * @param order the new order to display the items
3583 *
3584 * @exception SWTException <ul>
3585 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
3586 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
3587 * </ul>
3588 * @exception IllegalArgumentException <ul>
3589 * <li>ERROR_NULL_ARGUMENT - if the item order is null</li>
3590 * <li>ERROR_INVALID_ARGUMENT - if the item order is not the same length as the number of items</li>
3591 * </ul>
3592 *
3593 * @see Table#getColumnOrder()
3594 * @see TableColumn#getMoveable()
3595 * @see TableColumn#setMoveable(boolean)
3596 * @see SWT#Move
3597 *
3598 * @since 3.1
3599 */

3600public void setColumnOrder (int [] order) {
3601    checkWidget ();
3602    if (order == null) error (SWT.ERROR_NULL_ARGUMENT);
3603    int hwndHeader = OS.SendMessage (handle, OS.LVM_GETHEADER, 0, 0);
3604    if (columnCount == 0) {
3605        if (order.length != 0) error (SWT.ERROR_INVALID_ARGUMENT);
3606        return;
3607    }
3608    if (order.length != columnCount) error (SWT.ERROR_INVALID_ARGUMENT);
3609    int [] oldOrder = new int [columnCount];
3610    OS.SendMessage (handle, OS.LVM_GETCOLUMNORDERARRAY, columnCount, oldOrder);
3611    boolean reorder = false;
3612    boolean [] seen = new boolean [columnCount];
3613    for (int i=0; i<order.length; i++) {
3614        int index = order [i];
3615        if (index < 0 || index >= columnCount) error (SWT.ERROR_INVALID_RANGE);
3616        if (seen [index]) error (SWT.ERROR_INVALID_ARGUMENT);
3617        seen [index] = true;
3618        if (index != oldOrder [i]) reorder = true;
3619    }
3620    if (reorder) {
3621        RECT [] oldRects = new RECT [columnCount];
3622        for (int i=0; i<columnCount; i++) {
3623            oldRects [i] = new RECT ();
3624            OS.SendMessage (hwndHeader, OS.HDM_GETITEMRECT, i, oldRects [i]);
3625        }
3626        OS.SendMessage (handle, OS.LVM_SETCOLUMNORDERARRAY, order.length, order);
3627        /*
3628        * Bug in Windows. When LVM_SETCOLUMNORDERARRAY is used to change
3629        * the column order, the header redraws correctly but the table does
3630        * not. The fix is to force a redraw.
3631        */

3632        OS.InvalidateRect (handle, null, true);
3633        TableColumn[] newColumns = new TableColumn [columnCount];
3634        System.arraycopy (columns, 0, newColumns, 0, columnCount);
3635        RECT newRect = new RECT ();
3636        for (int i=0; i<columnCount; i++) {
3637            TableColumn column = newColumns [i];
3638            if (!column.isDisposed ()) {
3639                OS.SendMessage (hwndHeader, OS.HDM_GETITEMRECT, i, newRect);
3640                if (newRect.left != oldRects [i].left) {
3641                    column.updateToolTip (i);
3642                    column.sendEvent (SWT.Move);
3643                }
3644            }
3645        }
3646    }
3647}
3648
3649void setCustomDraw (boolean customDraw) {
3650    if (this.customDraw == customDraw) return;
3651    if (!this.customDraw && customDraw && currentItem != null) {
3652        OS.InvalidateRect (handle, null, true);
3653    }
3654    this.customDraw = customDraw;
3655}
3656
3657void setDeferResize (boolean defer) {
3658    if (defer) {
3659        if (resizeCount++ == 0) {
3660            wasResized = false;
3661            if (hooks (SWT.MeasureItem) || hooks (SWT.EraseItem) || hooks (SWT.PaintItem)) {
3662                if (drawCount++ == 0 && OS.IsWindowVisible (handle)) {
3663                    OS.DefWindowProc (handle, OS.WM_SETREDRAW, 0, 0);
3664                }
3665            }
3666        }
3667    } else {
3668        if (--resizeCount == 0) {
3669            if (hooks (SWT.MeasureItem) || hooks (SWT.EraseItem) || hooks (SWT.PaintItem)) {
3670                if (--drawCount == 0 /*&& OS.IsWindowVisible (handle)*/) {
3671                    OS.DefWindowProc (handle, OS.WM_SETREDRAW, 1, 0);
3672                    if (OS.IsWinCE) {
3673                        int hwndHeader = OS.SendMessage (handle, OS.LVM_GETHEADER, 0, 0);
3674                        if (hwndHeader != 0) OS.InvalidateRect (hwndHeader, null, true);
3675                        OS.InvalidateRect (handle, null, true);
3676                    } else {
3677                        int flags = OS.RDW_ERASE | OS.RDW_FRAME | OS.RDW_INVALIDATE | OS.RDW_ALLCHILDREN;
3678                        OS.RedrawWindow (handle, null, 0, flags);
3679                    }
3680                }
3681            }
3682            if (wasResized) {
3683                wasResized = false;
3684                setResizeChildren (false);
3685                sendEvent (SWT.Resize);
3686                if (isDisposed ()) return;
3687                if (layout != null) {
3688                    markLayout (false, false);
3689                    updateLayout (false, false);
3690                }
3691                setResizeChildren (true);
3692            }
3693        }
3694    }
3695}
3696
3697void setCheckboxImageList (int width, int height, boolean fixScroll) {
3698    if ((style & SWT.CHECK) == 0) return;
3699    int count = 4, flags = 0;
3700    if (OS.IsWinCE) {
3701        flags |= OS.ILC_COLOR;
3702    } else {
3703        int hDC = OS.GetDC (handle);
3704        int bits = OS.GetDeviceCaps (hDC, OS.BITSPIXEL);
3705        int planes = OS.GetDeviceCaps (hDC, OS.PLANES);
3706        OS.ReleaseDC (handle, hDC);
3707        int depth = bits * planes;
3708        switch (depth) {
3709            case 4: flags |= OS.ILC_COLOR4; break;
3710            case 8: flags |= OS.ILC_COLOR8; break;
3711            case 16: flags |= OS.ILC_COLOR16; break;
3712            case 24: flags |= OS.ILC_COLOR24; break;
3713            case 32: flags |= OS.ILC_COLOR32; break;
3714            default: flags |= OS.ILC_COLOR; break;
3715        }
3716    }
3717    if ((style & SWT.RIGHT_TO_LEFT) != 0) flags |= OS.ILC_MIRROR;
3718    if (OS.COMCTL32_MAJOR < 6 || !OS.IsAppThemed ()) flags |= OS.ILC_MASK;
3719    int hStateList = OS.ImageList_Create (width, height, flags, count, count);
3720    int hDC = OS.GetDC (handle);
3721    int memDC = OS.CreateCompatibleDC (hDC);
3722    int hBitmap = OS.CreateCompatibleBitmap (hDC, width * count, height);
3723    int hOldBitmap = OS.SelectObject (memDC, hBitmap);
3724    RECT rect = new RECT ();
3725    OS.SetRect (rect, 0, 0, width * count, height);
3726    int clrBackground;
3727    if (OS.COMCTL32_MAJOR >= 6 && OS.IsAppThemed ()) {
3728        Control control = findBackgroundControl ();
3729        if (control == null) control = this;
3730        clrBackground = control.getBackgroundPixel ();
3731    } else {
3732        clrBackground = 0x020000FF;
3733        if ((clrBackground & 0xFFFFFF) == OS.GetSysColor (OS.COLOR_WINDOW)) {
3734            clrBackground = 0x0200FF00;
3735        }
3736    }
3737    int hBrush = OS.CreateSolidBrush (clrBackground);
3738    OS.FillRect (memDC, rect, hBrush);
3739    OS.DeleteObject (hBrush);
3740    int oldFont = OS.SelectObject (hDC, defaultFont ());
3741    TEXTMETRIC tm = OS.IsUnicode ? (TEXTMETRIC) new TEXTMETRICW () : new TEXTMETRICA ();
3742    OS.GetTextMetrics (hDC, tm);
3743    OS.SelectObject (hDC, oldFont);
3744    int itemWidth = Math.min (tm.tmHeight, width);
3745    int itemHeight = Math.min (tm.tmHeight, height);
3746    int left = (width - itemWidth) / 2, top = (height - itemHeight) / 2 + 1;
3747    OS.SetRect (rect, left, top, left + itemWidth, top + itemHeight);
3748    if (OS.COMCTL32_MAJOR >= 6 && OS.IsAppThemed ()) {
3749        int hTheme = display.hButtonTheme ();
3750        OS.DrawThemeBackground (hTheme, memDC, OS.BP_CHECKBOX, OS.CBS_UNCHECKEDNORMAL, rect, null);
3751        rect.left += width; rect.right += width;
3752        OS.DrawThemeBackground (hTheme, memDC, OS.BP_CHECKBOX, OS.CBS_CHECKEDNORMAL, rect, null);
3753        rect.left += width; rect.right += width;
3754        OS.DrawThemeBackground (hTheme, memDC, OS.BP_CHECKBOX, OS.CBS_UNCHECKEDNORMAL, rect, null);
3755        rect.left += width; rect.right += width;
3756        OS.DrawThemeBackground (hTheme, memDC, OS.BP_CHECKBOX, OS.CBS_MIXEDNORMAL, rect, null);
3757    } else {
3758        OS.DrawFrameControl (memDC, rect, OS.DFC_BUTTON, OS.DFCS_BUTTONCHECK | OS.DFCS_FLAT);
3759        rect.left += width; rect.right += width;
3760        OS.DrawFrameControl (memDC, rect, OS.DFC_BUTTON, OS.DFCS_BUTTONCHECK | OS.DFCS_CHECKED | OS.DFCS_FLAT);
3761        rect.left += width; rect.right += width;
3762        OS.DrawFrameControl (memDC, rect, OS.DFC_BUTTON, OS.DFCS_BUTTONCHECK | OS.DFCS_INACTIVE | OS.DFCS_FLAT);
3763        rect.left += width; rect.right += width;
3764        OS.DrawFrameControl (memDC, rect, OS.DFC_BUTTON, OS.DFCS_BUTTONCHECK | OS.DFCS_CHECKED | OS.DFCS_INACTIVE | OS.DFCS_FLAT);
3765    }
3766    OS.SelectObject (memDC, hOldBitmap);
3767    OS.DeleteDC (memDC);
3768    OS.ReleaseDC (handle, hDC);
3769    if (OS.COMCTL32_MAJOR >= 6 && OS.IsAppThemed ()) {
3770        OS.ImageList_Add (hStateList, hBitmap, 0);
3771    } else {
3772        OS.ImageList_AddMasked (hStateList, hBitmap, clrBackground);
3773    }
3774    OS.DeleteObject (hBitmap);
3775    /*
3776    * Bug in Windows. Making any change to an item that
3777    * changes the item height of a table while the table
3778    * is scrolled can cause the lines to draw incorrectly.
3779    * This happens even when the lines are not currently
3780    * visible and are shown afterwards. The fix is to
3781    * save the top index, scroll to the top of the table
3782    * and then restore the original top index.
3783    */

3784    int topIndex = getTopIndex ();
3785    if (fixScroll && topIndex != 0) {
3786        setRedraw (false);
3787        setTopIndex (0);
3788    }
3789    int hOldStateList = OS.SendMessage (handle, OS.LVM_GETIMAGELIST, OS.LVSIL_STATE, 0);
3790    OS.SendMessage (handle, OS.LVM_SETIMAGELIST, OS.LVSIL_STATE, hStateList);
3791    if (hOldStateList != 0) OS.ImageList_Destroy (hOldStateList);
3792    /*
3793    * Bug in Windows. Setting the LVSIL_STATE state image list
3794    * when the table already has a LVSIL_SMALL image list causes
3795    * pixel corruption of the images. The fix is to reset the
3796    * LVSIL_SMALL image list.
3797    */

3798    if (!OS.IsWinCE && OS.WIN32_VERSION >= OS.VERSION (6, 0)) {
3799        int hImageList = OS.SendMessage (handle, OS.LVM_GETIMAGELIST, OS.LVSIL_SMALL, 0);
3800        OS.SendMessage (handle, OS.LVM_SETIMAGELIST, OS.LVSIL_SMALL, hImageList);
3801    }
3802    if (fixScroll && topIndex != 0) {
3803        setTopIndex (topIndex);
3804        setRedraw (true);
3805    }
3806}
3807
3808void setFocusIndex (int index) {
3809// checkWidget ();
3810
/*
3811    * An index of -1 will apply the change to all
3812    * items. Ensure that index is greater than -1.
3813    */

3814    if (index < 0) return;
3815    LVITEM lvItem = new LVITEM ();
3816    lvItem.state = OS.LVIS_FOCUSED;
3817    lvItem.stateMask = OS.LVIS_FOCUSED;
3818    ignoreSelect = true;
3819    OS.SendMessage (handle, OS.LVM_SETITEMSTATE, index, lvItem);
3820    ignoreSelect = false;
3821    OS.SendMessage (handle, OS.LVM_SETSELECTIONMARK, 0, index);
3822}
3823
3824public void setFont (Font font) {
3825    checkWidget ();
3826    /*
3827    * Bug in Windows. Making any change to an item that
3828    * changes the item height of a table while the table
3829    * is scrolled can cause the lines to draw incorrectly.
3830    * This happens even when the lines are not currently
3831    * visible and are shown afterwards. The fix is to
3832    * save the top index, scroll to the top of the table
3833    * and then restore the original top index.
3834    */

3835    int topIndex = getTopIndex ();
3836    if (topIndex != 0) {
3837        setRedraw (false);
3838        setTopIndex (0);
3839    }
3840    if (itemHeight != -1) {
3841        int bits = OS.GetWindowLong (handle, OS.GWL_STYLE);
3842        OS.SetWindowLong (handle, OS.GWL_STYLE, bits | OS.LVS_OWNERDRAWFIXED);
3843    }
3844    super.setFont (font);
3845    if (itemHeight != -1) {
3846        int bits = OS.GetWindowLong (handle, OS.GWL_STYLE);
3847        OS.SetWindowLong (handle, OS.GWL_STYLE, bits & ~OS.LVS_OWNERDRAWFIXED);
3848    }
3849    setScrollWidth (null, true);
3850    if (topIndex != 0) {
3851        setTopIndex (topIndex);
3852        setRedraw (true);
3853    }
3854    
3855    /*
3856    * Bug in Windows. Setting the font will cause the table
3857    * to be redrawn but not the column headers. The fix is
3858    * to force a redraw of the column headers.
3859    */

3860    int hwndHeader = OS.SendMessage (handle, OS.LVM_GETHEADER, 0, 0);
3861    OS.InvalidateRect (hwndHeader, null, true);
3862}
3863
3864void setForegroundPixel (int pixel) {
3865    /*
3866    * The Windows table control uses CLR_DEFAULT to indicate
3867    * that it is using the default foreground color. This
3868    * is undocumented.
3869    */

3870    if (pixel == -1) pixel = OS.CLR_DEFAULT;
3871    OS.SendMessage (handle, OS.LVM_SETTEXTCOLOR, 0, pixel);
3872        
3873    /*
3874    * Feature in Windows. When the foreground color is
3875    * changed, the table does not redraw until the next
3876    * WM_PAINT. The fix is to force a redraw.
3877    */

3878    OS.InvalidateRect (handle, null, true);
3879}
3880
3881/**
3882 * Marks the receiver's header as visible if the argument is <code>true</code>,
3883 * and marks it invisible otherwise.
3884 * <p>
3885 * If one of the receiver's ancestors is not visible or some
3886 * other condition makes the receiver not visible, marking
3887 * it visible may not actually cause it to be displayed.
3888 * </p>
3889 *
3890 * @param show the new visibility state
3891 *
3892 * @exception SWTException <ul>
3893 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
3894 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
3895 * </ul>
3896 */

3897public void setHeaderVisible (boolean show) {
3898    checkWidget ();
3899    int newBits = OS.GetWindowLong (handle, OS.GWL_STYLE);
3900    newBits &= ~OS.LVS_NOCOLUMNHEADER;
3901    if (!show) newBits |= OS.LVS_NOCOLUMNHEADER;
3902    /*
3903    * Feature in Windows. Setting or clearing LVS_NOCOLUMNHEADER
3904    * causes the table to scroll to the beginning. The fix is to
3905    * save and restore the top index causing the table to scroll
3906    * to the new location.
3907    */

3908    int oldIndex = getTopIndex ();
3909    OS.SetWindowLong (handle, OS.GWL_STYLE, newBits);
3910    
3911    /*
3912    * Bug in Windows. Making any change to an item that
3913    * changes the item height of a table while the table
3914    * is scrolled can cause the lines to draw incorrectly.
3915    * This happens even when the lines are not currently
3916    * visible and are shown afterwards. The fix is to
3917    * save the top index, scroll to the top of the table
3918    * and then restore the original top index.
3919    */

3920    int newIndex = getTopIndex ();
3921    if (newIndex != 0) {
3922        setRedraw (false);
3923        setTopIndex (0);
3924    }
3925    if (show) {
3926        int bits = OS.SendMessage (handle, OS.LVM_GETEXTENDEDLISTVIEWSTYLE, 0, 0);
3927        if ((bits & OS.LVS_EX_GRIDLINES) != 0) fixItemHeight (false);
3928    }
3929    setTopIndex (oldIndex);
3930    if (newIndex != 0) {
3931        setRedraw (true);
3932    }
3933    updateHeaderToolTips ();
3934}
3935
3936/**
3937 * Sets the number of items contained in the receiver.
3938 *
3939 * @param count the number of items
3940 *
3941 * @exception SWTException <ul>
3942 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
3943 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
3944 * </ul>
3945 *
3946 * @since 3.0
3947 */

3948public void setItemCount (int count) {
3949    checkWidget ();
3950    count = Math.max (0, count);
3951    int itemCount = OS.SendMessage (handle, OS.LVM_GETITEMCOUNT, 0, 0);
3952    if (count == itemCount) return;
3953    setDeferResize (true);
3954    boolean isVirtual = (style & SWT.VIRTUAL) != 0;
3955    if (!isVirtual) setRedraw (false);
3956    int index = count;
3957    while (index < itemCount) {
3958        TableItem item = items [index];
3959        if (item != null && !item.isDisposed ()) item.release (false);
3960        if (!isVirtual) {
3961            ignoreSelect = ignoreShrink = true;
3962            int code = OS.SendMessage (handle, OS.LVM_DELETEITEM, count, 0);
3963            ignoreSelect = ignoreShrink = false;
3964            if (code == 0) break;
3965        }
3966        index++;
3967    }
3968    if (index < itemCount) error (SWT.ERROR_ITEM_NOT_REMOVED);
3969    int length = Math.max (4, (count + 3) / 4 * 4);
3970    TableItem [] newItems = new TableItem [length];
3971    System.arraycopy (items, 0, newItems, 0, Math.min (count, itemCount));
3972    items = newItems;
3973    if (isVirtual) {
3974        int flags = OS.LVSICF_NOINVALIDATEALL | OS.LVSICF_NOSCROLL;
3975        OS.SendMessage (handle, OS.LVM_SETITEMCOUNT, count, flags);
3976        /*
3977        * Bug in Windows. When a virtual table contains items and
3978        * LVM_SETITEMCOUNT is used to set the new item count to zero,
3979        * Windows does not redraw the table. Note that simply not
3980        * specifying LVSICF_NOINVALIDATEALL or LVSICF_NOSCROLL does
3981        * correct the problem. The fix is to force a redraw.
3982        */

3983        if (count == 0 && itemCount != 0) {
3984            OS.InvalidateRect (handle, null, true);
3985        }
3986    } else {
3987        for (int i=itemCount; i<count; i++) {
3988            items [i] = new TableItem (this, SWT.NONE, i, true);
3989        }
3990    }
3991    if (!isVirtual) setRedraw (true);
3992    if (itemCount == 0) setScrollWidth (null, false);
3993    setDeferResize (false);
3994}
3995
3996void setItemHeight (boolean fixScroll) {
3997    /*
3998    * Bug in Windows. Making any change to an item that
3999    * changes the item height of a table while the table
4000    * is scrolled can cause the lines to draw incorrectly.
4001    * This happens even when the lines are not currently
4002    * visible and are shown afterwards. The fix is to
4003    * save the top index, scroll to the top of the table
4004    * and then restore the original top index.
4005    */

4006    int topIndex = getTopIndex ();
4007    if (fixScroll && topIndex != 0) {
4008        setRedraw (false);
4009        setTopIndex (0);
4010    }
4011    if (itemHeight == -1) {
4012        /*
4013        * Feature in Windows. Windows has no API to restore the
4014        * defualt item height for a table. The fix is to use
4015        * WM_SETFONT which recomputes and assigns the default item
4016        * height.
4017        */

4018        int hFont = OS.SendMessage (handle, OS.WM_GETFONT, 0, 0);
4019        OS.SendMessage (handle, OS.WM_SETFONT, hFont, 0);
4020    } else {
4021        /*
4022        * Feature in Windows. Window has no API to set the item
4023        * height for a table. The fix is to set temporarily set
4024        * LVS_OWNERDRAWFIXED then resize the table, causing a
4025        * WM_MEASUREITEM to be sent, then clear LVS_OWNERDRAWFIXED.
4026        */

4027        forceResize ();
4028        RECT rect = new RECT ();
4029        OS.GetWindowRect (handle, rect);
4030        int width = rect.right - rect.left, height = rect.bottom - rect.top;
4031        int bits = OS.GetWindowLong (handle, OS.GWL_STYLE);
4032        OS.SetWindowLong (handle, OS.GWL_STYLE, bits | OS.LVS_OWNERDRAWFIXED);
4033        int flags = OS.SWP_NOACTIVATE | OS.SWP_NOMOVE | OS.SWP_NOREDRAW | OS.SWP_NOZORDER;
4034        ignoreResize = true;
4035        SetWindowPos (handle, 0 , 0, 0, width, height + 1, flags);
4036        SetWindowPos (handle, 0 , 0, 0, width, height, flags);
4037        ignoreResize = false;
4038        OS.SetWindowLong (handle, OS.GWL_STYLE, bits);
4039    }
4040    if (fixScroll && topIndex != 0) {
4041        setTopIndex (topIndex);
4042        setRedraw (true);
4043    }
4044}
4045
4046/**
4047 * Sets the height of the area which would be used to
4048 * display <em>one</em> of the items in the table.
4049 *
4050 * @param itemHeight the height of one item
4051 *
4052 * @exception SWTException <ul>
4053 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
4054 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
4055 * </ul>
4056 *
4057 * @since 3.2
4058 */

4059/*public*/ void setItemHeight (int itemHeight) {
4060    checkWidget ();
4061    if (itemHeight < -1) error (SWT.ERROR_INVALID_ARGUMENT);
4062    this.itemHeight = itemHeight;
4063    setItemHeight (true);
4064    setScrollWidth (null, true);
4065}
4066
4067/**
4068 * Marks the receiver's lines as visible if the argument is <code>true</code>,
4069 * and marks it invisible otherwise.
4070 * <p>
4071 * If one of the receiver's ancestors is not visible or some
4072 * other condition makes the receiver not visible, marking
4073 * it visible may not actually cause it to be displayed.
4074 * </p>
4075 *
4076 * @param show the new visibility state
4077 *
4078 * @exception SWTException <ul>
4079 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
4080 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
4081 * </ul>
4082 */

4083public void setLinesVisible (boolean show) {
4084    checkWidget ();
4085    int newBits = show ? OS.LVS_EX_GRIDLINES : 0;
4086    OS.SendMessage (handle, OS.LVM_SETEXTENDEDLISTVIEWSTYLE, OS.LVS_EX_GRIDLINES, newBits);
4087    if (show) {
4088        int bits = OS.GetWindowLong (handle, OS.GWL_STYLE);
4089        if ((bits & OS.LVS_NOCOLUMNHEADER) == 0) fixItemHeight (true);
4090    }
4091}
4092
4093public void setRedraw (boolean redraw) {
4094    checkWidget ();
4095    /*
4096     * Feature in Windows. When WM_SETREDRAW is used to turn
4097     * off drawing in a widget, it clears the WS_VISIBLE bits
4098     * and then sets them when redraw is turned back on. This
4099     * means that WM_SETREDRAW will make a widget unexpectedly
4100     * visible. The fix is to track the visibility state while
4101     * drawing is turned off and restore it when drawing is turned
4102     * back on.
4103     */

4104    if (drawCount == 0) {
4105        int bits = OS.GetWindowLong (handle, OS.GWL_STYLE);
4106        if ((bits & OS.WS_VISIBLE) == 0) state |= HIDDEN;
4107    }
4108    if (redraw) {
4109        if (--drawCount == 0) {
4110            /*
4111            * When many items are added to a table, it is faster to
4112            * temporarily unsubclass the window proc so that messages
4113            * are dispatched directly to the table.
4114            *
4115            * NOTE: This is optimization somewhat dangerous because any
4116            * operation can occur when redraw is turned off, even operations
4117            * where the table must be subclassed in order to have the correct
4118            * behavior or work around a Windows bug.
4119            *
4120            * This code is intentionally commented.
4121            */

4122// subclass ();
4123

4124            /* Set the width of the horizontal scroll bar */
4125            setScrollWidth (null, true);
4126
4127            /*
4128            * Bug in Windows. For some reason, when WM_SETREDRAW is used
4129            * to turn redraw back on this may result in a WM_SIZE. If the
4130            * table column widths are adjusted in WM_SIZE, blank lines may
4131            * be inserted at the top of the widget. A call to LVM_GETTOPINDEX
4132            * will return a negative number (this is an impossible result).
4133            * The fix is to send the resize notification after the size has
4134            * been changed in the operating system.
4135            */

4136            setDeferResize (true);
4137            OS.SendMessage (handle, OS.WM_SETREDRAW, 1, 0);
4138            int hwndHeader = OS.SendMessage (handle, OS.LVM_GETHEADER, 0, 0);
4139            if (hwndHeader != 0) OS.SendMessage (hwndHeader, OS.WM_SETREDRAW, 1, 0);
4140            if ((state & HIDDEN) != 0) {
4141                state &= ~HIDDEN;
4142                OS.ShowWindow (handle, OS.SW_HIDE);
4143            } else {
4144                if (OS.IsWinCE) {
4145                    OS.InvalidateRect (handle, null, false);
4146                    if (hwndHeader != 0) {
4147                        OS.InvalidateRect (hwndHeader, null, false);
4148                    }
4149                } else {
4150                    int flags = OS.RDW_ERASE | OS.RDW_FRAME | OS.RDW_INVALIDATE | OS.RDW_ALLCHILDREN;
4151                    OS.RedrawWindow (handle, null, 0, flags);
4152                }
4153            }
4154            setDeferResize (false);
4155        }
4156    } else {
4157        if (drawCount++ == 0) {
4158            OS.SendMessage (handle, OS.WM_SETREDRAW, 0, 0);
4159            int hwndHeader = OS.SendMessage (handle, OS.LVM_GETHEADER, 0, 0);
4160            if (hwndHeader != 0) OS.SendMessage (hwndHeader, OS.WM_SETREDRAW, 0, 0);
4161            
4162            /*
4163            * When many items are added to a table, it is faster to
4164            * temporarily unsubclass the window proc so that messages
4165            * are dispatched directly to the table.
4166            *
4167            * NOTE: This is optimization somewhat dangerous because any
4168            * operation can occur when redraw is turned off, even operations
4169            * where the table must be subclassed in order to have the correct
4170            * behavior or work around a Windows bug.
4171            *
4172            * This code is intentionally commented.
4173            */

4174// unsubclass ();
4175
}
4176    }
4177}
4178
4179boolean setScrollWidth (TableItem item, boolean force) {
4180    if (currentItem != null) {
4181        if (currentItem != item) fixScrollWidth = true;
4182        return false;
4183    }
4184    if (!force && (drawCount != 0 || !OS.IsWindowVisible (handle))) {
4185        fixScrollWidth = true;
4186        return false;
4187    }
4188    fixScrollWidth = false;
4189    /*
4190    * NOTE: It is much faster to measure the strings and compute the
4191    * width of the scroll bar in non-virtual table rather than using
4192    * LVM_SETCOLUMNWIDTH with LVSCW_AUTOSIZE.
4193    */

4194    if (columnCount == 0) {
4195        int newWidth = 0, imageIndent = 0, index = 0;
4196        int itemCount = OS.SendMessage (handle, OS.LVM_GETITEMCOUNT, 0, 0);
4197        while (index < itemCount) {
4198            String JavaDoc string = null;
4199            int font = -1;
4200            if (item != null) {
4201                string = item.text;
4202                imageIndent = Math.max (imageIndent, item.imageIndent);
4203                if (item.cellFont != null) font = item.cellFont [0];
4204                if (font == -1) font = item.font;
4205            } else {
4206                if (items [index] != null) {
4207                    TableItem tableItem = items [index];
4208                    string = tableItem.text;
4209                    imageIndent = Math.max (imageIndent, tableItem.imageIndent);
4210                    if (tableItem.cellFont != null) font = tableItem.cellFont [0];
4211                    if (font == -1) font = tableItem.font;
4212                }
4213            }
4214            if (string != null && string.length () != 0) {
4215                if (font != -1) {
4216                    int hDC = OS.GetDC (handle);
4217                    int oldFont = OS.SelectObject (hDC, font);
4218                    int flags = OS.DT_CALCRECT | OS.DT_SINGLELINE | OS.DT_NOPREFIX;
4219                    TCHAR buffer = new TCHAR (getCodePage (), string, false);
4220                    RECT rect = new RECT ();
4221                    OS.DrawText (hDC, buffer, buffer.length (), rect, flags);
4222                    OS.SelectObject (hDC, oldFont);
4223                    OS.ReleaseDC (handle, hDC);
4224                    newWidth = Math.max (newWidth, rect.right - rect.left);
4225                } else {
4226                    TCHAR buffer = new TCHAR (getCodePage (), string, true);
4227                    newWidth = Math.max (newWidth, OS.SendMessage (handle, OS.LVM_GETSTRINGWIDTH, 0, buffer));
4228                }
4229            }
4230            if (item != null) break;
4231            index++;
4232        }
4233        /*
4234        * Bug in Windows. When the width of the first column is
4235        * small but not zero, Windows draws '...' outside of the
4236        * bounds of the text. This is strange, but only causes
4237        * problems when the item is selected. In this case, Windows
4238        * clears the '...' but doesn't redraw it when the item is
4239        * deselected, causing pixel corruption. The fix is to ensure
4240        * that the column is at least wide enough to draw a single
4241        * space.
4242        */

4243        if (newWidth == 0) {
4244            TCHAR buffer = new TCHAR (getCodePage (), " ", true);
4245            newWidth = Math.max (newWidth, OS.SendMessage (handle, OS.LVM_GETSTRINGWIDTH, 0, buffer));
4246        }
4247        int hStateList = OS.SendMessage (handle, OS.LVM_GETIMAGELIST, OS.LVSIL_STATE, 0);
4248        if (hStateList != 0) {
4249            int [] cx = new int [1], cy = new int [1];
4250            OS.ImageList_GetIconSize (hStateList, cx, cy);
4251            newWidth += cx [0] + INSET;
4252        }
4253        int hImageList = OS.SendMessage (handle, OS.LVM_GETIMAGELIST, OS.LVSIL_SMALL, 0);
4254        if (hImageList != 0) {
4255            int [] cx = new int [1], cy = new int [1];
4256            OS.ImageList_GetIconSize (hImageList, cx, cy);
4257            newWidth += (imageIndent + 1) * cx [0];
4258        } else {
4259            /*
4260            * Bug in Windows. When LVM_SETIMAGELIST is used to remove the
4261            * image list by setting it to NULL, the item width and height
4262            * is not changed and space is reserved for icons despite the
4263            * fact that there are none. The fix is to set the image list
4264            * to be very small before setting it to NULL. This causes
4265            * Windows to reserve the smallest possible space when an image
4266            * list is removed. In this case, the scroll width must be one
4267            * pixel larger.
4268            */

4269            newWidth++;
4270        }
4271        newWidth += INSET * 2;
4272        int oldWidth = OS.SendMessage (handle, OS.LVM_GETCOLUMNWIDTH, 0, 0);
4273        if (!OS.IsWinCE && OS.WIN32_VERSION >= OS.VERSION (6, 0)) {
4274            newWidth += VISTA_EXTRA;
4275        }
4276        if (newWidth > oldWidth) {
4277            /*
4278            * Feature in Windows. When LVM_SETCOLUMNWIDTH is sent,
4279            * Windows draws right away instead of queuing a WM_PAINT.
4280            * This can cause recursive calls when called from paint
4281            * or from messages that are retrieving the item data,
4282            * such as WM_NOTIFY, causing a stack overflow. The fix
4283            * is to turn off redraw and queue a repaint, collapsing
4284            * the recursive calls.
4285            */

4286            boolean redraw = drawCount == 0 && OS.IsWindowVisible (handle);
4287            if (redraw) OS.DefWindowProc (handle, OS.WM_SETREDRAW, 0, 0);
4288            OS.SendMessage (handle, OS.LVM_SETCOLUMNWIDTH, 0, newWidth);
4289            if (redraw) {
4290                OS.DefWindowProc (handle, OS.WM_SETREDRAW, 1, 0);
4291                if (OS.IsWinCE) {
4292                    int hwndHeader = OS.SendMessage (handle, OS.LVM_GETHEADER, 0, 0);
4293                    if (hwndHeader != 0) OS.InvalidateRect (hwndHeader, null, true);
4294                    OS.InvalidateRect (handle, null, true);
4295                } else {
4296                    int flags = OS.RDW_ERASE | OS.RDW_FRAME | OS.RDW_INVALIDATE | OS.RDW_ALLCHILDREN;
4297                    OS.RedrawWindow (handle, null, 0, flags);
4298                }
4299            }
4300            return true;
4301        }
4302    }
4303    return false;
4304}
4305
4306/**
4307 * Selects the items at the given zero-relative indices in the receiver.
4308 * The current selection is cleared before the new items are selected.
4309 * <p>
4310 * Indices that are out of range and duplicate indices are ignored.
4311 * If the receiver is single-select and multiple indices are specified,
4312 * then all indices are ignored.
4313 * </p>
4314 *
4315 * @param indices the indices of the items to select
4316 *
4317 * @exception IllegalArgumentException <ul>
4318 * <li>ERROR_NULL_ARGUMENT - if the array of indices is null</li>
4319 * </ul>
4320 * @exception SWTException <ul>
4321 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
4322 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
4323 * </ul>
4324 *
4325 * @see Table#deselectAll()
4326 * @see Table#select(int[])
4327 */

4328public void setSelection (int [] indices) {
4329    checkWidget ();
4330    if (indices == null) error (SWT.ERROR_NULL_ARGUMENT);
4331    deselectAll ();
4332    int length = indices.length;
4333    if (length == 0 || ((style & SWT.SINGLE) != 0 && length > 1)) return;
4334    select (indices);
4335    int focusIndex = indices [0];
4336    if (focusIndex != -1) setFocusIndex (focusIndex);
4337    showSelection ();
4338}
4339
4340/**
4341 * Sets the receiver's selection to the given item.
4342 * The current selection is cleared before the new item is selected.
4343 * <p>
4344 * If the item is not in the receiver, then it is ignored.
4345 * </p>
4346 *
4347 * @param item the item to select
4348 *
4349 * @exception IllegalArgumentException <ul>
4350 * <li>ERROR_NULL_ARGUMENT - if the item is null</li>
4351 * <li>ERROR_INVALID_ARGUMENT - if the item has been disposed</li>
4352 * </ul>
4353 * @exception SWTException <ul>
4354 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
4355 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
4356 * </ul>
4357 *
4358 * @since 3.2
4359 */

4360public void setSelection (TableItem item) {
4361    checkWidget ();
4362    if (item == null) error (SWT.ERROR_NULL_ARGUMENT);
4363    setSelection (new TableItem [] {item});
4364}
4365
4366/**
4367 * Sets the receiver's selection to be the given array of items.
4368 * The current selection is cleared before the new items are selected.
4369 * <p>
4370 * Items that are not in the receiver are ignored.
4371 * If the receiver is single-select and multiple items are specified,
4372 * then all items are ignored.
4373 * </p>
4374 *
4375 * @param items the array of items
4376 *
4377 * @exception IllegalArgumentException <ul>
4378 * <li>ERROR_NULL_ARGUMENT - if the array of items is null</li>
4379 * <li>ERROR_INVALID_ARGUMENT - if one of the items has been disposed</li>
4380 * </ul>
4381 * @exception SWTException <ul>
4382 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
4383 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
4384 * </ul>
4385 *
4386 * @see Table#deselectAll()
4387 * @see Table#select(int[])
4388 * @see Table#setSelection(int[])
4389 */

4390public void setSelection (TableItem [] items) {
4391    checkWidget ();
4392    if (items == null) error (SWT.ERROR_NULL_ARGUMENT);
4393    deselectAll ();
4394    int length = items.length;
4395    if (length == 0 || ((style & SWT.SINGLE) != 0 && length > 1)) return;
4396    int focusIndex = -1;
4397    for (int i=length-1; i>=0; --i) {
4398        int index = indexOf (items [i]);
4399        if (index != -1) {
4400            select (focusIndex = index);
4401        }
4402    }
4403    if (focusIndex != -1) setFocusIndex (focusIndex);
4404    showSelection ();
4405}
4406
4407/**
4408 * Selects the item at the given zero-relative index in the receiver.
4409 * The current selection is first cleared, then the new item is selected.
4410 *
4411 * @param index the index of the item to select
4412 *
4413 * @exception SWTException <ul>
4414 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
4415 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
4416 * </ul>
4417 *
4418 * @see Table#deselectAll()
4419 * @see Table#select(int)
4420 */

4421public void setSelection (int index) {
4422    checkWidget ();
4423    deselectAll ();
4424    select (index);
4425    if (index != -1) setFocusIndex (index);
4426    showSelection ();
4427}
4428
4429/**
4430 * Selects the items in the range specified by the given zero-relative
4431 * indices in the receiver. The range of indices is inclusive.
4432 * The current selection is cleared before the new items are selected.
4433 * <p>
4434 * Indices that are out of range are ignored and no items will be selected
4435 * if start is greater than end.
4436 * If the receiver is single-select and there is more than one item in the
4437 * given range, then all indices are ignored.
4438 * </p>
4439 *
4440 * @param start the start index of the items to select
4441 * @param end the end index of the items to select
4442 *
4443 * @exception SWTException <ul>
4444 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
4445 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
4446 * </ul>
4447 *
4448 * @see Table#deselectAll()
4449 * @see Table#select(int,int)
4450 */

4451public void setSelection (int start, int end) {
4452    checkWidget ();
4453    deselectAll ();
4454    if (end < 0 || start > end || ((style & SWT.SINGLE) != 0 && start != end)) return;
4455    int count = OS.SendMessage (handle, OS.LVM_GETITEMCOUNT, 0, 0);
4456    if (count == 0 || start >= count) return;
4457    start = Math.max (0, start);
4458    end = Math.min (end, count - 1);
4459    select (start, end);
4460    setFocusIndex (start);
4461    showSelection ();
4462}
4463
4464/**
4465 * Sets the column used by the sort indicator for the receiver. A null
4466 * value will clear the sort indicator. The current sort column is cleared
4467 * before the new column is set.
4468 *
4469 * @param column the column used by the sort indicator or <code>null</code>
4470 *
4471 * @exception IllegalArgumentException <ul>
4472 * <li>ERROR_INVALID_ARGUMENT - if the column is disposed</li>
4473 * </ul>
4474 * @exception SWTException <ul>
4475 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
4476 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
4477 * </ul>
4478 *
4479 * @since 3.2
4480 */

4481public void setSortColumn (TableColumn column) {
4482    checkWidget ();
4483    if (column != null && column.isDisposed ()) error (SWT.ERROR_INVALID_ARGUMENT);
4484    if (sortColumn != null && !sortColumn.isDisposed ()) {
4485        sortColumn.setSortDirection (SWT.NONE);
4486    }
4487    sortColumn = column;
4488    if (sortColumn != null && sortDirection != SWT.NONE) {
4489        sortColumn.setSortDirection (sortDirection);
4490    }
4491}
4492
4493/**
4494 * Sets the direction of the sort indicator for the receiver. The value
4495 * can be one of <code>UP</code>, <code>DOWN</code> or <code>NONE</code>.
4496 *
4497 * @param direction the direction of the sort indicator
4498 *
4499 * @exception SWTException <ul>
4500 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
4501 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
4502 * </ul>
4503 *
4504 * @since 3.2
4505 */

4506public void setSortDirection (int direction) {
4507    checkWidget ();
4508    if ((direction & (SWT.UP | SWT.DOWN)) == 0 && direction != SWT.NONE) return;
4509    sortDirection = direction;
4510    if (sortColumn != null && !sortColumn.isDisposed ()) {
4511        sortColumn.setSortDirection (direction);
4512    }
4513}
4514
4515void setSubImagesVisible (boolean visible) {
4516    int dwExStyle = OS.SendMessage (handle, OS.LVM_GETEXTENDEDLISTVIEWSTYLE, 0, 0);
4517    if ((dwExStyle & OS.LVS_EX_SUBITEMIMAGES) != 0 == visible) return;
4518    int bits = visible ? OS.LVS_EX_SUBITEMIMAGES : 0;
4519    OS.SendMessage (handle, OS.LVM_SETEXTENDEDLISTVIEWSTYLE, OS.LVS_EX_SUBITEMIMAGES, bits);
4520}
4521
4522void setTableEmpty () {
4523    if (imageList != null) {
4524        /*
4525        * Bug in Windows. When LVM_SETIMAGELIST is used to remove the
4526        * image list by setting it to NULL, the item width and height
4527        * is not changed and space is reserved for icons despite the
4528        * fact that there are none. The fix is to set the image list
4529        * to be very small before setting it to NULL. This causes
4530        * Windows to reserve the smallest possible space when an image
4531        * list is removed.
4532        */

4533        int hImageList = OS.ImageList_Create (1, 1, 0, 0, 0);
4534        OS.SendMessage (handle, OS.LVM_SETIMAGELIST, OS.LVSIL_SMALL, hImageList);
4535        OS.SendMessage (handle, OS.LVM_SETIMAGELIST, OS.LVSIL_SMALL, 0);
4536        if (headerImageList != null) {
4537            int hHeaderImageList = headerImageList.getHandle ();
4538            int hwndHeader = OS.SendMessage (handle, OS.LVM_GETHEADER, 0, 0);
4539            OS.SendMessage (hwndHeader, OS.HDM_SETIMAGELIST, 0, hHeaderImageList);
4540        }
4541        OS.ImageList_Destroy (hImageList);
4542        display.releaseImageList (imageList);
4543        imageList = null;
4544        if (itemHeight != -1) setItemHeight (false);
4545    }
4546    if (!hooks (SWT.MeasureItem) && !hooks (SWT.EraseItem) && !hooks (SWT.PaintItem)) {
4547        Control control = findBackgroundControl ();
4548        if (control == null) control = this;
4549        if (control.backgroundImage == null) {
4550            setCustomDraw (false);
4551            setBackgroundTransparent (false);
4552            if (OS.COMCTL32_MAJOR < 6) style &= ~SWT.DOUBLE_BUFFERED;
4553        }
4554    }
4555    items = new TableItem [4];
4556    if (columnCount == 0) {
4557        OS.SendMessage (handle, OS.LVM_SETCOLUMNWIDTH, 0, 0);
4558        setScrollWidth (null, false);
4559    }
4560}
4561
4562/**
4563 * Sets the zero-relative index of the item which is currently
4564 * at the top of the receiver. This index can change when items
4565 * are scrolled or new items are added and removed.
4566 *
4567 * @param index the index of the top item
4568 *
4569 * @exception SWTException <ul>
4570 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
4571 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
4572 * </ul>
4573 */

4574public void setTopIndex (int index) {
4575    checkWidget ();
4576    int topIndex = OS.SendMessage (handle, OS.LVM_GETTOPINDEX, 0, 0);
4577    if (index == topIndex) return;
4578    
4579    /*
4580    * Bug in Windows. For some reason, LVM_SCROLL refuses to
4581    * scroll a table vertically when the width and height of
4582    * the table is smaller than a certain size. The values
4583    * that seem to cause the problem are width=68 and height=6
4584    * but there is no guarantee that these values cause the
4585    * failure on different machines or on different versions
4586    * of Windows. It may depend on the font and any number
4587    * of other factors. For example, setting the font to
4588    * anything but the default sometimes fixes the problem.
4589    * The fix is to use LVM_GETCOUNTPERPAGE to detect the
4590    * case when the number of visible items is zero and
4591    * use LVM_ENSUREVISIBLE to scroll the table to make the
4592    * index visible.
4593    */

4594
4595    /*
4596    * Bug in Windows. When the table header is visible and
4597    * there is not enough space to show a single table item,
4598    * LVM_GETCOUNTPERPAGE can return a negative number instead
4599    * of zero. The fix is to test for negative or zero.
4600    */

4601    if (OS.SendMessage (handle, OS.LVM_GETCOUNTPERPAGE, 0, 0) <= 0) {
4602        /*
4603        * Bug in Windows. For some reason, LVM_ENSUREVISIBLE can
4604        * scroll one item more or one item less when there is not
4605        * enough space to show a single table item. The fix is
4606        * to detect the case and call LVM_ENSUREVISIBLE again with
4607        * the same arguments. It seems that once LVM_ENSUREVISIBLE
4608        * has scrolled into the general area, it is able to scroll
4609        * to the exact item.
4610        */

4611        OS.SendMessage (handle, OS.LVM_ENSUREVISIBLE, index, 1);
4612        if (index != OS.SendMessage (handle, OS.LVM_GETTOPINDEX, 0, 0)) {
4613            OS.SendMessage (handle, OS.LVM_ENSUREVISIBLE, index, 1);
4614        }
4615        return;
4616    }
4617
4618    /* Use LVM_SCROLL to scroll the table */
4619    RECT rect = new RECT ();
4620    rect.left = OS.LVIR_BOUNDS;
4621    ignoreCustomDraw = true;
4622    OS.SendMessage (handle, OS.LVM_GETITEMRECT, 0, rect);
4623    ignoreCustomDraw = false;
4624    int dy = (index - topIndex) * (rect.bottom - rect.top);
4625    OS.SendMessage (handle, OS.LVM_SCROLL, 0, dy);
4626}
4627
4628/**
4629 * Shows the column. If the column is already showing in the receiver,
4630 * this method simply returns. Otherwise, the columns are scrolled until
4631 * the column is visible.
4632 *
4633 * @param column the column to be shown
4634 *
4635 * @exception IllegalArgumentException <ul>
4636 * <li>ERROR_NULL_ARGUMENT - if the column is null</li>
4637 * <li>ERROR_INVALID_ARGUMENT - if the column has been disposed</li>
4638 * </ul>
4639 * @exception SWTException <ul>
4640 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
4641 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
4642 * </ul>
4643 *
4644 * @since 3.0
4645 */

4646public void showColumn (TableColumn column) {
4647    checkWidget ();
4648    if (column == null) error (SWT.ERROR_NULL_ARGUMENT);
4649    if (column.isDisposed()) error(SWT.ERROR_INVALID_ARGUMENT);
4650    if (column.parent != this) return;
4651    int index = indexOf (column);
4652    if (!(0 <= index && index < columnCount)) return;
4653    /*
4654    * Feature in Windows. Calling LVM_GETSUBITEMRECT with -1 for the
4655    * row number gives the bounds of the item that would be above the
4656    * first row in the table. This is undocumented and does not work
4657    * for the first column. In this case, to get the bounds of the
4658    * first column, get the bounds of the second column and subtract
4659    * the width of the first. The left edge of the second column is
4660    * also used as the right edge of the first.
4661    */

4662    RECT itemRect = new RECT ();
4663    itemRect.left = OS.LVIR_BOUNDS;
4664    if (index == 0) {
4665        itemRect.top = 1;
4666        ignoreCustomDraw = true;
4667        OS.SendMessage (handle, OS.LVM_GETSUBITEMRECT, -1, itemRect);
4668        ignoreCustomDraw = false;
4669        itemRect.right = itemRect.left;
4670        int width = OS.SendMessage (handle, OS.LVM_GETCOLUMNWIDTH, 0, 0);
4671        itemRect.left = itemRect.right - width;
4672    } else {
4673        itemRect.top = index;
4674        ignoreCustomDraw = true;
4675        OS.SendMessage (handle, OS.LVM_GETSUBITEMRECT, -1, itemRect);
4676        ignoreCustomDraw = false;
4677    }
4678    /*
4679    * Bug in Windows. When a table that is drawing grid lines
4680    * is slowly scrolled horizontally to the left, the table does
4681    * not redraw the newly exposed vertical grid lines. The fix
4682    * is to save the old scroll position, call the window proc,
4683    * get the new scroll position and redraw the new area.
4684    */

4685    int oldPos = 0;
4686    int bits = OS.SendMessage (handle, OS.LVM_GETEXTENDEDLISTVIEWSTYLE, 0, 0);
4687    if ((bits & OS.LVS_EX_GRIDLINES) != 0) {
4688        SCROLLINFO info = new SCROLLINFO ();
4689        info.cbSize = SCROLLINFO.sizeof;
4690        info.fMask = OS.SIF_POS;
4691        OS.GetScrollInfo (handle, OS.SB_HORZ, info);
4692        oldPos = info.nPos;
4693    }
4694    RECT rect = new RECT ();
4695    OS.GetClientRect (handle, rect);
4696    if (itemRect.left < rect.left) {
4697        int dx = itemRect.left - rect.left;
4698        OS.SendMessage (handle, OS.LVM_SCROLL, dx, 0);
4699    } else {
4700        int width = Math.min (rect.right - rect.left, itemRect.right - itemRect.left);
4701        if (itemRect.left + width > rect.right) {
4702            int dx = itemRect.left + width - rect.right;
4703            OS.SendMessage (handle, OS.LVM_SCROLL, dx, 0);
4704        }
4705    }
4706    /*
4707    * Bug in Windows. When a table that is drawing grid lines
4708    * is slowly scrolled horizontally to the left, the table does
4709    * not redraw the newly exposed vertical grid lines. The fix
4710    * is to save the old scroll position, call the window proc,
4711    * get the new scroll position and redraw the new area.
4712    */

4713    if ((bits & OS.LVS_EX_GRIDLINES) != 0) {
4714        SCROLLINFO info = new SCROLLINFO ();
4715        info.cbSize = SCROLLINFO.sizeof;
4716        info.fMask = OS.SIF_POS;
4717        OS.GetScrollInfo (handle, OS.SB_HORZ, info);
4718        int newPos = info.nPos;
4719        if (newPos < oldPos) {
4720            rect.right = oldPos - newPos + GRID_WIDTH;
4721            OS.InvalidateRect (handle, rect, true);
4722        }
4723    }
4724}
4725
4726void showItem (int index) {
4727    /*
4728    * Bug in Windows. For some reason, when there is insufficient space
4729    * to show an item, LVM_ENSUREVISIBLE causes blank lines to be
4730    * inserted at the top of the widget. A call to LVM_GETTOPINDEX will
4731    * return a negative number (this is an impossible result). The fix
4732    * is to use LVM_GETCOUNTPERPAGE to detect the case when the number
4733    * of visible items is zero and use LVM_ENSUREVISIBLE with the
4734    * fPartialOK flag set to true to scroll the table.
4735    */

4736    if (OS.SendMessage (handle, OS.LVM_GETCOUNTPERPAGE, 0, 0) <= 0) {
4737        /*
4738        * Bug in Windows. For some reason, LVM_ENSUREVISIBLE can
4739        * scroll one item more or one item less when there is not
4740        * enough space to show a single table item. The fix is
4741        * to detect the case and call LVM_ENSUREVISIBLE again with
4742        * the same arguments. It seems that once LVM_ENSUREVISIBLE
4743        * has scrolled into the general area, it is able to scroll
4744        * to the exact item.
4745        */

4746        OS.SendMessage (handle, OS.LVM_ENSUREVISIBLE, index, 1);
4747        if (index != OS.SendMessage (handle, OS.LVM_GETTOPINDEX, 0, 0)) {
4748            OS.SendMessage (handle, OS.LVM_ENSUREVISIBLE, index, 1);
4749        }
4750    } else {
4751        OS.SendMessage (handle, OS.LVM_ENSUREVISIBLE, index, 0);
4752    }
4753}
4754
4755/**
4756 * Shows the item. If the item is already showing in the receiver,
4757 * this method simply returns. Otherwise, the items are scrolled until
4758 * the item is visible.
4759 *
4760 * @param item the item to be shown
4761 *
4762 * @exception IllegalArgumentException <ul>
4763 * <li>ERROR_NULL_ARGUMENT - if the item is null</li>
4764 * <li>ERROR_INVALID_ARGUMENT - if the item has been disposed</li>
4765 * </ul>
4766 * @exception SWTException <ul>
4767 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
4768 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
4769 * </ul>
4770 *
4771 * @see Table#showSelection()
4772 */

4773public void showItem (TableItem item) {
4774    checkWidget ();
4775    if (item == null) error (SWT.ERROR_NULL_ARGUMENT);
4776    if (item.isDisposed()) error(SWT.ERROR_INVALID_ARGUMENT);
4777    int index = indexOf (item);
4778    if (index != -1) showItem (index);
4779}
4780
4781/**
4782 * Shows the selection. If the selection is already showing in the receiver,
4783 * this method simply returns. Otherwise, the items are scrolled until
4784 * the selection is visible.
4785 *
4786 * @exception SWTException <ul>
4787 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
4788 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
4789 * </ul>
4790 *
4791 * @see Table#showItem(TableItem)
4792 */

4793public void showSelection () {
4794    checkWidget ();
4795    int index = OS.SendMessage (handle, OS.LVM_GETNEXTITEM, -1, OS.LVNI_SELECTED);
4796    if (index != -1) showItem (index);
4797}
4798
4799/*public*/ void sort () {
4800    checkWidget ();
4801// if ((style & SWT.VIRTUAL) != 0) return;
4802
// int itemCount = OS.SendMessage (handle, OS.LVM_GETITEMCOUNT, 0, 0);
4803
// if (itemCount == 0 || itemCount == 1) return;
4804
// Comparator comparator = new Comparator () {
4805
// int index = sortColumn == null ? 0 : indexOf (sortColumn);
4806
// public int compare (Object object1, Object object2) {
4807
// TableItem item1 = (TableItem) object1, item2 = (TableItem) object2;
4808
// if (sortDirection == SWT.UP || sortDirection == SWT.NONE) {
4809
// return item1.getText (index).compareTo (item2.getText (index));
4810
// } else {
4811
// return item2.getText (index).compareTo (item1.getText (index));
4812
// }
4813
// }
4814
// };
4815
// Arrays.sort (items, 0, itemCount, comparator);
4816
// redraw ();
4817
}
4818
4819void subclass () {
4820    super.subclass ();
4821    if (HeaderProc != 0) {
4822        int hwndHeader = OS.SendMessage (handle, OS.LVM_GETHEADER, 0, 0);
4823        OS.SetWindowLong (hwndHeader, OS.GWL_WNDPROC, display.windowProc);
4824    }
4825}
4826
4827String JavaDoc toolTipText (NMTTDISPINFO hdr) {
4828    int hwndToolTip = OS.SendMessage (handle, OS.LVM_GETTOOLTIPS, 0, 0);
4829    if (hwndToolTip == hdr.hwndFrom && toolTipText != null) return ""; //$NON-NLS-1$
4830
if (headerToolTipHandle == hdr.hwndFrom) {
4831        for (int i=0; i<columnCount; i++) {
4832            TableColumn column = columns [i];
4833            if (column.id == hdr.idFrom) return column.toolTipText;
4834        }
4835    }
4836    return super.toolTipText (hdr);
4837}
4838
4839void unsubclass () {
4840    super.unsubclass ();
4841    if (HeaderProc != 0) {
4842        int hwndHeader = OS.SendMessage (handle, OS.LVM_GETHEADER, 0, 0);
4843        OS.SetWindowLong (hwndHeader, OS.GWL_WNDPROC, HeaderProc);
4844    }
4845}
4846
4847void update (boolean all) {
4848// checkWidget ();
4849
/*
4850    * When there are many columns in a table, scrolling performance
4851    * can be improved by temporarily unsubclassing the window proc
4852    * so that internal messages are dispatched directly to the table.
4853    * If the application expects to see a paint event or has a child
4854    * whose font, foreground or background color might be needed,
4855    * the window proc cannot be unsubclassed.
4856    *
4857    * NOTE: The header tooltip can subclass the header proc so the
4858    * current proc must be restored or header tooltips stop working.
4859    */

4860    int oldHeaderProc = 0, oldTableProc = 0;
4861    int hwndHeader = OS.SendMessage (handle, OS.LVM_GETHEADER, 0, 0);
4862    boolean fixSubclass = !hasChildren () && !hooks (SWT.Paint) && !filters (SWT.Paint);
4863    if (fixSubclass) {
4864        oldTableProc = OS.SetWindowLong (handle, OS.GWL_WNDPROC, TableProc);
4865        oldHeaderProc = OS.SetWindowLong (hwndHeader, OS.GWL_WNDPROC, HeaderProc);
4866    }
4867    super.update (all);
4868    if (fixSubclass) {
4869        OS.SetWindowLong (handle, OS.GWL_WNDPROC, oldTableProc);
4870        OS.SetWindowLong (hwndHeader, OS.GWL_WNDPROC, oldHeaderProc);
4871    }
4872}
4873
4874void updateHeaderToolTips () {
4875    if (headerToolTipHandle == 0) return;
4876    int hwndHeader = OS.SendMessage (handle, OS.LVM_GETHEADER, 0, 0);
4877    RECT rect = new RECT ();
4878    TOOLINFO lpti = new TOOLINFO ();
4879    lpti.cbSize = TOOLINFO.sizeof;
4880    lpti.uFlags = OS.TTF_SUBCLASS;
4881    lpti.hwnd = hwndHeader;
4882    lpti.lpszText = OS.LPSTR_TEXTCALLBACK;
4883    for (int i=0; i<columnCount; i++) {
4884        TableColumn column = columns [i];
4885        if (OS.SendMessage (hwndHeader, OS.HDM_GETITEMRECT, i, rect) != 0) {
4886            lpti.uId = column.id = display.nextToolTipId++;
4887            lpti.left = rect.left;
4888            lpti.top = rect.top;
4889            lpti.right = rect.right;
4890            lpti.bottom = rect.bottom;
4891            OS.SendMessage (headerToolTipHandle, OS.TTM_ADDTOOL, 0, lpti);
4892        }
4893    }
4894}
4895
4896void updateImages () {
4897    if (sortColumn != null && !sortColumn.isDisposed ()) {
4898        if (OS.COMCTL32_MAJOR < 6) {
4899            switch (sortDirection) {
4900                case SWT.UP:
4901                case SWT.DOWN:
4902                    sortColumn.setImage (display.getSortImage (sortDirection), true, true);
4903                    break;
4904            }
4905        }
4906    }
4907}
4908
4909void updateMoveable () {
4910    int index = 0;
4911    while (index < columnCount) {
4912        if (columns [index].moveable) break;
4913        index++;
4914    }
4915    int newBits = index < columnCount ? OS.LVS_EX_HEADERDRAGDROP : 0;
4916    OS.SendMessage (handle, OS.LVM_SETEXTENDEDLISTVIEWSTYLE, OS.LVS_EX_HEADERDRAGDROP, newBits);
4917}
4918
4919int widgetStyle () {
4920    int bits = super.widgetStyle () | OS.LVS_SHAREIMAGELISTS;
4921    if ((style & SWT.HIDE_SELECTION) == 0) bits |= OS.LVS_SHOWSELALWAYS;
4922    if ((style & SWT.SINGLE) != 0) bits |= OS.LVS_SINGLESEL;
4923    /*
4924    * This code is intentionally commented. In the future,
4925    * the FLAT bit may be used to make the header flat and
4926    * unresponsive to mouse clicks.
4927    */

4928// if ((style & SWT.FLAT) != 0) bits |= OS.LVS_NOSORTHEADER;
4929
bits |= OS.LVS_REPORT | OS.LVS_NOCOLUMNHEADER;
4930    if ((style & SWT.VIRTUAL) != 0) bits |= OS.LVS_OWNERDATA;
4931    return bits;
4932}
4933
4934TCHAR windowClass () {
4935    return TableClass;
4936}
4937
4938int windowProc () {
4939    return TableProc;
4940}
4941
4942int windowProc (int hwnd, int msg, int wParam, int lParam) {
4943    if (handle == 0) return 0;
4944    if (hwnd != handle) {
4945        switch (msg) {
4946            /* This code is intentionally commented */
4947// case OS.WM_CONTEXTMENU: {
4948
// LRESULT result = wmContextMenu (hwnd, wParam, lParam);
4949
// if (result != null) return result.value;
4950
// break;
4951
// }
4952
case OS.WM_CAPTURECHANGED: {
4953                /*
4954                * Bug in Windows. When the capture changes during a
4955                * header drag, Windows does not redraw the header item
4956                * such that the header remains pressed. For example,
4957                * when focus is assigned to a push button, the mouse is
4958                * pressed (but not released), then the SPACE key is
4959                * pressed to activate the button, the capture changes,
4960                * the header not notified and NM_RELEASEDCAPTURE is not
4961                * sent. The fix is to redraw the header when the capture
4962                * changes to another control.
4963                *
4964                * This does not happen on XP.
4965                */

4966                if (OS.COMCTL32_MAJOR < 6) {
4967                    if (lParam != 0) {
4968                        int hwndHeader = OS.SendMessage (handle, OS.LVM_GETHEADER, 0, 0);
4969                        if (lParam != hwndHeader) OS.InvalidateRect (hwndHeader, null, true);
4970                    }
4971                }
4972                break;
4973            }
4974            case OS.WM_MOUSELEAVE: {
4975                /*
4976                * Bug in Windows. On XP, when a tooltip is hidden
4977                * due to a time out or mouse press, the tooltip
4978                * remains active although no longer visible and
4979                * won't show again until another tooltip becomes
4980                * active. The fix is to reset the tooltip bounds.
4981                */

4982                if (OS.COMCTL32_MAJOR >= 6) updateHeaderToolTips ();
4983                updateHeaderToolTips ();
4984                break;
4985            }
4986            case OS.WM_NOTIFY: {
4987                NMHDR hdr = new NMHDR ();
4988                OS.MoveMemory (hdr, lParam, NMHDR.sizeof);
4989                switch (hdr.code) {
4990                    case OS.TTN_SHOW:
4991                    case OS.TTN_POP:
4992                    case OS.TTN_GETDISPINFOA:
4993                    case OS.TTN_GETDISPINFOW:
4994                        return OS.SendMessage (handle, msg, wParam, lParam);
4995                }
4996                break;
4997            }
4998            case OS.WM_SETCURSOR: {
4999                if (wParam == hwnd) {
5000                    int hitTest = (short) (lParam & 0xFFFF);
5001                    if (hitTest == OS.HTCLIENT) {
5002                        HDHITTESTINFO pinfo = new HDHITTESTINFO ();
5003                        int pos = OS.GetMessagePos ();
5004                        POINT pt = new POINT ();
5005                        pt.x = (short) (pos & 0xFFFF);
5006                        pt.y = (short) (pos >> 16);
5007                        OS.ScreenToClient (hwnd, pt);
5008                        pinfo.x = pt.x;
5009                        pinfo.y = pt.y;
5010                        int hwndHeader = OS.SendMessage (handle, OS.LVM_GETHEADER, 0, 0);
5011                        int index = OS.SendMessage (hwndHeader, OS.HDM_HITTEST, 0, pinfo);
5012                        if (0 <= index && index < columnCount && !columns [index].resizable) {
5013                            if ((pinfo.flags & (OS.HHT_ONDIVIDER | OS.HHT_ONDIVOPEN)) != 0) {
5014                                OS.SetCursor (OS.LoadCursor (0, OS.IDC_ARROW));
5015                                return 1;
5016                            }
5017                        }
5018                    }
5019                }
5020                break;
5021            }
5022        }
5023        return callWindowProc (hwnd, msg, wParam, lParam);
5024    }
5025    return super.windowProc (hwnd, msg, wParam, lParam);
5026}
5027
5028LRESULT WM_CHAR (int wParam, int lParam) {
5029    LRESULT result = super.WM_CHAR (wParam, lParam);
5030    if (result != null) return result;
5031    switch (wParam) {
5032        case ' ':
5033            if ((style & SWT.CHECK) != 0) {
5034                int index = OS.SendMessage (handle, OS.LVM_GETNEXTITEM, -1, OS.LVNI_FOCUSED);
5035                if (index != -1) {
5036                    TableItem item = _getItem (index);
5037                    item.setChecked (!item.getChecked (), true);
5038                    if (!OS.IsWinCE) {
5039                        OS.NotifyWinEvent (OS.EVENT_OBJECT_FOCUS, handle, OS.OBJID_CLIENT, index + 1);
5040                    }
5041                }
5042            }
5043            /*
5044            * NOTE: Call the window proc with WM_KEYDOWN rather than WM_CHAR
5045            * so that the key that was ignored during WM_KEYDOWN is processed.
5046            * This allows the application to cancel an operation that is normally
5047            * performed in WM_KEYDOWN from WM_CHAR.
5048            */

5049            int code = callWindowProc (handle, OS.WM_KEYDOWN, wParam, lParam);
5050            return new LRESULT (code);
5051        case SWT.CR:
5052            /*
5053            * Feature in Windows. Windows sends LVN_ITEMACTIVATE from WM_KEYDOWN
5054            * instead of WM_CHAR. This means that application code that expects
5055            * to consume the key press and therefore avoid a SWT.DefaultSelection
5056            * event will fail. The fix is to ignore LVN_ITEMACTIVATE when it is
5057            * caused by WM_KEYDOWN and send SWT.DefaultSelection from WM_CHAR.
5058            */

5059            int index = OS.SendMessage (handle, OS.LVM_GETNEXTITEM, -1, OS.LVNI_FOCUSED);
5060            if (index != -1) {
5061                Event event = new Event ();
5062                event.item = _getItem (index);
5063                postEvent (SWT.DefaultSelection, event);
5064            }
5065            return LRESULT.ZERO;
5066    }
5067    return result;
5068}
5069
5070LRESULT WM_CONTEXTMENU (int wParam, int lParam) {
5071    /*
5072    * Feature in Windows. For some reason, when the right
5073    * mouse button is pressed over an item, Windows sends
5074    * a WM_CONTEXTMENU from WM_RBUTTONDOWN, instead of from
5075    * WM_RBUTTONUP. This causes two context menus requests
5076    * to be sent. The fix is to ignore WM_CONTEXTMENU on
5077    * mouse down.
5078    *
5079    * NOTE: This only happens when dragging is disabled.
5080    * When the table is detecting drag, the WM_CONTEXTMENU
5081    * is not sent WM_RBUTTONUP.
5082    */

5083    if (!display.runDragDrop) return LRESULT.ZERO;
5084    return super.WM_CONTEXTMENU (wParam, lParam);
5085}
5086
5087LRESULT WM_ERASEBKGND (int wParam, int lParam) {
5088    LRESULT result = super.WM_ERASEBKGND (wParam, lParam);
5089    if (findImageControl () != null) return LRESULT.ONE;
5090    if (OS.COMCTL32_MAJOR < 6) {
5091        if ((style & SWT.DOUBLE_BUFFERED) != 0) {
5092            int bits = OS.SendMessage (handle, OS.LVM_GETEXTENDEDLISTVIEWSTYLE, 0, 0);
5093            if ((bits & OS.LVS_EX_DOUBLEBUFFER) == 0) return LRESULT.ONE;
5094        }
5095    }
5096    return result;
5097}
5098
5099LRESULT WM_GETOBJECT (int wParam, int lParam) {
5100    /*
5101    * Ensure that there is an accessible object created for this
5102    * control because support for checked item accessibility is
5103    * temporarily implemented in the accessibility package.
5104    */

5105    if ((style & SWT.CHECK) != 0) {
5106        if (accessible == null) accessible = new_Accessible (this);
5107    }
5108    return super.WM_GETOBJECT (wParam, lParam);
5109}
5110
5111LRESULT WM_KEYDOWN (int wParam, int lParam) {
5112    LRESULT result = super.WM_KEYDOWN (wParam, lParam);
5113    if (result != null) return result;
5114    switch (wParam) {
5115        case OS.VK_SPACE:
5116            /*
5117            * Ensure that the window proc does not process VK_SPACE
5118            * so that it can be handled in WM_CHAR. This allows the
5119            * application to cancel an operation that is normally
5120            * performed in WM_KEYDOWN from WM_CHAR.
5121            */

5122            return LRESULT.ZERO;
5123        case OS.VK_ADD:
5124            if (OS.GetKeyState (OS.VK_CONTROL) < 0) {
5125                int index = 0;
5126                while (index < columnCount) {
5127                    if (!columns [index].getResizable ()) break;
5128                    index++;
5129                }
5130                if (index != columnCount || hooks (SWT.MeasureItem)) {
5131                    TableColumn [] newColumns = new TableColumn [columnCount];
5132                    System.arraycopy (columns, 0, newColumns, 0, columnCount);
5133                    for (int i=0; i<newColumns.length; i++) {
5134                        TableColumn column = newColumns [i];
5135                        if (!column.isDisposed () && column.getResizable ()) {
5136                            column.pack ();
5137                        }
5138                    }
5139                    return LRESULT.ZERO;
5140                }
5141            }
5142            break;
5143        case OS.VK_PRIOR:
5144        case OS.VK_NEXT:
5145        case OS.VK_HOME:
5146        case OS.VK_END:
5147            /*
5148            * When there are many columns in a table, scrolling performance
5149            * can be improved by temporarily unsubclassing the window proc
5150            * so that internal messages are dispatched directly to the table.
5151            * If the application expects to see a paint event, the window
5152            * proc cannot be unsubclassed or the event will not be seen.
5153            *
5154            * NOTE: The header tooltip can subclass the header proc so the
5155            * current proc must be restored or header tooltips stop working.
5156            */

5157            int oldHeaderProc = 0, oldTableProc = 0;
5158            int hwndHeader = OS.SendMessage (handle, OS.LVM_GETHEADER, 0, 0);
5159            boolean fixSubclass = !hasChildren () && !hooks (SWT.Paint) && !filters (SWT.Paint);
5160            if (fixSubclass) {
5161                oldTableProc = OS.SetWindowLong (handle, OS.GWL_WNDPROC, TableProc);
5162                oldHeaderProc = OS.SetWindowLong (hwndHeader, OS.GWL_WNDPROC, HeaderProc);
5163            }
5164            int code = callWindowProc (handle, OS.WM_KEYDOWN, wParam, lParam);
5165            result = code == 0 ? LRESULT.ZERO : new LRESULT (code);
5166            if (fixSubclass) {
5167                OS.SetWindowLong (handle, OS.GWL_WNDPROC, oldTableProc);
5168                OS.SetWindowLong (hwndHeader, OS.GWL_WNDPROC, oldHeaderProc);
5169            }
5170            //FALL THROUGH
5171
case OS.VK_UP:
5172        case OS.VK_DOWN:
5173            OS.SendMessage (handle, OS.WM_CHANGEUISTATE, OS.UIS_INITIALIZE, 0);
5174            break;
5175    }
5176    return result;
5177}
5178
5179LRESULT WM_KILLFOCUS (int wParam, int lParam) {
5180    LRESULT result = super.WM_KILLFOCUS (wParam, lParam);
5181    /*
5182    * Bug in Windows. When focus is lost, Windows does not
5183    * redraw the selection properly, leaving the image and
5184    * check box appearing selected. The fix is to redraw
5185    * the table.
5186    */

5187    if (imageList != null || (style & SWT.CHECK) != 0) {
5188        OS.InvalidateRect (handle, null, false);
5189    }
5190    return result;
5191}
5192
5193LRESULT WM_LBUTTONDBLCLK (int wParam, int lParam) {
5194
5195    /*
5196    * Feature in Windows. When the user selects outside of
5197    * a table item, Windows deselects all the items, even
5198    * when the table is multi-select. While not strictly
5199    * wrong, this is unexpected. The fix is to detect the
5200    * case and avoid calling the window proc.
5201    */

5202    LVHITTESTINFO pinfo = new LVHITTESTINFO ();
5203    pinfo.x = (short) (lParam & 0xFFFF);
5204    pinfo.y = (short) (lParam >> 16);
5205    int index = OS.SendMessage (handle, OS.LVM_HITTEST, 0, pinfo);
5206    Display display = this.display;
5207    display.captureChanged = false;
5208    sendMouseEvent (SWT.MouseDown, 1, handle, OS.WM_LBUTTONDOWN, wParam, lParam);
5209    if (!sendMouseEvent (SWT.MouseDoubleClick, 1, handle, OS.WM_LBUTTONDBLCLK, wParam, lParam)) {
5210        if (!display.captureChanged && !isDisposed ()) {
5211            if (OS.GetCapture () != handle) OS.SetCapture (handle);
5212        }
5213        return LRESULT.ZERO;
5214    }
5215    if (pinfo.iItem != -1) callWindowProc (handle, OS.WM_LBUTTONDBLCLK, wParam, lParam);
5216    if (!display.captureChanged && !isDisposed ()) {
5217        if (OS.GetCapture () != handle) OS.SetCapture (handle);
5218    }
5219    
5220    /* Look for check/uncheck */
5221    if ((style & SWT.CHECK) != 0) {
5222        /*
5223        * Note that when the table has LVS_EX_FULLROWSELECT and the
5224        * user clicks anywhere on a row except on the check box, all
5225        * of the bits are set. The hit test flags are LVHT_ONITEM.
5226        * This means that a bit test for LVHT_ONITEMSTATEICON is not
5227        * the correct way to determine that the user has selected
5228        * the check box, equality is needed.
5229        */

5230        if (index != -1 && pinfo.flags == OS.LVHT_ONITEMSTATEICON) {
5231            TableItem item = _getItem (index);
5232            item.setChecked (!item.getChecked (), true);
5233            if (!OS.IsWinCE) {
5234                OS.NotifyWinEvent (OS.EVENT_OBJECT_FOCUS, handle, OS.OBJID_CLIENT, index + 1);
5235            }
5236        }
5237    }
5238    return LRESULT.ZERO;
5239}
5240
5241LRESULT WM_LBUTTONDOWN (int wParam, int lParam) {
5242    /*
5243    * Feature in Windows. For some reason, capturing
5244    * the mouse after processing the mouse event for the
5245    * widget interferes with the normal mouse processing
5246    * for the widget. The fix is to avoid the automatic
5247    * mouse capture.
5248    */

5249    LRESULT result = sendMouseDownEvent (SWT.MouseDown, 1, OS.WM_LBUTTONDOWN, wParam, lParam);
5250    if (result == LRESULT.ZERO) return result;
5251
5252    /* Look for check/uncheck */
5253    if ((style & SWT.CHECK) != 0) {
5254        LVHITTESTINFO pinfo = new LVHITTESTINFO ();
5255        pinfo.x = (short) (lParam & 0xFFFF);
5256        pinfo.y = (short) (lParam >> 16);
5257        /*
5258        * Note that when the table has LVS_EX_FULLROWSELECT and the
5259        * user clicks anywhere on a row except on the check box, all
5260        * of the bits are set. The hit test flags are LVHT_ONITEM.
5261        * This means that a bit test for LVHT_ONITEMSTATEICON is not
5262        * the correct way to determine that the user has selected
5263        * the check box, equality is needed.
5264        */

5265        int index = OS.SendMessage (handle, OS.LVM_HITTEST, 0, pinfo);
5266        if (index != -1 && pinfo.flags == OS.LVHT_ONITEMSTATEICON) {
5267            TableItem item = _getItem (index);
5268            item.setChecked (!item.getChecked (), true);
5269            if (!OS.IsWinCE) {
5270                OS.NotifyWinEvent (OS.EVENT_OBJECT_FOCUS, handle, OS.OBJID_CLIENT, index + 1);
5271            }
5272        }
5273    }
5274    return result;
5275}
5276
5277LRESULT WM_MOUSEHOVER (int wParam, int lParam) {
5278    /*
5279    * Feature in Windows. Despite the fact that hot
5280    * tracking is not enabled, the hot tracking code
5281    * in WM_MOUSEHOVER is executed causing the item
5282    * under the cursor to be selected. The fix is to
5283    * avoid calling the window proc.
5284    */

5285    LRESULT result = super.WM_MOUSEHOVER (wParam, lParam);
5286    int bits = OS.SendMessage (handle, OS.LVM_GETEXTENDEDLISTVIEWSTYLE, 0, 0);
5287    int mask = OS.LVS_EX_ONECLICKACTIVATE | OS.LVS_EX_TRACKSELECT | OS.LVS_EX_TWOCLICKACTIVATE;
5288    if ((bits & mask) != 0) return result;
5289    return LRESULT.ZERO;
5290}
5291
5292LRESULT WM_PAINT (int wParam, int lParam) {
5293    if (!ignoreShrink) {
5294        /* Resize the item array to match the item count */
5295        int count = OS.SendMessage (handle, OS.LVM_GETITEMCOUNT, 0, 0);
5296        if (items.length > 4 && items.length - count > 3) {
5297            int length = Math.max (4, (count + 3) / 4 * 4);
5298            TableItem [] newItems = new TableItem [length];
5299            System.arraycopy (items, 0, newItems, 0, count);
5300            items = newItems;
5301        }
5302    }
5303    if (fixScrollWidth) setScrollWidth (null, true);
5304    if (OS.COMCTL32_MAJOR < 6) {
5305        if ((style & SWT.DOUBLE_BUFFERED) != 0 || findImageControl () != null) {
5306            int bits = OS.SendMessage (handle, OS.LVM_GETEXTENDEDLISTVIEWSTYLE, 0, 0);
5307            if ((bits & OS.LVS_EX_DOUBLEBUFFER) == 0) {
5308                GC gc = null;
5309                int paintDC = 0;
5310                PAINTSTRUCT ps = new PAINTSTRUCT ();
5311                boolean hooksPaint = hooks (SWT.Paint);
5312                if (hooksPaint) {
5313                    GCData data = new GCData ();
5314                    data.ps = ps;
5315                    data.hwnd = handle;
5316                    gc = GC.win32_new (this, data);
5317                    paintDC = gc.handle;
5318                } else {
5319                    paintDC = OS.BeginPaint (handle, ps);
5320                }
5321                int width = ps.right - ps.left;
5322                int height = ps.bottom - ps.top;
5323                if (width != 0 && height != 0) {
5324                    int hDC = OS.CreateCompatibleDC (paintDC);
5325                    POINT lpPoint1 = new POINT (), lpPoint2 = new POINT ();
5326                    OS.SetWindowOrgEx (hDC, ps.left, ps.top, lpPoint1);
5327                    OS.SetBrushOrgEx (hDC, ps.left, ps.top, lpPoint2);
5328                    int hBitmap = OS.CreateCompatibleBitmap (paintDC, width, height);
5329                    int hOldBitmap = OS.SelectObject (hDC, hBitmap);
5330                    if (OS.SendMessage (handle, OS.LVM_GETBKCOLOR, 0, 0) != OS.CLR_NONE) {
5331                        RECT rect = new RECT ();
5332                        OS.SetRect (rect, ps.left, ps.top, ps.right, ps.bottom);
5333                        drawBackground (hDC, rect);
5334                    }
5335                    callWindowProc (handle, OS.WM_PAINT, hDC, 0);
5336                    OS.SetWindowOrgEx (hDC, lpPoint1.x, lpPoint1.y, null);
5337                    OS.SetBrushOrgEx (hDC, lpPoint2.x, lpPoint2.y, null);
5338                    OS.BitBlt (paintDC, ps.left, ps.top, width, height, hDC, 0, 0, OS.SRCCOPY);
5339                    OS.SelectObject (hDC, hOldBitmap);
5340                    OS.DeleteObject (hBitmap);
5341                    OS.DeleteObject (hDC);
5342                    if (hooksPaint) {
5343                        Event event = new Event ();
5344                        event.gc = gc;
5345                        event.x = ps.left;
5346                        event.y = ps.top;
5347                        event.width = ps.right - ps.left;
5348                        event.height = ps.bottom - ps.top;
5349                        sendEvent (SWT.Paint, event);
5350                        // widget could be disposed at this point
5351
event.gc = null;
5352                    }
5353                }
5354                if (hooksPaint) {
5355                    gc.dispose ();
5356                } else {
5357                    OS.EndPaint (handle, ps);
5358                }
5359                return LRESULT.ZERO;
5360            }
5361        }
5362    }
5363    return super.WM_PAINT (wParam, lParam);
5364}
5365
5366LRESULT WM_RBUTTONDBLCLK (int wParam, int lParam) {
5367    /*
5368    * Feature in Windows. When the user selects outside of
5369    * a table item, Windows deselects all the items, even
5370    * when the table is multi-select. While not strictly
5371    * wrong, this is unexpected. The fix is to detect the
5372    * case and avoid calling the window proc.
5373    */

5374    LVHITTESTINFO pinfo = new LVHITTESTINFO ();
5375    pinfo.x = (short) (lParam & 0xFFFF);
5376    pinfo.y = (short) (lParam >> 16);
5377    OS.SendMessage (handle, OS.LVM_HITTEST, 0, pinfo);
5378    Display display = this.display;
5379    display.captureChanged = false;
5380    sendMouseEvent (SWT.MouseDown, 3, handle, OS.WM_RBUTTONDOWN, wParam, lParam);
5381    if (sendMouseEvent (SWT.MouseDoubleClick, 3, handle, OS.WM_RBUTTONDBLCLK, wParam, lParam)) {
5382        if (pinfo.iItem != -1) callWindowProc (handle, OS.WM_RBUTTONDBLCLK, wParam, lParam);
5383    }
5384    if (!display.captureChanged && !isDisposed ()) {
5385        if (OS.GetCapture () != handle) OS.SetCapture (handle);
5386    }
5387    return LRESULT.ZERO;
5388}
5389
5390LRESULT WM_RBUTTONDOWN (int wParam, int lParam) {
5391    /*
5392    * Feature in Windows. For some reason, capturing
5393    * the mouse after processing the mouse event for the
5394    * widget interferes with the normal mouse processing
5395    * for the widget. The fix is to avoid the automatic
5396    * mouse capture.
5397    */

5398    return sendMouseDownEvent (SWT.MouseDown, 3, OS.WM_RBUTTONDOWN, wParam, lParam);
5399}
5400
5401LRESULT WM_SETFOCUS (int wParam, int lParam) {
5402    LRESULT result = super.WM_SETFOCUS (wParam, lParam);
5403    /*
5404    * Bug in Windows. When focus is gained after the
5405    * selection has been changed using LVM_SETITEMSTATE,
5406    * Windows redraws the selected text but does not
5407    * redraw the image or the check box, leaving them
5408    * appearing unselected. The fix is to redraw
5409    * the table.
5410    */

5411    if (imageList != null || (style & SWT.CHECK) != 0) {
5412        OS.InvalidateRect (handle, null, false);
5413    }
5414    
5415    /*
5416    * Bug in Windows. For some reason, the table does
5417    * not set the default focus rectangle to be the first
5418    * item in the table when it gets focus and there is
5419    * no selected item. The fix to make the first item
5420    * be the focus item.
5421    */

5422    int count = OS.SendMessage (handle, OS.LVM_GETITEMCOUNT, 0, 0);
5423    if (count == 0) return result;
5424    int index = OS.SendMessage (handle, OS.LVM_GETNEXTITEM, -1, OS.LVNI_FOCUSED);
5425    if (index == -1) {
5426        LVITEM lvItem = new LVITEM ();
5427        lvItem.state = OS.LVIS_FOCUSED;
5428        lvItem.stateMask = OS.LVIS_FOCUSED;
5429        ignoreSelect = true;
5430        OS.SendMessage (handle, OS.LVM_SETITEMSTATE, 0, lvItem);
5431        ignoreSelect = false;
5432    }
5433    return result;
5434}
5435
5436LRESULT WM_SETFONT (int wParam, int lParam) {
5437    LRESULT result = super.WM_SETFONT (wParam, lParam);
5438    if (result != null) return result;
5439    
5440    /*
5441    * Bug in Windows. When a header has a sort indicator
5442    * triangle, Windows resizes the indicator based on the
5443    * size of the n-1th font. The fix is to always make
5444    * the n-1th font be the default. This makes the sort
5445    * indicator always be the default size.
5446    *
5447    * NOTE: The table window proc sets the actual font in
5448    * the header so that all that is necessary here is to
5449    * set the default first.
5450    */

5451    int hwndHeader = OS.SendMessage (handle, OS.LVM_GETHEADER, 0, 0);
5452    OS.SendMessage (hwndHeader, OS.WM_SETFONT, 0, lParam);
5453    
5454    if (headerToolTipHandle != 0) {
5455        OS.SendMessage (headerToolTipHandle, OS.WM_SETFONT, wParam, lParam);
5456    }
5457    return result;
5458}
5459
5460LRESULT WM_SIZE (int wParam, int lParam) {
5461    if (ignoreResize) return null;
5462    if (hooks (SWT.EraseItem) || hooks (SWT.PaintItem)) {
5463        OS.InvalidateRect (handle, null, true);
5464    }
5465    if (resizeCount != 0) {
5466        wasResized = true;
5467        return null;
5468    }
5469    return super.WM_SIZE (wParam, lParam);
5470}
5471
5472LRESULT WM_SYSCOLORCHANGE (int wParam, int lParam) {
5473    LRESULT result = super.WM_SYSCOLORCHANGE (wParam, lParam);
5474    if (result != null) return result;
5475    if (findBackgroundControl () == null) {
5476        setBackgroundPixel (defaultBackground ());
5477    } else {
5478        if ((style & SWT.CHECK) != 0) {
5479            fixCheckboxImageListColor (true);
5480        }
5481    }
5482    return result;
5483}
5484
5485LRESULT WM_HSCROLL (int wParam, int lParam) {
5486    /*
5487    * Bug in Windows. When a table that is drawing grid lines
5488    * is slowly scrolled horizontally to the left, the table does
5489    * not redraw the newly exposed vertical grid lines. The fix
5490    * is to save the old scroll position, call the window proc,
5491    * get the new scroll position and redraw the new area.
5492    */

5493    int oldPos = 0;
5494    int bits = OS.SendMessage (handle, OS.LVM_GETEXTENDEDLISTVIEWSTYLE, 0, 0);
5495    if ((bits & OS.LVS_EX_GRIDLINES) != 0) {
5496        SCROLLINFO info = new SCROLLINFO ();
5497        info.cbSize = SCROLLINFO.sizeof;
5498        info.fMask = OS.SIF_POS;
5499        OS.GetScrollInfo (handle, OS.SB_HORZ, info);
5500        oldPos = info.nPos;
5501    }
5502    
5503    /*
5504    * When there are many columns in a table, scrolling performance
5505    * can be improved by temporarily unsubclassing the window proc
5506    * so that internal messages are dispatched directly to the table.
5507    * If the application expects to see a paint event or has a child
5508    * whose font, foreground or background color might be needed,
5509    * the window proc cannot be unsubclassed
5510    *
5511    * NOTE: The header tooltip can subclass the header proc so the
5512    * current proc must be restored or header tooltips stop working.
5513    */

5514    int oldHeaderProc = 0, oldTableProc = 0;
5515    int hwndHeader = OS.SendMessage (handle, OS.LVM_GETHEADER, 0, 0);
5516    boolean fixSubclass = !hasChildren () && !hooks (SWT.Paint) && !filters (SWT.Paint);
5517    if (fixSubclass) {
5518        oldTableProc = OS.SetWindowLong (handle, OS.GWL_WNDPROC, TableProc);
5519        oldHeaderProc = OS.SetWindowLong (hwndHeader, OS.GWL_WNDPROC, HeaderProc);
5520    }
5521    LRESULT result = super.WM_HSCROLL (wParam, lParam);
5522    if (fixSubclass) {
5523        OS.SetWindowLong (handle, OS.GWL_WNDPROC, oldTableProc);
5524        OS.SetWindowLong (hwndHeader, OS.GWL_WNDPROC, oldHeaderProc);
5525    }
5526    
5527    /*
5528    * Bug in Windows. When a table that is drawing grid lines
5529    * is slowly scrolled horizontally to the left, the table does
5530    * not redraw the newly exposed vertical grid lines. The fix
5531    * is to save the old scroll position, call the window proc,
5532    * get the new scroll position and redraw the new area.
5533    */

5534    if ((bits & OS.LVS_EX_GRIDLINES) != 0) {
5535        SCROLLINFO info = new SCROLLINFO ();
5536        info.cbSize = SCROLLINFO.sizeof;
5537        info.fMask = OS.SIF_POS;
5538        OS.GetScrollInfo (handle, OS.SB_HORZ, info);
5539        int newPos = info.nPos;
5540        if (newPos < oldPos) {
5541            RECT rect = new RECT ();
5542            OS.GetClientRect (handle, rect);
5543            rect.right = oldPos - newPos + GRID_WIDTH;
5544            OS.InvalidateRect (handle, rect, true);
5545        }
5546    }
5547    return result;
5548}
5549
5550LRESULT WM_VSCROLL (int wParam, int lParam) {
5551    /*
5552    * When there are many columns in a table, scrolling performance
5553    * can be improved by temporarily unsubclassing the window proc
5554    * so that internal messages are dispatched directly to the table.
5555    * If the application expects to see a paint event or has a child
5556    * whose font, foreground or background color might be needed,
5557    * the window proc cannot be unsubclassed.
5558    *
5559    * NOTE: The header tooltip can subclass the header proc so the
5560    * current proc must be restored or header tooltips stop working.
5561    */

5562    int oldHeaderProc = 0, oldTableProc = 0;
5563    int hwndHeader = OS.SendMessage (handle, OS.LVM_GETHEADER, 0, 0);
5564    boolean fixSubclass = !hasChildren () && !hooks (SWT.Paint) && !filters (SWT.Paint);
5565    if (fixSubclass) {
5566        oldTableProc = OS.SetWindowLong (handle, OS.GWL_WNDPROC, TableProc);
5567        oldHeaderProc = OS.SetWindowLong (hwndHeader, OS.GWL_WNDPROC, HeaderProc);
5568    }
5569    LRESULT result = super.WM_VSCROLL (wParam, lParam);
5570    if (fixSubclass) {
5571        OS.SetWindowLong (handle, OS.GWL_WNDPROC, oldTableProc);
5572        OS.SetWindowLong (hwndHeader, OS.GWL_WNDPROC, oldHeaderProc);
5573    }
5574    
5575    /*
5576    * Bug in Windows. When a table is drawing grid lines and the
5577    * user scrolls vertically up or down by a line or a page, the
5578    * table does not redraw the grid lines for newly exposed items.
5579    * The fix is to invalidate the items.
5580    */

5581    int bits = OS.SendMessage (handle, OS.LVM_GETEXTENDEDLISTVIEWSTYLE, 0, 0);
5582    if ((bits & OS.LVS_EX_GRIDLINES) != 0) {
5583        int code = wParam & 0xFFFF;
5584        switch (code) {
5585            case OS.SB_ENDSCROLL:
5586            case OS.SB_THUMBPOSITION:
5587            case OS.SB_THUMBTRACK:
5588            case OS.SB_TOP:
5589            case OS.SB_BOTTOM:
5590                break;
5591            case OS.SB_LINEDOWN:
5592            case OS.SB_LINEUP:
5593                RECT rect = new RECT ();
5594                OS.GetWindowRect (hwndHeader, rect);
5595                int headerHeight = rect.bottom - rect.top;
5596                RECT clientRect = new RECT ();
5597                OS.GetClientRect (handle, clientRect);
5598                clientRect.top += headerHeight;
5599                int empty = OS.SendMessage (handle, OS.LVM_APPROXIMATEVIEWRECT, 0, 0);
5600                int oneItem = OS.SendMessage (handle, OS.LVM_APPROXIMATEVIEWRECT, 1, 0);
5601                int itemHeight = (oneItem >> 16) - (empty >> 16);
5602                if (code == OS.SB_LINEDOWN) {
5603                    clientRect.top = clientRect.bottom - itemHeight - GRID_WIDTH;
5604                } else {
5605                    clientRect.bottom = clientRect.top + itemHeight + GRID_WIDTH;
5606                }
5607                OS.InvalidateRect (handle, clientRect, true);
5608                break;
5609            case OS.SB_PAGEDOWN:
5610            case OS.SB_PAGEUP:
5611                OS.InvalidateRect (handle, null, true);
5612                break;
5613        }
5614    }
5615    return result;
5616}
5617
5618LRESULT wmMeasureChild (int wParam, int lParam) {
5619    MEASUREITEMSTRUCT struct = new MEASUREITEMSTRUCT ();
5620    OS.MoveMemory (struct, lParam, MEASUREITEMSTRUCT.sizeof);
5621    if (itemHeight == -1) {
5622        int empty = OS.SendMessage (handle, OS.LVM_APPROXIMATEVIEWRECT, 0, 0);
5623        int oneItem = OS.SendMessage (handle, OS.LVM_APPROXIMATEVIEWRECT, 1, 0);
5624        struct.itemHeight = (oneItem >> 16) - (empty >> 16);
5625    } else {
5626        struct.itemHeight = itemHeight;
5627    }
5628    OS.MoveMemory (lParam, struct, MEASUREITEMSTRUCT.sizeof);
5629    return null;
5630}
5631
5632LRESULT wmNotify (NMHDR hdr, int wParam, int lParam) {
5633    int hwndHeader = OS.SendMessage (handle, OS.LVM_GETHEADER, 0, 0);
5634    if (hdr.hwndFrom == hwndHeader) {
5635        /*
5636        * Feature in Windows. On NT, the automatically created
5637        * header control is created as a UNICODE window, not an
5638        * ANSI window despite the fact that the parent is created
5639        * as an ANSI window. This means that it sends UNICODE
5640        * notification messages to the parent window on NT for
5641        * no good reason. The data and size in the NMHEADER and
5642        * HDITEM structs is identical between the platforms so no
5643        * different message is actually necessary. Despite this,
5644        * Windows sends different messages. The fix is to look
5645        * for both messages, despite the platform. This works
5646        * because only one will be sent on either platform, never
5647        * both.
5648        */

5649        switch (hdr.code) {
5650            case OS.HDN_BEGINTRACKW:
5651            case OS.HDN_BEGINTRACKA:
5652            case OS.HDN_DIVIDERDBLCLICKW:
5653            case OS.HDN_DIVIDERDBLCLICKA: {
5654                if (columnCount == 0) return LRESULT.ONE;
5655                NMHEADER phdn = new NMHEADER ();
5656                OS.MoveMemory (phdn, lParam, NMHEADER.sizeof);
5657                TableColumn column = columns [phdn.iItem];
5658                if (column != null && !column.getResizable ()) {
5659                    return LRESULT.ONE;
5660                }
5661                ignoreColumnMove = true;
5662                switch (hdr.code) {
5663                    case OS.HDN_DIVIDERDBLCLICKW:
5664                    case OS.HDN_DIVIDERDBLCLICKA:
5665                        /*
5666                        * Bug in Windows. When the first column of a table does not
5667                        * have an image and the user double clicks on the divider,
5668                        * Windows packs the column but does not take into account
5669                        * the empty space left for the image. The fix is to measure
5670                        * each items ourselves rather than letting Windows do it.
5671                        */

5672                        boolean fixPack = phdn.iItem == 0 && !firstColumnImage;
5673                        if (column != null && (fixPack || hooks (SWT.MeasureItem))) {
5674                            column.pack ();
5675                            return LRESULT.ONE;
5676                        }
5677                }
5678                break;
5679            }
5680            case OS.NM_RELEASEDCAPTURE: {
5681                if (!ignoreColumnMove) {
5682                    for (int i=0; i<columnCount; i++) {
5683                        TableColumn column = columns [i];
5684                        column.updateToolTip (i);
5685                    }
5686                }
5687                ignoreColumnMove = false;
5688                break;
5689            }
5690            case OS.HDN_BEGINDRAG: {
5691                if (ignoreColumnMove) return LRESULT.ONE;
5692                int bits = OS.SendMessage (handle, OS.LVM_GETEXTENDEDLISTVIEWSTYLE, 0, 0);
5693                if ((bits & OS.LVS_EX_HEADERDRAGDROP) == 0) break;
5694                if (columnCount == 0) return LRESULT.ONE;
5695                NMHEADER phdn = new NMHEADER ();
5696                OS.MoveMemory (phdn, lParam, NMHEADER.sizeof);
5697                if (phdn.iItem != -1) {
5698                    TableColumn column = columns [phdn.iItem];
5699                    if (column != null && !column.getMoveable ()) {
5700                        ignoreColumnMove = true;
5701                        return LRESULT.ONE;
5702                    }
5703                }
5704                break;
5705            }
5706            case OS.HDN_ENDDRAG: {
5707                int bits = OS.SendMessage (handle, OS.LVM_GETEXTENDEDLISTVIEWSTYLE, 0, 0);
5708                if ((bits & OS.LVS_EX_HEADERDRAGDROP) == 0) break;
5709                NMHEADER phdn = new NMHEADER ();
5710                OS.MoveMemory (phdn, lParam, NMHEADER.sizeof);
5711                if (phdn.iItem != -1 && phdn.pitem != 0) {
5712                    HDITEM pitem = new HDITEM ();
5713                    OS.MoveMemory (pitem, phdn.pitem, HDITEM.sizeof);
5714                    if ((pitem.mask & OS.HDI_ORDER) != 0 && pitem.iOrder != -1) {
5715                        if (columnCount == 0) break;
5716                        int [] order = new int [columnCount];
5717                        OS.SendMessage (handle, OS.LVM_GETCOLUMNORDERARRAY, columnCount, order);
5718                        int index = 0;
5719                        while (index < order.length) {
5720                            if (order [index] == phdn.iItem) break;
5721                            index++;
5722                        }
5723                        if (index == order.length) index = 0;
5724                        if (index == pitem.iOrder) break;
5725                        int start = Math.min (index, pitem.iOrder);
5726                        int end = Math.max (index, pitem.iOrder);
5727                        ignoreColumnMove = false;
5728                        for (int i=start; i<=end; i++) {
5729                            TableColumn column = columns [order [i]];
5730                            if (!column.isDisposed ()) {
5731                                column.postEvent (SWT.Move);
5732                            }
5733                        }
5734                    }
5735                }
5736                break;
5737            }
5738            case OS.HDN_ITEMCHANGEDW:
5739            case OS.HDN_ITEMCHANGEDA: {
5740                /*
5741                * Bug in Windows. When a table has the LVS_EX_GRIDLINES extended
5742                * style and the user drags any column over the first column in the
5743                * table, making the size become zero, when the user drags a column
5744                * such that the size of the first column becomes non-zero, the grid
5745                * lines are not redrawn. The fix is to detect the case and force
5746                * a redraw of the first column.
5747                */

5748                int width = OS.SendMessage (handle, OS.LVM_GETCOLUMNWIDTH, 0, 0);
5749                if (lastWidth == 0 && width > 0) {
5750                    int bits = OS.SendMessage (handle, OS.LVM_GETEXTENDEDLISTVIEWSTYLE, 0, 0);
5751                    if ((bits & OS.LVS_EX_GRIDLINES) != 0) {
5752                        RECT rect = new RECT ();
5753                        OS.GetClientRect (handle, rect);
5754                        rect.right = rect.left + width;
5755                        OS.InvalidateRect (handle, rect, true);
5756                    }
5757                }
5758                lastWidth = width;
5759                if (!ignoreColumnResize) {
5760                    NMHEADER phdn = new NMHEADER ();
5761                    OS.MoveMemory (phdn, lParam, NMHEADER.sizeof);
5762                    if (phdn.pitem != 0) {
5763                        HDITEM pitem = new HDITEM ();
5764                        OS.MoveMemory (pitem, phdn.pitem, HDITEM.sizeof);
5765                        if ((pitem.mask & OS.HDI_WIDTH) != 0) {
5766                            TableColumn column = columns [phdn.iItem];
5767                            if (column != null) {
5768                                column.updateToolTip (phdn.iItem);
5769                                column.sendEvent (SWT.Resize);
5770                                if (isDisposed ()) return LRESULT.ZERO;
5771                                /*
5772                                * It is possible (but unlikely), that application
5773                                * code could have disposed the column in the move
5774                                * event. If this happens, process the move event
5775                                * for those columns that have not been destroyed.
5776                                */

5777                                TableColumn [] newColumns = new TableColumn [columnCount];
5778                                System.arraycopy (columns, 0, newColumns, 0, columnCount);
5779                                int [] order = new int [columnCount];
5780                                OS.SendMessage (handle, OS.LVM_GETCOLUMNORDERARRAY, columnCount, order);
5781                                boolean moved = false;
5782                                for (int i=0; i<columnCount; i++) {
5783                                    TableColumn nextColumn = newColumns [order [i]];
5784                                    if (moved && !nextColumn.isDisposed ()) {
5785                                        nextColumn.updateToolTip (order [i]);
5786                                        nextColumn.sendEvent (SWT.Move);
5787                                    }
5788                                    if (nextColumn == column) moved = true;
5789                                }
5790                            }
5791                        }
5792                    }
5793                }
5794                break;
5795            }
5796            case OS.HDN_ITEMDBLCLICKW:
5797            case OS.HDN_ITEMDBLCLICKA: {
5798                NMHEADER phdn = new NMHEADER ();
5799                OS.MoveMemory (phdn, lParam, NMHEADER.sizeof);
5800                TableColumn column = columns [phdn.iItem];
5801                if (column != null) {
5802                    column.postEvent (SWT.DefaultSelection);
5803                }
5804                break;
5805            }
5806        }
5807    }
5808    LRESULT result = super.wmNotify (hdr, wParam, lParam);
5809    if (result != null) return result;
5810    switch (hdr.code) {
5811        case OS.TTN_GETDISPINFOA:
5812        case OS.TTN_GETDISPINFOW: {
5813            tipRequested = true;
5814            int code = callWindowProc (handle, OS.WM_NOTIFY, wParam, lParam);
5815            tipRequested = false;
5816            return new LRESULT (code);
5817        }
5818    }
5819    return result;
5820}
5821
5822LRESULT wmNotifyChild (NMHDR hdr, int wParam, int lParam) {
5823    switch (hdr.code) {
5824        case OS.LVN_ODFINDITEMA:
5825        case OS.LVN_ODFINDITEMW: {
5826            if ((style & SWT.VIRTUAL) != 0) return new LRESULT (-1);
5827            break;
5828        }
5829        case OS.LVN_ODSTATECHANGED: {
5830            if ((style & SWT.VIRTUAL) != 0) {
5831                if (!ignoreSelect) {
5832                    NMLVODSTATECHANGE lpStateChange = new NMLVODSTATECHANGE ();
5833                    OS.MoveMemory (lpStateChange, lParam, NMLVODSTATECHANGE.sizeof);
5834                    boolean oldSelected = (lpStateChange.uOldState & OS.LVIS_SELECTED) != 0;
5835                    boolean newSelected = (lpStateChange.uNewState & OS.LVIS_SELECTED) != 0;
5836                    if (oldSelected != newSelected) wasSelected = true;
5837                }
5838            }
5839            break;
5840        }
5841        case OS.LVN_GETDISPINFOA:
5842        case OS.LVN_GETDISPINFOW: {
5843// if (drawCount != 0 || !OS.IsWindowVisible (handle)) break;
5844
NMLVDISPINFO plvfi = new NMLVDISPINFO ();
5845            OS.MoveMemory (plvfi, lParam, NMLVDISPINFO.sizeof);
5846            
5847            /*
5848            * When an item is being deleted from a virtual table, do not
5849            * allow the application to provide data for a new item that
5850            * becomes visible until the item has been removed from the
5851            * items array. Because arbitrary application code can run
5852            * during the callback, the items array might be accessed
5853            * in an inconsistent state. Rather than answering the data
5854            * right away, queue a redraw for later.
5855            */

5856            if ((style & SWT.VIRTUAL) != 0) {
5857                if (ignoreShrink) {
5858                    OS.SendMessage (handle, OS.LVM_REDRAWITEMS, plvfi.iItem, plvfi.iItem);
5859                    break;
5860                }
5861            }
5862
5863            /*
5864            * Feature in Windows. When a new table item is inserted
5865            * using LVM_INSERTITEM in a table that is transparent
5866            * (ie. LVM_SETBKCOLOR has been called with CLR_NONE),
5867            * TVM_INSERTITEM calls LVN_GETDISPINFO before the item
5868            * has been added to the array. The fix is to check for
5869            * null.
5870            */

5871            TableItem item = _getItem (plvfi.iItem);
5872            if (item == null) break;
5873            
5874            /*
5875            * The cached flag is used by both virtual and non-virtual
5876            * tables to indicate that Windows has asked at least once
5877            * for a table item.
5878            */

5879            if (!item.cached) {
5880                if ((style & SWT.VIRTUAL) != 0) {
5881                    lastIndexOf = plvfi.iItem;
5882                    if (!checkData (item, lastIndexOf, false)) break;
5883                    TableItem newItem = fixScrollWidth ? null : item;
5884                    if (setScrollWidth (newItem, true)) {
5885                        OS.InvalidateRect (handle, null, true);
5886                    }
5887                }
5888                item.cached = true;
5889            }
5890            if ((plvfi.mask & OS.LVIF_TEXT) != 0) {
5891                String JavaDoc string = null;
5892                if (plvfi.iSubItem == 0) {
5893                    string = item.text;
5894                } else {
5895                    String JavaDoc [] strings = item.strings;
5896                    if (strings != null) string = strings [plvfi.iSubItem];
5897                }
5898                if (string != null) {
5899                    /*
5900                    * Bug in Windows. When pszText points to a zero length
5901                    * NULL terminated string, Windows correctly draws the
5902                    * empty string but the cache of the bounds for the item
5903                    * is not reset. This means that when the text for the
5904                    * item is set and then reset to an empty string, the
5905                    * selection draws using the bounds of the previous text.
5906                    * The fix is to use a space rather than an empty string
5907                    * when anything but a tool tip is requested (to avoid
5908                    * a tool tip that is a single space).
5909                    *
5910                    * NOTE: This is only a problem for items in the first
5911                    * column. Assigning NULL to other columns stops Windows
5912                    * from drawing the selection when LVS_EX_FULLROWSELECT
5913                    * is set.
5914                    */

5915                    int length = Math.min (string.length (), plvfi.cchTextMax - 1);
5916                    if (!tipRequested && plvfi.iSubItem == 0 && length == 0) {
5917                        string = " "; //$NON-NLS-1$
5918
length = 1;
5919                    }
5920                    char [] buffer = display.tableBuffer;
5921                    if (buffer == null || plvfi.cchTextMax > buffer.length) {
5922                        buffer = display.tableBuffer = new char [plvfi.cchTextMax];
5923                    }
5924                    string.getChars (0, length, buffer, 0);
5925                    buffer [length++] = 0;
5926                    if (OS.IsUnicode) {
5927                        OS.MoveMemory (plvfi.pszText, buffer, length * 2);
5928                    } else {
5929                        OS.WideCharToMultiByte (getCodePage (), 0, buffer, length, plvfi.pszText, plvfi.cchTextMax, null, null);
5930                        OS.MoveMemory (plvfi.pszText + plvfi.cchTextMax - 1, new byte [1], 1);
5931                    }
5932                }
5933            }
5934            boolean move = false;
5935            if ((plvfi.mask & OS.LVIF_IMAGE) != 0) {
5936                Image image = null;
5937                if (plvfi.iSubItem == 0) {
5938                    image = item.image;
5939                } else {
5940                    Image [] images = item.images;
5941                    if (images != null) image = images [plvfi.iSubItem];
5942                }
5943                if (image != null) {
5944                    plvfi.iImage = imageIndex (image, plvfi.iSubItem);
5945                    move = true;
5946                }
5947            }
5948            if ((plvfi.mask & OS.LVIF_STATE) != 0) {
5949                if (plvfi.iSubItem == 0) {
5950                    int state = 1;
5951                    if (item.checked) state++;
5952                    if (item.grayed) state +=2;
5953                    plvfi.state = state << 12;
5954                    plvfi.stateMask = OS.LVIS_STATEIMAGEMASK;
5955                    move = true;
5956                }
5957            }
5958            if ((plvfi.mask & OS.LVIF_INDENT) != 0) {
5959                if (plvfi.iSubItem == 0) {
5960                    plvfi.iIndent = item.imageIndent;
5961                    move = true;
5962                }
5963            }
5964            if (move) OS.MoveMemory (lParam, plvfi, NMLVDISPINFO.sizeof);
5965            break;
5966        }
5967        case OS.NM_CUSTOMDRAW: {
5968            int hwndHeader = OS.SendMessage (handle, OS.LVM_GETHEADER, 0, 0);
5969            if (hdr.hwndFrom == hwndHeader) break;
5970            if (!customDraw && findImageControl () == null) {
5971                /*
5972                * Feature in Windows. When the table is disabled, it draws
5973                * with a gray background but does not gray the text. The fix
5974                * is to explicitly gray the text using Custom Draw.
5975                */

5976                if (OS.IsWindowEnabled (handle)) {
5977                    /*
5978                    * Feature in Windows. On Vista using the explorer theme,
5979                    * Windows draws a vertical line to separate columns. When
5980                    * there is only a single column, the line looks strange.
5981                    * The fix is to draw the background using custom draw.
5982                    */

5983                    if (!explorerTheme || columnCount != 0) break;
5984                }
5985            }
5986            NMLVCUSTOMDRAW nmcd = new NMLVCUSTOMDRAW ();
5987            OS.MoveMemory (nmcd, lParam, NMLVCUSTOMDRAW.sizeof);
5988            switch (nmcd.dwDrawStage) {
5989                case OS.CDDS_PREPAINT: return CDDS_PREPAINT (nmcd, wParam, lParam);
5990                case OS.CDDS_ITEMPREPAINT: return CDDS_ITEMPREPAINT (nmcd, wParam, lParam);
5991                case OS.CDDS_ITEMPOSTPAINT: return CDDS_ITEMPOSTPAINT (nmcd, wParam, lParam);
5992                case OS.CDDS_SUBITEMPREPAINT: return CDDS_SUBITEMPREPAINT (nmcd, wParam, lParam);
5993                case OS.CDDS_SUBITEMPOSTPAINT: return CDDS_SUBITEMPOSTPAINT (nmcd, wParam, lParam);
5994                case OS.CDDS_POSTPAINT: return CDDS_POSTPAINT (nmcd, wParam, lParam);
5995            }
5996            break;
5997        }
5998        case OS.LVN_MARQUEEBEGIN: {
5999            if ((style & SWT.SINGLE) != 0) return LRESULT.ONE;
6000            if (hooks (SWT.MouseDown) || hooks (SWT.MouseUp)) {
6001                return LRESULT.ONE;
6002            }
6003            break;
6004        }
6005        case OS.LVN_BEGINDRAG:
6006        case OS.LVN_BEGINRDRAG: {
6007            dragStarted = true;
6008            if (hdr.code == OS.LVN_BEGINDRAG) {
6009                int pos = OS.GetMessagePos ();
6010                POINT pt = new POINT ();
6011                pt.x = (short) (pos & 0xFFFF);
6012                pt.y = (short) (pos >> 16);
6013                OS.ScreenToClient (handle, pt);
6014                sendDragEvent (1, pt.x, pt.y);
6015            }
6016            break;
6017        }
6018        case OS.LVN_COLUMNCLICK: {
6019            NMLISTVIEW pnmlv = new NMLISTVIEW ();
6020            OS.MoveMemory(pnmlv, lParam, NMLISTVIEW.sizeof);
6021            TableColumn column = columns [pnmlv.iSubItem];
6022            if (column != null) {
6023                column.postEvent (SWT.Selection);
6024            }
6025            break;
6026        }
6027        case OS.LVN_ITEMACTIVATE: {
6028            if (ignoreActivate) break;
6029            NMLISTVIEW pnmlv = new NMLISTVIEW ();
6030            OS.MoveMemory(pnmlv, lParam, NMLISTVIEW.sizeof);
6031            if (pnmlv.iItem != -1) {
6032                Event event = new Event ();
6033                event.item = _getItem (pnmlv.iItem);
6034                postEvent (SWT.DefaultSelection, event);
6035            }
6036            break;
6037        }
6038        case OS.LVN_ITEMCHANGED: {
6039            if (!ignoreSelect) {
6040                NMLISTVIEW pnmlv = new NMLISTVIEW ();
6041                OS.MoveMemory (pnmlv, lParam, NMLISTVIEW.sizeof);
6042                if ((pnmlv.uChanged & OS.LVIF_STATE) != 0) {
6043                    if (pnmlv.iItem == -1) {
6044                        wasSelected = true;
6045                    } else {
6046                        boolean oldSelected = (pnmlv.uOldState & OS.LVIS_SELECTED) != 0;
6047                        boolean newSelected = (pnmlv.uNewState & OS.LVIS_SELECTED) != 0;
6048                        if (oldSelected != newSelected) wasSelected = true;
6049                    }
6050                }
6051            }
6052            if (hooks (SWT.EraseItem) || hooks (SWT.PaintItem)) {
6053                int hwndHeader = OS.SendMessage (handle, OS.LVM_GETHEADER, 0, 0);
6054                int count = OS.SendMessage (hwndHeader, OS.HDM_GETITEMCOUNT, 0, 0);
6055                if (count != 0) {
6056                    forceResize ();
6057                    RECT rect = new RECT ();
6058                    OS.GetClientRect (handle, rect);
6059                    NMLISTVIEW pnmlv = new NMLISTVIEW ();
6060                    OS.MoveMemory (pnmlv, lParam, NMLISTVIEW.sizeof);
6061                    if (pnmlv.iItem != -1) {
6062                        RECT itemRect = new RECT ();
6063                        itemRect.left = OS.LVIR_BOUNDS;
6064                        ignoreCustomDraw = true;
6065                        OS.SendMessage (handle, OS. LVM_GETITEMRECT, pnmlv.iItem, itemRect);
6066                        ignoreCustomDraw = false;
6067                        RECT headerRect = new RECT ();
6068                        int index = OS.SendMessage (hwndHeader, OS.HDM_ORDERTOINDEX, count - 1, 0);
6069                        OS.SendMessage (hwndHeader, OS.HDM_GETITEMRECT, index, headerRect);
6070                        OS.MapWindowPoints (hwndHeader, handle, headerRect, 2);
6071                        rect.left = headerRect.right;
6072                        rect.top = itemRect.top;
6073                        rect.bottom = itemRect.bottom;
6074                        OS.InvalidateRect (handle, rect, true);
6075                    }
6076                }
6077            }
6078            break;
6079        }
6080        case OS.NM_RECOGNIZEGESTURE:
6081            /*
6082            * Feature on Pocket PC. The tree and table controls detect the tap
6083            * and hold gesture by default. They send a GN_CONTEXTMENU message to show
6084            * the popup menu. This default behaviour is unwanted on Pocket PC 2002
6085            * when no menu has been set, as it still draws a red circle. The fix
6086            * is to disable this default behaviour when no menu is set by returning
6087            * TRUE when receiving the Pocket PC 2002 specific NM_RECOGNIZEGESTURE
6088            * message.
6089            */

6090            if (OS.IsPPC) {
6091                boolean hasMenu = menu != null && !menu.isDisposed ();
6092                if (!hasMenu && !hooks (SWT.MenuDetect)) return LRESULT.ONE;
6093            }
6094            break;
6095        case OS.GN_CONTEXTMENU:
6096            if (OS.IsPPC) {
6097                boolean hasMenu = menu != null && !menu.isDisposed ();
6098                if (hasMenu || hooks (SWT.MenuDetect)) {
6099                    NMRGINFO nmrg = new NMRGINFO ();
6100                    OS.MoveMemory (nmrg, lParam, NMRGINFO.sizeof);
6101                    showMenu (nmrg.x, nmrg.y);
6102                    return LRESULT.ONE;
6103                }
6104            }
6105            break;
6106    }
6107    return super.wmNotifyChild (hdr, wParam, lParam);
6108}
6109
6110}
6111
Popular Tags