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);
605 FIXME("%d %ld\n",wParam,lParam);
606 if (!TREEVIEW_ValidItem (infoPtr, (HTREEITEM)lParam)) return 0;
607 FIXME("%d %ld\n",wParam,lParam);
609 infoPtr->insertBeforeorAfter=(BOOL) wParam;
610 infoPtr->insertMarkItem=(HTREEITEM) lParam;
613 TREEVIEW_Refresh (hwnd, hdc);
620 TREEVIEW_SetTextColor (HWND hwnd, WPARAM wParam, LPARAM lParam)
622 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
623 COLORREF prevColor=infoPtr->clrText;
626 infoPtr->clrText=(COLORREF) lParam;
627 return (LRESULT) prevColor;
631 TREEVIEW_GetBkColor (HWND hwnd)
633 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
636 return (LRESULT) infoPtr->clrBk;
640 TREEVIEW_SetBkColor (HWND hwnd, WPARAM wParam, LPARAM lParam)
642 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
643 COLORREF prevColor=infoPtr->clrBk;
646 infoPtr->clrBk=(COLORREF) lParam;
647 return (LRESULT) prevColor;
651 TREEVIEW_GetTextColor (HWND hwnd)
653 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
656 return (LRESULT) infoPtr->clrText;
660 /* cdmode: custom draw mode as received from app. in first NMCUSTOMDRAW
663 #define TREEVIEW_LEFT_MARGIN 8
667 TREEVIEW_DrawItem (HWND hwnd, HDC hdc, TREEVIEW_ITEM *wineItem)
669 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
670 DWORD dwStyle = GetWindowLongA (hwnd, GWL_STYLE);
671 INT center,xpos,cx,cy, cditem;
673 UINT uTextJustify = DT_LEFT;
677 if (wineItem->state & TVIS_BOLD)
678 hOldFont = SelectObject (hdc, infoPtr->hBoldFont);
680 hOldFont = SelectObject (hdc, infoPtr->hFont);
683 TRACE ("cdmode:%x\n",infoPtr->cdmode);
684 if (infoPtr->cdmode & CDRF_NOTIFYITEMDRAW) {
685 cditem=TREEVIEW_SendCustomDrawItemNotify
686 (hwnd, hdc, wineItem, CDDS_ITEMPREPAINT);
687 TRACE("prepaint:cditem-app returns 0x%x\n",cditem);
689 if (cditem & CDRF_SKIPDEFAULT)
694 * Set drawing starting points
696 r = wineItem->rect; /* this item rectangle */
697 center = (r.top+r.bottom)/2; /* this item vertical center */
698 xpos = r.left + TREEVIEW_LEFT_MARGIN;/* horizontal starting point */
701 * Display the tree hierarchy
703 if ( dwStyle & TVS_HASLINES)
706 * Write links to parent node
707 * we draw the L starting from the child to the parent
709 * points[0] is attached to the current item
710 * points[1] is the L corner
711 * points[2] is attached to the parent or the up sibling
713 if ( dwStyle & TVS_LINESATROOT)
715 TREEVIEW_ITEM *upNode = NULL;
716 BOOL hasParentOrSibling = TRUE;
717 RECT upRect = {0,0,0,0};
718 HPEN hOldPen, hNewPen;
721 * determine the target location of the line at root, either be linked
722 * to the up sibling or to the parent node.
724 if (wineItem->upsibling)
725 upNode = TREEVIEW_ValidItem (infoPtr, wineItem->upsibling);
726 else if (wineItem->parent)
727 upNode = TREEVIEW_ValidItem (infoPtr, wineItem->parent);
729 hasParentOrSibling = FALSE;
732 upRect = upNode->rect;
734 if ( wineItem->iLevel == 0 )
736 points[2].x = points[1].x = upRect.left+8;
737 points[0].x = points[2].x + 10;
738 points[2].y = upRect.bottom-3;
739 points[1].y = points[0].y = center;
743 points[2].x = points[1].x = 8 + (20*wineItem->iLevel);
744 points[2].y = ( upNode->cChildren == 0) ?
745 upRect.top : /* is linked to the "L" above */
746 ( wineItem->upsibling != NULL) ?
747 upRect.bottom-3: /* is linked to an icon */
748 upRect.bottom+1; /* is linked to a +/- box */
749 points[1].y = points[0].y = center;
750 points[0].x = points[1].x + 10;
756 hNewPen = CreatePen(PS_DOT, 0, infoPtr->clrLine);
757 hOldPen = SelectObject( hdc, hNewPen );
759 if (hasParentOrSibling)
760 Polyline (hdc,points,3);
762 Polyline (hdc,points,2);
764 DeleteObject(hNewPen);
765 SelectObject(hdc, hOldPen);
770 * Display the (+/-) signs
772 if (wineItem->iLevel != 0)/* update position only for non root node */
773 xpos+=(5*wineItem->iLevel);
775 if (( dwStyle & TVS_HASBUTTONS) && ( dwStyle & TVS_HASLINES))
777 if ( (wineItem->cChildren) ||
778 (wineItem->cChildren == I_CHILDRENCALLBACK))
780 /* Setup expand box coordinate to facilitate the LMBClick handling */
781 wineItem->expandBox.left = xpos-4;
782 wineItem->expandBox.top = center-4;
783 wineItem->expandBox.right = xpos+5;
784 wineItem->expandBox.bottom = center+5;
788 wineItem->expandBox.left,
789 wineItem->expandBox.top ,
790 wineItem->expandBox.right,
791 wineItem->expandBox.bottom);
793 MoveToEx (hdc, xpos-2, center, NULL);
794 LineTo (hdc, xpos+3, center);
796 if (!(wineItem->state & TVIS_EXPANDED)) {
797 MoveToEx (hdc, xpos, center-2, NULL);
798 LineTo (hdc, xpos, center+3);
804 * Display the image associated with this item
806 xpos += 13; /* update position */
807 if (wineItem->mask & (TVIF_IMAGE|TVIF_SELECTEDIMAGE)) {
809 HIMAGELIST *himlp = NULL;
811 /* State images are displayed to the left of the Normal image
812 * image number is in state; zero should be `display no image'.
813 * FIXME: that last sentence looks like it needs some checking.
815 if (infoPtr->himlState)
816 himlp=&infoPtr->himlState;
817 imageIndex=wineItem->state>>12;
818 imageIndex++; /* yeah, right */
819 TRACE ("imindex:%d\n",imageIndex);
820 if ((himlp) && (imageIndex))
822 imageIndex--; /* see FIXME */
823 ImageList_Draw ( *himlp, imageIndex, hdc, xpos-2, r.top+1, ILD_NORMAL);
824 ImageList_GetIconSize (*himlp, &cx, &cy);
825 wineItem->statebitmap.left=xpos-2;
826 wineItem->statebitmap.right=xpos-2+cx;
827 wineItem->statebitmap.top=r.top+1;
828 wineItem->statebitmap.bottom=r.top+1+cy;
832 /* Now, draw the normal image; can be either selected or
833 * non-selected image.
837 if (infoPtr->himlNormal)
838 himlp=&infoPtr->himlNormal; /* get the image list */
840 imageIndex = wineItem->iImage;
841 if ( (wineItem->state & TVIS_SELECTED) &&
842 (wineItem->iSelectedImage)) {
844 /* The item is curently selected */
845 if (wineItem->iSelectedImage == I_IMAGECALLBACK)
846 TREEVIEW_SendDispInfoNotify
847 (hwnd, wineItem, TVN_GETDISPINFOA, TVIF_SELECTEDIMAGE);
849 imageIndex = wineItem->iSelectedImage;
851 /* The item is not selected */
852 if (wineItem->iImage == I_IMAGECALLBACK)
853 TREEVIEW_SendDispInfoNotify
854 (hwnd, wineItem, TVN_GETDISPINFOA, TVIF_IMAGE);
856 imageIndex = wineItem->iImage;
863 if(wineItem->stateMask & TVIS_OVERLAYMASK)
864 ovlIdx = wineItem->state & TVIS_OVERLAYMASK;
866 ImageList_Draw ( *himlp, imageIndex, hdc, xpos-2, r.top+1, ILD_NORMAL|ovlIdx);
867 ImageList_GetIconSize (*himlp, &cx, &cy);
868 wineItem->bitmap.left=xpos-2;
869 wineItem->bitmap.right=xpos-2+cx;
870 wineItem->bitmap.top=r.top+1;
871 wineItem->bitmap.bottom=r.top+1+cy;
878 * Display the text associated with this item
881 if ((wineItem->mask & TVIF_TEXT) && (wineItem->pszText))
883 COLORREF oldTextColor = 0;
886 BOOL inFocus = GetFocus() == hwnd;
888 TREEVIEW_ITEM tmpItem;
891 if (wineItem->pszText == LPSTR_TEXTCALLBACKA)
893 tmpItem.hItem = wineItem->hItem;
894 tmpItem.state = wineItem->state;
895 tmpItem.lParam = wineItem->lParam;
896 tmpItem.pszText = buf;
897 tmpItem.cchTextMax = sizeof(buf);
899 TREEVIEW_SendDispInfoNotify(hwnd, &tmpItem, TVN_GETDISPINFOA, TVIF_TEXT);
905 wineItem->text.left = r.left;
906 wineItem->text.right = r.right;
907 wineItem->text.top = r.top;
908 wineItem->text.bottom= r.bottom;
910 oldBkMode = SetBkMode(hdc, TRANSPARENT);
912 /* - If item is drop target or it is selected and window is in focus -
913 * use blue background (COLOR_HIGHLIGHT).
914 * - If item is selected, window is not in focus, but it has style
915 * TVS_SHOWSELALWAYS - use grey background (COLOR_BTNFACE)
916 * - Otherwise - don't fill background
918 if ((wineItem->state & TVIS_DROPHILITED) ||
919 ((wineItem->state & TVIS_SELECTED) &&
920 (inFocus || (GetWindowLongA( hwnd, GWL_STYLE) & TVS_SHOWSELALWAYS))))
922 if ((wineItem->state & TVIS_DROPHILITED) || inFocus)
924 hbrBk = CreateSolidBrush(GetSysColor( COLOR_HIGHLIGHT));
925 oldTextColor = SetTextColor(hdc, GetSysColor( COLOR_HIGHLIGHTTEXT));
929 hbrBk = CreateSolidBrush(GetSysColor( COLOR_BTNFACE));
931 if (infoPtr->clrText == -1)
932 oldTextColor = SetTextColor(hdc, GetSysColor( COLOR_WINDOWTEXT));
934 oldTextColor = SetTextColor(hdc, infoPtr->clrText);
939 if (infoPtr->clrText == -1)
940 oldTextColor = SetTextColor(hdc, GetSysColor( COLOR_WINDOWTEXT));
942 oldTextColor = SetTextColor(hdc, infoPtr->clrText);
945 if (wineItem->pszText != LPSTR_TEXTCALLBACKA)
946 tmpItem.pszText = wineItem->pszText;
948 /* Obtain the text coordinate */
952 lstrlenA(tmpItem.pszText),
954 uTextJustify | DT_VCENTER | DT_SINGLELINE | DT_CALCRECT | DT_NOPREFIX);
956 /* We need to reset it to items height */
957 wineItem->text.top = r.top;
958 wineItem->text.bottom = r.bottom;
959 wineItem->text.right += 4; /* This is extra for focus rectangle */
963 FillRect(hdc, &wineItem->text, hbrBk);
967 wineItem->text.left += 2;
972 lstrlenA(tmpItem.pszText),
974 uTextJustify | DT_VCENTER | DT_SINGLELINE | DT_NOPREFIX);
976 wineItem->text.left -=2;
978 /* Restore the hdc state */
979 SetTextColor( hdc, oldTextColor);
981 /* Draw the box arround the selected item */
982 if (wineItem->state & TVIS_SELECTED && inFocus)
984 HPEN hNewPen = CreatePen(PS_DOT, 0, GetSysColor(COLOR_WINDOWTEXT) );
985 HPEN hOldPen = SelectObject( hdc, hNewPen );
986 INT rop = SetROP2(hdc, R2_XORPEN);
989 points[4].x = points[0].x = wineItem->text.left;
990 points[4].y = points[0].y = wineItem->text.top;
991 points[1].x = wineItem->text.right-1 ;
992 points[1].y = wineItem->text.top;
993 points[2].x = wineItem->text.right-1;
994 points[2].y = wineItem->text.bottom-1;
995 points[3].x = wineItem->text.left;
996 points[3].y = wineItem->text.bottom-1;
998 Polyline (hdc,points,5);
1001 DeleteObject(hNewPen);
1002 SelectObject(hdc, hOldPen);
1005 if (oldBkMode != TRANSPARENT)
1006 SetBkMode(hdc, oldBkMode);
1009 /* Draw insertion mark if necessary */
1011 if (infoPtr->insertMarkItem)
1012 TRACE ("item:%d,mark:%d\n", (int)wineItem->hItem,
1013 (int) infoPtr->insertMarkItem);
1014 if (wineItem->hItem==infoPtr->insertMarkItem) {
1015 HPEN hNewPen, hOldPen;
1018 hNewPen = CreatePen(PS_SOLID, 2, infoPtr->clrInsertMark);
1019 hOldPen = SelectObject( hdc, hNewPen );
1021 if (infoPtr->insertBeforeorAfter)
1022 offset=wineItem->text.top+1;
1024 offset=wineItem->text.bottom-1;
1026 MoveToEx (hdc, wineItem->text.left, offset-3, NULL);
1027 LineTo (hdc, wineItem->text.left, offset+3);
1029 MoveToEx (hdc, wineItem->text.left, offset, NULL);
1030 LineTo (hdc, r.right-2, offset);
1032 MoveToEx (hdc, r.right-2, offset+3, NULL);
1033 LineTo (hdc, r.right-2, offset-3);
1035 DeleteObject(hNewPen);
1037 SelectObject(hdc, hOldPen);
1040 if (cditem & CDRF_NOTIFYPOSTPAINT) {
1041 cditem=TREEVIEW_SendCustomDrawItemNotify
1042 (hwnd, hdc, wineItem, CDDS_ITEMPOSTPAINT);
1043 TRACE("postpaint:cditem-app returns 0x%x\n",cditem);
1046 SelectObject (hdc, hOldFont);
1050 TREEVIEW_GetItemRect (HWND hwnd, WPARAM wParam, LPARAM lParam)
1052 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
1053 TREEVIEW_ITEM *wineItem;
1055 LPRECT lpRect = (LPRECT)lParam;
1060 * validate parameters
1065 if (infoPtr->Timer & TV_REFRESH_TIMER_SET) {
1067 TREEVIEW_Refresh (hwnd, hdc); /* we want a rect for the current view */
1068 ReleaseDC(hwnd,hdc);
1073 * retrieve the item ptr
1075 iItem = (HTREEITEM *) lParam;
1076 wineItem = TREEVIEW_ValidItem (infoPtr, *iItem);
1077 if ((!wineItem) || (!wineItem->visible))
1081 * If wParam is TRUE return the text size otherwise return
1082 * the whole item size
1085 lpRect->left = wineItem->text.left;
1086 lpRect->right = wineItem->text.right;
1087 lpRect->bottom = wineItem->text.bottom;
1088 lpRect->top = wineItem->text.top;
1090 lpRect->left = wineItem->rect.left;
1091 lpRect->right = wineItem->rect.right;
1092 lpRect->bottom = wineItem->rect.bottom;
1093 lpRect->top = wineItem->rect.top;
1096 TRACE("[L:%d R:%d T:%d B:%d]\n",
1097 lpRect->left,lpRect->right,
1098 lpRect->top,lpRect->bottom);
1104 TREEVIEW_GetVisibleCount (HWND hwnd, WPARAM wParam, LPARAM lParam)
1107 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
1109 return (LRESULT) infoPtr->uVisibleHeight / infoPtr->uRealItemHeight;
1115 TREEVIEW_SetItemA (HWND hwnd, WPARAM wParam, LPARAM lParam)
1117 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
1118 TREEVIEW_ITEM *wineItem;
1122 tvItem=(LPTVITEMEXA) lParam;
1123 iItem=(INT)tvItem->hItem;
1124 TRACE("item %d,mask %x\n",iItem,tvItem->mask);
1126 wineItem = TREEVIEW_ValidItem (infoPtr, (HTREEITEM)iItem);
1127 if (!wineItem) return FALSE;
1129 if (tvItem->mask & TVIF_CHILDREN) {
1130 wineItem->cChildren=tvItem->cChildren;
1133 if (tvItem->mask & TVIF_IMAGE) {
1134 wineItem->iImage=tvItem->iImage;
1137 if (tvItem->mask & TVIF_INTEGRAL) {
1138 wineItem->iIntegral=tvItem->iIntegral;
1141 if (tvItem->mask & TVIF_PARAM) {
1142 wineItem->lParam=tvItem->lParam;
1145 if (tvItem->mask & TVIF_SELECTEDIMAGE) {
1146 wineItem->iSelectedImage=tvItem->iSelectedImage;
1149 if (tvItem->mask & TVIF_STATE) {
1150 TRACE ("prevstate,state,mask:%x,%x,%x\n",wineItem->state,tvItem->state,
1152 wineItem->state&= ~tvItem->stateMask;
1153 wineItem->state|= (tvItem->state & tvItem->stateMask);
1154 wineItem->stateMask|= tvItem->stateMask;
1157 if (tvItem->mask & TVIF_TEXT)
1159 if (tvItem->pszText!=LPSTR_TEXTCALLBACKA)
1161 len=lstrlenA (tvItem->pszText) + 1;
1162 if (len>wineItem->cchTextMax)
1164 wineItem->pszText= COMCTL32_ReAlloc (wineItem->pszText, len);
1165 wineItem->cchTextMax = len;
1168 lstrcpynA (wineItem->pszText, tvItem->pszText,len);
1172 if (wineItem->cchTextMax)
1174 COMCTL32_Free (wineItem->pszText);
1175 wineItem->cchTextMax=0;
1177 wineItem->pszText=LPSTR_TEXTCALLBACKA;
1181 wineItem->mask |= tvItem->mask;
1187 TREEVIEW_GetItemState (HWND hwnd, WPARAM wParam, LPARAM lParam)
1190 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
1191 TREEVIEW_ITEM *wineItem;
1194 wineItem = TREEVIEW_ValidItem (infoPtr, (HTREEITEM)wParam);
1195 if (!wineItem) return 0;
1197 return (wineItem->state & lParam);
1204 TREEVIEW_Refresh (HWND hwnd, HDC hdc)
1206 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
1212 INT iItem, indent, x, y, height, itemHeight;
1213 INT viewtop,viewbottom,viewleft,viewright;
1214 TREEVIEW_ITEM *wineItem, *prevItem;
1219 if (infoPtr->Timer & TV_REFRESH_TIMER_SET) {
1220 KillTimer (hwnd, TV_REFRESH_TIMER);
1221 infoPtr->Timer &= ~TV_REFRESH_TIMER_SET;
1225 GetClientRect (hwnd, &rect);
1226 if ((rect.left >= rect.right) || (rect.top >= rect.bottom)) return;
1228 infoPtr->cdmode=TREEVIEW_SendCustomDrawNotify(hwnd,CDDS_PREPAINT,hdc,rect);
1230 if (infoPtr->cdmode==CDRF_SKIPDEFAULT) return;
1232 infoPtr->uVisibleHeight= rect.bottom-rect.top + 1;
1233 infoPtr->uVisibleWidth= rect.right-rect.left + 1;
1235 viewtop=infoPtr->cy;
1236 viewbottom=infoPtr->cy + rect.bottom-rect.top;
1237 viewleft=infoPtr->cx;
1238 viewright=infoPtr->cx + rect.right-rect.left;
1240 TRACE("[%d %d %d %d]\n",viewtop,viewbottom,viewleft,viewright);
1242 /* draw background */
1244 hbrBk = CreateSolidBrush (infoPtr->clrBk);
1245 FillRect(hdc, &rect, hbrBk);
1246 DeleteObject(hbrBk);
1248 ImageList_GetIconSize (infoPtr->himlNormal, &x, &itemHeight);
1249 if (infoPtr->uItemHeight>itemHeight)
1250 itemHeight=infoPtr->uItemHeight;
1252 // assume that bold and normal fonts have same height
1253 hOldFont = SelectObject (hdc, infoPtr->hBoldFont);
1254 GetTextMetricsA (hdc, &tm);
1255 if ((tm.tmHeight + tm.tmExternalLeading + FOCUS_BORDER) > itemHeight)
1256 itemHeight=tm.tmHeight + tm.tmExternalLeading + FOCUS_BORDER;
1257 SelectObject (hdc, hOldFont);
1259 infoPtr->uRealItemHeight=itemHeight;
1261 iItem=(INT)infoPtr->TopRootItem;
1262 infoPtr->firstVisible=0;
1269 wineItem= & infoPtr->items[iItem];
1270 wineItem->iLevel=indent;
1272 /* FIXME: remove this in later stage */
1274 if (wineItem->pszText!=LPSTR_TEXTCALLBACK32A)
1275 TRACE (treeview, "%d %d [%d %d %d %d] (%s)\n",y,x,
1276 wineItem->rect.top, wineItem->rect.bottom,
1277 wineItem->rect.left, wineItem->rect.right,
1280 TRACE (treeview, "%d [%d %d %d %d] (CALLBACK)\n",
1282 wineItem->rect.top, wineItem->rect.bottom,
1283 wineItem->rect.left, wineItem->rect.right);
1286 height=itemHeight * wineItem->iIntegral;
1287 if ((y >= viewtop) && (y <= viewbottom) &&
1288 (x >= viewleft ) && (x <= viewright)) {
1289 wineItem->visible = TRUE;
1290 wineItem->rect.top = y - infoPtr->cy + rect.top;
1291 wineItem->rect.bottom = wineItem->rect.top + height-1;
1292 wineItem->rect.left = x - infoPtr->cx + rect.left;
1293 wineItem->rect.right = rect.right;
1294 if (!infoPtr->firstVisible)
1295 infoPtr->firstVisible=wineItem->hItem;
1296 TREEVIEW_DrawItem (hwnd, hdc, wineItem);
1299 wineItem->visible = FALSE;
1300 wineItem->rect.left = wineItem->rect.top = 0;
1301 wineItem->rect.right= wineItem->rect.bottom = 0;
1302 wineItem->text.left = wineItem->text.top = 0;
1303 wineItem->text.right= wineItem->text.bottom = 0;
1306 /* look up next item */
1308 if ((wineItem->firstChild) && (wineItem->state & TVIS_EXPANDED)) {
1309 iItem=(INT)wineItem->firstChild;
1311 x+=infoPtr->uIndent;
1312 if (x>infoPtr->uTotalWidth)
1313 infoPtr->uTotalWidth=x;
1316 iItem=(INT)wineItem->sibling;
1317 while ((!iItem) && (indent>0)) {
1319 x-=infoPtr->uIndent;
1320 wineItem=&infoPtr->items[(INT)wineItem->parent];
1321 iItem=(INT)wineItem->sibling;
1327 /* FIXME: infoPtr->uTotalWidth should also take item label into account */
1328 /* FIXME: or should query item sizes (ie check CDRF_NEWFONT) */
1330 infoPtr->uTotalHeight=y;
1331 if (y >= (viewbottom-viewtop)) {
1332 if (!(infoPtr->uInternalStatus & TV_VSCROLL))
1333 ShowScrollBar (hwnd, SB_VERT, TRUE);
1334 infoPtr->uInternalStatus |=TV_VSCROLL;
1335 SetScrollRange (hwnd, SB_VERT, 0,
1336 y - infoPtr->uVisibleHeight, FALSE);
1337 SetScrollPos (hwnd, SB_VERT, infoPtr->cy, TRUE);
1340 if (infoPtr->uInternalStatus & TV_VSCROLL)
1341 ShowScrollBar (hwnd, SB_VERT, FALSE);
1342 infoPtr->uInternalStatus &= ~TV_VSCROLL;
1346 if (infoPtr->cdmode & CDRF_NOTIFYPOSTPAINT)
1347 infoPtr->cdmode=TREEVIEW_SendCustomDrawNotify
1348 (hwnd, CDDS_POSTPAINT, hdc, rect);
1355 TREEVIEW_HandleTimer (HWND hwnd, WPARAM wParam, LPARAM lParam)
1357 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
1359 TRACE(" %d\n",wParam);
1362 case TV_REFRESH_TIMER:
1363 KillTimer (hwnd, TV_REFRESH_TIMER);
1364 infoPtr->Timer &= ~TV_REFRESH_TIMER_SET;
1365 InvalidateRect(hwnd, NULL, FALSE);
1368 KillTimer (hwnd, TV_EDIT_TIMER);
1369 infoPtr->Timer &= ~TV_EDIT_TIMER_SET;
1372 ERR("got unknown timer\n");
1380 TREEVIEW_QueueRefresh (HWND hwnd)
1383 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
1386 if (infoPtr->Timer & TV_REFRESH_TIMER_SET) {
1387 KillTimer (hwnd, TV_REFRESH_TIMER);
1390 SetTimer (hwnd, TV_REFRESH_TIMER, TV_REFRESH_DELAY, 0);
1391 infoPtr->Timer|=TV_REFRESH_TIMER_SET;
1397 TREEVIEW_GetItemA (HWND hwnd, WPARAM wParam, LPARAM lParam)
1399 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
1401 TREEVIEW_ITEM *wineItem;
1404 tvItem=(LPTVITEMEXA) lParam;
1405 iItem=(INT)tvItem->hItem;
1407 wineItem = TREEVIEW_ValidItem (infoPtr, (HTREEITEM)iItem);
1408 if (!wineItem) return FALSE;
1410 if (tvItem->mask & TVIF_CHILDREN) {
1411 if (TVIF_CHILDREN==I_CHILDRENCALLBACK)
1412 FIXME("I_CHILDRENCALLBACK not supported\n");
1413 tvItem->cChildren=wineItem->cChildren;
1416 if (tvItem->mask & TVIF_HANDLE) {
1417 tvItem->hItem=wineItem->hItem;
1420 if (tvItem->mask & TVIF_IMAGE) {
1421 tvItem->iImage=wineItem->iImage;
1424 if (tvItem->mask & TVIF_INTEGRAL) {
1425 tvItem->iIntegral=wineItem->iIntegral;
1428 /* undocumented: windows ignores TVIF_PARAM and
1429 * always sets lParam
1431 tvItem->lParam=wineItem->lParam;
1433 if (tvItem->mask & TVIF_SELECTEDIMAGE) {
1434 tvItem->iSelectedImage=wineItem->iSelectedImage;
1437 if (tvItem->mask & TVIF_STATE) {
1438 tvItem->state=wineItem->state & tvItem->stateMask;
1441 if (tvItem->mask & TVIF_TEXT) {
1442 if (wineItem->pszText == LPSTR_TEXTCALLBACKA) {
1443 tvItem->pszText = LPSTR_TEXTCALLBACKA; /* FIXME:send notification? */
1444 ERR(" GetItem called with LPSTR_TEXTCALLBACK\n");
1446 else if (wineItem->pszText) {
1447 lstrcpynA (tvItem->pszText, wineItem->pszText, tvItem->cchTextMax);
1451 TRACE("item %d<%p>, txt %p, img %p, action %x\n",
1452 iItem, tvItem, tvItem->pszText, &tvItem->iImage, tvItem->mask);
1459 TREEVIEW_GetItemW (HWND hwnd, WPARAM wParam, LPARAM lParam)
1461 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
1463 TREEVIEW_ITEM *wineItem;
1466 tvItem=(LPTVITEMEXA) lParam;
1467 iItem=(INT)tvItem->hItem;
1469 wineItem = TREEVIEW_ValidItem (infoPtr, (HTREEITEM)iItem);
1470 if (!wineItem) return FALSE;
1472 if (tvItem->mask & TVIF_CHILDREN) {
1473 if (TVIF_CHILDREN==I_CHILDRENCALLBACK)
1474 FIXME("I_CHILDRENCALLBACK not supported\n");
1475 tvItem->cChildren=wineItem->cChildren;
1478 if (tvItem->mask & TVIF_HANDLE) {
1479 tvItem->hItem=wineItem->hItem;
1482 if (tvItem->mask & TVIF_IMAGE) {
1483 tvItem->iImage=wineItem->iImage;
1486 if (tvItem->mask & TVIF_INTEGRAL) {
1487 tvItem->iIntegral=wineItem->iIntegral;
1490 /* undocumented: windows ignores TVIF_PARAM and
1491 * always sets lParam
1493 tvItem->lParam=wineItem->lParam;
1495 if (tvItem->mask & TVIF_SELECTEDIMAGE) {
1496 tvItem->iSelectedImage=wineItem->iSelectedImage;
1499 if (tvItem->mask & TVIF_STATE) {
1500 tvItem->state=wineItem->state & tvItem->stateMask;
1504 if (tvItem->mask & TVIF_TEXT) {
1505 if (wineItem->pszText == LPSTR_TEXTCALLBACKW) {
1506 tvItem->pszText = LPSTR_TEXTCALLBACKW; /* FIXME:send notification? */
1507 ERR(" GetItem called with LPSTR_TEXTCALLBACK\n");
1509 else if (wineItem->pszText) {
1510 lstrcpynAtoW (tvItem->pszText, wineItem->pszText, tvItem->cchTextMax);
1514 wineItem->pszText = NULL;
1516 TRACE("item %d<%p>, txt %p, img %p, action %x\n",
1517 iItem, tvItem, tvItem->pszText, &tvItem->iImage, tvItem->mask);
1524 /* FIXME: check implementation of TVGN_NEXT/TVGN_NEXTVISIBLE */
1527 TREEVIEW_GetNextItem (HWND hwnd, WPARAM wParam, LPARAM lParam)
1530 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
1531 TREEVIEW_ITEM *wineItem, *returnItem;
1532 INT iItem = (INT)lParam, retval = 0, flag = (INT)wParam;
1537 retval = (INT)infoPtr->TopRootItem;
1541 retval = (INT)infoPtr->selectedItem;
1544 case TVGN_FIRSTVISIBLE: /* FIXME:we should only recalculate, not redraw */
1546 TREEVIEW_Refresh (hwnd, hdc);
1547 ReleaseDC(hwnd,hdc);
1548 retval = (INT)infoPtr->firstVisible;
1551 case TVGN_DROPHILITE:
1552 retval = (INT)infoPtr->dropItem;
1556 wineItem = TREEVIEW_ValidItem (infoPtr, (HTREEITEM)iItem);
1557 retval = wineItem ? (INT)wineItem->sibling : 0;
1561 wineItem = TREEVIEW_ValidItem (infoPtr, (HTREEITEM)iItem);
1562 retval = wineItem ? (INT)wineItem->upsibling : 0;
1566 wineItem = TREEVIEW_ValidItem (infoPtr, (HTREEITEM)iItem);
1567 retval = wineItem ? (INT)wineItem->parent : 0;
1571 wineItem = TREEVIEW_ValidItem (infoPtr, (HTREEITEM)iItem);
1572 retval = wineItem ? (INT)wineItem->firstChild : 0;
1575 case TVGN_LASTVISIBLE:
1576 if((wineItem = TREEVIEW_ValidItem (infoPtr, (HTREEITEM)iItem))) {
1577 returnItem = TREEVIEW_GetLastListItem (infoPtr,wineItem);
1578 retval = returnItem ? (INT)returnItem->hItem : 0;
1582 case TVGN_NEXTVISIBLE:
1583 if((wineItem = TREEVIEW_ValidItem (infoPtr, (HTREEITEM)iItem))) {
1584 returnItem = TREEVIEW_GetNextListItem (infoPtr,wineItem);
1585 retval = returnItem ? (INT)returnItem->hItem : 0;
1589 case TVGN_PREVIOUSVISIBLE:
1590 if((wineItem = TREEVIEW_ValidItem (infoPtr, (HTREEITEM)iItem))) {
1591 returnItem = TREEVIEW_GetPrevListItem (infoPtr, wineItem);
1592 retval = returnItem ? (INT)returnItem->hItem : 0;
1597 FIXME("Unknown msg %x,item %x\n", flag,iItem);
1601 TRACE("flags %x, item %d returns %d\n", flag, iItem, retval);
1607 TREEVIEW_GetCount (HWND hwnd, WPARAM wParam, LPARAM lParam)
1609 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
1611 TRACE(" %d\n",infoPtr->uNumItems);
1612 return (LRESULT) infoPtr->uNumItems;
1615 /***************************************************************************
1616 * This method does the chaining of the insertion of a treeview item
1618 * If parent is NULL, we're inserting at the root of the list.
1620 static void TREEVIEW_InsertBefore(
1621 TREEVIEW_INFO *infoPtr,
1622 TREEVIEW_ITEM *newItem,
1623 TREEVIEW_ITEM *sibling,
1624 TREEVIEW_ITEM *parent)
1626 HTREEITEM siblingHandle = 0;
1627 HTREEITEM upSiblingHandle = 0;
1628 TREEVIEW_ITEM *upSibling = NULL;
1630 if (newItem == NULL)
1631 ERR("NULL newItem, impossible condition\n");
1633 if (sibling != NULL) /* Insert before this sibling for this parent */
1635 /* Store the new item sibling up sibling and sibling tem handle */
1636 siblingHandle = sibling->hItem;
1637 upSiblingHandle = sibling->upsibling;
1638 /* As well as a pointer to the upsibling sibling object */
1639 if ( (INT)sibling->upsibling != 0 )
1640 upSibling = &infoPtr->items[(INT)sibling->upsibling];
1642 /* Adjust the sibling pointer */
1643 sibling->upsibling = newItem->hItem;
1645 /* Adjust the new item pointers */
1646 newItem->upsibling = upSiblingHandle;
1647 newItem->sibling = siblingHandle;
1649 /* Adjust the up sibling pointer */
1650 if ( upSibling != NULL )
1651 upSibling->sibling = newItem->hItem;
1653 /* this item is the first child of this parent, adjust parent pointers */
1655 parent->firstChild = newItem->hItem;
1657 infoPtr->TopRootItem= newItem->hItem;
1659 else /* Insert as first child of this parent */
1661 parent->firstChild = newItem->hItem;
1664 /***************************************************************************
1665 * This method does the chaining of the insertion of a treeview item
1667 * If parent is NULL, we're inserting at the root of the list.
1669 static void TREEVIEW_InsertAfter(
1670 TREEVIEW_INFO *infoPtr,
1671 TREEVIEW_ITEM *newItem,
1672 TREEVIEW_ITEM *upSibling,
1673 TREEVIEW_ITEM *parent)
1675 HTREEITEM upSiblingHandle = 0;
1676 HTREEITEM siblingHandle = 0;
1677 TREEVIEW_ITEM *sibling = NULL;
1680 if (newItem == NULL)
1681 ERR("NULL newItem, impossible condition\n");
1683 if (upSibling != NULL) /* Insert after this upsibling for this parent */
1685 /* Store the new item up sibling and sibling item handle */
1686 upSiblingHandle = upSibling->hItem;
1687 siblingHandle = upSibling->sibling;
1688 /* As well as a pointer to the upsibling sibling object */
1689 if ( (INT)upSibling->sibling != 0 )
1690 sibling = &infoPtr->items[(INT)upSibling->sibling];
1692 /* Adjust the up sibling pointer */
1693 upSibling->sibling = newItem->hItem;
1695 /* Adjust the new item pointers */
1696 newItem->upsibling = upSiblingHandle;
1697 newItem->sibling = siblingHandle;
1699 /* Adjust the sibling pointer */
1700 if ( sibling != NULL )
1701 sibling->upsibling = newItem->hItem;
1704 newItem is the last of the level, nothing else to do
1707 else /* Insert as first child of this parent */
1709 parent->firstChild = newItem->hItem;
1712 /***************************************************************************
1713 * Forward the DPA local callback to the treeview owner callback
1715 static INT WINAPI TREEVIEW_CallBackCompare(
1720 /* Forward the call to the client define callback */
1721 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr((HWND)tvInfoPtr);
1722 return (infoPtr->pCallBackSort->lpfnCompare)(
1723 ((TREEVIEW_ITEM*)first)->lParam,
1724 ((TREEVIEW_ITEM*)second)->lParam,
1725 infoPtr->pCallBackSort->lParam);
1728 /***************************************************************************
1729 * Treeview native sort routine: sort on item text.
1731 static INT WINAPI TREEVIEW_SortOnName (
1736 HWND hwnd=(HWND) tvInfoPtr;
1738 TREEVIEW_ITEM *item;
1741 item=(TREEVIEW_ITEM *) first;
1742 if (item->pszText==LPSTR_TEXTCALLBACKA) {
1743 TREEVIEW_SendDispInfoNotify (hwnd, item, TVN_GETDISPINFOA, TVIF_TEXT);
1747 item=(TREEVIEW_ITEM *) second;
1748 if (item->pszText==LPSTR_TEXTCALLBACKA) {
1749 TREEVIEW_SendDispInfoNotify (hwnd, item, TVN_GETDISPINFOA, TVIF_TEXT);
1753 return -strcmp (txt1,txt2);
1756 /***************************************************************************
1757 * Setup the treeview structure with regards of the sort method
1758 * and sort the children of the TV item specified in lParam
1759 * fRecurse: currently unused. Should be zero.
1760 * parent: if pSort!=NULL, should equal pSort->hParent.
1761 * otherwise, item which child items are to be sorted.
1762 * pSort: sort method info. if NULL, sort on item text.
1763 * if non-NULL, sort on item's lParam content, and let the
1764 * application decide what that means. See also TVM_SORTCHILDRENCB.
1767 static LRESULT WINAPI TREEVIEW_Sort (
1774 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
1775 TREEVIEW_ITEM *sortMe = NULL; /* Node for which we sort the children */
1777 /* Obtain the TVSORTBC struct */
1778 infoPtr->pCallBackSort = pSort;
1780 /* undocumented feature: TVI_ROOT means `sort the whole tree' */
1782 if (parent==TVI_ROOT)
1783 parent=infoPtr->TopRootItem;
1785 /* Check for a valid handle to the parent item */
1786 if (!TREEVIEW_ValidItem(infoPtr, parent))
1788 ERR ("invalid item hParent=%x\n", (INT)parent);
1792 /* Obtain the parent node to sort */
1793 sortMe = &infoPtr->items[ (INT)parent ];
1795 /* Make sure there is something to sort */
1796 if ( sortMe->cChildren > 1 )
1798 /* pointer organization */
1799 HDPA sortList = DPA_Create(sortMe->cChildren);
1800 HTREEITEM itemHandle = sortMe->firstChild;
1801 TREEVIEW_ITEM *itemPtr = & infoPtr->items[ (INT)itemHandle ];
1803 /* TREEVIEW_ITEM rechaining */
1809 /* Build the list of item to sort */
1813 sortList, /* the list */
1814 sortMe->cChildren+1, /* force the insertion to be an append */
1815 itemPtr); /* the ptr to store */
1817 /* Get the next sibling */
1818 itemHandle = itemPtr->sibling;
1819 itemPtr = & infoPtr->items[ (INT)itemHandle ];
1820 } while ( itemHandle != NULL );
1822 /* let DPA perform the sort activity */
1825 sortList, /* what */
1826 TREEVIEW_CallBackCompare, /* how */
1830 sortList, /* what */
1831 TREEVIEW_SortOnName, /* how */
1835 * Reorganized TREEVIEW_ITEM structures.
1836 * Note that we know we have at least two elements.
1839 /* Get the first item and get ready to start... */
1840 item = DPA_GetPtr(sortList, count++);
1841 while ( (nextItem = DPA_GetPtr(sortList, count++)) != NULL )
1843 /* link the two current item toghether */
1844 ((TREEVIEW_ITEM*)item)->sibling = ((TREEVIEW_ITEM*)nextItem)->hItem;
1845 ((TREEVIEW_ITEM*)nextItem)->upsibling = ((TREEVIEW_ITEM*)item)->hItem;
1847 if (prevItem == NULL) /* this is the first item, update the parent */
1849 sortMe->firstChild = ((TREEVIEW_ITEM*)item)->hItem;
1850 ((TREEVIEW_ITEM*)item)->upsibling = NULL;
1852 else /* fix the back chaining */
1854 ((TREEVIEW_ITEM*)item)->upsibling = ((TREEVIEW_ITEM*)prevItem)->hItem;
1857 /* get ready for the next one */
1862 /* the last item is pointed to by item and never has a sibling */
1863 ((TREEVIEW_ITEM*)item)->sibling = NULL;
1865 DPA_Destroy(sortList);
1873 /***************************************************************************
1874 * Setup the treeview structure with regards of the sort method
1875 * and sort the children of the TV item specified in lParam
1877 static LRESULT WINAPI TREEVIEW_SortChildrenCB(
1883 LPTVSORTCB pSort=(LPTVSORTCB) lParam;
1885 return TREEVIEW_Sort (hwnd, wParam, pSort->hParent, pSort);
1889 /***************************************************************************
1890 * Sort the children of the TV item specified in lParam.
1892 static LRESULT WINAPI TREEVIEW_SortChildren (
1897 return TREEVIEW_Sort (hwnd, (BOOL) wParam, (HTREEITEM) lParam, NULL);
1902 /* the method used below isn't the most memory-friendly, but it avoids
1903 a lot of memory reallocations */
1905 /* BTW: we waste handle 0; 0 is not an allowed handle. */
1908 TREEVIEW_InsertItemA (HWND hwnd, WPARAM wParam, LPARAM lParam)
1911 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
1912 TVINSERTSTRUCTA *ptdi;
1914 TREEVIEW_ITEM *wineItem, *parentItem, *prevsib, *sibItem;
1915 INT iItem,listItems,i,len;
1917 /* Item to insert */
1918 ptdi = (LPTVINSERTSTRUCTA) lParam;
1920 /* check if memory is available */
1922 if (infoPtr->uNumPtrsAlloced==0) {
1923 infoPtr->items = COMCTL32_Alloc (TVITEM_ALLOC*sizeof (TREEVIEW_ITEM));
1924 infoPtr->freeList= COMCTL32_Alloc ((1+(TVITEM_ALLOC>>5)) * sizeof (INT));
1925 infoPtr->uNumPtrsAlloced=TVITEM_ALLOC;
1926 infoPtr->TopRootItem=(HTREEITEM)1;
1930 * Reallocate contiguous space for items
1932 if (infoPtr->uNumItems == (infoPtr->uNumPtrsAlloced-1) ) {
1933 TREEVIEW_ITEM *oldItems = infoPtr->items;
1934 INT *oldfreeList = infoPtr->freeList;
1936 infoPtr->uNumPtrsAlloced*=2;
1937 infoPtr->items = COMCTL32_Alloc (infoPtr->uNumPtrsAlloced*sizeof (TREEVIEW_ITEM));
1938 infoPtr->freeList= COMCTL32_Alloc ((1+(infoPtr->uNumPtrsAlloced>>5))*sizeof (INT));
1940 memcpy (&infoPtr->items[0], &oldItems[0],
1941 infoPtr->uNumPtrsAlloced/2 * sizeof(TREEVIEW_ITEM));
1942 memcpy (&infoPtr->freeList[0], &oldfreeList[0],
1943 (infoPtr->uNumPtrsAlloced>>6) * sizeof(INT));
1945 COMCTL32_Free (oldItems);
1946 COMCTL32_Free (oldfreeList);
1950 * Reset infoPtr structure with new stat according to current TV picture
1953 infoPtr->uNumItems++;
1954 if ((INT)infoPtr->uMaxHandle==(infoPtr->uNumItems-1)) {
1955 iItem=infoPtr->uNumItems;
1956 infoPtr->uMaxHandle = (HTREEITEM)((INT)infoPtr->uMaxHandle + 1);
1957 } else { /* check freelist */
1958 for (i=0; i<=infoPtr->uNumPtrsAlloced>>5; i++) {
1959 if (infoPtr->freeList[i]) {
1960 iItem=ffs (infoPtr->freeList[i])-1;
1961 tv_clear_bit(iItem,&infoPtr->freeList[i]);
1968 if (TRACE_ON(treeview)) {
1969 for (i=0; i<=infoPtr->uNumPtrsAlloced>>5; i++)
1970 TRACE("%8x\n",infoPtr->freeList[i]);
1973 if (!iItem) ERR("Argh -- can't find free item.\n");
1976 * Find the parent item of the new item
1978 tvItem= & ptdi->DUMMYUNIONNAME.itemex;
1979 wineItem=& infoPtr->items[iItem];
1981 if ((ptdi->hParent==TVI_ROOT) || (ptdi->hParent==0)) {
1983 wineItem->parent = 0;
1984 sibItem = &infoPtr->items [(INT)infoPtr->TopRootItem];
1985 listItems = infoPtr->uNumItems;
1988 parentItem = &infoPtr->items[(INT)ptdi->hParent];
1990 /* Do the insertion here it if it's the only item of this parent */
1991 if (!parentItem->firstChild)
1992 parentItem->firstChild=(HTREEITEM)iItem;
1994 wineItem->parent = ptdi->hParent;
1995 sibItem = &infoPtr->items [(INT)parentItem->firstChild];
1996 listItems = parentItem->cChildren;
1997 parentItem->cChildren++;
2001 /* NOTE: I am moving some setup of the wineItem object that was initialy
2002 * done at the end of the function since some of the values are
2003 * required by the Callback sorting
2006 if (tvItem->mask & TVIF_TEXT)
2009 * Setup the item text stuff here since it's required by the Sort method
2010 * when the insertion are ordered
2012 if (tvItem->pszText!=LPSTR_TEXTCALLBACKA)
2014 TRACE("(%p,%s)\n", &tvItem->pszText, tvItem->pszText);
2015 len = lstrlenA (tvItem->pszText)+1;
2016 wineItem->pszText= COMCTL32_Alloc (len+1);
2017 strcpy (wineItem->pszText, tvItem->pszText);
2018 wineItem->cchTextMax=len;
2022 TRACE("LPSTR_TEXTCALLBACK\n");
2023 wineItem->pszText = LPSTR_TEXTCALLBACKA;
2024 wineItem->cchTextMax = 0;
2028 if (tvItem->mask & TVIF_PARAM)
2029 wineItem->lParam=tvItem->lParam;
2032 wineItem->upsibling=0; /* needed in case we're the first item in a list */
2033 wineItem->sibling=0;
2034 wineItem->firstChild=0;
2035 wineItem->hItem=(HTREEITEM)iItem;
2040 switch ((DWORD) ptdi->hInsertAfter) {
2041 case (DWORD) TVI_FIRST:
2042 if (sibItem==wineItem) break;
2043 if (wineItem->parent) {
2044 wineItem->sibling=parentItem->firstChild;
2045 parentItem->firstChild=(HTREEITEM)iItem;
2047 wineItem->sibling=infoPtr->TopRootItem;
2048 infoPtr->TopRootItem=(HTREEITEM)iItem;
2050 sibItem->upsibling=(HTREEITEM)iItem;
2053 case (DWORD) TVI_SORT:
2054 if (sibItem==wineItem)
2056 * This item is the first child of the level and it
2057 * has already been inserted
2062 TREEVIEW_ITEM *aChild;
2065 TREEVIEW_ITEM *previousChild = NULL;
2066 BOOL bItemInserted = FALSE;
2069 aChild = &infoPtr->items[(INT)parentItem->firstChild];
2071 aChild = &infoPtr->items[(INT)infoPtr->TopRootItem];
2073 /* lookup the text if using LPSTR_TEXTCALLBACKs */
2074 if (wineItem->pszText==LPSTR_TEXTCALLBACKA) {
2075 TREEVIEW_SendDispInfoNotify (hwnd, wineItem, TVN_GETDISPINFOA, TVIF_TEXT);
2078 /* Iterate the parent children to see where we fit in */
2079 while ( aChild != NULL )
2083 /* lookup the text if using LPSTR_TEXTCALLBACKs */
2084 if (aChild->pszText==LPSTR_TEXTCALLBACKA) {
2085 TREEVIEW_SendDispInfoNotify (hwnd, aChild, TVN_GETDISPINFOA, TVIF_TEXT);
2088 comp = strcmp(wineItem->pszText, aChild->pszText);
2089 if ( comp < 0 ) /* we are smaller than the current one */
2091 TREEVIEW_InsertBefore(infoPtr, wineItem, aChild, parentItem);
2092 bItemInserted = TRUE;
2095 else if ( comp > 0 ) /* we are bigger than the current one */
2097 previousChild = aChild;
2098 aChild = (aChild->sibling == 0) /* This will help us to exit */
2099 ? NULL /* if there is no more sibling */
2100 : &infoPtr->items[(INT)aChild->sibling];
2102 /* Look at the next item */
2105 else if ( comp == 0 )
2108 * An item with this name is already existing, therefore,
2109 * we add after the one we found
2111 TREEVIEW_InsertAfter(infoPtr, wineItem, aChild, parentItem);
2112 bItemInserted = TRUE;
2118 * we reach the end of the child list and the item as not
2119 * yet been inserted, therefore, insert it after the last child.
2121 if ( (! bItemInserted ) && (aChild == NULL) )
2122 TREEVIEW_InsertAfter(infoPtr, wineItem, previousChild, parentItem);
2128 case (DWORD) TVI_LAST:
2129 if (sibItem==wineItem) break;
2130 while (sibItem->sibling) {
2132 sibItem=&infoPtr->items [(INT)sibItem->sibling];
2134 sibItem->sibling=(HTREEITEM)iItem;
2135 wineItem->upsibling=sibItem->hItem;
2139 TREEVIEW_ITEM *localsibItem = sibItem;
2140 while ((localsibItem->sibling) &&
2141 (localsibItem->hItem!=ptdi->hInsertAfter))
2143 prevsib=localsibItem;
2144 localsibItem=&infoPtr->items [(INT)localsibItem->sibling];
2146 if (localsibItem->hItem!=ptdi->hInsertAfter) {
2147 WARN("tried to insert item after nonexisting handle %d treating as TVI_LAST.\n",
2148 (INT) ptdi->hInsertAfter);
2150 * retry placing it last
2152 if (sibItem==wineItem) break;
2153 while (sibItem->sibling) {
2155 sibItem=&infoPtr->items [(INT)sibItem->sibling];
2157 sibItem->sibling=(HTREEITEM)iItem;
2158 wineItem->upsibling=sibItem->hItem;
2161 prevsib=localsibItem;
2162 if (localsibItem->sibling) {
2163 localsibItem=&infoPtr->items [(INT)localsibItem->sibling];
2164 localsibItem->upsibling=(HTREEITEM)iItem;
2165 wineItem->sibling=localsibItem->hItem;
2167 prevsib->sibling=(HTREEITEM)iItem;
2168 wineItem->upsibling=prevsib->hItem;
2175 /* Fill in info structure */
2177 TRACE("new item %d; parent %d, mask %x\n", iItem,
2178 (INT)wineItem->parent,tvItem->mask);
2180 wineItem->mask=tvItem->mask;
2181 wineItem->iIntegral=1;
2183 if (tvItem->mask & TVIF_CHILDREN) {
2184 wineItem->cChildren=tvItem->cChildren;
2185 if (tvItem->cChildren==I_CHILDRENCALLBACK)
2186 FIXME(" I_CHILDRENCALLBACK not supported\n");
2189 wineItem->expandBox.left = 0; /* Initialize the expandBox */
2190 wineItem->expandBox.top = 0;
2191 wineItem->expandBox.right = 0;
2192 wineItem->expandBox.bottom = 0;
2194 if (tvItem->mask & TVIF_IMAGE)
2195 wineItem->iImage=tvItem->iImage;
2197 /* If the application sets TVIF_INTEGRAL without
2198 supplying a TVITEMEX structure, it's toast */
2200 if (tvItem->mask & TVIF_INTEGRAL)
2201 wineItem->iIntegral=tvItem->iIntegral;
2203 if (tvItem->mask & TVIF_SELECTEDIMAGE)
2204 wineItem->iSelectedImage=tvItem->iSelectedImage;
2206 if (tvItem->mask & TVIF_STATE) {
2207 TRACE("item state: %x ->%x\n", wineItem->state, tvItem->state);
2208 TRACE("statemask: %x ->%x\n", wineItem->stateMask, tvItem->stateMask);
2209 wineItem->state=tvItem->state;
2210 wineItem->stateMask=tvItem->stateMask;
2213 TREEVIEW_QueueRefresh (hwnd);
2215 return (LRESULT) iItem;
2220 TREEVIEW_InsertItemW(HWND hwnd, WPARAM wParam, LPARAM lParam)
2222 TVINSERTSTRUCTW *tvisW;
2223 TVINSERTSTRUCTA tvisA;
2226 tvisW = (LPTVINSERTSTRUCTW)lParam;
2228 tvisA.hParent = tvisW->hParent;
2229 tvisA.hInsertAfter = tvisW->hInsertAfter;
2231 tvisA.DUMMYUNIONNAME.item.mask = tvisW->DUMMYUNIONNAME.item.mask;
2232 tvisA.DUMMYUNIONNAME.item.hItem = tvisW->DUMMYUNIONNAME.item.hItem;
2233 tvisA.DUMMYUNIONNAME.item.state = tvisW->DUMMYUNIONNAME.item.state;
2234 tvisA.DUMMYUNIONNAME.item.stateMask = tvisW->DUMMYUNIONNAME.item.stateMask;
2235 tvisA.DUMMYUNIONNAME.item.cchTextMax = tvisW->DUMMYUNIONNAME.item.cchTextMax;
2237 if(tvisW->DUMMYUNIONNAME.item.pszText)
2239 if (tvisW->DUMMYUNIONNAME.item.pszText!=LPSTR_TEXTCALLBACKW)
2241 int len = lstrlenW (tvisW->DUMMYUNIONNAME.item.pszText)+1;
2242 tvisA.DUMMYUNIONNAME.item.pszText = COMCTL32_Alloc (len);
2243 lstrcpyWtoA (tvisA.DUMMYUNIONNAME.item.pszText,
2244 tvisW->DUMMYUNIONNAME.item.pszText );
2248 tvisA.DUMMYUNIONNAME.item.pszText = LPSTR_TEXTCALLBACKA;
2249 tvisA.DUMMYUNIONNAME.item.cchTextMax = 0;
2253 tvisA.DUMMYUNIONNAME.item.iImage = tvisW->DUMMYUNIONNAME.item.iImage;
2254 tvisA.DUMMYUNIONNAME.item.iSelectedImage = tvisW->DUMMYUNIONNAME.item.iSelectedImage;
2255 tvisA.DUMMYUNIONNAME.item.cChildren = tvisW->DUMMYUNIONNAME.item.cChildren;
2256 tvisA.DUMMYUNIONNAME.item.lParam = tvisW->DUMMYUNIONNAME.item.lParam;
2258 lRes = TREEVIEW_InsertItemA(hwnd,wParam,(LPARAM)&tvisA);
2260 if (tvisA.DUMMYUNIONNAME.item.pszText!=LPSTR_TEXTCALLBACKA)
2262 COMCTL32_Free(tvisA.DUMMYUNIONNAME.item.pszText);
2271 TREEVIEW_DeleteItem (HWND hwnd, WPARAM wParam, LPARAM lParam)
2273 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
2275 TREEVIEW_ITEM *wineItem;
2277 TRACE("item = %08lx\n", lParam);
2279 if (lParam == (INT)TVI_ROOT) {
2280 TREEVIEW_RemoveTree (hwnd);
2282 iItem= (INT) lParam;
2283 wineItem = TREEVIEW_ValidItem (infoPtr, (HTREEITEM)iItem);
2284 if (!wineItem) return FALSE;
2286 if (wineItem->pszText==LPSTR_TEXTCALLBACKA)
2287 TRACE("LPSTR_TEXTCALLBACK\n");
2289 TRACE("%s\n",wineItem->pszText);
2290 TREEVIEW_RemoveItem (hwnd, wineItem);
2293 TREEVIEW_QueueRefresh (hwnd);
2301 TREEVIEW_GetIndent (HWND hwnd)
2303 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
2306 return infoPtr->uIndent;
2310 TREEVIEW_SetIndent (HWND hwnd, WPARAM wParam)
2312 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
2316 newIndent=(INT) wParam;
2317 if (newIndent < MINIMUM_INDENT) newIndent=MINIMUM_INDENT;
2318 infoPtr->uIndent=newIndent;
2324 TREEVIEW_GetToolTips (HWND hwnd)
2327 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
2330 return infoPtr->hwndToolTip;
2335 TREEVIEW_SetToolTips (HWND hwnd, WPARAM wParam)
2338 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
2342 prevToolTip=infoPtr->hwndToolTip;
2343 infoPtr->hwndToolTip= (HWND) wParam;
2349 static LRESULT CALLBACK
2350 TREEVIEW_GetEditControl (HWND hwnd)
2353 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
2355 return infoPtr->hwndEdit;
2359 TREEVIEW_Edit_SubclassProc (HWND hwnd, UINT uMsg, WPARAM wParam,
2367 HDC hdc = (HDC) wParam;
2368 GetClientRect (hwnd, &rc);
2369 Rectangle (hdc, rc.left, rc.top, rc.right, rc.bottom);
2375 return DLGC_WANTARROWS | DLGC_WANTALLKEYS;
2379 if (wParam == VK_ESCAPE)
2381 TREEVIEW_EndEditLabelNow(GetParent(hwnd), (WPARAM)TRUE, 0);
2384 else if (wParam == VK_RETURN)
2385 TREEVIEW_EndEditLabelNow(GetParent(hwnd), (WPARAM)FALSE, 0);
2392 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(GetParent(hwnd));
2394 return CallWindowProcA (infoPtr->wpEditOrig, hwnd, uMsg, wParam, lParam);
2405 /* should handle edit control messages here */
2408 TREEVIEW_Command (HWND hwnd, WPARAM wParam, LPARAM lParam)
2411 TRACE("%x %ld\n",wParam, lParam);
2413 switch (HIWORD(wParam))
2418 * Adjust the edit window size
2420 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
2421 TREEVIEW_ITEM *editItem = TREEVIEW_ValidItem(infoPtr, infoPtr->editItem);
2422 INT iLength = GetWindowTextLengthA(infoPtr->hwndEdit);
2423 HDC hdc = GetDC(infoPtr->hwndEdit);
2426 if ( GetTextMetricsA(hdc, &tm) )
2428 LONG newWidth = (iLength * tm.tmAveCharWidth) + 15;
2433 editItem->text.left - 2,
2434 editItem->text.top - 1,
2436 editItem->text.bottom - editItem->text.top + 3,
2439 ReleaseDC(hwnd, hdc);
2445 /* TREEVIEW_EndEditLabelNow(hwnd, (WPARAM)FALSE, 0);
2450 return SendMessageA (GetParent (hwnd), WM_COMMAND, wParam, lParam);
2457 TREEVIEW_Size (HWND hwnd, WPARAM wParam, LPARAM lParam)
2460 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
2462 if (infoPtr->bAutoSize)
2464 infoPtr->bAutoSize = FALSE;
2467 infoPtr->bAutoSize = TRUE;
2469 if (wParam == SIZE_RESTORED)
2471 infoPtr->uTotalWidth = LOWORD (lParam);
2472 infoPtr->uTotalHeight = HIWORD (lParam);
2474 FIXME("WM_SIZE flag %x %lx not handled\n", wParam, lParam);
2477 TREEVIEW_QueueRefresh (hwnd);
2484 TREEVIEW_StyleChanged (HWND hwnd, WPARAM wParam, LPARAM lParam)
2488 TRACE("(%x %lx)\n",wParam,lParam);
2490 TREEVIEW_Refresh (hwnd, hdc);
2491 ReleaseDC(hwnd,hdc);
2497 TREEVIEW_Create (HWND hwnd, WPARAM wParam, LPARAM lParam)
2499 TREEVIEW_INFO *infoPtr;
2500 DWORD dwStyle = GetWindowLongA (hwnd, GWL_STYLE);
2505 TRACE("wnd %x, style %lx\n",hwnd,dwStyle);
2506 /* allocate memory for info structure */
2507 infoPtr = (TREEVIEW_INFO *) COMCTL32_Alloc (sizeof(TREEVIEW_INFO));
2509 SetWindowLongA( hwnd, 0, (DWORD)infoPtr);
2511 if (infoPtr == NULL) {
2512 ERR("could not allocate info memory!\n");
2516 if ((TREEVIEW_INFO*) GetWindowLongA( hwnd, 0) != infoPtr) {
2517 ERR("pointer assignment error!\n");
2523 /* set default settings */
2524 infoPtr->uInternalStatus=0;
2525 infoPtr->uNumItems=0;
2526 infoPtr->clrBk = GetSysColor (COLOR_WINDOW);
2527 infoPtr->clrText = GetSysColor (COLOR_WINDOWTEXT);
2528 infoPtr->clrLine = GetSysColor (COLOR_WINDOWTEXT);
2529 infoPtr->clrInsertMark = GetSysColor (COLOR_BTNTEXT);
2532 infoPtr->uIndent = 15;
2533 infoPtr->himlNormal = NULL;
2534 infoPtr->himlState = NULL;
2535 infoPtr->uItemHeight = -1;
2536 GetTextMetricsA (hdc, &tm);
2537 infoPtr->hFont = GetStockObject (DEFAULT_GUI_FONT);
2538 GetObjectA (infoPtr->hFont, sizeof (LOGFONTA), &logFont);
2539 logFont.lfWeight=FW_BOLD;
2540 infoPtr->hBoldFont = CreateFontIndirectA (&logFont);
2542 infoPtr->items = NULL;
2543 infoPtr->selectedItem=0;
2544 infoPtr->clrText=-1; /* use system color */
2545 infoPtr->dropItem=0;
2546 infoPtr->insertMarkItem=0;
2547 infoPtr->insertBeforeorAfter=0;
2548 infoPtr->pCallBackSort=NULL;
2549 infoPtr->uScrollTime = 300; /* milliseconds */
2550 infoPtr->wpEditOrig = NULL; /* we haven't subclassed anything yet */
2552 infoPtr->hwndToolTip=0;
2553 if (!(dwStyle & TVS_NOTOOLTIPS)) { /* Create tooltip control */
2556 infoPtr->hwndToolTip =
2557 CreateWindowExA (0, TOOLTIPS_CLASSA, NULL, 0,
2558 CW_USEDEFAULT, CW_USEDEFAULT,
2559 CW_USEDEFAULT, CW_USEDEFAULT,
2562 /* Send NM_TOOLTIPSCREATED notification */
2563 if (infoPtr->hwndToolTip) {
2564 NMTOOLTIPSCREATED nmttc;
2566 nmttc.hdr.hwndFrom = hwnd;
2567 nmttc.hdr.idFrom = GetWindowLongA( hwnd, GWL_ID);
2568 nmttc.hdr.code = NM_TOOLTIPSCREATED;
2569 nmttc.hwndToolTips = infoPtr->hwndToolTip;
2571 SendMessageA (GetParent (hwnd), WM_NOTIFY,
2572 (WPARAM) GetWindowLongA( hwnd, GWL_ID), (LPARAM)&nmttc);
2575 ZeroMemory (&ti, sizeof(TTTOOLINFOA));
2576 ti.cbSize = sizeof(TTTOOLINFOA);
2577 ti.uFlags = TTF_IDISHWND | TTF_TRACK | TTF_TRANSPARENT ;
2580 ti.lpszText = "Test"; /* LPSTR_TEXTCALLBACK; */
2581 SetRectEmpty (&ti.rect);
2583 SendMessageA (infoPtr->hwndToolTip, TTM_ADDTOOLA, 0, (LPARAM)&ti);
2586 infoPtr->hwndEdit = CreateWindowExA (
2590 WS_CHILD | WS_BORDER | ES_AUTOHSCROLL |
2591 ES_WANTRETURN | ES_LEFT,
2594 0,0,0); /* FIXME: (HMENU)IDTVEDIT,pcs->hInstance,0);*/
2596 SendMessageA ( infoPtr->hwndEdit, WM_SETFONT, infoPtr->hFont, FALSE);
2597 infoPtr->wpEditOrig = (WNDPROC)SetWindowLongA (
2600 (LONG) TREEVIEW_Edit_SubclassProc);
2602 if (dwStyle & TVS_CHECKBOXES) {
2606 infoPtr->himlState =
2607 ImageList_Create (16, 16,ILC_COLOR|ILC_MASK, 15, 1);
2609 hbmLoad = LoadBitmapA (COMCTL32_hModule, MAKEINTRESOURCEA(IDT_CHECK));
2610 TRACE ("%x\n",hbmLoad);
2611 nIndex = ImageList_AddMasked (infoPtr->himlState, hbmLoad, CLR_DEFAULT);
2612 TRACE ("%d\n",nIndex);
2613 DeleteObject (hbmLoad);
2615 ReleaseDC (hwnd, hdc);
2622 TREEVIEW_Destroy (HWND hwnd)
2624 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
2627 TREEVIEW_RemoveTree (hwnd);
2628 SetWindowLongA (hwnd, 0, (DWORD)NULL);
2630 if (infoPtr->Timer & TV_REFRESH_TIMER_SET)
2631 KillTimer (hwnd, TV_REFRESH_TIMER);
2632 if (infoPtr->hwndToolTip)
2633 DestroyWindow (infoPtr->hwndToolTip);
2635 COMCTL32_Free (infoPtr);
2641 TREEVIEW_Paint (HWND hwnd, WPARAM wParam, LPARAM lParam)
2647 hdc = wParam==0 ? BeginPaint (hwnd, &ps) : (HDC)wParam;
2648 TREEVIEW_Refresh (hwnd, hdc);
2649 if(!wParam) EndPaint (hwnd, &ps);
2656 TREEVIEW_SetFocus (HWND hwnd, WPARAM wParam, LPARAM lParam)
2658 TREEVIEW_SendSimpleNotify (hwnd, NM_SETFOCUS);
2659 InvalidateRect(hwnd, NULL, FALSE);
2664 TREEVIEW_KillFocus (HWND hwnd, WPARAM wParam, LPARAM lParam)
2666 TREEVIEW_SendSimpleNotify (hwnd, NM_KILLFOCUS);
2667 InvalidateRect(hwnd, NULL, FALSE);
2672 TREEVIEW_EraseBackground (HWND hwnd, WPARAM wParam, LPARAM lParam)
2674 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
2675 HBRUSH hBrush = CreateSolidBrush (infoPtr->clrBk);
2679 GetClientRect (hwnd, &rect);
2680 FillRect ((HDC)wParam, &rect, hBrush);
2681 DeleteObject (hBrush);
2697 TREEVIEW_SendSimpleNotify (HWND hwnd, UINT code)
2702 nmhdr.hwndFrom = hwnd;
2703 nmhdr.idFrom = GetWindowLongA( hwnd, GWL_ID);
2706 return (BOOL) SendMessageA (GetParent (hwnd), WM_NOTIFY,
2707 (WPARAM)nmhdr.idFrom, (LPARAM)&nmhdr);
2713 TREEVIEW_SendTreeviewNotify (HWND hwnd, UINT code, UINT action,
2714 HTREEITEM oldItem, HTREEITEM newItem)
2717 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
2719 TREEVIEW_ITEM *wineItem;
2721 TRACE("code:%x action:%x olditem:%x newitem:%x\n",
2722 code,action,(INT)oldItem,(INT)newItem);
2723 nmhdr.hdr.hwndFrom = hwnd;
2724 nmhdr.hdr.idFrom = GetWindowLongA( hwnd, GWL_ID);
2725 nmhdr.hdr.code = code;
2726 nmhdr.action = action;
2728 wineItem=& infoPtr->items[(INT)oldItem];
2729 nmhdr.itemOld.mask = wineItem->mask;
2730 nmhdr.itemOld.hItem = wineItem->hItem;
2731 nmhdr.itemOld.state = wineItem->state;
2732 nmhdr.itemOld.stateMask = wineItem->stateMask;
2733 nmhdr.itemOld.iImage = wineItem->iImage;
2734 nmhdr.itemOld.pszText = wineItem->pszText;
2735 nmhdr.itemOld.cchTextMax= wineItem->cchTextMax;
2736 nmhdr.itemOld.iImage = wineItem->iImage;
2737 nmhdr.itemOld.iSelectedImage = wineItem->iSelectedImage;
2738 nmhdr.itemOld.cChildren = wineItem->cChildren;
2739 nmhdr.itemOld.lParam = wineItem->lParam;
2743 wineItem=& infoPtr->items[(INT)newItem];
2744 nmhdr.itemNew.mask = wineItem->mask;
2745 nmhdr.itemNew.hItem = wineItem->hItem;
2746 nmhdr.itemNew.state = wineItem->state;
2747 nmhdr.itemNew.stateMask = wineItem->stateMask;
2748 nmhdr.itemNew.iImage = wineItem->iImage;
2749 nmhdr.itemNew.pszText = wineItem->pszText;
2750 nmhdr.itemNew.cchTextMax= wineItem->cchTextMax;
2751 nmhdr.itemNew.iImage = wineItem->iImage;
2752 nmhdr.itemNew.iSelectedImage = wineItem->iSelectedImage;
2753 nmhdr.itemNew.cChildren = wineItem->cChildren;
2754 nmhdr.itemNew.lParam = wineItem->lParam;
2760 return (BOOL)SendMessageA (GetParent (hwnd), WM_NOTIFY,
2761 (WPARAM) GetWindowLongA( hwnd, GWL_ID), (LPARAM)&nmhdr);
2766 TREEVIEW_SendTreeviewDnDNotify (HWND hwnd, UINT code, HTREEITEM dragItem,
2769 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
2771 TREEVIEW_ITEM *wineItem;
2773 TRACE("code:%x dragitem:%x\n", code,(INT)dragItem);
2775 nmhdr.hdr.hwndFrom = hwnd;
2776 nmhdr.hdr.idFrom = GetWindowLongA( hwnd, GWL_ID);
2777 nmhdr.hdr.code = code;
2779 wineItem=& infoPtr->items[(INT)dragItem];
2780 nmhdr.itemNew.mask = wineItem->mask;
2781 nmhdr.itemNew.hItem = wineItem->hItem;
2782 nmhdr.itemNew.state = wineItem->state;
2783 nmhdr.itemNew.lParam = wineItem->lParam;
2785 nmhdr.ptDrag.x = pt.x;
2786 nmhdr.ptDrag.y = pt.y;
2788 return (BOOL)SendMessageA (GetParent (hwnd), WM_NOTIFY,
2789 (WPARAM) GetWindowLongA( hwnd, GWL_ID), (LPARAM)&nmhdr);
2796 TREEVIEW_SendDispInfoNotify (HWND hwnd, TREEVIEW_ITEM *wineItem,
2797 UINT code, UINT what)
2803 TRACE("item %d, action %x, state %d\n",
2804 (INT)wineItem->hItem,
2806 (INT)wineItem->state);
2808 tvdi.hdr.hwndFrom = hwnd;
2809 tvdi.hdr.idFrom = GetWindowLongA( hwnd, GWL_ID);
2810 tvdi.hdr.code = code;
2811 tvdi.item.mask = what;
2812 tvdi.item.hItem = wineItem->hItem;
2813 tvdi.item.state = wineItem->state;
2814 tvdi.item.lParam = wineItem->lParam;
2815 tvdi.item.pszText = COMCTL32_Alloc (128*sizeof(char));
2816 tvdi.item.cchTextMax = 128;
2817 buf = tvdi.item.pszText;
2819 retval=(BOOL)SendMessageA (
2822 (WPARAM)tvdi.hdr.idFrom,
2825 if (what & TVIF_TEXT) {
2826 wineItem->pszText = tvdi.item.pszText;
2827 if (buf==tvdi.item.pszText) {
2828 wineItem->cchTextMax = 128;
2830 TRACE("user-supplied buffer\n");
2831 COMCTL32_Free (buf);
2832 wineItem->cchTextMax = 0;
2835 if (what & TVIF_SELECTEDIMAGE)
2836 wineItem->iSelectedImage = tvdi.item.iSelectedImage;
2837 if (what & TVIF_IMAGE)
2838 wineItem->iImage = tvdi.item.iImage;
2839 if (what & TVIF_CHILDREN)
2840 wineItem->cChildren = tvdi.item.cChildren;
2848 TREEVIEW_SendCustomDrawNotify (HWND hwnd, DWORD dwDrawStage, HDC hdc,
2851 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
2852 NMTVCUSTOMDRAW nmcdhdr;
2853 LPNMCUSTOMDRAW nmcd;
2855 TRACE("drawstage:%lx hdc:%x\n", dwDrawStage, hdc);
2857 nmcd= & nmcdhdr.nmcd;
2858 nmcd->hdr.hwndFrom = hwnd;
2859 nmcd->hdr.idFrom = GetWindowLongA( hwnd, GWL_ID);
2860 nmcd->hdr.code = NM_CUSTOMDRAW;
2861 nmcd->dwDrawStage= dwDrawStage;
2863 nmcd->rc.left = rc.left;
2864 nmcd->rc.right = rc.right;
2865 nmcd->rc.bottom = rc.bottom;
2866 nmcd->rc.top = rc.top;
2867 nmcd->dwItemSpec = 0;
2868 nmcd->uItemState = 0;
2869 nmcd->lItemlParam= 0;
2870 nmcdhdr.clrText = infoPtr->clrText;
2871 nmcdhdr.clrTextBk= infoPtr->clrBk;
2874 return (BOOL)SendMessageA (GetParent (hwnd), WM_NOTIFY,
2875 (WPARAM) GetWindowLongA( hwnd, GWL_ID), (LPARAM)&nmcdhdr);
2881 /* FIXME: need to find out when the flags in uItemState need to be set */
2884 TREEVIEW_SendCustomDrawItemNotify (HWND hwnd, HDC hdc,
2885 TREEVIEW_ITEM *wineItem, UINT uItemDrawState)
2887 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
2888 NMTVCUSTOMDRAW nmcdhdr;
2889 LPNMCUSTOMDRAW nmcd;
2890 DWORD dwDrawStage,dwItemSpec;
2894 dwDrawStage=CDDS_ITEM | uItemDrawState;
2895 dwItemSpec=(DWORD)wineItem->hItem;
2897 if (wineItem->hItem==infoPtr->selectedItem) uItemState|=CDIS_SELECTED;
2898 if (wineItem->hItem==infoPtr->focusItem) uItemState|=CDIS_FOCUS;
2899 if (wineItem->hItem==infoPtr->hotItem) uItemState|=CDIS_HOT;
2901 nmcd= & nmcdhdr.nmcd;
2902 nmcd->hdr.hwndFrom = hwnd;
2903 nmcd->hdr.idFrom = GetWindowLongA( hwnd, GWL_ID);
2904 nmcd->hdr.code = NM_CUSTOMDRAW;
2905 nmcd->dwDrawStage= dwDrawStage;
2907 nmcd->rc.left = wineItem->rect.left;
2908 nmcd->rc.right = wineItem->rect.right;
2909 nmcd->rc.bottom = wineItem->rect.bottom;
2910 nmcd->rc.top = wineItem->rect.top;
2911 nmcd->dwItemSpec = dwItemSpec;
2912 nmcd->uItemState = uItemState;
2913 nmcd->lItemlParam= wineItem->lParam;
2914 nmcdhdr.clrText = infoPtr->clrText;
2915 nmcdhdr.clrTextBk= infoPtr->clrBk;
2916 nmcdhdr.iLevel = wineItem->iLevel;
2918 TRACE("drawstage:%lx hdc:%x item:%lx, itemstate:%x, lItemlParam:%lx\n",
2919 nmcd->dwDrawStage, nmcd->hdc, nmcd->dwItemSpec,
2920 nmcd->uItemState, nmcd->lItemlParam);
2922 retval=SendMessageA (GetParent (hwnd), WM_NOTIFY,
2923 (WPARAM) GetWindowLongA( hwnd, GWL_ID), (LPARAM)&nmcdhdr);
2925 infoPtr->clrText=nmcdhdr.clrText;
2926 infoPtr->clrBk =nmcdhdr.clrTextBk;
2927 return (BOOL) retval;
2932 /* Note:If the specified item is the child of a collapsed parent item,
2933 the parent's list of child items is (recursively) expanded to reveal the
2934 specified item. This is mentioned for TREEVIEW_SelectItem; don't
2935 know if it also applies here.
2939 TREEVIEW_Expand (HWND hwnd, WPARAM wParam, LPARAM lParam)
2941 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
2942 TREEVIEW_ITEM *wineItem;
2946 flag = (UINT) wParam;
2947 expand = (INT) lParam;
2949 wineItem = TREEVIEW_ValidItem (infoPtr, (HTREEITEM)expand);
2953 if (!wineItem->cChildren)
2956 if (wineItem->pszText==LPSTR_TEXTCALLBACKA)
2957 TRACE ("For item %d, flags %d, state %d\n",
2958 expand, flag, wineItem->state);
2960 TRACE("For (%s) item:%d, flags %x, state:%d\n",
2961 wineItem->pszText, flag, expand, wineItem->state);
2963 if (wineItem->cChildren==I_CHILDRENCALLBACK) {
2964 FIXME("we don't handle I_CHILDRENCALLBACK yet\n");
2968 if (flag == TVE_TOGGLE) { /* FIXME: check exact behaviour here */
2969 flag &= ~TVE_TOGGLE; /* ie: bitwise ops or 'case' ops */
2970 if (wineItem->state & TVIS_EXPANDED)
2971 flag |= TVE_COLLAPSE;
2978 case TVE_COLLAPSERESET:
2979 TRACE(" case TVE_COLLAPSERESET\n");
2980 if (!wineItem->state & TVIS_EXPANDED)
2983 wineItem->state &= ~(TVIS_EXPANDEDONCE | TVIS_EXPANDED);
2984 TREEVIEW_RemoveAllChildren (hwnd, wineItem);
2988 TRACE(" case TVE_COLLAPSE\n");
2989 if (!wineItem->state & TVIS_EXPANDED)
2992 wineItem->state &= ~TVIS_EXPANDED;
2996 TRACE(" case TVE_EXPAND\n");
2997 if (wineItem->state & TVIS_EXPANDED)
3000 TRACE(" is not expanded...\n");
3002 if (!(wineItem->state & TVIS_EXPANDEDONCE))
3004 TRACE(" and has never been expanded...\n");
3005 wineItem->state |= TVIS_EXPANDED;
3007 /* this item has never been expanded */
3008 if (TREEVIEW_SendTreeviewNotify (
3015 TRACE(" TVN_ITEMEXPANDINGA returned TRUE, exiting...\n");
3020 * Since the TVN_ITEMEXPANDINGA message may has caused the parent to
3021 * insert new items which in turn may have cause items placeholder
3022 * reallocation, I reassign the current item pointer so we have
3023 * something valid to work with...
3024 * However, this should not be necessary,
3025 * investigation required in TREEVIEW_InsertItemA
3027 wineItem = TREEVIEW_ValidItem (infoPtr, (HTREEITEM)expand);
3031 "Catastropic situation, cannot retreive item #%d\n",
3036 wineItem->state |= TVIS_EXPANDEDONCE;
3037 TRACE(" TVN_ITEMEXPANDINGA sent...\n");
3039 TREEVIEW_SendTreeviewNotify (
3046 TRACE(" TVN_ITEMEXPANDEDA sent...\n");
3051 /* this item has already been expanded */
3052 wineItem->state |= TVIS_EXPANDED;
3056 case TVE_EXPANDPARTIAL:
3057 TRACE(" case TVE_EXPANDPARTIAL\n");
3058 FIXME("TVE_EXPANDPARTIAL not implemented\n");
3059 wineItem->state ^=TVIS_EXPANDED;
3060 wineItem->state |=TVIS_EXPANDEDONCE;
3064 TRACE("Exiting, Item %d state is now %d...\n",
3068 TREEVIEW_QueueRefresh (hwnd);
3074 static TREEVIEW_ITEM *
3075 TREEVIEW_HitTestPoint (HWND hwnd, POINT pt)
3077 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
3078 TREEVIEW_ITEM *wineItem;
3081 GetClientRect (hwnd, &rect);
3083 if (!infoPtr->firstVisible) return NULL;
3085 wineItem=&infoPtr->items [(INT)infoPtr->firstVisible];
3087 while ((wineItem!=NULL) && (pt.y > wineItem->rect.bottom))
3088 wineItem=TREEVIEW_GetNextListItem (infoPtr,wineItem);
3100 TREEVIEW_HitTest (HWND hwnd, LPARAM lParam)
3102 LPTVHITTESTINFO lpht=(LPTVHITTESTINFO) lParam;
3103 TREEVIEW_ITEM *wineItem;
3107 GetClientRect (hwnd, &rect);
3111 if (x < rect.left) status|=TVHT_TOLEFT;
3112 if (x > rect.right) status|=TVHT_TORIGHT;
3113 if (y < rect.top ) status|=TVHT_ABOVE;
3114 if (y > rect.bottom) status|=TVHT_BELOW;
3121 wineItem=TREEVIEW_HitTestPoint (hwnd, lpht->pt);
3123 lpht->flags=TVHT_NOWHERE;
3129 if (x < wineItem->expandBox.left) {
3130 lpht->flags |= TVHT_ONITEMINDENT;
3133 if ( PtInRect ( &wineItem->expandBox, lpht->pt)) {
3134 lpht->flags |= TVHT_ONITEMBUTTON;
3137 if ( PtInRect ( &wineItem->bitmap, lpht->pt)) {
3138 lpht->flags |= TVHT_ONITEMICON;
3141 if ( PtInRect ( &wineItem->statebitmap, lpht->pt)) {
3142 lpht->flags |= TVHT_ONITEMSTATEICON;
3145 if ( PtInRect ( &wineItem->text, lpht->pt)) {
3146 lpht->flags |= TVHT_ONITEMLABEL;
3150 lpht->flags|=TVHT_ONITEMRIGHT;
3154 lpht->hItem=wineItem->hItem;
3155 TRACE ("(%ld,%ld):result %x\n",lpht->pt.x,lpht->pt.y,lpht->flags);
3157 return (LRESULT) wineItem->hItem;
3161 TREEVIEW_EditLabelA (HWND hwnd, WPARAM wParam, LPARAM lParam)
3163 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
3164 TREEVIEW_ITEM *wineItem;
3167 * If the style allow editing...
3169 if ( GetWindowLongA( hwnd, GWL_STYLE) & TVS_EDITLABELS )
3172 if ( infoPtr->editItem == 0 ) /* If we are not curently editing */
3174 wineItem = TREEVIEW_ValidItem(infoPtr,(HTREEITEM) lParam);
3175 if ( wineItem == NULL )
3177 ERR("Cannot get valid TREEVIEW_ITEM for lParam\n");
3181 TRACE("Edit started for %s.\n", wineItem->pszText);
3182 infoPtr->editItem = wineItem->hItem;
3186 * It is common practice for a windows program to get this
3187 * edit control and then subclass it. It is assumed that a
3188 * new edit control is given every time.
3190 * As a result some programs really mess up the edit control
3191 * so we need to destory our old edit control and create a new
3192 * one. Recycling would be nice but we would need to reset
3193 * everything. So recreating may just be easyier
3196 DestroyWindow(infoPtr->hwndEdit);
3197 infoPtr->hwndEdit = CreateWindowExA (
3201 WS_CHILD | WS_BORDER | ES_AUTOHSCROLL |
3202 ES_WANTRETURN | ES_LEFT,
3205 0,0,0); /* FIXME: (HMENU)IDTVEDIT,pcs->hInstance,0);*/
3210 (LONG) TREEVIEW_Edit_SubclassProc);
3213 SendMessageA ( infoPtr->hwndEdit, WM_SETFONT, infoPtr->hFont, FALSE);
3215 SetWindowTextA( infoPtr->hwndEdit, wineItem->pszText );
3216 SendMessageA ( infoPtr->hwndEdit, EM_SETSEL, 0, -1 );
3219 ** NOTE: this must be after the edit control is created
3220 ** (according to TVN_BEGINLABELEDITA docs), before position is set.
3222 if ( TREEVIEW_SendDispInfoNotify( /* Return true to cancel edition */
3225 TVN_BEGINLABELEDITA,
3229 ** FIXME: Is this right, should we return a handle even though edit was cancelled?
3231 TRACE("Edit cancelled by TVN_BEGINLABELEDITA for %s.\n", wineItem->pszText);
3233 TREEVIEW_EndEditLabelNow(hwnd, (WPARAM)TRUE, 0);
3241 wineItem->text.left - 2,
3242 wineItem->text.top - 1,
3243 wineItem->text.right - wineItem->text.left + 20 ,
3244 wineItem->text.bottom - wineItem->text.top + 3,
3247 SetFocus ( infoPtr->hwndEdit);
3248 ShowWindow ( infoPtr->hwndEdit, SW_SHOW);
3254 ** return NULL since we cannot edit this.
3260 return infoPtr->hwndEdit;
3264 TREEVIEW_EndEditLabelNow (HWND hwnd, WPARAM wParam, LPARAM lParam)
3266 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
3267 TREEVIEW_ITEM *editedItem = TREEVIEW_ValidItem (infoPtr, infoPtr->editItem);
3268 BOOL bRevert = (BOOL)wParam;
3276 ** NOTE: we have to get the new text before calling TVN_ENDLABELEDITA
3277 ** since some apps(eg regedit, win98) validate the text in TVN_ENDLABELEDITA.
3280 oldText = editedItem->pszText;
3282 if ( !bRevert ) /* wParam is set to true to cancel the edition */
3285 iLength = GetWindowTextLengthA( infoPtr->hwndEdit );
3288 WARN("Zero length string for new label(not changing).");
3292 newText = COMCTL32_Alloc( iLength+1 );
3293 if( newText == NULL )
3295 ERR("OutOfMemory, cannot allocate space for label");
3298 GetWindowTextA( infoPtr->hwndEdit, newText, iLength+1);
3307 * notify our parent with the new string(or NULL if wParam==TRUE)
3309 tvdi.hdr.hwndFrom = hwnd;
3310 tvdi.hdr.idFrom = GetWindowLongA( hwnd, GWL_ID);
3311 tvdi.hdr.code = TVN_ENDLABELEDITA;
3312 tvdi.item.hItem = editedItem->hItem;
3313 tvdi.item.lParam = editedItem->lParam;
3314 tvdi.item.mask = TVIF_TEXT|TVIF_HANDLE|TVIF_PARAM;
3315 tvdi.item.pszText = newText;
3317 if(!SendMessageA ( /* return false to cancel edition */
3320 (WPARAM)tvdi.hdr.idFrom,
3323 if( newText == NULL ) /*we are supposed to ignore the return if (and pszText==NULL), MSDOCs */
3327 if (oldText != LPSTR_TEXTCALLBACKA)
3332 if( newText != NULL )
3333 COMCTL32_Free(newText);
3335 editedItem->pszText=oldText; /* revert back to the old label */
3339 COMCTL32_Free(oldText);
3341 editedItem->pszText=newText; /* use the new label */
3344 else if( newText!=NULL )
3347 ** Is really this necessary? shouldnt an app update its internal data in TVN_ENDLABELEDITA?
3352 * This is a callback string so we need
3353 * to inform the parent that the string
3357 tvdi.hdr.hwndFrom = hwnd;
3358 tvdi.hdr.idFrom = GetWindowLongA( hwnd, GWL_ID);
3359 tvdi.hdr.code = TVN_SETDISPINFOA;
3360 tvdi.item.mask = TVIF_TEXT;
3361 tvdi.item.pszText = newText;
3366 (WPARAM)tvdi.hdr.idFrom,
3371 COMCTL32_Free(newText);
3375 ShowWindow(infoPtr->hwndEdit, SW_HIDE);
3376 EnableWindow(infoPtr->hwndEdit, FALSE);
3378 /* update the window to eliminate fragments and the like */
3379 TreeView_GetItemRect(hwnd,infoPtr->editItem,&itemRect,FALSE);
3380 RedrawWindow(hwnd,&itemRect,0,RDW_ERASE|RDW_INVALIDATE|RDW_UPDATENOW);
3382 infoPtr->editItem = 0;
3384 return !bRevert; /* return true if label edit succesful, otherwise false */
3390 TREEVIEW_LButtonDoubleClick (HWND hwnd, WPARAM wParam, LPARAM lParam)
3392 TREEVIEW_ITEM *wineItem;
3396 pt.x = (INT)LOWORD(lParam);
3397 pt.y = (INT)HIWORD(lParam);
3400 wineItem=TREEVIEW_HitTestPoint (hwnd, pt);
3401 if (!wineItem) return 0;
3402 TRACE("item %d \n",(INT)wineItem->hItem);
3404 if (TREEVIEW_SendSimpleNotify (hwnd, NM_DBLCLK)!=TRUE) { /* FIXME!*/
3405 TREEVIEW_Expand (hwnd, (WPARAM) TVE_TOGGLE, (LPARAM) wineItem->hItem);
3412 TREEVIEW_LButtonDown (HWND hwnd, WPARAM wParam, LPARAM lParam)
3414 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
3418 ht.pt.x = (INT)LOWORD(lParam);
3419 ht.pt.y = (INT)HIWORD(lParam);
3422 iItem=TREEVIEW_HitTest (hwnd, (LPARAM) &ht);
3423 TRACE("item %d \n",iItem);
3425 if (ht.flags & TVHT_ONITEMBUTTON) {
3426 TREEVIEW_Expand (hwnd, (WPARAM) TVE_TOGGLE, (LPARAM) iItem);
3430 infoPtr->uInternalStatus|=TV_LDRAG;
3437 TREEVIEW_LButtonUp (HWND hwnd, WPARAM wParam, LPARAM lParam)
3439 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
3441 TREEVIEW_ITEM *wineItem;
3443 DWORD dwStyle = GetWindowLongA (hwnd, GWL_STYLE);
3445 ht.pt.x = (INT)LOWORD(lParam);
3446 ht.pt.y = (INT)HIWORD(lParam);
3450 /* Return true to cancel default behaviour */
3451 if ( TREEVIEW_SendSimpleNotify (hwnd, NM_CLICK) )
3455 iItem = TREEVIEW_HitTest (hwnd, (LPARAM) &ht);
3456 TRACE ("%d\n",iItem);
3460 wineItem = TREEVIEW_ValidItem(infoPtr, (HTREEITEM)iItem);
3463 * if we are TVS_SINGLEEXPAND then we want this single click to
3464 * do a bunch of things.
3466 if ((dwStyle & TVS_SINGLEEXPAND)&&
3467 ( ht.flags & (TVHT_ONITEMLABEL | TVHT_ONITEMICON))&&
3468 ( infoPtr->editItem == 0 ))
3470 TREEVIEW_ITEM *SelItem;
3472 * Send the notification
3474 TREEVIEW_SendTreeviewNotify (hwnd,TVN_SINGLEEXPAND,0,
3475 (HTREEITEM)iItem,0);
3477 * Close the previous selection all the way to the root
3478 * as long as the new selection is not a child
3481 if ((infoPtr->selectedItem)&&(infoPtr->selectedItem != (HTREEITEM)iItem))
3483 BOOL closeit = TRUE;
3486 while (closeit && SelItem)
3488 closeit = (SelItem->hItem != infoPtr->selectedItem);
3489 SelItem = TREEVIEW_ValidItem(infoPtr,SelItem->parent);
3494 SelItem = TREEVIEW_ValidItem(infoPtr,infoPtr->selectedItem);
3495 while ((SelItem)&&(SelItem->hItem != wineItem->hItem))
3497 TREEVIEW_Expand (hwnd,(WPARAM)TVE_COLLAPSE,(LPARAM)SelItem->hItem);
3498 SelItem = TREEVIEW_ValidItem(infoPtr,SelItem->parent);
3504 * Expand the current item
3506 TREEVIEW_Expand (hwnd, (WPARAM) TVE_TOGGLE, (LPARAM) wineItem->hItem);
3509 infoPtr->uInternalStatus &= ~(TV_LDRAG | TV_LDRAGGING);
3512 * If the style allow editing and the node is already selected
3513 * and the click occured on the item label...
3515 if ( ( GetWindowLongA( hwnd, GWL_STYLE) & TVS_EDITLABELS ) &&
3516 ( wineItem->state & TVIS_SELECTED ) &&
3517 ( ht.flags & TVHT_ONITEMLABEL ))
3519 if ( infoPtr->editItem == 0 ) /* If we are not curently editing */
3521 if( SendMessageA(hwnd, TVM_EDITLABELA, 0, (LPARAM)iItem) == 0)
3525 else if ( infoPtr->editItem != 0 ) /* If we are curently editing */
3527 TREEVIEW_EndEditLabelNow(hwnd, (WPARAM)FALSE, 0);
3529 else if ( ht.flags & (TVHT_ONITEMLABEL | TVHT_ONITEMICON))
3531 TREEVIEW_DoSelectItem ( hwnd, TVGN_CARET, (HTREEITEM)iItem, TVC_BYMOUSE);
3534 if (ht.flags & TVHT_ONITEMSTATEICON) {
3537 if (dwStyle & TVS_CHECKBOXES) { /* TVS_CHECKBOXES requires _us_ */
3538 int state; /* to toggle the current state */
3539 state=1-(wineItem->state>>12);
3540 TRACE ("state:%x\n", state);
3541 wineItem->state&= ~TVIS_STATEIMAGEMASK;
3542 wineItem->state|=state<<12;
3543 TRACE ("state:%x\n", wineItem->state);
3544 TREEVIEW_QueueRefresh (hwnd);
3552 TREEVIEW_RButtonDown (HWND hwnd, WPARAM wParam, LPARAM lParam)
3554 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
3557 infoPtr->uInternalStatus|=TV_RDRAG;
3562 TREEVIEW_RButtonUp (HWND hwnd, WPARAM wParam, LPARAM lParam)
3564 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
3567 if (TREEVIEW_SendSimpleNotify (hwnd, NM_RCLICK)) return 0;
3568 infoPtr->uInternalStatus&= ~(TV_RDRAG | TV_RDRAGGING);
3574 TREEVIEW_MouseMove (HWND hwnd, WPARAM wParam, LPARAM lParam)
3576 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
3577 TREEVIEW_ITEM *hotItem;
3580 pt.x=(INT) LOWORD (lParam);
3581 pt.y=(INT) HIWORD (lParam);
3582 hotItem=TREEVIEW_HitTestPoint (hwnd, pt);
3583 if (!hotItem) return 0;
3584 infoPtr->focusItem=hotItem->hItem;
3586 if ( GetWindowLongA( hwnd, GWL_STYLE) & TVS_DISABLEDRAGDROP) return 0;
3588 if (infoPtr->uInternalStatus & TV_LDRAG) {
3589 TREEVIEW_SendTreeviewDnDNotify (hwnd, TVN_BEGINDRAGA, hotItem->hItem, pt);
3590 infoPtr->uInternalStatus &= ~TV_LDRAG;
3591 infoPtr->uInternalStatus |= TV_LDRAGGING;
3592 infoPtr->dropItem=hotItem->hItem;
3596 if (infoPtr->uInternalStatus & TV_RDRAG) {
3597 TREEVIEW_SendTreeviewDnDNotify (hwnd, TVN_BEGINRDRAGA, hotItem->hItem, pt);
3598 infoPtr->uInternalStatus &= ~TV_RDRAG;
3599 infoPtr->uInternalStatus |= TV_RDRAGGING;
3600 infoPtr->dropItem=hotItem->hItem;
3609 TREEVIEW_CreateDragImage (HWND hwnd, WPARAM wParam, LPARAM lParam)
3611 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
3612 TREEVIEW_ITEM *dragItem;
3616 HBITMAP hbmp,hOldbmp;
3623 if (!(infoPtr->himlNormal)) return 0;
3624 dragItem=TREEVIEW_ValidItem (infoPtr, (HTREEITEM) lParam);
3626 if (!dragItem) return 0;
3628 if (dragItem->pszText==LPSTR_TEXTCALLBACKA) {
3629 TREEVIEW_SendDispInfoNotify (hwnd, dragItem, TVN_GETDISPINFOA, TVIF_TEXT);
3631 itemtxt=dragItem->pszText;
3633 hwtop=GetDesktopWindow ();
3634 htopdc= GetDC (hwtop);
3635 hdc=CreateCompatibleDC (htopdc);
3637 hOldFont=SelectObject (hdc, infoPtr->hFont);
3638 GetTextExtentPoint32A (hdc, itemtxt, lstrlenA (itemtxt), &size);
3639 TRACE("%d %d %s %d\n",size.cx,size.cy,itemtxt,lstrlenA(itemtxt));
3640 hbmp=CreateCompatibleBitmap (htopdc, size.cx, size.cy);
3641 hOldbmp=SelectObject (hdc, hbmp);
3643 ImageList_GetIconSize (infoPtr->himlNormal, &cx, &cy);
3645 if (cy>size.cy) size.cy=cy;
3647 infoPtr->dragList=ImageList_Create (size.cx, size.cy, ILC_COLOR, 10, 10);
3648 ImageList_Draw (infoPtr->himlNormal, dragItem->iImage, hdc, 0, 0, ILD_NORMAL);
3651 ImageList_GetImageInfo (infoPtr->himlNormal, dragItem->hItem, &iminfo);
3652 ImageList_AddMasked (infoPtr->dragList, iminfo.hbmImage, CLR_DEFAULT);
3655 /* draw item text */
3657 SetRect (&rc, cx, 0, size.cx,size.cy);
3658 DrawTextA (hdc, itemtxt, lstrlenA (itemtxt), &rc, DT_LEFT);
3659 SelectObject (hdc, hOldFont);
3660 SelectObject (hdc, hOldbmp);
3662 ImageList_Add (infoPtr->dragList, hbmp, 0);
3665 DeleteObject (hbmp);
3666 ReleaseDC (hwtop, htopdc);
3668 return (LRESULT)infoPtr->dragList;
3673 TREEVIEW_DoSelectItem (HWND hwnd, INT action, HTREEITEM newSelect, INT cause)
3676 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
3677 TREEVIEW_ITEM *prevItem,*wineItem;
3680 wineItem = TREEVIEW_ValidItem (infoPtr, (HTREEITEM)newSelect);
3682 TRACE("Entering item %d, flag %x, cause %x, state %d\n",
3688 if ( (wineItem) && (wineItem->parent))
3691 * If the item has a collapse parent expand the parent so he
3692 * can expose the item
3694 TREEVIEW_ITEM *parentItem = TREEVIEW_ValidItem (infoPtr, wineItem->parent);
3695 if ( !(parentItem->state & TVIS_EXPANDED))
3696 TREEVIEW_Expand (hwnd, TVE_EXPAND, (LPARAM) wineItem->parent);
3702 prevSelect=(INT)infoPtr->selectedItem;
3704 if ((HTREEITEM)prevSelect==newSelect)
3707 prevItem= TREEVIEW_ValidItem (infoPtr, (HTREEITEM)prevSelect);
3710 if (TREEVIEW_SendTreeviewNotify(
3714 (HTREEITEM)prevSelect,
3715 (HTREEITEM)newSelect))
3716 return FALSE; /* FIXME: OK? */
3719 prevItem->state &= ~TVIS_SELECTED;
3721 wineItem->state |= TVIS_SELECTED;
3723 infoPtr->selectedItem=(HTREEITEM)newSelect;
3725 TREEVIEW_SendTreeviewNotify(
3729 (HTREEITEM)prevSelect,
3730 (HTREEITEM)newSelect);
3734 case TVGN_DROPHILITE:
3735 prevItem= TREEVIEW_ValidItem (infoPtr, infoPtr->dropItem);
3738 prevItem->state &= ~TVIS_DROPHILITED;
3740 infoPtr->dropItem=(HTREEITEM)newSelect;
3743 wineItem->state |=TVIS_DROPHILITED;
3747 case TVGN_FIRSTVISIBLE:
3748 FIXME("FIRSTVISIBLE not implemented\n");
3752 TREEVIEW_QueueRefresh (hwnd);
3754 TRACE("Leaving state %d\n", wineItem->state);
3758 /* FIXME: handle NM_KILLFocus etc */
3760 TREEVIEW_SelectItem (HWND hwnd, WPARAM wParam, LPARAM lParam)
3763 return TREEVIEW_DoSelectItem (hwnd, wParam, (HTREEITEM) lParam, TVC_UNKNOWN);
3770 TREEVIEW_GetFont (HWND hwnd, WPARAM wParam, LPARAM lParam)
3773 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
3775 TRACE("%x\n",infoPtr->hFont);
3776 return infoPtr->hFont;
3780 TREEVIEW_SetFont (HWND hwnd, WPARAM wParam, LPARAM lParam)
3783 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
3786 HFONT hFont, hOldFont;
3790 TRACE("%x %lx\n",wParam, lParam);
3792 infoPtr->hFont = (HFONT)wParam;
3794 hFont = infoPtr->hFont ? infoPtr->hFont : GetStockObject (SYSTEM_FONT);
3796 GetObjectA (infoPtr->hFont, sizeof (LOGFONTA), &logFont);
3797 logFont.lfWeight=FW_BOLD;
3798 infoPtr->hBoldFont = CreateFontIndirectA (&logFont);
3801 hOldFont = SelectObject (hdc, hFont);
3802 GetTextMetricsA (hdc, &tm);
3803 height= tm.tmHeight + tm.tmExternalLeading + FOCUS_BORDER;
3804 if (height>infoPtr->uRealItemHeight)
3805 infoPtr->uRealItemHeight=height;
3806 SelectObject (hdc, hOldFont);
3810 TREEVIEW_QueueRefresh (hwnd);
3818 TREEVIEW_VScroll (HWND hwnd, WPARAM wParam, LPARAM lParam)
3821 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
3825 TRACE("wp %x, lp %lx\n", wParam, lParam);
3826 if (!infoPtr->uInternalStatus & TV_VSCROLL) return FALSE;
3828 switch (LOWORD (wParam)) {
3830 if (!infoPtr->cy) return FALSE;
3831 infoPtr->cy -= infoPtr->uRealItemHeight;
3832 if (infoPtr->cy < 0) infoPtr->cy=0;
3835 nVisibleItems = infoPtr->uVisibleHeight / infoPtr->uRealItemHeight;
3836 maxHeight=infoPtr->uTotalHeight - nVisibleItems * infoPtr->uRealItemHeight;
3837 if (infoPtr->cy >= maxHeight) return FALSE;
3838 infoPtr->cy += infoPtr->uRealItemHeight;
3839 if (infoPtr->cy >= maxHeight)
3840 infoPtr->cy = maxHeight;
3843 if (!infoPtr->cy) return FALSE;
3844 infoPtr->cy -= infoPtr->uVisibleHeight;
3845 if (infoPtr->cy < 0) infoPtr->cy=0;
3848 nVisibleItems = infoPtr->uVisibleHeight / infoPtr->uRealItemHeight;
3849 maxHeight=infoPtr->uTotalHeight - nVisibleItems * infoPtr->uRealItemHeight;
3850 if (infoPtr->cy == maxHeight) return FALSE;
3851 infoPtr->cy += infoPtr->uVisibleHeight;
3852 if (infoPtr->cy >= maxHeight)
3853 infoPtr->cy = maxHeight;
3856 infoPtr->cy = HIWORD (wParam);
3861 TREEVIEW_QueueRefresh (hwnd);
3866 TREEVIEW_HScroll (HWND hwnd, WPARAM wParam, LPARAM lParam)
3868 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
3871 TRACE("wp %lx, lp %x\n", lParam, wParam);
3873 if (!infoPtr->uInternalStatus & TV_HSCROLL) return FALSE;
3875 switch (LOWORD (wParam)) {
3877 if (!infoPtr->cx) return FALSE;
3878 infoPtr->cx -= infoPtr->uRealItemHeight;
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->uRealItemHeight; /*FIXME */
3885 if (infoPtr->cx > maxWidth)
3886 infoPtr->cx = maxWidth;
3889 if (!infoPtr->cx) return FALSE;
3890 infoPtr->cx -= infoPtr->uVisibleWidth;
3891 if (infoPtr->cx < 0) infoPtr->cx=0;
3894 maxWidth=infoPtr->uTotalWidth-infoPtr->uVisibleWidth;
3895 if (infoPtr->cx == maxWidth) return FALSE;
3896 infoPtr->cx += infoPtr->uVisibleWidth;
3897 if (infoPtr->cx > maxWidth)
3898 infoPtr->cx = maxWidth;
3901 infoPtr->cx = HIWORD (wParam);
3906 TREEVIEW_QueueRefresh (hwnd);
3910 static LRESULT TREEVIEW_MouseWheel (HWND hwnd, WPARAM wParam, LPARAM lParam)
3913 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
3914 short gcWheelDelta = 0;
3915 UINT pulScrollLines = 3;
3917 SystemParametersInfoW(SPI_GETWHEELSCROLLLINES,0, &pulScrollLines, 0);
3919 gcWheelDelta -= (short) HIWORD(wParam);
3920 pulScrollLines *= (gcWheelDelta / WHEEL_DELTA);
3922 if (abs(gcWheelDelta) >= WHEEL_DELTA && pulScrollLines)
3924 int wheelDy = pulScrollLines * infoPtr->uRealItemHeight;
3925 int newDy = infoPtr->cy + wheelDy;
3926 int maxDy = infoPtr->uTotalHeight - infoPtr->uVisibleHeight;
3928 if (newDy > maxDy) newDy = maxDy;
3929 if (newDy < 0) newDy = 0;
3931 if (newDy == infoPtr->cy) return TRUE;
3933 TREEVIEW_VScroll(hwnd, MAKEWPARAM(SB_THUMBTRACK,newDy),0);
3939 TREEVIEW_KeyDown (HWND hwnd, WPARAM wParam, LPARAM lParam)
3941 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
3942 HTREEITEM hNewSelection = 0;
3943 INT scrollNeeds = -1;
3944 INT cyChangeNeeds = -1;
3945 INT prevSelect = (INT)infoPtr->selectedItem;
3947 TREEVIEW_ITEM *prevItem =
3948 (prevSelect != 0 ) ?
3949 TREEVIEW_ValidItem (infoPtr, (HTREEITEM)prevSelect) :
3952 TREEVIEW_ITEM *newItem = NULL;
3954 TRACE("%x %lx\n",wParam, lParam);
3956 if (prevSelect == 0)
3961 newItem=TREEVIEW_GetPrevListItem (infoPtr, prevItem);
3964 newItem=& infoPtr->items[(INT)infoPtr->TopRootItem];
3966 hNewSelection = newItem->hItem;
3968 if (! newItem->visible)
3969 scrollNeeds = SB_LINEUP;
3973 newItem=TREEVIEW_GetNextListItem (infoPtr, prevItem);
3978 hNewSelection = newItem->hItem;
3980 if (! newItem->visible)
3981 scrollNeeds = SB_LINEDOWN;
3986 newItem = &infoPtr->items[(INT)infoPtr->TopRootItem];
3987 hNewSelection = newItem->hItem;
3992 newItem = &infoPtr->items[(INT)infoPtr->TopRootItem];
3993 newItem = TREEVIEW_GetLastListItem (infoPtr, newItem);
3994 hNewSelection = newItem->hItem;
3996 if (! newItem->visible)
3997 cyChangeNeeds = infoPtr->uTotalHeight-infoPtr->uVisibleHeight;
4002 if ( (prevItem->cChildren > 0) && (prevItem->state & TVIS_EXPANDED) )
4004 TREEVIEW_Expand(hwnd, TVE_COLLAPSE, prevSelect );
4006 else if ((INT)prevItem->parent)
4008 newItem = (& infoPtr->items[(INT)prevItem->parent]);
4009 if (! newItem->visible)
4010 /* FIXME find a way to make this item the first visible... */
4013 hNewSelection = newItem->hItem;
4019 if ( ( prevItem->cChildren > 0) ||
4020 ( prevItem->cChildren == I_CHILDRENCALLBACK))
4022 if (! (prevItem->state & TVIS_EXPANDED))
4023 TREEVIEW_Expand(hwnd, TVE_EXPAND, prevSelect );
4026 newItem = (& infoPtr->items[(INT)prevItem->firstChild]);
4027 hNewSelection = newItem->hItem;
4034 if (! (prevItem->state & TVIS_EXPANDED))
4035 TREEVIEW_Expand(hwnd, TVE_EXPAND, prevSelect );
4039 if (prevItem->state & TVIS_EXPANDED)
4040 TREEVIEW_Expand(hwnd, TVE_COLLAPSE, prevSelect );
4045 newItem=TREEVIEW_GetListItem(
4048 -1*(TREEVIEW_GetVisibleCount(hwnd,0,0)-3));
4052 hNewSelection = newItem->hItem;
4054 if (! newItem->visible)
4055 scrollNeeds = SB_PAGEUP;
4060 newItem=TREEVIEW_GetListItem(
4063 TREEVIEW_GetVisibleCount(hwnd,0,0)-3);
4068 hNewSelection = newItem->hItem;
4070 if (! newItem->visible)
4071 scrollNeeds = SB_PAGEDOWN;
4080 FIXME("%x not implemented\n", wParam);
4087 This works but does not send notification...
4089 prevItem->state &= ~TVIS_SELECTED;
4090 newItem->state |= TVIS_SELECTED;
4091 infoPtr->selectedItem = hNewSelection;
4092 TREEVIEW_QueueRefresh (hwnd);
4095 if ( TREEVIEW_DoSelectItem(
4098 (HTREEITEM)hNewSelection,
4101 /* If selection change is allowed for the new item, perform scrolling */
4102 if (scrollNeeds != -1)
4103 TREEVIEW_VScroll(hwnd, scrollNeeds, 0);
4105 if (cyChangeNeeds != -1)
4106 infoPtr->cy = cyChangeNeeds;
4108 /* FIXME: Something happen in the load the in the two weeks before
4109 april 1st 1999 which makes this SetFocus mandatory otherwise, the focus
4110 is lost... However the SetFocus should not be required...*/
4121 TREEVIEW_GetScrollTime (HWND hwnd)
4123 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
4125 return infoPtr->uScrollTime;
4130 TREEVIEW_SetScrollTime (HWND hwnd, UINT uScrollTime)
4132 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
4133 UINT uOldScrollTime = infoPtr->uScrollTime;
4135 infoPtr->uScrollTime = min (uScrollTime, 100);
4137 return uOldScrollTime;
4141 static LRESULT WINAPI
4142 TREEVIEW_WindowProc (HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
4144 TREEVIEW_INFO *infoPtr;
4145 if (uMsg==WM_CREATE)
4146 return TREEVIEW_Create (hwnd, wParam, lParam);
4148 if (!(infoPtr = TREEVIEW_GetInfoPtr(hwnd)))
4149 return DefWindowProcA (hwnd, uMsg, wParam, lParam);
4153 case TVM_INSERTITEMA:
4154 return TREEVIEW_InsertItemA (hwnd, wParam, lParam);
4156 case TVM_INSERTITEMW:
4157 return TREEVIEW_InsertItemW(hwnd,wParam,lParam);;
4159 case TVM_DELETEITEM:
4160 return TREEVIEW_DeleteItem (hwnd, wParam, lParam);
4163 return TREEVIEW_Expand (hwnd, wParam, lParam);
4165 case TVM_GETITEMRECT:
4166 return TREEVIEW_GetItemRect (hwnd, wParam, lParam);
4169 return TREEVIEW_GetCount (hwnd, wParam, lParam);
4172 return TREEVIEW_GetIndent (hwnd);
4175 return TREEVIEW_SetIndent (hwnd, wParam);
4177 case TVM_GETIMAGELIST:
4178 return TREEVIEW_GetImageList (hwnd, wParam, lParam);
4180 case TVM_SETIMAGELIST:
4181 return TREEVIEW_SetImageList (hwnd, wParam, lParam);
4183 case TVM_GETNEXTITEM:
4184 return TREEVIEW_GetNextItem (hwnd, wParam, lParam);
4186 case TVM_SELECTITEM:
4187 return TREEVIEW_SelectItem (hwnd, wParam, lParam);
4190 return TREEVIEW_GetItemA (hwnd, wParam, lParam);
4193 return TREEVIEW_GetItemW (hwnd, wParam, lParam);
4196 return TREEVIEW_SetItemA (hwnd, wParam, lParam);
4199 FIXME("Unimplemented msg TVM_SETITEMW\n");
4202 case TVM_EDITLABELA:
4203 return TREEVIEW_EditLabelA(hwnd, wParam, lParam);
4205 case TVM_EDITLABELW:
4206 FIXME("Unimplemented msg TVM_EDITLABELW \n");
4209 case TVM_GETEDITCONTROL:
4210 return TREEVIEW_GetEditControl (hwnd);
4212 case TVM_GETVISIBLECOUNT:
4213 return TREEVIEW_GetVisibleCount (hwnd, wParam, lParam);
4216 return TREEVIEW_HitTest (hwnd, lParam);
4218 case TVM_CREATEDRAGIMAGE:
4219 return TREEVIEW_CreateDragImage (hwnd, wParam, lParam);
4221 case TVM_SORTCHILDREN:
4222 return TREEVIEW_SortChildren (hwnd, wParam, lParam);
4224 case TVM_ENSUREVISIBLE:
4225 FIXME("Unimplemented msg TVM_ENSUREVISIBLE\n");
4228 case TVM_SORTCHILDRENCB:
4229 return TREEVIEW_SortChildrenCB(hwnd, wParam, lParam);
4231 case TVM_ENDEDITLABELNOW:
4232 if (infoPtr->editItem)
4233 return TREEVIEW_EndEditLabelNow (hwnd, wParam, lParam);
4235 case TVM_GETISEARCHSTRINGA:
4236 FIXME("Unimplemented msg TVM_GETISEARCHSTRINGA\n");
4239 case TVM_GETISEARCHSTRINGW:
4240 FIXME("Unimplemented msg TVM_GETISEARCHSTRINGW\n");
4243 case TVM_GETTOOLTIPS:
4244 return TREEVIEW_GetToolTips (hwnd);
4246 case TVM_SETTOOLTIPS:
4247 return TREEVIEW_SetToolTips (hwnd, wParam);
4249 case TVM_SETINSERTMARK:
4250 return TREEVIEW_SetInsertMark (hwnd,wParam, lParam);
4252 case TVM_SETITEMHEIGHT:
4253 return TREEVIEW_SetItemHeight (hwnd, wParam);
4255 case TVM_GETITEMHEIGHT:
4256 return TREEVIEW_GetItemHeight (hwnd);
4258 case TVM_SETBKCOLOR:
4259 return TREEVIEW_SetBkColor (hwnd, wParam, lParam);
4261 case TVM_SETTEXTCOLOR:
4262 return TREEVIEW_SetTextColor (hwnd, wParam, lParam);
4264 case TVM_GETBKCOLOR:
4265 return TREEVIEW_GetBkColor (hwnd);
4267 case TVM_GETTEXTCOLOR:
4268 return TREEVIEW_GetTextColor (hwnd);
4270 case TVM_SETSCROLLTIME:
4271 return TREEVIEW_SetScrollTime (hwnd, (UINT)wParam);
4273 case TVM_GETSCROLLTIME:
4274 return TREEVIEW_GetScrollTime (hwnd);
4276 case TVM_GETITEMSTATE:
4277 return TREEVIEW_GetItemState (hwnd,wParam, lParam);
4279 case TVM_GETLINECOLOR:
4280 return TREEVIEW_GetLineColor (hwnd,wParam, lParam);
4282 case TVM_SETLINECOLOR:
4283 return TREEVIEW_SetLineColor (hwnd,wParam, lParam);
4285 case TVM_SETINSERTMARKCOLOR:
4286 return TREEVIEW_SetInsertMarkColor (hwnd,wParam, lParam);
4288 case TVM_GETINSERTMARKCOLOR:
4289 return TREEVIEW_GetInsertMarkColor (hwnd,wParam, lParam);
4291 case TVM_SETUNICODEFORMAT:
4292 FIXME("Unimplemented msg TVM_SETUNICODEFORMAT\n");
4295 case TVM_GETUNICODEFORMAT:
4296 FIXME("Unimplemented msg TVM_GETUNICODEFORMAT\n");
4300 return TREEVIEW_Command (hwnd, wParam, lParam);
4303 return TREEVIEW_Destroy (hwnd);
4305 /* case WM_ENABLE: */
4308 return TREEVIEW_EraseBackground (hwnd, wParam, lParam);
4311 return DLGC_WANTARROWS | DLGC_WANTCHARS;
4314 return TREEVIEW_Paint (hwnd, wParam, lParam);
4317 return TREEVIEW_GetFont (hwnd, wParam, lParam);
4320 return TREEVIEW_SetFont (hwnd, wParam, lParam);
4323 return TREEVIEW_KeyDown (hwnd, wParam, lParam);
4326 return TREEVIEW_SetFocus (hwnd, wParam, lParam);
4329 return TREEVIEW_KillFocus (hwnd, wParam, lParam);
4331 case WM_LBUTTONDOWN:
4332 return TREEVIEW_LButtonDown (hwnd, wParam, lParam);
4335 return TREEVIEW_LButtonUp (hwnd, wParam, lParam);
4337 case WM_LBUTTONDBLCLK:
4338 return TREEVIEW_LButtonDoubleClick (hwnd, wParam, lParam);
4340 case WM_RBUTTONDOWN:
4341 return TREEVIEW_RButtonDown (hwnd, wParam, lParam);
4344 return TREEVIEW_RButtonUp (hwnd, wParam, lParam);
4347 return TREEVIEW_MouseMove (hwnd, wParam, lParam);
4349 case WM_STYLECHANGED:
4350 return TREEVIEW_StyleChanged (hwnd, wParam, lParam);
4352 /* case WM_SYSCOLORCHANGE: */
4353 /* case WM_SETREDRAW: */
4356 return TREEVIEW_HandleTimer (hwnd, wParam, lParam);
4359 return TREEVIEW_Size (hwnd, wParam,lParam);
4362 return TREEVIEW_HScroll (hwnd, wParam, lParam);
4364 return TREEVIEW_VScroll (hwnd, wParam, lParam);
4367 if (wParam & (MK_SHIFT | MK_CONTROL))
4368 return DefWindowProcA( hwnd, uMsg, wParam, lParam );
4369 return TREEVIEW_MouseWheel (hwnd, wParam, lParam);
4372 TRACE ("drawItem\n");
4373 return DefWindowProcA (hwnd, uMsg, wParam, lParam);
4376 if (uMsg >= WM_USER)
4377 FIXME("Unknown msg %04x wp=%08x lp=%08lx\n",
4378 uMsg, wParam, lParam);
4379 return DefWindowProcA (hwnd, uMsg, wParam, lParam);
4386 TREEVIEW_Register (void)
4392 ZeroMemory (&wndClass, sizeof(WNDCLASSA));
4393 wndClass.style = CS_GLOBALCLASS | CS_DBLCLKS;
4394 wndClass.lpfnWndProc = (WNDPROC)TREEVIEW_WindowProc;
4395 wndClass.cbClsExtra = 0;
4396 wndClass.cbWndExtra = sizeof(TREEVIEW_INFO *);
4397 wndClass.hCursor = LoadCursorA (0, IDC_ARROWA);
4398 wndClass.hbrBackground = 0;
4399 wndClass.lpszClassName = WC_TREEVIEWA;
4401 RegisterClassA (&wndClass);
4406 TREEVIEW_Unregister (void)
4408 UnregisterClassA (WC_TREEVIEWA, (HINSTANCE)NULL);