DOSFS_ToDosFCBFormat: fail if extension longer than 3 characters.
[wine/gsoc-2012-control.git] / dlls / comctl32 / treeview.c
blob524d77bf00caf1192f1c4e306b7f8ba576854622
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
370 (hwnd, TVN_DELETEITEMA, 0, killItem->hItem, 0);
373 if (infoPtr->uNumPtrsAlloced) {
374 COMCTL32_Free (infoPtr->items);
375 COMCTL32_Free (infoPtr->freeList);
376 infoPtr->uNumItems=0;
377 infoPtr->uNumPtrsAlloced=0;
378 infoPtr->uMaxHandle=0;
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 ImageList_Draw ( *himlp, imageIndex, hdc, xpos-2, r.top+1, ILD_NORMAL);
761 ImageList_GetIconSize (*himlp, &cx, &cy);
762 wineItem->bitmap.left=xpos-2;
763 wineItem->bitmap.right=xpos-2+cx;
764 wineItem->bitmap.top=r.top+1;
765 wineItem->bitmap.bottom=r.top+1+cy;
766 xpos+=cx;
772 * Display the text associated with this item
774 r.left=xpos;
775 if ((wineItem->mask & TVIF_TEXT) && (wineItem->pszText))
777 COLORREF oldBkColor = 0;
778 COLORREF oldTextColor = 0;
779 INT oldBkMode;
781 r.left += 3;
782 r.right -= 3;
784 wineItem->text.left = r.left;
785 wineItem->text.right = r.right;
786 wineItem->text.top = r.top;
787 wineItem->text.bottom= r.bottom;
789 if (wineItem->pszText== LPSTR_TEXTCALLBACKA) {
790 TRACE("LPSTR_TEXTCALLBACK\n");
791 TREEVIEW_SendDispInfoNotify (hwnd, wineItem, TVN_GETDISPINFOA, TVIF_TEXT);
794 /* Yep, there are some things that need to be straightened out here.
795 Removing the comments around the setTextColor does not give the right
796 results. Dito FillRect.
800 /* GetTextExtentPoint32A (hdc, wineItem->pszText,
801 strlen (wineItem->pszText), &size); */
803 /* FillRect ( hdc, &wineItem->text, GetSysColorBrush (infoPtr->clrBk));
807 if (!(cditem & CDRF_NOTIFYPOSTPAINT) &&
808 (wineItem->state & (TVIS_SELECTED | TVIS_DROPHILITED)) ) {
809 oldBkMode = SetBkMode (hdc, OPAQUE);
810 oldBkColor = SetBkColor (hdc, GetSysColor( COLOR_HIGHLIGHT));
811 oldTextColor = SetTextColor(hdc, GetSysColor( COLOR_HIGHLIGHTTEXT));
812 } else {
813 oldBkMode = SetBkMode (hdc, TRANSPARENT);
814 oldBkColor = SetBkColor (hdc, infoPtr->clrBk);
815 /* oldTextColor = SetTextColor(hdc, infoPtr->clrText); */
820 /* Draw it */
821 DrawTextA ( hdc,
822 wineItem->pszText,
823 lstrlenA(wineItem->pszText),
824 &wineItem->text,
825 uTextJustify | DT_VCENTER | DT_SINGLELINE );
827 /* Obtain the text coordinate */
828 DrawTextA (
829 hdc,
830 wineItem->pszText,
831 lstrlenA(wineItem->pszText),
832 &wineItem->text,
833 uTextJustify | DT_VCENTER | DT_SINGLELINE | DT_CALCRECT);
835 /* Restore the hdc state */
836 SetTextColor( hdc, oldTextColor);
838 if (oldBkMode != TRANSPARENT)
839 SetBkMode(hdc, oldBkMode);
840 if (wineItem->state & (TVIS_SELECTED | TVIS_DROPHILITED))
841 SetBkColor (hdc, oldBkColor);
843 /* Draw the box arround the selected item */
844 if (wineItem->state & TVIS_SELECTED )
846 HPEN hNewPen = CreatePen(PS_DOT, 0, GetSysColor(COLOR_WINDOWTEXT) );
847 HPEN hOldPen = SelectObject( hdc, hNewPen );
848 POINT points[4];
850 points[0].x = wineItem->text.left-1;
851 points[0].y = wineItem->text.top+1;
852 points[1].x = wineItem->text.right;
853 points[1].y = wineItem->text.top+1;
854 points[2].x = wineItem->text.right;
855 points[2].y = wineItem->text.bottom;
856 points[3].x = wineItem->text.left-1;
857 points[3].y = wineItem->text.bottom;
859 Polyline (hdc,points,4);
861 DeleteObject(hNewPen);
862 SelectObject(hdc, hOldPen);
866 /* Draw insertion mark if necessary */
868 if (infoPtr->insertMarkItem)
869 TRACE ("item:%d,mark:%d\n", (int)wineItem->hItem,
870 (int) infoPtr->insertMarkItem);
871 if (wineItem->hItem==infoPtr->insertMarkItem) {
872 HPEN hNewPen, hOldPen;
873 int offset;
875 hNewPen = CreatePen(PS_SOLID, 2, infoPtr->clrInsertMark);
876 hOldPen = SelectObject( hdc, hNewPen );
878 if (infoPtr->insertBeforeorAfter)
879 offset=wineItem->text.top+1;
880 else
881 offset=wineItem->text.bottom-1;
883 MoveToEx (hdc, wineItem->text.left, offset-3, NULL);
884 LineTo (hdc, wineItem->text.left, offset+3);
886 MoveToEx (hdc, wineItem->text.left, offset, NULL);
887 LineTo (hdc, r.right-2, offset);
889 MoveToEx (hdc, r.right-2, offset+3, NULL);
890 LineTo (hdc, r.right-2, offset-3);
892 DeleteObject(hNewPen);
894 SelectObject(hdc, hOldPen);
897 if (cditem & CDRF_NOTIFYPOSTPAINT) {
898 cditem=TREEVIEW_SendCustomDrawItemNotify
899 (hwnd, hdc, wineItem, CDDS_ITEMPOSTPAINT);
900 TRACE("postpaint:cditem-app returns 0x%x\n",cditem);
903 SelectObject (hdc, hOldFont);
906 static LRESULT
907 TREEVIEW_GetItemRect (HWND hwnd, WPARAM wParam, LPARAM lParam)
909 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
910 TREEVIEW_ITEM *wineItem;
911 HTREEITEM *iItem;
912 LPRECT lpRect = (LPRECT)lParam;
913 HDC hdc;
915 TRACE("\n");
917 * validate parameters
919 if (lpRect == NULL)
920 return FALSE;
922 if (infoPtr->Timer & TV_REFRESH_TIMER_SET) {
923 hdc = GetDC (hwnd);
924 TREEVIEW_Refresh (hwnd, hdc); /* we want a rect for the current view */
925 ReleaseDC(hwnd,hdc);
930 * retrieve the item ptr
932 iItem = (HTREEITEM *) lParam;
933 wineItem = TREEVIEW_ValidItem (infoPtr, *iItem);
934 if ((!wineItem) || (!wineItem->visible))
935 return FALSE;
938 * If wParam is TRUE return the text size otherwise return
939 * the whole item size
941 if ((INT) wParam) {
942 lpRect->left = wineItem->text.left;
943 lpRect->right = wineItem->text.right;
944 lpRect->bottom = wineItem->text.bottom;
945 lpRect->top = wineItem->text.top;
946 } else {
947 lpRect->left = wineItem->rect.left;
948 lpRect->right = wineItem->rect.right;
949 lpRect->bottom = wineItem->rect.bottom;
950 lpRect->top = wineItem->rect.top;
953 TRACE("[L:%d R:%d T:%d B:%d]\n",
954 lpRect->left,lpRect->right,
955 lpRect->top,lpRect->bottom);
957 return TRUE;
960 static LRESULT
961 TREEVIEW_GetVisibleCount (HWND hwnd, WPARAM wParam, LPARAM lParam)
964 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
966 return (LRESULT) infoPtr->uVisibleHeight / infoPtr->uRealItemHeight;
971 static LRESULT
972 TREEVIEW_SetItemA (HWND hwnd, WPARAM wParam, LPARAM lParam)
974 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
975 TREEVIEW_ITEM *wineItem;
976 TVITEMEXA *tvItem;
977 INT iItem,len;
979 tvItem=(LPTVITEMEXA) lParam;
980 iItem=(INT)tvItem->hItem;
981 TRACE("item %d,mask %x\n",iItem,tvItem->mask);
983 wineItem = TREEVIEW_ValidItem (infoPtr, (HTREEITEM)iItem);
984 if (!wineItem) return FALSE;
986 if (tvItem->mask & TVIF_CHILDREN) {
987 wineItem->cChildren=tvItem->cChildren;
990 if (tvItem->mask & TVIF_IMAGE) {
991 wineItem->iImage=tvItem->iImage;
994 if (tvItem->mask & TVIF_INTEGRAL) {
995 wineItem->iIntegral=tvItem->iIntegral;
998 if (tvItem->mask & TVIF_PARAM) {
999 wineItem->lParam=tvItem->lParam;
1002 if (tvItem->mask & TVIF_SELECTEDIMAGE) {
1003 wineItem->iSelectedImage=tvItem->iSelectedImage;
1006 if (tvItem->mask & TVIF_STATE) {
1007 TRACE ("prevstate,state,mask:%x,%x,%x\n",wineItem->state,tvItem->state,
1008 tvItem->stateMask);
1009 wineItem->state&= ~tvItem->stateMask;
1010 wineItem->state|= (tvItem->state & tvItem->stateMask);
1011 wineItem->stateMask|= tvItem->stateMask;
1014 if (tvItem->mask & TVIF_TEXT) {
1015 if (tvItem->pszText!=LPSTR_TEXTCALLBACKA) {
1016 len=lstrlenA (tvItem->pszText);
1017 if (len>wineItem->cchTextMax)
1018 wineItem->pszText= COMCTL32_ReAlloc (wineItem->pszText, len+1);
1019 lstrcpynA (wineItem->pszText, tvItem->pszText,len);
1020 } else {
1021 if (wineItem->cchTextMax) {
1022 COMCTL32_Free (wineItem->pszText);
1023 wineItem->cchTextMax=0;
1025 wineItem->pszText=LPSTR_TEXTCALLBACKA;
1029 wineItem->mask |= tvItem->mask;
1031 return TRUE;
1034 static LRESULT
1035 TREEVIEW_GetItemState (HWND hwnd, WPARAM wParam, LPARAM lParam)
1038 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
1039 TREEVIEW_ITEM *wineItem;
1041 TRACE("\n");
1042 wineItem = TREEVIEW_ValidItem (infoPtr, (HTREEITEM)wParam);
1043 if (!wineItem) return 0;
1045 return (wineItem->state & lParam);
1051 static void
1052 TREEVIEW_Refresh (HWND hwnd, HDC hdc)
1054 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
1055 TEXTMETRICA tm;
1056 HBRUSH hbrBk;
1057 RECT rect;
1058 INT iItem, indent, x, y, cx, height, itemHeight;
1059 INT viewtop,viewbottom,viewleft,viewright;
1060 TREEVIEW_ITEM *wineItem, *prevItem;
1062 TRACE("\n");
1065 if (infoPtr->Timer & TV_REFRESH_TIMER_SET) {
1066 KillTimer (hwnd, TV_REFRESH_TIMER);
1067 infoPtr->Timer &= ~TV_REFRESH_TIMER_SET;
1071 GetClientRect (hwnd, &rect);
1072 if ((rect.left-rect.right ==0) || (rect.top-rect.bottom==0)) return;
1074 infoPtr->cdmode=TREEVIEW_SendCustomDrawNotify
1075 (hwnd, CDDS_PREPAINT, hdc, rect);
1077 if (infoPtr->cdmode==CDRF_SKIPDEFAULT) return;
1079 infoPtr->uVisibleHeight= rect.bottom-rect.top;
1080 infoPtr->uVisibleWidth= rect.right-rect.left;
1082 viewtop=infoPtr->cy;
1083 viewbottom=infoPtr->cy + rect.bottom-rect.top;
1084 viewleft=infoPtr->cx;
1085 viewright=infoPtr->cx + rect.right-rect.left;
1087 /* draw background */
1089 hbrBk = CreateSolidBrush (infoPtr->clrBk);
1090 FillRect(hdc, &rect, hbrBk);
1091 DeleteObject(hbrBk);
1093 iItem=(INT)infoPtr->TopRootItem;
1094 infoPtr->firstVisible=0;
1095 wineItem=NULL;
1096 indent=0;
1097 x=y=0;
1098 TRACE("[%d %d %d %d]\n",viewtop,viewbottom,viewleft,viewright);
1100 while (iItem) {
1101 prevItem=wineItem;
1102 wineItem= & infoPtr->items[iItem];
1103 wineItem->iLevel=indent;
1105 ImageList_GetIconSize (infoPtr->himlNormal, &cx, &itemHeight);
1106 if (infoPtr->uItemHeight>itemHeight)
1107 itemHeight=infoPtr->uItemHeight;
1109 GetTextMetricsA (hdc, &tm);
1110 if ((tm.tmHeight + tm.tmExternalLeading) > itemHeight)
1111 itemHeight=tm.tmHeight + tm.tmExternalLeading;
1113 infoPtr->uRealItemHeight=itemHeight;
1116 /* FIXME: remove this in later stage */
1118 if (wineItem->pszText!=LPSTR_TEXTCALLBACK32A)
1119 TRACE (treeview, "%d %d [%d %d %d %d] (%s)\n",y,x,
1120 wineItem->rect.top, wineItem->rect.bottom,
1121 wineItem->rect.left, wineItem->rect.right,
1122 wineItem->pszText);
1123 else
1124 TRACE (treeview, "%d [%d %d %d %d] (CALLBACK)\n",
1125 wineItem->hItem,
1126 wineItem->rect.top, wineItem->rect.bottom,
1127 wineItem->rect.left, wineItem->rect.right);
1130 height=itemHeight * wineItem->iIntegral +1;
1131 if ((y >= viewtop) && (y <= viewbottom) &&
1132 (x >= viewleft ) && (x <= viewright)) {
1133 wineItem->visible = TRUE;
1134 wineItem->rect.top = y - infoPtr->cy + rect.top;
1135 wineItem->rect.bottom = wineItem->rect.top + height ;
1136 wineItem->rect.left = x - infoPtr->cx + rect.left;
1137 wineItem->rect.right = rect.right;
1138 if (!infoPtr->firstVisible)
1139 infoPtr->firstVisible=wineItem->hItem;
1140 TREEVIEW_DrawItem (hwnd, hdc, wineItem);
1142 else {
1143 wineItem->visible = FALSE;
1144 wineItem->rect.left = wineItem->rect.top = 0;
1145 wineItem->rect.right= wineItem->rect.bottom = 0;
1146 wineItem->text.left = wineItem->text.top = 0;
1147 wineItem->text.right= wineItem->text.bottom = 0;
1150 /* look up next item */
1152 if ((wineItem->firstChild) && (wineItem->state & TVIS_EXPANDED)) {
1153 iItem=(INT)wineItem->firstChild;
1154 indent++;
1155 x+=infoPtr->uIndent;
1156 if (x>infoPtr->uTotalWidth)
1157 infoPtr->uTotalWidth=x;
1159 else {
1160 iItem=(INT)wineItem->sibling;
1161 while ((!iItem) && (indent>0)) {
1162 indent--;
1163 x-=infoPtr->uIndent;
1164 prevItem=wineItem;
1165 wineItem=&infoPtr->items[(INT)wineItem->parent];
1166 iItem=(INT)wineItem->sibling;
1169 y +=height;
1170 } /* while */
1172 /* FIXME: infoPtr->uTotalWidth should also take item label into account */
1173 /* FIXME: or should query item sizes (ie check CDRF_NEWFONT) */
1175 infoPtr->uTotalHeight=y;
1176 if (y >= (viewbottom-viewtop)) {
1177 if (!(infoPtr->uInternalStatus & TV_VSCROLL))
1178 ShowScrollBar (hwnd, SB_VERT, TRUE);
1179 infoPtr->uInternalStatus |=TV_VSCROLL;
1180 SetScrollRange (hwnd, SB_VERT, 0,
1181 y - infoPtr->uVisibleHeight, FALSE);
1182 SetScrollPos (hwnd, SB_VERT, infoPtr->cy, TRUE);
1184 else {
1185 if (infoPtr->uInternalStatus & TV_VSCROLL)
1186 ShowScrollBar (hwnd, SB_VERT, FALSE);
1187 infoPtr->uInternalStatus &= ~TV_VSCROLL;
1191 if (infoPtr->cdmode & CDRF_NOTIFYPOSTPAINT)
1192 infoPtr->cdmode=TREEVIEW_SendCustomDrawNotify
1193 (hwnd, CDDS_POSTPAINT, hdc, rect);
1195 TRACE("done\n");
1199 static LRESULT
1200 TREEVIEW_HandleTimer (HWND hwnd, WPARAM wParam, LPARAM lParam)
1202 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
1204 TRACE(" %d\n",wParam);
1206 switch (wParam) {
1207 case TV_REFRESH_TIMER:
1208 KillTimer (hwnd, TV_REFRESH_TIMER);
1209 infoPtr->Timer &= ~TV_REFRESH_TIMER_SET;
1210 InvalidateRect(hwnd, NULL, FALSE);
1211 return 0;
1212 case TV_EDIT_TIMER:
1213 KillTimer (hwnd, TV_EDIT_TIMER);
1214 infoPtr->Timer &= ~TV_EDIT_TIMER_SET;
1215 return 0;
1216 default:
1217 ERR("got unknown timer\n");
1220 return 1;
1224 static void
1225 TREEVIEW_QueueRefresh (HWND hwnd)
1228 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
1230 TRACE("\n");
1231 if (infoPtr->Timer & TV_REFRESH_TIMER_SET) {
1232 KillTimer (hwnd, TV_REFRESH_TIMER);
1235 SetTimer (hwnd, TV_REFRESH_TIMER, TV_REFRESH_DELAY, 0);
1236 infoPtr->Timer|=TV_REFRESH_TIMER_SET;
1241 static LRESULT
1242 TREEVIEW_GetItemA (HWND hwnd, WPARAM wParam, LPARAM lParam)
1244 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
1245 LPTVITEMEXA tvItem;
1246 TREEVIEW_ITEM *wineItem;
1247 INT iItem;
1249 tvItem=(LPTVITEMEXA) lParam;
1250 iItem=(INT)tvItem->hItem;
1252 wineItem = TREEVIEW_ValidItem (infoPtr, (HTREEITEM)iItem);
1253 if (!wineItem) return FALSE;
1255 if (tvItem->mask & TVIF_CHILDREN) {
1256 if (TVIF_CHILDREN==I_CHILDRENCALLBACK)
1257 FIXME("I_CHILDRENCALLBACK not supported\n");
1258 tvItem->cChildren=wineItem->cChildren;
1261 if (tvItem->mask & TVIF_HANDLE) {
1262 tvItem->hItem=wineItem->hItem;
1265 if (tvItem->mask & TVIF_IMAGE) {
1266 tvItem->iImage=wineItem->iImage;
1269 if (tvItem->mask & TVIF_INTEGRAL) {
1270 tvItem->iIntegral=wineItem->iIntegral;
1273 /* undocumented: windows ignores TVIF_PARAM and
1274 * always sets lParam
1276 tvItem->lParam=wineItem->lParam;
1278 if (tvItem->mask & TVIF_SELECTEDIMAGE) {
1279 tvItem->iSelectedImage=wineItem->iSelectedImage;
1282 if (tvItem->mask & TVIF_STATE) {
1283 tvItem->state=wineItem->state & tvItem->stateMask;
1286 if (tvItem->mask & TVIF_TEXT) {
1287 if (wineItem->pszText == LPSTR_TEXTCALLBACKA) {
1288 tvItem->pszText = LPSTR_TEXTCALLBACKA; /* FIXME:send notification? */
1289 ERR(" GetItem called with LPSTR_TEXTCALLBACK\n");
1291 else if (wineItem->pszText) {
1292 lstrcpynA (tvItem->pszText, wineItem->pszText, tvItem->cchTextMax);
1296 TRACE("item %d<%p>, txt %p, img %p, action %x\n",
1297 iItem, tvItem, tvItem->pszText, &tvItem->iImage, tvItem->mask);
1299 return TRUE;
1304 /* FIXME: check implementation of TVGN_NEXT/TVGN_NEXTVISIBLE */
1306 static LRESULT
1307 TREEVIEW_GetNextItem (HWND hwnd, WPARAM wParam, LPARAM lParam)
1310 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
1311 TREEVIEW_ITEM *wineItem, *returnItem;
1312 INT iItem, retval, flag;
1313 HDC hdc;
1315 flag = (INT) wParam;
1316 iItem = (INT) lParam;
1317 retval=0;
1318 switch (flag) {
1319 case TVGN_ROOT: retval=(INT)infoPtr->TopRootItem;
1320 break;
1321 case TVGN_CARET:retval=(INT)infoPtr->selectedItem;
1322 break;
1323 case TVGN_FIRSTVISIBLE: /* FIXME:we should only recalculate, not redraw */
1324 hdc = GetDC (hwnd);
1325 TREEVIEW_Refresh (hwnd, hdc);
1326 ReleaseDC(hwnd,hdc);
1327 retval=(INT)infoPtr->firstVisible;
1328 break;
1329 case TVGN_DROPHILITE:
1330 retval=(INT)infoPtr->dropItem;
1331 break;
1333 if (retval) {
1334 TRACE("flags:%x, returns %u\n", flag, retval);
1335 return retval;
1338 wineItem = TREEVIEW_ValidItem (infoPtr, (HTREEITEM)iItem);
1339 returnItem = NULL;
1340 if (!wineItem) return FALSE;
1342 switch (flag) {
1343 case TVGN_NEXT: retval=(INT)wineItem->sibling;
1344 break;
1345 case TVGN_PREVIOUS:
1346 retval=(INT)wineItem->upsibling;
1347 break;
1348 case TVGN_PARENT:
1349 retval=(INT)wineItem->parent;
1350 break;
1351 case TVGN_CHILD:
1352 retval=(INT)wineItem->firstChild;
1353 break;
1354 case TVGN_LASTVISIBLE:
1355 returnItem=TREEVIEW_GetLastListItem (infoPtr,wineItem);
1356 break;
1357 case TVGN_NEXTVISIBLE:
1358 returnItem=TREEVIEW_GetNextListItem (infoPtr,wineItem);
1359 break;
1360 case TVGN_PREVIOUSVISIBLE:
1361 returnItem=TREEVIEW_GetPrevListItem (infoPtr, wineItem);
1362 break;
1363 default: FIXME("Unknown msg %x,item %x\n", flag,iItem);
1364 break;
1367 if (returnItem) {
1368 TRACE("flags:%x, item %d;returns %d\n", flag, iItem,
1369 (INT)returnItem->hItem);
1370 return (INT)returnItem->hItem;
1373 TRACE("flags:%x, item %d;returns %d\n", flag, iItem,retval);
1374 return retval;
1378 static LRESULT
1379 TREEVIEW_GetCount (HWND hwnd, WPARAM wParam, LPARAM lParam)
1381 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
1383 TRACE(" %d\n",infoPtr->uNumItems);
1384 return (LRESULT) infoPtr->uNumItems;
1387 /***************************************************************************
1388 * This method does the chaining of the insertion of a treeview item
1389 * before an item.
1390 * If parent is NULL, we're inserting at the root of the list.
1392 static void TREEVIEW_InsertBefore(
1393 TREEVIEW_INFO *infoPtr,
1394 TREEVIEW_ITEM *newItem,
1395 TREEVIEW_ITEM *sibling,
1396 TREEVIEW_ITEM *parent)
1398 HTREEITEM siblingHandle = 0;
1399 HTREEITEM upSiblingHandle = 0;
1400 TREEVIEW_ITEM *upSibling = NULL;
1402 if (newItem == NULL)
1403 ERR("NULL newItem, impossible condition\n");
1405 if (sibling != NULL) /* Insert before this sibling for this parent */
1407 /* Store the new item sibling up sibling and sibling tem handle */
1408 siblingHandle = sibling->hItem;
1409 upSiblingHandle = sibling->upsibling;
1410 /* As well as a pointer to the upsibling sibling object */
1411 if ( (INT)sibling->upsibling != 0 )
1412 upSibling = &infoPtr->items[(INT)sibling->upsibling];
1414 /* Adjust the sibling pointer */
1415 sibling->upsibling = newItem->hItem;
1417 /* Adjust the new item pointers */
1418 newItem->upsibling = upSiblingHandle;
1419 newItem->sibling = siblingHandle;
1421 /* Adjust the up sibling pointer */
1422 if ( upSibling != NULL )
1423 upSibling->sibling = newItem->hItem;
1424 else
1425 /* this item is the first child of this parent, adjust parent pointers */
1426 if (parent)
1427 parent->firstChild = newItem->hItem;
1428 else
1429 infoPtr->TopRootItem= newItem->hItem;
1431 else /* Insert as first child of this parent */
1432 if (parent)
1433 parent->firstChild = newItem->hItem;
1436 /***************************************************************************
1437 * This method does the chaining of the insertion of a treeview item
1438 * after an item.
1439 * If parent is NULL, we're inserting at the root of the list.
1441 static void TREEVIEW_InsertAfter(
1442 TREEVIEW_INFO *infoPtr,
1443 TREEVIEW_ITEM *newItem,
1444 TREEVIEW_ITEM *upSibling,
1445 TREEVIEW_ITEM *parent)
1447 HTREEITEM upSiblingHandle = 0;
1448 HTREEITEM siblingHandle = 0;
1449 TREEVIEW_ITEM *sibling = NULL;
1452 if (newItem == NULL)
1453 ERR("NULL newItem, impossible condition\n");
1455 if (upSibling != NULL) /* Insert after this upsibling for this parent */
1457 /* Store the new item up sibling and sibling item handle */
1458 upSiblingHandle = upSibling->hItem;
1459 siblingHandle = upSibling->sibling;
1460 /* As well as a pointer to the upsibling sibling object */
1461 if ( (INT)upSibling->sibling != 0 )
1462 sibling = &infoPtr->items[(INT)upSibling->sibling];
1464 /* Adjust the up sibling pointer */
1465 upSibling->sibling = newItem->hItem;
1467 /* Adjust the new item pointers */
1468 newItem->upsibling = upSiblingHandle;
1469 newItem->sibling = siblingHandle;
1471 /* Adjust the sibling pointer */
1472 if ( sibling != NULL )
1473 sibling->upsibling = newItem->hItem;
1475 else
1476 newItem is the last of the level, nothing else to do
1479 else /* Insert as first child of this parent */
1480 if (parent)
1481 parent->firstChild = newItem->hItem;
1484 /***************************************************************************
1485 * Forward the DPA local callback to the treeview owner callback
1487 static INT WINAPI TREEVIEW_CallBackCompare(
1488 LPVOID first,
1489 LPVOID second,
1490 LPARAM tvInfoPtr)
1492 /* Forward the call to the client define callback */
1493 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr((HWND)tvInfoPtr);
1494 return (infoPtr->pCallBackSort->lpfnCompare)(
1495 ((TREEVIEW_ITEM*)first)->lParam,
1496 ((TREEVIEW_ITEM*)second)->lParam,
1497 infoPtr->pCallBackSort->lParam);
1500 /***************************************************************************
1501 * Treeview native sort routine: sort on item text.
1503 static INT WINAPI TREEVIEW_SortOnName (
1504 LPVOID first,
1505 LPVOID second,
1506 LPARAM tvInfoPtr)
1508 HWND hwnd=(HWND) tvInfoPtr;
1509 char *txt1, *txt2;
1510 TREEVIEW_ITEM *item;
1513 item=(TREEVIEW_ITEM *) first;
1514 if (item->pszText==LPSTR_TEXTCALLBACKA) {
1515 TREEVIEW_SendDispInfoNotify (hwnd, item, TVN_GETDISPINFOA, TVIF_TEXT);
1517 txt1=item->pszText;
1519 item=(TREEVIEW_ITEM *) second;
1520 if (item->pszText==LPSTR_TEXTCALLBACKA) {
1521 TREEVIEW_SendDispInfoNotify (hwnd, item, TVN_GETDISPINFOA, TVIF_TEXT);
1523 txt2=item->pszText;
1525 return -strcmp (txt1,txt2);
1528 /***************************************************************************
1529 * Setup the treeview structure with regards of the sort method
1530 * and sort the children of the TV item specified in lParam
1531 * fRecurse: currently unused. Should be zero.
1532 * parent: if pSort!=NULL, should equal pSort->hParent.
1533 * otherwise, item which child items are to be sorted.
1534 * pSort: sort method info. if NULL, sort on item text.
1535 * if non-NULL, sort on item's lParam content, and let the
1536 * application decide what that means. See also TVM_SORTCHILDRENCB.
1539 static LRESULT WINAPI TREEVIEW_Sort (
1540 HWND hwnd,
1541 BOOL fRecurse,
1542 HTREEITEM parent,
1543 LPTVSORTCB pSort
1546 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
1547 TREEVIEW_ITEM *sortMe = NULL; /* Node for which we sort the children */
1549 /* Obtain the TVSORTBC struct */
1550 infoPtr->pCallBackSort = pSort;
1552 /* undocumented feature: TVI_ROOT means `sort the whole tree' */
1554 if (parent==TVI_ROOT)
1555 parent=infoPtr->TopRootItem;
1557 /* Check for a valid handle to the parent item */
1558 if (!TREEVIEW_ValidItem(infoPtr, parent))
1560 ERR ("invalid item hParent=%x\n", (INT)parent);
1561 return FALSE;
1564 /* Obtain the parent node to sort */
1565 sortMe = &infoPtr->items[ (INT)parent ];
1567 /* Make sure there is something to sort */
1568 if ( sortMe->cChildren > 1 )
1570 /* pointer organization */
1571 HDPA sortList = DPA_Create(sortMe->cChildren);
1572 HTREEITEM itemHandle = sortMe->firstChild;
1573 TREEVIEW_ITEM *itemPtr = & infoPtr->items[ (INT)itemHandle ];
1575 /* TREEVIEW_ITEM rechaining */
1576 INT count = 0;
1577 VOID *item = 0;
1578 VOID *nextItem = 0;
1579 VOID *prevItem = 0;
1581 /* Build the list of item to sort */
1584 DPA_InsertPtr(
1585 sortList, /* the list */
1586 sortMe->cChildren+1, /* force the insertion to be an append */
1587 itemPtr); /* the ptr to store */
1589 /* Get the next sibling */
1590 itemHandle = itemPtr->sibling;
1591 itemPtr = & infoPtr->items[ (INT)itemHandle ];
1592 } while ( itemHandle != NULL );
1594 /* let DPA perform the sort activity */
1595 if (pSort)
1596 DPA_Sort(
1597 sortList, /* what */
1598 TREEVIEW_CallBackCompare, /* how */
1599 hwnd); /* owner */
1600 else
1601 DPA_Sort (
1602 sortList, /* what */
1603 TREEVIEW_SortOnName, /* how */
1604 hwnd); /* owner */
1607 * Reorganized TREEVIEW_ITEM structures.
1608 * Note that we know we have at least two elements.
1611 /* Get the first item and get ready to start... */
1612 item = DPA_GetPtr(sortList, count++);
1613 while ( (nextItem = DPA_GetPtr(sortList, count++)) != NULL )
1615 /* link the two current item toghether */
1616 ((TREEVIEW_ITEM*)item)->sibling = ((TREEVIEW_ITEM*)nextItem)->hItem;
1617 ((TREEVIEW_ITEM*)nextItem)->upsibling = ((TREEVIEW_ITEM*)item)->hItem;
1619 if (prevItem == NULL) /* this is the first item, update the parent */
1621 sortMe->firstChild = ((TREEVIEW_ITEM*)item)->hItem;
1622 ((TREEVIEW_ITEM*)item)->upsibling = NULL;
1624 else /* fix the back chaining */
1626 ((TREEVIEW_ITEM*)item)->upsibling = ((TREEVIEW_ITEM*)prevItem)->hItem;
1629 /* get ready for the next one */
1630 prevItem = item;
1631 item = nextItem;
1634 /* the last item is pointed to by item and never has a sibling */
1635 ((TREEVIEW_ITEM*)item)->sibling = NULL;
1637 DPA_Destroy(sortList);
1639 return TRUE;
1641 return FALSE;
1645 /***************************************************************************
1646 * Setup the treeview structure with regards of the sort method
1647 * and sort the children of the TV item specified in lParam
1649 static LRESULT WINAPI TREEVIEW_SortChildrenCB(
1650 HWND hwnd,
1651 WPARAM wParam,
1652 LPARAM lParam
1655 LPTVSORTCB pSort=(LPTVSORTCB) lParam;
1657 return TREEVIEW_Sort (hwnd, wParam, pSort->hParent, pSort);
1661 /***************************************************************************
1662 * Sort the children of the TV item specified in lParam.
1664 static LRESULT WINAPI TREEVIEW_SortChildren (
1665 HWND hwnd,
1666 WPARAM wParam,
1667 LPARAM lParam)
1669 return TREEVIEW_Sort (hwnd, (BOOL) wParam, (HTREEITEM) lParam, NULL);
1674 /* the method used below isn't the most memory-friendly, but it avoids
1675 a lot of memory reallocations */
1677 /* BTW: we waste handle 0; 0 is not an allowed handle. */
1679 static LRESULT
1680 TREEVIEW_InsertItemA (HWND hwnd, WPARAM wParam, LPARAM lParam)
1683 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
1684 TVINSERTSTRUCTA *ptdi;
1685 TVITEMEXA *tvItem;
1686 TREEVIEW_ITEM *wineItem, *parentItem, *prevsib, *sibItem;
1687 INT iItem,listItems,i,len;
1689 /* Item to insert */
1690 ptdi = (LPTVINSERTSTRUCTA) lParam;
1692 /* check if memory is available */
1694 if (infoPtr->uNumPtrsAlloced==0) {
1695 infoPtr->items = COMCTL32_Alloc (TVITEM_ALLOC*sizeof (TREEVIEW_ITEM));
1696 infoPtr->freeList= COMCTL32_Alloc ((1+(TVITEM_ALLOC>>5)) * sizeof (INT));
1697 infoPtr->uNumPtrsAlloced=TVITEM_ALLOC;
1698 infoPtr->TopRootItem=(HTREEITEM)1;
1702 * Reallocate contiguous space for items
1704 if (infoPtr->uNumItems == (infoPtr->uNumPtrsAlloced-1) ) {
1705 TREEVIEW_ITEM *oldItems = infoPtr->items;
1706 INT *oldfreeList = infoPtr->freeList;
1708 infoPtr->uNumPtrsAlloced*=2;
1709 infoPtr->items = COMCTL32_Alloc (infoPtr->uNumPtrsAlloced*sizeof (TREEVIEW_ITEM));
1710 infoPtr->freeList= COMCTL32_Alloc ((1+(infoPtr->uNumPtrsAlloced>>5))*sizeof (INT));
1712 memcpy (&infoPtr->items[0], &oldItems[0],
1713 infoPtr->uNumPtrsAlloced/2 * sizeof(TREEVIEW_ITEM));
1714 memcpy (&infoPtr->freeList[0], &oldfreeList[0],
1715 (infoPtr->uNumPtrsAlloced>>6) * sizeof(INT));
1717 COMCTL32_Free (oldItems);
1718 COMCTL32_Free (oldfreeList);
1722 * Reset infoPtr structure with new stat according to current TV picture
1724 iItem=0;
1725 infoPtr->uNumItems++;
1726 if ((INT)infoPtr->uMaxHandle==(infoPtr->uNumItems-1)) {
1727 iItem=infoPtr->uNumItems;
1728 infoPtr->uMaxHandle = (HTREEITEM)((INT)infoPtr->uMaxHandle + 1);
1729 } else { /* check freelist */
1730 for (i=0; i<=infoPtr->uNumPtrsAlloced>>5; i++) {
1731 if (infoPtr->freeList[i]) {
1732 iItem=ffs (infoPtr->freeList[i])-1;
1733 tv_clear_bit(iItem,&infoPtr->freeList[i]);
1734 iItem+=i<<5;
1735 break;
1740 if (TRACE_ON(treeview)) {
1741 for (i=0; i<=infoPtr->uNumPtrsAlloced>>5; i++)
1742 TRACE("%8x\n",infoPtr->freeList[i]);
1745 if (!iItem) ERR("Argh -- can't find free item.\n");
1748 * Find the parent item of the new item
1750 tvItem= & ptdi->DUMMYUNIONNAME.itemex;
1751 wineItem=& infoPtr->items[iItem];
1753 if ((ptdi->hParent==TVI_ROOT) || (ptdi->hParent==0)) {
1754 parentItem = NULL;
1755 wineItem->parent = 0;
1756 sibItem = &infoPtr->items [(INT)infoPtr->TopRootItem];
1757 listItems = infoPtr->uNumItems;
1759 else {
1760 parentItem = &infoPtr->items[(INT)ptdi->hParent];
1762 /* Do the insertion here it if it's the only item of this parent */
1763 if (!parentItem->firstChild)
1764 parentItem->firstChild=(HTREEITEM)iItem;
1766 wineItem->parent = ptdi->hParent;
1767 sibItem = &infoPtr->items [(INT)parentItem->firstChild];
1768 parentItem->cChildren++;
1769 listItems = parentItem->cChildren;
1773 /* NOTE: I am moving some setup of the wineItem object that was initialy
1774 * done at the end of the function since some of the values are
1775 * required by the Callback sorting
1778 if (tvItem->mask & TVIF_TEXT)
1781 * Setup the item text stuff here since it's required by the Sort method
1782 * when the insertion are ordered
1784 if (tvItem->pszText!=LPSTR_TEXTCALLBACKA)
1786 TRACE("(%p,%s)\n", &tvItem->pszText, tvItem->pszText);
1787 len = lstrlenA (tvItem->pszText)+1;
1788 wineItem->pszText= COMCTL32_Alloc (len+1);
1789 lstrcpyA (wineItem->pszText, tvItem->pszText);
1790 wineItem->cchTextMax=len;
1792 else
1794 TRACE("LPSTR_TEXTCALLBACK\n");
1795 wineItem->pszText = LPSTR_TEXTCALLBACKA;
1796 wineItem->cchTextMax = 0;
1800 if (tvItem->mask & TVIF_PARAM)
1801 wineItem->lParam=tvItem->lParam;
1804 wineItem->upsibling=0; /* needed in case we're the first item in a list */
1805 wineItem->sibling=0;
1806 wineItem->firstChild=0;
1807 wineItem->hItem=(HTREEITEM)iItem;
1809 if (listItems>1) {
1810 prevsib=NULL;
1812 switch ((DWORD) ptdi->hInsertAfter) {
1813 case (DWORD) TVI_FIRST:
1814 if (sibItem==wineItem) break;
1815 if (wineItem->parent) {
1816 wineItem->sibling=parentItem->firstChild;
1817 parentItem->firstChild=(HTREEITEM)iItem;
1818 } else {
1819 wineItem->sibling=infoPtr->TopRootItem;
1820 infoPtr->TopRootItem=(HTREEITEM)iItem;
1822 sibItem->upsibling=(HTREEITEM)iItem;
1823 break;
1825 case (DWORD) TVI_SORT:
1826 if (sibItem==wineItem)
1828 * This item is the first child of the level and it
1829 * has already been inserted
1831 break;
1832 else
1834 TREEVIEW_ITEM *aChild;
1837 TREEVIEW_ITEM *previousChild = NULL;
1838 BOOL bItemInserted = FALSE;
1840 if (parentItem)
1841 aChild = &infoPtr->items[(INT)parentItem->firstChild];
1842 else
1843 aChild = &infoPtr->items[(INT)infoPtr->TopRootItem];
1845 /* lookup the text if using LPSTR_TEXTCALLBACKs */
1846 if (wineItem->pszText==LPSTR_TEXTCALLBACKA) {
1847 TREEVIEW_SendDispInfoNotify (hwnd, wineItem, TVN_GETDISPINFOA, TVIF_TEXT);
1850 /* Iterate the parent children to see where we fit in */
1851 while ( aChild != NULL )
1853 INT comp;
1855 /* lookup the text if using LPSTR_TEXTCALLBACKs */
1856 if (aChild->pszText==LPSTR_TEXTCALLBACKA) {
1857 TREEVIEW_SendDispInfoNotify (hwnd, aChild, TVN_GETDISPINFOA, TVIF_TEXT);
1860 comp = strcmp(wineItem->pszText, aChild->pszText);
1861 if ( comp < 0 ) /* we are smaller than the current one */
1863 TREEVIEW_InsertBefore(infoPtr, wineItem, aChild, parentItem);
1864 bItemInserted = TRUE;
1865 break;
1867 else if ( comp > 0 ) /* we are bigger than the current one */
1869 previousChild = aChild;
1870 aChild = (aChild->sibling == 0) /* This will help us to exit */
1871 ? NULL /* if there is no more sibling */
1872 : &infoPtr->items[(INT)aChild->sibling];
1874 /* Look at the next item */
1875 continue;
1877 else if ( comp == 0 )
1880 * An item with this name is already existing, therefore,
1881 * we add after the one we found
1883 TREEVIEW_InsertAfter(infoPtr, wineItem, aChild, parentItem);
1884 bItemInserted = TRUE;
1885 break;
1890 * we reach the end of the child list and the item as not
1891 * yet been inserted, therefore, insert it after the last child.
1893 if ( (! bItemInserted ) && (aChild == NULL) )
1894 TREEVIEW_InsertAfter(infoPtr, wineItem, previousChild, parentItem);
1896 break;
1900 case (DWORD) TVI_LAST:
1901 if (sibItem==wineItem) break;
1902 while (sibItem->sibling) {
1903 prevsib=sibItem;
1904 sibItem=&infoPtr->items [(INT)sibItem->sibling];
1906 sibItem->sibling=(HTREEITEM)iItem;
1907 wineItem->upsibling=sibItem->hItem;
1908 break;
1909 default:
1910 while ((sibItem->sibling) && (sibItem->hItem!=ptdi->hInsertAfter))
1912 prevsib=sibItem;
1913 sibItem=&infoPtr->items [(INT)sibItem->sibling];
1915 if (sibItem->hItem!=ptdi->hInsertAfter) {
1916 ERR("tried to insert item after nonexisting handle %d.\n",
1917 (INT) ptdi->hInsertAfter);
1918 break;
1920 prevsib=sibItem;
1921 if (sibItem->sibling) {
1922 sibItem=&infoPtr->items [(INT)sibItem->sibling];
1923 sibItem->upsibling=(HTREEITEM)iItem;
1924 wineItem->sibling=sibItem->hItem;
1926 prevsib->sibling=(HTREEITEM)iItem;
1927 wineItem->upsibling=prevsib->hItem;
1928 break;
1933 /* Fill in info structure */
1935 TRACE("new item %d; parent %d, mask %x\n", iItem,
1936 (INT)wineItem->parent,tvItem->mask);
1938 wineItem->mask=tvItem->mask;
1939 wineItem->iIntegral=1;
1941 if (tvItem->mask & TVIF_CHILDREN) {
1942 wineItem->cChildren=tvItem->cChildren;
1943 if (tvItem->cChildren==I_CHILDRENCALLBACK)
1944 FIXME(" I_CHILDRENCALLBACK not supported\n");
1947 wineItem->expandBox.left = 0; /* Initialize the expandBox */
1948 wineItem->expandBox.top = 0;
1949 wineItem->expandBox.right = 0;
1950 wineItem->expandBox.bottom = 0;
1952 if (tvItem->mask & TVIF_IMAGE)
1953 wineItem->iImage=tvItem->iImage;
1955 /* If the application sets TVIF_INTEGRAL without
1956 supplying a TVITEMEX structure, it's toast */
1958 if (tvItem->mask & TVIF_INTEGRAL)
1959 wineItem->iIntegral=tvItem->iIntegral;
1961 if (tvItem->mask & TVIF_SELECTEDIMAGE)
1962 wineItem->iSelectedImage=tvItem->iSelectedImage;
1964 if (tvItem->mask & TVIF_STATE) {
1965 TRACE("item state: %x ->%x\n", wineItem->state, tvItem->state);
1966 TRACE("statemask: %x ->%x\n", wineItem->stateMask, tvItem->stateMask);
1967 wineItem->state=tvItem->state;
1968 wineItem->stateMask=tvItem->stateMask;
1971 TREEVIEW_QueueRefresh (hwnd);
1973 return (LRESULT) iItem;
1977 static LRESULT
1978 TREEVIEW_InsertItemW(HWND hwnd, WPARAM wParam, LPARAM lParam)
1980 TVINSERTSTRUCTW *tvisW;
1981 TVINSERTSTRUCTA tvisA;
1982 LRESULT lRes;
1984 tvisW = (LPTVINSERTSTRUCTW)lParam;
1986 tvisA.hParent = tvisW->hParent;
1987 tvisA.hInsertAfter = tvisW->hInsertAfter;
1989 tvisA.DUMMYUNIONNAME.item.mask = tvisW->DUMMYUNIONNAME.item.mask;
1990 tvisA.DUMMYUNIONNAME.item.hItem = tvisW->DUMMYUNIONNAME.item.hItem;
1991 tvisA.DUMMYUNIONNAME.item.state = tvisW->DUMMYUNIONNAME.item.state;
1992 tvisA.DUMMYUNIONNAME.item.stateMask = tvisW->DUMMYUNIONNAME.item.stateMask;
1993 tvisA.DUMMYUNIONNAME.item.cchTextMax = tvisW->DUMMYUNIONNAME.item.cchTextMax;
1995 if(tvisW->DUMMYUNIONNAME.item.pszText)
1997 if (tvisW->DUMMYUNIONNAME.item.pszText!=LPSTR_TEXTCALLBACKW)
1999 int len = lstrlenW (tvisW->DUMMYUNIONNAME.item.pszText)+1;
2000 tvisA.DUMMYUNIONNAME.item.pszText = COMCTL32_Alloc (len);
2001 lstrcpyWtoA (tvisA.DUMMYUNIONNAME.item.pszText,
2002 tvisW->DUMMYUNIONNAME.item.pszText );
2004 else
2006 tvisA.DUMMYUNIONNAME.item.pszText = LPSTR_TEXTCALLBACKA;
2007 tvisA.DUMMYUNIONNAME.item.cchTextMax = 0;
2011 tvisA.DUMMYUNIONNAME.item.iImage = tvisW->DUMMYUNIONNAME.item.iImage;
2012 tvisA.DUMMYUNIONNAME.item.iSelectedImage = tvisW->DUMMYUNIONNAME.item.iSelectedImage;
2013 tvisA.DUMMYUNIONNAME.item.cChildren = tvisW->DUMMYUNIONNAME.item.cChildren;
2014 tvisA.DUMMYUNIONNAME.item.lParam = tvisW->DUMMYUNIONNAME.item.lParam;
2016 lRes = TREEVIEW_InsertItemA(hwnd,wParam,(LPARAM)&tvisA);
2018 if (tvisA.DUMMYUNIONNAME.item.pszText!=LPSTR_TEXTCALLBACKA)
2020 COMCTL32_Free(tvisA.DUMMYUNIONNAME.item.pszText);
2023 return lRes;
2028 static LRESULT
2029 TREEVIEW_DeleteItem (HWND hwnd, WPARAM wParam, LPARAM lParam)
2031 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
2032 INT iItem;
2033 TREEVIEW_ITEM *wineItem;
2035 TRACE("\n");
2037 if (lParam == (INT)TVI_ROOT) {
2038 TREEVIEW_RemoveTree (hwnd);
2039 } else {
2040 iItem= (INT) lParam;
2041 wineItem = TREEVIEW_ValidItem (infoPtr, (HTREEITEM)iItem);
2042 if (!wineItem) return FALSE;
2044 if (wineItem->pszText==LPSTR_TEXTCALLBACKA)
2045 TRACE("LPSTR_TEXTCALLBACK\n");
2046 else
2047 TRACE("%s\n",wineItem->pszText);
2048 TREEVIEW_RemoveItem (hwnd, wineItem);
2051 TREEVIEW_QueueRefresh (hwnd);
2053 return TRUE;
2058 static LRESULT
2059 TREEVIEW_GetIndent (HWND hwnd)
2061 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
2063 TRACE("\n");
2064 return infoPtr->uIndent;
2067 static LRESULT
2068 TREEVIEW_SetIndent (HWND hwnd, WPARAM wParam)
2070 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
2071 INT newIndent;
2073 TRACE("\n");
2074 newIndent=(INT) wParam;
2075 if (newIndent < MINIMUM_INDENT) newIndent=MINIMUM_INDENT;
2076 infoPtr->uIndent=newIndent;
2078 return 0;
2081 static LRESULT
2082 TREEVIEW_GetToolTips (HWND hwnd)
2085 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
2087 TRACE("\n");
2088 return infoPtr->hwndToolTip;
2092 static LRESULT
2093 TREEVIEW_SetToolTips (HWND hwnd, WPARAM wParam)
2096 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
2097 HWND prevToolTip;
2099 TRACE("\n");
2100 prevToolTip=infoPtr->hwndToolTip;
2101 infoPtr->hwndToolTip= (HWND) wParam;
2103 return prevToolTip;
2107 static LRESULT CALLBACK
2108 TREEVIEW_GetEditControl (HWND hwnd)
2111 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
2113 return infoPtr->hwndEdit;
2116 LRESULT CALLBACK
2117 TREEVIEW_Edit_SubclassProc (HWND hwnd, UINT uMsg, WPARAM wParam,
2118 LPARAM lParam)
2120 switch (uMsg)
2122 case WM_ERASEBKGND:
2124 RECT rc;
2125 HDC hdc = (HDC) wParam;
2126 GetClientRect (hwnd, &rc);
2127 Rectangle (hdc, rc.left, rc.top, rc.right, rc.bottom);
2128 return -1;
2131 case WM_GETDLGCODE:
2133 return DLGC_WANTARROWS | DLGC_WANTALLKEYS;
2136 default:
2138 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(GetParent(hwnd));
2139 if (infoPtr!=NULL)
2140 return CallWindowProcA (infoPtr->wpEditOrig, hwnd, uMsg, wParam, lParam);
2141 else
2142 break;
2147 return 0;
2151 /* should handle edit control messages here */
2153 static LRESULT
2154 TREEVIEW_Command (HWND hwnd, WPARAM wParam, LPARAM lParam)
2157 TRACE("%x %ld\n",wParam, lParam);
2159 switch (HIWORD(wParam))
2161 case EN_UPDATE:
2164 * Adjust the edit window size
2166 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
2167 TREEVIEW_ITEM *editItem = TREEVIEW_ValidItem(infoPtr, infoPtr->editItem);
2168 INT iLength = GetWindowTextLengthA(infoPtr->hwndEdit);
2169 HDC hdc = GetDC(infoPtr->hwndEdit);
2170 TEXTMETRICA tm;
2172 if ( GetTextMetricsA(hdc, &tm) )
2174 LONG newWidth = (iLength * tm.tmAveCharWidth) + 15;
2176 SetWindowPos (
2177 infoPtr->hwndEdit,
2178 HWND_TOP,
2179 editItem->text.left - 2,
2180 editItem->text.top - 1,
2181 newWidth,
2182 editItem->text.bottom - editItem->text.top + 3,
2183 SWP_DRAWFRAME );
2185 ReleaseDC(hwnd, hdc);
2187 break;
2190 case EN_KILLFOCUS:
2191 /* TREEVIEW_EndEditLabelNow(hwnd, (WPARAM)FALSE, 0);
2193 break;
2195 default:
2196 return SendMessageA (GetParent (hwnd), WM_COMMAND, wParam, lParam);
2199 return 0;
2202 static LRESULT
2203 TREEVIEW_Size (HWND hwnd, WPARAM wParam, LPARAM lParam)
2206 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
2208 if (infoPtr->bAutoSize)
2210 infoPtr->bAutoSize = FALSE;
2211 return 0;
2213 infoPtr->bAutoSize = TRUE;
2215 if (wParam == SIZE_RESTORED)
2217 infoPtr->uTotalWidth = LOWORD (lParam);
2218 infoPtr->uTotalHeight = HIWORD (lParam);
2219 } else {
2220 FIXME("WM_SIZE flag %x %lx not handled\n", wParam, lParam);
2223 TREEVIEW_QueueRefresh (hwnd);
2224 return 0;
2229 static LRESULT
2230 TREEVIEW_StyleChanged (HWND hwnd, WPARAM wParam, LPARAM lParam)
2232 HDC hdc;
2234 TRACE("(%x %lx)\n",wParam,lParam);
2235 hdc = GetDC (hwnd);
2236 TREEVIEW_Refresh (hwnd, hdc);
2237 ReleaseDC(hwnd,hdc);
2239 return 0;
2242 static LRESULT
2243 TREEVIEW_Create (HWND hwnd, WPARAM wParam, LPARAM lParam)
2245 TREEVIEW_INFO *infoPtr;
2246 DWORD dwStyle = GetWindowLongA (hwnd, GWL_STYLE);
2247 LOGFONTA logFont;
2248 TEXTMETRICA tm;
2249 HDC hdc;
2251 TRACE("wnd %x, style %lx\n",hwnd,dwStyle);
2252 /* allocate memory for info structure */
2253 infoPtr = (TREEVIEW_INFO *) COMCTL32_Alloc (sizeof(TREEVIEW_INFO));
2255 SetWindowLongA( hwnd, 0, (DWORD)infoPtr);
2257 if (infoPtr == NULL) {
2258 ERR("could not allocate info memory!\n");
2259 return 0;
2262 if ((TREEVIEW_INFO*) GetWindowLongA( hwnd, 0) != infoPtr) {
2263 ERR("pointer assignment error!\n");
2264 return 0;
2267 hdc=GetDC (hwnd);
2269 /* set default settings */
2270 infoPtr->uInternalStatus=0;
2271 infoPtr->uNumItems=0;
2272 infoPtr->clrBk = GetSysColor (COLOR_WINDOW);
2273 infoPtr->clrText = GetSysColor (COLOR_WINDOWTEXT);
2274 infoPtr->clrLine = GetSysColor (COLOR_WINDOWTEXT);
2275 infoPtr->clrInsertMark = GetSysColor (COLOR_BTNTEXT);
2276 infoPtr->cy = 0;
2277 infoPtr->cx = 0;
2278 infoPtr->uIndent = 15;
2279 infoPtr->himlNormal = NULL;
2280 infoPtr->himlState = NULL;
2281 infoPtr->uItemHeight = -1;
2282 GetTextMetricsA (hdc, &tm);
2283 infoPtr->hFont = GetStockObject (DEFAULT_GUI_FONT);
2284 GetObjectA (infoPtr->hFont, sizeof (LOGFONTA), &logFont);
2285 logFont.lfWeight=FW_BOLD;
2286 infoPtr->hBoldFont = CreateFontIndirectA (&logFont);
2288 infoPtr->items = NULL;
2289 infoPtr->selectedItem=0;
2290 infoPtr->clrText=-1; /* use system color */
2291 infoPtr->dropItem=0;
2292 infoPtr->insertMarkItem=0;
2293 infoPtr->insertBeforeorAfter=0;
2294 infoPtr->pCallBackSort=NULL;
2295 infoPtr->uScrollTime = 300; /* milliseconds */
2296 infoPtr->wpEditOrig = NULL; /* we haven't subclassed anything yet */
2298 infoPtr->hwndToolTip=0;
2299 if (!(dwStyle & TVS_NOTOOLTIPS)) { /* Create tooltip control */
2300 TTTOOLINFOA ti;
2302 infoPtr->hwndToolTip =
2303 CreateWindowExA (0, TOOLTIPS_CLASSA, NULL, 0,
2304 CW_USEDEFAULT, CW_USEDEFAULT,
2305 CW_USEDEFAULT, CW_USEDEFAULT,
2306 hwnd, 0, 0, 0);
2308 /* Send NM_TOOLTIPSCREATED notification */
2309 if (infoPtr->hwndToolTip) {
2310 NMTOOLTIPSCREATED nmttc;
2312 nmttc.hdr.hwndFrom = hwnd;
2313 nmttc.hdr.idFrom = GetWindowLongA( hwnd, GWL_ID);
2314 nmttc.hdr.code = NM_TOOLTIPSCREATED;
2315 nmttc.hwndToolTips = infoPtr->hwndToolTip;
2317 SendMessageA (GetParent (hwnd), WM_NOTIFY,
2318 (WPARAM) GetWindowLongA( hwnd, GWL_ID), (LPARAM)&nmttc);
2321 ZeroMemory (&ti, sizeof(TTTOOLINFOA));
2322 ti.cbSize = sizeof(TTTOOLINFOA);
2323 ti.uFlags = TTF_IDISHWND | TTF_TRACK | TTF_TRANSPARENT ;
2324 ti.hwnd = hwnd;
2325 ti.uId = 0;
2326 ti.lpszText = "Test"; /* LPSTR_TEXTCALLBACK; */
2327 SetRectEmpty (&ti.rect);
2329 SendMessageA (infoPtr->hwndToolTip, TTM_ADDTOOLA, 0, (LPARAM)&ti);
2332 infoPtr->hwndEdit = CreateWindowExA (
2333 WS_EX_LEFT,
2334 "EDIT",
2336 WS_CHILD | WS_BORDER | ES_AUTOHSCROLL |
2337 ES_WANTRETURN | ES_LEFT,
2338 0, 0, 0, 0,
2339 hwnd,
2340 0,0,0); /* FIXME: (HMENU)IDTVEDIT,pcs->hInstance,0);*/
2342 SendMessageA ( infoPtr->hwndEdit, WM_SETFONT, infoPtr->hFont, FALSE);
2343 infoPtr->wpEditOrig = (WNDPROC)SetWindowLongA (
2344 infoPtr->hwndEdit,
2345 GWL_WNDPROC,
2346 (LONG) TREEVIEW_Edit_SubclassProc);
2348 if (dwStyle & TVS_CHECKBOXES) {
2349 HBITMAP hbmLoad;
2350 int nIndex;
2352 infoPtr->himlState =
2353 ImageList_Create (16, 16,ILC_COLOR|ILC_MASK, 15, 1);
2355 hbmLoad = LoadBitmapA (COMCTL32_hModule, MAKEINTRESOURCEA(IDT_CHECK));
2356 TRACE ("%x\n",hbmLoad);
2357 nIndex = ImageList_AddMasked (infoPtr->himlState, hbmLoad, CLR_DEFAULT);
2358 TRACE ("%d\n",nIndex);
2359 DeleteObject (hbmLoad);
2361 ReleaseDC (hwnd, hdc);
2362 return 0;
2367 static LRESULT
2368 TREEVIEW_Destroy (HWND hwnd)
2370 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
2372 TRACE("\n");
2373 TREEVIEW_RemoveTree (hwnd);
2374 SetWindowLongA (hwnd, 0, (DWORD)NULL);
2376 if (infoPtr->Timer & TV_REFRESH_TIMER_SET)
2377 KillTimer (hwnd, TV_REFRESH_TIMER);
2378 if (infoPtr->hwndToolTip)
2379 DestroyWindow (infoPtr->hwndToolTip);
2381 COMCTL32_Free (infoPtr);
2382 return 0;
2386 static LRESULT
2387 TREEVIEW_Paint (HWND hwnd, WPARAM wParam, LPARAM lParam)
2389 HDC hdc;
2390 PAINTSTRUCT ps;
2392 TRACE("\n");
2393 hdc = wParam==0 ? BeginPaint (hwnd, &ps) : (HDC)wParam;
2394 TREEVIEW_Refresh (hwnd, hdc);
2395 ReleaseDC(hwnd,hdc);
2396 if(!wParam) EndPaint (hwnd, &ps);
2397 TRACE("done\n");
2399 return DefWindowProcA (hwnd, WM_PAINT, wParam, lParam);
2402 static LRESULT
2403 TREEVIEW_SetFocus (HWND hwnd, WPARAM wParam, LPARAM lParam)
2405 TREEVIEW_SendSimpleNotify (hwnd, NM_SETFOCUS);
2406 InvalidateRect(hwnd, NULL, FALSE);
2407 return 0;
2410 static LRESULT
2411 TREEVIEW_KillFocus (HWND hwnd, WPARAM wParam, LPARAM lParam)
2413 TREEVIEW_SendSimpleNotify (hwnd, NM_KILLFOCUS);
2414 InvalidateRect(hwnd, NULL, FALSE);
2415 return 0;
2418 static LRESULT
2419 TREEVIEW_EraseBackground (HWND hwnd, WPARAM wParam, LPARAM lParam)
2421 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
2422 HBRUSH hBrush = CreateSolidBrush (infoPtr->clrBk);
2423 RECT rect;
2425 TRACE("\n");
2426 GetClientRect (hwnd, &rect);
2427 FillRect ((HDC)wParam, &rect, hBrush);
2428 DeleteObject (hBrush);
2429 return TRUE;
2437 /* Notifications */
2443 static BOOL
2444 TREEVIEW_SendSimpleNotify (HWND hwnd, UINT code)
2446 NMHDR nmhdr;
2448 TRACE("%x\n",code);
2449 nmhdr.hwndFrom = hwnd;
2450 nmhdr.idFrom = GetWindowLongA( hwnd, GWL_ID);
2451 nmhdr.code = code;
2453 return (BOOL) SendMessageA (GetParent (hwnd), WM_NOTIFY,
2454 (WPARAM)nmhdr.idFrom, (LPARAM)&nmhdr);
2459 static BOOL
2460 TREEVIEW_SendTreeviewNotify (HWND hwnd, UINT code, UINT action,
2461 HTREEITEM oldItem, HTREEITEM newItem)
2464 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
2465 NMTREEVIEWA nmhdr;
2466 TREEVIEW_ITEM *wineItem;
2468 TRACE("code:%x action:%x olditem:%x newitem:%x\n",
2469 code,action,(INT)oldItem,(INT)newItem);
2470 nmhdr.hdr.hwndFrom = hwnd;
2471 nmhdr.hdr.idFrom = GetWindowLongA( hwnd, GWL_ID);
2472 nmhdr.hdr.code = code;
2473 nmhdr.action = action;
2474 if (oldItem) {
2475 wineItem=& infoPtr->items[(INT)oldItem];
2476 nmhdr.itemOld.mask = wineItem->mask;
2477 nmhdr.itemOld.hItem = wineItem->hItem;
2478 nmhdr.itemOld.state = wineItem->state;
2479 nmhdr.itemOld.stateMask = wineItem->stateMask;
2480 nmhdr.itemOld.iImage = wineItem->iImage;
2481 nmhdr.itemOld.pszText = wineItem->pszText;
2482 nmhdr.itemOld.cchTextMax= wineItem->cchTextMax;
2483 nmhdr.itemOld.iImage = wineItem->iImage;
2484 nmhdr.itemOld.iSelectedImage = wineItem->iSelectedImage;
2485 nmhdr.itemOld.cChildren = wineItem->cChildren;
2486 nmhdr.itemOld.lParam = wineItem->lParam;
2489 if (newItem) {
2490 wineItem=& infoPtr->items[(INT)newItem];
2491 nmhdr.itemNew.mask = wineItem->mask;
2492 nmhdr.itemNew.hItem = wineItem->hItem;
2493 nmhdr.itemNew.state = wineItem->state;
2494 nmhdr.itemNew.stateMask = wineItem->stateMask;
2495 nmhdr.itemNew.iImage = wineItem->iImage;
2496 nmhdr.itemNew.pszText = wineItem->pszText;
2497 nmhdr.itemNew.cchTextMax= wineItem->cchTextMax;
2498 nmhdr.itemNew.iImage = wineItem->iImage;
2499 nmhdr.itemNew.iSelectedImage = wineItem->iSelectedImage;
2500 nmhdr.itemNew.cChildren = wineItem->cChildren;
2501 nmhdr.itemNew.lParam = wineItem->lParam;
2504 nmhdr.ptDrag.x = 0;
2505 nmhdr.ptDrag.y = 0;
2507 return (BOOL)SendMessageA (GetParent (hwnd), WM_NOTIFY,
2508 (WPARAM) GetWindowLongA( hwnd, GWL_ID), (LPARAM)&nmhdr);
2512 static BOOL
2513 TREEVIEW_SendTreeviewDnDNotify (HWND hwnd, UINT code, HTREEITEM dragItem,
2514 POINT pt)
2516 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
2517 NMTREEVIEWA nmhdr;
2518 TREEVIEW_ITEM *wineItem;
2520 TRACE("code:%x dragitem:%x\n", code,(INT)dragItem);
2522 nmhdr.hdr.hwndFrom = hwnd;
2523 nmhdr.hdr.idFrom = GetWindowLongA( hwnd, GWL_ID);
2524 nmhdr.hdr.code = code;
2525 nmhdr.action = 0;
2526 wineItem=& infoPtr->items[(INT)dragItem];
2527 nmhdr.itemNew.mask = wineItem->mask;
2528 nmhdr.itemNew.hItem = wineItem->hItem;
2529 nmhdr.itemNew.state = wineItem->state;
2530 nmhdr.itemNew.lParam = wineItem->lParam;
2532 nmhdr.ptDrag.x = pt.x;
2533 nmhdr.ptDrag.y = pt.y;
2535 return (BOOL)SendMessageA (GetParent (hwnd), WM_NOTIFY,
2536 (WPARAM) GetWindowLongA( hwnd, GWL_ID), (LPARAM)&nmhdr);
2542 static BOOL
2543 TREEVIEW_SendDispInfoNotify (HWND hwnd, TREEVIEW_ITEM *wineItem,
2544 UINT code, UINT what)
2546 NMTVDISPINFOA tvdi;
2547 BOOL retval;
2548 char *buf;
2550 TRACE("item %d, action %x, state %d\n",
2551 (INT)wineItem->hItem,
2552 what,
2553 (INT)wineItem->state);
2555 tvdi.hdr.hwndFrom = hwnd;
2556 tvdi.hdr.idFrom = GetWindowLongA( hwnd, GWL_ID);
2557 tvdi.hdr.code = code;
2558 tvdi.item.mask = what;
2559 tvdi.item.hItem = wineItem->hItem;
2560 tvdi.item.state = wineItem->state;
2561 tvdi.item.lParam = wineItem->lParam;
2562 tvdi.item.pszText = COMCTL32_Alloc (128*sizeof(char));
2563 tvdi.item.cchTextMax = 128;
2564 buf = tvdi.item.pszText;
2566 retval=(BOOL)SendMessageA (
2567 GetParent(hwnd),
2568 WM_NOTIFY,
2569 (WPARAM)tvdi.hdr.idFrom,
2570 (LPARAM)&tvdi);
2572 if (what & TVIF_TEXT) {
2573 wineItem->pszText = tvdi.item.pszText;
2574 if (buf==tvdi.item.pszText) {
2575 wineItem->cchTextMax = 128;
2576 } else {
2577 TRACE("user-supplied buffer\n");
2578 COMCTL32_Free (buf);
2579 wineItem->cchTextMax = 0;
2582 if (what & TVIF_SELECTEDIMAGE)
2583 wineItem->iSelectedImage = tvdi.item.iSelectedImage;
2584 if (what & TVIF_IMAGE)
2585 wineItem->iImage = tvdi.item.iImage;
2586 if (what & TVIF_CHILDREN)
2587 wineItem->cChildren = tvdi.item.cChildren;
2589 return retval;
2594 static BOOL
2595 TREEVIEW_SendCustomDrawNotify (HWND hwnd, DWORD dwDrawStage, HDC hdc,
2596 RECT rc)
2598 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
2599 NMTVCUSTOMDRAW nmcdhdr;
2600 LPNMCUSTOMDRAW nmcd;
2602 TRACE("drawstage:%lx hdc:%x\n", dwDrawStage, hdc);
2604 nmcd= & nmcdhdr.nmcd;
2605 nmcd->hdr.hwndFrom = hwnd;
2606 nmcd->hdr.idFrom = GetWindowLongA( hwnd, GWL_ID);
2607 nmcd->hdr.code = NM_CUSTOMDRAW;
2608 nmcd->dwDrawStage= dwDrawStage;
2609 nmcd->hdc = hdc;
2610 nmcd->rc.left = rc.left;
2611 nmcd->rc.right = rc.right;
2612 nmcd->rc.bottom = rc.bottom;
2613 nmcd->rc.top = rc.top;
2614 nmcd->dwItemSpec = 0;
2615 nmcd->uItemState = 0;
2616 nmcd->lItemlParam= 0;
2617 nmcdhdr.clrText = infoPtr->clrText;
2618 nmcdhdr.clrTextBk= infoPtr->clrBk;
2619 nmcdhdr.iLevel = 0;
2621 return (BOOL)SendMessageA (GetParent (hwnd), WM_NOTIFY,
2622 (WPARAM) GetWindowLongA( hwnd, GWL_ID), (LPARAM)&nmcdhdr);
2628 /* FIXME: need to find out when the flags in uItemState need to be set */
2630 static BOOL
2631 TREEVIEW_SendCustomDrawItemNotify (HWND hwnd, HDC hdc,
2632 TREEVIEW_ITEM *wineItem, UINT uItemDrawState)
2634 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
2635 NMTVCUSTOMDRAW nmcdhdr;
2636 LPNMCUSTOMDRAW nmcd;
2637 DWORD dwDrawStage,dwItemSpec;
2638 UINT uItemState;
2639 INT retval;
2641 dwDrawStage=CDDS_ITEM | uItemDrawState;
2642 dwItemSpec=(DWORD)wineItem->hItem;
2643 uItemState=0;
2644 if (wineItem->hItem==infoPtr->selectedItem) uItemState|=CDIS_SELECTED;
2645 if (wineItem->hItem==infoPtr->focusItem) uItemState|=CDIS_FOCUS;
2646 if (wineItem->hItem==infoPtr->hotItem) uItemState|=CDIS_HOT;
2648 nmcd= & nmcdhdr.nmcd;
2649 nmcd->hdr.hwndFrom = hwnd;
2650 nmcd->hdr.idFrom = GetWindowLongA( hwnd, GWL_ID);
2651 nmcd->hdr.code = NM_CUSTOMDRAW;
2652 nmcd->dwDrawStage= dwDrawStage;
2653 nmcd->hdc = hdc;
2654 nmcd->rc.left = wineItem->rect.left;
2655 nmcd->rc.right = wineItem->rect.right;
2656 nmcd->rc.bottom = wineItem->rect.bottom;
2657 nmcd->rc.top = wineItem->rect.top;
2658 nmcd->dwItemSpec = dwItemSpec;
2659 nmcd->uItemState = uItemState;
2660 nmcd->lItemlParam= wineItem->lParam;
2661 nmcdhdr.clrText = infoPtr->clrText;
2662 nmcdhdr.clrTextBk= infoPtr->clrBk;
2663 nmcdhdr.iLevel = wineItem->iLevel;
2665 TRACE("drawstage:%lx hdc:%x item:%lx, itemstate:%x, lItemlParam:%lx\n",
2666 nmcd->dwDrawStage, nmcd->hdc, nmcd->dwItemSpec,
2667 nmcd->uItemState, nmcd->lItemlParam);
2669 retval=SendMessageA (GetParent (hwnd), WM_NOTIFY,
2670 (WPARAM) GetWindowLongA( hwnd, GWL_ID), (LPARAM)&nmcdhdr);
2672 infoPtr->clrText=nmcdhdr.clrText;
2673 infoPtr->clrBk =nmcdhdr.clrTextBk;
2674 return (BOOL) retval;
2679 /* Note:If the specified item is the child of a collapsed parent item,
2680 the parent's list of child items is (recursively) expanded to reveal the
2681 specified item. This is mentioned for TREEVIEW_SelectItem; don't
2682 know if it also applies here.
2685 static LRESULT
2686 TREEVIEW_Expand (HWND hwnd, WPARAM wParam, LPARAM lParam)
2688 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
2689 TREEVIEW_ITEM *wineItem;
2690 UINT flag;
2691 INT expand;
2693 flag = (UINT) wParam;
2694 expand = (INT) lParam;
2696 wineItem = TREEVIEW_ValidItem (infoPtr, (HTREEITEM)expand);
2698 if (!wineItem)
2699 return 0;
2700 if (!wineItem->cChildren)
2701 return 0;
2703 if (wineItem->pszText==LPSTR_TEXTCALLBACKA)
2704 TRACE ("For item %d, flags %d, state %d\n",
2705 expand, flag, wineItem->state);
2706 else
2707 TRACE("For (%s) item:%d, flags %x, state:%d\n",
2708 wineItem->pszText, flag, expand, wineItem->state);
2710 if (wineItem->cChildren==I_CHILDRENCALLBACK) {
2711 FIXME("we don't handle I_CHILDRENCALLBACK yet\n");
2712 return 0;
2715 if (flag == TVE_TOGGLE) { /* FIXME: check exact behaviour here */
2716 flag &= ~TVE_TOGGLE; /* ie: bitwise ops or 'case' ops */
2717 if (wineItem->state & TVIS_EXPANDED)
2718 flag |= TVE_COLLAPSE;
2719 else
2720 flag |= TVE_EXPAND;
2723 switch (flag)
2725 case TVE_COLLAPSERESET:
2726 TRACE(" case TVE_COLLAPSERESET\n");
2727 if (!wineItem->state & TVIS_EXPANDED)
2728 return 0;
2730 wineItem->state &= ~(TVIS_EXPANDEDONCE | TVIS_EXPANDED);
2731 TREEVIEW_RemoveAllChildren (hwnd, wineItem);
2732 break;
2734 case TVE_COLLAPSE:
2735 TRACE(" case TVE_COLLAPSE\n");
2736 if (!wineItem->state & TVIS_EXPANDED)
2737 return 0;
2739 wineItem->state &= ~TVIS_EXPANDED;
2740 break;
2742 case TVE_EXPAND:
2743 TRACE(" case TVE_EXPAND\n");
2744 if (wineItem->state & TVIS_EXPANDED)
2745 return 0;
2747 TRACE(" is not expanded...\n");
2749 if (!(wineItem->state & TVIS_EXPANDEDONCE))
2751 TRACE(" and has never been expanded...\n");
2752 wineItem->state |= TVIS_EXPANDED;
2754 /* this item has never been expanded */
2755 if (TREEVIEW_SendTreeviewNotify (
2756 hwnd,
2757 TVN_ITEMEXPANDINGA,
2758 TVE_EXPAND,
2760 (HTREEITEM)expand))
2762 TRACE(" TVN_ITEMEXPANDINGA returned TRUE, exiting...\n");
2763 return FALSE;
2766 /* FIXME
2767 * Since the TVN_ITEMEXPANDINGA message may has caused the parent to
2768 * insert new items which in turn may have cause items placeholder
2769 * reallocation, I reassign the current item pointer so we have
2770 * something valid to work with...
2771 * However, this should not be necessary,
2772 * investigation required in TREEVIEW_InsertItemA
2774 wineItem = TREEVIEW_ValidItem (infoPtr, (HTREEITEM)expand);
2775 if (! wineItem)
2777 ERR(
2778 "Catastropic situation, cannot retreive item #%d\n",
2779 expand);
2780 return FALSE;
2783 wineItem->state |= TVIS_EXPANDEDONCE;
2784 TRACE(" TVN_ITEMEXPANDINGA sent...\n");
2786 TREEVIEW_SendTreeviewNotify (
2787 hwnd,
2788 TVN_ITEMEXPANDEDA,
2789 TVE_EXPAND,
2791 (HTREEITEM)expand);
2793 TRACE(" TVN_ITEMEXPANDEDA sent...\n");
2796 else
2798 /* this item has already been expanded */
2799 wineItem->state |= TVIS_EXPANDED;
2801 break;
2803 case TVE_EXPANDPARTIAL:
2804 TRACE(" case TVE_EXPANDPARTIAL\n");
2805 FIXME("TVE_EXPANDPARTIAL not implemented\n");
2806 wineItem->state ^=TVIS_EXPANDED;
2807 wineItem->state |=TVIS_EXPANDEDONCE;
2808 break;
2811 TRACE("Exiting, Item %d state is now %d...\n",
2812 expand,
2813 wineItem->state);
2815 TREEVIEW_QueueRefresh (hwnd);
2816 return TRUE;
2821 static TREEVIEW_ITEM *
2822 TREEVIEW_HitTestPoint (HWND hwnd, POINT pt)
2824 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
2825 TREEVIEW_ITEM *wineItem;
2826 RECT rect;
2828 GetClientRect (hwnd, &rect);
2830 if (!infoPtr->firstVisible) return NULL;
2832 wineItem=&infoPtr->items [(INT)infoPtr->firstVisible];
2834 while ((wineItem!=NULL) && (pt.y > wineItem->rect.bottom))
2835 wineItem=TREEVIEW_GetNextListItem (infoPtr,wineItem);
2837 if (!wineItem)
2838 return NULL;
2840 return wineItem;
2846 static LRESULT
2847 TREEVIEW_HitTest (HWND hwnd, LPARAM lParam)
2849 LPTVHITTESTINFO lpht=(LPTVHITTESTINFO) lParam;
2850 TREEVIEW_ITEM *wineItem;
2851 RECT rect;
2852 UINT status,x,y;
2854 GetClientRect (hwnd, &rect);
2855 status=0;
2856 x=lpht->pt.x;
2857 y=lpht->pt.y;
2858 if (x < rect.left) status|=TVHT_TOLEFT;
2859 if (x > rect.right) status|=TVHT_TORIGHT;
2860 if (y < rect.top ) status|=TVHT_ABOVE;
2861 if (y > rect.bottom) status|=TVHT_BELOW;
2863 if (status) {
2864 lpht->flags=status;
2865 return 0;
2868 wineItem=TREEVIEW_HitTestPoint (hwnd, lpht->pt);
2869 if (!wineItem) {
2870 lpht->flags=TVHT_NOWHERE;
2871 return 0;
2874 lpht->flags=0;
2876 if (x < wineItem->expandBox.left) {
2877 lpht->flags |= TVHT_ONITEMINDENT;
2878 goto done;
2880 if ( PtInRect ( &wineItem->expandBox, lpht->pt)) {
2881 lpht->flags |= TVHT_ONITEMBUTTON;
2882 goto done;
2884 if ( PtInRect ( &wineItem->bitmap, lpht->pt)) {
2885 lpht->flags |= TVHT_ONITEMICON;
2886 goto done;
2888 if ( PtInRect ( &wineItem->statebitmap, lpht->pt)) {
2889 lpht->flags |= TVHT_ONITEMSTATEICON;
2890 goto done;
2892 if ( PtInRect ( &wineItem->text, lpht->pt)) {
2893 lpht->flags |= TVHT_ONITEMLABEL;
2894 goto done;
2897 lpht->flags|=TVHT_ONITEMRIGHT;
2900 done:
2901 lpht->hItem=wineItem->hItem;
2902 TRACE ("(%ld,%ld):result %x\n",lpht->pt.x,lpht->pt.y,lpht->flags);
2904 return (LRESULT) wineItem->hItem;
2907 LRESULT WINAPI
2908 TREEVIEW_EndEditLabelNow (HWND hwnd, WPARAM wParam, LPARAM lParam)
2910 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
2911 TREEVIEW_ITEM *editedItem = TREEVIEW_ValidItem (infoPtr, infoPtr->editItem);
2912 BOOL bRevert = (BOOL)wParam;
2913 BOOL bReturn = ! bRevert;
2915 if ( ! (BOOL)wParam ) /* wParam is set to true to cancel the edition */
2917 if ( TREEVIEW_SendDispInfoNotify( /* return true to cancel edition */
2918 hwnd,
2919 editedItem,
2920 TVN_ENDLABELEDITA,
2923 bRevert = TRUE;
2924 bReturn = FALSE;
2928 if (bRevert == FALSE) /* Apply the changes */
2930 char tmpText[1024];
2931 int iLength = GetWindowTextA(infoPtr->hwndEdit, tmpText, 1023);
2932 bReturn = FALSE;
2934 if (iLength == 0)
2936 ERR("Problem retreiving new item label.");
2938 else if (iLength >= 1023)
2940 ERR(
2941 "Insuficient space to retrieve new item label, new label ignored.");
2943 else
2945 if (strcmp( tmpText, editedItem->pszText ) == 0)
2946 /* Do nothing if the label has not changed */
2947 bReturn = TRUE;
2948 else
2950 LPSTR tmpLabel = COMCTL32_Alloc( iLength+1 );
2952 if ( tmpLabel == NULL )
2953 ERR(
2954 "OutOfMemory, cannot allocate space for label");
2955 else
2957 COMCTL32_Free(editedItem->pszText);
2958 editedItem->pszText = tmpLabel;
2959 lstrcpyA( editedItem->pszText, tmpText);
2960 bReturn = TRUE;
2965 ShowWindow(infoPtr->hwndEdit, SW_HIDE);
2966 EnableWindow(infoPtr->hwndEdit, FALSE);
2967 infoPtr->editItem = 0;
2970 return bReturn;
2975 static LRESULT
2976 TREEVIEW_LButtonDoubleClick (HWND hwnd, WPARAM wParam, LPARAM lParam)
2978 TREEVIEW_ITEM *wineItem;
2979 POINT pt;
2981 TRACE("\n");
2982 pt.x = (INT)LOWORD(lParam);
2983 pt.y = (INT)HIWORD(lParam);
2984 SetFocus (hwnd);
2986 wineItem=TREEVIEW_HitTestPoint (hwnd, pt);
2987 if (!wineItem) return 0;
2988 TRACE("item %d \n",(INT)wineItem->hItem);
2990 if (TREEVIEW_SendSimpleNotify (hwnd, NM_DBLCLK)!=TRUE) { /* FIXME!*/
2991 TREEVIEW_Expand (hwnd, (WPARAM) TVE_TOGGLE, (LPARAM) wineItem->hItem);
2993 return TRUE;
2997 static LRESULT
2998 TREEVIEW_LButtonDown (HWND hwnd, WPARAM wParam, LPARAM lParam)
3000 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
3001 INT iItem;
3002 TVHITTESTINFO ht;
3004 ht.pt.x = (INT)LOWORD(lParam);
3005 ht.pt.y = (INT)HIWORD(lParam);
3007 SetFocus (hwnd);
3008 iItem=TREEVIEW_HitTest (hwnd, (LPARAM) &ht);
3009 TRACE("item %d \n",iItem);
3011 if (ht.flags & TVHT_ONITEMBUTTON) {
3012 TREEVIEW_Expand (hwnd, (WPARAM) TVE_TOGGLE, (LPARAM) iItem);
3014 else
3016 infoPtr->uInternalStatus|=TV_LDRAG;
3019 return 0;
3022 static LRESULT
3023 TREEVIEW_LButtonUp (HWND hwnd, WPARAM wParam, LPARAM lParam)
3025 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
3026 INT iItem;
3027 TREEVIEW_ITEM *wineItem;
3028 TVHITTESTINFO ht;
3030 ht.pt.x = (INT)LOWORD(lParam);
3031 ht.pt.y = (INT)HIWORD(lParam);
3033 TRACE("\n");
3035 /* Return true to cancel default behaviour */
3036 if ( TREEVIEW_SendSimpleNotify (hwnd, NM_CLICK) )
3037 return 0;
3039 /* Get the item */
3040 iItem = TREEVIEW_HitTest (hwnd, (LPARAM) &ht);
3041 TRACE ("%d\n",iItem);
3042 if (!iItem)
3043 return 0;
3045 wineItem = TREEVIEW_ValidItem(infoPtr, (HTREEITEM)iItem);
3047 infoPtr->uInternalStatus &= ~(TV_LDRAG | TV_LDRAGGING);
3050 * If the style allow editing and the node is already selected
3051 * and the click occured on the item label...
3053 if ( ( GetWindowLongA( hwnd, GWL_STYLE) & TVS_EDITLABELS ) &&
3054 ( wineItem->state & TVIS_SELECTED ) &&
3055 ( ht.flags & TVHT_ONITEMLABEL ))
3057 if ( infoPtr->editItem == 0 ) /* If we are not curently editing */
3059 if ( TREEVIEW_SendDispInfoNotify( /* Return true to cancel edition */
3060 hwnd,
3061 wineItem,
3062 TVN_BEGINLABELEDITA,
3065 return 0;
3068 TRACE("Edit started for %s.\n", wineItem->pszText);
3069 infoPtr->editItem = wineItem->hItem;
3071 SetWindowPos (
3072 infoPtr->hwndEdit,
3073 HWND_TOP,
3074 wineItem->text.left - 2,
3075 wineItem->text.top - 1,
3076 wineItem->text.right - wineItem->text.left + 20 ,
3077 wineItem->text.bottom - wineItem->text.top + 3,
3078 SWP_DRAWFRAME );
3080 SetWindowTextA( infoPtr->hwndEdit, wineItem->pszText );
3081 SendMessageA ( infoPtr->hwndEdit, EM_SETSEL, 0, -1 );
3082 SetFocus ( infoPtr->hwndEdit);
3083 ShowWindow ( infoPtr->hwndEdit, SW_SHOW);
3086 else if ( infoPtr->editItem != 0 ) /* If we are curently editing */
3088 TREEVIEW_EndEditLabelNow(hwnd, (WPARAM)FALSE, 0);
3090 else if ( ht.flags & (TVHT_ONITEMLABEL | TVHT_ONITEMICON))
3092 TREEVIEW_DoSelectItem ( hwnd, TVGN_CARET, (HTREEITEM)iItem, TVC_BYMOUSE);
3095 if (ht.flags & TVHT_ONITEMSTATEICON) {
3096 DWORD dwStyle = GetWindowLongA (hwnd, GWL_STYLE);
3099 if (dwStyle & TVS_CHECKBOXES) { /* TVS_CHECKBOXES requires _us_ */
3100 int state; /* to toggle the current state */
3101 state=1-(wineItem->state>>12);
3102 TRACE ("state:%x\n", state);
3103 wineItem->state&= ~TVIS_STATEIMAGEMASK;
3104 wineItem->state|=state<<12;
3105 TRACE ("state:%x\n", wineItem->state);
3106 TREEVIEW_QueueRefresh (hwnd);
3109 return 0;
3113 static LRESULT
3114 TREEVIEW_RButtonDown (HWND hwnd, WPARAM wParam, LPARAM lParam)
3116 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
3118 TRACE("\n");
3119 infoPtr->uInternalStatus|=TV_RDRAG;
3120 return 0;
3123 static LRESULT
3124 TREEVIEW_RButtonUp (HWND hwnd, WPARAM wParam, LPARAM lParam)
3126 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
3128 TRACE("\n");
3129 if (TREEVIEW_SendSimpleNotify (hwnd, NM_RCLICK)) return 0;
3130 infoPtr->uInternalStatus&= ~(TV_RDRAG | TV_RDRAGGING);
3131 return 0;
3135 static LRESULT
3136 TREEVIEW_MouseMove (HWND hwnd, WPARAM wParam, LPARAM lParam)
3138 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
3139 TREEVIEW_ITEM *hotItem;
3140 POINT pt;
3142 pt.x=(INT) LOWORD (lParam);
3143 pt.y=(INT) HIWORD (lParam);
3144 hotItem=TREEVIEW_HitTestPoint (hwnd, pt);
3145 if (!hotItem) return 0;
3146 infoPtr->focusItem=hotItem->hItem;
3148 if ( GetWindowLongA( hwnd, GWL_STYLE) & TVS_DISABLEDRAGDROP) return 0;
3150 if (infoPtr->uInternalStatus & TV_LDRAG) {
3151 TREEVIEW_SendTreeviewDnDNotify (hwnd, TVN_BEGINDRAGA, hotItem->hItem, pt);
3152 infoPtr->uInternalStatus &= ~TV_LDRAG;
3153 infoPtr->uInternalStatus |= TV_LDRAGGING;
3154 infoPtr->dropItem=hotItem->hItem;
3155 return 0;
3158 if (infoPtr->uInternalStatus & TV_RDRAG) {
3159 TREEVIEW_SendTreeviewDnDNotify (hwnd, TVN_BEGINRDRAGA, hotItem->hItem, pt);
3160 infoPtr->uInternalStatus &= ~TV_RDRAG;
3161 infoPtr->uInternalStatus |= TV_RDRAGGING;
3162 infoPtr->dropItem=hotItem->hItem;
3163 return 0;
3166 return 0;
3170 static LRESULT
3171 TREEVIEW_CreateDragImage (HWND hwnd, WPARAM wParam, LPARAM lParam)
3173 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
3174 TREEVIEW_ITEM *dragItem;
3175 INT cx,cy;
3176 HDC hdc,htopdc;
3177 HWND hwtop;
3178 HBITMAP hbmp,hOldbmp;
3179 SIZE size;
3180 RECT rc;
3181 HFONT hOldFont;
3182 char *itemtxt;
3184 TRACE("\n");
3185 if (!(infoPtr->himlNormal)) return 0;
3186 dragItem=TREEVIEW_ValidItem (infoPtr, (HTREEITEM) lParam);
3188 if (!dragItem) return 0;
3190 if (dragItem->pszText==LPSTR_TEXTCALLBACKA) {
3191 TREEVIEW_SendDispInfoNotify (hwnd, dragItem, TVN_GETDISPINFOA, TVIF_TEXT);
3193 itemtxt=dragItem->pszText;
3195 hwtop=GetDesktopWindow ();
3196 htopdc= GetDC (hwtop);
3197 hdc=CreateCompatibleDC (htopdc);
3199 hOldFont=SelectObject (hdc, infoPtr->hFont);
3200 GetTextExtentPoint32A (hdc, itemtxt, lstrlenA (itemtxt), &size);
3201 TRACE("%d %d %s %d\n",size.cx,size.cy,itemtxt,lstrlenA(itemtxt));
3202 hbmp=CreateCompatibleBitmap (htopdc, size.cx, size.cy);
3203 hOldbmp=SelectObject (hdc, hbmp);
3205 ImageList_GetIconSize (infoPtr->himlNormal, &cx, &cy);
3206 size.cx+=cx;
3207 if (cy>size.cy) size.cy=cy;
3209 infoPtr->dragList=ImageList_Create (size.cx, size.cy, ILC_COLOR, 10, 10);
3210 ImageList_Draw (infoPtr->himlNormal, dragItem->iImage, hdc, 0, 0, ILD_NORMAL);
3213 ImageList_GetImageInfo (infoPtr->himlNormal, dragItem->hItem, &iminfo);
3214 ImageList_AddMasked (infoPtr->dragList, iminfo.hbmImage, CLR_DEFAULT);
3217 /* draw item text */
3219 SetRect (&rc, cx, 0, size.cx,size.cy);
3220 DrawTextA (hdc, itemtxt, lstrlenA (itemtxt), &rc, DT_LEFT);
3221 SelectObject (hdc, hOldFont);
3222 SelectObject (hdc, hOldbmp);
3224 ImageList_Add (infoPtr->dragList, hbmp, 0);
3226 DeleteDC (hdc);
3227 DeleteObject (hbmp);
3228 ReleaseDC (hwtop, htopdc);
3230 return (LRESULT)infoPtr->dragList;
3234 static LRESULT
3235 TREEVIEW_DoSelectItem (HWND hwnd, INT action, HTREEITEM newSelect, INT cause)
3238 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
3239 TREEVIEW_ITEM *prevItem,*wineItem;
3240 INT prevSelect;
3242 wineItem = TREEVIEW_ValidItem (infoPtr, (HTREEITEM)newSelect);
3244 TRACE("Entering item %d, flag %x, cause %x, state %d\n",
3245 (INT)newSelect,
3246 action,
3247 cause,
3248 wineItem->state);
3250 if ( (wineItem) && (wineItem->parent))
3253 * If the item has a collapse parent expand the parent so he
3254 * can expose the item
3256 TREEVIEW_ITEM *parentItem = TREEVIEW_ValidItem (infoPtr, wineItem->parent);
3257 if ( !(parentItem->state & TVIS_EXPANDED))
3258 TREEVIEW_Expand (hwnd, TVE_EXPAND, (LPARAM) wineItem->parent);
3261 switch (action)
3263 case TVGN_CARET:
3264 prevSelect=(INT)infoPtr->selectedItem;
3266 if ((HTREEITEM)prevSelect==newSelect)
3267 return FALSE;
3269 prevItem= TREEVIEW_ValidItem (infoPtr, (HTREEITEM)prevSelect);
3271 if (newSelect)
3272 if (TREEVIEW_SendTreeviewNotify(
3273 hwnd,
3274 TVN_SELCHANGINGA,
3275 cause,
3276 (HTREEITEM)prevSelect,
3277 (HTREEITEM)newSelect))
3278 return FALSE; /* FIXME: OK? */
3280 if (prevItem)
3281 prevItem->state &= ~TVIS_SELECTED;
3282 if (wineItem)
3283 wineItem->state |= TVIS_SELECTED;
3285 infoPtr->selectedItem=(HTREEITEM)newSelect;
3287 TREEVIEW_SendTreeviewNotify(
3288 hwnd,
3289 TVN_SELCHANGEDA,
3290 cause,
3291 (HTREEITEM)prevSelect,
3292 (HTREEITEM)newSelect);
3294 break;
3296 case TVGN_DROPHILITE:
3297 prevItem= TREEVIEW_ValidItem (infoPtr, infoPtr->dropItem);
3299 if (prevItem)
3300 prevItem->state &= ~TVIS_DROPHILITED;
3302 infoPtr->dropItem=(HTREEITEM)newSelect;
3304 if (wineItem)
3305 wineItem->state |=TVIS_DROPHILITED;
3307 break;
3309 case TVGN_FIRSTVISIBLE:
3310 FIXME("FIRSTVISIBLE not implemented\n");
3311 break;
3314 TREEVIEW_QueueRefresh (hwnd);
3316 TRACE("Leaving state %d\n", wineItem->state);
3317 return TRUE;
3320 /* FIXME: handle NM_KILLFocus etc */
3321 static LRESULT
3322 TREEVIEW_SelectItem (HWND hwnd, WPARAM wParam, LPARAM lParam)
3325 return TREEVIEW_DoSelectItem (hwnd, wParam, (HTREEITEM) lParam, TVC_UNKNOWN);
3331 static LRESULT
3332 TREEVIEW_GetFont (HWND hwnd, WPARAM wParam, LPARAM lParam)
3335 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
3337 TRACE("%x\n",infoPtr->hFont);
3338 return infoPtr->hFont;
3341 static LRESULT
3342 TREEVIEW_SetFont (HWND hwnd, WPARAM wParam, LPARAM lParam)
3345 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
3346 TEXTMETRICA tm;
3347 LOGFONTA logFont;
3348 HFONT hFont, hOldFont;
3349 INT height;
3350 HDC hdc;
3352 TRACE("%x %lx\n",wParam, lParam);
3354 infoPtr->hFont = (HFONT)wParam;
3356 hFont = infoPtr->hFont ? infoPtr->hFont : GetStockObject (SYSTEM_FONT);
3358 GetObjectA (infoPtr->hFont, sizeof (LOGFONTA), &logFont);
3359 logFont.lfWeight=FW_BOLD;
3360 infoPtr->hBoldFont = CreateFontIndirectA (&logFont);
3362 hdc = GetDC (0);
3363 hOldFont = SelectObject (hdc, hFont);
3364 GetTextMetricsA (hdc, &tm);
3365 height= tm.tmHeight + tm.tmExternalLeading;
3366 if (height>infoPtr->uRealItemHeight)
3367 infoPtr->uRealItemHeight=height;
3368 SelectObject (hdc, hOldFont);
3369 ReleaseDC (0, hdc);
3371 if (lParam)
3372 TREEVIEW_QueueRefresh (hwnd);
3374 return 0;
3379 static LRESULT
3380 TREEVIEW_VScroll (HWND hwnd, WPARAM wParam, LPARAM lParam)
3383 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
3384 int maxHeight;
3386 TRACE("wp %x, lp %lx\n", wParam, lParam);
3387 if (!infoPtr->uInternalStatus & TV_VSCROLL) return FALSE;
3389 switch (LOWORD (wParam)) {
3390 case SB_LINEUP:
3391 if (!infoPtr->cy) return FALSE;
3392 infoPtr->cy -= infoPtr->uRealItemHeight;
3393 if (infoPtr->cy < 0) infoPtr->cy=0;
3394 break;
3395 case SB_LINEDOWN:
3396 maxHeight=infoPtr->uTotalHeight-infoPtr->uVisibleHeight;
3397 if (infoPtr->cy == maxHeight) return FALSE;
3398 infoPtr->cy += infoPtr->uRealItemHeight;
3399 if (infoPtr->cy > maxHeight)
3400 infoPtr->cy = maxHeight;
3401 break;
3402 case SB_PAGEUP:
3403 if (!infoPtr->cy) return FALSE;
3404 infoPtr->cy -= infoPtr->uVisibleHeight;
3405 if (infoPtr->cy < 0) infoPtr->cy=0;
3406 break;
3407 case SB_PAGEDOWN:
3408 maxHeight=infoPtr->uTotalHeight-infoPtr->uVisibleHeight;
3409 if (infoPtr->cy == maxHeight) return FALSE;
3410 infoPtr->cy += infoPtr->uVisibleHeight;
3411 if (infoPtr->cy > maxHeight)
3412 infoPtr->cy = maxHeight;
3413 break;
3414 case SB_THUMBTRACK:
3415 infoPtr->cy = HIWORD (wParam);
3416 break;
3420 TREEVIEW_QueueRefresh (hwnd);
3421 return TRUE;
3424 static LRESULT
3425 TREEVIEW_HScroll (HWND hwnd, WPARAM wParam, LPARAM lParam)
3427 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
3428 int maxWidth;
3430 TRACE("wp %lx, lp %x\n", lParam, wParam);
3432 if (!infoPtr->uInternalStatus & TV_HSCROLL) return FALSE;
3434 switch (LOWORD (wParam)) {
3435 case SB_LINEUP:
3436 if (!infoPtr->cx) return FALSE;
3437 infoPtr->cx -= infoPtr->uRealItemHeight;
3438 if (infoPtr->cx < 0) infoPtr->cx=0;
3439 break;
3440 case SB_LINEDOWN:
3441 maxWidth=infoPtr->uTotalWidth-infoPtr->uVisibleWidth;
3442 if (infoPtr->cx == maxWidth) return FALSE;
3443 infoPtr->cx += infoPtr->uRealItemHeight; /*FIXME */
3444 if (infoPtr->cx > maxWidth)
3445 infoPtr->cx = maxWidth;
3446 break;
3447 case SB_PAGEUP:
3448 if (!infoPtr->cx) return FALSE;
3449 infoPtr->cx -= infoPtr->uVisibleWidth;
3450 if (infoPtr->cx < 0) infoPtr->cx=0;
3451 break;
3452 case SB_PAGEDOWN:
3453 maxWidth=infoPtr->uTotalWidth-infoPtr->uVisibleWidth;
3454 if (infoPtr->cx == maxWidth) return FALSE;
3455 infoPtr->cx += infoPtr->uVisibleWidth;
3456 if (infoPtr->cx > maxWidth)
3457 infoPtr->cx = maxWidth;
3458 break;
3459 case SB_THUMBTRACK:
3460 infoPtr->cx = HIWORD (wParam);
3461 break;
3465 TREEVIEW_QueueRefresh (hwnd);
3466 return TRUE;
3470 static LRESULT
3471 TREEVIEW_KeyDown (HWND hwnd, WPARAM wParam, LPARAM lParam)
3473 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
3474 HTREEITEM hNewSelection = 0;
3475 INT scrollNeeds = -1;
3476 INT cyChangeNeeds = -1;
3477 INT prevSelect = (INT)infoPtr->selectedItem;
3479 TREEVIEW_ITEM *prevItem =
3480 (prevSelect != 0 ) ?
3481 TREEVIEW_ValidItem (infoPtr, (HTREEITEM)prevSelect) :
3482 NULL;
3484 TREEVIEW_ITEM *newItem = NULL;
3486 TRACE("%x %lx\n",wParam, lParam);
3488 if (prevSelect == 0)
3489 return FALSE;
3491 switch (wParam) {
3492 case VK_UP:
3493 newItem=TREEVIEW_GetPrevListItem (infoPtr, prevItem);
3495 if (!newItem)
3496 newItem=& infoPtr->items[(INT)infoPtr->TopRootItem];
3498 hNewSelection = newItem->hItem;
3500 if (! newItem->visible)
3501 scrollNeeds = SB_LINEUP;
3502 break;
3504 case VK_DOWN:
3505 newItem=TREEVIEW_GetNextListItem (infoPtr, prevItem);
3507 if (!newItem)
3508 newItem=prevItem;
3510 hNewSelection = newItem->hItem;
3512 if (! newItem->visible)
3513 scrollNeeds = SB_LINEDOWN;
3515 break;
3517 case VK_HOME:
3518 newItem = &infoPtr->items[(INT)infoPtr->TopRootItem];
3519 hNewSelection = newItem->hItem;
3520 cyChangeNeeds = 0;
3521 break;
3523 case VK_END:
3524 newItem = &infoPtr->items[(INT)infoPtr->TopRootItem];
3525 newItem = TREEVIEW_GetLastListItem (infoPtr, newItem);
3526 hNewSelection = newItem->hItem;
3528 if (! newItem->visible)
3529 cyChangeNeeds = infoPtr->uTotalHeight-infoPtr->uVisibleHeight;
3531 break;
3533 case VK_LEFT:
3534 if ( (prevItem->cChildren > 0) && (prevItem->state & TVIS_EXPANDED) )
3536 TREEVIEW_Expand(hwnd, TVE_COLLAPSE, prevSelect );
3538 else if ((INT)prevItem->parent)
3540 newItem = (& infoPtr->items[(INT)prevItem->parent]);
3541 if (! newItem->visible)
3542 /* FIXME find a way to make this item the first visible... */
3543 newItem = NULL;
3545 hNewSelection = newItem->hItem;
3548 break;
3550 case VK_RIGHT:
3551 if ( ( prevItem->cChildren > 0) ||
3552 ( prevItem->cChildren == I_CHILDRENCALLBACK))
3554 if (! (prevItem->state & TVIS_EXPANDED))
3555 TREEVIEW_Expand(hwnd, TVE_EXPAND, prevSelect );
3556 else
3558 newItem = (& infoPtr->items[(INT)prevItem->firstChild]);
3559 hNewSelection = newItem->hItem;
3563 break;
3565 case VK_ADD:
3566 if (! (prevItem->state & TVIS_EXPANDED))
3567 TREEVIEW_Expand(hwnd, TVE_EXPAND, prevSelect );
3568 break;
3570 case VK_SUBTRACT:
3571 if (prevItem->state & TVIS_EXPANDED)
3572 TREEVIEW_Expand(hwnd, TVE_COLLAPSE, prevSelect );
3573 break;
3575 case VK_PRIOR:
3577 newItem=TREEVIEW_GetListItem(
3578 infoPtr,
3579 prevItem,
3580 -1*(TREEVIEW_GetVisibleCount(hwnd,0,0)-3));
3581 if (!newItem)
3582 newItem=prevItem;
3584 hNewSelection = newItem->hItem;
3586 if (! newItem->visible)
3587 scrollNeeds = SB_PAGEUP;
3589 break;
3591 case VK_NEXT:
3592 newItem=TREEVIEW_GetListItem(
3593 infoPtr,
3594 prevItem,
3595 TREEVIEW_GetVisibleCount(hwnd,0,0)-3);
3597 if (!newItem)
3598 newItem=prevItem;
3600 hNewSelection = newItem->hItem;
3602 if (! newItem->visible)
3603 scrollNeeds = SB_PAGEDOWN;
3605 break;
3607 case VK_BACK:
3609 case VK_RETURN:
3611 default:
3612 FIXME("%x not implemented\n", wParam);
3613 break;
3616 if (hNewSelection)
3619 This works but does not send notification...
3621 prevItem->state &= ~TVIS_SELECTED;
3622 newItem->state |= TVIS_SELECTED;
3623 infoPtr->selectedItem = hNewSelection;
3624 TREEVIEW_QueueRefresh (hwnd);
3627 if ( TREEVIEW_DoSelectItem(
3628 hwnd,
3629 TVGN_CARET,
3630 (HTREEITEM)hNewSelection,
3631 TVC_BYKEYBOARD))
3633 /* If selection change is allowed for the new item, perform scrolling */
3634 if (scrollNeeds != -1)
3635 TREEVIEW_VScroll(hwnd, scrollNeeds, 0);
3637 if (cyChangeNeeds != -1)
3638 infoPtr->cy = cyChangeNeeds;
3640 /* FIXME: Something happen in the load the in the two weeks before
3641 april 1st 1999 which makes this SetFocus mandatory otherwise, the focus
3642 is lost... However the SetFocus should not be required...*/
3644 SetFocus(hwnd);
3648 return FALSE;
3652 static LRESULT
3653 TREEVIEW_GetScrollTime (HWND hwnd)
3655 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
3657 return infoPtr->uScrollTime;
3661 static LRESULT
3662 TREEVIEW_SetScrollTime (HWND hwnd, UINT uScrollTime)
3664 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
3665 UINT uOldScrollTime = infoPtr->uScrollTime;
3667 infoPtr->uScrollTime = min (uScrollTime, 100);
3669 return uOldScrollTime;
3673 static LRESULT WINAPI
3674 TREEVIEW_WindowProc (HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
3676 if (uMsg==WM_CREATE)
3677 return TREEVIEW_Create (hwnd, wParam, lParam);
3679 if (!TREEVIEW_GetInfoPtr(hwnd))
3680 return DefWindowProcA (hwnd, uMsg, wParam, lParam);
3682 switch (uMsg) {
3684 case TVM_INSERTITEMA:
3685 return TREEVIEW_InsertItemA (hwnd, wParam, lParam);
3687 case TVM_INSERTITEMW:
3688 return TREEVIEW_InsertItemW(hwnd,wParam,lParam);;
3690 case TVM_DELETEITEM:
3691 return TREEVIEW_DeleteItem (hwnd, wParam, lParam);
3693 case TVM_EXPAND:
3694 return TREEVIEW_Expand (hwnd, wParam, lParam);
3696 case TVM_GETITEMRECT:
3697 return TREEVIEW_GetItemRect (hwnd, wParam, lParam);
3699 case TVM_GETCOUNT:
3700 return TREEVIEW_GetCount (hwnd, wParam, lParam);
3702 case TVM_GETINDENT:
3703 return TREEVIEW_GetIndent (hwnd);
3705 case TVM_SETINDENT:
3706 return TREEVIEW_SetIndent (hwnd, wParam);
3708 case TVM_GETIMAGELIST:
3709 return TREEVIEW_GetImageList (hwnd, wParam, lParam);
3711 case TVM_SETIMAGELIST:
3712 return TREEVIEW_SetImageList (hwnd, wParam, lParam);
3714 case TVM_GETNEXTITEM:
3715 return TREEVIEW_GetNextItem (hwnd, wParam, lParam);
3717 case TVM_SELECTITEM:
3718 return TREEVIEW_SelectItem (hwnd, wParam, lParam);
3720 case TVM_GETITEMA:
3721 return TREEVIEW_GetItemA (hwnd, wParam, lParam);
3723 case TVM_GETITEMW:
3724 FIXME("Unimplemented msg TVM_GETITEMW\n");
3725 return 0;
3727 case TVM_SETITEMA:
3728 return TREEVIEW_SetItemA (hwnd, wParam, lParam);
3730 case TVM_SETITEMW:
3731 FIXME("Unimplemented msg TVM_SETITEMW\n");
3732 return 0;
3734 case TVM_EDITLABELA:
3735 FIXME("Unimplemented msg TVM_EDITLABELA \n");
3736 return 0;
3738 case TVM_EDITLABELW:
3739 FIXME("Unimplemented msg TVM_EDITLABELW \n");
3740 return 0;
3742 case TVM_GETEDITCONTROL:
3743 return TREEVIEW_GetEditControl (hwnd);
3745 case TVM_GETVISIBLECOUNT:
3746 return TREEVIEW_GetVisibleCount (hwnd, wParam, lParam);
3748 case TVM_HITTEST:
3749 return TREEVIEW_HitTest (hwnd, lParam);
3751 case TVM_CREATEDRAGIMAGE:
3752 return TREEVIEW_CreateDragImage (hwnd, wParam, lParam);
3754 case TVM_SORTCHILDREN:
3755 return TREEVIEW_SortChildren (hwnd, wParam, lParam);
3757 case TVM_ENSUREVISIBLE:
3758 FIXME("Unimplemented msg TVM_ENSUREVISIBLE\n");
3759 return 0;
3761 case TVM_SORTCHILDRENCB:
3762 return TREEVIEW_SortChildrenCB(hwnd, wParam, lParam);
3764 case TVM_ENDEDITLABELNOW:
3765 return TREEVIEW_EndEditLabelNow (hwnd, wParam, lParam);
3767 case TVM_GETISEARCHSTRINGA:
3768 FIXME("Unimplemented msg TVM_GETISEARCHSTRINGA\n");
3769 return 0;
3771 case TVM_GETISEARCHSTRINGW:
3772 FIXME("Unimplemented msg TVM_GETISEARCHSTRINGW\n");
3773 return 0;
3775 case TVM_GETTOOLTIPS:
3776 return TREEVIEW_GetToolTips (hwnd);
3778 case TVM_SETTOOLTIPS:
3779 return TREEVIEW_SetToolTips (hwnd, wParam);
3781 case TVM_SETINSERTMARK:
3782 return TREEVIEW_SetInsertMark (hwnd,wParam, lParam);
3784 case TVM_SETITEMHEIGHT:
3785 return TREEVIEW_SetItemHeight (hwnd, wParam);
3787 case TVM_GETITEMHEIGHT:
3788 return TREEVIEW_GetItemHeight (hwnd);
3790 case TVM_SETBKCOLOR:
3791 return TREEVIEW_SetBkColor (hwnd, wParam, lParam);
3793 case TVM_SETTEXTCOLOR:
3794 return TREEVIEW_SetTextColor (hwnd, wParam, lParam);
3796 case TVM_GETBKCOLOR:
3797 return TREEVIEW_GetBkColor (hwnd);
3799 case TVM_GETTEXTCOLOR:
3800 return TREEVIEW_GetTextColor (hwnd);
3802 case TVM_SETSCROLLTIME:
3803 return TREEVIEW_SetScrollTime (hwnd, (UINT)wParam);
3805 case TVM_GETSCROLLTIME:
3806 return TREEVIEW_GetScrollTime (hwnd);
3808 case TVM_GETITEMSTATE:
3809 return TREEVIEW_GetItemState (hwnd,wParam, lParam);
3811 case TVM_GETLINECOLOR:
3812 return TREEVIEW_GetLineColor (hwnd,wParam, lParam);
3814 case TVM_SETLINECOLOR:
3815 return TREEVIEW_SetLineColor (hwnd,wParam, lParam);
3817 case TVM_SETINSERTMARKCOLOR:
3818 return TREEVIEW_SetInsertMarkColor (hwnd,wParam, lParam);
3820 case TVM_GETINSERTMARKCOLOR:
3821 return TREEVIEW_GetInsertMarkColor (hwnd,wParam, lParam);
3823 case TVM_SETUNICODEFORMAT:
3824 FIXME("Unimplemented msg TVM_SETUNICODEFORMAT\n");
3825 return 0;
3827 case TVM_GETUNICODEFORMAT:
3828 FIXME("Unimplemented msg TVM_GETUNICODEFORMAT\n");
3829 return 0;
3831 case WM_COMMAND:
3832 return TREEVIEW_Command (hwnd, wParam, lParam);
3834 case WM_DESTROY:
3835 return TREEVIEW_Destroy (hwnd);
3837 /* case WM_ENABLE: */
3839 case WM_ERASEBKGND:
3840 return TREEVIEW_EraseBackground (hwnd, wParam, lParam);
3842 case WM_GETDLGCODE:
3843 return DLGC_WANTARROWS | DLGC_WANTCHARS;
3845 case WM_PAINT:
3846 return TREEVIEW_Paint (hwnd, wParam, lParam);
3848 case WM_GETFONT:
3849 return TREEVIEW_GetFont (hwnd, wParam, lParam);
3851 case WM_SETFONT:
3852 return TREEVIEW_SetFont (hwnd, wParam, lParam);
3854 case WM_KEYDOWN:
3855 return TREEVIEW_KeyDown (hwnd, wParam, lParam);
3857 case WM_SETFOCUS:
3858 return TREEVIEW_SetFocus (hwnd, wParam, lParam);
3860 case WM_KILLFOCUS:
3861 return TREEVIEW_KillFocus (hwnd, wParam, lParam);
3863 case WM_LBUTTONDOWN:
3864 return TREEVIEW_LButtonDown (hwnd, wParam, lParam);
3866 case WM_LBUTTONUP:
3867 return TREEVIEW_LButtonUp (hwnd, wParam, lParam);
3869 case WM_LBUTTONDBLCLK:
3870 return TREEVIEW_LButtonDoubleClick (hwnd, wParam, lParam);
3872 case WM_RBUTTONDOWN:
3873 return TREEVIEW_RButtonDown (hwnd, wParam, lParam);
3875 case WM_RBUTTONUP:
3876 return TREEVIEW_RButtonUp (hwnd, wParam, lParam);
3878 case WM_MOUSEMOVE:
3879 return TREEVIEW_MouseMove (hwnd, wParam, lParam);
3881 case WM_STYLECHANGED:
3882 return TREEVIEW_StyleChanged (hwnd, wParam, lParam);
3884 /* case WM_SYSCOLORCHANGE: */
3885 /* case WM_SETREDRAW: */
3887 case WM_TIMER:
3888 return TREEVIEW_HandleTimer (hwnd, wParam, lParam);
3890 case WM_SIZE:
3891 return TREEVIEW_Size (hwnd, wParam,lParam);
3893 case WM_HSCROLL:
3894 return TREEVIEW_HScroll (hwnd, wParam, lParam);
3895 case WM_VSCROLL:
3896 return TREEVIEW_VScroll (hwnd, wParam, lParam);
3898 case WM_DRAWITEM:
3899 TRACE ("drawItem\n");
3900 return DefWindowProcA (hwnd, uMsg, wParam, lParam);
3902 default:
3903 if (uMsg >= WM_USER)
3904 FIXME("Unknown msg %04x wp=%08x lp=%08lx\n",
3905 uMsg, wParam, lParam);
3906 return DefWindowProcA (hwnd, uMsg, wParam, lParam);
3908 return 0;
3912 VOID
3913 TREEVIEW_Register (void)
3915 WNDCLASSA wndClass;
3917 TRACE("\n");
3919 ZeroMemory (&wndClass, sizeof(WNDCLASSA));
3920 wndClass.style = CS_GLOBALCLASS | CS_DBLCLKS;
3921 wndClass.lpfnWndProc = (WNDPROC)TREEVIEW_WindowProc;
3922 wndClass.cbClsExtra = 0;
3923 wndClass.cbWndExtra = sizeof(TREEVIEW_INFO *);
3924 wndClass.hCursor = LoadCursorA (0, IDC_ARROWA);
3925 wndClass.hbrBackground = 0;
3926 wndClass.lpszClassName = WC_TREEVIEWA;
3928 RegisterClassA (&wndClass);
3932 VOID
3933 TREEVIEW_Unregister (void)
3935 UnregisterClassA (WC_TREEVIEWA, (HINSTANCE)NULL);