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