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