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