/*******************************************************************************
* Copyright (c) 2000, 2018 IBM Corporation and others.
*
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* IBM Corporation - initial API and implementation
* Roland Oldenburg
* The item children that may be added to instances of this class
* must be of type
* Style
* Here is an example of using a
* Note that although this class is a subclass of
* Note: Only one of the styles SINGLE, and MULTI may be specified.
*
* IMPORTANT: This class is not intended to be subclassed.
*
* The style value is either one of the style constants defined in
* class
* When
* Specifically, the indices of the returned array represent
* the current visual order of the items, and the contents
* of the array represent the creation order of the items.
*
* Note: This is not the actual structure used by the receiver
* to maintain its list of items, so modifying the array will
* not affect the receiver.
*
* Note: This is not the actual structure used by the receiver
* to maintain its list of items, so modifying the array will
* not affect the receiver.
*
* If one of the receiver's ancestors is not visible or some
* other condition makes the receiver not visible, this method
* may still indicate that it is considered visible even though
* it may not actually be showing.
*
* The item that is returned represents an item that could be selected by the user.
* For example, if selection only occurs in items in the first column, then null is
* returned if the point is outside of the item.
* Note that the SWT.FULL_SELECTION style hint, which specifies the selection policy,
* determines the extent of the selection.
*
* Note: This is not the actual structure used by the receiver
* to maintain its list of items, so modifying the array will
* not affect the receiver.
*
* If one of the receiver's ancestors is not visible or some
* other condition makes the receiver not visible, this method
* may still indicate that it is considered visible even though
* it may not actually be showing.
*
* Note: This is not the actual structure used by the receiver
* to maintain its selection, so modifying the array will
* not affect the receiver.
*
* Note: This is not the actual structure used by the receiver
* to maintain its selection, so modifying the array will
* not affect the receiver.
*
* If the item at a given index is not selected, it is selected.
* If the item at a given index was already selected, it remains selected.
* Indices that are out of range and duplicate indices are ignored.
* If the receiver is single-select and multiple indices are specified,
* then all indices are ignored.
*
* If an item in the given range is not selected, it is selected.
* If an item in the given range was already selected, it remains selected.
* Indices that are out of range are ignored and no items will be selected
* if start is greater than end.
* If the receiver is single-select and there is more than one item in the
* given range, then all indices are ignored.
*
* If the receiver is single-select, do nothing.
* TableItem
.
* VIRTUAL
is used to create a Table
whose
* TableItem
s are to be populated by the client on an on-demand basis
* instead of up-front. This can provide significant performance improvements for
* tables that are very large or for which TableItem
population is
* expensive (for example, retrieving values from an external source).
* Table
with style VIRTUAL
:
*
* final Table table = new Table (parent, SWT.VIRTUAL | SWT.BORDER);
* table.setItemCount (1000000);
* table.addListener (SWT.SetData, new Listener () {
* public void handleEvent (Event event) {
* TableItem item = (TableItem) event.item;
* int index = table.indexOf (item);
* item.setText ("Item " + index);
* System.out.println (item.getText ());
* }
* });
*
Composite
,
* it does not normally make sense to add Control
children to
* it, or set a layout on it, unless implementing something like a cell
* editor.
*
*
* SWT
which is applicable to instances of this
* class, or must be built by bitwise OR'ing together
* (that is, using the int
"|" operator) two or more
* of those SWT
style constants. The class description
* lists the style constants that are applicable to the class.
* Style bits are also inherited from superclasses.
*
*
* @exception SWTException
*
*
* @see SWT#SINGLE
* @see SWT#MULTI
* @see SWT#CHECK
* @see SWT#FULL_SELECTION
* @see SWT#HIDE_SELECTION
* @see SWT#VIRTUAL
* @see SWT#NO_SCROLL
* @see Widget#checkSubclass
* @see Widget#getStyle
*/
public Table (Composite parent, int style) {
super (parent, checkStyle (style));
}
@Override
void _addListener (int eventType, Listener listener) {
super._addListener (eventType, listener);
switch (eventType) {
case SWT.MeasureItem:
case SWT.EraseItem:
case SWT.PaintItem:
setCustomDraw (true);
setBackgroundTransparent (true);
break;
}
}
boolean _checkGrow (int count) {
//TODO - code could be shared but it would mix keyed and non-keyed logic
if (keys == null) {
if (count == items.length) {
/*
* Grow the array faster when redraw is off or the
* table is not visible. When the table is painted,
* the items array is resized to be smaller to reduce
* memory usage.
*/
boolean small = getDrawing () && OS.IsWindowVisible (handle);
int length = small ? items.length + 4 : Math.max (4, items.length * 3 / 2);
TableItem [] newItems = new TableItem [length];
System.arraycopy (items, 0, newItems, 0, items.length);
items = newItems;
}
} else {
//TODO - don't shrink when count is very small (ie. 2 or 4 elements)?
//TODO - why? if setItemCount(1000000) is used after a shrink, then we won't compress
//TODO - get rid of ignoreShrink?
if (!ignoreShrink && keyCount > count / 2) {
boolean small = getDrawing () && OS.IsWindowVisible (handle);
int length = small ? count + 4 : Math.max (4, count * 3 / 2);
TableItem [] newItems = new TableItem [length];
for (int i=0; iwidgetSelected
is called, the item field of the event object is valid.
* If the receiver has the SWT.CHECK
style and the check selection changes,
* the event object detail field contains the value SWT.CHECK
.
* widgetDefaultSelected
is typically called when an item is double-clicked.
* The item field of the event object is valid for default selection, but the detail field is not used.
*
*
* @exception SWTException
*
*
* @see SelectionListener
* @see #removeSelectionListener
* @see SelectionEvent
*/
public void addSelectionListener (SelectionListener listener) {
checkWidget ();
if (listener == null) error (SWT.ERROR_NULL_ARGUMENT);
TypedListener typedListener = new TypedListener (listener);
addListener (SWT.Selection,typedListener);
addListener (SWT.DefaultSelection,typedListener);
}
@Override
long callWindowProc (long hwnd, int msg, long wParam, long lParam) {
return callWindowProc (hwnd, msg, wParam, lParam, false);
}
long callWindowProc (long hwnd, int msg, long wParam, long lParam, boolean forceSelect) {
if (handle == 0) return 0;
if (hwndHeader != 0 && hwnd == hwndHeader) {
return OS.CallWindowProc (HeaderProc, hwnd, msg, wParam, lParam);
}
int topIndex = 0;
boolean checkSelection = false, checkActivate = false, redraw = false;
switch (msg) {
/* Keyboard messages */
/*
* Feature in Windows. Windows sends LVN_ITEMACTIVATE from WM_KEYDOWN
* instead of WM_CHAR. This means that application code that expects
* to consume the key press and therefore avoid a SWT.DefaultSelection
* event will fail. The fix is to ignore LVN_ITEMACTIVATE when it is
* caused by WM_KEYDOWN and send SWT.DefaultSelection from WM_CHAR.
*/
case OS.WM_KEYDOWN:
checkActivate = true;
//FALL THROUGH
case OS.WM_CHAR:
case OS.WM_IME_CHAR:
case OS.WM_KEYUP:
case OS.WM_SYSCHAR:
case OS.WM_SYSKEYDOWN:
case OS.WM_SYSKEYUP:
//FALL THROUGH
/* Scroll messages */
case OS.WM_HSCROLL:
case OS.WM_VSCROLL:
//FALL THROUGH
/* Resize messages */
case OS.WM_WINDOWPOSCHANGED:
redraw = findImageControl () != null && getDrawing () && OS.IsWindowVisible (handle);
if (redraw) {
/*
* Feature in Windows. When LVM_SETBKCOLOR is used with CLR_NONE
* to make the background of the table transparent, drawing becomes
* slow. The fix is to temporarily clear CLR_NONE when redraw is
* turned off.
*/
OS.DefWindowProc (handle, OS.WM_SETREDRAW, 0, 0);
OS.SendMessage (handle, OS.LVM_SETBKCOLOR, 0, 0xFFFFFF);
}
//FALL THROUGH
/* Mouse messages */
case OS.WM_LBUTTONDBLCLK:
case OS.WM_LBUTTONDOWN:
case OS.WM_LBUTTONUP:
case OS.WM_MBUTTONDBLCLK:
case OS.WM_MBUTTONDOWN:
case OS.WM_MBUTTONUP:
case OS.WM_MOUSEHOVER:
case OS.WM_MOUSELEAVE:
case OS.WM_MOUSEMOVE:
case OS.WM_MOUSEWHEEL:
case OS.WM_RBUTTONDBLCLK:
case OS.WM_RBUTTONDOWN:
case OS.WM_RBUTTONUP:
case OS.WM_XBUTTONDBLCLK:
case OS.WM_XBUTTONDOWN:
case OS.WM_XBUTTONUP:
checkSelection = true;
//FALL THROUGH
/* Other messages */
case OS.WM_SETFONT:
case OS.WM_TIMER: {
if (findImageControl () != null) {
topIndex = (int)OS.SendMessage (handle, OS.LVM_GETTOPINDEX, 0, 0);
}
}
}
boolean oldSelected = wasSelected;
if (checkSelection) wasSelected = false;
if (checkActivate) ignoreActivate = true;
/*
* Bug in Windows. For some reason, when the WS_EX_COMPOSITED
* style is set in a parent of a table and the header is visible,
* Windows issues an endless stream of WM_PAINT messages. The
* fix is to call BeginPaint() and EndPaint() outside of WM_PAINT
* and pass the paint HDC in to the window proc.
*/
boolean fixPaint = false;
if (msg == OS.WM_PAINT) {
int bits0 = OS.GetWindowLong (handle, OS.GWL_STYLE);
if ((bits0 & OS.LVS_NOCOLUMNHEADER) == 0) {
long hwndParent = OS.GetParent (handle), hwndOwner = 0;
while (hwndParent != 0) {
int bits1 = OS.GetWindowLong (hwndParent, OS.GWL_EXSTYLE);
if ((bits1 & OS.WS_EX_COMPOSITED) != 0) {
fixPaint = true;
break;
}
hwndOwner = OS.GetWindow (hwndParent, OS.GW_OWNER);
if (hwndOwner != 0) break;
hwndParent = OS.GetParent (hwndParent);
}
}
}
/* Remove the scroll bars that Windows keeps automatically adding */
boolean fixScroll = false;
if ((style & SWT.H_SCROLL) == 0 || (style & SWT.V_SCROLL) == 0) {
switch (msg) {
case OS.WM_PAINT:
case OS.WM_NCPAINT:
case OS.WM_WINDOWPOSCHANGING: {
int bits = OS.GetWindowLong (hwnd, OS.GWL_STYLE);
if ((style & SWT.H_SCROLL) == 0 && (bits & OS.WS_HSCROLL) != 0) {
fixScroll = true;
bits &= ~OS.WS_HSCROLL;
}
if ((style & SWT.V_SCROLL) == 0 && (bits & OS.WS_VSCROLL) != 0) {
fixScroll = true;
bits &= ~OS.WS_VSCROLL;
}
if (fixScroll) OS.SetWindowLong (handle, OS.GWL_STYLE, bits);
}
}
}
long code = 0;
if (fixPaint) {
PAINTSTRUCT ps = new PAINTSTRUCT ();
long hDC = OS.BeginPaint (hwnd, ps);
code = OS.CallWindowProc (TableProc, hwnd, OS.WM_PAINT, hDC, lParam);
OS.EndPaint (hwnd, ps);
} else {
code = OS.CallWindowProc (TableProc, hwnd, msg, wParam, lParam);
}
if (fixScroll) {
int flags = OS.RDW_FRAME | OS.RDW_INVALIDATE;
OS.RedrawWindow (handle, null, 0, flags);
}
if (checkActivate) ignoreActivate = false;
if (checkSelection) {
if (wasSelected || forceSelect) {
Event event = new Event ();
int index = (int)OS.SendMessage (handle, OS.LVM_GETNEXTITEM, -1, OS.LVNI_FOCUSED);
if (index != -1) event.item = _getItem (index);
sendSelectionEvent (SWT.Selection, event, false);
}
wasSelected = oldSelected;
}
switch (msg) {
/* Keyboard messages */
case OS.WM_KEYDOWN:
case OS.WM_CHAR:
case OS.WM_IME_CHAR:
case OS.WM_KEYUP:
case OS.WM_SYSCHAR:
case OS.WM_SYSKEYDOWN:
case OS.WM_SYSKEYUP:
//FALL THROUGH
/* Scroll messages */
case OS.WM_HSCROLL:
case OS.WM_VSCROLL:
//FALL THROUGH
/* Resize messages */
case OS.WM_WINDOWPOSCHANGED:
if (redraw) {
OS.SendMessage (handle, OS.LVM_SETBKCOLOR, 0, OS.CLR_NONE);
OS.DefWindowProc (handle, OS.WM_SETREDRAW, 1, 0);
OS.InvalidateRect (handle, null, true);
long hwndHeader = OS.SendMessage (handle, OS.LVM_GETHEADER, 0, 0);
if (hwndHeader != 0) OS.InvalidateRect (hwndHeader, null, true);
}
//FALL THROUGH
/* Mouse messages */
case OS.WM_LBUTTONDBLCLK:
case OS.WM_LBUTTONDOWN:
case OS.WM_LBUTTONUP:
case OS.WM_MBUTTONDBLCLK:
case OS.WM_MBUTTONDOWN:
case OS.WM_MBUTTONUP:
case OS.WM_MOUSEHOVER:
case OS.WM_MOUSELEAVE:
case OS.WM_MOUSEMOVE:
case OS.WM_MOUSEWHEEL:
case OS.WM_RBUTTONDBLCLK:
case OS.WM_RBUTTONDOWN:
case OS.WM_RBUTTONUP:
case OS.WM_XBUTTONDBLCLK:
case OS.WM_XBUTTONDOWN:
case OS.WM_XBUTTONUP:
//FALL THROUGH
/* Other messages */
case OS.WM_SETFONT:
case OS.WM_TIMER: {
if (findImageControl () != null) {
if (topIndex != OS.SendMessage (handle, OS.LVM_GETTOPINDEX, 0, 0)) {
OS.InvalidateRect (handle, null, true);
}
}
break;
}
case OS.WM_PAINT:
painted = true;
break;
}
return code;
}
static int checkStyle (int style) {
/*
* Feature in Windows. Even when WS_HSCROLL or
* WS_VSCROLL is not specified, Windows creates
* trees and tables with scroll bars. The fix
* is to set H_SCROLL and V_SCROLL.
*
* NOTE: This code appears on all platforms so that
* applications have consistent scroll bar behavior.
*/
if ((style & SWT.NO_SCROLL) == 0) {
style |= SWT.H_SCROLL | SWT.V_SCROLL;
}
return checkBits (style, SWT.SINGLE, SWT.MULTI, 0, 0, 0, 0);
}
LRESULT CDDS_ITEMPOSTPAINT (NMLVCUSTOMDRAW nmcd, long wParam, long lParam) {
long hDC = nmcd.hdc;
if (explorerTheme && !ignoreCustomDraw) {
hotIndex = -1;
if (hooks (SWT.EraseItem) && nmcd.left != nmcd.right) {
OS.RestoreDC (hDC, -1);
}
}
/*
* Bug in Windows. When the table has the extended style
* LVS_EX_FULLROWSELECT and LVM_SETBKCOLOR is used with
* CLR_NONE to make the table transparent, Windows fills
* a black rectangle around any column that contains an
* image. The fix is clear LVS_EX_FULLROWSELECT during
* custom draw.
*
* NOTE: Since CDIS_FOCUS is cleared during custom draw,
* it is necessary to draw the focus rectangle after the
* item has been drawn.
*/
if (!ignoreCustomDraw && !ignoreDrawFocus && nmcd.left != nmcd.right) {
if (OS.IsWindowVisible (handle) && OS.IsWindowEnabled (handle)) {
if (!explorerTheme && (style & SWT.FULL_SELECTION) != 0) {
if ((int)OS.SendMessage (handle, OS.LVM_GETBKCOLOR, 0, 0) == OS.CLR_NONE) {
int dwExStyle = (int)OS.SendMessage (handle, OS.LVM_GETEXTENDEDLISTVIEWSTYLE, 0, 0);
if ((dwExStyle & OS.LVS_EX_FULLROWSELECT) == 0) {
// if ((nmcd.uItemState & OS.CDIS_FOCUS) != 0) {
if (OS.SendMessage (handle, OS.LVM_GETNEXTITEM, -1, OS.LVNI_FOCUSED) == nmcd.dwItemSpec) {
if (handle == OS.GetFocus ()) {
int uiState = (int)OS.SendMessage (handle, OS.WM_QUERYUISTATE, 0, 0);
if ((uiState & OS.UISF_HIDEFOCUS) == 0) {
RECT rect = new RECT ();
rect.left = OS.LVIR_BOUNDS;
boolean oldIgnore = ignoreCustomDraw;
ignoreCustomDraw = true;
OS.SendMessage (handle, OS. LVM_GETITEMRECT, nmcd.dwItemSpec, rect);
long hwndHeader = OS.SendMessage (handle, OS.LVM_GETHEADER, 0, 0);
int index = (int)OS.SendMessage (hwndHeader, OS.HDM_ORDERTOINDEX, 0, 0);
RECT itemRect = new RECT ();
if (index == 0) {
itemRect.left = OS.LVIR_LABEL;
OS.SendMessage (handle, OS. LVM_GETITEMRECT, index, itemRect);
} else {
itemRect.top = index;
itemRect.left = OS.LVIR_ICON;
OS.SendMessage (handle, OS. LVM_GETSUBITEMRECT, nmcd.dwItemSpec, itemRect);
}
ignoreCustomDraw = oldIgnore;
rect.left = itemRect.left;
OS.DrawFocusRect (nmcd.hdc, rect);
}
}
}
}
}
}
}
}
return null;
}
LRESULT CDDS_ITEMPREPAINT (NMLVCUSTOMDRAW nmcd, long wParam, long lParam) {
/*
* Bug in Windows. When the table has the extended style
* LVS_EX_FULLROWSELECT and LVM_SETBKCOLOR is used with
* CLR_NONE to make the table transparent, Windows fills
* a black rectangle around any column that contains an
* image. The fix is clear LVS_EX_FULLROWSELECT during
* custom draw.
*
* NOTE: It is also necessary to clear CDIS_FOCUS to stop
* the table from drawing the focus rectangle around the
* first item instead of the full row.
*/
if (!ignoreCustomDraw) {
if (OS.IsWindowVisible (handle) && OS.IsWindowEnabled (handle)) {
if (!explorerTheme && (style & SWT.FULL_SELECTION) != 0) {
if ((int)OS.SendMessage (handle, OS.LVM_GETBKCOLOR, 0, 0) == OS.CLR_NONE) {
int dwExStyle = (int)OS.SendMessage (handle, OS.LVM_GETEXTENDEDLISTVIEWSTYLE, 0, 0);
if ((dwExStyle & OS.LVS_EX_FULLROWSELECT) == 0) {
if ((nmcd.uItemState & OS.CDIS_FOCUS) != 0) {
nmcd.uItemState &= ~OS.CDIS_FOCUS;
OS.MoveMemory (lParam, nmcd, NMLVCUSTOMDRAW.sizeof);
}
}
}
}
}
}
if (explorerTheme && !ignoreCustomDraw) {
hotIndex = (nmcd.uItemState & OS.CDIS_HOT) != 0 ? (int)nmcd.dwItemSpec : -1;
if (hooks (SWT.EraseItem) && nmcd.left != nmcd.right) {
OS.SaveDC (nmcd.hdc);
long hrgn = OS.CreateRectRgn (0, 0, 0, 0);
OS.SelectClipRgn (nmcd.hdc, hrgn);
OS.DeleteObject (hrgn);
}
}
return new LRESULT (OS.CDRF_NOTIFYSUBITEMDRAW | OS.CDRF_NOTIFYPOSTPAINT);
}
LRESULT CDDS_POSTPAINT (NMLVCUSTOMDRAW nmcd, long wParam, long lParam) {
if (ignoreCustomDraw) return null;
/*
* Bug in Windows. When the table has the extended style
* LVS_EX_FULLROWSELECT and LVM_SETBKCOLOR is used with
* CLR_NONE to make the table transparent, Windows fills
* a black rectangle around any column that contains an
* image. The fix is clear LVS_EX_FULLROWSELECT during
* custom draw.
*/
if (--customCount == 0 && OS.IsWindowVisible (handle)) {
if (!explorerTheme && (style & SWT.FULL_SELECTION) != 0) {
if ((int)OS.SendMessage (handle, OS.LVM_GETBKCOLOR, 0, 0) == OS.CLR_NONE) {
int dwExStyle = (int)OS.SendMessage (handle, OS.LVM_GETEXTENDEDLISTVIEWSTYLE, 0, 0);
if ((dwExStyle & OS.LVS_EX_FULLROWSELECT) == 0) {
int bits = OS.LVS_EX_FULLROWSELECT;
/*
* Feature in Windows. When LVM_SETEXTENDEDLISTVIEWSTYLE is
* used to set or clear the extended style bits and the table
* has a tooltip, the tooltip is hidden. The fix is to clear
* the tooltip before setting the bits and then reset it.
*/
long hwndToolTip = OS.SendMessage (handle, OS.LVM_SETTOOLTIPS, 0, 0);
long rgn = OS.CreateRectRgn (0, 0, 0, 0);
int result = OS.GetUpdateRgn (handle, rgn, true);
OS.SendMessage (handle, OS.LVM_SETEXTENDEDLISTVIEWSTYLE, bits, bits);
OS.ValidateRect (handle, null);
if (result != OS.NULLREGION) OS.InvalidateRgn (handle, rgn, true);
OS.DeleteObject (rgn);
/*
* Bug in Windows. Despite the documentation, LVM_SETTOOLTIPS
* uses WPARAM instead of LPARAM for the new tooltip The fix
* is to put the tooltip in both parameters.
*/
hwndToolTip = OS.SendMessage (handle, OS.LVM_SETTOOLTIPS, hwndToolTip, hwndToolTip);
}
}
}
}
return null;
}
LRESULT CDDS_PREPAINT (NMLVCUSTOMDRAW nmcd, long wParam, long lParam) {
if (ignoreCustomDraw) {
return new LRESULT (OS.CDRF_NOTIFYITEMDRAW | OS.CDRF_NOTIFYPOSTPAINT);
}
/*
* Bug in Windows. When the table has the extended style
* LVS_EX_FULLROWSELECT and LVM_SETBKCOLOR is used with
* CLR_NONE to make the table transparent, Windows fills
* a black rectangle around any column that contains an
* image. The fix is clear LVS_EX_FULLROWSELECT during
* custom draw.
*/
if (customCount++ == 0 && OS.IsWindowVisible (handle)) {
if (!explorerTheme && (style & SWT.FULL_SELECTION) != 0) {
if ((int)OS.SendMessage (handle, OS.LVM_GETBKCOLOR, 0, 0) == OS.CLR_NONE) {
int dwExStyle = (int)OS.SendMessage (handle, OS.LVM_GETEXTENDEDLISTVIEWSTYLE, 0, 0);
if ((dwExStyle & OS.LVS_EX_FULLROWSELECT) != 0) {
int bits = OS.LVS_EX_FULLROWSELECT;
/*
* Feature in Windows. When LVM_SETEXTENDEDLISTVIEWSTYLE is
* used to set or clear the extended style bits and the table
* has a tooltip, the tooltip is hidden. The fix is to clear
* the tooltip before setting the bits and then reset it.
*/
long hwndToolTip = OS.SendMessage (handle, OS.LVM_SETTOOLTIPS, 0, 0);
long rgn = OS.CreateRectRgn (0, 0, 0, 0);
int result = OS.GetUpdateRgn (handle, rgn, true);
OS.SendMessage (handle, OS.LVM_SETEXTENDEDLISTVIEWSTYLE, bits, 0);
OS.ValidateRect (handle, null);
if (result != OS.NULLREGION) OS.InvalidateRgn (handle, rgn, true);
OS.DeleteObject (rgn);
/*
* Bug in Windows. Despite the documentation, LVM_SETTOOLTIPS
* uses WPARAM instead of LPARAM for the new tooltip The fix
* is to put the tooltip in both parameters.
*/
hwndToolTip = OS.SendMessage (handle, OS.LVM_SETTOOLTIPS, hwndToolTip, hwndToolTip);
}
}
}
}
if (OS.IsWindowVisible (handle)) {
/*
* Feature in Windows. On Vista using the explorer theme,
* Windows draws a vertical line to separate columns. When
* there is only a single column, the line looks strange.
* The fix is to draw the background using custom draw.
*/
RECT rect = new RECT ();
OS.SetRect (rect, nmcd.left, nmcd.top, nmcd.right, nmcd.bottom);
if (explorerTheme && columnCount == 0) {
long hDC = nmcd.hdc;
if (OS.IsWindowEnabled (handle) || findImageControl () != null) {
drawBackground (hDC, rect);
} else {
fillBackground (hDC, OS.GetSysColor (OS.COLOR_3DFACE), rect);
}
} else {
Control control = findBackgroundControl ();
if (control != null && control.backgroundImage != null) {
fillImageBackground (nmcd.hdc, control, rect, 0, 0);
} else {
final boolean enabled = OS.IsWindowEnabled (handle);
if (enabled && (int)OS.SendMessage (handle, OS.LVM_GETBKCOLOR, 0, 0) == OS.CLR_NONE || !enabled && hasCustomBackground()) {
if (control == null) control = this;
fillBackground (nmcd.hdc, control.getBackgroundPixel (), rect);
if (OS.IsAppThemed ()) {
if (sortColumn != null && sortDirection != SWT.NONE) {
int index = indexOf (sortColumn);
if (index != -1) {
parent.forceResize ();
int clrSortBk = getSortColumnPixel ();
RECT columnRect = new RECT (), headerRect = new RECT ();
OS.GetClientRect (handle, columnRect);
long hwndHeader = OS.SendMessage (handle, OS.LVM_GETHEADER, 0, 0);
if (OS.SendMessage (hwndHeader, OS.HDM_GETITEMRECT, index, headerRect) != 0) {
OS.MapWindowPoints (hwndHeader, handle, headerRect, 2);
columnRect.left = headerRect.left;
columnRect.right = headerRect.right;
if (OS.IntersectRect(columnRect, columnRect, rect)) {
fillBackground (nmcd.hdc, clrSortBk, columnRect);
}
}
}
}
}
}
}
}
}
return new LRESULT (OS.CDRF_NOTIFYITEMDRAW | OS.CDRF_NOTIFYPOSTPAINT);
}
LRESULT CDDS_SUBITEMPOSTPAINT (NMLVCUSTOMDRAW nmcd, long wParam, long lParam) {
if (ignoreCustomDraw) return null;
if (nmcd.left == nmcd.right) return new LRESULT (OS.CDRF_DODEFAULT);
long hDC = nmcd.hdc;
if (ignoreDrawForeground) OS.RestoreDC (hDC, -1);
if (OS.IsWindowVisible (handle)) {
/*
* Feature in Windows. When there is a sort column, the sort column
* color draws on top of the background color for an item. The fix
* is to clear the sort column in CDDS_SUBITEMPREPAINT, and reset it
* in CDDS_SUBITEMPOSTPAINT.
*
* Update region is saved and restored around LVM_SETSELECTEDCOLUMN
* to prevent infinite WM_PAINT on Vista.
*/
if ((int)OS.SendMessage (handle, OS.LVM_GETBKCOLOR, 0, 0) != OS.CLR_NONE) {
if ((sortDirection & (SWT.UP | SWT.DOWN)) != 0) {
if (sortColumn != null && !sortColumn.isDisposed ()) {
int oldColumn = (int)OS.SendMessage (handle, OS.LVM_GETSELECTEDCOLUMN, 0, 0);
if (oldColumn == -1) {
int newColumn = indexOf (sortColumn);
long rgn = OS.CreateRectRgn (0, 0, 0, 0);
int result = OS.GetUpdateRgn (handle, rgn, true);
OS.SendMessage (handle, OS.LVM_SETSELECTEDCOLUMN, newColumn, 0);
OS.ValidateRect (handle, null);
if (result != OS.NULLREGION) OS.InvalidateRgn (handle, rgn, true);
OS.DeleteObject (rgn);
}
}
}
}
if (hooks (SWT.PaintItem)) {
TableItem item = _getItem ((int)nmcd.dwItemSpec);
sendPaintItemEvent (item, nmcd);
//widget could be disposed at this point
}
if (!ignoreDrawFocus && focusRect != null) {
OS.SetTextColor (nmcd.hdc, 0);
OS.SetBkColor (nmcd.hdc, 0xFFFFFF);
OS.DrawFocusRect (nmcd.hdc, focusRect);
focusRect = null;
}
}
return null;
}
LRESULT CDDS_SUBITEMPREPAINT (NMLVCUSTOMDRAW nmcd, long wParam, long lParam) {
long hDC = nmcd.hdc;
if (explorerTheme && !ignoreCustomDraw && hooks (SWT.EraseItem) && (nmcd.left != nmcd.right)) {
OS.RestoreDC (hDC, -1);
}
/*
* Feature in Windows. When a new table item is inserted
* using LVM_INSERTITEM in a table that is transparent
* (ie. LVM_SETBKCOLOR has been called with CLR_NONE),
* TVM_INSERTITEM calls NM_CUSTOMDRAW before the new item
* has been added to the array. The fix is to check for
* null.
*
* NOTE: Force the item to be created if it does not exist.
*/
TableItem item = _getItem ((int)nmcd.dwItemSpec);
if (item == null || item.isDisposed ()) return null;
long hFont = item.fontHandle (nmcd.iSubItem);
if (hFont != -1) OS.SelectObject (hDC, hFont);
if (ignoreCustomDraw || (nmcd.left == nmcd.right)) {
return new LRESULT (hFont == -1 ? OS.CDRF_DODEFAULT : OS.CDRF_NEWFONT);
}
int code = OS.CDRF_DODEFAULT;
selectionForeground = -1;
ignoreDrawForeground = ignoreDrawSelection = ignoreDrawFocus = ignoreDrawBackground = false;
if (OS.IsWindowVisible (handle)) {
Event measureEvent = null;
if (hooks (SWT.MeasureItem)) {
measureEvent = sendMeasureItemEvent (item, (int)nmcd.dwItemSpec, nmcd.iSubItem, nmcd.hdc);
if (isDisposed () || item.isDisposed ()) return null;
}
if (hooks (SWT.EraseItem)) {
sendEraseItemEvent (item, nmcd, lParam, measureEvent);
if (isDisposed () || item.isDisposed ()) return null;
code |= OS.CDRF_NOTIFYPOSTPAINT;
}
if (ignoreDrawForeground || hooks (SWT.PaintItem)) code |= OS.CDRF_NOTIFYPOSTPAINT;
}
int clrText = item.cellForeground != null ? item.cellForeground [nmcd.iSubItem] : -1;
if (clrText == -1) clrText = item.foreground;
int clrTextBk = item.cellBackground != null ? item.cellBackground [nmcd.iSubItem] : -1;
if (clrTextBk == -1) clrTextBk = item.background;
if (selectionForeground != -1) clrText = selectionForeground;
/*
* Bug in Windows. When the table has the extended style
* LVS_EX_FULLROWSELECT and LVM_SETBKCOLOR is used with
* CLR_NONE to make the table transparent, Windows draws
* a black rectangle around any column that contains an
* image. The fix is emulate LVS_EX_FULLROWSELECT by
* drawing the selection.
*/
final boolean enabled = OS.IsWindowEnabled (handle);
if (OS.IsWindowVisible (handle) && enabled) {
if (!explorerTheme && !ignoreDrawSelection && (style & SWT.FULL_SELECTION) != 0) {
int bits = (int)OS.SendMessage (handle, OS.LVM_GETEXTENDEDLISTVIEWSTYLE, 0, 0);
if ((bits & OS.LVS_EX_FULLROWSELECT) == 0) {
/*
* Bug in Windows. For some reason, CDIS_SELECTED always set,
* even for items that are not selected. The fix is to get
* the selection state from the item.
*/
LVITEM lvItem = new LVITEM ();
lvItem.mask = OS.LVIF_STATE;
lvItem.stateMask = OS.LVIS_SELECTED;
lvItem.iItem = (int)nmcd.dwItemSpec;
long result = OS.SendMessage (handle, OS.LVM_GETITEM, 0, lvItem);
if ((result != 0 && (lvItem.state & OS.LVIS_SELECTED) != 0)) {
int clrSelection = -1;
if (nmcd.iSubItem == 0) {
if (OS.GetFocus () == handle || display.getHighContrast ()) {
clrSelection = OS.GetSysColor (OS.COLOR_HIGHLIGHT);
} else {
if ((style & SWT.HIDE_SELECTION) == 0) {
clrSelection = OS.GetSysColor (OS.COLOR_3DFACE);
}
}
} else {
if (OS.GetFocus () == handle || display.getHighContrast ()) {
clrText = OS.GetSysColor (OS.COLOR_HIGHLIGHTTEXT);
clrTextBk = clrSelection = OS.GetSysColor (OS.COLOR_HIGHLIGHT);
} else {
if ((style & SWT.HIDE_SELECTION) == 0) {
clrTextBk = clrSelection = OS.GetSysColor (OS.COLOR_3DFACE);
}
}
}
if (clrSelection != -1) {
RECT rect = item.getBounds ((int)nmcd.dwItemSpec, nmcd.iSubItem, true, nmcd.iSubItem != 0, true, false, hDC);
fillBackground (hDC, clrSelection, rect);
}
}
}
}
}
if (!ignoreDrawForeground) {
/*
* Bug in Windows. When the attributes are for one cell in a table,
* Windows does not reset them for the next cell. As a result, all
* subsequent cells are drawn using the previous font, foreground and
* background colors. The fix is to set the all attributes when any
* attribute could have changed.
*/
boolean hasAttributes = true;
if (hFont == -1 && clrText == -1 && clrTextBk == -1) {
if (item.cellForeground == null && item.cellBackground == null && item.cellFont == null) {
int count = (int)OS.SendMessage (hwndHeader, OS.HDM_GETITEMCOUNT, 0, 0);
if (count == 1) hasAttributes = false;
}
}
if (hasAttributes) {
if (hFont == -1) hFont = OS.SendMessage (handle, OS.WM_GETFONT, 0, 0);
OS.SelectObject (hDC, hFont);
if (enabled) {
nmcd.clrText = clrText == -1 ? getForegroundPixel () : clrText;
if (clrTextBk == -1) {
nmcd.clrTextBk = OS.CLR_NONE;
if (selectionForeground == -1) {
Control control = findBackgroundControl ();
if (control == null) control = this;
if (control.backgroundImage == null) {
if ((int)OS.SendMessage (handle, OS.LVM_GETBKCOLOR, 0, 0) != OS.CLR_NONE) {
nmcd.clrTextBk = control.getBackgroundPixel ();
}
}
}
} else {
nmcd.clrTextBk = selectionForeground != -1 ? OS.CLR_NONE : clrTextBk;
}
OS.MoveMemory (lParam, nmcd, NMLVCUSTOMDRAW.sizeof);
}
code |= OS.CDRF_NEWFONT;
}
}
/*
* Feature in Windows. When there is a sort column, the sort column
* color draws on top of the background color for an item. The fix
* is to clear the sort column in CDDS_SUBITEMPREPAINT, and reset it
* in CDDS_SUBITEMPOSTPAINT.
*
* Update region is saved and restored around LVM_SETSELECTEDCOLUMN
* to prevent infinite WM_PAINT on Vista.
*/
if ((enabled && clrTextBk != -1) || (!enabled && hasCustomBackground())) {
int oldColumn = (int)OS.SendMessage (handle, OS.LVM_GETSELECTEDCOLUMN, 0, 0);
if (oldColumn != -1 && oldColumn == nmcd.iSubItem) {
long rgn = OS.CreateRectRgn (0, 0, 0, 0);
int result = OS.GetUpdateRgn (handle, rgn, true);
OS.SendMessage (handle, OS.LVM_SETSELECTEDCOLUMN, -1, 0);
OS.ValidateRect (handle, null);
if (result != OS.NULLREGION) OS.InvalidateRgn (handle, rgn, true);
OS.DeleteObject (rgn);
code |= OS.CDRF_NOTIFYPOSTPAINT;
}
}
if (!enabled) {
/*
* Feature in Windows. When the table is disabled, it draws
* with a gray background but does not gray the text. The fix
* is to explicitly gray the text, but only, when it wasn't customized.
*/
nmcd.clrText = OS.GetSysColor (OS.COLOR_GRAYTEXT);
if (findImageControl () != null || hasCustomBackground()) {
nmcd.clrTextBk = OS.CLR_NONE;
}
nmcd.uItemState &= ~OS.CDIS_SELECTED;
OS.MoveMemory (lParam, nmcd, NMLVCUSTOMDRAW.sizeof);
code |= OS.CDRF_NEWFONT;
}
return new LRESULT (code);
}
@Override
void checkBuffered () {
super.checkBuffered ();
style |= SWT.DOUBLE_BUFFERED;
}
boolean checkData (TableItem item, boolean redraw) {
if ((style & SWT.VIRTUAL) == 0) return true;
return checkData (item, indexOf (item), redraw);
}
boolean checkData (TableItem item, int index, boolean redraw) {
if ((style & SWT.VIRTUAL) == 0) return true;
if (!item.cached) {
item.cached = true;
Event event = new Event ();
event.item = item;
event.index = index;
currentItem = item;
sendEvent (SWT.SetData, event);
//widget could be disposed at this point
currentItem = null;
if (isDisposed () || item.isDisposed ()) return false;
if (redraw) {
if (!setScrollWidth (item, false)) {
item.redraw ();
}
}
}
return true;
}
@Override
boolean checkHandle (long hwnd) {
if (hwnd == handle) return true;
return hwnd == OS.SendMessage (handle, OS.LVM_GETHEADER, 0, 0);
}
@Override
protected void checkSubclass () {
if (!isValidSubclass ()) error (SWT.ERROR_INVALID_SUBCLASS);
}
/**
* Clears the item at the given zero-relative index in the receiver.
* The text, icon and other attributes of the item are set to the default
* value. If the table was created with the SWT.VIRTUAL
style,
* these attributes are requested again as needed.
*
* @param index the index of the item to clear
*
* @exception IllegalArgumentException
*
* @exception SWTException
*
*
* @see SWT#VIRTUAL
* @see SWT#SetData
*
* @since 3.0
*/
public void clear (int index) {
checkWidget ();
int count = (int)OS.SendMessage (handle, OS.LVM_GETITEMCOUNT, 0, 0);
if (!(0 <= index && index < count)) error (SWT.ERROR_INVALID_RANGE);
TableItem item = _getItem (index, false);
if (item != null) {
if (item != currentItem) item.clear ();
/*
* Bug in Windows. Despite the fact that every item in the
* table always has LPSTR_TEXTCALLBACK, Windows caches the
* bounds for the selected items. This means that
* when you change the string to be something else, Windows
* correctly asks you for the new string but when the item
* is selected, the selection draws using the bounds of the
* previous item. The fix is to reset LPSTR_TEXTCALLBACK
* even though it has not changed, causing Windows to flush
* cached bounds.
*/
if ((style & SWT.VIRTUAL) == 0 && item.cached) {
LVITEM lvItem = new LVITEM ();
lvItem.mask = OS.LVIF_TEXT | OS.LVIF_INDENT;
lvItem.pszText = OS.LPSTR_TEXTCALLBACK;
lvItem.iItem = index;
OS.SendMessage (handle, OS.LVM_SETITEM, 0, lvItem);
item.cached = false;
}
if (currentItem == null && getDrawing () && OS.IsWindowVisible (handle)) {
OS.SendMessage (handle, OS.LVM_REDRAWITEMS, index, index);
}
setScrollWidth (item, false);
}
}
/**
* Removes the items from the receiver which are between the given
* zero-relative start and end indices (inclusive). The text, icon
* and other attributes of the items are set to their default values.
* If the table was created with the SWT.VIRTUAL
style,
* these attributes are requested again as needed.
*
* @param start the start index of the item to clear
* @param end the end index of the item to clear
*
* @exception IllegalArgumentException
*
* @exception SWTException
*
*
* @see SWT#VIRTUAL
* @see SWT#SetData
*
* @since 3.0
*/
public void clear (int start, int end) {
checkWidget ();
if (start > end) return;
int count = (int)OS.SendMessage (handle, OS.LVM_GETITEMCOUNT, 0, 0);
if (!(0 <= start && start <= end && end < count)) {
error (SWT.ERROR_INVALID_RANGE);
}
if (start == 0 && end == count - 1) {
clearAll ();
} else {
LVITEM lvItem = null;
boolean cleared = false;
for (int i=start; i<=end; i++) {
TableItem item = _getItem (i, false);
if (item != null) {
if (item != currentItem) {
cleared = true;
item.clear ();
}
/*
* Bug in Windows. Despite the fact that every item in the
* table always has LPSTR_TEXTCALLBACK, Windows caches the
* bounds for the selected items. This means that
* when you change the string to be something else, Windows
* correctly asks you for the new string but when the item
* is selected, the selection draws using the bounds of the
* previous item. The fix is to reset LPSTR_TEXTCALLBACK
* even though it has not changed, causing Windows to flush
* cached bounds.
*/
if ((style & SWT.VIRTUAL) == 0 && item.cached) {
if (lvItem == null) {
lvItem = new LVITEM ();
lvItem.mask = OS.LVIF_TEXT | OS.LVIF_INDENT;
lvItem.pszText = OS.LPSTR_TEXTCALLBACK;
}
lvItem.iItem = i;
OS.SendMessage (handle, OS.LVM_SETITEM, 0, lvItem);
item.cached = false;
}
}
}
if (cleared) {
if (currentItem == null && getDrawing () && OS.IsWindowVisible (handle)) {
OS.SendMessage (handle, OS.LVM_REDRAWITEMS, start, end);
}
TableItem item = start == end ? _getItem (start, false) : null;
setScrollWidth (item, false);
}
}
}
/**
* Clears the items at the given zero-relative indices in the receiver.
* The text, icon and other attributes of the items are set to their default
* values. If the table was created with the SWT.VIRTUAL
style,
* these attributes are requested again as needed.
*
* @param indices the array of indices of the items
*
* @exception IllegalArgumentException
*
* @exception SWTException
*
*
* @see SWT#VIRTUAL
* @see SWT#SetData
*
* @since 3.0
*/
public void clear (int [] indices) {
checkWidget ();
if (indices == null) error (SWT.ERROR_NULL_ARGUMENT);
if (indices.length == 0) return;
int count = (int)OS.SendMessage (handle, OS.LVM_GETITEMCOUNT, 0, 0);
for (int i=0; i
*
*
* @see SWT#VIRTUAL
* @see SWT#SetData
*
* @since 3.0
*/
public void clearAll () {
checkWidget ();
LVITEM lvItem = null;
boolean cleared = false;
int count = (int)OS.SendMessage (handle, OS.LVM_GETITEMCOUNT, 0, 0);
for (int i=0; i
*
*/
public void deselect (int [] indices) {
checkWidget ();
if (indices == null) error (SWT.ERROR_NULL_ARGUMENT);
if (indices.length == 0) return;
LVITEM lvItem = new LVITEM ();
lvItem.stateMask = OS.LVIS_SELECTED;
for (int i=0; i
*
*/
public void deselect (int index) {
checkWidget ();
/*
* An index of -1 will apply the change to all
* items. Ensure that index is greater than -1.
*/
if (index < 0) return;
LVITEM lvItem = new LVITEM ();
lvItem.stateMask = OS.LVIS_SELECTED;
ignoreSelect = true;
OS.SendMessage (handle, OS.LVM_SETITEMSTATE, index, lvItem);
ignoreSelect = false;
}
/**
* Deselects the items at the given zero-relative indices in the receiver.
* If the item at the given zero-relative index in the receiver
* is selected, it is deselected. If the item at the index
* was not selected, it remains deselected. The range of the
* indices is inclusive. Indices that are out of range are ignored.
*
* @param start the start index of the items to deselect
* @param end the end index of the items to deselect
*
* @exception SWTException
*
*/
public void deselect (int start, int end) {
checkWidget ();
int count = (int)OS.SendMessage (handle, OS.LVM_GETITEMCOUNT, 0, 0);
if (start == 0 && end == count - 1) {
deselectAll ();
} else {
LVITEM lvItem = new LVITEM ();
lvItem.stateMask = OS.LVIS_SELECTED;
/*
* An index of -1 will apply the change to all
* items. Ensure that indices are greater than -1.
*/
start = Math.max (0, start);
for (int i=start; i<=end; i++) {
ignoreSelect = true;
OS.SendMessage (handle, OS.LVM_SETITEMSTATE, i, lvItem);
ignoreSelect = false;
}
}
}
/**
* Deselects all selected items in the receiver.
*
* @exception SWTException
*
*/
public void deselectAll () {
checkWidget ();
LVITEM lvItem = new LVITEM ();
lvItem.mask = OS.LVIF_STATE;
lvItem.stateMask = OS.LVIS_SELECTED;
ignoreSelect = true;
OS.SendMessage (handle, OS.LVM_SETITEMSTATE, -1, lvItem);
ignoreSelect = false;
}
void destroyItem (TableColumn column) {
int index = 0;
while (index < columnCount) {
if (columns [index] == column) break;
index++;
}
int oldColumn = (int)OS.SendMessage (handle, OS.LVM_GETSELECTEDCOLUMN, 0, 0);
if (oldColumn == index) {
OS.SendMessage (handle, OS.LVM_SETSELECTEDCOLUMN, -1, 0);
} else {
if (oldColumn > index) {
OS.SendMessage (handle, OS.LVM_SETSELECTEDCOLUMN, oldColumn - 1, 0);
}
}
int orderIndex = 0;
int [] oldOrder = new int [columnCount];
OS.SendMessage (handle, OS.LVM_GETCOLUMNORDERARRAY, columnCount, oldOrder);
while (orderIndex < columnCount) {
if (oldOrder [orderIndex] == index) break;
orderIndex++;
}
ignoreColumnResize = true;
boolean first = false;
if (index == 0) {
first = true;
/*
* Changing the content of a column using LVM_SETCOLUMN causes
* the table control to send paint events. At this point the
* partially disposed column is still part of the table and
* paint handler can try to access it. This can cause exceptions.
* The fix is to turn redraw off.
*/
setRedraw (false);
if (columnCount > 1) {
index = 1;
int cchTextMax = 1024;
long hHeap = OS.GetProcessHeap ();
int byteCount = cchTextMax * TCHAR.sizeof;
long pszText = OS.HeapAlloc (hHeap, OS.HEAP_ZERO_MEMORY, byteCount);
LVCOLUMN lvColumn = new LVCOLUMN ();
lvColumn.mask = OS.LVCF_TEXT | OS.LVCF_IMAGE | OS.LVCF_WIDTH | OS.LVCF_FMT;
lvColumn.pszText = pszText;
lvColumn.cchTextMax = cchTextMax;
OS.SendMessage (handle, OS.LVM_GETCOLUMN, 1, lvColumn);
lvColumn.fmt &= ~(OS.LVCFMT_CENTER | OS.LVCFMT_RIGHT);
lvColumn.fmt |= OS.LVCFMT_LEFT;
OS.SendMessage (handle, OS.LVM_SETCOLUMN, 0, lvColumn);
if (pszText != 0) OS.HeapFree (hHeap, 0, pszText);
} else {
long hHeap = OS.GetProcessHeap ();
long pszText = OS.HeapAlloc (hHeap, OS.HEAP_ZERO_MEMORY, TCHAR.sizeof);
LVCOLUMN lvColumn = new LVCOLUMN ();
lvColumn.mask = OS.LVCF_TEXT | OS.LVCF_IMAGE | OS.LVCF_WIDTH | OS.LVCF_FMT;
lvColumn.pszText = pszText;
lvColumn.iImage = OS.I_IMAGENONE;
lvColumn.fmt = OS.LVCFMT_LEFT;
OS.SendMessage (handle, OS.LVM_SETCOLUMN, 0, lvColumn);
if (pszText != 0) OS.HeapFree (hHeap, 0, pszText);
HDITEM hdItem = new HDITEM ();
hdItem.mask = OS.HDI_FORMAT;
hdItem.fmt = OS.HDF_LEFT;
long hwndHeader = OS.SendMessage (handle, OS.LVM_GETHEADER, 0, 0);
OS.SendMessage (hwndHeader, OS.HDM_SETITEM, index, hdItem);
}
setRedraw (true);
/*
* Bug in Windows. Despite the fact that every item in the
* table always has LPSTR_TEXTCALLBACK, Windows caches the
* bounds for the selected items. This means that
* when you change the string to be something else, Windows
* correctly asks you for the new string but when the item
* is selected, the selection draws using the bounds of the
* previous item. The fix is to reset LPSTR_TEXTCALLBACK
* even though it has not changed, causing Windows to flush
* cached bounds.
*/
if ((style & SWT.VIRTUAL) == 0) {
LVITEM lvItem = new LVITEM ();
lvItem.mask = OS.LVIF_TEXT | OS.LVIF_IMAGE;
lvItem.pszText = OS.LPSTR_TEXTCALLBACK;
lvItem.iImage = OS.I_IMAGECALLBACK;
int itemCount = (int)OS.SendMessage (handle, OS.LVM_GETITEMCOUNT, 0, 0);
for (int i=0; iERROR_INVALID_RANGE
despite
* the fact that a single column of data may be visible in the table.
* This occurs when the programmer uses the table like a list, adding
* items but never creating a column.
*
* @param index the index of the column to return
* @return the column at the given index
*
* @exception IllegalArgumentException
*
* @exception SWTException
*
*
* @see Table#getColumnOrder()
* @see Table#setColumnOrder(int[])
* @see TableColumn#getMoveable()
* @see TableColumn#setMoveable(boolean)
* @see SWT#Move
*/
public TableColumn getColumn (int index) {
checkWidget ();
if (!(0 <= index && index < columnCount)) error (SWT.ERROR_INVALID_RANGE);
return columns [index];
}
/**
* Returns the number of columns contained in the receiver.
* If no TableColumn
s were created by the programmer,
* this value is zero, despite the fact that visually, one column
* of items may be visible. This occurs when the programmer uses
* the table like a list, adding items but never creating a column.
*
* @return the number of columns
*
* @exception SWTException
*
*/
public int getColumnCount () {
checkWidget ();
return columnCount;
}
/**
* Returns an array of zero-relative integers that map
* the creation order of the receiver's items to the
* order in which they are currently being displayed.
*
*
*
* @see Table#setColumnOrder(int[])
* @see TableColumn#getMoveable()
* @see TableColumn#setMoveable(boolean)
* @see SWT#Move
*
* @since 3.1
*/
public int[] getColumnOrder () {
checkWidget ();
if (columnCount == 0) return new int [0];
int [] order = new int [columnCount];
OS.SendMessage (handle, OS.LVM_GETCOLUMNORDERARRAY, columnCount, order);
return order;
}
/**
* Returns an array of TableColumn
s which are the
* columns in the receiver. Columns are returned in the order
* that they were created. If no TableColumn
s were
* created by the programmer, the array is empty, despite the fact
* that visually, one column of items may be visible. This occurs
* when the programmer uses the table like a list, adding items but
* never creating a column.
*
*
*
* @see Table#getColumnOrder()
* @see Table#setColumnOrder(int[])
* @see TableColumn#getMoveable()
* @see TableColumn#setMoveable(boolean)
* @see SWT#Move
*/
public TableColumn [] getColumns () {
checkWidget ();
TableColumn [] result = new TableColumn [columnCount];
System.arraycopy (columns, 0, result, 0, columnCount);
return result;
}
int getFocusIndex () {
// checkWidget ();
return (int)OS.SendMessage (handle, OS.LVM_GETNEXTITEM, -1, OS.LVNI_FOCUSED);
}
/**
* Returns the width in points of a grid line.
*
* @return the width of a grid line in points
*
* @exception SWTException
*
*/
public int getGridLineWidth () {
checkWidget ();
return DPIUtil.autoScaleDown(getGridLineWidthInPixels());
}
int getGridLineWidthInPixels () {
return GRID_WIDTH;
}
/**
* Returns the header background color.
*
* @return the receiver's header background color.
*
* @exception SWTException
*
* @since 3.106
*/
public Color getHeaderBackground () {
checkWidget ();
return Color.win32_new (display, getHeaderBackgroundPixel());
}
private int getHeaderBackgroundPixel() {
return headerBackground != -1 ? headerBackground : defaultBackground();
}
/**
* Returns the header foreground color.
*
* @return the receiver's header foreground color.
*
* @exception SWTException
*
* @since 3.106
*/
public Color getHeaderForeground () {
checkWidget ();
return Color.win32_new (display, getHeaderForegroundPixel());
}
private int getHeaderForegroundPixel() {
return headerForeground != -1 ? headerForeground : defaultForeground();
}
/**
* Returns the height of the receiver's header
*
* @return the height of the header or zero if the header is not visible
*
* @exception SWTException
*
*
* @since 2.0
*/
public int getHeaderHeight () {
checkWidget ();
return DPIUtil.autoScaleDown(getHeaderHeightInPixels ());
}
int getHeaderHeightInPixels () {
if (hwndHeader == 0) return 0;
RECT rect = new RECT ();
OS.GetWindowRect (hwndHeader, rect);
return rect.bottom - rect.top;
}
/**
* Returns true
if the receiver's header is visible,
* and false
otherwise.
*
*
*/
public boolean getHeaderVisible () {
checkWidget ();
int bits = OS.GetWindowLong (handle, OS.GWL_STYLE);
return (bits & OS.LVS_NOCOLUMNHEADER) == 0;
}
/**
* Returns the item at the given, zero-relative index in the
* receiver. Throws an exception if the index is out of range.
*
* @param index the index of the item to return
* @return the item at the given index
*
* @exception IllegalArgumentException
*
* @exception SWTException
*
*/
public TableItem getItem (int index) {
checkWidget ();
int count = (int)OS.SendMessage (handle, OS.LVM_GETITEMCOUNT, 0, 0);
if (!(0 <= index && index < count)) error (SWT.ERROR_INVALID_RANGE);
return _getItem (index);
}
/**
* Returns the item at the given point in the receiver
* or null if no such item exists. The point is in the
* coordinate system of the receiver.
*
*
* @exception SWTException
*
*/
public TableItem getItem (Point point) {
checkWidget ();
if (point == null) error (SWT.ERROR_NULL_ARGUMENT);
return getItemInPixels (DPIUtil.autoScaleUp(point));
}
TableItem getItemInPixels (Point point) {
int count = (int)OS.SendMessage (handle, OS.LVM_GETITEMCOUNT, 0, 0);
if (count == 0) return null;
LVHITTESTINFO pinfo = new LVHITTESTINFO ();
pinfo.x = point.x;
pinfo.y = point.y;
if ((style & SWT.FULL_SELECTION) == 0) {
if (hooks (SWT.MeasureItem)) {
/*
* Bug in Windows. When LVM_SUBITEMHITTEST is used to hittest
* a point that is above the table, instead of returning -1 to
* indicate that the hittest failed, a negative index is returned.
* The fix is to consider any value that is negative a failure.
*/
if (OS.SendMessage (handle, OS.LVM_SUBITEMHITTEST, 0, pinfo) < 0) {
RECT rect = new RECT ();
rect.left = OS.LVIR_ICON;
ignoreCustomDraw = true;
long code = OS.SendMessage (handle, OS.LVM_GETITEMRECT, 0, rect);
ignoreCustomDraw = false;
if (code != 0) {
pinfo.x = rect.left;
/*
* Bug in Windows. When LVM_SUBITEMHITTEST is used to hittest
* a point that is above the table, instead of returning -1 to
* indicate that the hittest failed, a negative index is returned.
* The fix is to consider any value that is negative a failure.
*/
OS.SendMessage (handle, OS.LVM_SUBITEMHITTEST, 0, pinfo);
if (pinfo.iItem < 0) pinfo.iItem = -1;
}
}
if (pinfo.iItem != -1 && pinfo.iSubItem == 0) {
if (hitTestSelection (pinfo.iItem, pinfo.x, pinfo.y)) {
return _getItem (pinfo.iItem);
}
}
return null;
}
}
OS.SendMessage (handle, OS.LVM_HITTEST, 0, pinfo);
if (pinfo.iItem != -1) {
/*
* Bug in Windows. When the point that is used by
* LVM_HITTEST is inside the header, Windows returns
* the first item in the table. The fix is to check
* when LVM_HITTEST returns the first item and make
* sure that when the point is within the header,
* the first item is not returned.
*/
if (pinfo.iItem == 0) {
int bits = OS.GetWindowLong (handle, OS.GWL_STYLE);
if ((bits & OS.LVS_NOCOLUMNHEADER) == 0) {
if (hwndHeader != 0) {
RECT rect = new RECT ();
OS.GetWindowRect (hwndHeader, rect);
POINT pt = new POINT ();
pt.x = pinfo.x;
pt.y = pinfo.y;
OS.MapWindowPoints (handle, 0, pt, 1);
if (OS.PtInRect (rect, pt)) return null;
}
}
}
return _getItem (pinfo.iItem);
}
return null;
}
/**
* Returns the number of items contained in the receiver.
*
* @return the number of items
*
* @exception SWTException
*
*/
public int getItemCount () {
checkWidget ();
return (int)OS.SendMessage (handle, OS.LVM_GETITEMCOUNT, 0, 0);
}
/**
* Returns the height of the area which would be used to
* display one of the items in the receiver.
*
* @return the height of one item
*
* @exception SWTException
*
*/
public int getItemHeight () {
checkWidget ();
return DPIUtil.autoScaleDown(getItemHeightInPixels());
}
int getItemHeightInPixels () {
if (!painted && hooks (SWT.MeasureItem)) hitTestSelection (0, 0, 0);
long empty = OS.SendMessage (handle, OS.LVM_APPROXIMATEVIEWRECT, 0, 0);
long oneItem = OS.SendMessage (handle, OS.LVM_APPROXIMATEVIEWRECT, 1, 0);
return OS.HIWORD (oneItem) - OS.HIWORD (empty);
}
/**
* Returns a (possibly empty) array of TableItem
s which
* are the items in the receiver.
*
*
*/
public TableItem [] getItems () {
checkWidget ();
int count = (int)OS.SendMessage (handle, OS.LVM_GETITEMCOUNT, 0, 0);
TableItem [] result = new TableItem [count];
if ((style & SWT.VIRTUAL) != 0) {
for (int i=0; ifalse
otherwise. Note that some platforms draw
* grid lines while others may draw alternating row colors.
*
*
*/
public boolean getLinesVisible () {
checkWidget ();
return _getLinesVisible();
}
private boolean _getLinesVisible() {
int bits = (int)OS.SendMessage (handle, OS.LVM_GETEXTENDEDLISTVIEWSTYLE, 0, 0);
return (bits & OS.LVS_EX_GRIDLINES) != 0;
}
/**
* Returns an array of TableItem
s that are currently
* selected in the receiver. The order of the items is unspecified.
* An empty array indicates that no items are selected.
*
*
*/
public TableItem [] getSelection () {
checkWidget ();
int i = -1, j = 0, count = (int)OS.SendMessage (handle, OS.LVM_GETSELECTEDCOUNT, 0, 0);
TableItem [] result = new TableItem [count];
while ((i = (int)OS.SendMessage (handle, OS.LVM_GETNEXTITEM, i, OS.LVNI_SELECTED)) != -1) {
result [j++] = _getItem (i);
}
return result;
}
/**
* Returns the number of selected items contained in the receiver.
*
* @return the number of selected items
*
* @exception SWTException
*
*/
public int getSelectionCount () {
checkWidget ();
return (int)OS.SendMessage (handle, OS.LVM_GETSELECTEDCOUNT, 0, 0);
}
/**
* Returns the zero-relative index of the item which is currently
* selected in the receiver, or -1 if no item is selected.
*
* @return the index of the selected item
*
* @exception SWTException
*
*/
public int getSelectionIndex () {
checkWidget ();
int focusIndex = (int)OS.SendMessage (handle, OS.LVM_GETNEXTITEM, -1, OS.LVNI_FOCUSED);
int selectedIndex = (int)OS.SendMessage (handle, OS.LVM_GETNEXTITEM, -1, OS.LVNI_SELECTED);
if (focusIndex == selectedIndex) return selectedIndex;
int i = -1;
while ((i = (int)OS.SendMessage (handle, OS.LVM_GETNEXTITEM, i, OS.LVNI_SELECTED)) != -1) {
if (i == focusIndex) return i;
}
return selectedIndex;
}
/**
* Returns the zero-relative indices of the items which are currently
* selected in the receiver. The order of the indices is unspecified.
* The array is empty if no items are selected.
*
*
*/
public int [] getSelectionIndices () {
checkWidget ();
int i = -1, j = 0, count = (int)OS.SendMessage (handle, OS.LVM_GETSELECTEDCOUNT, 0, 0);
int [] result = new int [count];
while ((i = (int)OS.SendMessage (handle, OS.LVM_GETNEXTITEM, i, OS.LVNI_SELECTED)) != -1) {
result [j++] = i;
}
return result;
}
/**
* Returns the column which shows the sort indicator for
* the receiver. The value may be null if no column shows
* the sort indicator.
*
* @return the sort indicator
*
* @exception SWTException
*
*
* @see #setSortColumn(TableColumn)
*
* @since 3.2
*/
public TableColumn getSortColumn () {
checkWidget ();
return sortColumn;
}
int getSortColumnPixel () {
int pixel = OS.IsWindowEnabled (handle) || hasCustomBackground() ? getBackgroundPixel () : OS.GetSysColor (OS.COLOR_3DFACE);
return getSlightlyDifferentBackgroundColor(pixel);
}
/**
* Returns the direction of the sort indicator for the receiver.
* The value will be one of UP
, DOWN
* or NONE
.
*
* @return the sort direction
*
* @exception SWTException
*
*
* @see #setSortDirection(int)
*
* @since 3.2
*/
public int getSortDirection () {
checkWidget ();
return sortDirection;
}
/**
* Returns the zero-relative index of the item which is currently
* at the top of the receiver. This index can change when items are
* scrolled or new items are added or removed.
*
* @return the index of the top item
*
* @exception SWTException
*
*/
public int getTopIndex () {
checkWidget ();
/*
* Bug in Windows. Under rare circumstances, LVM_GETTOPINDEX
* can return a negative number. When this happens, the table
* is displaying blank lines at the top of the controls. The
* fix is to check for a negative number and return zero instead.
*/
return Math.max (0, (int)OS.SendMessage (handle, OS.LVM_GETTOPINDEX, 0, 0));
}
boolean hasChildren () {
long hwndChild = OS.GetWindow (handle, OS.GW_CHILD);
while (hwndChild != 0) {
if (hwndChild != hwndHeader) return true;
hwndChild = OS.GetWindow (hwndChild, OS.GW_HWNDNEXT);
}
return false;
}
boolean hitTestSelection (int index, int x, int y) {
int count = (int)OS.SendMessage (handle, OS.LVM_GETITEMCOUNT, 0, 0);
if (count == 0) return false;
if (!hooks (SWT.MeasureItem)) return false;
boolean result = false;
if (0 <= index && index < count) {
TableItem item = _getItem (index);
long hDC = OS.GetDC (handle);
long oldFont = 0, newFont = OS.SendMessage (handle, OS.WM_GETFONT, 0, 0);
if (newFont != 0) oldFont = OS.SelectObject (hDC, newFont);
long hFont = item.fontHandle (0);
if (hFont != -1) hFont = OS.SelectObject (hDC, hFont);
Event event = sendMeasureItemEvent (item, index, 0, hDC);
if (event.getBoundsInPixels ().contains (x, y)) result = true;
if (hFont != -1) hFont = OS.SelectObject (hDC, hFont);
if (newFont != 0) OS.SelectObject (hDC, oldFont);
OS.ReleaseDC (handle, hDC);
// if (isDisposed () || item.isDisposed ()) return false;
}
return result;
}
int imageIndex (Image image, int column) {
if (image == null) return OS.I_IMAGENONE;
if (column == 0) {
firstColumnImage = true;
} else {
setSubImagesVisible (true);
}
if (imageList == null) {
Rectangle bounds = image.getBoundsInPixels ();
imageList = display.getImageList (style & SWT.RIGHT_TO_LEFT, bounds.width, bounds.height);
int index = imageList.indexOf (image);
if (index == -1) index = imageList.add (image);
long hImageList = imageList.getHandle ();
/*
* Bug in Windows. Making any change to an item that
* changes the item height of a table while the table
* is scrolled can cause the lines to draw incorrectly.
* This happens even when the lines are not currently
* visible and are shown afterwards. The fix is to
* save the top index, scroll to the top of the table
* and then restore the original top index.
*/
int topIndex = getTopIndex ();
if (topIndex != 0) {
setRedraw (false);
setTopIndex (0);
}
OS.SendMessage (handle, OS.LVM_SETIMAGELIST, OS.LVSIL_SMALL, hImageList);
if (headerImageList != null) {
long hHeaderImageList = headerImageList.getHandle ();
OS.SendMessage (hwndHeader, OS.HDM_SETIMAGELIST, 0, hHeaderImageList);
}
fixCheckboxImageList (false);
setItemHeight (false);
if (topIndex != 0) {
setTopIndex (topIndex);
setRedraw (true);
}
return index;
}
int index = imageList.indexOf (image);
if (index != -1) return index;
return imageList.add (image);
}
int imageIndexHeader (Image image) {
if (image == null) return OS.I_IMAGENONE;
if (headerImageList == null) {
Rectangle bounds = image.getBoundsInPixels ();
headerImageList = display.getImageList (style & SWT.RIGHT_TO_LEFT, bounds.width, bounds.height);
int index = headerImageList.indexOf (image);
if (index == -1) index = headerImageList.add (image);
long hImageList = headerImageList.getHandle ();
OS.SendMessage (hwndHeader, OS.HDM_SETIMAGELIST, 0, hImageList);
return index;
}
int index = headerImageList.indexOf (image);
if (index != -1) return index;
return headerImageList.add (image);
}
/**
* Searches the receiver's list starting at the first column
* (index 0) until a column is found that is equal to the
* argument, and returns the index of that column. If no column
* is found, returns -1.
*
* @param column the search column
* @return the index of the column
*
* @exception IllegalArgumentException
*
* @exception SWTException
*
*/
public int indexOf (TableColumn column) {
checkWidget ();
if (column == null) error (SWT.ERROR_NULL_ARGUMENT);
for (int i=0; i
*
*/
public int indexOf (TableItem item) {
checkWidget ();
if (item == null) error (SWT.ERROR_NULL_ARGUMENT);
//TODO - find other loops that can be optimized
if (keys == null) {
int count = (int)OS.SendMessage (handle, OS.LVM_GETITEMCOUNT, 0, 0);
if (1 <= lastIndexOf && lastIndexOf < count - 1) {
if (_getItem (lastIndexOf, false) == item) return lastIndexOf;
if (_getItem (lastIndexOf + 1, false) == item) return ++lastIndexOf;
if (_getItem (lastIndexOf - 1, false) == item) return --lastIndexOf;
}
if (lastIndexOf < count / 2) {
for (int i=0; ifalse
otherwise. Indices out of
* range are ignored.
*
* @param index the index of the item
* @return the selection state of the item at the index
*
* @exception SWTException
*
*/
public boolean isSelected (int index) {
checkWidget ();
LVITEM lvItem = new LVITEM ();
lvItem.mask = OS.LVIF_STATE;
lvItem.stateMask = OS.LVIS_SELECTED;
lvItem.iItem = index;
long result = OS.SendMessage (handle, OS.LVM_GETITEM, 0, lvItem);
return (result != 0) && ((lvItem.state & OS.LVIS_SELECTED) != 0);
}
@Override
void register () {
super.register ();
if (hwndHeader != 0) display.addControl (hwndHeader, this);
}
@Override
void releaseChildren (boolean destroy) {
if (_hasItems ()) {
int itemCount = (int)OS.SendMessage (handle, OS.LVM_GETITEMCOUNT, 0, 0);
if (keys == null) {
for (int i=0; i
*
*/
public void remove (int [] indices) {
checkWidget ();
if (indices == null) error (SWT.ERROR_NULL_ARGUMENT);
if (indices.length == 0) return;
int [] newIndices = new int [indices.length];
System.arraycopy (indices, 0, newIndices, 0, indices.length);
sort (newIndices);
int start = newIndices [newIndices.length - 1], end = newIndices [0];
int count = (int)OS.SendMessage (handle, OS.LVM_GETITEMCOUNT, 0, 0);
if (!(0 <= start && start <= end && end < count)) {
error (SWT.ERROR_INVALID_RANGE);
}
setDeferResize (true);
int last = -1;
for (int i=0; i
*
*/
public void remove (int index) {
checkWidget ();
int count = (int)OS.SendMessage (handle, OS.LVM_GETITEMCOUNT, 0, 0);
if (!(0 <= index && index < count)) error (SWT.ERROR_INVALID_RANGE);
TableItem item = _getItem (index, false);
if (item != null && !item.isDisposed ()) item.release (false);
setDeferResize (true);
ignoreSelect = ignoreShrink = true;
long code = OS.SendMessage (handle, OS.LVM_DELETEITEM, index, 0);
ignoreSelect = ignoreShrink = false;
if (code == 0) error (SWT.ERROR_ITEM_NOT_REMOVED);
_removeItem (index, count);
--count;
if (count == 0) setTableEmpty ();
setDeferResize (false);
}
/**
* Removes the items from the receiver which are
* between the given zero-relative start and end
* indices (inclusive).
*
* @param start the start of the range
* @param end the end of the range
*
* @exception IllegalArgumentException
*
* @exception SWTException
*
*/
public void remove (int start, int end) {
checkWidget ();
if (start > end) return;
int count = (int)OS.SendMessage (handle, OS.LVM_GETITEMCOUNT, 0, 0);
if (!(0 <= start && start <= end && end < count)) {
error (SWT.ERROR_INVALID_RANGE);
}
if (start == 0 && end == count - 1) {
removeAll ();
} else {
setDeferResize (true);
int index = start;
while (index <= end) {
TableItem item = _getItem (index, false);
if (item != null && !item.isDisposed ()) item.release (false);
ignoreSelect = ignoreShrink = true;
long code = OS.SendMessage (handle, OS.LVM_DELETEITEM, start, 0);
ignoreSelect = ignoreShrink = false;
if (code == 0) break;
index++;
}
_removeItems (start, index, count);
if (index <= end) error (SWT.ERROR_ITEM_NOT_REMOVED);
/*
* This code is intentionally commented. It is not necessary
* to check for an empty table because removeAll() was called
* when the start == 0 and end == count - 1.
*/
//if (count - index == 0) setTableEmpty ();
setDeferResize (false);
}
}
/**
* Removes all of the items from the receiver.
*
* @exception SWTException
*
*/
public void removeAll () {
checkWidget ();
int itemCount = (int)OS.SendMessage (handle, OS.LVM_GETITEMCOUNT, 0, 0);
for (int i=0; i
*
*
* @see SelectionListener
* @see #addSelectionListener(SelectionListener)
*/
public void removeSelectionListener(SelectionListener listener) {
checkWidget ();
if (listener == null) error (SWT.ERROR_NULL_ARGUMENT);
if (eventTable == null) return;
eventTable.unhook (SWT.Selection, listener);
eventTable.unhook (SWT.DefaultSelection,listener);
}
/**
* Selects the items at the given zero-relative indices in the receiver.
* The current selection is not cleared before the new items are selected.
*
*
* @exception SWTException
*
*
* @see Table#setSelection(int[])
*/
public void select (int [] indices) {
checkWidget ();
if (indices == null) error (SWT.ERROR_NULL_ARGUMENT);
int length = indices.length;
if (length == 0 || ((style & SWT.SINGLE) != 0 && length > 1)) return;
LVITEM lvItem = new LVITEM ();
lvItem.state = OS.LVIS_SELECTED;
lvItem.stateMask = OS.LVIS_SELECTED;
for (int i=length-1; i>=0; --i) {
/*
* An index of -1 will apply the change to all
* items. Ensure that indices are greater than -1.
*/
if (indices [i] >= 0) {
ignoreSelect = true;
OS.SendMessage (handle, OS.LVM_SETITEMSTATE, indices [i], lvItem);
ignoreSelect = false;
}
}
}
@Override
void reskinChildren (int flags) {
if (_hasItems ()) {
int itemCount = (int)OS.SendMessage (handle, OS.LVM_GETITEMCOUNT, 0, 0);
for (int i=0; i
*
*
* @see Table#setSelection(int,int)
*/
public void select (int start, int end) {
checkWidget ();
if (end < 0 || start > end || ((style & SWT.SINGLE) != 0 && start != end)) return;
int count = (int)OS.SendMessage (handle, OS.LVM_GETITEMCOUNT, 0, 0);
if (count == 0 || start >= count) return;
start = Math.max (0, start);
end = Math.min (end, count - 1);
if (start == 0 && end == count - 1) {
selectAll ();
} else {
/*
* An index of -1 will apply the change to all
* items. Indices must be greater than -1.
*/
LVITEM lvItem = new LVITEM ();
lvItem.state = OS.LVIS_SELECTED;
lvItem.stateMask = OS.LVIS_SELECTED;
for (int i=start; i<=end; i++) {
ignoreSelect = true;
OS.SendMessage (handle, OS.LVM_SETITEMSTATE, i, lvItem);
ignoreSelect = false;
}
}
}
/**
* Selects all of the items in the receiver.
*
*
*/
public void selectAll () {
checkWidget ();
if ((style & SWT.SINGLE) != 0) return;
LVITEM lvItem = new LVITEM ();
lvItem.mask = OS.LVIF_STATE;
lvItem.state = OS.LVIS_SELECTED;
lvItem.stateMask = OS.LVIS_SELECTED;
ignoreSelect = true;
OS.SendMessage (handle, OS.LVM_SETITEMSTATE, -1, lvItem);
ignoreSelect = false;
}
void sendEraseItemEvent (TableItem item, NMLVCUSTOMDRAW nmcd, long lParam, Event measureEvent) {
long hDC = nmcd.hdc;
int clrText = item.cellForeground != null ? item.cellForeground [nmcd.iSubItem] : -1;
if (clrText == -1) clrText = item.foreground;
int clrTextBk = -1;
if (OS.IsAppThemed ()) {
if (sortColumn != null && sortDirection != SWT.NONE) {
if (findImageControl () == null) {
if (indexOf (sortColumn) == nmcd.iSubItem) {
clrTextBk = getSortColumnPixel ();
}
}
}
}
clrTextBk = item.cellBackground != null ? item.cellBackground [nmcd.iSubItem] : -1;
if (clrTextBk == -1) clrTextBk = item.background;
/*
* Bug in Windows. For some reason, CDIS_SELECTED always set,
* even for items that are not selected. The fix is to get
* the selection state from the item.
*/
LVITEM lvItem = new LVITEM ();
lvItem.mask = OS.LVIF_STATE;
lvItem.stateMask = OS.LVIS_SELECTED;
lvItem.iItem = (int)nmcd.dwItemSpec;
long result = OS.SendMessage (handle, OS.LVM_GETITEM, 0, lvItem);
boolean selected = (result != 0 && (lvItem.state & OS.LVIS_SELECTED) != 0);
GCData data = new GCData ();
data.device = display;
int clrSelectionBk = -1;
boolean drawSelected = false, drawBackground = false, drawHot = false, drawDrophilited = false;
if (nmcd.iSubItem == 0 || (style & SWT.FULL_SELECTION) != 0) {
drawHot = hotIndex == nmcd.dwItemSpec;
drawDrophilited = (nmcd.uItemState & OS.CDIS_DROPHILITED) != 0;
}
if (OS.IsWindowEnabled (handle)) {
if (selected && (nmcd.iSubItem == 0 || (style & SWT.FULL_SELECTION) != 0)) {
if (OS.GetFocus () == handle || display.getHighContrast ()) {
drawSelected = true;
data.foreground = OS.GetSysColor (OS.COLOR_HIGHLIGHTTEXT);
data.background = clrSelectionBk = OS.GetSysColor (OS.COLOR_HIGHLIGHT);
} else {
drawSelected = (style & SWT.HIDE_SELECTION) == 0;
data.foreground = OS.GetTextColor (hDC);
data.background = clrSelectionBk = OS.GetSysColor (OS.COLOR_3DFACE);
}
if (explorerTheme) {
data.foreground = clrText != -1 ? clrText : getForegroundPixel ();
}
} else {
drawBackground = clrTextBk != -1;
/*
* Bug in Windows. When LVM_SETTEXTBKCOLOR, LVM_SETBKCOLOR
* or LVM_SETTEXTCOLOR is used to set the background color of
* the the text or the control, the color is not set in the HDC
* that is provided in Custom Draw. The fix is to explicitly
* set the color.
*/
if (clrText == -1 || clrTextBk == -1) {
Control control = findBackgroundControl ();
if (control == null) control = this;
if (clrText == -1) clrText = control.getForegroundPixel ();
if (clrTextBk == -1) clrTextBk = control.getBackgroundPixel ();
}
data.foreground = clrText != -1 ? clrText : OS.GetTextColor (hDC);
data.background = clrTextBk != -1 ? clrTextBk : OS.GetBkColor (hDC);
}
} else {
data.foreground = OS.GetSysColor (OS.COLOR_GRAYTEXT);
data.background = OS.GetSysColor (OS.COLOR_3DFACE);
if (selected) clrSelectionBk = data.background;
}
data.font = item.getFont (nmcd.iSubItem);
data.uiState = (int)OS.SendMessage (handle, OS.WM_QUERYUISTATE, 0, 0);
int nSavedDC = OS.SaveDC (hDC);
GC gc = GC.win32_new (hDC, data);
RECT cellRect = item.getBounds ((int)nmcd.dwItemSpec, nmcd.iSubItem, true, true, true, true, hDC);
Event event = new Event ();
event.item = item;
event.gc = gc;
event.index = nmcd.iSubItem;
event.detail |= SWT.FOREGROUND;
// if ((nmcd.uItemState & OS.CDIS_FOCUS) != 0) {
if (OS.SendMessage (handle, OS.LVM_GETNEXTITEM, -1, OS.LVNI_FOCUSED) == nmcd.dwItemSpec) {
if (nmcd.iSubItem == 0 || (style & SWT.FULL_SELECTION) != 0) {
if (handle == OS.GetFocus ()) {
int uiState = (int)OS.SendMessage (handle, OS.WM_QUERYUISTATE, 0, 0);
if ((uiState & OS.UISF_HIDEFOCUS) == 0) event.detail |= SWT.FOCUSED;
}
}
}
boolean focused = (event.detail & SWT.FOCUSED) != 0;
if (drawHot) event.detail |= SWT.HOT;
if (drawSelected) event.detail |= SWT.SELECTED;
if (drawBackground) event.detail |= SWT.BACKGROUND;
Rectangle boundsInPixels = new Rectangle (cellRect.left, cellRect.top, cellRect.right - cellRect.left, cellRect.bottom - cellRect.top);
event.setBoundsInPixels (boundsInPixels);
gc.setClipping (DPIUtil.autoScaleDown(boundsInPixels));
sendEvent (SWT.EraseItem, event);
event.gc = null;
int clrSelectionText = data.foreground;
gc.dispose ();
OS.RestoreDC (hDC, nSavedDC);
if (isDisposed () || item.isDisposed ()) return;
if (event.doit) {
ignoreDrawForeground = (event.detail & SWT.FOREGROUND) == 0;
ignoreDrawBackground = (event.detail & SWT.BACKGROUND) == 0;
ignoreDrawSelection = (event.detail & SWT.SELECTED) == 0;
ignoreDrawFocus = (event.detail & SWT.FOCUSED) == 0;
ignoreDrawHot = (event.detail & SWT.HOT) == 0;
} else {
ignoreDrawForeground = ignoreDrawBackground = ignoreDrawSelection = ignoreDrawFocus = ignoreDrawHot = true;
}
if (drawSelected) {
if (ignoreDrawSelection) {
ignoreDrawHot = true;
if (nmcd.iSubItem == 0 || (style & SWT.FULL_SELECTION) != 0) {
selectionForeground = clrSelectionText;
}
nmcd.uItemState &= ~OS.CDIS_SELECTED;
OS.MoveMemory (lParam, nmcd, NMLVCUSTOMDRAW.sizeof);
}
} else {
if (ignoreDrawSelection) {
nmcd.uItemState |= OS.CDIS_SELECTED;
OS.MoveMemory (lParam, nmcd, NMLVCUSTOMDRAW.sizeof);
}
}
boolean firstColumn = nmcd.iSubItem == OS.SendMessage (hwndHeader, OS.HDM_ORDERTOINDEX, 0, 0);
if (ignoreDrawForeground && ignoreDrawHot && !drawDrophilited) {
if (!ignoreDrawBackground && drawBackground) {
RECT backgroundRect = item.getBounds ((int)nmcd.dwItemSpec, nmcd.iSubItem, true, false, true, false, hDC);
fillBackground (hDC, clrTextBk, backgroundRect);
}
}
focusRect = null;
if (!ignoreDrawHot || !ignoreDrawSelection || !ignoreDrawFocus || drawDrophilited) {
boolean fullText = (style & SWT.FULL_SELECTION) != 0 || !firstColumn;
RECT textRect = item.getBounds ((int)nmcd.dwItemSpec, nmcd.iSubItem, true, false, fullText, false, hDC);
if ((style & SWT.FULL_SELECTION) == 0) {
if (measureEvent != null) {
Rectangle boundInPixels = measureEvent.getBoundsInPixels();
textRect.right = Math.min (cellRect.right, boundInPixels.x + boundInPixels.width);
}
if (!ignoreDrawFocus) {
nmcd.uItemState &= ~OS.CDIS_FOCUS;
OS.MoveMemory (lParam, nmcd, NMLVCUSTOMDRAW.sizeof);
focusRect = textRect;
}
}
if (explorerTheme) {
if (!ignoreDrawHot || drawDrophilited || (!ignoreDrawSelection && clrSelectionBk != -1)) {
RECT pClipRect = new RECT ();
OS.SetRect (pClipRect, nmcd.left, nmcd.top, nmcd.right, nmcd.bottom);
RECT rect = new RECT ();
OS.SetRect (rect, nmcd.left, nmcd.top, nmcd.right, nmcd.bottom);
if ((style & SWT.FULL_SELECTION) != 0) {
int count = (int)OS.SendMessage (hwndHeader, OS.HDM_GETITEMCOUNT, 0, 0);
int index = (int)OS.SendMessage (hwndHeader, OS.HDM_ORDERTOINDEX, count - 1, 0);
RECT headerRect = new RECT ();
OS.SendMessage (hwndHeader, OS.HDM_GETITEMRECT, index, headerRect);
OS.MapWindowPoints (hwndHeader, handle, headerRect, 2);
rect.right = headerRect.right;
index = (int)OS.SendMessage (hwndHeader, OS.HDM_ORDERTOINDEX, 0, 0);
OS.SendMessage (hwndHeader, OS.HDM_GETITEMRECT, index, headerRect);
OS.MapWindowPoints (hwndHeader, handle, headerRect, 2);
rect.left = headerRect.left;
pClipRect.left = cellRect.left;
pClipRect.right += EXPLORER_EXTRA;
} else {
rect.right += EXPLORER_EXTRA;
pClipRect.right += EXPLORER_EXTRA;
}
long hTheme = OS.OpenThemeData (handle, Display.LISTVIEW);
int iStateId = selected ? OS.LISS_SELECTED : OS.LISS_HOT;
if (OS.GetFocus () != handle && selected && !drawHot) iStateId = OS.LISS_SELECTEDNOTFOCUS;
if (drawDrophilited) iStateId = OS.LISS_SELECTED;
OS.DrawThemeBackground (hTheme, hDC, OS.LVP_LISTITEM, iStateId, rect, pClipRect);
OS.CloseThemeData (hTheme);
}
} else {
if (!ignoreDrawSelection && clrSelectionBk != -1) fillBackground (hDC, clrSelectionBk, textRect);
}
}
if (focused && ignoreDrawFocus) {
nmcd.uItemState &= ~OS.CDIS_FOCUS;
OS.MoveMemory (lParam, nmcd, NMLVCUSTOMDRAW.sizeof);
}
if (ignoreDrawForeground) {
RECT clipRect = item.getBounds ((int)nmcd.dwItemSpec, nmcd.iSubItem, true, true, true, false, hDC);
OS.SaveDC (hDC);
OS.SelectClipRgn (hDC, 0);
OS.ExcludeClipRect (hDC, clipRect.left, clipRect.top, clipRect.right, clipRect.bottom);
}
}
Event sendEraseItemEvent (TableItem item, NMTTCUSTOMDRAW nmcd, int column, RECT cellRect) {
int nSavedDC = OS.SaveDC (nmcd.hdc);
RECT insetRect = toolTipInset (cellRect);
OS.SetWindowOrgEx (nmcd.hdc, insetRect.left, insetRect.top, null);
GCData data = new GCData ();
data.device = display;
data.foreground = OS.GetTextColor (nmcd.hdc);
data.background = OS.GetBkColor (nmcd.hdc);
data.font = item.getFont (column);
data.uiState = (int)OS.SendMessage (handle, OS.WM_QUERYUISTATE, 0, 0);
GC gc = GC.win32_new (nmcd.hdc, data);
Event event = new Event ();
event.item = item;
event.index = column;
event.gc = gc;
event.detail |= SWT.FOREGROUND;
event.setBoundsInPixels(new Rectangle(cellRect.left, cellRect.top, cellRect.right - cellRect.left, cellRect.bottom - cellRect.top));
//gc.setClipping (event.x, event.y, event.width, event.height);
sendEvent (SWT.EraseItem, event);
event.gc = null;
//int newTextClr = data.foreground;
gc.dispose ();
OS.RestoreDC (nmcd.hdc, nSavedDC);
return event;
}
Event sendMeasureItemEvent (TableItem item, int row, int column, long hDC) {
GCData data = new GCData ();
data.device = display;
data.font = item.getFont (column);
int nSavedDC = OS.SaveDC (hDC);
GC gc = GC.win32_new (hDC, data);
RECT itemRect = item.getBounds (row, column, true, true, false, false, hDC);
Event event = new Event ();
event.item = item;
event.gc = gc;
event.index = column;
event.setBoundsInPixels(new Rectangle(itemRect.left, itemRect.top, itemRect.right - itemRect.left, itemRect.bottom - itemRect.top));
boolean drawSelected = false;
if (OS.IsWindowEnabled (handle)) {
LVITEM lvItem = new LVITEM ();
lvItem.mask = OS.LVIF_STATE;
lvItem.stateMask = OS.LVIS_SELECTED;
lvItem.iItem = (int)row;
long result = OS.SendMessage (handle, OS.LVM_GETITEM, 0, lvItem);
boolean selected = (result != 0 && (lvItem.state & OS.LVIS_SELECTED) != 0);
if (selected && (column == 0 || (style & SWT.FULL_SELECTION) != 0)) {
if (OS.GetFocus () == handle || display.getHighContrast ()) {
drawSelected = true;
} else {
drawSelected = (style & SWT.HIDE_SELECTION) == 0;
}
}
}
if (drawSelected) event.detail |= SWT.SELECTED;
sendEvent (SWT.MeasureItem, event);
event.gc = null;
gc.dispose ();
OS.RestoreDC (hDC, nSavedDC);
if (!isDisposed () && !item.isDisposed ()) {
Rectangle boundsInPixels = event.getBoundsInPixels();
if (columnCount == 0) {
int width = (int)OS.SendMessage (handle, OS.LVM_GETCOLUMNWIDTH, 0, 0);
if (boundsInPixels.x + boundsInPixels.width > width) setScrollWidth (boundsInPixels.x + boundsInPixels.width);
}
long empty = OS.SendMessage (handle, OS.LVM_APPROXIMATEVIEWRECT, 0, 0);
long oneItem = OS.SendMessage (handle, OS.LVM_APPROXIMATEVIEWRECT, 1, 0);
int itemHeight = OS.HIWORD (oneItem) - OS.HIWORD (empty);
/*
* Possible recursion: when setItemHeight() is called during
* SWT.MeasureItem event processing with a non-zero table-row
* selection. Refer bug 400174 and 458786
*/
if (!settingItemHeight && boundsInPixels.height > itemHeight) {
settingItemHeight = true;
setItemHeight (boundsInPixels.height);
settingItemHeight = false;
}
}
return event;
}
LRESULT sendMouseDownEvent (int type, int button, int msg, long wParam, long lParam) {
Display display = this.display;
display.captureChanged = false;
if (!sendMouseEvent (type, button, handle, msg, wParam, lParam)) {
if (!display.captureChanged && !isDisposed ()) {
if (OS.GetCapture () != handle) OS.SetCapture (handle);
}
return LRESULT.ZERO;
}
/*
* Feature in Windows. Inside WM_LBUTTONDOWN and WM_RBUTTONDOWN,
* the widget starts a modal loop to determine if the user wants
* to begin a drag/drop operation or marque select. Unfortunately,
* this modal loop eats the corresponding mouse up. The fix is to
* detect the cases when the modal loop has eaten the mouse up and
* issue a fake mouse up.
*
* By observation, when the mouse is clicked anywhere but the check
* box, the widget eats the mouse up. When the mouse is dragged,
* the widget does not eat the mouse up.
*/
LVHITTESTINFO pinfo = new LVHITTESTINFO ();
pinfo.x = OS.GET_X_LPARAM (lParam);
pinfo.y = OS.GET_Y_LPARAM (lParam);
OS.SendMessage (handle, OS.LVM_HITTEST, 0, pinfo);
if ((style & SWT.FULL_SELECTION) == 0) {
if (hooks (SWT.MeasureItem)) {
/*
* Bug in Windows. When LVM_SUBITEMHITTEST is used to hittest
* a point that is above the table, instead of returning -1 to
* indicate that the hittest failed, a negative index is returned.
* The fix is to consider any value that is negative a failure.
*/
if (OS.SendMessage (handle, OS.LVM_SUBITEMHITTEST, 0, pinfo) < 0) {
int count = (int)OS.SendMessage (handle, OS.LVM_GETITEMCOUNT, 0, 0);
if (count != 0) {
RECT rect = new RECT ();
rect.left = OS.LVIR_ICON;
ignoreCustomDraw = true;
long code = OS.SendMessage (handle, OS.LVM_GETITEMRECT, 0, rect);
ignoreCustomDraw = false;
if (code != 0) {
pinfo.x = rect.left;
/*
* Bug in Windows. When LVM_SUBITEMHITTEST is used to hittest
* a point that is above the table, instead of returning -1 to
* indicate that the hittest failed, a negative index is returned.
* The fix is to consider any value that is negative a failure.
*/
OS.SendMessage (handle, OS.LVM_SUBITEMHITTEST, 0, pinfo);
if (pinfo.iItem < 0) pinfo.iItem = -1;
pinfo.flags &= ~(OS.LVHT_ONITEMICON | OS.LVHT_ONITEMLABEL);
}
}
} else {
if (pinfo.iSubItem != 0) pinfo.iItem = -1;
}
}
}
/*
* Force the table to have focus so that when the user
* reselects the focus item, the LVIS_FOCUSED state bits
* for the item will be set. If the user did not click on
* an item, then set focus to the table so that it will
* come to the front and take focus in the work around
* below.
*/
OS.SetFocus (handle);
/*
* Feature in Windows. When the user selects outside of
* a table item, Windows deselects all the items, even
* when the table is multi-select. While not strictly
* wrong, this is unexpected. The fix is to detect the
* case and avoid calling the window proc.
*/
if ((style & SWT.SINGLE) != 0 || hooks (SWT.MouseDown) || hooks (SWT.MouseUp)) {
if (pinfo.iItem == -1) {
if (!display.captureChanged && !isDisposed ()) {
if (OS.GetCapture () != handle) OS.SetCapture (handle);
}
return LRESULT.ZERO;
}
}
/*
* Feature in Windows. When a table item is reselected
* in a single-select table, Windows does not issue a
* WM_NOTIFY because the item state has not changed.
* This is strictly correct but is inconsistent with the
* list widget and other widgets in Windows. The fix is
* to detect the case when an item is reselected and mark
* it as selected.
*/
boolean forceSelect = false;
int count = (int)OS.SendMessage (handle, OS.LVM_GETSELECTEDCOUNT, 0, 0);
if (count == 1 && pinfo.iItem != -1) {
LVITEM lvItem = new LVITEM ();
lvItem.mask = OS.LVIF_STATE;
lvItem.stateMask = OS.LVIS_SELECTED;
lvItem.iItem = pinfo.iItem;
OS.SendMessage (handle, OS.LVM_GETITEM, 0, lvItem);
if ((lvItem.state & OS.LVIS_SELECTED) != 0) {
forceSelect = true;
}
}
/* Determine whether the user has selected an item based on SWT.MeasureItem */
fullRowSelect = false;
if (pinfo.iItem != -1) {
if ((style & SWT.FULL_SELECTION) == 0) {
if (hooks (SWT.MeasureItem)) {
fullRowSelect = hitTestSelection (pinfo.iItem, pinfo.x, pinfo.y);
if (fullRowSelect) {
int flags = OS.LVHT_ONITEMICON | OS.LVHT_ONITEMLABEL;
if ((pinfo.flags & flags) != 0) fullRowSelect = false;
}
}
}
}
/*
* Feature in Windows. Inside WM_LBUTTONDOWN and WM_RBUTTONDOWN,
* the widget starts a modal loop to determine if the user wants
* to begin a drag/drop operation or marque select. This modal
* loop eats mouse events until a drag is detected. The fix is
* to avoid this behavior by only running the drag and drop when
* the event is hooked and the mouse is over an item.
*/
boolean dragDetect = (state & DRAG_DETECT) != 0 && hooks (SWT.DragDetect);
if (!dragDetect) {
int flags = OS.LVHT_ONITEMICON | OS.LVHT_ONITEMLABEL;
dragDetect = pinfo.iItem == -1 || (pinfo.flags & flags) == 0;
if (fullRowSelect) dragDetect = true;
}
/*
* Temporarily set LVS_EX_FULLROWSELECT to allow drag and drop
* and the mouse to manipulate items based on the results of
* the SWT.MeasureItem event.
*/
if (fullRowSelect) {
OS.UpdateWindow (handle);
OS.DefWindowProc (handle, OS.WM_SETREDRAW, 0, 0);
OS.SendMessage (handle, OS.LVM_SETEXTENDEDLISTVIEWSTYLE, OS.LVS_EX_FULLROWSELECT, OS.LVS_EX_FULLROWSELECT);
}
dragStarted = false;
display.dragCancelled = false;
if (!dragDetect) display.runDragDrop = false;
long code = callWindowProc (handle, msg, wParam, lParam, forceSelect);
if (!dragDetect) display.runDragDrop = true;
if (fullRowSelect) {
fullRowSelect = false;
OS.DefWindowProc (handle, OS.WM_SETREDRAW, 1, 0);
OS.SendMessage (handle, OS.LVM_SETEXTENDEDLISTVIEWSTYLE, OS.LVS_EX_FULLROWSELECT, 0);
}
if (dragStarted || display.dragCancelled) {
if (!display.captureChanged && !isDisposed ()) {
if (OS.GetCapture () != handle) OS.SetCapture (handle);
}
} else {
int flags = OS.LVHT_ONITEMLABEL | OS.LVHT_ONITEMICON;
boolean fakeMouseUp = (pinfo.flags & flags) != 0;
if (!fakeMouseUp && (style & SWT.MULTI) != 0) {
fakeMouseUp = (pinfo.flags & OS.LVHT_ONITEMSTATEICON) == 0;
}
if (fakeMouseUp) {
sendMouseEvent (SWT.MouseUp, button, handle, msg, wParam, lParam);
}
}
return new LRESULT (code);
}
void sendPaintItemEvent (TableItem item, NMLVCUSTOMDRAW nmcd) {
long hDC = nmcd.hdc;
GCData data = new GCData ();
data.device = display;
data.font = item.getFont (nmcd.iSubItem);
/*
* Bug in Windows. For some reason, CDIS_SELECTED always set,
* even for items that are not selected. The fix is to get
* the selection state from the item.
*/
LVITEM lvItem = new LVITEM ();
lvItem.mask = OS.LVIF_STATE;
lvItem.stateMask = OS.LVIS_SELECTED;
lvItem.iItem = (int)nmcd.dwItemSpec;
long result = OS.SendMessage (handle, OS.LVM_GETITEM, 0, lvItem);
boolean selected = result != 0 && (lvItem.state & OS.LVIS_SELECTED) != 0;
boolean drawSelected = false, drawBackground = false, drawHot = false;
if (nmcd.iSubItem == 0 || (style & SWT.FULL_SELECTION) != 0) {
drawHot = hotIndex == nmcd.dwItemSpec;
}
if (OS.IsWindowEnabled (handle)) {
if (selected && (nmcd.iSubItem == 0 || (style & SWT.FULL_SELECTION) != 0)) {
if (OS.GetFocus () == handle || display.getHighContrast ()) {
drawSelected = true;
if (selectionForeground != -1) {
data.foreground = selectionForeground;
} else {
data.foreground = OS.GetSysColor (OS.COLOR_HIGHLIGHTTEXT);
}
data.background = OS.GetSysColor (OS.COLOR_HIGHLIGHT);
} else {
drawSelected = (style & SWT.HIDE_SELECTION) == 0;
data.foreground = OS.GetTextColor (hDC);
data.background = OS.GetSysColor (OS.COLOR_3DFACE);
}
if (explorerTheme && selectionForeground == -1) {
int clrText = item.cellForeground != null ? item.cellForeground [nmcd.iSubItem] : -1;
if (clrText == -1) clrText = item.foreground;
data.foreground = clrText != -1 ? clrText : getForegroundPixel ();
}
} else {
int clrText = item.cellForeground != null ? item.cellForeground [nmcd.iSubItem] : -1;
if (clrText == -1) clrText = item.foreground;
int clrTextBk = item.cellBackground != null ? item.cellBackground [nmcd.iSubItem] : -1;
if (clrTextBk == -1) clrTextBk = item.background;
drawBackground = clrTextBk != -1;
/*
* Bug in Windows. When LVM_SETTEXTBKCOLOR, LVM_SETBKCOLOR
* or LVM_SETTEXTCOLOR is used to set the background color of
* the the text or the control, the color is not set in the HDC
* that is provided in Custom Draw. The fix is to explicitly
* set the color.
*/
if (clrText == -1 || clrTextBk == -1) {
Control control = findBackgroundControl ();
if (control == null) control = this;
if (clrText == -1) clrText = control.getForegroundPixel ();
if (clrTextBk == -1) clrTextBk = control.getBackgroundPixel ();
}
data.foreground = clrText != -1 ? clrText : OS.GetTextColor (hDC);
data.background = clrTextBk != -1 ? clrTextBk : OS.GetBkColor (hDC);
}
} else {
data.foreground = OS.GetSysColor (OS.COLOR_GRAYTEXT);
data.background = OS.GetSysColor (OS.COLOR_3DFACE);
}
data.uiState = (int)OS.SendMessage (handle, OS.WM_QUERYUISTATE, 0, 0);
int nSavedDC = OS.SaveDC (hDC);
GC gc = GC.win32_new (hDC, data);
RECT itemRect = item.getBounds ((int)nmcd.dwItemSpec, nmcd.iSubItem, true, true, false, false, hDC);
Event event = new Event ();
event.item = item;
event.gc = gc;
event.index = nmcd.iSubItem;
event.detail |= SWT.FOREGROUND;
// if ((nmcd.uItemState & OS.CDIS_FOCUS) != 0) {
if (OS.SendMessage (handle, OS.LVM_GETNEXTITEM, -1, OS.LVNI_FOCUSED) == nmcd.dwItemSpec) {
if (nmcd.iSubItem == 0 || (style & SWT.FULL_SELECTION) != 0) {
if (handle == OS.GetFocus ()) {
int uiState = (int)OS.SendMessage (handle, OS.WM_QUERYUISTATE, 0, 0);
if ((uiState & OS.UISF_HIDEFOCUS) == 0) event.detail |= SWT.FOCUSED;
}
}
}
if (drawHot) event.detail |= SWT.HOT;
if (drawSelected) event.detail |= SWT.SELECTED;
if (drawBackground) event.detail |= SWT.BACKGROUND;
event.setBoundsInPixels(new Rectangle(itemRect.left, itemRect.top, itemRect.right - itemRect.left, itemRect.bottom - itemRect.top));
RECT cellRect = item.getBounds ((int)nmcd.dwItemSpec, nmcd.iSubItem, true, true, true, true, hDC);
int cellWidth = cellRect.right - cellRect.left;
int cellHeight = cellRect.bottom - cellRect.top;
gc.setClipping (DPIUtil.autoScaleDown(new Rectangle (cellRect.left, cellRect.top, cellWidth, cellHeight)));
sendEvent (SWT.PaintItem, event);
if (data.focusDrawn) focusRect = null;
event.gc = null;
gc.dispose ();
OS.RestoreDC (hDC, nSavedDC);
}
Event sendPaintItemEvent (TableItem item, NMTTCUSTOMDRAW nmcd, int column, RECT itemRect) {
int nSavedDC = OS.SaveDC (nmcd.hdc);
RECT insetRect = toolTipInset (itemRect);
OS.SetWindowOrgEx (nmcd.hdc, insetRect.left, insetRect.top, null);
GCData data = new GCData ();
data.device = display;
data.font = item.getFont (column);
data.foreground = OS.GetTextColor (nmcd.hdc);
data.background = OS.GetBkColor (nmcd.hdc);
data.uiState = (int)OS.SendMessage (handle, OS.WM_QUERYUISTATE, 0, 0);
GC gc = GC.win32_new (nmcd.hdc, data);
Event event = new Event ();
event.item = item;
event.index = column;
event.gc = gc;
event.detail |= SWT.FOREGROUND;
event.setBoundsInPixels(new Rectangle(itemRect.left, itemRect.top, itemRect.right - itemRect.left, itemRect.bottom - itemRect.top));
//gc.setClipping (cellRect.left, cellRect.top, cellWidth, cellHeight);
sendEvent (SWT.PaintItem, event);
event.gc = null;
gc.dispose ();
OS.RestoreDC (nmcd.hdc, nSavedDC);
return event;
}
@Override
void setBackgroundImage (long hBitmap) {
super.setBackgroundImage (hBitmap);
if (hBitmap != 0) {
setBackgroundTransparent (true);
} else {
if (!hooks (SWT.MeasureItem) && !hooks (SWT.EraseItem) && !hooks (SWT.PaintItem)) {
setBackgroundTransparent (false);
}
}
}
@Override
void setBackgroundPixel (int newPixel) {
int oldPixel = (int)OS.SendMessage (handle, OS.LVM_GETBKCOLOR, 0, 0);
if (oldPixel != OS.CLR_NONE) {
if (findImageControl () != null) return;
if (newPixel == -1) newPixel = defaultBackground ();
if (oldPixel != newPixel) {
OS.SendMessage (handle, OS.LVM_SETBKCOLOR, 0, newPixel);
OS.SendMessage (handle, OS.LVM_SETTEXTBKCOLOR, 0, newPixel);
if ((style & SWT.CHECK) != 0) fixCheckboxImageListColor (true);
}
}
/*
* Feature in Windows. When the background color is changed,
* the table does not redraw until the next WM_PAINT. The fix
* is to force a redraw.
*/
OS.InvalidateRect (handle, null, true);
}
void setBackgroundTransparent (boolean transparent) {
/*
* Bug in Windows. When the table has the extended style
* LVS_EX_FULLROWSELECT and LVM_SETBKCOLOR is used with
* CLR_NONE to make the table transparent, Windows draws
* a black rectangle around the first column. The fix is
* clear LVS_EX_FULLROWSELECT.
*
* Feature in Windows. When LVM_SETBKCOLOR is used with
* CLR_NONE and LVM_SETSELECTEDCOLUMN is used to select
* a column, Windows fills the column with the selection
* color, drawing on top of the background image and any
* other custom drawing. The fix is to clear the selected
* column.
*/
int oldPixel = (int)OS.SendMessage (handle, OS.LVM_GETBKCOLOR, 0, 0);
if (transparent) {
if (oldPixel != OS.CLR_NONE) {
/*
* Bug in Windows. When the background color is changed,
* the table does not redraw until the next WM_PAINT. The
* fix is to force a redraw.
*/
OS.SendMessage (handle, OS.LVM_SETBKCOLOR, 0, OS.CLR_NONE);
OS.SendMessage (handle, OS.LVM_SETTEXTBKCOLOR, 0, OS.CLR_NONE);
OS.InvalidateRect (handle, null, true);
/* Clear LVS_EX_FULLROWSELECT */
if (!explorerTheme && (style & SWT.FULL_SELECTION) != 0) {
int bits = OS.LVS_EX_FULLROWSELECT;
OS.SendMessage (handle, OS.LVM_SETEXTENDEDLISTVIEWSTYLE, bits, 0);
}
/* Clear LVM_SETSELECTEDCOLUMN */
if ((sortDirection & (SWT.UP | SWT.DOWN)) != 0) {
if (sortColumn != null && !sortColumn.isDisposed ()) {
OS.SendMessage (handle, OS.LVM_SETSELECTEDCOLUMN, -1, 0);
/*
* Bug in Windows. When LVM_SETSELECTEDCOLUMN is set, Windows
* does not redraw either the new or the previous selected column.
* The fix is to force a redraw.
*/
OS.InvalidateRect (handle, null, true);
}
}
}
} else {
if (oldPixel == OS.CLR_NONE) {
Control control = findBackgroundControl ();
if (control == null) control = this;
if (control.backgroundImage == null) {
int newPixel = control.getBackgroundPixel ();
OS.SendMessage (handle, OS.LVM_SETBKCOLOR, 0, newPixel);
OS.SendMessage (handle, OS.LVM_SETTEXTBKCOLOR, 0, newPixel);
if ((style & SWT.CHECK) != 0) fixCheckboxImageListColor (true);
OS.InvalidateRect (handle, null, true);
}
/* Set LVS_EX_FULLROWSELECT */
if (!explorerTheme && (style & SWT.FULL_SELECTION) != 0) {
if (!hooks (SWT.EraseItem) && !hooks (SWT.PaintItem)) {
int bits = OS.LVS_EX_FULLROWSELECT;
OS.SendMessage (handle, OS.LVM_SETEXTENDEDLISTVIEWSTYLE, bits, bits);
}
}
/* Set LVM_SETSELECTEDCOLUMN */
if ((sortDirection & (SWT.UP | SWT.DOWN)) != 0) {
if (sortColumn != null && !sortColumn.isDisposed ()) {
int column = indexOf (sortColumn);
if (column != -1) {
OS.SendMessage (handle, OS.LVM_SETSELECTEDCOLUMN, column, 0);
/*
* Bug in Windows. When LVM_SETSELECTEDCOLUMN is set, Windows
* does not redraw either the new or the previous selected column.
* The fix is to force a redraw.
*/
OS.InvalidateRect (handle, null, true);
}
}
}
}
}
}
@Override
void setBoundsInPixels (int x, int y, int width, int height, int flags, boolean defer) {
/*
* Bug in Windows. If the table column widths are adjusted
* in WM_SIZE or WM_POSITIONCHANGED using LVM_SETCOLUMNWIDTH
* blank lines may be inserted at the top of the table. A
* call to LVM_GETTOPINDEX will return a negative number (this
* is an impossible result). Once the blank lines appear,
* there seems to be no way to get rid of them, other than
* destroying and recreating the table. The fix is to send
* the resize notification after the size has been changed in
* the operating system.
*
* NOTE: This does not fix the case when the user is resizing
* columns dynamically. There is no fix for this case at this
* time.
*/
setDeferResize (true);
super.setBoundsInPixels (x, y, width, height, flags, false);
setDeferResize (false);
}
/**
* Sets the order that the items in the receiver should
* be displayed in to the given argument which is described
* in terms of the zero-relative ordering of when the items
* were added.
*
* @param order the new order to display the items
*
* @exception SWTException
*
* @exception IllegalArgumentException
*
*
* @see Table#getColumnOrder()
* @see TableColumn#getMoveable()
* @see TableColumn#setMoveable(boolean)
* @see SWT#Move
*
* @since 3.1
*/
public void setColumnOrder (int [] order) {
checkWidget ();
if (order == null) error (SWT.ERROR_NULL_ARGUMENT);
if (columnCount == 0) {
if (order.length != 0) error (SWT.ERROR_INVALID_ARGUMENT);
return;
}
if (order.length != columnCount) error (SWT.ERROR_INVALID_ARGUMENT);
int [] oldOrder = new int [columnCount];
OS.SendMessage (handle, OS.LVM_GETCOLUMNORDERARRAY, columnCount, oldOrder);
boolean reorder = false;
boolean [] seen = new boolean [columnCount];
for (int i=0; i
* Note: This operation is a HINT and is not supported on all platforms. If * the native header has a 3D look and feel (e.g. Windows 7), this method * will cause the header to look FLAT irrespective of the state of the table style. *
* @param color the new color (or null) * * @exception IllegalArgumentExceptiontrue
,
* and marks it invisible otherwise.
* * If one of the receiver's ancestors is not visible or some * other condition makes the receiver not visible, marking * it visible may not actually cause it to be displayed. *
* * @param show the new visibility state * * @exception SWTExceptiontrue
,
* and marks it invisible otherwise. Note that some platforms draw grid lines
* while others may draw alternating row colors.
* * If one of the receiver's ancestors is not visible or some * other condition makes the receiver not visible, marking * it visible may not actually cause it to be displayed. *
* * @param show the new visibility state * * @exception SWTException* Indices that are out of range and duplicate indices are ignored. * If the receiver is single-select and multiple indices are specified, * then all indices are ignored. *
* * @param indices the indices of the items to select * * @exception IllegalArgumentException* If the item is not in the receiver, then it is ignored. *
* * @param item the item to select * * @exception IllegalArgumentException* Items that are not in the receiver are ignored. * If the receiver is single-select and multiple items are specified, * then all items are ignored. *
* * @param items the array of items * * @exception IllegalArgumentException* Indices that are out of range are ignored and no items will be selected * if start is greater than end. * If the receiver is single-select and there is more than one item in the * given range, then all indices are ignored. *
* * @param start the start index of the items to select * @param end the end index of the items to select * * @exception SWTExceptionnull
*
* @exception IllegalArgumentException UP
, DOWN
or NONE
.
*
* @param direction the direction of the sort indicator
*
* @exception SWTException