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