Ignore:
Timestamp:
May 22, 2000, 7:25:13 PM (25 years ago)
Author:
cbratschi
Message:

merged with Corel WINE 20000513, added new DPA_* functions

File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/comctl32/treeview.cpp

    r3520 r3585  
    1 /* $Id: treeview.cpp,v 1.13 2000-05-10 19:50:33 cbratschi Exp $ */
     1/* $Id: treeview.cpp,v 1.14 2000-05-22 17:25:13 cbratschi Exp $ */
    22/* Treeview control
    33 *
     
    88 * Copyright 1999-2000 Christoph Bratschi (cbratschi@datacomm.ch)
    99 *
     10 * Note that TREEVIEW_INFO * and HTREEITEM are the same thing.
    1011 *
    11  * FIXMEs  (for personal use)
    12     Expand:             -ctlmacro expands twice ->toggle.
    13    -DblClick:   ctlmacro.exe's NM_DBLCLK seems to go wrong (returns FALSE).
    14    -treehelper: stack corruption makes big window.
    1512 *
    1613 * Status: complete (many things untested)
     
    3027 - expand not finished
    3128 - WM_ENABLE: draw disabled control
     29 - AMD cpuid: text not drawn (> treeview.cpp,v 1.13 2000/05/10 19:50:33)
     30  t1: GDI32: SetBkColor to FFFFFF
     31  t1: GDI32: GetTextMetricsA returned 1
     32  t1: GDI32: SetTextAlign
     33-->
     34  t1: GDI32: RestoreDC
     35  t1: SendInternalMessageA WM_NOTIFY for 68000003 65 1312e4
     36  t1: USER32:  GetDlgCtrlID
     37
     38  missing lines at -->:
     39  t1: GDI32: GetTextExtentPoint32A 10001a1 Processor Speed Test 20 returned 1 (93,14)
     40  t1: GDI32: ExtTextOutA 10001a1 Processor Speed Test
     41  t1: GDI32: GetTextAlign
     42  t1: GDI32: GetTextExtentPoint32A 10001a1 454 Mhz 7 returned 1 (37,14)
     43  t1: GDI32: ExtTextOutA 10001a1 454 Mhz
     44  t1: GDI32: GetTextAlign
     45
     46  don't know why it fails
    3247*/
    3348
     49#include <assert.h>
    3450#include <stdlib.h>
    3551#include <string.h>
    3652#include <math.h>
     53#include <limits.h>
    3754#include "winbase.h"
    3855#include "wingdi.h"
     
    4562/* ffs should be in <string.h>. */
    4663
    47 //#define OS2LINEHACK //CB: too slow, but looks good
    48 
    49 /* Defines, since they do not need to return previous state, and nr
    50  * has no side effects in this file.
     64//#define NDEBUG
     65#define NDEBUG_TEXT
     66
     67#define TEXT_CALLBACK_SIZE 260
     68
     69#define TREEVIEW_LEFT_MARGIN 8
     70
     71#define MINIMUM_INDENT 19
     72
     73#define CALLBACK_MASK_ALL (TVIF_TEXT|TVIF_CHILDREN|TVIF_IMAGE|TVIF_SELECTEDIMAGE)
     74
     75#define STATEIMAGEINDEX(x) (((x) >> 12) & 0x0f)
     76#define OVERLAYIMAGEINDEX(x) (((x) >> 8) & 0x0f)
     77
     78typedef VOID (*TREEVIEW_ItemEnumFunc)(TREEVIEW_INFO *, TREEVIEW_ITEM *,LPVOID);
     79
     80static BOOL    TREEVIEW_UnqueueRefresh(TREEVIEW_INFO *,BOOL calc,BOOL refresh);
     81static void    TREEVIEW_QueueRefresh(TREEVIEW_INFO *);
     82static void    TREEVIEW_Refresh(TREEVIEW_INFO *infoPtr);
     83static void    TREEVIEW_RefreshItem(TREEVIEW_INFO *infoPtr,TREEVIEW_ITEM *item,DWORD changed);
     84
     85static VOID    TREEVIEW_HideInfoTip(TREEVIEW_INFO *infoPtr);
     86static LRESULT TREEVIEW_DoSelectItem(TREEVIEW_INFO *, INT, HTREEITEM, INT);
     87static LRESULT TREEVIEW_EnsureVisible(TREEVIEW_INFO *, HTREEITEM);
     88static LRESULT TREEVIEW_RButtonUp(TREEVIEW_INFO *, LPPOINT);
     89static LRESULT TREEVIEW_EndEditLabelNow(TREEVIEW_INFO *infoPtr, BOOL bCancel);
     90static VOID    TREEVIEW_UpdateScrollBars(TREEVIEW_INFO *infoPtr, BOOL);
     91static VOID    TREEVIEW_CheckInfoTip(TREEVIEW_INFO *infoPtr);
     92static VOID    TREEVIEW_ISearch(TREEVIEW_INFO *infoPtr,CHAR ch);
     93
     94/* Random Utilities *****************************************************/
     95
     96#ifdef NDEBUG
     97static inline void
     98TREEVIEW_VerifyTree(TREEVIEW_INFO *infoPtr)
     99{
     100    (void)infoPtr;
     101}
     102
     103#else
     104/* The definition is at the end of the file. */
     105static void TREEVIEW_VerifyTree(TREEVIEW_INFO *infoPtr);
     106#ifndef NDEBUG_TEXT
     107static void TREEVIEW_WriteVerify(CHAR *text)
     108{
     109  dprintf((text));
     110}
     111#else
     112static inline void TREEVIEW_WriteVerify(CHAR * text) {}
     113#endif
     114#endif
     115
     116/* Returns the treeview private data if hwnd is a treeview.
     117 * Otherwise returns an undefined value. */
     118#define TREEVIEW_GetInfoPtr(hwnd) ((TREEVIEW_INFO *)getInfoPtr(hwnd))
     119
     120/* Don't call this. Nothing wants an item index. */
     121static inline int
     122TREEVIEW_GetItemIndex(TREEVIEW_INFO *infoPtr, HTREEITEM handle)
     123{
     124    assert(infoPtr != NULL);
     125
     126    return DPA_GetPtrIndex(infoPtr->items,handle);
     127}
     128
     129/***************************************************************************
     130 * This method checks that handle is an item for this tree.
    51131 */
    52 #define tv_test_bit(nr,bf)      (((LPBYTE)bf)[nr>>3]&(1<<(nr&7)))
    53 #define tv_set_bit(nr,bf)       ((LPBYTE)bf)[nr>>3]|=(1<<(nr&7))
    54 #define tv_clear_bit(nr,bf)     ((LPBYTE)bf)[nr>>3]&=~(1<<(nr&7))
    55 
    56 #define TREEVIEW_GetInfoPtr(hwnd) ((TREEVIEW_INFO *)getInfoPtr(hwnd))
    57 
    58 static BOOL    TREEVIEW_SendTreeviewNotify (HWND hwnd, UINT code, UINT action, HTREEITEM oldItem, HTREEITEM newItem);
    59 static BOOL    TREEVIEW_SendTreeviewDnDNotify (HWND hwnd, UINT code, HTREEITEM dragItem, POINT pt);
    60 static INT     TREEVIEW_CallbackChildren(HWND hwnd,TREEVIEW_ITEM *wineItem);
    61 static INT     TREEVIEW_CallbackImage(HWND hwnd,TREEVIEW_ITEM *wineItem);
    62 static INT     TREEVIEW_CallbackSelectedImage(HWND hwnd,TREEVIEW_ITEM *wineItem);
    63 static WCHAR*  TREEVIEW_CallbackText(HWND hwnd,TREEVIEW_ITEM *wineItem,BOOL *mustFree);
    64 static BOOL    TREEVIEW_SendCustomDrawNotify (HWND hwnd, DWORD dwDrawStage, HDC hdc, RECT rc);
    65 static BOOL    TREEVIEW_SendCustomDrawItemNotify (HWND hwnd, HDC hdc, TREEVIEW_ITEM *tvItem, UINT uItemDrawState);
    66 static LRESULT TREEVIEW_RButtonUp (HWND hwnd, LPPOINT pPt);
    67 static LRESULT TREEVIEW_SelectItem (HWND hwnd, WPARAM wParam, LPARAM lParam);
    68 static LRESULT TREEVIEW_DoSelectItem(HWND hwnd,INT action,HTREEITEM newSelect,INT cause);
    69 static void    TREEVIEW_Refresh(HWND hwnd);
    70 static void    TREEVIEW_RefreshItem(HWND hwnd,TREEVIEW_ITEM *item,BOOL wholeLine);
    71 static void    TREEVIEW_Draw(HWND hwnd,HDC hdc,RECT *updateRect);
    72 static BOOL    TREEVIEW_UnqueueRefresh(HWND hwnd,BOOL calc,BOOL refresh);
    73 static void    TREEVIEW_QueueRefresh(HWND hwnd);
    74 static void    TREEVIEW_CalcItem(HWND hwnd,HDC hdc,DWORD dwStyle,TREEVIEW_INFO *infoPtr,TREEVIEW_ITEM *item);
    75 static BOOL    TREEVIEW_CalcItems(HWND hwnd,HDC hdc,TREEVIEW_INFO *infoPtr);
    76 static LRESULT TREEVIEW_EnsureVisible(HWND hwnd,HTREEITEM hItem);
    77 static VOID    TREEVIEW_ISearch(HWND hwnd,CHAR ch);
    78 static VOID    TREEVIEW_CheckInfoTip(HWND hwnd);
    79 static VOID TREEVIEW_HideInfoTip(HWND hwnd,TREEVIEW_INFO *infoPtr);
    80 
    81 static LRESULT CALLBACK TREEVIEW_Edit_SubclassProc (HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
    82 
    83 LRESULT WINAPI TREEVIEW_EndEditLabelNow (HWND hwnd, BOOL bCancel);
    84 
    85 HWND TREEVIEW_EditLabel(HWND hwnd, HTREEITEM hItem,BOOL unicode);
    86 
    87 /* helper functions. Work with the assumption that validity of operands
    88    is checked beforehand, and that tree state is valid.  */
    89 
    90 /* FIXME: MS documentation says `GetNextVisibleItem' returns NULL
    91    if not succesfull'. Probably only applies to derefencing infoPtr
    92    (ie we are offered a valid treeview structure)
    93    and not whether there is a next `visible' child.
    94    FIXME: check other failures.
     132static BOOL
     133TREEVIEW_ValidItem(TREEVIEW_INFO *infoPtr,HTREEITEM handle)
     134{
     135    if (TREEVIEW_GetItemIndex(infoPtr, handle) == -1)
     136    {
     137        //TRACE("invalid item %p\n", handle);
     138        return FALSE;
     139    }
     140    else
     141        return TRUE;
     142}
     143
     144static HFONT
     145TREEVIEW_CreateBoldFont(HFONT hOrigFont)
     146{
     147    LOGFONTA font;
     148
     149    GetObjectA(hOrigFont, sizeof(font), &font);
     150    font.lfWeight = FW_BOLD;
     151
     152    return CreateFontIndirectA(&font);
     153}
     154
     155static inline HFONT
     156TREEVIEW_FontForItem(TREEVIEW_INFO *infoPtr, TREEVIEW_ITEM *item)
     157{
     158    return (item->state & TVIS_BOLD) ? infoPtr->hBoldFont : infoPtr->hFont;
     159}
     160
     161/* for trace/debugging purposes only */
     162static const WCHAR *
     163TREEVIEW_ItemName(TREEVIEW_ITEM *item)
     164{
     165    if (item == NULL) return (WCHAR*)L"<null item>";
     166    if (item->pszText == LPSTR_TEXTCALLBACKW) return (WCHAR*)L"<callback>";
     167    if (item->pszText == NULL) return (WCHAR*)L"<null>";
     168    /* It would be nice to check item->callbackMask & TVIF_TEXT here,
     169     * and indicate that this is a callback string, but there is nowhere
     170     * to put that indication. (Or we could be clever, and put it before
     171     * pszText, but that is just too hacky.) */
     172    return item->pszText;
     173}
     174
     175/* An item is not a child of itself. */
     176static BOOL
     177TREEVIEW_IsChildOf(TREEVIEW_ITEM *parent, TREEVIEW_ITEM *child)
     178{
     179    do
     180    {
     181        child = child->parent;
     182        if (child == parent) return TRUE;
     183    } while (child != NULL);
     184
     185    return FALSE;
     186}
     187
     188/* Tree Traversal *******************************************************/
     189
     190/***************************************************************************
     191 * This method returns the last expanded sibling or child child item
     192 * of a tree node
    95193 */
     194static TREEVIEW_ITEM *
     195TREEVIEW_GetLastListItem(TREEVIEW_INFO *infoPtr, TREEVIEW_ITEM *wineItem)
     196{
     197    /*
     198     * Get this item's last sibling
     199     */
     200    while (wineItem->nextSibling)
     201        wineItem = wineItem->nextSibling;
     202
     203    /*
     204     * If the last sibling has expanded children, restart.
     205     */
     206    if ((wineItem->state & TVIS_EXPANDED) && wineItem->firstChild != NULL)
     207    {
     208        return TREEVIEW_GetLastListItem(infoPtr, wineItem->firstChild);
     209    }
     210
     211    return wineItem;
     212}
    96213
    97214/***************************************************************************
    98  * This method returns the TREEVIEW_ITEM object given the handle
    99  */
    100 static TREEVIEW_ITEM* TREEVIEW_ValidItem(
    101   TREEVIEW_INFO *infoPtr,
    102   HTREEITEM  handle)
    103 {
    104   if ((!handle) || (handle>infoPtr->uMaxHandle))
    105     return NULL;
    106 
    107   if (tv_test_bit ((INT)handle, infoPtr->freeList))
    108     return NULL;
    109 
    110   return &infoPtr->items[(INT)handle];
    111 }
    112 
    113 /***************************************************************************
    114  * This function uses cChildren field to decide whether item has children
    115  * or not.
    116  * Note: return value doesn't reflect physical presence of children.
    117  */
    118 static INT TREEVIEW_HasChildren(
    119   HWND hwnd,
    120   TREEVIEW_ITEM *wineItem)
    121 {
    122    INT cChildren = 0;
    123 
    124    if ( wineItem->mask & TVIF_CHILDREN )
    125    {
    126       if (wineItem->cChildren == I_CHILDRENCALLBACK)
    127         cChildren = TREEVIEW_CallbackChildren(hwnd,wineItem);
    128       else
    129         cChildren = wineItem->cChildren;
    130    }
    131    else if ( wineItem->firstChild )
    132       cChildren = 1;
    133 
    134    return cChildren;
    135 }
    136 
    137 /***************************************************************************
    138  * This method returns the last expanded child item of a tree node
    139  */
    140 static TREEVIEW_ITEM *TREEVIEW_GetLastListItem(
    141   HWND hwnd,
    142   TREEVIEW_INFO *infoPtr,
    143   TREEVIEW_ITEM *tvItem)
    144 {
    145   TREEVIEW_ITEM *wineItem = tvItem;
    146 
    147   /*
    148    * Get this item last sibling
    149    */
    150   while (wineItem->sibling)
    151           wineItem=& infoPtr->items [(INT)wineItem->sibling];
    152 
    153   /*
    154    * If the last sibling has expanded children, restart.
    155    */
    156   if ((wineItem->state & TVIS_EXPANDED) &&
    157        TREEVIEW_HasChildren(hwnd, wineItem))
    158   {
    159        return TREEVIEW_GetLastListItem(
    160              hwnd,
    161              infoPtr,
    162              &(infoPtr->items[(INT)wineItem->firstChild]));
    163   }
    164 
    165   return wineItem;
    166 }
    167 
    168 /***************************************************************************
    169  * This method returns the previous physical item in the list not
     215 * This method returns the previous non-hidden item in the list not
    170216 * considering the tree hierarchy.
    171217 */
    172 static TREEVIEW_ITEM *TREEVIEW_GetPrevListItem(
    173   HWND hwnd,
    174   TREEVIEW_INFO *infoPtr,
    175   TREEVIEW_ITEM *tvItem)
    176 {
    177   if (tvItem->upsibling)
    178   {
    179     /*
    180      * This item has a upsibling, get the last item.  Since, GetLastListItem
    181      * first looks at siblings, we must feed it with the first child.
    182      */
    183     TREEVIEW_ITEM *upItem = &infoPtr->items[(INT)tvItem->upsibling];
    184 
    185     if ( (upItem->state & TVIS_EXPANDED) &&
    186           TREEVIEW_HasChildren(hwnd, upItem) )
    187     {
    188        return TREEVIEW_GetLastListItem(
    189                hwnd,
    190                infoPtr,
    191                &infoPtr->items[(INT)upItem->firstChild]);
     218static TREEVIEW_ITEM *
     219TREEVIEW_GetPrevListItem(TREEVIEW_INFO *infoPtr, TREEVIEW_ITEM *tvItem)
     220{
     221    if (tvItem->prevSibling)
     222    {
     223        /*
     224         * This item has a prevSibling, get the last item.  Since
     225         * GetLastListItem first looks at siblings, we must feed it with the
     226         * first child.
     227         */
     228        TREEVIEW_ITEM *upItem = tvItem->prevSibling;
     229
     230        if ((upItem->state & TVIS_EXPANDED) && upItem->firstChild != NULL)
     231            return TREEVIEW_GetLastListItem(infoPtr, upItem->firstChild);
     232        else
     233            return upItem;
    192234    }
    193235    else
    194       return upItem;
    195   }
    196   else
    197   {
    198     /*
    199      * this item does not have a upsibling, get the parent
    200      */
    201     if (tvItem->parent)
    202       return &infoPtr->items[(INT)tvItem->parent];
    203   }
    204 
    205   return NULL;
     236    {
     237        /*
     238         * this item does not have a prevSibling, get the parent
     239         */
     240        return (tvItem->parent != infoPtr->root) ? tvItem->parent : NULL;
     241    }
    206242}
    207243
     
    211247 * considering the tree hierarchy.
    212248 */
    213 static TREEVIEW_ITEM *TREEVIEW_GetNextListItem(
    214   HWND hwnd,
    215   TREEVIEW_INFO *infoPtr,
    216   TREEVIEW_ITEM *tvItem)
    217 {
    218   TREEVIEW_ITEM *wineItem = NULL;
    219 
    220   /*
    221    * If this item has children and is expanded, return the first child
    222    */
    223   if ( (tvItem->state & TVIS_EXPANDED) &&
    224         TREEVIEW_HasChildren(hwnd, tvItem) )
    225   {
    226      return (& infoPtr->items[(INT)tvItem->firstChild]);
    227   }
    228 
    229 
    230   /*
    231    * try to get the sibling
    232    */
    233   if (tvItem->sibling)
    234                 return (& infoPtr->items[(INT)tvItem->sibling]);
    235 
    236   /*
    237    * Otherwise, get the parent's sibling.
    238    */
    239   wineItem=tvItem;
    240   while (wineItem->parent) {
    241     wineItem=& infoPtr->items [(INT)wineItem->parent];
    242         if (wineItem->sibling)
    243       return (& infoPtr->items [(INT)wineItem->sibling]);
    244   }
    245 
    246   return NULL;
     249static TREEVIEW_ITEM *
     250TREEVIEW_GetNextListItem(TREEVIEW_INFO *infoPtr, TREEVIEW_ITEM *tvItem)
     251{
     252    assert(infoPtr != NULL);
     253    assert(tvItem != NULL);
     254
     255    /*
     256     * If this item has children and is expanded, return the first child
     257     */
     258    if ((tvItem->state & TVIS_EXPANDED) && tvItem->firstChild != NULL)
     259    {
     260        return tvItem->firstChild;
     261    }
     262
     263
     264    /*
     265     * try to get the sibling
     266     */
     267    if (tvItem->nextSibling)
     268        return tvItem->nextSibling;
     269
     270    /*
     271     * Otherwise, get the parent's sibling.
     272     */
     273    while (tvItem->parent)
     274    {
     275        tvItem = tvItem->parent;
     276
     277        if (tvItem->nextSibling)
     278            return tvItem->nextSibling;
     279    }
     280
     281    return NULL;
    247282}
    248283
     
    254289 *             forward if count is >0.
    255290 */
    256 static TREEVIEW_ITEM *TREEVIEW_GetListItem(
    257   HWND hwnd,
    258   TREEVIEW_INFO *infoPtr,
    259   TREEVIEW_ITEM *tvItem,
    260   LONG          count)
    261 {
    262   TREEVIEW_ITEM *previousItem = NULL;
    263   TREEVIEW_ITEM *wineItem     = tvItem;
    264   LONG          iter          = 0;
    265 
    266   if      (count > 0)
    267   {
    268     /* Find count item downward */
    269     while ((iter++ < count) && (wineItem != NULL))
    270     {
    271       /* Keep a pointer to the previous in case we ask for more than we got */
    272       previousItem = wineItem;
    273       wineItem     = TREEVIEW_GetNextListItem(hwnd,infoPtr,wineItem);
    274     }
    275 
    276     if (wineItem == NULL)
    277       wineItem = previousItem;
    278   }
    279   else if (count < 0)
    280   {
    281     /* Find count item upward */
    282     while ((iter-- > count) && (wineItem != NULL))
    283     {
    284       /* Keep a pointer to the previous in case we ask for more than we got */
    285       previousItem = wineItem;
    286       wineItem = TREEVIEW_GetPrevListItem(hwnd,infoPtr,wineItem);
    287     }
    288 
    289     if (wineItem == NULL)
    290       wineItem = previousItem;
    291   }
    292   else
    293     wineItem = NULL;
    294 
    295   return wineItem;
    296 }
    297 
     291static TREEVIEW_ITEM *
     292TREEVIEW_GetListItem(TREEVIEW_INFO *infoPtr, TREEVIEW_ITEM *wineItem,
     293                     LONG count)
     294{
     295    TREEVIEW_ITEM *(*next_item)(TREEVIEW_INFO *, TREEVIEW_ITEM *);
     296    TREEVIEW_ITEM *previousItem;
     297
     298    assert(wineItem != NULL);
     299
     300    if (count > 0)
     301    {
     302        next_item = TREEVIEW_GetNextListItem;
     303    }
     304    else if (count < 0)
     305    {
     306        count = -count;
     307        next_item = TREEVIEW_GetPrevListItem;
     308    }
     309    else
     310        return wineItem;
     311
     312    do
     313    {
     314        previousItem = wineItem;
     315        wineItem = next_item(infoPtr, wineItem);
     316
     317    } while (--count && wineItem != NULL);
     318
     319
     320    return wineItem ? wineItem : previousItem;
     321}
     322
     323static VOID
     324TREEVIEW_ForEachDisplayed(TREEVIEW_INFO *infoPtr,TREEVIEW_ItemEnumFunc func,LPVOID data)
     325{
     326    TREEVIEW_ITEM *item;
     327
     328    for (item = infoPtr->root->firstChild; item != NULL;
     329         item = TREEVIEW_GetNextListItem(infoPtr, item))
     330    {
     331        func(infoPtr, item, data);
     332    }
     333}
     334
     335/* Notifications */
     336
     337static VOID
     338TREEVIEW_TVItemFromItem(UINT mask,TVITEMW *tvItem,TREEVIEW_ITEM *item)
     339{
     340    tvItem->mask = mask;
     341    tvItem->hItem = item;
     342    tvItem->state = item->state;
     343    tvItem->stateMask = 0;
     344    tvItem->iImage = item->iImage;
     345    tvItem->pszText = item->pszText;
     346    tvItem->cchTextMax = item->cchTextMax;
     347    tvItem->iImage = item->iImage;
     348    tvItem->iSelectedImage = item->iSelectedImage;
     349    tvItem->cChildren = item->cChildren;
     350    tvItem->lParam = item->lParam;
     351}
     352
     353static BOOL TREEVIEW_SendTreeviewNotify(TREEVIEW_INFO *infoPtr,UINT code,UINT action,UINT mask,HTREEITEM oldItem,HTREEITEM newItem)
     354{
     355  NMTREEVIEWW nmhdr;
     356  CHAR *oldText = NULL,*newText = NULL;
     357  BOOL rc;
     358
     359  ZeroMemory(&nmhdr,sizeof(NMTREEVIEWW));
     360
     361  nmhdr.action = action;
     362  if (oldItem)
     363  {
     364    TREEVIEW_TVItemFromItem(mask, &nmhdr.itemOld, oldItem);
     365    if (!isUnicodeNotify(&infoPtr->header))
     366    {
     367      if (!oldItem->pszText || (oldItem->pszText == LPSTR_TEXTCALLBACKW)) nmhdr.itemOld.pszText = NULL; else
     368      {
     369        INT len = lstrlenW(oldItem->pszText)+1;
     370
     371        oldText = (CHAR*)COMCTL32_Alloc(len);
     372        lstrcpyWtoA(oldText,oldItem->pszText);
     373        nmhdr.itemOld.pszText = (WCHAR*)oldText;
     374        nmhdr.itemOld.cchTextMax = len;
     375      }
     376    }
     377  }
     378
     379  if (newItem)
     380  {
     381    TREEVIEW_TVItemFromItem(mask, &nmhdr.itemNew, newItem);
     382    if (!isUnicodeNotify(&infoPtr->header))
     383    {
     384      if (!newItem->pszText || (newItem->pszText == LPSTR_TEXTCALLBACKW)) nmhdr.itemNew.pszText = NULL; else
     385      {
     386        INT len = lstrlenW(newItem->pszText)+1;
     387
     388        newText = (CHAR*)COMCTL32_Alloc(len);
     389        lstrcpyWtoA(newText,newItem->pszText);
     390        nmhdr.itemNew.pszText = (WCHAR*)newText;
     391        nmhdr.itemNew.cchTextMax = len;
     392      }
     393    }
     394  }
     395
     396  nmhdr.ptDrag.x = 0;
     397  nmhdr.ptDrag.y = 0;
     398
     399  rc = (BOOL)sendNotify(infoPtr->hwnd,code,&nmhdr.hdr);
     400
     401  if (oldText) COMCTL32_Free(oldText);
     402  if (newText) COMCTL32_Free(newText);
     403
     404  return rc;
     405}
     406
     407static BOOL
     408TREEVIEW_SendTreeviewDnDNotify (TREEVIEW_INFO *infoPtr,UINT code,HTREEITEM dragItem,POINT pt)
     409{
     410  NMTREEVIEWA nmhdr;
     411
     412  nmhdr.action = 0;
     413  nmhdr.itemNew.mask = TVIF_STATE | TVIF_PARAM | TVIF_HANDLE;
     414  nmhdr.itemNew.hItem = dragItem;
     415  nmhdr.itemNew.state = dragItem->state;
     416  nmhdr.itemNew.lParam = dragItem->lParam;
     417
     418  nmhdr.ptDrag.x = pt.x;
     419  nmhdr.ptDrag.y = pt.y;
     420
     421  return (BOOL)sendNotify(infoPtr->hwnd,code,&nmhdr.hdr);
     422}
     423
     424static BOOL
     425TREEVIEW_SendCustomDrawNotify(TREEVIEW_INFO *infoPtr,DWORD dwDrawStage,HDC hdc,RECT rc)
     426{
     427  NMTVCUSTOMDRAW nmcdhdr;
     428  LPNMCUSTOMDRAW nmcd;
     429
     430  nmcd = &nmcdhdr.nmcd;
     431  nmcd->dwDrawStage = dwDrawStage;
     432  nmcd->hdc         = hdc;
     433  nmcd->rc          = rc;
     434  nmcd->dwItemSpec  = 0;
     435  nmcd->uItemState  = 0;
     436  nmcd->lItemlParam = 0;
     437  nmcdhdr.clrText   = infoPtr->clrText;
     438  nmcdhdr.clrTextBk = infoPtr->clrBk;
     439  nmcdhdr.iLevel    = 0;
     440
     441  return (BOOL)sendNotify(infoPtr->hwnd,NM_CUSTOMDRAW,&nmcdhdr.nmcd.hdr);
     442}
     443
     444/* FIXME: need to find out when the flags in uItemState need to be set */
     445static BOOL
     446TREEVIEW_SendCustomDrawItemNotify(TREEVIEW_INFO *infoPtr,HDC hdc,TREEVIEW_ITEM *wineItem,UINT uItemDrawState)
     447{
     448  NMTVCUSTOMDRAW nmcdhdr;
     449  LPNMCUSTOMDRAW nmcd;
     450  DWORD dwDrawStage,dwItemSpec;
     451  UINT uItemState;
     452  INT retval;
     453
     454  dwDrawStage = CDDS_ITEM | uItemDrawState;
     455  dwItemSpec = (DWORD)wineItem;
     456  uItemState = 0;
     457  if (wineItem == infoPtr->selectedItem)
     458  {
     459    uItemState |= CDIS_SELECTED;
     460    if (GetFocus() == infoPtr->hwnd) uItemState |= CDIS_FOCUS;
     461  }
     462  if (wineItem == infoPtr->focusItem)
     463    uItemState |= CDIS_FOCUS;
     464  if (wineItem == infoPtr->hotItem)
     465    uItemState |= CDIS_HOT;
     466
     467  nmcd = &nmcdhdr.nmcd;
     468  nmcd->dwDrawStage = dwDrawStage;
     469  nmcd->hdc         = hdc;
     470  nmcd->rc          = wineItem->rect;
     471  nmcd->dwItemSpec  = dwItemSpec;
     472  nmcd->uItemState  = uItemState;
     473  nmcd->lItemlParam = wineItem->lParam;
     474  nmcdhdr.clrText   = infoPtr->clrText;
     475  nmcdhdr.clrTextBk = infoPtr->clrBk;
     476  nmcdhdr.iLevel    = wineItem->iLevel;
     477
     478  retval = sendNotify(infoPtr->hwnd,NM_CUSTOMDRAW,&nmcdhdr.nmcd.hdr);
     479
     480  infoPtr->clrText = nmcdhdr.clrText;
     481  infoPtr->clrBk   = nmcdhdr.clrTextBk;
     482
     483  return (BOOL)retval;
     484}
     485
     486static void
     487TREEVIEW_GetDispInfo(TREEVIEW_INFO *infoPtr,TREEVIEW_ITEM *wineItem,NMTVDISPINFOW* info,UINT mask)
     488{
     489  info->item.mask = mask;
     490  info->item.hItem = wineItem;
     491  info->item.state = wineItem->state;
     492  info->item.stateMask = 0;
     493  info->item.lParam = wineItem->lParam;
     494
     495  sendNotify(infoPtr->hwnd,isUnicodeNotify(&infoPtr->header) ? TVN_GETDISPINFOW:TVN_GETDISPINFOA,&info->hdr);
     496}
     497
     498static void
     499TREEVIEW_UpdateDispInfo(TREEVIEW_INFO *infoPtr,TREEVIEW_ITEM *wineItem,UINT mask)
     500{
     501  NMTVDISPINFOW callback;
     502
     503  mask &= wineItem->callbackMask;
     504
     505  if (mask == 0) return;
     506
     507  if ((mask & TVIF_TEXT) /*&& (wineItem->cchTextMax < TEXT_CALLBACK_SIZE)*/)
     508  {
     509    wineItem->pszText = (WCHAR*)COMCTL32_ReAlloc(wineItem->pszText,TEXT_CALLBACK_SIZE*sizeof(WCHAR));
     510    wineItem->cchTextMax = TEXT_CALLBACK_SIZE;
     511  }
     512
     513  callback.item.pszText         = wineItem->pszText;
     514  callback.item.cchTextMax      = wineItem->cchTextMax;
     515  callback.item.iImage          = wineItem->iImage;
     516  callback.item.iSelectedImage  = wineItem->iSelectedImage;
     517  callback.item.cChildren       = wineItem->cChildren;
     518
     519  TREEVIEW_GetDispInfo(infoPtr,wineItem,&callback,mask);
     520
     521  /* It may have changed due to a call to SetItem. */
     522  mask &= wineItem->callbackMask;
     523
     524  if (mask & TVIF_TEXT)
     525  {
     526    if (callback.item.pszText != wineItem->pszText)
     527    {
     528      INT len = MAX((isUnicodeNotify(&infoPtr->header) ? lstrlenW(callback.item.pszText):lstrlenA((CHAR*)callback.item.pszText))+1,TEXT_CALLBACK_SIZE);
     529
     530      wineItem->pszText = (WCHAR*)COMCTL32_ReAlloc(wineItem->pszText,len*sizeof(WCHAR));
     531      if (isUnicodeNotify(&infoPtr->header))
     532        lstrcpyW(wineItem->pszText,callback.item.pszText);
     533      else
     534        lstrcpyAtoW(wineItem->pszText,(CHAR*)callback.item.pszText);
     535      wineItem->cchTextMax = len;
     536    } else if (!isUnicodeNotify(&infoPtr->header))
     537    {
     538      WCHAR *newText;
     539      INT len = MAX(lstrlenA((CHAR*)callback.item.pszText)+1,TEXT_CALLBACK_SIZE);
     540
     541      newText = (WCHAR*)COMCTL32_Alloc(len*sizeof(WCHAR));
     542      lstrcpyAtoW(newText,(LPSTR)callback.item.pszText);
     543      COMCTL32_Free(wineItem->pszText);
     544      wineItem->pszText = newText;
     545      wineItem->cchTextMax = len;
     546    }
     547  }
     548
     549  if (mask & TVIF_IMAGE)
     550    wineItem->iImage = callback.item.iImage;
     551
     552  if (mask & TVIF_SELECTEDIMAGE)
     553    wineItem->iSelectedImage = callback.item.iSelectedImage;
     554
     555  if (mask & TVIF_CHILDREN)
     556    wineItem->cChildren = callback.item.cChildren;
     557
     558  /* These members are now permanently set. */
     559  if (callback.item.mask & TVIF_DI_SETITEM)
     560    wineItem->callbackMask &= ~callback.item.mask;
     561}
     562
     563static VOID TREEVIEW_SendKeyDownNotify(TREEVIEW_INFO *infoPtr,UINT code,WORD wVKey)
     564{
     565  NMTVKEYDOWN nmkdhdr;
     566
     567  nmkdhdr.wVKey = wVKey;
     568  nmkdhdr.flags = 0;
     569
     570  sendNotify(infoPtr->hwnd,code,&nmkdhdr.hdr);
     571}
    298572
    299573/***************************************************************************
    300  * This method
     574 * This function uses cChildren field to decide whether the item has
     575 * children or not.
     576 * Note: if this returns TRUE, the child items may not actually exist,
     577 * they could be virtual.
     578 *
     579 * Just use wineItem->firstChild to check for physical children.
    301580 */
    302 static void TREEVIEW_RemoveAllChildren(
    303   HWND hwnd,
    304   TREEVIEW_ITEM *parentItem)
    305 {
    306   TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
    307   TREEVIEW_ITEM *killItem;
    308   INT    kill;
    309 
    310   kill=(INT)parentItem->firstChild;
    311   while (kill)
    312   {
    313     tv_set_bit ( kill, infoPtr->freeList);
    314     infoPtr->uNumItems--;
    315     killItem=& infoPtr->items[kill];
    316     if (killItem->pszText != LPSTR_TEXTCALLBACKW)
    317       COMCTL32_Free (killItem->pszText);
    318     killItem->pszText = NULL;
    319     TREEVIEW_SendTreeviewNotify (hwnd,isUnicodeNotify(&infoPtr->header) ? TVN_DELETEITEMW:TVN_DELETEITEMA, 0, (HTREEITEM)kill, 0);
    320     if (killItem->firstChild)
    321       TREEVIEW_RemoveAllChildren (hwnd, killItem);
    322     kill=(INT)killItem->sibling;
    323   }
    324 
    325   parentItem->firstChild = 0;
    326 }
    327 
    328 
    329 static void
    330 TREEVIEW_RemoveItem (HWND hwnd, TREEVIEW_ITEM *wineItem)
    331 
    332 {
    333  TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
    334  TREEVIEW_ITEM *parentItem, *upsiblingItem, *siblingItem;
    335  INT iItem;
    336 
    337  if (infoPtr->hwndEdit) SetFocus(hwnd);
    338 
    339  iItem=(INT)wineItem->hItem;
    340  tv_set_bit(iItem,infoPtr->freeList);
    341  infoPtr->uNumItems--;
    342  parentItem=NULL;
    343  if (wineItem->pszText != LPSTR_TEXTCALLBACKW)
    344    COMCTL32_Free (wineItem->pszText);
    345  wineItem->pszText = NULL;
    346 
    347  TREEVIEW_SendTreeviewNotify (hwnd,isUnicodeNotify(&infoPtr->header) ? TVN_DELETEITEMW:TVN_DELETEITEMA, 0, (HTREEITEM)iItem, 0);
    348 
    349  if (wineItem->firstChild)
    350    TREEVIEW_RemoveAllChildren (hwnd,wineItem);
    351 
    352  if (wineItem->parent) {
    353    parentItem=& infoPtr->items [(INT)wineItem->parent];
    354    if ((INT)parentItem->firstChild==iItem)
    355      parentItem->firstChild=wineItem->sibling;
    356  }
    357 
    358  if (iItem==(INT)infoPtr->TopRootItem)
    359         infoPtr->TopRootItem=(HTREEITEM)wineItem->sibling;
    360  if (wineItem->upsibling) {
    361         upsiblingItem=& infoPtr->items [(INT)wineItem->upsibling];
    362         upsiblingItem->sibling=wineItem->sibling;
    363  }
    364  if (wineItem->sibling) {
    365         siblingItem=& infoPtr->items [(INT)wineItem->sibling];
    366         siblingItem->upsibling=wineItem->upsibling;
    367  }
    368 
    369  if (iItem==(INT)infoPtr->selectedItem) {
    370    if (!wineItem->upsibling)
    371      infoPtr->selectedItem = 0;
    372    else
    373      TREEVIEW_DoSelectItem(hwnd, TVGN_CARET, wineItem->upsibling, TVC_UNKNOWN);
    374  }
    375 }
    376 
    377 /* Note:TREEVIEW_RemoveTree doesn't remove infoPtr itself */
    378 
    379 static void TREEVIEW_RemoveTree (HWND hwnd)
    380 {
    381   TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
    382   TREEVIEW_ITEM *killItem;
    383   int i;
    384 
    385   TREEVIEW_EndEditLabelNow(hwnd,TRUE);
    386 
    387   for (i=1; i<=(INT)infoPtr->uMaxHandle; i++)
    388         if (!tv_test_bit (i, infoPtr->freeList)) {
    389                 killItem=& infoPtr->items [i];
    390                 if (killItem->pszText != LPSTR_TEXTCALLBACKW)
    391                   COMCTL32_Free (killItem->pszText);
    392                 killItem->pszText = NULL;
    393 
    394                 TREEVIEW_SendTreeviewNotify(hwnd,isUnicodeNotify(&infoPtr->header) ? TVN_DELETEITEMW:TVN_DELETEITEMA, 0, killItem->hItem, 0);
    395                 }
    396 
    397   if (infoPtr->uNumPtrsAlloced) {
    398         COMCTL32_Free (infoPtr->items);
    399         COMCTL32_Free (infoPtr->freeList);
    400         infoPtr->uNumItems=0;
    401         infoPtr->uNumPtrsAlloced=0;
    402         infoPtr->uMaxHandle=0;
    403         infoPtr->lefttop.x = infoPtr->lefttop.y = 0;
    404     }
    405 }
    406 
    407 
    408 static LRESULT
    409 TREEVIEW_GetImageList (HWND hwnd, WPARAM wParam, LPARAM lParam)
    410 {
    411   TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
    412 
    413   if ((INT)wParam == TVSIL_NORMAL)
    414         return (LRESULT) infoPtr->himlNormal;
    415   if ((INT)wParam == TVSIL_STATE)
    416         return (LRESULT) infoPtr->himlState;
    417 
    418   return 0;
    419 }
    420 
    421 static LRESULT
    422 TREEVIEW_SetImageList (HWND hwnd, WPARAM wParam, LPARAM lParam)
    423 {
    424     TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
    425     HIMAGELIST himlTemp;
    426 
    427     switch ((INT)wParam) {
    428         case TVSIL_NORMAL:
    429             himlTemp = infoPtr->himlNormal;
    430             infoPtr->himlNormal = (HIMAGELIST)lParam;
    431             return (LRESULT)himlTemp;
    432 
    433         case TVSIL_STATE:
    434             himlTemp = infoPtr->himlState;
    435             infoPtr->himlState = (HIMAGELIST)lParam;
    436             return (LRESULT)himlTemp;
    437     }
    438 
    439     return (LRESULT)NULL;
    440 }
    441 
    442 
    443 
    444 static LRESULT
    445 TREEVIEW_SetItemHeight (HWND hwnd, WPARAM wParam)
    446 {
    447   TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
    448   INT cx,cy,prevHeight = infoPtr->uItemHeight;
    449 
    450   if (wParam == -1)
    451   {
    452     infoPtr->uItemHeight = -1;
    453 
    454     return prevHeight;
    455   }
    456 
    457   ImageList_GetIconSize (infoPtr->himlNormal, &cx, &cy);
    458 
    459   if (wParam > cy) cy = wParam;
    460   infoPtr->uItemHeight = cy;
    461 
    462   if (!(GetWindowLongA(hwnd, GWL_STYLE) & TVS_NONEVENHEIGHT))
    463     if (infoPtr->uItemHeight & 0x1) infoPtr->uItemHeight++;
    464 
    465   if (prevHeight != infoPtr->uItemHeight)
    466   {
    467     infoPtr->uInternalStatus |= TV_CALCALL;
    468     TREEVIEW_QueueRefresh(hwnd);
    469   }
    470 
    471   return prevHeight;
    472 }
    473 
    474 static LRESULT
    475 TREEVIEW_GetItemHeight (HWND hwnd)
    476 {
    477   TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
    478 
    479   return infoPtr->uItemHeight;
    480 }
    481 
    482 static LRESULT
    483 TREEVIEW_GetLineColor (HWND hwnd, WPARAM wParam, LPARAM lParam)
    484 {
    485   TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
    486 
    487   //TRACE("\n");
    488   return (LRESULT) infoPtr->clrLine;
    489 }
    490 
    491 static LRESULT
    492 TREEVIEW_SetLineColor (HWND hwnd, WPARAM wParam, LPARAM lParam)
    493 {
    494   TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
    495   COLORREF prevColor=infoPtr->clrLine;
    496 
    497   //TRACE("\n");
    498   infoPtr->clrLine=(COLORREF) lParam;
    499   return (LRESULT) prevColor;
    500 }
    501 
    502 static LRESULT
    503 TREEVIEW_GetInsertMarkColor (HWND hwnd, WPARAM wParam, LPARAM lParam)
    504 {
    505   TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
    506 
    507   //TRACE("\n");
    508   return (LRESULT) infoPtr->clrInsertMark;
    509 }
    510 
    511 static LRESULT
    512 TREEVIEW_SetInsertMarkColor (HWND hwnd, WPARAM wParam, LPARAM lParam)
    513 {
    514   TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
    515   COLORREF prevColor=infoPtr->clrInsertMark;
    516 
    517   //TRACE("%d %ld\n",wParam,lParam);
    518   infoPtr->clrInsertMark=(COLORREF) lParam;
    519   return (LRESULT) prevColor;
    520 }
    521 
    522 static LRESULT
    523 TREEVIEW_SetInsertMark (HWND hwnd, WPARAM wParam, LPARAM lParam)
    524 {
    525   TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
    526 
    527   //FIXME("%d %ld\n",wParam,lParam);
    528   if (!TREEVIEW_ValidItem (infoPtr, (HTREEITEM)lParam)) return 0;
    529   //FIXME("%d %ld\n",wParam,lParam);
    530 
    531   infoPtr->insertBeforeorAfter=(BOOL) wParam;
    532   infoPtr->insertMarkItem=(HTREEITEM) lParam;
    533 
    534   TREEVIEW_Refresh(hwnd);
    535 
    536   return 1;
    537 }
    538 
    539 static LRESULT
    540 TREEVIEW_SetTextColor (HWND hwnd, WPARAM wParam, LPARAM lParam)
    541 {
    542   TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
    543   COLORREF prevColor=infoPtr->clrText;
    544 
    545   infoPtr->clrText=(COLORREF) lParam;
    546   if (infoPtr->clrText != prevColor)
    547     TREEVIEW_Refresh(hwnd);
    548 
    549   return (LRESULT) prevColor;
    550 }
    551 
    552 static LRESULT
    553 TREEVIEW_GetBkColor (HWND hwnd)
    554 {
    555   TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
    556 
    557   return (LRESULT) infoPtr->clrBk;
    558 }
    559 
    560 static LRESULT
    561 TREEVIEW_SetBkColor (HWND hwnd, WPARAM wParam, LPARAM lParam)
    562 {
    563   TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
    564   COLORREF prevColor=infoPtr->clrBk;
    565 
    566   infoPtr->clrBk=(COLORREF) lParam;
    567   if (infoPtr->clrBk != prevColor)
    568     TREEVIEW_Refresh(hwnd);
    569 
    570 
    571   return (LRESULT) prevColor;
    572 }
    573 
    574 static LRESULT
    575 TREEVIEW_GetTextColor (HWND hwnd)
    576 {
    577   TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
    578 
    579   return (LRESULT) infoPtr->clrText;
    580 }
    581 
    582 
    583 /* cdmode: custom draw mode as received from app. in first NMCUSTOMDRAW
    584            notification */
    585 
    586 #define TREEVIEW_LEFT_MARGIN 8
    587 
    588 #ifdef OS2LINEHACK
    589 //CB: hack for PS_DOT bug in Open32 pen handling
    590 
    591 BOOL drawPixel;
    592 HDC  drawDC;
    593 
    594 VOID CALLBACK TREEVIEW_DDAProc(int x,int y,LPARAM lpData)
    595 {
    596   if (drawPixel) SetPixel(drawDC,x,y,(COLORREF)lpData);
    597   drawPixel = !drawPixel;
    598 }
    599 
    600 static void TREEVIEW_Polyline(HDC hdc,const POINT *lppt,int cPoints,COLORREF color)
    601 {
    602   INT x;
    603 
    604   drawPixel = TRUE;
    605   drawDC = hdc;
    606   for (x = 0;x < cPoints-1;x++)
    607     LineDDA(lppt[x].x,lppt[x].y,lppt[x+1].x,lppt[x+1].y,TREEVIEW_DDAProc,color);
    608 }
    609 #endif
    610 
    611 //pen must be selected!
    612 
    613 static void TREEVIEW_DrawVLines(HDC hdc,TREEVIEW_INFO *infoPtr,TREEVIEW_ITEM *item)
    614 {
    615   POINT points[2];
    616   INT center;
    617 
    618   center = (item->rect.top+item->rect.bottom)/2;
    619 //INT cChildren = TREEVIEW_HasChildren(hwnd,wineItem);
    620   if ((item->iLevel == 0) && !item->upsibling && item->sibling)
    621   {
    622     TREEVIEW_ITEM *lastItem = &infoPtr->items[(INT)item->sibling];
    623 
    624     while (lastItem->sibling) lastItem = &infoPtr->items[(INT)lastItem->sibling];
    625 
    626     points[0].y = center;
    627     points[1].x = points[0].x = 8;
    628     points[1].y = (lastItem->rect.top+lastItem->rect.bottom)/2;
    629 #ifdef OS2LINEHACK
    630     TREEVIEW_Polyline(hdc,points,2,infoPtr->clrLine);
    631 #else
    632     Polyline(hdc,points,2);
    633 #endif
    634   }
    635 
    636   if (item->firstChild && (item->state & TVIS_EXPANDED))
    637   {
    638     TREEVIEW_ITEM *lastItem = &infoPtr->items[(INT)item->firstChild];
    639 
    640     while (lastItem->sibling) lastItem = &infoPtr->items[(INT)lastItem->sibling];
    641 
    642     points[0].y = (lastItem->upsibling != NULL) ?
    643                    item->rect.bottom-3:  /* is linked to an icon       */
    644                    item->rect.bottom+1;  /* is linked to a +/- box     */
    645     points[1].x = points[0].x = 28+20*item->iLevel-infoPtr->lefttop.x;
    646     points[1].y = (lastItem->rect.top+lastItem->rect.bottom)/2;  /* is linked to a +/- box     */
    647 #ifdef OS2LINEHACK
    648     TREEVIEW_Polyline(hdc,points,2,infoPtr->clrLine);
    649 #else
    650     Polyline(hdc,points,2);
    651 #endif
    652   }
    653 }
    654 
    655 static VOID TREEVIEW_DrawHottrackLine(HDC hdc,TREEVIEW_ITEM *item)
    656 {
    657   HPEN hPen,hOldPen;
    658   INT rop;
    659 
    660   if (!item->visible) return;
    661 
    662   rop = SetROP2(hdc,R2_XORPEN);
    663   hPen = CreatePen(PS_SOLID,2,RGB(0,0,0));
    664   hOldPen = SelectObject(hdc,hPen);
    665 
    666   MoveToEx(hdc,item->text.left,item->text.bottom-1,NULL);
    667   LineTo(hdc,item->text.right,item->text.bottom-1);
    668 
    669   DeleteObject(hPen);
    670   SelectObject(hdc,hOldPen);
    671   SetROP2(hdc,rop);
    672 }
    673 
    674 static void
    675 TREEVIEW_DrawItem(HWND hwnd,HDC hdc,TREEVIEW_ITEM *item,DWORD dwStyle,TREEVIEW_INFO *infoPtr)
    676 {
    677   INT   center,cditem;
    678   HFONT hOldFont;
    679 
    680   if (!item->calculated) TREEVIEW_CalcItem(hwnd,hdc,dwStyle,infoPtr,item);
    681 
    682   if (item->state & TVIS_BOLD)
    683     hOldFont = SelectObject(hdc,infoPtr->hBoldFont);
    684   else
    685     hOldFont = SelectObject(hdc,infoPtr->hFont);
    686 
    687   cditem = 0;
    688 
    689   if (infoPtr->cdmode & CDRF_NOTIFYITEMDRAW)
    690   {
    691     cditem = TREEVIEW_SendCustomDrawItemNotify(hwnd, hdc, item, CDDS_ITEMPREPAINT);
    692 
    693     if (cditem & CDRF_SKIPDEFAULT) return;
    694   }
    695 
    696   //CDRF_NEWFONT: CCM_SETVERSION < 5 clips text (what we do now)
    697   //                             > 5 ??? recalc?
    698 
    699   /*
    700    * Set drawing starting points
    701    */
    702   center = (item->rect.top+item->rect.bottom)/2; /* this item vertical center */
    703 
    704   /*
    705    * Display the tree hierarchy
    706    */
    707   if (dwStyle & TVS_HASLINES)
    708   {
    709     /*
    710      * Write links to parent node
    711      * we draw the L starting from the child to the parent
    712      *
    713      * points[0] is attached to the current item
    714      * points[1] is the L corner
    715      * points[2] is attached to the parent or the up sibling
    716      */
    717     if (dwStyle & TVS_LINESATROOT)
    718     {
    719       TREEVIEW_ITEM *upNode    = NULL;
    720       RECT  upRect             = {0,0,0,0};
    721       HPEN  hOldPen, hnewPen;
    722       POINT points[2];
    723 
    724       hnewPen = CreatePen(PS_DOT, 0, infoPtr->clrLine);
    725       hOldPen = SelectObject(hdc,hnewPen);
    726 
    727       TREEVIEW_DrawVLines(hdc,infoPtr,item);
    728 
    729       /*
    730        * determine the target location of the line at root, either be linked
    731        * to the up sibling or to the parent node.
    732        */
    733       if (item->upsibling)
    734         upNode  = TREEVIEW_ValidItem (infoPtr, item->upsibling);
    735       else if (item->parent)
    736         upNode  = TREEVIEW_ValidItem (infoPtr, item->parent);
    737 
    738       if (upNode) upRect = upNode->rect;
    739 
    740       if (item->iLevel == 0)
    741       {
    742         points[1].x = upRect.left+8;
    743         points[0].x = points[1].x + 10;
    744         points[1].y = points[0].y = center;
    745       } else
    746       {
    747         points[1].x = 8+(20*item->iLevel)-infoPtr->lefttop.x;
    748         points[1].y = points[0].y = center;
    749         points[0].x = points[1].x + 10;
    750       }
    751 
    752       /*
    753        * Get a dotted pen
    754        */
    755 #ifdef OS2LINEHACK
    756       TREEVIEW_Polyline(hdc,points,2,infoPtr->clrLine);
    757 #else
    758       Polyline(hdc,points,2);
    759 #endif
    760       DeleteObject(hnewPen);
    761       SelectObject(hdc, hOldPen);
    762     }
    763   }
    764 
    765   /*
    766    * Display the (+/-) signs
    767    */
    768   if ((dwStyle & TVS_HASBUTTONS) && (dwStyle & TVS_HASLINES))
    769   {
    770     if (TREEVIEW_HasChildren(hwnd,item))
    771     {
    772       INT vCenter = (item->expandBox.top+item->expandBox.bottom)/2;
    773       INT hCenter = (item->expandBox.right+item->expandBox.left)/2;
    774 
    775       Rectangle(hdc,item->expandBox.left,item->expandBox.top,item->expandBox.right,item->expandBox.bottom);
    776 
    777       MoveToEx(hdc,item->expandBox.left+2,vCenter,NULL);
    778       LineTo(hdc,item->expandBox.right-2,vCenter);
    779 
    780       if (!(item->state & TVIS_EXPANDED))
    781       {
    782         MoveToEx(hdc,hCenter,item->expandBox.top+2,NULL);
    783         LineTo(hdc,hCenter,item->expandBox.bottom-2);
    784       }
    785     }
    786   }
    787 
    788   /*
    789    * Display the image associated with this item
    790    */
    791   if ((item->mask & (TVIF_IMAGE | TVIF_SELECTEDIMAGE)) || (dwStyle & TVS_CHECKBOXES))
    792   {
    793     INT        imageIndex;
    794     HIMAGELIST *himlp = NULL;
    795 
    796     /* State images are displayed to the left of the Normal image
    797      * image number is in state; zero should be `display no image'.
    798      * FIXME: that last sentence looks like it needs some checking.
    799      */
    800     if (infoPtr->himlState)
    801       himlp =& infoPtr->himlState;
    802     imageIndex = item->state >> 12;
    803 
    804     if (himlp && imageIndex)
    805     {
    806       imageIndex--;       /* see FIXME */
    807       ImageList_Draw(*himlp,imageIndex,hdc,item->statebitmap.left,item->statebitmap.top,ILD_NORMAL);
    808     }
    809 
    810     /* Now, draw the normal image; can be either selected or
    811      * non-selected image.
    812      */
    813 
    814     himlp = NULL;
    815     if (infoPtr->himlNormal)
    816       himlp = &infoPtr->himlNormal; /* get the image list */
    817 
    818     imageIndex = item->iImage;
    819     if ((item->state & TVIS_SELECTED) && item->iSelectedImage)
    820     {
    821       /* The item is curently selected */
    822       if (item->iSelectedImage == I_IMAGECALLBACK)
    823         imageIndex = TREEVIEW_CallbackSelectedImage(hwnd,item);
    824       else
    825         imageIndex = item->iSelectedImage;
    826     } else
    827     {
    828       /* The item is not selected */
    829       if (item->iImage == I_IMAGECALLBACK)
    830         imageIndex = TREEVIEW_CallbackImage(hwnd,item);
    831       else
    832         imageIndex = item->iImage;
    833     }
    834 
    835     if (himlp && (imageIndex != I_IMAGENONE))
    836     {
    837       int ovlIdx = 0;
    838 
    839       if(item->stateMask & TVIS_OVERLAYMASK)
    840         ovlIdx = item->state & TVIS_OVERLAYMASK;
    841 
    842       ImageList_Draw(*himlp,imageIndex,hdc,item->bitmap.left,item->bitmap.top,ILD_NORMAL | ovlIdx);
    843     }
    844   }
    845 
    846   /*
    847    * Display the text associated with this item
    848    */
    849   /* Don't paint item's text if it's being edited */
    850   if (!infoPtr->hwndEdit || (infoPtr->editItem != item->hItem))
    851   {
    852     if ((item->mask & TVIF_TEXT) && (item->pszText))
    853     {
    854       UINT  uTextJustify = DT_LEFT;
    855       COLORREF oldTextColor = 0;
    856       INT      oldBkMode;
    857       HBRUSH   hbrBk = 0;
    858       BOOL     inFocus = GetFocus() == hwnd;
    859       WCHAR* text;
    860       BOOL mustFree = FALSE;
    861 
    862       oldBkMode = SetBkMode(hdc, TRANSPARENT);
    863 
    864       /* - If item is drop target or it is selected and window is in focus -
    865        * use blue background (COLOR_HIGHLIGHT).
    866        * - If item is selected, window is not in focus, but it has style
    867        * TVS_SHOWSELALWAYS - use grey background (COLOR_BTNFACE)
    868        * - Otherwise - don't fill background
    869        */
    870       if ((item->state & TVIS_DROPHILITED) ||
    871           ((item->state & TVIS_SELECTED) &&
    872           (inFocus || (GetWindowLongA( hwnd, GWL_STYLE) & TVS_SHOWSELALWAYS))))
    873       {
    874         if ((item->state & TVIS_DROPHILITED) || inFocus)
    875         {
    876           hbrBk = CreateSolidBrush(GetSysColor( COLOR_HIGHLIGHT));
    877           oldTextColor = SetTextColor(hdc, GetSysColor( COLOR_HIGHLIGHTTEXT));
    878         } else
    879         {
    880           hbrBk = CreateSolidBrush(GetSysColor( COLOR_BTNFACE));
    881 
    882           if (infoPtr->clrText == -1)
    883             oldTextColor = SetTextColor(hdc, GetSysColor( COLOR_WINDOWTEXT));
    884           else
    885             oldTextColor = SetTextColor(hdc, infoPtr->clrText);
    886         }
    887       } else
    888       {
    889         if (infoPtr->clrText == -1)
    890           oldTextColor = SetTextColor(hdc, GetSysColor( COLOR_WINDOWTEXT));
    891         else
    892           oldTextColor = SetTextColor(hdc, infoPtr->clrText);
    893       }
    894 
    895       if (item->pszText == LPSTR_TEXTCALLBACKW)
    896         text = TREEVIEW_CallbackText(hwnd,item,&mustFree);
    897       else
    898         text = item->pszText;
    899 
    900       if (hbrBk)
    901       {
    902         FillRect(hdc, &item->text, hbrBk);
    903         DeleteObject(hbrBk);
    904       }
    905 
    906       item->text.left += 2;
    907 
    908       /* Draw it */
    909       DrawTextW(hdc,text,lstrlenW(text),&item->text,uTextJustify | DT_VCENTER | DT_SINGLELINE | DT_NOPREFIX);
    910       if (mustFree) COMCTL32_Free(text);
    911 
    912       item->text.left -= 2;
    913 
    914       /* Restore the hdc state */
    915       SetTextColor( hdc, oldTextColor);
    916 
    917       /* Draw the box arround the selected item */
    918       if (item->state & TVIS_SELECTED)
    919       {
    920         HPEN  hNewPen     = CreatePen(PS_DOT, 0, GetSysColor(COLOR_WINDOWTEXT) );
    921         HPEN  hOldPen     = SelectObject( hdc, hNewPen );
    922         INT   rop         = SetROP2(hdc, R2_XORPEN);
    923         POINT points[5];
    924 
    925         points[4].x = points[0].x = item->text.left;
    926         points[4].y = points[0].y = item->text.top;
    927         points[1].x = item->text.right-1 ;
    928         points[1].y = item->text.top;
    929         points[2].x = item->text.right-1;
    930         points[2].y = item->text.bottom-1;
    931         points[3].x = item->text.left;
    932         points[3].y = item->text.bottom-1;
    933 
    934         Polyline (hdc,points,5);
    935 
    936         SetROP2(hdc, rop);
    937         DeleteObject(hNewPen);
    938         SelectObject(hdc, hOldPen);
    939       }
    940 
    941       if (oldBkMode != TRANSPARENT)
    942         SetBkMode(hdc, oldBkMode);
    943     }
    944   }
    945 
    946   /* Draw insertion mark if necessary */
    947 
    948   if (item->hItem == infoPtr->insertMarkItem)
    949   {
    950     HPEN hNewPen, hOldPen;
    951     int offset;
    952 
    953     hNewPen = CreatePen(PS_SOLID, 2, infoPtr->clrInsertMark);
    954     hOldPen = SelectObject( hdc, hNewPen );
    955 
    956     if (infoPtr->insertBeforeorAfter)
    957       offset = item->text.top+1;
    958     else
    959       offset = item->text.bottom-1;
    960 
    961     MoveToEx(hdc,item->text.left, offset-3, NULL);
    962     LineTo(hdc,item->text.left, offset+3);
    963 
    964     MoveToEx(hdc,item->text.left,offset,NULL);
    965     LineTo(hdc,item->text.right-2,offset);
    966 
    967     MoveToEx(hdc,item->text.right-2, offset+3, NULL);
    968     LineTo(hdc,item->text.right-2, offset-3);
    969 
    970     DeleteObject(hNewPen);
    971 
    972     SelectObject(hdc, hOldPen);
    973   }
    974 
    975   //draw hot item if necessary
    976   if (item->hItem == infoPtr->hotItem)
    977     TREEVIEW_DrawHottrackLine(hdc,item);
    978 
    979   if (cditem & CDRF_NOTIFYPOSTPAINT)
    980     cditem = TREEVIEW_SendCustomDrawItemNotify(hwnd, hdc, item, CDDS_ITEMPOSTPAINT);
    981 
    982   SelectObject (hdc, hOldFont);
    983 }
    984 
    985 static LRESULT
    986 TREEVIEW_GetItemRect (HWND hwnd, WPARAM wParam, LPARAM lParam)
    987 {
    988   TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
    989   TREEVIEW_ITEM *wineItem;
    990   HTREEITEM     *iItem;
    991   LPRECT        lpRect   = (LPRECT)lParam;
    992 
    993   /*
    994    * validate parameters
    995    */
    996   if ((infoPtr == NULL) || (lpRect == NULL))
    997     return FALSE;
    998 
    999   TREEVIEW_UnqueueRefresh(hwnd,TRUE,TRUE);
    1000 
    1001   /*
    1002    * retrieve the item ptr
    1003    */
    1004   iItem = (HTREEITEM *) lParam;
    1005   wineItem = TREEVIEW_ValidItem (infoPtr, *iItem);
    1006   if (!wineItem || !wineItem->visible)
    1007     return FALSE;
    1008 
    1009   /*
    1010    * If wParam is TRUE return the text size otherwise return
    1011    * the whole item size
    1012    */
    1013   if (wParam)
    1014   {
    1015     lpRect->left   = wineItem->text.left;
    1016     lpRect->right  = wineItem->text.right;
    1017     lpRect->bottom = wineItem->text.bottom;
    1018     lpRect->top    = wineItem->text.top;
    1019   } else
    1020   {
    1021     lpRect->left   = wineItem->rect.left;
    1022     lpRect->right  = wineItem->rect.right;
    1023     lpRect->bottom = wineItem->rect.bottom;
    1024     lpRect->top    = wineItem->rect.top;
    1025   }
    1026 
    1027   return TRUE;
    1028 }
    1029 
    1030 static LRESULT
    1031 TREEVIEW_GetVisibleCount (HWND hwnd,  WPARAM wParam, LPARAM lParam)
    1032 
    1033 {
    1034   TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
    1035 
    1036   return (LRESULT)infoPtr->uVisibleHeight/infoPtr->uRealItemHeight;
    1037 }
    1038 
    1039 static LRESULT TREEVIEW_SetItem(HWND hwnd,WPARAM wParam,LPARAM lParam,BOOL unicode)
    1040 {
    1041   TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
    1042   TREEVIEW_ITEM *wineItem;
    1043   TVITEMEXW *tvItem;
    1044   INT iItem,len;
    1045   BOOL mustRefresh = FALSE;
    1046   BOOL mustRepaintItem = FALSE;
    1047   BOOL refreshFullLine = FALSE;
    1048 
    1049   if (infoPtr->hwndEdit) SetFocus(hwnd);
    1050 
    1051   tvItem = (LPTVITEMEXW)lParam;
    1052   iItem = (INT)tvItem->hItem;
    1053 
    1054   wineItem = TREEVIEW_ValidItem(infoPtr,(HTREEITEM)iItem);
    1055   if (!wineItem) return FALSE;
    1056 
    1057   if ((tvItem->mask & TVIF_CHILDREN) && (wineItem->cChildren != tvItem->cChildren))
    1058   {
    1059     wineItem->cChildren = tvItem->cChildren;
    1060     mustRefresh = TRUE;
    1061   }
    1062 
    1063   if ((tvItem->mask & TVIF_IMAGE) && (wineItem->iImage != tvItem->iImage))
    1064   {
    1065     wineItem->iImage = tvItem->iImage;
    1066     mustRepaintItem = TRUE;
    1067   }
    1068 
    1069   if ((tvItem->mask & TVIF_INTEGRAL) && (wineItem->iIntegral != tvItem->iIntegral))
    1070   {
    1071     wineItem->iIntegral = tvItem->iIntegral;
    1072     mustRefresh = TRUE;
    1073   }
    1074 
    1075   if (tvItem->mask & TVIF_PARAM)
    1076     wineItem->lParam = tvItem->lParam;
    1077 
    1078   if ((tvItem->mask & TVIF_SELECTEDIMAGE) && (wineItem->iSelectedImage != tvItem->iSelectedImage))
    1079   {
    1080     wineItem->iSelectedImage = tvItem->iSelectedImage;
    1081     mustRepaintItem = TRUE;
    1082   }
    1083 
    1084   if (tvItem->mask & TVIF_STATE)
    1085   {
    1086     DWORD oldState = wineItem->state,oldMask = wineItem->stateMask;
    1087 
    1088     wineItem->state &= ~tvItem->stateMask;
    1089     wineItem->state |= (tvItem->state & tvItem->stateMask);
    1090     wineItem->stateMask |= tvItem->stateMask;
    1091     mustRepaintItem = (oldState != wineItem->state) || (oldMask != wineItem->stateMask);
    1092   }
    1093 
    1094   if (tvItem->mask & TVIF_TEXT)
    1095   {
    1096     if (unicode)
    1097     {
    1098       if (tvItem->pszText != LPSTR_TEXTCALLBACKW)
    1099       {
    1100         len = lstrlenW(tvItem->pszText)+1;
    1101 
    1102         mustRepaintItem = ((wineItem->pszText == LPSTR_TEXTCALLBACKW) || (lstrcmpW(wineItem->pszText,tvItem->pszText) != 0));
    1103         if (len > wineItem->cchTextMax)
    1104         {
    1105           if (wineItem->pszText != LPSTR_TEXTCALLBACKW) COMCTL32_Free(wineItem->pszText);
    1106           wineItem->pszText= (WCHAR*)COMCTL32_Alloc(len*sizeof(WCHAR));
    1107           wineItem->cchTextMax = len;
    1108         }
    1109         lstrcpyW(wineItem->pszText,tvItem->pszText);
    1110       } else
    1111       {
    1112         if (wineItem->pszText != LPSTR_TEXTCALLBACKW) COMCTL32_Free(wineItem->pszText);
    1113         wineItem->cchTextMax = 0;
    1114         wineItem->pszText = LPSTR_TEXTCALLBACKW;
    1115         mustRepaintItem = TRUE;
    1116       }
    1117     } else
    1118     {
    1119       if ((LPSTR)tvItem->pszText != LPSTR_TEXTCALLBACKA)
    1120       {
    1121         mustRepaintItem = ((wineItem->pszText == LPSTR_TEXTCALLBACKW) || (lstrcmpAtoW((CHAR*)tvItem->pszText,wineItem->pszText) != 0));
    1122         len = lstrlenA((LPSTR)tvItem->pszText)+1;
    1123         if (len > wineItem->cchTextMax)
    1124         {
    1125           if (wineItem->pszText != LPSTR_TEXTCALLBACKW) COMCTL32_Free(wineItem->pszText);
    1126           wineItem->pszText = (WCHAR*)COMCTL32_Alloc(len*sizeof(WCHAR));
    1127           wineItem->cchTextMax = len;
    1128         }
    1129         lstrcpyAtoW(wineItem->pszText,(LPSTR)tvItem->pszText);
    1130       } else
    1131       {
    1132         if (wineItem->pszText != LPSTR_TEXTCALLBACKW) COMCTL32_Free(wineItem->pszText);
    1133         wineItem->cchTextMax = 0;
    1134         wineItem->pszText = LPSTR_TEXTCALLBACKW;
    1135         mustRepaintItem = TRUE;
    1136       }
    1137     }
    1138     refreshFullLine = TRUE;
    1139   }
    1140 
    1141   if (wineItem->mask != (wineItem->mask | tvItem->mask))
    1142   {
    1143     wineItem->mask |= tvItem->mask;
    1144     mustRepaintItem = TRUE;
    1145   }
    1146 
    1147   if (mustRepaintItem || mustRefresh)
    1148   {
    1149     wineItem->calculated = FALSE;
    1150     if (mustRefresh)
    1151       TREEVIEW_QueueRefresh(hwnd);
    1152     else
    1153       TREEVIEW_RefreshItem(hwnd,wineItem,refreshFullLine);
    1154   }
    1155 
    1156   return TRUE;
    1157 }
    1158 
    1159 static LRESULT
    1160 TREEVIEW_GetItemState (HWND hwnd, WPARAM wParam, LPARAM lParam)
    1161 
    1162 {
    1163     TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
    1164     TREEVIEW_ITEM *wineItem;
    1165 
    1166     wineItem = TREEVIEW_ValidItem (infoPtr, (HTREEITEM)wParam);
    1167     if (!wineItem) return 0;
    1168 
    1169     return (wineItem->state & lParam);
    1170 }
    1171 
    1172 static void TREEVIEW_Refresh(HWND hwnd)
    1173 {
    1174   TREEVIEW_UnqueueRefresh(hwnd,TRUE,FALSE);
    1175 
    1176   InvalidateRect(hwnd,NULL,TRUE);
    1177 }
    1178 
    1179 static void TREEVIEW_RefreshItem(HWND hwnd,TREEVIEW_ITEM *item,BOOL wholeLine)
    1180 {
    1181   if (item && item->visible)
    1182   {
    1183     RECT rect = item->rect;
    1184 
    1185     if (wholeLine)
    1186     {
    1187       RECT client;
    1188 
    1189       GetClientRect(hwnd,&client);
    1190       rect.left = 0;
    1191       rect.right = client.right;
    1192     } else
    1193     {
    1194       DWORD dwStyle = GetWindowLongA(hwnd,GWL_STYLE);
    1195 
    1196       //redraw text and image
    1197       if ((((item->mask & (TVIF_IMAGE | TVIF_SELECTEDIMAGE)) == (TVIF_IMAGE | TVIF_SELECTEDIMAGE)) || (dwStyle & TVS_CHECKBOXES)) && (item->iSelectedImage != item->iImage))
    1198       {
    1199         rect.left += TREEVIEW_LEFT_MARGIN;
    1200         if (item->iLevel != 0) rect.left += (5*item->iLevel);
    1201         rect.left += 15;
    1202       } else rect = item->text;
    1203     }
    1204 
    1205     InvalidateRect(hwnd,&rect,TRUE);
    1206   }
    1207 }
    1208 
    1209 //HDC parameter is optional
    1210 
    1211 static void TREEVIEW_CalcItem(HWND hwnd,HDC hdc,DWORD dwStyle,TREEVIEW_INFO *infoPtr,TREEVIEW_ITEM *item)
    1212 {
    1213   RECT r;
    1214   INT center,xpos;
    1215   BOOL ownDC = FALSE;
    1216 
     581static INT
     582TREEVIEW_HasChildren(TREEVIEW_INFO *infoPtr,TREEVIEW_ITEM *wineItem)
     583{
     584  TREEVIEW_UpdateDispInfo(infoPtr, wineItem, TVIF_CHILDREN);
     585
     586  return wineItem->cChildren > 0;
     587}
     588
     589/* Item Position ********************************************************/
     590
     591static VOID
     592TREEVIEW_ComputeTextWidth(TREEVIEW_INFO *infoPtr,TREEVIEW_ITEM *item,HDC hDC,BOOL useFont)
     593{
     594    HDC hdc;
     595    HFONT hOldFont;
     596    RECT rect;
     597
     598    /* DRAW's OM docker creates items like this */
     599    if (item->pszText == NULL)
     600    {
     601        item->textWidth = 0;
     602        return;
     603    }
     604
     605    hdc = hDC ? hDC:GetDC(infoPtr->hwnd);
     606    if (!hDC || !useFont) hOldFont = SelectObject(hdc, TREEVIEW_FontForItem(infoPtr, item));
     607
     608    SetRectEmpty(&rect);
     609    DrawTextW (hdc,item->pszText,lstrlenW(item->pszText),&rect,DT_LEFT | DT_VCENTER | DT_SINGLELINE | DT_CALCRECT | DT_NOPREFIX);
     610    item->textWidth = rect.right-rect.left;
     611    item->textHeight = rect.bottom-rect.top;
     612    item->rect.right = item->textOffset+item->textWidth+4;
     613
     614    if (!hDC || !useFont) SelectObject(hdc, hOldFont);
     615    if (!hDC) ReleaseDC(infoPtr->hwnd,hdc);
     616}
     617
     618static INT CALLBACK
     619TREEVIEW_ResetItemOrderVisible(LPVOID pItem, DWORD unused)
     620{
     621    TREEVIEW_ITEM *item = (TREEVIEW_ITEM *)pItem;
     622    (void)unused;
     623
     624    item->displayOrder = -1;
     625    item->displayed = FALSE;
     626    item->inclient = FALSE;
     627
     628    return 1;
     629}
     630
     631static VOID TREEVIEW_CalcItem(TREEVIEW_INFO *infoPtr,TREEVIEW_ITEM *item,HDC hdc)
     632{
     633  TREEVIEW_UpdateDispInfo(infoPtr,item,TVIF_TEXT);
     634  TREEVIEW_ComputeTextWidth(infoPtr,item,hdc,FALSE);
    1217635  item->calculated = TRUE;
    1218 
    1219   r      = item->rect;               /* this item rectangle */
    1220   center = (r.top+r.bottom)/2;           /* this item vertical center */
    1221   xpos   = r.left + TREEVIEW_LEFT_MARGIN;/* horizontal starting point */
    1222 
    1223   if (item->iLevel != 0)/*  update position only for non root node */
    1224     xpos += 5*item->iLevel;
    1225 
    1226   if ((dwStyle & TVS_HASBUTTONS) && (dwStyle & TVS_HASLINES))
    1227   {
    1228     if (TREEVIEW_HasChildren(hwnd,item))
    1229     {
    1230       /* Setup expand box coordinate to facilitate the LMBClick handling */
    1231       item->expandBox.left   = xpos-4;
    1232       item->expandBox.top    = center-4;
    1233       item->expandBox.right  = xpos+5;
    1234       item->expandBox.bottom = center+5;
    1235     } else SetRectEmpty(&item->expandBox);
    1236   } else SetRectEmpty(&item->expandBox);
    1237 
    1238   xpos += 13; /* update position */
    1239 
    1240   if ((item->mask & (TVIF_IMAGE | TVIF_SELECTEDIMAGE)) || (dwStyle & TVS_CHECKBOXES))
    1241   {
    1242     INT        imageIndex,cx,cy;
    1243     HIMAGELIST *himlp = NULL;
    1244 
    1245     /* State images are displayed to the left of the Normal image
    1246      * image number is in state; zero should be `display no image'.
    1247      * FIXME: that last sentence looks like it needs some checking.
    1248      */
    1249     if (infoPtr->himlState)
    1250       himlp = &infoPtr->himlState;
    1251     imageIndex = item->state >> 12;
    1252 
    1253     if (himlp && imageIndex)
    1254     {
    1255       if (!hdc)
    1256       {
    1257         ownDC = TRUE;
    1258         hdc = GetDC(hwnd);
    1259       }
    1260 
    1261       imageIndex--;       /* see FIXME */
    1262       ImageList_GetIconSize(*himlp,&cx,&cy);
    1263       item->statebitmap.left = xpos-2;
    1264       item->statebitmap.right = xpos-2+cx;
    1265       item->statebitmap.top = r.top+1;
    1266       item->statebitmap.bottom = r.top+1+cy;
    1267       xpos += cx;
    1268     } else SetRectEmpty(&item->statebitmap);
    1269 
    1270     /* Now, draw the normal image; can be either selected or
    1271      * non-selected image.
    1272      */
    1273 
    1274     himlp = NULL;
    1275     if (infoPtr->himlNormal)
    1276       himlp = &infoPtr->himlNormal; /* get the image list */
    1277 
    1278     if (himlp)
    1279     {
    1280       int ovlIdx = 0;
    1281 
    1282       if(item->stateMask & TVIS_OVERLAYMASK)
    1283         ovlIdx = item->state & TVIS_OVERLAYMASK;
    1284 
    1285       if (!hdc)
    1286       {
    1287         ownDC = TRUE;
    1288         hdc = GetDC(hwnd);
    1289       }
    1290 
    1291       ImageList_GetIconSize(*himlp,&cx,&cy);
    1292       item->bitmap.left = xpos-2;
    1293       item->bitmap.right = xpos-2+cx;
    1294       item->bitmap.top = r.top+1;
    1295       item->bitmap.bottom = r.top+1+cy;
    1296       xpos += cx;
    1297     } else SetRectEmpty(&item->bitmap);
    1298   } else
    1299   {
    1300     SetRectEmpty(&item->statebitmap);
    1301     SetRectEmpty(&item->bitmap);
    1302   }
    1303 
    1304   r.left = xpos;
    1305   if ((item->mask & TVIF_TEXT) && item->pszText)
    1306   {
    1307     UINT  uTextJustify = DT_LEFT;
    1308     HFONT hOldFont;
    1309     WCHAR* text;
    1310     BOOL mustFree = FALSE;
    1311 
    1312     r.left += 3;
    1313     r.right -= 3;
    1314 
    1315     item->text.left  = r.left;
    1316     item->text.right = r.right;
    1317     item->text.top   = r.top;
    1318     item->text.bottom= r.bottom;
    1319 
    1320     if (item->pszText == LPSTR_TEXTCALLBACKW)
    1321       text = TREEVIEW_CallbackText(hwnd,item,&mustFree);
    1322     else
    1323       text = item->pszText;
    1324 
    1325     if (!hdc)
    1326     {
    1327       ownDC = TRUE;
    1328       hdc = GetDC(hwnd);
    1329     }
    1330 
    1331     if (item->state & TVIS_BOLD)
    1332       hOldFont = SelectObject (hdc, infoPtr->hBoldFont);
    1333     else
    1334       hOldFont = SelectObject (hdc, infoPtr->hFont);
    1335 
    1336     /* Obtain the text coordinate */
    1337     DrawTextW (hdc,text,lstrlenW(text),&item->text,uTextJustify | DT_VCENTER | DT_SINGLELINE | DT_CALCRECT | DT_NOPREFIX);
    1338     if (mustFree) COMCTL32_Free(text);
    1339 
    1340     SelectObject(hdc,hOldFont);
    1341 
    1342     /* We need to reset it to items height */
    1343     item->text.top = r.top;
    1344     item->text.bottom = r.bottom;
    1345     item->text.right += 4; /* This is extra for focus rectangle */
    1346 
    1347     xpos = item->text.right;
    1348   } else SetRectEmpty(&item->text);
    1349 
    1350   item->rect.right = xpos;
    1351 
    1352   if (ownDC) ReleaseDC(hwnd,hdc);
    1353 }
    1354 
    1355 //HDC parameter is optional
    1356 
    1357 static BOOL TREEVIEW_CalcItems(HWND hwnd,HDC hdc,TREEVIEW_INFO *infoPtr)
     636}
     637
     638static BOOL TREEVIEW_CalcItems(TREEVIEW_INFO *infoPtr)
    1358639{
    1359640  TREEVIEW_ITEM *item;
    1360   INT iItem, indent,x,y,height,itemHeight;
    1361   TEXTMETRICA tm;
    1362   RECT rect,view;
    1363   BOOL ownDC = FALSE,changedLeftTop = FALSE;
    1364   DWORD dwStyle = GetWindowLongA(hwnd,GWL_STYLE);
     641  int order = 0;
     642  BOOL lar = ((infoPtr->dwStyle & (TVS_LINESATROOT|TVS_HASLINES|TVS_HASBUTTONS)) > TVS_LINESATROOT);
     643  HDC hdc = 0;
    1365644  INT maxXScroll,maxYScroll;
    1366645  INT xDiff,yDiff;
    1367   HFONT oldFont;
    1368 
    1369   GetClientRect(hwnd,&rect);
    1370   infoPtr->uVisibleHeight = rect.bottom-rect.top;
    1371   infoPtr->uVisibleWidth = rect.right-rect.left;
    1372 
    1373   if (infoPtr->uInternalStatus & TV_CALCALL)
    1374   {
    1375     TREEVIEW_HideInfoTip(hwnd,infoPtr);
    1376     itemHeight = 0;
    1377     ImageList_GetIconSize(infoPtr->himlNormal,&x,&itemHeight);
    1378     if (infoPtr->uItemHeight != -1) itemHeight = MAX(infoPtr->uItemHeight,itemHeight);
    1379 
    1380     if (!hdc)
    1381     {
    1382       ownDC = TRUE;
    1383       hdc = GetDC(hwnd);
    1384     }
    1385 
    1386     oldFont = SelectObject(hdc,infoPtr->hFont);
    1387     GetTextMetricsA(hdc,&tm);
    1388     itemHeight = MAX(tm.tmHeight+tm.tmExternalLeading,itemHeight);
    1389     SelectObject(hdc,infoPtr->hBoldFont);
    1390     GetTextMetricsA(hdc,&tm);
    1391     itemHeight = MAX(tm.tmHeight+tm.tmExternalLeading,itemHeight);
    1392     SelectObject(hdc,oldFont);
    1393     if (!(dwStyle & TVS_NONEVENHEIGHT))
    1394       if (itemHeight & 0x1) itemHeight++; //important for PS_DOT pen!
    1395     infoPtr->uRealItemHeight = itemHeight;
    1396     infoPtr->uVScrollStep = itemHeight+ITEM_VSPACE;
    1397   } else
    1398   {
    1399     itemHeight = infoPtr->uRealItemHeight;
    1400   }
    1401   infoPtr->uTotalWidth = 0;
    1402 
    1403   view = rect;
    1404   OffsetRect(&view,infoPtr->lefttop.x,infoPtr->lefttop.y);
    1405 
    1406   iItem = (INT)infoPtr->TopRootItem;
    1407   infoPtr->firstVisible = 0;
    1408   item = NULL;
    1409   indent = x = y = 0;
    1410 
    1411   while (iItem)
    1412   {
    1413     item = &infoPtr->items[iItem];
    1414     item->iLevel = indent;
    1415 
    1416     height = itemHeight*item->iIntegral+ITEM_VSPACE;
    1417 
    1418     //calculate size and fill rects
     646  BOOL changedLeftTop = FALSE;
     647  RECT rc;
     648
     649  TREEVIEW_HideInfoTip(infoPtr);
     650
     651  // 1) reset
     652  infoPtr->treeWidth = 0;
     653  infoPtr->treeHeight = 0;
     654  GetClientRect(infoPtr->hwnd,&rc);
     655  infoPtr->clientWidth = rc.right;
     656  infoPtr->clientHeight = rc.bottom;
     657  DPA_EnumCallback(infoPtr->items,TREEVIEW_ResetItemOrderVisible,(LPARAM)NULL);
     658
     659  // 2) get order and tree height
     660  infoPtr->firstVisible = NULL;
     661  for (item = infoPtr->root->firstChild;item != NULL;item = TREEVIEW_GetNextListItem(infoPtr, item))
     662  {
     663    INT itemW;
     664
     665    item->displayOrder = order;
     666
    1419667    if ((infoPtr->uInternalStatus & TV_CALCALL) || !item->calculated)
    1420668    {
    1421       item->rect.top    = y-view.top;
    1422       item->rect.bottom = item->rect.top+height;
    1423       item->rect.left   = x-view.left;
    1424       item->rect.right  = rect.right; //dummy
    1425       if (!hdc)
    1426       {
    1427         ownDC = TRUE;
    1428         hdc = GetDC(hwnd);
    1429       }
    1430       TREEVIEW_CalcItem(hwnd,hdc,dwStyle,infoPtr,item);
    1431     } else
    1432     {
    1433       INT xOffset,yOffset;
    1434 
    1435       xOffset = (x-view.left)-item->rect.left;
    1436       yOffset = (y-view.top)-item->rect.top;
    1437       OffsetRect(&item->rect,xOffset,yOffset);
    1438       OffsetRect(&item->text,xOffset,yOffset);
    1439       OffsetRect(&item->expandBox,xOffset,yOffset);
    1440       OffsetRect(&item->bitmap,xOffset,yOffset);
    1441       OffsetRect(&item->statebitmap,xOffset,yOffset);
    1442     }
    1443     infoPtr->uTotalWidth = MAX(infoPtr->uTotalWidth,item->rect.right+infoPtr->lefttop.x);
    1444 
    1445     if (((y >= view.top) && (y < view.bottom)) || ((y+height > view.top) && (y+height <= view.bottom)))
    1446     {
    1447       item->visible = TRUE;
    1448       if (!infoPtr->firstVisible)
    1449         infoPtr->firstVisible = item->hItem;
    1450     } else item->visible = FALSE;
    1451 
    1452     /* look up next item */
    1453 
    1454     if ((item->firstChild) && (item->state & TVIS_EXPANDED))
    1455     {
    1456       iItem = (INT)item->firstChild;
    1457       indent++;
    1458       x += infoPtr->uIndent;
    1459     } else
    1460     {
    1461       iItem = (INT)item->sibling;
    1462       while ((!iItem) && (indent > 0))
    1463       {
    1464         indent--;
    1465         x -= infoPtr->uIndent;
    1466         item = &infoPtr->items[(INT)item->parent];
    1467         iItem = (INT)item->sibling;
    1468       }
    1469     }
    1470     y += height;
    1471   } /* while */
    1472 
    1473   if (ownDC) ReleaseDC(hwnd,hdc);
    1474 
     669      if (!hdc) hdc = GetDC(infoPtr->hwnd);
     670      TREEVIEW_CalcItem(infoPtr,item,hdc);
     671    }
     672
     673    //set internal metrics
     674    item->linesOffset = infoPtr->uIndent * (item->iLevel + lar - 1) - infoPtr->lefttop.x;
     675    item->stateOffset = item->linesOffset + infoPtr->uIndent;
     676    item->imageOffset = item->stateOffset + (STATEIMAGEINDEX(item->state) ? infoPtr->stateImageWidth : 0);
     677    item->textOffset  = item->imageOffset + infoPtr->normalImageWidth;
     678
     679    //set item rect
     680    item->rect.top = infoPtr->uItemHeight*item->displayOrder-infoPtr->lefttop.y;
     681    item->rect.bottom = item->rect.top+infoPtr->uItemHeight*item->iIntegral;
     682    item->rect.left = item->stateOffset;
     683    item->rect.right = item->textOffset+item->textWidth+4;
     684    item->displayed = TRUE;
     685    item->inclient = (((item->rect.top >= 0) || (item->rect.bottom >= 0)) && (item->rect.top < infoPtr->clientHeight));
     686    if (!infoPtr->firstVisible && item->inclient) infoPtr->firstVisible = item;
     687
     688    itemW = item->textOffset+item->textWidth+4+infoPtr->lefttop.x;
     689    if (itemW > infoPtr->treeWidth)
     690      infoPtr->treeWidth = itemW;
     691
     692    infoPtr->treeHeight += item->rect.bottom-item->rect.top;
     693
     694    order += item->iIntegral;
     695  }
     696  infoPtr->maxDisplayOrder = order;
     697
     698  if (hdc) ReleaseDC(infoPtr->hwnd,hdc);
    1475699  infoPtr->uInternalStatus &= ~TV_CALCALL;
    1476   infoPtr->uTotalHeight = y;
    1477 
    1478   //check cx and cy
     700
     701  // 3) check lefttop
    1479702  yDiff = xDiff = 0;
    1480703
    1481   maxYScroll = infoPtr->uTotalHeight-infoPtr->uVisibleHeight;
     704  maxYScroll = infoPtr->treeHeight-infoPtr->clientHeight;
    1482705  if (maxYScroll < 0) maxYScroll = 0;
    1483706  if (infoPtr->lefttop.y > maxYScroll)
    1484707  {
    1485     INT mod = maxYScroll % infoPtr->uVScrollStep;
    1486 
    1487     if (mod > 0) maxYScroll += infoPtr->uVScrollStep-mod;
     708    INT mod = maxYScroll % infoPtr->uItemHeight;
     709
     710    if (mod > 0) maxYScroll += infoPtr->uItemHeight-mod;
    1488711    yDiff = infoPtr->lefttop.y-maxYScroll;
    1489712    infoPtr->lefttop.y = maxYScroll;
    1490713  }
    1491714
    1492   maxXScroll = infoPtr->uTotalWidth-infoPtr->uVisibleWidth;
     715  maxXScroll = infoPtr->treeWidth-infoPtr->clientWidth;
    1493716  if (maxXScroll < 0) maxXScroll = 0;
    1494717  if (infoPtr->lefttop.x > maxXScroll)
     
    1501724  if (changedLeftTop)
    1502725  {
    1503     iItem = (INT)infoPtr->TopRootItem;
    1504     item = NULL;
    1505     indent = 0;
    1506     infoPtr->firstVisible = 0;
    1507 
    1508     while (iItem)
    1509     {
    1510       item = &infoPtr->items[iItem];
    1511       OffsetRect(&item->rect,xDiff,yDiff);
    1512       OffsetRect(&item->text,xDiff,yDiff);
    1513       OffsetRect(&item->expandBox,xDiff,yDiff);
    1514       OffsetRect(&item->bitmap,xDiff,yDiff);
    1515       OffsetRect(&item->statebitmap,xDiff,yDiff);
    1516 
    1517       y = view.top+item->rect.top;
    1518       if (((y >= view.top) && (y < view.bottom)) || ((y+height > view.top) && (y+height <= view.bottom)))
    1519       {
    1520         item->visible = TRUE;
    1521         if (!infoPtr->firstVisible)
    1522           infoPtr->firstVisible = item->hItem;
    1523       } else item->visible = FALSE;
    1524 
    1525       if ((item->firstChild) && (item->state & TVIS_EXPANDED))
    1526       {
    1527         iItem = (INT)item->firstChild;
    1528         indent++;
    1529       } else
    1530       {
    1531         iItem = (INT)item->sibling;
    1532         while ((!iItem) && (indent > 0))
    1533         {
    1534           indent--;
    1535           item = &infoPtr->items[(INT)item->parent];
    1536           iItem = (INT)item->sibling;
    1537         }
    1538       }
    1539     } /* while */
    1540   }
    1541 
    1542   if (!(dwStyle & TVS_NOSCROLL) && (infoPtr->uVisibleHeight > 0) && (infoPtr->uVisibleWidth > 0))
    1543   {
    1544     if (infoPtr->uTotalHeight > infoPtr->uVisibleHeight)
     726    //update visible flag and position
     727    infoPtr->firstVisible = NULL;
     728    for (item = infoPtr->root->firstChild;item != NULL;item = TREEVIEW_GetNextListItem(infoPtr, item))
     729    {
     730      item->rect.top += yDiff;
     731      item->rect.bottom += yDiff;
     732      item->rect.left += xDiff;
     733      item->rect.right += xDiff;
     734      item->inclient = (((item->rect.top >= 0) || (item->rect.bottom >= 0)) && (item->rect.top < infoPtr->clientHeight));
     735      if (!infoPtr->firstVisible && item->inclient) infoPtr->firstVisible = item;
     736      item->linesOffset += xDiff;
     737      item->stateOffset += xDiff;
     738      item->imageOffset += xDiff;
     739      item->textOffset  += xDiff;
     740    }
     741  }
     742
     743  // 4) set scrollbars
     744  if (!(infoPtr->dwStyle & TVS_NOSCROLL) && (infoPtr->clientHeight > 0) && (infoPtr->clientWidth > 0))
     745  {
     746    if (infoPtr->treeHeight > infoPtr->clientHeight)
    1545747    {
    1546748      SCROLLINFO info;
    1547       INT visH = ((INT)(infoPtr->uVisibleHeight/infoPtr->uVScrollStep))*infoPtr->uVScrollStep;
     749      INT visH = ((INT)(infoPtr->clientHeight/infoPtr->uItemHeight))*infoPtr->uItemHeight;
    1548750
    1549751      info.cbSize = sizeof(info);
    1550752      info.nMin   = 0;
    1551       info.nMax   = infoPtr->uTotalHeight-1;
     753      info.nMax   = infoPtr->treeHeight-1;
    1552754      info.nPos   = infoPtr->lefttop.y;
    1553755      info.nPage  = visH;
    1554756      info.fMask = SIF_RANGE | SIF_POS | SIF_PAGE;
    1555757      infoPtr->uInternalStatus |= TV_VSCROLL;
    1556       SetScrollInfo(hwnd,SB_VERT,&info,TRUE);
     758      SetScrollInfo(infoPtr->hwnd,SB_VERT,&info,TRUE);
    1557759    } else
    1558760    {
    1559761      if (infoPtr->uInternalStatus & TV_VSCROLL)
    1560         ShowScrollBar(hwnd,SB_VERT,FALSE);
     762        ShowScrollBar(infoPtr->hwnd,SB_VERT,FALSE);
    1561763      infoPtr->uInternalStatus &= ~TV_VSCROLL;
    1562764    }
    1563     if (!(dwStyle & TVS_NOHSCROLL) && (infoPtr->uTotalWidth > infoPtr->uVisibleWidth))
     765    if (!(infoPtr->dwStyle & TVS_NOHSCROLL) && (infoPtr->treeWidth > infoPtr->clientWidth))
    1564766    {
    1565767      SCROLLINFO info;
     
    1567769      info.cbSize = sizeof(info);
    1568770      info.nMin   = 0;
    1569       info.nMax   = infoPtr->uTotalWidth-1;
     771      info.nMax   = infoPtr->treeWidth-1;
    1570772      info.nPos   = infoPtr->lefttop.x;
    1571       info.nPage  = MAX(infoPtr->uVisibleWidth,1);
     773      info.nPage  = MAX(infoPtr->clientWidth,1);
    1572774      info.fMask  = SIF_RANGE | SIF_POS | SIF_PAGE;
    1573775      infoPtr->uInternalStatus |= TV_HSCROLL;
    1574       SetScrollInfo(hwnd,SB_HORZ,&info,TRUE);
     776      SetScrollInfo(infoPtr->hwnd,SB_HORZ,&info,TRUE);
    1575777    } else
    1576778    {
    1577779      if (infoPtr->uInternalStatus & TV_HSCROLL)
    1578         ShowScrollBar(hwnd,SB_HORZ,FALSE);
     780        ShowScrollBar(infoPtr->hwnd,SB_HORZ,FALSE);
    1579781      infoPtr->uInternalStatus &= ~TV_HSCROLL;
    1580782    }
     
    1582784  {
    1583785    if (infoPtr->uInternalStatus & (TV_VSCROLL | TV_HSCROLL))
    1584       ShowScrollBar(hwnd,SB_BOTH,FALSE);
     786      ShowScrollBar(infoPtr->hwnd,SB_BOTH,FALSE);
    1585787    infoPtr->uInternalStatus &= ~(TV_VSCROLL | TV_HSCROLL);
    1586788  }
    1587789
     790  infoPtr->dwStyle = GetWindowLongA(infoPtr->hwnd,GWL_STYLE);
     791
    1588792  return changedLeftTop;
    1589793}
    1590794
     795static VOID TREEVIEW_MoveItems(TREEVIEW_INFO *infoPtr,INT xScroll,INT yScroll)
     796{
     797  TREEVIEW_ITEM *item;
     798
     799  infoPtr->firstVisible = NULL;
     800  for (item = infoPtr->root->firstChild;item;item = TREEVIEW_GetNextListItem(infoPtr,item))
     801  {
     802    item->rect.top += yScroll;
     803    item->rect.bottom += yScroll;
     804    item->rect.left += xScroll;
     805    item->rect.right += xScroll;
     806    item->inclient = (((item->rect.top >= 0) || (item->rect.bottom >= 0)) && (item->rect.top < infoPtr->clientHeight));
     807    if (!infoPtr->firstVisible && item->inclient) infoPtr->firstVisible = item;
     808
     809    //set internal metrics
     810    item->linesOffset += xScroll;
     811    item->stateOffset += xScroll;
     812    item->imageOffset += xScroll;
     813    item->textOffset  += xScroll;
     814  }
     815}
     816
     817/* Item Allocation **********************************************************/
     818
     819static TREEVIEW_ITEM *
     820TREEVIEW_AllocateItem(TREEVIEW_INFO *infoPtr)
     821{
     822    TREEVIEW_ITEM *newItem = (TREEVIEW_ITEM*)COMCTL32_Alloc(sizeof(TREEVIEW_ITEM));
     823
     824    if (!newItem)
     825        return NULL;
     826
     827    ZeroMemory(newItem,sizeof(TREEVIEW_ITEM));
     828    if (DPA_InsertPtr(infoPtr->items, INT_MAX, newItem) == -1)
     829    {
     830        COMCTL32_Free(newItem);
     831        return NULL;
     832    }
     833
     834    return newItem;
     835}
     836
     837/* Exact opposite of TREEVIEW_AllocateItem. In particular, it does not
     838 * free item->pszText. */
    1591839static void
    1592 TREEVIEW_Draw(HWND hwnd,HDC hdc,RECT *updateRect)
    1593 {
    1594     TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
    1595     DWORD dwStyle = GetWindowLongA(hwnd,GWL_STYLE);
    1596     HBRUSH hbrBk;
     840TREEVIEW_FreeItem(TREEVIEW_INFO *infoPtr, TREEVIEW_ITEM *item)
     841{
     842    DPA_DeletePtr(infoPtr->items, DPA_GetPtrIndex(infoPtr->items, item));
     843    COMCTL32_Free(item);
     844}
     845
     846
     847/* Item Insertion *******************************************************/
     848
     849/***************************************************************************
     850 * This method inserts newItem before sibling as a child of parent.
     851 * sibling can be NULL, but only if parent has no children.
     852 */
     853static void
     854TREEVIEW_InsertBefore(TREEVIEW_ITEM *newItem, TREEVIEW_ITEM *sibling,
     855                      TREEVIEW_ITEM *parent)
     856{
     857    assert(newItem != NULL);
     858    assert(parent != NULL);
     859
     860    if (sibling != NULL)
     861    {
     862        assert(sibling->parent == parent);
     863
     864        if (sibling->prevSibling != NULL)
     865            sibling->prevSibling->nextSibling = newItem;
     866
     867        newItem->prevSibling = sibling->prevSibling;
     868        sibling->prevSibling = newItem;
     869    }
     870
     871    newItem->nextSibling = sibling;
     872
     873    if (parent->firstChild == sibling)
     874    {
     875        newItem->nextSibling = parent->firstChild;
     876        parent->firstChild = newItem;
     877    }
     878
     879    if (parent->lastChild == NULL)
     880        parent->lastChild = newItem;
     881}
     882
     883/***************************************************************************
     884 * This method inserts newItem after sibling as a child of parent.
     885 * sibling can be NULL, but only if parent has no children.
     886 */
     887static void
     888TREEVIEW_InsertAfter(TREEVIEW_ITEM *newItem, TREEVIEW_ITEM *sibling,
     889                     TREEVIEW_ITEM *parent)
     890{
     891    assert(newItem != NULL);
     892    assert(parent != NULL);
     893
     894    if (sibling != NULL)
     895    {
     896        assert(sibling->parent == parent);
     897
     898        if (sibling->nextSibling != NULL)
     899            sibling->nextSibling->prevSibling = newItem;
     900
     901        newItem->nextSibling = sibling->nextSibling;
     902        sibling->nextSibling = newItem;
     903    }
     904
     905    newItem->prevSibling = sibling;
     906
     907    if (parent->lastChild == sibling)
     908    {
     909        newItem->prevSibling = parent->lastChild;
     910        parent->lastChild = newItem;
     911    }
     912
     913    if (parent->firstChild == NULL)
     914        parent->firstChild = newItem;
     915}
     916
     917static BOOL
     918TREEVIEW_DoSetItem(TREEVIEW_INFO *infoPtr,TREEVIEW_ITEM *wineItem,const TVITEMEXW *tvItem,BOOL unicode,DWORD *changed = NULL)
     919{
     920  UINT callbackClear = 0;
     921  UINT callbackSet = 0;
     922
     923  if (changed) *changed = 0;
     924
     925  /* Do this first in case it fails. */
     926
     927  if (tvItem->mask & TVIF_TEXT)
     928  {
     929    /*
     930     * Setup the item text stuff here since it's required by the Sort method
     931     * when the insertion are ordered
     932     */
     933    if (unicode)
     934    {
     935      if (tvItem->pszText != LPSTR_TEXTCALLBACKW)
     936      {
     937        INT len = lstrlenW(tvItem->pszText)+1;
     938
     939        callbackClear |= TVIF_TEXT;
     940
     941        if (changed && ((wineItem->callbackMask & LVIF_TEXT) || (lstrcmpW(wineItem->pszText,tvItem->pszText) != 0))) *changed |= TVIF_TEXT;
     942        if (len > wineItem->cchTextMax)
     943        {
     944          LPWSTR newText = (WCHAR*)COMCTL32_ReAlloc(wineItem->pszText,len*sizeof(WCHAR));
     945          if (newText == NULL) return FALSE;
     946
     947          wineItem->pszText = newText;
     948          wineItem->cchTextMax = len;
     949        }
     950        lstrcpyW (wineItem->pszText,tvItem->pszText);
     951      } else
     952      {
     953        callbackSet |= TVIF_TEXT;
     954
     955        wineItem->pszText = (WCHAR*)COMCTL32_ReAlloc(wineItem->pszText,TEXT_CALLBACK_SIZE*sizeof(WCHAR));
     956        wineItem->cchTextMax = TEXT_CALLBACK_SIZE;
     957        if (changed) *changed |= TVIF_TEXT;
     958      }
     959    } else
     960    {
     961      if ((LPSTR)tvItem->pszText != LPSTR_TEXTCALLBACKA)
     962      {
     963        INT len = lstrlenA((LPSTR)tvItem->pszText)+1;
     964
     965        callbackClear |= TVIF_TEXT;
     966
     967        if (changed && ((wineItem->callbackMask & LVIF_TEXT) || (lstrcmpAtoW((CHAR*)tvItem->pszText,wineItem->pszText) != 0))) *changed |= TVIF_TEXT;
     968        if (len > wineItem->cchTextMax)
     969        {
     970          LPWSTR newText = (WCHAR*)COMCTL32_ReAlloc(wineItem->pszText,len*sizeof(WCHAR));
     971          if (newText == NULL) return FALSE;
     972
     973          wineItem->pszText = newText;
     974          wineItem->cchTextMax = len;
     975        }
     976        lstrcpyAtoW (wineItem->pszText,(LPSTR)tvItem->pszText);
     977      } else
     978      {
     979        callbackSet |= TVIF_TEXT;
     980
     981        wineItem->pszText = (WCHAR*)COMCTL32_ReAlloc(wineItem->pszText,TEXT_CALLBACK_SIZE*sizeof(WCHAR));
     982        wineItem->cchTextMax = TEXT_CALLBACK_SIZE;
     983        if (changed) *changed |= TVIF_TEXT;
     984      }
     985    }
     986  }
     987
     988  if (tvItem->mask & TVIF_CHILDREN)
     989  {
     990    if (changed && (wineItem->cChildren != tvItem->cChildren)) *changed |= TVIF_CHILDREN;
     991    wineItem->cChildren = tvItem->cChildren;
     992
     993    if (wineItem->cChildren == I_CHILDRENCALLBACK)
     994      callbackSet |= TVIF_CHILDREN;
     995    else
     996      callbackClear |= TVIF_CHILDREN;
     997  }
     998
     999  if (tvItem->mask & TVIF_IMAGE)
     1000  {
     1001    if (changed && (wineItem->iImage != tvItem->iImage)) *changed |= TVIF_IMAGE;
     1002    wineItem->iImage = tvItem->iImage;
     1003
     1004    if (wineItem->iImage == I_IMAGECALLBACK)
     1005      callbackSet |= TVIF_IMAGE;
     1006    else
     1007      callbackClear |= TVIF_IMAGE;
     1008  }
     1009
     1010  if (tvItem->mask & TVIF_SELECTEDIMAGE)
     1011  {
     1012    if (changed && (wineItem->iSelectedImage != tvItem->iSelectedImage)) *changed |= TVIF_SELECTEDIMAGE;
     1013    wineItem->iSelectedImage = tvItem->iSelectedImage;
     1014
     1015    if (wineItem->iImage == I_IMAGECALLBACK)
     1016      callbackSet |= TVIF_SELECTEDIMAGE;
     1017    else
     1018      callbackClear |= TVIF_SELECTEDIMAGE;
     1019  }
     1020
     1021  if (tvItem->mask & TVIF_PARAM)
     1022  {
     1023    if (changed && (wineItem->lParam != tvItem->lParam)) *changed |= TVIF_PARAM;
     1024    wineItem->lParam = tvItem->lParam;
     1025  }
     1026
     1027  /* If the application sets TVIF_INTEGRAL without
     1028   * supplying a TVITEMEX structure, it's toast. */
     1029  if (tvItem->mask & TVIF_INTEGRAL)
     1030  {
     1031    if (changed && (wineItem->iIntegral != tvItem->iIntegral)) *changed |= TVIF_INTEGRAL;
     1032    wineItem->iIntegral = tvItem->iIntegral;
     1033  }
     1034
     1035  if (tvItem->mask & TVIF_STATE)
     1036  {
     1037    DWORD oldState = wineItem->state;
     1038
     1039    wineItem->state &= ~tvItem->stateMask;
     1040    wineItem->state |= (tvItem->state & tvItem->stateMask);
     1041    if (changed && (wineItem->state != oldState)) *changed |= TVIF_STATE;
     1042  }
     1043
     1044  wineItem->callbackMask |= callbackSet;
     1045  wineItem->callbackMask &= ~callbackClear;
     1046
     1047  return TRUE;
     1048}
     1049
     1050/* Note that the new item is pre-zeroed. */
     1051static LRESULT
     1052TREEVIEW_InsertItem(TREEVIEW_INFO *infoPtr,WPARAM wParam,LPARAM lParam,BOOL unicode)
     1053{
     1054  TVINSERTSTRUCTW *ptdi = (LPTVINSERTSTRUCTW)lParam;
     1055  TVITEMEXW *tvItem;
     1056  HTREEITEM insertAfter;
     1057  INT x;
     1058  TREEVIEW_ITEM *newItem, *parentItem;
     1059
     1060  if (!ptdi) return NULL;
     1061  tvItem = &ptdi->itemex;
     1062
     1063  if (infoPtr->hwndEdit) SetFocus(infoPtr->hwnd);
     1064
     1065  if ((ptdi->hParent == TVI_ROOT) || (ptdi->hParent == 0))
     1066  {
     1067    parentItem = infoPtr->root;
     1068  } else
     1069  {
     1070    parentItem = ptdi->hParent;
     1071
     1072    if (!TREEVIEW_ValidItem(infoPtr,parentItem))
     1073    {
     1074      //dprintf(("invalid parent %p\n",parentItem));
     1075
     1076      return (LRESULT)(HTREEITEM)NULL;
     1077    }
     1078  }
     1079
     1080  insertAfter = ptdi->hInsertAfter;
     1081
     1082  /* Validate this now for convenience. */
     1083  if ((insertAfter != TVI_FIRST) && (insertAfter != TVI_LAST) && (insertAfter != TVI_SORT))
     1084  {
     1085    if (!TREEVIEW_ValidItem(infoPtr,insertAfter))
     1086    {
     1087      //dprintf(("invalid insert after %p\n",insertAfter));
     1088      insertAfter = TVI_LAST;
     1089    } else if (parentItem != insertAfter->parent)
     1090    {
     1091      //dprintf(("invalid insert after parent %p %p %p",insertAfter,insertAfter->parent,parentItem));
     1092      //CB: Verified with AMD cpuid 2.04 (there are other strange things...)
     1093      insertAfter = parentItem->lastChild;
     1094    }
     1095  }
     1096
     1097  //TRACE("parent %p position %p: %s\n", parentItem, insertAfter,
     1098  //      (tvItem->mask & TVIF_TEXT)
     1099  //      ? ((tvItem->pszText == LPSTR_TEXTCALLBACKA) ? "<callback>"
     1100  //      : tvItem->pszText)
     1101  //      : "<no label>");
     1102
     1103  newItem = TREEVIEW_AllocateItem(infoPtr);
     1104  if (newItem == NULL)
     1105    return (LRESULT)(HTREEITEM)NULL;
     1106
     1107  newItem->parent = parentItem;
     1108  newItem->iIntegral = 1;
     1109
     1110  if (!TREEVIEW_DoSetItem(infoPtr,newItem,tvItem,unicode))
     1111    return (LRESULT)(HTREEITEM)NULL;
     1112
     1113  /* After this point, nothing can fail. (Except for TVI_SORT.) */
     1114
     1115  infoPtr->uNumItems++;
     1116
     1117  if (insertAfter == TVI_FIRST)
     1118  {
     1119    TREEVIEW_InsertBefore(newItem, parentItem->firstChild, parentItem);
     1120  } else if (insertAfter == TVI_LAST)
     1121  {
     1122    TREEVIEW_InsertAfter(newItem, parentItem->lastChild, parentItem);
     1123  } else if (insertAfter == TVI_SORT)
     1124  {
     1125    TREEVIEW_ITEM *aChild;
     1126    TREEVIEW_ITEM *previousChild = NULL;
     1127    BOOL bItemInserted = FALSE;
     1128
     1129    aChild = parentItem->firstChild;
     1130
     1131    TREEVIEW_UpdateDispInfo(infoPtr, newItem, TVIF_TEXT);
     1132
     1133    /* Iterate the parent children to see where we fit in */
     1134    while (aChild)
     1135    {
     1136      INT comp;
     1137
     1138      TREEVIEW_UpdateDispInfo(infoPtr,aChild,TVIF_TEXT);
     1139      comp = lstrcmpW(newItem->pszText,aChild->pszText);
     1140
     1141      if (comp < 0)     /* we are smaller than the current one */
     1142      {
     1143        TREEVIEW_InsertBefore(newItem, aChild, parentItem);
     1144        bItemInserted = TRUE;
     1145        break;
     1146      }
     1147      else if (comp > 0)        /* we are bigger than the current one */
     1148      {
     1149        previousChild = aChild;
     1150        aChild = (aChild->nextSibling == 0)     /* This will help us to exit   */
     1151                  ? NULL        /* if there is no more sibling */
     1152                  : aChild->nextSibling;
     1153
     1154        /* Look at the next item */
     1155        continue;
     1156      }
     1157      else if (comp == 0)
     1158      {
     1159        /*
     1160         * An item with this name is already existing, therefore,
     1161         * we add after the one we found
     1162         */
     1163        TREEVIEW_InsertAfter(newItem, aChild, parentItem);
     1164        bItemInserted = TRUE;
     1165        break;
     1166      }
     1167    }
     1168
     1169    /*
     1170     * we reach the end of the child list and the item as not
     1171     * yet been inserted, therefore, insert it after the last child.
     1172     */
     1173    if ((!bItemInserted) && (aChild == NULL))
     1174      TREEVIEW_InsertAfter(newItem, previousChild, parentItem);
     1175  } else
     1176  {
     1177    /* hInsertAfter names a specific item we want to insert after */
     1178    TREEVIEW_InsertAfter(newItem,insertAfter,parentItem);
     1179  }
     1180
     1181  //TRACE("new item %p; parent %p, mask %x\n", newItem,
     1182  // newItem->parent, tvItem->mask);
     1183
     1184  newItem->iLevel = newItem->parent->iLevel + 1;
     1185
     1186  if (newItem->parent->cChildren == 0)
     1187    newItem->parent->cChildren = 1;
     1188
     1189  if (infoPtr->dwStyle & TVS_CHECKBOXES)
     1190  {
     1191    if (STATEIMAGEINDEX(newItem->state) == 0)
     1192      newItem->state |= INDEXTOSTATEIMAGEMASK(1);
     1193  }
     1194
     1195  TREEVIEW_VerifyTree(infoPtr);
     1196
     1197  newItem->calculated = FALSE;
     1198  TREEVIEW_QueueRefresh(infoPtr);
     1199
     1200  return (LRESULT)newItem;
     1201}
     1202
     1203/* Item Deletion ************************************************************/
     1204static void
     1205TREEVIEW_RemoveItem(TREEVIEW_INFO *infoPtr, TREEVIEW_ITEM *wineItem);
     1206
     1207static void
     1208TREEVIEW_RemoveAllChildren(TREEVIEW_INFO *infoPtr, TREEVIEW_ITEM *parentItem)
     1209{
     1210    TREEVIEW_ITEM *kill = parentItem->firstChild;
     1211
     1212    while (kill != NULL)
     1213    {
     1214        TREEVIEW_ITEM *next = kill->nextSibling;
     1215
     1216        TREEVIEW_RemoveItem(infoPtr, kill);
     1217
     1218        kill = next;
     1219    }
     1220
     1221    assert(parentItem->cChildren <= 0); /* I_CHILDRENCALLBACK or 0 */
     1222    assert(parentItem->firstChild == NULL);
     1223    assert(parentItem->lastChild == NULL);
     1224}
     1225
     1226static void
     1227TREEVIEW_UnlinkItem(TREEVIEW_ITEM *item)
     1228{
     1229    TREEVIEW_ITEM *parentItem = item->parent;
     1230
     1231    assert(item != NULL);
     1232    assert(item->parent != NULL); /* i.e. it must not be the root */
     1233
     1234    if (parentItem->firstChild == item)
     1235        parentItem->firstChild = item->nextSibling;
     1236
     1237    if (parentItem->lastChild == item)
     1238        parentItem->lastChild = item->prevSibling;
     1239
     1240    if (parentItem->firstChild == NULL && parentItem->lastChild == NULL
     1241        && parentItem->cChildren > 0)
     1242        parentItem->cChildren = 0;
     1243
     1244    if (item->prevSibling)
     1245        item->prevSibling->nextSibling = item->nextSibling;
     1246
     1247    if (item->nextSibling)
     1248        item->nextSibling->prevSibling = item->prevSibling;
     1249}
     1250
     1251static void
     1252TREEVIEW_RemoveItem(TREEVIEW_INFO *infoPtr, TREEVIEW_ITEM *wineItem)
     1253{
     1254    TREEVIEW_ITEM *newFirstVisible;
     1255
     1256    //TRACE("%p, (%s)\n", wineItem, TREEVIEW_ItemName(wineItem));
     1257
     1258    TREEVIEW_SendTreeviewNotify(infoPtr,isUnicodeNotify(&infoPtr->header) ? TVN_DELETEITEMW:TVN_DELETEITEMA,TVIF_HANDLE | TVIF_PARAM,0,wineItem,0);
     1259
     1260    if (wineItem->firstChild)
     1261        TREEVIEW_RemoveAllChildren(infoPtr, wineItem);
     1262
     1263    /* Selection is handled in TREEVIEW_DeleteItem. */
     1264
     1265    if (infoPtr->insertMarkItem == wineItem)
     1266        infoPtr->insertMarkItem = NULL;
     1267
     1268    /* XXX focusItem, hotItem, dropItem */
     1269
     1270    TREEVIEW_UnlinkItem(wineItem);
     1271
     1272    infoPtr->uNumItems--;
     1273
     1274    if (wineItem->pszText != LPSTR_TEXTCALLBACKW)
     1275        COMCTL32_Free(wineItem->pszText);
     1276
     1277    TREEVIEW_FreeItem(infoPtr, wineItem);
     1278}
     1279
     1280
     1281/* Empty out the tree. */
     1282static void
     1283TREEVIEW_RemoveTree(TREEVIEW_INFO *infoPtr)
     1284{
     1285    TREEVIEW_RemoveAllChildren(infoPtr, infoPtr->root);
     1286
     1287    assert(infoPtr->uNumItems == 0);    /* root isn't counted in uNumItems */
     1288}
     1289
     1290static LRESULT
     1291TREEVIEW_DeleteItem(TREEVIEW_INFO *infoPtr, WPARAM wParam, LPARAM lParam)
     1292{
     1293    TREEVIEW_ITEM *oldSelection = infoPtr->selectedItem;
     1294    TREEVIEW_ITEM *newSelection = NULL;
     1295
     1296    if (lParam == (LPARAM)TVI_ROOT)
     1297    {
     1298        newSelection = NULL;
     1299        TREEVIEW_RemoveTree(infoPtr);
     1300    }
     1301    else
     1302    {
     1303        TREEVIEW_ITEM *wineItem = (TREEVIEW_ITEM *)lParam;
     1304
     1305        if (!TREEVIEW_ValidItem(infoPtr, wineItem))
     1306            return FALSE;
     1307
     1308        //TRACE("%p (%s)\n", wineItem, TREEVIEW_ItemName(wineItem));
     1309
     1310        if (infoPtr->selectedItem != NULL
     1311            && (wineItem == infoPtr->selectedItem
     1312                || TREEVIEW_IsChildOf(wineItem, infoPtr->selectedItem)))
     1313        {
     1314            if (wineItem->nextSibling)
     1315                newSelection = wineItem->nextSibling;
     1316            else if (wineItem->parent != infoPtr->root)
     1317                newSelection = wineItem->parent;
     1318        }
     1319
     1320        TREEVIEW_RemoveItem(infoPtr, wineItem);
     1321    }
     1322
     1323    /* Don't change if somebody else already has. */
     1324    if (oldSelection == infoPtr->selectedItem
     1325        && TREEVIEW_ValidItem(infoPtr, newSelection))
     1326    {
     1327        TREEVIEW_DoSelectItem(infoPtr, TVGN_CARET, newSelection, TVC_UNKNOWN);
     1328    }
     1329
     1330    TREEVIEW_VerifyTree(infoPtr);
     1331
     1332    TREEVIEW_QueueRefresh(infoPtr);
     1333
     1334    return TRUE;
     1335}
     1336
     1337/* Get/Set Messages *********************************************************/
     1338
     1339static LRESULT
     1340TREEVIEW_GetIndent(TREEVIEW_INFO *infoPtr)
     1341{
     1342  return infoPtr->uIndent;
     1343}
     1344
     1345static LRESULT
     1346TREEVIEW_SetIndent(TREEVIEW_INFO *infoPtr, WPARAM wParam)
     1347{
     1348  INT newIndent;
     1349
     1350  newIndent = (INT)wParam;
     1351  if (newIndent < MINIMUM_INDENT)
     1352    newIndent = MINIMUM_INDENT;
     1353
     1354  if (infoPtr->uIndent != newIndent)
     1355  {
     1356    infoPtr->uIndent = newIndent;
     1357
     1358    infoPtr->uInternalStatus |= TV_CALCALL;
     1359    TREEVIEW_QueueRefresh(infoPtr);
     1360  }
     1361
     1362  return 0;
     1363}
     1364
     1365static LRESULT
     1366TREEVIEW_GetToolTips(TREEVIEW_INFO *infoPtr)
     1367{
     1368    return infoPtr->hwndToolTip;
     1369}
     1370
     1371static LRESULT
     1372TREEVIEW_SetToolTips(TREEVIEW_INFO *infoPtr, WPARAM wParam)
     1373{
     1374    HWND prevToolTip;
     1375
     1376    prevToolTip = infoPtr->hwndToolTip;
     1377    infoPtr->hwndToolTip = (HWND)wParam;
     1378
     1379    return prevToolTip;
     1380}
     1381
     1382static LRESULT
     1383TREEVIEW_GetScrollTime(TREEVIEW_INFO *infoPtr)
     1384{
     1385    return infoPtr->uScrollTime;
     1386}
     1387
     1388static LRESULT
     1389TREEVIEW_SetScrollTime(TREEVIEW_INFO *infoPtr, UINT uScrollTime)
     1390{
     1391    UINT uOldScrollTime = infoPtr->uScrollTime;
     1392
     1393    infoPtr->uScrollTime = min(uScrollTime, 100);
     1394
     1395    return uOldScrollTime;
     1396}
     1397
     1398
     1399static LRESULT
     1400TREEVIEW_GetImageList(TREEVIEW_INFO *infoPtr, WPARAM wParam, LPARAM lParam)
     1401{
     1402    switch (wParam)
     1403    {
     1404    case (WPARAM)TVSIL_NORMAL:
     1405        return (LRESULT)infoPtr->himlNormal;
     1406
     1407    case (WPARAM)TVSIL_STATE:
     1408        return (LRESULT)infoPtr->himlState;
     1409
     1410    default:
     1411        return 0;
     1412    }
     1413}
     1414
     1415static LRESULT
     1416TREEVIEW_SetImageList(TREEVIEW_INFO *infoPtr, WPARAM wParam, LPARAM lParam)
     1417{
     1418    HIMAGELIST himlNew = (HIMAGELIST)lParam;
     1419    HIMAGELIST himlOld = 0;
     1420
     1421    switch (wParam)
     1422    {
     1423      case (WPARAM)TVSIL_NORMAL:
     1424        himlOld = infoPtr->himlNormal;
     1425        infoPtr->himlNormal = himlNew;
     1426
     1427        if (himlNew != NULL)
     1428            ImageList_GetIconSize(himlNew, &infoPtr->normalImageWidth,
     1429                                  &infoPtr->normalImageHeight);
     1430        else
     1431        {
     1432            infoPtr->normalImageWidth = 0;
     1433            infoPtr->normalImageHeight = 0;
     1434        }
     1435
     1436        break;
     1437
     1438      case (WPARAM)TVSIL_STATE:
     1439        himlOld = infoPtr->himlState;
     1440        infoPtr->himlState = himlNew;
     1441
     1442        if (himlNew != NULL)
     1443            ImageList_GetIconSize(himlNew, &infoPtr->stateImageWidth,
     1444                                  &infoPtr->stateImageHeight);
     1445        else
     1446        {
     1447            infoPtr->stateImageWidth = 0;
     1448            infoPtr->stateImageHeight = 0;
     1449        }
     1450
     1451        break;
     1452    }
     1453
     1454    infoPtr->uInternalStatus |= TV_CALCALL;
     1455    TREEVIEW_QueueRefresh(infoPtr);
     1456
     1457    return (LRESULT)himlOld;
     1458}
     1459
     1460/* Compute the natural height (based on the font size) for items. */
     1461static UINT
     1462TREEVIEW_NaturalHeight(TREEVIEW_INFO *infoPtr)
     1463{
     1464  TEXTMETRICA tm;
     1465  HDC hdc = GetDC(infoPtr->hwnd);
     1466  HFONT hOldFont = SelectObject(hdc, infoPtr->hFont);
     1467  INT height;
     1468
     1469  GetTextMetricsA(hdc, &tm);
     1470
     1471  SelectObject(hdc, hOldFont);
     1472  ReleaseDC(infoPtr->hwnd, hdc);
     1473
     1474  /* The 16 is a hack because our fonts are tiny. */
     1475  height = MAX(16, tm.tmHeight + tm.tmExternalLeading);
     1476  if (!(infoPtr->dwStyle & TVS_NONEVENHEIGHT))
     1477    if (height & 0x1) height++; //important for PS_DOT pen!
     1478
     1479  return height;
     1480}
     1481
     1482static LRESULT
     1483TREEVIEW_SetItemHeight(TREEVIEW_INFO *infoPtr, WPARAM wParam)
     1484{
     1485    INT prevHeight = infoPtr->uItemHeight;
     1486
     1487    if (wParam == (WPARAM)(SHORT)-1)
     1488    {
     1489        infoPtr->uItemHeight = TREEVIEW_NaturalHeight(infoPtr);
     1490        infoPtr->bHeightSet = FALSE;
     1491    }
     1492    else
     1493    {
     1494        infoPtr->uItemHeight = wParam;
     1495        infoPtr->bHeightSet = TRUE;
     1496    }
     1497
     1498    if (!(infoPtr->dwStyle & TVS_NONEVENHEIGHT))
     1499      if (infoPtr->uItemHeight & 0x1) infoPtr->uItemHeight++;
     1500
     1501    if (infoPtr->uItemHeight != prevHeight)
     1502    {
     1503        infoPtr->uInternalStatus |= TV_CALCALL;
     1504        TREEVIEW_QueueRefresh(infoPtr);
     1505    }
     1506
     1507    return prevHeight;
     1508}
     1509
     1510static LRESULT
     1511TREEVIEW_GetItemHeight(TREEVIEW_INFO *infoPtr)
     1512{
     1513    return infoPtr->uItemHeight;
     1514}
     1515
     1516
     1517static LRESULT
     1518TREEVIEW_GetFont(TREEVIEW_INFO *infoPtr, WPARAM wParam, LPARAM lParam)
     1519{
     1520    return infoPtr->hFont;
     1521}
     1522
     1523static LRESULT
     1524TREEVIEW_SetFont(TREEVIEW_INFO *infoPtr, WPARAM wParam, LPARAM lParam)
     1525{
     1526  infoPtr->hFont = wParam ? (HFONT)wParam : GetStockObject(SYSTEM_FONT);
     1527
     1528  DeleteObject(infoPtr->hBoldFont);
     1529  infoPtr->hBoldFont = TREEVIEW_CreateBoldFont(infoPtr->hFont);
     1530
     1531  if (!infoPtr->bHeightSet)
     1532      infoPtr->uItemHeight = TREEVIEW_NaturalHeight(infoPtr);
     1533
     1534  SendMessageA(infoPtr->hwndToolTip,WM_SETFONT,infoPtr->hFont,1);
     1535  infoPtr->uInternalStatus |= TV_CALCALL;
     1536  TREEVIEW_CalcItems(infoPtr);
     1537
     1538  if (lParam)
     1539  {
     1540    TREEVIEW_UnqueueRefresh(infoPtr,FALSE,FALSE);
     1541    TREEVIEW_Refresh(infoPtr);
     1542  }
     1543
     1544  return 0;
     1545}
     1546
     1547
     1548static LRESULT
     1549TREEVIEW_GetLineColor(TREEVIEW_INFO *infoPtr, WPARAM wParam, LPARAM lParam)
     1550{
     1551    return (LRESULT)infoPtr->clrLine;
     1552}
     1553
     1554static LRESULT
     1555TREEVIEW_SetLineColor(TREEVIEW_INFO *infoPtr, WPARAM wParam, LPARAM lParam)
     1556{
     1557    COLORREF prevColor = infoPtr->clrLine;
     1558
     1559    infoPtr->clrLine = (COLORREF)lParam;
     1560
     1561    return (LRESULT)prevColor;
     1562}
     1563
     1564
     1565static LRESULT
     1566TREEVIEW_GetTextColor(TREEVIEW_INFO *infoPtr)
     1567{
     1568    return (LRESULT)infoPtr->clrText;
     1569}
     1570
     1571static LRESULT
     1572TREEVIEW_SetTextColor(TREEVIEW_INFO *infoPtr, WPARAM wParam, LPARAM lParam)
     1573{
     1574    COLORREF prevColor = infoPtr->clrText;
     1575
     1576    infoPtr->clrText = (COLORREF)lParam;
     1577
     1578    if (infoPtr->clrText != prevColor)
     1579        TREEVIEW_QueueRefresh(infoPtr);
     1580
     1581    return (LRESULT)prevColor;
     1582}
     1583
     1584
     1585static LRESULT
     1586TREEVIEW_GetBkColor(TREEVIEW_INFO *infoPtr)
     1587{
     1588    return (LRESULT)infoPtr->clrBk;
     1589}
     1590
     1591static LRESULT
     1592TREEVIEW_SetBkColor(TREEVIEW_INFO *infoPtr, WPARAM wParam, LPARAM lParam)
     1593{
     1594    COLORREF prevColor = infoPtr->clrBk;
     1595
     1596    infoPtr->clrBk = (COLORREF)lParam;
     1597
     1598    if (infoPtr->clrBk != prevColor)
     1599        TREEVIEW_QueueRefresh(infoPtr);
     1600
     1601    return (LRESULT)prevColor;
     1602}
     1603
     1604
     1605static LRESULT
     1606TREEVIEW_GetInsertMarkColor(TREEVIEW_INFO *infoPtr, WPARAM wParam,
     1607                            LPARAM lParam)
     1608{
     1609    return (LRESULT)infoPtr->clrInsertMark;
     1610}
     1611
     1612static LRESULT
     1613TREEVIEW_SetInsertMarkColor(TREEVIEW_INFO *infoPtr, WPARAM wParam,
     1614                            LPARAM lParam)
     1615{
     1616    COLORREF prevColor = infoPtr->clrInsertMark;
     1617
     1618    infoPtr->clrInsertMark = (COLORREF)lParam;
     1619
     1620    return (LRESULT)prevColor;
     1621}
     1622
     1623
     1624static LRESULT
     1625TREEVIEW_SetInsertMark(TREEVIEW_INFO *infoPtr, WPARAM wParam, LPARAM lParam)
     1626{
     1627    if (!TREEVIEW_ValidItem(infoPtr, (HTREEITEM)lParam))
     1628        return 0;
     1629
     1630    infoPtr->insertBeforeorAfter = (BOOL)wParam;
     1631    infoPtr->insertMarkItem = (HTREEITEM)lParam;
     1632
     1633    TREEVIEW_QueueRefresh(infoPtr);
     1634
     1635    return 1;
     1636}
     1637
     1638/************************************************************************
     1639 * Some serious braindamage here. lParam is a pointer to both the
     1640 * input HTREEITEM and the output RECT.
     1641 */
     1642static LRESULT
     1643TREEVIEW_GetItemRect(TREEVIEW_INFO *infoPtr, WPARAM wParam, LPARAM lParam)
     1644{
     1645    TREEVIEW_ITEM *wineItem;
     1646    const HTREEITEM *pItem = (HTREEITEM *)lParam;
     1647    LPRECT lpRect = (LPRECT)lParam;
     1648
     1649    /*
     1650     * validate parameters
     1651     */
     1652    if ((pItem == NULL) || (lpRect == NULL))
     1653        return FALSE;
     1654
     1655    TREEVIEW_UnqueueRefresh(infoPtr,TRUE,TRUE);
     1656
     1657    wineItem = *pItem;
     1658    if (!TREEVIEW_ValidItem(infoPtr, *pItem) || !wineItem->displayed)
     1659        return FALSE;
     1660
     1661    if (!wineItem->calculated)
     1662      TREEVIEW_CalcItem(infoPtr,wineItem,(HDC)0);
     1663
     1664    /*
     1665     * If wParam is TRUE return the text size otherwise return
     1666     * the whole item size
     1667     */
     1668    if (wParam)
     1669    {
     1670        /* Windows does not send TVN_GETDISPINFO here. */
     1671
     1672        lpRect->top = wineItem->rect.top;
     1673        lpRect->bottom = wineItem->rect.bottom;
     1674
     1675        lpRect->left = wineItem->textOffset;
     1676        lpRect->right = wineItem->textOffset+wineItem->textWidth+4;
     1677    }
     1678    else
     1679    {
     1680        *lpRect = wineItem->rect;
     1681    }
     1682
     1683    return TRUE;
     1684}
     1685
     1686static inline LRESULT
     1687TREEVIEW_GetVisibleCount(TREEVIEW_INFO *infoPtr)
     1688{
     1689    /* Suprise! This does not take integral height into account. */
     1690    return infoPtr->clientHeight / infoPtr->uItemHeight;
     1691}
     1692
     1693static LRESULT
     1694TREEVIEW_GetItem(TREEVIEW_INFO *infoPtr, WPARAM wParam, LPARAM lParam,BOOL unicode)
     1695{
     1696    LPTVITEMEXW tvItem;
     1697    TREEVIEW_ITEM *wineItem;
     1698
     1699    tvItem = (LPTVITEMEXW)lParam;
     1700
     1701    wineItem = tvItem->hItem;
     1702
     1703    if (!TREEVIEW_ValidItem(infoPtr, wineItem))
     1704        return FALSE;
     1705
     1706    TREEVIEW_UpdateDispInfo(infoPtr, wineItem, tvItem->mask);
     1707
     1708    if (tvItem->mask & TVIF_CHILDREN)
     1709        tvItem->cChildren = wineItem->cChildren;
     1710
     1711    if (tvItem->mask & TVIF_HANDLE)
     1712        tvItem->hItem = wineItem;
     1713
     1714    if (tvItem->mask & TVIF_IMAGE)
     1715        tvItem->iImage = wineItem->iImage;
     1716
     1717    if (tvItem->mask & TVIF_INTEGRAL)
     1718        tvItem->iIntegral = wineItem->iIntegral;
     1719
     1720    /* undocumented: windows ignores TVIF_PARAM and
     1721     * * always sets lParam
     1722     */
     1723    tvItem->lParam = wineItem->lParam;
     1724
     1725    if (tvItem->mask & TVIF_SELECTEDIMAGE)
     1726        tvItem->iSelectedImage = wineItem->iSelectedImage;
     1727
     1728    if (tvItem->mask & TVIF_STATE)
     1729        tvItem->state = wineItem->state & tvItem->stateMask;
     1730
     1731    if (tvItem->mask & TVIF_TEXT)
     1732    {
     1733      if (unicode)
     1734        lstrcpynW(tvItem->pszText,wineItem->pszText,tvItem->cchTextMax);
     1735      else
     1736        lstrcpynWtoA((CHAR*)tvItem->pszText,wineItem->pszText,tvItem->cchTextMax);
     1737    }
     1738
     1739    return TRUE;
     1740}
     1741
     1742/* Beware MSDN Library Visual Studio 6.0. It says -1 on failure, 0 on success,
     1743 * which is wrong. */
     1744static LRESULT
     1745TREEVIEW_SetItem(TREEVIEW_INFO *infoPtr, WPARAM wParam, LPARAM lParam,BOOL unicode)
     1746{
     1747    TREEVIEW_ITEM *wineItem;
     1748    TVITEMEXW *tvItem;
     1749    DWORD changed = 0;
     1750
     1751    if (infoPtr->hwndEdit) SetFocus(infoPtr->hwnd);
     1752
     1753    tvItem = (LPTVITEMEXW)lParam;
     1754    wineItem = tvItem->hItem;
     1755
     1756    if (!TREEVIEW_ValidItem(infoPtr, wineItem))
     1757        return FALSE;
     1758
     1759    if (!TREEVIEW_DoSetItem(infoPtr,wineItem,tvItem,unicode,&changed))
     1760        return FALSE;
     1761
     1762    /* If the text or TVIS_BOLD was changed, and it is displayed, recalculate. */
     1763    if (((changed & TVIF_TEXT) || ((tvItem->mask & TVIF_STATE) && (tvItem->stateMask & TVIS_BOLD))) && wineItem->displayed)
     1764    {
     1765      TREEVIEW_ComputeTextWidth(infoPtr,wineItem,0,FALSE);
     1766    }
     1767
     1768    if (changed & (TVIF_INTEGRAL | TVIF_CHILDREN))
     1769      TREEVIEW_QueueRefresh(infoPtr);
     1770    else
     1771      TREEVIEW_RefreshItem(infoPtr,wineItem,changed);
     1772
     1773    return TRUE;
     1774}
     1775
     1776static LRESULT
     1777TREEVIEW_GetItemState(TREEVIEW_INFO *infoPtr, WPARAM wParam, LPARAM lParam)
     1778{
     1779    TREEVIEW_ITEM *wineItem = (HTREEITEM)wParam;
     1780
     1781    if (!wineItem || !TREEVIEW_ValidItem(infoPtr, wineItem))
     1782        return 0;
     1783
     1784    return (wineItem->state & lParam);
     1785}
     1786
     1787static LRESULT
     1788TREEVIEW_GetNextItem(TREEVIEW_INFO *infoPtr, WPARAM wParam, LPARAM lParam)
     1789{
     1790    TREEVIEW_ITEM *wineItem;
     1791    TREEVIEW_ITEM *retval;
     1792    WPARAM which;
     1793
     1794    which = (INT)wParam;
     1795    wineItem = (TREEVIEW_ITEM *)lParam;
     1796    retval = 0;
     1797
     1798    /* handle all the global data here */
     1799    switch (which)
     1800    {
     1801      case TVGN_CHILD:            /* Special case: child of 0 is root */
     1802        if (wineItem)
     1803            break;
     1804        /* fall through */
     1805      case TVGN_ROOT:
     1806        retval = infoPtr->root->firstChild;
     1807        break;
     1808
     1809      case TVGN_CARET:
     1810        retval = infoPtr->selectedItem;
     1811        break;
     1812
     1813      case TVGN_FIRSTVISIBLE:
     1814        TREEVIEW_UnqueueRefresh(infoPtr,TRUE,TRUE);
     1815        retval = infoPtr->firstVisible;
     1816        break;
     1817
     1818      case TVGN_DROPHILITE:
     1819        retval = infoPtr->dropItem;
     1820        break;
     1821    }
     1822
     1823    if (retval)
     1824    {
     1825        //TRACE("flags:%x, returns %p\n", which, retval);
     1826        return (LRESULT)retval;
     1827    }
     1828
     1829    if (!TREEVIEW_ValidItem(infoPtr, wineItem))
     1830        return FALSE;
     1831
     1832    switch (which)
     1833    {
     1834      case TVGN_NEXT:
     1835        retval = wineItem->nextSibling;
     1836        break;
     1837
     1838      case TVGN_PREVIOUS:
     1839        retval = wineItem->prevSibling;
     1840        break;
     1841
     1842      case TVGN_PARENT:
     1843        retval = (wineItem->parent != infoPtr->root) ? wineItem->parent : NULL;
     1844        break;
     1845
     1846      case TVGN_CHILD:
     1847        retval = wineItem->firstChild;
     1848        break;
     1849
     1850      case TVGN_LASTVISIBLE:
     1851        retval = TREEVIEW_GetLastListItem(infoPtr, wineItem);
     1852        break;
     1853
     1854      case TVGN_NEXTVISIBLE:
     1855        retval = TREEVIEW_GetNextListItem(infoPtr, wineItem);
     1856        break;
     1857
     1858      case TVGN_PREVIOUSVISIBLE:
     1859        retval = TREEVIEW_GetPrevListItem(infoPtr, wineItem);
     1860        break;
     1861
     1862      default:
     1863        //TRACE("Unknown msg %x,item %p\n", which, wineItem);
     1864        break;
     1865    }
     1866
     1867    //TRACE("flags:%x, item %p;returns %p\n", which, wineItem, retval);
     1868    return (LRESULT)retval;
     1869}
     1870
     1871
     1872static LRESULT
     1873TREEVIEW_GetCount(TREEVIEW_INFO *infoPtr, WPARAM wParam, LPARAM lParam)
     1874{
     1875    return (LRESULT)infoPtr->uNumItems;
     1876}
     1877
     1878static VOID
     1879TREEVIEW_ToggleItemState(TREEVIEW_INFO *infoPtr, TREEVIEW_ITEM *item)
     1880{
     1881    if (infoPtr->dwStyle & TVS_CHECKBOXES)
     1882    {
     1883        static const unsigned int state_table[] = { 0, 2, 1 };
     1884
     1885        unsigned int state;
     1886
     1887        state = STATEIMAGEINDEX(item->state);
     1888        //TRACE("state:%x\n", state);
     1889        item->state &= ~TVIS_STATEIMAGEMASK;
     1890
     1891        if (state < 3)
     1892            state = state_table[state];
     1893
     1894        item->state |= INDEXTOSTATEIMAGEMASK(state);
     1895
     1896        //TRACE("state:%x\n", state);
     1897        TREEVIEW_RefreshItem(infoPtr,item,TVIF_IMAGE);
     1898    }
     1899}
     1900
     1901
     1902/* Painting *************************************************************/
     1903
     1904/* Draw the lines and expand button for an item. Also draws one section
     1905 * of the line from item's parent to item's parent's next sibling. */
     1906static void
     1907TREEVIEW_DrawItemLines(TREEVIEW_INFO *infoPtr, HDC hdc, TREEVIEW_ITEM *item)
     1908{
     1909    LONG centerx, centery;
     1910    BOOL lar = ((infoPtr->dwStyle
     1911                 & (TVS_LINESATROOT|TVS_HASLINES|TVS_HASBUTTONS))
     1912                > TVS_LINESATROOT);
     1913
     1914    if (!lar && item->iLevel == 0)
     1915        return;
     1916
     1917    centerx = (item->linesOffset + item->stateOffset) / 2;
     1918    centery = (item->rect.top + item->rect.bottom) / 2;
     1919
     1920    if (infoPtr->dwStyle & TVS_HASLINES)
     1921    {
     1922        HPEN hOldPen, hNewPen;
     1923        HTREEITEM parent;
     1924
     1925        /*
     1926         * Get a dotted grey pen
     1927         */
     1928        hNewPen = CreatePen(PS_DOT, 0, infoPtr->clrLine);
     1929        hOldPen = SelectObject(hdc, hNewPen);
     1930
     1931        MoveToEx(hdc, item->stateOffset, centery, NULL);
     1932        LineTo(hdc, centerx - 1, centery);
     1933
     1934        if (item->prevSibling || item->parent != infoPtr->root)
     1935        {
     1936            MoveToEx(hdc, centerx, item->rect.top, NULL);
     1937            LineTo(hdc, centerx, centery);
     1938        }
     1939
     1940        if (item->nextSibling)
     1941        {
     1942            MoveToEx(hdc, centerx, centery, NULL);
     1943            LineTo(hdc, centerx, item->rect.bottom + 1);
     1944        }
     1945
     1946        /* Draw the line from our parent to its next sibling. */
     1947        parent = item->parent;
     1948        while (parent != infoPtr->root)
     1949        {
     1950            int pcenterx = (parent->linesOffset + parent->stateOffset) / 2;
     1951
     1952            if (parent->nextSibling
     1953                /* skip top-levels unless TVS_LINESATROOT */
     1954                && parent->stateOffset > parent->linesOffset)
     1955            {
     1956                MoveToEx(hdc, pcenterx, item->rect.top, NULL);
     1957                LineTo(hdc, pcenterx, item->rect.bottom + 1);
     1958            }
     1959
     1960            parent = parent->parent;
     1961        }
     1962
     1963        SelectObject(hdc, hOldPen);
     1964        DeleteObject(hNewPen);
     1965    }
     1966
     1967    /*
     1968     * Display the (+/-) signs
     1969     */
     1970
     1971    if (infoPtr->dwStyle & TVS_HASBUTTONS)
     1972    {
     1973        if (item->cChildren)
     1974        {
     1975            LONG height = item->rect.bottom - item->rect.top;
     1976            LONG width  = item->stateOffset - item->linesOffset;
     1977            LONG rectsize = MIN(height, width) / 4;
     1978            /* plussize = ceil(rectsize * 3/4) */
     1979            LONG plussize = (rectsize + 1) * 3 / 4;
     1980
     1981            HPEN hNewPen = CreatePen(PS_SOLID, 0, infoPtr->clrLine);
     1982            HPEN hOldPen = SelectObject(hdc, hNewPen);
     1983
     1984            Rectangle(hdc, centerx - rectsize, centery - rectsize,
     1985                      centerx + rectsize + 1, centery + rectsize + 1);
     1986
     1987            SelectObject(hdc, hOldPen);
     1988            DeleteObject(hNewPen);
     1989
     1990            MoveToEx(hdc, centerx - plussize + 1, centery, NULL);
     1991            LineTo(hdc, centerx + plussize, centery);
     1992
     1993            if (!(item->state & TVIS_EXPANDED))
     1994            {
     1995                MoveToEx(hdc, centerx, centery - plussize + 1, NULL);
     1996                LineTo(hdc, centerx, centery + plussize);
     1997            }
     1998        }
     1999    }
     2000}
     2001
     2002static VOID TREEVIEW_DrawHottrackLine(HDC hdc,TREEVIEW_ITEM *item)
     2003{
     2004  HPEN hPen,hOldPen;
     2005  INT rop;
     2006
     2007  if (!item->inclient) return;
     2008
     2009  rop = SetROP2(hdc,R2_XORPEN);
     2010  hPen = CreatePen(PS_SOLID,2,RGB(0,0,0));
     2011  hOldPen = SelectObject(hdc,hPen);
     2012
     2013  MoveToEx(hdc,item->textOffset,item->rect.bottom-1,NULL);
     2014  LineTo(hdc,item->textOffset+item->textWidth,item->rect.bottom-1);
     2015
     2016  DeleteObject(hPen);
     2017  SelectObject(hdc,hOldPen);
     2018  SetROP2(hdc,rop);
     2019}
     2020
     2021static void
     2022TREEVIEW_DrawItem(TREEVIEW_INFO *infoPtr, HDC hdc, TREEVIEW_ITEM *wineItem)
     2023{
     2024    INT cditem;
     2025    HFONT hOldFont;
     2026    int centery;
     2027
     2028    if (!wineItem->calculated) TREEVIEW_CalcItem(infoPtr,wineItem,hdc);
     2029
     2030    hOldFont = SelectObject(hdc, TREEVIEW_FontForItem(infoPtr, wineItem));
     2031
     2032    TREEVIEW_UpdateDispInfo(infoPtr, wineItem, CALLBACK_MASK_ALL);
     2033
     2034    /* The custom draw handler can query the text rectangle,
     2035     * so get ready. */
     2036    TREEVIEW_ComputeTextWidth(infoPtr,wineItem,hdc,TRUE);
     2037
     2038    cditem = 0;
     2039
     2040    if (infoPtr->cdmode & CDRF_NOTIFYITEMDRAW)
     2041    {
     2042        cditem = TREEVIEW_SendCustomDrawItemNotify(infoPtr, hdc, wineItem, CDDS_ITEMPREPAINT);
     2043
     2044        if (cditem & CDRF_SKIPDEFAULT)
     2045        {
     2046            SelectObject(hdc, hOldFont);
     2047            return;
     2048        }
     2049    }
     2050
     2051    if (cditem & CDRF_NEWFONT)
     2052        TREEVIEW_ComputeTextWidth(infoPtr,wineItem,hdc,TRUE);
     2053
     2054    TREEVIEW_DrawItemLines(infoPtr, hdc, wineItem);
     2055
     2056    centery = (wineItem->rect.top + wineItem->rect.bottom) / 2;
     2057
     2058    /*
     2059     * Display the images associated with this item
     2060     */
     2061    {
     2062        INT imageIndex;
     2063
     2064        /* State images are displayed to the left of the Normal image
     2065         * image number is in state; zero should be `display no image'.
     2066         */
     2067        imageIndex = STATEIMAGEINDEX(wineItem->state);
     2068
     2069        if (infoPtr->himlState && imageIndex)
     2070        {
     2071            ImageList_Draw(infoPtr->himlState, imageIndex - 1, hdc,
     2072                           wineItem->stateOffset,
     2073                           centery - infoPtr->stateImageHeight / 2,
     2074                           ILD_NORMAL);
     2075        }
     2076
     2077        /* Now, draw the normal image; can be either selected or
     2078         * non-selected image.
     2079         */
     2080
     2081        if ((wineItem->state & TVIS_SELECTED) && wineItem->iSelectedImage)
     2082        {
     2083            /* The item is curently selected */
     2084            imageIndex = wineItem->iSelectedImage;
     2085        }
     2086        else
     2087        {
     2088            /* The item is not selected */
     2089            imageIndex = wineItem->iImage;
     2090        }
     2091
     2092        if (infoPtr->himlNormal && (imageIndex != I_IMAGENONE))
     2093        {
     2094            int ovlIdx = wineItem->state & TVIS_OVERLAYMASK;
     2095
     2096            ImageList_Draw(infoPtr->himlNormal, imageIndex, hdc,
     2097                           wineItem->imageOffset,
     2098                           centery - infoPtr->normalImageHeight / 2,
     2099                           ILD_NORMAL | ovlIdx);
     2100        }
     2101    }
     2102
     2103
     2104    /*
     2105     * Display the text associated with this item
     2106     */
     2107
     2108    /* Don't paint item's text if it's being edited */
     2109    if (!infoPtr->hwndEdit)
     2110    {
     2111        if (wineItem->pszText && (wineItem->pszText[0] != 0))
     2112        {
     2113            COLORREF oldTextColor = 0;
     2114            INT oldBkMode;
     2115            HBRUSH hbrBk = 0;
     2116            BOOL inFocus = (GetFocus() == infoPtr->hwnd);
     2117            RECT rcText;
     2118
     2119            oldBkMode = SetBkMode(hdc, TRANSPARENT);
     2120
     2121            /* - If item is drop target or it is selected and window is in focus -
     2122             * use blue background (COLOR_HIGHLIGHT).
     2123             * - If item is selected, window is not in focus, but it has style
     2124             * TVS_SHOWSELALWAYS - use grey background (COLOR_BTNFACE)
     2125             * - Otherwise - don't fill background
     2126             */
     2127            if ((wineItem->state & TVIS_DROPHILITED) ||
     2128                ((wineItem->state & TVIS_SELECTED) &&
     2129                 (inFocus || (infoPtr->dwStyle & TVS_SHOWSELALWAYS))))
     2130            {
     2131                if ((wineItem->state & TVIS_DROPHILITED) || inFocus)
     2132                {
     2133                    hbrBk = CreateSolidBrush(GetSysColor(COLOR_HIGHLIGHT));
     2134                    oldTextColor =
     2135                        SetTextColor(hdc, GetSysColor(COLOR_HIGHLIGHTTEXT));
     2136                }
     2137                else
     2138                {
     2139                    hbrBk = CreateSolidBrush(GetSysColor(COLOR_BTNFACE));
     2140
     2141                    if (infoPtr->clrText == -1)
     2142                        oldTextColor =
     2143                            SetTextColor(hdc, GetSysColor(COLOR_WINDOWTEXT));
     2144                    else
     2145                        oldTextColor = SetTextColor(hdc, infoPtr->clrText);
     2146                }
     2147            }
     2148            else
     2149            {
     2150                if (infoPtr->clrText == -1)
     2151                    oldTextColor =
     2152                        SetTextColor(hdc, GetSysColor(COLOR_WINDOWTEXT));
     2153                else
     2154                    oldTextColor = SetTextColor(hdc, infoPtr->clrText);
     2155            }
     2156
     2157            rcText.top = wineItem->rect.top;
     2158            rcText.bottom = wineItem->rect.bottom;
     2159            rcText.left = wineItem->textOffset;
     2160            rcText.right = rcText.left + wineItem->textWidth + 4;
     2161
     2162            if (hbrBk)
     2163            {
     2164                FillRect(hdc, &rcText, hbrBk);
     2165                DeleteObject(hbrBk);
     2166            }
     2167
     2168            /* Draw the box arround the selected item */
     2169            if ((wineItem->state & TVIS_SELECTED) && inFocus)
     2170            {
     2171                HPEN hNewPen = CreatePen(PS_DOT, 0,
     2172                                         GetSysColor(COLOR_WINDOWTEXT));
     2173                HPEN hOldPen = SelectObject(hdc, hNewPen);
     2174                INT rop = SetROP2(hdc, R2_XORPEN);
     2175                POINT points[5];
     2176
     2177                points[4].x = points[0].x = rcText.left;
     2178                points[4].y = points[0].y = rcText.top;
     2179                points[1].x = rcText.right - 1;
     2180                points[1].y = rcText.top;
     2181                points[2].x = rcText.right - 1;
     2182                points[2].y = rcText.bottom - 1;
     2183                points[3].x = rcText.left;
     2184                points[3].y = rcText.bottom - 1;
     2185
     2186                Polyline(hdc, points, 5);
     2187
     2188                SetROP2(hdc, rop);
     2189                SelectObject(hdc, hOldPen);
     2190                DeleteObject(hNewPen);
     2191            }
     2192
     2193            rcText.left += 2;
     2194            rcText.right -= 2;
     2195
     2196            /* Draw it */
     2197            DrawTextW(hdc,
     2198                      wineItem->pszText,
     2199                      lstrlenW(wineItem->pszText),
     2200                      &rcText,
     2201                      DT_LEFT | DT_VCENTER | DT_SINGLELINE | DT_NOPREFIX);
     2202
     2203            /* Restore the hdc state */
     2204            SetTextColor(hdc, oldTextColor);
     2205
     2206            if (oldBkMode != TRANSPARENT)
     2207                SetBkMode(hdc, oldBkMode);
     2208        }
     2209    }
     2210
     2211    /* Draw insertion mark if necessary */
     2212
     2213    if (wineItem == infoPtr->insertMarkItem)
     2214    {
     2215        HPEN hNewPen, hOldPen;
     2216        int offset;
     2217        int left, right;
     2218
     2219        hNewPen = CreatePen(PS_SOLID, 2, infoPtr->clrInsertMark);
     2220        hOldPen = SelectObject(hdc, hNewPen);
     2221
     2222        if (infoPtr->insertBeforeorAfter)
     2223            offset = wineItem->rect.bottom - 1;
     2224        else
     2225            offset = wineItem->rect.top + 1;
     2226
     2227        left = wineItem->textOffset - 2;
     2228        right = wineItem->textOffset + wineItem->textWidth + 2;
     2229
     2230        MoveToEx(hdc, left, offset - 3, NULL);
     2231        LineTo(hdc, left, offset + 4);
     2232
     2233        MoveToEx(hdc, left, offset, NULL);
     2234        LineTo(hdc, right + 1, offset);
     2235
     2236        MoveToEx(hdc, right, offset + 3, NULL);
     2237        LineTo(hdc, right, offset - 4);
     2238
     2239        SelectObject(hdc, hOldPen);
     2240        DeleteObject(hNewPen);
     2241    }
     2242
     2243    //draw hot item if necessary
     2244    if (wineItem == infoPtr->hotItem)
     2245      TREEVIEW_DrawHottrackLine(hdc,wineItem);
     2246
     2247    if (cditem & CDRF_NOTIFYPOSTPAINT)
     2248    {
     2249        cditem = TREEVIEW_SendCustomDrawItemNotify(infoPtr, hdc, wineItem, CDDS_ITEMPOSTPAINT);
     2250    }
     2251
     2252    SelectObject(hdc, hOldFont);
     2253}
     2254
     2255static void TREEVIEW_QueueRefresh(TREEVIEW_INFO *infoPtr)
     2256{
     2257  if (infoPtr->Timer & TV_REFRESH_TIMER_SET)
     2258    KillTimer(infoPtr->hwnd,TV_REFRESH_TIMER);
     2259
     2260  if (infoPtr->uInternalStatus & TV_NOREDRAW)
     2261  {
     2262    infoPtr->Timer &= ~TV_REFRESH_TIMER_SET;
     2263
     2264    return;
     2265  }
     2266
     2267  SetTimer(infoPtr->hwnd,TV_REFRESH_TIMER,TV_REFRESH_DELAY,0);
     2268  infoPtr->Timer |= TV_REFRESH_TIMER_SET;
     2269}
     2270
     2271static BOOL TREEVIEW_UnqueueRefresh(TREEVIEW_INFO *infoPtr,BOOL calc,BOOL refresh)
     2272{
     2273  if (infoPtr->Timer & TV_REFRESH_TIMER_SET)
     2274  {
     2275    KillTimer(infoPtr->hwnd,TV_REFRESH_TIMER);
     2276    infoPtr->Timer &= ~TV_REFRESH_TIMER_SET;
     2277
     2278    if (calc) TREEVIEW_CalcItems(infoPtr);
     2279    if (refresh)
     2280    {
     2281      TREEVIEW_Refresh(infoPtr);
     2282      UpdateWindow(infoPtr->hwnd);
     2283    }
     2284
     2285    return TRUE;
     2286  }
     2287
     2288  return FALSE;
     2289}
     2290
     2291static void
     2292TREEVIEW_Draw(TREEVIEW_INFO *infoPtr,HDC hdc,RECT *updateRect)
     2293{
    15972294    RECT rect;
    15982295    TREEVIEW_ITEM *item;
    1599     INT iItem,indent;
    1600     BOOL visFound = FALSE;
    1601     HPEN hNewPen,hOldPen;
    1602 
    1603     TREEVIEW_UnqueueRefresh(hwnd,TRUE,FALSE);
    1604 
    1605     GetClientRect (hwnd, &rect);
     2296    INT iItem;
     2297
     2298    TREEVIEW_UnqueueRefresh(infoPtr,TRUE,FALSE);
     2299
     2300    GetClientRect(infoPtr->hwnd,&rect);
    16062301    if ((rect.left == rect.right) || (rect.top == rect.bottom)) return;
    16072302
    1608     infoPtr->cdmode = TREEVIEW_SendCustomDrawNotify(hwnd, CDDS_PREPAINT, hdc, rect);
     2303    infoPtr->cdmode = TREEVIEW_SendCustomDrawNotify(infoPtr,CDDS_PREPAINT,hdc,rect);
    16092304
    16102305    if (infoPtr->cdmode == CDRF_SKIPDEFAULT) return;
    16112306
    1612     //draw items
    1613 
    1614     hNewPen = CreatePen(PS_DOT,0,infoPtr->clrLine);
    1615 
    1616     iItem = (INT)infoPtr->TopRootItem;
    1617     indent = 0;
    1618 
    1619     while (iItem)
    1620     {
    1621       item = &infoPtr->items[iItem];
    1622       if (item->visible)
     2307    /* We iterate through all visible items in order */
     2308    item = infoPtr->firstVisible;
     2309
     2310    while ((item != NULL) && item->inclient)
     2311    {
     2312      RECT itemRect = item->rect;
     2313
     2314      if (!item->calculated) TREEVIEW_CalcItem(infoPtr,item,hdc);
     2315      if (updateRect && IntersectRect(NULL,&itemRect,updateRect))
     2316        TREEVIEW_DrawItem(infoPtr,hdc,item);
     2317      else
    16232318      {
    1624         if (updateRect && IntersectRect(NULL,&item->rect,updateRect))
    1625         {
    1626           TREEVIEW_DrawItem(hwnd,hdc,item,dwStyle,infoPtr);
    1627           visFound = TRUE;
    1628         } else if (updateRect && (item->rect.top >= updateRect->bottom)) break;
    1629       } else if (visFound) break;
    1630       if (!visFound && (dwStyle & TVS_HASLINES) && (dwStyle & TVS_LINESATROOT))
    1631       {
    1632         //draw vertical connections
    1633         hOldPen = SelectObject(hdc,hNewPen);
    1634         TREEVIEW_DrawVLines(hdc,infoPtr,item);
    1635         SelectObject(hdc,hOldPen);
     2319        if (updateRect && (item->rect.top >= updateRect->bottom)) break;
     2320        itemRect.left = 0;
     2321        if (updateRect && IntersectRect(NULL,&itemRect,updateRect))
     2322          TREEVIEW_DrawItemLines(infoPtr,hdc,item);
    16362323      }
    1637       if (item->firstChild && (item->state & TVIS_EXPANDED))
    1638       {
    1639         iItem = (INT)item->firstChild;
    1640         indent++;
    1641       } else
    1642       {
    1643         iItem = (INT)item->sibling;
    1644         while (!iItem && (indent > 0))
    1645         {
    1646           item = &infoPtr->items[(INT)item->parent];
    1647           iItem = (INT)item->sibling;
    1648           indent--;
    1649         }
    1650       }
    1651     }
    1652 
    1653     DeleteObject(hNewPen);
     2324      item = TREEVIEW_GetNextListItem(infoPtr,item);
     2325    }
    16542326
    16552327    if (infoPtr->cdmode & CDRF_NOTIFYPOSTPAINT)
    1656       infoPtr->cdmode = TREEVIEW_SendCustomDrawNotify(hwnd, CDDS_POSTPAINT, hdc, rect);
    1657 }
    1658 
    1659 static LRESULT TREEVIEW_SetRedraw(HWND hwnd,WPARAM wParam,LPARAM lParam)
    1660 {
    1661   TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
    1662 
    1663   if (wParam)
    1664   {
    1665     if (!(infoPtr->uInternalStatus & TV_NOREDRAW)) return 0;
    1666     infoPtr->uInternalStatus &= ~TV_NOREDRAW;
    1667     TREEVIEW_CalcItems(hwnd,0,infoPtr);
    1668     TREEVIEW_Refresh(hwnd);
     2328        infoPtr->cdmode = TREEVIEW_SendCustomDrawNotify(infoPtr, CDDS_POSTPAINT, hdc, rect);
     2329}
     2330
     2331static void TREEVIEW_Refresh(TREEVIEW_INFO *infoPtr)
     2332{
     2333  TREEVIEW_UnqueueRefresh(infoPtr,TRUE,FALSE);
     2334
     2335  InvalidateRect(infoPtr->hwnd,NULL,TRUE);
     2336}
     2337
     2338static void TREEVIEW_RefreshItem(TREEVIEW_INFO *infoPtr,TREEVIEW_ITEM *item,DWORD changed)
     2339{
     2340  if (item && item->inclient)
     2341  {
     2342    RECT rect = item->rect;
     2343
     2344    //left
     2345    if (changed & TVIF_STATE) rect.left = item->stateOffset;
     2346    else if (changed & (TVIF_IMAGE | TVIF_SELECTEDIMAGE)) rect.left = item->imageOffset;
     2347    else if (changed & TVIF_TEXT) rect.left = item->textOffset;
     2348
     2349    //right
     2350    if (changed & TVIF_TEXT);
     2351    else if (changed & (TVIF_IMAGE | TVIF_SELECTEDIMAGE)) rect.right = item->imageOffset-infoPtr->normalImageWidth;
     2352    else if (changed & TVIF_STATE) rect.right = item->stateOffset-infoPtr->stateImageWidth;
     2353
     2354    InvalidateRect(infoPtr->hwnd,&rect,TRUE);
     2355  }
     2356}
     2357
     2358static LRESULT
     2359TREEVIEW_Paint(TREEVIEW_INFO *infoPtr,WPARAM wParam,LPARAM lParam)
     2360{
     2361  HDC hdc = (HDC)wParam;
     2362
     2363  if (!hdc)
     2364  {
     2365    PAINTSTRUCT ps;
     2366
     2367    hdc = BeginPaint(infoPtr->hwnd,&ps);
     2368    TREEVIEW_Draw(infoPtr,hdc,&ps.rcPaint);
     2369    EndPaint(infoPtr->hwnd,&ps);
    16692370  } else
    1670   {
    1671     infoPtr->uInternalStatus |= TV_NOREDRAW;
    1672   }
     2371      TREEVIEW_Draw(infoPtr,hdc,NULL);
    16732372
    16742373  return 0;
    16752374}
    16762375
    1677 
    1678 static LRESULT
    1679 TREEVIEW_HandleTimer (HWND hwnd, WPARAM wParam, LPARAM lParam)
    1680 {
    1681   TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
    1682 
    1683   switch (wParam)
    1684   {
    1685     case TV_REFRESH_TIMER:
    1686       KillTimer (hwnd, TV_REFRESH_TIMER);
    1687       infoPtr->Timer &= ~TV_REFRESH_TIMER_SET;
    1688       TREEVIEW_CalcItems(hwnd,0,infoPtr);
    1689       TREEVIEW_Refresh(hwnd);
    1690       return 0;
    1691 
    1692     case TV_EDIT_TIMER:
    1693       KillTimer (hwnd, TV_EDIT_TIMER);
    1694       infoPtr->Timer &= ~TV_EDIT_TIMER_SET;
    1695       if (infoPtr->editItem)
    1696         TREEVIEW_EditLabel(hwnd,infoPtr->editItem,TRUE);
    1697       return 0;
    1698 
    1699     case TV_INFOTIP_TIMER:
    1700       TREEVIEW_CheckInfoTip(hwnd);
    1701       return 0;
    1702  }
    1703 
    1704  return 1;
    1705 }
    1706 
    1707 static void TREEVIEW_QueueRefresh(HWND hwnd)
    1708 
    1709 {
    1710   TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
    1711 
    1712   if (infoPtr->Timer & TV_REFRESH_TIMER_SET)
    1713     KillTimer (hwnd, TV_REFRESH_TIMER);
    1714 
    1715   if (infoPtr->uInternalStatus & TV_NOREDRAW)
    1716   {
    1717     infoPtr->Timer &= ~TV_REFRESH_TIMER_SET;
    1718 
    1719     return;
    1720   }
    1721 
    1722   SetTimer (hwnd, TV_REFRESH_TIMER, TV_REFRESH_DELAY, 0);
    1723   infoPtr->Timer |= TV_REFRESH_TIMER_SET;
    1724 }
    1725 
    1726 static BOOL TREEVIEW_UnqueueRefresh(HWND hwnd,BOOL calc,BOOL refresh)
    1727 {
    1728   TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
    1729 
    1730   if (infoPtr->Timer & TV_REFRESH_TIMER_SET)
    1731   {
    1732     KillTimer (hwnd, TV_REFRESH_TIMER);
    1733     infoPtr->Timer &= ~TV_REFRESH_TIMER_SET;
    1734     if (calc) TREEVIEW_CalcItems(hwnd,0,infoPtr);
    1735     if (refresh)
    1736     {
    1737       TREEVIEW_Refresh(hwnd);
    1738       UpdateWindow(hwnd);
    1739     }
    1740 
    1741     return TRUE;
    1742   }
    1743 
    1744   return FALSE;
    1745 }
    1746 
    1747 static LRESULT
    1748 TREEVIEW_GetItem(HWND hwnd,WPARAM wParam,LPARAM lParam,BOOL unicode)
    1749 {
    1750   TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
    1751   LPTVITEMEXW    tvItem;
    1752   TREEVIEW_ITEM *wineItem;
    1753   INT         iItem;
    1754 
    1755   tvItem = (LPTVITEMEXW)lParam;
    1756   iItem = (INT)tvItem->hItem;
    1757 
    1758   wineItem = TREEVIEW_ValidItem (infoPtr, (HTREEITEM)iItem);
    1759   if (!wineItem) return FALSE;
    1760 
    1761   if (tvItem->mask & TVIF_CHILDREN)
    1762     tvItem->cChildren = TREEVIEW_HasChildren(hwnd, wineItem);
    1763 
    1764   if (tvItem->mask & TVIF_HANDLE)
    1765     tvItem->hItem = wineItem->hItem;
    1766 
    1767   if (tvItem->mask & TVIF_IMAGE)
    1768   {
    1769     if (wineItem->iImage == I_IMAGECALLBACK)
    1770       tvItem->iImage = TREEVIEW_CallbackImage(hwnd,wineItem);
    1771     else
    1772       tvItem->iImage = wineItem->iImage;
    1773   }
    1774 
    1775   if (tvItem->mask & TVIF_INTEGRAL)
    1776     tvItem->iIntegral = wineItem->iIntegral;
    1777 
    1778   // undocumented: windows ignores TVIF_PARAM and
    1779   // always sets lParam
    1780   tvItem->lParam = wineItem->lParam;
    1781 
    1782   if (tvItem->mask & TVIF_SELECTEDIMAGE)
    1783   {
    1784     if (wineItem->iSelectedImage == I_IMAGECALLBACK)
    1785       tvItem->iSelectedImage = TREEVIEW_CallbackSelectedImage(hwnd,wineItem);
    1786     else
    1787       tvItem->iSelectedImage = wineItem->iSelectedImage;
    1788   }
    1789 
    1790   if (tvItem->mask & TVIF_STATE)
    1791     tvItem->state = wineItem->state & tvItem->stateMask;
    1792 
    1793   if (tvItem->mask & TVIF_TEXT)
    1794   {
    1795     WCHAR* text;
    1796     BOOL mustFree = FALSE;
    1797 
    1798     if (wineItem->pszText == LPSTR_TEXTCALLBACKW)
    1799       text = TREEVIEW_CallbackText(hwnd,wineItem,&mustFree);
    1800     else
    1801       text = wineItem->pszText;
    1802 
    1803     if (unicode)
    1804       lstrcpynW(tvItem->pszText,text,tvItem->cchTextMax);
    1805     else
    1806       lstrcpynWtoA((LPSTR)tvItem->pszText,text,tvItem->cchTextMax);
    1807     if (mustFree) COMCTL32_Free(text);
    1808   }
    1809 
    1810   return TRUE;
    1811 }
    1812 
    1813 
    1814 
    1815 /* FIXME: check implementation of TVGN_NEXT/TVGN_NEXTVISIBLE */
    1816 
    1817 static LRESULT
    1818 TREEVIEW_GetNextItem (HWND hwnd, WPARAM wParam, LPARAM lParam)
    1819 
    1820 {
    1821   TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
    1822   TREEVIEW_ITEM *wineItem, *returnItem;
    1823   INT iItem, retval, flag;
    1824 
    1825   flag  = (INT) wParam;
    1826   iItem = (INT) lParam;
    1827   retval = 0;
    1828   switch (flag)
    1829   {
    1830     case TVGN_CHILD: /* Special case: child of 0 is root */
    1831       if (iItem) break;
    1832 
    1833     case TVGN_ROOT:
    1834       retval = (INT)infoPtr->TopRootItem;
    1835       break;
    1836 
    1837     case TVGN_CARET:
    1838       retval = (INT)infoPtr->selectedItem;
    1839       break;
    1840 
    1841     case TVGN_FIRSTVISIBLE:
    1842       TREEVIEW_UnqueueRefresh(hwnd,TRUE,TRUE);
    1843       retval = (INT)infoPtr->firstVisible;
    1844       break;
    1845 
    1846     case TVGN_DROPHILITE:
    1847       retval = (INT)infoPtr->dropItem;
    1848       break;
    1849   }
    1850 
    1851   if (retval) return retval;
    1852 
    1853   wineItem = TREEVIEW_ValidItem (infoPtr, (HTREEITEM)iItem);
    1854   returnItem = NULL;
    1855   if (!wineItem) return FALSE;
    1856 
    1857   switch (flag)
    1858   {
    1859     case TVGN_NEXT:
    1860       retval = (INT)wineItem->sibling;
    1861       break;
    1862 
    1863     case TVGN_PREVIOUS:
    1864       retval = (INT)wineItem->upsibling;
    1865       break;
    1866 
    1867     case TVGN_PARENT:
    1868       retval = (INT)wineItem->parent;
    1869       break;
    1870 
    1871     case TVGN_CHILD:
    1872       retval = (INT)wineItem->firstChild;
    1873       break;
    1874 
    1875     case TVGN_LASTVISIBLE:
    1876       returnItem = TREEVIEW_GetLastListItem (hwnd,infoPtr,wineItem);
    1877       break;
    1878 
    1879     case TVGN_NEXTVISIBLE:
    1880       returnItem = TREEVIEW_GetNextListItem (hwnd,infoPtr,wineItem);
    1881       break;
    1882 
    1883     case TVGN_PREVIOUSVISIBLE:
    1884       returnItem = TREEVIEW_GetPrevListItem (hwnd,infoPtr, wineItem);
    1885       break;
    1886 
    1887     default:
    1888       break;
    1889   }
    1890 
    1891   if (returnItem) return (INT)returnItem->hItem;
    1892 
    1893   return retval;
    1894 }
    1895 
    1896 
    1897 static LRESULT
    1898 TREEVIEW_GetCount (HWND hwnd, WPARAM wParam, LPARAM lParam)
    1899 {
    1900   TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
    1901 
    1902   return (LRESULT) infoPtr->uNumItems;
    1903 }
    1904 
    1905 /***************************************************************************
    1906  * This method does the chaining of the insertion of a treeview item
    1907  * before an item.
    1908  * If parent is NULL, we're inserting at the root of the list.
    1909  */
    1910 static void TREEVIEW_InsertBefore(
    1911     TREEVIEW_INFO *infoPtr,
    1912     TREEVIEW_ITEM *newItem,
    1913     TREEVIEW_ITEM *sibling,
    1914     TREEVIEW_ITEM *parent)
    1915 {
    1916   HTREEITEM     siblingHandle   = 0;
    1917   HTREEITEM     upSiblingHandle = 0;
    1918   TREEVIEW_ITEM *upSibling      = NULL;
    1919 
    1920 //  if (newItem == NULL)
    1921 //    ERR(treeview, "NULL newItem, impossible condition\n");
    1922 
    1923 //  if (parent == NULL)
    1924 //    ERR(treeview, "NULL parent, impossible condition\n");
    1925 
    1926   if (sibling != NULL) /* Insert before this sibling for this parent */
    1927   {
    1928     /* Store the new item sibling up sibling and sibling tem handle */
    1929     siblingHandle   = sibling->hItem;
    1930     upSiblingHandle = sibling->upsibling;
    1931     /* As well as a pointer to the upsibling sibling object */
    1932     if ( (INT)sibling->upsibling != 0 )
    1933       upSibling = &infoPtr->items[(INT)sibling->upsibling];
    1934 
    1935     /* Adjust the sibling pointer */
    1936     sibling->upsibling = newItem->hItem;
    1937 
    1938     /* Adjust the new item pointers */
    1939     newItem->upsibling = upSiblingHandle;
    1940     newItem->sibling   = siblingHandle;
    1941 
    1942     /* Adjust the up sibling pointer */
    1943     if ( upSibling != NULL )
    1944       upSibling->sibling = newItem->hItem;
    1945     else
    1946     if (parent)
    1947       /* this item is the first child of this parent, adjust parent pointers */
    1948       parent->firstChild = newItem->hItem;
    1949     else infoPtr->TopRootItem = newItem->hItem;
    1950   }
    1951   else /* Insert as first child of this parent */
    1952   if (parent)
    1953     parent->firstChild = newItem->hItem;
    1954 }
    1955 
    1956 /***************************************************************************
    1957  * This method does the chaining of the insertion of a treeview item
    1958  * If parent is NULL, we're inserting at the root of the list.
    1959  * after an item.
    1960  */
    1961 static void TREEVIEW_InsertAfter(
    1962     TREEVIEW_INFO *infoPtr,
    1963     TREEVIEW_ITEM *newItem,
    1964     TREEVIEW_ITEM *upSibling,
    1965     TREEVIEW_ITEM *parent)
    1966 {
    1967   HTREEITEM     upSiblingHandle = 0;
    1968   HTREEITEM     siblingHandle   = 0;
    1969   TREEVIEW_ITEM *sibling        = NULL;
    1970 
    1971 //  if (newItem == NULL)
    1972 //    ERR(treeview, "NULL newItem, impossible condition\n");
    1973 
    1974 //  if (parent == NULL)
    1975 //    ERR(treeview, "NULL parent, impossible condition\n");
    1976 
    1977   if (upSibling != NULL) /* Insert after this upsibling for this parent */
    1978   {
    1979     /* Store the new item up sibling and sibling item handle */
    1980     upSiblingHandle = upSibling->hItem;
    1981     siblingHandle   = upSibling->sibling;
    1982     /* As well as a pointer to the upsibling sibling object */
    1983     if ( (INT)upSibling->sibling != 0 )
    1984       sibling = &infoPtr->items[(INT)upSibling->sibling];
    1985 
    1986     /* Adjust the up sibling pointer */
    1987     upSibling->sibling = newItem->hItem;
    1988 
    1989     /* Adjust the new item pointers */
    1990     newItem->upsibling = upSiblingHandle;
    1991     newItem->sibling   = siblingHandle;
    1992 
    1993     /* Adjust the sibling pointer */
    1994     if ( sibling != NULL )
    1995       sibling->upsibling = newItem->hItem;
    1996     /*
    1997     else
    1998       newItem is the last of the level, nothing else to do
    1999     */
    2000   }
    2001   else /* Insert as first child of this parent */
    2002   if (parent)
    2003     parent->firstChild = newItem->hItem;
    2004 }
     2376/* Sorting **************************************************************/
    20052377
    20062378/***************************************************************************
    20072379 * Forward the DPA local callback to the treeview owner callback
    20082380 */
    2009 static INT WINAPI TREEVIEW_CallBackCompare(LPVOID first,LPVOID second,LPARAM tvInfoPtr)
    2010 {
    2011   /* Forward the call to the client define callback */
    2012   TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr((HWND)tvInfoPtr);
    2013 
    2014   return (infoPtr->pCallBackSort->lpfnCompare)(
    2015     ((TREEVIEW_ITEM*)first)->lParam,
    2016     ((TREEVIEW_ITEM*)second)->lParam,
    2017     infoPtr->pCallBackSort->lParam);
     2381static INT WINAPI
     2382TREEVIEW_CallBackCompare(LPVOID pvFirst, LPVOID pvSecond, LPARAM callback)
     2383{
     2384    LPTVSORTCB pCallBackSort = (LPTVSORTCB)callback;
     2385
     2386    TREEVIEW_ITEM *first = (TREEVIEW_ITEM *)pvFirst;
     2387    TREEVIEW_ITEM *second = (TREEVIEW_ITEM *)pvSecond;
     2388
     2389    /* Forward the call to the client-defined callback */
     2390    return pCallBackSort->lpfnCompare(first->lParam,
     2391                                      second->lParam,
     2392                                      pCallBackSort->lParam);
    20182393}
    20192394
     
    20212396 * Treeview native sort routine: sort on item text.
    20222397 */
    2023 static INT WINAPI TREEVIEW_SortOnName(LPVOID first,LPVOID second,LPARAM tvInfoPtr)
    2024 {
    2025   HWND hwnd = (HWND)tvInfoPtr;
    2026   WCHAR *txt1,*txt2;
    2027   TREEVIEW_ITEM *item;
    2028   INT res;
    2029   BOOL mustFree1 = FALSE,mustFree2 = FALSE;
    2030 
    2031   item = (TREEVIEW_ITEM*)first;
    2032   if (item->pszText == LPSTR_TEXTCALLBACKW)
    2033     txt1 = TREEVIEW_CallbackText(hwnd,item,&mustFree1);
    2034   else
    2035     txt1 = item->pszText;
    2036 
    2037   item = (TREEVIEW_ITEM*)second;
    2038   if (item->pszText == LPSTR_TEXTCALLBACKW)
    2039     txt2 = TREEVIEW_CallbackText(hwnd,item,&mustFree2);
    2040   else
    2041     txt2 = item->pszText;
    2042 
    2043   res = lstrcmpiW(txt1,txt2); //CB: or lstrcmpW?
    2044   if (mustFree1) COMCTL32_Free(txt1);
    2045   if (mustFree2) COMCTL32_Free(txt2);
    2046 
    2047   return res;
     2398static INT WINAPI
     2399TREEVIEW_SortOnName(LPVOID pvFirst, LPVOID pvSecond, LPARAM tvInfoPtr)
     2400{
     2401    TREEVIEW_INFO *infoPtr = (TREEVIEW_INFO *)tvInfoPtr;
     2402
     2403    TREEVIEW_ITEM *first = (TREEVIEW_ITEM *)pvFirst;
     2404    TREEVIEW_ITEM *second = (TREEVIEW_ITEM *)pvSecond;
     2405
     2406    TREEVIEW_UpdateDispInfo(infoPtr, first, TVIF_TEXT);
     2407    TREEVIEW_UpdateDispInfo(infoPtr, second, TVIF_TEXT);
     2408
     2409    return lstrcmpiW(first->pszText, second->pszText);
     2410}
     2411
     2412/* Returns the number of physical children belonging to item. */
     2413static INT
     2414TREEVIEW_CountChildren(TREEVIEW_INFO *infoPtr, TREEVIEW_ITEM *item)
     2415{
     2416    INT cChildren = 0;
     2417    HTREEITEM hti;
     2418
     2419    for (hti = item->firstChild; hti != NULL; hti = hti->nextSibling)
     2420        cChildren++;
     2421
     2422    return cChildren;
     2423}
     2424
     2425/* Returns a DPA containing a pointer to each physical child of item in
     2426 * sibling order. If item has no children, an empty DPA is returned. */
     2427static HDPA
     2428TREEVIEW_BuildChildDPA(TREEVIEW_INFO *infoPtr, TREEVIEW_ITEM *item)
     2429{
     2430    HTREEITEM child = item->firstChild;
     2431
     2432    HDPA list = DPA_Create(8);
     2433    if (list == 0) return NULL;
     2434
     2435    for (child = item->firstChild; child != NULL; child = child->nextSibling)
     2436    {
     2437        if (DPA_InsertPtr(list, INT_MAX, child) == -1)
     2438        {
     2439            DPA_Destroy(list);
     2440            return NULL;
     2441        }
     2442    }
     2443
     2444    return list;
    20482445}
    20492446
     
    20592456 */
    20602457
    2061 static LRESULT WINAPI TREEVIEW_Sort(HWND hwnd,BOOL fRecurse,HTREEITEM parent,LPTVSORTCB pSort)
    2062 {
    2063   TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
    2064   TREEVIEW_ITEM *sortMe  = NULL; /* Node for which we sort the children */
    2065   INT            cChildren;
    2066   HTREEITEM      hti;
    2067   BOOL root = FALSE;
    2068 
    2069   /* Obtain the TVSORTBC struct */
    2070   infoPtr->pCallBackSort = pSort;
    2071 
    2072   /* undocumented feature: TVI_ROOT means `sort the whole tree' */
    2073 
    2074   if ((parent == TVI_ROOT) || (parent == 0))
    2075   {
    2076     root = TRUE;
    2077     parent = infoPtr->TopRootItem;
    2078   }
    2079 
    2080   /* Check for a valid handle to the parent item */
    2081   sortMe = TREEVIEW_ValidItem(infoPtr,parent);
    2082   if (!sortMe)
     2458static LRESULT
     2459TREEVIEW_Sort(TREEVIEW_INFO *infoPtr, BOOL fRecurse, HTREEITEM parent,
     2460              LPTVSORTCB pSort)
     2461{
     2462    INT cChildren;
     2463    PFNDPACOMPARE pfnCompare;
     2464    LPARAM lpCompare;
     2465    BOOL root = FALSE;
     2466
     2467    /* undocumented feature: TVI_ROOT means `sort the whole tree' */
     2468    if ((parent == TVI_ROOT) || (parent == 0))
     2469    {
     2470      root = TRUE;
     2471      parent = infoPtr->root;
     2472    }
     2473
     2474    /* Check for a valid handle to the parent item */
     2475    if (!TREEVIEW_ValidItem(infoPtr, parent))
     2476    {
     2477        //ERR("invalid item hParent=%x\n", (INT)parent);
     2478        return FALSE;
     2479    }
     2480
     2481    if (pSort)
     2482    {
     2483        pfnCompare = TREEVIEW_CallBackCompare;
     2484        lpCompare = (LPARAM)pSort;
     2485    }
     2486    else
     2487    {
     2488        pfnCompare = TREEVIEW_SortOnName;
     2489        lpCompare = (LPARAM)infoPtr;
     2490    }
     2491
     2492    cChildren = TREEVIEW_CountChildren(infoPtr,parent);
     2493
     2494    /* Make sure there is something to sort */
     2495    if (cChildren > 1)
     2496    {
     2497        /* TREEVIEW_ITEM rechaining */
     2498        INT count = 0;
     2499        HTREEITEM item = 0;
     2500        HTREEITEM nextItem = 0;
     2501        HTREEITEM prevItem = 0;
     2502
     2503        HDPA sortList = TREEVIEW_BuildChildDPA(infoPtr, parent);
     2504
     2505        if (sortList == NULL)
     2506            return FALSE;
     2507
     2508        /* let DPA sort the list */
     2509        DPA_Sort(sortList, pfnCompare, lpCompare);
     2510
     2511        /* The order of DPA entries has been changed, so fixup the
     2512         * nextSibling and prevSibling pointers. */
     2513
     2514        item = (HTREEITEM)DPA_GetPtr(sortList, count++);
     2515        while ((nextItem = (HTREEITEM)DPA_GetPtr(sortList, count++)) != NULL)
     2516        {
     2517            /* link the two current item toghether */
     2518            item->nextSibling = nextItem;
     2519            nextItem->prevSibling = item;
     2520
     2521            if (prevItem == NULL)
     2522            {
     2523                /* this is the first item, update the parent */
     2524                parent->firstChild = item;
     2525                item->prevSibling = NULL;
     2526            }
     2527            else
     2528            {
     2529                /* fix the back chaining */
     2530                item->prevSibling = prevItem;
     2531            }
     2532
     2533            /* get ready for the next one */
     2534            prevItem = item;
     2535            item = nextItem;
     2536        }
     2537
     2538        /* the last item is pointed to by item and never has a sibling */
     2539        item->nextSibling = NULL;
     2540        parent->lastChild = item;
     2541
     2542        DPA_Destroy(sortList);
     2543
     2544        TREEVIEW_VerifyTree(infoPtr);
     2545
     2546        TREEVIEW_QueueRefresh(infoPtr);
     2547
     2548        return TRUE;
     2549    }
    20832550    return FALSE;
    2084 
    2085   cChildren = 0;
    2086   for(hti = root ? sortMe->hItem:sortMe->firstChild; hti; hti = infoPtr->items[(INT)hti].sibling)
    2087       cChildren++;
    2088 
    2089   /* Make sure there is something to sort */
    2090   if (cChildren > 1)
    2091   {
    2092     /* pointer organization */
    2093     HDPA          sortList   = DPA_Create(cChildren);
    2094     HTREEITEM     itemHandle = root ? sortMe->hItem:sortMe->firstChild;
    2095     TREEVIEW_ITEM *itemPtr   = &infoPtr->items[(INT)itemHandle];
    2096 
    2097     /* TREEVIEW_ITEM rechaining */
    2098     INT  count     = 0;
    2099     VOID *item     = 0;
    2100     VOID *nextItem = 0;
    2101     VOID *prevItem = 0;
    2102 
    2103     /* Build the list of item to sort */
    2104     do
    2105     {
    2106       itemPtr    = &infoPtr->items[(INT)itemHandle];
    2107       DPA_InsertPtr(
    2108         sortList,      /* the list */
    2109         cChildren+1,   /* force the insertion to be an append */
    2110         itemPtr);      /* the ptr to store */
    2111 
    2112       /* Get the next sibling */
    2113       itemHandle = itemPtr->sibling;
    2114     } while (itemHandle != NULL);
    2115 
    2116     /* let DPA perform the sort activity */
    2117         if (pSort)
    2118         DPA_Sort(
    2119                 sortList,                  /* what  */
    2120                 TREEVIEW_CallBackCompare,  /* how   */
    2121                 hwnd);                     /* owner */
    2122         else
    2123                 DPA_Sort (
    2124                         sortList,            /* what  */
    2125                         TREEVIEW_SortOnName, /* how   */
    2126                         hwnd);               /* owner */
    2127 
    2128     /*
    2129      * Reorganized TREEVIEW_ITEM structures.
    2130      * Note that we know we have at least two elements.
    2131      */
    2132 
    2133     /* Get the first item and get ready to start... */
    2134     item = DPA_GetPtr(sortList, count++);
    2135     while ((nextItem = DPA_GetPtr(sortList, count++)) != NULL)
    2136     {
    2137       /* link the two current item toghether */
    2138       ((TREEVIEW_ITEM*)item)->sibling       = ((TREEVIEW_ITEM*)nextItem)->hItem;
    2139       ((TREEVIEW_ITEM*)nextItem)->upsibling = ((TREEVIEW_ITEM*)item)->hItem;
    2140 
    2141       if (prevItem == NULL) /* this is the first item, update the parent */
    2142       {
    2143         if (root)
    2144           infoPtr->TopRootItem = ((TREEVIEW_ITEM*)item)->hItem;
    2145         else
    2146           sortMe->firstChild = ((TREEVIEW_ITEM*)item)->hItem;
    2147         ((TREEVIEW_ITEM*)item)->upsibling = NULL;
    2148       }
    2149       else                  /* fix the back chaining */
    2150       {
    2151         ((TREEVIEW_ITEM*)item)->upsibling = ((TREEVIEW_ITEM*)prevItem)->hItem;
    2152       }
    2153 
    2154       /* get ready for the next one */
    2155       prevItem = item;
    2156       item     = nextItem;
    2157     }
    2158 
    2159     /* the last item is pointed to by item and never has a sibling */
    2160     ((TREEVIEW_ITEM*)item)->sibling = NULL;
    2161 
    2162     DPA_Destroy(sortList);
    2163     TREEVIEW_QueueRefresh(hwnd);
    2164 
    2165     return TRUE;
    2166   }
    2167   return FALSE;
    21682551}
    21692552
     
    21722555 * and sort the children of the TV item specified in lParam
    21732556 */
    2174 static LRESULT WINAPI TREEVIEW_SortChildrenCB(HWND hwnd,WPARAM wParam,LPARAM lParam)
    2175 {
    2176   LPTVSORTCB pSort = (LPTVSORTCB) lParam;
    2177 
    2178   return TREEVIEW_Sort (hwnd, wParam, pSort->hParent, pSort);
     2557static LRESULT
     2558TREEVIEW_SortChildrenCB(TREEVIEW_INFO *infoPtr, WPARAM wParam, LPARAM lParam)
     2559{
     2560    LPTVSORTCB pSort = (LPTVSORTCB)lParam;
     2561
     2562    return TREEVIEW_Sort(infoPtr, wParam, pSort->hParent, pSort);
    21792563}
    21802564
     
    21832567 * Sort the children of the TV item specified in lParam.
    21842568 */
    2185 static LRESULT WINAPI TREEVIEW_SortChildren(HWND hwnd,WPARAM wParam,LPARAM lParam)
    2186 {
    2187   return TREEVIEW_Sort (hwnd, (BOOL) wParam, (HTREEITEM) lParam, NULL);
    2188 }
    2189 
    2190 
    2191 int ffs(int mask)
    2192 {
    2193         int bit;
    2194 
    2195         if (mask == 0)
    2196                 return(0);
    2197         for (bit = 1; !(mask & 1); bit++)
    2198                 mask >>= 1;
    2199         return(bit);
    2200 }
    2201 
    2202 /* the method used below isn't the most memory-friendly, but it avoids
    2203    a lot of memory reallocations */
    2204 
    2205 /* BTW: we waste handle 0; 0 is not an allowed handle. */
    2206 
    2207 static LRESULT
    2208 TREEVIEW_InsertItem(HWND hwnd,WPARAM wParam,LPARAM lParam,BOOL unicode)
    2209 {
    2210   TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
    2211   TVINSERTSTRUCTW *ptdi;
    2212   TVITEMEXW     *tvItem;
    2213   TREEVIEW_ITEM *wineItem, *parentItem, *prevsib, *sibItem;
    2214   INT           iItem,i,len;
    2215 
    2216   if (infoPtr->hwndEdit) SetFocus(hwnd);
    2217 
    2218   /* Item to insert */
    2219   ptdi = (LPTVINSERTSTRUCTW)lParam;
    2220 
    2221   /* check if memory is available */
    2222 
    2223   if (infoPtr->uNumPtrsAlloced==0) {
    2224         infoPtr->items = (TREEVIEW_ITEM*)COMCTL32_Alloc (TVITEM_ALLOC*sizeof (TREEVIEW_ITEM));
    2225         infoPtr->freeList= (INT*)COMCTL32_Alloc (((TVITEM_ALLOC>>5)) * sizeof (INT));
    2226         infoPtr->uNumPtrsAlloced=TVITEM_ALLOC;
    2227         infoPtr->TopRootItem=(HTREEITEM)1;
    2228    }
    2229 
    2230   /*
    2231    * Reallocate contiguous space for items
    2232    */
    2233   if (infoPtr->uNumItems == (infoPtr->uNumPtrsAlloced-1) ) {
    2234         TREEVIEW_ITEM *oldItems = infoPtr->items;
    2235         INT *oldfreeList = infoPtr->freeList;
    2236 
    2237         infoPtr->uNumPtrsAlloced*=2;
    2238     infoPtr->items = (TREEVIEW_ITEM*)COMCTL32_Alloc (infoPtr->uNumPtrsAlloced*sizeof (TREEVIEW_ITEM));
    2239     infoPtr->freeList= (INT*)COMCTL32_Alloc (((infoPtr->uNumPtrsAlloced>>5))*sizeof (INT));
    2240 
    2241     memcpy (&infoPtr->items[0], &oldItems[0],
    2242                     infoPtr->uNumPtrsAlloced/2 * sizeof(TREEVIEW_ITEM));
    2243     memcpy (&infoPtr->freeList[0], &oldfreeList[0],
    2244                     (infoPtr->uNumPtrsAlloced>>6) * sizeof(INT));
    2245 
    2246     COMCTL32_Free (oldItems);
    2247     COMCTL32_Free (oldfreeList);
    2248    }
    2249 
    2250   /*
    2251    * Reset infoPtr structure with new stat according to current TV picture
    2252    */
    2253   iItem=0;
    2254   infoPtr->uNumItems++;
    2255   if ((INT)infoPtr->uMaxHandle==(infoPtr->uNumItems-1))  {
    2256         iItem=infoPtr->uNumItems;
    2257         infoPtr->uMaxHandle = (HTREEITEM)((INT)infoPtr->uMaxHandle + 1);
    2258   } else {                                       /* check freelist */
    2259         for (i=0; i<infoPtr->uNumPtrsAlloced>>5; i++) {
    2260                 if (infoPtr->freeList[i]) {
    2261                         for(iItem = 0; iItem < 32; iItem++)
    2262                         {
    2263                            if(tv_test_bit(iItem, &infoPtr->freeList[i]))
    2264                               break;
    2265                         }
    2266                         tv_clear_bit(iItem,&infoPtr->freeList[i]);
    2267                         iItem+=i<<5;
    2268                         break;
    2269                 }
    2270     }
    2271   }
    2272 
    2273   /*
    2274    * Find the parent item of the new item
    2275    */
    2276   tvItem = &ptdi->itemex;
    2277   wineItem = &infoPtr->items[iItem];
    2278 
    2279   if ((ptdi->hParent==TVI_ROOT) || (ptdi->hParent==0)) {
    2280     parentItem       = NULL;
    2281     wineItem->parent = 0;
    2282     sibItem          = &infoPtr->items [(INT)infoPtr->TopRootItem];
    2283   }
    2284   else  {
    2285         parentItem = &infoPtr->items[(INT)ptdi->hParent];
    2286 
    2287     /* Do the insertion here it if it's the only item of this parent */
    2288         if (!parentItem->firstChild)
    2289                 parentItem->firstChild=(HTREEITEM)iItem;
    2290 
    2291         wineItem->parent = ptdi->hParent;
    2292         sibItem          = &infoPtr->items [(INT)parentItem->firstChild];
    2293   }
    2294 
    2295 
    2296   /* NOTE: I am moving some setup of the wineItem object that was initialy
    2297    *       done at the end of the function since some of the values are
    2298    *       required by the Callback sorting
    2299    */
    2300 
    2301   if (tvItem->mask & TVIF_TEXT)
    2302   {
    2303     /*
    2304      * Setup the item text stuff here since it's required by the Sort method
    2305      * when the insertion are ordered
    2306      */
    2307     if (unicode)
    2308     {
    2309       if (tvItem->pszText != LPSTR_TEXTCALLBACKW)
     2569static LRESULT
     2570TREEVIEW_SortChildren(TREEVIEW_INFO *infoPtr, WPARAM wParam, LPARAM lParam)
     2571{
     2572    return TREEVIEW_Sort(infoPtr, (BOOL)wParam, (HTREEITEM)lParam, NULL);
     2573}
     2574
     2575
     2576/* Expansion/Collapse ***************************************************/
     2577
     2578static BOOL
     2579TREEVIEW_SendExpanding(TREEVIEW_INFO *infoPtr, TREEVIEW_ITEM *wineItem,
     2580                       UINT action)
     2581{
     2582    return !TREEVIEW_SendTreeviewNotify(infoPtr,isUnicodeNotify(&infoPtr->header) ? TVN_ITEMEXPANDINGW:TVN_ITEMEXPANDINGA, action,
     2583                                        TVIF_HANDLE | TVIF_STATE | TVIF_PARAM
     2584                                        | TVIF_IMAGE | TVIF_SELECTEDIMAGE,
     2585                                        0, wineItem);
     2586}
     2587
     2588static VOID
     2589TREEVIEW_SendExpanded(TREEVIEW_INFO *infoPtr, TREEVIEW_ITEM *wineItem,
     2590                      UINT action)
     2591{
     2592    TREEVIEW_SendTreeviewNotify(infoPtr,isUnicodeNotify(&infoPtr->header) ? TVN_ITEMEXPANDEDW:TVN_ITEMEXPANDEDA, action,
     2593                                TVIF_HANDLE | TVIF_STATE | TVIF_PARAM
     2594                                | TVIF_IMAGE | TVIF_SELECTEDIMAGE,
     2595                                0, wineItem);
     2596}
     2597
     2598
     2599/* This corresponds to TVM_EXPAND with TVE_COLLAPSE.
     2600 * bRemoveChildren corresponds to TVE_COLLAPSERESET. */
     2601static BOOL
     2602TREEVIEW_Collapse(TREEVIEW_INFO *infoPtr, TREEVIEW_ITEM *wineItem,
     2603                  BOOL bRemoveChildren, BOOL bUser)
     2604{
     2605    UINT action = TVE_COLLAPSE | (bRemoveChildren ? TVE_COLLAPSERESET : 0);
     2606    BOOL bSetSelection, bSetFirstVisible;
     2607
     2608    //TRACE("TVE_COLLAPSE %p %s\n", wineItem, TREEVIEW_ItemName(wineItem));
     2609
     2610    if (!(wineItem->state & TVIS_EXPANDED) || wineItem->firstChild == NULL)
     2611        return FALSE;
     2612
     2613    if (bUser)
     2614        TREEVIEW_SendExpanding(infoPtr, wineItem, action);
     2615
     2616    wineItem->state &= ~TVIS_EXPANDED;
     2617
     2618    if (bUser)
     2619        TREEVIEW_SendExpanded(infoPtr, wineItem, action);
     2620
     2621    bSetSelection = (infoPtr->selectedItem != NULL
     2622                     && TREEVIEW_IsChildOf(wineItem, infoPtr->selectedItem));
     2623
     2624    if (TREEVIEW_IsChildOf(wineItem,infoPtr->firstVisible))
     2625    {
     2626      infoPtr->lefttop.y += wineItem->rect.top;
     2627    }
     2628
     2629    if (bRemoveChildren)
     2630    {
     2631        //TRACE("TVE_COLLAPSERESET\n");
     2632        wineItem->state &= ~TVIS_EXPANDEDONCE;
     2633        TREEVIEW_RemoveAllChildren(infoPtr, wineItem);
     2634    }
     2635
     2636    //update window
     2637    //CB: todo: optimize!
     2638    TREEVIEW_UnqueueRefresh(infoPtr,FALSE,FALSE);
     2639    //CB: todo: precalc expanded items here
     2640    TREEVIEW_CalcItems(infoPtr);
     2641    TREEVIEW_Refresh(infoPtr);
     2642
     2643    if (bSetSelection)
     2644    {
     2645        /* Don't call DoSelectItem, it sends notifications. */
     2646        if (infoPtr->selectedItem)
     2647            infoPtr->selectedItem->state &= ~TVIS_SELECTED;
     2648        wineItem->state |= TVIS_SELECTED;
     2649        infoPtr->selectedItem = wineItem;
     2650
     2651        TREEVIEW_EnsureVisible(infoPtr, wineItem);
     2652    }
     2653
     2654    return TRUE;
     2655}
     2656
     2657static BOOL
     2658TREEVIEW_Expand(TREEVIEW_INFO *infoPtr, TREEVIEW_ITEM *wineItem,
     2659                BOOL bExpandPartial, BOOL bUser)
     2660{
     2661    //TRACE("TVE_EXPAND\n");
     2662
     2663    if (!TREEVIEW_HasChildren(infoPtr, wineItem)
     2664        || (wineItem->state & TVIS_EXPANDED))
     2665        return FALSE;
     2666
     2667    //TRACE("  is not expanded...\n");
     2668
     2669    if (bUser || !(wineItem->state & TVIS_EXPANDEDONCE))
     2670    {
     2671        //TRACE("  and has never been expanded...\n");
     2672
     2673        if (bExpandPartial)
     2674        {
     2675return FALSE; //CB: how does this work??? (only display one level? nonsense in MSDN docu)
     2676          wineItem->state ^=TVIS_EXPANDED;
     2677          wineItem->state |=TVIS_EXPANDEDONCE;
     2678        }
     2679
     2680        wineItem->state |= TVIS_EXPANDED;
     2681        //TRACE("  TVN_ITEMEXPANDING sent...\n");
     2682
     2683        if (!TREEVIEW_SendExpanding(infoPtr, wineItem, TVE_EXPAND))
     2684        {
     2685            //TRACE("  TVN_ITEMEXPANDING returned TRUE, exiting...\n");
     2686            return FALSE;
     2687        }
     2688
     2689        wineItem->state |= TVIS_EXPANDEDONCE;
     2690
     2691        TREEVIEW_SendExpanded(infoPtr, wineItem, TVE_EXPAND);
     2692
     2693        //TRACE("  TVN_ITEMEXPANDED sent...\n");
     2694    }
     2695    else
     2696    {
     2697        /* this item has already been expanded */
     2698        wineItem->state |= TVIS_EXPANDED;
     2699    }
     2700
     2701    //if (bExpandPartial)
     2702    //  FIXME("TVE_EXPANDPARTIAL not implemented\n");
     2703
     2704    //update window
     2705    //CB: todo: optimize!
     2706    TREEVIEW_UnqueueRefresh(infoPtr,TRUE,FALSE);
     2707
     2708    /* Scroll up so that as many children as possible are visible.
     2709     * This looses when expanding causes an HScroll bar to appear, but we
     2710     * don't know that yet, so the last item is obscured. */
     2711    if (wineItem->firstChild && wineItem->inclient && (infoPtr->firstVisible != wineItem) && (wineItem->lastChild->rect.bottom > infoPtr->clientHeight))
     2712    {
     2713      INT childrenH = wineItem->lastChild->rect.bottom-wineItem->firstChild->rect.top;
     2714      INT itemH = wineItem->rect.bottom-wineItem->rect.top;
     2715      INT clientH = ((INT)(infoPtr->clientHeight/infoPtr->uItemHeight))*infoPtr->uItemHeight;
     2716
     2717      if (itemH+childrenH > clientH)
    23102718      {
    2311         len = lstrlenW(tvItem->pszText)+1;
    2312         wineItem->pszText = (WCHAR*)COMCTL32_Alloc(len*sizeof(WCHAR));
    2313         lstrcpyW (wineItem->pszText, tvItem->pszText);
    2314         wineItem->cchTextMax = len;
     2719        infoPtr->lefttop.y += wineItem->rect.top;
    23152720      } else
    23162721      {
    2317         wineItem->pszText = LPSTR_TEXTCALLBACKW;
    2318         wineItem->cchTextMax = 0;
     2722        INT diff = wineItem->lastChild->rect.bottom-clientH;
     2723
     2724        infoPtr->lefttop.y += diff;
    23192725      }
    2320     } else
    2321     {
    2322       if ((LPSTR)tvItem->pszText != LPSTR_TEXTCALLBACKA)
    2323       {
    2324         len = lstrlenA((LPSTR)tvItem->pszText)+1;
    2325         wineItem->pszText = (WCHAR*)COMCTL32_Alloc(len*sizeof(WCHAR));
    2326         lstrcpyAtoW (wineItem->pszText,(LPSTR)tvItem->pszText);
    2327         wineItem->cchTextMax = len;
    2328       } else
    2329       {
    2330         wineItem->pszText = LPSTR_TEXTCALLBACKW;
    2331         wineItem->cchTextMax = 0;
    2332       }
    2333     }
    2334   }
    2335 
    2336   if (tvItem->mask & TVIF_PARAM)
    2337     wineItem->lParam=tvItem->lParam;
    2338 
    2339 
    2340   wineItem->upsibling=0;  /* needed in case we're the first item in a list */
    2341   wineItem->sibling=0;
    2342   wineItem->firstChild=0;
    2343   wineItem->hItem=(HTREEITEM)iItem;
    2344 
    2345   if (sibItem != wineItem)
    2346   {
    2347     prevsib=NULL;
    2348 
    2349     if (ptdi->hInsertAfter == TVI_FIRST)
    2350     {
    2351       if (wineItem->parent) {
    2352         wineItem->sibling=parentItem->firstChild;
    2353         parentItem->firstChild=(HTREEITEM)iItem;
    2354       } else {
    2355         wineItem->sibling=infoPtr->TopRootItem;
    2356         infoPtr->TopRootItem=(HTREEITEM)iItem;
    2357       }
    2358       sibItem->upsibling=(HTREEITEM)iItem;
    2359     } else if (ptdi->hInsertAfter == TVI_SORT)
    2360     {
    2361       TREEVIEW_ITEM *aChild;
    2362       TREEVIEW_ITEM *previousChild = NULL;
    2363       BOOL bItemInserted           = FALSE;
    2364       WCHAR* text;
    2365       BOOL mustFree = FALSE;
    2366 
    2367       if (parentItem)
    2368         aChild = &infoPtr->items[(INT)parentItem->firstChild];
    2369       else
    2370         aChild = &infoPtr->items[(INT)infoPtr->TopRootItem];
    2371 
    2372       /* lookup the text if using LPSTR_TEXTCALLBACKs */
    2373       if (wineItem->pszText == LPSTR_TEXTCALLBACKW)
    2374         text = TREEVIEW_CallbackText(hwnd,wineItem,&mustFree);
    2375       else
    2376         text = wineItem->pszText;
    2377 
    2378       /* Iterate the parent children to see where we fit in */
    2379       while ( aChild != NULL )
    2380       {
    2381         INT comp;
    2382         WCHAR* text2;
    2383         BOOL mustFree2 = FALSE;
    2384 
    2385         /* lookup the text if using LPSTR_TEXTCALLBACKs */
    2386         if (aChild->pszText == LPSTR_TEXTCALLBACKW)
    2387           text2 = TREEVIEW_CallbackText(hwnd,aChild,&mustFree2);
    2388         else
    2389           text2 = aChild->pszText;
    2390 
    2391         comp = lstrcmpW(text,text2);
    2392         if (mustFree2) COMCTL32_Free(text2);
    2393         if ( comp < 0 )  /* we are smaller than the current one */
    2394         {
    2395           TREEVIEW_InsertBefore(infoPtr, wineItem, aChild, parentItem);
    2396           bItemInserted = TRUE;
    2397           break;
    2398         }
    2399         else if ( comp > 0 )  /* we are bigger than the current one */
    2400         {
    2401           previousChild = aChild;
    2402           aChild = (aChild->sibling == 0)  /* This will help us to exit   */
    2403                       ? NULL               /* if there is no more sibling */
    2404                       : &infoPtr->items[(INT)aChild->sibling];
    2405 
    2406           /* Look at the next item */
    2407           continue;
    2408         }
    2409         else if ( comp == 0 )
    2410         {
    2411           /*
    2412            * An item with this name is already existing, therefore,
    2413            * we add after the one we found
    2414            */
    2415           TREEVIEW_InsertAfter(infoPtr, wineItem, aChild, parentItem);
    2416           bItemInserted = TRUE;
    2417           break;
    2418         }
    2419       }
    2420       if (mustFree) COMCTL32_Free(text);
    2421 
    2422       /*
    2423        * we reach the end of the child list and the item as not
    2424        * yet been inserted, therefore, insert it after the last child.
    2425        */
    2426       if ( (! bItemInserted ) && (aChild == NULL) )
    2427         TREEVIEW_InsertAfter(infoPtr, wineItem, previousChild, parentItem);
    2428     } else if (ptdi->hInsertAfter == TVI_LAST)
    2429     {
    2430 TVI_LAST_CASE:
    2431       while (sibItem->sibling) {
    2432         prevsib=sibItem;
    2433         sibItem=&infoPtr->items [(INT)sibItem->sibling];
    2434       }
    2435       sibItem->sibling=(HTREEITEM)iItem;
    2436       wineItem->upsibling=sibItem->hItem;
    2437     } else
    2438     {
    2439       while ((sibItem->sibling) && (sibItem->hItem!=ptdi->hInsertAfter))
    2440       {
    2441         prevsib=sibItem;
    2442         sibItem=&infoPtr->items [(INT)sibItem->sibling];
    2443       }
    2444       if (sibItem->hItem!=ptdi->hInsertAfter) {
    2445         goto TVI_LAST_CASE;
    2446       }
    2447       prevsib=sibItem;
    2448       if (sibItem->sibling) {
    2449         sibItem=&infoPtr->items [(INT)sibItem->sibling];
    2450         sibItem->upsibling=(HTREEITEM)iItem;
    2451         wineItem->sibling=sibItem->hItem;
    2452       }
    2453       prevsib->sibling=(HTREEITEM)iItem;
    2454       wineItem->upsibling=prevsib->hItem;
    2455     }
    2456   }
    2457 
    2458   /* Fill in info structure */
    2459 
    2460   wineItem->mask = tvItem->mask;
    2461   wineItem->iIntegral = 1;
    2462 
    2463   if (tvItem->mask & TVIF_CHILDREN)
    2464     wineItem->cChildren=tvItem->cChildren;
    2465 
    2466   wineItem->expandBox.left   = 0; /* Initialize the expandBox */
    2467   wineItem->expandBox.top    = 0;
    2468   wineItem->expandBox.right  = 0;
    2469   wineItem->expandBox.bottom = 0;
    2470 
    2471   if (tvItem->mask & TVIF_IMAGE)
    2472    wineItem->iImage = tvItem->iImage;
    2473 
    2474   /* If the application sets TVIF_INTEGRAL without
    2475      supplying a TVITEMEX structure, it's toast */
    2476 
    2477   if (tvItem->mask & TVIF_INTEGRAL)
    2478     wineItem->iIntegral = tvItem->iIntegral;
    2479 
    2480   if (tvItem->mask & TVIF_SELECTEDIMAGE)
    2481     wineItem->iSelectedImage = tvItem->iSelectedImage;
    2482 
    2483   if (tvItem->mask & TVIF_STATE)
    2484   {
    2485     wineItem->state = tvItem->state;
    2486     wineItem->stateMask = tvItem->stateMask;
    2487   }
    2488 
    2489   wineItem->calculated = FALSE;
    2490   //TREEVIEW_Sort(hwnd,0,NULL,NULL); //CB: the Corel people do this, I think it's wrong
    2491   TREEVIEW_QueueRefresh(hwnd);
    2492 
    2493   return (LRESULT) iItem;
    2494 }
    2495 
    2496 
    2497 static LRESULT
    2498 TREEVIEW_DeleteItem (HWND hwnd, WPARAM wParam, LPARAM lParam)
    2499 {
    2500   TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
    2501   INT iItem;
    2502   TREEVIEW_ITEM *wineItem;
    2503 
    2504   if (lParam == (INT)TVI_ROOT)
    2505   {
    2506     TREEVIEW_RemoveTree (hwnd);
    2507   } else
    2508   {
    2509     iItem = (INT)lParam;
    2510     wineItem = TREEVIEW_ValidItem (infoPtr, (HTREEITEM)iItem);
    2511     if (!wineItem) return FALSE;
    2512 
    2513     TREEVIEW_RemoveItem (hwnd, wineItem);
    2514   }
    2515 
    2516   TREEVIEW_QueueRefresh(hwnd);
    2517 
    2518   return TRUE;
    2519 }
    2520 
    2521 
    2522 
    2523 static LRESULT
    2524 TREEVIEW_GetIndent (HWND hwnd)
    2525 {
    2526   TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
    2527 
    2528   return infoPtr->uIndent;
    2529 }
    2530 
    2531 static LRESULT
    2532 TREEVIEW_SetIndent (HWND hwnd, WPARAM wParam)
    2533 {
    2534   TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
    2535   INT newIndent;
    2536 
    2537   newIndent = (INT)wParam;
    2538   if (newIndent < MINIMUM_INDENT) newIndent = MINIMUM_INDENT;
    2539   if (newIndent != infoPtr->uIndent)
    2540   {
    2541     infoPtr->uIndent = newIndent;
    2542     infoPtr->uInternalStatus |= TV_CALCALL;
    2543     TREEVIEW_QueueRefresh(hwnd);
    2544   }
    2545 
    2546   return 0;
    2547 }
    2548 
    2549 static LRESULT
    2550 TREEVIEW_GetToolTips (HWND hwnd)
    2551 {
    2552   TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
    2553 
    2554   return infoPtr->hwndToolTip;
    2555 }
    2556 
    2557 
    2558 static LRESULT
    2559 TREEVIEW_SetToolTips (HWND hwnd, WPARAM wParam)
    2560 {
    2561   TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
    2562   HWND prevToolTip;
    2563 
    2564   prevToolTip = infoPtr->hwndToolTip;
    2565   infoPtr->hwndToolTip = (HWND)wParam;
    2566 
    2567   return prevToolTip;
    2568 }
    2569 
    2570 
    2571 static LRESULT CALLBACK
    2572 TREEVIEW_GetEditControl (HWND hwnd)
    2573 {
    2574   TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
    2575 
    2576   return infoPtr->hwndEdit;
    2577 }
    2578 
    2579 
    2580 //@@@PH: Note - this SubclassProc is sometimes called with the
    2581 //       wrong window handle. Therefore, infoPtr points to anything
    2582 //       but the expected structure.
    2583 LRESULT CALLBACK
    2584 TREEVIEW_Edit_SubclassProc (HWND hwnd, UINT uMsg, WPARAM wParam,
    2585                                                         LPARAM lParam)
    2586 {
    2587   BOOL bCancel = FALSE;
    2588   static BOOL bIgnoreKillFocus = FALSE;
    2589 
    2590   switch (uMsg)
    2591   {
    2592     case WM_PAINT:
    2593         {
    2594           LRESULT rc;
    2595           TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(GetParent(hwnd));
    2596           //TRACE("WM_PAINT start\n");
    2597           rc = CallWindowProcA( infoPtr->wpEditOrig, hwnd, uMsg, wParam, lParam);
    2598           //TRACE("WM_PAINT done\n");
    2599           return rc;
    2600         }
    2601 
    2602     case WM_KILLFOCUS:
    2603         if(bIgnoreKillFocus)
    2604         {
    2605             return TRUE;
    2606         }
    2607         break;
    2608 
    2609     case WM_GETDLGCODE:
    2610       return DLGC_WANTARROWS | DLGC_WANTALLKEYS;
    2611 
    2612     case WM_KEYDOWN:
    2613         if (VK_ESCAPE == (INT)wParam)
    2614         {
    2615             bCancel = TRUE;
    2616             break;
    2617         }
    2618         else if (VK_RETURN == (INT)wParam)
    2619         {
    2620             break;
    2621         }
    2622 
    2623     default:
    2624     {
    2625       TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(GetParent(hwnd));
    2626 
    2627       //@@@PH 1999/11/05 method called with freed infoPtr memory object
    2628       if (infoPtr != NULL)
    2629         return CallWindowProcA( infoPtr->wpEditOrig, hwnd, uMsg, wParam, lParam);
    2630       else
    2631         break;
    2632     }
    2633   }
    2634 
    2635   /* Processing LVN_ENDLABELEDIT message could kill the focus       */
    2636   /* eg. Using a messagebox                                         */
    2637   bIgnoreKillFocus = TRUE;
    2638   TREEVIEW_EndEditLabelNow(GetParent(hwnd), bCancel);
    2639   bIgnoreKillFocus = FALSE;
    2640 
    2641   return 0;
    2642 }
    2643 
    2644 /* should handle edit control messages here */
    2645 
    2646 static LRESULT
    2647 TREEVIEW_Command (HWND hwnd, WPARAM wParam, LPARAM lParam)
    2648 
    2649 {
    2650   switch (HIWORD(wParam))
    2651   {
    2652     case EN_UPDATE:
    2653     {
    2654       /*
    2655        * Adjust the edit window size
    2656        */
    2657       char buffer[1024];
    2658       TREEVIEW_INFO *infoPtr  = TREEVIEW_GetInfoPtr(hwnd);
    2659       TREEVIEW_ITEM *editItem = TREEVIEW_ValidItem(infoPtr, infoPtr->editItem);
    2660       HDC           hdc       = GetDC(infoPtr->hwndEdit);
    2661       SIZE          sz;
    2662       int           len;
    2663       HFONT         hFont, hOldFont=0;
    2664 
    2665       len = GetWindowTextA(infoPtr->hwndEdit, buffer, 1024);
    2666 
    2667       /* Select font to get the right dimension of the string */
    2668       hFont = SendMessageA(infoPtr->hwndEdit, WM_GETFONT, 0, 0);
    2669       if(hFont != 0)
    2670       {
    2671           hOldFont = SelectObject(hdc, hFont);
    2672       }
    2673 
    2674       if (GetTextExtentPoint32A(hdc, buffer, strlen(buffer), &sz))
    2675       {
    2676           TEXTMETRICA textMetric;
    2677           /* Add Extra spacing for the next character */
    2678           GetTextMetricsA(hdc, &textMetric);
    2679           sz.cx += (textMetric.tmMaxCharWidth * 2);
    2680 
    2681           SetWindowPos (
    2682             infoPtr->hwndEdit,
    2683             HWND_TOP,
    2684             0,
    2685             0,
    2686             sz.cx,
    2687             editItem->text.bottom - editItem->text.top + 3,
    2688             SWP_NOMOVE|SWP_DRAWFRAME);
    2689       }
    2690 
    2691       if(hFont != 0)
    2692       {
    2693           SelectObject(hdc, hOldFont);
    2694       }
    2695 
    2696       ReleaseDC(hwnd, hdc);
    2697       break;
    2698     }
    2699 
    2700     default:
    2701       return SendMessageA (GetParent (hwnd), WM_COMMAND, wParam, lParam);
    2702   }
    2703 
    2704   return 0;
    2705 }
    2706 
    2707 static LRESULT TREEVIEW_Size (HWND hwnd, WPARAM wParam, LPARAM lParam)
    2708 {
    2709   TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
    2710 
    2711   if (wParam == SIZE_RESTORED)
    2712     if (TREEVIEW_CalcItems(hwnd,0,infoPtr))
    2713       TREEVIEW_Refresh(hwnd);
    2714 
    2715   return TRUE;
    2716 }
    2717 
    2718 static LRESULT
    2719 TREEVIEW_StyleChanged (HWND hwnd, WPARAM wParam, LPARAM lParam)
    2720 {
    2721   TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
    2722 
    2723   if (infoPtr->hwndEdit) SetFocus(hwnd);
    2724 
    2725   infoPtr->uInternalStatus |= TV_CALCALL;
    2726   TREEVIEW_QueueRefresh(hwnd);
    2727 
    2728   return 0;
    2729 }
    2730 
    2731 static LRESULT
    2732 TREEVIEW_SysColorChange(HWND hwnd,WPARAM wParam,LPARAM lParam)
    2733 {
    2734   TREEVIEW_Refresh(hwnd);
    2735 
    2736   return DefWindowProcA(hwnd,WM_SYSCOLORCHANGE,wParam,lParam);
    2737 }
    2738 
    2739 static LRESULT
    2740 TREEVIEW_Create (HWND hwnd, WPARAM wParam, LPARAM lParam)
    2741 {
    2742   LPCREATESTRUCTA lpcs = (LPCREATESTRUCTA)lParam;
    2743   TREEVIEW_INFO *infoPtr;
    2744   DWORD dwStyle = GetWindowLongA (hwnd, GWL_STYLE);
    2745   LOGFONTA logFont;
    2746   TEXTMETRICA tm;
    2747   HDC hdc;
    2748 
    2749   /* allocate memory for info structure */
    2750   infoPtr = (TREEVIEW_INFO*)initControl(hwnd,sizeof(TREEVIEW_INFO));
    2751 
    2752   if (!infoPtr) return 0;
    2753 
    2754   hdc = GetDC(hwnd);
    2755 
    2756   /* set default settings */
    2757   infoPtr->uInternalStatus = TV_CALCALL;
    2758   infoPtr->uNumItems = 0;
    2759   infoPtr->clrBk   = GetSysColor (COLOR_WINDOW);
    2760   infoPtr->clrLine = GetSysColor (COLOR_WINDOWTEXT);
    2761   infoPtr->clrInsertMark = GetSysColor (COLOR_BTNTEXT);
    2762   infoPtr->lefttop.y = 0;
    2763   infoPtr->lefttop.x = 0;
    2764   infoPtr->uIndent = 15;
    2765   infoPtr->himlNormal = NULL;
    2766   infoPtr->himlState = NULL;
    2767   infoPtr->uItemHeight = -1;
    2768   infoPtr->uRealItemHeight = 0;
    2769   infoPtr->uVScrollStep = 1;
    2770   infoPtr->uVisibleHeight = lpcs->cy;
    2771   infoPtr->uTotalHeight = 0;
    2772   infoPtr->uVisibleWidth = lpcs->cx;
    2773   infoPtr->uTotalWidth = 0;
    2774 
    2775   GetTextMetricsA (hdc, &tm);
    2776   infoPtr->hFont = GetStockObject (DEFAULT_GUI_FONT);
    2777   GetObjectA (infoPtr->hFont, sizeof (LOGFONTA), &logFont);
    2778   logFont.lfWeight = FW_BOLD;
    2779   infoPtr->hBoldFont = CreateFontIndirectA (&logFont);
    2780 
    2781   infoPtr->items = NULL;
    2782   infoPtr->selectedItem = 0;
    2783   infoPtr->clrText = -1;        /* use system color */
    2784   infoPtr->dropItem = 0;
    2785   infoPtr->insertMarkItem = 0;
    2786   infoPtr->insertBeforeorAfter = 0;
    2787   infoPtr->pCallBackSort = NULL;
    2788   infoPtr->uScrollTime = 300;  /* milliseconds */
    2789   infoPtr->hwndEdit = 0;
    2790   infoPtr->pszISearch = NULL;
    2791   infoPtr->uISearchLen = 0;
    2792 
    2793   infoPtr->tipItem = 0;
    2794   infoPtr->hwndToolTip = 0;
    2795   if (!(dwStyle & TVS_NOTOOLTIPS))
    2796   {
    2797     infoPtr->hwndToolTip = createToolTip(hwnd,TTF_TRACK | TTF_ABSOLUTE | TTF_TRANSPARENT,TRUE);
    2798     SendMessageA(infoPtr->hwndToolTip,WM_SETFONT,infoPtr->hFont,0);
    2799   }
    2800 
    2801   if (dwStyle & TVS_CHECKBOXES)
    2802   {
    2803     HBITMAP hbmLoad;
    2804     int nIndex;
    2805 
    2806     infoPtr->himlState = ImageList_Create (16, 16,ILC_COLOR | ILC_MASK, 15, 1);
    2807 
    2808     //MSDN docu says: uses DrawFrameControl but never believe what they write
    2809     hbmLoad = LoadBitmapA (COMCTL32_hModule, MAKEINTRESOURCEA(IDT_CHECK));
    2810     nIndex = ImageList_AddMasked (infoPtr->himlState, hbmLoad, CLR_DEFAULT);
    2811     DeleteObject (hbmLoad);
    2812   }
    2813   ReleaseDC (hwnd, hdc);
    2814 
    2815   return 0;
    2816 }
    2817 
    2818 static LRESULT
    2819 TREEVIEW_Destroy (HWND hwnd)
    2820 {
    2821    TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
    2822 
    2823    TREEVIEW_RemoveTree (hwnd);
    2824    if (infoPtr->Timer & TV_REFRESH_TIMER_SET)
    2825      KillTimer (hwnd, TV_REFRESH_TIMER);
    2826    destroyToolTip(infoPtr->hwndToolTip);
    2827 
    2828    /* Restore original windproc so that we can free infoPtr */
    2829    if (infoPtr->hwndEdit)
    2830      SetWindowLongA (infoPtr->hwndEdit, GWL_WNDPROC, (DWORD)infoPtr->wpEditOrig);
    2831 
    2832    DeleteObject(infoPtr->hBoldFont);
    2833    COMCTL32_Free(infoPtr->pszISearch);
    2834    doneControl(hwnd);
    2835 
    2836    return 0;
    2837 }
    2838 
    2839 
    2840 static LRESULT
    2841 TREEVIEW_Paint (HWND hwnd, WPARAM wParam, LPARAM lParam)
    2842 {
    2843   HDC hdc = (HDC)wParam;
    2844 
    2845   if (!hdc)
    2846   {
    2847     PAINTSTRUCT ps;
    2848 
    2849     hdc = BeginPaint(hwnd,&ps);
    2850     TREEVIEW_Draw(hwnd, hdc,&ps.rcPaint);
    2851     EndPaint(hwnd,&ps);
    2852   } else
    2853       TREEVIEW_Draw(hwnd,hdc,NULL);
    2854 
    2855   return 0;
    2856 }
    2857 
    2858 static LRESULT
    2859 TREEVIEW_SetFocus (HWND hwnd, WPARAM wParam, LPARAM lParam)
    2860 {
    2861   TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
    2862   DWORD dwStyle = GetWindowLongA(hwnd,GWL_STYLE);
    2863 
    2864   sendNotify(hwnd,NM_SETFOCUS);
    2865 
    2866   if (!(dwStyle & TVS_SHOWSELALWAYS))
    2867   {
    2868     if (infoPtr->selectedItem)
    2869       TREEVIEW_RefreshItem(hwnd,TREEVIEW_ValidItem(infoPtr,infoPtr->selectedItem),FALSE);
    2870     else if (infoPtr->firstVisible)
    2871       TREEVIEW_DoSelectItem(hwnd,TVGN_CARET,infoPtr->firstVisible,TVC_UNKNOWN);
    2872   }
    2873 
    2874   return 0;
    2875 }
    2876 
    2877 static LRESULT
    2878 TREEVIEW_KillFocus (HWND hwnd, WPARAM wParam, LPARAM lParam)
    2879 {
    2880   TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
    2881   DWORD dwStyle = GetWindowLongA(hwnd,GWL_STYLE);
    2882 
    2883   sendNotify(hwnd,NM_KILLFOCUS);
    2884 
    2885   if (!(dwStyle & TVS_SHOWSELALWAYS) && infoPtr->selectedItem)
    2886     TREEVIEW_RefreshItem(hwnd,TREEVIEW_ValidItem(infoPtr,infoPtr->selectedItem),FALSE);
    2887 
    2888   return 0;
    2889 }
    2890 
    2891 static LRESULT TREEVIEW_Enable(HWND hwnd,WPARAM wParam,LPARAM lParam)
    2892 {
    2893   TREEVIEW_Refresh(hwnd);
    2894 
    2895   return DefWindowProcA(hwnd,WM_ENABLE,wParam,lParam);
    2896 }
    2897 
    2898 static LRESULT
    2899 TREEVIEW_EraseBackground (HWND hwnd, WPARAM wParam, LPARAM lParam)
    2900 {
    2901     TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
    2902     HBRUSH hBrush = CreateSolidBrush (infoPtr->clrBk);
    2903     RECT rect;
    2904 
    2905     GetClientRect (hwnd, &rect);
    2906     FillRect ((HDC)wParam, &rect, hBrush);
    2907     DeleteObject (hBrush);
     2726    }
     2727
     2728    TREEVIEW_CalcItems(infoPtr);
     2729    TREEVIEW_Refresh(infoPtr);
     2730    //CB: todo: check cx and cy to fit ranges!
    29082731
    29092732    return TRUE;
    29102733}
    29112734
    2912 static LRESULT TREEVIEW_GetDlgCode(HWND hwnd,WPARAM wParam,LPARAM lParam)
    2913 {
    2914   return DLGC_WANTARROWS | DLGC_WANTCHARS;
    2915 }
    2916 
    2917 /* Notifications */
    2918 
    29192735static BOOL
    2920 TREEVIEW_SendTreeviewNotify (HWND hwnd,UINT code,UINT action,HTREEITEM oldItem,HTREEITEM newItem)
    2921 {
    2922   TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
    2923   NMTREEVIEWW nmhdr;
    2924   TREEVIEW_ITEM  *wineItem;
    2925   CHAR *oldText = NULL,*newText = NULL;
    2926   BOOL rc;
    2927 
    2928   ZeroMemory(&nmhdr,sizeof(NMTREEVIEWW));
    2929 
    2930   nmhdr.action = action;
    2931   if (oldItem)
    2932   {
    2933     wineItem = &infoPtr->items[(INT)oldItem];
    2934     nmhdr.itemOld.mask      = wineItem->mask;
    2935     nmhdr.itemOld.hItem     = wineItem->hItem;
    2936     nmhdr.itemOld.state     = wineItem->state;
    2937     nmhdr.itemOld.stateMask = wineItem->stateMask;
    2938     nmhdr.itemOld.iImage    = wineItem->iImage;
    2939     if (!isUnicodeNotify(&infoPtr->header))
    2940     {
    2941       if (!wineItem->pszText || (wineItem->pszText == LPSTR_TEXTCALLBACKW)) nmhdr.itemOld.pszText = NULL; else
    2942       {
    2943         INT len = lstrlenW(wineItem->pszText)+1;
    2944 
    2945         oldText = (CHAR*)COMCTL32_Alloc(len);
    2946         lstrcpyWtoA(oldText,wineItem->pszText);
    2947         nmhdr.itemOld.pszText = (WCHAR*)oldText;
    2948       }
    2949     } else nmhdr.itemOld.pszText   = wineItem->pszText;
    2950     nmhdr.itemOld.cchTextMax= wineItem->cchTextMax;
    2951     nmhdr.itemOld.iImage    = wineItem->iImage;
    2952     nmhdr.itemOld.iSelectedImage    = wineItem->iSelectedImage;
    2953     nmhdr.itemOld.cChildren = wineItem->cChildren;
    2954     nmhdr.itemOld.lParam    = wineItem->lParam;
    2955   }
    2956 
    2957   if (newItem)
    2958   {
    2959     wineItem = &infoPtr->items[(INT)newItem];
    2960     nmhdr.itemNew.mask              = wineItem->mask;
    2961     nmhdr.itemNew.hItem             = wineItem->hItem;
    2962     nmhdr.itemNew.state             = wineItem->state;
    2963     nmhdr.itemNew.stateMask = wineItem->stateMask;
    2964     nmhdr.itemNew.iImage    = wineItem->iImage;
    2965     if (!isUnicodeNotify(&infoPtr->header))
    2966     {
    2967       if (!wineItem->pszText || (wineItem->pszText == LPSTR_TEXTCALLBACKW)) nmhdr.itemNew.pszText = NULL; else
    2968       {
    2969         INT len = lstrlenW(wineItem->pszText)+1;
    2970 
    2971         newText = (CHAR*)COMCTL32_Alloc(len);
    2972         lstrcpyWtoA(newText,wineItem->pszText);
    2973         nmhdr.itemNew.pszText = (WCHAR*)newText;
    2974       }
    2975     } else nmhdr.itemNew.pszText   = wineItem->pszText;
    2976     nmhdr.itemNew.cchTextMax= wineItem->cchTextMax;
    2977     nmhdr.itemNew.iImage    = wineItem->iImage;
    2978     nmhdr.itemNew.iSelectedImage    = wineItem->iSelectedImage;
    2979     nmhdr.itemNew.cChildren = wineItem->cChildren;
    2980     nmhdr.itemNew.lParam    = wineItem->lParam;
    2981   }
    2982 
    2983   nmhdr.ptDrag.x = 0;
    2984   nmhdr.ptDrag.y = 0;
    2985 
    2986   rc = (BOOL)sendNotify(hwnd,code,&nmhdr.hdr);
    2987 
    2988   if (oldText) COMCTL32_Free(oldText);
    2989   if (newText) COMCTL32_Free(newText);
    2990 
    2991   return rc;
    2992 }
    2993 
    2994 static BOOL
    2995 TREEVIEW_SendTreeviewDnDNotify (HWND hwnd, UINT code, HTREEITEM dragItem,
    2996                                                                 POINT pt)
    2997 {
    2998   TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
    2999   NMTREEVIEWA nmhdr;
    3000   TREEVIEW_ITEM  *wineItem;
    3001 
    3002   nmhdr.action = 0;
    3003   wineItem=& infoPtr->items[(INT)dragItem];
    3004   nmhdr.itemNew.mask    = wineItem->mask;
    3005   nmhdr.itemNew.hItem   = wineItem->hItem;
    3006   nmhdr.itemNew.state   = wineItem->state;
    3007   nmhdr.itemNew.lParam  = wineItem->lParam;
    3008 
    3009   nmhdr.ptDrag.x = pt.x;
    3010   nmhdr.ptDrag.y = pt.y;
    3011 
    3012   return (BOOL)sendNotify(hwnd,code,&nmhdr.hdr);
    3013 
    3014 }
    3015 
    3016 static INT TREEVIEW_CallbackImage(HWND hwnd,TREEVIEW_ITEM *wineItem)
    3017 {
    3018   NMTVDISPINFOW tvdi;
    3019   BOOL retval;
    3020 
    3021   tvdi.item.mask   = TVIF_IMAGE;
    3022   tvdi.item.hItem  = wineItem->hItem;
    3023   tvdi.item.state  = wineItem->state;
    3024   tvdi.item.lParam = wineItem->lParam;
    3025   tvdi.item.iImage = 0;
    3026 
    3027   retval = (BOOL)sendNotify(hwnd,isUnicodeNotify(hwnd) ? TVN_GETDISPINFOW:TVN_GETDISPINFOA,&tvdi.hdr);
    3028 
    3029   if (tvdi.item.mask & TVIF_DI_SETITEM)
    3030     wineItem->iImage = tvdi.item.iImage;
    3031 
    3032   return tvdi.item.iImage;
    3033 }
    3034 
    3035 static INT TREEVIEW_CallbackSelectedImage(HWND hwnd,TREEVIEW_ITEM *wineItem)
    3036 {
    3037   NMTVDISPINFOW tvdi;
    3038   BOOL retval;
    3039 
    3040   tvdi.item.mask           = TVIF_SELECTEDIMAGE;
    3041   tvdi.item.hItem          = wineItem->hItem;
    3042   tvdi.item.state          = wineItem->state;
    3043   tvdi.item.lParam         = wineItem->lParam;
    3044   tvdi.item.iSelectedImage = 0;
    3045 
    3046   retval = (BOOL)sendNotify(hwnd,isUnicodeNotify(hwnd) ? TVN_GETDISPINFOW:TVN_GETDISPINFOA,&tvdi.hdr);
    3047 
    3048   if (tvdi.item.mask & TVIF_DI_SETITEM)
    3049     wineItem->iSelectedImage = tvdi.item.iSelectedImage;
    3050 
    3051   return tvdi.item.iSelectedImage;
    3052 }
    3053 
    3054 static WCHAR* TREEVIEW_CallbackText(HWND hwnd,TREEVIEW_ITEM *wineItem,BOOL *mustFree)
    3055 {
    3056   TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
    3057   NMTVDISPINFOW tvdi;
    3058   BOOL retval;
    3059   WCHAR *buf;
    3060 
    3061   tvdi.item.mask        = TVIF_TEXT;
    3062   tvdi.item.hItem       = wineItem->hItem;
    3063   tvdi.item.state       = wineItem->state;
    3064   tvdi.item.lParam      = wineItem->lParam;
    3065   tvdi.item.pszText     = (WCHAR*)COMCTL32_Alloc(128*(isUnicodeNotify(&infoPtr->header) ? sizeof(WCHAR):sizeof(CHAR)));
    3066   tvdi.item.cchTextMax  = 128;
    3067   buf = tvdi.item.pszText;
    3068 
    3069   retval = (BOOL)sendNotify(hwnd,isUnicodeNotify(&infoPtr->header) ? TVN_GETDISPINFOW:TVN_GETDISPINFOA,&tvdi.hdr);
    3070 
    3071   if (tvdi.item.pszText == LPSTR_TEXTCALLBACKW)
    3072   {
    3073     *mustFree = FALSE;
    3074     return NULL;
    3075   }
    3076 
    3077   if (isUnicodeNotify(&infoPtr->header))
    3078   {
    3079     if (tvdi.item.mask & TVIF_DI_SETITEM)
    3080     {
    3081       if (buf == tvdi.item.pszText)
    3082       {
    3083         wineItem->pszText = tvdi.item.pszText;
    3084         wineItem->cchTextMax = 128;
    3085       } else
    3086       { //user-supplied buffer
    3087         INT len = lstrlenW(tvdi.item.pszText);
    3088 
    3089         COMCTL32_Free(buf);
    3090         if (len > 0)
    3091         {
    3092           len++;
    3093           wineItem->pszText = (WCHAR*)COMCTL32_Alloc(len*sizeof(WCHAR));
    3094           lstrcpyW(wineItem->pszText,tvdi.item.pszText);
    3095           wineItem->cchTextMax = len;
    3096         } else
    3097         {
    3098           wineItem->pszText = NULL;
    3099           wineItem->cchTextMax = len;
    3100         }
    3101       }
    3102       *mustFree = FALSE;
    3103       return wineItem->pszText;
    3104     } else
    3105     {
    3106       if (buf != tvdi.item.pszText)
    3107       {
    3108         COMCTL32_Free(buf);
    3109         *mustFree = FALSE;
    3110       } else *mustFree = TRUE;
    3111       return tvdi.item.pszText;
    3112     }
    3113   } else
    3114   {
    3115     if (tvdi.item.mask & TVIF_DI_SETITEM)
    3116     {
    3117       if (buf == tvdi.item.pszText)
    3118       {
    3119         wineItem->cchTextMax = 128;
    3120         wineItem->pszText = (WCHAR*)COMCTL32_Alloc(128*sizeof(WCHAR));
    3121         lstrcpynAtoW(wineItem->pszText,(CHAR*)tvdi.item.pszText,wineItem->cchTextMax);
    3122         COMCTL32_Free(buf);
    3123       } else
    3124       { //user-supplied buffer
    3125         INT len = lstrlenA((CHAR*)tvdi.item.pszText);
    3126 
    3127         COMCTL32_Free(buf);
    3128         if (len > 0)
    3129         {
    3130           len++;
    3131           wineItem->pszText = (WCHAR*)COMCTL32_Alloc(len*sizeof(WCHAR));
    3132           lstrcpyAtoW(wineItem->pszText,(CHAR*)tvdi.item.pszText);
    3133           wineItem->cchTextMax = len;
    3134         } else
    3135         {
    3136           wineItem->pszText = NULL;
    3137           wineItem->cchTextMax = len;
    3138         }
    3139       }
    3140       *mustFree = FALSE;
    3141       return wineItem->pszText;
    3142     } else
    3143     {
    3144       INT len = lstrlenA((LPSTR)tvdi.item.pszText);
    3145       WCHAR* textW = (WCHAR*)COMCTL32_Alloc((len+1)*sizeof(WCHAR));
    3146 
    3147       lstrcpyAtoW(textW,(LPSTR)tvdi.item.pszText);
    3148       COMCTL32_Free(buf);
    3149       *mustFree = TRUE;
    3150       return textW;
    3151     }
    3152   }
    3153 }
    3154 
    3155 static INT TREEVIEW_CallbackChildren(HWND hwnd,TREEVIEW_ITEM *wineItem)
    3156 {
    3157   NMTVDISPINFOW tvdi;
    3158   BOOL retval;
    3159 
    3160   tvdi.item.mask      = TVIF_CHILDREN;
    3161   tvdi.item.hItem     = wineItem->hItem;
    3162   tvdi.item.state     = wineItem->state;
    3163   tvdi.item.lParam    = wineItem->lParam;
    3164   tvdi.item.cChildren = 0;
    3165 
    3166   retval = (BOOL)sendNotify(hwnd,isUnicodeNotify(hwnd) ? TVN_GETDISPINFOW:TVN_GETDISPINFOA,&tvdi.hdr);
    3167 
    3168   if (tvdi.item.mask & TVIF_DI_SETITEM)
    3169     wineItem->cChildren = tvdi.item.cChildren;
    3170 
    3171   return tvdi.item.cChildren;
    3172 }
    3173 
    3174 static BOOL
    3175 TREEVIEW_SendCustomDrawNotify (HWND hwnd, DWORD dwDrawStage, HDC hdc,
    3176                         RECT rc)
    3177 {
    3178   TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
    3179   NMTVCUSTOMDRAW nmcdhdr;
    3180   LPNMCUSTOMDRAW nmcd;
    3181 
    3182   nmcd = &nmcdhdr.nmcd;
    3183   nmcd->dwDrawStage= dwDrawStage;
    3184   nmcd->hdc        = hdc;
    3185   nmcd->rc.left    = rc.left;
    3186   nmcd->rc.right   = rc.right;
    3187   nmcd->rc.bottom  = rc.bottom;
    3188   nmcd->rc.top     = rc.top;
    3189   nmcd->dwItemSpec = 0;
    3190   nmcd->uItemState = 0;
    3191   nmcd->lItemlParam= 0;
    3192   nmcdhdr.clrText  = infoPtr->clrText;
    3193   nmcdhdr.clrTextBk= infoPtr->clrBk;
    3194   nmcdhdr.iLevel   = 0;
    3195 
    3196   return (BOOL)sendNotify(hwnd,NM_CUSTOMDRAW,&nmcdhdr.nmcd.hdr);
    3197 }
    3198 
    3199 /* FIXME: need to find out when the flags in uItemState need to be set */
    3200 
    3201 static BOOL
    3202 TREEVIEW_SendCustomDrawItemNotify (HWND hwnd, HDC hdc,
    3203                         TREEVIEW_ITEM *wineItem, UINT uItemDrawState)
    3204 {
    3205   TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
    3206   NMTVCUSTOMDRAW nmcdhdr;
    3207   LPNMCUSTOMDRAW nmcd;
    3208   DWORD dwDrawStage,dwItemSpec;
    3209   UINT uItemState;
    3210   INT retval;
    3211 
    3212   dwDrawStage=CDDS_ITEM | uItemDrawState;
    3213   dwItemSpec=(DWORD)wineItem->hItem;
    3214   uItemState=0;
    3215   if (wineItem->hItem == infoPtr->selectedItem)
    3216   {
    3217     uItemState|=CDIS_SELECTED;
    3218     if (GetFocus() == hwnd) uItemState |= CDIS_FOCUS;
    3219   }
    3220   if (wineItem->hItem==infoPtr->hotItem)      uItemState|=CDIS_HOT;
    3221 
    3222   nmcd= & nmcdhdr.nmcd;
    3223   nmcd->dwDrawStage= dwDrawStage;
    3224   nmcd->hdc                = hdc;
    3225   nmcd->rc.left    = wineItem->rect.left;
    3226   nmcd->rc.right   = wineItem->rect.right;
    3227   nmcd->rc.bottom  = wineItem->rect.bottom;
    3228   nmcd->rc.top     = wineItem->rect.top;
    3229   nmcd->dwItemSpec = dwItemSpec;
    3230   nmcd->uItemState = uItemState;
    3231   nmcd->lItemlParam= wineItem->lParam;
    3232 
    3233   nmcdhdr.clrText  = infoPtr->clrText;
    3234   nmcdhdr.clrTextBk= infoPtr->clrBk;
    3235   nmcdhdr.iLevel   = wineItem->iLevel;
    3236 
    3237   //TRACE (treeview,"drawstage:%lx hdc:%x item:%lx, itemstate:%x\n",
    3238   //               dwDrawStage, hdc, dwItemSpec, uItemState);
    3239 
    3240   retval = sendNotify(hwnd,NM_CUSTOMDRAW,&nmcdhdr.nmcd.hdr);
    3241 
    3242   infoPtr->clrText=nmcdhdr.clrText;
    3243   infoPtr->clrBk  =nmcdhdr.clrTextBk;
    3244 
    3245   return (BOOL) retval;
    3246 }
    3247 
    3248 static VOID TREEVIEW_SendKeyDownNotify(HWND hwnd,UINT code,WORD wVKey)
    3249 {
    3250   NMTVKEYDOWN nmkdhdr;
    3251 
    3252   nmkdhdr.wVKey = wVKey;
    3253   nmkdhdr.flags = 0;
    3254 
    3255   sendNotify(hwnd,code,&nmkdhdr.hdr);
     2736TREEVIEW_Toggle(TREEVIEW_INFO *infoPtr, TREEVIEW_ITEM *wineItem, BOOL bUser)
     2737{
     2738    if (wineItem->state & TVIS_EXPANDED)
     2739        return TREEVIEW_Collapse(infoPtr, wineItem, FALSE, bUser);
     2740    else
     2741        return TREEVIEW_Expand(infoPtr, wineItem, FALSE, bUser);
     2742}
     2743
     2744static VOID
     2745TREEVIEW_ExpandAll(TREEVIEW_INFO *infoPtr, TREEVIEW_ITEM *item)
     2746{
     2747    TREEVIEW_Expand(infoPtr, item, FALSE, TRUE);
     2748
     2749    for (item = item->firstChild; item != NULL; item = item->nextSibling)
     2750    {
     2751        if (TREEVIEW_HasChildren(infoPtr, item))
     2752            TREEVIEW_ExpandAll(infoPtr, item);
     2753    }
    32562754}
    32572755
     
    32632761
    32642762static LRESULT
    3265 TREEVIEW_Expand (HWND hwnd, WPARAM wParam, LPARAM lParam)
    3266 {
    3267   TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
     2763TREEVIEW_ExpandMsg(TREEVIEW_INFO *infoPtr, WPARAM wParam, LPARAM lParam)
     2764{
     2765    TREEVIEW_ITEM *wineItem = (HTREEITEM)lParam;
     2766    UINT flag = wParam;
     2767
     2768    if (!TREEVIEW_ValidItem(infoPtr, wineItem))
     2769        return 0;
     2770
     2771    switch (flag & TVE_TOGGLE)
     2772    {
     2773    case TVE_COLLAPSE:
     2774        return TREEVIEW_Collapse(infoPtr, wineItem, flag & TVE_COLLAPSERESET,
     2775                                 FALSE);
     2776
     2777    case TVE_EXPAND:
     2778        return TREEVIEW_Expand(infoPtr, wineItem, flag & TVE_EXPANDPARTIAL,
     2779                               FALSE);
     2780
     2781    case TVE_TOGGLE:
     2782        return TREEVIEW_Toggle(infoPtr, wineItem, TRUE);
     2783
     2784    default:
     2785        return 0;
     2786    }
     2787
     2788}
     2789
     2790/* Hit-Testing **********************************************************/
     2791
     2792static TREEVIEW_ITEM *
     2793TREEVIEW_HitTestPoint(TREEVIEW_INFO *infoPtr,POINT pt)
     2794{
    32682795  TREEVIEW_ITEM *wineItem;
    3269   UINT flag;
    3270   INT expand;
    3271 
    3272   flag = (UINT)wParam;
    3273   expand = (INT)lParam;
    3274 
    3275   wineItem = TREEVIEW_ValidItem (infoPtr,(HTREEITEM)expand);
    3276 
    3277   if (!wineItem)
    3278     return 0;
    3279 
    3280   if (!TREEVIEW_HasChildren(hwnd, wineItem))
    3281     return 0;
    3282 
    3283   if ((flag & 0xF) == TVE_TOGGLE)
    3284   {
    3285     if (wineItem->state & TVIS_EXPANDED)
    3286       flag = (flag & ~0xF)+TVE_COLLAPSE;
     2796
     2797  if (!infoPtr->firstVisible)
     2798    return NULL;
     2799
     2800  wineItem = infoPtr->firstVisible;
     2801
     2802  while (wineItem && (pt.y > wineItem->rect.bottom))
     2803    wineItem = TREEVIEW_GetNextListItem(infoPtr,wineItem);
     2804
     2805  return wineItem;
     2806}
     2807
     2808static LRESULT
     2809TREEVIEW_HitTest(TREEVIEW_INFO *infoPtr,LPTVHITTESTINFO lpht,BOOL ignoreClientRect)
     2810{
     2811    TREEVIEW_ITEM *wineItem;
     2812    RECT rect;
     2813    UINT status, x, y;
     2814
     2815    if (!lpht) return 0;
     2816
     2817    status = 0;
     2818    x = lpht->pt.x;
     2819    y = lpht->pt.y;
     2820    lpht->hItem = 0;
     2821
     2822    if (!ignoreClientRect)
     2823    {
     2824      GetClientRect(infoPtr->hwnd,&rect);
     2825
     2826      if (x < rect.left) status |= TVHT_TOLEFT;
     2827      else if (x > rect.right) status |= TVHT_TORIGHT;
     2828      if (y < rect.top) status |= TVHT_ABOVE;
     2829      else if (y > rect.bottom) status |= TVHT_BELOW;
     2830
     2831      if (status)
     2832      {
     2833        lpht->flags = status;
     2834        return (LRESULT)(HTREEITEM)NULL;
     2835      }
     2836    }
     2837
     2838    wineItem = TREEVIEW_HitTestPoint(infoPtr,lpht->pt);
     2839    if (!wineItem)
     2840    {
     2841        lpht->flags = TVHT_NOWHERE;
     2842        return (LRESULT)(HTREEITEM)NULL;
     2843    }
     2844
     2845    if (!wineItem->calculated)
     2846      TREEVIEW_CalcItem(infoPtr,wineItem,0);
     2847
     2848    if (x >= wineItem->textOffset + wineItem->textWidth)
     2849    {
     2850        lpht->flags = TVHT_ONITEMRIGHT;
     2851    }
     2852    else if (x >= wineItem->textOffset)
     2853    {
     2854        lpht->flags = TVHT_ONITEMLABEL;
     2855    }
     2856    else if (x >= wineItem->imageOffset)
     2857    {
     2858        lpht->flags = TVHT_ONITEMICON;
     2859    }
     2860    else if (x >= wineItem->stateOffset)
     2861    {
     2862        lpht->flags = TVHT_ONITEMSTATEICON;
     2863    }
     2864    else if ((x >= wineItem->linesOffset) && (infoPtr->dwStyle & TVS_HASBUTTONS))
     2865    {
     2866        lpht->flags = TVHT_ONITEMBUTTON;
     2867    }
    32872868    else
    3288       flag = (flag & ~0xF)+TVE_EXPAND;
    3289   }
    3290 
    3291   switch (flag & 0xF)
    3292   {
    3293     case TVE_COLLAPSE:
    3294     {
    3295       POINT oldLeftTop = infoPtr->lefttop;
    3296 
    3297       if (!wineItem->state & TVIS_EXPANDED)
    3298         return 0;
    3299 
    3300       if (flag & TVE_COLLAPSERESET)
    3301       {
    3302         wineItem->state &= ~(TVIS_EXPANDEDONCE | TVIS_EXPANDED);
    3303         TREEVIEW_RemoveAllChildren (hwnd, wineItem);
    3304       } else wineItem->state &= ~TVIS_EXPANDED;
    3305 
    3306       //update window
    3307       //CB: todo: optimize!
    3308       TREEVIEW_UnqueueRefresh(hwnd,FALSE,FALSE);
    3309       //CB: todo: precalc expanded items here
    3310       TREEVIEW_CalcItems(hwnd,0,infoPtr);
    3311       TREEVIEW_Refresh(hwnd);
    3312       //CB: todo: check cx and cy to fit ranges!
    3313 
    3314       //check selection
    3315       HTREEITEM hItem = infoPtr->selectedItem;
    3316 
    3317       if (!TREEVIEW_ValidItem (infoPtr, hItem))
    3318         hItem = wineItem->hItem;
    3319       else
    3320       {
    3321         while (hItem)
     2869    {
     2870        lpht->flags = TVHT_ONITEMINDENT;
     2871    }
     2872
     2873    lpht->hItem = wineItem;
     2874    //TRACE("(%ld,%ld):result %x\n", lpht->pt.x, lpht->pt.y, lpht->flags);
     2875
     2876    return (LRESULT)wineItem;
     2877}
     2878
     2879/* Item Label Editing ***************************************************/
     2880
     2881static LRESULT
     2882TREEVIEW_GetEditControl(TREEVIEW_INFO *infoPtr)
     2883{
     2884    return infoPtr->hwndEdit;
     2885}
     2886
     2887static LRESULT CALLBACK
     2888TREEVIEW_Edit_SubclassProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
     2889{
     2890    TREEVIEW_INFO *infoPtr;
     2891    BOOL bCancel = FALSE;
     2892
     2893    switch (uMsg)
     2894    {
     2895    case WM_PAINT:
    33222896        {
    3323           hItem = infoPtr->items[(INT)hItem].parent;
    3324 
    3325           if (hItem == wineItem->hItem)
     2897            LRESULT rc;
     2898            TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(GetParent(hwnd));
     2899
     2900            //TRACE("WM_PAINT start\n");
     2901            rc = CallWindowProcA(infoPtr->wpEditOrig, hwnd, uMsg, wParam,
     2902                                 lParam);
     2903            //TRACE("WM_PAINT done\n");
     2904            return rc;
     2905        }
     2906
     2907    case WM_KILLFOCUS:
     2908    {
     2909        TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(GetParent(hwnd));
     2910        if (infoPtr->bIgnoreEditKillFocus)
     2911            return TRUE;
     2912
     2913        break;
     2914    }
     2915
     2916    case WM_GETDLGCODE:
     2917        return DLGC_WANTARROWS | DLGC_WANTALLKEYS;
     2918
     2919    case WM_KEYDOWN:
     2920        if (wParam == (WPARAM)VK_ESCAPE)
     2921        {
     2922            bCancel = TRUE;
    33262923            break;
    33272924        }
    3328       }
    3329 
    3330       if (hItem)
    3331         TREEVIEW_DoSelectItem(hwnd,TVGN_CARET,hItem,TVC_UNKNOWN);
    3332 
    3333       break;
    3334     }
    3335 
    3336     case TVE_EXPAND:
    3337     {
    3338       POINT oldLeftTop = infoPtr->lefttop;
    3339 
    3340       if (wineItem->state & TVIS_EXPANDED)
    3341         return 0;
    3342 
    3343       if (flag & TVE_EXPANDPARTIAL)
    3344       {
    3345 return FALSE; //CB: how does this work??? (only display one level? nonsense in MSDN docu)
    3346         wineItem->state ^=TVIS_EXPANDED;
    3347         wineItem->state |=TVIS_EXPANDEDONCE;
    3348         break;
    3349       }
    3350 
    3351       if (!(wineItem->state & TVIS_EXPANDEDONCE))
    3352       {
    3353         //TRACE(treeview, "  and has never been expanded...\n");
    3354         wineItem->state |= TVIS_EXPANDED;
    3355 
    3356         /* this item has never been expanded */
    3357         if (TREEVIEW_SendTreeviewNotify(hwnd,isUnicodeNotify(&infoPtr->header) ? TVN_ITEMEXPANDINGW:TVN_ITEMEXPANDINGA,TVE_EXPAND,0,(HTREEITEM)expand))
    3358           return FALSE;
    3359 
    3360 
    3361         /* FIXME
    3362          * Since the TVN_ITEMEXPANDING message may has caused the parent to
    3363          * insert new items which in turn may have cause items placeholder
    3364          * reallocation, I reassign the current item pointer so we have
    3365          * something valid to work with...
    3366          * However, this should not be necessary,
    3367          * investigation required in TREEVIEW_InsertItemA
    3368          */
    3369         wineItem = TREEVIEW_ValidItem (infoPtr, (HTREEITEM)expand);
    3370         if (!wineItem)
     2925        else if (wParam == (WPARAM)VK_RETURN)
    33712926        {
    3372           //ERR(treeview,
    3373           //  "Catastropic situation, cannot retreive item #%d\n",
    3374           //  expand);
    3375           return FALSE;
     2927            break;
    33762928        }
    33772929
    3378         wineItem->state |= TVIS_EXPANDEDONCE;
    3379 
    3380         TREEVIEW_SendTreeviewNotify (hwnd,isUnicodeNotify(&infoPtr->header) ? TVN_ITEMEXPANDEDW:TVN_ITEMEXPANDEDA,TVE_EXPAND,0,(HTREEITEM)expand);
    3381       } else
    3382       {
    3383         /* this item has already been expanded */
    3384         wineItem->state |= TVIS_EXPANDED;
    3385       }
    3386 
    3387       //update window
    3388       //CB: todo: optimize!
    3389       TREEVIEW_UnqueueRefresh(hwnd,FALSE,FALSE);
    3390       //CB: todo: precalc expanded items here
    3391       TREEVIEW_CalcItems(hwnd,0,infoPtr);
    3392       TREEVIEW_Refresh(hwnd);
    3393       //CB: todo: check cx and cy to fit ranges!
    3394 
    3395       break;
    3396     }
    3397 
     2930        /* fall through */
    33982931    default:
    3399       return FALSE;
    3400   }
    3401 
    3402   return TRUE;
    3403 }
    3404 
    3405 static LRESULT TREEVIEW_HitTest(HWND hwnd,LPTVHITTESTINFO lpht,BOOL ignoreClientRect)
    3406 {
    3407   TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
    3408   TREEVIEW_ITEM *wineItem;
    3409   RECT rect;
    3410   UINT status;
    3411   INT x,y;
    3412 
    3413   if (!lpht) return 0;
    3414 
    3415   status = 0;
    3416   x = lpht->pt.x;
    3417   y = lpht->pt.y;
    3418 
    3419   if (!ignoreClientRect)
    3420   {
    3421     GetClientRect (hwnd, &rect);
    3422 
    3423     if (x < rect.left)  status |= TVHT_TOLEFT;
    3424     if (x > rect.right) status |= TVHT_TORIGHT;
    3425     if (y < rect.top )  status |= TVHT_ABOVE;
    3426     if (y > rect.bottom) status |= TVHT_BELOW;
    3427 
    3428     if (status)
    3429     {
    3430       lpht->flags = status;
    3431       lpht->hItem = 0;
    3432 
    3433       return 0;
    3434     }
    3435   }
    3436 
    3437   if (infoPtr->firstVisible)
    3438   {
    3439    wineItem = &infoPtr->items [(INT)infoPtr->firstVisible];
    3440 
    3441    while ((wineItem != NULL) && (y > wineItem->rect.bottom))
    3442      wineItem = TREEVIEW_GetNextListItem(hwnd,infoPtr,wineItem);
    3443   } else wineItem = NULL;
    3444 
    3445   if (!wineItem)
    3446   {
    3447     lpht->flags = TVHT_NOWHERE;
    3448     lpht->hItem = 0;
     2932        {
     2933            TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(GetParent(hwnd));
     2934
     2935            //@@@PH 1999/11/05 method called with freed infoPtr memory object
     2936            if (infoPtr != NULL)
     2937              return CallWindowProcA(infoPtr->wpEditOrig, hwnd, uMsg, wParam, lParam);
     2938            else
     2939              break;
     2940        }
     2941    }
     2942
     2943    /* Processing TVN_ENDLABELEDIT message could kill the focus       */
     2944    /* eg. Using a messagebox                                         */
     2945
     2946    infoPtr = TREEVIEW_GetInfoPtr(GetParent(hwnd));
     2947    infoPtr->bIgnoreEditKillFocus = TRUE;
     2948    TREEVIEW_EndEditLabelNow(infoPtr, bCancel || !infoPtr->bLabelChanged);
     2949    infoPtr->bIgnoreEditKillFocus = FALSE;
    34492950
    34502951    return 0;
    3451   }
    3452 
    3453   lpht->flags = 0;
    3454 
    3455   if (x < wineItem->expandBox.left) {
    3456     lpht->flags |= TVHT_ONITEMINDENT;
    3457         goto done;
    3458   }
    3459   if ( PtInRect ( &wineItem->expandBox, lpht->pt)) {
    3460     lpht->flags |= TVHT_ONITEMBUTTON;
    3461         goto done;
    3462   }
    3463   if ( PtInRect ( &wineItem->bitmap, lpht->pt)) {
    3464         lpht->flags |= TVHT_ONITEMICON;
    3465     goto done;
    3466   }
    3467   if ( PtInRect ( &wineItem->statebitmap, lpht->pt)) {
    3468         lpht->flags |= TVHT_ONITEMSTATEICON;
    3469     goto done;
    3470   }
    3471   if ( PtInRect ( &wineItem->text, lpht->pt)) {
    3472     lpht->flags |= TVHT_ONITEMLABEL;
    3473         goto done;
    3474   }
    3475 
    3476   lpht->flags |= TVHT_ONITEMRIGHT;
    3477 
    3478 done:
    3479   lpht->hItem = wineItem->hItem;
    3480 
    3481   return (LRESULT) wineItem->hItem;
    3482 }
    3483 
    3484 HWND TREEVIEW_EditLabel(HWND hwnd,HTREEITEM hItem,BOOL unicode)
     2952}
     2953
     2954
     2955/* should handle edit control messages here */
     2956
     2957static LRESULT
     2958TREEVIEW_Command(TREEVIEW_INFO *infoPtr, WPARAM wParam, LPARAM lParam)
     2959{
     2960    //TRACE("%x %ld\n", wParam, lParam);
     2961
     2962    switch (HIWORD(wParam))
     2963    {
     2964    case EN_UPDATE:
     2965        {
     2966            /*
     2967             * Adjust the edit window size
     2968             */
     2969            char buffer[1024];
     2970            TREEVIEW_ITEM *editItem = infoPtr->selectedItem;
     2971            HDC hdc = GetDC(infoPtr->hwndEdit);
     2972            SIZE sz;
     2973            int len;
     2974            HFONT hFont, hOldFont = 0;
     2975
     2976            infoPtr->bLabelChanged = TRUE;
     2977
     2978            len = GetWindowTextA(infoPtr->hwndEdit, buffer, sizeof(buffer));
     2979
     2980            /* Select font to get the right dimension of the string */
     2981            hFont = SendMessageA(infoPtr->hwndEdit, WM_GETFONT, 0, 0);
     2982            if (hFont != 0)
     2983            {
     2984                hOldFont = SelectObject(hdc, hFont);
     2985            }
     2986
     2987            if (GetTextExtentPoint32A(hdc, buffer, strlen(buffer), &sz))
     2988            {
     2989                TEXTMETRICA textMetric;
     2990
     2991                /* Add Extra spacing for the next character */
     2992                GetTextMetricsA(hdc, &textMetric);
     2993                sz.cx += (textMetric.tmMaxCharWidth * 2);
     2994
     2995                sz.cx = MAX(sz.cx, textMetric.tmMaxCharWidth * 3);
     2996                sz.cx = MIN(sz.cx,
     2997                            infoPtr->clientWidth - editItem->textOffset + 2);
     2998
     2999                SetWindowPos(infoPtr->hwndEdit,
     3000                             HWND_TOP,
     3001                             0,
     3002                             0,
     3003                             sz.cx,
     3004                             editItem->rect.bottom - editItem->rect.top + 3,
     3005                             SWP_NOMOVE | SWP_DRAWFRAME);
     3006            }
     3007
     3008            if (hFont != 0)
     3009            {
     3010                SelectObject(hdc, hOldFont);
     3011            }
     3012
     3013            ReleaseDC(infoPtr->hwnd, hdc);
     3014            break;
     3015        }
     3016
     3017    default:
     3018        return SendMessageA(GetParent(infoPtr->hwnd), WM_COMMAND, wParam, lParam);
     3019    }
     3020
     3021    return 0;
     3022}
     3023
     3024HWND TREEVIEW_EditLabel(TREEVIEW_INFO *infoPtr,HTREEITEM hItem,BOOL unicode)
    34853025{
    34863026  SIZE sz;
    3487   TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
    3488   TREEVIEW_ITEM *editItem = TREEVIEW_ValidItem(infoPtr, (HTREEITEM)hItem);
    3489   HINSTANCE hinst = GetWindowLongA(hwnd, GWL_HINSTANCE);
     3027  TREEVIEW_ITEM *editItem = hItem;
     3028  HINSTANCE hinst = GetWindowLongA(infoPtr->hwnd,GWL_HINSTANCE);
    34903029  HDC hdc;
    34913030  HFONT hOldFont = 0;
    34923031  TEXTMETRICA textMetric;
    3493   WCHAR* textW;
    34943032  CHAR* textA = NULL;
    3495   BOOL mustFree = FALSE;
    34963033  NMTVDISPINFOW tvdi;
    34973034
    3498   if (!editItem)
    3499     return FALSE;
     3035  if (!TREEVIEW_ValidItem(infoPtr, editItem))
     3036    return (HWND)NULL;
    35003037
    35013038  if(infoPtr->hwndEdit)
    35023039    return infoPtr->hwndEdit;
    35033040
     3041  infoPtr->bLabelChanged = FALSE;
     3042
    35043043  /* Make shure that edit item is selected */
    35053044
    3506   TREEVIEW_DoSelectItem ( hwnd, TVGN_CARET, hItem, TVC_UNKNOWN);
    3507 
    3508   if (editItem->pszText == LPSTR_TEXTCALLBACKW)
    3509     textW = TREEVIEW_CallbackText(hwnd,editItem,&mustFree);
    3510   else
    3511     textW = editItem->pszText;
    3512 
    3513   hdc = GetDC(hwnd);
     3045  TREEVIEW_DoSelectItem ( infoPtr, TVGN_CARET, hItem, TVC_UNKNOWN);
     3046
     3047  TREEVIEW_UpdateDispInfo(infoPtr, editItem, TVIF_TEXT);
     3048
     3049  hdc = GetDC(infoPtr->hwnd);
    35143050  /* Select the font to get appropriate metric dimensions */
    35153051  if(infoPtr->hFont != 0)
     
    35193055
    35203056  /*Get String Lenght in pixels */
    3521   GetTextExtentPoint32W(hdc,textW,lstrlenW(textW),&sz);
     3057  GetTextExtentPoint32W(hdc,editItem->pszText,lstrlenW(editItem->pszText),&sz);
    35223058
    35233059  /*Add Extra spacing for the next character */
     
    35253061  sz.cx += (textMetric.tmMaxCharWidth * 2);
    35263062
     3063  sz.cx = MAX(sz.cx, textMetric.tmMaxCharWidth * 3);
     3064  sz.cx = MIN(sz.cx, infoPtr->clientWidth - editItem->textOffset + 2);
     3065
    35273066  if(infoPtr->hFont != 0)
    35283067  {
     
    35303069  }
    35313070
    3532   ReleaseDC(hwnd, hdc);
     3071  ReleaseDC(infoPtr->hwnd, hdc);
    35333072  infoPtr->hwndEdit = CreateWindowExA (
    35343073                         WS_EX_LEFT,
     
    35373076                         WS_CHILD | WS_BORDER | ES_AUTOHSCROLL | WS_CLIPSIBLINGS |
    35383077                         ES_WANTRETURN | ES_LEFT,
    3539                          editItem->text.left - 2, editItem->text.top  - 1,
    3540                          sz.cx+3, editItem->text.bottom - editItem->text.top  + 3,
    3541                          hwnd,
     3078                         editItem->textOffset - 2, editItem->rect.top  - 1,
     3079                         sz.cx+3, editItem->rect.bottom - editItem->rect.top  + 3,
     3080                         infoPtr->hwnd,
    35423081                         0,hinst,0); /* FIXME: (HMENU)IDTVEDIT,pcs->hInstance,0);*/
    35433082
    3544   SendMessageA ( infoPtr->hwndEdit, WM_SETFONT, infoPtr->hFont, FALSE);
    3545   infoPtr->wpEditOrig = (WNDPROC)SetWindowLongA (
    3546                                    infoPtr->hwndEdit,
    3547                                    GWL_WNDPROC,
    3548                   (DWORD) TREEVIEW_Edit_SubclassProc);
     3083    /* Get a 2D border. */
     3084  SetWindowLongA(infoPtr->hwndEdit, GWL_EXSTYLE,GetWindowLongA(infoPtr->hwndEdit, GWL_EXSTYLE) & ~WS_EX_CLIENTEDGE);
     3085  SetWindowLongA(infoPtr->hwndEdit, GWL_STYLE,GetWindowLongA(infoPtr->hwndEdit, GWL_STYLE) | WS_BORDER);
     3086
     3087  SendMessageA(infoPtr->hwndEdit, WM_SETFONT, TREEVIEW_FontForItem(infoPtr, editItem),FALSE);
     3088
     3089  infoPtr->wpEditOrig = (WNDPROC)SetWindowLongA (infoPtr->hwndEdit,GWL_WNDPROC,(DWORD)TREEVIEW_Edit_SubclassProc);
    35493090
    35503091  tvdi.item.mask        = TVIF_HANDLE | TVIF_TEXT | TVIF_STATE | TVIF_PARAM;
    3551   tvdi.item.hItem       = editItem->hItem;
     3092  tvdi.item.hItem       = editItem;
    35523093  tvdi.item.state       = editItem->state;
    35533094  tvdi.item.lParam      = editItem->lParam;
    35543095  if (isUnicodeNotify(&infoPtr->header))
    35553096  {
    3556     tvdi.item.pszText     = textW;
     3097    tvdi.item.pszText     = editItem->pszText;
    35573098  } else
    35583099  {
    3559     textA = HEAP_strdupWtoA(GetProcessHeap(),0,textW);
     3100    textA = HEAP_strdupWtoA(GetProcessHeap(),0,editItem->pszText);
    35603101    tvdi.item.pszText = (LPWSTR)textA;
    35613102  }
    35623103
    3563   if (sendNotify(hwnd,isUnicodeNotify(&infoPtr->header) ? TVN_BEGINLABELEDITW:TVN_BEGINLABELEDITA,&tvdi.hdr))
     3104  if (sendNotify(infoPtr->hwnd,isUnicodeNotify(&infoPtr->header) ? TVN_BEGINLABELEDITW:TVN_BEGINLABELEDITA,&tvdi.hdr))
    35643105  {
    35653106     DestroyWindow(infoPtr->hwndEdit);
    35663107     infoPtr->hwndEdit = 0;
    3567      if (mustFree) COMCTL32_Free(textW);
    3568      if (textA) COMCTL32_Free(textA);
     3108     if (textA) HeapFree(GetProcessHeap(),0,textA);
    35693109
    35703110     return (HWND)0;
     
    35723112  if (textA) HeapFree(GetProcessHeap(),0,textA);
    35733113
    3574   infoPtr->editItem = hItem;
    3575   SetWindowTextW(infoPtr->hwndEdit,textW);
     3114  infoPtr->selectedItem = hItem;
     3115  SetWindowTextW(infoPtr->hwndEdit,editItem->pszText);
    35763116  SetFocus(infoPtr->hwndEdit);
    35773117  SendMessageA(infoPtr->hwndEdit, EM_SETSEL, 0, -1);
    35783118  ShowWindow(infoPtr->hwndEdit, SW_SHOW);
    3579   if (mustFree) COMCTL32_Free(textW);
    35803119
    35813120  return infoPtr->hwndEdit;
     
    35833122
    35843123
    3585 LRESULT WINAPI
    3586 TREEVIEW_EndEditLabelNow(HWND hwnd,BOOL bCancel)
    3587 {
    3588   TREEVIEW_INFO *infoPtr    = TREEVIEW_GetInfoPtr(hwnd);
    3589   TREEVIEW_ITEM *editedItem = TREEVIEW_ValidItem (infoPtr, infoPtr->editItem);
     3124static LRESULT
     3125TREEVIEW_EndEditLabelNow(TREEVIEW_INFO *infoPtr,BOOL bCancel)
     3126{
     3127  TREEVIEW_ITEM *editedItem = infoPtr->selectedItem;
    35903128  NMTVDISPINFOW tvdi;
    3591   DWORD dwStyle = GetWindowLongA(hwnd,GWL_STYLE);
     3129  DWORD dwStyle = GetWindowLongA(infoPtr->hwnd,GWL_STYLE);
    35923130  BOOL bCommit;
    35933131  WCHAR *textW = NULL;
     
    35993137
    36003138  tvdi.item.mask        = 0;
    3601   tvdi.item.hItem       = editedItem->hItem;
     3139  tvdi.item.hItem       = editedItem;
    36023140  tvdi.item.state       = editedItem->state;
    36033141  tvdi.item.lParam      = editedItem->lParam;
     
    36123150      INT len = iLength+1;
    36133151
    3614       textA = (CHAR*)COMCTL32_Alloc(len*sizeof(CHAR));
     3152      textA = (CHAR*)COMCTL32_Alloc(1024*sizeof(CHAR));
    36153153      lstrcpynWtoA(textA,textW,len);
    36163154      tvdi.item.pszText = (WCHAR*)textA;
     
    36233161  }
    36243162
    3625   bCommit = (BOOL)sendNotify(hwnd,isUnicodeNotify(&infoPtr->header) ? TVN_ENDLABELEDITW:TVN_ENDLABELEDITA,&tvdi.hdr);
     3163  bCommit = (BOOL)sendNotify(infoPtr->hwnd,isUnicodeNotify(&infoPtr->header) ? TVN_ENDLABELEDITW:TVN_ENDLABELEDITA,&tvdi.hdr);
    36263164
    36273165  if (!bCancel && bCommit) /* Apply the changes */
    36283166  {
    3629     WCHAR* text;
    36303167    BOOL mustFree = FALSE;
    36313168
     
    36333170      lstrcpynAtoW(textW,textA,iLength+1);
    36343171
    3635     if (editedItem->pszText == LPSTR_TEXTCALLBACKW)
    3636       text = TREEVIEW_CallbackText(hwnd,editedItem,&mustFree);
    3637     else
    3638       text = editedItem->pszText;
    3639 
    3640     if (lstrcmpW(textW,text) != 0)
    3641     {
    3642       if (editedItem->pszText == LPSTR_TEXTCALLBACKW)
     3172    if (lstrcmpW(textW,editedItem->pszText) != 0)
     3173    {
     3174      if (editedItem->callbackMask & TVIF_TEXT)
    36433175      {
    36443176        NMTVDISPINFOW tvdi;
     
    36463178
    36473179        tvdi.item.mask        = TVIF_TEXT;
    3648         tvdi.item.hItem       = editedItem->hItem;
     3180        tvdi.item.hItem       = editedItem;
    36493181        tvdi.item.state       = editedItem->state;
    36503182        tvdi.item.lParam      = editedItem->lParam;
     
    36593191        }
    36603192
    3661         retval = (BOOL)sendNotify(hwnd,isUnicodeNotify(&infoPtr->header) ? TVN_SETDISPINFOW:TVN_SETDISPINFOA,&tvdi.hdr);
     3193        retval = (BOOL)sendNotify(infoPtr->hwnd,isUnicodeNotify(&infoPtr->header) ? TVN_SETDISPINFOW:TVN_SETDISPINFOA,&tvdi.hdr);
    36623194      } else
    36633195      {
    3664         if (!COMCTL32_ReAlloc(text,(iLength+1)*sizeof(WCHAR)))
     3196        if (!COMCTL32_ReAlloc(editedItem->pszText,(iLength+1)*sizeof(WCHAR)))
    36653197        {
    36663198          //ERR("OutOfMemory, cannot allocate space for label");
     
    36693201          if (textA) COMCTL32_Free(textA);
    36703202          if (textW) COMCTL32_Free(textW);
    3671           if (mustFree) COMCTL32_Free(text);
    36723203
    36733204          return FALSE;
     
    36793210      }
    36803211    }
    3681     if (mustFree) COMCTL32_Free(text);
    36823212  }
    36833213
     
    36853215  DestroyWindow(infoPtr->hwndEdit);
    36863216  infoPtr->hwndEdit = 0;
    3687   infoPtr->editItem = 0;
    36883217  if (textA) COMCTL32_Free(textA);
    36893218  if (textW) COMCTL32_Free(textW);
    36903219
    36913220  editedItem->calculated = FALSE;
    3692   TREEVIEW_CalcItem(hwnd,0,dwStyle,infoPtr,editedItem);
    3693   TREEVIEW_RefreshItem(hwnd,editedItem,TRUE);
     3221  TREEVIEW_RefreshItem(infoPtr,editedItem,0);
    36943222
    36953223  return TRUE;
    36963224}
    36973225
     3226static LRESULT
     3227TREEVIEW_HandleTimer (TREEVIEW_INFO *infoPtr, WPARAM wParam, LPARAM lParam)
     3228{
     3229  switch (wParam)
     3230  {
     3231    case TV_REFRESH_TIMER:
     3232      KillTimer(infoPtr->hwnd,TV_REFRESH_TIMER);
     3233      infoPtr->Timer &= ~TV_REFRESH_TIMER_SET;
     3234      TREEVIEW_CalcItems(infoPtr);
     3235      TREEVIEW_Refresh(infoPtr);
     3236      return 0;
     3237
     3238    case TV_EDIT_TIMER:
     3239      KillTimer(infoPtr->hwnd,TV_EDIT_TIMER);
     3240      infoPtr->Timer &= ~TV_EDIT_TIMER_SET;
     3241      TREEVIEW_EditLabel(infoPtr,infoPtr->selectedItem,TRUE);
     3242      return 0;
     3243
     3244    case TV_INFOTIP_TIMER:
     3245      TREEVIEW_CheckInfoTip(infoPtr);
     3246      return 0;
     3247 }
     3248
     3249 return 1;
     3250}
     3251
     3252/* Mouse Tracking/Drag **************************************************/
    36983253
    36993254/***************************************************************************
     
    37013256 * Windows.
    37023257 */
    3703 static LRESULT TREEVIEW_TrackMouse(HWND hwnd, POINT pt)
    3704 {
    3705    INT cxDrag = GetSystemMetrics(SM_CXDRAG);
    3706    INT cyDrag = GetSystemMetrics(SM_CYDRAG);
    3707    RECT r;
    3708    MSG msg;
    3709 
    3710    r.top    = pt.y - cyDrag;
    3711    r.left   = pt.x - cxDrag;
    3712    r.bottom = pt.y + cyDrag;
    3713    r.right  = pt.x + cxDrag;
    3714 
    3715    SetCapture(hwnd);
    3716 
    3717    while(1)
    3718    {
    3719       if (PeekMessageA(&msg, 0, 0, 0, PM_REMOVE|PM_NOYIELD))
    3720       {
    3721          if (msg.message == WM_MOUSEMOVE)
    3722          {
    3723             pt.x = (INT)LOWORD(msg.lParam);
    3724             pt.y = (INT)HIWORD(msg.lParam);
    3725             if (PtInRect(&r, pt))
    3726                continue;
    3727             else
     3258static LRESULT
     3259TREEVIEW_TrackMouse(TREEVIEW_INFO *infoPtr, POINT pt)
     3260{
     3261    INT cxDrag = GetSystemMetrics(SM_CXDRAG);
     3262    INT cyDrag = GetSystemMetrics(SM_CYDRAG);
     3263    RECT r;
     3264    MSG msg;
     3265
     3266    r.top = pt.y - cyDrag;
     3267    r.left = pt.x - cxDrag;
     3268    r.bottom = pt.y + cyDrag;
     3269    r.right = pt.x + cxDrag;
     3270
     3271    SetCapture(infoPtr->hwnd);
     3272
     3273    while (1)
     3274    {
     3275        if (PeekMessageA(&msg, 0, 0, 0, PM_REMOVE | PM_NOYIELD))
     3276        {
     3277            if (msg.message == WM_MOUSEMOVE)
    37283278            {
    3729                ReleaseCapture();
    3730                return 1;
     3279                pt.x = (INT)LOWORD(msg.lParam);
     3280                pt.y = (INT)HIWORD(msg.lParam);
     3281                if (PtInRect(&r, pt))
     3282                    continue;
     3283                else
     3284                {
     3285                    ReleaseCapture();
     3286                    return 1;
     3287                }
    37313288            }
    3732          }
    3733          else if (msg.message >= WM_LBUTTONDOWN &&
    3734                   msg.message <= WM_RBUTTONDBLCLK)
    3735          {
    3736             if (msg.message == WM_RBUTTONUP)
    3737               TREEVIEW_RButtonUp (hwnd, &pt);
    3738             break;
    3739          }
    3740 
    3741          DispatchMessageA( &msg );
    3742       }
    3743 
    3744       if (GetCapture() != hwnd)
    3745          return 0;
    3746    }
    3747 
    3748    ReleaseCapture();
    3749    return 0;
    3750 }
    3751 
    3752 static LRESULT
    3753 TREEVIEW_LButtonDoubleClick (HWND hwnd, WPARAM wParam, LPARAM lParam)
    3754 {
    3755   TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
     3289            else if (msg.message >= WM_LBUTTONDOWN &&
     3290                     msg.message <= WM_RBUTTONDBLCLK)
     3291            {
     3292                if (msg.message == WM_RBUTTONUP)
     3293                    TREEVIEW_RButtonUp(infoPtr, &pt);
     3294                break;
     3295            }
     3296
     3297            DispatchMessageA(&msg);
     3298        }
     3299
     3300        if (GetCapture() != infoPtr->hwnd)
     3301            return 0;
     3302    }
     3303
     3304    ReleaseCapture();
     3305    return 0;
     3306}
     3307
     3308static LRESULT
     3309TREEVIEW_LButtonDoubleClick(TREEVIEW_INFO *infoPtr, WPARAM wParam, LPARAM lParam)
     3310{
    37563311  TREEVIEW_ITEM *wineItem;
    3757   TV_HITTESTINFO hitinfo;
    3758 
    3759   hitinfo.pt.x = (INT)LOWORD(lParam);
    3760   hitinfo.pt.y = (INT)HIWORD(lParam);
    3761   SetFocus (hwnd);
     3312  TV_HITTESTINFO hit;
     3313
     3314  hit.pt.x = (INT)LOWORD(lParam);
     3315  hit.pt.y = (INT)HIWORD(lParam);
     3316  SetFocus (infoPtr->hwnd);
    37623317
    37633318  if (infoPtr->Timer & TV_EDIT_TIMER_SET)
    37643319  {
    37653320     /* If there is pending 'edit label' event - kill it now */
    3766      infoPtr->editItem = 0;
    3767      KillTimer (hwnd, TV_EDIT_TIMER);
    3768   }
    3769 
    3770   if (!TREEVIEW_HitTest(hwnd,&hitinfo,FALSE) || !(hitinfo.flags & TVHT_ONITEM)) return FALSE;
    3771   wineItem = &infoPtr->items [(INT)hitinfo.hItem];
     3321     KillTimer(infoPtr->hwnd,TV_EDIT_TIMER);
     3322  }
     3323
     3324  wineItem = (TREEVIEW_ITEM *)TREEVIEW_HitTest(infoPtr,&hit,FALSE);
     3325  if (!wineItem)
     3326    return 0;
    37723327
    37733328  //MSDN documentation says: stop on non zero. but this isn't true in this case
    3774   if (!sendNotify(hwnd,NM_DBLCLK))
    3775     TREEVIEW_Expand (hwnd, (WPARAM) TVE_TOGGLE, (LPARAM) wineItem->hItem);
     3329  if (!sendNotify(infoPtr->hwnd,NM_DBLCLK))
     3330  {                             /* FIXME! */
     3331    switch (hit.flags)
     3332    {
     3333      case TVHT_ONITEMRIGHT:
     3334        /* FIXME: we should not have send NM_DBLCLK in this case. */
     3335        break;
     3336
     3337      case TVHT_ONITEMINDENT:
     3338        if (!(infoPtr->dwStyle & TVS_HASLINES))
     3339        {
     3340          break;
     3341        } else
     3342        {
     3343          int level = hit.pt.x / infoPtr->uIndent;
     3344
     3345          if (!(infoPtr->dwStyle & TVS_LINESATROOT)) level++;
     3346
     3347          while (wineItem->iLevel > level)
     3348          {
     3349            wineItem = wineItem->parent;
     3350          }
     3351
     3352          /* fall through */
     3353        }
     3354
     3355      case TVHT_ONITEMLABEL:
     3356      case TVHT_ONITEMICON:
     3357      case TVHT_ONITEMSTATEICON:
     3358      case TVHT_ONITEMBUTTON:
     3359        TREEVIEW_Toggle(infoPtr, wineItem, TRUE);
     3360        break;
     3361    }
     3362 }
    37763363
    37773364 return TRUE;
    37783365}
    37793366
    3780 HTREEITEM TREEVIEW_GetInfoTipItem(HWND hwnd,TREEVIEW_INFO *infoPtr,POINT pt)
    3781 {
    3782   TVHITTESTINFO ht;
    3783 
    3784   ht.pt = pt;
    3785   TREEVIEW_HitTest(hwnd,&ht,FALSE);
    3786 
    3787   if (ht.hItem && (ht.flags & TVHT_ONITEM))
    3788   {
    3789     TREEVIEW_ITEM *item;
    3790 
    3791     item = &infoPtr->items[(INT)ht.hItem];
    3792     if (item->visible && ((item->text.left < 0) || (item->text.right > infoPtr->uVisibleWidth)))
    3793       return ht.hItem;
    3794   }
    3795 
    3796   //check tool rect -> no flickering on tip frame
    3797   if (infoPtr->tipItem)
    3798   {
    3799     POINT pt2 = pt;
    3800     RECT rect;
    3801 
    3802     GetWindowRect(infoPtr->hwndToolTip,&rect);
    3803     ClientToScreen(hwnd,&pt2);
    3804     return PtInRect(&rect,pt2) ? infoPtr->tipItem:0;
    3805   }
    3806 
    3807   return 0;
    3808 }
    3809 
    3810 VOID TREEVIEW_ShowInfoTip(HWND hwnd,TREEVIEW_INFO *infoPtr,TREEVIEW_ITEM *item)
    3811 {
    3812   LPWSTR text;
    3813   BOOL mustFree = FALSE;
    3814   TTTOOLINFOW ti;
    3815   POINT pt;
    3816   DWORD dwStyle = GetWindowLongA(hwnd,GWL_STYLE);
    3817 
    3818   if (dwStyle & TVS_INFOTIP)
    3819   {
    3820     NMTVGETINFOTIPW tvgit;
    3821     WCHAR* buf = (WCHAR*)COMCTL32_Alloc(isUnicodeNotify(&infoPtr->header) ? INFOTIPSIZE*sizeof(WCHAR):INFOTIPSIZE*sizeof(CHAR));
    3822 
    3823     tvgit.pszText    = buf;
    3824     tvgit.cchTextMax = INFOTIPSIZE;
    3825     tvgit.hItem      = item->hItem;
    3826     tvgit.lParam     = item->lParam;
    3827 
    3828     sendNotify(hwnd,isUnicodeNotify(&infoPtr->header) ? TVN_GETINFOTIPW:TVN_GETINFOTIPA,&tvgit.hdr);
    3829     if (isUnicodeNotify(&infoPtr->header))
    3830     {
    3831       if (buf != tvgit.pszText) COMCTL32_Free(buf); else mustFree = TRUE;
    3832       text = tvgit.pszText;
    3833     } else
    3834     {
    3835       text = (WCHAR*)COMCTL32_Alloc(tvgit.cchTextMax*sizeof(WCHAR));
    3836       lstrcpyAtoW(text,(LPSTR)tvgit.pszText);
    3837       COMCTL32_Free(buf);
    3838     }
    3839   } else
    3840   {
    3841     if (item->pszText == LPSTR_TEXTCALLBACKW)
    3842       text = TREEVIEW_CallbackText(hwnd,item,&mustFree);
    3843     else
    3844       text = item->pszText;
    3845   }
    3846 
    3847   infoPtr->tipItem = item->hItem;
    3848   SetTimer(hwnd,TV_INFOTIP_TIMER,TV_INFOTIP_DELAY,0);
    3849   infoPtr->Timer |= TV_INFOTIP_TIMER_SET;
    3850 
    3851   ti.cbSize   = sizeof(ti);
    3852   ti.uId      = 0;
    3853   ti.hwnd     = hwnd;
    3854   ti.hinst    = 0;
    3855   ti.lpszText = text;
    3856   SendMessageA(infoPtr->hwndToolTip,TTM_TRACKACTIVATE,(WPARAM)FALSE,(LPARAM)&ti);
    3857   pt.x = item->text.left;
    3858   pt.y = item->text.top;
    3859   ClientToScreen(hwnd,&pt);
    3860   SendMessageA(infoPtr->hwndToolTip,TTM_TRACKPOSITION,0,(LPARAM)MAKELPARAM(pt.x,pt.y));
    3861   SendMessageA(infoPtr->hwndToolTip,TTM_UPDATETIPTEXTW,0,(LPARAM)&ti);
    3862   SendMessageA(infoPtr->hwndToolTip,TTM_TRACKACTIVATE,(WPARAM)TRUE,(LPARAM)&ti);
    3863 
    3864   if (mustFree) COMCTL32_Free(text);
    3865 }
    3866 
    3867 VOID TREEVIEW_HideInfoTip(HWND hwnd,TREEVIEW_INFO *infoPtr)
    3868 {
    3869   if (infoPtr->tipItem)
    3870   {
    3871     TTTOOLINFOA ti;
    3872 
    3873     infoPtr->tipItem = 0;
    3874     KillTimer(hwnd,TV_INFOTIP_TIMER);
    3875     infoPtr->Timer &= ~TV_INFOTIP_TIMER_SET;
    3876 
    3877     ti.cbSize   = sizeof(TTTOOLINFOA);
    3878     ti.uId      = 0;
    3879     ti.hwnd     = (UINT)hwnd;
    3880 
    3881     SendMessageA(infoPtr->hwndToolTip,TTM_TRACKACTIVATE,(WPARAM)FALSE,(LPARAM)&ti);
    3882   }
    3883 }
    3884 
    3885 static VOID TREEVIEW_CheckInfoTip(HWND hwnd)
    3886 {
    3887   TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
    3888   HTREEITEM hItem;
    3889   POINT pt;
    3890 
    3891   GetCursorPos(&pt);
    3892   ScreenToClient(hwnd,&pt);
    3893   hItem = TREEVIEW_GetInfoTipItem(hwnd,infoPtr,pt);
    3894 
    3895   if (hItem != infoPtr->tipItem)
    3896     TREEVIEW_HideInfoTip(hwnd,infoPtr);
    3897 }
    3898 
    3899 HTREEITEM TREEVIEW_GetHottrackItem(HWND hwnd,TREEVIEW_INFO *infoPtr,POINT pt)
    3900 {
    3901   TVHITTESTINFO ht;
    3902 
    3903   ht.pt = pt;
    3904   TREEVIEW_HitTest(hwnd,&ht,FALSE);
    3905 
    3906   return (ht.hItem && (ht.flags & TVHT_ONITEM)) ? ht.hItem:0;
    3907 }
    3908 
    3909 static LRESULT TREEVIEW_MouseMove(HWND hwnd,WPARAM wParam,LPARAM lParam)
    3910 {
    3911   TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
    3912   DWORD dwStyle = GetWindowLongA(hwnd,GWL_STYLE);
    3913   POINT pt;
    3914 
    3915   pt.x = (INT)LOWORD(lParam);
    3916   pt.y = (INT)HIWORD(lParam);
    3917 
    3918   if (infoPtr->hwndToolTip)
    3919   {
    3920     HTREEITEM hItem = TREEVIEW_GetInfoTipItem(hwnd,infoPtr,pt);
    3921 
    3922     if (infoPtr->tipItem != hItem)
    3923     {
    3924       if (hItem)
    3925       {
    3926         TREEVIEW_ITEM *item;
    3927 
    3928         item = &infoPtr->items[(INT)hItem];
    3929         TREEVIEW_ShowInfoTip(hwnd,infoPtr,item);
    3930       } else TREEVIEW_HideInfoTip(hwnd,infoPtr);
    3931     }
    3932   }
    3933 
    3934   if (dwStyle & TVS_TRACKSELECT)
    3935   {
    3936     HTREEITEM hItem = TREEVIEW_GetHottrackItem(hwnd,infoPtr,pt);
    3937 
    3938     if (infoPtr->hotItem != hItem)
    3939     {
    3940       TREEVIEW_ITEM *item;
    3941       HDC hdc = 0;
    3942 
    3943       item =  TREEVIEW_ValidItem(infoPtr,infoPtr->hotItem);
    3944       if (item)
    3945       {
    3946         if (!hdc) hdc = GetDC(hwnd);
    3947         TREEVIEW_DrawHottrackLine(hdc,item);
    3948       }
    3949       if (hItem)
    3950       {
    3951         item = &infoPtr->items[(INT)hItem];
    3952         if (item)
    3953         {
    3954           if (!hdc) hdc = GetDC(hwnd);
    3955           TREEVIEW_DrawHottrackLine(hdc,item);
    3956         }
    3957       }
    3958       if (hdc) ReleaseDC(hwnd,hdc);
    3959     }
    3960   }
    3961 
    3962   return 0;
    3963 }
    3964 
    3965 static LRESULT
    3966 TREEVIEW_LButtonDown (HWND hwnd, WPARAM wParam, LPARAM lParam)
    3967 {
    3968   TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
     3367static LRESULT
     3368TREEVIEW_LButtonDown(TREEVIEW_INFO *infoPtr, WPARAM wParam, LPARAM lParam)
     3369{
    39693370  TVHITTESTINFO ht;
    39703371  BOOL bTrack;
    3971   DWORD dwStyle = GetWindowLongA (hwnd, GWL_STYLE);
    39723372
    39733373  /* If Edit control is active - kill it and return.
     
    39783378  if (infoPtr->hwndEdit)
    39793379  {
    3980      SetFocus (hwnd);
     3380     SetFocus(infoPtr->hwnd);
    39813381     return 0;
    39823382  }
     
    39853385  ht.pt.y = (INT)HIWORD(lParam);
    39863386
    3987   TREEVIEW_HitTest (hwnd,&ht,FALSE);
    3988 
    3989   bTrack = (ht.flags & TVHT_ONITEM) && !(dwStyle & TVS_DISABLEDRAGDROP);
     3387  TREEVIEW_HitTest(infoPtr,&ht,FALSE);
     3388
     3389  bTrack = (ht.flags & TVHT_ONITEM) && !(infoPtr->dwStyle & TVS_DISABLEDRAGDROP);
    39903390
    39913391  /* Send NM_CLICK right away */
    39923392  if (!bTrack)
    3993      if (sendNotify(hwnd,NM_CLICK))
     3393     if (sendNotify(infoPtr->hwnd,NM_CLICK))
    39943394        goto setfocus;
    39953395
    39963396  if (ht.flags & TVHT_ONITEMBUTTON)
    39973397  {
    3998     TREEVIEW_Expand (hwnd, (WPARAM) TVE_TOGGLE, (LPARAM) ht.hItem);
     3398    TREEVIEW_Toggle(infoPtr,ht.hItem,TRUE);
    39993399    goto setfocus;
    40003400  } else if (bTrack)
    40013401  {
    4002     if (TREEVIEW_TrackMouse(hwnd, ht.pt))
    4003     {
    4004       TREEVIEW_SendTreeviewDnDNotify (hwnd,isUnicodeNotify(&infoPtr->header) ? TVN_BEGINDRAGW:TVN_BEGINDRAGA, ht.hItem, ht.pt);
     3402    if (TREEVIEW_TrackMouse(infoPtr,ht.pt))
     3403    {
     3404      TREEVIEW_SendTreeviewDnDNotify(infoPtr,isUnicodeNotify(&infoPtr->header) ? TVN_BEGINDRAGW:TVN_BEGINDRAGA, ht.hItem, ht.pt);
    40053405      infoPtr->dropItem = ht.hItem;
    40063406      return 0;
     
    40083408  }
    40093409
    4010   if (sendNotify(hwnd,NM_CLICK))
     3410  if (sendNotify(infoPtr->hwnd,NM_CLICK))
    40113411     goto setfocus;
    40123412
     
    40153415   * and the click occured on the item label...
    40163416   */
    4017   if ((dwStyle & TVS_EDITLABELS) && (ht.flags & TVHT_ONITEMLABEL) && (infoPtr->selectedItem == ht.hItem))
     3417  if ((infoPtr->dwStyle & TVS_EDITLABELS) && (ht.flags & TVHT_ONITEMLABEL) && (infoPtr->selectedItem == ht.hItem))
    40183418  {
    40193419    if (infoPtr->Timer & TV_EDIT_TIMER_SET)
    4020       KillTimer (hwnd, TV_EDIT_TIMER);
    4021 
    4022     infoPtr->editItem = ht.hItem;
    4023     SetTimer (hwnd, TV_EDIT_TIMER, GetDoubleClickTime(), 0);
    4024     infoPtr->Timer|=TV_EDIT_TIMER_SET;
     3420      KillTimer(infoPtr->hwnd,TV_EDIT_TIMER);
     3421
     3422    SetTimer(infoPtr->hwnd,TV_EDIT_TIMER,GetDoubleClickTime(),0);
     3423    infoPtr->Timer |= TV_EDIT_TIMER_SET;
    40253424  } else if (ht.flags & (TVHT_ONITEMLABEL | TVHT_ONITEMICON))
    40263425  {
    4027     TREEVIEW_DoSelectItem(hwnd,TVGN_CARET,ht.hItem,TVC_BYMOUSE);
     3426    TREEVIEW_DoSelectItem(infoPtr,TVGN_CARET,ht.hItem,TVC_BYMOUSE);
    40283427  } else if (ht.flags & TVHT_ONITEMSTATEICON)
    40293428  {
    4030     if (dwStyle & TVS_CHECKBOXES)  /* TVS_CHECKBOXES requires _us_ */
    4031     {                              /* to toggle the current state  */
    4032       int state;
    4033       TREEVIEW_ITEM *wineItem;
    4034 
    4035       wineItem = TREEVIEW_ValidItem(infoPtr, ht.hItem);
    4036 
    4037       state = 1-(wineItem->state>>12);
    4038       wineItem->state &= ~TVIS_STATEIMAGEMASK;
    4039       wineItem->state |= state<<12;
    4040       if (wineItem->visible)
    4041       {
    4042         TREEVIEW_UnqueueRefresh(hwnd,TRUE,TRUE);
    4043         InvalidateRect(hwnd,&wineItem->statebitmap,TRUE);
    4044       }
     3429    if (infoPtr->dwStyle & TVS_CHECKBOXES)
     3430    {
     3431      /* TVS_CHECKBOXES requires us to toggle the current state */
     3432      if (infoPtr->dwStyle & TVS_CHECKBOXES)
     3433        TREEVIEW_ToggleItemState(infoPtr,ht.hItem);
    40453434    }
    40463435  }
    40473436
    40483437setfocus:
    4049   SetFocus (hwnd);
     3438  SetFocus(infoPtr->hwnd);
    40503439
    40513440  return 0;
    40523441}
    40533442
    4054 
    4055 static LRESULT
    4056 TREEVIEW_RButtonDown (HWND hwnd, WPARAM wParam, LPARAM lParam)
    4057 {
    4058  TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
     3443static LRESULT
     3444TREEVIEW_RButtonDown(TREEVIEW_INFO *infoPtr, WPARAM wParam, LPARAM lParam)
     3445{
    40593446   TVHITTESTINFO ht;
    40603447
    40613448   if (infoPtr->hwndEdit)
    40623449   {
    4063       SetFocus(hwnd);
     3450      SetFocus(infoPtr->hwnd);
    40643451      return 0;
    40653452   }
     
    40683455   ht.pt.y = (INT)HIWORD(lParam);
    40693456
    4070    TREEVIEW_HitTest(hwnd,&ht,FALSE);
    4071 
    4072    if (TREEVIEW_TrackMouse(hwnd, ht.pt))
     3457   TREEVIEW_HitTest(infoPtr,&ht,FALSE);
     3458
     3459   if (TREEVIEW_TrackMouse(infoPtr,ht.pt))
    40733460   {
    40743461      if (ht.hItem)
    40753462      {
    4076          TREEVIEW_SendTreeviewDnDNotify (hwnd,isUnicodeNotify(&infoPtr->header) ? TVN_BEGINRDRAGW:TVN_BEGINDRAGA, ht.hItem, ht.pt);
     3463         TREEVIEW_SendTreeviewDnDNotify(infoPtr,isUnicodeNotify(&infoPtr->header) ? TVN_BEGINRDRAGW:TVN_BEGINDRAGA, ht.hItem, ht.pt);
    40773464         infoPtr->dropItem = ht.hItem;
    40783465      }
     
    40803467   else
    40813468   {
    4082       SetFocus(hwnd);
    4083       sendNotify(hwnd,NM_RCLICK);
     3469      SetFocus(infoPtr->hwnd);
     3470      sendNotify(infoPtr->hwnd,NM_RCLICK);
    40843471   }
    40853472
     
    40873474}
    40883475
    4089 static LRESULT TREEVIEW_RButtonDoubleClick(HWND hwnd,WPARAM wParam,LPARAM lParam)
    4090 {
    4091   sendNotify(hwnd,NM_RDBLCLK);
    4092 
    4093   return DefWindowProcA(hwnd,WM_RBUTTONDBLCLK,wParam,lParam);
    4094 }
    4095 
    4096 static LRESULT
    4097 TREEVIEW_RButtonUp (HWND hwnd, LPPOINT pPt)
    4098 {
    4099     POINT pt;
    4100 
    4101     pt.x = pPt->x;
    4102     pt.y = pPt->y;
     3476static LRESULT
     3477TREEVIEW_RButtonUp(TREEVIEW_INFO *infoPtr, LPPOINT pPt)
     3478{
     3479    POINT pt = *pPt;
    41033480
    41043481    /* Change to screen coordinate for WM_CONTEXTMENU */
    4105     ClientToScreen(hwnd, &pt);
     3482    ClientToScreen(infoPtr->hwnd, &pt);
    41063483
    41073484    /* Send the WM_CONTEXTMENU on a right click */
    4108     SendMessageA( hwnd, WM_CONTEXTMENU, (WPARAM) hwnd, MAKELPARAM(pt.x, pt.y));
     3485    SendMessageA( infoPtr->hwnd, WM_CONTEXTMENU, (WPARAM)infoPtr->hwnd, MAKELPARAM(pt.x, pt.y));
    41093486    return 0;
    41103487}
    41113488
    4112 static LRESULT TREEVIEW_CreateDragImage (HWND hwnd, WPARAM wParam, LPARAM lParam)
    4113 {
    4114   TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
     3489static LRESULT TREEVIEW_CreateDragImage (TREEVIEW_INFO *infoPtr, WPARAM wParam, LPARAM lParam)
     3490{
    41153491  TREEVIEW_ITEM *dragItem;
    41163492  INT cx,cy;
     
    41213497  RECT  rc;
    41223498  HFONT hOldFont;
    4123   WCHAR *itemtxt;
    4124   BOOL mustFree = FALSE;
    41253499
    41263500  if (!(infoPtr->himlNormal)) return 0;
    4127   dragItem = TREEVIEW_ValidItem (infoPtr, (HTREEITEM) lParam);
    4128 
    4129   if (!dragItem) return 0;
    4130 
    4131   if (dragItem->pszText == LPSTR_TEXTCALLBACKW)
    4132     itemtxt = TREEVIEW_CallbackText(hwnd,dragItem,&mustFree);
    4133   else
    4134     itemtxt = dragItem->pszText;
     3501  if (!dragItem || !TREEVIEW_ValidItem(infoPtr, dragItem))
     3502    return 0;
     3503
     3504  TREEVIEW_UpdateDispInfo(infoPtr, dragItem, TVIF_TEXT);
    41353505
    41363506  hwtop = GetDesktopWindow();
     
    41393509
    41403510  hOldFont = SelectObject (hdc, infoPtr->hFont);
    4141   GetTextExtentPoint32W (hdc, itemtxt, lstrlenW (itemtxt), &size);
     3511  GetTextExtentPoint32W (hdc, dragItem->pszText, lstrlenW (dragItem->pszText), &size);
    41423512
    41433513  hbmp = CreateCompatibleBitmap (htopdc, size.cx, size.cy);
     
    41593529
    41603530  SetRect (&rc, cx, 0, size.cx,size.cy);
    4161   DrawTextW (hdc, itemtxt, lstrlenW (itemtxt), &rc, DT_LEFT);
     3531  DrawTextW (hdc, dragItem->pszText, lstrlenW(dragItem->pszText), &rc, DT_LEFT);
    41623532  SelectObject (hdc, hOldFont);
    41633533  SelectObject (hdc, hOldbmp);
     
    41683538  DeleteObject (hbmp);
    41693539  ReleaseDC (hwtop, htopdc);
    4170   if (mustFree) COMCTL32_Free(itemtxt);
    41713540
    41723541  return (LRESULT)infoPtr->dragList;
    41733542}
    41743543
    4175 
    4176 static LRESULT
    4177 TREEVIEW_DoSelectItem (HWND hwnd, INT action, HTREEITEM newSelect, INT cause)
    4178 {
    4179   TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
    4180   TREEVIEW_ITEM *prevItem,*wineItem;
    4181   DWORD dwStyle = GetWindowLongA(hwnd,GWL_STYLE);
    4182   INT prevSelect;
     3544/* Selection ************************************************************/
     3545
     3546static LRESULT
     3547TREEVIEW_DoSelectItem(TREEVIEW_INFO *infoPtr, INT action, HTREEITEM newSelect,
     3548                      INT cause)
     3549{
     3550  TREEVIEW_ITEM *prevSelect;
    41833551  BOOL refreshPrev = FALSE,refreshNew = FALSE;
    41843552
    4185   wineItem = TREEVIEW_ValidItem (infoPtr, (HTREEITEM)newSelect);
    4186 
    4187   if (wineItem && wineItem->parent)
    4188   {
    4189     TREEVIEW_ITEM *parent = TREEVIEW_ValidItem(infoPtr,wineItem->parent);
     3553  assert(newSelect == NULL || TREEVIEW_ValidItem(infoPtr, newSelect));
     3554
     3555  //TRACE("Entering item %p (%s), flag %x, cause %x, state %d\n",
     3556  //    newSelect, TREEVIEW_ItemName(newSelect), action, cause,
     3557  //    newSelect ? newSelect->state : 0);
     3558
     3559  if (newSelect && newSelect->parent)
     3560  {
     3561    TREEVIEW_ITEM *parent = newSelect->parent;
    41903562
    41913563    /*
     
    41953567    while (parent && !(parent->state & TVIS_EXPANDED))
    41963568    {
    4197       TREEVIEW_Expand(hwnd,TVE_EXPAND,(LPARAM)parent->hItem);
    4198       parent = TREEVIEW_ValidItem(infoPtr,parent->parent);
    4199     }
    4200   }
     3569      TREEVIEW_Expand(infoPtr,parent,FALSE,FALSE);
     3570      parent = parent->parent;
     3571    }
     3572  }
     3573
    42013574
    42023575  switch (action)
    42033576  {
    42043577    case TVGN_CARET:
    4205       prevSelect = (INT)infoPtr->selectedItem;
    4206 
    4207       if ((HTREEITEM)prevSelect == newSelect)
    4208         return FALSE;
    4209 
    4210       prevItem = TREEVIEW_ValidItem (infoPtr, (HTREEITEM)prevSelect);
     3578      prevSelect = infoPtr->selectedItem;
     3579
     3580      if (prevSelect == newSelect)
     3581          return FALSE;
     3582
     3583      if (TREEVIEW_SendTreeviewNotify(infoPtr,
     3584                                      isUnicodeNotify(&infoPtr->header) ? TVN_SELCHANGINGW:TVN_SELCHANGINGA,
     3585                                      cause,
     3586                                      TVIF_HANDLE | TVIF_STATE | TVIF_PARAM,
     3587                                      prevSelect,
     3588                                      newSelect))
     3589        return FALSE;       /* FIXME: OK? */
     3590
     3591      if (prevSelect)
     3592      {
     3593        refreshPrev = prevSelect->state & TVIS_SELECTED;
     3594        prevSelect->state &= ~TVIS_SELECTED;
     3595      }
    42113596
    42123597      if (newSelect)
    4213         if (TREEVIEW_SendTreeviewNotify(hwnd,isUnicodeNotify(&infoPtr->header) ? TVN_SELCHANGINGW:TVN_SELCHANGINGA,cause,(HTREEITEM)prevSelect,(HTREEITEM)newSelect))
    4214           return FALSE;       /* FIXME: OK? */
    4215 
    4216       if (prevItem)
    42173598      {
    4218         refreshPrev = prevItem->state & TVIS_SELECTED;
    4219         prevItem->state &= ~TVIS_SELECTED;
     3599        refreshNew = !(newSelect->state & TVIS_SELECTED);
     3600        newSelect->state |= TVIS_SELECTED;
    42203601      }
    4221       if (wineItem)
    4222       {
    4223         refreshNew = !(wineItem->state & TVIS_SELECTED);
    4224         wineItem->state |=  TVIS_SELECTED;
    4225       }
    4226 
    4227       infoPtr->selectedItem = (HTREEITEM)newSelect;
    4228 
    4229       if ((cause == TVC_BYMOUSE) && (dwStyle & TVS_SINGLEEXPAND))
     3602
     3603      infoPtr->selectedItem = newSelect;
     3604
     3605      if ((cause == TVC_BYMOUSE) && (infoPtr->dwStyle & TVS_SINGLEEXPAND))
    42303606      {
    42313607        BOOL control = GetKeyState(VK_CONTROL) & 0x8000;
    4232         UINT rc = TREEVIEW_SendTreeviewNotify(hwnd,TVN_SINGLEEXPAND,0,prevItem->hItem,wineItem->hItem);
    4233 
    4234         if (!(rc & TVNRET_SKIPOLD) && !control && prevItem->state & TVIS_EXPANDED)
    4235           TREEVIEW_Expand(hwnd,TVE_COLLAPSE,(LPARAM)prevItem->hItem);
    4236         if (!(rc & TVNRET_SKIPNEW) && TREEVIEW_HasChildren(hwnd,wineItem))
    4237           TREEVIEW_Expand(hwnd,TVE_TOGGLE,(LPARAM)wineItem->hItem);
     3608        UINT rc = TREEVIEW_SendTreeviewNotify(infoPtr,TVN_SINGLEEXPAND,0,TVIF_HANDLE | TVIF_STATE | TVIF_PARAM,prevSelect,newSelect);
     3609
     3610        if (!(rc & TVNRET_SKIPOLD) && !control && (prevSelect->state & TVIS_EXPANDED))
     3611          TREEVIEW_Collapse(infoPtr,prevSelect,FALSE,FALSE);
     3612        if (!(rc & TVNRET_SKIPNEW) && TREEVIEW_HasChildren(infoPtr,newSelect))
     3613          TREEVIEW_Toggle(infoPtr,newSelect,FALSE);
    42383614      }
    42393615
    4240       TREEVIEW_UnqueueRefresh(hwnd,TRUE,TRUE);
    4241       if (dwStyle & TVS_FULLROWSELECT)
     3616      TREEVIEW_UnqueueRefresh(infoPtr,TRUE,TRUE);
     3617      TREEVIEW_EnsureVisible(infoPtr,infoPtr->selectedItem);
     3618      if (infoPtr->dwStyle & TVS_FULLROWSELECT)
    42423619      {
    42433620        TREEVIEW_ITEM *item;
    42443621
    42453622        //deselect last selected row
    4246         if (prevItem)
     3623        if (prevSelect)
    42473624        {
    4248           if (refreshPrev) TREEVIEW_RefreshItem(hwnd,prevItem,FALSE);
    4249           if (prevItem->upsibling)
     3625          if (refreshPrev) TREEVIEW_RefreshItem(infoPtr,prevSelect,TVIF_IMAGE | TVIF_TEXT);
     3626          if (prevSelect->prevSibling)
    42503627          {
    4251             item = &infoPtr->items[(INT)prevItem->upsibling];
     3628            item = prevSelect->prevSibling;
    42523629            while (item)
    42533630            {
     
    42553632              {
    42563633                item->state &= ~TVIS_SELECTED;
    4257                 TREEVIEW_RefreshItem(hwnd,item,FALSE);
     3634                TREEVIEW_RefreshItem(infoPtr,item,TVIF_IMAGE | TVIF_TEXT);
    42583635              }
    4259               item = &infoPtr->items[(INT)item->upsibling];
     3636              item = item->prevSibling;
    42603637            }
    42613638          }
    4262           if (prevItem->sibling)
     3639          if (prevSelect->nextSibling)
    42633640          {
    4264             item = &infoPtr->items[(INT)prevItem->sibling];
     3641            item = prevSelect->nextSibling;
    42653642            while (item)
    42663643            {
     
    42683645              {
    42693646                item->state &= ~TVIS_SELECTED;
    4270                 TREEVIEW_RefreshItem(hwnd,item,FALSE);
     3647                TREEVIEW_RefreshItem(infoPtr,item,TVIF_IMAGE | TVIF_TEXT);
    42713648              }
    4272               item = &infoPtr->items[(INT)item->sibling];
     3649              item = item->nextSibling;
    42733650            }
    42743651          }
     
    42763653
    42773654        //select new row
    4278         if (wineItem)
     3655        if (newSelect)
    42793656        {
    4280           if (refreshNew) TREEVIEW_RefreshItem(hwnd,wineItem,FALSE);
    4281           if (wineItem->upsibling)
     3657          if (refreshNew) TREEVIEW_RefreshItem(infoPtr,newSelect,TVIF_IMAGE | TVIF_TEXT);
     3658          if (newSelect->prevSibling)
    42823659          {
    4283             item = &infoPtr->items[(INT)wineItem->upsibling];
     3660            item = newSelect->prevSibling;
    42843661            while (item)
    42853662            {
     
    42873664              {
    42883665                item->state |= TVIS_SELECTED;
    4289                 TREEVIEW_RefreshItem(hwnd,item,FALSE);
     3666                TREEVIEW_RefreshItem(infoPtr,item,TVIF_IMAGE | TVIF_TEXT);
    42903667              }
    4291               item = &infoPtr->items[(INT)item->upsibling];
     3668              item = item->prevSibling;
    42923669            }
    42933670          }
    4294           if (wineItem->sibling)
     3671          if (newSelect->nextSibling)
    42953672          {
    4296             item =  &infoPtr->items[(INT)wineItem->sibling];
     3673            item =  newSelect->nextSibling;
    42973674            while (item)
    42983675            {
     
    43003677              {
    43013678                item->state |= TVIS_SELECTED;
    4302                 TREEVIEW_RefreshItem(hwnd,item,FALSE);
     3679                TREEVIEW_RefreshItem(infoPtr,item,TVIF_IMAGE | TVIF_TEXT);
    43033680              }
    4304               item = &infoPtr->items[(INT)item->sibling];
     3681              item = item->nextSibling;
    43053682            }
    43063683          }
     
    43083685      } else
    43093686      {
    4310         if (refreshPrev) TREEVIEW_RefreshItem(hwnd,prevItem,FALSE);
    4311         if (refreshNew) TREEVIEW_RefreshItem(hwnd,wineItem,FALSE);
     3687        if (refreshPrev) TREEVIEW_RefreshItem(infoPtr,prevSelect,TVIF_IMAGE | TVIF_TEXT);
     3688        if (refreshNew) TREEVIEW_RefreshItem(infoPtr,newSelect,TVIF_IMAGE | TVIF_TEXT);
    43123689      }
    43133690
    4314       TREEVIEW_SendTreeviewNotify(hwnd,isUnicodeNotify(&infoPtr->header) ? TVN_SELCHANGEDW:TVN_SELCHANGEDA,cause,(HTREEITEM)prevSelect,(HTREEITEM)newSelect);
    4315 
     3691      TREEVIEW_SendTreeviewNotify(infoPtr,
     3692                                  isUnicodeNotify(&infoPtr->header) ? TVN_SELCHANGEDW:TVN_SELCHANGEDA,
     3693                                  cause,
     3694                                  TVIF_HANDLE | TVIF_STATE | TVIF_PARAM,
     3695                                  prevSelect,
     3696                                  newSelect);
    43163697      break;
    43173698
    43183699    case TVGN_DROPHILITE:
    4319       prevItem = TREEVIEW_ValidItem (infoPtr, infoPtr->dropItem);
    4320 
    4321       if (prevItem)
     3700      prevSelect = infoPtr->dropItem;
     3701
     3702      if (prevSelect)
    43223703      {
    4323         refreshPrev = prevItem->state & TVIS_DROPHILITED;
    4324         prevItem->state &= ~TVIS_DROPHILITED;
     3704        refreshPrev = prevSelect->state & TVIS_DROPHILITED;
     3705        prevSelect->state &= ~TVIS_DROPHILITED;
    43253706      }
    43263707
    4327       infoPtr->dropItem = (HTREEITEM)newSelect;
    4328 
    4329       if (wineItem)
     3708      infoPtr->dropItem = newSelect;
     3709
     3710      if (newSelect)
    43303711      {
    4331         refreshNew = !(wineItem->state & TVIS_DROPHILITED);
    4332         wineItem->state |=TVIS_DROPHILITED;
     3712        refreshNew = !(newSelect->state & TVIS_DROPHILITED);
     3713        newSelect->state |= TVIS_DROPHILITED;
    43333714      }
    43343715
    4335       TREEVIEW_UnqueueRefresh(hwnd,TRUE,TRUE);
    4336       if (refreshPrev) TREEVIEW_RefreshItem(hwnd,prevItem,FALSE);
    4337       if (refreshNew) TREEVIEW_RefreshItem(hwnd,wineItem,FALSE);
     3716      TREEVIEW_UnqueueRefresh(infoPtr,TRUE,TRUE);
     3717      if (refreshPrev) TREEVIEW_RefreshItem(infoPtr,prevSelect,TVIF_IMAGE | TVIF_TEXT);
     3718      if (refreshNew) TREEVIEW_RefreshItem(infoPtr,newSelect,TVIF_IMAGE | TVIF_TEXT);
    43383719
    43393720      break;
     
    43463727      if (!infoPtr->firstVisible) return FALSE;
    43473728
    4348       firstVis = &infoPtr->items[(INT)infoPtr->firstVisible];
    4349 
    4350       if (wineItem->rect.top < 0)
    4351         scrollY = wineItem->rect.top;
     3729      firstVis = infoPtr->firstVisible;
     3730
     3731      if (newSelect->rect.top < 0)
     3732        scrollY = newSelect->rect.top;
    43523733      else
    43533734      {
    4354         scrollY = MIN(wineItem->rect.top,(infoPtr->uTotalHeight-infoPtr->uVisibleHeight)-infoPtr->lefttop.y);
    4355         scrollY -= scrollY % infoPtr->uVScrollStep;
     3735        scrollY = MIN(newSelect->rect.top,(infoPtr->treeHeight-infoPtr->clientHeight)-infoPtr->lefttop.y);
     3736        scrollY -= scrollY % infoPtr->uItemHeight;
    43563737      }
    43573738
     
    43593740      {
    43603741        infoPtr->lefttop.y += scrollY;
    4361         if (!TREEVIEW_UnqueueRefresh(hwnd,TRUE,TRUE))
    4362           TREEVIEW_CalcItems(hwnd,0,infoPtr);
    4363 
    4364         ScrollWindowEx(hwnd,0,-scrollY,NULL,NULL,0,NULL,SW_INVALIDATE | SW_SCROLLCHILDREN | (infoPtr->uScrollTime << 16));
     3742        if (!TREEVIEW_UnqueueRefresh(infoPtr,TRUE,TRUE))
     3743          TREEVIEW_CalcItems(infoPtr);
     3744
     3745        ScrollWindowEx(infoPtr->hwnd,0,-scrollY,NULL,NULL,0,NULL,SW_INVALIDATE | SW_SCROLLCHILDREN | (infoPtr->uScrollTime << 16));
    43653746      }
    43663747
    43673748      break;
    43683749    }
    4369  }
    4370 
    4371  return TRUE;
    4372 }
    4373 
    4374 static LRESULT
    4375 TREEVIEW_SelectItem (HWND hwnd, WPARAM wParam, LPARAM lParam)
    4376 {
    4377   return TREEVIEW_DoSelectItem(hwnd,wParam,(HTREEITEM)lParam,TVC_UNKNOWN);
    4378 }
    4379 
    4380 static LRESULT
    4381 TREEVIEW_GetFont (HWND hwnd, WPARAM wParam, LPARAM lParam)
    4382 {
    4383   TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
    4384 
    4385   return infoPtr->hFont;
    4386 }
    4387 
    4388 static LRESULT
    4389 TREEVIEW_SetFont (HWND hwnd, WPARAM wParam, LPARAM lParam)
    4390 {
    4391   TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
    4392   TEXTMETRICA tm;
    4393   LOGFONTA logFont;
    4394   HFONT hFont, hOldFont;
    4395   INT height;
    4396   HDC hdc;
    4397 
    4398   infoPtr->hFont = (HFONT)wParam;
    4399 
    4400   hFont = infoPtr->hFont ? infoPtr->hFont : GetStockObject (SYSTEM_FONT);
    4401 
    4402   GetObjectA (infoPtr->hFont, sizeof (LOGFONTA), &logFont);
    4403   logFont.lfWeight = FW_BOLD;
    4404   DeleteObject(infoPtr->hBoldFont);
    4405   infoPtr->hBoldFont = CreateFontIndirectA (&logFont);
    4406 
    4407   SendMessageA(infoPtr->hwndToolTip,WM_SETFONT,infoPtr->hFont,1);
    4408   infoPtr->uInternalStatus |= TV_CALCALL;
    4409   TREEVIEW_CalcItems(hwnd,0,infoPtr);
    4410 
    4411   if (lParam)
    4412   {
    4413     TREEVIEW_UnqueueRefresh(hwnd,FALSE,FALSE);
    4414     TREEVIEW_Refresh(hwnd);
    4415   }
    4416 
    4417   return 0;
    4418 }
    4419 
    4420 static LRESULT
    4421 TREEVIEW_VScroll(HWND hwnd,WPARAM wParam,LPARAM lParam)
    4422 {
    4423   TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
     3750  }
     3751
     3752  //TRACE("Leaving state %d\n", newSelect ? newSelect->state : 0);
     3753  return TRUE;
     3754}
     3755
     3756/* FIXME: handle NM_KILLFOCUS etc */
     3757static LRESULT
     3758TREEVIEW_SelectItem(TREEVIEW_INFO *infoPtr, WPARAM wParam, LPARAM lParam)
     3759{
     3760    HTREEITEM item = (HTREEITEM)lParam;
     3761
     3762    if (item != NULL && !TREEVIEW_ValidItem(infoPtr, item))
     3763        return FALSE;
     3764
     3765    //TRACE("%p (%s) %d\n", item, TREEVIEW_ItemName(item), (int)wParam);
     3766
     3767    if (!TREEVIEW_DoSelectItem(infoPtr, wParam, item, TVC_UNKNOWN))
     3768        return FALSE;
     3769
     3770    return TRUE;
     3771}
     3772
     3773/* Scrolling ************************************************************/
     3774
     3775static LRESULT TREEVIEW_EnsureVisible(TREEVIEW_INFO *infoPtr,HTREEITEM item)
     3776{
     3777  RECT rect;
     3778  INT scrollY;
     3779
     3780  if (!item) return FALSE;
     3781
     3782  if (!TREEVIEW_ValidItem(infoPtr,item))
     3783    return FALSE;
     3784
     3785  if (item && item->parent)
     3786  {
     3787    TREEVIEW_ITEM *parent = item->parent;
     3788
     3789    while (parent && !(parent->state & TVIS_EXPANDED))
     3790    {
     3791      TREEVIEW_Expand(infoPtr,parent,FALSE,FALSE);
     3792      parent = parent->parent;
     3793    }
     3794  }
     3795
     3796  TREEVIEW_UnqueueRefresh(infoPtr,TRUE,TRUE);
     3797  GetClientRect(infoPtr->hwnd,&rect);
     3798  if (item->rect.top < 0)
     3799    scrollY = item->rect.top;
     3800  else if (item->rect.bottom > rect.bottom)
     3801  {
     3802    INT mod;
     3803
     3804    scrollY = item->rect.bottom-rect.bottom;
     3805    mod = scrollY % infoPtr->uItemHeight;
     3806    if (mod) scrollY += infoPtr->uItemHeight-mod;
     3807  } else return FALSE;
     3808
     3809  if (scrollY != 0)
     3810  {
     3811    infoPtr->lefttop.y += scrollY;
     3812    TREEVIEW_CalcItems(infoPtr);
     3813    ScrollWindowEx(infoPtr->hwnd,0,-scrollY,NULL,NULL,0,NULL,SW_INVALIDATE | SW_SCROLLCHILDREN | (infoPtr->uScrollTime << 16));
     3814
     3815    return TRUE;
     3816  }
     3817
     3818  return FALSE;
     3819}
     3820
     3821static LRESULT
     3822TREEVIEW_VScroll(TREEVIEW_INFO *infoPtr,WPARAM wParam,LPARAM lParam)
     3823{
    44243824  INT newY,mod;
    4425   INT maxY = infoPtr->uTotalHeight-infoPtr->uVisibleHeight;
     3825  INT maxY = infoPtr->treeHeight-infoPtr->clientHeight;
    44263826
    44273827  if (!(infoPtr->uInternalStatus & TV_VSCROLL)) return FALSE;
    44283828
    4429   mod = maxY % infoPtr->uVScrollStep;
    4430   if (mod > 0) maxY += infoPtr->uVScrollStep-mod;
     3829  mod = maxY % infoPtr->uItemHeight;
     3830  if (mod > 0) maxY += infoPtr->uItemHeight-mod;
    44313831
    44323832  switch (LOWORD (wParam))
    44333833  {
    44343834    case SB_LINEUP:
    4435       newY = infoPtr->lefttop.y-infoPtr->uVScrollStep;
     3835      newY = infoPtr->lefttop.y-infoPtr->uItemHeight;
    44363836      if (newY < 0) newY = 0;
    44373837      break;
    44383838
    44393839    case SB_LINEDOWN:
    4440       newY = infoPtr->lefttop.y+infoPtr->uVScrollStep;
     3840      newY = infoPtr->lefttop.y+infoPtr->uItemHeight;
    44413841      if (newY > maxY) newY = maxY;
    44423842      break;
    44433843
    44443844    case SB_PAGEUP:
    4445       newY = infoPtr->lefttop.y-MAX(((INT)(infoPtr->uVisibleHeight/infoPtr->uVScrollStep))*infoPtr->uVScrollStep,infoPtr->uVScrollStep);
     3845      newY = infoPtr->lefttop.y-MAX(((INT)(infoPtr->clientHeight/infoPtr->uItemHeight))*infoPtr->uItemHeight,infoPtr->uItemHeight);
    44463846      if (newY < 0) newY = 0;
    44473847      break;
    44483848
    44493849    case SB_PAGEDOWN:
    4450       newY = infoPtr->lefttop.y+MAX(((INT)(infoPtr->uVisibleHeight/infoPtr->uVScrollStep))*infoPtr->uVScrollStep,infoPtr->uVScrollStep);
     3850      newY = infoPtr->lefttop.y+MAX(((INT)(infoPtr->clientHeight/infoPtr->uItemHeight))*infoPtr->uItemHeight,infoPtr->uItemHeight);
    44513851      if (newY > maxY) newY = maxY;
    44523852      break;
     
    44543854    case SB_THUMBTRACK:
    44553855      newY = HIWORD(wParam);
    4456       mod = newY % infoPtr->uVScrollStep;
     3856      mod = newY % infoPtr->uItemHeight;
    44573857      if (mod > 0) newY -= mod;
    44583858      break;
     
    44653865  {
    44663866    INT scrollY = infoPtr->lefttop.y-newY;
    4467 
    4468     TREEVIEW_HideInfoTip(hwnd,infoPtr);
     3867    SCROLLINFO info;
     3868
     3869    TREEVIEW_HideInfoTip(infoPtr);
    44693870    infoPtr->lefttop.y = newY;
    4470     if (!TREEVIEW_UnqueueRefresh(hwnd,TRUE,TRUE))
    4471       TREEVIEW_CalcItems(hwnd,0,infoPtr);
    4472     ScrollWindowEx(hwnd,0,scrollY,NULL,NULL,0,NULL,SW_INVALIDATE | SW_SCROLLCHILDREN | (infoPtr->uScrollTime << 16));
     3871    if (!TREEVIEW_UnqueueRefresh(infoPtr,TRUE,TRUE))
     3872      TREEVIEW_MoveItems(infoPtr,0,scrollY);
     3873
     3874    info.cbSize = sizeof(info);
     3875    info.nPos   = infoPtr->lefttop.y;
     3876    info.fMask  = SIF_POS;
     3877    SetScrollInfo(infoPtr->hwnd,SB_VERT,&info,TRUE);
     3878
     3879    ScrollWindowEx(infoPtr->hwnd,0,scrollY,NULL,NULL,0,NULL,SW_INVALIDATE | SW_SCROLLCHILDREN | (infoPtr->uScrollTime << 16));
    44733880
    44743881    return TRUE;
     
    44793886
    44803887static LRESULT
    4481 TREEVIEW_HScroll (HWND hwnd, WPARAM wParam, LPARAM lParam)
    4482 {
    4483   TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
     3888TREEVIEW_HScroll(TREEVIEW_INFO *infoPtr,WPARAM wParam,LPARAM lParam)
     3889{
    44843890  int maxWidth;
    44853891  int lastPos = infoPtr->lefttop.x;
     3892  SCROLLINFO info;
    44863893
    44873894  if (!(infoPtr->uInternalStatus & TV_HSCROLL)) return FALSE;
     
    44913898    case SB_LINEUP:
    44923899      if (!infoPtr->lefttop.x) return FALSE;
    4493       infoPtr->lefttop.x -= infoPtr->uRealItemHeight;
     3900      infoPtr->lefttop.x -= infoPtr->uItemHeight;
    44943901      if (infoPtr->lefttop.x < 0) infoPtr->lefttop.x = 0;
    44953902      break;
    44963903
    44973904    case SB_LINEDOWN:
    4498       maxWidth = infoPtr->uTotalWidth-infoPtr->uVisibleWidth;
     3905      maxWidth = infoPtr->treeWidth-infoPtr->clientWidth;
    44993906      if (maxWidth <= 0) return FALSE;
    45003907      if (infoPtr->lefttop.x == maxWidth) return FALSE;
    4501       infoPtr->lefttop.x += infoPtr->uRealItemHeight; /*FIXME */
     3908      infoPtr->lefttop.x += infoPtr->uItemHeight; /*FIXME */
    45023909      if (infoPtr->lefttop.x > maxWidth)
    45033910        infoPtr->lefttop.x = maxWidth;
     
    45063913    case SB_PAGEUP:
    45073914      if (!infoPtr->lefttop.x) return FALSE;
    4508       infoPtr->lefttop.x -= infoPtr->uVisibleWidth;
     3915      infoPtr->lefttop.x -= infoPtr->clientWidth;
    45093916      if (infoPtr->lefttop.x < 0) infoPtr->lefttop.x = 0;
    45103917      break;
    45113918
    45123919    case SB_PAGEDOWN:
    4513       maxWidth = infoPtr->uTotalWidth-infoPtr->uVisibleWidth;
     3920      maxWidth = infoPtr->treeWidth-infoPtr->clientWidth;
    45143921      if (maxWidth <= 0) return FALSE;
    45153922      if (infoPtr->lefttop.x == maxWidth) return FALSE;
    4516       infoPtr->lefttop.x += infoPtr->uVisibleWidth;
     3923      infoPtr->lefttop.x += infoPtr->clientWidth;
    45173924      if (infoPtr->lefttop.x > maxWidth)
    45183925        infoPtr->lefttop.x = maxWidth;
     
    45203927
    45213928    case SB_THUMBTRACK:
    4522       maxWidth = infoPtr->uTotalWidth-infoPtr->uVisibleWidth;
     3929      maxWidth = infoPtr->treeWidth-infoPtr->clientWidth;
    45233930      if (maxWidth <= 0) return FALSE;
    45243931      infoPtr->lefttop.x = HIWORD(wParam);
     
    45333940  if (lastPos != infoPtr->lefttop.x)
    45343941  {
    4535     TREEVIEW_HideInfoTip(hwnd,infoPtr);
    4536     if (!TREEVIEW_UnqueueRefresh(hwnd,TRUE,TRUE))
    4537       TREEVIEW_CalcItems(hwnd,0,infoPtr);
    4538     ScrollWindowEx(hwnd,lastPos-infoPtr->lefttop.x,0,NULL,NULL,0,NULL,SW_INVALIDATE | SW_SCROLLCHILDREN | (infoPtr->uScrollTime << 16));
     3942    TREEVIEW_HideInfoTip(infoPtr);
     3943    if (!TREEVIEW_UnqueueRefresh(infoPtr,TRUE,TRUE))
     3944      TREEVIEW_MoveItems(infoPtr,lastPos-infoPtr->lefttop.x,0);
     3945
     3946    info.cbSize = sizeof(info);
     3947    info.nPos   = infoPtr->lefttop.x;
     3948    info.fMask  = SIF_POS;
     3949    SetScrollInfo(infoPtr->hwnd,SB_HORZ,&info,TRUE);
     3950
     3951    ScrollWindowEx(infoPtr->hwnd,lastPos-infoPtr->lefttop.x,0,NULL,NULL,0,NULL,SW_INVALIDATE | SW_SCROLLCHILDREN | (infoPtr->uScrollTime << 16));
    45393952
    45403953    return TRUE;
     
    45443957}
    45453958
    4546 static LRESULT TREEVIEW_MouseWheel (HWND hwnd, WPARAM wParam, LPARAM lParam)
    4547 {
    4548 
    4549     TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
    4550     short gcWheelDelta = 0;
     3959static LRESULT
     3960TREEVIEW_MouseWheel(TREEVIEW_INFO *infoPtr, WPARAM wParam, LPARAM lParam)
     3961{
     3962    short gcWheelDelta;
    45513963    UINT pulScrollLines = 3;
    45523964
    4553     if (wParam & (MK_SHIFT | MK_CONTROL))
    4554       return DefWindowProcA( hwnd, WM_MOUSEWHEEL, wParam, lParam );
    4555 
    4556 
    4557     SystemParametersInfoW(SPI_GETWHEELSCROLLLINES,0, &pulScrollLines, 0);
    4558 
    4559     gcWheelDelta -= (short) HIWORD(wParam);
     3965    if (infoPtr->firstVisible == NULL)
     3966        return TRUE;
     3967
     3968    SystemParametersInfoW(SPI_GETWHEELSCROLLLINES, 0, &pulScrollLines, 0);
     3969
     3970    gcWheelDelta = -(short)HIWORD(wParam);
    45603971    pulScrollLines *= (gcWheelDelta / WHEEL_DELTA);
    45613972
    45623973    if (abs(gcWheelDelta) >= WHEEL_DELTA && pulScrollLines)
    45633974    {
    4564         int wheelDy = pulScrollLines * infoPtr->uRealItemHeight;
     3975        int wheelDy = pulScrollLines * infoPtr->uItemHeight;
    45653976        int newDy = infoPtr->lefttop.y + wheelDy;
    4566         int maxDy = infoPtr->uTotalHeight-infoPtr->uVisibleHeight;
     3977        int maxDy = infoPtr->treeHeight-infoPtr->clientHeight;
    45673978
    45683979        if (newDy > maxDy) newDy = maxDy;
     3980
    45693981        if (newDy < 0) newDy = 0;
    45703982
    4571         if (newDy == infoPtr->lefttop.y) return TRUE;
    4572 
    4573         TREEVIEW_VScroll(hwnd, MAKEWPARAM(SB_THUMBTRACK,newDy),0);
    4574     }
    4575   return TRUE;
    4576 }
    4577 
    4578 static LRESULT
    4579 TREEVIEW_KeyDown (HWND hwnd, WPARAM wParam, LPARAM lParam)
    4580 {
    4581   TREEVIEW_INFO *infoPtr        = TREEVIEW_GetInfoPtr(hwnd);
    4582   HTREEITEM     hNewSelection   = 0;
    4583   INT           prevSelect      = (INT)infoPtr->selectedItem;
    4584   TREEVIEW_ITEM *prevItem       =
    4585     (prevSelect != 0 ) ?
    4586       TREEVIEW_ValidItem (infoPtr, (HTREEITEM)prevSelect) :
    4587       NULL;
    4588   TREEVIEW_ITEM *newItem        = NULL;
    4589   BOOL control;
    4590 
    4591   TREEVIEW_SendKeyDownNotify(hwnd,TVN_KEYDOWN,wParam);
    4592 
    4593   control = GetKeyState(VK_CONTROL) & 0x8000;
    4594 
    4595   if (control)
    4596   {
    4597     INT scrollMsg = -1;
    4598     BOOL horz = FALSE;
    4599 
    4600     switch (wParam)
    4601     {
    4602       case VK_UP:
    4603         scrollMsg = SB_LINELEFT;
     3983        TREEVIEW_VScroll(infoPtr, MAKEWPARAM(SB_THUMBTRACK, newDy), 0);
     3984    }
     3985    return TRUE;
     3986}
     3987
     3988/* Create/Destroy *******************************************************/
     3989
     3990static LRESULT
     3991TREEVIEW_Create (HWND hwnd, WPARAM wParam, LPARAM lParam)
     3992{
     3993  LPCREATESTRUCTA lpcs = (LPCREATESTRUCTA)lParam;
     3994  TREEVIEW_INFO *infoPtr;
     3995  RECT rcClient;
     3996
     3997  /* allocate memory for info structure */
     3998  infoPtr = (TREEVIEW_INFO*)initControl(hwnd,sizeof(TREEVIEW_INFO));
     3999
     4000  if (!infoPtr) return 0;
     4001
     4002  /* set default settings */
     4003  infoPtr->hwnd = hwnd;
     4004  infoPtr->dwStyle = GetWindowLongA(hwnd, GWL_STYLE);
     4005  infoPtr->uInternalStatus = TV_CALCALL;
     4006  infoPtr->Timer = 0;
     4007  infoPtr->uNumItems = 0;
     4008  infoPtr->cdmode = 0;
     4009  infoPtr->uScrollTime = 300;   /* milliseconds */
     4010
     4011  GetClientRect(hwnd,&rcClient);
     4012
     4013  /* No scroll bars yet. */
     4014  infoPtr->clientWidth = rcClient.right;
     4015  infoPtr->clientHeight = rcClient.bottom;
     4016
     4017  infoPtr->treeWidth = 0;
     4018  infoPtr->treeHeight = 0;
     4019
     4020  infoPtr->uIndent = 19;
     4021  infoPtr->selectedItem = 0;
     4022  infoPtr->firstVisible = 0;
     4023  infoPtr->maxDisplayOrder = 0;
     4024  infoPtr->dropItem = 0;
     4025  infoPtr->insertMarkItem = 0;
     4026  infoPtr->insertBeforeorAfter = 0;
     4027
     4028  infoPtr->clrBk = GetSysColor(COLOR_WINDOW);
     4029  infoPtr->clrText = -1;        /* use system color */
     4030  infoPtr->clrLine = RGB(128, 128, 128);
     4031  infoPtr->clrInsertMark = GetSysColor(COLOR_BTNTEXT);
     4032
     4033  infoPtr->lefttop.y = 0;
     4034  infoPtr->lefttop.x = 0;
     4035
     4036  infoPtr->hwndEdit = 0;
     4037  infoPtr->wpEditOrig = NULL;
     4038  infoPtr->bIgnoreEditKillFocus = FALSE;
     4039  infoPtr->bLabelChanged = FALSE;
     4040
     4041  infoPtr->himlNormal = NULL;
     4042  infoPtr->himlState = NULL;
     4043  infoPtr->normalImageWidth = 0;
     4044  infoPtr->normalImageHeight = 0;
     4045  infoPtr->stateImageWidth = 0;
     4046  infoPtr->stateImageHeight = 0;
     4047
     4048  infoPtr->items = DPA_Create(16);
     4049
     4050  infoPtr->hFont = GetStockObject(DEFAULT_GUI_FONT);
     4051  infoPtr->hBoldFont = TREEVIEW_CreateBoldFont(infoPtr->hFont);
     4052
     4053  infoPtr->uItemHeight = TREEVIEW_NaturalHeight(infoPtr);
     4054
     4055  infoPtr->root = TREEVIEW_AllocateItem(infoPtr);
     4056  infoPtr->root->state = TVIS_EXPANDED;
     4057  infoPtr->root->iLevel = -1;
     4058  infoPtr->root->displayOrder = -1;
     4059
     4060  infoPtr->pszISearch = NULL;
     4061  infoPtr->uISearchLen = 0;
     4062
     4063  infoPtr->tipItem = 0;
     4064  infoPtr->hwndToolTip = 0;
     4065  if (!(infoPtr->dwStyle & TVS_NOTOOLTIPS))
     4066  {
     4067    infoPtr->hwndToolTip = createToolTip(hwnd,TTF_TRACK | TTF_ABSOLUTE | TTF_TRANSPARENT,TRUE);
     4068    SendMessageA(infoPtr->hwndToolTip,WM_SETFONT,infoPtr->hFont,0);
     4069  }
     4070
     4071  if (infoPtr->dwStyle & TVS_CHECKBOXES)
     4072  {
     4073    HBITMAP hbmLoad;
     4074    int nIndex;
     4075
     4076    infoPtr->himlState = ImageList_Create (16, 16,ILC_COLOR | ILC_MASK, 15, 1);
     4077
     4078    //MSDN docu says: uses DrawFrameControl but never believe what they write
     4079    hbmLoad = LoadBitmapA (COMCTL32_hModule, MAKEINTRESOURCEA(IDT_CHECK));
     4080    nIndex = ImageList_AddMasked (infoPtr->himlState, hbmLoad, CLR_DEFAULT);
     4081    DeleteObject (hbmLoad);
     4082
     4083    infoPtr->stateImageWidth = 16;
     4084    infoPtr->stateImageHeight = 16;
     4085  }
     4086
     4087  return 0;
     4088}
     4089
     4090static LRESULT
     4091TREEVIEW_Destroy(TREEVIEW_INFO *infoPtr)
     4092{
     4093   TREEVIEW_RemoveTree(infoPtr);
     4094
     4095   /* tool tip is automatically destroyed: we are its owner */
     4096
     4097   /* Restore original windproc. */
     4098   if (infoPtr->hwndEdit)
     4099     SetWindowLongA(infoPtr->hwndEdit, GWL_WNDPROC,(LONG)infoPtr->wpEditOrig);
     4100
     4101   if (infoPtr->Timer & TV_REFRESH_TIMER_SET)
     4102     KillTimer(infoPtr->hwnd, TV_REFRESH_TIMER);
     4103
     4104   DeleteObject(infoPtr->hBoldFont);
     4105   COMCTL32_Free(infoPtr->pszISearch);
     4106   doneControl(infoPtr->hwnd);
     4107
     4108   return 0;
     4109}
     4110
     4111/* Miscellaneous Messages ***********************************************/
     4112
     4113static LRESULT
     4114TREEVIEW_ScrollKeyDown(TREEVIEW_INFO *infoPtr, WPARAM key)
     4115{
     4116    static const struct
     4117    {
     4118        unsigned char code;
     4119    }
     4120    scroll[] =
     4121    {
     4122#define SCROLL_ENTRY(dir, code) { ((dir) << 7) | (code) }
     4123        SCROLL_ENTRY(SB_VERT, SB_PAGEUP),       /* VK_PRIOR */
     4124        SCROLL_ENTRY(SB_VERT, SB_PAGEDOWN),     /* VK_NEXT */
     4125        SCROLL_ENTRY(SB_VERT, SB_BOTTOM),       /* VK_END */
     4126        SCROLL_ENTRY(SB_VERT, SB_TOP),          /* VK_HOME */
     4127        SCROLL_ENTRY(SB_HORZ, SB_LINEUP),       /* VK_LEFT */
     4128        SCROLL_ENTRY(SB_VERT, SB_LINEUP),       /* VK_UP */
     4129        SCROLL_ENTRY(SB_HORZ, SB_LINEDOWN),     /* VK_RIGHT */
     4130        SCROLL_ENTRY(SB_VERT, SB_LINEDOWN)      /* VK_DOWN */
     4131#undef SCROLL_ENTRY
     4132    };
     4133
     4134    if (key >= VK_PRIOR && key <= VK_DOWN)
     4135    {
     4136        unsigned char code = scroll[key - VK_PRIOR].code;
     4137
     4138        (((code & (1 << 7)) == (SB_HORZ << 7))
     4139         ? TREEVIEW_HScroll
     4140         : TREEVIEW_VScroll)(infoPtr, code & 0x7F, 0);
     4141    }
     4142
     4143    return 0;
     4144}
     4145
     4146/************************************************************************
     4147 *        TREEVIEW_KeyDown
     4148 *
     4149 * VK_UP       Move selection to the previous non-hidden item.
     4150 * VK_DOWN     Move selection to the next non-hidden item.
     4151 * VK_HOME     Move selection to the first item.
     4152 * VK_END      Move selection to the last item.
     4153 * VK_LEFT     If expanded then collapse, otherwise move to parent.
     4154 * VK_RIGHT    If collapsed then expand, otherwise move to first child.
     4155 * VK_ADD      Expand.
     4156 * VK_SUBTRACT Collapse.
     4157 * VK_MULTIPLY Expand all.
     4158 * VK_PRIOR    Move up GetVisibleCount items.
     4159 * VK_NEXT     Move down GetVisibleCount items.
     4160 * VK_BACK     Move to parent.
     4161 * CTRL-Left,Right,Up,Down,PgUp,PgDown,Home,End: Scroll without changing selection
     4162 */
     4163static LRESULT
     4164TREEVIEW_KeyDown(TREEVIEW_INFO *infoPtr, WPARAM wParam, LPARAM lParam)
     4165{
     4166  /* If it is non-NULL and different, it will be selected and visible. */
     4167  TREEVIEW_ITEM *newSelection = NULL;
     4168  TREEVIEW_ITEM *prevItem = infoPtr->selectedItem;
     4169
     4170  TREEVIEW_SendKeyDownNotify(infoPtr,TVN_KEYDOWN,wParam);
     4171
     4172  //TRACE("%x %lx\n", wParam, lParam);
     4173
     4174  if (prevItem == NULL)
     4175      return FALSE;
     4176
     4177  if (GetAsyncKeyState(VK_CONTROL) & 0x8000)
     4178      return TREEVIEW_ScrollKeyDown(infoPtr, wParam);
     4179
     4180  switch (wParam)
     4181  {
     4182    case VK_UP:
     4183        newSelection = TREEVIEW_GetPrevListItem(infoPtr, prevItem);
     4184        if (!newSelection)
     4185            newSelection = infoPtr->root->firstChild;
    46044186        break;
    46054187
    4606       case VK_DOWN:
    4607         scrollMsg = SB_LINERIGHT;
     4188    case VK_DOWN:
     4189        newSelection = TREEVIEW_GetNextListItem(infoPtr, prevItem);
    46084190        break;
    46094191
    4610       case VK_LEFT:
    4611         scrollMsg = SB_LINELEFT;
    4612         horz = TRUE;
     4192    case VK_HOME:
     4193        newSelection = infoPtr->root->firstChild;
    46134194        break;
    46144195
    4615       case VK_RIGHT:
    4616         scrollMsg = SB_LINERIGHT;
    4617         horz = TRUE;
     4196    case VK_END:
     4197        newSelection = TREEVIEW_GetLastListItem(infoPtr,
     4198                                                infoPtr->root->lastChild);
    46184199        break;
    46194200
    4620       case VK_PRIOR:
    4621         scrollMsg = SB_PAGELEFT;
     4201    case VK_LEFT:
     4202        if (prevItem->state & TVIS_EXPANDED)
     4203        {
     4204            TREEVIEW_Collapse(infoPtr, prevItem, FALSE, TRUE);
     4205        }
     4206        else if (prevItem->parent != infoPtr->root)
     4207        {
     4208            newSelection = prevItem->parent;
     4209        }
    46224210        break;
    46234211
    4624       case VK_NEXT:
    4625         scrollMsg = SB_PAGERIGHT;
     4212    case VK_RIGHT:
     4213        if (TREEVIEW_HasChildren(infoPtr, prevItem))
     4214        {
     4215            if (!(prevItem->state & TVIS_EXPANDED))
     4216                TREEVIEW_Expand(infoPtr, prevItem, FALSE, TRUE);
     4217            else
     4218            {
     4219                newSelection = prevItem->firstChild;
     4220            }
     4221        }
     4222
    46264223        break;
    4627     }
    4628     if (scrollMsg != -1)
    4629     {
    4630       SendMessageA(hwnd,horz ? WM_HSCROLL:WM_VSCROLL,MAKELONG(scrollMsg,0),0);
    4631       return TRUE;
    4632     }
    4633   }
    4634 
    4635   if (prevSelect == 0)
    4636     return FALSE;
    4637 
    4638   switch (wParam)
    4639   {
    4640     case VK_UP:
    4641       newItem = TREEVIEW_GetPrevListItem(hwnd,infoPtr, prevItem);
    4642 
    4643       if (!newItem)
    4644         newItem = &infoPtr->items[(INT)infoPtr->TopRootItem];
    4645 
    4646       hNewSelection = newItem->hItem;
    4647 
     4224
     4225    case VK_MULTIPLY:
     4226        TREEVIEW_ExpandAll(infoPtr, prevItem);
     4227        break;
     4228
     4229    case VK_ADD:
     4230        if (!(prevItem->state & TVIS_EXPANDED))
     4231            TREEVIEW_Expand(infoPtr, prevItem, FALSE, TRUE);
     4232        break;
     4233
     4234    case VK_SUBTRACT:
     4235        if (prevItem->state & TVIS_EXPANDED)
     4236            TREEVIEW_Collapse(infoPtr, prevItem, FALSE, TRUE);
     4237        break;
     4238
     4239    case VK_PRIOR:
     4240        newSelection
     4241            = TREEVIEW_GetListItem(infoPtr, prevItem,
     4242                                   -TREEVIEW_GetVisibleCount(infoPtr));
     4243        break;
     4244
     4245    case VK_NEXT:
     4246        newSelection
     4247            = TREEVIEW_GetListItem(infoPtr, prevItem,
     4248                                   TREEVIEW_GetVisibleCount(infoPtr));
     4249        break;
     4250
     4251    case VK_RETURN:
     4252      sendNotify(infoPtr->hwnd,NM_RETURN);
    46484253      break;
    46494254
    4650     case VK_DOWN:
    4651       newItem = TREEVIEW_GetNextListItem (hwnd,infoPtr, prevItem);
    4652 
    4653       if (!newItem)
    4654         newItem = prevItem;
    4655 
    4656       hNewSelection = newItem->hItem;
    4657 
    4658       break;
    4659 
    4660     case VK_HOME:
    4661       newItem = &infoPtr->items[(INT)infoPtr->TopRootItem];
    4662       hNewSelection = newItem->hItem;
    4663 
    4664       break;
    4665 
    4666     case VK_END:
    4667       newItem       = &infoPtr->items[(INT)infoPtr->TopRootItem];
    4668       newItem       = TREEVIEW_GetLastListItem (hwnd,infoPtr, newItem);
    4669       hNewSelection = newItem->hItem;
    4670 
    4671       break;
    4672 
    4673     case VK_LEFT:
    4674       if ( (prevItem->state & TVIS_EXPANDED) &&
    4675          TREEVIEW_HasChildren(hwnd, prevItem))
     4255    case VK_BACK:
     4256        newSelection = prevItem->parent;
     4257        if (newSelection == infoPtr->root)
     4258            newSelection = NULL;
     4259        break;
     4260
     4261    case VK_SPACE:
     4262        if (infoPtr->dwStyle & TVS_CHECKBOXES)
     4263            TREEVIEW_ToggleItemState(infoPtr, prevItem);
     4264        break;
     4265  }
     4266
     4267  if (newSelection && newSelection != prevItem)
     4268  {
     4269    if (TREEVIEW_DoSelectItem(infoPtr,TVGN_CARET,newSelection,TVC_BYKEYBOARD))
     4270    {
     4271      TREEVIEW_EnsureVisible(infoPtr, newSelection);
     4272    }
     4273  }
     4274
     4275  return FALSE;
     4276}
     4277
     4278static LRESULT
     4279TREEVIEW_Size(TREEVIEW_INFO *infoPtr, WPARAM wParam, LPARAM lParam)
     4280{
     4281  if (wParam == SIZE_RESTORED)
     4282  {
     4283    infoPtr->clientWidth = LOWORD(lParam);
     4284    infoPtr->clientHeight = HIWORD(lParam);
     4285    if (TREEVIEW_CalcItems(infoPtr))
     4286      TREEVIEW_Refresh(infoPtr);
     4287  }
     4288
     4289  return 0;
     4290}
     4291
     4292static LRESULT
     4293TREEVIEW_StyleChanged(TREEVIEW_INFO *infoPtr, WPARAM wParam, LPARAM lParam)
     4294{
     4295  //TRACE("(%x %lx)\n", wParam, lParam);
     4296
     4297  if (wParam == GWL_STYLE)
     4298    infoPtr->dwStyle = ((LPSTYLESTRUCT)lParam)->styleNew;
     4299
     4300  if (infoPtr->hwndEdit) SetFocus(infoPtr->hwnd);
     4301
     4302  infoPtr->uInternalStatus |= TV_CALCALL;
     4303  TREEVIEW_QueueRefresh(infoPtr);
     4304
     4305  return 0;
     4306}
     4307
     4308static LRESULT
     4309TREEVIEW_SetFocus(TREEVIEW_INFO *infoPtr, WPARAM wParam, LPARAM lParam)
     4310{
     4311  sendNotify(infoPtr->hwnd,NM_SETFOCUS);
     4312
     4313  if (!(infoPtr->dwStyle & TVS_SHOWSELALWAYS))
     4314  {
     4315    if (infoPtr->selectedItem)
     4316      TREEVIEW_RefreshItem(infoPtr,infoPtr->selectedItem,TVIF_IMAGE | TVIF_TEXT);
     4317    else if (infoPtr->firstVisible)
     4318      TREEVIEW_DoSelectItem(infoPtr,TVGN_CARET,infoPtr->firstVisible,TVC_UNKNOWN);
     4319  }
     4320
     4321  return 0;
     4322}
     4323
     4324static LRESULT
     4325TREEVIEW_KillFocus(TREEVIEW_INFO *infoPtr, WPARAM wParam, LPARAM lParam)
     4326{
     4327  sendNotify(infoPtr->hwnd,NM_KILLFOCUS);
     4328
     4329  if (!(infoPtr->dwStyle & TVS_SHOWSELALWAYS) && infoPtr->selectedItem)
     4330    TREEVIEW_RefreshItem(infoPtr,infoPtr->selectedItem,TVIF_IMAGE | TVIF_TEXT);
     4331
     4332  return 0;
     4333}
     4334
     4335static LRESULT TREEVIEW_RButtonDoubleClick(TREEVIEW_INFO *infoPtr,WPARAM wParam,LPARAM lParam)
     4336{
     4337  sendNotify(infoPtr->hwnd,NM_RDBLCLK);
     4338
     4339  return DefWindowProcA(infoPtr->hwnd,WM_RBUTTONDBLCLK,wParam,lParam);
     4340}
     4341
     4342
     4343HTREEITEM TREEVIEW_GetInfoTipItem(TREEVIEW_INFO *infoPtr,POINT pt)
     4344{
     4345  TVHITTESTINFO ht;
     4346
     4347  ht.pt = pt;
     4348  TREEVIEW_HitTest(infoPtr,&ht,FALSE);
     4349
     4350  if (ht.hItem && (ht.flags & TVHT_ONITEM))
     4351  {
     4352    TREEVIEW_ITEM *item = ht.hItem;
     4353
     4354    if (item->inclient && ((item->textOffset < 0) || (item->textOffset+item->textWidth > infoPtr->clientWidth)))
     4355      return ht.hItem;
     4356  }
     4357
     4358  //check tool rect -> no flickering on tip frame
     4359  if (infoPtr->tipItem)
     4360  {
     4361    POINT pt2 = pt;
     4362    RECT rect;
     4363
     4364    GetWindowRect(infoPtr->hwndToolTip,&rect);
     4365    ClientToScreen(infoPtr->hwnd,&pt2);
     4366    return PtInRect(&rect,pt2) ? infoPtr->tipItem:0;
     4367  }
     4368
     4369  return 0;
     4370}
     4371
     4372VOID TREEVIEW_ShowInfoTip(TREEVIEW_INFO *infoPtr,TREEVIEW_ITEM *item)
     4373{
     4374  LPWSTR text;
     4375  BOOL mustFree = FALSE;
     4376  TTTOOLINFOW ti;
     4377  POINT pt;
     4378
     4379  if (infoPtr->dwStyle & TVS_INFOTIP)
     4380  {
     4381    NMTVGETINFOTIPW tvgit;
     4382    WCHAR* buf = (WCHAR*)COMCTL32_Alloc(isUnicodeNotify(&infoPtr->header) ? INFOTIPSIZE*sizeof(WCHAR):INFOTIPSIZE*sizeof(CHAR));
     4383
     4384    tvgit.pszText    = buf;
     4385    tvgit.cchTextMax = INFOTIPSIZE;
     4386    tvgit.hItem      = item;
     4387    tvgit.lParam     = item->lParam;
     4388
     4389    sendNotify(infoPtr->hwnd,isUnicodeNotify(&infoPtr->header) ? TVN_GETINFOTIPW:TVN_GETINFOTIPA,&tvgit.hdr);
     4390    if (isUnicodeNotify(&infoPtr->header))
     4391    {
     4392      if (buf != tvgit.pszText) COMCTL32_Free(buf); else mustFree = TRUE;
     4393      text = tvgit.pszText;
     4394    } else
     4395    {
     4396      text = (WCHAR*)COMCTL32_Alloc(tvgit.cchTextMax*sizeof(WCHAR));
     4397      mustFree = TRUE;
     4398      lstrcpyAtoW(text,(LPSTR)tvgit.pszText);
     4399      COMCTL32_Free(buf);
     4400    }
     4401  } else
     4402  {
     4403    TREEVIEW_UpdateDispInfo(infoPtr,item,TVIF_TEXT);
     4404    text = item->pszText;
     4405  }
     4406
     4407  infoPtr->tipItem = item;
     4408  SetTimer(infoPtr->hwnd,TV_INFOTIP_TIMER,TV_INFOTIP_DELAY,0);
     4409  infoPtr->Timer |= TV_INFOTIP_TIMER_SET;
     4410
     4411  ti.cbSize   = sizeof(ti);
     4412  ti.uId      = 0;
     4413  ti.hwnd     = infoPtr->hwnd;
     4414  ti.hinst    = 0;
     4415  ti.lpszText = text;
     4416  SendMessageA(infoPtr->hwndToolTip,TTM_TRACKACTIVATE,(WPARAM)FALSE,(LPARAM)&ti);
     4417  pt.x = item->textOffset;
     4418  pt.y = item->rect.top;
     4419  ClientToScreen(infoPtr->hwnd,&pt);
     4420  SendMessageA(infoPtr->hwndToolTip,TTM_TRACKPOSITION,0,(LPARAM)MAKELPARAM(pt.x,pt.y));
     4421  SendMessageA(infoPtr->hwndToolTip,TTM_UPDATETIPTEXTW,0,(LPARAM)&ti);
     4422  SendMessageA(infoPtr->hwndToolTip,TTM_TRACKACTIVATE,(WPARAM)TRUE,(LPARAM)&ti);
     4423
     4424  if (mustFree) COMCTL32_Free(text);
     4425}
     4426
     4427VOID TREEVIEW_HideInfoTip(TREEVIEW_INFO *infoPtr)
     4428{
     4429  if (infoPtr->tipItem)
     4430  {
     4431    TTTOOLINFOA ti;
     4432
     4433    infoPtr->tipItem = 0;
     4434    KillTimer(infoPtr->hwnd,TV_INFOTIP_TIMER);
     4435    infoPtr->Timer &= ~TV_INFOTIP_TIMER_SET;
     4436
     4437    ti.cbSize   = sizeof(TTTOOLINFOA);
     4438    ti.uId      = 0;
     4439    ti.hwnd     = (UINT)infoPtr->hwnd;
     4440
     4441    SendMessageA(infoPtr->hwndToolTip,TTM_TRACKACTIVATE,(WPARAM)FALSE,(LPARAM)&ti);
     4442  }
     4443}
     4444
     4445static VOID TREEVIEW_CheckInfoTip(TREEVIEW_INFO *infoPtr)
     4446{
     4447  HTREEITEM hItem;
     4448  POINT pt;
     4449
     4450  GetCursorPos(&pt);
     4451  ScreenToClient(infoPtr->hwnd,&pt);
     4452  hItem = TREEVIEW_GetInfoTipItem(infoPtr,pt);
     4453
     4454  if (hItem != infoPtr->tipItem)
     4455    TREEVIEW_HideInfoTip(infoPtr);
     4456}
     4457
     4458HTREEITEM TREEVIEW_GetHottrackItem(TREEVIEW_INFO *infoPtr,POINT pt)
     4459{
     4460  TVHITTESTINFO ht;
     4461
     4462  ht.pt = pt;
     4463  TREEVIEW_HitTest(infoPtr,&ht,FALSE);
     4464
     4465  return (ht.hItem && (ht.flags & TVHT_ONITEM)) ? ht.hItem:0;
     4466}
     4467
     4468static LRESULT TREEVIEW_MouseMove(TREEVIEW_INFO *infoPtr,WPARAM wParam,LPARAM lParam)
     4469{
     4470  POINT pt;
     4471
     4472  pt.x = (INT)LOWORD(lParam);
     4473  pt.y = (INT)HIWORD(lParam);
     4474
     4475  if (infoPtr->hwndToolTip)
     4476  {
     4477    HTREEITEM hItem = TREEVIEW_GetInfoTipItem(infoPtr,pt);
     4478
     4479    if (infoPtr->tipItem != hItem)
     4480    {
     4481      if (hItem)
    46764482      {
    4677         TREEVIEW_Expand(hwnd, TVE_COLLAPSE, prevSelect );
    4678       } else if ((INT)prevItem->parent)
     4483        TREEVIEW_ITEM *item = hItem;
     4484
     4485        TREEVIEW_ShowInfoTip(infoPtr,item);
     4486      } else TREEVIEW_HideInfoTip(infoPtr);
     4487    }
     4488  }
     4489
     4490  if (infoPtr->dwStyle & TVS_TRACKSELECT)
     4491  {
     4492    HTREEITEM hItem = TREEVIEW_GetHottrackItem(infoPtr,pt);
     4493
     4494    if (infoPtr->hotItem != hItem)
     4495    {
     4496      TREEVIEW_ITEM *item;
     4497      HDC hdc = 0;
     4498
     4499      item = infoPtr->hotItem;
     4500      if (TREEVIEW_ValidItem(infoPtr,infoPtr->hotItem))
    46794501      {
    4680         newItem = (& infoPtr->items[(INT)prevItem->parent]);
    4681 
    4682         hNewSelection = newItem->hItem; //CB: what does Win32??? Please not the Corel hack!!!
     4502        if (!hdc) hdc = GetDC(infoPtr->hwnd);
     4503        TREEVIEW_DrawHottrackLine(hdc,item);
    46834504      }
    4684 
    4685       break;
    4686 
    4687     case VK_RIGHT:
    4688       if (TREEVIEW_HasChildren(hwnd, prevItem))
     4505      if (hItem)
    46894506      {
    4690         if (!(prevItem->state & TVIS_EXPANDED))
    4691           TREEVIEW_Expand(hwnd, TVE_EXPAND, prevSelect );
    4692          else
    4693          {
    4694            newItem = (& infoPtr->items[(INT)prevItem->firstChild]);
    4695           hNewSelection = newItem->hItem;
    4696          }
     4507        item = hItem;
     4508        if (item)
     4509        {
     4510          if (!hdc) hdc = GetDC(infoPtr->hwnd);
     4511          TREEVIEW_DrawHottrackLine(hdc,item);
     4512        }
    46974513      }
    4698 
    4699       break;
    4700 
    4701     case VK_ADD:
    4702       if (!(prevItem->state & TVIS_EXPANDED))
    4703         TREEVIEW_Expand(hwnd, TVE_EXPAND, prevSelect );
    4704       break;
    4705 
    4706     case VK_SUBTRACT:
    4707       if (prevItem->state & TVIS_EXPANDED)
    4708       TREEVIEW_Expand(hwnd, TVE_COLLAPSE, prevSelect );
    4709     break;
    4710 
    4711     case VK_PRIOR:
    4712       newItem=TREEVIEW_GetListItem(
    4713               hwnd,
    4714               infoPtr,
    4715               prevItem,
    4716               -1*(TREEVIEW_GetVisibleCount(hwnd,0,0)-3));
    4717       if (!newItem)
    4718         newItem = prevItem;
    4719 
    4720       hNewSelection = newItem->hItem;
    4721 
    4722       break;
    4723 
    4724     case VK_NEXT:
    4725       newItem=TREEVIEW_GetListItem(
    4726               hwnd,
    4727               infoPtr,
    4728               prevItem,
    4729               TREEVIEW_GetVisibleCount(hwnd,0,0)-3);
    4730 
    4731       if (!newItem)
    4732         newItem = prevItem;
    4733 
    4734       hNewSelection = newItem->hItem;
    4735 
    4736       break;
    4737 
    4738     case VK_RETURN:
    4739       sendNotify(hwnd,NM_RETURN);
    4740       break;
    4741 
    4742     case VK_BACK:
    4743 
    4744     default:
    4745       break;
    4746   }
    4747 
    4748   if (hNewSelection)
    4749   {
    4750     if (TREEVIEW_DoSelectItem(hwnd,TVGN_CARET,(HTREEITEM)hNewSelection,TVC_BYKEYBOARD))
    4751     {
    4752       TREEVIEW_EnsureVisible(hwnd,hNewSelection);
    4753     }
    4754   }
    4755 
    4756   return FALSE;
    4757 }
    4758 
    4759 static LRESULT TREEVIEW_Char(HWND hwnd,WPARAM wParam,LPARAM lParam)
     4514      if (hdc) ReleaseDC(infoPtr->hwnd,hdc);
     4515    }
     4516  }
     4517
     4518  return 0;
     4519}
     4520
     4521static LRESULT TREEVIEW_SetRedraw(TREEVIEW_INFO *infoPtr,WPARAM wParam,LPARAM lParam)
     4522{
     4523  if (wParam)
     4524  {
     4525    if (!(infoPtr->uInternalStatus & TV_NOREDRAW)) return 0;
     4526    infoPtr->uInternalStatus &= ~TV_NOREDRAW;
     4527    TREEVIEW_CalcItems(infoPtr);
     4528    TREEVIEW_Refresh(infoPtr);
     4529  } else
     4530  {
     4531    infoPtr->uInternalStatus |= TV_NOREDRAW;
     4532  }
     4533
     4534  return 0;
     4535}
     4536
     4537static LRESULT
     4538TREEVIEW_SysColorChange(TREEVIEW_INFO *infoPtr,WPARAM wParam,LPARAM lParam)
     4539{
     4540  TREEVIEW_Refresh(infoPtr);
     4541
     4542  return DefWindowProcA(infoPtr->hwnd,WM_SYSCOLORCHANGE,wParam,lParam);
     4543}
     4544
     4545static LRESULT TREEVIEW_Enable(TREEVIEW_INFO *infoPtr,WPARAM wParam,LPARAM lParam)
     4546{
     4547  TREEVIEW_Refresh(infoPtr);
     4548
     4549  return DefWindowProcA(infoPtr->hwnd,WM_ENABLE,wParam,lParam);
     4550}
     4551
     4552static LRESULT
     4553TREEVIEW_EraseBackground(TREEVIEW_INFO *infoPtr, WPARAM wParam, LPARAM lParam)
     4554{
     4555    HBRUSH hBrush = CreateSolidBrush (infoPtr->clrBk);
     4556    RECT rect;
     4557
     4558    GetClientRect (infoPtr->hwnd, &rect);
     4559    FillRect ((HDC)wParam, &rect, hBrush);
     4560    DeleteObject (hBrush);
     4561
     4562    return TRUE;
     4563}
     4564
     4565static LRESULT TREEVIEW_GetDlgCode(TREEVIEW_INFO *infoPtr,WPARAM wParam,LPARAM lParam)
     4566{
     4567  return DLGC_WANTARROWS | DLGC_WANTCHARS;
     4568}
     4569
     4570
     4571static LRESULT TREEVIEW_Char(TREEVIEW_INFO *infoPtr,WPARAM wParam,LPARAM lParam)
    47604572{
    47614573  CHAR ch = (CHAR)wParam;
    47624574
    4763   TREEVIEW_ISearch(hwnd,ch);
     4575  TREEVIEW_ISearch(infoPtr,ch);
    47644576
    47654577  return 0;
    47664578}
    47674579
    4768 static LRESULT
    4769 TREEVIEW_GetScrollTime (HWND hwnd)
    4770 {
    4771   TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
    4772 
    4773   return infoPtr->uScrollTime;
    4774 }
    4775 
    4776 static LRESULT
    4777 TREEVIEW_SetScrollTime (HWND hwnd, UINT uScrollTime)
    4778 {
    4779   TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
    4780   UINT uOldScrollTime = infoPtr->uScrollTime;
    4781 
    4782   infoPtr->uScrollTime = min (uScrollTime, 100);
    4783 
    4784   return uOldScrollTime;
    4785 }
    4786 
    4787 static LRESULT TREEVIEW_EnsureVisible(HWND hwnd,HTREEITEM hItem)
    4788 {
    4789   TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
    4790   TREEVIEW_ITEM *item;
    4791   RECT rect;
    4792   INT scrollY;
    4793 
    4794   item = TREEVIEW_ValidItem(infoPtr,hItem);
    4795 
    4796   if (!item) return FALSE;
    4797 
    4798   if (item && item->parent)
    4799   {
    4800     TREEVIEW_ITEM *parent = TREEVIEW_ValidItem(infoPtr,item->parent);
    4801 
    4802     while (parent && !(parent->state & TVIS_EXPANDED))
    4803     {
    4804       TREEVIEW_Expand(hwnd,TVE_EXPAND,(LPARAM)parent->hItem);
    4805       parent = TREEVIEW_ValidItem(infoPtr,parent->parent);
    4806     }
    4807   }
    4808 
    4809   TREEVIEW_UnqueueRefresh(hwnd,TRUE,TRUE);
    4810   GetClientRect(hwnd,&rect);
    4811   if (item->rect.top < 0)
    4812     scrollY = item->rect.top;
    4813   else if (item->rect.bottom > rect.bottom)
    4814   {
    4815     INT mod;
    4816 
    4817     scrollY = item->rect.bottom-rect.bottom;
    4818     mod = scrollY % infoPtr->uVScrollStep;
    4819     if (mod) scrollY += infoPtr->uVScrollStep-mod;
    4820   } else return FALSE;
    4821 
    4822   if (scrollY != 0)
    4823   {
    4824     infoPtr->lefttop.y += scrollY;
    4825     TREEVIEW_CalcItems(hwnd,0,infoPtr);
    4826     ScrollWindowEx(hwnd,0,-scrollY,NULL,NULL,0,NULL,SW_INVALIDATE | SW_SCROLLCHILDREN | (infoPtr->uScrollTime << 16));
    4827 
    4828     return TRUE;
    4829   }
    4830 
    4831   return FALSE;
    4832 }
    4833 
    4834 static BOOL TREEVIEW_Compare(HWND hwnd,TREEVIEW_ITEM *item,LPWSTR text,INT textlen,BOOL *matchLast)
    4835 {
    4836   WCHAR* itemtext;
    4837   BOOL mustFree = FALSE,res;
     4580static BOOL TREEVIEW_Compare(TREEVIEW_INFO *infoPtr,TREEVIEW_ITEM *item,LPWSTR text,INT textlen,BOOL *matchLast)
     4581{
     4582  BOOL res;
    48384583  INT itemlen;
    48394584
    4840   if (item->pszText == LPSTR_TEXTCALLBACKW)
    4841     itemtext = TREEVIEW_CallbackText(hwnd,item,&mustFree);
    4842   else
    4843     itemtext = item->pszText;
    4844 
    4845   itemlen = lstrlenW(itemtext);
     4585  TREEVIEW_UpdateDispInfo(infoPtr,item,TVIF_TEXT);
     4586
     4587  itemlen = lstrlenW(item->pszText);
    48464588  if (itemlen < textlen)
    48474589  {
     
    48494591  } else
    48504592  {
    4851     res = (lstrcmpniW(itemtext,text,textlen) == 0);
     4593    res = (lstrcmpniW(item->pszText,text,textlen) == 0);
    48524594  }
    48534595  if (!res && matchLast)
     
    48554597    textlen--;
    48564598    if ((textlen > 0) && (itemlen >= textlen))
    4857       *matchLast = (lstrcmpniW(itemtext,text,textlen) == 0);
     4599      *matchLast = (lstrcmpniW(item->pszText,text,textlen) == 0);
    48584600    else
    48594601      *matchLast = FALSE;
    48604602  }
    4861   if (mustFree) COMCTL32_Free(itemtext);
    48624603
    48634604  return res;
    48644605}
    48654606
    4866 static TREEVIEW_ITEM* TREEVIEW_Search(HWND hwnd,TREEVIEW_INFO *infoPtr,INT iItem,LPWSTR text,INT textlen,TREEVIEW_ITEM **nearest)
    4867 {
    4868   TREEVIEW_ITEM *item,*start;
     4607static TREEVIEW_ITEM* TREEVIEW_Search(TREEVIEW_INFO *infoPtr,TREEVIEW_ITEM *item,LPWSTR text,INT textlen,TREEVIEW_ITEM **nearest)
     4608{
     4609  TREEVIEW_ITEM *start;
    48694610  BOOL bMatchLast;
    48704611
    4871   item = start = &infoPtr->items[iItem];
     4612  start = item;
    48724613  if (nearest) *nearest = NULL;
    48734614
     
    48774618    if (nearest)
    48784619    {
    4879       if (TREEVIEW_Compare(hwnd,item,text,textlen,(!*nearest) ? &bMatchLast:NULL))
     4620      if (TREEVIEW_Compare(infoPtr,item,text,textlen,(!*nearest) ? &bMatchLast:NULL))
    48804621        return item;
    48814622      else if (!*nearest && bMatchLast)
     
    48834624    } else
    48844625    {
    4885       if (TREEVIEW_Compare(hwnd,item,text,textlen,NULL))
     4626      if (TREEVIEW_Compare(infoPtr,item,text,textlen,NULL))
    48864627        return item;
    48874628    }
    4888     item = TREEVIEW_GetNextListItem(hwnd,infoPtr,item);
    4889   }
    4890 
    4891   iItem = (INT)infoPtr->TopRootItem;
    4892   item = &infoPtr->items[iItem];
     4629    item = TREEVIEW_GetNextListItem(infoPtr,item);
     4630  }
     4631
     4632  item = infoPtr->root;
    48934633
    48944634  //search first to start
     
    48974637    if (nearest)
    48984638    {
    4899       if (TREEVIEW_Compare(hwnd,item,text,textlen,(!*nearest) ? &bMatchLast:NULL))
     4639      if (TREEVIEW_Compare(infoPtr,item,text,textlen,(!*nearest) ? &bMatchLast:NULL))
    49004640        return item;
    49014641      else if (!*nearest && bMatchLast)
     
    49034643    } else
    49044644    {
    4905       if (TREEVIEW_Compare(hwnd,item,text,textlen,NULL))
     4645      if (TREEVIEW_Compare(infoPtr,item,text,textlen,NULL))
    49064646        return item;
    49074647    }
    4908     item = TREEVIEW_GetNextListItem(hwnd,infoPtr,item);
     4648    item = TREEVIEW_GetNextListItem(infoPtr,item);
    49094649  }
    49104650
     
    49144654//NOTE: sister function in listview control -> sync changes
    49154655
    4916 static VOID TREEVIEW_ISearch(HWND hwnd,CHAR ch)
    4917 {
    4918   TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
     4656static VOID TREEVIEW_ISearch(TREEVIEW_INFO *infoPtr,CHAR ch)
     4657{
    49194658  LPWSTR newString;
    4920   INT len,iItem;
     4659  INT len;
    49214660  CHAR ch2[2];
    49224661  TREEVIEW_ITEM *item,*nearest = NULL;
     
    49564695  if (infoPtr->selectedItem)
    49574696  {
    4958     iItem = (INT)infoPtr->selectedItem;
    4959     item = &infoPtr->items[iItem];
     4697    item = infoPtr->selectedItem;
    49604698
    49614699    //check if new string is valid for old selection
    4962     if (TREEVIEW_Compare(hwnd,item,newString,len,NULL))
     4700    if (TREEVIEW_Compare(infoPtr,item,newString,len,NULL))
    49634701      goto ISearchDone;
    49644702
    49654703    //no match, continue with next item
    4966     item = TREEVIEW_GetNextListItem(hwnd,infoPtr,item);
    4967     if (!item) item = &infoPtr->items[(INT)infoPtr->TopRootItem];
    4968     iItem = (INT)item->hItem;
    4969   } else iItem = (INT)infoPtr->TopRootItem;
     4704    item = TREEVIEW_GetNextListItem(infoPtr,item);
     4705    if (!item) item = infoPtr->root;
     4706  } else item = infoPtr->root;
    49704707
    49714708  //scan
    4972   item = TREEVIEW_Search(hwnd,infoPtr,iItem,newString,len,checkNearest ? &nearest:NULL);
     4709  item = TREEVIEW_Search(infoPtr,item,newString,len,checkNearest ? &nearest:NULL);
    49734710
    49744711  if (!item && nearest)
    49754712  {
    4976     TREEVIEW_SelectItem(hwnd,(WPARAM)TVGN_CARET,(LPARAM)nearest->hItem);
    4977     TREEVIEW_EnsureVisible(hwnd,nearest->hItem);
     4713    TREEVIEW_SelectItem(infoPtr,(WPARAM)TVGN_CARET,(LPARAM)nearest);
     4714    TREEVIEW_EnsureVisible(infoPtr,nearest);
    49784715    infoPtr->dwISearchTime = GetTickCount();
    49794716
     
    49894726    infoPtr->pszISearch = newString;
    49904727    infoPtr->uISearchLen = len;
    4991     TREEVIEW_SelectItem(hwnd,(WPARAM)TVGN_CARET,(LPARAM)item->hItem);
    4992     TREEVIEW_EnsureVisible(hwnd,item->hItem);
     4728    TREEVIEW_SelectItem(infoPtr,(WPARAM)TVGN_CARET,(LPARAM)item);
     4729    TREEVIEW_EnsureVisible(infoPtr,item);
    49934730    infoPtr->dwISearchTime = GetTickCount();
    49944731  } else
     
    49994736}
    50004737
    5001 static LRESULT TREEVIEW_GetISearchString(HWND hwnd,LPWSTR lpsz,BOOL unicode)
    5002 {
    5003   TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
    5004 
     4738static LRESULT TREEVIEW_GetISearchString(TREEVIEW_INFO *infoPtr,LPWSTR lpsz,BOOL unicode)
     4739{
    50054740  if (infoPtr->uISearchLen == 0) return 0;
    50064741
     
    50134748}
    50144749
    5015 static LRESULT TREEVIEW_SetCursor(HWND hwnd,WPARAM wParam,LPARAM lParam)
    5016 {
    5017   sendNotify(hwnd,NM_SETCURSOR); //CB: todo: result
    5018 
    5019   return DefWindowProcA(hwnd,WM_SETCURSOR,wParam,lParam);
     4750static LRESULT TREEVIEW_SetCursor(TREEVIEW_INFO *infoPtr,WPARAM wParam,LPARAM lParam)
     4751{
     4752  sendNotify(infoPtr->hwnd,NM_SETCURSOR); //CB: todo: result
     4753
     4754  return DefWindowProcA(infoPtr->hwnd,WM_SETCURSOR,wParam,lParam);
     4755}
     4756
     4757static LRESULT TREEVIEW_NCCreate(HWND hwnd,WPARAM wParam,LPARAM lParam)
     4758{
     4759  DWORD dwStyle = GetWindowLongA(hwnd,GWL_STYLE),oldStyle = dwStyle;
     4760
     4761  dwStyle &= ~(WS_HSCROLL | WS_VSCROLL);
     4762  if (dwStyle != oldStyle) SetWindowLongA(hwnd,GWL_STYLE,dwStyle);
     4763
     4764  return DefWindowProcA(hwnd,WM_NCCREATE,wParam,lParam);
    50204765}
    50214766
     
    50234768TREEVIEW_WindowProc (HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
    50244769{
    5025    if (uMsg == WM_CREATE)
    5026      return TREEVIEW_Create (hwnd, wParam, lParam);
    5027 
    5028    if (!TREEVIEW_GetInfoPtr(hwnd))
    5029      return DefWindowProcA (hwnd, uMsg, wParam, lParam);
     4770    TREEVIEW_INFO *infoPtr;
     4771
     4772    if (uMsg == WM_NCCREATE)
     4773    {
     4774      return TREEVIEW_NCCreate(hwnd,wParam,lParam);
     4775    } else if (uMsg == WM_CREATE)
     4776    {
     4777      return TREEVIEW_Create(hwnd, wParam, lParam);
     4778    }
     4779
     4780    infoPtr = TREEVIEW_GetInfoPtr(hwnd);
     4781    if (infoPtr) TREEVIEW_VerifyTree(infoPtr); else goto def;
    50304782
    50314783    switch (uMsg)
    50324784    {
    50334785        case TVM_INSERTITEMA:
    5034           return TREEVIEW_InsertItem(hwnd,wParam,lParam,FALSE);
     4786          return TREEVIEW_InsertItem(infoPtr,wParam,lParam,FALSE);
    50354787
    50364788        case TVM_INSERTITEMW:
    5037           return TREEVIEW_InsertItem(hwnd,wParam,lParam,TRUE);
     4789          return TREEVIEW_InsertItem(infoPtr,wParam,lParam,TRUE);
    50384790
    50394791        case TVM_DELETEITEM:
    5040           return TREEVIEW_DeleteItem (hwnd, wParam, lParam);
     4792          return TREEVIEW_DeleteItem(infoPtr,wParam,lParam);
    50414793
    50424794        case TVM_EXPAND:
    5043           return TREEVIEW_Expand (hwnd, wParam, lParam);
     4795          return TREEVIEW_ExpandMsg(infoPtr,wParam,lParam);
    50444796
    50454797        case TVM_GETITEMRECT:
    5046           return TREEVIEW_GetItemRect (hwnd, wParam, lParam);
     4798          return TREEVIEW_GetItemRect(infoPtr,wParam,lParam);
    50474799
    50484800        case TVM_GETCOUNT:
    5049           return TREEVIEW_GetCount (hwnd, wParam, lParam);
     4801          return TREEVIEW_GetCount(infoPtr,wParam,lParam);
    50504802
    50514803        case TVM_GETINDENT:
    5052           return TREEVIEW_GetIndent (hwnd);
     4804          return TREEVIEW_GetIndent(infoPtr);
    50534805
    50544806        case TVM_SETINDENT:
    5055           return TREEVIEW_SetIndent (hwnd, wParam);
     4807          return TREEVIEW_SetIndent(infoPtr,wParam);
    50564808
    50574809        case TVM_GETIMAGELIST:
    5058           return TREEVIEW_GetImageList (hwnd, wParam, lParam);
     4810          return TREEVIEW_GetImageList(infoPtr,wParam,lParam);
    50594811
    50604812        case TVM_SETIMAGELIST:
    5061           return TREEVIEW_SetImageList (hwnd, wParam, lParam);
     4813          return TREEVIEW_SetImageList (infoPtr, wParam, lParam);
    50624814
    50634815        case TVM_GETNEXTITEM:
    5064           return TREEVIEW_GetNextItem (hwnd, wParam, lParam);
     4816          return TREEVIEW_GetNextItem (infoPtr, wParam, lParam);
    50654817
    50664818        case TVM_SELECTITEM:
    5067           return TREEVIEW_SelectItem (hwnd, wParam, lParam);
     4819          return TREEVIEW_SelectItem (infoPtr, wParam, lParam);
    50684820
    50694821        case TVM_GETITEMA:
    5070           return TREEVIEW_GetItem(hwnd,wParam,lParam,FALSE);
     4822          return TREEVIEW_GetItem(infoPtr,wParam,lParam,FALSE);
    50714823
    50724824        case TVM_GETITEMW:
    5073           return TREEVIEW_GetItem(hwnd,wParam,lParam,TRUE);
     4825          return TREEVIEW_GetItem(infoPtr,wParam,lParam,TRUE);
    50744826
    50754827        case TVM_SETITEMA:
    5076           return TREEVIEW_SetItem(hwnd,wParam,lParam,FALSE);
     4828          return TREEVIEW_SetItem(infoPtr,wParam,lParam,FALSE);
    50774829
    50784830        case TVM_SETITEMW:
    5079           return TREEVIEW_SetItem(hwnd,wParam,lParam,TRUE);
     4831          return TREEVIEW_SetItem(infoPtr,wParam,lParam,TRUE);
    50804832
    50814833        case TVM_EDITLABELA:
    5082           return TREEVIEW_EditLabel(hwnd,(HTREEITEM)lParam,FALSE);
     4834          return TREEVIEW_EditLabel(infoPtr,(HTREEITEM)lParam,FALSE);
    50834835
    50844836        case TVM_EDITLABELW:
    5085           return TREEVIEW_EditLabel(hwnd,(HTREEITEM)lParam,TRUE);
     4837          return TREEVIEW_EditLabel(infoPtr,(HTREEITEM)lParam,TRUE);
    50864838
    50874839        case TVM_GETEDITCONTROL:
    5088           return TREEVIEW_GetEditControl (hwnd);
     4840          return TREEVIEW_GetEditControl (infoPtr);
    50894841
    50904842        case TVM_GETVISIBLECOUNT:
    5091           return TREEVIEW_GetVisibleCount (hwnd, wParam, lParam);
     4843          return TREEVIEW_GetVisibleCount (infoPtr);
    50924844
    50934845        case TVM_HITTEST:
    5094           return TREEVIEW_HitTest(hwnd,(LPTVHITTESTINFO)lParam,FALSE);
     4846          return TREEVIEW_HitTest(infoPtr,(LPTVHITTESTINFO)lParam,FALSE);
    50954847
    50964848        case TVM_CREATEDRAGIMAGE:
    5097           return TREEVIEW_CreateDragImage (hwnd, wParam, lParam);
     4849          return TREEVIEW_CreateDragImage (infoPtr, wParam, lParam);
    50984850
    50994851        case TVM_SORTCHILDREN:
    5100           return TREEVIEW_SortChildren(hwnd, wParam, lParam);
     4852          return TREEVIEW_SortChildren(infoPtr, wParam, lParam);
    51014853
    51024854        case TVM_ENSUREVISIBLE:
    5103           return TREEVIEW_EnsureVisible(hwnd,(HTREEITEM)lParam);
     4855          return TREEVIEW_EnsureVisible(infoPtr,(HTREEITEM)lParam);
    51044856
    51054857        case TVM_SORTCHILDRENCB:
    5106           return TREEVIEW_SortChildrenCB(hwnd, wParam, lParam);
     4858          return TREEVIEW_SortChildrenCB(infoPtr, wParam, lParam);
    51074859
    51084860        case TVM_ENDEDITLABELNOW:
    5109           return TREEVIEW_EndEditLabelNow (hwnd,(BOOL)wParam);
     4861          return TREEVIEW_EndEditLabelNow (infoPtr,(BOOL)wParam);
    51104862
    51114863        case TVM_GETISEARCHSTRINGA:
    5112           return TREEVIEW_GetISearchString(hwnd,(LPWSTR)lParam,FALSE);
     4864          return TREEVIEW_GetISearchString(infoPtr,(LPWSTR)lParam,FALSE);
    51134865
    51144866        case TVM_GETISEARCHSTRINGW:
    5115           return TREEVIEW_GetISearchString(hwnd,(LPWSTR)lParam,TRUE);
     4867          return TREEVIEW_GetISearchString(infoPtr,(LPWSTR)lParam,TRUE);
    51164868
    51174869        case TVM_GETTOOLTIPS:
    5118           return TREEVIEW_GetToolTips (hwnd);
     4870          return TREEVIEW_GetToolTips (infoPtr);
    51194871
    51204872        case TVM_SETTOOLTIPS:
    5121           return TREEVIEW_SetToolTips (hwnd, wParam);
     4873          return TREEVIEW_SetToolTips (infoPtr, wParam);
    51224874
    51234875        case TVM_SETINSERTMARK:
    5124           return TREEVIEW_SetInsertMark (hwnd,wParam, lParam);
     4876          return TREEVIEW_SetInsertMark (infoPtr,wParam, lParam);
    51254877
    51264878        case TVM_SETITEMHEIGHT:
    5127           return TREEVIEW_SetItemHeight (hwnd, wParam);
     4879          return TREEVIEW_SetItemHeight (infoPtr, wParam);
    51284880
    51294881        case TVM_GETITEMHEIGHT:
    5130           return TREEVIEW_GetItemHeight (hwnd);
     4882          return TREEVIEW_GetItemHeight (infoPtr);
    51314883
    51324884        case TVM_SETBKCOLOR:
    5133           return TREEVIEW_SetBkColor (hwnd, wParam, lParam);
     4885          return TREEVIEW_SetBkColor (infoPtr, wParam, lParam);
    51344886
    51354887        case TVM_SETTEXTCOLOR:
    5136           return TREEVIEW_SetTextColor (hwnd, wParam, lParam);
     4888          return TREEVIEW_SetTextColor (infoPtr, wParam, lParam);
    51374889
    51384890        case TVM_GETBKCOLOR:
    5139           return TREEVIEW_GetBkColor (hwnd);
     4891          return TREEVIEW_GetBkColor (infoPtr);
    51404892
    51414893        case TVM_GETTEXTCOLOR:
    5142           return TREEVIEW_GetTextColor (hwnd);
     4894          return TREEVIEW_GetTextColor (infoPtr);
    51434895
    51444896        case TVM_SETSCROLLTIME:
    5145           return TREEVIEW_SetScrollTime (hwnd, (UINT)wParam);
     4897          return TREEVIEW_SetScrollTime (infoPtr, (UINT)wParam);
    51464898
    51474899        case TVM_GETSCROLLTIME:
    5148           return TREEVIEW_GetScrollTime (hwnd);
     4900          return TREEVIEW_GetScrollTime (infoPtr);
    51494901
    51504902        case TVM_GETITEMSTATE:
    5151           return TREEVIEW_GetItemState (hwnd,wParam, lParam);
     4903          return TREEVIEW_GetItemState (infoPtr,wParam, lParam);
    51524904
    51534905        case TVM_GETLINECOLOR:
    5154           return TREEVIEW_GetLineColor (hwnd,wParam, lParam);
     4906          return TREEVIEW_GetLineColor (infoPtr,wParam, lParam);
    51554907
    51564908        case TVM_SETLINECOLOR:
    5157           return TREEVIEW_SetLineColor (hwnd,wParam, lParam);
     4909          return TREEVIEW_SetLineColor (infoPtr,wParam, lParam);
    51584910
    51594911        case TVM_SETINSERTMARKCOLOR:
    5160           return TREEVIEW_SetInsertMarkColor (hwnd,wParam, lParam);
     4912          return TREEVIEW_SetInsertMarkColor (infoPtr,wParam, lParam);
    51614913
    51624914        case TVM_GETINSERTMARKCOLOR:
    5163           return TREEVIEW_GetInsertMarkColor (hwnd,wParam, lParam);
     4915          return TREEVIEW_GetInsertMarkColor (infoPtr,wParam, lParam);
    51644916
    51654917        case WM_COMMAND:
    5166           return TREEVIEW_Command (hwnd, wParam, lParam);
     4918          return TREEVIEW_Command (infoPtr, wParam, lParam);
    51674919
    51684920        case WM_DESTROY:
    5169           return TREEVIEW_Destroy (hwnd);
     4921          return TREEVIEW_Destroy (infoPtr);
    51704922
    51714923        case WM_ENABLE:
    5172           return TREEVIEW_Enable(hwnd,wParam,lParam);
     4924          return TREEVIEW_Enable(infoPtr,wParam,lParam);
    51734925
    51744926        case WM_ERASEBKGND:
    5175           return TREEVIEW_EraseBackground (hwnd, wParam, lParam);
     4927          return TREEVIEW_EraseBackground (infoPtr, wParam, lParam);
    51764928
    51774929        case WM_GETDLGCODE:
    5178           return TREEVIEW_GetDlgCode(hwnd,wParam,lParam);
     4930          return TREEVIEW_GetDlgCode(infoPtr,wParam,lParam);
    51794931
    51804932        case WM_PAINT:
    5181           return TREEVIEW_Paint (hwnd, wParam, lParam);
     4933          return TREEVIEW_Paint (infoPtr, wParam, lParam);
    51824934
    51834935        case WM_GETFONT:
    5184           return TREEVIEW_GetFont (hwnd, wParam, lParam);
     4936          return TREEVIEW_GetFont (infoPtr, wParam, lParam);
    51854937
    51864938        case WM_SETFONT:
    5187           return TREEVIEW_SetFont (hwnd, wParam, lParam);
     4939          return TREEVIEW_SetFont (infoPtr, wParam, lParam);
    51884940
    51894941        case WM_KEYDOWN:
    5190           return TREEVIEW_KeyDown (hwnd, wParam, lParam);
     4942          return TREEVIEW_KeyDown (infoPtr, wParam, lParam);
    51914943
    51924944        case WM_CHAR:
    5193           return TREEVIEW_Char(hwnd,wParam,lParam);
     4945          return TREEVIEW_Char(infoPtr,wParam,lParam);
    51944946
    51954947        case WM_SETFOCUS:
    5196           return TREEVIEW_SetFocus (hwnd, wParam, lParam);
     4948          return TREEVIEW_SetFocus (infoPtr, wParam, lParam);
    51974949
    51984950        case WM_KILLFOCUS:
    5199           return TREEVIEW_KillFocus (hwnd, wParam, lParam);
     4951          return TREEVIEW_KillFocus (infoPtr, wParam, lParam);
    52004952
    52014953        case WM_MOUSEMOVE:
    5202           return TREEVIEW_MouseMove(hwnd,wParam,lParam);
     4954          return TREEVIEW_MouseMove(infoPtr,wParam,lParam);
    52034955
    52044956        case WM_LBUTTONDOWN:
    5205           return TREEVIEW_LButtonDown (hwnd, wParam, lParam);
     4957          return TREEVIEW_LButtonDown (infoPtr, wParam, lParam);
    52064958
    52074959        case WM_LBUTTONDBLCLK:
    5208           return TREEVIEW_LButtonDoubleClick (hwnd, wParam, lParam);
     4960          return TREEVIEW_LButtonDoubleClick (infoPtr, wParam, lParam);
    52094961
    52104962        case WM_RBUTTONDOWN:
    5211           return TREEVIEW_RButtonDown (hwnd, wParam, lParam);
     4963          return TREEVIEW_RButtonDown (infoPtr, wParam, lParam);
    52124964
    52134965        case WM_RBUTTONDBLCLK:
    5214           return TREEVIEW_RButtonDoubleClick(hwnd,wParam,lParam);
     4966          return TREEVIEW_RButtonDoubleClick(infoPtr,wParam,lParam);
    52154967
    52164968        case WM_STYLECHANGED:
    5217           return TREEVIEW_StyleChanged (hwnd, wParam, lParam);
     4969          return TREEVIEW_StyleChanged (infoPtr, wParam, lParam);
    52184970
    52194971        case WM_SYSCOLORCHANGE:
    5220           return TREEVIEW_SysColorChange(hwnd,wParam,lParam);
     4972          return TREEVIEW_SysColorChange(infoPtr,wParam,lParam);
    52214973
    52224974        case WM_SETCURSOR:
    5223           return TREEVIEW_SetCursor(hwnd,wParam,lParam);
     4975          return TREEVIEW_SetCursor(infoPtr,wParam,lParam);
    52244976
    52254977        case WM_SETREDRAW:
    5226           return TREEVIEW_SetRedraw(hwnd,wParam,lParam);
     4978          return TREEVIEW_SetRedraw(infoPtr,wParam,lParam);
    52274979
    52284980        case WM_TIMER:
    5229           return TREEVIEW_HandleTimer (hwnd, wParam, lParam);
     4981          return TREEVIEW_HandleTimer (infoPtr, wParam, lParam);
    52304982
    52314983        case WM_SIZE:
    5232           return TREEVIEW_Size (hwnd, wParam,lParam);
     4984          return TREEVIEW_Size (infoPtr, wParam,lParam);
    52334985
    52344986        case WM_HSCROLL:
    5235           return TREEVIEW_HScroll (hwnd, wParam, lParam);
     4987          return TREEVIEW_HScroll (infoPtr, wParam, lParam);
    52364988
    52374989        case WM_VSCROLL:
    5238           return TREEVIEW_VScroll (hwnd, wParam, lParam);
     4990          return TREEVIEW_VScroll (infoPtr, wParam, lParam);
    52394991
    52404992        case WM_MOUSEWHEEL:
    5241           return TREEVIEW_MouseWheel (hwnd, wParam, lParam);
     4993          return TREEVIEW_MouseWheel (infoPtr, wParam, lParam);
    52424994
    52434995        case WM_DRAWITEM:
     
    52464998
    52474999        default:
     5000def:
    52485001          return defComCtl32ProcA(hwnd,uMsg,wParam,lParam);
    52495002      }
     
    52515004}
    52525005
     5006/* Tree Verification ****************************************************/
     5007
     5008#ifndef NDEBUG
     5009static inline void
     5010TREEVIEW_VerifyChildren(TREEVIEW_INFO *infoPtr, TREEVIEW_ITEM *item);
     5011
     5012static inline void TREEVIEW_VerifyItemCommon(TREEVIEW_INFO *infoPtr,
     5013                                             TREEVIEW_ITEM *item)
     5014{
     5015    TREEVIEW_WriteVerify("TV: checking infoPtr");
     5016    assert(infoPtr != NULL);
     5017    TREEVIEW_WriteVerify("TV: checking item");
     5018    assert(item != NULL);
     5019
     5020    /* both NULL, or both non-null */
     5021    TREEVIEW_WriteVerify("TV: checking firstChild,lastChild");
     5022    assert((item->firstChild == NULL) == (item->lastChild == NULL));
     5023
     5024    TREEVIEW_WriteVerify("TV: checking firstChild,lastChild != item");
     5025    assert(item->firstChild != item);
     5026    assert(item->lastChild != item);
     5027
     5028    TREEVIEW_WriteVerify("TV: checking firstChild");
     5029    if (item->firstChild)
     5030    {
     5031        assert(item->firstChild->parent == item);
     5032        assert(item->firstChild->prevSibling == NULL);
     5033    }
     5034
     5035    TREEVIEW_WriteVerify("TV: checking lastChild");
     5036    if (item->lastChild)
     5037    {
     5038        TREEVIEW_WriteVerify("TV: checking lastChild->parent");
     5039        assert(item->lastChild->parent == item);
     5040        TREEVIEW_WriteVerify("TV: checking lastChild->nextSibling");
     5041        assert(item->lastChild->nextSibling == NULL);
     5042    }
     5043
     5044    TREEVIEW_WriteVerify("TV: checking nextSibling");
     5045    assert(item->nextSibling != item);
     5046    if (item->nextSibling)
     5047    {
     5048        assert(item->nextSibling->parent == item->parent);
     5049        assert(item->nextSibling->prevSibling == item);
     5050    }
     5051
     5052    TREEVIEW_WriteVerify("TV: checking prevSibling");
     5053    assert(item->prevSibling != item);
     5054    if (item->prevSibling)
     5055    {
     5056        assert(item->prevSibling->parent == item->parent);
     5057        assert(item->prevSibling->nextSibling == item);
     5058    }
     5059}
     5060
     5061static inline void
     5062TREEVIEW_VerifyItem(TREEVIEW_INFO *infoPtr, TREEVIEW_ITEM *item)
     5063{
     5064    TREEVIEW_WriteVerify("TV: checking item");
     5065    assert(item != NULL);
     5066
     5067    TREEVIEW_WriteVerify("TV: checking parent, iLevel");
     5068    assert(item->parent != NULL);
     5069    assert(item->parent != item);
     5070    assert(item->iLevel == item->parent->iLevel + 1);
     5071
     5072    TREEVIEW_WriteVerify("TV: checking PtrIndex");
     5073    assert(DPA_GetPtrIndex(infoPtr->items, item) != -1);
     5074
     5075    TREEVIEW_VerifyItemCommon(infoPtr, item);
     5076
     5077    TREEVIEW_VerifyChildren(infoPtr, item);
     5078}
     5079
     5080static inline void
     5081TREEVIEW_VerifyChildren(TREEVIEW_INFO *infoPtr, TREEVIEW_ITEM *item)
     5082{
     5083    TREEVIEW_ITEM *child;
     5084    assert(item != NULL);
     5085
     5086    for (child = item->firstChild; child != NULL; child = child->nextSibling)
     5087        TREEVIEW_VerifyItem(infoPtr, child);
     5088}
     5089
     5090static inline void
     5091TREEVIEW_VerifyRoot(TREEVIEW_INFO *infoPtr)
     5092{
     5093    TREEVIEW_ITEM *root = infoPtr->root;
     5094
     5095    TREEVIEW_WriteVerify("TV: checking root");
     5096    assert(root != NULL);
     5097    TREEVIEW_WriteVerify("TV: checking iLevel");
     5098    assert(root->iLevel == -1);
     5099    TREEVIEW_WriteVerify("TV: checking parent");
     5100    assert(root->parent == NULL);
     5101    TREEVIEW_WriteVerify("TV: checking prevSibling");
     5102    assert(root->prevSibling == NULL);
     5103
     5104    TREEVIEW_VerifyItemCommon(infoPtr, root);
     5105
     5106    TREEVIEW_VerifyChildren(infoPtr, root);
     5107}
     5108
     5109static void
     5110TREEVIEW_VerifyTree(TREEVIEW_INFO *infoPtr)
     5111{
     5112  TREEVIEW_WriteVerify("TV: checking infoPtr");
     5113  assert(infoPtr != NULL);
     5114
     5115  TREEVIEW_VerifyRoot(infoPtr);
     5116}
     5117#endif
     5118
     5119/* Class Registration ***************************************************/
    52535120
    52545121VOID TREEVIEW_Register (VOID)
Note: See TracChangeset for help on using the changeset viewer.