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