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