Mercurial > projects > dwt2
view org.eclipse.swt.win32.win32.x86/src/org/eclipse/swt/widgets/Tree.d @ 112:9f4c18c268b2
Update to compile and execute with dmd 2.052.
author | kntroh |
---|---|
date | Wed, 16 Mar 2011 21:53:53 +0900 |
parents | 2e09b0e6857a |
children |
line wrap: on
line source
/******************************************************************************* * Copyright (c) 2000, 2008 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * IBM Corporation - initial API and implementation * Port to the D programming language: * Frank Benoit <benoit@tionex.de> *******************************************************************************/ module org.eclipse.swt.widgets.Tree; import org.eclipse.swt.SWT; import org.eclipse.swt.SWTException; import org.eclipse.swt.events.SelectionEvent; import org.eclipse.swt.events.SelectionListener; import org.eclipse.swt.events.TreeListener; import org.eclipse.swt.graphics.Cursor; import org.eclipse.swt.graphics.Font; import org.eclipse.swt.graphics.GC; import org.eclipse.swt.graphics.GCData; import org.eclipse.swt.graphics.Image; import org.eclipse.swt.graphics.Point; import org.eclipse.swt.graphics.Rectangle; import org.eclipse.swt.internal.ImageList; import org.eclipse.swt.internal.win32.OS; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.TreeItem; import org.eclipse.swt.widgets.TreeColumn; import org.eclipse.swt.widgets.TypedListener; import org.eclipse.swt.widgets.Event; import org.eclipse.swt.widgets.Display; import org.eclipse.swt.widgets.Shell; import org.eclipse.swt.widgets.Control; import org.eclipse.swt.widgets.Listener; import org.eclipse.swt.widgets.Widget; import org.eclipse.swt.widgets.Table; import java.lang.all; /** * Instances of this class provide a selectable user interface object * that displays a hierarchy of items and issues notification when an * item in the hierarchy is selected. * <p> * The item children that may be added to instances of this class * must be of type <code>TreeItem</code>. * </p><p> * Style <code>VIRTUAL</code> is used to create a <code>Tree</code> whose * <code>TreeItem</code>s are to be populated by the client on an on-demand basis * instead of up-front. This can provide significant performance improvements for * trees that are very large or for which <code>TreeItem</code> population is * expensive (for example, retrieving values from an external source). * </p><p> * Here is an example of using a <code>Tree</code> with style <code>VIRTUAL</code>: * <code><pre> * final Tree tree = new Tree(parent, SWT.VIRTUAL | SWT.BORDER); * tree.setItemCount(20); * tree.addListener(SWT.SetData, new Listener() { * public void handleEvent(Event event) { * TreeItem item = (TreeItem)event.item; * TreeItem parentItem = item.getParentItem(); * String text = null; * if (parentItem is null) { * text = "node " + tree.indexOf(item); * } else { * text = parentItem.getText() + " - " + parentItem.indexOf(item); * } * item.setText(text); * System.out.println(text); * item.setItemCount(10); * } * }); * </pre></code> * </p><p> * Note that although this class is a subclass of <code>Composite</code>, * it does not normally make sense to add <code>Control</code> children to * it, or set a layout on it, unless implementing something like a cell * editor. * </p><p> * <dl> * <dt><b>Styles:</b></dt> * <dd>SINGLE, MULTI, CHECK, FULL_SELECTION, VIRTUAL, NO_SCROLL</dd> * <dt><b>Events:</b></dt> * <dd>Selection, DefaultSelection, Collapse, Expand, SetData, MeasureItem, EraseItem, PaintItem</dd> * </dl> * </p><p> * Note: Only one of the styles SINGLE and MULTI may be specified. * </p><p> * IMPORTANT: This class is <em>not</em> intended to be subclassed. * </p> * * @see <a href="http://www.eclipse.org/swt/snippets/#tree">Tree, TreeItem, TreeColumn snippets</a> * @see <a href="http://www.eclipse.org/swt/examples.php">SWT Example: ControlExample</a> * @see <a href="http://www.eclipse.org/swt/">Sample code and further information</a> */ public class Tree : Composite { alias Composite.computeSize computeSize; alias Composite.setBackgroundImage setBackgroundImage; alias Composite.setBounds setBounds; alias Composite.setCursor setCursor; alias Composite.sort sort; TreeItem [] items; TreeColumn [] columns; int columnCount; ImageList imageList, headerImageList; TreeItem currentItem; TreeColumn sortColumn; RECT* focusRect; HWND hwndParent, hwndHeader; HANDLE hAnchor, hInsert, hSelect; int lastID; HANDLE hFirstIndexOf, hLastIndexOf; int lastIndexOf, itemCount, sortDirection; bool dragStarted, gestureCompleted, insertAfter, shrink, ignoreShrink; bool ignoreSelect, ignoreExpand, ignoreDeselect, ignoreResize; bool lockSelection, oldSelected, newSelected, ignoreColumnMove; bool linesVisible, customDraw, printClient, painted, ignoreItemHeight; bool ignoreCustomDraw, ignoreDrawForeground, ignoreDrawBackground, ignoreDrawFocus; bool ignoreDrawSelection, ignoreDrawHot, ignoreFullSelection, explorerTheme; int scrollWidth, selectionForeground; HANDLE headerToolTipHandle, itemToolTipHandle; static const int INSET = 3; static const int GRID_WIDTH = 1; static const int SORT_WIDTH = 10; static const int HEADER_MARGIN = 12; static const int HEADER_EXTRA = 3; static const int INCREMENT = 5; static const int EXPLORER_EXTRA = 2; static const int DRAG_IMAGE_SIZE = 301; static const bool EXPLORER_THEME = true; private static /+const+/ WNDPROC TreeProc; static const TCHAR[] TreeClass = OS.WC_TREEVIEW; private static /+const+/ WNDPROC HeaderProc; static const TCHAR[] HeaderClass = OS.WC_HEADER; private static bool static_this_completed = false; private static void static_this() { if( static_this_completed ){ return; } synchronized { if( static_this_completed ){ return; } WNDCLASS lpWndClass; OS.GetClassInfo (null, TreeClass.ptr, &lpWndClass); TreeProc = lpWndClass.lpfnWndProc; OS.GetClassInfo (null, HeaderClass.ptr, &lpWndClass); HeaderProc = lpWndClass.lpfnWndProc; static_this_completed = true; } } private static Tree sThis; /** * Constructs a new instance of this class given its parent * and a style value describing its behavior and appearance. * <p> * The style value is either one of the style constants defined in * class <code>SWT</code> which is applicable to instances of this * class, or must be built by <em>bitwise OR</em>'ing together * (that is, using the <code>int</code> "|" operator) two or more * of those <code>SWT</code> style constants. The class description * lists the style constants that are applicable to the class. * Style bits are also inherited from superclasses. * </p> * * @param parent a composite control which will be the parent of the new instance (cannot be null) * @param style the style of control to construct * * @exception IllegalArgumentException <ul> * <li>ERROR_NULL_ARGUMENT - if the parent is null</li> * </ul> * @exception SWTException <ul> * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the parent</li> * <li>ERROR_INVALID_SUBCLASS - if this class is not an allowed subclass</li> * </ul> * * @see SWT#SINGLE * @see SWT#MULTI * @see SWT#CHECK * @see SWT#FULL_SELECTION * @see SWT#VIRTUAL * @see SWT#NO_SCROLL * @see Widget#checkSubclass * @see Widget#getStyle */ public this (Composite parent, int style) { static_this(); 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) is 0) { style |= SWT.H_SCROLL | SWT.V_SCROLL; } /* * Note: Windows only supports TVS_NOSCROLL and TVS_NOHSCROLL. */ if ((style & SWT.H_SCROLL) !is 0 && (style & SWT.V_SCROLL) is 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) !is 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 is SWT.MeasureItem) { /* * This code is intentionally commented. */ // if (explorerTheme) { // int bits1 = (int)/*64*/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) !is 0) { if (eventType !is SWT.MeasureItem) { if (!explorerTheme) bits &= ~OS.TVS_FULLROWSELECT; } } if (bits !is 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 = OS.SendMessage (handle, OS.TVM_GETCOUNT, 0, 0); if (count !is 0 && (bits & OS.TVS_NOHSCROLL) !is 0) { static if (!OS.IsWinCE) OS.ShowScrollBar (handle, OS.SB_HORZ, false); } } break; } default: } } TreeItem _getItem (HANDLE hItem) { TVITEM tvItem; tvItem.mask = OS.TVIF_HANDLE | OS.TVIF_PARAM; tvItem.hItem = cast(HTREEITEM)hItem; if (OS.SendMessage (handle, OS.TVM_GETITEM, 0, &tvItem) !is 0) { return _getItem (tvItem.hItem, tvItem.lParam); } return null; } TreeItem _getItem (HANDLE hItem, int id) { if ((style & SWT.VIRTUAL) is 0) return items [id]; return id !is -1 ? items [id] : new TreeItem (this, SWT.NONE, cast(HANDLE)-1, cast(HANDLE)-1, hItem); } void _setBackgroundPixel (int newPixel) { int oldPixel = OS.SendMessage (handle, OS.TVM_GETBKCOLOR, 0, 0); if (oldPixel !is 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 !is -1) { OS.SendMessage (handle, OS.TVM_SETBKCOLOR, 0, -1); } /* Set the background color */ OS.SendMessage (handle, OS.TVM_SETBKCOLOR, 0, newPixel); /* * Feature in Windows. When TVM_SETBKCOLOR is used to * set the background color of a tree, the plus/minus * animation draws badly. The fix is to clear the effect. */ if (explorerTheme) { int bits2 = OS.SendMessage (handle, OS.TVM_GETEXTENDEDSTYLE, 0, 0); if (newPixel is -1 && findImageControl () is 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) !is 0) setCheckboxImageList (); } } /** * Adds the listener to the collection of listeners who will * be notified when the user changes the receiver's selection, by sending * it one of the messages defined in the <code>SelectionListener</code> * interface. * <p> * When <code>widgetSelected</code> is called, the item field of the event object is valid. * If the receiver has the <code>SWT.CHECK</code> style and the check selection changes, * the event object detail field contains the value <code>SWT.CHECK</code>. * <code>widgetDefaultSelected</code> is typically called when an item is double-clicked. * The item field of the event object is valid for default selection, but the detail field is not used. * </p> * * @param listener the listener which should be notified when the user changes the receiver's selection * * @exception IllegalArgumentException <ul> * <li>ERROR_NULL_ARGUMENT - if the listener is null</li> * </ul> * @exception SWTException <ul> * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> * </ul> * * @see SelectionListener * @see #removeSelectionListener * @see SelectionEvent */ public void addSelectionListener(SelectionListener listener) { checkWidget (); if (listener is null) error (SWT.ERROR_NULL_ARGUMENT); TypedListener typedListener = new TypedListener (listener); addListener (SWT.Selection, typedListener); addListener (SWT.DefaultSelection, typedListener); } /** * Adds the listener to the collection of listeners who will * be notified when an item in the receiver is expanded or collapsed * by sending it one of the messages defined in the <code>TreeListener</code> * interface. * * @param listener the listener which should be notified * * @exception IllegalArgumentException <ul> * <li>ERROR_NULL_ARGUMENT - if the listener is null</li> * </ul> * @exception SWTException <ul> * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> * </ul> * * @see TreeListener * @see #removeTreeListener */ public void addTreeListener(TreeListener listener) { checkWidget (); if (listener is null) error (SWT.ERROR_NULL_ARGUMENT); TypedListener typedListener = new TypedListener (listener); addListener (SWT.Expand, typedListener); addListener (SWT.Collapse, typedListener); } override HWND borderHandle () { return hwndParent !is null ? hwndParent : handle; } LRESULT CDDS_ITEMPOSTPAINT (NMTVCUSTOMDRAW* nmcd, int wParam, int lParam) { if (ignoreCustomDraw) return null; if (nmcd.nmcd.rc.left is nmcd.nmcd.rc.right) return new LRESULT (OS.CDRF_DODEFAULT); auto hDC = nmcd.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 is 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.nmcd.rc.left >= nmcd.nmcd.rc.right || nmcd.nmcd.rc.top >= nmcd.nmcd.rc.bottom) return null; if (!OS.IsWindowVisible (handle)) return null; if ((style & SWT.FULL_SELECTION) !is 0 || findImageControl () !is null || ignoreDrawSelection || explorerTheme) { OS.SetBkMode (hDC, OS.TRANSPARENT); } bool selected = isItemSelected (nmcd); bool hot = explorerTheme && (nmcd.nmcd.uItemState & OS.CDIS_HOT) !is 0; if (OS.IsWindowEnabled (handle)) { if (explorerTheme) { int bits = OS.GetWindowLong (handle, OS.GWL_STYLE); if ((bits & OS.TVS_TRACKSELECT) !is 0) { if ((style & SWT.FULL_SELECTION) !is 0 && (selected || hot)) { OS.SetTextColor (hDC, OS.GetSysColor (OS.COLOR_WINDOWTEXT)); } else { OS.SetTextColor (hDC, getForegroundPixel ()); } } } } int [] order = null; RECT clientRect; OS.GetClientRect (scrolledHandle (), &clientRect); if (hwndHeader !is null) { OS.MapWindowPoints (hwndParent, handle, cast(POINT*) &clientRect, 2); if (columnCount !is 0) { order = new int [columnCount]; OS.SendMessage (hwndHeader, OS.HDM_GETORDERARRAY, columnCount, cast(int) order.ptr); } } int sortIndex = -1, clrSortBk = -1; if (OS.COMCTL32_MAJOR >= 6 && OS.IsAppThemed ()) { if (sortColumn !is null && sortDirection !is SWT.NONE) { if (findImageControl () is null) { sortIndex = indexOf (sortColumn); clrSortBk = getSortColumnPixel (); } } } int x = 0; Point size = null; for (int i=0; i<Math.max (1, columnCount); i++) { int index = order is null ? i : order [i], width = nmcd.nmcd.rc.right - nmcd.nmcd.rc.left; if (columnCount > 0 && hwndHeader !is null) { HDITEM hdItem; hdItem.mask = OS.HDI_WIDTH; OS.SendMessage (hwndHeader, OS.HDM_GETITEM, index, &hdItem); width = hdItem.cxy; } if (i is 0) { if ((style & SWT.FULL_SELECTION) !is 0) { bool clear = !explorerTheme && !ignoreDrawSelection && findImageControl () is null; if (clear || (selected && !ignoreDrawSelection) || (hot && !ignoreDrawHot)) { bool draw = true; RECT pClipRect; OS.SetRect (&pClipRect, width, nmcd.nmcd.rc.top, nmcd.nmcd.rc.right, nmcd.nmcd.rc.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 !is null) { HDITEM hdItem; hdItem.mask = OS.HDI_WIDTH; OS.SendMessage (hwndHeader, OS.HDM_GETITEM, index, &hdItem); pClipRect.right = Math.min (pClipRect.right, nmcd.nmcd.rc.left + hdItem.cxy); } } RECT pRect; OS.SetRect (&pRect, nmcd.nmcd.rc.left, nmcd.nmcd.rc.top, nmcd.nmcd.rc.right, nmcd.nmcd.rc.bottom); if (columnCount > 0 && hwndHeader !is null) { int totalWidth = 0; HDITEM hdItem; hdItem.mask = OS.HDI_WIDTH; for (int j=0; j<columnCount; j++) { OS.SendMessage (hwndHeader, OS.HDM_GETITEM, j, &hdItem); totalWidth += hdItem.cxy; } if (totalWidth > clientRect.right - clientRect.left) { pRect.left = 0; pRect.right = totalWidth; } else { pRect.left = clientRect.left; pRect.right = clientRect.right; } } draw = false; auto hTheme = OS.OpenThemeData (handle, cast(TCHAR*) Display.TREEVIEW); int iStateId = selected ? OS.TREIS_SELECTED : OS.TREIS_HOT; if (OS.GetFocus () !is 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; bool drawItem = true, drawText = true, drawImage = true, drawBackground_ = false; if (i is 0) { drawItem = drawImage = drawText = false; if (findImageControl () !is null) { if (explorerTheme) { if (OS.IsWindowEnabled (handle) && !hooks (SWT.EraseItem)) { Image image = null; if (index is 0) { image = item.image; } else { Image [] images = item.images; if (images !is null) image = images [index]; } if (image !is null) { Rectangle bounds = image.getBounds (); if (size is null) size = getImageSize (); if (!ignoreDrawForeground) { GCData data = new GCData(); data.device = display; GC gc = GC.win32_new (hDC, data); RECT* iconRect = item.getBounds (index, false, true, false, false, true, hDC); gc.setClipping (iconRect.left, iconRect.top, iconRect.right - iconRect.left, iconRect.bottom - iconRect.top); gc.drawImage (image, 0, 0, bounds.width, bounds.height, iconRect.left, iconRect.top, size.x, size.y); OS.SelectClipRgn (hDC, null); 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) !is 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.nmcd.rc.top, x + width, nmcd.nmcd.rc.bottom); backgroundRect = rect; } int clrText = -1, clrTextBk = -1; auto hFont = item.fontHandle (index); if (selectionForeground !is -1) clrText = selectionForeground; if (OS.IsWindowEnabled (handle)) { bool drawForeground = false; if (selected) { if (i !is 0 && (style & SWT.FULL_SELECTION) is 0) { OS.SetTextColor (hDC, getForegroundPixel ()); OS.SetBkColor (hDC, getBackgroundPixel ()); drawForeground = drawBackground_ = true; } } else { drawForeground = drawBackground_ = true; } if (drawForeground) { clrText = item.cellForeground !is null ? item.cellForeground [index] : -1; if (clrText is -1) clrText = item.foreground; } if (drawBackground_) { clrTextBk = item.cellBackground !is null ? item.cellBackground [index] : -1; if (clrTextBk is -1) clrTextBk = item.background; if (clrTextBk is -1 && index is sortIndex) clrTextBk = clrSortBk; } } else { if (clrTextBk is -1 && index is sortIndex) { drawBackground_ = true; clrTextBk = clrSortBk; } } if (explorerTheme) { if (selected || (nmcd.nmcd.uItemState & OS.CDIS_HOT) !is 0) { if ((style & SWT.FULL_SELECTION) !is 0) { drawBackground_ = false; } else { if (i is 0) { drawBackground_ = false; if (!hooks (SWT.EraseItem)) drawText = false; } } } } if (drawItem) { if (i !is 0) { if (hooks (SWT.MeasureItem)) { sendMeasureItemEvent (item, index, hDC); if (isDisposed () || item.isDisposed ()) break; } if (hooks (SWT.EraseItem)) { RECT* cellRect = item.getBounds (index, true, true, true, true, true, hDC); int nSavedDC = OS.SaveDC (hDC); GCData data = new GCData (); data.device = display; data.foreground = OS.GetTextColor (hDC); data.background = OS.GetBkColor (hDC); if (!selected || (style & SWT.FULL_SELECTION) is 0) { if (clrText !is -1) data.foreground = clrText; if (clrTextBk !is -1) data.background = clrTextBk; } data.font = item.getFont (index); data.uiState = 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 !is -1) event.detail |= SWT.BACKGROUND; if ((style & SWT.FULL_SELECTION) !is 0) { if (hot) event.detail |= SWT.HOT; if (selected) event.detail |= SWT.SELECTED; if (!explorerTheme) { //if ((nmcd.uItemState & OS.CDIS_FOCUS) !is 0) { if (OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_CARET, 0) is nmcd.nmcd.dwItemSpec) { if (handle is OS.GetFocus ()) { int uiState = OS.SendMessage (handle, OS.WM_QUERYUISTATE, 0, 0); if ((uiState & OS.UISF_HIDEFOCUS) is 0) event.detail |= SWT.FOCUSED; } } } } event.x = cellRect.left; event.y = cellRect.top; event.width = cellRect.right - cellRect.left; event.height = cellRect.bottom - cellRect.top; gc.setClipping (event.x, event.y, event.width, event.height); sendEvent (SWT.EraseItem, event); event.gc = null; int newTextClr = data.foreground; gc.dispose (); OS.RestoreDC (hDC, nSavedDC); if (isDisposed () || item.isDisposed ()) break; if (event.doit) { ignoreDrawForeground = (event.detail & SWT.FOREGROUND) is 0; ignoreDrawBackground = (event.detail & SWT.BACKGROUND) is 0; if ((style & SWT.FULL_SELECTION) !is 0) { ignoreDrawSelection = (event.detail & SWT.SELECTED) is 0; ignoreDrawFocus = (event.detail & SWT.FOCUSED) is 0; ignoreDrawHot = (event.detail & SWT.HOT) is 0; } } else { ignoreDrawForeground = ignoreDrawBackground = ignoreDrawSelection = ignoreDrawFocus = ignoreDrawHot = true; } if (selected && ignoreDrawSelection) ignoreDrawHot = true; if ((style & SWT.FULL_SELECTION) !is 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 is OS.GetFocus () || display.getHighContrast ()) && OS.IsWindowEnabled (handle)) { clrTextBk = OS.GetSysColor (OS.COLOR_HIGHLIGHT); } else { clrTextBk = OS.GetSysColor (OS.COLOR_3DFACE); } if (!ignoreFullSelection && index is columnCount - 1) { RECT* selectionRect = new RECT (); OS.SetRect (selectionRect, backgroundRect.left, backgroundRect.top, nmcd.nmcd.rc.right, backgroundRect.bottom); backgroundRect = selectionRect; } } else { RECT pRect; OS.SetRect (&pRect, nmcd.nmcd.rc.left, nmcd.nmcd.rc.top, nmcd.nmcd.rc.right, nmcd.nmcd.rc.bottom); if (columnCount > 0 && hwndHeader !is null) { int totalWidth = 0; HDITEM hdItem; hdItem.mask = OS.HDI_WIDTH; for (int j=0; j<columnCount; j++) { OS.SendMessage (hwndHeader, OS.HDM_GETITEM, j, &hdItem); totalWidth += hdItem.cxy; } if (totalWidth > clientRect.right - clientRect.left) { pRect.left = 0; pRect.right = totalWidth; } else { pRect.left = clientRect.left; pRect.right = clientRect.right; } if (index is columnCount - 1) { RECT* selectionRect = new RECT (); OS.SetRect (selectionRect, backgroundRect.left, backgroundRect.top, pRect.right, backgroundRect.bottom); backgroundRect = selectionRect; } } auto hTheme = OS.OpenThemeData (handle, cast(TCHAR*) Display.TREEVIEW); int iStateId = selected ? OS.TREIS_SELECTED : OS.TREIS_HOT; if (OS.GetFocus () !is 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 is -1 && OS.IsWindowEnabled (handle)) { Control control = findBackgroundControl (); if (control is null) control = this; clrTextBk = control.getBackgroundPixel (); } } } } } } if (selectionForeground !is -1) clrText = selectionForeground; } if (!ignoreDrawBackground) { if (clrTextBk !is -1) { if (drawBackground_) fillBackground (hDC, clrTextBk, backgroundRect); } else { Control control = findImageControl (); if (control !is null) { if (i is 0) { int right = Math.min (rect.right, width); OS.SetRect (rect, rect.left, rect.top, right, rect.bottom); if (drawBackground_) fillImageBackground (hDC, control, rect); } else { if (drawBackground_) fillImageBackground (hDC, control, rect); } } } } rect.left += INSET - 1; if (drawImage) { Image image = null; if (index is 0) { image = item.image; } else { Image [] images = item.images; if (images !is null) image = images [index]; } int inset = i !is 0 ? INSET : 0; int offset = i !is 0 ? INSET : INSET + 2; if (image !is null) { Rectangle bounds = image.getBounds (); if (size is null) size = getImageSize (); if (!ignoreDrawForeground) { //int y1 = rect.top + (index is 0 ? (getItemHeight () - size.y) / 2 : 0); int y1 = rect.top; int x1 = Math.max (rect.left, rect.left - inset + 1); GCData data = new GCData(); data.device = display; GC gc = GC.win32_new (hDC, data); gc.setClipping (x1, rect.top, rect.right - x1, rect.bottom - rect.top); gc.drawImage (image, 0, 0, bounds.width, bounds.height, x1, y1, size.x, size.y); OS.SelectClipRgn (hDC, null); gc.dispose (); } OS.SetRect (rect, rect.left + size.x + offset, rect.top, rect.right - inset, rect.bottom); } else { if (i is 0) { if (OS.SendMessage (handle, OS.TVM_GETIMAGELIST, OS.TVSIL_NORMAL, 0) !is 0) { if (size is 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 is 0) { string = item.text; } else { String [] strings = item.strings; if (strings !is null) string = strings [index]; } if (string !is null) { if (hFont !is cast(HFONT)-1) hFont = cast(HFONT)OS.SelectObject (hDC, hFont); if (clrText !is -1) clrText = OS.SetTextColor (hDC, clrText); if (clrTextBk !is -1) clrTextBk = OS.SetBkColor (hDC, clrTextBk); int flags = OS.DT_NOPREFIX | OS.DT_SINGLELINE | OS.DT_VCENTER; if (i !is 0) flags |= OS.DT_ENDELLIPSIS; TreeColumn column = columns !is null ? columns [index] : null; if (column !is null) { if ((column.style & SWT.CENTER) !is 0) flags |= OS.DT_CENTER; if ((column.style & SWT.RIGHT) !is 0) flags |= OS.DT_RIGHT; } StringT buffer = StrToTCHARs (getCodePage (), string, false); if (!ignoreDrawForeground) OS.DrawText (hDC, buffer.ptr, buffer.length, rect, flags); OS.DrawText (hDC, buffer.ptr, buffer.length, rect, flags | OS.DT_CALCRECT); if (hFont !is cast(HFONT)-1) hFont = cast(HFONT)OS.SelectObject (hDC, hFont); if (clrText !is -1) clrText = OS.SetTextColor (hDC, clrText); if (clrTextBk !is -1) clrTextBk = OS.SetBkColor (hDC, clrTextBk); } } } } if (selectionForeground !is -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) !is 0) { if (selectionForeground !is -1) data.foreground = selectionForeground; } else { if (clrText !is -1) data.foreground = clrText; if (clrTextBk !is -1) data.background = clrTextBk; } data.uiState = 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 !is -1) event.detail |= SWT.BACKGROUND; if (hot) event.detail |= SWT.HOT; if (selected && (i is 0 /*nmcd.iSubItem is 0*/ || (style & SWT.FULL_SELECTION) !is 0)) { event.detail |= SWT.SELECTED; } if (!explorerTheme) { //if ((nmcd.uItemState & OS.CDIS_FOCUS) !is 0) { if (OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_CARET, 0) is nmcd.nmcd.dwItemSpec) { if (i is 0 /*nmcd.iSubItem is 0*/ || (style & SWT.FULL_SELECTION) !is 0) { if (handle is OS.GetFocus ()) { int uiState = OS.SendMessage (handle, OS.WM_QUERYUISTATE, 0, 0); if ((uiState & OS.UISF_HIDEFOCUS) is 0) event.detail |= SWT.FOCUSED; } } } } event.x = itemRect.left; event.y = itemRect.top; event.width = itemRect.right - itemRect.left; event.height = itemRect.bottom - itemRect.top; RECT* cellRect = item.getBounds (index, true, true, true, true, true, hDC); int cellWidth = cellRect.right - cellRect.left; int cellHeight = cellRect.bottom - cellRect.top; gc.setClipping (cellRect.left, cellRect.top, cellWidth, cellHeight); sendEvent (SWT.PaintItem, event); 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) !is 0) { if (columnCount !is 0) { HDITEM hdItem; hdItem.mask = OS.HDI_WIDTH; OS.SendMessage (hwndHeader, OS.HDM_GETITEM, 0, &hdItem); RECT rect; OS.SetRect (&rect, nmcd.nmcd.rc.left + hdItem.cxy, nmcd.nmcd.rc.top, nmcd.nmcd.rc.right, nmcd.nmcd.rc.bottom); OS.DrawEdge (hDC, &rect, OS.BDR_SUNKENINNER, OS.BF_BOTTOM); } } RECT rect; OS.SetRect (&rect, nmcd.nmcd.rc.left, nmcd.nmcd.rc.top, nmcd.nmcd.rc.right, nmcd.nmcd.rc.bottom); OS.DrawEdge (hDC, &rect, OS.BDR_SUNKENINNER, OS.BF_BOTTOM); } if (!ignoreDrawFocus && focusRect !is null) { OS.DrawFocusRect (hDC, focusRect); focusRect = null; } else { if (!explorerTheme) { if (handle is OS.GetFocus ()) { int uiState = OS.SendMessage (handle, OS.WM_QUERYUISTATE, 0, 0); if ((uiState & OS.UISF_HIDEFOCUS) is 0) { auto hItem = cast(HANDLE) OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_CARET, 0); if (hItem is item.handle) { if (!ignoreDrawFocus && findImageControl () !is null) { if ((style & SWT.FULL_SELECTION) !is 0) { RECT focusRect; OS.SetRect (&focusRect, 0, nmcd.nmcd.rc.top, clientRect.right + 1, nmcd.nmcd.rc.bottom); OS.DrawFocusRect (hDC, &focusRect); } else { int index = 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, null); } } } } } } } return new LRESULT (OS.CDRF_DODEFAULT); } LRESULT CDDS_ITEMPREPAINT (NMTVCUSTOMDRAW* nmcd, int wParam, int 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 is null) return null; auto hDC = nmcd.nmcd.hdc; int index = hwndHeader !is null ? OS.SendMessage (hwndHeader, OS.HDM_ORDERTOINDEX, 0, 0) : 0; auto hFont = item.fontHandle (index); if (hFont !is cast(HFONT)-1) OS.SelectObject (hDC, hFont); if (ignoreCustomDraw || nmcd.nmcd.rc.left is nmcd.nmcd.rc.right) { return new LRESULT (hFont is cast(HFONT)-1 ? OS.CDRF_DODEFAULT : OS.CDRF_NEWFONT); } RECT* clipRect = null; if (columnCount !is 0) { bool clip = !printClient; if (!OS.IsWinCE && OS.WIN32_VERSION >= OS.VERSION (6, 0)) { clip = true; } if (clip) { clipRect = new RECT (); HDITEM hdItem; hdItem.mask = OS.HDI_WIDTH; OS.SendMessage (hwndHeader, OS.HDM_GETITEM, index, &hdItem); OS.SetRect (clipRect, nmcd.nmcd.rc.left, nmcd.nmcd.rc.top, nmcd.nmcd.rc.left + hdItem.cxy, nmcd.nmcd.rc.bottom); } } int clrText = -1, clrTextBk = -1; if (OS.IsWindowEnabled (handle)) { clrText = item.cellForeground !is null ? item.cellForeground [index] : -1; if (clrText is -1) clrText = item.foreground; clrTextBk = item.cellBackground !is null ? item.cellBackground [index] : -1; if (clrTextBk is -1) clrTextBk = item.background; } int clrSortBk = -1; if (OS.COMCTL32_MAJOR >= 6 && OS.IsAppThemed ()) { if (sortColumn !is null && sortDirection !is SWT.NONE) { if (findImageControl () is null) { if (indexOf (sortColumn) is index) { clrSortBk = getSortColumnPixel (); if (clrTextBk is -1) clrTextBk = clrSortBk; } } } } bool selected = isItemSelected (nmcd); bool hot = explorerTheme && (nmcd.nmcd.uItemState & OS.CDIS_HOT) !is 0; bool focused = explorerTheme && (nmcd.nmcd.uItemState & OS.CDIS_FOCUS) !is 0; if (OS.IsWindowVisible (handle) && nmcd.nmcd.rc.left < nmcd.nmcd.rc.right && nmcd.nmcd.rc.top < nmcd.nmcd.rc.bottom) { if (hFont !is cast(HFONT)-1) OS.SelectObject (hDC, hFont); if (linesVisible) { RECT rect; OS.SetRect (&rect, nmcd.nmcd.rc.left, nmcd.nmcd.rc.top, nmcd.nmcd.rc.right, nmcd.nmcd.rc.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; if (hooks (SWT.MeasureItem)) { measureEvent = sendMeasureItemEvent (item, index, hDC); if (isDisposed () || item.isDisposed ()) return null; } selectionForeground = -1; ignoreDrawForeground = ignoreDrawBackground = ignoreDrawSelection = ignoreDrawFocus = ignoreDrawHot = ignoreFullSelection = false; if (hooks (SWT.EraseItem)) { RECT rect; OS.SetRect (&rect, nmcd.nmcd.rc.left, nmcd.nmcd.rc.top, nmcd.nmcd.rc.right, nmcd.nmcd.rc.bottom); RECT* cellRect = item.getBounds (index, true, true, true, true, true, hDC); if (clrSortBk !is -1) { drawBackground (hDC, cellRect, clrSortBk); } else { if (OS.IsWindowEnabled (handle) || findImageControl () !is 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 !is -1) data.foreground = clrText; if (clrTextBk !is -1) data.background = clrTextBk; } data.uiState = 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 !is -1) event.detail |= SWT.BACKGROUND; if (hot) event.detail |= SWT.HOT; if (selected) event.detail |= SWT.SELECTED; if (!explorerTheme) { //if ((nmcd.uItemState & OS.CDIS_FOCUS) !is 0) { if (OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_CARET, 0) is nmcd.nmcd.dwItemSpec) { if (handle is OS.GetFocus ()) { int uiState = OS.SendMessage (handle, OS.WM_QUERYUISTATE, 0, 0); if ((uiState & OS.UISF_HIDEFOCUS) is 0) { focused = true; event.detail |= SWT.FOCUSED; } } } } event.x = cellRect.left; event.y = cellRect.top; event.width = cellRect.right - cellRect.left; event.height = cellRect.bottom - cellRect.top; gc.setClipping (event.x, event.y, event.width, event.height); sendEvent (SWT.EraseItem, event); event.gc = null; int newTextClr = data.foreground; gc.dispose (); OS.RestoreDC (hDC, nSavedDC); if (isDisposed () || item.isDisposed ()) return null; if (event.doit) { ignoreDrawForeground = (event.detail & SWT.FOREGROUND) is 0; ignoreDrawBackground = (event.detail & SWT.BACKGROUND) is 0; ignoreDrawSelection = (event.detail & SWT.SELECTED) is 0; ignoreDrawFocus = (event.detail & SWT.FOCUSED) is 0; ignoreDrawHot = (event.detail & SWT.HOT) is 0; } else { ignoreDrawForeground = ignoreDrawBackground = ignoreDrawSelection = ignoreDrawFocus = ignoreDrawHot = true; } if (selected && ignoreDrawSelection) ignoreDrawHot = true; if (!ignoreDrawBackground && clrTextBk !is -1) { bool draw = !selected && !hot; if (!explorerTheme && selected) draw = !ignoreDrawSelection; if (draw) { if (columnCount is 0) { if ((style & SWT.FULL_SELECTION) !is 0) { fillBackground (hDC, clrTextBk, &rect); } else { RECT* textRect = item.getBounds (index, true, false, false, false, true, hDC); if (measureEvent !is null) { textRect.right = Math.min (cellRect.right, measureEvent.x + measureEvent.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) is 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 !is null) { pRect.right = Math.min (pClipRect.right, measureEvent.x + measureEvent.width); } else { pRect.right += EXPLORER_EXTRA; pClipRect.right += EXPLORER_EXTRA; } pRect.left -= EXPLORER_EXTRA; pClipRect.left -= EXPLORER_EXTRA; auto hTheme = OS.OpenThemeData (handle, Display.TREEVIEW.ptr); int iStateId = selected ? OS.TREIS_SELECTED : OS.TREIS_HOT; if (OS.GetFocus () !is 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) !is 0) { if ((style & SWT.FULL_SELECTION) !is 0 && columnCount is 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 !is null) { textRect.right = Math.min (cellRect.right, measureEvent.x + measureEvent.width); } fillBackground (hDC, OS.GetBkColor (hDC), textRect); } } } else { if (selected || hot) { selectionForeground = clrText = newTextClr; ignoreDrawSelection = ignoreDrawHot = true; } if (explorerTheme) { nmcd.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 is -1 ? getForegroundPixel () : clrText; if (nmcd.clrText is newColor) { nmcd.clrText |= 0x20000000; if (nmcd.clrText is newColor) nmcd.clrText &= ~0x20000000; } else { nmcd.clrText = newColor; } OS.MoveMemory (lParam, nmcd, NMTVCUSTOMDRAW.sizeof); } } if (focused && !ignoreDrawFocus && (style & SWT.FULL_SELECTION) is 0) { RECT* textRect = item.getBounds (index, true, explorerTheme, false, false, true, hDC); if (measureEvent !is null) { textRect.right = Math.min (cellRect.right, measureEvent.x + measureEvent.width); } nmcd.nmcd.uItemState &= ~OS.CDIS_FOCUS; OS.MoveMemory (lParam, nmcd, NMTVCUSTOMDRAW.sizeof); focusRect = textRect; } if (explorerTheme) { if (selected || (hot && ignoreDrawHot)) nmcd.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, null); 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 !is 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) !is 0) { int bits = OS.GetWindowLong (handle, OS.GWL_STYLE); if ((bits & OS.TVS_FULLROWSELECT) is 0) { RECT rect; OS.SetRect (&rect, nmcd.nmcd.rc.left, nmcd.nmcd.rc.top, nmcd.nmcd.rc.right, nmcd.nmcd.rc.bottom); if (selected) { fillBackground (hDC, OS.GetBkColor (hDC), &rect); } else { if (OS.IsWindowEnabled (handle)) drawBackground (hDC, &rect); } nmcd.nmcd.uItemState &= ~OS.CDIS_FOCUS; OS.MoveMemory (lParam, nmcd, NMTVCUSTOMDRAW.sizeof); } } } LRESULT result = null; if (clrText is -1 && clrTextBk is -1 && hFont is cast(HFONT)-1) { result = new LRESULT (OS.CDRF_DODEFAULT | OS.CDRF_NOTIFYPOSTPAINT); } else { result = new LRESULT (OS.CDRF_NEWFONT | OS.CDRF_NOTIFYPOSTPAINT); if (hFont !is cast(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 !is -1) { int bits = OS.GetWindowLong (handle, OS.GWL_STYLE); if ((bits & OS.TVS_FULLROWSELECT) is 0) { if (columnCount !is 0 && hwndHeader !is null) { RECT rect; HDITEM hdItem; hdItem.mask = OS.HDI_WIDTH; OS.SendMessage (hwndHeader, OS.HDM_GETITEM, index, &hdItem); OS.SetRect (&rect, nmcd.nmcd.rc.left, nmcd.nmcd.rc.top, nmcd.nmcd.rc.left + hdItem.cxy, nmcd.nmcd.rc.bottom); if (OS.COMCTL32_MAJOR < 6 || !OS.IsAppThemed ()) { RECT itemRect; if (OS.TreeView_GetItemRect (handle, cast(HTREEITEM)item.handle, &itemRect, true)) { rect.left = Math.min (itemRect.left, rect.right); } } if ((style & SWT.FULL_SELECTION) !is 0) { if (!selected) fillBackground (hDC, clrTextBk, &rect); } else { if (explorerTheme) { if (!selected && !hot) fillBackground (hDC, clrTextBk, &rect); } else { fillBackground (hDC, clrTextBk, &rect); } } } else { if ((style & SWT.FULL_SELECTION) !is 0) { RECT rect; OS.SetRect (&rect, nmcd.nmcd.rc.left, nmcd.nmcd.rc.top, nmcd.nmcd.rc.right, nmcd.nmcd.rc.bottom); if (!selected) fillBackground (hDC, clrTextBk, &rect); } } } } if (!selected) { nmcd.clrText = clrText is -1 ? getForegroundPixel () : clrText; nmcd.clrTextBk = clrTextBk is -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 () !is null) { if (!selected && (nmcd.nmcd.uItemState & (OS.CDIS_HOT | OS.CDIS_SELECTED)) is 0) { nmcd.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 is -1 ? getForegroundPixel () : clrText; if (nmcd.clrText is newColor) { nmcd.clrText |= 0x20000000; if (nmcd.clrText is newColor) nmcd.clrText &= ~0x20000000; } else { nmcd.clrText = newColor; } OS.MoveMemory (lParam, nmcd, NMTVCUSTOMDRAW.sizeof); if (clrTextBk !is -1) { if ((style & SWT.FULL_SELECTION) !is 0) { RECT rect; if (columnCount !is 0) { HDITEM hdItem; hdItem.mask = OS.HDI_WIDTH; OS.SendMessage (hwndHeader, OS.HDM_GETITEM, index, &hdItem); OS.SetRect (&rect, nmcd.nmcd.rc.left, nmcd.nmcd.rc.top, nmcd.nmcd.rc.left + hdItem.cxy, nmcd.nmcd.rc.bottom); } else { OS.SetRect (&rect, nmcd.nmcd.rc.left, nmcd.nmcd.rc.top, nmcd.nmcd.rc.right, nmcd.nmcd.rc.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 !is -1) { RECT rect; HDITEM hdItem; hdItem.mask = OS.HDI_WIDTH; OS.SendMessage (hwndHeader, OS.HDM_GETITEM, index, &hdItem); OS.SetRect (&rect, nmcd.nmcd.rc.left, nmcd.nmcd.rc.top, nmcd.nmcd.rc.left + hdItem.cxy, nmcd.nmcd.rc.bottom); fillBackground (hDC, clrSortBk, &rect); } } OS.SaveDC (hDC); if (clipRect !is null) { auto hRgn = OS.CreateRectRgn (clipRect.left, clipRect.top, clipRect.right, clipRect.bottom); POINT lpPoint; OS.GetWindowOrgEx (hDC, &lpPoint); OS.OffsetRgn (hRgn, -lpPoint.x, -lpPoint.y); OS.SelectClipRgn (hDC, hRgn); OS.DeleteObject (hRgn); } return result; } LRESULT CDDS_POSTPAINT (NMTVCUSTOMDRAW* nmcd, int wParam, int lParam) { if (ignoreCustomDraw) return null; if (OS.IsWindowVisible (handle)) { if (OS.COMCTL32_MAJOR >= 6 && OS.IsAppThemed ()) { if (sortColumn !is null && sortDirection !is SWT.NONE) { if (findImageControl () is null) { int index = indexOf (sortColumn); if (index !is -1) { int top = nmcd.nmcd.rc.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. */ HANDLE hItem; if (!OS.IsWinCE && OS.WIN32_VERSION >= OS.VERSION (6, 0)) { hItem = getBottomItem (); } else { hItem = cast(HANDLE) OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_LASTVISIBLE, 0); } if (hItem !is null) { RECT rect; if (OS.TreeView_GetItemRect (handle, cast(HTREEITEM)hItem, &rect, false)) { top = rect.bottom; } } RECT rect; OS.SetRect (&rect, nmcd.nmcd.rc.left, top, nmcd.nmcd.rc.right, nmcd.nmcd.rc.bottom); RECT headerRect; OS.SendMessage (hwndHeader, OS.HDM_GETITEMRECT, index, &headerRect); rect.left = headerRect.left; rect.right = headerRect.right; fillBackground (nmcd.nmcd.hdc, getSortColumnPixel (), &rect); } } } } if (linesVisible) { auto hDC = nmcd.nmcd.hdc; if (hwndHeader !is null) { int x = 0; RECT rect; HDITEM hdItem; hdItem.mask = OS.HDI_WIDTH; for (int i=0; i<columnCount; i++) { int index = OS.SendMessage (hwndHeader, OS.HDM_ORDERTOINDEX, i, 0); OS.SendMessage (hwndHeader, OS.HDM_GETITEM, index, &hdItem); OS.SetRect (&rect, x, nmcd.nmcd.rc.top, x + hdItem.cxy, nmcd.nmcd.rc.bottom); OS.DrawEdge (hDC, &rect, OS.BDR_SUNKENINNER, OS.BF_RIGHT); x += hdItem.cxy; } } int height = 0; RECT rect; /* * Bug in Windows. For some reason, during a collapse, * when TVM_GETNEXTITEM is sent with TVGN_LASTVISIBLE * and the collapse causes the item being collapsed * to become the last visible item in the tree, the * message takes a long time to process. In order for * the slowness to happen, the children of the item * must have children. Times of up to 11 seconds have * been observed with 23 children, each having one * child. The fix is to use the bottom partially * visible item rather than the last possible item * that could be visible. * * NOTE: This problem only happens on Vista during * WM_NOTIFY with NM_CUSTOMDRAW and CDDS_POSTPAINT. */ HANDLE hItem; if (!OS.IsWinCE && OS.WIN32_VERSION >= OS.VERSION (6, 0)) { hItem = getBottomItem (); } else { hItem = cast(HANDLE) OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_LASTVISIBLE, 0); } if (hItem !is null) { if (OS.TreeView_GetItemRect (handle, cast(HTREEITEM)hItem, &rect, false)) { height = rect.bottom - rect.top; } } if (height is 0) { height = OS.SendMessage (handle, OS.TVM_GETITEMHEIGHT, 0, 0); OS.GetClientRect (handle, &rect); OS.SetRect (&rect, rect.left, rect.top, rect.right, rect.top + height); OS.DrawEdge (hDC, &rect, OS.BDR_SUNKENINNER, OS.BF_BOTTOM); } while (rect.bottom < nmcd.nmcd.rc.bottom) { int top = rect.top + height; OS.SetRect (&rect, rect.left, top, rect.right, top + height); OS.DrawEdge (hDC, &rect, OS.BDR_SUNKENINNER, OS.BF_BOTTOM); } } } return new LRESULT (OS.CDRF_DODEFAULT); } LRESULT CDDS_PREPAINT (NMTVCUSTOMDRAW* nmcd, int wParam, int lParam) { if (explorerTheme) { if ((OS.IsWindowEnabled (handle) && hooks (SWT.EraseItem)) || findImageControl () !is null) { RECT rect; OS.SetRect (&rect, nmcd.nmcd.rc.left, nmcd.nmcd.rc.top, nmcd.nmcd.rc.right, nmcd.nmcd.rc.bottom); drawBackground (nmcd.nmcd.hdc, &rect); } } return new LRESULT (OS.CDRF_NOTIFYITEMDRAW | OS.CDRF_NOTIFYPOSTPAINT); } override int callWindowProc (HWND hwnd, int msg, int wParam, int lParam) { if (handle is null) return 0; if (hwndParent !is null && hwnd is hwndParent) { return OS.DefWindowProc (hwnd, msg, wParam, lParam); } if (hwndHeader !is null && hwnd is 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) !is 0) break; HANDLE hItem = cast(HANDLE) OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_CARET, 0); if (hItem is null) { hItem = cast(HANDLE) OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_FIRSTVISIBLE, 0); if (hItem !is null) { TVITEM tvItem; tvItem.mask = OS.TVIF_HANDLE | OS.TVIF_STATE; tvItem.hItem = cast(HTREEITEM)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 = null; if ((tvItem.state & OS.TVIS_SELECTED) is 0) { OS.SendMessage (handle, OS.TVM_SETITEM, 0, &tvItem); } } } break; } default: } HANDLE hItem; bool redraw = false; switch (msg) { /* Keyboard messages */ case OS.WM_KEYDOWN: if (wParam is OS.VK_CONTROL || wParam is 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 () !is null && drawCount is 0 && OS.IsWindowVisible (handle); if (redraw) OS.DefWindowProc (handle, OS.WM_SETREDRAW, 0, 0); //FALL THROUGH /* Mouse messages */ case OS.WM_LBUTTONDBLCLK: case OS.WM_LBUTTONDOWN: case OS.WM_LBUTTONUP: case OS.WM_MBUTTONDBLCLK: case OS.WM_MBUTTONDOWN: case OS.WM_MBUTTONUP: case OS.WM_MOUSEHOVER: case OS.WM_MOUSELEAVE: case OS.WM_MOUSEMOVE: case OS.WM_MOUSEWHEEL: case OS.WM_RBUTTONDBLCLK: case OS.WM_RBUTTONDOWN: case OS.WM_RBUTTONUP: case OS.WM_XBUTTONDBLCLK: case OS.WM_XBUTTONDOWN: case OS.WM_XBUTTONUP: //FALL THROUGH /* Other messages */ case OS.WM_SETFONT: case OS.WM_TIMER: { if (findImageControl () !is null) { hItem = cast(HANDLE) OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_FIRSTVISIBLE, 0); } break; } default: } int /*long*/ code = OS.CallWindowProc (TreeProc, hwnd, msg, wParam, lParam); switch (msg) { /* Keyboard messages */ case OS.WM_KEYDOWN: if (wParam is OS.VK_CONTROL || wParam is 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 !is null) 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 () !is null) { if (hItem !is cast(HANDLE) OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_FIRSTVISIBLE, 0)) { OS.InvalidateRect (handle, null, true); } } updateScrollBar (); break; } case OS.WM_PAINT: painted = true; break; default: } return code; } override void checkBuffered () { super.checkBuffered (); if ((style & SWT.VIRTUAL) !is 0) { style |= SWT.DOUBLE_BUFFERED; OS.SendMessage (handle, OS.TVM_SETSCROLLTIME, 0, 0); } if (EXPLORER_THEME) { if (!OS.IsWinCE && OS.WIN32_VERSION >= OS.VERSION (6, 0) && OS.IsAppThemed ()) { int exStyle = OS.SendMessage (handle, OS.TVM_GETEXTENDEDSTYLE, 0, 0); if ((exStyle & OS.TVS_EX_DOUBLEBUFFER) !is 0) style |= SWT.DOUBLE_BUFFERED; } } } bool checkData (TreeItem item, bool redraw) { if ((style & SWT.VIRTUAL) is 0) return true; if (!item.cached) { TreeItem parentItem = item.getParentItem (); return checkData (item, parentItem is null ? indexOf (item) : parentItem.indexOf (item), redraw); } return true; } bool checkData (TreeItem item, int index, bool redraw) { if ((style & SWT.VIRTUAL) is 0) return true; if (!item.cached) { item.cached = true; Event event = new Event (); event.item = item; event.index = index; TreeItem oldItem = currentItem; currentItem = item; sendEvent (SWT.SetData, event); //widget could be disposed at this point currentItem = oldItem; if (isDisposed () || item.isDisposed ()) return false; if (redraw) item.redraw (); } return true; } override bool checkHandle (HWND hwnd) { return hwnd is handle || (hwndParent !is null && hwnd is hwndParent) || (hwndHeader !is null && hwnd is hwndHeader); } bool checkScroll (HANDLE hItem) { /* * Feature in Windows. If redraw is turned off using WM_SETREDRAW * and a tree item that is not a child of the first root is selected or * scrolled using TVM_SELECTITEM or TVM_ENSUREVISIBLE, then scrolling * does not occur. The fix is to detect this case, and make sure * that redraw is temporarily enabled. To avoid flashing, DefWindowProc() * is called to disable redrawing. * * NOTE: The code that actually works around the problem is in the * callers of this method. */ if (drawCount is 0) return false; int /*long*/ hRoot = OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_ROOT, 0); int /*long*/ hParent = OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_PARENT, hItem); while (hParent !is hRoot && hParent !is 0) { hParent = OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_PARENT, hParent); } return hParent is 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 <code>SWT.VIRTUAL</code> style, * these attributes are requested again as needed. * * @param index the index of the item to clear * @param all <code>true</code> if all child items of the indexed item should be * cleared recursively, and <code>false</code> otherwise * * @exception IllegalArgumentException <ul> * <li>ERROR_INVALID_RANGE - if the index is not between 0 and the number of elements in the list minus 1 (inclusive)</li> * </ul> * @exception SWTException <ul> * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> * </ul> * * @see SWT#VIRTUAL * @see SWT#SetData * * @since 3.2 */ public void clear (int index, bool all) { checkWidget (); HANDLE hItem = cast(HANDLE) OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_ROOT, 0); if (hItem is null) error (SWT.ERROR_INVALID_RANGE); hItem = findItem (hItem, index); if (hItem is null) error (SWT.ERROR_INVALID_RANGE); TVITEM tvItem; tvItem.mask = OS.TVIF_HANDLE | OS.TVIF_PARAM; clear (hItem, &tvItem); if (all) { hItem = cast(HANDLE) OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_CHILD, hItem); clearAll (hItem, &tvItem, all); } } void clear (HANDLE hItem, TVITEM* tvItem) { tvItem.hItem = cast(HTREEITEM)hItem; TreeItem item = null; if (OS.SendMessage (handle, OS.TVM_GETITEM, 0, tvItem) !is 0) { item = tvItem.lParam !is -1 ? items [tvItem.lParam] : null; } if (item !is null) { if ((style & SWT.VIRTUAL) !is 0 && !item.cached) return; item.clear (); item.redraw (); } } /** * Clears all the items in the receiver. The text, icon and other * attributes of the items are set to their default values. If the * tree was created with the <code>SWT.VIRTUAL</code> style, these * attributes are requested again as needed. * * @param all <code>true</code> if all child items should be cleared * recursively, and <code>false</code> otherwise * * @exception SWTException <ul> * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> * </ul> * * @see SWT#VIRTUAL * @see SWT#SetData * * @since 3.2 */ public void clearAll (bool all) { checkWidget (); HANDLE hItem = cast(HANDLE) OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_ROOT, 0); if (hItem is null) return; if (all) { bool redraw = false; for (int i=0; i<items.length; i++) { TreeItem item = items [i]; if (item !is null && item !is currentItem) { item.clear (); redraw = true; } } if (redraw) OS.InvalidateRect (handle, null, true); } else { TVITEM tvItem; tvItem.mask = OS.TVIF_HANDLE | OS.TVIF_PARAM; clearAll (hItem, &tvItem, all); } } void clearAll (HANDLE hItem, TVITEM* tvItem, bool all) { while (hItem !is null) { clear (hItem, tvItem); if (all) { auto hFirstItem = cast(HANDLE)OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_CHILD, hItem); clearAll (hFirstItem, tvItem, all); } hItem = cast(HANDLE) OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_NEXT, hItem); } } private static extern(Windows) int CompareFunc (int lParam1, int lParam2, int lParamSort) { return sThis.CompareProc( lParam1, lParam2, lParamSort ); } int CompareProc (int lParam1, int lParam2, int lParamSort) { TreeItem item1 = items [lParam1], item2 = items [lParam2]; String text1 = item1.getText (lParamSort), text2 = item2.getText (lParamSort); return sortDirection is SWT.UP ? ( text1 < text2 ) : ( text2 < text1 ); } override public Point computeSize (int wHint, int hHint, bool changed) { checkWidget (); int width = 0, height = 0; if (hwndHeader !is null) { HDITEM hdItem; hdItem.mask = OS.HDI_WIDTH; for (int i=0; i<columnCount; i++) { OS.SendMessage (hwndHeader, OS.HDM_GETITEM, i, &hdItem); width += hdItem.cxy; } RECT rect; OS.GetWindowRect (hwndHeader, &rect); height += rect.bottom - rect.top; } RECT rect; HANDLE hItem = cast(HANDLE) OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_ROOT, 0); while (hItem !is null) { if ((style & SWT.VIRTUAL) is 0 && !painted) { TVITEM tvItem; tvItem.mask = OS.TVIF_HANDLE | OS.TVIF_TEXT; tvItem.hItem = cast(HTREEITEM)hItem; tvItem.pszText = OS.LPSTR_TEXTCALLBACK; ignoreCustomDraw = true; OS.SendMessage (handle, OS.TVM_SETITEM, 0, &tvItem); ignoreCustomDraw = false; } if (OS.TreeView_GetItemRect (handle, cast(HTREEITEM)hItem, &rect, true)) { width = Math.max (width, rect.right); height += rect.bottom - rect.top; } hItem = cast(HANDLE) OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_NEXTVISIBLE, hItem); } if (width is 0) width = DEFAULT_WIDTH; if (height is 0) height = DEFAULT_HEIGHT; if (wHint !is SWT.DEFAULT) width = wHint; if (hHint !is SWT.DEFAULT) height = hHint; int border = getBorderWidth (); width += border * 2; height += border * 2; if ((style & SWT.V_SCROLL) !is 0) { width += OS.GetSystemMetrics (OS.SM_CXVSCROLL); } if ((style & SWT.H_SCROLL) !is 0) { height += OS.GetSystemMetrics (OS.SM_CYHSCROLL); } return new Point (width, height); } override void createHandle () { super.createHandle (); state &= ~(CANVAS | THEME_BACKGROUND); /* Use the Explorer theme */ if (EXPLORER_THEME) { if (!OS.IsWinCE && OS.WIN32_VERSION >= OS.VERSION (6, 0) && OS.IsAppThemed ()) { explorerTheme = true; OS.SetWindowTheme (handle, cast(TCHAR*) Display.EXPLORER, null); int bits = OS.TVS_EX_DOUBLEBUFFER | OS.TVS_EX_FADEINOUTEXPANDOS | OS.TVS_EX_RICHTOOLTIP; /* * This code is intentionally commented. */ // if ((style & SWT.FULL_SELECTION) is 0) bits |= OS.TVS_EX_AUTOHSCROLL; OS.SendMessage (handle, OS.TVM_SETEXTENDEDSTYLE, 0, bits); /* * Bug in Windows. When the tree is using the explorer * theme, it does not use COLOR_WINDOW_TEXT for the * default foreground color. The fix is to explicitly * set the foreground. */ setForegroundPixel (-1); } } /* * Feature in Windows. In version 5.8 of COMCTL32.DLL, * if the font is changed for an item, the bounds for the * item are not updated, causing the text to be clipped. * The fix is to detect the version of COMCTL32.DLL, and * if it is one of the versions with the problem, then * use version 5.00 of the control (a version that does * not have the problem). This is the recommended work * around from the MSDN. */ static if (!OS.IsWinCE) { if (OS.COMCTL32_MAJOR < 6) { OS.SendMessage (handle, OS.CCM_SETVERSION, 5, 0); } } /* Set the checkbox image list */ if ((style & SWT.CHECK) !is 0) setCheckboxImageList (); /* * Feature in Windows. When the control is created, * it does not use the default system font. A new HFONT * is created and destroyed when the control is destroyed. * This means that a program that queries the font from * this control, uses the font in another control and then * destroys this control will have the font unexpectedly * destroyed in the other control. The fix is to assign * the font ourselves each time the control is created. * The control will not destroy a font that it did not * create. */ HFONT hFont = OS.GetStockObject (OS.SYSTEM_FONT); OS.SendMessage (handle, OS.WM_SETFONT, hFont, 0); } void createHeaderToolTips () { static if (OS.IsWinCE) return; if (headerToolTipHandle !is null) return; int bits = 0; if (OS.WIN32_VERSION >= OS.VERSION (4, 10)) { if ((style & SWT.RIGHT_TO_LEFT) !is 0) bits |= OS.WS_EX_LAYOUTRTL; } headerToolTipHandle = OS.CreateWindowEx ( bits, OS.TOOLTIPS_CLASS.ptr, null, OS.TTS_NOPREFIX, OS.CW_USEDEFAULT, 0, OS.CW_USEDEFAULT, 0, handle, null, OS.GetModuleHandle (null), null); if (headerToolTipHandle is null) error (SWT.ERROR_NO_HANDLES); /* * Feature in Windows. Despite the fact that the * tool tip text contains \r\n, the tooltip will * not honour the new line unless TTM_SETMAXTIPWIDTH * is set. The fix is to set TTM_SETMAXTIPWIDTH to * a large value. */ OS.SendMessage (headerToolTipHandle, OS.TTM_SETMAXTIPWIDTH, 0, 0x7FFF); } void createItem (TreeColumn column, int index) { if (hwndHeader is null) createParent (); if (!(0 <= index && index <= columnCount)) error (SWT.ERROR_INVALID_RANGE); if (columnCount is columns.length) { TreeColumn [] newColumns = new TreeColumn [columns.length + 4]; System.arraycopy (columns, 0, newColumns, 0, columns.length); columns = newColumns; } for (int i=0; i<items.length; i++) { TreeItem item = items [i]; if (item !is null) { String [] strings = item.strings; if (strings !is null) { String [] temp = new String [columnCount + 1]; System.arraycopy (strings, 0, temp, 0, index); System.arraycopy (strings, index, temp, index + 1, columnCount - index); item.strings = temp; } Image [] images = item.images; if (images !is null) { Image [] temp = new Image [columnCount + 1]; System.arraycopy (images, 0, temp, 0, index); System.arraycopy (images, index, temp, index + 1, columnCount - index); item.images = temp; } if (index is 0) { if (columnCount !is 0) { if (strings is null) { item.strings = new String [columnCount + 1]; item.strings [1] = item.text; } item.text = ""; if (images is null) { item.images = new Image [columnCount + 1]; item.images [1] = item.image; } item.image = null; } } if (item.cellBackground !is null) { int [] cellBackground = item.cellBackground; int [] temp = new int [columnCount + 1]; System.arraycopy (cellBackground, 0, temp, 0, index); System.arraycopy (cellBackground, index, temp, index + 1, columnCount - index); temp [index] = -1; item.cellBackground = temp; } if (item.cellForeground !is null) { int [] cellForeground = item.cellForeground; int [] temp = new int [columnCount + 1]; System.arraycopy (cellForeground, 0, temp, 0, index); System.arraycopy (cellForeground, index, temp, index + 1, columnCount - index); temp [index] = -1; item.cellForeground = temp; } if (item.cellFont !is null) { Font [] cellFont = item.cellFont; Font [] temp = new Font [columnCount + 1]; System.arraycopy (cellFont, 0, temp, 0, index); System.arraycopy (cellFont, index, temp, index + 1, columnCount- index); item.cellFont = temp; } } } System.arraycopy (columns, index, columns, index + 1, columnCount++ - index); columns [index] = column; /* * Bug in Windows. For some reason, when HDM_INSERTITEM * is used to insert an item into a header without text, * if is not possible to set the text at a later time. * The fix is to insert the item with an empty string. */ auto hHeap = OS.GetProcessHeap (); auto pszText = cast(TCHAR*) OS.HeapAlloc (hHeap, OS.HEAP_ZERO_MEMORY, TCHAR.sizeof); HDITEM hdItem; hdItem.mask = OS.HDI_TEXT | OS.HDI_FORMAT; hdItem.pszText = pszText; if ((column.style & SWT.LEFT) is SWT.LEFT) hdItem.fmt = OS.HDF_LEFT; if ((column.style & SWT.CENTER) is SWT.CENTER) hdItem.fmt = OS.HDF_CENTER; if ((column.style & SWT.RIGHT) is SWT.RIGHT) hdItem.fmt = OS.HDF_RIGHT; OS.SendMessage (hwndHeader, OS.HDM_INSERTITEM, index, &hdItem); if (pszText !is null) OS.HeapFree (hHeap, 0, pszText); /* When the first column is created, hide the horizontal scroll bar */ if (columnCount is 1) { scrollWidth = 0; if ((style & SWT.H_SCROLL) !is 0) { int bits = OS.GetWindowLong (handle, OS.GWL_STYLE); bits |= OS.TVS_NOHSCROLL; OS.SetWindowLong (handle, OS.GWL_STYLE, bits); } /* * Bug in Windows. When TVS_NOHSCROLL is set after items * have been inserted into the tree, Windows shows the * scroll bar. The fix is to check for this case and * explicitly hide the scroll bar explicitly. */ int count = OS.SendMessage (handle, OS.TVM_GETCOUNT, 0, 0); if (count !is 0) { static if (!OS.IsWinCE) OS.ShowScrollBar (handle, OS.SB_HORZ, false); } createItemToolTips (); if (itemToolTipHandle !is null) { OS.SendMessage (itemToolTipHandle, OS.TTM_SETDELAYTIME, OS.TTDT_AUTOMATIC, -1); } } setScrollWidth (); updateImageList (); updateScrollBar (); /* Redraw to hide the items when the first column is created */ if (columnCount is 1 && OS.SendMessage (handle, OS.TVM_GETCOUNT, 0, 0) !is 0) { OS.InvalidateRect (handle, null, true); } /* Add the tool tip item for the header */ if (headerToolTipHandle !is null) { RECT rect; if (OS.SendMessage (hwndHeader, OS.HDM_GETITEMRECT, index, &rect) !is 0) { TOOLINFO lpti; lpti.cbSize = OS.TOOLINFO_sizeof; lpti.uFlags = OS.TTF_SUBCLASS; lpti.hwnd = hwndHeader; lpti.uId = column.id = display.nextToolTipId++; lpti.rect.left = rect.left; lpti.rect.top = rect.top; lpti.rect.right = rect.right; lpti.rect.bottom = rect.bottom; lpti.lpszText = OS.LPSTR_TEXTCALLBACK; OS.SendMessage (headerToolTipHandle, OS.TTM_ADDTOOL, 0, &lpti); } } } void createItem (TreeItem item, HANDLE hParent, HANDLE hInsertAfter, HANDLE hItem) { int id = -1; if (item !is null) { id = lastID < items.length ? lastID : 0; while (id < items.length && items [id] !is null) id++; if (id is items.length) { /* * Grow the array faster when redraw is off or the * table is not visible. When the table is painted, * the items array is resized to be smaller to reduce * memory usage. */ int length = 0; if (drawCount is 0 && OS.IsWindowVisible (handle)) { length = items.length + 4; } else { shrink = true; length = Math.max (4, items.length * 3 / 2); } TreeItem [] newItems = new TreeItem [length]; System.arraycopy (items, 0, newItems, 0, items.length); items = newItems; } lastID = id + 1; } HANDLE hNewItem; HANDLE hFirstItem = cast(HANDLE) OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_CHILD, hParent); bool fixParent = hFirstItem is null; if (hItem is null) { TVINSERTSTRUCT tvInsert; tvInsert.hParent = cast(HTREEITEM)hParent; tvInsert.hInsertAfter = cast(HTREEITEM)hInsertAfter; tvInsert.item.lParam = id; tvInsert.item.pszText = OS.LPSTR_TEXTCALLBACK; tvInsert.item.iImage = tvInsert.item.iSelectedImage = cast(HBITMAP) OS.I_IMAGECALLBACK; tvInsert.item.mask = OS.TVIF_TEXT | OS.TVIF_IMAGE | OS.TVIF_SELECTEDIMAGE | OS.TVIF_HANDLE | OS.TVIF_PARAM; if ((style & SWT.CHECK) !is 0) { tvInsert.item.mask = tvInsert.item.mask | OS.TVIF_STATE; tvInsert.item.state = 1 << 12; tvInsert.item.stateMask = OS.TVIS_STATEIMAGEMASK; } ignoreCustomDraw = true; hNewItem = cast(HANDLE) OS.SendMessage (handle, OS.TVM_INSERTITEM, 0, &tvInsert); ignoreCustomDraw = false; if (hNewItem is null) error (SWT.ERROR_ITEM_NOT_ADDED); /* * This code is intentionally commented. */ // if (hParent !is 0) { // int bits = OS.GetWindowLong (handle, OS.GWL_STYLE); // bits |= OS.TVS_LINESATROOT; // OS.SetWindowLong (handle, OS.GWL_STYLE, bits); // } } else { TVITEM tvItem; tvItem.mask = OS.TVIF_HANDLE | OS.TVIF_PARAM; tvItem.hItem = cast(HTREEITEM)( hNewItem = hItem ); tvItem.lParam = id; OS.SendMessage (handle, OS.TVM_SETITEM, 0, &tvItem); } if (item !is null) { item.handle = hNewItem; items [id] = item; } if (hFirstItem is null) { if (cast(int)hInsertAfter is OS.TVI_FIRST || cast(int)hInsertAfter is OS.TVI_LAST) { hFirstIndexOf = hLastIndexOf = hFirstItem = hNewItem; itemCount = lastIndexOf = 0; } } if (hFirstItem is hFirstIndexOf && itemCount !is -1) itemCount++; if (hItem is null) { /* * Bug in Windows. When a child item is added to a parent item * that has no children outside of WM_NOTIFY with control code * TVN_ITEMEXPANDED, the tree widget does not redraw the + / - * indicator. The fix is to detect the case when the first * child is added to a visible parent item and redraw the parent. */ if (fixParent) { if (drawCount is 0 && OS.IsWindowVisible (handle)) { RECT rect; if (OS.TreeView_GetItemRect (handle, cast(HTREEITEM)hParent, &rect, false)) { OS.InvalidateRect (handle, &rect, true); } } } /* * Bug in Windows. When a new item is added while Windows * is requesting data a tree item using TVN_GETDISPINFO, * outstanding damage for items that are below the new item * is not scrolled. The fix is to explicitly damage the * new area. */ if ((style & SWT.VIRTUAL) !is 0) { if (currentItem !is null) { RECT rect; if (OS.TreeView_GetItemRect (handle, cast(HTREEITEM)hNewItem, &rect, false)) { RECT damageRect; bool damaged = cast(bool) OS.GetUpdateRect (handle, &damageRect, true); if (damaged && damageRect.top < rect.bottom) { static if (OS.IsWinCE) { OS.OffsetRect (&damageRect, 0, rect.bottom - rect.top); OS.InvalidateRect (handle, &damageRect, true); } else { HRGN rgn = OS.CreateRectRgn (0, 0, 0, 0); int result = OS.GetUpdateRgn (handle, rgn, true); if (result !is OS.NULLREGION) { OS.OffsetRgn (rgn, 0, rect.bottom - rect.top); OS.InvalidateRgn (handle, rgn, true); } OS.DeleteObject (rgn); } } } } } updateScrollBar (); } } void createItemToolTips () { static if (OS.IsWinCE) return; if (itemToolTipHandle !is null) return; int bits1 = OS.GetWindowLong (handle, OS.GWL_STYLE); bits1 |= OS.TVS_NOTOOLTIPS; OS.SetWindowLong (handle, OS.GWL_STYLE, bits1); int bits2 = 0; if (OS.WIN32_VERSION >= OS.VERSION (4, 10)) { if ((style & SWT.RIGHT_TO_LEFT) !is 0) bits2 |= OS.WS_EX_LAYOUTRTL; } /* * Feature in Windows. For some reason, when the user * clicks on a tool tip, it temporarily takes focus, even * when WS_EX_NOACTIVATE is specified. The fix is to * use WS_EX_TRANSPARENT, even though WS_EX_TRANSPARENT * is documented to affect painting, not hit testing. * * NOTE: Windows 2000 doesn't have the problem and * setting WS_EX_TRANSPARENT causes pixel corruption. */ if (OS.COMCTL32_MAJOR >= 6) bits2 |= OS.WS_EX_TRANSPARENT; itemToolTipHandle = OS.CreateWindowEx ( bits2, OS.TOOLTIPS_CLASS.ptr, null, OS.TTS_NOPREFIX | OS.TTS_NOANIMATE | OS.TTS_NOFADE, OS.CW_USEDEFAULT, 0, OS.CW_USEDEFAULT, 0, handle, null, OS.GetModuleHandle (null), null); if (itemToolTipHandle is null) error (SWT.ERROR_NO_HANDLES); OS.SendMessage (itemToolTipHandle, OS.TTM_SETDELAYTIME, OS.TTDT_INITIAL, 0); TOOLINFO lpti; lpti.cbSize = OS.TOOLINFO_sizeof; lpti.hwnd = handle; lpti.uId = cast(int)handle; lpti.uFlags = OS.TTF_SUBCLASS | OS.TTF_TRANSPARENT; lpti.lpszText = OS.LPSTR_TEXTCALLBACK; OS.SendMessage (itemToolTipHandle, OS.TTM_ADDTOOL, 0, &lpti); } void createParent () { forceResize (); RECT rect; OS.GetWindowRect (handle, &rect); OS.MapWindowPoints (null, parent.handle, cast(POINT*) &rect, 2); int oldStyle = OS.GetWindowLong (handle, OS.GWL_STYLE); int newStyle = super.widgetStyle () & ~OS.WS_VISIBLE; if ((oldStyle & OS.WS_DISABLED) !is 0) newStyle |= OS.WS_DISABLED; // if ((oldStyle & OS.WS_VISIBLE) !is 0) newStyle |= OS.WS_VISIBLE; hwndParent = OS.CreateWindowEx ( super.widgetExtStyle (), StrToTCHARz( 0, super.windowClass () ), null, newStyle, rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top, parent.handle, null, OS.GetModuleHandle (null), null); if (hwndParent is null) error (SWT.ERROR_NO_HANDLES); OS.SetWindowLongPtr (hwndParent, OS.GWLP_ID, cast(LONG_PTR)hwndParent); int bits = 0; if (OS.WIN32_VERSION >= OS.VERSION (4, 10)) { bits |= OS.WS_EX_NOINHERITLAYOUT; if ((style & SWT.RIGHT_TO_LEFT) !is 0) bits |= OS.WS_EX_LAYOUTRTL; } hwndHeader = OS.CreateWindowEx ( bits, HeaderClass.ptr, null, OS.HDS_BUTTONS | OS.HDS_FULLDRAG | OS.HDS_DRAGDROP | OS.HDS_HIDDEN | OS.WS_CHILD | OS.WS_CLIPSIBLINGS, 0, 0, 0, 0, hwndParent, null, OS.GetModuleHandle (null), null); if (hwndHeader is null) error (SWT.ERROR_NO_HANDLES); OS.SetWindowLongPtr (hwndHeader, OS.GWLP_ID, cast(LONG_PTR)hwndHeader); if (OS.IsDBLocale) { auto hIMC = OS.ImmGetContext (handle); OS.ImmAssociateContext (hwndParent, hIMC); OS.ImmAssociateContext (hwndHeader, hIMC); OS.ImmReleaseContext (handle, hIMC); } //This code is intentionally commented // if (!OS.IsPPC) { // if ((style & SWT.BORDER) !is 0) { // int oldExStyle = OS.GetWindowLong (handle, OS.GWL_EXSTYLE); // oldExStyle &= ~OS.WS_EX_CLIENTEDGE; // OS.SetWindowLong (handle, OS.GWL_EXSTYLE, oldExStyle); // } // } HFONT hFont = cast(HFONT) OS.SendMessage (handle, OS.WM_GETFONT, 0, 0); if (hFont !is null) OS.SendMessage (hwndHeader, OS.WM_SETFONT, hFont, 0); HANDLE hwndInsertAfter = OS.GetWindow (handle, OS.GW_HWNDPREV); int flags = OS.SWP_NOSIZE | OS.SWP_NOMOVE | OS.SWP_NOACTIVATE; SetWindowPos (hwndParent, hwndInsertAfter, 0, 0, 0, 0, flags); SCROLLINFO info; info.cbSize = SCROLLINFO.sizeof; info.fMask = OS.SIF_RANGE | OS.SIF_PAGE; OS.GetScrollInfo (hwndParent, OS.SB_HORZ, &info); info.nPage = info.nMax + 1; OS.SetScrollInfo (hwndParent, OS.SB_HORZ, &info, true); OS.GetScrollInfo (hwndParent, OS.SB_VERT, &info); info.nPage = info.nMax + 1; OS.SetScrollInfo (hwndParent, OS.SB_VERT, &info, true); customDraw = true; deregister (); if ((oldStyle & OS.WS_VISIBLE) !is 0) { OS.ShowWindow (hwndParent, OS.SW_SHOW); } HWND hwndFocus = OS.GetFocus (); if (hwndFocus is handle) OS.SetFocus (hwndParent); OS.SetParent (handle, hwndParent); if (hwndFocus is handle) OS.SetFocus (handle); register (); subclass (); } override void createWidget () { super.createWidget (); items = new TreeItem [4]; columns = new TreeColumn [4]; itemCount = -1; } override int defaultBackground () { return OS.GetSysColor (OS.COLOR_WINDOW); } override void deregister () { super.deregister (); if (hwndParent !is null) display.removeControl (hwndParent); if (hwndHeader !is null) display.removeControl (hwndHeader); } void deselect (HANDLE hItem, TVITEM* tvItem, HANDLE hIgnoreItem) { while (hItem !is null) { if (hItem !is hIgnoreItem) { tvItem.hItem = cast(HTREEITEM)hItem; OS.SendMessage (handle, OS.TVM_SETITEM, 0, tvItem); } auto hFirstItem = cast(HANDLE) OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_CHILD, hItem); deselect (hFirstItem, tvItem, hIgnoreItem); hItem = cast(HANDLE) OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_NEXT, hItem); } } /** * Deselects an item in the receiver. If the item was already * deselected, it remains deselected. * * @param item the item to be deselected * * @exception IllegalArgumentException <ul> * <li>ERROR_NULL_ARGUMENT - if the item is null</li> * <li>ERROR_INVALID_ARGUMENT - if the item has been disposed</li> * </ul> * @exception SWTException <ul> * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> * </ul> * * @since 3.4 */ public void deselect (TreeItem item) { checkWidget (); if (item is null) error (SWT.ERROR_NULL_ARGUMENT); if (item.isDisposed ()) error (SWT.ERROR_INVALID_ARGUMENT); TVITEM tvItem; tvItem.mask = OS.TVIF_HANDLE | OS.TVIF_STATE; tvItem.stateMask = OS.TVIS_SELECTED; tvItem.hItem = cast(HTREEITEM)item.handle; OS.SendMessage (handle, OS.TVM_SETITEM, 0, cast(int)&tvItem); } /** * Deselects all selected items in the receiver. * * @exception SWTException <ul> * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> * </ul> */ public void deselectAll () { checkWidget (); TVITEM tvItem; tvItem.mask = OS.TVIF_HANDLE | OS.TVIF_STATE; tvItem.stateMask = OS.TVIS_SELECTED; if ((style & SWT.SINGLE) !is 0) { HANDLE hItem = cast(HANDLE) OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_CARET, 0); if (hItem !is null) { tvItem.hItem = cast(HTREEITEM)hItem; OS.SendMessage (handle, OS.TVM_SETITEM, 0, &tvItem); } } else { auto oldProc = OS.GetWindowLongPtr (handle, OS.GWLP_WNDPROC); OS.SetWindowLongPtr (handle, OS.GWLP_WNDPROC, cast(LONG_PTR)TreeProc); if ((style & SWT.VIRTUAL) !is 0) { HANDLE hItem = cast(HANDLE) OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_ROOT, 0); deselect (hItem, &tvItem, null); } else { for (int i=0; i<items.length; i++) { TreeItem item = items [i]; if (item !is null) { tvItem.hItem = cast(HTREEITEM)item.handle; OS.SendMessage (handle, OS.TVM_SETITEM, 0, &tvItem); } } } OS.SetWindowLongPtr (handle, OS.GWLP_WNDPROC, oldProc); } } void destroyItem (TreeColumn column) { if (hwndHeader is null) error (SWT.ERROR_ITEM_NOT_REMOVED); int index = 0; while (index < columnCount) { if (columns [index] is column) break; index++; } int [] oldOrder = new int [columnCount]; OS.SendMessage (hwndHeader, OS.HDM_GETORDERARRAY, columnCount, oldOrder.ptr); int orderIndex = 0; while (orderIndex < columnCount) { if (oldOrder [orderIndex] is index) break; orderIndex++; } RECT headerRect; OS.SendMessage (hwndHeader, OS.HDM_GETITEMRECT, index, &headerRect); if (OS.SendMessage (hwndHeader, OS.HDM_DELETEITEM, index, 0) is 0) { error (SWT.ERROR_ITEM_NOT_REMOVED); } System.arraycopy (columns, index + 1, columns, index, --columnCount - index); columns [columnCount] = null; for (int i=0; i<items.length; i++) { TreeItem item = items [i]; if (item !is null) { if (columnCount is 0) { item.strings = null; item.images = null; item.cellBackground = null; item.cellForeground = null; item.cellFont = null; } else { if (item.strings !is null) { String [] strings = item.strings; if (index is 0) { item.text = strings [1] !is null ? strings [1] : ""; } String [] temp = new String [columnCount]; System.arraycopy (strings, 0, temp, 0, index); System.arraycopy (strings, index + 1, temp, index, columnCount - index); item.strings = temp; } else { if (index is 0) item.text = ""; } if (item.images !is null) { Image [] images = item.images; if (index is 0) item.image = images [1]; Image [] temp = new Image [columnCount]; System.arraycopy (images, 0, temp, 0, index); System.arraycopy (images, index + 1, temp, index, columnCount - index); item.images = temp; } else { if (index is 0) item.image = null; } if (item.cellBackground !is null) { int [] cellBackground = item.cellBackground; int [] temp = new int [columnCount]; System.arraycopy (cellBackground, 0, temp, 0, index); System.arraycopy (cellBackground, index + 1, temp, index, columnCount - index); item.cellBackground = temp; } if (item.cellForeground !is null) { int [] cellForeground = item.cellForeground; int [] temp = new int [columnCount]; System.arraycopy (cellForeground, 0, temp, 0, index); System.arraycopy (cellForeground, index + 1, temp, index, columnCount - index); item.cellForeground = temp; } if (item.cellFont !is null) { Font [] cellFont = item.cellFont; Font [] temp = new Font [columnCount]; System.arraycopy (cellFont, 0, temp, 0, index); System.arraycopy (cellFont, index + 1, temp, index, columnCount - index); item.cellFont = temp; } } } } /* * When the last column is deleted, show the horizontal * scroll bar. Otherwise, left align the first column * and redraw the columns to the right. */ if (columnCount is 0) { scrollWidth = 0; if (!hooks (SWT.MeasureItem)) { int bits = OS.GetWindowLong (handle, OS.GWL_STYLE); if ((style & SWT.H_SCROLL) !is 0) bits &= ~OS.TVS_NOHSCROLL; OS.SetWindowLong (handle, OS.GWL_STYLE, bits); OS.InvalidateRect (handle, null, true); } if (itemToolTipHandle !is null) { OS.SendMessage (itemToolTipHandle, OS.TTM_SETDELAYTIME, OS.TTDT_INITIAL, 0); } } else { if (index is 0) { columns [0].style &= ~(SWT.LEFT | SWT.RIGHT | SWT.CENTER); columns [0].style |= SWT.LEFT; HDITEM hdItem; hdItem.mask = OS.HDI_FORMAT | OS.HDI_IMAGE; OS.SendMessage (hwndHeader, OS.HDM_GETITEM, index, &hdItem); hdItem.fmt &= ~OS.HDF_JUSTIFYMASK; hdItem.fmt |= OS.HDF_LEFT; OS.SendMessage (hwndHeader, OS.HDM_SETITEM, index, &hdItem); } RECT rect; OS.GetClientRect (handle, &rect); rect.left = headerRect.left; OS.InvalidateRect (handle, &rect, true); } setScrollWidth (); updateImageList (); updateScrollBar (); if (columnCount !is 0) { int [] newOrder = new int [columnCount]; OS.SendMessage (hwndHeader, OS.HDM_GETORDERARRAY, columnCount, newOrder.ptr); TreeColumn [] newColumns = new TreeColumn [columnCount - orderIndex]; for (int i=orderIndex; i<newOrder.length; i++) { newColumns [i - orderIndex] = columns [newOrder [i]]; newColumns [i - orderIndex].updateToolTip (newOrder [i]); } for (int i=0; i<newColumns.length; i++) { if (!newColumns [i].isDisposed ()) { newColumns [i].sendEvent (SWT.Move); } } } /* Remove the tool tip item for the header */ if (headerToolTipHandle !is null) { TOOLINFO lpti; lpti.cbSize = OS.TOOLINFO_sizeof; lpti.uId = column.id; lpti.hwnd = hwndHeader; OS.SendMessage (headerToolTipHandle, OS.TTM_DELTOOL, 0, &lpti); } } void destroyItem (TreeItem item, HANDLE hItem) { hFirstIndexOf = hLastIndexOf = null; itemCount = -1; /* * Feature in Windows. When an item is removed that is not * visible in the tree because it belongs to a collapsed branch, * Windows redraws the tree causing a flash for each item that * is removed. The fix is to detect whether the item is visible, * force the widget to be fully painted, turn off redraw, remove * the item and validate the damage caused by the removing of * the item. * * NOTE: This fix is not necessary when double buffering and * can cause problems for virtual trees due to the call to * UpdateWindow() that flushes outstanding WM_PAINT events, * allowing application code to add or remove items during * this remove operation. */ HANDLE hParent; bool fixRedraw = false; if ((style & SWT.DOUBLE_BUFFERED) is 0) { if (drawCount is 0 && OS.IsWindowVisible (handle)) { RECT rect; fixRedraw = !OS.TreeView_GetItemRect (handle, cast(HTREEITEM)hItem, &rect, false); } } if (fixRedraw) { hParent = cast(HANDLE) OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_PARENT, hItem); OS.UpdateWindow (handle); OS.DefWindowProc (handle, OS.WM_SETREDRAW, 0, 0); /* * This code is intentionally commented. */ // OS.SendMessage (handle, OS.WM_SETREDRAW, 0, 0); } if ((style & SWT.MULTI) !is 0) { ignoreDeselect = ignoreSelect = lockSelection = true; } /* * Feature in Windows. When an item is deleted and a tool tip * is showing, Window requests the new text for the new item * that is under the cursor right away. This means that when * multiple items are deleted, the tool tip flashes, showing * each new item in the tool tip as it is scrolled into view. * The fix is to hide tool tips when any item is deleted. * * NOTE: This only happens on Vista. */ if (!OS.IsWinCE && OS.WIN32_VERSION >= OS.VERSION (6, 0)) { auto hwndToolTip = cast(HWND) OS.SendMessage (handle, OS.TVM_GETTOOLTIPS, 0, 0); if (hwndToolTip !is null) OS.SendMessage (hwndToolTip, OS.TTM_POP, 0 ,0); } shrink = ignoreShrink = true; OS.SendMessage (handle, OS.TVM_DELETEITEM, 0, hItem); ignoreShrink = false; if ((style & SWT.MULTI) !is 0) { ignoreDeselect = ignoreSelect = lockSelection = false; } if (fixRedraw) { OS.DefWindowProc (handle, OS.WM_SETREDRAW, 1, 0); OS.ValidateRect (handle, null); /* * If the item that was deleted was the last child of a tree item that * is visible, redraw the parent item to force the + / - to be updated. */ if (OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_CHILD, hParent) is 0) { RECT rect; if (OS.TreeView_GetItemRect (handle, cast(HTREEITEM)hParent, &rect, false)) { OS.InvalidateRect (handle, &rect, true); } } } int count = OS.SendMessage (handle, OS.TVM_GETCOUNT, 0, 0); if (count is 0) { if (imageList !is null) { OS.SendMessage (handle, OS.TVM_SETIMAGELIST, 0, 0); display.releaseImageList (imageList); } imageList = null; if (hwndParent is null && !linesVisible) { if (!hooks (SWT.MeasureItem) && !hooks (SWT.EraseItem) && !hooks (SWT.PaintItem)) { customDraw = false; } } items = new TreeItem [4]; scrollWidth = 0; setScrollWidth (); } updateScrollBar (); } override void destroyScrollBar (int type) { super.destroyScrollBar (type); int bits = OS.GetWindowLong (handle, OS.GWL_STYLE); if ((style & (SWT.H_SCROLL | SWT.V_SCROLL)) is 0) { bits &= ~(OS.WS_HSCROLL | OS.WS_VSCROLL); bits |= OS.TVS_NOSCROLL; } else { if ((style & SWT.H_SCROLL) is 0) { bits &= ~OS.WS_HSCROLL; bits |= OS.TVS_NOHSCROLL; } } OS.SetWindowLong (handle, OS.GWL_STYLE, bits); } override void enableDrag (bool enabled) { int bits = OS.GetWindowLong (handle, OS.GWL_STYLE); if (enabled && hooks (SWT.DragDetect)) { bits &= ~OS.TVS_DISABLEDRAGDROP; } else { bits |= OS.TVS_DISABLEDRAGDROP; } OS.SetWindowLong (handle, OS.GWL_STYLE, bits); } override void enableWidget (bool enabled) { super.enableWidget (enabled); /* * Feature in Windows. When a tree is given a background color * using TVM_SETBKCOLOR and the tree is disabled, Windows draws * the tree using the background color rather than the disabled * colors. This is different from the table which draws grayed. * The fix is to set the default background color while the tree * is disabled and restore it when enabled. */ Control control = findBackgroundControl (); /* * Bug in Windows. On Vista only, Windows does not draw using * the background color when the tree is disabled. The fix is * to set the default color, even when the color has not been * changed, causing Windows to draw correctly. */ if (!OS.IsWinCE && OS.WIN32_VERSION >= OS.VERSION (6, 0)) { if (control is null) control = this; } if (control !is null) { if (control.backgroundImage is null) { _setBackgroundPixel (enabled ? control.getBackgroundPixel () : -1); } } if (hwndParent !is null) OS.EnableWindow (hwndParent, enabled); /* * Feature in Windows. When the tree has the style * TVS_FULLROWSELECT, the background color for the * entire row is filled when an item is painted, * drawing on top of the sort column color. The fix * is to clear TVS_FULLROWSELECT when a their is * as sort column. */ updateFullSelection (); } bool findCell (int x, int y, ref TreeItem item, ref int index, ref RECT* cellRect, ref RECT* itemRect) { bool found = false; TVHITTESTINFO lpht; lpht.pt.x = x; lpht.pt.y = y; OS.SendMessage (handle, OS.TVM_HITTEST, 0, &lpht); if (lpht.hItem !is null) { item = _getItem (lpht.hItem); POINT pt; pt.x = x; pt.y = y; auto hDC = OS.GetDC (handle); HFONT oldFont; auto newFont = cast(HFONT)OS.SendMessage (handle, OS.WM_GETFONT, 0, 0); if (newFont !is null) oldFont = OS.SelectObject (hDC, newFont); RECT rect; if (hwndParent !is null) { OS.GetClientRect (hwndParent, &rect); OS.MapWindowPoints (hwndParent, handle, cast(POINT*)&rect, 2); } else { OS.GetClientRect (handle, &rect); } int count = Math.max (1, columnCount); int [] order = new int [count]; if (hwndHeader !is null) OS.SendMessage (hwndHeader, OS.HDM_GETORDERARRAY, count, cast(int) order.ptr); index = 0; bool quit = false; while (index < count && !quit) { auto hFont = item.fontHandle (order [index]); if (hFont !is cast(HFONT)-1) hFont = OS.SelectObject (hDC, hFont); cellRect = item.getBounds (order [index], true, false, true, false, true, hDC); if (cellRect.left > rect.right) { quit = true; } else { cellRect.right = Math.min (cellRect.right, rect.right); if (OS.PtInRect ( cellRect, pt)) { if (isCustomToolTip ()) { Event event = sendMeasureItemEvent (item, order [index], hDC); if (isDisposed () || item.isDisposed ()) break; itemRect = new RECT (); itemRect.left = event.x; itemRect.right = event.x + event.width; itemRect.top = event.y; itemRect.bottom = event.y + event.height; } else { itemRect = item.getBounds (order [index], true, false, false, false, false, hDC); } if (itemRect.right > cellRect.right) found = true; quit = true; } } if (hFont !is cast(HFONT)-1) OS.SelectObject (hDC, hFont); if (!found) index++; } if (newFont !is null) OS.SelectObject (hDC, oldFont); OS.ReleaseDC (handle, hDC); } return found; } int findIndex (HANDLE hFirstItem, HANDLE hItem) { if (hFirstItem is null) return -1; if (hFirstItem is hFirstIndexOf) { if (hFirstIndexOf is hItem) { hLastIndexOf = hFirstIndexOf; return lastIndexOf = 0; } if (hLastIndexOf is hItem) return lastIndexOf; auto hPrevItem = cast(HANDLE) OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_PREVIOUS, hLastIndexOf); if (hPrevItem is hItem) { hLastIndexOf = hPrevItem; return --lastIndexOf; } HANDLE hNextItem = cast(HANDLE) OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_NEXT, hLastIndexOf); if (hNextItem is hItem) { hLastIndexOf = hNextItem; return ++lastIndexOf; } int previousIndex = lastIndexOf - 1; while (hPrevItem !is null && hPrevItem !is hItem) { hPrevItem = cast(HANDLE) OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_PREVIOUS, hPrevItem); --previousIndex; } if (hPrevItem is hItem) { hLastIndexOf = hPrevItem; return lastIndexOf = previousIndex; } int nextIndex = lastIndexOf + 1; while (hNextItem !is null && hNextItem !is hItem) { hNextItem = cast(HANDLE) OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_NEXT, hNextItem); nextIndex++; } if (hNextItem is hItem) { hLastIndexOf = hNextItem; return lastIndexOf = nextIndex; } return -1; } int index = 0; auto hNextItem = hFirstItem; while (hNextItem !is null && hNextItem !is hItem) { hNextItem = cast(HANDLE) OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_NEXT, hNextItem); index++; } if (hNextItem is hItem) { itemCount = -1; hFirstIndexOf = hFirstItem; hLastIndexOf = hNextItem; return lastIndexOf = index; } return -1; } override Widget findItem (HANDLE hItem) { return _getItem (hItem); } HANDLE findItem (HANDLE hFirstItem, int index) { if (hFirstItem is null) return null; if (hFirstItem is hFirstIndexOf) { if (index is 0) { lastIndexOf = 0; return hLastIndexOf = hFirstIndexOf; } if (lastIndexOf is index) return hLastIndexOf; if (lastIndexOf - 1 is index) { --lastIndexOf; return hLastIndexOf = cast(HANDLE) OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_PREVIOUS, hLastIndexOf); } if (lastIndexOf + 1 is index) { lastIndexOf++; return hLastIndexOf = cast(HANDLE) OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_NEXT, hLastIndexOf); } if (index < lastIndexOf) { int previousIndex = lastIndexOf - 1; auto hPrevItem = cast(HANDLE) OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_PREVIOUS, hLastIndexOf); while (hPrevItem !is null && index < previousIndex) { hPrevItem = cast(HANDLE) OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_PREVIOUS, hPrevItem); --previousIndex; } if (index is previousIndex) { lastIndexOf = previousIndex; return hLastIndexOf = hPrevItem; } } else { int nextIndex = lastIndexOf + 1; auto hNextItem = cast(HANDLE) OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_NEXT, hLastIndexOf); while (hNextItem !is null && nextIndex < index) { hNextItem = cast(HANDLE) OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_NEXT, hNextItem); nextIndex++; } if (index is nextIndex) { lastIndexOf = nextIndex; return hLastIndexOf = hNextItem; } } return null; } int nextIndex = 0; auto hNextItem = hFirstItem; while (hNextItem !is null && nextIndex < index) { hNextItem = cast(HANDLE) OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_NEXT, hNextItem); nextIndex++; } if (index is nextIndex) { itemCount = -1; lastIndexOf = nextIndex; hFirstIndexOf = hFirstItem; return hLastIndexOf = hNextItem; } return null; } TreeItem getFocusItem () { // checkWidget (); HANDLE hItem = cast(HANDLE) OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_CARET, 0); return hItem !is null ? _getItem (hItem) : null; } /** * Returns the width in pixels of a grid line. * * @return the width of a grid line in pixels * * @exception SWTException <ul> * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> * </ul> * * @since 3.1 */ public int getGridLineWidth () { checkWidget (); return GRID_WIDTH; } /** * Returns the height of the receiver's header * * @return the height of the header or zero if the header is not visible * * @exception SWTException <ul> * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> * </ul> * * @since 3.1 */ public int getHeaderHeight () { checkWidget (); if (hwndHeader is null) return 0; RECT rect; OS.GetWindowRect (hwndHeader, &rect); return rect.bottom - rect.top; } /** * Returns <code>true</code> if the receiver's header is visible, * and <code>false</code> otherwise. * <p> * If one of the receiver's ancestors is not visible or some * other condition makes the receiver not visible, this method * may still indicate that it is considered visible even though * it may not actually be showing. * </p> * * @return the receiver's header's visibility state * * @exception SWTException <ul> * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> * </ul> * * @since 3.1 */ public bool getHeaderVisible () { checkWidget (); if (hwndHeader is null) return false; int bits = OS.GetWindowLong (hwndHeader, OS.GWL_STYLE); return (bits & OS.WS_VISIBLE) !is 0; } Point getImageSize () { if (imageList !is null) return imageList.getImageSize (); return new Point (0, getItemHeight ()); } HANDLE getBottomItem () { HANDLE hItem = cast(HANDLE) OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_FIRSTVISIBLE, 0); if (hItem is null) return null; int index = 0, count = OS.SendMessage (handle, OS.TVM_GETVISIBLECOUNT, 0, 0); while (index < count) { HANDLE hNextItem = cast(HANDLE) OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_NEXTVISIBLE, hItem); if (hNextItem is null) return hItem; hItem = hNextItem; index++; } return hItem; } /** * Returns the column at the given, zero-relative index in the * receiver. Throws an exception if the index is out of range. * Columns are returned in the order that they were created. * If no <code>TreeColumn</code>s were created by the programmer, * this method will throw <code>ERROR_INVALID_RANGE</code> despite * the fact that a single column of data may be visible in the tree. * This occurs when the programmer uses the tree like a list, adding * items but never creating a column. * * @param index the index of the column to return * @return the column at the given index * * @exception IllegalArgumentException <ul> * <li>ERROR_INVALID_RANGE - if the index is not between 0 and the number of elements in the list minus 1 (inclusive)</li> * </ul> * @exception SWTException <ul> * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> * </ul> * * @see Tree#getColumnOrder() * @see Tree#setColumnOrder(int[]) * @see TreeColumn#getMoveable() * @see TreeColumn#setMoveable(bool) * @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 <code>TreeColumn</code>s were created by the programmer, * this value is zero, despite the fact that visually, one column * of items may be visible. This occurs when the programmer uses * the tree like a list, adding items but never creating a column. * * @return the number of columns * * @exception SWTException <ul> * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> * </ul> * * @since 3.1 */ public int getColumnCount () { checkWidget (); 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. * <p> * Specifically, the indices of the returned array represent * the current visual order of the items, and the contents * of the array represent the creation order of the items. * </p><p> * Note: This is not the actual structure used by the receiver * to maintain its list of items, so modifying the array will * not affect the receiver. * </p> * * @return the current visual order of the receiver's items * * @exception SWTException <ul> * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> * </ul> * * @see Tree#setColumnOrder(int[]) * @see TreeColumn#getMoveable() * @see TreeColumn#setMoveable(bool) * @see SWT#Move * * @since 3.2 */ public int[] getColumnOrder () { checkWidget (); if (columnCount is 0) return null; int [] order = new int [columnCount]; OS.SendMessage (hwndHeader, OS.HDM_GETORDERARRAY, columnCount, order.ptr); return order; } /** * Returns an array of <code>TreeColumn</code>s which are the * columns in the receiver. Columns are returned in the order * that they were created. If no <code>TreeColumn</code>s were * created by the programmer, the array is empty, despite the fact * that visually, one column of items may be visible. This occurs * when the programmer uses the tree like a list, adding items but * never creating a column. * <p> * Note: This is not the actual structure used by the receiver * to maintain its list of items, so modifying the array will * not affect the receiver. * </p> * * @return the items in the receiver * * @exception SWTException <ul> * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> * </ul> * * @see Tree#getColumnOrder() * @see Tree#setColumnOrder(int[]) * @see TreeColumn#getMoveable() * @see TreeColumn#setMoveable(bool) * @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 <ul> * <li>ERROR_INVALID_RANGE - if the index is not between 0 and the number of elements in the list minus 1 (inclusive)</li> * </ul> * @exception SWTException <ul> * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> * </ul> * * @since 3.1 */ public TreeItem getItem (int index) { checkWidget (); if (index < 0) error (SWT.ERROR_INVALID_RANGE); auto hFirstItem = cast(HANDLE) OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_ROOT, 0); if (hFirstItem is null) error (SWT.ERROR_INVALID_RANGE); HANDLE hItem = findItem (hFirstItem, index); if (hItem is null) 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 = nmcd.nmcd.lItemlParam; if ((style & SWT.VIRTUAL) !is 0) { if (id is -1) { TVITEM tvItem; tvItem.mask = OS.TVIF_HANDLE | OS.TVIF_PARAM; tvItem.hItem = cast(HTREEITEM) nmcd.nmcd.dwItemSpec; OS.SendMessage (handle, OS.TVM_GETITEM, 0, &tvItem); id = tvItem.lParam; } } return _getItem (cast(HANDLE) nmcd.nmcd.dwItemSpec, id); } /** * Returns the item at the given point in the receiver * or null if no such item exists. The point is in the * coordinate system of the receiver. * <p> * The item that is returned represents an item that could be selected by the user. * For example, if selection only occurs in items in the first column, then null is * returned if the point is outside of the item. * Note that the SWT.FULL_SELECTION style hint, which specifies the selection policy, * determines the extent of the selection. * </p> * * @param point the point used to locate the item * @return the item at the given point, or null if the point is not in a selectable item * * @exception IllegalArgumentException <ul> * <li>ERROR_NULL_ARGUMENT - if the point is null</li> * </ul> * @exception SWTException <ul> * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> * </ul> */ public TreeItem getItem (Point point) { checkWidget (); if (point is null) error (SWT.ERROR_NULL_ARGUMENT); TVHITTESTINFO lpht; lpht.pt.x = point.x; lpht.pt.y = point.y; OS.SendMessage (handle, OS.TVM_HITTEST, 0, &lpht); if (lpht.hItem !is null) { int flags = OS.TVHT_ONITEM; if ((style & SWT.FULL_SELECTION) !is 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.pt.x, lpht.pt.y)) { lpht.flags |= OS.TVHT_ONITEMICON | OS.TVHT_ONITEMLABEL; } } } if ((lpht.flags & flags) !is 0) return _getItem (lpht.hItem); } return null; } /** * Returns the number of items contained in the receiver * that are direct item children of the receiver. The * number that is returned is the number of roots in the * tree. * * @return the number of items * * @exception SWTException <ul> * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> * </ul> */ public int getItemCount () { checkWidget (); HANDLE hItem = cast(HANDLE) OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_ROOT, 0); if (hItem is null) return 0; return getItemCount (hItem); } int getItemCount (HANDLE hItem) { int count = 0; auto hFirstItem = hItem; if (hItem is hFirstIndexOf) { if (itemCount !is -1) return itemCount; hFirstItem = hLastIndexOf; count = lastIndexOf; } while (hFirstItem !is null) { hFirstItem = cast(HANDLE) OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_NEXT, hFirstItem); count++; } if (hItem is hFirstIndexOf) itemCount = count; return count; } /** * Returns the height of the area which would be used to * display <em>one</em> of the items in the tree. * * @return the height of one item * * @exception SWTException <ul> * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> * </ul> */ public int getItemHeight () { checkWidget (); return OS.SendMessage (handle, OS.TVM_GETITEMHEIGHT, 0, 0); } /** * Returns a (possibly empty) array of items contained in the * receiver that are direct item children of the receiver. These * are the roots of the tree. * <p> * Note: This is not the actual structure used by the receiver * to maintain its list of items, so modifying the array will * not affect the receiver. * </p> * * @return the items * * @exception SWTException <ul> * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> * </ul> */ public TreeItem [] getItems () { checkWidget (); HANDLE hItem = cast(HANDLE) OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_ROOT, 0); if (hItem is null) return null; return getItems (hItem); } TreeItem [] getItems (HANDLE hTreeItem) { int count = 0; auto hItem = hTreeItem; while (hItem !is null) { hItem = cast(HANDLE) OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_NEXT, hItem); count++; } int index = 0; TreeItem [] result = new TreeItem [count]; TVITEM tvItem; tvItem.mask = OS.TVIF_HANDLE | OS.TVIF_PARAM; tvItem.hItem = cast(HTREEITEM)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 !is null) { OS.SendMessage (handle, OS.TVM_GETITEM, 0, &tvItem); TreeItem item = _getItem (tvItem.hItem, tvItem.lParam); if (item !is null) result [index++] = item; tvItem.hItem = cast(HTREEITEM) OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_NEXT, tvItem.hItem); } if (index !is count) { TreeItem [] newResult = new TreeItem [index]; System.arraycopy (result, 0, newResult, 0, index); result = newResult; } return result; } /** * Returns <code>true</code> if the receiver's lines are visible, * and <code>false</code> otherwise. * <p> * If one of the receiver's ancestors is not visible or some * other condition makes the receiver not visible, this method * may still indicate that it is considered visible even though * it may not actually be showing. * </p> * * @return the visibility state of the lines * * @exception SWTException <ul> * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> * </ul> * * @since 3.1 */ public bool getLinesVisible () { checkWidget (); return linesVisible; } HANDLE getNextSelection (HANDLE hItem, TVITEM* tvItem) { while (hItem !is null) { int state = 0; static if (OS.IsWinCE) { tvItem.hItem = hItem; OS.SendMessage (handle, OS.TVM_GETITEM, 0, tvItem); state = tvItem.state; } else { state = OS.SendMessage (handle, OS.TVM_GETITEMSTATE, hItem, OS.TVIS_SELECTED); } if ((state & OS.TVIS_SELECTED) !is 0) return hItem; auto hFirstItem = cast(HANDLE) OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_CHILD, hItem); auto hSelected = getNextSelection (hFirstItem, tvItem); if (hSelected !is null) return hSelected; hItem = cast(HANDLE) OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_NEXT, hItem); } return null; } /** * Returns the receiver's parent item, which must be a * <code>TreeItem</code> or null when the receiver is a * root. * * @return the receiver's parent item * * @exception SWTException <ul> * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> * </ul> */ public TreeItem getParentItem () { checkWidget (); return null; } int getSelection (HANDLE hItem, TVITEM* tvItem, TreeItem [] selection, int index, int count, bool bigSelection, bool all) { while (hItem !is null) { if (OS.IsWinCE || bigSelection) { tvItem.hItem = cast(HTREEITEM)hItem; OS.SendMessage (handle, OS.TVM_GETITEM, 0, tvItem); if ((tvItem.state & OS.TVIS_SELECTED) !is 0) { if (selection !is null && index < selection.length) { selection [index] = _getItem (hItem, tvItem.lParam); } index++; } } else { int state = OS.SendMessage (handle, OS.TVM_GETITEMSTATE, hItem, OS.TVIS_SELECTED); if ((state & OS.TVIS_SELECTED) !is 0) { if (tvItem !is null && selection !is null && index < selection.length) { tvItem.hItem = cast(HTREEITEM)hItem; OS.SendMessage (handle, OS.TVM_GETITEM, 0, tvItem); selection [index] = _getItem (hItem, tvItem.lParam); } index++; } } if (index is count) break; if (all) { auto hFirstItem = cast(HANDLE) OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_CHILD, hItem); if ((index = getSelection (hFirstItem, tvItem, selection, index, count, bigSelection, all)) is count) { break; } hItem = cast(HANDLE) OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_NEXT, hItem); } else { hItem = cast(HANDLE) OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_NEXTVISIBLE, hItem); } } return index; } /** * Returns an array of <code>TreeItem</code>s that are currently * selected in the receiver. The order of the items is unspecified. * An empty array indicates that no items are selected. * <p> * Note: This is not the actual structure used by the receiver * to maintain its selection, so modifying the array will * not affect the receiver. * </p> * @return an array representing the selection * * @exception SWTException <ul> * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> * </ul> */ public TreeItem [] getSelection () { checkWidget (); if ((style & SWT.SINGLE) !is 0) { HANDLE hItem = cast(HANDLE) OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_CARET, 0); if (hItem is null) return new TreeItem [0]; TVITEM tvItem; tvItem.mask = OS.TVIF_HANDLE | OS.TVIF_PARAM | OS.TVIF_STATE; tvItem.hItem = cast(HTREEITEM)hItem; OS.SendMessage (handle, OS.TVM_GETITEM, 0, &tvItem); if ((tvItem.state & OS.TVIS_SELECTED) is 0) return new TreeItem [0]; return [_getItem (tvItem.hItem, tvItem.lParam)]; } int count = 0; TreeItem [] guess = new TreeItem [(style & SWT.VIRTUAL) !is 0 ? 8 : 1]; int /*long*/ oldProc = OS.GetWindowLongPtr (handle, OS.GWLP_WNDPROC); OS.SetWindowLongPtr (handle, OS.GWLP_WNDPROC, cast(LONG_PTR)TreeProc); if ((style & SWT.VIRTUAL) !is 0) { TVITEM tvItem; tvItem.mask = OS.TVIF_HANDLE | OS.TVIF_PARAM | OS.TVIF_STATE; HANDLE hItem = cast(HANDLE) OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_ROOT, 0); count = getSelection (hItem, &tvItem, guess, 0, -1, false, true); } else { TVITEM tvItem; static if (OS.IsWinCE) { //tvItem = new TVITEM (); tvItem.mask = OS.TVIF_STATE; } for (int i=0; i<items.length; i++) { TreeItem item = items [i]; if (item !is null) { HANDLE hItem = item.handle; int state = 0; static if (OS.IsWinCE) { tvItem.hItem = hItem; OS.SendMessage (handle, OS.TVM_GETITEM, 0, &tvItem); state = tvItem.state; } else { state = OS.SendMessage (handle, OS.TVM_GETITEMSTATE, hItem, OS.TVIS_SELECTED); } if ((state & OS.TVIS_SELECTED) !is 0) { if (count < guess.length) guess [count] = item; count++; } } } } OS.SetWindowLongPtr (handle, OS.GWLP_WNDPROC, oldProc); if (count is 0) return new TreeItem [0]; if (count is guess.length) return guess; TreeItem [] result = new TreeItem [count]; if (count < guess.length) { System.arraycopy (guess, 0, result, 0, count); return result; } OS.SetWindowLongPtr (handle, OS.GWLP_WNDPROC, cast(LONG_PTR)TreeProc); TVITEM tvItem; tvItem.mask = OS.TVIF_HANDLE | OS.TVIF_PARAM | OS.TVIF_STATE; HANDLE hItem = cast(HANDLE) OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_ROOT, 0); int itemCount = OS.SendMessage (handle, OS.TVM_GETCOUNT, 0, 0); bool bigSelection = result.length > itemCount / 2; if (count !is getSelection (hItem, &tvItem, result, 0, count, bigSelection, false)) { getSelection (hItem, &tvItem, result, 0, count, bigSelection, true); } OS.SetWindowLongPtr (handle, OS.GWLP_WNDPROC, oldProc); return result; } /** * Returns the number of selected items contained in the receiver. * * @return the number of selected items * * @exception SWTException <ul> * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> * </ul> */ public int getSelectionCount () { checkWidget (); if ((style & SWT.SINGLE) !is 0) { HANDLE hItem = cast(HANDLE) OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_CARET, 0); if (hItem is null) return 0; int state = 0; static if (OS.IsWinCE) { TVITEM tvItem; tvItem.hItem = hItem; tvItem.mask = OS.TVIF_STATE; OS.SendMessage (handle, OS.TVM_GETITEM, 0, &tvItem); state = tvItem.state; } else { state = OS.SendMessage (handle, OS.TVM_GETITEMSTATE, hItem, OS.TVIS_SELECTED); } return (state & OS.TVIS_SELECTED) is 0 ? 0 : 1; } int count = 0; int /*long*/ oldProc = OS.GetWindowLongPtr (handle, OS.GWLP_WNDPROC); TVITEM tvItem_; TVITEM* tvItem = null; static if (OS.IsWinCE) { tvItem = &tvitem_; tvItem.mask = OS.TVIF_STATE; } OS.SetWindowLongPtr (handle, OS.GWLP_WNDPROC, cast(LONG_PTR)TreeProc); if ((style & SWT.VIRTUAL) !is 0) { HANDLE hItem = cast(HANDLE) OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_ROOT, 0); count = getSelection (hItem, tvItem, null, 0, -1, false, true); } else { for (int i=0; i<items.length; i++) { TreeItem item = items [i]; if (item !is null) { auto hItem = item.handle; int state = 0; static if (OS.IsWinCE) { tvItem.hItem = hItem; OS.SendMessage (handle, OS.TVM_GETITEM, 0, tvItem); state = tvItem.state; } else { state = OS.SendMessage (handle, OS.TVM_GETITEMSTATE, hItem, OS.TVIS_SELECTED); } if ((state & OS.TVIS_SELECTED) !is 0) count++; } } } OS.SetWindowLongPtr (handle, OS.GWLP_WNDPROC, oldProc); return count; } /** * Returns the column which shows the sort indicator for * the receiver. The value may be null if no column shows * the sort indicator. * * @return the sort indicator * * @exception SWTException <ul> * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> * </ul> * * @see #setSortColumn(TreeColumn) * * @since 3.2 */ public TreeColumn getSortColumn () { checkWidget (); return sortColumn; } int getSortColumnPixel () { int pixel = OS.IsWindowEnabled (handle) ? getBackgroundPixel () : OS.GetSysColor (OS.COLOR_3DFACE); int red = pixel & 0xFF; int green = (pixel & 0xFF00) >> 8; int blue = (pixel & 0xFF0000) >> 16; if (red > 240 && green > 240 && blue > 240) { red -= 8; green -= 8; blue -= 8; } else { red = Math.min (0xFF, (red / 10) + red); green = Math.min (0xFF, (green / 10) + green); blue = Math.min (0xFF, (blue / 10) + blue); } return (red & 0xFF) | ((green & 0xFF) << 8) | ((blue & 0xFF) << 16); } /** * Returns the direction of the sort indicator for the receiver. * The value will be one of <code>UP</code>, <code>DOWN</code> * or <code>NONE</code>. * * @return the sort direction * * @exception SWTException <ul> * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> * </ul> * * @see #setSortDirection(int) * * @since 3.2 */ public int getSortDirection () { checkWidget (); return sortDirection; } /** * Returns the item which is currently at the top of the receiver. * This item can change when items are expanded, collapsed, scrolled * or new items are added or removed. * * @return the item at the top of the receiver * * @exception SWTException <ul> * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> * </ul> * * @since 2.1 */ public TreeItem getTopItem () { checkWidget (); HANDLE hItem = cast(HANDLE) OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_FIRSTVISIBLE, 0); return hItem !is null ? _getItem (hItem) : null; } bool hitTestSelection (HANDLE hItem, int x, int y) { if (hItem is null) return false; TreeItem item = _getItem (hItem); if (item is null) return false; if (!hooks (SWT.MeasureItem)) return false; bool result = false; //BUG? - moved columns, only hittest first column //BUG? - check drag detect int [] order = new int [1], index = new int [1]; auto hDC = OS.GetDC (handle); HFONT oldFont, newFont = cast(HFONT)OS.SendMessage (handle, OS.WM_GETFONT, 0, 0); if (newFont !is null) oldFont = OS.SelectObject (hDC, newFont); auto hFont = item.fontHandle (order [index [0]]); if (hFont !is cast(HFONT)-1) hFont = OS.SelectObject (hDC, hFont); Event event = sendMeasureItemEvent (item, order [index [0]], hDC); if (event.getBounds ().contains (x, y)) result = true; if (newFont !is null) OS.SelectObject (hDC, oldFont); OS.ReleaseDC (handle, hDC); // if (isDisposed () || item.isDisposed ()) return false; return result; } int imageIndex (Image image, int index) { if (image is null) return OS.I_IMAGENONE; if (imageList is null) { Rectangle bounds = image.getBounds (); imageList = display.getImageList (style & SWT.RIGHT_TO_LEFT, bounds.width, bounds.height); } int imageIndex = imageList.indexOf (image); if (imageIndex is -1) imageIndex = imageList.add (image); if (hwndHeader is null || OS.SendMessage (hwndHeader, OS.HDM_ORDERTOINDEX, 0, 0) is 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. */ auto hImageList = imageList.getHandle (); auto hOldImageList = cast(HANDLE) OS.SendMessage (handle, OS.TVM_GETIMAGELIST, OS.TVSIL_NORMAL, 0); if (hOldImageList !is hImageList) { OS.SendMessage (handle, OS.TVM_SETIMAGELIST, OS.TVSIL_NORMAL, hImageList); updateScrollBar (); } } return imageIndex; } int imageIndexHeader (Image image) { if (image is null) return OS.I_IMAGENONE; if (headerImageList is null) { Rectangle bounds = image.getBounds (); headerImageList = display.getImageList (style & SWT.RIGHT_TO_LEFT, bounds.width, bounds.height); int index = headerImageList.indexOf (image); if (index is -1) index = headerImageList.add (image); auto hImageList = headerImageList.getHandle (); if (hwndHeader !is null) { OS.SendMessage (hwndHeader, OS.HDM_SETIMAGELIST, 0, hImageList); } updateScrollBar (); return index; } int index = headerImageList.indexOf (image); if (index !is -1) return index; return headerImageList.add (image); } /** * Searches the receiver's list starting at the first column * (index 0) until a column is found that is equal to the * argument, and returns the index of that column. If no column * is found, returns -1. * * @param column the search column * @return the index of the column * * @exception IllegalArgumentException <ul> * <li>ERROR_NULL_ARGUMENT - if the column is null</li> * </ul> * @exception SWTException <ul> * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> * </ul> * * @since 3.1 */ public int indexOf (TreeColumn column) { checkWidget (); if (column is null) error (SWT.ERROR_NULL_ARGUMENT); if (column.isDisposed()) error(SWT.ERROR_INVALID_ARGUMENT); for (int i=0; i<columnCount; i++) { if (columns [i] is column) return i; } return -1; } /** * Searches the receiver's list starting at the first item * (index 0) until an item is found that is equal to the * argument, and returns the index of that item. If no item * is found, returns -1. * * @param item the search item * @return the index of the item * * @exception IllegalArgumentException <ul> * <li>ERROR_NULL_ARGUMENT - if the item is null</li> * <li>ERROR_INVALID_ARGUMENT - if the item has been disposed</li> * </ul> * @exception SWTException <ul> * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> * </ul> * * @since 3.1 */ public int indexOf (TreeItem item) { checkWidget (); if (item is null) error (SWT.ERROR_NULL_ARGUMENT); if (item.isDisposed()) error(SWT.ERROR_INVALID_ARGUMENT); HANDLE hItem = cast(HANDLE) OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_ROOT, 0); return hItem is null ? -1 : findIndex (hItem, item.handle); } bool isCustomToolTip () { return hooks (SWT.MeasureItem); } bool isItemSelected (NMTVCUSTOMDRAW* nmcd) { bool selected = false; if (OS.IsWindowEnabled (handle)) { TVITEM tvItem; tvItem.mask = OS.TVIF_HANDLE | OS.TVIF_STATE; tvItem.hItem = cast(HTREEITEM)nmcd.nmcd.dwItemSpec; OS.SendMessage (handle, OS.TVM_GETITEM, 0, &tvItem); if ((tvItem.state & (OS.TVIS_SELECTED | OS.TVIS_DROPHILITED)) !is 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 is OS.GetFocus ()) { if (OS.GetTextColor (nmcd.nmcd.hdc) !is OS.GetSysColor (OS.COLOR_HIGHLIGHTTEXT)) { selected = false; } else { if (OS.GetBkColor (nmcd.nmcd.hdc) !is OS.GetSysColor (OS.COLOR_HIGHLIGHT)) { selected = false; } } } } else { if (nmcd.nmcd.dwDrawStage is 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.nmcd.hdc) is OS.GetSysColor (OS.COLOR_HIGHLIGHTTEXT)) { if (OS.GetBkColor (nmcd.nmcd.hdc) is OS.GetSysColor (OS.COLOR_HIGHLIGHT)) { selected = true; } } } } } return selected; } void redrawSelection () { if ((style & SWT.SINGLE) !is 0) { HANDLE hItem = cast(HANDLE) OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_CARET, 0); if (hItem !is null) { RECT rect; if (OS.TreeView_GetItemRect (handle, cast(HTREEITEM)hItem, &rect, false)) { OS.InvalidateRect (handle, &rect, true); } } } else { HANDLE hItem = cast(HANDLE) OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_FIRSTVISIBLE, 0); if (hItem !is null) { TVITEM tvItem; static if (OS.IsWinCE) { //tvItem = new TVITEM (); tvItem.mask = OS.TVIF_STATE; } RECT rect; int index = 0, count = OS.SendMessage (handle, OS.TVM_GETVISIBLECOUNT, 0, 0); while (index <= count && hItem !is null) { int state = 0; static if (OS.IsWinCE) { tvItem.hItem = hItem; OS.SendMessage (handle, OS.TVM_GETITEM, 0, &tvItem); state = tvItem.state; } else { state = OS.SendMessage (handle, OS.TVM_GETITEMSTATE, hItem, OS.TVIS_SELECTED); } if ((state & OS.TVIS_SELECTED) !is 0) { if (OS.TreeView_GetItemRect (handle, cast(HTREEITEM)hItem, &rect, false)) { OS.InvalidateRect (handle, &rect, true); } } hItem = cast(HANDLE) OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_NEXTVISIBLE, hItem); index++; } } } } override void register () { super.register (); if (hwndParent !is null) display.addControl (hwndParent, this); if (hwndHeader !is null) display.addControl (hwndHeader, this); } void releaseItem (HANDLE hItem, TVITEM* tvItem, bool release) { if (hItem is hAnchor) hAnchor = null; if (hItem is hInsert) hInsert = null; tvItem.hItem = cast(HTREEITEM)hItem; if (OS.SendMessage (handle, OS.TVM_GETITEM, 0, tvItem) !is 0) { if (tvItem.lParam !is -1) { if (tvItem.lParam < lastID) lastID = tvItem.lParam; if (release) { TreeItem item = items [tvItem.lParam]; if (item !is null) item.release (false); } items [tvItem.lParam] = null; } } } void releaseItems (HANDLE hItem, TVITEM* tvItem) { hItem = cast(HANDLE) OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_CHILD, hItem); while (hItem !is null) { releaseItems (hItem, tvItem); releaseItem (hItem, tvItem, true); hItem = cast(HANDLE) OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_NEXT, hItem); } } override void releaseHandle () { super.releaseHandle (); hwndParent = hwndHeader = null; } override void releaseChildren (bool destroy) { if (items !is null) { for (int i=0; i<items.length; i++) { TreeItem item = items [i]; if (item !is null && !item.isDisposed ()) { item.release (false); } } items = null; } if (columns !is null) { for (int i=0; i<columns.length; i++) { TreeColumn column = columns [i]; if (column !is null && !column.isDisposed ()) { column.release (false); } } columns = null; } super.releaseChildren (destroy); } override void releaseWidget () { super.releaseWidget (); /* * Feature in Windows. For some reason, when TVM_GETIMAGELIST * or TVM_SETIMAGELIST is sent, the tree issues NM_CUSTOMDRAW * messages. This behavior is unwanted when the tree is being * disposed. The fix is to ignore NM_CUSTOMDRAW messages by * clearing the custom draw flag. * * NOTE: This only happens on Windows XP. */ customDraw = false; if (imageList !is null) { OS.SendMessage (handle, OS.TVM_SETIMAGELIST, OS.TVSIL_NORMAL, 0); display.releaseImageList (imageList); } if (headerImageList !is null) { if (hwndHeader !is null) { OS.SendMessage (hwndHeader, OS.HDM_SETIMAGELIST, 0, 0); } display.releaseImageList (headerImageList); } imageList = headerImageList = null; auto hStateList = cast(HANDLE) OS.SendMessage (handle, OS.TVM_GETIMAGELIST, OS.TVSIL_STATE, 0); OS.SendMessage (handle, OS.TVM_SETIMAGELIST, OS.TVSIL_STATE, 0); if (hStateList !is null) OS.ImageList_Destroy (hStateList); if (itemToolTipHandle !is null) OS.DestroyWindow (itemToolTipHandle); if (headerToolTipHandle !is null) OS.DestroyWindow (headerToolTipHandle); itemToolTipHandle = headerToolTipHandle = null; } /** * Removes all of the items from the receiver. * * @exception SWTException <ul> * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> * </ul> */ public void removeAll () { checkWidget (); hFirstIndexOf = hLastIndexOf = null; itemCount = -1; for (int i=0; i<items.length; i++) { TreeItem item = items [i]; if (item !is null && !item.isDisposed ()) { item.release (false); } } ignoreDeselect = ignoreSelect = true; bool redraw = drawCount is 0 && OS.IsWindowVisible (handle); if (redraw) OS.DefWindowProc (handle, OS.WM_SETREDRAW, 0, 0); shrink = ignoreShrink = true; int /*long*/ result = OS.SendMessage (handle, OS.TVM_DELETEITEM, 0, OS.TVI_ROOT); ignoreShrink = false; if (redraw) { OS.DefWindowProc (handle, OS.WM_SETREDRAW, 1, 0); OS.InvalidateRect (handle, null, true); } ignoreDeselect = ignoreSelect = false; if (result is 0) error (SWT.ERROR_ITEM_NOT_REMOVED); if (imageList !is null) { OS.SendMessage (handle, OS.TVM_SETIMAGELIST, 0, 0); display.releaseImageList (imageList); } imageList = null; if (hwndParent is null && !linesVisible) { if (!hooks (SWT.MeasureItem) && !hooks (SWT.EraseItem) && !hooks (SWT.PaintItem)) { customDraw = false; } } hAnchor = hInsert = hFirstIndexOf = hLastIndexOf = null; itemCount = -1; items = new TreeItem [4]; scrollWidth = 0; setScrollWidth (); updateScrollBar (); } /** * Removes the listener from the collection of listeners who will * be notified when the user changes the receiver's selection. * * @param listener the listener which should no longer be notified * * @exception IllegalArgumentException <ul> * <li>ERROR_NULL_ARGUMENT - if the listener is null</li> * </ul> * @exception SWTException <ul> * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> * </ul> * * @see SelectionListener * @see #addSelectionListener */ public void removeSelectionListener (SelectionListener listener) { checkWidget (); if (listener is null) error (SWT.ERROR_NULL_ARGUMENT); eventTable.unhook (SWT.Selection, listener); eventTable.unhook (SWT.DefaultSelection, listener); } /** * Removes the listener from the collection of listeners who will * be notified when items in the receiver are expanded or collapsed. * * @param listener the listener which should no longer be notified * * @exception IllegalArgumentException <ul> * <li>ERROR_NULL_ARGUMENT - if the listener is null</li> * </ul> * @exception SWTException <ul> * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> * </ul> * * @see TreeListener * @see #addTreeListener */ public void removeTreeListener(TreeListener listener) { checkWidget (); if (listener is null) error (SWT.ERROR_NULL_ARGUMENT); if (eventTable is null) return; eventTable.unhook (SWT.Expand, listener); eventTable.unhook (SWT.Collapse, listener); } /** * Display a mark indicating the point at which an item will be inserted. * The drop insert item has a visual hint to show where a dragged item * will be inserted when dropped on the tree. * * @param item the insert item. Null will clear the insertion mark. * @param before true places the insert mark above 'item'. false places * the insert mark below 'item'. * * @exception IllegalArgumentException <ul> * <li>ERROR_INVALID_ARGUMENT - if the item has been disposed</li> * </ul> * @exception SWTException <ul> * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> * </ul> */ public void setInsertMark (TreeItem item, bool before) { checkWidget (); HANDLE hItem; if (item !is null) { if (item.isDisposed()) error(SWT.ERROR_INVALID_ARGUMENT); hItem = item.handle; } hInsert = hItem; insertAfter = !before; OS.SendMessage (handle, OS.TVM_SETINSERTMARK, insertAfter ? 1 : 0, hInsert); } /** * Sets the number of root-level items contained in the receiver. * * @param count the number of items * * @exception SWTException <ul> * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> * </ul> * * @since 3.2 */ public void setItemCount (int count) { checkWidget (); count = Math.max (0, count); HANDLE hItem = cast(HANDLE) OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_ROOT, 0); setItemCount (count, cast(HANDLE) OS.TVGN_ROOT, hItem); } void setItemCount (int count, HANDLE hParent, HANDLE hItem) { bool redraw = false; if (OS.SendMessage (handle, OS.TVM_GETCOUNT, 0, 0) is 0) { redraw = drawCount is 0 && OS.IsWindowVisible (handle); if (redraw) OS.DefWindowProc (handle, OS.WM_SETREDRAW, 0, 0); } int itemCount = 0; while (hItem !is null && itemCount < count) { hItem = cast(HANDLE) OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_NEXT, hItem); itemCount++; } bool expanded = false; TVITEM tvItem; tvItem.mask = OS.TVIF_HANDLE | OS.TVIF_PARAM; if (!redraw && (style & SWT.VIRTUAL) !is 0) { if (OS.IsWinCE) { tvItem.hItem = cast(HTREEITEM)hParent; tvItem.mask = OS.TVIF_STATE; OS.SendMessage (handle, OS.TVM_GETITEM, 0, cast(int)&tvItem); expanded = (tvItem.state & OS.TVIS_EXPANDED) !is 0; } else { /* * Bug in Windows. Despite the fact that TVM_GETITEMSTATE claims * to return only the bits specified by the stateMask, when called * with TVIS_EXPANDED, the entire state is returned. The fix is * to explicitly check for the TVIS_EXPANDED bit. */ int state = OS.SendMessage (handle, OS.TVM_GETITEMSTATE, hParent, OS.TVIS_EXPANDED); expanded = (state & OS.TVIS_EXPANDED) !is 0; } } while (hItem !is null) { tvItem.hItem = cast(HTREEITEM)hItem; OS.SendMessage (handle, OS.TVM_GETITEM, 0, &tvItem); hItem = cast(HANDLE) OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_NEXT, hItem); TreeItem item = tvItem.lParam !is -1 ? items [tvItem.lParam] : null; if (item !is null && !item.isDisposed ()) { item.dispose (); } else { releaseItem (tvItem.hItem, &tvItem, false); destroyItem (null, tvItem.hItem); } } if ((style & SWT.VIRTUAL) !is 0) { for (int i=itemCount; i<count; i++) { if (expanded) ignoreShrink = true; createItem (null, hParent, cast(HTREEITEM) OS.TVI_LAST, null); if (expanded) ignoreShrink = false; } } else { shrink = true; int extra = Math.max (4, (count + 3) / 4 * 4); TreeItem [] newItems = new TreeItem [items.length + extra]; System.arraycopy (items, 0, newItems, 0, items.length); items = newItems; for (int i=itemCount; i<count; i++) { new TreeItem (this, SWT.NONE, hParent, cast(HTREEITEM) OS.TVI_LAST, null); } } if (redraw) { OS.DefWindowProc (handle, OS.WM_SETREDRAW, 1, 0); OS.InvalidateRect (handle, null, true); } } /** * Sets the height of the area which would be used to * display <em>one</em> of the items in the tree. * * @param itemHeight the height of one item * * @exception SWTException <ul> * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> * </ul> * * @since 3.2 */ /*public*/ void setItemHeight (int itemHeight) { checkWidget (); if (itemHeight < -1) error (SWT.ERROR_INVALID_ARGUMENT); OS.SendMessage (handle, OS.TVM_SETITEMHEIGHT, itemHeight, 0); } /** * Marks the receiver's lines as visible if the argument is <code>true</code>, * and marks it invisible otherwise. * <p> * If one of the receiver's ancestors is not visible or some * other condition makes the receiver not visible, marking * it visible may not actually cause it to be displayed. * </p> * * @param show the new visibility state * * @exception SWTException <ul> * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> * </ul> * * @since 3.1 */ public void setLinesVisible (bool show) { checkWidget (); if (linesVisible is show) return; linesVisible = show; if (hwndParent is null && linesVisible) customDraw = true; OS.InvalidateRect (handle, null, true); } override HWND scrolledHandle () { if (hwndHeader is null) return handle; return columnCount is 0 && scrollWidth is 0 ? handle : hwndParent; } void select (HANDLE hItem, TVITEM* tvItem) { while (hItem !is null) { tvItem.hItem = cast(HTREEITEM)hItem; OS.SendMessage (handle, OS.TVM_SETITEM, 0, tvItem); auto hFirstItem = cast(HANDLE) OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_CHILD, hItem); select (hFirstItem, tvItem); hItem = cast(HANDLE) OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_NEXT, hItem); } } /** * Selects an item in the receiver. If the item was already * selected, it remains selected. * * @param item the item to be selected * * @exception IllegalArgumentException <ul> * <li>ERROR_NULL_ARGUMENT - if the item is null</li> * <li>ERROR_INVALID_ARGUMENT - if the item has been disposed</li> * </ul> * @exception SWTException <ul> * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> * </ul> * * @since 3.4 */ public void select (TreeItem item) { checkWidget (); if (item is null) error (SWT.ERROR_NULL_ARGUMENT); if (item.isDisposed ()) error (SWT.ERROR_INVALID_ARGUMENT); if ((style & SWT.SINGLE) !is 0) { auto hItem = item.handle; int state = 0; static if (OS.IsWinCE) { TVITEM tvItem; tvItem.hItem = hItem; tvItem.mask = OS.TVIF_STATE; OS.SendMessage (handle, OS.TVM_GETITEM, 0, cast(int)&tvItem); state = tvItem.state; } else { state = OS.SendMessage (handle, OS.TVM_GETITEMSTATE, cast(int)&hItem, OS.TVIS_SELECTED); } if ((state & OS.TVIS_SELECTED) !is 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)) is 0) { hInfo = new SCROLLINFO (); hInfo.cbSize = SCROLLINFO.sizeof; hInfo.fMask = OS.SIF_ALL; OS.GetScrollInfo (handle, OS.SB_HORZ, hInfo); } SCROLLINFO vInfo; vInfo.cbSize = SCROLLINFO.sizeof; vInfo.fMask = OS.SIF_ALL; OS.GetScrollInfo (handle, OS.SB_VERT, &vInfo); bool redraw = drawCount is 0 && OS.IsWindowVisible (handle); if (redraw) { OS.UpdateWindow (handle); OS.DefWindowProc (handle, OS.WM_SETREDRAW, 0, 0); } setSelection (item); if (hInfo !is null) { int /*long*/ hThumb = OS.MAKELPARAM (OS.SB_THUMBPOSITION, hInfo.nPos); OS.SendMessage (handle, OS.WM_HSCROLL, hThumb, 0); } int /*long*/ vThumb = OS.MAKELPARAM (OS.SB_THUMBPOSITION, vInfo.nPos); OS.SendMessage (handle, OS.WM_VSCROLL, vThumb, 0); if (redraw) { OS.DefWindowProc (handle, OS.WM_SETREDRAW, 1, 0); OS.InvalidateRect (handle, null, true); if ((style & SWT.DOUBLE_BUFFERED) is 0) { int oldStyle = style; style |= SWT.DOUBLE_BUFFERED; OS.UpdateWindow (handle); style = oldStyle; } } return; } TVITEM tvItem; tvItem.mask = OS.TVIF_HANDLE | OS.TVIF_STATE; tvItem.stateMask = OS.TVIS_SELECTED; tvItem.state = OS.TVIS_SELECTED; tvItem.hItem = cast(HTREEITEM)item.handle; OS.SendMessage (handle, OS.TVM_SETITEM, 0, cast(int)&tvItem); } /** * Selects all of the items in the receiver. * <p> * If the receiver is single-select, do nothing. * </p> * * @exception SWTException <ul> * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> * </ul> */ public void selectAll () { checkWidget (); if ((style & SWT.SINGLE) !is 0) return; TVITEM tvItem; tvItem.mask = OS.TVIF_HANDLE | OS.TVIF_STATE; tvItem.state = OS.TVIS_SELECTED; tvItem.stateMask = OS.TVIS_SELECTED; int /*long*/ oldProc = OS.GetWindowLongPtr (handle, OS.GWLP_WNDPROC); OS.SetWindowLongPtr (handle, OS.GWLP_WNDPROC, cast(LONG_PTR)TreeProc); if ((style & SWT.VIRTUAL) !is 0) { HANDLE hItem = cast(HANDLE) OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_ROOT, 0); select (hItem, &tvItem); } else { for (int i=0; i<items.length; i++) { TreeItem item = items [i]; if (item !is null) { tvItem.hItem = cast(HTREEITEM)item.handle; OS.SendMessage (handle, OS.TVM_SETITEM, 0, &tvItem); } } } OS.SetWindowLongPtr (handle, OS.GWLP_WNDPROC, oldProc); } Event sendEraseItemEvent (TreeItem item, NMTTCUSTOMDRAW* nmcd, int column, RECT* cellRect) { int nSavedDC = OS.SaveDC (nmcd.nmcd.hdc); RECT* insetRect = toolTipInset (cellRect); OS.SetWindowOrgEx (nmcd.nmcd.hdc, insetRect.left, insetRect.top, null); GCData data = new GCData (); data.device = display; data.foreground = OS.GetTextColor (nmcd.nmcd.hdc); data.background = OS.GetBkColor (nmcd.nmcd.hdc); data.font = item.getFont (column); data.uiState = OS.SendMessage (handle, OS.WM_QUERYUISTATE, 0, 0); GC gc = GC.win32_new (nmcd.nmcd.hdc, data); Event event = new Event (); event.item = item; event.index = column; event.gc = gc; event.detail |= SWT.FOREGROUND; event.x = cellRect.left; event.y = cellRect.top; event.width = cellRect.right - cellRect.left; event.height = cellRect.bottom - cellRect.top; //gc.setClipping (event.x, event.y, event.width, event.height); sendEvent (SWT.EraseItem, event); event.gc = null; //int newTextClr = data.foreground; gc.dispose (); OS.RestoreDC (nmcd.nmcd.hdc, nSavedDC); return event; } Event sendMeasureItemEvent (TreeItem item, int index, HDC hDC) { RECT* itemRect = item.getBounds (index, true, true, false, false, false, hDC); int nSavedDC = OS.SaveDC (hDC); GCData data = new GCData (); data.device = display; data.font = item.getFont (index); GC gc = GC.win32_new (hDC, data); Event event = new Event (); event.item = item; event.gc = gc; event.index = index; event.x = itemRect.left; event.y = itemRect.top; event.width = itemRect.right - itemRect.left; event.height = itemRect.bottom - itemRect.top; sendEvent (SWT.MeasureItem, event); event.gc = null; gc.dispose (); OS.RestoreDC (hDC, nSavedDC); if (isDisposed () || item.isDisposed ()) return null; if (hwndHeader !is null) { if (columnCount is 0) { if (event.x + event.width > scrollWidth) { setScrollWidth (scrollWidth = event.x + event.width); } } } if (event.height > getItemHeight ()) setItemHeight (event.height); return event; } Event sendPaintItemEvent (TreeItem item, NMTTCUSTOMDRAW* nmcd, int column, RECT* itemRect) { int nSavedDC = OS.SaveDC (nmcd.nmcd.hdc); RECT* insetRect = toolTipInset (itemRect); OS.SetWindowOrgEx (nmcd.nmcd.hdc, insetRect.left, insetRect.top, null); GCData data = new GCData (); data.device = display; data.font = item.getFont (column); data.foreground = OS.GetTextColor (nmcd.nmcd.hdc); data.background = OS.GetBkColor (nmcd.nmcd.hdc); data.uiState = OS.SendMessage (handle, OS.WM_QUERYUISTATE, 0, 0); GC gc = GC.win32_new (nmcd.nmcd.hdc, data); Event event = new Event (); event.item = item; event.index = column; event.gc = gc; event.detail |= SWT.FOREGROUND; event.x = itemRect.left; event.y = itemRect.top; event.width = itemRect.right - itemRect.left; event.height = itemRect.bottom - itemRect.top; //gc.setClipping (cellRect.left, cellRect.top, cellWidth, cellHeight); sendEvent (SWT.PaintItem, event); event.gc = null; gc.dispose (); OS.RestoreDC (nmcd.nmcd.hdc, nSavedDC); return event; } override void setBackgroundImage (HBITMAP hBitmap) { super.setBackgroundImage (hBitmap); if (hBitmap !is null) { /* * 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) is -1) { OS.SendMessage (handle, OS.TVM_SETBKCOLOR, 0, -1); } _setBackgroundPixel (-1); } else { Control control = findBackgroundControl (); if (control is null) control = this; if (control.backgroundImage is 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 !is null) { setBackgroundImage (control.backgroundImage); return; } /* * Feature in Windows. When a tree is given a background color * using TVM_SETBKCOLOR and the tree is disabled, Windows draws * the tree using the background color rather than the disabled * colors. This is different from the table which draws grayed. * The fix is to set the default background color while the tree * is disabled and restore it when enabled. */ if (OS.IsWindowEnabled (handle)) _setBackgroundPixel (pixel); /* * Feature in Windows. When the tree has the style * TVS_FULLROWSELECT, the background color for the * entire row is filled when an item is painted, * drawing on top of the background image. The fix * is to restore TVS_FULLROWSELECT when a background * color is set. */ updateFullSelection (); } 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 (); auto hCursor = cursor is null ? OS.LoadCursor (null, cast(TCHAR*) OS.IDC_ARROW) : cursor.handle; OS.SetCursor (hCursor); } /** * Sets the order that the items in the receiver should * be displayed in to the given argument which is described * in terms of the zero-relative ordering of when the items * were added. * * @param order the new order to display the items * * @exception SWTException <ul> * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> * </ul> * @exception IllegalArgumentException <ul> * <li>ERROR_INVALID_ARGUMENT - if the item order is not the same length as the number of items</li> * </ul> * * @see Tree#getColumnOrder() * @see TreeColumn#getMoveable() * @see TreeColumn#setMoveable(bool) * @see SWT#Move * * @since 3.2 */ public void setColumnOrder (int [] order) { checkWidget (); // SWT extension: allow null array //if (order is null) error (SWT.ERROR_NULL_ARGUMENT); if (columnCount is 0) { if (order.length !is 0) error (SWT.ERROR_INVALID_ARGUMENT); return; } if (order.length !is columnCount) error (SWT.ERROR_INVALID_ARGUMENT); int [] oldOrder = new int [columnCount]; OS.SendMessage (hwndHeader, OS.HDM_GETORDERARRAY, columnCount, oldOrder.ptr); bool reorder = false; bool [] seen = new bool [columnCount]; for (int i=0; i<order.length; i++) { int index = order [i]; if (index < 0 || index >= columnCount) error (SWT.ERROR_INVALID_RANGE); if (seen [index]) error (SWT.ERROR_INVALID_ARGUMENT); seen [index] = true; if (index !is oldOrder [i]) reorder = true; } if (reorder) { RECT [] oldRects = new RECT [columnCount]; for (int i=0; i<columnCount; i++) { //oldRects [i] = new RECT (); OS.SendMessage (hwndHeader, OS.HDM_GETITEMRECT, i, & oldRects [i]); } OS.SendMessage (hwndHeader, OS.HDM_SETORDERARRAY, order.length, order.ptr); OS.InvalidateRect (handle, null, true); updateImageList (); TreeColumn [] newColumns = new TreeColumn [columnCount]; System.arraycopy (columns, 0, newColumns, 0, columnCount); RECT newRect; for (int i=0; i<columnCount; i++) { TreeColumn column = newColumns [i]; if (!column.isDisposed ()) { OS.SendMessage (hwndHeader, OS.HDM_GETITEMRECT, i, &newRect); if (newRect.left !is oldRects [i].left) { column.updateToolTip (i); column.sendEvent (SWT.Move); } } } } } void setCheckboxImageList () { if ((style & SWT.CHECK) is 0) return; int count = 5, flags = 0; static if (OS.IsWinCE) { flags |= OS.ILC_COLOR; } else { if (OS.COMCTL32_MAJOR >= 6 && OS.IsAppThemed ()) { flags |= OS.ILC_COLOR32; } else { auto hDC = OS.GetDC (handle); int bits = OS.GetDeviceCaps (hDC, OS.BITSPIXEL); int planes = OS.GetDeviceCaps (hDC, OS.PLANES); OS.ReleaseDC (handle, hDC); int depth = bits * planes; switch (depth) { case 4: flags |= OS.ILC_COLOR4; break; case 8: flags |= OS.ILC_COLOR8; break; case 16: flags |= OS.ILC_COLOR16; break; case 24: flags |= OS.ILC_COLOR24; break; case 32: flags |= OS.ILC_COLOR32; break; default: flags |= OS.ILC_COLOR; break; } flags |= OS.ILC_MASK; } } if ((style & SWT.RIGHT_TO_LEFT) !is 0) flags |= OS.ILC_MIRROR; int height = OS.SendMessage (handle, OS.TVM_GETITEMHEIGHT, 0, 0), width = height; auto hStateList = OS.ImageList_Create (width, height, flags, count, count); auto hDC = OS.GetDC (handle); auto memDC = OS.CreateCompatibleDC (hDC); auto hBitmap = OS.CreateCompatibleBitmap (hDC, width * count, height); auto hOldBitmap = OS.SelectObject (memDC, hBitmap); RECT rect; OS.SetRect (&rect, 0, 0, width * count, height); /* * NOTE: DrawFrameControl() draws a black and white * mask when not drawing a push button. In order to * make the box surrounding the check mark transparent, * fill it with a color that is neither black or white. */ int clrBackground = 0; if (OS.COMCTL32_MAJOR >= 6 && OS.IsAppThemed ()) { Control control = findBackgroundControl (); if (control is null) control = this; clrBackground = control.getBackgroundPixel (); } else { clrBackground = 0x020000FF; if ((clrBackground & 0xFFFFFF) is OS.GetSysColor (OS.COLOR_WINDOW)) { clrBackground = 0x0200FF00; } } auto hBrush = OS.CreateSolidBrush (clrBackground); OS.FillRect (memDC, &rect, hBrush); OS.DeleteObject (hBrush); auto oldFont = OS.SelectObject (hDC, defaultFont ()); TEXTMETRIC tm; OS.GetTextMetrics (hDC, &tm); OS.SelectObject (hDC, oldFont); int itemWidth = Math.min (tm.tmHeight, width); int itemHeight = Math.min (tm.tmHeight, height); int left = (width - itemWidth) / 2, top = (height - itemHeight) / 2 + 1; OS.SetRect (&rect, left + width, top, left + width + itemWidth, top + itemHeight); if (OS.COMCTL32_MAJOR >= 6 && OS.IsAppThemed ()) { auto hTheme = display.hButtonTheme (); OS.DrawThemeBackground (hTheme, memDC, OS.BP_CHECKBOX, OS.CBS_UNCHECKEDNORMAL, &rect, null); rect.left += width; rect.right += width; OS.DrawThemeBackground (hTheme, memDC, OS.BP_CHECKBOX, OS.CBS_CHECKEDNORMAL, &rect, null); rect.left += width; rect.right += width; OS.DrawThemeBackground (hTheme, memDC, OS.BP_CHECKBOX, OS.CBS_UNCHECKEDNORMAL, &rect, null); rect.left += width; rect.right += width; OS.DrawThemeBackground (hTheme, memDC, OS.BP_CHECKBOX, OS.CBS_MIXEDNORMAL, &rect, null); } else { OS.DrawFrameControl (memDC, &rect, OS.DFC_BUTTON, OS.DFCS_BUTTONCHECK | OS.DFCS_FLAT); rect.left += width; rect.right += width; OS.DrawFrameControl (memDC, &rect, OS.DFC_BUTTON, OS.DFCS_BUTTONCHECK | OS.DFCS_CHECKED | OS.DFCS_FLAT); rect.left += width; rect.right += width; OS.DrawFrameControl (memDC, &rect, OS.DFC_BUTTON, OS.DFCS_BUTTONCHECK | OS.DFCS_INACTIVE | OS.DFCS_FLAT); rect.left += width; rect.right += width; OS.DrawFrameControl (memDC, &rect, OS.DFC_BUTTON, OS.DFCS_BUTTONCHECK | OS.DFCS_CHECKED | OS.DFCS_INACTIVE | OS.DFCS_FLAT); } OS.SelectObject (memDC, hOldBitmap); OS.DeleteDC (memDC); OS.ReleaseDC (handle, hDC); if (OS.COMCTL32_MAJOR >= 6 && OS.IsAppThemed ()) { OS.ImageList_Add (hStateList, hBitmap, null); } else { OS.ImageList_AddMasked (hStateList, hBitmap, clrBackground); } OS.DeleteObject (hBitmap); auto hOldStateList = cast(HANDLE) OS.SendMessage (handle, OS.TVM_GETIMAGELIST, OS.TVSIL_STATE, 0); OS.SendMessage (handle, OS.TVM_SETIMAGELIST, OS.TVSIL_STATE, hStateList); if (hOldStateList !is null) OS.ImageList_Destroy (hOldStateList); } override public void setFont (Font font) { checkWidget (); super.setFont (font); if ((style & SWT.CHECK) !is 0) setCheckboxImageList (); } override void setForegroundPixel (int pixel) { /* * Bug in Windows. When the tree is using the explorer * theme, it does not use COLOR_WINDOW_TEXT for the * foreground. When TVM_SETTEXTCOLOR is called with -1, * it resets the color to black, not COLOR_WINDOW_TEXT. * The fix is to explicitly set the color. */ if (explorerTheme) { if (pixel is -1) pixel = defaultForeground (); } OS.SendMessage (handle, OS.TVM_SETTEXTCOLOR, 0, pixel); } /** * Marks the receiver's header as visible if the argument is <code>true</code>, * and marks it invisible otherwise. * <p> * If one of the receiver's ancestors is not visible or some * other condition makes the receiver not visible, marking * it visible may not actually cause it to be displayed. * </p> * * @param show the new visibility state * * @exception SWTException <ul> * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> * </ul> * * @since 3.1 */ public void setHeaderVisible (bool show) { checkWidget (); if (hwndHeader is null) { if (!show) return; createParent (); } int bits = OS.GetWindowLong (hwndHeader, OS.GWL_STYLE); if (show) { if ((bits & OS.HDS_HIDDEN) is 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) !is 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 (bool 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. */ HANDLE hItem; if (redraw) { if (drawCount is 1) { int count = OS.SendMessage (handle, OS.TVM_GETCOUNT, 0, 0); if (count is 0) { TVINSERTSTRUCT tvInsert; tvInsert.hInsertAfter = cast(HTREEITEM) OS.TVI_FIRST; hItem = cast(HANDLE) OS.SendMessage (handle, OS.TVM_INSERTITEM, 0, &tvInsert); } OS.DefWindowProc (handle, OS.WM_SETREDRAW, 1, 0); updateScrollBar (); } } super.setRedraw (redraw); if (!redraw) { if (drawCount is 1) OS.DefWindowProc (handle, OS.WM_SETREDRAW, 0, 0); } if (hItem !is null) { ignoreShrink = true; OS.SendMessage (handle, OS.TVM_DELETEITEM, 0, hItem); ignoreShrink = false; } } void setScrollWidth () { if (hwndHeader is null || hwndParent is null) return; int width = 0; HDITEM hdItem; for (int i=0; i<columnCount; i++) { hdItem.mask = OS.HDI_WIDTH; OS.SendMessage (hwndHeader, OS.HDM_GETITEM, i, &hdItem); width += hdItem.cxy; } setScrollWidth (Math.max (scrollWidth, width)); } void setScrollWidth (int width) { if (hwndHeader is null || hwndParent is null) return; //TEMPORARY CODE //scrollWidth = width; int left = 0; RECT rect; SCROLLINFO info; info.cbSize = SCROLLINFO.sizeof; info.fMask = OS.SIF_RANGE | OS.SIF_PAGE; if (columnCount is 0 && width is 0) { OS.GetScrollInfo (hwndParent, OS.SB_HORZ, &info); info.nPage = info.nMax + 1; OS.SetScrollInfo (hwndParent, OS.SB_HORZ, &info, true); OS.GetScrollInfo (hwndParent, OS.SB_VERT, &info); info.nPage = info.nMax + 1; OS.SetScrollInfo (hwndParent, OS.SB_VERT, &info, true); } else { if ((style & SWT.H_SCROLL) !is 0) { OS.GetClientRect (hwndParent, &rect); OS.GetScrollInfo (hwndParent, OS.SB_HORZ, &info); info.nMax = width; info.nPage = rect.right - rect.left + 1; OS.SetScrollInfo (hwndParent, OS.SB_HORZ, &info, true); info.fMask = OS.SIF_POS; OS.GetScrollInfo (hwndParent, OS.SB_HORZ, &info); left = info.nPos; } } if (horizontalBar !is null) { horizontalBar.setIncrement (INCREMENT); horizontalBar.setPageIncrement (info.nPage); } OS.GetClientRect (hwndParent, &rect); HDLAYOUT playout; RECT layoutrect = rect; playout.prc = &layoutrect; WINDOWPOS pos; playout.pwpos = &pos; OS.SendMessage (hwndHeader, OS.HDM_LAYOUT, 0, &playout); SetWindowPos (hwndHeader, cast(HWND)OS.HWND_TOP, pos.x - left, pos.y, pos.cx + left, pos.cy, OS.SWP_NOACTIVATE); int bits = OS.GetWindowLong (handle, OS.GWL_EXSTYLE); int b = (bits & OS.WS_EX_CLIENTEDGE) !is 0 ? OS.GetSystemMetrics (OS.SM_CXEDGE) : 0; int w = pos.cx + (columnCount is 0 && width is 0 ? 0 : OS.GetSystemMetrics (OS.SM_CXVSCROLL)); int h = rect.bottom - rect.top - pos.cy; bool oldIgnore = ignoreResize; ignoreResize = true; SetWindowPos (handle, null, pos.x - left - b, pos.y + pos.cy - b, w + left + b * 2, h + b * 2, OS.SWP_NOACTIVATE | OS.SWP_NOZORDER); ignoreResize = oldIgnore; } void setSelection (HANDLE hItem, TVITEM* tvItem, TreeItem [] selection) { while (hItem !is null) { int index = 0; while (index < selection.length) { TreeItem item = selection [index]; if (item !is null && item.handle is hItem) break; index++; } tvItem.hItem = cast(HTREEITEM)hItem; OS.SendMessage (handle, OS.TVM_GETITEM, 0, &tvItem); if ((tvItem.state & OS.TVIS_SELECTED) !is 0) { if (index is selection.length) { tvItem.state = 0; OS.SendMessage (handle, OS.TVM_SETITEM, 0, &tvItem); } } else { if (index !is selection.length) { tvItem.state = OS.TVIS_SELECTED; OS.SendMessage (handle, OS.TVM_SETITEM, 0, &tvItem); } } auto hFirstItem = cast(HANDLE) OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_CHILD, hItem); setSelection (hFirstItem, tvItem, selection); hItem = cast(HANDLE) OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_NEXT, hItem); } } /** * Sets the receiver's selection to the given item. * The current selection is cleared before the new item is selected. * <p> * If the item is not in the receiver, then it is ignored. * </p> * * @param item the item to select * * @exception IllegalArgumentException <ul> * <li>ERROR_NULL_ARGUMENT - if the item is null</li> * <li>ERROR_INVALID_ARGUMENT - if the item has been disposed</li> * </ul> * @exception SWTException <ul> * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> * </ul> * * @since 3.2 */ public void setSelection (TreeItem item) { checkWidget (); if (item is null) error (SWT.ERROR_NULL_ARGUMENT); setSelection ([item]); } /** * Sets the receiver's selection to be the given array of items. * The current selection is cleared before the new items are selected. * <p> * Items that are not in the receiver are ignored. * If the receiver is single-select and multiple items are specified, * then all items are ignored. * </p> * * @param items the array of items * * @exception IllegalArgumentException <ul> * <li>ERROR_INVALID_ARGUMENT - if one of the items has been disposed</li> * </ul> * @exception SWTException <ul> * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> * </ul> * * @see Tree#deselectAll() */ public void setSelection (TreeItem [] items) { checkWidget (); // SWT extension: allow null array //if (items is null) error (SWT.ERROR_NULL_ARGUMENT); int length = items.length; if (length is 0 || ((style & SWT.SINGLE) !is 0 && length > 1)) { deselectAll(); return; } /* Select/deselect the first item */ TreeItem item = items [0]; if (item !is null) { if (item.isDisposed ()) error (SWT.ERROR_INVALID_ARGUMENT); HANDLE hOldItem = cast(HANDLE) OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_CARET, 0); HANDLE 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. */ bool 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) is 0) { OS.SendMessage (handle, OS.TVM_SELECTITEM, OS.TVGN_FIRSTVISIBLE, hNewItem); int /*long*/ hParent = OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_PARENT, hNewItem); if (hParent is 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 is hNewItem) { TVITEM tvItem; tvItem.mask = OS.TVIF_HANDLE | OS.TVIF_STATE; tvItem.state = OS.TVIS_SELECTED; tvItem.stateMask = OS.TVIS_SELECTED; tvItem.hItem = cast(HTREEITEM)hNewItem; OS.SendMessage (handle, OS.TVM_SETITEM, 0, &tvItem); showItem (hNewItem); } } if ((style & SWT.SINGLE) !is 0) return; /* Select/deselect the rest of the items */ TVITEM tvItem; tvItem.mask = OS.TVIF_HANDLE | OS.TVIF_STATE; tvItem.stateMask = OS.TVIS_SELECTED; int /*long*/ oldProc = OS.GetWindowLongPtr (handle, OS.GWLP_WNDPROC); OS.SetWindowLongPtr (handle, OS.GWLP_WNDPROC, cast(LONG_PTR)TreeProc); if ((style & SWT.VIRTUAL) !is 0) { HANDLE hItem = cast(HANDLE) OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_ROOT, 0); setSelection (hItem, &tvItem, items); } else { for (int i=0; i<this.items.length; i++) { item = this.items [i]; if (item !is null) { int index = 0; while (index < length) { if (items [index] is item) break; index++; } tvItem.hItem = cast(HTREEITEM)item.handle; OS.SendMessage (handle, OS.TVM_GETITEM, 0, &tvItem); if ((tvItem.state & OS.TVIS_SELECTED) !is 0) { if (index is length) { tvItem.state = 0; OS.SendMessage (handle, OS.TVM_SETITEM, 0, &tvItem); } } else { if (index !is length) { tvItem.state = OS.TVIS_SELECTED; OS.SendMessage (handle, OS.TVM_SETITEM, 0, &tvItem); } } } } } OS.SetWindowLongPtr (handle, OS.GWLP_WNDPROC, oldProc); } /** * Sets the column used by the sort indicator for the receiver. A null * value will clear the sort indicator. The current sort column is cleared * before the new column is set. * * @param column the column used by the sort indicator or <code>null</code> * * @exception IllegalArgumentException <ul> * <li>ERROR_INVALID_ARGUMENT - if the column is disposed</li> * </ul> * @exception SWTException <ul> * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> * </ul> * * @since 3.2 */ public void setSortColumn (TreeColumn column) { checkWidget (); if (column !is null && column.isDisposed ()) error (SWT.ERROR_INVALID_ARGUMENT); if (sortColumn !is null && !sortColumn.isDisposed ()) { sortColumn.setSortDirection (SWT.NONE); } sortColumn = column; if (sortColumn !is null && sortDirection !is SWT.NONE) { sortColumn.setSortDirection (sortDirection); } } /** * Sets the direction of the sort indicator for the receiver. The value * can be one of <code>UP</code>, <code>DOWN</code> or <code>NONE</code>. * * @param direction the direction of the sort indicator * * @exception SWTException <ul> * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> * </ul> * * @since 3.2 */ public void setSortDirection (int direction) { checkWidget (); if ((direction & (SWT.UP | SWT.DOWN)) is 0 && direction !is SWT.NONE) return; sortDirection = direction; if (sortColumn !is null && !sortColumn.isDisposed ()) { sortColumn.setSortDirection (direction); } } /** * Sets the item which is currently at the top of the receiver. * This item can change when items are expanded, collapsed, scrolled * or new items are added or removed. * * @param item the item to be shown * * @exception IllegalArgumentException <ul> * <li>ERROR_NULL_ARGUMENT - if the item is null</li> * <li>ERROR_INVALID_ARGUMENT - if the item has been disposed</li> * </ul> * @exception SWTException <ul> * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> * </ul> * * @see Tree#getTopItem() * * @since 2.1 */ public void setTopItem (TreeItem item) { checkWidget (); if (item is null) SWT.error (SWT.ERROR_NULL_ARGUMENT); if (item.isDisposed ()) SWT.error (SWT.ERROR_INVALID_ARGUMENT); HANDLE hItem = item.handle; auto hTopItem = cast(HANDLE) OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_FIRSTVISIBLE, 0); if (hItem is hTopItem) return; bool fixScroll = checkScroll (hItem), redraw = false; if (fixScroll) { OS.SendMessage (handle, OS.WM_SETREDRAW, 1, 0); OS.DefWindowProc (handle, OS.WM_SETREDRAW, 0, 0); } else { redraw = drawCount is 0 && OS.IsWindowVisible (handle); if (redraw) OS.DefWindowProc (handle, OS.WM_SETREDRAW, 0, 0); } OS.SendMessage (handle, OS.TVM_SELECTITEM, OS.TVGN_FIRSTVISIBLE, hItem); auto hParent = cast(HANDLE) OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_PARENT, hItem); if (hParent is null) 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 (HANDLE 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) is 0) { bool 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 is 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 { bool scroll = true; RECT itemRect; if (OS.TreeView_GetItemRect (handle, cast(HTREEITEM)hItem, &itemRect, true)) { forceResize (); RECT rect; OS.GetClientRect (handle, &rect); POINT pt; 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) { bool 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 !is null) { RECT itemRect; if (OS.TreeView_GetItemRect (handle, cast(HTREEITEM)hItem, &itemRect, true)) { forceResize (); RECT rect; OS.GetClientRect (hwndParent, &rect); OS.MapWindowPoints (hwndParent, handle, cast(POINT*) &rect, 2); POINT pt; pt.x = itemRect.left; pt.y = itemRect.top; if (!OS.PtInRect (&rect, pt)) { pt.y = itemRect.bottom; if (!OS.PtInRect (&rect, pt)) { SCROLLINFO info; info.cbSize = SCROLLINFO.sizeof; info.fMask = OS.SIF_POS; info.nPos = Math.max (0, pt.x - Tree.INSET / 2); OS.SetScrollInfo (hwndParent, OS.SB_HORZ, &info, true); setScrollWidth (); } } } } updateScrollBar (); } /** * Shows the column. If the column is already showing in the receiver, * this method simply returns. Otherwise, the columns are scrolled until * the column is visible. * * @param column the column to be shown * * @exception IllegalArgumentException <ul> * <li>ERROR_NULL_ARGUMENT - if the item is null</li> * <li>ERROR_INVALID_ARGUMENT - if the item has been disposed</li> * </ul> * @exception SWTException <ul> * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> * </ul> * * @since 3.1 */ public void showColumn (TreeColumn column) { checkWidget (); if (column is null) error (SWT.ERROR_NULL_ARGUMENT); if (column.isDisposed ()) error(SWT.ERROR_INVALID_ARGUMENT); if (column.parent !is this) return; int index = indexOf (column); if (index is -1) return; if (0 <= index && index < columnCount) { forceResize (); RECT rect; OS.GetClientRect (hwndParent, &rect); OS.MapWindowPoints (hwndParent, handle, cast(POINT*) &rect, 2); RECT headerRect; OS.SendMessage (hwndHeader, OS.HDM_GETITEMRECT, index, &headerRect); bool 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; info.cbSize = SCROLLINFO.sizeof; info.fMask = OS.SIF_POS; info.nPos = Math.max (0, headerRect.left - Tree.INSET / 2); OS.SetScrollInfo (hwndParent, OS.SB_HORZ, &info, true); setScrollWidth (); } } } /** * Shows the item. If the item is already showing in the receiver, * this method simply returns. Otherwise, the items are scrolled * and expanded until the item is visible. * * @param item the item to be shown * * @exception IllegalArgumentException <ul> * <li>ERROR_NULL_ARGUMENT - if the item is null</li> * <li>ERROR_INVALID_ARGUMENT - if the item has been disposed</li> * </ul> * @exception SWTException <ul> * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> * </ul> * * @see Tree#showSelection() */ public void showItem (TreeItem item) { checkWidget (); if (item is null) error (SWT.ERROR_NULL_ARGUMENT); if (item.isDisposed ()) error(SWT.ERROR_INVALID_ARGUMENT); showItem (item.handle); } /** * Shows the selection. If the selection is already showing in the receiver, * this method simply returns. Otherwise, the items are scrolled until * the selection is visible. * * @exception SWTException <ul> * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> * </ul> * * @see Tree#showItem(TreeItem) */ public void showSelection () { checkWidget (); HANDLE hItem; if ((style & SWT.SINGLE) !is 0) { hItem = cast(HANDLE) OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_CARET, 0); if (hItem is null) return; int state = 0; static if (OS.IsWinCE) { TVITEM tvItem; tvItem.hItem = hItem; tvItem.mask = OS.TVIF_STATE; OS.SendMessage (handle, OS.TVM_GETITEM, 0, &tvItem); state = tvItem.state; } else { state = OS.SendMessage (handle, OS.TVM_GETITEMSTATE, hItem, OS.TVIS_SELECTED); } if ((state & OS.TVIS_SELECTED) is 0) return; } else { int /*long*/ oldProc = OS.GetWindowLongPtr (handle, OS.GWLP_WNDPROC); OS.SetWindowLongPtr (handle, OS.GWLP_WNDPROC, cast(LONG_PTR)TreeProc); TVITEM tvItem_; TVITEM* tvItem; static if (OS.IsWinCE) { tvItem = &tvItem_; tvItem.mask = OS.TVIF_STATE; } if ((style & SWT.VIRTUAL) !is 0) { auto hRoot = cast(HANDLE) OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_ROOT, 0); hItem = getNextSelection (hRoot, tvItem); } else { //FIXME - this code expands first selected item it finds int index = 0; while (index <items.length) { TreeItem item = items [index]; if (item !is null) { int state = 0; static if (OS.IsWinCE) { tvItem.hItem = item.handle; OS.SendMessage (handle, OS.TVM_GETITEM, 0, tvItem); state = tvItem.state; } else { state = OS.SendMessage (handle, OS.TVM_GETITEMSTATE, item.handle, OS.TVIS_SELECTED); } if ((state & OS.TVIS_SELECTED) !is 0) { hItem = item.handle; break; } } index++; } } OS.SetWindowLongPtr (handle, OS.GWLP_WNDPROC, oldProc); } if (hItem !is null) showItem (hItem); } /*public*/ void sort () { checkWidget (); if ((style & SWT.VIRTUAL) !is 0) return; sort ( cast(HTREEITEM) OS.TVI_ROOT, false); } void sort (HANDLE hParent, bool all) { int itemCount = OS.SendMessage (handle, OS.TVM_GETCOUNT, 0, 0); if (itemCount is 0 || itemCount is 1) return; hFirstIndexOf = hLastIndexOf = null; itemCount = -1; if (sortDirection is SWT.UP || sortDirection is SWT.NONE) { OS.SendMessage (handle, OS.TVM_SORTCHILDREN, all ? 1 : 0, hParent); } else { //Callback compareCallback = new Callback (this, "CompareFunc", 3); //int lpfnCompare = compareCallback.getAddress (); sThis = this; TVSORTCB psort; psort.hParent = cast(HTREEITEM)hParent; psort.lpfnCompare = &CompareFunc; psort.lParam = sortColumn is null ? 0 : indexOf (sortColumn); OS.SendMessage (handle, OS.TVM_SORTCHILDRENCB, all ? 1 : 0, &psort); sThis = null; //compareCallback.dispose (); } } override void subclass () { super.subclass (); if (hwndHeader !is null) { OS.SetWindowLongPtr (hwndHeader, OS.GWLP_WNDPROC, display.windowProc); } } RECT* toolTipInset (RECT* rect) { if (!OS.IsWinCE && OS.WIN32_VERSION >= OS.VERSION (6, 0)) { RECT* insetRect = new RECT(); OS.SetRect (insetRect, rect.left - 1, rect.top - 1, rect.right + 1, rect.bottom + 1); return insetRect; } return rect; } RECT* toolTipRect (RECT* rect) { RECT* toolRect = new RECT (); if (!OS.IsWinCE && OS.WIN32_VERSION >= OS.VERSION (6, 0)) { OS.SetRect (toolRect, rect.left - 1, rect.top - 1, rect.right + 1, rect.bottom + 1); } else { OS.SetRect (toolRect, rect.left, rect.top, rect.right, rect.bottom); int dwStyle = OS.GetWindowLong (itemToolTipHandle, OS.GWL_STYLE); int dwExStyle = OS.GetWindowLong (itemToolTipHandle, OS.GWL_EXSTYLE); OS.AdjustWindowRectEx (toolRect, dwStyle, false, dwExStyle); } return toolRect; } override String toolTipText (NMTTDISPINFO* hdr) { auto hwndToolTip = cast(HWND) OS.SendMessage (handle, OS.TVM_GETTOOLTIPS, 0, 0); if (hwndToolTip is hdr.hdr.hwndFrom && toolTipText_ !is null) return ""; //$NON-NLS-1$ if (headerToolTipHandle is hdr.hdr.hwndFrom) { for (int i=0; i<columnCount; i++) { TreeColumn column = columns [i]; if (column.id is hdr.hdr.idFrom) return column.toolTipText; } return super.toolTipText (hdr); } if (itemToolTipHandle is hdr.hdr.hwndFrom) { if (toolTipText_ !is null) return ""; int pos = OS.GetMessagePos (); POINT pt; OS.POINTSTOPOINT (pt, pos); OS.ScreenToClient (handle, &pt); int index; TreeItem item; RECT* cellRect, itemRect; if (findCell (pt.x, pt.y, item, index, cellRect, itemRect)) { String text = null; if (index is 0) { text = item.text; } else { String[] strings = item.strings; if (strings !is null) text = strings [index]; } //TEMPORARY CODE if (!OS.IsWinCE && OS.WIN32_VERSION >= OS.VERSION (6, 0)) { if (isCustomToolTip ()) text = " "; } if (text !is null) return text; } } return super.toolTipText (hdr); } override HWND topHandle () { return hwndParent !is null ? hwndParent : handle; } void updateFullSelection () { if ((style & SWT.FULL_SELECTION) !is 0) { int oldBits = OS.GetWindowLong (handle, OS.GWL_STYLE), newBits = oldBits; if ((newBits & OS.TVS_FULLROWSELECT) !is 0) { if (!OS.IsWindowEnabled (handle) || findImageControl () !is null) { if (!explorerTheme) newBits &= ~OS.TVS_FULLROWSELECT; } } else { if (OS.IsWindowEnabled (handle) && findImageControl () is null) { if (!hooks (SWT.EraseItem) && !hooks (SWT.PaintItem)) { newBits |= OS.TVS_FULLROWSELECT; } } } if (newBits !is oldBits) { OS.SetWindowLong (handle, OS.GWL_STYLE, newBits); OS.InvalidateRect (handle, null, true); } } } void updateHeaderToolTips () { if (headerToolTipHandle is null) return; RECT rect; TOOLINFO lpti; lpti.cbSize = OS.TOOLINFO_sizeof; lpti.uFlags = OS.TTF_SUBCLASS; lpti.hwnd = hwndHeader; lpti.lpszText = OS.LPSTR_TEXTCALLBACK; for (int i=0; i<columnCount; i++) { TreeColumn column = columns [i]; if (OS.SendMessage (hwndHeader, OS.HDM_GETITEMRECT, i, &rect) !is 0) { lpti.uId = column.id = display.nextToolTipId++; lpti.rect.left = rect.left; lpti.rect.top = rect.top; lpti.rect.right = rect.right; lpti.rect.bottom = rect.bottom; OS.SendMessage (headerToolTipHandle, OS.TTM_ADDTOOL, 0, &lpti); } } } void updateImageList () { if (imageList is null) return; if (hwndHeader is null) return; int i = 0, index = OS.SendMessage (hwndHeader, OS.HDM_ORDERTOINDEX, 0, 0); while (i < items.length) { TreeItem item = items [i]; if (item !is null) { Image image = null; if (index is 0) { image = item.image; } else { Image [] images = item.images; if (images !is null) image = images [index]; } if (image !is null) break; } i++; } /* * Feature in Windows. When setting the same image list multiple * times, Windows does work making this operation slow. The fix * is to test for the same image list before setting the new one. */ HBITMAP hImageList = i is items.length ? null : imageList.getHandle (); HANDLE hOldImageList = cast(HANDLE) OS.SendMessage (handle, OS.TVM_GETIMAGELIST, OS.TVSIL_NORMAL, 0); if (hImageList !is hOldImageList) { OS.SendMessage (handle, OS.TVM_SETIMAGELIST, OS.TVSIL_NORMAL, hImageList); } } override void updateImages () { if (sortColumn !is null && !sortColumn.isDisposed ()) { if (OS.COMCTL32_MAJOR < 6) { switch (sortDirection) { case SWT.UP: case SWT.DOWN: sortColumn.setImage (display.getSortImage (sortDirection), true, true); break; default: } } } } void updateScrollBar () { if (hwndParent !is null) { if (columnCount !is 0 || scrollWidth !is 0) { SCROLLINFO info; info.cbSize = SCROLLINFO.sizeof; info.fMask = OS.SIF_ALL; int itemCount = OS.SendMessage (handle, OS.TVM_GETCOUNT, 0, 0); if (itemCount is 0) { OS.GetScrollInfo (hwndParent, OS.SB_VERT, &info); info.nPage = info.nMax + 1; OS.SetScrollInfo (hwndParent, OS.SB_VERT, &info, true); } else { OS.GetScrollInfo (handle, OS.SB_VERT, &info); if (!OS.IsWinCE && OS.WIN32_VERSION >= OS.VERSION(4, 10)) { if (info.nPage is 0) { SCROLLBARINFO psbi; psbi.cbSize = SCROLLBARINFO.sizeof; OS.GetScrollBarInfo (handle, OS.OBJID_VSCROLL, &psbi); if ((psbi.rgstate [0] & OS.STATE_SYSTEM_INVISIBLE) !is 0) { info.nPage = info.nMax + 1; } } } OS.SetScrollInfo (hwndParent, OS.SB_VERT, &info, true); } } } } override void unsubclass () { super.unsubclass (); if (hwndHeader !is null) { OS.SetWindowLongPtr (hwndHeader, OS.GWLP_WNDPROC, cast(LONG_PTR)HeaderProc); } } override int widgetStyle () { int bits = super.widgetStyle () | OS.TVS_SHOWSELALWAYS | OS.TVS_LINESATROOT | OS.TVS_HASBUTTONS | OS.TVS_NONEVENHEIGHT; if (EXPLORER_THEME && !OS.IsWinCE && OS.WIN32_VERSION >= OS.VERSION (6, 0) && OS.IsAppThemed ()) { bits |= OS.TVS_TRACKSELECT; if ((style & SWT.FULL_SELECTION) !is 0) bits |= OS.TVS_FULLROWSELECT; } else { if ((style & SWT.FULL_SELECTION) !is 0) { bits |= OS.TVS_FULLROWSELECT; } else { bits |= OS.TVS_HASLINES; } } if ((style & (SWT.H_SCROLL | SWT.V_SCROLL)) is 0) { bits &= ~(OS.WS_HSCROLL | OS.WS_VSCROLL); bits |= OS.TVS_NOSCROLL; } else { if ((style & SWT.H_SCROLL) is 0) { bits &= ~OS.WS_HSCROLL; bits |= OS.TVS_NOHSCROLL; } } // bits |= OS.TVS_NOTOOLTIPS | OS.TVS_DISABLEDRAGDROP; return bits | OS.TVS_DISABLEDRAGDROP; } override String windowClass () { return TCHARsToStr(TreeClass); } override int windowProc () { return cast(int) TreeProc; } override int windowProc (HWND hwnd, int msg, int wParam, int lParam) { if (hwndHeader !is null && hwnd is hwndHeader) { switch (msg) { /* This code is intentionally commented */ // case OS.WM_CONTEXTMENU: { // LRESULT result = wmContextMenu (hwnd, wParam, lParam); // if (result !is null) return result.value; // break; // } case OS.WM_CAPTURECHANGED: { /* * Bug in Windows. When the capture changes during a * header drag, Windows does not redraw the header item * such that the header remains pressed. For example, * when focus is assigned to a push button, the mouse is * pressed (but not released), then the SPACE key is * pressed to activate the button, the capture changes, * the header not notified and NM_RELEASEDCAPTURE is not * sent. The fix is to redraw the header when the capture * changes to another control. * * This does not happen on XP. */ if (OS.COMCTL32_MAJOR < 6) { if (lParam !is 0 && cast(HANDLE) lParam !is hwndHeader) { OS.InvalidateRect (hwndHeader, null, true); } } break; } case OS.WM_MOUSELEAVE: { /* * Bug in Windows. On XP, when a tooltip is hidden * due to a time out or mouse press, the tooltip * remains active although no longer visible and * won't show again until another tooltip becomes * active. The fix is to reset the tooltip bounds. */ if (OS.COMCTL32_MAJOR >= 6) updateHeaderToolTips (); updateHeaderToolTips (); break; } case OS.WM_NOTIFY: { NMHDR* hdr = cast(NMHDR*)lParam; //OS.MoveMemory (hdr, lParam, NMHDR.sizeof); switch (hdr.code) { case OS.TTN_SHOW: case OS.TTN_POP: case OS.TTN_GETDISPINFOA: case OS.TTN_GETDISPINFOW: return OS.SendMessage (handle, msg, wParam, lParam); default: } break; } case OS.WM_SETCURSOR: { if (cast(HWND)wParam is hwnd) { int hitTest = cast(short) OS.LOWORD (lParam); if (hitTest is OS.HTCLIENT) { HDHITTESTINFO pinfo; int pos = OS.GetMessagePos (); POINT pt; OS.POINTSTOPOINT (pt, pos); OS.ScreenToClient (hwnd, &pt); pinfo.pt.x = pt.x; pinfo.pt.y = pt.y; int index = OS.SendMessage (hwndHeader, OS.HDM_HITTEST, 0, &pinfo); if (0 <= index && index < columnCount && !columns [index].resizable) { if ((pinfo.flags & (OS.HHT_ONDIVIDER | OS.HHT_ONDIVOPEN)) !is 0) { OS.SetCursor (OS.LoadCursor (null, cast(TCHAR*) OS.IDC_ARROW)); return 1; } } } } break; } default: } return callWindowProc (hwnd, msg, wParam, lParam); } if (hwndParent !is null && hwnd is hwndParent) { switch (msg) { case OS.WM_MOVE: { sendEvent (SWT.Move); return 0; } case OS.WM_SIZE: { setScrollWidth (); if (ignoreResize) return 0; setResizeChildren (false); int /*long*/ code = callWindowProc (hwnd, OS.WM_SIZE, wParam, lParam); sendEvent (SWT.Resize); if (isDisposed ()) return 0; if (layout_ !is null) { markLayout (false, false); updateLayout (false, false); } setResizeChildren (true); updateScrollBar (); return code; } case OS.WM_NCPAINT: { LRESULT result = wmNCPaint (hwnd, wParam, lParam); if (result !is null) return result.value; break; } case OS.WM_PRINT: { LRESULT result = wmPrint (hwnd, wParam, lParam); if (result !is null) return result.value; break; } case OS.WM_COMMAND: case OS.WM_NOTIFY: case OS.WM_SYSCOLORCHANGE: { return OS.SendMessage (handle, msg, wParam, lParam); } case OS.WM_HSCROLL: { /* * Bug on WinCE. lParam should be NULL when the message is not sent * by a scroll bar control, but it contains the handle to the window. * When the message is sent by a scroll bar control, it correctly * contains the handle to the scroll bar. The fix is to check for * both. */ if (horizontalBar !is null && (lParam is 0 || lParam is cast(int)hwndParent)) { wmScroll (horizontalBar, true, hwndParent, OS.WM_HSCROLL, wParam, lParam); } setScrollWidth (); break; } case OS.WM_VSCROLL: { SCROLLINFO info; info.cbSize = SCROLLINFO.sizeof; info.fMask = OS.SIF_ALL; OS.GetScrollInfo (hwndParent, OS.SB_VERT, &info); /* * Update the nPos field to match the nTrackPos field * so that the tree scrolls when the scroll bar of the * parent is dragged. * * NOTE: For some reason, this code is only necessary * on Windows Vista. */ if (!OS.IsWinCE && OS.WIN32_VERSION >= OS.VERSION (6, 0)) { if (OS.LOWORD (wParam) is OS.SB_THUMBTRACK) { info.nPos = info.nTrackPos; } } OS.SetScrollInfo (handle, OS.SB_VERT, &info, true); int /*long*/ code = OS.SendMessage (handle, OS.WM_VSCROLL, wParam, lParam); OS.GetScrollInfo (handle, OS.SB_VERT, &info); OS.SetScrollInfo (hwndParent, OS.SB_VERT, &info, true); return code; } default: } return callWindowProc (hwnd, msg, wParam, lParam); } if (msg is Display.DI_GETDRAGIMAGE) { /* * When there is more than one item selected, DI_GETDRAGIMAGE * returns the item under the cursor. This happens because * the tree does not have implement multi-select. The fix * is to disable DI_GETDRAGIMAGE when more than one item is * selected. */ if ((style & SWT.MULTI) !is 0 || hooks (SWT.EraseItem) || hooks (SWT.PaintItem)) { auto hItem = cast(HANDLE) OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_FIRSTVISIBLE, 0); TreeItem [] items = new TreeItem [10]; TVITEM tvItem; tvItem.mask = OS.TVIF_HANDLE | OS.TVIF_PARAM | OS.TVIF_STATE; int count = getSelection (hItem, &tvItem, items, 0, 10, false, true); if (count is 0) return 0; POINT mousePos; OS.POINTSTOPOINT (mousePos, OS.GetMessagePos ()); OS.MapWindowPoints (null, handle, &mousePos, 1); RECT clientRect; OS.GetClientRect(handle, &clientRect); RECT rect = *(items [0].getBounds (0, true, true, false)); if ((style & SWT.FULL_SELECTION) !is 0) { int width = DRAG_IMAGE_SIZE; rect.left = Math.max (clientRect.left, mousePos.x - width / 2); if (clientRect.right > rect.left + width) { rect.right = rect.left + width; } else { rect.right = clientRect.right; rect.left = Math.max (clientRect.left, rect.right - width); } } auto 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) !is 0) { itemRect.left = rect.left; itemRect.right = rect.right; } auto 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 */ auto hdc = OS.GetDC (handle); auto memHdc = OS.CreateCompatibleDC (hdc); BITMAPINFOHEADER bmiHeader; 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.ptr, &bmiHeader, BITMAPINFOHEADER.sizeof); void* [1] pBits; auto memDib = OS.CreateDIBSection (null, cast(BITMAPINFO*) bmi.ptr, OS.DIB_RGB_COLORS, pBits.ptr, null, 0); if (memDib is null) SWT.error (SWT.ERROR_NO_HANDLES); auto oldMemBitmap = OS.SelectObject (memHdc, memDib); int colorKey = 0x0000FD; POINT pt; 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 (null, hdc); OS.DeleteObject (hRgn); SHDRAGIMAGE shdi; 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) !is 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 (int wParam, int lParam) { LRESULT result = super.WM_CHAR (wParam, lParam); if (result !is 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 (wParam) { case ' ': { HANDLE hItem = cast(HANDLE) OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_CARET, 0); if (hItem !is null) { hAnchor = hItem; OS.SendMessage (handle, OS.TVM_ENSUREVISIBLE, 0, hItem); TVITEM tvItem; tvItem.mask = OS.TVIF_HANDLE | OS.TVIF_STATE | OS.TVIF_PARAM; tvItem.hItem = cast(HTREEITEM)hItem; if ((style & SWT.CHECK) !is 0) { tvItem.stateMask = OS.TVIS_STATEIMAGEMASK; OS.SendMessage (handle, OS.TVM_GETITEM, 0, &tvItem); int state = tvItem.state >> 12; if ((state & 0x1) !is 0) { state++; } else { --state; } tvItem.state = state << 12; OS.SendMessage (handle, OS.TVM_SETITEM, 0, &tvItem); static if (!OS.IsWinCE) { int id = cast(int) hItem; if (OS.COMCTL32_MAJOR >= 6) { id = OS.SendMessage (handle, OS.TVM_MAPHTREEITEMTOACCID, hItem, 0); } OS.NotifyWinEvent (OS.EVENT_OBJECT_FOCUS, handle, OS.OBJID_CLIENT, id); } } tvItem.stateMask = OS.TVIS_SELECTED; OS.SendMessage (handle, OS.TVM_GETITEM, 0, &tvItem); if ((style & SWT.MULTI) !is 0 && OS.GetKeyState (OS.VK_CONTROL) < 0) { if ((tvItem.state & OS.TVIS_SELECTED) !is 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, tvItem.lParam); Event event = new Event (); event.item = item; postEvent (SWT.Selection, event); if ((style & SWT.CHECK) !is 0) { event = new Event (); event.item = item; event.detail = SWT.CHECK; postEvent (SWT.Selection, event); } } return LRESULT.ZERO; } case SWT.CR: { /* * Feature in Windows. Windows sends NM_RETURN from WM_KEYDOWN * instead of using WM_CHAR. This means that application code * that expects to consume the key press and therefore avoid a * SWT.DefaultSelection event from WM_CHAR will fail. The fix * is to implement SWT.DefaultSelection in WM_CHAR instead of * using NM_RETURN. */ Event event = new Event (); HANDLE hItem = cast(HANDLE) OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_CARET, 0); if (hItem !is null) event.item = _getItem (hItem); postEvent (SWT.DefaultSelection, event); return LRESULT.ZERO; } case SWT.ESC: return LRESULT.ZERO; default: } return result; } override LRESULT WM_ERASEBKGND (int wParam, int lParam) { LRESULT result = super.WM_ERASEBKGND (wParam, lParam); if ((style & SWT.DOUBLE_BUFFERED) !is 0) return LRESULT.ONE; if (findImageControl () !is null) return LRESULT.ONE; return result; } override LRESULT WM_GETOBJECT (int wParam, int 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) !is 0 || hwndParent !is null) { if (accessible is null) accessible = new_Accessible (this); } return super.WM_GETOBJECT (wParam, lParam); } override LRESULT WM_HSCROLL (int wParam, int lParam) { bool fixScroll = false; if ((style & SWT.DOUBLE_BUFFERED) !is 0) { fixScroll = (style & SWT.VIRTUAL) !is 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 !is null) return result; return result; } override LRESULT WM_KEYDOWN (int /*long*/ wParam, int /*long*/ lParam) { LRESULT result = super.WM_KEYDOWN (wParam, lParam); if (result !is null) return result; switch (wParam) { case OS.VK_SPACE: /* * Ensure that the window proc does not process VK_SPACE * so that it can be handled in WM_CHAR. This allows the * application to cancel an operation that is normally * performed in WM_KEYDOWN from WM_CHAR. */ return LRESULT.ZERO; case OS.VK_ADD: if (OS.GetKeyState (OS.VK_CONTROL) < 0) { if (hwndHeader !is null) { TreeColumn [] newColumns = new TreeColumn [columnCount]; System.arraycopy (columns, 0, newColumns, 0, columnCount); for (int i=0; i<columnCount; i++) { TreeColumn column = newColumns [i]; if (!column.isDisposed () && column.getResizable ()) { column.pack (); } } } } break; case OS.VK_UP: case OS.VK_DOWN: case OS.VK_PRIOR: case OS.VK_NEXT: case OS.VK_HOME: case OS.VK_END: { OS.SendMessage (handle, OS.WM_CHANGEUISTATE, OS.UIS_INITIALIZE, 0); if ((style & SWT.SINGLE) !is 0) break; if (OS.GetKeyState (OS.VK_SHIFT) < 0) { HANDLE hItem = cast(HANDLE) OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_CARET, 0); if (hItem !is null) { if (hAnchor is null) hAnchor = hItem; ignoreSelect = ignoreDeselect = true; int /*long*/ code = callWindowProc (handle, OS.WM_KEYDOWN, wParam, lParam); ignoreSelect = ignoreDeselect = false; auto hNewItem = cast(HANDLE) OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_CARET, 0); TVITEM tvItem; tvItem.mask = OS.TVIF_HANDLE | OS.TVIF_STATE; tvItem.stateMask = OS.TVIS_SELECTED; auto hDeselectItem = hItem; RECT rect1; if (!OS.TreeView_GetItemRect (handle, cast(HTREEITEM)hAnchor, &rect1, false)) { hAnchor = hItem; OS.TreeView_GetItemRect (handle, cast(HTREEITEM)hAnchor, &rect1, false); } RECT rect2; OS.TreeView_GetItemRect (handle, cast(HTREEITEM)hDeselectItem, &rect2, false); int flags = rect1.top < rect2.top ? OS.TVGN_PREVIOUSVISIBLE : OS.TVGN_NEXTVISIBLE; while (hDeselectItem !is hAnchor) { tvItem.hItem = cast(HTREEITEM)hDeselectItem; OS.SendMessage (handle, OS.TVM_SETITEM, 0, &tvItem); hDeselectItem = cast(HANDLE) OS.SendMessage (handle, OS.TVM_GETNEXTITEM, flags, hDeselectItem); } auto hSelectItem = hAnchor; OS.TreeView_GetItemRect (handle, cast(HTREEITEM)hNewItem, &rect1, false); OS.TreeView_GetItemRect (handle, cast(HTREEITEM)hSelectItem, &rect2, false); tvItem.state = OS.TVIS_SELECTED; flags = rect1.top < rect2.top ? OS.TVGN_PREVIOUSVISIBLE : OS.TVGN_NEXTVISIBLE; while (hSelectItem !is hNewItem) { tvItem.hItem = cast(HTREEITEM)hSelectItem; OS.SendMessage (handle, OS.TVM_SETITEM, 0, &tvItem); hSelectItem = cast(HANDLE) OS.SendMessage (handle, OS.TVM_GETNEXTITEM, flags, hSelectItem); } tvItem.hItem = cast(HTREEITEM)hNewItem; OS.SendMessage (handle, OS.TVM_SETITEM, 0, &tvItem); tvItem.mask = OS.TVIF_HANDLE | OS.TVIF_PARAM; tvItem.hItem = cast(HTREEITEM)hNewItem; OS.SendMessage (handle, OS.TVM_GETITEM, 0, &tvItem); Event event = new Event (); event.item = _getItem (hNewItem, tvItem.lParam); postEvent (SWT.Selection, event); return new LRESULT (code); } } if (OS.GetKeyState (OS.VK_CONTROL) < 0) { HANDLE hItem = cast(HANDLE) OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_CARET, 0); if (hItem !is null) { TVITEM tvItem; tvItem.mask = OS.TVIF_HANDLE | OS.TVIF_STATE; tvItem.stateMask = OS.TVIS_SELECTED; tvItem.hItem = cast(HTREEITEM)hItem; OS.SendMessage (handle, OS.TVM_GETITEM, 0, &tvItem); bool oldSelected = (tvItem.state & OS.TVIS_SELECTED) !is 0; HANDLE hNewItem; switch (wParam) { case OS.VK_UP: hNewItem = cast(HANDLE) OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_PREVIOUSVISIBLE, hItem); break; case OS.VK_DOWN: hNewItem = cast(HANDLE) OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_NEXTVISIBLE, hItem); break; case OS.VK_HOME: hNewItem = cast(HANDLE) OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_ROOT, 0); break; case OS.VK_PRIOR: hNewItem = cast(HANDLE) OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_FIRSTVISIBLE, 0); if (hNewItem is hItem) { OS.SendMessage (handle, OS.WM_VSCROLL, OS.SB_PAGEUP, 0); hNewItem = cast(HANDLE) OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_FIRSTVISIBLE, 0); } break; case OS.VK_NEXT: RECT rect, clientRect; OS.GetClientRect (handle, &clientRect); hNewItem = cast(HANDLE) OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_FIRSTVISIBLE, 0); do { auto hVisible = cast(HANDLE) OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_NEXTVISIBLE, hNewItem); if (hVisible is null) break; if (!OS.TreeView_GetItemRect (handle, cast(HTREEITEM)hVisible, &rect, false)) break; if (rect.bottom > clientRect.bottom) break; if ((hNewItem = hVisible) is hItem) { OS.SendMessage (handle, OS.WM_VSCROLL, OS.SB_PAGEDOWN, 0); } } while (hNewItem !is null); break; case OS.VK_END: hNewItem = cast(HANDLE) OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_LASTVISIBLE, 0); break; default: } if (hNewItem !is null) { OS.SendMessage (handle, OS.TVM_ENSUREVISIBLE, 0, hNewItem); tvItem.hItem = cast(HTREEITEM)hNewItem; OS.SendMessage (handle, OS.TVM_GETITEM, 0, &tvItem); bool newSelected = (tvItem.state & OS.TVIS_SELECTED) !is 0; bool redraw = !newSelected && drawCount is 0 && OS.IsWindowVisible (handle); if (redraw) { OS.UpdateWindow (handle); OS.DefWindowProc (handle, OS.WM_SETREDRAW, 0, 0); } hSelect = hNewItem; ignoreSelect = true; OS.SendMessage (handle, OS.TVM_SELECTITEM, OS.TVGN_CARET, hNewItem); ignoreSelect = false; hSelect = null; if (oldSelected) { tvItem.state = OS.TVIS_SELECTED; tvItem.hItem = cast(HTREEITEM)hItem; OS.SendMessage (handle, OS.TVM_SETITEM, 0, &tvItem); } if (!newSelected) { tvItem.state = 0; tvItem.hItem = cast(HTREEITEM)hNewItem; OS.SendMessage (handle, OS.TVM_SETITEM, 0, &tvItem); } if (redraw) { RECT rect1, rect2; bool fItemRect = (style & SWT.FULL_SELECTION) is 0; if (hooks (SWT.EraseItem) || hooks (SWT.PaintItem)) fItemRect = false; if (!OS.IsWinCE && OS.WIN32_VERSION >= OS.VERSION (6, 0)) fItemRect = false; OS.TreeView_GetItemRect (handle, cast(HTREEITEM)hItem, &rect1, fItemRect); OS.TreeView_GetItemRect (handle, cast(HTREEITEM)hNewItem, &rect2, fItemRect); OS.DefWindowProc (handle, OS.WM_SETREDRAW, 1, 0); OS.InvalidateRect (handle, &rect1, true); OS.InvalidateRect (handle, &rect2, true); OS.UpdateWindow (handle); } return LRESULT.ZERO; } } } int /*long*/ code = callWindowProc (handle, OS.WM_KEYDOWN, wParam, lParam); hAnchor = cast(HANDLE) OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_CARET, 0); return new LRESULT (code); } default: } return result; } override LRESULT WM_KILLFOCUS (int wParam, int 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. */ bool redraw = (style & SWT.MULTI) !is 0; if (!redraw) { if (!OS.IsWinCE && OS.COMCTL32_MAJOR >= 6) { if (imageList !is null) { int bits = OS.GetWindowLong (handle, OS.GWL_STYLE); if ((bits & OS.TVS_FULLROWSELECT) is 0) { redraw = true; } } } } if (redraw) redrawSelection (); return super.WM_KILLFOCUS (wParam, lParam); } override LRESULT WM_LBUTTONDBLCLK (int wParam, int lParam) { TVHITTESTINFO lpht; lpht.pt.x = OS.GET_X_LPARAM (lParam); lpht.pt.y = OS.GET_Y_LPARAM (lParam); OS.SendMessage (handle, OS.TVM_HITTEST, 0, &lpht); if (lpht.hItem !is null) { if ((style & SWT.CHECK) !is 0) { if ((lpht.flags & OS.TVHT_ONITEMSTATEICON) !is 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 () !is handle) OS.SetCapture (handle); } return LRESULT.ZERO; } if (!display.captureChanged && !isDisposed ()) { if (OS.GetCapture () !is handle) OS.SetCapture (handle); } OS.SetFocus (handle); TVITEM 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) !is 0) { state++; } else { --state; } tvItem.state = state << 12; OS.SendMessage (handle, OS.TVM_SETITEM, 0, &tvItem); static if (!OS.IsWinCE) { int id = cast(int) tvItem.hItem; if (OS.COMCTL32_MAJOR >= 6) { id = OS.SendMessage (handle, OS.TVM_MAPHTREEITEMTOACCID, tvItem.hItem, 0); } OS.NotifyWinEvent (OS.EVENT_OBJECT_FOCUS, handle, OS.OBJID_CLIENT, id); } Event event = new Event (); event.item = _getItem (tvItem.hItem, tvItem.lParam); event.detail = SWT.CHECK; postEvent (SWT.Selection, event); return LRESULT.ZERO; } } } LRESULT result = super.WM_LBUTTONDBLCLK (wParam, lParam); if (result is LRESULT.ZERO) return result; if (lpht.hItem !is null) { int flags = OS.TVHT_ONITEM; if ((style & SWT.FULL_SELECTION) !is 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.pt.x, lpht.pt.y)) { lpht.flags |= OS.TVHT_ONITEMICON | OS.TVHT_ONITEMLABEL; } } } if ((lpht.flags & flags) !is 0) { Event event = new Event (); event.item = _getItem (lpht.hItem); postEvent (SWT.DefaultSelection, event); } } return result; } override LRESULT WM_LBUTTONDOWN (int wParam, int 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; lpht.pt.x = OS.GET_X_LPARAM (lParam); lpht.pt.y = OS.GET_Y_LPARAM (lParam); OS.SendMessage (handle, OS.TVM_HITTEST, 0, &lpht); if (lpht.hItem is null || (lpht.flags & OS.TVHT_ONITEMBUTTON) !is 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 () !is handle) OS.SetCapture (handle); } return LRESULT.ZERO; } bool fixSelection = false, deselected = false; HANDLE hOldSelection = cast(HANDLE) OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_CARET, 0); if (lpht.hItem !is null && (style & SWT.MULTI) !is 0) { if (hOldSelection !is null) { TVITEM 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) !is 0) { fixSelection = true; tvItem.stateMask = OS.TVIS_SELECTED; auto hNext = cast(HANDLE) OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_NEXTVISIBLE, lpht.hItem); while (hNext !is null) { if (hNext is hAnchor) hAnchor = null; tvItem.hItem = cast(HTREEITEM)hNext; OS.SendMessage (handle, OS.TVM_GETITEM, 0, &tvItem); if ((tvItem.state & OS.TVIS_SELECTED) !is 0) deselected = true; tvItem.state = 0; OS.SendMessage (handle, OS.TVM_SETITEM, 0, &tvItem); HANDLE hItem = hNext = cast(HANDLE) OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_NEXTVISIBLE, hNext); while (hItem !is null && hItem !is lpht.hItem) { hItem = cast(HANDLE) OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_PARENT, hItem); } if (hItem is null) break; } } } } dragStarted = gestureCompleted = false; if (fixSelection) ignoreDeselect = ignoreSelect = lockSelection = true; int /*long*/ code = callWindowProc (handle, OS.WM_LBUTTONDOWN, wParam, lParam); if (!OS.IsWinCE && OS.WIN32_VERSION >= OS.VERSION (6, 0)) { if (OS.GetFocus () !is handle) OS.SetFocus (handle); } if (fixSelection) ignoreDeselect = ignoreSelect = lockSelection = false; HANDLE hNewSelection = cast(HANDLE) OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_CARET, 0); if (hOldSelection !is hNewSelection) hAnchor = hNewSelection; if (dragStarted) { if (!display.captureChanged && !isDisposed ()) { if (OS.GetCapture () !is 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) !is 0) { int bits = OS.GetWindowLong (handle, OS.GWL_STYLE); if ((bits & OS.TVS_FULLROWSELECT) is 0) { if (OS.SendMessage (handle, OS.TVM_GETIMAGELIST, OS.TVSIL_NORMAL, 0) is 0) { auto hItem = cast(HANDLE) OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_CARET, 0); if (hItem !is null) { RECT rect; if (OS.TreeView_GetItemRect (handle, cast(HTREEITEM)hItem, &rect, false)) { OS.InvalidateRect (handle, &rect, true); } } } } } if (deselected) { Event event = new Event (); event.item = _getItem (lpht.hItem); postEvent (SWT.Selection, event); } return new LRESULT (code); } /* Look for check/uncheck */ if ((style & SWT.CHECK) !is 0) { if ((lpht.flags & OS.TVHT_ONITEMSTATEICON) !is 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 () !is handle) OS.SetCapture (handle); } return LRESULT.ZERO; } if (!display.captureChanged && !isDisposed ()) { if (OS.GetCapture () !is handle) OS.SetCapture (handle); } OS.SetFocus (handle); TVITEM 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) !is 0) { state++; } else { --state; } tvItem.state = state << 12; OS.SendMessage (handle, OS.TVM_SETITEM, 0, &tvItem); static if (!OS.IsWinCE) { int id = cast(int) tvItem.hItem; if (OS.COMCTL32_MAJOR >= 6) { id = OS.SendMessage (handle, OS.TVM_MAPHTREEITEMTOACCID, tvItem.hItem, 0); } OS.NotifyWinEvent (OS.EVENT_OBJECT_FOCUS, handle, OS.OBJID_CLIENT, id); } Event event = new Event (); event.item = _getItem (tvItem.hItem, tvItem.lParam); event.detail = SWT.CHECK; postEvent (SWT.Selection, event); 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. */ bool selected = false; bool fakeSelection = false; if (lpht.hItem !is null) { if ((style & SWT.FULL_SELECTION) !is 0) { int bits = OS.GetWindowLong (handle, OS.GWL_STYLE); if ((bits & OS.TVS_FULLROWSELECT) is 0) fakeSelection = true; } else { if (hooks (SWT.MeasureItem)) { selected = hitTestSelection (lpht.hItem, lpht.pt.x, lpht.pt.y) !is 0; if (selected) { if ((lpht.flags & OS.TVHT_ONITEM) is 0) fakeSelection = true; } } } } /* Process the mouse when an item is not selected */ if (!selected && (style & SWT.FULL_SELECTION) is 0) { if ((lpht.flags & OS.TVHT_ONITEM) is 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 () !is handle) OS.SetCapture (handle); } return LRESULT.ZERO; } int /*long*/ code = callWindowProc (handle, OS.WM_LBUTTONDOWN, wParam, lParam); if (!OS.IsWinCE && OS.WIN32_VERSION >= OS.VERSION (6, 0)) { if (OS.GetFocus () !is handle) OS.SetFocus (handle); } if (!display.captureChanged && !isDisposed ()) { if (OS.GetCapture () !is handle) OS.SetCapture (handle); } return new LRESULT (code); } } /* Get the selected state of the item under the mouse */ TVITEM tvItem; tvItem.mask = OS.TVIF_HANDLE | OS.TVIF_STATE; tvItem.stateMask = OS.TVIS_SELECTED; bool hittestSelected = false; if ((style & SWT.MULTI) !is 0) { tvItem.hItem = lpht.hItem; OS.SendMessage (handle, OS.TVM_GETITEM, 0, &tvItem); hittestSelected = (tvItem.state & OS.TVIS_SELECTED) !is 0; } /* Get the selected state of the last selected item */ auto hOldItem = cast(HANDLE) OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_CARET, 0); if ((style & SWT.MULTI) !is 0) { tvItem.hItem = cast(HTREEITEM)hOldItem; OS.SendMessage (handle, OS.TVM_GETITEM, 0, &tvItem); /* Check for CONTROL or drag selection */ if (hittestSelected || (wParam & OS.MK_CONTROL) !is 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 = OS.SendMessage (handle, OS.WM_QUERYUISTATE, 0, 0); if ((uiState & OS.UISF_HIDEFOCUS) !is 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 () !is handle) OS.SetCapture (handle); } return LRESULT.ZERO; } hSelect = lpht.hItem; dragStarted = gestureCompleted = false; ignoreDeselect = ignoreSelect = true; int /*long*/ code = callWindowProc (handle, OS.WM_LBUTTONDOWN, wParam, lParam); if (!OS.IsWinCE && OS.WIN32_VERSION >= OS.VERSION (6, 0)) { if (OS.GetFocus () !is handle) OS.SetFocus (handle); } auto hNewItem = cast(HANDLE) OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_CARET, 0); if (fakeSelection) { if (hOldItem is null || (hNewItem is hOldItem && lpht.hItem !is hOldItem)) { OS.SendMessage (handle, OS.TVM_SELECTITEM, OS.TVGN_CARET, lpht.hItem); hNewItem = cast(HANDLE) OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_CARET, 0); } if (!dragStarted && (state & DRAG_DETECT) !is 0 && hooks (SWT.DragDetect)) { dragStarted = dragDetect (handle, lpht.pt.x, lpht.pt.y, false, null, null); } } ignoreDeselect = ignoreSelect = false; hSelect = null; if (dragStarted) { if (!display.captureChanged && !isDisposed ()) { if (OS.GetCapture () !is 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) !is 0) { if (hOldItem is hNewItem) { tvItem.mask = OS.TVIF_HANDLE | OS.TVIF_STATE; tvItem.state = OS.TVIS_SELECTED; tvItem.stateMask = OS.TVIS_SELECTED; tvItem.hItem = cast(HTREEITEM)hNewItem; OS.SendMessage (handle, OS.TVM_SETITEM, 0, &tvItem); } } /* Reselect the last item that was unselected */ if ((style & SWT.MULTI) !is 0) { /* Check for CONTROL and reselect the last item */ if (hittestSelected || (wParam & OS.MK_CONTROL) !is 0) { if (hOldItem is hNewItem && hOldItem is lpht.hItem) { if ((wParam & OS.MK_CONTROL) !is 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) !is 0) { tvItem.state = OS.TVIS_SELECTED; OS.SendMessage (handle, OS.TVM_SETITEM, 0, &tvItem); } if ((wParam & OS.MK_CONTROL) !is 0 && !dragStarted) { if (hittestSelected) { tvItem.state = 0; tvItem.hItem = lpht.hItem; OS.SendMessage (handle, OS.TVM_SETITEM, 0, &tvItem); } } } RECT rect1, rect2; bool fItemRect = (style & SWT.FULL_SELECTION) is 0; if (hooks (SWT.EraseItem) || hooks (SWT.PaintItem)) fItemRect = false; if (!OS.IsWinCE && OS.WIN32_VERSION >= OS.VERSION (6, 0)) fItemRect = false; OS.TreeView_GetItemRect (handle, cast(HTREEITEM)hOldItem, &rect1, fItemRect); OS.TreeView_GetItemRect (handle, cast(HTREEITEM)hNewItem, &rect2, fItemRect); OS.DefWindowProc (handle, OS.WM_SETREDRAW, 1, 0); OS.InvalidateRect (handle, &rect1, true); OS.InvalidateRect (handle, &rect2, true); OS.UpdateWindow (handle); } /* Check for SHIFT or normal select and deselect/reselect items */ if ((wParam & OS.MK_CONTROL) is 0) { if (!hittestSelected || !dragStarted) { tvItem.state = 0; auto oldProc = OS.GetWindowLongPtr (handle, OS.GWLP_WNDPROC); OS.SetWindowLongPtr (handle, OS.GWLP_WNDPROC, cast(LONG_PTR)TreeProc); if ((style & SWT.VIRTUAL) !is 0) { HANDLE hItem = cast(HANDLE) OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_ROOT, 0); deselect (hItem, &tvItem, hNewItem); } else { for (int i=0; i<items.length; i++) { TreeItem item = items [i]; if (item !is null && item.handle !is hNewItem) { tvItem.hItem = cast(HTREEITEM)item.handle; OS.SendMessage (handle, OS.TVM_SETITEM, 0, &tvItem); } } } tvItem.hItem = cast(HTREEITEM)hNewItem; tvItem.state = OS.TVIS_SELECTED; OS.SendMessage (handle, OS.TVM_SETITEM, 0, &tvItem); OS.SetWindowLongPtr (handle, OS.GWLP_WNDPROC, oldProc); if ((wParam & OS.MK_SHIFT) !is 0) { RECT rect1; if (hAnchor is null) hAnchor = hNewItem; if (OS.TreeView_GetItemRect (handle, cast(HTREEITEM)hAnchor, &rect1, false)) { RECT rect2; if (OS.TreeView_GetItemRect (handle, cast(HTREEITEM)hNewItem, &rect2, false)) { int flags = rect1.top < rect2.top ? OS.TVGN_NEXTVISIBLE : OS.TVGN_PREVIOUSVISIBLE; tvItem.state = OS.TVIS_SELECTED; auto hItem = tvItem.hItem = cast(HTREEITEM)hAnchor; OS.SendMessage (handle, OS.TVM_SETITEM, 0, &tvItem); while (hItem !is hNewItem) { tvItem.hItem = hItem; OS.SendMessage (handle, OS.TVM_SETITEM, 0, &tvItem); hItem = cast(HTREEITEM) OS.SendMessage (handle, OS.TVM_GETNEXTITEM, flags, hItem); } } } } } } } if ((wParam & OS.MK_SHIFT) is 0) hAnchor = hNewItem; /* Issue notification */ if (!gestureCompleted) { tvItem.hItem = cast(HTREEITEM)hNewItem; tvItem.mask = OS.TVIF_HANDLE | OS.TVIF_PARAM; OS.SendMessage (handle, OS.TVM_GETITEM, 0, &tvItem); Event event = new Event (); event.item = _getItem (tvItem.hItem, tvItem.lParam); postEvent (SWT.Selection, event); } gestureCompleted = false; /* * Feature in Windows. Inside WM_LBUTTONDOWN and WM_RBUTTONDOWN, * the widget starts a modal loop to determine if the user wants * to begin a drag/drop operation or marquee select. Unfortunately, * this modal loop eats the corresponding mouse up. The fix is to * detect the cases when the modal loop has eaten the mouse up and * issue a fake mouse up. */ if (dragStarted) { sendDragEvent (1, OS.GET_X_LPARAM (lParam), OS.GET_Y_LPARAM (lParam)); } else { int bits = OS.GetWindowLong (handle, OS.GWL_STYLE); if ((bits & OS.TVS_DISABLEDRAGDROP) is 0) { sendMouseEvent (SWT.MouseUp, 1, handle, OS.WM_LBUTTONUP, wParam, lParam); } } dragStarted = false; return new LRESULT (code); } override LRESULT WM_MOUSEMOVE (int wParam, int lParam) { Display display = this.display; LRESULT result = super.WM_MOUSEMOVE (wParam, lParam); if (result !is null) return result; if (itemToolTipHandle !is null) { /* * Bug in Windows. On some machines that do not have XBUTTONs, * the MK_XBUTTON1 and OS.MK_XBUTTON2 bits are sometimes set, * causing mouse capture to become stuck. The fix is to test * for the extra buttons only when they exist. */ int mask = OS.MK_LBUTTON | OS.MK_MBUTTON | OS.MK_RBUTTON; if (display.xMouse) mask |= OS.MK_XBUTTON1 | OS.MK_XBUTTON2; if ((wParam & mask) is 0) { int x = OS.GET_X_LPARAM (lParam); int y = OS.GET_Y_LPARAM (lParam); int index; TreeItem item; RECT* cellRect, itemRect; if (findCell (x, y, item, index, cellRect, itemRect)) { /* * Feature in Windows. When the new tool rectangle is * set using TTM_NEWTOOLRECT and the tooltip is visible, * Windows draws the tooltip right away and the sends * WM_NOTIFY with TTN_SHOW. This means that the tooltip * shows first at the wrong location and then moves to * the right one. The fix is to hide the tooltip window. */ if (OS.SendMessage (itemToolTipHandle, OS.TTM_GETCURRENTTOOL, 0, 0) is 0) { if (OS.IsWindowVisible (itemToolTipHandle)) { OS.ShowWindow (itemToolTipHandle, OS.SW_HIDE); } } TOOLINFO lpti; lpti.cbSize = OS.TOOLINFO_sizeof; lpti.hwnd = handle; lpti.uId = cast(int) handle; lpti.uFlags = OS.TTF_SUBCLASS | OS.TTF_TRANSPARENT; lpti.rect.left = cellRect.left; lpti.rect.top = cellRect.top; lpti.rect.right = cellRect.right; lpti.rect.bottom = cellRect.bottom; OS.SendMessage (itemToolTipHandle, OS.TTM_NEWTOOLRECT, 0, &lpti); } } } return result; } override LRESULT WM_MOVE (int wParam, int lParam) { if (ignoreResize) return null; return super.WM_MOVE (wParam, lParam); } override LRESULT WM_RBUTTONDOWN (int wParam, int lParam) { /* * Feature in Windows. The receiver uses WM_RBUTTONDOWN * to initiate a drag/drop operation depending on how the * user moves the mouse. If the user clicks the right button, * without moving the mouse, the tree consumes the corresponding * WM_RBUTTONUP. The fix is to avoid calling the window proc for * the tree. */ Display display = this.display; display.captureChanged = false; if (!sendMouseEvent (SWT.MouseDown, 3, handle, OS.WM_RBUTTONDOWN, wParam, lParam)) { if (!display.captureChanged && !isDisposed ()) { if (OS.GetCapture () !is handle) OS.SetCapture (handle); } return LRESULT.ZERO; } /* * This code is intentionally commented. */ // if (OS.GetCapture () !is handle) OS.SetCapture (handle); if (OS.GetFocus () !is handle) OS.SetFocus (handle); /* * Feature in Windows. When the user selects a tree item * with the right mouse button, the item remains selected * only as long as the user does not release or move the * mouse. As soon as this happens, the selection snaps * back to the previous selection. This behavior can be * observed in the Explorer but is not instantly apparent * because the Explorer explicitly sets the selection when * the user chooses a menu item. If the user cancels the * menu, the selection snaps back. The fix is to avoid * calling the window proc and do the selection ourselves. * This behavior is consistent with the table. */ TVHITTESTINFO lpht; lpht.pt.x = OS.GET_X_LPARAM (lParam); lpht.pt.y = OS.GET_Y_LPARAM (lParam); OS.SendMessage (handle, OS.TVM_HITTEST, 0, &lpht); if (lpht.hItem !is null) { bool fakeSelection = (style & SWT.FULL_SELECTION) !is 0; if (!fakeSelection) { if (hooks (SWT.MeasureItem)) { fakeSelection = hitTestSelection (lpht.hItem, lpht.pt.x, lpht.pt.y); } else { int flags = OS.TVHT_ONITEMICON | OS.TVHT_ONITEMLABEL; fakeSelection = (lpht.flags & flags) !is 0; } } if (fakeSelection) { if ((wParam & (OS.MK_CONTROL | OS.MK_SHIFT)) is 0) { TVITEM tvItem; tvItem.mask = OS.TVIF_HANDLE | OS.TVIF_STATE; tvItem.stateMask = OS.TVIS_SELECTED; tvItem.hItem = lpht.hItem; OS.SendMessage (handle, OS.TVM_GETITEM, 0, &tvItem); if ((tvItem.state & OS.TVIS_SELECTED) is 0) { ignoreSelect = true; OS.SendMessage (handle, OS.TVM_SELECTITEM, OS.TVGN_CARET, 0); ignoreSelect = false; OS.SendMessage (handle, OS.TVM_SELECTITEM, OS.TVGN_CARET, lpht.hItem); } } } } return LRESULT.ZERO; } override LRESULT WM_PAINT (int wParam, int lParam) { if (shrink && !ignoreShrink) { /* Resize the item array to fit the last item */ int count = items.length - 1; while (count >= 0) { if (items [count] !is 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) !is 0 || findImageControl () !is null) { bool doubleBuffer = true; if (explorerTheme) { int exStyle = OS.SendMessage (handle, OS.TVM_GETEXTENDEDSTYLE, 0, 0); if ((exStyle & OS.TVS_EX_DOUBLEBUFFER) !is 0) doubleBuffer = false; } if (doubleBuffer) { GC gc = null; HDC paintDC; PAINTSTRUCT ps; bool hooksPaint = hooks (SWT.Paint); if (hooksPaint) { GCData data = new GCData (); data.ps = &ps; data.hwnd = handle; gc = GC.win32_new (this, data); paintDC = gc.handle; } else { paintDC = OS.BeginPaint (handle, &ps); } int width = ps.rcPaint.right - ps.rcPaint.left; int height = ps.rcPaint.bottom - ps.rcPaint.top; if (width !is 0 && height !is 0) { auto hDC = OS.CreateCompatibleDC (paintDC); POINT lpPoint1, lpPoint2; OS.SetWindowOrgEx (hDC, ps.rcPaint.left, ps.rcPaint.top, &lpPoint1); OS.SetBrushOrgEx (hDC, ps.rcPaint.left, ps.rcPaint.top, &lpPoint2); auto hBitmap = OS.CreateCompatibleBitmap (paintDC, width, height); auto hOldBitmap = OS.SelectObject (hDC, hBitmap); RECT rect; OS.SetRect (&rect, ps.rcPaint.left, ps.rcPaint.top, ps.rcPaint.right, ps.rcPaint.bottom); drawBackground (hDC, &rect); callWindowProc (handle, OS.WM_PAINT, cast(int) hDC, 0); OS.SetWindowOrgEx (hDC, lpPoint1.x, lpPoint1.y, null); OS.SetBrushOrgEx (hDC, lpPoint2.x, lpPoint2.y, null); OS.BitBlt (paintDC, ps.rcPaint.left, ps.rcPaint.top, width, height, hDC, 0, 0, OS.SRCCOPY); OS.SelectObject (hDC, hOldBitmap); OS.DeleteObject (hBitmap); OS.DeleteObject (hDC); if (hooksPaint) { Event event = new Event (); event.gc = gc; event.x = ps.rcPaint.left; event.y = ps.rcPaint.top; event.width = ps.rcPaint.right - ps.rcPaint.left; event.height = ps.rcPaint.bottom - ps.rcPaint.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_PRINTCLIENT (int wParam, int lParam) { LRESULT result = super.WM_PRINTCLIENT (wParam, lParam); if (result !is null) return result; /* * Feature in Windows. For some reason, when WM_PRINT is used * to capture an image of a hierarchy that contains a tree with * columns, the clipping that is used to stop the first column * from drawing on top of subsequent columns stops the first * column and the tree lines from drawing. This does not happen * during WM_PAINT. The fix is to draw without clipping and * then draw the rest of the columns on top. Since the drawing * is happening in WM_PRINTCLIENT, the redrawing is not visible. */ printClient = true; int /*long*/ code = callWindowProc (handle, OS.WM_PRINTCLIENT, wParam, lParam); printClient = false; return new LRESULT (code); } override LRESULT WM_SETFOCUS (int wParam, int 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. */ bool redraw = (style & SWT.MULTI) !is 0; if (!redraw) { if (!OS.IsWinCE && OS.COMCTL32_MAJOR >= 6) { if (imageList !is null) { int bits = OS.GetWindowLong (handle, OS.GWL_STYLE); if ((bits & OS.TVS_FULLROWSELECT) is 0) { redraw = true; } } } } if (redraw) redrawSelection (); return super.WM_SETFOCUS (wParam, lParam); } override LRESULT WM_SETFONT (int wParam, int lParam) { LRESULT result = super.WM_SETFONT (wParam, lParam); if (result !is null) return result; if (hwndHeader !is null) { /* * 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 !is null) { OS.SendMessage (itemToolTipHandle, OS.WM_SETFONT, wParam, lParam); } if (headerToolTipHandle !is null) { OS.SendMessage (headerToolTipHandle, OS.WM_SETFONT, wParam, lParam); } return result; } override LRESULT WM_SETREDRAW (int wParam, int lParam) { LRESULT result = super.WM_SETREDRAW (wParam, lParam); if (result !is null) return result; /* * Bug in Windows. Under certain circumstances, when * WM_SETREDRAW is used to turn off drawing and then * TVM_GETITEMRECT is sent to get the bounds of an item * that is not inside the client area, Windows segment * faults. The fix is to call the default window proc * rather than the default tree proc. * * NOTE: This problem is intermittent and happens on * Windows Vista running under the theme manager. */ if (!OS.IsWinCE && OS.WIN32_VERSION >= OS.VERSION (6, 0)) { int /*long*/ code = OS.DefWindowProc (handle, OS.WM_SETREDRAW, wParam, lParam); return code is 0 ? LRESULT.ZERO : new LRESULT (code); } return result; } override LRESULT WM_SIZE (int wParam, int lParam) { /* * Bug in Windows. When TVS_NOHSCROLL is set when the * size of the tree is zero, the scroll bar is shown the * next time the tree resizes. The fix is to hide the * scroll bar every time the tree is resized. */ int bits = OS.GetWindowLong (handle, OS.GWL_STYLE); if ((bits & OS.TVS_NOHSCROLL) !is 0) { static if (!OS.IsWinCE) OS.ShowScrollBar (handle, OS.SB_HORZ, false); } /* * Bug in Windows. On Vista, when the Explorer theme * is used with a full selection tree, when the tree * is resized to be smaller, the rounded right edge * of the selected items is not drawn. The fix is the * redraw the entire tree. */ if (explorerTheme && (style & SWT.FULL_SELECTION) !is 0) { OS.InvalidateRect (handle, null, false); } if (ignoreResize) return null; return super.WM_SIZE (wParam, lParam); } override LRESULT WM_SYSCOLORCHANGE (int wParam, int lParam) { LRESULT result = super.WM_SYSCOLORCHANGE (wParam, lParam); if (result !is 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 is -1) setForegroundPixel (-1); } if ((style & SWT.CHECK) !is 0) setCheckboxImageList (); return result; } override LRESULT WM_VSCROLL (int /*long*/ wParam, int /*long*/ lParam) { bool fixScroll = false; if ((style & SWT.DOUBLE_BUFFERED) !is 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) !is 0 || hooks (SWT.EraseItem) || hooks (SWT.PaintItem); break; default: } } 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 !is null) return result; return result; } override LRESULT wmColorChild (int wParam, int lParam) { if (findImageControl () !is null) { if (OS.COMCTL32_MAJOR < 6) { return super.wmColorChild (wParam, lParam); } return new LRESULT ( cast(int) 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, int wParam, int lParam) { if (hdr.hwndFrom is itemToolTipHandle) { LRESULT result = wmNotifyToolTip (hdr, wParam, lParam); if (result !is null) return result; } if (hdr.hwndFrom is hwndHeader) { LRESULT result = wmNotifyHeader (hdr, wParam, lParam); if (result !is null) return result; } return super.wmNotify (hdr, wParam, lParam); } override LRESULT wmNotifyChild (NMHDR* hdr, int wParam, int lParam) { switch (hdr.code) { case OS.TVN_GETDISPINFOA: case OS.TVN_GETDISPINFOW: { NMTVDISPINFO* lptvdi = cast(NMTVDISPINFO*)lParam; //OS.MoveMemory (lptvdi, lParam, NMTVDISPINFO.sizeof); if ((style & SWT.VIRTUAL) !is 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. */ bool 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 !is null && lptvdi.item.lParam !is -1) { if (items [lptvdi.item.lParam] !is null && items [lptvdi.item.lParam].cached) { checkVisible = false; } } } if (checkVisible) { if (drawCount !is 0 || !OS.IsWindowVisible (handle)) break; RECT itemRect; if (!OS.TreeView_GetItemRect (handle, lptvdi.item.hItem, &itemRect, false)) { break; } RECT rect; OS.GetClientRect (handle, &rect); if (!OS.IntersectRect (&rect, &rect, &itemRect)) break; if (ignoreShrink) { OS.InvalidateRect (handle, &rect, true); break; } } } if (items is 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 = lptvdi.item.lParam; if ((style & SWT.VIRTUAL) !is 0) { if (id is -1) { TVITEM tvItem; tvItem.mask = OS.TVIF_HANDLE | OS.TVIF_PARAM; tvItem.hItem = lptvdi.item.hItem; OS.SendMessage (handle, OS.TVM_GETITEM, 0, &tvItem); id = tvItem.lParam; } } TreeItem item = _getItem (lptvdi.item.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 is null) break; if (item.isDisposed ()) break; if (!item.cached) { if ((style & SWT.VIRTUAL) !is 0) { if (!checkData (item, false)) break; } if (painted) item.cached = true; } int index = 0; if (hwndHeader !is null) { index = OS.SendMessage (hwndHeader, OS.HDM_ORDERTOINDEX, 0, 0); } if ((lptvdi.item.mask & OS.TVIF_TEXT) !is 0) { String string = null; if (index is 0) { string = item.text; } else { String [] strings = item.strings; if (strings !is null) string = strings [index]; } if (string !is null) { StringT buffer = StrToTCHARs (getCodePage (), string, false); int byteCount = Math.min (buffer.length, lptvdi.item.cchTextMax - 1) * TCHAR.sizeof; OS.MoveMemory (lptvdi.item.pszText, buffer.ptr, byteCount); int st = byteCount/TCHAR.sizeof; lptvdi.item.pszText[ st .. st+1 ] = 0; //OS.MoveMemory (lptvdi.pszText + byteCount, new byte [TCHAR.sizeof], TCHAR.sizeof); lptvdi.item.cchTextMax = Math.min (lptvdi.item.cchTextMax, string.length + 1); } } if ((lptvdi.item.mask & (OS.TVIF_IMAGE | OS.TVIF_SELECTEDIMAGE)) !is 0) { Image image = null; if (index is 0) { image = item.image; } else { Image [] images = item.images; if (images !is null) image = images [index]; } lptvdi.item.iImage = lptvdi.item.iSelectedImage = OS.I_IMAGENONE; if (image !is null) { lptvdi.item.iImage = lptvdi.item.iSelectedImage = imageIndex (image, index); } if (explorerTheme && OS.IsWindowEnabled (handle)) { if (findImageControl () !is null) { lptvdi.item.iImage = lptvdi.item.iSelectedImage = OS.I_IMAGENONE; } } } //OS.MoveMemory (cast(void*)lParam, lptvdi, NMTVDISPINFO.sizeof); break; } case OS.NM_CUSTOMDRAW: { if (hdr.hwndFrom is hwndHeader) break; if (hooks (SWT.MeasureItem)) { if (hwndHeader is null) createParent (); } if (!customDraw && findImageControl () is null) { if (OS.COMCTL32_MAJOR >= 6 && OS.IsAppThemed ()) { if (sortColumn is null || sortDirection is SWT.NONE) { break; } } } NMTVCUSTOMDRAW* nmcd = cast(NMTVCUSTOMDRAW*)lParam; //OS.MoveMemory (nmcd, lParam, NMTVCUSTOMDRAW.sizeof); switch (nmcd.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); default: } 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; int pos = OS.GetMessagePos (); OS.POINTSTOPOINT (pt, pos); OS.ScreenToClient (handle, &pt); TVHITTESTINFO lpht; lpht.pt.x = pt.x; lpht.pt.y = pt.y; OS.SendMessage (handle, OS.TVM_HITTEST, 0, &lpht); if (lpht.hItem !is null && (lpht.flags & OS.TVHT_ONITEM) !is 0) { return LRESULT.ONE; } } break; } /* * Bug in Windows. On Vista, when TVM_SELECTITEM is called * with TVGN_CARET in order to set the selection, for some * reason, Windows deselects the previous two items that * were selected. The fix is to stop the selection from * changing on all but the item that is supposed to be * selected. */ case OS.TVN_ITEMCHANGINGA: case OS.TVN_ITEMCHANGINGW: { if (!OS.IsWinCE && OS.WIN32_VERSION >= OS.VERSION (6, 0)) { if ((style & SWT.MULTI) !is 0) { if (hSelect !is null) { NMTVITEMCHANGE* pnm = cast(NMTVITEMCHANGE*)lParam; //OS.MoveMemory (pnm, lParam, NMTVITEMCHANGE.sizeof); if (hSelect is pnm.hItem) break; return LRESULT.ONE; } } } break; } case OS.TVN_SELCHANGINGA: case OS.TVN_SELCHANGINGW: { if ((style & SWT.MULTI) !is 0) { if (lockSelection) { /* Save the old selection state for both items */ auto treeView = cast(NMTREEVIEW*)lParam; TVITEM* tvItem = &treeView.itemOld; oldSelected = (tvItem.state & OS.TVIS_SELECTED) !is 0; tvItem = &treeView.itemNew; newSelected = (tvItem.state & OS.TVIS_SELECTED) !is 0; } } if (!ignoreSelect && !ignoreDeselect) { hAnchor = null; if ((style & SWT.MULTI) !is 0) deselectAll (); } break; } case OS.TVN_SELCHANGEDA: case OS.TVN_SELCHANGEDW: { NMTREEVIEW* treeView = null; if ((style & SWT.MULTI) !is 0) { if (lockSelection) { /* Restore the old selection state of both items */ if (oldSelected) { if (treeView is null) { treeView = cast(NMTREEVIEW*)lParam; } 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 is null) { treeView = cast(NMTREEVIEW*)lParam; } 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 is null) { treeView = cast(NMTREEVIEW*)lParam; } TVITEM tvItem = treeView.itemNew; hAnchor = tvItem.hItem; Event event = new Event (); event.item = _getItem (&tvItem.hItem, tvItem.lParam); postEvent (SWT.Selection, event); } updateScrollBar (); break; } case OS.TVN_ITEMEXPANDINGA: case OS.TVN_ITEMEXPANDINGW: { bool runExpanded = false; if ((style & SWT.VIRTUAL) !is 0) style &= ~SWT.DOUBLE_BUFFERED; if (hooks (SWT.EraseItem) || hooks (SWT.PaintItem)) style &= ~SWT.DOUBLE_BUFFERED; if (findImageControl () !is null && drawCount is 0 && OS.IsWindowVisible (handle)) { OS.DefWindowProc (handle, OS.WM_SETREDRAW, 0, 0); } /* * Bug in Windows. When TVM_SETINSERTMARK is used to set * an insert mark for a tree and an item is expanded or * collapsed near the insert mark, the tree does not redraw * the insert mark properly. The fix is to hide and show * the insert mark whenever an item is expanded or collapsed. */ if (hInsert !is null) { OS.SendMessage (handle, OS.TVM_SETINSERTMARK, 0, 0); } if (!ignoreExpand) { NMTREEVIEW* treeView = cast(NMTREEVIEW*)lParam; 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 is null) break; TreeItem item = _getItem (tvItem.hItem, tvItem.lParam); if (item is 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) is 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; default: } /* * 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. */ auto hFirstItem = cast(HANDLE) OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_CHILD, tvItem.hItem); runExpanded = hFirstItem is null; } if (!runExpanded) break; //FALL THROUGH } case OS.TVN_ITEMEXPANDEDA: case OS.TVN_ITEMEXPANDEDW: { if ((style & SWT.VIRTUAL) !is 0) style |= SWT.DOUBLE_BUFFERED; if (hooks (SWT.EraseItem) || hooks (SWT.PaintItem)) style |= SWT.DOUBLE_BUFFERED; if (findImageControl () !is null && drawCount is 0 /*&& OS.IsWindowVisible (handle)*/) { OS.DefWindowProc (handle, OS.WM_SETREDRAW, 1, 0); OS.InvalidateRect (handle, null, true); } /* * Bug in Windows. When TVM_SETINSERTMARK is used to set * an insert mark for a tree and an item is expanded or * collapsed near the insert mark, the tree does not redraw * the insert mark properly. The fix is to hide and show * the insert mark whenever an item is expanded or collapsed. */ if (hInsert !is null) { OS.SendMessage (handle, OS.TVM_SETINSERTMARK, insertAfter ? 1 : 0, hInsert); } /* * Bug in Windows. When a tree item that has an image * with alpha is expanded or collapsed, the area where * the image is drawn is not erased before it is drawn. * This means that the image gets darker each time. * The fix is to redraw the item. */ if (!OS.IsWinCE && OS.COMCTL32_MAJOR >= 6) { if (imageList !is null) { NMTREEVIEW* treeView = cast(NMTREEVIEW*)lParam; TVITEM* tvItem = &treeView.itemNew; if (tvItem.hItem !is null) { int bits = OS.GetWindowLong (handle, OS.GWL_STYLE); if ((bits & OS.TVS_FULLROWSELECT) is 0) { RECT rect; if (OS.TreeView_GetItemRect (handle, tvItem.hItem, &rect, false)) { OS.InvalidateRect (handle, &rect, true); } } } } } updateScrollBar (); break; } case OS.TVN_BEGINDRAGA: case OS.TVN_BEGINDRAGW: if (OS.GetKeyState (OS.VK_LBUTTON) >= 0) break; //FALL THROUGH case OS.TVN_BEGINRDRAGA: case OS.TVN_BEGINRDRAGW: { dragStarted = true; NMTREEVIEW* treeView = cast(NMTREEVIEW*)lParam; TVITEM* tvItem = &treeView.itemNew; if (tvItem.hItem !is null && (tvItem.state & OS.TVIS_SELECTED) is 0) { hSelect = tvItem.hItem; ignoreSelect = ignoreDeselect = true; OS.SendMessage (handle, OS.TVM_SELECTITEM, OS.TVGN_CARET, tvItem.hItem); ignoreSelect = ignoreDeselect = false; hSelect = null; } break; } case OS.NM_RECOGNIZEGESTURE: { /* * Feature in Pocket PC. The tree and table controls detect the tap * and hold gesture by default. They send a GN_CONTEXTMENU message to show * the popup menu. This default behaviour is unwanted on Pocket PC 2002 * when no menu has been set, as it still draws a red circle. The fix * is to disable this default behaviour when no menu is set by returning * TRUE when receiving the Pocket PC 2002 specific NM_RECOGNIZEGESTURE * message. */ if (OS.IsPPC) { bool hasMenu = menu !is null && !menu.isDisposed (); if (!hasMenu && !hooks (SWT.MenuDetect)) return LRESULT.ONE; } break; } case OS.GN_CONTEXTMENU: { if (OS.IsPPC) { bool hasMenu = menu !is null && !menu.isDisposed (); if (hasMenu || hooks (SWT.MenuDetect)) { NMRGINFO* nmrg = cast(NMRGINFO*)lParam; //OS.MoveMemory (nmrg, lParam, NMRGINFO.sizeof); showMenu (nmrg.x, nmrg.y); gestureCompleted = true; return LRESULT.ONE; } } break; } default: } return super.wmNotifyChild (hdr, wParam, lParam); } LRESULT wmNotifyHeader (NMHDR* hdr, int /*long*/ wParam, int /*long*/ lParam) { /* * Feature in Windows. On NT, the automatically created * header control is created as a UNICODE window, not an * ANSI window despite the fact that the parent is created * as an ANSI window. This means that it sends UNICODE * notification messages to the parent window on NT for * no good reason. The data and size in the NMHEADER and * HDITEM structs is identical between the platforms so no * different message is actually necessary. Despite this, * Windows sends different messages. The fix is to look * for both messages, despite the platform. This works * because only one will be sent on either platform, never * both. */ switch (hdr.code) { case OS.HDN_BEGINTRACKW: case OS.HDN_BEGINTRACKA: case OS.HDN_DIVIDERDBLCLICKW: case OS.HDN_DIVIDERDBLCLICKA: { NMHEADER* phdn = cast(NMHEADER*)lParam; TreeColumn column = columns [phdn.iItem]; if (column !is null && !column.getResizable ()) { return LRESULT.ONE; } ignoreColumnMove = true; switch (hdr.code) { case OS.HDN_DIVIDERDBLCLICKW: case OS.HDN_DIVIDERDBLCLICKA: if (column !is null) column.pack (); default: } break; } case OS.NM_RELEASEDCAPTURE: { if (!ignoreColumnMove) { for (int i=0; i<columnCount; i++) { TreeColumn column = columns [i]; column.updateToolTip (i); } updateImageList (); } ignoreColumnMove = false; break; } case OS.HDN_BEGINDRAG: { if (ignoreColumnMove) return LRESULT.ONE; NMHEADER* phdn = cast(NMHEADER*)lParam; if (phdn.iItem !is -1) { TreeColumn column = columns [phdn.iItem]; if (column !is null && !column.getMoveable ()) { ignoreColumnMove = true; return LRESULT.ONE; } } break; } case OS.HDN_ENDDRAG: { NMHEADER* phdn = cast(NMHEADER*)lParam; if (cast(int)phdn.iItem !is -1 && phdn.pitem !is null) { HDITEM* pitem = cast(HDITEM*)phdn.pitem; if ((pitem.mask & OS.HDI_ORDER) !is 0 && pitem.iOrder !is -1) { int [] order = new int [columnCount]; OS.SendMessage (hwndHeader, OS.HDM_GETORDERARRAY, columnCount, order.ptr); int index = 0; while (index < order.length) { if (order [index] is phdn.iItem) break; index++; } if (index is order.length) index = 0; if (index is pitem.iOrder) break; int start = Math.min (index, pitem.iOrder); int end = Math.max (index, pitem.iOrder); RECT rect, headerRect; OS.GetClientRect (handle, &rect); OS.SendMessage (hwndHeader, OS.HDM_GETITEMRECT, order [start], &headerRect); rect.left = Math.max (rect.left, headerRect.left); OS.SendMessage (hwndHeader, OS.HDM_GETITEMRECT, order [end], &headerRect); rect.right = Math.min (rect.right, headerRect.right); OS.InvalidateRect (handle, &rect, true); ignoreColumnMove = false; for (int i=start; i<=end; i++) { TreeColumn column = columns [order [i]]; if (!column.isDisposed ()) { column.postEvent (SWT.Move); } } } } break; } case OS.HDN_ITEMCHANGINGW: case OS.HDN_ITEMCHANGINGA: { NMHEADER* phdn = cast(NMHEADER*)lParam; if (phdn.pitem !is null) { HDITEM* newItem = cast(HDITEM*)phdn.pitem; if ((newItem.mask & OS.HDI_WIDTH) !is 0) { RECT rect; OS.GetClientRect (handle, &rect); HDITEM oldItem; oldItem.mask = OS.HDI_WIDTH; OS.SendMessage (hwndHeader, OS.HDM_GETITEM, phdn.iItem, &oldItem); int deltaX = newItem.cxy - oldItem.cxy; RECT headerRect; OS.SendMessage (hwndHeader, OS.HDM_GETITEMRECT, phdn.iItem, &headerRect); int gridWidth = linesVisible ? GRID_WIDTH : 0; rect.left = headerRect.right - gridWidth; int newX = rect.left + deltaX; rect.right = Math.max (rect.right, rect.left + Math.abs (deltaX)); if (explorerTheme || (findImageControl () !is null || hooks (SWT.MeasureItem) || hooks (SWT.EraseItem) || hooks (SWT.PaintItem))) { rect.left -= OS.GetSystemMetrics (OS.SM_CXFOCUSBORDER); OS.InvalidateRect (handle, &rect, true); OS.OffsetRect (&rect, deltaX, 0); OS.InvalidateRect (handle, &rect, true); } else { int flags = OS.SW_INVALIDATE | OS.SW_ERASE; OS.ScrollWindowEx (handle, deltaX, 0, &rect, null, null, null, flags); } if (OS.SendMessage (hwndHeader, OS.HDM_ORDERTOINDEX, phdn.iItem, 0) !is 0) { rect.left = headerRect.left; rect.right = newX; OS.InvalidateRect (handle, &rect, true); } setScrollWidth (); } } break; } case OS.HDN_ITEMCHANGEDW: case OS.HDN_ITEMCHANGEDA: { NMHEADER* phdn = cast(NMHEADER*)lParam; if (phdn.pitem !is null) { HDITEM* pitem = cast(HDITEM*)phdn.pitem; if ((pitem.mask & OS.HDI_WIDTH) !is 0) { if (ignoreColumnMove) { if (!OS.IsWinCE && OS.WIN32_VERSION >= OS.VERSION (6, 0)) { int flags = OS.RDW_UPDATENOW | OS.RDW_ALLCHILDREN; OS.RedrawWindow (handle, null, null, flags); } else { if ((style & SWT.DOUBLE_BUFFERED) is 0) { int oldStyle = style; style |= SWT.DOUBLE_BUFFERED; OS.UpdateWindow (handle); style = oldStyle; } } } TreeColumn column = columns [phdn.iItem]; if (column !is null) { column.updateToolTip (phdn.iItem); column.sendEvent (SWT.Resize); if (isDisposed ()) return LRESULT.ZERO; TreeColumn [] newColumns = new TreeColumn [columnCount]; System.arraycopy (columns, 0, newColumns, 0, columnCount); int [] order = new int [columnCount]; OS.SendMessage (hwndHeader, OS.HDM_GETORDERARRAY, columnCount, order.ptr); bool moved = false; for (int i=0; i<columnCount; i++) { TreeColumn nextColumn = newColumns [order [i]]; if (moved && !nextColumn.isDisposed ()) { nextColumn.updateToolTip (order [i]); nextColumn.sendEvent (SWT.Move); } if (nextColumn is column) moved = true; } } } setScrollWidth (); } break; } case OS.HDN_ITEMCLICKW: case OS.HDN_ITEMCLICKA: { NMHEADER* phdn = cast(NMHEADER*)lParam; TreeColumn column = columns [phdn.iItem]; if (column !is null) { column.postEvent (SWT.Selection); } break; } case OS.HDN_ITEMDBLCLICKW: case OS.HDN_ITEMDBLCLICKA: { NMHEADER* phdn = cast(NMHEADER*)lParam; TreeColumn column = columns [phdn.iItem]; if (column !is null) { column.postEvent (SWT.DefaultSelection); } break; } default: } return null; } LRESULT wmNotifyToolTip (NMHDR* hdr, int /*long*/ wParam, int /*long*/ lParam) { if (OS.IsWinCE) return null; switch (hdr.code) { case OS.NM_CUSTOMDRAW: { NMTTCUSTOMDRAW* nmcd = cast(NMTTCUSTOMDRAW*)lParam; return wmNotifyToolTip (nmcd, lParam); } case OS.TTN_SHOW: { LRESULT result = super.wmNotify (hdr, wParam, lParam); if (result !is null) return result; int pos = OS.GetMessagePos (); POINT pt; OS.POINTSTOPOINT (pt, pos); OS.ScreenToClient (handle, &pt); int index; TreeItem item; RECT* cellRect, itemRect; if (findCell (pt.x, pt.y, item, index, cellRect, itemRect)) { RECT* toolRect = toolTipRect (itemRect); OS.MapWindowPoints (handle, null, cast(POINT*)toolRect, 2); int width = toolRect.right - toolRect.left; int height = toolRect.bottom - toolRect.top; int flags = OS.SWP_NOACTIVATE | OS.SWP_NOZORDER | OS.SWP_NOSIZE; if (isCustomToolTip ()) flags &= ~OS.SWP_NOSIZE; SetWindowPos (itemToolTipHandle, null, toolRect.left, toolRect.top, width, height, flags); return LRESULT.ONE; } return result; } default: } return null; } LRESULT wmNotifyToolTip (NMTTCUSTOMDRAW* nmcd, int /*long*/ lParam) { if (OS.IsWinCE) return null; switch (nmcd.nmcd.dwDrawStage) { case OS.CDDS_PREPAINT: { if (isCustomToolTip ()) { //TEMPORARY CODE //nmcd.uDrawFlags |= OS.DT_CALCRECT; //OS.MoveMemory (lParam, nmcd, NMTTCUSTOMDRAW.sizeof); if (!OS.IsWinCE && OS.WIN32_VERSION < OS.VERSION (6, 0)) { OS.SetTextColor (nmcd.nmcd.hdc, OS.GetSysColor (OS.COLOR_INFOBK)); } return new LRESULT (OS.CDRF_NOTIFYPOSTPAINT | OS.CDRF_NEWFONT); } break; } case OS.CDDS_POSTPAINT: { if (!OS.IsWinCE && OS.WIN32_VERSION < OS.VERSION (6, 0)) { OS.SetTextColor (nmcd.nmcd.hdc, OS.GetSysColor (OS.COLOR_INFOTEXT)); } if (OS.SendMessage (itemToolTipHandle, OS.TTM_GETCURRENTTOOL, 0, 0) !is 0) { TOOLINFO lpti; lpti.cbSize = OS.TOOLINFO_sizeof; if (OS.SendMessage (itemToolTipHandle, OS.TTM_GETCURRENTTOOL, 0, cast(int)&lpti) !is 0) { int index; TreeItem item; RECT* cellRect, itemRect; int pos = OS.GetMessagePos (); POINT pt; OS.POINTSTOPOINT (pt, pos); OS.ScreenToClient (handle, &pt); if (findCell (pt.x, pt.y, item, index, cellRect, itemRect)) { auto hDC = OS.GetDC (handle); auto hFont = item.fontHandle (index); if (hFont is cast(HFONT)-1) hFont = cast(HFONT) OS.SendMessage (handle, OS.WM_GETFONT, 0, 0); HFONT oldFont = OS.SelectObject (hDC, hFont); LRESULT result = null; bool drawForeground = true; cellRect = item.getBounds (index, true, true, false, false, false, hDC); if (hooks (SWT.EraseItem)) { Event event = sendEraseItemEvent (item, nmcd, index, cellRect); if (isDisposed () || item.isDisposed ()) break; if (event.doit) { drawForeground = (event.detail & SWT.FOREGROUND) !is 0; } else { drawForeground = false; } } if (drawForeground) { int nSavedDC = OS.SaveDC (nmcd.nmcd.hdc); int gridWidth = getLinesVisible () ? Table.GRID_WIDTH : 0; RECT* insetRect = toolTipInset (cellRect); OS.SetWindowOrgEx (nmcd.nmcd.hdc, insetRect.left, insetRect.top, null); GCData data = new GCData (); data.device = display; data.foreground = OS.GetTextColor (nmcd.nmcd.hdc); data.background = OS.GetBkColor (nmcd.nmcd.hdc); data.font = Font.win32_new (display, hFont); GC gc = GC.win32_new (nmcd.nmcd.hdc, data); int x = cellRect.left + INSET; if (index !is 0) x -= gridWidth; Image image = item.getImage (index); if (image !is null || index is 0) { Point size = getImageSize (); RECT* imageRect = item.getBounds (index, false, true, false, false, false, hDC); if (imageList is null) size.x = imageRect.right - imageRect.left; if (image !is null) { Rectangle rect = image.getBounds (); gc.drawImage (image, rect.x, rect.y, rect.width, rect.height, x, imageRect.top, size.x, size.y); x += INSET + (index is 0 ? 1 : 0); } x += size.x; } else { x += INSET; } String string = item.getText (index); if (string !is null) { int flags = OS.DT_NOPREFIX | OS.DT_SINGLELINE | OS.DT_VCENTER; TreeColumn column = columns !is null ? columns [index] : null; if (column !is null) { if ((column.style & SWT.CENTER) !is 0) flags |= OS.DT_CENTER; if ((column.style & SWT.RIGHT) !is 0) flags |= OS.DT_RIGHT; } StringT buffer = StrToTCHARs (getCodePage (), string, false); RECT textRect; OS.SetRect (&textRect, x, cellRect.top, cellRect.right, cellRect.bottom); OS.DrawText (nmcd.nmcd.hdc, buffer.ptr, buffer.length, &textRect, flags); } gc.dispose (); OS.RestoreDC (nmcd.nmcd.hdc, nSavedDC); } if (hooks (SWT.PaintItem)) { itemRect = item.getBounds (index, true, true, false, false, false, hDC); sendPaintItemEvent (item, nmcd, index, itemRect); } OS.SelectObject (hDC, oldFont); OS.ReleaseDC (handle, hDC); if (result !is null) return result; } break; } } break; } default: } return null; } }