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