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