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.
47 #include "debugtools.h"
49 DEFAULT_DEBUG_CHANNEL(treeview);
51 /* ffs should be in <string.h>. */
53 /* Defines, since they do not need to return previous state, and nr
54 * has no side effects in this file.
56 #define tv_test_bit(nr,bf) (((LPBYTE)bf)[nr>>3]&(1<<(nr&7)))
57 #define tv_set_bit(nr,bf) ((LPBYTE)bf)[nr>>3]|=(1<<(nr&7))
58 #define tv_clear_bit(nr,bf) ((LPBYTE)bf)[nr>>3]&=~(1<<(nr&7))
60 #define MINIMUM_INDENT 10
61 #define TV_REFRESH_DELAY 100 /* 100 ms delay between two refreshes */
62 #define TV_DEFAULTITEMHEIGHT 16
63 #define TVITEM_ALLOC 16 /* default nr of items to allocate at first try */
66 /* internal structures */
80 int iLevel; /* indentation level:0=root level */
82 HTREEITEM parent; /* handle to parent or 0 if at root*/
83 HTREEITEM firstChild; /* handle to first child or 0 if no child*/
84 HTREEITEM sibling; /* handle to next item in list, 0 if last */
85 HTREEITEM upsibling; /* handle to previous item in list, 0 if first */
89 RECT expandBox; /* expand box (+/-) coordinate */
98 UINT bAutoSize; /* merge with uInternalStatus */
100 UINT uNumItems; /* number of valid TREEVIEW_ITEMs */
101 UINT uNumPtrsAlloced;
102 HTREEITEM uMaxHandle; /* needed for delete_item */
103 HTREEITEM TopRootItem; /* handle to first item in treeview */
104 INT cdmode; /* last custom draw setting */
105 UINT uScrollTime; /* max. time for scrolling in milliseconds*/
106 UINT uItemHeight; /* item height, -1 for default item height */
107 UINT uRealItemHeight;/* current item height in pixels */
108 UINT uVisibleHeight; /* visible height of treeview in pixels */
109 UINT uTotalHeight; /* total height of treeview in pixels */
112 UINT uIndent; /* indentation in pixels */
113 HTREEITEM selectedItem; /* handle to selected item or 0 if none */
114 HTREEITEM focusItem; /* handle to item that has focus, 0 if none */
115 HTREEITEM hotItem; /* handle currently under cursor, 0 if none */
116 HTREEITEM editItem; /* handle to item currently editted, 0 if none */
117 HTREEITEM firstVisible; /* handle to first visible item */
118 HTREEITEM dropItem; /* handle to item selected by drag cursor */
119 HTREEITEM insertMarkItem; /* item after which insertion mark is placed */
120 BOOL insertBeforeorAfter; /* flag used by TVM_SETINSERTMARK */
121 HIMAGELIST dragList; /* Bitmap of dragged item */
122 INT cx,cy; /* current x/y place in list */
126 COLORREF clrInsertMark;
131 WNDPROC wpEditOrig; /* needed for subclassing edit control */
132 HIMAGELIST himlNormal;
133 HIMAGELIST himlState;
134 LPTVSORTCB pCallBackSort; /* ptr to TVSORTCB struct for callback sorting */
135 TREEVIEW_ITEM *items; /* itemlist */
136 INT *freeList; /* bitmap indicating which elements are valid */
137 /* 1=valid, 0=free; */
138 /* size of list= uNumPtrsAlloced/32 */
142 /* bitflags for infoPtr->uInternalStatus */
144 #define TV_HSCROLL 0x01 /* treeview too large to fit in window */
145 #define TV_VSCROLL 0x02 /* (horizontal/vertical) */
146 #define TV_LDRAG 0x04 /* Lbutton pushed to start drag */
147 #define TV_LDRAGGING 0x08 /* Lbutton pushed, mouse moved. */
148 #define TV_RDRAG 0x10 /* dito Rbutton */
149 #define TV_RDRAGGING 0x20
151 /* bitflags for infoPtr->timer */
153 #define TV_REFRESH_TIMER 1
154 #define TV_EDIT_TIMER 2
155 #define TV_REFRESH_TIMER_SET 1
156 #define TV_EDIT_TIMER_SET 2
158 #define TREEVIEW_GetInfoPtr(hwnd) \
159 ((TREEVIEW_INFO *) GetWindowLongA( hwnd, 0))
161 #define FOCUS_BORDER 3
164 TREEVIEW_SendSimpleNotify (HWND hwnd, UINT code);
166 TREEVIEW_SendTreeviewNotify (HWND hwnd, UINT code, UINT action,
167 HTREEITEM oldItem, HTREEITEM newItem);
169 TREEVIEW_SendTreeviewDnDNotify (HWND hwnd, UINT code, HTREEITEM dragItem,
172 TREEVIEW_SendDispInfoNotify (HWND hwnd, TREEVIEW_ITEM *wineItem,
173 UINT code, UINT what);
175 TREEVIEW_SendCustomDrawNotify (HWND hwnd, DWORD dwDrawStage, HDC hdc,
178 TREEVIEW_SendCustomDrawItemNotify (HWND hwnd, HDC hdc,
179 TREEVIEW_ITEM *tvItem, UINT uItemDrawState);
181 TREEVIEW_DoSelectItem (HWND hwnd, INT action, HTREEITEM newSelect, INT cause);
183 TREEVIEW_Refresh (HWND hwnd, HDC hdc);
185 static LRESULT CALLBACK
186 TREEVIEW_Edit_SubclassProc (HWND hwnd, UINT uMsg, WPARAM wParam,
190 TREEVIEW_EditLabelA (HWND hwnd, WPARAM wParam, LPARAM lParam);
193 TREEVIEW_EndEditLabelNow (HWND hwnd, WPARAM wParam, LPARAM lParam);
198 /* helper functions. Work with the assumption that validity of operands
199 is checked beforehand, and that tree state is valid. */
201 /* FIXME: MS documentation says `GetNextVisibleItem' returns NULL
202 if not successfull. Probably only applies to dereferencing infoPtr
203 (i.e. we are offered a valid treeview structure)
204 and not whether there is a next `visible' child.
205 FIXME: check other failures.
208 /***************************************************************************
209 * This method returns the TREEVIEW_ITEM object given the handle
211 static TREEVIEW_ITEM* TREEVIEW_ValidItem(
212 TREEVIEW_INFO *infoPtr,
215 if ((!handle) || (handle>infoPtr->uMaxHandle))
218 if (tv_test_bit ((INT)handle, infoPtr->freeList))
221 return &infoPtr->items[(INT)handle];
224 /***************************************************************************
225 * This method returns the last expanded child item of a tree node
227 static TREEVIEW_ITEM *TREEVIEW_GetLastListItem(
228 TREEVIEW_INFO *infoPtr,
229 TREEVIEW_ITEM *tvItem)
231 TREEVIEW_ITEM *wineItem = tvItem;
234 * Get this item last sibling
236 while (wineItem->sibling)
237 wineItem=& infoPtr->items [(INT)wineItem->sibling];
240 * If the last sibling has expanded children, restart.
242 if ( ( wineItem->cChildren > 0 ) && ( wineItem->state & TVIS_EXPANDED) )
243 return TREEVIEW_GetLastListItem(
245 &(infoPtr->items[(INT)wineItem->firstChild]));
250 /***************************************************************************
251 * This method returns the previous physical item in the list not
252 * considering the tree hierarchy.
254 static TREEVIEW_ITEM *TREEVIEW_GetPrevListItem(
255 TREEVIEW_INFO *infoPtr,
256 TREEVIEW_ITEM *tvItem)
258 if (tvItem->upsibling)
261 * This item has a upsibling, get the last item. Since, GetLastListItem
262 * first looks at siblings, we must feed it with the first child.
264 TREEVIEW_ITEM *upItem = &infoPtr->items[(INT)tvItem->upsibling];
266 if ( ( upItem->cChildren > 0 ) && ( upItem->state & TVIS_EXPANDED) )
267 return TREEVIEW_GetLastListItem(
269 &infoPtr->items[(INT)upItem->firstChild]);
276 * this item does not have a upsibling, get the parent
279 return &infoPtr->items[(INT)tvItem->parent];
286 /***************************************************************************
287 * This method returns the next physical item in the treeview not
288 * considering the tree hierarchy.
290 static TREEVIEW_ITEM *TREEVIEW_GetNextListItem(
291 TREEVIEW_INFO *infoPtr,
292 TREEVIEW_ITEM *tvItem)
294 TREEVIEW_ITEM *wineItem = NULL;
297 * If this item has children and is expanded, return the first child
299 if ((tvItem->firstChild) && (tvItem->state & TVIS_EXPANDED))
300 return (& infoPtr->items[(INT)tvItem->firstChild]);
304 * try to get the sibling
307 return (& infoPtr->items[(INT)tvItem->sibling]);
310 * Otherwise, get the parent's sibling.
313 while (wineItem->parent) {
314 wineItem=& infoPtr->items [(INT)wineItem->parent];
315 if (wineItem->sibling)
316 return (& infoPtr->items [(INT)wineItem->sibling]);
322 /***************************************************************************
323 * This method returns the nth item starting at the given item. It returns
324 * the last item (or first) we we run out of items.
326 * Will scroll backward if count is <0.
327 * forward if count is >0.
329 static TREEVIEW_ITEM *TREEVIEW_GetListItem(
330 TREEVIEW_INFO *infoPtr,
331 TREEVIEW_ITEM *tvItem,
334 TREEVIEW_ITEM *previousItem = NULL;
335 TREEVIEW_ITEM *wineItem = tvItem;
340 /* Find count item downward */
341 while ((iter++ < count) && (wineItem != NULL))
343 /* Keep a pointer to the previous in case we ask for more than we got */
344 previousItem = wineItem;
345 wineItem = TREEVIEW_GetNextListItem(infoPtr, wineItem);
348 if (wineItem == NULL)
349 wineItem = previousItem;
353 /* Find count item upward */
354 while ((iter-- > count) && (wineItem != NULL))
356 /* Keep a pointer to the previous in case we ask for more than we got */
357 previousItem = wineItem;
358 wineItem = TREEVIEW_GetPrevListItem(infoPtr, wineItem);
361 if (wineItem == NULL)
362 wineItem = previousItem;
371 /***************************************************************************
374 static void TREEVIEW_RemoveAllChildren(
376 TREEVIEW_ITEM *parentItem)
378 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
379 TREEVIEW_ITEM *killItem;
382 kill=(INT)parentItem->firstChild;
384 tv_set_bit ( kill, infoPtr->freeList);
385 killItem=& infoPtr->items[kill];
386 if (killItem->pszText!=LPSTR_TEXTCALLBACKA)
387 COMCTL32_Free (killItem->pszText);
388 TREEVIEW_SendTreeviewNotify (hwnd, TVN_DELETEITEMA, 0, (HTREEITEM)kill, 0);
389 if (killItem->firstChild)
390 TREEVIEW_RemoveAllChildren (hwnd, killItem);
391 kill=(INT)killItem->sibling;
394 if (parentItem->cChildren>0) {
395 infoPtr->uNumItems -= parentItem->cChildren;
396 parentItem->firstChild = 0;
397 parentItem->cChildren = 0;
404 TREEVIEW_RemoveItem (HWND hwnd, TREEVIEW_ITEM *wineItem)
407 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
408 TREEVIEW_ITEM *parentItem, *upsiblingItem, *siblingItem;
411 iItem=(INT)wineItem->hItem;
412 tv_set_bit(iItem,infoPtr->freeList);
413 infoPtr->uNumItems--;
415 if (wineItem->pszText!=LPSTR_TEXTCALLBACKA)
416 COMCTL32_Free (wineItem->pszText);
418 TREEVIEW_SendTreeviewNotify (hwnd, TVN_DELETEITEMA, 0, (HTREEITEM)iItem, 0);
420 if (wineItem->firstChild)
421 TREEVIEW_RemoveAllChildren (hwnd,wineItem);
423 if (wineItem->parent) {
424 parentItem=& infoPtr->items [(INT)wineItem->parent];
425 switch (parentItem->cChildren) {
426 case I_CHILDRENCALLBACK:
427 FIXME("we don't handle I_CHILDRENCALLBACK yet\n");
430 parentItem->cChildren=0;
431 parentItem->firstChild=0;
434 parentItem->cChildren--;
435 if ((INT)parentItem->firstChild==iItem)
436 parentItem->firstChild=wineItem->sibling;
440 if (iItem==(INT)infoPtr->TopRootItem)
441 infoPtr->TopRootItem=(HTREEITEM)wineItem->sibling;
442 if (wineItem->upsibling) {
443 upsiblingItem=& infoPtr->items [(INT)wineItem->upsibling];
444 upsiblingItem->sibling=wineItem->sibling;
446 if (wineItem->sibling) {
447 siblingItem=& infoPtr->items [(INT)wineItem->sibling];
448 siblingItem->upsibling=wineItem->upsibling;
456 /* Note:TREEVIEW_RemoveTree doesn't remove infoPtr itself */
458 static void TREEVIEW_RemoveTree (HWND hwnd)
461 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
462 TREEVIEW_ITEM *killItem;
465 for (i = 1; i <= (INT)infoPtr->uMaxHandle; i++)
466 if (!tv_test_bit (i, infoPtr->freeList)) {
467 killItem = &infoPtr->items[i];
468 if (killItem->pszText != LPSTR_TEXTCALLBACKA)
469 COMCTL32_Free (killItem->pszText);
470 TREEVIEW_SendTreeviewNotify(hwnd, TVN_DELETEITEMA, 0,
473 if (infoPtr->uNumPtrsAlloced) {
474 COMCTL32_Free (infoPtr->items);
475 COMCTL32_Free (infoPtr->freeList);
476 infoPtr->uNumItems = 0;
477 infoPtr->uNumPtrsAlloced = 0;
478 infoPtr->uMaxHandle = 0;
479 infoPtr->TopRootItem = 0;
490 TREEVIEW_GetImageList (HWND hwnd, WPARAM wParam, LPARAM lParam)
492 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
496 if ((INT)wParam == TVSIL_NORMAL)
497 return (LRESULT) infoPtr->himlNormal;
498 if ((INT)wParam == TVSIL_STATE)
499 return (LRESULT) infoPtr->himlState;
505 TREEVIEW_SetImageList (HWND hwnd, WPARAM wParam, LPARAM lParam)
507 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
510 TRACE("%x,%lx\n", wParam, lParam);
511 switch ((INT)wParam) {
513 himlTemp = infoPtr->himlNormal;
514 infoPtr->himlNormal = (HIMAGELIST)lParam;
515 return (LRESULT)himlTemp;
518 himlTemp = infoPtr->himlState;
519 infoPtr->himlState = (HIMAGELIST)lParam;
520 return (LRESULT)himlTemp;
523 return (LRESULT)NULL;
529 TREEVIEW_SetItemHeight (HWND hwnd, WPARAM wParam)
531 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
532 INT cx,cy,prevHeight=infoPtr->uItemHeight;
536 infoPtr->uItemHeight=-1;
540 ImageList_GetIconSize (infoPtr->himlNormal, &cx, &cy);
542 if (wParam>cy) cy=wParam;
543 infoPtr->uItemHeight=cy;
545 if (!( GetWindowLongA( hwnd, GWL_STYLE) & TVS_NONEVENHEIGHT))
546 infoPtr->uItemHeight = (INT) wParam & 0xfffffffe;
551 TREEVIEW_GetItemHeight (HWND hwnd)
553 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
556 return infoPtr->uItemHeight;
560 TREEVIEW_GetLineColor (HWND hwnd, WPARAM wParam, LPARAM lParam)
562 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
565 return (LRESULT) infoPtr->clrLine;
569 TREEVIEW_SetLineColor (HWND hwnd, WPARAM wParam, LPARAM lParam)
571 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
572 COLORREF prevColor=infoPtr->clrLine;
575 infoPtr->clrLine=(COLORREF) lParam;
576 return (LRESULT) prevColor;
580 TREEVIEW_GetInsertMarkColor (HWND hwnd, WPARAM wParam, LPARAM lParam)
582 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
585 return (LRESULT) infoPtr->clrInsertMark;
589 TREEVIEW_SetInsertMarkColor (HWND hwnd, WPARAM wParam, LPARAM lParam)
591 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
592 COLORREF prevColor=infoPtr->clrInsertMark;
594 TRACE("%d %ld\n",wParam,lParam);
595 infoPtr->clrInsertMark=(COLORREF) lParam;
596 return (LRESULT) prevColor;
600 TREEVIEW_SetInsertMark (HWND hwnd, WPARAM wParam, LPARAM lParam)
602 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
604 FIXME("%d %ld\n",wParam,lParam);
605 if (!TREEVIEW_ValidItem (infoPtr, (HTREEITEM)lParam)) return 0;
606 FIXME("%d %ld\n",wParam,lParam);
608 infoPtr->insertBeforeorAfter=(BOOL) wParam;
609 infoPtr->insertMarkItem=(HTREEITEM) lParam;
611 InvalidateRect(hwnd, NULL, FALSE);
617 TREEVIEW_SetTextColor (HWND hwnd, WPARAM wParam, LPARAM lParam)
619 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
620 COLORREF prevColor=infoPtr->clrText;
623 infoPtr->clrText=(COLORREF) lParam;
624 return (LRESULT) prevColor;
628 TREEVIEW_GetBkColor (HWND hwnd)
630 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
633 return (LRESULT) infoPtr->clrBk;
637 TREEVIEW_SetBkColor (HWND hwnd, WPARAM wParam, LPARAM lParam)
639 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
640 COLORREF prevColor=infoPtr->clrBk;
643 infoPtr->clrBk=(COLORREF) lParam;
644 return (LRESULT) prevColor;
648 TREEVIEW_GetTextColor (HWND hwnd)
650 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
653 return (LRESULT) infoPtr->clrText;
657 /* cdmode: custom draw mode as received from app. in first NMCUSTOMDRAW
660 #define TREEVIEW_LEFT_MARGIN 8
664 TREEVIEW_DrawItem (HWND hwnd, HDC hdc, TREEVIEW_ITEM *wineItem)
666 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
667 DWORD dwStyle = GetWindowLongA (hwnd, GWL_STYLE);
668 INT center,xpos,cx,cy, cditem;
670 UINT uTextJustify = DT_LEFT;
674 if (wineItem->state & TVIS_BOLD)
675 hOldFont = SelectObject (hdc, infoPtr->hBoldFont);
677 hOldFont = SelectObject (hdc, infoPtr->hFont);
680 TRACE ("cdmode:%x\n",infoPtr->cdmode);
681 if (infoPtr->cdmode & CDRF_NOTIFYITEMDRAW) {
682 cditem=TREEVIEW_SendCustomDrawItemNotify
683 (hwnd, hdc, wineItem, CDDS_ITEMPREPAINT);
684 TRACE("prepaint:cditem-app returns 0x%x\n",cditem);
686 if (cditem & CDRF_SKIPDEFAULT)
691 * Set drawing starting points
693 r = wineItem->rect; /* this item rectangle */
694 center = (r.top+r.bottom)/2; /* this item vertical center */
695 xpos = r.left + TREEVIEW_LEFT_MARGIN;/* horizontal starting point */
698 * Display the tree hierarchy
700 if ( dwStyle & TVS_HASLINES)
703 * Write links to parent node
704 * we draw the L starting from the child to the parent
706 * points[0] is attached to the current item
707 * points[1] is the L corner
708 * points[2] is attached to the parent or the up sibling
710 if ( dwStyle & TVS_LINESATROOT)
712 TREEVIEW_ITEM *upNode = NULL;
713 BOOL hasParentOrSibling = TRUE;
714 RECT upRect = {0,0,0,0};
715 HPEN hOldPen, hNewPen;
718 * determine the target location of the line at root, either be linked
719 * to the up sibling or to the parent node.
721 if (wineItem->upsibling)
722 upNode = TREEVIEW_ValidItem (infoPtr, wineItem->upsibling);
723 else if (wineItem->parent)
724 upNode = TREEVIEW_ValidItem (infoPtr, wineItem->parent);
726 hasParentOrSibling = FALSE;
729 upRect = upNode->rect;
731 if ( wineItem->iLevel == 0 )
733 points[2].x = points[1].x = upRect.left+8;
734 points[0].x = points[2].x + 10;
735 points[2].y = upRect.bottom-3;
736 points[1].y = points[0].y = center;
740 points[2].x = points[1].x = 8 + (20*wineItem->iLevel);
741 points[2].y = ( upNode->cChildren == 0) ?
742 upRect.top : /* is linked to the "L" above */
743 ( wineItem->upsibling != NULL) ?
744 upRect.bottom-3: /* is linked to an icon */
745 upRect.bottom+1; /* is linked to a +/- box */
746 points[1].y = points[0].y = center;
747 points[0].x = points[1].x + 10;
753 hNewPen = CreatePen(PS_DOT, 0, infoPtr->clrLine);
754 hOldPen = SelectObject( hdc, hNewPen );
756 if (hasParentOrSibling)
757 Polyline (hdc,points,3);
759 Polyline (hdc,points,2);
761 DeleteObject(hNewPen);
762 SelectObject(hdc, hOldPen);
767 * Display the (+/-) signs
769 if (wineItem->iLevel != 0)/* update position only for non root node */
770 xpos+=(5*wineItem->iLevel);
772 if (( dwStyle & TVS_HASBUTTONS) && ( dwStyle & TVS_HASLINES))
774 if ( (wineItem->cChildren) ||
775 (wineItem->cChildren == I_CHILDRENCALLBACK))
777 /* Setup expand box coordinate to facilitate the LMBClick handling */
778 wineItem->expandBox.left = xpos-4;
779 wineItem->expandBox.top = center-4;
780 wineItem->expandBox.right = xpos+5;
781 wineItem->expandBox.bottom = center+5;
785 wineItem->expandBox.left,
786 wineItem->expandBox.top ,
787 wineItem->expandBox.right,
788 wineItem->expandBox.bottom);
790 MoveToEx (hdc, xpos-2, center, NULL);
791 LineTo (hdc, xpos+3, center);
793 if (!(wineItem->state & TVIS_EXPANDED)) {
794 MoveToEx (hdc, xpos, center-2, NULL);
795 LineTo (hdc, xpos, center+3);
801 * Display the image associated with this item
803 xpos += 13; /* update position */
804 if (wineItem->mask & (TVIF_IMAGE|TVIF_SELECTEDIMAGE)) {
806 HIMAGELIST *himlp = NULL;
808 /* State images are displayed to the left of the Normal image
809 * image number is in state; zero should be `display no image'.
810 * FIXME: that last sentence looks like it needs some checking.
812 if (infoPtr->himlState)
813 himlp=&infoPtr->himlState;
814 imageIndex=wineItem->state>>12;
815 imageIndex++; /* yeah, right */
816 TRACE ("imindex:%d\n",imageIndex);
817 if ((himlp) && (imageIndex))
819 imageIndex--; /* see FIXME */
820 ImageList_Draw ( *himlp, imageIndex, hdc, xpos-2, r.top+1, ILD_NORMAL);
821 ImageList_GetIconSize (*himlp, &cx, &cy);
822 wineItem->statebitmap.left=xpos-2;
823 wineItem->statebitmap.right=xpos-2+cx;
824 wineItem->statebitmap.top=r.top+1;
825 wineItem->statebitmap.bottom=r.top+1+cy;
829 /* Now, draw the normal image; can be either selected or
830 * non-selected image.
834 if (infoPtr->himlNormal)
835 himlp=&infoPtr->himlNormal; /* get the image list */
837 imageIndex = wineItem->iImage;
838 if ( (wineItem->state & TVIS_SELECTED) &&
839 (wineItem->iSelectedImage)) {
841 /* The item is curently selected */
842 if (wineItem->iSelectedImage == I_IMAGECALLBACK)
843 TREEVIEW_SendDispInfoNotify
844 (hwnd, wineItem, TVN_GETDISPINFOA, TVIF_SELECTEDIMAGE);
846 imageIndex = wineItem->iSelectedImage;
848 /* The item is not selected */
849 if (wineItem->iImage == I_IMAGECALLBACK)
850 TREEVIEW_SendDispInfoNotify
851 (hwnd, wineItem, TVN_GETDISPINFOA, TVIF_IMAGE);
853 imageIndex = wineItem->iImage;
860 if(wineItem->stateMask & TVIS_OVERLAYMASK)
861 ovlIdx = wineItem->state & TVIS_OVERLAYMASK;
863 ImageList_Draw ( *himlp, imageIndex, hdc, xpos-2, r.top+1, ILD_NORMAL|ovlIdx);
864 ImageList_GetIconSize (*himlp, &cx, &cy);
865 wineItem->bitmap.left=xpos-2;
866 wineItem->bitmap.right=xpos-2+cx;
867 wineItem->bitmap.top=r.top+1;
868 wineItem->bitmap.bottom=r.top+1+cy;
875 * Display the text associated with this item
878 if ((wineItem->mask & TVIF_TEXT) && (wineItem->pszText))
880 COLORREF oldTextColor = 0;
883 BOOL inFocus = GetFocus() == hwnd;
885 TREEVIEW_ITEM tmpItem;
888 if (wineItem->pszText == LPSTR_TEXTCALLBACKA)
890 tmpItem.hItem = wineItem->hItem;
891 tmpItem.state = wineItem->state;
892 tmpItem.lParam = wineItem->lParam;
893 tmpItem.pszText = buf;
894 tmpItem.cchTextMax = sizeof(buf);
896 TREEVIEW_SendDispInfoNotify(hwnd, &tmpItem, TVN_GETDISPINFOA, TVIF_TEXT);
902 wineItem->text.left = r.left;
903 wineItem->text.right = r.right;
904 wineItem->text.top = r.top;
905 wineItem->text.bottom= r.bottom;
907 oldBkMode = SetBkMode(hdc, TRANSPARENT);
909 /* - If item is drop target or it is selected and window is in focus -
910 * use blue background (COLOR_HIGHLIGHT).
911 * - If item is selected, window is not in focus, but it has style
912 * TVS_SHOWSELALWAYS - use grey background (COLOR_BTNFACE)
913 * - Otherwise - don't fill background
915 if ((wineItem->state & TVIS_DROPHILITED) ||
916 ((wineItem->state & TVIS_SELECTED) &&
917 (inFocus || (GetWindowLongA( hwnd, GWL_STYLE) & TVS_SHOWSELALWAYS))))
919 if ((wineItem->state & TVIS_DROPHILITED) || inFocus)
921 hbrBk = CreateSolidBrush(GetSysColor( COLOR_HIGHLIGHT));
922 oldTextColor = SetTextColor(hdc, GetSysColor( COLOR_HIGHLIGHTTEXT));
926 hbrBk = CreateSolidBrush(GetSysColor( COLOR_BTNFACE));
928 if (infoPtr->clrText == -1)
929 oldTextColor = SetTextColor(hdc, GetSysColor( COLOR_WINDOWTEXT));
931 oldTextColor = SetTextColor(hdc, infoPtr->clrText);
936 if (infoPtr->clrText == -1)
937 oldTextColor = SetTextColor(hdc, GetSysColor( COLOR_WINDOWTEXT));
939 oldTextColor = SetTextColor(hdc, infoPtr->clrText);
942 if (wineItem->pszText != LPSTR_TEXTCALLBACKA)
943 tmpItem.pszText = wineItem->pszText;
945 /* Obtain the text coordinate */
949 lstrlenA(tmpItem.pszText),
951 uTextJustify | DT_VCENTER | DT_SINGLELINE | DT_CALCRECT | DT_NOPREFIX);
953 /* We need to reset it to items height */
954 wineItem->text.top = r.top;
955 wineItem->text.bottom = r.bottom;
956 wineItem->text.right += 4; /* This is extra for focus rectangle */
960 FillRect(hdc, &wineItem->text, hbrBk);
964 wineItem->text.left += 2;
969 lstrlenA(tmpItem.pszText),
971 uTextJustify | DT_VCENTER | DT_SINGLELINE | DT_NOPREFIX);
973 wineItem->text.left -=2;
975 /* Restore the hdc state */
976 SetTextColor( hdc, oldTextColor);
978 /* Draw the box arround the selected item */
979 if (wineItem->state & TVIS_SELECTED && inFocus)
981 HPEN hNewPen = CreatePen(PS_DOT, 0, GetSysColor(COLOR_WINDOWTEXT) );
982 HPEN hOldPen = SelectObject( hdc, hNewPen );
983 INT rop = SetROP2(hdc, R2_XORPEN);
986 points[4].x = points[0].x = wineItem->text.left;
987 points[4].y = points[0].y = wineItem->text.top;
988 points[1].x = wineItem->text.right-1 ;
989 points[1].y = wineItem->text.top;
990 points[2].x = wineItem->text.right-1;
991 points[2].y = wineItem->text.bottom-1;
992 points[3].x = wineItem->text.left;
993 points[3].y = wineItem->text.bottom-1;
995 Polyline (hdc,points,5);
998 DeleteObject(hNewPen);
999 SelectObject(hdc, hOldPen);
1002 if (oldBkMode != TRANSPARENT)
1003 SetBkMode(hdc, oldBkMode);
1006 /* Draw insertion mark if necessary */
1008 if (infoPtr->insertMarkItem)
1009 TRACE ("item:%d,mark:%d\n", (int)wineItem->hItem,
1010 (int) infoPtr->insertMarkItem);
1011 if (wineItem->hItem==infoPtr->insertMarkItem) {
1012 HPEN hNewPen, hOldPen;
1015 hNewPen = CreatePen(PS_SOLID, 2, infoPtr->clrInsertMark);
1016 hOldPen = SelectObject( hdc, hNewPen );
1018 if (infoPtr->insertBeforeorAfter)
1019 offset=wineItem->text.top+1;
1021 offset=wineItem->text.bottom-1;
1023 MoveToEx (hdc, wineItem->text.left, offset-3, NULL);
1024 LineTo (hdc, wineItem->text.left, offset+3);
1026 MoveToEx (hdc, wineItem->text.left, offset, NULL);
1027 LineTo (hdc, r.right-2, offset);
1029 MoveToEx (hdc, r.right-2, offset+3, NULL);
1030 LineTo (hdc, r.right-2, offset-3);
1032 DeleteObject(hNewPen);
1034 SelectObject(hdc, hOldPen);
1037 if (cditem & CDRF_NOTIFYPOSTPAINT) {
1038 cditem=TREEVIEW_SendCustomDrawItemNotify
1039 (hwnd, hdc, wineItem, CDDS_ITEMPOSTPAINT);
1040 TRACE("postpaint:cditem-app returns 0x%x\n",cditem);
1043 SelectObject (hdc, hOldFont);
1047 TREEVIEW_GetItemRect (HWND hwnd, WPARAM wParam, LPARAM lParam)
1049 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
1050 TREEVIEW_ITEM *wineItem;
1052 LPRECT lpRect = (LPRECT)lParam;
1056 * validate parameters
1061 if (infoPtr->Timer & TV_REFRESH_TIMER_SET) {
1062 InvalidateRect(hwnd, NULL, FALSE);
1067 * retrieve the item ptr
1069 iItem = (HTREEITEM *) lParam;
1070 wineItem = TREEVIEW_ValidItem (infoPtr, *iItem);
1071 if ((!wineItem) || (!wineItem->visible))
1075 * If wParam is TRUE return the text size otherwise return
1076 * the whole item size
1079 lpRect->left = wineItem->text.left;
1080 lpRect->right = wineItem->text.right;
1081 lpRect->bottom = wineItem->text.bottom;
1082 lpRect->top = wineItem->text.top;
1084 lpRect->left = wineItem->rect.left;
1085 lpRect->right = wineItem->rect.right;
1086 lpRect->bottom = wineItem->rect.bottom;
1087 lpRect->top = wineItem->rect.top;
1090 TRACE("[L:%d R:%d T:%d B:%d]\n",
1091 lpRect->left,lpRect->right,
1092 lpRect->top,lpRect->bottom);
1098 TREEVIEW_GetVisibleCount (HWND hwnd, WPARAM wParam, LPARAM lParam)
1101 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
1103 return (LRESULT) infoPtr->uVisibleHeight / infoPtr->uRealItemHeight;
1109 TREEVIEW_SetItemA (HWND hwnd, WPARAM wParam, LPARAM lParam)
1111 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
1112 TREEVIEW_ITEM *wineItem;
1116 tvItem=(LPTVITEMEXA) lParam;
1117 iItem=(INT)tvItem->hItem;
1118 TRACE("item %d,mask %x\n",iItem,tvItem->mask);
1120 wineItem = TREEVIEW_ValidItem (infoPtr, (HTREEITEM)iItem);
1121 if (!wineItem) return FALSE;
1123 if (tvItem->mask & TVIF_CHILDREN) {
1124 wineItem->cChildren=tvItem->cChildren;
1127 if (tvItem->mask & TVIF_IMAGE) {
1128 wineItem->iImage=tvItem->iImage;
1131 if (tvItem->mask & TVIF_INTEGRAL) {
1132 wineItem->iIntegral=tvItem->iIntegral;
1135 if (tvItem->mask & TVIF_PARAM) {
1136 wineItem->lParam=tvItem->lParam;
1139 if (tvItem->mask & TVIF_SELECTEDIMAGE) {
1140 wineItem->iSelectedImage=tvItem->iSelectedImage;
1143 if (tvItem->mask & TVIF_STATE) {
1144 TRACE ("prevstate,state,mask:%x,%x,%x\n",wineItem->state,tvItem->state,
1146 wineItem->state&= ~tvItem->stateMask;
1147 wineItem->state|= (tvItem->state & tvItem->stateMask);
1148 wineItem->stateMask|= tvItem->stateMask;
1151 if (tvItem->mask & TVIF_TEXT)
1153 if (tvItem->pszText!=LPSTR_TEXTCALLBACKA)
1155 len=lstrlenA (tvItem->pszText) + 1;
1156 if (len>wineItem->cchTextMax)
1158 wineItem->pszText= COMCTL32_ReAlloc (wineItem->pszText, len);
1159 wineItem->cchTextMax = len;
1162 lstrcpynA (wineItem->pszText, tvItem->pszText,len);
1166 if (wineItem->cchTextMax)
1168 COMCTL32_Free (wineItem->pszText);
1169 wineItem->cchTextMax=0;
1171 wineItem->pszText=LPSTR_TEXTCALLBACKA;
1175 wineItem->mask |= tvItem->mask;
1181 TREEVIEW_GetItemState (HWND hwnd, WPARAM wParam, LPARAM lParam)
1184 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
1185 TREEVIEW_ITEM *wineItem;
1188 wineItem = TREEVIEW_ValidItem (infoPtr, (HTREEITEM)wParam);
1189 if (!wineItem) return 0;
1191 return (wineItem->state & lParam);
1198 TREEVIEW_Refresh (HWND hwnd, HDC hdc)
1200 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
1206 INT iItem, indent, x, y, height, itemHeight;
1207 INT viewtop,viewbottom,viewleft,viewright;
1208 TREEVIEW_ITEM *wineItem, *prevItem;
1213 if (infoPtr->Timer & TV_REFRESH_TIMER_SET) {
1214 KillTimer (hwnd, TV_REFRESH_TIMER);
1215 infoPtr->Timer &= ~TV_REFRESH_TIMER_SET;
1219 GetClientRect (hwnd, &rect);
1220 if ((rect.left >= rect.right) || (rect.top >= rect.bottom)) return;
1222 infoPtr->cdmode=TREEVIEW_SendCustomDrawNotify(hwnd,CDDS_PREPAINT,hdc,rect);
1224 if (infoPtr->cdmode==CDRF_SKIPDEFAULT) return;
1226 infoPtr->uVisibleHeight= rect.bottom-rect.top + 1;
1227 infoPtr->uVisibleWidth= rect.right-rect.left + 1;
1229 viewtop=infoPtr->cy;
1230 viewbottom=infoPtr->cy + rect.bottom-rect.top;
1231 viewleft=infoPtr->cx;
1232 viewright=infoPtr->cx + rect.right-rect.left;
1234 TRACE("[%d %d %d %d]\n",viewtop,viewbottom,viewleft,viewright);
1236 /* draw background */
1238 hbrBk = CreateSolidBrush (infoPtr->clrBk);
1239 FillRect(hdc, &rect, hbrBk);
1240 DeleteObject(hbrBk);
1242 ImageList_GetIconSize (infoPtr->himlNormal, &x, &itemHeight);
1243 if (infoPtr->uItemHeight>itemHeight)
1244 itemHeight=infoPtr->uItemHeight;
1246 // assume that bold and normal fonts have same height
1247 hOldFont = SelectObject (hdc, infoPtr->hBoldFont);
1248 GetTextMetricsA (hdc, &tm);
1249 if ((tm.tmHeight + tm.tmExternalLeading + FOCUS_BORDER) > itemHeight)
1250 itemHeight=tm.tmHeight + tm.tmExternalLeading + FOCUS_BORDER;
1251 SelectObject (hdc, hOldFont);
1253 infoPtr->uRealItemHeight=itemHeight;
1255 iItem=(INT)infoPtr->TopRootItem;
1256 infoPtr->firstVisible=0;
1263 wineItem= & infoPtr->items[iItem];
1264 wineItem->iLevel=indent;
1266 /* FIXME: remove this in later stage */
1268 if (wineItem->pszText!=LPSTR_TEXTCALLBACK32A)
1269 TRACE (treeview, "%d %d [%d %d %d %d] (%s)\n",y,x,
1270 wineItem->rect.top, wineItem->rect.bottom,
1271 wineItem->rect.left, wineItem->rect.right,
1274 TRACE (treeview, "%d [%d %d %d %d] (CALLBACK)\n",
1276 wineItem->rect.top, wineItem->rect.bottom,
1277 wineItem->rect.left, wineItem->rect.right);
1280 height=itemHeight * wineItem->iIntegral;
1281 if ((y >= viewtop) && (y <= viewbottom) &&
1282 (x >= viewleft ) && (x <= viewright)) {
1283 wineItem->visible = TRUE;
1284 wineItem->rect.top = y - infoPtr->cy + rect.top;
1285 wineItem->rect.bottom = wineItem->rect.top + height-1;
1286 wineItem->rect.left = x - infoPtr->cx + rect.left;
1287 wineItem->rect.right = rect.right;
1288 if (!infoPtr->firstVisible)
1289 infoPtr->firstVisible=wineItem->hItem;
1290 TREEVIEW_DrawItem (hwnd, hdc, wineItem);
1293 wineItem->visible = FALSE;
1294 wineItem->rect.left = wineItem->rect.top = 0;
1295 wineItem->rect.right= wineItem->rect.bottom = 0;
1296 wineItem->text.left = wineItem->text.top = 0;
1297 wineItem->text.right= wineItem->text.bottom = 0;
1300 /* look up next item */
1302 if ((wineItem->firstChild) && (wineItem->state & TVIS_EXPANDED)) {
1303 iItem=(INT)wineItem->firstChild;
1305 x+=infoPtr->uIndent;
1306 if (x>infoPtr->uTotalWidth)
1307 infoPtr->uTotalWidth=x;
1310 iItem=(INT)wineItem->sibling;
1311 while ((!iItem) && (indent>0)) {
1313 x-=infoPtr->uIndent;
1314 wineItem=&infoPtr->items[(INT)wineItem->parent];
1315 iItem=(INT)wineItem->sibling;
1321 /* FIXME: infoPtr->uTotalWidth should also take item label into account */
1322 /* FIXME: or should query item sizes (ie check CDRF_NEWFONT) */
1324 infoPtr->uTotalHeight=y;
1325 if (y >= (viewbottom-viewtop)) {
1326 if (!(infoPtr->uInternalStatus & TV_VSCROLL))
1327 ShowScrollBar (hwnd, SB_VERT, TRUE);
1328 infoPtr->uInternalStatus |=TV_VSCROLL;
1329 SetScrollRange (hwnd, SB_VERT, 0,
1330 y - infoPtr->uVisibleHeight, FALSE);
1331 SetScrollPos (hwnd, SB_VERT, infoPtr->cy, TRUE);
1334 if (infoPtr->uInternalStatus & TV_VSCROLL)
1335 ShowScrollBar (hwnd, SB_VERT, FALSE);
1336 infoPtr->uInternalStatus &= ~TV_VSCROLL;
1340 if (infoPtr->cdmode & CDRF_NOTIFYPOSTPAINT)
1341 infoPtr->cdmode=TREEVIEW_SendCustomDrawNotify
1342 (hwnd, CDDS_POSTPAINT, hdc, rect);
1349 TREEVIEW_HandleTimer (HWND hwnd, WPARAM wParam, LPARAM lParam)
1351 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
1353 TRACE(" %d\n",wParam);
1356 case TV_REFRESH_TIMER:
1357 KillTimer (hwnd, TV_REFRESH_TIMER);
1358 infoPtr->Timer &= ~TV_REFRESH_TIMER_SET;
1359 InvalidateRect(hwnd, NULL, FALSE);
1362 KillTimer (hwnd, TV_EDIT_TIMER);
1363 infoPtr->Timer &= ~TV_EDIT_TIMER_SET;
1366 ERR("got unknown timer\n");
1374 TREEVIEW_QueueRefresh (HWND hwnd)
1377 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
1380 if (infoPtr->Timer & TV_REFRESH_TIMER_SET) {
1381 KillTimer (hwnd, TV_REFRESH_TIMER);
1384 SetTimer (hwnd, TV_REFRESH_TIMER, TV_REFRESH_DELAY, 0);
1385 infoPtr->Timer|=TV_REFRESH_TIMER_SET;
1391 TREEVIEW_GetItemA (HWND hwnd, WPARAM wParam, LPARAM lParam)
1393 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
1395 TREEVIEW_ITEM *wineItem;
1398 tvItem=(LPTVITEMEXA) lParam;
1399 iItem=(INT)tvItem->hItem;
1401 wineItem = TREEVIEW_ValidItem (infoPtr, (HTREEITEM)iItem);
1402 if (!wineItem) return FALSE;
1404 if (tvItem->mask & TVIF_CHILDREN) {
1405 if (TVIF_CHILDREN==I_CHILDRENCALLBACK)
1406 FIXME("I_CHILDRENCALLBACK not supported\n");
1407 tvItem->cChildren=wineItem->cChildren;
1410 if (tvItem->mask & TVIF_HANDLE) {
1411 tvItem->hItem=wineItem->hItem;
1414 if (tvItem->mask & TVIF_IMAGE) {
1415 tvItem->iImage=wineItem->iImage;
1418 if (tvItem->mask & TVIF_INTEGRAL) {
1419 tvItem->iIntegral=wineItem->iIntegral;
1422 /* undocumented: windows ignores TVIF_PARAM and
1423 * always sets lParam
1425 tvItem->lParam=wineItem->lParam;
1427 if (tvItem->mask & TVIF_SELECTEDIMAGE) {
1428 tvItem->iSelectedImage=wineItem->iSelectedImage;
1431 if (tvItem->mask & TVIF_STATE) {
1432 tvItem->state=wineItem->state & tvItem->stateMask;
1435 if (tvItem->mask & TVIF_TEXT) {
1436 if (wineItem->pszText == LPSTR_TEXTCALLBACKA) {
1437 tvItem->pszText = LPSTR_TEXTCALLBACKA; /* FIXME:send notification? */
1438 ERR(" GetItem called with LPSTR_TEXTCALLBACK\n");
1440 else if (wineItem->pszText) {
1441 lstrcpynA (tvItem->pszText, wineItem->pszText, tvItem->cchTextMax);
1445 TRACE("item %d<%p>, txt %p, img %p, action %x\n",
1446 iItem, tvItem, tvItem->pszText, &tvItem->iImage, tvItem->mask);
1453 TREEVIEW_GetItemW (HWND hwnd, WPARAM wParam, LPARAM lParam)
1455 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
1457 TREEVIEW_ITEM *wineItem;
1460 tvItem=(LPTVITEMEXA) lParam;
1461 iItem=(INT)tvItem->hItem;
1463 wineItem = TREEVIEW_ValidItem (infoPtr, (HTREEITEM)iItem);
1464 if (!wineItem) return FALSE;
1466 if (tvItem->mask & TVIF_CHILDREN) {
1467 if (TVIF_CHILDREN==I_CHILDRENCALLBACK)
1468 FIXME("I_CHILDRENCALLBACK not supported\n");
1469 tvItem->cChildren=wineItem->cChildren;
1472 if (tvItem->mask & TVIF_HANDLE) {
1473 tvItem->hItem=wineItem->hItem;
1476 if (tvItem->mask & TVIF_IMAGE) {
1477 tvItem->iImage=wineItem->iImage;
1480 if (tvItem->mask & TVIF_INTEGRAL) {
1481 tvItem->iIntegral=wineItem->iIntegral;
1484 /* undocumented: windows ignores TVIF_PARAM and
1485 * always sets lParam
1487 tvItem->lParam=wineItem->lParam;
1489 if (tvItem->mask & TVIF_SELECTEDIMAGE) {
1490 tvItem->iSelectedImage=wineItem->iSelectedImage;
1493 if (tvItem->mask & TVIF_STATE) {
1494 tvItem->state=wineItem->state & tvItem->stateMask;
1498 if (tvItem->mask & TVIF_TEXT) {
1499 if (wineItem->pszText == LPSTR_TEXTCALLBACKW) {
1500 tvItem->pszText = LPSTR_TEXTCALLBACKW; /* FIXME:send notification? */
1501 ERR(" GetItem called with LPSTR_TEXTCALLBACK\n");
1503 else if (wineItem->pszText) {
1504 lstrcpynAtoW (tvItem->pszText, wineItem->pszText, tvItem->cchTextMax);
1508 wineItem->pszText = NULL;
1510 TRACE("item %d<%p>, txt %p, img %p, action %x\n",
1511 iItem, tvItem, tvItem->pszText, &tvItem->iImage, tvItem->mask);
1518 /* FIXME: check implementation of TVGN_NEXT/TVGN_NEXTVISIBLE */
1521 TREEVIEW_GetNextItem (HWND hwnd, WPARAM wParam, LPARAM lParam)
1524 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
1525 TREEVIEW_ITEM *wineItem, *returnItem;
1526 INT iItem = (INT)lParam, retval = 0, flag = (INT)wParam;
1530 retval = (INT)infoPtr->TopRootItem;
1534 retval = (INT)infoPtr->selectedItem;
1537 case TVGN_FIRSTVISIBLE: /* FIXME:we should only recalculate, not redraw */
1538 InvalidateRect(hwnd, NULL, FALSE);
1539 retval = (INT)infoPtr->firstVisible;
1542 case TVGN_DROPHILITE:
1543 retval = (INT)infoPtr->dropItem;
1547 wineItem = TREEVIEW_ValidItem (infoPtr, (HTREEITEM)iItem);
1548 retval = wineItem ? (INT)wineItem->sibling : 0;
1552 wineItem = TREEVIEW_ValidItem (infoPtr, (HTREEITEM)iItem);
1553 retval = wineItem ? (INT)wineItem->upsibling : 0;
1557 wineItem = TREEVIEW_ValidItem (infoPtr, (HTREEITEM)iItem);
1558 retval = wineItem ? (INT)wineItem->parent : 0;
1562 wineItem = TREEVIEW_ValidItem (infoPtr, (HTREEITEM)iItem);
1563 retval = wineItem ? (INT)wineItem->firstChild : 0;
1566 case TVGN_LASTVISIBLE:
1567 if((wineItem = TREEVIEW_ValidItem (infoPtr, (HTREEITEM)iItem))) {
1568 returnItem = TREEVIEW_GetLastListItem (infoPtr,wineItem);
1569 retval = returnItem ? (INT)returnItem->hItem : 0;
1573 case TVGN_NEXTVISIBLE:
1574 if((wineItem = TREEVIEW_ValidItem (infoPtr, (HTREEITEM)iItem))) {
1575 returnItem = TREEVIEW_GetNextListItem (infoPtr,wineItem);
1576 retval = returnItem ? (INT)returnItem->hItem : 0;
1580 case TVGN_PREVIOUSVISIBLE:
1581 if((wineItem = TREEVIEW_ValidItem (infoPtr, (HTREEITEM)iItem))) {
1582 returnItem = TREEVIEW_GetPrevListItem (infoPtr, wineItem);
1583 retval = returnItem ? (INT)returnItem->hItem : 0;
1588 FIXME("Unknown msg %x,item %x\n", flag,iItem);
1592 TRACE("flags %x, item %d returns %d\n", flag, iItem, retval);
1598 TREEVIEW_GetCount (HWND hwnd, WPARAM wParam, LPARAM lParam)
1600 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
1602 TRACE(" %d\n",infoPtr->uNumItems);
1603 return (LRESULT) infoPtr->uNumItems;
1606 /***************************************************************************
1607 * This method does the chaining of the insertion of a treeview item
1609 * If parent is NULL, we're inserting at the root of the list.
1611 static void TREEVIEW_InsertBefore(
1612 TREEVIEW_INFO *infoPtr,
1613 TREEVIEW_ITEM *newItem,
1614 TREEVIEW_ITEM *sibling,
1615 TREEVIEW_ITEM *parent)
1617 HTREEITEM siblingHandle = 0;
1618 HTREEITEM upSiblingHandle = 0;
1619 TREEVIEW_ITEM *upSibling = NULL;
1621 if (newItem == NULL)
1622 ERR("NULL newItem, impossible condition\n");
1624 if (sibling != NULL) /* Insert before this sibling for this parent */
1626 /* Store the new item sibling up sibling and sibling tem handle */
1627 siblingHandle = sibling->hItem;
1628 upSiblingHandle = sibling->upsibling;
1629 /* As well as a pointer to the upsibling sibling object */
1630 if ( (INT)sibling->upsibling != 0 )
1631 upSibling = &infoPtr->items[(INT)sibling->upsibling];
1633 /* Adjust the sibling pointer */
1634 sibling->upsibling = newItem->hItem;
1636 /* Adjust the new item pointers */
1637 newItem->upsibling = upSiblingHandle;
1638 newItem->sibling = siblingHandle;
1640 /* Adjust the up sibling pointer */
1641 if ( upSibling != NULL )
1642 upSibling->sibling = newItem->hItem;
1644 /* this item is the first child of this parent, adjust parent pointers */
1646 parent->firstChild = newItem->hItem;
1648 infoPtr->TopRootItem= newItem->hItem;
1650 else /* Insert as first child of this parent */
1652 parent->firstChild = newItem->hItem;
1655 /***************************************************************************
1656 * This method does the chaining of the insertion of a treeview item
1658 * If parent is NULL, we're inserting at the root of the list.
1660 static void TREEVIEW_InsertAfter(
1661 TREEVIEW_INFO *infoPtr,
1662 TREEVIEW_ITEM *newItem,
1663 TREEVIEW_ITEM *upSibling,
1664 TREEVIEW_ITEM *parent)
1666 HTREEITEM upSiblingHandle = 0;
1667 HTREEITEM siblingHandle = 0;
1668 TREEVIEW_ITEM *sibling = NULL;
1671 if (newItem == NULL)
1672 ERR("NULL newItem, impossible condition\n");
1674 if (upSibling != NULL) /* Insert after this upsibling for this parent */
1676 /* Store the new item up sibling and sibling item handle */
1677 upSiblingHandle = upSibling->hItem;
1678 siblingHandle = upSibling->sibling;
1679 /* As well as a pointer to the upsibling sibling object */
1680 if ( (INT)upSibling->sibling != 0 )
1681 sibling = &infoPtr->items[(INT)upSibling->sibling];
1683 /* Adjust the up sibling pointer */
1684 upSibling->sibling = newItem->hItem;
1686 /* Adjust the new item pointers */
1687 newItem->upsibling = upSiblingHandle;
1688 newItem->sibling = siblingHandle;
1690 /* Adjust the sibling pointer */
1691 if ( sibling != NULL )
1692 sibling->upsibling = newItem->hItem;
1695 newItem is the last of the level, nothing else to do
1698 else /* Insert as first child of this parent */
1700 parent->firstChild = newItem->hItem;
1703 /***************************************************************************
1704 * Forward the DPA local callback to the treeview owner callback
1706 static INT WINAPI TREEVIEW_CallBackCompare(
1711 /* Forward the call to the client define callback */
1712 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr((HWND)tvInfoPtr);
1713 return (infoPtr->pCallBackSort->lpfnCompare)(
1714 ((TREEVIEW_ITEM*)first)->lParam,
1715 ((TREEVIEW_ITEM*)second)->lParam,
1716 infoPtr->pCallBackSort->lParam);
1719 /***************************************************************************
1720 * Treeview native sort routine: sort on item text.
1722 static INT WINAPI TREEVIEW_SortOnName (
1727 HWND hwnd=(HWND) tvInfoPtr;
1729 TREEVIEW_ITEM *item;
1732 item=(TREEVIEW_ITEM *) first;
1733 if (item->pszText==LPSTR_TEXTCALLBACKA) {
1734 TREEVIEW_SendDispInfoNotify (hwnd, item, TVN_GETDISPINFOA, TVIF_TEXT);
1738 item=(TREEVIEW_ITEM *) second;
1739 if (item->pszText==LPSTR_TEXTCALLBACKA) {
1740 TREEVIEW_SendDispInfoNotify (hwnd, item, TVN_GETDISPINFOA, TVIF_TEXT);
1744 return -strcmp (txt1,txt2);
1747 /***************************************************************************
1748 * Setup the treeview structure with regards of the sort method
1749 * and sort the children of the TV item specified in lParam
1750 * fRecurse: currently unused. Should be zero.
1751 * parent: if pSort!=NULL, should equal pSort->hParent.
1752 * otherwise, item which child items are to be sorted.
1753 * pSort: sort method info. if NULL, sort on item text.
1754 * if non-NULL, sort on item's lParam content, and let the
1755 * application decide what that means. See also TVM_SORTCHILDRENCB.
1758 static LRESULT WINAPI TREEVIEW_Sort (
1765 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
1766 TREEVIEW_ITEM *sortMe = NULL; /* Node for which we sort the children */
1768 /* Obtain the TVSORTBC struct */
1769 infoPtr->pCallBackSort = pSort;
1771 /* undocumented feature: TVI_ROOT means `sort the whole tree' */
1773 if (parent==TVI_ROOT)
1774 parent=infoPtr->TopRootItem;
1776 /* Check for a valid handle to the parent item */
1777 if (!TREEVIEW_ValidItem(infoPtr, parent))
1779 ERR ("invalid item hParent=%x\n", (INT)parent);
1783 /* Obtain the parent node to sort */
1784 sortMe = &infoPtr->items[ (INT)parent ];
1786 /* Make sure there is something to sort */
1787 if ( sortMe->cChildren > 1 )
1789 /* pointer organization */
1790 HDPA sortList = DPA_Create(sortMe->cChildren);
1791 HTREEITEM itemHandle = sortMe->firstChild;
1792 TREEVIEW_ITEM *itemPtr = & infoPtr->items[ (INT)itemHandle ];
1794 /* TREEVIEW_ITEM rechaining */
1800 /* Build the list of item to sort */
1804 sortList, /* the list */
1805 sortMe->cChildren+1, /* force the insertion to be an append */
1806 itemPtr); /* the ptr to store */
1808 /* Get the next sibling */
1809 itemHandle = itemPtr->sibling;
1810 itemPtr = & infoPtr->items[ (INT)itemHandle ];
1811 } while ( itemHandle != NULL );
1813 /* let DPA perform the sort activity */
1816 sortList, /* what */
1817 TREEVIEW_CallBackCompare, /* how */
1821 sortList, /* what */
1822 TREEVIEW_SortOnName, /* how */
1826 * Reorganized TREEVIEW_ITEM structures.
1827 * Note that we know we have at least two elements.
1830 /* Get the first item and get ready to start... */
1831 item = DPA_GetPtr(sortList, count++);
1832 while ( (nextItem = DPA_GetPtr(sortList, count++)) != NULL )
1834 /* link the two current item toghether */
1835 ((TREEVIEW_ITEM*)item)->sibling = ((TREEVIEW_ITEM*)nextItem)->hItem;
1836 ((TREEVIEW_ITEM*)nextItem)->upsibling = ((TREEVIEW_ITEM*)item)->hItem;
1838 if (prevItem == NULL) /* this is the first item, update the parent */
1840 sortMe->firstChild = ((TREEVIEW_ITEM*)item)->hItem;
1841 ((TREEVIEW_ITEM*)item)->upsibling = NULL;
1843 else /* fix the back chaining */
1845 ((TREEVIEW_ITEM*)item)->upsibling = ((TREEVIEW_ITEM*)prevItem)->hItem;
1848 /* get ready for the next one */
1853 /* the last item is pointed to by item and never has a sibling */
1854 ((TREEVIEW_ITEM*)item)->sibling = NULL;
1856 DPA_Destroy(sortList);
1864 /***************************************************************************
1865 * Setup the treeview structure with regards of the sort method
1866 * and sort the children of the TV item specified in lParam
1868 static LRESULT WINAPI TREEVIEW_SortChildrenCB(
1874 LPTVSORTCB pSort=(LPTVSORTCB) lParam;
1876 return TREEVIEW_Sort (hwnd, wParam, pSort->hParent, pSort);
1880 /***************************************************************************
1881 * Sort the children of the TV item specified in lParam.
1883 static LRESULT WINAPI TREEVIEW_SortChildren (
1888 return TREEVIEW_Sort (hwnd, (BOOL) wParam, (HTREEITEM) lParam, NULL);
1893 /* the method used below isn't the most memory-friendly, but it avoids
1894 a lot of memory reallocations */
1896 /* BTW: we waste handle 0; 0 is not an allowed handle. */
1899 TREEVIEW_InsertItemA (HWND hwnd, WPARAM wParam, LPARAM lParam)
1902 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
1903 TVINSERTSTRUCTA *ptdi;
1905 TREEVIEW_ITEM *wineItem, *parentItem, *prevsib, *sibItem;
1906 INT iItem,listItems,i,len;
1908 /* Item to insert */
1909 ptdi = (LPTVINSERTSTRUCTA) lParam;
1911 /* check if memory is available */
1913 if (infoPtr->uNumPtrsAlloced==0) {
1914 infoPtr->items = COMCTL32_Alloc (TVITEM_ALLOC*sizeof (TREEVIEW_ITEM));
1915 infoPtr->freeList= COMCTL32_Alloc ((1+(TVITEM_ALLOC>>5)) * sizeof (INT));
1916 infoPtr->uNumPtrsAlloced=TVITEM_ALLOC;
1917 infoPtr->TopRootItem=(HTREEITEM)1;
1921 * Reallocate contiguous space for items
1923 if (infoPtr->uNumItems == (infoPtr->uNumPtrsAlloced-1) ) {
1924 TREEVIEW_ITEM *oldItems = infoPtr->items;
1925 INT *oldfreeList = infoPtr->freeList;
1927 infoPtr->uNumPtrsAlloced*=2;
1928 infoPtr->items = COMCTL32_Alloc (infoPtr->uNumPtrsAlloced*sizeof (TREEVIEW_ITEM));
1929 infoPtr->freeList= COMCTL32_Alloc ((1+(infoPtr->uNumPtrsAlloced>>5))*sizeof (INT));
1931 memcpy (&infoPtr->items[0], &oldItems[0],
1932 infoPtr->uNumPtrsAlloced/2 * sizeof(TREEVIEW_ITEM));
1933 memcpy (&infoPtr->freeList[0], &oldfreeList[0],
1934 (infoPtr->uNumPtrsAlloced>>6) * sizeof(INT));
1936 COMCTL32_Free (oldItems);
1937 COMCTL32_Free (oldfreeList);
1941 * Reset infoPtr structure with new stat according to current TV picture
1944 infoPtr->uNumItems++;
1945 if ((INT)infoPtr->uMaxHandle==(infoPtr->uNumItems-1)) {
1946 iItem=infoPtr->uNumItems;
1947 infoPtr->uMaxHandle = (HTREEITEM)((INT)infoPtr->uMaxHandle + 1);
1948 } else { /* check freelist */
1949 for (i=0; i<=infoPtr->uNumPtrsAlloced>>5; i++) {
1950 if (infoPtr->freeList[i]) {
1951 iItem=ffs (infoPtr->freeList[i])-1;
1952 tv_clear_bit(iItem,&infoPtr->freeList[i]);
1959 if (TRACE_ON(treeview)) {
1960 for (i=0; i<=infoPtr->uNumPtrsAlloced>>5; i++)
1961 TRACE("%8x\n",infoPtr->freeList[i]);
1964 if (!iItem) ERR("Argh -- can't find free item.\n");
1967 * Find the parent item of the new item
1969 tvItem= & ptdi->DUMMYUNIONNAME.itemex;
1970 wineItem=& infoPtr->items[iItem];
1972 if ((ptdi->hParent==TVI_ROOT) || (ptdi->hParent==0)) {
1974 wineItem->parent = 0;
1975 sibItem = &infoPtr->items [(INT)infoPtr->TopRootItem];
1976 listItems = infoPtr->uNumItems;
1979 parentItem = &infoPtr->items[(INT)ptdi->hParent];
1981 /* Do the insertion here it if it's the only item of this parent */
1982 if (!parentItem->firstChild)
1983 parentItem->firstChild=(HTREEITEM)iItem;
1985 wineItem->parent = ptdi->hParent;
1986 sibItem = &infoPtr->items [(INT)parentItem->firstChild];
1987 listItems = parentItem->cChildren;
1988 parentItem->cChildren++;
1992 /* NOTE: I am moving some setup of the wineItem object that was initialy
1993 * done at the end of the function since some of the values are
1994 * required by the Callback sorting
1997 if (tvItem->mask & TVIF_TEXT)
2000 * Setup the item text stuff here since it's required by the Sort method
2001 * when the insertion are ordered
2003 if (tvItem->pszText!=LPSTR_TEXTCALLBACKA)
2005 TRACE("(%p,%s)\n", &tvItem->pszText, tvItem->pszText);
2006 len = lstrlenA (tvItem->pszText)+1;
2007 wineItem->pszText= COMCTL32_Alloc (len+1);
2008 strcpy (wineItem->pszText, tvItem->pszText);
2009 wineItem->cchTextMax=len;
2013 TRACE("LPSTR_TEXTCALLBACK\n");
2014 wineItem->pszText = LPSTR_TEXTCALLBACKA;
2015 wineItem->cchTextMax = 0;
2019 if (tvItem->mask & TVIF_PARAM)
2020 wineItem->lParam=tvItem->lParam;
2023 wineItem->upsibling=0; /* needed in case we're the first item in a list */
2024 wineItem->sibling=0;
2025 wineItem->firstChild=0;
2026 wineItem->hItem=(HTREEITEM)iItem;
2031 switch ((DWORD) ptdi->hInsertAfter) {
2032 case (DWORD) TVI_FIRST:
2033 if (sibItem==wineItem) break;
2034 if (wineItem->parent) {
2035 wineItem->sibling=parentItem->firstChild;
2036 parentItem->firstChild=(HTREEITEM)iItem;
2038 wineItem->sibling=infoPtr->TopRootItem;
2039 infoPtr->TopRootItem=(HTREEITEM)iItem;
2041 sibItem->upsibling=(HTREEITEM)iItem;
2044 case (DWORD) TVI_SORT:
2045 if (sibItem==wineItem)
2047 * This item is the first child of the level and it
2048 * has already been inserted
2053 TREEVIEW_ITEM *aChild;
2056 TREEVIEW_ITEM *previousChild = NULL;
2057 BOOL bItemInserted = FALSE;
2060 aChild = &infoPtr->items[(INT)parentItem->firstChild];
2062 aChild = &infoPtr->items[(INT)infoPtr->TopRootItem];
2064 /* lookup the text if using LPSTR_TEXTCALLBACKs */
2065 if (wineItem->pszText==LPSTR_TEXTCALLBACKA) {
2066 TREEVIEW_SendDispInfoNotify (hwnd, wineItem, TVN_GETDISPINFOA, TVIF_TEXT);
2069 /* Iterate the parent children to see where we fit in */
2070 while ( aChild != NULL )
2074 /* lookup the text if using LPSTR_TEXTCALLBACKs */
2075 if (aChild->pszText==LPSTR_TEXTCALLBACKA) {
2076 TREEVIEW_SendDispInfoNotify (hwnd, aChild, TVN_GETDISPINFOA, TVIF_TEXT);
2079 comp = strcmp(wineItem->pszText, aChild->pszText);
2080 if ( comp < 0 ) /* we are smaller than the current one */
2082 TREEVIEW_InsertBefore(infoPtr, wineItem, aChild, parentItem);
2083 bItemInserted = TRUE;
2086 else if ( comp > 0 ) /* we are bigger than the current one */
2088 previousChild = aChild;
2089 aChild = (aChild->sibling == 0) /* This will help us to exit */
2090 ? NULL /* if there is no more sibling */
2091 : &infoPtr->items[(INT)aChild->sibling];
2093 /* Look at the next item */
2096 else if ( comp == 0 )
2099 * An item with this name is already existing, therefore,
2100 * we add after the one we found
2102 TREEVIEW_InsertAfter(infoPtr, wineItem, aChild, parentItem);
2103 bItemInserted = TRUE;
2109 * we reach the end of the child list and the item as not
2110 * yet been inserted, therefore, insert it after the last child.
2112 if ( (! bItemInserted ) && (aChild == NULL) )
2113 TREEVIEW_InsertAfter(infoPtr, wineItem, previousChild, parentItem);
2119 case (DWORD) TVI_LAST:
2120 if (sibItem==wineItem) break;
2121 while (sibItem->sibling) {
2123 sibItem=&infoPtr->items [(INT)sibItem->sibling];
2125 sibItem->sibling=(HTREEITEM)iItem;
2126 wineItem->upsibling=sibItem->hItem;
2130 TREEVIEW_ITEM *localsibItem = sibItem;
2131 while ((localsibItem->sibling) &&
2132 (localsibItem->hItem!=ptdi->hInsertAfter))
2134 prevsib=localsibItem;
2135 localsibItem=&infoPtr->items [(INT)localsibItem->sibling];
2137 if (localsibItem->hItem!=ptdi->hInsertAfter) {
2138 WARN("tried to insert item after nonexisting handle %d treating as TVI_LAST.\n",
2139 (INT) ptdi->hInsertAfter);
2141 * retry placing it last
2143 if (sibItem==wineItem) break;
2144 while (sibItem->sibling) {
2146 sibItem=&infoPtr->items [(INT)sibItem->sibling];
2148 sibItem->sibling=(HTREEITEM)iItem;
2149 wineItem->upsibling=sibItem->hItem;
2152 prevsib=localsibItem;
2153 if (localsibItem->sibling) {
2154 localsibItem=&infoPtr->items [(INT)localsibItem->sibling];
2155 localsibItem->upsibling=(HTREEITEM)iItem;
2156 wineItem->sibling=localsibItem->hItem;
2158 prevsib->sibling=(HTREEITEM)iItem;
2159 wineItem->upsibling=prevsib->hItem;
2166 /* Fill in info structure */
2168 TRACE("new item %d; parent %d, mask %x\n", iItem,
2169 (INT)wineItem->parent,tvItem->mask);
2171 wineItem->mask=tvItem->mask;
2172 wineItem->iIntegral=1;
2174 if (tvItem->mask & TVIF_CHILDREN) {
2175 wineItem->cChildren=tvItem->cChildren;
2176 if (tvItem->cChildren==I_CHILDRENCALLBACK)
2177 FIXME(" I_CHILDRENCALLBACK not supported\n");
2180 wineItem->expandBox.left = 0; /* Initialize the expandBox */
2181 wineItem->expandBox.top = 0;
2182 wineItem->expandBox.right = 0;
2183 wineItem->expandBox.bottom = 0;
2185 if (tvItem->mask & TVIF_IMAGE)
2186 wineItem->iImage=tvItem->iImage;
2188 /* If the application sets TVIF_INTEGRAL without
2189 supplying a TVITEMEX structure, it's toast */
2191 if (tvItem->mask & TVIF_INTEGRAL)
2192 wineItem->iIntegral=tvItem->iIntegral;
2194 if (tvItem->mask & TVIF_SELECTEDIMAGE)
2195 wineItem->iSelectedImage=tvItem->iSelectedImage;
2197 if (tvItem->mask & TVIF_STATE) {
2198 TRACE("item state: %x ->%x\n", wineItem->state, tvItem->state);
2199 TRACE("statemask: %x ->%x\n", wineItem->stateMask, tvItem->stateMask);
2200 wineItem->state=tvItem->state;
2201 wineItem->stateMask=tvItem->stateMask;
2204 TREEVIEW_QueueRefresh (hwnd);
2206 return (LRESULT) iItem;
2211 TREEVIEW_InsertItemW(HWND hwnd, WPARAM wParam, LPARAM lParam)
2213 TVINSERTSTRUCTW *tvisW;
2214 TVINSERTSTRUCTA tvisA;
2217 tvisW = (LPTVINSERTSTRUCTW)lParam;
2219 tvisA.hParent = tvisW->hParent;
2220 tvisA.hInsertAfter = tvisW->hInsertAfter;
2222 tvisA.DUMMYUNIONNAME.item.mask = tvisW->DUMMYUNIONNAME.item.mask;
2223 tvisA.DUMMYUNIONNAME.item.hItem = tvisW->DUMMYUNIONNAME.item.hItem;
2224 tvisA.DUMMYUNIONNAME.item.state = tvisW->DUMMYUNIONNAME.item.state;
2225 tvisA.DUMMYUNIONNAME.item.stateMask = tvisW->DUMMYUNIONNAME.item.stateMask;
2226 tvisA.DUMMYUNIONNAME.item.cchTextMax = tvisW->DUMMYUNIONNAME.item.cchTextMax;
2228 if(tvisW->DUMMYUNIONNAME.item.pszText)
2230 if (tvisW->DUMMYUNIONNAME.item.pszText!=LPSTR_TEXTCALLBACKW)
2232 int len = lstrlenW (tvisW->DUMMYUNIONNAME.item.pszText)+1;
2233 tvisA.DUMMYUNIONNAME.item.pszText = COMCTL32_Alloc (len);
2234 lstrcpyWtoA (tvisA.DUMMYUNIONNAME.item.pszText,
2235 tvisW->DUMMYUNIONNAME.item.pszText );
2239 tvisA.DUMMYUNIONNAME.item.pszText = LPSTR_TEXTCALLBACKA;
2240 tvisA.DUMMYUNIONNAME.item.cchTextMax = 0;
2244 tvisA.DUMMYUNIONNAME.item.iImage = tvisW->DUMMYUNIONNAME.item.iImage;
2245 tvisA.DUMMYUNIONNAME.item.iSelectedImage = tvisW->DUMMYUNIONNAME.item.iSelectedImage;
2246 tvisA.DUMMYUNIONNAME.item.cChildren = tvisW->DUMMYUNIONNAME.item.cChildren;
2247 tvisA.DUMMYUNIONNAME.item.lParam = tvisW->DUMMYUNIONNAME.item.lParam;
2249 lRes = TREEVIEW_InsertItemA(hwnd,wParam,(LPARAM)&tvisA);
2251 if (tvisA.DUMMYUNIONNAME.item.pszText!=LPSTR_TEXTCALLBACKA)
2253 COMCTL32_Free(tvisA.DUMMYUNIONNAME.item.pszText);
2262 TREEVIEW_DeleteItem (HWND hwnd, WPARAM wParam, LPARAM lParam)
2264 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
2266 TREEVIEW_ITEM *wineItem;
2268 TRACE("item = %08lx\n", lParam);
2270 if (lParam == (INT)TVI_ROOT) {
2271 TREEVIEW_RemoveTree (hwnd);
2273 iItem= (INT) lParam;
2274 wineItem = TREEVIEW_ValidItem (infoPtr, (HTREEITEM)iItem);
2275 if (!wineItem) return FALSE;
2277 if (wineItem->pszText==LPSTR_TEXTCALLBACKA)
2278 TRACE("LPSTR_TEXTCALLBACK\n");
2280 TRACE("%s\n",wineItem->pszText);
2281 TREEVIEW_RemoveItem (hwnd, wineItem);
2284 TREEVIEW_QueueRefresh (hwnd);
2292 TREEVIEW_GetIndent (HWND hwnd)
2294 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
2297 return infoPtr->uIndent;
2301 TREEVIEW_SetIndent (HWND hwnd, WPARAM wParam)
2303 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
2307 newIndent=(INT) wParam;
2308 if (newIndent < MINIMUM_INDENT) newIndent=MINIMUM_INDENT;
2309 infoPtr->uIndent=newIndent;
2315 TREEVIEW_GetToolTips (HWND hwnd)
2318 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
2321 return infoPtr->hwndToolTip;
2326 TREEVIEW_SetToolTips (HWND hwnd, WPARAM wParam)
2329 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
2333 prevToolTip=infoPtr->hwndToolTip;
2334 infoPtr->hwndToolTip= (HWND) wParam;
2340 static LRESULT CALLBACK
2341 TREEVIEW_GetEditControl (HWND hwnd)
2344 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
2346 return infoPtr->hwndEdit;
2350 TREEVIEW_Edit_SubclassProc (HWND hwnd, UINT uMsg, WPARAM wParam,
2358 HDC hdc = (HDC) wParam;
2359 GetClientRect (hwnd, &rc);
2360 Rectangle (hdc, rc.left, rc.top, rc.right, rc.bottom);
2366 return DLGC_WANTARROWS | DLGC_WANTALLKEYS;
2370 if (wParam == VK_ESCAPE)
2372 TREEVIEW_EndEditLabelNow(GetParent(hwnd), (WPARAM)TRUE, 0);
2375 else if (wParam == VK_RETURN)
2376 TREEVIEW_EndEditLabelNow(GetParent(hwnd), (WPARAM)FALSE, 0);
2383 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(GetParent(hwnd));
2385 return CallWindowProcA (infoPtr->wpEditOrig, hwnd, uMsg, wParam, lParam);
2396 /* should handle edit control messages here */
2399 TREEVIEW_Command (HWND hwnd, WPARAM wParam, LPARAM lParam)
2402 TRACE("%x %ld\n",wParam, lParam);
2404 switch (HIWORD(wParam))
2409 * Adjust the edit window size
2411 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
2412 TREEVIEW_ITEM *editItem = TREEVIEW_ValidItem(infoPtr, infoPtr->editItem);
2413 INT iLength = GetWindowTextLengthA(infoPtr->hwndEdit);
2414 HDC hdc = GetDC(infoPtr->hwndEdit);
2417 if ( GetTextMetricsA(hdc, &tm) )
2419 LONG newWidth = (iLength * tm.tmAveCharWidth) + 15;
2424 editItem->text.left - 2,
2425 editItem->text.top - 1,
2427 editItem->text.bottom - editItem->text.top + 3,
2430 ReleaseDC(hwnd, hdc);
2436 /* TREEVIEW_EndEditLabelNow(hwnd, (WPARAM)FALSE, 0);
2441 return SendMessageA (GetParent (hwnd), WM_COMMAND, wParam, lParam);
2448 TREEVIEW_Size (HWND hwnd, WPARAM wParam, LPARAM lParam)
2451 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
2453 if (infoPtr->bAutoSize)
2455 infoPtr->bAutoSize = FALSE;
2458 infoPtr->bAutoSize = TRUE;
2460 if (wParam == SIZE_RESTORED)
2462 infoPtr->uTotalWidth = LOWORD (lParam);
2463 infoPtr->uTotalHeight = HIWORD (lParam);
2465 FIXME("WM_SIZE flag %x %lx not handled\n", wParam, lParam);
2468 TREEVIEW_QueueRefresh (hwnd);
2475 TREEVIEW_StyleChanged (HWND hwnd, WPARAM wParam, LPARAM lParam)
2477 TRACE("(%x %lx)\n",wParam,lParam);
2479 InvalidateRect(hwnd, NULL, FALSE);
2485 TREEVIEW_Create (HWND hwnd, WPARAM wParam, LPARAM lParam)
2487 TREEVIEW_INFO *infoPtr;
2488 DWORD dwStyle = GetWindowLongA (hwnd, GWL_STYLE);
2493 TRACE("wnd %x, style %lx\n",hwnd,dwStyle);
2494 /* allocate memory for info structure */
2495 infoPtr = (TREEVIEW_INFO *) COMCTL32_Alloc (sizeof(TREEVIEW_INFO));
2497 SetWindowLongA( hwnd, 0, (DWORD)infoPtr);
2499 if (infoPtr == NULL) {
2500 ERR("could not allocate info memory!\n");
2504 if ((TREEVIEW_INFO*) GetWindowLongA( hwnd, 0) != infoPtr) {
2505 ERR("pointer assignment error!\n");
2511 /* set default settings */
2512 infoPtr->uInternalStatus=0;
2513 infoPtr->uNumItems=0;
2514 infoPtr->clrBk = GetSysColor (COLOR_WINDOW);
2515 infoPtr->clrText = GetSysColor (COLOR_WINDOWTEXT);
2516 infoPtr->clrLine = GetSysColor (COLOR_WINDOWTEXT);
2517 infoPtr->clrInsertMark = GetSysColor (COLOR_BTNTEXT);
2520 infoPtr->uIndent = 15;
2521 infoPtr->himlNormal = NULL;
2522 infoPtr->himlState = NULL;
2523 infoPtr->uItemHeight = -1;
2524 GetTextMetricsA (hdc, &tm);
2525 infoPtr->hFont = GetStockObject (DEFAULT_GUI_FONT);
2526 GetObjectA (infoPtr->hFont, sizeof (LOGFONTA), &logFont);
2527 logFont.lfWeight=FW_BOLD;
2528 infoPtr->hBoldFont = CreateFontIndirectA (&logFont);
2530 infoPtr->items = NULL;
2531 infoPtr->selectedItem=0;
2532 infoPtr->clrText=-1; /* use system color */
2533 infoPtr->dropItem=0;
2534 infoPtr->insertMarkItem=0;
2535 infoPtr->insertBeforeorAfter=0;
2536 infoPtr->pCallBackSort=NULL;
2537 infoPtr->uScrollTime = 300; /* milliseconds */
2538 infoPtr->wpEditOrig = NULL; /* we haven't subclassed anything yet */
2540 infoPtr->hwndToolTip=0;
2541 if (!(dwStyle & TVS_NOTOOLTIPS)) { /* Create tooltip control */
2544 infoPtr->hwndToolTip =
2545 CreateWindowExA (0, TOOLTIPS_CLASSA, NULL, 0,
2546 CW_USEDEFAULT, CW_USEDEFAULT,
2547 CW_USEDEFAULT, CW_USEDEFAULT,
2550 /* Send NM_TOOLTIPSCREATED notification */
2551 if (infoPtr->hwndToolTip) {
2552 NMTOOLTIPSCREATED nmttc;
2554 nmttc.hdr.hwndFrom = hwnd;
2555 nmttc.hdr.idFrom = GetWindowLongA( hwnd, GWL_ID);
2556 nmttc.hdr.code = NM_TOOLTIPSCREATED;
2557 nmttc.hwndToolTips = infoPtr->hwndToolTip;
2559 SendMessageA (GetParent (hwnd), WM_NOTIFY,
2560 (WPARAM) GetWindowLongA( hwnd, GWL_ID), (LPARAM)&nmttc);
2563 ZeroMemory (&ti, sizeof(TTTOOLINFOA));
2564 ti.cbSize = sizeof(TTTOOLINFOA);
2565 ti.uFlags = TTF_IDISHWND | TTF_TRACK | TTF_TRANSPARENT ;
2568 ti.lpszText = "Test"; /* LPSTR_TEXTCALLBACK; */
2569 SetRectEmpty (&ti.rect);
2571 SendMessageA (infoPtr->hwndToolTip, TTM_ADDTOOLA, 0, (LPARAM)&ti);
2574 infoPtr->hwndEdit = CreateWindowExA (
2578 WS_CHILD | WS_BORDER | ES_AUTOHSCROLL |
2579 ES_WANTRETURN | ES_LEFT,
2582 0,0,0); /* FIXME: (HMENU)IDTVEDIT,pcs->hInstance,0);*/
2584 SendMessageA ( infoPtr->hwndEdit, WM_SETFONT, infoPtr->hFont, FALSE);
2585 infoPtr->wpEditOrig = (WNDPROC)SetWindowLongA (
2588 (LONG) TREEVIEW_Edit_SubclassProc);
2590 if (dwStyle & TVS_CHECKBOXES) {
2594 infoPtr->himlState =
2595 ImageList_Create (16, 16,ILC_COLOR|ILC_MASK, 15, 1);
2597 hbmLoad = LoadBitmapA (COMCTL32_hModule, MAKEINTRESOURCEA(IDT_CHECK));
2598 TRACE ("%x\n",hbmLoad);
2599 nIndex = ImageList_AddMasked (infoPtr->himlState, hbmLoad, CLR_DEFAULT);
2600 TRACE ("%d\n",nIndex);
2601 DeleteObject (hbmLoad);
2603 ReleaseDC (hwnd, hdc);
2610 TREEVIEW_Destroy (HWND hwnd)
2612 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
2615 TREEVIEW_RemoveTree (hwnd);
2616 SetWindowLongA (hwnd, 0, (DWORD)NULL);
2618 if (infoPtr->Timer & TV_REFRESH_TIMER_SET)
2619 KillTimer (hwnd, TV_REFRESH_TIMER);
2620 if (infoPtr->hwndToolTip)
2621 DestroyWindow (infoPtr->hwndToolTip);
2623 COMCTL32_Free (infoPtr);
2629 TREEVIEW_Paint (HWND hwnd, WPARAM wParam, LPARAM lParam)
2635 hdc = wParam==0 ? BeginPaint (hwnd, &ps) : (HDC)wParam;
2636 TREEVIEW_Refresh (hwnd, hdc);
2637 if(!wParam) EndPaint (hwnd, &ps);
2644 TREEVIEW_SetFocus (HWND hwnd, WPARAM wParam, LPARAM lParam)
2646 TREEVIEW_SendSimpleNotify (hwnd, NM_SETFOCUS);
2647 InvalidateRect(hwnd, NULL, FALSE);
2652 TREEVIEW_KillFocus (HWND hwnd, WPARAM wParam, LPARAM lParam)
2654 TREEVIEW_SendSimpleNotify (hwnd, NM_KILLFOCUS);
2655 InvalidateRect(hwnd, NULL, FALSE);
2660 TREEVIEW_EraseBackground (HWND hwnd, WPARAM wParam, LPARAM lParam)
2662 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
2663 HBRUSH hBrush = CreateSolidBrush (infoPtr->clrBk);
2667 GetClientRect (hwnd, &rect);
2668 FillRect ((HDC)wParam, &rect, hBrush);
2669 DeleteObject (hBrush);
2685 TREEVIEW_SendSimpleNotify (HWND hwnd, UINT code)
2690 nmhdr.hwndFrom = hwnd;
2691 nmhdr.idFrom = GetWindowLongA( hwnd, GWL_ID);
2694 return (BOOL) SendMessageA (GetParent (hwnd), WM_NOTIFY,
2695 (WPARAM)nmhdr.idFrom, (LPARAM)&nmhdr);
2701 TREEVIEW_SendTreeviewNotify (HWND hwnd, UINT code, UINT action,
2702 HTREEITEM oldItem, HTREEITEM newItem)
2705 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
2707 TREEVIEW_ITEM *wineItem;
2709 TRACE("code:%x action:%x olditem:%x newitem:%x\n",
2710 code,action,(INT)oldItem,(INT)newItem);
2711 nmhdr.hdr.hwndFrom = hwnd;
2712 nmhdr.hdr.idFrom = GetWindowLongA( hwnd, GWL_ID);
2713 nmhdr.hdr.code = code;
2714 nmhdr.action = action;
2716 wineItem=& infoPtr->items[(INT)oldItem];
2717 nmhdr.itemOld.mask = wineItem->mask;
2718 nmhdr.itemOld.hItem = wineItem->hItem;
2719 nmhdr.itemOld.state = wineItem->state;
2720 nmhdr.itemOld.stateMask = wineItem->stateMask;
2721 nmhdr.itemOld.iImage = wineItem->iImage;
2722 nmhdr.itemOld.pszText = wineItem->pszText;
2723 nmhdr.itemOld.cchTextMax= wineItem->cchTextMax;
2724 nmhdr.itemOld.iImage = wineItem->iImage;
2725 nmhdr.itemOld.iSelectedImage = wineItem->iSelectedImage;
2726 nmhdr.itemOld.cChildren = wineItem->cChildren;
2727 nmhdr.itemOld.lParam = wineItem->lParam;
2731 wineItem=& infoPtr->items[(INT)newItem];
2732 nmhdr.itemNew.mask = wineItem->mask;
2733 nmhdr.itemNew.hItem = wineItem->hItem;
2734 nmhdr.itemNew.state = wineItem->state;
2735 nmhdr.itemNew.stateMask = wineItem->stateMask;
2736 nmhdr.itemNew.iImage = wineItem->iImage;
2737 nmhdr.itemNew.pszText = wineItem->pszText;
2738 nmhdr.itemNew.cchTextMax= wineItem->cchTextMax;
2739 nmhdr.itemNew.iImage = wineItem->iImage;
2740 nmhdr.itemNew.iSelectedImage = wineItem->iSelectedImage;
2741 nmhdr.itemNew.cChildren = wineItem->cChildren;
2742 nmhdr.itemNew.lParam = wineItem->lParam;
2748 return (BOOL)SendMessageA (GetParent (hwnd), WM_NOTIFY,
2749 (WPARAM) GetWindowLongA( hwnd, GWL_ID), (LPARAM)&nmhdr);
2754 TREEVIEW_SendTreeviewDnDNotify (HWND hwnd, UINT code, HTREEITEM dragItem,
2757 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
2759 TREEVIEW_ITEM *wineItem;
2761 TRACE("code:%x dragitem:%x\n", code,(INT)dragItem);
2763 nmhdr.hdr.hwndFrom = hwnd;
2764 nmhdr.hdr.idFrom = GetWindowLongA( hwnd, GWL_ID);
2765 nmhdr.hdr.code = code;
2767 wineItem=& infoPtr->items[(INT)dragItem];
2768 nmhdr.itemNew.mask = wineItem->mask;
2769 nmhdr.itemNew.hItem = wineItem->hItem;
2770 nmhdr.itemNew.state = wineItem->state;
2771 nmhdr.itemNew.lParam = wineItem->lParam;
2773 nmhdr.ptDrag.x = pt.x;
2774 nmhdr.ptDrag.y = pt.y;
2776 return (BOOL)SendMessageA (GetParent (hwnd), WM_NOTIFY,
2777 (WPARAM) GetWindowLongA( hwnd, GWL_ID), (LPARAM)&nmhdr);
2784 TREEVIEW_SendDispInfoNotify (HWND hwnd, TREEVIEW_ITEM *wineItem,
2785 UINT code, UINT what)
2791 TRACE("item %d, action %x, state %d\n",
2792 (INT)wineItem->hItem,
2794 (INT)wineItem->state);
2796 tvdi.hdr.hwndFrom = hwnd;
2797 tvdi.hdr.idFrom = GetWindowLongA( hwnd, GWL_ID);
2798 tvdi.hdr.code = code;
2799 tvdi.item.mask = what;
2800 tvdi.item.hItem = wineItem->hItem;
2801 tvdi.item.state = wineItem->state;
2802 tvdi.item.lParam = wineItem->lParam;
2803 tvdi.item.pszText = COMCTL32_Alloc (128*sizeof(char));
2804 tvdi.item.cchTextMax = 128;
2805 buf = tvdi.item.pszText;
2807 retval=(BOOL)SendMessageA (
2810 (WPARAM)tvdi.hdr.idFrom,
2813 if (what & TVIF_TEXT) {
2814 wineItem->pszText = tvdi.item.pszText;
2815 if (buf==tvdi.item.pszText) {
2816 wineItem->cchTextMax = 128;
2818 TRACE("user-supplied buffer\n");
2819 COMCTL32_Free (buf);
2820 wineItem->cchTextMax = 0;
2823 if (what & TVIF_SELECTEDIMAGE)
2824 wineItem->iSelectedImage = tvdi.item.iSelectedImage;
2825 if (what & TVIF_IMAGE)
2826 wineItem->iImage = tvdi.item.iImage;
2827 if (what & TVIF_CHILDREN)
2828 wineItem->cChildren = tvdi.item.cChildren;
2836 TREEVIEW_SendCustomDrawNotify (HWND hwnd, DWORD dwDrawStage, HDC hdc,
2839 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
2840 NMTVCUSTOMDRAW nmcdhdr;
2841 LPNMCUSTOMDRAW nmcd;
2843 TRACE("drawstage:%lx hdc:%x\n", dwDrawStage, hdc);
2845 nmcd= & nmcdhdr.nmcd;
2846 nmcd->hdr.hwndFrom = hwnd;
2847 nmcd->hdr.idFrom = GetWindowLongA( hwnd, GWL_ID);
2848 nmcd->hdr.code = NM_CUSTOMDRAW;
2849 nmcd->dwDrawStage= dwDrawStage;
2851 nmcd->rc.left = rc.left;
2852 nmcd->rc.right = rc.right;
2853 nmcd->rc.bottom = rc.bottom;
2854 nmcd->rc.top = rc.top;
2855 nmcd->dwItemSpec = 0;
2856 nmcd->uItemState = 0;
2857 nmcd->lItemlParam= 0;
2858 nmcdhdr.clrText = infoPtr->clrText;
2859 nmcdhdr.clrTextBk= infoPtr->clrBk;
2862 return (BOOL)SendMessageA (GetParent (hwnd), WM_NOTIFY,
2863 (WPARAM) GetWindowLongA( hwnd, GWL_ID), (LPARAM)&nmcdhdr);
2869 /* FIXME: need to find out when the flags in uItemState need to be set */
2872 TREEVIEW_SendCustomDrawItemNotify (HWND hwnd, HDC hdc,
2873 TREEVIEW_ITEM *wineItem, UINT uItemDrawState)
2875 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
2876 NMTVCUSTOMDRAW nmcdhdr;
2877 LPNMCUSTOMDRAW nmcd;
2878 DWORD dwDrawStage,dwItemSpec;
2882 dwDrawStage=CDDS_ITEM | uItemDrawState;
2883 dwItemSpec=(DWORD)wineItem->hItem;
2885 if (wineItem->hItem==infoPtr->selectedItem) uItemState|=CDIS_SELECTED;
2886 if (wineItem->hItem==infoPtr->focusItem) uItemState|=CDIS_FOCUS;
2887 if (wineItem->hItem==infoPtr->hotItem) uItemState|=CDIS_HOT;
2889 nmcd= & nmcdhdr.nmcd;
2890 nmcd->hdr.hwndFrom = hwnd;
2891 nmcd->hdr.idFrom = GetWindowLongA( hwnd, GWL_ID);
2892 nmcd->hdr.code = NM_CUSTOMDRAW;
2893 nmcd->dwDrawStage= dwDrawStage;
2895 nmcd->rc.left = wineItem->rect.left;
2896 nmcd->rc.right = wineItem->rect.right;
2897 nmcd->rc.bottom = wineItem->rect.bottom;
2898 nmcd->rc.top = wineItem->rect.top;
2899 nmcd->dwItemSpec = dwItemSpec;
2900 nmcd->uItemState = uItemState;
2901 nmcd->lItemlParam= wineItem->lParam;
2902 nmcdhdr.clrText = infoPtr->clrText;
2903 nmcdhdr.clrTextBk= infoPtr->clrBk;
2904 nmcdhdr.iLevel = wineItem->iLevel;
2906 TRACE("drawstage:%lx hdc:%x item:%lx, itemstate:%x, lItemlParam:%lx\n",
2907 nmcd->dwDrawStage, nmcd->hdc, nmcd->dwItemSpec,
2908 nmcd->uItemState, nmcd->lItemlParam);
2910 retval=SendMessageA (GetParent (hwnd), WM_NOTIFY,
2911 (WPARAM) GetWindowLongA( hwnd, GWL_ID), (LPARAM)&nmcdhdr);
2913 infoPtr->clrText=nmcdhdr.clrText;
2914 infoPtr->clrBk =nmcdhdr.clrTextBk;
2915 return (BOOL) retval;
2920 /* Note:If the specified item is the child of a collapsed parent item,
2921 the parent's list of child items is (recursively) expanded to reveal the
2922 specified item. This is mentioned for TREEVIEW_SelectItem; don't
2923 know if it also applies here.
2927 TREEVIEW_Expand (HWND hwnd, WPARAM wParam, LPARAM lParam)
2929 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
2930 TREEVIEW_ITEM *wineItem;
2934 flag = (UINT) wParam;
2935 expand = (INT) lParam;
2937 wineItem = TREEVIEW_ValidItem (infoPtr, (HTREEITEM)expand);
2941 if (!wineItem->cChildren)
2944 if (wineItem->pszText==LPSTR_TEXTCALLBACKA)
2945 TRACE ("For item %d, flags %d, state %d\n",
2946 expand, flag, wineItem->state);
2948 TRACE("For (%s) item:%d, flags %x, state:%d\n",
2949 wineItem->pszText, flag, expand, wineItem->state);
2951 if (wineItem->cChildren==I_CHILDRENCALLBACK) {
2952 FIXME("we don't handle I_CHILDRENCALLBACK yet\n");
2956 if (flag == TVE_TOGGLE) { /* FIXME: check exact behaviour here */
2957 flag &= ~TVE_TOGGLE; /* ie: bitwise ops or 'case' ops */
2958 if (wineItem->state & TVIS_EXPANDED)
2959 flag |= TVE_COLLAPSE;
2966 case TVE_COLLAPSERESET:
2967 TRACE(" case TVE_COLLAPSERESET\n");
2968 if (!wineItem->state & TVIS_EXPANDED)
2971 wineItem->state &= ~(TVIS_EXPANDEDONCE | TVIS_EXPANDED);
2972 TREEVIEW_RemoveAllChildren (hwnd, wineItem);
2976 TRACE(" case TVE_COLLAPSE\n");
2977 if (!wineItem->state & TVIS_EXPANDED)
2980 wineItem->state &= ~TVIS_EXPANDED;
2984 TRACE(" case TVE_EXPAND\n");
2985 if (wineItem->state & TVIS_EXPANDED)
2988 TRACE(" is not expanded...\n");
2990 if (!(wineItem->state & TVIS_EXPANDEDONCE))
2992 TRACE(" and has never been expanded...\n");
2993 wineItem->state |= TVIS_EXPANDED;
2995 /* this item has never been expanded */
2996 if (TREEVIEW_SendTreeviewNotify (
3003 TRACE(" TVN_ITEMEXPANDINGA returned TRUE, exiting...\n");
3008 * Since the TVN_ITEMEXPANDINGA message may has caused the parent to
3009 * insert new items which in turn may have cause items placeholder
3010 * reallocation, I reassign the current item pointer so we have
3011 * something valid to work with...
3012 * However, this should not be necessary,
3013 * investigation required in TREEVIEW_InsertItemA
3015 wineItem = TREEVIEW_ValidItem (infoPtr, (HTREEITEM)expand);
3019 "Catastrophic situation, cannot retrieve item #%d\n",
3024 wineItem->state |= TVIS_EXPANDEDONCE;
3025 TRACE(" TVN_ITEMEXPANDINGA sent...\n");
3027 TREEVIEW_SendTreeviewNotify (
3034 TRACE(" TVN_ITEMEXPANDEDA sent...\n");
3039 /* this item has already been expanded */
3040 wineItem->state |= TVIS_EXPANDED;
3044 case TVE_EXPANDPARTIAL:
3045 TRACE(" case TVE_EXPANDPARTIAL\n");
3046 FIXME("TVE_EXPANDPARTIAL not implemented\n");
3047 wineItem->state ^=TVIS_EXPANDED;
3048 wineItem->state |=TVIS_EXPANDEDONCE;
3052 TRACE("Exiting, Item %d state is now %d...\n",
3056 TREEVIEW_QueueRefresh (hwnd);
3062 static TREEVIEW_ITEM *
3063 TREEVIEW_HitTestPoint (HWND hwnd, POINT pt)
3065 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
3066 TREEVIEW_ITEM *wineItem;
3069 GetClientRect (hwnd, &rect);
3071 if (!infoPtr->firstVisible) return NULL;
3073 wineItem=&infoPtr->items [(INT)infoPtr->firstVisible];
3075 while ((wineItem!=NULL) && (pt.y > wineItem->rect.bottom))
3076 wineItem=TREEVIEW_GetNextListItem (infoPtr,wineItem);
3088 TREEVIEW_HitTest (HWND hwnd, LPARAM lParam)
3090 LPTVHITTESTINFO lpht=(LPTVHITTESTINFO) lParam;
3091 TREEVIEW_ITEM *wineItem;
3095 GetClientRect (hwnd, &rect);
3099 if (x < rect.left) status|=TVHT_TOLEFT;
3100 if (x > rect.right) status|=TVHT_TORIGHT;
3101 if (y < rect.top ) status|=TVHT_ABOVE;
3102 if (y > rect.bottom) status|=TVHT_BELOW;
3109 wineItem=TREEVIEW_HitTestPoint (hwnd, lpht->pt);
3111 lpht->flags=TVHT_NOWHERE;
3117 if (x < wineItem->expandBox.left) {
3118 lpht->flags |= TVHT_ONITEMINDENT;
3121 if ( PtInRect ( &wineItem->expandBox, lpht->pt)) {
3122 lpht->flags |= TVHT_ONITEMBUTTON;
3125 if ( PtInRect ( &wineItem->bitmap, lpht->pt)) {
3126 lpht->flags |= TVHT_ONITEMICON;
3129 if ( PtInRect ( &wineItem->statebitmap, lpht->pt)) {
3130 lpht->flags |= TVHT_ONITEMSTATEICON;
3133 if ( PtInRect ( &wineItem->text, lpht->pt)) {
3134 lpht->flags |= TVHT_ONITEMLABEL;
3138 lpht->flags|=TVHT_ONITEMRIGHT;
3142 lpht->hItem=wineItem->hItem;
3143 TRACE ("(%ld,%ld):result %x\n",lpht->pt.x,lpht->pt.y,lpht->flags);
3145 return (LRESULT) wineItem->hItem;
3149 TREEVIEW_EditLabelA (HWND hwnd, WPARAM wParam, LPARAM lParam)
3151 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
3152 TREEVIEW_ITEM *wineItem;
3155 * If the style allow editing...
3157 if ( GetWindowLongA( hwnd, GWL_STYLE) & TVS_EDITLABELS )
3160 if ( infoPtr->editItem == 0 ) /* If we are not curently editing */
3162 wineItem = TREEVIEW_ValidItem(infoPtr,(HTREEITEM) lParam);
3163 if ( wineItem == NULL )
3165 ERR("Cannot get valid TREEVIEW_ITEM for lParam\n");
3169 TRACE("Edit started for %s.\n", wineItem->pszText);
3170 infoPtr->editItem = wineItem->hItem;
3174 * It is common practice for a windows program to get this
3175 * edit control and then subclass it. It is assumed that a
3176 * new edit control is given every time.
3178 * As a result some programs really mess up the edit control
3179 * so we need to destory our old edit control and create a new
3180 * one. Recycling would be nice but we would need to reset
3181 * everything. So recreating may just be easyier
3184 DestroyWindow(infoPtr->hwndEdit);
3185 infoPtr->hwndEdit = CreateWindowExA (
3189 WS_CHILD | WS_BORDER | ES_AUTOHSCROLL |
3190 ES_WANTRETURN | ES_LEFT,
3193 0,0,0); /* FIXME: (HMENU)IDTVEDIT,pcs->hInstance,0);*/
3198 (LONG) TREEVIEW_Edit_SubclassProc);
3201 SendMessageA ( infoPtr->hwndEdit, WM_SETFONT, infoPtr->hFont, FALSE);
3203 SetWindowTextA( infoPtr->hwndEdit, wineItem->pszText );
3204 SendMessageA ( infoPtr->hwndEdit, EM_SETSEL, 0, -1 );
3207 ** NOTE: this must be after the edit control is created
3208 ** (according to TVN_BEGINLABELEDITA docs), before position is set.
3210 if ( TREEVIEW_SendDispInfoNotify( /* Return true to cancel edition */
3213 TVN_BEGINLABELEDITA,
3217 ** FIXME: Is this right, should we return a handle even though edit was cancelled?
3219 TRACE("Edit cancelled by TVN_BEGINLABELEDITA for %s.\n", wineItem->pszText);
3221 TREEVIEW_EndEditLabelNow(hwnd, (WPARAM)TRUE, 0);
3229 wineItem->text.left - 2,
3230 wineItem->text.top - 1,
3231 wineItem->text.right - wineItem->text.left + 20 ,
3232 wineItem->text.bottom - wineItem->text.top + 3,
3235 SetFocus ( infoPtr->hwndEdit);
3236 ShowWindow ( infoPtr->hwndEdit, SW_SHOW);
3242 ** return NULL since we cannot edit this.
3248 return infoPtr->hwndEdit;
3252 TREEVIEW_EndEditLabelNow (HWND hwnd, WPARAM wParam, LPARAM lParam)
3254 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
3255 TREEVIEW_ITEM *editedItem = TREEVIEW_ValidItem (infoPtr, infoPtr->editItem);
3256 BOOL bRevert = (BOOL)wParam;
3264 ** NOTE: we have to get the new text before calling TVN_ENDLABELEDITA
3265 ** since some apps(eg regedit, win98) validate the text in TVN_ENDLABELEDITA.
3268 oldText = editedItem->pszText;
3270 if ( !bRevert ) /* wParam is set to true to cancel the edition */
3273 iLength = GetWindowTextLengthA( infoPtr->hwndEdit );
3276 WARN("Zero length string for new label(not changing).");
3280 newText = COMCTL32_Alloc( iLength+1 );
3281 if( newText == NULL )
3283 ERR("OutOfMemory, cannot allocate space for label");
3286 GetWindowTextA( infoPtr->hwndEdit, newText, iLength+1);
3295 * notify our parent with the new string(or NULL if wParam==TRUE)
3297 tvdi.hdr.hwndFrom = hwnd;
3298 tvdi.hdr.idFrom = GetWindowLongA( hwnd, GWL_ID);
3299 tvdi.hdr.code = TVN_ENDLABELEDITA;
3300 tvdi.item.hItem = editedItem->hItem;
3301 tvdi.item.lParam = editedItem->lParam;
3302 tvdi.item.mask = TVIF_TEXT|TVIF_HANDLE|TVIF_PARAM;
3303 tvdi.item.pszText = newText;
3305 if(!SendMessageA ( /* return false to cancel edition */
3308 (WPARAM)tvdi.hdr.idFrom,
3311 if( newText == NULL ) /*we are supposed to ignore the return if (and pszText==NULL), MSDOCs */
3315 if (oldText != LPSTR_TEXTCALLBACKA)
3320 if( newText != NULL )
3321 COMCTL32_Free(newText);
3323 editedItem->pszText=oldText; /* revert back to the old label */
3327 COMCTL32_Free(oldText);
3329 editedItem->pszText=newText; /* use the new label */
3332 else if( newText!=NULL )
3335 ** Is really this necessary? shouldnt an app update its internal data in TVN_ENDLABELEDITA?
3340 * This is a callback string so we need
3341 * to inform the parent that the string
3345 tvdi.hdr.hwndFrom = hwnd;
3346 tvdi.hdr.idFrom = GetWindowLongA( hwnd, GWL_ID);
3347 tvdi.hdr.code = TVN_SETDISPINFOA;
3348 tvdi.item.mask = TVIF_TEXT;
3349 tvdi.item.pszText = newText;
3354 (WPARAM)tvdi.hdr.idFrom,
3359 COMCTL32_Free(newText);
3363 ShowWindow(infoPtr->hwndEdit, SW_HIDE);
3364 EnableWindow(infoPtr->hwndEdit, FALSE);
3366 /* update the window to eliminate fragments and the like */
3367 TreeView_GetItemRect(hwnd,infoPtr->editItem,&itemRect,FALSE);
3368 RedrawWindow(hwnd,&itemRect,0,RDW_ERASE|RDW_INVALIDATE|RDW_UPDATENOW);
3370 infoPtr->editItem = 0;
3372 return !bRevert; /* return true if label edit succesful, otherwise false */
3378 TREEVIEW_LButtonDoubleClick (HWND hwnd, WPARAM wParam, LPARAM lParam)
3380 TREEVIEW_ITEM *wineItem;
3384 pt.x = (INT)LOWORD(lParam);
3385 pt.y = (INT)HIWORD(lParam);
3388 wineItem=TREEVIEW_HitTestPoint (hwnd, pt);
3389 if (!wineItem) return 0;
3390 TRACE("item %d \n",(INT)wineItem->hItem);
3392 if (TREEVIEW_SendSimpleNotify (hwnd, NM_DBLCLK)!=TRUE) { /* FIXME!*/
3393 TREEVIEW_Expand (hwnd, (WPARAM) TVE_TOGGLE, (LPARAM) wineItem->hItem);
3400 TREEVIEW_LButtonDown (HWND hwnd, WPARAM wParam, LPARAM lParam)
3402 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
3406 ht.pt.x = (INT)LOWORD(lParam);
3407 ht.pt.y = (INT)HIWORD(lParam);
3410 iItem=TREEVIEW_HitTest (hwnd, (LPARAM) &ht);
3411 TRACE("item %d \n",iItem);
3413 if (ht.flags & TVHT_ONITEMBUTTON) {
3414 TREEVIEW_Expand (hwnd, (WPARAM) TVE_TOGGLE, (LPARAM) iItem);
3418 infoPtr->uInternalStatus|=TV_LDRAG;
3425 TREEVIEW_LButtonUp (HWND hwnd, WPARAM wParam, LPARAM lParam)
3427 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
3429 TREEVIEW_ITEM *wineItem;
3431 DWORD dwStyle = GetWindowLongA (hwnd, GWL_STYLE);
3433 ht.pt.x = (INT)LOWORD(lParam);
3434 ht.pt.y = (INT)HIWORD(lParam);
3438 /* Return true to cancel default behaviour */
3439 if ( TREEVIEW_SendSimpleNotify (hwnd, NM_CLICK) )
3443 iItem = TREEVIEW_HitTest (hwnd, (LPARAM) &ht);
3444 TRACE ("%d\n",iItem);
3448 wineItem = TREEVIEW_ValidItem(infoPtr, (HTREEITEM)iItem);
3451 * if we are TVS_SINGLEEXPAND then we want this single click to
3452 * do a bunch of things.
3454 if ((dwStyle & TVS_SINGLEEXPAND)&&
3455 ( ht.flags & (TVHT_ONITEMLABEL | TVHT_ONITEMICON))&&
3456 ( infoPtr->editItem == 0 ))
3458 TREEVIEW_ITEM *SelItem;
3460 * Send the notification
3462 TREEVIEW_SendTreeviewNotify (hwnd,TVN_SINGLEEXPAND,0,
3463 (HTREEITEM)iItem,0);
3465 * Close the previous selection all the way to the root
3466 * as long as the new selection is not a child
3469 if ((infoPtr->selectedItem)&&(infoPtr->selectedItem != (HTREEITEM)iItem))
3471 BOOL closeit = TRUE;
3474 while (closeit && SelItem)
3476 closeit = (SelItem->hItem != infoPtr->selectedItem);
3477 SelItem = TREEVIEW_ValidItem(infoPtr,SelItem->parent);
3482 SelItem = TREEVIEW_ValidItem(infoPtr,infoPtr->selectedItem);
3483 while ((SelItem)&&(SelItem->hItem != wineItem->hItem))
3485 TREEVIEW_Expand (hwnd,(WPARAM)TVE_COLLAPSE,(LPARAM)SelItem->hItem);
3486 SelItem = TREEVIEW_ValidItem(infoPtr,SelItem->parent);
3492 * Expand the current item
3494 TREEVIEW_Expand (hwnd, (WPARAM) TVE_TOGGLE, (LPARAM) wineItem->hItem);
3497 infoPtr->uInternalStatus &= ~(TV_LDRAG | TV_LDRAGGING);
3500 * If the style allow editing and the node is already selected
3501 * and the click occured on the item label...
3503 if ( ( GetWindowLongA( hwnd, GWL_STYLE) & TVS_EDITLABELS ) &&
3504 ( wineItem->state & TVIS_SELECTED ) &&
3505 ( ht.flags & TVHT_ONITEMLABEL ))
3507 if ( infoPtr->editItem == 0 ) /* If we are not curently editing */
3509 if( SendMessageA(hwnd, TVM_EDITLABELA, 0, (LPARAM)iItem) == 0)
3513 else if ( infoPtr->editItem != 0 ) /* If we are curently editing */
3515 TREEVIEW_EndEditLabelNow(hwnd, (WPARAM)FALSE, 0);
3517 else if ( ht.flags & (TVHT_ONITEMLABEL | TVHT_ONITEMICON))
3519 TREEVIEW_DoSelectItem ( hwnd, TVGN_CARET, (HTREEITEM)iItem, TVC_BYMOUSE);
3522 if (ht.flags & TVHT_ONITEMSTATEICON) {
3525 if (dwStyle & TVS_CHECKBOXES) { /* TVS_CHECKBOXES requires _us_ */
3526 int state; /* to toggle the current state */
3527 state=1-(wineItem->state>>12);
3528 TRACE ("state:%x\n", state);
3529 wineItem->state&= ~TVIS_STATEIMAGEMASK;
3530 wineItem->state|=state<<12;
3531 TRACE ("state:%x\n", wineItem->state);
3532 TREEVIEW_QueueRefresh (hwnd);
3540 TREEVIEW_RButtonDown (HWND hwnd, WPARAM wParam, LPARAM lParam)
3542 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
3545 infoPtr->uInternalStatus|=TV_RDRAG;
3550 TREEVIEW_RButtonUp (HWND hwnd, WPARAM wParam, LPARAM lParam)
3552 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
3555 if (TREEVIEW_SendSimpleNotify (hwnd, NM_RCLICK)) return 0;
3556 infoPtr->uInternalStatus&= ~(TV_RDRAG | TV_RDRAGGING);
3562 TREEVIEW_MouseMove (HWND hwnd, WPARAM wParam, LPARAM lParam)
3564 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
3565 TREEVIEW_ITEM *hotItem;
3568 pt.x=(INT) LOWORD (lParam);
3569 pt.y=(INT) HIWORD (lParam);
3570 hotItem=TREEVIEW_HitTestPoint (hwnd, pt);
3571 if (!hotItem) return 0;
3572 infoPtr->focusItem=hotItem->hItem;
3574 if ( GetWindowLongA( hwnd, GWL_STYLE) & TVS_DISABLEDRAGDROP) return 0;
3576 if (infoPtr->uInternalStatus & TV_LDRAG) {
3577 TREEVIEW_SendTreeviewDnDNotify (hwnd, TVN_BEGINDRAGA, hotItem->hItem, pt);
3578 infoPtr->uInternalStatus &= ~TV_LDRAG;
3579 infoPtr->uInternalStatus |= TV_LDRAGGING;
3580 infoPtr->dropItem=hotItem->hItem;
3584 if (infoPtr->uInternalStatus & TV_RDRAG) {
3585 TREEVIEW_SendTreeviewDnDNotify (hwnd, TVN_BEGINRDRAGA, hotItem->hItem, pt);
3586 infoPtr->uInternalStatus &= ~TV_RDRAG;
3587 infoPtr->uInternalStatus |= TV_RDRAGGING;
3588 infoPtr->dropItem=hotItem->hItem;
3597 TREEVIEW_CreateDragImage (HWND hwnd, WPARAM wParam, LPARAM lParam)
3599 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
3600 TREEVIEW_ITEM *dragItem;
3604 HBITMAP hbmp,hOldbmp;
3611 if (!(infoPtr->himlNormal)) return 0;
3612 dragItem=TREEVIEW_ValidItem (infoPtr, (HTREEITEM) lParam);
3614 if (!dragItem) return 0;
3616 if (dragItem->pszText==LPSTR_TEXTCALLBACKA) {
3617 TREEVIEW_SendDispInfoNotify (hwnd, dragItem, TVN_GETDISPINFOA, TVIF_TEXT);
3619 itemtxt=dragItem->pszText;
3621 hwtop=GetDesktopWindow ();
3622 htopdc= GetDC (hwtop);
3623 hdc=CreateCompatibleDC (htopdc);
3625 hOldFont=SelectObject (hdc, infoPtr->hFont);
3626 GetTextExtentPoint32A (hdc, itemtxt, lstrlenA (itemtxt), &size);
3627 TRACE("%d %d %s %d\n",size.cx,size.cy,itemtxt,lstrlenA(itemtxt));
3628 hbmp=CreateCompatibleBitmap (htopdc, size.cx, size.cy);
3629 hOldbmp=SelectObject (hdc, hbmp);
3631 ImageList_GetIconSize (infoPtr->himlNormal, &cx, &cy);
3633 if (cy>size.cy) size.cy=cy;
3635 infoPtr->dragList=ImageList_Create (size.cx, size.cy, ILC_COLOR, 10, 10);
3636 ImageList_Draw (infoPtr->himlNormal, dragItem->iImage, hdc, 0, 0, ILD_NORMAL);
3639 ImageList_GetImageInfo (infoPtr->himlNormal, dragItem->hItem, &iminfo);
3640 ImageList_AddMasked (infoPtr->dragList, iminfo.hbmImage, CLR_DEFAULT);
3643 /* draw item text */
3645 SetRect (&rc, cx, 0, size.cx,size.cy);
3646 DrawTextA (hdc, itemtxt, lstrlenA (itemtxt), &rc, DT_LEFT);
3647 SelectObject (hdc, hOldFont);
3648 SelectObject (hdc, hOldbmp);
3650 ImageList_Add (infoPtr->dragList, hbmp, 0);
3653 DeleteObject (hbmp);
3654 ReleaseDC (hwtop, htopdc);
3656 return (LRESULT)infoPtr->dragList;
3661 TREEVIEW_DoSelectItem (HWND hwnd, INT action, HTREEITEM newSelect, INT cause)
3664 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
3665 TREEVIEW_ITEM *prevItem,*wineItem;
3668 wineItem = TREEVIEW_ValidItem (infoPtr, (HTREEITEM)newSelect);
3670 TRACE("Entering item %d, flag %x, cause %x, state %d\n",
3676 if ( (wineItem) && (wineItem->parent))
3679 * If the item has a collapse parent expand the parent so he
3680 * can expose the item
3682 TREEVIEW_ITEM *parentItem = TREEVIEW_ValidItem (infoPtr, wineItem->parent);
3683 if ( !(parentItem->state & TVIS_EXPANDED))
3684 TREEVIEW_Expand (hwnd, TVE_EXPAND, (LPARAM) wineItem->parent);
3690 prevSelect=(INT)infoPtr->selectedItem;
3692 if ((HTREEITEM)prevSelect==newSelect)
3695 prevItem= TREEVIEW_ValidItem (infoPtr, (HTREEITEM)prevSelect);
3698 if (TREEVIEW_SendTreeviewNotify(
3702 (HTREEITEM)prevSelect,
3703 (HTREEITEM)newSelect))
3704 return FALSE; /* FIXME: OK? */
3707 prevItem->state &= ~TVIS_SELECTED;
3709 wineItem->state |= TVIS_SELECTED;
3711 infoPtr->selectedItem=(HTREEITEM)newSelect;
3713 TREEVIEW_SendTreeviewNotify(
3717 (HTREEITEM)prevSelect,
3718 (HTREEITEM)newSelect);
3722 case TVGN_DROPHILITE:
3723 prevItem= TREEVIEW_ValidItem (infoPtr, infoPtr->dropItem);
3726 prevItem->state &= ~TVIS_DROPHILITED;
3728 infoPtr->dropItem=(HTREEITEM)newSelect;
3731 wineItem->state |=TVIS_DROPHILITED;
3735 case TVGN_FIRSTVISIBLE:
3736 FIXME("FIRSTVISIBLE not implemented\n");
3740 TREEVIEW_QueueRefresh (hwnd);
3742 TRACE("Leaving state %d\n", wineItem->state);
3746 /* FIXME: handle NM_KILLFocus etc */
3748 TREEVIEW_SelectItem (HWND hwnd, WPARAM wParam, LPARAM lParam)
3751 return TREEVIEW_DoSelectItem (hwnd, wParam, (HTREEITEM) lParam, TVC_UNKNOWN);
3758 TREEVIEW_GetFont (HWND hwnd, WPARAM wParam, LPARAM lParam)
3761 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
3763 TRACE("%x\n",infoPtr->hFont);
3764 return infoPtr->hFont;
3768 TREEVIEW_SetFont (HWND hwnd, WPARAM wParam, LPARAM lParam)
3771 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
3774 HFONT hFont, hOldFont;
3778 TRACE("%x %lx\n",wParam, lParam);
3780 infoPtr->hFont = (HFONT)wParam;
3782 hFont = infoPtr->hFont ? infoPtr->hFont : GetStockObject (SYSTEM_FONT);
3784 GetObjectA (infoPtr->hFont, sizeof (LOGFONTA), &logFont);
3785 logFont.lfWeight=FW_BOLD;
3786 infoPtr->hBoldFont = CreateFontIndirectA (&logFont);
3789 hOldFont = SelectObject (hdc, hFont);
3790 GetTextMetricsA (hdc, &tm);
3791 height= tm.tmHeight + tm.tmExternalLeading + FOCUS_BORDER;
3792 if (height>infoPtr->uRealItemHeight)
3793 infoPtr->uRealItemHeight=height;
3794 SelectObject (hdc, hOldFont);
3798 TREEVIEW_QueueRefresh (hwnd);
3806 TREEVIEW_VScroll (HWND hwnd, WPARAM wParam, LPARAM lParam)
3809 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
3813 TRACE("wp %x, lp %lx\n", wParam, lParam);
3814 if (!infoPtr->uInternalStatus & TV_VSCROLL) return FALSE;
3816 switch (LOWORD (wParam)) {
3818 if (!infoPtr->cy) return FALSE;
3819 infoPtr->cy -= infoPtr->uRealItemHeight;
3820 if (infoPtr->cy < 0) infoPtr->cy=0;
3823 nVisibleItems = infoPtr->uVisibleHeight / infoPtr->uRealItemHeight;
3824 maxHeight=infoPtr->uTotalHeight - nVisibleItems * infoPtr->uRealItemHeight;
3825 if (infoPtr->cy >= maxHeight) return FALSE;
3826 infoPtr->cy += infoPtr->uRealItemHeight;
3827 if (infoPtr->cy >= maxHeight)
3828 infoPtr->cy = maxHeight;
3831 if (!infoPtr->cy) return FALSE;
3832 infoPtr->cy -= infoPtr->uVisibleHeight;
3833 if (infoPtr->cy < 0) infoPtr->cy=0;
3836 nVisibleItems = infoPtr->uVisibleHeight / infoPtr->uRealItemHeight;
3837 maxHeight=infoPtr->uTotalHeight - nVisibleItems * infoPtr->uRealItemHeight;
3838 if (infoPtr->cy == maxHeight) return FALSE;
3839 infoPtr->cy += infoPtr->uVisibleHeight;
3840 if (infoPtr->cy >= maxHeight)
3841 infoPtr->cy = maxHeight;
3844 infoPtr->cy = HIWORD (wParam);
3849 TREEVIEW_QueueRefresh (hwnd);
3854 TREEVIEW_HScroll (HWND hwnd, WPARAM wParam, LPARAM lParam)
3856 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
3859 TRACE("wp %lx, lp %x\n", lParam, wParam);
3861 if (!infoPtr->uInternalStatus & TV_HSCROLL) return FALSE;
3863 switch (LOWORD (wParam)) {
3865 if (!infoPtr->cx) return FALSE;
3866 infoPtr->cx -= infoPtr->uRealItemHeight;
3867 if (infoPtr->cx < 0) infoPtr->cx=0;
3870 maxWidth=infoPtr->uTotalWidth-infoPtr->uVisibleWidth;
3871 if (infoPtr->cx == maxWidth) return FALSE;
3872 infoPtr->cx += infoPtr->uRealItemHeight; /*FIXME */
3873 if (infoPtr->cx > maxWidth)
3874 infoPtr->cx = maxWidth;
3877 if (!infoPtr->cx) return FALSE;
3878 infoPtr->cx -= infoPtr->uVisibleWidth;
3879 if (infoPtr->cx < 0) infoPtr->cx=0;
3882 maxWidth=infoPtr->uTotalWidth-infoPtr->uVisibleWidth;
3883 if (infoPtr->cx == maxWidth) return FALSE;
3884 infoPtr->cx += infoPtr->uVisibleWidth;
3885 if (infoPtr->cx > maxWidth)
3886 infoPtr->cx = maxWidth;
3889 infoPtr->cx = HIWORD (wParam);
3894 TREEVIEW_QueueRefresh (hwnd);
3898 static LRESULT TREEVIEW_MouseWheel (HWND hwnd, WPARAM wParam, LPARAM lParam)
3901 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
3902 short gcWheelDelta = 0;
3903 UINT pulScrollLines = 3;
3905 SystemParametersInfoW(SPI_GETWHEELSCROLLLINES,0, &pulScrollLines, 0);
3907 gcWheelDelta -= (short) HIWORD(wParam);
3908 pulScrollLines *= (gcWheelDelta / WHEEL_DELTA);
3910 if (abs(gcWheelDelta) >= WHEEL_DELTA && pulScrollLines)
3912 int wheelDy = pulScrollLines * infoPtr->uRealItemHeight;
3913 int newDy = infoPtr->cy + wheelDy;
3914 int maxDy = infoPtr->uTotalHeight - infoPtr->uVisibleHeight;
3916 if (newDy > maxDy) newDy = maxDy;
3917 if (newDy < 0) newDy = 0;
3919 if (newDy == infoPtr->cy) return TRUE;
3921 TREEVIEW_VScroll(hwnd, MAKEWPARAM(SB_THUMBTRACK,newDy),0);
3927 TREEVIEW_KeyDown (HWND hwnd, WPARAM wParam, LPARAM lParam)
3929 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
3930 HTREEITEM hNewSelection = 0;
3931 INT scrollNeeds = -1;
3932 INT cyChangeNeeds = -1;
3933 INT prevSelect = (INT)infoPtr->selectedItem;
3935 TREEVIEW_ITEM *prevItem =
3936 (prevSelect != 0 ) ?
3937 TREEVIEW_ValidItem (infoPtr, (HTREEITEM)prevSelect) :
3940 TREEVIEW_ITEM *newItem = NULL;
3942 TRACE("%x %lx\n",wParam, lParam);
3944 if (prevSelect == 0)
3949 newItem=TREEVIEW_GetPrevListItem (infoPtr, prevItem);
3952 newItem=& infoPtr->items[(INT)infoPtr->TopRootItem];
3954 hNewSelection = newItem->hItem;
3956 if (! newItem->visible)
3957 scrollNeeds = SB_LINEUP;
3961 newItem=TREEVIEW_GetNextListItem (infoPtr, prevItem);
3966 hNewSelection = newItem->hItem;
3968 if (! newItem->visible)
3969 scrollNeeds = SB_LINEDOWN;
3974 newItem = &infoPtr->items[(INT)infoPtr->TopRootItem];
3975 hNewSelection = newItem->hItem;
3980 newItem = &infoPtr->items[(INT)infoPtr->TopRootItem];
3981 newItem = TREEVIEW_GetLastListItem (infoPtr, newItem);
3982 hNewSelection = newItem->hItem;
3984 if (! newItem->visible)
3985 cyChangeNeeds = infoPtr->uTotalHeight-infoPtr->uVisibleHeight;
3990 if ( (prevItem->cChildren > 0) && (prevItem->state & TVIS_EXPANDED) )
3992 TREEVIEW_Expand(hwnd, TVE_COLLAPSE, prevSelect );
3994 else if ((INT)prevItem->parent)
3996 newItem = (& infoPtr->items[(INT)prevItem->parent]);
3997 if (! newItem->visible)
3998 /* FIXME find a way to make this item the first visible... */
4001 hNewSelection = newItem->hItem;
4007 if ( ( prevItem->cChildren > 0) ||
4008 ( prevItem->cChildren == I_CHILDRENCALLBACK))
4010 if (! (prevItem->state & TVIS_EXPANDED))
4011 TREEVIEW_Expand(hwnd, TVE_EXPAND, prevSelect );
4014 newItem = (& infoPtr->items[(INT)prevItem->firstChild]);
4015 hNewSelection = newItem->hItem;
4022 if (! (prevItem->state & TVIS_EXPANDED))
4023 TREEVIEW_Expand(hwnd, TVE_EXPAND, prevSelect );
4027 if (prevItem->state & TVIS_EXPANDED)
4028 TREEVIEW_Expand(hwnd, TVE_COLLAPSE, prevSelect );
4033 newItem=TREEVIEW_GetListItem(
4036 -1*(TREEVIEW_GetVisibleCount(hwnd,0,0)-3));
4040 hNewSelection = newItem->hItem;
4042 if (! newItem->visible)
4043 scrollNeeds = SB_PAGEUP;
4048 newItem=TREEVIEW_GetListItem(
4051 TREEVIEW_GetVisibleCount(hwnd,0,0)-3);
4056 hNewSelection = newItem->hItem;
4058 if (! newItem->visible)
4059 scrollNeeds = SB_PAGEDOWN;
4068 FIXME("%x not implemented\n", wParam);
4075 This works but does not send notification...
4077 prevItem->state &= ~TVIS_SELECTED;
4078 newItem->state |= TVIS_SELECTED;
4079 infoPtr->selectedItem = hNewSelection;
4080 TREEVIEW_QueueRefresh (hwnd);
4083 if ( TREEVIEW_DoSelectItem(
4086 (HTREEITEM)hNewSelection,
4089 /* If selection change is allowed for the new item, perform scrolling */
4090 if (scrollNeeds != -1)
4091 TREEVIEW_VScroll(hwnd, scrollNeeds, 0);
4093 if (cyChangeNeeds != -1)
4094 infoPtr->cy = cyChangeNeeds;
4096 /* FIXME: Something happen in the load the in the two weeks before
4097 april 1st 1999 which makes this SetFocus mandatory otherwise, the focus
4098 is lost... However the SetFocus should not be required...*/
4109 TREEVIEW_GetScrollTime (HWND hwnd)
4111 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
4113 return infoPtr->uScrollTime;
4118 TREEVIEW_SetScrollTime (HWND hwnd, UINT uScrollTime)
4120 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
4121 UINT uOldScrollTime = infoPtr->uScrollTime;
4123 infoPtr->uScrollTime = min (uScrollTime, 100);
4125 return uOldScrollTime;
4129 static LRESULT WINAPI
4130 TREEVIEW_WindowProc (HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
4132 TREEVIEW_INFO *infoPtr;
4133 if (uMsg==WM_CREATE)
4134 return TREEVIEW_Create (hwnd, wParam, lParam);
4136 if (!(infoPtr = TREEVIEW_GetInfoPtr(hwnd)))
4137 return DefWindowProcA (hwnd, uMsg, wParam, lParam);
4141 case TVM_INSERTITEMA:
4142 return TREEVIEW_InsertItemA (hwnd, wParam, lParam);
4144 case TVM_INSERTITEMW:
4145 return TREEVIEW_InsertItemW(hwnd,wParam,lParam);;
4147 case TVM_DELETEITEM:
4148 return TREEVIEW_DeleteItem (hwnd, wParam, lParam);
4151 return TREEVIEW_Expand (hwnd, wParam, lParam);
4153 case TVM_GETITEMRECT:
4154 return TREEVIEW_GetItemRect (hwnd, wParam, lParam);
4157 return TREEVIEW_GetCount (hwnd, wParam, lParam);
4160 return TREEVIEW_GetIndent (hwnd);
4163 return TREEVIEW_SetIndent (hwnd, wParam);
4165 case TVM_GETIMAGELIST:
4166 return TREEVIEW_GetImageList (hwnd, wParam, lParam);
4168 case TVM_SETIMAGELIST:
4169 return TREEVIEW_SetImageList (hwnd, wParam, lParam);
4171 case TVM_GETNEXTITEM:
4172 return TREEVIEW_GetNextItem (hwnd, wParam, lParam);
4174 case TVM_SELECTITEM:
4175 return TREEVIEW_SelectItem (hwnd, wParam, lParam);
4178 return TREEVIEW_GetItemA (hwnd, wParam, lParam);
4181 return TREEVIEW_GetItemW (hwnd, wParam, lParam);
4184 return TREEVIEW_SetItemA (hwnd, wParam, lParam);
4187 FIXME("Unimplemented msg TVM_SETITEMW\n");
4190 case TVM_EDITLABELA:
4191 return TREEVIEW_EditLabelA(hwnd, wParam, lParam);
4193 case TVM_EDITLABELW:
4194 FIXME("Unimplemented msg TVM_EDITLABELW \n");
4197 case TVM_GETEDITCONTROL:
4198 return TREEVIEW_GetEditControl (hwnd);
4200 case TVM_GETVISIBLECOUNT:
4201 return TREEVIEW_GetVisibleCount (hwnd, wParam, lParam);
4204 return TREEVIEW_HitTest (hwnd, lParam);
4206 case TVM_CREATEDRAGIMAGE:
4207 return TREEVIEW_CreateDragImage (hwnd, wParam, lParam);
4209 case TVM_SORTCHILDREN:
4210 return TREEVIEW_SortChildren (hwnd, wParam, lParam);
4212 case TVM_ENSUREVISIBLE:
4213 FIXME("Unimplemented msg TVM_ENSUREVISIBLE\n");
4216 case TVM_SORTCHILDRENCB:
4217 return TREEVIEW_SortChildrenCB(hwnd, wParam, lParam);
4219 case TVM_ENDEDITLABELNOW:
4220 if (infoPtr->editItem)
4221 return TREEVIEW_EndEditLabelNow (hwnd, wParam, lParam);
4223 case TVM_GETISEARCHSTRINGA:
4224 FIXME("Unimplemented msg TVM_GETISEARCHSTRINGA\n");
4227 case TVM_GETISEARCHSTRINGW:
4228 FIXME("Unimplemented msg TVM_GETISEARCHSTRINGW\n");
4231 case TVM_GETTOOLTIPS:
4232 return TREEVIEW_GetToolTips (hwnd);
4234 case TVM_SETTOOLTIPS:
4235 return TREEVIEW_SetToolTips (hwnd, wParam);
4237 case TVM_SETINSERTMARK:
4238 return TREEVIEW_SetInsertMark (hwnd,wParam, lParam);
4240 case TVM_SETITEMHEIGHT:
4241 return TREEVIEW_SetItemHeight (hwnd, wParam);
4243 case TVM_GETITEMHEIGHT:
4244 return TREEVIEW_GetItemHeight (hwnd);
4246 case TVM_SETBKCOLOR:
4247 return TREEVIEW_SetBkColor (hwnd, wParam, lParam);
4249 case TVM_SETTEXTCOLOR:
4250 return TREEVIEW_SetTextColor (hwnd, wParam, lParam);
4252 case TVM_GETBKCOLOR:
4253 return TREEVIEW_GetBkColor (hwnd);
4255 case TVM_GETTEXTCOLOR:
4256 return TREEVIEW_GetTextColor (hwnd);
4258 case TVM_SETSCROLLTIME:
4259 return TREEVIEW_SetScrollTime (hwnd, (UINT)wParam);
4261 case TVM_GETSCROLLTIME:
4262 return TREEVIEW_GetScrollTime (hwnd);
4264 case TVM_GETITEMSTATE:
4265 return TREEVIEW_GetItemState (hwnd,wParam, lParam);
4267 case TVM_GETLINECOLOR:
4268 return TREEVIEW_GetLineColor (hwnd,wParam, lParam);
4270 case TVM_SETLINECOLOR:
4271 return TREEVIEW_SetLineColor (hwnd,wParam, lParam);
4273 case TVM_SETINSERTMARKCOLOR:
4274 return TREEVIEW_SetInsertMarkColor (hwnd,wParam, lParam);
4276 case TVM_GETINSERTMARKCOLOR:
4277 return TREEVIEW_GetInsertMarkColor (hwnd,wParam, lParam);
4279 case TVM_SETUNICODEFORMAT:
4280 FIXME("Unimplemented msg TVM_SETUNICODEFORMAT\n");
4283 case TVM_GETUNICODEFORMAT:
4284 FIXME("Unimplemented msg TVM_GETUNICODEFORMAT\n");
4288 return TREEVIEW_Command (hwnd, wParam, lParam);
4291 return TREEVIEW_Destroy (hwnd);
4293 /* case WM_ENABLE: */
4296 return TREEVIEW_EraseBackground (hwnd, wParam, lParam);
4299 return DLGC_WANTARROWS | DLGC_WANTCHARS;
4302 return TREEVIEW_Paint (hwnd, wParam, lParam);
4305 return TREEVIEW_GetFont (hwnd, wParam, lParam);
4308 return TREEVIEW_SetFont (hwnd, wParam, lParam);
4311 return TREEVIEW_KeyDown (hwnd, wParam, lParam);
4314 return TREEVIEW_SetFocus (hwnd, wParam, lParam);
4317 return TREEVIEW_KillFocus (hwnd, wParam, lParam);
4319 case WM_LBUTTONDOWN:
4320 return TREEVIEW_LButtonDown (hwnd, wParam, lParam);
4323 return TREEVIEW_LButtonUp (hwnd, wParam, lParam);
4325 case WM_LBUTTONDBLCLK:
4326 return TREEVIEW_LButtonDoubleClick (hwnd, wParam, lParam);
4328 case WM_RBUTTONDOWN:
4329 return TREEVIEW_RButtonDown (hwnd, wParam, lParam);
4332 return TREEVIEW_RButtonUp (hwnd, wParam, lParam);
4335 return TREEVIEW_MouseMove (hwnd, wParam, lParam);
4337 case WM_STYLECHANGED:
4338 return TREEVIEW_StyleChanged (hwnd, wParam, lParam);
4340 /* case WM_SYSCOLORCHANGE: */
4341 /* case WM_SETREDRAW: */
4344 return TREEVIEW_HandleTimer (hwnd, wParam, lParam);
4347 return TREEVIEW_Size (hwnd, wParam,lParam);
4350 return TREEVIEW_HScroll (hwnd, wParam, lParam);
4352 return TREEVIEW_VScroll (hwnd, wParam, lParam);
4355 if (wParam & (MK_SHIFT | MK_CONTROL))
4356 return DefWindowProcA( hwnd, uMsg, wParam, lParam );
4357 return TREEVIEW_MouseWheel (hwnd, wParam, lParam);
4360 TRACE ("drawItem\n");
4361 return DefWindowProcA (hwnd, uMsg, wParam, lParam);
4364 if (uMsg >= WM_USER)
4365 FIXME("Unknown msg %04x wp=%08x lp=%08lx\n",
4366 uMsg, wParam, lParam);
4367 return DefWindowProcA (hwnd, uMsg, wParam, lParam);
4374 TREEVIEW_Register (void)
4380 ZeroMemory (&wndClass, sizeof(WNDCLASSA));
4381 wndClass.style = CS_GLOBALCLASS | CS_DBLCLKS;
4382 wndClass.lpfnWndProc = (WNDPROC)TREEVIEW_WindowProc;
4383 wndClass.cbClsExtra = 0;
4384 wndClass.cbWndExtra = sizeof(TREEVIEW_INFO *);
4385 wndClass.hCursor = LoadCursorA (0, IDC_ARROWA);
4386 wndClass.hbrBackground = 0;
4387 wndClass.lpszClassName = WC_TREEVIEWA;
4389 RegisterClassA (&wndClass);
4394 TREEVIEW_Unregister (void)
4396 UnregisterClassA (WC_TREEVIEWA, (HINSTANCE)NULL);