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