blob: 89a866c92342a62407a33634a973047635267ced [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2000, 2007 IBM Corporation and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* IBM Corporation - initial API and implementation
*******************************************************************************/
package org.eclipse.swt.widgets;
import org.eclipse.swt.internal.*;
import org.eclipse.swt.internal.win32.*;
import org.eclipse.swt.*;
import org.eclipse.swt.graphics.*;
import org.eclipse.swt.events.*;
/**
* 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.
* <p>
* The item children that may be added to instances of this class
* must be of type <code>TreeItem</code>.
* </p><p>
* Style <code>VIRTUAL</code> is used to create a <code>Tree</code> whose
* <code>TreeItem</code>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 <code>TreeItem</code> population is
* expensive (for example, retrieving values from an external source).
* </p><p>
* Here is an example of using a <code>Tree</code> with style <code>VIRTUAL</code>:
* <code><pre>
* 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);
* }
* });
* </pre></code>
* </p><p>
* Note that although this class is a subclass of <code>Composite</code>,
* it does not normally make sense to add <code>Control</code> children to
* it, or set a layout on it, unless implementing something like a cell
* editor.
* </p><p>
* <dl>
* <dt><b>Styles:</b></dt>
* <dd>SINGLE, MULTI, CHECK, FULL_SELECTION, VIRTUAL</dd>
* <dt><b>Events:</b></dt>
* <dd>Selection, DefaultSelection, Collapse, Expand, SetData, MeasureItem, EraseItem, PaintItem</dd>
* </dl>
* </p><p>
* Note: Only one of the styles SINGLE and MULTI may be specified.
* </p><p>
* IMPORTANT: This class is <em>not</em> intended to be subclassed.
* </p>
*/
public class Tree extends Composite {
TreeItem [] items;
TreeColumn [] columns;
ImageList imageList, headerImageList;
TreeItem currentItem;
TreeColumn sortColumn;
int /*long*/ hwndParent, hwndHeader, hAnchor, hInsert, hSelect;
int lastID;
int /*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, printClient, painted, ignoreItemHeight;
boolean ignoreCustomDraw, ignoreDrawForeground, ignoreDrawBackground, ignoreDrawFocus;
boolean ignoreDrawSelection, ignoreDrawHot, ignoreFullSelection, explorerTheme;
int scrollWidth, selectionForeground;
int /*long*/ headerToolTipHandle, itemToolTipHandle;
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 boolean EXPLORER_THEME = true;
static final int /*long*/ TreeProc;
static final TCHAR TreeClass = new TCHAR (0, OS.WC_TREEVIEW, true);
static final int /*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.
* <p>
* The style value is either one of the style constants defined in
* class <code>SWT</code> which is applicable to instances of this
* class, or must be built by <em>bitwise OR</em>'ing together
* (that is, using the <code>int</code> "|" operator) two or more
* of those <code>SWT</code> style constants. The class description
* lists the style constants that are applicable to the class.
* Style bits are also inherited from superclasses.
* </p>
*
* @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 <ul>
* <li>ERROR_NULL_ARGUMENT - if the parent is null</li>
* </ul>
* @exception SWTException <ul>
* <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the parent</li>
* <li>ERROR_INVALID_SUBCLASS - if this class is not an allowed subclass</li>
* </ul>
*
* @see SWT#SINGLE
* @see SWT#MULTI
* @see SWT#CHECK
* @see Widget#checkSubclass
* @see Widget#getStyle
*/
public Tree (Composite parent, int style) {
super (parent, checkStyle (style));
}
static int checkStyle (int style) {
/*
* Feature in Windows. It is not possible to create
* a tree that scrolls and does not have scroll bars.
* The TVS_NOSCROLL style will remove the scroll bars
* but the tree will never scroll. Therefore, no matter
* what style bits are specified, set the H_SCROLL and
* V_SCROLL bits so that the SWT style will match the
* widget that Windows creates.
*/
style |= SWT.H_SCROLL | SWT.V_SCROLL;
return checkBits (style, SWT.SINGLE, SWT.MULTI, 0, 0, 0, 0);
}
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;
OS.SendMessage (handle, OS.TVM_SETSCROLLTIME, 0, 0);
int bits = OS.GetWindowLong (handle, OS.GWL_STYLE);
bits |= OS.TVS_NOTOOLTIPS;
if (eventType == SWT.MeasureItem) 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)/*64*/OS.SendMessage (handle, OS.TVM_GETCOUNT, 0, 0);
if (count != 0 && (bits & OS.TVS_NOHSCROLL) != 0) {
if (!OS.IsWinCE) OS.ShowScrollBar (handle, OS.SB_HORZ, false);
}
}
break;
}
}
}
TreeItem _getItem (int /*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)/*64*/tvItem.lParam);
}
return null;
}
TreeItem _getItem (int /*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);
}
void _setBackgroundPixel (int newPixel) {
int oldPixel = (int)/*64*/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) {
int bits2 = (int)/*64*/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 <code>SelectionListener</code>
* interface.
* <p>
* When <code>widgetSelected</code> is called, the item field of the event object is valid.
* If the receiver has the <code>SWT.CHECK</code> style and the check selection changes,
* the event object detail field contains the value <code>SWT.CHECK</code>.
* <code>widgetDefaultSelected</code> 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.
* </p>
*
* @param listener the listener which should be notified when the user changes the receiver's selection
*
* @exception IllegalArgumentException <ul>
* <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
* </ul>
* @exception SWTException <ul>
* <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
* <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
* </ul>
*
* @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 <code>TreeListener</code>
* interface.
*
* @param listener the listener which should be notified
*
* @exception IllegalArgumentException <ul>
* <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
* </ul>
* @exception SWTException <ul>
* <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
* <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
* </ul>
*
* @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);
}
int /*long*/ borderHandle () {
return hwndParent != 0 ? hwndParent : handle;
}
LRESULT CDDS_ITEMPOSTPAINT (NMTVCUSTOMDRAW nmcd, int /*long*/ wParam, int /*long*/ lParam) {
if (ignoreCustomDraw) return null;
if (nmcd.left == nmcd.right) return new LRESULT (OS.CDRF_DODEFAULT);
int /*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 count = 0;
int [] order = null;
RECT clientRect = new RECT ();
OS.GetClientRect (scrolledHandle (), clientRect);
if (hwndHeader != 0) {
OS.MapWindowPoints (hwndParent, handle, clientRect, 2);
count = (int)/*64*/OS.SendMessage (hwndHeader, OS.HDM_GETITEMCOUNT, 0, 0);
if (count != 0) {
order = new int [count];
OS.SendMessage (hwndHeader, OS.HDM_GETORDERARRAY, count, order);
}
}
int sortIndex = -1, clrSortBk = -1;
if (OS.COMCTL32_MAJOR >= 6 && 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<Math.max (1, count); i++) {
int index = order == null ? i : order [i], width = nmcd.right - nmcd.left;
if (count > 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 (count > 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 (count > 0 && hwndHeader != 0) {
int totalWidth = 0;
HDITEM hdItem = new HDITEM ();
hdItem.mask = OS.HDI_WIDTH;
for (int j=0; j<count; j++) {
OS.SendMessage (hwndHeader, OS.HDM_GETITEM, j, hdItem);
totalWidth += hdItem.cxy;
}
if (totalWidth > clientRect.right - clientRect.left) {
pRect.left = 0;
pRect.right = totalWidth;
} else {
pRect.left = clientRect.left;
pRect.right = clientRect.right;
}
}
draw = false;
int /*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);
}
} else {
if (explorerTheme && hooks (SWT.EraseItem)) {
if ((selected && !ignoreDrawSelection) || (hot && !ignoreDrawHot)) {
RECT pRect = item.getBounds (index, true, true, false, false, false, hDC);
RECT pClipRect = item.getBounds (index, true, true, false, false, true, hDC);
pRect.left -= EXPLORER_EXTRA;
pRect.right += EXPLORER_EXTRA;
pClipRect.left -= EXPLORER_EXTRA;
pClipRect.right += EXPLORER_EXTRA;
int /*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 (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 ();
if (size == null) size = getImageSize ();
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);
gc.setClipping (iconRect.left, iconRect.top, iconRect.right - iconRect.left, iconRect.bottom - iconRect.top);
gc.drawImage (image, 0, 0, bounds.width, bounds.height, iconRect.left, 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;
int /*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);
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 = Font.win32_new (display, hFont);
data.uiState = (int)/*64*/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)/*64*/OS.SendMessage (handle, OS.WM_QUERYUISTATE, 0, 0);
if ((uiState & OS.UISF_HIDEFOCUS) == 0) event.detail |= SWT.FOCUSED;
}
}
}
}
event.x = cellRect.left;
event.y = cellRect.top;
event.width = cellRect.right - cellRect.left;
event.height = 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 (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 () && OS.IsWindowEnabled (handle)) {
clrTextBk = OS.GetSysColor (OS.COLOR_HIGHLIGHT);
} else {
clrTextBk = OS.GetSysColor (OS.COLOR_3DFACE);
}
if (!ignoreFullSelection && index == count - 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 (count > 0 && hwndHeader != 0) {
int totalWidth = 0;
HDITEM hdItem = new HDITEM ();
hdItem.mask = OS.HDI_WIDTH;
for (int j=0; j<count; j++) {
OS.SendMessage (hwndHeader, OS.HDM_GETITEM, j, hdItem);
totalWidth += hdItem.cxy;
}
if (totalWidth > clientRect.right - clientRect.left) {
pRect.left = 0;
pRect.right = totalWidth;
} else {
pRect.left = clientRect.left;
pRect.right = clientRect.right;
}
if (index == count - 1) {
RECT selectionRect = new RECT ();
OS.SetRect (selectionRect, backgroundRect.left, backgroundRect.top, pRect.right, backgroundRect.bottom);
backgroundRect = selectionRect;
}
}
int /*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);
} else {
if (drawBackground) fillImageBackground (hDC, control, rect);
}
}
}
}
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 ();
if (size == null) size = getImageSize ();
if (!ignoreDrawForeground) {
//int y1 = rect.top + (index == 0 ? (getItemHeight () - size.y) / 2 : 0);
int y1 = rect.top;
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 (x1, rect.top, rect.right - x1, rect.bottom - rect.top);
gc.drawImage (image, 0, 0, bounds.width, bounds.height, x1, y1, size.x, size.y);
OS.SelectClipRgn (hDC, 0);
gc.dispose ();
}
OS.SetRect (rect, rect.left + 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;
}
TCHAR buffer = new TCHAR (getCodePage (), string, false);
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 = Font.win32_new (display, hFont);
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)/*64*/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)/*64*/OS.SendMessage (handle, OS.WM_QUERYUISTATE, 0, 0);
if ((uiState & OS.UISF_HIDEFOCUS) == 0) event.detail |= SWT.FOCUSED;
}
}
}
}
event.x = itemRect.left;
event.y = itemRect.top;
event.width = itemRect.right - itemRect.left;
event.height = 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 (cellRect.left, cellRect.top, cellWidth, cellHeight);
sendEvent (SWT.PaintItem, event);
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 (hwndHeader != 0) {
if (OS.SendMessage (hwndHeader, OS.HDM_GETITEMCOUNT, 0, 0) != 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 (!explorerTheme) {
if (handle == OS.GetFocus ()) {
int uiState = (int)/*64*/OS.SendMessage (handle, OS.WM_QUERYUISTATE, 0, 0);
if ((uiState & OS.UISF_HIDEFOCUS) == 0) {
int /*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, nmcd.left, nmcd.top, nmcd.right, nmcd.bottom);
if (count > 0 && hwndHeader != 0) {
int width = 0;
HDITEM hdItem = new HDITEM ();
hdItem.mask = OS.HDI_WIDTH;
for (int j=0; j<count; j++) {
OS.SendMessage (hwndHeader, OS.HDM_GETITEM, j, hdItem);
width += hdItem.cxy;
}
focusRect.left = 0;
RECT rect = new RECT ();
OS.GetClientRect (handle, rect);
focusRect.right = Math.max (width, rect.right - OS.GetSystemMetrics (OS.SM_CXVSCROLL));
}
OS.DrawFocusRect (hDC, focusRect);
} else {
int index = (int)/*64*/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, int /*long*/ wParam, int /*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;
int /*long*/ hDC = nmcd.hdc;
int index = hwndHeader != 0 ? (int)/*64*/OS.SendMessage (hwndHeader, OS.HDM_ORDERTOINDEX, 0, 0) : 0;
int /*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);
}
int count = 0;
RECT clipRect = null;
if (hwndHeader != 0) {
count = (int)/*64*/OS.SendMessage (hwndHeader, OS.HDM_GETITEMCOUNT, 0, 0);
if (count != 0) {
boolean clip = !printClient;
if (!OS.IsWinCE && OS.WIN32_VERSION >= OS.VERSION (6, 0)) {
clip = true;
}
if (clip) {
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.COMCTL32_MAJOR >= 6 && 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;
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
if (hooks (SWT.MeasureItem)) {
sendMeasureItemEvent (item, index, hDC);
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);
if (OS.IsWindowEnabled (handle) || findImageControl () != null) {
drawBackground (hDC, rect);
} else {
fillBackground (hDC, OS.GetBkColor (hDC), rect);
}
RECT cellRect = item.getBounds (index, true, true, true, true, true, hDC);
if (clrSortBk != -1) {
RECT fullRect = item.getBounds (index, true, true, true, true, true, hDC);
drawBackground (hDC, fullRect, clrSortBk);
}
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)/*64*/OS.SendMessage (handle, OS.WM_QUERYUISTATE, 0, 0);
if (hFont != -1) data.font = Font.win32_new (display, hFont);
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 (!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)/*64*/OS.SendMessage (handle, OS.WM_QUERYUISTATE, 0, 0);
if ((uiState & OS.UISF_HIDEFOCUS) == 0) event.detail |= SWT.FOCUSED;
}
}
}
event.x = cellRect.left;
event.y = cellRect.top;
event.width = cellRect.right - cellRect.left;
event.height = 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 (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 (count == 0) {
if ((style & SWT.FULL_SELECTION) != 0) {
fillBackground (hDC, clrTextBk, rect);
} else {
RECT textRect = item.getBounds (index, true, false, false, false, true, hDC);
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) {
/*
* 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 && count == 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);
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 (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 (count != 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.COMCTL32_MAJOR < 6 || !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 {
if (explorerTheme) {
if (!selected && !hot) 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 (count != 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) {
int /*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, int /*long*/ wParam, int /*long*/ lParam) {
if (ignoreCustomDraw) return null;
if (OS.IsWindowVisible (handle)) {
if (OS.COMCTL32_MAJOR >= 6 && 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.
*/
int /*long*/ hItem = 0;
if (!OS.IsWinCE && 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) {
int /*long*/ hDC = nmcd.hdc;
if (hwndHeader != 0) {
int x = 0;
RECT rect = new RECT ();
HDITEM hdItem = new HDITEM ();
hdItem.mask = OS.HDI_WIDTH;
int count = (int)/*64*/OS.SendMessage (hwndHeader, OS.HDM_GETITEMCOUNT, 0, 0);
for (int i=0; i<count; i++) {
int index = (int)/*64*/OS.SendMessage (hwndHeader, OS.HDM_ORDERTOINDEX, i, 0);
OS.SendMessage (hwndHeader, OS.HDM_GETITEM, index, hdItem);
OS.SetRect (rect, x, nmcd.top, x + hdItem.cxy, nmcd.bottom);
OS.DrawEdge (hDC, rect, OS.BDR_SUNKENINNER, OS.BF_RIGHT);
x += hdItem.cxy;
}
}
int height = 0;
RECT rect = new RECT ();
/*
* 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.
*/
int /*long*/ hItem = 0;
if (!OS.IsWinCE && OS.WIN32_VERSION >= 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)/*64*/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);
}
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, int /*long*/ wParam, int /*long*/ lParam) {
if (explorerTheme) {
if ((OS.IsWindowEnabled (handle) && hooks (SWT.EraseItem)) || 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);
}
int /*long*/ callWindowProc (int /*long*/ hwnd, int msg, int /*long*/ wParam, int /*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;
int /*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;
}
}
int /*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 && drawCount == 0 && 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;
}
}
int /*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;
}
void checkBuffered () {
super.checkBuffered ();
if ((style & SWT.VIRTUAL) != 0) {
style |= SWT.DOUBLE_BUFFERED;
OS.SendMessage (handle, OS.TVM_SETSCROLLTIME, 0, 0);
}
if (EXPLORER_THEME) {
if (!OS.IsWinCE && OS.WIN32_VERSION >= OS.VERSION (6, 0) && OS.IsAppThemed ()) {
int exStyle = (int)/*64*/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;
sendEvent (SWT.SetData, event);
//widget could be disposed at this point
currentItem = oldItem;
if (isDisposed () || item.isDisposed ()) return false;
if (redraw) item.redraw ();
}
return true;
}
boolean checkHandle (int /*long*/ hwnd) {
return hwnd == handle || (hwndParent != 0 && hwnd == hwndParent) || (hwndHeader != 0 && hwnd == hwndHeader);
}
boolean checkScroll (int /*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 (drawCount == 0) return false;
int /*long*/ hRoot = OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_ROOT, 0);
int /*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;
}
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 <code>SWT.VIRTUAL</code> style,
* these attributes are requested again as needed.
*
* @param index the index of the item to clear
* @param all <code>true</code> if all child items of the indexed item should be
* cleared recursively, and <code>false</code> otherwise
*
* @exception IllegalArgumentException <ul>
* <li>ERROR_INVALID_RANGE - if the index is not between 0 and the number of elements in the list minus 1 (inclusive)</li>
* </ul>
* @exception SWTException <ul>
* <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
* <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
* </ul>
*
* @see SWT#VIRTUAL
* @see SWT#SetData
*
* @since 3.2
*/
public void clear (int index, boolean all) {
checkWidget ();
int /*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 (int /*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)/*64*/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 <code>SWT.VIRTUAL</code> style, these
* attributes are requested again as needed.
*
* @param all <code>true</code> if all child items should be cleared
* recursively, and <code>false</code> otherwise
*
* @exception SWTException <ul>
* <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
* <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
* </ul>
*
* @see SWT#VIRTUAL
* @see SWT#SetData
*
* @since 3.2
*/
public void clearAll (boolean all) {
checkWidget ();
int /*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<items.length; i++) {
TreeItem item = items [i];
if (item != null && item != currentItem) {
item.clear ();
redraw = true;
}
}
if (redraw) OS.InvalidateRect (handle, null, true);
} else {
TVITEM tvItem = new TVITEM ();
tvItem.mask = OS.TVIF_HANDLE | OS.TVIF_PARAM;
clearAll (hItem, tvItem, all);
}
}
void clearAll (int /*long*/ hItem, TVITEM tvItem, boolean all) {
while (hItem != 0) {
clear (hItem, tvItem);
if (all) {
int /*long*/ hFirstItem = OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_CHILD, hItem);
clearAll (hFirstItem, tvItem, all);
}
hItem = OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_NEXT, hItem);
}
}
int /*long*/ CompareFunc (int /*long*/ lParam1, int /*long*/ lParam2, int /*long*/ lParamSort) {
TreeItem item1 = items [(int)/*64*/lParam1], item2 = items [(int)/*64*/lParam2];
String text1 = item1.getText ((int)/*64*/lParamSort), text2 = item2.getText ((int)/*64*/lParamSort);
return sortDirection == SWT.UP ? text1.compareTo (text2) : text2.compareTo (text1);
}
public Point computeSize (int wHint, int hHint, boolean changed) {
checkWidget ();
int width = 0, height = 0;
if (hwndHeader != 0) {
HDITEM hdItem = new HDITEM ();
hdItem.mask = OS.HDI_WIDTH;
int count = (int)/*64*/OS.SendMessage (hwndHeader, OS.HDM_GETITEMCOUNT, 0, 0);
for (int i=0; i<count; i++) {
OS.SendMessage (hwndHeader, OS.HDM_GETITEM, i, hdItem);
width += hdItem.cxy;
}
RECT rect = new RECT ();
OS.GetWindowRect (hwndHeader, rect);
height += rect.bottom - rect.top;
}
RECT rect = new RECT ();
int /*long*/ hItem = OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_ROOT, 0);
while (hItem != 0) {
if ((style & SWT.VIRTUAL) == 0 && !painted) {
TVITEM tvItem = new TVITEM ();
tvItem.mask = OS.TVIF_HANDLE | OS.TVIF_TEXT;
tvItem.hItem = hItem;
tvItem.pszText = OS.LPSTR_TEXTCALLBACK;
ignoreCustomDraw = true;
OS.SendMessage (handle, OS.TVM_SETITEM, 0, tvItem);
ignoreCustomDraw = false;
}
if (OS.TreeView_GetItemRect (handle, hItem, rect, true)) {
width = Math.max (width, rect.right);
height += rect.bottom - rect.top;
}
hItem = OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_NEXTVISIBLE, hItem);
}
if (width == 0) width = DEFAULT_WIDTH;
if (height == 0) height = DEFAULT_HEIGHT;
if (wHint != SWT.DEFAULT) width = wHint;
if (hHint != SWT.DEFAULT) height = hHint;
int border = getBorderWidth ();
width += border * 2;
height += border * 2;
if ((style & SWT.V_SCROLL) != 0) {
width += OS.GetSystemMetrics (OS.SM_CXVSCROLL);
}
if ((style & SWT.H_SCROLL) != 0) {
height += OS.GetSystemMetrics (OS.SM_CYHSCROLL);
}
return new Point (width, height);
}
void createHandle () {
super.createHandle ();
state &= ~(CANVAS | THEME_BACKGROUND);
/* Use the Explorer theme */
if (EXPLORER_THEME) {
if (!OS.IsWinCE && OS.WIN32_VERSION >= OS.VERSION (6, 0) && OS.IsAppThemed ()) {
explorerTheme = true;
OS.SetWindowTheme (handle, Display.EXPLORER, null);
int bits = OS.TVS_EX_DOUBLEBUFFER | OS.TVS_EX_FADEINOUTEXPANDOS | OS.TVS_EX_RICHTOOLTIP;
if ((style & SWT.FULL_SELECTION) == 0) bits |= OS.TVS_EX_AUTOHSCROLL;
OS.SendMessage (handle, OS.TVM_SETEXTENDEDSTYLE, 0, bits);
/*
* 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.
*/
setForegroundPixel (-1);
}
}
/*
* Feature in Windows. In version 5.8 of COMCTL32.DLL,
* if the font is changed for an item, the bounds for the
* item are not updated, causing the text to be clipped.
* The fix is to detect the version of COMCTL32.DLL, and
* if it is one of the versions with the problem, then
* use version 5.00 of the control (a version that does
* not have the problem). This is the recommended work
* around from the MSDN.
*/
if (!OS.IsWinCE) {
if (OS.COMCTL32_MAJOR < 6) {
OS.SendMessage (handle, OS.CCM_SETVERSION, 5, 0);
}
}
/* Set the checkbox image list */
if ((style & SWT.CHECK) != 0) setCheckboxImageList ();
/*
* Feature in Windows. When the control is created,
* it does not use the default system font. A new HFONT
* is created and destroyed when the control is destroyed.
* This means that a program that queries the font from
* this control, uses the font in another control and then
* destroys this control will have the font unexpectedly
* destroyed in the other control. The fix is to assign
* the font ourselves each time the control is created.
* The control will not destroy a font that it did not
* create.
*/
int /*long*/ hFont = OS.GetStockObject (OS.SYSTEM_FONT);
OS.SendMessage (handle, OS.WM_SETFONT, hFont, 0);
}
void createHeaderToolTips () {
if (OS.IsWinCE) return;
if (headerToolTipHandle != 0) return;
headerToolTipHandle = OS.CreateWindowEx (
0,
new TCHAR (0, OS.TOOLTIPS_CLASS, true),
null,
OS.TTS_NOPREFIX,
OS.CW_USEDEFAULT, 0, OS.CW_USEDEFAULT, 0,
handle,
0,
OS.GetModuleHandle (null),
null);
if (headerToolTipHandle == 0) error (SWT.ERROR_NO_HANDLES);
/*
* Feature in Windows. Despite the fact that the
* tool tip text contains \r\n, the tooltip will
* not honour the new line unless TTM_SETMAXTIPWIDTH
* is set. The fix is to set TTM_SETMAXTIPWIDTH to
* a large value.
*/
OS.SendMessage (headerToolTipHandle, OS.TTM_SETMAXTIPWIDTH, 0, 0x7FFF);
}
void createItem (TreeColumn column, int index) {
if (hwndHeader == 0) createParent ();
int columnCount = (int)/*64*/OS.SendMessage (hwndHeader, OS.HDM_GETITEMCOUNT, 0, 0);
if (!(0 <= index && index <= columnCount)) error (SWT.ERROR_INVALID_RANGE);
if (columnCount == columns.length) {
TreeColumn [] newColumns = new TreeColumn [columns.length + 4];
System.arraycopy (columns, 0, newColumns, 0, columns.length);
columns = newColumns;
}
for (int i=0; i<items.length; i++) {
TreeItem item = items [i];
if (item != null) {
String [] strings = item.strings;
if (strings != null) {
String [] temp = new String [columnCount + 1];
System.arraycopy (strings, 0, temp, 0, index);
System.arraycopy (strings, index, temp, index + 1, columnCount - index);
item.strings = temp;
}
Image [] images = item.images;
if (images != null) {
Image [] temp = new Image [columnCount + 1];
System.arraycopy (images, 0, temp, 0, index);
System.arraycopy (images, index, temp, index + 1, columnCount - index);
item.images = temp;
}
if (index == 0) {
if (columnCount != 0) {
if (strings == null) {
item.strings = new String [columnCount + 1];
item.strings [1] = item.text;
}
item.text = "";
if (images == null) {
item.images = new Image [columnCount + 1];
item.images [1] = item.image;
}
item.image = null;
}
}
if (item.cellBackground != null) {
int [] cellBackground = item.cellBackground;
int [] temp = new int [columnCount + 1];
System.arraycopy (cellBackground, 0, temp, 0, index);
System.arraycopy (cellBackground, index, temp, index + 1, columnCount - index);
temp [index] = -1;
item.cellBackground = temp;
}
if (item.cellForeground != null) {
int [] cellForeground = item.cellForeground;
int [] temp = new int [columnCount + 1];
System.arraycopy (cellForeground, 0, temp, 0, index);
System.arraycopy (cellForeground, index, temp, index + 1, columnCount - index);
temp [index] = -1;
item.cellForeground = temp;
}
if (item.cellFont != null) {
Font [] cellFont = item.cellFont;
Font [] temp = new Font [columnCount + 1];
System.arraycopy (cellFont, 0, temp, 0, index);
System.arraycopy (cellFont, index, temp, index + 1, columnCount- index);
item.cellFont = temp;
}
}
}
System.arraycopy (columns, index, columns, index + 1, columnCount - index);
columns [index] = column;
/*
* Bug in Windows. For some reason, when HDM_INSERTITEM
* is used to insert an item into a header without text,
* if is not possible to set the text at a later time.
* The fix is to insert the item with an empty string.
*/
int /*long*/ hHeap = OS.GetProcessHeap ();
int /*long*/ pszText = OS.HeapAlloc (hHeap, OS.HEAP_ZERO_MEMORY, TCHAR.sizeof);
HDITEM hdItem = new HDITEM ();
hdItem.mask = OS.HDI_TEXT | OS.HDI_FORMAT;
hdItem.pszText = pszText;
if ((column.style & SWT.LEFT) == SWT.LEFT) hdItem.fmt = OS.HDF_LEFT;
if ((column.style & SWT.CENTER) == SWT.CENTER) hdItem.fmt = OS.HDF_CENTER;
if ((column.style & SWT.RIGHT) == SWT.RIGHT) hdItem.fmt = OS.HDF_RIGHT;
OS.SendMessage (hwndHeader, OS.HDM_INSERTITEM, index, hdItem);
if (pszText != 0) OS.HeapFree (hHeap, 0, pszText);
/* When the first column is created, hide the horizontal scroll bar */
if (columnCount == 0) {
scrollWidth = 0;
int bits = OS.GetWindowLong (handle, OS.GWL_STYLE);
bits |= OS.TVS_NOHSCROLL;
OS.SetWindowLong (handle, OS.GWL_STYLE, bits);
/*
* 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 explicitly.
*/
int count = (int)/*64*/OS.SendMessage (handle, OS.TVM_GETCOUNT, 0, 0);
if (count != 0) {
if (!OS.IsWinCE) OS.ShowScrollBar (handle, OS.SB_HORZ, false);
}
if (itemToolTipHandle != 0) {
OS.SendMessage (itemToolTipHandle, OS.TTM_SETDELAYTIME, OS.TTDT_AUTOMATIC, -1);
}
}
setScrollWidth ();
updateImageList ();
updateScrollBar ();
/* Redraw to hide the items when the first column is created */
if (columnCount == 0 && OS.SendMessage (handle, OS.TVM_GETCOUNT, 0, 0) != 0) {
OS.InvalidateRect (handle, null, true);
}
/* Add the tool tip item for the header */
if (headerToolTipHandle != 0) {
RECT rect = new RECT ();
if (OS.SendMessage (hwndHeader, OS.HDM_GETITEMRECT, index, rect) != 0) {
TOOLINFO lpti = new TOOLINFO ();
lpti.cbSize = TOOLINFO.sizeof;
lpti.uFlags = OS.TTF_SUBCLASS;
lpti.hwnd = hwndHeader;
lpti.uId = column.id = display.nextToolTipId++;
lpti.left = rect.left;
lpti.top = rect.top;
lpti.right = rect.right;
lpti.bottom = rect.bottom;
lpti.lpszText = OS.LPSTR_TEXTCALLBACK;
OS.SendMessage (headerToolTipHandle, OS.TTM_ADDTOOL, 0, lpti);
}
}
}
void createItem (TreeItem item, int /*long*/ hParent, int /*long*/ hInsertAfter, int /*long*/ hItem) {
int id = -1;
if (item != null) {
id = lastID < items.length ? lastID : 0;
while (id < items.length && items [id] != null) id++;
if (id == items.length) {
/*
* Grow the array faster when redraw is off or the
* table is not visible. When the table is painted,
* the items array is resized to be smaller to reduce
* memory usage.
*/
int length = 0;
if (drawCount == 0 && OS.IsWindowVisible (handle)) {
length = items.length + 4;
} else {
shrink = true;
length = Math.max (4, items.length * 3 / 2);
}
TreeItem [] newItems = new TreeItem [length];
System.arraycopy (items, 0, newItems, 0, items.length);
items = newItems;
}
lastID = id + 1;
}
int /*long*/ hNewItem = 0;
int /*long*/ hFirstItem = OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_CHILD, hParent);
boolean fixParent = hFirstItem == 0;
if (hItem == 0) {
TVINSERTSTRUCT tvInsert = new TVINSERTSTRUCT ();
tvInsert.hParent = hParent;
tvInsert.hInsertAfter = hInsertAfter;
tvInsert.lParam = id;
tvInsert.pszText = OS.LPSTR_TEXTCALLBACK;
tvInsert.iImage = tvInsert.iSelectedImage = OS.I_IMAGECALLBACK;
tvInsert.mask = OS.TVIF_TEXT | OS.TVIF_IMAGE | OS.TVIF_SELECTEDIMAGE | OS.TVIF_HANDLE | OS.TVIF_PARAM;
if ((style & SWT.CHECK) != 0) {
tvInsert.mask = tvInsert.mask | OS.TVIF_STATE;
tvInsert.state = 1 << 12;
tvInsert.stateMask = OS.TVIS_STATEIMAGEMASK;
}
ignoreCustomDraw = true;
hNewItem = OS.SendMessage (handle, OS.TVM_INSERTITEM, 0, tvInsert);
ignoreCustomDraw = false;
if (hNewItem == 0) error (SWT.ERROR_ITEM_NOT_ADDED);
/*
* This code is intentionally commented.
*/
// if (hParent != 0) {
// int bits = OS.GetWindowLong (handle, OS.GWL_STYLE);
// bits |= OS.TVS_LINESATROOT;
// OS.SetWindowLong (handle, OS.GWL_STYLE, bits);
// }
} else {
TVITEM tvItem = new TVITEM ();
tvItem.mask = OS.TVIF_HANDLE | OS.TVIF_PARAM;
tvItem.hItem = hNewItem = hItem;
tvItem.lParam = id;
OS.SendMessage (handle, OS.TVM_SETITEM, 0, tvItem);
}
if (item != null) {
item.handle = hNewItem;
items [id] = item;
}
if (hFirstItem == 0) {
if (hInsertAfter == OS.TVI_FIRST || hInsertAfter == OS.TVI_LAST) {
hFirstIndexOf = hLastIndexOf = hFirstItem = hNewItem;
itemCount = lastIndexOf = 0;
}
}
if (hFirstItem == hFirstIndexOf && itemCount != -1) itemCount++;
if (hItem == 0) {
/*
* Bug in Windows. When a child item is added to a parent item
* that has no children outside of WM_NOTIFY with control code
* TVN_ITEMEXPANDED, the tree widget does not redraw the +/-
* indicator. The fix is to detect the case when the first
* child is added to a visible parent item and redraw the parent.
*/
if (fixParent) {
if (drawCount == 0 && OS.IsWindowVisible (handle)) {
RECT rect = new RECT ();
if (OS.TreeView_GetItemRect (handle, hParent, rect, false)) {
OS.InvalidateRect (handle, rect, true);
}
}
}
/*
* Bug in Windows. When a new item is added while Windows
* is requesting data a tree item using TVN_GETDISPINFO,
* outstanding damage for items that are below the new item
* is not scrolled. The fix is to explicitly damage the
* new area.
*/
if ((style & SWT.VIRTUAL) != 0) {
if (currentItem != null) {
RECT rect = new RECT ();
if (OS.TreeView_GetItemRect (handle, hNewItem, rect, false)) {
RECT damageRect = new RECT ();
boolean damaged = OS.GetUpdateRect (handle, damageRect, true);
if (damaged && damageRect.top < rect.bottom) {
if (OS.IsWinCE) {
OS.OffsetRect (damageRect, 0, rect.bottom - rect.top);
OS.InvalidateRect (handle, damageRect, true);
} else {
int /*long*/ rgn = OS.CreateRectRgn (0, 0, 0, 0);
int result = OS.GetUpdateRgn (handle, rgn, true);
if (result != OS.NULLREGION) {
OS.OffsetRgn (rgn, 0, rect.bottom - rect.top);
OS.InvalidateRgn (handle, rgn, true);
}
OS.DeleteObject (rgn);
}
}
}
}
}
updateScrollBar ();
}
}
void createItemToolTips () {
if (OS.IsWinCE) return;
if (itemToolTipHandle != 0) return;
int bits = OS.GetWindowLong (handle, OS.GWL_STYLE);
bits |= OS.TVS_NOTOOLTIPS;
OS.SetWindowLong (handle, OS.GWL_STYLE, bits);
itemToolTipHandle = OS.CreateWindowEx (
OS.WS_EX_TRANSPARENT,
new TCHAR (0, OS.TOOLTIPS_CLASS, true),
null,
OS.TTS_NOPREFIX,
OS.CW_USEDEFAULT, 0, OS.CW_USEDEFAULT, 0,
handle,
0,
OS.GetModuleHandle (null),
null);
if (itemToolTipHandle == 0) error (SWT.ERROR_NO_HANDLES);
OS.SendMessage (itemToolTipHandle, OS.TTM_SETDELAYTIME, OS.TTDT_INITIAL, 0);
TOOLINFO lpti = new TOOLINFO ();
lpti.cbSize = TOOLINFO.sizeof;
lpti.hwnd = handle;
lpti.uId = handle;
lpti.uFlags = OS.TTF_SUBCLASS | OS.TTF_TRANSPARENT;
lpti.lpszText = OS.LPSTR_TEXTCALLBACK;
OS.SendMessage (itemToolTipHandle, OS.TTM_ADDTOOL, 0, lpti);
}
void createParent () {
forceResize ();
RECT rect = new RECT ();
OS.GetWindowRect (handle, rect);
OS.MapWindowPoints (0, parent.handle, rect, 2);
int oldStyle = OS.GetWindowLong (handle, OS.GWL_STYLE);
int newStyle = super.widgetStyle () & ~OS.WS_VISIBLE;
if ((oldStyle & OS.WS_DISABLED) != 0) newStyle |= OS.WS_DISABLED;
// if ((oldStyle & OS.WS_VISIBLE) != 0) newStyle |= OS.WS_VISIBLE;
hwndParent = OS.CreateWindowEx (
super.widgetExtStyle (),
super.windowClass (),
null,
newStyle,
rect.left,
rect.top,
rect.right - rect.left,
rect.bottom - rect.top,
parent.handle,
0,
OS.GetModuleHandle (null),
null);
if (hwndParent == 0) error (SWT.ERROR_NO_HANDLES);
OS.SetWindowLongPtr (hwndParent, OS.GWLP_ID, hwndParent);
int bits = 0;
if (OS.WIN32_VERSION >= OS.VERSION (4, 10)) {
bits |= OS.WS_EX_NOINHERITLAYOUT;
if ((style & SWT.RIGHT_TO_LEFT) != 0) bits |= OS.WS_EX_LAYOUTRTL;
}
hwndHeader = OS.CreateWindowEx (
bits,
HeaderClass,
null,
OS.HDS_BUTTONS | OS.HDS_FULLDRAG | OS.HDS_DRAGDROP | OS.HDS_HIDDEN | OS.WS_CHILD | OS.WS_CLIPSIBLINGS,
0, 0, 0, 0,
hwndParent,
0,
OS.GetModuleHandle (null),
null);
if (hwndHeader == 0) error (SWT.ERROR_NO_HANDLES);
OS.SetWindowLongPtr (hwndHeader, OS.GWLP_ID, hwndHeader);
if (OS.IsDBLocale) {
int /*long*/ hIMC = OS.ImmGetContext (handle);
OS.ImmAssociateContext (hwndParent, hIMC);
OS.ImmAssociateContext (hwndHeader, hIMC);
OS.ImmReleaseContext (handle, hIMC);
}
//This code is intentionally commented
// if (!OS.IsPPC) {
// if ((style & SWT.BORDER) != 0) {
// int oldExStyle = OS.GetWindowLong (handle, OS.GWL_EXSTYLE);
// oldExStyle &= ~OS.WS_EX_CLIENTEDGE;
// OS.SetWindowLong (handle, OS.GWL_EXSTYLE, oldExStyle);
// }
// }
int /*long*/ hFont = OS.SendMessage (handle, OS.WM_GETFONT, 0, 0);
if (hFont != 0) OS.SendMessage (hwndHeader, OS.WM_SETFONT, hFont, 0);
int /*long*/ hwndInsertAfter = OS.GetWindow (handle, OS.GW_HWNDPREV);
int flags = OS.SWP_NOSIZE | OS.SWP_NOMOVE | OS.SWP_NOACTIVATE;
SetWindowPos (hwndParent, hwndInsertAfter, 0, 0, 0, 0, flags);
SCROLLINFO info = new SCROLLINFO ();
info.cbSize = SCROLLINFO.sizeof;
info.fMask = OS.SIF_RANGE | OS.SIF_PAGE;
OS.GetScrollInfo (hwndParent, OS.SB_HORZ, info);
info.nPage = info.nMax + 1;
OS.SetScrollInfo (hwndParent, OS.SB_HORZ, info, true);
OS.GetScrollInfo (hwndParent, OS.SB_VERT, info);
info.nPage = info.nMax + 1;
OS.SetScrollInfo (hwndParent, OS.SB_VERT, info, true);
customDraw = true;
deregister ();
if ((oldStyle & OS.WS_VISIBLE) != 0) {
OS.ShowWindow (hwndParent, OS.SW_SHOW);
}
int /*long*/ hwndFocus = OS.GetFocus ();
if (hwndFocus == handle) OS.SetFocus (hwndParent);
OS.SetParent (handle, hwndParent);
if (hwndFocus == handle) OS.SetFocus (handle);
register ();
subclass ();
createItemToolTips ();
}
void createWidget () {
super.createWidget ();
items = new TreeItem [4];
columns = new TreeColumn [4];
itemCount = -1;
}
int defaultBackground () {
return OS.GetSysColor (OS.COLOR_WINDOW);
}
void deregister () {
super.deregister ();
if (hwndParent != 0) display.removeControl (hwndParent);
if (hwndHeader != 0) display.removeControl (hwndHeader);
}
void deselect (int /*long*/ hItem, TVITEM tvItem, int /*long*/ hIgnoreItem) {
while (hItem != 0) {
if (hItem != hIgnoreItem) {
tvItem.hItem = hItem;
OS.SendMessage (handle, OS.TVM_SETITEM, 0, tvItem);
}
int /*long*/ hFirstItem = OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_CHILD, hItem);
deselect (hFirstItem, tvItem, hIgnoreItem);
hItem = OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_NEXT, hItem);
}
}
/**
* Deselects an item in the receiver. If the item was already
* deselected, it remains deselected.
*
* @param item the item to be deselected
*
* @exception IllegalArgumentException <ul>
* <li>ERROR_NULL_ARGUMENT - if the item is null</li>
* <li>ERROR_INVALID_ARGUMENT - if the item has been disposed</li>
* </ul>
* @exception SWTException <ul>
* <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
* <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
* </ul>
*
* @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 <ul>
* <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
* <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
* </ul>
*/
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) {
int /*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 {
int /*long*/ oldProc = OS.GetWindowLongPtr (handle, OS.GWLP_WNDPROC);
OS.SetWindowLongPtr (handle, OS.GWLP_WNDPROC, TreeProc);
if ((style & SWT.VIRTUAL) != 0) {
int /*long*/ hItem = OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_ROOT, 0);
deselect (hItem, tvItem, 0);
} else {
for (int i=0; i<items.length; i++) {
TreeItem item = items [i];
if (item != null) {
tvItem.hItem = item.handle;
OS.SendMessage (handle, OS.TVM_SETITEM, 0, tvItem);
}
}
}
OS.SetWindowLongPtr (handle, OS.GWLP_WNDPROC, oldProc);
}
}
void destroyItem (TreeColumn column) {
if (hwndHeader == 0) error (SWT.ERROR_ITEM_NOT_REMOVED);
int columnCount = (int)/*64*/OS.SendMessage (hwndHeader, OS.HDM_GETITEMCOUNT, 0, 0);
int index = 0;
while (index < columnCount) {
if (columns [index] == column) break;
index++;
}
int [] oldOrder = new int [columnCount];
OS.SendMessage (hwndHeader, OS.HDM_GETORDERARRAY, columnCount, oldOrder);
int orderIndex = 0;
while (orderIndex < columnCount) {
if (oldOrder [orderIndex] == index) break;
orderIndex++;
}
RECT headerRect = new RECT ();
OS.SendMessage (hwndHeader, OS.HDM_GETITEMRECT, index, headerRect);
if (OS.SendMessage (hwndHeader, OS.HDM_DELETEITEM, index, 0) == 0) {
error (SWT.ERROR_ITEM_NOT_REMOVED);
}
System.arraycopy (columns, index + 1, columns, index, --columnCount - index);
columns [columnCount] = null;
for (int i=0; i<items.length; i++) {
TreeItem item = items [i];
if (item != null) {
if (columnCount == 0) {
item.strings = null;
item.images = null;
item.cellBackground = null;
item.cellForeground = null;
item.cellFont = null;
} else {
if (item.strings != null) {
String [] strings = item.strings;
if (index == 0) {
item.text = strings [1] != null ? strings [1] : "";
}
String [] temp = new String [columnCount];
System.arraycopy (strings, 0, temp, 0, index);
System.arraycopy (strings, index + 1, temp, index, columnCount - index);
item.strings = temp;
} else {
if (index == 0) item.text = "";
}
if (item.images != null) {
Image [] images = item.images;
if (index == 0) item.image = images [1];
Image [] temp = new Image [columnCount];
System.arraycopy (images, 0, temp, 0, index);
System.arraycopy (images, index + 1, temp, index, columnCount - index);
item.images = temp;
} else {
if (index == 0) item.image = null;
}
if (item.cellBackground != null) {
int [] cellBackground = item.cellBackground;
int [] temp = new int [columnCount];
System.arraycopy (cellBackground, 0, temp, 0, index);
System.arraycopy (cellBackground, index + 1, temp, index, columnCount - index);
item.cellBackground = temp;
}
if (item.cellForeground != null) {
int [] cellForeground = item.cellForeground;
int [] temp = new int [columnCount];
System.arraycopy (cellForeground, 0, temp, 0, index);
System.arraycopy (cellForeground, index + 1, temp, index, columnCount - index);
item.cellForeground = temp;
}
if (item.cellFont != null) {
Font [] cellFont = item.cellFont;
Font [] temp = new Font [columnCount];
System.arraycopy (cellFont, 0, temp, 0, index);
System.arraycopy (cellFont, index + 1, temp, index, columnCount - index);
item.cellFont = temp;
}
}
}
}
/*
* When the last column is deleted, show the horizontal
* scroll bar. Otherwise, left align the first column
* and redraw the columns to the right.
*/
if (columnCount == 0) {
scrollWidth = 0;
if (!hooks (SWT.MeasureItem)) {
int bits = OS.GetWindowLong (handle, OS.GWL_STYLE);
bits &= ~OS.TVS_NOHSCROLL;
OS.SetWindowLong (handle, OS.GWL_STYLE, bits);
OS.InvalidateRect (handle, null, true);
}
if (itemToolTipHandle != 0) {
OS.SendMessage (itemToolTipHandle, OS.TTM_SETDELAYTIME, OS.TTDT_INITIAL, 0);
}
} else {
if (index == 0) {
columns [0].style &= ~(SWT.LEFT | SWT.RIGHT | SWT.CENTER);
columns [0].style |= SWT.LEFT;
HDITEM hdItem = new HDITEM ();
hdItem.mask = OS.HDI_FORMAT | OS.HDI_IMAGE;
OS.SendMessage (hwndHeader, OS.HDM_GETITEM, index, hdItem);
hdItem.fmt &= ~OS.HDF_JUSTIFYMASK;
hdItem.fmt |= OS.HDF_LEFT;
OS.SendMessage (hwndHeader, OS.HDM_SETITEM, index, hdItem);
}
RECT rect = new RECT ();
OS.GetClientRect (handle, rect);
rect.left = headerRect.left;
OS.InvalidateRect (handle, rect, true);
}
setScrollWidth ();
updateImageList ();
updateScrollBar ();
if (columnCount != 0) {
int [] newOrder = new int [columnCount];
OS.SendMessage (hwndHeader, OS.HDM_GETORDERARRAY, columnCount, newOrder);
TreeColumn [] newColumns = new TreeColumn [columnCount - orderIndex];
for (int i=orderIndex; i<newOrder.length; i++) {
newColumns [i - orderIndex] = columns [newOrder [i]];
newColumns [i - orderIndex].updateToolTip (newOrder [i]);
}
for (int i=0; i<newColumns.length; i++) {
if (!newColumns [i].isDisposed ()) {
newColumns [i].sendEvent (SWT.Move);
}
}
}
/* Remove the tool tip item for the header */
if (headerToolTipHandle != 0) {
TOOLINFO lpti = new TOOLINFO ();
lpti.cbSize = TOOLINFO.sizeof;
lpti.uId = column.id;
lpti.hwnd = hwndHeader;
OS.SendMessage (headerToolTipHandle, OS.TTM_DELTOOL, 0, lpti);
}
}
void destroyItem (TreeItem item, int /*long*/ hItem) {
hFirstIndexOf = hLastIndexOf = 0;
itemCount = -1;
/*
* Feature in Windows. When an item is removed that is not
* visible in the tree because it belongs to a collapsed branch,
* Windows redraws the tree causing a flash for each item that
* is removed. The fix is to detect whether the item is visible,
* force the widget to be fully painted, turn off redraw, remove
* the item and validate the damage caused by the removing of
* the item.
*
* NOTE: This fix is not necessary when double buffering and
* can cause problems for virtual trees due to the call to
* UpdateWindow() that flushes outstanding WM_PAINT events,
* allowing application code to add or remove items during
* this remove operation.
*/
int /*long*/ hParent = 0;
boolean fixRedraw = false;
if ((style & SWT.DOUBLE_BUFFERED) == 0) {
if (drawCount == 0 && OS.IsWindowVisible (handle)) {
RECT rect = new RECT ();
fixRedraw = !OS.TreeView_GetItemRect (handle, hItem, rect, false);
}
}
if (fixRedraw) {
hParent = OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_PARENT, hItem);
OS.UpdateWindow (handle);
OS.DefWindowProc (handle, OS.WM_SETREDRAW, 0, 0);
/*
* This code is intentionally commented.
*/
// OS.SendMessage (handle, OS.WM_SETREDRAW, 0, 0);
}
if ((style & SWT.MULTI) != 0) {
ignoreDeselect = ignoreSelect = lockSelection = true;
}
/*
* Feature in Windows. When an item is deleted and a tool tip
* is showing, Window requests the new text for the new item
* that is under the cursor right away. This means that when
* multiple items are deleted, the tool tip flashes, showing
* each new item in the tool tip as it is scrolled into view.
* The fix is to hide tool tips when any item is deleted.
*
* NOTE: This only happens on Vista.
*/
if (!OS.IsWinCE && OS.WIN32_VERSION >= OS.VERSION (6, 0)) {
int /*long*/ hwndToolTip = OS.SendMessage (handle, OS.TVM_GETTOOLTIPS, 0, 0);
if (hwndToolTip != 0) OS.SendMessage (hwndToolTip, OS.TTM_POP, 0 ,0);
}
shrink = ignoreShrink = true;
OS.SendMessage (handle, OS.TVM_DELETEITEM, 0, hItem);
ignoreShrink = false;
if ((style & SWT.MULTI) != 0) {
ignoreDeselect = ignoreSelect = lockSelection = false;
}
if (fixRedraw) {
OS.DefWindowProc (handle, OS.WM_SETREDRAW, 1, 0);
OS.ValidateRect (handle, null);
/*
* If the item that was deleted was the last child of a tree item that
* is visible, redraw the parent item to force the +/- to be updated.
*/
if (OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_CHILD, hParent) == 0) {
RECT rect = new RECT ();
if (OS.TreeView_GetItemRect (handle, hParent, rect, false)) {
OS.InvalidateRect (handle, rect, true);
}
}
}
int count = (int)/*64*/OS.SendMessage (handle, OS.TVM_GETCOUNT, 0, 0);
if (count == 0) {
if (imageList != null) {
OS.SendMessage (handle, OS.TVM_SETIMAGELIST, 0, 0);
display.releaseImageList (imageList);
}
imageList = null;
if (hwndParent == 0 && !linesVisible) {
if (!hooks (SWT.MeasureItem) && !hooks (SWT.EraseItem) && !hooks (SWT.PaintItem)) {
customDraw = false;
}
}
items = new TreeItem [4];
scrollWidth = 0;
setScrollWidth ();
}
updateScrollBar ();
}
void enableDrag (boolean enabled) {
int bits = OS.GetWindowLong (handle, OS.GWL_STYLE);
if (enabled && hooks (SWT.DragDetect)) {
bits &= ~OS.TVS_DISABLEDRAGDROP;
} else {
bits |= OS.TVS_DISABLEDRAGDROP;
}
OS.SetWindowLong (handle, OS.GWL_STYLE, bits);
}
void enableWidget (boolean enabled) {
super.enableWidget (enabled);
/*
* Feature in Windows. When a tree is given a background color
* using TVM_SETBKCOLOR and the tree is disabled, Windows draws
* the tree using the background color rather than the disabled
* colors. This is different from the table which draws grayed.
* The fix is to set the default background color while the tree
* is disabled and restore it when enabled.
*/
Control control = findBackgroundControl ();
/*
* Bug in Windows. On Vista only, Windows does not draw using
* the background color when the tree is disabled. The fix is
* to set the default color, even when the color has not been
* changed, causing Windows to draw correctly.
*/
if (!OS.IsWinCE && OS.WIN32_VERSION >= OS.VERSION (6, 0)) {
if (control == null) control = this;
}
if (control != null) {
if (control.backgroundImage == null) {
_setBackgroundPixel (enabled ? control.getBackgroundPixel () : -1);
}
}
if (hwndParent != 0) OS.EnableWindow (hwndParent, enabled);
/*
* 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 sort column color. The fix
* is to clear TVS_FULLROWSELECT when a their is
* as sort column.
*/
updateFullSelection ();
}
boolean findCell (int x, int y, TreeItem [] item, int [] index, RECT [] cellRect, RECT [] itemRect) {
boolean found = false;
TVHITTESTINFO lpht = new TVHITTESTINFO ();
lpht.x = x;
lpht.y = y;
OS.SendMessage (handle, OS.TVM_HITTEST, 0, lpht);
if (lpht.hItem != 0) {
item [0] = _getItem (lpht.hItem);
POINT pt = new POINT ();
pt.x = x;
pt.y = y;
int /*long*/ hDC = OS.GetDC (handle);
int /*long*/ oldFont = 0, newFont = OS.SendMessage (handle, OS.WM_GETFONT, 0, 0);
if (newFont != 0) oldFont = OS.SelectObject (hDC, newFont);
RECT rect = new RECT ();
if (hwndParent != 0) {
OS.GetClientRect (hwndParent, rect);
OS.MapWindowPoints (hwndParent, handle, rect, 2);
} else {
OS.GetClientRect (handle, rect);
}
int count = 1;
if (hwndHeader != 0) {
count = Math.max (1, (int)/*64*/OS.SendMessage (hwndHeader, OS.HDM_GETITEMCOUNT, 0, 0));
}
int [] order = new int [count];
if (hwndHeader != 0) OS.SendMessage (hwndHeader, OS.HDM_GETORDERARRAY, count, order);
index [0] = 0;
boolean quit = false;
while (index [0] < count && !quit) {
int /*long*/ hFont = item [0].fontHandle (order [index [0]]);
if (hFont != -1) hFont = OS.SelectObject (hDC, hFont);
cellRect [0] = item [0].getBounds (order [index [0]], true, false, true, false, true, hDC);
if (cellRect [0].left > rect.right) {
quit = true;
} else {
cellRect [0].right = Math.min (cellRect [0].right, rect.right);
if (OS.PtInRect (cellRect [0], pt)) {
if (hooks (SWT.MeasureItem) || hooks (SWT.EraseItem) || hooks (SWT.PaintItem)) {
Event event = sendMeasureItemEvent (item [0], order [index [0]], hDC);
if (isDisposed () || item [0].isDisposed ()) break;
itemRect [0] = new RECT ();
itemRect [0].left = event.x;
itemRect [0].right = event.x + event.width;
itemRect [0].top = event.y;
itemRect [0].bottom = event.y + event.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 (int /*long*/ hFirstItem, int /*long*/ hItem) {
if (hFirstItem == 0) return -1;
if (hFirstItem == hFirstIndexOf) {
if (hFirstIndexOf == hItem) {
hLastIndexOf = hFirstIndexOf;
return lastIndexOf = 0;
}
if (hLastIndexOf == hItem) return lastIndexOf;
int /*long*/ hPrevItem = OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_PREVIOUS, hLastIndexOf);
if (hPrevItem == hItem) {
hLastIndexOf = hPrevItem;
return --lastIndexOf;
}
int /*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;
int /*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;
}
Widget findItem (int /*long*/ hItem) {
return _getItem (hItem);
}
int /*long*/ findItem (int /*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;
int /*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;
int /*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;
int /*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 ();
int /*long*/ hItem = OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_CARET, 0);
return hItem != 0 ? _getItem (hItem) : null;
}
/**
* Returns the width in pixels of a grid line.
*
* @return the width of a grid line in pixels
*
* @exception SWTException <ul>
* <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
* <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
* </ul>
*
* @since 3.1
*/
public int getGridLineWidth () {
checkWidget ();
return GRID_WIDTH;
}
/**
* Returns the height of the receiver's header
*
* @return the height of the header or zero if the header is not visible
*
* @exception SWTException <ul>
* <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
* <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
* </ul>
*
* @since 3.1
*/
public int getHeaderHeight () {
checkWidget ();
if (hwndHeader == 0) return 0;
RECT rect = new RECT ();
OS.GetWindowRect (hwndHeader, rect);
return rect.bottom - rect.top;
}
/**
* Returns <code>true</code> if the receiver's header is visible,
* and <code>false</code> otherwise.
* <p>
* 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.
* </p>
*
* @return the receiver's header's visibility state
*
* @exception SWTException <ul>
* <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
* <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
* </ul>
*
* @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, getItemHeight ());
}
int /*long*/ getBottomItem () {
int /*long*/ hItem = OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_FIRSTVISIBLE, 0);
if (hItem == 0) return 0;
int index = 0, count = (int)/*64*/OS.SendMessage (handle, OS.TVM_GETVISIBLECOUNT, 0, 0);
while (index < count) {
int /*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 <code>TreeColumn</code>s were created by the programmer,
* this method will throw <code>ERROR_INVALID_RANGE</code> 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 <ul>
* <li>ERROR_INVALID_RANGE - if the index is not between 0 and the number of elements in the list minus 1 (inclusive)</li>
* </ul>
* @exception SWTException <ul>
* <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
* <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
* </ul>
*
* @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 (hwndHeader == 0) error (SWT.ERROR_INVALID_RANGE);
int count = (int)/*64*/OS.SendMessage (hwndHeader, OS.HDM_GETITEMCOUNT, 0, 0);
if (!(0 <= index && index < count)) error (SWT.ERROR_INVALID_RANGE);
return columns [index];
}
/**
* Returns the number of columns contained in the receiver.
* If no <code>TreeColumn</code>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 <ul>
* <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
* <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
* </ul>
*
* @since 3.1
*/
public int getColumnCount () {
checkWidget ();
if (hwndHeader == 0) return 0;
return (int)/*64*/OS.SendMessage (hwndHeader, OS.HDM_GETITEMCOUNT, 0, 0);
}
/**
* 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.
* <p>
* 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.
* </p><p>
* 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.
* </p>
*
* @return the current visual order of the receiver's items
*
* @exception SWTException <ul>
* <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
* <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
* </ul>
*
* @see Tree#setColumnOrder(int[])
* @see TreeColumn#getMoveable()
* @see TreeColumn#setMoveable(boolean)
* @see SWT#Move
*
* @since 3.2
*/
public int[] getColumnOrder () {
checkWidget ();
if (hwndHeader == 0) return new int [0];
int count = (int)/*64*/OS.SendMessage (hwndHeader, OS.HDM_GETITEMCOUNT, 0, 0);
int [] order = new int [count];
OS.SendMessage (hwndHeader, OS.HDM_GETORDERARRAY, count, order);
return order;
}
/**
* Returns an array of <code>TreeColumn</code>s which are the
* columns in the receiver. Columns are returned in the order
* that they were created. If no <code>TreeColumn</code>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.
* <p>
* 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.
* </p>
*
* @return the items in the receiver
*
* @exception SWTException <ul>
* <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
* <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
* </ul>
*
* @see Tree#getColumnOrder()
* @see Tree#setColumnOrder(int[])
* @see TreeColumn#getMoveable()
* @see TreeColumn#setMoveable(boolean)
* @see SWT#Move
*
* @since 3.1
*/
public TreeColumn [] getColumns () {
checkWidget ();
if (hwndHeader == 0) return new TreeColumn [0];
int count = (int)/*64*/OS.SendMessage (hwndHeader, OS.HDM_GETITEMCOUNT, 0, 0);
TreeColumn [] result = new TreeColumn [count];
System.arraycopy (columns, 0, result, 0, count);
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 <ul>
* <li>ERROR_INVALID_RANGE - if the index is not between 0 and the number of elements in the list minus 1 (inclusive)</li>
* </ul>
* @exception SWTException <ul>
* <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
* <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
* </ul>
*
* @since 3.1
*/
public TreeItem getItem (int index) {
checkWidget ();
if (index < 0) error (SWT.ERROR_INVALID_RANGE);
int /*long*/ hFirstItem = OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_ROOT, 0);
if (hFirstItem == 0) error (SWT.ERROR_INVALID_RANGE);
int /*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)/*64*/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)/*64*/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.
* <p>
* 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.
* </p>
*
* @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 <ul>
* <li>ERROR_NULL_ARGUMENT - if the point is null</li>
* </ul>
* @exception SWTException <ul>
* <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
* <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
* </ul>
*/
public TreeItem getItem (Point point) {
checkWidget ();
if (point == null) error (SWT.ERROR_NULL_ARGUMENT);
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 <ul>
* <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
* <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
* </ul>
*/
public int getItemCount () {
checkWidget ();
int /*long*/ hItem = OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_ROOT, 0);
if (hItem == 0) return 0;
return getItemCount (hItem);
}
int getItemCount (int /*long*/ hItem) {
int count = 0;
int /*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 <em>one</em> of the items in the tree.
*
* @return the height of one item
*
* @exception SWTException <ul>
* <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
* <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
* </ul>
*/
public int getItemHeight () {
checkWidget ();
return (int)/*64*/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.
* <p>
* 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.
* </p>
*
* @return the items
*
* @exception SWTException <ul>
* <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
* <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
* </ul>
*/
public TreeItem [] getItems () {
checkWidget ();
int /*long*/ hItem = OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_ROOT, 0);
if (hItem == 0) return new TreeItem [0];
return getItems (hItem);
}
TreeItem [] getItems (int /*long*/ hTreeItem) {
int count = 0;
int /*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)/*64*/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 <code>true</code> if the receiver's lines are visible,
* and <code>false</code> otherwise.
* <p>
* 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.
* </p>
*
* @return the visibility state of the lines
*
* @exception SWTException <ul>
* <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
* <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
* </ul>
*
* @since 3.1
*/
public boolean getLinesVisible () {
checkWidget ();
return linesVisible;
}
int /*long*/ getNextSelection (int /*long*/ hItem, TVITEM tvItem) {
while (hItem != 0) {
int state = 0;
if (OS.IsWinCE) {
tvItem.hItem = hItem;
OS.SendMessage (handle, OS.TVM_GETITEM, 0, tvItem);
state = tvItem.state;
} else {
state = (int)/*64*/OS.SendMessage (handle, OS.TVM_GETITEMSTATE, hItem, OS.TVIS_SELECTED);
}
if ((state & OS.TVIS_SELECTED) != 0) return hItem;
int /*long*/ hFirstItem = OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_CHILD, hItem);
int /*long*/ hSelected = getNextSelection (hFirstItem, tvItem);
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
* <code>TreeItem</code> or null when the receiver is a
* root.
*
* @return the receiver's parent item
*
* @exception SWTException <ul>
* <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
* <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
* </ul>
*/
public TreeItem getParentItem () {
checkWidget ();
return null;
}
int getSelection (int /*long*/ hItem, TVITEM tvItem, TreeItem [] selection, int index, int count, boolean bigSelection, boolean all) {
while (hItem != 0) {
if (OS.IsWinCE || 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) {
selection [index] = _getItem (hItem, (int)/*64*/tvItem.lParam);
}
index++;
}
} else {
int state = (int)/*64*/OS.SendMessage (handle, OS.TVM_GETITEMSTATE, hItem, OS.TVIS_SELECTED);
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);
selection [index] = _getItem (hItem, (int)/*64*/tvItem.lParam);
}
index++;
}
}
if (index == count) break;
if (all) {
int /*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 <code>TreeItem</code>s that are currently
* selected in the receiver. The order of the items is unspecified.
* An empty array indicates that no items are selected.
* <p>
* Note: This is not the actual structure used by the receiver
* to maintain its selection, so modifying the array will
* not affect the receiver.
* </p>
* @return an array representing the selection
*
* @exception SWTException <ul>
* <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
* <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
* </ul>
*/
public TreeItem [] getSelection () {
checkWidget ();
if ((style & SWT.SINGLE) != 0) {
int /*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];
return new TreeItem [] {_getItem (tvItem.hItem, (int)/*64*/tvItem.lParam)};
}
int count = 0;
TreeItem [] guess = new TreeItem [(style & SWT.VIRTUAL) != 0 ? 8 : 1];
int /*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;
int /*long*/ hItem = OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_ROOT, 0);
count = getSelection (hItem, tvItem, guess, 0, -1, false, true);
} else {
TVITEM tvItem = null;
if (OS.IsWinCE) {
tvItem = new TVITEM ();
tvItem.mask = OS.TVIF_STATE;
}
for (int i=0; i<items.length; i++) {
TreeItem item = items [i];
if (item != null) {
int /*long*/ hItem = item.handle;
int state = 0;
if (OS.IsWinCE) {
tvItem.hItem = hItem;
OS.SendMessage (handle, OS.TVM_GETITEM, 0, tvItem);
state = tvItem.state;
} else {
state = (int)/*64*/OS.SendMessage (handle, OS.TVM_GETITEMSTATE, hItem, OS.TVIS_SELECTED);
}
if ((state & OS.TVIS_SELECTED) != 0) {
if (count < guess.length) guess [count] = item;
count++;
}
}
}
}
OS.SetWindowLongPtr (handle, OS.GWLP_WNDPROC, oldProc);
if (count == 0) return new TreeItem [0];
if (count == guess.length) return guess;
TreeItem [] result = new TreeItem [count];
if (count < guess.length) {
System.arraycopy (guess, 0, result, 0, count);
return result;
}
OS.SetWindowLongPtr (handle, OS.GWLP_WNDPROC, TreeProc);
TVITEM tvItem = new TVITEM ();
tvItem.mask = OS.TVIF_HANDLE | OS.TVIF_PARAM | OS.TVIF_STATE;
int /*long*/ hItem = OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_ROOT, 0);
int itemCount = (int)/*64*/OS.SendMessage (handle, OS.TVM_GETCOUNT, 0, 0);
boolean bigSelection = result.length > itemCount / 2;
if (count != getSelection (hItem, tvItem, result, 0, count, bigSelection, false)) {
getSelection (hItem, tvItem, result, 0, count, bigSelection, true);
}
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 <ul>
* <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
* <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
* </ul>
*/
public int getSelectionCount () {
checkWidget ();
if ((style & SWT.SINGLE) != 0) {
int /*long*/ hItem = OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_CARET, 0);
if (hItem == 0) return 0;
int state = 0;
if (OS.IsWinCE) {
TVITEM tvItem = new TVITEM ();
tvItem.hItem = hItem;
tvItem.mask = OS.TVIF_STATE;
OS.SendMessage (handle, OS.TVM_GETITEM, 0, tvItem);
state = tvItem.state;
} else {
state = (int)/*64*/OS.SendMessage (handle, OS.TVM_GETITEMSTATE, hItem, OS.TVIS_SELECTED);
}
return (state & OS.TVIS_SELECTED) == 0 ? 0 : 1;
}
int count = 0;
int /*long*/ oldProc = OS.GetWindowLongPtr (handle, OS.GWLP_WNDPROC);
TVITEM tvItem = null;
if (OS.IsWinCE) {
tvItem = new TVITEM ();
tvItem.mask = OS.TVIF_STATE;
}
OS.SetWindowLongPtr (handle, OS.GWLP_WNDPROC, TreeProc);
if ((style & SWT.VIRTUAL) != 0) {
int /*long*/ hItem = OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_ROOT, 0);
count = getSelection (hItem, tvItem, null, 0, -1, false, true);
} else {
for (int i=0; i<items.length; i++) {
TreeItem item = items [i];
if (item != null) {
int /*long*/ hItem = item.handle;
int state = 0;
if (OS.IsWinCE) {
tvItem.hItem = hItem;
OS.SendMessage (handle, OS.TVM_GETITEM, 0, tvItem);
state = tvItem.state;
} else {
state = (int)/*64*/OS.SendMessage (handle, OS.TVM_GETITEMSTATE, hItem, OS.TVIS_SELECTED);
}
if ((state & OS.TVIS_SELECTED) != 0) count++;
}
}
}
OS.SetWindowLongPtr (handle, OS.GWLP_WNDPROC, oldProc);
return count;
}
/**
* Returns the column which shows the sort indicator for
* the receiver. The value may be null if no column shows
* the sort indicator.
*
* @return the sort indicator
*
* @exception SWTException <ul>
* <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
* <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
* </ul>
*
* @see #setSortColumn(TreeColumn)
*
* @since 3.2
*/
public TreeColumn getSortColumn () {
checkWidget ();
return sortColumn;
}
int getSortColumnPixel () {
int pixel = OS.IsWindowEnabled (handle) ? getBackgroundPixel () : OS.GetSysColor (OS.COLOR_3DFACE);
int red = pixel & 0xFF;
int green = (pixel & 0xFF00) >> 8;
int blue = (pixel & 0xFF0000) >> 16;
if (red > 240 && green > 240 && blue > 240) {
red -= 8;
green -= 8;
blue -= 8;
} else {
red = Math.min (0xFF, (red / 10) + red);
green = Math.min (0xFF, (green / 10) + green);
blue = Math.min (0xFF, (blue / 10) + blue);
}
return (red & 0xFF) | ((green & 0xFF) << 8) | ((blue & 0xFF) << 16);
}
/**
* Returns the direction of the sort indicator for the receiver.
* The value will be one of <code>UP</code>, <code>DOWN</code>
* or <code>NONE</code>.
*
* @return the sort direction
*
* @exception SWTException <ul>
* <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
* <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
* </ul>
*
* @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 <ul>
* <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
* <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
* </ul>
*
* @since 2.1
*/
public TreeItem getTopItem () {
checkWidget ();
int /*long*/ hItem = OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_FIRSTVISIBLE, 0);
return hItem != 0 ? _getItem (hItem) : null;
}
boolean hitTestSelection (int /*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];
int /*long*/ hDC = OS.GetDC (handle);
int /*long*/ oldFont = 0, newFont = OS.SendMessage (handle, OS.WM_GETFONT, 0, 0);
if (newFont != 0) oldFont = OS.SelectObject (hDC, newFont);
int /*long*/ hFont = item.fontHandle (order [index [0]]);
if (hFont != -1) hFont = OS.SelectObject (hDC, hFont);
Event event = sendMeasureItemEvent (item, order [index [0]], hDC);
if (event.getBounds ().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.getBounds ();
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) {
int /*long*/ hImageList = imageList.getHandle ();
int /*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.getBounds ();
headerImageList = display.getImageList (style & SWT.RIGHT_TO_LEFT, bounds.width, bounds.height);
int index = headerImageList.indexOf (image);
if (index == -1) index = headerImageList.add (image);
int /*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 <ul>
* <li>ERROR_NULL_ARGUMENT - if the column is null</li>
* </ul>
* @exception SWTException <ul>
* <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
* <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
* </ul>
*
* @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);
if (hwndHeader == 0) return -1;
int count = (int)/*64*/OS.SendMessage (hwndHeader, OS.HDM_GETITEMCOUNT, 0, 0);
for (int i=0; i<count; i++) {
if (columns [i] == column) return i;
}
return -1;
}
/**
* Searches the receiver's list starting at the first item
* (index 0) until an item is found that is equal to the
* argument, and returns the index of that item. If no item
* is found, returns -1.
*
* @param item the search item
* @return the index of the item
*
* @exception IllegalArgumentException <ul>
* <li>ERROR_NULL_ARGUMENT - if the item is null</li>
* <li>ERROR_INVALID_ARGUMENT - if the item has been disposed</li>
* </ul>
* @exception SWTException <ul>
* <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
* <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
* </ul>
*
* @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);
int /*long*/ hItem = OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_ROOT, 0);
return hItem == 0 ? -1 : findIndex (hItem, item.handle);
}
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) {
int /*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 {
int /*long*/ hItem = OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_FIRSTVISIBLE, 0);
if (hItem != 0) {
TVITEM tvItem = null;
if (OS.IsWinCE) {
tvItem = new TVITEM ();
tvItem.mask = OS.TVIF_STATE;
}
RECT rect = new RECT ();
int index = 0, count = (int)/*64*/OS.SendMessage (handle, OS.TVM_GETVISIBLECOUNT, 0, 0);
while (index < count && hItem != 0) {
int state = 0;
if (OS.IsWinCE) {
tvItem.hItem = hItem;
OS.SendMessage (handle, OS.TVM_GETITEM, 0, tvItem);
state = tvItem.state;
} else {
state = (int)/*64*/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++;
}
}
}
}
void register () {
super.register ();
if (hwndParent != 0) display.addControl (hwndParent, this);
if (hwndHeader != 0) display.addControl (hwndHeader, this);
}
void releaseItem (int /*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)/*64*/tvItem.lParam;
if (release) {
TreeItem item = items [(int)/*64*/tvItem.lParam];
if (item != null) item.release (false);
}
items [(int)/*64*/tvItem.lParam] = null;
}
}
}
void releaseItems (int /*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);
}
}
void releaseHandle () {
super.releaseHandle ();
hwndParent = hwndHeader = 0;
}
void releaseChildren (boolean destroy) {
if (items != null) {
for (int i=0; i<items.length; i++) {
TreeItem item = items [i];
if (item != null && !item.isDisposed ()) {
item.release (false);
}
}
items = null;
}
if (columns != null) {
for (int i=0; i<columns.length; i++) {
TreeColumn column = columns [i];
if (column != null && !column.isDisposed ()) {
column.release (false);
}
}
columns = null;
}
super.releaseChildren (destroy);
}
void releaseWidget () {
super.releaseWidget ();
/*
* Feature in Windows. For some reason, when TVM_GETIMAGELIST
* or TVM_SETIMAGELIST is sent, the tree issues NM_CUSTOMDRAW
* messages. This behavior is unwanted when the tree is being
* disposed. The fix is to ignore NM_CUSTOMDRAW messages by
* clearing the custom draw flag.
*
* NOTE: This only happens on Windows XP.
*/
customDraw = false;
if (imageList != null) {
OS.SendMessage (handle, OS.TVM_SETIMAGELIST, OS.TVSIL_NORMAL, 0);
display.releaseImageList (imageList);
}
if (headerImageList != null) {
if (hwndHeader != 0) {
OS.SendMessage (hwndHeader, OS.HDM_SETIMAGELIST, 0, 0);
}
display.releaseImageList (headerImageList);
}
imageList = headerImageList = null;
int /*long*/ hStateList = OS.SendMessage (handle, OS.TVM_GETIMAGELIST, OS.TVSIL_STATE, 0);
OS.SendMessage (handle, OS.TVM_SETIMAGELIST, OS.TVSIL_STATE, 0);
if (hStateList != 0) OS.ImageList_Destroy (hStateList);
if (itemToolTipHandle != 0) OS.DestroyWindow (itemToolTipHandle);
if (headerToolTipHandle != 0) OS.DestroyWindow (headerToolTipHandle);
itemToolTipHandle = headerToolTipHandle = 0;
}
/**
* Removes all of the items from the receiver.
*
* @exception SWTException <ul>
* <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
* <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
* </ul>
*/
public void removeAll () {
checkWidget ();
hFirstIndexOf = hLastIndexOf = 0;
itemCount = -1;
for (int i=0; i<items.length; i++) {
TreeItem item = items [i];
if (item != null && !item.isDisposed ()) {
item.release (false);
}
}
ignoreDeselect = ignoreSelect = true;
boolean redraw = drawCount == 0 && OS.IsWindowVisible (handle);
if (redraw) OS.DefWindowProc (handle, OS.WM_SETREDRAW, 0, 0);
shrink = ignoreShrink = true;
int /*long*/ result = OS.SendMessage (handle, OS.TVM_DELETEITEM, 0, OS.TVI_ROOT);
ignoreShrink = false;
if (redraw) {
OS.DefWindowProc (handle, OS.WM_SETREDRAW, 1, 0);
OS.InvalidateRect (handle, null, true);
}
ignoreDeselect = ignoreSelect = false;
if (result == 0) error (SWT.ERROR_ITEM_NOT_REMOVED);
if (imageList != null) {
OS.SendMessage (handle, OS.TVM_SETIMAGELIST, 0, 0);
display.releaseImageList (imageList);
}
imageList = null;
if (hwndParent == 0 && !linesVisible) {
if (!hooks (SWT.MeasureItem) && !hooks (SWT.EraseItem) && !hooks (SWT.PaintItem)) {
customDraw = false;
}
}
hAnchor = hInsert = hFirstIndexOf = hLastIndexOf = 0;
itemCount = -1;
items = new TreeItem [4];
scrollWidth = 0;
setScrollWidth ();
updateScrollBar ();
}
/**
* Removes the listener from the collection of listeners who will
* be notified when the user changes the receiver's selection.
*
* @param listener the listener which should no longer be notified
*
* @exception IllegalArgumentException <ul>
* <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
* </ul>
* @exception SWTException <ul>
* <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
* <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
* </ul>
*
* @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 <ul>
* <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
* </ul>
* @exception SWTException <ul>
* <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
* <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
* </ul>
*
* @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);
}
/**
* Display a mark indicating the point at which an item will be inserted.
* The drop insert item has a visual hint to show where a dragged item
* will be inserted when dropped on the tree.
*
* @param item the insert item. Null will clear the insertion mark.
* @param before true places the insert mark above 'item'. false places
* the insert mark below 'item'.
*
* @exception IllegalArgumentException <ul>
* <li>ERROR_INVALID_ARGUMENT - if the item has been disposed</li>
* </ul>
* @exception SWTException <ul>
* <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
* <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
* </ul>
*/
public void setInsertMark (TreeItem item, boolean before) {
checkWidget ();
int /*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 <ul>
* <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
* <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
* </ul>
*
* @since 3.2
*/
public void setItemCount (int count) {
checkWidget ();
count = Math.max (0, count);
int /*long*/ hItem = OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_ROOT, 0);
setItemCount (count, OS.TVGN_ROOT, hItem);
}
void setItemCount (int count, int /*long*/ hParent, int /*long*/ hItem) {
boolean redraw = false;
if (OS.SendMessage (handle, OS.TVM_GETCOUNT, 0, 0) == 0) {
redraw = drawCount == 0 && 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) {
if (OS.IsWinCE) {
tvItem.hItem = hParent;
tvItem.mask = OS.TVIF_STATE;
OS.SendMessage (handle, OS.TVM_GETITEM, 0, tvItem);
expanded = (tvItem.state & OS.TVIS_EXPANDED) != 0;
} else {
/*
* 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)/*64*/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)/*64*/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; i<count; i++) {
if (expanded) ignoreShrink = true;
createItem (null, hParent, OS.TVI_LAST, 0);
if (expanded) ignoreShrink = false;
}
} else {
shrink = true;
int extra = Math.max (4, (count + 3) / 4 * 4);
TreeItem [] newItems = new TreeItem [items.length + extra];
System.arraycopy (items, 0, newItems, 0, items.length);
items = newItems;
for (int i=itemCount; i<count; i++) {
new TreeItem (this, SWT.NONE, hParent, OS.TVI_LAST, 0);
}
}
if (redraw) {
OS.DefWindowProc (handle, OS.WM_SETREDRAW, 1, 0);
OS.InvalidateRect (handle, null, true);
}
}
/**
* Sets the height of the area which would be used to
* display <em>one</em> of the items in the tree.
*
* @param itemHeight the height of one item
*
* @exception SWTException <ul>
* <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
* <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
* </ul>
*
* @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 <code>true</code>,
* and marks it invisible otherwise.
* <p>
* 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.
* </p>
*
* @param show the new visibility state
*
* @exception SWTException <ul>
* <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
* <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
* </ul>
*
* @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);
}
int /*long*/ scrolledHandle () {
if (hwndHeader == 0) return handle;
int count = (int)/*64*/OS.SendMessage (hwndHeader, OS.HDM_GETITEMCOUNT, 0, 0);
return count == 0 && scrollWidth == 0 ? handle : hwndParent;
}
void select (int /*long*/ hItem, TVITEM tvItem) {
while (hItem != 0) {
tvItem.hItem = hItem;
OS.SendMessage (handle, OS.TVM_SETITEM, 0, tvItem);
int /*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 <ul>
* <li>ERROR_NULL_ARGUMENT - if the item is null</li>
* <li>ERROR_INVALID_ARGUMENT - if the item has been disposed</li>
* </ul>
* @exception SWTException <ul>
* <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
* <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
* </ul>
*
* @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) {
int /*long*/ hItem = item.handle;
int state = 0;
if (OS.IsWinCE) {
TVITEM tvItem = new TVITEM ();
tvItem.hItem = hItem;
tvItem.mask = OS.TVIF_STATE;
OS.SendMessage (handle, OS.TVM_GETITEM, 0, tvItem);
state = tvItem.state;
} else {
state = (int)/*64*/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) == 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 = drawCount == 0 && OS.IsWindowVisible (handle);
if (redraw) {
OS.UpdateWindow (handle);
OS.DefWindowProc (handle, OS.WM_SETREDRAW, 0, 0);
}
setSelection (item);
if (hInfo != null) {
int /*long*/ hThumb = OS.MAKELPARAM (OS.SB_THUMBPOSITION, hInfo.nPos);
OS.SendMessage (handle, OS.WM_HSCROLL, hThumb, 0);
}
int /*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;
}
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.
* <p>
* If the receiver is single-select, do nothing.
* </p>
*
* @exception SWTException <ul>
* <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
* <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
* </ul>
*/
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;
int /*long*/ oldProc = OS.GetWindowLongPtr (handle, OS.GWLP_WNDPROC);
OS.SetWindowLongPtr (handle, OS.GWLP_WNDPROC, TreeProc);
if ((style & SWT.VIRTUAL) != 0) {
int /*long*/ hItem = OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_ROOT, 0);
select (hItem, tvItem);
} else {
for (int i=0; i<items.length; i++) {
TreeItem item = items [i];
if (item != null) {
tvItem.hItem = item.handle;
OS.SendMessage (handle, OS.TVM_SETITEM, 0, tvItem);
}
}
}
OS.SetWindowLongPtr (handle, OS.GWLP_WNDPROC, oldProc);
}
Event sendEraseItemEvent (TreeItem item, NMTTCUSTOMDRAW nmcd, int column, RECT cellRect, int /*long*/ hFont) {
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 = Font.win32_new (display, hFont);
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.x = cellRect.left;
event.y = cellRect.top;
event.width = cellRect.right - cellRect.left;
event.height = 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, int /*long*/ hDC) {
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 = Font.win32_new (display, item.fontHandle (index));
GC gc = GC.win32_new (hDC, data);
Event event = new Event ();
event.item = item;
event.gc = gc;
event.index = index;
event.x = itemRect.left;
event.y = itemRect.top;
event.width = itemRect.right - itemRect.left;
event.height = itemRect.bottom - itemRect.top;
sendEvent (SWT.MeasureItem, event);
event.gc = null;
gc.dispose ();
OS.RestoreDC (hDC, nSavedDC);
if (isDisposed () || item.isDisposed ()) return null;
if (hwndHeader != 0) {
int count = (int)/*64*/OS.SendMessage (hwndHeader, OS.HDM_GETITEMCOUNT, 0, 0);
if (count == 0) {
if (event.x + event.width > scrollWidth) {
setScrollWidth (scrollWidth = event.x + event.width);
}
}
}
if (event.height > getItemHeight ()) setItemHeight (event.height);
return event;
}
Event sendPaintItemEvent (TreeItem item, NMTTCUSTOMDRAW nmcd, int column, RECT itemRect, int /*long*/ hFont) {
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 = Font.win32_new (display, hFont);
data.foreground = OS.GetTextColor (nmcd.hdc);
data.background = OS.GetBkColor (nmcd.hdc);
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.x = itemRect.left;
event.y = itemRect.top;
event.width = itemRect.right - itemRect.left;
event.height = 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;
}
void setBackgroundImage (int /*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 ();
}
void setBackgroundPixel (int pixel) {
Control control = findImageControl ();
if (control != null) {
setBackgroundImage (control.backgroundImage);
return;
}
/*
* Feature in Windows. When a tree is given a background color
* using TVM_SETBKCOLOR and the tree is disabled, Windows draws
* the tree using the background color rather than the disabled
* colors. This is different from the table which draws grayed.
* The fix is to set the default background color while the tree
* is disabled and restore it when enabled.
*/
if (OS.IsWindowEnabled (handle)) _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 ();
}
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 ();
int /*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 <ul>
* <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
* <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
* </ul>
* @exception IllegalArgumentException <ul>
* <li>ERROR_NULL_ARGUMENT - if the item order is null</li>
* <li>ERROR_INVALID_ARGUMENT - if the item order is not the same length as the number of items</li>
* </ul>
*
* @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);
int count = 0;
if (hwndHeader != 0) {
count = (int)/*64*/OS.SendMessage (hwndHeader, OS.HDM_GETITEMCOUNT, 0, 0);
}
if (count == 0) {
if (order.length != 0) error (SWT.ERROR_INVALID_ARGUMENT);
return;
}
if (order.length != count) error (SWT.ERROR_INVALID_ARGUMENT);
int [] oldOrder = new int [count];
OS.SendMessage (hwndHeader, OS.HDM_GETORDERARRAY, count, oldOrder);
boolean reorder = false;
boolean [] seen = new boolean [count];
for (int i=0; i<order.length; i++) {
int index = order [i];
if (index < 0 || index >= count) 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 [count];
for (int i=0; i<count; i++) {
oldRects [i] = new RECT ();
OS.SendMessage (hwndHeader, OS.HDM_GETITEMRECT, i, oldRects [i]);
}
OS.SendMessage (hwndHeader, OS.HDM_SETORDERARRAY, order.length, order);
OS.InvalidateRect (handle, null, true);
updateImageList ();
TreeColumn [] newColumns = new TreeColumn [count];
System.arraycopy (columns, 0, newColumns, 0, count);
RECT newRect = new RECT ();
for (int i=0; i<count; i++) {
TreeColumn column = newColumns [i];
if (!column.isDisposed ()) {
OS.SendMessage (hwndHeader, OS.HDM_GETITEMRECT, i, newRect);
if (newRect.left != oldRects [i].left) {
column.updateToolTip (i);
column.sendEvent (SWT.Move);
}
}
}
}
}
void setCheckboxImageList () {
if ((style & SWT.CHECK) == 0) return;
int count = 5, flags = 0;
if (OS.IsWinCE) {
flags |= OS.ILC_COLOR;
} else {
if (OS.COMCTL32_MAJOR >= 6 && OS.IsAppThemed ()) {
flags |= OS.ILC_COLOR32;
} else {
int /*long*/ hDC = OS.GetDC (handle);
int bits = OS.GetDeviceCaps (hDC, OS.BITSPIXEL);
int planes = OS.GetDeviceCaps (hDC, OS.PLANES);
OS.ReleaseDC (handle, hDC);
int depth = bits * planes;
switch (depth) {
case 4: flags |= OS.ILC_COLOR4; break;
case 8: flags |= OS.ILC_COLOR8; break;
case 16: flags |= OS.ILC_COLOR16; break;
case 24: flags |= OS.ILC_COLOR24; break;
case 32: flags |= OS.ILC_COLOR32; break;
default: flags |= OS.ILC_COLOR; break;
}
flags |= OS.ILC_MASK;
}
}
if ((style & SWT.RIGHT_TO_LEFT) != 0) flags |= OS.ILC_MIRROR;
int height = (int)/*64*/OS.SendMessage (handle, OS.TVM_GETITEMHEIGHT, 0, 0), width = height;
int /*long*/ hStateList = OS.ImageList_Create (width, height, flags, count, count);
int /*long*/ hDC = OS.GetDC (handle);
int /*long*/ memDC = OS.CreateCompatibleDC (hDC);
int /*long*/ hBitmap = OS.CreateCompatibleBitmap (hDC, width * count, height);
int /*long*/ hOldBitmap = OS.SelectObject (memDC, hBitmap);
RECT rect = new RECT ();
OS.SetRect (rect, 0, 0, width * count, height);
/*
* NOTE: DrawFrameControl() draws a black and white
* mask when not drawing a push button. In order to
* make the box surrounding the check mark transparent,
* fill it with a color that is neither black or white.
*/
int clrBackground = 0;
if (OS.COMCTL32_MAJOR >= 6 && OS.IsAppThemed ()) {
Control control = findBackgroundControl ();
if (control == null) control = this;
clrBackground = control.getBackgroundPixel ();
} else {
clrBackground = 0x020000FF;
if ((clrBackground & 0xFFFFFF) == OS.GetSysColor (OS.COLOR_WINDOW)) {
clrBackground = 0x0200FF00;
}
}
int /*long*/ hBrush = OS.CreateSolidBrush (clrBackground);
OS.FillRect (memDC, rect, hBrush);
OS.DeleteObject (hBrush);
int /*long*/ oldFont = OS.SelectObject (hDC, defaultFont ());
TEXTMETRIC tm = OS.IsUnicode ? (TEXTMETRIC) new TEXTMETRICW () : new TEXTMETRICA ();
OS.GetTextMetrics (hDC, tm);
OS.SelectObject (hDC, oldFont);
int itemWidth = Math.min (tm.tmHeight, width);
int itemHeight = Math.min (tm.tmHeight, height);
int left = (width - itemWidth) / 2, top = (height - itemHeight) / 2 + 1;
OS.SetRect (rect, left + width, top, left + width + itemWidth, top + itemHeight);
if (OS.COMCTL32_MAJOR >= 6 && OS.IsAppThemed ()) {
int /*long*/ hTheme = display.hButtonTheme ();
OS.DrawThemeBackground (hTheme, memDC, OS.BP_CHECKBOX, OS.CBS_UNCHECKEDNORMAL, rect, null);
rect.left += width; rect.right += width;
OS.DrawThemeBackground (hTheme, memDC, OS.BP_CHECKBOX, OS.CBS_CHECKEDNORMAL, rect, null);
rect.left += width; rect.right += width;
OS.DrawThemeBackground (hTheme, memDC, OS.BP_CHECKBOX, OS.CBS_UNCHECKEDNORMAL, rect, null);
rect.left += width; rect.right += width;
OS.DrawThemeBackground (hTheme, memDC, OS.BP_CHECKBOX, OS.CBS_MIXEDNORMAL, rect, null);
} else {
OS.DrawFrameControl (memDC, rect, OS.DFC_BUTTON, OS.DFCS_BUTTONCHECK | OS.DFCS_FLAT);
rect.left += width; rect.right += width;
OS.DrawFrameControl (memDC, rect, OS.DFC_BUTTON, OS.DFCS_BUTTONCHECK | OS.DFCS_CHECKED | OS.DFCS_FLAT);
rect.left += width; rect.right += width;
OS.DrawFrameControl (memDC, rect, OS.DFC_BUTTON, OS.DFCS_BUTTONCHECK | OS.DFCS_INACTIVE | OS.DFCS_FLAT);
rect.left += width; rect.right += width;
OS.DrawFrameControl (memDC, rect, OS.DFC_BUTTON, OS.DFCS_BUTTONCHECK | OS.DFCS_CHECKED | OS.DFCS_INACTIVE | OS.DFCS_FLAT);
}
OS.SelectObject (memDC, hOldBitmap);
OS.DeleteDC (memDC);
OS.ReleaseDC (handle, hDC);
if (OS.COMCTL32_MAJOR >= 6 && OS.IsAppThemed ()) {
OS.ImageList_Add (hStateList, hBitmap, 0);
} else {
OS.ImageList_AddMasked (hStateList, hBitmap, clrBackground);
}
OS.DeleteObject (hBitmap);
int /*long*/ hOldStateList = OS.SendMessage (handle, OS.TVM_GETIMAGELIST, OS.TVSIL_STATE, 0);
OS.SendMessage (handle, OS.TVM_SETIMAGELIST, OS.TVSIL_STATE, hStateList);
if (hOldStateList != 0) OS.ImageList_Destroy (hOldStateList);
}
public void setFont (Font font) {
checkWidget ();
super.setFont (font);
if ((style & SWT.CHECK) != 0) setCheckboxImageList ();
}
void setForegroundPixel (int pixel) {
/*
* Bug in Windows. When the tree is using the explorer
* theme, it does not use COLOR_WINDOW_TEXT for the
* foreground. When TVM_SETTEXTCOLOR is called with -1,
* it resets the color to black, not COLOR_WINDOW_TEXT.
* The fix is to explicitly set the color.
*/
if (explorerTheme) {
if (pixel == -1) pixel = defaultForeground ();
}
OS.SendMessage (handle, OS.TVM_SETTEXTCOLOR, 0, pixel);
}
/**
* Marks the receiver's header as visible if the argument is <code>true</code>,
* and marks it invisible otherwise.
* <p>
* 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.
* </p>
*
* @param show the new visibility state
*
* @exception SWTException <ul>
* <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
* <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
* </ul>
*
* @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 ();
}
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.
*/
int /*long*/ hItem = 0;
if (redraw) {
if (drawCount == 1) {
int count = (int)/*64*/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);
if (!redraw) {
if (drawCount == 1) 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 ();
int count = (int)/*64*/OS.SendMessage (hwndHeader, OS.HDM_GETITEMCOUNT, 0, 0);
for (int i=0; i<count; i++) {
hdItem.mask = OS.HDI_WIDTH;
OS.SendMessage (hwndHeader, OS.HDM_GETITEM, i, hdItem);
width += hdItem.cxy;
}
setScrollWidth (Math.max (scrollWidth, width));
}
void setScrollWidth (int width) {
if (hwndHeader == 0 || hwndParent == 0) return;
//TEMPORARY CODE
//scrollWidth = width;
int left = 0;
RECT rect = new RECT ();
SCROLLINFO info = new SCROLLINFO ();
info.cbSize = SCROLLINFO.sizeof;
info.fMask = OS.SIF_RANGE | OS.SIF_PAGE;
int count = (int)/*64*/OS.SendMessage (hwndHeader, OS.HDM_GETITEMCOUNT, 0, 0);
if (count == 0 && width == 0) {
OS.GetScrollInfo (hwndParent, OS.SB_HORZ, info);
info.nPage = info.nMax + 1;
OS.SetScrollInfo (hwndParent, OS.SB_HORZ, info, true);
OS.GetScrollInfo (hwndParent, OS.SB_VERT, info);
info.nPage = info.nMax + 1;
OS.SetScrollInfo (hwndParent, OS.SB_VERT, info, true);
} else {
OS.GetClientRect (hwndParent, rect);
OS.GetScrollInfo (hwndParent, OS.SB_HORZ, info);
info.nMax = width;
info.nPage = rect.right - rect.left + 1;
OS.SetScrollInfo (hwndParent, OS.SB_HORZ, info, true);
info.fMask = OS.SIF_POS;
OS.GetScrollInfo (hwndParent, OS.SB_HORZ, info);
left = info.nPos;
}
if (horizontalBar != null) {
horizontalBar.setIncrement (INCREMENT);
horizontalBar.setPageIncrement (info.nPage);
}
OS.GetClientRect (hwndParent, rect);
int /*long*/ hHeap = OS.GetProcessHeap ();
HDLAYOUT playout = new HDLAYOUT ();
playout.prc = OS.HeapAlloc (hHeap, OS.HEAP_ZERO_MEMORY, RECT.sizeof);
playout.pwpos = OS.HeapAlloc (hHeap, OS.HEAP_ZERO_MEMORY, WINDOWPOS.sizeof);
OS.MoveMemory (playout.prc, rect, RECT.sizeof);
OS.SendMessage (hwndHeader, OS.HDM_LAYOUT, 0, playout);
WINDOWPOS pos = new WINDOWPOS ();
OS.MoveMemory (pos, playout.pwpos, WINDOWPOS.sizeof);
if (playout.prc != 0) OS.HeapFree (hHeap, 0, playout.prc);
if (playout.pwpos != 0) OS.HeapFree (hHeap, 0, playout.pwpos);
SetWindowPos (hwndHeader, OS.HWND_TOP, pos.x - left, pos.y, pos.cx + left, pos.cy, OS.SWP_NOACTIVATE);
int bits = OS.GetWindowLong (handle, OS.GWL_EXSTYLE);
int b = (bits & OS.WS_EX_CLIENTEDGE) != 0 ? OS.GetSystemMetrics (OS.SM_CXEDGE) : 0;
int w = pos.cx + (count == 0 && width == 0 ? 0 : OS.GetSystemMetrics (OS.SM_CXVSCROLL));
int h = rect.bottom - rect.top - pos.cy;
boolean oldIgnore = ignoreResize;
ignoreResize = true;
SetWindowPos (handle, 0, pos.x - left - b, pos.y + pos.cy - b, w + left + b * 2, h + b * 2, OS.SWP_NOACTIVATE | OS.SWP_NOZORDER);
ignoreResize = oldIgnore;
}
void setSelection (int /*long*/ hItem, TVITEM tvItem, TreeItem [] selection) {
while (hItem != 0) {
int index = 0;
while (index < selection.length) {
TreeItem item = selection [index];
if (item != null && item.handle == hItem) break;
index++;
}
tvItem.hItem = hItem;
OS.SendMessage (handle, OS.TVM_GETITEM, 0, tvItem);
if ((tvItem.state & OS.TVIS_SELECTED) != 0) {
if (index == selection.length) {
tvItem.state = 0;
OS.SendMessage (handle, OS.TVM_SETITEM, 0, tvItem);
}
} else {
if (index != selection.length) {
tvItem.state = OS.TVIS_SELECTED;
OS.SendMessage (handle, OS.TVM_SETITEM, 0, tvItem);
}
}
int /*long*/ hFirstItem = OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_CHILD, hItem);
setSelection (hFirstItem, tvItem, selection);
hItem = OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_NEXT, hItem);
}
}
/**
* Sets the receiver's selection to the given item.
* The current selection is cleared before the new item is selected.
* <p>
* If the item is not in the receiver, then it is ignored.
* </p>
*
* @param item the item to select
*
* @exception IllegalArgumentException <ul>
* <li>ERROR_NULL_ARGUMENT - if the item is null</li>
* <li>ERROR_INVALID_ARGUMENT - if the item has been disposed</li>
* </ul>
* @exception SWTException <ul>
* <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
* <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
* </ul>
*
* @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.
* <p>
* 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.
* </p>
*
* @param items the array of items
*
* @exception IllegalArgumentException <ul>
* <li>ERROR_NULL_ARGUMENT - if the array of items is null</li>
* <li>ERROR_INVALID_ARGUMENT - if one of the items has been disposed</li>
* </ul>
* @exception SWTException <ul>
* <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
* <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
* </ul>
*
* @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);
int /*long*/ hOldItem = OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_CARET, 0);
int /*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);
int /*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;
int /*long*/ oldProc = OS.GetWindowLongPtr (handle, OS.GWLP_WNDPROC);
OS.SetWindowLongPtr (handle, OS.GWLP_WNDPROC, TreeProc);
if ((style & SWT.VIRTUAL) != 0) {
int /*long*/ hItem = OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_ROOT, 0);
setSelection (hItem, tvItem, items);
} else {
for (int i=0; i<this.items.length; i++) {
item = this.items [i];
if (item != null) {
int index = 0;
while (index < length) {
if (items [index] == item) break;
index++;
}
tvItem.hItem = item.handle;
OS.SendMessage (handle, OS.TVM_GETITEM, 0, tvItem);
if ((tvItem.state & OS.TVIS_SELECTED) != 0) {
if (index == length) {
tvItem.state = 0;
OS.SendMessage (handle, OS.TVM_SETITEM, 0, tvItem);
}
} else {
if (index != length) {
tvItem.state = OS.TVIS_SELECTED;
OS.SendMessage (handle, OS.TVM_SETITEM, 0, tvItem);
}
}
}
}
}
OS.SetWindowLongPtr (handle, OS.GWLP_WNDPROC, oldProc);
}
/**
* Sets the column used by the sort indicator for the receiver. A null
* value will clear the sort indicator. The current sort column is cleared
* before the new column is set.
*
* @param column the column used by the sort indicator or <code>null</code>
*
* @exception IllegalArgumentException <ul>
* <li>ERROR_INVALID_ARGUMENT - if the column is disposed</li>
* </ul>
* @exception SWTException <ul>
* <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
* <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
* </ul>
*
* @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 <code>UP</code>, <code>DOWN</code> or <code>NONE</code>.
*
* @param direction the direction of the sort indicator
*
* @exception SWTException <ul>
* <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
* <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
* </ul>
*
* @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 <ul>
* <li>ERROR_NULL_ARGUMENT - if the item is null</li>
* <li>ERROR_INVALID_ARGUMENT - if the item has been disposed</li>
* </ul>
* @exception SWTException <ul>
* <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
* <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
* </ul>
*
* @see Tree#getTopItem()
*
* @since 2.1
*/
public void setTopItem (TreeItem item) {
checkWidget ();
if (item == null) SWT.error (SWT.ERROR_NULL_ARGUMENT);
if (item.isDisposed ()) SWT.error (SWT.ERROR_INVALID_ARGUMENT);
int /*long*/ hItem = item.handle;
int /*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 = drawCount == 0 && OS.IsWindowVisible (handle);
if (redraw) OS.DefWindowProc (handle, OS.WM_SETREDRAW, 0, 0);
}
OS.SendMessage (handle, OS.TVM_SELECTITEM, OS.TVGN_FIRSTVISIBLE, hItem);
int /*long*/ hParent = OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_PARENT, hItem);
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);
} else {
if (redraw) {
OS.DefWindowProc (handle, OS.WM_SETREDRAW, 1, 0);
OS.InvalidateRect (handle, null, true);
}
}
updateScrollBar ();
}
void showItem (int /*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 <ul>
* <li>ERROR_NULL_ARGUMENT - if the item is null</li>
* <li>ERROR_INVALID_ARGUMENT - if the item has been disposed</li>
* </ul>
* @exception SWTException <ul>
* <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
* <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
* </ul>
*
* @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;
int count = (int)/*64*/OS.SendMessage (hwndHeader, OS.HDM_GETITEMCOUNT, 0, 0);
if (0 <= index && index < count) {
if (hwndParent != 0) {
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 <ul>
* <li>ERROR_NULL_ARGUMENT - if the item is null</li>
* <li>ERROR_INVALID_ARGUMENT - if the item has been disposed</li>
* </ul>
* @exception SWTException <ul>
* <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
* <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
* </ul>
*
* @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 <ul>
* <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
* <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
* </ul>
*
* @see Tree#showItem(TreeItem)
*/
public void showSelection () {
checkWidget ();
int /*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 = 0;
if (OS.IsWinCE) {
TVITEM tvItem = new TVITEM ();
tvItem.hItem = hItem;
tvItem.mask = OS.TVIF_STATE;
OS.SendMessage (handle, OS.TVM_GETITEM, 0, tvItem);
state = tvItem.state;
} else {
state = (int)/*64*/OS.SendMessage (handle, OS.TVM_GETITEMSTATE, hItem, OS.TVIS_SELECTED);
}
if ((state & OS.TVIS_SELECTED) == 0) return;
} else {
int /*long*/ oldProc = OS.GetWindowLongPtr (handle, OS.GWLP_WNDPROC);
OS.SetWindowLongPtr (handle, OS.GWLP_WNDPROC, TreeProc);
TVITEM tvItem = null;
if (OS.IsWinCE) {
tvItem = new TVITEM ();
tvItem.mask = OS.TVIF_STATE;
}
if ((style & SWT.VIRTUAL) != 0) {
int /*long*/ hRoot = OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_ROOT, 0);
hItem = getNextSelection (hRoot, tvItem);
} else {
//FIXME - this code expands first selected item it finds
int index = 0;
while (index <items.length) {
TreeItem item = items [index];
if (item != null) {
int state = 0;
if (OS.IsWinCE) {
tvItem.hItem = item.handle;
OS.SendMessage (handle, OS.TVM_GETITEM, 0, tvItem);
state = tvItem.state;
} else {
state = (int)/*64*/OS.SendMessage (handle, OS.TVM_GETITEMSTATE, item.handle, OS.TVIS_SELECTED);
}
if ((state & OS.TVIS_SELECTED) != 0) {
hItem = item.handle;
break;
}
}
index++;
}
}
OS.SetWindowLongPtr (handle, OS.GWLP_WNDPROC, oldProc);
}
if (hItem != 0) showItem (hItem);
}
/*public*/ void sort () {
checkWidget ();
if ((style & SWT.VIRTUAL) != 0) return;
sort (OS.TVI_ROOT, false);
}
void sort (int /*long*/ hParent, boolean all) {
int itemCount = (int)/*64*/OS.SendMessage (handle, OS.TVM_GETCOUNT, 0, 0);
if (itemCount == 0 || itemCount == 1) return;
hFirstIndexOf = hLastIndexOf = 0;
itemCount = -1;
if (sortDirection == SWT.UP || sortDirection == SWT.NONE) {
OS.SendMessage (handle, OS.TVM_SORTCHILDREN, all ? 1 : 0, hParent);
} else {
Callback compareCallback = new Callback (this, "CompareFunc", 3);
int /*long*/ lpfnCompare = compareCallback.getAddress ();
TVSORTCB psort = new TVSORTCB ();
psort.hParent = hParent;
psort.lpfnCompare = lpfnCompare;
psort.lParam = sortColumn == null ? 0 : indexOf (sortColumn);
OS.SendMessage (handle, OS.TVM_SORTCHILDRENCB, all ? 1 : 0, psort);
compareCallback.dispose ();
}
}
void subclass () {
super.subclass ();
if (hwndHeader != 0) {
OS.SetWindowLongPtr (hwndHeader, OS.GWLP_WNDPROC, display.windowProc);
}
}
RECT toolTipInset (RECT rect) {
if (!OS.IsWinCE && OS.WIN32_VERSION >= OS.VERSION (6, 0)) {
RECT insetRect = new RECT ();
OS.SetRect (insetRect, rect.left - 1, rect.top - 1, rect.right + 1, rect.bottom + 1);
return insetRect;
}
return rect;
}
RECT toolTipRect (RECT rect) {
RECT toolRect = new RECT ();
if (!OS.IsWinCE && OS.WIN32_VERSION >= OS.VERSION (6, 0)) {
OS.SetRect (toolRect, rect.left - 1, rect.top - 1, rect.right + 1, rect.bottom + 1);
} else {
OS.SetRect (toolRect, rect.left, rect.top, rect.right, rect.bottom);
int dwStyle = OS.GetWindowLong (itemToolTipHandle, OS.GWL_STYLE);
int dwExStyle = OS.GetWindowLong (itemToolTipHandle, OS.GWL_EXSTYLE);
OS.AdjustWindowRectEx (toolRect, dwStyle, false, dwExStyle);
}
return toolRect;
}
String toolTipText (NMTTDISPINFO hdr) {
int /*long*/ hwndToolTip = OS.SendMessage (handle, OS.TVM_GETTOOLTIPS, 0, 0);
if (hwndToolTip == hdr.hwndFrom && toolTipText != null) return ""; //$NON-NLS-1$
if (headerToolTipHandle == hdr.hwndFrom) {
int count = (int)/*64*/OS.SendMessage (hwndHeader, OS.HDM_GETITEMCOUNT, 0, 0);
for (int i=0; i<count; i++) {
TreeColumn column = columns [i];
if (column.id == hdr.idFrom) return column.toolTipText;
}
return super.toolTipText (hdr);
}
if (itemToolTipHandle == hdr.hwndFrom) {
if (toolTipText != null) return "";
int pos = OS.GetMessagePos ();
POINT pt = new POINT();
OS.POINTSTOPOINT (pt, pos);
OS.ScreenToClient (handle, pt);
int [] index = new int [1];
TreeItem [] item = new TreeItem [1];
RECT [] cellRect = new RECT [1], itemRect = new RECT [1];
if (findCell (pt.x, pt.y, item, index, cellRect, itemRect)) {
String text = null;
if (index [0] == 0) {
text = item [0].text;
} else {
String[] strings = item [0].strings;
if (strings != null) text = strings [index [0]];
}
//TEMPORARY CODE
if (hooks (SWT.MeasureItem) || hooks (SWT.EraseItem) || hooks (SWT.PaintItem)) {
text = " ";
}
if (text != null) return text;
}
}
return super.toolTipText (hdr);
}
int /*long*/ topHandle () {
return hwndParent != 0 ? hwndParent : handle;
}
void updateFullSelection () {
if ((style & SWT.FULL_SELECTION) != 0) {
int oldBits = OS.GetWindowLong (handle, OS.GWL_STYLE), newBits = oldBits;
if ((newBits & OS.TVS_FULLROWSELECT) != 0) {
if (!OS.IsWindowEnabled (handle) || findImageControl () != null) {
if (!explorerTheme) newBits &= ~OS.TVS_FULLROWSELECT;
}
} else {
if (OS.IsWindowEnabled (handle) && findImageControl () == null) {
if (!hooks (SWT.EraseItem) && !hooks (SWT.PaintItem)) {
newBits |= OS.TVS_FULLROWSELECT;
}
}
}
if (newBits != oldBits) {
OS.SetWindowLong (handle, OS.GWL_STYLE, newBits);
OS.InvalidateRect (handle, null, true);
}
}
}
void updateHeaderToolTips () {
if (headerToolTipHandle == 0) return;
RECT rect = new RECT ();
TOOLINFO lpti = new TOOLINFO ();
lpti.cbSize = TOOLINFO.sizeof;
lpti.uFlags = OS.TTF_SUBCLASS;
lpti.hwnd = hwndHeader;
lpti.lpszText = OS.LPSTR_TEXTCALLBACK;
int count = (int)/*64*/OS.SendMessage (hwndHeader, OS.HDM_GETITEMCOUNT, 0, 0);
for (int i=0; i<count; i++) {
TreeColumn column = columns [i];
if (OS.SendMessage (hwndHeader, OS.HDM_GETITEMRECT, i, rect) != 0) {
lpti.uId = column.id = display.nextToolTipId++;
lpti.left = rect.left;
lpti.top = rect.top;
lpti.right = rect.right;
lpti.bottom = rect.bottom;
OS.SendMessage (headerToolTipHandle, OS.TTM_ADDTOOL, 0, lpti);
}
}
}
void updateImageList () {
if (imageList == null) return;
if (hwndHeader == 0) return;
int i = 0, index = (int)/*64*/OS.SendMessage (hwndHeader, OS.HDM_ORDERTOINDEX, 0, 0);
while (i < items.length) {
TreeItem item = items [i];
if (item != null) {
Image image = null;
if (index == 0) {
image = item.image;
} else {
Image [] images = item.images;
if (images != null) image = images [index];
}
if (image != null) break;
}
i++;
}
/*
* 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.
*/
int /*long*/ hImageList = i == items.length ? 0 : imageList.getHandle ();
int /*long*/ hOldImageList = OS.SendMessage (handle, OS.TVM_GETIMAGELIST, OS.TVSIL_NORMAL, 0);
if (hImageList != hOldImageList) {
OS.SendMessage (handle, OS.TVM_SETIMAGELIST, OS.TVSIL_NORMAL, hImageList);
}
}
void updateImages () {
if (sortColumn != null && !sortColumn.isDisposed ()) {
if (OS.COMCTL32_MAJOR < 6) {
switch (sortDirection) {
case SWT.UP:
case SWT.DOWN:
sortColumn.setImage (display.getSortImage (sortDirection), true, true);
break;
}
}
}
}
void updateScrollBar () {
if (hwndParent != 0) {
int columnCount = (int)/*64*/OS.SendMessage (hwndHeader, OS.HDM_GETITEMCOUNT, 0, 0);
if (columnCount != 0 || scrollWidth != 0) {
SCROLLINFO info = new SCROLLINFO ();
info.cbSize = SCROLLINFO.sizeof;
info.fMask = OS.SIF_ALL;
int itemCount = (int)/*64*/OS.SendMessage (handle, OS.TVM_GETCOUNT, 0, 0);
if (itemCount == 0) {
OS.GetScrollInfo (hwndParent, OS.SB_VERT, info);
info.nPage = info.nMax + 1;
OS.SetScrollInfo (hwndParent, OS.SB_VERT, info, true);
} else {
OS.GetScrollInfo (handle, OS.SB_VERT, info);
if (info.nPage == 0) {
SCROLLBARINFO psbi = new SCROLLBARINFO ();
psbi.cbSize = SCROLLBARINFO.sizeof;
OS.GetScrollBarInfo (handle, OS.OBJID_VSCROLL, psbi);
if ((psbi.rgstate [0] & OS.STATE_SYSTEM_INVISIBLE) != 0) {
info.nPage = info.nMax + 1;
}
}
OS.SetScrollInfo (hwndParent, OS.SB_VERT, info, true);
}
}
}
}
void unsubclass () {
super.unsubclass ();
if (hwndHeader != 0) {
OS.SetWindowLongPtr (hwndHeader, OS.GWLP_WNDPROC, HeaderProc);
}
}
int widgetStyle () {
int bits = super.widgetStyle () | OS.TVS_SHOWSELALWAYS | OS.TVS_LINESATROOT | OS.TVS_HASBUTTONS | OS.TVS_NONEVENHEIGHT;
if (EXPLORER_THEME && !OS.IsWinCE && OS.WIN32_VERSION >= OS.VERSION (6, 0) && OS.IsAppThemed ()) {
bits |= OS.TVS_TRACKSELECT;
if ((style & SWT.FULL_SELECTION) != 0) bits |= OS.TVS_FULLROWSELECT;
} else {
if ((style & SWT.FULL_SELECTION) != 0) {
bits |= OS.TVS_FULLROWSELECT;
} else {
bits |= OS.TVS_HASLINES;
}
}
// bits |= OS.TVS_NOTOOLTIPS | OS.TVS_DISABLEDRAGDROP;
return bits | OS.TVS_DISABLEDRAGDROP;
}
TCHAR windowClass () {
return TreeClass;
}
int /*long*/ windowProc () {
return TreeProc;
}
int /*long*/ windowProc (int /*long*/ hwnd, int msg, int /*long*/ wParam, int /*long*/ lParam) {
if (hwndHeader != 0 && hwnd == hwndHeader) {
switch (msg) {
/* This code is intentionally commented */
// case OS.WM_CONTEXTMENU: {
// LRESULT result = wmContextMenu (hwnd, wParam, lParam);
// if (result != null) return result.value;
// break;
// }
case OS.WM_CAPTURECHANGED: {
/*
* Bug in Windows. When the capture changes during a
* header drag, Windows does not redraw the header item
* such that the header remains pressed. For example,
* when focus is assigned to a push button, the mouse is
* pressed (but not released), then the SPACE key is
* pressed to activate the button, the capture changes,
* the header not notified and NM_RELEASEDCAPTURE is not
* sent. The fix is to redraw the header when the capture
* changes to another control.
*
* This does not happen on XP.
*/
if (OS.COMCTL32_MAJOR < 6) {
if (lParam != 0 && lParam != hwndHeader) {
OS.InvalidateRect (hwndHeader, null, true);
}
}
break;
}
case OS.WM_MOUSELEAVE: {
/*
* Bug in Windows. On XP, when a tooltip is hidden
* due to a time out or mouse press, the tooltip
* remains active although no longer visible and
* won't show again until another tooltip becomes
* active. The fix is to reset the tooltip bounds.
*/
if (OS.COMCTL32_MAJOR >= 6) updateHeaderToolTips ();
updateHeaderToolTips ();
break;
}
case OS.WM_NOTIFY: {
NMHDR hdr = new NMHDR ();
OS.MoveMemory (hdr, lParam, NMHDR.sizeof);
switch (hdr.code) {
case OS.TTN_SHOW:
case OS.TTN_POP:
case OS.TTN_GETDISPINFOA:
case OS.TTN_GETDISPINFOW:
return OS.SendMessage (handle, msg, wParam, lParam);
}
break;
}
case OS.WM_SETCURSOR: {
if (wParam == hwnd) {
int hitTest = (short) OS.LOWORD (lParam);
if (hitTest == OS.HTCLIENT) {
HDHITTESTINFO pinfo = new HDHITTESTINFO ();
int pos = OS.GetMessagePos ();
POINT pt = new POINT ();
OS.POINTSTOPOINT (pt, pos);
OS.ScreenToClient (hwnd, pt);
pinfo.x = pt.x;
pinfo.y = pt.y;
int columnCount = (int)/*64*/OS.SendMessage (hwndHeader, OS.HDM_GETITEMCOUNT, 0, 0);
int index = (int)/*64*/OS.SendMessage (hwndHeader, OS.HDM_HITTEST, 0, pinfo);
if (0 <= index && index < columnCount && !columns [index].resizable) {
if ((pinfo.flags & (OS.HHT_ONDIVIDER | OS.HHT_ONDIVOPEN)) != 0) {
OS.SetCursor (OS.LoadCursor (0, OS.IDC_ARROW));
return 1;
}
}
}
}
break;
}
}
return callWindowProc (hwnd, msg, wParam, lParam);
}
if (hwndParent != 0 && hwnd == hwndParent) {
switch (msg) {
case OS.WM_MOVE: {
sendEvent (SWT.Move);
return 0;
}
case OS.WM_SIZE: {
setScrollWidth ();
if (ignoreResize) return 0;
setResizeChildren (false);
int /*long*/ code = callWindowProc (hwnd, OS.WM_SIZE, wParam, lParam);
sendEvent (SWT.Resize);
if (isDisposed ()) return 0;
if (layout != null) {
markLayout (false, false);
updateLayout (false, false);
}
setResizeChildren (true);
updateScrollBar ();
return code;
}
case OS.WM_NCPAINT: {
LRESULT result = wmNCPaint (hwnd, wParam, lParam);
if (result != null) return result.value;
break;
}
case OS.WM_PRINT: {
LRESULT result = wmPrint (hwnd, wParam, lParam);
if (result != null) return result.value;
break;
}
case OS.WM_COMMAND:
case OS.WM_NOTIFY:
case OS.WM_SYSCOLORCHANGE: {
return OS.SendMessage (handle, msg, wParam, lParam);
}
case OS.WM_HSCROLL: {
/*
* Bug on WinCE. lParam should be NULL when the message is not sent
* by a scroll bar control, but it contains the handle to the window.
* When the message is sent by a scroll bar control, it correctly
* contains the handle to the scroll bar. The fix is to check for
* both.
*/
if (horizontalBar != null && (lParam == 0 || lParam == hwndParent)) {
wmScroll (horizontalBar, true, hwndParent, OS.WM_HSCROLL, wParam, lParam);
}
setScrollWidth ();
break;
}
case OS.WM_VSCROLL: {
SCROLLINFO info = new SCROLLINFO ();
info.cbSize = SCROLLINFO.sizeof;
info.fMask = OS.SIF_ALL;
OS.GetScrollInfo (hwndParent, OS.SB_VERT, info);
/*
* Update the nPos field to match the nTrackPos field
* so that the tree scrolls when the scroll bar of the
* parent is dragged.
*
* NOTE: For some reason, this code is only necessary
* on Windows Vista.
*/
if (!OS.IsWinCE && OS.WIN32_VERSION >= OS.VERSION (6, 0)) {
if (OS.LOWORD (wParam) == OS.SB_THUMBTRACK) {
info.nPos = info.nTrackPos;
}
}
OS.SetScrollInfo (handle, OS.SB_VERT, info, true);
int /*long*/ code = OS.SendMessage (handle, OS.WM_VSCROLL, wParam, lParam);
OS.GetScrollInfo (handle, OS.SB_VERT, info);
OS.SetScrollInfo (hwndParent, OS.SB_VERT, info, true);
return code;
}
}
return callWindowProc (hwnd, msg, wParam, lParam);
}
return super.windowProc (hwnd, msg, wParam, lParam);
}
LRESULT WM_CHAR (int /*long*/ wParam, int /*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)/*64*/wParam) {
case ' ': {
int /*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);
if (!OS.IsWinCE) {
int /*long*/ id = hItem;
if (OS.COMCTL32_MAJOR >= 6) {
id = OS.SendMessage (handle, OS.TVM_MAPHTREEITEMTOACCID, hItem, 0);
}
OS.NotifyWinEvent (OS.EVENT_OBJECT_FOCUS, handle, OS.OBJID_CLIENT, (int)/*64*/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)/*64*/tvItem.lParam);
Event event = new Event ();
event.item = item;
postEvent (SWT.Selection, event);
if ((style & SWT.CHECK) != 0) {
event = new Event ();
event.item = item;
event.detail = SWT.CHECK;
postEvent (SWT.Selection, event);
}
}
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 ();
int /*long*/ hItem = OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_CARET, 0);
if (hItem != 0) event.item = _getItem (hItem);
postEvent (SWT.DefaultSelection, event);
return LRESULT.ZERO;
}
case SWT.ESC:
return LRESULT.ZERO;
}
return result;
}
LRESULT WM_ERASEBKGND (int /*long*/ wParam, int /*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;
}
LRESULT WM_GETOBJECT (int /*long*/ wParam, int /*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);
}
LRESULT WM_HSCROLL (int /*long*/ wParam, int /*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;
}
LRESULT WM_KEYDOWN (int /*long*/ wParam, int /*long*/ lParam) {
LRESULT result = super.WM_KEYDOWN (wParam, lParam);
if (result != null) return result;
switch ((int)/*64*/wParam) {
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) {
int count = (int)/*64*/OS.SendMessage (hwndHeader, OS.HDM_GETITEMCOUNT, 0, 0);
TreeColumn [] newColumns = new TreeColumn [count];
System.arraycopy (columns, 0, newColumns, 0, count);
for (int i=0; i<count; i++) {
TreeColumn column = newColumns [i];
if (!column.isDisposed () && column.getResizable ()) {
column.pack ();
}
}
}
}
break;
case OS.VK_UP:
case OS.VK_DOWN:
case OS.VK_PRIOR:
case OS.VK_NEXT:
case OS.VK_HOME:
case OS.VK_END: {
OS.SendMessage (handle, OS.WM_CHANGEUISTATE, OS.UIS_INITIALIZE, 0);
if ((style & SWT.SINGLE) != 0) break;
if (OS.GetKeyState (OS.VK_SHIFT) < 0) {
int /*long*/ hItem = OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_CARET, 0);
if (hItem != 0) {
if (hAnchor == 0) hAnchor = hItem;
ignoreSelect = ignoreDeselect = true;
int /*long*/ code = callWindowProc (handle, OS.WM_KEYDOWN, wParam, lParam);
ignoreSelect = ignoreDeselect = false;
int /*long*/ hNewItem = OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_CARET, 0);
TVITEM tvItem = new TVITEM ();
tvItem.mask = OS.TVIF_HANDLE | OS.TVIF_STATE;
tvItem.stateMask = OS.TVIS_SELECTED;
int /*long*/ hDeselectItem = hItem;
RECT rect1 = new RECT ();
if (!OS.TreeView_GetItemRect (handle, hAnchor, rect1, false)) {
hAnchor = hItem;
OS.TreeView_GetItemRect (handle, hAnchor, rect1, false);
}
RECT rect2 = new RECT ();
OS.TreeView_GetItemRect (handle, hDeselectItem, rect2, false);
int flags = rect1.top < rect2.top ? OS.TVGN_PREVIOUSVISIBLE : OS.TVGN_NEXTVISIBLE;
while (hDeselectItem != hAnchor) {
tvItem.hItem = hDeselectItem;
OS.SendMessage (handle, OS.TVM_SETITEM, 0, tvItem);
hDeselectItem = OS.SendMessage (handle, OS.TVM_GETNEXTITEM, flags, hDeselectItem);
}
int /*long*/ hSelectItem = hAnchor;
OS.TreeView_GetItemRect (handle, hNewItem, rect1, false);
OS.TreeView_GetItemRect (handle, hSelectItem, rect2, false);
tvItem.state = OS.TVIS_SELECTED;
flags = rect1.top < rect2.top ? OS.TVGN_PREVIOUSVISIBLE : OS.TVGN_NEXTVISIBLE;
while (hSelectItem != hNewItem) {
tvItem.hItem = hSelectItem;
OS.SendMessage (handle, OS.TVM_SETITEM, 0, tvItem);
hSelectItem = OS.SendMessage (handle, OS.TVM_GETNEXTITEM, flags, hSelectItem);
}
tvItem.hItem = hNewItem;
OS.SendMessage (handle, OS.TVM_SETITEM, 0, tvItem);
tvItem.mask = OS.TVIF_HANDLE | OS.TVIF_PARAM;
tvItem.hItem = hNewItem;
OS.SendMessage (handle, OS.TVM_GETITEM, 0, tvItem);
Event event = new Event ();
event.item = _getItem (hNewItem, (int)/*64*/tvItem.lParam);
postEvent (SWT.Selection, event);
return new LRESULT (code);
}
}
if (OS.GetKeyState (OS.VK_CONTROL) < 0) {
int /*long*/ hItem = OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_CARET, 0);
if (hItem != 0) {
TVITEM tvItem = new TVITEM ();
tvItem.mask = OS.TVIF_HANDLE | OS.TVIF_STATE;
tvItem.stateMask = OS.TVIS_SELECTED;
tvItem.hItem = hItem;
OS.SendMessage (handle, OS.TVM_GETITEM, 0, tvItem);
boolean oldSelected = (tvItem.state & OS.TVIS_SELECTED) != 0;
int /*long*/ hNewItem = 0;
switch ((int)/*64*/wParam) {
case OS.VK_UP:
hNewItem = OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_PREVIOUSVISIBLE, hItem);
break;
case OS.VK_DOWN:
hNewItem = OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_NEXTVISIBLE, hItem);
break;
case OS.VK_HOME:
hNewItem = OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_ROOT, 0);
break;
case OS.VK_PRIOR:
hNewItem = OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_FIRSTVISIBLE, 0);
if (hNewItem == hItem) {
OS.SendMessage (handle, OS.WM_VSCROLL, OS.SB_PAGEUP, 0);
hNewItem = OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_FIRSTVISIBLE, 0);
}
break;
case OS.VK_NEXT:
RECT rect = new RECT (), clientRect = new RECT ();
OS.GetClientRect (handle, clientRect);
hNewItem = OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_FIRSTVISIBLE, 0);
do {
int /*long*/ hVisible = OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_NEXTVISIBLE, hNewItem);
if (hVisible == 0) break;
if (!OS.TreeView_GetItemRect (handle, hVisible, rect, false)) break;
if (rect.bottom > 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 && drawCount == 0 && 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 ();
boolean fItemRect = (style & SWT.FULL_SELECTION) == 0;
if (hooks (SWT.EraseItem) || hooks (SWT.PaintItem)) fItemRect = false;
if (!OS.IsWinCE && OS.WIN32_VERSION >= OS.VERSION (6, 0)) fItemRect = false;
OS.TreeView_GetItemRect (handle, hItem, rect1, fItemRect);
OS.TreeView_GetItemRect (handle, hNewItem, rect2, fItemRect);
OS.DefWindowProc (handle, OS.WM_SETREDRAW, 1, 0);
OS.InvalidateRect (handle, rect1, true);
OS.InvalidateRect (handle, rect2, true);
OS.UpdateWindow (handle);
}
return LRESULT.ZERO;
}
}
}
int /*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;
}
LRESULT WM_KILLFOCUS (int /*long*/ wParam, int /*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) {
if (!OS.IsWinCE && OS.COMCTL32_MAJOR >= 6) {
if (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);
}
LRESULT WM_LBUTTONDBLCLK (int /*long*/ wParam, int /*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);
if (!OS.IsWinCE) {
int /*long*/ id = tvItem.hItem;
if (OS.COMCTL32_MAJOR >= 6) {
id = OS.SendMessage (handle, OS.TVM_MAPHTREEITEMTOACCID, tvItem.hItem, 0);
}
OS.NotifyWinEvent (OS.EVENT_OBJECT_FOCUS, handle, OS.OBJID_CLIENT, (int)/*64*/id);
}
Event event = new Event ();
event.item = _getItem (tvItem.hItem, (int)/*64*/tvItem.lParam);
event.detail = SWT.CHECK;
postEvent (SWT.Selection, event);
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);
postEvent (SWT.DefaultSelection, event);
}
}
return result;
}
LRESULT WM_LBUTTONDOWN (int /*long*/ wParam, int /*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;
int /*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;
int /*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);
int /*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) ignoreDeselect = ignoreSelect = lockSelection = true;
int /*long*/ code = callWindowProc (handle, OS.WM_LBUTTONDOWN, wParam, lParam);
if (fixSelection) ignoreDeselect = ignoreSelect = lockSelection = false;
int /*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);
}
}
if (deselected) {
Event event = new Event ();
event.item = _getItem (lpht.hItem);
postEvent (SWT.Selection, event);
}
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);
if (!OS.IsWinCE) {
int /*long*/ id = tvItem.hItem;
if (OS.COMCTL32_MAJOR >= 6) {
id = OS.SendMessage (handle, OS.TVM_MAPHTREEITEMTOACCID, tvItem.hItem, 0);
}
OS.NotifyWinEvent (OS.EVENT_OBJECT_FOCUS, handle, OS.OBJID_CLIENT, (int)/*64*/id);
}
Event event = new Event ();
event.item = _getItem (tvItem.hItem, (int)/*64*/tvItem.lParam);
event.detail = SWT.CHECK;
postEvent (SWT.Selection, event);
return LRESULT.ZERO;
}
}
/* Process the mouse when an item is not selected */
if ((style & SWT.FULL_SELECTION) == 0) {
if ((lpht.flags & OS.TVHT_ONITEM) == 0 && !hooks (SWT.MeasureItem)) {
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;
}
int /*long*/ code = callWindowProc (handle, OS.WM_LBUTTONDOWN, wParam, lParam);
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, focused = 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;
focused = OS.GetFocus () == handle;
}
/* Get the selected state of the last selected item */
boolean redraw = false;
int /*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 using 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
* detect that WM_UPDATEUISTATE will be sent and avoid using
* WM_SETREDRAW to disable 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 causes the pixel corruption
* is found in Composite.WM_UPDATEUISTATE().
*/
int uiState = (int)/*64*/OS.SendMessage (handle, OS.WM_QUERYUISTATE, 0, 0);
if ((uiState & OS.UISF_HIDEFOCUS) == 0) {
redraw = focused && drawCount == 0 && OS.IsWindowVisible (handle);
}
if (redraw) {
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;
int /*long*/ code = callWindowProc (handle, OS.WM_LBUTTONDOWN, wParam, lParam);
int /*long*/ hNewItem = OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_CARET, 0);
/*
* 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 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)) {
if (hitTestSelection (lpht.hItem, lpht.x, lpht.y)) {
if ((lpht.flags & OS.TVHT_ONITEM) == 0) fakeSelection = true;
}
}
}
}
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);
}
}
}
if (redraw) {
RECT rect1 = new RECT (), rect2 = new RECT ();
boolean fItemRect = (style & SWT.FULL_SELECTION) == 0;
if (hooks (SWT.EraseItem) || hooks (SWT.PaintItem)) fItemRect = false;
if (!OS.IsWinCE && OS.WIN32_VERSION >= OS.VERSION (6, 0)) fItemRect = false;
OS.TreeView_GetItemRect (handle, hOldItem, rect1, fItemRect);
OS.TreeView_GetItemRect (handle, hNewItem, rect2, fItemRect);
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;
int /*long*/ oldProc = OS.GetWindowLongPtr (handle, OS.GWLP_WNDPROC);
OS.SetWindowLongPtr (handle, OS.GWLP_WNDPROC, TreeProc);
if ((style & SWT.VIRTUAL) != 0) {
int /*long*/ hItem = OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_ROOT, 0);
deselect (hItem, tvItem, hNewItem);
} else {
for (int i=0; i<items.length; i++) {
TreeItem item = items [i];
if (item != null && item.handle != hNewItem) {
tvItem.hItem = item.handle;
OS.SendMessage (handle, OS.TVM_SETITEM, 0, tvItem);
}
}
}
tvItem.hItem = hNewItem;
tvItem.state = OS.TVIS_SELECTED;
OS.SendMessage (handle, OS.TVM_SETITEM, 0, tvItem);
OS.SetWindowLongPtr (handle, OS.GWLP_WNDPROC, oldProc);
if ((wParam & OS.MK_SHIFT) != 0) {
RECT rect1 = new RECT ();
if (hAnchor == 0) hAnchor = hNewItem;
if (OS.TreeView_GetItemRect (handle, hAnchor, rect1, false)) {
RECT rect2 = new RECT ();
if (OS.TreeView_GetItemRect (handle, hNewItem, rect2, false)) {
int flags = rect1.top < rect2.top ? OS.TVGN_NEXTVISIBLE : OS.TVGN_PREVIOUSVISIBLE;
tvItem.state = OS.TVIS_SELECTED;
int /*long*/ hItem = tvItem.hItem = hAnchor;
OS.SendMessage (handle, OS.TVM_SETITEM, 0, tvItem);
while (hItem != hNewItem) {
tvItem.hItem = hItem;
OS.SendMessage (handle, OS.TVM_SETITEM, 0, tvItem);
hItem = OS.SendMessage (handle, OS.TVM_GETNEXTITEM, flags, hItem);
}
}
}
}
}
}
}
if ((wParam & OS.MK_SHIFT) == 0) hAnchor = hNewItem;
/* Issue notification */
if (!gestureCompleted) {
tvItem.hItem = hNewItem;
tvItem.mask = OS.TVIF_HANDLE | OS.TVIF_PARAM;
OS.SendMessage (handle, OS.TVM_GETITEM, 0, tvItem);
Event event = new Event ();
event.item = _getItem (tvItem.hItem, (int)/*64*/tvItem.lParam);
postEvent (SWT.Selection, event);
}
gestureCompleted = false;
/*
* Feature in Windows. Inside WM_LBUTTONDOWN and WM_RBUTTONDOWN,
* the widget starts a modal loop to determine if the user wants
* to begin a drag/drop operation or marquee select. Unfortunately,
* this modal loop eats the corresponding mouse up. The fix is to
* detect the cases when the modal loop has eaten the mouse up and
* issue a fake mouse up.
*/
if (dragStarted) {
sendDragEvent (1, OS.GET_X_LPARAM (lParam), OS.GET_Y_LPARAM (lParam));
} else {
int bits = OS.GetWindowLong (handle, OS.GWL_STYLE);
if ((bits & OS.TVS_DISABLEDRAGDROP) == 0) {
sendMouseEvent (SWT.MouseUp, 1, handle, OS.WM_LBUTTONUP, wParam, lParam);
}
}
dragStarted = false;
return new LRESULT (code);
}
LRESULT WM_MOUSEMOVE (int /*long*/ wParam, int /*long*/ lParam) {
Display display = this.display;
LRESULT result = super.WM_MOUSEMOVE (wParam, lParam);
if (result != null) return result;
if (itemToolTipHandle != 0) {
/*
* Bug in Windows. On some machines that do not have XBUTTONs,
* the MK_XBUTTON1 and OS.MK_XBUTTON2 bits are sometimes set,
* causing mouse capture to become stuck. The fix is to test
* for the extra buttons only when they exist.
*/
int mask = OS.MK_LBUTTON | OS.MK_MBUTTON | OS.MK_RBUTTON;
if (display.xMouse) mask |= OS.MK_XBUTTON1 | OS.MK_XBUTTON2;
if ((wParam & mask) == 0) {
int x = OS.GET_X_LPARAM (lParam);
int y = OS.GET_Y_LPARAM (lParam);
int [] index = new int [1];
TreeItem [] item = new TreeItem [1];
RECT [] cellRect = new RECT [1], itemRect = new RECT [1];
if (findCell (x, y, item, index, cellRect, itemRect)) {
/*
* Feature in Windows. When the new tool rectangle is
* set using TTM_NEWTOOLRECT and the tooltip is visible,
* Windows draws the tooltip right away and the sends
* WM_NOTIFY with TTN_SHOW. This means that the tooltip
* shows first at the wrong location and then moves to
* the right one. The fix is to hide the tooltip window.
*/
if (OS.SendMessage (itemToolTipHandle, OS.TTM_GETCURRENTTOOL, 0, 0) == 0) {
if (OS.IsWindowVisible (itemToolTipHandle)) {
OS.ShowWindow (itemToolTipHandle, OS.SW_HIDE);
}
}
TOOLINFO lpti = new TOOLINFO ();
lpti.cbSize = TOOLINFO.sizeof;
lpti.hwnd = handle;
lpti.uId = handle;
lpti.uFlags = OS.TTF_SUBCLASS | OS.TTF_TRANSPARENT;
lpti.left = cellRect [0].left;
lpti.top = cellRect [0].top;
lpti.right = cellRect [0].right;
lpti.bottom = cellRect [0].bottom;
OS.SendMessage (itemToolTipHandle, OS.TTM_NEWTOOLRECT, 0, lpti);
}
}
}
return result;
}
LRESULT WM_MOVE (int /*long*/ wParam, int /*long*/ lParam) {
if (ignoreResize) return null;
return super.WM_MOVE (wParam, lParam);
}
LRESULT WM_RBUTTONDOWN (int /*long*/ wParam, int /*long*/ lParam) {
/*
* Feature in Windows. The receiver uses WM_RBUTTONDOWN
* to initiate a drag/drop operation depending on how the
* user moves the mouse. If the user clicks the right button,
* without moving the mouse, the tree consumes the corresponding
* WM_RBUTTONUP. The fix is to avoid calling the window proc for
* the tree.
*/
Display display = this.display;
display.captureChanged = false;
if (!sendMouseEvent (SWT.MouseDown, 3, handle, OS.WM_RBUTTONDOWN, wParam, lParam)) {
if (!display.captureChanged && !isDisposed ()) {
if (OS.GetCapture () != handle) OS.SetCapture (handle);
}
return LRESULT.ZERO;
}
/*
* This code is intentionally commented.
*/
// if (OS.GetCapture () != handle) OS.SetCapture (handle);
setFocus ();
/*
* Feature in Windows. When the user selects a tree item
* with the right mouse button, the item remains selected
* only as long as the user does not release or move the
* mouse. As soon as this happens, the selection snaps
* back to the previous selection. This behavior can be
* observed in the Explorer but is not instantly apparent
* because the Explorer explicitly sets the selection when
* the user chooses a menu item. If the user cancels the
* menu, the selection snaps back. The fix is to avoid
* calling the window proc and do the selection ourselves.
* This behavior is consistent with the table.
*/
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) {
boolean fakeSelection = (style & SWT.FULL_SELECTION) != 0;
if (!fakeSelection) {
if (hooks (SWT.MeasureItem)) {
fakeSelection = hitTestSelection (lpht.hItem, lpht.x, lpht.y);
} else {
int flags = OS.TVHT_ONITEMICON | OS.TVHT_ONITEMLABEL;
fakeSelection = (lpht.flags & flags) != 0;
}
}
if (fakeSelection) {
if ((wParam & (OS.MK_CONTROL | OS.MK_SHIFT)) == 0) {
TVITEM tvItem = new TVITEM ();
tvItem.mask = OS.TVIF_HANDLE | OS.TVIF_STATE;
tvItem.stateMask = OS.TVIS_SELECTED;
tvItem.hItem = lpht.hItem;
OS.SendMessage (handle, OS.TVM_GETITEM, 0, tvItem);
if ((tvItem.state & OS.TVIS_SELECTED) == 0) {
ignoreSelect = true;
OS.SendMessage (handle, OS.TVM_SELECTITEM, OS.TVGN_CARET, 0);
ignoreSelect = false;
OS.SendMessage (handle, OS.TVM_SELECTITEM, OS.TVGN_CARET, lpht.hItem);
}
}
}
}
return LRESULT.ZERO;
}
LRESULT WM_PAINT (int /*long*/ wParam, int /*long*/ lParam) {
if (shrink && !ignoreShrink) {
/* Resize the item array to fit the last item */
int count = items.length - 1;
while (count >= 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)/*64*/OS.SendMessage (handle, OS.TVM_GETEXTENDEDSTYLE, 0, 0);
if ((exStyle & OS.TVS_EX_DOUBLEBUFFER) != 0) doubleBuffer = false;
}
if (doubleBuffer) {
GC gc = null;
int /*long*/ paintDC = 0;
PAINTSTRUCT ps = new PAINTSTRUCT ();
boolean hooksPaint = hooks (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) {
int /*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);
int /*long*/ hBitmap = OS.CreateCompatibleBitmap (paintDC, width, height);
int /*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.x = ps.left;
event.y = ps.top;
event.width = ps.right - ps.left;
event.height = 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);
}
LRESULT WM_PRINTCLIENT (int /*long*/ wParam, int /*long*/ lParam) {
LRESULT result = super.WM_PRINTCLIENT (wParam, lParam);
if (result != null) return result;
/*
* Feature in Windows. For some reason, when WM_PRINT is used
* to capture an image of a hierarchy that contains a tree with
* columns, the clipping that is used to stop the first column
* from drawing on top of subsequent columns stops the first
* column and the tree lines from drawing. This does not happen
* during WM_PAINT. The fix is to draw without clipping and
* then draw the rest of the columns on top. Since the drawing
* is happening in WM_PRINTCLIENT, the redrawing is not visible.
*/
printClient = true;
int /*long*/ code = callWindowProc (handle, OS.WM_PRINTCLIENT, wParam, lParam);
printClient = false;
return new LRESULT (code);
}
LRESULT WM_SETFOCUS (int /*long*/ wParam, int /*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) {
if (!OS.IsWinCE && OS.COMCTL32_MAJOR >= 6) {
if (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);
}
LRESULT WM_SETFONT (int /*long*/ wParam, int /*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.SendMessage (itemToolTipHandle, OS.WM_SETFONT, wParam, lParam);
}
if (headerToolTipHandle != 0) {
OS.SendMessage (headerToolTipHandle, OS.WM_SETFONT, wParam, lParam);
}
return result;
}
LRESULT WM_SETREDRAW (int /*long*/ wParam, int /*long*/ lParam) {
LRESULT result = super.WM_SETREDRAW (wParam, lParam);
if (result != null) return result;
/*
* 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.
*/
if (!OS.IsWinCE && OS.WIN32_VERSION >= OS.VERSION (6, 0)) {
int /*long*/ code = OS.DefWindowProc (handle, OS.WM_SETREDRAW, wParam, lParam);
return code == 0 ? LRESULT.ZERO : new LRESULT (code);
}
return result;
}
LRESULT WM_SIZE (int /*long*/ wParam, int /*long*/ lParam) {
/*
* 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) {
if (!OS.IsWinCE) 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);
}
LRESULT WM_SYSCOLORCHANGE (int /*long*/ wParam, int /*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;
}
LRESULT WM_VSCROLL (int /*long*/ wParam, int /*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;
}
LRESULT wmColorChild (int /*long*/ wParam, int /*long*/ lParam) {
if (findImageControl () != null) {
if (OS.COMCTL32_MAJOR < 6) {
return super.wmColorChild (wParam, lParam);
}
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;
}
LRESULT wmNotify (NMHDR hdr, int /*long*/ wParam, int /*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);
}
LRESULT wmNotifyChild (NMHDR hdr, int /*long*/ wParam, int /*long*/ lParam) {
switch (hdr.code) {
case OS.TVN_GETDISPINFOA:
case OS.TVN_GETDISPINFOW: {
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)/*64*/lptvdi.lParam] != null && items [(int)/*64*/lptvdi.lParam].cached) {
checkVisible = false;
}
}
}
if (checkVisible) {
if (drawCount != 0 || !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)/*64*/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)/*64*/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)/*64*/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) {
TCHAR buffer = new TCHAR (getCodePage (), string, false);
int byteCount = Math.min (buffer.length (), lptvdi.cchTextMax - 1) * TCHAR.sizeof;
OS.MoveMemory (lptvdi.pszText, buffer, byteCount);
OS.MoveMemory (lptvdi.pszText + byteCount, new byte [TCHAR.sizeof], TCHAR.sizeof);
lptvdi.cchTextMax = Math.min (lptvdi.cchTextMax, string.length () + 1);
}
}
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 ();
} else {
if (hooks (SWT.EraseItem) || hooks (SWT.PaintItem)) {
createItemToolTips ();
}
}
if (!customDraw && findImageControl () == null) {
if (OS.COMCTL32_MAJOR >= 6 && 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_ITEMCHANGINGA:
case OS.TVN_ITEMCHANGINGW: {
if (!OS.IsWinCE && OS.WIN32_VERSION >= OS.VERSION (6, 0)) {
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_SELCHANGINGA:
case OS.TVN_SELCHANGINGW: {
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_SELCHANGEDA:
case OS.TVN_SELCHANGEDW: {
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)/*64*/tvItem.lParam);
postEvent (SWT.Selection, event);
}
updateScrollBar ();
break;
}
case OS.TVN_ITEMEXPANDINGA:
case OS.TVN_ITEMEXPANDINGW: {
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 && drawCount == 0 && 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)/*64*/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.
*/
int /*long*/ hFirstItem = OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_CHILD, tvItem.hItem);
runExpanded = hFirstItem == 0;
}
if (!runExpanded) break;
//FALL THROUGH
}
case OS.TVN_ITEMEXPANDEDA:
case OS.TVN_ITEMEXPANDEDW: {
if ((style & SWT.VIRTUAL) != 0) style |= SWT.DOUBLE_BUFFERED;
if (hooks (SWT.EraseItem) || hooks (SWT.PaintItem)) style |= SWT.DOUBLE_BUFFERED;
if (findImageControl () != null && drawCount == 0 /*&& 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 (!OS.IsWinCE && OS.COMCTL32_MAJOR >= 6) {
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_BEGINDRAGA:
case OS.TVN_BEGINDRAGW:
case OS.TVN_BEGINRDRAGA:
case OS.TVN_BEGINRDRAGW: {
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;
}
dragStarted = true;
break;
}
case OS.NM_RECOGNIZEGESTURE: {
/*
* Feature in Pocket PC. The tree and table controls detect the tap
* and hold gesture by default. They send a GN_CONTEXTMENU message to show
* the popup menu. This default behaviour is unwanted on Pocket PC 2002
* when no menu has been set, as it still draws a red circle. The fix
* is to disable this default behaviour when no menu is set by returning
* TRUE when receiving the Pocket PC 2002 specific NM_RECOGNIZEGESTURE
* message.
*/
if (OS.IsPPC) {
boolean hasMenu = menu != null && !menu.isDisposed ();
if (!hasMenu && !hooks (SWT.MenuDetect)) return LRESULT.ONE;
}
break;
}
case OS.GN_CONTEXTMENU: {
if (OS.IsPPC) {
boolean hasMenu = menu != null && !menu.isDisposed ();
if (hasMenu || hooks (SWT.MenuDetect)) {
NMRGINFO nmrg = new NMRGINFO ();
OS.MoveMemory (nmrg, lParam, NMRGINFO.sizeof);
showMenu (nmrg.x, nmrg.y);
gestureCompleted = true;
return LRESULT.ONE;
}
}
break;
}
}
return super.wmNotifyChild (hdr, wParam, lParam);
}
LRESULT wmNotifyHeader (NMHDR hdr, int /*long*/ wParam, int /*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_BEGINTRACKW:
case OS.HDN_BEGINTRACKA:
case OS.HDN_DIVIDERDBLCLICKW:
case OS.HDN_DIVIDERDBLCLICKA: {
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;
switch (hdr.code) {
case OS.HDN_DIVIDERDBLCLICKW:
case OS.HDN_DIVIDERDBLCLICKA:
if (column != null) column.pack ();
}
break;
}
case OS.NM_RELEASEDCAPTURE: {
if (!ignoreColumnMove) {
int count = (int)/*64*/OS.SendMessage (hwndHeader, OS.HDM_GETITEMCOUNT, 0, 0);
for (int i=0; i<count; i++) {
TreeColumn column = columns [i];
column.updateToolTip (i);
}
updateImageList ();
}
ignoreColumnMove = false;
break;
}
case OS.HDN_BEGINDRAG: {
if (ignoreColumnMove) return LRESULT.ONE;
NMHEADER phdn = new NMHEADER ();
OS.MoveMemory (phdn, lParam, NMHEADER.sizeof);
if (phdn.iItem != -1) {
TreeColumn column = columns [phdn.iItem];
if (column != null && !column.getMoveable ()) {
ignoreColumnMove = true;
return LRESULT.ONE;
}
}
break;
}
case OS.HDN_ENDDRAG: {
NMHEADER phdn = new NMHEADER ();
OS.MoveMemory (phdn, lParam, NMHEADER.sizeof);
if (phdn.iItem != -1 && phdn.pitem != 0) {
HDITEM pitem = new HDITEM ();
OS.MoveMemory (pitem, phdn.pitem, HDITEM.sizeof);
if ((pitem.mask & OS.HDI_ORDER) != 0 && pitem.iOrder != -1) {
int count = (int)/*64*/OS.SendMessage (hwndHeader, OS.HDM_GETITEMCOUNT, 0, 0);
int [] order = new int [count];
OS.SendMessage (hwndHeader, OS.HDM_GETORDERARRAY, count, order);
int index = 0;
while (index < order.length) {
if (order [index] == phdn.iItem) break;
index++;
}
if (index == order.length) index = 0;
if (index == pitem.iOrder) break;
int start = Math.min (index, pitem.iOrder);
int end = Math.max (index, pitem.iOrder);
RECT rect = new RECT (), headerRect = new RECT ();
OS.GetClientRect (handle, rect);
OS.SendMessage (hwndHeader, OS.HDM_GETITEMRECT, order [start], headerRect);
rect.left = Math.max (rect.left, headerRect.left);
OS.SendMessage (hwndHeader, OS.HDM_GETITEMRECT, order [end], headerRect);
rect.right = Math.min (rect.right, headerRect.right);
OS.InvalidateRect (handle, rect, true);
ignoreColumnMove = false;
for (int i=start; i<=end; i++) {
TreeColumn column = columns [order [i]];
if (!column.isDisposed ()) {
column.postEvent (SWT.Move);
}
}
}
}
break;
}
case OS.HDN_ITEMCHANGINGW:
case OS.HDN_ITEMCHANGINGA: {
NMHEADER phdn = new NMHEADER ();
OS.MoveMemory (phdn, lParam, NMHEADER.sizeof);
if (phdn.pitem != 0) {
HDITEM newItem = new HDITEM ();
OS.MoveMemory (newItem, phdn.pitem, HDITEM.sizeof);
if ((newItem.mask & OS.HDI_WIDTH) != 0) {
RECT rect = new RECT ();
OS.GetClientRect (handle, rect);
HDITEM oldItem = new HDITEM ();
oldItem.mask = OS.HDI_WIDTH;
OS.SendMessage (hwndHeader, OS.HDM_GETITEM, phdn.iItem, oldItem);
int deltaX = newItem.cxy - oldItem.cxy;
RECT headerRect = new RECT ();
OS.SendMessage (hwndHeader, OS.HDM_GETITEMRECT, phdn.iItem, headerRect);
int gridWidth = linesVisible ? GRID_WIDTH : 0;
rect.left = headerRect.right - gridWidth;
int newX = rect.left + deltaX;
rect.right = Math.max (rect.right, rect.left + Math.abs (deltaX));
if (explorerTheme || (findImageControl () != null || hooks (SWT.EraseItem) || hooks (SWT.PaintItem))) {
OS.InvalidateRect (handle, rect, true);
OS.OffsetRect (rect, deltaX, 0);
OS.InvalidateRect (handle, rect, true);
} else {
int flags = OS.SW_INVALIDATE | OS.SW_ERASE;
OS.ScrollWindowEx (handle, deltaX, 0, rect, null, 0, null, flags);
}
if (OS.SendMessage (hwndHeader, OS.HDM_ORDERTOINDEX, phdn.iItem, 0) != 0) {
rect.left = headerRect.left;
rect.right = newX;
OS.InvalidateRect (handle, rect, true);
}
setScrollWidth ();
}
}
break;
}
case OS.HDN_ITEMCHANGEDW:
case OS.HDN_ITEMCHANGEDA: {
NMHEADER phdn = new NMHEADER ();
OS.MoveMemory (phdn, lParam, NMHEADER.sizeof);
if (phdn.pitem != 0) {
HDITEM pitem = new HDITEM ();
OS.MoveMemory (pitem, phdn.pitem, HDITEM.sizeof);
if ((pitem.mask & OS.HDI_WIDTH) != 0) {
if (ignoreColumnMove) {
if (!OS.IsWinCE && OS.WIN32_VERSION >= OS.VERSION (6, 0)) {
int flags = OS.RDW_UPDATENOW | OS.RDW_ALLCHILDREN;
OS.RedrawWindow (handle, null, 0, flags);
} else {
if ((style & SWT.DOUBLE_BUFFERED) == 0) {
int oldStyle = style;
style |= SWT.DOUBLE_BUFFERED;
OS.UpdateWindow (handle);
style = oldStyle;
}
}
}
TreeColumn column = columns [phdn.iItem];
if (column != null) {
column.updateToolTip (phdn.iItem);
column.sendEvent (SWT.Resize);
if (isDisposed ()) return LRESULT.ZERO;
int count = (int)/*64*/OS.SendMessage (hwndHeader, OS.HDM_GETITEMCOUNT, 0, 0);
TreeColumn [] newColumns = new TreeColumn [count];
System.arraycopy (columns, 0, newColumns, 0, count);
int [] order = new int [count];
OS.SendMessage (hwndHeader, OS.HDM_GETORDERARRAY, count, order);
boolean moved = false;
for (int i=0; i<count; i++) {
TreeColumn nextColumn = newColumns [order [i]];
if (moved && !nextColumn.isDisposed ()) {
nextColumn.updateToolTip (order [i]);
nextColumn.sendEvent (SWT.Move);
}
if (nextColumn == column) moved = true;
}
}
}
setScrollWidth ();
}
break;
}
case OS.HDN_ITEMCLICKW:
case OS.HDN_ITEMCLICKA: {
NMHEADER phdn = new NMHEADER ();
OS.MoveMemory (phdn, lParam, NMHEADER.sizeof);
TreeColumn column = columns [phdn.iItem];
if (column != null) {
column.postEvent (SWT.Selection);
}
break;
}
case OS.HDN_ITEMDBLCLICKW:
case OS.HDN_ITEMDBLCLICKA: {
NMHEADER phdn = new NMHEADER ();
OS.MoveMemory (phdn, lParam, NMHEADER.sizeof);
TreeColumn column = columns [phdn.iItem];
if (column != null) {
column.postEvent (SWT.DefaultSelection);
}
break;
}
}
return null;
}
LRESULT wmNotifyToolTip (NMHDR hdr, int /*long*/ wParam, int /*long*/ lParam) {
if (OS.IsWinCE) return null;
switch (hdr.code) {
case OS.NM_CUSTOMDRAW: {
NMTTCUSTOMDRAW nmcd = new NMTTCUSTOMDRAW ();
OS.MoveMemory (nmcd, lParam, NMTTCUSTOMDRAW.sizeof);
return wmNotifyToolTip (nmcd, lParam);
}
case OS.TTN_SHOW: {
LRESULT result = super.wmNotify (hdr, wParam, lParam);
if (result != null) return result;
int pos = OS.GetMessagePos ();
POINT pt = new POINT();
OS.POINTSTOPOINT (pt, pos);
OS.ScreenToClient (handle, pt);
int [] index = new int [1];
TreeItem [] item = new TreeItem [1];
RECT [] cellRect = new RECT [1], itemRect = new RECT [1];
if (findCell (pt.x, pt.y, item, index, cellRect, itemRect)) {
RECT toolRect = toolTipRect (itemRect [0]);
OS.MapWindowPoints (handle, 0, toolRect, 2);
int width = toolRect.right - toolRect.left;
int height = toolRect.bottom - toolRect.top;
int flags = OS.SWP_NOACTIVATE | OS.SWP_NOZORDER | OS.SWP_NOSIZE;
if (hooks (SWT.MeasureItem) || hooks (SWT.EraseItem) || hooks (SWT.PaintItem)) {
flags &= ~OS.SWP_NOSIZE;
}
SetWindowPos (itemToolTipHandle, 0, toolRect.left, toolRect.top, width, height, flags);
return LRESULT.ONE;
}
return result;
}
}
return null;
}
LRESULT wmNotifyToolTip (NMTTCUSTOMDRAW nmcd, int /*long*/ lParam) {
if (OS.IsWinCE) return null;
switch (nmcd.dwDrawStage) {
case OS.CDDS_PREPAINT: {
if (hooks (SWT.EraseItem) || hooks (SWT.PaintItem)) {
//TEMPORARY CODE
// nmcd.uDrawFlags |= OS.DT_CALCRECT;
// OS.MoveMemory (lParam, nmcd, NMTTCUSTOMDRAW.sizeof);
return new LRESULT (OS.CDRF_NOTIFYPOSTPAINT | OS.CDRF_NEWFONT);
}
break;
}
case OS.CDDS_POSTPAINT: {
if (OS.SendMessage (itemToolTipHandle, OS.TTM_GETCURRENTTOOL, 0, 0) != 0) {
TOOLINFO lpti = new TOOLINFO ();
lpti.cbSize = TOOLINFO.sizeof;
if (OS.SendMessage (itemToolTipHandle, OS.TTM_GETCURRENTTOOL, 0, lpti) != 0) {
int [] index = new int [1];
TreeItem [] item = new TreeItem [1];
RECT [] cellRect = new RECT [1], itemRect = new RECT [1];
int pos = OS.GetMessagePos ();
POINT pt = new POINT();
OS.POINTSTOPOINT (pt, pos);
OS.ScreenToClient (handle, pt);
if (findCell (pt.x, pt.y, item, index, cellRect, itemRect)) {
int /*long*/ hDC = OS.GetDC (handle);
int /*long*/ hFont = item [0].fontHandle (index [0]);
if (hFont == -1) hFont = OS.SendMessage (handle, OS.WM_GETFONT, 0, 0);
int /*long*/ oldFont = OS.SelectObject (hDC, hFont);
LRESULT result = null;
boolean drawForeground = true;
cellRect [0] = item [0].getBounds (index [0], true, true, false, false, false, hDC);
if (hooks (SWT.EraseItem)) {
Event event = sendEraseItemEvent (item [0], nmcd, index [0], cellRect [0], hFont);
if (isDisposed () || item [0].isDisposed ()) break;
if (event.doit) {
drawForeground = (event.detail & SWT.FOREGROUND) != 0;
} else {
drawForeground = false;
}
}
if (drawForeground) {
int nSavedDC = OS.SaveDC (nmcd.hdc);
int gridWidth = getLinesVisible () ? Table.GRID_WIDTH : 0;
RECT insetRect = toolTipInset (cellRect [0]);
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 = Font.win32_new (display, hFont);
GC gc = GC.win32_new (nmcd.hdc, data);
int x = cellRect [0].left + INSET;
if (index [0] != 0) x -= gridWidth;
Image image = item [0].getImage (index [0]);
if (image != null || index [0] == 0) {
Point size = getImageSize ();
RECT imageRect = item [0].getBounds (index [0], false, true, false, false, false, hDC);
if (imageList == null) size.x = imageRect.right - imageRect.left;
if (image != null) {
Rectangle rect = image.getBounds ();
gc.drawImage (image, rect.x, rect.y, rect.width, rect.height, x, imageRect.top, size.x, size.y);
x += INSET + (index [0] == 0 ? 1 : 0);
}
x += size.x;
} else {
x += INSET;
}
String string = item [0].getText (index [0]);
if (string != null) {
int flags = OS.DT_NOPREFIX | OS.DT_SINGLELINE | OS.DT_VCENTER;
TreeColumn column = columns != null ? columns [index [0]] : null;
if (column != null) {
if ((column.style & SWT.CENTER) != 0) flags |= OS.DT_CENTER;
if ((column.style & SWT.RIGHT) != 0) flags |= OS.DT_RIGHT;
}
TCHAR buffer = new TCHAR (getCodePage (), string, false);
RECT textRect = new RECT ();
OS.SetRect (textRect, x, cellRect [0].top, cellRect [0].right, cellRect [0].bottom);
OS.DrawText (nmcd.hdc, buffer, buffer.length (), textRect, flags);
}
gc.dispose ();
OS.RestoreDC (nmcd.hdc, nSavedDC);
}
if (hooks (SWT.PaintItem)) {
itemRect [0] = item [0].getBounds (index [0], true, true, false, false, false, hDC);
sendPaintItemEvent (item [0], nmcd, index[0], itemRect [0], hFont);
}
OS.SelectObject (hDC, oldFont);
OS.ReleaseDC (handle, hDC);
if (result != null) return result;
}
break;
}
}
break;
}
}
return null;
}
}