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