/*******************************************************************************
* 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
*******************************************************************************/
package org.eclipse.swt.widgets;
import org.eclipse.swt.*;
import org.eclipse.swt.events.*;
import org.eclipse.swt.graphics.*;
import org.eclipse.swt.internal.*;
import org.eclipse.swt.internal.win32.*;
/**
* Instances of this class provide a selectable user interface object
* that displays a hierarchy of items and issues notification when an
* item in the hierarchy is selected.
*
* The item children that may be added to instances of this class
* must be of type TreeItem
.
*
* Style VIRTUAL
is used to create a Tree
whose
* TreeItem
s are to be populated by the client on an on-demand basis
* instead of up-front. This can provide significant performance improvements for
* trees that are very large or for which TreeItem
population is
* expensive (for example, retrieving values from an external source).
*
* Here is an example of using a Tree
with style VIRTUAL
:
*
* final Tree tree = new Tree(parent, SWT.VIRTUAL | SWT.BORDER);
* tree.setItemCount(20);
* tree.addListener(SWT.SetData, new Listener() {
* public void handleEvent(Event event) {
* TreeItem item = (TreeItem)event.item;
* TreeItem parentItem = item.getParentItem();
* String text = null;
* if (parentItem == null) {
* text = "node " + tree.indexOf(item);
* } else {
* text = parentItem.getText() + " - " + parentItem.indexOf(item);
* }
* item.setText(text);
* System.out.println(text);
* item.setItemCount(10);
* }
* });
*
*
* Note that although this class is a subclass of 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.
*
*
* - Styles:
* - SINGLE, MULTI, CHECK, FULL_SELECTION, VIRTUAL, NO_SCROLL
* - Events:
* - Selection, DefaultSelection, Collapse, Expand, SetData, MeasureItem, EraseItem, PaintItem
*
*
* Note: Only one of the styles SINGLE and MULTI may be specified.
*
* IMPORTANT: This class is not intended to be subclassed.
*
*
* @see Tree, TreeItem, TreeColumn snippets
* @see SWT Example: ControlExample
* @see Sample code and further information
* @noextend This class is not intended to be subclassed by clients.
*/
public class Tree extends Composite {
TreeItem [] items;
TreeColumn [] columns;
int columnCount;
ImageList imageList, headerImageList;
TreeItem currentItem;
TreeColumn sortColumn;
RECT focusRect;
long hwndParent, hwndHeader, hAnchor, hInsert, hSelect;
int lastID;
long hFirstIndexOf, hLastIndexOf;
int lastIndexOf, itemCount, sortDirection;
boolean dragStarted, gestureCompleted, insertAfter, shrink, ignoreShrink;
boolean ignoreSelect, ignoreExpand, ignoreDeselect, ignoreResize;
boolean lockSelection, oldSelected, newSelected, ignoreColumnMove;
boolean linesVisible, customDraw, painted, ignoreItemHeight;
boolean ignoreCustomDraw, ignoreDrawForeground, ignoreDrawBackground, ignoreDrawFocus;
boolean ignoreDrawSelection, ignoreDrawHot, ignoreFullSelection, explorerTheme;
boolean createdAsRTL;
boolean headerItemDragging;
int scrollWidth, selectionForeground;
long headerToolTipHandle, itemToolTipHandle;
long lastTimerID = -1;
int lastTimerCount;
int headerBackground = -1;
int headerForeground = -1;
static final boolean ENABLE_TVS_EX_FADEINOUTEXPANDOS = System.getProperty("org.eclipse.swt.internal.win32.enableFadeInOutExpandos") != null;
static final int TIMER_MAX_COUNT = 8;
static final int INSET = 3;
static final int GRID_WIDTH = 1;
static final int SORT_WIDTH = 10;
static final int HEADER_MARGIN = 12;
static final int HEADER_EXTRA = 3;
static final int INCREMENT = 5;
static final int EXPLORER_EXTRA = 2;
static final int DRAG_IMAGE_SIZE = 301;
static final long TreeProc;
static final TCHAR TreeClass = new TCHAR (0, OS.WC_TREEVIEW, true);
static final long HeaderProc;
static final TCHAR HeaderClass = new TCHAR (0, OS.WC_HEADER, true);
static {
WNDCLASS lpWndClass = new WNDCLASS ();
OS.GetClassInfo (0, TreeClass, lpWndClass);
TreeProc = lpWndClass.lpfnWndProc;
OS.GetClassInfo (0, HeaderClass, lpWndClass);
HeaderProc = lpWndClass.lpfnWndProc;
}
/**
* Constructs a new instance of this class given its parent
* and a style value describing its behavior and appearance.
*
* The style value is either one of the style constants defined in
* class 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.
*
*
* @param parent a composite control which will be the parent of the new instance (cannot be null)
* @param style the style of control to construct
*
* @exception IllegalArgumentException
* - ERROR_NULL_ARGUMENT - if the parent is null
*
* @exception SWTException
* - ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the parent
* - ERROR_INVALID_SUBCLASS - if this class is not an allowed subclass
*
*
* @see SWT#SINGLE
* @see SWT#MULTI
* @see SWT#CHECK
* @see SWT#FULL_SELECTION
* @see SWT#VIRTUAL
* @see SWT#NO_SCROLL
* @see Widget#checkSubclass
* @see Widget#getStyle
*/
public Tree (Composite parent, int style) {
super (parent, checkStyle (style));
}
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;
}
/*
* Note: Windows only supports TVS_NOSCROLL and TVS_NOHSCROLL.
*/
if ((style & SWT.H_SCROLL) != 0 && (style & SWT.V_SCROLL) == 0) {
style |= SWT.V_SCROLL;
}
return checkBits (style, SWT.SINGLE, SWT.MULTI, 0, 0, 0, 0);
}
@Override
void _addListener (int eventType, Listener listener) {
super._addListener (eventType, listener);
switch (eventType) {
case SWT.DragDetect: {
if ((state & DRAG_DETECT) != 0) {
int bits = OS.GetWindowLong (handle, OS.GWL_STYLE);
bits &= ~OS.TVS_DISABLEDRAGDROP;
OS.SetWindowLong (handle, OS.GWL_STYLE, bits);
}
break;
}
case SWT.MeasureItem:
case SWT.EraseItem:
case SWT.PaintItem: {
customDraw = true;
style |= SWT.DOUBLE_BUFFERED;
if (isCustomToolTip ()) createItemToolTips ();
OS.SendMessage (handle, OS.TVM_SETSCROLLTIME, 0, 0);
int bits = OS.GetWindowLong (handle, OS.GWL_STYLE);
if (eventType == SWT.MeasureItem) {
/*
* This code is intentionally commented.
*/
// if (explorerTheme) {
// int bits1 = (int)OS.SendMessage (handle, OS.TVM_GETEXTENDEDSTYLE, 0, 0);
// bits1 &= ~OS.TVS_EX_AUTOHSCROLL;
// OS.SendMessage (handle, OS.TVM_SETEXTENDEDSTYLE, 0, bits1);
// }
bits |= OS.TVS_NOHSCROLL;
}
/*
* Feature in Windows. When the tree has the style
* TVS_FULLROWSELECT, the background color for the
* entire row is filled when an item is painted,
* drawing on top of any custom drawing. The fix
* is to clear TVS_FULLROWSELECT.
*/
if ((style & SWT.FULL_SELECTION) != 0) {
if (eventType != SWT.MeasureItem) {
if (!explorerTheme) bits &= ~OS.TVS_FULLROWSELECT;
}
}
if (bits != OS.GetWindowLong (handle, OS.GWL_STYLE)) {
OS.SetWindowLong (handle, OS.GWL_STYLE, bits);
OS.InvalidateRect (handle, null, true);
/*
* Bug in Windows. When TVS_NOHSCROLL is set after items
* have been inserted into the tree, Windows shows the
* scroll bar. The fix is to check for this case and
* explicitly hide the scroll bar.
*/
int count = (int)OS.SendMessage (handle, OS.TVM_GETCOUNT, 0, 0);
if (count != 0 && (bits & OS.TVS_NOHSCROLL) != 0) {
OS.ShowScrollBar (handle, OS.SB_HORZ, false);
}
}
break;
}
}
}
TreeItem _getItem (long hItem) {
TVITEM tvItem = new TVITEM ();
tvItem.mask = OS.TVIF_HANDLE | OS.TVIF_PARAM;
tvItem.hItem = hItem;
if (OS.SendMessage (handle, OS.TVM_GETITEM, 0, tvItem) != 0) {
return _getItem (tvItem.hItem, (int)tvItem.lParam);
}
return null;
}
TreeItem _getItem (long hItem, int id) {
if ((style & SWT.VIRTUAL) == 0) return items [id];
return id != -1 ? items [id] : new TreeItem (this, SWT.NONE, -1, -1, hItem);
}
@Override
void _removeListener (int eventType, Listener listener) {
super._removeListener (eventType, listener);
switch (eventType) {
case SWT.MeasureItem: {
/**
* If H_SCROLL is set, reverting the TVS_NOHSCROLL settings which
* was applied while adding SWT.MeasureItem event Listener.
*/
if ((style & SWT.H_SCROLL) != 0 && (state & DISPOSE_SENT) == 0) {
int bits = OS.GetWindowLong (handle, OS.GWL_STYLE);
bits &= ~OS.TVS_NOHSCROLL;
OS.SetWindowLong (handle, OS.GWL_STYLE, bits);
OS.InvalidateRect (handle, null, true);
}
break;
}
}
}
void _setBackgroundPixel (int newPixel) {
int oldPixel = (int)OS.SendMessage (handle, OS.TVM_GETBKCOLOR, 0, 0);
if (oldPixel != newPixel) {
/*
* Bug in Windows. When TVM_SETBKCOLOR is used more
* than once to set the background color of a tree,
* the background color of the lines and the plus/minus
* does not change to the new color. The fix is to set
* the background color to the default before setting
* the new color.
*/
if (oldPixel != -1) {
OS.SendMessage (handle, OS.TVM_SETBKCOLOR, 0, -1);
}
/* Set the background color */
OS.SendMessage (handle, OS.TVM_SETBKCOLOR, 0, newPixel);
/*
* Feature in Windows. When TVM_SETBKCOLOR is used to
* set the background color of a tree, the plus/minus
* animation draws badly. The fix is to clear the effect.
*/
if (explorerTheme && ENABLE_TVS_EX_FADEINOUTEXPANDOS) {
int bits2 = (int)OS.SendMessage (handle, OS.TVM_GETEXTENDEDSTYLE, 0, 0);
if (newPixel == -1 && findImageControl () == null) {
bits2 |= OS.TVS_EX_FADEINOUTEXPANDOS;
} else {
bits2 &= ~OS.TVS_EX_FADEINOUTEXPANDOS;
}
OS.SendMessage (handle, OS.TVM_SETEXTENDEDSTYLE, 0, bits2);
}
/* Set the checkbox image list */
if ((style & SWT.CHECK) != 0) setCheckboxImageList ();
}
}
/**
* Adds the listener to the collection of listeners who will
* be notified when the user changes the receiver's selection, by sending
* it one of the messages defined in the SelectionListener
* interface.
*
* When widgetSelected
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.
*
*
* @param listener the listener which should be notified when the user changes the receiver's selection
*
* @exception IllegalArgumentException
* - ERROR_NULL_ARGUMENT - if the listener is null
*
* @exception SWTException
* - ERROR_WIDGET_DISPOSED - if the receiver has been disposed
* - ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver
*
*
* @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);
}
/**
* Adds the listener to the collection of listeners who will
* be notified when an item in the receiver is expanded or collapsed
* by sending it one of the messages defined in the TreeListener
* interface.
*
* @param listener the listener which should be notified
*
* @exception IllegalArgumentException
* - ERROR_NULL_ARGUMENT - if the listener is null
*
* @exception SWTException
* - ERROR_WIDGET_DISPOSED - if the receiver has been disposed
* - ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver
*
*
* @see TreeListener
* @see #removeTreeListener
*/
public void addTreeListener(TreeListener listener) {
checkWidget ();
if (listener == null) error (SWT.ERROR_NULL_ARGUMENT);
TypedListener typedListener = new TypedListener (listener);
addListener (SWT.Expand, typedListener);
addListener (SWT.Collapse, typedListener);
}
@Override
long borderHandle () {
return hwndParent != 0 ? hwndParent : handle;
}
LRESULT CDDS_ITEMPOSTPAINT (NMTVCUSTOMDRAW nmcd, long wParam, long lParam) {
if (ignoreCustomDraw) return null;
if (nmcd.left == nmcd.right) return new LRESULT (OS.CDRF_DODEFAULT);
long hDC = nmcd.hdc;
OS.RestoreDC (hDC, -1);
TreeItem item = getItem (nmcd);
/*
* Feature in Windows. When a new tree item is inserted
* using TVM_INSERTITEM and the tree is using custom draw,
* a NM_CUSTOMDRAW is sent before TVM_INSERTITEM returns
* and before the item is added to the items array. The
* fix is to check for null.
*
* NOTE: This only happens on XP with the version 6.00 of
* COMCTL32.DLL,
*/
if (item == null) return null;
/*
* Feature in Windows. Under certain circumstances, Windows
* sends CDDS_ITEMPOSTPAINT for an empty rectangle. This is
* not a problem providing that graphics do not occur outside
* the rectangle. The fix is to test for the rectangle and
* draw nothing.
*
* NOTE: This seems to happen when both I_IMAGECALLBACK
* and LPSTR_TEXTCALLBACK are used at the same time with
* TVM_SETITEM.
*/
if (nmcd.left >= nmcd.right || nmcd.top >= nmcd.bottom) return null;
if (!OS.IsWindowVisible (handle)) return null;
if ((style & SWT.FULL_SELECTION) != 0 || findImageControl () != null || ignoreDrawSelection || explorerTheme) {
OS.SetBkMode (hDC, OS.TRANSPARENT);
}
boolean selected = isItemSelected (nmcd);
boolean hot = explorerTheme && (nmcd.uItemState & OS.CDIS_HOT) != 0;
if (OS.IsWindowEnabled (handle)) {
if (explorerTheme) {
int bits = OS.GetWindowLong (handle, OS.GWL_STYLE);
if ((bits & OS.TVS_TRACKSELECT) != 0) {
if ((style & SWT.FULL_SELECTION) != 0 && (selected || hot)) {
OS.SetTextColor (hDC, OS.GetSysColor (OS.COLOR_WINDOWTEXT));
} else {
OS.SetTextColor (hDC, getForegroundPixel ());
}
}
}
}
int [] order = null;
RECT clientRect = new RECT ();
OS.GetClientRect (scrolledHandle (), clientRect);
if (hwndHeader != 0) {
OS.MapWindowPoints (hwndParent, handle, clientRect, 2);
if (columnCount != 0) {
order = new int [columnCount];
OS.SendMessage (hwndHeader, OS.HDM_GETORDERARRAY, columnCount, order);
}
}
int sortIndex = -1, clrSortBk = -1;
if (OS.IsAppThemed ()) {
if (sortColumn != null && sortDirection != SWT.NONE) {
if (findImageControl () == null) {
sortIndex = indexOf (sortColumn);
clrSortBk = getSortColumnPixel ();
}
}
}
int x = 0;
Point size = null;
for (int i=0; i 0 && hwndHeader != 0) {
HDITEM hdItem = new HDITEM ();
hdItem.mask = OS.HDI_WIDTH;
OS.SendMessage (hwndHeader, OS.HDM_GETITEM, index, hdItem);
width = hdItem.cxy;
}
if (i == 0) {
if ((style & SWT.FULL_SELECTION) != 0) {
boolean clear = !explorerTheme && !ignoreDrawSelection && findImageControl () == null;
if (clear || (selected && !ignoreDrawSelection) || (hot && !ignoreDrawHot)) {
boolean draw = true;
RECT pClipRect = new RECT ();
OS.SetRect (pClipRect, width, nmcd.top, nmcd.right, nmcd.bottom);
if (explorerTheme) {
if (hooks (SWT.EraseItem)) {
RECT itemRect = item.getBounds (index, true, true, false, false, true, hDC);
itemRect.left -= EXPLORER_EXTRA;
itemRect.right += EXPLORER_EXTRA + 1;
pClipRect.left = itemRect.left;
pClipRect.right = itemRect.right;
if (columnCount > 0 && hwndHeader != 0) {
HDITEM hdItem = new HDITEM ();
hdItem.mask = OS.HDI_WIDTH;
OS.SendMessage (hwndHeader, OS.HDM_GETITEM, index, hdItem);
pClipRect.right = Math.min (pClipRect.right, nmcd.left + hdItem.cxy);
}
}
RECT pRect = new RECT ();
OS.SetRect (pRect, nmcd.left, nmcd.top, nmcd.right, nmcd.bottom);
if (columnCount > 0 && hwndHeader != 0) {
int totalWidth = 0;
HDITEM hdItem = new HDITEM ();
hdItem.mask = OS.HDI_WIDTH;
for (int j=0; j clientRect.right - clientRect.left) {
pRect.left = 0;
pRect.right = totalWidth;
} else {
pRect.left = clientRect.left;
pRect.right = clientRect.right;
}
}
draw = false;
long hTheme = OS.OpenThemeData (handle, Display.TREEVIEW);
int iStateId = selected ? OS.TREIS_SELECTED : OS.TREIS_HOT;
if (OS.GetFocus () != handle && selected && !hot) iStateId = OS.TREIS_SELECTEDNOTFOCUS;
OS.DrawThemeBackground (hTheme, hDC, OS.TVP_TREEITEM, iStateId, pRect, pClipRect);
OS.CloseThemeData (hTheme);
}
if (draw) fillBackground (hDC, OS.GetBkColor (hDC), pClipRect);
}
}
}
if (x + width > clientRect.left) {
RECT rect = new RECT (), backgroundRect = null;
boolean drawItem = true, drawText = true, drawImage = true, drawBackground = false;
if (i == 0) {
drawItem = drawImage = drawText = false;
if (findImageControl () != null) {
if (explorerTheme) {
if (OS.IsWindowEnabled (handle) && !hooks (SWT.EraseItem)) {
Image image = null;
if (index == 0) {
image = item.image;
} else {
Image [] images = item.images;
if (images != null) image = images [index];
}
if (image != null) {
Rectangle bounds = image.getBounds (); // Points
if (size == null) size = DPIUtil.autoScaleDown (getImageSize ()); // To Points
if (!ignoreDrawForeground) {
GCData data = new GCData();
data.device = display;
GC gc = GC.win32_new (hDC, data);
RECT iconRect = item.getBounds (index, false, true, false, false, true, hDC); // Pixels
gc.setClipping (DPIUtil.autoScaleDown(new Rectangle(iconRect.left, iconRect.top, iconRect.right - iconRect.left, iconRect.bottom - iconRect.top)));
gc.drawImage (image, 0, 0, bounds.width, bounds.height, DPIUtil.autoScaleDown(iconRect.left), DPIUtil.autoScaleDown(iconRect.top), size.x, size.y);
OS.SelectClipRgn (hDC, 0);
gc.dispose ();
}
}
}
} else {
drawItem = drawText = drawBackground = true;
rect = item.getBounds (index, true, false, false, false, true, hDC);
if (linesVisible) {
rect.right++;
rect.bottom++;
}
}
}
if (selected && !ignoreDrawSelection && !ignoreDrawBackground) {
if (!explorerTheme) fillBackground (hDC, OS.GetBkColor (hDC), rect);
drawBackground = false;
}
backgroundRect = rect;
if (hooks (SWT.EraseItem)) {
drawItem = drawText = drawImage = true;
rect = item.getBounds (index, true, true, false, false, true, hDC);
if ((style & SWT.FULL_SELECTION) != 0) {
backgroundRect = rect;
} else {
backgroundRect = item.getBounds (index, true, false, false, false, true, hDC);
}
}
} else {
selectionForeground = -1;
ignoreDrawForeground = ignoreDrawBackground = ignoreDrawSelection = ignoreDrawFocus = ignoreDrawHot = false;
OS.SetRect (rect, x, nmcd.top, x + width, nmcd.bottom);
backgroundRect = rect;
}
int clrText = -1, clrTextBk = -1;
long hFont = item.fontHandle (index);
if (selectionForeground != -1) clrText = selectionForeground;
if (OS.IsWindowEnabled (handle)) {
boolean drawForeground = false;
if (selected) {
if (i != 0 && (style & SWT.FULL_SELECTION) == 0) {
OS.SetTextColor (hDC, getForegroundPixel ());
OS.SetBkColor (hDC, getBackgroundPixel ());
drawForeground = drawBackground = true;
}
} else {
drawForeground = drawBackground = true;
}
if (drawForeground) {
clrText = item.cellForeground != null ? item.cellForeground [index] : -1;
if (clrText == -1) clrText = item.foreground;
}
if (drawBackground) {
clrTextBk = item.cellBackground != null ? item.cellBackground [index] : -1;
if (clrTextBk == -1) clrTextBk = item.background;
if (clrTextBk == -1 && index == sortIndex) clrTextBk = clrSortBk;
}
} else {
if (clrTextBk == -1 && index == sortIndex) {
drawBackground = true;
clrTextBk = clrSortBk;
}
}
if (explorerTheme) {
if (selected || (nmcd.uItemState & OS.CDIS_HOT) != 0) {
if ((style & SWT.FULL_SELECTION) != 0) {
drawBackground = false;
} else {
if (i == 0) {
drawBackground = false;
if (!hooks (SWT.EraseItem)) drawText = false;
}
}
}
}
if (drawItem) {
if (i != 0) {
if (hooks (SWT.MeasureItem)) {
sendMeasureItemEvent (item, index, hDC, selected ? SWT.SELECTED : 0);
if (isDisposed () || item.isDisposed ()) break;
}
if (hooks (SWT.EraseItem)) {
RECT cellRect = item.getBounds (index, true, true, true, true, true, hDC);
int nSavedDC = OS.SaveDC (hDC);
GCData data = new GCData ();
data.device = display;
data.foreground = OS.GetTextColor (hDC);
data.background = OS.GetBkColor (hDC);
if (!selected || (style & SWT.FULL_SELECTION) == 0) {
if (clrText != -1) data.foreground = clrText;
if (clrTextBk != -1) data.background = clrTextBk;
}
data.font = item.getFont (index);
data.uiState = (int)OS.SendMessage (handle, OS.WM_QUERYUISTATE, 0, 0);
GC gc = GC.win32_new (hDC, data);
Event event = new Event ();
event.item = item;
event.index = index;
event.gc = gc;
event.detail |= SWT.FOREGROUND;
if (clrTextBk != -1) event.detail |= SWT.BACKGROUND;
if ((style & SWT.FULL_SELECTION) != 0) {
if (hot) event.detail |= SWT.HOT;
if (selected) event.detail |= SWT.SELECTED;
if (!explorerTheme) {
//if ((nmcd.uItemState & OS.CDIS_FOCUS) != 0) {
if (OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_CARET, 0) == nmcd.dwItemSpec) {
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;
}
}
}
}
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 newTextClr = data.foreground;
gc.dispose ();
OS.RestoreDC (hDC, nSavedDC);
if (isDisposed () || item.isDisposed ()) break;
if (event.doit) {
ignoreDrawForeground = (event.detail & SWT.FOREGROUND) == 0;
ignoreDrawBackground = (event.detail & SWT.BACKGROUND) == 0;
if ((style & SWT.FULL_SELECTION) != 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 (selected && ignoreDrawSelection) ignoreDrawHot = true;
if ((style & SWT.FULL_SELECTION) != 0) {
if (ignoreDrawSelection) ignoreFullSelection = true;
if (!ignoreDrawSelection || !ignoreDrawHot) {
if (!selected && !hot) {
selectionForeground = OS.GetSysColor (OS.COLOR_HIGHLIGHTTEXT);
} else {
if (!explorerTheme) {
drawBackground = true;
ignoreDrawBackground = false;
if ((handle == OS.GetFocus () || display.getHighContrast ()) && OS.IsWindowEnabled (handle)) {
clrTextBk = OS.GetSysColor (OS.COLOR_HIGHLIGHT);
} else {
clrTextBk = OS.GetSysColor (OS.COLOR_3DFACE);
}
if (!ignoreFullSelection && index == columnCount - 1) {
RECT selectionRect = new RECT ();
OS.SetRect (selectionRect, backgroundRect.left, backgroundRect.top, nmcd.right, backgroundRect.bottom);
backgroundRect = selectionRect;
}
} else {
RECT pRect = new RECT ();
OS.SetRect (pRect, nmcd.left, nmcd.top, nmcd.right, nmcd.bottom);
if (columnCount > 0 && hwndHeader != 0) {
int totalWidth = 0;
HDITEM hdItem = new HDITEM ();
hdItem.mask = OS.HDI_WIDTH;
for (int j=0; j clientRect.right - clientRect.left) {
pRect.left = 0;
pRect.right = totalWidth;
} else {
pRect.left = clientRect.left;
pRect.right = clientRect.right;
}
if (index == columnCount - 1) {
RECT selectionRect = new RECT ();
OS.SetRect (selectionRect, backgroundRect.left, backgroundRect.top, pRect.right, backgroundRect.bottom);
backgroundRect = selectionRect;
}
}
long hTheme = OS.OpenThemeData (handle, Display.TREEVIEW);
int iStateId = selected ? OS.TREIS_SELECTED : OS.TREIS_HOT;
if (OS.GetFocus () != handle && selected && !hot) iStateId = OS.TREIS_SELECTEDNOTFOCUS;
OS.DrawThemeBackground (hTheme, hDC, OS.TVP_TREEITEM, iStateId, pRect, backgroundRect);
OS.CloseThemeData (hTheme);
}
}
} else {
if (selected) {
selectionForeground = newTextClr;
if (!explorerTheme) {
if (clrTextBk == -1 && OS.IsWindowEnabled (handle)) {
Control control = findBackgroundControl ();
if (control == null) control = this;
clrTextBk = control.getBackgroundPixel ();
}
}
}
}
}
}
if (selectionForeground != -1) clrText = selectionForeground;
}
if (!ignoreDrawBackground) {
if (clrTextBk != -1) {
if (drawBackground) fillBackground (hDC, clrTextBk, backgroundRect);
} else {
Control control = findImageControl ();
if (control != null) {
if (i == 0) {
int right = Math.min (rect.right, width);
OS.SetRect (rect, rect.left, rect.top, right, rect.bottom);
if (drawBackground) fillImageBackground (hDC, control, rect, 0, 0);
} else {
if (drawBackground) fillImageBackground (hDC, control, rect, 0, 0);
}
}
}
}
rect.left += INSET - 1;
if (drawImage) {
Image image = null;
if (index == 0) {
image = item.image;
} else {
Image [] images = item.images;
if (images != null) image = images [index];
}
int inset = i != 0 ? INSET : 0;
int offset = i != 0 ? INSET : INSET + 2;
if (image != null) {
Rectangle bounds = image.getBounds (); // Points
if (size == null) size = DPIUtil.autoScaleDown (getImageSize ()); // To Points
if (!ignoreDrawForeground) {
//int y1 = rect.top + (index == 0 ? (getItemHeight () - size.y) / 2 : 0);
int y1 = rect.top + DPIUtil.autoScaleUp((getItemHeight () - size.y) / 2);
int x1 = Math.max (rect.left, rect.left - inset + 1);
GCData data = new GCData();
data.device = display;
GC gc = GC.win32_new (hDC, data);
gc.setClipping (DPIUtil.autoScaleDown(new Rectangle(x1, rect.top, rect.right - x1, rect.bottom - rect.top)));
gc.drawImage (image, 0, 0, bounds.width, bounds.height, DPIUtil.autoScaleDown(x1), DPIUtil.autoScaleDown(y1), size.x, size.y);
OS.SelectClipRgn (hDC, 0);
gc.dispose ();
}
OS.SetRect (rect, rect.left + DPIUtil.autoScaleUp(size.x) + offset, rect.top, rect.right - inset, rect.bottom);
} else {
if (i == 0) {
if (OS.SendMessage (handle, OS.TVM_GETIMAGELIST, OS.TVSIL_NORMAL, 0) != 0) {
if (size == null) size = getImageSize ();
rect.left = Math.min (rect.left + size.x + offset, rect.right);
}
} else {
OS.SetRect (rect, rect.left + offset, rect.top, rect.right - inset, rect.bottom);
}
}
}
if (drawText) {
/*
* Bug in Windows. When DrawText() is used with DT_VCENTER
* and DT_ENDELLIPSIS, the ellipsis can draw outside of the
* rectangle when the rectangle is empty. The fix is avoid
* all text drawing for empty rectangles.
*/
if (rect.left < rect.right) {
String string = null;
if (index == 0) {
string = item.text;
} else {
String [] strings = item.strings;
if (strings != null) string = strings [index];
}
if (string != null) {
if (hFont != -1) hFont = OS.SelectObject (hDC, hFont);
if (clrText != -1) clrText = OS.SetTextColor (hDC, clrText);
if (clrTextBk != -1) clrTextBk = OS.SetBkColor (hDC, clrTextBk);
int flags = OS.DT_NOPREFIX | OS.DT_SINGLELINE | OS.DT_VCENTER;
if (i != 0) flags |= OS.DT_ENDELLIPSIS;
TreeColumn column = columns != null ? columns [index] : null;
if (column != null) {
if ((column.style & SWT.CENTER) != 0) flags |= OS.DT_CENTER;
if ((column.style & SWT.RIGHT) != 0) flags |= OS.DT_RIGHT;
}
if ((string != null) && (string.length() > Item.TEXT_LIMIT)) {
string = string.substring(0, Item.TEXT_LIMIT - Item.ELLIPSIS.length()) + Item.ELLIPSIS;
}
char [] buffer = string.toCharArray ();
if (!ignoreDrawForeground) OS.DrawText (hDC, buffer, buffer.length, rect, flags);
OS.DrawText (hDC, buffer, buffer.length, rect, flags | OS.DT_CALCRECT);
if (hFont != -1) hFont = OS.SelectObject (hDC, hFont);
if (clrText != -1) clrText = OS.SetTextColor (hDC, clrText);
if (clrTextBk != -1) clrTextBk = OS.SetBkColor (hDC, clrTextBk);
}
}
}
}
if (selectionForeground != -1) clrText = selectionForeground;
if (hooks (SWT.PaintItem)) {
RECT itemRect = item.getBounds (index, true, true, false, false, false, hDC);
int nSavedDC = OS.SaveDC (hDC);
GCData data = new GCData ();
data.device = display;
data.font = item.getFont (index);
data.foreground = OS.GetTextColor (hDC);
data.background = OS.GetBkColor (hDC);
if (selected && (style & SWT.FULL_SELECTION) != 0) {
if (selectionForeground != -1) data.foreground = selectionForeground;
} else {
if (clrText != -1) data.foreground = clrText;
if (clrTextBk != -1) data.background = clrTextBk;
}
data.uiState = (int)OS.SendMessage (handle, OS.WM_QUERYUISTATE, 0, 0);
GC gc = GC.win32_new (hDC, data);
Event event = new Event ();
event.item = item;
event.index = index;
event.gc = gc;
event.detail |= SWT.FOREGROUND;
if (clrTextBk != -1) event.detail |= SWT.BACKGROUND;
if (hot) event.detail |= SWT.HOT;
if (selected && (i == 0 /*nmcd.iSubItem == 0*/ || (style & SWT.FULL_SELECTION) != 0)) {
event.detail |= SWT.SELECTED;
}
if (!explorerTheme) {
//if ((nmcd.uItemState & OS.CDIS_FOCUS) != 0) {
if (OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_CARET, 0) == nmcd.dwItemSpec) {
if (i == 0 /*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;
}
}
}
}
event.setBoundsInPixels(new Rectangle(itemRect.left, itemRect.top, itemRect.right - itemRect.left, itemRect.bottom - itemRect.top));
RECT cellRect = item.getBounds (index, true, 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);
if (isDisposed () || item.isDisposed ()) break;
}
}
x += width;
if (x > clientRect.right) break;
}
if (linesVisible) {
if ((style & SWT.FULL_SELECTION) != 0) {
if (columnCount != 0) {
HDITEM hdItem = new HDITEM ();
hdItem.mask = OS.HDI_WIDTH;
OS.SendMessage (hwndHeader, OS.HDM_GETITEM, 0, hdItem);
RECT rect = new RECT ();
OS.SetRect (rect, nmcd.left + hdItem.cxy, nmcd.top, nmcd.right, nmcd.bottom);
OS.DrawEdge (hDC, rect, OS.BDR_SUNKENINNER, OS.BF_BOTTOM);
}
}
RECT rect = new RECT ();
OS.SetRect (rect, nmcd.left, nmcd.top, nmcd.right, nmcd.bottom);
OS.DrawEdge (hDC, rect, OS.BDR_SUNKENINNER, OS.BF_BOTTOM);
}
if (!ignoreDrawFocus && focusRect != null) {
OS.DrawFocusRect (hDC, focusRect);
focusRect = null;
} else {
if (!explorerTheme) {
if (handle == OS.GetFocus ()) {
int uiState = (int)OS.SendMessage (handle, OS.WM_QUERYUISTATE, 0, 0);
if ((uiState & OS.UISF_HIDEFOCUS) == 0) {
long hItem = OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_CARET, 0);
if (hItem == item.handle) {
if (!ignoreDrawFocus && findImageControl () != null) {
if ((style & SWT.FULL_SELECTION) != 0) {
RECT focusRect = new RECT ();
OS.SetRect (focusRect, 0, nmcd.top, clientRect.right + 1, nmcd.bottom);
OS.DrawFocusRect (hDC, focusRect);
} else {
int index = (int)OS.SendMessage (hwndHeader, OS.HDM_ORDERTOINDEX, 0, 0);
RECT focusRect = item.getBounds (index, true, false, false, false, false, hDC);
RECT clipRect = item.getBounds (index, true, false, false, false, true, hDC);
OS.IntersectClipRect (hDC, clipRect.left, clipRect.top, clipRect.right, clipRect.bottom);
OS.DrawFocusRect (hDC, focusRect);
OS.SelectClipRgn (hDC, 0);
}
}
}
}
}
}
}
return new LRESULT (OS.CDRF_DODEFAULT);
}
LRESULT CDDS_ITEMPREPAINT (NMTVCUSTOMDRAW nmcd, long wParam, long lParam) {
/*
* Even when custom draw is being ignored, the font needs
* to be selected into the HDC so that the item bounds are
* measured correctly.
*/
TreeItem item = getItem (nmcd);
/*
* Feature in Windows. When a new tree item is inserted
* using TVM_INSERTITEM and the tree is using custom draw,
* a NM_CUSTOMDRAW is sent before TVM_INSERTITEM returns
* and before the item is added to the items array. The
* fix is to check for null.
*
* NOTE: This only happens on XP with the version 6.00 of
* COMCTL32.DLL,
*/
if (item == null) return null;
long hDC = nmcd.hdc;
int index = hwndHeader != 0 ? (int)OS.SendMessage (hwndHeader, OS.HDM_ORDERTOINDEX, 0, 0) : 0;
long hFont = item.fontHandle (index);
if (hFont != -1) OS.SelectObject (hDC, hFont);
if (ignoreCustomDraw || nmcd.left == nmcd.right) {
return new LRESULT (hFont == -1 ? OS.CDRF_DODEFAULT : OS.CDRF_NEWFONT);
}
RECT clipRect = null;
if (columnCount != 0) {
clipRect = new RECT ();
HDITEM hdItem = new HDITEM ();
hdItem.mask = OS.HDI_WIDTH;
OS.SendMessage (hwndHeader, OS.HDM_GETITEM, index, hdItem);
OS.SetRect (clipRect, nmcd.left, nmcd.top, nmcd.left + hdItem.cxy, nmcd.bottom);
}
int clrText = -1, clrTextBk = -1;
if (OS.IsWindowEnabled (handle)) {
clrText = item.cellForeground != null ? item.cellForeground [index] : -1;
if (clrText == -1) clrText = item.foreground;
clrTextBk = item.cellBackground != null ? item.cellBackground [index] : -1;
if (clrTextBk == -1) clrTextBk = item.background;
}
int clrSortBk = -1;
if (OS.IsAppThemed ()) {
if (sortColumn != null && sortDirection != SWT.NONE) {
if (findImageControl () == null) {
if (indexOf (sortColumn) == index) {
clrSortBk = getSortColumnPixel ();
if (clrTextBk == -1) clrTextBk = clrSortBk;
}
}
}
}
boolean selected = isItemSelected (nmcd);
boolean hot = explorerTheme && (nmcd.uItemState & OS.CDIS_HOT) != 0;
boolean focused = explorerTheme && (nmcd.uItemState & OS.CDIS_FOCUS) != 0;
if (OS.IsWindowVisible (handle) && nmcd.left < nmcd.right && nmcd.top < nmcd.bottom) {
if (hFont != -1) OS.SelectObject (hDC, hFont);
if (linesVisible) {
RECT rect = new RECT ();
OS.SetRect (rect, nmcd.left, nmcd.top, nmcd.right, nmcd.bottom);
OS.DrawEdge (hDC, rect, OS.BDR_SUNKENINNER, OS.BF_BOTTOM);
}
//TODO - BUG - measure and erase sent when first column is clipped
Event measureEvent = null;
Rectangle boundsInPixels = null;
if (hooks (SWT.MeasureItem)) {
measureEvent = sendMeasureItemEvent (item, index, hDC, selected ? SWT.SELECTED : 0);
boundsInPixels = measureEvent.getBoundsInPixels ();
if (isDisposed () || item.isDisposed ()) return null;
}
selectionForeground = -1;
ignoreDrawForeground = ignoreDrawBackground = ignoreDrawSelection = ignoreDrawFocus = ignoreDrawHot = ignoreFullSelection = false;
if (hooks (SWT.EraseItem)) {
RECT rect = new RECT ();
OS.SetRect (rect, nmcd.left, nmcd.top, nmcd.right, nmcd.bottom);
RECT cellRect = item.getBounds (index, true, true, true, true, true, hDC);
if (clrSortBk != -1) {
drawBackground (hDC, cellRect, clrSortBk, 0, 0);
} else {
if (OS.IsWindowEnabled (handle) || findImageControl () != null) {
drawBackground (hDC, rect);
} else {
fillBackground (hDC, OS.GetBkColor (hDC), rect);
}
}
int nSavedDC = OS.SaveDC (hDC);
GCData data = new GCData ();
data.device = display;
if (selected && explorerTheme) {
data.foreground = OS.GetSysColor (OS.COLOR_WINDOWTEXT);
} else {
data.foreground = OS.GetTextColor (hDC);
}
data.background = OS.GetBkColor (hDC);
if (!selected) {
if (clrText != -1) data.foreground = clrText;
if (clrTextBk != -1) data.background = clrTextBk;
}
data.uiState = (int)OS.SendMessage (handle, OS.WM_QUERYUISTATE, 0, 0);
data.font = item.getFont (index);
GC gc = GC.win32_new (hDC, data);
Event event = new Event ();
event.index = index;
event.item = item;
event.gc = gc;
event.detail |= SWT.FOREGROUND;
if (clrTextBk != -1) event.detail |= SWT.BACKGROUND;
if (hot) event.detail |= SWT.HOT;
if (selected) event.detail |= SWT.SELECTED;
//if ((nmcd.uItemState & OS.CDIS_FOCUS) != 0) {
if (OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_CARET, 0) == nmcd.dwItemSpec) {
if (handle == OS.GetFocus ()) {
int uiState = (int)OS.SendMessage (handle, OS.WM_QUERYUISTATE, 0, 0);
if ((uiState & OS.UISF_HIDEFOCUS) == 0) {
if (!explorerTheme || !selected) {
focused = true;
event.detail |= SWT.FOCUSED;
}
}
}
}
Rectangle boundsInPixels2 = new Rectangle (cellRect.left, cellRect.top, cellRect.right - cellRect.left, cellRect.bottom - cellRect.top);
event.setBoundsInPixels (boundsInPixels2);
gc.setClipping (DPIUtil.autoScaleDown (boundsInPixels2));
sendEvent (SWT.EraseItem, event);
event.gc = null;
int newTextClr = data.foreground;
gc.dispose ();
OS.RestoreDC (hDC, nSavedDC);
if (isDisposed () || item.isDisposed ()) return null;
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 (selected && ignoreDrawSelection) ignoreDrawHot = true;
if (!ignoreDrawBackground && clrTextBk != -1) {
boolean draw = !selected && !hot;
if (!explorerTheme && selected) draw = !ignoreDrawSelection;
if (draw) {
if (columnCount == 0) {
if ((style & SWT.FULL_SELECTION) != 0) {
fillBackground (hDC, clrTextBk, rect);
} else {
RECT textRect = item.getBounds (index, true, false, false, false, true, hDC);
if (measureEvent != null) {
textRect.right = Math.min (cellRect.right, boundsInPixels.x + boundsInPixels.width);
}
fillBackground (hDC, clrTextBk, textRect);
}
} else {
fillBackground (hDC, clrTextBk, cellRect);
}
}
}
if (ignoreDrawSelection) ignoreFullSelection = true;
if (!ignoreDrawSelection || !ignoreDrawHot) {
if (!selected && !hot) {
selectionForeground = clrText = OS.GetSysColor (OS.COLOR_HIGHLIGHTTEXT);
}
if (explorerTheme) {
if ((style & SWT.FULL_SELECTION) == 0) {
RECT pRect = item.getBounds (index, true, true, false, false, false, hDC);
RECT pClipRect = item.getBounds (index, true, true, true, false, true, hDC);
if (measureEvent != null) {
pRect.right = Math.min (pClipRect.right, boundsInPixels.x + boundsInPixels.width);
} else {
pRect.right += EXPLORER_EXTRA;
pClipRect.right += EXPLORER_EXTRA;
}
pRect.left -= EXPLORER_EXTRA;
pClipRect.left -= EXPLORER_EXTRA;
long hTheme = OS.OpenThemeData (handle, Display.TREEVIEW);
int iStateId = selected ? OS.TREIS_SELECTED : OS.TREIS_HOT;
if (OS.GetFocus () != handle && selected && !hot) iStateId = OS.TREIS_SELECTEDNOTFOCUS;
OS.DrawThemeBackground (hTheme, hDC, OS.TVP_TREEITEM, iStateId, pRect, pClipRect);
OS.CloseThemeData (hTheme);
}
} else {
/*
* Feature in Windows. When the tree has the style
* TVS_FULLROWSELECT, the background color for the
* entire row is filled when an item is painted,
* drawing on top of any custom drawing. The fix
* is to emulate TVS_FULLROWSELECT.
*/
if ((style & SWT.FULL_SELECTION) != 0) {
if ((style & SWT.FULL_SELECTION) != 0 && columnCount == 0) {
fillBackground (hDC, OS.GetBkColor (hDC), rect);
} else {
fillBackground (hDC, OS.GetBkColor (hDC), cellRect);
}
} else {
RECT textRect = item.getBounds (index, true, false, false, false, true, hDC);
if (measureEvent != null) {
textRect.right = Math.min (cellRect.right, boundsInPixels.x + boundsInPixels.width);
}
fillBackground (hDC, OS.GetBkColor (hDC), textRect);
}
}
} else {
if (selected || hot) {
selectionForeground = clrText = newTextClr;
ignoreDrawSelection = ignoreDrawHot = true;
}
if (explorerTheme) {
nmcd.uItemState |= OS.CDIS_DISABLED;
/*
* Feature in Windows. On Vista only, when the text
* color is unchanged and an item is asked to draw
* disabled, it uses the disabled color. The fix is
* to modify the color so that is it no longer equal.
*/
int newColor = clrText == -1 ? getForegroundPixel () : clrText;
if (nmcd.clrText == newColor) {
nmcd.clrText |= 0x20000000;
if (nmcd.clrText == newColor) nmcd.clrText &= ~0x20000000;
} else {
nmcd.clrText = newColor;
}
OS.MoveMemory (lParam, nmcd, NMTVCUSTOMDRAW.sizeof);
}
}
if (focused && !ignoreDrawFocus && (style & SWT.FULL_SELECTION) == 0) {
RECT textRect = item.getBounds (index, true, explorerTheme, false, false, true, hDC);
if (measureEvent != null) {
textRect.right = Math.min (cellRect.right, boundsInPixels.x + boundsInPixels.width);
}
nmcd.uItemState &= ~OS.CDIS_FOCUS;
OS.MoveMemory (lParam, nmcd, NMTVCUSTOMDRAW.sizeof);
focusRect = textRect;
}
if (explorerTheme) {
if (selected || (hot && ignoreDrawHot)) nmcd.uItemState &= ~OS.CDIS_HOT;
OS.MoveMemory (lParam, nmcd, NMTVCUSTOMDRAW.sizeof);
}
RECT itemRect = item.getBounds (index, true, true, false, false, false, hDC);
OS.SaveDC (hDC);
OS.SelectClipRgn (hDC, 0);
if (explorerTheme) {
itemRect.left -= EXPLORER_EXTRA;
itemRect.right += EXPLORER_EXTRA;
}
//TODO - bug in Windows selection or SWT itemRect
/*if (selected)*/ itemRect.right++;
if (linesVisible) itemRect.bottom++;
if (clipRect != null) {
OS.IntersectClipRect (hDC, clipRect.left, clipRect.top, clipRect.right, clipRect.bottom);
}
OS.ExcludeClipRect (hDC, itemRect.left, itemRect.top, itemRect.right, itemRect.bottom);
return new LRESULT (OS.CDRF_DODEFAULT | OS.CDRF_NOTIFYPOSTPAINT);
}
/*
* Feature in Windows. When the tree has the style
* TVS_FULLROWSELECT, the background color for the
* entire row is filled when an item is painted,
* drawing on top of any custom drawing. The fix
* is to emulate TVS_FULLROWSELECT.
*/
if ((style & SWT.FULL_SELECTION) != 0) {
int bits = OS.GetWindowLong (handle, OS.GWL_STYLE);
if ((bits & OS.TVS_FULLROWSELECT) == 0) {
RECT rect = new RECT ();
OS.SetRect (rect, nmcd.left, nmcd.top, nmcd.right, nmcd.bottom);
if (selected) {
fillBackground (hDC, OS.GetBkColor (hDC), rect);
} else {
if (OS.IsWindowEnabled (handle)) drawBackground (hDC, rect);
}
nmcd.uItemState &= ~OS.CDIS_FOCUS;
OS.MoveMemory (lParam, nmcd, NMTVCUSTOMDRAW.sizeof);
}
}
}
LRESULT result = null;
if (clrText == -1 && clrTextBk == -1 && hFont == -1) {
result = new LRESULT (OS.CDRF_DODEFAULT | OS.CDRF_NOTIFYPOSTPAINT);
} else {
result = new LRESULT (OS.CDRF_NEWFONT | OS.CDRF_NOTIFYPOSTPAINT);
if (hFont != -1) OS.SelectObject (hDC, hFont);
if (OS.IsWindowEnabled (handle) && OS.IsWindowVisible (handle)) {
/*
* Feature in Windows. Windows does not fill the entire cell
* with the background color when TVS_FULLROWSELECT is not set.
* The fix is to fill the cell with the background color.
*/
if (clrTextBk != -1) {
int bits = OS.GetWindowLong (handle, OS.GWL_STYLE);
if ((bits & OS.TVS_FULLROWSELECT) == 0) {
if (columnCount != 0 && hwndHeader != 0) {
RECT rect = new RECT ();
HDITEM hdItem = new HDITEM ();
hdItem.mask = OS.HDI_WIDTH;
OS.SendMessage (hwndHeader, OS.HDM_GETITEM, index, hdItem);
OS.SetRect (rect, nmcd.left, nmcd.top, nmcd.left + hdItem.cxy, nmcd.bottom);
if (!OS.IsAppThemed ()) {
RECT itemRect = new RECT ();
if (OS.TreeView_GetItemRect (handle, item.handle, itemRect, true)) {
rect.left = Math.min (itemRect.left, rect.right);
}
}
if ((style & SWT.FULL_SELECTION) != 0) {
if (!selected) fillBackground (hDC, clrTextBk, rect);
} else {
fillBackground (hDC, clrTextBk, rect);
}
} else {
if ((style & SWT.FULL_SELECTION) != 0) {
RECT rect = new RECT ();
OS.SetRect (rect, nmcd.left, nmcd.top, nmcd.right, nmcd.bottom);
if (!selected) fillBackground (hDC, clrTextBk, rect);
}
}
}
}
if (!selected) {
nmcd.clrText = clrText == -1 ? getForegroundPixel () : clrText;
nmcd.clrTextBk = clrTextBk == -1 ? getBackgroundPixel () : clrTextBk;
OS.MoveMemory (lParam, nmcd, NMTVCUSTOMDRAW.sizeof);
}
}
}
if (OS.IsWindowEnabled (handle)) {
/*
* On Vista only, when an item is asked to draw disabled,
* the background of the text is not filled with the
* background color of the tree. This is true for both
* regular and full selection trees. In order to draw a
* background image, mark the item as disabled using
* CDIS_DISABLED (when not selected) and set the text
* to the regular text color to avoid drawing disabled.
*/
if (explorerTheme) {
if (findImageControl () != null) {
if (!selected && (nmcd.uItemState & (OS.CDIS_HOT | OS.CDIS_SELECTED)) == 0) {
nmcd.uItemState |= OS.CDIS_DISABLED;
/*
* Feature in Windows. On Vista only, when the text
* color is unchanged and an item is asked to draw
* disabled, it uses the disabled color. The fix is
* to modify the color so it is no longer equal.
*/
int newColor = clrText == -1 ? getForegroundPixel () : clrText;
if (nmcd.clrText == newColor) {
nmcd.clrText |= 0x20000000;
if (nmcd.clrText == newColor) nmcd.clrText &= ~0x20000000;
} else {
nmcd.clrText = newColor;
}
OS.MoveMemory (lParam, nmcd, NMTVCUSTOMDRAW.sizeof);
if (clrTextBk != -1) {
if ((style & SWT.FULL_SELECTION) != 0) {
RECT rect = new RECT ();
if (columnCount != 0) {
HDITEM hdItem = new HDITEM ();
hdItem.mask = OS.HDI_WIDTH;
OS.SendMessage (hwndHeader, OS.HDM_GETITEM, index, hdItem);
OS.SetRect (rect, nmcd.left, nmcd.top, nmcd.left + hdItem.cxy, nmcd.bottom);
} else {
OS.SetRect (rect, nmcd.left, nmcd.top, nmcd.right, nmcd.bottom);
}
fillBackground (hDC, clrTextBk, rect);
} else {
RECT textRect = item.getBounds (index, true, false, true, false, true, hDC);
fillBackground (hDC, clrTextBk, textRect);
}
}
}
}
}
} else {
/*
* Feature in Windows. When the tree is disabled, it draws
* with a gray background over the sort column. The fix is
* to fill the background with the sort column color.
*/
if (clrSortBk != -1) {
RECT rect = new RECT ();
HDITEM hdItem = new HDITEM ();
hdItem.mask = OS.HDI_WIDTH;
OS.SendMessage (hwndHeader, OS.HDM_GETITEM, index, hdItem);
OS.SetRect (rect, nmcd.left, nmcd.top, nmcd.left + hdItem.cxy, nmcd.bottom);
fillBackground (hDC, clrSortBk, rect);
}
}
OS.SaveDC (hDC);
if (clipRect != null) {
long hRgn = OS.CreateRectRgn (clipRect.left, clipRect.top, clipRect.right, clipRect.bottom);
POINT lpPoint = new POINT ();
OS.GetWindowOrgEx (hDC, lpPoint);
OS.OffsetRgn (hRgn, -lpPoint.x, -lpPoint.y);
OS.SelectClipRgn (hDC, hRgn);
OS.DeleteObject (hRgn);
}
return result;
}
LRESULT CDDS_POSTPAINT (NMTVCUSTOMDRAW nmcd, long wParam, long lParam) {
if (ignoreCustomDraw) return null;
if (OS.IsWindowVisible (handle)) {
if (OS.IsAppThemed ()) {
if (sortColumn != null && sortDirection != SWT.NONE) {
if (findImageControl () == null) {
int index = indexOf (sortColumn);
if (index != -1) {
int top = nmcd.top;
/*
* Bug in Windows. For some reason, during a collapse,
* when TVM_GETNEXTITEM is sent with TVGN_LASTVISIBLE
* and the collapse causes the item being collapsed
* to become the last visible item in the tree, the
* message takes a long time to process. In order for
* the slowness to happen, the children of the item
* must have children. Times of up to 11 seconds have
* been observed with 23 children, each having one
* child. The fix is to use the bottom partially
* visible item rather than the last possible item
* that could be visible.
*
* NOTE: This problem only happens on Vista during
* WM_NOTIFY with NM_CUSTOMDRAW and CDDS_POSTPAINT.
*/
long hItem = 0;
if (OS.WIN32_VERSION >= OS.VERSION (6, 0)) {
hItem = getBottomItem ();
} else {
hItem = OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_LASTVISIBLE, 0);
}
if (hItem != 0) {
RECT rect = new RECT ();
if (OS.TreeView_GetItemRect (handle, hItem, rect, false)) {
top = rect.bottom;
}
}
RECT rect = new RECT ();
OS.SetRect (rect, nmcd.left, top, nmcd.right, nmcd.bottom);
RECT headerRect = new RECT ();
OS.SendMessage (hwndHeader, OS.HDM_GETITEMRECT, index, headerRect);
rect.left = headerRect.left;
rect.right = headerRect.right;
fillBackground (nmcd.hdc, getSortColumnPixel (), rect);
}
}
}
}
if (linesVisible) {
long hDC = nmcd.hdc;
if (hwndHeader != 0) {
int x = 0;
RECT rect = new RECT ();
HDITEM hdItem = new HDITEM ();
hdItem.mask = OS.HDI_WIDTH;
for (int i=0; i= OS.VERSION (6, 0)) {
hItem = getBottomItem ();
} else {
hItem = OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_LASTVISIBLE, 0);
}
if (hItem != 0) {
if (OS.TreeView_GetItemRect (handle, hItem, rect, false)) {
height = rect.bottom - rect.top;
}
}
if (height == 0) {
height = (int)OS.SendMessage (handle, OS.TVM_GETITEMHEIGHT, 0, 0);
OS.GetClientRect (handle, rect);
OS.SetRect (rect, rect.left, rect.top, rect.right, rect.top + height);
OS.DrawEdge (hDC, rect, OS.BDR_SUNKENINNER, OS.BF_BOTTOM);
}
if (height != 0) {
while (rect.bottom < nmcd.bottom) {
int top = rect.top + height;
OS.SetRect (rect, rect.left, top, rect.right, top + height);
OS.DrawEdge (hDC, rect, OS.BDR_SUNKENINNER, OS.BF_BOTTOM);
}
}
}
}
return new LRESULT (OS.CDRF_DODEFAULT);
}
LRESULT CDDS_PREPAINT (NMTVCUSTOMDRAW nmcd, long wParam, long lParam) {
if (explorerTheme) {
if ((OS.IsWindowEnabled (handle) && hooks (SWT.EraseItem)) || hasCustomBackground() || findImageControl () != null) {
RECT rect = new RECT ();
OS.SetRect (rect, nmcd.left, nmcd.top, nmcd.right, nmcd.bottom);
drawBackground (nmcd.hdc, rect);
}
}
return new LRESULT (OS.CDRF_NOTIFYITEMDRAW | OS.CDRF_NOTIFYPOSTPAINT);
}
@Override
long callWindowProc (long hwnd, int msg, long wParam, long lParam) {
if (handle == 0) return 0;
if (hwndParent != 0 && hwnd == hwndParent) {
return OS.DefWindowProc (hwnd, msg, wParam, lParam);
}
if (hwndHeader != 0 && hwnd == hwndHeader) {
return OS.CallWindowProc (HeaderProc, hwnd, msg, wParam, lParam);
}
switch (msg) {
case OS.WM_SETFOCUS: {
/*
* Feature in Windows. When a tree control processes WM_SETFOCUS,
* if no item is selected, the first item in the tree is selected.
* This is unexpected and might clear the previous selection.
* The fix is to detect that there is no selection and set it to
* the first visible item in the tree. If the item was not selected,
* only the focus is assigned.
*/
if ((style & SWT.SINGLE) != 0) break;
long hItem = OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_CARET, 0);
if (hItem == 0) {
hItem = OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_FIRSTVISIBLE, 0);
if (hItem != 0) {
TVITEM tvItem = new TVITEM ();
tvItem.mask = OS.TVIF_HANDLE | OS.TVIF_STATE;
tvItem.hItem = hItem;
OS.SendMessage (handle, OS.TVM_GETITEM, 0, tvItem);
hSelect = hItem;
ignoreDeselect = ignoreSelect = lockSelection = true;
OS.SendMessage (handle, OS.TVM_SELECTITEM, OS.TVGN_CARET, hItem);
ignoreDeselect = ignoreSelect = lockSelection = false;
hSelect = 0;
if ((tvItem.state & OS.TVIS_SELECTED) == 0) {
OS.SendMessage (handle, OS.TVM_SETITEM, 0, tvItem);
}
}
}
break;
}
}
long hItem = 0;
boolean redraw = false;
switch (msg) {
/* Keyboard messages */
case OS.WM_KEYDOWN:
if (wParam == OS.VK_CONTROL || wParam == OS.VK_SHIFT) break;
//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_SIZE:
redraw = findImageControl () != null && getDrawing () && OS.IsWindowVisible (handle);
if (redraw) OS.DefWindowProc (handle, OS.WM_SETREDRAW, 0, 0);
//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) {
hItem = OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_FIRSTVISIBLE, 0);
}
break;
}
}
long code = OS.CallWindowProc (TreeProc, hwnd, msg, wParam, lParam);
switch (msg) {
/* Keyboard messages */
case OS.WM_KEYDOWN:
if (wParam == OS.VK_CONTROL || wParam == OS.VK_SHIFT) break;
//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_SIZE:
if (redraw) {
OS.DefWindowProc (handle, OS.WM_SETREDRAW, 1, 0);
OS.InvalidateRect (handle, null, true);
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 (hItem != OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_FIRSTVISIBLE, 0)) {
OS.InvalidateRect (handle, null, true);
}
}
updateScrollBar ();
break;
}
case OS.WM_PAINT:
painted = true;
break;
}
return code;
}
@Override
void checkBuffered () {
super.checkBuffered ();
if ((style & SWT.VIRTUAL) != 0) {
style |= SWT.DOUBLE_BUFFERED;
OS.SendMessage (handle, OS.TVM_SETSCROLLTIME, 0, 0);
}
if (OS.IsAppThemed ()) {
int exStyle = (int)OS.SendMessage (handle, OS.TVM_GETEXTENDEDSTYLE, 0, 0);
if ((exStyle & OS.TVS_EX_DOUBLEBUFFER) != 0) style |= SWT.DOUBLE_BUFFERED;
}
}
boolean checkData (TreeItem item, boolean redraw) {
if ((style & SWT.VIRTUAL) == 0) return true;
if (!item.cached) {
TreeItem parentItem = item.getParentItem ();
return checkData (item, parentItem == null ? indexOf (item) : parentItem.indexOf (item), redraw);
}
return true;
}
boolean checkData (TreeItem 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;
TreeItem oldItem = currentItem;
currentItem = item;
/*
* Bug in Windows. If the tree scrolls during WM_NOTIFY
* with TVN_GETDISPINFO, pixel corruption occurs. The fix
* is to detect that the top item has changed and redraw
* the entire tree.
*/
long hTopItem = OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_FIRSTVISIBLE, 0);
sendEvent (SWT.SetData, event);
//widget could be disposed at this point
currentItem = oldItem;
if (isDisposed () || item.isDisposed ()) return false;
if (redraw) item.redraw ();
if (hTopItem != OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_FIRSTVISIBLE, 0)) {
OS.InvalidateRect (handle, null, true);
}
}
return true;
}
@Override
boolean checkHandle (long hwnd) {
return hwnd == handle || (hwndParent != 0 && hwnd == hwndParent) || (hwndHeader != 0 && hwnd == hwndHeader);
}
boolean checkScroll (long hItem) {
/*
* Feature in Windows. If redraw is turned off using WM_SETREDRAW
* and a tree item that is not a child of the first root is selected or
* scrolled using TVM_SELECTITEM or TVM_ENSUREVISIBLE, then scrolling
* does not occur. The fix is to detect this case, and make sure
* that redraw is temporarily enabled. To avoid flashing, DefWindowProc()
* is called to disable redrawing.
*
* NOTE: The code that actually works around the problem is in the
* callers of this method.
*/
if (getDrawing ()) return false;
long hRoot = OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_ROOT, 0);
long hParent = OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_PARENT, hItem);
while (hParent != hRoot && hParent != 0) {
hParent = OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_PARENT, hParent);
}
return hParent == 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 tree was created with the SWT.VIRTUAL
style,
* these attributes are requested again as needed.
*
* @param index the index of the item to clear
* @param all true
if all child items of the indexed item should be
* cleared recursively, and false
otherwise
*
* @exception IllegalArgumentException
* - ERROR_INVALID_RANGE - if the index is not between 0 and the number of elements in the list minus 1 (inclusive)
*
* @exception SWTException
* - ERROR_WIDGET_DISPOSED - if the receiver has been disposed
* - ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver
*
*
* @see SWT#VIRTUAL
* @see SWT#SetData
*
* @since 3.2
*/
public void clear (int index, boolean all) {
checkWidget ();
long hItem = OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_ROOT, 0);
if (hItem == 0) error (SWT.ERROR_INVALID_RANGE);
hItem = findItem (hItem, index);
if (hItem == 0) error (SWT.ERROR_INVALID_RANGE);
TVITEM tvItem = new TVITEM ();
tvItem.mask = OS.TVIF_HANDLE | OS.TVIF_PARAM;
clear (hItem, tvItem);
if (all) {
hItem = OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_CHILD, hItem);
clearAll (hItem, tvItem, all);
}
}
void clear (long hItem, TVITEM tvItem) {
tvItem.hItem = hItem;
TreeItem item = null;
if (OS.SendMessage (handle, OS.TVM_GETITEM, 0, tvItem) != 0) {
item = tvItem.lParam != -1 ? items [(int)tvItem.lParam] : null;
}
if (item != null) {
if ((style & SWT.VIRTUAL) != 0 && !item.cached) return;
item.clear ();
item.redraw ();
}
}
/**
* Clears all the items in the receiver. The text, icon and other
* attributes of the items are set to their default values. If the
* tree was created with the SWT.VIRTUAL
style, these
* attributes are requested again as needed.
*
* @param all true
if all child items should be cleared
* recursively, and false
otherwise
*
* @exception SWTException
* - ERROR_WIDGET_DISPOSED - if the receiver has been disposed
* - ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver
*
*
* @see SWT#VIRTUAL
* @see SWT#SetData
*
* @since 3.2
*/
public void clearAll (boolean all) {
checkWidget ();
long hItem = OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_ROOT, 0);
if (hItem == 0) return;
if (all) {
boolean redraw = false;
for (int i=0; i
* ERROR_NULL_ARGUMENT - if the item is null
* ERROR_INVALID_ARGUMENT - if the item has been disposed
*
* @exception SWTException
* - ERROR_WIDGET_DISPOSED - if the receiver has been disposed
* - ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver
*
*
* @since 3.4
*/
public void deselect (TreeItem item) {
checkWidget ();
if (item == null) error (SWT.ERROR_NULL_ARGUMENT);
if (item.isDisposed ()) error (SWT.ERROR_INVALID_ARGUMENT);
TVITEM tvItem = new TVITEM ();
tvItem.mask = OS.TVIF_HANDLE | OS.TVIF_STATE;
tvItem.stateMask = OS.TVIS_SELECTED;
tvItem.hItem = item.handle;
OS.SendMessage (handle, OS.TVM_SETITEM, 0, tvItem);
}
/**
* Deselects all selected items in the receiver.
*
* @exception SWTException
* - ERROR_WIDGET_DISPOSED - if the receiver has been disposed
* - ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver
*
*/
public void deselectAll () {
checkWidget ();
TVITEM tvItem = new TVITEM ();
tvItem.mask = OS.TVIF_HANDLE | OS.TVIF_STATE;
tvItem.stateMask = OS.TVIS_SELECTED;
if ((style & SWT.SINGLE) != 0) {
long hItem = OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_CARET, 0);
if (hItem != 0) {
tvItem.hItem = hItem;
OS.SendMessage (handle, OS.TVM_SETITEM, 0, tvItem);
}
} else {
long oldProc = OS.GetWindowLongPtr (handle, OS.GWLP_WNDPROC);
OS.SetWindowLongPtr (handle, OS.GWLP_WNDPROC, TreeProc);
if ((style & SWT.VIRTUAL) != 0) {
long hItem = OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_ROOT, 0);
deselect (hItem, tvItem, 0);
} else {
for (int i=0; i rect.right) {
quit = true;
} else {
cellRect [0].right = Math.min (cellRect [0].right, rect.right);
if (OS.PtInRect (cellRect [0], pt)) {
if (isCustomToolTip ()) {
int state = (int)OS.SendMessage (handle, OS.TVM_GETITEMSTATE, lpht.hItem, OS.TVIS_SELECTED);
int detail = (state & OS.TVIS_SELECTED) != 0 ? SWT.SELECTED : 0;
Event event = sendMeasureItemEvent (item [0], order [index [0]], hDC, detail);
if (isDisposed () || item [0].isDisposed ()) break;
Rectangle boundsInPixels = event.getBoundsInPixels();
itemRect [0] = new RECT ();
itemRect [0].left = boundsInPixels.x;
itemRect [0].right = boundsInPixels.x + boundsInPixels.width;
itemRect [0].top = boundsInPixels.y;
itemRect [0].bottom = boundsInPixels.y + boundsInPixels.height;
} else {
itemRect [0] = item [0].getBounds (order [index [0]], true, false, false, false, false, hDC);
}
if (itemRect [0].right > cellRect [0].right) found = true;
quit = true;
}
}
if (hFont != -1) OS.SelectObject (hDC, hFont);
if (!found) index [0]++;
}
if (newFont != 0) OS.SelectObject (hDC, oldFont);
OS.ReleaseDC (handle, hDC);
}
return found;
}
int findIndex (long hFirstItem, long hItem) {
if (hFirstItem == 0) return -1;
if (hFirstItem == hFirstIndexOf) {
if (hFirstIndexOf == hItem) {
hLastIndexOf = hFirstIndexOf;
return lastIndexOf = 0;
}
if (hLastIndexOf == hItem) return lastIndexOf;
long hPrevItem = OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_PREVIOUS, hLastIndexOf);
if (hPrevItem == hItem) {
hLastIndexOf = hPrevItem;
return --lastIndexOf;
}
long hNextItem = OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_NEXT, hLastIndexOf);
if (hNextItem == hItem) {
hLastIndexOf = hNextItem;
return ++lastIndexOf;
}
int previousIndex = lastIndexOf - 1;
while (hPrevItem != 0 && hPrevItem != hItem) {
hPrevItem = OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_PREVIOUS, hPrevItem);
--previousIndex;
}
if (hPrevItem == hItem) {
hLastIndexOf = hPrevItem;
return lastIndexOf = previousIndex;
}
int nextIndex = lastIndexOf + 1;
while (hNextItem != 0 && hNextItem != hItem) {
hNextItem = OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_NEXT, hNextItem);
nextIndex++;
}
if (hNextItem == hItem) {
hLastIndexOf = hNextItem;
return lastIndexOf = nextIndex;
}
return -1;
}
int index = 0;
long hNextItem = hFirstItem;
while (hNextItem != 0 && hNextItem != hItem) {
hNextItem = OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_NEXT, hNextItem);
index++;
}
if (hNextItem == hItem) {
itemCount = -1;
hFirstIndexOf = hFirstItem;
hLastIndexOf = hNextItem;
return lastIndexOf = index;
}
return -1;
}
@Override
Widget findItem (long hItem) {
return _getItem (hItem);
}
long findItem (long hFirstItem, int index) {
if (hFirstItem == 0) return 0;
if (hFirstItem == hFirstIndexOf) {
if (index == 0) {
lastIndexOf = 0;
return hLastIndexOf = hFirstIndexOf;
}
if (lastIndexOf == index) return hLastIndexOf;
if (lastIndexOf - 1 == index) {
--lastIndexOf;
return hLastIndexOf = OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_PREVIOUS, hLastIndexOf);
}
if (lastIndexOf + 1 == index) {
lastIndexOf++;
return hLastIndexOf = OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_NEXT, hLastIndexOf);
}
if (index < lastIndexOf) {
int previousIndex = lastIndexOf - 1;
long hPrevItem = OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_PREVIOUS, hLastIndexOf);
while (hPrevItem != 0 && index < previousIndex) {
hPrevItem = OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_PREVIOUS, hPrevItem);
--previousIndex;
}
if (index == previousIndex) {
lastIndexOf = previousIndex;
return hLastIndexOf = hPrevItem;
}
} else {
int nextIndex = lastIndexOf + 1;
long hNextItem = OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_NEXT, hLastIndexOf);
while (hNextItem != 0 && nextIndex < index) {
hNextItem = OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_NEXT, hNextItem);
nextIndex++;
}
if (index == nextIndex) {
lastIndexOf = nextIndex;
return hLastIndexOf = hNextItem;
}
}
return 0;
}
int nextIndex = 0;
long hNextItem = hFirstItem;
while (hNextItem != 0 && nextIndex < index) {
hNextItem = OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_NEXT, hNextItem);
nextIndex++;
}
if (index == nextIndex) {
itemCount = -1;
lastIndexOf = nextIndex;
hFirstIndexOf = hFirstItem;
return hLastIndexOf = hNextItem;
}
return 0;
}
TreeItem getFocusItem () {
// checkWidget ();
long hItem = OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_CARET, 0);
return hItem != 0 ? _getItem (hItem) : null;
}
/**
* Returns the width in points of a grid line.
*
* @return the width of a grid line in points
*
* @exception SWTException
* - ERROR_WIDGET_DISPOSED - if the receiver has been disposed
* - ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver
*
*
* @since 3.1
*/
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
* - ERROR_WIDGET_DISPOSED - if the receiver has been disposed
* - ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver
*
* @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
* - ERROR_WIDGET_DISPOSED - if the receiver has been disposed
* - ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver
*
* @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
* - ERROR_WIDGET_DISPOSED - if the receiver has been disposed
* - ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver
*
*
* @since 3.1
*/
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.
*
* 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.
*
*
* @return the receiver's header's visibility state
*
* @exception SWTException
* - ERROR_WIDGET_DISPOSED - if the receiver has been disposed
* - ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver
*
*
* @since 3.1
*/
public boolean getHeaderVisible () {
checkWidget ();
if (hwndHeader == 0) return false;
int bits = OS.GetWindowLong (hwndHeader, OS.GWL_STYLE);
return (bits & OS.WS_VISIBLE) != 0;
}
Point getImageSize () {
if (imageList != null) return imageList.getImageSize ();
return new Point (0, getItemHeightInPixels ());
}
long getBottomItem () {
long hItem = OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_FIRSTVISIBLE, 0);
if (hItem == 0) return 0;
int index = 0, count = (int)OS.SendMessage (handle, OS.TVM_GETVISIBLECOUNT, 0, 0);
while (index <= count) {
long hNextItem = OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_NEXTVISIBLE, hItem);
if (hNextItem == 0) return hItem;
hItem = hNextItem;
index++;
}
return hItem;
}
/**
* Returns the column at the given, zero-relative index in the
* receiver. Throws an exception if the index is out of range.
* Columns are returned in the order that they were created.
* If no TreeColumn
s were created by the programmer,
* this method will throw ERROR_INVALID_RANGE
despite
* the fact that a single column of data may be visible in the tree.
* This occurs when the programmer uses the tree 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
* - ERROR_INVALID_RANGE - if the index is not between 0 and the number of elements in the list minus 1 (inclusive)
*
* @exception SWTException
* - ERROR_WIDGET_DISPOSED - if the receiver has been disposed
* - ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver
*
*
* @see Tree#getColumnOrder()
* @see Tree#setColumnOrder(int[])
* @see TreeColumn#getMoveable()
* @see TreeColumn#setMoveable(boolean)
* @see SWT#Move
*
* @since 3.1
*/
public TreeColumn 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 TreeColumn
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 tree like a list, adding items but never creating a column.
*
* @return the number of columns
*
* @exception SWTException
* - ERROR_WIDGET_DISPOSED - if the receiver has been disposed
* - ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver
*
*
* @since 3.1
*/
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.
*
* 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.
*
*
* @return the current visual order of the receiver's items
*
* @exception SWTException
* - ERROR_WIDGET_DISPOSED - if the receiver has been disposed
* - ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver
*
*
* @see Tree#setColumnOrder(int[])
* @see TreeColumn#getMoveable()
* @see TreeColumn#setMoveable(boolean)
* @see SWT#Move
*
* @since 3.2
*/
public int[] getColumnOrder () {
checkWidget ();
if (columnCount == 0) return new int [0];
int [] order = new int [columnCount];
OS.SendMessage (hwndHeader, OS.HDM_GETORDERARRAY, columnCount, order);
return order;
}
/**
* Returns an array of TreeColumn
s which are the
* columns in the receiver. Columns are returned in the order
* that they were created. If no TreeColumn
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 tree like a list, adding items but
* never creating a column.
*
* 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.
*
*
* @return the items in the receiver
*
* @exception SWTException
* - ERROR_WIDGET_DISPOSED - if the receiver has been disposed
* - ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver
*
*
* @see Tree#getColumnOrder()
* @see Tree#setColumnOrder(int[])
* @see TreeColumn#getMoveable()
* @see TreeColumn#setMoveable(boolean)
* @see SWT#Move
*
* @since 3.1
*/
public TreeColumn [] getColumns () {
checkWidget ();
TreeColumn [] result = new TreeColumn [columnCount];
System.arraycopy (columns, 0, result, 0, columnCount);
return result;
}
/**
* 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
* - ERROR_INVALID_RANGE - if the index is not between 0 and the number of elements in the list minus 1 (inclusive)
*
* @exception SWTException
* - ERROR_WIDGET_DISPOSED - if the receiver has been disposed
* - ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver
*
*
* @since 3.1
*/
public TreeItem getItem (int index) {
checkWidget ();
if (index < 0) error (SWT.ERROR_INVALID_RANGE);
long hFirstItem = OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_ROOT, 0);
if (hFirstItem == 0) error (SWT.ERROR_INVALID_RANGE);
long hItem = findItem (hFirstItem, index);
if (hItem == 0) error (SWT.ERROR_INVALID_RANGE);
return _getItem (hItem);
}
TreeItem getItem (NMTVCUSTOMDRAW nmcd) {
/*
* Bug in Windows. If the lParam field of TVITEM
* is changed during custom draw using TVM_SETITEM,
* the lItemlParam field of the NMTVCUSTOMDRAW struct
* is not updated until the next custom draw. The
* fix is to query the field from the item instead
* of using the struct.
*/
int id = (int)nmcd.lItemlParam;
if ((style & SWT.VIRTUAL) != 0) {
if (id == -1) {
TVITEM tvItem = new TVITEM ();
tvItem.mask = OS.TVIF_HANDLE | OS.TVIF_PARAM;
tvItem.hItem = nmcd.dwItemSpec;
OS.SendMessage (handle, OS.TVM_GETITEM, 0, tvItem);
id = (int)tvItem.lParam;
}
}
return _getItem (nmcd.dwItemSpec, id);
}
/**
* 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.
*
* 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.
*
*
* @param point the point used to locate the item
* @return the item at the given point, or null if the point is not in a selectable item
*
* @exception IllegalArgumentException
* - ERROR_NULL_ARGUMENT - if the point is null
*
* @exception SWTException
* - ERROR_WIDGET_DISPOSED - if the receiver has been disposed
* - ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver
*
*/
public TreeItem getItem (Point point) {
checkWidget ();
if (point == null) error (SWT.ERROR_NULL_ARGUMENT);
return getItemInPixels(DPIUtil.autoScaleUp(point));
}
TreeItem getItemInPixels (Point point) {
TVHITTESTINFO lpht = new TVHITTESTINFO ();
lpht.x = point.x;
lpht.y = point.y;
OS.SendMessage (handle, OS.TVM_HITTEST, 0, lpht);
if (lpht.hItem != 0) {
int flags = OS.TVHT_ONITEM;
if ((style & SWT.FULL_SELECTION) != 0) {
flags |= OS.TVHT_ONITEMRIGHT | OS.TVHT_ONITEMINDENT;
} else {
if (hooks (SWT.MeasureItem)) {
lpht.flags &= ~(OS.TVHT_ONITEMICON | OS.TVHT_ONITEMLABEL);
if (hitTestSelection (lpht.hItem, lpht.x, lpht.y)) {
lpht.flags |= OS.TVHT_ONITEMICON | OS.TVHT_ONITEMLABEL;
}
}
}
if ((lpht.flags & flags) != 0) return _getItem (lpht.hItem);
}
return null;
}
/**
* Returns the number of items contained in the receiver
* that are direct item children of the receiver. The
* number that is returned is the number of roots in the
* tree.
*
* @return the number of items
*
* @exception SWTException
* - ERROR_WIDGET_DISPOSED - if the receiver has been disposed
* - ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver
*
*/
public int getItemCount () {
checkWidget ();
long hItem = OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_ROOT, 0);
if (hItem == 0) return 0;
return getItemCount (hItem);
}
int getItemCount (long hItem) {
int count = 0;
long hFirstItem = hItem;
if (hItem == hFirstIndexOf) {
if (itemCount != -1) return itemCount;
hFirstItem = hLastIndexOf;
count = lastIndexOf;
}
while (hFirstItem != 0) {
hFirstItem = OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_NEXT, hFirstItem);
count++;
}
if (hItem == hFirstIndexOf) itemCount = count;
return count;
}
/**
* Returns the height of the area which would be used to
* display one of the items in the tree.
*
* @return the height of one item
*
* @exception SWTException
* - ERROR_WIDGET_DISPOSED - if the receiver has been disposed
* - ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver
*
*/
public int getItemHeight () {
checkWidget ();
return DPIUtil.autoScaleDown(getItemHeightInPixels());
}
int getItemHeightInPixels () {
return (int)OS.SendMessage (handle, OS.TVM_GETITEMHEIGHT, 0, 0);
}
/**
* Returns a (possibly empty) array of items contained in the
* receiver that are direct item children of the receiver. These
* are the roots of the tree.
*
* 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.
*
*
* @return the items
*
* @exception SWTException
* - ERROR_WIDGET_DISPOSED - if the receiver has been disposed
* - ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver
*
*/
public TreeItem [] getItems () {
checkWidget ();
long hItem = OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_ROOT, 0);
if (hItem == 0) return new TreeItem [0];
return getItems (hItem);
}
TreeItem [] getItems (long hTreeItem) {
int count = 0;
long hItem = hTreeItem;
while (hItem != 0) {
hItem = OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_NEXT, hItem);
count++;
}
int index = 0;
TreeItem [] result = new TreeItem [count];
TVITEM tvItem = new TVITEM ();
tvItem.mask = OS.TVIF_HANDLE | OS.TVIF_PARAM;
tvItem.hItem = hTreeItem;
/*
* Feature in Windows. In some cases an expand or collapse message
* can occur from within TVM_DELETEITEM. When this happens, the item
* being destroyed has been removed from the list of items but has not
* been deleted from the tree. The fix is to check for null items and
* remove them from the list.
*/
while (tvItem.hItem != 0) {
OS.SendMessage (handle, OS.TVM_GETITEM, 0, tvItem);
TreeItem item = _getItem (tvItem.hItem, (int)tvItem.lParam);
if (item != null) result [index++] = item;
tvItem.hItem = OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_NEXT, tvItem.hItem);
}
if (index != count) {
TreeItem [] newResult = new TreeItem [index];
System.arraycopy (result, 0, newResult, 0, index);
result = newResult;
}
return result;
}
/**
* Returns true
if the receiver's lines are visible,
* and false
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, this method
* may still indicate that it is considered visible even though
* it may not actually be showing.
*
*
* @return the visibility state of the lines
*
* @exception SWTException
* - ERROR_WIDGET_DISPOSED - if the receiver has been disposed
* - ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver
*
*
* @since 3.1
*/
public boolean getLinesVisible () {
checkWidget ();
return linesVisible;
}
long getNextSelection (long hItem) {
while (hItem != 0) {
int state = (int)OS.SendMessage (handle, OS.TVM_GETITEMSTATE, hItem, OS.TVIS_SELECTED);
if ((state & OS.TVIS_SELECTED) != 0) return hItem;
long hFirstItem = OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_CHILD, hItem);
long hSelected = getNextSelection (hFirstItem);
if (hSelected != 0) return hSelected;
hItem = OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_NEXT, hItem);
}
return 0;
}
/**
* Returns the receiver's parent item, which must be a
* TreeItem
or null when the receiver is a
* root.
*
* @return the receiver's parent item
*
* @exception SWTException
* - ERROR_WIDGET_DISPOSED - if the receiver has been disposed
* - ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver
*
*/
public TreeItem getParentItem () {
checkWidget ();
return null;
}
int getSelection (long hItem, TVITEM tvItem, TreeItem [] selection, int index, int count, boolean bigSelection, boolean all) {
while (hItem != 0) {
boolean expanded = true;
if (bigSelection) {
tvItem.hItem = hItem;
OS.SendMessage (handle, OS.TVM_GETITEM, 0, tvItem);
if ((tvItem.state & OS.TVIS_SELECTED) != 0) {
if (selection != null && index < selection.length) {
TreeItem item = _getItem (hItem, (int)tvItem.lParam);
if (item != null) {
selection [index] = item;
} else {
index--;
}
}
index++;
}
expanded = (tvItem.state & OS.TVIS_EXPANDED) != 0;
} else {
int state = (int)OS.SendMessage (handle, OS.TVM_GETITEMSTATE, hItem, OS.TVIS_SELECTED | OS.TVIS_EXPANDED);
if ((state & OS.TVIS_SELECTED) != 0) {
if (tvItem != null && selection != null && index < selection.length) {
tvItem.hItem = hItem;
OS.SendMessage (handle, OS.TVM_GETITEM, 0, tvItem);
TreeItem item = _getItem (hItem, (int)tvItem.lParam);
if (item != null) {
selection [index] = item;
} else {
index--;
}
}
index++;
}
expanded = (state & OS.TVIS_EXPANDED) != 0;
}
if (index == count) break;
if (all) {
if (expanded) {
long hFirstItem = OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_CHILD, hItem);
if ((index = getSelection (hFirstItem, tvItem, selection, index, count, bigSelection, all)) == count) {
break;
}
}
hItem = OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_NEXT, hItem);
} else {
hItem = OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_NEXTVISIBLE, hItem);
}
}
return index;
}
/**
* Returns an array of TreeItem
s that are currently
* selected in the receiver. The order of the items is unspecified.
* An empty array indicates that no items are selected.
*
* Note: This is not the actual structure used by the receiver
* to maintain its selection, so modifying the array will
* not affect the receiver.
*
* @return an array representing the selection
*
* @exception SWTException
* - ERROR_WIDGET_DISPOSED - if the receiver has been disposed
* - ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver
*
*/
public TreeItem [] getSelection () {
checkWidget ();
if ((style & SWT.SINGLE) != 0) {
long hItem = OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_CARET, 0);
if (hItem == 0) return new TreeItem [0];
TVITEM tvItem = new TVITEM ();
tvItem.mask = OS.TVIF_HANDLE | OS.TVIF_PARAM | OS.TVIF_STATE;
tvItem.hItem = hItem;
OS.SendMessage (handle, OS.TVM_GETITEM, 0, tvItem);
if ((tvItem.state & OS.TVIS_SELECTED) == 0) return new TreeItem [0];
TreeItem item = _getItem (tvItem.hItem, (int)tvItem.lParam);
if (item == null) return new TreeItem [0];
return new TreeItem [] {item};
}
int count = 0;
TreeItem [] guess = new TreeItem [(style & SWT.VIRTUAL) != 0 ? 8 : 1];
long oldProc = OS.GetWindowLongPtr (handle, OS.GWLP_WNDPROC);
OS.SetWindowLongPtr (handle, OS.GWLP_WNDPROC, TreeProc);
if ((style & SWT.VIRTUAL) != 0) {
TVITEM tvItem = new TVITEM ();
tvItem.mask = OS.TVIF_HANDLE | OS.TVIF_PARAM | OS.TVIF_STATE;
long hItem = OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_ROOT, 0);
count = getSelection (hItem, tvItem, guess, 0, -1, false, true);
} else {
for (int i=0; i itemCount / 2;
if (count != getSelection (hItem, tvItem, result, 0, count, bigSelection, false)) {
count = getSelection (hItem, tvItem, result, 0, count, bigSelection, true);
}
if (count != result.length) {
TreeItem[] newResult = new TreeItem[count];
System.arraycopy (result, 0, newResult, 0, count);
result = newResult;
}
OS.SetWindowLongPtr (handle, OS.GWLP_WNDPROC, oldProc);
return result;
}
/**
* Returns the number of selected items contained in the receiver.
*
* @return the number of selected items
*
* @exception SWTException
* - ERROR_WIDGET_DISPOSED - if the receiver has been disposed
* - ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver
*
*/
public int getSelectionCount () {
checkWidget ();
if ((style & SWT.SINGLE) != 0) {
long hItem = OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_CARET, 0);
if (hItem == 0) return 0;
int state = (int)OS.SendMessage (handle, OS.TVM_GETITEMSTATE, hItem, OS.TVIS_SELECTED);
return (state & OS.TVIS_SELECTED) == 0 ? 0 : 1;
}
int count = 0;
long oldProc = OS.GetWindowLongPtr (handle, OS.GWLP_WNDPROC);
OS.SetWindowLongPtr (handle, OS.GWLP_WNDPROC, TreeProc);
if ((style & SWT.VIRTUAL) != 0) {
long hItem = OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_ROOT, 0);
count = getSelection (hItem, null, null, 0, -1, false, true);
} else {
for (int i=0; i
* ERROR_WIDGET_DISPOSED - if the receiver has been disposed
* ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver
*
*
* @see #setSortColumn(TreeColumn)
*
* @since 3.2
*/
public TreeColumn 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
* - ERROR_WIDGET_DISPOSED - if the receiver has been disposed
* - ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver
*
*
* @see #setSortDirection(int)
*
* @since 3.2
*/
public int getSortDirection () {
checkWidget ();
return sortDirection;
}
/**
* Returns the item which is currently at the top of the receiver.
* This item can change when items are expanded, collapsed, scrolled
* or new items are added or removed.
*
* @return the item at the top of the receiver
*
* @exception SWTException
* - ERROR_WIDGET_DISPOSED - if the receiver has been disposed
* - ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver
*
*
* @since 2.1
*/
public TreeItem getTopItem () {
checkWidget ();
long hItem = OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_FIRSTVISIBLE, 0);
return hItem != 0 ? _getItem (hItem) : null;
}
boolean hitTestSelection (long hItem, int x, int y) {
if (hItem == 0) return false;
TreeItem item = _getItem (hItem);
if (item == null) return false;
if (!hooks (SWT.MeasureItem)) return false;
boolean result = false;
//BUG? - moved columns, only hittest first column
//BUG? - check drag detect
int [] order = new int [1], index = new int [1];
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 (order [index [0]]);
if (hFont != -1) hFont = OS.SelectObject (hDC, hFont);
int state = (int)OS.SendMessage (handle, OS.TVM_GETITEMSTATE, hItem, OS.TVIS_SELECTED);
int detail = (state & OS.TVIS_SELECTED) != 0 ? SWT.SELECTED : 0;
Event event = sendMeasureItemEvent (item, order [index [0]], hDC, detail);
if (event.getBoundsInPixels ().contains (x, y)) result = true;
if (newFont != 0) OS.SelectObject (hDC, oldFont);
OS.ReleaseDC (handle, hDC);
// if (isDisposed () || item.isDisposed ()) return false;
return result;
}
int imageIndex (Image image, int index) {
if (image == null) return OS.I_IMAGENONE;
if (imageList == null) {
Rectangle bounds = image.getBoundsInPixels ();
imageList = display.getImageList (style & SWT.RIGHT_TO_LEFT, bounds.width, bounds.height);
}
int imageIndex = imageList.indexOf (image);
if (imageIndex == -1) imageIndex = imageList.add (image);
if (hwndHeader == 0 || OS.SendMessage (hwndHeader, OS.HDM_ORDERTOINDEX, 0, 0) == index) {
/*
* Feature in Windows. When setting the same image list multiple
* times, Windows does work making this operation slow. The fix
* is to test for the same image list before setting the new one.
*/
long hImageList = imageList.getHandle ();
long hOldImageList = OS.SendMessage (handle, OS.TVM_GETIMAGELIST, OS.TVSIL_NORMAL, 0);
if (hOldImageList != hImageList) {
OS.SendMessage (handle, OS.TVM_SETIMAGELIST, OS.TVSIL_NORMAL, hImageList);
updateScrollBar ();
}
}
return imageIndex;
}
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 ();
if (hwndHeader != 0) {
OS.SendMessage (hwndHeader, OS.HDM_SETIMAGELIST, 0, hImageList);
}
updateScrollBar ();
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
* - ERROR_NULL_ARGUMENT - if the column is null
*
* @exception SWTException
* - ERROR_WIDGET_DISPOSED - if the receiver has been disposed
* - ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver
*
*
* @since 3.1
*/
public int indexOf (TreeColumn column) {
checkWidget ();
if (column == null) error (SWT.ERROR_NULL_ARGUMENT);
if (column.isDisposed()) error(SWT.ERROR_INVALID_ARGUMENT);
for (int i=0; i
* ERROR_NULL_ARGUMENT - if the item is null
* ERROR_INVALID_ARGUMENT - if the item has been disposed
*
* @exception SWTException
* - ERROR_WIDGET_DISPOSED - if the receiver has been disposed
* - ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver
*
*
* @since 3.1
*/
public int indexOf (TreeItem item) {
checkWidget ();
if (item == null) error (SWT.ERROR_NULL_ARGUMENT);
if (item.isDisposed()) error(SWT.ERROR_INVALID_ARGUMENT);
long hItem = OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_ROOT, 0);
return hItem == 0 ? -1 : findIndex (hItem, item.handle);
}
boolean isCustomToolTip () {
return hooks (SWT.MeasureItem);
}
boolean isItemSelected (NMTVCUSTOMDRAW nmcd) {
boolean selected = false;
if (OS.IsWindowEnabled (handle)) {
TVITEM tvItem = new TVITEM ();
tvItem.mask = OS.TVIF_HANDLE | OS.TVIF_STATE;
tvItem.hItem = nmcd.dwItemSpec;
OS.SendMessage (handle, OS.TVM_GETITEM, 0, tvItem);
if ((tvItem.state & (OS.TVIS_SELECTED | OS.TVIS_DROPHILITED)) != 0) {
selected = true;
/*
* Feature in Windows. When the mouse is pressed and the
* selection is first drawn for a tree, the previously
* selected item is redrawn but the the TVIS_SELECTED bits
* are not cleared. When the user moves the mouse slightly
* and a drag and drop operation is not started, the item is
* drawn again and this time with TVIS_SELECTED is cleared.
* This means that an item that contains colored cells will
* not draw with the correct background until the mouse is
* moved. The fix is to test for the selection colors and
* guess that the item is not selected.
*
* NOTE: This code does not work when the foreground and
* background of the tree are set to the selection colors
* but this does not happen in a regular application.
*/
if (handle == OS.GetFocus ()) {
if (OS.GetTextColor (nmcd.hdc) != OS.GetSysColor (OS.COLOR_HIGHLIGHTTEXT)) {
selected = false;
} else {
if (OS.GetBkColor (nmcd.hdc) != OS.GetSysColor (OS.COLOR_HIGHLIGHT)) {
selected = false;
}
}
}
} else {
if (nmcd.dwDrawStage == OS.CDDS_ITEMPOSTPAINT) {
/*
* Feature in Windows. When the mouse is pressed and the
* selection is first drawn for a tree, the item is drawn
* selected, but the TVIS_SELECTED bits for the item are
* not set. When the user moves the mouse slightly and
* a drag and drop operation is not started, the item is
* drawn again and this time TVIS_SELECTED is set. This
* means that an item that is in a tree that has the style
* TVS_FULLROWSELECT and that also contains colored cells
* will not draw the entire row selected until the user
* moves the mouse. The fix is to test for the selection
* colors and guess that the item is selected.
*
* NOTE: This code does not work when the foreground and
* background of the tree are set to the selection colors
* but this does not happen in a regular application.
*/
if (OS.GetTextColor (nmcd.hdc) == OS.GetSysColor (OS.COLOR_HIGHLIGHTTEXT)) {
if (OS.GetBkColor (nmcd.hdc) == OS.GetSysColor (OS.COLOR_HIGHLIGHT)) {
selected = true;
}
}
}
}
}
return selected;
}
void redrawSelection () {
if ((style & SWT.SINGLE) != 0) {
long hItem = OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_CARET, 0);
if (hItem != 0) {
RECT rect = new RECT ();
if (OS.TreeView_GetItemRect (handle, hItem, rect, false)) {
OS.InvalidateRect (handle, rect, true);
}
}
} else {
long hItem = OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_FIRSTVISIBLE, 0);
if (hItem != 0) {
RECT rect = new RECT ();
int index = 0, count = (int)OS.SendMessage (handle, OS.TVM_GETVISIBLECOUNT, 0, 0);
while (index <= count && hItem != 0) {
int state = (int)OS.SendMessage (handle, OS.TVM_GETITEMSTATE, hItem, OS.TVIS_SELECTED);
if ((state & OS.TVIS_SELECTED) != 0) {
if (OS.TreeView_GetItemRect (handle, hItem, rect, false)) {
OS.InvalidateRect (handle, rect, true);
}
}
hItem = OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_NEXTVISIBLE, hItem);
index++;
}
}
}
}
@Override
void register () {
super.register ();
if (hwndParent != 0) display.addControl (hwndParent, this);
if (hwndHeader != 0) display.addControl (hwndHeader, this);
}
void releaseItem (long hItem, TVITEM tvItem, boolean release) {
if (hItem == hAnchor) hAnchor = 0;
if (hItem == hInsert) hInsert = 0;
tvItem.hItem = hItem;
if (OS.SendMessage (handle, OS.TVM_GETITEM, 0, tvItem) != 0) {
if (tvItem.lParam != -1) {
if (tvItem.lParam < lastID) lastID = (int)tvItem.lParam;
if (release) {
TreeItem item = items [(int)tvItem.lParam];
if (item != null) item.release (false);
}
items [(int)tvItem.lParam] = null;
}
}
}
void releaseItems (long hItem, TVITEM tvItem) {
hItem = OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_CHILD, hItem);
while (hItem != 0) {
releaseItems (hItem, tvItem);
releaseItem (hItem, tvItem, true);
hItem = OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_NEXT, hItem);
}
}
@Override
void releaseHandle () {
super.releaseHandle ();
hwndParent = hwndHeader = 0;
}
@Override
void releaseChildren (boolean destroy) {
if (items != null) {
for (int i=0; i
* ERROR_WIDGET_DISPOSED - if the receiver has been disposed
* ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver
*
*/
public void removeAll () {
checkWidget ();
hFirstIndexOf = hLastIndexOf = 0;
itemCount = -1;
for (int i=0; i
* ERROR_NULL_ARGUMENT - if the listener is null
*
* @exception SWTException
* - ERROR_WIDGET_DISPOSED - if the receiver has been disposed
* - ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver
*
*
* @see SelectionListener
* @see #addSelectionListener
*/
public void removeSelectionListener (SelectionListener listener) {
checkWidget ();
if (listener == null) error (SWT.ERROR_NULL_ARGUMENT);
eventTable.unhook (SWT.Selection, listener);
eventTable.unhook (SWT.DefaultSelection, listener);
}
/**
* Removes the listener from the collection of listeners who will
* be notified when items in the receiver are expanded or collapsed.
*
* @param listener the listener which should no longer be notified
*
* @exception IllegalArgumentException
* - ERROR_NULL_ARGUMENT - if the listener is null
*
* @exception SWTException
* - ERROR_WIDGET_DISPOSED - if the receiver has been disposed
* - ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver
*
*
* @see TreeListener
* @see #addTreeListener
*/
public void removeTreeListener(TreeListener listener) {
checkWidget ();
if (listener == null) error (SWT.ERROR_NULL_ARGUMENT);
if (eventTable == null) return;
eventTable.unhook (SWT.Expand, listener);
eventTable.unhook (SWT.Collapse, listener);
}
@Override
void reskinChildren (int flags) {
if (items != null) {
for (int i=0; i
* ERROR_INVALID_ARGUMENT - if the item has been disposed
*
* @exception SWTException
* - ERROR_WIDGET_DISPOSED - if the receiver has been disposed
* - ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver
*
*/
public void setInsertMark (TreeItem item, boolean before) {
checkWidget ();
long hItem = 0;
if (item != null) {
if (item.isDisposed()) error(SWT.ERROR_INVALID_ARGUMENT);
hItem = item.handle;
}
hInsert = hItem;
insertAfter = !before;
OS.SendMessage (handle, OS.TVM_SETINSERTMARK, insertAfter ? 1 : 0, hInsert);
}
/**
* Sets the number of root-level items contained in the receiver.
*
* @param count the number of items
*
* @exception SWTException
* - ERROR_WIDGET_DISPOSED - if the receiver has been disposed
* - ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver
*
*
* @since 3.2
*/
public void setItemCount (int count) {
checkWidget ();
count = Math.max (0, count);
long hItem = OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_ROOT, 0);
setItemCount (count, OS.TVGN_ROOT, hItem);
}
void setItemCount (int count, long hParent, long hItem) {
boolean redraw = false;
if (OS.SendMessage (handle, OS.TVM_GETCOUNT, 0, 0) == 0) {
redraw = getDrawing () && OS.IsWindowVisible (handle);
if (redraw) OS.DefWindowProc (handle, OS.WM_SETREDRAW, 0, 0);
}
int itemCount = 0;
while (hItem != 0 && itemCount < count) {
hItem = OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_NEXT, hItem);
itemCount++;
}
boolean expanded = false;
TVITEM tvItem = new TVITEM ();
tvItem.mask = OS.TVIF_HANDLE | OS.TVIF_PARAM;
if (!redraw && (style & SWT.VIRTUAL) != 0) {
/*
* Bug in Windows. Despite the fact that TVM_GETITEMSTATE claims
* to return only the bits specified by the stateMask, when called
* with TVIS_EXPANDED, the entire state is returned. The fix is
* to explicitly check for the TVIS_EXPANDED bit.
*/
int state = (int)OS.SendMessage (handle, OS.TVM_GETITEMSTATE, hParent, OS.TVIS_EXPANDED);
expanded = (state & OS.TVIS_EXPANDED) != 0;
}
while (hItem != 0) {
tvItem.hItem = hItem;
OS.SendMessage (handle, OS.TVM_GETITEM, 0, tvItem);
hItem = OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_NEXT, hItem);
TreeItem item = tvItem.lParam != -1 ? items [(int)tvItem.lParam] : null;
if (item != null && !item.isDisposed ()) {
item.dispose ();
} else {
releaseItem (tvItem.hItem, tvItem, false);
destroyItem (null, tvItem.hItem);
}
}
if ((style & SWT.VIRTUAL) != 0) {
for (int i=itemCount; ione of the items in the tree.
*
* @param itemHeight the height of one item
*
* @exception SWTException
* - ERROR_WIDGET_DISPOSED - if the receiver has been disposed
* - ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver
*
*
* @since 3.2
*/
/*public*/ void setItemHeight (int itemHeight) {
checkWidget ();
if (itemHeight < -1) error (SWT.ERROR_INVALID_ARGUMENT);
OS.SendMessage (handle, OS.TVM_SETITEMHEIGHT, itemHeight, 0);
}
/**
* Marks the receiver's lines as visible if the argument is true
,
* 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
* - ERROR_WIDGET_DISPOSED - if the receiver has been disposed
* - ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver
*
*
* @since 3.1
*/
public void setLinesVisible (boolean show) {
checkWidget ();
if (linesVisible == show) return;
linesVisible = show;
if (hwndParent == 0 && linesVisible) customDraw = true;
OS.InvalidateRect (handle, null, true);
if (hwndHeader != 0) OS.InvalidateRect (hwndHeader, null, true);
}
@Override
long scrolledHandle () {
if (hwndHeader == 0) return handle;
return columnCount == 0 && scrollWidth == 0 ? handle : hwndParent;
}
void select (long hItem, TVITEM tvItem) {
while (hItem != 0) {
tvItem.hItem = hItem;
OS.SendMessage (handle, OS.TVM_SETITEM, 0, tvItem);
int state = (int)OS.SendMessage (handle, OS.TVM_GETITEMSTATE, hItem, OS.TVIS_EXPANDED);
if ((state & OS.TVIS_EXPANDED) != 0) {
long hFirstItem = OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_CHILD, hItem);
select (hFirstItem, tvItem);
}
hItem = OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_NEXT, hItem);
}
}
/**
* Selects an item in the receiver. If the item was already
* selected, it remains selected.
*
* @param item the item to be selected
*
* @exception IllegalArgumentException
* - ERROR_NULL_ARGUMENT - if the item is null
* - ERROR_INVALID_ARGUMENT - if the item has been disposed
*
* @exception SWTException
* - ERROR_WIDGET_DISPOSED - if the receiver has been disposed
* - ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver
*
*
* @since 3.4
*/
public void select (TreeItem item) {
checkWidget ();
if (item == null) error (SWT.ERROR_NULL_ARGUMENT);
if (item.isDisposed ()) error (SWT.ERROR_INVALID_ARGUMENT);
if ((style & SWT.SINGLE) != 0) {
long hItem = item.handle;
int state = (int)OS.SendMessage (handle, OS.TVM_GETITEMSTATE, hItem, OS.TVIS_SELECTED);
if ((state & OS.TVIS_SELECTED) != 0) return;
/*
* Feature in Windows. When an item is selected with
* TVM_SELECTITEM and TVGN_CARET, the tree expands and
* scrolls to show the new selected item. Unfortunately,
* there is no other way in Windows to set the focus
* and select an item. The fix is to save the current
* scroll bar positions, turn off redraw, select the item,
* then scroll back to the original position and redraw
* the entire tree.
*/
SCROLLINFO hInfo = null;
int bits = OS.GetWindowLong (handle, OS.GWL_STYLE);
if ((bits & (OS.TVS_NOHSCROLL | OS.TVS_NOSCROLL)) == 0) {
hInfo = new SCROLLINFO ();
hInfo.cbSize = SCROLLINFO.sizeof;
hInfo.fMask = OS.SIF_ALL;
OS.GetScrollInfo (handle, OS.SB_HORZ, hInfo);
}
SCROLLINFO vInfo = new SCROLLINFO ();
vInfo.cbSize = SCROLLINFO.sizeof;
vInfo.fMask = OS.SIF_ALL;
OS.GetScrollInfo (handle, OS.SB_VERT, vInfo);
boolean redraw = getDrawing () && OS.IsWindowVisible (handle);
if (redraw) {
OS.UpdateWindow (handle);
OS.DefWindowProc (handle, OS.WM_SETREDRAW, 0, 0);
}
setSelection (item);
if (hInfo != null) {
long hThumb = OS.MAKELPARAM (OS.SB_THUMBPOSITION, hInfo.nPos);
OS.SendMessage (handle, OS.WM_HSCROLL, hThumb, 0);
}
/*
* Feature in Windows. It seems that Vista does not
* use wParam to get the new position when WM_VSCROLL
* is sent with SB_THUMBPOSITION. The fix is to use
* SetScrollInfo() to move the scroll bar thumb before
* calling WM_VSCROLL.
*
* NOTE: This code is only necessary on Windows Vista.
*/
OS.SetScrollInfo (handle, OS.SB_VERT, vInfo, true);
long vThumb = OS.MAKELPARAM (OS.SB_THUMBPOSITION, vInfo.nPos);
OS.SendMessage (handle, OS.WM_VSCROLL, vThumb, 0);
if (redraw) {
OS.DefWindowProc (handle, OS.WM_SETREDRAW, 1, 0);
OS.InvalidateRect (handle, null, true);
if ((style & SWT.DOUBLE_BUFFERED) == 0) {
int oldStyle = style;
style |= SWT.DOUBLE_BUFFERED;
OS.UpdateWindow (handle);
style = oldStyle;
}
}
return;
}
expandToItem(item);
TVITEM tvItem = new TVITEM ();
tvItem.mask = OS.TVIF_HANDLE | OS.TVIF_STATE;
tvItem.stateMask = OS.TVIS_SELECTED;
tvItem.state = OS.TVIS_SELECTED;
tvItem.hItem = item.handle;
OS.SendMessage (handle, OS.TVM_SETITEM, 0, tvItem);
}
/**
* Selects all of the items in the receiver.
*
* If the receiver is single-select, do nothing.
*
*
* @exception SWTException
* - ERROR_WIDGET_DISPOSED - if the receiver has been disposed
* - ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver
*
*/
public void selectAll () {
checkWidget ();
if ((style & SWT.SINGLE) != 0) return;
TVITEM tvItem = new TVITEM ();
tvItem.mask = OS.TVIF_HANDLE | OS.TVIF_STATE;
tvItem.state = OS.TVIS_SELECTED;
tvItem.stateMask = OS.TVIS_SELECTED;
long oldProc = OS.GetWindowLongPtr (handle, OS.GWLP_WNDPROC);
OS.SetWindowLongPtr (handle, OS.GWLP_WNDPROC, TreeProc);
long hItem = OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_ROOT, 0);
select (hItem, tvItem);
OS.SetWindowLongPtr (handle, OS.GWLP_WNDPROC, oldProc);
}
Event sendEraseItemEvent (TreeItem 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 (TreeItem item, int index, long hDC, int detail) {
RECT itemRect = item.getBounds (index, true, true, false, false, false, hDC);
int nSavedDC = OS.SaveDC (hDC);
GCData data = new GCData ();
data.device = display;
data.font = item.getFont (index);
GC gc = GC.win32_new (hDC, data);
Event event = new Event ();
event.item = item;
event.gc = gc;
event.index = index;
event.setBoundsInPixels(new Rectangle(itemRect.left, itemRect.top, itemRect.right - itemRect.left, itemRect.bottom - itemRect.top));
event.detail = detail;
sendEvent (SWT.MeasureItem, event);
event.gc = null;
gc.dispose ();
OS.RestoreDC (hDC, nSavedDC);
if (isDisposed () || item.isDisposed ()) return null;
Rectangle rect = event.getBoundsInPixels ();
if (hwndHeader != 0) {
if (columnCount == 0) {
if (rect.x + rect.width > scrollWidth) {
setScrollWidth (scrollWidth = rect.x + rect.width);
}
}
}
if (rect.height > getItemHeightInPixels ()) setItemHeight (rect.height);
return event;
}
Event sendPaintItemEvent (TreeItem 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) {
/*
* Feature in Windows. If TVM_SETBKCOLOR is never
* used to set the background color of a tree, the
* background color of the lines and the plus/minus
* will be drawn using the default background color,
* not the HBRUSH returned from WM_CTLCOLOR. The fix
* is to set the background color to the default (when
* it is already the default) to make Windows use the
* brush.
*/
if (OS.SendMessage (handle, OS.TVM_GETBKCOLOR, 0, 0) == -1) {
OS.SendMessage (handle, OS.TVM_SETBKCOLOR, 0, -1);
}
_setBackgroundPixel (-1);
} else {
Control control = findBackgroundControl ();
if (control == null) control = this;
if (control.backgroundImage == null) {
setBackgroundPixel (control.getBackgroundPixel ());
}
}
/*
* Feature in Windows. When the tree has the style
* TVS_FULLROWSELECT, the background color for the
* entire row is filled when an item is painted,
* drawing on top of the background image. The fix
* is to clear TVS_FULLROWSELECT when a background
* image is set.
*/
updateFullSelection ();
}
@Override
void setBackgroundPixel (int pixel) {
Control control = findImageControl ();
if (control != null) {
setBackgroundImage (control.backgroundImage);
return;
}
_setBackgroundPixel (pixel);
/*
* Feature in Windows. When the tree has the style
* TVS_FULLROWSELECT, the background color for the
* entire row is filled when an item is painted,
* drawing on top of the background image. The fix
* is to restore TVS_FULLROWSELECT when a background
* color is set.
*/
updateFullSelection ();
}
@Override
void setCursor () {
/*
* Bug in Windows. Under certain circumstances, when WM_SETCURSOR
* is sent from SendMessage(), Windows GP's in the window proc for
* the tree. The fix is to avoid calling the tree window proc and
* set the cursor for the tree outside of WM_SETCURSOR.
*
* NOTE: This code assumes that the default cursor for the tree
* is IDC_ARROW.
*/
Cursor cursor = findCursor ();
long hCursor = cursor == null ? OS.LoadCursor (0, OS.IDC_ARROW) : cursor.handle;
OS.SetCursor (hCursor);
}
/**
* 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
* - ERROR_WIDGET_DISPOSED - if the receiver has been disposed
* - ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver
*
* @exception IllegalArgumentException
* - ERROR_NULL_ARGUMENT - if the item order is null
* - ERROR_INVALID_ARGUMENT - if the item order is not the same length as the number of items
*
*
* @see Tree#getColumnOrder()
* @see TreeColumn#getMoveable()
* @see TreeColumn#setMoveable(boolean)
* @see SWT#Move
*
* @since 3.2
*/
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 (hwndHeader, OS.HDM_GETORDERARRAY, columnCount, oldOrder);
boolean reorder = false;
boolean [] seen = new boolean [columnCount];
for (int i=0; i= columnCount) error (SWT.ERROR_INVALID_RANGE);
if (seen [index]) error (SWT.ERROR_INVALID_ARGUMENT);
seen [index] = true;
if (index != oldOrder [i]) reorder = true;
}
if (reorder) {
RECT [] oldRects = new RECT [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 tree style.
*
* @param color the new color (or null)
*
* @exception IllegalArgumentException
* - ERROR_INVALID_ARGUMENT - if the argument has been disposed
*
* @exception SWTException
* - ERROR_WIDGET_DISPOSED - if the receiver has been disposed
* - ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver
*
* @since 3.106
*/
public void setHeaderBackground (Color color) {
checkWidget ();
int pixel = -1;
if (color != null) {
if (color.isDisposed()) error(SWT.ERROR_INVALID_ARGUMENT);
pixel = color.handle;
}
if (pixel == headerBackground) return;
headerBackground = pixel;
if (getHeaderVisible()) {
OS.InvalidateRect (hwndHeader, null, true);
}
}
/**
* Sets the header foreground color to the color specified
* by the argument, or to the default system color if the argument is null.
*
* 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 tree style.
*
* @param color the new color (or null)
*
* @exception IllegalArgumentException
* - ERROR_INVALID_ARGUMENT - if the argument has been disposed
*
* @exception SWTException
* - ERROR_WIDGET_DISPOSED - if the receiver has been disposed
* - ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver
*
* @since 3.106
*/
public void setHeaderForeground (Color color) {
checkWidget ();
int pixel = -1;
if (color != null) {
if (color.isDisposed()) error(SWT.ERROR_INVALID_ARGUMENT);
pixel = color.handle;
}
if (pixel == headerForeground) return;
headerForeground = pixel;
if (getHeaderVisible()) {
OS.InvalidateRect (hwndHeader, null, true);
}
}
/**
* Marks the receiver's header as visible if the argument is true
,
* 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 SWTException
* - ERROR_WIDGET_DISPOSED - if the receiver has been disposed
* - ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver
*
*
* @since 3.1
*/
public void setHeaderVisible (boolean show) {
checkWidget ();
if (hwndHeader == 0) {
if (!show) return;
createParent ();
}
int bits = OS.GetWindowLong (hwndHeader, OS.GWL_STYLE);
if (show) {
if ((bits & OS.HDS_HIDDEN) == 0) return;
bits &= ~OS.HDS_HIDDEN;
OS.SetWindowLong (hwndHeader, OS.GWL_STYLE, bits);
OS.ShowWindow (hwndHeader, OS.SW_SHOW);
} else {
if ((bits & OS.HDS_HIDDEN) != 0) return;
bits |= OS.HDS_HIDDEN;
OS.SetWindowLong (hwndHeader, OS.GWL_STYLE, bits);
OS.ShowWindow (hwndHeader, OS.SW_HIDE);
}
setScrollWidth ();
updateHeaderToolTips ();
updateScrollBar ();
}
@Override
public void setRedraw (boolean redraw) {
checkWidget ();
/*
* Feature in Windows. When WM_SETREDRAW is used to
* turn off redraw, the scroll bars are updated when
* items are added and removed. The fix is to call
* the default window proc to stop all drawing.
*
* Bug in Windows. For some reason, when WM_SETREDRAW
* is used to turn redraw on for a tree and the tree
* contains no items, the last item in the tree does
* not redraw properly. If the tree has only one item,
* that item is not drawn. If another window is dragged
* on top of the item, parts of the item are redrawn
* and erased at random. The fix is to ensure that this
* case doesn't happen by inserting and deleting an item
* when redraw is turned on and there are no items in
* the tree.
*/
long hItem = 0;
boolean willEnableDraw = redraw && (drawCount == 1);
if (willEnableDraw) {
int count = (int)OS.SendMessage (handle, OS.TVM_GETCOUNT, 0, 0);
if (count == 0) {
TVINSERTSTRUCT tvInsert = new TVINSERTSTRUCT ();
tvInsert.hInsertAfter = OS.TVI_FIRST;
hItem = OS.SendMessage (handle, OS.TVM_INSERTITEM, 0, tvInsert);
}
OS.DefWindowProc (handle, OS.WM_SETREDRAW, 1, 0);
updateScrollBar ();
}
super.setRedraw (redraw);
boolean haveDisabledDraw = !redraw && (drawCount == 1);
if (haveDisabledDraw) {
OS.DefWindowProc (handle, OS.WM_SETREDRAW, 0, 0);
}
if (hItem != 0) {
ignoreShrink = true;
OS.SendMessage (handle, OS.TVM_DELETEITEM, 0, hItem);
ignoreShrink = false;
}
}
void setScrollWidth () {
if (hwndHeader == 0 || hwndParent == 0) return;
int width = 0;
HDITEM hdItem = new HDITEM ();
for (int i=0; i
* If the item is not in the receiver, then it is ignored.
*
*
* @param item the item to select
*
* @exception IllegalArgumentException
* - ERROR_NULL_ARGUMENT - if the item is null
* - ERROR_INVALID_ARGUMENT - if the item has been disposed
*
* @exception SWTException
* - ERROR_WIDGET_DISPOSED - if the receiver has been disposed
* - ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver
*
*
* @since 3.2
*/
public void setSelection (TreeItem item) {
checkWidget ();
if (item == null) error (SWT.ERROR_NULL_ARGUMENT);
setSelection (new TreeItem [] {item});
}
/**
* Sets the receiver's selection to be the given array of items.
* The current selection is cleared before the new items are selected,
* and if necessary the receiver is scrolled to make the new selection visible.
*
* 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
* - ERROR_NULL_ARGUMENT - if the array of items is null
* - ERROR_INVALID_ARGUMENT - if one of the items has been disposed
*
* @exception SWTException
* - ERROR_WIDGET_DISPOSED - if the receiver has been disposed
* - ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver
*
*
* @see Tree#deselectAll()
*/
public void setSelection (TreeItem [] items) {
checkWidget ();
if (items == null) error (SWT.ERROR_NULL_ARGUMENT);
int length = items.length;
if (length == 0 || ((style & SWT.SINGLE) != 0 && length > 1)) {
deselectAll();
return;
}
/* Select/deselect the first item */
TreeItem item = items [0];
if (item != null) {
if (item.isDisposed ()) error (SWT.ERROR_INVALID_ARGUMENT);
long hOldItem = OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_CARET, 0);
long hNewItem = hAnchor = item.handle;
/*
* Bug in Windows. When TVM_SELECTITEM is used to select and
* scroll an item to be visible and the client area of the tree
* is smaller that the size of one item, TVM_SELECTITEM makes
* the next item in the tree visible by making it the top item
* instead of making the desired item visible. The fix is to
* detect the case when the client area is too small and make
* the desired visible item be the top item in the tree.
*
* Note that TVM_SELECTITEM when called with TVGN_FIRSTVISIBLE
* also requires the work around for scrolling.
*/
boolean fixScroll = checkScroll (hNewItem);
if (fixScroll) {
OS.SendMessage (handle, OS.WM_SETREDRAW, 1, 0);
OS.DefWindowProc (handle, OS.WM_SETREDRAW, 0, 0);
}
ignoreSelect = true;
OS.SendMessage (handle, OS.TVM_SELECTITEM, OS.TVGN_CARET, hNewItem);
ignoreSelect = false;
if (OS.SendMessage (handle, OS.TVM_GETVISIBLECOUNT, 0, 0) == 0) {
OS.SendMessage (handle, OS.TVM_SELECTITEM, OS.TVGN_FIRSTVISIBLE, hNewItem);
long hParent = OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_PARENT, hNewItem);
if (hParent == 0) OS.SendMessage (handle, OS.WM_HSCROLL, OS.SB_TOP, 0);
}
if (fixScroll) {
OS.DefWindowProc (handle, OS.WM_SETREDRAW, 1, 0);
OS.SendMessage (handle, OS.WM_SETREDRAW, 0, 0);
}
/*
* Feature in Windows. When the old and new focused item
* are the same, Windows does not check to make sure that
* the item is actually selected, not just focused. The
* fix is to force the item to draw selected by setting
* the state mask, and to ensure that it is visible.
*/
if (hOldItem == hNewItem) {
TVITEM tvItem = new TVITEM ();
tvItem.mask = OS.TVIF_HANDLE | OS.TVIF_STATE;
tvItem.state = OS.TVIS_SELECTED;
tvItem.stateMask = OS.TVIS_SELECTED;
tvItem.hItem = hNewItem;
OS.SendMessage (handle, OS.TVM_SETITEM, 0, tvItem);
showItem (hNewItem);
}
}
if ((style & SWT.SINGLE) != 0) return;
/* Select/deselect the rest of the items */
TVITEM tvItem = new TVITEM ();
tvItem.mask = OS.TVIF_HANDLE | OS.TVIF_STATE;
tvItem.stateMask = OS.TVIS_SELECTED;
long oldProc = OS.GetWindowLongPtr (handle, OS.GWLP_WNDPROC);
OS.SetWindowLongPtr (handle, OS.GWLP_WNDPROC, TreeProc);
if ((style & SWT.VIRTUAL) != 0) {
long hItem = OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_ROOT, 0);
setSelection (hItem, tvItem, items);
} else {
for (int i=0; inull
*
* @exception IllegalArgumentException
* - ERROR_INVALID_ARGUMENT - if the column is disposed
*
* @exception SWTException
* - ERROR_WIDGET_DISPOSED - if the receiver has been disposed
* - ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver
*
*
* @since 3.2
*/
public void setSortColumn (TreeColumn column) {
checkWidget ();
if (column != null && column.isDisposed ()) error (SWT.ERROR_INVALID_ARGUMENT);
if (sortColumn != null && !sortColumn.isDisposed ()) {
sortColumn.setSortDirection (SWT.NONE);
}
sortColumn = column;
if (sortColumn != null && sortDirection != SWT.NONE) {
sortColumn.setSortDirection (sortDirection);
}
}
/**
* Sets the direction of the sort indicator for the receiver. The value
* can be one of UP
, DOWN
or NONE
.
*
* @param direction the direction of the sort indicator
*
* @exception SWTException
* - ERROR_WIDGET_DISPOSED - if the receiver has been disposed
* - ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver
*
*
* @since 3.2
*/
public void setSortDirection (int direction) {
checkWidget ();
if ((direction & (SWT.UP | SWT.DOWN)) == 0 && direction != SWT.NONE) return;
sortDirection = direction;
if (sortColumn != null && !sortColumn.isDisposed ()) {
sortColumn.setSortDirection (direction);
}
}
/**
* Sets the item which is currently at the top of the receiver.
* This item can change when items are expanded, collapsed, scrolled
* or new items are added or removed.
*
* @param item the item to be shown
*
* @exception IllegalArgumentException
* - ERROR_NULL_ARGUMENT - if the item is null
* - ERROR_INVALID_ARGUMENT - if the item has been disposed
*
* @exception SWTException
* - ERROR_WIDGET_DISPOSED - if the receiver has been disposed
* - ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver
*
*
* @see Tree#getTopItem()
*
* @since 2.1
*/
public void setTopItem (TreeItem item) {
checkWidget ();
if (item == null) error (SWT.ERROR_NULL_ARGUMENT);
if (item.isDisposed ()) error (SWT.ERROR_INVALID_ARGUMENT);
long hItem = item.handle;
long hTopItem = OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_FIRSTVISIBLE, 0);
if (hItem == hTopItem) return;
boolean fixScroll = checkScroll (hItem), redraw = false;
if (fixScroll) {
OS.SendMessage (handle, OS.WM_SETREDRAW, 1, 0);
OS.DefWindowProc (handle, OS.WM_SETREDRAW, 0, 0);
} else {
redraw = getDrawing () && OS.IsWindowVisible (handle);
if (redraw) OS.DefWindowProc (handle, OS.WM_SETREDRAW, 0, 0);
}
SCROLLINFO hInfo = null;
int bits = OS.GetWindowLong (handle, OS.GWL_STYLE);
long hParent = OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_PARENT, hItem);
if (hParent != 0 && (bits & (OS.TVS_NOHSCROLL | OS.TVS_NOSCROLL)) == 0) {
hInfo = new SCROLLINFO ();
hInfo.cbSize = SCROLLINFO.sizeof;
hInfo.fMask = OS.SIF_ALL;
OS.GetScrollInfo (handle, OS.SB_HORZ, hInfo);
}
OS.SendMessage (handle, OS.TVM_SELECTITEM, OS.TVGN_FIRSTVISIBLE, hItem);
if (hParent != 0) {
if (hInfo != null) {
long hThumb = OS.MAKELPARAM (OS.SB_THUMBPOSITION, hInfo.nPos);
OS.SendMessage (handle, OS.WM_HSCROLL, hThumb, 0);
}
} else {
OS.SendMessage (handle, OS.WM_HSCROLL, OS.SB_TOP, 0);
}
if (fixScroll) {
OS.DefWindowProc (handle, OS.WM_SETREDRAW, 1, 0);
OS.SendMessage (handle, OS.WM_SETREDRAW, 0, 0);
} else {
if (redraw) {
OS.DefWindowProc (handle, OS.WM_SETREDRAW, 1, 0);
OS.InvalidateRect (handle, null, true);
}
}
updateScrollBar ();
}
void showItem (long hItem) {
/*
* Bug in Windows. When TVM_ENSUREVISIBLE is used to ensure
* that an item is visible and the client area of the tree is
* smaller that the size of one item, TVM_ENSUREVISIBLE makes
* the next item in the tree visible by making it the top item
* instead of making the desired item visible. The fix is to
* detect the case when the client area is too small and make
* the desired visible item be the top item in the tree.
*/
if (OS.SendMessage (handle, OS.TVM_GETVISIBLECOUNT, 0, 0) == 0) {
boolean fixScroll = checkScroll (hItem);
if (fixScroll) {
OS.SendMessage (handle, OS.WM_SETREDRAW, 1, 0);
OS.DefWindowProc (handle, OS.WM_SETREDRAW, 0, 0);
}
OS.SendMessage (handle, OS.TVM_SELECTITEM, OS.TVGN_FIRSTVISIBLE, hItem);
/* This code is intentionally commented */
//int hParent = OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_PARENT, hItem);
//if (hParent == 0) OS.SendMessage (handle, OS.WM_HSCROLL, OS.SB_TOP, 0);
OS.SendMessage (handle, OS.WM_HSCROLL, OS.SB_TOP, 0);
if (fixScroll) {
OS.DefWindowProc (handle, OS.WM_SETREDRAW, 1, 0);
OS.SendMessage (handle, OS.WM_SETREDRAW, 0, 0);
}
} else {
boolean scroll = true;
RECT itemRect = new RECT ();
if (OS.TreeView_GetItemRect (handle, hItem, itemRect, true)) {
forceResize ();
RECT rect = new RECT ();
OS.GetClientRect (handle, rect);
POINT pt = new POINT ();
pt.x = itemRect.left;
pt.y = itemRect.top;
if (OS.PtInRect (rect, pt)) {
pt.y = itemRect.bottom;
if (OS.PtInRect (rect, pt)) scroll = false;
}
}
if (scroll) {
boolean fixScroll = checkScroll (hItem);
if (fixScroll) {
OS.SendMessage (handle, OS.WM_SETREDRAW, 1, 0);
OS.DefWindowProc (handle, OS.WM_SETREDRAW, 0, 0);
}
OS.SendMessage (handle, OS.TVM_ENSUREVISIBLE, 0, hItem);
if (fixScroll) {
OS.DefWindowProc (handle, OS.WM_SETREDRAW, 1, 0);
OS.SendMessage (handle, OS.WM_SETREDRAW, 0, 0);
}
}
}
if (hwndParent != 0) {
RECT itemRect = new RECT ();
if (OS.TreeView_GetItemRect (handle, hItem, itemRect, true)) {
forceResize ();
RECT rect = new RECT ();
OS.GetClientRect (hwndParent, rect);
OS.MapWindowPoints (hwndParent, handle, rect, 2);
POINT pt = new POINT ();
pt.x = itemRect.left;
pt.y = itemRect.top;
if (!OS.PtInRect (rect, pt)) {
pt.y = itemRect.bottom;
if (!OS.PtInRect (rect, pt)) {
SCROLLINFO info = new SCROLLINFO ();
info.cbSize = SCROLLINFO.sizeof;
info.fMask = OS.SIF_POS;
info.nPos = Math.max (0, pt.x - Tree.INSET / 2);
OS.SetScrollInfo (hwndParent, OS.SB_HORZ, info, true);
setScrollWidth ();
}
}
}
}
updateScrollBar ();
}
/**
* Shows the column. If the column is already showing in the receiver,
* this method simply returns. Otherwise, the columns are scrolled until
* the column is visible.
*
* @param column the column to be shown
*
* @exception IllegalArgumentException
* - ERROR_NULL_ARGUMENT - if the item is null
* - ERROR_INVALID_ARGUMENT - if the item has been disposed
*
* @exception SWTException
* - ERROR_WIDGET_DISPOSED - if the receiver has been disposed
* - ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver
*
*
* @since 3.1
*/
public void showColumn (TreeColumn column) {
checkWidget ();
if (column == null) error (SWT.ERROR_NULL_ARGUMENT);
if (column.isDisposed ()) error(SWT.ERROR_INVALID_ARGUMENT);
if (column.parent != this) return;
int index = indexOf (column);
if (index == -1) return;
if (0 <= index && index < columnCount) {
forceResize ();
RECT rect = new RECT ();
OS.GetClientRect (hwndParent, rect);
OS.MapWindowPoints (hwndParent, handle, rect, 2);
RECT headerRect = new RECT ();
OS.SendMessage (hwndHeader, OS.HDM_GETITEMRECT, index, headerRect);
boolean scroll = headerRect.left < rect.left;
if (!scroll) {
int width = Math.min (rect.right - rect.left, headerRect.right - headerRect.left);
scroll = headerRect.left + width > rect.right;
}
if (scroll) {
SCROLLINFO info = new SCROLLINFO ();
info.cbSize = SCROLLINFO.sizeof;
info.fMask = OS.SIF_POS;
info.nPos = Math.max (0, headerRect.left - Tree.INSET / 2);
OS.SetScrollInfo (hwndParent, OS.SB_HORZ, info, true);
setScrollWidth ();
}
}
}
/**
* Shows the item. If the item is already showing in the receiver,
* this method simply returns. Otherwise, the items are scrolled
* and expanded until the item is visible.
*
* @param item the item to be shown
*
* @exception IllegalArgumentException
* - ERROR_NULL_ARGUMENT - if the item is null
* - ERROR_INVALID_ARGUMENT - if the item has been disposed
*
* @exception SWTException
* - ERROR_WIDGET_DISPOSED - if the receiver has been disposed
* - ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver
*
*
* @see Tree#showSelection()
*/
public void showItem (TreeItem item) {
checkWidget ();
if (item == null) error (SWT.ERROR_NULL_ARGUMENT);
if (item.isDisposed ()) error(SWT.ERROR_INVALID_ARGUMENT);
showItem (item.handle);
}
/**
* Shows the selection. If the selection is already showing in the receiver,
* this method simply returns. Otherwise, the items are scrolled until
* the selection is visible.
*
* @exception SWTException
* - ERROR_WIDGET_DISPOSED - if the receiver has been disposed
* - ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver
*
*
* @see Tree#showItem(TreeItem)
*/
public void showSelection () {
checkWidget ();
long hItem = 0;
if ((style & SWT.SINGLE) != 0) {
hItem = OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_CARET, 0);
if (hItem == 0) return;
int state = (int)OS.SendMessage (handle, OS.TVM_GETITEMSTATE, hItem, OS.TVIS_SELECTED);
if ((state & OS.TVIS_SELECTED) == 0) return;
} else {
long oldProc = OS.GetWindowLongPtr (handle, OS.GWLP_WNDPROC);
OS.SetWindowLongPtr (handle, OS.GWLP_WNDPROC, TreeProc);
if ((style & SWT.VIRTUAL) != 0) {
long hRoot = OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_ROOT, 0);
hItem = getNextSelection (hRoot);
} else {
//FIXME - this code expands first selected item it finds
int index = 0;
while (index rect.left + width) {
rect.right = rect.left + width;
} else {
rect.right = clientRect.right;
rect.left = Math.max (clientRect.left, rect.right - width);
}
} else {
rect.left = Math.max (rect.left, clientRect.left);
rect.right = Math.min (rect.right, clientRect.right);
}
long hRgn = OS.CreateRectRgn (rect.left, rect.top, rect.right, rect.bottom);
for (int i = 1; i < count; i++) {
if (rect.bottom - rect.top > DRAG_IMAGE_SIZE) break;
if (rect.bottom > clientRect.bottom) break;
RECT itemRect = items[i].getBounds (0, true, true, false);
if ((style & SWT.FULL_SELECTION) != 0) {
itemRect.left = rect.left;
itemRect.right = rect.right;
} else {
itemRect.left = Math.max (itemRect.left, clientRect.left);
itemRect.right = Math.min (itemRect.right, clientRect.right);
}
long rectRgn = OS.CreateRectRgn (itemRect.left, itemRect.top, itemRect.right, itemRect.bottom);
OS.CombineRgn (hRgn, hRgn, rectRgn, OS.RGN_OR);
OS.DeleteObject (rectRgn);
rect.bottom = itemRect.bottom;
}
OS.GetRgnBox (hRgn, rect);
/* Create resources */
long hdc = OS.GetDC (handle);
long memHdc = OS.CreateCompatibleDC (hdc);
BITMAPINFOHEADER bmiHeader = new BITMAPINFOHEADER ();
bmiHeader.biSize = BITMAPINFOHEADER.sizeof;
bmiHeader.biWidth = rect.right - rect.left;
bmiHeader.biHeight = -(rect.bottom - rect.top);
bmiHeader.biPlanes = 1;
bmiHeader.biBitCount = 32;
bmiHeader.biCompression = OS.BI_RGB;
byte [] bmi = new byte [BITMAPINFOHEADER.sizeof];
OS.MoveMemory (bmi, bmiHeader, BITMAPINFOHEADER.sizeof);
long [] pBits = new long [1];
long memDib = OS.CreateDIBSection (0, bmi, OS.DIB_RGB_COLORS, pBits, 0, 0);
if (memDib == 0) error (SWT.ERROR_NO_HANDLES);
long oldMemBitmap = OS.SelectObject (memHdc, memDib);
int colorKey = 0x0000FD;
POINT pt = new POINT ();
OS.SetWindowOrgEx (memHdc, rect.left, rect.top, pt);
OS.FillRect (memHdc, rect, findBrush (colorKey, OS.BS_SOLID));
OS.OffsetRgn (hRgn, -rect.left, -rect.top);
OS.SelectClipRgn (memHdc, hRgn);
OS.PrintWindow (handle, memHdc, 0);
OS.SetWindowOrgEx (memHdc, pt.x, pt.y, null);
OS.SelectObject (memHdc, oldMemBitmap);
OS.DeleteDC (memHdc);
OS.ReleaseDC (0, hdc);
OS.DeleteObject (hRgn);
SHDRAGIMAGE shdi = new SHDRAGIMAGE ();
shdi.hbmpDragImage = memDib;
shdi.crColorKey = colorKey;
shdi.sizeDragImage.cx = bmiHeader.biWidth;
shdi.sizeDragImage.cy = -bmiHeader.biHeight;
shdi.ptOffset.x = mousePos.x - rect.left;
shdi.ptOffset.y = mousePos.y - rect.top;
if ((style & SWT.MIRRORED) != 0) {
shdi.ptOffset.x = shdi.sizeDragImage.cx - shdi.ptOffset.x;
}
OS.MoveMemory (lParam, shdi, SHDRAGIMAGE.sizeof);
return 1;
}
}
return super.windowProc (hwnd, msg, wParam, lParam);
}
@Override
LRESULT WM_CHAR (long wParam, long lParam) {
LRESULT result = super.WM_CHAR (wParam, lParam);
if (result != null) return result;
/*
* Feature in Windows. The tree control beeps
* in WM_CHAR when the search for the item that
* matches the key stroke fails. This is the
* standard tree behavior but is unexpected when
* the key that was typed was ESC, CR or SPACE.
* The fix is to avoid calling the tree window
* proc in these cases.
*/
switch ((int)wParam) {
case ' ': {
long hItem = OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_CARET, 0);
if (hItem != 0) {
hAnchor = hItem;
OS.SendMessage (handle, OS.TVM_ENSUREVISIBLE, 0, hItem);
TVITEM tvItem = new TVITEM ();
tvItem.mask = OS.TVIF_HANDLE | OS.TVIF_STATE | OS.TVIF_PARAM;
tvItem.hItem = hItem;
if ((style & SWT.CHECK) != 0) {
tvItem.stateMask = OS.TVIS_STATEIMAGEMASK;
OS.SendMessage (handle, OS.TVM_GETITEM, 0, tvItem);
int state = tvItem.state >> 12;
if ((state & 0x1) != 0) {
state++;
} else {
--state;
}
tvItem.state = state << 12;
OS.SendMessage (handle, OS.TVM_SETITEM, 0, tvItem);
long id = OS.SendMessage (handle, OS.TVM_MAPHTREEITEMTOACCID, hItem, 0);
OS.NotifyWinEvent (OS.EVENT_OBJECT_FOCUS, handle, OS.OBJID_CLIENT, (int)id);
}
tvItem.stateMask = OS.TVIS_SELECTED;
OS.SendMessage (handle, OS.TVM_GETITEM, 0, tvItem);
if ((style & SWT.MULTI) != 0 && OS.GetKeyState (OS.VK_CONTROL) < 0) {
if ((tvItem.state & OS.TVIS_SELECTED) != 0) {
tvItem.state &= ~OS.TVIS_SELECTED;
} else {
tvItem.state |= OS.TVIS_SELECTED;
}
} else {
tvItem.state |= OS.TVIS_SELECTED;
}
OS.SendMessage (handle, OS.TVM_SETITEM, 0, tvItem);
TreeItem item = _getItem (hItem, (int)tvItem.lParam);
Event event = new Event ();
event.item = item;
sendSelectionEvent (SWT.Selection, event, false);
if ((style & SWT.CHECK) != 0) {
event = new Event ();
event.item = item;
event.detail = SWT.CHECK;
sendSelectionEvent (SWT.Selection, event, false);
}
}
return LRESULT.ZERO;
}
case SWT.CR: {
/*
* Feature in Windows. Windows sends NM_RETURN from WM_KEYDOWN
* instead of using WM_CHAR. This means that application code
* that expects to consume the key press and therefore avoid a
* SWT.DefaultSelection event from WM_CHAR will fail. The fix
* is to implement SWT.DefaultSelection in WM_CHAR instead of
* using NM_RETURN.
*/
Event event = new Event ();
long hItem = OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_CARET, 0);
if (hItem != 0) event.item = _getItem (hItem);
sendSelectionEvent (SWT.DefaultSelection, event, false);
return LRESULT.ZERO;
}
case SWT.ESC:
return LRESULT.ZERO;
}
return result;
}
@Override
LRESULT WM_ERASEBKGND (long wParam, long lParam) {
LRESULT result = super.WM_ERASEBKGND (wParam, lParam);
if ((style & SWT.DOUBLE_BUFFERED) != 0) return LRESULT.ONE;
if (findImageControl () != null) return LRESULT.ONE;
return result;
}
@Override
LRESULT WM_GETOBJECT (long wParam, long lParam) {
/*
* Ensure that there is an accessible object created for this
* control because support for checked item and tree column
* accessibility is temporarily implemented in the accessibility
* package.
*/
if ((style & SWT.CHECK) != 0 || hwndParent != 0) {
if (accessible == null) accessible = new_Accessible (this);
}
return super.WM_GETOBJECT (wParam, lParam);
}
@Override
LRESULT WM_HSCROLL (long wParam, long lParam) {
boolean fixScroll = false;
if ((style & SWT.DOUBLE_BUFFERED) != 0) {
fixScroll = (style & SWT.VIRTUAL) != 0 || hooks (SWT.EraseItem) || hooks (SWT.PaintItem);
}
if (fixScroll) {
style &= ~SWT.DOUBLE_BUFFERED;
if (explorerTheme) {
OS.SendMessage (handle, OS.TVM_SETEXTENDEDSTYLE, OS.TVS_EX_DOUBLEBUFFER, 0);
}
}
LRESULT result = super.WM_HSCROLL (wParam, lParam);
if (fixScroll) {
style |= SWT.DOUBLE_BUFFERED;
if (explorerTheme) {
OS.SendMessage (handle, OS.TVM_SETEXTENDEDSTYLE, OS.TVS_EX_DOUBLEBUFFER, OS.TVS_EX_DOUBLEBUFFER);
}
}
if (result != null) return result;
return result;
}
@Override
LRESULT WM_KEYDOWN (long wParam, long lParam) {
LRESULT result = super.WM_KEYDOWN (wParam, lParam);
if (result != null) return result;
switch ((int)wParam) {
case OS.VK_LEFT:
case OS.VK_RIGHT:
/*
* Bug in Windows. The behavior for the left and right keys is not
* changed if the orientation changes after the control was created.
* The fix is to replace VK_LEFT by VK_RIGHT and VK_RIGHT by VK_LEFT
* when the current orientation differs from the orientation used to
* create the control.
*/
boolean isRTL = (style & SWT.RIGHT_TO_LEFT) != 0;
if (isRTL != createdAsRTL) {
long code = callWindowProc (handle, OS.WM_KEYDOWN, wParam == OS.VK_RIGHT ? OS.VK_LEFT : OS.VK_RIGHT, lParam);
return new LRESULT (code);
}
break;
case OS.VK_SPACE:
/*
* Ensure that the window proc does not process VK_SPACE
* so that it can be handled in WM_CHAR. This allows the
* application to cancel an operation that is normally
* performed in WM_KEYDOWN from WM_CHAR.
*/
return LRESULT.ZERO;
case OS.VK_ADD:
if (OS.GetKeyState (OS.VK_CONTROL) < 0) {
if (hwndHeader != 0) {
TreeColumn [] newColumns = new TreeColumn [columnCount];
System.arraycopy (columns, 0, newColumns, 0, columnCount);
for (int i=0; i clientRect.bottom) break;
if ((hNewItem = hVisible) == hItem) {
OS.SendMessage (handle, OS.WM_VSCROLL, OS.SB_PAGEDOWN, 0);
}
} while (hNewItem != 0);
break;
case OS.VK_END:
hNewItem = OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_LASTVISIBLE, 0);
break;
}
if (hNewItem != 0) {
OS.SendMessage (handle, OS.TVM_ENSUREVISIBLE, 0, hNewItem);
tvItem.hItem = hNewItem;
OS.SendMessage (handle, OS.TVM_GETITEM, 0, tvItem);
boolean newSelected = (tvItem.state & OS.TVIS_SELECTED) != 0;
boolean redraw = !newSelected && getDrawing () && OS.IsWindowVisible (handle);
if (redraw) {
OS.UpdateWindow (handle);
OS.DefWindowProc (handle, OS.WM_SETREDRAW, 0, 0);
}
hSelect = hNewItem;
ignoreSelect = true;
OS.SendMessage (handle, OS.TVM_SELECTITEM, OS.TVGN_CARET, hNewItem);
ignoreSelect = false;
hSelect = 0;
if (oldSelected) {
tvItem.state = OS.TVIS_SELECTED;
tvItem.hItem = hItem;
OS.SendMessage (handle, OS.TVM_SETITEM, 0, tvItem);
}
if (!newSelected) {
tvItem.state = 0;
tvItem.hItem = hNewItem;
OS.SendMessage (handle, OS.TVM_SETITEM, 0, tvItem);
}
if (redraw) {
RECT rect1 = new RECT (), rect2 = new RECT ();
OS.TreeView_GetItemRect (handle, hItem, rect1, false);
OS.TreeView_GetItemRect (handle, hNewItem, rect2, false);
OS.DefWindowProc (handle, OS.WM_SETREDRAW, 1, 0);
OS.InvalidateRect (handle, rect1, true);
OS.InvalidateRect (handle, rect2, true);
OS.UpdateWindow (handle);
}
return LRESULT.ZERO;
}
}
}
long code = callWindowProc (handle, OS.WM_KEYDOWN, wParam, lParam);
hAnchor = OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_CARET, 0);
return new LRESULT (code);
}
}
return result;
}
@Override
LRESULT WM_KILLFOCUS (long wParam, long lParam) {
/*
* Bug in Windows. When a tree item that has an image
* with alpha is expanded or collapsed, the area where
* the image is drawn is not erased before it is drawn.
* This means that the image gets darker each time.
* The fix is to redraw the selection.
*
* Feature in Windows. When multiple item have
* the TVIS_SELECTED state, Windows redraws only
* the focused item in the color used to show the
* selection when the tree loses or gains focus.
* The fix is to force Windows to redraw the
* selection when focus is gained or lost.
*/
boolean redraw = (style & SWT.MULTI) != 0;
if (!redraw && imageList != null) {
int bits = OS.GetWindowLong (handle, OS.GWL_STYLE);
if ((bits & OS.TVS_FULLROWSELECT) == 0) {
redraw = true;
}
}
if (redraw) redrawSelection ();
return super.WM_KILLFOCUS (wParam, lParam);
}
@Override
LRESULT WM_LBUTTONDBLCLK (long wParam, long lParam) {
TVHITTESTINFO lpht = new TVHITTESTINFO ();
lpht.x = OS.GET_X_LPARAM (lParam);
lpht.y = OS.GET_Y_LPARAM (lParam);
OS.SendMessage (handle, OS.TVM_HITTEST, 0, lpht);
if (lpht.hItem != 0) {
if ((style & SWT.CHECK) != 0) {
if ((lpht.flags & OS.TVHT_ONITEMSTATEICON) != 0) {
Display display = this.display;
display.captureChanged = false;
sendMouseEvent (SWT.MouseDown, 1, handle, OS.WM_LBUTTONDOWN, wParam, lParam);
if (!sendMouseEvent (SWT.MouseDoubleClick, 1, handle, OS.WM_LBUTTONDBLCLK, wParam, lParam)) {
if (!display.captureChanged && !isDisposed ()) {
if (OS.GetCapture () != handle) OS.SetCapture (handle);
}
return LRESULT.ZERO;
}
if (!display.captureChanged && !isDisposed ()) {
if (OS.GetCapture () != handle) OS.SetCapture (handle);
}
OS.SetFocus (handle);
TVITEM tvItem = new TVITEM ();
tvItem.hItem = lpht.hItem;
tvItem.mask = OS.TVIF_HANDLE | OS.TVIF_PARAM | OS.TVIF_STATE;
tvItem.stateMask = OS.TVIS_STATEIMAGEMASK;
OS.SendMessage (handle, OS.TVM_GETITEM, 0, tvItem);
int state = tvItem.state >> 12;
if ((state & 0x1) != 0) {
state++;
} else {
--state;
}
tvItem.state = state << 12;
OS.SendMessage (handle, OS.TVM_SETITEM, 0, tvItem);
long id = OS.SendMessage (handle, OS.TVM_MAPHTREEITEMTOACCID, tvItem.hItem, 0);
OS.NotifyWinEvent (OS.EVENT_OBJECT_FOCUS, handle, OS.OBJID_CLIENT, (int)id);
Event event = new Event ();
event.item = _getItem (tvItem.hItem, (int)tvItem.lParam);
event.detail = SWT.CHECK;
sendSelectionEvent (SWT.Selection, event, false);
return LRESULT.ZERO;
}
}
}
LRESULT result = super.WM_LBUTTONDBLCLK (wParam, lParam);
if (result == LRESULT.ZERO) return result;
if (lpht.hItem != 0) {
int flags = OS.TVHT_ONITEM;
if ((style & SWT.FULL_SELECTION) != 0) {
flags |= OS.TVHT_ONITEMRIGHT | OS.TVHT_ONITEMINDENT;
} else {
if (hooks (SWT.MeasureItem)) {
lpht.flags &= ~(OS.TVHT_ONITEMICON | OS.TVHT_ONITEMLABEL);
if (hitTestSelection (lpht.hItem, lpht.x, lpht.y)) {
lpht.flags |= OS.TVHT_ONITEMICON | OS.TVHT_ONITEMLABEL;
}
}
}
if ((lpht.flags & flags) != 0) {
Event event = new Event ();
event.item = _getItem (lpht.hItem);
sendSelectionEvent (SWT.DefaultSelection, event, false);
}
}
return result;
}
@Override
LRESULT WM_LBUTTONDOWN (long wParam, long lParam) {
/*
* In a multi-select tree, if the user is collapsing a subtree that
* contains selected items, clear the selection from these items and
* issue a selection event. Only items that are selected and visible
* are cleared. This code also runs in the case when the white space
* below the last item is selected.
*/
TVHITTESTINFO lpht = new TVHITTESTINFO ();
lpht.x = OS.GET_X_LPARAM (lParam);
lpht.y = OS.GET_Y_LPARAM (lParam);
OS.SendMessage (handle, OS.TVM_HITTEST, 0, lpht);
if (lpht.hItem == 0 || (lpht.flags & OS.TVHT_ONITEMBUTTON) != 0) {
Display display = this.display;
display.captureChanged = false;
if (!sendMouseEvent (SWT.MouseDown, 1, handle, OS.WM_LBUTTONDOWN, wParam, lParam)) {
if (!display.captureChanged && !isDisposed ()) {
if (OS.GetCapture () != handle) OS.SetCapture (handle);
}
return LRESULT.ZERO;
}
boolean fixSelection = false, deselected = false;
long hOldSelection = OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_CARET, 0);
if (lpht.hItem != 0 && (style & SWT.MULTI) != 0) {
if (hOldSelection != 0) {
TVITEM tvItem = new TVITEM ();
tvItem.mask = OS.TVIF_HANDLE | OS.TVIF_STATE;
tvItem.hItem = lpht.hItem;
OS.SendMessage (handle, OS.TVM_GETITEM, 0, tvItem);
if ((tvItem.state & OS.TVIS_EXPANDED) != 0) {
fixSelection = true;
tvItem.stateMask = OS.TVIS_SELECTED;
long hNext = OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_NEXTVISIBLE, lpht.hItem);
while (hNext != 0) {
if (hNext == hAnchor) hAnchor = 0;
tvItem.hItem = hNext;
OS.SendMessage (handle, OS.TVM_GETITEM, 0, tvItem);
if ((tvItem.state & OS.TVIS_SELECTED) != 0) deselected = true;
tvItem.state = 0;
OS.SendMessage (handle, OS.TVM_SETITEM, 0, tvItem);
long hItem = hNext = OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_NEXTVISIBLE, hNext);
while (hItem != 0 && hItem != lpht.hItem) {
hItem = OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_PARENT, hItem);
}
if (hItem == 0) break;
}
}
}
}
dragStarted = gestureCompleted = false;
if (fixSelection) {
hSelect = lpht.hItem;
ignoreDeselect = ignoreSelect = lockSelection = true;
}
long code = callWindowProc (handle, OS.WM_LBUTTONDOWN, wParam, lParam);
/* Bug 225404 */
if (OS.GetFocus () != handle) OS.SetFocus (handle);
if (fixSelection) {
hSelect = 0;
ignoreDeselect = ignoreSelect = lockSelection = false;
}
long hNewSelection = OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_CARET, 0);
if (hOldSelection != hNewSelection) hAnchor = hNewSelection;
if (dragStarted) {
if (!display.captureChanged && !isDisposed ()) {
if (OS.GetCapture () != handle) OS.SetCapture (handle);
}
}
/*
* Bug in Windows. When a tree has no images and an item is
* expanded or collapsed, for some reason, Windows changes
* the size of the selection. When the user expands a tree
* item, the selection rectangle is made a few pixels larger.
* When the user collapses an item, the selection rectangle
* is restored to the original size but the selection is not
* redrawn, causing pixel corruption. The fix is to detect
* this case and redraw the item.
*/
if ((lpht.flags & OS.TVHT_ONITEMBUTTON) != 0) {
int bits = OS.GetWindowLong (handle, OS.GWL_STYLE);
if ((bits & OS.TVS_FULLROWSELECT) == 0) {
if (OS.SendMessage (handle, OS.TVM_GETIMAGELIST, OS.TVSIL_NORMAL, 0) == 0) {
long hItem = OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_CARET, 0);
if (hItem != 0) {
RECT rect = new RECT ();
if (OS.TreeView_GetItemRect (handle, hItem, rect, false)) {
OS.InvalidateRect (handle, rect, true);
}
}
}
}
}
if (deselected) {
Event event = new Event ();
event.item = _getItem (lpht.hItem);
sendSelectionEvent (SWT.Selection, event, false);
}
return new LRESULT (code);
}
/* Look for check/uncheck */
if ((style & SWT.CHECK) != 0) {
if ((lpht.flags & OS.TVHT_ONITEMSTATEICON) != 0) {
Display display = this.display;
display.captureChanged = false;
if (!sendMouseEvent (SWT.MouseDown, 1, handle, OS.WM_LBUTTONDOWN, wParam, lParam)) {
if (!display.captureChanged && !isDisposed ()) {
if (OS.GetCapture () != handle) OS.SetCapture (handle);
}
return LRESULT.ZERO;
}
if (!display.captureChanged && !isDisposed ()) {
if (OS.GetCapture () != handle) OS.SetCapture (handle);
}
OS.SetFocus (handle);
TVITEM tvItem = new TVITEM ();
tvItem.hItem = lpht.hItem;
tvItem.mask = OS.TVIF_HANDLE | OS.TVIF_PARAM | OS.TVIF_STATE;
tvItem.stateMask = OS.TVIS_STATEIMAGEMASK;
OS.SendMessage (handle, OS.TVM_GETITEM, 0, tvItem);
int state = tvItem.state >> 12;
if ((state & 0x1) != 0) {
state++;
} else {
--state;
}
tvItem.state = state << 12;
OS.SendMessage (handle, OS.TVM_SETITEM, 0, tvItem);
long id = OS.SendMessage (handle, OS.TVM_MAPHTREEITEMTOACCID, tvItem.hItem, 0);
OS.NotifyWinEvent (OS.EVENT_OBJECT_FOCUS, handle, OS.OBJID_CLIENT, (int)id);
Event event = new Event ();
event.item = _getItem (tvItem.hItem, (int)tvItem.lParam);
event.detail = SWT.CHECK;
sendSelectionEvent (SWT.Selection, event, false);
return LRESULT.ZERO;
}
}
/*
* Feature in Windows. When the tree has the style
* TVS_FULLROWSELECT, the background color for the
* entire row is filled when an item is painted,
* drawing on top of any custom drawing. The fix
* is to emulate TVS_FULLROWSELECT.
*/
boolean selected = false;
boolean fakeSelection = false;
if (lpht.hItem != 0) {
if ((style & SWT.FULL_SELECTION) != 0) {
int bits = OS.GetWindowLong (handle, OS.GWL_STYLE);
if ((bits & OS.TVS_FULLROWSELECT) == 0) fakeSelection = true;
} else {
if (hooks (SWT.MeasureItem)) {
selected = hitTestSelection (lpht.hItem, lpht.x, lpht.y);
if (selected) {
if ((lpht.flags & OS.TVHT_ONITEM) == 0) fakeSelection = true;
}
}
}
}
/* Process the mouse when an item is not selected */
if (!selected && (style & SWT.FULL_SELECTION) == 0) {
if ((lpht.flags & OS.TVHT_ONITEM) == 0) {
Display display = this.display;
display.captureChanged = false;
if (!sendMouseEvent (SWT.MouseDown, 1, handle, OS.WM_LBUTTONDOWN, wParam, lParam)) {
if (!display.captureChanged && !isDisposed ()) {
if (OS.GetCapture () != handle) OS.SetCapture (handle);
}
return LRESULT.ZERO;
}
long code = callWindowProc (handle, OS.WM_LBUTTONDOWN, wParam, lParam);
/* Bug 225404 */
if (OS.GetFocus () != handle) OS.SetFocus (handle);
if (!display.captureChanged && !isDisposed ()) {
if (OS.GetCapture () != handle) OS.SetCapture (handle);
}
return new LRESULT (code);
}
}
/* Get the selected state of the item under the mouse */
TVITEM tvItem = new TVITEM ();
tvItem.mask = OS.TVIF_HANDLE | OS.TVIF_STATE;
tvItem.stateMask = OS.TVIS_SELECTED;
boolean hittestSelected = false;
if ((style & SWT.MULTI) != 0) {
tvItem.hItem = lpht.hItem;
OS.SendMessage (handle, OS.TVM_GETITEM, 0, tvItem);
hittestSelected = (tvItem.state & OS.TVIS_SELECTED) != 0;
}
/* Get the selected state of the last selected item */
long hOldItem = OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_CARET, 0);
if ((style & SWT.MULTI) != 0) {
tvItem.hItem = hOldItem;
OS.SendMessage (handle, OS.TVM_GETITEM, 0, tvItem);
/* Check for CONTROL or drag selection */
if (hittestSelected || (wParam & OS.MK_CONTROL) != 0) {
/*
* Feature in Windows. When the tree is not drawing focus
* and the user selects a tree item while the CONTROL key
* is down, the tree window proc sends WM_UPDATEUISTATE
* to the top level window, causing controls within the shell
* to redraw. When drag detect is enabled, the tree window
* proc runs a modal loop that allows WM_PAINT messages to be
* delivered during WM_LBUTTONDOWN. When WM_SETREDRAW is used
* to disable drawing for the tree and a WM_PAINT happens for
* a parent of the tree (or a sibling that overlaps), the parent
* will draw on top of the tree. If WM_SETREDRAW is turned back
* on without redrawing the entire tree, pixel corruption occurs.
* This case only seems to happen when the tree has been given
* focus from WM_MOUSEACTIVATE of the shell. The fix is to
* force the WM_UPDATEUISTATE to be sent before disabling
* the drawing.
*
* NOTE: Any redraw of a parent (or sibling) will be dispatched
* during the modal drag detect loop. This code only fixes the
* case where the tree causes a redraw from WM_UPDATEUISTATE.
* In SWT, the InvalidateRect() that caused the pixel corruption
* is found in Composite.WM_UPDATEUISTATE().
*/
int uiState = (int)OS.SendMessage (handle, OS.WM_QUERYUISTATE, 0, 0);
if ((uiState & OS.UISF_HIDEFOCUS) != 0) {
OS.SendMessage (handle, OS.WM_CHANGEUISTATE, OS.UIS_INITIALIZE, 0);
}
OS.UpdateWindow (handle);
OS.DefWindowProc (handle, OS.WM_SETREDRAW, 0, 0);
} else {
deselectAll ();
}
}
/* Do the selection */
Display display = this.display;
display.captureChanged = false;
if (!sendMouseEvent (SWT.MouseDown, 1, handle, OS.WM_LBUTTONDOWN, wParam, lParam)) {
if (!display.captureChanged && !isDisposed ()) {
if (OS.GetCapture () != handle) OS.SetCapture (handle);
}
return LRESULT.ZERO;
}
hSelect = lpht.hItem;
dragStarted = gestureCompleted = false;
ignoreDeselect = ignoreSelect = true;
long code = callWindowProc (handle, OS.WM_LBUTTONDOWN, wParam, lParam);
/* Bug 225404 */
if (OS.GetFocus () != handle) OS.SetFocus (handle);
long hNewItem = OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_CARET, 0);
if (fakeSelection) {
if (hOldItem == 0 || (hNewItem == hOldItem && lpht.hItem != hOldItem)) {
OS.SendMessage (handle, OS.TVM_SELECTITEM, OS.TVGN_CARET, lpht.hItem);
hNewItem = OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_CARET, 0);
}
if (!dragStarted && (state & DRAG_DETECT) != 0 && hooks (SWT.DragDetect)) {
dragStarted = dragDetect (handle, lpht.x, lpht.y, false, null, null);
}
}
ignoreDeselect = ignoreSelect = false;
hSelect = 0;
if (dragStarted) {
if (!display.captureChanged && !isDisposed ()) {
if (OS.GetCapture () != handle) OS.SetCapture (handle);
}
}
/*
* Feature in Windows. When the old and new focused item
* are the same, Windows does not check to make sure that
* the item is actually selected, not just focused. The
* fix is to force the item to draw selected by setting
* the state mask. This is only necessary when the tree
* is single select.
*/
if ((style & SWT.SINGLE) != 0) {
if (hOldItem == hNewItem) {
tvItem.mask = OS.TVIF_HANDLE | OS.TVIF_STATE;
tvItem.state = OS.TVIS_SELECTED;
tvItem.stateMask = OS.TVIS_SELECTED;
tvItem.hItem = hNewItem;
OS.SendMessage (handle, OS.TVM_SETITEM, 0, tvItem);
}
}
/* Reselect the last item that was unselected */
if ((style & SWT.MULTI) != 0) {
/* Check for CONTROL and reselect the last item */
if (hittestSelected || (wParam & OS.MK_CONTROL) != 0) {
if (hOldItem == hNewItem && hOldItem == lpht.hItem) {
if ((wParam & OS.MK_CONTROL) != 0) {
tvItem.state ^= OS.TVIS_SELECTED;
if (dragStarted) tvItem.state = OS.TVIS_SELECTED;
OS.SendMessage (handle, OS.TVM_SETITEM, 0, tvItem);
}
} else {
if ((tvItem.state & OS.TVIS_SELECTED) != 0) {
tvItem.state = OS.TVIS_SELECTED;
OS.SendMessage (handle, OS.TVM_SETITEM, 0, tvItem);
}
if ((wParam & OS.MK_CONTROL) != 0 && !dragStarted) {
if (hittestSelected) {
tvItem.state = 0;
tvItem.hItem = lpht.hItem;
OS.SendMessage (handle, OS.TVM_SETITEM, 0, tvItem);
}
}
}
RECT rect1 = new RECT (), rect2 = new RECT ();
OS.TreeView_GetItemRect (handle, hOldItem, rect1, false);
OS.TreeView_GetItemRect (handle, hNewItem, rect2, false);
OS.DefWindowProc (handle, OS.WM_SETREDRAW, 1, 0);
OS.InvalidateRect (handle, rect1, true);
OS.InvalidateRect (handle, rect2, true);
OS.UpdateWindow (handle);
}
/* Check for SHIFT or normal select and deselect/reselect items */
if ((wParam & OS.MK_CONTROL) == 0) {
if (!hittestSelected || !dragStarted) {
tvItem.state = 0;
long oldProc = OS.GetWindowLongPtr (handle, OS.GWLP_WNDPROC);
OS.SetWindowLongPtr (handle, OS.GWLP_WNDPROC, TreeProc);
if ((style & SWT.VIRTUAL) != 0) {
long hItem = OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_ROOT, 0);
deselect (hItem, tvItem, hNewItem);
} else {
for (int i=0; i= 0) {
if (items [count] != null) break;
--count;
}
count++;
if (items.length > 4 && items.length - count > 3) {
int length = Math.max (4, (count + 3) / 4 * 4);
TreeItem [] newItems = new TreeItem [length];
System.arraycopy (items, 0, newItems, 0, count);
items = newItems;
}
shrink = false;
}
if ((style & SWT.DOUBLE_BUFFERED) != 0 || findImageControl () != null) {
boolean doubleBuffer = true;
if (explorerTheme) {
int exStyle = (int)OS.SendMessage (handle, OS.TVM_GETEXTENDEDSTYLE, 0, 0);
if ((exStyle & OS.TVS_EX_DOUBLEBUFFER) != 0) doubleBuffer = false;
}
if (doubleBuffer) {
GC gc = null;
long paintDC = 0;
PAINTSTRUCT ps = new PAINTSTRUCT ();
boolean hooksPaint = hooks (SWT.Paint) || filters (SWT.Paint);
if (hooksPaint) {
GCData data = new GCData ();
data.ps = ps;
data.hwnd = handle;
gc = GC.win32_new (this, data);
paintDC = gc.handle;
} else {
paintDC = OS.BeginPaint (handle, ps);
}
int width = ps.right - ps.left;
int height = ps.bottom - ps.top;
if (width != 0 && height != 0) {
long hDC = OS.CreateCompatibleDC (paintDC);
POINT lpPoint1 = new POINT (), lpPoint2 = new POINT ();
OS.SetWindowOrgEx (hDC, ps.left, ps.top, lpPoint1);
OS.SetBrushOrgEx (hDC, ps.left, ps.top, lpPoint2);
long hBitmap = OS.CreateCompatibleBitmap (paintDC, width, height);
long hOldBitmap = OS.SelectObject (hDC, hBitmap);
RECT rect = new RECT ();
OS.SetRect (rect, ps.left, ps.top, ps.right, ps.bottom);
drawBackground (hDC, rect);
callWindowProc (handle, OS.WM_PAINT, hDC, 0);
OS.SetWindowOrgEx (hDC, lpPoint1.x, lpPoint1.y, null);
OS.SetBrushOrgEx (hDC, lpPoint2.x, lpPoint2.y, null);
OS.BitBlt (paintDC, ps.left, ps.top, width, height, hDC, 0, 0, OS.SRCCOPY);
OS.SelectObject (hDC, hOldBitmap);
OS.DeleteObject (hBitmap);
OS.DeleteObject (hDC);
if (hooksPaint) {
Event event = new Event ();
event.gc = gc;
event.setBoundsInPixels(new Rectangle(ps.left, ps.top, ps.right - ps.left, ps.bottom - ps.top));
sendEvent (SWT.Paint, event);
// widget could be disposed at this point
event.gc = null;
}
}
if (hooksPaint) {
gc.dispose ();
} else {
OS.EndPaint (handle, ps);
}
return LRESULT.ZERO;
}
}
return super.WM_PAINT (wParam, lParam);
}
@Override
LRESULT WM_SETCURSOR (long wParam, long lParam) {
LRESULT result = super.WM_SETCURSOR (wParam, lParam);
if (result != null) return result;
/*
* Feature in Windows. On Windows 7, the tree control show the
* hand cursor when the mouse is over an item. This is the
* correct Windows 7 behavior but not correct for SWT. The fix
* is to always ensure a cursor is set.
*/
if (OS.WIN32_VERSION >= OS.VERSION (6, 1)) {
if (wParam == handle) {
int hitTest = (short) OS.LOWORD (lParam);
if (hitTest == OS.HTCLIENT) {
OS.SetCursor (OS.LoadCursor (0, OS.IDC_ARROW));
return LRESULT.ONE;
}
}
}
return null;
}
@Override
LRESULT WM_SETFOCUS (long wParam, long lParam) {
/*
* Bug in Windows. When a tree item that has an image
* with alpha is expanded or collapsed, the area where
* the image is drawn is not erased before it is drawn.
* This means that the image gets darker each time.
* The fix is to redraw the selection.
*
* Feature in Windows. When multiple item have
* the TVIS_SELECTED state, Windows redraws only
* the focused item in the color used to show the
* selection when the tree loses or gains focus.
* The fix is to force Windows to redraw the
* selection when focus is gained or lost.
*/
boolean redraw = (style & SWT.MULTI) != 0;
if (!redraw && imageList != null) {
int bits = OS.GetWindowLong (handle, OS.GWL_STYLE);
if ((bits & OS.TVS_FULLROWSELECT) == 0) {
redraw = true;
}
}
if (redraw) redrawSelection ();
return super.WM_SETFOCUS (wParam, lParam);
}
@Override
LRESULT WM_SETFONT (long wParam, long lParam) {
LRESULT result = super.WM_SETFONT (wParam, lParam);
if (result != null) return result;
if (hwndHeader != 0) {
/*
* Bug in Windows. When a header has a sort indicator
* triangle, Windows resizes the indicator based on the
* size of the n-1th font. The fix is to always make
* the n-1th font be the default. This makes the sort
* indicator always be the default size.
*/
OS.SendMessage (hwndHeader, OS.WM_SETFONT, 0, lParam);
OS.SendMessage (hwndHeader, OS.WM_SETFONT, wParam, lParam);
}
if (itemToolTipHandle != 0) {
OS.ShowWindow (itemToolTipHandle, OS.SW_HIDE);
OS.SendMessage (itemToolTipHandle, OS.WM_SETFONT, wParam, lParam);
}
if (headerToolTipHandle != 0) {
OS.SendMessage (headerToolTipHandle, OS.WM_SETFONT, wParam, lParam);
updateHeaderToolTips ();
}
return result;
}
@Override
LRESULT WM_SETREDRAW (long wParam, long lParam) {
LRESULT result = super.WM_SETREDRAW (wParam, lParam);
if (result != null) return result;
if (itemToolTipHandle != 0) OS.ShowWindow (itemToolTipHandle, OS.SW_HIDE);
/*
* Bug in Windows. Under certain circumstances, when
* WM_SETREDRAW is used to turn off drawing and then
* TVM_GETITEMRECT is sent to get the bounds of an item
* that is not inside the client area, Windows segment
* faults. The fix is to call the default window proc
* rather than the default tree proc.
*
* NOTE: This problem is intermittent and happens on
* Windows Vista running under the theme manager.
*/
long code = OS.DefWindowProc (handle, OS.WM_SETREDRAW, wParam, lParam);
return code == 0 ? LRESULT.ZERO : new LRESULT (code);
}
@Override
LRESULT WM_SIZE (long wParam, long lParam) {
if (itemToolTipHandle != 0) OS.ShowWindow (itemToolTipHandle, OS.SW_HIDE);
/*
* Bug in Windows. When TVS_NOHSCROLL is set when the
* size of the tree is zero, the scroll bar is shown the
* next time the tree resizes. The fix is to hide the
* scroll bar every time the tree is resized.
*/
int bits = OS.GetWindowLong (handle, OS.GWL_STYLE);
if ((bits & OS.TVS_NOHSCROLL) != 0) {
OS.ShowScrollBar (handle, OS.SB_HORZ, false);
}
/*
* Bug in Windows. On Vista, when the Explorer theme
* is used with a full selection tree, when the tree
* is resized to be smaller, the rounded right edge
* of the selected items is not drawn. The fix is the
* redraw the entire tree.
*/
if (explorerTheme && (style & SWT.FULL_SELECTION) != 0) {
OS.InvalidateRect (handle, null, false);
}
if (ignoreResize) return null;
return super.WM_SIZE (wParam, lParam);
}
@Override
LRESULT WM_SYSCOLORCHANGE (long wParam, long lParam) {
LRESULT result = super.WM_SYSCOLORCHANGE (wParam, lParam);
if (result != null) return result;
/*
* Bug in Windows. When the tree is using the explorer
* theme, it does not use COLOR_WINDOW_TEXT for the
* default foreground color. The fix is to explicitly
* set the foreground.
*/
if (explorerTheme) {
if (foreground == -1) setForegroundPixel (-1);
}
if ((style & SWT.CHECK) != 0) setCheckboxImageList ();
return result;
}
@Override
LRESULT WM_VSCROLL (long wParam, long lParam) {
boolean fixScroll = false;
if ((style & SWT.DOUBLE_BUFFERED) != 0) {
int code = OS.LOWORD (wParam);
switch (code) {
case OS.SB_TOP:
case OS.SB_BOTTOM:
case OS.SB_LINEDOWN:
case OS.SB_LINEUP:
case OS.SB_PAGEDOWN:
case OS.SB_PAGEUP:
fixScroll = (style & SWT.VIRTUAL) != 0 || hooks (SWT.EraseItem) || hooks (SWT.PaintItem);
break;
}
}
if (fixScroll) {
style &= ~SWT.DOUBLE_BUFFERED;
if (explorerTheme) {
OS.SendMessage (handle, OS.TVM_SETEXTENDEDSTYLE, OS.TVS_EX_DOUBLEBUFFER, 0);
}
}
LRESULT result = super.WM_VSCROLL (wParam, lParam);
if (fixScroll) {
style |= SWT.DOUBLE_BUFFERED;
if (explorerTheme) {
OS.SendMessage (handle, OS.TVM_SETEXTENDEDSTYLE, OS.TVS_EX_DOUBLEBUFFER, OS.TVS_EX_DOUBLEBUFFER);
}
}
if (result != null) return result;
return result;
}
@Override
LRESULT WM_TIMER (long wParam, long lParam) {
LRESULT result = super.WM_TIMER (wParam, lParam);
if (result != null) return result;
/* Bug in Windows. When the expandos are visible (or in process of fading away)
* and the tree control is hidden the animation timer does not stop calling the
* window proc till the tree is visible again. This can cause performance problems
* specially in cases there the application has several tree controls in this state.
* The fix is to detect a timer that repeats itself several times when the control
* is not visible and stop it. The timer is stopped by sending a fake mouse move event.
*
* Note: Just killing the timer could cause some internal clean up task related to the
* animation not to run.
*/
long bits = OS.SendMessage (handle, OS.TVM_GETEXTENDEDSTYLE, 0, 0);
if ((bits & OS.TVS_EX_FADEINOUTEXPANDOS) != 0) {
if (!OS.IsWindowVisible (handle)) {
if (lastTimerID == wParam) {
lastTimerCount++;
} else {
lastTimerCount = 0;
}
lastTimerID = wParam;
if (lastTimerCount >= TIMER_MAX_COUNT) {
OS.CallWindowProc (TreeProc, handle, OS.WM_MOUSEMOVE, 0, 0);
lastTimerID = -1;
lastTimerCount = 0;
}
} else {
lastTimerID = -1;
lastTimerCount = 0;
}
}
return result;
};
@Override
LRESULT wmColorChild (long wParam, long lParam) {
if (findImageControl () != null) {
return new LRESULT (OS.GetStockObject (OS.NULL_BRUSH));
}
/*
* Feature in Windows. Tree controls send WM_CTLCOLOREDIT
* to allow application code to change the default colors.
* This is undocumented and conflicts with TVM_SETTEXTCOLOR
* and TVM_SETBKCOLOR, the documented way to do this. The
* fix is to ignore WM_CTLCOLOREDIT messages from trees.
*/
return null;
}
@Override
LRESULT wmNotify (NMHDR hdr, long wParam, long lParam) {
if (hdr.hwndFrom == itemToolTipHandle) {
LRESULT result = wmNotifyToolTip (hdr, wParam, lParam);
if (result != null) return result;
}
if (hdr.hwndFrom == hwndHeader) {
LRESULT result = wmNotifyHeader (hdr, wParam, lParam);
if (result != null) return result;
}
return super.wmNotify (hdr, wParam, lParam);
}
@Override
LRESULT wmNotifyChild (NMHDR hdr, long wParam, long lParam) {
switch (hdr.code) {
case OS.TVN_GETDISPINFO: {
NMTVDISPINFO lptvdi = new NMTVDISPINFO ();
OS.MoveMemory (lptvdi, lParam, NMTVDISPINFO.sizeof);
if ((style & SWT.VIRTUAL) != 0) {
/*
* Feature in Windows. When a new tree item is inserted
* using TVM_INSERTITEM, a TVN_GETDISPINFO is sent before
* TVM_INSERTITEM returns and before the item is added to
* the items array. The fix is to check for null.
*
* NOTE: This only happens on XP with the version 6.00 of
* COMCTL32.DLL.
*/
boolean checkVisible = true;
/*
* When an item is being deleted from a virtual tree, do not
* allow the application to provide data for a new item that
* becomes visible until the item has been removed from the
* items array. Because arbitrary application code can run
* during the callback, the items array might be accessed
* in an inconsistent state. Rather than answering the data
* right away, queue a redraw for later.
*/
if (!ignoreShrink) {
if (items != null && lptvdi.lParam != -1) {
if (items [(int)lptvdi.lParam] != null && items [(int)lptvdi.lParam].cached) {
checkVisible = false;
}
}
}
if (checkVisible) {
if (!getDrawing () || !OS.IsWindowVisible (handle)) break;
RECT itemRect = new RECT ();
if (!OS.TreeView_GetItemRect (handle, lptvdi.hItem, itemRect, false)) {
break;
}
RECT rect = new RECT ();
OS.GetClientRect (handle, rect);
if (!OS.IntersectRect (rect, rect, itemRect)) break;
if (ignoreShrink) {
OS.InvalidateRect (handle, rect, true);
break;
}
}
}
if (items == null) break;
/*
* Bug in Windows. If the lParam field of TVITEM
* is changed during custom draw using TVM_SETITEM,
* the lItemlParam field of the NMTVCUSTOMDRAW struct
* is not updated until the next custom draw. The
* fix is to query the field from the item instead
* of using the struct.
*/
int id = (int)lptvdi.lParam;
if ((style & SWT.VIRTUAL) != 0) {
if (id == -1) {
TVITEM tvItem = new TVITEM ();
tvItem.mask = OS.TVIF_HANDLE | OS.TVIF_PARAM;
tvItem.hItem = lptvdi.hItem;
OS.SendMessage (handle, OS.TVM_GETITEM, 0, tvItem);
id = (int)tvItem.lParam;
}
}
TreeItem item = _getItem (lptvdi.hItem, id);
/*
* Feature in Windows. When a new tree item is inserted
* using TVM_INSERTITEM, a TVN_GETDISPINFO is sent before
* TVM_INSERTITEM returns and before the item is added to
* the items array. The fix is to check for null.
*
* NOTE: This only happens on XP with the version 6.00 of
* COMCTL32.DLL.
*
* Feature in Windows. When TVM_DELETEITEM is called with
* TVI_ROOT to remove all items from a tree, under certain
* circumstances, the tree sends TVN_GETDISPINFO for items
* that are about to be disposed. The fix is to check for
* disposed items.
*/
if (item == null) break;
if (item.isDisposed ()) break;
if (!item.cached) {
if ((style & SWT.VIRTUAL) != 0) {
if (!checkData (item, false)) break;
}
if (painted) item.cached = true;
}
int index = 0;
if (hwndHeader != 0) {
index = (int)OS.SendMessage (hwndHeader, OS.HDM_ORDERTOINDEX, 0, 0);
}
if ((lptvdi.mask & OS.TVIF_TEXT) != 0) {
String string = null;
if (index == 0) {
string = item.text;
} else {
String [] strings = item.strings;
if (strings != null) string = strings [index];
}
if (string != null) {
int length = Math.min (string.length() + 1, lptvdi.cchTextMax);
char [] buffer = new char [length];
string.getChars(0, length - 1, buffer, 0);
OS.MoveMemory (lptvdi.pszText, buffer, length * TCHAR.sizeof);
lptvdi.cchTextMax = length;
}
}
if ((lptvdi.mask & (OS.TVIF_IMAGE | OS.TVIF_SELECTEDIMAGE)) != 0) {
Image image = null;
if (index == 0) {
image = item.image;
} else {
Image [] images = item.images;
if (images != null) image = images [index];
}
lptvdi.iImage = lptvdi.iSelectedImage = OS.I_IMAGENONE;
if (image != null) {
lptvdi.iImage = lptvdi.iSelectedImage = imageIndex (image, index);
}
if (explorerTheme && OS.IsWindowEnabled (handle)) {
if (findImageControl () != null) {
lptvdi.iImage = lptvdi.iSelectedImage = OS.I_IMAGENONE;
}
}
}
OS.MoveMemory (lParam, lptvdi, NMTVDISPINFO.sizeof);
break;
}
case OS.NM_CUSTOMDRAW: {
if (hdr.hwndFrom == hwndHeader) break;
if (hooks (SWT.MeasureItem)) {
if (hwndHeader == 0) createParent ();
}
if (!customDraw && findImageControl () == null) {
if (OS.IsAppThemed ()) {
if (sortColumn == null || sortDirection == SWT.NONE) {
break;
}
}
}
NMTVCUSTOMDRAW nmcd = new NMTVCUSTOMDRAW ();
OS.MoveMemory (nmcd, lParam, NMTVCUSTOMDRAW.sizeof);
switch (nmcd.dwDrawStage) {
case OS.CDDS_PREPAINT: return CDDS_PREPAINT (nmcd, wParam, lParam);
case OS.CDDS_ITEMPREPAINT: return CDDS_ITEMPREPAINT (nmcd, wParam, lParam);
case OS.CDDS_ITEMPOSTPAINT: return CDDS_ITEMPOSTPAINT (nmcd, wParam, lParam);
case OS.CDDS_POSTPAINT: return CDDS_POSTPAINT (nmcd, wParam, lParam);
}
break;
}
case OS.NM_DBLCLK: {
/*
* When the user double clicks on a tree item
* or a line beside the item, the window proc
* for the tree collapses or expand the branch.
* When application code associates an action
* with double clicking, then the tree expand
* is unexpected and unwanted. The fix is to
* avoid the operation by testing to see whether
* the mouse was inside a tree item.
*/
if (hooks (SWT.MeasureItem)) return LRESULT.ONE;
if (hooks (SWT.DefaultSelection)) {
POINT pt = new POINT ();
int pos = OS.GetMessagePos ();
OS.POINTSTOPOINT (pt, pos);
OS.ScreenToClient (handle, pt);
TVHITTESTINFO lpht = new TVHITTESTINFO ();
lpht.x = pt.x;
lpht.y = pt.y;
OS.SendMessage (handle, OS.TVM_HITTEST, 0, lpht);
if (lpht.hItem != 0 && (lpht.flags & OS.TVHT_ONITEM) != 0) {
return LRESULT.ONE;
}
}
break;
}
/*
* Bug in Windows. On Vista, when TVM_SELECTITEM is called
* with TVGN_CARET in order to set the selection, for some
* reason, Windows deselects the previous two items that
* were selected. The fix is to stop the selection from
* changing on all but the item that is supposed to be
* selected.
*/
case OS.TVN_ITEMCHANGING: {
if ((style & SWT.MULTI) != 0) {
if (hSelect != 0) {
NMTVITEMCHANGE pnm = new NMTVITEMCHANGE ();
OS.MoveMemory (pnm, lParam, NMTVITEMCHANGE.sizeof);
if (hSelect == pnm.hItem) break;
return LRESULT.ONE;
}
}
break;
}
case OS.TVN_SELCHANGING: {
if ((style & SWT.MULTI) != 0) {
if (lockSelection) {
/* Save the old selection state for both items */
NMTREEVIEW treeView = new NMTREEVIEW ();
OS.MoveMemory (treeView, lParam, NMTREEVIEW.sizeof);
TVITEM tvItem = treeView.itemOld;
oldSelected = (tvItem.state & OS.TVIS_SELECTED) != 0;
tvItem = treeView.itemNew;
newSelected = (tvItem.state & OS.TVIS_SELECTED) != 0;
}
}
if (!ignoreSelect && !ignoreDeselect) {
hAnchor = 0;
if ((style & SWT.MULTI) != 0) deselectAll ();
}
break;
}
case OS.TVN_SELCHANGED: {
NMTREEVIEW treeView = null;
if ((style & SWT.MULTI) != 0) {
if (lockSelection) {
/* Restore the old selection state of both items */
if (oldSelected) {
if (treeView == null) {
treeView = new NMTREEVIEW ();
OS.MoveMemory (treeView, lParam, NMTREEVIEW.sizeof);
}
TVITEM tvItem = treeView.itemOld;
tvItem.mask = OS.TVIF_STATE;
tvItem.stateMask = OS.TVIS_SELECTED;
tvItem.state = OS.TVIS_SELECTED;
OS.SendMessage (handle, OS.TVM_SETITEM, 0, tvItem);
}
if (!newSelected && ignoreSelect) {
if (treeView == null) {
treeView = new NMTREEVIEW ();
OS.MoveMemory (treeView, lParam, NMTREEVIEW.sizeof);
}
TVITEM tvItem = treeView.itemNew;
tvItem.mask = OS.TVIF_STATE;
tvItem.stateMask = OS.TVIS_SELECTED;
tvItem.state = 0;
OS.SendMessage (handle, OS.TVM_SETITEM, 0, tvItem);
}
}
}
if (!ignoreSelect) {
if (treeView == null) {
treeView = new NMTREEVIEW ();
OS.MoveMemory (treeView, lParam, NMTREEVIEW.sizeof);
}
TVITEM tvItem = treeView.itemNew;
hAnchor = tvItem.hItem;
Event event = new Event ();
event.item = _getItem (tvItem.hItem, (int)tvItem.lParam);
sendSelectionEvent (SWT.Selection, event, false);
}
updateScrollBar ();
break;
}
case OS.TVN_ITEMEXPANDING: {
if (itemToolTipHandle != 0) OS.ShowWindow (itemToolTipHandle, OS.SW_HIDE);
boolean runExpanded = false;
if ((style & SWT.VIRTUAL) != 0) style &= ~SWT.DOUBLE_BUFFERED;
if (hooks (SWT.EraseItem) || hooks (SWT.PaintItem)) style &= ~SWT.DOUBLE_BUFFERED;
if (findImageControl () != null && getDrawing () && OS.IsWindowVisible (handle)) {
OS.DefWindowProc (handle, OS.WM_SETREDRAW, 0, 0);
}
/*
* Bug in Windows. When TVM_SETINSERTMARK is used to set
* an insert mark for a tree and an item is expanded or
* collapsed near the insert mark, the tree does not redraw
* the insert mark properly. The fix is to hide and show
* the insert mark whenever an item is expanded or collapsed.
*/
if (hInsert != 0) {
OS.SendMessage (handle, OS.TVM_SETINSERTMARK, 0, 0);
}
if (!ignoreExpand) {
NMTREEVIEW treeView = new NMTREEVIEW ();
OS.MoveMemory (treeView, lParam, NMTREEVIEW.sizeof);
TVITEM tvItem = treeView.itemNew;
/*
* Feature in Windows. In some cases, TVM_ITEMEXPANDING
* is sent from within TVM_DELETEITEM for the tree item
* being destroyed. By the time the message is sent,
* the item has already been removed from the list of
* items. The fix is to check for null.
*/
if (items == null) break;
TreeItem item = _getItem (tvItem.hItem, (int)tvItem.lParam);
if (item == null) break;
Event event = new Event ();
event.item = item;
switch (treeView.action) {
case OS.TVE_EXPAND:
/*
* Bug in Windows. When the numeric keypad asterisk
* key is used to expand every item in the tree, Windows
* sends TVN_ITEMEXPANDING to items in the tree that
* have already been expanded. The fix is to detect
* that the item is already expanded and ignore the
* notification.
*/
if ((tvItem.state & OS.TVIS_EXPANDED) == 0) {
sendEvent (SWT.Expand, event);
if (isDisposed ()) return LRESULT.ZERO;
}
break;
case OS.TVE_COLLAPSE:
sendEvent (SWT.Collapse, event);
if (isDisposed ()) return LRESULT.ZERO;
break;
}
/*
* Bug in Windows. When all of the items are deleted during
* TVN_ITEMEXPANDING, Windows does not send TVN_ITEMEXPANDED.
* The fix is to detect this case and run the TVN_ITEMEXPANDED
* code in this method.
*/
long hFirstItem = OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_CHILD, tvItem.hItem);
runExpanded = hFirstItem == 0;
}
if (!runExpanded) break;
//FALL THROUGH
}
case OS.TVN_ITEMEXPANDED: {
if ((style & SWT.VIRTUAL) != 0) style |= SWT.DOUBLE_BUFFERED;
if (hooks (SWT.EraseItem) || hooks (SWT.PaintItem)) style |= SWT.DOUBLE_BUFFERED;
if (findImageControl () != null && getDrawing () /*&& OS.IsWindowVisible (handle)*/) {
OS.DefWindowProc (handle, OS.WM_SETREDRAW, 1, 0);
OS.InvalidateRect (handle, null, true);
}
/*
* Bug in Windows. When TVM_SETINSERTMARK is used to set
* an insert mark for a tree and an item is expanded or
* collapsed near the insert mark, the tree does not redraw
* the insert mark properly. The fix is to hide and show
* the insert mark whenever an item is expanded or collapsed.
*/
if (hInsert != 0) {
OS.SendMessage (handle, OS.TVM_SETINSERTMARK, insertAfter ? 1 : 0, hInsert);
}
/*
* Bug in Windows. When a tree item that has an image
* with alpha is expanded or collapsed, the area where
* the image is drawn is not erased before it is drawn.
* This means that the image gets darker each time.
* The fix is to redraw the item.
*/
if (imageList != null) {
NMTREEVIEW treeView = new NMTREEVIEW ();
OS.MoveMemory (treeView, lParam, NMTREEVIEW.sizeof);
TVITEM tvItem = treeView.itemNew;
if (tvItem.hItem != 0) {
int bits = OS.GetWindowLong (handle, OS.GWL_STYLE);
if ((bits & OS.TVS_FULLROWSELECT) == 0) {
RECT rect = new RECT ();
if (OS.TreeView_GetItemRect (handle, tvItem.hItem, rect, false)) {
OS.InvalidateRect (handle, rect, true);
}
}
}
}
updateScrollBar ();
break;
}
case OS.TVN_BEGINDRAG:
if (OS.GetKeyState (OS.VK_LBUTTON) >= 0) break;
//FALL THROUGH
case OS.TVN_BEGINRDRAG: {
dragStarted = true;
NMTREEVIEW treeView = new NMTREEVIEW ();
OS.MoveMemory (treeView, lParam, NMTREEVIEW.sizeof);
TVITEM tvItem = treeView.itemNew;
if (tvItem.hItem != 0 && (tvItem.state & OS.TVIS_SELECTED) == 0) {
hSelect = tvItem.hItem;
ignoreSelect = ignoreDeselect = true;
OS.SendMessage (handle, OS.TVM_SELECTITEM, OS.TVGN_CARET, tvItem.hItem);
ignoreSelect = ignoreDeselect = false;
hSelect = 0;
}
break;
}
}
return super.wmNotifyChild (hdr, wParam, lParam);
}
LRESULT wmNotifyHeader (NMHDR hdr, long wParam, long lParam) {
/*
* Feature in Windows. On NT, the automatically created
* header control is created as a UNICODE window, not an
* ANSI window despite the fact that the parent is created
* as an ANSI window. This means that it sends UNICODE
* notification messages to the parent window on NT for
* no good reason. The data and size in the NMHEADER and
* HDITEM structs is identical between the platforms so no
* different message is actually necessary. Despite this,
* Windows sends different messages. The fix is to look
* for both messages, despite the platform. This works
* because only one will be sent on either platform, never
* both.
*/
switch (hdr.code) {
case OS.HDN_BEGINTRACK:
case OS.HDN_DIVIDERDBLCLICK: {
NMHEADER phdn = new NMHEADER ();
OS.MoveMemory (phdn, lParam, NMHEADER.sizeof);
TreeColumn column = columns [phdn.iItem];
if (column != null && !column.getResizable ()) {
return LRESULT.ONE;
}
ignoreColumnMove = true;
if (hdr.code == OS.HDN_DIVIDERDBLCLICK) {
if (column != null) column.pack ();
}
break;
}
case OS.NM_CUSTOMDRAW: {
NMCUSTOMDRAW nmcd = new NMCUSTOMDRAW();
OS.MoveMemory(nmcd, lParam, NMCUSTOMDRAW.sizeof);
switch (nmcd.dwDrawStage) {
case OS.CDDS_PREPAINT: {
/* Drawing here will be deleted by further drawing steps, even with OS.CDRF_SKIPDEFAULT.
Changing the TextColor and returning OS.CDRF_NEWFONT has no effect. */
return new LRESULT (customHeaderDrawing() ? OS.CDRF_NOTIFYITEMDRAW | OS.CDRF_NOTIFYPOSTPAINT : OS.CDRF_DODEFAULT);
}
case OS.CDDS_ITEMPREPAINT: {
// draw background
RECT rect = new RECT();
OS.SetRect(rect, nmcd.left, nmcd.top, nmcd.right, nmcd.bottom);
int pixel = getHeaderBackgroundPixel();
if ((nmcd.uItemState & OS.CDIS_SELECTED) != 0) {
pixel = getDifferentColor(pixel);
} else if (columns[(int) nmcd.dwItemSpec] == sortColumn && sortDirection != SWT.NONE) {
pixel = getSlightlyDifferentColor(pixel);
}
long brush = OS.CreateSolidBrush(pixel);
OS.FillRect(nmcd.hdc, rect, brush);
OS.DeleteObject(brush);
return new LRESULT(OS.CDRF_SKIPDEFAULT); // if we got here, we will paint everything ourself
}
case OS.CDDS_POSTPAINT: {
// get the cursor position
POINT cursorPos = new POINT();
OS.GetCursorPos(cursorPos);
OS.MapWindowPoints(0, hwndHeader, cursorPos, 1);
// drawing all cells
int highlightedHeaderDividerX = -1;
int lastColumnRight = -1;
RECT [] rects = new RECT [columnCount];
for (int i=0; i lastColumnRight) {
lastColumnRight = rects[i].right;
}
if (columns[i] == sortColumn && sortDirection != SWT.NONE) {
// the display.getSortImage looks terrible after scaling up.
long pen = OS.CreatePen (OS.PS_SOLID, 1, getHeaderForegroundPixel());
long oldPen = OS.SelectObject (nmcd.hdc, pen);
int center = rects[i].left + (rects[i].right - rects[i].left) / 2;
/*
* Sort indicator size needs to scale as per the Native Windows OS DPI level
* when header is custom drawn. For more details refer bug 537097.
*/
int leg = DPIUtil.autoScaleUpUsingNativeDPI(3);
if (sortDirection == SWT.UP) {
OS.Polyline(nmcd.hdc, new int[] {center-leg, 1+leg, center+1, 0}, 2);
OS.Polyline(nmcd.hdc, new int[] {center+leg, 1+leg, center-1, 0}, 2);
} else if (sortDirection == SWT.DOWN) {
OS.Polyline(nmcd.hdc, new int[] {center-leg, 1, center+1, 1+leg+1}, 2);
OS.Polyline(nmcd.hdc, new int[] {center+leg, 1, center-1, 1+leg+1}, 2);
}
OS.SelectObject (nmcd.hdc, oldPen);
OS.DeleteObject (pen);
}
/* Windows 7 and 10 always draw a nearly invisible vertical line between the columns, even if lines are disabled.
This line uses no fixed color constant, but calculates it from the background color.
The method getSlightlyDifferentColor gives us a color, that is near enough to the windows algorithm. */
long pen = OS.CreatePen (OS.PS_SOLID, getGridLineWidthInPixels(), getSlightlyDifferentColor(getHeaderBackgroundPixel()));
long oldPen = OS.SelectObject (nmcd.hdc, pen);
OS.Polyline(nmcd.hdc, new int[] {rects[i].right-1, rects[i].top, rects[i].right-1, rects[i].bottom}, 2);
OS.SelectObject (nmcd.hdc, oldPen);
OS.DeleteObject (pen);
pen = OS.CreatePen (OS.PS_SOLID, getGridLineWidthInPixels(), OS.GetSysColor(OS.COLOR_3DFACE));
oldPen = OS.SelectObject (nmcd.hdc, pen);
/* To differentiate headers, always draw header column separator. */
OS.Polyline(nmcd.hdc, new int[] {rects[i].right-1, rects[i].top, rects[i].right-1, rects[i].bottom}, 2);
/* To differentiate header & content area, always draw the line separator between header & first row. */
if (i == 0) OS.Polyline(nmcd.hdc, new int[] {nmcd.left, nmcd.bottom-1, nmcd.right, nmcd.bottom-1}, 2);
OS.SelectObject (nmcd.hdc, oldPen);
OS.DeleteObject (pen);
if (headerItemDragging && highlightedHeaderDividerX == -1) {
int distanceToLeftBorder = cursorPos.x - rects[i].left;
int distanceToRightBorder = rects[i].right - cursorPos.x;
if (distanceToLeftBorder >= 0 && distanceToRightBorder >= 0) {
// the cursor is in the current rectangle
highlightedHeaderDividerX = distanceToLeftBorder <= distanceToRightBorder ? rects[i].left-1 : rects[i].right;
}
}
int x = rects[i].left + INSET + 2;
if (columns[i].image != null) {
GCData data = new GCData();
data.device = display;
GC gc = GC.win32_new (nmcd.hdc, data);
int y = Math.max (0, (nmcd.bottom - columns[i].image.getBoundsInPixels().height) / 2);
gc.drawImage (columns[i].image, DPIUtil.autoScaleDown(x), DPIUtil.autoScaleDown(y));
x += columns[i].image.getBoundsInPixels().width + 12;
gc.dispose ();
}
if (columns[i].text != null) {
int flags = OS.DT_NOPREFIX | OS.DT_SINGLELINE | OS.DT_VCENTER;
if ((columns[i].style & SWT.CENTER) != 0) flags |= OS.DT_CENTER;
if ((columns[i].style & SWT.RIGHT) != 0) flags |= OS.DT_RIGHT;
char [] buffer = columns[i].text.toCharArray ();
OS.SetBkMode(nmcd.hdc, OS.TRANSPARENT);
OS.SetTextColor(nmcd.hdc, getHeaderForegroundPixel());
RECT textRect = new RECT();
textRect.left = x;
textRect.top = rects[i].top;
textRect.right = rects[i].right;
textRect.bottom = rects[i].bottom;
OS.DrawText (nmcd.hdc, buffer, buffer.length, textRect, flags);
}
}
if (lastColumnRight < nmcd.right) {
// draw background of the 'no column' area
RECT rect = new RECT();
OS.SetRect(rect, lastColumnRight, nmcd.top, nmcd.right, nmcd.bottom-1);
long brush = OS.CreateSolidBrush(getHeaderBackgroundPixel());
OS.FillRect(nmcd.hdc, rect, brush);
OS.DeleteObject(brush);
}
// always draw the highlighted border at the end, to avoid overdrawing by other borders.
if (highlightedHeaderDividerX != -1) {
long pen = OS.CreatePen (OS.PS_SOLID, 4, OS.GetSysColor(OS.COLOR_HIGHLIGHT));
long oldPen = OS.SelectObject (nmcd.hdc, pen);
OS.Polyline(nmcd.hdc, new int[] {highlightedHeaderDividerX, nmcd.top, highlightedHeaderDividerX, nmcd.bottom}, 2);
OS.SelectObject (nmcd.hdc, oldPen);
OS.DeleteObject (pen);
}
return new LRESULT(OS.CDRF_DODEFAULT);
}
}
break;
}
case OS.NM_RELEASEDCAPTURE: {
if (!ignoreColumnMove) {
for (int i=0; i