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