X11DRV_SetFocus: really don't mess with focus for managed windows.
[wine/multimedia.git] / dlls / comctl32 / treeview.c
blob6d3ee44af04810bf7cdb6d19cb1922a258532f41
1 /* Treeview control
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
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.
32 * Separate drawing/calculation.
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.
42 #include <string.h>
43 #include "winbase.h"
44 #include "wingdi.h"
45 #include "commctrl.h"
46 #include "treeview.h"
47 #include "comctl32.h"
48 #include "debugtools.h"
50 DEFAULT_DEBUG_CHANNEL(treeview)
52 /* ffs should be in <string.h>. */
54 /* Defines, since they do not need to return previous state, and nr
55 * has no side effects in this file.
57 #define tv_test_bit(nr,bf) (((LPBYTE)bf)[nr>>3]&(1<<(nr&7)))
58 #define tv_set_bit(nr,bf) ((LPBYTE)bf)[nr>>3]|=(1<<(nr&7))
59 #define tv_clear_bit(nr,bf) ((LPBYTE)bf)[nr>>3]&=~(1<<(nr&7))
62 #define TREEVIEW_GetInfoPtr(hwnd) \
63 ((TREEVIEW_INFO *) GetWindowLongA( hwnd, 0))
65 static BOOL
66 TREEVIEW_SendSimpleNotify (HWND hwnd, UINT code);
67 static BOOL
68 TREEVIEW_SendTreeviewNotify (HWND hwnd, UINT code, UINT action,
69 HTREEITEM oldItem, HTREEITEM newItem);
70 static BOOL
71 TREEVIEW_SendTreeviewDnDNotify (HWND hwnd, UINT code, HTREEITEM dragItem,
72 POINT pt);
73 static BOOL
74 TREEVIEW_SendDispInfoNotify (HWND hwnd, TREEVIEW_ITEM *wineItem,
75 UINT code, UINT what);
76 static BOOL
77 TREEVIEW_SendCustomDrawNotify (HWND hwnd, DWORD dwDrawStage, HDC hdc,
78 RECT rc);
79 static BOOL
80 TREEVIEW_SendCustomDrawItemNotify (HWND hwnd, HDC hdc,
81 TREEVIEW_ITEM *tvItem, UINT uItemDrawState);
82 static LRESULT
83 TREEVIEW_DoSelectItem (HWND hwnd, INT action, HTREEITEM newSelect, INT cause);
84 static void
85 TREEVIEW_Refresh (HWND hwnd, HDC hdc);
87 static LRESULT CALLBACK
88 TREEVIEW_Edit_SubclassProc (HWND hwnd, UINT uMsg, WPARAM wParam,
89 LPARAM lParam);
91 LRESULT WINAPI
92 TREEVIEW_EndEditLabelNow (HWND hwnd, WPARAM wParam, LPARAM lParam);
97 /* helper functions. Work with the assumption that validity of operands
98 is checked beforehand, and that tree state is valid. */
100 /* FIXME: MS documentation says `GetNextVisibleItem' returns NULL
101 if not successfull. Probably only applies to dereferencing infoPtr
102 (i.e. we are offered a valid treeview structure)
103 and not whether there is a next `visible' child.
104 FIXME: check other failures.
107 /***************************************************************************
108 * This method returns the TREEVIEW_ITEM object given the handle
110 static TREEVIEW_ITEM* TREEVIEW_ValidItem(
111 TREEVIEW_INFO *infoPtr,
112 HTREEITEM handle)
114 if ((!handle) || (handle>infoPtr->uMaxHandle))
115 return NULL;
117 if (tv_test_bit ((INT)handle, infoPtr->freeList))
118 return NULL;
120 return &infoPtr->items[(INT)handle];
123 /***************************************************************************
124 * This method returns the last expanded child item of a tree node
126 static TREEVIEW_ITEM *TREEVIEW_GetLastListItem(
127 TREEVIEW_INFO *infoPtr,
128 TREEVIEW_ITEM *tvItem)
130 TREEVIEW_ITEM *wineItem = tvItem;
133 * Get this item last sibling
135 while (wineItem->sibling)
136 wineItem=& infoPtr->items [(INT)wineItem->sibling];
139 * If the last sibling has expanded children, restart.
141 if ( ( wineItem->cChildren > 0 ) && ( wineItem->state & TVIS_EXPANDED) )
142 return TREEVIEW_GetLastListItem(
143 infoPtr,
144 &(infoPtr->items[(INT)wineItem->firstChild]));
146 return wineItem;
149 /***************************************************************************
150 * This method returns the previous physical item in the list not
151 * considering the tree hierarchy.
153 static TREEVIEW_ITEM *TREEVIEW_GetPrevListItem(
154 TREEVIEW_INFO *infoPtr,
155 TREEVIEW_ITEM *tvItem)
157 if (tvItem->upsibling)
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.
163 TREEVIEW_ITEM *upItem = &infoPtr->items[(INT)tvItem->upsibling];
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;
172 else
175 * this item does not have a upsibling, get the parent
177 if (tvItem->parent)
178 return &infoPtr->items[(INT)tvItem->parent];
181 return NULL;
185 /***************************************************************************
186 * This method returns the next physical item in the treeview not
187 * considering the tree hierarchy.
189 static TREEVIEW_ITEM *TREEVIEW_GetNextListItem(
190 TREEVIEW_INFO *infoPtr,
191 TREEVIEW_ITEM *tvItem)
193 TREEVIEW_ITEM *wineItem = NULL;
196 * If this item has children and is expanded, return the first child
198 if ((tvItem->firstChild) && (tvItem->state & TVIS_EXPANDED))
199 return (& infoPtr->items[(INT)tvItem->firstChild]);
203 * try to get the sibling
205 if (tvItem->sibling)
206 return (& infoPtr->items[(INT)tvItem->sibling]);
209 * Otherwise, get the parent's sibling.
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]);
218 return NULL;
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.
225 * Will scroll backward if count is <0.
226 * forward if count is >0.
228 static TREEVIEW_ITEM *TREEVIEW_GetListItem(
229 TREEVIEW_INFO *infoPtr,
230 TREEVIEW_ITEM *tvItem,
231 LONG count)
233 TREEVIEW_ITEM *previousItem = NULL;
234 TREEVIEW_ITEM *wineItem = tvItem;
235 LONG iter = 0;
237 if (count > 0)
239 /* Find count item downward */
240 while ((iter++ < count) && (wineItem != NULL))
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);
247 if (wineItem == NULL)
248 wineItem = previousItem;
250 else if (count < 0)
252 /* Find count item upward */
253 while ((iter-- > count) && (wineItem != NULL))
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);
260 if (wineItem == NULL)
261 wineItem = previousItem;
263 else
264 wineItem = NULL;
266 return wineItem;
270 /***************************************************************************
271 * This method
273 static void TREEVIEW_RemoveAllChildren(
274 HWND hwnd,
275 TREEVIEW_ITEM *parentItem)
277 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
278 TREEVIEW_ITEM *killItem;
279 INT kill;
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_DELETEITEMA, 0, (HTREEITEM)kill, 0);
288 if (killItem->firstChild)
289 TREEVIEW_RemoveAllChildren (hwnd, killItem);
290 kill=(INT)killItem->sibling;
293 if (parentItem->cChildren>0) {
294 infoPtr->uNumItems -= parentItem->cChildren;
295 parentItem->firstChild = 0;
296 parentItem->cChildren = 0;
302 static void
303 TREEVIEW_RemoveItem (HWND hwnd, TREEVIEW_ITEM *wineItem)
306 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
307 TREEVIEW_ITEM *parentItem, *upsiblingItem, *siblingItem;
308 INT iItem;
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);
317 TREEVIEW_SendTreeviewNotify (hwnd, TVN_DELETEITEMA, 0, (HTREEITEM)iItem, 0);
319 if (wineItem->firstChild)
320 TREEVIEW_RemoveAllChildren (hwnd,wineItem);
322 if (wineItem->parent) {
323 parentItem=& infoPtr->items [(INT)wineItem->parent];
324 switch (parentItem->cChildren) {
325 case I_CHILDRENCALLBACK:
326 FIXME("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;
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;
345 if (wineItem->sibling) {
346 siblingItem=& infoPtr->items [(INT)wineItem->sibling];
347 siblingItem->upsibling=wineItem->upsibling;
355 /* Note:TREEVIEW_RemoveTree doesn't remove infoPtr itself */
357 static void TREEVIEW_RemoveTree (HWND hwnd)
360 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
361 TREEVIEW_ITEM *killItem;
362 int i;
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(hwnd, TVN_DELETEITEMA, 0,
370 killItem->hItem, 0);
372 if (infoPtr->uNumPtrsAlloced) {
373 COMCTL32_Free (infoPtr->items);
374 COMCTL32_Free (infoPtr->freeList);
375 infoPtr->uNumItems = 0;
376 infoPtr->uNumPtrsAlloced = 0;
377 infoPtr->uMaxHandle = 0;
378 infoPtr->TopRootItem = 0;
388 static LRESULT
389 TREEVIEW_GetImageList (HWND hwnd, WPARAM wParam, LPARAM lParam)
391 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
393 TRACE("\n");
395 if ((INT)wParam == TVSIL_NORMAL)
396 return (LRESULT) infoPtr->himlNormal;
397 if ((INT)wParam == TVSIL_STATE)
398 return (LRESULT) infoPtr->himlState;
400 return 0;
403 static LRESULT
404 TREEVIEW_SetImageList (HWND hwnd, WPARAM wParam, LPARAM lParam)
406 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
407 HIMAGELIST himlTemp;
409 TRACE("%x,%lx\n", wParam, lParam);
410 switch ((INT)wParam) {
411 case TVSIL_NORMAL:
412 himlTemp = infoPtr->himlNormal;
413 infoPtr->himlNormal = (HIMAGELIST)lParam;
414 return (LRESULT)himlTemp;
416 case TVSIL_STATE:
417 himlTemp = infoPtr->himlState;
418 infoPtr->himlState = (HIMAGELIST)lParam;
419 return (LRESULT)himlTemp;
422 return (LRESULT)NULL;
427 static LRESULT
428 TREEVIEW_SetItemHeight (HWND hwnd, WPARAM wParam)
430 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
431 INT cx,cy,prevHeight=infoPtr->uItemHeight;
433 TRACE("\n");
434 if (wParam==-1) {
435 infoPtr->uItemHeight=-1;
436 return prevHeight;
439 ImageList_GetIconSize (infoPtr->himlNormal, &cx, &cy);
441 if (wParam>cy) cy=wParam;
442 infoPtr->uItemHeight=cy;
444 if (!( GetWindowLongA( hwnd, GWL_STYLE) & TVS_NONEVENHEIGHT))
445 infoPtr->uItemHeight = (INT) wParam & 0xfffffffe;
446 return prevHeight;
449 static LRESULT
450 TREEVIEW_GetItemHeight (HWND hwnd)
452 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
454 TRACE("\n");
455 return infoPtr->uItemHeight;
458 static LRESULT
459 TREEVIEW_GetLineColor (HWND hwnd, WPARAM wParam, LPARAM lParam)
461 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
463 TRACE("\n");
464 return (LRESULT) infoPtr->clrLine;
467 static LRESULT
468 TREEVIEW_SetLineColor (HWND hwnd, WPARAM wParam, LPARAM lParam)
470 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
471 COLORREF prevColor=infoPtr->clrLine;
473 TRACE("\n");
474 infoPtr->clrLine=(COLORREF) lParam;
475 return (LRESULT) prevColor;
478 static LRESULT
479 TREEVIEW_GetInsertMarkColor (HWND hwnd, WPARAM wParam, LPARAM lParam)
481 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
483 TRACE("\n");
484 return (LRESULT) infoPtr->clrInsertMark;
487 static LRESULT
488 TREEVIEW_SetInsertMarkColor (HWND hwnd, WPARAM wParam, LPARAM lParam)
490 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
491 COLORREF prevColor=infoPtr->clrInsertMark;
493 TRACE("%d %ld\n",wParam,lParam);
494 infoPtr->clrInsertMark=(COLORREF) lParam;
495 return (LRESULT) prevColor;
498 static LRESULT
499 TREEVIEW_SetInsertMark (HWND hwnd, WPARAM wParam, LPARAM lParam)
501 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
502 HDC hdc;
504 FIXME("%d %ld\n",wParam,lParam);
505 if (!TREEVIEW_ValidItem (infoPtr, (HTREEITEM)lParam)) return 0;
506 FIXME("%d %ld\n",wParam,lParam);
508 infoPtr->insertBeforeorAfter=(BOOL) wParam;
509 infoPtr->insertMarkItem=(HTREEITEM) lParam;
511 hdc = GetDC (hwnd);
512 TREEVIEW_Refresh (hwnd, hdc);
513 ReleaseDC(hwnd,hdc);
515 return 1;
518 static LRESULT
519 TREEVIEW_SetTextColor (HWND hwnd, WPARAM wParam, LPARAM lParam)
521 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
522 COLORREF prevColor=infoPtr->clrText;
524 TRACE("\n");
525 infoPtr->clrText=(COLORREF) lParam;
526 return (LRESULT) prevColor;
529 static LRESULT
530 TREEVIEW_GetBkColor (HWND hwnd)
532 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
534 TRACE("\n");
535 return (LRESULT) infoPtr->clrBk;
538 static LRESULT
539 TREEVIEW_SetBkColor (HWND hwnd, WPARAM wParam, LPARAM lParam)
541 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
542 COLORREF prevColor=infoPtr->clrBk;
544 TRACE("\n");
545 infoPtr->clrBk=(COLORREF) lParam;
546 return (LRESULT) prevColor;
549 static LRESULT
550 TREEVIEW_GetTextColor (HWND hwnd)
552 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
554 TRACE("\n");
555 return (LRESULT) infoPtr->clrText;
559 /* cdmode: custom draw mode as received from app. in first NMCUSTOMDRAW
560 notification */
562 #define TREEVIEW_LEFT_MARGIN 8
565 static void
566 TREEVIEW_DrawItem (HWND hwnd, HDC hdc, TREEVIEW_ITEM *wineItem)
568 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
569 DWORD dwStyle = GetWindowLongA (hwnd, GWL_STYLE);
570 INT center,xpos,cx,cy, cditem;
571 HFONT hOldFont;
572 UINT uTextJustify = DT_LEFT;
573 RECT r;
576 if (wineItem->state & TVIS_BOLD)
577 hOldFont = SelectObject (hdc, infoPtr->hBoldFont);
578 else
579 hOldFont = SelectObject (hdc, infoPtr->hFont);
581 cditem=0;
582 TRACE ("cdmode:%x\n",infoPtr->cdmode);
583 if (infoPtr->cdmode & CDRF_NOTIFYITEMDRAW) {
584 cditem=TREEVIEW_SendCustomDrawItemNotify
585 (hwnd, hdc, wineItem, CDDS_ITEMPREPAINT);
586 TRACE("prepaint:cditem-app returns 0x%x\n",cditem);
588 if (cditem & CDRF_SKIPDEFAULT)
589 return;
593 * Set drawing starting points
595 r = wineItem->rect; /* this item rectangle */
596 center = (r.top+r.bottom)/2; /* this item vertical center */
597 xpos = r.left + TREEVIEW_LEFT_MARGIN;/* horizontal starting point */
600 * Display the tree hierarchy
602 if ( dwStyle & TVS_HASLINES)
605 * Write links to parent node
606 * we draw the L starting from the child to the parent
608 * points[0] is attached to the current item
609 * points[1] is the L corner
610 * points[2] is attached to the parent or the up sibling
612 if ( dwStyle & TVS_LINESATROOT)
614 TREEVIEW_ITEM *upNode = NULL;
615 BOOL hasParentOrSibling = TRUE;
616 RECT upRect = {0,0,0,0};
617 HPEN hOldPen, hNewPen;
618 POINT points[3];
620 * determine the target location of the line at root, either be linked
621 * to the up sibling or to the parent node.
623 if (wineItem->upsibling)
624 upNode = TREEVIEW_ValidItem (infoPtr, wineItem->upsibling);
625 else if (wineItem->parent)
626 upNode = TREEVIEW_ValidItem (infoPtr, wineItem->parent);
627 else
628 hasParentOrSibling = FALSE;
630 if (upNode)
631 upRect = upNode->rect;
633 if ( wineItem->iLevel == 0 )
635 points[2].x = points[1].x = upRect.left+8;
636 points[0].x = points[2].x + 10;
637 points[2].y = upRect.bottom-3;
638 points[1].y = points[0].y = center;
640 else
642 points[2].x = points[1].x = 8 + (20*wineItem->iLevel);
643 points[2].y = ( upNode->cChildren == 0) ?
644 upRect.top : /* is linked to the "L" above */
645 ( wineItem->upsibling != NULL) ?
646 upRect.bottom-3: /* is linked to an icon */
647 upRect.bottom+1; /* is linked to a +/- box */
648 points[1].y = points[0].y = center;
649 points[0].x = points[1].x + 10;
653 * Get a dotted pen
655 hNewPen = CreatePen(PS_DOT, 0, infoPtr->clrLine);
656 hOldPen = SelectObject( hdc, hNewPen );
658 if (hasParentOrSibling)
659 Polyline (hdc,points,3);
660 else
661 Polyline (hdc,points,2);
663 DeleteObject(hNewPen);
664 SelectObject(hdc, hOldPen);
669 * Display the (+/-) signs
671 if (wineItem->iLevel != 0)/* update position only for non root node */
672 xpos+=(5*wineItem->iLevel);
674 if (( dwStyle & TVS_HASBUTTONS) && ( dwStyle & TVS_HASLINES))
676 if ( (wineItem->cChildren) ||
677 (wineItem->cChildren == I_CHILDRENCALLBACK))
679 /* Setup expand box coordinate to facilitate the LMBClick handling */
680 wineItem->expandBox.left = xpos-4;
681 wineItem->expandBox.top = center-4;
682 wineItem->expandBox.right = xpos+5;
683 wineItem->expandBox.bottom = center+5;
685 Rectangle (
686 hdc,
687 wineItem->expandBox.left,
688 wineItem->expandBox.top ,
689 wineItem->expandBox.right,
690 wineItem->expandBox.bottom);
692 MoveToEx (hdc, xpos-2, center, NULL);
693 LineTo (hdc, xpos+3, center);
695 if (!(wineItem->state & TVIS_EXPANDED)) {
696 MoveToEx (hdc, xpos, center-2, NULL);
697 LineTo (hdc, xpos, center+3);
703 * Display the image associated with this item
705 xpos += 13; /* update position */
706 if (wineItem->mask & (TVIF_IMAGE|TVIF_SELECTEDIMAGE)) {
707 INT imageIndex;
708 HIMAGELIST *himlp = NULL;
710 /* State images are displayed to the left of the Normal image
711 * image number is in state; zero should be `display no image'.
712 * FIXME: that last sentence looks like it needs some checking.
714 if (infoPtr->himlState)
715 himlp=&infoPtr->himlState;
716 imageIndex=wineItem->state>>12;
717 imageIndex++; /* yeah, right */
718 TRACE ("imindex:%d\n",imageIndex);
719 if ((himlp) && (imageIndex))
721 imageIndex--; /* see FIXME */
722 ImageList_Draw ( *himlp, imageIndex, hdc, xpos-2, r.top+1, ILD_NORMAL);
723 ImageList_GetIconSize (*himlp, &cx, &cy);
724 wineItem->statebitmap.left=xpos-2;
725 wineItem->statebitmap.right=xpos-2+cx;
726 wineItem->statebitmap.top=r.top+1;
727 wineItem->statebitmap.bottom=r.top+1+cy;
728 xpos+=cx;
731 /* Now, draw the normal image; can be either selected or
732 * non-selected image.
735 himlp=NULL;
736 if (infoPtr->himlNormal)
737 himlp=&infoPtr->himlNormal; /* get the image list */
739 imageIndex = wineItem->iImage;
740 if ( (wineItem->state & TVIS_SELECTED) &&
741 (wineItem->iSelectedImage)) {
743 /* The item is curently selected */
744 if (wineItem->iSelectedImage == I_IMAGECALLBACK)
745 TREEVIEW_SendDispInfoNotify
746 (hwnd, wineItem, TVN_GETDISPINFOA, TVIF_SELECTEDIMAGE);
748 imageIndex = wineItem->iSelectedImage;
749 } else {
750 /* The item is not selected */
751 if (wineItem->iImage == I_IMAGECALLBACK)
752 TREEVIEW_SendDispInfoNotify
753 (hwnd, wineItem, TVN_GETDISPINFOA, TVIF_IMAGE);
755 imageIndex = wineItem->iImage;
758 if (himlp)
760 int ovlIdx = 0;
762 if(wineItem->stateMask & TVIS_OVERLAYMASK)
763 ovlIdx = wineItem->state & TVIS_OVERLAYMASK;
765 ImageList_Draw ( *himlp, imageIndex, hdc, xpos-2, r.top+1, ILD_NORMAL|ovlIdx);
766 ImageList_GetIconSize (*himlp, &cx, &cy);
767 wineItem->bitmap.left=xpos-2;
768 wineItem->bitmap.right=xpos-2+cx;
769 wineItem->bitmap.top=r.top+1;
770 wineItem->bitmap.bottom=r.top+1+cy;
771 xpos+=cx;
777 * Display the text associated with this item
779 r.left=xpos;
780 if ((wineItem->mask & TVIF_TEXT) && (wineItem->pszText))
782 COLORREF oldBkColor = 0;
783 COLORREF oldTextColor = 0;
784 INT oldBkMode;
786 r.left += 3;
787 r.right -= 3;
789 wineItem->text.left = r.left;
790 wineItem->text.right = r.right;
791 wineItem->text.top = r.top;
792 wineItem->text.bottom= r.bottom;
794 if (wineItem->pszText== LPSTR_TEXTCALLBACKA) {
795 TRACE("LPSTR_TEXTCALLBACK\n");
796 TREEVIEW_SendDispInfoNotify (hwnd, wineItem, TVN_GETDISPINFOA, TVIF_TEXT);
799 /* Yep, there are some things that need to be straightened out here.
800 Removing the comments around the setTextColor does not give the right
801 results. Dito FillRect.
805 /* GetTextExtentPoint32A (hdc, wineItem->pszText,
806 strlen (wineItem->pszText), &size); */
808 /* FillRect ( hdc, &wineItem->text, GetSysColorBrush (infoPtr->clrBk));
812 if (!(cditem & CDRF_NOTIFYPOSTPAINT) &&
813 (wineItem->state & (TVIS_SELECTED | TVIS_DROPHILITED)) ) {
814 oldBkMode = SetBkMode (hdc, OPAQUE);
815 oldBkColor = SetBkColor (hdc, GetSysColor( COLOR_HIGHLIGHT));
816 oldTextColor = SetTextColor(hdc, GetSysColor( COLOR_HIGHLIGHTTEXT));
817 } else {
818 oldBkMode = SetBkMode (hdc, TRANSPARENT);
819 oldBkColor = SetBkColor (hdc, infoPtr->clrBk);
820 /* oldTextColor = SetTextColor(hdc, infoPtr->clrText); */
825 /* Draw it */
826 DrawTextA ( hdc,
827 wineItem->pszText,
828 lstrlenA(wineItem->pszText),
829 &wineItem->text,
830 uTextJustify | DT_VCENTER | DT_SINGLELINE );
832 /* Obtain the text coordinate */
833 DrawTextA (
834 hdc,
835 wineItem->pszText,
836 lstrlenA(wineItem->pszText),
837 &wineItem->text,
838 uTextJustify | DT_VCENTER | DT_SINGLELINE | DT_CALCRECT);
840 /* Restore the hdc state */
841 SetTextColor( hdc, oldTextColor);
843 if (oldBkMode != TRANSPARENT)
844 SetBkMode(hdc, oldBkMode);
845 if (wineItem->state & (TVIS_SELECTED | TVIS_DROPHILITED))
846 SetBkColor (hdc, oldBkColor);
848 /* Draw the box arround the selected item */
849 if (wineItem->state & TVIS_SELECTED )
851 HPEN hNewPen = CreatePen(PS_DOT, 0, GetSysColor(COLOR_WINDOWTEXT) );
852 HPEN hOldPen = SelectObject( hdc, hNewPen );
853 POINT points[4];
855 points[0].x = wineItem->text.left-1;
856 points[0].y = wineItem->text.top+1;
857 points[1].x = wineItem->text.right;
858 points[1].y = wineItem->text.top+1;
859 points[2].x = wineItem->text.right;
860 points[2].y = wineItem->text.bottom;
861 points[3].x = wineItem->text.left-1;
862 points[3].y = wineItem->text.bottom;
864 Polyline (hdc,points,4);
866 DeleteObject(hNewPen);
867 SelectObject(hdc, hOldPen);
871 /* Draw insertion mark if necessary */
873 if (infoPtr->insertMarkItem)
874 TRACE ("item:%d,mark:%d\n", (int)wineItem->hItem,
875 (int) infoPtr->insertMarkItem);
876 if (wineItem->hItem==infoPtr->insertMarkItem) {
877 HPEN hNewPen, hOldPen;
878 int offset;
880 hNewPen = CreatePen(PS_SOLID, 2, infoPtr->clrInsertMark);
881 hOldPen = SelectObject( hdc, hNewPen );
883 if (infoPtr->insertBeforeorAfter)
884 offset=wineItem->text.top+1;
885 else
886 offset=wineItem->text.bottom-1;
888 MoveToEx (hdc, wineItem->text.left, offset-3, NULL);
889 LineTo (hdc, wineItem->text.left, offset+3);
891 MoveToEx (hdc, wineItem->text.left, offset, NULL);
892 LineTo (hdc, r.right-2, offset);
894 MoveToEx (hdc, r.right-2, offset+3, NULL);
895 LineTo (hdc, r.right-2, offset-3);
897 DeleteObject(hNewPen);
899 SelectObject(hdc, hOldPen);
902 if (cditem & CDRF_NOTIFYPOSTPAINT) {
903 cditem=TREEVIEW_SendCustomDrawItemNotify
904 (hwnd, hdc, wineItem, CDDS_ITEMPOSTPAINT);
905 TRACE("postpaint:cditem-app returns 0x%x\n",cditem);
908 SelectObject (hdc, hOldFont);
911 static LRESULT
912 TREEVIEW_GetItemRect (HWND hwnd, WPARAM wParam, LPARAM lParam)
914 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
915 TREEVIEW_ITEM *wineItem;
916 HTREEITEM *iItem;
917 LPRECT lpRect = (LPRECT)lParam;
918 HDC hdc;
920 TRACE("\n");
922 * validate parameters
924 if (lpRect == NULL)
925 return FALSE;
927 if (infoPtr->Timer & TV_REFRESH_TIMER_SET) {
928 hdc = GetDC (hwnd);
929 TREEVIEW_Refresh (hwnd, hdc); /* we want a rect for the current view */
930 ReleaseDC(hwnd,hdc);
935 * retrieve the item ptr
937 iItem = (HTREEITEM *) lParam;
938 wineItem = TREEVIEW_ValidItem (infoPtr, *iItem);
939 if ((!wineItem) || (!wineItem->visible))
940 return FALSE;
943 * If wParam is TRUE return the text size otherwise return
944 * the whole item size
946 if ((INT) wParam) {
947 lpRect->left = wineItem->text.left;
948 lpRect->right = wineItem->text.right;
949 lpRect->bottom = wineItem->text.bottom;
950 lpRect->top = wineItem->text.top;
951 } else {
952 lpRect->left = wineItem->rect.left;
953 lpRect->right = wineItem->rect.right;
954 lpRect->bottom = wineItem->rect.bottom;
955 lpRect->top = wineItem->rect.top;
958 TRACE("[L:%d R:%d T:%d B:%d]\n",
959 lpRect->left,lpRect->right,
960 lpRect->top,lpRect->bottom);
962 return TRUE;
965 static LRESULT
966 TREEVIEW_GetVisibleCount (HWND hwnd, WPARAM wParam, LPARAM lParam)
969 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
971 return (LRESULT) infoPtr->uVisibleHeight / infoPtr->uRealItemHeight;
976 static LRESULT
977 TREEVIEW_SetItemA (HWND hwnd, WPARAM wParam, LPARAM lParam)
979 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
980 TREEVIEW_ITEM *wineItem;
981 TVITEMEXA *tvItem;
982 INT iItem,len;
984 tvItem=(LPTVITEMEXA) lParam;
985 iItem=(INT)tvItem->hItem;
986 TRACE("item %d,mask %x\n",iItem,tvItem->mask);
988 wineItem = TREEVIEW_ValidItem (infoPtr, (HTREEITEM)iItem);
989 if (!wineItem) return FALSE;
991 if (tvItem->mask & TVIF_CHILDREN) {
992 wineItem->cChildren=tvItem->cChildren;
995 if (tvItem->mask & TVIF_IMAGE) {
996 wineItem->iImage=tvItem->iImage;
999 if (tvItem->mask & TVIF_INTEGRAL) {
1000 wineItem->iIntegral=tvItem->iIntegral;
1003 if (tvItem->mask & TVIF_PARAM) {
1004 wineItem->lParam=tvItem->lParam;
1007 if (tvItem->mask & TVIF_SELECTEDIMAGE) {
1008 wineItem->iSelectedImage=tvItem->iSelectedImage;
1011 if (tvItem->mask & TVIF_STATE) {
1012 TRACE ("prevstate,state,mask:%x,%x,%x\n",wineItem->state,tvItem->state,
1013 tvItem->stateMask);
1014 wineItem->state&= ~tvItem->stateMask;
1015 wineItem->state|= (tvItem->state & tvItem->stateMask);
1016 wineItem->stateMask|= tvItem->stateMask;
1019 if (tvItem->mask & TVIF_TEXT) {
1020 if (tvItem->pszText!=LPSTR_TEXTCALLBACKA) {
1021 len=lstrlenA (tvItem->pszText);
1022 if (len>wineItem->cchTextMax)
1023 wineItem->pszText= COMCTL32_ReAlloc (wineItem->pszText, len+1);
1024 lstrcpynA (wineItem->pszText, tvItem->pszText,len+1);
1025 } else {
1026 if (wineItem->cchTextMax) {
1027 COMCTL32_Free (wineItem->pszText);
1028 wineItem->cchTextMax=0;
1030 wineItem->pszText=LPSTR_TEXTCALLBACKA;
1034 wineItem->mask |= tvItem->mask;
1036 return TRUE;
1039 static LRESULT
1040 TREEVIEW_GetItemState (HWND hwnd, WPARAM wParam, LPARAM lParam)
1043 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
1044 TREEVIEW_ITEM *wineItem;
1046 TRACE("\n");
1047 wineItem = TREEVIEW_ValidItem (infoPtr, (HTREEITEM)wParam);
1048 if (!wineItem) return 0;
1050 return (wineItem->state & lParam);
1056 static void
1057 TREEVIEW_Refresh (HWND hwnd, HDC hdc)
1059 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
1060 TEXTMETRICA tm;
1061 HBRUSH hbrBk;
1062 RECT rect;
1063 INT iItem, indent, x, y, height, itemHeight;
1064 INT viewtop,viewbottom,viewleft,viewright;
1065 TREEVIEW_ITEM *wineItem, *prevItem;
1067 TRACE("\n");
1070 if (infoPtr->Timer & TV_REFRESH_TIMER_SET) {
1071 KillTimer (hwnd, TV_REFRESH_TIMER);
1072 infoPtr->Timer &= ~TV_REFRESH_TIMER_SET;
1076 GetClientRect (hwnd, &rect);
1077 if ((rect.left-rect.right ==0) || (rect.top-rect.bottom==0)) return;
1079 hdc=GetDC (hwnd);
1081 infoPtr->cdmode=TREEVIEW_SendCustomDrawNotify(hwnd,CDDS_PREPAINT,hdc,rect);
1083 if (infoPtr->cdmode==CDRF_SKIPDEFAULT) return;
1085 infoPtr->uVisibleHeight= rect.bottom-rect.top;
1086 infoPtr->uVisibleWidth= rect.right-rect.left;
1088 viewtop=infoPtr->cy;
1089 viewbottom=infoPtr->cy + rect.bottom-rect.top;
1090 viewleft=infoPtr->cx;
1091 viewright=infoPtr->cx + rect.right-rect.left;
1093 TRACE("[%d %d %d %d]\n",viewtop,viewbottom,viewleft,viewright);
1095 /* draw background */
1097 hbrBk = CreateSolidBrush (infoPtr->clrBk);
1098 FillRect(hdc, &rect, hbrBk);
1099 DeleteObject(hbrBk);
1101 ImageList_GetIconSize (infoPtr->himlNormal, &x, &itemHeight);
1102 if (infoPtr->uItemHeight>itemHeight)
1103 itemHeight=infoPtr->uItemHeight;
1105 GetTextMetricsA (hdc, &tm);
1106 if ((tm.tmHeight + tm.tmExternalLeading) > itemHeight)
1107 itemHeight=tm.tmHeight + tm.tmExternalLeading;
1109 infoPtr->uRealItemHeight=itemHeight;
1111 iItem=(INT)infoPtr->TopRootItem;
1112 infoPtr->firstVisible=0;
1113 wineItem=NULL;
1114 indent=0;
1115 x=y=0;
1117 while (iItem) {
1118 prevItem=wineItem;
1119 wineItem= & infoPtr->items[iItem];
1120 wineItem->iLevel=indent;
1122 /* FIXME: remove this in later stage */
1124 if (wineItem->pszText!=LPSTR_TEXTCALLBACK32A)
1125 TRACE (treeview, "%d %d [%d %d %d %d] (%s)\n",y,x,
1126 wineItem->rect.top, wineItem->rect.bottom,
1127 wineItem->rect.left, wineItem->rect.right,
1128 wineItem->pszText);
1129 else
1130 TRACE (treeview, "%d [%d %d %d %d] (CALLBACK)\n",
1131 wineItem->hItem,
1132 wineItem->rect.top, wineItem->rect.bottom,
1133 wineItem->rect.left, wineItem->rect.right);
1136 height=itemHeight * wineItem->iIntegral +1;
1137 if ((y >= viewtop) && (y <= viewbottom) &&
1138 (x >= viewleft ) && (x <= viewright)) {
1139 wineItem->visible = TRUE;
1140 wineItem->rect.top = y - infoPtr->cy + rect.top;
1141 wineItem->rect.bottom = wineItem->rect.top + height ;
1142 wineItem->rect.left = x - infoPtr->cx + rect.left;
1143 wineItem->rect.right = rect.right;
1144 if (!infoPtr->firstVisible)
1145 infoPtr->firstVisible=wineItem->hItem;
1146 TREEVIEW_DrawItem (hwnd, hdc, wineItem);
1148 else {
1149 wineItem->visible = FALSE;
1150 wineItem->rect.left = wineItem->rect.top = 0;
1151 wineItem->rect.right= wineItem->rect.bottom = 0;
1152 wineItem->text.left = wineItem->text.top = 0;
1153 wineItem->text.right= wineItem->text.bottom = 0;
1156 /* look up next item */
1158 if ((wineItem->firstChild) && (wineItem->state & TVIS_EXPANDED)) {
1159 iItem=(INT)wineItem->firstChild;
1160 indent++;
1161 x+=infoPtr->uIndent;
1162 if (x>infoPtr->uTotalWidth)
1163 infoPtr->uTotalWidth=x;
1165 else {
1166 iItem=(INT)wineItem->sibling;
1167 while ((!iItem) && (indent>0)) {
1168 indent--;
1169 x-=infoPtr->uIndent;
1170 wineItem=&infoPtr->items[(INT)wineItem->parent];
1171 iItem=(INT)wineItem->sibling;
1174 y +=height;
1175 } /* while */
1177 /* FIXME: infoPtr->uTotalWidth should also take item label into account */
1178 /* FIXME: or should query item sizes (ie check CDRF_NEWFONT) */
1180 infoPtr->uTotalHeight=y;
1181 if (y >= (viewbottom-viewtop)) {
1182 if (!(infoPtr->uInternalStatus & TV_VSCROLL))
1183 ShowScrollBar (hwnd, SB_VERT, TRUE);
1184 infoPtr->uInternalStatus |=TV_VSCROLL;
1185 SetScrollRange (hwnd, SB_VERT, 0,
1186 y - infoPtr->uVisibleHeight, FALSE);
1187 SetScrollPos (hwnd, SB_VERT, infoPtr->cy, TRUE);
1189 else {
1190 if (infoPtr->uInternalStatus & TV_VSCROLL)
1191 ShowScrollBar (hwnd, SB_VERT, FALSE);
1192 infoPtr->uInternalStatus &= ~TV_VSCROLL;
1196 if (infoPtr->cdmode & CDRF_NOTIFYPOSTPAINT)
1197 infoPtr->cdmode=TREEVIEW_SendCustomDrawNotify
1198 (hwnd, CDDS_POSTPAINT, hdc, rect);
1200 TRACE("done\n");
1204 static LRESULT
1205 TREEVIEW_HandleTimer (HWND hwnd, WPARAM wParam, LPARAM lParam)
1207 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
1209 TRACE(" %d\n",wParam);
1211 switch (wParam) {
1212 case TV_REFRESH_TIMER:
1213 KillTimer (hwnd, TV_REFRESH_TIMER);
1214 infoPtr->Timer &= ~TV_REFRESH_TIMER_SET;
1215 InvalidateRect(hwnd, NULL, FALSE);
1216 return 0;
1217 case TV_EDIT_TIMER:
1218 KillTimer (hwnd, TV_EDIT_TIMER);
1219 infoPtr->Timer &= ~TV_EDIT_TIMER_SET;
1220 return 0;
1221 default:
1222 ERR("got unknown timer\n");
1225 return 1;
1229 static void
1230 TREEVIEW_QueueRefresh (HWND hwnd)
1233 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
1235 TRACE("\n");
1236 if (infoPtr->Timer & TV_REFRESH_TIMER_SET) {
1237 KillTimer (hwnd, TV_REFRESH_TIMER);
1240 SetTimer (hwnd, TV_REFRESH_TIMER, TV_REFRESH_DELAY, 0);
1241 infoPtr->Timer|=TV_REFRESH_TIMER_SET;
1246 static LRESULT
1247 TREEVIEW_GetItemA (HWND hwnd, WPARAM wParam, LPARAM lParam)
1249 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
1250 LPTVITEMEXA tvItem;
1251 TREEVIEW_ITEM *wineItem;
1252 INT iItem;
1254 tvItem=(LPTVITEMEXA) lParam;
1255 iItem=(INT)tvItem->hItem;
1257 wineItem = TREEVIEW_ValidItem (infoPtr, (HTREEITEM)iItem);
1258 if (!wineItem) return FALSE;
1260 if (tvItem->mask & TVIF_CHILDREN) {
1261 if (TVIF_CHILDREN==I_CHILDRENCALLBACK)
1262 FIXME("I_CHILDRENCALLBACK not supported\n");
1263 tvItem->cChildren=wineItem->cChildren;
1266 if (tvItem->mask & TVIF_HANDLE) {
1267 tvItem->hItem=wineItem->hItem;
1270 if (tvItem->mask & TVIF_IMAGE) {
1271 tvItem->iImage=wineItem->iImage;
1274 if (tvItem->mask & TVIF_INTEGRAL) {
1275 tvItem->iIntegral=wineItem->iIntegral;
1278 /* undocumented: windows ignores TVIF_PARAM and
1279 * always sets lParam
1281 tvItem->lParam=wineItem->lParam;
1283 if (tvItem->mask & TVIF_SELECTEDIMAGE) {
1284 tvItem->iSelectedImage=wineItem->iSelectedImage;
1287 if (tvItem->mask & TVIF_STATE) {
1288 tvItem->state=wineItem->state & tvItem->stateMask;
1291 if (tvItem->mask & TVIF_TEXT) {
1292 if (wineItem->pszText == LPSTR_TEXTCALLBACKA) {
1293 tvItem->pszText = LPSTR_TEXTCALLBACKA; /* FIXME:send notification? */
1294 ERR(" GetItem called with LPSTR_TEXTCALLBACK\n");
1296 else if (wineItem->pszText) {
1297 lstrcpynA (tvItem->pszText, wineItem->pszText, tvItem->cchTextMax);
1301 TRACE("item %d<%p>, txt %p, img %p, action %x\n",
1302 iItem, tvItem, tvItem->pszText, &tvItem->iImage, tvItem->mask);
1304 return TRUE;
1309 /* FIXME: check implementation of TVGN_NEXT/TVGN_NEXTVISIBLE */
1311 static LRESULT
1312 TREEVIEW_GetNextItem (HWND hwnd, WPARAM wParam, LPARAM lParam)
1315 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
1316 TREEVIEW_ITEM *wineItem, *returnItem;
1317 INT iItem = (INT)lParam, retval = 0, flag = (INT)wParam;
1318 HDC hdc;
1320 switch (flag) {
1321 case TVGN_ROOT:
1322 retval = (INT)infoPtr->TopRootItem;
1323 break;
1325 case TVGN_CARET:
1326 retval = (INT)infoPtr->selectedItem;
1327 break;
1329 case TVGN_FIRSTVISIBLE: /* FIXME:we should only recalculate, not redraw */
1330 hdc = GetDC (hwnd);
1331 TREEVIEW_Refresh (hwnd, hdc);
1332 ReleaseDC(hwnd,hdc);
1333 retval = (INT)infoPtr->firstVisible;
1334 break;
1336 case TVGN_DROPHILITE:
1337 retval = (INT)infoPtr->dropItem;
1338 break;
1340 case TVGN_NEXT:
1341 wineItem = TREEVIEW_ValidItem (infoPtr, (HTREEITEM)iItem);
1342 retval = wineItem ? (INT)wineItem->sibling : 0;
1343 break;
1345 case TVGN_PREVIOUS:
1346 wineItem = TREEVIEW_ValidItem (infoPtr, (HTREEITEM)iItem);
1347 retval = wineItem ? (INT)wineItem->upsibling : 0;
1348 break;
1350 case TVGN_PARENT:
1351 wineItem = TREEVIEW_ValidItem (infoPtr, (HTREEITEM)iItem);
1352 retval = wineItem ? (INT)wineItem->parent : 0;
1353 break;
1355 case TVGN_CHILD:
1356 wineItem = TREEVIEW_ValidItem (infoPtr, (HTREEITEM)iItem);
1357 retval = wineItem ? (INT)wineItem->firstChild : 0;
1358 break;
1360 case TVGN_LASTVISIBLE:
1361 if((wineItem = TREEVIEW_ValidItem (infoPtr, (HTREEITEM)iItem))) {
1362 returnItem = TREEVIEW_GetLastListItem (infoPtr,wineItem);
1363 retval = returnItem ? (INT)returnItem->hItem : 0;
1365 break;
1367 case TVGN_NEXTVISIBLE:
1368 if((wineItem = TREEVIEW_ValidItem (infoPtr, (HTREEITEM)iItem))) {
1369 returnItem = TREEVIEW_GetNextListItem (infoPtr,wineItem);
1370 retval = returnItem ? (INT)returnItem->hItem : 0;
1372 break;
1374 case TVGN_PREVIOUSVISIBLE:
1375 if((wineItem = TREEVIEW_ValidItem (infoPtr, (HTREEITEM)iItem))) {
1376 returnItem = TREEVIEW_GetPrevListItem (infoPtr, wineItem);
1377 retval = returnItem ? (INT)returnItem->hItem : 0;
1379 break;
1381 default:
1382 FIXME("Unknown msg %x,item %x\n", flag,iItem);
1383 break;
1386 TRACE("flags %x, item %d returns %d\n", flag, iItem, retval);
1387 return retval;
1391 static LRESULT
1392 TREEVIEW_GetCount (HWND hwnd, WPARAM wParam, LPARAM lParam)
1394 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
1396 TRACE(" %d\n",infoPtr->uNumItems);
1397 return (LRESULT) infoPtr->uNumItems;
1400 /***************************************************************************
1401 * This method does the chaining of the insertion of a treeview item
1402 * before an item.
1403 * If parent is NULL, we're inserting at the root of the list.
1405 static void TREEVIEW_InsertBefore(
1406 TREEVIEW_INFO *infoPtr,
1407 TREEVIEW_ITEM *newItem,
1408 TREEVIEW_ITEM *sibling,
1409 TREEVIEW_ITEM *parent)
1411 HTREEITEM siblingHandle = 0;
1412 HTREEITEM upSiblingHandle = 0;
1413 TREEVIEW_ITEM *upSibling = NULL;
1415 if (newItem == NULL)
1416 ERR("NULL newItem, impossible condition\n");
1418 if (sibling != NULL) /* Insert before this sibling for this parent */
1420 /* Store the new item sibling up sibling and sibling tem handle */
1421 siblingHandle = sibling->hItem;
1422 upSiblingHandle = sibling->upsibling;
1423 /* As well as a pointer to the upsibling sibling object */
1424 if ( (INT)sibling->upsibling != 0 )
1425 upSibling = &infoPtr->items[(INT)sibling->upsibling];
1427 /* Adjust the sibling pointer */
1428 sibling->upsibling = newItem->hItem;
1430 /* Adjust the new item pointers */
1431 newItem->upsibling = upSiblingHandle;
1432 newItem->sibling = siblingHandle;
1434 /* Adjust the up sibling pointer */
1435 if ( upSibling != NULL )
1436 upSibling->sibling = newItem->hItem;
1437 else
1438 /* this item is the first child of this parent, adjust parent pointers */
1439 if (parent)
1440 parent->firstChild = newItem->hItem;
1441 else
1442 infoPtr->TopRootItem= newItem->hItem;
1444 else /* Insert as first child of this parent */
1445 if (parent)
1446 parent->firstChild = newItem->hItem;
1449 /***************************************************************************
1450 * This method does the chaining of the insertion of a treeview item
1451 * after an item.
1452 * If parent is NULL, we're inserting at the root of the list.
1454 static void TREEVIEW_InsertAfter(
1455 TREEVIEW_INFO *infoPtr,
1456 TREEVIEW_ITEM *newItem,
1457 TREEVIEW_ITEM *upSibling,
1458 TREEVIEW_ITEM *parent)
1460 HTREEITEM upSiblingHandle = 0;
1461 HTREEITEM siblingHandle = 0;
1462 TREEVIEW_ITEM *sibling = NULL;
1465 if (newItem == NULL)
1466 ERR("NULL newItem, impossible condition\n");
1468 if (upSibling != NULL) /* Insert after this upsibling for this parent */
1470 /* Store the new item up sibling and sibling item handle */
1471 upSiblingHandle = upSibling->hItem;
1472 siblingHandle = upSibling->sibling;
1473 /* As well as a pointer to the upsibling sibling object */
1474 if ( (INT)upSibling->sibling != 0 )
1475 sibling = &infoPtr->items[(INT)upSibling->sibling];
1477 /* Adjust the up sibling pointer */
1478 upSibling->sibling = newItem->hItem;
1480 /* Adjust the new item pointers */
1481 newItem->upsibling = upSiblingHandle;
1482 newItem->sibling = siblingHandle;
1484 /* Adjust the sibling pointer */
1485 if ( sibling != NULL )
1486 sibling->upsibling = newItem->hItem;
1488 else
1489 newItem is the last of the level, nothing else to do
1492 else /* Insert as first child of this parent */
1493 if (parent)
1494 parent->firstChild = newItem->hItem;
1497 /***************************************************************************
1498 * Forward the DPA local callback to the treeview owner callback
1500 static INT WINAPI TREEVIEW_CallBackCompare(
1501 LPVOID first,
1502 LPVOID second,
1503 LPARAM tvInfoPtr)
1505 /* Forward the call to the client define callback */
1506 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr((HWND)tvInfoPtr);
1507 return (infoPtr->pCallBackSort->lpfnCompare)(
1508 ((TREEVIEW_ITEM*)first)->lParam,
1509 ((TREEVIEW_ITEM*)second)->lParam,
1510 infoPtr->pCallBackSort->lParam);
1513 /***************************************************************************
1514 * Treeview native sort routine: sort on item text.
1516 static INT WINAPI TREEVIEW_SortOnName (
1517 LPVOID first,
1518 LPVOID second,
1519 LPARAM tvInfoPtr)
1521 HWND hwnd=(HWND) tvInfoPtr;
1522 char *txt1, *txt2;
1523 TREEVIEW_ITEM *item;
1526 item=(TREEVIEW_ITEM *) first;
1527 if (item->pszText==LPSTR_TEXTCALLBACKA) {
1528 TREEVIEW_SendDispInfoNotify (hwnd, item, TVN_GETDISPINFOA, TVIF_TEXT);
1530 txt1=item->pszText;
1532 item=(TREEVIEW_ITEM *) second;
1533 if (item->pszText==LPSTR_TEXTCALLBACKA) {
1534 TREEVIEW_SendDispInfoNotify (hwnd, item, TVN_GETDISPINFOA, TVIF_TEXT);
1536 txt2=item->pszText;
1538 return -strcmp (txt1,txt2);
1541 /***************************************************************************
1542 * Setup the treeview structure with regards of the sort method
1543 * and sort the children of the TV item specified in lParam
1544 * fRecurse: currently unused. Should be zero.
1545 * parent: if pSort!=NULL, should equal pSort->hParent.
1546 * otherwise, item which child items are to be sorted.
1547 * pSort: sort method info. if NULL, sort on item text.
1548 * if non-NULL, sort on item's lParam content, and let the
1549 * application decide what that means. See also TVM_SORTCHILDRENCB.
1552 static LRESULT WINAPI TREEVIEW_Sort (
1553 HWND hwnd,
1554 BOOL fRecurse,
1555 HTREEITEM parent,
1556 LPTVSORTCB pSort
1559 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
1560 TREEVIEW_ITEM *sortMe = NULL; /* Node for which we sort the children */
1562 /* Obtain the TVSORTBC struct */
1563 infoPtr->pCallBackSort = pSort;
1565 /* undocumented feature: TVI_ROOT means `sort the whole tree' */
1567 if (parent==TVI_ROOT)
1568 parent=infoPtr->TopRootItem;
1570 /* Check for a valid handle to the parent item */
1571 if (!TREEVIEW_ValidItem(infoPtr, parent))
1573 ERR ("invalid item hParent=%x\n", (INT)parent);
1574 return FALSE;
1577 /* Obtain the parent node to sort */
1578 sortMe = &infoPtr->items[ (INT)parent ];
1580 /* Make sure there is something to sort */
1581 if ( sortMe->cChildren > 1 )
1583 /* pointer organization */
1584 HDPA sortList = DPA_Create(sortMe->cChildren);
1585 HTREEITEM itemHandle = sortMe->firstChild;
1586 TREEVIEW_ITEM *itemPtr = & infoPtr->items[ (INT)itemHandle ];
1588 /* TREEVIEW_ITEM rechaining */
1589 INT count = 0;
1590 VOID *item = 0;
1591 VOID *nextItem = 0;
1592 VOID *prevItem = 0;
1594 /* Build the list of item to sort */
1597 DPA_InsertPtr(
1598 sortList, /* the list */
1599 sortMe->cChildren+1, /* force the insertion to be an append */
1600 itemPtr); /* the ptr to store */
1602 /* Get the next sibling */
1603 itemHandle = itemPtr->sibling;
1604 itemPtr = & infoPtr->items[ (INT)itemHandle ];
1605 } while ( itemHandle != NULL );
1607 /* let DPA perform the sort activity */
1608 if (pSort)
1609 DPA_Sort(
1610 sortList, /* what */
1611 TREEVIEW_CallBackCompare, /* how */
1612 hwnd); /* owner */
1613 else
1614 DPA_Sort (
1615 sortList, /* what */
1616 TREEVIEW_SortOnName, /* how */
1617 hwnd); /* owner */
1620 * Reorganized TREEVIEW_ITEM structures.
1621 * Note that we know we have at least two elements.
1624 /* Get the first item and get ready to start... */
1625 item = DPA_GetPtr(sortList, count++);
1626 while ( (nextItem = DPA_GetPtr(sortList, count++)) != NULL )
1628 /* link the two current item toghether */
1629 ((TREEVIEW_ITEM*)item)->sibling = ((TREEVIEW_ITEM*)nextItem)->hItem;
1630 ((TREEVIEW_ITEM*)nextItem)->upsibling = ((TREEVIEW_ITEM*)item)->hItem;
1632 if (prevItem == NULL) /* this is the first item, update the parent */
1634 sortMe->firstChild = ((TREEVIEW_ITEM*)item)->hItem;
1635 ((TREEVIEW_ITEM*)item)->upsibling = NULL;
1637 else /* fix the back chaining */
1639 ((TREEVIEW_ITEM*)item)->upsibling = ((TREEVIEW_ITEM*)prevItem)->hItem;
1642 /* get ready for the next one */
1643 prevItem = item;
1644 item = nextItem;
1647 /* the last item is pointed to by item and never has a sibling */
1648 ((TREEVIEW_ITEM*)item)->sibling = NULL;
1650 DPA_Destroy(sortList);
1652 return TRUE;
1654 return FALSE;
1658 /***************************************************************************
1659 * Setup the treeview structure with regards of the sort method
1660 * and sort the children of the TV item specified in lParam
1662 static LRESULT WINAPI TREEVIEW_SortChildrenCB(
1663 HWND hwnd,
1664 WPARAM wParam,
1665 LPARAM lParam
1668 LPTVSORTCB pSort=(LPTVSORTCB) lParam;
1670 return TREEVIEW_Sort (hwnd, wParam, pSort->hParent, pSort);
1674 /***************************************************************************
1675 * Sort the children of the TV item specified in lParam.
1677 static LRESULT WINAPI TREEVIEW_SortChildren (
1678 HWND hwnd,
1679 WPARAM wParam,
1680 LPARAM lParam)
1682 return TREEVIEW_Sort (hwnd, (BOOL) wParam, (HTREEITEM) lParam, NULL);
1687 /* the method used below isn't the most memory-friendly, but it avoids
1688 a lot of memory reallocations */
1690 /* BTW: we waste handle 0; 0 is not an allowed handle. */
1692 static LRESULT
1693 TREEVIEW_InsertItemA (HWND hwnd, WPARAM wParam, LPARAM lParam)
1696 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
1697 TVINSERTSTRUCTA *ptdi;
1698 TVITEMEXA *tvItem;
1699 TREEVIEW_ITEM *wineItem, *parentItem, *prevsib, *sibItem;
1700 INT iItem,listItems,i,len;
1702 /* Item to insert */
1703 ptdi = (LPTVINSERTSTRUCTA) lParam;
1705 /* check if memory is available */
1707 if (infoPtr->uNumPtrsAlloced==0) {
1708 infoPtr->items = COMCTL32_Alloc (TVITEM_ALLOC*sizeof (TREEVIEW_ITEM));
1709 infoPtr->freeList= COMCTL32_Alloc ((1+(TVITEM_ALLOC>>5)) * sizeof (INT));
1710 infoPtr->uNumPtrsAlloced=TVITEM_ALLOC;
1711 infoPtr->TopRootItem=(HTREEITEM)1;
1715 * Reallocate contiguous space for items
1717 if (infoPtr->uNumItems == (infoPtr->uNumPtrsAlloced-1) ) {
1718 TREEVIEW_ITEM *oldItems = infoPtr->items;
1719 INT *oldfreeList = infoPtr->freeList;
1721 infoPtr->uNumPtrsAlloced*=2;
1722 infoPtr->items = COMCTL32_Alloc (infoPtr->uNumPtrsAlloced*sizeof (TREEVIEW_ITEM));
1723 infoPtr->freeList= COMCTL32_Alloc ((1+(infoPtr->uNumPtrsAlloced>>5))*sizeof (INT));
1725 memcpy (&infoPtr->items[0], &oldItems[0],
1726 infoPtr->uNumPtrsAlloced/2 * sizeof(TREEVIEW_ITEM));
1727 memcpy (&infoPtr->freeList[0], &oldfreeList[0],
1728 (infoPtr->uNumPtrsAlloced>>6) * sizeof(INT));
1730 COMCTL32_Free (oldItems);
1731 COMCTL32_Free (oldfreeList);
1735 * Reset infoPtr structure with new stat according to current TV picture
1737 iItem=0;
1738 infoPtr->uNumItems++;
1739 if ((INT)infoPtr->uMaxHandle==(infoPtr->uNumItems-1)) {
1740 iItem=infoPtr->uNumItems;
1741 infoPtr->uMaxHandle = (HTREEITEM)((INT)infoPtr->uMaxHandle + 1);
1742 } else { /* check freelist */
1743 for (i=0; i<=infoPtr->uNumPtrsAlloced>>5; i++) {
1744 if (infoPtr->freeList[i]) {
1745 iItem=ffs (infoPtr->freeList[i])-1;
1746 tv_clear_bit(iItem,&infoPtr->freeList[i]);
1747 iItem+=i<<5;
1748 break;
1753 if (TRACE_ON(treeview)) {
1754 for (i=0; i<=infoPtr->uNumPtrsAlloced>>5; i++)
1755 TRACE("%8x\n",infoPtr->freeList[i]);
1758 if (!iItem) ERR("Argh -- can't find free item.\n");
1761 * Find the parent item of the new item
1763 tvItem= & ptdi->DUMMYUNIONNAME.itemex;
1764 wineItem=& infoPtr->items[iItem];
1766 if ((ptdi->hParent==TVI_ROOT) || (ptdi->hParent==0)) {
1767 parentItem = NULL;
1768 wineItem->parent = 0;
1769 sibItem = &infoPtr->items [(INT)infoPtr->TopRootItem];
1770 listItems = infoPtr->uNumItems;
1772 else {
1773 parentItem = &infoPtr->items[(INT)ptdi->hParent];
1775 /* Do the insertion here it if it's the only item of this parent */
1776 if (!parentItem->firstChild)
1777 parentItem->firstChild=(HTREEITEM)iItem;
1779 wineItem->parent = ptdi->hParent;
1780 sibItem = &infoPtr->items [(INT)parentItem->firstChild];
1781 listItems = parentItem->cChildren;
1782 parentItem->cChildren++;
1786 /* NOTE: I am moving some setup of the wineItem object that was initialy
1787 * done at the end of the function since some of the values are
1788 * required by the Callback sorting
1791 if (tvItem->mask & TVIF_TEXT)
1794 * Setup the item text stuff here since it's required by the Sort method
1795 * when the insertion are ordered
1797 if (tvItem->pszText!=LPSTR_TEXTCALLBACKA)
1799 TRACE("(%p,%s)\n", &tvItem->pszText, tvItem->pszText);
1800 len = lstrlenA (tvItem->pszText)+1;
1801 wineItem->pszText= COMCTL32_Alloc (len+1);
1802 lstrcpyA (wineItem->pszText, tvItem->pszText);
1803 wineItem->cchTextMax=len;
1805 else
1807 TRACE("LPSTR_TEXTCALLBACK\n");
1808 wineItem->pszText = LPSTR_TEXTCALLBACKA;
1809 wineItem->cchTextMax = 0;
1813 if (tvItem->mask & TVIF_PARAM)
1814 wineItem->lParam=tvItem->lParam;
1817 wineItem->upsibling=0; /* needed in case we're the first item in a list */
1818 wineItem->sibling=0;
1819 wineItem->firstChild=0;
1820 wineItem->hItem=(HTREEITEM)iItem;
1822 if (listItems!=0) {
1823 prevsib=NULL;
1825 switch ((DWORD) ptdi->hInsertAfter) {
1826 case (DWORD) TVI_FIRST:
1827 if (sibItem==wineItem) break;
1828 if (wineItem->parent) {
1829 wineItem->sibling=parentItem->firstChild;
1830 parentItem->firstChild=(HTREEITEM)iItem;
1831 } else {
1832 wineItem->sibling=infoPtr->TopRootItem;
1833 infoPtr->TopRootItem=(HTREEITEM)iItem;
1835 sibItem->upsibling=(HTREEITEM)iItem;
1836 break;
1838 case (DWORD) TVI_SORT:
1839 if (sibItem==wineItem)
1841 * This item is the first child of the level and it
1842 * has already been inserted
1844 break;
1845 else
1847 TREEVIEW_ITEM *aChild;
1850 TREEVIEW_ITEM *previousChild = NULL;
1851 BOOL bItemInserted = FALSE;
1853 if (parentItem)
1854 aChild = &infoPtr->items[(INT)parentItem->firstChild];
1855 else
1856 aChild = &infoPtr->items[(INT)infoPtr->TopRootItem];
1858 /* lookup the text if using LPSTR_TEXTCALLBACKs */
1859 if (wineItem->pszText==LPSTR_TEXTCALLBACKA) {
1860 TREEVIEW_SendDispInfoNotify (hwnd, wineItem, TVN_GETDISPINFOA, TVIF_TEXT);
1863 /* Iterate the parent children to see where we fit in */
1864 while ( aChild != NULL )
1866 INT comp;
1868 /* lookup the text if using LPSTR_TEXTCALLBACKs */
1869 if (aChild->pszText==LPSTR_TEXTCALLBACKA) {
1870 TREEVIEW_SendDispInfoNotify (hwnd, aChild, TVN_GETDISPINFOA, TVIF_TEXT);
1873 comp = strcmp(wineItem->pszText, aChild->pszText);
1874 if ( comp < 0 ) /* we are smaller than the current one */
1876 TREEVIEW_InsertBefore(infoPtr, wineItem, aChild, parentItem);
1877 bItemInserted = TRUE;
1878 break;
1880 else if ( comp > 0 ) /* we are bigger than the current one */
1882 previousChild = aChild;
1883 aChild = (aChild->sibling == 0) /* This will help us to exit */
1884 ? NULL /* if there is no more sibling */
1885 : &infoPtr->items[(INT)aChild->sibling];
1887 /* Look at the next item */
1888 continue;
1890 else if ( comp == 0 )
1893 * An item with this name is already existing, therefore,
1894 * we add after the one we found
1896 TREEVIEW_InsertAfter(infoPtr, wineItem, aChild, parentItem);
1897 bItemInserted = TRUE;
1898 break;
1903 * we reach the end of the child list and the item as not
1904 * yet been inserted, therefore, insert it after the last child.
1906 if ( (! bItemInserted ) && (aChild == NULL) )
1907 TREEVIEW_InsertAfter(infoPtr, wineItem, previousChild, parentItem);
1909 break;
1913 case (DWORD) TVI_LAST:
1914 if (sibItem==wineItem) break;
1915 while (sibItem->sibling) {
1916 prevsib=sibItem;
1917 sibItem=&infoPtr->items [(INT)sibItem->sibling];
1919 sibItem->sibling=(HTREEITEM)iItem;
1920 wineItem->upsibling=sibItem->hItem;
1921 break;
1922 default:
1923 while ((sibItem->sibling) && (sibItem->hItem!=ptdi->hInsertAfter))
1925 prevsib=sibItem;
1926 sibItem=&infoPtr->items [(INT)sibItem->sibling];
1928 if (sibItem->hItem!=ptdi->hInsertAfter) {
1929 ERR("tried to insert item after nonexisting handle %d.\n",
1930 (INT) ptdi->hInsertAfter);
1931 break;
1933 prevsib=sibItem;
1934 if (sibItem->sibling) {
1935 sibItem=&infoPtr->items [(INT)sibItem->sibling];
1936 sibItem->upsibling=(HTREEITEM)iItem;
1937 wineItem->sibling=sibItem->hItem;
1939 prevsib->sibling=(HTREEITEM)iItem;
1940 wineItem->upsibling=prevsib->hItem;
1941 break;
1946 /* Fill in info structure */
1948 TRACE("new item %d; parent %d, mask %x\n", iItem,
1949 (INT)wineItem->parent,tvItem->mask);
1951 wineItem->mask=tvItem->mask;
1952 wineItem->iIntegral=1;
1954 if (tvItem->mask & TVIF_CHILDREN) {
1955 wineItem->cChildren=tvItem->cChildren;
1956 if (tvItem->cChildren==I_CHILDRENCALLBACK)
1957 FIXME(" I_CHILDRENCALLBACK not supported\n");
1960 wineItem->expandBox.left = 0; /* Initialize the expandBox */
1961 wineItem->expandBox.top = 0;
1962 wineItem->expandBox.right = 0;
1963 wineItem->expandBox.bottom = 0;
1965 if (tvItem->mask & TVIF_IMAGE)
1966 wineItem->iImage=tvItem->iImage;
1968 /* If the application sets TVIF_INTEGRAL without
1969 supplying a TVITEMEX structure, it's toast */
1971 if (tvItem->mask & TVIF_INTEGRAL)
1972 wineItem->iIntegral=tvItem->iIntegral;
1974 if (tvItem->mask & TVIF_SELECTEDIMAGE)
1975 wineItem->iSelectedImage=tvItem->iSelectedImage;
1977 if (tvItem->mask & TVIF_STATE) {
1978 TRACE("item state: %x ->%x\n", wineItem->state, tvItem->state);
1979 TRACE("statemask: %x ->%x\n", wineItem->stateMask, tvItem->stateMask);
1980 wineItem->state=tvItem->state;
1981 wineItem->stateMask=tvItem->stateMask;
1984 TREEVIEW_QueueRefresh (hwnd);
1986 return (LRESULT) iItem;
1990 static LRESULT
1991 TREEVIEW_InsertItemW(HWND hwnd, WPARAM wParam, LPARAM lParam)
1993 TVINSERTSTRUCTW *tvisW;
1994 TVINSERTSTRUCTA tvisA;
1995 LRESULT lRes;
1997 tvisW = (LPTVINSERTSTRUCTW)lParam;
1999 tvisA.hParent = tvisW->hParent;
2000 tvisA.hInsertAfter = tvisW->hInsertAfter;
2002 tvisA.DUMMYUNIONNAME.item.mask = tvisW->DUMMYUNIONNAME.item.mask;
2003 tvisA.DUMMYUNIONNAME.item.hItem = tvisW->DUMMYUNIONNAME.item.hItem;
2004 tvisA.DUMMYUNIONNAME.item.state = tvisW->DUMMYUNIONNAME.item.state;
2005 tvisA.DUMMYUNIONNAME.item.stateMask = tvisW->DUMMYUNIONNAME.item.stateMask;
2006 tvisA.DUMMYUNIONNAME.item.cchTextMax = tvisW->DUMMYUNIONNAME.item.cchTextMax;
2008 if(tvisW->DUMMYUNIONNAME.item.pszText)
2010 if (tvisW->DUMMYUNIONNAME.item.pszText!=LPSTR_TEXTCALLBACKW)
2012 int len = lstrlenW (tvisW->DUMMYUNIONNAME.item.pszText)+1;
2013 tvisA.DUMMYUNIONNAME.item.pszText = COMCTL32_Alloc (len);
2014 lstrcpyWtoA (tvisA.DUMMYUNIONNAME.item.pszText,
2015 tvisW->DUMMYUNIONNAME.item.pszText );
2017 else
2019 tvisA.DUMMYUNIONNAME.item.pszText = LPSTR_TEXTCALLBACKA;
2020 tvisA.DUMMYUNIONNAME.item.cchTextMax = 0;
2024 tvisA.DUMMYUNIONNAME.item.iImage = tvisW->DUMMYUNIONNAME.item.iImage;
2025 tvisA.DUMMYUNIONNAME.item.iSelectedImage = tvisW->DUMMYUNIONNAME.item.iSelectedImage;
2026 tvisA.DUMMYUNIONNAME.item.cChildren = tvisW->DUMMYUNIONNAME.item.cChildren;
2027 tvisA.DUMMYUNIONNAME.item.lParam = tvisW->DUMMYUNIONNAME.item.lParam;
2029 lRes = TREEVIEW_InsertItemA(hwnd,wParam,(LPARAM)&tvisA);
2031 if (tvisA.DUMMYUNIONNAME.item.pszText!=LPSTR_TEXTCALLBACKA)
2033 COMCTL32_Free(tvisA.DUMMYUNIONNAME.item.pszText);
2036 return lRes;
2041 static LRESULT
2042 TREEVIEW_DeleteItem (HWND hwnd, WPARAM wParam, LPARAM lParam)
2044 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
2045 INT iItem;
2046 TREEVIEW_ITEM *wineItem;
2048 TRACE("item = %08lx\n", lParam);
2050 if (lParam == (INT)TVI_ROOT) {
2051 TREEVIEW_RemoveTree (hwnd);
2052 } else {
2053 iItem= (INT) lParam;
2054 wineItem = TREEVIEW_ValidItem (infoPtr, (HTREEITEM)iItem);
2055 if (!wineItem) return FALSE;
2057 if (wineItem->pszText==LPSTR_TEXTCALLBACKA)
2058 TRACE("LPSTR_TEXTCALLBACK\n");
2059 else
2060 TRACE("%s\n",wineItem->pszText);
2061 TREEVIEW_RemoveItem (hwnd, wineItem);
2064 TREEVIEW_QueueRefresh (hwnd);
2066 return TRUE;
2071 static LRESULT
2072 TREEVIEW_GetIndent (HWND hwnd)
2074 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
2076 TRACE("\n");
2077 return infoPtr->uIndent;
2080 static LRESULT
2081 TREEVIEW_SetIndent (HWND hwnd, WPARAM wParam)
2083 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
2084 INT newIndent;
2086 TRACE("\n");
2087 newIndent=(INT) wParam;
2088 if (newIndent < MINIMUM_INDENT) newIndent=MINIMUM_INDENT;
2089 infoPtr->uIndent=newIndent;
2091 return 0;
2094 static LRESULT
2095 TREEVIEW_GetToolTips (HWND hwnd)
2098 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
2100 TRACE("\n");
2101 return infoPtr->hwndToolTip;
2105 static LRESULT
2106 TREEVIEW_SetToolTips (HWND hwnd, WPARAM wParam)
2109 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
2110 HWND prevToolTip;
2112 TRACE("\n");
2113 prevToolTip=infoPtr->hwndToolTip;
2114 infoPtr->hwndToolTip= (HWND) wParam;
2116 return prevToolTip;
2120 static LRESULT CALLBACK
2121 TREEVIEW_GetEditControl (HWND hwnd)
2124 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
2126 return infoPtr->hwndEdit;
2129 LRESULT CALLBACK
2130 TREEVIEW_Edit_SubclassProc (HWND hwnd, UINT uMsg, WPARAM wParam,
2131 LPARAM lParam)
2133 switch (uMsg)
2135 case WM_ERASEBKGND:
2137 RECT rc;
2138 HDC hdc = (HDC) wParam;
2139 GetClientRect (hwnd, &rc);
2140 Rectangle (hdc, rc.left, rc.top, rc.right, rc.bottom);
2141 return -1;
2144 case WM_GETDLGCODE:
2146 return DLGC_WANTARROWS | DLGC_WANTALLKEYS;
2149 default:
2151 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(GetParent(hwnd));
2152 if (infoPtr!=NULL)
2153 return CallWindowProcA (infoPtr->wpEditOrig, hwnd, uMsg, wParam, lParam);
2154 else
2155 break;
2160 return 0;
2164 /* should handle edit control messages here */
2166 static LRESULT
2167 TREEVIEW_Command (HWND hwnd, WPARAM wParam, LPARAM lParam)
2170 TRACE("%x %ld\n",wParam, lParam);
2172 switch (HIWORD(wParam))
2174 case EN_UPDATE:
2177 * Adjust the edit window size
2179 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
2180 TREEVIEW_ITEM *editItem = TREEVIEW_ValidItem(infoPtr, infoPtr->editItem);
2181 INT iLength = GetWindowTextLengthA(infoPtr->hwndEdit);
2182 HDC hdc = GetDC(infoPtr->hwndEdit);
2183 TEXTMETRICA tm;
2185 if ( GetTextMetricsA(hdc, &tm) )
2187 LONG newWidth = (iLength * tm.tmAveCharWidth) + 15;
2189 SetWindowPos (
2190 infoPtr->hwndEdit,
2191 HWND_TOP,
2192 editItem->text.left - 2,
2193 editItem->text.top - 1,
2194 newWidth,
2195 editItem->text.bottom - editItem->text.top + 3,
2196 SWP_DRAWFRAME );
2198 ReleaseDC(hwnd, hdc);
2200 break;
2203 case EN_KILLFOCUS:
2204 /* TREEVIEW_EndEditLabelNow(hwnd, (WPARAM)FALSE, 0);
2206 break;
2208 default:
2209 return SendMessageA (GetParent (hwnd), WM_COMMAND, wParam, lParam);
2212 return 0;
2215 static LRESULT
2216 TREEVIEW_Size (HWND hwnd, WPARAM wParam, LPARAM lParam)
2219 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
2221 if (infoPtr->bAutoSize)
2223 infoPtr->bAutoSize = FALSE;
2224 return 0;
2226 infoPtr->bAutoSize = TRUE;
2228 if (wParam == SIZE_RESTORED)
2230 infoPtr->uTotalWidth = LOWORD (lParam);
2231 infoPtr->uTotalHeight = HIWORD (lParam);
2232 } else {
2233 FIXME("WM_SIZE flag %x %lx not handled\n", wParam, lParam);
2236 TREEVIEW_QueueRefresh (hwnd);
2237 return 0;
2242 static LRESULT
2243 TREEVIEW_StyleChanged (HWND hwnd, WPARAM wParam, LPARAM lParam)
2245 HDC hdc;
2247 TRACE("(%x %lx)\n",wParam,lParam);
2248 hdc = GetDC (hwnd);
2249 TREEVIEW_Refresh (hwnd, hdc);
2250 ReleaseDC(hwnd,hdc);
2252 return 0;
2255 static LRESULT
2256 TREEVIEW_Create (HWND hwnd, WPARAM wParam, LPARAM lParam)
2258 TREEVIEW_INFO *infoPtr;
2259 DWORD dwStyle = GetWindowLongA (hwnd, GWL_STYLE);
2260 LOGFONTA logFont;
2261 TEXTMETRICA tm;
2262 HDC hdc;
2264 TRACE("wnd %x, style %lx\n",hwnd,dwStyle);
2265 /* allocate memory for info structure */
2266 infoPtr = (TREEVIEW_INFO *) COMCTL32_Alloc (sizeof(TREEVIEW_INFO));
2268 SetWindowLongA( hwnd, 0, (DWORD)infoPtr);
2270 if (infoPtr == NULL) {
2271 ERR("could not allocate info memory!\n");
2272 return 0;
2275 if ((TREEVIEW_INFO*) GetWindowLongA( hwnd, 0) != infoPtr) {
2276 ERR("pointer assignment error!\n");
2277 return 0;
2280 hdc=GetDC (hwnd);
2282 /* set default settings */
2283 infoPtr->uInternalStatus=0;
2284 infoPtr->uNumItems=0;
2285 infoPtr->clrBk = GetSysColor (COLOR_WINDOW);
2286 infoPtr->clrText = GetSysColor (COLOR_WINDOWTEXT);
2287 infoPtr->clrLine = GetSysColor (COLOR_WINDOWTEXT);
2288 infoPtr->clrInsertMark = GetSysColor (COLOR_BTNTEXT);
2289 infoPtr->cy = 0;
2290 infoPtr->cx = 0;
2291 infoPtr->uIndent = 15;
2292 infoPtr->himlNormal = NULL;
2293 infoPtr->himlState = NULL;
2294 infoPtr->uItemHeight = -1;
2295 GetTextMetricsA (hdc, &tm);
2296 infoPtr->hFont = GetStockObject (DEFAULT_GUI_FONT);
2297 GetObjectA (infoPtr->hFont, sizeof (LOGFONTA), &logFont);
2298 logFont.lfWeight=FW_BOLD;
2299 infoPtr->hBoldFont = CreateFontIndirectA (&logFont);
2301 infoPtr->items = NULL;
2302 infoPtr->selectedItem=0;
2303 infoPtr->clrText=-1; /* use system color */
2304 infoPtr->dropItem=0;
2305 infoPtr->insertMarkItem=0;
2306 infoPtr->insertBeforeorAfter=0;
2307 infoPtr->pCallBackSort=NULL;
2308 infoPtr->uScrollTime = 300; /* milliseconds */
2309 infoPtr->wpEditOrig = NULL; /* we haven't subclassed anything yet */
2311 infoPtr->hwndToolTip=0;
2312 if (!(dwStyle & TVS_NOTOOLTIPS)) { /* Create tooltip control */
2313 TTTOOLINFOA ti;
2315 infoPtr->hwndToolTip =
2316 CreateWindowExA (0, TOOLTIPS_CLASSA, NULL, 0,
2317 CW_USEDEFAULT, CW_USEDEFAULT,
2318 CW_USEDEFAULT, CW_USEDEFAULT,
2319 hwnd, 0, 0, 0);
2321 /* Send NM_TOOLTIPSCREATED notification */
2322 if (infoPtr->hwndToolTip) {
2323 NMTOOLTIPSCREATED nmttc;
2325 nmttc.hdr.hwndFrom = hwnd;
2326 nmttc.hdr.idFrom = GetWindowLongA( hwnd, GWL_ID);
2327 nmttc.hdr.code = NM_TOOLTIPSCREATED;
2328 nmttc.hwndToolTips = infoPtr->hwndToolTip;
2330 SendMessageA (GetParent (hwnd), WM_NOTIFY,
2331 (WPARAM) GetWindowLongA( hwnd, GWL_ID), (LPARAM)&nmttc);
2334 ZeroMemory (&ti, sizeof(TTTOOLINFOA));
2335 ti.cbSize = sizeof(TTTOOLINFOA);
2336 ti.uFlags = TTF_IDISHWND | TTF_TRACK | TTF_TRANSPARENT ;
2337 ti.hwnd = hwnd;
2338 ti.uId = 0;
2339 ti.lpszText = "Test"; /* LPSTR_TEXTCALLBACK; */
2340 SetRectEmpty (&ti.rect);
2342 SendMessageA (infoPtr->hwndToolTip, TTM_ADDTOOLA, 0, (LPARAM)&ti);
2345 infoPtr->hwndEdit = CreateWindowExA (
2346 WS_EX_LEFT,
2347 "EDIT",
2349 WS_CHILD | WS_BORDER | ES_AUTOHSCROLL |
2350 ES_WANTRETURN | ES_LEFT,
2351 0, 0, 0, 0,
2352 hwnd,
2353 0,0,0); /* FIXME: (HMENU)IDTVEDIT,pcs->hInstance,0);*/
2355 SendMessageA ( infoPtr->hwndEdit, WM_SETFONT, infoPtr->hFont, FALSE);
2356 infoPtr->wpEditOrig = (WNDPROC)SetWindowLongA (
2357 infoPtr->hwndEdit,
2358 GWL_WNDPROC,
2359 (LONG) TREEVIEW_Edit_SubclassProc);
2361 if (dwStyle & TVS_CHECKBOXES) {
2362 HBITMAP hbmLoad;
2363 int nIndex;
2365 infoPtr->himlState =
2366 ImageList_Create (16, 16,ILC_COLOR|ILC_MASK, 15, 1);
2368 hbmLoad = LoadBitmapA (COMCTL32_hModule, MAKEINTRESOURCEA(IDT_CHECK));
2369 TRACE ("%x\n",hbmLoad);
2370 nIndex = ImageList_AddMasked (infoPtr->himlState, hbmLoad, CLR_DEFAULT);
2371 TRACE ("%d\n",nIndex);
2372 DeleteObject (hbmLoad);
2374 ReleaseDC (hwnd, hdc);
2375 return 0;
2380 static LRESULT
2381 TREEVIEW_Destroy (HWND hwnd)
2383 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
2385 TRACE("\n");
2386 TREEVIEW_RemoveTree (hwnd);
2387 SetWindowLongA (hwnd, 0, (DWORD)NULL);
2389 if (infoPtr->Timer & TV_REFRESH_TIMER_SET)
2390 KillTimer (hwnd, TV_REFRESH_TIMER);
2391 if (infoPtr->hwndToolTip)
2392 DestroyWindow (infoPtr->hwndToolTip);
2394 COMCTL32_Free (infoPtr);
2395 return 0;
2399 static LRESULT
2400 TREEVIEW_Paint (HWND hwnd, WPARAM wParam, LPARAM lParam)
2402 HDC hdc;
2403 PAINTSTRUCT ps;
2405 TRACE("\n");
2406 hdc = wParam==0 ? BeginPaint (hwnd, &ps) : (HDC)wParam;
2407 TREEVIEW_Refresh (hwnd, hdc);
2408 ReleaseDC(hwnd,hdc);
2409 if(!wParam) EndPaint (hwnd, &ps);
2410 TRACE("done\n");
2412 return DefWindowProcA (hwnd, WM_PAINT, wParam, lParam);
2415 static LRESULT
2416 TREEVIEW_SetFocus (HWND hwnd, WPARAM wParam, LPARAM lParam)
2418 TREEVIEW_SendSimpleNotify (hwnd, NM_SETFOCUS);
2419 InvalidateRect(hwnd, NULL, FALSE);
2420 return 0;
2423 static LRESULT
2424 TREEVIEW_KillFocus (HWND hwnd, WPARAM wParam, LPARAM lParam)
2426 TREEVIEW_SendSimpleNotify (hwnd, NM_KILLFOCUS);
2427 InvalidateRect(hwnd, NULL, FALSE);
2428 return 0;
2431 static LRESULT
2432 TREEVIEW_EraseBackground (HWND hwnd, WPARAM wParam, LPARAM lParam)
2434 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
2435 HBRUSH hBrush = CreateSolidBrush (infoPtr->clrBk);
2436 RECT rect;
2438 TRACE("\n");
2439 GetClientRect (hwnd, &rect);
2440 FillRect ((HDC)wParam, &rect, hBrush);
2441 DeleteObject (hBrush);
2442 return TRUE;
2450 /* Notifications */
2456 static BOOL
2457 TREEVIEW_SendSimpleNotify (HWND hwnd, UINT code)
2459 NMHDR nmhdr;
2461 TRACE("%x\n",code);
2462 nmhdr.hwndFrom = hwnd;
2463 nmhdr.idFrom = GetWindowLongA( hwnd, GWL_ID);
2464 nmhdr.code = code;
2466 return (BOOL) SendMessageA (GetParent (hwnd), WM_NOTIFY,
2467 (WPARAM)nmhdr.idFrom, (LPARAM)&nmhdr);
2472 static BOOL
2473 TREEVIEW_SendTreeviewNotify (HWND hwnd, UINT code, UINT action,
2474 HTREEITEM oldItem, HTREEITEM newItem)
2477 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
2478 NMTREEVIEWA nmhdr;
2479 TREEVIEW_ITEM *wineItem;
2481 TRACE("code:%x action:%x olditem:%x newitem:%x\n",
2482 code,action,(INT)oldItem,(INT)newItem);
2483 nmhdr.hdr.hwndFrom = hwnd;
2484 nmhdr.hdr.idFrom = GetWindowLongA( hwnd, GWL_ID);
2485 nmhdr.hdr.code = code;
2486 nmhdr.action = action;
2487 if (oldItem) {
2488 wineItem=& infoPtr->items[(INT)oldItem];
2489 nmhdr.itemOld.mask = wineItem->mask;
2490 nmhdr.itemOld.hItem = wineItem->hItem;
2491 nmhdr.itemOld.state = wineItem->state;
2492 nmhdr.itemOld.stateMask = wineItem->stateMask;
2493 nmhdr.itemOld.iImage = wineItem->iImage;
2494 nmhdr.itemOld.pszText = wineItem->pszText;
2495 nmhdr.itemOld.cchTextMax= wineItem->cchTextMax;
2496 nmhdr.itemOld.iImage = wineItem->iImage;
2497 nmhdr.itemOld.iSelectedImage = wineItem->iSelectedImage;
2498 nmhdr.itemOld.cChildren = wineItem->cChildren;
2499 nmhdr.itemOld.lParam = wineItem->lParam;
2502 if (newItem) {
2503 wineItem=& infoPtr->items[(INT)newItem];
2504 nmhdr.itemNew.mask = wineItem->mask;
2505 nmhdr.itemNew.hItem = wineItem->hItem;
2506 nmhdr.itemNew.state = wineItem->state;
2507 nmhdr.itemNew.stateMask = wineItem->stateMask;
2508 nmhdr.itemNew.iImage = wineItem->iImage;
2509 nmhdr.itemNew.pszText = wineItem->pszText;
2510 nmhdr.itemNew.cchTextMax= wineItem->cchTextMax;
2511 nmhdr.itemNew.iImage = wineItem->iImage;
2512 nmhdr.itemNew.iSelectedImage = wineItem->iSelectedImage;
2513 nmhdr.itemNew.cChildren = wineItem->cChildren;
2514 nmhdr.itemNew.lParam = wineItem->lParam;
2517 nmhdr.ptDrag.x = 0;
2518 nmhdr.ptDrag.y = 0;
2520 return (BOOL)SendMessageA (GetParent (hwnd), WM_NOTIFY,
2521 (WPARAM) GetWindowLongA( hwnd, GWL_ID), (LPARAM)&nmhdr);
2525 static BOOL
2526 TREEVIEW_SendTreeviewDnDNotify (HWND hwnd, UINT code, HTREEITEM dragItem,
2527 POINT pt)
2529 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
2530 NMTREEVIEWA nmhdr;
2531 TREEVIEW_ITEM *wineItem;
2533 TRACE("code:%x dragitem:%x\n", code,(INT)dragItem);
2535 nmhdr.hdr.hwndFrom = hwnd;
2536 nmhdr.hdr.idFrom = GetWindowLongA( hwnd, GWL_ID);
2537 nmhdr.hdr.code = code;
2538 nmhdr.action = 0;
2539 wineItem=& infoPtr->items[(INT)dragItem];
2540 nmhdr.itemNew.mask = wineItem->mask;
2541 nmhdr.itemNew.hItem = wineItem->hItem;
2542 nmhdr.itemNew.state = wineItem->state;
2543 nmhdr.itemNew.lParam = wineItem->lParam;
2545 nmhdr.ptDrag.x = pt.x;
2546 nmhdr.ptDrag.y = pt.y;
2548 return (BOOL)SendMessageA (GetParent (hwnd), WM_NOTIFY,
2549 (WPARAM) GetWindowLongA( hwnd, GWL_ID), (LPARAM)&nmhdr);
2555 static BOOL
2556 TREEVIEW_SendDispInfoNotify (HWND hwnd, TREEVIEW_ITEM *wineItem,
2557 UINT code, UINT what)
2559 NMTVDISPINFOA tvdi;
2560 BOOL retval;
2561 char *buf;
2563 TRACE("item %d, action %x, state %d\n",
2564 (INT)wineItem->hItem,
2565 what,
2566 (INT)wineItem->state);
2568 tvdi.hdr.hwndFrom = hwnd;
2569 tvdi.hdr.idFrom = GetWindowLongA( hwnd, GWL_ID);
2570 tvdi.hdr.code = code;
2571 tvdi.item.mask = what;
2572 tvdi.item.hItem = wineItem->hItem;
2573 tvdi.item.state = wineItem->state;
2574 tvdi.item.lParam = wineItem->lParam;
2575 tvdi.item.pszText = COMCTL32_Alloc (128*sizeof(char));
2576 tvdi.item.cchTextMax = 128;
2577 buf = tvdi.item.pszText;
2579 retval=(BOOL)SendMessageA (
2580 GetParent(hwnd),
2581 WM_NOTIFY,
2582 (WPARAM)tvdi.hdr.idFrom,
2583 (LPARAM)&tvdi);
2585 if (what & TVIF_TEXT) {
2586 wineItem->pszText = tvdi.item.pszText;
2587 if (buf==tvdi.item.pszText) {
2588 wineItem->cchTextMax = 128;
2589 } else {
2590 TRACE("user-supplied buffer\n");
2591 COMCTL32_Free (buf);
2592 wineItem->cchTextMax = 0;
2595 if (what & TVIF_SELECTEDIMAGE)
2596 wineItem->iSelectedImage = tvdi.item.iSelectedImage;
2597 if (what & TVIF_IMAGE)
2598 wineItem->iImage = tvdi.item.iImage;
2599 if (what & TVIF_CHILDREN)
2600 wineItem->cChildren = tvdi.item.cChildren;
2602 return retval;
2607 static BOOL
2608 TREEVIEW_SendCustomDrawNotify (HWND hwnd, DWORD dwDrawStage, HDC hdc,
2609 RECT rc)
2611 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
2612 NMTVCUSTOMDRAW nmcdhdr;
2613 LPNMCUSTOMDRAW nmcd;
2615 TRACE("drawstage:%lx hdc:%x\n", dwDrawStage, hdc);
2617 nmcd= & nmcdhdr.nmcd;
2618 nmcd->hdr.hwndFrom = hwnd;
2619 nmcd->hdr.idFrom = GetWindowLongA( hwnd, GWL_ID);
2620 nmcd->hdr.code = NM_CUSTOMDRAW;
2621 nmcd->dwDrawStage= dwDrawStage;
2622 nmcd->hdc = hdc;
2623 nmcd->rc.left = rc.left;
2624 nmcd->rc.right = rc.right;
2625 nmcd->rc.bottom = rc.bottom;
2626 nmcd->rc.top = rc.top;
2627 nmcd->dwItemSpec = 0;
2628 nmcd->uItemState = 0;
2629 nmcd->lItemlParam= 0;
2630 nmcdhdr.clrText = infoPtr->clrText;
2631 nmcdhdr.clrTextBk= infoPtr->clrBk;
2632 nmcdhdr.iLevel = 0;
2634 return (BOOL)SendMessageA (GetParent (hwnd), WM_NOTIFY,
2635 (WPARAM) GetWindowLongA( hwnd, GWL_ID), (LPARAM)&nmcdhdr);
2641 /* FIXME: need to find out when the flags in uItemState need to be set */
2643 static BOOL
2644 TREEVIEW_SendCustomDrawItemNotify (HWND hwnd, HDC hdc,
2645 TREEVIEW_ITEM *wineItem, UINT uItemDrawState)
2647 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
2648 NMTVCUSTOMDRAW nmcdhdr;
2649 LPNMCUSTOMDRAW nmcd;
2650 DWORD dwDrawStage,dwItemSpec;
2651 UINT uItemState;
2652 INT retval;
2654 dwDrawStage=CDDS_ITEM | uItemDrawState;
2655 dwItemSpec=(DWORD)wineItem->hItem;
2656 uItemState=0;
2657 if (wineItem->hItem==infoPtr->selectedItem) uItemState|=CDIS_SELECTED;
2658 if (wineItem->hItem==infoPtr->focusItem) uItemState|=CDIS_FOCUS;
2659 if (wineItem->hItem==infoPtr->hotItem) uItemState|=CDIS_HOT;
2661 nmcd= & nmcdhdr.nmcd;
2662 nmcd->hdr.hwndFrom = hwnd;
2663 nmcd->hdr.idFrom = GetWindowLongA( hwnd, GWL_ID);
2664 nmcd->hdr.code = NM_CUSTOMDRAW;
2665 nmcd->dwDrawStage= dwDrawStage;
2666 nmcd->hdc = hdc;
2667 nmcd->rc.left = wineItem->rect.left;
2668 nmcd->rc.right = wineItem->rect.right;
2669 nmcd->rc.bottom = wineItem->rect.bottom;
2670 nmcd->rc.top = wineItem->rect.top;
2671 nmcd->dwItemSpec = dwItemSpec;
2672 nmcd->uItemState = uItemState;
2673 nmcd->lItemlParam= wineItem->lParam;
2674 nmcdhdr.clrText = infoPtr->clrText;
2675 nmcdhdr.clrTextBk= infoPtr->clrBk;
2676 nmcdhdr.iLevel = wineItem->iLevel;
2678 TRACE("drawstage:%lx hdc:%x item:%lx, itemstate:%x, lItemlParam:%lx\n",
2679 nmcd->dwDrawStage, nmcd->hdc, nmcd->dwItemSpec,
2680 nmcd->uItemState, nmcd->lItemlParam);
2682 retval=SendMessageA (GetParent (hwnd), WM_NOTIFY,
2683 (WPARAM) GetWindowLongA( hwnd, GWL_ID), (LPARAM)&nmcdhdr);
2685 infoPtr->clrText=nmcdhdr.clrText;
2686 infoPtr->clrBk =nmcdhdr.clrTextBk;
2687 return (BOOL) retval;
2692 /* Note:If the specified item is the child of a collapsed parent item,
2693 the parent's list of child items is (recursively) expanded to reveal the
2694 specified item. This is mentioned for TREEVIEW_SelectItem; don't
2695 know if it also applies here.
2698 static LRESULT
2699 TREEVIEW_Expand (HWND hwnd, WPARAM wParam, LPARAM lParam)
2701 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
2702 TREEVIEW_ITEM *wineItem;
2703 UINT flag;
2704 INT expand;
2706 flag = (UINT) wParam;
2707 expand = (INT) lParam;
2709 wineItem = TREEVIEW_ValidItem (infoPtr, (HTREEITEM)expand);
2711 if (!wineItem)
2712 return 0;
2713 if (!wineItem->cChildren)
2714 return 0;
2716 if (wineItem->pszText==LPSTR_TEXTCALLBACKA)
2717 TRACE ("For item %d, flags %d, state %d\n",
2718 expand, flag, wineItem->state);
2719 else
2720 TRACE("For (%s) item:%d, flags %x, state:%d\n",
2721 wineItem->pszText, flag, expand, wineItem->state);
2723 if (wineItem->cChildren==I_CHILDRENCALLBACK) {
2724 FIXME("we don't handle I_CHILDRENCALLBACK yet\n");
2725 return 0;
2728 if (flag == TVE_TOGGLE) { /* FIXME: check exact behaviour here */
2729 flag &= ~TVE_TOGGLE; /* ie: bitwise ops or 'case' ops */
2730 if (wineItem->state & TVIS_EXPANDED)
2731 flag |= TVE_COLLAPSE;
2732 else
2733 flag |= TVE_EXPAND;
2736 switch (flag)
2738 case TVE_COLLAPSERESET:
2739 TRACE(" case TVE_COLLAPSERESET\n");
2740 if (!wineItem->state & TVIS_EXPANDED)
2741 return 0;
2743 wineItem->state &= ~(TVIS_EXPANDEDONCE | TVIS_EXPANDED);
2744 TREEVIEW_RemoveAllChildren (hwnd, wineItem);
2745 break;
2747 case TVE_COLLAPSE:
2748 TRACE(" case TVE_COLLAPSE\n");
2749 if (!wineItem->state & TVIS_EXPANDED)
2750 return 0;
2752 wineItem->state &= ~TVIS_EXPANDED;
2753 break;
2755 case TVE_EXPAND:
2756 TRACE(" case TVE_EXPAND\n");
2757 if (wineItem->state & TVIS_EXPANDED)
2758 return 0;
2760 TRACE(" is not expanded...\n");
2762 if (!(wineItem->state & TVIS_EXPANDEDONCE))
2764 TRACE(" and has never been expanded...\n");
2765 wineItem->state |= TVIS_EXPANDED;
2767 /* this item has never been expanded */
2768 if (TREEVIEW_SendTreeviewNotify (
2769 hwnd,
2770 TVN_ITEMEXPANDINGA,
2771 TVE_EXPAND,
2773 (HTREEITEM)expand))
2775 TRACE(" TVN_ITEMEXPANDINGA returned TRUE, exiting...\n");
2776 return FALSE;
2779 /* FIXME
2780 * Since the TVN_ITEMEXPANDINGA message may has caused the parent to
2781 * insert new items which in turn may have cause items placeholder
2782 * reallocation, I reassign the current item pointer so we have
2783 * something valid to work with...
2784 * However, this should not be necessary,
2785 * investigation required in TREEVIEW_InsertItemA
2787 wineItem = TREEVIEW_ValidItem (infoPtr, (HTREEITEM)expand);
2788 if (! wineItem)
2790 ERR(
2791 "Catastropic situation, cannot retreive item #%d\n",
2792 expand);
2793 return FALSE;
2796 wineItem->state |= TVIS_EXPANDEDONCE;
2797 TRACE(" TVN_ITEMEXPANDINGA sent...\n");
2799 TREEVIEW_SendTreeviewNotify (
2800 hwnd,
2801 TVN_ITEMEXPANDEDA,
2802 TVE_EXPAND,
2804 (HTREEITEM)expand);
2806 TRACE(" TVN_ITEMEXPANDEDA sent...\n");
2809 else
2811 /* this item has already been expanded */
2812 wineItem->state |= TVIS_EXPANDED;
2814 break;
2816 case TVE_EXPANDPARTIAL:
2817 TRACE(" case TVE_EXPANDPARTIAL\n");
2818 FIXME("TVE_EXPANDPARTIAL not implemented\n");
2819 wineItem->state ^=TVIS_EXPANDED;
2820 wineItem->state |=TVIS_EXPANDEDONCE;
2821 break;
2824 TRACE("Exiting, Item %d state is now %d...\n",
2825 expand,
2826 wineItem->state);
2828 TREEVIEW_QueueRefresh (hwnd);
2829 return TRUE;
2834 static TREEVIEW_ITEM *
2835 TREEVIEW_HitTestPoint (HWND hwnd, POINT pt)
2837 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
2838 TREEVIEW_ITEM *wineItem;
2839 RECT rect;
2841 GetClientRect (hwnd, &rect);
2843 if (!infoPtr->firstVisible) return NULL;
2845 wineItem=&infoPtr->items [(INT)infoPtr->firstVisible];
2847 while ((wineItem!=NULL) && (pt.y > wineItem->rect.bottom))
2848 wineItem=TREEVIEW_GetNextListItem (infoPtr,wineItem);
2850 if (!wineItem)
2851 return NULL;
2853 return wineItem;
2859 static LRESULT
2860 TREEVIEW_HitTest (HWND hwnd, LPARAM lParam)
2862 LPTVHITTESTINFO lpht=(LPTVHITTESTINFO) lParam;
2863 TREEVIEW_ITEM *wineItem;
2864 RECT rect;
2865 UINT status,x,y;
2867 GetClientRect (hwnd, &rect);
2868 status=0;
2869 x=lpht->pt.x;
2870 y=lpht->pt.y;
2871 if (x < rect.left) status|=TVHT_TOLEFT;
2872 if (x > rect.right) status|=TVHT_TORIGHT;
2873 if (y < rect.top ) status|=TVHT_ABOVE;
2874 if (y > rect.bottom) status|=TVHT_BELOW;
2876 if (status) {
2877 lpht->flags=status;
2878 return 0;
2881 wineItem=TREEVIEW_HitTestPoint (hwnd, lpht->pt);
2882 if (!wineItem) {
2883 lpht->flags=TVHT_NOWHERE;
2884 return 0;
2887 lpht->flags=0;
2889 if (x < wineItem->expandBox.left) {
2890 lpht->flags |= TVHT_ONITEMINDENT;
2891 goto done;
2893 if ( PtInRect ( &wineItem->expandBox, lpht->pt)) {
2894 lpht->flags |= TVHT_ONITEMBUTTON;
2895 goto done;
2897 if ( PtInRect ( &wineItem->bitmap, lpht->pt)) {
2898 lpht->flags |= TVHT_ONITEMICON;
2899 goto done;
2901 if ( PtInRect ( &wineItem->statebitmap, lpht->pt)) {
2902 lpht->flags |= TVHT_ONITEMSTATEICON;
2903 goto done;
2905 if ( PtInRect ( &wineItem->text, lpht->pt)) {
2906 lpht->flags |= TVHT_ONITEMLABEL;
2907 goto done;
2910 lpht->flags|=TVHT_ONITEMRIGHT;
2913 done:
2914 lpht->hItem=wineItem->hItem;
2915 TRACE ("(%ld,%ld):result %x\n",lpht->pt.x,lpht->pt.y,lpht->flags);
2917 return (LRESULT) wineItem->hItem;
2920 LRESULT WINAPI
2921 TREEVIEW_EndEditLabelNow (HWND hwnd, WPARAM wParam, LPARAM lParam)
2923 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
2924 TREEVIEW_ITEM *editedItem = TREEVIEW_ValidItem (infoPtr, infoPtr->editItem);
2925 BOOL bRevert = (BOOL)wParam;
2926 BOOL bReturn = ! bRevert;
2928 if ( ! (BOOL)wParam ) /* wParam is set to true to cancel the edition */
2930 if ( TREEVIEW_SendDispInfoNotify( /* return true to cancel edition */
2931 hwnd,
2932 editedItem,
2933 TVN_ENDLABELEDITA,
2936 bRevert = TRUE;
2937 bReturn = FALSE;
2941 if (bRevert == FALSE) /* Apply the changes */
2943 char tmpText[1024];
2944 int iLength = GetWindowTextA(infoPtr->hwndEdit, tmpText, 1023);
2945 bReturn = FALSE;
2947 if (iLength == 0)
2949 ERR("Problem retreiving new item label.");
2951 else if (iLength >= 1023)
2953 ERR(
2954 "Insuficient space to retrieve new item label, new label ignored.");
2956 else
2958 if (strcmp( tmpText, editedItem->pszText ) == 0)
2959 /* Do nothing if the label has not changed */
2960 bReturn = TRUE;
2961 else
2963 LPSTR tmpLabel = COMCTL32_Alloc( iLength+1 );
2965 if ( tmpLabel == NULL )
2966 ERR(
2967 "OutOfMemory, cannot allocate space for label");
2968 else
2970 COMCTL32_Free(editedItem->pszText);
2971 editedItem->pszText = tmpLabel;
2972 lstrcpyA( editedItem->pszText, tmpText);
2973 bReturn = TRUE;
2978 ShowWindow(infoPtr->hwndEdit, SW_HIDE);
2979 EnableWindow(infoPtr->hwndEdit, FALSE);
2980 infoPtr->editItem = 0;
2983 return bReturn;
2988 static LRESULT
2989 TREEVIEW_LButtonDoubleClick (HWND hwnd, WPARAM wParam, LPARAM lParam)
2991 TREEVIEW_ITEM *wineItem;
2992 POINT pt;
2994 TRACE("\n");
2995 pt.x = (INT)LOWORD(lParam);
2996 pt.y = (INT)HIWORD(lParam);
2997 SetFocus (hwnd);
2999 wineItem=TREEVIEW_HitTestPoint (hwnd, pt);
3000 if (!wineItem) return 0;
3001 TRACE("item %d \n",(INT)wineItem->hItem);
3003 if (TREEVIEW_SendSimpleNotify (hwnd, NM_DBLCLK)!=TRUE) { /* FIXME!*/
3004 TREEVIEW_Expand (hwnd, (WPARAM) TVE_TOGGLE, (LPARAM) wineItem->hItem);
3006 return TRUE;
3010 static LRESULT
3011 TREEVIEW_LButtonDown (HWND hwnd, WPARAM wParam, LPARAM lParam)
3013 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
3014 INT iItem;
3015 TVHITTESTINFO ht;
3017 ht.pt.x = (INT)LOWORD(lParam);
3018 ht.pt.y = (INT)HIWORD(lParam);
3020 SetFocus (hwnd);
3021 iItem=TREEVIEW_HitTest (hwnd, (LPARAM) &ht);
3022 TRACE("item %d \n",iItem);
3024 if (ht.flags & TVHT_ONITEMBUTTON) {
3025 TREEVIEW_Expand (hwnd, (WPARAM) TVE_TOGGLE, (LPARAM) iItem);
3027 else
3029 infoPtr->uInternalStatus|=TV_LDRAG;
3032 return 0;
3035 static LRESULT
3036 TREEVIEW_LButtonUp (HWND hwnd, WPARAM wParam, LPARAM lParam)
3038 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
3039 INT iItem;
3040 TREEVIEW_ITEM *wineItem;
3041 TVHITTESTINFO ht;
3043 ht.pt.x = (INT)LOWORD(lParam);
3044 ht.pt.y = (INT)HIWORD(lParam);
3046 TRACE("\n");
3048 /* Return true to cancel default behaviour */
3049 if ( TREEVIEW_SendSimpleNotify (hwnd, NM_CLICK) )
3050 return 0;
3052 /* Get the item */
3053 iItem = TREEVIEW_HitTest (hwnd, (LPARAM) &ht);
3054 TRACE ("%d\n",iItem);
3055 if (!iItem)
3056 return 0;
3058 wineItem = TREEVIEW_ValidItem(infoPtr, (HTREEITEM)iItem);
3060 infoPtr->uInternalStatus &= ~(TV_LDRAG | TV_LDRAGGING);
3063 * If the style allow editing and the node is already selected
3064 * and the click occured on the item label...
3066 if ( ( GetWindowLongA( hwnd, GWL_STYLE) & TVS_EDITLABELS ) &&
3067 ( wineItem->state & TVIS_SELECTED ) &&
3068 ( ht.flags & TVHT_ONITEMLABEL ))
3070 if ( infoPtr->editItem == 0 ) /* If we are not curently editing */
3072 if ( TREEVIEW_SendDispInfoNotify( /* Return true to cancel edition */
3073 hwnd,
3074 wineItem,
3075 TVN_BEGINLABELEDITA,
3078 return 0;
3081 TRACE("Edit started for %s.\n", wineItem->pszText);
3082 infoPtr->editItem = wineItem->hItem;
3084 SetWindowPos (
3085 infoPtr->hwndEdit,
3086 HWND_TOP,
3087 wineItem->text.left - 2,
3088 wineItem->text.top - 1,
3089 wineItem->text.right - wineItem->text.left + 20 ,
3090 wineItem->text.bottom - wineItem->text.top + 3,
3091 SWP_DRAWFRAME );
3093 SetWindowTextA( infoPtr->hwndEdit, wineItem->pszText );
3094 SendMessageA ( infoPtr->hwndEdit, EM_SETSEL, 0, -1 );
3095 SetFocus ( infoPtr->hwndEdit);
3096 ShowWindow ( infoPtr->hwndEdit, SW_SHOW);
3099 else if ( infoPtr->editItem != 0 ) /* If we are curently editing */
3101 TREEVIEW_EndEditLabelNow(hwnd, (WPARAM)FALSE, 0);
3103 else if ( ht.flags & (TVHT_ONITEMLABEL | TVHT_ONITEMICON))
3105 TREEVIEW_DoSelectItem ( hwnd, TVGN_CARET, (HTREEITEM)iItem, TVC_BYMOUSE);
3108 if (ht.flags & TVHT_ONITEMSTATEICON) {
3109 DWORD dwStyle = GetWindowLongA (hwnd, GWL_STYLE);
3112 if (dwStyle & TVS_CHECKBOXES) { /* TVS_CHECKBOXES requires _us_ */
3113 int state; /* to toggle the current state */
3114 state=1-(wineItem->state>>12);
3115 TRACE ("state:%x\n", state);
3116 wineItem->state&= ~TVIS_STATEIMAGEMASK;
3117 wineItem->state|=state<<12;
3118 TRACE ("state:%x\n", wineItem->state);
3119 TREEVIEW_QueueRefresh (hwnd);
3122 return 0;
3126 static LRESULT
3127 TREEVIEW_RButtonDown (HWND hwnd, WPARAM wParam, LPARAM lParam)
3129 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
3131 TRACE("\n");
3132 infoPtr->uInternalStatus|=TV_RDRAG;
3133 return 0;
3136 static LRESULT
3137 TREEVIEW_RButtonUp (HWND hwnd, WPARAM wParam, LPARAM lParam)
3139 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
3141 TRACE("\n");
3142 if (TREEVIEW_SendSimpleNotify (hwnd, NM_RCLICK)) return 0;
3143 infoPtr->uInternalStatus&= ~(TV_RDRAG | TV_RDRAGGING);
3144 return 0;
3148 static LRESULT
3149 TREEVIEW_MouseMove (HWND hwnd, WPARAM wParam, LPARAM lParam)
3151 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
3152 TREEVIEW_ITEM *hotItem;
3153 POINT pt;
3155 pt.x=(INT) LOWORD (lParam);
3156 pt.y=(INT) HIWORD (lParam);
3157 hotItem=TREEVIEW_HitTestPoint (hwnd, pt);
3158 if (!hotItem) return 0;
3159 infoPtr->focusItem=hotItem->hItem;
3161 if ( GetWindowLongA( hwnd, GWL_STYLE) & TVS_DISABLEDRAGDROP) return 0;
3163 if (infoPtr->uInternalStatus & TV_LDRAG) {
3164 TREEVIEW_SendTreeviewDnDNotify (hwnd, TVN_BEGINDRAGA, hotItem->hItem, pt);
3165 infoPtr->uInternalStatus &= ~TV_LDRAG;
3166 infoPtr->uInternalStatus |= TV_LDRAGGING;
3167 infoPtr->dropItem=hotItem->hItem;
3168 return 0;
3171 if (infoPtr->uInternalStatus & TV_RDRAG) {
3172 TREEVIEW_SendTreeviewDnDNotify (hwnd, TVN_BEGINRDRAGA, hotItem->hItem, pt);
3173 infoPtr->uInternalStatus &= ~TV_RDRAG;
3174 infoPtr->uInternalStatus |= TV_RDRAGGING;
3175 infoPtr->dropItem=hotItem->hItem;
3176 return 0;
3179 return 0;
3183 static LRESULT
3184 TREEVIEW_CreateDragImage (HWND hwnd, WPARAM wParam, LPARAM lParam)
3186 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
3187 TREEVIEW_ITEM *dragItem;
3188 INT cx,cy;
3189 HDC hdc,htopdc;
3190 HWND hwtop;
3191 HBITMAP hbmp,hOldbmp;
3192 SIZE size;
3193 RECT rc;
3194 HFONT hOldFont;
3195 char *itemtxt;
3197 TRACE("\n");
3198 if (!(infoPtr->himlNormal)) return 0;
3199 dragItem=TREEVIEW_ValidItem (infoPtr, (HTREEITEM) lParam);
3201 if (!dragItem) return 0;
3203 if (dragItem->pszText==LPSTR_TEXTCALLBACKA) {
3204 TREEVIEW_SendDispInfoNotify (hwnd, dragItem, TVN_GETDISPINFOA, TVIF_TEXT);
3206 itemtxt=dragItem->pszText;
3208 hwtop=GetDesktopWindow ();
3209 htopdc= GetDC (hwtop);
3210 hdc=CreateCompatibleDC (htopdc);
3212 hOldFont=SelectObject (hdc, infoPtr->hFont);
3213 GetTextExtentPoint32A (hdc, itemtxt, lstrlenA (itemtxt), &size);
3214 TRACE("%d %d %s %d\n",size.cx,size.cy,itemtxt,lstrlenA(itemtxt));
3215 hbmp=CreateCompatibleBitmap (htopdc, size.cx, size.cy);
3216 hOldbmp=SelectObject (hdc, hbmp);
3218 ImageList_GetIconSize (infoPtr->himlNormal, &cx, &cy);
3219 size.cx+=cx;
3220 if (cy>size.cy) size.cy=cy;
3222 infoPtr->dragList=ImageList_Create (size.cx, size.cy, ILC_COLOR, 10, 10);
3223 ImageList_Draw (infoPtr->himlNormal, dragItem->iImage, hdc, 0, 0, ILD_NORMAL);
3226 ImageList_GetImageInfo (infoPtr->himlNormal, dragItem->hItem, &iminfo);
3227 ImageList_AddMasked (infoPtr->dragList, iminfo.hbmImage, CLR_DEFAULT);
3230 /* draw item text */
3232 SetRect (&rc, cx, 0, size.cx,size.cy);
3233 DrawTextA (hdc, itemtxt, lstrlenA (itemtxt), &rc, DT_LEFT);
3234 SelectObject (hdc, hOldFont);
3235 SelectObject (hdc, hOldbmp);
3237 ImageList_Add (infoPtr->dragList, hbmp, 0);
3239 DeleteDC (hdc);
3240 DeleteObject (hbmp);
3241 ReleaseDC (hwtop, htopdc);
3243 return (LRESULT)infoPtr->dragList;
3247 static LRESULT
3248 TREEVIEW_DoSelectItem (HWND hwnd, INT action, HTREEITEM newSelect, INT cause)
3251 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
3252 TREEVIEW_ITEM *prevItem,*wineItem;
3253 INT prevSelect;
3255 wineItem = TREEVIEW_ValidItem (infoPtr, (HTREEITEM)newSelect);
3257 TRACE("Entering item %d, flag %x, cause %x, state %d\n",
3258 (INT)newSelect,
3259 action,
3260 cause,
3261 wineItem->state);
3263 if ( (wineItem) && (wineItem->parent))
3266 * If the item has a collapse parent expand the parent so he
3267 * can expose the item
3269 TREEVIEW_ITEM *parentItem = TREEVIEW_ValidItem (infoPtr, wineItem->parent);
3270 if ( !(parentItem->state & TVIS_EXPANDED))
3271 TREEVIEW_Expand (hwnd, TVE_EXPAND, (LPARAM) wineItem->parent);
3274 switch (action)
3276 case TVGN_CARET:
3277 prevSelect=(INT)infoPtr->selectedItem;
3279 if ((HTREEITEM)prevSelect==newSelect)
3280 return FALSE;
3282 prevItem= TREEVIEW_ValidItem (infoPtr, (HTREEITEM)prevSelect);
3284 if (newSelect)
3285 if (TREEVIEW_SendTreeviewNotify(
3286 hwnd,
3287 TVN_SELCHANGINGA,
3288 cause,
3289 (HTREEITEM)prevSelect,
3290 (HTREEITEM)newSelect))
3291 return FALSE; /* FIXME: OK? */
3293 if (prevItem)
3294 prevItem->state &= ~TVIS_SELECTED;
3295 if (wineItem)
3296 wineItem->state |= TVIS_SELECTED;
3298 infoPtr->selectedItem=(HTREEITEM)newSelect;
3300 TREEVIEW_SendTreeviewNotify(
3301 hwnd,
3302 TVN_SELCHANGEDA,
3303 cause,
3304 (HTREEITEM)prevSelect,
3305 (HTREEITEM)newSelect);
3307 break;
3309 case TVGN_DROPHILITE:
3310 prevItem= TREEVIEW_ValidItem (infoPtr, infoPtr->dropItem);
3312 if (prevItem)
3313 prevItem->state &= ~TVIS_DROPHILITED;
3315 infoPtr->dropItem=(HTREEITEM)newSelect;
3317 if (wineItem)
3318 wineItem->state |=TVIS_DROPHILITED;
3320 break;
3322 case TVGN_FIRSTVISIBLE:
3323 FIXME("FIRSTVISIBLE not implemented\n");
3324 break;
3327 TREEVIEW_QueueRefresh (hwnd);
3329 TRACE("Leaving state %d\n", wineItem->state);
3330 return TRUE;
3333 /* FIXME: handle NM_KILLFocus etc */
3334 static LRESULT
3335 TREEVIEW_SelectItem (HWND hwnd, WPARAM wParam, LPARAM lParam)
3338 return TREEVIEW_DoSelectItem (hwnd, wParam, (HTREEITEM) lParam, TVC_UNKNOWN);
3344 static LRESULT
3345 TREEVIEW_GetFont (HWND hwnd, WPARAM wParam, LPARAM lParam)
3348 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
3350 TRACE("%x\n",infoPtr->hFont);
3351 return infoPtr->hFont;
3354 static LRESULT
3355 TREEVIEW_SetFont (HWND hwnd, WPARAM wParam, LPARAM lParam)
3358 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
3359 TEXTMETRICA tm;
3360 LOGFONTA logFont;
3361 HFONT hFont, hOldFont;
3362 INT height;
3363 HDC hdc;
3365 TRACE("%x %lx\n",wParam, lParam);
3367 infoPtr->hFont = (HFONT)wParam;
3369 hFont = infoPtr->hFont ? infoPtr->hFont : GetStockObject (SYSTEM_FONT);
3371 GetObjectA (infoPtr->hFont, sizeof (LOGFONTA), &logFont);
3372 logFont.lfWeight=FW_BOLD;
3373 infoPtr->hBoldFont = CreateFontIndirectA (&logFont);
3375 hdc = GetDC (0);
3376 hOldFont = SelectObject (hdc, hFont);
3377 GetTextMetricsA (hdc, &tm);
3378 height= tm.tmHeight + tm.tmExternalLeading;
3379 if (height>infoPtr->uRealItemHeight)
3380 infoPtr->uRealItemHeight=height;
3381 SelectObject (hdc, hOldFont);
3382 ReleaseDC (0, hdc);
3384 if (lParam)
3385 TREEVIEW_QueueRefresh (hwnd);
3387 return 0;
3392 static LRESULT
3393 TREEVIEW_VScroll (HWND hwnd, WPARAM wParam, LPARAM lParam)
3396 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
3397 int maxHeight;
3399 TRACE("wp %x, lp %lx\n", wParam, lParam);
3400 if (!infoPtr->uInternalStatus & TV_VSCROLL) return FALSE;
3402 switch (LOWORD (wParam)) {
3403 case SB_LINEUP:
3404 if (!infoPtr->cy) return FALSE;
3405 infoPtr->cy -= infoPtr->uRealItemHeight;
3406 if (infoPtr->cy < 0) infoPtr->cy=0;
3407 break;
3408 case SB_LINEDOWN:
3409 maxHeight=infoPtr->uTotalHeight-infoPtr->uVisibleHeight;
3410 if (infoPtr->cy == maxHeight) return FALSE;
3411 infoPtr->cy += infoPtr->uRealItemHeight;
3412 if (infoPtr->cy > maxHeight)
3413 infoPtr->cy = maxHeight;
3414 break;
3415 case SB_PAGEUP:
3416 if (!infoPtr->cy) return FALSE;
3417 infoPtr->cy -= infoPtr->uVisibleHeight;
3418 if (infoPtr->cy < 0) infoPtr->cy=0;
3419 break;
3420 case SB_PAGEDOWN:
3421 maxHeight=infoPtr->uTotalHeight-infoPtr->uVisibleHeight;
3422 if (infoPtr->cy == maxHeight) return FALSE;
3423 infoPtr->cy += infoPtr->uVisibleHeight;
3424 if (infoPtr->cy > maxHeight)
3425 infoPtr->cy = maxHeight;
3426 break;
3427 case SB_THUMBTRACK:
3428 infoPtr->cy = HIWORD (wParam);
3429 break;
3433 TREEVIEW_QueueRefresh (hwnd);
3434 return TRUE;
3437 static LRESULT
3438 TREEVIEW_HScroll (HWND hwnd, WPARAM wParam, LPARAM lParam)
3440 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
3441 int maxWidth;
3443 TRACE("wp %lx, lp %x\n", lParam, wParam);
3445 if (!infoPtr->uInternalStatus & TV_HSCROLL) return FALSE;
3447 switch (LOWORD (wParam)) {
3448 case SB_LINEUP:
3449 if (!infoPtr->cx) return FALSE;
3450 infoPtr->cx -= infoPtr->uRealItemHeight;
3451 if (infoPtr->cx < 0) infoPtr->cx=0;
3452 break;
3453 case SB_LINEDOWN:
3454 maxWidth=infoPtr->uTotalWidth-infoPtr->uVisibleWidth;
3455 if (infoPtr->cx == maxWidth) return FALSE;
3456 infoPtr->cx += infoPtr->uRealItemHeight; /*FIXME */
3457 if (infoPtr->cx > maxWidth)
3458 infoPtr->cx = maxWidth;
3459 break;
3460 case SB_PAGEUP:
3461 if (!infoPtr->cx) return FALSE;
3462 infoPtr->cx -= infoPtr->uVisibleWidth;
3463 if (infoPtr->cx < 0) infoPtr->cx=0;
3464 break;
3465 case SB_PAGEDOWN:
3466 maxWidth=infoPtr->uTotalWidth-infoPtr->uVisibleWidth;
3467 if (infoPtr->cx == maxWidth) return FALSE;
3468 infoPtr->cx += infoPtr->uVisibleWidth;
3469 if (infoPtr->cx > maxWidth)
3470 infoPtr->cx = maxWidth;
3471 break;
3472 case SB_THUMBTRACK:
3473 infoPtr->cx = HIWORD (wParam);
3474 break;
3478 TREEVIEW_QueueRefresh (hwnd);
3479 return TRUE;
3482 static LRESULT TREEVIEW_MouseWheel (HWND hwnd, WPARAM wParam, LPARAM lParam)
3485 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
3486 short gcWheelDelta = 0;
3487 UINT pulScrollLines = 3;
3489 SystemParametersInfoW(SPI_GETWHEELSCROLLLINES,0, &pulScrollLines, 0);
3491 gcWheelDelta -= (short) HIWORD(wParam);
3492 pulScrollLines *= (gcWheelDelta / WHEEL_DELTA);
3494 if (abs(gcWheelDelta) >= WHEEL_DELTA && pulScrollLines)
3496 int wheelDy = pulScrollLines * infoPtr->uRealItemHeight;
3497 int newDy = infoPtr->cy + wheelDy;
3498 int maxDy = infoPtr->uTotalHeight - infoPtr->uVisibleHeight;
3500 if (newDy > maxDy) newDy = maxDy;
3501 if (newDy < 0) newDy = 0;
3503 if (newDy == infoPtr->cy) return TRUE;
3505 TREEVIEW_VScroll(hwnd, MAKEWPARAM(SB_THUMBTRACK,newDy),0);
3507 return TRUE;
3510 static LRESULT
3511 TREEVIEW_KeyDown (HWND hwnd, WPARAM wParam, LPARAM lParam)
3513 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
3514 HTREEITEM hNewSelection = 0;
3515 INT scrollNeeds = -1;
3516 INT cyChangeNeeds = -1;
3517 INT prevSelect = (INT)infoPtr->selectedItem;
3519 TREEVIEW_ITEM *prevItem =
3520 (prevSelect != 0 ) ?
3521 TREEVIEW_ValidItem (infoPtr, (HTREEITEM)prevSelect) :
3522 NULL;
3524 TREEVIEW_ITEM *newItem = NULL;
3526 TRACE("%x %lx\n",wParam, lParam);
3528 if (prevSelect == 0)
3529 return FALSE;
3531 switch (wParam) {
3532 case VK_UP:
3533 newItem=TREEVIEW_GetPrevListItem (infoPtr, prevItem);
3535 if (!newItem)
3536 newItem=& infoPtr->items[(INT)infoPtr->TopRootItem];
3538 hNewSelection = newItem->hItem;
3540 if (! newItem->visible)
3541 scrollNeeds = SB_LINEUP;
3542 break;
3544 case VK_DOWN:
3545 newItem=TREEVIEW_GetNextListItem (infoPtr, prevItem);
3547 if (!newItem)
3548 newItem=prevItem;
3550 hNewSelection = newItem->hItem;
3552 if (! newItem->visible)
3553 scrollNeeds = SB_LINEDOWN;
3555 break;
3557 case VK_HOME:
3558 newItem = &infoPtr->items[(INT)infoPtr->TopRootItem];
3559 hNewSelection = newItem->hItem;
3560 cyChangeNeeds = 0;
3561 break;
3563 case VK_END:
3564 newItem = &infoPtr->items[(INT)infoPtr->TopRootItem];
3565 newItem = TREEVIEW_GetLastListItem (infoPtr, newItem);
3566 hNewSelection = newItem->hItem;
3568 if (! newItem->visible)
3569 cyChangeNeeds = infoPtr->uTotalHeight-infoPtr->uVisibleHeight;
3571 break;
3573 case VK_LEFT:
3574 if ( (prevItem->cChildren > 0) && (prevItem->state & TVIS_EXPANDED) )
3576 TREEVIEW_Expand(hwnd, TVE_COLLAPSE, prevSelect );
3578 else if ((INT)prevItem->parent)
3580 newItem = (& infoPtr->items[(INT)prevItem->parent]);
3581 if (! newItem->visible)
3582 /* FIXME find a way to make this item the first visible... */
3583 newItem = NULL;
3585 hNewSelection = newItem->hItem;
3588 break;
3590 case VK_RIGHT:
3591 if ( ( prevItem->cChildren > 0) ||
3592 ( prevItem->cChildren == I_CHILDRENCALLBACK))
3594 if (! (prevItem->state & TVIS_EXPANDED))
3595 TREEVIEW_Expand(hwnd, TVE_EXPAND, prevSelect );
3596 else
3598 newItem = (& infoPtr->items[(INT)prevItem->firstChild]);
3599 hNewSelection = newItem->hItem;
3603 break;
3605 case VK_ADD:
3606 if (! (prevItem->state & TVIS_EXPANDED))
3607 TREEVIEW_Expand(hwnd, TVE_EXPAND, prevSelect );
3608 break;
3610 case VK_SUBTRACT:
3611 if (prevItem->state & TVIS_EXPANDED)
3612 TREEVIEW_Expand(hwnd, TVE_COLLAPSE, prevSelect );
3613 break;
3615 case VK_PRIOR:
3617 newItem=TREEVIEW_GetListItem(
3618 infoPtr,
3619 prevItem,
3620 -1*(TREEVIEW_GetVisibleCount(hwnd,0,0)-3));
3621 if (!newItem)
3622 newItem=prevItem;
3624 hNewSelection = newItem->hItem;
3626 if (! newItem->visible)
3627 scrollNeeds = SB_PAGEUP;
3629 break;
3631 case VK_NEXT:
3632 newItem=TREEVIEW_GetListItem(
3633 infoPtr,
3634 prevItem,
3635 TREEVIEW_GetVisibleCount(hwnd,0,0)-3);
3637 if (!newItem)
3638 newItem=prevItem;
3640 hNewSelection = newItem->hItem;
3642 if (! newItem->visible)
3643 scrollNeeds = SB_PAGEDOWN;
3645 break;
3647 case VK_BACK:
3649 case VK_RETURN:
3651 default:
3652 FIXME("%x not implemented\n", wParam);
3653 break;
3656 if (hNewSelection)
3659 This works but does not send notification...
3661 prevItem->state &= ~TVIS_SELECTED;
3662 newItem->state |= TVIS_SELECTED;
3663 infoPtr->selectedItem = hNewSelection;
3664 TREEVIEW_QueueRefresh (hwnd);
3667 if ( TREEVIEW_DoSelectItem(
3668 hwnd,
3669 TVGN_CARET,
3670 (HTREEITEM)hNewSelection,
3671 TVC_BYKEYBOARD))
3673 /* If selection change is allowed for the new item, perform scrolling */
3674 if (scrollNeeds != -1)
3675 TREEVIEW_VScroll(hwnd, scrollNeeds, 0);
3677 if (cyChangeNeeds != -1)
3678 infoPtr->cy = cyChangeNeeds;
3680 /* FIXME: Something happen in the load the in the two weeks before
3681 april 1st 1999 which makes this SetFocus mandatory otherwise, the focus
3682 is lost... However the SetFocus should not be required...*/
3684 SetFocus(hwnd);
3688 return FALSE;
3692 static LRESULT
3693 TREEVIEW_GetScrollTime (HWND hwnd)
3695 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
3697 return infoPtr->uScrollTime;
3701 static LRESULT
3702 TREEVIEW_SetScrollTime (HWND hwnd, UINT uScrollTime)
3704 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
3705 UINT uOldScrollTime = infoPtr->uScrollTime;
3707 infoPtr->uScrollTime = min (uScrollTime, 100);
3709 return uOldScrollTime;
3713 static LRESULT WINAPI
3714 TREEVIEW_WindowProc (HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
3716 if (uMsg==WM_CREATE)
3717 return TREEVIEW_Create (hwnd, wParam, lParam);
3719 if (!TREEVIEW_GetInfoPtr(hwnd))
3720 return DefWindowProcA (hwnd, uMsg, wParam, lParam);
3722 switch (uMsg) {
3724 case TVM_INSERTITEMA:
3725 return TREEVIEW_InsertItemA (hwnd, wParam, lParam);
3727 case TVM_INSERTITEMW:
3728 return TREEVIEW_InsertItemW(hwnd,wParam,lParam);;
3730 case TVM_DELETEITEM:
3731 return TREEVIEW_DeleteItem (hwnd, wParam, lParam);
3733 case TVM_EXPAND:
3734 return TREEVIEW_Expand (hwnd, wParam, lParam);
3736 case TVM_GETITEMRECT:
3737 return TREEVIEW_GetItemRect (hwnd, wParam, lParam);
3739 case TVM_GETCOUNT:
3740 return TREEVIEW_GetCount (hwnd, wParam, lParam);
3742 case TVM_GETINDENT:
3743 return TREEVIEW_GetIndent (hwnd);
3745 case TVM_SETINDENT:
3746 return TREEVIEW_SetIndent (hwnd, wParam);
3748 case TVM_GETIMAGELIST:
3749 return TREEVIEW_GetImageList (hwnd, wParam, lParam);
3751 case TVM_SETIMAGELIST:
3752 return TREEVIEW_SetImageList (hwnd, wParam, lParam);
3754 case TVM_GETNEXTITEM:
3755 return TREEVIEW_GetNextItem (hwnd, wParam, lParam);
3757 case TVM_SELECTITEM:
3758 return TREEVIEW_SelectItem (hwnd, wParam, lParam);
3760 case TVM_GETITEMA:
3761 return TREEVIEW_GetItemA (hwnd, wParam, lParam);
3763 case TVM_GETITEMW:
3764 FIXME("Unimplemented msg TVM_GETITEMW\n");
3765 return 0;
3767 case TVM_SETITEMA:
3768 return TREEVIEW_SetItemA (hwnd, wParam, lParam);
3770 case TVM_SETITEMW:
3771 FIXME("Unimplemented msg TVM_SETITEMW\n");
3772 return 0;
3774 case TVM_EDITLABELA:
3775 FIXME("Unimplemented msg TVM_EDITLABELA \n");
3776 return 0;
3778 case TVM_EDITLABELW:
3779 FIXME("Unimplemented msg TVM_EDITLABELW \n");
3780 return 0;
3782 case TVM_GETEDITCONTROL:
3783 return TREEVIEW_GetEditControl (hwnd);
3785 case TVM_GETVISIBLECOUNT:
3786 return TREEVIEW_GetVisibleCount (hwnd, wParam, lParam);
3788 case TVM_HITTEST:
3789 return TREEVIEW_HitTest (hwnd, lParam);
3791 case TVM_CREATEDRAGIMAGE:
3792 return TREEVIEW_CreateDragImage (hwnd, wParam, lParam);
3794 case TVM_SORTCHILDREN:
3795 return TREEVIEW_SortChildren (hwnd, wParam, lParam);
3797 case TVM_ENSUREVISIBLE:
3798 FIXME("Unimplemented msg TVM_ENSUREVISIBLE\n");
3799 return 0;
3801 case TVM_SORTCHILDRENCB:
3802 return TREEVIEW_SortChildrenCB(hwnd, wParam, lParam);
3804 case TVM_ENDEDITLABELNOW:
3805 return TREEVIEW_EndEditLabelNow (hwnd, wParam, lParam);
3807 case TVM_GETISEARCHSTRINGA:
3808 FIXME("Unimplemented msg TVM_GETISEARCHSTRINGA\n");
3809 return 0;
3811 case TVM_GETISEARCHSTRINGW:
3812 FIXME("Unimplemented msg TVM_GETISEARCHSTRINGW\n");
3813 return 0;
3815 case TVM_GETTOOLTIPS:
3816 return TREEVIEW_GetToolTips (hwnd);
3818 case TVM_SETTOOLTIPS:
3819 return TREEVIEW_SetToolTips (hwnd, wParam);
3821 case TVM_SETINSERTMARK:
3822 return TREEVIEW_SetInsertMark (hwnd,wParam, lParam);
3824 case TVM_SETITEMHEIGHT:
3825 return TREEVIEW_SetItemHeight (hwnd, wParam);
3827 case TVM_GETITEMHEIGHT:
3828 return TREEVIEW_GetItemHeight (hwnd);
3830 case TVM_SETBKCOLOR:
3831 return TREEVIEW_SetBkColor (hwnd, wParam, lParam);
3833 case TVM_SETTEXTCOLOR:
3834 return TREEVIEW_SetTextColor (hwnd, wParam, lParam);
3836 case TVM_GETBKCOLOR:
3837 return TREEVIEW_GetBkColor (hwnd);
3839 case TVM_GETTEXTCOLOR:
3840 return TREEVIEW_GetTextColor (hwnd);
3842 case TVM_SETSCROLLTIME:
3843 return TREEVIEW_SetScrollTime (hwnd, (UINT)wParam);
3845 case TVM_GETSCROLLTIME:
3846 return TREEVIEW_GetScrollTime (hwnd);
3848 case TVM_GETITEMSTATE:
3849 return TREEVIEW_GetItemState (hwnd,wParam, lParam);
3851 case TVM_GETLINECOLOR:
3852 return TREEVIEW_GetLineColor (hwnd,wParam, lParam);
3854 case TVM_SETLINECOLOR:
3855 return TREEVIEW_SetLineColor (hwnd,wParam, lParam);
3857 case TVM_SETINSERTMARKCOLOR:
3858 return TREEVIEW_SetInsertMarkColor (hwnd,wParam, lParam);
3860 case TVM_GETINSERTMARKCOLOR:
3861 return TREEVIEW_GetInsertMarkColor (hwnd,wParam, lParam);
3863 case TVM_SETUNICODEFORMAT:
3864 FIXME("Unimplemented msg TVM_SETUNICODEFORMAT\n");
3865 return 0;
3867 case TVM_GETUNICODEFORMAT:
3868 FIXME("Unimplemented msg TVM_GETUNICODEFORMAT\n");
3869 return 0;
3871 case WM_COMMAND:
3872 return TREEVIEW_Command (hwnd, wParam, lParam);
3874 case WM_DESTROY:
3875 return TREEVIEW_Destroy (hwnd);
3877 /* case WM_ENABLE: */
3879 case WM_ERASEBKGND:
3880 return TREEVIEW_EraseBackground (hwnd, wParam, lParam);
3882 case WM_GETDLGCODE:
3883 return DLGC_WANTARROWS | DLGC_WANTCHARS;
3885 case WM_PAINT:
3886 return TREEVIEW_Paint (hwnd, wParam, lParam);
3888 case WM_GETFONT:
3889 return TREEVIEW_GetFont (hwnd, wParam, lParam);
3891 case WM_SETFONT:
3892 return TREEVIEW_SetFont (hwnd, wParam, lParam);
3894 case WM_KEYDOWN:
3895 return TREEVIEW_KeyDown (hwnd, wParam, lParam);
3897 case WM_SETFOCUS:
3898 return TREEVIEW_SetFocus (hwnd, wParam, lParam);
3900 case WM_KILLFOCUS:
3901 return TREEVIEW_KillFocus (hwnd, wParam, lParam);
3903 case WM_LBUTTONDOWN:
3904 return TREEVIEW_LButtonDown (hwnd, wParam, lParam);
3906 case WM_LBUTTONUP:
3907 return TREEVIEW_LButtonUp (hwnd, wParam, lParam);
3909 case WM_LBUTTONDBLCLK:
3910 return TREEVIEW_LButtonDoubleClick (hwnd, wParam, lParam);
3912 case WM_RBUTTONDOWN:
3913 return TREEVIEW_RButtonDown (hwnd, wParam, lParam);
3915 case WM_RBUTTONUP:
3916 return TREEVIEW_RButtonUp (hwnd, wParam, lParam);
3918 case WM_MOUSEMOVE:
3919 return TREEVIEW_MouseMove (hwnd, wParam, lParam);
3921 case WM_STYLECHANGED:
3922 return TREEVIEW_StyleChanged (hwnd, wParam, lParam);
3924 /* case WM_SYSCOLORCHANGE: */
3925 /* case WM_SETREDRAW: */
3927 case WM_TIMER:
3928 return TREEVIEW_HandleTimer (hwnd, wParam, lParam);
3930 case WM_SIZE:
3931 return TREEVIEW_Size (hwnd, wParam,lParam);
3933 case WM_HSCROLL:
3934 return TREEVIEW_HScroll (hwnd, wParam, lParam);
3935 case WM_VSCROLL:
3936 return TREEVIEW_VScroll (hwnd, wParam, lParam);
3938 case WM_MOUSEWHEEL:
3939 if (wParam & (MK_SHIFT | MK_CONTROL))
3940 return DefWindowProcA( hwnd, uMsg, wParam, lParam );
3941 return TREEVIEW_MouseWheel (hwnd, wParam, lParam);
3943 case WM_DRAWITEM:
3944 TRACE ("drawItem\n");
3945 return DefWindowProcA (hwnd, uMsg, wParam, lParam);
3947 default:
3948 if (uMsg >= WM_USER)
3949 FIXME("Unknown msg %04x wp=%08x lp=%08lx\n",
3950 uMsg, wParam, lParam);
3951 return DefWindowProcA (hwnd, uMsg, wParam, lParam);
3953 return 0;
3957 VOID
3958 TREEVIEW_Register (void)
3960 WNDCLASSA wndClass;
3962 TRACE("\n");
3964 ZeroMemory (&wndClass, sizeof(WNDCLASSA));
3965 wndClass.style = CS_GLOBALCLASS | CS_DBLCLKS;
3966 wndClass.lpfnWndProc = (WNDPROC)TREEVIEW_WindowProc;
3967 wndClass.cbClsExtra = 0;
3968 wndClass.cbWndExtra = sizeof(TREEVIEW_INFO *);
3969 wndClass.hCursor = LoadCursorA (0, IDC_ARROWA);
3970 wndClass.hbrBackground = 0;
3971 wndClass.lpszClassName = WC_TREEVIEWA;
3973 RegisterClassA (&wndClass);
3977 VOID
3978 TREEVIEW_Unregister (void)
3980 UnregisterClassA (WC_TREEVIEWA, (HINSTANCE)NULL);