Merged main Wine changes into Corel's treeview control rewritten by
Serge Ivanov and Andrew Lewycky.  Fixed item focus behavior to match
Windows.  Fixed item selection when un/expanding items.  Implemented
WM_SETREDRAW.  Added Corel's COMCTL32_CreateToolTip() helper function
to commctrl.c.

diff --git a/dlls/comctl32/comctl32.h b/dlls/comctl32/comctl32.h
index 93bdb0b..9cb48e0 100644
--- a/dlls/comctl32/comctl32.h
+++ b/dlls/comctl32/comctl32.h
@@ -7,6 +7,9 @@
  *
  */
 
+#ifndef __WINE_COMCTL32_H
+#define __WINE_COMCTL32_H
+
 extern HMODULE COMCTL32_hModule;
 
 /* Property sheet / Wizard */
@@ -62,3 +65,7 @@
 
 #define IDT_CHECK        401
 
+/* Internal function */
+HWND COMCTL32_CreateToolTip (HWND);
+
+#endif  /* __WINE_COMCTL32_H */
diff --git a/dlls/comctl32/commctrl.c b/dlls/comctl32/commctrl.c
index 52a183d..1c0fc2e 100644
--- a/dlls/comctl32/commctrl.c
+++ b/dlls/comctl32/commctrl.c
@@ -1150,3 +1150,45 @@
 {
    COMCTL32_uiLang = uiLang;
 }
+
+
+/***********************************************************************
+ * COMCTL32_CreateToolTip [NOT AN API]
+ *
+ * Creates a tooltip for the control specified in hwnd and does all
+ * necessary setup and notifications.
+ *
+ * PARAMS
+ *     hwndOwner [I] Handle to the window that will own the tool tip.
+ *
+ * RETURNS
+ *     Success: Handle of tool tip window.
+ *     Failure: NULL
+ */
+HWND
+COMCTL32_CreateToolTip(HWND hwndOwner)
+{
+    HWND hwndToolTip;
+
+    hwndToolTip = CreateWindowExA(0, TOOLTIPS_CLASSA, NULL, 0,
+				  CW_USEDEFAULT, CW_USEDEFAULT,
+				  CW_USEDEFAULT, CW_USEDEFAULT, hwndOwner,
+				  0, 0, 0);
+
+    /* Send NM_TOOLTIPSCREATED notification */
+    if (hwndToolTip)
+    {
+	NMTOOLTIPSCREATED nmttc;
+
+	nmttc.hdr.hwndFrom = hwndOwner;
+	nmttc.hdr.idFrom = GetWindowLongA(hwndOwner, GWL_ID);
+	nmttc.hdr.code = NM_TOOLTIPSCREATED;
+	nmttc.hwndToolTips = hwndToolTip;
+
+	SendMessageA(GetParent(hwndOwner), WM_NOTIFY,
+		     (WPARAM)GetWindowLongA(hwndOwner, GWL_ID),
+		     (LPARAM)&nmttc);
+    }
+
+    return hwndToolTip;
+}
diff --git a/dlls/comctl32/treeview.c b/dlls/comctl32/treeview.c
index 17b29c5..ea451fd 100644
--- a/dlls/comctl32/treeview.c
+++ b/dlls/comctl32/treeview.c
@@ -4,70 +4,40 @@
  * Copyright 1998,1999 Alex Priem <alexp@sci.kun.nl>
  * Copyright 1999 Sylvain St-Germain
  *
+ * Note that TREEVIEW_INFO * and HTREEITEM are the same thing.
+ *
+ * Note2: All items always! have valid (allocated) pszText field.
+ *      If item's text == LPSTR_TEXTCALLBACKA we allocate buffer
+ *      of size TEXT_CALLBACK_SIZE in DoSetItem.
+ *      We use callbackMask to keep track of fields to be updated.
  *
  * TODO:
- *   Using DPA to store the item ptr would be good.
- *   Node label edition is implemented but something happened in wine in the
- *   two last weeks of march 99 that broke it.
- *   refreshtreeview: 	
-		-small array containing info about positions.
- 		-better implementation of RefreshItem:
-              1) draw lines between parents
-              2) draw items
-			  3) draw lines from parent<->items.
-		-implement partial drawing?
- *  -drag&drop: TVM_CREATEDRAGIMAGE should create drag bitmap.
- *  -scrollbars: horizontal scrollbar doesn't work.
- *  -Unicode messages
- *  -check custom draw
- *  -I_CHILDRENCALLBACK
- *   FIXME: check fontsize. (uRealItemHeight)
- *          test focusItem  (redraw in different color)
-			uHotItem
-			Edit: needs timer
-				  better implementation.
- *   WM_HSCROLL is broken.
- *   use separate routine to get item text/image.
- *  
- *   Separate drawing/calculation.
+ *   missing notifications: NM_SETCURSOR, TVN_GETINFOTIP, TVN_KEYDOWN,
+ *      TVN_SETDISPINFO, TVN_SINGLEEXPAND
  *
- * FIXMEs  (for personal use)
-    Expand:		-ctlmacro expands twice ->toggle.
-   -DblClick:	ctlmacro.exe's NM_DBLCLK seems to go wrong (returns FALSE).
-   -treehelper: stack corruption makes big window. 
-	     
+ *   missing styles: TVS_FULLROWSELECT, TVS_INFOTIP, TVS_NOSCROLL,
+ *      TVS_RTLREADING, TVS_TRACKSELECT
+ *
+ *   missing item styles: TVIS_CUT, TVIS_EXPANDPARTIAL
+ *
+ *   Make the insertion mark look right.
+ *   Scroll (instead of repaint) as much as possible.
  */
 
-
+#include <assert.h>
 #include <string.h>
+#include <limits.h>
 #include "winbase.h"
 #include "wingdi.h"
 #include "commctrl.h"
 #include "comctl32.h"
 #include "debugtools.h"
 
-DEFAULT_DEBUG_CHANNEL(treeview);
-
-/* ffs should be in <string.h>. */
-
-/* Defines, since they do not need to return previous state, and nr
- * has no side effects in this file.
- */
-#define tv_test_bit(nr,bf)	(((LPBYTE)bf)[nr>>3]&(1<<(nr&7)))
-#define tv_set_bit(nr,bf)	((LPBYTE)bf)[nr>>3]|=(1<<(nr&7))
-#define tv_clear_bit(nr,bf)	((LPBYTE)bf)[nr>>3]&=~(1<<(nr&7))
-
-#define MINIMUM_INDENT 10
-#define TV_REFRESH_DELAY 100     /* 100 ms delay between two refreshes */
-#define TV_DEFAULTITEMHEIGHT 16
-#define TVITEM_ALLOC	16	/* default nr of items to allocate at first try */
-
-
 /* internal structures */
 
-typedef struct {
-  UINT      mask;
-  HTREEITEM hItem;
+typedef struct _TREEITEM    /* HTREEITEM is a _TREEINFO *. */
+{
+  UINT      callbackMask;
   UINT      state;
   UINT      stateMask;
   LPSTR     pszText;
@@ -76,69 +46,80 @@
   int       iSelectedImage;
   int       cChildren;
   LPARAM    lParam;
-  int       iIntegral;
+  int       iIntegral;      /* item height multiplier (1 is normal) */
   int       iLevel;         /* indentation level:0=root level */
-  COLORREF  clrText;
   HTREEITEM parent;         /* handle to parent or 0 if at root*/
   HTREEITEM firstChild;     /* handle to first child or 0 if no child*/
-  HTREEITEM sibling;        /* handle to next item in list, 0 if last */
-  HTREEITEM upsibling;      /* handle to previous item in list, 0 if first */
-  int       visible;
+  HTREEITEM lastChild;
+  HTREEITEM prevSibling;    /* handle to prev item in list, 0 if first */
+  HTREEITEM nextSibling;    /* handle to next item in list, 0 if last */
   RECT      rect;
-  RECT      text;
-  RECT      expandBox;      /* expand box (+/-) coordinate */
-  RECT		bitmap;
-  RECT      statebitmap;
+  LONG      linesOffset;
+  LONG      stateOffset;
+  LONG      imageOffset;
+  LONG      textOffset;
+  LONG      textWidth;      /* horizontal text extent for pszText */
+  LONG      visibleOrder;   /* visible ordering, 0 is first visible item */
 } TREEVIEW_ITEM;
 
 
-typedef struct
+typedef struct tagTREEVIEW_INFO
 {
-  UINT          uInternalStatus;    
-  UINT          bAutoSize;      /* merge with uInternalStatus */
+  HWND          hwnd;
+  DWORD         dwStyle;
+  HTREEITEM     root;
+  UINT          uInternalStatus;
   INT           Timer;
   UINT          uNumItems;      /* number of valid TREEVIEW_ITEMs */
-  UINT          uNumPtrsAlloced; 
-  HTREEITEM     uMaxHandle;     /* needed for delete_item */
-  HTREEITEM     TopRootItem;    /* handle to first item in treeview */
   INT           cdmode;         /* last custom draw setting */
   UINT          uScrollTime;	/* max. time for scrolling in milliseconds*/
-  UINT          uItemHeight;    /* item height, -1 for default item height */
-  UINT          uRealItemHeight;/* current item height in pixels */
-  UINT          uVisibleHeight; /* visible height of treeview in pixels */
-  UINT          uTotalHeight;   /* total height of treeview in pixels */
-  UINT          uVisibleWidth;      
-  UINT          uTotalWidth;  
+  BOOL		bRedraw;	/* if FALSE we validate but don't redraw in TREEVIEW_Paint() */
+
+  UINT          uItemHeight;    /* item height */
+  BOOL          bHeightSet;
+
+  LONG          clientWidth;    /* width of control window */
+  LONG          clientHeight;   /* height of control window */
+
+  LONG          treeWidth;      /* width of visible tree items */
+  LONG          treeHeight;     /* height of visible tree items */
+
   UINT          uIndent;        /* indentation in pixels */
   HTREEITEM     selectedItem;   /* handle to selected item or 0 if none */
-  HTREEITEM     focusItem;      /* handle to item that has focus, 0 if none */
   HTREEITEM     hotItem;        /* handle currently under cursor, 0 if none */
-  HTREEITEM     editItem;       /* handle to item currently editted, 0 if none */
+  HTREEITEM	focusedItem;    /* item that was under the cursor when WM_LBUTTONDOWN was received */
+
   HTREEITEM     firstVisible;   /* handle to first visible item */
+  LONG          maxVisibleOrder;
   HTREEITEM     dropItem;       /* handle to item selected by drag cursor */
   HTREEITEM     insertMarkItem; /* item after which insertion mark is placed */
-  BOOL			insertBeforeorAfter; /* flag used by TVM_SETINSERTMARK */
+  BOOL          insertBeforeorAfter; /* flag used by TVM_SETINSERTMARK */
   HIMAGELIST    dragList;       /* Bitmap of dragged item */
-  INT           cx,cy;          /* current x/y place in list */
-  COLORREF      clrBk;    
+  LONG          scrollX;
+  COLORREF      clrBk;
   COLORREF      clrText;
   COLORREF      clrLine;
-  COLORREF  	clrInsertMark;
+  COLORREF      clrInsertMark;
   HFONT         hFont;
   HFONT         hBoldFont;
   HWND          hwndToolTip;
+
   HWND          hwndEdit;
-  WNDPROC       wpEditOrig;     /* needed for subclassing edit control */
-  HIMAGELIST    himlNormal;  
+  WNDPROC       wpEditOrig;     /* orig window proc for subclassing edit */
+  BOOL          bIgnoreEditKillFocus;
+  BOOL          bLabelChanged;
+
+  HIMAGELIST    himlNormal;
+  int           normalImageHeight;
+  int           normalImageWidth;
   HIMAGELIST    himlState;
-  LPTVSORTCB    pCallBackSort; /* ptr to TVSORTCB struct for callback sorting */
-  TREEVIEW_ITEM *items;        /* itemlist */
-  INT           *freeList;     /* bitmap indicating which elements are valid */
-                               /* 1=valid, 0=free;   */
-                               /* size of list= uNumPtrsAlloced/32 */
+  int           stateImageHeight;
+  int           stateImageWidth;
+  HDPA          items;
 } TREEVIEW_INFO;
 
 
+
 /* bitflags for infoPtr->uInternalStatus */
 
 #define TV_HSCROLL 	0x01    /* treeview too large to fit in window */
@@ -150,136 +131,178 @@
 
 /* bitflags for infoPtr->timer */
 
-#define TV_REFRESH_TIMER 1	
 #define TV_EDIT_TIMER    2
-#define TV_REFRESH_TIMER_SET 1  
-#define TV_EDIT_TIMER_SET 2  
-
-#define TREEVIEW_GetInfoPtr(hwnd) \
-  ((TREEVIEW_INFO *) GetWindowLongA( hwnd, 0))
-
-#define FOCUS_BORDER 3
-
-static BOOL
-TREEVIEW_SendSimpleNotify (HWND hwnd, UINT code);
-static BOOL
-TREEVIEW_SendTreeviewNotify (HWND hwnd, UINT code, UINT action, 
-			HTREEITEM oldItem, HTREEITEM newItem);
-static BOOL
-TREEVIEW_SendTreeviewDnDNotify (HWND hwnd, UINT code, HTREEITEM dragItem, 
-			POINT pt);
-static BOOL
-TREEVIEW_SendDispInfoNotify (HWND hwnd, TREEVIEW_ITEM *wineItem, 
-			UINT code, UINT what);
-static BOOL
-TREEVIEW_SendCustomDrawNotify (HWND hwnd, DWORD dwDrawStage, HDC hdc,
-			RECT rc);
-static BOOL
-TREEVIEW_SendCustomDrawItemNotify (HWND hwnd, HDC hdc,
-            TREEVIEW_ITEM *tvItem, UINT uItemDrawState);
-static LRESULT
-TREEVIEW_DoSelectItem (HWND hwnd, INT action, HTREEITEM newSelect, INT cause);
-static void
-TREEVIEW_Refresh (HWND hwnd, HDC hdc);
-
-static LRESULT CALLBACK
-TREEVIEW_Edit_SubclassProc (HWND hwnd, UINT uMsg, WPARAM wParam, 
-							LPARAM lParam);
-
-static LRESULT
-TREEVIEW_EditLabelA (HWND hwnd, WPARAM wParam, LPARAM lParam);
-
-static LRESULT
-TREEVIEW_EndEditLabelNow (HWND hwnd, WPARAM wParam, LPARAM lParam);
+#define TV_EDIT_TIMER_SET 2
 
 
+VOID TREEVIEW_Register (VOID);
+VOID TREEVIEW_Unregister (VOID);
 
 
-/* helper functions. Work with the assumption that validity of operands 
-   is checked beforehand, and that tree state is valid.  */
+DEFAULT_DEBUG_CHANNEL(treeview);
 
-/* FIXME: MS documentation says `GetNextVisibleItem' returns NULL 
-   if not successfull. Probably only applies to dereferencing infoPtr
-   (i.e. we are offered a valid treeview structure)
-   and not whether there is a next `visible' child. 
-   FIXME: check other failures.
- */
 
-/***************************************************************************
- * This method returns the TREEVIEW_ITEM object given the handle
- */
-static TREEVIEW_ITEM* TREEVIEW_ValidItem(
-  TREEVIEW_INFO *infoPtr,
-  HTREEITEM  handle)
+#define TEXT_CALLBACK_SIZE 260
+
+#define TREEVIEW_LEFT_MARGIN 8
+
+#define MINIMUM_INDENT 19
+
+#define CALLBACK_MASK_ALL (TVIF_TEXT|TVIF_CHILDREN|TVIF_IMAGE|TVIF_SELECTEDIMAGE)
+
+#define STATEIMAGEINDEX(x) (((x) >> 12) & 0x0f)
+#define OVERLAYIMAGEINDEX(x) (((x) >> 8) & 0x0f)
+#define ISVISIBLE(x)         ((x)->visibleOrder >= 0)
+
+
+typedef VOID (*TREEVIEW_ItemEnumFunc)(TREEVIEW_INFO *, TREEVIEW_ITEM *,LPVOID);
+
+
+static VOID TREEVIEW_QueueRefresh(TREEVIEW_INFO *);
+static VOID TREEVIEW_QueueItemRefresh(TREEVIEW_INFO *, TREEVIEW_ITEM *);
+
+static LRESULT TREEVIEW_DoSelectItem(TREEVIEW_INFO *, INT, HTREEITEM, INT);
+static VOID TREEVIEW_SetFirstVisible(TREEVIEW_INFO *, TREEVIEW_ITEM *, BOOL);
+static LRESULT TREEVIEW_EnsureVisible(TREEVIEW_INFO *, HTREEITEM, BOOL);
+static LRESULT TREEVIEW_RButtonUp(TREEVIEW_INFO *, LPPOINT);
+static LRESULT TREEVIEW_EndEditLabelNow(TREEVIEW_INFO *infoPtr, BOOL bCancel);
+static VOID TREEVIEW_UpdateScrollBars(TREEVIEW_INFO *infoPtr);
+static LRESULT TREEVIEW_HScroll(TREEVIEW_INFO *, WPARAM);
+
+
+/* Random Utilities *****************************************************/
+
+#ifdef NDEBUG
+static inline void
+TREEVIEW_VerifyTree(TREEVIEW_INFO *infoPtr)
 {
-  if ((!handle) || (handle>infoPtr->uMaxHandle)) 
-    return NULL;
+    (void)infoPtr;
+}
+#else
+/* The definition is at the end of the file. */
+static void TREEVIEW_VerifyTree(TREEVIEW_INFO *infoPtr);
+#endif
 
-  if (tv_test_bit ((INT)handle, infoPtr->freeList)) 
-    return NULL;
+/* Returns the treeview private data if hwnd is a treeview.
+ * Otherwise returns an undefined value. */
+static TREEVIEW_INFO *
+TREEVIEW_GetInfoPtr(HWND hwnd)
+{
+    return (TREEVIEW_INFO *)GetWindowLongA(hwnd, 0);
+}
 
-  return &infoPtr->items[(INT)handle];
+/* Don't call this. Nothing wants an item index. */
+static inline int
+TREEVIEW_GetItemIndex(TREEVIEW_INFO *infoPtr, HTREEITEM handle)
+{
+    assert(infoPtr != NULL);
+
+    return DPA_GetPtrIndex(infoPtr->items, handle);
 }
 
 /***************************************************************************
- * This method returns the last expanded child item of a tree node
+ * This method checks that handle is an item for this tree.
  */
-static TREEVIEW_ITEM *TREEVIEW_GetLastListItem(
-  TREEVIEW_INFO *infoPtr,
-  TREEVIEW_ITEM *tvItem)
+static BOOL
+TREEVIEW_ValidItem(TREEVIEW_INFO *infoPtr, HTREEITEM handle)
 {
-  TREEVIEW_ITEM *wineItem = tvItem;
+    if (TREEVIEW_GetItemIndex(infoPtr, handle) == -1)
+    {
+	TRACE("invalid item %p\n", handle);
+	return FALSE;
+    }
+    else
+	return TRUE;
+}
 
-  /* 
-   * Get this item last sibling 
-   */
-  while (wineItem->sibling) 
- 	  wineItem=& infoPtr->items [(INT)wineItem->sibling];
+static HFONT
+TREEVIEW_CreateBoldFont(HFONT hOrigFont)
+{
+    LOGFONTA font;
 
-  /* 
-   * If the last sibling has expanded children, restart.
-   */
-  if ( ( wineItem->cChildren > 0 ) && ( wineItem->state & TVIS_EXPANDED) )
-    return TREEVIEW_GetLastListItem(
-             infoPtr, 
-             &(infoPtr->items[(INT)wineItem->firstChild]));
+    GetObjectA(hOrigFont, sizeof(font), &font);
+    font.lfWeight = FW_BOLD;
+    return CreateFontIndirectA(&font);
+}
 
-  return wineItem;
+static inline HFONT
+TREEVIEW_FontForItem(TREEVIEW_INFO *infoPtr, TREEVIEW_ITEM *item)
+{
+    return (item->state & TVIS_BOLD) ? infoPtr->hBoldFont : infoPtr->hFont;
+}
+
+/* for trace/debugging purposes only */
+static const char *
+TREEVIEW_ItemName(TREEVIEW_ITEM *item)
+{
+    if (item == NULL) return "<null item>";
+    if (item->pszText == LPSTR_TEXTCALLBACKA) return "<callback>";
+    if (item->pszText == NULL) return "<null>";
+    return item->pszText;
+}
+
+/* An item is not a child of itself. */
+static BOOL
+TREEVIEW_IsChildOf(TREEVIEW_ITEM *parent, TREEVIEW_ITEM *child)
+{
+    do
+    {
+	child = child->parent;
+	if (child == parent) return TRUE;
+    } while (child != NULL);
+
+    return FALSE;
+}
+
+
+/* Tree Traversal *******************************************************/
+
+/***************************************************************************
+ * This method returns the last expanded sibling or child child item
+ * of a tree node
+ */
+static TREEVIEW_ITEM *
+TREEVIEW_GetLastListItem(TREEVIEW_INFO *infoPtr, TREEVIEW_ITEM *wineItem)
+{
+    if (!wineItem)
+       return NULL;
+
+    while (wineItem->lastChild)
+    {
+       if (wineItem->state & TVIS_EXPANDED)
+          wineItem = wineItem->lastChild;
+       else
+          break;
+    }
+
+    if (wineItem == infoPtr->root)
+        return NULL;
+
+    return wineItem;
 }
 
 /***************************************************************************
- * This method returns the previous physical item in the list not 
+ * This method returns the previous non-hidden item in the list not 
  * considering the tree hierarchy.
  */
-static TREEVIEW_ITEM *TREEVIEW_GetPrevListItem(
-  TREEVIEW_INFO *infoPtr, 
-  TREEVIEW_ITEM *tvItem)
+static TREEVIEW_ITEM *
+TREEVIEW_GetPrevListItem(TREEVIEW_INFO *infoPtr, TREEVIEW_ITEM *tvItem)
 {
-  if (tvItem->upsibling) 
-  {
-    /* 
-     * This item has a upsibling, get the last item.  Since, GetLastListItem
-     * first looks at siblings, we must feed it with the first child.
-     */
-    TREEVIEW_ITEM *upItem = &infoPtr->items[(INT)tvItem->upsibling];
-    
-    if ( ( upItem->cChildren > 0 ) && ( upItem->state & TVIS_EXPANDED) )
-      return TREEVIEW_GetLastListItem( 
-               infoPtr, 
-               &infoPtr->items[(INT)upItem->firstChild]);
-    else
-      return upItem;
-  }
-  else
-  {
-    /*
-     * this item does not have a upsibling, get the parent
-     */
-    if (tvItem->parent) 
-      return &infoPtr->items[(INT)tvItem->parent];
-  }
+    if (tvItem->prevSibling)
+    {
+	/* This item has a prevSibling, get the last item in the sibling's tree. */
+	TREEVIEW_ITEM *upItem = tvItem->prevSibling;
 
-  return NULL;
+	if ((upItem->state & TVIS_EXPANDED) && upItem->lastChild != NULL)
+	    return TREEVIEW_GetLastListItem(infoPtr, upItem->lastChild);
+	else
+	    return upItem;
+    }
+    else
+    {
+	/* this item does not have a prevSibling, get the parent */
+	return (tvItem->parent != infoPtr->root) ? tvItem->parent : NULL;
+    }
 }
 
 
@@ -287,36 +310,38 @@
  * This method returns the next physical item in the treeview not 
  * considering the tree hierarchy.
  */
-static TREEVIEW_ITEM *TREEVIEW_GetNextListItem(
-  TREEVIEW_INFO *infoPtr, 
-  TREEVIEW_ITEM *tvItem)
+static TREEVIEW_ITEM *
+TREEVIEW_GetNextListItem(TREEVIEW_INFO *infoPtr, TREEVIEW_ITEM *tvItem)
 {
-  TREEVIEW_ITEM *wineItem = NULL;
+    assert(tvItem != NULL);
 
-  /* 
-   * If this item has children and is expanded, return the first child
-   */
-  if ((tvItem->firstChild) && (tvItem->state & TVIS_EXPANDED)) 
-		return (& infoPtr->items[(INT)tvItem->firstChild]);
+    /* 
+     * If this item has children and is expanded, return the first child
+     */
+    if ((tvItem->state & TVIS_EXPANDED) && tvItem->firstChild != NULL)
+    {
+	return tvItem->firstChild;
+    }
 
 
-  /*
-   * try to get the sibling
-   */
-  if (tvItem->sibling) 
-		return (& infoPtr->items[(INT)tvItem->sibling]);
+    /*
+     * try to get the sibling
+     */
+    if (tvItem->nextSibling)
+	return tvItem->nextSibling;
 
-  /*
-   * Otherwise, get the parent's sibling.
-   */
-  wineItem=tvItem;
-  while (wineItem->parent) {
-    wineItem=& infoPtr->items [(INT)wineItem->parent];
-  	if (wineItem->sibling) 
-      return (& infoPtr->items [(INT)wineItem->sibling]);
-  } 
+    /*
+     * Otherwise, get the parent's sibling.
+     */
+    while (tvItem->parent)
+    {
+	tvItem = tvItem->parent;
 
-  return NULL;  
+	if (tvItem->nextSibling)
+	    return tvItem->nextSibling;
+    }
+
+    return NULL;
 }
 
 /***************************************************************************
@@ -326,1422 +351,2347 @@
  * Will scroll backward if count is <0.
  *             forward if count is >0.
  */
-static TREEVIEW_ITEM *TREEVIEW_GetListItem(
-  TREEVIEW_INFO *infoPtr, 
-  TREEVIEW_ITEM *tvItem,
-  LONG          count)
+static TREEVIEW_ITEM *
+TREEVIEW_GetListItem(TREEVIEW_INFO *infoPtr, TREEVIEW_ITEM *wineItem,
+		     LONG count)
 {
-  TREEVIEW_ITEM *previousItem = NULL;
-  TREEVIEW_ITEM *wineItem     = tvItem;
-  LONG          iter          = 0;
+    TREEVIEW_ITEM *(*next_item)(TREEVIEW_INFO *, TREEVIEW_ITEM *);
+    TREEVIEW_ITEM *previousItem;
 
-  if      (count > 0)
-  {
-    /* Find count item downward */
-    while ((iter++ < count) && (wineItem != NULL))
-    {
-      /* Keep a pointer to the previous in case we ask for more than we got */
-      previousItem = wineItem; 
-      wineItem     = TREEVIEW_GetNextListItem(infoPtr, wineItem);
-    } 
+    assert(wineItem != NULL);
 
-    if (wineItem == NULL)
-      wineItem = previousItem;
-  }
-  else if (count < 0)
-  {
-    /* Find count item upward */
-    while ((iter-- > count) && (wineItem != NULL))
+    if (count > 0)
     {
-      /* Keep a pointer to the previous in case we ask for more than we got */
-      previousItem = wineItem; 
-      wineItem = TREEVIEW_GetPrevListItem(infoPtr, wineItem);
+	next_item = TREEVIEW_GetNextListItem;
     }
+    else if (count < 0)
+    {
+	count = -count;
+	next_item = TREEVIEW_GetPrevListItem;
+    }
+    else
+	return wineItem;
 
-    if (wineItem == NULL)
-      wineItem = previousItem;
-  }
-  else
-    wineItem = NULL;
+    do
+    {
+	previousItem = wineItem;
+	wineItem = next_item(infoPtr, wineItem);
 
-  return wineItem;
+    } while (--count && wineItem != NULL);
+
+
+    return wineItem ? wineItem : previousItem;
 }
 
- 
-/***************************************************************************
- * This method 
- */
-static void TREEVIEW_RemoveAllChildren(
-  HWND hwnd,
-  TREEVIEW_ITEM *parentItem)
+/* Notifications ************************************************************/
+
+static BOOL
+TREEVIEW_SendSimpleNotify(TREEVIEW_INFO *infoPtr, UINT code)
 {
- TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
- TREEVIEW_ITEM *killItem;
- INT	kill;
- 
- kill=(INT)parentItem->firstChild;
- while (kill) {
- 	tv_set_bit ( kill, infoPtr->freeList);
- 	killItem=& infoPtr->items[kill];
-	if (killItem->pszText!=LPSTR_TEXTCALLBACKA) 
-		COMCTL32_Free (killItem->pszText);
- 	TREEVIEW_SendTreeviewNotify (hwnd, TVN_DELETEITEMA, 0, (HTREEITEM)kill, 0);
-	if (killItem->firstChild) 
-			TREEVIEW_RemoveAllChildren (hwnd, killItem);
-	kill=(INT)killItem->sibling;
- }
+    NMHDR nmhdr;
+    HWND hwnd = infoPtr->hwnd;
 
- if (parentItem->cChildren>0) {
- 	infoPtr->uNumItems -= parentItem->cChildren;
- 	parentItem->firstChild = 0;
- 	parentItem->cChildren  = 0;
- }
+    TRACE("%x\n", code);
+    nmhdr.hwndFrom = hwnd;
+    nmhdr.idFrom = GetWindowLongA(hwnd, GWL_ID);
+    nmhdr.code = code;
 
+    return (BOOL)SendMessageA(GetParent(hwnd), WM_NOTIFY,
+			      (WPARAM)nmhdr.idFrom, (LPARAM)&nmhdr);
 }
 
+static VOID
+TREEVIEW_TVItemFromItem(UINT mask, TVITEMA *tvItem, TREEVIEW_ITEM *item)
+{
+    tvItem->mask = mask;
+    tvItem->hItem = item;
+    tvItem->state = item->state;
+    tvItem->stateMask = 0;
+    tvItem->iImage = item->iImage;
+    tvItem->pszText = item->pszText;
+    tvItem->cchTextMax = item->cchTextMax;
+    tvItem->iImage = item->iImage;
+    tvItem->iSelectedImage = item->iSelectedImage;
+    tvItem->cChildren = item->cChildren;
+    tvItem->lParam = item->lParam;
+}
+
+static BOOL
+TREEVIEW_SendTreeviewNotify(TREEVIEW_INFO *infoPtr, UINT code, UINT action,
+			    UINT mask, HTREEITEM oldItem, HTREEITEM newItem)
+{
+    HWND hwnd = infoPtr->hwnd;
+    NMTREEVIEWA nmhdr;
+
+    TRACE("code:%x action:%x olditem:%p newitem:%p\n",
+	  code, action, oldItem, newItem);
+
+    ZeroMemory(&nmhdr, sizeof(NMTREEVIEWA));
+
+    nmhdr.hdr.hwndFrom = hwnd;
+    nmhdr.hdr.idFrom = GetWindowLongA(hwnd, GWL_ID);
+    nmhdr.hdr.code = code;
+    nmhdr.action = action;
+
+    if (oldItem)
+	TREEVIEW_TVItemFromItem(mask, &nmhdr.itemOld, oldItem);
+
+    if (newItem)
+	TREEVIEW_TVItemFromItem(mask, &nmhdr.itemNew, newItem);
+
+    nmhdr.ptDrag.x = 0;
+    nmhdr.ptDrag.y = 0;
+
+    return (BOOL)SendMessageA(GetParent(hwnd), WM_NOTIFY,
+			      (WPARAM)GetWindowLongA(hwnd, GWL_ID),
+			      (LPARAM)&nmhdr);
+}
+
+static BOOL
+TREEVIEW_SendTreeviewDnDNotify(TREEVIEW_INFO *infoPtr, UINT code,
+			       HTREEITEM dragItem, POINT pt)
+{
+    HWND hwnd = infoPtr->hwnd;
+    NMTREEVIEWA nmhdr;
+
+    TRACE("code:%x dragitem:%p\n", code, dragItem);
+
+    nmhdr.hdr.hwndFrom = hwnd;
+    nmhdr.hdr.idFrom = GetWindowLongA(hwnd, GWL_ID);
+    nmhdr.hdr.code = code;
+    nmhdr.action = 0;
+    nmhdr.itemNew.mask = TVIF_STATE | TVIF_PARAM | TVIF_HANDLE;
+    nmhdr.itemNew.hItem = dragItem;
+    nmhdr.itemNew.state = dragItem->state;
+    nmhdr.itemNew.lParam = dragItem->lParam;
+
+    nmhdr.ptDrag.x = pt.x;
+    nmhdr.ptDrag.y = pt.y;
+
+    return (BOOL)SendMessageA(GetParent(hwnd), WM_NOTIFY,
+			      (WPARAM)GetWindowLongA(hwnd, GWL_ID),
+			      (LPARAM)&nmhdr);
+}
+
+
+static BOOL
+TREEVIEW_SendCustomDrawNotify(TREEVIEW_INFO *infoPtr, DWORD dwDrawStage,
+			      HDC hdc, RECT rc)
+{
+    HWND hwnd = infoPtr->hwnd;
+    NMTVCUSTOMDRAW nmcdhdr;
+    LPNMCUSTOMDRAW nmcd;
+
+    TRACE("drawstage:%lx hdc:%x\n", dwDrawStage, hdc);
+
+    nmcd = &nmcdhdr.nmcd;
+    nmcd->hdr.hwndFrom = hwnd;
+    nmcd->hdr.idFrom = GetWindowLongA(hwnd, GWL_ID);
+    nmcd->hdr.code = NM_CUSTOMDRAW;
+    nmcd->dwDrawStage = dwDrawStage;
+    nmcd->hdc = hdc;
+    nmcd->rc = rc;
+    nmcd->dwItemSpec = 0;
+    nmcd->uItemState = 0;
+    nmcd->lItemlParam = 0;
+    nmcdhdr.clrText = infoPtr->clrText;
+    nmcdhdr.clrTextBk = infoPtr->clrBk;
+    nmcdhdr.iLevel = 0;
+
+    return (BOOL)SendMessageA(GetParent(hwnd), WM_NOTIFY,
+			      (WPARAM)GetWindowLongA(hwnd, GWL_ID),
+			      (LPARAM)&nmcdhdr);
+}
+
+
+
+/* FIXME: need to find out when the flags in uItemState need to be set */
+
+static BOOL
+TREEVIEW_SendCustomDrawItemNotify(TREEVIEW_INFO *infoPtr, HDC hdc,
+				  TREEVIEW_ITEM *wineItem, UINT uItemDrawState)
+{
+    HWND hwnd = infoPtr->hwnd;
+    NMTVCUSTOMDRAW nmcdhdr;
+    LPNMCUSTOMDRAW nmcd;
+    DWORD dwDrawStage, dwItemSpec;
+    UINT uItemState;
+    INT retval;
+
+    dwDrawStage = CDDS_ITEM | uItemDrawState;
+    dwItemSpec = (DWORD)wineItem;
+    uItemState = 0;
+    if (wineItem->state & TVIS_SELECTED)
+	uItemState |= CDIS_SELECTED;
+    if (wineItem == infoPtr->selectedItem)
+	uItemState |= CDIS_FOCUS;
+    if (wineItem == infoPtr->hotItem)
+	uItemState |= CDIS_HOT;
+
+    nmcd = &nmcdhdr.nmcd;
+    nmcd->hdr.hwndFrom = hwnd;
+    nmcd->hdr.idFrom = GetWindowLongA(hwnd, GWL_ID);
+    nmcd->hdr.code = NM_CUSTOMDRAW;
+    nmcd->dwDrawStage = dwDrawStage;
+    nmcd->hdc = hdc;
+    nmcd->rc = wineItem->rect;
+    nmcd->dwItemSpec = dwItemSpec;
+    nmcd->uItemState = uItemState;
+    nmcd->lItemlParam = wineItem->lParam;
+    nmcdhdr.clrText = infoPtr->clrText;
+    nmcdhdr.clrTextBk = infoPtr->clrBk;
+    nmcdhdr.iLevel = wineItem->iLevel;
+
+    TRACE("drawstage:%lx hdc:%x item:%lx, itemstate:%x, lItemlParam:%lx\n",
+	  nmcd->dwDrawStage, nmcd->hdc, nmcd->dwItemSpec,
+	  nmcd->uItemState, nmcd->lItemlParam);
+
+    retval = SendMessageA(GetParent(hwnd), WM_NOTIFY,
+			  (WPARAM)GetWindowLongA(hwnd, GWL_ID),
+			  (LPARAM)&nmcdhdr);
+
+    infoPtr->clrText = nmcdhdr.clrText;
+    infoPtr->clrBk = nmcdhdr.clrTextBk;
+    return (BOOL)retval;
+}
+
+static BOOL
+TREEVIEW_BeginLabelEditNotify(TREEVIEW_INFO *infoPtr, TREEVIEW_ITEM *editItem)
+{
+    HWND hwnd = infoPtr->hwnd;
+    NMTVDISPINFOA tvdi;
+
+    tvdi.hdr.hwndFrom = hwnd;
+    tvdi.hdr.idFrom = GetWindowLongA(hwnd, GWL_ID);
+    tvdi.hdr.code = TVN_BEGINLABELEDITA;
+
+    tvdi.item.mask = TVIF_HANDLE | TVIF_STATE | TVIF_PARAM | TVIF_TEXT;
+    tvdi.item.hItem = editItem;
+    tvdi.item.state = editItem->state;
+    tvdi.item.lParam = editItem->lParam;
+    tvdi.item.pszText = editItem->pszText;
+    tvdi.item.cchTextMax = editItem->cchTextMax;
+
+    return SendMessageA(GetParent(hwnd), WM_NOTIFY, tvdi.hdr.idFrom,
+			(LPARAM)&tvdi);
+}
 
 static void
-TREEVIEW_RemoveItem (HWND hwnd, TREEVIEW_ITEM *wineItem)
-
+TREEVIEW_UpdateDispInfo(TREEVIEW_INFO *infoPtr, TREEVIEW_ITEM *wineItem,
+			UINT mask)
 {
- TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
- TREEVIEW_ITEM *parentItem, *upsiblingItem, *siblingItem;
- INT iItem;
+    NMTVDISPINFOA callback;
+    HWND hwnd = infoPtr->hwnd;
 
- iItem=(INT)wineItem->hItem;
- tv_set_bit(iItem,infoPtr->freeList);
- infoPtr->uNumItems--;
- parentItem=NULL;
- if (wineItem->pszText!=LPSTR_TEXTCALLBACKA) 
-	COMCTL32_Free (wineItem->pszText);
+    mask &= wineItem->callbackMask;
 
- TREEVIEW_SendTreeviewNotify (hwnd, TVN_DELETEITEMA, 0, (HTREEITEM)iItem, 0);
+    if (mask == 0) return;
 
- if (wineItem->firstChild) 
- 	TREEVIEW_RemoveAllChildren (hwnd,wineItem);
+    callback.hdr.hwndFrom         = hwnd;
+    callback.hdr.idFrom           = GetWindowLongA(hwnd, GWL_ID);
+    callback.hdr.code             = TVN_GETDISPINFOA;
 
- if (wineItem->parent) {
-	parentItem=& infoPtr->items [(INT)wineItem->parent];
-	switch (parentItem->cChildren) {
-		case I_CHILDRENCALLBACK: 
-				FIXME("we don't handle I_CHILDRENCALLBACK yet\n");
-				break;
-		case 1:
-			parentItem->cChildren=0;
-			parentItem->firstChild=0;    
-			return;
-		default:
-			parentItem->cChildren--;
-			if ((INT)parentItem->firstChild==iItem) 
-				parentItem->firstChild=wineItem->sibling;
+    /* 'state' always contains valid value, as well as 'lParam'.
+     * All other parameters are uninitialized.
+     */
+    callback.item.pszText         = wineItem->pszText;
+    callback.item.cchTextMax      = wineItem->cchTextMax;
+    callback.item.mask            = mask;
+    callback.item.hItem           = wineItem;
+    callback.item.state           = wineItem->state;
+    callback.item.lParam          = wineItem->lParam;
+
+    /* If text is changed we need to recalculate textWidth */
+    if (mask & TVIF_TEXT)
+       wineItem->textWidth = 0;
+
+    SendMessageA(GetParent(hwnd), WM_NOTIFY, callback.hdr.idFrom, (LPARAM)&callback);
+
+    /* It may have changed due to a call to SetItem. */ 
+    mask &= wineItem->callbackMask;
+
+    if ((mask & TVIF_TEXT) && callback.item.pszText != wineItem->pszText)
+    {
+	/* Instead of copying text into our buffer user specified its own */
+	int len = max(lstrlenA(callback.item.pszText) + 1, TEXT_CALLBACK_SIZE);
+	LPSTR newText = COMCTL32_ReAlloc(wineItem->pszText, len);
+
+	if (newText)
+	{
+	   wineItem->pszText = newText;
+	   strcpy(wineItem->pszText, callback.item.pszText);
+	   wineItem->cchTextMax = len;
+	}
+        /* If ReAlloc fails we have nothing to do, but keep original text */
+    }
+
+    if (mask & TVIF_IMAGE)
+	wineItem->iImage = callback.item.iImage;
+
+    if (mask & TVIF_SELECTEDIMAGE)
+	wineItem->iSelectedImage = callback.item.iSelectedImage;
+
+    if (mask & TVIF_CHILDREN)
+	wineItem->cChildren = callback.item.cChildren;
+
+    /* These members are now permanently set. */
+    if (callback.item.mask & TVIF_DI_SETITEM)
+	wineItem->callbackMask &= ~callback.item.mask;
+}
+
+/***************************************************************************
+ * This function uses cChildren field to decide whether the item has
+ * children or not.
+ * Note: if this returns TRUE, the child items may not actually exist,
+ * they could be virtual.
+ *
+ * Just use wineItem->firstChild to check for physical children.
+ */
+static BOOL
+TREEVIEW_HasChildren(TREEVIEW_INFO *infoPtr, TREEVIEW_ITEM *wineItem)
+{
+    TREEVIEW_UpdateDispInfo(infoPtr, wineItem, TVIF_CHILDREN);
+
+    return wineItem->cChildren > 0;
+}
+
+
+/* Item Position ********************************************************/
+
+/* Compute linesOffset, stateOffset, imageOffset, textOffset of an item. */
+static VOID
+TREEVIEW_ComputeItemInternalMetrics(TREEVIEW_INFO *infoPtr,
+				    TREEVIEW_ITEM *item)
+{
+    /* Same effect, different optimisation. */
+#if 0
+    BOOL lar = ((infoPtr->dwStyle & TVS_LINESATROOT)
+		&& (infoPtr->dwStyle & (TVS_HASLINES|TVS_HASBUTTONS)));
+#else
+    BOOL lar = ((infoPtr->dwStyle
+		 & (TVS_LINESATROOT|TVS_HASLINES|TVS_HASBUTTONS))
+		> TVS_LINESATROOT);
+#endif
+
+    item->linesOffset = infoPtr->uIndent * (item->iLevel + lar - 1)
+	- infoPtr->scrollX;
+    item->stateOffset = item->linesOffset + infoPtr->uIndent;
+    item->imageOffset = item->stateOffset
+	+ (STATEIMAGEINDEX(item->state) ? infoPtr->stateImageWidth : 0);
+    item->textOffset  = item->imageOffset + infoPtr->normalImageWidth;
+}
+
+static VOID
+TREEVIEW_ComputeTextWidth(TREEVIEW_INFO *infoPtr, TREEVIEW_ITEM *item, HDC hDC)
+{
+    HDC hdc;
+    HFONT hOldFont=0;
+    SIZE sz;
+
+    /* DRAW's OM docker creates items like this */
+    if (item->pszText == NULL)
+    {
+	item->textWidth = 0;
+	return;
+    }
+
+    if (item->textWidth != 0 && !(item->callbackMask & TVIF_TEXT))
+       return;
+
+    if (hDC != 0)
+    {
+	hdc = hDC;
+    }
+    else
+    {
+	hdc = GetDC(infoPtr->hwnd);
+	hOldFont = SelectObject(hdc, TREEVIEW_FontForItem(infoPtr, item));
+    }
+
+    GetTextExtentPoint32A(hdc, item->pszText, strlen(item->pszText), &sz);
+    item->textWidth = sz.cx;
+
+    if (hDC == 0)
+    {
+	SelectObject(hdc, hOldFont);
+	ReleaseDC(0, hdc);
+    }
+}
+
+static VOID
+TREEVIEW_ComputeItemRect(TREEVIEW_INFO *infoPtr, TREEVIEW_ITEM *item)
+{
+    item->rect.top = infoPtr->uItemHeight *
+	(item->visibleOrder - infoPtr->firstVisible->visibleOrder);
+
+    item->rect.bottom = item->rect.top
+	+ infoPtr->uItemHeight * item->iIntegral;
+
+    item->rect.left = 0;
+    item->rect.right = infoPtr->clientWidth;
+}
+
+/* We know that only items after start need their order updated. */
+static void
+TREEVIEW_RecalculateVisibleOrder(TREEVIEW_INFO *infoPtr, TREEVIEW_ITEM *start)
+{
+    TREEVIEW_ITEM *item;
+    int order;
+
+    if (!start)
+    {
+	start = infoPtr->root->firstChild;
+	order = 0;
+    }
+    else
+	order = start->visibleOrder;
+
+    for (item = start; item != NULL;
+         item = TREEVIEW_GetNextListItem(infoPtr, item))
+    {
+	item->visibleOrder = order;
+	order += item->iIntegral;
+    }
+
+    infoPtr->maxVisibleOrder = order;
+
+    for (item = start; item != NULL;
+	 item = TREEVIEW_GetNextListItem(infoPtr, item))
+    {
+	TREEVIEW_ComputeItemRect(infoPtr, item);
+    }
+}
+
+
+/* Update metrics of all items in selected subtree.
+ * root must be expanded
+ */
+static VOID
+TREEVIEW_UpdateSubTree(TREEVIEW_INFO *infoPtr, TREEVIEW_ITEM *root)
+{
+   TREEVIEW_ITEM *sibling;
+   HDC hdc;
+   HFONT hOldFont;
+
+   if (!root->firstChild || !(root->state & TVIS_EXPANDED))
+      return;
+
+   root->state &= ~TVIS_EXPANDED;
+   sibling = TREEVIEW_GetNextListItem(infoPtr, root);
+   root->state |= TVIS_EXPANDED;
+
+   hdc = GetDC(infoPtr->hwnd);
+   hOldFont = SelectObject(hdc, infoPtr->hFont);
+
+   for (; root != sibling;
+        root = TREEVIEW_GetNextListItem(infoPtr, root))
+   {
+      TREEVIEW_ComputeItemInternalMetrics(infoPtr, root);
+
+      if (root->callbackMask & TVIF_TEXT)
+         TREEVIEW_UpdateDispInfo(infoPtr, root, TVIF_TEXT);
+
+      if (root->textWidth == 0)
+      {
+         SelectObject(hdc, TREEVIEW_FontForItem(infoPtr, root));
+         TREEVIEW_ComputeTextWidth(infoPtr, root, hdc);
+      }
+   }
+
+   SelectObject(hdc, hOldFont);
+   ReleaseDC(infoPtr->hwnd, hdc);
+}
+
+/* Item Allocation **********************************************************/
+
+static TREEVIEW_ITEM *
+TREEVIEW_AllocateItem(TREEVIEW_INFO *infoPtr)
+{
+    TREEVIEW_ITEM *newItem = COMCTL32_Alloc(sizeof(TREEVIEW_ITEM));
+
+    if (!newItem)
+	return NULL;
+
+    if (DPA_InsertPtr(infoPtr->items, INT_MAX, newItem) == -1)
+    {
+	COMCTL32_Free(newItem);
+	return NULL;
+    }
+
+    return newItem;
+}
+
+/* Exact opposite of TREEVIEW_AllocateItem. In particular, it does not
+ * free item->pszText. */
+static void
+TREEVIEW_FreeItem(TREEVIEW_INFO *infoPtr, TREEVIEW_ITEM *item)
+{
+    DPA_DeletePtr(infoPtr->items, DPA_GetPtrIndex(infoPtr->items, item));
+    COMCTL32_Free(item);
+}
+
+
+/* Item Insertion *******************************************************/
+
+/***************************************************************************
+ * This method inserts newItem before sibling as a child of parent.
+ * sibling can be NULL, but only if parent has no children.
+ */
+static void
+TREEVIEW_InsertBefore(TREEVIEW_ITEM *newItem, TREEVIEW_ITEM *sibling,
+		      TREEVIEW_ITEM *parent)
+{
+    assert(newItem != NULL);
+    assert(parent != NULL);
+
+    if (sibling != NULL)
+    {
+	assert(sibling->parent == parent);
+
+	if (sibling->prevSibling != NULL)
+	    sibling->prevSibling->nextSibling = newItem;
+
+	newItem->prevSibling = sibling->prevSibling;
+	sibling->prevSibling = newItem;
+    }
+    else
+       newItem->prevSibling = NULL;
+
+    newItem->nextSibling = sibling;
+
+    if (parent->firstChild == sibling)
+	parent->firstChild = newItem;
+
+    if (parent->lastChild == NULL)
+	parent->lastChild = newItem;
+}
+
+/***************************************************************************
+ * This method inserts newItem after sibling as a child of parent.
+ * sibling can be NULL, but only if parent has no children.
+ */
+static void
+TREEVIEW_InsertAfter(TREEVIEW_ITEM *newItem, TREEVIEW_ITEM *sibling,
+		     TREEVIEW_ITEM *parent)
+{
+    assert(newItem != NULL);
+    assert(parent != NULL);
+
+    if (sibling != NULL)
+    {
+	assert(sibling->parent == parent);
+
+	if (sibling->nextSibling != NULL)
+	    sibling->nextSibling->prevSibling = newItem;
+
+	newItem->nextSibling = sibling->nextSibling;
+	sibling->nextSibling = newItem;
+    }
+    else
+       newItem->nextSibling = NULL;
+
+    newItem->prevSibling = sibling;
+
+    if (parent->lastChild == sibling)
+	parent->lastChild = newItem;
+
+    if (parent->firstChild == NULL)
+	parent->firstChild = newItem;
+}
+
+static BOOL
+TREEVIEW_DoSetItem(TREEVIEW_INFO *infoPtr, TREEVIEW_ITEM *wineItem,
+		   const TVITEMEXA *tvItem)
+{
+    UINT callbackClear = 0;
+    UINT callbackSet = 0;
+
+    /* Do this first in case it fails. */
+    if (tvItem->mask & TVIF_TEXT)
+    {
+	if (tvItem->pszText != LPSTR_TEXTCALLBACKA)
+	{
+	    int len = lstrlenA(tvItem->pszText) + 1;
+	    LPSTR newText = COMCTL32_ReAlloc(wineItem->pszText, len);
+
+	    if (newText == NULL) return FALSE;
+
+	    callbackClear |= TVIF_TEXT;
+
+	    wineItem->pszText = newText;
+	    wineItem->cchTextMax = len;
+	    lstrcpynA(wineItem->pszText, tvItem->pszText, len);
+	}
+	else
+	{
+	    callbackSet |= TVIF_TEXT;
+
+	    wineItem->pszText = COMCTL32_ReAlloc(wineItem->pszText,
+						 TEXT_CALLBACK_SIZE);
+	    wineItem->cchTextMax = TEXT_CALLBACK_SIZE;
+	}
+    }
+
+    if (tvItem->mask & TVIF_CHILDREN)
+    {
+	wineItem->cChildren = tvItem->cChildren;
+
+	if (wineItem->cChildren == I_CHILDRENCALLBACK)
+	    callbackSet |= TVIF_CHILDREN;
+	else
+	    callbackClear |= TVIF_CHILDREN;
+    }
+
+    if (tvItem->mask & TVIF_IMAGE)
+    {
+	wineItem->iImage = tvItem->iImage;
+
+	if (wineItem->iImage == I_IMAGECALLBACK)
+	    callbackSet |= TVIF_IMAGE;
+	else
+	    callbackClear |= TVIF_IMAGE;
+    }
+
+    if (tvItem->mask & TVIF_SELECTEDIMAGE)
+    {
+	wineItem->iSelectedImage = tvItem->iSelectedImage;
+
+	if (wineItem->iSelectedImage == I_IMAGECALLBACK)
+	    callbackSet |= TVIF_SELECTEDIMAGE;
+	else
+	    callbackClear |= TVIF_SELECTEDIMAGE;
+    }
+
+    if (tvItem->mask & TVIF_PARAM)
+	wineItem->lParam = tvItem->lParam;
+
+    /* If the application sets TVIF_INTEGRAL without
+     * supplying a TVITEMEX structure, it's toast. */
+    if (tvItem->mask & TVIF_INTEGRAL)
+	wineItem->iIntegral = tvItem->iIntegral;
+
+    if (tvItem->mask & TVIF_STATE)
+    {
+	TRACE("prevstate,state,mask:%x,%x,%x\n", wineItem->state, tvItem->state,
+	      tvItem->stateMask);
+	wineItem->state &= ~tvItem->stateMask;
+	wineItem->state |= (tvItem->state & tvItem->stateMask);
+    }
+
+    wineItem->callbackMask |= callbackSet;
+    wineItem->callbackMask &= ~callbackClear;
+
+    return TRUE;
+}
+
+/* Note that the new item is pre-zeroed. */
+static LRESULT
+TREEVIEW_InsertItemA(TREEVIEW_INFO *infoPtr, LPARAM lParam)
+{
+    const TVINSERTSTRUCTA *ptdi = (LPTVINSERTSTRUCTA) lParam;
+    const TVITEMEXA *tvItem = &ptdi->DUMMYUNIONNAME.itemex;
+    HTREEITEM insertAfter;
+    TREEVIEW_ITEM *newItem, *parentItem;
+    BOOL bTextUpdated = FALSE;
+
+    if (ptdi->hParent == TVI_ROOT || ptdi->hParent == 0)
+    {
+	parentItem = infoPtr->root;
+    }
+    else
+    {
+	parentItem = ptdi->hParent;
+
+	if (!TREEVIEW_ValidItem(infoPtr, parentItem))
+	{
+	    WARN("invalid parent %p\n", parentItem);
+	    return (LRESULT)(HTREEITEM)NULL;
+	}
+    }
+
+    insertAfter = ptdi->hInsertAfter;
+
+    /* Validate this now for convenience. */
+    switch ((DWORD)insertAfter)
+    {
+    case (DWORD)TVI_FIRST:
+    case (DWORD)TVI_LAST:
+    case (DWORD)TVI_SORT:
+	break;
+
+    default:
+	if (!TREEVIEW_ValidItem(infoPtr, insertAfter) ||
+            insertAfter->parent != parentItem)
+	{
+	    WARN("invalid insert after %p\n", insertAfter);
+	    insertAfter = TVI_LAST;
+	}
+    }
+
+    TRACE("parent %p position %p: %s\n", parentItem, insertAfter,
+	  (tvItem->mask & TVIF_TEXT)
+	  ? ((tvItem->pszText == LPSTR_TEXTCALLBACKA) ? "<callback>"
+	     : tvItem->pszText)
+	  : "<no label>");
+
+    newItem = TREEVIEW_AllocateItem(infoPtr);
+    if (newItem == NULL)
+	return (LRESULT)(HTREEITEM)NULL;
+
+    newItem->parent = parentItem;
+    newItem->iIntegral = 1;
+
+    if (!TREEVIEW_DoSetItem(infoPtr, newItem, tvItem))
+	return (LRESULT)(HTREEITEM)NULL;
+
+    /* After this point, nothing can fail. (Except for TVI_SORT.) */
+
+    infoPtr->uNumItems++;
+
+    switch ((DWORD)insertAfter)
+    {
+    case (DWORD)TVI_FIRST:
+	TREEVIEW_InsertBefore(newItem, parentItem->firstChild, parentItem);
+	if (infoPtr->firstVisible == parentItem->firstChild)
+	    TREEVIEW_SetFirstVisible(infoPtr, newItem, TRUE);
+	break;
+
+    case (DWORD)TVI_LAST:
+	TREEVIEW_InsertAfter(newItem, parentItem->lastChild, parentItem);
+	break;
+
+	/* hInsertAfter names a specific item we want to insert after */
+    default:
+	TREEVIEW_InsertAfter(newItem, insertAfter, insertAfter->parent);
+	break;
+
+    case (DWORD)TVI_SORT:
+	{
+	    TREEVIEW_ITEM *aChild;
+	    TREEVIEW_ITEM *previousChild = NULL;
+	    BOOL bItemInserted = FALSE;
+
+	    aChild = parentItem->firstChild;
+
+	    bTextUpdated = TRUE;
+	    TREEVIEW_UpdateDispInfo(infoPtr, newItem, TVIF_TEXT);
+
+	    /* Iterate the parent children to see where we fit in */
+	    while (aChild != NULL)
+	    {
+		INT comp;
+
+		TREEVIEW_UpdateDispInfo(infoPtr, aChild, TVIF_TEXT);
+		comp = lstrcmpA(newItem->pszText, aChild->pszText);
+
+		if (comp < 0)	/* we are smaller than the current one */
+		{
+		    TREEVIEW_InsertBefore(newItem, aChild, parentItem);
+		    bItemInserted = TRUE;
+		    break;
 		}
- }
+		else if (comp > 0)	/* we are bigger than the current one */
+		{
+		    previousChild = aChild;
 
- if (iItem==(INT)infoPtr->TopRootItem) 
-	infoPtr->TopRootItem=(HTREEITEM)wineItem->sibling;
- if (wineItem->upsibling) {
-	upsiblingItem=& infoPtr->items [(INT)wineItem->upsibling];
-	upsiblingItem->sibling=wineItem->sibling;
- }
- if (wineItem->sibling) {
-	siblingItem=& infoPtr->items [(INT)wineItem->sibling];
-	siblingItem->upsibling=wineItem->upsibling;
- }
+		    /* This will help us to exit if there is no more sibling */
+		    aChild = (aChild->nextSibling == 0)
+			? NULL	
+			: aChild->nextSibling;
+
+		    /* Look at the next item */
+		    continue;
+		}
+		else if (comp == 0)
+		{
+		    /* 
+		     * An item with this name is already existing, therefore,  
+		     * we add after the one we found 
+		     */
+		    TREEVIEW_InsertAfter(newItem, aChild, parentItem);
+		    bItemInserted = TRUE;
+		    break;
+		}
+	    }
+
+	    /* 
+	     * we reach the end of the child list and the item as not
+	     * yet been inserted, therefore, insert it after the last child.
+	     */
+	    if ((!bItemInserted) && (aChild == NULL))
+		TREEVIEW_InsertAfter(newItem, previousChild, parentItem);
+
+	    break;
+	}
+    }
+
+
+    TRACE("new item %p; parent %p, mask %x\n", newItem,
+	  newItem->parent, tvItem->mask);
+
+    newItem->iLevel = newItem->parent->iLevel + 1;
+
+    if (newItem->parent->cChildren == 0)
+	newItem->parent->cChildren = 1;
+
+    if (infoPtr->dwStyle & TVS_CHECKBOXES)
+    {
+	if (STATEIMAGEINDEX(newItem->state) == 0)
+	    newItem->state |= INDEXTOSTATEIMAGEMASK(1);
+    }
+
+    if (infoPtr->firstVisible == NULL)
+	infoPtr->firstVisible = newItem;
+
+    TREEVIEW_VerifyTree(infoPtr);
+
+    if (parentItem == infoPtr->root ||
+        (ISVISIBLE(parentItem) && parentItem->state & TVIS_EXPANDED))
+    {
+       TREEVIEW_ITEM *prev = TREEVIEW_GetPrevListItem(infoPtr, newItem);
+
+       TREEVIEW_RecalculateVisibleOrder(infoPtr, prev);
+       TREEVIEW_ComputeItemInternalMetrics(infoPtr, newItem);
+
+       if (!bTextUpdated)
+          TREEVIEW_UpdateDispInfo(infoPtr, newItem, TVIF_TEXT);
+
+       TREEVIEW_ComputeTextWidth(infoPtr, newItem, 0);
+       TREEVIEW_UpdateScrollBars(infoPtr);
+       TREEVIEW_QueueRefresh(infoPtr);
+    }
+    else
+    {
+       newItem->visibleOrder = -1;
+
+       /* refresh treeview if newItem is the first item inserted under parentItem */
+       if (ISVISIBLE(parentItem) && newItem->prevSibling == newItem->nextSibling)
+       {
+          /* parent got '+' - update it */
+          TREEVIEW_QueueItemRefresh(infoPtr, parentItem);
+       }
+    }
+
+    return (LRESULT)newItem;
 }
 
 
-
-
-
-/* Note:TREEVIEW_RemoveTree doesn't remove infoPtr itself */
-
-static void TREEVIEW_RemoveTree (HWND hwnd)
-					   
-{
-  TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
-  TREEVIEW_ITEM *killItem;
-  int i;
-
-  for (i = 1; i <= (INT)infoPtr->uMaxHandle; i++) 
-    if (!tv_test_bit (i, infoPtr->freeList)) {
-      killItem = &infoPtr->items[i];	
-      if (killItem->pszText != LPSTR_TEXTCALLBACKA)
-	COMCTL32_Free (killItem->pszText);
-      TREEVIEW_SendTreeviewNotify(hwnd, TVN_DELETEITEMA, 0,
-				  killItem->hItem, 0);
-    } 
-  if (infoPtr->uNumPtrsAlloced) {
-    COMCTL32_Free (infoPtr->items);
-    COMCTL32_Free (infoPtr->freeList);
-    infoPtr->uNumItems = 0;
-    infoPtr->uNumPtrsAlloced = 0;
-    infoPtr->uMaxHandle = 0;
-    infoPtr->TopRootItem = 0;
-  }   
-}
-
-
-
-
-
-
-
 static LRESULT
-TREEVIEW_GetImageList (HWND hwnd, WPARAM wParam, LPARAM lParam)
+TREEVIEW_InsertItemW(TREEVIEW_INFO *infoPtr, LPARAM lParam)
 {
-  TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
+    TVINSERTSTRUCTW *tvisW;
+    TVINSERTSTRUCTA tvisA;
+    LRESULT lRes;
 
-  TRACE("\n");
+    tvisW = (LPTVINSERTSTRUCTW) lParam;
 
-  if ((INT)wParam == TVSIL_NORMAL) 
-	return (LRESULT) infoPtr->himlNormal;
-  if ((INT)wParam == TVSIL_STATE) 
-	return (LRESULT) infoPtr->himlState;
+    tvisA.hParent = tvisW->hParent;
+    tvisA.hInsertAfter = tvisW->hInsertAfter;
+
+    tvisA.DUMMYUNIONNAME.item.mask = tvisW->DUMMYUNIONNAME.item.mask;
+    tvisA.DUMMYUNIONNAME.item.hItem = tvisW->DUMMYUNIONNAME.item.hItem;
+    tvisA.DUMMYUNIONNAME.item.state = tvisW->DUMMYUNIONNAME.item.state;
+    tvisA.DUMMYUNIONNAME.item.stateMask = tvisW->DUMMYUNIONNAME.item.stateMask;
+    tvisA.DUMMYUNIONNAME.item.cchTextMax =
+	tvisW->DUMMYUNIONNAME.item.cchTextMax;
+
+    if (tvisW->DUMMYUNIONNAME.item.pszText)
+    {
+	if (tvisW->DUMMYUNIONNAME.item.pszText != LPSTR_TEXTCALLBACKW)
+	{
+	    int len = lstrlenW(tvisW->DUMMYUNIONNAME.item.pszText) + 1;
+
+	    tvisA.DUMMYUNIONNAME.item.pszText = COMCTL32_Alloc(len);
+	    lstrcpyWtoA(tvisA.DUMMYUNIONNAME.item.pszText,
+			tvisW->DUMMYUNIONNAME.item.pszText);
+	}
+	else
+	{
+	    tvisA.DUMMYUNIONNAME.item.pszText = LPSTR_TEXTCALLBACKA;
+	    tvisA.DUMMYUNIONNAME.item.cchTextMax = 0;
+	}
+    }
+
+    tvisA.DUMMYUNIONNAME.item.iImage = tvisW->DUMMYUNIONNAME.item.iImage;
+    tvisA.DUMMYUNIONNAME.item.iSelectedImage =
+	tvisW->DUMMYUNIONNAME.item.iSelectedImage;
+    tvisA.DUMMYUNIONNAME.item.cChildren = tvisW->DUMMYUNIONNAME.item.cChildren;
+    tvisA.DUMMYUNIONNAME.item.lParam = tvisW->DUMMYUNIONNAME.item.lParam;
+
+    lRes = TREEVIEW_InsertItemA(infoPtr, (LPARAM)&tvisA);
+
+    if (tvisA.DUMMYUNIONNAME.item.pszText != LPSTR_TEXTCALLBACKA)
+    {
+	COMCTL32_Free(tvisA.DUMMYUNIONNAME.item.pszText);
+    }
+
+    return lRes;
+
+}
+
+
+/* Item Deletion ************************************************************/
+static void
+TREEVIEW_RemoveItem(TREEVIEW_INFO *infoPtr, TREEVIEW_ITEM *wineItem);
+
+static void
+TREEVIEW_RemoveAllChildren(TREEVIEW_INFO *infoPtr, TREEVIEW_ITEM *parentItem)
+{
+    TREEVIEW_ITEM *kill = parentItem->firstChild;
+
+    while (kill != NULL)
+    {
+	TREEVIEW_ITEM *next = kill->nextSibling;
+
+	TREEVIEW_RemoveItem(infoPtr, kill);
+
+	kill = next;
+    }
+
+    assert(parentItem->cChildren <= 0); /* I_CHILDRENCALLBACK or 0 */
+    assert(parentItem->firstChild == NULL);
+    assert(parentItem->lastChild == NULL);
+}
+
+static void
+TREEVIEW_UnlinkItem(TREEVIEW_ITEM *item)
+{
+    TREEVIEW_ITEM *parentItem = item->parent;
+
+    assert(item != NULL);
+    assert(item->parent != NULL); /* i.e. it must not be the root */
+
+    if (parentItem->firstChild == item)
+	parentItem->firstChild = item->nextSibling;
+
+    if (parentItem->lastChild == item)
+	parentItem->lastChild = item->prevSibling;
+
+    if (parentItem->firstChild == NULL && parentItem->lastChild == NULL
+	&& parentItem->cChildren > 0)
+	parentItem->cChildren = 0;
+
+    if (item->prevSibling)
+	item->prevSibling->nextSibling = item->nextSibling;
+
+    if (item->nextSibling)
+	item->nextSibling->prevSibling = item->prevSibling;
+}
+
+static void
+TREEVIEW_RemoveItem(TREEVIEW_INFO *infoPtr, TREEVIEW_ITEM *wineItem)
+{
+    TRACE("%p, (%s)\n", wineItem, TREEVIEW_ItemName(wineItem));
+
+    TREEVIEW_SendTreeviewNotify(infoPtr, TVN_DELETEITEMA,
+				TVIF_HANDLE | TVIF_PARAM, 0, wineItem, 0);
+
+    if (wineItem->firstChild)
+	TREEVIEW_RemoveAllChildren(infoPtr, wineItem);
+
+    TREEVIEW_UnlinkItem(wineItem);
+
+    infoPtr->uNumItems--;
+
+    if (wineItem->pszText != LPSTR_TEXTCALLBACKA)
+	COMCTL32_Free(wineItem->pszText);
+
+    TREEVIEW_FreeItem(infoPtr, wineItem);
+}
+
+
+/* Empty out the tree. */
+static void
+TREEVIEW_RemoveTree(TREEVIEW_INFO *infoPtr)
+{
+    TREEVIEW_RemoveAllChildren(infoPtr, infoPtr->root);
+
+    assert(infoPtr->uNumItems == 0);	/* root isn't counted in uNumItems */
+}
+
+static LRESULT
+TREEVIEW_DeleteItem(TREEVIEW_INFO *infoPtr, HTREEITEM wineItem)
+{
+    TREEVIEW_ITEM *oldSelection = infoPtr->selectedItem;
+    TREEVIEW_ITEM *newSelection = oldSelection;
+    TREEVIEW_ITEM *newFirstVisible = NULL;
+    TREEVIEW_ITEM *parent, *prev = NULL;
+    BOOL visible = FALSE;
+
+    if (wineItem == TVI_ROOT)
+    {
+	TRACE("TVI_ROOT\n");
+	parent = infoPtr->root;
+	newSelection = NULL;
+	visible = TRUE;
+	TREEVIEW_RemoveTree(infoPtr);
+    }
+    else
+    {
+	if (!TREEVIEW_ValidItem(infoPtr, wineItem))
+	    return FALSE;
+
+	TRACE("%p (%s)\n", wineItem, TREEVIEW_ItemName(wineItem));
+	parent = wineItem->parent;
+
+        if (ISVISIBLE(wineItem))
+        {
+            prev = TREEVIEW_GetPrevListItem(infoPtr, wineItem);
+            visible = TRUE;
+        }
+
+	if (infoPtr->selectedItem != NULL
+	    && (wineItem == infoPtr->selectedItem
+		|| TREEVIEW_IsChildOf(wineItem, infoPtr->selectedItem)))
+	{
+	    if (wineItem->nextSibling)
+		newSelection = wineItem->nextSibling;
+	    else if (wineItem->parent != infoPtr->root)
+		newSelection = wineItem->parent;
+	}
+
+	if (infoPtr->firstVisible == wineItem)
+	{
+	    if (wineItem->nextSibling)
+	       newFirstVisible = wineItem->nextSibling;
+	    else if (wineItem->prevSibling)
+	       newFirstVisible = wineItem->prevSibling;
+	    else if (wineItem->parent != infoPtr->root)
+	       newFirstVisible = wineItem->parent;
+	}
+	else
+	    newFirstVisible = infoPtr->firstVisible;
+
+	TREEVIEW_RemoveItem(infoPtr, wineItem);
+    }
+
+    /* Don't change if somebody else already has. */
+    if (oldSelection == infoPtr->selectedItem)
+    {
+	if (TREEVIEW_ValidItem(infoPtr, newSelection))
+	    TREEVIEW_DoSelectItem(infoPtr, TVGN_CARET, newSelection, TVC_UNKNOWN);
+	else
+	    infoPtr->selectedItem = 0;
+    }
+
+    /* Validate insertMark dropItem.
+     * hotItem ??? - used for comparision only.
+     */
+    if (!TREEVIEW_ValidItem(infoPtr, infoPtr->insertMarkItem))
+	infoPtr->insertMarkItem = 0;
+
+    if (!TREEVIEW_ValidItem(infoPtr, infoPtr->dropItem))
+	infoPtr->dropItem = 0;
+
+    if (!TREEVIEW_ValidItem(infoPtr, newFirstVisible))
+        newFirstVisible = infoPtr->root->firstChild;
+
+    TREEVIEW_VerifyTree(infoPtr);
+
+
+    if (visible)
+    {
+       TREEVIEW_RecalculateVisibleOrder(infoPtr, prev);
+       TREEVIEW_SetFirstVisible(infoPtr, newFirstVisible, TRUE);
+       TREEVIEW_UpdateScrollBars(infoPtr);
+       TREEVIEW_QueueRefresh(infoPtr);
+    }
+    else if (ISVISIBLE(parent) && !TREEVIEW_HasChildren(infoPtr, parent))
+    {
+       /* parent lost '+/-' - update it */
+       TREEVIEW_QueueItemRefresh(infoPtr, parent);
+    }
+
+    return TRUE;
+}
+
+
+/* Get/Set Messages *********************************************************/
+static LRESULT
+TREEVIEW_SetRedraw(TREEVIEW_INFO* infoPtr, WPARAM wParam, LPARAM lParam)
+{
+  if(wParam)
+    infoPtr->bRedraw = TRUE;
+  else
+    infoPtr->bRedraw = FALSE;
 
   return 0;
 }
 
 static LRESULT
-TREEVIEW_SetImageList (HWND hwnd, WPARAM wParam, LPARAM lParam)
+TREEVIEW_GetIndent(TREEVIEW_INFO *infoPtr)
 {
-    TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
-    HIMAGELIST himlTemp;
+    TRACE("\n");
+    return infoPtr->uIndent;
+}
 
-    TRACE("%x,%lx\n", wParam, lParam);
-    switch ((INT)wParam) {
-	case TVSIL_NORMAL:
-	    himlTemp = infoPtr->himlNormal;
-	    infoPtr->himlNormal = (HIMAGELIST)lParam;
-	    return (LRESULT)himlTemp;
+static LRESULT
+TREEVIEW_SetIndent(TREEVIEW_INFO *infoPtr, UINT newIndent)
+{
+    TRACE("\n");
 
-	case TVSIL_STATE:
-	    himlTemp = infoPtr->himlState;
-	    infoPtr->himlState = (HIMAGELIST)lParam;
-	    return (LRESULT)himlTemp;
+    if (newIndent < MINIMUM_INDENT)
+	newIndent = MINIMUM_INDENT;
+
+    if (infoPtr->uIndent != newIndent)
+    {
+	infoPtr->uIndent = newIndent;
+	TREEVIEW_UpdateSubTree(infoPtr, infoPtr->root);
+	TREEVIEW_UpdateScrollBars(infoPtr);
+	TREEVIEW_QueueRefresh(infoPtr);
     }
 
-    return (LRESULT)NULL;
+    return 0;
 }
 
 
-
 static LRESULT
-TREEVIEW_SetItemHeight (HWND hwnd, WPARAM wParam)
+TREEVIEW_GetToolTips(TREEVIEW_INFO *infoPtr)
 {
-  TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
-  INT cx,cy,prevHeight=infoPtr->uItemHeight;
-
-  TRACE("\n");
-  if (wParam==-1) {
-	infoPtr->uItemHeight=-1;
-	return prevHeight;
-  }
-
-  ImageList_GetIconSize (infoPtr->himlNormal, &cx, &cy);
-
-  if (wParam>cy) cy=wParam;
-  infoPtr->uItemHeight=cy;
-
-  if (!( GetWindowLongA( hwnd, GWL_STYLE) & TVS_NONEVENHEIGHT))
-	infoPtr->uItemHeight = (INT) wParam & 0xfffffffe;
-  return prevHeight;
+    TRACE("\n");
+    return infoPtr->hwndToolTip;
 }
 
 static LRESULT
-TREEVIEW_GetItemHeight (HWND hwnd)
+TREEVIEW_SetToolTips(TREEVIEW_INFO *infoPtr, HWND hwndTT)
 {
-  TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
-  
-  TRACE("\n");
-  return infoPtr->uItemHeight;
-}
-  
-static LRESULT
-TREEVIEW_GetLineColor (HWND hwnd, WPARAM wParam, LPARAM lParam)
-{
-  TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
+    HWND prevToolTip;
 
-  TRACE("\n");
-  return (LRESULT) infoPtr->clrLine;
+    TRACE("\n");
+    prevToolTip = infoPtr->hwndToolTip;
+    infoPtr->hwndToolTip = hwndTT;
+
+    return prevToolTip;
+}
+
+
+static LRESULT
+TREEVIEW_GetScrollTime(TREEVIEW_INFO *infoPtr)
+{
+    return infoPtr->uScrollTime;
 }
 
 static LRESULT
-TREEVIEW_SetLineColor (HWND hwnd, WPARAM wParam, LPARAM lParam)
+TREEVIEW_SetScrollTime(TREEVIEW_INFO *infoPtr, UINT uScrollTime)
 {
-  TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
-  COLORREF prevColor=infoPtr->clrLine;
+    UINT uOldScrollTime = infoPtr->uScrollTime;
 
-  TRACE("\n");
-  infoPtr->clrLine=(COLORREF) lParam;
-  return (LRESULT) prevColor;
+    infoPtr->uScrollTime = min(uScrollTime, 100);
+
+    return uOldScrollTime;
+}
+
+
+static LRESULT
+TREEVIEW_GetImageList(TREEVIEW_INFO *infoPtr, WPARAM wParam)
+{
+    TRACE("\n");
+
+    switch (wParam)
+    {
+    case (WPARAM)TVSIL_NORMAL:
+	return (LRESULT)infoPtr->himlNormal;
+
+    case (WPARAM)TVSIL_STATE:
+	return (LRESULT)infoPtr->himlState;
+
+    default:
+	return 0;
+    }
 }
 
 static LRESULT
-TREEVIEW_GetInsertMarkColor (HWND hwnd, WPARAM wParam, LPARAM lParam)
+TREEVIEW_SetImageList(TREEVIEW_INFO *infoPtr, WPARAM wParam, HIMAGELIST himlNew)
 {
-  TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
-
-  TRACE("\n");
-  return (LRESULT) infoPtr->clrInsertMark;
-}
-
-static LRESULT
-TREEVIEW_SetInsertMarkColor (HWND hwnd, WPARAM wParam, LPARAM lParam)
-{
-  TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
-  COLORREF prevColor=infoPtr->clrInsertMark;
-
-  TRACE("%d %ld\n",wParam,lParam);
-  infoPtr->clrInsertMark=(COLORREF) lParam;
-  return (LRESULT) prevColor;
-}
-
-static LRESULT
-TREEVIEW_SetInsertMark (HWND hwnd, WPARAM wParam, LPARAM lParam)
-{
-  TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
-
-  FIXME("%d %ld\n",wParam,lParam);
-  if (!TREEVIEW_ValidItem (infoPtr, (HTREEITEM)lParam)) return 0;
-  FIXME("%d %ld\n",wParam,lParam);
-
-  infoPtr->insertBeforeorAfter=(BOOL) wParam;
-  infoPtr->insertMarkItem=(HTREEITEM) lParam;
-  
-  InvalidateRect(hwnd, NULL, FALSE);
-
-  return 1;
-}
-
-static LRESULT
-TREEVIEW_SetTextColor (HWND hwnd, WPARAM wParam, LPARAM lParam)
-{
-  TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
-  COLORREF prevColor=infoPtr->clrText;
-
-  TRACE("\n");
-  infoPtr->clrText=(COLORREF) lParam;
-  return (LRESULT) prevColor;
-}
-
-static LRESULT
-TREEVIEW_GetBkColor (HWND hwnd)
-{
-  TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
-	
-  TRACE("\n");
-  return (LRESULT) infoPtr->clrBk;
-}
-
-static LRESULT
-TREEVIEW_SetBkColor (HWND hwnd, WPARAM wParam, LPARAM lParam)
-{
-  TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
-  COLORREF prevColor=infoPtr->clrBk;
-
-  TRACE("\n");
-  infoPtr->clrBk=(COLORREF) lParam;
-  return (LRESULT) prevColor;
-}
-
-static LRESULT
-TREEVIEW_GetTextColor (HWND hwnd)
-{
-  TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
-	
-  TRACE("\n");
-  return (LRESULT) infoPtr->clrText;
-}
+    HIMAGELIST himlOld = 0;
+    int oldWidth  = infoPtr->normalImageWidth;
+    int oldHeight = infoPtr->normalImageHeight;
 
 
-/* cdmode: custom draw mode as received from app. in first NMCUSTOMDRAW 
-           notification */
+    TRACE("%x,%p\n", wParam, himlNew);
 
-#define TREEVIEW_LEFT_MARGIN 8
+    switch (wParam)
+    {
+    case (WPARAM)TVSIL_NORMAL:
+	himlOld = infoPtr->himlNormal;
+	infoPtr->himlNormal = himlNew;
 
-
-static void
-TREEVIEW_DrawItem (HWND hwnd, HDC hdc, TREEVIEW_ITEM *wineItem)
-{
-  TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
-  DWORD dwStyle = GetWindowLongA (hwnd, GWL_STYLE);
-  INT   center,xpos,cx,cy, cditem;
-  HFONT hOldFont;
-  UINT  uTextJustify = DT_LEFT;
-  RECT  r;
-
- 
-  if (wineItem->state & TVIS_BOLD) 
-  	hOldFont = SelectObject (hdc, infoPtr->hBoldFont);
-  else 
-  	hOldFont = SelectObject (hdc, infoPtr->hFont);
-
-  cditem=0;
-  TRACE ("cdmode:%x\n",infoPtr->cdmode);
-  if (infoPtr->cdmode & CDRF_NOTIFYITEMDRAW) {
-		cditem=TREEVIEW_SendCustomDrawItemNotify 
- 					(hwnd, hdc, wineItem, CDDS_ITEMPREPAINT);
-		TRACE("prepaint:cditem-app returns 0x%x\n",cditem);
-
-		if (cditem & CDRF_SKIPDEFAULT) 
-			return;
+	if (himlNew != NULL)
+	    ImageList_GetIconSize(himlNew, &infoPtr->normalImageWidth,
+				  &infoPtr->normalImageHeight);
+	else
+	{
+	    infoPtr->normalImageWidth = 0;
+	    infoPtr->normalImageHeight = 0;
 	}
 
-  /* 
-   * Set drawing starting points 
-   */
-  r      = wineItem->rect;               /* this item rectangle */
-  center = (r.top+r.bottom)/2;           /* this item vertical center */
-  xpos   = r.left + TREEVIEW_LEFT_MARGIN;/* horizontal starting point */
+	break;
 
-  /* 
-   * Display the tree hierarchy 
-   */
-  if ( dwStyle & TVS_HASLINES) 
-  {
-    /* 
-     * Write links to parent node 
-     * we draw the L starting from the child to the parent
-     *
-     * points[0] is attached to the current item
-     * points[1] is the L corner
-     * points[2] is attached to the parent or the up sibling
-     */
-    if ( dwStyle & TVS_LINESATROOT) 
-    {
-      TREEVIEW_ITEM *upNode    = NULL;
-     	BOOL  hasParentOrSibling = TRUE;
-      RECT  upRect             = {0,0,0,0};
-      HPEN  hOldPen, hNewPen;
-    	POINT points[3];
-      /* 
-       * determine the target location of the line at root, either be linked
-       * to the up sibling or to the parent node.
-       */
-      if (wineItem->upsibling)
-      	upNode  = TREEVIEW_ValidItem (infoPtr, wineItem->upsibling);
-      else if (wineItem->parent)
-      	upNode  = TREEVIEW_ValidItem (infoPtr, wineItem->parent);
-      else
-        hasParentOrSibling = FALSE;
+    case (WPARAM)TVSIL_STATE:
+	himlOld = infoPtr->himlState;
+	infoPtr->himlState = himlNew;
 
-      if (upNode)
-        upRect = upNode->rect;
-
-      if ( wineItem->iLevel == 0 )
-      {
-        points[2].x = points[1].x = upRect.left+8;
-        points[0].x = points[2].x + 10;
-        points[2].y = upRect.bottom-3;      
-        points[1].y = points[0].y = center;
-      }
-      else
-      {
-        points[2].x = points[1].x = 8 + (20*wineItem->iLevel); 
-        points[2].y = ( upNode->cChildren == 0) ? 
-                        upRect.top :        /* is linked to the "L" above */
-                        ( wineItem->upsibling != NULL) ? 
-                          upRect.bottom-3:  /* is linked to an icon       */
-                          upRect.bottom+1;  /* is linked to a +/- box     */
-        points[1].y = points[0].y = center;
-        points[0].x = points[1].x + 10; 
-      }
-    
-      /* 
-       * Get a dotted pen
-       */ 
-      hNewPen = CreatePen(PS_DOT, 0, infoPtr->clrLine);
-      hOldPen = SelectObject( hdc, hNewPen );
-  
-      if (hasParentOrSibling)
-        Polyline (hdc,points,3); 
-      else
-        Polyline (hdc,points,2); 
-      
-      DeleteObject(hNewPen);
-      SelectObject(hdc, hOldPen);
-    }
-  }
-
-  /* 
-   * Display the (+/-) signs
-   */
-  if (wineItem->iLevel != 0)/*  update position only for non root node */
-    xpos+=(5*wineItem->iLevel);
-
-  if (( dwStyle & TVS_HASBUTTONS) && ( dwStyle & TVS_HASLINES))
-  {
-	  if ( (wineItem->cChildren) ||
-	       (wineItem->cChildren == I_CHILDRENCALLBACK))
-    {
-      /* Setup expand box coordinate to facilitate the LMBClick handling */
-      wineItem->expandBox.left   = xpos-4;
-      wineItem->expandBox.top    = center-4;
-      wineItem->expandBox.right  = xpos+5;
-      wineItem->expandBox.bottom = center+5;
-
-  		Rectangle (
-        hdc, 
-        wineItem->expandBox.left, 
-        wineItem->expandBox.top , 
-        wineItem->expandBox.right, 
-        wineItem->expandBox.bottom);
-
-  		MoveToEx (hdc, xpos-2, center, NULL);
-  		LineTo   (hdc, xpos+3, center);
-  
-  		if (!(wineItem->state & TVIS_EXPANDED)) {
-  			MoveToEx (hdc, xpos,   center-2, NULL);
-  			LineTo   (hdc, xpos,   center+3);
-  	  }
-    }
-  }
-
-  /* 
-   * Display the image associated with this item
-   */
-  xpos += 13; /* update position */
-  if (wineItem->mask & (TVIF_IMAGE|TVIF_SELECTEDIMAGE)) {
-    INT        imageIndex;
-    HIMAGELIST *himlp = NULL;
-
-   /* State images are displayed to the left of the Normal image
-    * image number is in state; zero should be `display no image'.
-    * FIXME: that last sentence looks like it needs some checking.
-    */
-   	if (infoPtr->himlState) 
-        himlp=&infoPtr->himlState;
-	imageIndex=wineItem->state>>12;
-	imageIndex++;          /* yeah, right */
-	TRACE ("imindex:%d\n",imageIndex);
-    if ((himlp) && (imageIndex))
-    { 
-	  imageIndex--;       /* see FIXME */
-      ImageList_Draw ( *himlp, imageIndex, hdc, xpos-2, r.top+1, ILD_NORMAL);
-   	  ImageList_GetIconSize (*himlp, &cx, &cy);
-	  wineItem->statebitmap.left=xpos-2;
-	  wineItem->statebitmap.right=xpos-2+cx;
-	  wineItem->statebitmap.top=r.top+1;
-	  wineItem->statebitmap.bottom=r.top+1+cy;
-   	  xpos+=cx;
-    }
-	
-		/* Now, draw the normal image; can be either selected or
-		 * non-selected image. 
-		 */
-
-	himlp=NULL;
-  	if (infoPtr->himlNormal) 
-      himlp=&infoPtr->himlNormal; /* get the image list */
-
-    imageIndex = wineItem->iImage;
-  	if ( (wineItem->state & TVIS_SELECTED) && 
-         (wineItem->iSelectedImage)) { 
-        
-      /* The item is curently selected */
-		  if (wineItem->iSelectedImage == I_IMAGECALLBACK) 
-  			TREEVIEW_SendDispInfoNotify 
-					(hwnd, wineItem, TVN_GETDISPINFOA, TVIF_SELECTEDIMAGE);
-
-      	  imageIndex = wineItem->iSelectedImage;
-	  } else { 
-      /* The item is not selected */
-		  if (wineItem->iImage == I_IMAGECALLBACK) 
-			  TREEVIEW_SendDispInfoNotify 
-					(hwnd, wineItem, TVN_GETDISPINFOA, TVIF_IMAGE);
-
-      imageIndex = wineItem->iImage;
-  	}
- 
-    if (himlp)         
-    { 
-	int ovlIdx = 0;
-
-	if(wineItem->stateMask & TVIS_OVERLAYMASK)
-        	ovlIdx = wineItem->state & TVIS_OVERLAYMASK;
-
-	ImageList_Draw ( *himlp, imageIndex, hdc, xpos-2, r.top+1, ILD_NORMAL|ovlIdx);
-	ImageList_GetIconSize (*himlp, &cx, &cy);
-	wineItem->bitmap.left=xpos-2;
-	wineItem->bitmap.right=xpos-2+cx;
-	wineItem->bitmap.top=r.top+1;
-	wineItem->bitmap.bottom=r.top+1+cy;
-	xpos+=cx;
-    }
-  }
-
-
-  /* 
-   * Display the text associated with this item
-   */
-  r.left=xpos;
-  if ((wineItem->mask & TVIF_TEXT) && (wineItem->pszText)) 
-  {
-    COLORREF oldTextColor = 0;
-    INT      oldBkMode;
-    HBRUSH   hbrBk = 0;
-    BOOL     inFocus = GetFocus() == hwnd;
-
-    TREEVIEW_ITEM tmpItem;
-    char  buf[128];
-
-    if (wineItem->pszText == LPSTR_TEXTCALLBACKA) 
-    {
-        tmpItem.hItem      = wineItem->hItem;
-        tmpItem.state      = wineItem->state;
-        tmpItem.lParam     = wineItem->lParam;
-        tmpItem.pszText    = buf;
-        tmpItem.cchTextMax = sizeof(buf);
-
-        TREEVIEW_SendDispInfoNotify(hwnd, &tmpItem, TVN_GETDISPINFOA, TVIF_TEXT);
-    }
-
-    r.left += 3;
-    r.right -= 3;
-
-    wineItem->text.left  = r.left;  
-    wineItem->text.right = r.right;
-    wineItem->text.top   = r.top;
-    wineItem->text.bottom= r.bottom;
-
-    oldBkMode = SetBkMode(hdc, TRANSPARENT);
-
-    /* - If item is drop target or it is selected and window is in focus -
-     * use blue background (COLOR_HIGHLIGHT).
-     * - If item is selected, window is not in focus, but it has style
-     * TVS_SHOWSELALWAYS - use grey background (COLOR_BTNFACE)
-     * - Otherwise - don't fill background
-     */
-    if ((wineItem->state & TVIS_DROPHILITED) ||
-        ((wineItem->state & TVIS_SELECTED) &&
-        (inFocus || (GetWindowLongA( hwnd, GWL_STYLE) & TVS_SHOWSELALWAYS))))
-    {
-       if ((wineItem->state & TVIS_DROPHILITED) || inFocus)
-       {
-          hbrBk = CreateSolidBrush(GetSysColor( COLOR_HIGHLIGHT));
-          oldTextColor = SetTextColor(hdc, GetSysColor( COLOR_HIGHLIGHTTEXT));
-       }
-       else
-       {
-          hbrBk = CreateSolidBrush(GetSysColor( COLOR_BTNFACE));
-
-          if (infoPtr->clrText == -1)
-             oldTextColor = SetTextColor(hdc, GetSysColor( COLOR_WINDOWTEXT));
-          else
-             oldTextColor = SetTextColor(hdc, infoPtr->clrText);
-       }
-    } 
-    else 
-    {
-       if (infoPtr->clrText == -1)
-          oldTextColor = SetTextColor(hdc, GetSysColor( COLOR_WINDOWTEXT));
-       else
-          oldTextColor = SetTextColor(hdc, infoPtr->clrText);
-    }
-
-    if (wineItem->pszText != LPSTR_TEXTCALLBACKA)
-       tmpItem.pszText = wineItem->pszText;
-
-    /* Obtain the text coordinate */
-    DrawTextA (
-         hdc,
-         tmpItem.pszText,
-         lstrlenA(tmpItem.pszText),
-         &wineItem->text,
-         uTextJustify | DT_VCENTER | DT_SINGLELINE | DT_CALCRECT | DT_NOPREFIX);
-
-    /* We need to reset it to items height */
-    wineItem->text.top = r.top;
-    wineItem->text.bottom = r.bottom;
-    wineItem->text.right += 4; /* This is extra for focus rectangle */
-
-    if (hbrBk)
-    {
-       FillRect(hdc, &wineItem->text, hbrBk);
-       DeleteObject(hbrBk);
-    }
-
-    wineItem->text.left += 2;
-
-    /* Draw it */
-    DrawTextA ( hdc, 
-      tmpItem.pszText, 
-      lstrlenA(tmpItem.pszText), 
-      &wineItem->text, 
-      uTextJustify | DT_VCENTER | DT_SINGLELINE | DT_NOPREFIX); 
-
-    wineItem->text.left -=2;
-
-    /* Restore the hdc state */
-    SetTextColor( hdc, oldTextColor);
-
-    /* Draw the box arround the selected item */
-    if (wineItem->state & TVIS_SELECTED && inFocus) 
-    {
-      HPEN  hNewPen     = CreatePen(PS_DOT, 0, GetSysColor(COLOR_WINDOWTEXT) );
-      HPEN  hOldPen     = SelectObject( hdc, hNewPen );
-      INT   rop         = SetROP2(hdc, R2_XORPEN);
-      POINT points[5];
-
-      points[4].x = points[0].x = wineItem->text.left;
-      points[4].y = points[0].y = wineItem->text.top; 
-      points[1].x = wineItem->text.right-1 ;
-      points[1].y = wineItem->text.top; 
-      points[2].x = wineItem->text.right-1;
-      points[2].y = wineItem->text.bottom-1; 
-      points[3].x = wineItem->text.left;
-      points[3].y = wineItem->text.bottom-1;
-
-      Polyline (hdc,points,5); 
-
-      SetROP2(hdc, rop);
-      DeleteObject(hNewPen);
-      SelectObject(hdc, hOldPen);
-    }
-
-    if (oldBkMode != TRANSPARENT)
-       SetBkMode(hdc, oldBkMode);
-  }
-
-  /* Draw insertion mark if necessary */
-
-  if (infoPtr->insertMarkItem) 
-		TRACE ("item:%d,mark:%d\n", (int)wineItem->hItem,
-                               (int) infoPtr->insertMarkItem);
-  if (wineItem->hItem==infoPtr->insertMarkItem) {
-		HPEN hNewPen, hOldPen;
-		int offset;
-
-		hNewPen = CreatePen(PS_SOLID, 2, infoPtr->clrInsertMark);
-		hOldPen = SelectObject( hdc, hNewPen );
-	
-		if (infoPtr->insertBeforeorAfter)
-			offset=wineItem->text.top+1;
-		else
-			offset=wineItem->text.bottom-1;
-
-		MoveToEx (hdc, wineItem->text.left, offset-3, NULL);
-		LineTo (hdc, wineItem->text.left, offset+3);
-
-		MoveToEx (hdc, wineItem->text.left, offset, NULL);
-		LineTo (hdc, r.right-2, offset);
-
-		MoveToEx (hdc, r.right-2, offset+3, NULL);
-		LineTo (hdc, r.right-2, offset-3);
-
-      	DeleteObject(hNewPen);
-
-      	SelectObject(hdc, hOldPen);
+	if (himlNew != NULL)
+	    ImageList_GetIconSize(himlNew, &infoPtr->stateImageWidth,
+				  &infoPtr->stateImageHeight);
+	else
+	{
+	    infoPtr->stateImageWidth = 0;
+	    infoPtr->stateImageHeight = 0;
 	}
 
-  if (cditem & CDRF_NOTIFYPOSTPAINT) {
-		cditem=TREEVIEW_SendCustomDrawItemNotify 
-                       (hwnd, hdc, wineItem, CDDS_ITEMPOSTPAINT);
-		TRACE("postpaint:cditem-app returns 0x%x\n",cditem);
-	}
+	break;
+    }
 
-  SelectObject (hdc, hOldFont);
-}
-
-static LRESULT
-TREEVIEW_GetItemRect (HWND hwnd, WPARAM wParam, LPARAM lParam)
-{
-  TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
-  TREEVIEW_ITEM *wineItem;
-  HTREEITEM     *iItem;
-  LPRECT        lpRect   = (LPRECT)lParam;
-
-  TRACE("\n");
-  /* 
-   * validate parameters
-   */
-  if (lpRect == NULL)
-    return FALSE;
-
-  if (infoPtr->Timer & TV_REFRESH_TIMER_SET) {
-	InvalidateRect(hwnd, NULL, FALSE);  	
-   }
-
-
-  /* 
-   * retrieve the item ptr
-   */ 
-  iItem = (HTREEITEM *) lParam;
-  wineItem = TREEVIEW_ValidItem (infoPtr, *iItem);
-  if ((!wineItem) || (!wineItem->visible)) 
-    return FALSE;
-
-  /* 
-   * If wParam is TRUE return the text size otherwise return 
-   * the whole item size	
-   */
-  if ((INT) wParam) {
-  	lpRect->left	  = wineItem->text.left;
-    lpRect->right	  = wineItem->text.right;
-    lpRect->bottom	= wineItem->text.bottom;
-    lpRect->top	    = wineItem->text.top;
-  } else {
-    lpRect->left 	  = wineItem->rect.left;
-    lpRect->right	  = wineItem->rect.right;
-    lpRect->bottom  = wineItem->rect.bottom;
-    lpRect->top	    = wineItem->rect.top;
-  }
-
-  TRACE("[L:%d R:%d T:%d B:%d]\n", 
-      lpRect->left,lpRect->right,
-			lpRect->top,lpRect->bottom);
-
-  return TRUE;
-}
-
-static LRESULT
-TREEVIEW_GetVisibleCount (HWND hwnd,  WPARAM wParam, LPARAM lParam)
-
-{
-  TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
-
-  return (LRESULT) infoPtr->uVisibleHeight / infoPtr->uRealItemHeight;
-}
-
-
-
-static LRESULT
-TREEVIEW_SetItemA (HWND hwnd, WPARAM wParam, LPARAM lParam)
-{
-  TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
-  TREEVIEW_ITEM *wineItem;
-  TVITEMEXA *tvItem;
-  INT iItem,len;
-
-  tvItem=(LPTVITEMEXA) lParam;
-  iItem=(INT)tvItem->hItem;
-  TRACE("item %d,mask %x\n",iItem,tvItem->mask);
-
-  wineItem = TREEVIEW_ValidItem (infoPtr, (HTREEITEM)iItem);
-  if (!wineItem) return FALSE;
-
-  if (tvItem->mask & TVIF_CHILDREN) {
-        wineItem->cChildren=tvItem->cChildren;
-  }
-
-  if (tvItem->mask & TVIF_IMAGE) {
-       wineItem->iImage=tvItem->iImage;
-  }
-
-  if (tvItem->mask & TVIF_INTEGRAL) {
-        wineItem->iIntegral=tvItem->iIntegral; 
-  }
-
-  if (tvItem->mask & TVIF_PARAM) {
-        wineItem->lParam=tvItem->lParam;
-  }
-
-  if (tvItem->mask & TVIF_SELECTEDIMAGE) {
-        wineItem->iSelectedImage=tvItem->iSelectedImage;
-  }
-
-  if (tvItem->mask & TVIF_STATE) {
-		TRACE ("prevstate,state,mask:%x,%x,%x\n",wineItem->state,tvItem->state,
-tvItem->stateMask);
-        wineItem->state&= ~tvItem->stateMask;
-		wineItem->state|= (tvItem->state & tvItem->stateMask);
-		wineItem->stateMask|= tvItem->stateMask;  
-  }
-
-  if (tvItem->mask & TVIF_TEXT)
-  {
-    if (tvItem->pszText!=LPSTR_TEXTCALLBACKA)
+    if (oldWidth != infoPtr->normalImageWidth ||
+        oldHeight != infoPtr->normalImageHeight)
     {
-      len=lstrlenA (tvItem->pszText) + 1;
-      if (len>wineItem->cchTextMax)
-      {
-        wineItem->pszText= COMCTL32_ReAlloc (wineItem->pszText, len);
-        wineItem->cchTextMax = len;
-      }
+       TREEVIEW_UpdateSubTree(infoPtr, infoPtr->root);
+       TREEVIEW_UpdateScrollBars(infoPtr);
+    }
 
-      lstrcpynA (wineItem->pszText, tvItem->pszText,len);
+    TREEVIEW_QueueRefresh(infoPtr);
+
+    return (LRESULT)himlOld;
+}
+
+/* Compute the natural height (based on the font size) for items. */
+static UINT
+TREEVIEW_NaturalHeight(TREEVIEW_INFO *infoPtr)
+{
+    TEXTMETRICA tm;
+    HDC hdc = GetDC(0);
+    HFONT hOldFont = SelectObject(hdc, infoPtr->hFont);
+
+    GetTextMetricsA(hdc, &tm);
+
+    SelectObject(hdc, hOldFont);
+    ReleaseDC(0, hdc);
+
+    /* The 16 is a hack because our fonts are tiny. */
+    return max(16, tm.tmHeight + tm.tmExternalLeading);
+}
+
+static LRESULT
+TREEVIEW_SetItemHeight(TREEVIEW_INFO *infoPtr, INT newHeight)
+{
+    INT prevHeight = infoPtr->uItemHeight;
+
+    TRACE("%d \n", newHeight);
+    if (newHeight == -1)
+    {
+	infoPtr->uItemHeight = TREEVIEW_NaturalHeight(infoPtr);
+	infoPtr->bHeightSet = FALSE;
     }
     else
     {
-      if (wineItem->cchTextMax)
-      {
-        COMCTL32_Free (wineItem->pszText);
-	    wineItem->cchTextMax=0;
-      }
-      wineItem->pszText=LPSTR_TEXTCALLBACKA;
+	infoPtr->uItemHeight = newHeight;
+	infoPtr->bHeightSet = TRUE;
     }
-  }
 
-  wineItem->mask |= tvItem->mask;
+    /* Round down, unless we support odd ("non even") heights. */
+    if (!(infoPtr->dwStyle) & TVS_NONEVENHEIGHT)
+	infoPtr->uItemHeight &= ~1;
 
-  return TRUE;
+    if (infoPtr->uItemHeight != prevHeight)
+    {
+	TREEVIEW_RecalculateVisibleOrder(infoPtr, NULL);
+	TREEVIEW_UpdateScrollBars(infoPtr);
+	TREEVIEW_QueueRefresh(infoPtr);
+    }
+
+    return prevHeight;
 }
 
 static LRESULT
-TREEVIEW_GetItemState (HWND hwnd, WPARAM wParam, LPARAM lParam)
-
+TREEVIEW_GetItemHeight(TREEVIEW_INFO *infoPtr)
 {
-    TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
-	TREEVIEW_ITEM *wineItem;
-	
     TRACE("\n");
-  	wineItem = TREEVIEW_ValidItem (infoPtr, (HTREEITEM)wParam);
-	if (!wineItem) return 0;
-	
-	return (wineItem->state & lParam);
+    return infoPtr->uItemHeight;
 }
 
 
-
-
-static void
-TREEVIEW_Refresh (HWND hwnd, HDC hdc)
+static LRESULT
+TREEVIEW_GetFont(TREEVIEW_INFO *infoPtr)
 {
-    TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
-	TEXTMETRICA tm;
-	HBRUSH hbrBk;
-    HFONT hOldFont;
+    TRACE("%x\n", infoPtr->hFont);
+    return infoPtr->hFont;
+}
 
-    RECT rect;
-    INT iItem, indent, x, y, height, itemHeight;
-    INT viewtop,viewbottom,viewleft,viewright;
-    TREEVIEW_ITEM *wineItem, *prevItem;
+
+static INT CALLBACK
+TREEVIEW_ResetTextWidth(LPVOID pItem, DWORD unused)
+{
+    (void)unused;
+
+    ((TREEVIEW_ITEM *)pItem)->textWidth = 0;
+
+    return 1;
+}
+
+static LRESULT
+TREEVIEW_SetFont(TREEVIEW_INFO *infoPtr, HFONT hFont, BOOL bRedraw)
+{
+    UINT uHeight = infoPtr->uItemHeight;
+
+    TRACE("%x %i\n", hFont, bRedraw);
+
+    infoPtr->hFont = hFont ? hFont : GetStockObject(SYSTEM_FONT);
+
+    DeleteObject(infoPtr->hBoldFont);
+    infoPtr->hBoldFont = TREEVIEW_CreateBoldFont(infoPtr->hFont);
+
+    if (!infoPtr->bHeightSet)
+	infoPtr->uItemHeight = TREEVIEW_NaturalHeight(infoPtr);
+
+    if (uHeight != infoPtr->uItemHeight)
+       TREEVIEW_RecalculateVisibleOrder(infoPtr, NULL);
+
+    DPA_EnumCallback(infoPtr->items, TREEVIEW_ResetTextWidth, 0);
+
+    TREEVIEW_UpdateSubTree(infoPtr, infoPtr->root);
+    TREEVIEW_UpdateScrollBars(infoPtr);
+
+    if (bRedraw)
+	TREEVIEW_QueueRefresh(infoPtr);
+
+    return 0;
+}
+
+
+static LRESULT
+TREEVIEW_GetLineColor(TREEVIEW_INFO *infoPtr)
+{
+    TRACE("\n");
+    return (LRESULT)infoPtr->clrLine;
+}
+
+static LRESULT
+TREEVIEW_SetLineColor(TREEVIEW_INFO *infoPtr, COLORREF color)
+{
+    COLORREF prevColor = infoPtr->clrLine;
 
     TRACE("\n");
-
-
-    if (infoPtr->Timer & TV_REFRESH_TIMER_SET) {
-		KillTimer (hwnd, TV_REFRESH_TIMER);
-		infoPtr->Timer &= ~TV_REFRESH_TIMER_SET;
-    }
-
-    
-    GetClientRect (hwnd, &rect);
-    if ((rect.left >= rect.right) || (rect.top >= rect.bottom)) return;
-
-    infoPtr->cdmode=TREEVIEW_SendCustomDrawNotify(hwnd,CDDS_PREPAINT,hdc,rect);
-
-    if (infoPtr->cdmode==CDRF_SKIPDEFAULT) return;
-
-    infoPtr->uVisibleHeight= rect.bottom-rect.top + 1;
-    infoPtr->uVisibleWidth= rect.right-rect.left + 1;
-
-    viewtop=infoPtr->cy;
-    viewbottom=infoPtr->cy + rect.bottom-rect.top;
-    viewleft=infoPtr->cx;
-    viewright=infoPtr->cx + rect.right-rect.left;
-
-    TRACE("[%d %d %d %d]\n",viewtop,viewbottom,viewleft,viewright);
-
-    /* draw background */
-    
-    hbrBk = CreateSolidBrush (infoPtr->clrBk);
-    FillRect(hdc, &rect, hbrBk);
-    DeleteObject(hbrBk);
-
-    ImageList_GetIconSize (infoPtr->himlNormal, &x, &itemHeight);
-    if (infoPtr->uItemHeight>itemHeight)
-	itemHeight=infoPtr->uItemHeight;
-
-    // assume that bold and normal fonts have same height
-    hOldFont = SelectObject (hdc, infoPtr->hBoldFont);
-    GetTextMetricsA (hdc, &tm);
-    if ((tm.tmHeight + tm.tmExternalLeading + FOCUS_BORDER) > itemHeight)
-	 itemHeight=tm.tmHeight + tm.tmExternalLeading + FOCUS_BORDER;
-    SelectObject (hdc, hOldFont);
-
-    infoPtr->uRealItemHeight=itemHeight;
-
-    iItem=(INT)infoPtr->TopRootItem;
-    infoPtr->firstVisible=0;
-    wineItem=NULL;
-    indent=0;
-    x=y=0;
-
-    while (iItem) {
-	prevItem=wineItem;
-        wineItem= & infoPtr->items[iItem];
-	wineItem->iLevel=indent;
-
-/* FIXME: remove this in later stage  */
-/*
-		if (wineItem->pszText!=LPSTR_TEXTCALLBACK32A) 
-		TRACE (treeview, "%d %d [%d %d %d %d] (%s)\n",y,x,
-			wineItem->rect.top, wineItem->rect.bottom,
-			wineItem->rect.left, wineItem->rect.right,
-			wineItem->pszText);
-		else 
-		TRACE (treeview, "%d [%d %d %d %d] (CALLBACK)\n",
-				wineItem->hItem,
-				wineItem->rect.top, wineItem->rect.bottom,
-				wineItem->rect.left, wineItem->rect.right);
-*/
-
-		height=itemHeight * wineItem->iIntegral; 
-		if ((y >= viewtop) && (y <= viewbottom) &&
-	    	(x >= viewleft  ) && (x <= viewright)) {
-				wineItem->visible = TRUE;
-        		wineItem->rect.top = y - infoPtr->cy + rect.top;
-        		wineItem->rect.bottom = wineItem->rect.top + height-1;
-         		wineItem->rect.left = x - infoPtr->cx + rect.left;
-        		wineItem->rect.right = rect.right;
-			if (!infoPtr->firstVisible)
-				infoPtr->firstVisible=wineItem->hItem;
-       		TREEVIEW_DrawItem (hwnd, hdc, wineItem);
-		}
-		else {
-			wineItem->visible   = FALSE;
-			wineItem->rect.left = wineItem->rect.top    = 0;
-			wineItem->rect.right= wineItem->rect.bottom = 0;
-			wineItem->text.left = wineItem->text.top    = 0;
-			wineItem->text.right= wineItem->text.bottom = 0;
- 		}
-
-		/* look up next item */
-	
-		if ((wineItem->firstChild) && (wineItem->state & TVIS_EXPANDED)) {
-			iItem=(INT)wineItem->firstChild;
-			indent++;
-			x+=infoPtr->uIndent;
-			if (x>infoPtr->uTotalWidth) 	
-				infoPtr->uTotalWidth=x;
-		}
-		else {
-			iItem=(INT)wineItem->sibling;
-			while ((!iItem) && (indent>0)) {
-				indent--;
-				x-=infoPtr->uIndent;
-				wineItem=&infoPtr->items[(INT)wineItem->parent];
-				iItem=(INT)wineItem->sibling;
-			}
-		}
-        y +=height;
-    }				/* while */
-
-/* FIXME: infoPtr->uTotalWidth should also take item label into account */
-/* FIXME: or should query item sizes (ie check CDRF_NEWFONT) */
-
-    infoPtr->uTotalHeight=y;
-    if (y >= (viewbottom-viewtop)) {
- 		if (!(infoPtr->uInternalStatus & TV_VSCROLL))
-			ShowScrollBar (hwnd, SB_VERT, TRUE);
-		infoPtr->uInternalStatus |=TV_VSCROLL;
- 		SetScrollRange (hwnd, SB_VERT, 0, 
-					y - infoPtr->uVisibleHeight, FALSE);
-		SetScrollPos (hwnd, SB_VERT, infoPtr->cy, TRUE);
-	}
-    else {
-		if (infoPtr->uInternalStatus & TV_VSCROLL) 
-			ShowScrollBar (hwnd, SB_VERT, FALSE);
-		infoPtr->uInternalStatus &= ~TV_VSCROLL;
-	}
-
-
-	if (infoPtr->cdmode & CDRF_NOTIFYPOSTPAINT) 
-    	infoPtr->cdmode=TREEVIEW_SendCustomDrawNotify 
-								(hwnd, CDDS_POSTPAINT, hdc, rect);
-
-    TRACE("done\n");
-}
-
-
-static LRESULT 
-TREEVIEW_HandleTimer (HWND hwnd, WPARAM wParam, LPARAM lParam)
-{
-  TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
-
-  TRACE(" %d\n",wParam);
-
-  switch (wParam) {
-	case TV_REFRESH_TIMER:
-		KillTimer (hwnd, TV_REFRESH_TIMER);
-		infoPtr->Timer &= ~TV_REFRESH_TIMER_SET;
-		InvalidateRect(hwnd, NULL, FALSE);
-		return 0;
-	case TV_EDIT_TIMER:
-		KillTimer (hwnd, TV_EDIT_TIMER);
-		infoPtr->Timer &= ~TV_EDIT_TIMER_SET;
-		return 0;
-	default:
-		ERR("got unknown timer\n");
- }
-		
- return 1;
-}
-
-
-static void
-TREEVIEW_QueueRefresh (HWND hwnd)
-
-{
- TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
-
- TRACE("\n");
- if (infoPtr->Timer & TV_REFRESH_TIMER_SET) {
-	KillTimer (hwnd, TV_REFRESH_TIMER);
- }
-
- SetTimer (hwnd, TV_REFRESH_TIMER, TV_REFRESH_DELAY, 0);
- infoPtr->Timer|=TV_REFRESH_TIMER_SET;
-}
-
-
-
-static LRESULT
-TREEVIEW_GetItemA (HWND hwnd, WPARAM wParam, LPARAM lParam)
-{
-  TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
-  LPTVITEMEXA    tvItem;
-  TREEVIEW_ITEM *wineItem;
-  INT         iItem;
-
-  tvItem=(LPTVITEMEXA) lParam;
-  iItem=(INT)tvItem->hItem;
-
-  wineItem = TREEVIEW_ValidItem (infoPtr, (HTREEITEM)iItem);
-  if (!wineItem) return FALSE;
-
-   if (tvItem->mask & TVIF_CHILDREN) {
-		if (TVIF_CHILDREN==I_CHILDRENCALLBACK) 
-			FIXME("I_CHILDRENCALLBACK not supported\n");
-        tvItem->cChildren=wineItem->cChildren;
-   }
-
-   if (tvItem->mask & TVIF_HANDLE) {
-        tvItem->hItem=wineItem->hItem;
-   }
-
-   if (tvItem->mask & TVIF_IMAGE) {
-        tvItem->iImage=wineItem->iImage;
-   }
-
-   if (tvItem->mask & TVIF_INTEGRAL) {
-        tvItem->iIntegral=wineItem->iIntegral; 
-   }
-
-   /* undocumented: windows ignores TVIF_PARAM and
-	 * always sets lParam
-	 */
-   tvItem->lParam=wineItem->lParam;
-
-   if (tvItem->mask & TVIF_SELECTEDIMAGE) {
-        tvItem->iSelectedImage=wineItem->iSelectedImage;
-   }
-
-   if (tvItem->mask & TVIF_STATE) {
-        tvItem->state=wineItem->state & tvItem->stateMask;
-   }
-
-   if (tvItem->mask & TVIF_TEXT) {
-	if (wineItem->pszText == LPSTR_TEXTCALLBACKA) {
-	    tvItem->pszText = LPSTR_TEXTCALLBACKA;  /* FIXME:send notification? */
-		ERR(" GetItem called with LPSTR_TEXTCALLBACK\n");
-	}
-	else if (wineItem->pszText) {
-	    lstrcpynA (tvItem->pszText, wineItem->pszText, tvItem->cchTextMax);
-	}
-   }
-
-  TRACE("item %d<%p>, txt %p, img %p, action %x\n", 
-    iItem, tvItem, tvItem->pszText, &tvItem->iImage, tvItem->mask);
-
-  return TRUE;
+    infoPtr->clrLine = color;
+    return (LRESULT)prevColor;
 }
 
 
 static LRESULT
-TREEVIEW_GetItemW (HWND hwnd, WPARAM wParam, LPARAM lParam)
+TREEVIEW_GetTextColor(TREEVIEW_INFO *infoPtr)
 {
-  TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
-  LPTVITEMEXA    tvItem;
-  TREEVIEW_ITEM *wineItem;
-  INT         iItem;
-
-  tvItem=(LPTVITEMEXA) lParam;
-  iItem=(INT)tvItem->hItem;
-
-  wineItem = TREEVIEW_ValidItem (infoPtr, (HTREEITEM)iItem);
-  if (!wineItem) return FALSE;
-
-   if (tvItem->mask & TVIF_CHILDREN) {
-		if (TVIF_CHILDREN==I_CHILDRENCALLBACK)
-			FIXME("I_CHILDRENCALLBACK not supported\n");
-        tvItem->cChildren=wineItem->cChildren;
-   }
-
-   if (tvItem->mask & TVIF_HANDLE) {
-        tvItem->hItem=wineItem->hItem;
-   }
-
-   if (tvItem->mask & TVIF_IMAGE) {
-        tvItem->iImage=wineItem->iImage;
-   }
-
-   if (tvItem->mask & TVIF_INTEGRAL) {
-        tvItem->iIntegral=wineItem->iIntegral;
-   }
-
-   /* undocumented: windows ignores TVIF_PARAM and
-	 * always sets lParam
-	 */
-   tvItem->lParam=wineItem->lParam;
-
-   if (tvItem->mask & TVIF_SELECTEDIMAGE) {
-        tvItem->iSelectedImage=wineItem->iSelectedImage;
-   }
-
-   if (tvItem->mask & TVIF_STATE) {
-        tvItem->state=wineItem->state & tvItem->stateMask;
-   }
-
-#if 0
-   if (tvItem->mask & TVIF_TEXT) {
-	if (wineItem->pszText == LPSTR_TEXTCALLBACKW) {
-	    tvItem->pszText = LPSTR_TEXTCALLBACKW;  /* FIXME:send notification? */
-		ERR(" GetItem called with LPSTR_TEXTCALLBACK\n");
-	}
-	else if (wineItem->pszText) {
-	    lstrcpynAtoW (tvItem->pszText, wineItem->pszText, tvItem->cchTextMax);
-	}
-   }
-#endif
-  wineItem->pszText = NULL;
-
-  TRACE("item %d<%p>, txt %p, img %p, action %x\n",
-    iItem, tvItem, tvItem->pszText, &tvItem->iImage, tvItem->mask);
-
-  return TRUE;
+    TRACE("\n");
+    return (LRESULT)infoPtr->clrText;
 }
 
-
-
-/* FIXME: check implementation of TVGN_NEXT/TVGN_NEXTVISIBLE */
-
 static LRESULT
-TREEVIEW_GetNextItem (HWND hwnd, WPARAM wParam, LPARAM lParam)
-
+TREEVIEW_SetTextColor(TREEVIEW_INFO *infoPtr, COLORREF color)
 {
-  TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
-  TREEVIEW_ITEM *wineItem, *returnItem;
-  INT iItem = (INT)lParam, retval = 0, flag  = (INT)wParam;
+    COLORREF prevColor = infoPtr->clrText;
 
-  switch (flag) {
-  case TVGN_ROOT:
-    retval = (INT)infoPtr->TopRootItem;
-    break;
+    TRACE("\n");
+    infoPtr->clrText = color;
 
-  case TVGN_CARET:
-    retval = (INT)infoPtr->selectedItem;
-    break;
+    if (infoPtr->clrText != prevColor)
+	TREEVIEW_QueueRefresh(infoPtr);
 
-  case TVGN_FIRSTVISIBLE: /* FIXME:we should only recalculate, not redraw */
-    InvalidateRect(hwnd, NULL, FALSE);
-    retval = (INT)infoPtr->firstVisible;
-    break;
-
-  case TVGN_DROPHILITE:
-    retval = (INT)infoPtr->dropItem;
-    break;
- 
-  case TVGN_NEXT:
-    wineItem = TREEVIEW_ValidItem (infoPtr, (HTREEITEM)iItem);
-    retval = wineItem ? (INT)wineItem->sibling : 0;
-    break;
-
-  case TVGN_PREVIOUS:	
-    wineItem = TREEVIEW_ValidItem (infoPtr, (HTREEITEM)iItem);
-    retval = wineItem ? (INT)wineItem->upsibling : 0;
-    break;
-
-  case TVGN_PARENT:
-    wineItem = TREEVIEW_ValidItem (infoPtr, (HTREEITEM)iItem);
-    retval = wineItem ? (INT)wineItem->parent : 0;
-    break;
-
-  case TVGN_CHILD:
-    wineItem = TREEVIEW_ValidItem (infoPtr, (HTREEITEM)iItem);
-    retval = wineItem ? (INT)wineItem->firstChild : 0;
-    break;
-
-  case TVGN_LASTVISIBLE:  
-    if((wineItem = TREEVIEW_ValidItem (infoPtr, (HTREEITEM)iItem))) {
-      returnItem = TREEVIEW_GetLastListItem (infoPtr,wineItem);
-      retval = returnItem ? (INT)returnItem->hItem : 0;
-    }
-    break;
-
-  case TVGN_NEXTVISIBLE:  
-    if((wineItem = TREEVIEW_ValidItem (infoPtr, (HTREEITEM)iItem))) {
-      returnItem = TREEVIEW_GetNextListItem (infoPtr,wineItem);
-      retval = returnItem ? (INT)returnItem->hItem : 0;
-    }
-    break;
-
-  case TVGN_PREVIOUSVISIBLE: 
-    if((wineItem = TREEVIEW_ValidItem (infoPtr, (HTREEITEM)iItem))) {
-      returnItem = TREEVIEW_GetPrevListItem (infoPtr, wineItem);
-      retval = returnItem ? (INT)returnItem->hItem : 0;
-    }
-    break;
-
-  default:
-    FIXME("Unknown msg %x,item %x\n", flag,iItem);
-    break;
-  }
-
-  TRACE("flags %x, item %d returns %d\n", flag, iItem, retval);
-  return retval;
+    return (LRESULT)prevColor;
 }
 
 
 static LRESULT
-TREEVIEW_GetCount (HWND hwnd, WPARAM wParam, LPARAM lParam)
+TREEVIEW_GetBkColor(TREEVIEW_INFO *infoPtr)
 {
- TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
-
- TRACE(" %d\n",infoPtr->uNumItems);
- return (LRESULT) infoPtr->uNumItems;
+    TRACE("\n");
+    return (LRESULT)infoPtr->clrBk;
 }
 
-/***************************************************************************
- * This method does the chaining of the insertion of a treeview item 
- * before an item.
- * If parent is NULL, we're inserting at the root of the list.
+static LRESULT
+TREEVIEW_SetBkColor(TREEVIEW_INFO *infoPtr, COLORREF newColor)
+{
+    COLORREF prevColor = infoPtr->clrBk;
+
+    TRACE("\n");
+    infoPtr->clrBk = newColor;
+
+    if (newColor != prevColor)
+	TREEVIEW_QueueRefresh(infoPtr);
+
+    return (LRESULT)prevColor;
+}
+
+
+static LRESULT
+TREEVIEW_GetInsertMarkColor(TREEVIEW_INFO *infoPtr)
+{
+    TRACE("\n");
+    return (LRESULT)infoPtr->clrInsertMark;
+}
+
+static LRESULT
+TREEVIEW_SetInsertMarkColor(TREEVIEW_INFO *infoPtr, COLORREF color)
+{
+    COLORREF prevColor = infoPtr->clrInsertMark;
+
+    TRACE("%lx\n", color);
+    infoPtr->clrInsertMark = color;
+
+    return (LRESULT)prevColor;
+}
+
+
+static LRESULT
+TREEVIEW_SetInsertMark(TREEVIEW_INFO *infoPtr, BOOL wParam, HTREEITEM item)
+{
+    TRACE("%d %p\n", wParam, item);
+
+    if (!TREEVIEW_ValidItem(infoPtr, item))
+	return 0;
+
+    infoPtr->insertBeforeorAfter = wParam;
+    infoPtr->insertMarkItem = item;
+
+    TREEVIEW_QueueRefresh(infoPtr);
+
+    return 1;
+}
+
+
+/************************************************************************
+ * Some serious braindamage here. lParam is a pointer to both the
+ * input HTREEITEM and the output RECT.
  */
-static void TREEVIEW_InsertBefore(
-    TREEVIEW_INFO *infoPtr,
-    TREEVIEW_ITEM *newItem, 
-    TREEVIEW_ITEM *sibling,
-    TREEVIEW_ITEM *parent)
+static LRESULT
+TREEVIEW_GetItemRect(TREEVIEW_INFO *infoPtr, BOOL fTextRect, LPRECT lpRect)
 {
-  HTREEITEM     siblingHandle   = 0;
-  HTREEITEM     upSiblingHandle = 0;
-  TREEVIEW_ITEM *upSibling      = NULL;
+    TREEVIEW_ITEM *wineItem;
+    const HTREEITEM *pItem = (HTREEITEM *)lpRect;
 
-  if (newItem == NULL)
-    ERR("NULL newItem, impossible condition\n");
-
-  if (sibling != NULL) /* Insert before this sibling for this parent */
-  { 
-    /* Store the new item sibling up sibling and sibling tem handle */
-    siblingHandle   = sibling->hItem;
-    upSiblingHandle = sibling->upsibling;
-    /* As well as a pointer to the upsibling sibling object */
-    if ( (INT)sibling->upsibling != 0 )
-      upSibling = &infoPtr->items[(INT)sibling->upsibling];
-  
-    /* Adjust the sibling pointer */
-    sibling->upsibling = newItem->hItem;
-    
-    /* Adjust the new item pointers */
-    newItem->upsibling = upSiblingHandle;
-    newItem->sibling   = siblingHandle;
-    
-    /* Adjust the up sibling pointer */
-    if ( upSibling != NULL )        
-      upSibling->sibling = newItem->hItem;
-    else
-      /* this item is the first child of this parent, adjust parent pointers */
-	  if (parent)
-      	parent->firstChild = newItem->hItem;
-	  else 
-		infoPtr->TopRootItem= newItem->hItem;
-  }
-  else /* Insert as first child of this parent */
-	if (parent)
-    	parent->firstChild = newItem->hItem;
-}
-
-/***************************************************************************
- * This method does the chaining of the insertion of a treeview item 
- * after an item.
- * If parent is NULL, we're inserting at the root of the list.
- */
-static void TREEVIEW_InsertAfter(
-    TREEVIEW_INFO *infoPtr,
-    TREEVIEW_ITEM *newItem, 
-    TREEVIEW_ITEM *upSibling,
-    TREEVIEW_ITEM *parent)
-{
-  HTREEITEM     upSiblingHandle = 0;
-  HTREEITEM     siblingHandle   = 0;
-  TREEVIEW_ITEM *sibling        = NULL;
-
-
-  if (newItem == NULL)
-    ERR("NULL newItem, impossible condition\n");
-
-  if (upSibling != NULL) /* Insert after this upsibling for this parent */
-  { 
-    /* Store the new item up sibling and sibling item handle */
-    upSiblingHandle = upSibling->hItem;
-    siblingHandle   = upSibling->sibling;
-    /* As well as a pointer to the upsibling sibling object */
-    if ( (INT)upSibling->sibling != 0 )
-      sibling = &infoPtr->items[(INT)upSibling->sibling];
-  
-    /* Adjust the up sibling pointer */
-    upSibling->sibling = newItem->hItem;
-    
-    /* Adjust the new item pointers */
-    newItem->upsibling = upSiblingHandle;
-    newItem->sibling   = siblingHandle;
-    
-    /* Adjust the sibling pointer */
-    if ( sibling != NULL )        
-      sibling->upsibling = newItem->hItem; 
+    TRACE("\n");
     /*
-    else 
-      newItem is the last of the level, nothing else to do 
-    */
-  }
-  else /* Insert as first child of this parent */
-	if (parent)
-    	parent->firstChild = newItem->hItem;
+     * validate parameters
+     */
+    if (pItem == NULL)
+	return FALSE;
+
+    wineItem = *pItem;
+    if (!TREEVIEW_ValidItem(infoPtr, wineItem) || !ISVISIBLE(wineItem))
+	return FALSE;
+
+    /* 
+     * If wParam is TRUE return the text size otherwise return 
+     * the whole item size        
+     */
+    if (fTextRect)
+    {
+	/* Windows does not send TVN_GETDISPINFO here. */
+
+	lpRect->top = wineItem->rect.top;
+	lpRect->bottom = wineItem->rect.bottom;
+
+	lpRect->left = wineItem->textOffset;
+	lpRect->right = wineItem->textOffset + wineItem->textWidth;
+    }
+    else
+    {
+	*lpRect = wineItem->rect;
+    }
+
+    TRACE("%s [L:%d R:%d T:%d B:%d]\n", fTextRect ? "text" : "item",
+	  lpRect->left, lpRect->right, lpRect->top, lpRect->bottom);
+
+    return TRUE;
 }
 
+static inline LRESULT
+TREEVIEW_GetVisibleCount(TREEVIEW_INFO *infoPtr)
+{
+    /* Suprise! This does not take integral height into account. */
+    return infoPtr->clientHeight / infoPtr->uItemHeight;
+}
+
+
+static LRESULT
+TREEVIEW_GetItemA(TREEVIEW_INFO *infoPtr, LPTVITEMEXA tvItem)
+{
+    TREEVIEW_ITEM *wineItem;
+
+    wineItem = tvItem->hItem;
+    if (!TREEVIEW_ValidItem(infoPtr, wineItem))
+	return FALSE;
+
+    TREEVIEW_UpdateDispInfo(infoPtr, wineItem, tvItem->mask);
+
+    if (tvItem->mask & TVIF_CHILDREN)
+	tvItem->cChildren = wineItem->cChildren;
+
+    if (tvItem->mask & TVIF_HANDLE)
+	tvItem->hItem = wineItem;
+
+    if (tvItem->mask & TVIF_IMAGE)
+	tvItem->iImage = wineItem->iImage;
+
+    if (tvItem->mask & TVIF_INTEGRAL)
+	tvItem->iIntegral = wineItem->iIntegral;
+
+    /* undocumented: windows ignores TVIF_PARAM and
+     * * always sets lParam
+     */
+    tvItem->lParam = wineItem->lParam;
+
+    if (tvItem->mask & TVIF_SELECTEDIMAGE)
+	tvItem->iSelectedImage = wineItem->iSelectedImage;
+
+    if (tvItem->mask & TVIF_STATE)
+	tvItem->state = wineItem->state & tvItem->stateMask;
+
+    if (tvItem->mask & TVIF_TEXT)
+	lstrcpynA(tvItem->pszText, wineItem->pszText, tvItem->cchTextMax);
+
+    TRACE("item <%p>, txt %p, img %p, mask %x\n",
+	  wineItem, tvItem->pszText, &tvItem->iImage, tvItem->mask);
+
+    return TRUE;
+}
+
+/* Beware MSDN Library Visual Studio 6.0. It says -1 on failure, 0 on success,
+ * which is wrong. */
+static LRESULT
+TREEVIEW_SetItemA(TREEVIEW_INFO *infoPtr, LPTVITEMEXA tvItem)
+{
+    TREEVIEW_ITEM *wineItem;
+
+    wineItem = tvItem->hItem;
+    TRACE("item %d,mask %x\n", TREEVIEW_GetItemIndex(infoPtr, wineItem),
+	  tvItem->mask);
+
+    if (!TREEVIEW_ValidItem(infoPtr, wineItem))
+	return FALSE;
+
+    if (!TREEVIEW_DoSetItem(infoPtr, wineItem, tvItem))
+	return FALSE;
+
+    /* If the text or TVIS_BOLD was changed, and it is visible, recalculate. */
+    if ((tvItem->mask & TVIF_TEXT
+	 || (tvItem->mask & TVIF_STATE && tvItem->stateMask & TVIS_BOLD))
+	&& ISVISIBLE(wineItem))
+    {
+	TREEVIEW_UpdateDispInfo(infoPtr, wineItem, TVIF_TEXT);
+	TREEVIEW_ComputeTextWidth(infoPtr, wineItem, 0);
+    }
+
+    if (tvItem->mask != 0 && ISVISIBLE(wineItem))
+    {
+	/* The refresh updates everything, but we can't wait until then. */
+	TREEVIEW_ComputeItemInternalMetrics(infoPtr, wineItem);
+
+	if (tvItem->mask & TVIF_INTEGRAL)
+	{
+	    TREEVIEW_RecalculateVisibleOrder(infoPtr, wineItem);
+	    TREEVIEW_UpdateScrollBars(infoPtr);
+
+	    TREEVIEW_QueueRefresh(infoPtr);
+	}
+	else
+	{
+	    TREEVIEW_UpdateScrollBars(infoPtr);
+	    TREEVIEW_QueueItemRefresh(infoPtr, wineItem);
+	}
+    }
+
+    return TRUE;
+}
+
+static LRESULT
+TREEVIEW_GetItemW(TREEVIEW_INFO *infoPtr, LPTVITEMEXA tvItem)
+{
+    TREEVIEW_ITEM *wineItem;
+    INT         iItem;
+    iItem = (INT)tvItem->hItem;
+
+    wineItem = tvItem->hItem;
+    if(!TREEVIEW_ValidItem (infoPtr, wineItem))
+        return FALSE;
+
+    TREEVIEW_UpdateDispInfo(infoPtr, wineItem, tvItem->mask);
+
+    if (tvItem->mask & TVIF_CHILDREN) {
+        if (TVIF_CHILDREN==I_CHILDRENCALLBACK)
+            FIXME("I_CHILDRENCALLBACK not supported\n");
+        tvItem->cChildren = wineItem->cChildren;
+    }
+
+    if (tvItem->mask & TVIF_HANDLE) {
+        tvItem->hItem = wineItem;
+    }
+    if (tvItem->mask & TVIF_IMAGE) {
+        tvItem->iImage = wineItem->iImage;
+    }
+    if (tvItem->mask & TVIF_INTEGRAL) {
+        tvItem->iIntegral = wineItem->iIntegral;
+    }
+    /* undocumented: windows ignores TVIF_PARAM and
+     * always sets lParam           */
+    tvItem->lParam = wineItem->lParam;
+    if (tvItem->mask & TVIF_SELECTEDIMAGE) {
+        tvItem->iSelectedImage = wineItem->iSelectedImage;
+    }
+    if (tvItem->mask & TVIF_STATE) {
+        tvItem->state = wineItem->state & tvItem->stateMask;
+    }
+#if 0
+    if (tvItem->mask & TVIF_TEXT) {
+        if (wineItem->pszText == LPSTR_TEXTCALLBACKW) {
+            tvItem->pszText = LPSTR_TEXTCALLBACKW;  /* FIXME:send notification? */
+            ERR(" GetItem called with LPSTR_TEXTCALLBACK\n");
+        }
+        else if (wineItem->pszText) {
+            lstrcpynAtoW(tvItem->pszText, wineItem->pszText, tvItem->cchTextMax);
+        }
+    }
+#endif
+    wineItem->pszText = NULL;
+    TRACE("item %d<%p>, txt %p, img %p, action %x\n",
+        iItem, tvItem, tvItem->pszText, &tvItem->iImage, tvItem->mask);
+    return TRUE;
+}
+
+static LRESULT
+TREEVIEW_GetItemState(TREEVIEW_INFO *infoPtr, HTREEITEM wineItem, UINT mask)
+{
+    TRACE("\n");
+
+    if (!wineItem || !TREEVIEW_ValidItem(infoPtr, wineItem))
+	return 0;
+
+    return (wineItem->state & mask);
+}
+
+static LRESULT
+TREEVIEW_GetNextItem(TREEVIEW_INFO *infoPtr, UINT which, HTREEITEM wineItem)
+{
+    TREEVIEW_ITEM *retval;
+
+    retval = 0;
+
+    /* handle all the global data here */
+    switch (which)
+    {
+    case TVGN_CHILD:		/* Special case: child of 0 is root */
+	if (wineItem)
+	    break;
+	/* fall through */
+    case TVGN_ROOT:
+	retval = infoPtr->root->firstChild;
+	break;
+
+    case TVGN_CARET:
+	retval = infoPtr->selectedItem;
+	break;
+
+    case TVGN_FIRSTVISIBLE:
+	retval = infoPtr->firstVisible;
+	break;
+
+    case TVGN_DROPHILITE:
+	retval = infoPtr->dropItem;
+	break;
+
+    case TVGN_LASTVISIBLE:
+	retval = TREEVIEW_GetLastListItem(infoPtr, infoPtr->root);
+	break;
+    }
+
+    if (retval)
+    {
+	TRACE("flags:%x, returns %p\n", which, retval);
+	return (LRESULT)retval;
+    }
+
+    if (!TREEVIEW_ValidItem(infoPtr, wineItem))
+	return FALSE;
+
+    switch (which)
+    {
+    case TVGN_NEXT:
+	retval = wineItem->nextSibling;
+	break;
+    case TVGN_PREVIOUS:
+	retval = wineItem->prevSibling;
+	break;
+    case TVGN_PARENT:
+	retval = (wineItem->parent != infoPtr->root) ? wineItem->parent : NULL;
+	break;
+    case TVGN_CHILD:
+	retval = wineItem->firstChild;
+	break;
+    case TVGN_NEXTVISIBLE:
+	retval = TREEVIEW_GetNextListItem(infoPtr, wineItem);
+	break;
+    case TVGN_PREVIOUSVISIBLE:
+	retval = TREEVIEW_GetPrevListItem(infoPtr, wineItem);
+	break;
+    default:
+	TRACE("Unknown msg %x,item %p\n", which, wineItem);
+	break;
+    }
+
+    TRACE("flags:%x, item %p;returns %p\n", which, wineItem, retval);
+    return (LRESULT)retval;
+}
+
+
+static LRESULT
+TREEVIEW_GetCount(TREEVIEW_INFO *infoPtr)
+{
+    TRACE(" %d\n", infoPtr->uNumItems);
+    return (LRESULT)infoPtr->uNumItems;
+}
+
+static VOID
+TREEVIEW_ToggleItemState(TREEVIEW_INFO *infoPtr, TREEVIEW_ITEM *item)
+{
+    if (infoPtr->dwStyle & TVS_CHECKBOXES)
+    {
+	static const unsigned int state_table[] = { 0, 2, 1 };
+
+	unsigned int state;
+
+	state = STATEIMAGEINDEX(item->state);
+	TRACE("state:%x\n", state);
+	item->state &= ~TVIS_STATEIMAGEMASK;
+
+	if (state < 3)
+	    state = state_table[state];
+
+	item->state |= INDEXTOSTATEIMAGEMASK(state);
+
+	TRACE("state:%x\n", state);
+	TREEVIEW_QueueItemRefresh(infoPtr, item);
+    }
+}
+
+
+/* Painting *************************************************************/
+
+/* Draw the lines and expand button for an item. Also draws one section
+ * of the line from item's parent to item's parent's next sibling. */
+static void
+TREEVIEW_DrawItemLines(TREEVIEW_INFO *infoPtr, HDC hdc, TREEVIEW_ITEM *item)
+{
+    LONG centerx, centery;
+    BOOL lar = ((infoPtr->dwStyle
+		 & (TVS_LINESATROOT|TVS_HASLINES|TVS_HASBUTTONS))
+		> TVS_LINESATROOT);
+
+    if (!lar && item->iLevel == 0)
+	return;
+
+    centerx = (item->linesOffset + item->stateOffset) / 2;
+    centery = (item->rect.top + item->rect.bottom) / 2;
+
+    if (infoPtr->dwStyle & TVS_HASLINES)
+    {
+	HPEN hOldPen, hNewPen;
+	HTREEITEM parent;
+
+	/* 
+	 * Get a dotted grey pen
+	 */
+	hNewPen = CreatePen(PS_DOT, 0, infoPtr->clrLine);
+	hOldPen = SelectObject(hdc, hNewPen);
+
+	MoveToEx(hdc, item->stateOffset, centery, NULL);
+	LineTo(hdc, centerx - 1, centery);
+
+	if (item->prevSibling || item->parent != infoPtr->root)
+	{
+	    MoveToEx(hdc, centerx, item->rect.top, NULL);
+	    LineTo(hdc, centerx, centery);
+	}
+
+	if (item->nextSibling)
+	{
+	    MoveToEx(hdc, centerx, centery, NULL);
+	    LineTo(hdc, centerx, item->rect.bottom + 1);
+	}
+
+	/* Draw the line from our parent to its next sibling. */
+	parent = item->parent;
+	while (parent != infoPtr->root)
+	{
+	    int pcenterx = (parent->linesOffset + parent->stateOffset) / 2;
+
+	    if (parent->nextSibling
+		/* skip top-levels unless TVS_LINESATROOT */
+		&& parent->stateOffset > parent->linesOffset)
+	    {
+		MoveToEx(hdc, pcenterx, item->rect.top, NULL);
+		LineTo(hdc, pcenterx, item->rect.bottom + 1);
+	    }
+
+	    parent = parent->parent;
+	}
+
+	SelectObject(hdc, hOldPen);
+	DeleteObject(hNewPen);
+    }
+
+    /* 
+     * Display the (+/-) signs
+     */
+
+    if (infoPtr->dwStyle & TVS_HASBUTTONS)
+    {
+	if (item->cChildren)
+	{
+	    LONG height = item->rect.bottom - item->rect.top;
+	    LONG width  = item->stateOffset - item->linesOffset;
+	    LONG rectsize = min(height, width) / 4;
+	    /* plussize = ceil(rectsize * 3/4) */
+	    LONG plussize = (rectsize + 1) * 3 / 4;
+
+	    HPEN hNewPen  = CreatePen(PS_SOLID, 0, infoPtr->clrLine);
+	    HPEN hOldPen  = SelectObject(hdc, hNewPen);
+	    HBRUSH hbr    = CreateSolidBrush(infoPtr->clrBk);
+	    HBRUSH hbrOld = SelectObject(hdc, hbr);
+
+	    Rectangle(hdc, centerx - rectsize, centery - rectsize,
+		      centerx + rectsize + 1, centery + rectsize + 1);
+
+	    SelectObject(hdc, hbrOld);
+	    DeleteObject(hbr);
+
+	    SelectObject(hdc, hOldPen);
+	    DeleteObject(hNewPen);
+
+	    MoveToEx(hdc, centerx - plussize + 1, centery, NULL);
+	    LineTo(hdc, centerx + plussize, centery);
+
+	    if (!(item->state & TVIS_EXPANDED))
+	    {
+		MoveToEx(hdc, centerx, centery - plussize + 1, NULL);
+		LineTo(hdc, centerx, centery + plussize);
+	    }
+	}
+    }
+}
+
+static void
+TREEVIEW_DrawItem(TREEVIEW_INFO *infoPtr, HDC hdc, TREEVIEW_ITEM *wineItem)
+{
+    INT cditem;
+    HFONT hOldFont;
+    int centery;
+
+    hOldFont = SelectObject(hdc, TREEVIEW_FontForItem(infoPtr, wineItem));
+
+    TREEVIEW_UpdateDispInfo(infoPtr, wineItem, CALLBACK_MASK_ALL);
+
+    /* The custom draw handler can query the text rectangle,
+     * so get ready. */
+    TREEVIEW_ComputeTextWidth(infoPtr, wineItem, hdc);
+
+    cditem = 0;
+
+    if (infoPtr->cdmode & CDRF_NOTIFYITEMDRAW)
+    {
+	cditem = TREEVIEW_SendCustomDrawItemNotify
+	    (infoPtr, hdc, wineItem, CDDS_ITEMPREPAINT);
+	TRACE("prepaint:cditem-app returns 0x%x\n", cditem);
+
+	if (cditem & CDRF_SKIPDEFAULT)
+	{
+	    SelectObject(hdc, hOldFont);
+	    return;
+	}
+    }
+
+    if (cditem & CDRF_NEWFONT)
+	TREEVIEW_ComputeTextWidth(infoPtr, wineItem, hdc);
+
+    TREEVIEW_DrawItemLines(infoPtr, hdc, wineItem);
+
+    centery = (wineItem->rect.top + wineItem->rect.bottom) / 2;
+
+    /* 
+     * Display the images associated with this item
+     */
+    {
+	INT imageIndex;
+
+	/* State images are displayed to the left of the Normal image
+	 * image number is in state; zero should be `display no image'.
+	 */
+	imageIndex = STATEIMAGEINDEX(wineItem->state);
+
+	if (infoPtr->himlState && imageIndex)
+	{
+	    ImageList_Draw(infoPtr->himlState, imageIndex, hdc,
+			   wineItem->stateOffset,
+			   centery - infoPtr->stateImageHeight / 2,
+			   ILD_NORMAL);
+	}
+
+	/* Now, draw the normal image; can be either selected or
+	 * non-selected image. 
+	 */
+
+	if ((wineItem->state & TVIS_SELECTED) && (wineItem->iSelectedImage))
+	{
+	    /* The item is curently selected */
+	    imageIndex = wineItem->iSelectedImage;
+	}
+	else
+	{
+	    /* The item is not selected */
+	    imageIndex = wineItem->iImage;
+	}
+
+	if (infoPtr->himlNormal)
+	{
+	    int ovlIdx = wineItem->state & TVIS_OVERLAYMASK;
+
+	    ImageList_Draw(infoPtr->himlNormal, imageIndex, hdc,
+			   wineItem->imageOffset,
+			   centery - infoPtr->normalImageHeight / 2,
+			   ILD_NORMAL | ovlIdx);
+	}
+    }
+
+
+    /* 
+     * Display the text associated with this item
+     */
+
+    /* Don't paint item's text if it's being edited */
+    if (!infoPtr->hwndEdit || (infoPtr->selectedItem != wineItem))
+    {
+	if (wineItem->pszText)
+	{
+	    COLORREF oldTextColor = 0;
+	    INT oldBkMode;
+	    HBRUSH hbrBk = 0;
+	    BOOL inFocus = (GetFocus() == infoPtr->hwnd);
+	    RECT rcText;
+
+	    oldBkMode = SetBkMode(hdc, TRANSPARENT);
+
+	    /* - If item is drop target or it is selected and window is in focus -
+	     * use blue background (COLOR_HIGHLIGHT).
+	     * - If item is selected, window is not in focus, but it has style
+	     * TVS_SHOWSELALWAYS - use grey background (COLOR_BTNFACE)
+	     * - Otherwise - don't fill background
+	     */
+	    if ((wineItem->state & TVIS_DROPHILITED) || ((wineItem == infoPtr->focusedItem) && !(wineItem->state & TVIS_SELECTED)) ||
+		((wineItem->state & TVIS_SELECTED) && (!infoPtr->focusedItem) &&
+		 (inFocus || (infoPtr->dwStyle & TVS_SHOWSELALWAYS))))
+	    {
+		if ((wineItem->state & TVIS_DROPHILITED) || inFocus)
+		{
+		    hbrBk = CreateSolidBrush(GetSysColor(COLOR_HIGHLIGHT));
+		    oldTextColor =
+			SetTextColor(hdc, GetSysColor(COLOR_HIGHLIGHTTEXT));
+		}
+		else
+		{
+		    hbrBk = CreateSolidBrush(GetSysColor(COLOR_BTNFACE));
+
+		    if (infoPtr->clrText == -1)
+			oldTextColor =
+			    SetTextColor(hdc, GetSysColor(COLOR_WINDOWTEXT));
+		    else
+			oldTextColor = SetTextColor(hdc, infoPtr->clrText);
+		}
+	    }
+	    else
+	    {
+		if (infoPtr->clrText == -1)
+		    oldTextColor =
+			SetTextColor(hdc, GetSysColor(COLOR_WINDOWTEXT));
+		else
+		    oldTextColor = SetTextColor(hdc, infoPtr->clrText);
+	    }
+
+	    rcText.top = wineItem->rect.top;
+	    rcText.bottom = wineItem->rect.bottom;
+	    rcText.left = wineItem->textOffset;
+	    rcText.right = rcText.left + wineItem->textWidth + 4;
+
+	    if (hbrBk)
+	    {
+		FillRect(hdc, &rcText, hbrBk);
+		DeleteObject(hbrBk);
+	    }
+
+	    /* Draw the box arround the selected item */
+	    if ((wineItem == infoPtr->selectedItem) && inFocus)
+	    {
+		HPEN hNewPen = CreatePen(PS_DOT, 0,
+					 GetSysColor(COLOR_WINDOWTEXT));
+		HPEN hOldPen = SelectObject(hdc, hNewPen);
+		INT rop = SetROP2(hdc, R2_XORPEN);
+		POINT points[5];
+
+		points[4].x = points[0].x = rcText.left;
+		points[4].y = points[0].y = rcText.top;
+		points[1].x = rcText.right - 1;
+		points[1].y = rcText.top;
+		points[2].x = rcText.right - 1;
+		points[2].y = rcText.bottom - 1;
+		points[3].x = rcText.left;
+		points[3].y = rcText.bottom - 1;
+
+		Polyline(hdc, points, 5);
+
+		SetROP2(hdc, rop);
+		SelectObject(hdc, hOldPen);
+		DeleteObject(hNewPen);
+	    }
+
+	    rcText.left += 2;
+	    rcText.right -= 2;
+
+	    /* Draw it */
+	    DrawTextA(hdc,
+		      wineItem->pszText,
+		      lstrlenA(wineItem->pszText),
+		      &rcText,
+		      DT_CENTER | DT_VCENTER | DT_SINGLELINE | DT_NOPREFIX);
+
+	    /* Restore the hdc state */
+	    SetTextColor(hdc, oldTextColor);
+
+	    if (oldBkMode != TRANSPARENT)
+		SetBkMode(hdc, oldBkMode);
+	}
+    }
+
+    /* Draw insertion mark if necessary */
+
+    if (infoPtr->insertMarkItem)
+	TRACE("item:%d,mark:%d\n",
+	      TREEVIEW_GetItemIndex(infoPtr, wineItem),
+	      (int)infoPtr->insertMarkItem);
+
+    if (wineItem == infoPtr->insertMarkItem)
+    {
+	HPEN hNewPen, hOldPen;
+	int offset;
+	int left, right;
+
+	hNewPen = CreatePen(PS_SOLID, 2, infoPtr->clrInsertMark);
+	hOldPen = SelectObject(hdc, hNewPen);
+
+	if (infoPtr->insertBeforeorAfter)
+	    offset = wineItem->rect.bottom - 1;
+	else
+	    offset = wineItem->rect.top + 1;
+
+	left = wineItem->textOffset - 2;
+	right = wineItem->textOffset + wineItem->textWidth + 2;
+
+	MoveToEx(hdc, left, offset - 3, NULL);
+	LineTo(hdc, left, offset + 4);
+
+	MoveToEx(hdc, left, offset, NULL);
+	LineTo(hdc, right + 1, offset);
+
+	MoveToEx(hdc, right, offset + 3, NULL);
+	LineTo(hdc, right, offset - 4);
+
+	SelectObject(hdc, hOldPen);
+	DeleteObject(hNewPen);
+    }
+
+    if (cditem & CDRF_NOTIFYPOSTPAINT)
+    {
+	cditem = TREEVIEW_SendCustomDrawItemNotify
+	    (infoPtr, hdc, wineItem, CDDS_ITEMPOSTPAINT);
+	TRACE("postpaint:cditem-app returns 0x%x\n", cditem);
+    }
+
+    SelectObject(hdc, hOldFont);
+}
+
+/* Computes treeHeight and treeWidth and updates the scroll bars.
+ */
+static void
+TREEVIEW_UpdateScrollBars(TREEVIEW_INFO *infoPtr)
+{
+    TREEVIEW_ITEM *wineItem;
+    HWND hwnd = infoPtr->hwnd;
+    BOOL vert = FALSE;
+    BOOL horz = FALSE;
+    SCROLLINFO si;
+    LONG scrollX = infoPtr->scrollX;
+
+    infoPtr->treeWidth = 0;
+    infoPtr->treeHeight = 0;
+
+    /* We iterate through all visible items in order to get the tree height
+     * and width */
+    wineItem = infoPtr->root->firstChild;
+
+    while (wineItem != NULL)
+    {
+	if (ISVISIBLE(wineItem))
+	{
+            /* actually we draw text at textOffset + 2 */
+	    if (2+wineItem->textOffset+wineItem->textWidth > infoPtr->treeWidth)
+		infoPtr->treeWidth = wineItem->textOffset+wineItem->textWidth+2;
+
+	    /* This is scroll-adjusted, but we fix this below. */
+	    infoPtr->treeHeight = wineItem->rect.bottom;
+	}
+
+	wineItem = TREEVIEW_GetNextListItem(infoPtr, wineItem);
+    }
+
+    /* Fix the scroll adjusted treeHeight and treeWidth. */
+    if (infoPtr->root->firstChild)
+	infoPtr->treeHeight -= infoPtr->root->firstChild->rect.top;
+
+    infoPtr->treeWidth += infoPtr->scrollX;
+
+    /* Adding one scroll bar may take up enough space that it forces us
+     * to add the other as well. */
+    if (infoPtr->treeHeight > infoPtr->clientHeight)
+    {
+	vert = TRUE;
+
+	if (infoPtr->treeWidth
+	    > infoPtr->clientWidth - GetSystemMetrics(SM_CXVSCROLL))
+	    horz = TRUE;
+    }
+    else if (infoPtr->treeWidth > infoPtr->clientWidth)
+	horz = TRUE;
+
+    if (!vert && horz && infoPtr->treeHeight
+	> infoPtr->clientHeight - GetSystemMetrics(SM_CYVSCROLL))
+	vert = TRUE;
+
+    si.cbSize = sizeof(SCROLLINFO);
+    si.fMask  = SIF_POS|SIF_RANGE|SIF_PAGE;
+    si.nMin   = 0;
+
+    if (vert)
+    {
+	infoPtr->uInternalStatus |= TV_VSCROLL;
+
+	si.nPage = TREEVIEW_GetVisibleCount(infoPtr);
+	si.nPos  = infoPtr->firstVisible->visibleOrder;
+	si.nMax  = infoPtr->maxVisibleOrder - 1;
+
+	SetScrollInfo(hwnd, SB_VERT, &si, TRUE);
+    }
+    else
+    {
+	if (infoPtr->uInternalStatus & TV_VSCROLL)
+	    ShowScrollBar(hwnd, SB_VERT, FALSE);
+
+	infoPtr->uInternalStatus &= ~TV_VSCROLL;
+    }
+
+    if (horz)
+    {
+	infoPtr->uInternalStatus |= TV_HSCROLL;
+
+	si.nPage = infoPtr->clientWidth;
+	si.nPos  = infoPtr->scrollX;
+	si.nMax  = infoPtr->treeWidth - 1;
+
+	if (si.nPos > si.nMax - max( si.nPage-1, 0 ))
+        {
+           si.nPos = si.nMax - max( si.nPage-1, 0 );
+           scrollX = si.nPos;
+        }
+
+	SetScrollInfo(hwnd, SB_HORZ, &si, TRUE);
+    }
+    else
+    {
+	if (infoPtr->uInternalStatus & TV_HSCROLL)
+	    ShowScrollBar(hwnd, SB_HORZ, FALSE);
+
+	scrollX = 0;
+    }
+
+    if (infoPtr->scrollX != scrollX)
+    {
+	TREEVIEW_HScroll(infoPtr,
+	                 MAKEWPARAM(SB_THUMBPOSITION, scrollX));
+    }
+
+    if (!horz)
+	infoPtr->uInternalStatus &= ~TV_HSCROLL;
+}
+
+/* CtrlSpy doesn't mention this, but CorelDRAW's object manager needs it. */
+static LRESULT
+TREEVIEW_EraseBackground(TREEVIEW_INFO *infoPtr, HDC hDC)
+{
+    HBRUSH hBrush = CreateSolidBrush(infoPtr->clrBk);
+    RECT rect;
+
+    GetClientRect(infoPtr->hwnd, &rect);
+    FillRect(hDC, &rect, hBrush);
+    DeleteObject(hBrush);
+
+    return 1;
+}
+
+static void
+TREEVIEW_Refresh(TREEVIEW_INFO *infoPtr, HDC hdc, RECT *rc)
+{
+    HWND hwnd = infoPtr->hwnd;
+    RECT rect = *rc;
+    TREEVIEW_ITEM *wineItem;
+
+    if (infoPtr->clientHeight == 0 || infoPtr->clientWidth == 0)
+    {
+	TRACE("empty window\n");
+	return;
+    }
+
+    infoPtr->cdmode = TREEVIEW_SendCustomDrawNotify(infoPtr, CDDS_PREPAINT,
+						    hdc, rect);
+
+    if (infoPtr->cdmode == CDRF_SKIPDEFAULT)
+    {
+	ReleaseDC(hwnd, hdc);
+	return;
+    }
+
+    for (wineItem = infoPtr->root->firstChild;
+         wineItem != NULL;
+         wineItem = TREEVIEW_GetNextListItem(infoPtr, wineItem))
+    {
+	if (ISVISIBLE(wineItem))
+	{
+            /* Avoid unneeded calculations */
+            if (wineItem->rect.top > rect.bottom)
+                break;
+            if (wineItem->rect.bottom < rect.top)
+                continue;
+
+	    TREEVIEW_DrawItem(infoPtr, hdc, wineItem);
+	}
+    }
+
+    TREEVIEW_UpdateScrollBars(infoPtr);
+
+    if (infoPtr->cdmode & CDRF_NOTIFYPOSTPAINT)
+	infoPtr->cdmode =
+	    TREEVIEW_SendCustomDrawNotify(infoPtr, CDDS_POSTPAINT, hdc, rect);
+}
+
+static void
+TREEVIEW_QueueRefresh(TREEVIEW_INFO *infoPtr)
+{
+    InvalidateRect(infoPtr->hwnd, NULL, TRUE);
+}
+
+/* It be that item->rect is out of date. If so, we invalidate the wrong area,
+ * but then whoever updates item->rect knows that they must invalidate after
+ * correcting it. */
+static void
+TREEVIEW_QueueItemRefresh(TREEVIEW_INFO *infoPtr, TREEVIEW_ITEM *item)
+{
+    if (item != NULL)
+	InvalidateRect(infoPtr->hwnd, &item->rect, TRUE);
+}
+
+static LRESULT
+TREEVIEW_Paint(TREEVIEW_INFO *infoPtr, WPARAM wParam)
+{
+    HDC hdc;
+    PAINTSTRUCT ps;
+    RECT rc;
+
+    TRACE("\n");
+
+    if (wParam)
+    {
+        hdc = (HDC)wParam;
+        GetUpdateRect(infoPtr->hwnd, &rc, TRUE);
+    }
+    else
+    {
+        hdc = BeginPaint(infoPtr->hwnd, &ps);
+        rc = ps.rcPaint;
+    }
+
+    if(infoPtr->bRedraw) /* WM_SETREDRAW sets bRedraw */
+        TREEVIEW_Refresh(infoPtr, hdc, &rc);
+
+    if (!wParam)
+	EndPaint(infoPtr->hwnd, &ps);
+
+    return 0;
+}
+
+
+/* Sorting **************************************************************/
+
 /***************************************************************************
  * Forward the DPA local callback to the treeview owner callback
  */
-static INT WINAPI TREEVIEW_CallBackCompare( 
-  LPVOID first, 
-  LPVOID second, 
-  LPARAM tvInfoPtr)
+static INT WINAPI
+TREEVIEW_CallBackCompare(TREEVIEW_ITEM *first, TREEVIEW_ITEM *second, LPTVSORTCB pCallBackSort)
 {
-  /* Forward the call to the client define callback */
-  TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr((HWND)tvInfoPtr);
-  return (infoPtr->pCallBackSort->lpfnCompare)(
-    ((TREEVIEW_ITEM*)first)->lParam,
-    ((TREEVIEW_ITEM*)second)->lParam,
-    infoPtr->pCallBackSort->lParam);
+    /* Forward the call to the client-defined callback */
+    return pCallBackSort->lpfnCompare(first->lParam,
+				      second->lParam,
+				      pCallBackSort->lParam);
 }
 
 /***************************************************************************
  * Treeview native sort routine: sort on item text.
  */
-static INT WINAPI TREEVIEW_SortOnName ( 
-  LPVOID first, 
-  LPVOID second, 
-  LPARAM tvInfoPtr)
+static INT WINAPI
+TREEVIEW_SortOnName(TREEVIEW_ITEM *first, TREEVIEW_ITEM *second,
+                     TREEVIEW_INFO *infoPtr)
 {
-  HWND hwnd=(HWND) tvInfoPtr;
-  char *txt1, *txt2;
-  TREEVIEW_ITEM *item;
+    TREEVIEW_UpdateDispInfo(infoPtr, first, TVIF_TEXT);
+    TREEVIEW_UpdateDispInfo(infoPtr, second, TVIF_TEXT);
 
-	
-  item=(TREEVIEW_ITEM *) first;
-  if (item->pszText==LPSTR_TEXTCALLBACKA)  {
-	 TREEVIEW_SendDispInfoNotify (hwnd, item, TVN_GETDISPINFOA, TVIF_TEXT);
+    return strcasecmp(first->pszText, second->pszText);
+}
+
+/* Returns the number of physical children belonging to item. */
+static INT
+TREEVIEW_CountChildren(TREEVIEW_INFO *infoPtr, TREEVIEW_ITEM *item)
+{
+    INT cChildren = 0;
+    HTREEITEM hti;
+
+    for (hti = item->firstChild; hti != NULL; hti = hti->nextSibling)
+	cChildren++;
+
+    return cChildren;
+}
+
+/* Returns a DPA containing a pointer to each physical child of item in
+ * sibling order. If item has no children, an empty DPA is returned. */
+static HDPA
+TREEVIEW_BuildChildDPA(TREEVIEW_INFO *infoPtr, TREEVIEW_ITEM *item)
+{
+    HTREEITEM child = item->firstChild;
+
+    HDPA list = DPA_Create(8);
+    if (list == 0) return NULL;
+
+    for (child = item->firstChild; child != NULL; child = child->nextSibling)
+    {
+	if (DPA_InsertPtr(list, INT_MAX, child) == -1)
+	{
+	    DPA_Destroy(list);
+	    return NULL;
 	}
-  txt1=item->pszText;
+    }
 
-  item=(TREEVIEW_ITEM *) second;
-  if (item->pszText==LPSTR_TEXTCALLBACKA)  {
-	 TREEVIEW_SendDispInfoNotify (hwnd, item, TVN_GETDISPINFOA, TVIF_TEXT);
-	}
-  txt2=item->pszText;
-
-  return -strcmp (txt1,txt2);
+    return list;
 }
 
 /***************************************************************************
@@ -1755,109 +2705,116 @@
  *         application decide what that means. See also TVM_SORTCHILDRENCB.
  */
 
-static LRESULT WINAPI TREEVIEW_Sort (
-  HWND   hwnd, 
-  BOOL   fRecurse, 
-  HTREEITEM parent,
-  LPTVSORTCB pSort 
-  )
+static LRESULT
+TREEVIEW_Sort(TREEVIEW_INFO *infoPtr, BOOL fRecurse, HTREEITEM parent,
+	      LPTVSORTCB pSort)
 {
-  TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
-  TREEVIEW_ITEM *sortMe  = NULL; /* Node for which we sort the children */
+    INT cChildren;
+    PFNDPACOMPARE pfnCompare;
+    LPARAM lpCompare;
 
-  /* Obtain the TVSORTBC struct */
-  infoPtr->pCallBackSort = pSort;
+    /* undocumented feature: TVI_ROOT means `sort the whole tree' */
+    if (parent == TVI_ROOT)
+	parent = infoPtr->root;
 
-	/* undocumented feature: TVI_ROOT means `sort the whole tree' */
-
-  if (parent==TVI_ROOT)
-    parent=infoPtr->TopRootItem;
-
-  /* Check for a valid handle to the parent item */
-  if (!TREEVIEW_ValidItem(infoPtr, parent))
-  {
-    ERR ("invalid item hParent=%x\n", (INT)parent);
-    return FALSE;
-  }
-
-  /* Obtain the parent node to sort */  
-  sortMe = &infoPtr->items[ (INT)parent ];
-
-  /* Make sure there is something to sort */
-  if ( sortMe->cChildren > 1 ) 
-  {
-    /* pointer organization */
-    HDPA          sortList   = DPA_Create(sortMe->cChildren);
-    HTREEITEM     itemHandle = sortMe->firstChild;
-    TREEVIEW_ITEM *itemPtr   = & infoPtr->items[ (INT)itemHandle ];
-
-    /* TREEVIEW_ITEM rechaining */
-    INT  count     = 0;
-    VOID *item     = 0;
-    VOID *nextItem = 0;
-    VOID *prevItem = 0;
-
-    /* Build the list of item to sort */
-    do 
+    /* Check for a valid handle to the parent item */
+    if (!TREEVIEW_ValidItem(infoPtr, parent))
     {
-      DPA_InsertPtr(
-        sortList,              /* the list */
-        sortMe->cChildren+1,   /* force the insertion to be an append */
-        itemPtr);              /* the ptr to store */   
-
-      /* Get the next sibling */
-      itemHandle = itemPtr->sibling;
-      itemPtr    = & infoPtr->items[ (INT)itemHandle ];
-    } while ( itemHandle != NULL );
-
-    /* let DPA perform the sort activity */
-	if (pSort) 
-    	DPA_Sort(
-      		sortList,                  /* what  */ 
-      		TREEVIEW_CallBackCompare,  /* how   */
-      		hwnd);                     /* owner */
-	else 
-		DPA_Sort (
-			sortList,                  /* what  */
-      		TREEVIEW_SortOnName,       /* how   */
-			hwnd);                     /* owner */
-
-    /* 
-     * Reorganized TREEVIEW_ITEM structures. 
-     * Note that we know we have at least two elements.
-     */
-
-    /* Get the first item and get ready to start... */
-    item = DPA_GetPtr(sortList, count++);    
-    while ( (nextItem = DPA_GetPtr(sortList, count++)) != NULL )
-    {
-      /* link the two current item toghether */
-      ((TREEVIEW_ITEM*)item)->sibling       = ((TREEVIEW_ITEM*)nextItem)->hItem;
-      ((TREEVIEW_ITEM*)nextItem)->upsibling = ((TREEVIEW_ITEM*)item)->hItem;
-
-      if (prevItem == NULL) /* this is the first item, update the parent */
-      {
-        sortMe->firstChild                = ((TREEVIEW_ITEM*)item)->hItem;
-        ((TREEVIEW_ITEM*)item)->upsibling = NULL;
-      }
-      else                  /* fix the back chaining */
-      {
-        ((TREEVIEW_ITEM*)item)->upsibling = ((TREEVIEW_ITEM*)prevItem)->hItem;
-      }
-
-      /* get ready for the next one */
-      prevItem = item; 
-      item     = nextItem;
+	ERR("invalid item hParent=%x\n", (INT)parent);
+	return FALSE;
     }
 
-    /* the last item is pointed to by item and never has a sibling */
-    ((TREEVIEW_ITEM*)item)->sibling = NULL; 
+    if (pSort)
+    {
+	pfnCompare = (PFNDPACOMPARE)TREEVIEW_CallBackCompare;
+	lpCompare = (LPARAM)pSort;
+    }
+    else
+    {
+	pfnCompare = (PFNDPACOMPARE)TREEVIEW_SortOnName;
+	lpCompare = (LPARAM)infoPtr;
+    }
 
-    DPA_Destroy(sortList);
+    cChildren = TREEVIEW_CountChildren(infoPtr, parent);
 
-    return TRUE;
-  }
-  return FALSE;
+    /* Make sure there is something to sort */
+    if (cChildren > 1)
+    {
+	/* TREEVIEW_ITEM rechaining */
+	INT count = 0;
+	HTREEITEM item = 0;
+	HTREEITEM nextItem = 0;
+	HTREEITEM prevItem = 0;
+
+	HDPA sortList = TREEVIEW_BuildChildDPA(infoPtr, parent);
+
+	if (sortList == NULL)
+	    return FALSE;
+
+	/* let DPA sort the list */
+	DPA_Sort(sortList, pfnCompare, lpCompare);
+
+	/* The order of DPA entries has been changed, so fixup the
+	 * nextSibling and prevSibling pointers. */
+
+	item = (HTREEITEM)DPA_GetPtr(sortList, count++);
+	while ((nextItem = (HTREEITEM)DPA_GetPtr(sortList, count++)) != NULL)
+	{
+	    /* link the two current item toghether */
+	    item->nextSibling = nextItem;
+	    nextItem->prevSibling = item;
+
+	    if (prevItem == NULL)
+	    {
+		/* this is the first item, update the parent */
+		parent->firstChild = item;
+		item->prevSibling = NULL;
+	    }
+	    else
+	    {
+		/* fix the back chaining */
+		item->prevSibling = prevItem;
+	    }
+
+	    /* get ready for the next one */
+	    prevItem = item;
+	    item = nextItem;
+	}
+
+	/* the last item is pointed to by item and never has a sibling */
+	item->nextSibling = NULL;
+	parent->lastChild = item;
+
+	DPA_Destroy(sortList);
+
+	TREEVIEW_VerifyTree(infoPtr);
+
+	if (parent->state & TVIS_EXPANDED)
+	{
+	    int visOrder = infoPtr->firstVisible->visibleOrder;
+
+	    TREEVIEW_RecalculateVisibleOrder(infoPtr, parent);
+
+	    if (TREEVIEW_IsChildOf(parent, infoPtr->firstVisible))
+	    {
+	        TREEVIEW_ITEM *item;
+
+	        for (item = infoPtr->root->firstChild; item != NULL;
+	             item = TREEVIEW_GetNextListItem(infoPtr, item))
+	        {
+	            if (item->visibleOrder == visOrder)
+	                break;
+	        }
+
+                TREEVIEW_SetFirstVisible(infoPtr, item, FALSE);
+	    }
+
+	    TREEVIEW_QueueRefresh(infoPtr);
+	}
+
+	return TRUE;
+    }
+    return FALSE;
 }
 
 
@@ -1865,1058 +2822,213 @@
  * Setup the treeview structure with regards of the sort method
  * and sort the children of the TV item specified in lParam
  */
-static LRESULT WINAPI TREEVIEW_SortChildrenCB(
-  HWND   hwnd, 
-  WPARAM wParam, 
-  LPARAM lParam
-  )
+static LRESULT
+TREEVIEW_SortChildrenCB(TREEVIEW_INFO *infoPtr, WPARAM wParam, LPTVSORTCB pSort)
 {
- LPTVSORTCB pSort=(LPTVSORTCB) lParam;
-
- return TREEVIEW_Sort (hwnd, wParam, pSort->hParent, pSort);
+    return TREEVIEW_Sort(infoPtr, wParam, pSort->hParent, pSort);
 }
 
 
 /***************************************************************************
  * Sort the children of the TV item specified in lParam.
  */
-static LRESULT WINAPI TREEVIEW_SortChildren (
-  HWND   hwnd, 
-  WPARAM wParam, 
-  LPARAM lParam)
+static LRESULT
+TREEVIEW_SortChildren(TREEVIEW_INFO *infoPtr, WPARAM wParam, LPARAM lParam)
 {
- return TREEVIEW_Sort (hwnd, (BOOL) wParam, (HTREEITEM) lParam, NULL);
+    return TREEVIEW_Sort(infoPtr, (BOOL)wParam, (HTREEITEM)lParam, NULL);
 }
 
 
+/* Expansion/Collapse ***************************************************/
 
-/* the method used below isn't the most memory-friendly, but it avoids 
-   a lot of memory reallocations */ 
-
-/* BTW: we waste handle 0; 0 is not an allowed handle. */
-
-static LRESULT
-TREEVIEW_InsertItemA (HWND hwnd, WPARAM wParam, LPARAM lParam)
-
+static BOOL
+TREEVIEW_SendExpanding(TREEVIEW_INFO *infoPtr, TREEVIEW_ITEM *wineItem,
+		       UINT action)
 {
-  TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
-  TVINSERTSTRUCTA  *ptdi;
-  TVITEMEXA 	*tvItem;
-  TREEVIEW_ITEM *wineItem, *parentItem, *prevsib, *sibItem;
-  INT		iItem,listItems,i,len;
- 
-  /* Item to insert */
-  ptdi = (LPTVINSERTSTRUCTA) lParam;
+    return !TREEVIEW_SendTreeviewNotify(infoPtr, TVN_ITEMEXPANDINGA, action,
+					TVIF_HANDLE | TVIF_STATE | TVIF_PARAM
+					| TVIF_IMAGE | TVIF_SELECTEDIMAGE,
+					0, wineItem);
+}
 
-	/* check if memory is available */
-
-  if (infoPtr->uNumPtrsAlloced==0) {
-        infoPtr->items = COMCTL32_Alloc (TVITEM_ALLOC*sizeof (TREEVIEW_ITEM));
-        infoPtr->freeList= COMCTL32_Alloc ((1+(TVITEM_ALLOC>>5)) * sizeof (INT));
-        infoPtr->uNumPtrsAlloced=TVITEM_ALLOC;
-	infoPtr->TopRootItem=(HTREEITEM)1;
-   }
-
-  /* 
-   * Reallocate contiguous space for items 
-   */
-  if (infoPtr->uNumItems == (infoPtr->uNumPtrsAlloced-1) ) {
-   	TREEVIEW_ITEM *oldItems = infoPtr->items;
-	INT *oldfreeList = infoPtr->freeList;
-
-	infoPtr->uNumPtrsAlloced*=2;
-    infoPtr->items = COMCTL32_Alloc (infoPtr->uNumPtrsAlloced*sizeof (TREEVIEW_ITEM));
-    infoPtr->freeList= COMCTL32_Alloc ((1+(infoPtr->uNumPtrsAlloced>>5))*sizeof (INT));
-
-    memcpy (&infoPtr->items[0], &oldItems[0],
-                    infoPtr->uNumPtrsAlloced/2 * sizeof(TREEVIEW_ITEM));
-    memcpy (&infoPtr->freeList[0], &oldfreeList[0],
-                    (infoPtr->uNumPtrsAlloced>>6) * sizeof(INT));
-
-    COMCTL32_Free (oldItems);  
-    COMCTL32_Free (oldfreeList);  
-   }
-
-  /* 
-   * Reset infoPtr structure with new stat according to current TV picture
-   */
-  iItem=0;
-  infoPtr->uNumItems++;
-  if ((INT)infoPtr->uMaxHandle==(infoPtr->uNumItems-1))  { 
-  	iItem=infoPtr->uNumItems;
-  	infoPtr->uMaxHandle = (HTREEITEM)((INT)infoPtr->uMaxHandle + 1);
-  } else {					 /* check freelist */
-  	for (i=0; i<=infoPtr->uNumPtrsAlloced>>5; i++) {
-  		if (infoPtr->freeList[i]) {
-  			iItem=ffs (infoPtr->freeList[i])-1;
-  			tv_clear_bit(iItem,&infoPtr->freeList[i]);
-   			iItem+=i<<5;
-  			break;
-  		}
-    } 
-  }
-
-  if (TRACE_ON(treeview)) { 
-    for (i=0; i<=infoPtr->uNumPtrsAlloced>>5; i++) 
-	    TRACE("%8x\n",infoPtr->freeList[i]);
-  }
-
-  if (!iItem) ERR("Argh -- can't find free item.\n");
-
-  /* 
-   * Find the parent item of the new item 
-   */  
-  tvItem= & ptdi->DUMMYUNIONNAME.itemex;
-  wineItem=& infoPtr->items[iItem];
-
-  if ((ptdi->hParent==TVI_ROOT) || (ptdi->hParent==0)) {
-    parentItem       = NULL;
-    wineItem->parent = 0; 
-    sibItem          = &infoPtr->items [(INT)infoPtr->TopRootItem];
-    listItems        = infoPtr->uNumItems;
-  }
-  else  {
-  	parentItem = &infoPtr->items[(INT)ptdi->hParent];
-  
-    /* Do the insertion here it if it's the only item of this parent */
-  	if (!parentItem->firstChild) 
-  		parentItem->firstChild=(HTREEITEM)iItem;
-  
-  	wineItem->parent = ptdi->hParent;
-  	sibItem          = &infoPtr->items [(INT)parentItem->firstChild];
-  	listItems        = parentItem->cChildren;
-  	parentItem->cChildren++;
-  }
-
-  
-  /* NOTE: I am moving some setup of the wineItem object that was initialy 
-   *       done at the end of the function since some of the values are 
-   *       required by the Callback sorting 
-   */
-
-  if (tvItem->mask & TVIF_TEXT) 
-  {
-    /*
-     * Setup the item text stuff here since it's required by the Sort method
-     * when the insertion are ordered
-     */
-    if (tvItem->pszText!=LPSTR_TEXTCALLBACKA) 
-    {
-      TRACE("(%p,%s)\n", &tvItem->pszText, tvItem->pszText); 
-      len = lstrlenA (tvItem->pszText)+1;
-      wineItem->pszText= COMCTL32_Alloc (len+1);
-      strcpy (wineItem->pszText, tvItem->pszText);
-      wineItem->cchTextMax=len;
-    }
-    else 
-    {
-      TRACE("LPSTR_TEXTCALLBACK\n");
-      wineItem->pszText = LPSTR_TEXTCALLBACKA;
-      wineItem->cchTextMax = 0;
-    }
-  }
-
-  if (tvItem->mask & TVIF_PARAM) 
-    wineItem->lParam=tvItem->lParam;
-
-
-  wineItem->upsibling=0;  /* needed in case we're the first item in a list */ 
-  wineItem->sibling=0;     
-  wineItem->firstChild=0;
-  wineItem->hItem=(HTREEITEM)iItem;
-
-  if (listItems!=0) {
-     prevsib=NULL;
-
-     switch ((DWORD) ptdi->hInsertAfter) {
-		case (DWORD) TVI_FIRST: 
-			if (sibItem==wineItem) break;
-			if (wineItem->parent) {
-				wineItem->sibling=parentItem->firstChild;
-				parentItem->firstChild=(HTREEITEM)iItem;
-			} else {
-				wineItem->sibling=infoPtr->TopRootItem;
-				infoPtr->TopRootItem=(HTREEITEM)iItem;
-			}
-			sibItem->upsibling=(HTREEITEM)iItem;
-			break;
-
-		case (DWORD) TVI_SORT:  
-  	  if (sibItem==wineItem) 
-        /* 
-         * This item is the first child of the level and it 
-         * has already been inserted 
-         */                
-        break; 
-      else
-      {
-        TREEVIEW_ITEM *aChild;
-		  
-  
-        TREEVIEW_ITEM *previousChild = NULL;
-        BOOL bItemInserted           = FALSE;
-
-	    if (parentItem)
-          aChild = &infoPtr->items[(INT)parentItem->firstChild];
-		else 
-          aChild = &infoPtr->items[(INT)infoPtr->TopRootItem];
-  
-        /* lookup the text if using LPSTR_TEXTCALLBACKs */
-        if (wineItem->pszText==LPSTR_TEXTCALLBACKA) {
-          TREEVIEW_SendDispInfoNotify (hwnd, wineItem, TVN_GETDISPINFOA, TVIF_TEXT);
-        }
-    
-        /* Iterate the parent children to see where we fit in */
-        while ( aChild != NULL )
-        {
-          INT comp;
-
-          /* lookup the text if using LPSTR_TEXTCALLBACKs */
-          if (aChild->pszText==LPSTR_TEXTCALLBACKA) {
-            TREEVIEW_SendDispInfoNotify (hwnd, aChild, TVN_GETDISPINFOA, TVIF_TEXT);
-	  }
-
-          comp = strcmp(wineItem->pszText, aChild->pszText);
-          if ( comp < 0 )  /* we are smaller than the current one */
-          {
-            TREEVIEW_InsertBefore(infoPtr, wineItem, aChild, parentItem);
-            bItemInserted = TRUE;
-            break;
-          }
-          else if ( comp > 0 )  /* we are bigger than the current one */
-          {
-            previousChild = aChild;
-            aChild = (aChild->sibling == 0)  /* This will help us to exit   */
-                        ? NULL               /* if there is no more sibling */
-                        : &infoPtr->items[(INT)aChild->sibling];
-  
-            /* Look at the next item */
-            continue;
-          }
-          else if ( comp == 0 )
-          {
-            /* 
-             * An item with this name is already existing, therefore,  
-             * we add after the one we found 
-             */
-            TREEVIEW_InsertAfter(infoPtr, wineItem, aChild, parentItem);
-            bItemInserted = TRUE;
-            break;
-          }
-        }
-      
-        /* 
-         * we reach the end of the child list and the item as not
-         * yet been inserted, therefore, insert it after the last child.
-         */
-        if ( (! bItemInserted ) && (aChild == NULL) )
-          TREEVIEW_InsertAfter(infoPtr, wineItem, previousChild, parentItem);
-  
-        break;
-      }
-
-
-		case (DWORD) TVI_LAST:  
-			if (sibItem==wineItem) break;
-			while (sibItem->sibling) {
-				prevsib=sibItem;
-				sibItem=&infoPtr->items [(INT)sibItem->sibling];
-			}
-			sibItem->sibling=(HTREEITEM)iItem;
-			wineItem->upsibling=sibItem->hItem;
-			break;
-		default:
-                    {
-                        TREEVIEW_ITEM  *localsibItem = sibItem;
-			while ((localsibItem->sibling) && 
-                               (localsibItem->hItem!=ptdi->hInsertAfter))
-				{
-				prevsib=localsibItem;
-                localsibItem=&infoPtr->items [(INT)localsibItem->sibling];
-              }
-			if (localsibItem->hItem!=ptdi->hInsertAfter) {
-			 WARN("tried to insert item after nonexisting handle %d treating as TVI_LAST.\n",
-                      (INT) ptdi->hInsertAfter);
-                       /* 
-                        * retry placing it last 
-                        */
-			if (sibItem==wineItem) break;
-			 while (sibItem->sibling) {
-				 prevsib=sibItem;
-				 sibItem=&infoPtr->items [(INT)sibItem->sibling];
-			 }
-			 sibItem->sibling=(HTREEITEM)iItem;
-			 wineItem->upsibling=sibItem->hItem;
-              		  break;
-			}
-			prevsib=localsibItem;
-			if (localsibItem->sibling) {
-            	localsibItem=&infoPtr->items [(INT)localsibItem->sibling];
-				localsibItem->upsibling=(HTREEITEM)iItem;
-				wineItem->sibling=localsibItem->hItem;
-			}
-			prevsib->sibling=(HTREEITEM)iItem;
-			wineItem->upsibling=prevsib->hItem;
-			break;
-             }
-   	}
-   }	
-
-
-/* Fill in info structure */
-
-   TRACE("new item %d; parent %d, mask %x\n", iItem, 
-			(INT)wineItem->parent,tvItem->mask);
-
-   wineItem->mask=tvItem->mask;
-   wineItem->iIntegral=1; 
-
-   if (tvItem->mask & TVIF_CHILDREN) {
-	 wineItem->cChildren=tvItem->cChildren;
-	 if (tvItem->cChildren==I_CHILDRENCALLBACK) 
-			FIXME(" I_CHILDRENCALLBACK not supported\n");
-	}
-
-  wineItem->expandBox.left   = 0; /* Initialize the expandBox */
-  wineItem->expandBox.top    = 0;
-  wineItem->expandBox.right  = 0;
-  wineItem->expandBox.bottom = 0;
-
-   if (tvItem->mask & TVIF_IMAGE) 
-	wineItem->iImage=tvItem->iImage;
-
-		/* If the application sets TVIF_INTEGRAL without
-			supplying a TVITEMEX structure, it's toast */
-
-   if (tvItem->mask & TVIF_INTEGRAL) 
-   		wineItem->iIntegral=tvItem->iIntegral;   
-
-   if (tvItem->mask & TVIF_SELECTEDIMAGE) 
-	wineItem->iSelectedImage=tvItem->iSelectedImage;
-
-   if (tvItem->mask & TVIF_STATE) {
-     TRACE("item state: %x ->%x\n", wineItem->state, tvItem->state);
-     TRACE("statemask: %x ->%x\n", wineItem->stateMask, tvItem->stateMask);
-	wineItem->state=tvItem->state;
-	wineItem->stateMask=tvItem->stateMask;
-   }
-
-   TREEVIEW_QueueRefresh (hwnd);
-
-   return (LRESULT) iItem;
+static VOID
+TREEVIEW_SendExpanded(TREEVIEW_INFO *infoPtr, TREEVIEW_ITEM *wineItem,
+		      UINT action)
+{
+    TREEVIEW_SendTreeviewNotify(infoPtr, TVN_ITEMEXPANDEDA, action,
+				TVIF_HANDLE | TVIF_STATE | TVIF_PARAM
+				| TVIF_IMAGE | TVIF_SELECTEDIMAGE,
+				0, wineItem);
 }
 
 
-static LRESULT
-TREEVIEW_InsertItemW(HWND hwnd, WPARAM wParam, LPARAM lParam)
+/* This corresponds to TVM_EXPAND with TVE_COLLAPSE.
+ * bRemoveChildren corresponds to TVE_COLLAPSERESET. */
+static BOOL
+TREEVIEW_Collapse(TREEVIEW_INFO *infoPtr, TREEVIEW_ITEM *wineItem,
+		  BOOL bRemoveChildren, BOOL bUser)
 {
-    TVINSERTSTRUCTW *tvisW;
-    TVINSERTSTRUCTA tvisA;
-    LRESULT lRes;
+    UINT action = TVE_COLLAPSE | (bRemoveChildren ? TVE_COLLAPSERESET : 0);
+    BOOL bSetSelection, bSetFirstVisible;
 
-    tvisW = (LPTVINSERTSTRUCTW)lParam;
+    TRACE("TVE_COLLAPSE %p %s\n", wineItem, TREEVIEW_ItemName(wineItem));
 
-    tvisA.hParent = tvisW->hParent;
-    tvisA.hInsertAfter = tvisW->hInsertAfter;
+    if (!(wineItem->state & TVIS_EXPANDED) || wineItem->firstChild == NULL)
+	return FALSE;
 
-    tvisA.DUMMYUNIONNAME.item.mask           = tvisW->DUMMYUNIONNAME.item.mask;
-    tvisA.DUMMYUNIONNAME.item.hItem          = tvisW->DUMMYUNIONNAME.item.hItem;
-    tvisA.DUMMYUNIONNAME.item.state          = tvisW->DUMMYUNIONNAME.item.state;
-    tvisA.DUMMYUNIONNAME.item.stateMask      = tvisW->DUMMYUNIONNAME.item.stateMask;
-    tvisA.DUMMYUNIONNAME.item.cchTextMax     = tvisW->DUMMYUNIONNAME.item.cchTextMax;
+    if (bUser)
+	TREEVIEW_SendExpanding(infoPtr, wineItem, action);
 
-    if(tvisW->DUMMYUNIONNAME.item.pszText)
+    wineItem->state &= ~TVIS_EXPANDED;
+
+    if (bUser)
+	TREEVIEW_SendExpanded(infoPtr, wineItem, action);
+
+    bSetSelection = (infoPtr->selectedItem != NULL
+		     && TREEVIEW_IsChildOf(wineItem, infoPtr->selectedItem));
+
+    bSetFirstVisible = (infoPtr->firstVisible != NULL
+                        && TREEVIEW_IsChildOf(wineItem, infoPtr->firstVisible));
+
+    if (bRemoveChildren)
     {
-	if (tvisW->DUMMYUNIONNAME.item.pszText!=LPSTR_TEXTCALLBACKW) 
-	{ 
-	    int len = lstrlenW (tvisW->DUMMYUNIONNAME.item.pszText)+1;
-	    tvisA.DUMMYUNIONNAME.item.pszText = COMCTL32_Alloc (len);
-	    lstrcpyWtoA (tvisA.DUMMYUNIONNAME.item.pszText,
-			 tvisW->DUMMYUNIONNAME.item.pszText );
-	}
-	else 
+	TRACE("TVE_COLLAPSERESET\n");
+	wineItem->state &= ~TVIS_EXPANDEDONCE;
+	TREEVIEW_RemoveAllChildren(infoPtr, wineItem);
+    }
+
+    if (wineItem->firstChild)
+    {
+        TREEVIEW_ITEM *item, *sibling;
+
+	sibling = TREEVIEW_GetNextListItem(infoPtr, wineItem);
+
+	for (item = wineItem->firstChild; item != sibling;
+	     item = TREEVIEW_GetNextListItem(infoPtr, item))
 	{
-	    tvisA.DUMMYUNIONNAME.item.pszText = LPSTR_TEXTCALLBACKA;
-	    tvisA.DUMMYUNIONNAME.item.cchTextMax = 0;
+	    item->visibleOrder = -1;
 	}
     }
 
-    tvisA.DUMMYUNIONNAME.item.iImage         = tvisW->DUMMYUNIONNAME.item.iImage;
-    tvisA.DUMMYUNIONNAME.item.iSelectedImage = tvisW->DUMMYUNIONNAME.item.iSelectedImage;
-    tvisA.DUMMYUNIONNAME.item.cChildren      = tvisW->DUMMYUNIONNAME.item.cChildren;
-    tvisA.DUMMYUNIONNAME.item.lParam         = tvisW->DUMMYUNIONNAME.item.lParam;
+    TREEVIEW_RecalculateVisibleOrder(infoPtr, wineItem);
 
-    lRes = TREEVIEW_InsertItemA(hwnd,wParam,(LPARAM)&tvisA);
+    TREEVIEW_SetFirstVisible(infoPtr, bSetFirstVisible ? wineItem
+			     : infoPtr->firstVisible, TRUE);
 
-    if (tvisA.DUMMYUNIONNAME.item.pszText!=LPSTR_TEXTCALLBACKA) 
+    if (bSetSelection)
     {
-	COMCTL32_Free(tvisA.DUMMYUNIONNAME.item.pszText);
+	/* Don't call DoSelectItem, it sends notifications. */
+	if (TREEVIEW_ValidItem(infoPtr, infoPtr->selectedItem))
+	    infoPtr->selectedItem->state &= ~TVIS_SELECTED;
+	wineItem->state |= TVIS_SELECTED;
+	infoPtr->selectedItem = wineItem;
+
+	TREEVIEW_EnsureVisible(infoPtr, wineItem, FALSE);
     }
 
-    return lRes;
-
-}
-
-
-static LRESULT
-TREEVIEW_DeleteItem (HWND hwnd, WPARAM wParam, LPARAM lParam)
-{
-  TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
-  INT iItem;
-  TREEVIEW_ITEM *wineItem;
-
-  TRACE("item = %08lx\n", lParam);
-
-  if (lParam == (INT)TVI_ROOT) {
-	TREEVIEW_RemoveTree (hwnd);
-  } else {
-  	iItem= (INT) lParam;
-  	wineItem = TREEVIEW_ValidItem (infoPtr, (HTREEITEM)iItem);
-  	if (!wineItem) return FALSE;
-
-        if (wineItem->pszText==LPSTR_TEXTCALLBACKA)
-           TRACE("LPSTR_TEXTCALLBACK\n");
-	else
-           TRACE("%s\n",wineItem->pszText);
-	TREEVIEW_RemoveItem (hwnd, wineItem);
-  }
-
-  TREEVIEW_QueueRefresh (hwnd);
-
-  return TRUE;
-}
-
-
-
-static LRESULT
-TREEVIEW_GetIndent (HWND hwnd)
-{
- TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
-
- TRACE("\n");
- return infoPtr->uIndent;
-}
-
-static LRESULT
-TREEVIEW_SetIndent (HWND hwnd, WPARAM wParam)
-{
-  TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
-  INT newIndent;
-   
-  TRACE("\n");
-  newIndent=(INT) wParam;
-  if (newIndent < MINIMUM_INDENT) newIndent=MINIMUM_INDENT;
-  infoPtr->uIndent=newIndent;
-  
-  return 0;
-}
-
-static LRESULT
-TREEVIEW_GetToolTips (HWND hwnd)
-
-{
- TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
-
- TRACE("\n");
- return infoPtr->hwndToolTip;
-}
-
-
-static LRESULT
-TREEVIEW_SetToolTips (HWND hwnd, WPARAM wParam)
-
-{
- TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
- HWND prevToolTip;
-
- TRACE("\n");
- prevToolTip=infoPtr->hwndToolTip;
- infoPtr->hwndToolTip= (HWND) wParam;
-
- return prevToolTip;
-}
-
-
-static LRESULT CALLBACK
-TREEVIEW_GetEditControl (HWND hwnd)
-
-{
- TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
-
- return infoPtr->hwndEdit;
-}
-
-LRESULT CALLBACK
-TREEVIEW_Edit_SubclassProc (HWND hwnd, UINT uMsg, WPARAM wParam, 
-							LPARAM lParam)
-{
-  switch (uMsg) 
-  {
-    case WM_ERASEBKGND: 
-    {
-      RECT rc;
-      HDC  hdc = (HDC) wParam;
-      GetClientRect (hwnd, &rc);
-      Rectangle (hdc, rc.left, rc.top, rc.right, rc.bottom);
-      return -1;
-    }
-
-    case WM_GETDLGCODE:
-    {
-      return DLGC_WANTARROWS | DLGC_WANTALLKEYS;
-    }
-
-    case WM_KEYDOWN:
-        if (wParam == VK_ESCAPE)
-{
-            TREEVIEW_EndEditLabelNow(GetParent(hwnd), (WPARAM)TRUE, 0); 
-            return 1;
-}
-        else if (wParam == VK_RETURN)
-            TREEVIEW_EndEditLabelNow(GetParent(hwnd), (WPARAM)FALSE, 0); 
-    break; 
-
-
-
-    default:
-    {
-      TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(GetParent(hwnd));
-	  if (infoPtr!=NULL)
-       return CallWindowProcA (infoPtr->wpEditOrig, hwnd, uMsg, wParam, lParam);
-	  else 
-		break;
-		
-    }
-  }
-
-  return 0;
-}
-
-
-/* should handle edit control messages here */
-
-static LRESULT
-TREEVIEW_Command (HWND hwnd, WPARAM wParam, LPARAM lParam)
-
-{
-  TRACE("%x %ld\n",wParam, lParam);
- 
-  switch (HIWORD(wParam)) 
-  {
-		case EN_UPDATE:
-    {
-      /* 
-       * Adjust the edit window size 
-       */
-      TREEVIEW_INFO *infoPtr  = TREEVIEW_GetInfoPtr(hwnd);
-      TREEVIEW_ITEM *editItem = TREEVIEW_ValidItem(infoPtr, infoPtr->editItem);
-      INT           iLength   = GetWindowTextLengthA(infoPtr->hwndEdit);
-      HDC           hdc       = GetDC(infoPtr->hwndEdit);
-      TEXTMETRICA   tm;
-
-      if ( GetTextMetricsA(hdc, &tm) )
-      {
-        LONG newWidth = (iLength * tm.tmAveCharWidth) + 15;
-            
-    		SetWindowPos ( 
-          infoPtr->hwndEdit,
-          HWND_TOP, 
-          editItem->text.left - 2, 
-          editItem->text.top  - 1,
-          newWidth,
-          editItem->text.bottom - editItem->text.top  + 3,
-          SWP_DRAWFRAME );
-      }
-      ReleaseDC(hwnd, hdc);
-
-      break;
-    }
-
-    case EN_KILLFOCUS:
-/*      TREEVIEW_EndEditLabelNow(hwnd, (WPARAM)FALSE, 0); 
-*/
-      break;
-
-    default:
-      return SendMessageA (GetParent (hwnd), WM_COMMAND, wParam, lParam);
-  }
-
-  return 0;
-}
-
-static LRESULT
-TREEVIEW_Size (HWND hwnd, WPARAM wParam, LPARAM lParam)
-
-{
-  TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
-
-  if (infoPtr->bAutoSize) 
-  {
-    infoPtr->bAutoSize = FALSE;
-    return 0;
-  }
-  infoPtr->bAutoSize = TRUE;
-
-  if (wParam == SIZE_RESTORED)  
-  {
-    infoPtr->uTotalWidth  = LOWORD (lParam);
-  	infoPtr->uTotalHeight = HIWORD (lParam);
-  } else {
-  	FIXME("WM_SIZE flag %x %lx not handled\n", wParam, lParam);
-  }
-
-  TREEVIEW_QueueRefresh (hwnd);
-  return 0;
-}
-
-
-
-static LRESULT
-TREEVIEW_StyleChanged (HWND hwnd, WPARAM wParam, LPARAM lParam)
-{
-  TRACE("(%x %lx)\n",wParam,lParam);
-
-  InvalidateRect(hwnd, NULL, FALSE);  
-
-  return 0;
-}
-
-static LRESULT
-TREEVIEW_Create (HWND hwnd, WPARAM wParam, LPARAM lParam)
-{
-    TREEVIEW_INFO *infoPtr;
-	DWORD dwStyle = GetWindowLongA (hwnd, GWL_STYLE);
- 	LOGFONTA logFont;
-    TEXTMETRICA tm;
-	HDC hdc;
-  
-    TRACE("wnd %x, style %lx\n",hwnd,dwStyle);
-      /* allocate memory for info structure */
-    infoPtr = (TREEVIEW_INFO *) COMCTL32_Alloc (sizeof(TREEVIEW_INFO));
-
-    SetWindowLongA( hwnd, 0, (DWORD)infoPtr);
-
-    if (infoPtr == NULL) {
-		ERR("could not allocate info memory!\n");
-		return 0;
-    }
-
-    if ((TREEVIEW_INFO*) GetWindowLongA( hwnd, 0) != infoPtr) {
-		ERR("pointer assignment error!\n");
-		return 0;
-    }
-
-	hdc=GetDC (hwnd);
-
-    /* set default settings */
-    infoPtr->uInternalStatus=0;
-    infoPtr->uNumItems=0;
-    infoPtr->clrBk   = GetSysColor (COLOR_WINDOW);
-    infoPtr->clrText = GetSysColor (COLOR_WINDOWTEXT);
-    infoPtr->clrLine = GetSysColor (COLOR_WINDOWTEXT);
-    infoPtr->clrInsertMark = GetSysColor (COLOR_BTNTEXT);
-    infoPtr->cy = 0;
-    infoPtr->cx = 0;
-    infoPtr->uIndent = 15;
-    infoPtr->himlNormal = NULL;
-    infoPtr->himlState = NULL;
-	infoPtr->uItemHeight = -1;
-    GetTextMetricsA (hdc, &tm);
-    infoPtr->hFont = GetStockObject (DEFAULT_GUI_FONT);
-	GetObjectA (infoPtr->hFont, sizeof (LOGFONTA), &logFont);
-	logFont.lfWeight=FW_BOLD;
-    infoPtr->hBoldFont = CreateFontIndirectA (&logFont);
-    
-    infoPtr->items = NULL;
-    infoPtr->selectedItem=0;
-    infoPtr->clrText=-1;	/* use system color */
-    infoPtr->dropItem=0;
-	infoPtr->insertMarkItem=0;
-	infoPtr->insertBeforeorAfter=0;
-    infoPtr->pCallBackSort=NULL;
-    infoPtr->uScrollTime = 300;  /* milliseconds */
-	infoPtr->wpEditOrig = NULL; /* we haven't subclassed anything yet */
-
-	infoPtr->hwndToolTip=0;
-    if (!(dwStyle & TVS_NOTOOLTIPS)) {   /* Create tooltip control */
-		TTTOOLINFOA ti;
-
-		infoPtr->hwndToolTip =  
-			CreateWindowExA (0, TOOLTIPS_CLASSA, NULL, 0,
-                   CW_USEDEFAULT, CW_USEDEFAULT,
-                   CW_USEDEFAULT, CW_USEDEFAULT,
-                   hwnd, 0, 0, 0);
-
-        /* Send NM_TOOLTIPSCREATED notification */
-        if (infoPtr->hwndToolTip) {
-            NMTOOLTIPSCREATED nmttc;
-
-            nmttc.hdr.hwndFrom = hwnd;
-            nmttc.hdr.idFrom =  GetWindowLongA( hwnd, GWL_ID);
-            nmttc.hdr.code = NM_TOOLTIPSCREATED;
-            nmttc.hwndToolTips = infoPtr->hwndToolTip;
-
-            SendMessageA (GetParent (hwnd), WM_NOTIFY,
-                (WPARAM) GetWindowLongA( hwnd, GWL_ID), (LPARAM)&nmttc);
-        }
-
-		ZeroMemory (&ti, sizeof(TTTOOLINFOA));
-        ti.cbSize   = sizeof(TTTOOLINFOA);
-        ti.uFlags   = TTF_IDISHWND | TTF_TRACK | TTF_TRANSPARENT ;
-        ti.hwnd     = hwnd;
-        ti.uId      = 0;
-        ti.lpszText = "Test"; /* LPSTR_TEXTCALLBACK; */
-        SetRectEmpty (&ti.rect);
-
-        SendMessageA (infoPtr->hwndToolTip, TTM_ADDTOOLA, 0, (LPARAM)&ti);
-    }
-
- 	infoPtr->hwndEdit = CreateWindowExA ( 
-                          WS_EX_LEFT, 
-                          "EDIT",
-                          0,
-                          WS_CHILD | WS_BORDER | ES_AUTOHSCROLL | 
-                          ES_WANTRETURN | ES_LEFT,
-                          0, 0, 0, 0,
-                          hwnd, 
-                          0,0,0); /* FIXME: (HMENU)IDTVEDIT,pcs->hInstance,0);*/
-
-  SendMessageA ( infoPtr->hwndEdit, WM_SETFONT, infoPtr->hFont, FALSE);
-	infoPtr->wpEditOrig = (WNDPROC)SetWindowLongA (
-                                    infoPtr->hwndEdit,
-                                    GWL_WNDPROC, 
-                           					(LONG) TREEVIEW_Edit_SubclassProc);
-
-  if (dwStyle & TVS_CHECKBOXES) {	
-		HBITMAP hbmLoad;
-		int nIndex;
-
-		infoPtr->himlState = 
-             ImageList_Create (16, 16,ILC_COLOR|ILC_MASK, 15, 1);
-
-		hbmLoad = LoadBitmapA (COMCTL32_hModule, MAKEINTRESOURCEA(IDT_CHECK));
-		TRACE ("%x\n",hbmLoad);
-        nIndex = ImageList_AddMasked (infoPtr->himlState, hbmLoad, CLR_DEFAULT);
-		TRACE ("%d\n",nIndex);
-		DeleteObject (hbmLoad);
-	}
-  ReleaseDC (hwnd, hdc);
-  return 0;
-}
-
-
-
-static LRESULT 
-TREEVIEW_Destroy (HWND hwnd) 
-{
-  TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
-     
-  TRACE("\n");
-  TREEVIEW_RemoveTree (hwnd);
-  SetWindowLongA (hwnd, 0, (DWORD)NULL);
-
-  if (infoPtr->Timer & TV_REFRESH_TIMER_SET) 
-        KillTimer (hwnd, TV_REFRESH_TIMER);
-  if (infoPtr->hwndToolTip) 
-		DestroyWindow (infoPtr->hwndToolTip);
-
-  COMCTL32_Free (infoPtr);
-  return 0;
-}
-
-
-static LRESULT
-TREEVIEW_Paint (HWND hwnd, WPARAM wParam, LPARAM lParam)
-{
-  HDC hdc;
-  PAINTSTRUCT ps;
-
-  TRACE("\n");
-  hdc = wParam==0 ? BeginPaint (hwnd, &ps) : (HDC)wParam;
-  TREEVIEW_Refresh (hwnd, hdc);
-  if(!wParam) EndPaint (hwnd, &ps);
-  TRACE("done\n");
-      
-  return 0;
-}
-
-static LRESULT
-TREEVIEW_SetFocus (HWND hwnd, WPARAM wParam, LPARAM lParam)
-{
-   TREEVIEW_SendSimpleNotify (hwnd, NM_SETFOCUS);
-   InvalidateRect(hwnd, NULL, FALSE);
-   return 0;
-}
-
-static LRESULT
-TREEVIEW_KillFocus (HWND hwnd, WPARAM wParam, LPARAM lParam)
-{
-   TREEVIEW_SendSimpleNotify (hwnd, NM_KILLFOCUS);
-   InvalidateRect(hwnd, NULL, FALSE);
-   return 0;
-}
-
-static LRESULT
-TREEVIEW_EraseBackground (HWND hwnd, WPARAM wParam, LPARAM lParam)
-{
-    TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
-    HBRUSH hBrush = CreateSolidBrush (infoPtr->clrBk);
-    RECT rect;
+    TREEVIEW_UpdateScrollBars(infoPtr);
+    TREEVIEW_QueueRefresh(infoPtr);
 
-    TRACE("\n");
-    GetClientRect (hwnd, &rect);
-    FillRect ((HDC)wParam, &rect, hBrush);
-    DeleteObject (hBrush);
     return TRUE;
 }
 
-
-
-
-
-  
-/* Notifications */
-
-  
-
-
-
 static BOOL
-TREEVIEW_SendSimpleNotify (HWND hwnd, UINT code)
+TREEVIEW_Expand(TREEVIEW_INFO *infoPtr, TREEVIEW_ITEM *wineItem,
+		BOOL bExpandPartial, BOOL bUser)
 {
-    NMHDR nmhdr;
+    TRACE("\n");
 
-    TRACE("%x\n",code);
-    nmhdr.hwndFrom = hwnd;
-    nmhdr.idFrom   = GetWindowLongA( hwnd, GWL_ID);
-    nmhdr.code     = code;
+    if (!TREEVIEW_HasChildren(infoPtr, wineItem)
+	|| wineItem->state & TVIS_EXPANDED)
+	return FALSE;
 
-    return (BOOL) SendMessageA (GetParent (hwnd), WM_NOTIFY,
-                                   (WPARAM)nmhdr.idFrom, (LPARAM)&nmhdr);
-}
+    TRACE("TVE_EXPAND %p %s\n", wineItem, TREEVIEW_ItemName(wineItem));
 
-
-
-static BOOL
-TREEVIEW_SendTreeviewNotify (HWND hwnd, UINT code, UINT action, 
-			HTREEITEM oldItem, HTREEITEM newItem)
-
-{
-  TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
-  NMTREEVIEWA nmhdr;
-  TREEVIEW_ITEM  *wineItem;
-
-  TRACE("code:%x action:%x olditem:%x newitem:%x\n",
-		  code,action,(INT)oldItem,(INT)newItem);
-  nmhdr.hdr.hwndFrom = hwnd;
-  nmhdr.hdr.idFrom =  GetWindowLongA( hwnd, GWL_ID);
-  nmhdr.hdr.code = code;
-  nmhdr.action = action;
-  if (oldItem) {
-  	wineItem=& infoPtr->items[(INT)oldItem];
-  	nmhdr.itemOld.mask 		= wineItem->mask;
-  	nmhdr.itemOld.hItem		= wineItem->hItem;
-  	nmhdr.itemOld.state		= wineItem->state;
-  	nmhdr.itemOld.stateMask	= wineItem->stateMask;
-  	nmhdr.itemOld.iImage 	= wineItem->iImage;
-  	nmhdr.itemOld.pszText 	= wineItem->pszText;
-  	nmhdr.itemOld.cchTextMax= wineItem->cchTextMax;
-  	nmhdr.itemOld.iImage 	= wineItem->iImage;
-  	nmhdr.itemOld.iSelectedImage 	= wineItem->iSelectedImage;
-  	nmhdr.itemOld.cChildren = wineItem->cChildren;
-  	nmhdr.itemOld.lParam	= wineItem->lParam;
-  }
-
-  if (newItem) {
-  	wineItem=& infoPtr->items[(INT)newItem];
-  	nmhdr.itemNew.mask 		= wineItem->mask;
-  	nmhdr.itemNew.hItem		= wineItem->hItem;
-  	nmhdr.itemNew.state		= wineItem->state;
-  	nmhdr.itemNew.stateMask	= wineItem->stateMask;
-  	nmhdr.itemNew.iImage 	= wineItem->iImage;
-  	nmhdr.itemNew.pszText 	= wineItem->pszText;
-  	nmhdr.itemNew.cchTextMax= wineItem->cchTextMax;
-  	nmhdr.itemNew.iImage 	= wineItem->iImage;
-  	nmhdr.itemNew.iSelectedImage 	= wineItem->iSelectedImage;
-  	nmhdr.itemNew.cChildren = wineItem->cChildren;
-  	nmhdr.itemNew.lParam	= wineItem->lParam;
-  }
-
-  nmhdr.ptDrag.x = 0;
-  nmhdr.ptDrag.y = 0;
-
-  return (BOOL)SendMessageA (GetParent (hwnd), WM_NOTIFY,
-                                   (WPARAM) GetWindowLongA( hwnd, GWL_ID), (LPARAM)&nmhdr);
-
-}
-
-static BOOL
-TREEVIEW_SendTreeviewDnDNotify (HWND hwnd, UINT code, HTREEITEM dragItem, 
-								POINT pt)
-{
-  TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
-  NMTREEVIEWA nmhdr;
-  TREEVIEW_ITEM  *wineItem;
-
-  TRACE("code:%x dragitem:%x\n", code,(INT)dragItem);
-
-  nmhdr.hdr.hwndFrom = hwnd;
-  nmhdr.hdr.idFrom =  GetWindowLongA( hwnd, GWL_ID);
-  nmhdr.hdr.code = code;
-  nmhdr.action = 0;
-  wineItem=& infoPtr->items[(INT)dragItem];
-  nmhdr.itemNew.mask 	= wineItem->mask;
-  nmhdr.itemNew.hItem	= wineItem->hItem;
-  nmhdr.itemNew.state	= wineItem->state;
-  nmhdr.itemNew.lParam	= wineItem->lParam;
-
-  nmhdr.ptDrag.x = pt.x;
-  nmhdr.ptDrag.y = pt.y;
-
-  return (BOOL)SendMessageA (GetParent (hwnd), WM_NOTIFY,
-                                   (WPARAM) GetWindowLongA( hwnd, GWL_ID), (LPARAM)&nmhdr);
-
-}
-
-
-
-static BOOL
-TREEVIEW_SendDispInfoNotify (HWND hwnd, TREEVIEW_ITEM *wineItem, 
-								UINT code, UINT what)
-{
-  NMTVDISPINFOA tvdi;
-  BOOL retval;
-  char *buf;
-
-  TRACE("item %d, action %x, state %d\n",
-    (INT)wineItem->hItem,
-    what,
-    (INT)wineItem->state);
-
-  tvdi.hdr.hwndFrom	= hwnd;
-  tvdi.hdr.idFrom	=  GetWindowLongA( hwnd, GWL_ID);
-  tvdi.hdr.code		= code;
-  tvdi.item.mask	= what;
-  tvdi.item.hItem	= wineItem->hItem;
-  tvdi.item.state	= wineItem->state;
-  tvdi.item.lParam	= wineItem->lParam;
-  tvdi.item.pszText = COMCTL32_Alloc (128*sizeof(char));
-  tvdi.item.cchTextMax  = 128;
-  buf = tvdi.item.pszText;
-
-  retval=(BOOL)SendMessageA (
-                  GetParent(hwnd), 
-                  WM_NOTIFY,
-                  (WPARAM)tvdi.hdr.idFrom, 
-                  (LPARAM)&tvdi);
-
-  if (what & TVIF_TEXT) {
-		wineItem->pszText        = tvdi.item.pszText;
-		if (buf==tvdi.item.pszText) {
-			wineItem->cchTextMax = 128;
-		} else { 
-			TRACE("user-supplied buffer\n");
-			COMCTL32_Free (buf);
-			wineItem->cchTextMax = 0;
-		}
+    if (bUser || !(wineItem->state & TVIS_EXPANDEDONCE))
+    {
+	if (!TREEVIEW_SendExpanding(infoPtr, wineItem, TVE_EXPAND))
+	{
+	    TRACE("  TVN_ITEMEXPANDING returned TRUE, exiting...\n");
+	    return FALSE;
 	}
-  if (what & TVIF_SELECTEDIMAGE) 
-		wineItem->iSelectedImage = tvdi.item.iSelectedImage;
-  if (what & TVIF_IMAGE) 
-		wineItem->iImage         = tvdi.item.iImage;
-  if (what & TVIF_CHILDREN) 
-		wineItem->cChildren      = tvdi.item.cChildren;
 
- return retval;
+	wineItem->state |= TVIS_EXPANDED;
+	TREEVIEW_SendExpanded(infoPtr, wineItem, TVE_EXPAND);
+	wineItem->state |= TVIS_EXPANDEDONCE;
+    }
+    else
+    {
+	/* this item has already been expanded */
+	wineItem->state |= TVIS_EXPANDED;
+    }
+
+    if (bExpandPartial)
+	FIXME("TVE_EXPANDPARTIAL not implemented\n");
+
+    TREEVIEW_RecalculateVisibleOrder(infoPtr, wineItem);
+    TREEVIEW_UpdateSubTree(infoPtr, wineItem);
+    TREEVIEW_UpdateScrollBars(infoPtr);
+
+    /* Scroll up so that as many children as possible are visible.
+     * This looses when expanding causes an HScroll bar to appear, but we
+     * don't know that yet, so the last item is obscured. */
+    if (wineItem->firstChild != NULL)
+    {
+	int nChildren = wineItem->lastChild->visibleOrder
+	    - wineItem->firstChild->visibleOrder + 1;
+
+	int visible_pos = wineItem->visibleOrder
+	    - infoPtr->firstVisible->visibleOrder;
+
+	int rows_below = TREEVIEW_GetVisibleCount(infoPtr) - visible_pos - 1;
+
+	if (visible_pos > 0 && nChildren > rows_below)
+	{
+	    int scroll = nChildren - rows_below;
+
+	    if (scroll > visible_pos)
+		scroll = visible_pos;
+
+	    if (scroll > 0)
+	    {
+		TREEVIEW_ITEM *newFirstVisible
+		    = TREEVIEW_GetListItem(infoPtr, infoPtr->firstVisible,
+					   scroll);
+
+
+		TREEVIEW_SetFirstVisible(infoPtr, newFirstVisible, TRUE);
+	    }
+	}
+    }
+
+    TREEVIEW_QueueRefresh(infoPtr);
+
+    return TRUE;
 }
 
-
-
 static BOOL
-TREEVIEW_SendCustomDrawNotify (HWND hwnd, DWORD dwDrawStage, HDC hdc,
-			RECT rc)
+TREEVIEW_Toggle(TREEVIEW_INFO *infoPtr, TREEVIEW_ITEM *wineItem, BOOL bUser)
 {
-  TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
-  NMTVCUSTOMDRAW nmcdhdr;
-  LPNMCUSTOMDRAW nmcd;
+    TRACE("\n");
 
-  TRACE("drawstage:%lx hdc:%x\n", dwDrawStage, hdc);
-
-  nmcd= & nmcdhdr.nmcd;
-  nmcd->hdr.hwndFrom = hwnd;
-  nmcd->hdr.idFrom =  GetWindowLongA( hwnd, GWL_ID);
-  nmcd->hdr.code   = NM_CUSTOMDRAW;
-  nmcd->dwDrawStage= dwDrawStage;
-  nmcd->hdc		   = hdc;
-  nmcd->rc.left    = rc.left;
-  nmcd->rc.right   = rc.right;
-  nmcd->rc.bottom  = rc.bottom;
-  nmcd->rc.top     = rc.top;
-  nmcd->dwItemSpec = 0;
-  nmcd->uItemState = 0;
-  nmcd->lItemlParam= 0;
-  nmcdhdr.clrText  = infoPtr->clrText;
-  nmcdhdr.clrTextBk= infoPtr->clrBk;
-  nmcdhdr.iLevel   = 0;
-
-  return (BOOL)SendMessageA (GetParent (hwnd), WM_NOTIFY,
-                               (WPARAM) GetWindowLongA( hwnd, GWL_ID), (LPARAM)&nmcdhdr);
-
+    if (wineItem->state & TVIS_EXPANDED)
+	return TREEVIEW_Collapse(infoPtr, wineItem, FALSE, bUser);
+    else
+	return TREEVIEW_Expand(infoPtr, wineItem, FALSE, bUser);
 }
 
-
-
-/* FIXME: need to find out when the flags in uItemState need to be set */
-
-static BOOL
-TREEVIEW_SendCustomDrawItemNotify (HWND hwnd, HDC hdc,
-  			TREEVIEW_ITEM *wineItem, UINT uItemDrawState)
+static VOID
+TREEVIEW_ExpandAll(TREEVIEW_INFO *infoPtr, TREEVIEW_ITEM *item)
 {
- TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
- NMTVCUSTOMDRAW nmcdhdr;
- LPNMCUSTOMDRAW nmcd;
- DWORD dwDrawStage,dwItemSpec;
- UINT uItemState;
- INT retval;
- 
- dwDrawStage=CDDS_ITEM | uItemDrawState;
- dwItemSpec=(DWORD)wineItem->hItem;
- uItemState=0;
- if (wineItem->hItem==infoPtr->selectedItem) uItemState|=CDIS_SELECTED;
- if (wineItem->hItem==infoPtr->focusItem)	 uItemState|=CDIS_FOCUS;
- if (wineItem->hItem==infoPtr->hotItem)      uItemState|=CDIS_HOT;
+    TREEVIEW_Expand(infoPtr, item, FALSE, TRUE);
 
- nmcd= & nmcdhdr.nmcd;
- nmcd->hdr.hwndFrom = hwnd;
- nmcd->hdr.idFrom =  GetWindowLongA( hwnd, GWL_ID);
- nmcd->hdr.code   = NM_CUSTOMDRAW;
- nmcd->dwDrawStage= dwDrawStage;
- nmcd->hdc		  = hdc;
- nmcd->rc.left    = wineItem->rect.left;
- nmcd->rc.right   = wineItem->rect.right;
- nmcd->rc.bottom  = wineItem->rect.bottom;
- nmcd->rc.top     = wineItem->rect.top;
- nmcd->dwItemSpec = dwItemSpec;
- nmcd->uItemState = uItemState;
- nmcd->lItemlParam= wineItem->lParam;
- nmcdhdr.clrText  = infoPtr->clrText;
- nmcdhdr.clrTextBk= infoPtr->clrBk;
- nmcdhdr.iLevel   = wineItem->iLevel;
-
- TRACE("drawstage:%lx hdc:%x item:%lx, itemstate:%x, lItemlParam:%lx\n",
-		  nmcd->dwDrawStage, nmcd->hdc, nmcd->dwItemSpec, 
-          nmcd->uItemState, nmcd->lItemlParam);
-
- retval=SendMessageA (GetParent (hwnd), WM_NOTIFY,
-                 (WPARAM) GetWindowLongA( hwnd, GWL_ID), (LPARAM)&nmcdhdr);
-
- infoPtr->clrText=nmcdhdr.clrText;
- infoPtr->clrBk  =nmcdhdr.clrTextBk;
- return (BOOL) retval;
+    for (item = item->firstChild; item != NULL; item = item->nextSibling)
+    {
+	if (TREEVIEW_HasChildren(infoPtr, item))
+	    TREEVIEW_ExpandAll(infoPtr, item);
+    }
 }
 
-
-
 /* Note:If the specified item is the child of a collapsed parent item,
    the parent's list of child items is (recursively) expanded to reveal the 
    specified item. This is mentioned for TREEVIEW_SelectItem; don't 
@@ -2924,716 +3036,832 @@
 */
 
 static LRESULT
-TREEVIEW_Expand (HWND hwnd, WPARAM wParam, LPARAM lParam)
+TREEVIEW_ExpandMsg(TREEVIEW_INFO *infoPtr, UINT flag, HTREEITEM wineItem)
 {
-  TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
-  TREEVIEW_ITEM *wineItem;
-  UINT flag;
-  INT expand;
-  
-  flag = (UINT) wParam;
-  expand = (INT) lParam;
+    if (!TREEVIEW_ValidItem(infoPtr, wineItem))
+	return 0;
 
-  wineItem = TREEVIEW_ValidItem (infoPtr, (HTREEITEM)expand);
+    TRACE("For (%s) item:%d, flags %x, state:%d\n",
+	      TREEVIEW_ItemName(wineItem), flag,
+	      TREEVIEW_GetItemIndex(infoPtr, wineItem), wineItem->state);
 
-  if (!wineItem) 
-    return 0;
-  if (!wineItem->cChildren) 
-    return 0;
+    switch (flag & TVE_TOGGLE)
+    {
+    case TVE_COLLAPSE:
+	return TREEVIEW_Collapse(infoPtr, wineItem, flag & TVE_COLLAPSERESET,
+				 FALSE);
 
-	if (wineItem->pszText==LPSTR_TEXTCALLBACKA) 
-		TRACE ("For item %d, flags %d, state %d\n",
-				expand, flag, wineItem->state);
-	else
-  		TRACE("For (%s) item:%d, flags %x, state:%d\n", 
-                wineItem->pszText, flag, expand, wineItem->state);
+    case TVE_EXPAND:
+	return TREEVIEW_Expand(infoPtr, wineItem, flag & TVE_EXPANDPARTIAL,
+			       FALSE);
 
-  if (wineItem->cChildren==I_CHILDRENCALLBACK) {
-    FIXME("we don't handle I_CHILDRENCALLBACK yet\n");
-    return 0;
-  }
+    case TVE_TOGGLE:
+	return TREEVIEW_Toggle(infoPtr, wineItem, TRUE);
 
-  if (flag == TVE_TOGGLE) {    /* FIXME: check exact behaviour here */
-   flag &= ~TVE_TOGGLE;    /* ie: bitwise ops or 'case' ops */
-   if (wineItem->state & TVIS_EXPANDED) 
-     flag |= TVE_COLLAPSE;
-   else
-     flag |= TVE_EXPAND;
-  }
+    default:
+	return 0;
+    }
 
-  switch (flag) 
-  {
-    case TVE_COLLAPSERESET: 
-      TRACE("  case TVE_COLLAPSERESET\n");
-      if (!wineItem->state & TVIS_EXPANDED) 
-        return 0;
-
-       wineItem->state &= ~(TVIS_EXPANDEDONCE | TVIS_EXPANDED);
-       TREEVIEW_RemoveAllChildren (hwnd, wineItem);
-       break;
-
-    case TVE_COLLAPSE: 
-      TRACE("  case TVE_COLLAPSE\n");
-      if (!wineItem->state & TVIS_EXPANDED) 
-        return 0;
-
-      wineItem->state &= ~TVIS_EXPANDED;
-      break;
-
-    case TVE_EXPAND: 
-      TRACE("  case TVE_EXPAND\n");
-      if (wineItem->state & TVIS_EXPANDED) 
-        return 0;
-
-      TRACE("  is not expanded...\n");
-     
-      if (!(wineItem->state & TVIS_EXPANDEDONCE))  
-      { 
-        TRACE("  and has never been expanded...\n");
-        wineItem->state |= TVIS_EXPANDED;
-
-        /* this item has never been expanded */
-        if (TREEVIEW_SendTreeviewNotify (
-              hwnd, 
-              TVN_ITEMEXPANDINGA, 
-              TVE_EXPAND, 
-              0, 
-              (HTREEITEM)expand))
-        {
-          TRACE("  TVN_ITEMEXPANDINGA returned TRUE, exiting...\n");
-          return FALSE;   
-        }
-
-        /* FIXME
-         * Since the TVN_ITEMEXPANDINGA message may has caused the parent to
-         * insert new items which in turn may have cause items placeholder 
-         * reallocation, I reassign the current item pointer so we have 
-         * something valid to work with... 
-         * However, this should not be necessary, 
-         * investigation required in TREEVIEW_InsertItemA
-         */
-        wineItem = TREEVIEW_ValidItem (infoPtr, (HTREEITEM)expand);
-        if (! wineItem) 
-        { 
-          ERR(
-            "Catastrophic situation, cannot retrieve item #%d\n",
-            expand);
-          return FALSE;
-        }
-
-        wineItem->state |= TVIS_EXPANDEDONCE;
-        TRACE("  TVN_ITEMEXPANDINGA sent...\n");
-
-        TREEVIEW_SendTreeviewNotify (
-          hwnd, 
-          TVN_ITEMEXPANDEDA, 
-          TVE_EXPAND, 
-          0, 
-          (HTREEITEM)expand);
-
-        TRACE("  TVN_ITEMEXPANDEDA sent...\n");
-
-      }
-      else
-      {
-        /* this item has already been expanded */
-        wineItem->state |= TVIS_EXPANDED;
-      }
-      break;
-
-    case TVE_EXPANDPARTIAL:
-      TRACE("  case TVE_EXPANDPARTIAL\n");
-      FIXME("TVE_EXPANDPARTIAL not implemented\n");
-      wineItem->state ^=TVIS_EXPANDED;
-      wineItem->state |=TVIS_EXPANDEDONCE;
-      break;
-  }
-
-  TRACE("Exiting, Item %d state is now %d...\n", 
-    expand, 
-    wineItem->state);
-  
-  TREEVIEW_QueueRefresh (hwnd);
-  return TRUE;
+#if 0
+    TRACE("Exiting, Item %p state is now %d...\n", wineItem, wineItem->state);
+#endif
 }
 
-
+/* Hit-Testing **********************************************************/
 
 static TREEVIEW_ITEM *
-TREEVIEW_HitTestPoint (HWND hwnd, POINT pt)
+TREEVIEW_HitTestPoint(TREEVIEW_INFO *infoPtr, POINT pt)
 {
- TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
- TREEVIEW_ITEM *wineItem;
- RECT rect;
+    TREEVIEW_ITEM *wineItem;
+    LONG row;
 
- GetClientRect (hwnd, &rect);
-
- if (!infoPtr->firstVisible) return NULL;
-
- wineItem=&infoPtr->items [(INT)infoPtr->firstVisible];
-
- while ((wineItem!=NULL) && (pt.y > wineItem->rect.bottom))
-       wineItem=TREEVIEW_GetNextListItem (infoPtr,wineItem);
-	
- if (!wineItem) 
+    if (!infoPtr->firstVisible)
 	return NULL;
 
- return wineItem;
-}
+    row = pt.y / infoPtr->uItemHeight + infoPtr->firstVisible->visibleOrder;
 
-
-
-
-static LRESULT
-TREEVIEW_HitTest (HWND hwnd, LPARAM lParam)
-{
-  LPTVHITTESTINFO lpht=(LPTVHITTESTINFO) lParam;
-  TREEVIEW_ITEM *wineItem;
-  RECT rect;
-  UINT status,x,y;
-
-  GetClientRect (hwnd, &rect);
-  status=0;
-  x=lpht->pt.x;
-  y=lpht->pt.y;
-  if (x < rect.left)  status|=TVHT_TOLEFT;
-  if (x > rect.right) status|=TVHT_TORIGHT;
-  if (y < rect.top )  status|=TVHT_ABOVE;
-  if (y > rect.bottom) status|=TVHT_BELOW;
-
-  if (status) {
-    lpht->flags=status;
-    return 0;
-  }
-
-  wineItem=TREEVIEW_HitTestPoint (hwnd, lpht->pt);
-  if (!wineItem) {  
-    lpht->flags=TVHT_NOWHERE;
-    return 0;
-  }
-
-  lpht->flags=0;
-
-  if (x < wineItem->expandBox.left) {
-    lpht->flags |= TVHT_ONITEMINDENT;
-	goto done;
-  } 
-  if ( PtInRect ( &wineItem->expandBox, lpht->pt)) {
-    lpht->flags |= TVHT_ONITEMBUTTON;
-	goto done;
-  }
-  if ( PtInRect ( &wineItem->bitmap, lpht->pt)) {
-	lpht->flags |= TVHT_ONITEMICON;
-    goto done;
-  }
-  if ( PtInRect ( &wineItem->statebitmap, lpht->pt)) {
-	lpht->flags |= TVHT_ONITEMSTATEICON;
-    goto done;
-  }
-  if ( PtInRect ( &wineItem->text, lpht->pt)) {
-    lpht->flags |= TVHT_ONITEMLABEL;    
-	goto done;
-  } 
-  
-  lpht->flags|=TVHT_ONITEMRIGHT;
- 
-
-done:
-  lpht->hItem=wineItem->hItem;
-  TRACE ("(%ld,%ld):result %x\n",lpht->pt.x,lpht->pt.y,lpht->flags);
-
-  return (LRESULT) wineItem->hItem;
-}
-
-static LRESULT
-TREEVIEW_EditLabelA (HWND hwnd, WPARAM wParam, LPARAM lParam)
-{
-  TREEVIEW_INFO *infoPtr      = TREEVIEW_GetInfoPtr(hwnd);
-  TREEVIEW_ITEM *wineItem;
-
-  /*
-   * If the style allow editing...
-   */
-  if ( GetWindowLongA( hwnd, GWL_STYLE) & TVS_EDITLABELS )
-  {
-
-    if ( infoPtr->editItem == 0 ) /* If we are not curently editing */
+    for (wineItem = infoPtr->firstVisible; wineItem != NULL;
+	 wineItem = TREEVIEW_GetNextListItem(infoPtr, wineItem))
     {
-      wineItem = TREEVIEW_ValidItem(infoPtr,(HTREEITEM) lParam);
-      if ( wineItem == NULL )
-        {
-        ERR("Cannot get valid TREEVIEW_ITEM for lParam\n");
-        return 0;
-        }
-
-      TRACE("Edit started for %s.\n", wineItem->pszText);
-      infoPtr->editItem = wineItem->hItem;
-
-
-      /*
-       * It is common practice for a windows program to get this
-       * edit control and then subclass it. It is assumed that a
-       * new edit control is given every time.
-       *
-       * As a result some programs really mess up the edit control
-       * so we need to destory our old edit control and create a new
-       * one. Recycling would be nice but we would need to reset
-       * everything. So recreating may just be easyier
-       *
-       */
-	  DestroyWindow(infoPtr->hwndEdit);
- 	  infoPtr->hwndEdit = CreateWindowExA (
-                          WS_EX_LEFT,
-                          "EDIT",
-                          0,
-                          WS_CHILD | WS_BORDER | ES_AUTOHSCROLL |
-                          ES_WANTRETURN | ES_LEFT,
-                          0, 0, 0, 0,
-                          hwnd,
-                          0,0,0); /* FIXME: (HMENU)IDTVEDIT,pcs->hInstance,0);*/
-
- 	  SetWindowLongA (
-      infoPtr->hwndEdit,
-      GWL_WNDPROC,
-      (LONG) TREEVIEW_Edit_SubclassProc);
-
-
-      SendMessageA ( infoPtr->hwndEdit, WM_SETFONT, infoPtr->hFont, FALSE);
-
-      SetWindowTextA( infoPtr->hwndEdit, wineItem->pszText );
-      SendMessageA  ( infoPtr->hwndEdit, EM_SETSEL, 0, -1 );
-
-      /*
-      ** NOTE: this must be after the edit control is created
-      ** (according to TVN_BEGINLABELEDITA docs), before position is set.
-      */
-      if ( TREEVIEW_SendDispInfoNotify(  /* Return true to cancel edition */
-              hwnd,
-              wineItem,
-              TVN_BEGINLABELEDITA,
-              0))
-      {
-        /*
-        ** FIXME: Is this right, should we return a handle even though edit was cancelled?
-        */
-        TRACE("Edit cancelled by TVN_BEGINLABELEDITA for %s.\n", wineItem->pszText);
-
-        TREEVIEW_EndEditLabelNow(hwnd, (WPARAM)TRUE, 0);
-
-        return 0;
-      }
-
-      SetWindowPos (
-        infoPtr->hwndEdit,
-        HWND_TOP,
-        wineItem->text.left - 2,
-        wineItem->text.top  - 1,
-        wineItem->text.right  - wineItem->text.left + 20 ,
-        wineItem->text.bottom - wineItem->text.top  + 3,
-        SWP_DRAWFRAME );
-
-      SetFocus      ( infoPtr->hwndEdit);
-      ShowWindow    ( infoPtr->hwndEdit, SW_SHOW);
-    }
-  }
-  else
-  {
-  /*
-  ** return NULL since we cannot edit this.
-  */
-
-    return 0;
-  }
-
-  return infoPtr->hwndEdit;
-}
-
-static LRESULT
-TREEVIEW_EndEditLabelNow (HWND hwnd, WPARAM wParam, LPARAM lParam)
-{
-  TREEVIEW_INFO *infoPtr    = TREEVIEW_GetInfoPtr(hwnd);
-  TREEVIEW_ITEM *editedItem = TREEVIEW_ValidItem (infoPtr, infoPtr->editItem);
-  BOOL          bRevert     = (BOOL)wParam;
-  NMTVDISPINFOA tvdi;
-  RECT          itemRect;
-  LPSTR         oldText;
-  LPSTR         newText;
-  int           iLength;
-
-  /*
-  ** NOTE: we have to get the new text before calling TVN_ENDLABELEDITA
-  ** since some apps(eg regedit, win98) validate the text in TVN_ENDLABELEDITA.
-  */
-
-  oldText = editedItem->pszText;
-
-  if ( !bRevert ) /* wParam is set to true to cancel the edition */
-  {
-
-    iLength = GetWindowTextLengthA( infoPtr->hwndEdit );
-    if (iLength == 0)
-    {
-      WARN("Zero length string for new label(not changing).");
-      bRevert=TRUE;
+	if (row >= wineItem->visibleOrder
+	    && row < wineItem->visibleOrder + wineItem->iIntegral)
+	    break;
     }
 
-    newText = COMCTL32_Alloc( iLength+1 );
-    if( newText == NULL )
-      {
-      ERR("OutOfMemory, cannot allocate space for label");
-      return FALSE;
-      }
-    GetWindowTextA( infoPtr->hwndEdit, newText, iLength+1);
-  }
-  else
-  {
-  newText=NULL;
-  }
+    return wineItem;
+}
 
+static LRESULT
+TREEVIEW_HitTest(TREEVIEW_INFO *infoPtr, LPTVHITTESTINFO lpht)
+{
+    TREEVIEW_ITEM *wineItem;
+    RECT rect;
+    UINT status;
+    LONG x, y;
 
-  /*
-  * notify our parent with the new string(or NULL if wParam==TRUE)
-  */
-  tvdi.hdr.hwndFrom	= hwnd;
-  tvdi.hdr.idFrom	= GetWindowLongA( hwnd, GWL_ID);
-  tvdi.hdr.code     = TVN_ENDLABELEDITA;
-  tvdi.item.hItem 	= editedItem->hItem;
-  tvdi.item.lParam 	= editedItem->lParam;
-  tvdi.item.mask	= TVIF_TEXT|TVIF_HANDLE|TVIF_PARAM;
-  tvdi.item.pszText	= newText;
+    lpht->hItem = 0;
+    GetClientRect(infoPtr->hwnd, &rect);
+    status = 0;
+    x = lpht->pt.x;
+    y = lpht->pt.y;
 
-  if(!SendMessageA (                /* return false to cancel edition */
-              GetParent(hwnd),
-              WM_NOTIFY,
-              (WPARAM)tvdi.hdr.idFrom,
-              (LPARAM)&tvdi))
-  {
-  if( newText == NULL )  /*we are supposed to ignore the return if (and pszText==NULL), MSDOCs */
-    bRevert=TRUE;
-  }
-
-  if (oldText != LPSTR_TEXTCALLBACKA)
-  {
-
-    if( bRevert )
+    if (x < rect.left)
     {
-    if( newText != NULL )
-      COMCTL32_Free(newText);
+	status |= TVHT_TOLEFT;
+    }
+    else if (x > rect.right)
+    {
+	status |= TVHT_TORIGHT;
+    }
 
-    editedItem->pszText=oldText; /* revert back to the old label */
+    if (y < rect.top)
+    {
+	status |= TVHT_ABOVE;
+    }
+    else if (y > rect.bottom)
+    {
+	status |= TVHT_BELOW;
+    }
+
+    if (status)
+    {
+	lpht->flags = status;
+	return (LRESULT)(HTREEITEM)NULL;
+    }
+
+    wineItem = TREEVIEW_HitTestPoint(infoPtr, lpht->pt);
+    if (!wineItem)
+    {
+	lpht->flags = TVHT_NOWHERE;
+	return (LRESULT)(HTREEITEM)NULL;
+    }
+
+    if (x >= wineItem->textOffset + wineItem->textWidth)
+    {
+	lpht->flags = TVHT_ONITEMRIGHT;
+    }
+    else if (x >= wineItem->textOffset)
+    {
+	lpht->flags = TVHT_ONITEMLABEL;
+    }
+    else if (x >= wineItem->imageOffset)
+    {
+	lpht->flags = TVHT_ONITEMICON;
+    }
+    else if (x >= wineItem->stateOffset)
+    {
+	lpht->flags = TVHT_ONITEMSTATEICON;
+    }
+    else if (x >= wineItem->linesOffset && infoPtr->dwStyle & TVS_HASBUTTONS)
+    {
+	lpht->flags = TVHT_ONITEMBUTTON;
     }
     else
     {
-    COMCTL32_Free(oldText);
-
-    editedItem->pszText=newText; /* use the new label */
+	lpht->flags = TVHT_ONITEMINDENT;
     }
-  }
-  else if( newText!=NULL )
-  {
-    /*
-    ** Is really this necessary? shouldnt an app update its internal data in TVN_ENDLABELEDITA?
-    */
-    if( !bRevert )
+
+    lpht->hItem = wineItem;
+    TRACE("(%ld,%ld):result %x\n", lpht->pt.x, lpht->pt.y, lpht->flags);
+
+    return (LRESULT)wineItem;
+}
+
+/* Item Label Editing ***************************************************/
+
+static LRESULT
+TREEVIEW_GetEditControl(TREEVIEW_INFO *infoPtr)
+{
+    return infoPtr->hwndEdit;
+}
+
+static LRESULT CALLBACK
+TREEVIEW_Edit_SubclassProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
+{
+    TREEVIEW_INFO *infoPtr;
+    BOOL bCancel = FALSE;
+
+    switch (uMsg)
     {
-      /*
-      * This is a callback string so we need
-      * to inform the parent that the string
-      * has changed
-      *
-      */
-      tvdi.hdr.hwndFrom	= hwnd;
-      tvdi.hdr.idFrom	= GetWindowLongA( hwnd, GWL_ID);
-      tvdi.hdr.code	    = TVN_SETDISPINFOA;
-      tvdi.item.mask	= TVIF_TEXT;
-      tvdi.item.pszText	= newText;
+    case WM_PAINT:
+	{
+	    LRESULT rc;
+	    TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(GetParent(hwnd));
 
-      SendMessageA (
-              GetParent(hwnd),
-              WM_NOTIFY,
-              (WPARAM)tvdi.hdr.idFrom,
-              (LPARAM)&tvdi);
+	    TRACE("WM_PAINT start\n");
+	    rc = CallWindowProcA(infoPtr->wpEditOrig, hwnd, uMsg, wParam,
+				 lParam);
+	    TRACE("WM_PAINT done\n");
+	    return rc;
+	}
 
+    case WM_KILLFOCUS:
+    {
+	TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(GetParent(hwnd));
+	if (infoPtr->bIgnoreEditKillFocus)
+	    return TRUE;
+
+	break;
     }
 
-  COMCTL32_Free(newText);
-  }
-	
+    case WM_GETDLGCODE:
+	return DLGC_WANTARROWS | DLGC_WANTALLKEYS;
 
-  ShowWindow(infoPtr->hwndEdit, SW_HIDE);
-  EnableWindow(infoPtr->hwndEdit, FALSE);
+    case WM_KEYDOWN:
+	if (wParam == (WPARAM)VK_ESCAPE)
+	{
+	    bCancel = TRUE;
+	    break;
+	}
+	else if (wParam == (WPARAM)VK_RETURN)
+	{
+	    break;
+	}
 
-  /* update the window to eliminate fragments and the like */
-  TreeView_GetItemRect(hwnd,infoPtr->editItem,&itemRect,FALSE);
-  RedrawWindow(hwnd,&itemRect,0,RDW_ERASE|RDW_INVALIDATE|RDW_UPDATENOW);
+	/* fall through */
+    default:
+	{
+	    TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(GetParent(hwnd));
 
-  infoPtr->editItem = 0;
+	    return CallWindowProcA(infoPtr->wpEditOrig, hwnd, uMsg, wParam,
+				   lParam);
+	}
+    }
 
-  return !bRevert; /* return true if label edit succesful, otherwise false */
-}
+    /* Processing TVN_ENDLABELEDIT message could kill the focus       */
+    /* eg. Using a messagebox                                         */
 
+    infoPtr = TREEVIEW_GetInfoPtr(GetParent(hwnd));
+    infoPtr->bIgnoreEditKillFocus = TRUE;
+    TREEVIEW_EndEditLabelNow(infoPtr, bCancel || !infoPtr->bLabelChanged);
+    infoPtr->bIgnoreEditKillFocus = FALSE;
 
-
-static LRESULT
-TREEVIEW_LButtonDoubleClick (HWND hwnd, WPARAM wParam, LPARAM lParam)
-{
-  TREEVIEW_ITEM *wineItem;
-  POINT pt;
-
-  TRACE("\n");
-  pt.x = (INT)LOWORD(lParam);
-  pt.y = (INT)HIWORD(lParam);
-  SetFocus (hwnd);
-
-  wineItem=TREEVIEW_HitTestPoint (hwnd, pt);
-  if (!wineItem) return 0;
-  TRACE("item %d \n",(INT)wineItem->hItem);
- 
-  if (TREEVIEW_SendSimpleNotify (hwnd, NM_DBLCLK)!=TRUE) {     /* FIXME!*/
-  	TREEVIEW_Expand (hwnd, (WPARAM) TVE_TOGGLE, (LPARAM) wineItem->hItem);
- }
- return TRUE;
-}
-
-
-static LRESULT
-TREEVIEW_LButtonDown (HWND hwnd, WPARAM wParam, LPARAM lParam)
-{
-  TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
-  INT iItem;
-  TVHITTESTINFO ht;
-
-  ht.pt.x = (INT)LOWORD(lParam);
-  ht.pt.y = (INT)HIWORD(lParam);
-
-  SetFocus (hwnd);
-  iItem=TREEVIEW_HitTest (hwnd, (LPARAM) &ht);
-  TRACE("item %d \n",iItem);
-
-  if (ht.flags & TVHT_ONITEMBUTTON) {
-    TREEVIEW_Expand (hwnd, (WPARAM) TVE_TOGGLE, (LPARAM) iItem);
-  }
-  else
-  {
-    infoPtr->uInternalStatus|=TV_LDRAG;
-  }
-  
-  return 0;
-}
-
-static LRESULT
-TREEVIEW_LButtonUp (HWND hwnd, WPARAM wParam, LPARAM lParam)
-{
-  TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
-  INT           iItem;
-  TREEVIEW_ITEM *wineItem;
-  TVHITTESTINFO ht;
-  DWORD dwStyle = GetWindowLongA (hwnd, GWL_STYLE);
- 
-  ht.pt.x = (INT)LOWORD(lParam);
-  ht.pt.y = (INT)HIWORD(lParam);
- 
-  TRACE("\n");
-
-  /* Return true to cancel default behaviour */
-  if ( TREEVIEW_SendSimpleNotify (hwnd, NM_CLICK) )
     return 0;
+}
 
-  /* Get the item */
-  iItem = TREEVIEW_HitTest (hwnd, (LPARAM) &ht);
-  TRACE ("%d\n",iItem);
-  if (!iItem) 
+
+/* should handle edit control messages here */
+
+static LRESULT
+TREEVIEW_Command(TREEVIEW_INFO *infoPtr, WPARAM wParam, LPARAM lParam)
+{
+    TRACE("%x %ld\n", wParam, lParam);
+
+    switch (HIWORD(wParam))
+    {
+    case EN_UPDATE:
+	{
+	    /* 
+	     * Adjust the edit window size 
+	     */
+	    char buffer[1024];
+	    TREEVIEW_ITEM *editItem = infoPtr->selectedItem;
+	    HDC hdc = GetDC(infoPtr->hwndEdit);
+	    SIZE sz;
+	    int len;
+	    HFONT hFont, hOldFont = 0;
+
+	    infoPtr->bLabelChanged = TRUE;
+
+	    len = GetWindowTextA(infoPtr->hwndEdit, buffer, sizeof(buffer));
+
+	    /* Select font to get the right dimension of the string */
+	    hFont = SendMessageA(infoPtr->hwndEdit, WM_GETFONT, 0, 0);
+	    if (hFont != 0)
+	    {
+		hOldFont = SelectObject(hdc, hFont);
+	    }
+
+	    if (GetTextExtentPoint32A(hdc, buffer, strlen(buffer), &sz))
+	    {
+		TEXTMETRICA textMetric;
+
+		/* Add Extra spacing for the next character */
+		GetTextMetricsA(hdc, &textMetric);
+		sz.cx += (textMetric.tmMaxCharWidth * 2);
+
+		sz.cx = max(sz.cx, textMetric.tmMaxCharWidth * 3);
+		sz.cx = min(sz.cx,
+			    infoPtr->clientWidth - editItem->textOffset + 2);
+
+		SetWindowPos(infoPtr->hwndEdit,
+			     HWND_TOP,
+			     0,
+			     0,
+			     sz.cx,
+			     editItem->rect.bottom - editItem->rect.top + 3,
+			     SWP_NOMOVE | SWP_DRAWFRAME);
+	    }
+
+	    if (hFont != 0)
+	    {
+		SelectObject(hdc, hOldFont);
+	    }
+
+	    ReleaseDC(infoPtr->hwnd, hdc);
+	    break;
+	}
+
+    default:
+	return SendMessageA(GetParent(infoPtr->hwnd), WM_COMMAND, wParam, lParam);
+    }
+
     return 0;
+}
 
-  wineItem = TREEVIEW_ValidItem(infoPtr, (HTREEITEM)iItem);
- 
-  /* 
-   * if we are TVS_SINGLEEXPAND then we want this single click to 
-   * do a bunch of things.
-   */
-  if ((dwStyle & TVS_SINGLEEXPAND)&&
-      ( ht.flags & (TVHT_ONITEMLABEL | TVHT_ONITEMICON))&&
-      ( infoPtr->editItem == 0 ))
-  {
-    TREEVIEW_ITEM *SelItem;
-    /*
-     * Send the notification
-     */
-    TREEVIEW_SendTreeviewNotify (hwnd,TVN_SINGLEEXPAND,0,
-                                 (HTREEITEM)iItem,0);
-    /*
-     * Close the previous selection all the way to the root
-     * as long as the new selection is not a child
-     */
-    
-    if ((infoPtr->selectedItem)&&(infoPtr->selectedItem != (HTREEITEM)iItem))
+static HWND
+TREEVIEW_EditLabelA(TREEVIEW_INFO *infoPtr, HTREEITEM hItem)
+{
+    HWND hwnd = infoPtr->hwnd;
+    HWND hwndEdit;
+    SIZE sz;
+    TREEVIEW_ITEM *editItem = hItem;
+    HINSTANCE hinst = GetWindowLongA(hwnd, GWL_HINSTANCE);
+    HDC hdc;
+    HFONT hOldFont=0;
+    TEXTMETRICA textMetric;
+
+    TRACE("%x %p\n", (unsigned)hwnd, hItem);
+    if (!TREEVIEW_ValidItem(infoPtr, editItem))
+	return (HWND)NULL;
+
+    if (infoPtr->hwndEdit)
+	return infoPtr->hwndEdit;
+
+    infoPtr->bLabelChanged = FALSE;
+
+    /* Make sure that edit item is selected */
+    TREEVIEW_DoSelectItem(infoPtr, TVGN_CARET, hItem, TVC_UNKNOWN);
+    TREEVIEW_EnsureVisible(infoPtr, hItem, TRUE);
+
+    TREEVIEW_UpdateDispInfo(infoPtr, editItem, TVIF_TEXT);
+
+    hdc = GetDC(hwnd);
+    /* Select the font to get appropriate metric dimensions */
+    if (infoPtr->hFont != 0)
     {
-      BOOL closeit = TRUE;
-      SelItem = wineItem;
-
-      while (closeit && SelItem)
-      {
-        closeit = (SelItem->hItem != infoPtr->selectedItem);
-        SelItem = TREEVIEW_ValidItem(infoPtr,SelItem->parent);
-      }
-
-      if (closeit)
-      {
-        SelItem = TREEVIEW_ValidItem(infoPtr,infoPtr->selectedItem);
-        while ((SelItem)&&(SelItem->hItem != wineItem->hItem))
-        {
-          TREEVIEW_Expand (hwnd,(WPARAM)TVE_COLLAPSE,(LPARAM)SelItem->hItem);
-          SelItem = TREEVIEW_ValidItem(infoPtr,SelItem->parent);
-        }
-      }
+	hOldFont = SelectObject(hdc, infoPtr->hFont);
     }
-   
-    /*
-     * Expand the current item 
-     */
-    TREEVIEW_Expand (hwnd, (WPARAM) TVE_TOGGLE, (LPARAM) wineItem->hItem);
-  }
 
-  infoPtr->uInternalStatus &= ~(TV_LDRAG | TV_LDRAGGING);
+    /*Get String Lenght in pixels */
+    GetTextExtentPoint32A(hdc, editItem->pszText, strlen(editItem->pszText),
+			  &sz);
 
-  /* 
-   * If the style allow editing and the node is already selected 
-   * and the click occured on the item label...
-   */
-  if ( ( GetWindowLongA( hwnd, GWL_STYLE) & TVS_EDITLABELS ) && 
-       ( wineItem->state & TVIS_SELECTED ) &&
-       ( ht.flags & TVHT_ONITEMLABEL ))
-  {
-    if ( infoPtr->editItem == 0 ) /* If we are not curently editing */
+    /*Add Extra spacing for the next character */
+    GetTextMetricsA(hdc, &textMetric);
+    sz.cx += (textMetric.tmMaxCharWidth * 2);
+
+    sz.cx = max(sz.cx, textMetric.tmMaxCharWidth * 3);
+    sz.cx = min(sz.cx, infoPtr->clientWidth - editItem->textOffset + 2);
+
+    if (infoPtr->hFont != 0)
     {
-    if( SendMessageA(hwnd, TVM_EDITLABELA, 0, (LPARAM)iItem) == 0)
-        return 0;
+	SelectObject(hdc, hOldFont);
     }
-  }
-  else if ( infoPtr->editItem != 0 ) /* If we are curently editing */
-  {
-    TREEVIEW_EndEditLabelNow(hwnd, (WPARAM)FALSE, 0);
-  }
-  else if ( ht.flags & (TVHT_ONITEMLABEL | TVHT_ONITEMICON))
-  {
-    TREEVIEW_DoSelectItem ( hwnd, TVGN_CARET, (HTREEITEM)iItem, TVC_BYMOUSE);
-  }
 
-  if (ht.flags & TVHT_ONITEMSTATEICON) {
+    ReleaseDC(hwnd, hdc);
+    hwndEdit = CreateWindowExA(WS_EX_LEFT,
+			       "EDIT",
+			       0,
+			       WS_CHILD | WS_BORDER | ES_AUTOHSCROLL |
+			       WS_CLIPSIBLINGS | ES_WANTRETURN |
+			       ES_LEFT, editItem->textOffset - 2,
+			       editItem->rect.top - 1, sz.cx + 3,
+			       editItem->rect.bottom -
+			       editItem->rect.top + 3, hwnd, 0, hinst, 0);
+/* FIXME: (HMENU)IDTVEDIT,pcs->hInstance,0); */
 
-	
-	if (dwStyle & TVS_CHECKBOXES) {      /* TVS_CHECKBOXES requires _us_ */
-			int state;					 /* to toggle the current state */
-			state=1-(wineItem->state>>12);
-			TRACE ("state:%x\n", state);
-			wineItem->state&= ~TVIS_STATEIMAGEMASK;
-			wineItem->state|=state<<12;
-			TRACE ("state:%x\n", wineItem->state);
-			TREEVIEW_QueueRefresh (hwnd);
+    infoPtr->hwndEdit = hwndEdit;
+
+    /* Get a 2D border. */
+    SetWindowLongA(hwndEdit, GWL_EXSTYLE,
+		   GetWindowLongA(hwndEdit, GWL_EXSTYLE) & ~WS_EX_CLIENTEDGE);
+    SetWindowLongA(hwndEdit, GWL_STYLE,
+		   GetWindowLongA(hwndEdit, GWL_STYLE) | WS_BORDER);
+
+    SendMessageA(hwndEdit, WM_SETFONT, TREEVIEW_FontForItem(infoPtr, editItem),
+		 FALSE);
+
+    infoPtr->wpEditOrig = (WNDPROC)SetWindowLongA(hwndEdit, GWL_WNDPROC,
+						  (DWORD)
+						  TREEVIEW_Edit_SubclassProc);
+
+    if (TREEVIEW_BeginLabelEditNotify(infoPtr, editItem))
+    {
+	DestroyWindow(hwndEdit);
+	infoPtr->hwndEdit = 0;
+	return (HWND)NULL;
+    }
+
+    infoPtr->selectedItem = hItem;
+    SetWindowTextA(hwndEdit, editItem->pszText);
+    SetFocus(hwndEdit);
+    SendMessageA(hwndEdit, EM_SETSEL, 0, -1);
+    ShowWindow(hwndEdit, SW_SHOW);
+
+    return hwndEdit;
+}
+
+
+static LRESULT
+TREEVIEW_EndEditLabelNow(TREEVIEW_INFO *infoPtr, BOOL bCancel)
+{
+    HWND hwnd = infoPtr->hwnd;
+    TREEVIEW_ITEM *editedItem = infoPtr->selectedItem;
+    NMTVDISPINFOA tvdi;
+    BOOL bCommit;
+    char tmpText[1024] = { '\0' };
+    int iLength = 0;
+
+    if (!infoPtr->hwndEdit)
+	return FALSE;
+
+    tvdi.hdr.hwndFrom = hwnd;
+    tvdi.hdr.idFrom = GetWindowLongA(hwnd, GWL_ID);
+    tvdi.hdr.code = TVN_ENDLABELEDITA;
+    tvdi.item.mask = 0;
+    tvdi.item.hItem = editedItem;
+    tvdi.item.state = editedItem->state;
+    tvdi.item.lParam = editedItem->lParam;
+
+    if (!bCancel)
+    {
+	iLength = GetWindowTextA(infoPtr->hwndEdit, tmpText, 1023);
+
+	if (iLength >= 1023)
+	{
+	    ERR("Insuficient space to retrieve new item label.");
+	}
+
+	tvdi.item.pszText = tmpText;
+	tvdi.item.cchTextMax = iLength + 1;
+    }
+    else
+    {
+	tvdi.item.pszText = NULL;
+	tvdi.item.cchTextMax = 0;
+    }
+
+    bCommit = (BOOL)SendMessageA(GetParent(hwnd),
+				 WM_NOTIFY,
+				 (WPARAM)tvdi.hdr.idFrom, (LPARAM)&tvdi);
+
+    if (!bCancel && bCommit)	/* Apply the changes */
+    {
+	if (strcmp(tmpText, editedItem->pszText) != 0)
+	{
+	    if (NULL == COMCTL32_ReAlloc(editedItem->pszText, iLength + 1))
+	    {
+		ERR("OutOfMemory, cannot allocate space for label");
+		DestroyWindow(infoPtr->hwndEdit);
+		infoPtr->hwndEdit = 0;
+		return FALSE;
+	    }
+	    else
+	    {
+		editedItem->cchTextMax = iLength + 1;
+		lstrcpyA(editedItem->pszText, tmpText);
+	    }
+	}
+    }
+
+    ShowWindow(infoPtr->hwndEdit, SW_HIDE);
+    DestroyWindow(infoPtr->hwndEdit);
+    infoPtr->hwndEdit = 0;
+    return TRUE;
+}
+
+static LRESULT
+TREEVIEW_HandleTimer(TREEVIEW_INFO *infoPtr, WPARAM wParam)
+{
+    if (wParam != TV_EDIT_TIMER)
+    {
+	ERR("got unknown timer\n");
+	return 1;
+    }
+
+    KillTimer(infoPtr->hwnd, TV_EDIT_TIMER);
+    infoPtr->Timer &= ~TV_EDIT_TIMER_SET;
+
+    TREEVIEW_EditLabelA(infoPtr, infoPtr->selectedItem);
+
+    return 0;
+}
+
+
+/* Mouse Tracking/Drag **************************************************/
+
+/***************************************************************************
+ * This is quite unusual piece of code, but that's how it's implemented in
+ * Windows.
+ */
+static LRESULT
+TREEVIEW_TrackMouse(TREEVIEW_INFO *infoPtr, POINT pt)
+{
+    INT cxDrag = GetSystemMetrics(SM_CXDRAG);
+    INT cyDrag = GetSystemMetrics(SM_CYDRAG);
+    RECT r;
+    MSG msg;
+
+    r.top = pt.y - cyDrag;
+    r.left = pt.x - cxDrag;
+    r.bottom = pt.y + cyDrag;
+    r.right = pt.x + cxDrag;
+
+    SetCapture(infoPtr->hwnd);
+
+    while (1)
+    {
+	if (PeekMessageA(&msg, 0, 0, 0, PM_REMOVE | PM_NOYIELD))
+	{
+	    if (msg.message == WM_MOUSEMOVE)
+	    {
+		pt.x = (LONG)(INT16)LOWORD(msg.lParam);
+		pt.y = (LONG)(INT16)HIWORD(msg.lParam);
+		if (PtInRect(&r, pt))
+		    continue;
+		else
+		{
+		    ReleaseCapture();
+		    return 1;
 		}
-  }
-  return 0;
+	    }
+	    else if (msg.message >= WM_LBUTTONDOWN &&
+		     msg.message <= WM_RBUTTONDBLCLK)
+	    {
+		if (msg.message == WM_RBUTTONUP)
+		    TREEVIEW_RButtonUp(infoPtr, &pt);
+		break;
+	    }
+
+	    DispatchMessageA(&msg);
+	}
+
+	if (GetCapture() != infoPtr->hwnd)
+	    return 0;
+    }
+
+    ReleaseCapture();
+    return 0;
 }
 
 
 static LRESULT
-TREEVIEW_RButtonDown (HWND hwnd, WPARAM wParam, LPARAM lParam)
+TREEVIEW_LButtonDoubleClick(TREEVIEW_INFO *infoPtr, LPARAM lParam)
 {
- TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
+    TREEVIEW_ITEM *wineItem;
+    TVHITTESTINFO hit;
 
- TRACE("\n");
- infoPtr->uInternalStatus|=TV_RDRAG;
- return 0;
-}
+    TRACE("\n");
+    SetFocus(infoPtr->hwnd);
 
-static LRESULT
-TREEVIEW_RButtonUp (HWND hwnd, WPARAM wParam, LPARAM lParam)
-{
- TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
+    if (infoPtr->Timer & TV_EDIT_TIMER_SET)
+    {
+	/* If there is pending 'edit label' event - kill it now */
+	KillTimer(infoPtr->hwnd, TV_EDIT_TIMER);
+    }
 
- TRACE("\n");
- if (TREEVIEW_SendSimpleNotify (hwnd, NM_RCLICK)) return 0;
- infoPtr->uInternalStatus&= ~(TV_RDRAG | TV_RDRAGGING);
- return 0;
-}
+    hit.pt.x = (LONG)(INT16)LOWORD(lParam);
+    hit.pt.y = (LONG)(INT16)HIWORD(lParam);
 
-
-static LRESULT
-TREEVIEW_MouseMove (HWND hwnd, WPARAM wParam, LPARAM lParam)
-{
- TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
- TREEVIEW_ITEM *hotItem;
- POINT pt;
-
- pt.x=(INT) LOWORD (lParam);
- pt.y=(INT) HIWORD (lParam);
- hotItem=TREEVIEW_HitTestPoint (hwnd, pt);
- if (!hotItem) return 0;
- infoPtr->focusItem=hotItem->hItem;
-
- if ( GetWindowLongA( hwnd, GWL_STYLE) & TVS_DISABLEDRAGDROP) return 0;
-
- if (infoPtr->uInternalStatus & TV_LDRAG) {
-	TREEVIEW_SendTreeviewDnDNotify (hwnd, TVN_BEGINDRAGA, hotItem->hItem, pt);
-	infoPtr->uInternalStatus &= ~TV_LDRAG;
-	infoPtr->uInternalStatus |= TV_LDRAGGING;
-	infoPtr->dropItem=hotItem->hItem;
+    wineItem = (TREEVIEW_ITEM *)TREEVIEW_HitTest(infoPtr, &hit);
+    if (!wineItem)
 	return 0;
- }
+    TRACE("item %d \n", TREEVIEW_GetItemIndex(infoPtr, wineItem));
 
- if (infoPtr->uInternalStatus & TV_RDRAG) {
-	TREEVIEW_SendTreeviewDnDNotify (hwnd, TVN_BEGINRDRAGA, hotItem->hItem, pt);
-	infoPtr->uInternalStatus &= ~TV_RDRAG;
-	infoPtr->uInternalStatus |= TV_RDRAGGING;
-	infoPtr->dropItem=hotItem->hItem;
-	return 0;
- }
- 
- return 0;
+    if (TREEVIEW_SendSimpleNotify(infoPtr, NM_DBLCLK) == FALSE)
+    {				/* FIXME! */
+	switch (hit.flags)
+	{
+	case TVHT_ONITEMRIGHT:
+	    /* FIXME: we should not have send NM_DBLCLK in this case. */
+	    break;
+
+	case TVHT_ONITEMINDENT:
+	    if (!(infoPtr->dwStyle & TVS_HASLINES))
+	    {
+		break;
+	    }
+	    else
+	    {
+		int level = hit.pt.x / infoPtr->uIndent;
+		if (!(infoPtr->dwStyle & TVS_LINESATROOT)) level++;
+
+		while (wineItem->iLevel > level)
+		{
+		    wineItem = wineItem->parent;
+		}
+
+		/* fall through */
+	    }
+
+	case TVHT_ONITEMLABEL:
+	case TVHT_ONITEMICON:
+	case TVHT_ONITEMBUTTON:
+	    TREEVIEW_Toggle(infoPtr, wineItem, TRUE);
+	    break;
+
+	case TVHT_ONITEMSTATEICON:
+	   if (infoPtr->dwStyle & TVS_CHECKBOXES)
+	       TREEVIEW_ToggleItemState(infoPtr, wineItem);
+	   else
+	       TREEVIEW_Toggle(infoPtr, wineItem, TRUE);
+	   break;
+	}
+    }
+    return TRUE;
 }
 
 
 static LRESULT
-TREEVIEW_CreateDragImage (HWND hwnd, WPARAM wParam, LPARAM lParam)
+TREEVIEW_LButtonDown(TREEVIEW_INFO *infoPtr, LPARAM lParam)
 {
- TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
- TREEVIEW_ITEM *dragItem;
- INT cx,cy;
- HDC    hdc,htopdc;
- HWND hwtop;
- HBITMAP hbmp,hOldbmp;
- SIZE  size;
- RECT  rc;
- HFONT hOldFont;
- char    *itemtxt;
- 
- TRACE("\n");
- if (!(infoPtr->himlNormal))  return 0;
- dragItem=TREEVIEW_ValidItem (infoPtr, (HTREEITEM) lParam);
- 
- if (!dragItem) return 0;
+    HWND hwnd = infoPtr->hwnd;
+    TVHITTESTINFO ht;
+    BOOL bTrack;
+    HTREEITEM tempItem;
 
- if (dragItem->pszText==LPSTR_TEXTCALLBACKA) {
-     TREEVIEW_SendDispInfoNotify (hwnd, dragItem, TVN_GETDISPINFOA, TVIF_TEXT);
- }
- itemtxt=dragItem->pszText;
+    /* If Edit control is active - kill it and return.
+     * The best way to do it is to set focus to itself.
+     * Edit control subclassed procedure will automatically call
+     * EndEditLabelNow.
+     */
+    if (infoPtr->hwndEdit)
+    {
+	SetFocus(hwnd);
+	return 0;
+    }
 
- hwtop=GetDesktopWindow ();
- htopdc= GetDC (hwtop);
- hdc=CreateCompatibleDC (htopdc); 
- 
- hOldFont=SelectObject (hdc, infoPtr->hFont);
- GetTextExtentPoint32A (hdc, itemtxt, lstrlenA (itemtxt), &size);
- TRACE("%d %d %s %d\n",size.cx,size.cy,itemtxt,lstrlenA(itemtxt));
- hbmp=CreateCompatibleBitmap (htopdc, size.cx, size.cy);
- hOldbmp=SelectObject (hdc, hbmp);
+    ht.pt.x = (LONG)(INT16)LOWORD(lParam);
+    ht.pt.y = (LONG)(INT16)HIWORD(lParam);
 
- ImageList_GetIconSize (infoPtr->himlNormal, &cx, &cy);
- size.cx+=cx;
- if (cy>size.cy) size.cy=cy;
+    TREEVIEW_HitTest(infoPtr, &ht);
+    TRACE("item %d \n", TREEVIEW_GetItemIndex(infoPtr, ht.hItem));
 
- infoPtr->dragList=ImageList_Create (size.cx, size.cy, ILC_COLOR, 10, 10);
- ImageList_Draw (infoPtr->himlNormal, dragItem->iImage, hdc, 0, 0, ILD_NORMAL);
+    /* update focusedItem and redraw both items */
+    if(ht.hItem && (ht.flags & TVHT_ONITEM))
+    {
+        infoPtr->focusedItem = ht.hItem;
+        InvalidateRect(hwnd, &(((HTREEITEM)(ht.hItem))->rect), TRUE);
+
+        if(infoPtr->selectedItem)
+            InvalidateRect(hwnd, &(infoPtr->selectedItem->rect), TRUE);
+    }
+
+    bTrack = (ht.flags & TVHT_ONITEM)
+	&& !(infoPtr->dwStyle & TVS_DISABLEDRAGDROP);
+
+    /* Send NM_CLICK right away */
+    if (!bTrack)
+	if (TREEVIEW_SendSimpleNotify(infoPtr, NM_CLICK))
+	    goto setfocus;
+
+    if (ht.flags & TVHT_ONITEMBUTTON)
+    {
+	TREEVIEW_Toggle(infoPtr, ht.hItem, TRUE);
+	goto setfocus;
+    }
+    else if (bTrack)
+    {   /* if TREEVIEW_TrackMouse == 1 dragging occured and the cursor left the dragged item's rectangle */
+	if (TREEVIEW_TrackMouse(infoPtr, ht.pt))
+	{
+	    TREEVIEW_SendTreeviewDnDNotify(infoPtr, TVN_BEGINDRAGA, ht.hItem,
+					   ht.pt);
+	    infoPtr->dropItem = ht.hItem;
+
+            /* clean up focuedItem as we dragged and won't select this item */
+            if(infoPtr->focusedItem)
+            {
+                /* refresh the item that was focused */
+                tempItem = infoPtr->focusedItem;
+                infoPtr->focusedItem = 0;
+                InvalidateRect(infoPtr->hwnd, &tempItem->rect, TRUE);
+
+                /* refresh the selected item to return the filled background */
+                InvalidateRect(infoPtr->hwnd, &(infoPtr->selectedItem->rect), TRUE);
+            }
+
+	    return 0;
+        }
+    }
+
+    if (TREEVIEW_SendSimpleNotify(infoPtr, NM_CLICK))
+        goto setfocus;
+
+    /* 
+     * If the style allow editing and the node is already selected 
+     * and the click occured on the item label...
+     */
+    if ((infoPtr->dwStyle & TVS_EDITLABELS) &&
+	        (ht.flags & TVHT_ONITEMLABEL) && (infoPtr->selectedItem == ht.hItem))
+    {
+	if (infoPtr->Timer & TV_EDIT_TIMER_SET)
+	    KillTimer(hwnd, TV_EDIT_TIMER);
+
+	SetTimer(hwnd, TV_EDIT_TIMER, GetDoubleClickTime(), 0);
+	infoPtr->Timer |= TV_EDIT_TIMER_SET;
+    }
+    else if (ht.flags & TVHT_ONITEM) /* select the item if the hit was inside of the icon or text */
+    {
+        /*
+         * if we are TVS_SINGLEEXPAND then we want this single click to
+         * do a bunch of things.
+         */
+        if((infoPtr->dwStyle & TVS_SINGLEEXPAND) &&
+          (infoPtr->hwndEdit == 0))
+        {
+            TREEVIEW_ITEM *SelItem;
+
+            /*
+             * Send the notification
+             */
+            TREEVIEW_SendTreeviewNotify(infoPtr, TVN_SINGLEEXPAND, TVIF_HANDLE | TVIF_PARAM,
+                                0, ht.hItem, 0);
+            /*
+             * Close the previous selection all the way to the root
+             * as long as the new selection is not a child
+             */
+
+            if((infoPtr->selectedItem)
+                && (infoPtr->selectedItem != ht.hItem))
+            {
+                BOOL closeit = TRUE;
+                SelItem = ht.hItem;
+
+                while(closeit && SelItem)
+                {
+                    closeit = (SelItem != infoPtr->selectedItem);
+
+                    if(TREEVIEW_ValidItem(infoPtr, SelItem->parent))
+                        SelItem = SelItem->parent;
+                }
+
+                if(closeit)
+                {
+                    if(TREEVIEW_ValidItem(infoPtr, infoPtr->selectedItem))
+                        SelItem = infoPtr->selectedItem;
+
+                    while((SelItem) && (SelItem != ht.hItem))
+                    {
+                        TREEVIEW_Expand(infoPtr, SelItem, (WPARAM)TVE_COLLAPSE,
+                                FALSE);
+                        if(TREEVIEW_ValidItem(infoPtr, SelItem->parent))
+                            SelItem = SelItem->parent;
+                    }
+                }
+            }
+
+            /*
+             * Expand the current item
+             */
+            TREEVIEW_Expand(infoPtr, ht.hItem, TVE_TOGGLE, FALSE);
+        }
+        else
+            TREEVIEW_DoSelectItem(infoPtr, TVGN_CARET, ht.hItem, TVC_BYMOUSE);
+    }
+    else if (ht.flags & TVHT_ONITEMSTATEICON)
+    {
+	/* TVS_CHECKBOXES requires us to toggle the current state */
+	if (infoPtr->dwStyle & TVS_CHECKBOXES)
+	    TREEVIEW_ToggleItemState(infoPtr, ht.hItem);
+    }
+
+  setfocus:
+    SetFocus(hwnd);
+    return 0;
+}
+
+
+static LRESULT
+TREEVIEW_RButtonDown(TREEVIEW_INFO *infoPtr, LPARAM lParam)
+{
+    TVHITTESTINFO ht;
+
+    if (infoPtr->hwndEdit)
+    {
+	SetFocus(infoPtr->hwnd);
+	return 0;
+    }
+
+    ht.pt.x = (LONG)(INT16)LOWORD(lParam);
+    ht.pt.y = (LONG)(INT16)HIWORD(lParam);
+
+    TREEVIEW_HitTest(infoPtr, &ht);
+
+    if (TREEVIEW_TrackMouse(infoPtr, ht.pt))
+    {
+	if (ht.hItem)
+	{
+	    TREEVIEW_SendTreeviewDnDNotify(infoPtr, TVN_BEGINRDRAGA, ht.hItem,
+					   ht.pt);
+	    infoPtr->dropItem = ht.hItem;
+	}
+    }
+    else
+    {
+	SetFocus(infoPtr->hwnd);
+	TREEVIEW_SendSimpleNotify(infoPtr, NM_RCLICK);
+    }
+
+    return 0;
+}
+
+static LRESULT
+TREEVIEW_RButtonUp(TREEVIEW_INFO *infoPtr, LPPOINT pPt)
+{
+    HWND hwnd = infoPtr->hwnd;
+    POINT pt = *pPt;
+
+    /* Change to screen coordinate for WM_CONTEXTMENU */
+    ClientToScreen(hwnd, &pt);
+
+    SendMessageA(hwnd, WM_CONTEXTMENU, (WPARAM)hwnd, MAKELPARAM(pt.x, pt.y));
+    return 0;
+}
+
+
+static LRESULT
+TREEVIEW_CreateDragImage(TREEVIEW_INFO *infoPtr, WPARAM wParam, LPARAM lParam)
+{
+    TREEVIEW_ITEM *dragItem = (HTREEITEM)lParam;
+    INT cx, cy;
+    HDC hdc, htopdc;
+    HWND hwtop;
+    HBITMAP hbmp, hOldbmp;
+    SIZE size;
+    RECT rc;
+    HFONT hOldFont;
+
+    TRACE("\n");
+
+    if (!(infoPtr->himlNormal))
+	return 0;
+
+    if (!dragItem || !TREEVIEW_ValidItem(infoPtr, dragItem))
+	return 0;
+
+    TREEVIEW_UpdateDispInfo(infoPtr, dragItem, TVIF_TEXT);
+
+    hwtop = GetDesktopWindow();
+    htopdc = GetDC(hwtop);
+    hdc = CreateCompatibleDC(htopdc);
+
+    hOldFont = SelectObject(hdc, infoPtr->hFont);
+    GetTextExtentPoint32A(hdc, dragItem->pszText, lstrlenA(dragItem->pszText),
+			  &size);
+    TRACE("%d %d %s %d\n", size.cx, size.cy, dragItem->pszText,
+	  lstrlenA(dragItem->pszText));
+    hbmp = CreateCompatibleBitmap(htopdc, size.cx, size.cy);
+    hOldbmp = SelectObject(hdc, hbmp);
+
+    ImageList_GetIconSize(infoPtr->himlNormal, &cx, &cy);
+    size.cx += cx;
+    if (cy > size.cy)
+	size.cy = cy;
+
+    infoPtr->dragList = ImageList_Create(size.cx, size.cy, ILC_COLOR, 10, 10);
+    ImageList_Draw(infoPtr->himlNormal, dragItem->iImage, hdc, 0, 0,
+		   ILD_NORMAL);
 
 /*
  ImageList_GetImageInfo (infoPtr->himlNormal, dragItem->hItem, &iminfo);
@@ -3642,759 +3870,1231 @@
 
 /* draw item text */
 
- SetRect (&rc, cx, 0, size.cx,size.cy);
- DrawTextA (hdc, itemtxt, lstrlenA (itemtxt), &rc, DT_LEFT);
- SelectObject (hdc, hOldFont);
- SelectObject (hdc, hOldbmp);
+    SetRect(&rc, cx, 0, size.cx, size.cy);
+    DrawTextA(hdc, dragItem->pszText, lstrlenA(dragItem->pszText), &rc,
+	      DT_LEFT);
+    SelectObject(hdc, hOldFont);
+    SelectObject(hdc, hOldbmp);
 
- ImageList_Add (infoPtr->dragList, hbmp, 0);
+    ImageList_Add(infoPtr->dragList, hbmp, 0);
 
- DeleteDC (hdc);
- DeleteObject (hbmp);
- ReleaseDC (hwtop, htopdc);
+    DeleteDC(hdc);
+    DeleteObject(hbmp);
+    ReleaseDC(hwtop, htopdc);
 
- return (LRESULT)infoPtr->dragList;
+    return (LRESULT)infoPtr->dragList;
 }
 
+/* Selection ************************************************************/
 
 static LRESULT
-TREEVIEW_DoSelectItem (HWND hwnd, INT action, HTREEITEM newSelect, INT cause)
-
+TREEVIEW_DoSelectItem(TREEVIEW_INFO *infoPtr, INT action, HTREEITEM newSelect,
+		      INT cause)
 {
-  TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
-  TREEVIEW_ITEM *prevItem,*wineItem;
-  INT prevSelect;
+    TREEVIEW_ITEM *prevSelect;
+    RECT rcFocused;
 
-  wineItem = TREEVIEW_ValidItem (infoPtr, (HTREEITEM)newSelect);
+    assert(newSelect == NULL || TREEVIEW_ValidItem(infoPtr, newSelect));
 
-  TRACE("Entering item %d, flag %x, cause %x, state %d\n", 
-	(INT)newSelect, 
-	action, 
-	cause,
-	wineItem->state);
+    TRACE("Entering item %p (%s), flag %x, cause %x, state %d\n",
+	  newSelect, TREEVIEW_ItemName(newSelect), action, cause,
+	  newSelect ? newSelect->state : 0);
 
-  if ( (wineItem) && (wineItem->parent))
-  {
-	/* 
-	 * If the item has a collapse parent expand the parent so he 
-	 * can expose the item 
-	 */
-	TREEVIEW_ITEM *parentItem = TREEVIEW_ValidItem (infoPtr, wineItem->parent);
-	if ( !(parentItem->state & TVIS_EXPANDED)) 
-	  TREEVIEW_Expand (hwnd, TVE_EXPAND, (LPARAM) wineItem->parent);
-  }
+    /* reset and redraw focusedItem if focusedItem was set so we don't */
+    /* have to worry about the previously focused item when we set a new one */
+    if(infoPtr->focusedItem)
+    {
+        rcFocused = (infoPtr->focusedItem)->rect;
+        infoPtr->focusedItem = 0;
+        InvalidateRect(infoPtr->hwnd, &rcFocused, TRUE);
+    }
 
-  switch (action) 
-  {
-	case TVGN_CARET: 
-	  prevSelect=(INT)infoPtr->selectedItem;
+    switch (action)
+    {
+    case TVGN_CARET:
+	prevSelect = infoPtr->selectedItem;
 
-	  if ((HTREEITEM)prevSelect==newSelect) 
-		return FALSE;
+	if (prevSelect == newSelect)
+	    return FALSE;
 
-	  prevItem= TREEVIEW_ValidItem (infoPtr, (HTREEITEM)prevSelect);
+	if (TREEVIEW_SendTreeviewNotify(infoPtr,
+					TVN_SELCHANGINGA,
+					cause,
+					TVIF_HANDLE | TVIF_STATE | TVIF_PARAM,
+					prevSelect,
+					newSelect))
+	    return FALSE;
 
-	  if (newSelect) 
-		if (TREEVIEW_SendTreeviewNotify(
-			  hwnd, 
-			  TVN_SELCHANGINGA, 
-			  cause, 
-			  (HTREEITEM)prevSelect, 
-			  (HTREEITEM)newSelect)) 
-		  return FALSE;       /* FIXME: OK? */
-	
-	  if (prevItem) 
-		prevItem->state &= ~TVIS_SELECTED;
-	  if (wineItem) 
-		wineItem->state |=  TVIS_SELECTED;
+	if (prevSelect)
+	    prevSelect->state &= ~TVIS_SELECTED;
+	if (newSelect)
+	    newSelect->state |= TVIS_SELECTED;
 
-	  infoPtr->selectedItem=(HTREEITEM)newSelect;
+	infoPtr->selectedItem = newSelect;
 
-	  TREEVIEW_SendTreeviewNotify(
-		hwnd, 
-		TVN_SELCHANGEDA, 
-		cause,
-		(HTREEITEM)prevSelect,
-		(HTREEITEM)newSelect);
+	TREEVIEW_EnsureVisible(infoPtr, infoPtr->selectedItem, FALSE);
 
-	  break;
+	TREEVIEW_SendTreeviewNotify(infoPtr,
+				    TVN_SELCHANGEDA,
+				    cause,
+				    TVIF_HANDLE | TVIF_STATE | TVIF_PARAM,
+				    prevSelect,
+				    newSelect);
+	TREEVIEW_QueueItemRefresh(infoPtr, prevSelect);
+	TREEVIEW_QueueItemRefresh(infoPtr, newSelect);
+	break;
 
-	case TVGN_DROPHILITE: 
-	  prevItem= TREEVIEW_ValidItem (infoPtr, infoPtr->dropItem);
+    case TVGN_DROPHILITE:
+	prevSelect = infoPtr->dropItem;
 
-	  if (prevItem) 
-		prevItem->state &= ~TVIS_DROPHILITED;
+	if (prevSelect)
+	    prevSelect->state &= ~TVIS_DROPHILITED;
 
-	  infoPtr->dropItem=(HTREEITEM)newSelect;
+	infoPtr->dropItem = newSelect;
 
-	  if (wineItem) 
-		wineItem->state |=TVIS_DROPHILITED;
+	if (newSelect)
+	    newSelect->state |= TVIS_DROPHILITED;
 
-	  break;
+	TREEVIEW_QueueItemRefresh(infoPtr, prevSelect);
+	TREEVIEW_QueueItemRefresh(infoPtr, newSelect);
+	break;
 
-	case TVGN_FIRSTVISIBLE:
-	  FIXME("FIRSTVISIBLE not implemented\n");
-	  break;
- }
- 
- TREEVIEW_QueueRefresh (hwnd);
+    case TVGN_FIRSTVISIBLE:
+	TREEVIEW_EnsureVisible(infoPtr, newSelect, FALSE);
+	TREEVIEW_SetFirstVisible(infoPtr, newSelect, TRUE);
+	TREEVIEW_QueueRefresh(infoPtr);
+	break;
+    }
 
- TRACE("Leaving state %d\n", wineItem->state);
- return TRUE;
+    TRACE("Leaving state %d\n", newSelect ? newSelect->state : 0);
+    return TRUE;
 }
 
-/* FIXME: handle NM_KILLFocus etc */
+/* FIXME: handle NM_KILLFOCUS etc */
 static LRESULT
-TREEVIEW_SelectItem (HWND hwnd, WPARAM wParam, LPARAM lParam)
-
+TREEVIEW_SelectItem(TREEVIEW_INFO *infoPtr, INT wParam, HTREEITEM item)
 {
- return TREEVIEW_DoSelectItem (hwnd, wParam, (HTREEITEM) lParam, TVC_UNKNOWN);
+    if (item != NULL && !TREEVIEW_ValidItem(infoPtr, item))
+	return FALSE;
+
+    TRACE("%p (%s) %d\n", item, TREEVIEW_ItemName(item), wParam);
+
+    if (!TREEVIEW_DoSelectItem(infoPtr, wParam, item, TVC_UNKNOWN))
+	return FALSE;
+
+    return TRUE;
 }
 
+/* Scrolling ************************************************************/
 
-
-   
 static LRESULT
-TREEVIEW_GetFont (HWND hwnd, WPARAM wParam, LPARAM lParam)
-
+TREEVIEW_EnsureVisible(TREEVIEW_INFO *infoPtr, HTREEITEM item, BOOL bHScroll)
 {
- TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
+    HTREEITEM newFirstVisible = NULL;
+    int visible_pos;
 
- TRACE("%x\n",infoPtr->hFont);
- return infoPtr->hFont;
+    if (!TREEVIEW_ValidItem(infoPtr, item))
+	return FALSE;
+
+    if (!ISVISIBLE(item))
+    {
+	/* Expand parents as necessary. */
+	HTREEITEM parent = item->parent;
+
+	while (parent != infoPtr->root)
+	{
+	    if (!(parent->state & TVIS_EXPANDED))
+		TREEVIEW_Expand(infoPtr, parent, FALSE, FALSE);
+
+	    parent = parent->parent;
+	}
+    }
+
+    TRACE("%p (%s) %ld - %ld\n", item, TREEVIEW_ItemName(item), item->visibleOrder,
+	  infoPtr->firstVisible->visibleOrder);
+
+    visible_pos = item->visibleOrder - infoPtr->firstVisible->visibleOrder;
+
+    if (visible_pos < 0)
+    {
+	/* item is before the start of the list: put it at the top. */
+	newFirstVisible = item;
+    }
+    else if (visible_pos >= TREEVIEW_GetVisibleCount(infoPtr)
+	     /* Sometimes, before we are displayed, GVC is 0, causing us to
+	      * spuriously scroll up. */
+	     && visible_pos > 0)
+    {
+	/* item is past the end of the list. */
+	int scroll = visible_pos - TREEVIEW_GetVisibleCount(infoPtr);
+
+	newFirstVisible = TREEVIEW_GetListItem(infoPtr, infoPtr->firstVisible,
+					       scroll + 1);
+    }
+
+    if (bHScroll)
+    {
+        /* Scroll window so item's text is visible as much as possible */
+        /* Calculation of amount of extra space is taken from EditLabel code */
+        INT pos, x;
+        TEXTMETRICA textMetric;
+        HDC hdc = GetWindowDC(infoPtr->hwnd);
+
+        x = item->textWidth;
+
+        GetTextMetricsA(hdc, &textMetric);
+        ReleaseDC(infoPtr->hwnd, hdc);
+
+        x += (textMetric.tmMaxCharWidth * 2);
+        x = max(x, textMetric.tmMaxCharWidth * 3);
+
+	if (item->textOffset < 0)
+	   pos = item->textOffset;
+	else if (item->textOffset + x > infoPtr->clientWidth)
+        {
+           if (x > infoPtr->clientWidth)
+              pos = item->textOffset;
+           else
+              pos = item->textOffset + x - infoPtr->clientWidth;
+        }
+        else
+           pos = 0;
+
+	TREEVIEW_HScroll(infoPtr, MAKEWPARAM(SB_THUMBPOSITION, infoPtr->scrollX + pos));
+    }
+
+    if (newFirstVisible != NULL && newFirstVisible != infoPtr->firstVisible)
+    {
+	TREEVIEW_SetFirstVisible(infoPtr, newFirstVisible, TRUE);
+
+	return TRUE;
+    }
+
+    return FALSE;
+}
+
+static VOID
+TREEVIEW_SetFirstVisible(TREEVIEW_INFO *infoPtr,
+                         TREEVIEW_ITEM *newFirstVisible,
+                         BOOL bUpdateScrollPos)
+{
+    int gap_size;
+
+    TRACE("%p: %s\n", newFirstVisible, TREEVIEW_ItemName(newFirstVisible));
+
+    if (newFirstVisible != NULL)
+    {
+	/* Prevent an empty gap from appearing at the bottom... */
+	gap_size = TREEVIEW_GetVisibleCount(infoPtr)
+	    - infoPtr->maxVisibleOrder + newFirstVisible->visibleOrder;
+
+	if (gap_size > 0)
+	{
+	    newFirstVisible = TREEVIEW_GetListItem(infoPtr, newFirstVisible,
+						   -gap_size);
+
+	    /* ... unless we just don't have enough items. */
+	    if (newFirstVisible == NULL)
+		newFirstVisible = infoPtr->root->firstChild;
+	}
+    }
+
+    if (infoPtr->firstVisible != newFirstVisible)
+    {
+	if (infoPtr->firstVisible == NULL || newFirstVisible == NULL)
+	{
+	    infoPtr->firstVisible = newFirstVisible;
+	    TREEVIEW_QueueRefresh(infoPtr);
+	}
+	else
+	{
+	    TREEVIEW_ITEM *item;
+	    int scroll = infoPtr->uItemHeight *
+	                 (infoPtr->firstVisible->visibleOrder
+	                  - newFirstVisible->visibleOrder);
+
+	    infoPtr->firstVisible = newFirstVisible;
+
+	    for (item = infoPtr->root->firstChild; item != NULL;
+	         item = TREEVIEW_GetNextListItem(infoPtr, item))
+	    {
+	       item->rect.top += scroll;
+	       item->rect.bottom += scroll;
+	    }
+
+	    if (bUpdateScrollPos)
+		SetScrollPos(infoPtr->hwnd, SB_VERT,
+		              newFirstVisible->visibleOrder, TRUE);
+
+	    ScrollWindow(infoPtr->hwnd, 0, scroll, NULL, NULL);
+	    UpdateWindow(infoPtr->hwnd);
+	}
+    }
+}
+
+/************************************************************************
+ * VScroll is always in units of visible items. i.e. we always have a
+ * visible item aligned to the top of the control. (Unless we have no
+ * items at all.)
+ */
+static LRESULT
+TREEVIEW_VScroll(TREEVIEW_INFO *infoPtr, WPARAM wParam)
+{
+    TREEVIEW_ITEM *oldFirstVisible = infoPtr->firstVisible;
+    TREEVIEW_ITEM *newFirstVisible = NULL;
+
+    int nScrollCode = LOWORD(wParam);
+
+    TRACE("wp %x\n", wParam);
+
+    if (!(infoPtr->uInternalStatus & TV_VSCROLL))
+	return 0;
+
+    if (infoPtr->hwndEdit)
+	SetFocus(infoPtr->hwnd);
+
+    if (!oldFirstVisible)
+    {
+	assert(infoPtr->root->firstChild == NULL);
+	return 0;
+    }
+
+    switch (nScrollCode)
+    {
+    case SB_TOP:
+	newFirstVisible = infoPtr->root->firstChild;
+	break;
+
+    case SB_BOTTOM:
+	newFirstVisible = TREEVIEW_GetLastListItem(infoPtr, infoPtr->root);
+	break;
+
+    case SB_LINEUP:
+	newFirstVisible = TREEVIEW_GetPrevListItem(infoPtr, oldFirstVisible);
+	break;
+
+    case SB_LINEDOWN:
+	newFirstVisible = TREEVIEW_GetNextListItem(infoPtr, oldFirstVisible);
+	break;
+
+    case SB_PAGEUP:
+	newFirstVisible = TREEVIEW_GetListItem(infoPtr, oldFirstVisible,
+					       -max(1, TREEVIEW_GetVisibleCount(infoPtr)));
+	break;
+
+    case SB_PAGEDOWN:
+	newFirstVisible = TREEVIEW_GetListItem(infoPtr, oldFirstVisible,
+					       max(1, TREEVIEW_GetVisibleCount(infoPtr)));
+	break;
+
+    case SB_THUMBTRACK:
+    case SB_THUMBPOSITION:
+	newFirstVisible = TREEVIEW_GetListItem(infoPtr,
+					       infoPtr->root->firstChild,
+					       (LONG)(INT16)HIWORD(wParam));
+	break;
+
+    case SB_ENDSCROLL:
+	return 0;
+    }
+
+    if (newFirstVisible != NULL)
+    {
+	if (newFirstVisible != oldFirstVisible)
+	    TREEVIEW_SetFirstVisible(infoPtr, newFirstVisible,
+	                          nScrollCode != SB_THUMBTRACK);
+	else if (nScrollCode == SB_THUMBPOSITION)
+	    SetScrollPos(infoPtr->hwnd, SB_VERT,
+	                 newFirstVisible->visibleOrder, TRUE);
+    }
+
+    return 0;
 }
 
 static LRESULT
-TREEVIEW_SetFont (HWND hwnd, WPARAM wParam, LPARAM lParam)
-
+TREEVIEW_HScroll(TREEVIEW_INFO *infoPtr, WPARAM wParam)
 {
- TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
- TEXTMETRICA tm;
- LOGFONTA logFont;
- HFONT hFont, hOldFont;
- INT height;
- HDC hdc;
+    int maxWidth;
+    int scrollX = infoPtr->scrollX;
+    int nScrollCode = LOWORD(wParam);
 
- TRACE("%x %lx\n",wParam, lParam);
- 
- infoPtr->hFont = (HFONT)wParam;
+    TRACE("wp %x\n", wParam);
 
- hFont = infoPtr->hFont ? infoPtr->hFont : GetStockObject (SYSTEM_FONT);
+    if (!(infoPtr->uInternalStatus & TV_HSCROLL))
+	return FALSE;
 
- GetObjectA (infoPtr->hFont, sizeof (LOGFONTA), &logFont);
- logFont.lfWeight=FW_BOLD;
- infoPtr->hBoldFont = CreateFontIndirectA (&logFont);
+    if (infoPtr->hwndEdit)
+	SetFocus(infoPtr->hwnd);
 
- hdc = GetDC (0);
- hOldFont = SelectObject (hdc, hFont);
- GetTextMetricsA (hdc, &tm);
- height= tm.tmHeight + tm.tmExternalLeading + FOCUS_BORDER;
- if (height>infoPtr->uRealItemHeight) 
- 	infoPtr->uRealItemHeight=height;
- SelectObject (hdc, hOldFont);
- ReleaseDC (0, hdc);
+    maxWidth = infoPtr->treeWidth - infoPtr->clientWidth;
+    /* shall never occure */
+    if (maxWidth <= 0)
+    {
+       scrollX = 0;
+       goto scroll;
+    }
 
- if (lParam) 	
- 	TREEVIEW_QueueRefresh (hwnd);
- 
- return 0;
-}
+    switch (nScrollCode)
+    {
+    case SB_LINELEFT:
+	scrollX -= infoPtr->uItemHeight;
+	break;
+    case SB_LINERIGHT:
+	scrollX += infoPtr->uItemHeight;
+	break;
+    case SB_PAGELEFT:
+	scrollX -= infoPtr->clientWidth;
+	break;
+    case SB_PAGERIGHT:
+	scrollX += infoPtr->clientWidth;
+	break;
 
+    case SB_THUMBTRACK:
+    case SB_THUMBPOSITION:
+	scrollX = (int)(INT16)HIWORD(wParam);
+	break;
 
+    case SB_ENDSCROLL:
+       return 0;
+    }
 
-static LRESULT
-TREEVIEW_VScroll (HWND hwnd, WPARAM wParam, LPARAM lParam)
+    if (scrollX > maxWidth)
+        scrollX = maxWidth;
+    else if (scrollX < 0)
+        scrollX = 0;
 
-{
-  TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
-  WORD nVisibleItems;
-  int maxHeight;
+scroll:
+    if (scrollX != infoPtr->scrollX)
+    {
+        TREEVIEW_ITEM *item;
+        LONG scroll_pixels = infoPtr->scrollX - scrollX;
+        
+        for (item = infoPtr->root->firstChild; item != NULL;
+             item = TREEVIEW_GetNextListItem(infoPtr, item))
+        {
+           item->linesOffset += scroll_pixels;
+           item->stateOffset += scroll_pixels;
+           item->imageOffset += scroll_pixels;
+           item->textOffset  += scroll_pixels;
+        }
 
-  TRACE("wp %x, lp %lx\n", wParam, lParam);
-  if (!infoPtr->uInternalStatus & TV_VSCROLL) return FALSE;
+	ScrollWindow(infoPtr->hwnd, scroll_pixels, 0, NULL, NULL);
+	infoPtr->scrollX = scrollX;
+	UpdateWindow(infoPtr->hwnd);
+    }
 
-  switch (LOWORD (wParam)) {
-	case SB_LINEUP: 
-			if (!infoPtr->cy) return FALSE;
-			infoPtr->cy -= infoPtr->uRealItemHeight;
-			if (infoPtr->cy < 0) infoPtr->cy=0;
-			break;
-	case SB_LINEDOWN: 
-			nVisibleItems = infoPtr->uVisibleHeight / infoPtr->uRealItemHeight;
-			maxHeight=infoPtr->uTotalHeight - nVisibleItems * infoPtr->uRealItemHeight;
-			if (infoPtr->cy >= maxHeight) return FALSE;
-			infoPtr->cy += infoPtr->uRealItemHeight;
-			if (infoPtr->cy >= maxHeight) 
-				infoPtr->cy = maxHeight;
-			break;
-	case SB_PAGEUP:	
-			if (!infoPtr->cy) return FALSE;
-			infoPtr->cy -= infoPtr->uVisibleHeight;
-			if (infoPtr->cy < 0) infoPtr->cy=0;
-			break;
-	case SB_PAGEDOWN:
-			nVisibleItems = infoPtr->uVisibleHeight / infoPtr->uRealItemHeight;
-			maxHeight=infoPtr->uTotalHeight - nVisibleItems * infoPtr->uRealItemHeight;
-			if (infoPtr->cy == maxHeight) return FALSE;
-			infoPtr->cy += infoPtr->uVisibleHeight;
-            		if (infoPtr->cy >= maxHeight)
-                		infoPtr->cy = maxHeight;
-			break;
-	case SB_THUMBTRACK: 
-			infoPtr->cy = HIWORD (wParam);
-			break;
-			
-  }
-  
-  TREEVIEW_QueueRefresh (hwnd);
-  return TRUE;
+    if (nScrollCode != SB_THUMBTRACK)
+       SetScrollPos(infoPtr->hwnd, SB_HORZ, scrollX, TRUE);
+
+    return 0;
 }
 
 static LRESULT
-TREEVIEW_HScroll (HWND hwnd, WPARAM wParam, LPARAM lParam) 
+TREEVIEW_MouseWheel(TREEVIEW_INFO *infoPtr, WPARAM wParam)
 {
-  TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
-  int maxWidth;
-
-  TRACE("wp %lx, lp %x\n", lParam, wParam);
-	
-  if (!infoPtr->uInternalStatus & TV_HSCROLL) return FALSE;
-
-  switch (LOWORD (wParam)) {
-	case SB_LINEUP: 
-			if (!infoPtr->cx) return FALSE;
-			infoPtr->cx -= infoPtr->uRealItemHeight;
-			if (infoPtr->cx < 0) infoPtr->cx=0;
-			break;
-	case SB_LINEDOWN: 
-			maxWidth=infoPtr->uTotalWidth-infoPtr->uVisibleWidth;
-			if (infoPtr->cx == maxWidth) return FALSE;
-			infoPtr->cx += infoPtr->uRealItemHeight; /*FIXME */
-			if (infoPtr->cx > maxWidth) 
-				infoPtr->cx = maxWidth;
-			break;
-	case SB_PAGEUP:	
-			if (!infoPtr->cx) return FALSE;
-			infoPtr->cx -= infoPtr->uVisibleWidth;
-			if (infoPtr->cx < 0) infoPtr->cx=0;
-			break;
-	case SB_PAGEDOWN:
-			maxWidth=infoPtr->uTotalWidth-infoPtr->uVisibleWidth;
-			if (infoPtr->cx == maxWidth) return FALSE;
-			infoPtr->cx += infoPtr->uVisibleWidth;
-            if (infoPtr->cx > maxWidth)
-                infoPtr->cx = maxWidth;
-			break;
-	case SB_THUMBTRACK: 
-			infoPtr->cx = HIWORD (wParam);
-			break;
-			
-  }
-  
-  TREEVIEW_QueueRefresh (hwnd);
-  return TRUE;
-}
-
-static LRESULT TREEVIEW_MouseWheel (HWND hwnd, WPARAM wParam, LPARAM lParam)
-{
-
-    TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
-    short gcWheelDelta = 0;
+    short gcWheelDelta;
     UINT pulScrollLines = 3;
 
-    SystemParametersInfoW(SPI_GETWHEELSCROLLLINES,0, &pulScrollLines, 0);
+    if (infoPtr->firstVisible == NULL)
+	return TRUE;
 
-    gcWheelDelta -= (short) HIWORD(wParam);
+    SystemParametersInfoW(SPI_GETWHEELSCROLLLINES, 0, &pulScrollLines, 0);
+
+    gcWheelDelta = -(short)HIWORD(wParam);
     pulScrollLines *= (gcWheelDelta / WHEEL_DELTA);
 
     if (abs(gcWheelDelta) >= WHEEL_DELTA && pulScrollLines)
     {
-        int wheelDy = pulScrollLines * infoPtr->uRealItemHeight;
-        int newDy = infoPtr->cy + wheelDy;
-        int maxDy = infoPtr->uTotalHeight - infoPtr->uVisibleHeight;
+	int newDy = infoPtr->firstVisible->visibleOrder + pulScrollLines;
+	int maxDy = infoPtr->maxVisibleOrder;
 
-        if (newDy > maxDy) newDy = maxDy;
-        if (newDy < 0) newDy = 0;
+	if (newDy > maxDy)
+	    newDy = maxDy;
 
-        if (newDy == infoPtr->cy) return TRUE;
+	if (newDy < 0)
+	    newDy = 0;
 
-        TREEVIEW_VScroll(hwnd, MAKEWPARAM(SB_THUMBTRACK,newDy),0);
+	TREEVIEW_VScroll(infoPtr, MAKEWPARAM(SB_THUMBPOSITION, newDy));
     }
-  return TRUE;
+    return TRUE;
 }
 
+/* Create/Destroy *******************************************************/
+
 static LRESULT
-TREEVIEW_KeyDown (HWND hwnd, WPARAM wParam, LPARAM lParam)
-{
- TREEVIEW_INFO *infoPtr        = TREEVIEW_GetInfoPtr(hwnd);
- HTREEITEM     hNewSelection   = 0;
- INT           scrollNeeds     = -1;
- INT           cyChangeNeeds   = -1;
- INT           prevSelect      = (INT)infoPtr->selectedItem;
+TREEVIEW_Create(HWND hwnd)
+{ 
+    RECT rcClient;
+    TREEVIEW_INFO *infoPtr;
 
- TREEVIEW_ITEM *prevItem       = 
-    (prevSelect != 0 ) ? 
-      TREEVIEW_ValidItem (infoPtr, (HTREEITEM)prevSelect) :
-      NULL;
+    TRACE("wnd %x, style %lx\n", hwnd, GetWindowLongA(hwnd, GWL_STYLE));
 
- TREEVIEW_ITEM *newItem        = NULL;
-
- TRACE("%x %lx\n",wParam, lParam);
-
- if (prevSelect == 0) 
-   return FALSE;
-
- switch (wParam) {
-	case VK_UP: 
-		newItem=TREEVIEW_GetPrevListItem (infoPtr, prevItem);
-
-		if (!newItem) 
-			newItem=& infoPtr->items[(INT)infoPtr->TopRootItem];
-
-    	hNewSelection = newItem->hItem;
-
-    	if (! newItem->visible)
-      		scrollNeeds = SB_LINEUP;
-		break;
-
-	case VK_DOWN: 
-		newItem=TREEVIEW_GetNextListItem (infoPtr, prevItem);
-
-		if (!newItem) 
-      newItem=prevItem;
-
-    hNewSelection = newItem->hItem;
-
-    if (! newItem->visible)
-      scrollNeeds = SB_LINEDOWN;
-
-		break;
-
-	case VK_HOME:
-		newItem       = &infoPtr->items[(INT)infoPtr->TopRootItem];
-    hNewSelection = newItem->hItem;
-    cyChangeNeeds = 0;
-		break;
-
-	case VK_END:
-		newItem       = &infoPtr->items[(INT)infoPtr->TopRootItem];
-		newItem       = TREEVIEW_GetLastListItem (infoPtr, newItem);
-    hNewSelection = newItem->hItem;
-
-    if (! newItem->visible)
-      cyChangeNeeds = infoPtr->uTotalHeight-infoPtr->uVisibleHeight;
-
-		break;
-
-	case VK_LEFT:
-    if ( (prevItem->cChildren > 0) && (prevItem->state & TVIS_EXPANDED) )
-    {
-      TREEVIEW_Expand(hwnd, TVE_COLLAPSE, prevSelect );
-    }
-    else if ((INT)prevItem->parent) 
-    {
-      newItem = (& infoPtr->items[(INT)prevItem->parent]);
-      if (! newItem->visible) 
-        /* FIXME find a way to make this item the first visible... */
-        newItem = NULL; 
-
-      hNewSelection = newItem->hItem;
-    }
-
-    break;
-
-	case VK_RIGHT:
-    if ( ( prevItem->cChildren > 0)  || 
-         ( prevItem->cChildren == I_CHILDRENCALLBACK))
-    {
-      if (! (prevItem->state & TVIS_EXPANDED))
-        TREEVIEW_Expand(hwnd, TVE_EXPAND, prevSelect );
-      else
-      {
-        newItem = (& infoPtr->items[(INT)prevItem->firstChild]);
-        hNewSelection = newItem->hItem;
-      }
-    }
-
-    break;
-
-  case VK_ADD:
-    if (! (prevItem->state & TVIS_EXPANDED))
-      TREEVIEW_Expand(hwnd, TVE_EXPAND, prevSelect );
-    break;
-
-  case VK_SUBTRACT:
-    if (prevItem->state & TVIS_EXPANDED)
-      TREEVIEW_Expand(hwnd, TVE_COLLAPSE, prevSelect );
-    break;
-
-  case VK_PRIOR:
-    
-		newItem=TREEVIEW_GetListItem(
-              infoPtr, 
-              prevItem,
-              -1*(TREEVIEW_GetVisibleCount(hwnd,0,0)-3));
-		if (!newItem) 
-      newItem=prevItem;
-  
-    hNewSelection = newItem->hItem;
-
-    if (! newItem->visible)
-      scrollNeeds = SB_PAGEUP;
-
-		break;
-
-  case VK_NEXT:
-		newItem=TREEVIEW_GetListItem(
-              infoPtr, 
-              prevItem,
-              TREEVIEW_GetVisibleCount(hwnd,0,0)-3);
-
-		if (!newItem) 
-      newItem=prevItem;
-
-    hNewSelection = newItem->hItem;
-
-    if (! newItem->visible)
-      scrollNeeds = SB_PAGEDOWN;
-
-		break;
-
-	case VK_BACK:
-
-	case VK_RETURN:
-
-  default:
-		FIXME("%x not implemented\n", wParam);
-		break;
- }
-
-  if (hNewSelection) 
-  {
-/* 
-    This works but does not send notification...
-
-    prevItem->state      &= ~TVIS_SELECTED;
-    newItem->state       |=  TVIS_SELECTED;
-    infoPtr->selectedItem = hNewSelection;
-    TREEVIEW_QueueRefresh (hwnd);
-*/
-
-    if ( TREEVIEW_DoSelectItem( 
-           hwnd, 
-           TVGN_CARET, 
-           (HTREEITEM)hNewSelection, 
-           TVC_BYKEYBOARD))
-    {
-      /* If selection change is allowed for the new item, perform scrolling */
-      if (scrollNeeds != -1)
-        TREEVIEW_VScroll(hwnd, scrollNeeds, 0);
-  
-      if (cyChangeNeeds != -1)
-        infoPtr->cy = cyChangeNeeds;
-
-      /* FIXME: Something happen in the load the in the two weeks before 
-         april 1st 1999 which makes this SetFocus mandatory otherwise, the focus 
-         is lost... However the SetFocus should not be required...*/
-               
-      SetFocus(hwnd);
-    }
-  }
-
-  return FALSE;
-}
-
-
-static LRESULT
-TREEVIEW_GetScrollTime (HWND hwnd)
-{
-  TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
-
-  return infoPtr->uScrollTime;
-}
-
-
-static LRESULT
-TREEVIEW_SetScrollTime (HWND hwnd, UINT uScrollTime)
-{
-  TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
-  UINT uOldScrollTime = infoPtr->uScrollTime;
-
-  infoPtr->uScrollTime = min (uScrollTime, 100);
-
-  return uOldScrollTime;
-}
-
-
-static LRESULT WINAPI
-TREEVIEW_WindowProc (HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
-{
-   TREEVIEW_INFO *infoPtr;
-   if (uMsg==WM_CREATE)
-		return TREEVIEW_Create (hwnd, wParam, lParam);
-   
-   if (!(infoPtr = TREEVIEW_GetInfoPtr(hwnd)))
-       return DefWindowProcA (hwnd, uMsg, wParam, lParam);
-
-   switch (uMsg) {
-  
-    	case TVM_INSERTITEMA:
-          return TREEVIEW_InsertItemA (hwnd, wParam, lParam);
-
-    	case TVM_INSERTITEMW:
-      		return TREEVIEW_InsertItemW(hwnd,wParam,lParam);;
-
-    	case TVM_DELETEITEM:
-      		return TREEVIEW_DeleteItem (hwnd, wParam, lParam);
-
-    	case TVM_EXPAND:
-      		return TREEVIEW_Expand (hwnd, wParam, lParam);
-
-    	case TVM_GETITEMRECT:
-      		return TREEVIEW_GetItemRect (hwnd, wParam, lParam);
-
-    	case TVM_GETCOUNT:
-      		return TREEVIEW_GetCount (hwnd, wParam, lParam);
-
-    	case TVM_GETINDENT:
-      		return TREEVIEW_GetIndent (hwnd);
-
-    	case TVM_SETINDENT:
-      		return TREEVIEW_SetIndent (hwnd, wParam);
-
-    	case TVM_GETIMAGELIST:
-      		return TREEVIEW_GetImageList (hwnd, wParam, lParam);
-
-		case TVM_SETIMAGELIST:
-	    	return TREEVIEW_SetImageList (hwnd, wParam, lParam);
-
-    	case TVM_GETNEXTITEM:
-      		return TREEVIEW_GetNextItem (hwnd, wParam, lParam);
-
-    	case TVM_SELECTITEM:
-      		return TREEVIEW_SelectItem (hwnd, wParam, lParam);
-
-    	case TVM_GETITEMA:
-      		return TREEVIEW_GetItemA (hwnd, wParam, lParam);
-
-    	case TVM_GETITEMW:
-      		return TREEVIEW_GetItemW (hwnd, wParam, lParam);
-
-    	case TVM_SETITEMA:
-      		return TREEVIEW_SetItemA (hwnd, wParam, lParam);
-
-    	case TVM_SETITEMW:
-      		FIXME("Unimplemented msg TVM_SETITEMW\n");
-      		return 0;
-
-    	case TVM_EDITLABELA:
-    	    return TREEVIEW_EditLabelA(hwnd, wParam, lParam);
-
-    	case TVM_EDITLABELW:
-      		FIXME("Unimplemented msg TVM_EDITLABELW \n");
-      		return 0;
-
-    	case TVM_GETEDITCONTROL:
-      		return TREEVIEW_GetEditControl (hwnd);
-
-    	case TVM_GETVISIBLECOUNT:
-      		return TREEVIEW_GetVisibleCount (hwnd, wParam, lParam);
-
-    	case TVM_HITTEST:
-      		return TREEVIEW_HitTest (hwnd, lParam);
-
-    	case TVM_CREATEDRAGIMAGE:
-      		return TREEVIEW_CreateDragImage (hwnd, wParam, lParam);
-  
-    	case TVM_SORTCHILDREN:
-      		return TREEVIEW_SortChildren (hwnd, wParam, lParam);
-  
-    	case TVM_ENSUREVISIBLE:
-      		FIXME("Unimplemented msg TVM_ENSUREVISIBLE\n");
-      		return 0;
-  
-    	case TVM_SORTCHILDRENCB:
-      		return TREEVIEW_SortChildrenCB(hwnd, wParam, lParam);
-  
-    	case TVM_ENDEDITLABELNOW:
-                if (infoPtr->editItem)
-      		    return TREEVIEW_EndEditLabelNow (hwnd, wParam, lParam);
-  
-    	case TVM_GETISEARCHSTRINGA:
-      		FIXME("Unimplemented msg TVM_GETISEARCHSTRINGA\n");
-      		return 0;
-  
-    	case TVM_GETISEARCHSTRINGW:
-      		FIXME("Unimplemented msg TVM_GETISEARCHSTRINGW\n");
-      		return 0;
-  
-    	case TVM_GETTOOLTIPS:
-      		return TREEVIEW_GetToolTips (hwnd);
-
-    	case TVM_SETTOOLTIPS:
-      		return TREEVIEW_SetToolTips (hwnd, wParam);
-  
-    	case TVM_SETINSERTMARK:
-      		return TREEVIEW_SetInsertMark (hwnd,wParam, lParam);
-  
-    	case TVM_SETITEMHEIGHT:
-      		return TREEVIEW_SetItemHeight (hwnd, wParam);
-  
-    	case TVM_GETITEMHEIGHT:
-      		return TREEVIEW_GetItemHeight (hwnd);
-  
-    	case TVM_SETBKCOLOR:
-      		return TREEVIEW_SetBkColor (hwnd, wParam, lParam);
-	
-    	case TVM_SETTEXTCOLOR:
-      		return TREEVIEW_SetTextColor (hwnd, wParam, lParam);
-  
-    	case TVM_GETBKCOLOR:
-      		return TREEVIEW_GetBkColor (hwnd);
-  
-    	case TVM_GETTEXTCOLOR:
-      		return TREEVIEW_GetTextColor (hwnd);
-  
-    	case TVM_SETSCROLLTIME:
-      		return TREEVIEW_SetScrollTime (hwnd, (UINT)wParam);
-  
-    	case TVM_GETSCROLLTIME:
-      		return TREEVIEW_GetScrollTime (hwnd);
-
-    	case TVM_GETITEMSTATE:
-      		return TREEVIEW_GetItemState (hwnd,wParam, lParam);
-
-    	case TVM_GETLINECOLOR:
-      		return TREEVIEW_GetLineColor (hwnd,wParam, lParam);
-
-    	case TVM_SETLINECOLOR:
-      		return TREEVIEW_SetLineColor (hwnd,wParam, lParam);
-  
-    	case TVM_SETINSERTMARKCOLOR:
-      		return TREEVIEW_SetInsertMarkColor (hwnd,wParam, lParam);
-  
-    	case TVM_GETINSERTMARKCOLOR:
-      		return TREEVIEW_GetInsertMarkColor (hwnd,wParam, lParam);
-  
-    	case TVM_SETUNICODEFORMAT:
-      		FIXME("Unimplemented msg TVM_SETUNICODEFORMAT\n");
-      		return 0;
-  
-    	case TVM_GETUNICODEFORMAT:
-      		FIXME("Unimplemented msg TVM_GETUNICODEFORMAT\n");
-      		return 0;
-  
-		case WM_COMMAND: 
-			 return TREEVIEW_Command (hwnd, wParam, lParam);
-  
-		case WM_DESTROY:
-			return TREEVIEW_Destroy (hwnd);
-  
-/*		case WM_ENABLE: */
-  
-		case WM_ERASEBKGND:
-			return TREEVIEW_EraseBackground (hwnd, wParam, lParam);
-  
-		case WM_GETDLGCODE:
-	    	return DLGC_WANTARROWS | DLGC_WANTCHARS;
-  
-		case WM_PAINT:
-	    	return TREEVIEW_Paint (hwnd, wParam, lParam);
-  
-		case WM_GETFONT:
-	    	return TREEVIEW_GetFont (hwnd, wParam, lParam);
-
-		case WM_SETFONT:
-	    	return TREEVIEW_SetFont (hwnd, wParam, lParam);
-  
-		case WM_KEYDOWN:
-			return TREEVIEW_KeyDown (hwnd, wParam, lParam);
-  
-		case WM_SETFOCUS: 
-			return TREEVIEW_SetFocus (hwnd, wParam, lParam);
-
-		case WM_KILLFOCUS: 
-			return TREEVIEW_KillFocus (hwnd, wParam, lParam);
-  
-		case WM_LBUTTONDOWN:
-			return TREEVIEW_LButtonDown (hwnd, wParam, lParam);
-
-		case WM_LBUTTONUP:
-			return TREEVIEW_LButtonUp (hwnd, wParam, lParam);
-  
-		case WM_LBUTTONDBLCLK:
-			return TREEVIEW_LButtonDoubleClick (hwnd, wParam, lParam);
-  
-		case WM_RBUTTONDOWN:
-			return TREEVIEW_RButtonDown (hwnd, wParam, lParam);
-
-		case WM_RBUTTONUP:
-			return TREEVIEW_RButtonUp (hwnd, wParam, lParam);
-
-		case WM_MOUSEMOVE:
-			return TREEVIEW_MouseMove (hwnd, wParam, lParam);
-  
-		case WM_STYLECHANGED: 
-			return TREEVIEW_StyleChanged (hwnd, wParam, lParam);
-
-/*		case WM_SYSCOLORCHANGE: */
-/*		case WM_SETREDRAW: */
-  
-		case WM_TIMER:
-			return TREEVIEW_HandleTimer (hwnd, wParam, lParam);
+    infoPtr = (TREEVIEW_INFO *)COMCTL32_Alloc(sizeof(TREEVIEW_INFO));
  
-		case WM_SIZE: 
-			return TREEVIEW_Size (hwnd, wParam,lParam);
+    if (infoPtr == NULL)
+    {
+	ERR("could not allocate info memory!\n");
+	return 0;
+    }
 
-		case WM_HSCROLL: 
-			return TREEVIEW_HScroll (hwnd, wParam, lParam);
-		case WM_VSCROLL: 
-			return TREEVIEW_VScroll (hwnd, wParam, lParam);
-  
-                case WM_MOUSEWHEEL:
-                    if (wParam & (MK_SHIFT | MK_CONTROL))
-                        return DefWindowProcA( hwnd, uMsg, wParam, lParam );
-                    return TREEVIEW_MouseWheel (hwnd, wParam, lParam);
+    SetWindowLongA(hwnd, 0, (DWORD)infoPtr);
 
-		case WM_DRAWITEM:
-			TRACE ("drawItem\n");
-			return DefWindowProcA (hwnd, uMsg, wParam, lParam);
-  
-		default:
-	    	if (uMsg >= WM_USER)
-		FIXME("Unknown msg %04x wp=%08x lp=%08lx\n",
-  		     uMsg, wParam, lParam);
-  	    return DefWindowProcA (hwnd, uMsg, wParam, lParam);
-      }
+    infoPtr->hwnd = hwnd;
+    infoPtr->dwStyle = GetWindowLongA(hwnd, GWL_STYLE);
+    infoPtr->uInternalStatus = 0;
+    infoPtr->Timer = 0;
+    infoPtr->uNumItems = 0;
+    infoPtr->cdmode = 0;
+    infoPtr->uScrollTime = 300;	/* milliseconds */
+    infoPtr->bRedraw = TRUE;
+
+    GetClientRect(hwnd, &rcClient);
+
+    /* No scroll bars yet. */
+    infoPtr->clientWidth = rcClient.right;
+    infoPtr->clientHeight = rcClient.bottom;
+
+    infoPtr->treeWidth = 0;
+    infoPtr->treeHeight = 0;
+
+    infoPtr->uIndent = 19;
+    infoPtr->selectedItem = 0;
+    infoPtr->focusedItem = 0;
+    /* hotItem? */
+    infoPtr->firstVisible = 0;
+    infoPtr->maxVisibleOrder = 0;
+    infoPtr->dropItem = 0;
+    infoPtr->insertMarkItem = 0;
+    infoPtr->insertBeforeorAfter = 0;
+    /* dragList */
+
+    infoPtr->scrollX = 0;
+
+    infoPtr->clrBk = GetSysColor(COLOR_WINDOW);
+    infoPtr->clrText = -1;	/* use system color */
+    infoPtr->clrLine = RGB(128, 128, 128);
+    infoPtr->clrInsertMark = GetSysColor(COLOR_BTNTEXT);
+
+    /* hwndToolTip */
+
+    infoPtr->hwndEdit = 0;
+    infoPtr->wpEditOrig = NULL;
+    infoPtr->bIgnoreEditKillFocus = FALSE;
+    infoPtr->bLabelChanged = FALSE;
+
+    infoPtr->himlNormal = NULL;
+    infoPtr->himlState = NULL;
+    infoPtr->normalImageWidth = 0;
+    infoPtr->normalImageHeight = 0;
+    infoPtr->stateImageWidth = 0;
+    infoPtr->stateImageHeight = 0;
+
+    infoPtr->items = DPA_Create(16);
+
+    infoPtr->hFont = GetStockObject(DEFAULT_GUI_FONT);
+    infoPtr->hBoldFont = TREEVIEW_CreateBoldFont(infoPtr->hFont);
+
+    infoPtr->uItemHeight = TREEVIEW_NaturalHeight(infoPtr);
+
+    infoPtr->root = TREEVIEW_AllocateItem(infoPtr);
+    infoPtr->root->state = TVIS_EXPANDED;
+    infoPtr->root->iLevel = -1;
+    infoPtr->root->visibleOrder = -1;
+
+#if 0
+    infoPtr->hwndNotify = GetParent32 (hwnd);
+    infoPtr->bTransparent = ( GetWindowLongA( hwnd, GWL_STYLE) & TBSTYLE_FLAT);
+#endif
+
+    infoPtr->hwndToolTip = 0;
+    if (!(infoPtr->dwStyle & TVS_NOTOOLTIPS))
+	infoPtr->hwndToolTip = COMCTL32_CreateToolTip(hwnd);
+
+    if (infoPtr->dwStyle & TVS_CHECKBOXES)
+    {
+	RECT rc;
+	HBITMAP hbm, hbmOld;
+	HDC hdc;
+	int nIndex;
+
+	infoPtr->himlState =
+	    ImageList_Create(16, 16, ILC_COLOR | ILC_MASK, 3, 0);
+
+	hdc = CreateCompatibleDC(0);
+	hbm = CreateCompatibleBitmap(hdc, 48, 16);
+	hbmOld = SelectObject(hdc, hbm);
+
+	rc.left  = 0;   rc.top    = 0;
+	rc.right = 48;  rc.bottom = 16;
+	FillRect(hdc, &rc, (HBRUSH)(COLOR_WINDOW+1));
+
+	rc.left  = 18;   rc.top    = 2;
+	rc.right = 30;   rc.bottom = 14;
+	DrawFrameControl(hdc, &rc, DFC_BUTTON,
+	                  DFCS_BUTTONCHECK|DFCS_FLAT);
+
+	rc.left  = 34;   rc.right  = 46;
+	DrawFrameControl(hdc, &rc, DFC_BUTTON,
+	                  DFCS_BUTTONCHECK|DFCS_FLAT|DFCS_CHECKED);
+
+	nIndex = ImageList_AddMasked(infoPtr->himlState, hbm,
+	                              GetSysColor(COLOR_WINDOW));
+	TRACE("chckbox index %d\n", nIndex);
+	SelectObject(hdc, hbmOld);
+	DeleteObject(hbm);
+	DeleteDC(hdc);
+
+	infoPtr->stateImageWidth = 16;
+	infoPtr->stateImageHeight = 16;
+    }
     return 0;
 }
 
 
+static LRESULT
+TREEVIEW_Destroy(TREEVIEW_INFO *infoPtr)
+{
+    TRACE("\n");
+
+    TREEVIEW_RemoveTree(infoPtr);
+
+    /* tool tip is automatically destroyed: we are its owner */
+
+    /* Restore original windproc. */
+    if (infoPtr->hwndEdit)
+	SetWindowLongA(infoPtr->hwndEdit, GWL_WNDPROC,
+		       (LONG)infoPtr->wpEditOrig);
+
+    /* Deassociate treeview from the window before doing anything drastic. */
+    SetWindowLongA(infoPtr->hwnd, 0, (LONG)NULL);
+
+    DeleteObject(infoPtr->hBoldFont);
+    COMCTL32_Free(infoPtr);
+
+    return 0;
+}
+
+/* Miscellaneous Messages ***********************************************/
+
+static LRESULT
+TREEVIEW_ScrollKeyDown(TREEVIEW_INFO *infoPtr, WPARAM key)
+{
+    static const struct
+    {
+	unsigned char code;
+    }
+    scroll[] =
+    {
+#define SCROLL_ENTRY(dir, code) { ((dir) << 7) | (code) }
+	SCROLL_ENTRY(SB_VERT, SB_PAGEUP),	/* VK_PRIOR */
+	SCROLL_ENTRY(SB_VERT, SB_PAGEDOWN),	/* VK_NEXT */
+	SCROLL_ENTRY(SB_VERT, SB_BOTTOM),	/* VK_END */
+	SCROLL_ENTRY(SB_VERT, SB_TOP),		/* VK_HOME */
+	SCROLL_ENTRY(SB_HORZ, SB_LINEUP),	/* VK_LEFT */
+	SCROLL_ENTRY(SB_VERT, SB_LINEUP),	/* VK_UP */
+	SCROLL_ENTRY(SB_HORZ, SB_LINEDOWN),	/* VK_RIGHT */
+	SCROLL_ENTRY(SB_VERT, SB_LINEDOWN)	/* VK_DOWN */
+#undef SCROLL_ENTRY
+    };
+
+    if (key >= VK_PRIOR && key <= VK_DOWN)
+    {
+	unsigned char code = scroll[key - VK_PRIOR].code;
+
+	(((code & (1 << 7)) == (SB_HORZ << 7))
+	 ? TREEVIEW_HScroll
+	 : TREEVIEW_VScroll)(infoPtr, code & 0x7F);
+    }
+
+    return 0;
+}
+
+/************************************************************************
+ *        TREEVIEW_KeyDown
+ *
+ * VK_UP       Move selection to the previous non-hidden item.
+ * VK_DOWN     Move selection to the next non-hidden item.
+ * VK_HOME     Move selection to the first item.
+ * VK_END      Move selection to the last item.
+ * VK_LEFT     If expanded then collapse, otherwise move to parent.
+ * VK_RIGHT    If collapsed then expand, otherwise move to first child.
+ * VK_ADD      Expand.
+ * VK_SUBTRACT Collapse.
+ * VK_MULTIPLY Expand all.
+ * VK_PRIOR    Move up GetVisibleCount items.
+ * VK_NEXT     Move down GetVisibleCount items.
+ * VK_BACK     Move to parent.
+ * CTRL-Left,Right,Up,Down,PgUp,PgDown,Home,End: Scroll without changing selection
+ */
+static LRESULT
+TREEVIEW_KeyDown(TREEVIEW_INFO *infoPtr, WPARAM wParam)
+{
+    /* If it is non-NULL and different, it will be selected and visible. */
+    TREEVIEW_ITEM *newSelection = NULL;
+
+    TREEVIEW_ITEM *prevItem = infoPtr->selectedItem;
+
+    TRACE("%x\n", wParam);
+
+    if (prevItem == NULL)
+	return FALSE;
+
+    if (GetAsyncKeyState(VK_CONTROL) & 0x8000)
+	return TREEVIEW_ScrollKeyDown(infoPtr, wParam);
+
+    switch (wParam)
+    {
+    case VK_UP:
+	newSelection = TREEVIEW_GetPrevListItem(infoPtr, prevItem);
+	if (!newSelection)
+	    newSelection = infoPtr->root->firstChild;
+	break;
+
+    case VK_DOWN:
+	newSelection = TREEVIEW_GetNextListItem(infoPtr, prevItem);
+	break;
+
+    case VK_HOME:
+	newSelection = infoPtr->root->firstChild;
+	break;
+
+    case VK_END:
+	newSelection = TREEVIEW_GetLastListItem(infoPtr, infoPtr->root);
+	break;
+
+    case VK_LEFT:
+	if (prevItem->state & TVIS_EXPANDED)
+	{
+	    TREEVIEW_Collapse(infoPtr, prevItem, FALSE, TRUE);
+	}
+	else if (prevItem->parent != infoPtr->root)
+	{
+	    newSelection = prevItem->parent;
+	}
+	break;
+
+    case VK_RIGHT:
+	if (TREEVIEW_HasChildren(infoPtr, prevItem))
+	{
+	    if (!(prevItem->state & TVIS_EXPANDED))
+		TREEVIEW_Expand(infoPtr, prevItem, FALSE, TRUE);
+	    else
+	    {
+		newSelection = prevItem->firstChild;
+	    }
+	}
+
+	break;
+
+    case VK_MULTIPLY:
+	TREEVIEW_ExpandAll(infoPtr, prevItem);
+	break;
+
+    case VK_ADD:
+	if (!(prevItem->state & TVIS_EXPANDED))
+	    TREEVIEW_Expand(infoPtr, prevItem, FALSE, TRUE);
+	break;
+
+    case VK_SUBTRACT:
+	if (prevItem->state & TVIS_EXPANDED)
+	    TREEVIEW_Collapse(infoPtr, prevItem, FALSE, TRUE);
+	break;
+
+    case VK_PRIOR:
+	newSelection
+	    = TREEVIEW_GetListItem(infoPtr, prevItem,
+				   -TREEVIEW_GetVisibleCount(infoPtr));
+	break;
+
+    case VK_NEXT:
+	newSelection
+	    = TREEVIEW_GetListItem(infoPtr, prevItem,
+				   TREEVIEW_GetVisibleCount(infoPtr));
+	break;
+
+    case VK_BACK:
+	newSelection = prevItem->parent;
+	if (newSelection == infoPtr->root)
+	    newSelection = NULL;
+	break;
+
+    case VK_SPACE:
+	if (infoPtr->dwStyle & TVS_CHECKBOXES)
+	    TREEVIEW_ToggleItemState(infoPtr, prevItem);
+	break;
+    }
+
+    if (newSelection && newSelection != prevItem)
+    {
+	if (TREEVIEW_DoSelectItem(infoPtr, TVGN_CARET, newSelection,
+				  TVC_BYKEYBOARD))
+	{
+	    TREEVIEW_EnsureVisible(infoPtr, newSelection, FALSE);
+	}
+    }
+
+    return FALSE;
+}
+
+static LRESULT
+TREEVIEW_Size(TREEVIEW_INFO *infoPtr, WPARAM wParam, LPARAM lParam)
+{
+    if (wParam == SIZE_RESTORED)
+    {
+	infoPtr->clientWidth  = (LONG)(INT16)LOWORD(lParam);
+	infoPtr->clientHeight = (LONG)(INT16)HIWORD(lParam);
+
+	TREEVIEW_RecalculateVisibleOrder(infoPtr, NULL);
+	TREEVIEW_SetFirstVisible(infoPtr, infoPtr->firstVisible, TRUE);
+	TREEVIEW_UpdateScrollBars(infoPtr);
+    }
+    else
+    {
+	FIXME("WM_SIZE flag %x %lx not handled\n", wParam, lParam);
+    }
+
+    TREEVIEW_QueueRefresh(infoPtr);
+    return 0;
+}
+
+static LRESULT
+TREEVIEW_StyleChanged(TREEVIEW_INFO *infoPtr, WPARAM wParam, LPARAM lParam)
+{
+    TRACE("(%x %lx)\n", wParam, lParam);
+
+    if (wParam == GWL_STYLE)
+    {
+       DWORD dwNewStyle = ((LPSTYLESTRUCT)lParam)->styleNew;
+
+       /* we have to take special care about tooltips */
+       if ((infoPtr->dwStyle ^ dwNewStyle) & TVS_NOTOOLTIPS)
+       {
+          if (infoPtr->dwStyle & TVS_NOTOOLTIPS)
+          {
+              infoPtr->hwndToolTip = COMCTL32_CreateToolTip(infoPtr->hwnd);
+              TRACE("\n");
+          }
+          else
+          {
+             DestroyWindow(infoPtr->hwndToolTip);
+             infoPtr->hwndToolTip = 0;
+          }
+       }
+
+       infoPtr->dwStyle = dwNewStyle;
+    }
+
+    TREEVIEW_UpdateSubTree(infoPtr, infoPtr->root);
+    TREEVIEW_UpdateScrollBars(infoPtr);
+    TREEVIEW_QueueRefresh(infoPtr);
+
+    return 0;
+}
+
+static LRESULT
+TREEVIEW_SetFocus(TREEVIEW_INFO *infoPtr)
+{
+    TRACE("\n");
+
+    if (!infoPtr->selectedItem)
+    {
+	TREEVIEW_DoSelectItem(infoPtr, TVGN_CARET, infoPtr->firstVisible,
+			      TVC_UNKNOWN);
+    }
+
+    TREEVIEW_SendSimpleNotify(infoPtr, NM_SETFOCUS);
+    TREEVIEW_QueueItemRefresh(infoPtr, infoPtr->selectedItem);
+    return 0;
+}
+
+static LRESULT
+TREEVIEW_KillFocus(TREEVIEW_INFO *infoPtr)
+{
+    TRACE("\n");
+
+    TREEVIEW_SendSimpleNotify(infoPtr, NM_KILLFOCUS);
+    TREEVIEW_QueueItemRefresh(infoPtr, infoPtr->selectedItem);
+    return 0;
+}
+
+
+static LRESULT WINAPI
+TREEVIEW_WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
+{
+    TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
+    if (infoPtr) TREEVIEW_VerifyTree(infoPtr);
+    else
+    {
+	if (uMsg == WM_CREATE)
+	    TREEVIEW_Create(hwnd);
+	else
+	    goto def;
+    }
+
+    switch (uMsg)
+    {
+    case TVM_CREATEDRAGIMAGE:
+	return TREEVIEW_CreateDragImage(infoPtr, wParam, lParam);
+
+    case TVM_DELETEITEM:
+	return TREEVIEW_DeleteItem(infoPtr, (HTREEITEM)lParam);
+
+    case TVM_EDITLABELA:
+	return TREEVIEW_EditLabelA(infoPtr, (HTREEITEM)lParam);
+
+    case TVM_EDITLABELW:
+	FIXME("Unimplemented msg TVM_EDITLABELW\n");
+	return 0;
+
+    case TVM_ENDEDITLABELNOW:
+	return TREEVIEW_EndEditLabelNow(infoPtr, (BOOL)wParam);
+
+    case TVM_ENSUREVISIBLE:
+	return TREEVIEW_EnsureVisible(infoPtr, (HTREEITEM)lParam, TRUE);
+
+    case TVM_EXPAND:
+	return TREEVIEW_ExpandMsg(infoPtr, (UINT)wParam, (HTREEITEM)lParam);
+
+    case TVM_GETBKCOLOR:
+	return TREEVIEW_GetBkColor(infoPtr);
+
+    case TVM_GETCOUNT:
+	return TREEVIEW_GetCount(infoPtr);
+
+    case TVM_GETEDITCONTROL:
+	return TREEVIEW_GetEditControl(infoPtr);
+
+    case TVM_GETIMAGELIST:
+	return TREEVIEW_GetImageList(infoPtr, wParam);
+
+    case TVM_GETINDENT:
+	return TREEVIEW_GetIndent(infoPtr);
+
+    case TVM_GETINSERTMARKCOLOR:
+	return TREEVIEW_GetInsertMarkColor(infoPtr);
+
+    case TVM_GETISEARCHSTRINGA:
+	FIXME("Unimplemented msg TVM_GETISEARCHSTRINGA\n");
+	return 0;
+
+    case TVM_GETISEARCHSTRINGW:
+	FIXME("Unimplemented msg TVM_GETISEARCHSTRINGW\n");
+	return 0;
+
+    case TVM_GETITEMA:
+	return TREEVIEW_GetItemA(infoPtr, (LPTVITEMEXA)lParam);
+
+    case TVM_GETITEMW:
+	return TREEVIEW_GetItemW(infoPtr, (LPTVITEMEXA)lParam);
+
+    case TVM_GETITEMHEIGHT:
+	return TREEVIEW_GetItemHeight(infoPtr);
+
+    case TVM_GETITEMRECT:
+	return TREEVIEW_GetItemRect(infoPtr, (BOOL)wParam, (LPRECT)lParam);
+
+    case TVM_GETITEMSTATE:
+	return TREEVIEW_GetItemState(infoPtr, (HTREEITEM)wParam, (UINT)lParam);
+
+    case TVM_GETLINECOLOR:
+	return TREEVIEW_GetLineColor(infoPtr);
+
+    case TVM_GETNEXTITEM:
+	return TREEVIEW_GetNextItem(infoPtr, (UINT)wParam, (HTREEITEM)lParam);
+
+    case TVM_GETSCROLLTIME:
+	return TREEVIEW_GetScrollTime(infoPtr);
+
+    case TVM_GETTEXTCOLOR:
+	return TREEVIEW_GetTextColor(infoPtr);
+
+    case TVM_GETTOOLTIPS:
+	return TREEVIEW_GetToolTips(infoPtr);
+
+    case TVM_GETUNICODEFORMAT:
+	FIXME("Unimplemented msg TVM_GETUNICODEFORMAT\n");
+	return 0;
+
+    case TVM_GETVISIBLECOUNT:
+	return TREEVIEW_GetVisibleCount(infoPtr);
+
+    case TVM_HITTEST:
+	return TREEVIEW_HitTest(infoPtr, (LPTVHITTESTINFO)lParam);
+
+    case TVM_INSERTITEMA:
+	return TREEVIEW_InsertItemA(infoPtr, lParam);
+
+    case TVM_INSERTITEMW:
+	return TREEVIEW_InsertItemW(infoPtr, lParam);
+
+    case TVM_SELECTITEM:
+	return TREEVIEW_SelectItem(infoPtr, (INT)wParam, (HTREEITEM)lParam);
+
+    case TVM_SETBKCOLOR:
+	return TREEVIEW_SetBkColor(infoPtr, (COLORREF)lParam);
+
+    case TVM_SETIMAGELIST:
+	return TREEVIEW_SetImageList(infoPtr, wParam, (HIMAGELIST)lParam);
+
+    case TVM_SETINDENT:
+	return TREEVIEW_SetIndent(infoPtr, (UINT)wParam);
+
+    case TVM_SETINSERTMARK:
+	return TREEVIEW_SetInsertMark(infoPtr, (BOOL)wParam, (HTREEITEM)lParam);
+
+    case TVM_SETINSERTMARKCOLOR:
+	return TREEVIEW_SetInsertMarkColor(infoPtr, (COLORREF)lParam);
+
+    case TVM_SETITEMA:
+	return TREEVIEW_SetItemA(infoPtr, (LPTVITEMEXA)lParam);
+
+    case TVM_SETITEMW:
+	FIXME("Unimplemented msg TVM_SETITEMW\n");
+	return 0;
+
+    case TVM_SETLINECOLOR:
+	return TREEVIEW_SetLineColor(infoPtr, (COLORREF)lParam);
+
+    case TVM_SETITEMHEIGHT:
+	return TREEVIEW_SetItemHeight(infoPtr, (INT)(SHORT)wParam);
+
+    case TVM_SETSCROLLTIME:
+	return TREEVIEW_SetScrollTime(infoPtr, (UINT)wParam);
+
+    case TVM_SETTEXTCOLOR:
+	return TREEVIEW_SetTextColor(infoPtr, (COLORREF)lParam);
+
+    case TVM_SETTOOLTIPS:
+	return TREEVIEW_SetToolTips(infoPtr, (HWND)wParam);
+
+    case TVM_SETUNICODEFORMAT:
+	FIXME("Unimplemented msg TVM_SETUNICODEFORMAT\n");
+	return 0;
+
+    case TVM_SORTCHILDREN:
+	return TREEVIEW_SortChildren(infoPtr, wParam, lParam);
+
+    case TVM_SORTCHILDRENCB:
+	return TREEVIEW_SortChildrenCB(infoPtr, wParam, (LPTVSORTCB)lParam);
+
+	/* WM_CHAR */
+
+    case WM_COMMAND:
+	return TREEVIEW_Command(infoPtr, wParam, lParam);
+
+    case WM_DESTROY:
+	return TREEVIEW_Destroy(infoPtr);
+
+	/* WM_ENABLE */
+
+    case WM_ERASEBKGND:
+	return TREEVIEW_EraseBackground(infoPtr, (HDC)wParam);
+
+    case WM_GETDLGCODE:
+	return DLGC_WANTARROWS | DLGC_WANTCHARS;
+
+    case WM_GETFONT:
+	return TREEVIEW_GetFont(infoPtr);
+
+    case WM_HSCROLL:
+	return TREEVIEW_HScroll(infoPtr, wParam);
+
+    case WM_KEYDOWN:
+	return TREEVIEW_KeyDown(infoPtr, wParam);
+
+    case WM_KILLFOCUS:
+	return TREEVIEW_KillFocus(infoPtr);
+
+    case WM_LBUTTONDBLCLK:
+	return TREEVIEW_LButtonDoubleClick(infoPtr, lParam);
+
+    case WM_LBUTTONDOWN:
+	return TREEVIEW_LButtonDown(infoPtr, lParam);
+
+	/* WM_MBUTTONDOWN */
+
+	/* WM_MOUSEMOVE */
+
+	/* WM_NOTIFY */
+
+	/* WM_NOTIFYFORMAT */
+
+    case WM_PAINT:
+	return TREEVIEW_Paint(infoPtr, wParam);
+
+	/* WM_PRINTCLIENT */
+
+    case WM_RBUTTONDOWN:
+	return TREEVIEW_RButtonDown(infoPtr, lParam);
+
+    case WM_SETFOCUS:
+	return TREEVIEW_SetFocus(infoPtr);
+
+    case WM_SETFONT:
+	return TREEVIEW_SetFont(infoPtr, (HFONT)wParam, (BOOL)lParam);
+
+    case WM_SETREDRAW:
+        return TREEVIEW_SetRedraw(infoPtr, wParam, lParam);
+
+    case WM_SIZE:
+	return TREEVIEW_Size(infoPtr, wParam, lParam);
+
+    case WM_STYLECHANGED:
+	return TREEVIEW_StyleChanged(infoPtr, wParam, lParam);
+
+	/* WM_SYSCOLORCHANGE */
+
+	/* WM_SYSKEYDOWN */
+
+    case WM_TIMER:
+	return TREEVIEW_HandleTimer(infoPtr, wParam);
+
+    case WM_VSCROLL:
+	return TREEVIEW_VScroll(infoPtr, wParam);
+
+	/* WM_WININICHANGE */
+
+    case WM_MOUSEWHEEL:
+	if (wParam & (MK_SHIFT | MK_CONTROL))
+	    goto def;
+	return TREEVIEW_MouseWheel(infoPtr, wParam);
+
+    case WM_DRAWITEM:
+	TRACE("drawItem\n");
+	goto def;
+
+    default:
+	/* This mostly catches MFC and Delphi messages. :( */
+	if (uMsg >= WM_USER)
+	    TRACE("Unknown msg %04x wp=%08x lp=%08lx\n", uMsg, wParam, lParam);
+def:
+	return DefWindowProcA(hwnd, uMsg, wParam, lParam);
+    }
+    return 0;
+}
+
+
+/* Class Registration ***************************************************/
+
 VOID
-TREEVIEW_Register (void)
+TREEVIEW_Register(void)
 {
     WNDCLASSA wndClass;
 
     TRACE("\n");
 
-    ZeroMemory (&wndClass, sizeof(WNDCLASSA));
-    wndClass.style         = CS_GLOBALCLASS | CS_DBLCLKS;
-    wndClass.lpfnWndProc   = (WNDPROC)TREEVIEW_WindowProc;
-    wndClass.cbClsExtra    = 0;
-    wndClass.cbWndExtra    = sizeof(TREEVIEW_INFO *);
-    wndClass.hCursor       = LoadCursorA (0, IDC_ARROWA);
+    ZeroMemory(&wndClass, sizeof(WNDCLASSA));
+    wndClass.style = CS_GLOBALCLASS | CS_DBLCLKS;
+    wndClass.lpfnWndProc = (WNDPROC)TREEVIEW_WindowProc;
+    wndClass.cbClsExtra = 0;
+    wndClass.cbWndExtra = sizeof(TREEVIEW_INFO *);
+
+    wndClass.hCursor = LoadCursorA(0, IDC_ARROWA);
     wndClass.hbrBackground = 0;
     wndClass.lpszClassName = WC_TREEVIEWA;
- 
-    RegisterClassA (&wndClass);
+
+    RegisterClassA(&wndClass);
 }
 
 
 VOID
-TREEVIEW_Unregister (void)
+TREEVIEW_Unregister(void)
 {
-    UnregisterClassA (WC_TREEVIEWA, (HINSTANCE)NULL);
+    UnregisterClassA(WC_TREEVIEWA, (HINSTANCE) NULL);
 }
 
 
+/* Tree Verification ****************************************************/
 
+#ifndef NDEBUG
+static inline void
+TREEVIEW_VerifyChildren(TREEVIEW_INFO *infoPtr, TREEVIEW_ITEM *item);
+
+static inline void TREEVIEW_VerifyItemCommon(TREEVIEW_INFO *infoPtr,
+					     TREEVIEW_ITEM *item)
+{
+    assert(infoPtr != NULL);
+    assert(item != NULL);
+
+    /* both NULL, or both non-null */
+    assert((item->firstChild == NULL) == (item->lastChild == NULL));
+
+    assert(item->firstChild != item);
+    assert(item->lastChild != item);
+
+    if (item->firstChild)
+    {
+	assert(item->firstChild->parent == item);
+	assert(item->firstChild->prevSibling == NULL);
+    }
+
+    if (item->lastChild)
+    {
+	assert(item->lastChild->parent == item);
+	assert(item->lastChild->nextSibling == NULL);
+    }
+
+    assert(item->nextSibling != item);
+    if (item->nextSibling)
+    {
+	assert(item->nextSibling->parent == item->parent);
+	assert(item->nextSibling->prevSibling == item);
+    }
+
+    assert(item->prevSibling != item);
+    if (item->prevSibling)
+    {
+	assert(item->prevSibling->parent == item->parent);
+	assert(item->prevSibling->nextSibling == item);
+    }
+}
+
+static inline void
+TREEVIEW_VerifyItem(TREEVIEW_INFO *infoPtr, TREEVIEW_ITEM *item)
+{
+    assert(item != NULL);
+
+    assert(item->parent != NULL);
+    assert(item->parent != item);
+    assert(item->iLevel == item->parent->iLevel + 1);
+
+    assert(DPA_GetPtrIndex(infoPtr->items, item) != -1);
+
+    TREEVIEW_VerifyItemCommon(infoPtr, item);
+
+    TREEVIEW_VerifyChildren(infoPtr, item);
+}
+
+static inline void
+TREEVIEW_VerifyChildren(TREEVIEW_INFO *infoPtr, TREEVIEW_ITEM *item)
+{
+    TREEVIEW_ITEM *child;
+    assert(item != NULL);
+
+    for (child = item->firstChild; child != NULL; child = child->nextSibling)
+	TREEVIEW_VerifyItem(infoPtr, child);
+}
+
+static inline void
+TREEVIEW_VerifyRoot(TREEVIEW_INFO *infoPtr)
+{
+    TREEVIEW_ITEM *root = infoPtr->root;
+
+    assert(root != NULL);
+    assert(root->iLevel == -1);
+    assert(root->parent == NULL);
+    assert(root->prevSibling == NULL);
+
+    TREEVIEW_VerifyItemCommon(infoPtr, root);
+
+    TREEVIEW_VerifyChildren(infoPtr, root);
+}
+
+static void
+TREEVIEW_VerifyTree(TREEVIEW_INFO *infoPtr)
+{
+    assert(infoPtr != NULL);
+
+    TREEVIEW_VerifyRoot(infoPtr);
+}
+#endif