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