Various ANSI C compability fixes.
[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 static 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)
1465     parent=infoPtr->TopRootItem;
1466
1467   /* Check for a valid handle to the parent item */
1468   if (!TREEVIEW_ValidItem(infoPtr, parent))
1469   {
1470     ERR ("invalid item hParent=%x\n", (INT)parent);
1471     return FALSE;
1472   }
1473
1474   /* Obtain the parent node to sort */  
1475   sortMe = &infoPtr->items[ (INT)parent ];
1476
1477   /* Make sure there is something to sort */
1478   if ( sortMe->cChildren > 1 ) 
1479   {
1480     /* pointer organization */
1481     HDPA          sortList   = DPA_Create(sortMe->cChildren);
1482     HTREEITEM     itemHandle = sortMe->firstChild;
1483     TREEVIEW_ITEM *itemPtr   = & infoPtr->items[ (INT)itemHandle ];
1484
1485     /* TREEVIEW_ITEM rechaining */
1486     INT  count     = 0;
1487     VOID *item     = 0;
1488     VOID *nextItem = 0;
1489     VOID *prevItem = 0;
1490
1491     /* Build the list of item to sort */
1492     do 
1493     {
1494       DPA_InsertPtr(
1495         sortList,              /* the list */
1496         sortMe->cChildren+1,   /* force the insertion to be an append */
1497         itemPtr);              /* the ptr to store */   
1498
1499       /* Get the next sibling */
1500       itemHandle = itemPtr->sibling;
1501       itemPtr    = & infoPtr->items[ (INT)itemHandle ];
1502     } while ( itemHandle != NULL );
1503
1504     /* let DPA perform the sort activity */
1505         if (pSort) 
1506         DPA_Sort(
1507                 sortList,                  /* what  */ 
1508                 TREEVIEW_CallBackCompare,  /* how   */
1509                 hwnd);                     /* owner */
1510         else 
1511                 DPA_Sort (
1512                         sortList,                  /* what  */
1513                 TREEVIEW_SortOnName,       /* how   */
1514                         hwnd);                     /* owner */
1515
1516     /* 
1517      * Reorganized TREEVIEW_ITEM structures. 
1518      * Note that we know we have at least two elements.
1519      */
1520
1521     /* Get the first item and get ready to start... */
1522     item = DPA_GetPtr(sortList, count++);    
1523     while ( (nextItem = DPA_GetPtr(sortList, count++)) != NULL )
1524     {
1525       /* link the two current item toghether */
1526       ((TREEVIEW_ITEM*)item)->sibling       = ((TREEVIEW_ITEM*)nextItem)->hItem;
1527       ((TREEVIEW_ITEM*)nextItem)->upsibling = ((TREEVIEW_ITEM*)item)->hItem;
1528
1529       if (prevItem == NULL) /* this is the first item, update the parent */
1530       {
1531         sortMe->firstChild                = ((TREEVIEW_ITEM*)item)->hItem;
1532         ((TREEVIEW_ITEM*)item)->upsibling = NULL;
1533       }
1534       else                  /* fix the back chaining */
1535       {
1536         ((TREEVIEW_ITEM*)item)->upsibling = ((TREEVIEW_ITEM*)prevItem)->hItem;
1537       }
1538
1539       /* get ready for the next one */
1540       prevItem = item; 
1541       item     = nextItem;
1542     }
1543
1544     /* the last item is pointed to by item and never has a sibling */
1545     ((TREEVIEW_ITEM*)item)->sibling = NULL; 
1546
1547     DPA_Destroy(sortList);
1548
1549     return TRUE;
1550   }
1551   return FALSE;
1552 }
1553
1554
1555 /***************************************************************************
1556  * Setup the treeview structure with regards of the sort method
1557  * and sort the children of the TV item specified in lParam
1558  */
1559 static LRESULT WINAPI TREEVIEW_SortChildrenCB(
1560   HWND   hwnd, 
1561   WPARAM wParam, 
1562   LPARAM lParam
1563   )
1564 {
1565  LPTVSORTCB pSort=(LPTVSORTCB) lParam;
1566
1567  return TREEVIEW_Sort (hwnd, wParam, pSort->hParent, pSort);
1568 }
1569
1570
1571 /***************************************************************************
1572  * Sort the children of the TV item specified in lParam.
1573  */
1574 static LRESULT WINAPI TREEVIEW_SortChildren (
1575   HWND   hwnd, 
1576   WPARAM wParam, 
1577   LPARAM lParam)
1578 {
1579  return TREEVIEW_Sort (hwnd, (BOOL) wParam, (HTREEITEM) lParam, NULL);
1580 }
1581
1582
1583
1584 /* the method used below isn't the most memory-friendly, but it avoids 
1585    a lot of memory reallocations */ 
1586
1587 /* BTW: we waste handle 0; 0 is not an allowed handle. */
1588
1589 static LRESULT
1590 TREEVIEW_InsertItemA (HWND hwnd, WPARAM wParam, LPARAM lParam)
1591
1592 {
1593   TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
1594   TVINSERTSTRUCTA  *ptdi;
1595   TVITEMEXA     *tvItem;
1596   TREEVIEW_ITEM *wineItem, *parentItem, *prevsib, *sibItem;
1597   INT           iItem,listItems,i,len;
1598  
1599   /* Item to insert */
1600   ptdi = (LPTVINSERTSTRUCTA) lParam;
1601
1602         /* check if memory is available */
1603
1604   if (infoPtr->uNumPtrsAlloced==0) {
1605         infoPtr->items = COMCTL32_Alloc (TVITEM_ALLOC*sizeof (TREEVIEW_ITEM));
1606         infoPtr->freeList= COMCTL32_Alloc ((1+(TVITEM_ALLOC>>5)) * sizeof (INT));
1607         infoPtr->uNumPtrsAlloced=TVITEM_ALLOC;
1608         infoPtr->TopRootItem=(HTREEITEM)1;
1609    }
1610
1611   /* 
1612    * Reallocate contiguous space for items 
1613    */
1614   if (infoPtr->uNumItems == (infoPtr->uNumPtrsAlloced-1) ) {
1615         TREEVIEW_ITEM *oldItems = infoPtr->items;
1616         INT *oldfreeList = infoPtr->freeList;
1617
1618         infoPtr->uNumPtrsAlloced*=2;
1619     infoPtr->items = COMCTL32_Alloc (infoPtr->uNumPtrsAlloced*sizeof (TREEVIEW_ITEM));
1620     infoPtr->freeList= COMCTL32_Alloc ((1+(infoPtr->uNumPtrsAlloced>>5))*sizeof (INT));
1621
1622     memcpy (&infoPtr->items[0], &oldItems[0],
1623                     infoPtr->uNumPtrsAlloced/2 * sizeof(TREEVIEW_ITEM));
1624     memcpy (&infoPtr->freeList[0], &oldfreeList[0],
1625                     (infoPtr->uNumPtrsAlloced>>6) * sizeof(INT));
1626
1627     COMCTL32_Free (oldItems);  
1628     COMCTL32_Free (oldfreeList);  
1629    }
1630
1631   /* 
1632    * Reset infoPtr structure with new stat according to current TV picture
1633    */
1634   iItem=0;
1635   infoPtr->uNumItems++;
1636   if ((INT)infoPtr->uMaxHandle==(infoPtr->uNumItems-1))  { 
1637         iItem=infoPtr->uNumItems;
1638         infoPtr->uMaxHandle = (HTREEITEM)((INT)infoPtr->uMaxHandle + 1);
1639   } else {                                       /* check freelist */
1640         for (i=0; i<infoPtr->uNumPtrsAlloced>>5; i++) {
1641                 if (infoPtr->freeList[i]) {
1642                         iItem=ffs (infoPtr->freeList[i])-1;
1643                         tv_clear_bit(iItem,&infoPtr->freeList[i]);
1644                         iItem+=i<<5;
1645                         break;
1646                 }
1647     } 
1648   }
1649
1650   if (TRACE_ON(treeview)) { 
1651     for (i=0; i<infoPtr->uNumPtrsAlloced>>5; i++) 
1652             TRACE("%8x\n",infoPtr->freeList[i]);
1653   }
1654
1655   if (!iItem) ERR("Argh -- can't find free item.\n");
1656
1657   /* 
1658    * Find the parent item of the new item 
1659    */  
1660   tvItem= & ptdi->DUMMYUNIONNAME.itemex;
1661   wineItem=& infoPtr->items[iItem];
1662
1663   if ((ptdi->hParent==TVI_ROOT) || (ptdi->hParent==0)) {
1664     parentItem       = NULL;
1665     wineItem->parent = 0; 
1666     sibItem          = &infoPtr->items [(INT)infoPtr->TopRootItem];
1667     listItems        = infoPtr->uNumItems;
1668   }
1669   else  {
1670         parentItem = &infoPtr->items[(INT)ptdi->hParent];
1671   
1672     /* Do the insertion here it if it's the only item of this parent */
1673         if (!parentItem->firstChild) 
1674                 parentItem->firstChild=(HTREEITEM)iItem;
1675   
1676         wineItem->parent = ptdi->hParent;
1677         sibItem          = &infoPtr->items [(INT)parentItem->firstChild];
1678         parentItem->cChildren++;
1679         listItems        = parentItem->cChildren;
1680   }
1681
1682   
1683   /* NOTE: I am moving some setup of the wineItem object that was initialy 
1684    *       done at the end of the function since some of the values are 
1685    *       required by the Callback sorting 
1686    */
1687
1688   if (tvItem->mask & TVIF_TEXT) 
1689   {
1690     /*
1691      * Setup the item text stuff here since it's required by the Sort method
1692      * when the insertion are ordered
1693      */
1694     if (tvItem->pszText!=LPSTR_TEXTCALLBACKA) 
1695     {
1696       TRACE("(%p,%s)\n", &tvItem->pszText, tvItem->pszText); 
1697       len = lstrlenA (tvItem->pszText)+1;
1698       wineItem->pszText= COMCTL32_Alloc (len+1);
1699       lstrcpyA (wineItem->pszText, tvItem->pszText);
1700       wineItem->cchTextMax=len;
1701     }
1702     else 
1703     {
1704       TRACE("LPSTR_TEXTCALLBACK\n");
1705       wineItem->pszText = LPSTR_TEXTCALLBACKA;
1706       wineItem->cchTextMax = 0;
1707     }
1708   }
1709
1710   if (tvItem->mask & TVIF_PARAM) 
1711     wineItem->lParam=tvItem->lParam;
1712
1713
1714   wineItem->upsibling=0;  /* needed in case we're the first item in a list */ 
1715   wineItem->sibling=0;     
1716   wineItem->firstChild=0;
1717   wineItem->hItem=(HTREEITEM)iItem;
1718
1719   if (listItems>1) {
1720      prevsib=NULL;
1721
1722      switch ((DWORD) ptdi->hInsertAfter) {
1723                 case (DWORD) TVI_FIRST: 
1724                         if (wineItem->parent) {
1725                                 wineItem->sibling=parentItem->firstChild;
1726                                 parentItem->firstChild=(HTREEITEM)iItem;
1727                         } else {
1728                                 wineItem->sibling=infoPtr->TopRootItem;
1729                                 infoPtr->TopRootItem=(HTREEITEM)iItem;
1730                         }
1731                         sibItem->upsibling=(HTREEITEM)iItem;
1732                         break;
1733
1734                 case (DWORD) TVI_SORT:  
1735           if (sibItem==wineItem) 
1736         /* 
1737          * This item is the first child of the level and it 
1738          * has already been inserted 
1739          */                
1740         break; 
1741       else
1742       {
1743         TREEVIEW_ITEM *aChild;
1744                   
1745   
1746         TREEVIEW_ITEM *previousChild = NULL;
1747         BOOL bItemInserted           = FALSE;
1748
1749             if (parentItem)
1750           aChild = &infoPtr->items[(INT)parentItem->firstChild];
1751                 else 
1752           aChild = &infoPtr->items[(INT)infoPtr->TopRootItem];
1753   
1754         /* Iterate the parent children to see where we fit in */
1755         while ( aChild != NULL )
1756         {
1757           INT comp = strcmp(wineItem->pszText, aChild->pszText);
1758           if ( comp < 0 )  /* we are smaller than the current one */
1759           {
1760             TREEVIEW_InsertBefore(infoPtr, wineItem, aChild, parentItem);
1761             bItemInserted = TRUE;
1762             break;
1763           }
1764           else if ( comp > 0 )  /* we are bigger than the current one */
1765           {
1766             previousChild = aChild;
1767             aChild = (aChild->sibling == 0)  /* This will help us to exit   */
1768                         ? NULL               /* if there is no more sibling */
1769                         : &infoPtr->items[(INT)aChild->sibling];
1770   
1771             /* Look at the next item */
1772             continue;
1773           }
1774           else if ( comp == 0 )
1775           {
1776             /* 
1777              * An item with this name is already existing, therefore,  
1778              * we add after the one we found 
1779              */
1780             TREEVIEW_InsertAfter(infoPtr, wineItem, aChild, parentItem);
1781             bItemInserted = TRUE;
1782             break;
1783           }
1784         }
1785       
1786         /* 
1787          * we reach the end of the child list and the item as not
1788          * yet been inserted, therefore, insert it after the last child.
1789          */
1790         if ( (! bItemInserted ) && (aChild == NULL) )
1791           TREEVIEW_InsertAfter(infoPtr, wineItem, previousChild, parentItem);
1792   
1793         break;
1794       }
1795
1796
1797                 case (DWORD) TVI_LAST:  
1798                         if (sibItem==wineItem) break;
1799                         while (sibItem->sibling) {
1800                                 prevsib=sibItem;
1801                                 sibItem=&infoPtr->items [(INT)sibItem->sibling];
1802                         }
1803                         sibItem->sibling=(HTREEITEM)iItem;
1804                         wineItem->upsibling=sibItem->hItem;
1805                         break;
1806                 default:
1807                         while ((sibItem->sibling) && (sibItem->hItem!=ptdi->hInsertAfter))
1808                                 {
1809                                 prevsib=sibItem;
1810                 sibItem=&infoPtr->items [(INT)sibItem->sibling];
1811               }
1812                         if (sibItem->hItem!=ptdi->hInsertAfter) {
1813                          ERR("tried to insert item after nonexisting handle %d.\n",
1814                       (INT) ptdi->hInsertAfter);
1815                          break;
1816                         }
1817                         prevsib=sibItem;
1818                         if (sibItem->sibling) {
1819                 sibItem=&infoPtr->items [(INT)sibItem->sibling];
1820                                 sibItem->upsibling=(HTREEITEM)iItem;
1821                                 wineItem->sibling=sibItem->hItem;
1822                         }
1823                         prevsib->sibling=(HTREEITEM)iItem;
1824                         wineItem->upsibling=prevsib->hItem;
1825                         break;
1826         }
1827    }    
1828
1829
1830 /* Fill in info structure */
1831
1832    TRACE("new item %d; parent %d, mask %x\n", iItem, 
1833                         (INT)wineItem->parent,tvItem->mask);
1834
1835    wineItem->mask=tvItem->mask;
1836    wineItem->iIntegral=1; 
1837
1838    if (tvItem->mask & TVIF_CHILDREN) {
1839          wineItem->cChildren=tvItem->cChildren;
1840          if (tvItem->cChildren==I_CHILDRENCALLBACK) 
1841                         FIXME(" I_CHILDRENCALLBACK not supported\n");
1842         }
1843
1844   wineItem->expandBox.left   = 0; /* Initialize the expandBox */
1845   wineItem->expandBox.top    = 0;
1846   wineItem->expandBox.right  = 0;
1847   wineItem->expandBox.bottom = 0;
1848
1849    if (tvItem->mask & TVIF_IMAGE) 
1850         wineItem->iImage=tvItem->iImage;
1851
1852                 /* If the application sets TVIF_INTEGRAL without
1853                         supplying a TVITEMEX structure, it's toast */
1854
1855    if (tvItem->mask & TVIF_INTEGRAL) 
1856                 wineItem->iIntegral=tvItem->iIntegral;   
1857
1858    if (tvItem->mask & TVIF_SELECTEDIMAGE) 
1859         wineItem->iSelectedImage=tvItem->iSelectedImage;
1860
1861    if (tvItem->mask & TVIF_STATE) {
1862      TRACE("Changing item state from %d to %d\n", 
1863        wineItem->state, 
1864        tvItem->state);
1865         wineItem->state=tvItem->state;
1866         wineItem->stateMask=tvItem->stateMask;
1867    }
1868
1869
1870    TREEVIEW_QueueRefresh (hwnd);
1871
1872    return (LRESULT) iItem;
1873 }
1874
1875
1876 static LRESULT
1877 TREEVIEW_InsertItemW(HWND hwnd, WPARAM wParam, LPARAM lParam)
1878 {
1879     TVINSERTSTRUCTW *tvisW;
1880     TVINSERTSTRUCTA tvisA;
1881     LRESULT lRes;
1882
1883     tvisW = (LPTVINSERTSTRUCTW)lParam;
1884
1885     tvisA.hParent = tvisW->hParent;
1886     tvisA.hInsertAfter = tvisW->hInsertAfter;
1887
1888     tvisA.DUMMYUNIONNAME.item.mask           = tvisW->DUMMYUNIONNAME.item.mask;
1889     tvisA.DUMMYUNIONNAME.item.hItem          = tvisW->DUMMYUNIONNAME.item.hItem;
1890     tvisA.DUMMYUNIONNAME.item.state          = tvisW->DUMMYUNIONNAME.item.state;
1891     tvisA.DUMMYUNIONNAME.item.stateMask      = tvisW->DUMMYUNIONNAME.item.stateMask;
1892     tvisA.DUMMYUNIONNAME.item.cchTextMax     = tvisW->DUMMYUNIONNAME.item.cchTextMax;
1893
1894     if(tvisW->DUMMYUNIONNAME.item.pszText)
1895     {
1896         if (tvisW->DUMMYUNIONNAME.item.pszText!=LPSTR_TEXTCALLBACKW) 
1897         { 
1898             int len = lstrlenW (tvisW->DUMMYUNIONNAME.item.pszText)+1;
1899             tvisA.DUMMYUNIONNAME.item.pszText = COMCTL32_Alloc (len);
1900             lstrcpyWtoA (tvisA.DUMMYUNIONNAME.item.pszText,
1901                          tvisW->DUMMYUNIONNAME.item.pszText );
1902         }
1903         else 
1904         {
1905             tvisA.DUMMYUNIONNAME.item.pszText = LPSTR_TEXTCALLBACKA;
1906             tvisA.DUMMYUNIONNAME.item.cchTextMax = 0;
1907         }
1908     }
1909
1910     tvisA.DUMMYUNIONNAME.item.iImage         = tvisW->DUMMYUNIONNAME.item.iImage;
1911     tvisA.DUMMYUNIONNAME.item.iSelectedImage = tvisW->DUMMYUNIONNAME.item.iSelectedImage;
1912     tvisA.DUMMYUNIONNAME.item.cChildren      = tvisW->DUMMYUNIONNAME.item.cChildren;
1913     tvisA.DUMMYUNIONNAME.item.lParam         = tvisW->DUMMYUNIONNAME.item.lParam;
1914
1915     lRes = TREEVIEW_InsertItemA(hwnd,wParam,(LPARAM)&tvisA);
1916
1917     if (tvisA.DUMMYUNIONNAME.item.pszText!=LPSTR_TEXTCALLBACKA) 
1918     {
1919         COMCTL32_Free(tvisA.DUMMYUNIONNAME.item.pszText);
1920     }
1921
1922     return lRes;
1923
1924 }
1925
1926
1927 static LRESULT
1928 TREEVIEW_DeleteItem (HWND hwnd, WPARAM wParam, LPARAM lParam)
1929 {
1930   TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
1931   INT iItem;
1932   TREEVIEW_ITEM *wineItem;
1933
1934   TRACE("\n");
1935   if (!infoPtr) return FALSE;
1936
1937   if (lParam == (INT)TVI_ROOT) {
1938         TREEVIEW_RemoveTree (hwnd);
1939   } else {
1940         iItem= (INT) lParam;
1941         wineItem = TREEVIEW_ValidItem (infoPtr, (HTREEITEM)iItem);
1942         if (!wineItem) return FALSE;
1943     TRACE("%s\n",wineItem->pszText);
1944         TREEVIEW_RemoveItem (hwnd, wineItem);
1945   }
1946
1947   TREEVIEW_QueueRefresh (hwnd);
1948
1949   return TRUE;
1950 }
1951
1952
1953
1954 static LRESULT
1955 TREEVIEW_GetIndent (HWND hwnd)
1956 {
1957  TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
1958
1959  TRACE("\n");
1960  return infoPtr->uIndent;
1961 }
1962
1963 static LRESULT
1964 TREEVIEW_SetIndent (HWND hwnd, WPARAM wParam)
1965 {
1966   TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
1967   INT newIndent;
1968    
1969   TRACE("\n");
1970   newIndent=(INT) wParam;
1971   if (newIndent < MINIMUM_INDENT) newIndent=MINIMUM_INDENT;
1972   infoPtr->uIndent=newIndent;
1973   
1974   return 0;
1975 }
1976
1977 static LRESULT
1978 TREEVIEW_GetToolTips (HWND hwnd)
1979
1980 {
1981  TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
1982
1983  TRACE("\n");
1984  return infoPtr->hwndToolTip;
1985 }
1986
1987
1988 static LRESULT
1989 TREEVIEW_SetToolTips (HWND hwnd, WPARAM wParam)
1990
1991 {
1992  TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
1993  HWND prevToolTip;
1994
1995  TRACE("\n");
1996  prevToolTip=infoPtr->hwndToolTip;
1997  infoPtr->hwndToolTip= (HWND) wParam;
1998
1999  return prevToolTip;
2000 }
2001
2002
2003 static LRESULT CALLBACK
2004 TREEVIEW_GetEditControl (HWND hwnd)
2005
2006 {
2007  TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
2008
2009  return infoPtr->hwndEdit;
2010 }
2011
2012 LRESULT CALLBACK
2013 TREEVIEW_Edit_SubclassProc (HWND hwnd, UINT uMsg, WPARAM wParam, 
2014                                                         LPARAM lParam)
2015 {
2016   switch (uMsg) 
2017   {
2018     case WM_ERASEBKGND: 
2019     {
2020       RECT rc;
2021       HDC  hdc = (HDC) wParam;
2022       GetClientRect (hwnd, &rc);
2023       Rectangle (hdc, rc.left, rc.top, rc.right, rc.bottom);
2024       return -1;
2025     }
2026
2027     case WM_GETDLGCODE:
2028     {
2029       return DLGC_WANTARROWS | DLGC_WANTALLKEYS;
2030     }
2031
2032     default:
2033     {
2034       TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(GetParent(hwnd));
2035       return CallWindowProcA( infoPtr->wpEditOrig, hwnd, uMsg, wParam, lParam);
2036     }
2037   }
2038
2039   return 0;
2040 }
2041
2042
2043 /* should handle edit control messages here */
2044
2045 static LRESULT
2046 TREEVIEW_Command (HWND hwnd, WPARAM wParam, LPARAM lParam)
2047
2048 {
2049   TRACE("%x %ld\n",wParam, lParam);
2050  
2051   switch (HIWORD(wParam)) 
2052   {
2053                 case EN_UPDATE:
2054     {
2055       /* 
2056        * Adjust the edit window size 
2057        */
2058       TREEVIEW_INFO *infoPtr  = TREEVIEW_GetInfoPtr(hwnd);
2059       TREEVIEW_ITEM *editItem = TREEVIEW_ValidItem(infoPtr, infoPtr->editItem);
2060       INT           iLength   = GetWindowTextLengthA(infoPtr->hwndEdit);
2061       HDC           hdc       = GetDC(infoPtr->hwndEdit);
2062       TEXTMETRICA   tm;
2063
2064       if ( GetTextMetricsA(hdc, &tm) )
2065       {
2066         LONG newWidth = (iLength * tm.tmAveCharWidth) + 15;
2067             
2068                 SetWindowPos ( 
2069           infoPtr->hwndEdit,
2070           HWND_TOP, 
2071           editItem->text.left - 2, 
2072           editItem->text.top  - 1,
2073           newWidth,
2074           editItem->text.bottom - editItem->text.top  + 3,
2075           SWP_DRAWFRAME );
2076       }
2077       ReleaseDC(hwnd, hdc);
2078
2079       break;
2080     }
2081
2082     case EN_KILLFOCUS:
2083 /*      TREEVIEW_EndEditLabelNow(hwnd, (WPARAM)FALSE, 0); 
2084 */
2085       break;
2086
2087     default:
2088       return SendMessageA (GetParent (hwnd), WM_COMMAND, wParam, lParam);
2089   }
2090
2091   return 0;
2092 }
2093
2094 static LRESULT
2095 TREEVIEW_Size (HWND hwnd, WPARAM wParam, LPARAM lParam)
2096
2097 {
2098   TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
2099
2100   if (infoPtr->bAutoSize) 
2101   {
2102     infoPtr->bAutoSize = FALSE;
2103     return 0;
2104   }
2105   infoPtr->bAutoSize = TRUE;
2106
2107   if (wParam == SIZE_RESTORED)  
2108   {
2109     infoPtr->uTotalWidth  = LOWORD (lParam);
2110         infoPtr->uTotalHeight = HIWORD (lParam);
2111   } else {
2112         FIXME("WM_SIZE flag %x %lx not handled\n", wParam, lParam);
2113   }
2114
2115   TREEVIEW_QueueRefresh (hwnd);
2116   return 0;
2117 }
2118
2119
2120
2121 static LRESULT
2122 TREEVIEW_StyleChanged (HWND hwnd, WPARAM wParam, LPARAM lParam)
2123 {
2124   TRACE("(%x %lx)\n",wParam,lParam);
2125   
2126   TREEVIEW_Refresh (hwnd);
2127
2128   return 0;
2129 }
2130
2131 static LRESULT
2132 TREEVIEW_Create (HWND hwnd, WPARAM wParam, LPARAM lParam)
2133 {
2134     TREEVIEW_INFO *infoPtr;
2135         LOGFONTA logFont;
2136     TEXTMETRICA tm;
2137         HDC hdc;
2138   
2139     TRACE("wnd %x\n",hwnd);
2140       /* allocate memory for info structure */
2141     infoPtr = (TREEVIEW_INFO *) COMCTL32_Alloc (sizeof(TREEVIEW_INFO));
2142
2143     SetWindowLongA( hwnd, 0, (DWORD)infoPtr);
2144
2145     if (infoPtr == NULL) {
2146                 ERR("could not allocate info memory!\n");
2147                 return 0;
2148     }
2149
2150     if ((TREEVIEW_INFO*) GetWindowLongA( hwnd, 0) != infoPtr) {
2151                 ERR("pointer assignment error!\n");
2152                 return 0;
2153     }
2154
2155         hdc=GetDC (hwnd);
2156
2157     /* set default settings */
2158     infoPtr->uInternalStatus=0;
2159     infoPtr->uNumItems=0;
2160     infoPtr->clrBk   = GetSysColor (COLOR_WINDOW);
2161     infoPtr->clrText = GetSysColor (COLOR_BTNTEXT);
2162     infoPtr->clrLine = GetSysColor (COLOR_WINDOWTEXT);
2163     infoPtr->cy = 0;
2164     infoPtr->cx = 0;
2165     infoPtr->uIndent = 15;
2166     infoPtr->himlNormal = NULL;
2167     infoPtr->himlState = NULL;
2168         infoPtr->uItemHeight = -1;
2169     GetTextMetricsA (hdc, &tm);
2170     infoPtr->hFont = GetStockObject (DEFAULT_GUI_FONT);
2171         GetObjectA (infoPtr->hFont, sizeof (LOGFONTA), &logFont);
2172         logFont.lfWeight=FW_BOLD;
2173     infoPtr->hBoldFont = CreateFontIndirectA (&logFont);
2174     
2175     infoPtr->items = NULL;
2176     infoPtr->selectedItem=0;
2177     infoPtr->clrText=-1;        /* use system color */
2178     infoPtr->dropItem=0;
2179     infoPtr->pCallBackSort=NULL;
2180     infoPtr->uScrollTime = 300;  /* milliseconds */
2181
2182 /*
2183     infoPtr->hwndNotify = GetParent32 (hwnd);
2184     infoPtr->bTransparent = ( GetWindowLongA( hwnd, GWL_STYLE) & TBSTYLE_FLAT);
2185 */
2186
2187         infoPtr->hwndToolTip=0;
2188     if (!( GetWindowLongA( hwnd, GWL_STYLE) & TVS_NOTOOLTIPS)) {   /* Create tooltip control */
2189                 TTTOOLINFOA ti;
2190
2191                 infoPtr->hwndToolTip =  
2192                         CreateWindowExA (0, TOOLTIPS_CLASSA, NULL, 0,
2193                    CW_USEDEFAULT, CW_USEDEFAULT,
2194                    CW_USEDEFAULT, CW_USEDEFAULT,
2195                    hwnd, 0, 0, 0);
2196
2197         /* Send NM_TOOLTIPSCREATED notification */
2198         if (infoPtr->hwndToolTip) {
2199             NMTOOLTIPSCREATED nmttc;
2200
2201             nmttc.hdr.hwndFrom = hwnd;
2202             nmttc.hdr.idFrom =  GetWindowLongA( hwnd, GWL_ID);
2203             nmttc.hdr.code = NM_TOOLTIPSCREATED;
2204             nmttc.hwndToolTips = infoPtr->hwndToolTip;
2205
2206             SendMessageA (GetParent (hwnd), WM_NOTIFY,
2207                 (WPARAM) GetWindowLongA( hwnd, GWL_ID), (LPARAM)&nmttc);
2208         }
2209
2210                 ZeroMemory (&ti, sizeof(TTTOOLINFOA));
2211         ti.cbSize   = sizeof(TTTOOLINFOA);
2212         ti.uFlags   = TTF_IDISHWND | TTF_TRACK | TTF_TRANSPARENT ;
2213         ti.hwnd     = hwnd;
2214         ti.uId      = 0;
2215         ti.lpszText = "Test"; /* LPSTR_TEXTCALLBACK; */
2216         SetRectEmpty (&ti.rect);
2217
2218         SendMessageA (infoPtr->hwndToolTip, TTM_ADDTOOLA, 0, (LPARAM)&ti);
2219     }
2220
2221         infoPtr->hwndEdit = CreateWindowExA ( 
2222                           WS_EX_LEFT, 
2223                           "EDIT",
2224                           0,
2225                           WS_CHILD | WS_BORDER | ES_AUTOHSCROLL | 
2226                           ES_WANTRETURN | ES_LEFT,
2227                           0, 0, 0, 0,
2228                           hwnd, 
2229                           0,0,0); /* FIXME: (HMENU)IDTVEDIT,pcs->hInstance,0);*/
2230
2231   SendMessageA ( infoPtr->hwndEdit, WM_SETFONT, infoPtr->hFont, FALSE);
2232         infoPtr->wpEditOrig = (WNDPROC)SetWindowLongA (
2233                                     infoPtr->hwndEdit,
2234                                     GWL_WNDPROC, 
2235                                                                 (LONG) TREEVIEW_Edit_SubclassProc);
2236
2237   ReleaseDC (hwnd, hdc);
2238   return 0;
2239 }
2240
2241
2242
2243 static LRESULT 
2244 TREEVIEW_Destroy (HWND hwnd) 
2245 {
2246    TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
2247      
2248    TRACE("\n");
2249    TREEVIEW_RemoveTree (hwnd);
2250    if (infoPtr->Timer & TV_REFRESH_TIMER_SET) 
2251         KillTimer (hwnd, TV_REFRESH_TIMER);
2252    if (infoPtr->hwndToolTip) 
2253                 DestroyWindow (infoPtr->hwndToolTip);
2254
2255    COMCTL32_Free (infoPtr);
2256    return 0;
2257 }
2258
2259
2260 static LRESULT
2261 TREEVIEW_Paint (HWND hwnd, WPARAM wParam, LPARAM lParam)
2262 {
2263     HDC hdc;
2264     PAINTSTRUCT ps;
2265
2266     TRACE("\n");
2267     hdc = wParam==0 ? BeginPaint (hwnd, &ps) : (HDC)wParam;
2268     TREEVIEW_Refresh (hwnd);
2269     if(!wParam)
2270         EndPaint (hwnd, &ps);
2271     TRACE("done\n");
2272       
2273     return DefWindowProcA (hwnd, WM_PAINT, wParam, lParam);
2274 }
2275
2276 static LRESULT
2277 TREEVIEW_SetFocus (HWND hwnd, WPARAM wParam, LPARAM lParam)
2278 {
2279    TREEVIEW_SendSimpleNotify (hwnd, NM_SETFOCUS);
2280    InvalidateRect(hwnd, NULL, FALSE);
2281    return 0;
2282 }
2283
2284 static LRESULT
2285 TREEVIEW_KillFocus (HWND hwnd, WPARAM wParam, LPARAM lParam)
2286 {
2287    TREEVIEW_SendSimpleNotify (hwnd, NM_KILLFOCUS);
2288    InvalidateRect(hwnd, NULL, FALSE);
2289    return 0;
2290 }
2291
2292 static LRESULT
2293 TREEVIEW_EraseBackground (HWND hwnd, WPARAM wParam, LPARAM lParam)
2294 {
2295     TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
2296     HBRUSH hBrush = CreateSolidBrush (infoPtr->clrBk);
2297     RECT rect;
2298
2299     TRACE("\n");
2300     GetClientRect (hwnd, &rect);
2301     FillRect ((HDC)wParam, &rect, hBrush);
2302     DeleteObject (hBrush);
2303     return TRUE;
2304 }
2305
2306
2307
2308
2309
2310   
2311 /* Notifications */
2312
2313   
2314
2315
2316
2317 static BOOL
2318 TREEVIEW_SendSimpleNotify (HWND hwnd, UINT code)
2319 {
2320     NMHDR nmhdr;
2321
2322     TRACE("%x\n",code);
2323     nmhdr.hwndFrom = hwnd;
2324     nmhdr.idFrom   = GetWindowLongA( hwnd, GWL_ID);
2325     nmhdr.code     = code;
2326
2327     return (BOOL) SendMessageA (GetParent (hwnd), WM_NOTIFY,
2328                                    (WPARAM)nmhdr.idFrom, (LPARAM)&nmhdr);
2329 }
2330
2331
2332
2333 static BOOL
2334 TREEVIEW_SendTreeviewNotify (HWND hwnd, UINT code, UINT action, 
2335                         HTREEITEM oldItem, HTREEITEM newItem)
2336
2337 {
2338   TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
2339   NMTREEVIEWA nmhdr;
2340   TREEVIEW_ITEM  *wineItem;
2341
2342   TRACE("code:%x action:%x olditem:%x newitem:%x\n",
2343                   code,action,(INT)oldItem,(INT)newItem);
2344   nmhdr.hdr.hwndFrom = hwnd;
2345   nmhdr.hdr.idFrom =  GetWindowLongA( hwnd, GWL_ID);
2346   nmhdr.hdr.code = code;
2347   nmhdr.action = action;
2348   if (oldItem) {
2349         wineItem=& infoPtr->items[(INT)oldItem];
2350         nmhdr.itemOld.mask              = wineItem->mask;
2351         nmhdr.itemOld.hItem             = wineItem->hItem;
2352         nmhdr.itemOld.state             = wineItem->state;
2353         nmhdr.itemOld.stateMask = wineItem->stateMask;
2354         nmhdr.itemOld.iImage    = wineItem->iImage;
2355         nmhdr.itemOld.pszText   = wineItem->pszText;
2356         nmhdr.itemOld.cchTextMax= wineItem->cchTextMax;
2357         nmhdr.itemOld.iImage    = wineItem->iImage;
2358         nmhdr.itemOld.iSelectedImage    = wineItem->iSelectedImage;
2359         nmhdr.itemOld.cChildren = wineItem->cChildren;
2360         nmhdr.itemOld.lParam    = wineItem->lParam;
2361   }
2362
2363   if (newItem) {
2364         wineItem=& infoPtr->items[(INT)newItem];
2365         nmhdr.itemNew.mask              = wineItem->mask;
2366         nmhdr.itemNew.hItem             = wineItem->hItem;
2367         nmhdr.itemNew.state             = wineItem->state;
2368         nmhdr.itemNew.stateMask = wineItem->stateMask;
2369         nmhdr.itemNew.iImage    = wineItem->iImage;
2370         nmhdr.itemNew.pszText   = wineItem->pszText;
2371         nmhdr.itemNew.cchTextMax= wineItem->cchTextMax;
2372         nmhdr.itemNew.iImage    = wineItem->iImage;
2373         nmhdr.itemNew.iSelectedImage    = wineItem->iSelectedImage;
2374         nmhdr.itemNew.cChildren = wineItem->cChildren;
2375         nmhdr.itemNew.lParam    = wineItem->lParam;
2376   }
2377
2378   nmhdr.ptDrag.x = 0;
2379   nmhdr.ptDrag.y = 0;
2380
2381   return (BOOL)SendMessageA (GetParent (hwnd), WM_NOTIFY,
2382                                    (WPARAM) GetWindowLongA( hwnd, GWL_ID), (LPARAM)&nmhdr);
2383
2384 }
2385
2386 static BOOL
2387 TREEVIEW_SendTreeviewDnDNotify (HWND hwnd, UINT code, HTREEITEM dragItem, 
2388                                                                 POINT pt)
2389 {
2390   TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
2391   NMTREEVIEWA nmhdr;
2392   TREEVIEW_ITEM  *wineItem;
2393
2394   TRACE("code:%x dragitem:%x\n", code,(INT)dragItem);
2395
2396   nmhdr.hdr.hwndFrom = hwnd;
2397   nmhdr.hdr.idFrom =  GetWindowLongA( hwnd, GWL_ID);
2398   nmhdr.hdr.code = code;
2399   nmhdr.action = 0;
2400   wineItem=& infoPtr->items[(INT)dragItem];
2401   nmhdr.itemNew.mask    = wineItem->mask;
2402   nmhdr.itemNew.hItem   = wineItem->hItem;
2403   nmhdr.itemNew.state   = wineItem->state;
2404   nmhdr.itemNew.lParam  = wineItem->lParam;
2405
2406   nmhdr.ptDrag.x = pt.x;
2407   nmhdr.ptDrag.y = pt.y;
2408
2409   return (BOOL)SendMessageA (GetParent (hwnd), WM_NOTIFY,
2410                                    (WPARAM) GetWindowLongA( hwnd, GWL_ID), (LPARAM)&nmhdr);
2411
2412 }
2413
2414
2415
2416 static BOOL
2417 TREEVIEW_SendDispInfoNotify (HWND hwnd, TREEVIEW_ITEM *wineItem, 
2418                                                                 UINT code, UINT what)
2419 {
2420   NMTVDISPINFOA tvdi;
2421   BOOL retval;
2422   char *buf;
2423
2424   TRACE("item %d, action %x, state %d\n",
2425     (INT)wineItem->hItem,
2426     what,
2427     (INT)wineItem->state);
2428
2429   tvdi.hdr.hwndFrom     = hwnd;
2430   tvdi.hdr.idFrom       =  GetWindowLongA( hwnd, GWL_ID);
2431   tvdi.hdr.code         = code;
2432   tvdi.item.mask        = what;
2433   tvdi.item.hItem       = wineItem->hItem;
2434   tvdi.item.state       = wineItem->state;
2435   tvdi.item.lParam      = wineItem->lParam;
2436   tvdi.item.pszText = COMCTL32_Alloc (128*sizeof(char));
2437   buf = tvdi.item.pszText;
2438
2439   retval=(BOOL)SendMessageA (
2440                   GetParent(hwnd), 
2441                   WM_NOTIFY,
2442                   (WPARAM)tvdi.hdr.idFrom, 
2443                   (LPARAM)&tvdi);
2444
2445   if (what & TVIF_TEXT) {
2446                 wineItem->pszText        = tvdi.item.pszText;
2447                 if (buf==tvdi.item.pszText) {
2448                         wineItem->cchTextMax = 128;
2449                 } else { 
2450                         TRACE("user-supplied buffer\n");
2451                         COMCTL32_Free (buf);
2452                         wineItem->cchTextMax = 0;
2453                 }
2454         }
2455   if (what & TVIF_SELECTEDIMAGE) 
2456                 wineItem->iSelectedImage = tvdi.item.iSelectedImage;
2457   if (what & TVIF_IMAGE) 
2458                 wineItem->iImage         = tvdi.item.iImage;
2459   if (what & TVIF_CHILDREN) 
2460                 wineItem->cChildren      = tvdi.item.cChildren;
2461
2462  return retval;
2463 }
2464
2465
2466
2467 static BOOL
2468 TREEVIEW_SendCustomDrawNotify (HWND hwnd, DWORD dwDrawStage, HDC hdc,
2469                         RECT rc)
2470 {
2471   TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
2472   NMTVCUSTOMDRAW nmcdhdr;
2473   LPNMCUSTOMDRAW nmcd;
2474
2475   TRACE("drawstage:%lx hdc:%x\n", dwDrawStage, hdc);
2476
2477   nmcd= & nmcdhdr.nmcd;
2478   nmcd->hdr.hwndFrom = hwnd;
2479   nmcd->hdr.idFrom =  GetWindowLongA( hwnd, GWL_ID);
2480   nmcd->hdr.code   = NM_CUSTOMDRAW;
2481   nmcd->dwDrawStage= dwDrawStage;
2482   nmcd->hdc                = hdc;
2483   nmcd->rc.left    = rc.left;
2484   nmcd->rc.right   = rc.right;
2485   nmcd->rc.bottom  = rc.bottom;
2486   nmcd->rc.top     = rc.top;
2487   nmcd->dwItemSpec = 0;
2488   nmcd->uItemState = 0;
2489   nmcd->lItemlParam= 0;
2490   nmcdhdr.clrText  = infoPtr->clrText;
2491   nmcdhdr.clrTextBk= infoPtr->clrBk;
2492   nmcdhdr.iLevel   = 0;
2493
2494   return (BOOL)SendMessageA (GetParent (hwnd), WM_NOTIFY,
2495                                (WPARAM) GetWindowLongA( hwnd, GWL_ID), (LPARAM)&nmcdhdr);
2496
2497 }
2498
2499
2500
2501 /* FIXME: need to find out when the flags in uItemState need to be set */
2502
2503 static BOOL
2504 TREEVIEW_SendCustomDrawItemNotify (HWND hwnd, HDC hdc,
2505                         TREEVIEW_ITEM *wineItem, UINT uItemDrawState)
2506 {
2507  TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
2508  NMTVCUSTOMDRAW nmcdhdr;
2509  LPNMCUSTOMDRAW nmcd;
2510  DWORD dwDrawStage,dwItemSpec;
2511  UINT uItemState;
2512  
2513  dwDrawStage=CDDS_ITEM | uItemDrawState;
2514  dwItemSpec=(DWORD)wineItem->hItem;
2515  uItemState=0;
2516  if (wineItem->hItem==infoPtr->selectedItem) uItemState|=CDIS_SELECTED;
2517  if (wineItem->hItem==infoPtr->focusItem)        uItemState|=CDIS_FOCUS;
2518  if (wineItem->hItem==infoPtr->hotItem)      uItemState|=CDIS_HOT;
2519
2520  nmcd= & nmcdhdr.nmcd;
2521  nmcd->hdr.hwndFrom = hwnd;
2522  nmcd->hdr.idFrom =  GetWindowLongA( hwnd, GWL_ID);
2523  nmcd->hdr.code   = NM_CUSTOMDRAW;
2524  nmcd->dwDrawStage= dwDrawStage;
2525  nmcd->hdc                = hdc;
2526  nmcd->rc.left    = wineItem->rect.left;
2527  nmcd->rc.right   = wineItem->rect.right;
2528  nmcd->rc.bottom  = wineItem->rect.bottom;
2529  nmcd->rc.top     = wineItem->rect.top;
2530  nmcd->dwItemSpec = dwItemSpec;
2531  nmcd->uItemState = uItemState;
2532  nmcd->lItemlParam= wineItem->lParam;
2533
2534  nmcdhdr.clrText  = infoPtr->clrText;
2535  nmcdhdr.clrTextBk= infoPtr->clrBk;
2536  nmcdhdr.iLevel   = wineItem->iLevel;
2537
2538  TRACE("drawstage:%lx hdc:%x item:%lx, itemstate:%x\n",
2539                   dwDrawStage, hdc, dwItemSpec, uItemState);
2540
2541  return (BOOL)SendMessageA (GetParent (hwnd), WM_NOTIFY,
2542                                (WPARAM) GetWindowLongA( hwnd, GWL_ID), (LPARAM)&nmcdhdr);
2543 }
2544
2545
2546
2547 /* Note:If the specified item is the child of a collapsed parent item,
2548    the parent's list of child items is (recursively) expanded to reveal the 
2549    specified item. This is mentioned for TREEVIEW_SelectItem; don't 
2550    know if it also applies here.
2551 */
2552
2553 static LRESULT
2554 TREEVIEW_Expand (HWND hwnd, WPARAM wParam, LPARAM lParam)
2555 {
2556   TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
2557   TREEVIEW_ITEM *wineItem;
2558   UINT flag;
2559   INT expand;
2560   
2561   flag = (UINT) wParam;
2562   expand = (INT) lParam;
2563
2564   wineItem = TREEVIEW_ValidItem (infoPtr, (HTREEITEM)expand);
2565
2566   if (!wineItem) 
2567     return 0;
2568   if (!wineItem->cChildren) 
2569     return 0;
2570
2571         if (wineItem->pszText==LPSTR_TEXTCALLBACKA) 
2572                 TRACE ("For item %d, flags %d, state %d\n",
2573                                 expand, flag, wineItem->state);
2574         else
2575                 TRACE("For (%s) item:%d, flags %x, state:%d\n", 
2576                 wineItem->pszText, flag, expand, wineItem->state);
2577
2578   if (wineItem->cChildren==I_CHILDRENCALLBACK) {
2579     FIXME("we don't handle I_CHILDRENCALLBACK yet\n");
2580     return 0;
2581   }
2582
2583   if (flag == TVE_TOGGLE) {    /* FIXME: check exact behaviour here */
2584    flag &= ~TVE_TOGGLE;    /* ie: bitwise ops or 'case' ops */
2585    if (wineItem->state & TVIS_EXPANDED) 
2586      flag |= TVE_COLLAPSE;
2587    else
2588      flag |= TVE_EXPAND;
2589   }
2590
2591   switch (flag) 
2592   {
2593     case TVE_COLLAPSERESET: 
2594       TRACE("  case TVE_COLLAPSERESET\n");
2595       if (!wineItem->state & TVIS_EXPANDED) 
2596         return 0;
2597
2598        wineItem->state &= ~(TVIS_EXPANDEDONCE | TVIS_EXPANDED);
2599        TREEVIEW_RemoveAllChildren (hwnd, wineItem);
2600        break;
2601
2602     case TVE_COLLAPSE: 
2603       TRACE("  case TVE_COLLAPSE\n");
2604       if (!wineItem->state & TVIS_EXPANDED) 
2605         return 0;
2606
2607       wineItem->state &= ~TVIS_EXPANDED;
2608       break;
2609
2610     case TVE_EXPAND: 
2611       TRACE("  case TVE_EXPAND\n");
2612       if (wineItem->state & TVIS_EXPANDED) 
2613         return 0;
2614
2615       TRACE("  is not expanded...\n");
2616      
2617       if (!(wineItem->state & TVIS_EXPANDEDONCE))  
2618       { 
2619         TRACE("  and has never been expanded...\n");
2620         wineItem->state |= TVIS_EXPANDED;
2621
2622         /* this item has never been expanded */
2623         if (TREEVIEW_SendTreeviewNotify (
2624               hwnd, 
2625               TVN_ITEMEXPANDING, 
2626               TVE_EXPAND, 
2627               0, 
2628               (HTREEITEM)expand))
2629         {
2630           TRACE("  TVN_ITEMEXPANDING returned TRUE, exiting...\n");
2631           return FALSE;   
2632         }
2633
2634         /* FIXME
2635          * Since the TVN_ITEMEXPANDING message may has caused the parent to
2636          * insert new items which in turn may have cause items placeholder 
2637          * reallocation, I reassign the current item pointer so we have 
2638          * something valid to work with... 
2639          * However, this should not be necessary, 
2640          * investigation required in TREEVIEW_InsertItemA
2641          */
2642         wineItem = TREEVIEW_ValidItem (infoPtr, (HTREEITEM)expand);
2643         if (! wineItem) 
2644         { 
2645           ERR(
2646             "Catastropic situation, cannot retreive item #%d\n",
2647             expand);
2648           return FALSE;
2649         }
2650
2651         wineItem->state |= TVIS_EXPANDEDONCE;
2652         TRACE("  TVN_ITEMEXPANDING sent...\n");
2653
2654         TREEVIEW_SendTreeviewNotify (
2655           hwnd, 
2656           TVN_ITEMEXPANDED, 
2657           TVE_EXPAND, 
2658           0, 
2659           (HTREEITEM)expand);
2660
2661         TRACE("  TVN_ITEMEXPANDED sent...\n");
2662
2663       }
2664       else
2665       {
2666         /* this item has already been expanded */
2667         wineItem->state |= TVIS_EXPANDED;
2668       }
2669       break;
2670
2671     case TVE_EXPANDPARTIAL:
2672       TRACE("  case TVE_EXPANDPARTIAL\n");
2673       FIXME("TVE_EXPANDPARTIAL not implemented\n");
2674       wineItem->state ^=TVIS_EXPANDED;
2675       wineItem->state |=TVIS_EXPANDEDONCE;
2676       break;
2677   }
2678
2679   TRACE("Exiting, Item %d state is now %d...\n", 
2680     expand, 
2681     wineItem->state);
2682   
2683   TREEVIEW_QueueRefresh (hwnd);
2684   return TRUE;
2685 }
2686
2687
2688
2689 static TREEVIEW_ITEM *
2690 TREEVIEW_HitTestPoint (HWND hwnd, POINT pt)
2691 {
2692  TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
2693  TREEVIEW_ITEM *wineItem;
2694  RECT rect;
2695
2696  GetClientRect (hwnd, &rect);
2697
2698  if (!infoPtr->firstVisible) return NULL;
2699
2700  wineItem=&infoPtr->items [(INT)infoPtr->firstVisible];
2701
2702  while ((wineItem!=NULL) && (pt.y > wineItem->rect.bottom))
2703        wineItem=TREEVIEW_GetNextListItem (infoPtr,wineItem);
2704         
2705  if (!wineItem) 
2706         return NULL;
2707
2708  return wineItem;
2709 }
2710
2711
2712
2713
2714 static LRESULT
2715 TREEVIEW_HitTest (HWND hwnd, LPARAM lParam)
2716 {
2717   LPTVHITTESTINFO lpht=(LPTVHITTESTINFO) lParam;
2718   TREEVIEW_ITEM *wineItem;
2719   RECT rect;
2720   UINT status,x,y;
2721
2722   GetClientRect (hwnd, &rect);
2723   status=0;
2724   x=lpht->pt.x;
2725   y=lpht->pt.y;
2726   if (x < rect.left)  status|=TVHT_TOLEFT;
2727   if (x > rect.right) status|=TVHT_TORIGHT;
2728   if (y < rect.top )  status|=TVHT_ABOVE;
2729   if (y > rect.bottom) status|=TVHT_BELOW;
2730
2731   if (status) {
2732     lpht->flags=status;
2733     return 0;
2734   }
2735
2736   wineItem=TREEVIEW_HitTestPoint (hwnd, lpht->pt);
2737   if (!wineItem) {  
2738     lpht->flags=TVHT_NOWHERE;
2739     return 0;
2740   }
2741
2742   /* FIXME: implement other flags
2743    * Assign the appropriate flags depending on the click location 
2744    * Intitialize flags before to "|=" it... 
2745    */
2746   lpht->flags=0;
2747
2748   if (x < wineItem->expandBox.left) 
2749   {
2750     lpht->flags |= TVHT_ONITEMINDENT;
2751   } 
2752   else if ( ( x >= wineItem->expandBox.left) && 
2753             ( x <= wineItem->expandBox.right))
2754   {
2755     lpht->flags |= TVHT_ONITEMBUTTON;
2756   }
2757   else if (x < wineItem->rect.right) 
2758   {
2759     lpht->flags |= TVHT_ONITEMLABEL;    
2760   } 
2761   else
2762   {
2763     lpht->flags|=TVHT_ONITEMRIGHT;
2764   }
2765  
2766   lpht->hItem=wineItem->hItem;
2767
2768   return (LRESULT) wineItem->hItem;
2769 }
2770
2771 LRESULT WINAPI
2772 TREEVIEW_EndEditLabelNow (HWND hwnd, WPARAM wParam, LPARAM lParam)
2773 {
2774   TREEVIEW_INFO *infoPtr    = TREEVIEW_GetInfoPtr(hwnd);
2775   TREEVIEW_ITEM *editedItem = TREEVIEW_ValidItem (infoPtr, infoPtr->editItem);
2776   BOOL          bRevert     = (BOOL)wParam;
2777   BOOL          bReturn     = ! bRevert;
2778
2779   if ( ! (BOOL)wParam ) /* wParam is set to true to cancel the edition */
2780   {
2781                 if ( TREEVIEW_SendDispInfoNotify(  /* return true to cancel edition */
2782            hwnd, 
2783            editedItem,
2784            TVN_ENDLABELEDIT, 
2785            0))
2786     {
2787       bRevert = TRUE;
2788       bReturn = FALSE; 
2789     }
2790   }
2791
2792   if (bRevert == FALSE) /* Apply the changes */
2793   {
2794     char tmpText[1024];
2795     int  iLength = GetWindowTextA(infoPtr->hwndEdit, tmpText, 1023);
2796     bReturn      = FALSE;
2797
2798     if (iLength == 0) 
2799     {
2800       ERR("Problem retreiving new item label.");
2801     }
2802     else if (iLength >= 1023)
2803     {
2804       ERR(
2805         "Insuficient space to retrieve new item label, new label ignored.");
2806     }
2807     else
2808     {
2809       if (strcmp( tmpText, editedItem->pszText ) == 0)
2810         /* Do nothing if the label has not changed */
2811         bReturn = TRUE;
2812       else
2813       {
2814         LPSTR tmpLabel = COMCTL32_Alloc( iLength+1 );
2815   
2816         if ( tmpLabel == NULL )
2817           ERR(
2818             "OutOfMemory, cannot allocate space for label");
2819         else
2820         {
2821           COMCTL32_Free(editedItem->pszText);
2822           editedItem->pszText = tmpLabel;
2823           lstrcpyA( editedItem->pszText, tmpText);
2824           bReturn = TRUE;
2825         }
2826       }
2827     }
2828
2829                 ShowWindow(infoPtr->hwndEdit, SW_HIDE);
2830                 EnableWindow(infoPtr->hwndEdit, FALSE);
2831     infoPtr->editItem = 0;
2832   }
2833
2834   return bReturn;
2835 }
2836
2837
2838
2839 static LRESULT
2840 TREEVIEW_LButtonDoubleClick (HWND hwnd, WPARAM wParam, LPARAM lParam)
2841 {
2842   TREEVIEW_ITEM *wineItem;
2843   POINT pt;
2844
2845   TRACE("\n");
2846   pt.x = (INT)LOWORD(lParam);
2847   pt.y = (INT)HIWORD(lParam);
2848   SetFocus (hwnd);
2849
2850   wineItem=TREEVIEW_HitTestPoint (hwnd, pt);
2851   if (!wineItem) return 0;
2852   TRACE("item %d \n",(INT)wineItem->hItem);
2853  
2854   if (TREEVIEW_SendSimpleNotify (hwnd, NM_DBLCLK)!=TRUE) {     /* FIXME!*/
2855         TREEVIEW_Expand (hwnd, (WPARAM) TVE_TOGGLE, (LPARAM) wineItem->hItem);
2856  }
2857  return TRUE;
2858 }
2859
2860
2861 static LRESULT
2862 TREEVIEW_LButtonDown (HWND hwnd, WPARAM wParam, LPARAM lParam)
2863 {
2864   TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
2865   INT iItem;
2866   TVHITTESTINFO ht;
2867
2868   ht.pt.x = (INT)LOWORD(lParam);
2869   ht.pt.y = (INT)HIWORD(lParam);
2870
2871   SetFocus (hwnd);
2872   iItem=TREEVIEW_HitTest (hwnd, (LPARAM) &ht);
2873   TRACE("item %d \n",iItem);
2874
2875   if (ht.flags & TVHT_ONITEMBUTTON) {
2876     TREEVIEW_Expand (hwnd, (WPARAM) TVE_TOGGLE, (LPARAM) iItem);
2877   }
2878   else
2879   {
2880     infoPtr->uInternalStatus|=TV_LDRAG;
2881   }
2882   
2883   return 0;
2884 }
2885
2886 static LRESULT
2887 TREEVIEW_LButtonUp (HWND hwnd, WPARAM wParam, LPARAM lParam)
2888 {
2889   TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
2890   INT           iItem;
2891   TREEVIEW_ITEM *editItem;
2892   TVHITTESTINFO ht;
2893  
2894   ht.pt.x = (INT)LOWORD(lParam);
2895   ht.pt.y = (INT)HIWORD(lParam);
2896  
2897   TRACE("\n");
2898
2899   /* Return true to cancel default behaviour */
2900   if ( TREEVIEW_SendSimpleNotify (hwnd, NM_CLICK) )
2901     return 0;
2902
2903   /* Get the item */
2904   iItem = TREEVIEW_HitTest (hwnd, (LPARAM) &ht);
2905   if (!iItem) 
2906     return 0;
2907
2908   editItem = TREEVIEW_ValidItem(infoPtr, (HTREEITEM)iItem);
2909  
2910   infoPtr->uInternalStatus &= ~(TV_LDRAG | TV_LDRAGGING);
2911
2912   /* 
2913    * If the style allow editing and the node is already selected 
2914    * and the click occured on the item label...
2915    */
2916   if ( ( GetWindowLongA( hwnd, GWL_STYLE) & TVS_EDITLABELS ) && 
2917        ( editItem->state & TVIS_SELECTED ) &&
2918        ( ht.flags & TVHT_ONITEMLABEL ))
2919   {
2920     if ( infoPtr->editItem == 0 ) /* If we are not curently editing */
2921     {
2922                 if ( TREEVIEW_SendDispInfoNotify(  /* Return true to cancel edition */
2923               hwnd, 
2924               editItem, 
2925               TVN_BEGINLABELEDIT, 
2926               0))
2927       {
2928         return 0; 
2929       }
2930   
2931                 TRACE("Edit started for %s.\n", editItem->pszText);
2932                 infoPtr->editItem = editItem->hItem;
2933   
2934                 SetWindowPos ( 
2935         infoPtr->hwndEdit, 
2936         HWND_TOP, 
2937         editItem->text.left - 2, 
2938         editItem->text.top  - 1,
2939         editItem->text.right  - editItem->text.left + 20 ,
2940         editItem->text.bottom - editItem->text.top  + 3,
2941         SWP_DRAWFRAME );
2942   
2943                 SetWindowTextA( infoPtr->hwndEdit, editItem->pszText );
2944       SendMessageA  ( infoPtr->hwndEdit, EM_SETSEL, 0, -1 );
2945                 SetFocus      ( infoPtr->hwndEdit); 
2946       ShowWindow    ( infoPtr->hwndEdit, SW_SHOW); 
2947     }
2948   }
2949   else if ( infoPtr->editItem != 0 ) /* If we are curently editing */
2950   {
2951     TREEVIEW_EndEditLabelNow(hwnd, (WPARAM)FALSE, 0);
2952   }
2953   else if ( ht.flags & (TVHT_ONITEMLABEL | TVHT_ONITEMICON))
2954   {
2955     TREEVIEW_DoSelectItem (
2956       hwnd, 
2957       TVGN_CARET, 
2958       (HTREEITEM)iItem, 
2959       TVC_BYMOUSE);
2960   }
2961
2962   return 0;
2963 }
2964
2965
2966 static LRESULT
2967 TREEVIEW_RButtonDown (HWND hwnd, WPARAM wParam, LPARAM lParam)
2968 {
2969  TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
2970
2971  TRACE("\n");
2972  infoPtr->uInternalStatus|=TV_RDRAG;
2973  return 0;
2974 }
2975
2976 static LRESULT
2977 TREEVIEW_RButtonUp (HWND hwnd, WPARAM wParam, LPARAM lParam)
2978 {
2979  TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
2980
2981  TRACE("\n");
2982  if (TREEVIEW_SendSimpleNotify (hwnd, NM_RCLICK)) return 0;
2983  infoPtr->uInternalStatus&= ~(TV_RDRAG | TV_RDRAGGING);
2984  return 0;
2985 }
2986
2987
2988 static LRESULT
2989 TREEVIEW_MouseMove (HWND hwnd, WPARAM wParam, LPARAM lParam)
2990 {
2991  TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
2992  TREEVIEW_ITEM *hotItem;
2993  POINT pt;
2994
2995  pt.x=(INT) LOWORD (lParam);
2996  pt.y=(INT) HIWORD (lParam);
2997  hotItem=TREEVIEW_HitTestPoint (hwnd, pt);
2998  if (!hotItem) return 0;
2999  infoPtr->focusItem=hotItem->hItem;
3000
3001  if ( GetWindowLongA( hwnd, GWL_STYLE) & TVS_DISABLEDRAGDROP) return 0;
3002
3003  if (infoPtr->uInternalStatus & TV_LDRAG) {
3004         TREEVIEW_SendTreeviewDnDNotify (hwnd, TVN_BEGINDRAG, hotItem->hItem, pt);
3005         infoPtr->uInternalStatus &= ~TV_LDRAG;
3006         infoPtr->uInternalStatus |= TV_LDRAGGING;
3007         infoPtr->dropItem=hotItem->hItem;
3008         return 0;
3009  }
3010
3011  if (infoPtr->uInternalStatus & TV_RDRAG) {
3012         TREEVIEW_SendTreeviewDnDNotify (hwnd, TVN_BEGINRDRAG, hotItem->hItem, pt);
3013         infoPtr->uInternalStatus &= ~TV_RDRAG;
3014         infoPtr->uInternalStatus |= TV_RDRAGGING;
3015         infoPtr->dropItem=hotItem->hItem;
3016         return 0;
3017  }
3018  
3019  return 0;
3020 }
3021
3022
3023 static LRESULT
3024 TREEVIEW_CreateDragImage (HWND hwnd, WPARAM wParam, LPARAM lParam)
3025 {
3026  TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
3027  TREEVIEW_ITEM *dragItem;
3028  INT cx,cy;
3029  HDC    hdc,htopdc;
3030  HWND hwtop;
3031  HBITMAP hbmp,hOldbmp;
3032  SIZE  size;
3033  RECT  rc;
3034  HFONT hOldFont;
3035  char    *itemtxt;
3036  
3037  TRACE("\n");
3038  if (!(infoPtr->himlNormal))  return 0;
3039  dragItem=TREEVIEW_ValidItem (infoPtr, (HTREEITEM) lParam);
3040  
3041  if (!dragItem) return 0;
3042  itemtxt=dragItem->pszText;
3043
3044  hwtop=GetDesktopWindow ();
3045  htopdc= GetDC (hwtop);
3046  hdc=CreateCompatibleDC (htopdc); 
3047  
3048  hOldFont=SelectObject (hdc, infoPtr->hFont);
3049  GetTextExtentPoint32A (hdc, itemtxt, lstrlenA (itemtxt), &size);
3050  TRACE("%d %d %s %d\n",size.cx,size.cy,itemtxt,lstrlenA(itemtxt));
3051  hbmp=CreateCompatibleBitmap (htopdc, size.cx, size.cy);
3052  hOldbmp=SelectObject (hdc, hbmp);
3053
3054  ImageList_GetIconSize (infoPtr->himlNormal, &cx, &cy);
3055  size.cx+=cx;
3056  if (cy>size.cy) size.cy=cy;
3057
3058  infoPtr->dragList=ImageList_Create (size.cx, size.cy, ILC_COLOR, 10, 10);
3059  ImageList_Draw (infoPtr->himlNormal, dragItem->iImage, hdc, 0, 0, ILD_NORMAL);
3060
3061 /*
3062  ImageList_GetImageInfo (infoPtr->himlNormal, dragItem->hItem, &iminfo);
3063  ImageList_AddMasked (infoPtr->dragList, iminfo.hbmImage, CLR_DEFAULT);
3064 */
3065
3066 /* draw item text */
3067
3068  SetRect (&rc, cx, 0, size.cx,size.cy);
3069  DrawTextA (hdc, itemtxt, lstrlenA (itemtxt), &rc, DT_LEFT);
3070  SelectObject (hdc, hOldFont);
3071  SelectObject (hdc, hOldbmp);
3072
3073  ImageList_Add (infoPtr->dragList, hbmp, 0);
3074
3075  DeleteDC (hdc);
3076  DeleteObject (hbmp);
3077  ReleaseDC (hwtop, htopdc);
3078
3079  return (LRESULT)infoPtr->dragList;
3080 }
3081
3082
3083 static LRESULT
3084 TREEVIEW_DoSelectItem (HWND hwnd, INT action, HTREEITEM newSelect, INT cause)
3085
3086 {
3087   TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
3088   TREEVIEW_ITEM *prevItem,*wineItem;
3089   INT prevSelect;
3090
3091   wineItem = TREEVIEW_ValidItem (infoPtr, (HTREEITEM)newSelect);
3092
3093   TRACE("Entering item %d, flag %x, cause %x, state %d\n", 
3094     (INT)newSelect, 
3095     action, 
3096     cause,
3097     wineItem->state);
3098
3099   if ( (wineItem) && (wineItem->parent))
3100   {
3101     /* 
3102      * If the item has a collapse parent expand the parent so he 
3103      * can expose the item 
3104      */
3105     TREEVIEW_ITEM *parentItem = TREEVIEW_ValidItem (infoPtr, wineItem->parent);
3106     if ( !(parentItem->state & TVIS_EXPANDED)) 
3107       TREEVIEW_Expand (hwnd, TVE_EXPAND, (LPARAM) wineItem->parent);
3108   }
3109
3110   switch (action) 
3111   {
3112     case TVGN_CARET: 
3113       prevSelect=(INT)infoPtr->selectedItem;
3114
3115       if ((HTREEITEM)prevSelect==newSelect) 
3116         return FALSE;
3117
3118       prevItem= TREEVIEW_ValidItem (infoPtr, (HTREEITEM)prevSelect);
3119
3120       if (newSelect) 
3121         if (TREEVIEW_SendTreeviewNotify(
3122               hwnd, 
3123               TVN_SELCHANGING, 
3124               cause, 
3125               (HTREEITEM)prevSelect, 
3126               (HTREEITEM)newSelect)) 
3127           return FALSE;       /* FIXME: OK? */
3128     
3129       if (prevItem) 
3130         prevItem->state &= ~TVIS_SELECTED;
3131       if (wineItem) 
3132         wineItem->state |=  TVIS_SELECTED;
3133
3134       infoPtr->selectedItem=(HTREEITEM)newSelect;
3135
3136       TREEVIEW_SendTreeviewNotify(
3137         hwnd, 
3138         TVN_SELCHANGED, 
3139         cause,
3140         (HTREEITEM)prevSelect,
3141         (HTREEITEM)newSelect);
3142
3143       break;
3144
3145     case TVGN_DROPHILITE: 
3146       prevItem= TREEVIEW_ValidItem (infoPtr, infoPtr->dropItem);
3147
3148       if (prevItem) 
3149         prevItem->state &= ~TVIS_DROPHILITED;
3150
3151       infoPtr->dropItem=(HTREEITEM)newSelect;
3152
3153       if (wineItem) 
3154         wineItem->state |=TVIS_DROPHILITED;
3155
3156       break;
3157
3158     case TVGN_FIRSTVISIBLE:
3159       FIXME("FIRSTVISIBLE not implemented\n");
3160       break;
3161  }
3162  
3163  TREEVIEW_QueueRefresh (hwnd);
3164
3165   TRACE("Leaving state %d\n", wineItem->state);
3166  return TRUE;
3167 }
3168
3169 /* FIXME: handle NM_KILLFocus enzo */
3170 static LRESULT
3171 TREEVIEW_SelectItem (HWND hwnd, WPARAM wParam, LPARAM lParam)
3172
3173 {
3174  return TREEVIEW_DoSelectItem (hwnd, wParam, (HTREEITEM) lParam, TVC_UNKNOWN);
3175 }
3176
3177
3178
3179    
3180 static LRESULT
3181 TREEVIEW_GetFont (HWND hwnd, WPARAM wParam, LPARAM lParam)
3182
3183 {
3184  TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
3185
3186  TRACE("%x\n",infoPtr->hFont);
3187  return infoPtr->hFont;
3188 }
3189
3190 static LRESULT
3191 TREEVIEW_SetFont (HWND hwnd, WPARAM wParam, LPARAM lParam)
3192
3193 {
3194  TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
3195  TEXTMETRICA tm;
3196  LOGFONTA logFont;
3197  HFONT hFont, hOldFont;
3198  INT height;
3199  HDC hdc;
3200
3201  TRACE("%x %lx\n",wParam, lParam);
3202  
3203  infoPtr->hFont = (HFONT)wParam;
3204
3205  hFont = infoPtr->hFont ? infoPtr->hFont : GetStockObject (SYSTEM_FONT);
3206
3207  GetObjectA (infoPtr->hFont, sizeof (LOGFONTA), &logFont);
3208  logFont.lfWeight=FW_BOLD;
3209  infoPtr->hBoldFont = CreateFontIndirectA (&logFont);
3210
3211  hdc = GetDC (0);
3212  hOldFont = SelectObject (hdc, hFont);
3213  GetTextMetricsA (hdc, &tm);
3214  height= tm.tmHeight + tm.tmExternalLeading;
3215  if (height>infoPtr->uRealItemHeight) 
3216         infoPtr->uRealItemHeight=height;
3217  SelectObject (hdc, hOldFont);
3218  ReleaseDC (0, hdc);
3219
3220  if (lParam)    
3221         TREEVIEW_QueueRefresh (hwnd);
3222  
3223  return 0;
3224 }
3225
3226
3227
3228 static LRESULT
3229 TREEVIEW_VScroll (HWND hwnd, WPARAM wParam, LPARAM lParam)
3230
3231 {
3232   TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
3233   int maxHeight;
3234
3235   TRACE("wp %x, lp %lx\n", wParam, lParam);
3236   if (!infoPtr->uInternalStatus & TV_VSCROLL) return FALSE;
3237
3238   switch (LOWORD (wParam)) {
3239         case SB_LINEUP: 
3240                         if (!infoPtr->cy) return FALSE;
3241                         infoPtr->cy -= infoPtr->uRealItemHeight;
3242                         if (infoPtr->cy < 0) infoPtr->cy=0;
3243                         break;
3244         case SB_LINEDOWN: 
3245                         maxHeight=infoPtr->uTotalHeight-infoPtr->uVisibleHeight;
3246                         if (infoPtr->cy == maxHeight) return FALSE;
3247                         infoPtr->cy += infoPtr->uRealItemHeight;
3248                         if (infoPtr->cy > maxHeight) 
3249                                 infoPtr->cy = maxHeight;
3250                         break;
3251         case SB_PAGEUP: 
3252                         if (!infoPtr->cy) return FALSE;
3253                         infoPtr->cy -= infoPtr->uVisibleHeight;
3254                         if (infoPtr->cy < 0) infoPtr->cy=0;
3255                         break;
3256         case SB_PAGEDOWN:
3257                         maxHeight=infoPtr->uTotalHeight-infoPtr->uVisibleHeight;
3258                         if (infoPtr->cy == maxHeight) return FALSE;
3259                         infoPtr->cy += infoPtr->uVisibleHeight;
3260             if (infoPtr->cy > maxHeight)
3261                 infoPtr->cy = maxHeight;
3262                         break;
3263         case SB_THUMBTRACK: 
3264                         infoPtr->cy = HIWORD (wParam);
3265                         break;
3266                         
3267   }
3268   
3269   TREEVIEW_QueueRefresh (hwnd);
3270   return TRUE;
3271 }
3272
3273 static LRESULT
3274 TREEVIEW_HScroll (HWND hwnd, WPARAM wParam, LPARAM lParam) 
3275 {
3276   TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
3277   int maxWidth;
3278
3279   TRACE("wp %lx, lp %x\n", lParam, wParam);
3280         
3281   if (!infoPtr->uInternalStatus & TV_HSCROLL) return FALSE;
3282
3283   switch (LOWORD (wParam)) {
3284         case SB_LINEUP: 
3285                         if (!infoPtr->cx) return FALSE;
3286                         infoPtr->cx -= infoPtr->uRealItemHeight;
3287                         if (infoPtr->cx < 0) infoPtr->cx=0;
3288                         break;
3289         case SB_LINEDOWN: 
3290                         maxWidth=infoPtr->uTotalWidth-infoPtr->uVisibleWidth;
3291                         if (infoPtr->cx == maxWidth) return FALSE;
3292                         infoPtr->cx += infoPtr->uRealItemHeight; /*FIXME */
3293                         if (infoPtr->cx > maxWidth) 
3294                                 infoPtr->cx = maxWidth;
3295                         break;
3296         case SB_PAGEUP: 
3297                         if (!infoPtr->cx) return FALSE;
3298                         infoPtr->cx -= infoPtr->uVisibleWidth;
3299                         if (infoPtr->cx < 0) infoPtr->cx=0;
3300                         break;
3301         case SB_PAGEDOWN:
3302                         maxWidth=infoPtr->uTotalWidth-infoPtr->uVisibleWidth;
3303                         if (infoPtr->cx == maxWidth) return FALSE;
3304                         infoPtr->cx += infoPtr->uVisibleWidth;
3305             if (infoPtr->cx > maxWidth)
3306                 infoPtr->cx = maxWidth;
3307                         break;
3308         case SB_THUMBTRACK: 
3309                         infoPtr->cx = HIWORD (wParam);
3310                         break;
3311                         
3312   }
3313   
3314   TREEVIEW_QueueRefresh (hwnd);
3315   return TRUE;
3316 }
3317
3318
3319 static LRESULT
3320 TREEVIEW_KeyDown (HWND hwnd, WPARAM wParam, LPARAM lParam)
3321 {
3322  TREEVIEW_INFO *infoPtr        = TREEVIEW_GetInfoPtr(hwnd);
3323  HTREEITEM     hNewSelection   = 0;
3324  INT           scrollNeeds     = -1;
3325  INT           cyChangeNeeds   = -1;
3326  INT           prevSelect      = (INT)infoPtr->selectedItem;
3327
3328  TREEVIEW_ITEM *prevItem       = 
3329     (prevSelect != 0 ) ? 
3330       TREEVIEW_ValidItem (infoPtr, (HTREEITEM)prevSelect) :
3331       NULL;
3332
3333  TREEVIEW_ITEM *newItem        = NULL;
3334
3335  TRACE("%x %lx\n",wParam, lParam);
3336
3337  if (prevSelect == 0) 
3338    return FALSE;
3339
3340  switch (wParam) {
3341         case VK_UP: 
3342                 newItem=TREEVIEW_GetPrevListItem (infoPtr, prevItem);
3343
3344                 if (!newItem) 
3345                         newItem=& infoPtr->items[(INT)infoPtr->TopRootItem];
3346
3347     hNewSelection = newItem->hItem;
3348
3349     if (! newItem->visible)
3350       scrollNeeds = SB_LINEUP;
3351
3352                 break;
3353
3354         case VK_DOWN: 
3355                 newItem=TREEVIEW_GetNextListItem (infoPtr, prevItem);
3356
3357                 if (!newItem) 
3358       newItem=prevItem;
3359
3360     hNewSelection = newItem->hItem;
3361
3362     if (! newItem->visible)
3363       scrollNeeds = SB_LINEDOWN;
3364
3365                 break;
3366
3367         case VK_HOME:
3368                 newItem       = &infoPtr->items[(INT)infoPtr->TopRootItem];
3369     hNewSelection = newItem->hItem;
3370     cyChangeNeeds = 0;
3371                 break;
3372
3373         case VK_END:
3374                 newItem       = &infoPtr->items[(INT)infoPtr->TopRootItem];
3375                 newItem       = TREEVIEW_GetLastListItem (infoPtr, newItem);
3376     hNewSelection = newItem->hItem;
3377
3378     if (! newItem->visible)
3379       cyChangeNeeds = infoPtr->uTotalHeight-infoPtr->uVisibleHeight;
3380
3381                 break;
3382
3383         case VK_LEFT:
3384     if ( (prevItem->cChildren > 0) && (prevItem->state & TVIS_EXPANDED) )
3385     {
3386       TREEVIEW_Expand(hwnd, TVE_COLLAPSE, prevSelect );
3387     }
3388     else if ((INT)prevItem->parent) 
3389     {
3390       newItem = (& infoPtr->items[(INT)prevItem->parent]);
3391       if (! newItem->visible) 
3392         /* FIXME find a way to make this item the first visible... */
3393         newItem = NULL; 
3394
3395       hNewSelection = newItem->hItem;
3396     }
3397
3398     break;
3399
3400         case VK_RIGHT:
3401     if ( ( prevItem->cChildren > 0)  || 
3402          ( prevItem->cChildren == I_CHILDRENCALLBACK))
3403     {
3404       if (! (prevItem->state & TVIS_EXPANDED))
3405         TREEVIEW_Expand(hwnd, TVE_EXPAND, prevSelect );
3406       else
3407       {
3408         newItem = (& infoPtr->items[(INT)prevItem->firstChild]);
3409         hNewSelection = newItem->hItem;
3410       }
3411     }
3412
3413     break;
3414
3415   case VK_ADD:
3416     if (! (prevItem->state & TVIS_EXPANDED))
3417       TREEVIEW_Expand(hwnd, TVE_EXPAND, prevSelect );
3418     break;
3419
3420   case VK_SUBTRACT:
3421     if (prevItem->state & TVIS_EXPANDED)
3422       TREEVIEW_Expand(hwnd, TVE_COLLAPSE, prevSelect );
3423     break;
3424
3425   case VK_PRIOR:
3426     
3427                 newItem=TREEVIEW_GetListItem(
3428               infoPtr, 
3429               prevItem,
3430               -1*(TREEVIEW_GetVisibleCount(hwnd,0,0)-3));
3431                 if (!newItem) 
3432       newItem=prevItem;
3433   
3434     hNewSelection = newItem->hItem;
3435
3436     if (! newItem->visible)
3437       scrollNeeds = SB_PAGEUP;
3438
3439                 break;
3440
3441   case VK_NEXT:
3442                 newItem=TREEVIEW_GetListItem(
3443               infoPtr, 
3444               prevItem,
3445               TREEVIEW_GetVisibleCount(hwnd,0,0)-3);
3446
3447                 if (!newItem) 
3448       newItem=prevItem;
3449
3450     hNewSelection = newItem->hItem;
3451
3452     if (! newItem->visible)
3453       scrollNeeds = SB_PAGEDOWN;
3454
3455                 break;
3456
3457         case VK_BACK:
3458
3459         case VK_RETURN:
3460
3461   default:
3462                 FIXME("%x not implemented\n", wParam);
3463                 break;
3464  }
3465
3466   if (hNewSelection) 
3467   {
3468 /* 
3469     This works but does not send notification...
3470
3471     prevItem->state      &= ~TVIS_SELECTED;
3472     newItem->state       |=  TVIS_SELECTED;
3473     infoPtr->selectedItem = hNewSelection;
3474     TREEVIEW_QueueRefresh (hwnd);
3475 */
3476
3477     if ( TREEVIEW_DoSelectItem( 
3478            hwnd, 
3479            TVGN_CARET, 
3480            (HTREEITEM)hNewSelection, 
3481            TVC_BYKEYBOARD))
3482     {
3483       /* If selection change is allowed for the new item, perform scrolling */
3484       if (scrollNeeds != -1)
3485         TREEVIEW_VScroll(hwnd, scrollNeeds, 0);
3486   
3487       if (cyChangeNeeds != -1)
3488         infoPtr->cy = cyChangeNeeds;
3489
3490       /* FIXME: Something happen in the load the in the two weeks before 
3491          april 1st 1999 which makes this SetFocus mandatory otherwise, the focus 
3492          is lost... However the SetFocus should not be required...*/
3493                
3494       SetFocus(hwnd);
3495     }
3496   }
3497
3498   return FALSE;
3499 }
3500
3501
3502 static LRESULT
3503 TREEVIEW_GetScrollTime (HWND hwnd)
3504 {
3505   TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
3506
3507   return infoPtr->uScrollTime;
3508 }
3509
3510
3511 static LRESULT
3512 TREEVIEW_SetScrollTime (HWND hwnd, UINT uScrollTime)
3513 {
3514   TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
3515   UINT uOldScrollTime = infoPtr->uScrollTime;
3516
3517   infoPtr->uScrollTime = min (uScrollTime, 100);
3518
3519   return uOldScrollTime;
3520 }
3521
3522
3523 static LRESULT WINAPI
3524 TREEVIEW_WindowProc (HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
3525 {
3526     switch (uMsg) {
3527         case TVM_INSERTITEMA:
3528           return TREEVIEW_InsertItemA (hwnd, wParam, lParam);
3529
3530         case TVM_INSERTITEMW:
3531                 return TREEVIEW_InsertItemW(hwnd,wParam,lParam);;
3532
3533         case TVM_DELETEITEM:
3534                 return TREEVIEW_DeleteItem (hwnd, wParam, lParam);
3535
3536         case TVM_EXPAND:
3537                 return TREEVIEW_Expand (hwnd, wParam, lParam);
3538
3539         case TVM_GETITEMRECT:
3540                 return TREEVIEW_GetItemRect (hwnd, wParam, lParam);
3541
3542         case TVM_GETCOUNT:
3543                 return TREEVIEW_GetCount (hwnd, wParam, lParam);
3544
3545         case TVM_GETINDENT:
3546                 return TREEVIEW_GetIndent (hwnd);
3547
3548         case TVM_SETINDENT:
3549                 return TREEVIEW_SetIndent (hwnd, wParam);
3550
3551         case TVM_GETIMAGELIST:
3552                 return TREEVIEW_GetImageList (hwnd, wParam, lParam);
3553
3554                 case TVM_SETIMAGELIST:
3555                 return TREEVIEW_SetImageList (hwnd, wParam, lParam);
3556
3557         case TVM_GETNEXTITEM:
3558                 return TREEVIEW_GetNextItem (hwnd, wParam, lParam);
3559
3560         case TVM_SELECTITEM:
3561                 return TREEVIEW_SelectItem (hwnd, wParam, lParam);
3562
3563         case TVM_GETITEMA:
3564                 return TREEVIEW_GetItemA (hwnd, wParam, lParam);
3565
3566         case TVM_GETITEMW:
3567                 FIXME("Unimplemented msg TVM_GETITEMW\n");
3568                 return 0;
3569
3570         case TVM_SETITEMA:
3571                 return TREEVIEW_SetItemA (hwnd, wParam, lParam);
3572
3573         case TVM_SETITEMW:
3574                 FIXME("Unimplemented msg TVM_SETITEMW\n");
3575                 return 0;
3576
3577         case TVM_EDITLABELA:
3578                 FIXME("Unimplemented msg TVM_EDITLABELA \n");
3579                 return 0;
3580
3581         case TVM_EDITLABELW:
3582                 FIXME("Unimplemented msg TVM_EDITLABELW \n");
3583                 return 0;
3584
3585         case TVM_GETEDITCONTROL:
3586                 return TREEVIEW_GetEditControl (hwnd);
3587
3588         case TVM_GETVISIBLECOUNT:
3589                 return TREEVIEW_GetVisibleCount (hwnd, wParam, lParam);
3590
3591         case TVM_HITTEST:
3592                 return TREEVIEW_HitTest (hwnd, lParam);
3593
3594         case TVM_CREATEDRAGIMAGE:
3595                 return TREEVIEW_CreateDragImage (hwnd, wParam, lParam);
3596   
3597         case TVM_SORTCHILDREN:
3598                 return TREEVIEW_SortChildren (hwnd, wParam, lParam);
3599   
3600         case TVM_ENSUREVISIBLE:
3601                 FIXME("Unimplemented msg TVM_ENSUREVISIBLE\n");
3602                 return 0;
3603   
3604         case TVM_SORTCHILDRENCB:
3605                 return TREEVIEW_SortChildrenCB(hwnd, wParam, lParam);
3606   
3607         case TVM_ENDEDITLABELNOW:
3608                 return TREEVIEW_EndEditLabelNow (hwnd, wParam, lParam);
3609   
3610         case TVM_GETISEARCHSTRINGA:
3611                 FIXME("Unimplemented msg TVM_GETISEARCHSTRINGA\n");
3612                 return 0;
3613   
3614         case TVM_GETISEARCHSTRINGW:
3615                 FIXME("Unimplemented msg TVM_GETISEARCHSTRINGW\n");
3616                 return 0;
3617   
3618         case TVM_GETTOOLTIPS:
3619                 return TREEVIEW_GetToolTips (hwnd);
3620
3621         case TVM_SETTOOLTIPS:
3622                 return TREEVIEW_SetToolTips (hwnd, wParam);
3623   
3624         case TVM_SETINSERTMARK:
3625                 FIXME("Unimplemented msg TVM_SETINSERTMARK\n");
3626                 return 0;
3627   
3628         case TVM_SETITEMHEIGHT:
3629                 return TREEVIEW_SetItemHeight (hwnd, wParam);
3630   
3631         case TVM_GETITEMHEIGHT:
3632                 return TREEVIEW_GetItemHeight (hwnd);
3633   
3634         case TVM_SETBKCOLOR:
3635                 return TREEVIEW_SetBkColor (hwnd, wParam, lParam);
3636         
3637         case TVM_SETTEXTCOLOR:
3638                 return TREEVIEW_SetTextColor (hwnd, wParam, lParam);
3639   
3640         case TVM_GETBKCOLOR:
3641                 return TREEVIEW_GetBkColor (hwnd);
3642   
3643         case TVM_GETTEXTCOLOR:
3644                 return TREEVIEW_GetTextColor (hwnd);
3645   
3646         case TVM_SETSCROLLTIME:
3647                 return TREEVIEW_SetScrollTime (hwnd, (UINT)wParam);
3648   
3649         case TVM_GETSCROLLTIME:
3650                 return TREEVIEW_GetScrollTime (hwnd);
3651
3652         case TVM_GETITEMSTATE:
3653                 return TREEVIEW_GetItemState (hwnd,wParam, lParam);
3654
3655         case TVM_GETLINECOLOR:
3656                 return TREEVIEW_GetLineColor (hwnd,wParam, lParam);
3657
3658         case TVM_SETLINECOLOR:
3659                 return TREEVIEW_SetLineColor (hwnd,wParam, lParam);
3660   
3661         case TVM_SETINSERTMARKCOLOR:
3662                 FIXME("Unimplemented msg TVM_SETINSERTMARKCOLOR\n");
3663                 return 0;
3664   
3665         case TVM_SETUNICODEFORMAT:
3666                 FIXME("Unimplemented msg TVM_SETUNICODEFORMAT\n");
3667                 return 0;
3668   
3669         case TVM_GETUNICODEFORMAT:
3670                 FIXME("Unimplemented msg TVM_GETUNICODEFORMAT\n");
3671                 return 0;
3672   
3673                 case WM_COMMAND: 
3674                          return TREEVIEW_Command (hwnd, wParam, lParam);
3675   
3676                 case WM_CREATE:
3677                         return TREEVIEW_Create (hwnd, wParam, lParam);
3678   
3679                 case WM_DESTROY:
3680                         return TREEVIEW_Destroy (hwnd);
3681   
3682 /*              case WM_ENABLE: */
3683   
3684                 case WM_ERASEBKGND:
3685                         return TREEVIEW_EraseBackground (hwnd, wParam, lParam);
3686   
3687                 case WM_GETDLGCODE:
3688                 return DLGC_WANTARROWS | DLGC_WANTCHARS;
3689   
3690                 case WM_PAINT:
3691                 return TREEVIEW_Paint (hwnd, wParam, lParam);
3692   
3693                 case WM_GETFONT:
3694                 return TREEVIEW_GetFont (hwnd, wParam, lParam);
3695
3696                 case WM_SETFONT:
3697                 return TREEVIEW_SetFont (hwnd, wParam, lParam);
3698   
3699                 case WM_KEYDOWN:
3700                         return TREEVIEW_KeyDown (hwnd, wParam, lParam);
3701   
3702                 case WM_SETFOCUS: 
3703                         return TREEVIEW_SetFocus (hwnd, wParam, lParam);
3704
3705                 case WM_KILLFOCUS: 
3706                         return TREEVIEW_KillFocus (hwnd, wParam, lParam);
3707   
3708                 case WM_LBUTTONDOWN:
3709                         return TREEVIEW_LButtonDown (hwnd, wParam, lParam);
3710
3711                 case WM_LBUTTONUP:
3712                         return TREEVIEW_LButtonUp (hwnd, wParam, lParam);
3713   
3714                 case WM_LBUTTONDBLCLK:
3715                         return TREEVIEW_LButtonDoubleClick (hwnd, wParam, lParam);
3716   
3717                 case WM_RBUTTONDOWN:
3718                         return TREEVIEW_RButtonDown (hwnd, wParam, lParam);
3719
3720                 case WM_RBUTTONUP:
3721                         return TREEVIEW_RButtonUp (hwnd, wParam, lParam);
3722
3723                 case WM_MOUSEMOVE:
3724                         return TREEVIEW_MouseMove (hwnd, wParam, lParam);
3725   
3726                 case WM_STYLECHANGED: 
3727                         return TREEVIEW_StyleChanged (hwnd, wParam, lParam);
3728
3729 /*              case WM_SYSCOLORCHANGE: */
3730 /*              case WM_SETREDRAW: */
3731   
3732                 case WM_TIMER:
3733                         return TREEVIEW_HandleTimer (hwnd, wParam, lParam);
3734  
3735                 case WM_SIZE: 
3736                         return TREEVIEW_Size (hwnd, wParam,lParam);
3737
3738                 case WM_HSCROLL: 
3739                         return TREEVIEW_HScroll (hwnd, wParam, lParam);
3740                 case WM_VSCROLL: 
3741                         return TREEVIEW_VScroll (hwnd, wParam, lParam);
3742   
3743                 case WM_DRAWITEM:
3744                         TRACE ("drawItem\n");
3745                         return DefWindowProcA (hwnd, uMsg, wParam, lParam);
3746   
3747                 default:
3748                 if (uMsg >= WM_USER)
3749                 FIXME("Unknown msg %04x wp=%08x lp=%08lx\n",
3750                      uMsg, wParam, lParam);
3751             return DefWindowProcA (hwnd, uMsg, wParam, lParam);
3752       }
3753     return 0;
3754 }
3755
3756
3757 VOID
3758 TREEVIEW_Register (void)
3759 {
3760     WNDCLASSA wndClass;
3761
3762     TRACE("\n");
3763
3764     if (GlobalFindAtomA (WC_TREEVIEWA)) return;
3765
3766     ZeroMemory (&wndClass, sizeof(WNDCLASSA));
3767     wndClass.style         = CS_GLOBALCLASS | CS_DBLCLKS;
3768     wndClass.lpfnWndProc   = (WNDPROC)TREEVIEW_WindowProc;
3769     wndClass.cbClsExtra    = 0;
3770     wndClass.cbWndExtra    = sizeof(TREEVIEW_INFO *);
3771     wndClass.hCursor       = LoadCursorA (0, IDC_ARROWA);
3772     wndClass.hbrBackground = 0;
3773     wndClass.lpszClassName = WC_TREEVIEWA;
3774  
3775     RegisterClassA (&wndClass);
3776 }
3777
3778
3779 VOID
3780 TREEVIEW_Unregister (void)
3781 {
3782     if (GlobalFindAtomA (WC_TREEVIEWA))
3783         UnregisterClassA (WC_TREEVIEWA, (HINSTANCE)NULL);
3784 }
3785
3786
3787