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