3 * Copyright 1998 Eric Kohl <ekohl@abo.rhein-zeitung.de>
4 * Copyright 1998,1999 Alex Priem <alexp@sci.kun.nl>
5 * Copyright 1999 Sylvain St-Germain
9 * Using DPA to store the item ptr would be good.
10 * Node label edition is implemented but something happened in wine in the
11 * two last weeks of march 99 that broke it.
13 -small array containing info about positions.
14 -better implementation of RefreshItem:
15 1) draw lines between parents
17 3) draw lines from parent<->items.
18 -implement partial drawing?
19 * -drag&drop: TVM_CREATEDRAGIMAGE should create drag bitmap.
20 * -scrollbars: horizontal scrollbar doesn't work.
24 * FIXME: check fontsize. (uRealItemHeight)
25 * test focusItem (redraw in different color)
28 better implementation.
29 * WM_HSCROLL is broken.
30 * use separate routine to get item text/image.
32 * Separate drawing/calculation.
34 * FIXMEs (for personal use)
35 Expand: -ctlmacro expands twice ->toggle.
36 -DblClick: ctlmacro.exe's NM_DBLCLK seems to go wrong (returns FALSE).
37 -treehelper: stack corruption makes big window.
48 #include "debugtools.h"
50 DEFAULT_DEBUG_CHANNEL(treeview)
52 /* ffs should be in <string.h>. */
54 /* Defines, since they do not need to return previous state, and nr
55 * has no side effects in this file.
57 #define tv_test_bit(nr,bf) (((LPBYTE)bf)[nr>>3]&(1<<(nr&7)))
58 #define tv_set_bit(nr,bf) ((LPBYTE)bf)[nr>>3]|=(1<<(nr&7))
59 #define tv_clear_bit(nr,bf) ((LPBYTE)bf)[nr>>3]&=~(1<<(nr&7))
62 #define TREEVIEW_GetInfoPtr(hwnd) \
63 ((TREEVIEW_INFO *) GetWindowLongA( hwnd, 0))
66 TREEVIEW_SendSimpleNotify (HWND hwnd, UINT code);
68 TREEVIEW_SendTreeviewNotify (HWND hwnd, UINT code, UINT action,
69 HTREEITEM oldItem, HTREEITEM newItem);
71 TREEVIEW_SendTreeviewDnDNotify (HWND hwnd, UINT code, HTREEITEM dragItem,
74 TREEVIEW_SendDispInfoNotify (HWND hwnd, TREEVIEW_ITEM *wineItem,
75 UINT code, UINT what);
77 TREEVIEW_SendCustomDrawNotify (HWND hwnd, DWORD dwDrawStage, HDC hdc,
80 TREEVIEW_SendCustomDrawItemNotify (HWND hwnd, HDC hdc,
81 TREEVIEW_ITEM *tvItem, UINT uItemDrawState);
83 TREEVIEW_DoSelectItem (HWND hwnd, INT action, HTREEITEM newSelect, INT cause);
85 TREEVIEW_Refresh (HWND hwnd, HDC hdc);
87 static LRESULT CALLBACK
88 TREEVIEW_Edit_SubclassProc (HWND hwnd, UINT uMsg, WPARAM wParam,
92 TREEVIEW_EditLabelA (HWND hwnd, WPARAM wParam, LPARAM lParam);
95 TREEVIEW_EndEditLabelNow (HWND hwnd, WPARAM wParam, LPARAM lParam);
100 /* helper functions. Work with the assumption that validity of operands
101 is checked beforehand, and that tree state is valid. */
103 /* FIXME: MS documentation says `GetNextVisibleItem' returns NULL
104 if not successfull. Probably only applies to dereferencing infoPtr
105 (i.e. we are offered a valid treeview structure)
106 and not whether there is a next `visible' child.
107 FIXME: check other failures.
110 /***************************************************************************
111 * This method returns the TREEVIEW_ITEM object given the handle
113 static TREEVIEW_ITEM* TREEVIEW_ValidItem(
114 TREEVIEW_INFO *infoPtr,
117 if ((!handle) || (handle>infoPtr->uMaxHandle))
120 if (tv_test_bit ((INT)handle, infoPtr->freeList))
123 return &infoPtr->items[(INT)handle];
126 /***************************************************************************
127 * This method returns the last expanded child item of a tree node
129 static TREEVIEW_ITEM *TREEVIEW_GetLastListItem(
130 TREEVIEW_INFO *infoPtr,
131 TREEVIEW_ITEM *tvItem)
133 TREEVIEW_ITEM *wineItem = tvItem;
136 * Get this item last sibling
138 while (wineItem->sibling)
139 wineItem=& infoPtr->items [(INT)wineItem->sibling];
142 * If the last sibling has expanded children, restart.
144 if ( ( wineItem->cChildren > 0 ) && ( wineItem->state & TVIS_EXPANDED) )
145 return TREEVIEW_GetLastListItem(
147 &(infoPtr->items[(INT)wineItem->firstChild]));
152 /***************************************************************************
153 * This method returns the previous physical item in the list not
154 * considering the tree hierarchy.
156 static TREEVIEW_ITEM *TREEVIEW_GetPrevListItem(
157 TREEVIEW_INFO *infoPtr,
158 TREEVIEW_ITEM *tvItem)
160 if (tvItem->upsibling)
163 * This item has a upsibling, get the last item. Since, GetLastListItem
164 * first looks at siblings, we must feed it with the first child.
166 TREEVIEW_ITEM *upItem = &infoPtr->items[(INT)tvItem->upsibling];
168 if ( ( upItem->cChildren > 0 ) && ( upItem->state & TVIS_EXPANDED) )
169 return TREEVIEW_GetLastListItem(
171 &infoPtr->items[(INT)upItem->firstChild]);
178 * this item does not have a upsibling, get the parent
181 return &infoPtr->items[(INT)tvItem->parent];
188 /***************************************************************************
189 * This method returns the next physical item in the treeview not
190 * considering the tree hierarchy.
192 static TREEVIEW_ITEM *TREEVIEW_GetNextListItem(
193 TREEVIEW_INFO *infoPtr,
194 TREEVIEW_ITEM *tvItem)
196 TREEVIEW_ITEM *wineItem = NULL;
199 * If this item has children and is expanded, return the first child
201 if ((tvItem->firstChild) && (tvItem->state & TVIS_EXPANDED))
202 return (& infoPtr->items[(INT)tvItem->firstChild]);
206 * try to get the sibling
209 return (& infoPtr->items[(INT)tvItem->sibling]);
212 * Otherwise, get the parent's sibling.
215 while (wineItem->parent) {
216 wineItem=& infoPtr->items [(INT)wineItem->parent];
217 if (wineItem->sibling)
218 return (& infoPtr->items [(INT)wineItem->sibling]);
224 /***************************************************************************
225 * This method returns the nth item starting at the given item. It returns
226 * the last item (or first) we we run out of items.
228 * Will scroll backward if count is <0.
229 * forward if count is >0.
231 static TREEVIEW_ITEM *TREEVIEW_GetListItem(
232 TREEVIEW_INFO *infoPtr,
233 TREEVIEW_ITEM *tvItem,
236 TREEVIEW_ITEM *previousItem = NULL;
237 TREEVIEW_ITEM *wineItem = tvItem;
242 /* Find count item downward */
243 while ((iter++ < count) && (wineItem != NULL))
245 /* Keep a pointer to the previous in case we ask for more than we got */
246 previousItem = wineItem;
247 wineItem = TREEVIEW_GetNextListItem(infoPtr, wineItem);
250 if (wineItem == NULL)
251 wineItem = previousItem;
255 /* Find count item upward */
256 while ((iter-- > count) && (wineItem != NULL))
258 /* Keep a pointer to the previous in case we ask for more than we got */
259 previousItem = wineItem;
260 wineItem = TREEVIEW_GetPrevListItem(infoPtr, wineItem);
263 if (wineItem == NULL)
264 wineItem = previousItem;
273 /***************************************************************************
276 static void TREEVIEW_RemoveAllChildren(
278 TREEVIEW_ITEM *parentItem)
280 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
281 TREEVIEW_ITEM *killItem;
284 kill=(INT)parentItem->firstChild;
286 tv_set_bit ( kill, infoPtr->freeList);
287 killItem=& infoPtr->items[kill];
288 if (killItem->pszText!=LPSTR_TEXTCALLBACKA)
289 COMCTL32_Free (killItem->pszText);
290 TREEVIEW_SendTreeviewNotify (hwnd, TVN_DELETEITEMA, 0, (HTREEITEM)kill, 0);
291 if (killItem->firstChild)
292 TREEVIEW_RemoveAllChildren (hwnd, killItem);
293 kill=(INT)killItem->sibling;
296 if (parentItem->cChildren>0) {
297 infoPtr->uNumItems -= parentItem->cChildren;
298 parentItem->firstChild = 0;
299 parentItem->cChildren = 0;
306 TREEVIEW_RemoveItem (HWND hwnd, TREEVIEW_ITEM *wineItem)
309 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
310 TREEVIEW_ITEM *parentItem, *upsiblingItem, *siblingItem;
313 iItem=(INT)wineItem->hItem;
314 tv_set_bit(iItem,infoPtr->freeList);
315 infoPtr->uNumItems--;
317 if (wineItem->pszText!=LPSTR_TEXTCALLBACKA)
318 COMCTL32_Free (wineItem->pszText);
320 TREEVIEW_SendTreeviewNotify (hwnd, TVN_DELETEITEMA, 0, (HTREEITEM)iItem, 0);
322 if (wineItem->firstChild)
323 TREEVIEW_RemoveAllChildren (hwnd,wineItem);
325 if (wineItem->parent) {
326 parentItem=& infoPtr->items [(INT)wineItem->parent];
327 switch (parentItem->cChildren) {
328 case I_CHILDRENCALLBACK:
329 FIXME("we don't handle I_CHILDRENCALLBACK yet\n");
332 parentItem->cChildren=0;
333 parentItem->firstChild=0;
336 parentItem->cChildren--;
337 if ((INT)parentItem->firstChild==iItem)
338 parentItem->firstChild=wineItem->sibling;
342 if (iItem==(INT)infoPtr->TopRootItem)
343 infoPtr->TopRootItem=(HTREEITEM)wineItem->sibling;
344 if (wineItem->upsibling) {
345 upsiblingItem=& infoPtr->items [(INT)wineItem->upsibling];
346 upsiblingItem->sibling=wineItem->sibling;
348 if (wineItem->sibling) {
349 siblingItem=& infoPtr->items [(INT)wineItem->sibling];
350 siblingItem->upsibling=wineItem->upsibling;
358 /* Note:TREEVIEW_RemoveTree doesn't remove infoPtr itself */
360 static void TREEVIEW_RemoveTree (HWND hwnd)
363 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
364 TREEVIEW_ITEM *killItem;
367 for (i = 1; i <= (INT)infoPtr->uMaxHandle; i++)
368 if (!tv_test_bit (i, infoPtr->freeList)) {
369 killItem = &infoPtr->items[i];
370 if (killItem->pszText != LPSTR_TEXTCALLBACKA)
371 COMCTL32_Free (killItem->pszText);
372 TREEVIEW_SendTreeviewNotify(hwnd, TVN_DELETEITEMA, 0,
375 if (infoPtr->uNumPtrsAlloced) {
376 COMCTL32_Free (infoPtr->items);
377 COMCTL32_Free (infoPtr->freeList);
378 infoPtr->uNumItems = 0;
379 infoPtr->uNumPtrsAlloced = 0;
380 infoPtr->uMaxHandle = 0;
381 infoPtr->TopRootItem = 0;
392 TREEVIEW_GetImageList (HWND hwnd, WPARAM wParam, LPARAM lParam)
394 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
398 if ((INT)wParam == TVSIL_NORMAL)
399 return (LRESULT) infoPtr->himlNormal;
400 if ((INT)wParam == TVSIL_STATE)
401 return (LRESULT) infoPtr->himlState;
407 TREEVIEW_SetImageList (HWND hwnd, WPARAM wParam, LPARAM lParam)
409 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
412 TRACE("%x,%lx\n", wParam, lParam);
413 switch ((INT)wParam) {
415 himlTemp = infoPtr->himlNormal;
416 infoPtr->himlNormal = (HIMAGELIST)lParam;
417 return (LRESULT)himlTemp;
420 himlTemp = infoPtr->himlState;
421 infoPtr->himlState = (HIMAGELIST)lParam;
422 return (LRESULT)himlTemp;
425 return (LRESULT)NULL;
431 TREEVIEW_SetItemHeight (HWND hwnd, WPARAM wParam)
433 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
434 INT cx,cy,prevHeight=infoPtr->uItemHeight;
438 infoPtr->uItemHeight=-1;
442 ImageList_GetIconSize (infoPtr->himlNormal, &cx, &cy);
444 if (wParam>cy) cy=wParam;
445 infoPtr->uItemHeight=cy;
447 if (!( GetWindowLongA( hwnd, GWL_STYLE) & TVS_NONEVENHEIGHT))
448 infoPtr->uItemHeight = (INT) wParam & 0xfffffffe;
453 TREEVIEW_GetItemHeight (HWND hwnd)
455 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
458 return infoPtr->uItemHeight;
462 TREEVIEW_GetLineColor (HWND hwnd, WPARAM wParam, LPARAM lParam)
464 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
467 return (LRESULT) infoPtr->clrLine;
471 TREEVIEW_SetLineColor (HWND hwnd, WPARAM wParam, LPARAM lParam)
473 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
474 COLORREF prevColor=infoPtr->clrLine;
477 infoPtr->clrLine=(COLORREF) lParam;
478 return (LRESULT) prevColor;
482 TREEVIEW_GetInsertMarkColor (HWND hwnd, WPARAM wParam, LPARAM lParam)
484 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
487 return (LRESULT) infoPtr->clrInsertMark;
491 TREEVIEW_SetInsertMarkColor (HWND hwnd, WPARAM wParam, LPARAM lParam)
493 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
494 COLORREF prevColor=infoPtr->clrInsertMark;
496 TRACE("%d %ld\n",wParam,lParam);
497 infoPtr->clrInsertMark=(COLORREF) lParam;
498 return (LRESULT) prevColor;
502 TREEVIEW_SetInsertMark (HWND hwnd, WPARAM wParam, LPARAM lParam)
504 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
507 FIXME("%d %ld\n",wParam,lParam);
508 if (!TREEVIEW_ValidItem (infoPtr, (HTREEITEM)lParam)) return 0;
509 FIXME("%d %ld\n",wParam,lParam);
511 infoPtr->insertBeforeorAfter=(BOOL) wParam;
512 infoPtr->insertMarkItem=(HTREEITEM) lParam;
515 TREEVIEW_Refresh (hwnd, hdc);
522 TREEVIEW_SetTextColor (HWND hwnd, WPARAM wParam, LPARAM lParam)
524 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
525 COLORREF prevColor=infoPtr->clrText;
528 infoPtr->clrText=(COLORREF) lParam;
529 return (LRESULT) prevColor;
533 TREEVIEW_GetBkColor (HWND hwnd)
535 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
538 return (LRESULT) infoPtr->clrBk;
542 TREEVIEW_SetBkColor (HWND hwnd, WPARAM wParam, LPARAM lParam)
544 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
545 COLORREF prevColor=infoPtr->clrBk;
548 infoPtr->clrBk=(COLORREF) lParam;
549 return (LRESULT) prevColor;
553 TREEVIEW_GetTextColor (HWND hwnd)
555 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
558 return (LRESULT) infoPtr->clrText;
562 /* cdmode: custom draw mode as received from app. in first NMCUSTOMDRAW
565 #define TREEVIEW_LEFT_MARGIN 8
569 TREEVIEW_DrawItem (HWND hwnd, HDC hdc, TREEVIEW_ITEM *wineItem)
571 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
572 DWORD dwStyle = GetWindowLongA (hwnd, GWL_STYLE);
573 INT center,xpos,cx,cy, cditem;
575 UINT uTextJustify = DT_LEFT;
579 if (wineItem->state & TVIS_BOLD)
580 hOldFont = SelectObject (hdc, infoPtr->hBoldFont);
582 hOldFont = SelectObject (hdc, infoPtr->hFont);
585 TRACE ("cdmode:%x\n",infoPtr->cdmode);
586 if (infoPtr->cdmode & CDRF_NOTIFYITEMDRAW) {
587 cditem=TREEVIEW_SendCustomDrawItemNotify
588 (hwnd, hdc, wineItem, CDDS_ITEMPREPAINT);
589 TRACE("prepaint:cditem-app returns 0x%x\n",cditem);
591 if (cditem & CDRF_SKIPDEFAULT)
596 * Set drawing starting points
598 r = wineItem->rect; /* this item rectangle */
599 center = (r.top+r.bottom)/2; /* this item vertical center */
600 xpos = r.left + TREEVIEW_LEFT_MARGIN;/* horizontal starting point */
603 * Display the tree hierarchy
605 if ( dwStyle & TVS_HASLINES)
608 * Write links to parent node
609 * we draw the L starting from the child to the parent
611 * points[0] is attached to the current item
612 * points[1] is the L corner
613 * points[2] is attached to the parent or the up sibling
615 if ( dwStyle & TVS_LINESATROOT)
617 TREEVIEW_ITEM *upNode = NULL;
618 BOOL hasParentOrSibling = TRUE;
619 RECT upRect = {0,0,0,0};
620 HPEN hOldPen, hNewPen;
623 * determine the target location of the line at root, either be linked
624 * to the up sibling or to the parent node.
626 if (wineItem->upsibling)
627 upNode = TREEVIEW_ValidItem (infoPtr, wineItem->upsibling);
628 else if (wineItem->parent)
629 upNode = TREEVIEW_ValidItem (infoPtr, wineItem->parent);
631 hasParentOrSibling = FALSE;
634 upRect = upNode->rect;
636 if ( wineItem->iLevel == 0 )
638 points[2].x = points[1].x = upRect.left+8;
639 points[0].x = points[2].x + 10;
640 points[2].y = upRect.bottom-3;
641 points[1].y = points[0].y = center;
645 points[2].x = points[1].x = 8 + (20*wineItem->iLevel);
646 points[2].y = ( upNode->cChildren == 0) ?
647 upRect.top : /* is linked to the "L" above */
648 ( wineItem->upsibling != NULL) ?
649 upRect.bottom-3: /* is linked to an icon */
650 upRect.bottom+1; /* is linked to a +/- box */
651 points[1].y = points[0].y = center;
652 points[0].x = points[1].x + 10;
658 hNewPen = CreatePen(PS_DOT, 0, infoPtr->clrLine);
659 hOldPen = SelectObject( hdc, hNewPen );
661 if (hasParentOrSibling)
662 Polyline (hdc,points,3);
664 Polyline (hdc,points,2);
666 DeleteObject(hNewPen);
667 SelectObject(hdc, hOldPen);
672 * Display the (+/-) signs
674 if (wineItem->iLevel != 0)/* update position only for non root node */
675 xpos+=(5*wineItem->iLevel);
677 if (( dwStyle & TVS_HASBUTTONS) && ( dwStyle & TVS_HASLINES))
679 if ( (wineItem->cChildren) ||
680 (wineItem->cChildren == I_CHILDRENCALLBACK))
682 /* Setup expand box coordinate to facilitate the LMBClick handling */
683 wineItem->expandBox.left = xpos-4;
684 wineItem->expandBox.top = center-4;
685 wineItem->expandBox.right = xpos+5;
686 wineItem->expandBox.bottom = center+5;
690 wineItem->expandBox.left,
691 wineItem->expandBox.top ,
692 wineItem->expandBox.right,
693 wineItem->expandBox.bottom);
695 MoveToEx (hdc, xpos-2, center, NULL);
696 LineTo (hdc, xpos+3, center);
698 if (!(wineItem->state & TVIS_EXPANDED)) {
699 MoveToEx (hdc, xpos, center-2, NULL);
700 LineTo (hdc, xpos, center+3);
706 * Display the image associated with this item
708 xpos += 13; /* update position */
709 if (wineItem->mask & (TVIF_IMAGE|TVIF_SELECTEDIMAGE)) {
711 HIMAGELIST *himlp = NULL;
713 /* State images are displayed to the left of the Normal image
714 * image number is in state; zero should be `display no image'.
715 * FIXME: that last sentence looks like it needs some checking.
717 if (infoPtr->himlState)
718 himlp=&infoPtr->himlState;
719 imageIndex=wineItem->state>>12;
720 imageIndex++; /* yeah, right */
721 TRACE ("imindex:%d\n",imageIndex);
722 if ((himlp) && (imageIndex))
724 imageIndex--; /* see FIXME */
725 ImageList_Draw ( *himlp, imageIndex, hdc, xpos-2, r.top+1, ILD_NORMAL);
726 ImageList_GetIconSize (*himlp, &cx, &cy);
727 wineItem->statebitmap.left=xpos-2;
728 wineItem->statebitmap.right=xpos-2+cx;
729 wineItem->statebitmap.top=r.top+1;
730 wineItem->statebitmap.bottom=r.top+1+cy;
734 /* Now, draw the normal image; can be either selected or
735 * non-selected image.
739 if (infoPtr->himlNormal)
740 himlp=&infoPtr->himlNormal; /* get the image list */
742 imageIndex = wineItem->iImage;
743 if ( (wineItem->state & TVIS_SELECTED) &&
744 (wineItem->iSelectedImage)) {
746 /* The item is curently selected */
747 if (wineItem->iSelectedImage == I_IMAGECALLBACK)
748 TREEVIEW_SendDispInfoNotify
749 (hwnd, wineItem, TVN_GETDISPINFOA, TVIF_SELECTEDIMAGE);
751 imageIndex = wineItem->iSelectedImage;
753 /* The item is not selected */
754 if (wineItem->iImage == I_IMAGECALLBACK)
755 TREEVIEW_SendDispInfoNotify
756 (hwnd, wineItem, TVN_GETDISPINFOA, TVIF_IMAGE);
758 imageIndex = wineItem->iImage;
765 if(wineItem->stateMask & TVIS_OVERLAYMASK)
766 ovlIdx = wineItem->state & TVIS_OVERLAYMASK;
768 ImageList_Draw ( *himlp, imageIndex, hdc, xpos-2, r.top+1, ILD_NORMAL|ovlIdx);
769 ImageList_GetIconSize (*himlp, &cx, &cy);
770 wineItem->bitmap.left=xpos-2;
771 wineItem->bitmap.right=xpos-2+cx;
772 wineItem->bitmap.top=r.top+1;
773 wineItem->bitmap.bottom=r.top+1+cy;
780 * Display the text associated with this item
783 if ((wineItem->mask & TVIF_TEXT) && (wineItem->pszText))
785 COLORREF oldBkColor = 0;
786 COLORREF oldTextColor = 0;
792 wineItem->text.left = r.left;
793 wineItem->text.right = r.right;
794 wineItem->text.top = r.top;
795 wineItem->text.bottom= r.bottom;
797 if (wineItem->pszText== LPSTR_TEXTCALLBACKA) {
798 TRACE("LPSTR_TEXTCALLBACK\n");
799 TREEVIEW_SendDispInfoNotify (hwnd, wineItem, TVN_GETDISPINFOA, TVIF_TEXT);
802 /* Yep, there are some things that need to be straightened out here.
803 Removing the comments around the setTextColor does not give the right
804 results. Dito FillRect.
808 /* GetTextExtentPoint32A (hdc, wineItem->pszText,
809 strlen (wineItem->pszText), &size); */
811 /* FillRect ( hdc, &wineItem->text, GetSysColorBrush (infoPtr->clrBk));
815 if (!(cditem & CDRF_NOTIFYPOSTPAINT) &&
816 (wineItem->state & (TVIS_SELECTED | TVIS_DROPHILITED)) ) {
817 oldBkMode = SetBkMode (hdc, OPAQUE);
818 oldBkColor = SetBkColor (hdc, GetSysColor( COLOR_HIGHLIGHT));
819 oldTextColor = SetTextColor(hdc, GetSysColor( COLOR_HIGHLIGHTTEXT));
821 oldBkMode = SetBkMode (hdc, TRANSPARENT);
822 oldBkColor = SetBkColor (hdc, infoPtr->clrBk);
823 /* oldTextColor = SetTextColor(hdc, infoPtr->clrText); */
831 lstrlenA(wineItem->pszText),
833 uTextJustify | DT_VCENTER | DT_SINGLELINE );
835 /* Obtain the text coordinate */
839 lstrlenA(wineItem->pszText),
841 uTextJustify | DT_VCENTER | DT_SINGLELINE | DT_CALCRECT);
843 /* Restore the hdc state */
844 SetTextColor( hdc, oldTextColor);
846 if (oldBkMode != TRANSPARENT)
847 SetBkMode(hdc, oldBkMode);
848 if (wineItem->state & (TVIS_SELECTED | TVIS_DROPHILITED))
849 SetBkColor (hdc, oldBkColor);
851 /* Draw the box arround the selected item */
852 if (wineItem->state & TVIS_SELECTED )
854 HPEN hNewPen = CreatePen(PS_DOT, 0, GetSysColor(COLOR_WINDOWTEXT) );
855 HPEN hOldPen = SelectObject( hdc, hNewPen );
858 points[0].x = wineItem->text.left-1;
859 points[0].y = wineItem->text.top+1;
860 points[1].x = wineItem->text.right;
861 points[1].y = wineItem->text.top+1;
862 points[2].x = wineItem->text.right;
863 points[2].y = wineItem->text.bottom;
864 points[3].x = wineItem->text.left-1;
865 points[3].y = wineItem->text.bottom;
867 Polyline (hdc,points,4);
869 DeleteObject(hNewPen);
870 SelectObject(hdc, hOldPen);
874 /* Draw insertion mark if necessary */
876 if (infoPtr->insertMarkItem)
877 TRACE ("item:%d,mark:%d\n", (int)wineItem->hItem,
878 (int) infoPtr->insertMarkItem);
879 if (wineItem->hItem==infoPtr->insertMarkItem) {
880 HPEN hNewPen, hOldPen;
883 hNewPen = CreatePen(PS_SOLID, 2, infoPtr->clrInsertMark);
884 hOldPen = SelectObject( hdc, hNewPen );
886 if (infoPtr->insertBeforeorAfter)
887 offset=wineItem->text.top+1;
889 offset=wineItem->text.bottom-1;
891 MoveToEx (hdc, wineItem->text.left, offset-3, NULL);
892 LineTo (hdc, wineItem->text.left, offset+3);
894 MoveToEx (hdc, wineItem->text.left, offset, NULL);
895 LineTo (hdc, r.right-2, offset);
897 MoveToEx (hdc, r.right-2, offset+3, NULL);
898 LineTo (hdc, r.right-2, offset-3);
900 DeleteObject(hNewPen);
902 SelectObject(hdc, hOldPen);
905 if (cditem & CDRF_NOTIFYPOSTPAINT) {
906 cditem=TREEVIEW_SendCustomDrawItemNotify
907 (hwnd, hdc, wineItem, CDDS_ITEMPOSTPAINT);
908 TRACE("postpaint:cditem-app returns 0x%x\n",cditem);
911 SelectObject (hdc, hOldFont);
915 TREEVIEW_GetItemRect (HWND hwnd, WPARAM wParam, LPARAM lParam)
917 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
918 TREEVIEW_ITEM *wineItem;
920 LPRECT lpRect = (LPRECT)lParam;
925 * validate parameters
930 if (infoPtr->Timer & TV_REFRESH_TIMER_SET) {
932 TREEVIEW_Refresh (hwnd, hdc); /* we want a rect for the current view */
938 * retrieve the item ptr
940 iItem = (HTREEITEM *) lParam;
941 wineItem = TREEVIEW_ValidItem (infoPtr, *iItem);
942 if ((!wineItem) || (!wineItem->visible))
946 * If wParam is TRUE return the text size otherwise return
947 * the whole item size
950 lpRect->left = wineItem->text.left;
951 lpRect->right = wineItem->text.right;
952 lpRect->bottom = wineItem->text.bottom;
953 lpRect->top = wineItem->text.top;
955 lpRect->left = wineItem->rect.left;
956 lpRect->right = wineItem->rect.right;
957 lpRect->bottom = wineItem->rect.bottom;
958 lpRect->top = wineItem->rect.top;
961 TRACE("[L:%d R:%d T:%d B:%d]\n",
962 lpRect->left,lpRect->right,
963 lpRect->top,lpRect->bottom);
969 TREEVIEW_GetVisibleCount (HWND hwnd, WPARAM wParam, LPARAM lParam)
972 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
974 return (LRESULT) infoPtr->uVisibleHeight / infoPtr->uRealItemHeight;
980 TREEVIEW_SetItemA (HWND hwnd, WPARAM wParam, LPARAM lParam)
982 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
983 TREEVIEW_ITEM *wineItem;
987 tvItem=(LPTVITEMEXA) lParam;
988 iItem=(INT)tvItem->hItem;
989 TRACE("item %d,mask %x\n",iItem,tvItem->mask);
991 wineItem = TREEVIEW_ValidItem (infoPtr, (HTREEITEM)iItem);
992 if (!wineItem) return FALSE;
994 if (tvItem->mask & TVIF_CHILDREN) {
995 wineItem->cChildren=tvItem->cChildren;
998 if (tvItem->mask & TVIF_IMAGE) {
999 wineItem->iImage=tvItem->iImage;
1002 if (tvItem->mask & TVIF_INTEGRAL) {
1003 wineItem->iIntegral=tvItem->iIntegral;
1006 if (tvItem->mask & TVIF_PARAM) {
1007 wineItem->lParam=tvItem->lParam;
1010 if (tvItem->mask & TVIF_SELECTEDIMAGE) {
1011 wineItem->iSelectedImage=tvItem->iSelectedImage;
1014 if (tvItem->mask & TVIF_STATE) {
1015 TRACE ("prevstate,state,mask:%x,%x,%x\n",wineItem->state,tvItem->state,
1017 wineItem->state&= ~tvItem->stateMask;
1018 wineItem->state|= (tvItem->state & tvItem->stateMask);
1019 wineItem->stateMask|= tvItem->stateMask;
1022 if (tvItem->mask & TVIF_TEXT) {
1023 if (tvItem->pszText!=LPSTR_TEXTCALLBACKA) {
1024 len=lstrlenA (tvItem->pszText);
1025 if (len>wineItem->cchTextMax)
1026 wineItem->pszText= COMCTL32_ReAlloc (wineItem->pszText, len+1);
1027 lstrcpynA (wineItem->pszText, tvItem->pszText,len+1);
1029 if (wineItem->cchTextMax) {
1030 COMCTL32_Free (wineItem->pszText);
1031 wineItem->cchTextMax=0;
1033 wineItem->pszText=LPSTR_TEXTCALLBACKA;
1037 wineItem->mask |= tvItem->mask;
1043 TREEVIEW_GetItemState (HWND hwnd, WPARAM wParam, LPARAM lParam)
1046 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
1047 TREEVIEW_ITEM *wineItem;
1050 wineItem = TREEVIEW_ValidItem (infoPtr, (HTREEITEM)wParam);
1051 if (!wineItem) return 0;
1053 return (wineItem->state & lParam);
1060 TREEVIEW_Refresh (HWND hwnd, HDC hdc)
1062 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
1066 INT iItem, indent, x, y, height, itemHeight;
1067 INT viewtop,viewbottom,viewleft,viewright;
1068 TREEVIEW_ITEM *wineItem, *prevItem;
1073 if (infoPtr->Timer & TV_REFRESH_TIMER_SET) {
1074 KillTimer (hwnd, TV_REFRESH_TIMER);
1075 infoPtr->Timer &= ~TV_REFRESH_TIMER_SET;
1079 GetClientRect (hwnd, &rect);
1080 if ((rect.left-rect.right ==0) || (rect.top-rect.bottom==0)) return;
1082 infoPtr->cdmode=TREEVIEW_SendCustomDrawNotify(hwnd,CDDS_PREPAINT,hdc,rect);
1084 if (infoPtr->cdmode==CDRF_SKIPDEFAULT) return;
1086 infoPtr->uVisibleHeight= rect.bottom-rect.top;
1087 infoPtr->uVisibleWidth= rect.right-rect.left;
1089 viewtop=infoPtr->cy;
1090 viewbottom=infoPtr->cy + rect.bottom-rect.top;
1091 viewleft=infoPtr->cx;
1092 viewright=infoPtr->cx + rect.right-rect.left;
1094 TRACE("[%d %d %d %d]\n",viewtop,viewbottom,viewleft,viewright);
1096 /* draw background */
1098 hbrBk = CreateSolidBrush (infoPtr->clrBk);
1099 FillRect(hdc, &rect, hbrBk);
1100 DeleteObject(hbrBk);
1102 ImageList_GetIconSize (infoPtr->himlNormal, &x, &itemHeight);
1103 if (infoPtr->uItemHeight>itemHeight)
1104 itemHeight=infoPtr->uItemHeight;
1106 GetTextMetricsA (hdc, &tm);
1107 if ((tm.tmHeight + tm.tmExternalLeading) > itemHeight)
1108 itemHeight=tm.tmHeight + tm.tmExternalLeading;
1110 infoPtr->uRealItemHeight=itemHeight;
1112 iItem=(INT)infoPtr->TopRootItem;
1113 infoPtr->firstVisible=0;
1120 wineItem= & infoPtr->items[iItem];
1121 wineItem->iLevel=indent;
1123 /* FIXME: remove this in later stage */
1125 if (wineItem->pszText!=LPSTR_TEXTCALLBACK32A)
1126 TRACE (treeview, "%d %d [%d %d %d %d] (%s)\n",y,x,
1127 wineItem->rect.top, wineItem->rect.bottom,
1128 wineItem->rect.left, wineItem->rect.right,
1131 TRACE (treeview, "%d [%d %d %d %d] (CALLBACK)\n",
1133 wineItem->rect.top, wineItem->rect.bottom,
1134 wineItem->rect.left, wineItem->rect.right);
1137 height=itemHeight * wineItem->iIntegral +1;
1138 if ((y >= viewtop) && (y <= viewbottom) &&
1139 (x >= viewleft ) && (x <= viewright)) {
1140 wineItem->visible = TRUE;
1141 wineItem->rect.top = y - infoPtr->cy + rect.top;
1142 wineItem->rect.bottom = wineItem->rect.top + height ;
1143 wineItem->rect.left = x - infoPtr->cx + rect.left;
1144 wineItem->rect.right = rect.right;
1145 if (!infoPtr->firstVisible)
1146 infoPtr->firstVisible=wineItem->hItem;
1147 TREEVIEW_DrawItem (hwnd, hdc, wineItem);
1150 wineItem->visible = FALSE;
1151 wineItem->rect.left = wineItem->rect.top = 0;
1152 wineItem->rect.right= wineItem->rect.bottom = 0;
1153 wineItem->text.left = wineItem->text.top = 0;
1154 wineItem->text.right= wineItem->text.bottom = 0;
1157 /* look up next item */
1159 if ((wineItem->firstChild) && (wineItem->state & TVIS_EXPANDED)) {
1160 iItem=(INT)wineItem->firstChild;
1162 x+=infoPtr->uIndent;
1163 if (x>infoPtr->uTotalWidth)
1164 infoPtr->uTotalWidth=x;
1167 iItem=(INT)wineItem->sibling;
1168 while ((!iItem) && (indent>0)) {
1170 x-=infoPtr->uIndent;
1171 wineItem=&infoPtr->items[(INT)wineItem->parent];
1172 iItem=(INT)wineItem->sibling;
1178 /* FIXME: infoPtr->uTotalWidth should also take item label into account */
1179 /* FIXME: or should query item sizes (ie check CDRF_NEWFONT) */
1181 infoPtr->uTotalHeight=y;
1182 if (y >= (viewbottom-viewtop)) {
1183 if (!(infoPtr->uInternalStatus & TV_VSCROLL))
1184 ShowScrollBar (hwnd, SB_VERT, TRUE);
1185 infoPtr->uInternalStatus |=TV_VSCROLL;
1186 SetScrollRange (hwnd, SB_VERT, 0,
1187 y - infoPtr->uVisibleHeight, FALSE);
1188 SetScrollPos (hwnd, SB_VERT, infoPtr->cy, TRUE);
1191 if (infoPtr->uInternalStatus & TV_VSCROLL)
1192 ShowScrollBar (hwnd, SB_VERT, FALSE);
1193 infoPtr->uInternalStatus &= ~TV_VSCROLL;
1197 if (infoPtr->cdmode & CDRF_NOTIFYPOSTPAINT)
1198 infoPtr->cdmode=TREEVIEW_SendCustomDrawNotify
1199 (hwnd, CDDS_POSTPAINT, hdc, rect);
1206 TREEVIEW_HandleTimer (HWND hwnd, WPARAM wParam, LPARAM lParam)
1208 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
1210 TRACE(" %d\n",wParam);
1213 case TV_REFRESH_TIMER:
1214 KillTimer (hwnd, TV_REFRESH_TIMER);
1215 infoPtr->Timer &= ~TV_REFRESH_TIMER_SET;
1216 InvalidateRect(hwnd, NULL, FALSE);
1219 KillTimer (hwnd, TV_EDIT_TIMER);
1220 infoPtr->Timer &= ~TV_EDIT_TIMER_SET;
1223 ERR("got unknown timer\n");
1231 TREEVIEW_QueueRefresh (HWND hwnd)
1234 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
1237 if (infoPtr->Timer & TV_REFRESH_TIMER_SET) {
1238 KillTimer (hwnd, TV_REFRESH_TIMER);
1241 SetTimer (hwnd, TV_REFRESH_TIMER, TV_REFRESH_DELAY, 0);
1242 infoPtr->Timer|=TV_REFRESH_TIMER_SET;
1248 TREEVIEW_GetItemA (HWND hwnd, WPARAM wParam, LPARAM lParam)
1250 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
1252 TREEVIEW_ITEM *wineItem;
1255 tvItem=(LPTVITEMEXA) lParam;
1256 iItem=(INT)tvItem->hItem;
1258 wineItem = TREEVIEW_ValidItem (infoPtr, (HTREEITEM)iItem);
1259 if (!wineItem) return FALSE;
1261 if (tvItem->mask & TVIF_CHILDREN) {
1262 if (TVIF_CHILDREN==I_CHILDRENCALLBACK)
1263 FIXME("I_CHILDRENCALLBACK not supported\n");
1264 tvItem->cChildren=wineItem->cChildren;
1267 if (tvItem->mask & TVIF_HANDLE) {
1268 tvItem->hItem=wineItem->hItem;
1271 if (tvItem->mask & TVIF_IMAGE) {
1272 tvItem->iImage=wineItem->iImage;
1275 if (tvItem->mask & TVIF_INTEGRAL) {
1276 tvItem->iIntegral=wineItem->iIntegral;
1279 /* undocumented: windows ignores TVIF_PARAM and
1280 * always sets lParam
1282 tvItem->lParam=wineItem->lParam;
1284 if (tvItem->mask & TVIF_SELECTEDIMAGE) {
1285 tvItem->iSelectedImage=wineItem->iSelectedImage;
1288 if (tvItem->mask & TVIF_STATE) {
1289 tvItem->state=wineItem->state & tvItem->stateMask;
1292 if (tvItem->mask & TVIF_TEXT) {
1293 if (wineItem->pszText == LPSTR_TEXTCALLBACKA) {
1294 tvItem->pszText = LPSTR_TEXTCALLBACKA; /* FIXME:send notification? */
1295 ERR(" GetItem called with LPSTR_TEXTCALLBACK\n");
1297 else if (wineItem->pszText) {
1298 lstrcpynA (tvItem->pszText, wineItem->pszText, tvItem->cchTextMax);
1302 TRACE("item %d<%p>, txt %p, img %p, action %x\n",
1303 iItem, tvItem, tvItem->pszText, &tvItem->iImage, tvItem->mask);
1310 TREEVIEW_GetItemW (HWND hwnd, WPARAM wParam, LPARAM lParam)
1312 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
1314 TREEVIEW_ITEM *wineItem;
1317 tvItem=(LPTVITEMEXA) lParam;
1318 iItem=(INT)tvItem->hItem;
1320 wineItem = TREEVIEW_ValidItem (infoPtr, (HTREEITEM)iItem);
1321 if (!wineItem) return FALSE;
1323 if (tvItem->mask & TVIF_CHILDREN) {
1324 if (TVIF_CHILDREN==I_CHILDRENCALLBACK)
1325 FIXME("I_CHILDRENCALLBACK not supported\n");
1326 tvItem->cChildren=wineItem->cChildren;
1329 if (tvItem->mask & TVIF_HANDLE) {
1330 tvItem->hItem=wineItem->hItem;
1333 if (tvItem->mask & TVIF_IMAGE) {
1334 tvItem->iImage=wineItem->iImage;
1337 if (tvItem->mask & TVIF_INTEGRAL) {
1338 tvItem->iIntegral=wineItem->iIntegral;
1341 /* undocumented: windows ignores TVIF_PARAM and
1342 * always sets lParam
1344 tvItem->lParam=wineItem->lParam;
1346 if (tvItem->mask & TVIF_SELECTEDIMAGE) {
1347 tvItem->iSelectedImage=wineItem->iSelectedImage;
1350 if (tvItem->mask & TVIF_STATE) {
1351 tvItem->state=wineItem->state & tvItem->stateMask;
1355 if (tvItem->mask & TVIF_TEXT) {
1356 if (wineItem->pszText == LPSTR_TEXTCALLBACKW) {
1357 tvItem->pszText = LPSTR_TEXTCALLBACKW; /* FIXME:send notification? */
1358 ERR(" GetItem called with LPSTR_TEXTCALLBACK\n");
1360 else if (wineItem->pszText) {
1361 lstrcpynAtoW (tvItem->pszText, wineItem->pszText, tvItem->cchTextMax);
1365 wineItem->pszText = NULL;
1367 TRACE("item %d<%p>, txt %p, img %p, action %x\n",
1368 iItem, tvItem, tvItem->pszText, &tvItem->iImage, tvItem->mask);
1375 /* FIXME: check implementation of TVGN_NEXT/TVGN_NEXTVISIBLE */
1378 TREEVIEW_GetNextItem (HWND hwnd, WPARAM wParam, LPARAM lParam)
1381 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
1382 TREEVIEW_ITEM *wineItem, *returnItem;
1383 INT iItem = (INT)lParam, retval = 0, flag = (INT)wParam;
1388 retval = (INT)infoPtr->TopRootItem;
1392 retval = (INT)infoPtr->selectedItem;
1395 case TVGN_FIRSTVISIBLE: /* FIXME:we should only recalculate, not redraw */
1397 TREEVIEW_Refresh (hwnd, hdc);
1398 ReleaseDC(hwnd,hdc);
1399 retval = (INT)infoPtr->firstVisible;
1402 case TVGN_DROPHILITE:
1403 retval = (INT)infoPtr->dropItem;
1407 wineItem = TREEVIEW_ValidItem (infoPtr, (HTREEITEM)iItem);
1408 retval = wineItem ? (INT)wineItem->sibling : 0;
1412 wineItem = TREEVIEW_ValidItem (infoPtr, (HTREEITEM)iItem);
1413 retval = wineItem ? (INT)wineItem->upsibling : 0;
1417 wineItem = TREEVIEW_ValidItem (infoPtr, (HTREEITEM)iItem);
1418 retval = wineItem ? (INT)wineItem->parent : 0;
1422 wineItem = TREEVIEW_ValidItem (infoPtr, (HTREEITEM)iItem);
1423 retval = wineItem ? (INT)wineItem->firstChild : 0;
1426 case TVGN_LASTVISIBLE:
1427 if((wineItem = TREEVIEW_ValidItem (infoPtr, (HTREEITEM)iItem))) {
1428 returnItem = TREEVIEW_GetLastListItem (infoPtr,wineItem);
1429 retval = returnItem ? (INT)returnItem->hItem : 0;
1433 case TVGN_NEXTVISIBLE:
1434 if((wineItem = TREEVIEW_ValidItem (infoPtr, (HTREEITEM)iItem))) {
1435 returnItem = TREEVIEW_GetNextListItem (infoPtr,wineItem);
1436 retval = returnItem ? (INT)returnItem->hItem : 0;
1440 case TVGN_PREVIOUSVISIBLE:
1441 if((wineItem = TREEVIEW_ValidItem (infoPtr, (HTREEITEM)iItem))) {
1442 returnItem = TREEVIEW_GetPrevListItem (infoPtr, wineItem);
1443 retval = returnItem ? (INT)returnItem->hItem : 0;
1448 FIXME("Unknown msg %x,item %x\n", flag,iItem);
1452 TRACE("flags %x, item %d returns %d\n", flag, iItem, retval);
1458 TREEVIEW_GetCount (HWND hwnd, WPARAM wParam, LPARAM lParam)
1460 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
1462 TRACE(" %d\n",infoPtr->uNumItems);
1463 return (LRESULT) infoPtr->uNumItems;
1466 /***************************************************************************
1467 * This method does the chaining of the insertion of a treeview item
1469 * If parent is NULL, we're inserting at the root of the list.
1471 static void TREEVIEW_InsertBefore(
1472 TREEVIEW_INFO *infoPtr,
1473 TREEVIEW_ITEM *newItem,
1474 TREEVIEW_ITEM *sibling,
1475 TREEVIEW_ITEM *parent)
1477 HTREEITEM siblingHandle = 0;
1478 HTREEITEM upSiblingHandle = 0;
1479 TREEVIEW_ITEM *upSibling = NULL;
1481 if (newItem == NULL)
1482 ERR("NULL newItem, impossible condition\n");
1484 if (sibling != NULL) /* Insert before this sibling for this parent */
1486 /* Store the new item sibling up sibling and sibling tem handle */
1487 siblingHandle = sibling->hItem;
1488 upSiblingHandle = sibling->upsibling;
1489 /* As well as a pointer to the upsibling sibling object */
1490 if ( (INT)sibling->upsibling != 0 )
1491 upSibling = &infoPtr->items[(INT)sibling->upsibling];
1493 /* Adjust the sibling pointer */
1494 sibling->upsibling = newItem->hItem;
1496 /* Adjust the new item pointers */
1497 newItem->upsibling = upSiblingHandle;
1498 newItem->sibling = siblingHandle;
1500 /* Adjust the up sibling pointer */
1501 if ( upSibling != NULL )
1502 upSibling->sibling = newItem->hItem;
1504 /* this item is the first child of this parent, adjust parent pointers */
1506 parent->firstChild = newItem->hItem;
1508 infoPtr->TopRootItem= newItem->hItem;
1510 else /* Insert as first child of this parent */
1512 parent->firstChild = newItem->hItem;
1515 /***************************************************************************
1516 * This method does the chaining of the insertion of a treeview item
1518 * If parent is NULL, we're inserting at the root of the list.
1520 static void TREEVIEW_InsertAfter(
1521 TREEVIEW_INFO *infoPtr,
1522 TREEVIEW_ITEM *newItem,
1523 TREEVIEW_ITEM *upSibling,
1524 TREEVIEW_ITEM *parent)
1526 HTREEITEM upSiblingHandle = 0;
1527 HTREEITEM siblingHandle = 0;
1528 TREEVIEW_ITEM *sibling = NULL;
1531 if (newItem == NULL)
1532 ERR("NULL newItem, impossible condition\n");
1534 if (upSibling != NULL) /* Insert after this upsibling for this parent */
1536 /* Store the new item up sibling and sibling item handle */
1537 upSiblingHandle = upSibling->hItem;
1538 siblingHandle = upSibling->sibling;
1539 /* As well as a pointer to the upsibling sibling object */
1540 if ( (INT)upSibling->sibling != 0 )
1541 sibling = &infoPtr->items[(INT)upSibling->sibling];
1543 /* Adjust the up sibling pointer */
1544 upSibling->sibling = newItem->hItem;
1546 /* Adjust the new item pointers */
1547 newItem->upsibling = upSiblingHandle;
1548 newItem->sibling = siblingHandle;
1550 /* Adjust the sibling pointer */
1551 if ( sibling != NULL )
1552 sibling->upsibling = newItem->hItem;
1555 newItem is the last of the level, nothing else to do
1558 else /* Insert as first child of this parent */
1560 parent->firstChild = newItem->hItem;
1563 /***************************************************************************
1564 * Forward the DPA local callback to the treeview owner callback
1566 static INT WINAPI TREEVIEW_CallBackCompare(
1571 /* Forward the call to the client define callback */
1572 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr((HWND)tvInfoPtr);
1573 return (infoPtr->pCallBackSort->lpfnCompare)(
1574 ((TREEVIEW_ITEM*)first)->lParam,
1575 ((TREEVIEW_ITEM*)second)->lParam,
1576 infoPtr->pCallBackSort->lParam);
1579 /***************************************************************************
1580 * Treeview native sort routine: sort on item text.
1582 static INT WINAPI TREEVIEW_SortOnName (
1587 HWND hwnd=(HWND) tvInfoPtr;
1589 TREEVIEW_ITEM *item;
1592 item=(TREEVIEW_ITEM *) first;
1593 if (item->pszText==LPSTR_TEXTCALLBACKA) {
1594 TREEVIEW_SendDispInfoNotify (hwnd, item, TVN_GETDISPINFOA, TVIF_TEXT);
1598 item=(TREEVIEW_ITEM *) second;
1599 if (item->pszText==LPSTR_TEXTCALLBACKA) {
1600 TREEVIEW_SendDispInfoNotify (hwnd, item, TVN_GETDISPINFOA, TVIF_TEXT);
1604 return -strcmp (txt1,txt2);
1607 /***************************************************************************
1608 * Setup the treeview structure with regards of the sort method
1609 * and sort the children of the TV item specified in lParam
1610 * fRecurse: currently unused. Should be zero.
1611 * parent: if pSort!=NULL, should equal pSort->hParent.
1612 * otherwise, item which child items are to be sorted.
1613 * pSort: sort method info. if NULL, sort on item text.
1614 * if non-NULL, sort on item's lParam content, and let the
1615 * application decide what that means. See also TVM_SORTCHILDRENCB.
1618 static LRESULT WINAPI TREEVIEW_Sort (
1625 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
1626 TREEVIEW_ITEM *sortMe = NULL; /* Node for which we sort the children */
1628 /* Obtain the TVSORTBC struct */
1629 infoPtr->pCallBackSort = pSort;
1631 /* undocumented feature: TVI_ROOT means `sort the whole tree' */
1633 if (parent==TVI_ROOT)
1634 parent=infoPtr->TopRootItem;
1636 /* Check for a valid handle to the parent item */
1637 if (!TREEVIEW_ValidItem(infoPtr, parent))
1639 ERR ("invalid item hParent=%x\n", (INT)parent);
1643 /* Obtain the parent node to sort */
1644 sortMe = &infoPtr->items[ (INT)parent ];
1646 /* Make sure there is something to sort */
1647 if ( sortMe->cChildren > 1 )
1649 /* pointer organization */
1650 HDPA sortList = DPA_Create(sortMe->cChildren);
1651 HTREEITEM itemHandle = sortMe->firstChild;
1652 TREEVIEW_ITEM *itemPtr = & infoPtr->items[ (INT)itemHandle ];
1654 /* TREEVIEW_ITEM rechaining */
1660 /* Build the list of item to sort */
1664 sortList, /* the list */
1665 sortMe->cChildren+1, /* force the insertion to be an append */
1666 itemPtr); /* the ptr to store */
1668 /* Get the next sibling */
1669 itemHandle = itemPtr->sibling;
1670 itemPtr = & infoPtr->items[ (INT)itemHandle ];
1671 } while ( itemHandle != NULL );
1673 /* let DPA perform the sort activity */
1676 sortList, /* what */
1677 TREEVIEW_CallBackCompare, /* how */
1681 sortList, /* what */
1682 TREEVIEW_SortOnName, /* how */
1686 * Reorganized TREEVIEW_ITEM structures.
1687 * Note that we know we have at least two elements.
1690 /* Get the first item and get ready to start... */
1691 item = DPA_GetPtr(sortList, count++);
1692 while ( (nextItem = DPA_GetPtr(sortList, count++)) != NULL )
1694 /* link the two current item toghether */
1695 ((TREEVIEW_ITEM*)item)->sibling = ((TREEVIEW_ITEM*)nextItem)->hItem;
1696 ((TREEVIEW_ITEM*)nextItem)->upsibling = ((TREEVIEW_ITEM*)item)->hItem;
1698 if (prevItem == NULL) /* this is the first item, update the parent */
1700 sortMe->firstChild = ((TREEVIEW_ITEM*)item)->hItem;
1701 ((TREEVIEW_ITEM*)item)->upsibling = NULL;
1703 else /* fix the back chaining */
1705 ((TREEVIEW_ITEM*)item)->upsibling = ((TREEVIEW_ITEM*)prevItem)->hItem;
1708 /* get ready for the next one */
1713 /* the last item is pointed to by item and never has a sibling */
1714 ((TREEVIEW_ITEM*)item)->sibling = NULL;
1716 DPA_Destroy(sortList);
1724 /***************************************************************************
1725 * Setup the treeview structure with regards of the sort method
1726 * and sort the children of the TV item specified in lParam
1728 static LRESULT WINAPI TREEVIEW_SortChildrenCB(
1734 LPTVSORTCB pSort=(LPTVSORTCB) lParam;
1736 return TREEVIEW_Sort (hwnd, wParam, pSort->hParent, pSort);
1740 /***************************************************************************
1741 * Sort the children of the TV item specified in lParam.
1743 static LRESULT WINAPI TREEVIEW_SortChildren (
1748 return TREEVIEW_Sort (hwnd, (BOOL) wParam, (HTREEITEM) lParam, NULL);
1753 /* the method used below isn't the most memory-friendly, but it avoids
1754 a lot of memory reallocations */
1756 /* BTW: we waste handle 0; 0 is not an allowed handle. */
1759 TREEVIEW_InsertItemA (HWND hwnd, WPARAM wParam, LPARAM lParam)
1762 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
1763 TVINSERTSTRUCTA *ptdi;
1765 TREEVIEW_ITEM *wineItem, *parentItem, *prevsib, *sibItem;
1766 INT iItem,listItems,i,len;
1768 /* Item to insert */
1769 ptdi = (LPTVINSERTSTRUCTA) lParam;
1771 /* check if memory is available */
1773 if (infoPtr->uNumPtrsAlloced==0) {
1774 infoPtr->items = COMCTL32_Alloc (TVITEM_ALLOC*sizeof (TREEVIEW_ITEM));
1775 infoPtr->freeList= COMCTL32_Alloc ((1+(TVITEM_ALLOC>>5)) * sizeof (INT));
1776 infoPtr->uNumPtrsAlloced=TVITEM_ALLOC;
1777 infoPtr->TopRootItem=(HTREEITEM)1;
1781 * Reallocate contiguous space for items
1783 if (infoPtr->uNumItems == (infoPtr->uNumPtrsAlloced-1) ) {
1784 TREEVIEW_ITEM *oldItems = infoPtr->items;
1785 INT *oldfreeList = infoPtr->freeList;
1787 infoPtr->uNumPtrsAlloced*=2;
1788 infoPtr->items = COMCTL32_Alloc (infoPtr->uNumPtrsAlloced*sizeof (TREEVIEW_ITEM));
1789 infoPtr->freeList= COMCTL32_Alloc ((1+(infoPtr->uNumPtrsAlloced>>5))*sizeof (INT));
1791 memcpy (&infoPtr->items[0], &oldItems[0],
1792 infoPtr->uNumPtrsAlloced/2 * sizeof(TREEVIEW_ITEM));
1793 memcpy (&infoPtr->freeList[0], &oldfreeList[0],
1794 (infoPtr->uNumPtrsAlloced>>6) * sizeof(INT));
1796 COMCTL32_Free (oldItems);
1797 COMCTL32_Free (oldfreeList);
1801 * Reset infoPtr structure with new stat according to current TV picture
1804 infoPtr->uNumItems++;
1805 if ((INT)infoPtr->uMaxHandle==(infoPtr->uNumItems-1)) {
1806 iItem=infoPtr->uNumItems;
1807 infoPtr->uMaxHandle = (HTREEITEM)((INT)infoPtr->uMaxHandle + 1);
1808 } else { /* check freelist */
1809 for (i=0; i<=infoPtr->uNumPtrsAlloced>>5; i++) {
1810 if (infoPtr->freeList[i]) {
1811 iItem=ffs (infoPtr->freeList[i])-1;
1812 tv_clear_bit(iItem,&infoPtr->freeList[i]);
1819 if (TRACE_ON(treeview)) {
1820 for (i=0; i<=infoPtr->uNumPtrsAlloced>>5; i++)
1821 TRACE("%8x\n",infoPtr->freeList[i]);
1824 if (!iItem) ERR("Argh -- can't find free item.\n");
1827 * Find the parent item of the new item
1829 tvItem= & ptdi->DUMMYUNIONNAME.itemex;
1830 wineItem=& infoPtr->items[iItem];
1832 if ((ptdi->hParent==TVI_ROOT) || (ptdi->hParent==0)) {
1834 wineItem->parent = 0;
1835 sibItem = &infoPtr->items [(INT)infoPtr->TopRootItem];
1836 listItems = infoPtr->uNumItems;
1839 parentItem = &infoPtr->items[(INT)ptdi->hParent];
1841 /* Do the insertion here it if it's the only item of this parent */
1842 if (!parentItem->firstChild)
1843 parentItem->firstChild=(HTREEITEM)iItem;
1845 wineItem->parent = ptdi->hParent;
1846 sibItem = &infoPtr->items [(INT)parentItem->firstChild];
1847 listItems = parentItem->cChildren;
1848 parentItem->cChildren++;
1852 /* NOTE: I am moving some setup of the wineItem object that was initialy
1853 * done at the end of the function since some of the values are
1854 * required by the Callback sorting
1857 if (tvItem->mask & TVIF_TEXT)
1860 * Setup the item text stuff here since it's required by the Sort method
1861 * when the insertion are ordered
1863 if (tvItem->pszText!=LPSTR_TEXTCALLBACKA)
1865 TRACE("(%p,%s)\n", &tvItem->pszText, tvItem->pszText);
1866 len = lstrlenA (tvItem->pszText)+1;
1867 wineItem->pszText= COMCTL32_Alloc (len+1);
1868 lstrcpyA (wineItem->pszText, tvItem->pszText);
1869 wineItem->cchTextMax=len;
1873 TRACE("LPSTR_TEXTCALLBACK\n");
1874 wineItem->pszText = LPSTR_TEXTCALLBACKA;
1875 wineItem->cchTextMax = 0;
1879 if (tvItem->mask & TVIF_PARAM)
1880 wineItem->lParam=tvItem->lParam;
1883 wineItem->upsibling=0; /* needed in case we're the first item in a list */
1884 wineItem->sibling=0;
1885 wineItem->firstChild=0;
1886 wineItem->hItem=(HTREEITEM)iItem;
1891 switch ((DWORD) ptdi->hInsertAfter) {
1892 case (DWORD) TVI_FIRST:
1893 if (sibItem==wineItem) break;
1894 if (wineItem->parent) {
1895 wineItem->sibling=parentItem->firstChild;
1896 parentItem->firstChild=(HTREEITEM)iItem;
1898 wineItem->sibling=infoPtr->TopRootItem;
1899 infoPtr->TopRootItem=(HTREEITEM)iItem;
1901 sibItem->upsibling=(HTREEITEM)iItem;
1904 case (DWORD) TVI_SORT:
1905 if (sibItem==wineItem)
1907 * This item is the first child of the level and it
1908 * has already been inserted
1913 TREEVIEW_ITEM *aChild;
1916 TREEVIEW_ITEM *previousChild = NULL;
1917 BOOL bItemInserted = FALSE;
1920 aChild = &infoPtr->items[(INT)parentItem->firstChild];
1922 aChild = &infoPtr->items[(INT)infoPtr->TopRootItem];
1924 /* lookup the text if using LPSTR_TEXTCALLBACKs */
1925 if (wineItem->pszText==LPSTR_TEXTCALLBACKA) {
1926 TREEVIEW_SendDispInfoNotify (hwnd, wineItem, TVN_GETDISPINFOA, TVIF_TEXT);
1929 /* Iterate the parent children to see where we fit in */
1930 while ( aChild != NULL )
1934 /* lookup the text if using LPSTR_TEXTCALLBACKs */
1935 if (aChild->pszText==LPSTR_TEXTCALLBACKA) {
1936 TREEVIEW_SendDispInfoNotify (hwnd, aChild, TVN_GETDISPINFOA, TVIF_TEXT);
1939 comp = strcmp(wineItem->pszText, aChild->pszText);
1940 if ( comp < 0 ) /* we are smaller than the current one */
1942 TREEVIEW_InsertBefore(infoPtr, wineItem, aChild, parentItem);
1943 bItemInserted = TRUE;
1946 else if ( comp > 0 ) /* we are bigger than the current one */
1948 previousChild = aChild;
1949 aChild = (aChild->sibling == 0) /* This will help us to exit */
1950 ? NULL /* if there is no more sibling */
1951 : &infoPtr->items[(INT)aChild->sibling];
1953 /* Look at the next item */
1956 else if ( comp == 0 )
1959 * An item with this name is already existing, therefore,
1960 * we add after the one we found
1962 TREEVIEW_InsertAfter(infoPtr, wineItem, aChild, parentItem);
1963 bItemInserted = TRUE;
1969 * we reach the end of the child list and the item as not
1970 * yet been inserted, therefore, insert it after the last child.
1972 if ( (! bItemInserted ) && (aChild == NULL) )
1973 TREEVIEW_InsertAfter(infoPtr, wineItem, previousChild, parentItem);
1979 case (DWORD) TVI_LAST:
1980 if (sibItem==wineItem) break;
1981 while (sibItem->sibling) {
1983 sibItem=&infoPtr->items [(INT)sibItem->sibling];
1985 sibItem->sibling=(HTREEITEM)iItem;
1986 wineItem->upsibling=sibItem->hItem;
1990 TREEVIEW_ITEM *localsibItem = sibItem;
1991 while ((localsibItem->sibling) &&
1992 (localsibItem->hItem!=ptdi->hInsertAfter))
1994 prevsib=localsibItem;
1995 localsibItem=&infoPtr->items [(INT)localsibItem->sibling];
1997 if (localsibItem->hItem!=ptdi->hInsertAfter) {
1998 WARN("tried to insert item after nonexisting handle %d treating as TVI_LAST.\n",
1999 (INT) ptdi->hInsertAfter);
2001 * retry placing it last
2003 if (sibItem==wineItem) break;
2004 while (sibItem->sibling) {
2006 sibItem=&infoPtr->items [(INT)sibItem->sibling];
2008 sibItem->sibling=(HTREEITEM)iItem;
2009 wineItem->upsibling=sibItem->hItem;
2012 prevsib=localsibItem;
2013 if (localsibItem->sibling) {
2014 localsibItem=&infoPtr->items [(INT)localsibItem->sibling];
2015 localsibItem->upsibling=(HTREEITEM)iItem;
2016 wineItem->sibling=localsibItem->hItem;
2018 prevsib->sibling=(HTREEITEM)iItem;
2019 wineItem->upsibling=prevsib->hItem;
2026 /* Fill in info structure */
2028 TRACE("new item %d; parent %d, mask %x\n", iItem,
2029 (INT)wineItem->parent,tvItem->mask);
2031 wineItem->mask=tvItem->mask;
2032 wineItem->iIntegral=1;
2034 if (tvItem->mask & TVIF_CHILDREN) {
2035 wineItem->cChildren=tvItem->cChildren;
2036 if (tvItem->cChildren==I_CHILDRENCALLBACK)
2037 FIXME(" I_CHILDRENCALLBACK not supported\n");
2040 wineItem->expandBox.left = 0; /* Initialize the expandBox */
2041 wineItem->expandBox.top = 0;
2042 wineItem->expandBox.right = 0;
2043 wineItem->expandBox.bottom = 0;
2045 if (tvItem->mask & TVIF_IMAGE)
2046 wineItem->iImage=tvItem->iImage;
2048 /* If the application sets TVIF_INTEGRAL without
2049 supplying a TVITEMEX structure, it's toast */
2051 if (tvItem->mask & TVIF_INTEGRAL)
2052 wineItem->iIntegral=tvItem->iIntegral;
2054 if (tvItem->mask & TVIF_SELECTEDIMAGE)
2055 wineItem->iSelectedImage=tvItem->iSelectedImage;
2057 if (tvItem->mask & TVIF_STATE) {
2058 TRACE("item state: %x ->%x\n", wineItem->state, tvItem->state);
2059 TRACE("statemask: %x ->%x\n", wineItem->stateMask, tvItem->stateMask);
2060 wineItem->state=tvItem->state;
2061 wineItem->stateMask=tvItem->stateMask;
2064 TREEVIEW_QueueRefresh (hwnd);
2066 return (LRESULT) iItem;
2071 TREEVIEW_InsertItemW(HWND hwnd, WPARAM wParam, LPARAM lParam)
2073 TVINSERTSTRUCTW *tvisW;
2074 TVINSERTSTRUCTA tvisA;
2077 tvisW = (LPTVINSERTSTRUCTW)lParam;
2079 tvisA.hParent = tvisW->hParent;
2080 tvisA.hInsertAfter = tvisW->hInsertAfter;
2082 tvisA.DUMMYUNIONNAME.item.mask = tvisW->DUMMYUNIONNAME.item.mask;
2083 tvisA.DUMMYUNIONNAME.item.hItem = tvisW->DUMMYUNIONNAME.item.hItem;
2084 tvisA.DUMMYUNIONNAME.item.state = tvisW->DUMMYUNIONNAME.item.state;
2085 tvisA.DUMMYUNIONNAME.item.stateMask = tvisW->DUMMYUNIONNAME.item.stateMask;
2086 tvisA.DUMMYUNIONNAME.item.cchTextMax = tvisW->DUMMYUNIONNAME.item.cchTextMax;
2088 if(tvisW->DUMMYUNIONNAME.item.pszText)
2090 if (tvisW->DUMMYUNIONNAME.item.pszText!=LPSTR_TEXTCALLBACKW)
2092 int len = lstrlenW (tvisW->DUMMYUNIONNAME.item.pszText)+1;
2093 tvisA.DUMMYUNIONNAME.item.pszText = COMCTL32_Alloc (len);
2094 lstrcpyWtoA (tvisA.DUMMYUNIONNAME.item.pszText,
2095 tvisW->DUMMYUNIONNAME.item.pszText );
2099 tvisA.DUMMYUNIONNAME.item.pszText = LPSTR_TEXTCALLBACKA;
2100 tvisA.DUMMYUNIONNAME.item.cchTextMax = 0;
2104 tvisA.DUMMYUNIONNAME.item.iImage = tvisW->DUMMYUNIONNAME.item.iImage;
2105 tvisA.DUMMYUNIONNAME.item.iSelectedImage = tvisW->DUMMYUNIONNAME.item.iSelectedImage;
2106 tvisA.DUMMYUNIONNAME.item.cChildren = tvisW->DUMMYUNIONNAME.item.cChildren;
2107 tvisA.DUMMYUNIONNAME.item.lParam = tvisW->DUMMYUNIONNAME.item.lParam;
2109 lRes = TREEVIEW_InsertItemA(hwnd,wParam,(LPARAM)&tvisA);
2111 if (tvisA.DUMMYUNIONNAME.item.pszText!=LPSTR_TEXTCALLBACKA)
2113 COMCTL32_Free(tvisA.DUMMYUNIONNAME.item.pszText);
2122 TREEVIEW_DeleteItem (HWND hwnd, WPARAM wParam, LPARAM lParam)
2124 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
2126 TREEVIEW_ITEM *wineItem;
2128 TRACE("item = %08lx\n", lParam);
2130 if (lParam == (INT)TVI_ROOT) {
2131 TREEVIEW_RemoveTree (hwnd);
2133 iItem= (INT) lParam;
2134 wineItem = TREEVIEW_ValidItem (infoPtr, (HTREEITEM)iItem);
2135 if (!wineItem) return FALSE;
2137 if (wineItem->pszText==LPSTR_TEXTCALLBACKA)
2138 TRACE("LPSTR_TEXTCALLBACK\n");
2140 TRACE("%s\n",wineItem->pszText);
2141 TREEVIEW_RemoveItem (hwnd, wineItem);
2144 TREEVIEW_QueueRefresh (hwnd);
2152 TREEVIEW_GetIndent (HWND hwnd)
2154 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
2157 return infoPtr->uIndent;
2161 TREEVIEW_SetIndent (HWND hwnd, WPARAM wParam)
2163 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
2167 newIndent=(INT) wParam;
2168 if (newIndent < MINIMUM_INDENT) newIndent=MINIMUM_INDENT;
2169 infoPtr->uIndent=newIndent;
2175 TREEVIEW_GetToolTips (HWND hwnd)
2178 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
2181 return infoPtr->hwndToolTip;
2186 TREEVIEW_SetToolTips (HWND hwnd, WPARAM wParam)
2189 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
2193 prevToolTip=infoPtr->hwndToolTip;
2194 infoPtr->hwndToolTip= (HWND) wParam;
2200 static LRESULT CALLBACK
2201 TREEVIEW_GetEditControl (HWND hwnd)
2204 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
2206 return infoPtr->hwndEdit;
2210 TREEVIEW_Edit_SubclassProc (HWND hwnd, UINT uMsg, WPARAM wParam,
2218 HDC hdc = (HDC) wParam;
2219 GetClientRect (hwnd, &rc);
2220 Rectangle (hdc, rc.left, rc.top, rc.right, rc.bottom);
2226 return DLGC_WANTARROWS | DLGC_WANTALLKEYS;
2230 if (wParam == VK_ESCAPE)
2232 TREEVIEW_EndEditLabelNow(GetParent(hwnd), (WPARAM)TRUE, 0);
2235 else if (wParam == VK_RETURN)
2236 TREEVIEW_EndEditLabelNow(GetParent(hwnd), (WPARAM)FALSE, 0);
2243 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(GetParent(hwnd));
2245 return CallWindowProcA (infoPtr->wpEditOrig, hwnd, uMsg, wParam, lParam);
2256 /* should handle edit control messages here */
2259 TREEVIEW_Command (HWND hwnd, WPARAM wParam, LPARAM lParam)
2262 TRACE("%x %ld\n",wParam, lParam);
2264 switch (HIWORD(wParam))
2269 * Adjust the edit window size
2271 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
2272 TREEVIEW_ITEM *editItem = TREEVIEW_ValidItem(infoPtr, infoPtr->editItem);
2273 INT iLength = GetWindowTextLengthA(infoPtr->hwndEdit);
2274 HDC hdc = GetDC(infoPtr->hwndEdit);
2277 if ( GetTextMetricsA(hdc, &tm) )
2279 LONG newWidth = (iLength * tm.tmAveCharWidth) + 15;
2284 editItem->text.left - 2,
2285 editItem->text.top - 1,
2287 editItem->text.bottom - editItem->text.top + 3,
2290 ReleaseDC(hwnd, hdc);
2296 /* TREEVIEW_EndEditLabelNow(hwnd, (WPARAM)FALSE, 0);
2301 return SendMessageA (GetParent (hwnd), WM_COMMAND, wParam, lParam);
2308 TREEVIEW_Size (HWND hwnd, WPARAM wParam, LPARAM lParam)
2311 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
2313 if (infoPtr->bAutoSize)
2315 infoPtr->bAutoSize = FALSE;
2318 infoPtr->bAutoSize = TRUE;
2320 if (wParam == SIZE_RESTORED)
2322 infoPtr->uTotalWidth = LOWORD (lParam);
2323 infoPtr->uTotalHeight = HIWORD (lParam);
2325 FIXME("WM_SIZE flag %x %lx not handled\n", wParam, lParam);
2328 TREEVIEW_QueueRefresh (hwnd);
2335 TREEVIEW_StyleChanged (HWND hwnd, WPARAM wParam, LPARAM lParam)
2339 TRACE("(%x %lx)\n",wParam,lParam);
2341 TREEVIEW_Refresh (hwnd, hdc);
2342 ReleaseDC(hwnd,hdc);
2348 TREEVIEW_Create (HWND hwnd, WPARAM wParam, LPARAM lParam)
2350 TREEVIEW_INFO *infoPtr;
2351 DWORD dwStyle = GetWindowLongA (hwnd, GWL_STYLE);
2356 TRACE("wnd %x, style %lx\n",hwnd,dwStyle);
2357 /* allocate memory for info structure */
2358 infoPtr = (TREEVIEW_INFO *) COMCTL32_Alloc (sizeof(TREEVIEW_INFO));
2360 SetWindowLongA( hwnd, 0, (DWORD)infoPtr);
2362 if (infoPtr == NULL) {
2363 ERR("could not allocate info memory!\n");
2367 if ((TREEVIEW_INFO*) GetWindowLongA( hwnd, 0) != infoPtr) {
2368 ERR("pointer assignment error!\n");
2374 /* set default settings */
2375 infoPtr->uInternalStatus=0;
2376 infoPtr->uNumItems=0;
2377 infoPtr->clrBk = GetSysColor (COLOR_WINDOW);
2378 infoPtr->clrText = GetSysColor (COLOR_WINDOWTEXT);
2379 infoPtr->clrLine = GetSysColor (COLOR_WINDOWTEXT);
2380 infoPtr->clrInsertMark = GetSysColor (COLOR_BTNTEXT);
2383 infoPtr->uIndent = 15;
2384 infoPtr->himlNormal = NULL;
2385 infoPtr->himlState = NULL;
2386 infoPtr->uItemHeight = -1;
2387 GetTextMetricsA (hdc, &tm);
2388 infoPtr->hFont = GetStockObject (DEFAULT_GUI_FONT);
2389 GetObjectA (infoPtr->hFont, sizeof (LOGFONTA), &logFont);
2390 logFont.lfWeight=FW_BOLD;
2391 infoPtr->hBoldFont = CreateFontIndirectA (&logFont);
2393 infoPtr->items = NULL;
2394 infoPtr->selectedItem=0;
2395 infoPtr->clrText=-1; /* use system color */
2396 infoPtr->dropItem=0;
2397 infoPtr->insertMarkItem=0;
2398 infoPtr->insertBeforeorAfter=0;
2399 infoPtr->pCallBackSort=NULL;
2400 infoPtr->uScrollTime = 300; /* milliseconds */
2401 infoPtr->wpEditOrig = NULL; /* we haven't subclassed anything yet */
2403 infoPtr->hwndToolTip=0;
2404 if (!(dwStyle & TVS_NOTOOLTIPS)) { /* Create tooltip control */
2407 infoPtr->hwndToolTip =
2408 CreateWindowExA (0, TOOLTIPS_CLASSA, NULL, 0,
2409 CW_USEDEFAULT, CW_USEDEFAULT,
2410 CW_USEDEFAULT, CW_USEDEFAULT,
2413 /* Send NM_TOOLTIPSCREATED notification */
2414 if (infoPtr->hwndToolTip) {
2415 NMTOOLTIPSCREATED nmttc;
2417 nmttc.hdr.hwndFrom = hwnd;
2418 nmttc.hdr.idFrom = GetWindowLongA( hwnd, GWL_ID);
2419 nmttc.hdr.code = NM_TOOLTIPSCREATED;
2420 nmttc.hwndToolTips = infoPtr->hwndToolTip;
2422 SendMessageA (GetParent (hwnd), WM_NOTIFY,
2423 (WPARAM) GetWindowLongA( hwnd, GWL_ID), (LPARAM)&nmttc);
2426 ZeroMemory (&ti, sizeof(TTTOOLINFOA));
2427 ti.cbSize = sizeof(TTTOOLINFOA);
2428 ti.uFlags = TTF_IDISHWND | TTF_TRACK | TTF_TRANSPARENT ;
2431 ti.lpszText = "Test"; /* LPSTR_TEXTCALLBACK; */
2432 SetRectEmpty (&ti.rect);
2434 SendMessageA (infoPtr->hwndToolTip, TTM_ADDTOOLA, 0, (LPARAM)&ti);
2437 infoPtr->hwndEdit = CreateWindowExA (
2441 WS_CHILD | WS_BORDER | ES_AUTOHSCROLL |
2442 ES_WANTRETURN | ES_LEFT,
2445 0,0,0); /* FIXME: (HMENU)IDTVEDIT,pcs->hInstance,0);*/
2447 SendMessageA ( infoPtr->hwndEdit, WM_SETFONT, infoPtr->hFont, FALSE);
2448 infoPtr->wpEditOrig = (WNDPROC)SetWindowLongA (
2451 (LONG) TREEVIEW_Edit_SubclassProc);
2453 if (dwStyle & TVS_CHECKBOXES) {
2457 infoPtr->himlState =
2458 ImageList_Create (16, 16,ILC_COLOR|ILC_MASK, 15, 1);
2460 hbmLoad = LoadBitmapA (COMCTL32_hModule, MAKEINTRESOURCEA(IDT_CHECK));
2461 TRACE ("%x\n",hbmLoad);
2462 nIndex = ImageList_AddMasked (infoPtr->himlState, hbmLoad, CLR_DEFAULT);
2463 TRACE ("%d\n",nIndex);
2464 DeleteObject (hbmLoad);
2466 ReleaseDC (hwnd, hdc);
2473 TREEVIEW_Destroy (HWND hwnd)
2475 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
2478 TREEVIEW_RemoveTree (hwnd);
2479 SetWindowLongA (hwnd, 0, (DWORD)NULL);
2481 if (infoPtr->Timer & TV_REFRESH_TIMER_SET)
2482 KillTimer (hwnd, TV_REFRESH_TIMER);
2483 if (infoPtr->hwndToolTip)
2484 DestroyWindow (infoPtr->hwndToolTip);
2486 COMCTL32_Free (infoPtr);
2492 TREEVIEW_Paint (HWND hwnd, WPARAM wParam, LPARAM lParam)
2498 hdc = wParam==0 ? BeginPaint (hwnd, &ps) : (HDC)wParam;
2499 TREEVIEW_Refresh (hwnd, hdc);
2500 if(!wParam) EndPaint (hwnd, &ps);
2507 TREEVIEW_SetFocus (HWND hwnd, WPARAM wParam, LPARAM lParam)
2509 TREEVIEW_SendSimpleNotify (hwnd, NM_SETFOCUS);
2510 InvalidateRect(hwnd, NULL, FALSE);
2515 TREEVIEW_KillFocus (HWND hwnd, WPARAM wParam, LPARAM lParam)
2517 TREEVIEW_SendSimpleNotify (hwnd, NM_KILLFOCUS);
2518 InvalidateRect(hwnd, NULL, FALSE);
2523 TREEVIEW_EraseBackground (HWND hwnd, WPARAM wParam, LPARAM lParam)
2525 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
2526 HBRUSH hBrush = CreateSolidBrush (infoPtr->clrBk);
2530 GetClientRect (hwnd, &rect);
2531 FillRect ((HDC)wParam, &rect, hBrush);
2532 DeleteObject (hBrush);
2548 TREEVIEW_SendSimpleNotify (HWND hwnd, UINT code)
2553 nmhdr.hwndFrom = hwnd;
2554 nmhdr.idFrom = GetWindowLongA( hwnd, GWL_ID);
2557 return (BOOL) SendMessageA (GetParent (hwnd), WM_NOTIFY,
2558 (WPARAM)nmhdr.idFrom, (LPARAM)&nmhdr);
2564 TREEVIEW_SendTreeviewNotify (HWND hwnd, UINT code, UINT action,
2565 HTREEITEM oldItem, HTREEITEM newItem)
2568 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
2570 TREEVIEW_ITEM *wineItem;
2572 TRACE("code:%x action:%x olditem:%x newitem:%x\n",
2573 code,action,(INT)oldItem,(INT)newItem);
2574 nmhdr.hdr.hwndFrom = hwnd;
2575 nmhdr.hdr.idFrom = GetWindowLongA( hwnd, GWL_ID);
2576 nmhdr.hdr.code = code;
2577 nmhdr.action = action;
2579 wineItem=& infoPtr->items[(INT)oldItem];
2580 nmhdr.itemOld.mask = wineItem->mask;
2581 nmhdr.itemOld.hItem = wineItem->hItem;
2582 nmhdr.itemOld.state = wineItem->state;
2583 nmhdr.itemOld.stateMask = wineItem->stateMask;
2584 nmhdr.itemOld.iImage = wineItem->iImage;
2585 nmhdr.itemOld.pszText = wineItem->pszText;
2586 nmhdr.itemOld.cchTextMax= wineItem->cchTextMax;
2587 nmhdr.itemOld.iImage = wineItem->iImage;
2588 nmhdr.itemOld.iSelectedImage = wineItem->iSelectedImage;
2589 nmhdr.itemOld.cChildren = wineItem->cChildren;
2590 nmhdr.itemOld.lParam = wineItem->lParam;
2594 wineItem=& infoPtr->items[(INT)newItem];
2595 nmhdr.itemNew.mask = wineItem->mask;
2596 nmhdr.itemNew.hItem = wineItem->hItem;
2597 nmhdr.itemNew.state = wineItem->state;
2598 nmhdr.itemNew.stateMask = wineItem->stateMask;
2599 nmhdr.itemNew.iImage = wineItem->iImage;
2600 nmhdr.itemNew.pszText = wineItem->pszText;
2601 nmhdr.itemNew.cchTextMax= wineItem->cchTextMax;
2602 nmhdr.itemNew.iImage = wineItem->iImage;
2603 nmhdr.itemNew.iSelectedImage = wineItem->iSelectedImage;
2604 nmhdr.itemNew.cChildren = wineItem->cChildren;
2605 nmhdr.itemNew.lParam = wineItem->lParam;
2611 return (BOOL)SendMessageA (GetParent (hwnd), WM_NOTIFY,
2612 (WPARAM) GetWindowLongA( hwnd, GWL_ID), (LPARAM)&nmhdr);
2617 TREEVIEW_SendTreeviewDnDNotify (HWND hwnd, UINT code, HTREEITEM dragItem,
2620 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
2622 TREEVIEW_ITEM *wineItem;
2624 TRACE("code:%x dragitem:%x\n", code,(INT)dragItem);
2626 nmhdr.hdr.hwndFrom = hwnd;
2627 nmhdr.hdr.idFrom = GetWindowLongA( hwnd, GWL_ID);
2628 nmhdr.hdr.code = code;
2630 wineItem=& infoPtr->items[(INT)dragItem];
2631 nmhdr.itemNew.mask = wineItem->mask;
2632 nmhdr.itemNew.hItem = wineItem->hItem;
2633 nmhdr.itemNew.state = wineItem->state;
2634 nmhdr.itemNew.lParam = wineItem->lParam;
2636 nmhdr.ptDrag.x = pt.x;
2637 nmhdr.ptDrag.y = pt.y;
2639 return (BOOL)SendMessageA (GetParent (hwnd), WM_NOTIFY,
2640 (WPARAM) GetWindowLongA( hwnd, GWL_ID), (LPARAM)&nmhdr);
2647 TREEVIEW_SendDispInfoNotify (HWND hwnd, TREEVIEW_ITEM *wineItem,
2648 UINT code, UINT what)
2654 TRACE("item %d, action %x, state %d\n",
2655 (INT)wineItem->hItem,
2657 (INT)wineItem->state);
2659 tvdi.hdr.hwndFrom = hwnd;
2660 tvdi.hdr.idFrom = GetWindowLongA( hwnd, GWL_ID);
2661 tvdi.hdr.code = code;
2662 tvdi.item.mask = what;
2663 tvdi.item.hItem = wineItem->hItem;
2664 tvdi.item.state = wineItem->state;
2665 tvdi.item.lParam = wineItem->lParam;
2666 tvdi.item.pszText = COMCTL32_Alloc (128*sizeof(char));
2667 tvdi.item.cchTextMax = 128;
2668 buf = tvdi.item.pszText;
2670 retval=(BOOL)SendMessageA (
2673 (WPARAM)tvdi.hdr.idFrom,
2676 if (what & TVIF_TEXT) {
2677 wineItem->pszText = tvdi.item.pszText;
2678 if (buf==tvdi.item.pszText) {
2679 wineItem->cchTextMax = 128;
2681 TRACE("user-supplied buffer\n");
2682 COMCTL32_Free (buf);
2683 wineItem->cchTextMax = 0;
2686 if (what & TVIF_SELECTEDIMAGE)
2687 wineItem->iSelectedImage = tvdi.item.iSelectedImage;
2688 if (what & TVIF_IMAGE)
2689 wineItem->iImage = tvdi.item.iImage;
2690 if (what & TVIF_CHILDREN)
2691 wineItem->cChildren = tvdi.item.cChildren;
2699 TREEVIEW_SendCustomDrawNotify (HWND hwnd, DWORD dwDrawStage, HDC hdc,
2702 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
2703 NMTVCUSTOMDRAW nmcdhdr;
2704 LPNMCUSTOMDRAW nmcd;
2706 TRACE("drawstage:%lx hdc:%x\n", dwDrawStage, hdc);
2708 nmcd= & nmcdhdr.nmcd;
2709 nmcd->hdr.hwndFrom = hwnd;
2710 nmcd->hdr.idFrom = GetWindowLongA( hwnd, GWL_ID);
2711 nmcd->hdr.code = NM_CUSTOMDRAW;
2712 nmcd->dwDrawStage= dwDrawStage;
2714 nmcd->rc.left = rc.left;
2715 nmcd->rc.right = rc.right;
2716 nmcd->rc.bottom = rc.bottom;
2717 nmcd->rc.top = rc.top;
2718 nmcd->dwItemSpec = 0;
2719 nmcd->uItemState = 0;
2720 nmcd->lItemlParam= 0;
2721 nmcdhdr.clrText = infoPtr->clrText;
2722 nmcdhdr.clrTextBk= infoPtr->clrBk;
2725 return (BOOL)SendMessageA (GetParent (hwnd), WM_NOTIFY,
2726 (WPARAM) GetWindowLongA( hwnd, GWL_ID), (LPARAM)&nmcdhdr);
2732 /* FIXME: need to find out when the flags in uItemState need to be set */
2735 TREEVIEW_SendCustomDrawItemNotify (HWND hwnd, HDC hdc,
2736 TREEVIEW_ITEM *wineItem, UINT uItemDrawState)
2738 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
2739 NMTVCUSTOMDRAW nmcdhdr;
2740 LPNMCUSTOMDRAW nmcd;
2741 DWORD dwDrawStage,dwItemSpec;
2745 dwDrawStage=CDDS_ITEM | uItemDrawState;
2746 dwItemSpec=(DWORD)wineItem->hItem;
2748 if (wineItem->hItem==infoPtr->selectedItem) uItemState|=CDIS_SELECTED;
2749 if (wineItem->hItem==infoPtr->focusItem) uItemState|=CDIS_FOCUS;
2750 if (wineItem->hItem==infoPtr->hotItem) uItemState|=CDIS_HOT;
2752 nmcd= & nmcdhdr.nmcd;
2753 nmcd->hdr.hwndFrom = hwnd;
2754 nmcd->hdr.idFrom = GetWindowLongA( hwnd, GWL_ID);
2755 nmcd->hdr.code = NM_CUSTOMDRAW;
2756 nmcd->dwDrawStage= dwDrawStage;
2758 nmcd->rc.left = wineItem->rect.left;
2759 nmcd->rc.right = wineItem->rect.right;
2760 nmcd->rc.bottom = wineItem->rect.bottom;
2761 nmcd->rc.top = wineItem->rect.top;
2762 nmcd->dwItemSpec = dwItemSpec;
2763 nmcd->uItemState = uItemState;
2764 nmcd->lItemlParam= wineItem->lParam;
2765 nmcdhdr.clrText = infoPtr->clrText;
2766 nmcdhdr.clrTextBk= infoPtr->clrBk;
2767 nmcdhdr.iLevel = wineItem->iLevel;
2769 TRACE("drawstage:%lx hdc:%x item:%lx, itemstate:%x, lItemlParam:%lx\n",
2770 nmcd->dwDrawStage, nmcd->hdc, nmcd->dwItemSpec,
2771 nmcd->uItemState, nmcd->lItemlParam);
2773 retval=SendMessageA (GetParent (hwnd), WM_NOTIFY,
2774 (WPARAM) GetWindowLongA( hwnd, GWL_ID), (LPARAM)&nmcdhdr);
2776 infoPtr->clrText=nmcdhdr.clrText;
2777 infoPtr->clrBk =nmcdhdr.clrTextBk;
2778 return (BOOL) retval;
2783 /* Note:If the specified item is the child of a collapsed parent item,
2784 the parent's list of child items is (recursively) expanded to reveal the
2785 specified item. This is mentioned for TREEVIEW_SelectItem; don't
2786 know if it also applies here.
2790 TREEVIEW_Expand (HWND hwnd, WPARAM wParam, LPARAM lParam)
2792 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
2793 TREEVIEW_ITEM *wineItem;
2797 flag = (UINT) wParam;
2798 expand = (INT) lParam;
2800 wineItem = TREEVIEW_ValidItem (infoPtr, (HTREEITEM)expand);
2804 if (!wineItem->cChildren)
2807 if (wineItem->pszText==LPSTR_TEXTCALLBACKA)
2808 TRACE ("For item %d, flags %d, state %d\n",
2809 expand, flag, wineItem->state);
2811 TRACE("For (%s) item:%d, flags %x, state:%d\n",
2812 wineItem->pszText, flag, expand, wineItem->state);
2814 if (wineItem->cChildren==I_CHILDRENCALLBACK) {
2815 FIXME("we don't handle I_CHILDRENCALLBACK yet\n");
2819 if (flag == TVE_TOGGLE) { /* FIXME: check exact behaviour here */
2820 flag &= ~TVE_TOGGLE; /* ie: bitwise ops or 'case' ops */
2821 if (wineItem->state & TVIS_EXPANDED)
2822 flag |= TVE_COLLAPSE;
2829 case TVE_COLLAPSERESET:
2830 TRACE(" case TVE_COLLAPSERESET\n");
2831 if (!wineItem->state & TVIS_EXPANDED)
2834 wineItem->state &= ~(TVIS_EXPANDEDONCE | TVIS_EXPANDED);
2835 TREEVIEW_RemoveAllChildren (hwnd, wineItem);
2839 TRACE(" case TVE_COLLAPSE\n");
2840 if (!wineItem->state & TVIS_EXPANDED)
2843 wineItem->state &= ~TVIS_EXPANDED;
2847 TRACE(" case TVE_EXPAND\n");
2848 if (wineItem->state & TVIS_EXPANDED)
2851 TRACE(" is not expanded...\n");
2853 if (!(wineItem->state & TVIS_EXPANDEDONCE))
2855 TRACE(" and has never been expanded...\n");
2856 wineItem->state |= TVIS_EXPANDED;
2858 /* this item has never been expanded */
2859 if (TREEVIEW_SendTreeviewNotify (
2866 TRACE(" TVN_ITEMEXPANDINGA returned TRUE, exiting...\n");
2871 * Since the TVN_ITEMEXPANDINGA message may has caused the parent to
2872 * insert new items which in turn may have cause items placeholder
2873 * reallocation, I reassign the current item pointer so we have
2874 * something valid to work with...
2875 * However, this should not be necessary,
2876 * investigation required in TREEVIEW_InsertItemA
2878 wineItem = TREEVIEW_ValidItem (infoPtr, (HTREEITEM)expand);
2882 "Catastropic situation, cannot retreive item #%d\n",
2887 wineItem->state |= TVIS_EXPANDEDONCE;
2888 TRACE(" TVN_ITEMEXPANDINGA sent...\n");
2890 TREEVIEW_SendTreeviewNotify (
2897 TRACE(" TVN_ITEMEXPANDEDA sent...\n");
2902 /* this item has already been expanded */
2903 wineItem->state |= TVIS_EXPANDED;
2907 case TVE_EXPANDPARTIAL:
2908 TRACE(" case TVE_EXPANDPARTIAL\n");
2909 FIXME("TVE_EXPANDPARTIAL not implemented\n");
2910 wineItem->state ^=TVIS_EXPANDED;
2911 wineItem->state |=TVIS_EXPANDEDONCE;
2915 TRACE("Exiting, Item %d state is now %d...\n",
2919 TREEVIEW_QueueRefresh (hwnd);
2925 static TREEVIEW_ITEM *
2926 TREEVIEW_HitTestPoint (HWND hwnd, POINT pt)
2928 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
2929 TREEVIEW_ITEM *wineItem;
2932 GetClientRect (hwnd, &rect);
2934 if (!infoPtr->firstVisible) return NULL;
2936 wineItem=&infoPtr->items [(INT)infoPtr->firstVisible];
2938 while ((wineItem!=NULL) && (pt.y > wineItem->rect.bottom))
2939 wineItem=TREEVIEW_GetNextListItem (infoPtr,wineItem);
2951 TREEVIEW_HitTest (HWND hwnd, LPARAM lParam)
2953 LPTVHITTESTINFO lpht=(LPTVHITTESTINFO) lParam;
2954 TREEVIEW_ITEM *wineItem;
2958 GetClientRect (hwnd, &rect);
2962 if (x < rect.left) status|=TVHT_TOLEFT;
2963 if (x > rect.right) status|=TVHT_TORIGHT;
2964 if (y < rect.top ) status|=TVHT_ABOVE;
2965 if (y > rect.bottom) status|=TVHT_BELOW;
2972 wineItem=TREEVIEW_HitTestPoint (hwnd, lpht->pt);
2974 lpht->flags=TVHT_NOWHERE;
2980 if (x < wineItem->expandBox.left) {
2981 lpht->flags |= TVHT_ONITEMINDENT;
2984 if ( PtInRect ( &wineItem->expandBox, lpht->pt)) {
2985 lpht->flags |= TVHT_ONITEMBUTTON;
2988 if ( PtInRect ( &wineItem->bitmap, lpht->pt)) {
2989 lpht->flags |= TVHT_ONITEMICON;
2992 if ( PtInRect ( &wineItem->statebitmap, lpht->pt)) {
2993 lpht->flags |= TVHT_ONITEMSTATEICON;
2996 if ( PtInRect ( &wineItem->text, lpht->pt)) {
2997 lpht->flags |= TVHT_ONITEMLABEL;
3001 lpht->flags|=TVHT_ONITEMRIGHT;
3005 lpht->hItem=wineItem->hItem;
3006 TRACE ("(%ld,%ld):result %x\n",lpht->pt.x,lpht->pt.y,lpht->flags);
3008 return (LRESULT) wineItem->hItem;
3012 TREEVIEW_EditLabelA (HWND hwnd, WPARAM wParam, LPARAM lParam)
3014 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
3015 TREEVIEW_ITEM *wineItem;
3018 * If the style allow editing...
3020 if ( GetWindowLongA( hwnd, GWL_STYLE) & TVS_EDITLABELS )
3023 if ( infoPtr->editItem == 0 ) /* If we are not curently editing */
3025 wineItem = TREEVIEW_ValidItem(infoPtr,(HTREEITEM) lParam);
3026 if ( wineItem == NULL )
3028 ERR("Cannot get valid TREEVIEW_ITEM for lParam\n");
3032 TRACE("Edit started for %s.\n", wineItem->pszText);
3033 infoPtr->editItem = wineItem->hItem;
3037 * It is common practice for a windows program to get this
3038 * edit control and then subclass it. It is assumed that a
3039 * new edit control is given every time.
3041 * As a result some programs really mess up the edit control
3042 * so we need to destory our old edit control and create a new
3043 * one. Recycling would be nice but we would need to reset
3044 * everything. So recreating may just be easyier
3047 DestroyWindow(infoPtr->hwndEdit);
3048 infoPtr->hwndEdit = CreateWindowExA (
3052 WS_CHILD | WS_BORDER | ES_AUTOHSCROLL |
3053 ES_WANTRETURN | ES_LEFT,
3056 0,0,0); /* FIXME: (HMENU)IDTVEDIT,pcs->hInstance,0);*/
3061 (LONG) TREEVIEW_Edit_SubclassProc);
3064 SendMessageA ( infoPtr->hwndEdit, WM_SETFONT, infoPtr->hFont, FALSE);
3066 SetWindowTextA( infoPtr->hwndEdit, wineItem->pszText );
3067 SendMessageA ( infoPtr->hwndEdit, EM_SETSEL, 0, -1 );
3070 ** NOTE: this must be after the edit control is created
3071 ** (according to TVN_BEGINLABELEDITA docs), before position is set.
3073 if ( TREEVIEW_SendDispInfoNotify( /* Return true to cancel edition */
3076 TVN_BEGINLABELEDITA,
3080 ** FIXME: Is this right, should we return a handle even though edit was cancelled?
3082 TRACE("Edit cancelled by TVN_BEGINLABELEDITA for %s.\n", wineItem->pszText);
3084 TREEVIEW_EndEditLabelNow(hwnd, (WPARAM)TRUE, 0);
3092 wineItem->text.left - 2,
3093 wineItem->text.top - 1,
3094 wineItem->text.right - wineItem->text.left + 20 ,
3095 wineItem->text.bottom - wineItem->text.top + 3,
3098 SetFocus ( infoPtr->hwndEdit);
3099 ShowWindow ( infoPtr->hwndEdit, SW_SHOW);
3105 ** return NULL since we cannot edit this.
3111 return infoPtr->hwndEdit;
3115 TREEVIEW_EndEditLabelNow (HWND hwnd, WPARAM wParam, LPARAM lParam)
3117 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
3118 TREEVIEW_ITEM *editedItem = TREEVIEW_ValidItem (infoPtr, infoPtr->editItem);
3119 BOOL bRevert = (BOOL)wParam;
3127 ** NOTE: we have to get the new text before calling TVN_ENDLABELEDITA
3128 ** since some apps(eg regedit, win98) validate the text in TVN_ENDLABELEDITA.
3131 oldText = editedItem->pszText;
3133 if ( !bRevert ) /* wParam is set to true to cancel the edition */
3136 iLength = GetWindowTextLengthA( infoPtr->hwndEdit );
3139 WARN("Zero length string for new label(not changing).");
3143 newText = COMCTL32_Alloc( iLength+1 );
3144 if( newText == NULL )
3146 ERR("OutOfMemory, cannot allocate space for label");
3149 GetWindowTextA( infoPtr->hwndEdit, newText, iLength+1);
3158 * notify our parent with the new string(or NULL if wParam==TRUE)
3160 tvdi.hdr.hwndFrom = hwnd;
3161 tvdi.hdr.idFrom = GetWindowLongA( hwnd, GWL_ID);
3162 tvdi.hdr.code = TVN_ENDLABELEDITA;
3163 tvdi.item.hItem = editedItem->hItem;
3164 tvdi.item.lParam = editedItem->lParam;
3165 tvdi.item.mask = TVIF_TEXT|TVIF_HANDLE|TVIF_PARAM;
3166 tvdi.item.pszText = newText;
3168 if(!SendMessageA ( /* return false to cancel edition */
3171 (WPARAM)tvdi.hdr.idFrom,
3174 if( newText == NULL ) /*we are supposed to ignore the return if (and pszText==NULL), MSDOCs */
3178 if (oldText != LPSTR_TEXTCALLBACKA)
3183 if( newText != NULL )
3184 COMCTL32_Free(newText);
3186 editedItem->pszText=oldText; /* revert back to the old label */
3190 COMCTL32_Free(oldText);
3192 editedItem->pszText=newText; /* use the new label */
3195 else if( newText!=NULL )
3198 ** Is really this necessary? shouldnt an app update its internal data in TVN_ENDLABELEDITA?
3203 * This is a callback string so we need
3204 * to inform the parent that the string
3208 tvdi.hdr.hwndFrom = hwnd;
3209 tvdi.hdr.idFrom = GetWindowLongA( hwnd, GWL_ID);
3210 tvdi.hdr.code = TVN_SETDISPINFOA;
3211 tvdi.item.mask = TVIF_TEXT;
3212 tvdi.item.pszText = newText;
3217 (WPARAM)tvdi.hdr.idFrom,
3222 COMCTL32_Free(newText);
3226 ShowWindow(infoPtr->hwndEdit, SW_HIDE);
3227 EnableWindow(infoPtr->hwndEdit, FALSE);
3229 /* update the window to eliminate fragments and the like */
3230 TreeView_GetItemRect(hwnd,infoPtr->editItem,&itemRect,FALSE);
3231 RedrawWindow(hwnd,&itemRect,0,RDW_ERASE|RDW_INVALIDATE|RDW_UPDATENOW);
3233 infoPtr->editItem = 0;
3235 return !bRevert; /* return true if label edit succesful, otherwise false */
3241 TREEVIEW_LButtonDoubleClick (HWND hwnd, WPARAM wParam, LPARAM lParam)
3243 TREEVIEW_ITEM *wineItem;
3247 pt.x = (INT)LOWORD(lParam);
3248 pt.y = (INT)HIWORD(lParam);
3251 wineItem=TREEVIEW_HitTestPoint (hwnd, pt);
3252 if (!wineItem) return 0;
3253 TRACE("item %d \n",(INT)wineItem->hItem);
3255 if (TREEVIEW_SendSimpleNotify (hwnd, NM_DBLCLK)!=TRUE) { /* FIXME!*/
3256 TREEVIEW_Expand (hwnd, (WPARAM) TVE_TOGGLE, (LPARAM) wineItem->hItem);
3263 TREEVIEW_LButtonDown (HWND hwnd, WPARAM wParam, LPARAM lParam)
3265 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
3269 ht.pt.x = (INT)LOWORD(lParam);
3270 ht.pt.y = (INT)HIWORD(lParam);
3273 iItem=TREEVIEW_HitTest (hwnd, (LPARAM) &ht);
3274 TRACE("item %d \n",iItem);
3276 if (ht.flags & TVHT_ONITEMBUTTON) {
3277 TREEVIEW_Expand (hwnd, (WPARAM) TVE_TOGGLE, (LPARAM) iItem);
3281 infoPtr->uInternalStatus|=TV_LDRAG;
3288 TREEVIEW_LButtonUp (HWND hwnd, WPARAM wParam, LPARAM lParam)
3290 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
3292 TREEVIEW_ITEM *wineItem;
3294 DWORD dwStyle = GetWindowLongA (hwnd, GWL_STYLE);
3296 ht.pt.x = (INT)LOWORD(lParam);
3297 ht.pt.y = (INT)HIWORD(lParam);
3301 /* Return true to cancel default behaviour */
3302 if ( TREEVIEW_SendSimpleNotify (hwnd, NM_CLICK) )
3306 iItem = TREEVIEW_HitTest (hwnd, (LPARAM) &ht);
3307 TRACE ("%d\n",iItem);
3311 wineItem = TREEVIEW_ValidItem(infoPtr, (HTREEITEM)iItem);
3314 * if we are TVS_SINGLEEXPAND then we want this single click to
3315 * do a bunch of things.
3317 if ((dwStyle & TVS_SINGLEEXPAND)&&
3318 ( ht.flags & (TVHT_ONITEMLABEL | TVHT_ONITEMICON))&&
3319 ( infoPtr->editItem == 0 ))
3321 TREEVIEW_ITEM *SelItem;
3323 * Send the notification
3325 TREEVIEW_SendTreeviewNotify (hwnd,TVN_SINGLEEXPAND,0,
3326 (HTREEITEM)iItem,0);
3328 * Close the previous selection all the way to the root
3329 * as long as the new selection is not a child
3332 if ((infoPtr->selectedItem)&&(infoPtr->selectedItem != (HTREEITEM)iItem))
3334 BOOL closeit = TRUE;
3337 while (closeit && SelItem)
3339 closeit = (SelItem->hItem != infoPtr->selectedItem);
3340 SelItem = TREEVIEW_ValidItem(infoPtr,SelItem->parent);
3345 SelItem = TREEVIEW_ValidItem(infoPtr,infoPtr->selectedItem);
3346 while ((SelItem)&&(SelItem->hItem != wineItem->hItem))
3348 TREEVIEW_Expand (hwnd,(WPARAM)TVE_COLLAPSE,(LPARAM)SelItem->hItem);
3349 SelItem = TREEVIEW_ValidItem(infoPtr,SelItem->parent);
3355 * Expand the current item
3357 TREEVIEW_Expand (hwnd, (WPARAM) TVE_TOGGLE, (LPARAM) wineItem->hItem);
3360 infoPtr->uInternalStatus &= ~(TV_LDRAG | TV_LDRAGGING);
3363 * If the style allow editing and the node is already selected
3364 * and the click occured on the item label...
3366 if ( ( GetWindowLongA( hwnd, GWL_STYLE) & TVS_EDITLABELS ) &&
3367 ( wineItem->state & TVIS_SELECTED ) &&
3368 ( ht.flags & TVHT_ONITEMLABEL ))
3370 if ( infoPtr->editItem == 0 ) /* If we are not curently editing */
3372 if( SendMessageA(hwnd, TVM_EDITLABELA, 0, (LPARAM)iItem) == 0)
3376 else if ( infoPtr->editItem != 0 ) /* If we are curently editing */
3378 TREEVIEW_EndEditLabelNow(hwnd, (WPARAM)FALSE, 0);
3380 else if ( ht.flags & (TVHT_ONITEMLABEL | TVHT_ONITEMICON))
3382 TREEVIEW_DoSelectItem ( hwnd, TVGN_CARET, (HTREEITEM)iItem, TVC_BYMOUSE);
3385 if (ht.flags & TVHT_ONITEMSTATEICON) {
3388 if (dwStyle & TVS_CHECKBOXES) { /* TVS_CHECKBOXES requires _us_ */
3389 int state; /* to toggle the current state */
3390 state=1-(wineItem->state>>12);
3391 TRACE ("state:%x\n", state);
3392 wineItem->state&= ~TVIS_STATEIMAGEMASK;
3393 wineItem->state|=state<<12;
3394 TRACE ("state:%x\n", wineItem->state);
3395 TREEVIEW_QueueRefresh (hwnd);
3403 TREEVIEW_RButtonDown (HWND hwnd, WPARAM wParam, LPARAM lParam)
3405 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
3408 infoPtr->uInternalStatus|=TV_RDRAG;
3413 TREEVIEW_RButtonUp (HWND hwnd, WPARAM wParam, LPARAM lParam)
3415 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
3418 if (TREEVIEW_SendSimpleNotify (hwnd, NM_RCLICK)) return 0;
3419 infoPtr->uInternalStatus&= ~(TV_RDRAG | TV_RDRAGGING);
3425 TREEVIEW_MouseMove (HWND hwnd, WPARAM wParam, LPARAM lParam)
3427 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
3428 TREEVIEW_ITEM *hotItem;
3431 pt.x=(INT) LOWORD (lParam);
3432 pt.y=(INT) HIWORD (lParam);
3433 hotItem=TREEVIEW_HitTestPoint (hwnd, pt);
3434 if (!hotItem) return 0;
3435 infoPtr->focusItem=hotItem->hItem;
3437 if ( GetWindowLongA( hwnd, GWL_STYLE) & TVS_DISABLEDRAGDROP) return 0;
3439 if (infoPtr->uInternalStatus & TV_LDRAG) {
3440 TREEVIEW_SendTreeviewDnDNotify (hwnd, TVN_BEGINDRAGA, hotItem->hItem, pt);
3441 infoPtr->uInternalStatus &= ~TV_LDRAG;
3442 infoPtr->uInternalStatus |= TV_LDRAGGING;
3443 infoPtr->dropItem=hotItem->hItem;
3447 if (infoPtr->uInternalStatus & TV_RDRAG) {
3448 TREEVIEW_SendTreeviewDnDNotify (hwnd, TVN_BEGINRDRAGA, hotItem->hItem, pt);
3449 infoPtr->uInternalStatus &= ~TV_RDRAG;
3450 infoPtr->uInternalStatus |= TV_RDRAGGING;
3451 infoPtr->dropItem=hotItem->hItem;
3460 TREEVIEW_CreateDragImage (HWND hwnd, WPARAM wParam, LPARAM lParam)
3462 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
3463 TREEVIEW_ITEM *dragItem;
3467 HBITMAP hbmp,hOldbmp;
3474 if (!(infoPtr->himlNormal)) return 0;
3475 dragItem=TREEVIEW_ValidItem (infoPtr, (HTREEITEM) lParam);
3477 if (!dragItem) return 0;
3479 if (dragItem->pszText==LPSTR_TEXTCALLBACKA) {
3480 TREEVIEW_SendDispInfoNotify (hwnd, dragItem, TVN_GETDISPINFOA, TVIF_TEXT);
3482 itemtxt=dragItem->pszText;
3484 hwtop=GetDesktopWindow ();
3485 htopdc= GetDC (hwtop);
3486 hdc=CreateCompatibleDC (htopdc);
3488 hOldFont=SelectObject (hdc, infoPtr->hFont);
3489 GetTextExtentPoint32A (hdc, itemtxt, lstrlenA (itemtxt), &size);
3490 TRACE("%d %d %s %d\n",size.cx,size.cy,itemtxt,lstrlenA(itemtxt));
3491 hbmp=CreateCompatibleBitmap (htopdc, size.cx, size.cy);
3492 hOldbmp=SelectObject (hdc, hbmp);
3494 ImageList_GetIconSize (infoPtr->himlNormal, &cx, &cy);
3496 if (cy>size.cy) size.cy=cy;
3498 infoPtr->dragList=ImageList_Create (size.cx, size.cy, ILC_COLOR, 10, 10);
3499 ImageList_Draw (infoPtr->himlNormal, dragItem->iImage, hdc, 0, 0, ILD_NORMAL);
3502 ImageList_GetImageInfo (infoPtr->himlNormal, dragItem->hItem, &iminfo);
3503 ImageList_AddMasked (infoPtr->dragList, iminfo.hbmImage, CLR_DEFAULT);
3506 /* draw item text */
3508 SetRect (&rc, cx, 0, size.cx,size.cy);
3509 DrawTextA (hdc, itemtxt, lstrlenA (itemtxt), &rc, DT_LEFT);
3510 SelectObject (hdc, hOldFont);
3511 SelectObject (hdc, hOldbmp);
3513 ImageList_Add (infoPtr->dragList, hbmp, 0);
3516 DeleteObject (hbmp);
3517 ReleaseDC (hwtop, htopdc);
3519 return (LRESULT)infoPtr->dragList;
3524 TREEVIEW_DoSelectItem (HWND hwnd, INT action, HTREEITEM newSelect, INT cause)
3527 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
3528 TREEVIEW_ITEM *prevItem,*wineItem;
3531 wineItem = TREEVIEW_ValidItem (infoPtr, (HTREEITEM)newSelect);
3533 TRACE("Entering item %d, flag %x, cause %x, state %d\n",
3539 if ( (wineItem) && (wineItem->parent))
3542 * If the item has a collapse parent expand the parent so he
3543 * can expose the item
3545 TREEVIEW_ITEM *parentItem = TREEVIEW_ValidItem (infoPtr, wineItem->parent);
3546 if ( !(parentItem->state & TVIS_EXPANDED))
3547 TREEVIEW_Expand (hwnd, TVE_EXPAND, (LPARAM) wineItem->parent);
3553 prevSelect=(INT)infoPtr->selectedItem;
3555 if ((HTREEITEM)prevSelect==newSelect)
3558 prevItem= TREEVIEW_ValidItem (infoPtr, (HTREEITEM)prevSelect);
3561 if (TREEVIEW_SendTreeviewNotify(
3565 (HTREEITEM)prevSelect,
3566 (HTREEITEM)newSelect))
3567 return FALSE; /* FIXME: OK? */
3570 prevItem->state &= ~TVIS_SELECTED;
3572 wineItem->state |= TVIS_SELECTED;
3574 infoPtr->selectedItem=(HTREEITEM)newSelect;
3576 TREEVIEW_SendTreeviewNotify(
3580 (HTREEITEM)prevSelect,
3581 (HTREEITEM)newSelect);
3585 case TVGN_DROPHILITE:
3586 prevItem= TREEVIEW_ValidItem (infoPtr, infoPtr->dropItem);
3589 prevItem->state &= ~TVIS_DROPHILITED;
3591 infoPtr->dropItem=(HTREEITEM)newSelect;
3594 wineItem->state |=TVIS_DROPHILITED;
3598 case TVGN_FIRSTVISIBLE:
3599 FIXME("FIRSTVISIBLE not implemented\n");
3603 TREEVIEW_QueueRefresh (hwnd);
3605 TRACE("Leaving state %d\n", wineItem->state);
3609 /* FIXME: handle NM_KILLFocus etc */
3611 TREEVIEW_SelectItem (HWND hwnd, WPARAM wParam, LPARAM lParam)
3614 return TREEVIEW_DoSelectItem (hwnd, wParam, (HTREEITEM) lParam, TVC_UNKNOWN);
3621 TREEVIEW_GetFont (HWND hwnd, WPARAM wParam, LPARAM lParam)
3624 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
3626 TRACE("%x\n",infoPtr->hFont);
3627 return infoPtr->hFont;
3631 TREEVIEW_SetFont (HWND hwnd, WPARAM wParam, LPARAM lParam)
3634 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
3637 HFONT hFont, hOldFont;
3641 TRACE("%x %lx\n",wParam, lParam);
3643 infoPtr->hFont = (HFONT)wParam;
3645 hFont = infoPtr->hFont ? infoPtr->hFont : GetStockObject (SYSTEM_FONT);
3647 GetObjectA (infoPtr->hFont, sizeof (LOGFONTA), &logFont);
3648 logFont.lfWeight=FW_BOLD;
3649 infoPtr->hBoldFont = CreateFontIndirectA (&logFont);
3652 hOldFont = SelectObject (hdc, hFont);
3653 GetTextMetricsA (hdc, &tm);
3654 height= tm.tmHeight + tm.tmExternalLeading;
3655 if (height>infoPtr->uRealItemHeight)
3656 infoPtr->uRealItemHeight=height;
3657 SelectObject (hdc, hOldFont);
3661 TREEVIEW_QueueRefresh (hwnd);
3669 TREEVIEW_VScroll (HWND hwnd, WPARAM wParam, LPARAM lParam)
3672 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
3675 TRACE("wp %x, lp %lx\n", wParam, lParam);
3676 if (!infoPtr->uInternalStatus & TV_VSCROLL) return FALSE;
3678 switch (LOWORD (wParam)) {
3680 if (!infoPtr->cy) return FALSE;
3681 infoPtr->cy -= infoPtr->uRealItemHeight;
3682 if (infoPtr->cy < 0) infoPtr->cy=0;
3685 maxHeight=infoPtr->uTotalHeight-infoPtr->uVisibleHeight;
3686 if (infoPtr->cy == maxHeight) return FALSE;
3687 infoPtr->cy += infoPtr->uRealItemHeight;
3688 if (infoPtr->cy > maxHeight)
3689 infoPtr->cy = maxHeight;
3692 if (!infoPtr->cy) return FALSE;
3693 infoPtr->cy -= infoPtr->uVisibleHeight;
3694 if (infoPtr->cy < 0) infoPtr->cy=0;
3697 maxHeight=infoPtr->uTotalHeight-infoPtr->uVisibleHeight;
3698 if (infoPtr->cy == maxHeight) return FALSE;
3699 infoPtr->cy += infoPtr->uVisibleHeight;
3700 if (infoPtr->cy > maxHeight)
3701 infoPtr->cy = maxHeight;
3704 infoPtr->cy = HIWORD (wParam);
3709 TREEVIEW_QueueRefresh (hwnd);
3714 TREEVIEW_HScroll (HWND hwnd, WPARAM wParam, LPARAM lParam)
3716 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
3719 TRACE("wp %lx, lp %x\n", lParam, wParam);
3721 if (!infoPtr->uInternalStatus & TV_HSCROLL) return FALSE;
3723 switch (LOWORD (wParam)) {
3725 if (!infoPtr->cx) return FALSE;
3726 infoPtr->cx -= infoPtr->uRealItemHeight;
3727 if (infoPtr->cx < 0) infoPtr->cx=0;
3730 maxWidth=infoPtr->uTotalWidth-infoPtr->uVisibleWidth;
3731 if (infoPtr->cx == maxWidth) return FALSE;
3732 infoPtr->cx += infoPtr->uRealItemHeight; /*FIXME */
3733 if (infoPtr->cx > maxWidth)
3734 infoPtr->cx = maxWidth;
3737 if (!infoPtr->cx) return FALSE;
3738 infoPtr->cx -= infoPtr->uVisibleWidth;
3739 if (infoPtr->cx < 0) infoPtr->cx=0;
3742 maxWidth=infoPtr->uTotalWidth-infoPtr->uVisibleWidth;
3743 if (infoPtr->cx == maxWidth) return FALSE;
3744 infoPtr->cx += infoPtr->uVisibleWidth;
3745 if (infoPtr->cx > maxWidth)
3746 infoPtr->cx = maxWidth;
3749 infoPtr->cx = HIWORD (wParam);
3754 TREEVIEW_QueueRefresh (hwnd);
3758 static LRESULT TREEVIEW_MouseWheel (HWND hwnd, WPARAM wParam, LPARAM lParam)
3761 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
3762 short gcWheelDelta = 0;
3763 UINT pulScrollLines = 3;
3765 SystemParametersInfoW(SPI_GETWHEELSCROLLLINES,0, &pulScrollLines, 0);
3767 gcWheelDelta -= (short) HIWORD(wParam);
3768 pulScrollLines *= (gcWheelDelta / WHEEL_DELTA);
3770 if (abs(gcWheelDelta) >= WHEEL_DELTA && pulScrollLines)
3772 int wheelDy = pulScrollLines * infoPtr->uRealItemHeight;
3773 int newDy = infoPtr->cy + wheelDy;
3774 int maxDy = infoPtr->uTotalHeight - infoPtr->uVisibleHeight;
3776 if (newDy > maxDy) newDy = maxDy;
3777 if (newDy < 0) newDy = 0;
3779 if (newDy == infoPtr->cy) return TRUE;
3781 TREEVIEW_VScroll(hwnd, MAKEWPARAM(SB_THUMBTRACK,newDy),0);
3787 TREEVIEW_KeyDown (HWND hwnd, WPARAM wParam, LPARAM lParam)
3789 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
3790 HTREEITEM hNewSelection = 0;
3791 INT scrollNeeds = -1;
3792 INT cyChangeNeeds = -1;
3793 INT prevSelect = (INT)infoPtr->selectedItem;
3795 TREEVIEW_ITEM *prevItem =
3796 (prevSelect != 0 ) ?
3797 TREEVIEW_ValidItem (infoPtr, (HTREEITEM)prevSelect) :
3800 TREEVIEW_ITEM *newItem = NULL;
3802 TRACE("%x %lx\n",wParam, lParam);
3804 if (prevSelect == 0)
3809 newItem=TREEVIEW_GetPrevListItem (infoPtr, prevItem);
3812 newItem=& infoPtr->items[(INT)infoPtr->TopRootItem];
3814 hNewSelection = newItem->hItem;
3816 if (! newItem->visible)
3817 scrollNeeds = SB_LINEUP;
3821 newItem=TREEVIEW_GetNextListItem (infoPtr, prevItem);
3826 hNewSelection = newItem->hItem;
3828 if (! newItem->visible)
3829 scrollNeeds = SB_LINEDOWN;
3834 newItem = &infoPtr->items[(INT)infoPtr->TopRootItem];
3835 hNewSelection = newItem->hItem;
3840 newItem = &infoPtr->items[(INT)infoPtr->TopRootItem];
3841 newItem = TREEVIEW_GetLastListItem (infoPtr, newItem);
3842 hNewSelection = newItem->hItem;
3844 if (! newItem->visible)
3845 cyChangeNeeds = infoPtr->uTotalHeight-infoPtr->uVisibleHeight;
3850 if ( (prevItem->cChildren > 0) && (prevItem->state & TVIS_EXPANDED) )
3852 TREEVIEW_Expand(hwnd, TVE_COLLAPSE, prevSelect );
3854 else if ((INT)prevItem->parent)
3856 newItem = (& infoPtr->items[(INT)prevItem->parent]);
3857 if (! newItem->visible)
3858 /* FIXME find a way to make this item the first visible... */
3861 hNewSelection = newItem->hItem;
3867 if ( ( prevItem->cChildren > 0) ||
3868 ( prevItem->cChildren == I_CHILDRENCALLBACK))
3870 if (! (prevItem->state & TVIS_EXPANDED))
3871 TREEVIEW_Expand(hwnd, TVE_EXPAND, prevSelect );
3874 newItem = (& infoPtr->items[(INT)prevItem->firstChild]);
3875 hNewSelection = newItem->hItem;
3882 if (! (prevItem->state & TVIS_EXPANDED))
3883 TREEVIEW_Expand(hwnd, TVE_EXPAND, prevSelect );
3887 if (prevItem->state & TVIS_EXPANDED)
3888 TREEVIEW_Expand(hwnd, TVE_COLLAPSE, prevSelect );
3893 newItem=TREEVIEW_GetListItem(
3896 -1*(TREEVIEW_GetVisibleCount(hwnd,0,0)-3));
3900 hNewSelection = newItem->hItem;
3902 if (! newItem->visible)
3903 scrollNeeds = SB_PAGEUP;
3908 newItem=TREEVIEW_GetListItem(
3911 TREEVIEW_GetVisibleCount(hwnd,0,0)-3);
3916 hNewSelection = newItem->hItem;
3918 if (! newItem->visible)
3919 scrollNeeds = SB_PAGEDOWN;
3928 FIXME("%x not implemented\n", wParam);
3935 This works but does not send notification...
3937 prevItem->state &= ~TVIS_SELECTED;
3938 newItem->state |= TVIS_SELECTED;
3939 infoPtr->selectedItem = hNewSelection;
3940 TREEVIEW_QueueRefresh (hwnd);
3943 if ( TREEVIEW_DoSelectItem(
3946 (HTREEITEM)hNewSelection,
3949 /* If selection change is allowed for the new item, perform scrolling */
3950 if (scrollNeeds != -1)
3951 TREEVIEW_VScroll(hwnd, scrollNeeds, 0);
3953 if (cyChangeNeeds != -1)
3954 infoPtr->cy = cyChangeNeeds;
3956 /* FIXME: Something happen in the load the in the two weeks before
3957 april 1st 1999 which makes this SetFocus mandatory otherwise, the focus
3958 is lost... However the SetFocus should not be required...*/
3969 TREEVIEW_GetScrollTime (HWND hwnd)
3971 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
3973 return infoPtr->uScrollTime;
3978 TREEVIEW_SetScrollTime (HWND hwnd, UINT uScrollTime)
3980 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
3981 UINT uOldScrollTime = infoPtr->uScrollTime;
3983 infoPtr->uScrollTime = min (uScrollTime, 100);
3985 return uOldScrollTime;
3989 static LRESULT WINAPI
3990 TREEVIEW_WindowProc (HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
3992 TREEVIEW_INFO *infoPtr;
3993 if (uMsg==WM_CREATE)
3994 return TREEVIEW_Create (hwnd, wParam, lParam);
3996 if (!(infoPtr = TREEVIEW_GetInfoPtr(hwnd)))
3997 return DefWindowProcA (hwnd, uMsg, wParam, lParam);
4001 case TVM_INSERTITEMA:
4002 return TREEVIEW_InsertItemA (hwnd, wParam, lParam);
4004 case TVM_INSERTITEMW:
4005 return TREEVIEW_InsertItemW(hwnd,wParam,lParam);;
4007 case TVM_DELETEITEM:
4008 return TREEVIEW_DeleteItem (hwnd, wParam, lParam);
4011 return TREEVIEW_Expand (hwnd, wParam, lParam);
4013 case TVM_GETITEMRECT:
4014 return TREEVIEW_GetItemRect (hwnd, wParam, lParam);
4017 return TREEVIEW_GetCount (hwnd, wParam, lParam);
4020 return TREEVIEW_GetIndent (hwnd);
4023 return TREEVIEW_SetIndent (hwnd, wParam);
4025 case TVM_GETIMAGELIST:
4026 return TREEVIEW_GetImageList (hwnd, wParam, lParam);
4028 case TVM_SETIMAGELIST:
4029 return TREEVIEW_SetImageList (hwnd, wParam, lParam);
4031 case TVM_GETNEXTITEM:
4032 return TREEVIEW_GetNextItem (hwnd, wParam, lParam);
4034 case TVM_SELECTITEM:
4035 return TREEVIEW_SelectItem (hwnd, wParam, lParam);
4038 return TREEVIEW_GetItemA (hwnd, wParam, lParam);
4041 return TREEVIEW_GetItemW (hwnd, wParam, lParam);
4044 return TREEVIEW_SetItemA (hwnd, wParam, lParam);
4047 FIXME("Unimplemented msg TVM_SETITEMW\n");
4050 case TVM_EDITLABELA:
4051 return TREEVIEW_EditLabelA(hwnd, wParam, lParam);
4053 case TVM_EDITLABELW:
4054 FIXME("Unimplemented msg TVM_EDITLABELW \n");
4057 case TVM_GETEDITCONTROL:
4058 return TREEVIEW_GetEditControl (hwnd);
4060 case TVM_GETVISIBLECOUNT:
4061 return TREEVIEW_GetVisibleCount (hwnd, wParam, lParam);
4064 return TREEVIEW_HitTest (hwnd, lParam);
4066 case TVM_CREATEDRAGIMAGE:
4067 return TREEVIEW_CreateDragImage (hwnd, wParam, lParam);
4069 case TVM_SORTCHILDREN:
4070 return TREEVIEW_SortChildren (hwnd, wParam, lParam);
4072 case TVM_ENSUREVISIBLE:
4073 FIXME("Unimplemented msg TVM_ENSUREVISIBLE\n");
4076 case TVM_SORTCHILDRENCB:
4077 return TREEVIEW_SortChildrenCB(hwnd, wParam, lParam);
4079 case TVM_ENDEDITLABELNOW:
4080 if (infoPtr->editItem)
4081 return TREEVIEW_EndEditLabelNow (hwnd, wParam, lParam);
4083 case TVM_GETISEARCHSTRINGA:
4084 FIXME("Unimplemented msg TVM_GETISEARCHSTRINGA\n");
4087 case TVM_GETISEARCHSTRINGW:
4088 FIXME("Unimplemented msg TVM_GETISEARCHSTRINGW\n");
4091 case TVM_GETTOOLTIPS:
4092 return TREEVIEW_GetToolTips (hwnd);
4094 case TVM_SETTOOLTIPS:
4095 return TREEVIEW_SetToolTips (hwnd, wParam);
4097 case TVM_SETINSERTMARK:
4098 return TREEVIEW_SetInsertMark (hwnd,wParam, lParam);
4100 case TVM_SETITEMHEIGHT:
4101 return TREEVIEW_SetItemHeight (hwnd, wParam);
4103 case TVM_GETITEMHEIGHT:
4104 return TREEVIEW_GetItemHeight (hwnd);
4106 case TVM_SETBKCOLOR:
4107 return TREEVIEW_SetBkColor (hwnd, wParam, lParam);
4109 case TVM_SETTEXTCOLOR:
4110 return TREEVIEW_SetTextColor (hwnd, wParam, lParam);
4112 case TVM_GETBKCOLOR:
4113 return TREEVIEW_GetBkColor (hwnd);
4115 case TVM_GETTEXTCOLOR:
4116 return TREEVIEW_GetTextColor (hwnd);
4118 case TVM_SETSCROLLTIME:
4119 return TREEVIEW_SetScrollTime (hwnd, (UINT)wParam);
4121 case TVM_GETSCROLLTIME:
4122 return TREEVIEW_GetScrollTime (hwnd);
4124 case TVM_GETITEMSTATE:
4125 return TREEVIEW_GetItemState (hwnd,wParam, lParam);
4127 case TVM_GETLINECOLOR:
4128 return TREEVIEW_GetLineColor (hwnd,wParam, lParam);
4130 case TVM_SETLINECOLOR:
4131 return TREEVIEW_SetLineColor (hwnd,wParam, lParam);
4133 case TVM_SETINSERTMARKCOLOR:
4134 return TREEVIEW_SetInsertMarkColor (hwnd,wParam, lParam);
4136 case TVM_GETINSERTMARKCOLOR:
4137 return TREEVIEW_GetInsertMarkColor (hwnd,wParam, lParam);
4139 case TVM_SETUNICODEFORMAT:
4140 FIXME("Unimplemented msg TVM_SETUNICODEFORMAT\n");
4143 case TVM_GETUNICODEFORMAT:
4144 FIXME("Unimplemented msg TVM_GETUNICODEFORMAT\n");
4148 return TREEVIEW_Command (hwnd, wParam, lParam);
4151 return TREEVIEW_Destroy (hwnd);
4153 /* case WM_ENABLE: */
4156 return TREEVIEW_EraseBackground (hwnd, wParam, lParam);
4159 return DLGC_WANTARROWS | DLGC_WANTCHARS;
4162 return TREEVIEW_Paint (hwnd, wParam, lParam);
4165 return TREEVIEW_GetFont (hwnd, wParam, lParam);
4168 return TREEVIEW_SetFont (hwnd, wParam, lParam);
4171 return TREEVIEW_KeyDown (hwnd, wParam, lParam);
4174 return TREEVIEW_SetFocus (hwnd, wParam, lParam);
4177 return TREEVIEW_KillFocus (hwnd, wParam, lParam);
4179 case WM_LBUTTONDOWN:
4180 return TREEVIEW_LButtonDown (hwnd, wParam, lParam);
4183 return TREEVIEW_LButtonUp (hwnd, wParam, lParam);
4185 case WM_LBUTTONDBLCLK:
4186 return TREEVIEW_LButtonDoubleClick (hwnd, wParam, lParam);
4188 case WM_RBUTTONDOWN:
4189 return TREEVIEW_RButtonDown (hwnd, wParam, lParam);
4192 return TREEVIEW_RButtonUp (hwnd, wParam, lParam);
4195 return TREEVIEW_MouseMove (hwnd, wParam, lParam);
4197 case WM_STYLECHANGED:
4198 return TREEVIEW_StyleChanged (hwnd, wParam, lParam);
4200 /* case WM_SYSCOLORCHANGE: */
4201 /* case WM_SETREDRAW: */
4204 return TREEVIEW_HandleTimer (hwnd, wParam, lParam);
4207 return TREEVIEW_Size (hwnd, wParam,lParam);
4210 return TREEVIEW_HScroll (hwnd, wParam, lParam);
4212 return TREEVIEW_VScroll (hwnd, wParam, lParam);
4215 if (wParam & (MK_SHIFT | MK_CONTROL))
4216 return DefWindowProcA( hwnd, uMsg, wParam, lParam );
4217 return TREEVIEW_MouseWheel (hwnd, wParam, lParam);
4220 TRACE ("drawItem\n");
4221 return DefWindowProcA (hwnd, uMsg, wParam, lParam);
4224 if (uMsg >= WM_USER)
4225 FIXME("Unknown msg %04x wp=%08x lp=%08lx\n",
4226 uMsg, wParam, lParam);
4227 return DefWindowProcA (hwnd, uMsg, wParam, lParam);
4234 TREEVIEW_Register (void)
4240 ZeroMemory (&wndClass, sizeof(WNDCLASSA));
4241 wndClass.style = CS_GLOBALCLASS | CS_DBLCLKS;
4242 wndClass.lpfnWndProc = (WNDPROC)TREEVIEW_WindowProc;
4243 wndClass.cbClsExtra = 0;
4244 wndClass.cbWndExtra = sizeof(TREEVIEW_INFO *);
4245 wndClass.hCursor = LoadCursorA (0, IDC_ARROWA);
4246 wndClass.hbrBackground = 0;
4247 wndClass.lpszClassName = WC_TREEVIEWA;
4249 RegisterClassA (&wndClass);
4254 TREEVIEW_Unregister (void)
4256 UnregisterClassA (WC_TREEVIEWA, (HINSTANCE)NULL);