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
9 * Using DPA to store the item ptr would be good.
10 * Node label edition is implemented but something happened in wine in the
11 * two last weeks of march 99 that broke it.
13 -small array containing info about positions.
14 -better implementation of RefreshItem:
15 1) draw lines between parents
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.
24 * FIXME: check fontsize. (uRealItemHeight)
25 * test focusItem (redraw in different color)
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.
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 #define FOCUS_BORDER 3
68 TREEVIEW_SendSimpleNotify (HWND hwnd, UINT code);
70 TREEVIEW_SendTreeviewNotify (HWND hwnd, UINT code, UINT action,
71 HTREEITEM oldItem, HTREEITEM newItem);
73 TREEVIEW_SendTreeviewDnDNotify (HWND hwnd, UINT code, HTREEITEM dragItem,
76 TREEVIEW_SendDispInfoNotify (HWND hwnd, TREEVIEW_ITEM *wineItem,
77 UINT code, UINT what);
79 TREEVIEW_SendCustomDrawNotify (HWND hwnd, DWORD dwDrawStage, HDC hdc,
82 TREEVIEW_SendCustomDrawItemNotify (HWND hwnd, HDC hdc,
83 TREEVIEW_ITEM *tvItem, UINT uItemDrawState);
85 TREEVIEW_DoSelectItem (HWND hwnd, INT action, HTREEITEM newSelect, INT cause);
87 TREEVIEW_Refresh (HWND hwnd, HDC hdc);
89 static LRESULT CALLBACK
90 TREEVIEW_Edit_SubclassProc (HWND hwnd, UINT uMsg, WPARAM wParam,
94 TREEVIEW_EditLabelA (HWND hwnd, WPARAM wParam, LPARAM lParam);
97 TREEVIEW_EndEditLabelNow (HWND hwnd, WPARAM wParam, LPARAM lParam);
102 /* helper functions. Work with the assumption that validity of operands
103 is checked beforehand, and that tree state is valid. */
105 /* FIXME: MS documentation says `GetNextVisibleItem' returns NULL
106 if not successfull. Probably only applies to dereferencing infoPtr
107 (i.e. we are offered a valid treeview structure)
108 and not whether there is a next `visible' child.
109 FIXME: check other failures.
112 /***************************************************************************
113 * This method returns the TREEVIEW_ITEM object given the handle
115 static TREEVIEW_ITEM* TREEVIEW_ValidItem(
116 TREEVIEW_INFO *infoPtr,
119 if ((!handle) || (handle>infoPtr->uMaxHandle))
122 if (tv_test_bit ((INT)handle, infoPtr->freeList))
125 return &infoPtr->items[(INT)handle];
128 /***************************************************************************
129 * This method returns the last expanded child item of a tree node
131 static TREEVIEW_ITEM *TREEVIEW_GetLastListItem(
132 TREEVIEW_INFO *infoPtr,
133 TREEVIEW_ITEM *tvItem)
135 TREEVIEW_ITEM *wineItem = tvItem;
138 * Get this item last sibling
140 while (wineItem->sibling)
141 wineItem=& infoPtr->items [(INT)wineItem->sibling];
144 * If the last sibling has expanded children, restart.
146 if ( ( wineItem->cChildren > 0 ) && ( wineItem->state & TVIS_EXPANDED) )
147 return TREEVIEW_GetLastListItem(
149 &(infoPtr->items[(INT)wineItem->firstChild]));
154 /***************************************************************************
155 * This method returns the previous physical item in the list not
156 * considering the tree hierarchy.
158 static TREEVIEW_ITEM *TREEVIEW_GetPrevListItem(
159 TREEVIEW_INFO *infoPtr,
160 TREEVIEW_ITEM *tvItem)
162 if (tvItem->upsibling)
165 * This item has a upsibling, get the last item. Since, GetLastListItem
166 * first looks at siblings, we must feed it with the first child.
168 TREEVIEW_ITEM *upItem = &infoPtr->items[(INT)tvItem->upsibling];
170 if ( ( upItem->cChildren > 0 ) && ( upItem->state & TVIS_EXPANDED) )
171 return TREEVIEW_GetLastListItem(
173 &infoPtr->items[(INT)upItem->firstChild]);
180 * this item does not have a upsibling, get the parent
183 return &infoPtr->items[(INT)tvItem->parent];
190 /***************************************************************************
191 * This method returns the next physical item in the treeview not
192 * considering the tree hierarchy.
194 static TREEVIEW_ITEM *TREEVIEW_GetNextListItem(
195 TREEVIEW_INFO *infoPtr,
196 TREEVIEW_ITEM *tvItem)
198 TREEVIEW_ITEM *wineItem = NULL;
201 * If this item has children and is expanded, return the first child
203 if ((tvItem->firstChild) && (tvItem->state & TVIS_EXPANDED))
204 return (& infoPtr->items[(INT)tvItem->firstChild]);
208 * try to get the sibling
211 return (& infoPtr->items[(INT)tvItem->sibling]);
214 * Otherwise, get the parent's sibling.
217 while (wineItem->parent) {
218 wineItem=& infoPtr->items [(INT)wineItem->parent];
219 if (wineItem->sibling)
220 return (& infoPtr->items [(INT)wineItem->sibling]);
226 /***************************************************************************
227 * This method returns the nth item starting at the given item. It returns
228 * the last item (or first) we we run out of items.
230 * Will scroll backward if count is <0.
231 * forward if count is >0.
233 static TREEVIEW_ITEM *TREEVIEW_GetListItem(
234 TREEVIEW_INFO *infoPtr,
235 TREEVIEW_ITEM *tvItem,
238 TREEVIEW_ITEM *previousItem = NULL;
239 TREEVIEW_ITEM *wineItem = tvItem;
244 /* Find count item downward */
245 while ((iter++ < count) && (wineItem != NULL))
247 /* Keep a pointer to the previous in case we ask for more than we got */
248 previousItem = wineItem;
249 wineItem = TREEVIEW_GetNextListItem(infoPtr, wineItem);
252 if (wineItem == NULL)
253 wineItem = previousItem;
257 /* Find count item upward */
258 while ((iter-- > count) && (wineItem != NULL))
260 /* Keep a pointer to the previous in case we ask for more than we got */
261 previousItem = wineItem;
262 wineItem = TREEVIEW_GetPrevListItem(infoPtr, wineItem);
265 if (wineItem == NULL)
266 wineItem = previousItem;
275 /***************************************************************************
278 static void TREEVIEW_RemoveAllChildren(
280 TREEVIEW_ITEM *parentItem)
282 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
283 TREEVIEW_ITEM *killItem;
286 kill=(INT)parentItem->firstChild;
288 tv_set_bit ( kill, infoPtr->freeList);
289 killItem=& infoPtr->items[kill];
290 if (killItem->pszText!=LPSTR_TEXTCALLBACKA)
291 COMCTL32_Free (killItem->pszText);
292 TREEVIEW_SendTreeviewNotify (hwnd, TVN_DELETEITEMA, 0, (HTREEITEM)kill, 0);
293 if (killItem->firstChild)
294 TREEVIEW_RemoveAllChildren (hwnd, killItem);
295 kill=(INT)killItem->sibling;
298 if (parentItem->cChildren>0) {
299 infoPtr->uNumItems -= parentItem->cChildren;
300 parentItem->firstChild = 0;
301 parentItem->cChildren = 0;
308 TREEVIEW_RemoveItem (HWND hwnd, TREEVIEW_ITEM *wineItem)
311 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
312 TREEVIEW_ITEM *parentItem, *upsiblingItem, *siblingItem;
315 iItem=(INT)wineItem->hItem;
316 tv_set_bit(iItem,infoPtr->freeList);
317 infoPtr->uNumItems--;
319 if (wineItem->pszText!=LPSTR_TEXTCALLBACKA)
320 COMCTL32_Free (wineItem->pszText);
322 TREEVIEW_SendTreeviewNotify (hwnd, TVN_DELETEITEMA, 0, (HTREEITEM)iItem, 0);
324 if (wineItem->firstChild)
325 TREEVIEW_RemoveAllChildren (hwnd,wineItem);
327 if (wineItem->parent) {
328 parentItem=& infoPtr->items [(INT)wineItem->parent];
329 switch (parentItem->cChildren) {
330 case I_CHILDRENCALLBACK:
331 FIXME("we don't handle I_CHILDRENCALLBACK yet\n");
334 parentItem->cChildren=0;
335 parentItem->firstChild=0;
338 parentItem->cChildren--;
339 if ((INT)parentItem->firstChild==iItem)
340 parentItem->firstChild=wineItem->sibling;
344 if (iItem==(INT)infoPtr->TopRootItem)
345 infoPtr->TopRootItem=(HTREEITEM)wineItem->sibling;
346 if (wineItem->upsibling) {
347 upsiblingItem=& infoPtr->items [(INT)wineItem->upsibling];
348 upsiblingItem->sibling=wineItem->sibling;
350 if (wineItem->sibling) {
351 siblingItem=& infoPtr->items [(INT)wineItem->sibling];
352 siblingItem->upsibling=wineItem->upsibling;
360 /* Note:TREEVIEW_RemoveTree doesn't remove infoPtr itself */
362 static void TREEVIEW_RemoveTree (HWND hwnd)
365 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
366 TREEVIEW_ITEM *killItem;
369 for (i = 1; i <= (INT)infoPtr->uMaxHandle; i++)
370 if (!tv_test_bit (i, infoPtr->freeList)) {
371 killItem = &infoPtr->items[i];
372 if (killItem->pszText != LPSTR_TEXTCALLBACKA)
373 COMCTL32_Free (killItem->pszText);
374 TREEVIEW_SendTreeviewNotify(hwnd, TVN_DELETEITEMA, 0,
377 if (infoPtr->uNumPtrsAlloced) {
378 COMCTL32_Free (infoPtr->items);
379 COMCTL32_Free (infoPtr->freeList);
380 infoPtr->uNumItems = 0;
381 infoPtr->uNumPtrsAlloced = 0;
382 infoPtr->uMaxHandle = 0;
383 infoPtr->TopRootItem = 0;
394 TREEVIEW_GetImageList (HWND hwnd, WPARAM wParam, LPARAM lParam)
396 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
400 if ((INT)wParam == TVSIL_NORMAL)
401 return (LRESULT) infoPtr->himlNormal;
402 if ((INT)wParam == TVSIL_STATE)
403 return (LRESULT) infoPtr->himlState;
409 TREEVIEW_SetImageList (HWND hwnd, WPARAM wParam, LPARAM lParam)
411 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
414 TRACE("%x,%lx\n", wParam, lParam);
415 switch ((INT)wParam) {
417 himlTemp = infoPtr->himlNormal;
418 infoPtr->himlNormal = (HIMAGELIST)lParam;
419 return (LRESULT)himlTemp;
422 himlTemp = infoPtr->himlState;
423 infoPtr->himlState = (HIMAGELIST)lParam;
424 return (LRESULT)himlTemp;
427 return (LRESULT)NULL;
433 TREEVIEW_SetItemHeight (HWND hwnd, WPARAM wParam)
435 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
436 INT cx,cy,prevHeight=infoPtr->uItemHeight;
440 infoPtr->uItemHeight=-1;
444 ImageList_GetIconSize (infoPtr->himlNormal, &cx, &cy);
446 if (wParam>cy) cy=wParam;
447 infoPtr->uItemHeight=cy;
449 if (!( GetWindowLongA( hwnd, GWL_STYLE) & TVS_NONEVENHEIGHT))
450 infoPtr->uItemHeight = (INT) wParam & 0xfffffffe;
455 TREEVIEW_GetItemHeight (HWND hwnd)
457 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
460 return infoPtr->uItemHeight;
464 TREEVIEW_GetLineColor (HWND hwnd, WPARAM wParam, LPARAM lParam)
466 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
469 return (LRESULT) infoPtr->clrLine;
473 TREEVIEW_SetLineColor (HWND hwnd, WPARAM wParam, LPARAM lParam)
475 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
476 COLORREF prevColor=infoPtr->clrLine;
479 infoPtr->clrLine=(COLORREF) lParam;
480 return (LRESULT) prevColor;
484 TREEVIEW_GetInsertMarkColor (HWND hwnd, WPARAM wParam, LPARAM lParam)
486 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
489 return (LRESULT) infoPtr->clrInsertMark;
493 TREEVIEW_SetInsertMarkColor (HWND hwnd, WPARAM wParam, LPARAM lParam)
495 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
496 COLORREF prevColor=infoPtr->clrInsertMark;
498 TRACE("%d %ld\n",wParam,lParam);
499 infoPtr->clrInsertMark=(COLORREF) lParam;
500 return (LRESULT) prevColor;
504 TREEVIEW_SetInsertMark (HWND hwnd, WPARAM wParam, LPARAM lParam)
506 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
509 FIXME("%d %ld\n",wParam,lParam);
510 if (!TREEVIEW_ValidItem (infoPtr, (HTREEITEM)lParam)) return 0;
511 FIXME("%d %ld\n",wParam,lParam);
513 infoPtr->insertBeforeorAfter=(BOOL) wParam;
514 infoPtr->insertMarkItem=(HTREEITEM) lParam;
517 TREEVIEW_Refresh (hwnd, hdc);
524 TREEVIEW_SetTextColor (HWND hwnd, WPARAM wParam, LPARAM lParam)
526 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
527 COLORREF prevColor=infoPtr->clrText;
530 infoPtr->clrText=(COLORREF) lParam;
531 return (LRESULT) prevColor;
535 TREEVIEW_GetBkColor (HWND hwnd)
537 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
540 return (LRESULT) infoPtr->clrBk;
544 TREEVIEW_SetBkColor (HWND hwnd, WPARAM wParam, LPARAM lParam)
546 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
547 COLORREF prevColor=infoPtr->clrBk;
550 infoPtr->clrBk=(COLORREF) lParam;
551 return (LRESULT) prevColor;
555 TREEVIEW_GetTextColor (HWND hwnd)
557 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
560 return (LRESULT) infoPtr->clrText;
564 /* cdmode: custom draw mode as received from app. in first NMCUSTOMDRAW
567 #define TREEVIEW_LEFT_MARGIN 8
571 TREEVIEW_DrawItem (HWND hwnd, HDC hdc, TREEVIEW_ITEM *wineItem)
573 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
574 DWORD dwStyle = GetWindowLongA (hwnd, GWL_STYLE);
575 INT center,xpos,cx,cy, cditem;
577 UINT uTextJustify = DT_LEFT;
581 if (wineItem->state & TVIS_BOLD)
582 hOldFont = SelectObject (hdc, infoPtr->hBoldFont);
584 hOldFont = SelectObject (hdc, infoPtr->hFont);
587 TRACE ("cdmode:%x\n",infoPtr->cdmode);
588 if (infoPtr->cdmode & CDRF_NOTIFYITEMDRAW) {
589 cditem=TREEVIEW_SendCustomDrawItemNotify
590 (hwnd, hdc, wineItem, CDDS_ITEMPREPAINT);
591 TRACE("prepaint:cditem-app returns 0x%x\n",cditem);
593 if (cditem & CDRF_SKIPDEFAULT)
598 * Set drawing starting points
600 r = wineItem->rect; /* this item rectangle */
601 center = (r.top+r.bottom)/2; /* this item vertical center */
602 xpos = r.left + TREEVIEW_LEFT_MARGIN;/* horizontal starting point */
605 * Display the tree hierarchy
607 if ( dwStyle & TVS_HASLINES)
610 * Write links to parent node
611 * we draw the L starting from the child to the parent
613 * points[0] is attached to the current item
614 * points[1] is the L corner
615 * points[2] is attached to the parent or the up sibling
617 if ( dwStyle & TVS_LINESATROOT)
619 TREEVIEW_ITEM *upNode = NULL;
620 BOOL hasParentOrSibling = TRUE;
621 RECT upRect = {0,0,0,0};
622 HPEN hOldPen, hNewPen;
625 * determine the target location of the line at root, either be linked
626 * to the up sibling or to the parent node.
628 if (wineItem->upsibling)
629 upNode = TREEVIEW_ValidItem (infoPtr, wineItem->upsibling);
630 else if (wineItem->parent)
631 upNode = TREEVIEW_ValidItem (infoPtr, wineItem->parent);
633 hasParentOrSibling = FALSE;
636 upRect = upNode->rect;
638 if ( wineItem->iLevel == 0 )
640 points[2].x = points[1].x = upRect.left+8;
641 points[0].x = points[2].x + 10;
642 points[2].y = upRect.bottom-3;
643 points[1].y = points[0].y = center;
647 points[2].x = points[1].x = 8 + (20*wineItem->iLevel);
648 points[2].y = ( upNode->cChildren == 0) ?
649 upRect.top : /* is linked to the "L" above */
650 ( wineItem->upsibling != NULL) ?
651 upRect.bottom-3: /* is linked to an icon */
652 upRect.bottom+1; /* is linked to a +/- box */
653 points[1].y = points[0].y = center;
654 points[0].x = points[1].x + 10;
660 hNewPen = CreatePen(PS_DOT, 0, infoPtr->clrLine);
661 hOldPen = SelectObject( hdc, hNewPen );
663 if (hasParentOrSibling)
664 Polyline (hdc,points,3);
666 Polyline (hdc,points,2);
668 DeleteObject(hNewPen);
669 SelectObject(hdc, hOldPen);
674 * Display the (+/-) signs
676 if (wineItem->iLevel != 0)/* update position only for non root node */
677 xpos+=(5*wineItem->iLevel);
679 if (( dwStyle & TVS_HASBUTTONS) && ( dwStyle & TVS_HASLINES))
681 if ( (wineItem->cChildren) ||
682 (wineItem->cChildren == I_CHILDRENCALLBACK))
684 /* Setup expand box coordinate to facilitate the LMBClick handling */
685 wineItem->expandBox.left = xpos-4;
686 wineItem->expandBox.top = center-4;
687 wineItem->expandBox.right = xpos+5;
688 wineItem->expandBox.bottom = center+5;
692 wineItem->expandBox.left,
693 wineItem->expandBox.top ,
694 wineItem->expandBox.right,
695 wineItem->expandBox.bottom);
697 MoveToEx (hdc, xpos-2, center, NULL);
698 LineTo (hdc, xpos+3, center);
700 if (!(wineItem->state & TVIS_EXPANDED)) {
701 MoveToEx (hdc, xpos, center-2, NULL);
702 LineTo (hdc, xpos, center+3);
708 * Display the image associated with this item
710 xpos += 13; /* update position */
711 if (wineItem->mask & (TVIF_IMAGE|TVIF_SELECTEDIMAGE)) {
713 HIMAGELIST *himlp = NULL;
715 /* State images are displayed to the left of the Normal image
716 * image number is in state; zero should be `display no image'.
717 * FIXME: that last sentence looks like it needs some checking.
719 if (infoPtr->himlState)
720 himlp=&infoPtr->himlState;
721 imageIndex=wineItem->state>>12;
722 imageIndex++; /* yeah, right */
723 TRACE ("imindex:%d\n",imageIndex);
724 if ((himlp) && (imageIndex))
726 imageIndex--; /* see FIXME */
727 ImageList_Draw ( *himlp, imageIndex, hdc, xpos-2, r.top+1, ILD_NORMAL);
728 ImageList_GetIconSize (*himlp, &cx, &cy);
729 wineItem->statebitmap.left=xpos-2;
730 wineItem->statebitmap.right=xpos-2+cx;
731 wineItem->statebitmap.top=r.top+1;
732 wineItem->statebitmap.bottom=r.top+1+cy;
736 /* Now, draw the normal image; can be either selected or
737 * non-selected image.
741 if (infoPtr->himlNormal)
742 himlp=&infoPtr->himlNormal; /* get the image list */
744 imageIndex = wineItem->iImage;
745 if ( (wineItem->state & TVIS_SELECTED) &&
746 (wineItem->iSelectedImage)) {
748 /* The item is curently selected */
749 if (wineItem->iSelectedImage == I_IMAGECALLBACK)
750 TREEVIEW_SendDispInfoNotify
751 (hwnd, wineItem, TVN_GETDISPINFOA, TVIF_SELECTEDIMAGE);
753 imageIndex = wineItem->iSelectedImage;
755 /* The item is not selected */
756 if (wineItem->iImage == I_IMAGECALLBACK)
757 TREEVIEW_SendDispInfoNotify
758 (hwnd, wineItem, TVN_GETDISPINFOA, TVIF_IMAGE);
760 imageIndex = wineItem->iImage;
767 if(wineItem->stateMask & TVIS_OVERLAYMASK)
768 ovlIdx = wineItem->state & TVIS_OVERLAYMASK;
770 ImageList_Draw ( *himlp, imageIndex, hdc, xpos-2, r.top+1, ILD_NORMAL|ovlIdx);
771 ImageList_GetIconSize (*himlp, &cx, &cy);
772 wineItem->bitmap.left=xpos-2;
773 wineItem->bitmap.right=xpos-2+cx;
774 wineItem->bitmap.top=r.top+1;
775 wineItem->bitmap.bottom=r.top+1+cy;
782 * Display the text associated with this item
785 if ((wineItem->mask & TVIF_TEXT) && (wineItem->pszText))
787 COLORREF oldTextColor = 0;
790 BOOL inFocus = GetFocus() == hwnd;
792 TREEVIEW_ITEM tmpItem;
795 if (wineItem->pszText == LPSTR_TEXTCALLBACKA)
797 tmpItem.hItem = wineItem->hItem;
798 tmpItem.state = wineItem->state;
799 tmpItem.lParam = wineItem->lParam;
800 tmpItem.pszText = buf;
801 tmpItem.cchTextMax = sizeof(buf);
803 TREEVIEW_SendDispInfoNotify(hwnd, &tmpItem, TVN_GETDISPINFOA, TVIF_TEXT);
809 wineItem->text.left = r.left;
810 wineItem->text.right = r.right;
811 wineItem->text.top = r.top;
812 wineItem->text.bottom= r.bottom;
814 oldBkMode = SetBkMode(hdc, TRANSPARENT);
816 /* - If item is drop target or it is selected and window is in focus -
817 * use blue background (COLOR_HIGHLIGHT).
818 * - If item is selected, window is not in focus, but it has style
819 * TVS_SHOWSELALWAYS - use grey background (COLOR_BTNFACE)
820 * - Otherwise - don't fill background
822 if ((wineItem->state & TVIS_DROPHILITED) ||
823 ((wineItem->state & TVIS_SELECTED) &&
824 (inFocus || (GetWindowLongA( hwnd, GWL_STYLE) & TVS_SHOWSELALWAYS))))
826 if ((wineItem->state & TVIS_DROPHILITED) || inFocus)
828 hbrBk = CreateSolidBrush(GetSysColor( COLOR_HIGHLIGHT));
829 oldTextColor = SetTextColor(hdc, GetSysColor( COLOR_HIGHLIGHTTEXT));
833 hbrBk = CreateSolidBrush(GetSysColor( COLOR_BTNFACE));
835 if (infoPtr->clrText == -1)
836 oldTextColor = SetTextColor(hdc, GetSysColor( COLOR_WINDOWTEXT));
838 oldTextColor = SetTextColor(hdc, infoPtr->clrText);
843 if (infoPtr->clrText == -1)
844 oldTextColor = SetTextColor(hdc, GetSysColor( COLOR_WINDOWTEXT));
846 oldTextColor = SetTextColor(hdc, infoPtr->clrText);
849 if (wineItem->pszText != LPSTR_TEXTCALLBACKA)
850 tmpItem.pszText = wineItem->pszText;
852 /* Obtain the text coordinate */
856 lstrlenA(tmpItem.pszText),
858 uTextJustify | DT_VCENTER | DT_SINGLELINE | DT_CALCRECT | DT_NOPREFIX);
860 /* We need to reset it to items height */
861 wineItem->text.top = r.top;
862 wineItem->text.bottom = r.bottom;
863 wineItem->text.right += 4; /* This is extra for focus rectangle */
867 FillRect(hdc, &wineItem->text, hbrBk);
871 wineItem->text.left += 2;
876 lstrlenA(tmpItem.pszText),
878 uTextJustify | DT_VCENTER | DT_SINGLELINE | DT_NOPREFIX);
880 wineItem->text.left -=2;
882 /* Restore the hdc state */
883 SetTextColor( hdc, oldTextColor);
885 /* Draw the box arround the selected item */
886 if (wineItem->state & TVIS_SELECTED && inFocus)
888 HPEN hNewPen = CreatePen(PS_DOT, 0, GetSysColor(COLOR_WINDOWTEXT) );
889 HPEN hOldPen = SelectObject( hdc, hNewPen );
890 INT rop = SetROP2(hdc, R2_XORPEN);
893 points[4].x = points[0].x = wineItem->text.left;
894 points[4].y = points[0].y = wineItem->text.top;
895 points[1].x = wineItem->text.right-1 ;
896 points[1].y = wineItem->text.top;
897 points[2].x = wineItem->text.right-1;
898 points[2].y = wineItem->text.bottom-1;
899 points[3].x = wineItem->text.left;
900 points[3].y = wineItem->text.bottom-1;
902 Polyline (hdc,points,5);
905 DeleteObject(hNewPen);
906 SelectObject(hdc, hOldPen);
909 if (oldBkMode != TRANSPARENT)
910 SetBkMode(hdc, oldBkMode);
913 /* Draw insertion mark if necessary */
915 if (infoPtr->insertMarkItem)
916 TRACE ("item:%d,mark:%d\n", (int)wineItem->hItem,
917 (int) infoPtr->insertMarkItem);
918 if (wineItem->hItem==infoPtr->insertMarkItem) {
919 HPEN hNewPen, hOldPen;
922 hNewPen = CreatePen(PS_SOLID, 2, infoPtr->clrInsertMark);
923 hOldPen = SelectObject( hdc, hNewPen );
925 if (infoPtr->insertBeforeorAfter)
926 offset=wineItem->text.top+1;
928 offset=wineItem->text.bottom-1;
930 MoveToEx (hdc, wineItem->text.left, offset-3, NULL);
931 LineTo (hdc, wineItem->text.left, offset+3);
933 MoveToEx (hdc, wineItem->text.left, offset, NULL);
934 LineTo (hdc, r.right-2, offset);
936 MoveToEx (hdc, r.right-2, offset+3, NULL);
937 LineTo (hdc, r.right-2, offset-3);
939 DeleteObject(hNewPen);
941 SelectObject(hdc, hOldPen);
944 if (cditem & CDRF_NOTIFYPOSTPAINT) {
945 cditem=TREEVIEW_SendCustomDrawItemNotify
946 (hwnd, hdc, wineItem, CDDS_ITEMPOSTPAINT);
947 TRACE("postpaint:cditem-app returns 0x%x\n",cditem);
950 SelectObject (hdc, hOldFont);
954 TREEVIEW_GetItemRect (HWND hwnd, WPARAM wParam, LPARAM lParam)
956 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
957 TREEVIEW_ITEM *wineItem;
959 LPRECT lpRect = (LPRECT)lParam;
964 * validate parameters
969 if (infoPtr->Timer & TV_REFRESH_TIMER_SET) {
971 TREEVIEW_Refresh (hwnd, hdc); /* we want a rect for the current view */
977 * retrieve the item ptr
979 iItem = (HTREEITEM *) lParam;
980 wineItem = TREEVIEW_ValidItem (infoPtr, *iItem);
981 if ((!wineItem) || (!wineItem->visible))
985 * If wParam is TRUE return the text size otherwise return
986 * the whole item size
989 lpRect->left = wineItem->text.left;
990 lpRect->right = wineItem->text.right;
991 lpRect->bottom = wineItem->text.bottom;
992 lpRect->top = wineItem->text.top;
994 lpRect->left = wineItem->rect.left;
995 lpRect->right = wineItem->rect.right;
996 lpRect->bottom = wineItem->rect.bottom;
997 lpRect->top = wineItem->rect.top;
1000 TRACE("[L:%d R:%d T:%d B:%d]\n",
1001 lpRect->left,lpRect->right,
1002 lpRect->top,lpRect->bottom);
1008 TREEVIEW_GetVisibleCount (HWND hwnd, WPARAM wParam, LPARAM lParam)
1011 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
1013 return (LRESULT) infoPtr->uVisibleHeight / infoPtr->uRealItemHeight;
1019 TREEVIEW_SetItemA (HWND hwnd, WPARAM wParam, LPARAM lParam)
1021 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
1022 TREEVIEW_ITEM *wineItem;
1026 tvItem=(LPTVITEMEXA) lParam;
1027 iItem=(INT)tvItem->hItem;
1028 TRACE("item %d,mask %x\n",iItem,tvItem->mask);
1030 wineItem = TREEVIEW_ValidItem (infoPtr, (HTREEITEM)iItem);
1031 if (!wineItem) return FALSE;
1033 if (tvItem->mask & TVIF_CHILDREN) {
1034 wineItem->cChildren=tvItem->cChildren;
1037 if (tvItem->mask & TVIF_IMAGE) {
1038 wineItem->iImage=tvItem->iImage;
1041 if (tvItem->mask & TVIF_INTEGRAL) {
1042 wineItem->iIntegral=tvItem->iIntegral;
1045 if (tvItem->mask & TVIF_PARAM) {
1046 wineItem->lParam=tvItem->lParam;
1049 if (tvItem->mask & TVIF_SELECTEDIMAGE) {
1050 wineItem->iSelectedImage=tvItem->iSelectedImage;
1053 if (tvItem->mask & TVIF_STATE) {
1054 TRACE ("prevstate,state,mask:%x,%x,%x\n",wineItem->state,tvItem->state,
1056 wineItem->state&= ~tvItem->stateMask;
1057 wineItem->state|= (tvItem->state & tvItem->stateMask);
1058 wineItem->stateMask|= tvItem->stateMask;
1061 if (tvItem->mask & TVIF_TEXT)
1063 if (tvItem->pszText!=LPSTR_TEXTCALLBACKA)
1065 len=lstrlenA (tvItem->pszText) + 1;
1066 if (len>wineItem->cchTextMax)
1068 wineItem->pszText= COMCTL32_ReAlloc (wineItem->pszText, len);
1069 wineItem->cchTextMax = len;
1072 lstrcpynA (wineItem->pszText, tvItem->pszText,len);
1076 if (wineItem->cchTextMax)
1078 COMCTL32_Free (wineItem->pszText);
1079 wineItem->cchTextMax=0;
1081 wineItem->pszText=LPSTR_TEXTCALLBACKA;
1085 wineItem->mask |= tvItem->mask;
1091 TREEVIEW_GetItemState (HWND hwnd, WPARAM wParam, LPARAM lParam)
1094 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
1095 TREEVIEW_ITEM *wineItem;
1098 wineItem = TREEVIEW_ValidItem (infoPtr, (HTREEITEM)wParam);
1099 if (!wineItem) return 0;
1101 return (wineItem->state & lParam);
1108 TREEVIEW_Refresh (HWND hwnd, HDC hdc)
1110 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
1116 INT iItem, indent, x, y, height, itemHeight;
1117 INT viewtop,viewbottom,viewleft,viewright;
1118 TREEVIEW_ITEM *wineItem, *prevItem;
1123 if (infoPtr->Timer & TV_REFRESH_TIMER_SET) {
1124 KillTimer (hwnd, TV_REFRESH_TIMER);
1125 infoPtr->Timer &= ~TV_REFRESH_TIMER_SET;
1129 GetClientRect (hwnd, &rect);
1130 if ((rect.left >= rect.right) || (rect.top >= rect.bottom)) return;
1132 infoPtr->cdmode=TREEVIEW_SendCustomDrawNotify(hwnd,CDDS_PREPAINT,hdc,rect);
1134 if (infoPtr->cdmode==CDRF_SKIPDEFAULT) return;
1136 infoPtr->uVisibleHeight= rect.bottom-rect.top + 1;
1137 infoPtr->uVisibleWidth= rect.right-rect.left + 1;
1139 viewtop=infoPtr->cy;
1140 viewbottom=infoPtr->cy + rect.bottom-rect.top;
1141 viewleft=infoPtr->cx;
1142 viewright=infoPtr->cx + rect.right-rect.left;
1144 TRACE("[%d %d %d %d]\n",viewtop,viewbottom,viewleft,viewright);
1146 /* draw background */
1148 hbrBk = CreateSolidBrush (infoPtr->clrBk);
1149 FillRect(hdc, &rect, hbrBk);
1150 DeleteObject(hbrBk);
1152 ImageList_GetIconSize (infoPtr->himlNormal, &x, &itemHeight);
1153 if (infoPtr->uItemHeight>itemHeight)
1154 itemHeight=infoPtr->uItemHeight;
1156 // assume that bold and normal fonts have same height
1157 hOldFont = SelectObject (hdc, infoPtr->hBoldFont);
1158 GetTextMetricsA (hdc, &tm);
1159 if ((tm.tmHeight + tm.tmExternalLeading + FOCUS_BORDER) > itemHeight)
1160 itemHeight=tm.tmHeight + tm.tmExternalLeading + FOCUS_BORDER;
1161 SelectObject (hdc, hOldFont);
1163 infoPtr->uRealItemHeight=itemHeight;
1165 iItem=(INT)infoPtr->TopRootItem;
1166 infoPtr->firstVisible=0;
1173 wineItem= & infoPtr->items[iItem];
1174 wineItem->iLevel=indent;
1176 /* FIXME: remove this in later stage */
1178 if (wineItem->pszText!=LPSTR_TEXTCALLBACK32A)
1179 TRACE (treeview, "%d %d [%d %d %d %d] (%s)\n",y,x,
1180 wineItem->rect.top, wineItem->rect.bottom,
1181 wineItem->rect.left, wineItem->rect.right,
1184 TRACE (treeview, "%d [%d %d %d %d] (CALLBACK)\n",
1186 wineItem->rect.top, wineItem->rect.bottom,
1187 wineItem->rect.left, wineItem->rect.right);
1190 height=itemHeight * wineItem->iIntegral;
1191 if ((y >= viewtop) && (y <= viewbottom) &&
1192 (x >= viewleft ) && (x <= viewright)) {
1193 wineItem->visible = TRUE;
1194 wineItem->rect.top = y - infoPtr->cy + rect.top;
1195 wineItem->rect.bottom = wineItem->rect.top + height-1;
1196 wineItem->rect.left = x - infoPtr->cx + rect.left;
1197 wineItem->rect.right = rect.right;
1198 if (!infoPtr->firstVisible)
1199 infoPtr->firstVisible=wineItem->hItem;
1200 TREEVIEW_DrawItem (hwnd, hdc, wineItem);
1203 wineItem->visible = FALSE;
1204 wineItem->rect.left = wineItem->rect.top = 0;
1205 wineItem->rect.right= wineItem->rect.bottom = 0;
1206 wineItem->text.left = wineItem->text.top = 0;
1207 wineItem->text.right= wineItem->text.bottom = 0;
1210 /* look up next item */
1212 if ((wineItem->firstChild) && (wineItem->state & TVIS_EXPANDED)) {
1213 iItem=(INT)wineItem->firstChild;
1215 x+=infoPtr->uIndent;
1216 if (x>infoPtr->uTotalWidth)
1217 infoPtr->uTotalWidth=x;
1220 iItem=(INT)wineItem->sibling;
1221 while ((!iItem) && (indent>0)) {
1223 x-=infoPtr->uIndent;
1224 wineItem=&infoPtr->items[(INT)wineItem->parent];
1225 iItem=(INT)wineItem->sibling;
1231 /* FIXME: infoPtr->uTotalWidth should also take item label into account */
1232 /* FIXME: or should query item sizes (ie check CDRF_NEWFONT) */
1234 infoPtr->uTotalHeight=y;
1235 if (y >= (viewbottom-viewtop)) {
1236 if (!(infoPtr->uInternalStatus & TV_VSCROLL))
1237 ShowScrollBar (hwnd, SB_VERT, TRUE);
1238 infoPtr->uInternalStatus |=TV_VSCROLL;
1239 SetScrollRange (hwnd, SB_VERT, 0,
1240 y - infoPtr->uVisibleHeight, FALSE);
1241 SetScrollPos (hwnd, SB_VERT, infoPtr->cy, TRUE);
1244 if (infoPtr->uInternalStatus & TV_VSCROLL)
1245 ShowScrollBar (hwnd, SB_VERT, FALSE);
1246 infoPtr->uInternalStatus &= ~TV_VSCROLL;
1250 if (infoPtr->cdmode & CDRF_NOTIFYPOSTPAINT)
1251 infoPtr->cdmode=TREEVIEW_SendCustomDrawNotify
1252 (hwnd, CDDS_POSTPAINT, hdc, rect);
1259 TREEVIEW_HandleTimer (HWND hwnd, WPARAM wParam, LPARAM lParam)
1261 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
1263 TRACE(" %d\n",wParam);
1266 case TV_REFRESH_TIMER:
1267 KillTimer (hwnd, TV_REFRESH_TIMER);
1268 infoPtr->Timer &= ~TV_REFRESH_TIMER_SET;
1269 InvalidateRect(hwnd, NULL, FALSE);
1272 KillTimer (hwnd, TV_EDIT_TIMER);
1273 infoPtr->Timer &= ~TV_EDIT_TIMER_SET;
1276 ERR("got unknown timer\n");
1284 TREEVIEW_QueueRefresh (HWND hwnd)
1287 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
1290 if (infoPtr->Timer & TV_REFRESH_TIMER_SET) {
1291 KillTimer (hwnd, TV_REFRESH_TIMER);
1294 SetTimer (hwnd, TV_REFRESH_TIMER, TV_REFRESH_DELAY, 0);
1295 infoPtr->Timer|=TV_REFRESH_TIMER_SET;
1301 TREEVIEW_GetItemA (HWND hwnd, WPARAM wParam, LPARAM lParam)
1303 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
1305 TREEVIEW_ITEM *wineItem;
1308 tvItem=(LPTVITEMEXA) lParam;
1309 iItem=(INT)tvItem->hItem;
1311 wineItem = TREEVIEW_ValidItem (infoPtr, (HTREEITEM)iItem);
1312 if (!wineItem) return FALSE;
1314 if (tvItem->mask & TVIF_CHILDREN) {
1315 if (TVIF_CHILDREN==I_CHILDRENCALLBACK)
1316 FIXME("I_CHILDRENCALLBACK not supported\n");
1317 tvItem->cChildren=wineItem->cChildren;
1320 if (tvItem->mask & TVIF_HANDLE) {
1321 tvItem->hItem=wineItem->hItem;
1324 if (tvItem->mask & TVIF_IMAGE) {
1325 tvItem->iImage=wineItem->iImage;
1328 if (tvItem->mask & TVIF_INTEGRAL) {
1329 tvItem->iIntegral=wineItem->iIntegral;
1332 /* undocumented: windows ignores TVIF_PARAM and
1333 * always sets lParam
1335 tvItem->lParam=wineItem->lParam;
1337 if (tvItem->mask & TVIF_SELECTEDIMAGE) {
1338 tvItem->iSelectedImage=wineItem->iSelectedImage;
1341 if (tvItem->mask & TVIF_STATE) {
1342 tvItem->state=wineItem->state & tvItem->stateMask;
1345 if (tvItem->mask & TVIF_TEXT) {
1346 if (wineItem->pszText == LPSTR_TEXTCALLBACKA) {
1347 tvItem->pszText = LPSTR_TEXTCALLBACKA; /* FIXME:send notification? */
1348 ERR(" GetItem called with LPSTR_TEXTCALLBACK\n");
1350 else if (wineItem->pszText) {
1351 lstrcpynA (tvItem->pszText, wineItem->pszText, tvItem->cchTextMax);
1355 TRACE("item %d<%p>, txt %p, img %p, action %x\n",
1356 iItem, tvItem, tvItem->pszText, &tvItem->iImage, tvItem->mask);
1363 TREEVIEW_GetItemW (HWND hwnd, WPARAM wParam, LPARAM lParam)
1365 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
1367 TREEVIEW_ITEM *wineItem;
1370 tvItem=(LPTVITEMEXA) lParam;
1371 iItem=(INT)tvItem->hItem;
1373 wineItem = TREEVIEW_ValidItem (infoPtr, (HTREEITEM)iItem);
1374 if (!wineItem) return FALSE;
1376 if (tvItem->mask & TVIF_CHILDREN) {
1377 if (TVIF_CHILDREN==I_CHILDRENCALLBACK)
1378 FIXME("I_CHILDRENCALLBACK not supported\n");
1379 tvItem->cChildren=wineItem->cChildren;
1382 if (tvItem->mask & TVIF_HANDLE) {
1383 tvItem->hItem=wineItem->hItem;
1386 if (tvItem->mask & TVIF_IMAGE) {
1387 tvItem->iImage=wineItem->iImage;
1390 if (tvItem->mask & TVIF_INTEGRAL) {
1391 tvItem->iIntegral=wineItem->iIntegral;
1394 /* undocumented: windows ignores TVIF_PARAM and
1395 * always sets lParam
1397 tvItem->lParam=wineItem->lParam;
1399 if (tvItem->mask & TVIF_SELECTEDIMAGE) {
1400 tvItem->iSelectedImage=wineItem->iSelectedImage;
1403 if (tvItem->mask & TVIF_STATE) {
1404 tvItem->state=wineItem->state & tvItem->stateMask;
1408 if (tvItem->mask & TVIF_TEXT) {
1409 if (wineItem->pszText == LPSTR_TEXTCALLBACKW) {
1410 tvItem->pszText = LPSTR_TEXTCALLBACKW; /* FIXME:send notification? */
1411 ERR(" GetItem called with LPSTR_TEXTCALLBACK\n");
1413 else if (wineItem->pszText) {
1414 lstrcpynAtoW (tvItem->pszText, wineItem->pszText, tvItem->cchTextMax);
1418 wineItem->pszText = NULL;
1420 TRACE("item %d<%p>, txt %p, img %p, action %x\n",
1421 iItem, tvItem, tvItem->pszText, &tvItem->iImage, tvItem->mask);
1428 /* FIXME: check implementation of TVGN_NEXT/TVGN_NEXTVISIBLE */
1431 TREEVIEW_GetNextItem (HWND hwnd, WPARAM wParam, LPARAM lParam)
1434 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
1435 TREEVIEW_ITEM *wineItem, *returnItem;
1436 INT iItem = (INT)lParam, retval = 0, flag = (INT)wParam;
1441 retval = (INT)infoPtr->TopRootItem;
1445 retval = (INT)infoPtr->selectedItem;
1448 case TVGN_FIRSTVISIBLE: /* FIXME:we should only recalculate, not redraw */
1450 TREEVIEW_Refresh (hwnd, hdc);
1451 ReleaseDC(hwnd,hdc);
1452 retval = (INT)infoPtr->firstVisible;
1455 case TVGN_DROPHILITE:
1456 retval = (INT)infoPtr->dropItem;
1460 wineItem = TREEVIEW_ValidItem (infoPtr, (HTREEITEM)iItem);
1461 retval = wineItem ? (INT)wineItem->sibling : 0;
1465 wineItem = TREEVIEW_ValidItem (infoPtr, (HTREEITEM)iItem);
1466 retval = wineItem ? (INT)wineItem->upsibling : 0;
1470 wineItem = TREEVIEW_ValidItem (infoPtr, (HTREEITEM)iItem);
1471 retval = wineItem ? (INT)wineItem->parent : 0;
1475 wineItem = TREEVIEW_ValidItem (infoPtr, (HTREEITEM)iItem);
1476 retval = wineItem ? (INT)wineItem->firstChild : 0;
1479 case TVGN_LASTVISIBLE:
1480 if((wineItem = TREEVIEW_ValidItem (infoPtr, (HTREEITEM)iItem))) {
1481 returnItem = TREEVIEW_GetLastListItem (infoPtr,wineItem);
1482 retval = returnItem ? (INT)returnItem->hItem : 0;
1486 case TVGN_NEXTVISIBLE:
1487 if((wineItem = TREEVIEW_ValidItem (infoPtr, (HTREEITEM)iItem))) {
1488 returnItem = TREEVIEW_GetNextListItem (infoPtr,wineItem);
1489 retval = returnItem ? (INT)returnItem->hItem : 0;
1493 case TVGN_PREVIOUSVISIBLE:
1494 if((wineItem = TREEVIEW_ValidItem (infoPtr, (HTREEITEM)iItem))) {
1495 returnItem = TREEVIEW_GetPrevListItem (infoPtr, wineItem);
1496 retval = returnItem ? (INT)returnItem->hItem : 0;
1501 FIXME("Unknown msg %x,item %x\n", flag,iItem);
1505 TRACE("flags %x, item %d returns %d\n", flag, iItem, retval);
1511 TREEVIEW_GetCount (HWND hwnd, WPARAM wParam, LPARAM lParam)
1513 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
1515 TRACE(" %d\n",infoPtr->uNumItems);
1516 return (LRESULT) infoPtr->uNumItems;
1519 /***************************************************************************
1520 * This method does the chaining of the insertion of a treeview item
1522 * If parent is NULL, we're inserting at the root of the list.
1524 static void TREEVIEW_InsertBefore(
1525 TREEVIEW_INFO *infoPtr,
1526 TREEVIEW_ITEM *newItem,
1527 TREEVIEW_ITEM *sibling,
1528 TREEVIEW_ITEM *parent)
1530 HTREEITEM siblingHandle = 0;
1531 HTREEITEM upSiblingHandle = 0;
1532 TREEVIEW_ITEM *upSibling = NULL;
1534 if (newItem == NULL)
1535 ERR("NULL newItem, impossible condition\n");
1537 if (sibling != NULL) /* Insert before this sibling for this parent */
1539 /* Store the new item sibling up sibling and sibling tem handle */
1540 siblingHandle = sibling->hItem;
1541 upSiblingHandle = sibling->upsibling;
1542 /* As well as a pointer to the upsibling sibling object */
1543 if ( (INT)sibling->upsibling != 0 )
1544 upSibling = &infoPtr->items[(INT)sibling->upsibling];
1546 /* Adjust the sibling pointer */
1547 sibling->upsibling = newItem->hItem;
1549 /* Adjust the new item pointers */
1550 newItem->upsibling = upSiblingHandle;
1551 newItem->sibling = siblingHandle;
1553 /* Adjust the up sibling pointer */
1554 if ( upSibling != NULL )
1555 upSibling->sibling = newItem->hItem;
1557 /* this item is the first child of this parent, adjust parent pointers */
1559 parent->firstChild = newItem->hItem;
1561 infoPtr->TopRootItem= newItem->hItem;
1563 else /* Insert as first child of this parent */
1565 parent->firstChild = newItem->hItem;
1568 /***************************************************************************
1569 * This method does the chaining of the insertion of a treeview item
1571 * If parent is NULL, we're inserting at the root of the list.
1573 static void TREEVIEW_InsertAfter(
1574 TREEVIEW_INFO *infoPtr,
1575 TREEVIEW_ITEM *newItem,
1576 TREEVIEW_ITEM *upSibling,
1577 TREEVIEW_ITEM *parent)
1579 HTREEITEM upSiblingHandle = 0;
1580 HTREEITEM siblingHandle = 0;
1581 TREEVIEW_ITEM *sibling = NULL;
1584 if (newItem == NULL)
1585 ERR("NULL newItem, impossible condition\n");
1587 if (upSibling != NULL) /* Insert after this upsibling for this parent */
1589 /* Store the new item up sibling and sibling item handle */
1590 upSiblingHandle = upSibling->hItem;
1591 siblingHandle = upSibling->sibling;
1592 /* As well as a pointer to the upsibling sibling object */
1593 if ( (INT)upSibling->sibling != 0 )
1594 sibling = &infoPtr->items[(INT)upSibling->sibling];
1596 /* Adjust the up sibling pointer */
1597 upSibling->sibling = newItem->hItem;
1599 /* Adjust the new item pointers */
1600 newItem->upsibling = upSiblingHandle;
1601 newItem->sibling = siblingHandle;
1603 /* Adjust the sibling pointer */
1604 if ( sibling != NULL )
1605 sibling->upsibling = newItem->hItem;
1608 newItem is the last of the level, nothing else to do
1611 else /* Insert as first child of this parent */
1613 parent->firstChild = newItem->hItem;
1616 /***************************************************************************
1617 * Forward the DPA local callback to the treeview owner callback
1619 static INT WINAPI TREEVIEW_CallBackCompare(
1624 /* Forward the call to the client define callback */
1625 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr((HWND)tvInfoPtr);
1626 return (infoPtr->pCallBackSort->lpfnCompare)(
1627 ((TREEVIEW_ITEM*)first)->lParam,
1628 ((TREEVIEW_ITEM*)second)->lParam,
1629 infoPtr->pCallBackSort->lParam);
1632 /***************************************************************************
1633 * Treeview native sort routine: sort on item text.
1635 static INT WINAPI TREEVIEW_SortOnName (
1640 HWND hwnd=(HWND) tvInfoPtr;
1642 TREEVIEW_ITEM *item;
1645 item=(TREEVIEW_ITEM *) first;
1646 if (item->pszText==LPSTR_TEXTCALLBACKA) {
1647 TREEVIEW_SendDispInfoNotify (hwnd, item, TVN_GETDISPINFOA, TVIF_TEXT);
1651 item=(TREEVIEW_ITEM *) second;
1652 if (item->pszText==LPSTR_TEXTCALLBACKA) {
1653 TREEVIEW_SendDispInfoNotify (hwnd, item, TVN_GETDISPINFOA, TVIF_TEXT);
1657 return -strcmp (txt1,txt2);
1660 /***************************************************************************
1661 * Setup the treeview structure with regards of the sort method
1662 * and sort the children of the TV item specified in lParam
1663 * fRecurse: currently unused. Should be zero.
1664 * parent: if pSort!=NULL, should equal pSort->hParent.
1665 * otherwise, item which child items are to be sorted.
1666 * pSort: sort method info. if NULL, sort on item text.
1667 * if non-NULL, sort on item's lParam content, and let the
1668 * application decide what that means. See also TVM_SORTCHILDRENCB.
1671 static LRESULT WINAPI TREEVIEW_Sort (
1678 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
1679 TREEVIEW_ITEM *sortMe = NULL; /* Node for which we sort the children */
1681 /* Obtain the TVSORTBC struct */
1682 infoPtr->pCallBackSort = pSort;
1684 /* undocumented feature: TVI_ROOT means `sort the whole tree' */
1686 if (parent==TVI_ROOT)
1687 parent=infoPtr->TopRootItem;
1689 /* Check for a valid handle to the parent item */
1690 if (!TREEVIEW_ValidItem(infoPtr, parent))
1692 ERR ("invalid item hParent=%x\n", (INT)parent);
1696 /* Obtain the parent node to sort */
1697 sortMe = &infoPtr->items[ (INT)parent ];
1699 /* Make sure there is something to sort */
1700 if ( sortMe->cChildren > 1 )
1702 /* pointer organization */
1703 HDPA sortList = DPA_Create(sortMe->cChildren);
1704 HTREEITEM itemHandle = sortMe->firstChild;
1705 TREEVIEW_ITEM *itemPtr = & infoPtr->items[ (INT)itemHandle ];
1707 /* TREEVIEW_ITEM rechaining */
1713 /* Build the list of item to sort */
1717 sortList, /* the list */
1718 sortMe->cChildren+1, /* force the insertion to be an append */
1719 itemPtr); /* the ptr to store */
1721 /* Get the next sibling */
1722 itemHandle = itemPtr->sibling;
1723 itemPtr = & infoPtr->items[ (INT)itemHandle ];
1724 } while ( itemHandle != NULL );
1726 /* let DPA perform the sort activity */
1729 sortList, /* what */
1730 TREEVIEW_CallBackCompare, /* how */
1734 sortList, /* what */
1735 TREEVIEW_SortOnName, /* how */
1739 * Reorganized TREEVIEW_ITEM structures.
1740 * Note that we know we have at least two elements.
1743 /* Get the first item and get ready to start... */
1744 item = DPA_GetPtr(sortList, count++);
1745 while ( (nextItem = DPA_GetPtr(sortList, count++)) != NULL )
1747 /* link the two current item toghether */
1748 ((TREEVIEW_ITEM*)item)->sibling = ((TREEVIEW_ITEM*)nextItem)->hItem;
1749 ((TREEVIEW_ITEM*)nextItem)->upsibling = ((TREEVIEW_ITEM*)item)->hItem;
1751 if (prevItem == NULL) /* this is the first item, update the parent */
1753 sortMe->firstChild = ((TREEVIEW_ITEM*)item)->hItem;
1754 ((TREEVIEW_ITEM*)item)->upsibling = NULL;
1756 else /* fix the back chaining */
1758 ((TREEVIEW_ITEM*)item)->upsibling = ((TREEVIEW_ITEM*)prevItem)->hItem;
1761 /* get ready for the next one */
1766 /* the last item is pointed to by item and never has a sibling */
1767 ((TREEVIEW_ITEM*)item)->sibling = NULL;
1769 DPA_Destroy(sortList);
1777 /***************************************************************************
1778 * Setup the treeview structure with regards of the sort method
1779 * and sort the children of the TV item specified in lParam
1781 static LRESULT WINAPI TREEVIEW_SortChildrenCB(
1787 LPTVSORTCB pSort=(LPTVSORTCB) lParam;
1789 return TREEVIEW_Sort (hwnd, wParam, pSort->hParent, pSort);
1793 /***************************************************************************
1794 * Sort the children of the TV item specified in lParam.
1796 static LRESULT WINAPI TREEVIEW_SortChildren (
1801 return TREEVIEW_Sort (hwnd, (BOOL) wParam, (HTREEITEM) lParam, NULL);
1806 /* the method used below isn't the most memory-friendly, but it avoids
1807 a lot of memory reallocations */
1809 /* BTW: we waste handle 0; 0 is not an allowed handle. */
1812 TREEVIEW_InsertItemA (HWND hwnd, WPARAM wParam, LPARAM lParam)
1815 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
1816 TVINSERTSTRUCTA *ptdi;
1818 TREEVIEW_ITEM *wineItem, *parentItem, *prevsib, *sibItem;
1819 INT iItem,listItems,i,len;
1821 /* Item to insert */
1822 ptdi = (LPTVINSERTSTRUCTA) lParam;
1824 /* check if memory is available */
1826 if (infoPtr->uNumPtrsAlloced==0) {
1827 infoPtr->items = COMCTL32_Alloc (TVITEM_ALLOC*sizeof (TREEVIEW_ITEM));
1828 infoPtr->freeList= COMCTL32_Alloc ((1+(TVITEM_ALLOC>>5)) * sizeof (INT));
1829 infoPtr->uNumPtrsAlloced=TVITEM_ALLOC;
1830 infoPtr->TopRootItem=(HTREEITEM)1;
1834 * Reallocate contiguous space for items
1836 if (infoPtr->uNumItems == (infoPtr->uNumPtrsAlloced-1) ) {
1837 TREEVIEW_ITEM *oldItems = infoPtr->items;
1838 INT *oldfreeList = infoPtr->freeList;
1840 infoPtr->uNumPtrsAlloced*=2;
1841 infoPtr->items = COMCTL32_Alloc (infoPtr->uNumPtrsAlloced*sizeof (TREEVIEW_ITEM));
1842 infoPtr->freeList= COMCTL32_Alloc ((1+(infoPtr->uNumPtrsAlloced>>5))*sizeof (INT));
1844 memcpy (&infoPtr->items[0], &oldItems[0],
1845 infoPtr->uNumPtrsAlloced/2 * sizeof(TREEVIEW_ITEM));
1846 memcpy (&infoPtr->freeList[0], &oldfreeList[0],
1847 (infoPtr->uNumPtrsAlloced>>6) * sizeof(INT));
1849 COMCTL32_Free (oldItems);
1850 COMCTL32_Free (oldfreeList);
1854 * Reset infoPtr structure with new stat according to current TV picture
1857 infoPtr->uNumItems++;
1858 if ((INT)infoPtr->uMaxHandle==(infoPtr->uNumItems-1)) {
1859 iItem=infoPtr->uNumItems;
1860 infoPtr->uMaxHandle = (HTREEITEM)((INT)infoPtr->uMaxHandle + 1);
1861 } else { /* check freelist */
1862 for (i=0; i<=infoPtr->uNumPtrsAlloced>>5; i++) {
1863 if (infoPtr->freeList[i]) {
1864 iItem=ffs (infoPtr->freeList[i])-1;
1865 tv_clear_bit(iItem,&infoPtr->freeList[i]);
1872 if (TRACE_ON(treeview)) {
1873 for (i=0; i<=infoPtr->uNumPtrsAlloced>>5; i++)
1874 TRACE("%8x\n",infoPtr->freeList[i]);
1877 if (!iItem) ERR("Argh -- can't find free item.\n");
1880 * Find the parent item of the new item
1882 tvItem= & ptdi->DUMMYUNIONNAME.itemex;
1883 wineItem=& infoPtr->items[iItem];
1885 if ((ptdi->hParent==TVI_ROOT) || (ptdi->hParent==0)) {
1887 wineItem->parent = 0;
1888 sibItem = &infoPtr->items [(INT)infoPtr->TopRootItem];
1889 listItems = infoPtr->uNumItems;
1892 parentItem = &infoPtr->items[(INT)ptdi->hParent];
1894 /* Do the insertion here it if it's the only item of this parent */
1895 if (!parentItem->firstChild)
1896 parentItem->firstChild=(HTREEITEM)iItem;
1898 wineItem->parent = ptdi->hParent;
1899 sibItem = &infoPtr->items [(INT)parentItem->firstChild];
1900 listItems = parentItem->cChildren;
1901 parentItem->cChildren++;
1905 /* NOTE: I am moving some setup of the wineItem object that was initialy
1906 * done at the end of the function since some of the values are
1907 * required by the Callback sorting
1910 if (tvItem->mask & TVIF_TEXT)
1913 * Setup the item text stuff here since it's required by the Sort method
1914 * when the insertion are ordered
1916 if (tvItem->pszText!=LPSTR_TEXTCALLBACKA)
1918 TRACE("(%p,%s)\n", &tvItem->pszText, tvItem->pszText);
1919 len = lstrlenA (tvItem->pszText)+1;
1920 wineItem->pszText= COMCTL32_Alloc (len+1);
1921 lstrcpyA (wineItem->pszText, tvItem->pszText);
1922 wineItem->cchTextMax=len;
1926 TRACE("LPSTR_TEXTCALLBACK\n");
1927 wineItem->pszText = LPSTR_TEXTCALLBACKA;
1928 wineItem->cchTextMax = 0;
1932 if (tvItem->mask & TVIF_PARAM)
1933 wineItem->lParam=tvItem->lParam;
1936 wineItem->upsibling=0; /* needed in case we're the first item in a list */
1937 wineItem->sibling=0;
1938 wineItem->firstChild=0;
1939 wineItem->hItem=(HTREEITEM)iItem;
1944 switch ((DWORD) ptdi->hInsertAfter) {
1945 case (DWORD) TVI_FIRST:
1946 if (sibItem==wineItem) break;
1947 if (wineItem->parent) {
1948 wineItem->sibling=parentItem->firstChild;
1949 parentItem->firstChild=(HTREEITEM)iItem;
1951 wineItem->sibling=infoPtr->TopRootItem;
1952 infoPtr->TopRootItem=(HTREEITEM)iItem;
1954 sibItem->upsibling=(HTREEITEM)iItem;
1957 case (DWORD) TVI_SORT:
1958 if (sibItem==wineItem)
1960 * This item is the first child of the level and it
1961 * has already been inserted
1966 TREEVIEW_ITEM *aChild;
1969 TREEVIEW_ITEM *previousChild = NULL;
1970 BOOL bItemInserted = FALSE;
1973 aChild = &infoPtr->items[(INT)parentItem->firstChild];
1975 aChild = &infoPtr->items[(INT)infoPtr->TopRootItem];
1977 /* lookup the text if using LPSTR_TEXTCALLBACKs */
1978 if (wineItem->pszText==LPSTR_TEXTCALLBACKA) {
1979 TREEVIEW_SendDispInfoNotify (hwnd, wineItem, TVN_GETDISPINFOA, TVIF_TEXT);
1982 /* Iterate the parent children to see where we fit in */
1983 while ( aChild != NULL )
1987 /* lookup the text if using LPSTR_TEXTCALLBACKs */
1988 if (aChild->pszText==LPSTR_TEXTCALLBACKA) {
1989 TREEVIEW_SendDispInfoNotify (hwnd, aChild, TVN_GETDISPINFOA, TVIF_TEXT);
1992 comp = strcmp(wineItem->pszText, aChild->pszText);
1993 if ( comp < 0 ) /* we are smaller than the current one */
1995 TREEVIEW_InsertBefore(infoPtr, wineItem, aChild, parentItem);
1996 bItemInserted = TRUE;
1999 else if ( comp > 0 ) /* we are bigger than the current one */
2001 previousChild = aChild;
2002 aChild = (aChild->sibling == 0) /* This will help us to exit */
2003 ? NULL /* if there is no more sibling */
2004 : &infoPtr->items[(INT)aChild->sibling];
2006 /* Look at the next item */
2009 else if ( comp == 0 )
2012 * An item with this name is already existing, therefore,
2013 * we add after the one we found
2015 TREEVIEW_InsertAfter(infoPtr, wineItem, aChild, parentItem);
2016 bItemInserted = TRUE;
2022 * we reach the end of the child list and the item as not
2023 * yet been inserted, therefore, insert it after the last child.
2025 if ( (! bItemInserted ) && (aChild == NULL) )
2026 TREEVIEW_InsertAfter(infoPtr, wineItem, previousChild, parentItem);
2032 case (DWORD) TVI_LAST:
2033 if (sibItem==wineItem) break;
2034 while (sibItem->sibling) {
2036 sibItem=&infoPtr->items [(INT)sibItem->sibling];
2038 sibItem->sibling=(HTREEITEM)iItem;
2039 wineItem->upsibling=sibItem->hItem;
2043 TREEVIEW_ITEM *localsibItem = sibItem;
2044 while ((localsibItem->sibling) &&
2045 (localsibItem->hItem!=ptdi->hInsertAfter))
2047 prevsib=localsibItem;
2048 localsibItem=&infoPtr->items [(INT)localsibItem->sibling];
2050 if (localsibItem->hItem!=ptdi->hInsertAfter) {
2051 WARN("tried to insert item after nonexisting handle %d treating as TVI_LAST.\n",
2052 (INT) ptdi->hInsertAfter);
2054 * retry placing it last
2056 if (sibItem==wineItem) break;
2057 while (sibItem->sibling) {
2059 sibItem=&infoPtr->items [(INT)sibItem->sibling];
2061 sibItem->sibling=(HTREEITEM)iItem;
2062 wineItem->upsibling=sibItem->hItem;
2065 prevsib=localsibItem;
2066 if (localsibItem->sibling) {
2067 localsibItem=&infoPtr->items [(INT)localsibItem->sibling];
2068 localsibItem->upsibling=(HTREEITEM)iItem;
2069 wineItem->sibling=localsibItem->hItem;
2071 prevsib->sibling=(HTREEITEM)iItem;
2072 wineItem->upsibling=prevsib->hItem;
2079 /* Fill in info structure */
2081 TRACE("new item %d; parent %d, mask %x\n", iItem,
2082 (INT)wineItem->parent,tvItem->mask);
2084 wineItem->mask=tvItem->mask;
2085 wineItem->iIntegral=1;
2087 if (tvItem->mask & TVIF_CHILDREN) {
2088 wineItem->cChildren=tvItem->cChildren;
2089 if (tvItem->cChildren==I_CHILDRENCALLBACK)
2090 FIXME(" I_CHILDRENCALLBACK not supported\n");
2093 wineItem->expandBox.left = 0; /* Initialize the expandBox */
2094 wineItem->expandBox.top = 0;
2095 wineItem->expandBox.right = 0;
2096 wineItem->expandBox.bottom = 0;
2098 if (tvItem->mask & TVIF_IMAGE)
2099 wineItem->iImage=tvItem->iImage;
2101 /* If the application sets TVIF_INTEGRAL without
2102 supplying a TVITEMEX structure, it's toast */
2104 if (tvItem->mask & TVIF_INTEGRAL)
2105 wineItem->iIntegral=tvItem->iIntegral;
2107 if (tvItem->mask & TVIF_SELECTEDIMAGE)
2108 wineItem->iSelectedImage=tvItem->iSelectedImage;
2110 if (tvItem->mask & TVIF_STATE) {
2111 TRACE("item state: %x ->%x\n", wineItem->state, tvItem->state);
2112 TRACE("statemask: %x ->%x\n", wineItem->stateMask, tvItem->stateMask);
2113 wineItem->state=tvItem->state;
2114 wineItem->stateMask=tvItem->stateMask;
2117 TREEVIEW_QueueRefresh (hwnd);
2119 return (LRESULT) iItem;
2124 TREEVIEW_InsertItemW(HWND hwnd, WPARAM wParam, LPARAM lParam)
2126 TVINSERTSTRUCTW *tvisW;
2127 TVINSERTSTRUCTA tvisA;
2130 tvisW = (LPTVINSERTSTRUCTW)lParam;
2132 tvisA.hParent = tvisW->hParent;
2133 tvisA.hInsertAfter = tvisW->hInsertAfter;
2135 tvisA.DUMMYUNIONNAME.item.mask = tvisW->DUMMYUNIONNAME.item.mask;
2136 tvisA.DUMMYUNIONNAME.item.hItem = tvisW->DUMMYUNIONNAME.item.hItem;
2137 tvisA.DUMMYUNIONNAME.item.state = tvisW->DUMMYUNIONNAME.item.state;
2138 tvisA.DUMMYUNIONNAME.item.stateMask = tvisW->DUMMYUNIONNAME.item.stateMask;
2139 tvisA.DUMMYUNIONNAME.item.cchTextMax = tvisW->DUMMYUNIONNAME.item.cchTextMax;
2141 if(tvisW->DUMMYUNIONNAME.item.pszText)
2143 if (tvisW->DUMMYUNIONNAME.item.pszText!=LPSTR_TEXTCALLBACKW)
2145 int len = lstrlenW (tvisW->DUMMYUNIONNAME.item.pszText)+1;
2146 tvisA.DUMMYUNIONNAME.item.pszText = COMCTL32_Alloc (len);
2147 lstrcpyWtoA (tvisA.DUMMYUNIONNAME.item.pszText,
2148 tvisW->DUMMYUNIONNAME.item.pszText );
2152 tvisA.DUMMYUNIONNAME.item.pszText = LPSTR_TEXTCALLBACKA;
2153 tvisA.DUMMYUNIONNAME.item.cchTextMax = 0;
2157 tvisA.DUMMYUNIONNAME.item.iImage = tvisW->DUMMYUNIONNAME.item.iImage;
2158 tvisA.DUMMYUNIONNAME.item.iSelectedImage = tvisW->DUMMYUNIONNAME.item.iSelectedImage;
2159 tvisA.DUMMYUNIONNAME.item.cChildren = tvisW->DUMMYUNIONNAME.item.cChildren;
2160 tvisA.DUMMYUNIONNAME.item.lParam = tvisW->DUMMYUNIONNAME.item.lParam;
2162 lRes = TREEVIEW_InsertItemA(hwnd,wParam,(LPARAM)&tvisA);
2164 if (tvisA.DUMMYUNIONNAME.item.pszText!=LPSTR_TEXTCALLBACKA)
2166 COMCTL32_Free(tvisA.DUMMYUNIONNAME.item.pszText);
2175 TREEVIEW_DeleteItem (HWND hwnd, WPARAM wParam, LPARAM lParam)
2177 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
2179 TREEVIEW_ITEM *wineItem;
2181 TRACE("item = %08lx\n", lParam);
2183 if (lParam == (INT)TVI_ROOT) {
2184 TREEVIEW_RemoveTree (hwnd);
2186 iItem= (INT) lParam;
2187 wineItem = TREEVIEW_ValidItem (infoPtr, (HTREEITEM)iItem);
2188 if (!wineItem) return FALSE;
2190 if (wineItem->pszText==LPSTR_TEXTCALLBACKA)
2191 TRACE("LPSTR_TEXTCALLBACK\n");
2193 TRACE("%s\n",wineItem->pszText);
2194 TREEVIEW_RemoveItem (hwnd, wineItem);
2197 TREEVIEW_QueueRefresh (hwnd);
2205 TREEVIEW_GetIndent (HWND hwnd)
2207 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
2210 return infoPtr->uIndent;
2214 TREEVIEW_SetIndent (HWND hwnd, WPARAM wParam)
2216 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
2220 newIndent=(INT) wParam;
2221 if (newIndent < MINIMUM_INDENT) newIndent=MINIMUM_INDENT;
2222 infoPtr->uIndent=newIndent;
2228 TREEVIEW_GetToolTips (HWND hwnd)
2231 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
2234 return infoPtr->hwndToolTip;
2239 TREEVIEW_SetToolTips (HWND hwnd, WPARAM wParam)
2242 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
2246 prevToolTip=infoPtr->hwndToolTip;
2247 infoPtr->hwndToolTip= (HWND) wParam;
2253 static LRESULT CALLBACK
2254 TREEVIEW_GetEditControl (HWND hwnd)
2257 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
2259 return infoPtr->hwndEdit;
2263 TREEVIEW_Edit_SubclassProc (HWND hwnd, UINT uMsg, WPARAM wParam,
2271 HDC hdc = (HDC) wParam;
2272 GetClientRect (hwnd, &rc);
2273 Rectangle (hdc, rc.left, rc.top, rc.right, rc.bottom);
2279 return DLGC_WANTARROWS | DLGC_WANTALLKEYS;
2283 if (wParam == VK_ESCAPE)
2285 TREEVIEW_EndEditLabelNow(GetParent(hwnd), (WPARAM)TRUE, 0);
2288 else if (wParam == VK_RETURN)
2289 TREEVIEW_EndEditLabelNow(GetParent(hwnd), (WPARAM)FALSE, 0);
2296 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(GetParent(hwnd));
2298 return CallWindowProcA (infoPtr->wpEditOrig, hwnd, uMsg, wParam, lParam);
2309 /* should handle edit control messages here */
2312 TREEVIEW_Command (HWND hwnd, WPARAM wParam, LPARAM lParam)
2315 TRACE("%x %ld\n",wParam, lParam);
2317 switch (HIWORD(wParam))
2322 * Adjust the edit window size
2324 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
2325 TREEVIEW_ITEM *editItem = TREEVIEW_ValidItem(infoPtr, infoPtr->editItem);
2326 INT iLength = GetWindowTextLengthA(infoPtr->hwndEdit);
2327 HDC hdc = GetDC(infoPtr->hwndEdit);
2330 if ( GetTextMetricsA(hdc, &tm) )
2332 LONG newWidth = (iLength * tm.tmAveCharWidth) + 15;
2337 editItem->text.left - 2,
2338 editItem->text.top - 1,
2340 editItem->text.bottom - editItem->text.top + 3,
2343 ReleaseDC(hwnd, hdc);
2349 /* TREEVIEW_EndEditLabelNow(hwnd, (WPARAM)FALSE, 0);
2354 return SendMessageA (GetParent (hwnd), WM_COMMAND, wParam, lParam);
2361 TREEVIEW_Size (HWND hwnd, WPARAM wParam, LPARAM lParam)
2364 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
2366 if (infoPtr->bAutoSize)
2368 infoPtr->bAutoSize = FALSE;
2371 infoPtr->bAutoSize = TRUE;
2373 if (wParam == SIZE_RESTORED)
2375 infoPtr->uTotalWidth = LOWORD (lParam);
2376 infoPtr->uTotalHeight = HIWORD (lParam);
2378 FIXME("WM_SIZE flag %x %lx not handled\n", wParam, lParam);
2381 TREEVIEW_QueueRefresh (hwnd);
2388 TREEVIEW_StyleChanged (HWND hwnd, WPARAM wParam, LPARAM lParam)
2392 TRACE("(%x %lx)\n",wParam,lParam);
2394 TREEVIEW_Refresh (hwnd, hdc);
2395 ReleaseDC(hwnd,hdc);
2401 TREEVIEW_Create (HWND hwnd, WPARAM wParam, LPARAM lParam)
2403 TREEVIEW_INFO *infoPtr;
2404 DWORD dwStyle = GetWindowLongA (hwnd, GWL_STYLE);
2409 TRACE("wnd %x, style %lx\n",hwnd,dwStyle);
2410 /* allocate memory for info structure */
2411 infoPtr = (TREEVIEW_INFO *) COMCTL32_Alloc (sizeof(TREEVIEW_INFO));
2413 SetWindowLongA( hwnd, 0, (DWORD)infoPtr);
2415 if (infoPtr == NULL) {
2416 ERR("could not allocate info memory!\n");
2420 if ((TREEVIEW_INFO*) GetWindowLongA( hwnd, 0) != infoPtr) {
2421 ERR("pointer assignment error!\n");
2427 /* set default settings */
2428 infoPtr->uInternalStatus=0;
2429 infoPtr->uNumItems=0;
2430 infoPtr->clrBk = GetSysColor (COLOR_WINDOW);
2431 infoPtr->clrText = GetSysColor (COLOR_WINDOWTEXT);
2432 infoPtr->clrLine = GetSysColor (COLOR_WINDOWTEXT);
2433 infoPtr->clrInsertMark = GetSysColor (COLOR_BTNTEXT);
2436 infoPtr->uIndent = 15;
2437 infoPtr->himlNormal = NULL;
2438 infoPtr->himlState = NULL;
2439 infoPtr->uItemHeight = -1;
2440 GetTextMetricsA (hdc, &tm);
2441 infoPtr->hFont = GetStockObject (DEFAULT_GUI_FONT);
2442 GetObjectA (infoPtr->hFont, sizeof (LOGFONTA), &logFont);
2443 logFont.lfWeight=FW_BOLD;
2444 infoPtr->hBoldFont = CreateFontIndirectA (&logFont);
2446 infoPtr->items = NULL;
2447 infoPtr->selectedItem=0;
2448 infoPtr->clrText=-1; /* use system color */
2449 infoPtr->dropItem=0;
2450 infoPtr->insertMarkItem=0;
2451 infoPtr->insertBeforeorAfter=0;
2452 infoPtr->pCallBackSort=NULL;
2453 infoPtr->uScrollTime = 300; /* milliseconds */
2454 infoPtr->wpEditOrig = NULL; /* we haven't subclassed anything yet */
2456 infoPtr->hwndToolTip=0;
2457 if (!(dwStyle & TVS_NOTOOLTIPS)) { /* Create tooltip control */
2460 infoPtr->hwndToolTip =
2461 CreateWindowExA (0, TOOLTIPS_CLASSA, NULL, 0,
2462 CW_USEDEFAULT, CW_USEDEFAULT,
2463 CW_USEDEFAULT, CW_USEDEFAULT,
2466 /* Send NM_TOOLTIPSCREATED notification */
2467 if (infoPtr->hwndToolTip) {
2468 NMTOOLTIPSCREATED nmttc;
2470 nmttc.hdr.hwndFrom = hwnd;
2471 nmttc.hdr.idFrom = GetWindowLongA( hwnd, GWL_ID);
2472 nmttc.hdr.code = NM_TOOLTIPSCREATED;
2473 nmttc.hwndToolTips = infoPtr->hwndToolTip;
2475 SendMessageA (GetParent (hwnd), WM_NOTIFY,
2476 (WPARAM) GetWindowLongA( hwnd, GWL_ID), (LPARAM)&nmttc);
2479 ZeroMemory (&ti, sizeof(TTTOOLINFOA));
2480 ti.cbSize = sizeof(TTTOOLINFOA);
2481 ti.uFlags = TTF_IDISHWND | TTF_TRACK | TTF_TRANSPARENT ;
2484 ti.lpszText = "Test"; /* LPSTR_TEXTCALLBACK; */
2485 SetRectEmpty (&ti.rect);
2487 SendMessageA (infoPtr->hwndToolTip, TTM_ADDTOOLA, 0, (LPARAM)&ti);
2490 infoPtr->hwndEdit = CreateWindowExA (
2494 WS_CHILD | WS_BORDER | ES_AUTOHSCROLL |
2495 ES_WANTRETURN | ES_LEFT,
2498 0,0,0); /* FIXME: (HMENU)IDTVEDIT,pcs->hInstance,0);*/
2500 SendMessageA ( infoPtr->hwndEdit, WM_SETFONT, infoPtr->hFont, FALSE);
2501 infoPtr->wpEditOrig = (WNDPROC)SetWindowLongA (
2504 (LONG) TREEVIEW_Edit_SubclassProc);
2506 if (dwStyle & TVS_CHECKBOXES) {
2510 infoPtr->himlState =
2511 ImageList_Create (16, 16,ILC_COLOR|ILC_MASK, 15, 1);
2513 hbmLoad = LoadBitmapA (COMCTL32_hModule, MAKEINTRESOURCEA(IDT_CHECK));
2514 TRACE ("%x\n",hbmLoad);
2515 nIndex = ImageList_AddMasked (infoPtr->himlState, hbmLoad, CLR_DEFAULT);
2516 TRACE ("%d\n",nIndex);
2517 DeleteObject (hbmLoad);
2519 ReleaseDC (hwnd, hdc);
2526 TREEVIEW_Destroy (HWND hwnd)
2528 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
2531 TREEVIEW_RemoveTree (hwnd);
2532 SetWindowLongA (hwnd, 0, (DWORD)NULL);
2534 if (infoPtr->Timer & TV_REFRESH_TIMER_SET)
2535 KillTimer (hwnd, TV_REFRESH_TIMER);
2536 if (infoPtr->hwndToolTip)
2537 DestroyWindow (infoPtr->hwndToolTip);
2539 COMCTL32_Free (infoPtr);
2545 TREEVIEW_Paint (HWND hwnd, WPARAM wParam, LPARAM lParam)
2551 hdc = wParam==0 ? BeginPaint (hwnd, &ps) : (HDC)wParam;
2552 TREEVIEW_Refresh (hwnd, hdc);
2553 if(!wParam) EndPaint (hwnd, &ps);
2560 TREEVIEW_SetFocus (HWND hwnd, WPARAM wParam, LPARAM lParam)
2562 TREEVIEW_SendSimpleNotify (hwnd, NM_SETFOCUS);
2563 InvalidateRect(hwnd, NULL, FALSE);
2568 TREEVIEW_KillFocus (HWND hwnd, WPARAM wParam, LPARAM lParam)
2570 TREEVIEW_SendSimpleNotify (hwnd, NM_KILLFOCUS);
2571 InvalidateRect(hwnd, NULL, FALSE);
2576 TREEVIEW_EraseBackground (HWND hwnd, WPARAM wParam, LPARAM lParam)
2578 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
2579 HBRUSH hBrush = CreateSolidBrush (infoPtr->clrBk);
2583 GetClientRect (hwnd, &rect);
2584 FillRect ((HDC)wParam, &rect, hBrush);
2585 DeleteObject (hBrush);
2601 TREEVIEW_SendSimpleNotify (HWND hwnd, UINT code)
2606 nmhdr.hwndFrom = hwnd;
2607 nmhdr.idFrom = GetWindowLongA( hwnd, GWL_ID);
2610 return (BOOL) SendMessageA (GetParent (hwnd), WM_NOTIFY,
2611 (WPARAM)nmhdr.idFrom, (LPARAM)&nmhdr);
2617 TREEVIEW_SendTreeviewNotify (HWND hwnd, UINT code, UINT action,
2618 HTREEITEM oldItem, HTREEITEM newItem)
2621 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
2623 TREEVIEW_ITEM *wineItem;
2625 TRACE("code:%x action:%x olditem:%x newitem:%x\n",
2626 code,action,(INT)oldItem,(INT)newItem);
2627 nmhdr.hdr.hwndFrom = hwnd;
2628 nmhdr.hdr.idFrom = GetWindowLongA( hwnd, GWL_ID);
2629 nmhdr.hdr.code = code;
2630 nmhdr.action = action;
2632 wineItem=& infoPtr->items[(INT)oldItem];
2633 nmhdr.itemOld.mask = wineItem->mask;
2634 nmhdr.itemOld.hItem = wineItem->hItem;
2635 nmhdr.itemOld.state = wineItem->state;
2636 nmhdr.itemOld.stateMask = wineItem->stateMask;
2637 nmhdr.itemOld.iImage = wineItem->iImage;
2638 nmhdr.itemOld.pszText = wineItem->pszText;
2639 nmhdr.itemOld.cchTextMax= wineItem->cchTextMax;
2640 nmhdr.itemOld.iImage = wineItem->iImage;
2641 nmhdr.itemOld.iSelectedImage = wineItem->iSelectedImage;
2642 nmhdr.itemOld.cChildren = wineItem->cChildren;
2643 nmhdr.itemOld.lParam = wineItem->lParam;
2647 wineItem=& infoPtr->items[(INT)newItem];
2648 nmhdr.itemNew.mask = wineItem->mask;
2649 nmhdr.itemNew.hItem = wineItem->hItem;
2650 nmhdr.itemNew.state = wineItem->state;
2651 nmhdr.itemNew.stateMask = wineItem->stateMask;
2652 nmhdr.itemNew.iImage = wineItem->iImage;
2653 nmhdr.itemNew.pszText = wineItem->pszText;
2654 nmhdr.itemNew.cchTextMax= wineItem->cchTextMax;
2655 nmhdr.itemNew.iImage = wineItem->iImage;
2656 nmhdr.itemNew.iSelectedImage = wineItem->iSelectedImage;
2657 nmhdr.itemNew.cChildren = wineItem->cChildren;
2658 nmhdr.itemNew.lParam = wineItem->lParam;
2664 return (BOOL)SendMessageA (GetParent (hwnd), WM_NOTIFY,
2665 (WPARAM) GetWindowLongA( hwnd, GWL_ID), (LPARAM)&nmhdr);
2670 TREEVIEW_SendTreeviewDnDNotify (HWND hwnd, UINT code, HTREEITEM dragItem,
2673 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
2675 TREEVIEW_ITEM *wineItem;
2677 TRACE("code:%x dragitem:%x\n", code,(INT)dragItem);
2679 nmhdr.hdr.hwndFrom = hwnd;
2680 nmhdr.hdr.idFrom = GetWindowLongA( hwnd, GWL_ID);
2681 nmhdr.hdr.code = code;
2683 wineItem=& infoPtr->items[(INT)dragItem];
2684 nmhdr.itemNew.mask = wineItem->mask;
2685 nmhdr.itemNew.hItem = wineItem->hItem;
2686 nmhdr.itemNew.state = wineItem->state;
2687 nmhdr.itemNew.lParam = wineItem->lParam;
2689 nmhdr.ptDrag.x = pt.x;
2690 nmhdr.ptDrag.y = pt.y;
2692 return (BOOL)SendMessageA (GetParent (hwnd), WM_NOTIFY,
2693 (WPARAM) GetWindowLongA( hwnd, GWL_ID), (LPARAM)&nmhdr);
2700 TREEVIEW_SendDispInfoNotify (HWND hwnd, TREEVIEW_ITEM *wineItem,
2701 UINT code, UINT what)
2707 TRACE("item %d, action %x, state %d\n",
2708 (INT)wineItem->hItem,
2710 (INT)wineItem->state);
2712 tvdi.hdr.hwndFrom = hwnd;
2713 tvdi.hdr.idFrom = GetWindowLongA( hwnd, GWL_ID);
2714 tvdi.hdr.code = code;
2715 tvdi.item.mask = what;
2716 tvdi.item.hItem = wineItem->hItem;
2717 tvdi.item.state = wineItem->state;
2718 tvdi.item.lParam = wineItem->lParam;
2719 tvdi.item.pszText = COMCTL32_Alloc (128*sizeof(char));
2720 tvdi.item.cchTextMax = 128;
2721 buf = tvdi.item.pszText;
2723 retval=(BOOL)SendMessageA (
2726 (WPARAM)tvdi.hdr.idFrom,
2729 if (what & TVIF_TEXT) {
2730 wineItem->pszText = tvdi.item.pszText;
2731 if (buf==tvdi.item.pszText) {
2732 wineItem->cchTextMax = 128;
2734 TRACE("user-supplied buffer\n");
2735 COMCTL32_Free (buf);
2736 wineItem->cchTextMax = 0;
2739 if (what & TVIF_SELECTEDIMAGE)
2740 wineItem->iSelectedImage = tvdi.item.iSelectedImage;
2741 if (what & TVIF_IMAGE)
2742 wineItem->iImage = tvdi.item.iImage;
2743 if (what & TVIF_CHILDREN)
2744 wineItem->cChildren = tvdi.item.cChildren;
2752 TREEVIEW_SendCustomDrawNotify (HWND hwnd, DWORD dwDrawStage, HDC hdc,
2755 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
2756 NMTVCUSTOMDRAW nmcdhdr;
2757 LPNMCUSTOMDRAW nmcd;
2759 TRACE("drawstage:%lx hdc:%x\n", dwDrawStage, hdc);
2761 nmcd= & nmcdhdr.nmcd;
2762 nmcd->hdr.hwndFrom = hwnd;
2763 nmcd->hdr.idFrom = GetWindowLongA( hwnd, GWL_ID);
2764 nmcd->hdr.code = NM_CUSTOMDRAW;
2765 nmcd->dwDrawStage= dwDrawStage;
2767 nmcd->rc.left = rc.left;
2768 nmcd->rc.right = rc.right;
2769 nmcd->rc.bottom = rc.bottom;
2770 nmcd->rc.top = rc.top;
2771 nmcd->dwItemSpec = 0;
2772 nmcd->uItemState = 0;
2773 nmcd->lItemlParam= 0;
2774 nmcdhdr.clrText = infoPtr->clrText;
2775 nmcdhdr.clrTextBk= infoPtr->clrBk;
2778 return (BOOL)SendMessageA (GetParent (hwnd), WM_NOTIFY,
2779 (WPARAM) GetWindowLongA( hwnd, GWL_ID), (LPARAM)&nmcdhdr);
2785 /* FIXME: need to find out when the flags in uItemState need to be set */
2788 TREEVIEW_SendCustomDrawItemNotify (HWND hwnd, HDC hdc,
2789 TREEVIEW_ITEM *wineItem, UINT uItemDrawState)
2791 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
2792 NMTVCUSTOMDRAW nmcdhdr;
2793 LPNMCUSTOMDRAW nmcd;
2794 DWORD dwDrawStage,dwItemSpec;
2798 dwDrawStage=CDDS_ITEM | uItemDrawState;
2799 dwItemSpec=(DWORD)wineItem->hItem;
2801 if (wineItem->hItem==infoPtr->selectedItem) uItemState|=CDIS_SELECTED;
2802 if (wineItem->hItem==infoPtr->focusItem) uItemState|=CDIS_FOCUS;
2803 if (wineItem->hItem==infoPtr->hotItem) uItemState|=CDIS_HOT;
2805 nmcd= & nmcdhdr.nmcd;
2806 nmcd->hdr.hwndFrom = hwnd;
2807 nmcd->hdr.idFrom = GetWindowLongA( hwnd, GWL_ID);
2808 nmcd->hdr.code = NM_CUSTOMDRAW;
2809 nmcd->dwDrawStage= dwDrawStage;
2811 nmcd->rc.left = wineItem->rect.left;
2812 nmcd->rc.right = wineItem->rect.right;
2813 nmcd->rc.bottom = wineItem->rect.bottom;
2814 nmcd->rc.top = wineItem->rect.top;
2815 nmcd->dwItemSpec = dwItemSpec;
2816 nmcd->uItemState = uItemState;
2817 nmcd->lItemlParam= wineItem->lParam;
2818 nmcdhdr.clrText = infoPtr->clrText;
2819 nmcdhdr.clrTextBk= infoPtr->clrBk;
2820 nmcdhdr.iLevel = wineItem->iLevel;
2822 TRACE("drawstage:%lx hdc:%x item:%lx, itemstate:%x, lItemlParam:%lx\n",
2823 nmcd->dwDrawStage, nmcd->hdc, nmcd->dwItemSpec,
2824 nmcd->uItemState, nmcd->lItemlParam);
2826 retval=SendMessageA (GetParent (hwnd), WM_NOTIFY,
2827 (WPARAM) GetWindowLongA( hwnd, GWL_ID), (LPARAM)&nmcdhdr);
2829 infoPtr->clrText=nmcdhdr.clrText;
2830 infoPtr->clrBk =nmcdhdr.clrTextBk;
2831 return (BOOL) retval;
2836 /* Note:If the specified item is the child of a collapsed parent item,
2837 the parent's list of child items is (recursively) expanded to reveal the
2838 specified item. This is mentioned for TREEVIEW_SelectItem; don't
2839 know if it also applies here.
2843 TREEVIEW_Expand (HWND hwnd, WPARAM wParam, LPARAM lParam)
2845 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
2846 TREEVIEW_ITEM *wineItem;
2850 flag = (UINT) wParam;
2851 expand = (INT) lParam;
2853 wineItem = TREEVIEW_ValidItem (infoPtr, (HTREEITEM)expand);
2857 if (!wineItem->cChildren)
2860 if (wineItem->pszText==LPSTR_TEXTCALLBACKA)
2861 TRACE ("For item %d, flags %d, state %d\n",
2862 expand, flag, wineItem->state);
2864 TRACE("For (%s) item:%d, flags %x, state:%d\n",
2865 wineItem->pszText, flag, expand, wineItem->state);
2867 if (wineItem->cChildren==I_CHILDRENCALLBACK) {
2868 FIXME("we don't handle I_CHILDRENCALLBACK yet\n");
2872 if (flag == TVE_TOGGLE) { /* FIXME: check exact behaviour here */
2873 flag &= ~TVE_TOGGLE; /* ie: bitwise ops or 'case' ops */
2874 if (wineItem->state & TVIS_EXPANDED)
2875 flag |= TVE_COLLAPSE;
2882 case TVE_COLLAPSERESET:
2883 TRACE(" case TVE_COLLAPSERESET\n");
2884 if (!wineItem->state & TVIS_EXPANDED)
2887 wineItem->state &= ~(TVIS_EXPANDEDONCE | TVIS_EXPANDED);
2888 TREEVIEW_RemoveAllChildren (hwnd, wineItem);
2892 TRACE(" case TVE_COLLAPSE\n");
2893 if (!wineItem->state & TVIS_EXPANDED)
2896 wineItem->state &= ~TVIS_EXPANDED;
2900 TRACE(" case TVE_EXPAND\n");
2901 if (wineItem->state & TVIS_EXPANDED)
2904 TRACE(" is not expanded...\n");
2906 if (!(wineItem->state & TVIS_EXPANDEDONCE))
2908 TRACE(" and has never been expanded...\n");
2909 wineItem->state |= TVIS_EXPANDED;
2911 /* this item has never been expanded */
2912 if (TREEVIEW_SendTreeviewNotify (
2919 TRACE(" TVN_ITEMEXPANDINGA returned TRUE, exiting...\n");
2924 * Since the TVN_ITEMEXPANDINGA message may has caused the parent to
2925 * insert new items which in turn may have cause items placeholder
2926 * reallocation, I reassign the current item pointer so we have
2927 * something valid to work with...
2928 * However, this should not be necessary,
2929 * investigation required in TREEVIEW_InsertItemA
2931 wineItem = TREEVIEW_ValidItem (infoPtr, (HTREEITEM)expand);
2935 "Catastropic situation, cannot retreive item #%d\n",
2940 wineItem->state |= TVIS_EXPANDEDONCE;
2941 TRACE(" TVN_ITEMEXPANDINGA sent...\n");
2943 TREEVIEW_SendTreeviewNotify (
2950 TRACE(" TVN_ITEMEXPANDEDA sent...\n");
2955 /* this item has already been expanded */
2956 wineItem->state |= TVIS_EXPANDED;
2960 case TVE_EXPANDPARTIAL:
2961 TRACE(" case TVE_EXPANDPARTIAL\n");
2962 FIXME("TVE_EXPANDPARTIAL not implemented\n");
2963 wineItem->state ^=TVIS_EXPANDED;
2964 wineItem->state |=TVIS_EXPANDEDONCE;
2968 TRACE("Exiting, Item %d state is now %d...\n",
2972 TREEVIEW_QueueRefresh (hwnd);
2978 static TREEVIEW_ITEM *
2979 TREEVIEW_HitTestPoint (HWND hwnd, POINT pt)
2981 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
2982 TREEVIEW_ITEM *wineItem;
2985 GetClientRect (hwnd, &rect);
2987 if (!infoPtr->firstVisible) return NULL;
2989 wineItem=&infoPtr->items [(INT)infoPtr->firstVisible];
2991 while ((wineItem!=NULL) && (pt.y > wineItem->rect.bottom))
2992 wineItem=TREEVIEW_GetNextListItem (infoPtr,wineItem);
3004 TREEVIEW_HitTest (HWND hwnd, LPARAM lParam)
3006 LPTVHITTESTINFO lpht=(LPTVHITTESTINFO) lParam;
3007 TREEVIEW_ITEM *wineItem;
3011 GetClientRect (hwnd, &rect);
3015 if (x < rect.left) status|=TVHT_TOLEFT;
3016 if (x > rect.right) status|=TVHT_TORIGHT;
3017 if (y < rect.top ) status|=TVHT_ABOVE;
3018 if (y > rect.bottom) status|=TVHT_BELOW;
3025 wineItem=TREEVIEW_HitTestPoint (hwnd, lpht->pt);
3027 lpht->flags=TVHT_NOWHERE;
3033 if (x < wineItem->expandBox.left) {
3034 lpht->flags |= TVHT_ONITEMINDENT;
3037 if ( PtInRect ( &wineItem->expandBox, lpht->pt)) {
3038 lpht->flags |= TVHT_ONITEMBUTTON;
3041 if ( PtInRect ( &wineItem->bitmap, lpht->pt)) {
3042 lpht->flags |= TVHT_ONITEMICON;
3045 if ( PtInRect ( &wineItem->statebitmap, lpht->pt)) {
3046 lpht->flags |= TVHT_ONITEMSTATEICON;
3049 if ( PtInRect ( &wineItem->text, lpht->pt)) {
3050 lpht->flags |= TVHT_ONITEMLABEL;
3054 lpht->flags|=TVHT_ONITEMRIGHT;
3058 lpht->hItem=wineItem->hItem;
3059 TRACE ("(%ld,%ld):result %x\n",lpht->pt.x,lpht->pt.y,lpht->flags);
3061 return (LRESULT) wineItem->hItem;
3065 TREEVIEW_EditLabelA (HWND hwnd, WPARAM wParam, LPARAM lParam)
3067 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
3068 TREEVIEW_ITEM *wineItem;
3071 * If the style allow editing...
3073 if ( GetWindowLongA( hwnd, GWL_STYLE) & TVS_EDITLABELS )
3076 if ( infoPtr->editItem == 0 ) /* If we are not curently editing */
3078 wineItem = TREEVIEW_ValidItem(infoPtr,(HTREEITEM) lParam);
3079 if ( wineItem == NULL )
3081 ERR("Cannot get valid TREEVIEW_ITEM for lParam\n");
3085 TRACE("Edit started for %s.\n", wineItem->pszText);
3086 infoPtr->editItem = wineItem->hItem;
3090 * It is common practice for a windows program to get this
3091 * edit control and then subclass it. It is assumed that a
3092 * new edit control is given every time.
3094 * As a result some programs really mess up the edit control
3095 * so we need to destory our old edit control and create a new
3096 * one. Recycling would be nice but we would need to reset
3097 * everything. So recreating may just be easyier
3100 DestroyWindow(infoPtr->hwndEdit);
3101 infoPtr->hwndEdit = CreateWindowExA (
3105 WS_CHILD | WS_BORDER | ES_AUTOHSCROLL |
3106 ES_WANTRETURN | ES_LEFT,
3109 0,0,0); /* FIXME: (HMENU)IDTVEDIT,pcs->hInstance,0);*/
3114 (LONG) TREEVIEW_Edit_SubclassProc);
3117 SendMessageA ( infoPtr->hwndEdit, WM_SETFONT, infoPtr->hFont, FALSE);
3119 SetWindowTextA( infoPtr->hwndEdit, wineItem->pszText );
3120 SendMessageA ( infoPtr->hwndEdit, EM_SETSEL, 0, -1 );
3123 ** NOTE: this must be after the edit control is created
3124 ** (according to TVN_BEGINLABELEDITA docs), before position is set.
3126 if ( TREEVIEW_SendDispInfoNotify( /* Return true to cancel edition */
3129 TVN_BEGINLABELEDITA,
3133 ** FIXME: Is this right, should we return a handle even though edit was cancelled?
3135 TRACE("Edit cancelled by TVN_BEGINLABELEDITA for %s.\n", wineItem->pszText);
3137 TREEVIEW_EndEditLabelNow(hwnd, (WPARAM)TRUE, 0);
3145 wineItem->text.left - 2,
3146 wineItem->text.top - 1,
3147 wineItem->text.right - wineItem->text.left + 20 ,
3148 wineItem->text.bottom - wineItem->text.top + 3,
3151 SetFocus ( infoPtr->hwndEdit);
3152 ShowWindow ( infoPtr->hwndEdit, SW_SHOW);
3158 ** return NULL since we cannot edit this.
3164 return infoPtr->hwndEdit;
3168 TREEVIEW_EndEditLabelNow (HWND hwnd, WPARAM wParam, LPARAM lParam)
3170 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
3171 TREEVIEW_ITEM *editedItem = TREEVIEW_ValidItem (infoPtr, infoPtr->editItem);
3172 BOOL bRevert = (BOOL)wParam;
3180 ** NOTE: we have to get the new text before calling TVN_ENDLABELEDITA
3181 ** since some apps(eg regedit, win98) validate the text in TVN_ENDLABELEDITA.
3184 oldText = editedItem->pszText;
3186 if ( !bRevert ) /* wParam is set to true to cancel the edition */
3189 iLength = GetWindowTextLengthA( infoPtr->hwndEdit );
3192 WARN("Zero length string for new label(not changing).");
3196 newText = COMCTL32_Alloc( iLength+1 );
3197 if( newText == NULL )
3199 ERR("OutOfMemory, cannot allocate space for label");
3202 GetWindowTextA( infoPtr->hwndEdit, newText, iLength+1);
3211 * notify our parent with the new string(or NULL if wParam==TRUE)
3213 tvdi.hdr.hwndFrom = hwnd;
3214 tvdi.hdr.idFrom = GetWindowLongA( hwnd, GWL_ID);
3215 tvdi.hdr.code = TVN_ENDLABELEDITA;
3216 tvdi.item.hItem = editedItem->hItem;
3217 tvdi.item.lParam = editedItem->lParam;
3218 tvdi.item.mask = TVIF_TEXT|TVIF_HANDLE|TVIF_PARAM;
3219 tvdi.item.pszText = newText;
3221 if(!SendMessageA ( /* return false to cancel edition */
3224 (WPARAM)tvdi.hdr.idFrom,
3227 if( newText == NULL ) /*we are supposed to ignore the return if (and pszText==NULL), MSDOCs */
3231 if (oldText != LPSTR_TEXTCALLBACKA)
3236 if( newText != NULL )
3237 COMCTL32_Free(newText);
3239 editedItem->pszText=oldText; /* revert back to the old label */
3243 COMCTL32_Free(oldText);
3245 editedItem->pszText=newText; /* use the new label */
3248 else if( newText!=NULL )
3251 ** Is really this necessary? shouldnt an app update its internal data in TVN_ENDLABELEDITA?
3256 * This is a callback string so we need
3257 * to inform the parent that the string
3261 tvdi.hdr.hwndFrom = hwnd;
3262 tvdi.hdr.idFrom = GetWindowLongA( hwnd, GWL_ID);
3263 tvdi.hdr.code = TVN_SETDISPINFOA;
3264 tvdi.item.mask = TVIF_TEXT;
3265 tvdi.item.pszText = newText;
3270 (WPARAM)tvdi.hdr.idFrom,
3275 COMCTL32_Free(newText);
3279 ShowWindow(infoPtr->hwndEdit, SW_HIDE);
3280 EnableWindow(infoPtr->hwndEdit, FALSE);
3282 /* update the window to eliminate fragments and the like */
3283 TreeView_GetItemRect(hwnd,infoPtr->editItem,&itemRect,FALSE);
3284 RedrawWindow(hwnd,&itemRect,0,RDW_ERASE|RDW_INVALIDATE|RDW_UPDATENOW);
3286 infoPtr->editItem = 0;
3288 return !bRevert; /* return true if label edit succesful, otherwise false */
3294 TREEVIEW_LButtonDoubleClick (HWND hwnd, WPARAM wParam, LPARAM lParam)
3296 TREEVIEW_ITEM *wineItem;
3300 pt.x = (INT)LOWORD(lParam);
3301 pt.y = (INT)HIWORD(lParam);
3304 wineItem=TREEVIEW_HitTestPoint (hwnd, pt);
3305 if (!wineItem) return 0;
3306 TRACE("item %d \n",(INT)wineItem->hItem);
3308 if (TREEVIEW_SendSimpleNotify (hwnd, NM_DBLCLK)!=TRUE) { /* FIXME!*/
3309 TREEVIEW_Expand (hwnd, (WPARAM) TVE_TOGGLE, (LPARAM) wineItem->hItem);
3316 TREEVIEW_LButtonDown (HWND hwnd, WPARAM wParam, LPARAM lParam)
3318 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
3322 ht.pt.x = (INT)LOWORD(lParam);
3323 ht.pt.y = (INT)HIWORD(lParam);
3326 iItem=TREEVIEW_HitTest (hwnd, (LPARAM) &ht);
3327 TRACE("item %d \n",iItem);
3329 if (ht.flags & TVHT_ONITEMBUTTON) {
3330 TREEVIEW_Expand (hwnd, (WPARAM) TVE_TOGGLE, (LPARAM) iItem);
3334 infoPtr->uInternalStatus|=TV_LDRAG;
3341 TREEVIEW_LButtonUp (HWND hwnd, WPARAM wParam, LPARAM lParam)
3343 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
3345 TREEVIEW_ITEM *wineItem;
3347 DWORD dwStyle = GetWindowLongA (hwnd, GWL_STYLE);
3349 ht.pt.x = (INT)LOWORD(lParam);
3350 ht.pt.y = (INT)HIWORD(lParam);
3354 /* Return true to cancel default behaviour */
3355 if ( TREEVIEW_SendSimpleNotify (hwnd, NM_CLICK) )
3359 iItem = TREEVIEW_HitTest (hwnd, (LPARAM) &ht);
3360 TRACE ("%d\n",iItem);
3364 wineItem = TREEVIEW_ValidItem(infoPtr, (HTREEITEM)iItem);
3367 * if we are TVS_SINGLEEXPAND then we want this single click to
3368 * do a bunch of things.
3370 if ((dwStyle & TVS_SINGLEEXPAND)&&
3371 ( ht.flags & (TVHT_ONITEMLABEL | TVHT_ONITEMICON))&&
3372 ( infoPtr->editItem == 0 ))
3374 TREEVIEW_ITEM *SelItem;
3376 * Send the notification
3378 TREEVIEW_SendTreeviewNotify (hwnd,TVN_SINGLEEXPAND,0,
3379 (HTREEITEM)iItem,0);
3381 * Close the previous selection all the way to the root
3382 * as long as the new selection is not a child
3385 if ((infoPtr->selectedItem)&&(infoPtr->selectedItem != (HTREEITEM)iItem))
3387 BOOL closeit = TRUE;
3390 while (closeit && SelItem)
3392 closeit = (SelItem->hItem != infoPtr->selectedItem);
3393 SelItem = TREEVIEW_ValidItem(infoPtr,SelItem->parent);
3398 SelItem = TREEVIEW_ValidItem(infoPtr,infoPtr->selectedItem);
3399 while ((SelItem)&&(SelItem->hItem != wineItem->hItem))
3401 TREEVIEW_Expand (hwnd,(WPARAM)TVE_COLLAPSE,(LPARAM)SelItem->hItem);
3402 SelItem = TREEVIEW_ValidItem(infoPtr,SelItem->parent);
3408 * Expand the current item
3410 TREEVIEW_Expand (hwnd, (WPARAM) TVE_TOGGLE, (LPARAM) wineItem->hItem);
3413 infoPtr->uInternalStatus &= ~(TV_LDRAG | TV_LDRAGGING);
3416 * If the style allow editing and the node is already selected
3417 * and the click occured on the item label...
3419 if ( ( GetWindowLongA( hwnd, GWL_STYLE) & TVS_EDITLABELS ) &&
3420 ( wineItem->state & TVIS_SELECTED ) &&
3421 ( ht.flags & TVHT_ONITEMLABEL ))
3423 if ( infoPtr->editItem == 0 ) /* If we are not curently editing */
3425 if( SendMessageA(hwnd, TVM_EDITLABELA, 0, (LPARAM)iItem) == 0)
3429 else if ( infoPtr->editItem != 0 ) /* If we are curently editing */
3431 TREEVIEW_EndEditLabelNow(hwnd, (WPARAM)FALSE, 0);
3433 else if ( ht.flags & (TVHT_ONITEMLABEL | TVHT_ONITEMICON))
3435 TREEVIEW_DoSelectItem ( hwnd, TVGN_CARET, (HTREEITEM)iItem, TVC_BYMOUSE);
3438 if (ht.flags & TVHT_ONITEMSTATEICON) {
3441 if (dwStyle & TVS_CHECKBOXES) { /* TVS_CHECKBOXES requires _us_ */
3442 int state; /* to toggle the current state */
3443 state=1-(wineItem->state>>12);
3444 TRACE ("state:%x\n", state);
3445 wineItem->state&= ~TVIS_STATEIMAGEMASK;
3446 wineItem->state|=state<<12;
3447 TRACE ("state:%x\n", wineItem->state);
3448 TREEVIEW_QueueRefresh (hwnd);
3456 TREEVIEW_RButtonDown (HWND hwnd, WPARAM wParam, LPARAM lParam)
3458 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
3461 infoPtr->uInternalStatus|=TV_RDRAG;
3466 TREEVIEW_RButtonUp (HWND hwnd, WPARAM wParam, LPARAM lParam)
3468 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
3471 if (TREEVIEW_SendSimpleNotify (hwnd, NM_RCLICK)) return 0;
3472 infoPtr->uInternalStatus&= ~(TV_RDRAG | TV_RDRAGGING);
3478 TREEVIEW_MouseMove (HWND hwnd, WPARAM wParam, LPARAM lParam)
3480 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
3481 TREEVIEW_ITEM *hotItem;
3484 pt.x=(INT) LOWORD (lParam);
3485 pt.y=(INT) HIWORD (lParam);
3486 hotItem=TREEVIEW_HitTestPoint (hwnd, pt);
3487 if (!hotItem) return 0;
3488 infoPtr->focusItem=hotItem->hItem;
3490 if ( GetWindowLongA( hwnd, GWL_STYLE) & TVS_DISABLEDRAGDROP) return 0;
3492 if (infoPtr->uInternalStatus & TV_LDRAG) {
3493 TREEVIEW_SendTreeviewDnDNotify (hwnd, TVN_BEGINDRAGA, hotItem->hItem, pt);
3494 infoPtr->uInternalStatus &= ~TV_LDRAG;
3495 infoPtr->uInternalStatus |= TV_LDRAGGING;
3496 infoPtr->dropItem=hotItem->hItem;
3500 if (infoPtr->uInternalStatus & TV_RDRAG) {
3501 TREEVIEW_SendTreeviewDnDNotify (hwnd, TVN_BEGINRDRAGA, hotItem->hItem, pt);
3502 infoPtr->uInternalStatus &= ~TV_RDRAG;
3503 infoPtr->uInternalStatus |= TV_RDRAGGING;
3504 infoPtr->dropItem=hotItem->hItem;
3513 TREEVIEW_CreateDragImage (HWND hwnd, WPARAM wParam, LPARAM lParam)
3515 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
3516 TREEVIEW_ITEM *dragItem;
3520 HBITMAP hbmp,hOldbmp;
3527 if (!(infoPtr->himlNormal)) return 0;
3528 dragItem=TREEVIEW_ValidItem (infoPtr, (HTREEITEM) lParam);
3530 if (!dragItem) return 0;
3532 if (dragItem->pszText==LPSTR_TEXTCALLBACKA) {
3533 TREEVIEW_SendDispInfoNotify (hwnd, dragItem, TVN_GETDISPINFOA, TVIF_TEXT);
3535 itemtxt=dragItem->pszText;
3537 hwtop=GetDesktopWindow ();
3538 htopdc= GetDC (hwtop);
3539 hdc=CreateCompatibleDC (htopdc);
3541 hOldFont=SelectObject (hdc, infoPtr->hFont);
3542 GetTextExtentPoint32A (hdc, itemtxt, lstrlenA (itemtxt), &size);
3543 TRACE("%d %d %s %d\n",size.cx,size.cy,itemtxt,lstrlenA(itemtxt));
3544 hbmp=CreateCompatibleBitmap (htopdc, size.cx, size.cy);
3545 hOldbmp=SelectObject (hdc, hbmp);
3547 ImageList_GetIconSize (infoPtr->himlNormal, &cx, &cy);
3549 if (cy>size.cy) size.cy=cy;
3551 infoPtr->dragList=ImageList_Create (size.cx, size.cy, ILC_COLOR, 10, 10);
3552 ImageList_Draw (infoPtr->himlNormal, dragItem->iImage, hdc, 0, 0, ILD_NORMAL);
3555 ImageList_GetImageInfo (infoPtr->himlNormal, dragItem->hItem, &iminfo);
3556 ImageList_AddMasked (infoPtr->dragList, iminfo.hbmImage, CLR_DEFAULT);
3559 /* draw item text */
3561 SetRect (&rc, cx, 0, size.cx,size.cy);
3562 DrawTextA (hdc, itemtxt, lstrlenA (itemtxt), &rc, DT_LEFT);
3563 SelectObject (hdc, hOldFont);
3564 SelectObject (hdc, hOldbmp);
3566 ImageList_Add (infoPtr->dragList, hbmp, 0);
3569 DeleteObject (hbmp);
3570 ReleaseDC (hwtop, htopdc);
3572 return (LRESULT)infoPtr->dragList;
3577 TREEVIEW_DoSelectItem (HWND hwnd, INT action, HTREEITEM newSelect, INT cause)
3580 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
3581 TREEVIEW_ITEM *prevItem,*wineItem;
3584 wineItem = TREEVIEW_ValidItem (infoPtr, (HTREEITEM)newSelect);
3586 TRACE("Entering item %d, flag %x, cause %x, state %d\n",
3592 if ( (wineItem) && (wineItem->parent))
3595 * If the item has a collapse parent expand the parent so he
3596 * can expose the item
3598 TREEVIEW_ITEM *parentItem = TREEVIEW_ValidItem (infoPtr, wineItem->parent);
3599 if ( !(parentItem->state & TVIS_EXPANDED))
3600 TREEVIEW_Expand (hwnd, TVE_EXPAND, (LPARAM) wineItem->parent);
3606 prevSelect=(INT)infoPtr->selectedItem;
3608 if ((HTREEITEM)prevSelect==newSelect)
3611 prevItem= TREEVIEW_ValidItem (infoPtr, (HTREEITEM)prevSelect);
3614 if (TREEVIEW_SendTreeviewNotify(
3618 (HTREEITEM)prevSelect,
3619 (HTREEITEM)newSelect))
3620 return FALSE; /* FIXME: OK? */
3623 prevItem->state &= ~TVIS_SELECTED;
3625 wineItem->state |= TVIS_SELECTED;
3627 infoPtr->selectedItem=(HTREEITEM)newSelect;
3629 TREEVIEW_SendTreeviewNotify(
3633 (HTREEITEM)prevSelect,
3634 (HTREEITEM)newSelect);
3638 case TVGN_DROPHILITE:
3639 prevItem= TREEVIEW_ValidItem (infoPtr, infoPtr->dropItem);
3642 prevItem->state &= ~TVIS_DROPHILITED;
3644 infoPtr->dropItem=(HTREEITEM)newSelect;
3647 wineItem->state |=TVIS_DROPHILITED;
3651 case TVGN_FIRSTVISIBLE:
3652 FIXME("FIRSTVISIBLE not implemented\n");
3656 TREEVIEW_QueueRefresh (hwnd);
3658 TRACE("Leaving state %d\n", wineItem->state);
3662 /* FIXME: handle NM_KILLFocus etc */
3664 TREEVIEW_SelectItem (HWND hwnd, WPARAM wParam, LPARAM lParam)
3667 return TREEVIEW_DoSelectItem (hwnd, wParam, (HTREEITEM) lParam, TVC_UNKNOWN);
3674 TREEVIEW_GetFont (HWND hwnd, WPARAM wParam, LPARAM lParam)
3677 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
3679 TRACE("%x\n",infoPtr->hFont);
3680 return infoPtr->hFont;
3684 TREEVIEW_SetFont (HWND hwnd, WPARAM wParam, LPARAM lParam)
3687 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
3690 HFONT hFont, hOldFont;
3694 TRACE("%x %lx\n",wParam, lParam);
3696 infoPtr->hFont = (HFONT)wParam;
3698 hFont = infoPtr->hFont ? infoPtr->hFont : GetStockObject (SYSTEM_FONT);
3700 GetObjectA (infoPtr->hFont, sizeof (LOGFONTA), &logFont);
3701 logFont.lfWeight=FW_BOLD;
3702 infoPtr->hBoldFont = CreateFontIndirectA (&logFont);
3705 hOldFont = SelectObject (hdc, hFont);
3706 GetTextMetricsA (hdc, &tm);
3707 height= tm.tmHeight + tm.tmExternalLeading + FOCUS_BORDER;
3708 if (height>infoPtr->uRealItemHeight)
3709 infoPtr->uRealItemHeight=height;
3710 SelectObject (hdc, hOldFont);
3714 TREEVIEW_QueueRefresh (hwnd);
3722 TREEVIEW_VScroll (HWND hwnd, WPARAM wParam, LPARAM lParam)
3725 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
3729 TRACE("wp %x, lp %lx\n", wParam, lParam);
3730 if (!infoPtr->uInternalStatus & TV_VSCROLL) return FALSE;
3732 switch (LOWORD (wParam)) {
3734 if (!infoPtr->cy) return FALSE;
3735 infoPtr->cy -= infoPtr->uRealItemHeight;
3736 if (infoPtr->cy < 0) infoPtr->cy=0;
3739 nVisibleItems = infoPtr->uVisibleHeight / infoPtr->uRealItemHeight;
3740 maxHeight=infoPtr->uTotalHeight - nVisibleItems * infoPtr->uRealItemHeight;
3741 if (infoPtr->cy >= maxHeight) return FALSE;
3742 infoPtr->cy += infoPtr->uRealItemHeight;
3743 if (infoPtr->cy >= maxHeight)
3744 infoPtr->cy = maxHeight;
3747 if (!infoPtr->cy) return FALSE;
3748 infoPtr->cy -= infoPtr->uVisibleHeight;
3749 if (infoPtr->cy < 0) infoPtr->cy=0;
3752 nVisibleItems = infoPtr->uVisibleHeight / infoPtr->uRealItemHeight;
3753 maxHeight=infoPtr->uTotalHeight - nVisibleItems * infoPtr->uRealItemHeight;
3754 if (infoPtr->cy == maxHeight) return FALSE;
3755 infoPtr->cy += infoPtr->uVisibleHeight;
3756 if (infoPtr->cy >= maxHeight)
3757 infoPtr->cy = maxHeight;
3760 infoPtr->cy = HIWORD (wParam);
3765 TREEVIEW_QueueRefresh (hwnd);
3770 TREEVIEW_HScroll (HWND hwnd, WPARAM wParam, LPARAM lParam)
3772 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
3775 TRACE("wp %lx, lp %x\n", lParam, wParam);
3777 if (!infoPtr->uInternalStatus & TV_HSCROLL) return FALSE;
3779 switch (LOWORD (wParam)) {
3781 if (!infoPtr->cx) return FALSE;
3782 infoPtr->cx -= infoPtr->uRealItemHeight;
3783 if (infoPtr->cx < 0) infoPtr->cx=0;
3786 maxWidth=infoPtr->uTotalWidth-infoPtr->uVisibleWidth;
3787 if (infoPtr->cx == maxWidth) return FALSE;
3788 infoPtr->cx += infoPtr->uRealItemHeight; /*FIXME */
3789 if (infoPtr->cx > maxWidth)
3790 infoPtr->cx = maxWidth;
3793 if (!infoPtr->cx) return FALSE;
3794 infoPtr->cx -= infoPtr->uVisibleWidth;
3795 if (infoPtr->cx < 0) infoPtr->cx=0;
3798 maxWidth=infoPtr->uTotalWidth-infoPtr->uVisibleWidth;
3799 if (infoPtr->cx == maxWidth) return FALSE;
3800 infoPtr->cx += infoPtr->uVisibleWidth;
3801 if (infoPtr->cx > maxWidth)
3802 infoPtr->cx = maxWidth;
3805 infoPtr->cx = HIWORD (wParam);
3810 TREEVIEW_QueueRefresh (hwnd);
3814 static LRESULT TREEVIEW_MouseWheel (HWND hwnd, WPARAM wParam, LPARAM lParam)
3817 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
3818 short gcWheelDelta = 0;
3819 UINT pulScrollLines = 3;
3821 SystemParametersInfoW(SPI_GETWHEELSCROLLLINES,0, &pulScrollLines, 0);
3823 gcWheelDelta -= (short) HIWORD(wParam);
3824 pulScrollLines *= (gcWheelDelta / WHEEL_DELTA);
3826 if (abs(gcWheelDelta) >= WHEEL_DELTA && pulScrollLines)
3828 int wheelDy = pulScrollLines * infoPtr->uRealItemHeight;
3829 int newDy = infoPtr->cy + wheelDy;
3830 int maxDy = infoPtr->uTotalHeight - infoPtr->uVisibleHeight;
3832 if (newDy > maxDy) newDy = maxDy;
3833 if (newDy < 0) newDy = 0;
3835 if (newDy == infoPtr->cy) return TRUE;
3837 TREEVIEW_VScroll(hwnd, MAKEWPARAM(SB_THUMBTRACK,newDy),0);
3843 TREEVIEW_KeyDown (HWND hwnd, WPARAM wParam, LPARAM lParam)
3845 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
3846 HTREEITEM hNewSelection = 0;
3847 INT scrollNeeds = -1;
3848 INT cyChangeNeeds = -1;
3849 INT prevSelect = (INT)infoPtr->selectedItem;
3851 TREEVIEW_ITEM *prevItem =
3852 (prevSelect != 0 ) ?
3853 TREEVIEW_ValidItem (infoPtr, (HTREEITEM)prevSelect) :
3856 TREEVIEW_ITEM *newItem = NULL;
3858 TRACE("%x %lx\n",wParam, lParam);
3860 if (prevSelect == 0)
3865 newItem=TREEVIEW_GetPrevListItem (infoPtr, prevItem);
3868 newItem=& infoPtr->items[(INT)infoPtr->TopRootItem];
3870 hNewSelection = newItem->hItem;
3872 if (! newItem->visible)
3873 scrollNeeds = SB_LINEUP;
3877 newItem=TREEVIEW_GetNextListItem (infoPtr, prevItem);
3882 hNewSelection = newItem->hItem;
3884 if (! newItem->visible)
3885 scrollNeeds = SB_LINEDOWN;
3890 newItem = &infoPtr->items[(INT)infoPtr->TopRootItem];
3891 hNewSelection = newItem->hItem;
3896 newItem = &infoPtr->items[(INT)infoPtr->TopRootItem];
3897 newItem = TREEVIEW_GetLastListItem (infoPtr, newItem);
3898 hNewSelection = newItem->hItem;
3900 if (! newItem->visible)
3901 cyChangeNeeds = infoPtr->uTotalHeight-infoPtr->uVisibleHeight;
3906 if ( (prevItem->cChildren > 0) && (prevItem->state & TVIS_EXPANDED) )
3908 TREEVIEW_Expand(hwnd, TVE_COLLAPSE, prevSelect );
3910 else if ((INT)prevItem->parent)
3912 newItem = (& infoPtr->items[(INT)prevItem->parent]);
3913 if (! newItem->visible)
3914 /* FIXME find a way to make this item the first visible... */
3917 hNewSelection = newItem->hItem;
3923 if ( ( prevItem->cChildren > 0) ||
3924 ( prevItem->cChildren == I_CHILDRENCALLBACK))
3926 if (! (prevItem->state & TVIS_EXPANDED))
3927 TREEVIEW_Expand(hwnd, TVE_EXPAND, prevSelect );
3930 newItem = (& infoPtr->items[(INT)prevItem->firstChild]);
3931 hNewSelection = newItem->hItem;
3938 if (! (prevItem->state & TVIS_EXPANDED))
3939 TREEVIEW_Expand(hwnd, TVE_EXPAND, prevSelect );
3943 if (prevItem->state & TVIS_EXPANDED)
3944 TREEVIEW_Expand(hwnd, TVE_COLLAPSE, prevSelect );
3949 newItem=TREEVIEW_GetListItem(
3952 -1*(TREEVIEW_GetVisibleCount(hwnd,0,0)-3));
3956 hNewSelection = newItem->hItem;
3958 if (! newItem->visible)
3959 scrollNeeds = SB_PAGEUP;
3964 newItem=TREEVIEW_GetListItem(
3967 TREEVIEW_GetVisibleCount(hwnd,0,0)-3);
3972 hNewSelection = newItem->hItem;
3974 if (! newItem->visible)
3975 scrollNeeds = SB_PAGEDOWN;
3984 FIXME("%x not implemented\n", wParam);
3991 This works but does not send notification...
3993 prevItem->state &= ~TVIS_SELECTED;
3994 newItem->state |= TVIS_SELECTED;
3995 infoPtr->selectedItem = hNewSelection;
3996 TREEVIEW_QueueRefresh (hwnd);
3999 if ( TREEVIEW_DoSelectItem(
4002 (HTREEITEM)hNewSelection,
4005 /* If selection change is allowed for the new item, perform scrolling */
4006 if (scrollNeeds != -1)
4007 TREEVIEW_VScroll(hwnd, scrollNeeds, 0);
4009 if (cyChangeNeeds != -1)
4010 infoPtr->cy = cyChangeNeeds;
4012 /* FIXME: Something happen in the load the in the two weeks before
4013 april 1st 1999 which makes this SetFocus mandatory otherwise, the focus
4014 is lost... However the SetFocus should not be required...*/
4025 TREEVIEW_GetScrollTime (HWND hwnd)
4027 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
4029 return infoPtr->uScrollTime;
4034 TREEVIEW_SetScrollTime (HWND hwnd, UINT uScrollTime)
4036 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
4037 UINT uOldScrollTime = infoPtr->uScrollTime;
4039 infoPtr->uScrollTime = min (uScrollTime, 100);
4041 return uOldScrollTime;
4045 static LRESULT WINAPI
4046 TREEVIEW_WindowProc (HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
4048 TREEVIEW_INFO *infoPtr;
4049 if (uMsg==WM_CREATE)
4050 return TREEVIEW_Create (hwnd, wParam, lParam);
4052 if (!(infoPtr = TREEVIEW_GetInfoPtr(hwnd)))
4053 return DefWindowProcA (hwnd, uMsg, wParam, lParam);
4057 case TVM_INSERTITEMA:
4058 return TREEVIEW_InsertItemA (hwnd, wParam, lParam);
4060 case TVM_INSERTITEMW:
4061 return TREEVIEW_InsertItemW(hwnd,wParam,lParam);;
4063 case TVM_DELETEITEM:
4064 return TREEVIEW_DeleteItem (hwnd, wParam, lParam);
4067 return TREEVIEW_Expand (hwnd, wParam, lParam);
4069 case TVM_GETITEMRECT:
4070 return TREEVIEW_GetItemRect (hwnd, wParam, lParam);
4073 return TREEVIEW_GetCount (hwnd, wParam, lParam);
4076 return TREEVIEW_GetIndent (hwnd);
4079 return TREEVIEW_SetIndent (hwnd, wParam);
4081 case TVM_GETIMAGELIST:
4082 return TREEVIEW_GetImageList (hwnd, wParam, lParam);
4084 case TVM_SETIMAGELIST:
4085 return TREEVIEW_SetImageList (hwnd, wParam, lParam);
4087 case TVM_GETNEXTITEM:
4088 return TREEVIEW_GetNextItem (hwnd, wParam, lParam);
4090 case TVM_SELECTITEM:
4091 return TREEVIEW_SelectItem (hwnd, wParam, lParam);
4094 return TREEVIEW_GetItemA (hwnd, wParam, lParam);
4097 return TREEVIEW_GetItemW (hwnd, wParam, lParam);
4100 return TREEVIEW_SetItemA (hwnd, wParam, lParam);
4103 FIXME("Unimplemented msg TVM_SETITEMW\n");
4106 case TVM_EDITLABELA:
4107 return TREEVIEW_EditLabelA(hwnd, wParam, lParam);
4109 case TVM_EDITLABELW:
4110 FIXME("Unimplemented msg TVM_EDITLABELW \n");
4113 case TVM_GETEDITCONTROL:
4114 return TREEVIEW_GetEditControl (hwnd);
4116 case TVM_GETVISIBLECOUNT:
4117 return TREEVIEW_GetVisibleCount (hwnd, wParam, lParam);
4120 return TREEVIEW_HitTest (hwnd, lParam);
4122 case TVM_CREATEDRAGIMAGE:
4123 return TREEVIEW_CreateDragImage (hwnd, wParam, lParam);
4125 case TVM_SORTCHILDREN:
4126 return TREEVIEW_SortChildren (hwnd, wParam, lParam);
4128 case TVM_ENSUREVISIBLE:
4129 FIXME("Unimplemented msg TVM_ENSUREVISIBLE\n");
4132 case TVM_SORTCHILDRENCB:
4133 return TREEVIEW_SortChildrenCB(hwnd, wParam, lParam);
4135 case TVM_ENDEDITLABELNOW:
4136 if (infoPtr->editItem)
4137 return TREEVIEW_EndEditLabelNow (hwnd, wParam, lParam);
4139 case TVM_GETISEARCHSTRINGA:
4140 FIXME("Unimplemented msg TVM_GETISEARCHSTRINGA\n");
4143 case TVM_GETISEARCHSTRINGW:
4144 FIXME("Unimplemented msg TVM_GETISEARCHSTRINGW\n");
4147 case TVM_GETTOOLTIPS:
4148 return TREEVIEW_GetToolTips (hwnd);
4150 case TVM_SETTOOLTIPS:
4151 return TREEVIEW_SetToolTips (hwnd, wParam);
4153 case TVM_SETINSERTMARK:
4154 return TREEVIEW_SetInsertMark (hwnd,wParam, lParam);
4156 case TVM_SETITEMHEIGHT:
4157 return TREEVIEW_SetItemHeight (hwnd, wParam);
4159 case TVM_GETITEMHEIGHT:
4160 return TREEVIEW_GetItemHeight (hwnd);
4162 case TVM_SETBKCOLOR:
4163 return TREEVIEW_SetBkColor (hwnd, wParam, lParam);
4165 case TVM_SETTEXTCOLOR:
4166 return TREEVIEW_SetTextColor (hwnd, wParam, lParam);
4168 case TVM_GETBKCOLOR:
4169 return TREEVIEW_GetBkColor (hwnd);
4171 case TVM_GETTEXTCOLOR:
4172 return TREEVIEW_GetTextColor (hwnd);
4174 case TVM_SETSCROLLTIME:
4175 return TREEVIEW_SetScrollTime (hwnd, (UINT)wParam);
4177 case TVM_GETSCROLLTIME:
4178 return TREEVIEW_GetScrollTime (hwnd);
4180 case TVM_GETITEMSTATE:
4181 return TREEVIEW_GetItemState (hwnd,wParam, lParam);
4183 case TVM_GETLINECOLOR:
4184 return TREEVIEW_GetLineColor (hwnd,wParam, lParam);
4186 case TVM_SETLINECOLOR:
4187 return TREEVIEW_SetLineColor (hwnd,wParam, lParam);
4189 case TVM_SETINSERTMARKCOLOR:
4190 return TREEVIEW_SetInsertMarkColor (hwnd,wParam, lParam);
4192 case TVM_GETINSERTMARKCOLOR:
4193 return TREEVIEW_GetInsertMarkColor (hwnd,wParam, lParam);
4195 case TVM_SETUNICODEFORMAT:
4196 FIXME("Unimplemented msg TVM_SETUNICODEFORMAT\n");
4199 case TVM_GETUNICODEFORMAT:
4200 FIXME("Unimplemented msg TVM_GETUNICODEFORMAT\n");
4204 return TREEVIEW_Command (hwnd, wParam, lParam);
4207 return TREEVIEW_Destroy (hwnd);
4209 /* case WM_ENABLE: */
4212 return TREEVIEW_EraseBackground (hwnd, wParam, lParam);
4215 return DLGC_WANTARROWS | DLGC_WANTCHARS;
4218 return TREEVIEW_Paint (hwnd, wParam, lParam);
4221 return TREEVIEW_GetFont (hwnd, wParam, lParam);
4224 return TREEVIEW_SetFont (hwnd, wParam, lParam);
4227 return TREEVIEW_KeyDown (hwnd, wParam, lParam);
4230 return TREEVIEW_SetFocus (hwnd, wParam, lParam);
4233 return TREEVIEW_KillFocus (hwnd, wParam, lParam);
4235 case WM_LBUTTONDOWN:
4236 return TREEVIEW_LButtonDown (hwnd, wParam, lParam);
4239 return TREEVIEW_LButtonUp (hwnd, wParam, lParam);
4241 case WM_LBUTTONDBLCLK:
4242 return TREEVIEW_LButtonDoubleClick (hwnd, wParam, lParam);
4244 case WM_RBUTTONDOWN:
4245 return TREEVIEW_RButtonDown (hwnd, wParam, lParam);
4248 return TREEVIEW_RButtonUp (hwnd, wParam, lParam);
4251 return TREEVIEW_MouseMove (hwnd, wParam, lParam);
4253 case WM_STYLECHANGED:
4254 return TREEVIEW_StyleChanged (hwnd, wParam, lParam);
4256 /* case WM_SYSCOLORCHANGE: */
4257 /* case WM_SETREDRAW: */
4260 return TREEVIEW_HandleTimer (hwnd, wParam, lParam);
4263 return TREEVIEW_Size (hwnd, wParam,lParam);
4266 return TREEVIEW_HScroll (hwnd, wParam, lParam);
4268 return TREEVIEW_VScroll (hwnd, wParam, lParam);
4271 if (wParam & (MK_SHIFT | MK_CONTROL))
4272 return DefWindowProcA( hwnd, uMsg, wParam, lParam );
4273 return TREEVIEW_MouseWheel (hwnd, wParam, lParam);
4276 TRACE ("drawItem\n");
4277 return DefWindowProcA (hwnd, uMsg, wParam, lParam);
4280 if (uMsg >= WM_USER)
4281 FIXME("Unknown msg %04x wp=%08x lp=%08lx\n",
4282 uMsg, wParam, lParam);
4283 return DefWindowProcA (hwnd, uMsg, wParam, lParam);
4290 TREEVIEW_Register (void)
4296 ZeroMemory (&wndClass, sizeof(WNDCLASSA));
4297 wndClass.style = CS_GLOBALCLASS | CS_DBLCLKS;
4298 wndClass.lpfnWndProc = (WNDPROC)TREEVIEW_WindowProc;
4299 wndClass.cbClsExtra = 0;
4300 wndClass.cbWndExtra = sizeof(TREEVIEW_INFO *);
4301 wndClass.hCursor = LoadCursorA (0, IDC_ARROWA);
4302 wndClass.hbrBackground = 0;
4303 wndClass.lpszClassName = WC_TREEVIEWA;
4305 RegisterClassA (&wndClass);
4310 TREEVIEW_Unregister (void)
4312 UnregisterClassA (WC_TREEVIEWA, (HINSTANCE)NULL);