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