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 appened in wine in the
11 * two last weeks of march 99 that broke it.
13 -small array containing info about positions.
14 -better implementation of RefreshItem:
15 1) draw lines between parents
17 3) draw lines from parent<->items.
18 -implement partial drawing?
19 * -drag&drop: TVM_CREATEDRAGIMAGE should create drag bitmap.
20 * -scrollbars: horizontal scrollbar doesn't work.
24 * FIXME: check fontsize. (uRealItemHeight)
25 * test focusItem (redraw in different color)
28 better implementation.
29 * WM_HSCROLL is broken.
30 * use separate routine to get item text/image.
32 * Separate drawing/calculation.
34 * FIXMEs (for personal use)
35 Expand: -ctlmacro expands twice ->toggle.
36 -DblClick: ctlmacro.exe's NM_DBLCLK seems to go wrong (returns FALSE).
37 -treehelper: stack corruption makes big window.
47 #include "debugtools.h"
49 DEFAULT_DEBUG_CHANNEL(treeview)
51 /* ffs should be in <string.h>. */
53 /* Defines, since they do not need to return previous state, and nr
54 * has no side effects in this file.
56 #define tv_test_bit(nr,bf) (((LPBYTE)bf)[nr>>3]&(1<<(nr&7)))
57 #define tv_set_bit(nr,bf) ((LPBYTE)bf)[nr>>3]|=(1<<(nr&7))
58 #define tv_clear_bit(nr,bf) ((LPBYTE)bf)[nr>>3]&=~(1<<(nr&7))
61 #define TREEVIEW_GetInfoPtr(hwnd) \
62 ((TREEVIEW_INFO *) GetWindowLongA( hwnd, 0))
65 TREEVIEW_SendSimpleNotify (HWND hwnd, UINT code);
67 TREEVIEW_SendTreeviewNotify (HWND hwnd, UINT code, UINT action,
68 HTREEITEM oldItem, HTREEITEM newItem);
70 TREEVIEW_SendTreeviewDnDNotify (HWND hwnd, UINT code, HTREEITEM dragItem,
73 TREEVIEW_SendDispInfoNotify (HWND hwnd, TREEVIEW_ITEM *wineItem,
74 UINT code, UINT what);
76 TREEVIEW_SendCustomDrawNotify (HWND hwnd, DWORD dwDrawStage, HDC hdc,
79 TREEVIEW_SendCustomDrawItemNotify (HWND hwnd, HDC hdc,
80 TREEVIEW_ITEM *tvItem, UINT uItemDrawState);
82 TREEVIEW_DoSelectItem (HWND hwnd, INT action, HTREEITEM newSelect, INT cause);
84 TREEVIEW_Refresh (HWND hwnd);
86 static LRESULT CALLBACK
87 TREEVIEW_Edit_SubclassProc (HWND hwnd, UINT uMsg, WPARAM wParam,
91 TREEVIEW_EndEditLabelNow (HWND hwnd, WPARAM wParam, LPARAM lParam);
96 /* helper functions. Work with the assumption that validity of operands
97 is checked beforehand, and that tree state is valid. */
99 /* FIXME: MS documentation says `GetNextVisibleItem' returns NULL
100 if not succesfull'. Probably only applies to derefencing infoPtr
101 (ie we are offered a valid treeview structure)
102 and not whether there is a next `visible' child.
103 FIXME: check other failures.
106 /***************************************************************************
107 * This method returns the TREEVIEW_ITEM object given the handle
109 static TREEVIEW_ITEM* TREEVIEW_ValidItem(
110 TREEVIEW_INFO *infoPtr,
113 if ((!handle) || (handle>infoPtr->uMaxHandle))
116 if (tv_test_bit ((INT)handle, infoPtr->freeList))
119 return &infoPtr->items[(INT)handle];
122 /***************************************************************************
123 * This method returns the last expanded child item of a tree node
125 static TREEVIEW_ITEM *TREEVIEW_GetLastListItem(
126 TREEVIEW_INFO *infoPtr,
127 TREEVIEW_ITEM *tvItem)
130 TREEVIEW_ITEM *wineItem = tvItem;
133 * Get this item last sibling
135 while (wineItem->sibling)
136 wineItem=& infoPtr->items [(INT)wineItem->sibling];
139 * If the last sibling has expanded children, restart.
141 if ( ( wineItem->cChildren > 0 ) && ( wineItem->state & TVIS_EXPANDED) )
142 return TREEVIEW_GetLastListItem(
144 &(infoPtr->items[(INT)wineItem->firstChild]));
149 /***************************************************************************
150 * This method returns the previous physical item in the list not
151 * considering the tree hierarchy.
153 static TREEVIEW_ITEM *TREEVIEW_GetPrevListItem(
154 TREEVIEW_INFO *infoPtr,
155 TREEVIEW_ITEM *tvItem)
157 if (tvItem->upsibling)
160 * This item has a upsibling, get the last item. Since, GetLastListItem
161 * first looks at siblings, we must feed it with the first child.
163 TREEVIEW_ITEM *upItem = &infoPtr->items[(INT)tvItem->upsibling];
165 if ( ( upItem->cChildren > 0 ) && ( upItem->state & TVIS_EXPANDED) )
166 return TREEVIEW_GetLastListItem(
168 &infoPtr->items[(INT)upItem->firstChild]);
175 * this item does not have a upsibling, get the parent
178 return &infoPtr->items[(INT)tvItem->parent];
185 /***************************************************************************
186 * This method returns the next physical item in the treeview not
187 * considering the tree hierarchy.
189 static TREEVIEW_ITEM *TREEVIEW_GetNextListItem(
190 TREEVIEW_INFO *infoPtr,
191 TREEVIEW_ITEM *tvItem)
193 TREEVIEW_ITEM *wineItem = NULL;
196 * If this item has children and is expanded, return the first child
198 if ((tvItem->firstChild) && (tvItem->state & TVIS_EXPANDED))
199 return (& infoPtr->items[(INT)tvItem->firstChild]);
203 * try to get the sibling
206 return (& infoPtr->items[(INT)tvItem->sibling]);
209 * Otherwise, get the parent's sibling.
212 while (wineItem->parent) {
213 wineItem=& infoPtr->items [(INT)wineItem->parent];
214 if (wineItem->sibling)
215 return (& infoPtr->items [(INT)wineItem->sibling]);
221 /***************************************************************************
222 * This method returns the nth item starting at the given item. It returns
223 * the last item (or first) we we run out of items.
225 * Will scroll backward if count is <0.
226 * forward if count is >0.
228 static TREEVIEW_ITEM *TREEVIEW_GetListItem(
229 TREEVIEW_INFO *infoPtr,
230 TREEVIEW_ITEM *tvItem,
233 TREEVIEW_ITEM *previousItem = NULL;
234 TREEVIEW_ITEM *wineItem = tvItem;
239 /* Find count item downward */
240 while ((iter++ < count) && (wineItem != NULL))
242 /* Keep a pointer to the previous in case we ask for more than we got */
243 previousItem = wineItem;
244 wineItem = TREEVIEW_GetNextListItem(infoPtr, wineItem);
247 if (wineItem == NULL)
248 wineItem = previousItem;
252 /* Find count item upward */
253 while ((iter-- > count) && (wineItem != NULL))
255 /* Keep a pointer to the previous in case we ask for more than we got */
256 previousItem = wineItem;
257 wineItem = TREEVIEW_GetPrevListItem(infoPtr, wineItem);
260 if (wineItem == NULL)
261 wineItem = previousItem;
270 /***************************************************************************
273 static void TREEVIEW_RemoveAllChildren(
275 TREEVIEW_ITEM *parentItem)
277 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
278 TREEVIEW_ITEM *killItem;
281 kill=(INT)parentItem->firstChild;
283 tv_set_bit ( kill, infoPtr->freeList);
284 killItem=& infoPtr->items[kill];
285 if (killItem->pszText!=LPSTR_TEXTCALLBACKA)
286 COMCTL32_Free (killItem->pszText);
287 TREEVIEW_SendTreeviewNotify (hwnd, TVN_DELETEITEM, 0, (HTREEITEM)kill, 0);
288 if (killItem->firstChild)
289 TREEVIEW_RemoveAllChildren (hwnd, killItem);
290 kill=(INT)killItem->sibling;
293 if (parentItem->cChildren>0) {
294 infoPtr->uNumItems -= parentItem->cChildren;
295 parentItem->firstChild = 0;
296 parentItem->cChildren = 0;
303 TREEVIEW_RemoveItem (HWND hwnd, TREEVIEW_ITEM *wineItem)
306 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
307 TREEVIEW_ITEM *parentItem, *upsiblingItem, *siblingItem;
310 iItem=(INT)wineItem->hItem;
311 tv_set_bit(iItem,infoPtr->freeList);
312 infoPtr->uNumItems--;
314 if (wineItem->pszText!=LPSTR_TEXTCALLBACKA)
315 COMCTL32_Free (wineItem->pszText);
317 TREEVIEW_SendTreeviewNotify (hwnd, TVN_DELETEITEM, 0, (HTREEITEM)iItem, 0);
319 if (wineItem->firstChild)
320 TREEVIEW_RemoveAllChildren (hwnd,wineItem);
322 if (wineItem->parent) {
323 parentItem=& infoPtr->items [(INT)wineItem->parent];
324 switch (parentItem->cChildren) {
325 case I_CHILDRENCALLBACK:
326 FIXME("we don't handle I_CHILDRENCALLBACK yet\n");
329 parentItem->cChildren=0;
330 parentItem->firstChild=0;
333 parentItem->cChildren--;
334 if ((INT)parentItem->firstChild==iItem)
335 parentItem->firstChild=wineItem->sibling;
339 if (iItem==(INT)infoPtr->TopRootItem)
340 infoPtr->TopRootItem=(HTREEITEM)wineItem->sibling;
341 if (wineItem->upsibling) {
342 upsiblingItem=& infoPtr->items [(INT)wineItem->upsibling];
343 upsiblingItem->sibling=wineItem->sibling;
345 if (wineItem->sibling) {
346 siblingItem=& infoPtr->items [(INT)wineItem->sibling];
347 siblingItem->upsibling=wineItem->upsibling;
355 /* Note:TREEVIEW_RemoveTree doesn't remove infoPtr itself */
357 static void TREEVIEW_RemoveTree (HWND hwnd)
360 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
361 TREEVIEW_ITEM *killItem;
364 for (i=1; i<=(INT)infoPtr->uMaxHandle; i++)
365 if (!tv_test_bit (i, infoPtr->freeList)) {
366 killItem=& infoPtr->items [i];
367 if (killItem->pszText!=LPSTR_TEXTCALLBACKA)
368 COMCTL32_Free (killItem->pszText);
369 TREEVIEW_SendTreeviewNotify
370 (hwnd, TVN_DELETEITEM, 0, killItem->hItem, 0);
373 if (infoPtr->uNumPtrsAlloced) {
374 COMCTL32_Free (infoPtr->items);
375 COMCTL32_Free (infoPtr->freeList);
376 infoPtr->uNumItems=0;
377 infoPtr->uNumPtrsAlloced=0;
378 infoPtr->uMaxHandle=0;
389 TREEVIEW_GetImageList (HWND hwnd, WPARAM wParam, LPARAM lParam)
391 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
394 if (infoPtr==NULL) return 0;
396 if ((INT)wParam == TVSIL_NORMAL)
397 return (LRESULT) infoPtr->himlNormal;
398 if ((INT)wParam == TVSIL_STATE)
399 return (LRESULT) infoPtr->himlState;
405 TREEVIEW_SetImageList (HWND hwnd, WPARAM wParam, LPARAM lParam)
407 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
411 switch ((INT)wParam) {
413 himlTemp = infoPtr->himlNormal;
414 infoPtr->himlNormal = (HIMAGELIST)lParam;
415 return (LRESULT)himlTemp;
418 himlTemp = infoPtr->himlState;
419 infoPtr->himlState = (HIMAGELIST)lParam;
420 return (LRESULT)himlTemp;
423 return (LRESULT)NULL;
429 TREEVIEW_SetItemHeight (HWND hwnd, WPARAM wParam)
431 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
432 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_SetTextColor (HWND hwnd, WPARAM wParam, LPARAM lParam)
484 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
485 COLORREF prevColor=infoPtr->clrText;
488 infoPtr->clrText=(COLORREF) lParam;
489 return (LRESULT) prevColor;
493 TREEVIEW_GetBkColor (HWND hwnd)
495 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
498 return (LRESULT) infoPtr->clrText;
502 TREEVIEW_SetBkColor (HWND hwnd, WPARAM wParam, LPARAM lParam)
504 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
505 COLORREF prevColor=infoPtr->clrBk;
508 infoPtr->clrBk=(COLORREF) lParam;
509 return (LRESULT) prevColor;
513 TREEVIEW_GetTextColor (HWND hwnd)
515 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
518 return (LRESULT) infoPtr->clrBk;
522 /* cdmode: custom draw mode as received from app. in first NMCUSTOMDRAW
525 #define TREEVIEW_LEFT_MARGIN 8
529 TREEVIEW_DrawItem (HWND hwnd, HDC hdc, TREEVIEW_ITEM *wineItem)
531 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
532 DWORD dwStyle = GetWindowLongA (hwnd, GWL_STYLE);
533 INT center,xpos,cx,cy, cditem, drawmode;
535 UINT uTextJustify = DT_LEFT;
539 if (wineItem->state & TVIS_BOLD)
540 hOldFont = SelectObject (hdc, infoPtr->hBoldFont);
542 hOldFont = SelectObject (hdc, infoPtr->hFont);
545 TRACE ("cdmode:%x\n",infoPtr->cdmode);
546 if (infoPtr->cdmode & CDRF_NOTIFYITEMDRAW) {
547 drawmode=CDDS_ITEMPREPAINT;
549 if (infoPtr->cdmode & CDRF_NOTIFYSUBITEMDRAW)
550 drawmode|=CDDS_SUBITEM;
552 cditem=TREEVIEW_SendCustomDrawItemNotify (hwnd, hdc, wineItem, drawmode);
554 TRACE("cditem:%d\n",cditem);
556 if (cditem & CDRF_SKIPDEFAULT)
561 * Set drawing starting points
563 r = wineItem->rect; /* this item rectangle */
564 center = (r.top+r.bottom)/2; /* this item vertical center */
565 xpos = r.left + TREEVIEW_LEFT_MARGIN;/* horizontal starting point */
568 * Display the tree hierarchy
570 if ( dwStyle & TVS_HASLINES)
573 * Write links to parent node
574 * we draw the L starting from the child to the parent
576 * points[0] is attached to the current item
577 * points[1] is the L corner
578 * points[2] is attached to the parent or the up sibling
580 if ( dwStyle & TVS_LINESATROOT)
582 TREEVIEW_ITEM *upNode = NULL;
583 BOOL hasParentOrSibling = TRUE;
584 RECT upRect = {0,0,0,0};
585 HPEN hOldPen, hnewPen;
588 * determine the target location of the line at root, either be linked
589 * to the up sibling or to the parent node.
591 if (wineItem->upsibling)
592 upNode = TREEVIEW_ValidItem (infoPtr, wineItem->upsibling);
593 else if (wineItem->parent)
594 upNode = TREEVIEW_ValidItem (infoPtr, wineItem->parent);
596 hasParentOrSibling = FALSE;
599 upRect = upNode->rect;
601 if ( wineItem->iLevel == 0 )
603 points[2].x = points[1].x = upRect.left+8;
604 points[0].x = points[2].x + 10;
605 points[2].y = upRect.bottom-3;
606 points[1].y = points[0].y = center;
610 points[2].x = points[1].x = 8 + (20*wineItem->iLevel);
611 points[2].y = ( upNode->cChildren == 0) ?
612 upRect.top : /* is linked to the "L" above */
613 ( wineItem->upsibling != NULL) ?
614 upRect.bottom-3: /* is linked to an icon */
615 upRect.bottom+1; /* is linked to a +/- box */
616 points[1].y = points[0].y = center;
617 points[0].x = points[1].x + 10;
623 hnewPen = CreatePen(PS_DOT, 0, infoPtr->clrLine);
624 hOldPen = SelectObject( hdc, hnewPen );
626 if (hasParentOrSibling)
627 Polyline (hdc,points,3);
629 Polyline (hdc,points,2);
631 DeleteObject(hnewPen);
632 SelectObject(hdc, hOldPen);
637 * Display the (+/-) signs
639 if (wineItem->iLevel != 0)/* update position only for non root node */
640 xpos+=(5*wineItem->iLevel);
642 if (( dwStyle & TVS_HASBUTTONS) && ( dwStyle & TVS_HASLINES))
644 if ( (wineItem->cChildren) ||
645 (wineItem->cChildren == I_CHILDRENCALLBACK))
647 /* Setup expand box coordinate to facilitate the LMBClick handling */
648 wineItem->expandBox.left = xpos-4;
649 wineItem->expandBox.top = center-4;
650 wineItem->expandBox.right = xpos+5;
651 wineItem->expandBox.bottom = center+5;
655 wineItem->expandBox.left,
656 wineItem->expandBox.top ,
657 wineItem->expandBox.right,
658 wineItem->expandBox.bottom);
660 MoveToEx (hdc, xpos-2, center, NULL);
661 LineTo (hdc, xpos+3, center);
663 if (!(wineItem->state & TVIS_EXPANDED)) {
664 MoveToEx (hdc, xpos, center-2, NULL);
665 LineTo (hdc, xpos, center+3);
671 * Display the image assiciated with this item
673 xpos += 13; /* update position */
674 if (wineItem->mask & (TVIF_IMAGE|TVIF_SELECTEDIMAGE)) {
676 HIMAGELIST *himlp = NULL;
678 if (infoPtr->himlNormal)
679 himlp=&infoPtr->himlNormal; /* get the image list */
681 if ( (wineItem->state & TVIS_SELECTED) &&
682 (wineItem->iSelectedImage)) {
684 /* State images are displayed to the left of the Normal image*/
685 if (infoPtr->himlState)
686 himlp=&infoPtr->himlState;
688 /* The item is curently selected */
689 if (wineItem->iSelectedImage == I_IMAGECALLBACK)
690 TREEVIEW_SendDispInfoNotify (
696 imageIndex = wineItem->iSelectedImage;
699 /* This item is not selected */
700 if (wineItem->iImage == I_IMAGECALLBACK)
701 TREEVIEW_SendDispInfoNotify (
707 imageIndex = wineItem->iImage;
712 /* We found an image to display? Draw it. */
721 ImageList_GetIconSize (*himlp, &cx, &cy);
727 * Display the text assiciated with this item
730 if ((wineItem->mask & TVIF_TEXT) && (wineItem->pszText))
732 COLORREF oldBkColor = 0;
733 COLORREF oldTextColor = 0;
739 wineItem->text.left = r.left;
740 wineItem->text.right = r.right;
741 wineItem->text.top = r.top;
742 wineItem->text.bottom= r.bottom;
744 if (wineItem->state & (TVIS_SELECTED | TVIS_DROPHILITED) ) {
745 oldBkMode = SetBkMode (hdc, OPAQUE);
746 oldBkColor = SetBkColor (hdc, GetSysColor( COLOR_HIGHLIGHT));
747 oldTextColor = SetTextColor(hdc, GetSysColor( COLOR_HIGHLIGHTTEXT));
751 oldBkMode = SetBkMode(hdc, TRANSPARENT);
752 oldTextColor = SetTextColor(hdc, GetSysColor( COLOR_WINDOWTEXT));
755 if (wineItem->pszText== LPSTR_TEXTCALLBACKA) {
756 TRACE("LPSTR_TEXTCALLBACK\n");
757 TREEVIEW_SendDispInfoNotify (hwnd, wineItem, TVN_GETDISPINFO, TVIF_TEXT);
764 lstrlenA(wineItem->pszText),
766 uTextJustify | DT_VCENTER | DT_SINGLELINE );
768 /* Obtain the text coordinate */
772 lstrlenA(wineItem->pszText),
774 uTextJustify | DT_VCENTER | DT_SINGLELINE | DT_CALCRECT);
776 /* Restore the hdc state */
777 SetTextColor( hdc, oldTextColor);
779 if (oldBkMode != TRANSPARENT)
780 SetBkMode(hdc, oldBkMode);
781 if (wineItem->state & (TVIS_SELECTED | TVIS_DROPHILITED))
782 SetBkColor (hdc, oldBkColor);
784 /* Draw the box arround the selected item */
785 if (wineItem->state & TVIS_SELECTED )
787 HPEN hnewPen = CreatePen(PS_DOT, 0, GetSysColor(COLOR_WINDOWTEXT) );
788 HPEN hOldPen = SelectObject( hdc, hnewPen );
791 points[0].x = wineItem->text.left-1;
792 points[0].y = wineItem->text.top+1;
793 points[1].x = wineItem->text.right;
794 points[1].y = wineItem->text.top+1;
795 points[2].x = wineItem->text.right;
796 points[2].y = wineItem->text.bottom;
797 points[3].x = wineItem->text.left-1;
798 points[3].y = wineItem->text.bottom;
800 Polyline (hdc,points,4);
802 DeleteObject(hnewPen);
803 SelectObject(hdc, hOldPen);
807 if (cditem & CDRF_NOTIFYPOSTPAINT)
808 TREEVIEW_SendCustomDrawItemNotify (hwnd, hdc, wineItem, CDDS_ITEMPOSTPAINT);
810 SelectObject (hdc, hOldFont);
814 TREEVIEW_GetItemRect (HWND hwnd, WPARAM wParam, LPARAM lParam)
816 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
817 TREEVIEW_ITEM *wineItem;
819 LPRECT lpRect = (LPRECT)lParam;
823 * validate parameters
825 if ( (infoPtr==NULL) || (lpRect == NULL) )
828 if (infoPtr->Timer & TV_REFRESH_TIMER_SET)
829 TREEVIEW_Refresh (hwnd); /* we want a rect for the current view */
832 * retrive the item ptr
834 iItem = (HTREEITEM *) lParam;
835 wineItem = TREEVIEW_ValidItem (infoPtr, *iItem);
836 if ((!wineItem) || (!wineItem->visible))
840 * If wParam is TRUE return the text size otherwise return
841 * the whole item size
844 lpRect->left = wineItem->text.left;
845 lpRect->right = wineItem->text.right;
846 lpRect->bottom = wineItem->text.bottom;
847 lpRect->top = wineItem->text.top;
849 lpRect->left = wineItem->rect.left;
850 lpRect->right = wineItem->rect.right;
851 lpRect->bottom = wineItem->rect.bottom;
852 lpRect->top = wineItem->rect.top;
855 TRACE("[L:%d R:%d T:%d B:%d]\n",
856 lpRect->left,lpRect->right,
857 lpRect->top,lpRect->bottom);
863 TREEVIEW_GetVisibleCount (HWND hwnd, WPARAM wParam, LPARAM lParam)
866 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
868 return (LRESULT) infoPtr->uVisibleHeight / infoPtr->uRealItemHeight;
874 TREEVIEW_SetItemA (HWND hwnd, WPARAM wParam, LPARAM lParam)
876 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
877 TREEVIEW_ITEM *wineItem;
881 tvItem=(LPTVITEMEXA) lParam;
882 iItem=(INT)tvItem->hItem;
883 TRACE("item %d,mask %x\n",iItem,tvItem->mask);
885 wineItem = TREEVIEW_ValidItem (infoPtr, (HTREEITEM)iItem);
886 if (!wineItem) return FALSE;
888 if (tvItem->mask & TVIF_CHILDREN) {
889 wineItem->cChildren=tvItem->cChildren;
892 if (tvItem->mask & TVIF_IMAGE) {
893 wineItem->iImage=tvItem->iImage;
896 if (tvItem->mask & TVIF_INTEGRAL) {
897 wineItem->iIntegral=tvItem->iIntegral;
900 if (tvItem->mask & TVIF_PARAM) {
901 wineItem->lParam=tvItem->lParam;
904 if (tvItem->mask & TVIF_SELECTEDIMAGE) {
905 wineItem->iSelectedImage=tvItem->iSelectedImage;
908 if (tvItem->mask & TVIF_STATE) {
909 wineItem->state=tvItem->state & tvItem->stateMask;
912 if (tvItem->mask & TVIF_TEXT) {
913 if (tvItem->pszText!=LPSTR_TEXTCALLBACKA) {
914 len=lstrlenA (tvItem->pszText);
915 if (len>wineItem->cchTextMax)
916 wineItem->pszText= COMCTL32_ReAlloc (wineItem->pszText, len+1);
917 lstrcpynA (wineItem->pszText, tvItem->pszText,len);
919 if (wineItem->cchTextMax) {
920 COMCTL32_Free (wineItem->pszText);
921 wineItem->cchTextMax=0;
923 wineItem->pszText=LPSTR_TEXTCALLBACKA;
931 TREEVIEW_GetItemState (HWND hwnd, WPARAM wParam, LPARAM lParam)
934 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
935 TREEVIEW_ITEM *wineItem;
937 wineItem = TREEVIEW_ValidItem (infoPtr, (HTREEITEM)wParam);
938 if (!wineItem) return 0;
940 return (wineItem->state & lParam);
948 TREEVIEW_Refresh (HWND hwnd)
951 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
956 INT iItem, indent, x, y, cx, height, itemHeight;
957 INT viewtop,viewbottom,viewleft,viewright;
958 TREEVIEW_ITEM *wineItem, *prevItem;
964 if (infoPtr->Timer & TV_REFRESH_TIMER_SET) {
965 KillTimer (hwnd, TV_REFRESH_TIMER);
966 infoPtr->Timer &= ~TV_REFRESH_TIMER_SET;
970 GetClientRect (hwnd, &rect);
971 if ((rect.left-rect.right ==0) || (rect.top-rect.bottom==0)) return;
973 infoPtr->cdmode=TREEVIEW_SendCustomDrawNotify
974 (hwnd, CDDS_PREPAINT, hdc, rect);
976 if (infoPtr->cdmode==CDRF_SKIPDEFAULT) {
977 ReleaseDC (hwnd, hdc);
981 infoPtr->uVisibleHeight= rect.bottom-rect.top;
982 infoPtr->uVisibleWidth= rect.right-rect.left;
985 viewbottom=infoPtr->cy + rect.bottom-rect.top;
986 viewleft=infoPtr->cx;
987 viewright=infoPtr->cx + rect.right-rect.left;
991 /* draw background */
993 hbrBk = GetSysColorBrush (COLOR_WINDOW);
994 FillRect(hdc, &rect, hbrBk);
997 iItem=(INT)infoPtr->TopRootItem;
998 infoPtr->firstVisible=0;
1002 TRACE("[%d %d %d %d]\n",viewtop,viewbottom,viewleft,viewright);
1006 wineItem= & infoPtr->items[iItem];
1007 wineItem->iLevel=indent;
1009 ImageList_GetIconSize (infoPtr->himlNormal, &cx, &itemHeight);
1010 if (infoPtr->uItemHeight>itemHeight)
1011 itemHeight=infoPtr->uItemHeight;
1013 GetTextMetricsA (hdc, &tm);
1014 if ((tm.tmHeight + tm.tmExternalLeading) > itemHeight)
1015 itemHeight=tm.tmHeight + tm.tmExternalLeading;
1017 infoPtr->uRealItemHeight=itemHeight;
1020 /* FIXME: remove this in later stage */
1022 if (wineItem->pszText!=LPSTR_TEXTCALLBACK32A)
1023 TRACE (treeview, "%d %d [%d %d %d %d] (%s)\n",y,x,
1024 wineItem->rect.top, wineItem->rect.bottom,
1025 wineItem->rect.left, wineItem->rect.right,
1028 TRACE (treeview, "%d [%d %d %d %d] (CALLBACK)\n",
1030 wineItem->rect.top, wineItem->rect.bottom,
1031 wineItem->rect.left, wineItem->rect.right);
1034 height=itemHeight * wineItem->iIntegral +1;
1035 if ((y >= viewtop) && (y <= viewbottom) &&
1036 (x >= viewleft ) && (x <= viewright)) {
1037 wineItem->visible = TRUE;
1038 wineItem->rect.top = y - infoPtr->cy + rect.top;
1039 wineItem->rect.bottom = wineItem->rect.top + height ;
1040 wineItem->rect.left = x - infoPtr->cx + rect.left;
1041 wineItem->rect.right = rect.right;
1042 if (!infoPtr->firstVisible)
1043 infoPtr->firstVisible=wineItem->hItem;
1044 TREEVIEW_DrawItem (hwnd, hdc, wineItem);
1047 wineItem->visible = FALSE;
1048 wineItem->rect.left = wineItem->rect.top = 0;
1049 wineItem->rect.right= wineItem->rect.bottom = 0;
1050 wineItem->text.left = wineItem->text.top = 0;
1051 wineItem->text.right= wineItem->text.bottom = 0;
1054 /* look up next item */
1056 if ((wineItem->firstChild) && (wineItem->state & TVIS_EXPANDED)) {
1057 iItem=(INT)wineItem->firstChild;
1059 x+=infoPtr->uIndent;
1060 if (x>infoPtr->uTotalWidth)
1061 infoPtr->uTotalWidth=x;
1064 iItem=(INT)wineItem->sibling;
1065 while ((!iItem) && (indent>0)) {
1067 x-=infoPtr->uIndent;
1069 wineItem=&infoPtr->items[(INT)wineItem->parent];
1070 iItem=(INT)wineItem->sibling;
1076 /* FIXME: infoPtr->uTotalWidth should also take item label into account */
1077 /* FIXME: or should query item sizes (ie check CDRF_NEWFONT) */
1079 infoPtr->uTotalHeight=y;
1080 if (y >= (viewbottom-viewtop)) {
1081 if (!(infoPtr->uInternalStatus & TV_VSCROLL))
1082 ShowScrollBar (hwnd, SB_VERT, TRUE);
1083 infoPtr->uInternalStatus |=TV_VSCROLL;
1084 SetScrollRange (hwnd, SB_VERT, 0,
1085 y - infoPtr->uVisibleHeight, FALSE);
1086 SetScrollPos (hwnd, SB_VERT, infoPtr->cy, TRUE);
1089 if (infoPtr->uInternalStatus & TV_VSCROLL)
1090 ShowScrollBar (hwnd, SB_VERT, FALSE);
1091 infoPtr->uInternalStatus &= ~TV_VSCROLL;
1095 if (infoPtr->cdmode & CDRF_NOTIFYPOSTPAINT)
1096 infoPtr->cdmode=TREEVIEW_SendCustomDrawNotify
1097 (hwnd, CDDS_POSTPAINT, hdc, rect);
1099 ReleaseDC (hwnd, hdc);
1105 TREEVIEW_HandleTimer (HWND hwnd, WPARAM wParam, LPARAM lParam)
1107 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
1109 TRACE(" %d\n",wParam);
1110 if (!infoPtr) return FALSE;
1113 case TV_REFRESH_TIMER:
1114 KillTimer (hwnd, TV_REFRESH_TIMER);
1115 infoPtr->Timer &= ~TV_REFRESH_TIMER_SET;
1116 InvalidateRect(hwnd, NULL, FALSE);
1119 KillTimer (hwnd, TV_EDIT_TIMER);
1120 infoPtr->Timer &= ~TV_EDIT_TIMER_SET;
1123 ERR("got unknown timer\n");
1131 TREEVIEW_QueueRefresh (HWND hwnd)
1134 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
1137 if (infoPtr->Timer & TV_REFRESH_TIMER_SET) {
1138 KillTimer (hwnd, TV_REFRESH_TIMER);
1141 SetTimer (hwnd, TV_REFRESH_TIMER, TV_REFRESH_DELAY, 0);
1142 infoPtr->Timer|=TV_REFRESH_TIMER_SET;
1148 TREEVIEW_GetItemA (HWND hwnd, WPARAM wParam, LPARAM lParam)
1150 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
1152 TREEVIEW_ITEM *wineItem;
1155 tvItem=(LPTVITEMEXA) lParam;
1156 iItem=(INT)tvItem->hItem;
1158 wineItem = TREEVIEW_ValidItem (infoPtr, (HTREEITEM)iItem);
1159 if (!wineItem) return FALSE;
1161 if (tvItem->mask & TVIF_CHILDREN) {
1162 if (TVIF_CHILDREN==I_CHILDRENCALLBACK)
1163 FIXME("I_CHILDRENCALLBACK not supported\n");
1164 tvItem->cChildren=wineItem->cChildren;
1167 if (tvItem->mask & TVIF_HANDLE) {
1168 tvItem->hItem=wineItem->hItem;
1171 if (tvItem->mask & TVIF_IMAGE) {
1172 tvItem->iImage=wineItem->iImage;
1175 if (tvItem->mask & TVIF_INTEGRAL) {
1176 tvItem->iIntegral=wineItem->iIntegral;
1179 /* undocumented: windows ignores TVIF_PARAM and
1180 * always sets lParam
1182 tvItem->lParam=wineItem->lParam;
1184 if (tvItem->mask & TVIF_SELECTEDIMAGE) {
1185 tvItem->iSelectedImage=wineItem->iSelectedImage;
1188 if (tvItem->mask & TVIF_STATE) {
1189 tvItem->state=wineItem->state & tvItem->stateMask;
1192 if (tvItem->mask & TVIF_TEXT) {
1193 if (wineItem->pszText == LPSTR_TEXTCALLBACKA) {
1194 tvItem->pszText = LPSTR_TEXTCALLBACKA; /* FIXME:send notification? */
1195 ERR(" GetItem called with LPSTR_TEXTCALLBACK\n");
1197 else if (wineItem->pszText) {
1198 lstrcpynA (tvItem->pszText, wineItem->pszText, tvItem->cchTextMax);
1202 TRACE("item %d<%p>, txt %p, img %p, action %x\n",
1214 /* FIXME: check implementation of TVGN_NEXT/TVGN_NEXTVISIBLE */
1217 TREEVIEW_GetNextItem (HWND hwnd, WPARAM wParam, LPARAM lParam)
1220 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
1221 TREEVIEW_ITEM *wineItem, *returnItem;
1222 INT iItem, retval, flag;
1225 if (!infoPtr) return FALSE;
1226 flag = (INT) wParam;
1227 iItem = (INT) lParam;
1230 case TVGN_ROOT: retval=(INT)infoPtr->TopRootItem;
1232 case TVGN_CARET:retval=(INT)infoPtr->selectedItem;
1234 case TVGN_FIRSTVISIBLE:
1235 TREEVIEW_Refresh (hwnd);
1236 /* FIXME:we should only recalculate, not redraw */
1237 retval=(INT)infoPtr->firstVisible;
1239 case TVGN_DROPHILITE:
1240 retval=(INT)infoPtr->dropItem;
1244 TRACE("flags:%x, returns %u\n", flag, retval);
1248 wineItem = TREEVIEW_ValidItem (infoPtr, (HTREEITEM)iItem);
1250 if (!wineItem) return FALSE;
1253 case TVGN_NEXT: retval=(INT)wineItem->sibling;
1256 retval=(INT)wineItem->upsibling;
1259 retval=(INT)wineItem->parent;
1262 retval=(INT)wineItem->firstChild;
1264 case TVGN_LASTVISIBLE:
1265 returnItem=TREEVIEW_GetLastListItem (infoPtr,wineItem);
1267 case TVGN_NEXTVISIBLE:
1268 returnItem=TREEVIEW_GetNextListItem (infoPtr,wineItem);
1270 case TVGN_PREVIOUSVISIBLE:
1271 returnItem=TREEVIEW_GetPrevListItem (infoPtr, wineItem);
1273 default: FIXME("Unknown msg %x,item %x\n", flag,iItem);
1278 TRACE("flags:%x, item %d;returns %d\n", flag, iItem,
1279 (INT)returnItem->hItem);
1280 return (INT)returnItem->hItem;
1283 TRACE("flags:%x, item %d;returns %d\n", flag, iItem,retval);
1289 TREEVIEW_GetCount (HWND hwnd, WPARAM wParam, LPARAM lParam)
1291 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
1293 TRACE(" %d\n",infoPtr->uNumItems);
1294 return (LRESULT) infoPtr->uNumItems;
1297 /***************************************************************************
1298 * This method does the chaining of the insertion of a treeview item
1300 * If parent is NULL, we're inserting at the root of the list.
1302 static void TREEVIEW_InsertBefore(
1303 TREEVIEW_INFO *infoPtr,
1304 TREEVIEW_ITEM *newItem,
1305 TREEVIEW_ITEM *sibling,
1306 TREEVIEW_ITEM *parent)
1308 HTREEITEM siblingHandle = 0;
1309 HTREEITEM upSiblingHandle = 0;
1310 TREEVIEW_ITEM *upSibling = NULL;
1312 if (newItem == NULL)
1313 ERR("NULL newItem, impossible condition\n");
1315 if (sibling != NULL) /* Insert before this sibling for this parent */
1317 /* Store the new item sibling up sibling and sibling tem handle */
1318 siblingHandle = sibling->hItem;
1319 upSiblingHandle = sibling->upsibling;
1320 /* As well as a pointer to the upsibling sibling object */
1321 if ( (INT)sibling->upsibling != 0 )
1322 upSibling = &infoPtr->items[(INT)sibling->upsibling];
1324 /* Adjust the sibling pointer */
1325 sibling->upsibling = newItem->hItem;
1327 /* Adjust the new item pointers */
1328 newItem->upsibling = upSiblingHandle;
1329 newItem->sibling = siblingHandle;
1331 /* Adjust the up sibling pointer */
1332 if ( upSibling != NULL )
1333 upSibling->sibling = newItem->hItem;
1335 /* this item is the first child of this parent, adjust parent pointers */
1337 parent->firstChild = newItem->hItem;
1339 infoPtr->TopRootItem= newItem->hItem;
1341 else /* Insert as first child of this parent */
1343 parent->firstChild = newItem->hItem;
1346 /***************************************************************************
1347 * This method does the chaining of the insertion of a treeview item
1349 * If parent is NULL, we're inserting at the root of the list.
1351 static void TREEVIEW_InsertAfter(
1352 TREEVIEW_INFO *infoPtr,
1353 TREEVIEW_ITEM *newItem,
1354 TREEVIEW_ITEM *upSibling,
1355 TREEVIEW_ITEM *parent)
1357 HTREEITEM upSiblingHandle = 0;
1358 HTREEITEM siblingHandle = 0;
1359 TREEVIEW_ITEM *sibling = NULL;
1362 if (newItem == NULL)
1363 ERR("NULL newItem, impossible condition\n");
1365 if (upSibling != NULL) /* Insert after this upsibling for this parent */
1367 /* Store the new item up sibling and sibling item handle */
1368 upSiblingHandle = upSibling->hItem;
1369 siblingHandle = upSibling->sibling;
1370 /* As well as a pointer to the upsibling sibling object */
1371 if ( (INT)upSibling->sibling != 0 )
1372 sibling = &infoPtr->items[(INT)upSibling->sibling];
1374 /* Adjust the up sibling pointer */
1375 upSibling->sibling = newItem->hItem;
1377 /* Adjust the new item pointers */
1378 newItem->upsibling = upSiblingHandle;
1379 newItem->sibling = siblingHandle;
1381 /* Adjust the sibling pointer */
1382 if ( sibling != NULL )
1383 sibling->upsibling = newItem->hItem;
1386 newItem is the last of the level, nothing else to do
1389 else /* Insert as first child of this parent */
1391 parent->firstChild = newItem->hItem;
1394 /***************************************************************************
1395 * Forward the DPA local callback to the treeview owner callback
1397 static INT WINAPI TREEVIEW_CallBackCompare(
1402 /* Forward the call to the client define callback */
1403 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr((HWND)tvInfoPtr);
1404 return (infoPtr->pCallBackSort->lpfnCompare)(
1405 ((TREEVIEW_ITEM*)first)->lParam,
1406 ((TREEVIEW_ITEM*)second)->lParam,
1407 infoPtr->pCallBackSort->lParam);
1410 /***************************************************************************
1411 * Treeview native sort routine: sort on item text.
1413 static INT WINAPI TREEVIEW_SortOnName (
1418 HWND hwnd=(HWND) tvInfoPtr;
1420 TREEVIEW_ITEM *item;
1423 item=(TREEVIEW_ITEM *) first;
1424 if (item->pszText==LPSTR_TEXTCALLBACKA) {
1425 TREEVIEW_SendDispInfoNotify (hwnd, item, TVN_GETDISPINFO, TVIF_TEXT);
1429 item=(TREEVIEW_ITEM *) second;
1430 if (item->pszText==LPSTR_TEXTCALLBACKA) {
1431 TREEVIEW_SendDispInfoNotify (hwnd, item, TVN_GETDISPINFO, TVIF_TEXT);
1435 return -strcmp (txt1,txt2);
1438 /***************************************************************************
1439 * Setup the treeview structure with regards of the sort method
1440 * and sort the children of the TV item specified in lParam
1441 * fRecurse: currently unused. Should be zero.
1442 * parent: if pSort!=NULL, should equal pSort->hParent.
1443 * otherwise, item which child items are to be sorted.
1444 * pSort: sort method info. if NULL, sort on item text.
1445 * if non-NULL, sort on item's lParam content, and let the
1446 * application decide what that means. See also TVM_SORTCHILDRENCB.
1449 static LRESULT WINAPI TREEVIEW_Sort (
1456 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
1457 TREEVIEW_ITEM *sortMe = NULL; /* Node for which we sort the children */
1459 /* Obtain the TVSORTBC struct */
1460 infoPtr->pCallBackSort = pSort;
1462 /* undocumented feature: TVI_ROOT means `sort the whole tree' */
1464 if (parent==TVI_ROOT)
1465 parent=infoPtr->TopRootItem;
1467 /* Check for a valid handle to the parent item */
1468 if (!TREEVIEW_ValidItem(infoPtr, parent))
1470 ERR ("invalid item hParent=%x\n", (INT)parent);
1474 /* Obtain the parent node to sort */
1475 sortMe = &infoPtr->items[ (INT)parent ];
1477 /* Make sure there is something to sort */
1478 if ( sortMe->cChildren > 1 )
1480 /* pointer organization */
1481 HDPA sortList = DPA_Create(sortMe->cChildren);
1482 HTREEITEM itemHandle = sortMe->firstChild;
1483 TREEVIEW_ITEM *itemPtr = & infoPtr->items[ (INT)itemHandle ];
1485 /* TREEVIEW_ITEM rechaining */
1491 /* Build the list of item to sort */
1495 sortList, /* the list */
1496 sortMe->cChildren+1, /* force the insertion to be an append */
1497 itemPtr); /* the ptr to store */
1499 /* Get the next sibling */
1500 itemHandle = itemPtr->sibling;
1501 itemPtr = & infoPtr->items[ (INT)itemHandle ];
1502 } while ( itemHandle != NULL );
1504 /* let DPA perform the sort activity */
1507 sortList, /* what */
1508 TREEVIEW_CallBackCompare, /* how */
1512 sortList, /* what */
1513 TREEVIEW_SortOnName, /* how */
1517 * Reorganized TREEVIEW_ITEM structures.
1518 * Note that we know we have at least two elements.
1521 /* Get the first item and get ready to start... */
1522 item = DPA_GetPtr(sortList, count++);
1523 while ( (nextItem = DPA_GetPtr(sortList, count++)) != NULL )
1525 /* link the two current item toghether */
1526 ((TREEVIEW_ITEM*)item)->sibling = ((TREEVIEW_ITEM*)nextItem)->hItem;
1527 ((TREEVIEW_ITEM*)nextItem)->upsibling = ((TREEVIEW_ITEM*)item)->hItem;
1529 if (prevItem == NULL) /* this is the first item, update the parent */
1531 sortMe->firstChild = ((TREEVIEW_ITEM*)item)->hItem;
1532 ((TREEVIEW_ITEM*)item)->upsibling = NULL;
1534 else /* fix the back chaining */
1536 ((TREEVIEW_ITEM*)item)->upsibling = ((TREEVIEW_ITEM*)prevItem)->hItem;
1539 /* get ready for the next one */
1544 /* the last item is pointed to by item and never has a sibling */
1545 ((TREEVIEW_ITEM*)item)->sibling = NULL;
1547 DPA_Destroy(sortList);
1555 /***************************************************************************
1556 * Setup the treeview structure with regards of the sort method
1557 * and sort the children of the TV item specified in lParam
1559 static LRESULT WINAPI TREEVIEW_SortChildrenCB(
1565 LPTVSORTCB pSort=(LPTVSORTCB) lParam;
1567 return TREEVIEW_Sort (hwnd, wParam, pSort->hParent, pSort);
1571 /***************************************************************************
1572 * Sort the children of the TV item specified in lParam.
1574 static LRESULT WINAPI TREEVIEW_SortChildren (
1579 return TREEVIEW_Sort (hwnd, (BOOL) wParam, (HTREEITEM) lParam, NULL);
1584 /* the method used below isn't the most memory-friendly, but it avoids
1585 a lot of memory reallocations */
1587 /* BTW: we waste handle 0; 0 is not an allowed handle. */
1590 TREEVIEW_InsertItemA (HWND hwnd, WPARAM wParam, LPARAM lParam)
1593 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
1594 TVINSERTSTRUCTA *ptdi;
1596 TREEVIEW_ITEM *wineItem, *parentItem, *prevsib, *sibItem;
1597 INT iItem,listItems,i,len;
1599 /* Item to insert */
1600 ptdi = (LPTVINSERTSTRUCTA) lParam;
1602 /* check if memory is available */
1604 if (infoPtr->uNumPtrsAlloced==0) {
1605 infoPtr->items = COMCTL32_Alloc (TVITEM_ALLOC*sizeof (TREEVIEW_ITEM));
1606 infoPtr->freeList= COMCTL32_Alloc ((1+(TVITEM_ALLOC>>5)) * sizeof (INT));
1607 infoPtr->uNumPtrsAlloced=TVITEM_ALLOC;
1608 infoPtr->TopRootItem=(HTREEITEM)1;
1612 * Reallocate contiguous space for items
1614 if (infoPtr->uNumItems == (infoPtr->uNumPtrsAlloced-1) ) {
1615 TREEVIEW_ITEM *oldItems = infoPtr->items;
1616 INT *oldfreeList = infoPtr->freeList;
1618 infoPtr->uNumPtrsAlloced*=2;
1619 infoPtr->items = COMCTL32_Alloc (infoPtr->uNumPtrsAlloced*sizeof (TREEVIEW_ITEM));
1620 infoPtr->freeList= COMCTL32_Alloc ((1+(infoPtr->uNumPtrsAlloced>>5))*sizeof (INT));
1622 memcpy (&infoPtr->items[0], &oldItems[0],
1623 infoPtr->uNumPtrsAlloced/2 * sizeof(TREEVIEW_ITEM));
1624 memcpy (&infoPtr->freeList[0], &oldfreeList[0],
1625 (infoPtr->uNumPtrsAlloced>>6) * sizeof(INT));
1627 COMCTL32_Free (oldItems);
1628 COMCTL32_Free (oldfreeList);
1632 * Reset infoPtr structure with new stat according to current TV picture
1635 infoPtr->uNumItems++;
1636 if ((INT)infoPtr->uMaxHandle==(infoPtr->uNumItems-1)) {
1637 iItem=infoPtr->uNumItems;
1638 infoPtr->uMaxHandle = (HTREEITEM)((INT)infoPtr->uMaxHandle + 1);
1639 } else { /* check freelist */
1640 for (i=0; i<infoPtr->uNumPtrsAlloced>>5; i++) {
1641 if (infoPtr->freeList[i]) {
1642 iItem=ffs (infoPtr->freeList[i])-1;
1643 tv_clear_bit(iItem,&infoPtr->freeList[i]);
1650 if (TRACE_ON(treeview)) {
1651 for (i=0; i<infoPtr->uNumPtrsAlloced>>5; i++)
1652 TRACE("%8x\n",infoPtr->freeList[i]);
1655 if (!iItem) ERR("Argh -- can't find free item.\n");
1658 * Find the parent item of the new item
1660 tvItem= & ptdi->DUMMYUNIONNAME.itemex;
1661 wineItem=& infoPtr->items[iItem];
1663 if ((ptdi->hParent==TVI_ROOT) || (ptdi->hParent==0)) {
1665 wineItem->parent = 0;
1666 sibItem = &infoPtr->items [(INT)infoPtr->TopRootItem];
1667 listItems = infoPtr->uNumItems;
1670 parentItem = &infoPtr->items[(INT)ptdi->hParent];
1672 /* Do the insertion here it if it's the only item of this parent */
1673 if (!parentItem->firstChild)
1674 parentItem->firstChild=(HTREEITEM)iItem;
1676 wineItem->parent = ptdi->hParent;
1677 sibItem = &infoPtr->items [(INT)parentItem->firstChild];
1678 parentItem->cChildren++;
1679 listItems = parentItem->cChildren;
1683 /* NOTE: I am moving some setup of the wineItem object that was initialy
1684 * done at the end of the function since some of the values are
1685 * required by the Callback sorting
1688 if (tvItem->mask & TVIF_TEXT)
1691 * Setup the item text stuff here since it's required by the Sort method
1692 * when the insertion are ordered
1694 if (tvItem->pszText!=LPSTR_TEXTCALLBACKA)
1696 TRACE("(%p,%s)\n", &tvItem->pszText, tvItem->pszText);
1697 len = lstrlenA (tvItem->pszText)+1;
1698 wineItem->pszText= COMCTL32_Alloc (len+1);
1699 lstrcpyA (wineItem->pszText, tvItem->pszText);
1700 wineItem->cchTextMax=len;
1704 TRACE("LPSTR_TEXTCALLBACK\n");
1705 wineItem->pszText = LPSTR_TEXTCALLBACKA;
1706 wineItem->cchTextMax = 0;
1710 if (tvItem->mask & TVIF_PARAM)
1711 wineItem->lParam=tvItem->lParam;
1714 wineItem->upsibling=0; /* needed in case we're the first item in a list */
1715 wineItem->sibling=0;
1716 wineItem->firstChild=0;
1717 wineItem->hItem=(HTREEITEM)iItem;
1722 switch ((DWORD) ptdi->hInsertAfter) {
1723 case (DWORD) TVI_FIRST:
1724 if (sibItem==wineItem) break;
1725 if (wineItem->parent) {
1726 wineItem->sibling=parentItem->firstChild;
1727 parentItem->firstChild=(HTREEITEM)iItem;
1729 wineItem->sibling=infoPtr->TopRootItem;
1730 infoPtr->TopRootItem=(HTREEITEM)iItem;
1732 sibItem->upsibling=(HTREEITEM)iItem;
1735 case (DWORD) TVI_SORT:
1736 if (sibItem==wineItem)
1738 * This item is the first child of the level and it
1739 * has already been inserted
1744 TREEVIEW_ITEM *aChild;
1747 TREEVIEW_ITEM *previousChild = NULL;
1748 BOOL bItemInserted = FALSE;
1751 aChild = &infoPtr->items[(INT)parentItem->firstChild];
1753 aChild = &infoPtr->items[(INT)infoPtr->TopRootItem];
1755 /* Iterate the parent children to see where we fit in */
1756 while ( aChild != NULL )
1758 INT comp = strcmp(wineItem->pszText, aChild->pszText);
1759 if ( comp < 0 ) /* we are smaller than the current one */
1761 TREEVIEW_InsertBefore(infoPtr, wineItem, aChild, parentItem);
1762 bItemInserted = TRUE;
1765 else if ( comp > 0 ) /* we are bigger than the current one */
1767 previousChild = aChild;
1768 aChild = (aChild->sibling == 0) /* This will help us to exit */
1769 ? NULL /* if there is no more sibling */
1770 : &infoPtr->items[(INT)aChild->sibling];
1772 /* Look at the next item */
1775 else if ( comp == 0 )
1778 * An item with this name is already existing, therefore,
1779 * we add after the one we found
1781 TREEVIEW_InsertAfter(infoPtr, wineItem, aChild, parentItem);
1782 bItemInserted = TRUE;
1788 * we reach the end of the child list and the item as not
1789 * yet been inserted, therefore, insert it after the last child.
1791 if ( (! bItemInserted ) && (aChild == NULL) )
1792 TREEVIEW_InsertAfter(infoPtr, wineItem, previousChild, parentItem);
1798 case (DWORD) TVI_LAST:
1799 if (sibItem==wineItem) break;
1800 while (sibItem->sibling) {
1802 sibItem=&infoPtr->items [(INT)sibItem->sibling];
1804 sibItem->sibling=(HTREEITEM)iItem;
1805 wineItem->upsibling=sibItem->hItem;
1808 while ((sibItem->sibling) && (sibItem->hItem!=ptdi->hInsertAfter))
1811 sibItem=&infoPtr->items [(INT)sibItem->sibling];
1813 if (sibItem->hItem!=ptdi->hInsertAfter) {
1814 ERR("tried to insert item after nonexisting handle %d.\n",
1815 (INT) ptdi->hInsertAfter);
1819 if (sibItem->sibling) {
1820 sibItem=&infoPtr->items [(INT)sibItem->sibling];
1821 sibItem->upsibling=(HTREEITEM)iItem;
1822 wineItem->sibling=sibItem->hItem;
1824 prevsib->sibling=(HTREEITEM)iItem;
1825 wineItem->upsibling=prevsib->hItem;
1831 /* Fill in info structure */
1833 TRACE("new item %d; parent %d, mask %x\n", iItem,
1834 (INT)wineItem->parent,tvItem->mask);
1836 wineItem->mask=tvItem->mask;
1837 wineItem->iIntegral=1;
1839 if (tvItem->mask & TVIF_CHILDREN) {
1840 wineItem->cChildren=tvItem->cChildren;
1841 if (tvItem->cChildren==I_CHILDRENCALLBACK)
1842 FIXME(" I_CHILDRENCALLBACK not supported\n");
1845 wineItem->expandBox.left = 0; /* Initialize the expandBox */
1846 wineItem->expandBox.top = 0;
1847 wineItem->expandBox.right = 0;
1848 wineItem->expandBox.bottom = 0;
1850 if (tvItem->mask & TVIF_IMAGE)
1851 wineItem->iImage=tvItem->iImage;
1853 /* If the application sets TVIF_INTEGRAL without
1854 supplying a TVITEMEX structure, it's toast */
1856 if (tvItem->mask & TVIF_INTEGRAL)
1857 wineItem->iIntegral=tvItem->iIntegral;
1859 if (tvItem->mask & TVIF_SELECTEDIMAGE)
1860 wineItem->iSelectedImage=tvItem->iSelectedImage;
1862 if (tvItem->mask & TVIF_STATE) {
1863 TRACE("Changing item state from %d to %d\n",
1866 wineItem->state=tvItem->state;
1867 wineItem->stateMask=tvItem->stateMask;
1871 TREEVIEW_QueueRefresh (hwnd);
1873 return (LRESULT) iItem;
1878 TREEVIEW_InsertItemW(HWND hwnd, WPARAM wParam, LPARAM lParam)
1880 TVINSERTSTRUCTW *tvisW;
1881 TVINSERTSTRUCTA tvisA;
1884 tvisW = (LPTVINSERTSTRUCTW)lParam;
1886 tvisA.hParent = tvisW->hParent;
1887 tvisA.hInsertAfter = tvisW->hInsertAfter;
1889 tvisA.DUMMYUNIONNAME.item.mask = tvisW->DUMMYUNIONNAME.item.mask;
1890 tvisA.DUMMYUNIONNAME.item.hItem = tvisW->DUMMYUNIONNAME.item.hItem;
1891 tvisA.DUMMYUNIONNAME.item.state = tvisW->DUMMYUNIONNAME.item.state;
1892 tvisA.DUMMYUNIONNAME.item.stateMask = tvisW->DUMMYUNIONNAME.item.stateMask;
1893 tvisA.DUMMYUNIONNAME.item.cchTextMax = tvisW->DUMMYUNIONNAME.item.cchTextMax;
1895 if(tvisW->DUMMYUNIONNAME.item.pszText)
1897 if (tvisW->DUMMYUNIONNAME.item.pszText!=LPSTR_TEXTCALLBACKW)
1899 int len = lstrlenW (tvisW->DUMMYUNIONNAME.item.pszText)+1;
1900 tvisA.DUMMYUNIONNAME.item.pszText = COMCTL32_Alloc (len);
1901 lstrcpyWtoA (tvisA.DUMMYUNIONNAME.item.pszText,
1902 tvisW->DUMMYUNIONNAME.item.pszText );
1906 tvisA.DUMMYUNIONNAME.item.pszText = LPSTR_TEXTCALLBACKA;
1907 tvisA.DUMMYUNIONNAME.item.cchTextMax = 0;
1911 tvisA.DUMMYUNIONNAME.item.iImage = tvisW->DUMMYUNIONNAME.item.iImage;
1912 tvisA.DUMMYUNIONNAME.item.iSelectedImage = tvisW->DUMMYUNIONNAME.item.iSelectedImage;
1913 tvisA.DUMMYUNIONNAME.item.cChildren = tvisW->DUMMYUNIONNAME.item.cChildren;
1914 tvisA.DUMMYUNIONNAME.item.lParam = tvisW->DUMMYUNIONNAME.item.lParam;
1916 lRes = TREEVIEW_InsertItemA(hwnd,wParam,(LPARAM)&tvisA);
1918 if (tvisA.DUMMYUNIONNAME.item.pszText!=LPSTR_TEXTCALLBACKA)
1920 COMCTL32_Free(tvisA.DUMMYUNIONNAME.item.pszText);
1929 TREEVIEW_DeleteItem (HWND hwnd, WPARAM wParam, LPARAM lParam)
1931 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
1933 TREEVIEW_ITEM *wineItem;
1936 if (!infoPtr) return FALSE;
1938 if (lParam == (INT)TVI_ROOT) {
1939 TREEVIEW_RemoveTree (hwnd);
1941 iItem= (INT) lParam;
1942 wineItem = TREEVIEW_ValidItem (infoPtr, (HTREEITEM)iItem);
1943 if (!wineItem) return FALSE;
1944 TRACE("%s\n",wineItem->pszText);
1945 TREEVIEW_RemoveItem (hwnd, wineItem);
1948 TREEVIEW_QueueRefresh (hwnd);
1956 TREEVIEW_GetIndent (HWND hwnd)
1958 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
1961 return infoPtr->uIndent;
1965 TREEVIEW_SetIndent (HWND hwnd, WPARAM wParam)
1967 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
1971 newIndent=(INT) wParam;
1972 if (newIndent < MINIMUM_INDENT) newIndent=MINIMUM_INDENT;
1973 infoPtr->uIndent=newIndent;
1979 TREEVIEW_GetToolTips (HWND hwnd)
1982 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
1985 return infoPtr->hwndToolTip;
1990 TREEVIEW_SetToolTips (HWND hwnd, WPARAM wParam)
1993 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
1997 prevToolTip=infoPtr->hwndToolTip;
1998 infoPtr->hwndToolTip= (HWND) wParam;
2004 static LRESULT CALLBACK
2005 TREEVIEW_GetEditControl (HWND hwnd)
2008 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
2010 return infoPtr->hwndEdit;
2014 TREEVIEW_Edit_SubclassProc (HWND hwnd, UINT uMsg, WPARAM wParam,
2022 HDC hdc = (HDC) wParam;
2023 GetClientRect (hwnd, &rc);
2024 Rectangle (hdc, rc.left, rc.top, rc.right, rc.bottom);
2030 return DLGC_WANTARROWS | DLGC_WANTALLKEYS;
2035 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(GetParent(hwnd));
2036 return CallWindowProcA( infoPtr->wpEditOrig, hwnd, uMsg, wParam, lParam);
2044 /* should handle edit control messages here */
2047 TREEVIEW_Command (HWND hwnd, WPARAM wParam, LPARAM lParam)
2050 TRACE("%x %ld\n",wParam, lParam);
2052 switch (HIWORD(wParam))
2057 * Adjust the edit window size
2059 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
2060 TREEVIEW_ITEM *editItem = TREEVIEW_ValidItem(infoPtr, infoPtr->editItem);
2061 INT iLength = GetWindowTextLengthA(infoPtr->hwndEdit);
2062 HDC hdc = GetDC(infoPtr->hwndEdit);
2065 if ( GetTextMetricsA(hdc, &tm) )
2067 LONG newWidth = (iLength * tm.tmAveCharWidth) + 15;
2072 editItem->text.left - 2,
2073 editItem->text.top - 1,
2075 editItem->text.bottom - editItem->text.top + 3,
2078 ReleaseDC(hwnd, hdc);
2084 /* TREEVIEW_EndEditLabelNow(hwnd, (WPARAM)FALSE, 0);
2089 return SendMessageA (GetParent (hwnd), WM_COMMAND, wParam, lParam);
2096 TREEVIEW_Size (HWND hwnd, WPARAM wParam, LPARAM lParam)
2099 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
2101 if (infoPtr->bAutoSize)
2103 infoPtr->bAutoSize = FALSE;
2106 infoPtr->bAutoSize = TRUE;
2108 if (wParam == SIZE_RESTORED)
2110 infoPtr->uTotalWidth = LOWORD (lParam);
2111 infoPtr->uTotalHeight = HIWORD (lParam);
2113 FIXME("WM_SIZE flag %x %lx not handled\n", wParam, lParam);
2116 TREEVIEW_QueueRefresh (hwnd);
2123 TREEVIEW_StyleChanged (HWND hwnd, WPARAM wParam, LPARAM lParam)
2125 TRACE("(%x %lx)\n",wParam,lParam);
2127 TREEVIEW_Refresh (hwnd);
2133 TREEVIEW_Create (HWND hwnd, WPARAM wParam, LPARAM lParam)
2135 TREEVIEW_INFO *infoPtr;
2140 TRACE("wnd %x\n",hwnd);
2141 /* allocate memory for info structure */
2142 infoPtr = (TREEVIEW_INFO *) COMCTL32_Alloc (sizeof(TREEVIEW_INFO));
2144 SetWindowLongA( hwnd, 0, (DWORD)infoPtr);
2146 if (infoPtr == NULL) {
2147 ERR("could not allocate info memory!\n");
2151 if ((TREEVIEW_INFO*) GetWindowLongA( hwnd, 0) != infoPtr) {
2152 ERR("pointer assignment error!\n");
2158 /* set default settings */
2159 infoPtr->uInternalStatus=0;
2160 infoPtr->uNumItems=0;
2161 infoPtr->clrBk = GetSysColor (COLOR_WINDOW);
2162 infoPtr->clrText = GetSysColor (COLOR_BTNTEXT);
2163 infoPtr->clrLine = GetSysColor (COLOR_WINDOWTEXT);
2166 infoPtr->uIndent = 15;
2167 infoPtr->himlNormal = NULL;
2168 infoPtr->himlState = NULL;
2169 infoPtr->uItemHeight = -1;
2170 GetTextMetricsA (hdc, &tm);
2171 infoPtr->hFont = GetStockObject (DEFAULT_GUI_FONT);
2172 GetObjectA (infoPtr->hFont, sizeof (LOGFONTA), &logFont);
2173 logFont.lfWeight=FW_BOLD;
2174 infoPtr->hBoldFont = CreateFontIndirectA (&logFont);
2176 infoPtr->items = NULL;
2177 infoPtr->selectedItem=0;
2178 infoPtr->clrText=-1; /* use system color */
2179 infoPtr->dropItem=0;
2180 infoPtr->pCallBackSort=NULL;
2181 infoPtr->uScrollTime = 300; /* milliseconds */
2184 infoPtr->hwndNotify = GetParent32 (hwnd);
2185 infoPtr->bTransparent = ( GetWindowLongA( hwnd, GWL_STYLE) & TBSTYLE_FLAT);
2188 infoPtr->hwndToolTip=0;
2189 if (!( GetWindowLongA( hwnd, GWL_STYLE) & TVS_NOTOOLTIPS)) { /* Create tooltip control */
2192 infoPtr->hwndToolTip =
2193 CreateWindowExA (0, TOOLTIPS_CLASSA, NULL, 0,
2194 CW_USEDEFAULT, CW_USEDEFAULT,
2195 CW_USEDEFAULT, CW_USEDEFAULT,
2198 /* Send NM_TOOLTIPSCREATED notification */
2199 if (infoPtr->hwndToolTip) {
2200 NMTOOLTIPSCREATED nmttc;
2202 nmttc.hdr.hwndFrom = hwnd;
2203 nmttc.hdr.idFrom = GetWindowLongA( hwnd, GWL_ID);
2204 nmttc.hdr.code = NM_TOOLTIPSCREATED;
2205 nmttc.hwndToolTips = infoPtr->hwndToolTip;
2207 SendMessageA (GetParent (hwnd), WM_NOTIFY,
2208 (WPARAM) GetWindowLongA( hwnd, GWL_ID), (LPARAM)&nmttc);
2211 ZeroMemory (&ti, sizeof(TTTOOLINFOA));
2212 ti.cbSize = sizeof(TTTOOLINFOA);
2213 ti.uFlags = TTF_IDISHWND | TTF_TRACK | TTF_TRANSPARENT ;
2216 ti.lpszText = "Test"; /* LPSTR_TEXTCALLBACK; */
2217 SetRectEmpty (&ti.rect);
2219 SendMessageA (infoPtr->hwndToolTip, TTM_ADDTOOLA, 0, (LPARAM)&ti);
2222 infoPtr->hwndEdit = CreateWindowExA (
2226 WS_CHILD | WS_BORDER | ES_AUTOHSCROLL |
2227 ES_WANTRETURN | ES_LEFT,
2230 0,0,0); /* FIXME: (HMENU)IDTVEDIT,pcs->hInstance,0);*/
2232 SendMessageA ( infoPtr->hwndEdit, WM_SETFONT, infoPtr->hFont, FALSE);
2233 infoPtr->wpEditOrig = (WNDPROC)SetWindowLongA (
2236 (LONG) TREEVIEW_Edit_SubclassProc);
2238 ReleaseDC (hwnd, hdc);
2245 TREEVIEW_Destroy (HWND hwnd)
2247 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
2250 TREEVIEW_RemoveTree (hwnd);
2251 if (infoPtr->Timer & TV_REFRESH_TIMER_SET)
2252 KillTimer (hwnd, TV_REFRESH_TIMER);
2253 if (infoPtr->hwndToolTip)
2254 DestroyWindow (infoPtr->hwndToolTip);
2256 COMCTL32_Free (infoPtr);
2262 TREEVIEW_Paint (HWND hwnd, WPARAM wParam, LPARAM lParam)
2268 hdc = wParam==0 ? BeginPaint (hwnd, &ps) : (HDC)wParam;
2269 TREEVIEW_Refresh (hwnd);
2271 EndPaint (hwnd, &ps);
2274 return DefWindowProcA (hwnd, WM_PAINT, wParam, lParam);
2278 TREEVIEW_SetFocus (HWND hwnd, WPARAM wParam, LPARAM lParam)
2280 TREEVIEW_SendSimpleNotify (hwnd, NM_SETFOCUS);
2281 InvalidateRect(hwnd, NULL, FALSE);
2286 TREEVIEW_KillFocus (HWND hwnd, WPARAM wParam, LPARAM lParam)
2288 TREEVIEW_SendSimpleNotify (hwnd, NM_KILLFOCUS);
2289 InvalidateRect(hwnd, NULL, FALSE);
2294 TREEVIEW_EraseBackground (HWND hwnd, WPARAM wParam, LPARAM lParam)
2296 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
2297 HBRUSH hBrush = CreateSolidBrush (infoPtr->clrBk);
2301 GetClientRect (hwnd, &rect);
2302 FillRect ((HDC)wParam, &rect, hBrush);
2303 DeleteObject (hBrush);
2319 TREEVIEW_SendSimpleNotify (HWND hwnd, UINT code)
2324 nmhdr.hwndFrom = hwnd;
2325 nmhdr.idFrom = GetWindowLongA( hwnd, GWL_ID);
2328 return (BOOL) SendMessageA (GetParent (hwnd), WM_NOTIFY,
2329 (WPARAM)nmhdr.idFrom, (LPARAM)&nmhdr);
2335 TREEVIEW_SendTreeviewNotify (HWND hwnd, UINT code, UINT action,
2336 HTREEITEM oldItem, HTREEITEM newItem)
2339 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
2341 TREEVIEW_ITEM *wineItem;
2343 TRACE("code:%x action:%x olditem:%x newitem:%x\n",
2344 code,action,(INT)oldItem,(INT)newItem);
2345 nmhdr.hdr.hwndFrom = hwnd;
2346 nmhdr.hdr.idFrom = GetWindowLongA( hwnd, GWL_ID);
2347 nmhdr.hdr.code = code;
2348 nmhdr.action = action;
2350 wineItem=& infoPtr->items[(INT)oldItem];
2351 nmhdr.itemOld.mask = wineItem->mask;
2352 nmhdr.itemOld.hItem = wineItem->hItem;
2353 nmhdr.itemOld.state = wineItem->state;
2354 nmhdr.itemOld.stateMask = wineItem->stateMask;
2355 nmhdr.itemOld.iImage = wineItem->iImage;
2356 nmhdr.itemOld.pszText = wineItem->pszText;
2357 nmhdr.itemOld.cchTextMax= wineItem->cchTextMax;
2358 nmhdr.itemOld.iImage = wineItem->iImage;
2359 nmhdr.itemOld.iSelectedImage = wineItem->iSelectedImage;
2360 nmhdr.itemOld.cChildren = wineItem->cChildren;
2361 nmhdr.itemOld.lParam = wineItem->lParam;
2365 wineItem=& infoPtr->items[(INT)newItem];
2366 nmhdr.itemNew.mask = wineItem->mask;
2367 nmhdr.itemNew.hItem = wineItem->hItem;
2368 nmhdr.itemNew.state = wineItem->state;
2369 nmhdr.itemNew.stateMask = wineItem->stateMask;
2370 nmhdr.itemNew.iImage = wineItem->iImage;
2371 nmhdr.itemNew.pszText = wineItem->pszText;
2372 nmhdr.itemNew.cchTextMax= wineItem->cchTextMax;
2373 nmhdr.itemNew.iImage = wineItem->iImage;
2374 nmhdr.itemNew.iSelectedImage = wineItem->iSelectedImage;
2375 nmhdr.itemNew.cChildren = wineItem->cChildren;
2376 nmhdr.itemNew.lParam = wineItem->lParam;
2382 return (BOOL)SendMessageA (GetParent (hwnd), WM_NOTIFY,
2383 (WPARAM) GetWindowLongA( hwnd, GWL_ID), (LPARAM)&nmhdr);
2388 TREEVIEW_SendTreeviewDnDNotify (HWND hwnd, UINT code, HTREEITEM dragItem,
2391 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
2393 TREEVIEW_ITEM *wineItem;
2395 TRACE("code:%x dragitem:%x\n", code,(INT)dragItem);
2397 nmhdr.hdr.hwndFrom = hwnd;
2398 nmhdr.hdr.idFrom = GetWindowLongA( hwnd, GWL_ID);
2399 nmhdr.hdr.code = code;
2401 wineItem=& infoPtr->items[(INT)dragItem];
2402 nmhdr.itemNew.mask = wineItem->mask;
2403 nmhdr.itemNew.hItem = wineItem->hItem;
2404 nmhdr.itemNew.state = wineItem->state;
2405 nmhdr.itemNew.lParam = wineItem->lParam;
2407 nmhdr.ptDrag.x = pt.x;
2408 nmhdr.ptDrag.y = pt.y;
2410 return (BOOL)SendMessageA (GetParent (hwnd), WM_NOTIFY,
2411 (WPARAM) GetWindowLongA( hwnd, GWL_ID), (LPARAM)&nmhdr);
2418 TREEVIEW_SendDispInfoNotify (HWND hwnd, TREEVIEW_ITEM *wineItem,
2419 UINT code, UINT what)
2425 TRACE("item %d, action %x, state %d\n",
2426 (INT)wineItem->hItem,
2428 (INT)wineItem->state);
2430 tvdi.hdr.hwndFrom = hwnd;
2431 tvdi.hdr.idFrom = GetWindowLongA( hwnd, GWL_ID);
2432 tvdi.hdr.code = code;
2433 tvdi.item.mask = what;
2434 tvdi.item.hItem = wineItem->hItem;
2435 tvdi.item.state = wineItem->state;
2436 tvdi.item.lParam = wineItem->lParam;
2437 tvdi.item.pszText = COMCTL32_Alloc (128*sizeof(char));
2438 buf = tvdi.item.pszText;
2440 retval=(BOOL)SendMessageA (
2443 (WPARAM)tvdi.hdr.idFrom,
2446 if (what & TVIF_TEXT) {
2447 wineItem->pszText = tvdi.item.pszText;
2448 if (buf==tvdi.item.pszText) {
2449 wineItem->cchTextMax = 128;
2451 TRACE("user-supplied buffer\n");
2452 COMCTL32_Free (buf);
2453 wineItem->cchTextMax = 0;
2456 if (what & TVIF_SELECTEDIMAGE)
2457 wineItem->iSelectedImage = tvdi.item.iSelectedImage;
2458 if (what & TVIF_IMAGE)
2459 wineItem->iImage = tvdi.item.iImage;
2460 if (what & TVIF_CHILDREN)
2461 wineItem->cChildren = tvdi.item.cChildren;
2469 TREEVIEW_SendCustomDrawNotify (HWND hwnd, DWORD dwDrawStage, HDC hdc,
2472 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
2473 NMTVCUSTOMDRAW nmcdhdr;
2474 LPNMCUSTOMDRAW nmcd;
2476 TRACE("drawstage:%lx hdc:%x\n", dwDrawStage, hdc);
2478 nmcd= & nmcdhdr.nmcd;
2479 nmcd->hdr.hwndFrom = hwnd;
2480 nmcd->hdr.idFrom = GetWindowLongA( hwnd, GWL_ID);
2481 nmcd->hdr.code = NM_CUSTOMDRAW;
2482 nmcd->dwDrawStage= dwDrawStage;
2484 nmcd->rc.left = rc.left;
2485 nmcd->rc.right = rc.right;
2486 nmcd->rc.bottom = rc.bottom;
2487 nmcd->rc.top = rc.top;
2488 nmcd->dwItemSpec = 0;
2489 nmcd->uItemState = 0;
2490 nmcd->lItemlParam= 0;
2491 nmcdhdr.clrText = infoPtr->clrText;
2492 nmcdhdr.clrTextBk= infoPtr->clrBk;
2495 return (BOOL)SendMessageA (GetParent (hwnd), WM_NOTIFY,
2496 (WPARAM) GetWindowLongA( hwnd, GWL_ID), (LPARAM)&nmcdhdr);
2502 /* FIXME: need to find out when the flags in uItemState need to be set */
2505 TREEVIEW_SendCustomDrawItemNotify (HWND hwnd, HDC hdc,
2506 TREEVIEW_ITEM *wineItem, UINT uItemDrawState)
2508 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
2509 NMTVCUSTOMDRAW nmcdhdr;
2510 LPNMCUSTOMDRAW nmcd;
2511 DWORD dwDrawStage,dwItemSpec;
2514 dwDrawStage=CDDS_ITEM | uItemDrawState;
2515 dwItemSpec=(DWORD)wineItem->hItem;
2517 if (wineItem->hItem==infoPtr->selectedItem) uItemState|=CDIS_SELECTED;
2518 if (wineItem->hItem==infoPtr->focusItem) uItemState|=CDIS_FOCUS;
2519 if (wineItem->hItem==infoPtr->hotItem) uItemState|=CDIS_HOT;
2521 nmcd= & nmcdhdr.nmcd;
2522 nmcd->hdr.hwndFrom = hwnd;
2523 nmcd->hdr.idFrom = GetWindowLongA( hwnd, GWL_ID);
2524 nmcd->hdr.code = NM_CUSTOMDRAW;
2525 nmcd->dwDrawStage= dwDrawStage;
2527 nmcd->rc.left = wineItem->rect.left;
2528 nmcd->rc.right = wineItem->rect.right;
2529 nmcd->rc.bottom = wineItem->rect.bottom;
2530 nmcd->rc.top = wineItem->rect.top;
2531 nmcd->dwItemSpec = dwItemSpec;
2532 nmcd->uItemState = uItemState;
2533 nmcd->lItemlParam= wineItem->lParam;
2535 nmcdhdr.clrText = infoPtr->clrText;
2536 nmcdhdr.clrTextBk= infoPtr->clrBk;
2537 nmcdhdr.iLevel = wineItem->iLevel;
2539 TRACE("drawstage:%lx hdc:%x item:%lx, itemstate:%x\n",
2540 dwDrawStage, hdc, dwItemSpec, uItemState);
2542 return (BOOL)SendMessageA (GetParent (hwnd), WM_NOTIFY,
2543 (WPARAM) GetWindowLongA( hwnd, GWL_ID), (LPARAM)&nmcdhdr);
2548 /* Note:If the specified item is the child of a collapsed parent item,
2549 the parent's list of child items is (recursively) expanded to reveal the
2550 specified item. This is mentioned for TREEVIEW_SelectItem; don't
2551 know if it also applies here.
2555 TREEVIEW_Expand (HWND hwnd, WPARAM wParam, LPARAM lParam)
2557 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
2558 TREEVIEW_ITEM *wineItem;
2562 flag = (UINT) wParam;
2563 expand = (INT) lParam;
2565 wineItem = TREEVIEW_ValidItem (infoPtr, (HTREEITEM)expand);
2569 if (!wineItem->cChildren)
2572 if (wineItem->pszText==LPSTR_TEXTCALLBACKA)
2573 TRACE ("For item %d, flags %d, state %d\n",
2574 expand, flag, wineItem->state);
2576 TRACE("For (%s) item:%d, flags %x, state:%d\n",
2577 wineItem->pszText, flag, expand, wineItem->state);
2579 if (wineItem->cChildren==I_CHILDRENCALLBACK) {
2580 FIXME("we don't handle I_CHILDRENCALLBACK yet\n");
2584 if (flag == TVE_TOGGLE) { /* FIXME: check exact behaviour here */
2585 flag &= ~TVE_TOGGLE; /* ie: bitwise ops or 'case' ops */
2586 if (wineItem->state & TVIS_EXPANDED)
2587 flag |= TVE_COLLAPSE;
2594 case TVE_COLLAPSERESET:
2595 TRACE(" case TVE_COLLAPSERESET\n");
2596 if (!wineItem->state & TVIS_EXPANDED)
2599 wineItem->state &= ~(TVIS_EXPANDEDONCE | TVIS_EXPANDED);
2600 TREEVIEW_RemoveAllChildren (hwnd, wineItem);
2604 TRACE(" case TVE_COLLAPSE\n");
2605 if (!wineItem->state & TVIS_EXPANDED)
2608 wineItem->state &= ~TVIS_EXPANDED;
2612 TRACE(" case TVE_EXPAND\n");
2613 if (wineItem->state & TVIS_EXPANDED)
2616 TRACE(" is not expanded...\n");
2618 if (!(wineItem->state & TVIS_EXPANDEDONCE))
2620 TRACE(" and has never been expanded...\n");
2621 wineItem->state |= TVIS_EXPANDED;
2623 /* this item has never been expanded */
2624 if (TREEVIEW_SendTreeviewNotify (
2631 TRACE(" TVN_ITEMEXPANDING returned TRUE, exiting...\n");
2636 * Since the TVN_ITEMEXPANDING message may has caused the parent to
2637 * insert new items which in turn may have cause items placeholder
2638 * reallocation, I reassign the current item pointer so we have
2639 * something valid to work with...
2640 * However, this should not be necessary,
2641 * investigation required in TREEVIEW_InsertItemA
2643 wineItem = TREEVIEW_ValidItem (infoPtr, (HTREEITEM)expand);
2647 "Catastropic situation, cannot retreive item #%d\n",
2652 wineItem->state |= TVIS_EXPANDEDONCE;
2653 TRACE(" TVN_ITEMEXPANDING sent...\n");
2655 TREEVIEW_SendTreeviewNotify (
2662 TRACE(" TVN_ITEMEXPANDED sent...\n");
2667 /* this item has already been expanded */
2668 wineItem->state |= TVIS_EXPANDED;
2672 case TVE_EXPANDPARTIAL:
2673 TRACE(" case TVE_EXPANDPARTIAL\n");
2674 FIXME("TVE_EXPANDPARTIAL not implemented\n");
2675 wineItem->state ^=TVIS_EXPANDED;
2676 wineItem->state |=TVIS_EXPANDEDONCE;
2680 TRACE("Exiting, Item %d state is now %d...\n",
2684 TREEVIEW_QueueRefresh (hwnd);
2690 static TREEVIEW_ITEM *
2691 TREEVIEW_HitTestPoint (HWND hwnd, POINT pt)
2693 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
2694 TREEVIEW_ITEM *wineItem;
2697 GetClientRect (hwnd, &rect);
2699 if (!infoPtr->firstVisible) return NULL;
2701 wineItem=&infoPtr->items [(INT)infoPtr->firstVisible];
2703 while ((wineItem!=NULL) && (pt.y > wineItem->rect.bottom))
2704 wineItem=TREEVIEW_GetNextListItem (infoPtr,wineItem);
2716 TREEVIEW_HitTest (HWND hwnd, LPARAM lParam)
2718 LPTVHITTESTINFO lpht=(LPTVHITTESTINFO) lParam;
2719 TREEVIEW_ITEM *wineItem;
2723 GetClientRect (hwnd, &rect);
2727 if (x < rect.left) status|=TVHT_TOLEFT;
2728 if (x > rect.right) status|=TVHT_TORIGHT;
2729 if (y < rect.top ) status|=TVHT_ABOVE;
2730 if (y > rect.bottom) status|=TVHT_BELOW;
2737 wineItem=TREEVIEW_HitTestPoint (hwnd, lpht->pt);
2739 lpht->flags=TVHT_NOWHERE;
2743 /* FIXME: implement other flags
2744 * Assign the appropriate flags depending on the click location
2745 * Intitialize flags before to "|=" it...
2749 if (x < wineItem->expandBox.left)
2751 lpht->flags |= TVHT_ONITEMINDENT;
2753 else if ( ( x >= wineItem->expandBox.left) &&
2754 ( x <= wineItem->expandBox.right))
2756 lpht->flags |= TVHT_ONITEMBUTTON;
2758 else if (x < wineItem->rect.right)
2760 lpht->flags |= TVHT_ONITEMLABEL;
2764 lpht->flags|=TVHT_ONITEMRIGHT;
2767 lpht->hItem=wineItem->hItem;
2769 return (LRESULT) wineItem->hItem;
2773 TREEVIEW_EndEditLabelNow (HWND hwnd, WPARAM wParam, LPARAM lParam)
2775 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
2776 TREEVIEW_ITEM *editedItem = TREEVIEW_ValidItem (infoPtr, infoPtr->editItem);
2777 BOOL bRevert = (BOOL)wParam;
2778 BOOL bReturn = ! bRevert;
2780 if ( ! (BOOL)wParam ) /* wParam is set to true to cancel the edition */
2782 if ( TREEVIEW_SendDispInfoNotify( /* return true to cancel edition */
2793 if (bRevert == FALSE) /* Apply the changes */
2796 int iLength = GetWindowTextA(infoPtr->hwndEdit, tmpText, 1023);
2801 ERR("Problem retreiving new item label.");
2803 else if (iLength >= 1023)
2806 "Insuficient space to retrieve new item label, new label ignored.");
2810 if (strcmp( tmpText, editedItem->pszText ) == 0)
2811 /* Do nothing if the label has not changed */
2815 LPSTR tmpLabel = COMCTL32_Alloc( iLength+1 );
2817 if ( tmpLabel == NULL )
2819 "OutOfMemory, cannot allocate space for label");
2822 COMCTL32_Free(editedItem->pszText);
2823 editedItem->pszText = tmpLabel;
2824 lstrcpyA( editedItem->pszText, tmpText);
2830 ShowWindow(infoPtr->hwndEdit, SW_HIDE);
2831 EnableWindow(infoPtr->hwndEdit, FALSE);
2832 infoPtr->editItem = 0;
2841 TREEVIEW_LButtonDoubleClick (HWND hwnd, WPARAM wParam, LPARAM lParam)
2843 TREEVIEW_ITEM *wineItem;
2847 pt.x = (INT)LOWORD(lParam);
2848 pt.y = (INT)HIWORD(lParam);
2851 wineItem=TREEVIEW_HitTestPoint (hwnd, pt);
2852 if (!wineItem) return 0;
2853 TRACE("item %d \n",(INT)wineItem->hItem);
2855 if (TREEVIEW_SendSimpleNotify (hwnd, NM_DBLCLK)!=TRUE) { /* FIXME!*/
2856 TREEVIEW_Expand (hwnd, (WPARAM) TVE_TOGGLE, (LPARAM) wineItem->hItem);
2863 TREEVIEW_LButtonDown (HWND hwnd, WPARAM wParam, LPARAM lParam)
2865 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
2869 ht.pt.x = (INT)LOWORD(lParam);
2870 ht.pt.y = (INT)HIWORD(lParam);
2873 iItem=TREEVIEW_HitTest (hwnd, (LPARAM) &ht);
2874 TRACE("item %d \n",iItem);
2876 if (ht.flags & TVHT_ONITEMBUTTON) {
2877 TREEVIEW_Expand (hwnd, (WPARAM) TVE_TOGGLE, (LPARAM) iItem);
2881 infoPtr->uInternalStatus|=TV_LDRAG;
2888 TREEVIEW_LButtonUp (HWND hwnd, WPARAM wParam, LPARAM lParam)
2890 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
2892 TREEVIEW_ITEM *editItem;
2895 ht.pt.x = (INT)LOWORD(lParam);
2896 ht.pt.y = (INT)HIWORD(lParam);
2900 /* Return true to cancel default behaviour */
2901 if ( TREEVIEW_SendSimpleNotify (hwnd, NM_CLICK) )
2905 iItem = TREEVIEW_HitTest (hwnd, (LPARAM) &ht);
2909 editItem = TREEVIEW_ValidItem(infoPtr, (HTREEITEM)iItem);
2911 infoPtr->uInternalStatus &= ~(TV_LDRAG | TV_LDRAGGING);
2914 * If the style allow editing and the node is already selected
2915 * and the click occured on the item label...
2917 if ( ( GetWindowLongA( hwnd, GWL_STYLE) & TVS_EDITLABELS ) &&
2918 ( editItem->state & TVIS_SELECTED ) &&
2919 ( ht.flags & TVHT_ONITEMLABEL ))
2921 if ( infoPtr->editItem == 0 ) /* If we are not curently editing */
2923 if ( TREEVIEW_SendDispInfoNotify( /* Return true to cancel edition */
2932 TRACE("Edit started for %s.\n", editItem->pszText);
2933 infoPtr->editItem = editItem->hItem;
2938 editItem->text.left - 2,
2939 editItem->text.top - 1,
2940 editItem->text.right - editItem->text.left + 20 ,
2941 editItem->text.bottom - editItem->text.top + 3,
2944 SetWindowTextA( infoPtr->hwndEdit, editItem->pszText );
2945 SendMessageA ( infoPtr->hwndEdit, EM_SETSEL, 0, -1 );
2946 SetFocus ( infoPtr->hwndEdit);
2947 ShowWindow ( infoPtr->hwndEdit, SW_SHOW);
2950 else if ( infoPtr->editItem != 0 ) /* If we are curently editing */
2952 TREEVIEW_EndEditLabelNow(hwnd, (WPARAM)FALSE, 0);
2954 else if ( ht.flags & (TVHT_ONITEMLABEL | TVHT_ONITEMICON))
2956 TREEVIEW_DoSelectItem (
2968 TREEVIEW_RButtonDown (HWND hwnd, WPARAM wParam, LPARAM lParam)
2970 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
2973 infoPtr->uInternalStatus|=TV_RDRAG;
2978 TREEVIEW_RButtonUp (HWND hwnd, WPARAM wParam, LPARAM lParam)
2980 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
2983 if (TREEVIEW_SendSimpleNotify (hwnd, NM_RCLICK)) return 0;
2984 infoPtr->uInternalStatus&= ~(TV_RDRAG | TV_RDRAGGING);
2990 TREEVIEW_MouseMove (HWND hwnd, WPARAM wParam, LPARAM lParam)
2992 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
2993 TREEVIEW_ITEM *hotItem;
2996 pt.x=(INT) LOWORD (lParam);
2997 pt.y=(INT) HIWORD (lParam);
2998 hotItem=TREEVIEW_HitTestPoint (hwnd, pt);
2999 if (!hotItem) return 0;
3000 infoPtr->focusItem=hotItem->hItem;
3002 if ( GetWindowLongA( hwnd, GWL_STYLE) & TVS_DISABLEDRAGDROP) return 0;
3004 if (infoPtr->uInternalStatus & TV_LDRAG) {
3005 TREEVIEW_SendTreeviewDnDNotify (hwnd, TVN_BEGINDRAG, hotItem->hItem, pt);
3006 infoPtr->uInternalStatus &= ~TV_LDRAG;
3007 infoPtr->uInternalStatus |= TV_LDRAGGING;
3008 infoPtr->dropItem=hotItem->hItem;
3012 if (infoPtr->uInternalStatus & TV_RDRAG) {
3013 TREEVIEW_SendTreeviewDnDNotify (hwnd, TVN_BEGINRDRAG, hotItem->hItem, pt);
3014 infoPtr->uInternalStatus &= ~TV_RDRAG;
3015 infoPtr->uInternalStatus |= TV_RDRAGGING;
3016 infoPtr->dropItem=hotItem->hItem;
3025 TREEVIEW_CreateDragImage (HWND hwnd, WPARAM wParam, LPARAM lParam)
3027 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
3028 TREEVIEW_ITEM *dragItem;
3032 HBITMAP hbmp,hOldbmp;
3039 if (!(infoPtr->himlNormal)) return 0;
3040 dragItem=TREEVIEW_ValidItem (infoPtr, (HTREEITEM) lParam);
3042 if (!dragItem) return 0;
3043 itemtxt=dragItem->pszText;
3045 hwtop=GetDesktopWindow ();
3046 htopdc= GetDC (hwtop);
3047 hdc=CreateCompatibleDC (htopdc);
3049 hOldFont=SelectObject (hdc, infoPtr->hFont);
3050 GetTextExtentPoint32A (hdc, itemtxt, lstrlenA (itemtxt), &size);
3051 TRACE("%d %d %s %d\n",size.cx,size.cy,itemtxt,lstrlenA(itemtxt));
3052 hbmp=CreateCompatibleBitmap (htopdc, size.cx, size.cy);
3053 hOldbmp=SelectObject (hdc, hbmp);
3055 ImageList_GetIconSize (infoPtr->himlNormal, &cx, &cy);
3057 if (cy>size.cy) size.cy=cy;
3059 infoPtr->dragList=ImageList_Create (size.cx, size.cy, ILC_COLOR, 10, 10);
3060 ImageList_Draw (infoPtr->himlNormal, dragItem->iImage, hdc, 0, 0, ILD_NORMAL);
3063 ImageList_GetImageInfo (infoPtr->himlNormal, dragItem->hItem, &iminfo);
3064 ImageList_AddMasked (infoPtr->dragList, iminfo.hbmImage, CLR_DEFAULT);
3067 /* draw item text */
3069 SetRect (&rc, cx, 0, size.cx,size.cy);
3070 DrawTextA (hdc, itemtxt, lstrlenA (itemtxt), &rc, DT_LEFT);
3071 SelectObject (hdc, hOldFont);
3072 SelectObject (hdc, hOldbmp);
3074 ImageList_Add (infoPtr->dragList, hbmp, 0);
3077 DeleteObject (hbmp);
3078 ReleaseDC (hwtop, htopdc);
3080 return (LRESULT)infoPtr->dragList;
3085 TREEVIEW_DoSelectItem (HWND hwnd, INT action, HTREEITEM newSelect, INT cause)
3088 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
3089 TREEVIEW_ITEM *prevItem,*wineItem;
3092 wineItem = TREEVIEW_ValidItem (infoPtr, (HTREEITEM)newSelect);
3094 TRACE("Entering item %d, flag %x, cause %x, state %d\n",
3100 if ( (wineItem) && (wineItem->parent))
3103 * If the item has a collapse parent expand the parent so he
3104 * can expose the item
3106 TREEVIEW_ITEM *parentItem = TREEVIEW_ValidItem (infoPtr, wineItem->parent);
3107 if ( !(parentItem->state & TVIS_EXPANDED))
3108 TREEVIEW_Expand (hwnd, TVE_EXPAND, (LPARAM) wineItem->parent);
3114 prevSelect=(INT)infoPtr->selectedItem;
3116 if ((HTREEITEM)prevSelect==newSelect)
3119 prevItem= TREEVIEW_ValidItem (infoPtr, (HTREEITEM)prevSelect);
3122 if (TREEVIEW_SendTreeviewNotify(
3126 (HTREEITEM)prevSelect,
3127 (HTREEITEM)newSelect))
3128 return FALSE; /* FIXME: OK? */
3131 prevItem->state &= ~TVIS_SELECTED;
3133 wineItem->state |= TVIS_SELECTED;
3135 infoPtr->selectedItem=(HTREEITEM)newSelect;
3137 TREEVIEW_SendTreeviewNotify(
3141 (HTREEITEM)prevSelect,
3142 (HTREEITEM)newSelect);
3146 case TVGN_DROPHILITE:
3147 prevItem= TREEVIEW_ValidItem (infoPtr, infoPtr->dropItem);
3150 prevItem->state &= ~TVIS_DROPHILITED;
3152 infoPtr->dropItem=(HTREEITEM)newSelect;
3155 wineItem->state |=TVIS_DROPHILITED;
3159 case TVGN_FIRSTVISIBLE:
3160 FIXME("FIRSTVISIBLE not implemented\n");
3164 TREEVIEW_QueueRefresh (hwnd);
3166 TRACE("Leaving state %d\n", wineItem->state);
3170 /* FIXME: handle NM_KILLFocus enzo */
3172 TREEVIEW_SelectItem (HWND hwnd, WPARAM wParam, LPARAM lParam)
3175 return TREEVIEW_DoSelectItem (hwnd, wParam, (HTREEITEM) lParam, TVC_UNKNOWN);
3182 TREEVIEW_GetFont (HWND hwnd, WPARAM wParam, LPARAM lParam)
3185 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
3187 TRACE("%x\n",infoPtr->hFont);
3188 return infoPtr->hFont;
3192 TREEVIEW_SetFont (HWND hwnd, WPARAM wParam, LPARAM lParam)
3195 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
3198 HFONT hFont, hOldFont;
3202 TRACE("%x %lx\n",wParam, lParam);
3204 infoPtr->hFont = (HFONT)wParam;
3206 hFont = infoPtr->hFont ? infoPtr->hFont : GetStockObject (SYSTEM_FONT);
3208 GetObjectA (infoPtr->hFont, sizeof (LOGFONTA), &logFont);
3209 logFont.lfWeight=FW_BOLD;
3210 infoPtr->hBoldFont = CreateFontIndirectA (&logFont);
3213 hOldFont = SelectObject (hdc, hFont);
3214 GetTextMetricsA (hdc, &tm);
3215 height= tm.tmHeight + tm.tmExternalLeading;
3216 if (height>infoPtr->uRealItemHeight)
3217 infoPtr->uRealItemHeight=height;
3218 SelectObject (hdc, hOldFont);
3222 TREEVIEW_QueueRefresh (hwnd);
3230 TREEVIEW_VScroll (HWND hwnd, WPARAM wParam, LPARAM lParam)
3233 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
3236 TRACE("wp %x, lp %lx\n", wParam, lParam);
3237 if (!infoPtr->uInternalStatus & TV_VSCROLL) return FALSE;
3239 switch (LOWORD (wParam)) {
3241 if (!infoPtr->cy) return FALSE;
3242 infoPtr->cy -= infoPtr->uRealItemHeight;
3243 if (infoPtr->cy < 0) infoPtr->cy=0;
3246 maxHeight=infoPtr->uTotalHeight-infoPtr->uVisibleHeight;
3247 if (infoPtr->cy == maxHeight) return FALSE;
3248 infoPtr->cy += infoPtr->uRealItemHeight;
3249 if (infoPtr->cy > maxHeight)
3250 infoPtr->cy = maxHeight;
3253 if (!infoPtr->cy) return FALSE;
3254 infoPtr->cy -= infoPtr->uVisibleHeight;
3255 if (infoPtr->cy < 0) infoPtr->cy=0;
3258 maxHeight=infoPtr->uTotalHeight-infoPtr->uVisibleHeight;
3259 if (infoPtr->cy == maxHeight) return FALSE;
3260 infoPtr->cy += infoPtr->uVisibleHeight;
3261 if (infoPtr->cy > maxHeight)
3262 infoPtr->cy = maxHeight;
3265 infoPtr->cy = HIWORD (wParam);
3270 TREEVIEW_QueueRefresh (hwnd);
3275 TREEVIEW_HScroll (HWND hwnd, WPARAM wParam, LPARAM lParam)
3277 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
3280 TRACE("wp %lx, lp %x\n", lParam, wParam);
3282 if (!infoPtr->uInternalStatus & TV_HSCROLL) return FALSE;
3284 switch (LOWORD (wParam)) {
3286 if (!infoPtr->cx) return FALSE;
3287 infoPtr->cx -= infoPtr->uRealItemHeight;
3288 if (infoPtr->cx < 0) infoPtr->cx=0;
3291 maxWidth=infoPtr->uTotalWidth-infoPtr->uVisibleWidth;
3292 if (infoPtr->cx == maxWidth) return FALSE;
3293 infoPtr->cx += infoPtr->uRealItemHeight; /*FIXME */
3294 if (infoPtr->cx > maxWidth)
3295 infoPtr->cx = maxWidth;
3298 if (!infoPtr->cx) return FALSE;
3299 infoPtr->cx -= infoPtr->uVisibleWidth;
3300 if (infoPtr->cx < 0) infoPtr->cx=0;
3303 maxWidth=infoPtr->uTotalWidth-infoPtr->uVisibleWidth;
3304 if (infoPtr->cx == maxWidth) return FALSE;
3305 infoPtr->cx += infoPtr->uVisibleWidth;
3306 if (infoPtr->cx > maxWidth)
3307 infoPtr->cx = maxWidth;
3310 infoPtr->cx = HIWORD (wParam);
3315 TREEVIEW_QueueRefresh (hwnd);
3321 TREEVIEW_KeyDown (HWND hwnd, WPARAM wParam, LPARAM lParam)
3323 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
3324 HTREEITEM hNewSelection = 0;
3325 INT scrollNeeds = -1;
3326 INT cyChangeNeeds = -1;
3327 INT prevSelect = (INT)infoPtr->selectedItem;
3329 TREEVIEW_ITEM *prevItem =
3330 (prevSelect != 0 ) ?
3331 TREEVIEW_ValidItem (infoPtr, (HTREEITEM)prevSelect) :
3334 TREEVIEW_ITEM *newItem = NULL;
3336 TRACE("%x %lx\n",wParam, lParam);
3338 if (prevSelect == 0)
3343 newItem=TREEVIEW_GetPrevListItem (infoPtr, prevItem);
3346 newItem=& infoPtr->items[(INT)infoPtr->TopRootItem];
3348 hNewSelection = newItem->hItem;
3350 if (! newItem->visible)
3351 scrollNeeds = SB_LINEUP;
3356 newItem=TREEVIEW_GetNextListItem (infoPtr, prevItem);
3361 hNewSelection = newItem->hItem;
3363 if (! newItem->visible)
3364 scrollNeeds = SB_LINEDOWN;
3369 newItem = &infoPtr->items[(INT)infoPtr->TopRootItem];
3370 hNewSelection = newItem->hItem;
3375 newItem = &infoPtr->items[(INT)infoPtr->TopRootItem];
3376 newItem = TREEVIEW_GetLastListItem (infoPtr, newItem);
3377 hNewSelection = newItem->hItem;
3379 if (! newItem->visible)
3380 cyChangeNeeds = infoPtr->uTotalHeight-infoPtr->uVisibleHeight;
3385 if ( (prevItem->cChildren > 0) && (prevItem->state & TVIS_EXPANDED) )
3387 TREEVIEW_Expand(hwnd, TVE_COLLAPSE, prevSelect );
3389 else if ((INT)prevItem->parent)
3391 newItem = (& infoPtr->items[(INT)prevItem->parent]);
3392 if (! newItem->visible)
3393 /* FIXME find a way to make this item the first visible... */
3396 hNewSelection = newItem->hItem;
3402 if ( ( prevItem->cChildren > 0) ||
3403 ( prevItem->cChildren == I_CHILDRENCALLBACK))
3405 if (! (prevItem->state & TVIS_EXPANDED))
3406 TREEVIEW_Expand(hwnd, TVE_EXPAND, prevSelect );
3409 newItem = (& infoPtr->items[(INT)prevItem->firstChild]);
3410 hNewSelection = newItem->hItem;
3417 if (! (prevItem->state & TVIS_EXPANDED))
3418 TREEVIEW_Expand(hwnd, TVE_EXPAND, prevSelect );
3422 if (prevItem->state & TVIS_EXPANDED)
3423 TREEVIEW_Expand(hwnd, TVE_COLLAPSE, prevSelect );
3428 newItem=TREEVIEW_GetListItem(
3431 -1*(TREEVIEW_GetVisibleCount(hwnd,0,0)-3));
3435 hNewSelection = newItem->hItem;
3437 if (! newItem->visible)
3438 scrollNeeds = SB_PAGEUP;
3443 newItem=TREEVIEW_GetListItem(
3446 TREEVIEW_GetVisibleCount(hwnd,0,0)-3);
3451 hNewSelection = newItem->hItem;
3453 if (! newItem->visible)
3454 scrollNeeds = SB_PAGEDOWN;
3463 FIXME("%x not implemented\n", wParam);
3470 This works but does not send notification...
3472 prevItem->state &= ~TVIS_SELECTED;
3473 newItem->state |= TVIS_SELECTED;
3474 infoPtr->selectedItem = hNewSelection;
3475 TREEVIEW_QueueRefresh (hwnd);
3478 if ( TREEVIEW_DoSelectItem(
3481 (HTREEITEM)hNewSelection,
3484 /* If selection change is allowed for the new item, perform scrolling */
3485 if (scrollNeeds != -1)
3486 TREEVIEW_VScroll(hwnd, scrollNeeds, 0);
3488 if (cyChangeNeeds != -1)
3489 infoPtr->cy = cyChangeNeeds;
3491 /* FIXME: Something happen in the load the in the two weeks before
3492 april 1st 1999 which makes this SetFocus mandatory otherwise, the focus
3493 is lost... However the SetFocus should not be required...*/
3504 TREEVIEW_GetScrollTime (HWND hwnd)
3506 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
3508 return infoPtr->uScrollTime;
3513 TREEVIEW_SetScrollTime (HWND hwnd, UINT uScrollTime)
3515 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
3516 UINT uOldScrollTime = infoPtr->uScrollTime;
3518 infoPtr->uScrollTime = min (uScrollTime, 100);
3520 return uOldScrollTime;
3524 static LRESULT WINAPI
3525 TREEVIEW_WindowProc (HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
3528 case TVM_INSERTITEMA:
3529 return TREEVIEW_InsertItemA (hwnd, wParam, lParam);
3531 case TVM_INSERTITEMW:
3532 return TREEVIEW_InsertItemW(hwnd,wParam,lParam);;
3534 case TVM_DELETEITEM:
3535 return TREEVIEW_DeleteItem (hwnd, wParam, lParam);
3538 return TREEVIEW_Expand (hwnd, wParam, lParam);
3540 case TVM_GETITEMRECT:
3541 return TREEVIEW_GetItemRect (hwnd, wParam, lParam);
3544 return TREEVIEW_GetCount (hwnd, wParam, lParam);
3547 return TREEVIEW_GetIndent (hwnd);
3550 return TREEVIEW_SetIndent (hwnd, wParam);
3552 case TVM_GETIMAGELIST:
3553 return TREEVIEW_GetImageList (hwnd, wParam, lParam);
3555 case TVM_SETIMAGELIST:
3556 return TREEVIEW_SetImageList (hwnd, wParam, lParam);
3558 case TVM_GETNEXTITEM:
3559 return TREEVIEW_GetNextItem (hwnd, wParam, lParam);
3561 case TVM_SELECTITEM:
3562 return TREEVIEW_SelectItem (hwnd, wParam, lParam);
3565 return TREEVIEW_GetItemA (hwnd, wParam, lParam);
3568 FIXME("Unimplemented msg TVM_GETITEMW\n");
3572 return TREEVIEW_SetItemA (hwnd, wParam, lParam);
3575 FIXME("Unimplemented msg TVM_SETITEMW\n");
3578 case TVM_EDITLABELA:
3579 FIXME("Unimplemented msg TVM_EDITLABELA \n");
3582 case TVM_EDITLABELW:
3583 FIXME("Unimplemented msg TVM_EDITLABELW \n");
3586 case TVM_GETEDITCONTROL:
3587 return TREEVIEW_GetEditControl (hwnd);
3589 case TVM_GETVISIBLECOUNT:
3590 return TREEVIEW_GetVisibleCount (hwnd, wParam, lParam);
3593 return TREEVIEW_HitTest (hwnd, lParam);
3595 case TVM_CREATEDRAGIMAGE:
3596 return TREEVIEW_CreateDragImage (hwnd, wParam, lParam);
3598 case TVM_SORTCHILDREN:
3599 return TREEVIEW_SortChildren (hwnd, wParam, lParam);
3601 case TVM_ENSUREVISIBLE:
3602 FIXME("Unimplemented msg TVM_ENSUREVISIBLE\n");
3605 case TVM_SORTCHILDRENCB:
3606 return TREEVIEW_SortChildrenCB(hwnd, wParam, lParam);
3608 case TVM_ENDEDITLABELNOW:
3609 return TREEVIEW_EndEditLabelNow (hwnd, wParam, lParam);
3611 case TVM_GETISEARCHSTRINGA:
3612 FIXME("Unimplemented msg TVM_GETISEARCHSTRINGA\n");
3615 case TVM_GETISEARCHSTRINGW:
3616 FIXME("Unimplemented msg TVM_GETISEARCHSTRINGW\n");
3619 case TVM_GETTOOLTIPS:
3620 return TREEVIEW_GetToolTips (hwnd);
3622 case TVM_SETTOOLTIPS:
3623 return TREEVIEW_SetToolTips (hwnd, wParam);
3625 case TVM_SETINSERTMARK:
3626 FIXME("Unimplemented msg TVM_SETINSERTMARK\n");
3629 case TVM_SETITEMHEIGHT:
3630 return TREEVIEW_SetItemHeight (hwnd, wParam);
3632 case TVM_GETITEMHEIGHT:
3633 return TREEVIEW_GetItemHeight (hwnd);
3635 case TVM_SETBKCOLOR:
3636 return TREEVIEW_SetBkColor (hwnd, wParam, lParam);
3638 case TVM_SETTEXTCOLOR:
3639 return TREEVIEW_SetTextColor (hwnd, wParam, lParam);
3641 case TVM_GETBKCOLOR:
3642 return TREEVIEW_GetBkColor (hwnd);
3644 case TVM_GETTEXTCOLOR:
3645 return TREEVIEW_GetTextColor (hwnd);
3647 case TVM_SETSCROLLTIME:
3648 return TREEVIEW_SetScrollTime (hwnd, (UINT)wParam);
3650 case TVM_GETSCROLLTIME:
3651 return TREEVIEW_GetScrollTime (hwnd);
3653 case TVM_GETITEMSTATE:
3654 return TREEVIEW_GetItemState (hwnd,wParam, lParam);
3656 case TVM_GETLINECOLOR:
3657 return TREEVIEW_GetLineColor (hwnd,wParam, lParam);
3659 case TVM_SETLINECOLOR:
3660 return TREEVIEW_SetLineColor (hwnd,wParam, lParam);
3662 case TVM_SETINSERTMARKCOLOR:
3663 FIXME("Unimplemented msg TVM_SETINSERTMARKCOLOR\n");
3666 case TVM_SETUNICODEFORMAT:
3667 FIXME("Unimplemented msg TVM_SETUNICODEFORMAT\n");
3670 case TVM_GETUNICODEFORMAT:
3671 FIXME("Unimplemented msg TVM_GETUNICODEFORMAT\n");
3675 return TREEVIEW_Command (hwnd, wParam, lParam);
3678 return TREEVIEW_Create (hwnd, wParam, lParam);
3681 return TREEVIEW_Destroy (hwnd);
3683 /* case WM_ENABLE: */
3686 return TREEVIEW_EraseBackground (hwnd, wParam, lParam);
3689 return DLGC_WANTARROWS | DLGC_WANTCHARS;
3692 return TREEVIEW_Paint (hwnd, wParam, lParam);
3695 return TREEVIEW_GetFont (hwnd, wParam, lParam);
3698 return TREEVIEW_SetFont (hwnd, wParam, lParam);
3701 return TREEVIEW_KeyDown (hwnd, wParam, lParam);
3704 return TREEVIEW_SetFocus (hwnd, wParam, lParam);
3707 return TREEVIEW_KillFocus (hwnd, wParam, lParam);
3709 case WM_LBUTTONDOWN:
3710 return TREEVIEW_LButtonDown (hwnd, wParam, lParam);
3713 return TREEVIEW_LButtonUp (hwnd, wParam, lParam);
3715 case WM_LBUTTONDBLCLK:
3716 return TREEVIEW_LButtonDoubleClick (hwnd, wParam, lParam);
3718 case WM_RBUTTONDOWN:
3719 return TREEVIEW_RButtonDown (hwnd, wParam, lParam);
3722 return TREEVIEW_RButtonUp (hwnd, wParam, lParam);
3725 return TREEVIEW_MouseMove (hwnd, wParam, lParam);
3727 case WM_STYLECHANGED:
3728 return TREEVIEW_StyleChanged (hwnd, wParam, lParam);
3730 /* case WM_SYSCOLORCHANGE: */
3731 /* case WM_SETREDRAW: */
3734 return TREEVIEW_HandleTimer (hwnd, wParam, lParam);
3737 return TREEVIEW_Size (hwnd, wParam,lParam);
3740 return TREEVIEW_HScroll (hwnd, wParam, lParam);
3742 return TREEVIEW_VScroll (hwnd, wParam, lParam);
3745 TRACE ("drawItem\n");
3746 return DefWindowProcA (hwnd, uMsg, wParam, lParam);
3749 if (uMsg >= WM_USER)
3750 FIXME("Unknown msg %04x wp=%08x lp=%08lx\n",
3751 uMsg, wParam, lParam);
3752 return DefWindowProcA (hwnd, uMsg, wParam, lParam);
3759 TREEVIEW_Register (void)
3765 if (GlobalFindAtomA (WC_TREEVIEWA)) return;
3767 ZeroMemory (&wndClass, sizeof(WNDCLASSA));
3768 wndClass.style = CS_GLOBALCLASS | CS_DBLCLKS;
3769 wndClass.lpfnWndProc = (WNDPROC)TREEVIEW_WindowProc;
3770 wndClass.cbClsExtra = 0;
3771 wndClass.cbWndExtra = sizeof(TREEVIEW_INFO *);
3772 wndClass.hCursor = LoadCursorA (0, IDC_ARROWA);
3773 wndClass.hbrBackground = 0;
3774 wndClass.lpszClassName = WC_TREEVIEWA;
3776 RegisterClassA (&wndClass);
3781 TREEVIEW_Unregister (void)
3783 if (GlobalFindAtomA (WC_TREEVIEWA))
3784 UnregisterClassA (WC_TREEVIEWA, (HINSTANCE)NULL);