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