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