package org.eclipse.swt.widgets; | |
/* | |
* (c) Copyright IBM Corp. 2000, 2001. | |
* All Rights Reserved | |
*/ | |
import org.eclipse.swt.internal.win32.*; | |
import org.eclipse.swt.*; | |
import org.eclipse.swt.graphics.*; | |
import org.eclipse.swt.events.*; | |
/** | |
* Instances of this class provide a selectable user interface object | |
* that displays a hierarchy of items and issue notificiation 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> | |
* Note that although this class is a subclass of <code>Composite</code>, | |
* it does not make sense to add <code>Control</code> children to it, | |
* or set a layout on it. | |
* </p><p> | |
* <dl> | |
* <dt><b>Styles:</b></dt> | |
* <dd>SINGLE, MULTI, CHECK</dd> | |
* <dt><b>Events:</b></dt> | |
* <dd>Selection, DefaultSelection, Collapse, Expand</dd> | |
* </dl> | |
* <p> | |
* IMPORTANT: This class is <em>not</em> intended to be subclassed. | |
* </p> | |
*/ | |
public class Tree extends Composite { | |
int hAnchor; | |
TreeItem [] items; | |
ImageList imageList; | |
boolean dragStarted; | |
boolean ignoreSelect, ignoreExpand, ignoreDeselect; | |
static final int TreeProc; | |
static final byte [] TreeClass = OS.WC_TREEVIEW; | |
static { | |
WNDCLASSEX lpWndClass = new WNDCLASSEX (); | |
lpWndClass.cbSize = WNDCLASSEX.sizeof; | |
OS.GetClassInfoEx (0, TreeClass, lpWndClass); | |
TreeProc = lpWndClass.lpfnWndProc; | |
} | |
/** | |
* Constructs a new instance of this class given its parent | |
* and a style value describing its behavior and appearance. | |
* <p> | |
* The style value is either one of the style constants defined in | |
* class <code>SWT</code> which is applicable to instances of this | |
* class, or must be built by <em>bitwise OR</em>'ing together | |
* (that is, using the <code>int</code> "|" operator) two or more | |
* of those <code>SWT</code> style constants. The class description | |
* for all SWT widget classes should include a comment which | |
* describes the style constants which are applicable to the class. | |
* </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 | |
* @see Widget#checkSubclass | |
* @see Widget#getStyle | |
*/ | |
public Tree (Composite parent, int style) { | |
super (parent, checkStyle (style)); | |
} | |
/** | |
* Adds the listener to the collection of listeners who will | |
* be notified when the receiver's selection changes, 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 reciever has <code>SWT.CHECK</code> style set 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. | |
* </p> | |
* | |
* @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 SelectionListener | |
* @see #removeSelectionListener | |
* @see SelectionEvent | |
*/ | |
public void addSelectionListener(SelectionListener listener) { | |
checkWidget (); | |
if (listener == null) error (SWT.ERROR_NULL_ARGUMENT); | |
TypedListener typedListener = new TypedListener (listener); | |
addListener (SWT.Selection, typedListener); | |
addListener (SWT.DefaultSelection, typedListener); | |
} | |
/** | |
* Adds the listener to the collection of listeners who will | |
* be notified when an item in the receiver is expanded or collapsed | |
* by sending it one of the messages defined in the <code>TreeListener</code> | |
* interface. | |
* | |
* @param listener the listener which should be notified | |
* | |
* @exception IllegalArgumentException <ul> | |
* <li>ERROR_NULL_ARGUMENT - if the listener is null</li> | |
* </ul> | |
* @exception SWTException <ul> | |
* <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> | |
* <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> | |
* </ul> | |
* | |
* @see TreeListener | |
* @see #removeTreeListener | |
*/ | |
public void addTreeListener(TreeListener listener) { | |
checkWidget (); | |
if (listener == null) error (SWT.ERROR_NULL_ARGUMENT); | |
TypedListener typedListener = new TypedListener (listener); | |
addListener (SWT.Expand, typedListener); | |
addListener (SWT.Collapse, typedListener); | |
} | |
int callWindowProc (int msg, int wParam, int lParam) { | |
if (handle == 0) return 0; | |
return OS.CallWindowProc (TreeProc, handle, msg, wParam, lParam); | |
} | |
static int checkStyle (int style) { | |
/* | |
* Feature in Windows. It is not possible to create | |
* a tree that scrolls and does not have scroll bars. | |
* The TVS_NOSCROLL style will remove the scroll bars | |
* but the tree will never scroll. Therefore, no matter | |
* what style bits are specified, set the H_SCROLL and | |
* V_SCROLL bits so that the SWT style will match the | |
* widget that Windows creates. | |
*/ | |
style |= SWT.H_SCROLL | SWT.V_SCROLL; | |
return checkBits (style, SWT.SINGLE, SWT.MULTI, 0, 0, 0, 0); | |
} | |
public Point computeSize (int wHint, int hHint, boolean changed) { | |
checkWidget (); | |
int width = 0, height = 0; | |
RECT rect = new RECT (); | |
int hItem = OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_ROOT, 0); | |
while (hItem != 0) { | |
rect.left = hItem; | |
if (OS.SendMessage (handle, OS.TVM_GETITEMRECT, 1, rect) != 0) { | |
width = Math.max (width, rect.right - rect.left); | |
height += rect.bottom - rect.top; | |
} | |
hItem = OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_NEXT, hItem); | |
} | |
width = width * 2; | |
if (width == 0) width = DEFAULT_WIDTH; | |
if (height == 0) height = DEFAULT_HEIGHT; | |
if (wHint != SWT.DEFAULT) width = wHint; | |
if (hHint != SWT.DEFAULT) height = hHint; | |
int border = getBorderWidth (); | |
width += border * 2; height += border * 2; | |
if ((style & SWT.V_SCROLL) != 0) { | |
width += OS.GetSystemMetrics (OS.SM_CXVSCROLL); | |
} | |
if ((style & SWT.H_SCROLL) != 0) { | |
height += OS.GetSystemMetrics (OS.SM_CYHSCROLL); | |
} | |
return new Point (width, height); | |
} | |
void createHandle () { | |
super.createHandle (); | |
state &= ~CANVAS; | |
/* Set the checkbox image list */ | |
if ((style & SWT.CHECK) != 0) setCheckboxImageList (); | |
/* | |
* Feature in Windows. When the control is created, | |
* it does not use the default system font. A new HFONT | |
* is created and destroyed when the control is destroyed. | |
* This means that a program that queries the font from | |
* this control, uses the font in another control and then | |
* destroys this control will have the font unexpectedly | |
* destroyed in the other control. The fix is to assign | |
* the font ourselves each time the control is created. | |
* The control will not destroy a font that it did not | |
* create. | |
*/ | |
int hFont = OS.GetStockObject (OS.SYSTEM_FONT); | |
OS.SendMessage (handle, OS.WM_SETFONT, hFont, 0); | |
} | |
void createItem (TreeItem item, int hParent, int hInsertAfter) { | |
int id = 0; | |
while (id < items.length && items [id] != null) id++; | |
if (id == items.length) { | |
TreeItem [] newItems = new TreeItem [items.length + 4]; | |
System.arraycopy (items, 0, newItems, 0, items.length); | |
items = newItems; | |
} | |
TVINSERTSTRUCT tvInsert = new TVINSERTSTRUCT (); | |
tvInsert.hParent = hParent; | |
tvInsert.hInsertAfter = hInsertAfter; | |
tvInsert.lParam = id; | |
tvInsert.iImage = OS.I_IMAGENONE; | |
tvInsert.iSelectedImage = tvInsert.iImage; | |
tvInsert.mask = OS.TVIF_HANDLE | OS.TVIF_PARAM | OS.TVIF_IMAGE | OS.TVIF_SELECTEDIMAGE; | |
/* Set the initial unchecked state */ | |
if ((style & SWT.CHECK) != 0) { | |
tvInsert.mask = tvInsert.mask | OS.TVIF_STATE; | |
tvInsert.state = 1 << 12; | |
tvInsert.stateMask = OS.TVIS_STATEIMAGEMASK; | |
} | |
/* Insert the item */ | |
int hItem = OS.SendMessage (handle, OS.TVM_INSERTITEM, 0, tvInsert); | |
if (hItem == 0) error (SWT.ERROR_ITEM_NOT_ADDED); | |
item.handle = hItem; | |
items [id] = item; | |
/* | |
* This code is intentionally commented. | |
*/ | |
// if (hParent != 0) { | |
// int bits = OS.GetWindowLong (handle, OS.GWL_STYLE); | |
// bits |= OS.TVS_LINESATROOT; | |
// OS.SetWindowLong (handle, OS.GWL_STYLE, bits); | |
// } | |
/* | |
* Bug in Windows. When a child item ss 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 this case and force a redraw. | |
*/ | |
if (!OS.IsWindowVisible (handle) || drawCount > 0) return; | |
int hChild = OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_CHILD, hParent); | |
if (hChild == 0 || OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_NEXT, hChild) != 0) { | |
return; | |
} | |
RECT rect = new RECT (); | |
rect.left = hParent; | |
if (OS.SendMessage (handle, OS.TVM_GETITEMRECT, 0, rect) != 0) { | |
OS.InvalidateRect (handle, rect, false); | |
} | |
} | |
ScrollBar createScrollBar (int type) { | |
return new ScrollBar (this, type); | |
} | |
void createWidget () { | |
super.createWidget (); | |
items = new TreeItem [4]; | |
} | |
int defaultBackground () { | |
return OS.GetSysColor (OS.COLOR_WINDOW); | |
} | |
/** | |
* Deselects all selected items in the receiver. | |
* | |
* @exception SWTException <ul> | |
* <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> | |
* <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> | |
* </ul> | |
*/ | |
public void deselectAll () { | |
checkWidget (); | |
TVITEM tvItem = new TVITEM (); | |
tvItem.mask = OS.TVIF_STATE; | |
tvItem.stateMask = OS.TVIS_SELECTED; | |
if ((style & SWT.SINGLE) != 0) { | |
int hItem = OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_CARET, 0); | |
if (hItem != 0) { | |
tvItem.hItem = hItem; | |
OS.SendMessage (handle, OS.TVM_SETITEM, 0, tvItem); | |
} | |
return; | |
} | |
int oldProc = OS.GetWindowLong (handle, OS.GWL_WNDPROC); | |
OS.SetWindowLong (handle, OS.GWL_WNDPROC, TreeProc); | |
for (int i=0; i<items.length; i++) { | |
TreeItem item = items [i]; | |
if (item != null) { | |
tvItem.hItem = item.handle; | |
OS.SendMessage (handle, OS.TVM_SETITEM, 0, tvItem); | |
} | |
} | |
OS.SetWindowLong (handle, OS.GWL_WNDPROC, oldProc); | |
} | |
void destroyItem (TreeItem item) { | |
int hItem = item.handle; | |
TVITEM tvItem = new TVITEM (); | |
tvItem.mask = OS.TVIF_HANDLE | OS.TVIF_PARAM; | |
releaseItems (new TreeItem [] {item}, tvItem); | |
boolean fixRedraw = false; | |
if (drawCount == 0 && OS.IsWindowVisible (handle)) { | |
RECT rect = new RECT (); | |
rect.left = hItem; | |
fixRedraw = OS.SendMessage (handle, OS.TVM_GETITEMRECT, 0, rect) == 0; | |
} | |
if (fixRedraw) { | |
OS.UpdateWindow (handle); | |
OS.SendMessage (handle, OS.WM_SETREDRAW, 0, 0); | |
} | |
int result = OS.SendMessage (handle, OS.TVM_DELETEITEM, 0, hItem); | |
if (fixRedraw) { | |
OS.SendMessage (handle, OS.WM_SETREDRAW, 1, 0); | |
OS.ValidateRect (handle, null); | |
} | |
int count = OS.SendMessage (handle, OS.TVM_GETCOUNT, 0, 0); | |
if (count == 0) { | |
if (imageList != null) { | |
OS.SendMessage (handle, OS.TVM_SETIMAGELIST, 0, 0); | |
imageList.dispose (); | |
} | |
imageList = null; | |
items = new TreeItem [4]; | |
} | |
} | |
int getBackgroundPixel () { | |
int pixel = OS.SendMessage (handle, OS.TVM_GETBKCOLOR, 0, 0); | |
if (pixel == -1) return OS.GetSysColor (OS.COLOR_WINDOW); | |
return pixel; | |
} | |
int getForegroundPixel () { | |
int pixel = OS.SendMessage (handle, OS.TVM_GETTEXTCOLOR, 0, 0); | |
if (pixel == -1) return OS.GetSysColor (OS.COLOR_WINDOWTEXT); | |
return pixel; | |
} | |
/** | |
* 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. | |
* | |
* @param point the point used to locate the item | |
* @return the item at the given point | |
* | |
* @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> | |
*/ | |
public TreeItem getItem (Point point) { | |
checkWidget (); | |
if (point == null) error (SWT.ERROR_NULL_ARGUMENT); | |
TVHITTESTINFO lpht = new TVHITTESTINFO (); | |
lpht.x = point.x; lpht.y = point.y; | |
OS.SendMessage (handle, OS.TVM_HITTEST, 0, lpht); | |
if (lpht.hItem != 0 && (lpht.flags & OS.TVHT_ONITEM) != 0) { | |
TVITEM tvItem = new TVITEM (); | |
tvItem.mask = OS.TVIF_HANDLE | OS.TVIF_PARAM; | |
tvItem.hItem = lpht.hItem; | |
OS.SendMessage (handle, OS.TVM_GETITEM, 0, tvItem); | |
return items [tvItem.lParam]; | |
} | |
return null; | |
} | |
/** | |
* Returns the number of items contained in the receiver | |
* that are direct item children of the receiver. The | |
* number that is returned is the number of roots in the | |
* tree. | |
* | |
* @return the number of items | |
* | |
* @exception SWTException <ul> | |
* <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> | |
* <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> | |
* </ul> | |
*/ | |
public int getItemCount () { | |
checkWidget (); | |
int count = 0; | |
int hItem = OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_ROOT, 0); | |
while (hItem != 0) { | |
hItem = OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_NEXT, hItem); | |
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 the number 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 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 TreeItem [] getItems () { | |
checkWidget (); | |
int count = 0; | |
int hItem = OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_ROOT, 0); | |
while (hItem != 0) { | |
hItem = OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_NEXT, hItem); | |
count++; | |
} | |
int index = 0; | |
TreeItem [] result = new TreeItem [count]; | |
TVITEM tvItem = new TVITEM (); | |
tvItem.mask = OS.TVIF_HANDLE | OS.TVIF_PARAM; | |
tvItem.hItem = OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_ROOT, 0); | |
while (tvItem.hItem != 0) { | |
OS.SendMessage (handle, OS.TVM_GETITEM, 0, tvItem); | |
result [index++] = items [tvItem.lParam]; | |
tvItem.hItem = OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_NEXT, tvItem.hItem); | |
} | |
return result; | |
} | |
/** | |
* 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; | |
} | |
/** | |
* Returns an array of <code>TreeItem</code>s that are currently | |
* selected in the receiver. An empty array indicates that no | |
* items are selected. | |
* <p> | |
* Note: This is not the actual structure used by the receiver | |
* to maintain its selection, so modifying the array will | |
* not affect the receiver. | |
* </p> | |
* @return an array representing the selection | |
* | |
* @exception SWTException <ul> | |
* <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> | |
* <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> | |
* </ul> | |
*/ | |
public TreeItem [] getSelection () { | |
checkWidget (); | |
if ((style & SWT.SINGLE) != 0) { | |
int hItem = OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_CARET, 0); | |
if (hItem == 0) return new TreeItem [0]; | |
TVITEM tvItem = new TVITEM (); | |
tvItem.mask = OS.TVIF_PARAM | OS.TVIF_STATE; | |
tvItem.hItem = hItem; | |
OS.SendMessage (handle, OS.TVM_GETITEM, 0, tvItem); | |
if ((tvItem.state & OS.TVIS_SELECTED) == 0) return new TreeItem [0]; | |
return new TreeItem [] {items [tvItem.lParam]}; | |
} | |
int count = 0; | |
TVITEM tvItem = new TVITEM (); | |
tvItem.mask = OS.TVIF_PARAM | OS.TVIF_STATE; | |
int oldProc = OS.GetWindowLong (handle, OS.GWL_WNDPROC); | |
OS.SetWindowLong (handle, OS.GWL_WNDPROC, TreeProc); | |
for (int i=0; i<items.length; i++) { | |
TreeItem item = items [i]; | |
if (item != null) { | |
tvItem.hItem = item.handle; | |
OS.SendMessage (handle, OS.TVM_GETITEM, 0, tvItem); | |
if ((tvItem.state & OS.TVIS_SELECTED) != 0) count++; | |
} | |
} | |
TreeItem [] result = new TreeItem [count]; | |
if (count != 0) { | |
int index = 0; | |
for (int i=0; i<items.length; i++) { | |
TreeItem item = items [i]; | |
if (item != null) { | |
tvItem.hItem = item.handle; | |
OS.SendMessage (handle, OS.TVM_GETITEM, 0, tvItem); | |
if ((tvItem.state & OS.TVIS_SELECTED) != 0) { | |
result [index++] = item; | |
} | |
} | |
} | |
} | |
OS.SetWindowLong (handle, OS.GWL_WNDPROC, oldProc); | |
return result; | |
} | |
/** | |
* Returns the number of selected items contained in the receiver. | |
* | |
* @return the number of selected items | |
* | |
* @exception SWTException <ul> | |
* <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> | |
* <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> | |
* </ul> | |
*/ | |
public int getSelectionCount () { | |
checkWidget (); | |
if ((style & SWT.SINGLE) != 0) { | |
int hItem = OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_CARET, 0); | |
if (hItem == 0) return 0; | |
TVITEM tvItem = new TVITEM (); | |
tvItem.mask = OS.TVIF_STATE; | |
tvItem.hItem = hItem; | |
OS.SendMessage (handle, OS.TVM_GETITEM, 0, tvItem); | |
if ((tvItem.state & OS.TVIS_SELECTED) == 0) return 0; | |
return 1; | |
} | |
int count = 0; | |
TVITEM tvItem = new TVITEM (); | |
tvItem.mask = OS.TVIF_STATE; | |
int oldProc = OS.GetWindowLong (handle, OS.GWL_WNDPROC); | |
OS.SetWindowLong (handle, OS.GWL_WNDPROC, TreeProc); | |
for (int i=0; i<items.length; i++) { | |
TreeItem item = items [i]; | |
if (item != null) { | |
tvItem.hItem = item.handle; | |
OS.SendMessage (handle, OS.TVM_GETITEM, 0, tvItem); | |
if ((tvItem.state & OS.TVIS_SELECTED) != 0) count++; | |
} | |
} | |
OS.SetWindowLong (handle, OS.GWL_WNDPROC, oldProc); | |
return count; | |
} | |
int imageIndex (Image image) { | |
if (image == null) return OS.I_IMAGENONE; | |
if (imageList == null) { | |
int hOldList = OS.SendMessage (handle, OS.TVM_GETIMAGELIST, OS.TVSIL_NORMAL, 0); | |
if (hOldList != 0) OS.ImageList_Destroy (hOldList); | |
imageList = new ImageList (); | |
imageList.setBackground (getBackgroundPixel ()); | |
imageList.add (image); | |
int hImageList = imageList.getHandle (); | |
OS.SendMessage (handle, OS.TVM_SETIMAGELIST, OS.TVSIL_NORMAL, hImageList); | |
return 0; | |
} | |
int index = imageList.indexOf (image); | |
if (index != -1) return index; | |
return imageList.add (image); | |
} | |
void releaseItems (TreeItem [] nodes, TVITEM tvItem) { | |
for (int i=0; i<nodes.length; i++) { | |
TreeItem item = nodes [i]; | |
TreeItem [] sons = item.getItems (); | |
if (sons.length != 0) { | |
releaseItems (sons, tvItem); | |
} | |
int hItem = item.handle; | |
if (hItem == hAnchor) hAnchor = 0; | |
if (!item.isDisposed ()) { | |
tvItem.hItem = hItem; | |
OS.SendMessage (handle, OS.TVM_GETITEM, 0, tvItem); | |
items [tvItem.lParam] = null; | |
item.releaseWidget (); | |
item.releaseHandle (); | |
} | |
} | |
} | |
void releaseWidget () { | |
for (int i=0; i<items.length; i++) { | |
TreeItem item = items [i]; | |
if (item != null && !item.isDisposed ()) { | |
item.releaseWidget (); | |
item.releaseHandle (); | |
} | |
} | |
items = null; | |
super.releaseWidget (); | |
if (imageList != null) { | |
OS.SendMessage (handle, OS.TVM_SETIMAGELIST, OS.TVSIL_NORMAL, 0); | |
imageList.dispose (); | |
} else { | |
int hOldList = OS.SendMessage (handle, OS.TVM_GETIMAGELIST, OS.TVSIL_NORMAL, 0); | |
OS.SendMessage (handle, OS.TVM_SETIMAGELIST, OS.TVSIL_NORMAL, 0); | |
if (hOldList != 0) OS.ImageList_Destroy (hOldList); | |
} | |
imageList = null; | |
int hOldList = OS.SendMessage (handle, OS.TVM_GETIMAGELIST, OS.TVSIL_STATE, 0); | |
OS.SendMessage (handle, OS.TVM_SETIMAGELIST, OS.TVSIL_STATE, 0); | |
if (hOldList != 0) OS.ImageList_Destroy (hOldList); | |
} | |
/** | |
* Removes all of the items from the receiver. | |
* <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 removeAll () { | |
checkWidget (); | |
ignoreDeselect = ignoreSelect = true; | |
int result = OS.SendMessage (handle, OS.TVM_DELETEITEM, 0, OS.TVI_ROOT); | |
ignoreDeselect = ignoreSelect = false; | |
if (result == 0) error (SWT.ERROR_ITEM_NOT_REMOVED); | |
for (int i=0; i<items.length; i++) { | |
TreeItem item = items [i]; | |
if (item != null && !item.isDisposed ()) { | |
item.releaseWidget (); | |
item.releaseHandle (); | |
} | |
} | |
if (imageList != null) { | |
OS.SendMessage (handle, OS.TVM_SETIMAGELIST, 0, 0); | |
imageList.dispose (); | |
} | |
imageList = null; | |
items = new TreeItem [4]; | |
hAnchor = 0; | |
} | |
/** | |
* Removes the listener from the collection of listeners who will | |
* be notified when the receiver's selection changes. | |
* | |
* @param listener the listener which should no longer be notified | |
* | |
* @exception IllegalArgumentException <ul> | |
* <li>ERROR_NULL_ARGUMENT - if the listener is null</li> | |
* </ul> | |
* @exception SWTException <ul> | |
* <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> | |
* <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> | |
* </ul> | |
* | |
* @see SelectionListener | |
* @see #addSelectionListener | |
*/ | |
public void removeSelectionListener (SelectionListener listener) { | |
checkWidget (); | |
if (listener == null) error (SWT.ERROR_NULL_ARGUMENT); | |
eventTable.unhook (SWT.Selection, listener); | |
eventTable.unhook (SWT.DefaultSelection, listener); | |
} | |
/** | |
* Removes the listener from the collection of listeners who will | |
* be notified when items in the receiver are expanded or collapsed.. | |
* | |
* @param listener the listener which should no longer be notified | |
* | |
* @exception IllegalArgumentException <ul> | |
* <li>ERROR_NULL_ARGUMENT - if the listener is null</li> | |
* </ul> | |
* @exception SWTException <ul> | |
* <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> | |
* <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> | |
* </ul> | |
* | |
* @see TreeListener | |
* @see #addTreeListener | |
*/ | |
public void removeTreeListener(TreeListener listener) { | |
checkWidget (); | |
if (listener == null) error (SWT.ERROR_NULL_ARGUMENT); | |
if (eventTable == null) return; | |
eventTable.unhook (SWT.Expand, listener); | |
eventTable.unhook (SWT.Collapse, listener); | |
} | |
/** | |
* Display a mark indicating the point at which an item will be inserted. | |
* The drop insert item has a visual hint to show where a dragged item | |
* will be inserted when dropped on the tree. | |
* | |
* @param item the insert item. Null will clear the insertion mark. | |
* @param after true places the insert mark above 'item'. false places | |
* the insert mark below '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 void setInsertMark (TreeItem item, boolean before) { | |
checkWidget (); | |
int hItem = 0; | |
if (item != null) hItem = item.handle; | |
OS.SendMessage (handle, OS.TVM_SETINSERTMARK, (before) ? 0 : 1, hItem); | |
} | |
/** | |
* Selects all 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> | |
*/ | |
public void selectAll () { | |
checkWidget (); | |
if ((style & SWT.SINGLE) != 0) return; | |
int hItem = OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_CARET, 0); | |
if (hItem == 0) { | |
hItem = OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_ROOT, 0); | |
if (hItem != 0) { | |
ignoreSelect = true; | |
OS.SendMessage (handle, OS.TVM_SELECTITEM, OS.TVGN_CARET, hItem); | |
ignoreSelect = false; | |
OS.SendMessage (handle, OS.TVM_ENSUREVISIBLE, 0, hItem); | |
} | |
} | |
TVITEM tvItem = new TVITEM (); | |
tvItem.mask = OS.TVIF_STATE; | |
tvItem.state = OS.TVIS_SELECTED; | |
tvItem.stateMask = OS.TVIS_SELECTED; | |
int oldProc = OS.GetWindowLong (handle, OS.GWL_WNDPROC); | |
OS.SetWindowLong (handle, OS.GWL_WNDPROC, TreeProc); | |
for (int i=0; i<items.length; i++) { | |
TreeItem item = items [i]; | |
if (item != null) { | |
tvItem.hItem = item.handle; | |
OS.SendMessage (handle, OS.TVM_SETITEM, 0, tvItem); | |
} | |
} | |
OS.SetWindowLong (handle, OS.GWL_WNDPROC, oldProc); | |
} | |
void setBackgroundPixel (int pixel) { | |
if (background == pixel) return; | |
background = pixel; | |
/* | |
* Bug in Windows. When TVM_GETBKCOLOR 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. | |
*/ | |
int oldPixel = OS.SendMessage (handle, OS.TVM_GETBKCOLOR, 0, 0); | |
if (oldPixel != -1) OS.SendMessage (handle, OS.TVM_SETBKCOLOR, 0, -1); | |
OS.SendMessage (handle, OS.TVM_SETBKCOLOR, 0, pixel); | |
if (pixel == -1) pixel = getBackgroundPixel (); | |
if (imageList != null) { | |
imageList.setBackground (pixel); | |
int hImageList = imageList.getHandle (); | |
OS.SendMessage (handle, OS.TVM_SETIMAGELIST, OS.TVSIL_NORMAL, hImageList); | |
} | |
if ((style & SWT.CHECK) != 0) setCheckboxImageList (); | |
} | |
void setCheckboxImageList () { | |
if ((style & SWT.CHECK) == 0) return; | |
int count = 5; | |
int height = OS.SendMessage (handle, OS.TVM_GETITEMHEIGHT, 0, 0), width = height; | |
int hImageList = OS.ImageList_Create (width, height, OS.ILC_COLOR, count, count); | |
int hDC = OS.GetDC (handle); | |
int memDC = OS.CreateCompatibleDC (hDC); | |
int hBitmap = OS.CreateCompatibleBitmap (hDC, width * count, height); | |
int hOldBitmap = OS.SelectObject (memDC, hBitmap); | |
RECT rect = new RECT (); | |
OS.SetRect (rect, 0, 0, width * count, height); | |
int hBrush = OS.CreateSolidBrush (getBackgroundPixel ()); | |
OS.FillRect (memDC, rect, hBrush); | |
OS.DeleteObject (hBrush); | |
int oldFont = OS.SelectObject (hDC, defaultFont ()); | |
TEXTMETRIC tm = new TEXTMETRIC (); | |
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); | |
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); | |
OS.ImageList_AddMasked (hImageList, hBitmap, 0); | |
OS.DeleteObject (hBitmap); | |
int hOldList = OS.SendMessage (handle, OS.TVM_GETIMAGELIST, OS.TVSIL_STATE, 0); | |
OS.SendMessage (handle, OS.TVM_SETIMAGELIST, OS.TVSIL_STATE, hImageList); | |
if (hOldList != 0) OS.ImageList_Destroy (hOldList); | |
} | |
void setForegroundPixel (int pixel) { | |
if (foreground == pixel) return; | |
foreground = pixel; | |
OS.SendMessage (handle, OS.TVM_SETTEXTCOLOR, 0, pixel); | |
} | |
/** | |
* Sets the receiver's selection to be the given array of items. | |
* The current selected is first cleared, then the new items are | |
* selected. | |
* | |
* @param items the array of items | |
* | |
* @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 Tree#deselectAll() | |
*/ | |
public void setSelection (TreeItem [] items) { | |
checkWidget (); | |
if (items == null) error (SWT.ERROR_NULL_ARGUMENT); | |
/* Select/deselect the first item */ | |
int hOldItem = OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_CARET, 0); | |
if (items.length == 0) { | |
if (hOldItem != 0) { | |
TVITEM tvItem = new TVITEM (); | |
tvItem.mask = OS.TVIF_STATE; | |
tvItem.stateMask = OS.TVIS_SELECTED; | |
tvItem.hItem = hOldItem; | |
OS.SendMessage (handle, OS.TVM_SETITEM, 0, tvItem); | |
} | |
} else { | |
int hNewItem = 0; | |
TreeItem item = items [0]; | |
if (item != null) hNewItem = item.handle; | |
ignoreSelect = true; | |
OS.SendMessage (handle, OS.TVM_SELECTITEM, OS.TVGN_CARET, hNewItem); | |
ignoreSelect = false; | |
/* | |
* 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. | |
*/ | |
if (hOldItem == hNewItem) { | |
TVITEM tvItem = new TVITEM (); | |
tvItem.mask = OS.TVIF_STATE; | |
tvItem.state = OS.TVIS_SELECTED; | |
tvItem.stateMask = OS.TVIS_SELECTED; | |
tvItem.hItem = hNewItem; | |
OS.SendMessage (handle, OS.TVM_SETITEM, 0, tvItem); | |
} | |
} | |
if ((style & SWT.SINGLE) != 0) return; | |
/* Select/deselect the rest of the items */ | |
TVITEM tvItem = new TVITEM (); | |
tvItem.mask = OS.TVIF_STATE; | |
tvItem.stateMask = OS.TVIS_SELECTED; | |
int oldProc = OS.GetWindowLong (handle, OS.GWL_WNDPROC); | |
OS.SetWindowLong (handle, OS.GWL_WNDPROC, TreeProc); | |
for (int i=0; i<this.items.length; i++) { | |
TreeItem item = this.items [i]; | |
if (item != null) { | |
int index = 0; | |
while (index < items.length) { | |
if (items [index] == item) break; | |
index++; | |
} | |
tvItem.hItem = item.handle; | |
OS.SendMessage (handle, OS.TVM_GETITEM, 0, tvItem); | |
if ((tvItem.state & OS.TVIS_SELECTED) != 0) { | |
if (index == items.length) { | |
tvItem.state = 0; | |
OS.SendMessage (handle, OS.TVM_SETITEM, 0, tvItem); | |
} | |
} else { | |
if (index != items.length) { | |
tvItem.state = OS.TVIS_SELECTED; | |
OS.SendMessage (handle, OS.TVM_SETITEM, 0, tvItem); | |
} | |
} | |
} | |
} | |
OS.SetWindowLong (handle, OS.GWL_WNDPROC, oldProc); | |
} | |
/** | |
* 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 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 Tree#showSelection() | |
*/ | |
public void showItem (TreeItem item) { | |
checkWidget (); | |
if (item == null) error (SWT.ERROR_NULL_ARGUMENT); | |
OS.SendMessage (handle, OS.TVM_ENSUREVISIBLE, 0, 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 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 Tree#showItem(TreeItem) | |
*/ | |
public void showSelection () { | |
checkWidget (); | |
int hItem = OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_CARET, 0); | |
if (hItem == 0) return; | |
OS.SendMessage (handle, OS.TVM_ENSUREVISIBLE, 0, hItem); | |
} | |
int widgetStyle () { | |
int bits = super.widgetStyle () | OS.WS_TABSTOP | OS.TVS_SHOWSELALWAYS; | |
bits |= OS.TVS_LINESATROOT | OS.TVS_HASLINES | OS.TVS_HASBUTTONS; | |
/* | |
* This code is intentionally commented. In future, | |
* FULL_SELECTION may be implemented for trees and | |
* automatic tool tips turned off. | |
*/ | |
// if ((style & SWT.FULL_SELECTION) != 0) { | |
// bits |= OS.TVS_FULLROWSELECT; | |
// } else { | |
// bits |= OS.TVS_HASLINES | OS.TVS_HASBUTTONS; | |
// } | |
// bits |= OS.TVS_NOTOOLTIPS; | |
return bits; | |
} | |
byte [] windowClass () { | |
return TreeClass; | |
} | |
int windowProc () { | |
return TreeProc; | |
} | |
LRESULT WM_CHAR (int wParam, int lParam) { | |
LRESULT result = super.WM_CHAR (wParam, lParam); | |
if (result != null) return result; | |
/* | |
* Feature in Windows. The tree control beeps | |
* in WM_CHAR when the search for the item that | |
* matches the key stroke fails. This is the | |
* standard tree behavior but is unexpected when | |
* the key that was typed was CR or SPACE. The | |
* fix is to avoid calling the tree window proc | |
* in these cases. | |
*/ | |
switch (wParam) { | |
case OS.VK_RETURN: | |
case OS.VK_SPACE: return LRESULT.ZERO; | |
} | |
return result; | |
} | |
LRESULT WM_KEYDOWN (int wParam, int lParam) { | |
LRESULT result = super.WM_KEYDOWN (wParam, lParam); | |
if (result != null) return result; | |
if ((style & SWT.CHECK) != 0 && wParam == OS.VK_SPACE) { | |
int hItem = OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_CARET, 0); | |
if (hItem != 0) { | |
TVITEM tvItem = new TVITEM (); | |
tvItem.mask = OS.TVIF_HANDLE | OS.TVIF_STATE | OS.TVIF_PARAM; | |
tvItem.stateMask = OS.TVIS_STATEIMAGEMASK; | |
tvItem.hItem = hItem; | |
OS.SendMessage (handle, OS.TVM_GETITEM, 0, tvItem); | |
int state = tvItem.state >> 12; | |
if ((state & 0x1) != 0) { | |
state++; | |
} else { | |
--state; | |
} | |
tvItem.state = state << 12; | |
OS.SendMessage (handle, OS.TVM_SETITEM, 0, tvItem); | |
Event event = new Event (); | |
event.item = items [tvItem.lParam]; | |
event.detail = SWT.CHECK; | |
postEvent (SWT.Selection, event); | |
} | |
} | |
return result; | |
} | |
LRESULT WM_KILLFOCUS (int wParam, int lParam) { | |
LRESULT result = super.WM_KILLFOCUS (wParam, lParam); | |
if (result != null) return result; | |
if ((style & SWT.SINGLE) != 0) return result; | |
/* | |
* 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 all the | |
* visible items when focus is gained or lost. | |
*/ | |
OS.InvalidateRect (handle, null, false); | |
return result; | |
} | |
LRESULT WM_LBUTTONDOWN (int wParam, int lParam) { | |
/* | |
* Feature in Windows. When a tree item is | |
* reselected, Windows does not issue a WM_NOTIFY. | |
* This is inconsistent with the list widget and | |
* other widgets in Windows. The fix is to detect | |
* the case when an item is reselected and issue | |
* the notification. | |
*/ | |
TVHITTESTINFO lpht = new TVHITTESTINFO (); | |
lpht.x = (short) (lParam & 0xFFFF); | |
lpht.y = (short) (lParam >> 16); | |
OS.SendMessage (handle, OS.TVM_HITTEST, 0, lpht); | |
if (lpht.hItem == 0 || (lpht.flags & OS.TVHT_ONITEM) == 0) { | |
sendMouseEvent (SWT.MouseDown, 1, OS.WM_LBUTTONDOWN, wParam, lParam); | |
int code = callWindowProc (OS.WM_LBUTTONDOWN, wParam, lParam); | |
if (OS.GetCapture () != handle) OS.SetCapture (handle); | |
return new LRESULT (code); | |
} | |
/* Look for check/uncheck */ | |
if ((style & SWT.CHECK) != 0) { | |
if ((lpht.flags & OS.TVHT_ONITEMSTATEICON) != 0) { | |
TVITEM tvItem = new TVITEM (); | |
tvItem.hItem = lpht.hItem; | |
tvItem.mask = OS.TVIF_PARAM | OS.TVIF_STATE; | |
tvItem.stateMask = OS.TVIS_STATEIMAGEMASK; | |
OS.SendMessage (handle, OS.TVM_GETITEM, 0, tvItem); | |
int state = tvItem.state >> 12; | |
if ((state & 0x1) != 0) { | |
state++; | |
} else { | |
--state; | |
} | |
tvItem.state = state << 12; | |
OS.SendMessage (handle, OS.TVM_SETITEM, 0, tvItem); | |
Event event = new Event (); | |
event.item = items [tvItem.lParam]; | |
event.detail = SWT.CHECK; | |
postEvent (SWT.Selection, event); | |
sendMouseEvent (SWT.MouseDown, 1, OS.WM_LBUTTONDOWN, wParam, lParam); | |
if (OS.GetCapture () != handle) OS.SetCapture (handle); | |
return LRESULT.ZERO; | |
} | |
} | |
/* Get the selected state of the item under the mouse */ | |
TVITEM tvItem = new TVITEM (); | |
tvItem.mask = OS.TVIF_STATE; | |
tvItem.stateMask = OS.TVIS_SELECTED; | |
boolean hittestSelected = false; | |
if ((style & SWT.MULTI) != 0) { | |
tvItem.hItem = lpht.hItem; | |
OS.SendMessage (handle, OS.TVM_GETITEM, 0, tvItem); | |
hittestSelected = (tvItem.state & OS.TVIS_SELECTED) != 0; | |
} | |
/* Get the selected state of the last selected item */ | |
int hOldItem = OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_CARET, 0); | |
if ((style & SWT.MULTI) != 0) { | |
tvItem.hItem = hOldItem; | |
OS.SendMessage (handle, OS.TVM_GETITEM, 0, tvItem); | |
/* Check for CONTROL or drag selection */ | |
if (hittestSelected || (wParam & OS.MK_CONTROL) != 0) { | |
if (drawCount == 0) { | |
OS.UpdateWindow (handle); | |
OS.DefWindowProc (handle, OS.WM_SETREDRAW, 0, 0); | |
/* | |
* This code is intentionally commented. | |
*/ | |
// OS.SendMessage (handle, OS.WM_SETREDRAW, 0, 0); | |
} | |
} else { | |
deselectAll (); | |
} | |
} | |
/* Do the selection */ | |
sendMouseEvent (SWT.MouseDown, 1, OS.WM_LBUTTONDOWN, wParam, lParam); | |
dragStarted = false; | |
ignoreDeselect = ignoreSelect = true; | |
int code = callWindowProc (OS.WM_LBUTTONDOWN, wParam, lParam); | |
ignoreDeselect = ignoreSelect = false; | |
if (dragStarted && OS.GetCapture () != handle) OS.SetCapture (handle); | |
int hNewItem = OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_CARET, 0); | |
/* Reselect the last item that was unselected */ | |
if ((style & SWT.MULTI) != 0) { | |
/* Check for CONTROL and reselect the last item */ | |
if (hittestSelected || (wParam & OS.MK_CONTROL) != 0) { | |
if (hOldItem == hNewItem && hOldItem == lpht.hItem) { | |
if ((wParam & OS.MK_CONTROL) != 0) { | |
tvItem.state ^= OS.TVIS_SELECTED; | |
if (dragStarted) tvItem.state = OS.TVIS_SELECTED; | |
OS.SendMessage (handle, OS.TVM_SETITEM, 0, tvItem); | |
} | |
} else { | |
if ((tvItem.state & OS.TVIS_SELECTED) != 0) { | |
tvItem.state = OS.TVIS_SELECTED; | |
OS.SendMessage (handle, OS.TVM_SETITEM, 0, tvItem); | |
} | |
if ((wParam & OS.MK_CONTROL) != 0 && !dragStarted) { | |
if (hittestSelected) { | |
tvItem.state = 0; | |
tvItem.hItem = lpht.hItem; | |
OS.SendMessage (handle, OS.TVM_SETITEM, 0, tvItem); | |
} | |
} | |
} | |
if (drawCount == 0) { | |
RECT rect1 = new RECT (), rect2 = new RECT (); | |
rect1.left = hOldItem; rect2.left = hNewItem; | |
OS.SendMessage (handle, OS.TVM_GETITEMRECT, 1, rect1); | |
OS.SendMessage (handle, OS.TVM_GETITEMRECT, 1, rect2); | |
/* | |
* This code is intentionally commented. | |
*/ | |
// OS.SendMessage (handle, OS.WM_SETREDRAW, 1, 0); | |
OS.DefWindowProc (handle, OS.WM_SETREDRAW, 1, 0); | |
int flags = OS.RDW_UPDATENOW | OS.RDW_INVALIDATE; | |
OS.RedrawWindow (handle, rect1, 0, flags); | |
OS.RedrawWindow (handle, rect2, 0, flags); | |
} | |
} | |
/* Check for SHIFT or normal select and delect/reselect items */ | |
if ((wParam & OS.MK_CONTROL) == 0) { | |
if (!hittestSelected || !dragStarted) { | |
tvItem.state = 0; | |
int oldProc = OS.GetWindowLong (handle, OS.GWL_WNDPROC); | |
OS.SetWindowLong (handle, OS.GWL_WNDPROC, TreeProc); | |
for (int i=0; i<items.length; i++) { | |
TreeItem item = items [i]; | |
if (item != null && item.handle != hNewItem) { | |
tvItem.hItem = item.handle; | |
OS.SendMessage (handle, OS.TVM_SETITEM, 0, tvItem); | |
} | |
} | |
tvItem.hItem = hNewItem; | |
tvItem.state = OS.TVIS_SELECTED; | |
OS.SendMessage (handle, OS.TVM_SETITEM, 0, tvItem); | |
OS.SetWindowLong (handle, OS.GWL_WNDPROC, oldProc); | |
if ((wParam & OS.MK_SHIFT) != 0) { | |
RECT rect1 = new RECT (); | |
if (hAnchor == 0) hAnchor = hNewItem; | |
rect1.left = hAnchor; | |
if (OS.SendMessage (handle, OS.TVM_GETITEMRECT, 1, rect1) != 0) { | |
RECT rect2 = rect2 = new RECT (); | |
rect2.left = hNewItem; | |
OS.SendMessage (handle, OS.TVM_GETITEMRECT, 1, rect2); | |
int flags = rect1.top < rect2.top ? OS.TVGN_NEXTVISIBLE : OS.TVGN_PREVIOUSVISIBLE; | |
tvItem.state = OS.TVIS_SELECTED; | |
int hItem = tvItem.hItem = hAnchor; | |
OS.SendMessage (handle, OS.TVM_SETITEM, 0, tvItem); | |
while (hItem != hNewItem) { | |
tvItem.hItem = hItem; | |
OS.SendMessage (handle, OS.TVM_SETITEM, 0, tvItem); | |
hItem = OS.SendMessage (handle, OS.TVM_GETNEXTITEM, flags, hItem); | |
} | |
} | |
} | |
} | |
} | |
} | |
if ((wParam & OS.MK_SHIFT) == 0) hAnchor = hNewItem; | |
/* Issue notification */ | |
tvItem.hItem = hNewItem; | |
tvItem.mask = OS.TVIF_STATE | OS.TVIF_PARAM; | |
OS.SendMessage (handle, OS.TVM_GETITEM, 0, tvItem); | |
Event event = new Event (); | |
if ((tvItem.state & OS.TVIS_SELECTED) != 0) { | |
event.item = items [tvItem.lParam]; | |
} | |
postEvent (SWT.Selection, event); | |
/* | |
* Feature in Windows. Inside WM_LBUTTONDOWN and WM_RBUTTONDOWN, | |
* the widget starts a modal loop to determine if the user wants | |
* to begin a drag/drop operation or marque select. Unfortunately, | |
* this modal loop eats the corresponding mouse up. The fix is to | |
* detect the cases when the modal loop has eaten the mouse up and | |
* issue a fake mouse up. | |
*/ | |
if (dragStarted) { | |
sendEvent (SWT.DragDetect); | |
// widget could be disposed at this point | |
} else { | |
sendMouseEvent (SWT.MouseUp, 1, OS.WM_LBUTTONUP, wParam, lParam); | |
} | |
dragStarted = false; | |
return new LRESULT (code); | |
} | |
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. | |
*/ | |
sendMouseEvent (SWT.MouseDown, 3, OS.WM_RBUTTONDOWN, wParam, lParam); | |
/* | |
* This code is intentionally commented. | |
*/ | |
// if (OS.GetCapture () != handle) OS.SetCapture (handle); | |
setFocus (); | |
/* | |
* Feature in Windows. When the user selects a tree item | |
* with the right mouse button, the item remains selected | |
* only as long as the user does not release or move the | |
* mouse. As soon as this happens, the selection snaps | |
* back to the previous selection. This behavior can be | |
* observed in the Explorer but is not instantly apparent | |
* because the Explorer explicity sets the selection when | |
* the user chooses a menu item. If the user cancels the | |
* menu, the selection snaps back. The fix is to avoid | |
* calling the window proc and do the selection ourselves. | |
* This behavior is consistent with the table. | |
*/ | |
TVHITTESTINFO lpht = new TVHITTESTINFO (); | |
lpht.x = (short) (lParam & 0xFFFF); | |
lpht.y = (short) (lParam >> 16); | |
OS.SendMessage (handle, OS.TVM_HITTEST, 0, lpht); | |
if (lpht.hItem != 0 && (lpht.flags & (OS.TVHT_ONITEMICON | OS.TVHT_ONITEMLABEL)) != 0) { | |
if ((wParam & (OS.MK_CONTROL | OS.MK_SHIFT)) == 0) { | |
TVITEM tvItem = new TVITEM (); | |
tvItem.hItem = lpht.hItem; | |
OS.SendMessage (handle, OS.TVM_GETITEM, 0, tvItem); | |
if ((tvItem.state & OS.TVIS_SELECTED) == 0) { | |
OS.SendMessage (handle, OS.TVM_SELECTITEM, OS.TVGN_CARET, lpht.hItem); | |
} | |
} | |
} | |
return LRESULT.ZERO; | |
} | |
LRESULT WM_SETFOCUS (int wParam, int lParam) { | |
LRESULT result = super.WM_SETFOCUS (wParam, lParam); | |
if (result != null) return result; | |
if ((style & SWT.SINGLE) != 0) return result; | |
/* | |
* 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 all the | |
* visible items when focus is gained or lost. | |
*/ | |
OS.InvalidateRect (handle, null, false); | |
return result; | |
} | |
LRESULT WM_SYSCOLORCHANGE (int wParam, int lParam) { | |
LRESULT result = super.WM_SYSCOLORCHANGE (wParam, lParam); | |
if (result != null) return result; | |
if (imageList != null && background == -1) { | |
imageList.setBackground (defaultBackground ()); | |
int hImageList = imageList.getHandle (); | |
OS.SendMessage (handle, OS.TVM_SETIMAGELIST, OS.TVSIL_NORMAL, hImageList); | |
} | |
if ((style & SWT.CHECK) != 0) setCheckboxImageList (); | |
return result; | |
} | |
LRESULT wmNotifyChild (int wParam, int lParam) { | |
NMHDR hdr = new NMHDR (); | |
OS.MoveMemory (hdr, lParam, NMHDR.sizeof); | |
int code = hdr.code; | |
switch (code) { | |
case OS.NM_DBLCLK: | |
int pos = OS.GetMessagePos (); | |
TVHITTESTINFO lpht = new TVHITTESTINFO (); | |
POINT pt = new POINT (); | |
pt.x = (short) (pos & 0xFFFF); | |
pt.y = (short) (pos >> 16); | |
OS.ScreenToClient (handle, pt); | |
lpht.x = pt.x; lpht.y = pt.y; | |
OS.SendMessage (handle, OS.TVM_HITTEST, 0, lpht); | |
if ((lpht.flags & OS.TVHT_ONITEM) == 0) break; | |
// fall through | |
case OS.NM_RETURN: | |
case OS.TVN_SELCHANGED: | |
if (!ignoreSelect) { | |
TVITEM tvItem = null; | |
if (code == OS.TVN_SELCHANGED) { | |
tvItem = new TVITEM (); | |
int offset = NMHDR.sizeof + 4 + TVITEM.sizeof; | |
OS.MoveMemory (tvItem, lParam + offset, TVITEM.sizeof); | |
} else { | |
int hItem = OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_CARET, 0); | |
if (hItem == 0) { | |
tvItem = new TVITEM (); | |
tvItem.hItem = hItem; | |
tvItem.mask = OS.TVIF_PARAM | OS.TVIF_STATE; | |
OS.SendMessage (handle, OS.TVM_GETITEM, 0, tvItem); | |
} | |
} | |
TreeItem item = null; | |
if (tvItem != null) { | |
/* | |
* This code is intentionally commented. | |
*/ | |
// hAnchor = tvItem.hItem; | |
if ((tvItem.state & OS.TVIS_SELECTED) != 0) { | |
item = items [tvItem.lParam]; | |
} | |
} | |
Event event = new Event (); | |
event.item = item; | |
if (code == OS.TVN_SELCHANGED) { | |
postEvent (SWT.Selection, event); | |
} else { | |
postEvent (SWT.DefaultSelection, event); | |
} | |
} | |
if (code == OS.NM_DBLCLK && hooks (SWT.DefaultSelection)) { | |
return LRESULT.ONE; | |
} | |
break; | |
case OS.TVN_SELCHANGING: | |
if (!ignoreSelect && !ignoreDeselect) { | |
hAnchor = 0; | |
if ((style & SWT.MULTI) != 0) deselectAll (); | |
} | |
break; | |
case OS.TVN_ITEMEXPANDING: | |
if (!ignoreExpand) { | |
TVITEM tvItem = new TVITEM (); | |
int offset = NMHDR.sizeof + 4 + TVITEM.sizeof; | |
OS.MoveMemory (tvItem, lParam + offset, TVITEM.sizeof); | |
int [] action = new int [1]; | |
OS.MoveMemory (action, lParam + NMHDR.sizeof, 4); | |
Event event = new Event (); | |
event.item = items [tvItem.lParam]; | |
/* | |
* It is possible (but unlikely), that application | |
* code could have disposed the widget in the expand | |
* or collapse event. If this happens, end the | |
* processing of the Windows message by returning | |
* zero as the result of the window proc. | |
*/ | |
if (action [0] == OS.TVE_EXPAND) { | |
sendEvent (SWT.Expand, event); | |
if (isDisposed ()) return LRESULT.ZERO; | |
} | |
if (action [0] == OS.TVE_COLLAPSE) { | |
sendEvent (SWT.Collapse, event); | |
if (isDisposed ()) return LRESULT.ZERO; | |
} | |
} | |
break; | |
case OS.TVN_BEGINDRAG: | |
case OS.TVN_BEGINRDRAG: | |
TVITEM tvItem = new TVITEM (); | |
int offset = NMHDR.sizeof + 4 + TVITEM.sizeof; | |
OS.MoveMemory (tvItem, lParam + offset, TVITEM.sizeof); | |
if (tvItem.hItem != 0 && (tvItem.state & OS.TVIS_SELECTED) == 0) { | |
ignoreSelect = ignoreDeselect = true; | |
OS.SendMessage (handle, OS.TVM_SELECTITEM, OS.TVGN_CARET, tvItem.hItem); | |
ignoreSelect = ignoreDeselect = false; | |
} | |
dragStarted = true; | |
break; | |
} | |
return super.wmNotifyChild (wParam, lParam); | |
} | |
LRESULT wmScroll (int msg, int wParam, int lParam) { | |
int code = callWindowProc (msg, wParam, lParam); | |
if (code == 0) return LRESULT.ZERO; | |
return new LRESULT (code); | |
} | |
} |