Fixed some issues found by winapi_check.
[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  *
8  * TODO:
9  *   Using DPA to store the item ptr would be good.
10  *   Node label edition is implemented but something happened in wine in the
11  *   two last weeks of march 99 that broke it.
12  *   refreshtreeview:   
13                 -small array containing info about positions.
14                 -better implementation of RefreshItem:
15               1) draw lines between parents
16               2) draw items
17                           3) draw lines from parent<->items.
18                 -implement partial drawing?
19  *  -drag&drop: TVM_CREATEDRAGIMAGE should create drag bitmap.
20  *  -scrollbars: horizontal scrollbar doesn't work.
21  *  -Unicode messages
22  *  -check custom draw
23  *  -I_CHILDRENCALLBACK
24  *   FIXME: check fontsize. (uRealItemHeight)
25  *          test focusItem  (redraw in different color)
26                         uHotItem
27                         Edit: needs timer
28                                   better implementation.
29  *   WM_HSCROLL is broken.
30  *   use separate routine to get item text/image.
31  *  
32  *   Separate drawing/calculation.
33  *
34  * FIXMEs  (for personal use)
35     Expand:             -ctlmacro expands twice ->toggle.
36    -DblClick:   ctlmacro.exe's NM_DBLCLK seems to go wrong (returns FALSE).
37    -treehelper: stack corruption makes big window. 
38              
39  */
40
41
42 #include <string.h>
43 #include "winbase.h"
44 #include "wingdi.h"
45 #include "commctrl.h"
46 #include "treeview.h"
47 #include "comctl32.h"
48 #include "debugtools.h"
49
50 DEFAULT_DEBUG_CHANNEL(treeview)
51
52 /* ffs should be in <string.h>. */
53
54 /* Defines, since they do not need to return previous state, and nr
55  * has no side effects in this file.
56  */
57 #define tv_test_bit(nr,bf)      (((LPBYTE)bf)[nr>>3]&(1<<(nr&7)))
58 #define tv_set_bit(nr,bf)       ((LPBYTE)bf)[nr>>3]|=(1<<(nr&7))
59 #define tv_clear_bit(nr,bf)     ((LPBYTE)bf)[nr>>3]&=~(1<<(nr&7))
60
61
62 #define TREEVIEW_GetInfoPtr(hwnd) \
63   ((TREEVIEW_INFO *) GetWindowLongA( hwnd, 0))
64
65 static BOOL
66 TREEVIEW_SendSimpleNotify (HWND hwnd, UINT code);
67 static BOOL
68 TREEVIEW_SendTreeviewNotify (HWND hwnd, UINT code, UINT action, 
69                         HTREEITEM oldItem, HTREEITEM newItem);
70 static BOOL
71 TREEVIEW_SendTreeviewDnDNotify (HWND hwnd, UINT code, HTREEITEM dragItem, 
72                         POINT pt);
73 static BOOL
74 TREEVIEW_SendDispInfoNotify (HWND hwnd, TREEVIEW_ITEM *wineItem, 
75                         UINT code, UINT what);
76 static BOOL
77 TREEVIEW_SendCustomDrawNotify (HWND hwnd, DWORD dwDrawStage, HDC hdc,
78                         RECT rc);
79 static BOOL
80 TREEVIEW_SendCustomDrawItemNotify (HWND hwnd, HDC hdc,
81             TREEVIEW_ITEM *tvItem, UINT uItemDrawState);
82 static LRESULT
83 TREEVIEW_DoSelectItem (HWND hwnd, INT action, HTREEITEM newSelect, INT cause);
84 static void
85 TREEVIEW_Refresh (HWND hwnd, HDC hdc);
86
87 static LRESULT CALLBACK
88 TREEVIEW_Edit_SubclassProc (HWND hwnd, UINT uMsg, WPARAM wParam, 
89                                                         LPARAM lParam);
90
91 static LRESULT
92 TREEVIEW_EditLabelA (HWND hwnd, WPARAM wParam, LPARAM lParam);
93
94 static LRESULT
95 TREEVIEW_EndEditLabelNow (HWND hwnd, WPARAM wParam, LPARAM lParam);
96
97
98
99
100 /* helper functions. Work with the assumption that validity of operands 
101    is checked beforehand, and that tree state is valid.  */
102
103 /* FIXME: MS documentation says `GetNextVisibleItem' returns NULL 
104    if not successfull. Probably only applies to dereferencing infoPtr
105    (i.e. we are offered a valid treeview structure)
106    and not whether there is a next `visible' child. 
107    FIXME: check other failures.
108  */
109
110 /***************************************************************************
111  * This method returns the TREEVIEW_ITEM object given the handle
112  */
113 static TREEVIEW_ITEM* TREEVIEW_ValidItem(
114   TREEVIEW_INFO *infoPtr,
115   HTREEITEM  handle)
116 {
117   if ((!handle) || (handle>infoPtr->uMaxHandle)) 
118     return NULL;
119
120   if (tv_test_bit ((INT)handle, infoPtr->freeList)) 
121     return NULL;
122
123   return &infoPtr->items[(INT)handle];
124 }
125
126 /***************************************************************************
127  * This method returns the last expanded child item of a tree node
128  */
129 static TREEVIEW_ITEM *TREEVIEW_GetLastListItem(
130   TREEVIEW_INFO *infoPtr,
131   TREEVIEW_ITEM *tvItem)
132 {
133   TREEVIEW_ITEM *wineItem = tvItem;
134
135   /* 
136    * Get this item last sibling 
137    */
138   while (wineItem->sibling) 
139           wineItem=& infoPtr->items [(INT)wineItem->sibling];
140
141   /* 
142    * If the last sibling has expanded children, restart.
143    */
144   if ( ( wineItem->cChildren > 0 ) && ( wineItem->state & TVIS_EXPANDED) )
145     return TREEVIEW_GetLastListItem(
146              infoPtr, 
147              &(infoPtr->items[(INT)wineItem->firstChild]));
148
149   return wineItem;
150 }
151
152 /***************************************************************************
153  * This method returns the previous physical item in the list not 
154  * considering the tree hierarchy.
155  */
156 static TREEVIEW_ITEM *TREEVIEW_GetPrevListItem(
157   TREEVIEW_INFO *infoPtr, 
158   TREEVIEW_ITEM *tvItem)
159 {
160   if (tvItem->upsibling) 
161   {
162     /* 
163      * This item has a upsibling, get the last item.  Since, GetLastListItem
164      * first looks at siblings, we must feed it with the first child.
165      */
166     TREEVIEW_ITEM *upItem = &infoPtr->items[(INT)tvItem->upsibling];
167     
168     if ( ( upItem->cChildren > 0 ) && ( upItem->state & TVIS_EXPANDED) )
169       return TREEVIEW_GetLastListItem( 
170                infoPtr, 
171                &infoPtr->items[(INT)upItem->firstChild]);
172     else
173       return upItem;
174   }
175   else
176   {
177     /*
178      * this item does not have a upsibling, get the parent
179      */
180     if (tvItem->parent) 
181       return &infoPtr->items[(INT)tvItem->parent];
182   }
183
184   return NULL;
185 }
186
187
188 /***************************************************************************
189  * This method returns the next physical item in the treeview not 
190  * considering the tree hierarchy.
191  */
192 static TREEVIEW_ITEM *TREEVIEW_GetNextListItem(
193   TREEVIEW_INFO *infoPtr, 
194   TREEVIEW_ITEM *tvItem)
195 {
196   TREEVIEW_ITEM *wineItem = NULL;
197
198   /* 
199    * If this item has children and is expanded, return the first child
200    */
201   if ((tvItem->firstChild) && (tvItem->state & TVIS_EXPANDED)) 
202                 return (& infoPtr->items[(INT)tvItem->firstChild]);
203
204
205   /*
206    * try to get the sibling
207    */
208   if (tvItem->sibling) 
209                 return (& infoPtr->items[(INT)tvItem->sibling]);
210
211   /*
212    * Otherwise, get the parent's sibling.
213    */
214   wineItem=tvItem;
215   while (wineItem->parent) {
216     wineItem=& infoPtr->items [(INT)wineItem->parent];
217         if (wineItem->sibling) 
218       return (& infoPtr->items [(INT)wineItem->sibling]);
219   } 
220
221   return NULL;  
222 }
223
224 /***************************************************************************
225  * This method returns the nth item starting at the given item.  It returns 
226  * the last item (or first) we we run out of items.
227  *
228  * Will scroll backward if count is <0.
229  *             forward if count is >0.
230  */
231 static TREEVIEW_ITEM *TREEVIEW_GetListItem(
232   TREEVIEW_INFO *infoPtr, 
233   TREEVIEW_ITEM *tvItem,
234   LONG          count)
235 {
236   TREEVIEW_ITEM *previousItem = NULL;
237   TREEVIEW_ITEM *wineItem     = tvItem;
238   LONG          iter          = 0;
239
240   if      (count > 0)
241   {
242     /* Find count item downward */
243     while ((iter++ < count) && (wineItem != NULL))
244     {
245       /* Keep a pointer to the previous in case we ask for more than we got */
246       previousItem = wineItem; 
247       wineItem     = TREEVIEW_GetNextListItem(infoPtr, wineItem);
248     } 
249
250     if (wineItem == NULL)
251       wineItem = previousItem;
252   }
253   else if (count < 0)
254   {
255     /* Find count item upward */
256     while ((iter-- > count) && (wineItem != NULL))
257     {
258       /* Keep a pointer to the previous in case we ask for more than we got */
259       previousItem = wineItem; 
260       wineItem = TREEVIEW_GetPrevListItem(infoPtr, wineItem);
261     }
262
263     if (wineItem == NULL)
264       wineItem = previousItem;
265   }
266   else
267     wineItem = NULL;
268
269   return wineItem;
270 }
271
272  
273 /***************************************************************************
274  * This method 
275  */
276 static void TREEVIEW_RemoveAllChildren(
277   HWND hwnd,
278   TREEVIEW_ITEM *parentItem)
279 {
280  TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
281  TREEVIEW_ITEM *killItem;
282  INT    kill;
283  
284  kill=(INT)parentItem->firstChild;
285  while (kill) {
286         tv_set_bit ( kill, infoPtr->freeList);
287         killItem=& infoPtr->items[kill];
288         if (killItem->pszText!=LPSTR_TEXTCALLBACKA) 
289                 COMCTL32_Free (killItem->pszText);
290         TREEVIEW_SendTreeviewNotify (hwnd, TVN_DELETEITEMA, 0, (HTREEITEM)kill, 0);
291         if (killItem->firstChild) 
292                         TREEVIEW_RemoveAllChildren (hwnd, killItem);
293         kill=(INT)killItem->sibling;
294  }
295
296  if (parentItem->cChildren>0) {
297         infoPtr->uNumItems -= parentItem->cChildren;
298         parentItem->firstChild = 0;
299         parentItem->cChildren  = 0;
300  }
301
302 }
303
304
305 static void
306 TREEVIEW_RemoveItem (HWND hwnd, TREEVIEW_ITEM *wineItem)
307
308 {
309  TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
310  TREEVIEW_ITEM *parentItem, *upsiblingItem, *siblingItem;
311  INT iItem;
312
313  iItem=(INT)wineItem->hItem;
314  tv_set_bit(iItem,infoPtr->freeList);
315  infoPtr->uNumItems--;
316  parentItem=NULL;
317  if (wineItem->pszText!=LPSTR_TEXTCALLBACKA) 
318         COMCTL32_Free (wineItem->pszText);
319
320  TREEVIEW_SendTreeviewNotify (hwnd, TVN_DELETEITEMA, 0, (HTREEITEM)iItem, 0);
321
322  if (wineItem->firstChild) 
323         TREEVIEW_RemoveAllChildren (hwnd,wineItem);
324
325  if (wineItem->parent) {
326         parentItem=& infoPtr->items [(INT)wineItem->parent];
327         switch (parentItem->cChildren) {
328                 case I_CHILDRENCALLBACK: 
329                                 FIXME("we don't handle I_CHILDRENCALLBACK yet\n");
330                                 break;
331                 case 1:
332                         parentItem->cChildren=0;
333                         parentItem->firstChild=0;    
334                         return;
335                 default:
336                         parentItem->cChildren--;
337                         if ((INT)parentItem->firstChild==iItem) 
338                                 parentItem->firstChild=wineItem->sibling;
339                 }
340  }
341
342  if (iItem==(INT)infoPtr->TopRootItem) 
343         infoPtr->TopRootItem=(HTREEITEM)wineItem->sibling;
344  if (wineItem->upsibling) {
345         upsiblingItem=& infoPtr->items [(INT)wineItem->upsibling];
346         upsiblingItem->sibling=wineItem->sibling;
347  }
348  if (wineItem->sibling) {
349         siblingItem=& infoPtr->items [(INT)wineItem->sibling];
350         siblingItem->upsibling=wineItem->upsibling;
351  }
352 }
353
354
355
356
357
358 /* Note:TREEVIEW_RemoveTree doesn't remove infoPtr itself */
359
360 static void TREEVIEW_RemoveTree (HWND hwnd)
361                                            
362 {
363   TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
364   TREEVIEW_ITEM *killItem;
365   int i;
366
367   for (i = 1; i <= (INT)infoPtr->uMaxHandle; i++) 
368     if (!tv_test_bit (i, infoPtr->freeList)) {
369       killItem = &infoPtr->items[i];    
370       if (killItem->pszText != LPSTR_TEXTCALLBACKA)
371         COMCTL32_Free (killItem->pszText);
372       TREEVIEW_SendTreeviewNotify(hwnd, TVN_DELETEITEMA, 0,
373                                   killItem->hItem, 0);
374     } 
375   if (infoPtr->uNumPtrsAlloced) {
376     COMCTL32_Free (infoPtr->items);
377     COMCTL32_Free (infoPtr->freeList);
378     infoPtr->uNumItems = 0;
379     infoPtr->uNumPtrsAlloced = 0;
380     infoPtr->uMaxHandle = 0;
381     infoPtr->TopRootItem = 0;
382   }   
383 }
384
385
386
387
388
389
390
391 static LRESULT
392 TREEVIEW_GetImageList (HWND hwnd, WPARAM wParam, LPARAM lParam)
393 {
394   TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
395
396   TRACE("\n");
397
398   if ((INT)wParam == TVSIL_NORMAL) 
399         return (LRESULT) infoPtr->himlNormal;
400   if ((INT)wParam == TVSIL_STATE) 
401         return (LRESULT) infoPtr->himlState;
402
403   return 0;
404 }
405
406 static LRESULT
407 TREEVIEW_SetImageList (HWND hwnd, WPARAM wParam, LPARAM lParam)
408 {
409     TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
410     HIMAGELIST himlTemp;
411
412     TRACE("%x,%lx\n", wParam, lParam);
413     switch ((INT)wParam) {
414         case TVSIL_NORMAL:
415             himlTemp = infoPtr->himlNormal;
416             infoPtr->himlNormal = (HIMAGELIST)lParam;
417             return (LRESULT)himlTemp;
418
419         case TVSIL_STATE:
420             himlTemp = infoPtr->himlState;
421             infoPtr->himlState = (HIMAGELIST)lParam;
422             return (LRESULT)himlTemp;
423     }
424
425     return (LRESULT)NULL;
426 }
427
428
429
430 static LRESULT
431 TREEVIEW_SetItemHeight (HWND hwnd, WPARAM wParam)
432 {
433   TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
434   INT cx,cy,prevHeight=infoPtr->uItemHeight;
435
436   TRACE("\n");
437   if (wParam==-1) {
438         infoPtr->uItemHeight=-1;
439         return prevHeight;
440   }
441
442   ImageList_GetIconSize (infoPtr->himlNormal, &cx, &cy);
443
444   if (wParam>cy) cy=wParam;
445   infoPtr->uItemHeight=cy;
446
447   if (!( GetWindowLongA( hwnd, GWL_STYLE) & TVS_NONEVENHEIGHT))
448         infoPtr->uItemHeight = (INT) wParam & 0xfffffffe;
449   return prevHeight;
450 }
451
452 static LRESULT
453 TREEVIEW_GetItemHeight (HWND hwnd)
454 {
455   TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
456   
457   TRACE("\n");
458   return infoPtr->uItemHeight;
459 }
460   
461 static LRESULT
462 TREEVIEW_GetLineColor (HWND hwnd, WPARAM wParam, LPARAM lParam)
463 {
464   TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
465
466   TRACE("\n");
467   return (LRESULT) infoPtr->clrLine;
468 }
469
470 static LRESULT
471 TREEVIEW_SetLineColor (HWND hwnd, WPARAM wParam, LPARAM lParam)
472 {
473   TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
474   COLORREF prevColor=infoPtr->clrLine;
475
476   TRACE("\n");
477   infoPtr->clrLine=(COLORREF) lParam;
478   return (LRESULT) prevColor;
479 }
480
481 static LRESULT
482 TREEVIEW_GetInsertMarkColor (HWND hwnd, WPARAM wParam, LPARAM lParam)
483 {
484   TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
485
486   TRACE("\n");
487   return (LRESULT) infoPtr->clrInsertMark;
488 }
489
490 static LRESULT
491 TREEVIEW_SetInsertMarkColor (HWND hwnd, WPARAM wParam, LPARAM lParam)
492 {
493   TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
494   COLORREF prevColor=infoPtr->clrInsertMark;
495
496   TRACE("%d %ld\n",wParam,lParam);
497   infoPtr->clrInsertMark=(COLORREF) lParam;
498   return (LRESULT) prevColor;
499 }
500
501 static LRESULT
502 TREEVIEW_SetInsertMark (HWND hwnd, WPARAM wParam, LPARAM lParam)
503 {
504   TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
505   HDC hdc;
506
507   FIXME("%d %ld\n",wParam,lParam);
508   if (!TREEVIEW_ValidItem (infoPtr, (HTREEITEM)lParam)) return 0;
509   FIXME("%d %ld\n",wParam,lParam);
510
511   infoPtr->insertBeforeorAfter=(BOOL) wParam;
512   infoPtr->insertMarkItem=(HTREEITEM) lParam;
513   
514   hdc = GetDC (hwnd);
515   TREEVIEW_Refresh (hwnd, hdc);
516   ReleaseDC(hwnd,hdc);
517
518   return 1;
519 }
520
521 static LRESULT
522 TREEVIEW_SetTextColor (HWND hwnd, WPARAM wParam, LPARAM lParam)
523 {
524   TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
525   COLORREF prevColor=infoPtr->clrText;
526
527   TRACE("\n");
528   infoPtr->clrText=(COLORREF) lParam;
529   return (LRESULT) prevColor;
530 }
531
532 static LRESULT
533 TREEVIEW_GetBkColor (HWND hwnd)
534 {
535   TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
536         
537   TRACE("\n");
538   return (LRESULT) infoPtr->clrBk;
539 }
540
541 static LRESULT
542 TREEVIEW_SetBkColor (HWND hwnd, WPARAM wParam, LPARAM lParam)
543 {
544   TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
545   COLORREF prevColor=infoPtr->clrBk;
546
547   TRACE("\n");
548   infoPtr->clrBk=(COLORREF) lParam;
549   return (LRESULT) prevColor;
550 }
551
552 static LRESULT
553 TREEVIEW_GetTextColor (HWND hwnd)
554 {
555   TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
556         
557   TRACE("\n");
558   return (LRESULT) infoPtr->clrText;
559 }
560
561
562 /* cdmode: custom draw mode as received from app. in first NMCUSTOMDRAW 
563            notification */
564
565 #define TREEVIEW_LEFT_MARGIN 8
566
567
568 static void
569 TREEVIEW_DrawItem (HWND hwnd, HDC hdc, TREEVIEW_ITEM *wineItem)
570 {
571   TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
572   DWORD dwStyle = GetWindowLongA (hwnd, GWL_STYLE);
573   INT   center,xpos,cx,cy, cditem;
574   HFONT hOldFont;
575   UINT  uTextJustify = DT_LEFT;
576   RECT  r;
577
578  
579   if (wineItem->state & TVIS_BOLD) 
580         hOldFont = SelectObject (hdc, infoPtr->hBoldFont);
581   else 
582         hOldFont = SelectObject (hdc, infoPtr->hFont);
583
584   cditem=0;
585   TRACE ("cdmode:%x\n",infoPtr->cdmode);
586   if (infoPtr->cdmode & CDRF_NOTIFYITEMDRAW) {
587                 cditem=TREEVIEW_SendCustomDrawItemNotify 
588                                         (hwnd, hdc, wineItem, CDDS_ITEMPREPAINT);
589                 TRACE("prepaint:cditem-app returns 0x%x\n",cditem);
590
591                 if (cditem & CDRF_SKIPDEFAULT) 
592                         return;
593         }
594
595   /* 
596    * Set drawing starting points 
597    */
598   r      = wineItem->rect;               /* this item rectangle */
599   center = (r.top+r.bottom)/2;           /* this item vertical center */
600   xpos   = r.left + TREEVIEW_LEFT_MARGIN;/* horizontal starting point */
601
602   /* 
603    * Display the tree hierarchy 
604    */
605   if ( dwStyle & TVS_HASLINES) 
606   {
607     /* 
608      * Write links to parent node 
609      * we draw the L starting from the child to the parent
610      *
611      * points[0] is attached to the current item
612      * points[1] is the L corner
613      * points[2] is attached to the parent or the up sibling
614      */
615     if ( dwStyle & TVS_LINESATROOT) 
616     {
617       TREEVIEW_ITEM *upNode    = NULL;
618         BOOL  hasParentOrSibling = TRUE;
619       RECT  upRect             = {0,0,0,0};
620       HPEN  hOldPen, hNewPen;
621         POINT points[3];
622       /* 
623        * determine the target location of the line at root, either be linked
624        * to the up sibling or to the parent node.
625        */
626       if (wineItem->upsibling)
627         upNode  = TREEVIEW_ValidItem (infoPtr, wineItem->upsibling);
628       else if (wineItem->parent)
629         upNode  = TREEVIEW_ValidItem (infoPtr, wineItem->parent);
630       else
631         hasParentOrSibling = FALSE;
632
633       if (upNode)
634         upRect = upNode->rect;
635
636       if ( wineItem->iLevel == 0 )
637       {
638         points[2].x = points[1].x = upRect.left+8;
639         points[0].x = points[2].x + 10;
640         points[2].y = upRect.bottom-3;      
641         points[1].y = points[0].y = center;
642       }
643       else
644       {
645         points[2].x = points[1].x = 8 + (20*wineItem->iLevel); 
646         points[2].y = ( upNode->cChildren == 0) ? 
647                         upRect.top :        /* is linked to the "L" above */
648                         ( wineItem->upsibling != NULL) ? 
649                           upRect.bottom-3:  /* is linked to an icon       */
650                           upRect.bottom+1;  /* is linked to a +/- box     */
651         points[1].y = points[0].y = center;
652         points[0].x = points[1].x + 10; 
653       }
654     
655       /* 
656        * Get a dotted pen
657        */ 
658       hNewPen = CreatePen(PS_DOT, 0, infoPtr->clrLine);
659       hOldPen = SelectObject( hdc, hNewPen );
660   
661       if (hasParentOrSibling)
662         Polyline (hdc,points,3); 
663       else
664         Polyline (hdc,points,2); 
665       
666       DeleteObject(hNewPen);
667       SelectObject(hdc, hOldPen);
668     }
669   }
670
671   /* 
672    * Display the (+/-) signs
673    */
674   if (wineItem->iLevel != 0)/*  update position only for non root node */
675     xpos+=(5*wineItem->iLevel);
676
677   if (( dwStyle & TVS_HASBUTTONS) && ( dwStyle & TVS_HASLINES))
678   {
679           if ( (wineItem->cChildren) ||
680                (wineItem->cChildren == I_CHILDRENCALLBACK))
681     {
682       /* Setup expand box coordinate to facilitate the LMBClick handling */
683       wineItem->expandBox.left   = xpos-4;
684       wineItem->expandBox.top    = center-4;
685       wineItem->expandBox.right  = xpos+5;
686       wineItem->expandBox.bottom = center+5;
687
688                 Rectangle (
689         hdc, 
690         wineItem->expandBox.left, 
691         wineItem->expandBox.top , 
692         wineItem->expandBox.right, 
693         wineItem->expandBox.bottom);
694
695                 MoveToEx (hdc, xpos-2, center, NULL);
696                 LineTo   (hdc, xpos+3, center);
697   
698                 if (!(wineItem->state & TVIS_EXPANDED)) {
699                         MoveToEx (hdc, xpos,   center-2, NULL);
700                         LineTo   (hdc, xpos,   center+3);
701           }
702     }
703   }
704
705   /* 
706    * Display the image associated with this item
707    */
708   xpos += 13; /* update position */
709   if (wineItem->mask & (TVIF_IMAGE|TVIF_SELECTEDIMAGE)) {
710     INT        imageIndex;
711     HIMAGELIST *himlp = NULL;
712
713    /* State images are displayed to the left of the Normal image
714     * image number is in state; zero should be `display no image'.
715     * FIXME: that last sentence looks like it needs some checking.
716     */
717         if (infoPtr->himlState) 
718         himlp=&infoPtr->himlState;
719         imageIndex=wineItem->state>>12;
720         imageIndex++;          /* yeah, right */
721         TRACE ("imindex:%d\n",imageIndex);
722     if ((himlp) && (imageIndex))
723     { 
724           imageIndex--;       /* see FIXME */
725       ImageList_Draw ( *himlp, imageIndex, hdc, xpos-2, r.top+1, ILD_NORMAL);
726           ImageList_GetIconSize (*himlp, &cx, &cy);
727           wineItem->statebitmap.left=xpos-2;
728           wineItem->statebitmap.right=xpos-2+cx;
729           wineItem->statebitmap.top=r.top+1;
730           wineItem->statebitmap.bottom=r.top+1+cy;
731           xpos+=cx;
732     }
733         
734                 /* Now, draw the normal image; can be either selected or
735                  * non-selected image. 
736                  */
737
738         himlp=NULL;
739         if (infoPtr->himlNormal) 
740       himlp=&infoPtr->himlNormal; /* get the image list */
741
742     imageIndex = wineItem->iImage;
743         if ( (wineItem->state & TVIS_SELECTED) && 
744          (wineItem->iSelectedImage)) { 
745         
746       /* The item is curently selected */
747                   if (wineItem->iSelectedImage == I_IMAGECALLBACK) 
748                         TREEVIEW_SendDispInfoNotify 
749                                         (hwnd, wineItem, TVN_GETDISPINFOA, TVIF_SELECTEDIMAGE);
750
751           imageIndex = wineItem->iSelectedImage;
752           } else { 
753       /* The item is not selected */
754                   if (wineItem->iImage == I_IMAGECALLBACK) 
755                           TREEVIEW_SendDispInfoNotify 
756                                         (hwnd, wineItem, TVN_GETDISPINFOA, TVIF_IMAGE);
757
758       imageIndex = wineItem->iImage;
759         }
760  
761     if (himlp)         
762     { 
763         int ovlIdx = 0;
764
765         if(wineItem->stateMask & TVIS_OVERLAYMASK)
766                 ovlIdx = wineItem->state & TVIS_OVERLAYMASK;
767
768         ImageList_Draw ( *himlp, imageIndex, hdc, xpos-2, r.top+1, ILD_NORMAL|ovlIdx);
769         ImageList_GetIconSize (*himlp, &cx, &cy);
770         wineItem->bitmap.left=xpos-2;
771         wineItem->bitmap.right=xpos-2+cx;
772         wineItem->bitmap.top=r.top+1;
773         wineItem->bitmap.bottom=r.top+1+cy;
774         xpos+=cx;
775     }
776   }
777
778
779   /* 
780    * Display the text associated with this item
781    */
782   r.left=xpos;
783   if ((wineItem->mask & TVIF_TEXT) && (wineItem->pszText)) 
784   {
785     COLORREF oldBkColor = 0;
786     COLORREF oldTextColor = 0;
787     INT      oldBkMode;
788
789     r.left += 3;
790     r.right -= 3;
791
792     wineItem->text.left  = r.left;  
793     wineItem->text.right = r.right;
794     wineItem->text.top   = r.top;
795     wineItem->text.bottom= r.bottom;
796
797     if (wineItem->pszText== LPSTR_TEXTCALLBACKA) {
798       TRACE("LPSTR_TEXTCALLBACK\n");
799       TREEVIEW_SendDispInfoNotify (hwnd, wineItem, TVN_GETDISPINFOA, TVIF_TEXT);
800     }
801
802 /* Yep, there are some things that need to be straightened out here. 
803    Removing the comments around the setTextColor does not give the right
804    results. Dito FillRect.
805 */
806
807         
808 /*    GetTextExtentPoint32A (hdc, wineItem->pszText, 
809                                         strlen (wineItem->pszText), &size); */
810         
811 /*    FillRect ( hdc, &wineItem->text, GetSysColorBrush (infoPtr->clrBk));
812  */
813     
814
815     if (!(cditem & CDRF_NOTIFYPOSTPAINT) && 
816         (wineItem->state & (TVIS_SELECTED | TVIS_DROPHILITED)) ) {
817         oldBkMode    = SetBkMode  (hdc, OPAQUE);
818         oldBkColor   = SetBkColor  (hdc, GetSysColor( COLOR_HIGHLIGHT));
819         oldTextColor = SetTextColor(hdc, GetSysColor( COLOR_HIGHLIGHTTEXT));
820         } else {
821         oldBkMode    = SetBkMode  (hdc, TRANSPARENT);
822                 oldBkColor   = SetBkColor (hdc, infoPtr->clrBk);
823  /*         oldTextColor = SetTextColor(hdc, infoPtr->clrText);  */
824         }
825
826
827
828     /* Draw it */
829     DrawTextA ( hdc, 
830       wineItem->pszText, 
831       lstrlenA(wineItem->pszText), 
832       &wineItem->text, 
833       uTextJustify | DT_VCENTER | DT_SINGLELINE ); 
834
835     /* Obtain the text coordinate */
836     DrawTextA (
837       hdc, 
838       wineItem->pszText, 
839       lstrlenA(wineItem->pszText), 
840       &wineItem->text, 
841       uTextJustify | DT_VCENTER | DT_SINGLELINE | DT_CALCRECT); 
842
843     /* Restore the hdc state */
844     SetTextColor( hdc, oldTextColor);
845
846     if (oldBkMode != TRANSPARENT)
847       SetBkMode(hdc, oldBkMode);
848     if (wineItem->state & (TVIS_SELECTED | TVIS_DROPHILITED))
849       SetBkColor (hdc, oldBkColor);
850
851     /* Draw the box arround the selected item */
852     if (wineItem->state & TVIS_SELECTED ) 
853     {
854       HPEN  hNewPen     = CreatePen(PS_DOT, 0, GetSysColor(COLOR_WINDOWTEXT) );
855       HPEN  hOldPen     = SelectObject( hdc, hNewPen );
856       POINT points[4];
857       
858       points[0].x = wineItem->text.left-1;
859       points[0].y = wineItem->text.top+1; 
860       points[1].x = wineItem->text.right;
861       points[1].y = wineItem->text.top+1; 
862       points[2].x = wineItem->text.right;
863       points[2].y = wineItem->text.bottom; 
864       points[3].x = wineItem->text.left-1;
865       points[3].y = wineItem->text.bottom;
866
867       Polyline (hdc,points,4); 
868
869       DeleteObject(hNewPen);
870       SelectObject(hdc, hOldPen);
871     }
872   }
873
874   /* Draw insertion mark if necessary */
875
876   if (infoPtr->insertMarkItem) 
877                 TRACE ("item:%d,mark:%d\n", (int)wineItem->hItem,
878                                (int) infoPtr->insertMarkItem);
879   if (wineItem->hItem==infoPtr->insertMarkItem) {
880                 HPEN hNewPen, hOldPen;
881                 int offset;
882
883                 hNewPen = CreatePen(PS_SOLID, 2, infoPtr->clrInsertMark);
884                 hOldPen = SelectObject( hdc, hNewPen );
885         
886                 if (infoPtr->insertBeforeorAfter)
887                         offset=wineItem->text.top+1;
888                 else
889                         offset=wineItem->text.bottom-1;
890
891                 MoveToEx (hdc, wineItem->text.left, offset-3, NULL);
892                 LineTo (hdc, wineItem->text.left, offset+3);
893
894                 MoveToEx (hdc, wineItem->text.left, offset, NULL);
895                 LineTo (hdc, r.right-2, offset);
896
897                 MoveToEx (hdc, r.right-2, offset+3, NULL);
898                 LineTo (hdc, r.right-2, offset-3);
899
900         DeleteObject(hNewPen);
901
902         SelectObject(hdc, hOldPen);
903         }
904
905   if (cditem & CDRF_NOTIFYPOSTPAINT) {
906                 cditem=TREEVIEW_SendCustomDrawItemNotify 
907                        (hwnd, hdc, wineItem, CDDS_ITEMPOSTPAINT);
908                 TRACE("postpaint:cditem-app returns 0x%x\n",cditem);
909         }
910
911   SelectObject (hdc, hOldFont);
912 }
913
914 static LRESULT
915 TREEVIEW_GetItemRect (HWND hwnd, WPARAM wParam, LPARAM lParam)
916 {
917   TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
918   TREEVIEW_ITEM *wineItem;
919   HTREEITEM     *iItem;
920   LPRECT        lpRect   = (LPRECT)lParam;
921   HDC                   hdc;
922
923   TRACE("\n");
924   /* 
925    * validate parameters
926    */
927   if (lpRect == NULL)
928     return FALSE;
929
930   if (infoPtr->Timer & TV_REFRESH_TIMER_SET) {
931         hdc = GetDC (hwnd);
932         TREEVIEW_Refresh (hwnd, hdc); /* we want a rect for the current view */  
933         ReleaseDC(hwnd,hdc);
934    }
935
936
937   /* 
938    * retrieve the item ptr
939    */ 
940   iItem = (HTREEITEM *) lParam;
941   wineItem = TREEVIEW_ValidItem (infoPtr, *iItem);
942   if ((!wineItem) || (!wineItem->visible)) 
943     return FALSE;
944
945   /* 
946    * If wParam is TRUE return the text size otherwise return 
947    * the whole item size        
948    */
949   if ((INT) wParam) {
950         lpRect->left      = wineItem->text.left;
951     lpRect->right         = wineItem->text.right;
952     lpRect->bottom      = wineItem->text.bottom;
953     lpRect->top     = wineItem->text.top;
954   } else {
955     lpRect->left          = wineItem->rect.left;
956     lpRect->right         = wineItem->rect.right;
957     lpRect->bottom  = wineItem->rect.bottom;
958     lpRect->top     = wineItem->rect.top;
959   }
960
961   TRACE("[L:%d R:%d T:%d B:%d]\n", 
962       lpRect->left,lpRect->right,
963                         lpRect->top,lpRect->bottom);
964
965   return TRUE;
966 }
967
968 static LRESULT
969 TREEVIEW_GetVisibleCount (HWND hwnd,  WPARAM wParam, LPARAM lParam)
970
971 {
972   TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
973
974   return (LRESULT) infoPtr->uVisibleHeight / infoPtr->uRealItemHeight;
975 }
976
977
978
979 static LRESULT
980 TREEVIEW_SetItemA (HWND hwnd, WPARAM wParam, LPARAM lParam)
981 {
982   TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
983   TREEVIEW_ITEM *wineItem;
984   TVITEMEXA *tvItem;
985   INT iItem,len;
986
987   tvItem=(LPTVITEMEXA) lParam;
988   iItem=(INT)tvItem->hItem;
989   TRACE("item %d,mask %x\n",iItem,tvItem->mask);
990
991   wineItem = TREEVIEW_ValidItem (infoPtr, (HTREEITEM)iItem);
992   if (!wineItem) return FALSE;
993
994   if (tvItem->mask & TVIF_CHILDREN) {
995         wineItem->cChildren=tvItem->cChildren;
996   }
997
998   if (tvItem->mask & TVIF_IMAGE) {
999        wineItem->iImage=tvItem->iImage;
1000   }
1001
1002   if (tvItem->mask & TVIF_INTEGRAL) {
1003         wineItem->iIntegral=tvItem->iIntegral; 
1004   }
1005
1006   if (tvItem->mask & TVIF_PARAM) {
1007         wineItem->lParam=tvItem->lParam;
1008   }
1009
1010   if (tvItem->mask & TVIF_SELECTEDIMAGE) {
1011         wineItem->iSelectedImage=tvItem->iSelectedImage;
1012   }
1013
1014   if (tvItem->mask & TVIF_STATE) {
1015                 TRACE ("prevstate,state,mask:%x,%x,%x\n",wineItem->state,tvItem->state,
1016 tvItem->stateMask);
1017         wineItem->state&= ~tvItem->stateMask;
1018                 wineItem->state|= (tvItem->state & tvItem->stateMask);
1019                 wineItem->stateMask|= tvItem->stateMask;  
1020   }
1021
1022   if (tvItem->mask & TVIF_TEXT) {
1023                 if (tvItem->pszText!=LPSTR_TEXTCALLBACKA) {
1024         len=lstrlenA (tvItem->pszText);
1025         if (len>wineItem->cchTextMax) 
1026                         wineItem->pszText= COMCTL32_ReAlloc (wineItem->pszText, len+1);
1027         lstrcpynA (wineItem->pszText, tvItem->pszText,len+1);
1028                 } else {
1029                         if (wineItem->cchTextMax) {
1030                                 COMCTL32_Free (wineItem->pszText);
1031                                 wineItem->cchTextMax=0;
1032                         }
1033                 wineItem->pszText=LPSTR_TEXTCALLBACKA;
1034                 }
1035    }
1036
1037   wineItem->mask |= tvItem->mask;
1038
1039   return TRUE;
1040 }
1041
1042 static LRESULT
1043 TREEVIEW_GetItemState (HWND hwnd, WPARAM wParam, LPARAM lParam)
1044
1045 {
1046     TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
1047         TREEVIEW_ITEM *wineItem;
1048         
1049     TRACE("\n");
1050         wineItem = TREEVIEW_ValidItem (infoPtr, (HTREEITEM)wParam);
1051         if (!wineItem) return 0;
1052         
1053         return (wineItem->state & lParam);
1054 }
1055
1056
1057
1058
1059 static void
1060 TREEVIEW_Refresh (HWND hwnd, HDC hdc)
1061 {
1062     TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
1063         TEXTMETRICA tm;
1064         HBRUSH hbrBk;
1065     RECT rect;
1066     INT iItem, indent, x, y, height, itemHeight;
1067     INT viewtop,viewbottom,viewleft,viewright;
1068     TREEVIEW_ITEM *wineItem, *prevItem;
1069
1070     TRACE("\n");
1071
1072
1073     if (infoPtr->Timer & TV_REFRESH_TIMER_SET) {
1074                 KillTimer (hwnd, TV_REFRESH_TIMER);
1075                 infoPtr->Timer &= ~TV_REFRESH_TIMER_SET;
1076     }
1077
1078     
1079     GetClientRect (hwnd, &rect);
1080     if ((rect.left-rect.right ==0) || (rect.top-rect.bottom==0)) return;
1081
1082     infoPtr->cdmode=TREEVIEW_SendCustomDrawNotify(hwnd,CDDS_PREPAINT,hdc,rect);
1083
1084     if (infoPtr->cdmode==CDRF_SKIPDEFAULT) return;
1085
1086     infoPtr->uVisibleHeight= rect.bottom-rect.top;
1087     infoPtr->uVisibleWidth= rect.right-rect.left;
1088
1089     viewtop=infoPtr->cy;
1090     viewbottom=infoPtr->cy + rect.bottom-rect.top;
1091     viewleft=infoPtr->cx;
1092     viewright=infoPtr->cx + rect.right-rect.left;
1093
1094     TRACE("[%d %d %d %d]\n",viewtop,viewbottom,viewleft,viewright);
1095
1096     /* draw background */
1097     
1098     hbrBk = CreateSolidBrush (infoPtr->clrBk);
1099     FillRect(hdc, &rect, hbrBk);
1100     DeleteObject(hbrBk);
1101
1102     ImageList_GetIconSize (infoPtr->himlNormal, &x, &itemHeight);
1103     if (infoPtr->uItemHeight>itemHeight)
1104         itemHeight=infoPtr->uItemHeight;
1105
1106     GetTextMetricsA (hdc, &tm);
1107     if ((tm.tmHeight + tm.tmExternalLeading) > itemHeight)
1108          itemHeight=tm.tmHeight + tm.tmExternalLeading;
1109
1110     infoPtr->uRealItemHeight=itemHeight;
1111
1112     iItem=(INT)infoPtr->TopRootItem;
1113     infoPtr->firstVisible=0;
1114     wineItem=NULL;
1115     indent=0;
1116     x=y=0;
1117
1118     while (iItem) {
1119         prevItem=wineItem;
1120         wineItem= & infoPtr->items[iItem];
1121         wineItem->iLevel=indent;
1122
1123 /* FIXME: remove this in later stage  */
1124 /*
1125                 if (wineItem->pszText!=LPSTR_TEXTCALLBACK32A) 
1126                 TRACE (treeview, "%d %d [%d %d %d %d] (%s)\n",y,x,
1127                         wineItem->rect.top, wineItem->rect.bottom,
1128                         wineItem->rect.left, wineItem->rect.right,
1129                         wineItem->pszText);
1130                 else 
1131                 TRACE (treeview, "%d [%d %d %d %d] (CALLBACK)\n",
1132                                 wineItem->hItem,
1133                                 wineItem->rect.top, wineItem->rect.bottom,
1134                                 wineItem->rect.left, wineItem->rect.right);
1135 */
1136
1137                 height=itemHeight * wineItem->iIntegral +1;
1138                 if ((y >= viewtop) && (y <= viewbottom) &&
1139                 (x >= viewleft  ) && (x <= viewright)) {
1140                                 wineItem->visible = TRUE;
1141                         wineItem->rect.top = y - infoPtr->cy + rect.top;
1142                         wineItem->rect.bottom = wineItem->rect.top + height ;
1143                         wineItem->rect.left = x - infoPtr->cx + rect.left;
1144                         wineItem->rect.right = rect.right;
1145                         if (!infoPtr->firstVisible)
1146                                 infoPtr->firstVisible=wineItem->hItem;
1147                 TREEVIEW_DrawItem (hwnd, hdc, wineItem);
1148                 }
1149                 else {
1150                         wineItem->visible   = FALSE;
1151                         wineItem->rect.left = wineItem->rect.top    = 0;
1152                         wineItem->rect.right= wineItem->rect.bottom = 0;
1153                         wineItem->text.left = wineItem->text.top    = 0;
1154                         wineItem->text.right= wineItem->text.bottom = 0;
1155                 }
1156
1157                 /* look up next item */
1158         
1159                 if ((wineItem->firstChild) && (wineItem->state & TVIS_EXPANDED)) {
1160                         iItem=(INT)wineItem->firstChild;
1161                         indent++;
1162                         x+=infoPtr->uIndent;
1163                         if (x>infoPtr->uTotalWidth)     
1164                                 infoPtr->uTotalWidth=x;
1165                 }
1166                 else {
1167                         iItem=(INT)wineItem->sibling;
1168                         while ((!iItem) && (indent>0)) {
1169                                 indent--;
1170                                 x-=infoPtr->uIndent;
1171                                 wineItem=&infoPtr->items[(INT)wineItem->parent];
1172                                 iItem=(INT)wineItem->sibling;
1173                         }
1174                 }
1175         y +=height;
1176     }                           /* while */
1177
1178 /* FIXME: infoPtr->uTotalWidth should also take item label into account */
1179 /* FIXME: or should query item sizes (ie check CDRF_NEWFONT) */
1180
1181     infoPtr->uTotalHeight=y;
1182     if (y >= (viewbottom-viewtop)) {
1183                 if (!(infoPtr->uInternalStatus & TV_VSCROLL))
1184                         ShowScrollBar (hwnd, SB_VERT, TRUE);
1185                 infoPtr->uInternalStatus |=TV_VSCROLL;
1186                 SetScrollRange (hwnd, SB_VERT, 0, 
1187                                         y - infoPtr->uVisibleHeight, FALSE);
1188                 SetScrollPos (hwnd, SB_VERT, infoPtr->cy, TRUE);
1189         }
1190     else {
1191                 if (infoPtr->uInternalStatus & TV_VSCROLL) 
1192                         ShowScrollBar (hwnd, SB_VERT, FALSE);
1193                 infoPtr->uInternalStatus &= ~TV_VSCROLL;
1194         }
1195
1196
1197         if (infoPtr->cdmode & CDRF_NOTIFYPOSTPAINT) 
1198         infoPtr->cdmode=TREEVIEW_SendCustomDrawNotify 
1199                                                                 (hwnd, CDDS_POSTPAINT, hdc, rect);
1200
1201     TRACE("done\n");
1202 }
1203
1204
1205 static LRESULT 
1206 TREEVIEW_HandleTimer (HWND hwnd, WPARAM wParam, LPARAM lParam)
1207 {
1208   TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
1209
1210   TRACE(" %d\n",wParam);
1211
1212   switch (wParam) {
1213         case TV_REFRESH_TIMER:
1214                 KillTimer (hwnd, TV_REFRESH_TIMER);
1215                 infoPtr->Timer &= ~TV_REFRESH_TIMER_SET;
1216     InvalidateRect(hwnd, NULL, FALSE);
1217                 return 0;
1218         case TV_EDIT_TIMER:
1219                 KillTimer (hwnd, TV_EDIT_TIMER);
1220                 infoPtr->Timer &= ~TV_EDIT_TIMER_SET;
1221                 return 0;
1222         default:
1223                 ERR("got unknown timer\n");
1224  }
1225                 
1226  return 1;
1227 }
1228
1229
1230 static void
1231 TREEVIEW_QueueRefresh (HWND hwnd)
1232
1233 {
1234  TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
1235
1236  TRACE("\n");
1237  if (infoPtr->Timer & TV_REFRESH_TIMER_SET) {
1238         KillTimer (hwnd, TV_REFRESH_TIMER);
1239  }
1240
1241  SetTimer (hwnd, TV_REFRESH_TIMER, TV_REFRESH_DELAY, 0);
1242  infoPtr->Timer|=TV_REFRESH_TIMER_SET;
1243 }
1244
1245
1246
1247 static LRESULT
1248 TREEVIEW_GetItemA (HWND hwnd, WPARAM wParam, LPARAM lParam)
1249 {
1250   TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
1251   LPTVITEMEXA    tvItem;
1252   TREEVIEW_ITEM *wineItem;
1253   INT         iItem;
1254
1255   tvItem=(LPTVITEMEXA) lParam;
1256   iItem=(INT)tvItem->hItem;
1257
1258   wineItem = TREEVIEW_ValidItem (infoPtr, (HTREEITEM)iItem);
1259   if (!wineItem) return FALSE;
1260
1261    if (tvItem->mask & TVIF_CHILDREN) {
1262                 if (TVIF_CHILDREN==I_CHILDRENCALLBACK) 
1263                         FIXME("I_CHILDRENCALLBACK not supported\n");
1264         tvItem->cChildren=wineItem->cChildren;
1265    }
1266
1267    if (tvItem->mask & TVIF_HANDLE) {
1268         tvItem->hItem=wineItem->hItem;
1269    }
1270
1271    if (tvItem->mask & TVIF_IMAGE) {
1272         tvItem->iImage=wineItem->iImage;
1273    }
1274
1275    if (tvItem->mask & TVIF_INTEGRAL) {
1276         tvItem->iIntegral=wineItem->iIntegral; 
1277    }
1278
1279    /* undocumented: windows ignores TVIF_PARAM and
1280          * always sets lParam
1281          */
1282    tvItem->lParam=wineItem->lParam;
1283
1284    if (tvItem->mask & TVIF_SELECTEDIMAGE) {
1285         tvItem->iSelectedImage=wineItem->iSelectedImage;
1286    }
1287
1288    if (tvItem->mask & TVIF_STATE) {
1289         tvItem->state=wineItem->state & tvItem->stateMask;
1290    }
1291
1292    if (tvItem->mask & TVIF_TEXT) {
1293         if (wineItem->pszText == LPSTR_TEXTCALLBACKA) {
1294             tvItem->pszText = LPSTR_TEXTCALLBACKA;  /* FIXME:send notification? */
1295                 ERR(" GetItem called with LPSTR_TEXTCALLBACK\n");
1296         }
1297         else if (wineItem->pszText) {
1298             lstrcpynA (tvItem->pszText, wineItem->pszText, tvItem->cchTextMax);
1299         }
1300    }
1301
1302   TRACE("item %d<%p>, txt %p, img %p, action %x\n", 
1303     iItem, tvItem, tvItem->pszText, &tvItem->iImage, tvItem->mask);
1304
1305   return TRUE;
1306 }
1307
1308
1309 static LRESULT
1310 TREEVIEW_GetItemW (HWND hwnd, WPARAM wParam, LPARAM lParam)
1311 {
1312   TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
1313   LPTVITEMEXA    tvItem;
1314   TREEVIEW_ITEM *wineItem;
1315   INT         iItem;
1316
1317   tvItem=(LPTVITEMEXA) lParam;
1318   iItem=(INT)tvItem->hItem;
1319
1320   wineItem = TREEVIEW_ValidItem (infoPtr, (HTREEITEM)iItem);
1321   if (!wineItem) return FALSE;
1322
1323    if (tvItem->mask & TVIF_CHILDREN) {
1324                 if (TVIF_CHILDREN==I_CHILDRENCALLBACK)
1325                         FIXME("I_CHILDRENCALLBACK not supported\n");
1326         tvItem->cChildren=wineItem->cChildren;
1327    }
1328
1329    if (tvItem->mask & TVIF_HANDLE) {
1330         tvItem->hItem=wineItem->hItem;
1331    }
1332
1333    if (tvItem->mask & TVIF_IMAGE) {
1334         tvItem->iImage=wineItem->iImage;
1335    }
1336
1337    if (tvItem->mask & TVIF_INTEGRAL) {
1338         tvItem->iIntegral=wineItem->iIntegral;
1339    }
1340
1341    /* undocumented: windows ignores TVIF_PARAM and
1342          * always sets lParam
1343          */
1344    tvItem->lParam=wineItem->lParam;
1345
1346    if (tvItem->mask & TVIF_SELECTEDIMAGE) {
1347         tvItem->iSelectedImage=wineItem->iSelectedImage;
1348    }
1349
1350    if (tvItem->mask & TVIF_STATE) {
1351         tvItem->state=wineItem->state & tvItem->stateMask;
1352    }
1353
1354 #if 0
1355    if (tvItem->mask & TVIF_TEXT) {
1356         if (wineItem->pszText == LPSTR_TEXTCALLBACKW) {
1357             tvItem->pszText = LPSTR_TEXTCALLBACKW;  /* FIXME:send notification? */
1358                 ERR(" GetItem called with LPSTR_TEXTCALLBACK\n");
1359         }
1360         else if (wineItem->pszText) {
1361             lstrcpynAtoW (tvItem->pszText, wineItem->pszText, tvItem->cchTextMax);
1362         }
1363    }
1364 #endif
1365   wineItem->pszText = NULL;
1366
1367   TRACE("item %d<%p>, txt %p, img %p, action %x\n",
1368     iItem, tvItem, tvItem->pszText, &tvItem->iImage, tvItem->mask);
1369
1370   return TRUE;
1371 }
1372
1373
1374
1375 /* FIXME: check implementation of TVGN_NEXT/TVGN_NEXTVISIBLE */
1376
1377 static LRESULT
1378 TREEVIEW_GetNextItem (HWND hwnd, WPARAM wParam, LPARAM lParam)
1379
1380 {
1381   TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
1382   TREEVIEW_ITEM *wineItem, *returnItem;
1383   INT iItem = (INT)lParam, retval = 0, flag  = (INT)wParam;
1384   HDC hdc;
1385
1386   switch (flag) {
1387   case TVGN_ROOT:
1388     retval = (INT)infoPtr->TopRootItem;
1389     break;
1390
1391   case TVGN_CARET:
1392     retval = (INT)infoPtr->selectedItem;
1393     break;
1394
1395   case TVGN_FIRSTVISIBLE: /* FIXME:we should only recalculate, not redraw */
1396     hdc = GetDC (hwnd);
1397     TREEVIEW_Refresh (hwnd, hdc);
1398     ReleaseDC(hwnd,hdc);
1399     retval = (INT)infoPtr->firstVisible;
1400     break;
1401
1402   case TVGN_DROPHILITE:
1403     retval = (INT)infoPtr->dropItem;
1404     break;
1405  
1406   case TVGN_NEXT:
1407     wineItem = TREEVIEW_ValidItem (infoPtr, (HTREEITEM)iItem);
1408     retval = wineItem ? (INT)wineItem->sibling : 0;
1409     break;
1410
1411   case TVGN_PREVIOUS:   
1412     wineItem = TREEVIEW_ValidItem (infoPtr, (HTREEITEM)iItem);
1413     retval = wineItem ? (INT)wineItem->upsibling : 0;
1414     break;
1415
1416   case TVGN_PARENT:
1417     wineItem = TREEVIEW_ValidItem (infoPtr, (HTREEITEM)iItem);
1418     retval = wineItem ? (INT)wineItem->parent : 0;
1419     break;
1420
1421   case TVGN_CHILD:
1422     wineItem = TREEVIEW_ValidItem (infoPtr, (HTREEITEM)iItem);
1423     retval = wineItem ? (INT)wineItem->firstChild : 0;
1424     break;
1425
1426   case TVGN_LASTVISIBLE:  
1427     if((wineItem = TREEVIEW_ValidItem (infoPtr, (HTREEITEM)iItem))) {
1428       returnItem = TREEVIEW_GetLastListItem (infoPtr,wineItem);
1429       retval = returnItem ? (INT)returnItem->hItem : 0;
1430     }
1431     break;
1432
1433   case TVGN_NEXTVISIBLE:  
1434     if((wineItem = TREEVIEW_ValidItem (infoPtr, (HTREEITEM)iItem))) {
1435       returnItem = TREEVIEW_GetNextListItem (infoPtr,wineItem);
1436       retval = returnItem ? (INT)returnItem->hItem : 0;
1437     }
1438     break;
1439
1440   case TVGN_PREVIOUSVISIBLE: 
1441     if((wineItem = TREEVIEW_ValidItem (infoPtr, (HTREEITEM)iItem))) {
1442       returnItem = TREEVIEW_GetPrevListItem (infoPtr, wineItem);
1443       retval = returnItem ? (INT)returnItem->hItem : 0;
1444     }
1445     break;
1446
1447   default:
1448     FIXME("Unknown msg %x,item %x\n", flag,iItem);
1449     break;
1450   }
1451
1452   TRACE("flags %x, item %d returns %d\n", flag, iItem, retval);
1453   return retval;
1454 }
1455
1456
1457 static LRESULT
1458 TREEVIEW_GetCount (HWND hwnd, WPARAM wParam, LPARAM lParam)
1459 {
1460  TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
1461
1462  TRACE(" %d\n",infoPtr->uNumItems);
1463  return (LRESULT) infoPtr->uNumItems;
1464 }
1465
1466 /***************************************************************************
1467  * This method does the chaining of the insertion of a treeview item 
1468  * before an item.
1469  * If parent is NULL, we're inserting at the root of the list.
1470  */
1471 static void TREEVIEW_InsertBefore(
1472     TREEVIEW_INFO *infoPtr,
1473     TREEVIEW_ITEM *newItem, 
1474     TREEVIEW_ITEM *sibling,
1475     TREEVIEW_ITEM *parent)
1476 {
1477   HTREEITEM     siblingHandle   = 0;
1478   HTREEITEM     upSiblingHandle = 0;
1479   TREEVIEW_ITEM *upSibling      = NULL;
1480
1481   if (newItem == NULL)
1482     ERR("NULL newItem, impossible condition\n");
1483
1484   if (sibling != NULL) /* Insert before this sibling for this parent */
1485   { 
1486     /* Store the new item sibling up sibling and sibling tem handle */
1487     siblingHandle   = sibling->hItem;
1488     upSiblingHandle = sibling->upsibling;
1489     /* As well as a pointer to the upsibling sibling object */
1490     if ( (INT)sibling->upsibling != 0 )
1491       upSibling = &infoPtr->items[(INT)sibling->upsibling];
1492   
1493     /* Adjust the sibling pointer */
1494     sibling->upsibling = newItem->hItem;
1495     
1496     /* Adjust the new item pointers */
1497     newItem->upsibling = upSiblingHandle;
1498     newItem->sibling   = siblingHandle;
1499     
1500     /* Adjust the up sibling pointer */
1501     if ( upSibling != NULL )        
1502       upSibling->sibling = newItem->hItem;
1503     else
1504       /* this item is the first child of this parent, adjust parent pointers */
1505           if (parent)
1506         parent->firstChild = newItem->hItem;
1507           else 
1508                 infoPtr->TopRootItem= newItem->hItem;
1509   }
1510   else /* Insert as first child of this parent */
1511         if (parent)
1512         parent->firstChild = newItem->hItem;
1513 }
1514
1515 /***************************************************************************
1516  * This method does the chaining of the insertion of a treeview item 
1517  * after an item.
1518  * If parent is NULL, we're inserting at the root of the list.
1519  */
1520 static void TREEVIEW_InsertAfter(
1521     TREEVIEW_INFO *infoPtr,
1522     TREEVIEW_ITEM *newItem, 
1523     TREEVIEW_ITEM *upSibling,
1524     TREEVIEW_ITEM *parent)
1525 {
1526   HTREEITEM     upSiblingHandle = 0;
1527   HTREEITEM     siblingHandle   = 0;
1528   TREEVIEW_ITEM *sibling        = NULL;
1529
1530
1531   if (newItem == NULL)
1532     ERR("NULL newItem, impossible condition\n");
1533
1534   if (upSibling != NULL) /* Insert after this upsibling for this parent */
1535   { 
1536     /* Store the new item up sibling and sibling item handle */
1537     upSiblingHandle = upSibling->hItem;
1538     siblingHandle   = upSibling->sibling;
1539     /* As well as a pointer to the upsibling sibling object */
1540     if ( (INT)upSibling->sibling != 0 )
1541       sibling = &infoPtr->items[(INT)upSibling->sibling];
1542   
1543     /* Adjust the up sibling pointer */
1544     upSibling->sibling = newItem->hItem;
1545     
1546     /* Adjust the new item pointers */
1547     newItem->upsibling = upSiblingHandle;
1548     newItem->sibling   = siblingHandle;
1549     
1550     /* Adjust the sibling pointer */
1551     if ( sibling != NULL )        
1552       sibling->upsibling = newItem->hItem; 
1553     /*
1554     else 
1555       newItem is the last of the level, nothing else to do 
1556     */
1557   }
1558   else /* Insert as first child of this parent */
1559         if (parent)
1560         parent->firstChild = newItem->hItem;
1561 }
1562
1563 /***************************************************************************
1564  * Forward the DPA local callback to the treeview owner callback
1565  */
1566 static INT WINAPI TREEVIEW_CallBackCompare( 
1567   LPVOID first, 
1568   LPVOID second, 
1569   LPARAM tvInfoPtr)
1570 {
1571   /* Forward the call to the client define callback */
1572   TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr((HWND)tvInfoPtr);
1573   return (infoPtr->pCallBackSort->lpfnCompare)(
1574     ((TREEVIEW_ITEM*)first)->lParam,
1575     ((TREEVIEW_ITEM*)second)->lParam,
1576     infoPtr->pCallBackSort->lParam);
1577 }
1578
1579 /***************************************************************************
1580  * Treeview native sort routine: sort on item text.
1581  */
1582 static INT WINAPI TREEVIEW_SortOnName ( 
1583   LPVOID first, 
1584   LPVOID second, 
1585   LPARAM tvInfoPtr)
1586 {
1587   HWND hwnd=(HWND) tvInfoPtr;
1588   char *txt1, *txt2;
1589   TREEVIEW_ITEM *item;
1590
1591         
1592   item=(TREEVIEW_ITEM *) first;
1593   if (item->pszText==LPSTR_TEXTCALLBACKA)  {
1594          TREEVIEW_SendDispInfoNotify (hwnd, item, TVN_GETDISPINFOA, TVIF_TEXT);
1595         }
1596   txt1=item->pszText;
1597
1598   item=(TREEVIEW_ITEM *) second;
1599   if (item->pszText==LPSTR_TEXTCALLBACKA)  {
1600          TREEVIEW_SendDispInfoNotify (hwnd, item, TVN_GETDISPINFOA, TVIF_TEXT);
1601         }
1602   txt2=item->pszText;
1603
1604   return -strcmp (txt1,txt2);
1605 }
1606
1607 /***************************************************************************
1608  * Setup the treeview structure with regards of the sort method
1609  * and sort the children of the TV item specified in lParam
1610  * fRecurse: currently unused. Should be zero.
1611  * parent: if pSort!=NULL, should equal pSort->hParent.
1612  *         otherwise, item which child items are to be sorted.
1613  * pSort:  sort method info. if NULL, sort on item text.
1614  *         if non-NULL, sort on item's lParam content, and let the
1615  *         application decide what that means. See also TVM_SORTCHILDRENCB.
1616  */
1617
1618 static LRESULT WINAPI TREEVIEW_Sort (
1619   HWND   hwnd, 
1620   BOOL   fRecurse, 
1621   HTREEITEM parent,
1622   LPTVSORTCB pSort 
1623   )
1624 {
1625   TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
1626   TREEVIEW_ITEM *sortMe  = NULL; /* Node for which we sort the children */
1627
1628   /* Obtain the TVSORTBC struct */
1629   infoPtr->pCallBackSort = pSort;
1630
1631         /* undocumented feature: TVI_ROOT means `sort the whole tree' */
1632
1633   if (parent==TVI_ROOT)
1634     parent=infoPtr->TopRootItem;
1635
1636   /* Check for a valid handle to the parent item */
1637   if (!TREEVIEW_ValidItem(infoPtr, parent))
1638   {
1639     ERR ("invalid item hParent=%x\n", (INT)parent);
1640     return FALSE;
1641   }
1642
1643   /* Obtain the parent node to sort */  
1644   sortMe = &infoPtr->items[ (INT)parent ];
1645
1646   /* Make sure there is something to sort */
1647   if ( sortMe->cChildren > 1 ) 
1648   {
1649     /* pointer organization */
1650     HDPA          sortList   = DPA_Create(sortMe->cChildren);
1651     HTREEITEM     itemHandle = sortMe->firstChild;
1652     TREEVIEW_ITEM *itemPtr   = & infoPtr->items[ (INT)itemHandle ];
1653
1654     /* TREEVIEW_ITEM rechaining */
1655     INT  count     = 0;
1656     VOID *item     = 0;
1657     VOID *nextItem = 0;
1658     VOID *prevItem = 0;
1659
1660     /* Build the list of item to sort */
1661     do 
1662     {
1663       DPA_InsertPtr(
1664         sortList,              /* the list */
1665         sortMe->cChildren+1,   /* force the insertion to be an append */
1666         itemPtr);              /* the ptr to store */   
1667
1668       /* Get the next sibling */
1669       itemHandle = itemPtr->sibling;
1670       itemPtr    = & infoPtr->items[ (INT)itemHandle ];
1671     } while ( itemHandle != NULL );
1672
1673     /* let DPA perform the sort activity */
1674         if (pSort) 
1675         DPA_Sort(
1676                 sortList,                  /* what  */ 
1677                 TREEVIEW_CallBackCompare,  /* how   */
1678                 hwnd);                     /* owner */
1679         else 
1680                 DPA_Sort (
1681                         sortList,                  /* what  */
1682                 TREEVIEW_SortOnName,       /* how   */
1683                         hwnd);                     /* owner */
1684
1685     /* 
1686      * Reorganized TREEVIEW_ITEM structures. 
1687      * Note that we know we have at least two elements.
1688      */
1689
1690     /* Get the first item and get ready to start... */
1691     item = DPA_GetPtr(sortList, count++);    
1692     while ( (nextItem = DPA_GetPtr(sortList, count++)) != NULL )
1693     {
1694       /* link the two current item toghether */
1695       ((TREEVIEW_ITEM*)item)->sibling       = ((TREEVIEW_ITEM*)nextItem)->hItem;
1696       ((TREEVIEW_ITEM*)nextItem)->upsibling = ((TREEVIEW_ITEM*)item)->hItem;
1697
1698       if (prevItem == NULL) /* this is the first item, update the parent */
1699       {
1700         sortMe->firstChild                = ((TREEVIEW_ITEM*)item)->hItem;
1701         ((TREEVIEW_ITEM*)item)->upsibling = NULL;
1702       }
1703       else                  /* fix the back chaining */
1704       {
1705         ((TREEVIEW_ITEM*)item)->upsibling = ((TREEVIEW_ITEM*)prevItem)->hItem;
1706       }
1707
1708       /* get ready for the next one */
1709       prevItem = item; 
1710       item     = nextItem;
1711     }
1712
1713     /* the last item is pointed to by item and never has a sibling */
1714     ((TREEVIEW_ITEM*)item)->sibling = NULL; 
1715
1716     DPA_Destroy(sortList);
1717
1718     return TRUE;
1719   }
1720   return FALSE;
1721 }
1722
1723
1724 /***************************************************************************
1725  * Setup the treeview structure with regards of the sort method
1726  * and sort the children of the TV item specified in lParam
1727  */
1728 static LRESULT WINAPI TREEVIEW_SortChildrenCB(
1729   HWND   hwnd, 
1730   WPARAM wParam, 
1731   LPARAM lParam
1732   )
1733 {
1734  LPTVSORTCB pSort=(LPTVSORTCB) lParam;
1735
1736  return TREEVIEW_Sort (hwnd, wParam, pSort->hParent, pSort);
1737 }
1738
1739
1740 /***************************************************************************
1741  * Sort the children of the TV item specified in lParam.
1742  */
1743 static LRESULT WINAPI TREEVIEW_SortChildren (
1744   HWND   hwnd, 
1745   WPARAM wParam, 
1746   LPARAM lParam)
1747 {
1748  return TREEVIEW_Sort (hwnd, (BOOL) wParam, (HTREEITEM) lParam, NULL);
1749 }
1750
1751
1752
1753 /* the method used below isn't the most memory-friendly, but it avoids 
1754    a lot of memory reallocations */ 
1755
1756 /* BTW: we waste handle 0; 0 is not an allowed handle. */
1757
1758 static LRESULT
1759 TREEVIEW_InsertItemA (HWND hwnd, WPARAM wParam, LPARAM lParam)
1760
1761 {
1762   TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
1763   TVINSERTSTRUCTA  *ptdi;
1764   TVITEMEXA     *tvItem;
1765   TREEVIEW_ITEM *wineItem, *parentItem, *prevsib, *sibItem;
1766   INT           iItem,listItems,i,len;
1767  
1768   /* Item to insert */
1769   ptdi = (LPTVINSERTSTRUCTA) lParam;
1770
1771         /* check if memory is available */
1772
1773   if (infoPtr->uNumPtrsAlloced==0) {
1774         infoPtr->items = COMCTL32_Alloc (TVITEM_ALLOC*sizeof (TREEVIEW_ITEM));
1775         infoPtr->freeList= COMCTL32_Alloc ((1+(TVITEM_ALLOC>>5)) * sizeof (INT));
1776         infoPtr->uNumPtrsAlloced=TVITEM_ALLOC;
1777         infoPtr->TopRootItem=(HTREEITEM)1;
1778    }
1779
1780   /* 
1781    * Reallocate contiguous space for items 
1782    */
1783   if (infoPtr->uNumItems == (infoPtr->uNumPtrsAlloced-1) ) {
1784         TREEVIEW_ITEM *oldItems = infoPtr->items;
1785         INT *oldfreeList = infoPtr->freeList;
1786
1787         infoPtr->uNumPtrsAlloced*=2;
1788     infoPtr->items = COMCTL32_Alloc (infoPtr->uNumPtrsAlloced*sizeof (TREEVIEW_ITEM));
1789     infoPtr->freeList= COMCTL32_Alloc ((1+(infoPtr->uNumPtrsAlloced>>5))*sizeof (INT));
1790
1791     memcpy (&infoPtr->items[0], &oldItems[0],
1792                     infoPtr->uNumPtrsAlloced/2 * sizeof(TREEVIEW_ITEM));
1793     memcpy (&infoPtr->freeList[0], &oldfreeList[0],
1794                     (infoPtr->uNumPtrsAlloced>>6) * sizeof(INT));
1795
1796     COMCTL32_Free (oldItems);  
1797     COMCTL32_Free (oldfreeList);  
1798    }
1799
1800   /* 
1801    * Reset infoPtr structure with new stat according to current TV picture
1802    */
1803   iItem=0;
1804   infoPtr->uNumItems++;
1805   if ((INT)infoPtr->uMaxHandle==(infoPtr->uNumItems-1))  { 
1806         iItem=infoPtr->uNumItems;
1807         infoPtr->uMaxHandle = (HTREEITEM)((INT)infoPtr->uMaxHandle + 1);
1808   } else {                                       /* check freelist */
1809         for (i=0; i<=infoPtr->uNumPtrsAlloced>>5; i++) {
1810                 if (infoPtr->freeList[i]) {
1811                         iItem=ffs (infoPtr->freeList[i])-1;
1812                         tv_clear_bit(iItem,&infoPtr->freeList[i]);
1813                         iItem+=i<<5;
1814                         break;
1815                 }
1816     } 
1817   }
1818
1819   if (TRACE_ON(treeview)) { 
1820     for (i=0; i<=infoPtr->uNumPtrsAlloced>>5; i++) 
1821             TRACE("%8x\n",infoPtr->freeList[i]);
1822   }
1823
1824   if (!iItem) ERR("Argh -- can't find free item.\n");
1825
1826   /* 
1827    * Find the parent item of the new item 
1828    */  
1829   tvItem= & ptdi->DUMMYUNIONNAME.itemex;
1830   wineItem=& infoPtr->items[iItem];
1831
1832   if ((ptdi->hParent==TVI_ROOT) || (ptdi->hParent==0)) {
1833     parentItem       = NULL;
1834     wineItem->parent = 0; 
1835     sibItem          = &infoPtr->items [(INT)infoPtr->TopRootItem];
1836     listItems        = infoPtr->uNumItems;
1837   }
1838   else  {
1839         parentItem = &infoPtr->items[(INT)ptdi->hParent];
1840   
1841     /* Do the insertion here it if it's the only item of this parent */
1842         if (!parentItem->firstChild) 
1843                 parentItem->firstChild=(HTREEITEM)iItem;
1844   
1845         wineItem->parent = ptdi->hParent;
1846         sibItem          = &infoPtr->items [(INT)parentItem->firstChild];
1847         listItems        = parentItem->cChildren;
1848         parentItem->cChildren++;
1849   }
1850
1851   
1852   /* NOTE: I am moving some setup of the wineItem object that was initialy 
1853    *       done at the end of the function since some of the values are 
1854    *       required by the Callback sorting 
1855    */
1856
1857   if (tvItem->mask & TVIF_TEXT) 
1858   {
1859     /*
1860      * Setup the item text stuff here since it's required by the Sort method
1861      * when the insertion are ordered
1862      */
1863     if (tvItem->pszText!=LPSTR_TEXTCALLBACKA) 
1864     {
1865       TRACE("(%p,%s)\n", &tvItem->pszText, tvItem->pszText); 
1866       len = lstrlenA (tvItem->pszText)+1;
1867       wineItem->pszText= COMCTL32_Alloc (len+1);
1868       lstrcpyA (wineItem->pszText, tvItem->pszText);
1869       wineItem->cchTextMax=len;
1870     }
1871     else 
1872     {
1873       TRACE("LPSTR_TEXTCALLBACK\n");
1874       wineItem->pszText = LPSTR_TEXTCALLBACKA;
1875       wineItem->cchTextMax = 0;
1876     }
1877   }
1878
1879   if (tvItem->mask & TVIF_PARAM) 
1880     wineItem->lParam=tvItem->lParam;
1881
1882
1883   wineItem->upsibling=0;  /* needed in case we're the first item in a list */ 
1884   wineItem->sibling=0;     
1885   wineItem->firstChild=0;
1886   wineItem->hItem=(HTREEITEM)iItem;
1887
1888   if (listItems!=0) {
1889      prevsib=NULL;
1890
1891      switch ((DWORD) ptdi->hInsertAfter) {
1892                 case (DWORD) TVI_FIRST: 
1893                         if (sibItem==wineItem) break;
1894                         if (wineItem->parent) {
1895                                 wineItem->sibling=parentItem->firstChild;
1896                                 parentItem->firstChild=(HTREEITEM)iItem;
1897                         } else {
1898                                 wineItem->sibling=infoPtr->TopRootItem;
1899                                 infoPtr->TopRootItem=(HTREEITEM)iItem;
1900                         }
1901                         sibItem->upsibling=(HTREEITEM)iItem;
1902                         break;
1903
1904                 case (DWORD) TVI_SORT:  
1905           if (sibItem==wineItem) 
1906         /* 
1907          * This item is the first child of the level and it 
1908          * has already been inserted 
1909          */                
1910         break; 
1911       else
1912       {
1913         TREEVIEW_ITEM *aChild;
1914                   
1915   
1916         TREEVIEW_ITEM *previousChild = NULL;
1917         BOOL bItemInserted           = FALSE;
1918
1919             if (parentItem)
1920           aChild = &infoPtr->items[(INT)parentItem->firstChild];
1921                 else 
1922           aChild = &infoPtr->items[(INT)infoPtr->TopRootItem];
1923   
1924         /* lookup the text if using LPSTR_TEXTCALLBACKs */
1925         if (wineItem->pszText==LPSTR_TEXTCALLBACKA) {
1926           TREEVIEW_SendDispInfoNotify (hwnd, wineItem, TVN_GETDISPINFOA, TVIF_TEXT);
1927         }
1928     
1929         /* Iterate the parent children to see where we fit in */
1930         while ( aChild != NULL )
1931         {
1932           INT comp;
1933
1934           /* lookup the text if using LPSTR_TEXTCALLBACKs */
1935           if (aChild->pszText==LPSTR_TEXTCALLBACKA) {
1936             TREEVIEW_SendDispInfoNotify (hwnd, aChild, TVN_GETDISPINFOA, TVIF_TEXT);
1937           }
1938
1939           comp = strcmp(wineItem->pszText, aChild->pszText);
1940           if ( comp < 0 )  /* we are smaller than the current one */
1941           {
1942             TREEVIEW_InsertBefore(infoPtr, wineItem, aChild, parentItem);
1943             bItemInserted = TRUE;
1944             break;
1945           }
1946           else if ( comp > 0 )  /* we are bigger than the current one */
1947           {
1948             previousChild = aChild;
1949             aChild = (aChild->sibling == 0)  /* This will help us to exit   */
1950                         ? NULL               /* if there is no more sibling */
1951                         : &infoPtr->items[(INT)aChild->sibling];
1952   
1953             /* Look at the next item */
1954             continue;
1955           }
1956           else if ( comp == 0 )
1957           {
1958             /* 
1959              * An item with this name is already existing, therefore,  
1960              * we add after the one we found 
1961              */
1962             TREEVIEW_InsertAfter(infoPtr, wineItem, aChild, parentItem);
1963             bItemInserted = TRUE;
1964             break;
1965           }
1966         }
1967       
1968         /* 
1969          * we reach the end of the child list and the item as not
1970          * yet been inserted, therefore, insert it after the last child.
1971          */
1972         if ( (! bItemInserted ) && (aChild == NULL) )
1973           TREEVIEW_InsertAfter(infoPtr, wineItem, previousChild, parentItem);
1974   
1975         break;
1976       }
1977
1978
1979                 case (DWORD) TVI_LAST:  
1980                         if (sibItem==wineItem) break;
1981                         while (sibItem->sibling) {
1982                                 prevsib=sibItem;
1983                                 sibItem=&infoPtr->items [(INT)sibItem->sibling];
1984                         }
1985                         sibItem->sibling=(HTREEITEM)iItem;
1986                         wineItem->upsibling=sibItem->hItem;
1987                         break;
1988                 default:
1989                     {
1990                         TREEVIEW_ITEM  *localsibItem = sibItem;
1991                         while ((localsibItem->sibling) && 
1992                                (localsibItem->hItem!=ptdi->hInsertAfter))
1993                                 {
1994                                 prevsib=localsibItem;
1995                 localsibItem=&infoPtr->items [(INT)localsibItem->sibling];
1996               }
1997                         if (localsibItem->hItem!=ptdi->hInsertAfter) {
1998                          WARN("tried to insert item after nonexisting handle %d treating as TVI_LAST.\n",
1999                       (INT) ptdi->hInsertAfter);
2000                        /* 
2001                         * retry placing it last 
2002                         */
2003                         if (sibItem==wineItem) break;
2004                          while (sibItem->sibling) {
2005                                  prevsib=sibItem;
2006                                  sibItem=&infoPtr->items [(INT)sibItem->sibling];
2007                          }
2008                          sibItem->sibling=(HTREEITEM)iItem;
2009                          wineItem->upsibling=sibItem->hItem;
2010                           break;
2011                         }
2012                         prevsib=localsibItem;
2013                         if (localsibItem->sibling) {
2014                 localsibItem=&infoPtr->items [(INT)localsibItem->sibling];
2015                                 localsibItem->upsibling=(HTREEITEM)iItem;
2016                                 wineItem->sibling=localsibItem->hItem;
2017                         }
2018                         prevsib->sibling=(HTREEITEM)iItem;
2019                         wineItem->upsibling=prevsib->hItem;
2020                         break;
2021              }
2022         }
2023    }    
2024
2025
2026 /* Fill in info structure */
2027
2028    TRACE("new item %d; parent %d, mask %x\n", iItem, 
2029                         (INT)wineItem->parent,tvItem->mask);
2030
2031    wineItem->mask=tvItem->mask;
2032    wineItem->iIntegral=1; 
2033
2034    if (tvItem->mask & TVIF_CHILDREN) {
2035          wineItem->cChildren=tvItem->cChildren;
2036          if (tvItem->cChildren==I_CHILDRENCALLBACK) 
2037                         FIXME(" I_CHILDRENCALLBACK not supported\n");
2038         }
2039
2040   wineItem->expandBox.left   = 0; /* Initialize the expandBox */
2041   wineItem->expandBox.top    = 0;
2042   wineItem->expandBox.right  = 0;
2043   wineItem->expandBox.bottom = 0;
2044
2045    if (tvItem->mask & TVIF_IMAGE) 
2046         wineItem->iImage=tvItem->iImage;
2047
2048                 /* If the application sets TVIF_INTEGRAL without
2049                         supplying a TVITEMEX structure, it's toast */
2050
2051    if (tvItem->mask & TVIF_INTEGRAL) 
2052                 wineItem->iIntegral=tvItem->iIntegral;   
2053
2054    if (tvItem->mask & TVIF_SELECTEDIMAGE) 
2055         wineItem->iSelectedImage=tvItem->iSelectedImage;
2056
2057    if (tvItem->mask & TVIF_STATE) {
2058      TRACE("item state: %x ->%x\n", wineItem->state, tvItem->state);
2059      TRACE("statemask: %x ->%x\n", wineItem->stateMask, tvItem->stateMask);
2060         wineItem->state=tvItem->state;
2061         wineItem->stateMask=tvItem->stateMask;
2062    }
2063
2064    TREEVIEW_QueueRefresh (hwnd);
2065
2066    return (LRESULT) iItem;
2067 }
2068
2069
2070 static LRESULT
2071 TREEVIEW_InsertItemW(HWND hwnd, WPARAM wParam, LPARAM lParam)
2072 {
2073     TVINSERTSTRUCTW *tvisW;
2074     TVINSERTSTRUCTA tvisA;
2075     LRESULT lRes;
2076
2077     tvisW = (LPTVINSERTSTRUCTW)lParam;
2078
2079     tvisA.hParent = tvisW->hParent;
2080     tvisA.hInsertAfter = tvisW->hInsertAfter;
2081
2082     tvisA.DUMMYUNIONNAME.item.mask           = tvisW->DUMMYUNIONNAME.item.mask;
2083     tvisA.DUMMYUNIONNAME.item.hItem          = tvisW->DUMMYUNIONNAME.item.hItem;
2084     tvisA.DUMMYUNIONNAME.item.state          = tvisW->DUMMYUNIONNAME.item.state;
2085     tvisA.DUMMYUNIONNAME.item.stateMask      = tvisW->DUMMYUNIONNAME.item.stateMask;
2086     tvisA.DUMMYUNIONNAME.item.cchTextMax     = tvisW->DUMMYUNIONNAME.item.cchTextMax;
2087
2088     if(tvisW->DUMMYUNIONNAME.item.pszText)
2089     {
2090         if (tvisW->DUMMYUNIONNAME.item.pszText!=LPSTR_TEXTCALLBACKW) 
2091         { 
2092             int len = lstrlenW (tvisW->DUMMYUNIONNAME.item.pszText)+1;
2093             tvisA.DUMMYUNIONNAME.item.pszText = COMCTL32_Alloc (len);
2094             lstrcpyWtoA (tvisA.DUMMYUNIONNAME.item.pszText,
2095                          tvisW->DUMMYUNIONNAME.item.pszText );
2096         }
2097         else 
2098         {
2099             tvisA.DUMMYUNIONNAME.item.pszText = LPSTR_TEXTCALLBACKA;
2100             tvisA.DUMMYUNIONNAME.item.cchTextMax = 0;
2101         }
2102     }
2103
2104     tvisA.DUMMYUNIONNAME.item.iImage         = tvisW->DUMMYUNIONNAME.item.iImage;
2105     tvisA.DUMMYUNIONNAME.item.iSelectedImage = tvisW->DUMMYUNIONNAME.item.iSelectedImage;
2106     tvisA.DUMMYUNIONNAME.item.cChildren      = tvisW->DUMMYUNIONNAME.item.cChildren;
2107     tvisA.DUMMYUNIONNAME.item.lParam         = tvisW->DUMMYUNIONNAME.item.lParam;
2108
2109     lRes = TREEVIEW_InsertItemA(hwnd,wParam,(LPARAM)&tvisA);
2110
2111     if (tvisA.DUMMYUNIONNAME.item.pszText!=LPSTR_TEXTCALLBACKA) 
2112     {
2113         COMCTL32_Free(tvisA.DUMMYUNIONNAME.item.pszText);
2114     }
2115
2116     return lRes;
2117
2118 }
2119
2120
2121 static LRESULT
2122 TREEVIEW_DeleteItem (HWND hwnd, WPARAM wParam, LPARAM lParam)
2123 {
2124   TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
2125   INT iItem;
2126   TREEVIEW_ITEM *wineItem;
2127
2128   TRACE("item = %08lx\n", lParam);
2129
2130   if (lParam == (INT)TVI_ROOT) {
2131         TREEVIEW_RemoveTree (hwnd);
2132   } else {
2133         iItem= (INT) lParam;
2134         wineItem = TREEVIEW_ValidItem (infoPtr, (HTREEITEM)iItem);
2135         if (!wineItem) return FALSE;
2136
2137         if (wineItem->pszText==LPSTR_TEXTCALLBACKA)
2138            TRACE("LPSTR_TEXTCALLBACK\n");
2139         else
2140            TRACE("%s\n",wineItem->pszText);
2141         TREEVIEW_RemoveItem (hwnd, wineItem);
2142   }
2143
2144   TREEVIEW_QueueRefresh (hwnd);
2145
2146   return TRUE;
2147 }
2148
2149
2150
2151 static LRESULT
2152 TREEVIEW_GetIndent (HWND hwnd)
2153 {
2154  TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
2155
2156  TRACE("\n");
2157  return infoPtr->uIndent;
2158 }
2159
2160 static LRESULT
2161 TREEVIEW_SetIndent (HWND hwnd, WPARAM wParam)
2162 {
2163   TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
2164   INT newIndent;
2165    
2166   TRACE("\n");
2167   newIndent=(INT) wParam;
2168   if (newIndent < MINIMUM_INDENT) newIndent=MINIMUM_INDENT;
2169   infoPtr->uIndent=newIndent;
2170   
2171   return 0;
2172 }
2173
2174 static LRESULT
2175 TREEVIEW_GetToolTips (HWND hwnd)
2176
2177 {
2178  TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
2179
2180  TRACE("\n");
2181  return infoPtr->hwndToolTip;
2182 }
2183
2184
2185 static LRESULT
2186 TREEVIEW_SetToolTips (HWND hwnd, WPARAM wParam)
2187
2188 {
2189  TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
2190  HWND prevToolTip;
2191
2192  TRACE("\n");
2193  prevToolTip=infoPtr->hwndToolTip;
2194  infoPtr->hwndToolTip= (HWND) wParam;
2195
2196  return prevToolTip;
2197 }
2198
2199
2200 static LRESULT CALLBACK
2201 TREEVIEW_GetEditControl (HWND hwnd)
2202
2203 {
2204  TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
2205
2206  return infoPtr->hwndEdit;
2207 }
2208
2209 LRESULT CALLBACK
2210 TREEVIEW_Edit_SubclassProc (HWND hwnd, UINT uMsg, WPARAM wParam, 
2211                                                         LPARAM lParam)
2212 {
2213   switch (uMsg) 
2214   {
2215     case WM_ERASEBKGND: 
2216     {
2217       RECT rc;
2218       HDC  hdc = (HDC) wParam;
2219       GetClientRect (hwnd, &rc);
2220       Rectangle (hdc, rc.left, rc.top, rc.right, rc.bottom);
2221       return -1;
2222     }
2223
2224     case WM_GETDLGCODE:
2225     {
2226       return DLGC_WANTARROWS | DLGC_WANTALLKEYS;
2227     }
2228
2229     case WM_KEYDOWN:
2230         if (wParam == VK_ESCAPE)
2231 {
2232             TREEVIEW_EndEditLabelNow(GetParent(hwnd), (WPARAM)TRUE, 0); 
2233             return 1;
2234 }
2235         else if (wParam == VK_RETURN)
2236             TREEVIEW_EndEditLabelNow(GetParent(hwnd), (WPARAM)FALSE, 0); 
2237     break; 
2238
2239
2240
2241     default:
2242     {
2243       TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(GetParent(hwnd));
2244           if (infoPtr!=NULL)
2245        return CallWindowProcA (infoPtr->wpEditOrig, hwnd, uMsg, wParam, lParam);
2246           else 
2247                 break;
2248                 
2249     }
2250   }
2251
2252   return 0;
2253 }
2254
2255
2256 /* should handle edit control messages here */
2257
2258 static LRESULT
2259 TREEVIEW_Command (HWND hwnd, WPARAM wParam, LPARAM lParam)
2260
2261 {
2262   TRACE("%x %ld\n",wParam, lParam);
2263  
2264   switch (HIWORD(wParam)) 
2265   {
2266                 case EN_UPDATE:
2267     {
2268       /* 
2269        * Adjust the edit window size 
2270        */
2271       TREEVIEW_INFO *infoPtr  = TREEVIEW_GetInfoPtr(hwnd);
2272       TREEVIEW_ITEM *editItem = TREEVIEW_ValidItem(infoPtr, infoPtr->editItem);
2273       INT           iLength   = GetWindowTextLengthA(infoPtr->hwndEdit);
2274       HDC           hdc       = GetDC(infoPtr->hwndEdit);
2275       TEXTMETRICA   tm;
2276
2277       if ( GetTextMetricsA(hdc, &tm) )
2278       {
2279         LONG newWidth = (iLength * tm.tmAveCharWidth) + 15;
2280             
2281                 SetWindowPos ( 
2282           infoPtr->hwndEdit,
2283           HWND_TOP, 
2284           editItem->text.left - 2, 
2285           editItem->text.top  - 1,
2286           newWidth,
2287           editItem->text.bottom - editItem->text.top  + 3,
2288           SWP_DRAWFRAME );
2289       }
2290       ReleaseDC(hwnd, hdc);
2291
2292       break;
2293     }
2294
2295     case EN_KILLFOCUS:
2296 /*      TREEVIEW_EndEditLabelNow(hwnd, (WPARAM)FALSE, 0); 
2297 */
2298       break;
2299
2300     default:
2301       return SendMessageA (GetParent (hwnd), WM_COMMAND, wParam, lParam);
2302   }
2303
2304   return 0;
2305 }
2306
2307 static LRESULT
2308 TREEVIEW_Size (HWND hwnd, WPARAM wParam, LPARAM lParam)
2309
2310 {
2311   TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
2312
2313   if (infoPtr->bAutoSize) 
2314   {
2315     infoPtr->bAutoSize = FALSE;
2316     return 0;
2317   }
2318   infoPtr->bAutoSize = TRUE;
2319
2320   if (wParam == SIZE_RESTORED)  
2321   {
2322     infoPtr->uTotalWidth  = LOWORD (lParam);
2323         infoPtr->uTotalHeight = HIWORD (lParam);
2324   } else {
2325         FIXME("WM_SIZE flag %x %lx not handled\n", wParam, lParam);
2326   }
2327
2328   TREEVIEW_QueueRefresh (hwnd);
2329   return 0;
2330 }
2331
2332
2333
2334 static LRESULT
2335 TREEVIEW_StyleChanged (HWND hwnd, WPARAM wParam, LPARAM lParam)
2336 {
2337   HDC hdc;
2338   
2339   TRACE("(%x %lx)\n",wParam,lParam);
2340   hdc = GetDC (hwnd);
2341   TREEVIEW_Refresh (hwnd, hdc);
2342   ReleaseDC(hwnd,hdc);
2343
2344   return 0;
2345 }
2346
2347 static LRESULT
2348 TREEVIEW_Create (HWND hwnd, WPARAM wParam, LPARAM lParam)
2349 {
2350     TREEVIEW_INFO *infoPtr;
2351         DWORD dwStyle = GetWindowLongA (hwnd, GWL_STYLE);
2352         LOGFONTA logFont;
2353     TEXTMETRICA tm;
2354         HDC hdc;
2355   
2356     TRACE("wnd %x, style %lx\n",hwnd,dwStyle);
2357       /* allocate memory for info structure */
2358     infoPtr = (TREEVIEW_INFO *) COMCTL32_Alloc (sizeof(TREEVIEW_INFO));
2359
2360     SetWindowLongA( hwnd, 0, (DWORD)infoPtr);
2361
2362     if (infoPtr == NULL) {
2363                 ERR("could not allocate info memory!\n");
2364                 return 0;
2365     }
2366
2367     if ((TREEVIEW_INFO*) GetWindowLongA( hwnd, 0) != infoPtr) {
2368                 ERR("pointer assignment error!\n");
2369                 return 0;
2370     }
2371
2372         hdc=GetDC (hwnd);
2373
2374     /* set default settings */
2375     infoPtr->uInternalStatus=0;
2376     infoPtr->uNumItems=0;
2377     infoPtr->clrBk   = GetSysColor (COLOR_WINDOW);
2378     infoPtr->clrText = GetSysColor (COLOR_WINDOWTEXT);
2379     infoPtr->clrLine = GetSysColor (COLOR_WINDOWTEXT);
2380     infoPtr->clrInsertMark = GetSysColor (COLOR_BTNTEXT);
2381     infoPtr->cy = 0;
2382     infoPtr->cx = 0;
2383     infoPtr->uIndent = 15;
2384     infoPtr->himlNormal = NULL;
2385     infoPtr->himlState = NULL;
2386         infoPtr->uItemHeight = -1;
2387     GetTextMetricsA (hdc, &tm);
2388     infoPtr->hFont = GetStockObject (DEFAULT_GUI_FONT);
2389         GetObjectA (infoPtr->hFont, sizeof (LOGFONTA), &logFont);
2390         logFont.lfWeight=FW_BOLD;
2391     infoPtr->hBoldFont = CreateFontIndirectA (&logFont);
2392     
2393     infoPtr->items = NULL;
2394     infoPtr->selectedItem=0;
2395     infoPtr->clrText=-1;        /* use system color */
2396     infoPtr->dropItem=0;
2397         infoPtr->insertMarkItem=0;
2398         infoPtr->insertBeforeorAfter=0;
2399     infoPtr->pCallBackSort=NULL;
2400     infoPtr->uScrollTime = 300;  /* milliseconds */
2401         infoPtr->wpEditOrig = NULL; /* we haven't subclassed anything yet */
2402
2403         infoPtr->hwndToolTip=0;
2404     if (!(dwStyle & TVS_NOTOOLTIPS)) {   /* Create tooltip control */
2405                 TTTOOLINFOA ti;
2406
2407                 infoPtr->hwndToolTip =  
2408                         CreateWindowExA (0, TOOLTIPS_CLASSA, NULL, 0,
2409                    CW_USEDEFAULT, CW_USEDEFAULT,
2410                    CW_USEDEFAULT, CW_USEDEFAULT,
2411                    hwnd, 0, 0, 0);
2412
2413         /* Send NM_TOOLTIPSCREATED notification */
2414         if (infoPtr->hwndToolTip) {
2415             NMTOOLTIPSCREATED nmttc;
2416
2417             nmttc.hdr.hwndFrom = hwnd;
2418             nmttc.hdr.idFrom =  GetWindowLongA( hwnd, GWL_ID);
2419             nmttc.hdr.code = NM_TOOLTIPSCREATED;
2420             nmttc.hwndToolTips = infoPtr->hwndToolTip;
2421
2422             SendMessageA (GetParent (hwnd), WM_NOTIFY,
2423                 (WPARAM) GetWindowLongA( hwnd, GWL_ID), (LPARAM)&nmttc);
2424         }
2425
2426                 ZeroMemory (&ti, sizeof(TTTOOLINFOA));
2427         ti.cbSize   = sizeof(TTTOOLINFOA);
2428         ti.uFlags   = TTF_IDISHWND | TTF_TRACK | TTF_TRANSPARENT ;
2429         ti.hwnd     = hwnd;
2430         ti.uId      = 0;
2431         ti.lpszText = "Test"; /* LPSTR_TEXTCALLBACK; */
2432         SetRectEmpty (&ti.rect);
2433
2434         SendMessageA (infoPtr->hwndToolTip, TTM_ADDTOOLA, 0, (LPARAM)&ti);
2435     }
2436
2437         infoPtr->hwndEdit = CreateWindowExA ( 
2438                           WS_EX_LEFT, 
2439                           "EDIT",
2440                           0,
2441                           WS_CHILD | WS_BORDER | ES_AUTOHSCROLL | 
2442                           ES_WANTRETURN | ES_LEFT,
2443                           0, 0, 0, 0,
2444                           hwnd, 
2445                           0,0,0); /* FIXME: (HMENU)IDTVEDIT,pcs->hInstance,0);*/
2446
2447   SendMessageA ( infoPtr->hwndEdit, WM_SETFONT, infoPtr->hFont, FALSE);
2448         infoPtr->wpEditOrig = (WNDPROC)SetWindowLongA (
2449                                     infoPtr->hwndEdit,
2450                                     GWL_WNDPROC, 
2451                                                                 (LONG) TREEVIEW_Edit_SubclassProc);
2452
2453   if (dwStyle & TVS_CHECKBOXES) {       
2454                 HBITMAP hbmLoad;
2455                 int nIndex;
2456
2457                 infoPtr->himlState = 
2458              ImageList_Create (16, 16,ILC_COLOR|ILC_MASK, 15, 1);
2459
2460                 hbmLoad = LoadBitmapA (COMCTL32_hModule, MAKEINTRESOURCEA(IDT_CHECK));
2461                 TRACE ("%x\n",hbmLoad);
2462         nIndex = ImageList_AddMasked (infoPtr->himlState, hbmLoad, CLR_DEFAULT);
2463                 TRACE ("%d\n",nIndex);
2464                 DeleteObject (hbmLoad);
2465         }
2466   ReleaseDC (hwnd, hdc);
2467   return 0;
2468 }
2469
2470
2471
2472 static LRESULT 
2473 TREEVIEW_Destroy (HWND hwnd) 
2474 {
2475   TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
2476      
2477   TRACE("\n");
2478   TREEVIEW_RemoveTree (hwnd);
2479   SetWindowLongA (hwnd, 0, (DWORD)NULL);
2480
2481   if (infoPtr->Timer & TV_REFRESH_TIMER_SET) 
2482         KillTimer (hwnd, TV_REFRESH_TIMER);
2483   if (infoPtr->hwndToolTip) 
2484                 DestroyWindow (infoPtr->hwndToolTip);
2485
2486   COMCTL32_Free (infoPtr);
2487   return 0;
2488 }
2489
2490
2491 static LRESULT
2492 TREEVIEW_Paint (HWND hwnd, WPARAM wParam, LPARAM lParam)
2493 {
2494   HDC hdc;
2495   PAINTSTRUCT ps;
2496
2497   TRACE("\n");
2498   hdc = wParam==0 ? BeginPaint (hwnd, &ps) : (HDC)wParam;
2499   TREEVIEW_Refresh (hwnd, hdc);
2500   if(!wParam) EndPaint (hwnd, &ps);
2501   TRACE("done\n");
2502       
2503   return 0;
2504 }
2505
2506 static LRESULT
2507 TREEVIEW_SetFocus (HWND hwnd, WPARAM wParam, LPARAM lParam)
2508 {
2509    TREEVIEW_SendSimpleNotify (hwnd, NM_SETFOCUS);
2510    InvalidateRect(hwnd, NULL, FALSE);
2511    return 0;
2512 }
2513
2514 static LRESULT
2515 TREEVIEW_KillFocus (HWND hwnd, WPARAM wParam, LPARAM lParam)
2516 {
2517    TREEVIEW_SendSimpleNotify (hwnd, NM_KILLFOCUS);
2518    InvalidateRect(hwnd, NULL, FALSE);
2519    return 0;
2520 }
2521
2522 static LRESULT
2523 TREEVIEW_EraseBackground (HWND hwnd, WPARAM wParam, LPARAM lParam)
2524 {
2525     TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
2526     HBRUSH hBrush = CreateSolidBrush (infoPtr->clrBk);
2527     RECT rect;
2528
2529     TRACE("\n");
2530     GetClientRect (hwnd, &rect);
2531     FillRect ((HDC)wParam, &rect, hBrush);
2532     DeleteObject (hBrush);
2533     return TRUE;
2534 }
2535
2536
2537
2538
2539
2540   
2541 /* Notifications */
2542
2543   
2544
2545
2546
2547 static BOOL
2548 TREEVIEW_SendSimpleNotify (HWND hwnd, UINT code)
2549 {
2550     NMHDR nmhdr;
2551
2552     TRACE("%x\n",code);
2553     nmhdr.hwndFrom = hwnd;
2554     nmhdr.idFrom   = GetWindowLongA( hwnd, GWL_ID);
2555     nmhdr.code     = code;
2556
2557     return (BOOL) SendMessageA (GetParent (hwnd), WM_NOTIFY,
2558                                    (WPARAM)nmhdr.idFrom, (LPARAM)&nmhdr);
2559 }
2560
2561
2562
2563 static BOOL
2564 TREEVIEW_SendTreeviewNotify (HWND hwnd, UINT code, UINT action, 
2565                         HTREEITEM oldItem, HTREEITEM newItem)
2566
2567 {
2568   TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
2569   NMTREEVIEWA nmhdr;
2570   TREEVIEW_ITEM  *wineItem;
2571
2572   TRACE("code:%x action:%x olditem:%x newitem:%x\n",
2573                   code,action,(INT)oldItem,(INT)newItem);
2574   nmhdr.hdr.hwndFrom = hwnd;
2575   nmhdr.hdr.idFrom =  GetWindowLongA( hwnd, GWL_ID);
2576   nmhdr.hdr.code = code;
2577   nmhdr.action = action;
2578   if (oldItem) {
2579         wineItem=& infoPtr->items[(INT)oldItem];
2580         nmhdr.itemOld.mask              = wineItem->mask;
2581         nmhdr.itemOld.hItem             = wineItem->hItem;
2582         nmhdr.itemOld.state             = wineItem->state;
2583         nmhdr.itemOld.stateMask = wineItem->stateMask;
2584         nmhdr.itemOld.iImage    = wineItem->iImage;
2585         nmhdr.itemOld.pszText   = wineItem->pszText;
2586         nmhdr.itemOld.cchTextMax= wineItem->cchTextMax;
2587         nmhdr.itemOld.iImage    = wineItem->iImage;
2588         nmhdr.itemOld.iSelectedImage    = wineItem->iSelectedImage;
2589         nmhdr.itemOld.cChildren = wineItem->cChildren;
2590         nmhdr.itemOld.lParam    = wineItem->lParam;
2591   }
2592
2593   if (newItem) {
2594         wineItem=& infoPtr->items[(INT)newItem];
2595         nmhdr.itemNew.mask              = wineItem->mask;
2596         nmhdr.itemNew.hItem             = wineItem->hItem;
2597         nmhdr.itemNew.state             = wineItem->state;
2598         nmhdr.itemNew.stateMask = wineItem->stateMask;
2599         nmhdr.itemNew.iImage    = wineItem->iImage;
2600         nmhdr.itemNew.pszText   = wineItem->pszText;
2601         nmhdr.itemNew.cchTextMax= wineItem->cchTextMax;
2602         nmhdr.itemNew.iImage    = wineItem->iImage;
2603         nmhdr.itemNew.iSelectedImage    = wineItem->iSelectedImage;
2604         nmhdr.itemNew.cChildren = wineItem->cChildren;
2605         nmhdr.itemNew.lParam    = wineItem->lParam;
2606   }
2607
2608   nmhdr.ptDrag.x = 0;
2609   nmhdr.ptDrag.y = 0;
2610
2611   return (BOOL)SendMessageA (GetParent (hwnd), WM_NOTIFY,
2612                                    (WPARAM) GetWindowLongA( hwnd, GWL_ID), (LPARAM)&nmhdr);
2613
2614 }
2615
2616 static BOOL
2617 TREEVIEW_SendTreeviewDnDNotify (HWND hwnd, UINT code, HTREEITEM dragItem, 
2618                                                                 POINT pt)
2619 {
2620   TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
2621   NMTREEVIEWA nmhdr;
2622   TREEVIEW_ITEM  *wineItem;
2623
2624   TRACE("code:%x dragitem:%x\n", code,(INT)dragItem);
2625
2626   nmhdr.hdr.hwndFrom = hwnd;
2627   nmhdr.hdr.idFrom =  GetWindowLongA( hwnd, GWL_ID);
2628   nmhdr.hdr.code = code;
2629   nmhdr.action = 0;
2630   wineItem=& infoPtr->items[(INT)dragItem];
2631   nmhdr.itemNew.mask    = wineItem->mask;
2632   nmhdr.itemNew.hItem   = wineItem->hItem;
2633   nmhdr.itemNew.state   = wineItem->state;
2634   nmhdr.itemNew.lParam  = wineItem->lParam;
2635
2636   nmhdr.ptDrag.x = pt.x;
2637   nmhdr.ptDrag.y = pt.y;
2638
2639   return (BOOL)SendMessageA (GetParent (hwnd), WM_NOTIFY,
2640                                    (WPARAM) GetWindowLongA( hwnd, GWL_ID), (LPARAM)&nmhdr);
2641
2642 }
2643
2644
2645
2646 static BOOL
2647 TREEVIEW_SendDispInfoNotify (HWND hwnd, TREEVIEW_ITEM *wineItem, 
2648                                                                 UINT code, UINT what)
2649 {
2650   NMTVDISPINFOA tvdi;
2651   BOOL retval;
2652   char *buf;
2653
2654   TRACE("item %d, action %x, state %d\n",
2655     (INT)wineItem->hItem,
2656     what,
2657     (INT)wineItem->state);
2658
2659   tvdi.hdr.hwndFrom     = hwnd;
2660   tvdi.hdr.idFrom       =  GetWindowLongA( hwnd, GWL_ID);
2661   tvdi.hdr.code         = code;
2662   tvdi.item.mask        = what;
2663   tvdi.item.hItem       = wineItem->hItem;
2664   tvdi.item.state       = wineItem->state;
2665   tvdi.item.lParam      = wineItem->lParam;
2666   tvdi.item.pszText = COMCTL32_Alloc (128*sizeof(char));
2667   tvdi.item.cchTextMax  = 128;
2668   buf = tvdi.item.pszText;
2669
2670   retval=(BOOL)SendMessageA (
2671                   GetParent(hwnd), 
2672                   WM_NOTIFY,
2673                   (WPARAM)tvdi.hdr.idFrom, 
2674                   (LPARAM)&tvdi);
2675
2676   if (what & TVIF_TEXT) {
2677                 wineItem->pszText        = tvdi.item.pszText;
2678                 if (buf==tvdi.item.pszText) {
2679                         wineItem->cchTextMax = 128;
2680                 } else { 
2681                         TRACE("user-supplied buffer\n");
2682                         COMCTL32_Free (buf);
2683                         wineItem->cchTextMax = 0;
2684                 }
2685         }
2686   if (what & TVIF_SELECTEDIMAGE) 
2687                 wineItem->iSelectedImage = tvdi.item.iSelectedImage;
2688   if (what & TVIF_IMAGE) 
2689                 wineItem->iImage         = tvdi.item.iImage;
2690   if (what & TVIF_CHILDREN) 
2691                 wineItem->cChildren      = tvdi.item.cChildren;
2692
2693  return retval;
2694 }
2695
2696
2697
2698 static BOOL
2699 TREEVIEW_SendCustomDrawNotify (HWND hwnd, DWORD dwDrawStage, HDC hdc,
2700                         RECT rc)
2701 {
2702   TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
2703   NMTVCUSTOMDRAW nmcdhdr;
2704   LPNMCUSTOMDRAW nmcd;
2705
2706   TRACE("drawstage:%lx hdc:%x\n", dwDrawStage, hdc);
2707
2708   nmcd= & nmcdhdr.nmcd;
2709   nmcd->hdr.hwndFrom = hwnd;
2710   nmcd->hdr.idFrom =  GetWindowLongA( hwnd, GWL_ID);
2711   nmcd->hdr.code   = NM_CUSTOMDRAW;
2712   nmcd->dwDrawStage= dwDrawStage;
2713   nmcd->hdc                = hdc;
2714   nmcd->rc.left    = rc.left;
2715   nmcd->rc.right   = rc.right;
2716   nmcd->rc.bottom  = rc.bottom;
2717   nmcd->rc.top     = rc.top;
2718   nmcd->dwItemSpec = 0;
2719   nmcd->uItemState = 0;
2720   nmcd->lItemlParam= 0;
2721   nmcdhdr.clrText  = infoPtr->clrText;
2722   nmcdhdr.clrTextBk= infoPtr->clrBk;
2723   nmcdhdr.iLevel   = 0;
2724
2725   return (BOOL)SendMessageA (GetParent (hwnd), WM_NOTIFY,
2726                                (WPARAM) GetWindowLongA( hwnd, GWL_ID), (LPARAM)&nmcdhdr);
2727
2728 }
2729
2730
2731
2732 /* FIXME: need to find out when the flags in uItemState need to be set */
2733
2734 static BOOL
2735 TREEVIEW_SendCustomDrawItemNotify (HWND hwnd, HDC hdc,
2736                         TREEVIEW_ITEM *wineItem, UINT uItemDrawState)
2737 {
2738  TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
2739  NMTVCUSTOMDRAW nmcdhdr;
2740  LPNMCUSTOMDRAW nmcd;
2741  DWORD dwDrawStage,dwItemSpec;
2742  UINT uItemState;
2743  INT retval;
2744  
2745  dwDrawStage=CDDS_ITEM | uItemDrawState;
2746  dwItemSpec=(DWORD)wineItem->hItem;
2747  uItemState=0;
2748  if (wineItem->hItem==infoPtr->selectedItem) uItemState|=CDIS_SELECTED;
2749  if (wineItem->hItem==infoPtr->focusItem)        uItemState|=CDIS_FOCUS;
2750  if (wineItem->hItem==infoPtr->hotItem)      uItemState|=CDIS_HOT;
2751
2752  nmcd= & nmcdhdr.nmcd;
2753  nmcd->hdr.hwndFrom = hwnd;
2754  nmcd->hdr.idFrom =  GetWindowLongA( hwnd, GWL_ID);
2755  nmcd->hdr.code   = NM_CUSTOMDRAW;
2756  nmcd->dwDrawStage= dwDrawStage;
2757  nmcd->hdc                = hdc;
2758  nmcd->rc.left    = wineItem->rect.left;
2759  nmcd->rc.right   = wineItem->rect.right;
2760  nmcd->rc.bottom  = wineItem->rect.bottom;
2761  nmcd->rc.top     = wineItem->rect.top;
2762  nmcd->dwItemSpec = dwItemSpec;
2763  nmcd->uItemState = uItemState;
2764  nmcd->lItemlParam= wineItem->lParam;
2765  nmcdhdr.clrText  = infoPtr->clrText;
2766  nmcdhdr.clrTextBk= infoPtr->clrBk;
2767  nmcdhdr.iLevel   = wineItem->iLevel;
2768
2769  TRACE("drawstage:%lx hdc:%x item:%lx, itemstate:%x, lItemlParam:%lx\n",
2770                   nmcd->dwDrawStage, nmcd->hdc, nmcd->dwItemSpec, 
2771           nmcd->uItemState, nmcd->lItemlParam);
2772
2773  retval=SendMessageA (GetParent (hwnd), WM_NOTIFY,
2774                  (WPARAM) GetWindowLongA( hwnd, GWL_ID), (LPARAM)&nmcdhdr);
2775
2776  infoPtr->clrText=nmcdhdr.clrText;
2777  infoPtr->clrBk  =nmcdhdr.clrTextBk;
2778  return (BOOL) retval;
2779 }
2780
2781
2782
2783 /* Note:If the specified item is the child of a collapsed parent item,
2784    the parent's list of child items is (recursively) expanded to reveal the 
2785    specified item. This is mentioned for TREEVIEW_SelectItem; don't 
2786    know if it also applies here.
2787 */
2788
2789 static LRESULT
2790 TREEVIEW_Expand (HWND hwnd, WPARAM wParam, LPARAM lParam)
2791 {
2792   TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
2793   TREEVIEW_ITEM *wineItem;
2794   UINT flag;
2795   INT expand;
2796   
2797   flag = (UINT) wParam;
2798   expand = (INT) lParam;
2799
2800   wineItem = TREEVIEW_ValidItem (infoPtr, (HTREEITEM)expand);
2801
2802   if (!wineItem) 
2803     return 0;
2804   if (!wineItem->cChildren) 
2805     return 0;
2806
2807         if (wineItem->pszText==LPSTR_TEXTCALLBACKA) 
2808                 TRACE ("For item %d, flags %d, state %d\n",
2809                                 expand, flag, wineItem->state);
2810         else
2811                 TRACE("For (%s) item:%d, flags %x, state:%d\n", 
2812                 wineItem->pszText, flag, expand, wineItem->state);
2813
2814   if (wineItem->cChildren==I_CHILDRENCALLBACK) {
2815     FIXME("we don't handle I_CHILDRENCALLBACK yet\n");
2816     return 0;
2817   }
2818
2819   if (flag == TVE_TOGGLE) {    /* FIXME: check exact behaviour here */
2820    flag &= ~TVE_TOGGLE;    /* ie: bitwise ops or 'case' ops */
2821    if (wineItem->state & TVIS_EXPANDED) 
2822      flag |= TVE_COLLAPSE;
2823    else
2824      flag |= TVE_EXPAND;
2825   }
2826
2827   switch (flag) 
2828   {
2829     case TVE_COLLAPSERESET: 
2830       TRACE("  case TVE_COLLAPSERESET\n");
2831       if (!wineItem->state & TVIS_EXPANDED) 
2832         return 0;
2833
2834        wineItem->state &= ~(TVIS_EXPANDEDONCE | TVIS_EXPANDED);
2835        TREEVIEW_RemoveAllChildren (hwnd, wineItem);
2836        break;
2837
2838     case TVE_COLLAPSE: 
2839       TRACE("  case TVE_COLLAPSE\n");
2840       if (!wineItem->state & TVIS_EXPANDED) 
2841         return 0;
2842
2843       wineItem->state &= ~TVIS_EXPANDED;
2844       break;
2845
2846     case TVE_EXPAND: 
2847       TRACE("  case TVE_EXPAND\n");
2848       if (wineItem->state & TVIS_EXPANDED) 
2849         return 0;
2850
2851       TRACE("  is not expanded...\n");
2852      
2853       if (!(wineItem->state & TVIS_EXPANDEDONCE))  
2854       { 
2855         TRACE("  and has never been expanded...\n");
2856         wineItem->state |= TVIS_EXPANDED;
2857
2858         /* this item has never been expanded */
2859         if (TREEVIEW_SendTreeviewNotify (
2860               hwnd, 
2861               TVN_ITEMEXPANDINGA, 
2862               TVE_EXPAND, 
2863               0, 
2864               (HTREEITEM)expand))
2865         {
2866           TRACE("  TVN_ITEMEXPANDINGA returned TRUE, exiting...\n");
2867           return FALSE;   
2868         }
2869
2870         /* FIXME
2871          * Since the TVN_ITEMEXPANDINGA message may has caused the parent to
2872          * insert new items which in turn may have cause items placeholder 
2873          * reallocation, I reassign the current item pointer so we have 
2874          * something valid to work with... 
2875          * However, this should not be necessary, 
2876          * investigation required in TREEVIEW_InsertItemA
2877          */
2878         wineItem = TREEVIEW_ValidItem (infoPtr, (HTREEITEM)expand);
2879         if (! wineItem) 
2880         { 
2881           ERR(
2882             "Catastropic situation, cannot retreive item #%d\n",
2883             expand);
2884           return FALSE;
2885         }
2886
2887         wineItem->state |= TVIS_EXPANDEDONCE;
2888         TRACE("  TVN_ITEMEXPANDINGA sent...\n");
2889
2890         TREEVIEW_SendTreeviewNotify (
2891           hwnd, 
2892           TVN_ITEMEXPANDEDA, 
2893           TVE_EXPAND, 
2894           0, 
2895           (HTREEITEM)expand);
2896
2897         TRACE("  TVN_ITEMEXPANDEDA sent...\n");
2898
2899       }
2900       else
2901       {
2902         /* this item has already been expanded */
2903         wineItem->state |= TVIS_EXPANDED;
2904       }
2905       break;
2906
2907     case TVE_EXPANDPARTIAL:
2908       TRACE("  case TVE_EXPANDPARTIAL\n");
2909       FIXME("TVE_EXPANDPARTIAL not implemented\n");
2910       wineItem->state ^=TVIS_EXPANDED;
2911       wineItem->state |=TVIS_EXPANDEDONCE;
2912       break;
2913   }
2914
2915   TRACE("Exiting, Item %d state is now %d...\n", 
2916     expand, 
2917     wineItem->state);
2918   
2919   TREEVIEW_QueueRefresh (hwnd);
2920   return TRUE;
2921 }
2922
2923
2924
2925 static TREEVIEW_ITEM *
2926 TREEVIEW_HitTestPoint (HWND hwnd, POINT pt)
2927 {
2928  TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
2929  TREEVIEW_ITEM *wineItem;
2930  RECT rect;
2931
2932  GetClientRect (hwnd, &rect);
2933
2934  if (!infoPtr->firstVisible) return NULL;
2935
2936  wineItem=&infoPtr->items [(INT)infoPtr->firstVisible];
2937
2938  while ((wineItem!=NULL) && (pt.y > wineItem->rect.bottom))
2939        wineItem=TREEVIEW_GetNextListItem (infoPtr,wineItem);
2940         
2941  if (!wineItem) 
2942         return NULL;
2943
2944  return wineItem;
2945 }
2946
2947
2948
2949
2950 static LRESULT
2951 TREEVIEW_HitTest (HWND hwnd, LPARAM lParam)
2952 {
2953   LPTVHITTESTINFO lpht=(LPTVHITTESTINFO) lParam;
2954   TREEVIEW_ITEM *wineItem;
2955   RECT rect;
2956   UINT status,x,y;
2957
2958   GetClientRect (hwnd, &rect);
2959   status=0;
2960   x=lpht->pt.x;
2961   y=lpht->pt.y;
2962   if (x < rect.left)  status|=TVHT_TOLEFT;
2963   if (x > rect.right) status|=TVHT_TORIGHT;
2964   if (y < rect.top )  status|=TVHT_ABOVE;
2965   if (y > rect.bottom) status|=TVHT_BELOW;
2966
2967   if (status) {
2968     lpht->flags=status;
2969     return 0;
2970   }
2971
2972   wineItem=TREEVIEW_HitTestPoint (hwnd, lpht->pt);
2973   if (!wineItem) {  
2974     lpht->flags=TVHT_NOWHERE;
2975     return 0;
2976   }
2977
2978   lpht->flags=0;
2979
2980   if (x < wineItem->expandBox.left) {
2981     lpht->flags |= TVHT_ONITEMINDENT;
2982         goto done;
2983   } 
2984   if ( PtInRect ( &wineItem->expandBox, lpht->pt)) {
2985     lpht->flags |= TVHT_ONITEMBUTTON;
2986         goto done;
2987   }
2988   if ( PtInRect ( &wineItem->bitmap, lpht->pt)) {
2989         lpht->flags |= TVHT_ONITEMICON;
2990     goto done;
2991   }
2992   if ( PtInRect ( &wineItem->statebitmap, lpht->pt)) {
2993         lpht->flags |= TVHT_ONITEMSTATEICON;
2994     goto done;
2995   }
2996   if ( PtInRect ( &wineItem->text, lpht->pt)) {
2997     lpht->flags |= TVHT_ONITEMLABEL;    
2998         goto done;
2999   } 
3000   
3001   lpht->flags|=TVHT_ONITEMRIGHT;
3002  
3003
3004 done:
3005   lpht->hItem=wineItem->hItem;
3006   TRACE ("(%ld,%ld):result %x\n",lpht->pt.x,lpht->pt.y,lpht->flags);
3007
3008   return (LRESULT) wineItem->hItem;
3009 }
3010
3011 static LRESULT
3012 TREEVIEW_EditLabelA (HWND hwnd, WPARAM wParam, LPARAM lParam)
3013 {
3014   TREEVIEW_INFO *infoPtr      = TREEVIEW_GetInfoPtr(hwnd);
3015   TREEVIEW_ITEM *wineItem;
3016
3017   /*
3018    * If the style allow editing...
3019    */
3020   if ( GetWindowLongA( hwnd, GWL_STYLE) & TVS_EDITLABELS )
3021   {
3022
3023     if ( infoPtr->editItem == 0 ) /* If we are not curently editing */
3024     {
3025       wineItem = TREEVIEW_ValidItem(infoPtr,(HTREEITEM) lParam);
3026       if ( wineItem == NULL )
3027         {
3028         ERR("Cannot get valid TREEVIEW_ITEM for lParam\n");
3029         return 0;
3030         }
3031
3032       TRACE("Edit started for %s.\n", wineItem->pszText);
3033       infoPtr->editItem = wineItem->hItem;
3034
3035
3036       /*
3037        * It is common practice for a windows program to get this
3038        * edit control and then subclass it. It is assumed that a
3039        * new edit control is given every time.
3040        *
3041        * As a result some programs really mess up the edit control
3042        * so we need to destory our old edit control and create a new
3043        * one. Recycling would be nice but we would need to reset
3044        * everything. So recreating may just be easyier
3045        *
3046        */
3047           DestroyWindow(infoPtr->hwndEdit);
3048           infoPtr->hwndEdit = CreateWindowExA (
3049                           WS_EX_LEFT,
3050                           "EDIT",
3051                           0,
3052                           WS_CHILD | WS_BORDER | ES_AUTOHSCROLL |
3053                           ES_WANTRETURN | ES_LEFT,
3054                           0, 0, 0, 0,
3055                           hwnd,
3056                           0,0,0); /* FIXME: (HMENU)IDTVEDIT,pcs->hInstance,0);*/
3057
3058           SetWindowLongA (
3059       infoPtr->hwndEdit,
3060       GWL_WNDPROC,
3061       (LONG) TREEVIEW_Edit_SubclassProc);
3062
3063
3064       SendMessageA ( infoPtr->hwndEdit, WM_SETFONT, infoPtr->hFont, FALSE);
3065
3066       SetWindowTextA( infoPtr->hwndEdit, wineItem->pszText );
3067       SendMessageA  ( infoPtr->hwndEdit, EM_SETSEL, 0, -1 );
3068
3069       /*
3070       ** NOTE: this must be after the edit control is created
3071       ** (according to TVN_BEGINLABELEDITA docs), before position is set.
3072       */
3073       if ( TREEVIEW_SendDispInfoNotify(  /* Return true to cancel edition */
3074               hwnd,
3075               wineItem,
3076               TVN_BEGINLABELEDITA,
3077               0))
3078       {
3079         /*
3080         ** FIXME: Is this right, should we return a handle even though edit was cancelled?
3081         */
3082         TRACE("Edit cancelled by TVN_BEGINLABELEDITA for %s.\n", wineItem->pszText);
3083
3084         TREEVIEW_EndEditLabelNow(hwnd, (WPARAM)TRUE, 0);
3085
3086         return 0;
3087       }
3088
3089       SetWindowPos (
3090         infoPtr->hwndEdit,
3091         HWND_TOP,
3092         wineItem->text.left - 2,
3093         wineItem->text.top  - 1,
3094         wineItem->text.right  - wineItem->text.left + 20 ,
3095         wineItem->text.bottom - wineItem->text.top  + 3,
3096         SWP_DRAWFRAME );
3097
3098       SetFocus      ( infoPtr->hwndEdit);
3099       ShowWindow    ( infoPtr->hwndEdit, SW_SHOW);
3100     }
3101   }
3102   else
3103   {
3104   /*
3105   ** return NULL since we cannot edit this.
3106   */
3107
3108     return 0;
3109   }
3110
3111   return infoPtr->hwndEdit;
3112 }
3113
3114 static LRESULT
3115 TREEVIEW_EndEditLabelNow (HWND hwnd, WPARAM wParam, LPARAM lParam)
3116 {
3117   TREEVIEW_INFO *infoPtr    = TREEVIEW_GetInfoPtr(hwnd);
3118   TREEVIEW_ITEM *editedItem = TREEVIEW_ValidItem (infoPtr, infoPtr->editItem);
3119   BOOL          bRevert     = (BOOL)wParam;
3120   NMTVDISPINFOA tvdi;
3121   RECT          itemRect;
3122   LPSTR         oldText;
3123   LPSTR         newText;
3124   int           iLength;
3125
3126   /*
3127   ** NOTE: we have to get the new text before calling TVN_ENDLABELEDITA
3128   ** since some apps(eg regedit, win98) validate the text in TVN_ENDLABELEDITA.
3129   */
3130
3131   oldText = editedItem->pszText;
3132
3133   if ( !bRevert ) /* wParam is set to true to cancel the edition */
3134   {
3135
3136     iLength = GetWindowTextLengthA( infoPtr->hwndEdit );
3137     if (iLength == 0)
3138     {
3139       WARN("Zero length string for new label(not changing).");
3140       bRevert=TRUE;
3141     }
3142
3143     newText = COMCTL32_Alloc( iLength+1 );
3144     if( newText == NULL )
3145       {
3146       ERR("OutOfMemory, cannot allocate space for label");
3147       return FALSE;
3148       }
3149     GetWindowTextA( infoPtr->hwndEdit, newText, iLength+1);
3150   }
3151   else
3152   {
3153   newText=NULL;
3154   }
3155
3156
3157   /*
3158   * notify our parent with the new string(or NULL if wParam==TRUE)
3159   */
3160   tvdi.hdr.hwndFrom     = hwnd;
3161   tvdi.hdr.idFrom       = GetWindowLongA( hwnd, GWL_ID);
3162   tvdi.hdr.code     = TVN_ENDLABELEDITA;
3163   tvdi.item.hItem       = editedItem->hItem;
3164   tvdi.item.lParam      = editedItem->lParam;
3165   tvdi.item.mask        = TVIF_TEXT|TVIF_HANDLE|TVIF_PARAM;
3166   tvdi.item.pszText     = newText;
3167
3168   if(!SendMessageA (                /* return false to cancel edition */
3169               GetParent(hwnd),
3170               WM_NOTIFY,
3171               (WPARAM)tvdi.hdr.idFrom,
3172               (LPARAM)&tvdi))
3173   {
3174   if( newText == NULL )  /*we are supposed to ignore the return if (and pszText==NULL), MSDOCs */
3175     bRevert=TRUE;
3176   }
3177
3178   if (oldText != LPSTR_TEXTCALLBACKA)
3179   {
3180
3181     if( bRevert )
3182     {
3183     if( newText != NULL )
3184       COMCTL32_Free(newText);
3185
3186     editedItem->pszText=oldText; /* revert back to the old label */
3187     }
3188     else
3189     {
3190     COMCTL32_Free(oldText);
3191
3192     editedItem->pszText=newText; /* use the new label */
3193     }
3194   }
3195   else if( newText!=NULL )
3196   {
3197     /*
3198     ** Is really this necessary? shouldnt an app update its internal data in TVN_ENDLABELEDITA?
3199     */
3200     if( !bRevert )
3201     {
3202       /*
3203       * This is a callback string so we need
3204       * to inform the parent that the string
3205       * has changed
3206       *
3207       */
3208       tvdi.hdr.hwndFrom = hwnd;
3209       tvdi.hdr.idFrom   = GetWindowLongA( hwnd, GWL_ID);
3210       tvdi.hdr.code         = TVN_SETDISPINFOA;
3211       tvdi.item.mask    = TVIF_TEXT;
3212       tvdi.item.pszText = newText;
3213
3214       SendMessageA (
3215               GetParent(hwnd),
3216               WM_NOTIFY,
3217               (WPARAM)tvdi.hdr.idFrom,
3218               (LPARAM)&tvdi);
3219
3220     }
3221
3222   COMCTL32_Free(newText);
3223   }
3224         
3225
3226   ShowWindow(infoPtr->hwndEdit, SW_HIDE);
3227   EnableWindow(infoPtr->hwndEdit, FALSE);
3228
3229   /* update the window to eliminate fragments and the like */
3230   TreeView_GetItemRect(hwnd,infoPtr->editItem,&itemRect,FALSE);
3231   RedrawWindow(hwnd,&itemRect,0,RDW_ERASE|RDW_INVALIDATE|RDW_UPDATENOW);
3232
3233   infoPtr->editItem = 0;
3234
3235   return !bRevert; /* return true if label edit succesful, otherwise false */
3236 }
3237
3238
3239
3240 static LRESULT
3241 TREEVIEW_LButtonDoubleClick (HWND hwnd, WPARAM wParam, LPARAM lParam)
3242 {
3243   TREEVIEW_ITEM *wineItem;
3244   POINT pt;
3245
3246   TRACE("\n");
3247   pt.x = (INT)LOWORD(lParam);
3248   pt.y = (INT)HIWORD(lParam);
3249   SetFocus (hwnd);
3250
3251   wineItem=TREEVIEW_HitTestPoint (hwnd, pt);
3252   if (!wineItem) return 0;
3253   TRACE("item %d \n",(INT)wineItem->hItem);
3254  
3255   if (TREEVIEW_SendSimpleNotify (hwnd, NM_DBLCLK)!=TRUE) {     /* FIXME!*/
3256         TREEVIEW_Expand (hwnd, (WPARAM) TVE_TOGGLE, (LPARAM) wineItem->hItem);
3257  }
3258  return TRUE;
3259 }
3260
3261
3262 static LRESULT
3263 TREEVIEW_LButtonDown (HWND hwnd, WPARAM wParam, LPARAM lParam)
3264 {
3265   TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
3266   INT iItem;
3267   TVHITTESTINFO ht;
3268
3269   ht.pt.x = (INT)LOWORD(lParam);
3270   ht.pt.y = (INT)HIWORD(lParam);
3271
3272   SetFocus (hwnd);
3273   iItem=TREEVIEW_HitTest (hwnd, (LPARAM) &ht);
3274   TRACE("item %d \n",iItem);
3275
3276   if (ht.flags & TVHT_ONITEMBUTTON) {
3277     TREEVIEW_Expand (hwnd, (WPARAM) TVE_TOGGLE, (LPARAM) iItem);
3278   }
3279   else
3280   {
3281     infoPtr->uInternalStatus|=TV_LDRAG;
3282   }
3283   
3284   return 0;
3285 }
3286
3287 static LRESULT
3288 TREEVIEW_LButtonUp (HWND hwnd, WPARAM wParam, LPARAM lParam)
3289 {
3290   TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
3291   INT           iItem;
3292   TREEVIEW_ITEM *wineItem;
3293   TVHITTESTINFO ht;
3294   DWORD dwStyle = GetWindowLongA (hwnd, GWL_STYLE);
3295  
3296   ht.pt.x = (INT)LOWORD(lParam);
3297   ht.pt.y = (INT)HIWORD(lParam);
3298  
3299   TRACE("\n");
3300
3301   /* Return true to cancel default behaviour */
3302   if ( TREEVIEW_SendSimpleNotify (hwnd, NM_CLICK) )
3303     return 0;
3304
3305   /* Get the item */
3306   iItem = TREEVIEW_HitTest (hwnd, (LPARAM) &ht);
3307   TRACE ("%d\n",iItem);
3308   if (!iItem) 
3309     return 0;
3310
3311   wineItem = TREEVIEW_ValidItem(infoPtr, (HTREEITEM)iItem);
3312  
3313   /* 
3314    * if we are TVS_SINGLEEXPAND then we want this single click to 
3315    * do a bunch of things.
3316    */
3317   if ((dwStyle & TVS_SINGLEEXPAND)&&
3318       ( ht.flags & (TVHT_ONITEMLABEL | TVHT_ONITEMICON))&&
3319       ( infoPtr->editItem == 0 ))
3320   {
3321     TREEVIEW_ITEM *SelItem;
3322     /*
3323      * Send the notification
3324      */
3325     TREEVIEW_SendTreeviewNotify (hwnd,TVN_SINGLEEXPAND,0,
3326                                  (HTREEITEM)iItem,0);
3327     /*
3328      * Close the previous selection all the way to the root
3329      * as long as the new selection is not a child
3330      */
3331     
3332     if ((infoPtr->selectedItem)&&(infoPtr->selectedItem != (HTREEITEM)iItem))
3333     {
3334       BOOL closeit = TRUE;
3335       SelItem = wineItem;
3336
3337       while (closeit && SelItem)
3338       {
3339         closeit = (SelItem->hItem != infoPtr->selectedItem);
3340         SelItem = TREEVIEW_ValidItem(infoPtr,SelItem->parent);
3341       }
3342
3343       if (closeit)
3344       {
3345         SelItem = TREEVIEW_ValidItem(infoPtr,infoPtr->selectedItem);
3346         while ((SelItem)&&(SelItem->hItem != wineItem->hItem))
3347         {
3348           TREEVIEW_Expand (hwnd,(WPARAM)TVE_COLLAPSE,(LPARAM)SelItem->hItem);
3349           SelItem = TREEVIEW_ValidItem(infoPtr,SelItem->parent);
3350         }
3351       }
3352     }
3353    
3354     /*
3355      * Expand the current item 
3356      */
3357     TREEVIEW_Expand (hwnd, (WPARAM) TVE_TOGGLE, (LPARAM) wineItem->hItem);
3358   }
3359
3360   infoPtr->uInternalStatus &= ~(TV_LDRAG | TV_LDRAGGING);
3361
3362   /* 
3363    * If the style allow editing and the node is already selected 
3364    * and the click occured on the item label...
3365    */
3366   if ( ( GetWindowLongA( hwnd, GWL_STYLE) & TVS_EDITLABELS ) && 
3367        ( wineItem->state & TVIS_SELECTED ) &&
3368        ( ht.flags & TVHT_ONITEMLABEL ))
3369   {
3370     if ( infoPtr->editItem == 0 ) /* If we are not curently editing */
3371     {
3372     if( SendMessageA(hwnd, TVM_EDITLABELA, 0, (LPARAM)iItem) == 0)
3373         return 0;
3374     }
3375   }
3376   else if ( infoPtr->editItem != 0 ) /* If we are curently editing */
3377   {
3378     TREEVIEW_EndEditLabelNow(hwnd, (WPARAM)FALSE, 0);
3379   }
3380   else if ( ht.flags & (TVHT_ONITEMLABEL | TVHT_ONITEMICON))
3381   {
3382     TREEVIEW_DoSelectItem ( hwnd, TVGN_CARET, (HTREEITEM)iItem, TVC_BYMOUSE);
3383   }
3384
3385   if (ht.flags & TVHT_ONITEMSTATEICON) {
3386
3387         
3388         if (dwStyle & TVS_CHECKBOXES) {      /* TVS_CHECKBOXES requires _us_ */
3389                         int state;                                       /* to toggle the current state */
3390                         state=1-(wineItem->state>>12);
3391                         TRACE ("state:%x\n", state);
3392                         wineItem->state&= ~TVIS_STATEIMAGEMASK;
3393                         wineItem->state|=state<<12;
3394                         TRACE ("state:%x\n", wineItem->state);
3395                         TREEVIEW_QueueRefresh (hwnd);
3396                 }
3397   }
3398   return 0;
3399 }
3400
3401
3402 static LRESULT
3403 TREEVIEW_RButtonDown (HWND hwnd, WPARAM wParam, LPARAM lParam)
3404 {
3405  TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
3406
3407  TRACE("\n");
3408  infoPtr->uInternalStatus|=TV_RDRAG;
3409  return 0;
3410 }
3411
3412 static LRESULT
3413 TREEVIEW_RButtonUp (HWND hwnd, WPARAM wParam, LPARAM lParam)
3414 {
3415  TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
3416
3417  TRACE("\n");
3418  if (TREEVIEW_SendSimpleNotify (hwnd, NM_RCLICK)) return 0;
3419  infoPtr->uInternalStatus&= ~(TV_RDRAG | TV_RDRAGGING);
3420  return 0;
3421 }
3422
3423
3424 static LRESULT
3425 TREEVIEW_MouseMove (HWND hwnd, WPARAM wParam, LPARAM lParam)
3426 {
3427  TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
3428  TREEVIEW_ITEM *hotItem;
3429  POINT pt;
3430
3431  pt.x=(INT) LOWORD (lParam);
3432  pt.y=(INT) HIWORD (lParam);
3433  hotItem=TREEVIEW_HitTestPoint (hwnd, pt);
3434  if (!hotItem) return 0;
3435  infoPtr->focusItem=hotItem->hItem;
3436
3437  if ( GetWindowLongA( hwnd, GWL_STYLE) & TVS_DISABLEDRAGDROP) return 0;
3438
3439  if (infoPtr->uInternalStatus & TV_LDRAG) {
3440         TREEVIEW_SendTreeviewDnDNotify (hwnd, TVN_BEGINDRAGA, hotItem->hItem, pt);
3441         infoPtr->uInternalStatus &= ~TV_LDRAG;
3442         infoPtr->uInternalStatus |= TV_LDRAGGING;
3443         infoPtr->dropItem=hotItem->hItem;
3444         return 0;
3445  }
3446
3447  if (infoPtr->uInternalStatus & TV_RDRAG) {
3448         TREEVIEW_SendTreeviewDnDNotify (hwnd, TVN_BEGINRDRAGA, hotItem->hItem, pt);
3449         infoPtr->uInternalStatus &= ~TV_RDRAG;
3450         infoPtr->uInternalStatus |= TV_RDRAGGING;
3451         infoPtr->dropItem=hotItem->hItem;
3452         return 0;
3453  }
3454  
3455  return 0;
3456 }
3457
3458
3459 static LRESULT
3460 TREEVIEW_CreateDragImage (HWND hwnd, WPARAM wParam, LPARAM lParam)
3461 {
3462  TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
3463  TREEVIEW_ITEM *dragItem;
3464  INT cx,cy;
3465  HDC    hdc,htopdc;
3466  HWND hwtop;
3467  HBITMAP hbmp,hOldbmp;
3468  SIZE  size;
3469  RECT  rc;
3470  HFONT hOldFont;
3471  char    *itemtxt;
3472  
3473  TRACE("\n");
3474  if (!(infoPtr->himlNormal))  return 0;
3475  dragItem=TREEVIEW_ValidItem (infoPtr, (HTREEITEM) lParam);
3476  
3477  if (!dragItem) return 0;
3478
3479  if (dragItem->pszText==LPSTR_TEXTCALLBACKA) {
3480      TREEVIEW_SendDispInfoNotify (hwnd, dragItem, TVN_GETDISPINFOA, TVIF_TEXT);
3481  }
3482  itemtxt=dragItem->pszText;
3483
3484  hwtop=GetDesktopWindow ();
3485  htopdc= GetDC (hwtop);
3486  hdc=CreateCompatibleDC (htopdc); 
3487  
3488  hOldFont=SelectObject (hdc, infoPtr->hFont);
3489  GetTextExtentPoint32A (hdc, itemtxt, lstrlenA (itemtxt), &size);
3490  TRACE("%d %d %s %d\n",size.cx,size.cy,itemtxt,lstrlenA(itemtxt));
3491  hbmp=CreateCompatibleBitmap (htopdc, size.cx, size.cy);
3492  hOldbmp=SelectObject (hdc, hbmp);
3493
3494  ImageList_GetIconSize (infoPtr->himlNormal, &cx, &cy);
3495  size.cx+=cx;
3496  if (cy>size.cy) size.cy=cy;
3497
3498  infoPtr->dragList=ImageList_Create (size.cx, size.cy, ILC_COLOR, 10, 10);
3499  ImageList_Draw (infoPtr->himlNormal, dragItem->iImage, hdc, 0, 0, ILD_NORMAL);
3500
3501 /*
3502  ImageList_GetImageInfo (infoPtr->himlNormal, dragItem->hItem, &iminfo);
3503  ImageList_AddMasked (infoPtr->dragList, iminfo.hbmImage, CLR_DEFAULT);
3504 */
3505
3506 /* draw item text */
3507
3508  SetRect (&rc, cx, 0, size.cx,size.cy);
3509  DrawTextA (hdc, itemtxt, lstrlenA (itemtxt), &rc, DT_LEFT);
3510  SelectObject (hdc, hOldFont);
3511  SelectObject (hdc, hOldbmp);
3512
3513  ImageList_Add (infoPtr->dragList, hbmp, 0);
3514
3515  DeleteDC (hdc);
3516  DeleteObject (hbmp);
3517  ReleaseDC (hwtop, htopdc);
3518
3519  return (LRESULT)infoPtr->dragList;
3520 }
3521
3522
3523 static LRESULT
3524 TREEVIEW_DoSelectItem (HWND hwnd, INT action, HTREEITEM newSelect, INT cause)
3525
3526 {
3527   TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
3528   TREEVIEW_ITEM *prevItem,*wineItem;
3529   INT prevSelect;
3530
3531   wineItem = TREEVIEW_ValidItem (infoPtr, (HTREEITEM)newSelect);
3532
3533   TRACE("Entering item %d, flag %x, cause %x, state %d\n", 
3534         (INT)newSelect, 
3535         action, 
3536         cause,
3537         wineItem->state);
3538
3539   if ( (wineItem) && (wineItem->parent))
3540   {
3541         /* 
3542          * If the item has a collapse parent expand the parent so he 
3543          * can expose the item 
3544          */
3545         TREEVIEW_ITEM *parentItem = TREEVIEW_ValidItem (infoPtr, wineItem->parent);
3546         if ( !(parentItem->state & TVIS_EXPANDED)) 
3547           TREEVIEW_Expand (hwnd, TVE_EXPAND, (LPARAM) wineItem->parent);
3548   }
3549
3550   switch (action) 
3551   {
3552         case TVGN_CARET: 
3553           prevSelect=(INT)infoPtr->selectedItem;
3554
3555           if ((HTREEITEM)prevSelect==newSelect) 
3556                 return FALSE;
3557
3558           prevItem= TREEVIEW_ValidItem (infoPtr, (HTREEITEM)prevSelect);
3559
3560           if (newSelect) 
3561                 if (TREEVIEW_SendTreeviewNotify(
3562                           hwnd, 
3563                           TVN_SELCHANGINGA, 
3564                           cause, 
3565                           (HTREEITEM)prevSelect, 
3566                           (HTREEITEM)newSelect)) 
3567                   return FALSE;       /* FIXME: OK? */
3568         
3569           if (prevItem) 
3570                 prevItem->state &= ~TVIS_SELECTED;
3571           if (wineItem) 
3572                 wineItem->state |=  TVIS_SELECTED;
3573
3574           infoPtr->selectedItem=(HTREEITEM)newSelect;
3575
3576           TREEVIEW_SendTreeviewNotify(
3577                 hwnd, 
3578                 TVN_SELCHANGEDA, 
3579                 cause,
3580                 (HTREEITEM)prevSelect,
3581                 (HTREEITEM)newSelect);
3582
3583           break;
3584
3585         case TVGN_DROPHILITE: 
3586           prevItem= TREEVIEW_ValidItem (infoPtr, infoPtr->dropItem);
3587
3588           if (prevItem) 
3589                 prevItem->state &= ~TVIS_DROPHILITED;
3590
3591           infoPtr->dropItem=(HTREEITEM)newSelect;
3592
3593           if (wineItem) 
3594                 wineItem->state |=TVIS_DROPHILITED;
3595
3596           break;
3597
3598         case TVGN_FIRSTVISIBLE:
3599           FIXME("FIRSTVISIBLE not implemented\n");
3600           break;
3601  }
3602  
3603  TREEVIEW_QueueRefresh (hwnd);
3604
3605  TRACE("Leaving state %d\n", wineItem->state);
3606  return TRUE;
3607 }
3608
3609 /* FIXME: handle NM_KILLFocus etc */
3610 static LRESULT
3611 TREEVIEW_SelectItem (HWND hwnd, WPARAM wParam, LPARAM lParam)
3612
3613 {
3614  return TREEVIEW_DoSelectItem (hwnd, wParam, (HTREEITEM) lParam, TVC_UNKNOWN);
3615 }
3616
3617
3618
3619    
3620 static LRESULT
3621 TREEVIEW_GetFont (HWND hwnd, WPARAM wParam, LPARAM lParam)
3622
3623 {
3624  TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
3625
3626  TRACE("%x\n",infoPtr->hFont);
3627  return infoPtr->hFont;
3628 }
3629
3630 static LRESULT
3631 TREEVIEW_SetFont (HWND hwnd, WPARAM wParam, LPARAM lParam)
3632
3633 {
3634  TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
3635  TEXTMETRICA tm;
3636  LOGFONTA logFont;
3637  HFONT hFont, hOldFont;
3638  INT height;
3639  HDC hdc;
3640
3641  TRACE("%x %lx\n",wParam, lParam);
3642  
3643  infoPtr->hFont = (HFONT)wParam;
3644
3645  hFont = infoPtr->hFont ? infoPtr->hFont : GetStockObject (SYSTEM_FONT);
3646
3647  GetObjectA (infoPtr->hFont, sizeof (LOGFONTA), &logFont);
3648  logFont.lfWeight=FW_BOLD;
3649  infoPtr->hBoldFont = CreateFontIndirectA (&logFont);
3650
3651  hdc = GetDC (0);
3652  hOldFont = SelectObject (hdc, hFont);
3653  GetTextMetricsA (hdc, &tm);
3654  height= tm.tmHeight + tm.tmExternalLeading;
3655  if (height>infoPtr->uRealItemHeight) 
3656         infoPtr->uRealItemHeight=height;
3657  SelectObject (hdc, hOldFont);
3658  ReleaseDC (0, hdc);
3659
3660  if (lParam)    
3661         TREEVIEW_QueueRefresh (hwnd);
3662  
3663  return 0;
3664 }
3665
3666
3667
3668 static LRESULT
3669 TREEVIEW_VScroll (HWND hwnd, WPARAM wParam, LPARAM lParam)
3670
3671 {
3672   TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
3673   int maxHeight;
3674
3675   TRACE("wp %x, lp %lx\n", wParam, lParam);
3676   if (!infoPtr->uInternalStatus & TV_VSCROLL) return FALSE;
3677
3678   switch (LOWORD (wParam)) {
3679         case SB_LINEUP: 
3680                         if (!infoPtr->cy) return FALSE;
3681                         infoPtr->cy -= infoPtr->uRealItemHeight;
3682                         if (infoPtr->cy < 0) infoPtr->cy=0;
3683                         break;
3684         case SB_LINEDOWN: 
3685                         maxHeight=infoPtr->uTotalHeight-infoPtr->uVisibleHeight;
3686                         if (infoPtr->cy == maxHeight) return FALSE;
3687                         infoPtr->cy += infoPtr->uRealItemHeight;
3688                         if (infoPtr->cy > maxHeight) 
3689                                 infoPtr->cy = maxHeight;
3690                         break;
3691         case SB_PAGEUP: 
3692                         if (!infoPtr->cy) return FALSE;
3693                         infoPtr->cy -= infoPtr->uVisibleHeight;
3694                         if (infoPtr->cy < 0) infoPtr->cy=0;
3695                         break;
3696         case SB_PAGEDOWN:
3697                         maxHeight=infoPtr->uTotalHeight-infoPtr->uVisibleHeight;
3698                         if (infoPtr->cy == maxHeight) return FALSE;
3699                         infoPtr->cy += infoPtr->uVisibleHeight;
3700             if (infoPtr->cy > maxHeight)
3701                 infoPtr->cy = maxHeight;
3702                         break;
3703         case SB_THUMBTRACK: 
3704                         infoPtr->cy = HIWORD (wParam);
3705                         break;
3706                         
3707   }
3708   
3709   TREEVIEW_QueueRefresh (hwnd);
3710   return TRUE;
3711 }
3712
3713 static LRESULT
3714 TREEVIEW_HScroll (HWND hwnd, WPARAM wParam, LPARAM lParam) 
3715 {
3716   TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
3717   int maxWidth;
3718
3719   TRACE("wp %lx, lp %x\n", lParam, wParam);
3720         
3721   if (!infoPtr->uInternalStatus & TV_HSCROLL) return FALSE;
3722
3723   switch (LOWORD (wParam)) {
3724         case SB_LINEUP: 
3725                         if (!infoPtr->cx) return FALSE;
3726                         infoPtr->cx -= infoPtr->uRealItemHeight;
3727                         if (infoPtr->cx < 0) infoPtr->cx=0;
3728                         break;
3729         case SB_LINEDOWN: 
3730                         maxWidth=infoPtr->uTotalWidth-infoPtr->uVisibleWidth;
3731                         if (infoPtr->cx == maxWidth) return FALSE;
3732                         infoPtr->cx += infoPtr->uRealItemHeight; /*FIXME */
3733                         if (infoPtr->cx > maxWidth) 
3734                                 infoPtr->cx = maxWidth;
3735                         break;
3736         case SB_PAGEUP: 
3737                         if (!infoPtr->cx) return FALSE;
3738                         infoPtr->cx -= infoPtr->uVisibleWidth;
3739                         if (infoPtr->cx < 0) infoPtr->cx=0;
3740                         break;
3741         case SB_PAGEDOWN:
3742                         maxWidth=infoPtr->uTotalWidth-infoPtr->uVisibleWidth;
3743                         if (infoPtr->cx == maxWidth) return FALSE;
3744                         infoPtr->cx += infoPtr->uVisibleWidth;
3745             if (infoPtr->cx > maxWidth)
3746                 infoPtr->cx = maxWidth;
3747                         break;
3748         case SB_THUMBTRACK: 
3749                         infoPtr->cx = HIWORD (wParam);
3750                         break;
3751                         
3752   }
3753   
3754   TREEVIEW_QueueRefresh (hwnd);
3755   return TRUE;
3756 }
3757
3758 static LRESULT TREEVIEW_MouseWheel (HWND hwnd, WPARAM wParam, LPARAM lParam)
3759 {
3760
3761     TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
3762     short gcWheelDelta = 0;
3763     UINT pulScrollLines = 3;
3764
3765     SystemParametersInfoW(SPI_GETWHEELSCROLLLINES,0, &pulScrollLines, 0);
3766
3767     gcWheelDelta -= (short) HIWORD(wParam);
3768     pulScrollLines *= (gcWheelDelta / WHEEL_DELTA);
3769
3770     if (abs(gcWheelDelta) >= WHEEL_DELTA && pulScrollLines)
3771     {
3772         int wheelDy = pulScrollLines * infoPtr->uRealItemHeight;
3773         int newDy = infoPtr->cy + wheelDy;
3774         int maxDy = infoPtr->uTotalHeight - infoPtr->uVisibleHeight;
3775
3776         if (newDy > maxDy) newDy = maxDy;
3777         if (newDy < 0) newDy = 0;
3778
3779         if (newDy == infoPtr->cy) return TRUE;
3780
3781         TREEVIEW_VScroll(hwnd, MAKEWPARAM(SB_THUMBTRACK,newDy),0);
3782     }
3783   return TRUE;
3784 }
3785
3786 static LRESULT
3787 TREEVIEW_KeyDown (HWND hwnd, WPARAM wParam, LPARAM lParam)
3788 {
3789  TREEVIEW_INFO *infoPtr        = TREEVIEW_GetInfoPtr(hwnd);
3790  HTREEITEM     hNewSelection   = 0;
3791  INT           scrollNeeds     = -1;
3792  INT           cyChangeNeeds   = -1;
3793  INT           prevSelect      = (INT)infoPtr->selectedItem;
3794
3795  TREEVIEW_ITEM *prevItem       = 
3796     (prevSelect != 0 ) ? 
3797       TREEVIEW_ValidItem (infoPtr, (HTREEITEM)prevSelect) :
3798       NULL;
3799
3800  TREEVIEW_ITEM *newItem        = NULL;
3801
3802  TRACE("%x %lx\n",wParam, lParam);
3803
3804  if (prevSelect == 0) 
3805    return FALSE;
3806
3807  switch (wParam) {
3808         case VK_UP: 
3809                 newItem=TREEVIEW_GetPrevListItem (infoPtr, prevItem);
3810
3811                 if (!newItem) 
3812                         newItem=& infoPtr->items[(INT)infoPtr->TopRootItem];
3813
3814         hNewSelection = newItem->hItem;
3815
3816         if (! newItem->visible)
3817                 scrollNeeds = SB_LINEUP;
3818                 break;
3819
3820         case VK_DOWN: 
3821                 newItem=TREEVIEW_GetNextListItem (infoPtr, prevItem);
3822
3823                 if (!newItem) 
3824       newItem=prevItem;
3825
3826     hNewSelection = newItem->hItem;
3827
3828     if (! newItem->visible)
3829       scrollNeeds = SB_LINEDOWN;
3830
3831                 break;
3832
3833         case VK_HOME:
3834                 newItem       = &infoPtr->items[(INT)infoPtr->TopRootItem];
3835     hNewSelection = newItem->hItem;
3836     cyChangeNeeds = 0;
3837                 break;
3838
3839         case VK_END:
3840                 newItem       = &infoPtr->items[(INT)infoPtr->TopRootItem];
3841                 newItem       = TREEVIEW_GetLastListItem (infoPtr, newItem);
3842     hNewSelection = newItem->hItem;
3843
3844     if (! newItem->visible)
3845       cyChangeNeeds = infoPtr->uTotalHeight-infoPtr->uVisibleHeight;
3846
3847                 break;
3848
3849         case VK_LEFT:
3850     if ( (prevItem->cChildren > 0) && (prevItem->state & TVIS_EXPANDED) )
3851     {
3852       TREEVIEW_Expand(hwnd, TVE_COLLAPSE, prevSelect );
3853     }
3854     else if ((INT)prevItem->parent) 
3855     {
3856       newItem = (& infoPtr->items[(INT)prevItem->parent]);
3857       if (! newItem->visible) 
3858         /* FIXME find a way to make this item the first visible... */
3859         newItem = NULL; 
3860
3861       hNewSelection = newItem->hItem;
3862     }
3863
3864     break;
3865
3866         case VK_RIGHT:
3867     if ( ( prevItem->cChildren > 0)  || 
3868          ( prevItem->cChildren == I_CHILDRENCALLBACK))
3869     {
3870       if (! (prevItem->state & TVIS_EXPANDED))
3871         TREEVIEW_Expand(hwnd, TVE_EXPAND, prevSelect );
3872       else
3873       {
3874         newItem = (& infoPtr->items[(INT)prevItem->firstChild]);
3875         hNewSelection = newItem->hItem;
3876       }
3877     }
3878
3879     break;
3880
3881   case VK_ADD:
3882     if (! (prevItem->state & TVIS_EXPANDED))
3883       TREEVIEW_Expand(hwnd, TVE_EXPAND, prevSelect );
3884     break;
3885
3886   case VK_SUBTRACT:
3887     if (prevItem->state & TVIS_EXPANDED)
3888       TREEVIEW_Expand(hwnd, TVE_COLLAPSE, prevSelect );
3889     break;
3890
3891   case VK_PRIOR:
3892     
3893                 newItem=TREEVIEW_GetListItem(
3894               infoPtr, 
3895               prevItem,
3896               -1*(TREEVIEW_GetVisibleCount(hwnd,0,0)-3));
3897                 if (!newItem) 
3898       newItem=prevItem;
3899   
3900     hNewSelection = newItem->hItem;
3901
3902     if (! newItem->visible)
3903       scrollNeeds = SB_PAGEUP;
3904
3905                 break;
3906
3907   case VK_NEXT:
3908                 newItem=TREEVIEW_GetListItem(
3909               infoPtr, 
3910               prevItem,
3911               TREEVIEW_GetVisibleCount(hwnd,0,0)-3);
3912
3913                 if (!newItem) 
3914       newItem=prevItem;
3915
3916     hNewSelection = newItem->hItem;
3917
3918     if (! newItem->visible)
3919       scrollNeeds = SB_PAGEDOWN;
3920
3921                 break;
3922
3923         case VK_BACK:
3924
3925         case VK_RETURN:
3926
3927   default:
3928                 FIXME("%x not implemented\n", wParam);
3929                 break;
3930  }
3931
3932   if (hNewSelection) 
3933   {
3934 /* 
3935     This works but does not send notification...
3936
3937     prevItem->state      &= ~TVIS_SELECTED;
3938     newItem->state       |=  TVIS_SELECTED;
3939     infoPtr->selectedItem = hNewSelection;
3940     TREEVIEW_QueueRefresh (hwnd);
3941 */
3942
3943     if ( TREEVIEW_DoSelectItem( 
3944            hwnd, 
3945            TVGN_CARET, 
3946            (HTREEITEM)hNewSelection, 
3947            TVC_BYKEYBOARD))
3948     {
3949       /* If selection change is allowed for the new item, perform scrolling */
3950       if (scrollNeeds != -1)
3951         TREEVIEW_VScroll(hwnd, scrollNeeds, 0);
3952   
3953       if (cyChangeNeeds != -1)
3954         infoPtr->cy = cyChangeNeeds;
3955
3956       /* FIXME: Something happen in the load the in the two weeks before 
3957          april 1st 1999 which makes this SetFocus mandatory otherwise, the focus 
3958          is lost... However the SetFocus should not be required...*/
3959                
3960       SetFocus(hwnd);
3961     }
3962   }
3963
3964   return FALSE;
3965 }
3966
3967
3968 static LRESULT
3969 TREEVIEW_GetScrollTime (HWND hwnd)
3970 {
3971   TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
3972
3973   return infoPtr->uScrollTime;
3974 }
3975
3976
3977 static LRESULT
3978 TREEVIEW_SetScrollTime (HWND hwnd, UINT uScrollTime)
3979 {
3980   TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
3981   UINT uOldScrollTime = infoPtr->uScrollTime;
3982
3983   infoPtr->uScrollTime = min (uScrollTime, 100);
3984
3985   return uOldScrollTime;
3986 }
3987
3988
3989 static LRESULT WINAPI
3990 TREEVIEW_WindowProc (HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
3991 {
3992    TREEVIEW_INFO *infoPtr;
3993    if (uMsg==WM_CREATE)
3994                 return TREEVIEW_Create (hwnd, wParam, lParam);
3995    
3996    if (!(infoPtr = TREEVIEW_GetInfoPtr(hwnd)))
3997        return DefWindowProcA (hwnd, uMsg, wParam, lParam);
3998
3999    switch (uMsg) {
4000   
4001         case TVM_INSERTITEMA:
4002           return TREEVIEW_InsertItemA (hwnd, wParam, lParam);
4003
4004         case TVM_INSERTITEMW:
4005                 return TREEVIEW_InsertItemW(hwnd,wParam,lParam);;
4006
4007         case TVM_DELETEITEM:
4008                 return TREEVIEW_DeleteItem (hwnd, wParam, lParam);
4009
4010         case TVM_EXPAND:
4011                 return TREEVIEW_Expand (hwnd, wParam, lParam);
4012
4013         case TVM_GETITEMRECT:
4014                 return TREEVIEW_GetItemRect (hwnd, wParam, lParam);
4015
4016         case TVM_GETCOUNT:
4017                 return TREEVIEW_GetCount (hwnd, wParam, lParam);
4018
4019         case TVM_GETINDENT:
4020                 return TREEVIEW_GetIndent (hwnd);
4021
4022         case TVM_SETINDENT:
4023                 return TREEVIEW_SetIndent (hwnd, wParam);
4024
4025         case TVM_GETIMAGELIST:
4026                 return TREEVIEW_GetImageList (hwnd, wParam, lParam);
4027
4028                 case TVM_SETIMAGELIST:
4029                 return TREEVIEW_SetImageList (hwnd, wParam, lParam);
4030
4031         case TVM_GETNEXTITEM:
4032                 return TREEVIEW_GetNextItem (hwnd, wParam, lParam);
4033
4034         case TVM_SELECTITEM:
4035                 return TREEVIEW_SelectItem (hwnd, wParam, lParam);
4036
4037         case TVM_GETITEMA:
4038                 return TREEVIEW_GetItemA (hwnd, wParam, lParam);
4039
4040         case TVM_GETITEMW:
4041                 return TREEVIEW_GetItemW (hwnd, wParam, lParam);
4042
4043         case TVM_SETITEMA:
4044                 return TREEVIEW_SetItemA (hwnd, wParam, lParam);
4045
4046         case TVM_SETITEMW:
4047                 FIXME("Unimplemented msg TVM_SETITEMW\n");
4048                 return 0;
4049
4050         case TVM_EDITLABELA:
4051             return TREEVIEW_EditLabelA(hwnd, wParam, lParam);
4052
4053         case TVM_EDITLABELW:
4054                 FIXME("Unimplemented msg TVM_EDITLABELW \n");
4055                 return 0;
4056
4057         case TVM_GETEDITCONTROL:
4058                 return TREEVIEW_GetEditControl (hwnd);
4059
4060         case TVM_GETVISIBLECOUNT:
4061                 return TREEVIEW_GetVisibleCount (hwnd, wParam, lParam);
4062
4063         case TVM_HITTEST:
4064                 return TREEVIEW_HitTest (hwnd, lParam);
4065
4066         case TVM_CREATEDRAGIMAGE:
4067                 return TREEVIEW_CreateDragImage (hwnd, wParam, lParam);
4068   
4069         case TVM_SORTCHILDREN:
4070                 return TREEVIEW_SortChildren (hwnd, wParam, lParam);
4071   
4072         case TVM_ENSUREVISIBLE:
4073                 FIXME("Unimplemented msg TVM_ENSUREVISIBLE\n");
4074                 return 0;
4075   
4076         case TVM_SORTCHILDRENCB:
4077                 return TREEVIEW_SortChildrenCB(hwnd, wParam, lParam);
4078   
4079         case TVM_ENDEDITLABELNOW:
4080                 if (infoPtr->editItem)
4081                     return TREEVIEW_EndEditLabelNow (hwnd, wParam, lParam);
4082   
4083         case TVM_GETISEARCHSTRINGA:
4084                 FIXME("Unimplemented msg TVM_GETISEARCHSTRINGA\n");
4085                 return 0;
4086   
4087         case TVM_GETISEARCHSTRINGW:
4088                 FIXME("Unimplemented msg TVM_GETISEARCHSTRINGW\n");
4089                 return 0;
4090   
4091         case TVM_GETTOOLTIPS:
4092                 return TREEVIEW_GetToolTips (hwnd);
4093
4094         case TVM_SETTOOLTIPS:
4095                 return TREEVIEW_SetToolTips (hwnd, wParam);
4096   
4097         case TVM_SETINSERTMARK:
4098                 return TREEVIEW_SetInsertMark (hwnd,wParam, lParam);
4099   
4100         case TVM_SETITEMHEIGHT:
4101                 return TREEVIEW_SetItemHeight (hwnd, wParam);
4102   
4103         case TVM_GETITEMHEIGHT:
4104                 return TREEVIEW_GetItemHeight (hwnd);
4105   
4106         case TVM_SETBKCOLOR:
4107                 return TREEVIEW_SetBkColor (hwnd, wParam, lParam);
4108         
4109         case TVM_SETTEXTCOLOR:
4110                 return TREEVIEW_SetTextColor (hwnd, wParam, lParam);
4111   
4112         case TVM_GETBKCOLOR:
4113                 return TREEVIEW_GetBkColor (hwnd);
4114   
4115         case TVM_GETTEXTCOLOR:
4116                 return TREEVIEW_GetTextColor (hwnd);
4117   
4118         case TVM_SETSCROLLTIME:
4119                 return TREEVIEW_SetScrollTime (hwnd, (UINT)wParam);
4120   
4121         case TVM_GETSCROLLTIME:
4122                 return TREEVIEW_GetScrollTime (hwnd);
4123
4124         case TVM_GETITEMSTATE:
4125                 return TREEVIEW_GetItemState (hwnd,wParam, lParam);
4126
4127         case TVM_GETLINECOLOR:
4128                 return TREEVIEW_GetLineColor (hwnd,wParam, lParam);
4129
4130         case TVM_SETLINECOLOR:
4131                 return TREEVIEW_SetLineColor (hwnd,wParam, lParam);
4132   
4133         case TVM_SETINSERTMARKCOLOR:
4134                 return TREEVIEW_SetInsertMarkColor (hwnd,wParam, lParam);
4135   
4136         case TVM_GETINSERTMARKCOLOR:
4137                 return TREEVIEW_GetInsertMarkColor (hwnd,wParam, lParam);
4138   
4139         case TVM_SETUNICODEFORMAT:
4140                 FIXME("Unimplemented msg TVM_SETUNICODEFORMAT\n");
4141                 return 0;
4142   
4143         case TVM_GETUNICODEFORMAT:
4144                 FIXME("Unimplemented msg TVM_GETUNICODEFORMAT\n");
4145                 return 0;
4146   
4147                 case WM_COMMAND: 
4148                          return TREEVIEW_Command (hwnd, wParam, lParam);
4149   
4150                 case WM_DESTROY:
4151                         return TREEVIEW_Destroy (hwnd);
4152   
4153 /*              case WM_ENABLE: */
4154   
4155                 case WM_ERASEBKGND:
4156                         return TREEVIEW_EraseBackground (hwnd, wParam, lParam);
4157   
4158                 case WM_GETDLGCODE:
4159                 return DLGC_WANTARROWS | DLGC_WANTCHARS;
4160   
4161                 case WM_PAINT:
4162                 return TREEVIEW_Paint (hwnd, wParam, lParam);
4163   
4164                 case WM_GETFONT:
4165                 return TREEVIEW_GetFont (hwnd, wParam, lParam);
4166
4167                 case WM_SETFONT:
4168                 return TREEVIEW_SetFont (hwnd, wParam, lParam);
4169   
4170                 case WM_KEYDOWN:
4171                         return TREEVIEW_KeyDown (hwnd, wParam, lParam);
4172   
4173                 case WM_SETFOCUS: 
4174                         return TREEVIEW_SetFocus (hwnd, wParam, lParam);
4175
4176                 case WM_KILLFOCUS: 
4177                         return TREEVIEW_KillFocus (hwnd, wParam, lParam);
4178   
4179                 case WM_LBUTTONDOWN:
4180                         return TREEVIEW_LButtonDown (hwnd, wParam, lParam);
4181
4182                 case WM_LBUTTONUP:
4183                         return TREEVIEW_LButtonUp (hwnd, wParam, lParam);
4184   
4185                 case WM_LBUTTONDBLCLK:
4186                         return TREEVIEW_LButtonDoubleClick (hwnd, wParam, lParam);
4187   
4188                 case WM_RBUTTONDOWN:
4189                         return TREEVIEW_RButtonDown (hwnd, wParam, lParam);
4190
4191                 case WM_RBUTTONUP:
4192                         return TREEVIEW_RButtonUp (hwnd, wParam, lParam);
4193
4194                 case WM_MOUSEMOVE:
4195                         return TREEVIEW_MouseMove (hwnd, wParam, lParam);
4196   
4197                 case WM_STYLECHANGED: 
4198                         return TREEVIEW_StyleChanged (hwnd, wParam, lParam);
4199
4200 /*              case WM_SYSCOLORCHANGE: */
4201 /*              case WM_SETREDRAW: */
4202   
4203                 case WM_TIMER:
4204                         return TREEVIEW_HandleTimer (hwnd, wParam, lParam);
4205  
4206                 case WM_SIZE: 
4207                         return TREEVIEW_Size (hwnd, wParam,lParam);
4208
4209                 case WM_HSCROLL: 
4210                         return TREEVIEW_HScroll (hwnd, wParam, lParam);
4211                 case WM_VSCROLL: 
4212                         return TREEVIEW_VScroll (hwnd, wParam, lParam);
4213   
4214                 case WM_MOUSEWHEEL:
4215                     if (wParam & (MK_SHIFT | MK_CONTROL))
4216                         return DefWindowProcA( hwnd, uMsg, wParam, lParam );
4217                     return TREEVIEW_MouseWheel (hwnd, wParam, lParam);
4218
4219                 case WM_DRAWITEM:
4220                         TRACE ("drawItem\n");
4221                         return DefWindowProcA (hwnd, uMsg, wParam, lParam);
4222   
4223                 default:
4224                 if (uMsg >= WM_USER)
4225                 FIXME("Unknown msg %04x wp=%08x lp=%08lx\n",
4226                      uMsg, wParam, lParam);
4227             return DefWindowProcA (hwnd, uMsg, wParam, lParam);
4228       }
4229     return 0;
4230 }
4231
4232
4233 VOID
4234 TREEVIEW_Register (void)
4235 {
4236     WNDCLASSA wndClass;
4237
4238     TRACE("\n");
4239
4240     ZeroMemory (&wndClass, sizeof(WNDCLASSA));
4241     wndClass.style         = CS_GLOBALCLASS | CS_DBLCLKS;
4242     wndClass.lpfnWndProc   = (WNDPROC)TREEVIEW_WindowProc;
4243     wndClass.cbClsExtra    = 0;
4244     wndClass.cbWndExtra    = sizeof(TREEVIEW_INFO *);
4245     wndClass.hCursor       = LoadCursorA (0, IDC_ARROWA);
4246     wndClass.hbrBackground = 0;
4247     wndClass.lpszClassName = WC_TREEVIEWA;
4248  
4249     RegisterClassA (&wndClass);
4250 }
4251
4252
4253 VOID
4254 TREEVIEW_Unregister (void)
4255 {
4256     UnregisterClassA (WC_TREEVIEWA, (HINSTANCE)NULL);
4257 }
4258
4259
4260