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