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