- Preliminary implementation of TVS_NOSCROLL and TVS_NOHSCROLL
[wine] / dlls / comctl32 / treeview.c
1 /* Treeview control
2  *
3  * Copyright 1998 Eric Kohl <ekohl@abo.rhein-zeitung.de>
4  * Copyright 1998,1999 Alex Priem <alexp@sci.kun.nl>
5  * Copyright 1999 Sylvain St-Germain
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Lesser General Public
9  * License as published by the Free Software Foundation; either
10  * version 2.1 of the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Lesser General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public
18  * License along with this library; if not, write to the Free Software
19  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
20  *
21  * Note that TREEVIEW_INFO * and HTREEITEM are the same thing.
22  *
23  * Note2: All items always! have valid (allocated) pszText field.
24  *      If item's text == LPSTR_TEXTCALLBACKA we allocate buffer
25  *      of size TEXT_CALLBACK_SIZE in DoSetItem.
26  *      We use callbackMask to keep track of fields to be updated.
27  *
28  * TODO:
29  *   missing notifications: NM_SETCURSOR, TVN_GETINFOTIP, TVN_KEYDOWN,
30  *      TVN_SETDISPINFO, TVN_SINGLEEXPAND
31  *
32  *   missing styles: TVS_FULLROWSELECT, TVS_INFOTIP, TVS_NOSCROLL,
33  *      TVS_RTLREADING, TVS_TRACKSELECT
34  *
35  *   missing item styles: TVIS_CUT, TVIS_EXPANDPARTIAL
36  *
37  *   Make the insertion mark look right.
38  *   Scroll (instead of repaint) as much as possible.
39  */
40
41 #include <assert.h>
42 #include <ctype.h>
43 #include <string.h>
44 #include <limits.h>
45 #include <stdlib.h>
46
47 #include "winbase.h"
48 #include "wingdi.h"
49 #include "commctrl.h"
50 #include "comctl32.h"
51 #include "wine/debug.h"
52
53 /* internal structures */
54
55 typedef struct _TREEITEM    /* HTREEITEM is a _TREEINFO *. */
56 {
57   UINT      callbackMask;
58   UINT      state;
59   UINT      stateMask;
60   LPSTR     pszText;
61   int       cchTextMax;
62   int       iImage;
63   int       iSelectedImage;
64   int       cChildren;
65   LPARAM    lParam;
66   int       iIntegral;      /* item height multiplier (1 is normal) */
67   int       iLevel;         /* indentation level:0=root level */
68   HTREEITEM parent;         /* handle to parent or 0 if at root */
69   HTREEITEM firstChild;     /* handle to first child or 0 if no child */
70   HTREEITEM lastChild;
71   HTREEITEM prevSibling;    /* handle to prev item in list, 0 if first */
72   HTREEITEM nextSibling;    /* handle to next item in list, 0 if last */
73   RECT      rect;
74   LONG      linesOffset;
75   LONG      stateOffset;
76   LONG      imageOffset;
77   LONG      textOffset;
78   LONG      textWidth;      /* horizontal text extent for pszText */
79   LONG      visibleOrder;   /* visible ordering, 0 is first visible item */
80 } TREEVIEW_ITEM;
81
82
83 typedef struct tagTREEVIEW_INFO
84 {
85   HWND          hwnd;
86   HWND          hwndNotify;     /* Owner window to send notifications to */
87   DWORD         dwStyle;
88   HTREEITEM     root;
89   UINT          uInternalStatus;
90   INT           Timer;
91   UINT          uNumItems;      /* number of valid TREEVIEW_ITEMs */
92   INT           cdmode;         /* last custom draw setting */
93   UINT          uScrollTime;    /* max. time for scrolling in milliseconds */
94   BOOL          bRedraw;        /* if FALSE we validate but don't redraw in TREEVIEW_Paint() */
95
96   UINT          uItemHeight;    /* item height */
97   BOOL          bHeightSet;
98
99   LONG          clientWidth;    /* width of control window */
100   LONG          clientHeight;   /* height of control window */
101
102   LONG          treeWidth;      /* width of visible tree items */
103   LONG          treeHeight;     /* height of visible tree items */
104
105   UINT          uIndent;        /* indentation in pixels */
106   HTREEITEM     selectedItem;   /* handle to selected item or 0 if none */
107   HTREEITEM     hotItem;        /* handle currently under cursor, 0 if none */
108   HTREEITEM     focusedItem;    /* item that was under the cursor when WM_LBUTTONDOWN was received */
109
110   HTREEITEM     firstVisible;   /* handle to first visible item */
111   LONG          maxVisibleOrder;
112   HTREEITEM     dropItem;       /* handle to item selected by drag cursor */
113   HTREEITEM     insertMarkItem; /* item after which insertion mark is placed */
114   BOOL          insertBeforeorAfter; /* flag used by TVM_SETINSERTMARK */
115   HIMAGELIST    dragList;       /* Bitmap of dragged item */
116   LONG          scrollX;
117   COLORREF      clrBk;
118   COLORREF      clrText;
119   COLORREF      clrLine;
120   COLORREF      clrInsertMark;
121   HFONT         hFont;
122   HFONT         hBoldFont;
123   HWND          hwndToolTip;
124
125   HWND          hwndEdit;
126   WNDPROC       wpEditOrig;     /* orig window proc for subclassing edit */
127   BOOL          bIgnoreEditKillFocus;
128   BOOL          bLabelChanged;
129
130   BOOL          bNtfUnicode;    /* TRUE if should send NOTIFY with W */
131   BOOL          bUnicode;       /* set by CCM_SETUNICODEFORMAT       */
132   HIMAGELIST    himlNormal;
133   int           normalImageHeight;
134   int           normalImageWidth;
135   HIMAGELIST    himlState;
136   int           stateImageHeight;
137   int           stateImageWidth;
138   HDPA          items;
139
140   DWORD lastKeyPressTimestamp; /* Added */
141   WPARAM charCode; /* Added */
142   INT nSearchParamLength; /* Added */
143   CHAR szSearchParam[ MAX_PATH ]; /* Added */
144 } TREEVIEW_INFO;
145
146
147 /******** Defines that TREEVIEW_ProcessLetterKeys uses ****************/
148 #define KEY_DELAY       450
149
150 /* bitflags for infoPtr->uInternalStatus */
151
152 #define TV_HSCROLL      0x01    /* treeview too large to fit in window */
153 #define TV_VSCROLL      0x02    /* (horizontal/vertical) */
154 #define TV_LDRAG                0x04    /* Lbutton pushed to start drag */
155 #define TV_LDRAGGING    0x08    /* Lbutton pushed, mouse moved. */
156 #define TV_RDRAG                0x10    /* dito Rbutton */
157 #define TV_RDRAGGING    0x20    
158
159 /* bitflags for infoPtr->timer */
160
161 #define TV_EDIT_TIMER    2
162 #define TV_EDIT_TIMER_SET 2
163
164
165 VOID TREEVIEW_Register (VOID);
166 VOID TREEVIEW_Unregister (VOID);
167
168
169 WINE_DEFAULT_DEBUG_CHANNEL(treeview);
170
171
172 #define TEXT_CALLBACK_SIZE 260
173
174 #define TREEVIEW_LEFT_MARGIN 8
175
176 #define MINIMUM_INDENT 19
177
178 #define CALLBACK_MASK_ALL (TVIF_TEXT|TVIF_CHILDREN|TVIF_IMAGE|TVIF_SELECTEDIMAGE)
179
180 #define STATEIMAGEINDEX(x) (((x) >> 12) & 0x0f)
181 #define OVERLAYIMAGEINDEX(x) (((x) >> 8) & 0x0f)
182 #define ISVISIBLE(x)         ((x)->visibleOrder >= 0)
183
184
185 typedef VOID (*TREEVIEW_ItemEnumFunc)(TREEVIEW_INFO *, TREEVIEW_ITEM *,LPVOID);
186
187
188 static VOID TREEVIEW_Invalidate(TREEVIEW_INFO *, TREEVIEW_ITEM *);
189
190 static LRESULT TREEVIEW_DoSelectItem(TREEVIEW_INFO *, INT, HTREEITEM, INT);
191 static VOID TREEVIEW_SetFirstVisible(TREEVIEW_INFO *, TREEVIEW_ITEM *, BOOL);
192 static LRESULT TREEVIEW_EnsureVisible(TREEVIEW_INFO *, HTREEITEM, BOOL);
193 static LRESULT TREEVIEW_RButtonUp(TREEVIEW_INFO *, LPPOINT);
194 static LRESULT TREEVIEW_EndEditLabelNow(TREEVIEW_INFO *infoPtr, BOOL bCancel);
195 static VOID TREEVIEW_UpdateScrollBars(TREEVIEW_INFO *infoPtr);
196 static LRESULT TREEVIEW_HScroll(TREEVIEW_INFO *, WPARAM);
197 static LRESULT TREEVIEW_NotifyFormat (TREEVIEW_INFO *infoPtr, WPARAM wParam, LPARAM lParam);
198
199
200 /* Random Utilities *****************************************************/
201
202 #ifndef NDEBUG
203 static inline void
204 TREEVIEW_VerifyTree(TREEVIEW_INFO *infoPtr)
205 {
206     (void)infoPtr;
207 }
208 #else
209 /* The definition is at the end of the file. */
210 static void TREEVIEW_VerifyTree(TREEVIEW_INFO *infoPtr);
211 #endif
212
213 /* Returns the treeview private data if hwnd is a treeview.
214  * Otherwise returns an undefined value. */
215 static TREEVIEW_INFO *
216 TREEVIEW_GetInfoPtr(HWND hwnd)
217 {
218     return (TREEVIEW_INFO *)GetWindowLongA(hwnd, 0);
219 }
220
221 /* Don't call this. Nothing wants an item index. */
222 static inline int
223 TREEVIEW_GetItemIndex(TREEVIEW_INFO *infoPtr, HTREEITEM handle)
224 {
225     assert(infoPtr != NULL);
226
227     return DPA_GetPtrIndex(infoPtr->items, handle);
228 }
229
230 /***************************************************************************
231  * This method checks that handle is an item for this tree.
232  */
233 static BOOL
234 TREEVIEW_ValidItem(TREEVIEW_INFO *infoPtr, HTREEITEM handle)
235 {
236     if (TREEVIEW_GetItemIndex(infoPtr, handle) == -1)
237     {
238         TRACE("invalid item %p\n", handle);
239         return FALSE;
240     }
241     else
242         return TRUE;
243 }
244
245 static HFONT
246 TREEVIEW_CreateBoldFont(HFONT hOrigFont)
247 {
248     LOGFONTA font;
249
250     GetObjectA(hOrigFont, sizeof(font), &font);
251     font.lfWeight = FW_BOLD;
252     return CreateFontIndirectA(&font);
253 }
254
255 static inline HFONT
256 TREEVIEW_FontForItem(TREEVIEW_INFO *infoPtr, TREEVIEW_ITEM *item)
257 {
258     return (item->state & TVIS_BOLD) ? infoPtr->hBoldFont : infoPtr->hFont;
259 }
260
261 /* for trace/debugging purposes only */
262 static const char *
263 TREEVIEW_ItemName(TREEVIEW_ITEM *item)
264 {
265     if (item == NULL) return "<null item>";
266     if (item->pszText == LPSTR_TEXTCALLBACKA) return "<callback>";
267     if (item->pszText == NULL) return "<null>";
268     return item->pszText;
269 }
270
271 /* An item is not a child of itself. */
272 static BOOL
273 TREEVIEW_IsChildOf(TREEVIEW_ITEM *parent, TREEVIEW_ITEM *child)
274 {
275     do
276     {
277         child = child->parent;
278         if (child == parent) return TRUE;
279     } while (child != NULL);
280
281     return FALSE;
282 }
283
284
285 /* Tree Traversal *******************************************************/
286
287 /***************************************************************************
288  * This method returns the last expanded sibling or child child item
289  * of a tree node
290  */
291 static TREEVIEW_ITEM *
292 TREEVIEW_GetLastListItem(TREEVIEW_INFO *infoPtr, TREEVIEW_ITEM *wineItem)
293 {
294     if (!wineItem)
295        return NULL;
296
297     while (wineItem->lastChild)
298     {
299        if (wineItem->state & TVIS_EXPANDED)
300           wineItem = wineItem->lastChild;
301        else
302           break;
303     }
304
305     if (wineItem == infoPtr->root)
306         return NULL;
307
308     return wineItem;
309 }
310
311 /***************************************************************************
312  * This method returns the previous non-hidden item in the list not 
313  * considering the tree hierarchy.
314  */
315 static TREEVIEW_ITEM *
316 TREEVIEW_GetPrevListItem(TREEVIEW_INFO *infoPtr, TREEVIEW_ITEM *tvItem)
317 {
318     if (tvItem->prevSibling)
319     {
320         /* This item has a prevSibling, get the last item in the sibling's tree. */
321         TREEVIEW_ITEM *upItem = tvItem->prevSibling;
322
323         if ((upItem->state & TVIS_EXPANDED) && upItem->lastChild != NULL)
324             return TREEVIEW_GetLastListItem(infoPtr, upItem->lastChild);
325         else
326             return upItem;
327     }
328     else
329     {
330         /* this item does not have a prevSibling, get the parent */
331         return (tvItem->parent != infoPtr->root) ? tvItem->parent : NULL;
332     }
333 }
334
335
336 /***************************************************************************
337  * This method returns the next physical item in the treeview not 
338  * considering the tree hierarchy.
339  */
340 static TREEVIEW_ITEM *
341 TREEVIEW_GetNextListItem(TREEVIEW_INFO *infoPtr, TREEVIEW_ITEM *tvItem)
342 {
343     assert(tvItem != NULL);
344
345     /* 
346      * If this item has children and is expanded, return the first child
347      */
348     if ((tvItem->state & TVIS_EXPANDED) && tvItem->firstChild != NULL)
349     {
350         return tvItem->firstChild;
351     }
352
353
354     /*
355      * try to get the sibling
356      */
357     if (tvItem->nextSibling)
358         return tvItem->nextSibling;
359
360     /*
361      * Otherwise, get the parent's sibling.
362      */
363     while (tvItem->parent)
364     {
365         tvItem = tvItem->parent;
366
367         if (tvItem->nextSibling)
368             return tvItem->nextSibling;
369     }
370
371     return NULL;
372 }
373
374 /***************************************************************************
375  * This method returns the nth item starting at the given item.  It returns 
376  * the last item (or first) we we run out of items.
377  *
378  * Will scroll backward if count is <0.
379  *             forward if count is >0.
380  */
381 static TREEVIEW_ITEM *
382 TREEVIEW_GetListItem(TREEVIEW_INFO *infoPtr, TREEVIEW_ITEM *wineItem,
383                      LONG count)
384 {
385     TREEVIEW_ITEM *(*next_item)(TREEVIEW_INFO *, TREEVIEW_ITEM *);
386     TREEVIEW_ITEM *previousItem;
387
388     assert(wineItem != NULL);
389
390     if (count > 0)
391     {
392         next_item = TREEVIEW_GetNextListItem;
393     }
394     else if (count < 0)
395     {
396         count = -count;
397         next_item = TREEVIEW_GetPrevListItem;
398     }
399     else
400         return wineItem;
401
402     do
403     {
404         previousItem = wineItem;
405         wineItem = next_item(infoPtr, wineItem);
406
407     } while (--count && wineItem != NULL);
408
409
410     return wineItem ? wineItem : previousItem;
411 }
412
413 /* Notifications ************************************************************/
414
415 static LRESULT
416 TREEVIEW_SendRealNotify(TREEVIEW_INFO *infoPtr, WPARAM wParam, LPARAM lParam)
417 {
418     if (infoPtr->bNtfUnicode)
419         return (BOOL)SendMessageW(infoPtr->hwndNotify, WM_NOTIFY,
420                                   wParam, lParam);
421     else
422         return (BOOL)SendMessageA(infoPtr->hwndNotify, WM_NOTIFY,
423                                   wParam, lParam);
424 }
425
426 static BOOL
427 TREEVIEW_SendSimpleNotify(TREEVIEW_INFO *infoPtr, UINT code)
428 {
429     NMHDR nmhdr;
430     HWND hwnd = infoPtr->hwnd;
431
432     TRACE("%x\n", code);
433     nmhdr.hwndFrom = hwnd;
434     nmhdr.idFrom = GetWindowLongA(hwnd, GWL_ID);
435     nmhdr.code = code;
436
437     return (BOOL)TREEVIEW_SendRealNotify(infoPtr,
438                                   (WPARAM)nmhdr.idFrom, (LPARAM)&nmhdr);
439 }
440
441 static VOID
442 TREEVIEW_TVItemFromItem(TREEVIEW_INFO *infoPtr, UINT mask, TVITEMA *tvItem, TREEVIEW_ITEM *item)
443 {
444     tvItem->mask = mask;
445     tvItem->hItem = item;
446     tvItem->state = item->state;
447     tvItem->stateMask = 0;
448     tvItem->iImage = item->iImage;
449     tvItem->cchTextMax = item->cchTextMax;
450     tvItem->iImage = item->iImage;
451     tvItem->iSelectedImage = item->iSelectedImage;
452     tvItem->cChildren = item->cChildren;
453     tvItem->lParam = item->lParam;
454
455     /* **** **** **** **** WARNING **** **** **** **** */
456     /* This control stores all the data in A format    */
457     /* we will convert it to W if the notify format    */
458     /* is Unicode.                                     */
459     /* **** **** **** **** WARNING **** **** **** **** */
460     if (infoPtr->bNtfUnicode) {
461         INT len = MultiByteToWideChar( CP_ACP, 0, item->pszText, -1, NULL, 0 );
462         if (len > 1) {
463             tvItem->pszText = (LPSTR)COMCTL32_Alloc (len*sizeof(WCHAR));
464             MultiByteToWideChar( CP_ACP, 0, item->pszText, -1, (LPWSTR)tvItem->pszText, len*sizeof(WCHAR) );
465         }
466     }
467     else
468         tvItem->pszText = item->pszText;
469 }
470
471 static BOOL
472 TREEVIEW_SendTreeviewNotify(TREEVIEW_INFO *infoPtr, UINT code, UINT action,
473                             UINT mask, HTREEITEM oldItem, HTREEITEM newItem)
474 {
475     HWND hwnd = infoPtr->hwnd;
476     NMTREEVIEWA nmhdr;
477     BOOL ret;
478
479     TRACE("code:%x action:%x olditem:%p newitem:%p\n",
480           code, action, oldItem, newItem);
481
482     ZeroMemory(&nmhdr, sizeof(NMTREEVIEWA));
483
484     nmhdr.hdr.hwndFrom = hwnd;
485     nmhdr.hdr.idFrom = GetWindowLongA(hwnd, GWL_ID);
486     nmhdr.hdr.code = code;
487     nmhdr.action = action;
488
489     if (oldItem)
490         TREEVIEW_TVItemFromItem(infoPtr, mask, &nmhdr.itemOld, oldItem);
491
492     if (newItem)
493         TREEVIEW_TVItemFromItem(infoPtr, mask, &nmhdr.itemNew, newItem);
494
495     nmhdr.ptDrag.x = 0;
496     nmhdr.ptDrag.y = 0;
497
498     ret = (BOOL)TREEVIEW_SendRealNotify(infoPtr,
499                               (WPARAM)GetWindowLongA(hwnd, GWL_ID),
500                               (LPARAM)&nmhdr);
501     if (infoPtr->bNtfUnicode) {
502         COMCTL32_Free(nmhdr.itemOld.pszText);
503         COMCTL32_Free(nmhdr.itemNew.pszText);
504     }
505     return ret;
506 }
507
508 static BOOL
509 TREEVIEW_SendTreeviewDnDNotify(TREEVIEW_INFO *infoPtr, UINT code,
510                                HTREEITEM dragItem, POINT pt)
511 {
512     HWND hwnd = infoPtr->hwnd;
513     NMTREEVIEWA nmhdr;
514
515     TRACE("code:%x dragitem:%p\n", code, dragItem);
516
517     nmhdr.hdr.hwndFrom = hwnd;
518     nmhdr.hdr.idFrom = GetWindowLongA(hwnd, GWL_ID);
519     nmhdr.hdr.code = code;
520     nmhdr.action = 0;
521     nmhdr.itemNew.mask = TVIF_STATE | TVIF_PARAM | TVIF_HANDLE;
522     nmhdr.itemNew.hItem = dragItem;
523     nmhdr.itemNew.state = dragItem->state;
524     nmhdr.itemNew.lParam = dragItem->lParam;
525
526     nmhdr.ptDrag.x = pt.x;
527     nmhdr.ptDrag.y = pt.y;
528
529     return (BOOL)TREEVIEW_SendRealNotify(infoPtr,
530                               (WPARAM)GetWindowLongA(hwnd, GWL_ID),
531                               (LPARAM)&nmhdr);
532 }
533
534
535 static BOOL
536 TREEVIEW_SendCustomDrawNotify(TREEVIEW_INFO *infoPtr, DWORD dwDrawStage,
537                               HDC hdc, RECT rc)
538 {
539     HWND hwnd = infoPtr->hwnd;
540     NMTVCUSTOMDRAW nmcdhdr;
541     LPNMCUSTOMDRAW nmcd;
542
543     TRACE("drawstage:%lx hdc:%x\n", dwDrawStage, hdc);
544
545     nmcd = &nmcdhdr.nmcd;
546     nmcd->hdr.hwndFrom = hwnd;
547     nmcd->hdr.idFrom = GetWindowLongA(hwnd, GWL_ID);
548     nmcd->hdr.code = NM_CUSTOMDRAW;
549     nmcd->dwDrawStage = dwDrawStage;
550     nmcd->hdc = hdc;
551     nmcd->rc = rc;
552     nmcd->dwItemSpec = 0;
553     nmcd->uItemState = 0;
554     nmcd->lItemlParam = 0;
555     nmcdhdr.clrText = infoPtr->clrText;
556     nmcdhdr.clrTextBk = infoPtr->clrBk;
557     nmcdhdr.iLevel = 0;
558
559     return (BOOL)TREEVIEW_SendRealNotify(infoPtr,
560                               (WPARAM)GetWindowLongA(hwnd, GWL_ID),
561                               (LPARAM)&nmcdhdr);
562 }
563
564
565
566 /* FIXME: need to find out when the flags in uItemState need to be set */
567
568 static BOOL
569 TREEVIEW_SendCustomDrawItemNotify(TREEVIEW_INFO *infoPtr, HDC hdc,
570                                   TREEVIEW_ITEM *wineItem, UINT uItemDrawState)
571 {
572     HWND hwnd = infoPtr->hwnd;
573     NMTVCUSTOMDRAW nmcdhdr;
574     LPNMCUSTOMDRAW nmcd;
575     DWORD dwDrawStage, dwItemSpec;
576     UINT uItemState;
577     INT retval;
578
579     dwDrawStage = CDDS_ITEM | uItemDrawState;
580     dwItemSpec = (DWORD)wineItem;
581     uItemState = 0;
582     if (wineItem->state & TVIS_SELECTED)
583         uItemState |= CDIS_SELECTED;
584     if (wineItem == infoPtr->selectedItem)
585         uItemState |= CDIS_FOCUS;
586     if (wineItem == infoPtr->hotItem)
587         uItemState |= CDIS_HOT;
588
589     nmcd = &nmcdhdr.nmcd;
590     nmcd->hdr.hwndFrom = hwnd;
591     nmcd->hdr.idFrom = GetWindowLongA(hwnd, GWL_ID);
592     nmcd->hdr.code = NM_CUSTOMDRAW;
593     nmcd->dwDrawStage = dwDrawStage;
594     nmcd->hdc = hdc;
595     nmcd->rc = wineItem->rect;
596     nmcd->dwItemSpec = dwItemSpec;
597     nmcd->uItemState = uItemState;
598     nmcd->lItemlParam = wineItem->lParam;
599     nmcdhdr.clrText = infoPtr->clrText;
600     nmcdhdr.clrTextBk = infoPtr->clrBk;
601     nmcdhdr.iLevel = wineItem->iLevel;
602
603     TRACE("drawstage:%lx hdc:%x item:%lx, itemstate:%x, lItemlParam:%lx\n",
604           nmcd->dwDrawStage, nmcd->hdc, nmcd->dwItemSpec,
605           nmcd->uItemState, nmcd->lItemlParam);
606
607     retval = TREEVIEW_SendRealNotify(infoPtr,
608                           (WPARAM)GetWindowLongA(hwnd, GWL_ID),
609                           (LPARAM)&nmcdhdr);
610
611     infoPtr->clrText = nmcdhdr.clrText;
612     infoPtr->clrBk = nmcdhdr.clrTextBk;
613     return (BOOL)retval;
614 }
615
616 static BOOL
617 TREEVIEW_BeginLabelEditNotify(TREEVIEW_INFO *infoPtr, TREEVIEW_ITEM *editItem)
618 {
619     HWND hwnd = infoPtr->hwnd;
620     LPSTR allocated = NULL;
621     NMTVDISPINFOA tvdi;
622     BOOL ret;
623
624     tvdi.hdr.hwndFrom = hwnd;
625     tvdi.hdr.idFrom = GetWindowLongA(hwnd, GWL_ID);
626     tvdi.hdr.code = TVN_BEGINLABELEDITA;
627
628     tvdi.item.mask = TVIF_HANDLE | TVIF_STATE | TVIF_PARAM | TVIF_TEXT;
629     tvdi.item.hItem = editItem;
630     tvdi.item.state = editItem->state;
631     tvdi.item.lParam = editItem->lParam;
632     if (infoPtr->bNtfUnicode) {
633         INT len = MultiByteToWideChar( CP_ACP, 0, editItem->pszText, -1, NULL, 0 );
634         if (len > 1) {
635             tvdi.item.pszText = allocated = (LPSTR)COMCTL32_Alloc (len*sizeof(WCHAR));
636             MultiByteToWideChar( CP_ACP, 0, editItem->pszText, -1, (LPWSTR)tvdi.item.pszText, len*sizeof(WCHAR) );
637             tvdi.item.cchTextMax = len*sizeof(WCHAR);
638         }
639         else {
640             tvdi.item.pszText = editItem->pszText;  /* ??? */
641             tvdi.item.cchTextMax = editItem->cchTextMax;  /* ??? */
642         }
643     }
644     else {
645         tvdi.item.pszText = editItem->pszText;
646         tvdi.item.cchTextMax = editItem->cchTextMax;
647     }
648
649     ret = (BOOL)TREEVIEW_SendRealNotify(infoPtr,
650                                          tvdi.hdr.idFrom,
651                                         (LPARAM)&tvdi);
652     if (allocated)
653         COMCTL32_Free(allocated);
654     return ret;
655 }
656
657 static void
658 TREEVIEW_UpdateDispInfo(TREEVIEW_INFO *infoPtr, TREEVIEW_ITEM *wineItem,
659                         UINT mask)
660 {
661     NMTVDISPINFOA callback;
662     HWND hwnd = infoPtr->hwnd;
663
664     mask &= wineItem->callbackMask;
665
666     if (mask == 0) return;
667
668     callback.hdr.hwndFrom         = hwnd;
669     callback.hdr.idFrom           = GetWindowLongA(hwnd, GWL_ID);
670     callback.hdr.code             = (infoPtr->bNtfUnicode) ? TVN_GETDISPINFOW :
671                                                    TVN_GETDISPINFOA;
672
673     /* 'state' always contains valid value, as well as 'lParam'.
674      * All other parameters are uninitialized.
675      */
676     callback.item.pszText         = wineItem->pszText;
677     callback.item.cchTextMax      = wineItem->cchTextMax;
678     callback.item.mask            = mask;
679     callback.item.hItem           = wineItem;
680     callback.item.state           = wineItem->state;
681     callback.item.lParam          = wineItem->lParam;
682
683     /* If text is changed we need to recalculate textWidth */
684     if (mask & TVIF_TEXT)
685        wineItem->textWidth = 0;
686
687     TREEVIEW_SendRealNotify(infoPtr,
688                             (WPARAM)callback.hdr.idFrom, (LPARAM)&callback);
689
690     /* It may have changed due to a call to SetItem. */ 
691     mask &= wineItem->callbackMask;
692
693     if ((mask & TVIF_TEXT) && callback.item.pszText != wineItem->pszText)
694     {
695         /* Instead of copying text into our buffer user specified its own */
696         if (infoPtr->bNtfUnicode) {
697             LPWSTR newText;
698             int buflen;
699             int len = WideCharToMultiByte( CP_ACP, 0, 
700                                            (LPWSTR)callback.item.pszText, -1,
701                                            NULL, 0, NULL, NULL );
702             buflen = max((len+1)*sizeof(WCHAR), TEXT_CALLBACK_SIZE);
703             newText = (LPWSTR)COMCTL32_ReAlloc(wineItem->pszText, buflen);
704
705             TRACE("returned wstr %s, len=%d, buflen=%d\n",
706                   debugstr_w((LPWSTR)callback.item.pszText), len, buflen);
707
708             if (newText)
709             {
710                 wineItem->pszText = (LPSTR)newText;
711                 WideCharToMultiByte( CP_ACP, 0, 
712                                      (LPWSTR)callback.item.pszText, -1,
713                                      wineItem->pszText, buflen, 
714                                      NULL, NULL );
715                 wineItem->cchTextMax = buflen;
716             }
717             /* If ReAlloc fails we have nothing to do, but keep original text */
718         }
719         else {
720             int len = max(lstrlenA(callback.item.pszText) + 1, 
721                           TEXT_CALLBACK_SIZE);
722             LPSTR newText = COMCTL32_ReAlloc(wineItem->pszText, len);
723
724             TRACE("returned str %s, len=%d\n",
725                   debugstr_a(callback.item.pszText), len);
726
727             if (newText)
728             {
729                 wineItem->pszText = newText;
730                 strcpy(wineItem->pszText, callback.item.pszText);
731                 wineItem->cchTextMax = len;
732             }
733             /* If ReAlloc fails we have nothing to do, but keep original text */
734         }
735     }
736     else if (mask & TVIF_TEXT) {
737         /* User put text into our buffer, that is ok unless W string */
738         if (infoPtr->bNtfUnicode) {
739             LPWSTR newText;
740             LPSTR oldText = NULL;
741             int buflen;
742             int len = WideCharToMultiByte( CP_ACP, 0, 
743                                            (LPWSTR)callback.item.pszText, -1,
744                                            NULL, 0, NULL, NULL );
745             buflen = max((len+1)*sizeof(WCHAR), TEXT_CALLBACK_SIZE);
746             newText = (LPWSTR)COMCTL32_Alloc(buflen);
747
748             TRACE("same buffer wstr %s, len=%d, buflen=%d\n",
749                   debugstr_w((LPWSTR)callback.item.pszText), len, buflen);
750
751             if (newText)
752             {
753                 oldText = wineItem->pszText;
754                 wineItem->pszText = (LPSTR)newText;
755                 WideCharToMultiByte( CP_ACP, 0, 
756                                      (LPWSTR)callback.item.pszText, -1,
757                                      wineItem->pszText, buflen, NULL, NULL );
758                 wineItem->cchTextMax = buflen;
759                 if (oldText)
760                     COMCTL32_Free(oldText);
761             }
762         }
763     }
764  
765     if (mask & TVIF_IMAGE)
766         wineItem->iImage = callback.item.iImage;
767
768     if (mask & TVIF_SELECTEDIMAGE)
769         wineItem->iSelectedImage = callback.item.iSelectedImage;
770
771     if (mask & TVIF_CHILDREN)
772         wineItem->cChildren = callback.item.cChildren;
773
774     /* These members are now permanently set. */
775     if (callback.item.mask & TVIF_DI_SETITEM)
776         wineItem->callbackMask &= ~callback.item.mask;
777 }
778
779 /***************************************************************************
780  * This function uses cChildren field to decide whether the item has
781  * children or not.
782  * Note: if this returns TRUE, the child items may not actually exist,
783  * they could be virtual.
784  *
785  * Just use wineItem->firstChild to check for physical children.
786  */
787 static BOOL
788 TREEVIEW_HasChildren(TREEVIEW_INFO *infoPtr, TREEVIEW_ITEM *wineItem)
789 {
790     TREEVIEW_UpdateDispInfo(infoPtr, wineItem, TVIF_CHILDREN);
791
792     return wineItem->cChildren > 0;
793 }
794
795
796 /* Item Position ********************************************************/
797
798 /* Compute linesOffset, stateOffset, imageOffset, textOffset of an item. */
799 static VOID
800 TREEVIEW_ComputeItemInternalMetrics(TREEVIEW_INFO *infoPtr,
801                                     TREEVIEW_ITEM *item)
802 {
803     /* Same effect, different optimisation. */
804 #if 0
805     BOOL lar = ((infoPtr->dwStyle & TVS_LINESATROOT)
806                 && (infoPtr->dwStyle & (TVS_HASLINES|TVS_HASBUTTONS)));
807 #else
808     BOOL lar = ((infoPtr->dwStyle
809                  & (TVS_LINESATROOT|TVS_HASLINES|TVS_HASBUTTONS))
810                 > TVS_LINESATROOT);
811 #endif
812
813     item->linesOffset = infoPtr->uIndent * (item->iLevel + lar - 1)
814         - infoPtr->scrollX;
815     item->stateOffset = item->linesOffset + infoPtr->uIndent;
816     item->imageOffset = item->stateOffset
817         + (STATEIMAGEINDEX(item->state) ? infoPtr->stateImageWidth : 0);
818     item->textOffset  = item->imageOffset + infoPtr->normalImageWidth;
819 }
820
821 static VOID
822 TREEVIEW_ComputeTextWidth(TREEVIEW_INFO *infoPtr, TREEVIEW_ITEM *item, HDC hDC)
823 {
824     HDC hdc;
825     HFONT hOldFont=0;
826     SIZE sz;
827
828     /* DRAW's OM docker creates items like this */
829     if (item->pszText == NULL)
830     {
831         item->textWidth = 0;
832         return;
833     }
834
835     if (item->textWidth != 0 && !(item->callbackMask & TVIF_TEXT))
836        return;
837
838     if (hDC != 0)
839     {
840         hdc = hDC;
841     }
842     else
843     {
844         hdc = GetDC(infoPtr->hwnd);
845         hOldFont = SelectObject(hdc, TREEVIEW_FontForItem(infoPtr, item));
846     }
847
848     GetTextExtentPoint32A(hdc, item->pszText, strlen(item->pszText), &sz);
849     item->textWidth = sz.cx;
850
851     if (hDC == 0)
852     {
853         SelectObject(hdc, hOldFont);
854         ReleaseDC(0, hdc);
855     }
856 }
857
858 static VOID
859 TREEVIEW_ComputeItemRect(TREEVIEW_INFO *infoPtr, TREEVIEW_ITEM *item)
860 {
861     item->rect.top = infoPtr->uItemHeight *
862         (item->visibleOrder - infoPtr->firstVisible->visibleOrder);
863
864     item->rect.bottom = item->rect.top
865         + infoPtr->uItemHeight * item->iIntegral - 1;
866
867     item->rect.left = 0;
868     item->rect.right = infoPtr->clientWidth;
869 }
870
871 /* We know that only items after start need their order updated. */
872 static void
873 TREEVIEW_RecalculateVisibleOrder(TREEVIEW_INFO *infoPtr, TREEVIEW_ITEM *start)
874 {
875     TREEVIEW_ITEM *item;
876     int order;
877
878     if (!start)
879     {
880         start = infoPtr->root->firstChild;
881         order = 0;
882     }
883     else
884         order = start->visibleOrder;
885
886     for (item = start; item != NULL;
887          item = TREEVIEW_GetNextListItem(infoPtr, item))
888     {
889         item->visibleOrder = order;
890         order += item->iIntegral;
891     }
892
893     infoPtr->maxVisibleOrder = order;
894
895     for (item = start; item != NULL;
896          item = TREEVIEW_GetNextListItem(infoPtr, item))
897     {
898         TREEVIEW_ComputeItemRect(infoPtr, item);
899     }
900 }
901
902
903 /* Update metrics of all items in selected subtree.
904  * root must be expanded
905  */
906 static VOID
907 TREEVIEW_UpdateSubTree(TREEVIEW_INFO *infoPtr, TREEVIEW_ITEM *root)
908 {
909    TREEVIEW_ITEM *sibling;
910    HDC hdc;
911    HFONT hOldFont;
912
913    if (!root->firstChild || !(root->state & TVIS_EXPANDED))
914       return;
915
916    root->state &= ~TVIS_EXPANDED;
917    sibling = TREEVIEW_GetNextListItem(infoPtr, root);
918    root->state |= TVIS_EXPANDED;
919
920    hdc = GetDC(infoPtr->hwnd);
921    hOldFont = SelectObject(hdc, infoPtr->hFont);
922
923    for (; root != sibling;
924         root = TREEVIEW_GetNextListItem(infoPtr, root))
925    {
926       TREEVIEW_ComputeItemInternalMetrics(infoPtr, root);
927
928       if (root->callbackMask & TVIF_TEXT)
929          TREEVIEW_UpdateDispInfo(infoPtr, root, TVIF_TEXT);
930
931       if (root->textWidth == 0)
932       {
933          SelectObject(hdc, TREEVIEW_FontForItem(infoPtr, root));
934          TREEVIEW_ComputeTextWidth(infoPtr, root, hdc);
935       }
936    }
937
938    SelectObject(hdc, hOldFont);
939    ReleaseDC(infoPtr->hwnd, hdc);
940 }
941
942 /* Item Allocation **********************************************************/
943
944 static TREEVIEW_ITEM *
945 TREEVIEW_AllocateItem(TREEVIEW_INFO *infoPtr)
946 {
947     TREEVIEW_ITEM *newItem = COMCTL32_Alloc(sizeof(TREEVIEW_ITEM));
948
949     if (!newItem)
950         return NULL;
951
952     if (DPA_InsertPtr(infoPtr->items, INT_MAX, newItem) == -1)
953     {
954         COMCTL32_Free(newItem);
955         return NULL;
956     }
957
958     return newItem;
959 }
960
961 /* Exact opposite of TREEVIEW_AllocateItem. In particular, it does not
962  * free item->pszText. */
963 static void
964 TREEVIEW_FreeItem(TREEVIEW_INFO *infoPtr, TREEVIEW_ITEM *item)
965 {
966     DPA_DeletePtr(infoPtr->items, DPA_GetPtrIndex(infoPtr->items, item));
967     COMCTL32_Free(item);
968     if (infoPtr->selectedItem == item)
969         infoPtr->selectedItem = NULL;
970 }
971
972
973 /* Item Insertion *******************************************************/
974
975 /***************************************************************************
976  * This method inserts newItem before sibling as a child of parent.
977  * sibling can be NULL, but only if parent has no children.
978  */
979 static void
980 TREEVIEW_InsertBefore(TREEVIEW_ITEM *newItem, TREEVIEW_ITEM *sibling,
981                       TREEVIEW_ITEM *parent)
982 {
983     assert(newItem != NULL);
984     assert(parent != NULL);
985
986     if (sibling != NULL)
987     {
988         assert(sibling->parent == parent);
989
990         if (sibling->prevSibling != NULL)
991             sibling->prevSibling->nextSibling = newItem;
992
993         newItem->prevSibling = sibling->prevSibling;
994         sibling->prevSibling = newItem;
995     }
996     else
997        newItem->prevSibling = NULL;
998
999     newItem->nextSibling = sibling;
1000
1001     if (parent->firstChild == sibling)
1002         parent->firstChild = newItem;
1003
1004     if (parent->lastChild == NULL)
1005         parent->lastChild = newItem;
1006 }
1007
1008 /***************************************************************************
1009  * This method inserts newItem after sibling as a child of parent.
1010  * sibling can be NULL, but only if parent has no children.
1011  */
1012 static void
1013 TREEVIEW_InsertAfter(TREEVIEW_ITEM *newItem, TREEVIEW_ITEM *sibling,
1014                      TREEVIEW_ITEM *parent)
1015 {
1016     assert(newItem != NULL);
1017     assert(parent != NULL);
1018
1019     if (sibling != NULL)
1020     {
1021         assert(sibling->parent == parent);
1022
1023         if (sibling->nextSibling != NULL)
1024             sibling->nextSibling->prevSibling = newItem;
1025
1026         newItem->nextSibling = sibling->nextSibling;
1027         sibling->nextSibling = newItem;
1028     }
1029     else
1030        newItem->nextSibling = NULL;
1031
1032     newItem->prevSibling = sibling;
1033
1034     if (parent->lastChild == sibling)
1035         parent->lastChild = newItem;
1036
1037     if (parent->firstChild == NULL)
1038         parent->firstChild = newItem;
1039 }
1040
1041 static BOOL
1042 TREEVIEW_DoSetItem(TREEVIEW_INFO *infoPtr, TREEVIEW_ITEM *wineItem,
1043                    const TVITEMEXA *tvItem)
1044 {
1045     UINT callbackClear = 0;
1046     UINT callbackSet = 0;
1047
1048     /* Do this first in case it fails. */
1049     if (tvItem->mask & TVIF_TEXT)
1050     {
1051         wineItem->textWidth = 0; /* force width recalculation */
1052         if (tvItem->pszText != LPSTR_TEXTCALLBACKA)
1053         {
1054             int len = lstrlenA(tvItem->pszText) + 1;
1055             LPSTR newText = COMCTL32_ReAlloc(wineItem->pszText, len);
1056
1057             if (newText == NULL) return FALSE;
1058
1059             callbackClear |= TVIF_TEXT;
1060
1061             wineItem->pszText = newText;
1062             wineItem->cchTextMax = len;
1063             lstrcpynA(wineItem->pszText, tvItem->pszText, len);
1064             TRACE("setting text %s, item %p\n",
1065                   debugstr_a(wineItem->pszText), wineItem);
1066         }
1067         else
1068         {
1069             callbackSet |= TVIF_TEXT;
1070
1071             wineItem->pszText = COMCTL32_ReAlloc(wineItem->pszText,
1072                                                  TEXT_CALLBACK_SIZE);
1073             wineItem->cchTextMax = TEXT_CALLBACK_SIZE;
1074             TRACE("setting callback, item %p\n",
1075                   wineItem);
1076         }
1077     }
1078
1079     if (tvItem->mask & TVIF_CHILDREN)
1080     {
1081         wineItem->cChildren = tvItem->cChildren;
1082
1083         if (wineItem->cChildren == I_CHILDRENCALLBACK)
1084             callbackSet |= TVIF_CHILDREN;
1085         else
1086             callbackClear |= TVIF_CHILDREN;
1087     }
1088
1089     if (tvItem->mask & TVIF_IMAGE)
1090     {
1091         wineItem->iImage = tvItem->iImage;
1092
1093         if (wineItem->iImage == I_IMAGECALLBACK)
1094             callbackSet |= TVIF_IMAGE;
1095         else
1096             callbackClear |= TVIF_IMAGE;
1097     }
1098
1099     if (tvItem->mask & TVIF_SELECTEDIMAGE)
1100     {
1101         wineItem->iSelectedImage = tvItem->iSelectedImage;
1102
1103         if (wineItem->iSelectedImage == I_IMAGECALLBACK)
1104             callbackSet |= TVIF_SELECTEDIMAGE;
1105         else
1106             callbackClear |= TVIF_SELECTEDIMAGE;
1107     }
1108
1109     if (tvItem->mask & TVIF_PARAM)
1110         wineItem->lParam = tvItem->lParam;
1111
1112     /* If the application sets TVIF_INTEGRAL without
1113      * supplying a TVITEMEX structure, it's toast. */
1114     if (tvItem->mask & TVIF_INTEGRAL)
1115         wineItem->iIntegral = tvItem->iIntegral;
1116
1117     if (tvItem->mask & TVIF_STATE)
1118     {
1119         TRACE("prevstate,state,mask:%x,%x,%x\n", wineItem->state, tvItem->state,
1120               tvItem->stateMask);
1121         wineItem->state &= ~tvItem->stateMask;
1122         wineItem->state |= (tvItem->state & tvItem->stateMask);
1123     }
1124
1125     wineItem->callbackMask |= callbackSet;
1126     wineItem->callbackMask &= ~callbackClear;
1127
1128     return TRUE;
1129 }
1130
1131 /* Note that the new item is pre-zeroed. */
1132 static LRESULT
1133 TREEVIEW_InsertItemA(TREEVIEW_INFO *infoPtr, LPARAM lParam)
1134 {
1135     const TVINSERTSTRUCTA *ptdi = (LPTVINSERTSTRUCTA) lParam;
1136     const TVITEMEXA *tvItem = &ptdi->DUMMYUNIONNAME.itemex;
1137     HTREEITEM insertAfter;
1138     TREEVIEW_ITEM *newItem, *parentItem;
1139     BOOL bTextUpdated = FALSE;
1140
1141     if (ptdi->hParent == TVI_ROOT || ptdi->hParent == 0)
1142     {
1143         parentItem = infoPtr->root;
1144     }
1145     else
1146     {
1147         parentItem = ptdi->hParent;
1148
1149         if (!TREEVIEW_ValidItem(infoPtr, parentItem))
1150         {
1151             WARN("invalid parent %p\n", parentItem);
1152             return (LRESULT)(HTREEITEM)NULL;
1153         }
1154     }
1155
1156     insertAfter = ptdi->hInsertAfter;
1157
1158     /* Validate this now for convenience. */
1159     switch ((DWORD)insertAfter)
1160     {
1161     case (DWORD)TVI_FIRST:
1162     case (DWORD)TVI_LAST:
1163     case (DWORD)TVI_SORT:
1164         break;
1165
1166     default:
1167         if (!TREEVIEW_ValidItem(infoPtr, insertAfter) ||
1168             insertAfter->parent != parentItem)
1169         {
1170             WARN("invalid insert after %p\n", insertAfter);
1171             insertAfter = TVI_LAST;
1172         }
1173     }
1174
1175     TRACE("parent %p position %p: '%s'\n", parentItem, insertAfter,
1176           (tvItem->mask & TVIF_TEXT)
1177           ? ((tvItem->pszText == LPSTR_TEXTCALLBACKA) ? "<callback>"
1178              : tvItem->pszText)
1179           : "<no label>");
1180
1181     newItem = TREEVIEW_AllocateItem(infoPtr);
1182     if (newItem == NULL)
1183         return (LRESULT)(HTREEITEM)NULL;
1184
1185     newItem->parent = parentItem;
1186     newItem->iIntegral = 1;
1187
1188     if (!TREEVIEW_DoSetItem(infoPtr, newItem, tvItem))
1189         return (LRESULT)(HTREEITEM)NULL;
1190
1191     /* After this point, nothing can fail. (Except for TVI_SORT.) */
1192
1193     infoPtr->uNumItems++;
1194
1195     switch ((DWORD)insertAfter)
1196     {
1197     case (DWORD)TVI_FIRST:
1198         TREEVIEW_InsertBefore(newItem, parentItem->firstChild, parentItem);
1199         if (infoPtr->firstVisible == parentItem->firstChild)
1200             TREEVIEW_SetFirstVisible(infoPtr, newItem, TRUE);
1201         break;
1202
1203     case (DWORD)TVI_LAST:
1204         TREEVIEW_InsertAfter(newItem, parentItem->lastChild, parentItem);
1205         break;
1206
1207         /* hInsertAfter names a specific item we want to insert after */
1208     default:
1209         TREEVIEW_InsertAfter(newItem, insertAfter, insertAfter->parent);
1210         break;
1211
1212     case (DWORD)TVI_SORT:
1213         {
1214             TREEVIEW_ITEM *aChild;
1215             TREEVIEW_ITEM *previousChild = NULL;
1216             BOOL bItemInserted = FALSE;
1217
1218             aChild = parentItem->firstChild;
1219
1220             bTextUpdated = TRUE;
1221             TREEVIEW_UpdateDispInfo(infoPtr, newItem, TVIF_TEXT);
1222
1223             /* Iterate the parent children to see where we fit in */
1224             while (aChild != NULL)
1225             {
1226                 INT comp;
1227
1228                 TREEVIEW_UpdateDispInfo(infoPtr, aChild, TVIF_TEXT);
1229                 comp = lstrcmpA(newItem->pszText, aChild->pszText);
1230
1231                 if (comp < 0)   /* we are smaller than the current one */
1232                 {
1233                     TREEVIEW_InsertBefore(newItem, aChild, parentItem);
1234                     bItemInserted = TRUE;
1235                     break;
1236                 }
1237                 else if (comp > 0)      /* we are bigger than the current one */
1238                 {
1239                     previousChild = aChild;
1240
1241                     /* This will help us to exit if there is no more sibling */
1242                     aChild = (aChild->nextSibling == 0)
1243                         ? NULL  
1244                         : aChild->nextSibling;
1245
1246                     /* Look at the next item */
1247                     continue;
1248                 }
1249                 else if (comp == 0)
1250                 {
1251                     /* 
1252                      * An item with this name is already existing, therefore,  
1253                      * we add after the one we found 
1254                      */
1255                     TREEVIEW_InsertAfter(newItem, aChild, parentItem);
1256                     bItemInserted = TRUE;
1257                     break;
1258                 }
1259             }
1260
1261             /* 
1262              * we reach the end of the child list and the item has not
1263              * yet been inserted, therefore, insert it after the last child.
1264              */
1265             if ((!bItemInserted) && (aChild == NULL))
1266                 TREEVIEW_InsertAfter(newItem, previousChild, parentItem);
1267
1268             break;
1269         }
1270     }
1271
1272
1273     TRACE("new item %p; parent %p, mask %x\n", newItem,
1274           newItem->parent, tvItem->mask);
1275
1276     newItem->iLevel = newItem->parent->iLevel + 1;
1277
1278     if (newItem->parent->cChildren == 0)
1279         newItem->parent->cChildren = 1;
1280
1281     if (infoPtr->dwStyle & TVS_CHECKBOXES)
1282     {
1283         if (STATEIMAGEINDEX(newItem->state) == 0)
1284             newItem->state |= INDEXTOSTATEIMAGEMASK(1);
1285     }
1286
1287     if (infoPtr->firstVisible == NULL)
1288         infoPtr->firstVisible = newItem;
1289
1290     TREEVIEW_VerifyTree(infoPtr);
1291
1292     if (parentItem == infoPtr->root ||
1293         (ISVISIBLE(parentItem) && parentItem->state & TVIS_EXPANDED))
1294     {
1295        TREEVIEW_ITEM *item;
1296        TREEVIEW_ITEM *prev = TREEVIEW_GetPrevListItem(infoPtr, newItem);
1297
1298        TREEVIEW_RecalculateVisibleOrder(infoPtr, prev);
1299        TREEVIEW_ComputeItemInternalMetrics(infoPtr, newItem);
1300
1301        if (!bTextUpdated)
1302           TREEVIEW_UpdateDispInfo(infoPtr, newItem, TVIF_TEXT);
1303
1304        TREEVIEW_ComputeTextWidth(infoPtr, newItem, 0);
1305        TREEVIEW_UpdateScrollBars(infoPtr);
1306     /*
1307      * if the item was inserted in a visible part of the tree, 
1308      * invalidate it, as well as those after it
1309      */
1310        for (item = newItem;
1311             item != NULL;
1312             item = TREEVIEW_GetNextListItem(infoPtr, item))
1313           TREEVIEW_Invalidate(infoPtr, item);
1314     }
1315     else
1316     {
1317        newItem->visibleOrder = -1;
1318
1319        /* refresh treeview if newItem is the first item inserted under parentItem */
1320        if (ISVISIBLE(parentItem) && newItem->prevSibling == newItem->nextSibling)
1321        {
1322           /* parent got '+' - update it */
1323           TREEVIEW_Invalidate(infoPtr, parentItem);
1324        }
1325     }
1326
1327     return (LRESULT)newItem;
1328 }
1329
1330
1331 static LRESULT
1332 TREEVIEW_InsertItemW(TREEVIEW_INFO *infoPtr, LPARAM lParam)
1333 {
1334     TVINSERTSTRUCTW *tvisW;
1335     TVINSERTSTRUCTA tvisA;
1336     LRESULT lRes;
1337
1338     tvisW = (LPTVINSERTSTRUCTW) lParam;
1339
1340     tvisA.hParent = tvisW->hParent;
1341     tvisA.hInsertAfter = tvisW->hInsertAfter;
1342
1343     tvisA.DUMMYUNIONNAME.item.mask = tvisW->DUMMYUNIONNAME.item.mask;
1344     tvisA.DUMMYUNIONNAME.item.hItem = tvisW->DUMMYUNIONNAME.item.hItem;
1345     tvisA.DUMMYUNIONNAME.item.state = tvisW->DUMMYUNIONNAME.item.state;
1346     tvisA.DUMMYUNIONNAME.item.stateMask = tvisW->DUMMYUNIONNAME.item.stateMask;
1347     tvisA.DUMMYUNIONNAME.item.cchTextMax =
1348         tvisW->DUMMYUNIONNAME.item.cchTextMax;
1349
1350     if (tvisW->DUMMYUNIONNAME.item.pszText)
1351     {
1352         if (tvisW->DUMMYUNIONNAME.item.pszText != LPSTR_TEXTCALLBACKW)
1353         {
1354             int len = WideCharToMultiByte( CP_ACP, 0, tvisW->DUMMYUNIONNAME.item.pszText, -1,
1355                                            NULL, 0, NULL, NULL );
1356             tvisA.DUMMYUNIONNAME.item.pszText = COMCTL32_Alloc(len);
1357             WideCharToMultiByte( CP_ACP, 0, tvisW->DUMMYUNIONNAME.item.pszText, -1,
1358                                  tvisA.DUMMYUNIONNAME.item.pszText, len, NULL, NULL );
1359         }
1360         else
1361         {
1362             tvisA.DUMMYUNIONNAME.item.pszText = LPSTR_TEXTCALLBACKA;
1363             tvisA.DUMMYUNIONNAME.item.cchTextMax = 0;
1364         }
1365     }
1366
1367     tvisA.DUMMYUNIONNAME.item.iImage = tvisW->DUMMYUNIONNAME.item.iImage;
1368     tvisA.DUMMYUNIONNAME.item.iSelectedImage =
1369         tvisW->DUMMYUNIONNAME.item.iSelectedImage;
1370     tvisA.DUMMYUNIONNAME.item.cChildren = tvisW->DUMMYUNIONNAME.item.cChildren;
1371     tvisA.DUMMYUNIONNAME.item.lParam = tvisW->DUMMYUNIONNAME.item.lParam;
1372
1373     lRes = TREEVIEW_InsertItemA(infoPtr, (LPARAM)&tvisA);
1374
1375     if (tvisA.DUMMYUNIONNAME.item.pszText != LPSTR_TEXTCALLBACKA)
1376     {
1377         COMCTL32_Free(tvisA.DUMMYUNIONNAME.item.pszText);
1378     }
1379
1380     return lRes;
1381
1382 }
1383
1384
1385 /* Item Deletion ************************************************************/
1386 static void
1387 TREEVIEW_RemoveItem(TREEVIEW_INFO *infoPtr, TREEVIEW_ITEM *wineItem);
1388
1389 static void
1390 TREEVIEW_RemoveAllChildren(TREEVIEW_INFO *infoPtr, TREEVIEW_ITEM *parentItem)
1391 {
1392     TREEVIEW_ITEM *kill = parentItem->firstChild;
1393
1394     while (kill != NULL)
1395     {
1396         TREEVIEW_ITEM *next = kill->nextSibling;
1397
1398         TREEVIEW_RemoveItem(infoPtr, kill);
1399
1400         kill = next;
1401     }
1402
1403     assert(parentItem->cChildren <= 0); /* I_CHILDRENCALLBACK or 0 */
1404     assert(parentItem->firstChild == NULL);
1405     assert(parentItem->lastChild == NULL);
1406 }
1407
1408 static void
1409 TREEVIEW_UnlinkItem(TREEVIEW_ITEM *item)
1410 {
1411     TREEVIEW_ITEM *parentItem = item->parent;
1412
1413     assert(item != NULL);
1414     assert(item->parent != NULL); /* i.e. it must not be the root */
1415
1416     if (parentItem->firstChild == item)
1417         parentItem->firstChild = item->nextSibling;
1418
1419     if (parentItem->lastChild == item)
1420         parentItem->lastChild = item->prevSibling;
1421
1422     if (parentItem->firstChild == NULL && parentItem->lastChild == NULL
1423         && parentItem->cChildren > 0)
1424         parentItem->cChildren = 0;
1425
1426     if (item->prevSibling)
1427         item->prevSibling->nextSibling = item->nextSibling;
1428
1429     if (item->nextSibling)
1430         item->nextSibling->prevSibling = item->prevSibling;
1431 }
1432
1433 static void
1434 TREEVIEW_RemoveItem(TREEVIEW_INFO *infoPtr, TREEVIEW_ITEM *wineItem)
1435 {
1436     TRACE("%p, (%s)\n", wineItem, TREEVIEW_ItemName(wineItem));
1437
1438     TREEVIEW_SendTreeviewNotify(infoPtr, 
1439                                 (infoPtr->bNtfUnicode) ? TVN_DELETEITEMW : 
1440                                                          TVN_DELETEITEMA,
1441                                 TVIF_HANDLE | TVIF_PARAM, 0, wineItem, 0);
1442
1443     if (wineItem->firstChild)
1444         TREEVIEW_RemoveAllChildren(infoPtr, wineItem);
1445
1446     TREEVIEW_UnlinkItem(wineItem);
1447
1448     infoPtr->uNumItems--;
1449
1450     if (wineItem->pszText != LPSTR_TEXTCALLBACKA)
1451         COMCTL32_Free(wineItem->pszText);
1452
1453     TREEVIEW_FreeItem(infoPtr, wineItem);
1454 }
1455
1456
1457 /* Empty out the tree. */
1458 static void
1459 TREEVIEW_RemoveTree(TREEVIEW_INFO *infoPtr)
1460 {
1461     TREEVIEW_RemoveAllChildren(infoPtr, infoPtr->root);
1462
1463     assert(infoPtr->uNumItems == 0);    /* root isn't counted in uNumItems */
1464 }
1465
1466 static LRESULT
1467 TREEVIEW_DeleteItem(TREEVIEW_INFO *infoPtr, HTREEITEM wineItem)
1468 {
1469     TREEVIEW_ITEM *oldSelection = infoPtr->selectedItem;
1470     TREEVIEW_ITEM *newSelection = oldSelection;
1471     TREEVIEW_ITEM *newFirstVisible = NULL;
1472     TREEVIEW_ITEM *parent, *prev = NULL;
1473     BOOL visible = FALSE;
1474
1475     if (wineItem == TVI_ROOT)
1476     {
1477         TRACE("TVI_ROOT\n");
1478         parent = infoPtr->root;
1479         newSelection = NULL;
1480         visible = TRUE;
1481         TREEVIEW_RemoveTree(infoPtr);
1482     }
1483     else
1484     {
1485         if (!TREEVIEW_ValidItem(infoPtr, wineItem))
1486             return FALSE;
1487
1488         TRACE("%p (%s)\n", wineItem, TREEVIEW_ItemName(wineItem));
1489         parent = wineItem->parent;
1490
1491         if (ISVISIBLE(wineItem))
1492         {
1493             prev = TREEVIEW_GetPrevListItem(infoPtr, wineItem);
1494             visible = TRUE;
1495         }
1496
1497         if (infoPtr->selectedItem != NULL
1498             && (wineItem == infoPtr->selectedItem
1499                 || TREEVIEW_IsChildOf(wineItem, infoPtr->selectedItem)))
1500         {
1501             if (wineItem->nextSibling)
1502                 newSelection = wineItem->nextSibling;
1503             else if (wineItem->parent != infoPtr->root)
1504                 newSelection = wineItem->parent;
1505         }
1506
1507         if (infoPtr->firstVisible == wineItem)
1508         {
1509             if (wineItem->nextSibling)
1510                newFirstVisible = wineItem->nextSibling;
1511             else if (wineItem->prevSibling)
1512                newFirstVisible = wineItem->prevSibling;
1513             else if (wineItem->parent != infoPtr->root)
1514                newFirstVisible = wineItem->parent;
1515         }
1516         else
1517             newFirstVisible = infoPtr->firstVisible;
1518
1519         TREEVIEW_RemoveItem(infoPtr, wineItem);
1520     }
1521
1522     /* Don't change if somebody else already has. */
1523     if (oldSelection == infoPtr->selectedItem)
1524     {
1525         if (TREEVIEW_ValidItem(infoPtr, newSelection))
1526             TREEVIEW_DoSelectItem(infoPtr, TVGN_CARET, newSelection, TVC_UNKNOWN);
1527         else
1528             infoPtr->selectedItem = 0;
1529     }
1530
1531     /* Validate insertMark dropItem.
1532      * hotItem ??? - used for comparison only.
1533      */
1534     if (!TREEVIEW_ValidItem(infoPtr, infoPtr->insertMarkItem))
1535         infoPtr->insertMarkItem = 0;
1536
1537     if (!TREEVIEW_ValidItem(infoPtr, infoPtr->dropItem))
1538         infoPtr->dropItem = 0;
1539
1540     if (!TREEVIEW_ValidItem(infoPtr, newFirstVisible))
1541         newFirstVisible = infoPtr->root->firstChild;
1542
1543     TREEVIEW_VerifyTree(infoPtr);
1544
1545
1546     if (visible)
1547     {
1548        TREEVIEW_RecalculateVisibleOrder(infoPtr, prev);
1549        TREEVIEW_SetFirstVisible(infoPtr, newFirstVisible, TRUE);
1550        TREEVIEW_UpdateScrollBars(infoPtr);
1551        TREEVIEW_Invalidate(infoPtr, NULL);
1552     }
1553     else if (ISVISIBLE(parent) && !TREEVIEW_HasChildren(infoPtr, parent))
1554     {
1555        /* parent lost '+/-' - update it */
1556        TREEVIEW_Invalidate(infoPtr, parent);
1557     }
1558
1559     return TRUE;
1560 }
1561
1562
1563 /* Get/Set Messages *********************************************************/
1564 static LRESULT
1565 TREEVIEW_SetRedraw(TREEVIEW_INFO* infoPtr, WPARAM wParam, LPARAM lParam)
1566 {
1567   if(wParam)
1568     infoPtr->bRedraw = TRUE;
1569   else
1570     infoPtr->bRedraw = FALSE;
1571
1572   return 0;
1573 }
1574
1575 static LRESULT
1576 TREEVIEW_GetIndent(TREEVIEW_INFO *infoPtr)
1577 {
1578     TRACE("\n");
1579     return infoPtr->uIndent;
1580 }
1581
1582 static LRESULT
1583 TREEVIEW_SetIndent(TREEVIEW_INFO *infoPtr, UINT newIndent)
1584 {
1585     TRACE("\n");
1586
1587     if (newIndent < MINIMUM_INDENT)
1588         newIndent = MINIMUM_INDENT;
1589
1590     if (infoPtr->uIndent != newIndent)
1591     {
1592         infoPtr->uIndent = newIndent;
1593         TREEVIEW_UpdateSubTree(infoPtr, infoPtr->root);
1594         TREEVIEW_UpdateScrollBars(infoPtr);
1595         TREEVIEW_Invalidate(infoPtr, NULL);
1596     }
1597
1598     return 0;
1599 }
1600
1601
1602 static LRESULT
1603 TREEVIEW_GetToolTips(TREEVIEW_INFO *infoPtr)
1604 {
1605     TRACE("\n");
1606     return infoPtr->hwndToolTip;
1607 }
1608
1609 static LRESULT
1610 TREEVIEW_SetToolTips(TREEVIEW_INFO *infoPtr, HWND hwndTT)
1611 {
1612     HWND prevToolTip;
1613
1614     TRACE("\n");
1615     prevToolTip = infoPtr->hwndToolTip;
1616     infoPtr->hwndToolTip = hwndTT;
1617
1618     return prevToolTip;
1619 }
1620
1621
1622 static LRESULT
1623 TREEVIEW_GetScrollTime(TREEVIEW_INFO *infoPtr)
1624 {
1625     return infoPtr->uScrollTime;
1626 }
1627
1628 static LRESULT
1629 TREEVIEW_SetScrollTime(TREEVIEW_INFO *infoPtr, UINT uScrollTime)
1630 {
1631     UINT uOldScrollTime = infoPtr->uScrollTime;
1632
1633     infoPtr->uScrollTime = min(uScrollTime, 100);
1634
1635     return uOldScrollTime;
1636 }
1637
1638
1639 static LRESULT
1640 TREEVIEW_GetImageList(TREEVIEW_INFO *infoPtr, WPARAM wParam)
1641 {
1642     TRACE("\n");
1643
1644     switch (wParam)
1645     {
1646     case (WPARAM)TVSIL_NORMAL:
1647         return (LRESULT)infoPtr->himlNormal;
1648
1649     case (WPARAM)TVSIL_STATE:
1650         return (LRESULT)infoPtr->himlState;
1651
1652     default:
1653         return 0;
1654     }
1655 }
1656
1657 static LRESULT
1658 TREEVIEW_SetImageList(TREEVIEW_INFO *infoPtr, WPARAM wParam, HIMAGELIST himlNew)
1659 {
1660     HIMAGELIST himlOld = 0;
1661     int oldWidth  = infoPtr->normalImageWidth;
1662     int oldHeight = infoPtr->normalImageHeight;
1663
1664
1665     TRACE("%x,%p\n", wParam, himlNew);
1666
1667     switch (wParam)
1668     {
1669     case (WPARAM)TVSIL_NORMAL:
1670         himlOld = infoPtr->himlNormal;
1671         infoPtr->himlNormal = himlNew;
1672
1673         if (himlNew != NULL)
1674             ImageList_GetIconSize(himlNew, &infoPtr->normalImageWidth,
1675                                   &infoPtr->normalImageHeight);
1676         else
1677         {
1678             infoPtr->normalImageWidth = 0;
1679             infoPtr->normalImageHeight = 0;
1680         }
1681
1682         break;
1683
1684     case (WPARAM)TVSIL_STATE:
1685         himlOld = infoPtr->himlState;
1686         infoPtr->himlState = himlNew;
1687
1688         if (himlNew != NULL)
1689             ImageList_GetIconSize(himlNew, &infoPtr->stateImageWidth,
1690                                   &infoPtr->stateImageHeight);
1691         else
1692         {
1693             infoPtr->stateImageWidth = 0;
1694             infoPtr->stateImageHeight = 0;
1695         }
1696
1697         break;
1698     }
1699
1700     if (oldWidth != infoPtr->normalImageWidth ||
1701         oldHeight != infoPtr->normalImageHeight)
1702     {
1703        TREEVIEW_UpdateSubTree(infoPtr, infoPtr->root);
1704        TREEVIEW_UpdateScrollBars(infoPtr);
1705     }
1706
1707     TREEVIEW_Invalidate(infoPtr, NULL);
1708
1709     return (LRESULT)himlOld;
1710 }
1711
1712 /* Compute the natural height (based on the font size) for items. */
1713 static UINT
1714 TREEVIEW_NaturalHeight(TREEVIEW_INFO *infoPtr)
1715 {
1716     TEXTMETRICA tm;
1717     HDC hdc = GetDC(0);
1718     HFONT hOldFont = SelectObject(hdc, infoPtr->hFont);
1719
1720     GetTextMetricsA(hdc, &tm);
1721
1722     SelectObject(hdc, hOldFont);
1723     ReleaseDC(0, hdc);
1724
1725     /* The 16 is a hack because our fonts are tiny. */
1726     /* add 2 for the focus border and 1 more for margin some apps assume */
1727     return max(16, tm.tmHeight + tm.tmExternalLeading + 3);
1728 }
1729
1730 static LRESULT
1731 TREEVIEW_SetItemHeight(TREEVIEW_INFO *infoPtr, INT newHeight)
1732 {
1733     INT prevHeight = infoPtr->uItemHeight;
1734
1735     TRACE("%d \n", newHeight);
1736     if (newHeight == -1)
1737     {
1738         infoPtr->uItemHeight = TREEVIEW_NaturalHeight(infoPtr);
1739         infoPtr->bHeightSet = FALSE;
1740     }
1741     else
1742     {
1743         infoPtr->uItemHeight = newHeight;
1744         infoPtr->bHeightSet = TRUE;
1745     }
1746
1747     /* Round down, unless we support odd ("non even") heights. */
1748     if (!(infoPtr->dwStyle) & TVS_NONEVENHEIGHT)
1749         infoPtr->uItemHeight &= ~1;
1750
1751     if (infoPtr->uItemHeight != prevHeight)
1752     {
1753         TREEVIEW_RecalculateVisibleOrder(infoPtr, NULL);
1754         TREEVIEW_UpdateScrollBars(infoPtr);
1755         TREEVIEW_Invalidate(infoPtr, NULL);
1756     }
1757
1758     return prevHeight;
1759 }
1760
1761 static LRESULT
1762 TREEVIEW_GetItemHeight(TREEVIEW_INFO *infoPtr)
1763 {
1764     TRACE("\n");
1765     return infoPtr->uItemHeight;
1766 }
1767
1768
1769 static LRESULT
1770 TREEVIEW_GetFont(TREEVIEW_INFO *infoPtr)
1771 {
1772     TRACE("%x\n", infoPtr->hFont);
1773     return infoPtr->hFont;
1774 }
1775
1776
1777 static INT CALLBACK
1778 TREEVIEW_ResetTextWidth(LPVOID pItem, DWORD unused)
1779 {
1780     (void)unused;
1781
1782     ((TREEVIEW_ITEM *)pItem)->textWidth = 0;
1783
1784     return 1;
1785 }
1786
1787 static LRESULT
1788 TREEVIEW_SetFont(TREEVIEW_INFO *infoPtr, HFONT hFont, BOOL bRedraw)
1789 {
1790     UINT uHeight = infoPtr->uItemHeight;
1791
1792     TRACE("%x %i\n", hFont, bRedraw);
1793
1794     infoPtr->hFont = hFont ? hFont : GetStockObject(SYSTEM_FONT);
1795
1796     DeleteObject(infoPtr->hBoldFont);
1797     infoPtr->hBoldFont = TREEVIEW_CreateBoldFont(infoPtr->hFont);
1798
1799     if (!infoPtr->bHeightSet)
1800         infoPtr->uItemHeight = TREEVIEW_NaturalHeight(infoPtr);
1801
1802     if (uHeight != infoPtr->uItemHeight)
1803        TREEVIEW_RecalculateVisibleOrder(infoPtr, NULL);
1804
1805     DPA_EnumCallback(infoPtr->items, TREEVIEW_ResetTextWidth, 0);
1806
1807     TREEVIEW_UpdateSubTree(infoPtr, infoPtr->root);
1808     TREEVIEW_UpdateScrollBars(infoPtr);
1809
1810     if (bRedraw)
1811         TREEVIEW_Invalidate(infoPtr, NULL);
1812
1813     return 0;
1814 }
1815
1816
1817 static LRESULT
1818 TREEVIEW_GetLineColor(TREEVIEW_INFO *infoPtr)
1819 {
1820     TRACE("\n");
1821     return (LRESULT)infoPtr->clrLine;
1822 }
1823
1824 static LRESULT
1825 TREEVIEW_SetLineColor(TREEVIEW_INFO *infoPtr, COLORREF color)
1826 {
1827     COLORREF prevColor = infoPtr->clrLine;
1828
1829     TRACE("\n");
1830     infoPtr->clrLine = color;
1831     return (LRESULT)prevColor;
1832 }
1833
1834
1835 static LRESULT
1836 TREEVIEW_GetTextColor(TREEVIEW_INFO *infoPtr)
1837 {
1838     TRACE("\n");
1839     return (LRESULT)infoPtr->clrText;
1840 }
1841
1842 static LRESULT
1843 TREEVIEW_SetTextColor(TREEVIEW_INFO *infoPtr, COLORREF color)
1844 {
1845     COLORREF prevColor = infoPtr->clrText;
1846
1847     TRACE("\n");
1848     infoPtr->clrText = color;
1849
1850     if (infoPtr->clrText != prevColor)
1851         TREEVIEW_Invalidate(infoPtr, NULL);
1852
1853     return (LRESULT)prevColor;
1854 }
1855
1856
1857 static LRESULT
1858 TREEVIEW_GetBkColor(TREEVIEW_INFO *infoPtr)
1859 {
1860     TRACE("\n");
1861     return (LRESULT)infoPtr->clrBk;
1862 }
1863
1864 static LRESULT
1865 TREEVIEW_SetBkColor(TREEVIEW_INFO *infoPtr, COLORREF newColor)
1866 {
1867     COLORREF prevColor = infoPtr->clrBk;
1868
1869     TRACE("\n");
1870     infoPtr->clrBk = newColor;
1871
1872     if (newColor != prevColor)
1873         TREEVIEW_Invalidate(infoPtr, NULL);
1874
1875     return (LRESULT)prevColor;
1876 }
1877
1878
1879 static LRESULT
1880 TREEVIEW_GetInsertMarkColor(TREEVIEW_INFO *infoPtr)
1881 {
1882     TRACE("\n");
1883     return (LRESULT)infoPtr->clrInsertMark;
1884 }
1885
1886 static LRESULT
1887 TREEVIEW_SetInsertMarkColor(TREEVIEW_INFO *infoPtr, COLORREF color)
1888 {
1889     COLORREF prevColor = infoPtr->clrInsertMark;
1890
1891     TRACE("%lx\n", color);
1892     infoPtr->clrInsertMark = color;
1893
1894     return (LRESULT)prevColor;
1895 }
1896
1897
1898 static LRESULT
1899 TREEVIEW_SetInsertMark(TREEVIEW_INFO *infoPtr, BOOL wParam, HTREEITEM item)
1900 {
1901     TRACE("%d %p\n", wParam, item);
1902
1903     if (!TREEVIEW_ValidItem(infoPtr, item))
1904         return 0;
1905
1906     infoPtr->insertBeforeorAfter = wParam;
1907     infoPtr->insertMarkItem = item;
1908
1909     TREEVIEW_Invalidate(infoPtr, NULL);
1910
1911     return 1;
1912 }
1913
1914
1915 /************************************************************************
1916  * Some serious braindamage here. lParam is a pointer to both the
1917  * input HTREEITEM and the output RECT.
1918  */
1919 static LRESULT
1920 TREEVIEW_GetItemRect(TREEVIEW_INFO *infoPtr, BOOL fTextRect, LPRECT lpRect)
1921 {
1922     TREEVIEW_ITEM *wineItem;
1923     const HTREEITEM *pItem = (HTREEITEM *)lpRect;
1924
1925     TRACE("\n");
1926     /*
1927      * validate parameters
1928      */
1929     if (pItem == NULL)
1930         return FALSE;
1931
1932     wineItem = *pItem;
1933     if (!TREEVIEW_ValidItem(infoPtr, wineItem) || !ISVISIBLE(wineItem))
1934         return FALSE;
1935
1936     /* 
1937      * If wParam is TRUE return the text size otherwise return 
1938      * the whole item size        
1939      */
1940     if (fTextRect)
1941     {
1942         /* Windows does not send TVN_GETDISPINFO here. */
1943
1944         lpRect->top = wineItem->rect.top;
1945         lpRect->bottom = wineItem->rect.bottom;
1946
1947         lpRect->left = wineItem->textOffset;
1948         lpRect->right = wineItem->textOffset + wineItem->textWidth;
1949     }
1950     else
1951     {
1952         *lpRect = wineItem->rect;
1953     }
1954
1955     TRACE("%s [L:%d R:%d T:%d B:%d]\n", fTextRect ? "text" : "item",
1956           lpRect->left, lpRect->right, lpRect->top, lpRect->bottom);
1957
1958     return TRUE;
1959 }
1960
1961 static inline LRESULT
1962 TREEVIEW_GetVisibleCount(TREEVIEW_INFO *infoPtr)
1963 {
1964     /* Suprise! This does not take integral height into account. */
1965     return infoPtr->clientHeight / infoPtr->uItemHeight;
1966 }
1967
1968
1969 static LRESULT
1970 TREEVIEW_GetItemA(TREEVIEW_INFO *infoPtr, LPTVITEMEXA tvItem)
1971 {
1972     TREEVIEW_ITEM *wineItem;
1973
1974     wineItem = tvItem->hItem;
1975     if (!TREEVIEW_ValidItem(infoPtr, wineItem))
1976         return FALSE;
1977
1978     TREEVIEW_UpdateDispInfo(infoPtr, wineItem, tvItem->mask);
1979
1980     if (tvItem->mask & TVIF_CHILDREN)
1981         tvItem->cChildren = wineItem->cChildren;
1982
1983     if (tvItem->mask & TVIF_HANDLE)
1984         tvItem->hItem = wineItem;
1985
1986     if (tvItem->mask & TVIF_IMAGE)
1987         tvItem->iImage = wineItem->iImage;
1988
1989     if (tvItem->mask & TVIF_INTEGRAL)
1990         tvItem->iIntegral = wineItem->iIntegral;
1991
1992     /* undocumented: windows ignores TVIF_PARAM and
1993      * * always sets lParam
1994      */
1995     tvItem->lParam = wineItem->lParam;
1996
1997     if (tvItem->mask & TVIF_SELECTEDIMAGE)
1998         tvItem->iSelectedImage = wineItem->iSelectedImage;
1999
2000     if (tvItem->mask & TVIF_STATE)
2001         tvItem->state = wineItem->state & tvItem->stateMask;
2002
2003     if (tvItem->mask & TVIF_TEXT)
2004         lstrcpynA(tvItem->pszText, wineItem->pszText, tvItem->cchTextMax);
2005
2006     TRACE("item <%p>, txt %p, img %p, mask %x\n",
2007           wineItem, tvItem->pszText, &tvItem->iImage, tvItem->mask);
2008
2009     return TRUE;
2010 }
2011
2012 /* Beware MSDN Library Visual Studio 6.0. It says -1 on failure, 0 on success,
2013  * which is wrong. */
2014 static LRESULT
2015 TREEVIEW_SetItemA(TREEVIEW_INFO *infoPtr, LPTVITEMEXA tvItem)
2016 {
2017     TREEVIEW_ITEM *wineItem;
2018     TREEVIEW_ITEM originalItem;
2019
2020     wineItem = tvItem->hItem;
2021
2022     TRACE("item %d,mask %x\n", TREEVIEW_GetItemIndex(infoPtr, wineItem),
2023           tvItem->mask);
2024
2025     if (!TREEVIEW_ValidItem(infoPtr, wineItem))
2026         return FALSE;
2027
2028     if (!TREEVIEW_DoSetItem(infoPtr, wineItem, tvItem))
2029         return FALSE;
2030
2031     /* store the orignal item values */
2032     originalItem = *wineItem;
2033
2034     /* If the text or TVIS_BOLD was changed, and it is visible, recalculate. */
2035     if ((tvItem->mask & TVIF_TEXT
2036          || (tvItem->mask & TVIF_STATE && tvItem->stateMask & TVIS_BOLD))
2037         && ISVISIBLE(wineItem))
2038     {
2039         TREEVIEW_UpdateDispInfo(infoPtr, wineItem, TVIF_TEXT);
2040         TREEVIEW_ComputeTextWidth(infoPtr, wineItem, 0);
2041     }
2042
2043     if (tvItem->mask != 0 && ISVISIBLE(wineItem))
2044     {
2045         /* The refresh updates everything, but we can't wait until then. */
2046         TREEVIEW_ComputeItemInternalMetrics(infoPtr, wineItem);
2047
2048         /* if any of the items values changed, redraw the item */
2049         if(memcmp(&originalItem, wineItem, sizeof(TREEVIEW_ITEM)))
2050         {
2051             if (tvItem->mask & TVIF_INTEGRAL)
2052             {
2053                 TREEVIEW_RecalculateVisibleOrder(infoPtr, wineItem);
2054                 TREEVIEW_UpdateScrollBars(infoPtr);
2055
2056                 TREEVIEW_Invalidate(infoPtr, NULL);
2057             }
2058             else
2059             {
2060                 TREEVIEW_UpdateScrollBars(infoPtr);
2061                 TREEVIEW_Invalidate(infoPtr, wineItem);
2062             }
2063         }
2064     }
2065
2066     return TRUE;
2067 }
2068
2069 static LRESULT
2070 TREEVIEW_GetItemW(TREEVIEW_INFO *infoPtr, LPTVITEMEXW tvItem)
2071 {
2072     TREEVIEW_ITEM *wineItem;
2073     INT         iItem;
2074     iItem = (INT)tvItem->hItem;
2075
2076     wineItem = tvItem->hItem;
2077     if(!TREEVIEW_ValidItem (infoPtr, wineItem))
2078         return FALSE;
2079
2080     TREEVIEW_UpdateDispInfo(infoPtr, wineItem, tvItem->mask);
2081
2082     if (tvItem->mask & TVIF_CHILDREN) {
2083         if (TVIF_CHILDREN==I_CHILDRENCALLBACK)
2084             FIXME("I_CHILDRENCALLBACK not supported\n");
2085         tvItem->cChildren = wineItem->cChildren;
2086     }
2087
2088     if (tvItem->mask & TVIF_HANDLE) {
2089         tvItem->hItem = wineItem;
2090     }
2091     if (tvItem->mask & TVIF_IMAGE) {
2092         tvItem->iImage = wineItem->iImage;
2093     }
2094     if (tvItem->mask & TVIF_INTEGRAL) {
2095         tvItem->iIntegral = wineItem->iIntegral;
2096     }
2097     /* undocumented: windows ignores TVIF_PARAM and
2098      * always sets lParam           */
2099     tvItem->lParam = wineItem->lParam;
2100     if (tvItem->mask & TVIF_SELECTEDIMAGE) {
2101         tvItem->iSelectedImage = wineItem->iSelectedImage;
2102     }
2103     if (tvItem->mask & TVIF_STATE) {
2104         tvItem->state = wineItem->state & tvItem->stateMask;
2105     }
2106
2107     if (tvItem->mask & TVIF_TEXT) {
2108         if (wineItem->pszText == LPSTR_TEXTCALLBACKA) {
2109             tvItem->pszText = LPSTR_TEXTCALLBACKW;
2110             FIXME(" GetItem called with LPSTR_TEXTCALLBACK\n");
2111         }
2112         else if (wineItem->pszText) {
2113             TRACE("orig str %s at %p\n",
2114                   debugstr_a(wineItem->pszText), wineItem->pszText);
2115             MultiByteToWideChar(CP_ACP, 0, wineItem->pszText,
2116                                 -1 , tvItem->pszText, tvItem->cchTextMax);
2117         }
2118     }
2119
2120     TRACE("item %d<%p>, txt %p<%s>, img %p, action %x\n",
2121           iItem, tvItem, tvItem->pszText, debugstr_w(tvItem->pszText),
2122           &tvItem->iImage, tvItem->mask);
2123     return TRUE;
2124 }
2125
2126 static LRESULT
2127 TREEVIEW_SetItemW(TREEVIEW_INFO *infoPtr, LPTVITEMEXW tvItem)
2128 {
2129     TVITEMEXA tvItemA;
2130     INT len;
2131     LRESULT rc;
2132
2133     tvItemA.mask = tvItem->mask;
2134     tvItemA.hItem = tvItem->hItem;
2135     tvItemA.state = tvItem->state;
2136     tvItemA.stateMask = tvItem->stateMask;
2137     if (tvItem->mask & TVIF_TEXT) {
2138         len = WideCharToMultiByte(CP_ACP, 0, tvItem->pszText, -1,
2139                                   NULL ,0 , NULL,NULL);
2140         if (len) {
2141             len ++;
2142             tvItemA.pszText = HeapAlloc(GetProcessHeap(),0,len*sizeof(WCHAR));
2143             len = WideCharToMultiByte(CP_ACP, 0, tvItem->pszText, -1,
2144                                       tvItemA.pszText ,len*sizeof(WCHAR), 
2145                                       NULL,NULL);
2146         }
2147         else
2148             tvItemA.pszText = NULL;
2149     }
2150     tvItemA.cchTextMax = tvItem->cchTextMax;
2151     tvItemA.iImage = tvItem->iImage;
2152     tvItemA.iSelectedImage = tvItem->iSelectedImage;
2153     tvItemA.cChildren = tvItem->cChildren;
2154     tvItemA.lParam = tvItem->lParam;
2155     tvItemA.iIntegral = tvItem->iIntegral;
2156
2157     rc = TREEVIEW_SetItemA(infoPtr,&tvItemA);
2158     HeapFree(GetProcessHeap(),0,tvItemA.pszText);
2159     return rc;
2160 }
2161
2162 static LRESULT
2163 TREEVIEW_GetItemState(TREEVIEW_INFO *infoPtr, HTREEITEM wineItem, UINT mask)
2164 {
2165     TRACE("\n");
2166
2167     if (!wineItem || !TREEVIEW_ValidItem(infoPtr, wineItem))
2168         return 0;
2169
2170     return (wineItem->state & mask);
2171 }
2172
2173 static LRESULT
2174 TREEVIEW_GetNextItem(TREEVIEW_INFO *infoPtr, UINT which, HTREEITEM wineItem)
2175 {
2176     TREEVIEW_ITEM *retval;
2177
2178     retval = 0;
2179
2180     /* handle all the global data here */
2181     switch (which)
2182     {
2183     case TVGN_CHILD:            /* Special case: child of 0 is root */
2184         if (wineItem)
2185             break;
2186         /* fall through */
2187     case TVGN_ROOT:
2188         retval = infoPtr->root->firstChild;
2189         break;
2190
2191     case TVGN_CARET:
2192         retval = infoPtr->selectedItem;
2193         break;
2194
2195     case TVGN_FIRSTVISIBLE:
2196         retval = infoPtr->firstVisible;
2197         break;
2198
2199     case TVGN_DROPHILITE:
2200         retval = infoPtr->dropItem;
2201         break;
2202
2203     case TVGN_LASTVISIBLE:
2204         retval = TREEVIEW_GetLastListItem(infoPtr, infoPtr->root);
2205         break;
2206     }
2207
2208     if (retval)
2209     {
2210         TRACE("flags:%x, returns %p\n", which, retval);
2211         return (LRESULT)retval;
2212     }
2213
2214     if (wineItem == TVI_ROOT) wineItem = infoPtr->root;
2215
2216     if (!TREEVIEW_ValidItem(infoPtr, wineItem))
2217         return FALSE;
2218
2219     switch (which)
2220     {
2221     case TVGN_NEXT:
2222         retval = wineItem->nextSibling;
2223         break;
2224     case TVGN_PREVIOUS:
2225         retval = wineItem->prevSibling;
2226         break;
2227     case TVGN_PARENT:
2228         retval = (wineItem->parent != infoPtr->root) ? wineItem->parent : NULL;
2229         break;
2230     case TVGN_CHILD:
2231         retval = wineItem->firstChild;
2232         break;
2233     case TVGN_NEXTVISIBLE:
2234         retval = TREEVIEW_GetNextListItem(infoPtr, wineItem);
2235         break;
2236     case TVGN_PREVIOUSVISIBLE:
2237         retval = TREEVIEW_GetPrevListItem(infoPtr, wineItem);
2238         break;
2239     default:
2240         TRACE("Unknown msg %x,item %p\n", which, wineItem);
2241         break;
2242     }
2243
2244     TRACE("flags:%x, item %p;returns %p\n", which, wineItem, retval);
2245     return (LRESULT)retval;
2246 }
2247
2248
2249 static LRESULT
2250 TREEVIEW_GetCount(TREEVIEW_INFO *infoPtr)
2251 {
2252     TRACE(" %d\n", infoPtr->uNumItems);
2253     return (LRESULT)infoPtr->uNumItems;
2254 }
2255
2256 static VOID
2257 TREEVIEW_ToggleItemState(TREEVIEW_INFO *infoPtr, TREEVIEW_ITEM *item)
2258 {
2259     if (infoPtr->dwStyle & TVS_CHECKBOXES)
2260     {
2261         static const unsigned int state_table[] = { 0, 2, 1 };
2262
2263         unsigned int state;
2264
2265         state = STATEIMAGEINDEX(item->state);
2266         TRACE("state:%x\n", state);
2267         item->state &= ~TVIS_STATEIMAGEMASK;
2268
2269         if (state < 3)
2270             state = state_table[state];
2271
2272         item->state |= INDEXTOSTATEIMAGEMASK(state);
2273
2274         TRACE("state:%x\n", state);
2275         TREEVIEW_Invalidate(infoPtr, item);
2276     }
2277 }
2278
2279
2280 /* Painting *************************************************************/
2281
2282 /* Draw the lines and expand button for an item. Also draws one section
2283  * of the line from item's parent to item's parent's next sibling. */
2284 static void
2285 TREEVIEW_DrawItemLines(TREEVIEW_INFO *infoPtr, HDC hdc, TREEVIEW_ITEM *item)
2286 {
2287     LONG centerx, centery;
2288     BOOL lar = ((infoPtr->dwStyle
2289                  & (TVS_LINESATROOT|TVS_HASLINES|TVS_HASBUTTONS))
2290                 > TVS_LINESATROOT);
2291
2292     if (!lar && item->iLevel == 0)
2293         return;
2294
2295     centerx = (item->linesOffset + item->stateOffset) / 2;
2296     centery = (item->rect.top + item->rect.bottom) / 2;
2297
2298     if (infoPtr->dwStyle & TVS_HASLINES)
2299     {
2300         HPEN hOldPen, hNewPen;
2301         HTREEITEM parent;
2302
2303         /* 
2304          * Get a dotted grey pen
2305          */
2306         hNewPen = CreatePen(PS_ALTERNATE, 0, infoPtr->clrLine);
2307         hOldPen = SelectObject(hdc, hNewPen);
2308
2309         MoveToEx(hdc, item->stateOffset, centery, NULL);
2310         LineTo(hdc, centerx - 1, centery);
2311
2312         if (item->prevSibling || item->parent != infoPtr->root)
2313         {
2314             MoveToEx(hdc, centerx, item->rect.top, NULL);
2315             LineTo(hdc, centerx, centery);
2316         }
2317
2318         if (item->nextSibling)
2319         {
2320             MoveToEx(hdc, centerx, centery, NULL);
2321             LineTo(hdc, centerx, item->rect.bottom + 1);
2322         }
2323
2324         /* Draw the line from our parent to its next sibling. */
2325         parent = item->parent;
2326         while (parent != infoPtr->root)
2327         {
2328             int pcenterx = (parent->linesOffset + parent->stateOffset) / 2;
2329
2330             if (parent->nextSibling
2331                 /* skip top-levels unless TVS_LINESATROOT */
2332                 && parent->stateOffset > parent->linesOffset)
2333             {
2334                 MoveToEx(hdc, pcenterx, item->rect.top, NULL);
2335                 LineTo(hdc, pcenterx, item->rect.bottom + 1);
2336             }
2337
2338             parent = parent->parent;
2339         }
2340
2341         SelectObject(hdc, hOldPen);
2342         DeleteObject(hNewPen);
2343     }
2344
2345     /* 
2346      * Display the (+/-) signs
2347      */
2348
2349     if (infoPtr->dwStyle & TVS_HASBUTTONS)
2350     {
2351         if (item->cChildren)
2352         {
2353             LONG height = item->rect.bottom - item->rect.top;
2354             LONG width  = item->stateOffset - item->linesOffset;
2355             LONG rectsize = min(height, width) / 4;
2356             /* plussize = ceil(rectsize * 3/4) */
2357             LONG plussize = (rectsize + 1) * 3 / 4;
2358
2359             HPEN hNewPen  = CreatePen(PS_SOLID, 0, infoPtr->clrLine);
2360             HPEN hOldPen  = SelectObject(hdc, hNewPen);
2361             HBRUSH hbr    = CreateSolidBrush(infoPtr->clrBk);
2362             HBRUSH hbrOld = SelectObject(hdc, hbr);
2363
2364             Rectangle(hdc, centerx - rectsize, centery - rectsize,
2365                       centerx + rectsize + 1, centery + rectsize + 1);
2366
2367             SelectObject(hdc, hbrOld);
2368             DeleteObject(hbr);
2369
2370             SelectObject(hdc, hOldPen);
2371             DeleteObject(hNewPen);
2372
2373             MoveToEx(hdc, centerx - plussize + 1, centery, NULL);
2374             LineTo(hdc, centerx + plussize, centery);
2375
2376             if (!(item->state & TVIS_EXPANDED))
2377             {
2378                 MoveToEx(hdc, centerx, centery - plussize + 1, NULL);
2379                 LineTo(hdc, centerx, centery + plussize);
2380             }
2381         }
2382     }
2383 }
2384
2385 static void
2386 TREEVIEW_DrawItem(TREEVIEW_INFO *infoPtr, HDC hdc, TREEVIEW_ITEM *wineItem)
2387 {
2388     INT cditem;
2389     HFONT hOldFont;
2390     int centery;
2391
2392     hOldFont = SelectObject(hdc, TREEVIEW_FontForItem(infoPtr, wineItem));
2393
2394     TREEVIEW_UpdateDispInfo(infoPtr, wineItem, CALLBACK_MASK_ALL);
2395
2396     /* The custom draw handler can query the text rectangle,
2397      * so get ready. */
2398     TREEVIEW_ComputeTextWidth(infoPtr, wineItem, hdc);
2399
2400     cditem = 0;
2401
2402     if (infoPtr->cdmode & CDRF_NOTIFYITEMDRAW)
2403     {
2404         cditem = TREEVIEW_SendCustomDrawItemNotify
2405             (infoPtr, hdc, wineItem, CDDS_ITEMPREPAINT);
2406         TRACE("prepaint:cditem-app returns 0x%x\n", cditem);
2407
2408         if (cditem & CDRF_SKIPDEFAULT)
2409         {
2410             SelectObject(hdc, hOldFont);
2411             return;
2412         }
2413     }
2414
2415     if (cditem & CDRF_NEWFONT)
2416         TREEVIEW_ComputeTextWidth(infoPtr, wineItem, hdc);
2417
2418     TREEVIEW_DrawItemLines(infoPtr, hdc, wineItem);
2419
2420     centery = (wineItem->rect.top + wineItem->rect.bottom) / 2;
2421
2422     /* 
2423      * Display the images associated with this item
2424      */
2425     {
2426         INT imageIndex;
2427
2428         /* State images are displayed to the left of the Normal image
2429          * image number is in state; zero should be `display no image'.
2430          */
2431         imageIndex = STATEIMAGEINDEX(wineItem->state);
2432
2433         if (infoPtr->himlState && imageIndex)
2434         {
2435             ImageList_Draw(infoPtr->himlState, imageIndex, hdc,
2436                            wineItem->stateOffset,
2437                            centery - infoPtr->stateImageHeight / 2,
2438                            ILD_NORMAL);
2439         }
2440
2441         /* Now, draw the normal image; can be either selected or
2442          * non-selected image. 
2443          */
2444
2445         if ((wineItem->state & TVIS_SELECTED) && (wineItem->iSelectedImage))
2446         {
2447             /* The item is currently selected */
2448             imageIndex = wineItem->iSelectedImage;
2449         }
2450         else
2451         {
2452             /* The item is not selected */
2453             imageIndex = wineItem->iImage;
2454         }
2455
2456         if (infoPtr->himlNormal)
2457         {
2458             int ovlIdx = wineItem->state & TVIS_OVERLAYMASK;
2459
2460             ImageList_Draw(infoPtr->himlNormal, imageIndex, hdc,
2461                            wineItem->imageOffset,
2462                            centery - infoPtr->normalImageHeight / 2,
2463                            ILD_NORMAL | ovlIdx);
2464         }
2465     }
2466
2467
2468     /* 
2469      * Display the text associated with this item
2470      */
2471
2472     /* Don't paint item's text if it's being edited */
2473     if (!infoPtr->hwndEdit || (infoPtr->selectedItem != wineItem))
2474     {
2475         if (wineItem->pszText)
2476         {
2477             COLORREF oldTextColor = 0;
2478             INT oldBkMode;
2479             HBRUSH hbrBk = 0;
2480             BOOL inFocus = (GetFocus() == infoPtr->hwnd);
2481             RECT rcText;
2482
2483             oldBkMode = SetBkMode(hdc, TRANSPARENT);
2484
2485             /* - If item is drop target or it is selected and window is in focus -
2486              * use blue background (COLOR_HIGHLIGHT).
2487              * - If item is selected, window is not in focus, but it has style
2488              * TVS_SHOWSELALWAYS - use grey background (COLOR_BTNFACE)
2489              * - Otherwise - don't fill background
2490              */
2491             if ((wineItem->state & TVIS_DROPHILITED) || ((wineItem == infoPtr->focusedItem) && !(wineItem->state & TVIS_SELECTED)) ||
2492                 ((wineItem->state & TVIS_SELECTED) && (!infoPtr->focusedItem) &&
2493                  (inFocus || (infoPtr->dwStyle & TVS_SHOWSELALWAYS))))
2494             {
2495                 if ((wineItem->state & TVIS_DROPHILITED) || inFocus)
2496                 {
2497                     hbrBk = CreateSolidBrush(GetSysColor(COLOR_HIGHLIGHT));
2498                     oldTextColor =
2499                         SetTextColor(hdc, GetSysColor(COLOR_HIGHLIGHTTEXT));
2500                 }
2501                 else
2502                 {
2503                     hbrBk = CreateSolidBrush(GetSysColor(COLOR_BTNFACE));
2504
2505                     if (infoPtr->clrText == -1)
2506                         oldTextColor =
2507                             SetTextColor(hdc, GetSysColor(COLOR_WINDOWTEXT));
2508                     else
2509                         oldTextColor = SetTextColor(hdc, infoPtr->clrText);
2510                 }
2511             }
2512             else
2513             {
2514                 if (infoPtr->clrText == -1)
2515                     oldTextColor =
2516                         SetTextColor(hdc, GetSysColor(COLOR_WINDOWTEXT));
2517                 else
2518                     oldTextColor = SetTextColor(hdc, infoPtr->clrText);
2519             }
2520
2521             rcText.top = wineItem->rect.top;
2522             rcText.bottom = wineItem->rect.bottom;
2523             rcText.left = wineItem->textOffset;
2524             rcText.right = rcText.left + wineItem->textWidth + 4;
2525
2526             if (hbrBk)
2527             {
2528                 FillRect(hdc, &rcText, hbrBk);
2529                 DeleteObject(hbrBk);
2530             }
2531
2532             /* Draw the box around the selected item */
2533             if ((wineItem == infoPtr->selectedItem) && inFocus)
2534             {
2535                 DrawFocusRect(hdc,&rcText);
2536             }
2537
2538             InflateRect(&rcText, -2, -1); /* allow for the focus rect */
2539
2540             TRACE("drawing text %s at (%d,%d)-(%d,%d)\n",
2541                   debugstr_a(wineItem->pszText),
2542                   rcText.left, rcText.top, rcText.right, rcText.bottom);
2543
2544             /* Draw it */
2545             DrawTextA(hdc,
2546                       wineItem->pszText,
2547                       lstrlenA(wineItem->pszText),
2548                       &rcText,
2549                       DT_CENTER | DT_VCENTER | DT_SINGLELINE | DT_NOPREFIX);
2550
2551             /* Restore the hdc state */
2552             SetTextColor(hdc, oldTextColor);
2553
2554             if (oldBkMode != TRANSPARENT)
2555                 SetBkMode(hdc, oldBkMode);
2556         }
2557     }
2558
2559     /* Draw insertion mark if necessary */
2560
2561     if (infoPtr->insertMarkItem)
2562         TRACE("item:%d,mark:%d\n",
2563               TREEVIEW_GetItemIndex(infoPtr, wineItem),
2564               (int)infoPtr->insertMarkItem);
2565
2566     if (wineItem == infoPtr->insertMarkItem)
2567     {
2568         HPEN hNewPen, hOldPen;
2569         int offset;
2570         int left, right;
2571
2572         hNewPen = CreatePen(PS_SOLID, 2, infoPtr->clrInsertMark);
2573         hOldPen = SelectObject(hdc, hNewPen);
2574
2575         if (infoPtr->insertBeforeorAfter)
2576             offset = wineItem->rect.bottom - 1;
2577         else
2578             offset = wineItem->rect.top + 1;
2579
2580         left = wineItem->textOffset - 2;
2581         right = wineItem->textOffset + wineItem->textWidth + 2;
2582
2583         MoveToEx(hdc, left, offset - 3, NULL);
2584         LineTo(hdc, left, offset + 4);
2585
2586         MoveToEx(hdc, left, offset, NULL);
2587         LineTo(hdc, right + 1, offset);
2588
2589         MoveToEx(hdc, right, offset + 3, NULL);
2590         LineTo(hdc, right, offset - 4);
2591
2592         SelectObject(hdc, hOldPen);
2593         DeleteObject(hNewPen);
2594     }
2595
2596     if (cditem & CDRF_NOTIFYPOSTPAINT)
2597     {
2598         cditem = TREEVIEW_SendCustomDrawItemNotify
2599             (infoPtr, hdc, wineItem, CDDS_ITEMPOSTPAINT);
2600         TRACE("postpaint:cditem-app returns 0x%x\n", cditem);
2601     }
2602
2603     SelectObject(hdc, hOldFont);
2604 }
2605
2606 /* Computes treeHeight and treeWidth and updates the scroll bars.
2607  */
2608 static void
2609 TREEVIEW_UpdateScrollBars(TREEVIEW_INFO *infoPtr)
2610 {
2611     TREEVIEW_ITEM *wineItem;
2612     HWND hwnd = infoPtr->hwnd;
2613     BOOL vert = FALSE;
2614     BOOL horz = FALSE;
2615     SCROLLINFO si;
2616     LONG scrollX = infoPtr->scrollX;
2617
2618     infoPtr->treeWidth = 0;
2619     infoPtr->treeHeight = 0;
2620
2621     /* We iterate through all visible items in order to get the tree height
2622      * and width */
2623     wineItem = infoPtr->root->firstChild;
2624
2625     while (wineItem != NULL)
2626     {
2627         if (ISVISIBLE(wineItem))
2628         {
2629             /* actually we draw text at textOffset + 2 */
2630             if (2+wineItem->textOffset+wineItem->textWidth > infoPtr->treeWidth)
2631                 infoPtr->treeWidth = wineItem->textOffset+wineItem->textWidth+2;
2632
2633             /* This is scroll-adjusted, but we fix this below. */
2634             infoPtr->treeHeight = wineItem->rect.bottom;
2635         }
2636
2637         wineItem = TREEVIEW_GetNextListItem(infoPtr, wineItem);
2638     }
2639
2640     /* Fix the scroll adjusted treeHeight and treeWidth. */
2641     if (infoPtr->root->firstChild)
2642         infoPtr->treeHeight -= infoPtr->root->firstChild->rect.top;
2643
2644     infoPtr->treeWidth += infoPtr->scrollX;
2645
2646     if (infoPtr->dwStyle & TVS_NOSCROLL) return;
2647
2648     /* Adding one scroll bar may take up enough space that it forces us
2649      * to add the other as well. */
2650     if (infoPtr->treeHeight > infoPtr->clientHeight)
2651     {
2652         vert = TRUE;
2653
2654         if (infoPtr->treeWidth
2655             > infoPtr->clientWidth - GetSystemMetrics(SM_CXVSCROLL))
2656             horz = TRUE;
2657     }
2658     else if (infoPtr->treeWidth > infoPtr->clientWidth)
2659         horz = TRUE;
2660
2661     if (!vert && horz && infoPtr->treeHeight
2662         > infoPtr->clientHeight - GetSystemMetrics(SM_CYVSCROLL))
2663         vert = TRUE;
2664
2665     if (horz && (infoPtr->dwStyle & TVS_NOHSCROLL)) horz = FALSE;
2666
2667     si.cbSize = sizeof(SCROLLINFO);
2668     si.fMask  = SIF_POS|SIF_RANGE|SIF_PAGE;
2669     si.nMin   = 0;
2670
2671     if (vert)
2672     {
2673         si.nPage = TREEVIEW_GetVisibleCount(infoPtr);
2674         si.nPos  = infoPtr->firstVisible->visibleOrder;
2675         si.nMax  = infoPtr->maxVisibleOrder - 1;
2676
2677         SetScrollInfo(hwnd, SB_VERT, &si, TRUE);
2678
2679         if (!(infoPtr->uInternalStatus & TV_VSCROLL))
2680             ShowScrollBar(hwnd, SB_VERT, TRUE);
2681         infoPtr->uInternalStatus |= TV_VSCROLL;
2682     }
2683     else
2684     {
2685         if (infoPtr->uInternalStatus & TV_VSCROLL)
2686             ShowScrollBar(hwnd, SB_VERT, FALSE);
2687         infoPtr->uInternalStatus &= ~TV_VSCROLL;
2688     }
2689
2690     if (horz)
2691     {
2692         si.nPage = infoPtr->clientWidth;
2693         si.nPos  = infoPtr->scrollX;
2694         si.nMax  = infoPtr->treeWidth - 1;
2695
2696         if (si.nPos > si.nMax - max( si.nPage-1, 0 ))
2697         {
2698            si.nPos = si.nMax - max( si.nPage-1, 0 );
2699            scrollX = si.nPos;
2700         }
2701
2702         if (!(infoPtr->uInternalStatus & TV_HSCROLL))
2703             ShowScrollBar(hwnd, SB_HORZ, TRUE);
2704         infoPtr->uInternalStatus |= TV_HSCROLL;
2705
2706         SetScrollInfo(hwnd, SB_HORZ, &si, TRUE);
2707     }
2708     else
2709     {
2710         if (infoPtr->uInternalStatus & TV_HSCROLL)
2711             ShowScrollBar(hwnd, SB_HORZ, FALSE);
2712         infoPtr->uInternalStatus &= ~TV_HSCROLL;
2713
2714         scrollX = 0;
2715     }
2716
2717     if (infoPtr->scrollX != scrollX)
2718     {
2719         TREEVIEW_HScroll(infoPtr,
2720                          MAKEWPARAM(SB_THUMBPOSITION, scrollX));
2721     }
2722
2723     if (!horz)
2724         infoPtr->uInternalStatus &= ~TV_HSCROLL;
2725 }
2726
2727 /* CtrlSpy doesn't mention this, but CorelDRAW's object manager needs it. */
2728 static LRESULT
2729 TREEVIEW_EraseBackground(TREEVIEW_INFO *infoPtr, HDC hDC)
2730 {
2731     HBRUSH hBrush = CreateSolidBrush(infoPtr->clrBk);
2732     RECT rect;
2733
2734     GetClientRect(infoPtr->hwnd, &rect);
2735     FillRect(hDC, &rect, hBrush);
2736     DeleteObject(hBrush);
2737
2738     return 1;
2739 }
2740
2741 static void
2742 TREEVIEW_Refresh(TREEVIEW_INFO *infoPtr, HDC hdc, RECT *rc)
2743 {
2744     HWND hwnd = infoPtr->hwnd;
2745     RECT rect = *rc;
2746     TREEVIEW_ITEM *wineItem;
2747
2748     if (infoPtr->clientHeight == 0 || infoPtr->clientWidth == 0)
2749     {
2750         TRACE("empty window\n");
2751         return;
2752     }
2753
2754     infoPtr->cdmode = TREEVIEW_SendCustomDrawNotify(infoPtr, CDDS_PREPAINT,
2755                                                     hdc, rect);
2756
2757     if (infoPtr->cdmode == CDRF_SKIPDEFAULT)
2758     {
2759         ReleaseDC(hwnd, hdc);
2760         return;
2761     }
2762
2763     for (wineItem = infoPtr->root->firstChild;
2764          wineItem != NULL;
2765          wineItem = TREEVIEW_GetNextListItem(infoPtr, wineItem))
2766     {
2767         if (ISVISIBLE(wineItem))
2768         {
2769             /* Avoid unneeded calculations */
2770             if (wineItem->rect.top > rect.bottom)
2771                 break;
2772             if (wineItem->rect.bottom < rect.top)
2773                 continue;
2774
2775             TREEVIEW_DrawItem(infoPtr, hdc, wineItem);
2776         }
2777     }
2778
2779     TREEVIEW_UpdateScrollBars(infoPtr);
2780
2781     if (infoPtr->cdmode & CDRF_NOTIFYPOSTPAINT)
2782         infoPtr->cdmode =
2783             TREEVIEW_SendCustomDrawNotify(infoPtr, CDDS_POSTPAINT, hdc, rect);
2784 }
2785
2786 static void
2787 TREEVIEW_Invalidate(TREEVIEW_INFO *infoPtr, TREEVIEW_ITEM *item)
2788 {
2789     if (item != NULL)
2790         InvalidateRect(infoPtr->hwnd, &item->rect, TRUE);
2791     else
2792         InvalidateRect(infoPtr->hwnd, NULL, TRUE);
2793 }
2794
2795 static LRESULT
2796 TREEVIEW_Paint(TREEVIEW_INFO *infoPtr, WPARAM wParam)
2797 {
2798     HDC hdc;
2799     PAINTSTRUCT ps;
2800     RECT rc;
2801
2802     TRACE("\n");
2803
2804     if (wParam)
2805     {
2806         hdc = (HDC)wParam;
2807         if (!GetUpdateRect(infoPtr->hwnd, &rc, TRUE))
2808         {
2809             HBITMAP hbitmap;
2810             BITMAP bitmap;
2811             hbitmap = GetCurrentObject(hdc, OBJ_BITMAP);
2812             if (!hbitmap) return 0;
2813             GetObjectA(hbitmap, sizeof(BITMAP), &bitmap);
2814             rc.left = 0; rc.top = 0;
2815             rc.right = bitmap.bmWidth;
2816             rc.bottom = bitmap.bmHeight;  
2817             TREEVIEW_EraseBackground(infoPtr, wParam);
2818         }
2819     }
2820     else
2821     {
2822         hdc = BeginPaint(infoPtr->hwnd, &ps);
2823         rc = ps.rcPaint;
2824     }
2825
2826     if(infoPtr->bRedraw) /* WM_SETREDRAW sets bRedraw */
2827         TREEVIEW_Refresh(infoPtr, hdc, &rc);
2828
2829     if (!wParam)
2830         EndPaint(infoPtr->hwnd, &ps);
2831
2832     return 0;
2833 }
2834
2835
2836 /* Sorting **************************************************************/
2837
2838 /***************************************************************************
2839  * Forward the DPA local callback to the treeview owner callback
2840  */
2841 static INT WINAPI
2842 TREEVIEW_CallBackCompare(TREEVIEW_ITEM *first, TREEVIEW_ITEM *second, LPTVSORTCB pCallBackSort)
2843 {
2844     /* Forward the call to the client-defined callback */
2845     return pCallBackSort->lpfnCompare(first->lParam,
2846                                       second->lParam,
2847                                       pCallBackSort->lParam);
2848 }
2849
2850 /***************************************************************************
2851  * Treeview native sort routine: sort on item text.
2852  */
2853 static INT WINAPI
2854 TREEVIEW_SortOnName(TREEVIEW_ITEM *first, TREEVIEW_ITEM *second,
2855                      TREEVIEW_INFO *infoPtr)
2856 {
2857     TREEVIEW_UpdateDispInfo(infoPtr, first, TVIF_TEXT);
2858     TREEVIEW_UpdateDispInfo(infoPtr, second, TVIF_TEXT);
2859
2860     return strcasecmp(first->pszText, second->pszText);
2861 }
2862
2863 /* Returns the number of physical children belonging to item. */
2864 static INT
2865 TREEVIEW_CountChildren(TREEVIEW_INFO *infoPtr, TREEVIEW_ITEM *item)
2866 {
2867     INT cChildren = 0;
2868     HTREEITEM hti;
2869
2870     for (hti = item->firstChild; hti != NULL; hti = hti->nextSibling)
2871         cChildren++;
2872
2873     return cChildren;
2874 }
2875
2876 /* Returns a DPA containing a pointer to each physical child of item in
2877  * sibling order. If item has no children, an empty DPA is returned. */
2878 static HDPA
2879 TREEVIEW_BuildChildDPA(TREEVIEW_INFO *infoPtr, TREEVIEW_ITEM *item)
2880 {
2881     HTREEITEM child = item->firstChild;
2882
2883     HDPA list = DPA_Create(8);
2884     if (list == 0) return NULL;
2885
2886     for (child = item->firstChild; child != NULL; child = child->nextSibling)
2887     {
2888         if (DPA_InsertPtr(list, INT_MAX, child) == -1)
2889         {
2890             DPA_Destroy(list);
2891             return NULL;
2892         }
2893     }
2894
2895     return list;
2896 }
2897
2898 /***************************************************************************
2899  * Setup the treeview structure with regards of the sort method
2900  * and sort the children of the TV item specified in lParam
2901  * fRecurse: currently unused. Should be zero.
2902  * parent: if pSort!=NULL, should equal pSort->hParent.
2903  *         otherwise, item which child items are to be sorted.
2904  * pSort:  sort method info. if NULL, sort on item text.
2905  *         if non-NULL, sort on item's lParam content, and let the
2906  *         application decide what that means. See also TVM_SORTCHILDRENCB.
2907  */
2908
2909 static LRESULT
2910 TREEVIEW_Sort(TREEVIEW_INFO *infoPtr, BOOL fRecurse, HTREEITEM parent,
2911               LPTVSORTCB pSort)
2912 {
2913     INT cChildren;
2914     PFNDPACOMPARE pfnCompare;
2915     LPARAM lpCompare;
2916
2917     /* undocumented feature: TVI_ROOT means `sort the whole tree' */
2918     if (parent == TVI_ROOT)
2919         parent = infoPtr->root;
2920
2921     /* Check for a valid handle to the parent item */
2922     if (!TREEVIEW_ValidItem(infoPtr, parent))
2923     {
2924         ERR("invalid item hParent=%x\n", (INT)parent);
2925         return FALSE;
2926     }
2927
2928     if (pSort)
2929     {
2930         pfnCompare = (PFNDPACOMPARE)TREEVIEW_CallBackCompare;
2931         lpCompare = (LPARAM)pSort;
2932     }
2933     else
2934     {
2935         pfnCompare = (PFNDPACOMPARE)TREEVIEW_SortOnName;
2936         lpCompare = (LPARAM)infoPtr;
2937     }
2938
2939     cChildren = TREEVIEW_CountChildren(infoPtr, parent);
2940
2941     /* Make sure there is something to sort */
2942     if (cChildren > 1)
2943     {
2944         /* TREEVIEW_ITEM rechaining */
2945         INT count = 0;
2946         HTREEITEM item = 0;
2947         HTREEITEM nextItem = 0;
2948         HTREEITEM prevItem = 0;
2949
2950         HDPA sortList = TREEVIEW_BuildChildDPA(infoPtr, parent);
2951
2952         if (sortList == NULL)
2953             return FALSE;
2954
2955         /* let DPA sort the list */
2956         DPA_Sort(sortList, pfnCompare, lpCompare);
2957
2958         /* The order of DPA entries has been changed, so fixup the
2959          * nextSibling and prevSibling pointers. */
2960
2961         item = (HTREEITEM)DPA_GetPtr(sortList, count++);
2962         while ((nextItem = (HTREEITEM)DPA_GetPtr(sortList, count++)) != NULL)
2963         {
2964             /* link the two current item toghether */
2965             item->nextSibling = nextItem;
2966             nextItem->prevSibling = item;
2967
2968             if (prevItem == NULL)
2969             {
2970                 /* this is the first item, update the parent */
2971                 parent->firstChild = item;
2972                 item->prevSibling = NULL;
2973             }
2974             else
2975             {
2976                 /* fix the back chaining */
2977                 item->prevSibling = prevItem;
2978             }
2979
2980             /* get ready for the next one */
2981             prevItem = item;
2982             item = nextItem;
2983         }
2984
2985         /* the last item is pointed to by item and never has a sibling */
2986         item->nextSibling = NULL;
2987         parent->lastChild = item;
2988
2989         DPA_Destroy(sortList);
2990
2991         TREEVIEW_VerifyTree(infoPtr);
2992
2993         if (parent->state & TVIS_EXPANDED)
2994         {
2995             int visOrder = infoPtr->firstVisible->visibleOrder;
2996
2997         if (parent == infoPtr->root)
2998             TREEVIEW_RecalculateVisibleOrder(infoPtr, NULL);
2999         else
3000             TREEVIEW_RecalculateVisibleOrder(infoPtr, parent);
3001
3002             if (TREEVIEW_IsChildOf(parent, infoPtr->firstVisible))
3003             {
3004                 TREEVIEW_ITEM *item;
3005
3006                 for (item = infoPtr->root->firstChild; item != NULL;
3007                      item = TREEVIEW_GetNextListItem(infoPtr, item))
3008                 {
3009                     if (item->visibleOrder == visOrder)
3010                         break;
3011                 }
3012
3013                 TREEVIEW_SetFirstVisible(infoPtr, item, FALSE);
3014             }
3015
3016             TREEVIEW_Invalidate(infoPtr, NULL);
3017         }
3018
3019         return TRUE;
3020     }
3021     return FALSE;
3022 }
3023
3024
3025 /***************************************************************************
3026  * Setup the treeview structure with regards of the sort method
3027  * and sort the children of the TV item specified in lParam
3028  */
3029 static LRESULT
3030 TREEVIEW_SortChildrenCB(TREEVIEW_INFO *infoPtr, WPARAM wParam, LPTVSORTCB pSort)
3031 {
3032     return TREEVIEW_Sort(infoPtr, wParam, pSort->hParent, pSort);
3033 }
3034
3035
3036 /***************************************************************************
3037  * Sort the children of the TV item specified in lParam.
3038  */
3039 static LRESULT
3040 TREEVIEW_SortChildren(TREEVIEW_INFO *infoPtr, WPARAM wParam, LPARAM lParam)
3041 {
3042     return TREEVIEW_Sort(infoPtr, (BOOL)wParam, (HTREEITEM)lParam, NULL);
3043 }
3044
3045
3046 /* Expansion/Collapse ***************************************************/
3047
3048 static BOOL
3049 TREEVIEW_SendExpanding(TREEVIEW_INFO *infoPtr, TREEVIEW_ITEM *wineItem,
3050                        UINT action)
3051 {
3052     return !TREEVIEW_SendTreeviewNotify(infoPtr,
3053                                 (infoPtr->bNtfUnicode) ? TVN_ITEMEXPANDINGW : 
3054                                                          TVN_ITEMEXPANDINGA,
3055                                 action,
3056                                         TVIF_HANDLE | TVIF_STATE | TVIF_PARAM
3057                                         | TVIF_IMAGE | TVIF_SELECTEDIMAGE,
3058                                         0, wineItem);
3059 }
3060
3061 static VOID
3062 TREEVIEW_SendExpanded(TREEVIEW_INFO *infoPtr, TREEVIEW_ITEM *wineItem,
3063                       UINT action)
3064 {
3065     TREEVIEW_SendTreeviewNotify(infoPtr, 
3066                                 (infoPtr->bNtfUnicode) ? TVN_ITEMEXPANDEDW : 
3067                                                          TVN_ITEMEXPANDEDA,
3068                                 action,
3069                                 TVIF_HANDLE | TVIF_STATE | TVIF_PARAM
3070                                 | TVIF_IMAGE | TVIF_SELECTEDIMAGE,
3071                                 0, wineItem);
3072 }
3073
3074
3075 /* This corresponds to TVM_EXPAND with TVE_COLLAPSE.
3076  * bRemoveChildren corresponds to TVE_COLLAPSERESET. */
3077 static BOOL
3078 TREEVIEW_Collapse(TREEVIEW_INFO *infoPtr, TREEVIEW_ITEM *wineItem,
3079                   BOOL bRemoveChildren, BOOL bUser)
3080 {
3081     UINT action = TVE_COLLAPSE | (bRemoveChildren ? TVE_COLLAPSERESET : 0);
3082     BOOL bSetSelection, bSetFirstVisible;
3083
3084     TRACE("TVE_COLLAPSE %p %s\n", wineItem, TREEVIEW_ItemName(wineItem));
3085
3086     if (!(wineItem->state & TVIS_EXPANDED) || wineItem->firstChild == NULL)
3087         return FALSE;
3088
3089     if (bUser)
3090         TREEVIEW_SendExpanding(infoPtr, wineItem, action);
3091
3092     wineItem->state &= ~TVIS_EXPANDED;
3093
3094     if (bUser)
3095         TREEVIEW_SendExpanded(infoPtr, wineItem, action);
3096
3097     bSetSelection = (infoPtr->selectedItem != NULL
3098                      && TREEVIEW_IsChildOf(wineItem, infoPtr->selectedItem));
3099
3100     bSetFirstVisible = (infoPtr->firstVisible != NULL
3101                         && TREEVIEW_IsChildOf(wineItem, infoPtr->firstVisible));
3102
3103     if (bRemoveChildren)
3104     {
3105         TRACE("TVE_COLLAPSERESET\n");
3106         wineItem->state &= ~TVIS_EXPANDEDONCE;
3107         TREEVIEW_RemoveAllChildren(infoPtr, wineItem);
3108     }
3109
3110     if (wineItem->firstChild)
3111     {
3112         TREEVIEW_ITEM *item, *sibling;
3113
3114         sibling = TREEVIEW_GetNextListItem(infoPtr, wineItem);
3115
3116         for (item = wineItem->firstChild; item != sibling;
3117              item = TREEVIEW_GetNextListItem(infoPtr, item))
3118         {
3119             item->visibleOrder = -1;
3120         }
3121     }
3122
3123     TREEVIEW_RecalculateVisibleOrder(infoPtr, wineItem);
3124
3125     TREEVIEW_SetFirstVisible(infoPtr, bSetFirstVisible ? wineItem
3126                              : infoPtr->firstVisible, TRUE);
3127
3128     if (bSetSelection)
3129     {
3130         /* Don't call DoSelectItem, it sends notifications. */
3131         if (TREEVIEW_ValidItem(infoPtr, infoPtr->selectedItem))
3132             infoPtr->selectedItem->state &= ~TVIS_SELECTED;
3133         wineItem->state |= TVIS_SELECTED;
3134         infoPtr->selectedItem = wineItem;
3135
3136         TREEVIEW_EnsureVisible(infoPtr, wineItem, FALSE);
3137     }
3138
3139     TREEVIEW_UpdateScrollBars(infoPtr);
3140     TREEVIEW_Invalidate(infoPtr, NULL);
3141
3142     return TRUE;
3143 }
3144
3145 static BOOL
3146 TREEVIEW_Expand(TREEVIEW_INFO *infoPtr, TREEVIEW_ITEM *wineItem,
3147                 BOOL bExpandPartial, BOOL bUser)
3148 {
3149     TRACE("\n");
3150
3151     if (!TREEVIEW_HasChildren(infoPtr, wineItem)
3152         || wineItem->state & TVIS_EXPANDED)
3153         return FALSE;
3154
3155     TRACE("TVE_EXPAND %p %s\n", wineItem, TREEVIEW_ItemName(wineItem));
3156
3157     if (bUser || !(wineItem->state & TVIS_EXPANDEDONCE))
3158     {
3159         if (!TREEVIEW_SendExpanding(infoPtr, wineItem, TVE_EXPAND))
3160         {
3161             TRACE("  TVN_ITEMEXPANDING returned TRUE, exiting...\n");
3162             return FALSE;
3163         }
3164
3165         wineItem->state |= TVIS_EXPANDED;
3166         TREEVIEW_SendExpanded(infoPtr, wineItem, TVE_EXPAND);
3167         wineItem->state |= TVIS_EXPANDEDONCE;
3168     }
3169     else
3170     {
3171         /* this item has already been expanded */
3172         wineItem->state |= TVIS_EXPANDED;
3173     }
3174
3175     if (bExpandPartial)
3176         FIXME("TVE_EXPANDPARTIAL not implemented\n");
3177
3178     TREEVIEW_RecalculateVisibleOrder(infoPtr, wineItem);
3179     TREEVIEW_UpdateSubTree(infoPtr, wineItem);
3180     TREEVIEW_UpdateScrollBars(infoPtr);
3181
3182     /* Scroll up so that as many children as possible are visible.
3183      * This looses when expanding causes an HScroll bar to appear, but we
3184      * don't know that yet, so the last item is obscured. */
3185     if (wineItem->firstChild != NULL)
3186     {
3187         int nChildren = wineItem->lastChild->visibleOrder
3188             - wineItem->firstChild->visibleOrder + 1;
3189
3190         int visible_pos = wineItem->visibleOrder
3191             - infoPtr->firstVisible->visibleOrder;
3192
3193         int rows_below = TREEVIEW_GetVisibleCount(infoPtr) - visible_pos - 1;
3194
3195         if (visible_pos > 0 && nChildren > rows_below)
3196         {
3197             int scroll = nChildren - rows_below;
3198
3199             if (scroll > visible_pos)
3200                 scroll = visible_pos;
3201
3202             if (scroll > 0)
3203             {
3204                 TREEVIEW_ITEM *newFirstVisible
3205                     = TREEVIEW_GetListItem(infoPtr, infoPtr->firstVisible,
3206                                            scroll);
3207
3208
3209                 TREEVIEW_SetFirstVisible(infoPtr, newFirstVisible, TRUE);
3210             }
3211         }
3212     }
3213
3214     TREEVIEW_Invalidate(infoPtr, NULL);
3215
3216     return TRUE;
3217 }
3218
3219 static BOOL
3220 TREEVIEW_Toggle(TREEVIEW_INFO *infoPtr, TREEVIEW_ITEM *wineItem, BOOL bUser)
3221 {
3222     TRACE("\n");
3223
3224     if (wineItem->state & TVIS_EXPANDED)
3225         return TREEVIEW_Collapse(infoPtr, wineItem, FALSE, bUser);
3226     else
3227         return TREEVIEW_Expand(infoPtr, wineItem, FALSE, bUser);
3228 }
3229
3230 static VOID
3231 TREEVIEW_ExpandAll(TREEVIEW_INFO *infoPtr, TREEVIEW_ITEM *item)
3232 {
3233     TREEVIEW_Expand(infoPtr, item, FALSE, TRUE);
3234
3235     for (item = item->firstChild; item != NULL; item = item->nextSibling)
3236     {
3237         if (TREEVIEW_HasChildren(infoPtr, item))
3238             TREEVIEW_ExpandAll(infoPtr, item);
3239     }
3240 }
3241
3242 /* Note:If the specified item is the child of a collapsed parent item,
3243    the parent's list of child items is (recursively) expanded to reveal the 
3244    specified item. This is mentioned for TREEVIEW_SelectItem; don't 
3245    know if it also applies here.
3246 */
3247
3248 static LRESULT
3249 TREEVIEW_ExpandMsg(TREEVIEW_INFO *infoPtr, UINT flag, HTREEITEM wineItem)
3250 {
3251     if (!TREEVIEW_ValidItem(infoPtr, wineItem))
3252         return 0;
3253
3254     TRACE("For (%s) item:%d, flags %x, state:%d\n",
3255               TREEVIEW_ItemName(wineItem), flag,
3256               TREEVIEW_GetItemIndex(infoPtr, wineItem), wineItem->state);
3257
3258     switch (flag & TVE_TOGGLE)
3259     {
3260     case TVE_COLLAPSE:
3261         return TREEVIEW_Collapse(infoPtr, wineItem, flag & TVE_COLLAPSERESET,
3262                                  FALSE);
3263
3264     case TVE_EXPAND:
3265         return TREEVIEW_Expand(infoPtr, wineItem, flag & TVE_EXPANDPARTIAL,
3266                                FALSE);
3267
3268     case TVE_TOGGLE:
3269         return TREEVIEW_Toggle(infoPtr, wineItem, TRUE);
3270
3271     default:
3272         return 0;
3273     }
3274
3275 #if 0
3276     TRACE("Exiting, Item %p state is now %d...\n", wineItem, wineItem->state);
3277 #endif
3278 }
3279
3280 /* Hit-Testing **********************************************************/
3281
3282 static TREEVIEW_ITEM *
3283 TREEVIEW_HitTestPoint(TREEVIEW_INFO *infoPtr, POINT pt)
3284 {
3285     TREEVIEW_ITEM *wineItem;
3286     LONG row;
3287
3288     if (!infoPtr->firstVisible)
3289         return NULL;
3290
3291     row = pt.y / infoPtr->uItemHeight + infoPtr->firstVisible->visibleOrder;
3292
3293     for (wineItem = infoPtr->firstVisible; wineItem != NULL;
3294          wineItem = TREEVIEW_GetNextListItem(infoPtr, wineItem))
3295     {
3296         if (row >= wineItem->visibleOrder
3297             && row < wineItem->visibleOrder + wineItem->iIntegral)
3298             break;
3299     }
3300
3301     return wineItem;
3302 }
3303
3304 static LRESULT
3305 TREEVIEW_HitTest(TREEVIEW_INFO *infoPtr, LPTVHITTESTINFO lpht)
3306 {
3307     TREEVIEW_ITEM *wineItem;
3308     RECT rect;
3309     UINT status;
3310     LONG x, y;
3311
3312     lpht->hItem = 0;
3313     GetClientRect(infoPtr->hwnd, &rect);
3314     status = 0;
3315     x = lpht->pt.x;
3316     y = lpht->pt.y;
3317
3318     if (x < rect.left)
3319     {
3320         status |= TVHT_TOLEFT;
3321     }
3322     else if (x > rect.right)
3323     {
3324         status |= TVHT_TORIGHT;
3325     }
3326
3327     if (y < rect.top)
3328     {
3329         status |= TVHT_ABOVE;
3330     }
3331     else if (y > rect.bottom)
3332     {
3333         status |= TVHT_BELOW;
3334     }
3335
3336     if (status)
3337     {
3338         lpht->flags = status;
3339         return (LRESULT)(HTREEITEM)NULL;
3340     }
3341
3342     wineItem = TREEVIEW_HitTestPoint(infoPtr, lpht->pt);
3343     if (!wineItem)
3344     {
3345         lpht->flags = TVHT_NOWHERE;
3346         return (LRESULT)(HTREEITEM)NULL;
3347     }
3348
3349     if (x >= wineItem->textOffset + wineItem->textWidth)
3350     {
3351         lpht->flags = TVHT_ONITEMRIGHT;
3352     }
3353     else if (x >= wineItem->textOffset)
3354     {
3355         lpht->flags = TVHT_ONITEMLABEL;
3356     }
3357     else if (x >= wineItem->imageOffset)
3358     {
3359         lpht->flags = TVHT_ONITEMICON;
3360     }
3361     else if (x >= wineItem->stateOffset)
3362     {
3363         lpht->flags = TVHT_ONITEMSTATEICON;
3364     }
3365     else if (x >= wineItem->linesOffset && infoPtr->dwStyle & TVS_HASBUTTONS)
3366     {
3367         lpht->flags = TVHT_ONITEMBUTTON;
3368     }
3369     else
3370     {
3371         lpht->flags = TVHT_ONITEMINDENT;
3372     }
3373
3374     lpht->hItem = wineItem;
3375     TRACE("(%ld,%ld):result %x\n", lpht->pt.x, lpht->pt.y, lpht->flags);
3376
3377     return (LRESULT)wineItem;
3378 }
3379
3380 /* Item Label Editing ***************************************************/
3381
3382 static LRESULT
3383 TREEVIEW_GetEditControl(TREEVIEW_INFO *infoPtr)
3384 {
3385     return infoPtr->hwndEdit;
3386 }
3387
3388 static LRESULT CALLBACK
3389 TREEVIEW_Edit_SubclassProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
3390 {
3391     TREEVIEW_INFO *infoPtr;
3392     BOOL bCancel = FALSE;
3393
3394     switch (uMsg)
3395     {
3396     case WM_PAINT:
3397         {
3398             LRESULT rc;
3399             TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(GetParent(hwnd));
3400
3401             TRACE("WM_PAINT start\n");
3402             rc = CallWindowProcA(infoPtr->wpEditOrig, hwnd, uMsg, wParam,
3403                                  lParam);
3404             TRACE("WM_PAINT done\n");
3405             return rc;
3406         }
3407
3408     case WM_KILLFOCUS:
3409     {
3410         TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(GetParent(hwnd));
3411         if (infoPtr->bIgnoreEditKillFocus)
3412             return TRUE;
3413
3414         break;
3415     }
3416
3417     case WM_GETDLGCODE:
3418         return DLGC_WANTARROWS | DLGC_WANTALLKEYS;
3419
3420     case WM_KEYDOWN:
3421         if (wParam == (WPARAM)VK_ESCAPE)
3422         {
3423             bCancel = TRUE;
3424             break;
3425         }
3426         else if (wParam == (WPARAM)VK_RETURN)
3427         {
3428             break;
3429         }
3430
3431         /* fall through */
3432     default:
3433         {
3434             TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(GetParent(hwnd));
3435
3436             return CallWindowProcA(infoPtr->wpEditOrig, hwnd, uMsg, wParam,
3437                                    lParam);
3438         }
3439     }
3440
3441     /* Processing TVN_ENDLABELEDIT message could kill the focus       */
3442     /* eg. Using a messagebox                                         */
3443
3444     infoPtr = TREEVIEW_GetInfoPtr(GetParent(hwnd));
3445     infoPtr->bIgnoreEditKillFocus = TRUE;
3446     TREEVIEW_EndEditLabelNow(infoPtr, bCancel || !infoPtr->bLabelChanged);
3447     infoPtr->bIgnoreEditKillFocus = FALSE;
3448
3449     return 0;
3450 }
3451
3452
3453 /* should handle edit control messages here */
3454
3455 static LRESULT
3456 TREEVIEW_Command(TREEVIEW_INFO *infoPtr, WPARAM wParam, LPARAM lParam)
3457 {
3458     TRACE("%x %ld\n", wParam, lParam);
3459
3460     switch (HIWORD(wParam))
3461     {
3462     case EN_UPDATE:
3463         {
3464             /* 
3465              * Adjust the edit window size 
3466              */
3467             char buffer[1024];
3468             TREEVIEW_ITEM *editItem = infoPtr->selectedItem;
3469             HDC hdc = GetDC(infoPtr->hwndEdit);
3470             SIZE sz;
3471             int len;
3472             HFONT hFont, hOldFont = 0;
3473
3474             infoPtr->bLabelChanged = TRUE;
3475
3476             len = GetWindowTextA(infoPtr->hwndEdit, buffer, sizeof(buffer));
3477
3478             /* Select font to get the right dimension of the string */
3479             hFont = SendMessageA(infoPtr->hwndEdit, WM_GETFONT, 0, 0);
3480             if (hFont != 0)
3481             {
3482                 hOldFont = SelectObject(hdc, hFont);
3483             }
3484
3485             if (GetTextExtentPoint32A(hdc, buffer, strlen(buffer), &sz))
3486             {
3487                 TEXTMETRICA textMetric;
3488
3489                 /* Add Extra spacing for the next character */
3490                 GetTextMetricsA(hdc, &textMetric);
3491                 sz.cx += (textMetric.tmMaxCharWidth * 2);
3492
3493                 sz.cx = max(sz.cx, textMetric.tmMaxCharWidth * 3);
3494                 sz.cx = min(sz.cx,
3495                             infoPtr->clientWidth - editItem->textOffset + 2);
3496
3497                 SetWindowPos(infoPtr->hwndEdit,
3498                              HWND_TOP,
3499                              0,
3500                              0,
3501                              sz.cx,
3502                              editItem->rect.bottom - editItem->rect.top + 3,
3503                              SWP_NOMOVE | SWP_DRAWFRAME);
3504             }
3505
3506             if (hFont != 0)
3507             {
3508                 SelectObject(hdc, hOldFont);
3509             }
3510
3511             ReleaseDC(infoPtr->hwnd, hdc);
3512             break;
3513         }
3514
3515     default:
3516         return SendMessageA(GetParent(infoPtr->hwnd), WM_COMMAND, wParam, lParam);
3517     }
3518
3519     return 0;
3520 }
3521
3522 static HWND
3523 TREEVIEW_EditLabelA(TREEVIEW_INFO *infoPtr, HTREEITEM hItem)
3524 {
3525     HWND hwnd = infoPtr->hwnd;
3526     HWND hwndEdit;
3527     SIZE sz;
3528     TREEVIEW_ITEM *editItem = hItem;
3529     HINSTANCE hinst = GetWindowLongA(hwnd, GWL_HINSTANCE);
3530     HDC hdc;
3531     HFONT hOldFont=0;
3532     TEXTMETRICA textMetric;
3533
3534     TRACE("%x %p\n", (unsigned)hwnd, hItem);
3535     if (!TREEVIEW_ValidItem(infoPtr, editItem))
3536         return (HWND)NULL;
3537
3538     if (infoPtr->hwndEdit)
3539         return infoPtr->hwndEdit;
3540
3541     infoPtr->bLabelChanged = FALSE;
3542
3543     /* Make sure that edit item is selected */
3544     TREEVIEW_DoSelectItem(infoPtr, TVGN_CARET, hItem, TVC_UNKNOWN);
3545     TREEVIEW_EnsureVisible(infoPtr, hItem, TRUE);
3546
3547     TREEVIEW_UpdateDispInfo(infoPtr, editItem, TVIF_TEXT);
3548
3549     hdc = GetDC(hwnd);
3550     /* Select the font to get appropriate metric dimensions */
3551     if (infoPtr->hFont != 0)
3552     {
3553         hOldFont = SelectObject(hdc, infoPtr->hFont);
3554     }
3555
3556     /* Get string length in pixels */
3557     GetTextExtentPoint32A(hdc, editItem->pszText, strlen(editItem->pszText),
3558                           &sz);
3559
3560     /* Add Extra spacing for the next character */
3561     GetTextMetricsA(hdc, &textMetric);
3562     sz.cx += (textMetric.tmMaxCharWidth * 2);
3563
3564     sz.cx = max(sz.cx, textMetric.tmMaxCharWidth * 3);
3565     sz.cx = min(sz.cx, infoPtr->clientWidth - editItem->textOffset + 2);
3566
3567     if (infoPtr->hFont != 0)
3568     {
3569         SelectObject(hdc, hOldFont);
3570     }
3571
3572     ReleaseDC(hwnd, hdc);
3573     hwndEdit = CreateWindowExA(WS_EX_LEFT,
3574                                "EDIT",
3575                                0,
3576                                WS_CHILD | WS_BORDER | ES_AUTOHSCROLL |
3577                                WS_CLIPSIBLINGS | ES_WANTRETURN |
3578                                ES_LEFT, editItem->textOffset - 2,
3579                                editItem->rect.top - 1, sz.cx + 3,
3580                                editItem->rect.bottom -
3581                                editItem->rect.top + 3, hwnd, 0, hinst, 0);
3582 /* FIXME: (HMENU)IDTVEDIT,pcs->hInstance,0); */
3583
3584     infoPtr->hwndEdit = hwndEdit;
3585
3586     /* Get a 2D border. */
3587     SetWindowLongA(hwndEdit, GWL_EXSTYLE,
3588                    GetWindowLongA(hwndEdit, GWL_EXSTYLE) & ~WS_EX_CLIENTEDGE);
3589     SetWindowLongA(hwndEdit, GWL_STYLE,
3590                    GetWindowLongA(hwndEdit, GWL_STYLE) | WS_BORDER);
3591
3592     SendMessageA(hwndEdit, WM_SETFONT, TREEVIEW_FontForItem(infoPtr, editItem),
3593                  FALSE);
3594
3595     infoPtr->wpEditOrig = (WNDPROC)SetWindowLongA(hwndEdit, GWL_WNDPROC,
3596                                                   (DWORD)
3597                                                   TREEVIEW_Edit_SubclassProc);
3598
3599     if (TREEVIEW_BeginLabelEditNotify(infoPtr, editItem))
3600     {
3601         DestroyWindow(hwndEdit);
3602         infoPtr->hwndEdit = 0;
3603         return (HWND)NULL;
3604     }
3605
3606     infoPtr->selectedItem = hItem;
3607     SetWindowTextA(hwndEdit, editItem->pszText);
3608     SetFocus(hwndEdit);
3609     SendMessageA(hwndEdit, EM_SETSEL, 0, -1);
3610     ShowWindow(hwndEdit, SW_SHOW);
3611
3612     return hwndEdit;
3613 }
3614
3615
3616 static LRESULT
3617 TREEVIEW_EndEditLabelNow(TREEVIEW_INFO *infoPtr, BOOL bCancel)
3618 {
3619     HWND hwnd = infoPtr->hwnd;
3620     TREEVIEW_ITEM *editedItem = infoPtr->selectedItem;
3621     NMTVDISPINFOA tvdi;
3622     BOOL bCommit;
3623     char tmpText[1024] = { '\0' };
3624     int iLength = 0;
3625
3626     if (!infoPtr->hwndEdit)
3627         return FALSE;
3628
3629     tvdi.hdr.hwndFrom = hwnd;
3630     tvdi.hdr.idFrom = GetWindowLongA(hwnd, GWL_ID);
3631     tvdi.hdr.code = TVN_ENDLABELEDITA;
3632     tvdi.item.mask = 0;
3633     tvdi.item.hItem = editedItem;
3634     tvdi.item.state = editedItem->state;
3635     tvdi.item.lParam = editedItem->lParam;
3636
3637     if (!bCancel)
3638     {
3639         iLength = GetWindowTextA(infoPtr->hwndEdit, tmpText, 1023);
3640
3641         if (iLength >= 1023)
3642         {
3643             ERR("Insuficient space to retrieve new item label\n");
3644         }
3645
3646         tvdi.item.pszText = tmpText;
3647         tvdi.item.cchTextMax = iLength + 1;
3648     }
3649     else
3650     {
3651         tvdi.item.pszText = NULL;
3652         tvdi.item.cchTextMax = 0;
3653     }
3654
3655     bCommit = (BOOL)TREEVIEW_SendRealNotify(infoPtr,
3656                                  (WPARAM)tvdi.hdr.idFrom, (LPARAM)&tvdi);
3657
3658     if (!bCancel && bCommit)    /* Apply the changes */
3659     {
3660         if (strcmp(tmpText, editedItem->pszText) != 0)
3661         {
3662             if (NULL == COMCTL32_ReAlloc(editedItem->pszText, iLength + 1))
3663             {
3664                 ERR("OutOfMemory, cannot allocate space for label\n");
3665                 DestroyWindow(infoPtr->hwndEdit);
3666                 infoPtr->hwndEdit = 0;
3667                 return FALSE;
3668             }
3669             else
3670             {
3671                 editedItem->cchTextMax = iLength + 1;
3672                 lstrcpyA(editedItem->pszText, tmpText);
3673             }
3674         }
3675     }
3676
3677     ShowWindow(infoPtr->hwndEdit, SW_HIDE);
3678     DestroyWindow(infoPtr->hwndEdit);
3679     infoPtr->hwndEdit = 0;
3680     return TRUE;
3681 }
3682
3683 static LRESULT
3684 TREEVIEW_HandleTimer(TREEVIEW_INFO *infoPtr, WPARAM wParam)
3685 {
3686     if (wParam != TV_EDIT_TIMER)
3687     {
3688         ERR("got unknown timer\n");
3689         return 1;
3690     }
3691
3692     KillTimer(infoPtr->hwnd, TV_EDIT_TIMER);
3693     infoPtr->Timer &= ~TV_EDIT_TIMER_SET;
3694
3695     TREEVIEW_EditLabelA(infoPtr, infoPtr->selectedItem);
3696
3697     return 0;
3698 }
3699
3700
3701 /* Mouse Tracking/Drag **************************************************/
3702
3703 /***************************************************************************
3704  * This is quite unusual piece of code, but that's how it's implemented in
3705  * Windows.
3706  */
3707 static LRESULT
3708 TREEVIEW_TrackMouse(TREEVIEW_INFO *infoPtr, POINT pt)
3709 {
3710     INT cxDrag = GetSystemMetrics(SM_CXDRAG);
3711     INT cyDrag = GetSystemMetrics(SM_CYDRAG);
3712     RECT r;
3713     MSG msg;
3714
3715     r.top = pt.y - cyDrag;
3716     r.left = pt.x - cxDrag;
3717     r.bottom = pt.y + cyDrag;
3718     r.right = pt.x + cxDrag;
3719
3720     SetCapture(infoPtr->hwnd);
3721
3722     while (1)
3723     {
3724         if (PeekMessageA(&msg, 0, 0, 0, PM_REMOVE | PM_NOYIELD))
3725         {
3726             if (msg.message == WM_MOUSEMOVE)
3727             {
3728                 pt.x = SLOWORD(msg.lParam);
3729                 pt.y = SHIWORD(msg.lParam);
3730                 if (PtInRect(&r, pt))
3731                     continue;
3732                 else
3733                 {
3734                     ReleaseCapture();
3735                     return 1;
3736                 }
3737             }
3738             else if (msg.message >= WM_LBUTTONDOWN &&
3739                      msg.message <= WM_RBUTTONDBLCLK)
3740             {
3741                 if (msg.message == WM_RBUTTONUP)
3742                     TREEVIEW_RButtonUp(infoPtr, &pt);
3743                 break;
3744             }
3745
3746             DispatchMessageA(&msg);
3747         }
3748
3749         if (GetCapture() != infoPtr->hwnd)
3750             return 0;
3751     }
3752
3753     ReleaseCapture();
3754     return 0;
3755 }
3756
3757
3758 static LRESULT
3759 TREEVIEW_LButtonDoubleClick(TREEVIEW_INFO *infoPtr, LPARAM lParam)
3760 {
3761     TREEVIEW_ITEM *wineItem;
3762     TVHITTESTINFO hit;
3763
3764     TRACE("\n");
3765     SetFocus(infoPtr->hwnd);
3766
3767     if (infoPtr->Timer & TV_EDIT_TIMER_SET)
3768     {
3769         /* If there is pending 'edit label' event - kill it now */
3770         KillTimer(infoPtr->hwnd, TV_EDIT_TIMER);
3771     }
3772
3773     hit.pt.x = SLOWORD(lParam);
3774     hit.pt.y = SHIWORD(lParam);
3775
3776     wineItem = (TREEVIEW_ITEM *)TREEVIEW_HitTest(infoPtr, &hit);
3777     if (!wineItem)
3778         return 0;
3779     TRACE("item %d\n", TREEVIEW_GetItemIndex(infoPtr, wineItem));
3780
3781     if (TREEVIEW_SendSimpleNotify(infoPtr, NM_DBLCLK) == FALSE)
3782     {                           /* FIXME! */
3783         switch (hit.flags)
3784         {
3785         case TVHT_ONITEMRIGHT:
3786             /* FIXME: we should not have sent NM_DBLCLK in this case. */
3787             break;
3788
3789         case TVHT_ONITEMINDENT:
3790             if (!(infoPtr->dwStyle & TVS_HASLINES))
3791             {
3792                 break;
3793             }
3794             else
3795             {
3796                 int level = hit.pt.x / infoPtr->uIndent;
3797                 if (!(infoPtr->dwStyle & TVS_LINESATROOT)) level++;
3798
3799                 while (wineItem->iLevel > level)
3800                 {
3801                     wineItem = wineItem->parent;
3802                 }
3803
3804                 /* fall through */
3805             }
3806
3807         case TVHT_ONITEMLABEL:
3808         case TVHT_ONITEMICON:
3809         case TVHT_ONITEMBUTTON:
3810             TREEVIEW_Toggle(infoPtr, wineItem, TRUE);
3811             break;
3812
3813         case TVHT_ONITEMSTATEICON:
3814            if (infoPtr->dwStyle & TVS_CHECKBOXES)
3815                TREEVIEW_ToggleItemState(infoPtr, wineItem);
3816            else
3817                TREEVIEW_Toggle(infoPtr, wineItem, TRUE);
3818            break;
3819         }
3820     }
3821     return TRUE;
3822 }
3823
3824
3825 static LRESULT
3826 TREEVIEW_LButtonDown(TREEVIEW_INFO *infoPtr, LPARAM lParam)
3827 {
3828     HWND hwnd = infoPtr->hwnd;
3829     TVHITTESTINFO ht;
3830     BOOL bTrack;
3831     HTREEITEM tempItem;
3832
3833     /* If Edit control is active - kill it and return.
3834      * The best way to do it is to set focus to itself.
3835      * Edit control subclassed procedure will automatically call
3836      * EndEditLabelNow.
3837      */
3838     if (infoPtr->hwndEdit)
3839     {
3840         SetFocus(hwnd);
3841         return 0;
3842     }
3843
3844     ht.pt.x = SLOWORD(lParam);
3845     ht.pt.y = SHIWORD(lParam);
3846
3847     TREEVIEW_HitTest(infoPtr, &ht);
3848     TRACE("item %d\n", TREEVIEW_GetItemIndex(infoPtr, ht.hItem));
3849
3850     /* update focusedItem and redraw both items */
3851     if(ht.hItem && (ht.flags & TVHT_ONITEM))
3852     {
3853         infoPtr->focusedItem = ht.hItem;
3854         InvalidateRect(hwnd, &(((HTREEITEM)(ht.hItem))->rect), TRUE);
3855
3856         if(infoPtr->selectedItem)
3857             InvalidateRect(hwnd, &(infoPtr->selectedItem->rect), TRUE);
3858     }
3859
3860     bTrack = (ht.flags & TVHT_ONITEM)
3861         && !(infoPtr->dwStyle & TVS_DISABLEDRAGDROP);
3862
3863     /* Send NM_CLICK right away */
3864     if (!bTrack)
3865         if (TREEVIEW_SendSimpleNotify(infoPtr, NM_CLICK))
3866             goto setfocus;
3867
3868     if (ht.flags & TVHT_ONITEMBUTTON)
3869     {
3870         TREEVIEW_Toggle(infoPtr, ht.hItem, TRUE);
3871         goto setfocus;
3872     }
3873     else if (bTrack)
3874     {   /* if TREEVIEW_TrackMouse == 1 dragging occurred and the cursor left the dragged item's rectangle */
3875         if (TREEVIEW_TrackMouse(infoPtr, ht.pt))
3876         {
3877             TREEVIEW_SendTreeviewDnDNotify(infoPtr, TVN_BEGINDRAGA, ht.hItem,
3878                                            ht.pt);
3879             infoPtr->dropItem = ht.hItem;
3880
3881             /* clean up focusedItem as we dragged and won't select this item */
3882             if(infoPtr->focusedItem)
3883             {
3884                 /* refresh the item that was focused */
3885                 tempItem = infoPtr->focusedItem;
3886                 infoPtr->focusedItem = 0;
3887                 InvalidateRect(infoPtr->hwnd, &tempItem->rect, TRUE);
3888
3889                 /* refresh the selected item to return the filled background */
3890                 InvalidateRect(infoPtr->hwnd, &(infoPtr->selectedItem->rect), TRUE);
3891             }
3892
3893             return 0;
3894         }
3895     }
3896
3897     if (TREEVIEW_SendSimpleNotify(infoPtr, NM_CLICK))
3898         goto setfocus;
3899
3900     /* 
3901      * If the style allows editing and the node is already selected 
3902      * and the click occurred on the item label...
3903      */
3904     if ((infoPtr->dwStyle & TVS_EDITLABELS) &&
3905                 (ht.flags & TVHT_ONITEMLABEL) && (infoPtr->selectedItem == ht.hItem))
3906     {
3907         if (infoPtr->Timer & TV_EDIT_TIMER_SET)
3908             KillTimer(hwnd, TV_EDIT_TIMER);
3909
3910         SetTimer(hwnd, TV_EDIT_TIMER, GetDoubleClickTime(), 0);
3911         infoPtr->Timer |= TV_EDIT_TIMER_SET;
3912     }
3913     else if (ht.flags & TVHT_ONITEM) /* select the item if the hit was inside of the icon or text */
3914     {
3915         /*
3916          * if we are TVS_SINGLEEXPAND then we want this single click to
3917          * do a bunch of things.
3918          */
3919         if((infoPtr->dwStyle & TVS_SINGLEEXPAND) &&
3920           (infoPtr->hwndEdit == 0))
3921         {
3922             TREEVIEW_ITEM *SelItem;
3923
3924             /*
3925              * Send the notification
3926              */
3927             TREEVIEW_SendTreeviewNotify(infoPtr, TVN_SINGLEEXPAND, TVIF_HANDLE | TVIF_PARAM,
3928                                 0, ht.hItem, 0);
3929
3930             /*
3931              * Close the previous selection all the way to the root
3932              * as long as the new selection is not a child
3933              */
3934             if((infoPtr->selectedItem)
3935                 && (infoPtr->selectedItem != ht.hItem))
3936             {
3937                 BOOL closeit = TRUE;
3938                 SelItem = ht.hItem;
3939
3940                 /* determine if the hitItem is a child of the currently selected item */
3941                 while(closeit && SelItem && TREEVIEW_ValidItem(infoPtr, SelItem) && (SelItem != infoPtr->root))
3942                 {
3943                     closeit = (SelItem != infoPtr->selectedItem);
3944                     SelItem = SelItem->parent;
3945                 }
3946
3947                 if(closeit)
3948                 {
3949                     if(TREEVIEW_ValidItem(infoPtr, infoPtr->selectedItem))
3950                         SelItem = infoPtr->selectedItem;
3951
3952                     while(SelItem && (SelItem != ht.hItem) && TREEVIEW_ValidItem(infoPtr, SelItem) && (SelItem != infoPtr->root))
3953                     {
3954                         TREEVIEW_Collapse(infoPtr, SelItem, FALSE, FALSE);
3955                         SelItem = SelItem->parent;
3956                     }
3957                 }
3958             }
3959
3960             /*
3961              * Expand the current item
3962              */
3963             TREEVIEW_Expand(infoPtr, ht.hItem, TVE_TOGGLE, FALSE);
3964         }
3965
3966         /* Select the current item */
3967         TREEVIEW_DoSelectItem(infoPtr, TVGN_CARET, ht.hItem, TVC_BYMOUSE);
3968     }
3969     else if (ht.flags & TVHT_ONITEMSTATEICON)
3970     {
3971         /* TVS_CHECKBOXES requires us to toggle the current state */
3972         if (infoPtr->dwStyle & TVS_CHECKBOXES)
3973             TREEVIEW_ToggleItemState(infoPtr, ht.hItem);
3974     }
3975
3976   setfocus:
3977     SetFocus(hwnd);
3978     return 0;
3979 }
3980
3981
3982 static LRESULT
3983 TREEVIEW_RButtonDown(TREEVIEW_INFO *infoPtr, LPARAM lParam)
3984 {
3985     TVHITTESTINFO ht;
3986
3987     if (infoPtr->hwndEdit)
3988     {
3989         SetFocus(infoPtr->hwnd);
3990         return 0;
3991     }
3992
3993     ht.pt.x = SLOWORD(lParam);
3994     ht.pt.y = SHIWORD(lParam);
3995
3996     TREEVIEW_HitTest(infoPtr, &ht);
3997
3998     if (TREEVIEW_TrackMouse(infoPtr, ht.pt))
3999     {
4000         if (ht.hItem)
4001         {
4002             TREEVIEW_SendTreeviewDnDNotify(infoPtr, TVN_BEGINRDRAGA, ht.hItem,
4003                                            ht.pt);
4004             infoPtr->dropItem = ht.hItem;
4005         }
4006     }
4007     else
4008     {
4009         SetFocus(infoPtr->hwnd);
4010         TREEVIEW_SendSimpleNotify(infoPtr, NM_RCLICK);
4011     }
4012
4013     return 0;
4014 }
4015
4016 static LRESULT
4017 TREEVIEW_RButtonUp(TREEVIEW_INFO *infoPtr, LPPOINT pPt)
4018 {
4019     return 0;
4020 }
4021
4022
4023 static LRESULT
4024 TREEVIEW_CreateDragImage(TREEVIEW_INFO *infoPtr, WPARAM wParam, LPARAM lParam)
4025 {
4026     TREEVIEW_ITEM *dragItem = (HTREEITEM)lParam;
4027     INT cx, cy;
4028     HDC hdc, htopdc;
4029     HWND hwtop;
4030     HBITMAP hbmp, hOldbmp;
4031     SIZE size;
4032     RECT rc;
4033     HFONT hOldFont;
4034
4035     TRACE("\n");
4036
4037     if (!(infoPtr->himlNormal))
4038         return 0;
4039
4040     if (!dragItem || !TREEVIEW_ValidItem(infoPtr, dragItem))
4041         return 0;
4042
4043     TREEVIEW_UpdateDispInfo(infoPtr, dragItem, TVIF_TEXT);
4044
4045     hwtop = GetDesktopWindow();
4046     htopdc = GetDC(hwtop);
4047     hdc = CreateCompatibleDC(htopdc);
4048
4049     hOldFont = SelectObject(hdc, infoPtr->hFont);
4050     GetTextExtentPoint32A(hdc, dragItem->pszText, lstrlenA(dragItem->pszText),
4051                           &size);
4052     TRACE("%ld %ld %s %d\n", size.cx, size.cy, dragItem->pszText,
4053           lstrlenA(dragItem->pszText));
4054     hbmp = CreateCompatibleBitmap(htopdc, size.cx, size.cy);
4055     hOldbmp = SelectObject(hdc, hbmp);
4056
4057     ImageList_GetIconSize(infoPtr->himlNormal, &cx, &cy);
4058     size.cx += cx;
4059     if (cy > size.cy)
4060         size.cy = cy;
4061
4062     infoPtr->dragList = ImageList_Create(size.cx, size.cy, ILC_COLOR, 10, 10);
4063     ImageList_Draw(infoPtr->himlNormal, dragItem->iImage, hdc, 0, 0,
4064                    ILD_NORMAL);
4065
4066 /*
4067  ImageList_GetImageInfo (infoPtr->himlNormal, dragItem->hItem, &iminfo);
4068  ImageList_AddMasked (infoPtr->dragList, iminfo.hbmImage, CLR_DEFAULT);
4069 */
4070
4071 /* draw item text */
4072
4073     SetRect(&rc, cx, 0, size.cx, size.cy);
4074     DrawTextA(hdc, dragItem->pszText, lstrlenA(dragItem->pszText), &rc,
4075               DT_LEFT);
4076     SelectObject(hdc, hOldFont);
4077     SelectObject(hdc, hOldbmp);
4078
4079     ImageList_Add(infoPtr->dragList, hbmp, 0);
4080
4081     DeleteDC(hdc);
4082     DeleteObject(hbmp);
4083     ReleaseDC(hwtop, htopdc);
4084
4085     return (LRESULT)infoPtr->dragList;
4086 }
4087
4088 /* Selection ************************************************************/
4089
4090 static LRESULT
4091 TREEVIEW_DoSelectItem(TREEVIEW_INFO *infoPtr, INT action, HTREEITEM newSelect,
4092                       INT cause)
4093 {
4094     TREEVIEW_ITEM *prevSelect;
4095     RECT rcFocused;
4096
4097     assert(newSelect == NULL || TREEVIEW_ValidItem(infoPtr, newSelect));
4098
4099     TRACE("Entering item %p (%s), flag %x, cause %x, state %d\n",
4100           newSelect, TREEVIEW_ItemName(newSelect), action, cause,
4101           newSelect ? newSelect->state : 0);
4102
4103     /* reset and redraw focusedItem if focusedItem was set so we don't */
4104     /* have to worry about the previously focused item when we set a new one */
4105     if(infoPtr->focusedItem)
4106     {
4107         rcFocused = (infoPtr->focusedItem)->rect;
4108         infoPtr->focusedItem = 0;
4109         InvalidateRect(infoPtr->hwnd, &rcFocused, TRUE);
4110     }
4111
4112     switch (action)
4113     {
4114     case TVGN_CARET:
4115         prevSelect = infoPtr->selectedItem;
4116
4117         if (prevSelect == newSelect)
4118             return FALSE;
4119
4120         if (TREEVIEW_SendTreeviewNotify(infoPtr,
4121                                         TVN_SELCHANGINGA,
4122                                         cause,
4123                                         TVIF_HANDLE | TVIF_STATE | TVIF_PARAM,
4124                                         prevSelect,
4125                                         newSelect))
4126             return FALSE;
4127
4128         if (prevSelect)
4129             prevSelect->state &= ~TVIS_SELECTED;
4130         if (newSelect)
4131             newSelect->state |= TVIS_SELECTED;
4132
4133         infoPtr->selectedItem = newSelect;
4134
4135         TREEVIEW_EnsureVisible(infoPtr, infoPtr->selectedItem, FALSE);
4136
4137         TREEVIEW_SendTreeviewNotify(infoPtr,
4138                                     TVN_SELCHANGEDA,
4139                                     cause,
4140                                     TVIF_HANDLE | TVIF_STATE | TVIF_PARAM,
4141                                     prevSelect,
4142                                     newSelect);
4143         TREEVIEW_Invalidate(infoPtr, prevSelect);
4144         TREEVIEW_Invalidate(infoPtr, newSelect);
4145         break;
4146
4147     case TVGN_DROPHILITE:
4148         prevSelect = infoPtr->dropItem;
4149
4150         if (prevSelect)
4151             prevSelect->state &= ~TVIS_DROPHILITED;
4152
4153         infoPtr->dropItem = newSelect;
4154
4155         if (newSelect)
4156             newSelect->state |= TVIS_DROPHILITED;
4157
4158         TREEVIEW_Invalidate(infoPtr, prevSelect);
4159         TREEVIEW_Invalidate(infoPtr, newSelect);
4160         break;
4161
4162     case TVGN_FIRSTVISIBLE:
4163         TREEVIEW_EnsureVisible(infoPtr, newSelect, FALSE);
4164         TREEVIEW_SetFirstVisible(infoPtr, newSelect, TRUE);
4165         TREEVIEW_Invalidate(infoPtr, NULL);
4166         break;
4167     }
4168
4169     TRACE("Leaving state %d\n", newSelect ? newSelect->state : 0);
4170     return TRUE;
4171 }
4172
4173 /* FIXME: handle NM_KILLFOCUS etc */
4174 static LRESULT
4175 TREEVIEW_SelectItem(TREEVIEW_INFO *infoPtr, INT wParam, HTREEITEM item)
4176 {
4177     if (item != NULL && !TREEVIEW_ValidItem(infoPtr, item))
4178         return FALSE;
4179
4180     TRACE("%p (%s) %d\n", item, TREEVIEW_ItemName(item), wParam);
4181
4182     if (!TREEVIEW_DoSelectItem(infoPtr, wParam, item, TVC_UNKNOWN))
4183         return FALSE;
4184
4185     return TRUE;
4186 }
4187
4188 /*************************************************************************
4189  *              TREEVIEW_ProcessLetterKeys
4190  *
4191  *  Processes keyboard messages generated by pressing the letter keys 
4192  *  on the keyboard.
4193  *  What this does is perform a case insensitive search from the 
4194  *  current position with the following quirks:
4195  *  - If two chars or more are pressed in quick succession we search 
4196  *    for the corresponding string (e.g. 'abc').
4197  *  - If there is a delay we wipe away the current search string and 
4198  *    restart with just that char.
4199  *  - If the user keeps pressing the same character, whether slowly or 
4200  *    fast, so that the search string is entirely composed of this 
4201  *    character ('aaaaa' for instance), then we search for first item 
4202  *    that starting with that character.
4203  *  - If the user types the above character in quick succession, then 
4204  *    we must also search for the corresponding string ('aaaaa'), and 
4205  *    go to that string if there is a match.
4206  *
4207  * RETURNS
4208  *
4209  *  Zero.
4210  *
4211  * BUGS
4212  *
4213  *  - The current implementation has a list of characters it will 
4214  *    accept and it ignores averything else. In particular it will 
4215  *    ignore accentuated characters which seems to match what 
4216  *    Windows does. But I'm not sure it makes sense to follow 
4217  *    Windows there.
4218  *  - We don't sound a beep when the search fails.
4219  *  - The search should start from the focused item, not from the selected 
4220  *    item. One reason for this is to allow for multiple selections in trees.
4221  *    But currently infoPtr->focusedItem does not seem very usable.
4222  *
4223  * SEE ALSO
4224  *
4225  *  TREEVIEW_ProcessLetterKeys
4226  */
4227 static INT TREEVIEW_ProcessLetterKeys(
4228     HWND hwnd, /* handle to the window */
4229     WPARAM charCode, /* the character code, the actual character */
4230     LPARAM keyData /* key data */
4231     )
4232 {
4233     TREEVIEW_INFO *infoPtr;
4234     HTREEITEM nItem;
4235     HTREEITEM endidx,idx;
4236     TVITEMEXA item;
4237     CHAR buffer[MAX_PATH];
4238     DWORD timestamp,elapsed;
4239
4240     /* simple parameter checking */
4241     if (!hwnd || !charCode || !keyData)
4242         return 0;
4243
4244     infoPtr=(TREEVIEW_INFO*)GetWindowLongA(hwnd, 0);
4245     if (!infoPtr)
4246         return 0;
4247
4248     /* only allow the valid WM_CHARs through */
4249     if (!isalnum(charCode) &&
4250         charCode != '.' && charCode != '`' && charCode != '!' &&
4251         charCode != '@' && charCode != '#' && charCode != '$' &&
4252         charCode != '%' && charCode != '^' && charCode != '&' &&
4253         charCode != '*' && charCode != '(' && charCode != ')' &&
4254         charCode != '-' && charCode != '_' && charCode != '+' &&
4255         charCode != '=' && charCode != '\\'&& charCode != ']' &&
4256         charCode != '}' && charCode != '[' && charCode != '{' &&
4257         charCode != '/' && charCode != '?' && charCode != '>' &&
4258         charCode != '<' && charCode != ',' && charCode != '~')
4259         return 0;
4260
4261     /* compute how much time elapsed since last keypress */
4262     timestamp = GetTickCount();
4263     if (timestamp > infoPtr->lastKeyPressTimestamp) {
4264         elapsed=timestamp-infoPtr->lastKeyPressTimestamp;
4265     } else {
4266         elapsed=infoPtr->lastKeyPressTimestamp-timestamp;
4267     }
4268
4269     /* update the search parameters */
4270     infoPtr->lastKeyPressTimestamp=timestamp;
4271     if (elapsed < KEY_DELAY) {
4272         if (infoPtr->nSearchParamLength < sizeof(infoPtr->szSearchParam)) {
4273             infoPtr->szSearchParam[infoPtr->nSearchParamLength++]=charCode;
4274         }
4275         if (infoPtr->charCode != charCode) {
4276             infoPtr->charCode=charCode=0;
4277         }
4278     } else {
4279         infoPtr->charCode=charCode;
4280         infoPtr->szSearchParam[0]=charCode;
4281         infoPtr->nSearchParamLength=1;
4282         /* Redundant with the 1 char string */
4283         charCode=0;
4284     }
4285
4286     /* and search from the current position */
4287     nItem=NULL;
4288     if (infoPtr->selectedItem != NULL) {
4289         endidx=infoPtr->selectedItem;
4290         /* if looking for single character match,
4291          * then we must always move forward
4292          */
4293         if (infoPtr->nSearchParamLength == 1)
4294             idx=TREEVIEW_GetNextListItem(infoPtr,endidx);
4295         else
4296             idx=endidx;
4297     } else {
4298         endidx=NULL;
4299         idx=infoPtr->root->firstChild;
4300     }
4301     do {
4302         if (idx == NULL) {
4303             if (endidx == NULL)
4304                 break;
4305             idx=infoPtr->root->firstChild;
4306         }
4307
4308         /* get item */
4309         ZeroMemory(&item, sizeof(item));
4310         item.mask = TVIF_TEXT;
4311         item.hItem = idx;
4312         item.pszText = buffer;
4313         item.cchTextMax = sizeof(buffer);
4314         TREEVIEW_GetItemA( infoPtr, &item );
4315
4316         /* check for a match */
4317         if (strncasecmp(item.pszText,infoPtr->szSearchParam,infoPtr->nSearchParamLength) == 0) {
4318             nItem=idx;
4319             break;
4320         } else if ( (charCode != 0) && (nItem == NULL) &&
4321                     (nItem != infoPtr->selectedItem) &&
4322                     (strncasecmp(item.pszText,infoPtr->szSearchParam,1) == 0) ) {
4323             /* This would work but we must keep looking for a longer match */
4324             nItem=idx;
4325         }
4326         idx=TREEVIEW_GetNextListItem(infoPtr,idx);
4327     } while (idx != endidx);
4328
4329     if (nItem != NULL) {
4330         if (TREEVIEW_DoSelectItem(infoPtr, TVGN_CARET, nItem, TVC_BYKEYBOARD)) {
4331             TREEVIEW_EnsureVisible(infoPtr, nItem, FALSE);
4332         }
4333     }
4334
4335     return 0;
4336 }
4337
4338 /* Scrolling ************************************************************/
4339
4340 static LRESULT
4341 TREEVIEW_EnsureVisible(TREEVIEW_INFO *infoPtr, HTREEITEM item, BOOL bHScroll)
4342 {
4343     HTREEITEM newFirstVisible = NULL;
4344     int visible_pos;
4345
4346     if (!TREEVIEW_ValidItem(infoPtr, item))
4347         return FALSE;
4348
4349     if (!ISVISIBLE(item))
4350     {
4351         /* Expand parents as necessary. */
4352         HTREEITEM parent;
4353
4354         /* see if we are trying to ensure that root is vislble */
4355         if((item != infoPtr->root) && TREEVIEW_ValidItem(infoPtr, item))
4356           parent = item->parent;
4357         else
4358           parent = item; /* this item is the topmost item */
4359
4360         while (parent != infoPtr->root)
4361         {
4362             if (!(parent->state & TVIS_EXPANDED))
4363                 TREEVIEW_Expand(infoPtr, parent, FALSE, FALSE);
4364
4365             parent = parent->parent;
4366         }
4367     }
4368
4369     TRACE("%p (%s) %ld - %ld\n", item, TREEVIEW_ItemName(item), item->visibleOrder,
4370           infoPtr->firstVisible->visibleOrder);
4371
4372     visible_pos = item->visibleOrder - infoPtr->firstVisible->visibleOrder;
4373
4374     if (visible_pos < 0)
4375     {
4376         /* item is before the start of the list: put it at the top. */
4377         newFirstVisible = item;
4378     }
4379     else if (visible_pos >= TREEVIEW_GetVisibleCount(infoPtr)
4380              /* Sometimes, before we are displayed, GVC is 0, causing us to
4381               * spuriously scroll up. */
4382              && visible_pos > 0)
4383     {
4384         /* item is past the end of the list. */
4385         int scroll = visible_pos - TREEVIEW_GetVisibleCount(infoPtr);
4386
4387         newFirstVisible = TREEVIEW_GetListItem(infoPtr, infoPtr->firstVisible,
4388                                                scroll + 1);
4389     }
4390
4391     if (bHScroll)
4392     {
4393         /* Scroll window so item's text is visible as much as possible */
4394         /* Calculation of amount of extra space is taken from EditLabel code */
4395         INT pos, x;
4396         TEXTMETRICA textMetric;
4397         HDC hdc = GetWindowDC(infoPtr->hwnd);
4398
4399         x = item->textWidth;
4400
4401         GetTextMetricsA(hdc, &textMetric);
4402         ReleaseDC(infoPtr->hwnd, hdc);
4403
4404         x += (textMetric.tmMaxCharWidth * 2);
4405         x = max(x, textMetric.tmMaxCharWidth * 3);
4406
4407         if (item->textOffset < 0)
4408            pos = item->textOffset;
4409         else if (item->textOffset + x > infoPtr->clientWidth)
4410         {
4411            if (x > infoPtr->clientWidth)
4412               pos = item->textOffset;
4413            else
4414               pos = item->textOffset + x - infoPtr->clientWidth;
4415         }
4416         else
4417            pos = 0;
4418
4419         TREEVIEW_HScroll(infoPtr, MAKEWPARAM(SB_THUMBPOSITION, infoPtr->scrollX + pos));
4420     }
4421
4422     if (newFirstVisible != NULL && newFirstVisible != infoPtr->firstVisible)
4423     {
4424         TREEVIEW_SetFirstVisible(infoPtr, newFirstVisible, TRUE);
4425
4426         return TRUE;
4427     }
4428
4429     return FALSE;
4430 }
4431
4432 static VOID
4433 TREEVIEW_SetFirstVisible(TREEVIEW_INFO *infoPtr,
4434                          TREEVIEW_ITEM *newFirstVisible,
4435                          BOOL bUpdateScrollPos)
4436 {
4437     int gap_size;
4438
4439     TRACE("%p: %s\n", newFirstVisible, TREEVIEW_ItemName(newFirstVisible));
4440
4441     if (newFirstVisible != NULL)
4442     {
4443         /* Prevent an empty gap from appearing at the bottom... */
4444         gap_size = TREEVIEW_GetVisibleCount(infoPtr)
4445             - infoPtr->maxVisibleOrder + newFirstVisible->visibleOrder;
4446
4447         if (gap_size > 0)
4448         {
4449             newFirstVisible = TREEVIEW_GetListItem(infoPtr, newFirstVisible,
4450                                                    -gap_size);
4451
4452             /* ... unless we just don't have enough items. */
4453             if (newFirstVisible == NULL)
4454                 newFirstVisible = infoPtr->root->firstChild;
4455         }
4456     }
4457
4458     if (infoPtr->firstVisible != newFirstVisible)
4459     {
4460         if (infoPtr->firstVisible == NULL || newFirstVisible == NULL)
4461         {
4462             infoPtr->firstVisible = newFirstVisible;
4463             TREEVIEW_Invalidate(infoPtr, NULL);
4464         }
4465         else
4466         {
4467             TREEVIEW_ITEM *item;
4468             int scroll = infoPtr->uItemHeight *
4469                          (infoPtr->firstVisible->visibleOrder
4470                           - newFirstVisible->visibleOrder);
4471
4472             infoPtr->firstVisible = newFirstVisible;
4473
4474             for (item = infoPtr->root->firstChild; item != NULL;
4475                  item = TREEVIEW_GetNextListItem(infoPtr, item))
4476             {
4477                item->rect.top += scroll;
4478                item->rect.bottom += scroll;
4479             }
4480
4481             if (bUpdateScrollPos)
4482                 SetScrollPos(infoPtr->hwnd, SB_VERT,
4483                               newFirstVisible->visibleOrder, TRUE);
4484
4485             ScrollWindow(infoPtr->hwnd, 0, scroll, NULL, NULL);
4486             UpdateWindow(infoPtr->hwnd);
4487         }
4488     }
4489 }
4490
4491 /************************************************************************
4492  * VScroll is always in units of visible items. i.e. we always have a
4493  * visible item aligned to the top of the control. (Unless we have no
4494  * items at all.)
4495  */
4496 static LRESULT
4497 TREEVIEW_VScroll(TREEVIEW_INFO *infoPtr, WPARAM wParam)
4498 {
4499     TREEVIEW_ITEM *oldFirstVisible = infoPtr->firstVisible;
4500     TREEVIEW_ITEM *newFirstVisible = NULL;
4501
4502     int nScrollCode = LOWORD(wParam);
4503
4504     TRACE("wp %x\n", wParam);
4505
4506     if (!(infoPtr->uInternalStatus & TV_VSCROLL))
4507         return 0;
4508
4509     if (infoPtr->hwndEdit)
4510         SetFocus(infoPtr->hwnd);
4511
4512     if (!oldFirstVisible)
4513     {
4514         assert(infoPtr->root->firstChild == NULL);
4515         return 0;
4516     }
4517
4518     switch (nScrollCode)
4519     {
4520     case SB_TOP:
4521         newFirstVisible = infoPtr->root->firstChild;
4522         break;
4523
4524     case SB_BOTTOM:
4525         newFirstVisible = TREEVIEW_GetLastListItem(infoPtr, infoPtr->root);
4526         break;
4527
4528     case SB_LINEUP:
4529         newFirstVisible = TREEVIEW_GetPrevListItem(infoPtr, oldFirstVisible);
4530         break;
4531
4532     case SB_LINEDOWN:
4533         newFirstVisible = TREEVIEW_GetNextListItem(infoPtr, oldFirstVisible);
4534         break;
4535
4536     case SB_PAGEUP:
4537         newFirstVisible = TREEVIEW_GetListItem(infoPtr, oldFirstVisible,
4538                                                -max(1, TREEVIEW_GetVisibleCount(infoPtr)));
4539         break;
4540
4541     case SB_PAGEDOWN:
4542         newFirstVisible = TREEVIEW_GetListItem(infoPtr, oldFirstVisible,
4543                                                max(1, TREEVIEW_GetVisibleCount(infoPtr)));
4544         break;
4545
4546     case SB_THUMBTRACK:
4547     case SB_THUMBPOSITION:
4548         newFirstVisible = TREEVIEW_GetListItem(infoPtr,
4549                                                infoPtr->root->firstChild,
4550                                                (LONG)(SHORT)HIWORD(wParam));
4551         break;
4552
4553     case SB_ENDSCROLL:
4554         return 0;
4555     }
4556
4557     if (newFirstVisible != NULL)
4558     {
4559         if (newFirstVisible != oldFirstVisible)
4560             TREEVIEW_SetFirstVisible(infoPtr, newFirstVisible,
4561                                   nScrollCode != SB_THUMBTRACK);
4562         else if (nScrollCode == SB_THUMBPOSITION)
4563             SetScrollPos(infoPtr->hwnd, SB_VERT,
4564                          newFirstVisible->visibleOrder, TRUE);
4565     }
4566
4567     return 0;
4568 }
4569
4570 static LRESULT
4571 TREEVIEW_HScroll(TREEVIEW_INFO *infoPtr, WPARAM wParam)
4572 {
4573     int maxWidth;
4574     int scrollX = infoPtr->scrollX;
4575     int nScrollCode = LOWORD(wParam);
4576
4577     TRACE("wp %x\n", wParam);
4578
4579     if (!(infoPtr->uInternalStatus & TV_HSCROLL))
4580         return FALSE;
4581
4582     if (infoPtr->hwndEdit)
4583         SetFocus(infoPtr->hwnd);
4584
4585     maxWidth = infoPtr->treeWidth - infoPtr->clientWidth;
4586     /* shall never occur */
4587     if (maxWidth <= 0)
4588     {
4589        scrollX = 0;
4590        goto scroll;
4591     }
4592
4593     switch (nScrollCode)
4594     {
4595     case SB_LINELEFT:
4596         scrollX -= infoPtr->uItemHeight;
4597         break;
4598     case SB_LINERIGHT:
4599         scrollX += infoPtr->uItemHeight;
4600         break;
4601     case SB_PAGELEFT:
4602         scrollX -= infoPtr->clientWidth;
4603         break;
4604     case SB_PAGERIGHT:
4605         scrollX += infoPtr->clientWidth;
4606         break;
4607
4608     case SB_THUMBTRACK:
4609     case SB_THUMBPOSITION:
4610         scrollX = (int)(SHORT)HIWORD(wParam);
4611         break;
4612
4613     case SB_ENDSCROLL:
4614        return 0;
4615     }
4616
4617     if (scrollX > maxWidth)
4618         scrollX = maxWidth;
4619     else if (scrollX < 0)
4620         scrollX = 0;
4621
4622 scroll:
4623     if (scrollX != infoPtr->scrollX)
4624     {
4625         TREEVIEW_ITEM *item;
4626         LONG scroll_pixels = infoPtr->scrollX - scrollX;
4627         
4628         for (item = infoPtr->root->firstChild; item != NULL;
4629              item = TREEVIEW_GetNextListItem(infoPtr, item))
4630         {
4631            item->linesOffset += scroll_pixels;
4632            item->stateOffset += scroll_pixels;
4633            item->imageOffset += scroll_pixels;
4634            item->textOffset  += scroll_pixels;
4635         }
4636
4637         ScrollWindow(infoPtr->hwnd, scroll_pixels, 0, NULL, NULL);
4638         infoPtr->scrollX = scrollX;
4639         UpdateWindow(infoPtr->hwnd);
4640     }
4641
4642     if (nScrollCode != SB_THUMBTRACK)
4643        SetScrollPos(infoPtr->hwnd, SB_HORZ, scrollX, TRUE);
4644
4645     return 0;
4646 }
4647
4648 static LRESULT
4649 TREEVIEW_MouseWheel(TREEVIEW_INFO *infoPtr, WPARAM wParam)
4650 {
4651     short gcWheelDelta;
4652     UINT pulScrollLines = 3;
4653
4654     if (infoPtr->firstVisible == NULL)
4655         return TRUE;
4656
4657     SystemParametersInfoW(SPI_GETWHEELSCROLLLINES, 0, &pulScrollLines, 0);
4658
4659     gcWheelDelta = -(short)HIWORD(wParam);
4660     pulScrollLines *= (gcWheelDelta / WHEEL_DELTA);
4661
4662     if (abs(gcWheelDelta) >= WHEEL_DELTA && pulScrollLines)
4663     {
4664         int newDy = infoPtr->firstVisible->visibleOrder + pulScrollLines;
4665         int maxDy = infoPtr->maxVisibleOrder;
4666
4667         if (newDy > maxDy)
4668             newDy = maxDy;
4669
4670         if (newDy < 0)
4671             newDy = 0;
4672
4673         TREEVIEW_VScroll(infoPtr, MAKEWPARAM(SB_THUMBPOSITION, newDy));
4674     }
4675     return TRUE;
4676 }
4677
4678 /* Create/Destroy *******************************************************/
4679
4680 static LRESULT
4681 TREEVIEW_Create(HWND hwnd)
4682
4683     RECT rcClient;
4684     TREEVIEW_INFO *infoPtr;
4685
4686     TRACE("wnd %x, style %lx\n", hwnd, GetWindowLongA(hwnd, GWL_STYLE));
4687
4688     infoPtr = (TREEVIEW_INFO *)COMCTL32_Alloc(sizeof(TREEVIEW_INFO));
4689  
4690     if (infoPtr == NULL)
4691     {
4692         ERR("could not allocate info memory!\n");
4693         return 0;
4694     }
4695
4696     SetWindowLongA(hwnd, 0, (DWORD)infoPtr);
4697
4698     infoPtr->hwnd = hwnd;
4699     infoPtr->dwStyle = GetWindowLongA(hwnd, GWL_STYLE);
4700     infoPtr->uInternalStatus = 0;
4701     infoPtr->Timer = 0;
4702     infoPtr->uNumItems = 0;
4703     infoPtr->cdmode = 0;
4704     infoPtr->uScrollTime = 300; /* milliseconds */
4705     infoPtr->bRedraw = TRUE;
4706
4707     GetClientRect(hwnd, &rcClient);
4708
4709     /* No scroll bars yet. */
4710     infoPtr->clientWidth = rcClient.right;
4711     infoPtr->clientHeight = rcClient.bottom;
4712
4713     infoPtr->treeWidth = 0;
4714     infoPtr->treeHeight = 0;
4715
4716     infoPtr->uIndent = 19;
4717     infoPtr->selectedItem = 0;
4718     infoPtr->focusedItem = 0;
4719     /* hotItem? */
4720     infoPtr->firstVisible = 0;
4721     infoPtr->maxVisibleOrder = 0;
4722     infoPtr->dropItem = 0;
4723     infoPtr->insertMarkItem = 0;
4724     infoPtr->insertBeforeorAfter = 0;
4725     /* dragList */
4726
4727     infoPtr->scrollX = 0;
4728
4729     infoPtr->clrBk = GetSysColor(COLOR_WINDOW);
4730     infoPtr->clrText = -1;      /* use system color */
4731     infoPtr->clrLine = RGB(128, 128, 128);
4732     infoPtr->clrInsertMark = GetSysColor(COLOR_BTNTEXT);
4733
4734     /* hwndToolTip */
4735
4736     infoPtr->hwndEdit = 0;
4737     infoPtr->wpEditOrig = NULL;
4738     infoPtr->bIgnoreEditKillFocus = FALSE;
4739     infoPtr->bLabelChanged = FALSE;
4740
4741     infoPtr->himlNormal = NULL;
4742     infoPtr->himlState = NULL;
4743     infoPtr->normalImageWidth = 0;
4744     infoPtr->normalImageHeight = 0;
4745     infoPtr->stateImageWidth = 0;
4746     infoPtr->stateImageHeight = 0;
4747
4748     infoPtr->items = DPA_Create(16);
4749
4750     infoPtr->hFont = GetStockObject(DEFAULT_GUI_FONT);
4751     infoPtr->hBoldFont = TREEVIEW_CreateBoldFont(infoPtr->hFont);
4752
4753     infoPtr->uItemHeight = TREEVIEW_NaturalHeight(infoPtr);
4754
4755     infoPtr->root = TREEVIEW_AllocateItem(infoPtr);
4756     infoPtr->root->state = TVIS_EXPANDED;
4757     infoPtr->root->iLevel = -1;
4758     infoPtr->root->visibleOrder = -1;
4759
4760     infoPtr->hwndNotify = GetParent(hwnd);
4761 #if 0
4762     infoPtr->bTransparent = ( GetWindowLongA( hwnd, GWL_STYLE) & TBSTYLE_FLAT);
4763 #endif
4764
4765     infoPtr->hwndToolTip = 0;
4766
4767     infoPtr->bUnicode = IsWindowUnicode (hwnd);
4768
4769     /* Determine what type of notify should be issued */
4770     /* sets infoPtr->bNtfUnicode */
4771     TREEVIEW_NotifyFormat(infoPtr, 0, NF_REQUERY); 
4772
4773     if (!(infoPtr->dwStyle & TVS_NOTOOLTIPS))
4774         infoPtr->hwndToolTip = COMCTL32_CreateToolTip(hwnd);
4775
4776     if (infoPtr->dwStyle & TVS_CHECKBOXES)
4777     {
4778         RECT rc;
4779         HBITMAP hbm, hbmOld;
4780         HDC hdc;
4781         int nIndex;
4782
4783         infoPtr->himlState =
4784             ImageList_Create(16, 16, ILC_COLOR | ILC_MASK, 3, 0);
4785
4786         hdc = CreateCompatibleDC(0);
4787         hbm = CreateCompatibleBitmap(hdc, 48, 16);
4788         hbmOld = SelectObject(hdc, hbm);
4789
4790         rc.left  = 0;   rc.top    = 0;
4791         rc.right = 48;  rc.bottom = 16;
4792         FillRect(hdc, &rc, (HBRUSH)(COLOR_WINDOW+1));
4793
4794         rc.left  = 18;   rc.top    = 2;
4795         rc.right = 30;   rc.bottom = 14;
4796         DrawFrameControl(hdc, &rc, DFC_BUTTON,
4797                           DFCS_BUTTONCHECK|DFCS_FLAT);
4798
4799         rc.left  = 34;   rc.right  = 46;
4800         DrawFrameControl(hdc, &rc, DFC_BUTTON,
4801                           DFCS_BUTTONCHECK|DFCS_FLAT|DFCS_CHECKED);
4802
4803         nIndex = ImageList_AddMasked(infoPtr->himlState, hbm,
4804                                       GetSysColor(COLOR_WINDOW));
4805         TRACE("chckbox index %d\n", nIndex);
4806         SelectObject(hdc, hbmOld);
4807         DeleteObject(hbm);
4808         DeleteDC(hdc);
4809
4810         infoPtr->stateImageWidth = 16;
4811         infoPtr->stateImageHeight = 16;
4812     }
4813     return 0;
4814 }
4815
4816
4817 static LRESULT
4818 TREEVIEW_Destroy(TREEVIEW_INFO *infoPtr)
4819 {
4820     TRACE("\n");
4821
4822     TREEVIEW_RemoveTree(infoPtr);
4823
4824     /* tool tip is automatically destroyed: we are its owner */
4825
4826     /* Restore original wndproc */
4827     if (infoPtr->hwndEdit)
4828         SetWindowLongA(infoPtr->hwndEdit, GWL_WNDPROC,
4829                        (LONG)infoPtr->wpEditOrig);
4830
4831     /* Deassociate treeview from the window before doing anything drastic. */
4832     SetWindowLongA(infoPtr->hwnd, 0, (LONG)NULL);
4833
4834     DeleteObject(infoPtr->hBoldFont);
4835     COMCTL32_Free(infoPtr);
4836
4837     return 0;
4838 }
4839
4840 /* Miscellaneous Messages ***********************************************/
4841
4842 static LRESULT
4843 TREEVIEW_ScrollKeyDown(TREEVIEW_INFO *infoPtr, WPARAM key)
4844 {
4845     static const struct
4846     {
4847         unsigned char code;
4848     }
4849     scroll[] =
4850     {
4851 #define SCROLL_ENTRY(dir, code) { ((dir) << 7) | (code) }
4852         SCROLL_ENTRY(SB_VERT, SB_PAGEUP),       /* VK_PRIOR */
4853         SCROLL_ENTRY(SB_VERT, SB_PAGEDOWN),     /* VK_NEXT */
4854         SCROLL_ENTRY(SB_VERT, SB_BOTTOM),       /* VK_END */
4855         SCROLL_ENTRY(SB_VERT, SB_TOP),          /* VK_HOME */
4856         SCROLL_ENTRY(SB_HORZ, SB_LINEUP),       /* VK_LEFT */
4857         SCROLL_ENTRY(SB_VERT, SB_LINEUP),       /* VK_UP */
4858         SCROLL_ENTRY(SB_HORZ, SB_LINEDOWN),     /* VK_RIGHT */
4859         SCROLL_ENTRY(SB_VERT, SB_LINEDOWN)      /* VK_DOWN */
4860 #undef SCROLL_ENTRY
4861     };
4862
4863     if (key >= VK_PRIOR && key <= VK_DOWN)
4864     {
4865         unsigned char code = scroll[key - VK_PRIOR].code;
4866
4867         (((code & (1 << 7)) == (SB_HORZ << 7))
4868          ? TREEVIEW_HScroll
4869          : TREEVIEW_VScroll)(infoPtr, code & 0x7F);
4870     }
4871
4872     return 0;
4873 }
4874
4875 /************************************************************************
4876  *        TREEVIEW_KeyDown
4877  *
4878  * VK_UP       Move selection to the previous non-hidden item.
4879  * VK_DOWN     Move selection to the next non-hidden item.
4880  * VK_HOME     Move selection to the first item.
4881  * VK_END      Move selection to the last item.
4882  * VK_LEFT     If expanded then collapse, otherwise move to parent.
4883  * VK_RIGHT    If collapsed then expand, otherwise move to first child.
4884  * VK_ADD      Expand.
4885  * VK_SUBTRACT Collapse.
4886  * VK_MULTIPLY Expand all.
4887  * VK_PRIOR    Move up GetVisibleCount items.
4888  * VK_NEXT     Move down GetVisibleCount items.
4889  * VK_BACK     Move to parent.
4890  * CTRL-Left,Right,Up,Down,PgUp,PgDown,Home,End: Scroll without changing selection
4891  */
4892 static LRESULT
4893 TREEVIEW_KeyDown(TREEVIEW_INFO *infoPtr, WPARAM wParam)
4894 {
4895     /* If it is non-NULL and different, it will be selected and visible. */
4896     TREEVIEW_ITEM *newSelection = NULL;
4897
4898     TREEVIEW_ITEM *prevItem = infoPtr->selectedItem;
4899
4900     TRACE("%x\n", wParam);
4901
4902     if (prevItem == NULL)
4903         return FALSE;
4904
4905     if (GetAsyncKeyState(VK_CONTROL) & 0x8000)
4906         return TREEVIEW_ScrollKeyDown(infoPtr, wParam);
4907
4908     switch (wParam)
4909     {
4910     case VK_UP:
4911         newSelection = TREEVIEW_GetPrevListItem(infoPtr, prevItem);
4912         if (!newSelection)
4913             newSelection = infoPtr->root->firstChild;
4914         break;
4915
4916     case VK_DOWN:
4917         newSelection = TREEVIEW_GetNextListItem(infoPtr, prevItem);
4918         break;
4919
4920     case VK_HOME:
4921         newSelection = infoPtr->root->firstChild;
4922         break;
4923
4924     case VK_END:
4925         newSelection = TREEVIEW_GetLastListItem(infoPtr, infoPtr->root);
4926         break;
4927
4928     case VK_LEFT:
4929         if (prevItem->state & TVIS_EXPANDED)
4930         {
4931             TREEVIEW_Collapse(infoPtr, prevItem, FALSE, TRUE);
4932         }
4933         else if (prevItem->parent != infoPtr->root)
4934         {
4935             newSelection = prevItem->parent;
4936         }
4937         break;
4938
4939     case VK_RIGHT:
4940         if (TREEVIEW_HasChildren(infoPtr, prevItem))
4941         {
4942             if (!(prevItem->state & TVIS_EXPANDED))
4943                 TREEVIEW_Expand(infoPtr, prevItem, FALSE, TRUE);
4944             else
4945             {
4946                 newSelection = prevItem->firstChild;
4947             }
4948         }
4949
4950         break;
4951
4952     case VK_MULTIPLY:
4953         TREEVIEW_ExpandAll(infoPtr, prevItem);
4954         break;
4955
4956     case VK_ADD:
4957         if (!(prevItem->state & TVIS_EXPANDED))
4958             TREEVIEW_Expand(infoPtr, prevItem, FALSE, TRUE);
4959         break;
4960
4961     case VK_SUBTRACT:
4962         if (prevItem->state & TVIS_EXPANDED)
4963             TREEVIEW_Collapse(infoPtr, prevItem, FALSE, TRUE);
4964         break;
4965
4966     case VK_PRIOR:
4967         newSelection
4968             = TREEVIEW_GetListItem(infoPtr, prevItem,
4969                                    -TREEVIEW_GetVisibleCount(infoPtr));
4970         break;
4971
4972     case VK_NEXT:
4973         newSelection
4974             = TREEVIEW_GetListItem(infoPtr, prevItem,
4975                                    TREEVIEW_GetVisibleCount(infoPtr));
4976         break;
4977
4978     case VK_BACK:
4979         newSelection = prevItem->parent;
4980         if (newSelection == infoPtr->root)
4981             newSelection = NULL;
4982         break;
4983
4984     case VK_SPACE:
4985         if (infoPtr->dwStyle & TVS_CHECKBOXES)
4986             TREEVIEW_ToggleItemState(infoPtr, prevItem);
4987         break;
4988     }
4989
4990     if (newSelection && newSelection != prevItem)
4991     {
4992         if (TREEVIEW_DoSelectItem(infoPtr, TVGN_CARET, newSelection,
4993                                   TVC_BYKEYBOARD))
4994         {
4995             TREEVIEW_EnsureVisible(infoPtr, newSelection, FALSE);
4996         }
4997     }
4998
4999     return FALSE;
5000 }
5001
5002 static LRESULT
5003 TREEVIEW_Notify(TREEVIEW_INFO *infoPtr, WPARAM wParam, LPARAM lParam)
5004 {
5005     LPNMHDR lpnmh = (LPNMHDR)lParam;
5006
5007     if (lpnmh->code == PGN_CALCSIZE) {
5008         LPNMPGCALCSIZE lppgc = (LPNMPGCALCSIZE)lParam;
5009
5010         if (lppgc->dwFlag == PGF_CALCWIDTH) {
5011             lppgc->iWidth = infoPtr->treeWidth;
5012             TRACE("got PGN_CALCSIZE, returning horz size = %ld, client=%ld\n", 
5013                   infoPtr->treeWidth, infoPtr->clientWidth);
5014         }
5015         else {
5016             lppgc->iHeight = infoPtr->treeHeight;
5017             TRACE("got PGN_CALCSIZE, returning vert size = %ld, client=%ld\n", 
5018                   infoPtr->treeHeight, infoPtr->clientHeight);
5019         }
5020         return 0;
5021     }
5022     return DefWindowProcA(infoPtr->hwnd, WM_NOTIFY, wParam, lParam);
5023 }
5024
5025 static LRESULT
5026 TREEVIEW_NotifyFormat (TREEVIEW_INFO *infoPtr, WPARAM wParam, LPARAM lParam)
5027 {
5028     INT i;
5029
5030     if (lParam == NF_REQUERY) {
5031         i = SendMessageA(GetParent (infoPtr->hwnd),
5032                          WM_NOTIFYFORMAT, infoPtr->hwnd, NF_QUERY);
5033         if ((i < NFR_ANSI) || (i > NFR_UNICODE)) {
5034             ERR("wrong response to WM_NOTIFYFORMAT (%d), assuming ANSI\n",
5035                 i);
5036             i = NFR_ANSI;
5037         }
5038         infoPtr->bNtfUnicode = (i == NFR_UNICODE) ? 1 : 0;
5039         return (LRESULT)i;
5040     }
5041     return (LRESULT)((infoPtr->bNtfUnicode) ? NFR_UNICODE : NFR_ANSI);
5042 }
5043
5044
5045 static LRESULT
5046 TREEVIEW_Size(TREEVIEW_INFO *infoPtr, WPARAM wParam, LPARAM lParam)
5047 {
5048     if (wParam == SIZE_RESTORED)
5049     {
5050         infoPtr->clientWidth  = SLOWORD(lParam);
5051         infoPtr->clientHeight = SHIWORD(lParam);
5052
5053         TREEVIEW_RecalculateVisibleOrder(infoPtr, NULL);
5054         TREEVIEW_SetFirstVisible(infoPtr, infoPtr->firstVisible, TRUE);
5055         TREEVIEW_UpdateScrollBars(infoPtr);
5056     }
5057     else
5058     {
5059         FIXME("WM_SIZE flag %x %lx not handled\n", wParam, lParam);
5060     }
5061
5062     TREEVIEW_Invalidate(infoPtr, NULL);
5063     return 0;
5064 }
5065
5066 static LRESULT
5067 TREEVIEW_StyleChanged(TREEVIEW_INFO *infoPtr, WPARAM wParam, LPARAM lParam)
5068 {
5069     TRACE("(%x %lx)\n", wParam, lParam);
5070
5071     if (wParam == GWL_STYLE)
5072     {
5073        DWORD dwNewStyle = ((LPSTYLESTRUCT)lParam)->styleNew;
5074
5075        /* we have to take special care about tooltips */
5076        if ((infoPtr->dwStyle ^ dwNewStyle) & TVS_NOTOOLTIPS)
5077        {
5078           if (infoPtr->dwStyle & TVS_NOTOOLTIPS)
5079           {
5080               infoPtr->hwndToolTip = COMCTL32_CreateToolTip(infoPtr->hwnd);
5081               TRACE("\n");
5082           }
5083           else
5084           {
5085              DestroyWindow(infoPtr->hwndToolTip);
5086              infoPtr->hwndToolTip = 0;
5087           }
5088        }
5089
5090        infoPtr->dwStyle = dwNewStyle;
5091     }
5092
5093     TREEVIEW_UpdateSubTree(infoPtr, infoPtr->root);
5094     TREEVIEW_UpdateScrollBars(infoPtr);
5095     TREEVIEW_Invalidate(infoPtr, NULL);
5096
5097     return 0;
5098 }
5099
5100 static LRESULT
5101 TREEVIEW_SetFocus(TREEVIEW_INFO *infoPtr)
5102 {
5103     TRACE("\n");
5104
5105     if (!infoPtr->selectedItem)
5106     {
5107         TREEVIEW_DoSelectItem(infoPtr, TVGN_CARET, infoPtr->firstVisible,
5108                               TVC_UNKNOWN);
5109     }
5110
5111     TREEVIEW_SendSimpleNotify(infoPtr, NM_SETFOCUS);
5112     TREEVIEW_Invalidate(infoPtr, infoPtr->selectedItem);
5113     return 0;
5114 }
5115
5116 static LRESULT
5117 TREEVIEW_KillFocus(TREEVIEW_INFO *infoPtr)
5118 {
5119     TRACE("\n");
5120
5121     TREEVIEW_SendSimpleNotify(infoPtr, NM_KILLFOCUS);
5122     TREEVIEW_Invalidate(infoPtr, infoPtr->selectedItem);
5123     return 0;
5124 }
5125
5126
5127 static LRESULT WINAPI
5128 TREEVIEW_WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
5129 {
5130     TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
5131     if (infoPtr) TREEVIEW_VerifyTree(infoPtr);
5132     else
5133     {
5134         if (uMsg == WM_CREATE)
5135             TREEVIEW_Create(hwnd);
5136         else
5137             goto def;
5138     }
5139
5140     switch (uMsg)
5141     {
5142     case TVM_CREATEDRAGIMAGE:
5143         return TREEVIEW_CreateDragImage(infoPtr, wParam, lParam);
5144
5145     case TVM_DELETEITEM:
5146         return TREEVIEW_DeleteItem(infoPtr, (HTREEITEM)lParam);
5147
5148     case TVM_EDITLABELA:
5149         return TREEVIEW_EditLabelA(infoPtr, (HTREEITEM)lParam);
5150
5151     case TVM_EDITLABELW:
5152         FIXME("Unimplemented msg TVM_EDITLABELW\n");
5153         return 0;
5154
5155     case TVM_ENDEDITLABELNOW:
5156         return TREEVIEW_EndEditLabelNow(infoPtr, (BOOL)wParam);
5157
5158     case TVM_ENSUREVISIBLE:
5159         return TREEVIEW_EnsureVisible(infoPtr, (HTREEITEM)lParam, TRUE);
5160
5161     case TVM_EXPAND:
5162         return TREEVIEW_ExpandMsg(infoPtr, (UINT)wParam, (HTREEITEM)lParam);
5163
5164     case TVM_GETBKCOLOR:
5165         return TREEVIEW_GetBkColor(infoPtr);
5166
5167     case TVM_GETCOUNT:
5168         return TREEVIEW_GetCount(infoPtr);
5169
5170     case TVM_GETEDITCONTROL:
5171         return TREEVIEW_GetEditControl(infoPtr);
5172
5173     case TVM_GETIMAGELIST:
5174         return TREEVIEW_GetImageList(infoPtr, wParam);
5175
5176     case TVM_GETINDENT:
5177         return TREEVIEW_GetIndent(infoPtr);
5178
5179     case TVM_GETINSERTMARKCOLOR:
5180         return TREEVIEW_GetInsertMarkColor(infoPtr);
5181
5182     case TVM_GETISEARCHSTRINGA:
5183         FIXME("Unimplemented msg TVM_GETISEARCHSTRINGA\n");
5184         return 0;
5185
5186     case TVM_GETISEARCHSTRINGW:
5187         FIXME("Unimplemented msg TVM_GETISEARCHSTRINGW\n");
5188         return 0;
5189
5190     case TVM_GETITEMA:
5191         return TREEVIEW_GetItemA(infoPtr, (LPTVITEMEXA)lParam);
5192
5193     case TVM_GETITEMW:
5194         return TREEVIEW_GetItemW(infoPtr, (LPTVITEMEXW)lParam);
5195
5196     case TVM_GETITEMHEIGHT:
5197         return TREEVIEW_GetItemHeight(infoPtr);
5198
5199     case TVM_GETITEMRECT:
5200         return TREEVIEW_GetItemRect(infoPtr, (BOOL)wParam, (LPRECT)lParam);
5201
5202     case TVM_GETITEMSTATE:
5203         return TREEVIEW_GetItemState(infoPtr, (HTREEITEM)wParam, (UINT)lParam);
5204
5205     case TVM_GETLINECOLOR:
5206         return TREEVIEW_GetLineColor(infoPtr);
5207
5208     case TVM_GETNEXTITEM:
5209         return TREEVIEW_GetNextItem(infoPtr, (UINT)wParam, (HTREEITEM)lParam);
5210
5211     case TVM_GETSCROLLTIME:
5212         return TREEVIEW_GetScrollTime(infoPtr);
5213
5214     case TVM_GETTEXTCOLOR:
5215         return TREEVIEW_GetTextColor(infoPtr);
5216
5217     case TVM_GETTOOLTIPS:
5218         return TREEVIEW_GetToolTips(infoPtr);
5219
5220     case TVM_GETUNICODEFORMAT:
5221         FIXME("Unimplemented msg TVM_GETUNICODEFORMAT\n");
5222         return 0;
5223
5224     case TVM_GETVISIBLECOUNT:
5225         return TREEVIEW_GetVisibleCount(infoPtr);
5226
5227     case TVM_HITTEST:
5228         return TREEVIEW_HitTest(infoPtr, (LPTVHITTESTINFO)lParam);
5229
5230     case TVM_INSERTITEMA:
5231         return TREEVIEW_InsertItemA(infoPtr, lParam);
5232
5233     case TVM_INSERTITEMW:
5234         return TREEVIEW_InsertItemW(infoPtr, lParam);
5235
5236     case TVM_SELECTITEM:
5237         return TREEVIEW_SelectItem(infoPtr, (INT)wParam, (HTREEITEM)lParam);
5238
5239     case TVM_SETBKCOLOR:
5240         return TREEVIEW_SetBkColor(infoPtr, (COLORREF)lParam);
5241
5242     case TVM_SETIMAGELIST:
5243         return TREEVIEW_SetImageList(infoPtr, wParam, (HIMAGELIST)lParam);
5244
5245     case TVM_SETINDENT:
5246         return TREEVIEW_SetIndent(infoPtr, (UINT)wParam);
5247
5248     case TVM_SETINSERTMARK:
5249         return TREEVIEW_SetInsertMark(infoPtr, (BOOL)wParam, (HTREEITEM)lParam);
5250
5251     case TVM_SETINSERTMARKCOLOR:
5252         return TREEVIEW_SetInsertMarkColor(infoPtr, (COLORREF)lParam);
5253
5254     case TVM_SETITEMA:
5255         return TREEVIEW_SetItemA(infoPtr, (LPTVITEMEXA)lParam);
5256
5257     case TVM_SETITEMW:
5258     return TREEVIEW_SetItemW(infoPtr, (LPTVITEMEXW)lParam);
5259         return 0;
5260
5261     case TVM_SETLINECOLOR:
5262         return TREEVIEW_SetLineColor(infoPtr, (COLORREF)lParam);
5263
5264     case TVM_SETITEMHEIGHT:
5265         return TREEVIEW_SetItemHeight(infoPtr, (INT)(SHORT)wParam);
5266
5267     case TVM_SETSCROLLTIME:
5268         return TREEVIEW_SetScrollTime(infoPtr, (UINT)wParam);
5269
5270     case TVM_SETTEXTCOLOR:
5271         return TREEVIEW_SetTextColor(infoPtr, (COLORREF)lParam);
5272
5273     case TVM_SETTOOLTIPS:
5274         return TREEVIEW_SetToolTips(infoPtr, (HWND)wParam);
5275
5276     case TVM_SETUNICODEFORMAT:
5277         FIXME("Unimplemented msg TVM_SETUNICODEFORMAT\n");
5278         return 0;
5279
5280     case TVM_SORTCHILDREN:
5281         return TREEVIEW_SortChildren(infoPtr, wParam, lParam);
5282
5283     case TVM_SORTCHILDRENCB:
5284         return TREEVIEW_SortChildrenCB(infoPtr, wParam, (LPTVSORTCB)lParam);
5285
5286     case WM_CHAR:
5287         return TREEVIEW_ProcessLetterKeys( hwnd, wParam, lParam );
5288
5289     case WM_COMMAND:
5290         return TREEVIEW_Command(infoPtr, wParam, lParam);
5291
5292     case WM_DESTROY:
5293         return TREEVIEW_Destroy(infoPtr);
5294
5295         /* WM_ENABLE */
5296
5297     case WM_ERASEBKGND:
5298         return TREEVIEW_EraseBackground(infoPtr, (HDC)wParam);
5299
5300     case WM_GETDLGCODE:
5301         return DLGC_WANTARROWS | DLGC_WANTCHARS;
5302
5303     case WM_GETFONT:
5304         return TREEVIEW_GetFont(infoPtr);
5305
5306     case WM_HSCROLL:
5307         return TREEVIEW_HScroll(infoPtr, wParam);
5308
5309     case WM_KEYDOWN:
5310         return TREEVIEW_KeyDown(infoPtr, wParam);
5311
5312     case WM_KILLFOCUS:
5313         return TREEVIEW_KillFocus(infoPtr);
5314
5315     case WM_LBUTTONDBLCLK:
5316         return TREEVIEW_LButtonDoubleClick(infoPtr, lParam);
5317
5318     case WM_LBUTTONDOWN:
5319         return TREEVIEW_LButtonDown(infoPtr, lParam);
5320
5321         /* WM_MBUTTONDOWN */
5322
5323         /* WM_MOUSEMOVE */
5324
5325     case WM_NOTIFY:
5326         return TREEVIEW_Notify(infoPtr, wParam, lParam);
5327
5328     case WM_NOTIFYFORMAT:
5329         return TREEVIEW_NotifyFormat(infoPtr, wParam, lParam);
5330
5331     case WM_PAINT:
5332         return TREEVIEW_Paint(infoPtr, wParam);
5333
5334         /* WM_PRINTCLIENT */
5335
5336     case WM_RBUTTONDOWN:
5337         return TREEVIEW_RButtonDown(infoPtr, lParam);
5338
5339     case WM_SETFOCUS:
5340         return TREEVIEW_SetFocus(infoPtr);
5341
5342     case WM_SETFONT:
5343         return TREEVIEW_SetFont(infoPtr, (HFONT)wParam, (BOOL)lParam);
5344
5345     case WM_SETREDRAW:
5346         return TREEVIEW_SetRedraw(infoPtr, wParam, lParam);
5347
5348     case WM_SIZE:
5349         return TREEVIEW_Size(infoPtr, wParam, lParam);
5350
5351     case WM_STYLECHANGED:
5352         return TREEVIEW_StyleChanged(infoPtr, wParam, lParam);
5353
5354         /* WM_SYSCOLORCHANGE */
5355
5356         /* WM_SYSKEYDOWN */
5357
5358     case WM_TIMER:
5359         return TREEVIEW_HandleTimer(infoPtr, wParam);
5360
5361     case WM_VSCROLL:
5362         return TREEVIEW_VScroll(infoPtr, wParam);
5363
5364         /* WM_WININICHANGE */
5365
5366     case WM_MOUSEWHEEL:
5367         if (wParam & (MK_SHIFT | MK_CONTROL))
5368             goto def;
5369         return TREEVIEW_MouseWheel(infoPtr, wParam);
5370
5371     case WM_DRAWITEM:
5372         TRACE("drawItem\n");
5373         goto def;
5374
5375     default:
5376         /* This mostly catches MFC and Delphi messages. :( */
5377         if (uMsg >= WM_USER)
5378             TRACE("Unknown msg %04x wp=%08x lp=%08lx\n", uMsg, wParam, lParam);
5379 def:
5380         return DefWindowProcA(hwnd, uMsg, wParam, lParam);
5381     }
5382     return 0;
5383 }
5384
5385
5386 /* Class Registration ***************************************************/
5387
5388 VOID
5389 TREEVIEW_Register(void)
5390 {
5391     WNDCLASSA wndClass;
5392
5393     TRACE("\n");
5394
5395     ZeroMemory(&wndClass, sizeof(WNDCLASSA));
5396     wndClass.style = CS_GLOBALCLASS | CS_DBLCLKS;
5397     wndClass.lpfnWndProc = (WNDPROC)TREEVIEW_WindowProc;
5398     wndClass.cbClsExtra = 0;
5399     wndClass.cbWndExtra = sizeof(TREEVIEW_INFO *);
5400
5401     wndClass.hCursor = LoadCursorA(0, IDC_ARROWA);
5402     wndClass.hbrBackground = 0;
5403     wndClass.lpszClassName = WC_TREEVIEWA;
5404
5405     RegisterClassA(&wndClass);
5406 }
5407
5408
5409 VOID
5410 TREEVIEW_Unregister(void)
5411 {
5412     UnregisterClassA(WC_TREEVIEWA, (HINSTANCE) NULL);
5413 }
5414
5415
5416 /* Tree Verification ****************************************************/
5417
5418 #ifdef NDEBUG
5419 static inline void
5420 TREEVIEW_VerifyChildren(TREEVIEW_INFO *infoPtr, TREEVIEW_ITEM *item);
5421
5422 static inline void TREEVIEW_VerifyItemCommon(TREEVIEW_INFO *infoPtr,
5423                                              TREEVIEW_ITEM *item)
5424 {
5425     assert(infoPtr != NULL);
5426     assert(item != NULL);
5427
5428     /* both NULL, or both non-null */
5429     assert((item->firstChild == NULL) == (item->lastChild == NULL));
5430
5431     assert(item->firstChild != item);
5432     assert(item->lastChild != item);
5433
5434     if (item->firstChild)
5435     {
5436         assert(item->firstChild->parent == item);
5437         assert(item->firstChild->prevSibling == NULL);
5438     }
5439
5440     if (item->lastChild)
5441     {
5442         assert(item->lastChild->parent == item);
5443         assert(item->lastChild->nextSibling == NULL);
5444     }
5445
5446     assert(item->nextSibling != item);
5447     if (item->nextSibling)
5448     {
5449         assert(item->nextSibling->parent == item->parent);
5450         assert(item->nextSibling->prevSibling == item);
5451     }
5452
5453     assert(item->prevSibling != item);
5454     if (item->prevSibling)
5455     {
5456         assert(item->prevSibling->parent == item->parent);
5457         assert(item->prevSibling->nextSibling == item);
5458     }
5459 }
5460
5461 static inline void
5462 TREEVIEW_VerifyItem(TREEVIEW_INFO *infoPtr, TREEVIEW_ITEM *item)
5463 {
5464     assert(item != NULL);
5465
5466     assert(item->parent != NULL);
5467     assert(item->parent != item);
5468     assert(item->iLevel == item->parent->iLevel + 1);
5469
5470     assert(DPA_GetPtrIndex(infoPtr->items, item) != -1);
5471
5472     TREEVIEW_VerifyItemCommon(infoPtr, item);
5473
5474     TREEVIEW_VerifyChildren(infoPtr, item);
5475 }
5476
5477 static inline void
5478 TREEVIEW_VerifyChildren(TREEVIEW_INFO *infoPtr, TREEVIEW_ITEM *item)
5479 {
5480     TREEVIEW_ITEM *child;
5481     assert(item != NULL);
5482
5483     for (child = item->firstChild; child != NULL; child = child->nextSibling)
5484         TREEVIEW_VerifyItem(infoPtr, child);
5485 }
5486
5487 static inline void
5488 TREEVIEW_VerifyRoot(TREEVIEW_INFO *infoPtr)
5489 {
5490     TREEVIEW_ITEM *root = infoPtr->root;
5491
5492     assert(root != NULL);
5493     assert(root->iLevel == -1);
5494     assert(root->parent == NULL);
5495     assert(root->prevSibling == NULL);
5496
5497     TREEVIEW_VerifyItemCommon(infoPtr, root);
5498
5499     TREEVIEW_VerifyChildren(infoPtr, root);
5500 }
5501
5502 static void
5503 TREEVIEW_VerifyTree(TREEVIEW_INFO *infoPtr)
5504 {
5505     assert(infoPtr != NULL);
5506
5507     TREEVIEW_VerifyRoot(infoPtr);
5508 }
5509 #endif