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