* Feature in Windows.  When all of the items are deleted during * TVN_ITEMEXPANDING, Windows does not send TVN_ITEMEXPANDED, * even though the tree was expanded.  The fix is to detect this * case and runn the TVN_ITEMEXPANDED code.
* 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.
LRESULT wmNotifyChild (int wParam, int lParam) {	NMHDR hdr = new NMHDR ();	OS.MoveMemory (hdr, lParam, NMHDR.sizeof);	switch (hdr.code) {		case OS.TVN_GETDISPINFOA:		case OS.TVN_GETDISPINFOW: {			NMTVDISPINFO lptvdi = new NMTVDISPINFO ();			OS.MoveMemory (lptvdi, lParam, NMTVDISPINFO.sizeof);			if ((style & SWT.VIRTUAL) != 0) {				/*				* Feature in Windows.  When a new tree item is inserted				* using TVM_INSERTITEM, a TVN_GETDISPINFO is sent before				* TVM_INSERTITEM returns and before the item is added to				* the items array.  The fix is to check for null.				* 				* NOTE: This only happens on XP with the version 6.00 of				* COMCTL32.DLL.				*/				boolean checkVisible = true;				if (items != null && lptvdi.lParam != -1) {					if (items [lptvdi.lParam] != null && items [lptvdi.lParam].cached) {						checkVisible = false;					}				}				if (checkVisible) {					if (drawCount != 0 || !OS.IsWindowVisible (handle)) break;					RECT itemRect = new RECT ();					itemRect.left = lptvdi.hItem;					if (OS.SendMessage (handle, OS.TVM_GETITEMRECT, 0, itemRect) == 0) {						break;					}					RECT rect = new RECT ();					OS.GetClientRect (handle, rect);					if (!OS.IntersectRect (rect, rect, itemRect)) break;				}			}			if (items == null) break;			/*			* Bug in Windows.  If the lParam field of TVITEM			* is changed during custom draw using TVM_SETITEM,			* the lItemlParam field of the NMTVCUSTOMDRAW struct			* is not updated until the next custom draw.  The			* fix is to query the field from the item instead			* of using the struct.			*/			int id = lptvdi.lParam;			if ((style & SWT.VIRTUAL) != 0) {				if (id == -1) {					TVITEM tvItem = new TVITEM ();					tvItem.mask = OS.TVIF_HANDLE | OS.TVIF_PARAM;					tvItem.hItem = lptvdi.hItem;					OS.SendMessage (handle, OS.TVM_GETITEM, 0, tvItem);					id = tvItem.lParam;				}			}			TreeItem item = _getItem (lptvdi.hItem, id);			/*			* Feature in Windows.  When a new tree item is inserted			* using TVM_INSERTITEM, a TVN_GETDISPINFO is sent before			* TVM_INSERTITEM returns and before the item is added to			* the items array.  The fix is to check for null.			* 			* NOTE: This only happens on XP with the version 6.00 of			* COMCTL32.DLL.			* 			* Feature in Windows.  When TVM_DELETEITEM is called with			* TVI_ROOT to remove all items from a tree, under certain			* circumstances, the tree sends TVN_GETDISPINFO for items			* that are about to be disposed.  The fix is to check for			* disposed items.			*/			if (item == null) break;			if (item.isDisposed ()) break;			if (!item.cached) {				if ((style & SWT.VIRTUAL) != 0) {					if (!checkData (item, false)) break;				}				if (painted) item.cached = true;			}			int index = 0;			if (hwndHeader != 0) {				index = OS.SendMessage (hwndHeader, OS.HDM_ORDERTOINDEX, 0, 0);			}			if ((lptvdi.mask & OS.TVIF_TEXT) != 0) {				String string = null;				if (index == 0) {					string = item.text;				} else {					String [] strings  = item.strings;					if (strings != null) string = strings [index];				}				if (string != null) {					TCHAR buffer = new TCHAR (getCodePage (), string, false);					int byteCount = Math.min (buffer.length (), lptvdi.cchTextMax - 1) * TCHAR.sizeof;					OS.MoveMemory (lptvdi.pszText, buffer, byteCount);					OS.MoveMemory (lptvdi.pszText + byteCount, new byte [TCHAR.sizeof], TCHAR.sizeof);					lptvdi.cchTextMax = Math.min (lptvdi.cchTextMax, string.length () + 1);				}			}			if ((lptvdi.mask & (OS.TVIF_IMAGE | OS.TVIF_SELECTEDIMAGE)) != 0) {				Image image = null;				if (index == 0) {					image = item.image;				} else {					Image [] images  = item.images;					if (images != null) image = images [index];				}				lptvdi.iImage = lptvdi.iSelectedImage = OS.I_IMAGENONE;				if (image != null) {					lptvdi.iImage = lptvdi.iSelectedImage = imageIndex (image, index);				}			}			OS.MoveMemory (lParam, lptvdi, NMTVDISPINFO.sizeof);			break;		}		case OS.NM_CUSTOMDRAW: {			if (hdr.hwndFrom == hwndHeader) break;			if (hooks (SWT.MeasureItem)) {				if (hwndHeader == 0) createParent ();			}			if (!customDraw && findImageControl () == null) {				if (OS.COMCTL32_MAJOR >= 6 && OS.IsAppThemed ()) {					if (sortColumn == null || sortDirection == SWT.NONE) {						break;					}				}			}			NMTVCUSTOMDRAW nmcd = new NMTVCUSTOMDRAW ();			OS.MoveMemory (nmcd, lParam, NMTVCUSTOMDRAW.sizeof);					switch (nmcd.dwDrawStage) {				case OS.CDDS_PREPAINT: return CDDS_PREPAINT (wParam, lParam);				case OS.CDDS_ITEMPREPAINT: return CDDS_ITEMPREPAINT (wParam, lParam);				case OS.CDDS_ITEMPOSTPAINT: return CDDS_ITEMPOSTPAINT (wParam, lParam);				case OS.CDDS_POSTPAINT: return CDDS_POSTPAINT (wParam, lParam);			}			break;		}		case OS.NM_DBLCLK: {			if (hooks (SWT.DefaultSelection)) return LRESULT.ONE;			break;		}		case OS.TVN_SELCHANGEDA:		case OS.TVN_SELCHANGEDW: {			if ((style & SWT.MULTI) != 0) {				if (lockSelection) {					/* Restore the old selection state of both items */					if (oldSelected) {						TVITEM tvItem = new TVITEM ();						int offset = NMHDR.sizeof + 4;						OS.MoveMemory (tvItem, lParam + offset, TVITEM.sizeof);						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) {						TVITEM tvItem = new TVITEM ();						int offset = NMHDR.sizeof + 4 + TVITEM.sizeof;						OS.MoveMemory (tvItem, lParam + offset, TVITEM.sizeof);						tvItem.mask = OS.TVIF_STATE;						tvItem.stateMask = OS.TVIS_SELECTED;						tvItem.state = 0;						OS.SendMessage (handle, OS.TVM_SETITEM, 0, tvItem);					}				}			}			if (!ignoreSelect) {				TVITEM tvItem = new TVITEM ();				int offset = NMHDR.sizeof + 4 + TVITEM.sizeof;				OS.MoveMemory (tvItem, lParam + offset, TVITEM.sizeof);				hAnchor = tvItem.hItem;				Event event = new Event ();				event.item = _getItem (tvItem.hItem, tvItem.lParam);				postEvent (SWT.Selection, event);			}			updateScrollBar ();			break;		}		case OS.TVN_SELCHANGINGA:		case OS.TVN_SELCHANGINGW: {			if ((style & SWT.MULTI) != 0) {				if (lockSelection) {					/* Save the old selection state for both items */					TVITEM tvItem = new TVITEM ();					int offset1 = NMHDR.sizeof + 4;					OS.MoveMemory (tvItem, lParam + offset1, TVITEM.sizeof);					oldSelected = (tvItem.state & OS.TVIS_SELECTED) != 0;					int offset2 = NMHDR.sizeof + 4 + TVITEM.sizeof;					OS.MoveMemory (tvItem, lParam + offset2, TVITEM.sizeof);					newSelected = (tvItem.state & OS.TVIS_SELECTED) != 0;				}			}			if (!ignoreSelect && !ignoreDeselect) {				hAnchor = 0;				if ((style & SWT.MULTI) != 0) deselectAll ();			}			break;		}		case OS.TVN_ITEMEXPANDINGA:		case OS.TVN_ITEMEXPANDINGW: {			boolean runExpanded = false;			if ((style & SWT.VIRTUAL) != 0) style &= ~SWT.DOUBLE_BUFFERED;			if (findImageControl () != null && drawCount == 0) {				OS.DefWindowProc (handle, OS.WM_SETREDRAW, 0, 0);			}			/*			* Bug in Windows.  When TVM_SETINSERTMARK is used to set			* an insert mark for a tree and an item is expanded or			* collapsed near the insert mark, the tree does not redraw			* the insert mark properly.  The fix is to hide and show			* the insert mark whenever an item is expanded or collapsed.			*/			if (hInsert != 0) {				OS.SendMessage (handle, OS.TVM_SETINSERTMARK, 0, 0);			}			if (!ignoreExpand) {				TVITEM tvItem = new TVITEM ();				int offset = NMHDR.sizeof + 4 + TVITEM.sizeof;				OS.MoveMemory (tvItem, lParam + offset, TVITEM.sizeof);				/*				* Feature in Windows.  In some cases, TVM_ITEMEXPANDING				* is sent from within TVM_DELETEITEM for the tree item				* being destroyed.  By the time the message is sent,				* the item has already been removed from the list of				* items.  The fix is to check for null. 				*/				if (items == null) break;				TreeItem item = _getItem (tvItem.hItem, tvItem.lParam);				if (item == null) break;				Event event = new Event ();				event.item = item;				int [] action = new int [1];				OS.MoveMemory (action, lParam + NMHDR.sizeof, 4);				switch (action [0]) {					case OS.TVE_EXPAND:						/*						* Bug in Windows.  When the numeric keypad asterisk						* key is used to expand every item in the tree, Windows						* sends TVN_ITEMEXPANDING to items in the tree that						* have already been expanded.  The fix is to detect						* that the item is already expanded and ignore the						* notification.						*/						if ((tvItem.state & OS.TVIS_EXPANDED) == 0) {							sendEvent (SWT.Expand, event);							if (isDisposed ()) return LRESULT.ZERO;						}						break;					case OS.TVE_COLLAPSE:						sendEvent (SWT.Collapse, event);						if (isDisposed ()) return LRESULT.ZERO;						break;				}				/*				* Feature in Windows.  When all of the items are deleted during				* TVN_ITEMEXPANDING, Windows does not send TVN_ITEMEXPANDED,				* even though the tree was expanded.  The fix is to detect this				* case and runn the TVN_ITEMEXPANDED code.				*/				int hFirstItem = OS.SendMessage (handle, OS.TVM_GETNEXTITEM, OS.TVGN_CHILD, tvItem.hItem);				runExpanded = hFirstItem == 0;			}			if (!runExpanded) break;			//FALL THROUGH		}		case OS.TVN_ITEMEXPANDEDA:		case OS.TVN_ITEMEXPANDEDW: {			if ((style & SWT.VIRTUAL) != 0) style |= SWT.DOUBLE_BUFFERED;			if (findImageControl () != null && drawCount == 0) {				OS.DefWindowProc (handle, OS.WM_SETREDRAW, 1, 0);				OS.InvalidateRect (handle, null, true);			}			/*			* Bug in Windows.  When TVM_SETINSERTMARK is used to set			* an insert mark for a tree and an item is expanded or			* collapsed near the insert mark, the tree does not redraw			* the insert mark properly.  The fix is to hide and show			* the insert mark whenever an item is expanded or collapsed.			*/			if (hInsert != 0) {				OS.SendMessage (handle, OS.TVM_SETINSERTMARK, insertAfter ? 1 : 0, hInsert);			}			updateScrollBar ();			break;		}		case OS.TVN_BEGINDRAGA:		case OS.TVN_BEGINDRAGW:		case OS.TVN_BEGINRDRAGA:		case OS.TVN_BEGINRDRAGW: {			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;		}		case OS.NM_RECOGNIZEGESTURE: {			/* 			* Feature in Pocket PC.  The tree and table controls detect the tap			* and hold gesture by default. They send a GN_CONTEXTMENU message to show			* the popup menu.  This default behaviour is unwanted on Pocket PC 2002			* when no menu has been set, as it still draws a red circle.  The fix			* is to disable this default behaviour when no menu is set by returning			* TRUE when receiving the Pocket PC 2002 specific NM_RECOGNIZEGESTURE			* message.			*/			if (OS.IsPPC) {				boolean hasMenu = menu != null && !menu.isDisposed ();				if (!hasMenu && !hooks (SWT.MenuDetect)) return LRESULT.ONE;			}			break;		}		case OS.GN_CONTEXTMENU: {			if (OS.IsPPC) {				boolean hasMenu = menu != null && !menu.isDisposed ();				if (hasMenu || hooks (SWT.MenuDetect)) {					NMRGINFO nmrg = new NMRGINFO ();					OS.MoveMemory (nmrg, lParam, NMRGINFO.sizeof);					showMenu (nmrg.x, nmrg.y);					gestureCompleted = true;					return LRESULT.ONE;				}			}			break;		}	}	return super.wmNotifyChild (wParam, lParam);}