Fixed DC leak.
[wine/multimedia.git] / dlls / comctl32 / treeview.c
blob5d32246d14814f6a45673508bdc0d4bd10787cee
1 /* Treeview control
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
8 * TODO:
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.
12 * refreshtreeview:
13 -small array containing info about positions.
14 -better implementation of RefreshItem:
15 1) draw lines between parents
16 2) draw items
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.
21 * -Unicode messages
22 * -check custom draw
23 * -I_CHILDRENCALLBACK
24 * FIXME: check fontsize. (uRealItemHeight)
25 * test focusItem (redraw in different color)
26 uHotItem
27 Edit: needs timer
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.
42 #include <string.h>
43 #include "winbase.h"
44 #include "wingdi.h"
45 #include "commctrl.h"
46 #include "treeview.h"
47 #include "comctl32.h"
48 #include "debugtools.h"
50 DEFAULT_DEBUG_CHANNEL(treeview)
52 /* ffs should be in <string.h>. */
54 /* Defines, since they do not need to return previous state, and nr
55 * has no side effects in this file.
57 #define tv_test_bit(nr,bf) (((LPBYTE)bf)[nr>>3]&(1<<(nr&7)))
58 #define tv_set_bit(nr,bf) ((LPBYTE)bf)[nr>>3]|=(1<<(nr&7))
59 #define tv_clear_bit(nr,bf) ((LPBYTE)bf)[nr>>3]&=~(1<<(nr&7))
62 #define TREEVIEW_GetInfoPtr(hwnd) \
63 ((TREEVIEW_INFO *) GetWindowLongA( hwnd, 0))
65 static BOOL
66 TREEVIEW_SendSimpleNotify (HWND hwnd, UINT code);
67 static BOOL
68 TREEVIEW_SendTreeviewNotify (HWND hwnd, UINT code, UINT action,
69 HTREEITEM oldItem, HTREEITEM newItem);
70 static BOOL
71 TREEVIEW_SendTreeviewDnDNotify (HWND hwnd, UINT code, HTREEITEM dragItem,
72 POINT pt);
73 static BOOL
74 TREEVIEW_SendDispInfoNotify (HWND hwnd, TREEVIEW_ITEM *wineItem,
75 UINT code, UINT what);
76 static BOOL
77 TREEVIEW_SendCustomDrawNotify (HWND hwnd, DWORD dwDrawStage, HDC hdc,
78 RECT rc);
79 static BOOL
80 TREEVIEW_SendCustomDrawItemNotify (HWND hwnd, HDC hdc,
81 TREEVIEW_ITEM *tvItem, UINT uItemDrawState);
82 static LRESULT
83 TREEVIEW_DoSelectItem (HWND hwnd, INT action, HTREEITEM newSelect, INT cause);
84 static void
85 TREEVIEW_Refresh (HWND hwnd, HDC hdc);
87 static LRESULT CALLBACK
88 TREEVIEW_Edit_SubclassProc (HWND hwnd, UINT uMsg, WPARAM wParam,
89 LPARAM lParam);
91 LRESULT WINAPI
92 TREEVIEW_EndEditLabelNow (HWND hwnd, WPARAM wParam, LPARAM lParam);
97 /* helper functions. Work with the assumption that validity of operands
98 is checked beforehand, and that tree state is valid. */
100 /* FIXME: MS documentation says `GetNextVisibleItem' returns NULL
101 if not successfull. Probably only applies to dereferencing infoPtr
102 (i.e. we are offered a valid treeview structure)
103 and not whether there is a next `visible' child.
104 FIXME: check other failures.
107 /***************************************************************************
108 * This method returns the TREEVIEW_ITEM object given the handle
110 static TREEVIEW_ITEM* TREEVIEW_ValidItem(
111 TREEVIEW_INFO *infoPtr,
112 HTREEITEM handle)
114 if ((!handle) || (handle>infoPtr->uMaxHandle))
115 return NULL;
117 if (tv_test_bit ((INT)handle, infoPtr->freeList))
118 return NULL;
120 return &infoPtr->items[(INT)handle];
123 /***************************************************************************
124 * This method returns the last expanded child item of a tree node
126 static TREEVIEW_ITEM *TREEVIEW_GetLastListItem(
127 TREEVIEW_INFO *infoPtr,
128 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(
143 infoPtr,
144 &(infoPtr->items[(INT)wineItem->firstChild]));
146 return wineItem;
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(
167 infoPtr,
168 &infoPtr->items[(INT)upItem->firstChild]);
169 else
170 return upItem;
172 else
175 * this item does not have a upsibling, get the parent
177 if (tvItem->parent)
178 return &infoPtr->items[(INT)tvItem->parent];
181 return NULL;
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
205 if (tvItem->sibling)
206 return (& infoPtr->items[(INT)tvItem->sibling]);
209 * Otherwise, get the parent's sibling.
211 wineItem=tvItem;
212 while (wineItem->parent) {
213 wineItem=& infoPtr->items [(INT)wineItem->parent];
214 if (wineItem->sibling)
215 return (& infoPtr->items [(INT)wineItem->sibling]);
218 return NULL;
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,
231 LONG count)
233 TREEVIEW_ITEM *previousItem = NULL;
234 TREEVIEW_ITEM *wineItem = tvItem;
235 LONG iter = 0;
237 if (count > 0)
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;
250 else if (count < 0)
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;
263 else
264 wineItem = NULL;
266 return wineItem;
270 /***************************************************************************
271 * This method
273 static void TREEVIEW_RemoveAllChildren(
274 HWND hwnd,
275 TREEVIEW_ITEM *parentItem)
277 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
278 TREEVIEW_ITEM *killItem;
279 INT kill;
281 kill=(INT)parentItem->firstChild;
282 while (kill) {
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_DELETEITEMA, 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;
302 static void
303 TREEVIEW_RemoveItem (HWND hwnd, TREEVIEW_ITEM *wineItem)
306 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
307 TREEVIEW_ITEM *parentItem, *upsiblingItem, *siblingItem;
308 INT iItem;
310 iItem=(INT)wineItem->hItem;
311 tv_set_bit(iItem,infoPtr->freeList);
312 infoPtr->uNumItems--;
313 parentItem=NULL;
314 if (wineItem->pszText!=LPSTR_TEXTCALLBACKA)
315 COMCTL32_Free (wineItem->pszText);
317 TREEVIEW_SendTreeviewNotify (hwnd, TVN_DELETEITEMA, 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");
327 break;
328 case 1:
329 parentItem->cChildren=0;
330 parentItem->firstChild=0;
331 return;
332 default:
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;
362 int i;
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(hwnd, TVN_DELETEITEMA, 0,
370 killItem->hItem, 0);
372 if (infoPtr->uNumPtrsAlloced) {
373 COMCTL32_Free (infoPtr->items);
374 COMCTL32_Free (infoPtr->freeList);
375 infoPtr->uNumItems = 0;
376 infoPtr->uNumPtrsAlloced = 0;
377 infoPtr->uMaxHandle = 0;
378 infoPtr->TopRootItem = 0;
388 static LRESULT
389 TREEVIEW_GetImageList (HWND hwnd, WPARAM wParam, LPARAM lParam)
391 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
393 TRACE("\n");
395 if ((INT)wParam == TVSIL_NORMAL)
396 return (LRESULT) infoPtr->himlNormal;
397 if ((INT)wParam == TVSIL_STATE)
398 return (LRESULT) infoPtr->himlState;
400 return 0;
403 static LRESULT
404 TREEVIEW_SetImageList (HWND hwnd, WPARAM wParam, LPARAM lParam)
406 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
407 HIMAGELIST himlTemp;
409 TRACE("%x,%lx\n", wParam, lParam);
410 switch ((INT)wParam) {
411 case TVSIL_NORMAL:
412 himlTemp = infoPtr->himlNormal;
413 infoPtr->himlNormal = (HIMAGELIST)lParam;
414 return (LRESULT)himlTemp;
416 case TVSIL_STATE:
417 himlTemp = infoPtr->himlState;
418 infoPtr->himlState = (HIMAGELIST)lParam;
419 return (LRESULT)himlTemp;
422 return (LRESULT)NULL;
427 static LRESULT
428 TREEVIEW_SetItemHeight (HWND hwnd, WPARAM wParam)
430 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
431 INT cx,cy,prevHeight=infoPtr->uItemHeight;
433 TRACE("\n");
434 if (wParam==-1) {
435 infoPtr->uItemHeight=-1;
436 return prevHeight;
439 ImageList_GetIconSize (infoPtr->himlNormal, &cx, &cy);
441 if (wParam>cy) cy=wParam;
442 infoPtr->uItemHeight=cy;
444 if (!( GetWindowLongA( hwnd, GWL_STYLE) & TVS_NONEVENHEIGHT))
445 infoPtr->uItemHeight = (INT) wParam & 0xfffffffe;
446 return prevHeight;
449 static LRESULT
450 TREEVIEW_GetItemHeight (HWND hwnd)
452 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
454 TRACE("\n");
455 return infoPtr->uItemHeight;
458 static LRESULT
459 TREEVIEW_GetLineColor (HWND hwnd, WPARAM wParam, LPARAM lParam)
461 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
463 TRACE("\n");
464 return (LRESULT) infoPtr->clrLine;
467 static LRESULT
468 TREEVIEW_SetLineColor (HWND hwnd, WPARAM wParam, LPARAM lParam)
470 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
471 COLORREF prevColor=infoPtr->clrLine;
473 TRACE("\n");
474 infoPtr->clrLine=(COLORREF) lParam;
475 return (LRESULT) prevColor;
478 static LRESULT
479 TREEVIEW_GetInsertMarkColor (HWND hwnd, WPARAM wParam, LPARAM lParam)
481 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
483 TRACE("\n");
484 return (LRESULT) infoPtr->clrInsertMark;
487 static LRESULT
488 TREEVIEW_SetInsertMarkColor (HWND hwnd, WPARAM wParam, LPARAM lParam)
490 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
491 COLORREF prevColor=infoPtr->clrInsertMark;
493 TRACE("%d %ld\n",wParam,lParam);
494 infoPtr->clrInsertMark=(COLORREF) lParam;
495 return (LRESULT) prevColor;
498 static LRESULT
499 TREEVIEW_SetInsertMark (HWND hwnd, WPARAM wParam, LPARAM lParam)
501 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
502 HDC hdc;
504 FIXME("%d %ld\n",wParam,lParam);
505 if (!TREEVIEW_ValidItem (infoPtr, (HTREEITEM)lParam)) return 0;
506 FIXME("%d %ld\n",wParam,lParam);
508 infoPtr->insertBeforeorAfter=(BOOL) wParam;
509 infoPtr->insertMarkItem=(HTREEITEM) lParam;
511 hdc = GetDC (hwnd);
512 TREEVIEW_Refresh (hwnd, hdc);
513 ReleaseDC(hwnd,hdc);
515 return 1;
518 static LRESULT
519 TREEVIEW_SetTextColor (HWND hwnd, WPARAM wParam, LPARAM lParam)
521 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
522 COLORREF prevColor=infoPtr->clrText;
524 TRACE("\n");
525 infoPtr->clrText=(COLORREF) lParam;
526 return (LRESULT) prevColor;
529 static LRESULT
530 TREEVIEW_GetBkColor (HWND hwnd)
532 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
534 TRACE("\n");
535 return (LRESULT) infoPtr->clrBk;
538 static LRESULT
539 TREEVIEW_SetBkColor (HWND hwnd, WPARAM wParam, LPARAM lParam)
541 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
542 COLORREF prevColor=infoPtr->clrBk;
544 TRACE("\n");
545 infoPtr->clrBk=(COLORREF) lParam;
546 return (LRESULT) prevColor;
549 static LRESULT
550 TREEVIEW_GetTextColor (HWND hwnd)
552 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
554 TRACE("\n");
555 return (LRESULT) infoPtr->clrText;
559 /* cdmode: custom draw mode as received from app. in first NMCUSTOMDRAW
560 notification */
562 #define TREEVIEW_LEFT_MARGIN 8
565 static void
566 TREEVIEW_DrawItem (HWND hwnd, HDC hdc, TREEVIEW_ITEM *wineItem)
568 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
569 DWORD dwStyle = GetWindowLongA (hwnd, GWL_STYLE);
570 INT center,xpos,cx,cy, cditem;
571 HFONT hOldFont;
572 UINT uTextJustify = DT_LEFT;
573 RECT r;
576 if (wineItem->state & TVIS_BOLD)
577 hOldFont = SelectObject (hdc, infoPtr->hBoldFont);
578 else
579 hOldFont = SelectObject (hdc, infoPtr->hFont);
581 cditem=0;
582 TRACE ("cdmode:%x\n",infoPtr->cdmode);
583 if (infoPtr->cdmode & CDRF_NOTIFYITEMDRAW) {
584 cditem=TREEVIEW_SendCustomDrawItemNotify
585 (hwnd, hdc, wineItem, CDDS_ITEMPREPAINT);
586 TRACE("prepaint:cditem-app returns 0x%x\n",cditem);
588 if (cditem & CDRF_SKIPDEFAULT)
589 return;
593 * Set drawing starting points
595 r = wineItem->rect; /* this item rectangle */
596 center = (r.top+r.bottom)/2; /* this item vertical center */
597 xpos = r.left + TREEVIEW_LEFT_MARGIN;/* horizontal starting point */
600 * Display the tree hierarchy
602 if ( dwStyle & TVS_HASLINES)
605 * Write links to parent node
606 * we draw the L starting from the child to the parent
608 * points[0] is attached to the current item
609 * points[1] is the L corner
610 * points[2] is attached to the parent or the up sibling
612 if ( dwStyle & TVS_LINESATROOT)
614 TREEVIEW_ITEM *upNode = NULL;
615 BOOL hasParentOrSibling = TRUE;
616 RECT upRect = {0,0,0,0};
617 HPEN hOldPen, hNewPen;
618 POINT points[3];
620 * determine the target location of the line at root, either be linked
621 * to the up sibling or to the parent node.
623 if (wineItem->upsibling)
624 upNode = TREEVIEW_ValidItem (infoPtr, wineItem->upsibling);
625 else if (wineItem->parent)
626 upNode = TREEVIEW_ValidItem (infoPtr, wineItem->parent);
627 else
628 hasParentOrSibling = FALSE;
630 if (upNode)
631 upRect = upNode->rect;
633 if ( wineItem->iLevel == 0 )
635 points[2].x = points[1].x = upRect.left+8;
636 points[0].x = points[2].x + 10;
637 points[2].y = upRect.bottom-3;
638 points[1].y = points[0].y = center;
640 else
642 points[2].x = points[1].x = 8 + (20*wineItem->iLevel);
643 points[2].y = ( upNode->cChildren == 0) ?
644 upRect.top : /* is linked to the "L" above */
645 ( wineItem->upsibling != NULL) ?
646 upRect.bottom-3: /* is linked to an icon */
647 upRect.bottom+1; /* is linked to a +/- box */
648 points[1].y = points[0].y = center;
649 points[0].x = points[1].x + 10;
653 * Get a dotted pen
655 hNewPen = CreatePen(PS_DOT, 0, infoPtr->clrLine);
656 hOldPen = SelectObject( hdc, hNewPen );
658 if (hasParentOrSibling)
659 Polyline (hdc,points,3);
660 else
661 Polyline (hdc,points,2);
663 DeleteObject(hNewPen);
664 SelectObject(hdc, hOldPen);
669 * Display the (+/-) signs
671 if (wineItem->iLevel != 0)/* update position only for non root node */
672 xpos+=(5*wineItem->iLevel);
674 if (( dwStyle & TVS_HASBUTTONS) && ( dwStyle & TVS_HASLINES))
676 if ( (wineItem->cChildren) ||
677 (wineItem->cChildren == I_CHILDRENCALLBACK))
679 /* Setup expand box coordinate to facilitate the LMBClick handling */
680 wineItem->expandBox.left = xpos-4;
681 wineItem->expandBox.top = center-4;
682 wineItem->expandBox.right = xpos+5;
683 wineItem->expandBox.bottom = center+5;
685 Rectangle (
686 hdc,
687 wineItem->expandBox.left,
688 wineItem->expandBox.top ,
689 wineItem->expandBox.right,
690 wineItem->expandBox.bottom);
692 MoveToEx (hdc, xpos-2, center, NULL);
693 LineTo (hdc, xpos+3, center);
695 if (!(wineItem->state & TVIS_EXPANDED)) {
696 MoveToEx (hdc, xpos, center-2, NULL);
697 LineTo (hdc, xpos, center+3);
703 * Display the image associated with this item
705 xpos += 13; /* update position */
706 if (wineItem->mask & (TVIF_IMAGE|TVIF_SELECTEDIMAGE)) {
707 INT imageIndex;
708 HIMAGELIST *himlp = NULL;
710 /* State images are displayed to the left of the Normal image
711 * image number is in state; zero should be `display no image'.
712 * FIXME: that last sentence looks like it needs some checking.
714 if (infoPtr->himlState)
715 himlp=&infoPtr->himlState;
716 imageIndex=wineItem->state>>12;
717 imageIndex++; /* yeah, right */
718 TRACE ("imindex:%d\n",imageIndex);
719 if ((himlp) && (imageIndex))
721 imageIndex--; /* see FIXME */
722 ImageList_Draw ( *himlp, imageIndex, hdc, xpos-2, r.top+1, ILD_NORMAL);
723 ImageList_GetIconSize (*himlp, &cx, &cy);
724 wineItem->statebitmap.left=xpos-2;
725 wineItem->statebitmap.right=xpos-2+cx;
726 wineItem->statebitmap.top=r.top+1;
727 wineItem->statebitmap.bottom=r.top+1+cy;
728 xpos+=cx;
731 /* Now, draw the normal image; can be either selected or
732 * non-selected image.
735 himlp=NULL;
736 if (infoPtr->himlNormal)
737 himlp=&infoPtr->himlNormal; /* get the image list */
739 imageIndex = wineItem->iImage;
740 if ( (wineItem->state & TVIS_SELECTED) &&
741 (wineItem->iSelectedImage)) {
743 /* The item is curently selected */
744 if (wineItem->iSelectedImage == I_IMAGECALLBACK)
745 TREEVIEW_SendDispInfoNotify
746 (hwnd, wineItem, TVN_GETDISPINFOA, TVIF_SELECTEDIMAGE);
748 imageIndex = wineItem->iSelectedImage;
749 } else {
750 /* The item is not selected */
751 if (wineItem->iImage == I_IMAGECALLBACK)
752 TREEVIEW_SendDispInfoNotify
753 (hwnd, wineItem, TVN_GETDISPINFOA, TVIF_IMAGE);
755 imageIndex = wineItem->iImage;
758 if (himlp)
760 int ovlIdx = 0;
762 if(wineItem->stateMask & TVIS_OVERLAYMASK)
763 ovlIdx = wineItem->state & TVIS_OVERLAYMASK;
765 ImageList_Draw ( *himlp, imageIndex, hdc, xpos-2, r.top+1, ILD_NORMAL|ovlIdx);
766 ImageList_GetIconSize (*himlp, &cx, &cy);
767 wineItem->bitmap.left=xpos-2;
768 wineItem->bitmap.right=xpos-2+cx;
769 wineItem->bitmap.top=r.top+1;
770 wineItem->bitmap.bottom=r.top+1+cy;
771 xpos+=cx;
777 * Display the text associated with this item
779 r.left=xpos;
780 if ((wineItem->mask & TVIF_TEXT) && (wineItem->pszText))
782 COLORREF oldBkColor = 0;
783 COLORREF oldTextColor = 0;
784 INT oldBkMode;
786 r.left += 3;
787 r.right -= 3;
789 wineItem->text.left = r.left;
790 wineItem->text.right = r.right;
791 wineItem->text.top = r.top;
792 wineItem->text.bottom= r.bottom;
794 if (wineItem->pszText== LPSTR_TEXTCALLBACKA) {
795 TRACE("LPSTR_TEXTCALLBACK\n");
796 TREEVIEW_SendDispInfoNotify (hwnd, wineItem, TVN_GETDISPINFOA, TVIF_TEXT);
799 /* Yep, there are some things that need to be straightened out here.
800 Removing the comments around the setTextColor does not give the right
801 results. Dito FillRect.
805 /* GetTextExtentPoint32A (hdc, wineItem->pszText,
806 strlen (wineItem->pszText), &size); */
808 /* FillRect ( hdc, &wineItem->text, GetSysColorBrush (infoPtr->clrBk));
812 if (!(cditem & CDRF_NOTIFYPOSTPAINT) &&
813 (wineItem->state & (TVIS_SELECTED | TVIS_DROPHILITED)) ) {
814 oldBkMode = SetBkMode (hdc, OPAQUE);
815 oldBkColor = SetBkColor (hdc, GetSysColor( COLOR_HIGHLIGHT));
816 oldTextColor = SetTextColor(hdc, GetSysColor( COLOR_HIGHLIGHTTEXT));
817 } else {
818 oldBkMode = SetBkMode (hdc, TRANSPARENT);
819 oldBkColor = SetBkColor (hdc, infoPtr->clrBk);
820 /* oldTextColor = SetTextColor(hdc, infoPtr->clrText); */
825 /* Draw it */
826 DrawTextA ( hdc,
827 wineItem->pszText,
828 lstrlenA(wineItem->pszText),
829 &wineItem->text,
830 uTextJustify | DT_VCENTER | DT_SINGLELINE );
832 /* Obtain the text coordinate */
833 DrawTextA (
834 hdc,
835 wineItem->pszText,
836 lstrlenA(wineItem->pszText),
837 &wineItem->text,
838 uTextJustify | DT_VCENTER | DT_SINGLELINE | DT_CALCRECT);
840 /* Restore the hdc state */
841 SetTextColor( hdc, oldTextColor);
843 if (oldBkMode != TRANSPARENT)
844 SetBkMode(hdc, oldBkMode);
845 if (wineItem->state & (TVIS_SELECTED | TVIS_DROPHILITED))
846 SetBkColor (hdc, oldBkColor);
848 /* Draw the box arround the selected item */
849 if (wineItem->state & TVIS_SELECTED )
851 HPEN hNewPen = CreatePen(PS_DOT, 0, GetSysColor(COLOR_WINDOWTEXT) );
852 HPEN hOldPen = SelectObject( hdc, hNewPen );
853 POINT points[4];
855 points[0].x = wineItem->text.left-1;
856 points[0].y = wineItem->text.top+1;
857 points[1].x = wineItem->text.right;
858 points[1].y = wineItem->text.top+1;
859 points[2].x = wineItem->text.right;
860 points[2].y = wineItem->text.bottom;
861 points[3].x = wineItem->text.left-1;
862 points[3].y = wineItem->text.bottom;
864 Polyline (hdc,points,4);
866 DeleteObject(hNewPen);
867 SelectObject(hdc, hOldPen);
871 /* Draw insertion mark if necessary */
873 if (infoPtr->insertMarkItem)
874 TRACE ("item:%d,mark:%d\n", (int)wineItem->hItem,
875 (int) infoPtr->insertMarkItem);
876 if (wineItem->hItem==infoPtr->insertMarkItem) {
877 HPEN hNewPen, hOldPen;
878 int offset;
880 hNewPen = CreatePen(PS_SOLID, 2, infoPtr->clrInsertMark);
881 hOldPen = SelectObject( hdc, hNewPen );
883 if (infoPtr->insertBeforeorAfter)
884 offset=wineItem->text.top+1;
885 else
886 offset=wineItem->text.bottom-1;
888 MoveToEx (hdc, wineItem->text.left, offset-3, NULL);
889 LineTo (hdc, wineItem->text.left, offset+3);
891 MoveToEx (hdc, wineItem->text.left, offset, NULL);
892 LineTo (hdc, r.right-2, offset);
894 MoveToEx (hdc, r.right-2, offset+3, NULL);
895 LineTo (hdc, r.right-2, offset-3);
897 DeleteObject(hNewPen);
899 SelectObject(hdc, hOldPen);
902 if (cditem & CDRF_NOTIFYPOSTPAINT) {
903 cditem=TREEVIEW_SendCustomDrawItemNotify
904 (hwnd, hdc, wineItem, CDDS_ITEMPOSTPAINT);
905 TRACE("postpaint:cditem-app returns 0x%x\n",cditem);
908 SelectObject (hdc, hOldFont);
911 static LRESULT
912 TREEVIEW_GetItemRect (HWND hwnd, WPARAM wParam, LPARAM lParam)
914 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
915 TREEVIEW_ITEM *wineItem;
916 HTREEITEM *iItem;
917 LPRECT lpRect = (LPRECT)lParam;
918 HDC hdc;
920 TRACE("\n");
922 * validate parameters
924 if (lpRect == NULL)
925 return FALSE;
927 if (infoPtr->Timer & TV_REFRESH_TIMER_SET) {
928 hdc = GetDC (hwnd);
929 TREEVIEW_Refresh (hwnd, hdc); /* we want a rect for the current view */
930 ReleaseDC(hwnd,hdc);
935 * retrieve the item ptr
937 iItem = (HTREEITEM *) lParam;
938 wineItem = TREEVIEW_ValidItem (infoPtr, *iItem);
939 if ((!wineItem) || (!wineItem->visible))
940 return FALSE;
943 * If wParam is TRUE return the text size otherwise return
944 * the whole item size
946 if ((INT) wParam) {
947 lpRect->left = wineItem->text.left;
948 lpRect->right = wineItem->text.right;
949 lpRect->bottom = wineItem->text.bottom;
950 lpRect->top = wineItem->text.top;
951 } else {
952 lpRect->left = wineItem->rect.left;
953 lpRect->right = wineItem->rect.right;
954 lpRect->bottom = wineItem->rect.bottom;
955 lpRect->top = wineItem->rect.top;
958 TRACE("[L:%d R:%d T:%d B:%d]\n",
959 lpRect->left,lpRect->right,
960 lpRect->top,lpRect->bottom);
962 return TRUE;
965 static LRESULT
966 TREEVIEW_GetVisibleCount (HWND hwnd, WPARAM wParam, LPARAM lParam)
969 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
971 return (LRESULT) infoPtr->uVisibleHeight / infoPtr->uRealItemHeight;
976 static LRESULT
977 TREEVIEW_SetItemA (HWND hwnd, WPARAM wParam, LPARAM lParam)
979 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
980 TREEVIEW_ITEM *wineItem;
981 TVITEMEXA *tvItem;
982 INT iItem,len;
984 tvItem=(LPTVITEMEXA) lParam;
985 iItem=(INT)tvItem->hItem;
986 TRACE("item %d,mask %x\n",iItem,tvItem->mask);
988 wineItem = TREEVIEW_ValidItem (infoPtr, (HTREEITEM)iItem);
989 if (!wineItem) return FALSE;
991 if (tvItem->mask & TVIF_CHILDREN) {
992 wineItem->cChildren=tvItem->cChildren;
995 if (tvItem->mask & TVIF_IMAGE) {
996 wineItem->iImage=tvItem->iImage;
999 if (tvItem->mask & TVIF_INTEGRAL) {
1000 wineItem->iIntegral=tvItem->iIntegral;
1003 if (tvItem->mask & TVIF_PARAM) {
1004 wineItem->lParam=tvItem->lParam;
1007 if (tvItem->mask & TVIF_SELECTEDIMAGE) {
1008 wineItem->iSelectedImage=tvItem->iSelectedImage;
1011 if (tvItem->mask & TVIF_STATE) {
1012 TRACE ("prevstate,state,mask:%x,%x,%x\n",wineItem->state,tvItem->state,
1013 tvItem->stateMask);
1014 wineItem->state&= ~tvItem->stateMask;
1015 wineItem->state|= (tvItem->state & tvItem->stateMask);
1016 wineItem->stateMask|= tvItem->stateMask;
1019 if (tvItem->mask & TVIF_TEXT) {
1020 if (tvItem->pszText!=LPSTR_TEXTCALLBACKA) {
1021 len=lstrlenA (tvItem->pszText);
1022 if (len>wineItem->cchTextMax)
1023 wineItem->pszText= COMCTL32_ReAlloc (wineItem->pszText, len+1);
1024 lstrcpynA (wineItem->pszText, tvItem->pszText,len+1);
1025 } else {
1026 if (wineItem->cchTextMax) {
1027 COMCTL32_Free (wineItem->pszText);
1028 wineItem->cchTextMax=0;
1030 wineItem->pszText=LPSTR_TEXTCALLBACKA;
1034 wineItem->mask |= tvItem->mask;
1036 return TRUE;
1039 static LRESULT
1040 TREEVIEW_GetItemState (HWND hwnd, WPARAM wParam, LPARAM lParam)
1043 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
1044 TREEVIEW_ITEM *wineItem;
1046 TRACE("\n");
1047 wineItem = TREEVIEW_ValidItem (infoPtr, (HTREEITEM)wParam);
1048 if (!wineItem) return 0;
1050 return (wineItem->state & lParam);
1056 static void
1057 TREEVIEW_Refresh (HWND hwnd, HDC hdc)
1059 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
1060 TEXTMETRICA tm;
1061 HBRUSH hbrBk;
1062 RECT rect;
1063 INT iItem, indent, x, y, height, itemHeight;
1064 INT viewtop,viewbottom,viewleft,viewright;
1065 TREEVIEW_ITEM *wineItem, *prevItem;
1067 TRACE("\n");
1070 if (infoPtr->Timer & TV_REFRESH_TIMER_SET) {
1071 KillTimer (hwnd, TV_REFRESH_TIMER);
1072 infoPtr->Timer &= ~TV_REFRESH_TIMER_SET;
1076 GetClientRect (hwnd, &rect);
1077 if ((rect.left-rect.right ==0) || (rect.top-rect.bottom==0)) return;
1079 infoPtr->cdmode=TREEVIEW_SendCustomDrawNotify(hwnd,CDDS_PREPAINT,hdc,rect);
1081 if (infoPtr->cdmode==CDRF_SKIPDEFAULT) return;
1083 infoPtr->uVisibleHeight= rect.bottom-rect.top;
1084 infoPtr->uVisibleWidth= rect.right-rect.left;
1086 viewtop=infoPtr->cy;
1087 viewbottom=infoPtr->cy + rect.bottom-rect.top;
1088 viewleft=infoPtr->cx;
1089 viewright=infoPtr->cx + rect.right-rect.left;
1091 TRACE("[%d %d %d %d]\n",viewtop,viewbottom,viewleft,viewright);
1093 /* draw background */
1095 hbrBk = CreateSolidBrush (infoPtr->clrBk);
1096 FillRect(hdc, &rect, hbrBk);
1097 DeleteObject(hbrBk);
1099 ImageList_GetIconSize (infoPtr->himlNormal, &x, &itemHeight);
1100 if (infoPtr->uItemHeight>itemHeight)
1101 itemHeight=infoPtr->uItemHeight;
1103 GetTextMetricsA (hdc, &tm);
1104 if ((tm.tmHeight + tm.tmExternalLeading) > itemHeight)
1105 itemHeight=tm.tmHeight + tm.tmExternalLeading;
1107 infoPtr->uRealItemHeight=itemHeight;
1109 iItem=(INT)infoPtr->TopRootItem;
1110 infoPtr->firstVisible=0;
1111 wineItem=NULL;
1112 indent=0;
1113 x=y=0;
1115 while (iItem) {
1116 prevItem=wineItem;
1117 wineItem= & infoPtr->items[iItem];
1118 wineItem->iLevel=indent;
1120 /* FIXME: remove this in later stage */
1122 if (wineItem->pszText!=LPSTR_TEXTCALLBACK32A)
1123 TRACE (treeview, "%d %d [%d %d %d %d] (%s)\n",y,x,
1124 wineItem->rect.top, wineItem->rect.bottom,
1125 wineItem->rect.left, wineItem->rect.right,
1126 wineItem->pszText);
1127 else
1128 TRACE (treeview, "%d [%d %d %d %d] (CALLBACK)\n",
1129 wineItem->hItem,
1130 wineItem->rect.top, wineItem->rect.bottom,
1131 wineItem->rect.left, wineItem->rect.right);
1134 height=itemHeight * wineItem->iIntegral +1;
1135 if ((y >= viewtop) && (y <= viewbottom) &&
1136 (x >= viewleft ) && (x <= viewright)) {
1137 wineItem->visible = TRUE;
1138 wineItem->rect.top = y - infoPtr->cy + rect.top;
1139 wineItem->rect.bottom = wineItem->rect.top + height ;
1140 wineItem->rect.left = x - infoPtr->cx + rect.left;
1141 wineItem->rect.right = rect.right;
1142 if (!infoPtr->firstVisible)
1143 infoPtr->firstVisible=wineItem->hItem;
1144 TREEVIEW_DrawItem (hwnd, hdc, wineItem);
1146 else {
1147 wineItem->visible = FALSE;
1148 wineItem->rect.left = wineItem->rect.top = 0;
1149 wineItem->rect.right= wineItem->rect.bottom = 0;
1150 wineItem->text.left = wineItem->text.top = 0;
1151 wineItem->text.right= wineItem->text.bottom = 0;
1154 /* look up next item */
1156 if ((wineItem->firstChild) && (wineItem->state & TVIS_EXPANDED)) {
1157 iItem=(INT)wineItem->firstChild;
1158 indent++;
1159 x+=infoPtr->uIndent;
1160 if (x>infoPtr->uTotalWidth)
1161 infoPtr->uTotalWidth=x;
1163 else {
1164 iItem=(INT)wineItem->sibling;
1165 while ((!iItem) && (indent>0)) {
1166 indent--;
1167 x-=infoPtr->uIndent;
1168 wineItem=&infoPtr->items[(INT)wineItem->parent];
1169 iItem=(INT)wineItem->sibling;
1172 y +=height;
1173 } /* while */
1175 /* FIXME: infoPtr->uTotalWidth should also take item label into account */
1176 /* FIXME: or should query item sizes (ie check CDRF_NEWFONT) */
1178 infoPtr->uTotalHeight=y;
1179 if (y >= (viewbottom-viewtop)) {
1180 if (!(infoPtr->uInternalStatus & TV_VSCROLL))
1181 ShowScrollBar (hwnd, SB_VERT, TRUE);
1182 infoPtr->uInternalStatus |=TV_VSCROLL;
1183 SetScrollRange (hwnd, SB_VERT, 0,
1184 y - infoPtr->uVisibleHeight, FALSE);
1185 SetScrollPos (hwnd, SB_VERT, infoPtr->cy, TRUE);
1187 else {
1188 if (infoPtr->uInternalStatus & TV_VSCROLL)
1189 ShowScrollBar (hwnd, SB_VERT, FALSE);
1190 infoPtr->uInternalStatus &= ~TV_VSCROLL;
1194 if (infoPtr->cdmode & CDRF_NOTIFYPOSTPAINT)
1195 infoPtr->cdmode=TREEVIEW_SendCustomDrawNotify
1196 (hwnd, CDDS_POSTPAINT, hdc, rect);
1198 TRACE("done\n");
1202 static LRESULT
1203 TREEVIEW_HandleTimer (HWND hwnd, WPARAM wParam, LPARAM lParam)
1205 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
1207 TRACE(" %d\n",wParam);
1209 switch (wParam) {
1210 case TV_REFRESH_TIMER:
1211 KillTimer (hwnd, TV_REFRESH_TIMER);
1212 infoPtr->Timer &= ~TV_REFRESH_TIMER_SET;
1213 InvalidateRect(hwnd, NULL, FALSE);
1214 return 0;
1215 case TV_EDIT_TIMER:
1216 KillTimer (hwnd, TV_EDIT_TIMER);
1217 infoPtr->Timer &= ~TV_EDIT_TIMER_SET;
1218 return 0;
1219 default:
1220 ERR("got unknown timer\n");
1223 return 1;
1227 static void
1228 TREEVIEW_QueueRefresh (HWND hwnd)
1231 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
1233 TRACE("\n");
1234 if (infoPtr->Timer & TV_REFRESH_TIMER_SET) {
1235 KillTimer (hwnd, TV_REFRESH_TIMER);
1238 SetTimer (hwnd, TV_REFRESH_TIMER, TV_REFRESH_DELAY, 0);
1239 infoPtr->Timer|=TV_REFRESH_TIMER_SET;
1244 static LRESULT
1245 TREEVIEW_GetItemA (HWND hwnd, WPARAM wParam, LPARAM lParam)
1247 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
1248 LPTVITEMEXA tvItem;
1249 TREEVIEW_ITEM *wineItem;
1250 INT iItem;
1252 tvItem=(LPTVITEMEXA) lParam;
1253 iItem=(INT)tvItem->hItem;
1255 wineItem = TREEVIEW_ValidItem (infoPtr, (HTREEITEM)iItem);
1256 if (!wineItem) return FALSE;
1258 if (tvItem->mask & TVIF_CHILDREN) {
1259 if (TVIF_CHILDREN==I_CHILDRENCALLBACK)
1260 FIXME("I_CHILDRENCALLBACK not supported\n");
1261 tvItem->cChildren=wineItem->cChildren;
1264 if (tvItem->mask & TVIF_HANDLE) {
1265 tvItem->hItem=wineItem->hItem;
1268 if (tvItem->mask & TVIF_IMAGE) {
1269 tvItem->iImage=wineItem->iImage;
1272 if (tvItem->mask & TVIF_INTEGRAL) {
1273 tvItem->iIntegral=wineItem->iIntegral;
1276 /* undocumented: windows ignores TVIF_PARAM and
1277 * always sets lParam
1279 tvItem->lParam=wineItem->lParam;
1281 if (tvItem->mask & TVIF_SELECTEDIMAGE) {
1282 tvItem->iSelectedImage=wineItem->iSelectedImage;
1285 if (tvItem->mask & TVIF_STATE) {
1286 tvItem->state=wineItem->state & tvItem->stateMask;
1289 if (tvItem->mask & TVIF_TEXT) {
1290 if (wineItem->pszText == LPSTR_TEXTCALLBACKA) {
1291 tvItem->pszText = LPSTR_TEXTCALLBACKA; /* FIXME:send notification? */
1292 ERR(" GetItem called with LPSTR_TEXTCALLBACK\n");
1294 else if (wineItem->pszText) {
1295 lstrcpynA (tvItem->pszText, wineItem->pszText, tvItem->cchTextMax);
1299 TRACE("item %d<%p>, txt %p, img %p, action %x\n",
1300 iItem, tvItem, tvItem->pszText, &tvItem->iImage, tvItem->mask);
1302 return TRUE;
1307 /* FIXME: check implementation of TVGN_NEXT/TVGN_NEXTVISIBLE */
1309 static LRESULT
1310 TREEVIEW_GetNextItem (HWND hwnd, WPARAM wParam, LPARAM lParam)
1313 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
1314 TREEVIEW_ITEM *wineItem, *returnItem;
1315 INT iItem = (INT)lParam, retval = 0, flag = (INT)wParam;
1316 HDC hdc;
1318 switch (flag) {
1319 case TVGN_ROOT:
1320 retval = (INT)infoPtr->TopRootItem;
1321 break;
1323 case TVGN_CARET:
1324 retval = (INT)infoPtr->selectedItem;
1325 break;
1327 case TVGN_FIRSTVISIBLE: /* FIXME:we should only recalculate, not redraw */
1328 hdc = GetDC (hwnd);
1329 TREEVIEW_Refresh (hwnd, hdc);
1330 ReleaseDC(hwnd,hdc);
1331 retval = (INT)infoPtr->firstVisible;
1332 break;
1334 case TVGN_DROPHILITE:
1335 retval = (INT)infoPtr->dropItem;
1336 break;
1338 case TVGN_NEXT:
1339 wineItem = TREEVIEW_ValidItem (infoPtr, (HTREEITEM)iItem);
1340 retval = wineItem ? (INT)wineItem->sibling : 0;
1341 break;
1343 case TVGN_PREVIOUS:
1344 wineItem = TREEVIEW_ValidItem (infoPtr, (HTREEITEM)iItem);
1345 retval = wineItem ? (INT)wineItem->upsibling : 0;
1346 break;
1348 case TVGN_PARENT:
1349 wineItem = TREEVIEW_ValidItem (infoPtr, (HTREEITEM)iItem);
1350 retval = wineItem ? (INT)wineItem->parent : 0;
1351 break;
1353 case TVGN_CHILD:
1354 wineItem = TREEVIEW_ValidItem (infoPtr, (HTREEITEM)iItem);
1355 retval = wineItem ? (INT)wineItem->firstChild : 0;
1356 break;
1358 case TVGN_LASTVISIBLE:
1359 if((wineItem = TREEVIEW_ValidItem (infoPtr, (HTREEITEM)iItem))) {
1360 returnItem = TREEVIEW_GetLastListItem (infoPtr,wineItem);
1361 retval = returnItem ? (INT)returnItem->hItem : 0;
1363 break;
1365 case TVGN_NEXTVISIBLE:
1366 if((wineItem = TREEVIEW_ValidItem (infoPtr, (HTREEITEM)iItem))) {
1367 returnItem = TREEVIEW_GetNextListItem (infoPtr,wineItem);
1368 retval = returnItem ? (INT)returnItem->hItem : 0;
1370 break;
1372 case TVGN_PREVIOUSVISIBLE:
1373 if((wineItem = TREEVIEW_ValidItem (infoPtr, (HTREEITEM)iItem))) {
1374 returnItem = TREEVIEW_GetPrevListItem (infoPtr, wineItem);
1375 retval = returnItem ? (INT)returnItem->hItem : 0;
1377 break;
1379 default:
1380 FIXME("Unknown msg %x,item %x\n", flag,iItem);
1381 break;
1384 TRACE("flags %x, item %d returns %d\n", flag, iItem, retval);
1385 return retval;
1389 static LRESULT
1390 TREEVIEW_GetCount (HWND hwnd, WPARAM wParam, LPARAM lParam)
1392 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
1394 TRACE(" %d\n",infoPtr->uNumItems);
1395 return (LRESULT) infoPtr->uNumItems;
1398 /***************************************************************************
1399 * This method does the chaining of the insertion of a treeview item
1400 * before an item.
1401 * If parent is NULL, we're inserting at the root of the list.
1403 static void TREEVIEW_InsertBefore(
1404 TREEVIEW_INFO *infoPtr,
1405 TREEVIEW_ITEM *newItem,
1406 TREEVIEW_ITEM *sibling,
1407 TREEVIEW_ITEM *parent)
1409 HTREEITEM siblingHandle = 0;
1410 HTREEITEM upSiblingHandle = 0;
1411 TREEVIEW_ITEM *upSibling = NULL;
1413 if (newItem == NULL)
1414 ERR("NULL newItem, impossible condition\n");
1416 if (sibling != NULL) /* Insert before this sibling for this parent */
1418 /* Store the new item sibling up sibling and sibling tem handle */
1419 siblingHandle = sibling->hItem;
1420 upSiblingHandle = sibling->upsibling;
1421 /* As well as a pointer to the upsibling sibling object */
1422 if ( (INT)sibling->upsibling != 0 )
1423 upSibling = &infoPtr->items[(INT)sibling->upsibling];
1425 /* Adjust the sibling pointer */
1426 sibling->upsibling = newItem->hItem;
1428 /* Adjust the new item pointers */
1429 newItem->upsibling = upSiblingHandle;
1430 newItem->sibling = siblingHandle;
1432 /* Adjust the up sibling pointer */
1433 if ( upSibling != NULL )
1434 upSibling->sibling = newItem->hItem;
1435 else
1436 /* this item is the first child of this parent, adjust parent pointers */
1437 if (parent)
1438 parent->firstChild = newItem->hItem;
1439 else
1440 infoPtr->TopRootItem= newItem->hItem;
1442 else /* Insert as first child of this parent */
1443 if (parent)
1444 parent->firstChild = newItem->hItem;
1447 /***************************************************************************
1448 * This method does the chaining of the insertion of a treeview item
1449 * after an item.
1450 * If parent is NULL, we're inserting at the root of the list.
1452 static void TREEVIEW_InsertAfter(
1453 TREEVIEW_INFO *infoPtr,
1454 TREEVIEW_ITEM *newItem,
1455 TREEVIEW_ITEM *upSibling,
1456 TREEVIEW_ITEM *parent)
1458 HTREEITEM upSiblingHandle = 0;
1459 HTREEITEM siblingHandle = 0;
1460 TREEVIEW_ITEM *sibling = NULL;
1463 if (newItem == NULL)
1464 ERR("NULL newItem, impossible condition\n");
1466 if (upSibling != NULL) /* Insert after this upsibling for this parent */
1468 /* Store the new item up sibling and sibling item handle */
1469 upSiblingHandle = upSibling->hItem;
1470 siblingHandle = upSibling->sibling;
1471 /* As well as a pointer to the upsibling sibling object */
1472 if ( (INT)upSibling->sibling != 0 )
1473 sibling = &infoPtr->items[(INT)upSibling->sibling];
1475 /* Adjust the up sibling pointer */
1476 upSibling->sibling = newItem->hItem;
1478 /* Adjust the new item pointers */
1479 newItem->upsibling = upSiblingHandle;
1480 newItem->sibling = siblingHandle;
1482 /* Adjust the sibling pointer */
1483 if ( sibling != NULL )
1484 sibling->upsibling = newItem->hItem;
1486 else
1487 newItem is the last of the level, nothing else to do
1490 else /* Insert as first child of this parent */
1491 if (parent)
1492 parent->firstChild = newItem->hItem;
1495 /***************************************************************************
1496 * Forward the DPA local callback to the treeview owner callback
1498 static INT WINAPI TREEVIEW_CallBackCompare(
1499 LPVOID first,
1500 LPVOID second,
1501 LPARAM tvInfoPtr)
1503 /* Forward the call to the client define callback */
1504 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr((HWND)tvInfoPtr);
1505 return (infoPtr->pCallBackSort->lpfnCompare)(
1506 ((TREEVIEW_ITEM*)first)->lParam,
1507 ((TREEVIEW_ITEM*)second)->lParam,
1508 infoPtr->pCallBackSort->lParam);
1511 /***************************************************************************
1512 * Treeview native sort routine: sort on item text.
1514 static INT WINAPI TREEVIEW_SortOnName (
1515 LPVOID first,
1516 LPVOID second,
1517 LPARAM tvInfoPtr)
1519 HWND hwnd=(HWND) tvInfoPtr;
1520 char *txt1, *txt2;
1521 TREEVIEW_ITEM *item;
1524 item=(TREEVIEW_ITEM *) first;
1525 if (item->pszText==LPSTR_TEXTCALLBACKA) {
1526 TREEVIEW_SendDispInfoNotify (hwnd, item, TVN_GETDISPINFOA, TVIF_TEXT);
1528 txt1=item->pszText;
1530 item=(TREEVIEW_ITEM *) second;
1531 if (item->pszText==LPSTR_TEXTCALLBACKA) {
1532 TREEVIEW_SendDispInfoNotify (hwnd, item, TVN_GETDISPINFOA, TVIF_TEXT);
1534 txt2=item->pszText;
1536 return -strcmp (txt1,txt2);
1539 /***************************************************************************
1540 * Setup the treeview structure with regards of the sort method
1541 * and sort the children of the TV item specified in lParam
1542 * fRecurse: currently unused. Should be zero.
1543 * parent: if pSort!=NULL, should equal pSort->hParent.
1544 * otherwise, item which child items are to be sorted.
1545 * pSort: sort method info. if NULL, sort on item text.
1546 * if non-NULL, sort on item's lParam content, and let the
1547 * application decide what that means. See also TVM_SORTCHILDRENCB.
1550 static LRESULT WINAPI TREEVIEW_Sort (
1551 HWND hwnd,
1552 BOOL fRecurse,
1553 HTREEITEM parent,
1554 LPTVSORTCB pSort
1557 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
1558 TREEVIEW_ITEM *sortMe = NULL; /* Node for which we sort the children */
1560 /* Obtain the TVSORTBC struct */
1561 infoPtr->pCallBackSort = pSort;
1563 /* undocumented feature: TVI_ROOT means `sort the whole tree' */
1565 if (parent==TVI_ROOT)
1566 parent=infoPtr->TopRootItem;
1568 /* Check for a valid handle to the parent item */
1569 if (!TREEVIEW_ValidItem(infoPtr, parent))
1571 ERR ("invalid item hParent=%x\n", (INT)parent);
1572 return FALSE;
1575 /* Obtain the parent node to sort */
1576 sortMe = &infoPtr->items[ (INT)parent ];
1578 /* Make sure there is something to sort */
1579 if ( sortMe->cChildren > 1 )
1581 /* pointer organization */
1582 HDPA sortList = DPA_Create(sortMe->cChildren);
1583 HTREEITEM itemHandle = sortMe->firstChild;
1584 TREEVIEW_ITEM *itemPtr = & infoPtr->items[ (INT)itemHandle ];
1586 /* TREEVIEW_ITEM rechaining */
1587 INT count = 0;
1588 VOID *item = 0;
1589 VOID *nextItem = 0;
1590 VOID *prevItem = 0;
1592 /* Build the list of item to sort */
1595 DPA_InsertPtr(
1596 sortList, /* the list */
1597 sortMe->cChildren+1, /* force the insertion to be an append */
1598 itemPtr); /* the ptr to store */
1600 /* Get the next sibling */
1601 itemHandle = itemPtr->sibling;
1602 itemPtr = & infoPtr->items[ (INT)itemHandle ];
1603 } while ( itemHandle != NULL );
1605 /* let DPA perform the sort activity */
1606 if (pSort)
1607 DPA_Sort(
1608 sortList, /* what */
1609 TREEVIEW_CallBackCompare, /* how */
1610 hwnd); /* owner */
1611 else
1612 DPA_Sort (
1613 sortList, /* what */
1614 TREEVIEW_SortOnName, /* how */
1615 hwnd); /* owner */
1618 * Reorganized TREEVIEW_ITEM structures.
1619 * Note that we know we have at least two elements.
1622 /* Get the first item and get ready to start... */
1623 item = DPA_GetPtr(sortList, count++);
1624 while ( (nextItem = DPA_GetPtr(sortList, count++)) != NULL )
1626 /* link the two current item toghether */
1627 ((TREEVIEW_ITEM*)item)->sibling = ((TREEVIEW_ITEM*)nextItem)->hItem;
1628 ((TREEVIEW_ITEM*)nextItem)->upsibling = ((TREEVIEW_ITEM*)item)->hItem;
1630 if (prevItem == NULL) /* this is the first item, update the parent */
1632 sortMe->firstChild = ((TREEVIEW_ITEM*)item)->hItem;
1633 ((TREEVIEW_ITEM*)item)->upsibling = NULL;
1635 else /* fix the back chaining */
1637 ((TREEVIEW_ITEM*)item)->upsibling = ((TREEVIEW_ITEM*)prevItem)->hItem;
1640 /* get ready for the next one */
1641 prevItem = item;
1642 item = nextItem;
1645 /* the last item is pointed to by item and never has a sibling */
1646 ((TREEVIEW_ITEM*)item)->sibling = NULL;
1648 DPA_Destroy(sortList);
1650 return TRUE;
1652 return FALSE;
1656 /***************************************************************************
1657 * Setup the treeview structure with regards of the sort method
1658 * and sort the children of the TV item specified in lParam
1660 static LRESULT WINAPI TREEVIEW_SortChildrenCB(
1661 HWND hwnd,
1662 WPARAM wParam,
1663 LPARAM lParam
1666 LPTVSORTCB pSort=(LPTVSORTCB) lParam;
1668 return TREEVIEW_Sort (hwnd, wParam, pSort->hParent, pSort);
1672 /***************************************************************************
1673 * Sort the children of the TV item specified in lParam.
1675 static LRESULT WINAPI TREEVIEW_SortChildren (
1676 HWND hwnd,
1677 WPARAM wParam,
1678 LPARAM lParam)
1680 return TREEVIEW_Sort (hwnd, (BOOL) wParam, (HTREEITEM) lParam, NULL);
1685 /* the method used below isn't the most memory-friendly, but it avoids
1686 a lot of memory reallocations */
1688 /* BTW: we waste handle 0; 0 is not an allowed handle. */
1690 static LRESULT
1691 TREEVIEW_InsertItemA (HWND hwnd, WPARAM wParam, LPARAM lParam)
1694 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
1695 TVINSERTSTRUCTA *ptdi;
1696 TVITEMEXA *tvItem;
1697 TREEVIEW_ITEM *wineItem, *parentItem, *prevsib, *sibItem;
1698 INT iItem,listItems,i,len;
1700 /* Item to insert */
1701 ptdi = (LPTVINSERTSTRUCTA) lParam;
1703 /* check if memory is available */
1705 if (infoPtr->uNumPtrsAlloced==0) {
1706 infoPtr->items = COMCTL32_Alloc (TVITEM_ALLOC*sizeof (TREEVIEW_ITEM));
1707 infoPtr->freeList= COMCTL32_Alloc ((1+(TVITEM_ALLOC>>5)) * sizeof (INT));
1708 infoPtr->uNumPtrsAlloced=TVITEM_ALLOC;
1709 infoPtr->TopRootItem=(HTREEITEM)1;
1713 * Reallocate contiguous space for items
1715 if (infoPtr->uNumItems == (infoPtr->uNumPtrsAlloced-1) ) {
1716 TREEVIEW_ITEM *oldItems = infoPtr->items;
1717 INT *oldfreeList = infoPtr->freeList;
1719 infoPtr->uNumPtrsAlloced*=2;
1720 infoPtr->items = COMCTL32_Alloc (infoPtr->uNumPtrsAlloced*sizeof (TREEVIEW_ITEM));
1721 infoPtr->freeList= COMCTL32_Alloc ((1+(infoPtr->uNumPtrsAlloced>>5))*sizeof (INT));
1723 memcpy (&infoPtr->items[0], &oldItems[0],
1724 infoPtr->uNumPtrsAlloced/2 * sizeof(TREEVIEW_ITEM));
1725 memcpy (&infoPtr->freeList[0], &oldfreeList[0],
1726 (infoPtr->uNumPtrsAlloced>>6) * sizeof(INT));
1728 COMCTL32_Free (oldItems);
1729 COMCTL32_Free (oldfreeList);
1733 * Reset infoPtr structure with new stat according to current TV picture
1735 iItem=0;
1736 infoPtr->uNumItems++;
1737 if ((INT)infoPtr->uMaxHandle==(infoPtr->uNumItems-1)) {
1738 iItem=infoPtr->uNumItems;
1739 infoPtr->uMaxHandle = (HTREEITEM)((INT)infoPtr->uMaxHandle + 1);
1740 } else { /* check freelist */
1741 for (i=0; i<=infoPtr->uNumPtrsAlloced>>5; i++) {
1742 if (infoPtr->freeList[i]) {
1743 iItem=ffs (infoPtr->freeList[i])-1;
1744 tv_clear_bit(iItem,&infoPtr->freeList[i]);
1745 iItem+=i<<5;
1746 break;
1751 if (TRACE_ON(treeview)) {
1752 for (i=0; i<=infoPtr->uNumPtrsAlloced>>5; i++)
1753 TRACE("%8x\n",infoPtr->freeList[i]);
1756 if (!iItem) ERR("Argh -- can't find free item.\n");
1759 * Find the parent item of the new item
1761 tvItem= & ptdi->DUMMYUNIONNAME.itemex;
1762 wineItem=& infoPtr->items[iItem];
1764 if ((ptdi->hParent==TVI_ROOT) || (ptdi->hParent==0)) {
1765 parentItem = NULL;
1766 wineItem->parent = 0;
1767 sibItem = &infoPtr->items [(INT)infoPtr->TopRootItem];
1768 listItems = infoPtr->uNumItems;
1770 else {
1771 parentItem = &infoPtr->items[(INT)ptdi->hParent];
1773 /* Do the insertion here it if it's the only item of this parent */
1774 if (!parentItem->firstChild)
1775 parentItem->firstChild=(HTREEITEM)iItem;
1777 wineItem->parent = ptdi->hParent;
1778 sibItem = &infoPtr->items [(INT)parentItem->firstChild];
1779 listItems = parentItem->cChildren;
1780 parentItem->cChildren++;
1784 /* NOTE: I am moving some setup of the wineItem object that was initialy
1785 * done at the end of the function since some of the values are
1786 * required by the Callback sorting
1789 if (tvItem->mask & TVIF_TEXT)
1792 * Setup the item text stuff here since it's required by the Sort method
1793 * when the insertion are ordered
1795 if (tvItem->pszText!=LPSTR_TEXTCALLBACKA)
1797 TRACE("(%p,%s)\n", &tvItem->pszText, tvItem->pszText);
1798 len = lstrlenA (tvItem->pszText)+1;
1799 wineItem->pszText= COMCTL32_Alloc (len+1);
1800 lstrcpyA (wineItem->pszText, tvItem->pszText);
1801 wineItem->cchTextMax=len;
1803 else
1805 TRACE("LPSTR_TEXTCALLBACK\n");
1806 wineItem->pszText = LPSTR_TEXTCALLBACKA;
1807 wineItem->cchTextMax = 0;
1811 if (tvItem->mask & TVIF_PARAM)
1812 wineItem->lParam=tvItem->lParam;
1815 wineItem->upsibling=0; /* needed in case we're the first item in a list */
1816 wineItem->sibling=0;
1817 wineItem->firstChild=0;
1818 wineItem->hItem=(HTREEITEM)iItem;
1820 if (listItems!=0) {
1821 prevsib=NULL;
1823 switch ((DWORD) ptdi->hInsertAfter) {
1824 case (DWORD) TVI_FIRST:
1825 if (sibItem==wineItem) break;
1826 if (wineItem->parent) {
1827 wineItem->sibling=parentItem->firstChild;
1828 parentItem->firstChild=(HTREEITEM)iItem;
1829 } else {
1830 wineItem->sibling=infoPtr->TopRootItem;
1831 infoPtr->TopRootItem=(HTREEITEM)iItem;
1833 sibItem->upsibling=(HTREEITEM)iItem;
1834 break;
1836 case (DWORD) TVI_SORT:
1837 if (sibItem==wineItem)
1839 * This item is the first child of the level and it
1840 * has already been inserted
1842 break;
1843 else
1845 TREEVIEW_ITEM *aChild;
1848 TREEVIEW_ITEM *previousChild = NULL;
1849 BOOL bItemInserted = FALSE;
1851 if (parentItem)
1852 aChild = &infoPtr->items[(INT)parentItem->firstChild];
1853 else
1854 aChild = &infoPtr->items[(INT)infoPtr->TopRootItem];
1856 /* lookup the text if using LPSTR_TEXTCALLBACKs */
1857 if (wineItem->pszText==LPSTR_TEXTCALLBACKA) {
1858 TREEVIEW_SendDispInfoNotify (hwnd, wineItem, TVN_GETDISPINFOA, TVIF_TEXT);
1861 /* Iterate the parent children to see where we fit in */
1862 while ( aChild != NULL )
1864 INT comp;
1866 /* lookup the text if using LPSTR_TEXTCALLBACKs */
1867 if (aChild->pszText==LPSTR_TEXTCALLBACKA) {
1868 TREEVIEW_SendDispInfoNotify (hwnd, aChild, TVN_GETDISPINFOA, TVIF_TEXT);
1871 comp = strcmp(wineItem->pszText, aChild->pszText);
1872 if ( comp < 0 ) /* we are smaller than the current one */
1874 TREEVIEW_InsertBefore(infoPtr, wineItem, aChild, parentItem);
1875 bItemInserted = TRUE;
1876 break;
1878 else if ( comp > 0 ) /* we are bigger than the current one */
1880 previousChild = aChild;
1881 aChild = (aChild->sibling == 0) /* This will help us to exit */
1882 ? NULL /* if there is no more sibling */
1883 : &infoPtr->items[(INT)aChild->sibling];
1885 /* Look at the next item */
1886 continue;
1888 else if ( comp == 0 )
1891 * An item with this name is already existing, therefore,
1892 * we add after the one we found
1894 TREEVIEW_InsertAfter(infoPtr, wineItem, aChild, parentItem);
1895 bItemInserted = TRUE;
1896 break;
1901 * we reach the end of the child list and the item as not
1902 * yet been inserted, therefore, insert it after the last child.
1904 if ( (! bItemInserted ) && (aChild == NULL) )
1905 TREEVIEW_InsertAfter(infoPtr, wineItem, previousChild, parentItem);
1907 break;
1911 case (DWORD) TVI_LAST:
1912 if (sibItem==wineItem) break;
1913 while (sibItem->sibling) {
1914 prevsib=sibItem;
1915 sibItem=&infoPtr->items [(INT)sibItem->sibling];
1917 sibItem->sibling=(HTREEITEM)iItem;
1918 wineItem->upsibling=sibItem->hItem;
1919 break;
1920 default:
1921 while ((sibItem->sibling) && (sibItem->hItem!=ptdi->hInsertAfter))
1923 prevsib=sibItem;
1924 sibItem=&infoPtr->items [(INT)sibItem->sibling];
1926 if (sibItem->hItem!=ptdi->hInsertAfter) {
1927 ERR("tried to insert item after nonexisting handle %d.\n",
1928 (INT) ptdi->hInsertAfter);
1929 break;
1931 prevsib=sibItem;
1932 if (sibItem->sibling) {
1933 sibItem=&infoPtr->items [(INT)sibItem->sibling];
1934 sibItem->upsibling=(HTREEITEM)iItem;
1935 wineItem->sibling=sibItem->hItem;
1937 prevsib->sibling=(HTREEITEM)iItem;
1938 wineItem->upsibling=prevsib->hItem;
1939 break;
1944 /* Fill in info structure */
1946 TRACE("new item %d; parent %d, mask %x\n", iItem,
1947 (INT)wineItem->parent,tvItem->mask);
1949 wineItem->mask=tvItem->mask;
1950 wineItem->iIntegral=1;
1952 if (tvItem->mask & TVIF_CHILDREN) {
1953 wineItem->cChildren=tvItem->cChildren;
1954 if (tvItem->cChildren==I_CHILDRENCALLBACK)
1955 FIXME(" I_CHILDRENCALLBACK not supported\n");
1958 wineItem->expandBox.left = 0; /* Initialize the expandBox */
1959 wineItem->expandBox.top = 0;
1960 wineItem->expandBox.right = 0;
1961 wineItem->expandBox.bottom = 0;
1963 if (tvItem->mask & TVIF_IMAGE)
1964 wineItem->iImage=tvItem->iImage;
1966 /* If the application sets TVIF_INTEGRAL without
1967 supplying a TVITEMEX structure, it's toast */
1969 if (tvItem->mask & TVIF_INTEGRAL)
1970 wineItem->iIntegral=tvItem->iIntegral;
1972 if (tvItem->mask & TVIF_SELECTEDIMAGE)
1973 wineItem->iSelectedImage=tvItem->iSelectedImage;
1975 if (tvItem->mask & TVIF_STATE) {
1976 TRACE("item state: %x ->%x\n", wineItem->state, tvItem->state);
1977 TRACE("statemask: %x ->%x\n", wineItem->stateMask, tvItem->stateMask);
1978 wineItem->state=tvItem->state;
1979 wineItem->stateMask=tvItem->stateMask;
1982 TREEVIEW_QueueRefresh (hwnd);
1984 return (LRESULT) iItem;
1988 static LRESULT
1989 TREEVIEW_InsertItemW(HWND hwnd, WPARAM wParam, LPARAM lParam)
1991 TVINSERTSTRUCTW *tvisW;
1992 TVINSERTSTRUCTA tvisA;
1993 LRESULT lRes;
1995 tvisW = (LPTVINSERTSTRUCTW)lParam;
1997 tvisA.hParent = tvisW->hParent;
1998 tvisA.hInsertAfter = tvisW->hInsertAfter;
2000 tvisA.DUMMYUNIONNAME.item.mask = tvisW->DUMMYUNIONNAME.item.mask;
2001 tvisA.DUMMYUNIONNAME.item.hItem = tvisW->DUMMYUNIONNAME.item.hItem;
2002 tvisA.DUMMYUNIONNAME.item.state = tvisW->DUMMYUNIONNAME.item.state;
2003 tvisA.DUMMYUNIONNAME.item.stateMask = tvisW->DUMMYUNIONNAME.item.stateMask;
2004 tvisA.DUMMYUNIONNAME.item.cchTextMax = tvisW->DUMMYUNIONNAME.item.cchTextMax;
2006 if(tvisW->DUMMYUNIONNAME.item.pszText)
2008 if (tvisW->DUMMYUNIONNAME.item.pszText!=LPSTR_TEXTCALLBACKW)
2010 int len = lstrlenW (tvisW->DUMMYUNIONNAME.item.pszText)+1;
2011 tvisA.DUMMYUNIONNAME.item.pszText = COMCTL32_Alloc (len);
2012 lstrcpyWtoA (tvisA.DUMMYUNIONNAME.item.pszText,
2013 tvisW->DUMMYUNIONNAME.item.pszText );
2015 else
2017 tvisA.DUMMYUNIONNAME.item.pszText = LPSTR_TEXTCALLBACKA;
2018 tvisA.DUMMYUNIONNAME.item.cchTextMax = 0;
2022 tvisA.DUMMYUNIONNAME.item.iImage = tvisW->DUMMYUNIONNAME.item.iImage;
2023 tvisA.DUMMYUNIONNAME.item.iSelectedImage = tvisW->DUMMYUNIONNAME.item.iSelectedImage;
2024 tvisA.DUMMYUNIONNAME.item.cChildren = tvisW->DUMMYUNIONNAME.item.cChildren;
2025 tvisA.DUMMYUNIONNAME.item.lParam = tvisW->DUMMYUNIONNAME.item.lParam;
2027 lRes = TREEVIEW_InsertItemA(hwnd,wParam,(LPARAM)&tvisA);
2029 if (tvisA.DUMMYUNIONNAME.item.pszText!=LPSTR_TEXTCALLBACKA)
2031 COMCTL32_Free(tvisA.DUMMYUNIONNAME.item.pszText);
2034 return lRes;
2039 static LRESULT
2040 TREEVIEW_DeleteItem (HWND hwnd, WPARAM wParam, LPARAM lParam)
2042 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
2043 INT iItem;
2044 TREEVIEW_ITEM *wineItem;
2046 TRACE("item = %08lx\n", lParam);
2048 if (lParam == (INT)TVI_ROOT) {
2049 TREEVIEW_RemoveTree (hwnd);
2050 } else {
2051 iItem= (INT) lParam;
2052 wineItem = TREEVIEW_ValidItem (infoPtr, (HTREEITEM)iItem);
2053 if (!wineItem) return FALSE;
2055 if (wineItem->pszText==LPSTR_TEXTCALLBACKA)
2056 TRACE("LPSTR_TEXTCALLBACK\n");
2057 else
2058 TRACE("%s\n",wineItem->pszText);
2059 TREEVIEW_RemoveItem (hwnd, wineItem);
2062 TREEVIEW_QueueRefresh (hwnd);
2064 return TRUE;
2069 static LRESULT
2070 TREEVIEW_GetIndent (HWND hwnd)
2072 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
2074 TRACE("\n");
2075 return infoPtr->uIndent;
2078 static LRESULT
2079 TREEVIEW_SetIndent (HWND hwnd, WPARAM wParam)
2081 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
2082 INT newIndent;
2084 TRACE("\n");
2085 newIndent=(INT) wParam;
2086 if (newIndent < MINIMUM_INDENT) newIndent=MINIMUM_INDENT;
2087 infoPtr->uIndent=newIndent;
2089 return 0;
2092 static LRESULT
2093 TREEVIEW_GetToolTips (HWND hwnd)
2096 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
2098 TRACE("\n");
2099 return infoPtr->hwndToolTip;
2103 static LRESULT
2104 TREEVIEW_SetToolTips (HWND hwnd, WPARAM wParam)
2107 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
2108 HWND prevToolTip;
2110 TRACE("\n");
2111 prevToolTip=infoPtr->hwndToolTip;
2112 infoPtr->hwndToolTip= (HWND) wParam;
2114 return prevToolTip;
2118 static LRESULT CALLBACK
2119 TREEVIEW_GetEditControl (HWND hwnd)
2122 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
2124 return infoPtr->hwndEdit;
2127 LRESULT CALLBACK
2128 TREEVIEW_Edit_SubclassProc (HWND hwnd, UINT uMsg, WPARAM wParam,
2129 LPARAM lParam)
2131 switch (uMsg)
2133 case WM_ERASEBKGND:
2135 RECT rc;
2136 HDC hdc = (HDC) wParam;
2137 GetClientRect (hwnd, &rc);
2138 Rectangle (hdc, rc.left, rc.top, rc.right, rc.bottom);
2139 return -1;
2142 case WM_GETDLGCODE:
2144 return DLGC_WANTARROWS | DLGC_WANTALLKEYS;
2147 default:
2149 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(GetParent(hwnd));
2150 if (infoPtr!=NULL)
2151 return CallWindowProcA (infoPtr->wpEditOrig, hwnd, uMsg, wParam, lParam);
2152 else
2153 break;
2158 return 0;
2162 /* should handle edit control messages here */
2164 static LRESULT
2165 TREEVIEW_Command (HWND hwnd, WPARAM wParam, LPARAM lParam)
2168 TRACE("%x %ld\n",wParam, lParam);
2170 switch (HIWORD(wParam))
2172 case EN_UPDATE:
2175 * Adjust the edit window size
2177 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
2178 TREEVIEW_ITEM *editItem = TREEVIEW_ValidItem(infoPtr, infoPtr->editItem);
2179 INT iLength = GetWindowTextLengthA(infoPtr->hwndEdit);
2180 HDC hdc = GetDC(infoPtr->hwndEdit);
2181 TEXTMETRICA tm;
2183 if ( GetTextMetricsA(hdc, &tm) )
2185 LONG newWidth = (iLength * tm.tmAveCharWidth) + 15;
2187 SetWindowPos (
2188 infoPtr->hwndEdit,
2189 HWND_TOP,
2190 editItem->text.left - 2,
2191 editItem->text.top - 1,
2192 newWidth,
2193 editItem->text.bottom - editItem->text.top + 3,
2194 SWP_DRAWFRAME );
2196 ReleaseDC(hwnd, hdc);
2198 break;
2201 case EN_KILLFOCUS:
2202 /* TREEVIEW_EndEditLabelNow(hwnd, (WPARAM)FALSE, 0);
2204 break;
2206 default:
2207 return SendMessageA (GetParent (hwnd), WM_COMMAND, wParam, lParam);
2210 return 0;
2213 static LRESULT
2214 TREEVIEW_Size (HWND hwnd, WPARAM wParam, LPARAM lParam)
2217 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
2219 if (infoPtr->bAutoSize)
2221 infoPtr->bAutoSize = FALSE;
2222 return 0;
2224 infoPtr->bAutoSize = TRUE;
2226 if (wParam == SIZE_RESTORED)
2228 infoPtr->uTotalWidth = LOWORD (lParam);
2229 infoPtr->uTotalHeight = HIWORD (lParam);
2230 } else {
2231 FIXME("WM_SIZE flag %x %lx not handled\n", wParam, lParam);
2234 TREEVIEW_QueueRefresh (hwnd);
2235 return 0;
2240 static LRESULT
2241 TREEVIEW_StyleChanged (HWND hwnd, WPARAM wParam, LPARAM lParam)
2243 HDC hdc;
2245 TRACE("(%x %lx)\n",wParam,lParam);
2246 hdc = GetDC (hwnd);
2247 TREEVIEW_Refresh (hwnd, hdc);
2248 ReleaseDC(hwnd,hdc);
2250 return 0;
2253 static LRESULT
2254 TREEVIEW_Create (HWND hwnd, WPARAM wParam, LPARAM lParam)
2256 TREEVIEW_INFO *infoPtr;
2257 DWORD dwStyle = GetWindowLongA (hwnd, GWL_STYLE);
2258 LOGFONTA logFont;
2259 TEXTMETRICA tm;
2260 HDC hdc;
2262 TRACE("wnd %x, style %lx\n",hwnd,dwStyle);
2263 /* allocate memory for info structure */
2264 infoPtr = (TREEVIEW_INFO *) COMCTL32_Alloc (sizeof(TREEVIEW_INFO));
2266 SetWindowLongA( hwnd, 0, (DWORD)infoPtr);
2268 if (infoPtr == NULL) {
2269 ERR("could not allocate info memory!\n");
2270 return 0;
2273 if ((TREEVIEW_INFO*) GetWindowLongA( hwnd, 0) != infoPtr) {
2274 ERR("pointer assignment error!\n");
2275 return 0;
2278 hdc=GetDC (hwnd);
2280 /* set default settings */
2281 infoPtr->uInternalStatus=0;
2282 infoPtr->uNumItems=0;
2283 infoPtr->clrBk = GetSysColor (COLOR_WINDOW);
2284 infoPtr->clrText = GetSysColor (COLOR_WINDOWTEXT);
2285 infoPtr->clrLine = GetSysColor (COLOR_WINDOWTEXT);
2286 infoPtr->clrInsertMark = GetSysColor (COLOR_BTNTEXT);
2287 infoPtr->cy = 0;
2288 infoPtr->cx = 0;
2289 infoPtr->uIndent = 15;
2290 infoPtr->himlNormal = NULL;
2291 infoPtr->himlState = NULL;
2292 infoPtr->uItemHeight = -1;
2293 GetTextMetricsA (hdc, &tm);
2294 infoPtr->hFont = GetStockObject (DEFAULT_GUI_FONT);
2295 GetObjectA (infoPtr->hFont, sizeof (LOGFONTA), &logFont);
2296 logFont.lfWeight=FW_BOLD;
2297 infoPtr->hBoldFont = CreateFontIndirectA (&logFont);
2299 infoPtr->items = NULL;
2300 infoPtr->selectedItem=0;
2301 infoPtr->clrText=-1; /* use system color */
2302 infoPtr->dropItem=0;
2303 infoPtr->insertMarkItem=0;
2304 infoPtr->insertBeforeorAfter=0;
2305 infoPtr->pCallBackSort=NULL;
2306 infoPtr->uScrollTime = 300; /* milliseconds */
2307 infoPtr->wpEditOrig = NULL; /* we haven't subclassed anything yet */
2309 infoPtr->hwndToolTip=0;
2310 if (!(dwStyle & TVS_NOTOOLTIPS)) { /* Create tooltip control */
2311 TTTOOLINFOA ti;
2313 infoPtr->hwndToolTip =
2314 CreateWindowExA (0, TOOLTIPS_CLASSA, NULL, 0,
2315 CW_USEDEFAULT, CW_USEDEFAULT,
2316 CW_USEDEFAULT, CW_USEDEFAULT,
2317 hwnd, 0, 0, 0);
2319 /* Send NM_TOOLTIPSCREATED notification */
2320 if (infoPtr->hwndToolTip) {
2321 NMTOOLTIPSCREATED nmttc;
2323 nmttc.hdr.hwndFrom = hwnd;
2324 nmttc.hdr.idFrom = GetWindowLongA( hwnd, GWL_ID);
2325 nmttc.hdr.code = NM_TOOLTIPSCREATED;
2326 nmttc.hwndToolTips = infoPtr->hwndToolTip;
2328 SendMessageA (GetParent (hwnd), WM_NOTIFY,
2329 (WPARAM) GetWindowLongA( hwnd, GWL_ID), (LPARAM)&nmttc);
2332 ZeroMemory (&ti, sizeof(TTTOOLINFOA));
2333 ti.cbSize = sizeof(TTTOOLINFOA);
2334 ti.uFlags = TTF_IDISHWND | TTF_TRACK | TTF_TRANSPARENT ;
2335 ti.hwnd = hwnd;
2336 ti.uId = 0;
2337 ti.lpszText = "Test"; /* LPSTR_TEXTCALLBACK; */
2338 SetRectEmpty (&ti.rect);
2340 SendMessageA (infoPtr->hwndToolTip, TTM_ADDTOOLA, 0, (LPARAM)&ti);
2343 infoPtr->hwndEdit = CreateWindowExA (
2344 WS_EX_LEFT,
2345 "EDIT",
2347 WS_CHILD | WS_BORDER | ES_AUTOHSCROLL |
2348 ES_WANTRETURN | ES_LEFT,
2349 0, 0, 0, 0,
2350 hwnd,
2351 0,0,0); /* FIXME: (HMENU)IDTVEDIT,pcs->hInstance,0);*/
2353 SendMessageA ( infoPtr->hwndEdit, WM_SETFONT, infoPtr->hFont, FALSE);
2354 infoPtr->wpEditOrig = (WNDPROC)SetWindowLongA (
2355 infoPtr->hwndEdit,
2356 GWL_WNDPROC,
2357 (LONG) TREEVIEW_Edit_SubclassProc);
2359 if (dwStyle & TVS_CHECKBOXES) {
2360 HBITMAP hbmLoad;
2361 int nIndex;
2363 infoPtr->himlState =
2364 ImageList_Create (16, 16,ILC_COLOR|ILC_MASK, 15, 1);
2366 hbmLoad = LoadBitmapA (COMCTL32_hModule, MAKEINTRESOURCEA(IDT_CHECK));
2367 TRACE ("%x\n",hbmLoad);
2368 nIndex = ImageList_AddMasked (infoPtr->himlState, hbmLoad, CLR_DEFAULT);
2369 TRACE ("%d\n",nIndex);
2370 DeleteObject (hbmLoad);
2372 ReleaseDC (hwnd, hdc);
2373 return 0;
2378 static LRESULT
2379 TREEVIEW_Destroy (HWND hwnd)
2381 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
2383 TRACE("\n");
2384 TREEVIEW_RemoveTree (hwnd);
2385 SetWindowLongA (hwnd, 0, (DWORD)NULL);
2387 if (infoPtr->Timer & TV_REFRESH_TIMER_SET)
2388 KillTimer (hwnd, TV_REFRESH_TIMER);
2389 if (infoPtr->hwndToolTip)
2390 DestroyWindow (infoPtr->hwndToolTip);
2392 COMCTL32_Free (infoPtr);
2393 return 0;
2397 static LRESULT
2398 TREEVIEW_Paint (HWND hwnd, WPARAM wParam, LPARAM lParam)
2400 HDC hdc;
2401 PAINTSTRUCT ps;
2403 TRACE("\n");
2404 hdc = wParam==0 ? BeginPaint (hwnd, &ps) : (HDC)wParam;
2405 TREEVIEW_Refresh (hwnd, hdc);
2406 if(!wParam) EndPaint (hwnd, &ps);
2407 TRACE("done\n");
2409 return DefWindowProcA (hwnd, WM_PAINT, wParam, lParam);
2412 static LRESULT
2413 TREEVIEW_SetFocus (HWND hwnd, WPARAM wParam, LPARAM lParam)
2415 TREEVIEW_SendSimpleNotify (hwnd, NM_SETFOCUS);
2416 InvalidateRect(hwnd, NULL, FALSE);
2417 return 0;
2420 static LRESULT
2421 TREEVIEW_KillFocus (HWND hwnd, WPARAM wParam, LPARAM lParam)
2423 TREEVIEW_SendSimpleNotify (hwnd, NM_KILLFOCUS);
2424 InvalidateRect(hwnd, NULL, FALSE);
2425 return 0;
2428 static LRESULT
2429 TREEVIEW_EraseBackground (HWND hwnd, WPARAM wParam, LPARAM lParam)
2431 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
2432 HBRUSH hBrush = CreateSolidBrush (infoPtr->clrBk);
2433 RECT rect;
2435 TRACE("\n");
2436 GetClientRect (hwnd, &rect);
2437 FillRect ((HDC)wParam, &rect, hBrush);
2438 DeleteObject (hBrush);
2439 return TRUE;
2447 /* Notifications */
2453 static BOOL
2454 TREEVIEW_SendSimpleNotify (HWND hwnd, UINT code)
2456 NMHDR nmhdr;
2458 TRACE("%x\n",code);
2459 nmhdr.hwndFrom = hwnd;
2460 nmhdr.idFrom = GetWindowLongA( hwnd, GWL_ID);
2461 nmhdr.code = code;
2463 return (BOOL) SendMessageA (GetParent (hwnd), WM_NOTIFY,
2464 (WPARAM)nmhdr.idFrom, (LPARAM)&nmhdr);
2469 static BOOL
2470 TREEVIEW_SendTreeviewNotify (HWND hwnd, UINT code, UINT action,
2471 HTREEITEM oldItem, HTREEITEM newItem)
2474 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
2475 NMTREEVIEWA nmhdr;
2476 TREEVIEW_ITEM *wineItem;
2478 TRACE("code:%x action:%x olditem:%x newitem:%x\n",
2479 code,action,(INT)oldItem,(INT)newItem);
2480 nmhdr.hdr.hwndFrom = hwnd;
2481 nmhdr.hdr.idFrom = GetWindowLongA( hwnd, GWL_ID);
2482 nmhdr.hdr.code = code;
2483 nmhdr.action = action;
2484 if (oldItem) {
2485 wineItem=& infoPtr->items[(INT)oldItem];
2486 nmhdr.itemOld.mask = wineItem->mask;
2487 nmhdr.itemOld.hItem = wineItem->hItem;
2488 nmhdr.itemOld.state = wineItem->state;
2489 nmhdr.itemOld.stateMask = wineItem->stateMask;
2490 nmhdr.itemOld.iImage = wineItem->iImage;
2491 nmhdr.itemOld.pszText = wineItem->pszText;
2492 nmhdr.itemOld.cchTextMax= wineItem->cchTextMax;
2493 nmhdr.itemOld.iImage = wineItem->iImage;
2494 nmhdr.itemOld.iSelectedImage = wineItem->iSelectedImage;
2495 nmhdr.itemOld.cChildren = wineItem->cChildren;
2496 nmhdr.itemOld.lParam = wineItem->lParam;
2499 if (newItem) {
2500 wineItem=& infoPtr->items[(INT)newItem];
2501 nmhdr.itemNew.mask = wineItem->mask;
2502 nmhdr.itemNew.hItem = wineItem->hItem;
2503 nmhdr.itemNew.state = wineItem->state;
2504 nmhdr.itemNew.stateMask = wineItem->stateMask;
2505 nmhdr.itemNew.iImage = wineItem->iImage;
2506 nmhdr.itemNew.pszText = wineItem->pszText;
2507 nmhdr.itemNew.cchTextMax= wineItem->cchTextMax;
2508 nmhdr.itemNew.iImage = wineItem->iImage;
2509 nmhdr.itemNew.iSelectedImage = wineItem->iSelectedImage;
2510 nmhdr.itemNew.cChildren = wineItem->cChildren;
2511 nmhdr.itemNew.lParam = wineItem->lParam;
2514 nmhdr.ptDrag.x = 0;
2515 nmhdr.ptDrag.y = 0;
2517 return (BOOL)SendMessageA (GetParent (hwnd), WM_NOTIFY,
2518 (WPARAM) GetWindowLongA( hwnd, GWL_ID), (LPARAM)&nmhdr);
2522 static BOOL
2523 TREEVIEW_SendTreeviewDnDNotify (HWND hwnd, UINT code, HTREEITEM dragItem,
2524 POINT pt)
2526 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
2527 NMTREEVIEWA nmhdr;
2528 TREEVIEW_ITEM *wineItem;
2530 TRACE("code:%x dragitem:%x\n", code,(INT)dragItem);
2532 nmhdr.hdr.hwndFrom = hwnd;
2533 nmhdr.hdr.idFrom = GetWindowLongA( hwnd, GWL_ID);
2534 nmhdr.hdr.code = code;
2535 nmhdr.action = 0;
2536 wineItem=& infoPtr->items[(INT)dragItem];
2537 nmhdr.itemNew.mask = wineItem->mask;
2538 nmhdr.itemNew.hItem = wineItem->hItem;
2539 nmhdr.itemNew.state = wineItem->state;
2540 nmhdr.itemNew.lParam = wineItem->lParam;
2542 nmhdr.ptDrag.x = pt.x;
2543 nmhdr.ptDrag.y = pt.y;
2545 return (BOOL)SendMessageA (GetParent (hwnd), WM_NOTIFY,
2546 (WPARAM) GetWindowLongA( hwnd, GWL_ID), (LPARAM)&nmhdr);
2552 static BOOL
2553 TREEVIEW_SendDispInfoNotify (HWND hwnd, TREEVIEW_ITEM *wineItem,
2554 UINT code, UINT what)
2556 NMTVDISPINFOA tvdi;
2557 BOOL retval;
2558 char *buf;
2560 TRACE("item %d, action %x, state %d\n",
2561 (INT)wineItem->hItem,
2562 what,
2563 (INT)wineItem->state);
2565 tvdi.hdr.hwndFrom = hwnd;
2566 tvdi.hdr.idFrom = GetWindowLongA( hwnd, GWL_ID);
2567 tvdi.hdr.code = code;
2568 tvdi.item.mask = what;
2569 tvdi.item.hItem = wineItem->hItem;
2570 tvdi.item.state = wineItem->state;
2571 tvdi.item.lParam = wineItem->lParam;
2572 tvdi.item.pszText = COMCTL32_Alloc (128*sizeof(char));
2573 tvdi.item.cchTextMax = 128;
2574 buf = tvdi.item.pszText;
2576 retval=(BOOL)SendMessageA (
2577 GetParent(hwnd),
2578 WM_NOTIFY,
2579 (WPARAM)tvdi.hdr.idFrom,
2580 (LPARAM)&tvdi);
2582 if (what & TVIF_TEXT) {
2583 wineItem->pszText = tvdi.item.pszText;
2584 if (buf==tvdi.item.pszText) {
2585 wineItem->cchTextMax = 128;
2586 } else {
2587 TRACE("user-supplied buffer\n");
2588 COMCTL32_Free (buf);
2589 wineItem->cchTextMax = 0;
2592 if (what & TVIF_SELECTEDIMAGE)
2593 wineItem->iSelectedImage = tvdi.item.iSelectedImage;
2594 if (what & TVIF_IMAGE)
2595 wineItem->iImage = tvdi.item.iImage;
2596 if (what & TVIF_CHILDREN)
2597 wineItem->cChildren = tvdi.item.cChildren;
2599 return retval;
2604 static BOOL
2605 TREEVIEW_SendCustomDrawNotify (HWND hwnd, DWORD dwDrawStage, HDC hdc,
2606 RECT rc)
2608 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
2609 NMTVCUSTOMDRAW nmcdhdr;
2610 LPNMCUSTOMDRAW nmcd;
2612 TRACE("drawstage:%lx hdc:%x\n", dwDrawStage, hdc);
2614 nmcd= & nmcdhdr.nmcd;
2615 nmcd->hdr.hwndFrom = hwnd;
2616 nmcd->hdr.idFrom = GetWindowLongA( hwnd, GWL_ID);
2617 nmcd->hdr.code = NM_CUSTOMDRAW;
2618 nmcd->dwDrawStage= dwDrawStage;
2619 nmcd->hdc = hdc;
2620 nmcd->rc.left = rc.left;
2621 nmcd->rc.right = rc.right;
2622 nmcd->rc.bottom = rc.bottom;
2623 nmcd->rc.top = rc.top;
2624 nmcd->dwItemSpec = 0;
2625 nmcd->uItemState = 0;
2626 nmcd->lItemlParam= 0;
2627 nmcdhdr.clrText = infoPtr->clrText;
2628 nmcdhdr.clrTextBk= infoPtr->clrBk;
2629 nmcdhdr.iLevel = 0;
2631 return (BOOL)SendMessageA (GetParent (hwnd), WM_NOTIFY,
2632 (WPARAM) GetWindowLongA( hwnd, GWL_ID), (LPARAM)&nmcdhdr);
2638 /* FIXME: need to find out when the flags in uItemState need to be set */
2640 static BOOL
2641 TREEVIEW_SendCustomDrawItemNotify (HWND hwnd, HDC hdc,
2642 TREEVIEW_ITEM *wineItem, UINT uItemDrawState)
2644 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
2645 NMTVCUSTOMDRAW nmcdhdr;
2646 LPNMCUSTOMDRAW nmcd;
2647 DWORD dwDrawStage,dwItemSpec;
2648 UINT uItemState;
2649 INT retval;
2651 dwDrawStage=CDDS_ITEM | uItemDrawState;
2652 dwItemSpec=(DWORD)wineItem->hItem;
2653 uItemState=0;
2654 if (wineItem->hItem==infoPtr->selectedItem) uItemState|=CDIS_SELECTED;
2655 if (wineItem->hItem==infoPtr->focusItem) uItemState|=CDIS_FOCUS;
2656 if (wineItem->hItem==infoPtr->hotItem) uItemState|=CDIS_HOT;
2658 nmcd= & nmcdhdr.nmcd;
2659 nmcd->hdr.hwndFrom = hwnd;
2660 nmcd->hdr.idFrom = GetWindowLongA( hwnd, GWL_ID);
2661 nmcd->hdr.code = NM_CUSTOMDRAW;
2662 nmcd->dwDrawStage= dwDrawStage;
2663 nmcd->hdc = hdc;
2664 nmcd->rc.left = wineItem->rect.left;
2665 nmcd->rc.right = wineItem->rect.right;
2666 nmcd->rc.bottom = wineItem->rect.bottom;
2667 nmcd->rc.top = wineItem->rect.top;
2668 nmcd->dwItemSpec = dwItemSpec;
2669 nmcd->uItemState = uItemState;
2670 nmcd->lItemlParam= wineItem->lParam;
2671 nmcdhdr.clrText = infoPtr->clrText;
2672 nmcdhdr.clrTextBk= infoPtr->clrBk;
2673 nmcdhdr.iLevel = wineItem->iLevel;
2675 TRACE("drawstage:%lx hdc:%x item:%lx, itemstate:%x, lItemlParam:%lx\n",
2676 nmcd->dwDrawStage, nmcd->hdc, nmcd->dwItemSpec,
2677 nmcd->uItemState, nmcd->lItemlParam);
2679 retval=SendMessageA (GetParent (hwnd), WM_NOTIFY,
2680 (WPARAM) GetWindowLongA( hwnd, GWL_ID), (LPARAM)&nmcdhdr);
2682 infoPtr->clrText=nmcdhdr.clrText;
2683 infoPtr->clrBk =nmcdhdr.clrTextBk;
2684 return (BOOL) retval;
2689 /* Note:If the specified item is the child of a collapsed parent item,
2690 the parent's list of child items is (recursively) expanded to reveal the
2691 specified item. This is mentioned for TREEVIEW_SelectItem; don't
2692 know if it also applies here.
2695 static LRESULT
2696 TREEVIEW_Expand (HWND hwnd, WPARAM wParam, LPARAM lParam)
2698 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
2699 TREEVIEW_ITEM *wineItem;
2700 UINT flag;
2701 INT expand;
2703 flag = (UINT) wParam;
2704 expand = (INT) lParam;
2706 wineItem = TREEVIEW_ValidItem (infoPtr, (HTREEITEM)expand);
2708 if (!wineItem)
2709 return 0;
2710 if (!wineItem->cChildren)
2711 return 0;
2713 if (wineItem->pszText==LPSTR_TEXTCALLBACKA)
2714 TRACE ("For item %d, flags %d, state %d\n",
2715 expand, flag, wineItem->state);
2716 else
2717 TRACE("For (%s) item:%d, flags %x, state:%d\n",
2718 wineItem->pszText, flag, expand, wineItem->state);
2720 if (wineItem->cChildren==I_CHILDRENCALLBACK) {
2721 FIXME("we don't handle I_CHILDRENCALLBACK yet\n");
2722 return 0;
2725 if (flag == TVE_TOGGLE) { /* FIXME: check exact behaviour here */
2726 flag &= ~TVE_TOGGLE; /* ie: bitwise ops or 'case' ops */
2727 if (wineItem->state & TVIS_EXPANDED)
2728 flag |= TVE_COLLAPSE;
2729 else
2730 flag |= TVE_EXPAND;
2733 switch (flag)
2735 case TVE_COLLAPSERESET:
2736 TRACE(" case TVE_COLLAPSERESET\n");
2737 if (!wineItem->state & TVIS_EXPANDED)
2738 return 0;
2740 wineItem->state &= ~(TVIS_EXPANDEDONCE | TVIS_EXPANDED);
2741 TREEVIEW_RemoveAllChildren (hwnd, wineItem);
2742 break;
2744 case TVE_COLLAPSE:
2745 TRACE(" case TVE_COLLAPSE\n");
2746 if (!wineItem->state & TVIS_EXPANDED)
2747 return 0;
2749 wineItem->state &= ~TVIS_EXPANDED;
2750 break;
2752 case TVE_EXPAND:
2753 TRACE(" case TVE_EXPAND\n");
2754 if (wineItem->state & TVIS_EXPANDED)
2755 return 0;
2757 TRACE(" is not expanded...\n");
2759 if (!(wineItem->state & TVIS_EXPANDEDONCE))
2761 TRACE(" and has never been expanded...\n");
2762 wineItem->state |= TVIS_EXPANDED;
2764 /* this item has never been expanded */
2765 if (TREEVIEW_SendTreeviewNotify (
2766 hwnd,
2767 TVN_ITEMEXPANDINGA,
2768 TVE_EXPAND,
2770 (HTREEITEM)expand))
2772 TRACE(" TVN_ITEMEXPANDINGA returned TRUE, exiting...\n");
2773 return FALSE;
2776 /* FIXME
2777 * Since the TVN_ITEMEXPANDINGA message may has caused the parent to
2778 * insert new items which in turn may have cause items placeholder
2779 * reallocation, I reassign the current item pointer so we have
2780 * something valid to work with...
2781 * However, this should not be necessary,
2782 * investigation required in TREEVIEW_InsertItemA
2784 wineItem = TREEVIEW_ValidItem (infoPtr, (HTREEITEM)expand);
2785 if (! wineItem)
2787 ERR(
2788 "Catastropic situation, cannot retreive item #%d\n",
2789 expand);
2790 return FALSE;
2793 wineItem->state |= TVIS_EXPANDEDONCE;
2794 TRACE(" TVN_ITEMEXPANDINGA sent...\n");
2796 TREEVIEW_SendTreeviewNotify (
2797 hwnd,
2798 TVN_ITEMEXPANDEDA,
2799 TVE_EXPAND,
2801 (HTREEITEM)expand);
2803 TRACE(" TVN_ITEMEXPANDEDA sent...\n");
2806 else
2808 /* this item has already been expanded */
2809 wineItem->state |= TVIS_EXPANDED;
2811 break;
2813 case TVE_EXPANDPARTIAL:
2814 TRACE(" case TVE_EXPANDPARTIAL\n");
2815 FIXME("TVE_EXPANDPARTIAL not implemented\n");
2816 wineItem->state ^=TVIS_EXPANDED;
2817 wineItem->state |=TVIS_EXPANDEDONCE;
2818 break;
2821 TRACE("Exiting, Item %d state is now %d...\n",
2822 expand,
2823 wineItem->state);
2825 TREEVIEW_QueueRefresh (hwnd);
2826 return TRUE;
2831 static TREEVIEW_ITEM *
2832 TREEVIEW_HitTestPoint (HWND hwnd, POINT pt)
2834 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
2835 TREEVIEW_ITEM *wineItem;
2836 RECT rect;
2838 GetClientRect (hwnd, &rect);
2840 if (!infoPtr->firstVisible) return NULL;
2842 wineItem=&infoPtr->items [(INT)infoPtr->firstVisible];
2844 while ((wineItem!=NULL) && (pt.y > wineItem->rect.bottom))
2845 wineItem=TREEVIEW_GetNextListItem (infoPtr,wineItem);
2847 if (!wineItem)
2848 return NULL;
2850 return wineItem;
2856 static LRESULT
2857 TREEVIEW_HitTest (HWND hwnd, LPARAM lParam)
2859 LPTVHITTESTINFO lpht=(LPTVHITTESTINFO) lParam;
2860 TREEVIEW_ITEM *wineItem;
2861 RECT rect;
2862 UINT status,x,y;
2864 GetClientRect (hwnd, &rect);
2865 status=0;
2866 x=lpht->pt.x;
2867 y=lpht->pt.y;
2868 if (x < rect.left) status|=TVHT_TOLEFT;
2869 if (x > rect.right) status|=TVHT_TORIGHT;
2870 if (y < rect.top ) status|=TVHT_ABOVE;
2871 if (y > rect.bottom) status|=TVHT_BELOW;
2873 if (status) {
2874 lpht->flags=status;
2875 return 0;
2878 wineItem=TREEVIEW_HitTestPoint (hwnd, lpht->pt);
2879 if (!wineItem) {
2880 lpht->flags=TVHT_NOWHERE;
2881 return 0;
2884 lpht->flags=0;
2886 if (x < wineItem->expandBox.left) {
2887 lpht->flags |= TVHT_ONITEMINDENT;
2888 goto done;
2890 if ( PtInRect ( &wineItem->expandBox, lpht->pt)) {
2891 lpht->flags |= TVHT_ONITEMBUTTON;
2892 goto done;
2894 if ( PtInRect ( &wineItem->bitmap, lpht->pt)) {
2895 lpht->flags |= TVHT_ONITEMICON;
2896 goto done;
2898 if ( PtInRect ( &wineItem->statebitmap, lpht->pt)) {
2899 lpht->flags |= TVHT_ONITEMSTATEICON;
2900 goto done;
2902 if ( PtInRect ( &wineItem->text, lpht->pt)) {
2903 lpht->flags |= TVHT_ONITEMLABEL;
2904 goto done;
2907 lpht->flags|=TVHT_ONITEMRIGHT;
2910 done:
2911 lpht->hItem=wineItem->hItem;
2912 TRACE ("(%ld,%ld):result %x\n",lpht->pt.x,lpht->pt.y,lpht->flags);
2914 return (LRESULT) wineItem->hItem;
2917 LRESULT WINAPI
2918 TREEVIEW_EndEditLabelNow (HWND hwnd, WPARAM wParam, LPARAM lParam)
2920 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
2921 TREEVIEW_ITEM *editedItem = TREEVIEW_ValidItem (infoPtr, infoPtr->editItem);
2922 BOOL bRevert = (BOOL)wParam;
2923 BOOL bReturn = ! bRevert;
2925 if ( ! (BOOL)wParam ) /* wParam is set to true to cancel the edition */
2927 if ( TREEVIEW_SendDispInfoNotify( /* return true to cancel edition */
2928 hwnd,
2929 editedItem,
2930 TVN_ENDLABELEDITA,
2933 bRevert = TRUE;
2934 bReturn = FALSE;
2938 if (bRevert == FALSE) /* Apply the changes */
2940 char tmpText[1024];
2941 int iLength = GetWindowTextA(infoPtr->hwndEdit, tmpText, 1023);
2942 bReturn = FALSE;
2944 if (iLength == 0)
2946 ERR("Problem retreiving new item label.");
2948 else if (iLength >= 1023)
2950 ERR(
2951 "Insuficient space to retrieve new item label, new label ignored.");
2953 else
2955 if (strcmp( tmpText, editedItem->pszText ) == 0)
2956 /* Do nothing if the label has not changed */
2957 bReturn = TRUE;
2958 else
2960 LPSTR tmpLabel = COMCTL32_Alloc( iLength+1 );
2962 if ( tmpLabel == NULL )
2963 ERR(
2964 "OutOfMemory, cannot allocate space for label");
2965 else
2967 COMCTL32_Free(editedItem->pszText);
2968 editedItem->pszText = tmpLabel;
2969 lstrcpyA( editedItem->pszText, tmpText);
2970 bReturn = TRUE;
2975 ShowWindow(infoPtr->hwndEdit, SW_HIDE);
2976 EnableWindow(infoPtr->hwndEdit, FALSE);
2977 infoPtr->editItem = 0;
2980 return bReturn;
2985 static LRESULT
2986 TREEVIEW_LButtonDoubleClick (HWND hwnd, WPARAM wParam, LPARAM lParam)
2988 TREEVIEW_ITEM *wineItem;
2989 POINT pt;
2991 TRACE("\n");
2992 pt.x = (INT)LOWORD(lParam);
2993 pt.y = (INT)HIWORD(lParam);
2994 SetFocus (hwnd);
2996 wineItem=TREEVIEW_HitTestPoint (hwnd, pt);
2997 if (!wineItem) return 0;
2998 TRACE("item %d \n",(INT)wineItem->hItem);
3000 if (TREEVIEW_SendSimpleNotify (hwnd, NM_DBLCLK)!=TRUE) { /* FIXME!*/
3001 TREEVIEW_Expand (hwnd, (WPARAM) TVE_TOGGLE, (LPARAM) wineItem->hItem);
3003 return TRUE;
3007 static LRESULT
3008 TREEVIEW_LButtonDown (HWND hwnd, WPARAM wParam, LPARAM lParam)
3010 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
3011 INT iItem;
3012 TVHITTESTINFO ht;
3014 ht.pt.x = (INT)LOWORD(lParam);
3015 ht.pt.y = (INT)HIWORD(lParam);
3017 SetFocus (hwnd);
3018 iItem=TREEVIEW_HitTest (hwnd, (LPARAM) &ht);
3019 TRACE("item %d \n",iItem);
3021 if (ht.flags & TVHT_ONITEMBUTTON) {
3022 TREEVIEW_Expand (hwnd, (WPARAM) TVE_TOGGLE, (LPARAM) iItem);
3024 else
3026 infoPtr->uInternalStatus|=TV_LDRAG;
3029 return 0;
3032 static LRESULT
3033 TREEVIEW_LButtonUp (HWND hwnd, WPARAM wParam, LPARAM lParam)
3035 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
3036 INT iItem;
3037 TREEVIEW_ITEM *wineItem;
3038 TVHITTESTINFO ht;
3040 ht.pt.x = (INT)LOWORD(lParam);
3041 ht.pt.y = (INT)HIWORD(lParam);
3043 TRACE("\n");
3045 /* Return true to cancel default behaviour */
3046 if ( TREEVIEW_SendSimpleNotify (hwnd, NM_CLICK) )
3047 return 0;
3049 /* Get the item */
3050 iItem = TREEVIEW_HitTest (hwnd, (LPARAM) &ht);
3051 TRACE ("%d\n",iItem);
3052 if (!iItem)
3053 return 0;
3055 wineItem = TREEVIEW_ValidItem(infoPtr, (HTREEITEM)iItem);
3057 infoPtr->uInternalStatus &= ~(TV_LDRAG | TV_LDRAGGING);
3060 * If the style allow editing and the node is already selected
3061 * and the click occured on the item label...
3063 if ( ( GetWindowLongA( hwnd, GWL_STYLE) & TVS_EDITLABELS ) &&
3064 ( wineItem->state & TVIS_SELECTED ) &&
3065 ( ht.flags & TVHT_ONITEMLABEL ))
3067 if ( infoPtr->editItem == 0 ) /* If we are not curently editing */
3069 if ( TREEVIEW_SendDispInfoNotify( /* Return true to cancel edition */
3070 hwnd,
3071 wineItem,
3072 TVN_BEGINLABELEDITA,
3075 return 0;
3078 TRACE("Edit started for %s.\n", wineItem->pszText);
3079 infoPtr->editItem = wineItem->hItem;
3081 SetWindowPos (
3082 infoPtr->hwndEdit,
3083 HWND_TOP,
3084 wineItem->text.left - 2,
3085 wineItem->text.top - 1,
3086 wineItem->text.right - wineItem->text.left + 20 ,
3087 wineItem->text.bottom - wineItem->text.top + 3,
3088 SWP_DRAWFRAME );
3090 SetWindowTextA( infoPtr->hwndEdit, wineItem->pszText );
3091 SendMessageA ( infoPtr->hwndEdit, EM_SETSEL, 0, -1 );
3092 SetFocus ( infoPtr->hwndEdit);
3093 ShowWindow ( infoPtr->hwndEdit, SW_SHOW);
3096 else if ( infoPtr->editItem != 0 ) /* If we are curently editing */
3098 TREEVIEW_EndEditLabelNow(hwnd, (WPARAM)FALSE, 0);
3100 else if ( ht.flags & (TVHT_ONITEMLABEL | TVHT_ONITEMICON))
3102 TREEVIEW_DoSelectItem ( hwnd, TVGN_CARET, (HTREEITEM)iItem, TVC_BYMOUSE);
3105 if (ht.flags & TVHT_ONITEMSTATEICON) {
3106 DWORD dwStyle = GetWindowLongA (hwnd, GWL_STYLE);
3109 if (dwStyle & TVS_CHECKBOXES) { /* TVS_CHECKBOXES requires _us_ */
3110 int state; /* to toggle the current state */
3111 state=1-(wineItem->state>>12);
3112 TRACE ("state:%x\n", state);
3113 wineItem->state&= ~TVIS_STATEIMAGEMASK;
3114 wineItem->state|=state<<12;
3115 TRACE ("state:%x\n", wineItem->state);
3116 TREEVIEW_QueueRefresh (hwnd);
3119 return 0;
3123 static LRESULT
3124 TREEVIEW_RButtonDown (HWND hwnd, WPARAM wParam, LPARAM lParam)
3126 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
3128 TRACE("\n");
3129 infoPtr->uInternalStatus|=TV_RDRAG;
3130 return 0;
3133 static LRESULT
3134 TREEVIEW_RButtonUp (HWND hwnd, WPARAM wParam, LPARAM lParam)
3136 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
3138 TRACE("\n");
3139 if (TREEVIEW_SendSimpleNotify (hwnd, NM_RCLICK)) return 0;
3140 infoPtr->uInternalStatus&= ~(TV_RDRAG | TV_RDRAGGING);
3141 return 0;
3145 static LRESULT
3146 TREEVIEW_MouseMove (HWND hwnd, WPARAM wParam, LPARAM lParam)
3148 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
3149 TREEVIEW_ITEM *hotItem;
3150 POINT pt;
3152 pt.x=(INT) LOWORD (lParam);
3153 pt.y=(INT) HIWORD (lParam);
3154 hotItem=TREEVIEW_HitTestPoint (hwnd, pt);
3155 if (!hotItem) return 0;
3156 infoPtr->focusItem=hotItem->hItem;
3158 if ( GetWindowLongA( hwnd, GWL_STYLE) & TVS_DISABLEDRAGDROP) return 0;
3160 if (infoPtr->uInternalStatus & TV_LDRAG) {
3161 TREEVIEW_SendTreeviewDnDNotify (hwnd, TVN_BEGINDRAGA, hotItem->hItem, pt);
3162 infoPtr->uInternalStatus &= ~TV_LDRAG;
3163 infoPtr->uInternalStatus |= TV_LDRAGGING;
3164 infoPtr->dropItem=hotItem->hItem;
3165 return 0;
3168 if (infoPtr->uInternalStatus & TV_RDRAG) {
3169 TREEVIEW_SendTreeviewDnDNotify (hwnd, TVN_BEGINRDRAGA, hotItem->hItem, pt);
3170 infoPtr->uInternalStatus &= ~TV_RDRAG;
3171 infoPtr->uInternalStatus |= TV_RDRAGGING;
3172 infoPtr->dropItem=hotItem->hItem;
3173 return 0;
3176 return 0;
3180 static LRESULT
3181 TREEVIEW_CreateDragImage (HWND hwnd, WPARAM wParam, LPARAM lParam)
3183 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
3184 TREEVIEW_ITEM *dragItem;
3185 INT cx,cy;
3186 HDC hdc,htopdc;
3187 HWND hwtop;
3188 HBITMAP hbmp,hOldbmp;
3189 SIZE size;
3190 RECT rc;
3191 HFONT hOldFont;
3192 char *itemtxt;
3194 TRACE("\n");
3195 if (!(infoPtr->himlNormal)) return 0;
3196 dragItem=TREEVIEW_ValidItem (infoPtr, (HTREEITEM) lParam);
3198 if (!dragItem) return 0;
3200 if (dragItem->pszText==LPSTR_TEXTCALLBACKA) {
3201 TREEVIEW_SendDispInfoNotify (hwnd, dragItem, TVN_GETDISPINFOA, TVIF_TEXT);
3203 itemtxt=dragItem->pszText;
3205 hwtop=GetDesktopWindow ();
3206 htopdc= GetDC (hwtop);
3207 hdc=CreateCompatibleDC (htopdc);
3209 hOldFont=SelectObject (hdc, infoPtr->hFont);
3210 GetTextExtentPoint32A (hdc, itemtxt, lstrlenA (itemtxt), &size);
3211 TRACE("%d %d %s %d\n",size.cx,size.cy,itemtxt,lstrlenA(itemtxt));
3212 hbmp=CreateCompatibleBitmap (htopdc, size.cx, size.cy);
3213 hOldbmp=SelectObject (hdc, hbmp);
3215 ImageList_GetIconSize (infoPtr->himlNormal, &cx, &cy);
3216 size.cx+=cx;
3217 if (cy>size.cy) size.cy=cy;
3219 infoPtr->dragList=ImageList_Create (size.cx, size.cy, ILC_COLOR, 10, 10);
3220 ImageList_Draw (infoPtr->himlNormal, dragItem->iImage, hdc, 0, 0, ILD_NORMAL);
3223 ImageList_GetImageInfo (infoPtr->himlNormal, dragItem->hItem, &iminfo);
3224 ImageList_AddMasked (infoPtr->dragList, iminfo.hbmImage, CLR_DEFAULT);
3227 /* draw item text */
3229 SetRect (&rc, cx, 0, size.cx,size.cy);
3230 DrawTextA (hdc, itemtxt, lstrlenA (itemtxt), &rc, DT_LEFT);
3231 SelectObject (hdc, hOldFont);
3232 SelectObject (hdc, hOldbmp);
3234 ImageList_Add (infoPtr->dragList, hbmp, 0);
3236 DeleteDC (hdc);
3237 DeleteObject (hbmp);
3238 ReleaseDC (hwtop, htopdc);
3240 return (LRESULT)infoPtr->dragList;
3244 static LRESULT
3245 TREEVIEW_DoSelectItem (HWND hwnd, INT action, HTREEITEM newSelect, INT cause)
3248 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
3249 TREEVIEW_ITEM *prevItem,*wineItem;
3250 INT prevSelect;
3252 wineItem = TREEVIEW_ValidItem (infoPtr, (HTREEITEM)newSelect);
3254 TRACE("Entering item %d, flag %x, cause %x, state %d\n",
3255 (INT)newSelect,
3256 action,
3257 cause,
3258 wineItem->state);
3260 if ( (wineItem) && (wineItem->parent))
3263 * If the item has a collapse parent expand the parent so he
3264 * can expose the item
3266 TREEVIEW_ITEM *parentItem = TREEVIEW_ValidItem (infoPtr, wineItem->parent);
3267 if ( !(parentItem->state & TVIS_EXPANDED))
3268 TREEVIEW_Expand (hwnd, TVE_EXPAND, (LPARAM) wineItem->parent);
3271 switch (action)
3273 case TVGN_CARET:
3274 prevSelect=(INT)infoPtr->selectedItem;
3276 if ((HTREEITEM)prevSelect==newSelect)
3277 return FALSE;
3279 prevItem= TREEVIEW_ValidItem (infoPtr, (HTREEITEM)prevSelect);
3281 if (newSelect)
3282 if (TREEVIEW_SendTreeviewNotify(
3283 hwnd,
3284 TVN_SELCHANGINGA,
3285 cause,
3286 (HTREEITEM)prevSelect,
3287 (HTREEITEM)newSelect))
3288 return FALSE; /* FIXME: OK? */
3290 if (prevItem)
3291 prevItem->state &= ~TVIS_SELECTED;
3292 if (wineItem)
3293 wineItem->state |= TVIS_SELECTED;
3295 infoPtr->selectedItem=(HTREEITEM)newSelect;
3297 TREEVIEW_SendTreeviewNotify(
3298 hwnd,
3299 TVN_SELCHANGEDA,
3300 cause,
3301 (HTREEITEM)prevSelect,
3302 (HTREEITEM)newSelect);
3304 break;
3306 case TVGN_DROPHILITE:
3307 prevItem= TREEVIEW_ValidItem (infoPtr, infoPtr->dropItem);
3309 if (prevItem)
3310 prevItem->state &= ~TVIS_DROPHILITED;
3312 infoPtr->dropItem=(HTREEITEM)newSelect;
3314 if (wineItem)
3315 wineItem->state |=TVIS_DROPHILITED;
3317 break;
3319 case TVGN_FIRSTVISIBLE:
3320 FIXME("FIRSTVISIBLE not implemented\n");
3321 break;
3324 TREEVIEW_QueueRefresh (hwnd);
3326 TRACE("Leaving state %d\n", wineItem->state);
3327 return TRUE;
3330 /* FIXME: handle NM_KILLFocus etc */
3331 static LRESULT
3332 TREEVIEW_SelectItem (HWND hwnd, WPARAM wParam, LPARAM lParam)
3335 return TREEVIEW_DoSelectItem (hwnd, wParam, (HTREEITEM) lParam, TVC_UNKNOWN);
3341 static LRESULT
3342 TREEVIEW_GetFont (HWND hwnd, WPARAM wParam, LPARAM lParam)
3345 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
3347 TRACE("%x\n",infoPtr->hFont);
3348 return infoPtr->hFont;
3351 static LRESULT
3352 TREEVIEW_SetFont (HWND hwnd, WPARAM wParam, LPARAM lParam)
3355 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
3356 TEXTMETRICA tm;
3357 LOGFONTA logFont;
3358 HFONT hFont, hOldFont;
3359 INT height;
3360 HDC hdc;
3362 TRACE("%x %lx\n",wParam, lParam);
3364 infoPtr->hFont = (HFONT)wParam;
3366 hFont = infoPtr->hFont ? infoPtr->hFont : GetStockObject (SYSTEM_FONT);
3368 GetObjectA (infoPtr->hFont, sizeof (LOGFONTA), &logFont);
3369 logFont.lfWeight=FW_BOLD;
3370 infoPtr->hBoldFont = CreateFontIndirectA (&logFont);
3372 hdc = GetDC (0);
3373 hOldFont = SelectObject (hdc, hFont);
3374 GetTextMetricsA (hdc, &tm);
3375 height= tm.tmHeight + tm.tmExternalLeading;
3376 if (height>infoPtr->uRealItemHeight)
3377 infoPtr->uRealItemHeight=height;
3378 SelectObject (hdc, hOldFont);
3379 ReleaseDC (0, hdc);
3381 if (lParam)
3382 TREEVIEW_QueueRefresh (hwnd);
3384 return 0;
3389 static LRESULT
3390 TREEVIEW_VScroll (HWND hwnd, WPARAM wParam, LPARAM lParam)
3393 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
3394 int maxHeight;
3396 TRACE("wp %x, lp %lx\n", wParam, lParam);
3397 if (!infoPtr->uInternalStatus & TV_VSCROLL) return FALSE;
3399 switch (LOWORD (wParam)) {
3400 case SB_LINEUP:
3401 if (!infoPtr->cy) return FALSE;
3402 infoPtr->cy -= infoPtr->uRealItemHeight;
3403 if (infoPtr->cy < 0) infoPtr->cy=0;
3404 break;
3405 case SB_LINEDOWN:
3406 maxHeight=infoPtr->uTotalHeight-infoPtr->uVisibleHeight;
3407 if (infoPtr->cy == maxHeight) return FALSE;
3408 infoPtr->cy += infoPtr->uRealItemHeight;
3409 if (infoPtr->cy > maxHeight)
3410 infoPtr->cy = maxHeight;
3411 break;
3412 case SB_PAGEUP:
3413 if (!infoPtr->cy) return FALSE;
3414 infoPtr->cy -= infoPtr->uVisibleHeight;
3415 if (infoPtr->cy < 0) infoPtr->cy=0;
3416 break;
3417 case SB_PAGEDOWN:
3418 maxHeight=infoPtr->uTotalHeight-infoPtr->uVisibleHeight;
3419 if (infoPtr->cy == maxHeight) return FALSE;
3420 infoPtr->cy += infoPtr->uVisibleHeight;
3421 if (infoPtr->cy > maxHeight)
3422 infoPtr->cy = maxHeight;
3423 break;
3424 case SB_THUMBTRACK:
3425 infoPtr->cy = HIWORD (wParam);
3426 break;
3430 TREEVIEW_QueueRefresh (hwnd);
3431 return TRUE;
3434 static LRESULT
3435 TREEVIEW_HScroll (HWND hwnd, WPARAM wParam, LPARAM lParam)
3437 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
3438 int maxWidth;
3440 TRACE("wp %lx, lp %x\n", lParam, wParam);
3442 if (!infoPtr->uInternalStatus & TV_HSCROLL) return FALSE;
3444 switch (LOWORD (wParam)) {
3445 case SB_LINEUP:
3446 if (!infoPtr->cx) return FALSE;
3447 infoPtr->cx -= infoPtr->uRealItemHeight;
3448 if (infoPtr->cx < 0) infoPtr->cx=0;
3449 break;
3450 case SB_LINEDOWN:
3451 maxWidth=infoPtr->uTotalWidth-infoPtr->uVisibleWidth;
3452 if (infoPtr->cx == maxWidth) return FALSE;
3453 infoPtr->cx += infoPtr->uRealItemHeight; /*FIXME */
3454 if (infoPtr->cx > maxWidth)
3455 infoPtr->cx = maxWidth;
3456 break;
3457 case SB_PAGEUP:
3458 if (!infoPtr->cx) return FALSE;
3459 infoPtr->cx -= infoPtr->uVisibleWidth;
3460 if (infoPtr->cx < 0) infoPtr->cx=0;
3461 break;
3462 case SB_PAGEDOWN:
3463 maxWidth=infoPtr->uTotalWidth-infoPtr->uVisibleWidth;
3464 if (infoPtr->cx == maxWidth) return FALSE;
3465 infoPtr->cx += infoPtr->uVisibleWidth;
3466 if (infoPtr->cx > maxWidth)
3467 infoPtr->cx = maxWidth;
3468 break;
3469 case SB_THUMBTRACK:
3470 infoPtr->cx = HIWORD (wParam);
3471 break;
3475 TREEVIEW_QueueRefresh (hwnd);
3476 return TRUE;
3479 static LRESULT TREEVIEW_MouseWheel (HWND hwnd, WPARAM wParam, LPARAM lParam)
3482 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
3483 short gcWheelDelta = 0;
3484 UINT pulScrollLines = 3;
3486 SystemParametersInfoW(SPI_GETWHEELSCROLLLINES,0, &pulScrollLines, 0);
3488 gcWheelDelta -= (short) HIWORD(wParam);
3489 pulScrollLines *= (gcWheelDelta / WHEEL_DELTA);
3491 if (abs(gcWheelDelta) >= WHEEL_DELTA && pulScrollLines)
3493 int wheelDy = pulScrollLines * infoPtr->uRealItemHeight;
3494 int newDy = infoPtr->cy + wheelDy;
3495 int maxDy = infoPtr->uTotalHeight - infoPtr->uVisibleHeight;
3497 if (newDy > maxDy) newDy = maxDy;
3498 if (newDy < 0) newDy = 0;
3500 if (newDy == infoPtr->cy) return TRUE;
3502 TREEVIEW_VScroll(hwnd, MAKEWPARAM(SB_THUMBTRACK,newDy),0);
3504 return TRUE;
3507 static LRESULT
3508 TREEVIEW_KeyDown (HWND hwnd, WPARAM wParam, LPARAM lParam)
3510 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
3511 HTREEITEM hNewSelection = 0;
3512 INT scrollNeeds = -1;
3513 INT cyChangeNeeds = -1;
3514 INT prevSelect = (INT)infoPtr->selectedItem;
3516 TREEVIEW_ITEM *prevItem =
3517 (prevSelect != 0 ) ?
3518 TREEVIEW_ValidItem (infoPtr, (HTREEITEM)prevSelect) :
3519 NULL;
3521 TREEVIEW_ITEM *newItem = NULL;
3523 TRACE("%x %lx\n",wParam, lParam);
3525 if (prevSelect == 0)
3526 return FALSE;
3528 switch (wParam) {
3529 case VK_UP:
3530 newItem=TREEVIEW_GetPrevListItem (infoPtr, prevItem);
3532 if (!newItem)
3533 newItem=& infoPtr->items[(INT)infoPtr->TopRootItem];
3535 hNewSelection = newItem->hItem;
3537 if (! newItem->visible)
3538 scrollNeeds = SB_LINEUP;
3539 break;
3541 case VK_DOWN:
3542 newItem=TREEVIEW_GetNextListItem (infoPtr, prevItem);
3544 if (!newItem)
3545 newItem=prevItem;
3547 hNewSelection = newItem->hItem;
3549 if (! newItem->visible)
3550 scrollNeeds = SB_LINEDOWN;
3552 break;
3554 case VK_HOME:
3555 newItem = &infoPtr->items[(INT)infoPtr->TopRootItem];
3556 hNewSelection = newItem->hItem;
3557 cyChangeNeeds = 0;
3558 break;
3560 case VK_END:
3561 newItem = &infoPtr->items[(INT)infoPtr->TopRootItem];
3562 newItem = TREEVIEW_GetLastListItem (infoPtr, newItem);
3563 hNewSelection = newItem->hItem;
3565 if (! newItem->visible)
3566 cyChangeNeeds = infoPtr->uTotalHeight-infoPtr->uVisibleHeight;
3568 break;
3570 case VK_LEFT:
3571 if ( (prevItem->cChildren > 0) && (prevItem->state & TVIS_EXPANDED) )
3573 TREEVIEW_Expand(hwnd, TVE_COLLAPSE, prevSelect );
3575 else if ((INT)prevItem->parent)
3577 newItem = (& infoPtr->items[(INT)prevItem->parent]);
3578 if (! newItem->visible)
3579 /* FIXME find a way to make this item the first visible... */
3580 newItem = NULL;
3582 hNewSelection = newItem->hItem;
3585 break;
3587 case VK_RIGHT:
3588 if ( ( prevItem->cChildren > 0) ||
3589 ( prevItem->cChildren == I_CHILDRENCALLBACK))
3591 if (! (prevItem->state & TVIS_EXPANDED))
3592 TREEVIEW_Expand(hwnd, TVE_EXPAND, prevSelect );
3593 else
3595 newItem = (& infoPtr->items[(INT)prevItem->firstChild]);
3596 hNewSelection = newItem->hItem;
3600 break;
3602 case VK_ADD:
3603 if (! (prevItem->state & TVIS_EXPANDED))
3604 TREEVIEW_Expand(hwnd, TVE_EXPAND, prevSelect );
3605 break;
3607 case VK_SUBTRACT:
3608 if (prevItem->state & TVIS_EXPANDED)
3609 TREEVIEW_Expand(hwnd, TVE_COLLAPSE, prevSelect );
3610 break;
3612 case VK_PRIOR:
3614 newItem=TREEVIEW_GetListItem(
3615 infoPtr,
3616 prevItem,
3617 -1*(TREEVIEW_GetVisibleCount(hwnd,0,0)-3));
3618 if (!newItem)
3619 newItem=prevItem;
3621 hNewSelection = newItem->hItem;
3623 if (! newItem->visible)
3624 scrollNeeds = SB_PAGEUP;
3626 break;
3628 case VK_NEXT:
3629 newItem=TREEVIEW_GetListItem(
3630 infoPtr,
3631 prevItem,
3632 TREEVIEW_GetVisibleCount(hwnd,0,0)-3);
3634 if (!newItem)
3635 newItem=prevItem;
3637 hNewSelection = newItem->hItem;
3639 if (! newItem->visible)
3640 scrollNeeds = SB_PAGEDOWN;
3642 break;
3644 case VK_BACK:
3646 case VK_RETURN:
3648 default:
3649 FIXME("%x not implemented\n", wParam);
3650 break;
3653 if (hNewSelection)
3656 This works but does not send notification...
3658 prevItem->state &= ~TVIS_SELECTED;
3659 newItem->state |= TVIS_SELECTED;
3660 infoPtr->selectedItem = hNewSelection;
3661 TREEVIEW_QueueRefresh (hwnd);
3664 if ( TREEVIEW_DoSelectItem(
3665 hwnd,
3666 TVGN_CARET,
3667 (HTREEITEM)hNewSelection,
3668 TVC_BYKEYBOARD))
3670 /* If selection change is allowed for the new item, perform scrolling */
3671 if (scrollNeeds != -1)
3672 TREEVIEW_VScroll(hwnd, scrollNeeds, 0);
3674 if (cyChangeNeeds != -1)
3675 infoPtr->cy = cyChangeNeeds;
3677 /* FIXME: Something happen in the load the in the two weeks before
3678 april 1st 1999 which makes this SetFocus mandatory otherwise, the focus
3679 is lost... However the SetFocus should not be required...*/
3681 SetFocus(hwnd);
3685 return FALSE;
3689 static LRESULT
3690 TREEVIEW_GetScrollTime (HWND hwnd)
3692 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
3694 return infoPtr->uScrollTime;
3698 static LRESULT
3699 TREEVIEW_SetScrollTime (HWND hwnd, UINT uScrollTime)
3701 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
3702 UINT uOldScrollTime = infoPtr->uScrollTime;
3704 infoPtr->uScrollTime = min (uScrollTime, 100);
3706 return uOldScrollTime;
3710 static LRESULT WINAPI
3711 TREEVIEW_WindowProc (HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
3713 if (uMsg==WM_CREATE)
3714 return TREEVIEW_Create (hwnd, wParam, lParam);
3716 if (!TREEVIEW_GetInfoPtr(hwnd))
3717 return DefWindowProcA (hwnd, uMsg, wParam, lParam);
3719 switch (uMsg) {
3721 case TVM_INSERTITEMA:
3722 return TREEVIEW_InsertItemA (hwnd, wParam, lParam);
3724 case TVM_INSERTITEMW:
3725 return TREEVIEW_InsertItemW(hwnd,wParam,lParam);;
3727 case TVM_DELETEITEM:
3728 return TREEVIEW_DeleteItem (hwnd, wParam, lParam);
3730 case TVM_EXPAND:
3731 return TREEVIEW_Expand (hwnd, wParam, lParam);
3733 case TVM_GETITEMRECT:
3734 return TREEVIEW_GetItemRect (hwnd, wParam, lParam);
3736 case TVM_GETCOUNT:
3737 return TREEVIEW_GetCount (hwnd, wParam, lParam);
3739 case TVM_GETINDENT:
3740 return TREEVIEW_GetIndent (hwnd);
3742 case TVM_SETINDENT:
3743 return TREEVIEW_SetIndent (hwnd, wParam);
3745 case TVM_GETIMAGELIST:
3746 return TREEVIEW_GetImageList (hwnd, wParam, lParam);
3748 case TVM_SETIMAGELIST:
3749 return TREEVIEW_SetImageList (hwnd, wParam, lParam);
3751 case TVM_GETNEXTITEM:
3752 return TREEVIEW_GetNextItem (hwnd, wParam, lParam);
3754 case TVM_SELECTITEM:
3755 return TREEVIEW_SelectItem (hwnd, wParam, lParam);
3757 case TVM_GETITEMA:
3758 return TREEVIEW_GetItemA (hwnd, wParam, lParam);
3760 case TVM_GETITEMW:
3761 FIXME("Unimplemented msg TVM_GETITEMW\n");
3762 return 0;
3764 case TVM_SETITEMA:
3765 return TREEVIEW_SetItemA (hwnd, wParam, lParam);
3767 case TVM_SETITEMW:
3768 FIXME("Unimplemented msg TVM_SETITEMW\n");
3769 return 0;
3771 case TVM_EDITLABELA:
3772 FIXME("Unimplemented msg TVM_EDITLABELA \n");
3773 return 0;
3775 case TVM_EDITLABELW:
3776 FIXME("Unimplemented msg TVM_EDITLABELW \n");
3777 return 0;
3779 case TVM_GETEDITCONTROL:
3780 return TREEVIEW_GetEditControl (hwnd);
3782 case TVM_GETVISIBLECOUNT:
3783 return TREEVIEW_GetVisibleCount (hwnd, wParam, lParam);
3785 case TVM_HITTEST:
3786 return TREEVIEW_HitTest (hwnd, lParam);
3788 case TVM_CREATEDRAGIMAGE:
3789 return TREEVIEW_CreateDragImage (hwnd, wParam, lParam);
3791 case TVM_SORTCHILDREN:
3792 return TREEVIEW_SortChildren (hwnd, wParam, lParam);
3794 case TVM_ENSUREVISIBLE:
3795 FIXME("Unimplemented msg TVM_ENSUREVISIBLE\n");
3796 return 0;
3798 case TVM_SORTCHILDRENCB:
3799 return TREEVIEW_SortChildrenCB(hwnd, wParam, lParam);
3801 case TVM_ENDEDITLABELNOW:
3802 return TREEVIEW_EndEditLabelNow (hwnd, wParam, lParam);
3804 case TVM_GETISEARCHSTRINGA:
3805 FIXME("Unimplemented msg TVM_GETISEARCHSTRINGA\n");
3806 return 0;
3808 case TVM_GETISEARCHSTRINGW:
3809 FIXME("Unimplemented msg TVM_GETISEARCHSTRINGW\n");
3810 return 0;
3812 case TVM_GETTOOLTIPS:
3813 return TREEVIEW_GetToolTips (hwnd);
3815 case TVM_SETTOOLTIPS:
3816 return TREEVIEW_SetToolTips (hwnd, wParam);
3818 case TVM_SETINSERTMARK:
3819 return TREEVIEW_SetInsertMark (hwnd,wParam, lParam);
3821 case TVM_SETITEMHEIGHT:
3822 return TREEVIEW_SetItemHeight (hwnd, wParam);
3824 case TVM_GETITEMHEIGHT:
3825 return TREEVIEW_GetItemHeight (hwnd);
3827 case TVM_SETBKCOLOR:
3828 return TREEVIEW_SetBkColor (hwnd, wParam, lParam);
3830 case TVM_SETTEXTCOLOR:
3831 return TREEVIEW_SetTextColor (hwnd, wParam, lParam);
3833 case TVM_GETBKCOLOR:
3834 return TREEVIEW_GetBkColor (hwnd);
3836 case TVM_GETTEXTCOLOR:
3837 return TREEVIEW_GetTextColor (hwnd);
3839 case TVM_SETSCROLLTIME:
3840 return TREEVIEW_SetScrollTime (hwnd, (UINT)wParam);
3842 case TVM_GETSCROLLTIME:
3843 return TREEVIEW_GetScrollTime (hwnd);
3845 case TVM_GETITEMSTATE:
3846 return TREEVIEW_GetItemState (hwnd,wParam, lParam);
3848 case TVM_GETLINECOLOR:
3849 return TREEVIEW_GetLineColor (hwnd,wParam, lParam);
3851 case TVM_SETLINECOLOR:
3852 return TREEVIEW_SetLineColor (hwnd,wParam, lParam);
3854 case TVM_SETINSERTMARKCOLOR:
3855 return TREEVIEW_SetInsertMarkColor (hwnd,wParam, lParam);
3857 case TVM_GETINSERTMARKCOLOR:
3858 return TREEVIEW_GetInsertMarkColor (hwnd,wParam, lParam);
3860 case TVM_SETUNICODEFORMAT:
3861 FIXME("Unimplemented msg TVM_SETUNICODEFORMAT\n");
3862 return 0;
3864 case TVM_GETUNICODEFORMAT:
3865 FIXME("Unimplemented msg TVM_GETUNICODEFORMAT\n");
3866 return 0;
3868 case WM_COMMAND:
3869 return TREEVIEW_Command (hwnd, wParam, lParam);
3871 case WM_DESTROY:
3872 return TREEVIEW_Destroy (hwnd);
3874 /* case WM_ENABLE: */
3876 case WM_ERASEBKGND:
3877 return TREEVIEW_EraseBackground (hwnd, wParam, lParam);
3879 case WM_GETDLGCODE:
3880 return DLGC_WANTARROWS | DLGC_WANTCHARS;
3882 case WM_PAINT:
3883 return TREEVIEW_Paint (hwnd, wParam, lParam);
3885 case WM_GETFONT:
3886 return TREEVIEW_GetFont (hwnd, wParam, lParam);
3888 case WM_SETFONT:
3889 return TREEVIEW_SetFont (hwnd, wParam, lParam);
3891 case WM_KEYDOWN:
3892 return TREEVIEW_KeyDown (hwnd, wParam, lParam);
3894 case WM_SETFOCUS:
3895 return TREEVIEW_SetFocus (hwnd, wParam, lParam);
3897 case WM_KILLFOCUS:
3898 return TREEVIEW_KillFocus (hwnd, wParam, lParam);
3900 case WM_LBUTTONDOWN:
3901 return TREEVIEW_LButtonDown (hwnd, wParam, lParam);
3903 case WM_LBUTTONUP:
3904 return TREEVIEW_LButtonUp (hwnd, wParam, lParam);
3906 case WM_LBUTTONDBLCLK:
3907 return TREEVIEW_LButtonDoubleClick (hwnd, wParam, lParam);
3909 case WM_RBUTTONDOWN:
3910 return TREEVIEW_RButtonDown (hwnd, wParam, lParam);
3912 case WM_RBUTTONUP:
3913 return TREEVIEW_RButtonUp (hwnd, wParam, lParam);
3915 case WM_MOUSEMOVE:
3916 return TREEVIEW_MouseMove (hwnd, wParam, lParam);
3918 case WM_STYLECHANGED:
3919 return TREEVIEW_StyleChanged (hwnd, wParam, lParam);
3921 /* case WM_SYSCOLORCHANGE: */
3922 /* case WM_SETREDRAW: */
3924 case WM_TIMER:
3925 return TREEVIEW_HandleTimer (hwnd, wParam, lParam);
3927 case WM_SIZE:
3928 return TREEVIEW_Size (hwnd, wParam,lParam);
3930 case WM_HSCROLL:
3931 return TREEVIEW_HScroll (hwnd, wParam, lParam);
3932 case WM_VSCROLL:
3933 return TREEVIEW_VScroll (hwnd, wParam, lParam);
3935 case WM_MOUSEWHEEL:
3936 if (wParam & (MK_SHIFT | MK_CONTROL))
3937 return DefWindowProcA( hwnd, uMsg, wParam, lParam );
3938 return TREEVIEW_MouseWheel (hwnd, wParam, lParam);
3940 case WM_DRAWITEM:
3941 TRACE ("drawItem\n");
3942 return DefWindowProcA (hwnd, uMsg, wParam, lParam);
3944 default:
3945 if (uMsg >= WM_USER)
3946 FIXME("Unknown msg %04x wp=%08x lp=%08lx\n",
3947 uMsg, wParam, lParam);
3948 return DefWindowProcA (hwnd, uMsg, wParam, lParam);
3950 return 0;
3954 VOID
3955 TREEVIEW_Register (void)
3957 WNDCLASSA wndClass;
3959 TRACE("\n");
3961 ZeroMemory (&wndClass, sizeof(WNDCLASSA));
3962 wndClass.style = CS_GLOBALCLASS | CS_DBLCLKS;
3963 wndClass.lpfnWndProc = (WNDPROC)TREEVIEW_WindowProc;
3964 wndClass.cbClsExtra = 0;
3965 wndClass.cbWndExtra = sizeof(TREEVIEW_INFO *);
3966 wndClass.hCursor = LoadCursorA (0, IDC_ARROWA);
3967 wndClass.hbrBackground = 0;
3968 wndClass.lpszClassName = WC_TREEVIEWA;
3970 RegisterClassA (&wndClass);
3974 VOID
3975 TREEVIEW_Unregister (void)
3977 UnregisterClassA (WC_TREEVIEWA, (HINSTANCE)NULL);