Fixed hash function.
[wine/dcerpc.git] / dlls / comctl32 / treeview.c
blob8fc9f0b124882df4904bb3da3733b6fa33c0789b
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 "debugtools.h"
49 DEFAULT_DEBUG_CHANNEL(treeview)
51 /* ffs should be in <string.h>. */
53 /* Defines, since they do not need to return previous state, and nr
54 * has no side effects in this file.
56 #define tv_test_bit(nr,bf) (((LPBYTE)bf)[nr>>3]&(1<<(nr&7)))
57 #define tv_set_bit(nr,bf) ((LPBYTE)bf)[nr>>3]|=(1<<(nr&7))
58 #define tv_clear_bit(nr,bf) ((LPBYTE)bf)[nr>>3]&=~(1<<(nr&7))
61 #define TREEVIEW_GetInfoPtr(hwnd) \
62 ((TREEVIEW_INFO *) GetWindowLongA( hwnd, 0))
64 static BOOL
65 TREEVIEW_SendSimpleNotify (HWND hwnd, UINT code);
66 static BOOL
67 TREEVIEW_SendTreeviewNotify (HWND hwnd, UINT code, UINT action,
68 HTREEITEM oldItem, HTREEITEM newItem);
69 static BOOL
70 TREEVIEW_SendTreeviewDnDNotify (HWND hwnd, UINT code, HTREEITEM dragItem,
71 POINT pt);
72 static BOOL
73 TREEVIEW_SendDispInfoNotify (HWND hwnd, TREEVIEW_ITEM *wineItem,
74 UINT code, UINT what);
75 static BOOL
76 TREEVIEW_SendCustomDrawNotify (HWND hwnd, DWORD dwDrawStage, HDC hdc,
77 RECT rc);
78 static BOOL
79 TREEVIEW_SendCustomDrawItemNotify (HWND hwnd, HDC hdc,
80 TREEVIEW_ITEM *tvItem, UINT uItemDrawState);
81 static LRESULT
82 TREEVIEW_DoSelectItem (HWND hwnd, INT action, HTREEITEM newSelect, INT cause);
83 static void
84 TREEVIEW_Refresh (HWND hwnd);
86 static LRESULT CALLBACK
87 TREEVIEW_Edit_SubclassProc (HWND hwnd, UINT uMsg, WPARAM wParam,
88 LPARAM lParam);
90 LRESULT WINAPI
91 TREEVIEW_EndEditLabelNow (HWND hwnd, WPARAM wParam, LPARAM lParam);
96 /* helper functions. Work with the assumption that validity of operands
97 is checked beforehand, and that tree state is valid. */
99 /* FIXME: MS documentation says `GetNextVisibleItem' returns NULL
100 if not succesfull'. Probably only applies to derefencing infoPtr
101 (ie we are offered a valid treeview structure)
102 and not whether there is a next `visible' child.
103 FIXME: check other failures.
106 /***************************************************************************
107 * This method returns the TREEVIEW_ITEM object given the handle
109 static TREEVIEW_ITEM* TREEVIEW_ValidItem(
110 TREEVIEW_INFO *infoPtr,
111 HTREEITEM handle)
113 if ((!handle) || (handle>infoPtr->uMaxHandle))
114 return NULL;
116 if (tv_test_bit ((INT)handle, infoPtr->freeList))
117 return NULL;
119 return &infoPtr->items[(INT)handle];
122 /***************************************************************************
123 * This method returns the last expanded child item of a tree node
125 static TREEVIEW_ITEM *TREEVIEW_GetLastListItem(
126 TREEVIEW_INFO *infoPtr,
127 TREEVIEW_ITEM *tvItem)
130 TREEVIEW_ITEM *wineItem = tvItem;
133 * Get this item last sibling
135 while (wineItem->sibling)
136 wineItem=& infoPtr->items [(INT)wineItem->sibling];
139 * If the last sibling has expanded children, restart.
141 if ( ( wineItem->cChildren > 0 ) && ( wineItem->state & TVIS_EXPANDED) )
142 return TREEVIEW_GetLastListItem(
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_DELETEITEM, 0, (HTREEITEM)kill, 0);
288 if (killItem->firstChild)
289 TREEVIEW_RemoveAllChildren (hwnd, killItem);
290 kill=(INT)killItem->sibling;
293 if (parentItem->cChildren>0) {
294 infoPtr->uNumItems -= parentItem->cChildren;
295 parentItem->firstChild = 0;
296 parentItem->cChildren = 0;
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_DELETEITEM, 0, (HTREEITEM)iItem, 0);
319 if (wineItem->firstChild)
320 TREEVIEW_RemoveAllChildren (hwnd,wineItem);
322 if (wineItem->parent) {
323 parentItem=& infoPtr->items [(INT)wineItem->parent];
324 switch (parentItem->cChildren) {
325 case I_CHILDRENCALLBACK:
326 FIXME("we don't handle I_CHILDRENCALLBACK yet\n");
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_DELETEITEM, 0, killItem->hItem, 0);
373 if (infoPtr->uNumPtrsAlloced) {
374 COMCTL32_Free (infoPtr->items);
375 COMCTL32_Free (infoPtr->freeList);
376 infoPtr->uNumItems=0;
377 infoPtr->uNumPtrsAlloced=0;
378 infoPtr->uMaxHandle=0;
388 static LRESULT
389 TREEVIEW_GetImageList (HWND hwnd, WPARAM wParam, LPARAM lParam)
391 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
393 TRACE("\n");
394 if (infoPtr==NULL) return 0;
396 if ((INT)wParam == TVSIL_NORMAL)
397 return (LRESULT) infoPtr->himlNormal;
398 if ((INT)wParam == TVSIL_STATE)
399 return (LRESULT) infoPtr->himlState;
401 return 0;
404 static LRESULT
405 TREEVIEW_SetImageList (HWND hwnd, WPARAM wParam, LPARAM lParam)
407 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
408 HIMAGELIST himlTemp;
410 TRACE("\n");
411 switch ((INT)wParam) {
412 case TVSIL_NORMAL:
413 himlTemp = infoPtr->himlNormal;
414 infoPtr->himlNormal = (HIMAGELIST)lParam;
415 return (LRESULT)himlTemp;
417 case TVSIL_STATE:
418 himlTemp = infoPtr->himlState;
419 infoPtr->himlState = (HIMAGELIST)lParam;
420 return (LRESULT)himlTemp;
423 return (LRESULT)NULL;
428 static LRESULT
429 TREEVIEW_SetItemHeight (HWND hwnd, WPARAM wParam)
431 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
432 INT cx,cy,prevHeight=infoPtr->uItemHeight;
433 HDC hdc;
435 TRACE("\n");
436 if (wParam==-1) {
437 hdc=GetDC (hwnd);
438 infoPtr->uItemHeight=-1;
439 return prevHeight;
442 ImageList_GetIconSize (infoPtr->himlNormal, &cx, &cy);
444 if (wParam>cy) cy=wParam;
445 infoPtr->uItemHeight=cy;
447 if (!( GetWindowLongA( hwnd, GWL_STYLE) & TVS_NONEVENHEIGHT))
448 infoPtr->uItemHeight = (INT) wParam & 0xfffffffe;
449 return prevHeight;
452 static LRESULT
453 TREEVIEW_GetItemHeight (HWND hwnd)
455 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
457 TRACE("\n");
458 return infoPtr->uItemHeight;
461 static LRESULT
462 TREEVIEW_GetLineColor (HWND hwnd, WPARAM wParam, LPARAM lParam)
464 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
466 TRACE("\n");
467 return (LRESULT) infoPtr->clrLine;
470 static LRESULT
471 TREEVIEW_SetLineColor (HWND hwnd, WPARAM wParam, LPARAM lParam)
473 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
474 COLORREF prevColor=infoPtr->clrLine;
476 TRACE("\n");
477 infoPtr->clrLine=(COLORREF) lParam;
478 return (LRESULT) prevColor;
481 static LRESULT
482 TREEVIEW_SetTextColor (HWND hwnd, WPARAM wParam, LPARAM lParam)
484 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
485 COLORREF prevColor=infoPtr->clrText;
487 TRACE("\n");
488 infoPtr->clrText=(COLORREF) lParam;
489 return (LRESULT) prevColor;
492 static LRESULT
493 TREEVIEW_GetBkColor (HWND hwnd)
495 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
497 TRACE("\n");
498 return (LRESULT) infoPtr->clrBk;
501 static LRESULT
502 TREEVIEW_SetBkColor (HWND hwnd, WPARAM wParam, LPARAM lParam)
504 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
505 COLORREF prevColor=infoPtr->clrBk;
507 TRACE("\n");
508 infoPtr->clrBk=(COLORREF) lParam;
509 return (LRESULT) prevColor;
512 static LRESULT
513 TREEVIEW_GetTextColor (HWND hwnd)
515 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
517 TRACE("\n");
518 return (LRESULT) infoPtr->clrText;
522 /* cdmode: custom draw mode as received from app. in first NMCUSTOMDRAW
523 notification */
525 #define TREEVIEW_LEFT_MARGIN 8
528 static void
529 TREEVIEW_DrawItem (HWND hwnd, HDC hdc, TREEVIEW_ITEM *wineItem)
531 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
532 DWORD dwStyle = GetWindowLongA (hwnd, GWL_STYLE);
533 INT center,xpos,cx,cy, cditem, drawmode;
534 HFONT hOldFont;
535 UINT uTextJustify = DT_LEFT;
536 RECT r;
539 if (wineItem->state & TVIS_BOLD)
540 hOldFont = SelectObject (hdc, infoPtr->hBoldFont);
541 else
542 hOldFont = SelectObject (hdc, infoPtr->hFont);
544 cditem=0;
545 TRACE ("cdmode:%x\n",infoPtr->cdmode);
546 if (infoPtr->cdmode & CDRF_NOTIFYITEMDRAW) {
547 drawmode=CDDS_ITEMPREPAINT;
549 if (infoPtr->cdmode & CDRF_NOTIFYSUBITEMDRAW)
550 drawmode|=CDDS_SUBITEM;
552 cditem=TREEVIEW_SendCustomDrawItemNotify (hwnd, hdc, wineItem, drawmode);
554 TRACE("cditem:%d\n",cditem);
556 if (cditem & CDRF_SKIPDEFAULT)
557 return;
561 * Set drawing starting points
563 r = wineItem->rect; /* this item rectangle */
564 center = (r.top+r.bottom)/2; /* this item vertical center */
565 xpos = r.left + TREEVIEW_LEFT_MARGIN;/* horizontal starting point */
568 * Display the tree hierarchy
570 if ( dwStyle & TVS_HASLINES)
573 * Write links to parent node
574 * we draw the L starting from the child to the parent
576 * points[0] is attached to the current item
577 * points[1] is the L corner
578 * points[2] is attached to the parent or the up sibling
580 if ( dwStyle & TVS_LINESATROOT)
582 TREEVIEW_ITEM *upNode = NULL;
583 BOOL hasParentOrSibling = TRUE;
584 RECT upRect = {0,0,0,0};
585 HPEN hOldPen, hnewPen;
586 POINT points[3];
588 * determine the target location of the line at root, either be linked
589 * to the up sibling or to the parent node.
591 if (wineItem->upsibling)
592 upNode = TREEVIEW_ValidItem (infoPtr, wineItem->upsibling);
593 else if (wineItem->parent)
594 upNode = TREEVIEW_ValidItem (infoPtr, wineItem->parent);
595 else
596 hasParentOrSibling = FALSE;
598 if (upNode)
599 upRect = upNode->rect;
601 if ( wineItem->iLevel == 0 )
603 points[2].x = points[1].x = upRect.left+8;
604 points[0].x = points[2].x + 10;
605 points[2].y = upRect.bottom-3;
606 points[1].y = points[0].y = center;
608 else
610 points[2].x = points[1].x = 8 + (20*wineItem->iLevel);
611 points[2].y = ( upNode->cChildren == 0) ?
612 upRect.top : /* is linked to the "L" above */
613 ( wineItem->upsibling != NULL) ?
614 upRect.bottom-3: /* is linked to an icon */
615 upRect.bottom+1; /* is linked to a +/- box */
616 points[1].y = points[0].y = center;
617 points[0].x = points[1].x + 10;
621 * Get a dotted pen
623 hnewPen = CreatePen(PS_DOT, 0, infoPtr->clrLine);
624 hOldPen = SelectObject( hdc, hnewPen );
626 if (hasParentOrSibling)
627 Polyline (hdc,points,3);
628 else
629 Polyline (hdc,points,2);
631 DeleteObject(hnewPen);
632 SelectObject(hdc, hOldPen);
637 * Display the (+/-) signs
639 if (wineItem->iLevel != 0)/* update position only for non root node */
640 xpos+=(5*wineItem->iLevel);
642 if (( dwStyle & TVS_HASBUTTONS) && ( dwStyle & TVS_HASLINES))
644 if ( (wineItem->cChildren) ||
645 (wineItem->cChildren == I_CHILDRENCALLBACK))
647 /* Setup expand box coordinate to facilitate the LMBClick handling */
648 wineItem->expandBox.left = xpos-4;
649 wineItem->expandBox.top = center-4;
650 wineItem->expandBox.right = xpos+5;
651 wineItem->expandBox.bottom = center+5;
653 Rectangle (
654 hdc,
655 wineItem->expandBox.left,
656 wineItem->expandBox.top ,
657 wineItem->expandBox.right,
658 wineItem->expandBox.bottom);
660 MoveToEx (hdc, xpos-2, center, NULL);
661 LineTo (hdc, xpos+3, center);
663 if (!(wineItem->state & TVIS_EXPANDED)) {
664 MoveToEx (hdc, xpos, center-2, NULL);
665 LineTo (hdc, xpos, center+3);
671 * Display the image assiciated with this item
673 xpos += 13; /* update position */
674 if (wineItem->mask & (TVIF_IMAGE|TVIF_SELECTEDIMAGE)) {
675 INT imageIndex;
676 HIMAGELIST *himlp = NULL;
678 if (infoPtr->himlNormal)
679 himlp=&infoPtr->himlNormal; /* get the image list */
681 if ( (wineItem->state & TVIS_SELECTED) &&
682 (wineItem->iSelectedImage)) {
684 /* State images are displayed to the left of the Normal image*/
685 if (infoPtr->himlState)
686 himlp=&infoPtr->himlState;
688 /* The item is curently selected */
689 if (wineItem->iSelectedImage == I_IMAGECALLBACK)
690 TREEVIEW_SendDispInfoNotify (
691 hwnd,
692 wineItem,
693 TVN_GETDISPINFO,
694 TVIF_SELECTEDIMAGE);
696 imageIndex = wineItem->iSelectedImage;
698 } else {
699 /* This item is not selected */
700 if (wineItem->iImage == I_IMAGECALLBACK)
701 TREEVIEW_SendDispInfoNotify (
702 hwnd,
703 wineItem,
704 TVN_GETDISPINFO,
705 TVIF_IMAGE);
707 imageIndex = wineItem->iImage;
710 if (himlp)
712 /* We found an image to display? Draw it. */
713 ImageList_Draw (
714 *himlp,
715 wineItem->iImage,
716 hdc,
717 xpos-2,
718 r.top+1,
719 ILD_NORMAL);
721 ImageList_GetIconSize (*himlp, &cx, &cy);
722 xpos+=cx;
727 * Display the text assiciated with this item
729 r.left=xpos;
730 if ((wineItem->mask & TVIF_TEXT) && (wineItem->pszText))
732 COLORREF oldBkColor = 0;
733 COLORREF oldTextColor = 0;
734 INT oldBkMode;
736 r.left += 3;
737 r.right -= 3;
739 wineItem->text.left = r.left;
740 wineItem->text.right = r.right;
741 wineItem->text.top = r.top;
742 wineItem->text.bottom= r.bottom;
744 if (wineItem->state & (TVIS_SELECTED | TVIS_DROPHILITED) ) {
745 oldBkMode = SetBkMode (hdc, OPAQUE);
746 oldBkColor = SetBkColor (hdc, GetSysColor( COLOR_HIGHLIGHT));
747 oldTextColor = SetTextColor(hdc, GetSysColor( COLOR_HIGHLIGHTTEXT));
749 else
751 oldBkMode = SetBkMode(hdc, TRANSPARENT);
752 oldTextColor = SetTextColor(hdc, GetSysColor( COLOR_WINDOWTEXT));
755 if (wineItem->pszText== LPSTR_TEXTCALLBACKA) {
756 TRACE("LPSTR_TEXTCALLBACK\n");
757 TREEVIEW_SendDispInfoNotify (hwnd, wineItem, TVN_GETDISPINFO, TVIF_TEXT);
760 /* Draw it */
761 DrawTextA (
762 hdc,
763 wineItem->pszText,
764 lstrlenA(wineItem->pszText),
765 &wineItem->text,
766 uTextJustify | DT_VCENTER | DT_SINGLELINE );
768 /* Obtain the text coordinate */
769 DrawTextA (
770 hdc,
771 wineItem->pszText,
772 lstrlenA(wineItem->pszText),
773 &wineItem->text,
774 uTextJustify | DT_VCENTER | DT_SINGLELINE | DT_CALCRECT);
776 /* Restore the hdc state */
777 SetTextColor( hdc, oldTextColor);
779 if (oldBkMode != TRANSPARENT)
780 SetBkMode(hdc, oldBkMode);
781 if (wineItem->state & (TVIS_SELECTED | TVIS_DROPHILITED))
782 SetBkColor (hdc, oldBkColor);
784 /* Draw the box arround the selected item */
785 if (wineItem->state & TVIS_SELECTED )
787 HPEN hnewPen = CreatePen(PS_DOT, 0, GetSysColor(COLOR_WINDOWTEXT) );
788 HPEN hOldPen = SelectObject( hdc, hnewPen );
789 POINT points[4];
791 points[0].x = wineItem->text.left-1;
792 points[0].y = wineItem->text.top+1;
793 points[1].x = wineItem->text.right;
794 points[1].y = wineItem->text.top+1;
795 points[2].x = wineItem->text.right;
796 points[2].y = wineItem->text.bottom;
797 points[3].x = wineItem->text.left-1;
798 points[3].y = wineItem->text.bottom;
800 Polyline (hdc,points,4);
802 DeleteObject(hnewPen);
803 SelectObject(hdc, hOldPen);
807 if (cditem & CDRF_NOTIFYPOSTPAINT)
808 TREEVIEW_SendCustomDrawItemNotify (hwnd, hdc, wineItem, CDDS_ITEMPOSTPAINT);
810 SelectObject (hdc, hOldFont);
813 static LRESULT
814 TREEVIEW_GetItemRect (HWND hwnd, WPARAM wParam, LPARAM lParam)
816 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
817 TREEVIEW_ITEM *wineItem;
818 HTREEITEM *iItem;
819 LPRECT lpRect = (LPRECT)lParam;
821 TRACE("\n");
823 * validate parameters
825 if ( (infoPtr==NULL) || (lpRect == NULL) )
826 return FALSE;
828 if (infoPtr->Timer & TV_REFRESH_TIMER_SET)
829 TREEVIEW_Refresh (hwnd); /* we want a rect for the current view */
832 * retrive the item ptr
834 iItem = (HTREEITEM *) lParam;
835 wineItem = TREEVIEW_ValidItem (infoPtr, *iItem);
836 if ((!wineItem) || (!wineItem->visible))
837 return FALSE;
840 * If wParam is TRUE return the text size otherwise return
841 * the whole item size
843 if ((INT) wParam) {
844 lpRect->left = wineItem->text.left;
845 lpRect->right = wineItem->text.right;
846 lpRect->bottom = wineItem->text.bottom;
847 lpRect->top = wineItem->text.top;
848 } else {
849 lpRect->left = wineItem->rect.left;
850 lpRect->right = wineItem->rect.right;
851 lpRect->bottom = wineItem->rect.bottom;
852 lpRect->top = wineItem->rect.top;
855 TRACE("[L:%d R:%d T:%d B:%d]\n",
856 lpRect->left,lpRect->right,
857 lpRect->top,lpRect->bottom);
859 return TRUE;
862 static LRESULT
863 TREEVIEW_GetVisibleCount (HWND hwnd, WPARAM wParam, LPARAM lParam)
866 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
868 return (LRESULT) infoPtr->uVisibleHeight / infoPtr->uRealItemHeight;
873 static LRESULT
874 TREEVIEW_SetItemA (HWND hwnd, WPARAM wParam, LPARAM lParam)
876 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
877 TREEVIEW_ITEM *wineItem;
878 TVITEMEXA *tvItem;
879 INT iItem,len;
881 tvItem=(LPTVITEMEXA) lParam;
882 iItem=(INT)tvItem->hItem;
883 TRACE("item %d,mask %x\n",iItem,tvItem->mask);
885 wineItem = TREEVIEW_ValidItem (infoPtr, (HTREEITEM)iItem);
886 if (!wineItem) return FALSE;
888 if (tvItem->mask & TVIF_CHILDREN) {
889 wineItem->cChildren=tvItem->cChildren;
892 if (tvItem->mask & TVIF_IMAGE) {
893 wineItem->iImage=tvItem->iImage;
896 if (tvItem->mask & TVIF_INTEGRAL) {
897 wineItem->iIntegral=tvItem->iIntegral;
900 if (tvItem->mask & TVIF_PARAM) {
901 wineItem->lParam=tvItem->lParam;
904 if (tvItem->mask & TVIF_SELECTEDIMAGE) {
905 wineItem->iSelectedImage=tvItem->iSelectedImage;
908 if (tvItem->mask & TVIF_STATE) {
909 wineItem->state=tvItem->state & tvItem->stateMask;
912 if (tvItem->mask & TVIF_TEXT) {
913 if (tvItem->pszText!=LPSTR_TEXTCALLBACKA) {
914 len=lstrlenA (tvItem->pszText);
915 if (len>wineItem->cchTextMax)
916 wineItem->pszText= COMCTL32_ReAlloc (wineItem->pszText, len+1);
917 lstrcpynA (wineItem->pszText, tvItem->pszText,len);
918 } else {
919 if (wineItem->cchTextMax) {
920 COMCTL32_Free (wineItem->pszText);
921 wineItem->cchTextMax=0;
923 wineItem->pszText=LPSTR_TEXTCALLBACKA;
927 wineItem->mask |= tvItem->mask;
929 return TRUE;
932 static LRESULT
933 TREEVIEW_GetItemState (HWND hwnd, WPARAM wParam, LPARAM lParam)
936 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
937 TREEVIEW_ITEM *wineItem;
939 wineItem = TREEVIEW_ValidItem (infoPtr, (HTREEITEM)wParam);
940 if (!wineItem) return 0;
942 return (wineItem->state & lParam);
949 static void
950 TREEVIEW_Refresh (HWND hwnd)
953 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
954 TEXTMETRICA tm;
955 HBRUSH hbrBk;
956 RECT rect;
957 HDC hdc;
958 INT iItem, indent, x, y, cx, height, itemHeight;
959 INT viewtop,viewbottom,viewleft,viewright;
960 TREEVIEW_ITEM *wineItem, *prevItem;
962 TRACE("\n");
964 hdc=GetDC (hwnd);
966 if (infoPtr->Timer & TV_REFRESH_TIMER_SET) {
967 KillTimer (hwnd, TV_REFRESH_TIMER);
968 infoPtr->Timer &= ~TV_REFRESH_TIMER_SET;
972 GetClientRect (hwnd, &rect);
973 if ((rect.left-rect.right ==0) || (rect.top-rect.bottom==0)) return;
975 infoPtr->cdmode=TREEVIEW_SendCustomDrawNotify
976 (hwnd, CDDS_PREPAINT, hdc, rect);
978 if (infoPtr->cdmode==CDRF_SKIPDEFAULT) {
979 ReleaseDC (hwnd, hdc);
980 return;
983 infoPtr->uVisibleHeight= rect.bottom-rect.top;
984 infoPtr->uVisibleWidth= rect.right-rect.left;
986 viewtop=infoPtr->cy;
987 viewbottom=infoPtr->cy + rect.bottom-rect.top;
988 viewleft=infoPtr->cx;
989 viewright=infoPtr->cx + rect.right-rect.left;
991 /* draw background */
993 hbrBk = CreateSolidBrush (infoPtr->clrBk);
994 FillRect(hdc, &rect, hbrBk);
995 DeleteObject(hbrBk);
997 iItem=(INT)infoPtr->TopRootItem;
998 infoPtr->firstVisible=0;
999 wineItem=NULL;
1000 indent=0;
1001 x=y=0;
1002 TRACE("[%d %d %d %d]\n",viewtop,viewbottom,viewleft,viewright);
1004 while (iItem) {
1005 prevItem=wineItem;
1006 wineItem= & infoPtr->items[iItem];
1007 wineItem->iLevel=indent;
1009 ImageList_GetIconSize (infoPtr->himlNormal, &cx, &itemHeight);
1010 if (infoPtr->uItemHeight>itemHeight)
1011 itemHeight=infoPtr->uItemHeight;
1013 GetTextMetricsA (hdc, &tm);
1014 if ((tm.tmHeight + tm.tmExternalLeading) > itemHeight)
1015 itemHeight=tm.tmHeight + tm.tmExternalLeading;
1017 infoPtr->uRealItemHeight=itemHeight;
1020 /* FIXME: remove this in later stage */
1022 if (wineItem->pszText!=LPSTR_TEXTCALLBACK32A)
1023 TRACE (treeview, "%d %d [%d %d %d %d] (%s)\n",y,x,
1024 wineItem->rect.top, wineItem->rect.bottom,
1025 wineItem->rect.left, wineItem->rect.right,
1026 wineItem->pszText);
1027 else
1028 TRACE (treeview, "%d [%d %d %d %d] (CALLBACK)\n",
1029 wineItem->hItem,
1030 wineItem->rect.top, wineItem->rect.bottom,
1031 wineItem->rect.left, wineItem->rect.right);
1034 height=itemHeight * wineItem->iIntegral +1;
1035 if ((y >= viewtop) && (y <= viewbottom) &&
1036 (x >= viewleft ) && (x <= viewright)) {
1037 wineItem->visible = TRUE;
1038 wineItem->rect.top = y - infoPtr->cy + rect.top;
1039 wineItem->rect.bottom = wineItem->rect.top + height ;
1040 wineItem->rect.left = x - infoPtr->cx + rect.left;
1041 wineItem->rect.right = rect.right;
1042 if (!infoPtr->firstVisible)
1043 infoPtr->firstVisible=wineItem->hItem;
1044 TREEVIEW_DrawItem (hwnd, hdc, wineItem);
1046 else {
1047 wineItem->visible = FALSE;
1048 wineItem->rect.left = wineItem->rect.top = 0;
1049 wineItem->rect.right= wineItem->rect.bottom = 0;
1050 wineItem->text.left = wineItem->text.top = 0;
1051 wineItem->text.right= wineItem->text.bottom = 0;
1054 /* look up next item */
1056 if ((wineItem->firstChild) && (wineItem->state & TVIS_EXPANDED)) {
1057 iItem=(INT)wineItem->firstChild;
1058 indent++;
1059 x+=infoPtr->uIndent;
1060 if (x>infoPtr->uTotalWidth)
1061 infoPtr->uTotalWidth=x;
1063 else {
1064 iItem=(INT)wineItem->sibling;
1065 while ((!iItem) && (indent>0)) {
1066 indent--;
1067 x-=infoPtr->uIndent;
1068 prevItem=wineItem;
1069 wineItem=&infoPtr->items[(INT)wineItem->parent];
1070 iItem=(INT)wineItem->sibling;
1073 y +=height;
1074 } /* while */
1076 /* FIXME: infoPtr->uTotalWidth should also take item label into account */
1077 /* FIXME: or should query item sizes (ie check CDRF_NEWFONT) */
1079 infoPtr->uTotalHeight=y;
1080 if (y >= (viewbottom-viewtop)) {
1081 if (!(infoPtr->uInternalStatus & TV_VSCROLL))
1082 ShowScrollBar (hwnd, SB_VERT, TRUE);
1083 infoPtr->uInternalStatus |=TV_VSCROLL;
1084 SetScrollRange (hwnd, SB_VERT, 0,
1085 y - infoPtr->uVisibleHeight, FALSE);
1086 SetScrollPos (hwnd, SB_VERT, infoPtr->cy, TRUE);
1088 else {
1089 if (infoPtr->uInternalStatus & TV_VSCROLL)
1090 ShowScrollBar (hwnd, SB_VERT, FALSE);
1091 infoPtr->uInternalStatus &= ~TV_VSCROLL;
1095 if (infoPtr->cdmode & CDRF_NOTIFYPOSTPAINT)
1096 infoPtr->cdmode=TREEVIEW_SendCustomDrawNotify
1097 (hwnd, CDDS_POSTPAINT, hdc, rect);
1099 ReleaseDC (hwnd, hdc);
1100 TRACE("done\n");
1104 static LRESULT
1105 TREEVIEW_HandleTimer (HWND hwnd, WPARAM wParam, LPARAM lParam)
1107 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
1109 TRACE(" %d\n",wParam);
1110 if (!infoPtr) return FALSE;
1112 switch (wParam) {
1113 case TV_REFRESH_TIMER:
1114 KillTimer (hwnd, TV_REFRESH_TIMER);
1115 infoPtr->Timer &= ~TV_REFRESH_TIMER_SET;
1116 InvalidateRect(hwnd, NULL, FALSE);
1117 return 0;
1118 case TV_EDIT_TIMER:
1119 KillTimer (hwnd, TV_EDIT_TIMER);
1120 infoPtr->Timer &= ~TV_EDIT_TIMER_SET;
1121 return 0;
1122 default:
1123 ERR("got unknown timer\n");
1126 return 1;
1130 static void
1131 TREEVIEW_QueueRefresh (HWND hwnd)
1134 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
1136 TRACE("\n");
1137 if (infoPtr->Timer & TV_REFRESH_TIMER_SET) {
1138 KillTimer (hwnd, TV_REFRESH_TIMER);
1141 SetTimer (hwnd, TV_REFRESH_TIMER, TV_REFRESH_DELAY, 0);
1142 infoPtr->Timer|=TV_REFRESH_TIMER_SET;
1147 static LRESULT
1148 TREEVIEW_GetItemA (HWND hwnd, WPARAM wParam, LPARAM lParam)
1150 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
1151 LPTVITEMEXA tvItem;
1152 TREEVIEW_ITEM *wineItem;
1153 INT iItem;
1155 tvItem=(LPTVITEMEXA) lParam;
1156 iItem=(INT)tvItem->hItem;
1158 wineItem = TREEVIEW_ValidItem (infoPtr, (HTREEITEM)iItem);
1159 if (!wineItem) return FALSE;
1161 if (tvItem->mask & TVIF_CHILDREN) {
1162 if (TVIF_CHILDREN==I_CHILDRENCALLBACK)
1163 FIXME("I_CHILDRENCALLBACK not supported\n");
1164 tvItem->cChildren=wineItem->cChildren;
1167 if (tvItem->mask & TVIF_HANDLE) {
1168 tvItem->hItem=wineItem->hItem;
1171 if (tvItem->mask & TVIF_IMAGE) {
1172 tvItem->iImage=wineItem->iImage;
1175 if (tvItem->mask & TVIF_INTEGRAL) {
1176 tvItem->iIntegral=wineItem->iIntegral;
1179 /* undocumented: windows ignores TVIF_PARAM and
1180 * always sets lParam
1182 tvItem->lParam=wineItem->lParam;
1184 if (tvItem->mask & TVIF_SELECTEDIMAGE) {
1185 tvItem->iSelectedImage=wineItem->iSelectedImage;
1188 if (tvItem->mask & TVIF_STATE) {
1189 tvItem->state=wineItem->state & tvItem->stateMask;
1192 if (tvItem->mask & TVIF_TEXT) {
1193 if (wineItem->pszText == LPSTR_TEXTCALLBACKA) {
1194 tvItem->pszText = LPSTR_TEXTCALLBACKA; /* FIXME:send notification? */
1195 ERR(" GetItem called with LPSTR_TEXTCALLBACK\n");
1197 else if (wineItem->pszText) {
1198 lstrcpynA (tvItem->pszText, wineItem->pszText, tvItem->cchTextMax);
1202 TRACE("item %d<%p>, txt %p, img %p, action %x\n",
1203 iItem,
1204 tvItem,
1205 tvItem->pszText,
1206 & tvItem->iImage,
1207 tvItem->mask);
1209 return TRUE;
1214 /* FIXME: check implementation of TVGN_NEXT/TVGN_NEXTVISIBLE */
1216 static LRESULT
1217 TREEVIEW_GetNextItem (HWND hwnd, WPARAM wParam, LPARAM lParam)
1220 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
1221 TREEVIEW_ITEM *wineItem, *returnItem;
1222 INT iItem, retval, flag;
1225 if (!infoPtr) return FALSE;
1226 flag = (INT) wParam;
1227 iItem = (INT) lParam;
1228 retval=0;
1229 switch (flag) {
1230 case TVGN_ROOT: retval=(INT)infoPtr->TopRootItem;
1231 break;
1232 case TVGN_CARET:retval=(INT)infoPtr->selectedItem;
1233 break;
1234 case TVGN_FIRSTVISIBLE:
1235 TREEVIEW_Refresh (hwnd);
1236 /* FIXME:we should only recalculate, not redraw */
1237 retval=(INT)infoPtr->firstVisible;
1238 break;
1239 case TVGN_DROPHILITE:
1240 retval=(INT)infoPtr->dropItem;
1241 break;
1243 if (retval) {
1244 TRACE("flags:%x, returns %u\n", flag, retval);
1245 return retval;
1248 wineItem = TREEVIEW_ValidItem (infoPtr, (HTREEITEM)iItem);
1249 returnItem = NULL;
1250 if (!wineItem) return FALSE;
1252 switch (flag) {
1253 case TVGN_NEXT: retval=(INT)wineItem->sibling;
1254 break;
1255 case TVGN_PREVIOUS:
1256 retval=(INT)wineItem->upsibling;
1257 break;
1258 case TVGN_PARENT:
1259 retval=(INT)wineItem->parent;
1260 break;
1261 case TVGN_CHILD:
1262 retval=(INT)wineItem->firstChild;
1263 break;
1264 case TVGN_LASTVISIBLE:
1265 returnItem=TREEVIEW_GetLastListItem (infoPtr,wineItem);
1266 break;
1267 case TVGN_NEXTVISIBLE:
1268 returnItem=TREEVIEW_GetNextListItem (infoPtr,wineItem);
1269 break;
1270 case TVGN_PREVIOUSVISIBLE:
1271 returnItem=TREEVIEW_GetPrevListItem (infoPtr, wineItem);
1272 break;
1273 default: FIXME("Unknown msg %x,item %x\n", flag,iItem);
1274 break;
1277 if (returnItem) {
1278 TRACE("flags:%x, item %d;returns %d\n", flag, iItem,
1279 (INT)returnItem->hItem);
1280 return (INT)returnItem->hItem;
1283 TRACE("flags:%x, item %d;returns %d\n", flag, iItem,retval);
1284 return retval;
1288 static LRESULT
1289 TREEVIEW_GetCount (HWND hwnd, WPARAM wParam, LPARAM lParam)
1291 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
1293 TRACE(" %d\n",infoPtr->uNumItems);
1294 return (LRESULT) infoPtr->uNumItems;
1297 /***************************************************************************
1298 * This method does the chaining of the insertion of a treeview item
1299 * before an item.
1300 * If parent is NULL, we're inserting at the root of the list.
1302 static void TREEVIEW_InsertBefore(
1303 TREEVIEW_INFO *infoPtr,
1304 TREEVIEW_ITEM *newItem,
1305 TREEVIEW_ITEM *sibling,
1306 TREEVIEW_ITEM *parent)
1308 HTREEITEM siblingHandle = 0;
1309 HTREEITEM upSiblingHandle = 0;
1310 TREEVIEW_ITEM *upSibling = NULL;
1312 if (newItem == NULL)
1313 ERR("NULL newItem, impossible condition\n");
1315 if (sibling != NULL) /* Insert before this sibling for this parent */
1317 /* Store the new item sibling up sibling and sibling tem handle */
1318 siblingHandle = sibling->hItem;
1319 upSiblingHandle = sibling->upsibling;
1320 /* As well as a pointer to the upsibling sibling object */
1321 if ( (INT)sibling->upsibling != 0 )
1322 upSibling = &infoPtr->items[(INT)sibling->upsibling];
1324 /* Adjust the sibling pointer */
1325 sibling->upsibling = newItem->hItem;
1327 /* Adjust the new item pointers */
1328 newItem->upsibling = upSiblingHandle;
1329 newItem->sibling = siblingHandle;
1331 /* Adjust the up sibling pointer */
1332 if ( upSibling != NULL )
1333 upSibling->sibling = newItem->hItem;
1334 else
1335 /* this item is the first child of this parent, adjust parent pointers */
1336 if (parent)
1337 parent->firstChild = newItem->hItem;
1338 else
1339 infoPtr->TopRootItem= newItem->hItem;
1341 else /* Insert as first child of this parent */
1342 if (parent)
1343 parent->firstChild = newItem->hItem;
1346 /***************************************************************************
1347 * This method does the chaining of the insertion of a treeview item
1348 * after an item.
1349 * If parent is NULL, we're inserting at the root of the list.
1351 static void TREEVIEW_InsertAfter(
1352 TREEVIEW_INFO *infoPtr,
1353 TREEVIEW_ITEM *newItem,
1354 TREEVIEW_ITEM *upSibling,
1355 TREEVIEW_ITEM *parent)
1357 HTREEITEM upSiblingHandle = 0;
1358 HTREEITEM siblingHandle = 0;
1359 TREEVIEW_ITEM *sibling = NULL;
1362 if (newItem == NULL)
1363 ERR("NULL newItem, impossible condition\n");
1365 if (upSibling != NULL) /* Insert after this upsibling for this parent */
1367 /* Store the new item up sibling and sibling item handle */
1368 upSiblingHandle = upSibling->hItem;
1369 siblingHandle = upSibling->sibling;
1370 /* As well as a pointer to the upsibling sibling object */
1371 if ( (INT)upSibling->sibling != 0 )
1372 sibling = &infoPtr->items[(INT)upSibling->sibling];
1374 /* Adjust the up sibling pointer */
1375 upSibling->sibling = newItem->hItem;
1377 /* Adjust the new item pointers */
1378 newItem->upsibling = upSiblingHandle;
1379 newItem->sibling = siblingHandle;
1381 /* Adjust the sibling pointer */
1382 if ( sibling != NULL )
1383 sibling->upsibling = newItem->hItem;
1385 else
1386 newItem is the last of the level, nothing else to do
1389 else /* Insert as first child of this parent */
1390 if (parent)
1391 parent->firstChild = newItem->hItem;
1394 /***************************************************************************
1395 * Forward the DPA local callback to the treeview owner callback
1397 static INT WINAPI TREEVIEW_CallBackCompare(
1398 LPVOID first,
1399 LPVOID second,
1400 LPARAM tvInfoPtr)
1402 /* Forward the call to the client define callback */
1403 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr((HWND)tvInfoPtr);
1404 return (infoPtr->pCallBackSort->lpfnCompare)(
1405 ((TREEVIEW_ITEM*)first)->lParam,
1406 ((TREEVIEW_ITEM*)second)->lParam,
1407 infoPtr->pCallBackSort->lParam);
1410 /***************************************************************************
1411 * Treeview native sort routine: sort on item text.
1413 static INT WINAPI TREEVIEW_SortOnName (
1414 LPVOID first,
1415 LPVOID second,
1416 LPARAM tvInfoPtr)
1418 HWND hwnd=(HWND) tvInfoPtr;
1419 char *txt1, *txt2;
1420 TREEVIEW_ITEM *item;
1423 item=(TREEVIEW_ITEM *) first;
1424 if (item->pszText==LPSTR_TEXTCALLBACKA) {
1425 TREEVIEW_SendDispInfoNotify (hwnd, item, TVN_GETDISPINFO, TVIF_TEXT);
1427 txt1=item->pszText;
1429 item=(TREEVIEW_ITEM *) second;
1430 if (item->pszText==LPSTR_TEXTCALLBACKA) {
1431 TREEVIEW_SendDispInfoNotify (hwnd, item, TVN_GETDISPINFO, TVIF_TEXT);
1433 txt2=item->pszText;
1435 return -strcmp (txt1,txt2);
1438 /***************************************************************************
1439 * Setup the treeview structure with regards of the sort method
1440 * and sort the children of the TV item specified in lParam
1441 * fRecurse: currently unused. Should be zero.
1442 * parent: if pSort!=NULL, should equal pSort->hParent.
1443 * otherwise, item which child items are to be sorted.
1444 * pSort: sort method info. if NULL, sort on item text.
1445 * if non-NULL, sort on item's lParam content, and let the
1446 * application decide what that means. See also TVM_SORTCHILDRENCB.
1449 static LRESULT WINAPI TREEVIEW_Sort (
1450 HWND hwnd,
1451 BOOL fRecurse,
1452 HTREEITEM parent,
1453 LPTVSORTCB pSort
1456 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
1457 TREEVIEW_ITEM *sortMe = NULL; /* Node for which we sort the children */
1459 /* Obtain the TVSORTBC struct */
1460 infoPtr->pCallBackSort = pSort;
1462 /* undocumented feature: TVI_ROOT means `sort the whole tree' */
1464 if (parent==TVI_ROOT)
1465 parent=infoPtr->TopRootItem;
1467 /* Check for a valid handle to the parent item */
1468 if (!TREEVIEW_ValidItem(infoPtr, parent))
1470 ERR ("invalid item hParent=%x\n", (INT)parent);
1471 return FALSE;
1474 /* Obtain the parent node to sort */
1475 sortMe = &infoPtr->items[ (INT)parent ];
1477 /* Make sure there is something to sort */
1478 if ( sortMe->cChildren > 1 )
1480 /* pointer organization */
1481 HDPA sortList = DPA_Create(sortMe->cChildren);
1482 HTREEITEM itemHandle = sortMe->firstChild;
1483 TREEVIEW_ITEM *itemPtr = & infoPtr->items[ (INT)itemHandle ];
1485 /* TREEVIEW_ITEM rechaining */
1486 INT count = 0;
1487 VOID *item = 0;
1488 VOID *nextItem = 0;
1489 VOID *prevItem = 0;
1491 /* Build the list of item to sort */
1494 DPA_InsertPtr(
1495 sortList, /* the list */
1496 sortMe->cChildren+1, /* force the insertion to be an append */
1497 itemPtr); /* the ptr to store */
1499 /* Get the next sibling */
1500 itemHandle = itemPtr->sibling;
1501 itemPtr = & infoPtr->items[ (INT)itemHandle ];
1502 } while ( itemHandle != NULL );
1504 /* let DPA perform the sort activity */
1505 if (pSort)
1506 DPA_Sort(
1507 sortList, /* what */
1508 TREEVIEW_CallBackCompare, /* how */
1509 hwnd); /* owner */
1510 else
1511 DPA_Sort (
1512 sortList, /* what */
1513 TREEVIEW_SortOnName, /* how */
1514 hwnd); /* owner */
1517 * Reorganized TREEVIEW_ITEM structures.
1518 * Note that we know we have at least two elements.
1521 /* Get the first item and get ready to start... */
1522 item = DPA_GetPtr(sortList, count++);
1523 while ( (nextItem = DPA_GetPtr(sortList, count++)) != NULL )
1525 /* link the two current item toghether */
1526 ((TREEVIEW_ITEM*)item)->sibling = ((TREEVIEW_ITEM*)nextItem)->hItem;
1527 ((TREEVIEW_ITEM*)nextItem)->upsibling = ((TREEVIEW_ITEM*)item)->hItem;
1529 if (prevItem == NULL) /* this is the first item, update the parent */
1531 sortMe->firstChild = ((TREEVIEW_ITEM*)item)->hItem;
1532 ((TREEVIEW_ITEM*)item)->upsibling = NULL;
1534 else /* fix the back chaining */
1536 ((TREEVIEW_ITEM*)item)->upsibling = ((TREEVIEW_ITEM*)prevItem)->hItem;
1539 /* get ready for the next one */
1540 prevItem = item;
1541 item = nextItem;
1544 /* the last item is pointed to by item and never has a sibling */
1545 ((TREEVIEW_ITEM*)item)->sibling = NULL;
1547 DPA_Destroy(sortList);
1549 return TRUE;
1551 return FALSE;
1555 /***************************************************************************
1556 * Setup the treeview structure with regards of the sort method
1557 * and sort the children of the TV item specified in lParam
1559 static LRESULT WINAPI TREEVIEW_SortChildrenCB(
1560 HWND hwnd,
1561 WPARAM wParam,
1562 LPARAM lParam
1565 LPTVSORTCB pSort=(LPTVSORTCB) lParam;
1567 return TREEVIEW_Sort (hwnd, wParam, pSort->hParent, pSort);
1571 /***************************************************************************
1572 * Sort the children of the TV item specified in lParam.
1574 static LRESULT WINAPI TREEVIEW_SortChildren (
1575 HWND hwnd,
1576 WPARAM wParam,
1577 LPARAM lParam)
1579 return TREEVIEW_Sort (hwnd, (BOOL) wParam, (HTREEITEM) lParam, NULL);
1584 /* the method used below isn't the most memory-friendly, but it avoids
1585 a lot of memory reallocations */
1587 /* BTW: we waste handle 0; 0 is not an allowed handle. */
1589 static LRESULT
1590 TREEVIEW_InsertItemA (HWND hwnd, WPARAM wParam, LPARAM lParam)
1593 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
1594 TVINSERTSTRUCTA *ptdi;
1595 TVITEMEXA *tvItem;
1596 TREEVIEW_ITEM *wineItem, *parentItem, *prevsib, *sibItem;
1597 INT iItem,listItems,i,len;
1599 /* Item to insert */
1600 ptdi = (LPTVINSERTSTRUCTA) lParam;
1602 /* check if memory is available */
1604 if (infoPtr->uNumPtrsAlloced==0) {
1605 infoPtr->items = COMCTL32_Alloc (TVITEM_ALLOC*sizeof (TREEVIEW_ITEM));
1606 infoPtr->freeList= COMCTL32_Alloc ((1+(TVITEM_ALLOC>>5)) * sizeof (INT));
1607 infoPtr->uNumPtrsAlloced=TVITEM_ALLOC;
1608 infoPtr->TopRootItem=(HTREEITEM)1;
1612 * Reallocate contiguous space for items
1614 if (infoPtr->uNumItems == (infoPtr->uNumPtrsAlloced-1) ) {
1615 TREEVIEW_ITEM *oldItems = infoPtr->items;
1616 INT *oldfreeList = infoPtr->freeList;
1618 infoPtr->uNumPtrsAlloced*=2;
1619 infoPtr->items = COMCTL32_Alloc (infoPtr->uNumPtrsAlloced*sizeof (TREEVIEW_ITEM));
1620 infoPtr->freeList= COMCTL32_Alloc ((1+(infoPtr->uNumPtrsAlloced>>5))*sizeof (INT));
1622 memcpy (&infoPtr->items[0], &oldItems[0],
1623 infoPtr->uNumPtrsAlloced/2 * sizeof(TREEVIEW_ITEM));
1624 memcpy (&infoPtr->freeList[0], &oldfreeList[0],
1625 (infoPtr->uNumPtrsAlloced>>6) * sizeof(INT));
1627 COMCTL32_Free (oldItems);
1628 COMCTL32_Free (oldfreeList);
1632 * Reset infoPtr structure with new stat according to current TV picture
1634 iItem=0;
1635 infoPtr->uNumItems++;
1636 if ((INT)infoPtr->uMaxHandle==(infoPtr->uNumItems-1)) {
1637 iItem=infoPtr->uNumItems;
1638 infoPtr->uMaxHandle = (HTREEITEM)((INT)infoPtr->uMaxHandle + 1);
1639 } else { /* check freelist */
1640 for (i=0; i<infoPtr->uNumPtrsAlloced>>5; i++) {
1641 if (infoPtr->freeList[i]) {
1642 iItem=ffs (infoPtr->freeList[i])-1;
1643 tv_clear_bit(iItem,&infoPtr->freeList[i]);
1644 iItem+=i<<5;
1645 break;
1650 if (TRACE_ON(treeview)) {
1651 for (i=0; i<infoPtr->uNumPtrsAlloced>>5; i++)
1652 TRACE("%8x\n",infoPtr->freeList[i]);
1655 if (!iItem) ERR("Argh -- can't find free item.\n");
1658 * Find the parent item of the new item
1660 tvItem= & ptdi->DUMMYUNIONNAME.itemex;
1661 wineItem=& infoPtr->items[iItem];
1663 if ((ptdi->hParent==TVI_ROOT) || (ptdi->hParent==0)) {
1664 parentItem = NULL;
1665 wineItem->parent = 0;
1666 sibItem = &infoPtr->items [(INT)infoPtr->TopRootItem];
1667 listItems = infoPtr->uNumItems;
1669 else {
1670 parentItem = &infoPtr->items[(INT)ptdi->hParent];
1672 /* Do the insertion here it if it's the only item of this parent */
1673 if (!parentItem->firstChild)
1674 parentItem->firstChild=(HTREEITEM)iItem;
1676 wineItem->parent = ptdi->hParent;
1677 sibItem = &infoPtr->items [(INT)parentItem->firstChild];
1678 parentItem->cChildren++;
1679 listItems = parentItem->cChildren;
1683 /* NOTE: I am moving some setup of the wineItem object that was initialy
1684 * done at the end of the function since some of the values are
1685 * required by the Callback sorting
1688 if (tvItem->mask & TVIF_TEXT)
1691 * Setup the item text stuff here since it's required by the Sort method
1692 * when the insertion are ordered
1694 if (tvItem->pszText!=LPSTR_TEXTCALLBACKA)
1696 TRACE("(%p,%s)\n", &tvItem->pszText, tvItem->pszText);
1697 len = lstrlenA (tvItem->pszText)+1;
1698 wineItem->pszText= COMCTL32_Alloc (len+1);
1699 lstrcpyA (wineItem->pszText, tvItem->pszText);
1700 wineItem->cchTextMax=len;
1702 else
1704 TRACE("LPSTR_TEXTCALLBACK\n");
1705 wineItem->pszText = LPSTR_TEXTCALLBACKA;
1706 wineItem->cchTextMax = 0;
1710 if (tvItem->mask & TVIF_PARAM)
1711 wineItem->lParam=tvItem->lParam;
1714 wineItem->upsibling=0; /* needed in case we're the first item in a list */
1715 wineItem->sibling=0;
1716 wineItem->firstChild=0;
1717 wineItem->hItem=(HTREEITEM)iItem;
1719 if (listItems>1) {
1720 prevsib=NULL;
1722 switch ((DWORD) ptdi->hInsertAfter) {
1723 case (DWORD) TVI_FIRST:
1724 if (sibItem==wineItem) break;
1725 if (wineItem->parent) {
1726 wineItem->sibling=parentItem->firstChild;
1727 parentItem->firstChild=(HTREEITEM)iItem;
1728 } else {
1729 wineItem->sibling=infoPtr->TopRootItem;
1730 infoPtr->TopRootItem=(HTREEITEM)iItem;
1732 sibItem->upsibling=(HTREEITEM)iItem;
1733 break;
1735 case (DWORD) TVI_SORT:
1736 if (sibItem==wineItem)
1738 * This item is the first child of the level and it
1739 * has already been inserted
1741 break;
1742 else
1744 TREEVIEW_ITEM *aChild;
1747 TREEVIEW_ITEM *previousChild = NULL;
1748 BOOL bItemInserted = FALSE;
1750 if (parentItem)
1751 aChild = &infoPtr->items[(INT)parentItem->firstChild];
1752 else
1753 aChild = &infoPtr->items[(INT)infoPtr->TopRootItem];
1755 /* Iterate the parent children to see where we fit in */
1756 while ( aChild != NULL )
1758 INT comp = strcmp(wineItem->pszText, aChild->pszText);
1759 if ( comp < 0 ) /* we are smaller than the current one */
1761 TREEVIEW_InsertBefore(infoPtr, wineItem, aChild, parentItem);
1762 bItemInserted = TRUE;
1763 break;
1765 else if ( comp > 0 ) /* we are bigger than the current one */
1767 previousChild = aChild;
1768 aChild = (aChild->sibling == 0) /* This will help us to exit */
1769 ? NULL /* if there is no more sibling */
1770 : &infoPtr->items[(INT)aChild->sibling];
1772 /* Look at the next item */
1773 continue;
1775 else if ( comp == 0 )
1778 * An item with this name is already existing, therefore,
1779 * we add after the one we found
1781 TREEVIEW_InsertAfter(infoPtr, wineItem, aChild, parentItem);
1782 bItemInserted = TRUE;
1783 break;
1788 * we reach the end of the child list and the item as not
1789 * yet been inserted, therefore, insert it after the last child.
1791 if ( (! bItemInserted ) && (aChild == NULL) )
1792 TREEVIEW_InsertAfter(infoPtr, wineItem, previousChild, parentItem);
1794 break;
1798 case (DWORD) TVI_LAST:
1799 if (sibItem==wineItem) break;
1800 while (sibItem->sibling) {
1801 prevsib=sibItem;
1802 sibItem=&infoPtr->items [(INT)sibItem->sibling];
1804 sibItem->sibling=(HTREEITEM)iItem;
1805 wineItem->upsibling=sibItem->hItem;
1806 break;
1807 default:
1808 while ((sibItem->sibling) && (sibItem->hItem!=ptdi->hInsertAfter))
1810 prevsib=sibItem;
1811 sibItem=&infoPtr->items [(INT)sibItem->sibling];
1813 if (sibItem->hItem!=ptdi->hInsertAfter) {
1814 ERR("tried to insert item after nonexisting handle %d.\n",
1815 (INT) ptdi->hInsertAfter);
1816 break;
1818 prevsib=sibItem;
1819 if (sibItem->sibling) {
1820 sibItem=&infoPtr->items [(INT)sibItem->sibling];
1821 sibItem->upsibling=(HTREEITEM)iItem;
1822 wineItem->sibling=sibItem->hItem;
1824 prevsib->sibling=(HTREEITEM)iItem;
1825 wineItem->upsibling=prevsib->hItem;
1826 break;
1831 /* Fill in info structure */
1833 TRACE("new item %d; parent %d, mask %x\n", iItem,
1834 (INT)wineItem->parent,tvItem->mask);
1836 wineItem->mask=tvItem->mask;
1837 wineItem->iIntegral=1;
1839 if (tvItem->mask & TVIF_CHILDREN) {
1840 wineItem->cChildren=tvItem->cChildren;
1841 if (tvItem->cChildren==I_CHILDRENCALLBACK)
1842 FIXME(" I_CHILDRENCALLBACK not supported\n");
1845 wineItem->expandBox.left = 0; /* Initialize the expandBox */
1846 wineItem->expandBox.top = 0;
1847 wineItem->expandBox.right = 0;
1848 wineItem->expandBox.bottom = 0;
1850 if (tvItem->mask & TVIF_IMAGE)
1851 wineItem->iImage=tvItem->iImage;
1853 /* If the application sets TVIF_INTEGRAL without
1854 supplying a TVITEMEX structure, it's toast */
1856 if (tvItem->mask & TVIF_INTEGRAL)
1857 wineItem->iIntegral=tvItem->iIntegral;
1859 if (tvItem->mask & TVIF_SELECTEDIMAGE)
1860 wineItem->iSelectedImage=tvItem->iSelectedImage;
1862 if (tvItem->mask & TVIF_STATE) {
1863 TRACE("Changing item state from %d to %d\n",
1864 wineItem->state,
1865 tvItem->state);
1866 wineItem->state=tvItem->state;
1867 wineItem->stateMask=tvItem->stateMask;
1871 TREEVIEW_QueueRefresh (hwnd);
1873 return (LRESULT) iItem;
1877 static LRESULT
1878 TREEVIEW_InsertItemW(HWND hwnd, WPARAM wParam, LPARAM lParam)
1880 TVINSERTSTRUCTW *tvisW;
1881 TVINSERTSTRUCTA tvisA;
1882 LRESULT lRes;
1884 tvisW = (LPTVINSERTSTRUCTW)lParam;
1886 tvisA.hParent = tvisW->hParent;
1887 tvisA.hInsertAfter = tvisW->hInsertAfter;
1889 tvisA.DUMMYUNIONNAME.item.mask = tvisW->DUMMYUNIONNAME.item.mask;
1890 tvisA.DUMMYUNIONNAME.item.hItem = tvisW->DUMMYUNIONNAME.item.hItem;
1891 tvisA.DUMMYUNIONNAME.item.state = tvisW->DUMMYUNIONNAME.item.state;
1892 tvisA.DUMMYUNIONNAME.item.stateMask = tvisW->DUMMYUNIONNAME.item.stateMask;
1893 tvisA.DUMMYUNIONNAME.item.cchTextMax = tvisW->DUMMYUNIONNAME.item.cchTextMax;
1895 if(tvisW->DUMMYUNIONNAME.item.pszText)
1897 if (tvisW->DUMMYUNIONNAME.item.pszText!=LPSTR_TEXTCALLBACKW)
1899 int len = lstrlenW (tvisW->DUMMYUNIONNAME.item.pszText)+1;
1900 tvisA.DUMMYUNIONNAME.item.pszText = COMCTL32_Alloc (len);
1901 lstrcpyWtoA (tvisA.DUMMYUNIONNAME.item.pszText,
1902 tvisW->DUMMYUNIONNAME.item.pszText );
1904 else
1906 tvisA.DUMMYUNIONNAME.item.pszText = LPSTR_TEXTCALLBACKA;
1907 tvisA.DUMMYUNIONNAME.item.cchTextMax = 0;
1911 tvisA.DUMMYUNIONNAME.item.iImage = tvisW->DUMMYUNIONNAME.item.iImage;
1912 tvisA.DUMMYUNIONNAME.item.iSelectedImage = tvisW->DUMMYUNIONNAME.item.iSelectedImage;
1913 tvisA.DUMMYUNIONNAME.item.cChildren = tvisW->DUMMYUNIONNAME.item.cChildren;
1914 tvisA.DUMMYUNIONNAME.item.lParam = tvisW->DUMMYUNIONNAME.item.lParam;
1916 lRes = TREEVIEW_InsertItemA(hwnd,wParam,(LPARAM)&tvisA);
1918 if (tvisA.DUMMYUNIONNAME.item.pszText!=LPSTR_TEXTCALLBACKA)
1920 COMCTL32_Free(tvisA.DUMMYUNIONNAME.item.pszText);
1923 return lRes;
1928 static LRESULT
1929 TREEVIEW_DeleteItem (HWND hwnd, WPARAM wParam, LPARAM lParam)
1931 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
1932 INT iItem;
1933 TREEVIEW_ITEM *wineItem;
1935 TRACE("\n");
1936 if (!infoPtr) return FALSE;
1938 if (lParam == (INT)TVI_ROOT) {
1939 TREEVIEW_RemoveTree (hwnd);
1940 } else {
1941 iItem= (INT) lParam;
1942 wineItem = TREEVIEW_ValidItem (infoPtr, (HTREEITEM)iItem);
1943 if (!wineItem) return FALSE;
1944 TRACE("%s\n",wineItem->pszText);
1945 TREEVIEW_RemoveItem (hwnd, wineItem);
1948 TREEVIEW_QueueRefresh (hwnd);
1950 return TRUE;
1955 static LRESULT
1956 TREEVIEW_GetIndent (HWND hwnd)
1958 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
1960 TRACE("\n");
1961 return infoPtr->uIndent;
1964 static LRESULT
1965 TREEVIEW_SetIndent (HWND hwnd, WPARAM wParam)
1967 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
1968 INT newIndent;
1970 TRACE("\n");
1971 newIndent=(INT) wParam;
1972 if (newIndent < MINIMUM_INDENT) newIndent=MINIMUM_INDENT;
1973 infoPtr->uIndent=newIndent;
1975 return 0;
1978 static LRESULT
1979 TREEVIEW_GetToolTips (HWND hwnd)
1982 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
1984 TRACE("\n");
1985 return infoPtr->hwndToolTip;
1989 static LRESULT
1990 TREEVIEW_SetToolTips (HWND hwnd, WPARAM wParam)
1993 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
1994 HWND prevToolTip;
1996 TRACE("\n");
1997 prevToolTip=infoPtr->hwndToolTip;
1998 infoPtr->hwndToolTip= (HWND) wParam;
2000 return prevToolTip;
2004 static LRESULT CALLBACK
2005 TREEVIEW_GetEditControl (HWND hwnd)
2008 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
2010 return infoPtr->hwndEdit;
2013 LRESULT CALLBACK
2014 TREEVIEW_Edit_SubclassProc (HWND hwnd, UINT uMsg, WPARAM wParam,
2015 LPARAM lParam)
2017 switch (uMsg)
2019 case WM_ERASEBKGND:
2021 RECT rc;
2022 HDC hdc = (HDC) wParam;
2023 GetClientRect (hwnd, &rc);
2024 Rectangle (hdc, rc.left, rc.top, rc.right, rc.bottom);
2025 return -1;
2028 case WM_GETDLGCODE:
2030 return DLGC_WANTARROWS | DLGC_WANTALLKEYS;
2033 default:
2035 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(GetParent(hwnd));
2036 return CallWindowProcA( infoPtr->wpEditOrig, hwnd, uMsg, wParam, lParam);
2040 return 0;
2044 /* should handle edit control messages here */
2046 static LRESULT
2047 TREEVIEW_Command (HWND hwnd, WPARAM wParam, LPARAM lParam)
2050 TRACE("%x %ld\n",wParam, lParam);
2052 switch (HIWORD(wParam))
2054 case EN_UPDATE:
2057 * Adjust the edit window size
2059 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
2060 TREEVIEW_ITEM *editItem = TREEVIEW_ValidItem(infoPtr, infoPtr->editItem);
2061 INT iLength = GetWindowTextLengthA(infoPtr->hwndEdit);
2062 HDC hdc = GetDC(infoPtr->hwndEdit);
2063 TEXTMETRICA tm;
2065 if ( GetTextMetricsA(hdc, &tm) )
2067 LONG newWidth = (iLength * tm.tmAveCharWidth) + 15;
2069 SetWindowPos (
2070 infoPtr->hwndEdit,
2071 HWND_TOP,
2072 editItem->text.left - 2,
2073 editItem->text.top - 1,
2074 newWidth,
2075 editItem->text.bottom - editItem->text.top + 3,
2076 SWP_DRAWFRAME );
2078 ReleaseDC(hwnd, hdc);
2080 break;
2083 case EN_KILLFOCUS:
2084 /* TREEVIEW_EndEditLabelNow(hwnd, (WPARAM)FALSE, 0);
2086 break;
2088 default:
2089 return SendMessageA (GetParent (hwnd), WM_COMMAND, wParam, lParam);
2092 return 0;
2095 static LRESULT
2096 TREEVIEW_Size (HWND hwnd, WPARAM wParam, LPARAM lParam)
2099 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
2101 if (infoPtr->bAutoSize)
2103 infoPtr->bAutoSize = FALSE;
2104 return 0;
2106 infoPtr->bAutoSize = TRUE;
2108 if (wParam == SIZE_RESTORED)
2110 infoPtr->uTotalWidth = LOWORD (lParam);
2111 infoPtr->uTotalHeight = HIWORD (lParam);
2112 } else {
2113 FIXME("WM_SIZE flag %x %lx not handled\n", wParam, lParam);
2116 TREEVIEW_QueueRefresh (hwnd);
2117 return 0;
2122 static LRESULT
2123 TREEVIEW_StyleChanged (HWND hwnd, WPARAM wParam, LPARAM lParam)
2125 TRACE("(%x %lx)\n",wParam,lParam);
2127 TREEVIEW_Refresh (hwnd);
2129 return 0;
2132 static LRESULT
2133 TREEVIEW_Create (HWND hwnd, WPARAM wParam, LPARAM lParam)
2135 TREEVIEW_INFO *infoPtr;
2136 LOGFONTA logFont;
2137 TEXTMETRICA tm;
2138 HDC hdc;
2140 TRACE("wnd %x\n",hwnd);
2141 /* allocate memory for info structure */
2142 infoPtr = (TREEVIEW_INFO *) COMCTL32_Alloc (sizeof(TREEVIEW_INFO));
2144 SetWindowLongA( hwnd, 0, (DWORD)infoPtr);
2146 if (infoPtr == NULL) {
2147 ERR("could not allocate info memory!\n");
2148 return 0;
2151 if ((TREEVIEW_INFO*) GetWindowLongA( hwnd, 0) != infoPtr) {
2152 ERR("pointer assignment error!\n");
2153 return 0;
2156 hdc=GetDC (hwnd);
2158 /* set default settings */
2159 infoPtr->uInternalStatus=0;
2160 infoPtr->uNumItems=0;
2161 infoPtr->clrBk = GetSysColor (COLOR_WINDOW);
2162 infoPtr->clrText = GetSysColor (COLOR_BTNTEXT);
2163 infoPtr->clrLine = GetSysColor (COLOR_WINDOWTEXT);
2164 infoPtr->cy = 0;
2165 infoPtr->cx = 0;
2166 infoPtr->uIndent = 15;
2167 infoPtr->himlNormal = NULL;
2168 infoPtr->himlState = NULL;
2169 infoPtr->uItemHeight = -1;
2170 GetTextMetricsA (hdc, &tm);
2171 infoPtr->hFont = GetStockObject (DEFAULT_GUI_FONT);
2172 GetObjectA (infoPtr->hFont, sizeof (LOGFONTA), &logFont);
2173 logFont.lfWeight=FW_BOLD;
2174 infoPtr->hBoldFont = CreateFontIndirectA (&logFont);
2176 infoPtr->items = NULL;
2177 infoPtr->selectedItem=0;
2178 infoPtr->clrText=-1; /* use system color */
2179 infoPtr->dropItem=0;
2180 infoPtr->pCallBackSort=NULL;
2181 infoPtr->uScrollTime = 300; /* milliseconds */
2184 infoPtr->hwndNotify = GetParent32 (hwnd);
2185 infoPtr->bTransparent = ( GetWindowLongA( hwnd, GWL_STYLE) & TBSTYLE_FLAT);
2188 infoPtr->hwndToolTip=0;
2189 if (!( GetWindowLongA( hwnd, GWL_STYLE) & TVS_NOTOOLTIPS)) { /* Create tooltip control */
2190 TTTOOLINFOA ti;
2192 infoPtr->hwndToolTip =
2193 CreateWindowExA (0, TOOLTIPS_CLASSA, NULL, 0,
2194 CW_USEDEFAULT, CW_USEDEFAULT,
2195 CW_USEDEFAULT, CW_USEDEFAULT,
2196 hwnd, 0, 0, 0);
2198 /* Send NM_TOOLTIPSCREATED notification */
2199 if (infoPtr->hwndToolTip) {
2200 NMTOOLTIPSCREATED nmttc;
2202 nmttc.hdr.hwndFrom = hwnd;
2203 nmttc.hdr.idFrom = GetWindowLongA( hwnd, GWL_ID);
2204 nmttc.hdr.code = NM_TOOLTIPSCREATED;
2205 nmttc.hwndToolTips = infoPtr->hwndToolTip;
2207 SendMessageA (GetParent (hwnd), WM_NOTIFY,
2208 (WPARAM) GetWindowLongA( hwnd, GWL_ID), (LPARAM)&nmttc);
2211 ZeroMemory (&ti, sizeof(TTTOOLINFOA));
2212 ti.cbSize = sizeof(TTTOOLINFOA);
2213 ti.uFlags = TTF_IDISHWND | TTF_TRACK | TTF_TRANSPARENT ;
2214 ti.hwnd = hwnd;
2215 ti.uId = 0;
2216 ti.lpszText = "Test"; /* LPSTR_TEXTCALLBACK; */
2217 SetRectEmpty (&ti.rect);
2219 SendMessageA (infoPtr->hwndToolTip, TTM_ADDTOOLA, 0, (LPARAM)&ti);
2222 infoPtr->hwndEdit = CreateWindowExA (
2223 WS_EX_LEFT,
2224 "EDIT",
2226 WS_CHILD | WS_BORDER | ES_AUTOHSCROLL |
2227 ES_WANTRETURN | ES_LEFT,
2228 0, 0, 0, 0,
2229 hwnd,
2230 0,0,0); /* FIXME: (HMENU)IDTVEDIT,pcs->hInstance,0);*/
2232 SendMessageA ( infoPtr->hwndEdit, WM_SETFONT, infoPtr->hFont, FALSE);
2233 infoPtr->wpEditOrig = (WNDPROC)SetWindowLongA (
2234 infoPtr->hwndEdit,
2235 GWL_WNDPROC,
2236 (LONG) TREEVIEW_Edit_SubclassProc);
2238 ReleaseDC (hwnd, hdc);
2239 return 0;
2244 static LRESULT
2245 TREEVIEW_Destroy (HWND hwnd)
2247 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
2249 TRACE("\n");
2250 TREEVIEW_RemoveTree (hwnd);
2251 if (infoPtr->Timer & TV_REFRESH_TIMER_SET)
2252 KillTimer (hwnd, TV_REFRESH_TIMER);
2253 if (infoPtr->hwndToolTip)
2254 DestroyWindow (infoPtr->hwndToolTip);
2256 COMCTL32_Free (infoPtr);
2257 return 0;
2261 static LRESULT
2262 TREEVIEW_Paint (HWND hwnd, WPARAM wParam, LPARAM lParam)
2264 HDC hdc;
2265 PAINTSTRUCT ps;
2267 TRACE("\n");
2268 hdc = wParam==0 ? BeginPaint (hwnd, &ps) : (HDC)wParam;
2269 TREEVIEW_Refresh (hwnd);
2270 if(!wParam)
2271 EndPaint (hwnd, &ps);
2272 TRACE("done\n");
2274 return DefWindowProcA (hwnd, WM_PAINT, wParam, lParam);
2277 static LRESULT
2278 TREEVIEW_SetFocus (HWND hwnd, WPARAM wParam, LPARAM lParam)
2280 TREEVIEW_SendSimpleNotify (hwnd, NM_SETFOCUS);
2281 InvalidateRect(hwnd, NULL, FALSE);
2282 return 0;
2285 static LRESULT
2286 TREEVIEW_KillFocus (HWND hwnd, WPARAM wParam, LPARAM lParam)
2288 TREEVIEW_SendSimpleNotify (hwnd, NM_KILLFOCUS);
2289 InvalidateRect(hwnd, NULL, FALSE);
2290 return 0;
2293 static LRESULT
2294 TREEVIEW_EraseBackground (HWND hwnd, WPARAM wParam, LPARAM lParam)
2296 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
2297 HBRUSH hBrush = CreateSolidBrush (infoPtr->clrBk);
2298 RECT rect;
2300 TRACE("\n");
2301 GetClientRect (hwnd, &rect);
2302 FillRect ((HDC)wParam, &rect, hBrush);
2303 DeleteObject (hBrush);
2304 return TRUE;
2312 /* Notifications */
2318 static BOOL
2319 TREEVIEW_SendSimpleNotify (HWND hwnd, UINT code)
2321 NMHDR nmhdr;
2323 TRACE("%x\n",code);
2324 nmhdr.hwndFrom = hwnd;
2325 nmhdr.idFrom = GetWindowLongA( hwnd, GWL_ID);
2326 nmhdr.code = code;
2328 return (BOOL) SendMessageA (GetParent (hwnd), WM_NOTIFY,
2329 (WPARAM)nmhdr.idFrom, (LPARAM)&nmhdr);
2334 static BOOL
2335 TREEVIEW_SendTreeviewNotify (HWND hwnd, UINT code, UINT action,
2336 HTREEITEM oldItem, HTREEITEM newItem)
2339 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
2340 NMTREEVIEWA nmhdr;
2341 TREEVIEW_ITEM *wineItem;
2343 TRACE("code:%x action:%x olditem:%x newitem:%x\n",
2344 code,action,(INT)oldItem,(INT)newItem);
2345 nmhdr.hdr.hwndFrom = hwnd;
2346 nmhdr.hdr.idFrom = GetWindowLongA( hwnd, GWL_ID);
2347 nmhdr.hdr.code = code;
2348 nmhdr.action = action;
2349 if (oldItem) {
2350 wineItem=& infoPtr->items[(INT)oldItem];
2351 nmhdr.itemOld.mask = wineItem->mask;
2352 nmhdr.itemOld.hItem = wineItem->hItem;
2353 nmhdr.itemOld.state = wineItem->state;
2354 nmhdr.itemOld.stateMask = wineItem->stateMask;
2355 nmhdr.itemOld.iImage = wineItem->iImage;
2356 nmhdr.itemOld.pszText = wineItem->pszText;
2357 nmhdr.itemOld.cchTextMax= wineItem->cchTextMax;
2358 nmhdr.itemOld.iImage = wineItem->iImage;
2359 nmhdr.itemOld.iSelectedImage = wineItem->iSelectedImage;
2360 nmhdr.itemOld.cChildren = wineItem->cChildren;
2361 nmhdr.itemOld.lParam = wineItem->lParam;
2364 if (newItem) {
2365 wineItem=& infoPtr->items[(INT)newItem];
2366 nmhdr.itemNew.mask = wineItem->mask;
2367 nmhdr.itemNew.hItem = wineItem->hItem;
2368 nmhdr.itemNew.state = wineItem->state;
2369 nmhdr.itemNew.stateMask = wineItem->stateMask;
2370 nmhdr.itemNew.iImage = wineItem->iImage;
2371 nmhdr.itemNew.pszText = wineItem->pszText;
2372 nmhdr.itemNew.cchTextMax= wineItem->cchTextMax;
2373 nmhdr.itemNew.iImage = wineItem->iImage;
2374 nmhdr.itemNew.iSelectedImage = wineItem->iSelectedImage;
2375 nmhdr.itemNew.cChildren = wineItem->cChildren;
2376 nmhdr.itemNew.lParam = wineItem->lParam;
2379 nmhdr.ptDrag.x = 0;
2380 nmhdr.ptDrag.y = 0;
2382 return (BOOL)SendMessageA (GetParent (hwnd), WM_NOTIFY,
2383 (WPARAM) GetWindowLongA( hwnd, GWL_ID), (LPARAM)&nmhdr);
2387 static BOOL
2388 TREEVIEW_SendTreeviewDnDNotify (HWND hwnd, UINT code, HTREEITEM dragItem,
2389 POINT pt)
2391 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
2392 NMTREEVIEWA nmhdr;
2393 TREEVIEW_ITEM *wineItem;
2395 TRACE("code:%x dragitem:%x\n", code,(INT)dragItem);
2397 nmhdr.hdr.hwndFrom = hwnd;
2398 nmhdr.hdr.idFrom = GetWindowLongA( hwnd, GWL_ID);
2399 nmhdr.hdr.code = code;
2400 nmhdr.action = 0;
2401 wineItem=& infoPtr->items[(INT)dragItem];
2402 nmhdr.itemNew.mask = wineItem->mask;
2403 nmhdr.itemNew.hItem = wineItem->hItem;
2404 nmhdr.itemNew.state = wineItem->state;
2405 nmhdr.itemNew.lParam = wineItem->lParam;
2407 nmhdr.ptDrag.x = pt.x;
2408 nmhdr.ptDrag.y = pt.y;
2410 return (BOOL)SendMessageA (GetParent (hwnd), WM_NOTIFY,
2411 (WPARAM) GetWindowLongA( hwnd, GWL_ID), (LPARAM)&nmhdr);
2417 static BOOL
2418 TREEVIEW_SendDispInfoNotify (HWND hwnd, TREEVIEW_ITEM *wineItem,
2419 UINT code, UINT what)
2421 NMTVDISPINFOA tvdi;
2422 BOOL retval;
2423 char *buf;
2425 TRACE("item %d, action %x, state %d\n",
2426 (INT)wineItem->hItem,
2427 what,
2428 (INT)wineItem->state);
2430 tvdi.hdr.hwndFrom = hwnd;
2431 tvdi.hdr.idFrom = GetWindowLongA( hwnd, GWL_ID);
2432 tvdi.hdr.code = code;
2433 tvdi.item.mask = what;
2434 tvdi.item.hItem = wineItem->hItem;
2435 tvdi.item.state = wineItem->state;
2436 tvdi.item.lParam = wineItem->lParam;
2437 tvdi.item.pszText = COMCTL32_Alloc (128*sizeof(char));
2438 tvdi.item.cchTextMax = 128;
2439 buf = tvdi.item.pszText;
2441 retval=(BOOL)SendMessageA (
2442 GetParent(hwnd),
2443 WM_NOTIFY,
2444 (WPARAM)tvdi.hdr.idFrom,
2445 (LPARAM)&tvdi);
2447 if (what & TVIF_TEXT) {
2448 wineItem->pszText = tvdi.item.pszText;
2449 if (buf==tvdi.item.pszText) {
2450 wineItem->cchTextMax = 128;
2451 } else {
2452 TRACE("user-supplied buffer\n");
2453 COMCTL32_Free (buf);
2454 wineItem->cchTextMax = 0;
2457 if (what & TVIF_SELECTEDIMAGE)
2458 wineItem->iSelectedImage = tvdi.item.iSelectedImage;
2459 if (what & TVIF_IMAGE)
2460 wineItem->iImage = tvdi.item.iImage;
2461 if (what & TVIF_CHILDREN)
2462 wineItem->cChildren = tvdi.item.cChildren;
2464 return retval;
2469 static BOOL
2470 TREEVIEW_SendCustomDrawNotify (HWND hwnd, DWORD dwDrawStage, HDC hdc,
2471 RECT rc)
2473 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
2474 NMTVCUSTOMDRAW nmcdhdr;
2475 LPNMCUSTOMDRAW nmcd;
2477 TRACE("drawstage:%lx hdc:%x\n", dwDrawStage, hdc);
2479 nmcd= & nmcdhdr.nmcd;
2480 nmcd->hdr.hwndFrom = hwnd;
2481 nmcd->hdr.idFrom = GetWindowLongA( hwnd, GWL_ID);
2482 nmcd->hdr.code = NM_CUSTOMDRAW;
2483 nmcd->dwDrawStage= dwDrawStage;
2484 nmcd->hdc = hdc;
2485 nmcd->rc.left = rc.left;
2486 nmcd->rc.right = rc.right;
2487 nmcd->rc.bottom = rc.bottom;
2488 nmcd->rc.top = rc.top;
2489 nmcd->dwItemSpec = 0;
2490 nmcd->uItemState = 0;
2491 nmcd->lItemlParam= 0;
2492 nmcdhdr.clrText = infoPtr->clrText;
2493 nmcdhdr.clrTextBk= infoPtr->clrBk;
2494 nmcdhdr.iLevel = 0;
2496 return (BOOL)SendMessageA (GetParent (hwnd), WM_NOTIFY,
2497 (WPARAM) GetWindowLongA( hwnd, GWL_ID), (LPARAM)&nmcdhdr);
2503 /* FIXME: need to find out when the flags in uItemState need to be set */
2505 static BOOL
2506 TREEVIEW_SendCustomDrawItemNotify (HWND hwnd, HDC hdc,
2507 TREEVIEW_ITEM *wineItem, UINT uItemDrawState)
2509 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
2510 NMTVCUSTOMDRAW nmcdhdr;
2511 LPNMCUSTOMDRAW nmcd;
2512 DWORD dwDrawStage,dwItemSpec;
2513 UINT uItemState;
2515 dwDrawStage=CDDS_ITEM | uItemDrawState;
2516 dwItemSpec=(DWORD)wineItem->hItem;
2517 uItemState=0;
2518 if (wineItem->hItem==infoPtr->selectedItem) uItemState|=CDIS_SELECTED;
2519 if (wineItem->hItem==infoPtr->focusItem) uItemState|=CDIS_FOCUS;
2520 if (wineItem->hItem==infoPtr->hotItem) uItemState|=CDIS_HOT;
2522 nmcd= & nmcdhdr.nmcd;
2523 nmcd->hdr.hwndFrom = hwnd;
2524 nmcd->hdr.idFrom = GetWindowLongA( hwnd, GWL_ID);
2525 nmcd->hdr.code = NM_CUSTOMDRAW;
2526 nmcd->dwDrawStage= dwDrawStage;
2527 nmcd->hdc = hdc;
2528 nmcd->rc.left = wineItem->rect.left;
2529 nmcd->rc.right = wineItem->rect.right;
2530 nmcd->rc.bottom = wineItem->rect.bottom;
2531 nmcd->rc.top = wineItem->rect.top;
2532 nmcd->dwItemSpec = dwItemSpec;
2533 nmcd->uItemState = uItemState;
2534 nmcd->lItemlParam= wineItem->lParam;
2536 nmcdhdr.clrText = infoPtr->clrText;
2537 nmcdhdr.clrTextBk= infoPtr->clrBk;
2538 nmcdhdr.iLevel = wineItem->iLevel;
2540 TRACE("drawstage:%lx hdc:%x item:%lx, itemstate:%x\n",
2541 dwDrawStage, hdc, dwItemSpec, uItemState);
2543 return (BOOL)SendMessageA (GetParent (hwnd), WM_NOTIFY,
2544 (WPARAM) GetWindowLongA( hwnd, GWL_ID), (LPARAM)&nmcdhdr);
2549 /* Note:If the specified item is the child of a collapsed parent item,
2550 the parent's list of child items is (recursively) expanded to reveal the
2551 specified item. This is mentioned for TREEVIEW_SelectItem; don't
2552 know if it also applies here.
2555 static LRESULT
2556 TREEVIEW_Expand (HWND hwnd, WPARAM wParam, LPARAM lParam)
2558 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
2559 TREEVIEW_ITEM *wineItem;
2560 UINT flag;
2561 INT expand;
2563 flag = (UINT) wParam;
2564 expand = (INT) lParam;
2566 wineItem = TREEVIEW_ValidItem (infoPtr, (HTREEITEM)expand);
2568 if (!wineItem)
2569 return 0;
2570 if (!wineItem->cChildren)
2571 return 0;
2573 if (wineItem->pszText==LPSTR_TEXTCALLBACKA)
2574 TRACE ("For item %d, flags %d, state %d\n",
2575 expand, flag, wineItem->state);
2576 else
2577 TRACE("For (%s) item:%d, flags %x, state:%d\n",
2578 wineItem->pszText, flag, expand, wineItem->state);
2580 if (wineItem->cChildren==I_CHILDRENCALLBACK) {
2581 FIXME("we don't handle I_CHILDRENCALLBACK yet\n");
2582 return 0;
2585 if (flag == TVE_TOGGLE) { /* FIXME: check exact behaviour here */
2586 flag &= ~TVE_TOGGLE; /* ie: bitwise ops or 'case' ops */
2587 if (wineItem->state & TVIS_EXPANDED)
2588 flag |= TVE_COLLAPSE;
2589 else
2590 flag |= TVE_EXPAND;
2593 switch (flag)
2595 case TVE_COLLAPSERESET:
2596 TRACE(" case TVE_COLLAPSERESET\n");
2597 if (!wineItem->state & TVIS_EXPANDED)
2598 return 0;
2600 wineItem->state &= ~(TVIS_EXPANDEDONCE | TVIS_EXPANDED);
2601 TREEVIEW_RemoveAllChildren (hwnd, wineItem);
2602 break;
2604 case TVE_COLLAPSE:
2605 TRACE(" case TVE_COLLAPSE\n");
2606 if (!wineItem->state & TVIS_EXPANDED)
2607 return 0;
2609 wineItem->state &= ~TVIS_EXPANDED;
2610 break;
2612 case TVE_EXPAND:
2613 TRACE(" case TVE_EXPAND\n");
2614 if (wineItem->state & TVIS_EXPANDED)
2615 return 0;
2617 TRACE(" is not expanded...\n");
2619 if (!(wineItem->state & TVIS_EXPANDEDONCE))
2621 TRACE(" and has never been expanded...\n");
2622 wineItem->state |= TVIS_EXPANDED;
2624 /* this item has never been expanded */
2625 if (TREEVIEW_SendTreeviewNotify (
2626 hwnd,
2627 TVN_ITEMEXPANDING,
2628 TVE_EXPAND,
2630 (HTREEITEM)expand))
2632 TRACE(" TVN_ITEMEXPANDING returned TRUE, exiting...\n");
2633 return FALSE;
2636 /* FIXME
2637 * Since the TVN_ITEMEXPANDING message may has caused the parent to
2638 * insert new items which in turn may have cause items placeholder
2639 * reallocation, I reassign the current item pointer so we have
2640 * something valid to work with...
2641 * However, this should not be necessary,
2642 * investigation required in TREEVIEW_InsertItemA
2644 wineItem = TREEVIEW_ValidItem (infoPtr, (HTREEITEM)expand);
2645 if (! wineItem)
2647 ERR(
2648 "Catastropic situation, cannot retreive item #%d\n",
2649 expand);
2650 return FALSE;
2653 wineItem->state |= TVIS_EXPANDEDONCE;
2654 TRACE(" TVN_ITEMEXPANDING sent...\n");
2656 TREEVIEW_SendTreeviewNotify (
2657 hwnd,
2658 TVN_ITEMEXPANDED,
2659 TVE_EXPAND,
2661 (HTREEITEM)expand);
2663 TRACE(" TVN_ITEMEXPANDED sent...\n");
2666 else
2668 /* this item has already been expanded */
2669 wineItem->state |= TVIS_EXPANDED;
2671 break;
2673 case TVE_EXPANDPARTIAL:
2674 TRACE(" case TVE_EXPANDPARTIAL\n");
2675 FIXME("TVE_EXPANDPARTIAL not implemented\n");
2676 wineItem->state ^=TVIS_EXPANDED;
2677 wineItem->state |=TVIS_EXPANDEDONCE;
2678 break;
2681 TRACE("Exiting, Item %d state is now %d...\n",
2682 expand,
2683 wineItem->state);
2685 TREEVIEW_QueueRefresh (hwnd);
2686 return TRUE;
2691 static TREEVIEW_ITEM *
2692 TREEVIEW_HitTestPoint (HWND hwnd, POINT pt)
2694 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
2695 TREEVIEW_ITEM *wineItem;
2696 RECT rect;
2698 GetClientRect (hwnd, &rect);
2700 if (!infoPtr->firstVisible) return NULL;
2702 wineItem=&infoPtr->items [(INT)infoPtr->firstVisible];
2704 while ((wineItem!=NULL) && (pt.y > wineItem->rect.bottom))
2705 wineItem=TREEVIEW_GetNextListItem (infoPtr,wineItem);
2707 if (!wineItem)
2708 return NULL;
2710 return wineItem;
2716 static LRESULT
2717 TREEVIEW_HitTest (HWND hwnd, LPARAM lParam)
2719 LPTVHITTESTINFO lpht=(LPTVHITTESTINFO) lParam;
2720 TREEVIEW_ITEM *wineItem;
2721 RECT rect;
2722 UINT status,x,y;
2724 GetClientRect (hwnd, &rect);
2725 status=0;
2726 x=lpht->pt.x;
2727 y=lpht->pt.y;
2728 if (x < rect.left) status|=TVHT_TOLEFT;
2729 if (x > rect.right) status|=TVHT_TORIGHT;
2730 if (y < rect.top ) status|=TVHT_ABOVE;
2731 if (y > rect.bottom) status|=TVHT_BELOW;
2733 if (status) {
2734 lpht->flags=status;
2735 return 0;
2738 wineItem=TREEVIEW_HitTestPoint (hwnd, lpht->pt);
2739 if (!wineItem) {
2740 lpht->flags=TVHT_NOWHERE;
2741 return 0;
2744 /* FIXME: implement other flags
2745 * Assign the appropriate flags depending on the click location
2746 * Intitialize flags before to "|=" it...
2748 lpht->flags=0;
2750 if (x < wineItem->expandBox.left)
2752 lpht->flags |= TVHT_ONITEMINDENT;
2754 else if ( ( x >= wineItem->expandBox.left) &&
2755 ( x <= wineItem->expandBox.right))
2757 lpht->flags |= TVHT_ONITEMBUTTON;
2759 else if (x < wineItem->rect.right)
2761 lpht->flags |= TVHT_ONITEMLABEL;
2763 else
2765 lpht->flags|=TVHT_ONITEMRIGHT;
2768 lpht->hItem=wineItem->hItem;
2770 return (LRESULT) wineItem->hItem;
2773 LRESULT WINAPI
2774 TREEVIEW_EndEditLabelNow (HWND hwnd, WPARAM wParam, LPARAM lParam)
2776 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
2777 TREEVIEW_ITEM *editedItem = TREEVIEW_ValidItem (infoPtr, infoPtr->editItem);
2778 BOOL bRevert = (BOOL)wParam;
2779 BOOL bReturn = ! bRevert;
2781 if ( ! (BOOL)wParam ) /* wParam is set to true to cancel the edition */
2783 if ( TREEVIEW_SendDispInfoNotify( /* return true to cancel edition */
2784 hwnd,
2785 editedItem,
2786 TVN_ENDLABELEDIT,
2789 bRevert = TRUE;
2790 bReturn = FALSE;
2794 if (bRevert == FALSE) /* Apply the changes */
2796 char tmpText[1024];
2797 int iLength = GetWindowTextA(infoPtr->hwndEdit, tmpText, 1023);
2798 bReturn = FALSE;
2800 if (iLength == 0)
2802 ERR("Problem retreiving new item label.");
2804 else if (iLength >= 1023)
2806 ERR(
2807 "Insuficient space to retrieve new item label, new label ignored.");
2809 else
2811 if (strcmp( tmpText, editedItem->pszText ) == 0)
2812 /* Do nothing if the label has not changed */
2813 bReturn = TRUE;
2814 else
2816 LPSTR tmpLabel = COMCTL32_Alloc( iLength+1 );
2818 if ( tmpLabel == NULL )
2819 ERR(
2820 "OutOfMemory, cannot allocate space for label");
2821 else
2823 COMCTL32_Free(editedItem->pszText);
2824 editedItem->pszText = tmpLabel;
2825 lstrcpyA( editedItem->pszText, tmpText);
2826 bReturn = TRUE;
2831 ShowWindow(infoPtr->hwndEdit, SW_HIDE);
2832 EnableWindow(infoPtr->hwndEdit, FALSE);
2833 infoPtr->editItem = 0;
2836 return bReturn;
2841 static LRESULT
2842 TREEVIEW_LButtonDoubleClick (HWND hwnd, WPARAM wParam, LPARAM lParam)
2844 TREEVIEW_ITEM *wineItem;
2845 POINT pt;
2847 TRACE("\n");
2848 pt.x = (INT)LOWORD(lParam);
2849 pt.y = (INT)HIWORD(lParam);
2850 SetFocus (hwnd);
2852 wineItem=TREEVIEW_HitTestPoint (hwnd, pt);
2853 if (!wineItem) return 0;
2854 TRACE("item %d \n",(INT)wineItem->hItem);
2856 if (TREEVIEW_SendSimpleNotify (hwnd, NM_DBLCLK)!=TRUE) { /* FIXME!*/
2857 TREEVIEW_Expand (hwnd, (WPARAM) TVE_TOGGLE, (LPARAM) wineItem->hItem);
2859 return TRUE;
2863 static LRESULT
2864 TREEVIEW_LButtonDown (HWND hwnd, WPARAM wParam, LPARAM lParam)
2866 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
2867 INT iItem;
2868 TVHITTESTINFO ht;
2870 ht.pt.x = (INT)LOWORD(lParam);
2871 ht.pt.y = (INT)HIWORD(lParam);
2873 SetFocus (hwnd);
2874 iItem=TREEVIEW_HitTest (hwnd, (LPARAM) &ht);
2875 TRACE("item %d \n",iItem);
2877 if (ht.flags & TVHT_ONITEMBUTTON) {
2878 TREEVIEW_Expand (hwnd, (WPARAM) TVE_TOGGLE, (LPARAM) iItem);
2880 else
2882 infoPtr->uInternalStatus|=TV_LDRAG;
2885 return 0;
2888 static LRESULT
2889 TREEVIEW_LButtonUp (HWND hwnd, WPARAM wParam, LPARAM lParam)
2891 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
2892 INT iItem;
2893 TREEVIEW_ITEM *editItem;
2894 TVHITTESTINFO ht;
2896 ht.pt.x = (INT)LOWORD(lParam);
2897 ht.pt.y = (INT)HIWORD(lParam);
2899 TRACE("\n");
2901 /* Return true to cancel default behaviour */
2902 if ( TREEVIEW_SendSimpleNotify (hwnd, NM_CLICK) )
2903 return 0;
2905 /* Get the item */
2906 iItem = TREEVIEW_HitTest (hwnd, (LPARAM) &ht);
2907 if (!iItem)
2908 return 0;
2910 editItem = TREEVIEW_ValidItem(infoPtr, (HTREEITEM)iItem);
2912 infoPtr->uInternalStatus &= ~(TV_LDRAG | TV_LDRAGGING);
2915 * If the style allow editing and the node is already selected
2916 * and the click occured on the item label...
2918 if ( ( GetWindowLongA( hwnd, GWL_STYLE) & TVS_EDITLABELS ) &&
2919 ( editItem->state & TVIS_SELECTED ) &&
2920 ( ht.flags & TVHT_ONITEMLABEL ))
2922 if ( infoPtr->editItem == 0 ) /* If we are not curently editing */
2924 if ( TREEVIEW_SendDispInfoNotify( /* Return true to cancel edition */
2925 hwnd,
2926 editItem,
2927 TVN_BEGINLABELEDIT,
2930 return 0;
2933 TRACE("Edit started for %s.\n", editItem->pszText);
2934 infoPtr->editItem = editItem->hItem;
2936 SetWindowPos (
2937 infoPtr->hwndEdit,
2938 HWND_TOP,
2939 editItem->text.left - 2,
2940 editItem->text.top - 1,
2941 editItem->text.right - editItem->text.left + 20 ,
2942 editItem->text.bottom - editItem->text.top + 3,
2943 SWP_DRAWFRAME );
2945 SetWindowTextA( infoPtr->hwndEdit, editItem->pszText );
2946 SendMessageA ( infoPtr->hwndEdit, EM_SETSEL, 0, -1 );
2947 SetFocus ( infoPtr->hwndEdit);
2948 ShowWindow ( infoPtr->hwndEdit, SW_SHOW);
2951 else if ( infoPtr->editItem != 0 ) /* If we are curently editing */
2953 TREEVIEW_EndEditLabelNow(hwnd, (WPARAM)FALSE, 0);
2955 else if ( ht.flags & (TVHT_ONITEMLABEL | TVHT_ONITEMICON))
2957 TREEVIEW_DoSelectItem (
2958 hwnd,
2959 TVGN_CARET,
2960 (HTREEITEM)iItem,
2961 TVC_BYMOUSE);
2964 return 0;
2968 static LRESULT
2969 TREEVIEW_RButtonDown (HWND hwnd, WPARAM wParam, LPARAM lParam)
2971 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
2973 TRACE("\n");
2974 infoPtr->uInternalStatus|=TV_RDRAG;
2975 return 0;
2978 static LRESULT
2979 TREEVIEW_RButtonUp (HWND hwnd, WPARAM wParam, LPARAM lParam)
2981 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
2983 TRACE("\n");
2984 if (TREEVIEW_SendSimpleNotify (hwnd, NM_RCLICK)) return 0;
2985 infoPtr->uInternalStatus&= ~(TV_RDRAG | TV_RDRAGGING);
2986 return 0;
2990 static LRESULT
2991 TREEVIEW_MouseMove (HWND hwnd, WPARAM wParam, LPARAM lParam)
2993 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
2994 TREEVIEW_ITEM *hotItem;
2995 POINT pt;
2997 pt.x=(INT) LOWORD (lParam);
2998 pt.y=(INT) HIWORD (lParam);
2999 hotItem=TREEVIEW_HitTestPoint (hwnd, pt);
3000 if (!hotItem) return 0;
3001 infoPtr->focusItem=hotItem->hItem;
3003 if ( GetWindowLongA( hwnd, GWL_STYLE) & TVS_DISABLEDRAGDROP) return 0;
3005 if (infoPtr->uInternalStatus & TV_LDRAG) {
3006 TREEVIEW_SendTreeviewDnDNotify (hwnd, TVN_BEGINDRAG, hotItem->hItem, pt);
3007 infoPtr->uInternalStatus &= ~TV_LDRAG;
3008 infoPtr->uInternalStatus |= TV_LDRAGGING;
3009 infoPtr->dropItem=hotItem->hItem;
3010 return 0;
3013 if (infoPtr->uInternalStatus & TV_RDRAG) {
3014 TREEVIEW_SendTreeviewDnDNotify (hwnd, TVN_BEGINRDRAG, hotItem->hItem, pt);
3015 infoPtr->uInternalStatus &= ~TV_RDRAG;
3016 infoPtr->uInternalStatus |= TV_RDRAGGING;
3017 infoPtr->dropItem=hotItem->hItem;
3018 return 0;
3021 return 0;
3025 static LRESULT
3026 TREEVIEW_CreateDragImage (HWND hwnd, WPARAM wParam, LPARAM lParam)
3028 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
3029 TREEVIEW_ITEM *dragItem;
3030 INT cx,cy;
3031 HDC hdc,htopdc;
3032 HWND hwtop;
3033 HBITMAP hbmp,hOldbmp;
3034 SIZE size;
3035 RECT rc;
3036 HFONT hOldFont;
3037 char *itemtxt;
3039 TRACE("\n");
3040 if (!(infoPtr->himlNormal)) return 0;
3041 dragItem=TREEVIEW_ValidItem (infoPtr, (HTREEITEM) lParam);
3043 if (!dragItem) return 0;
3044 itemtxt=dragItem->pszText;
3046 hwtop=GetDesktopWindow ();
3047 htopdc= GetDC (hwtop);
3048 hdc=CreateCompatibleDC (htopdc);
3050 hOldFont=SelectObject (hdc, infoPtr->hFont);
3051 GetTextExtentPoint32A (hdc, itemtxt, lstrlenA (itemtxt), &size);
3052 TRACE("%d %d %s %d\n",size.cx,size.cy,itemtxt,lstrlenA(itemtxt));
3053 hbmp=CreateCompatibleBitmap (htopdc, size.cx, size.cy);
3054 hOldbmp=SelectObject (hdc, hbmp);
3056 ImageList_GetIconSize (infoPtr->himlNormal, &cx, &cy);
3057 size.cx+=cx;
3058 if (cy>size.cy) size.cy=cy;
3060 infoPtr->dragList=ImageList_Create (size.cx, size.cy, ILC_COLOR, 10, 10);
3061 ImageList_Draw (infoPtr->himlNormal, dragItem->iImage, hdc, 0, 0, ILD_NORMAL);
3064 ImageList_GetImageInfo (infoPtr->himlNormal, dragItem->hItem, &iminfo);
3065 ImageList_AddMasked (infoPtr->dragList, iminfo.hbmImage, CLR_DEFAULT);
3068 /* draw item text */
3070 SetRect (&rc, cx, 0, size.cx,size.cy);
3071 DrawTextA (hdc, itemtxt, lstrlenA (itemtxt), &rc, DT_LEFT);
3072 SelectObject (hdc, hOldFont);
3073 SelectObject (hdc, hOldbmp);
3075 ImageList_Add (infoPtr->dragList, hbmp, 0);
3077 DeleteDC (hdc);
3078 DeleteObject (hbmp);
3079 ReleaseDC (hwtop, htopdc);
3081 return (LRESULT)infoPtr->dragList;
3085 static LRESULT
3086 TREEVIEW_DoSelectItem (HWND hwnd, INT action, HTREEITEM newSelect, INT cause)
3089 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
3090 TREEVIEW_ITEM *prevItem,*wineItem;
3091 INT prevSelect;
3093 wineItem = TREEVIEW_ValidItem (infoPtr, (HTREEITEM)newSelect);
3095 TRACE("Entering item %d, flag %x, cause %x, state %d\n",
3096 (INT)newSelect,
3097 action,
3098 cause,
3099 wineItem->state);
3101 if ( (wineItem) && (wineItem->parent))
3104 * If the item has a collapse parent expand the parent so he
3105 * can expose the item
3107 TREEVIEW_ITEM *parentItem = TREEVIEW_ValidItem (infoPtr, wineItem->parent);
3108 if ( !(parentItem->state & TVIS_EXPANDED))
3109 TREEVIEW_Expand (hwnd, TVE_EXPAND, (LPARAM) wineItem->parent);
3112 switch (action)
3114 case TVGN_CARET:
3115 prevSelect=(INT)infoPtr->selectedItem;
3117 if ((HTREEITEM)prevSelect==newSelect)
3118 return FALSE;
3120 prevItem= TREEVIEW_ValidItem (infoPtr, (HTREEITEM)prevSelect);
3122 if (newSelect)
3123 if (TREEVIEW_SendTreeviewNotify(
3124 hwnd,
3125 TVN_SELCHANGING,
3126 cause,
3127 (HTREEITEM)prevSelect,
3128 (HTREEITEM)newSelect))
3129 return FALSE; /* FIXME: OK? */
3131 if (prevItem)
3132 prevItem->state &= ~TVIS_SELECTED;
3133 if (wineItem)
3134 wineItem->state |= TVIS_SELECTED;
3136 infoPtr->selectedItem=(HTREEITEM)newSelect;
3138 TREEVIEW_SendTreeviewNotify(
3139 hwnd,
3140 TVN_SELCHANGED,
3141 cause,
3142 (HTREEITEM)prevSelect,
3143 (HTREEITEM)newSelect);
3145 break;
3147 case TVGN_DROPHILITE:
3148 prevItem= TREEVIEW_ValidItem (infoPtr, infoPtr->dropItem);
3150 if (prevItem)
3151 prevItem->state &= ~TVIS_DROPHILITED;
3153 infoPtr->dropItem=(HTREEITEM)newSelect;
3155 if (wineItem)
3156 wineItem->state |=TVIS_DROPHILITED;
3158 break;
3160 case TVGN_FIRSTVISIBLE:
3161 FIXME("FIRSTVISIBLE not implemented\n");
3162 break;
3165 TREEVIEW_QueueRefresh (hwnd);
3167 TRACE("Leaving state %d\n", wineItem->state);
3168 return TRUE;
3171 /* FIXME: handle NM_KILLFocus enzo */
3172 static LRESULT
3173 TREEVIEW_SelectItem (HWND hwnd, WPARAM wParam, LPARAM lParam)
3176 return TREEVIEW_DoSelectItem (hwnd, wParam, (HTREEITEM) lParam, TVC_UNKNOWN);
3182 static LRESULT
3183 TREEVIEW_GetFont (HWND hwnd, WPARAM wParam, LPARAM lParam)
3186 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
3188 TRACE("%x\n",infoPtr->hFont);
3189 return infoPtr->hFont;
3192 static LRESULT
3193 TREEVIEW_SetFont (HWND hwnd, WPARAM wParam, LPARAM lParam)
3196 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
3197 TEXTMETRICA tm;
3198 LOGFONTA logFont;
3199 HFONT hFont, hOldFont;
3200 INT height;
3201 HDC hdc;
3203 TRACE("%x %lx\n",wParam, lParam);
3205 infoPtr->hFont = (HFONT)wParam;
3207 hFont = infoPtr->hFont ? infoPtr->hFont : GetStockObject (SYSTEM_FONT);
3209 GetObjectA (infoPtr->hFont, sizeof (LOGFONTA), &logFont);
3210 logFont.lfWeight=FW_BOLD;
3211 infoPtr->hBoldFont = CreateFontIndirectA (&logFont);
3213 hdc = GetDC (0);
3214 hOldFont = SelectObject (hdc, hFont);
3215 GetTextMetricsA (hdc, &tm);
3216 height= tm.tmHeight + tm.tmExternalLeading;
3217 if (height>infoPtr->uRealItemHeight)
3218 infoPtr->uRealItemHeight=height;
3219 SelectObject (hdc, hOldFont);
3220 ReleaseDC (0, hdc);
3222 if (lParam)
3223 TREEVIEW_QueueRefresh (hwnd);
3225 return 0;
3230 static LRESULT
3231 TREEVIEW_VScroll (HWND hwnd, WPARAM wParam, LPARAM lParam)
3234 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
3235 int maxHeight;
3237 TRACE("wp %x, lp %lx\n", wParam, lParam);
3238 if (!infoPtr->uInternalStatus & TV_VSCROLL) return FALSE;
3240 switch (LOWORD (wParam)) {
3241 case SB_LINEUP:
3242 if (!infoPtr->cy) return FALSE;
3243 infoPtr->cy -= infoPtr->uRealItemHeight;
3244 if (infoPtr->cy < 0) infoPtr->cy=0;
3245 break;
3246 case SB_LINEDOWN:
3247 maxHeight=infoPtr->uTotalHeight-infoPtr->uVisibleHeight;
3248 if (infoPtr->cy == maxHeight) return FALSE;
3249 infoPtr->cy += infoPtr->uRealItemHeight;
3250 if (infoPtr->cy > maxHeight)
3251 infoPtr->cy = maxHeight;
3252 break;
3253 case SB_PAGEUP:
3254 if (!infoPtr->cy) return FALSE;
3255 infoPtr->cy -= infoPtr->uVisibleHeight;
3256 if (infoPtr->cy < 0) infoPtr->cy=0;
3257 break;
3258 case SB_PAGEDOWN:
3259 maxHeight=infoPtr->uTotalHeight-infoPtr->uVisibleHeight;
3260 if (infoPtr->cy == maxHeight) return FALSE;
3261 infoPtr->cy += infoPtr->uVisibleHeight;
3262 if (infoPtr->cy > maxHeight)
3263 infoPtr->cy = maxHeight;
3264 break;
3265 case SB_THUMBTRACK:
3266 infoPtr->cy = HIWORD (wParam);
3267 break;
3271 TREEVIEW_QueueRefresh (hwnd);
3272 return TRUE;
3275 static LRESULT
3276 TREEVIEW_HScroll (HWND hwnd, WPARAM wParam, LPARAM lParam)
3278 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
3279 int maxWidth;
3281 TRACE("wp %lx, lp %x\n", lParam, wParam);
3283 if (!infoPtr->uInternalStatus & TV_HSCROLL) return FALSE;
3285 switch (LOWORD (wParam)) {
3286 case SB_LINEUP:
3287 if (!infoPtr->cx) return FALSE;
3288 infoPtr->cx -= infoPtr->uRealItemHeight;
3289 if (infoPtr->cx < 0) infoPtr->cx=0;
3290 break;
3291 case SB_LINEDOWN:
3292 maxWidth=infoPtr->uTotalWidth-infoPtr->uVisibleWidth;
3293 if (infoPtr->cx == maxWidth) return FALSE;
3294 infoPtr->cx += infoPtr->uRealItemHeight; /*FIXME */
3295 if (infoPtr->cx > maxWidth)
3296 infoPtr->cx = maxWidth;
3297 break;
3298 case SB_PAGEUP:
3299 if (!infoPtr->cx) return FALSE;
3300 infoPtr->cx -= infoPtr->uVisibleWidth;
3301 if (infoPtr->cx < 0) infoPtr->cx=0;
3302 break;
3303 case SB_PAGEDOWN:
3304 maxWidth=infoPtr->uTotalWidth-infoPtr->uVisibleWidth;
3305 if (infoPtr->cx == maxWidth) return FALSE;
3306 infoPtr->cx += infoPtr->uVisibleWidth;
3307 if (infoPtr->cx > maxWidth)
3308 infoPtr->cx = maxWidth;
3309 break;
3310 case SB_THUMBTRACK:
3311 infoPtr->cx = HIWORD (wParam);
3312 break;
3316 TREEVIEW_QueueRefresh (hwnd);
3317 return TRUE;
3321 static LRESULT
3322 TREEVIEW_KeyDown (HWND hwnd, WPARAM wParam, LPARAM lParam)
3324 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
3325 HTREEITEM hNewSelection = 0;
3326 INT scrollNeeds = -1;
3327 INT cyChangeNeeds = -1;
3328 INT prevSelect = (INT)infoPtr->selectedItem;
3330 TREEVIEW_ITEM *prevItem =
3331 (prevSelect != 0 ) ?
3332 TREEVIEW_ValidItem (infoPtr, (HTREEITEM)prevSelect) :
3333 NULL;
3335 TREEVIEW_ITEM *newItem = NULL;
3337 TRACE("%x %lx\n",wParam, lParam);
3339 if (prevSelect == 0)
3340 return FALSE;
3342 switch (wParam) {
3343 case VK_UP:
3344 newItem=TREEVIEW_GetPrevListItem (infoPtr, prevItem);
3346 if (!newItem)
3347 newItem=& infoPtr->items[(INT)infoPtr->TopRootItem];
3349 hNewSelection = newItem->hItem;
3351 if (! newItem->visible)
3352 scrollNeeds = SB_LINEUP;
3354 break;
3356 case VK_DOWN:
3357 newItem=TREEVIEW_GetNextListItem (infoPtr, prevItem);
3359 if (!newItem)
3360 newItem=prevItem;
3362 hNewSelection = newItem->hItem;
3364 if (! newItem->visible)
3365 scrollNeeds = SB_LINEDOWN;
3367 break;
3369 case VK_HOME:
3370 newItem = &infoPtr->items[(INT)infoPtr->TopRootItem];
3371 hNewSelection = newItem->hItem;
3372 cyChangeNeeds = 0;
3373 break;
3375 case VK_END:
3376 newItem = &infoPtr->items[(INT)infoPtr->TopRootItem];
3377 newItem = TREEVIEW_GetLastListItem (infoPtr, newItem);
3378 hNewSelection = newItem->hItem;
3380 if (! newItem->visible)
3381 cyChangeNeeds = infoPtr->uTotalHeight-infoPtr->uVisibleHeight;
3383 break;
3385 case VK_LEFT:
3386 if ( (prevItem->cChildren > 0) && (prevItem->state & TVIS_EXPANDED) )
3388 TREEVIEW_Expand(hwnd, TVE_COLLAPSE, prevSelect );
3390 else if ((INT)prevItem->parent)
3392 newItem = (& infoPtr->items[(INT)prevItem->parent]);
3393 if (! newItem->visible)
3394 /* FIXME find a way to make this item the first visible... */
3395 newItem = NULL;
3397 hNewSelection = newItem->hItem;
3400 break;
3402 case VK_RIGHT:
3403 if ( ( prevItem->cChildren > 0) ||
3404 ( prevItem->cChildren == I_CHILDRENCALLBACK))
3406 if (! (prevItem->state & TVIS_EXPANDED))
3407 TREEVIEW_Expand(hwnd, TVE_EXPAND, prevSelect );
3408 else
3410 newItem = (& infoPtr->items[(INT)prevItem->firstChild]);
3411 hNewSelection = newItem->hItem;
3415 break;
3417 case VK_ADD:
3418 if (! (prevItem->state & TVIS_EXPANDED))
3419 TREEVIEW_Expand(hwnd, TVE_EXPAND, prevSelect );
3420 break;
3422 case VK_SUBTRACT:
3423 if (prevItem->state & TVIS_EXPANDED)
3424 TREEVIEW_Expand(hwnd, TVE_COLLAPSE, prevSelect );
3425 break;
3427 case VK_PRIOR:
3429 newItem=TREEVIEW_GetListItem(
3430 infoPtr,
3431 prevItem,
3432 -1*(TREEVIEW_GetVisibleCount(hwnd,0,0)-3));
3433 if (!newItem)
3434 newItem=prevItem;
3436 hNewSelection = newItem->hItem;
3438 if (! newItem->visible)
3439 scrollNeeds = SB_PAGEUP;
3441 break;
3443 case VK_NEXT:
3444 newItem=TREEVIEW_GetListItem(
3445 infoPtr,
3446 prevItem,
3447 TREEVIEW_GetVisibleCount(hwnd,0,0)-3);
3449 if (!newItem)
3450 newItem=prevItem;
3452 hNewSelection = newItem->hItem;
3454 if (! newItem->visible)
3455 scrollNeeds = SB_PAGEDOWN;
3457 break;
3459 case VK_BACK:
3461 case VK_RETURN:
3463 default:
3464 FIXME("%x not implemented\n", wParam);
3465 break;
3468 if (hNewSelection)
3471 This works but does not send notification...
3473 prevItem->state &= ~TVIS_SELECTED;
3474 newItem->state |= TVIS_SELECTED;
3475 infoPtr->selectedItem = hNewSelection;
3476 TREEVIEW_QueueRefresh (hwnd);
3479 if ( TREEVIEW_DoSelectItem(
3480 hwnd,
3481 TVGN_CARET,
3482 (HTREEITEM)hNewSelection,
3483 TVC_BYKEYBOARD))
3485 /* If selection change is allowed for the new item, perform scrolling */
3486 if (scrollNeeds != -1)
3487 TREEVIEW_VScroll(hwnd, scrollNeeds, 0);
3489 if (cyChangeNeeds != -1)
3490 infoPtr->cy = cyChangeNeeds;
3492 /* FIXME: Something happen in the load the in the two weeks before
3493 april 1st 1999 which makes this SetFocus mandatory otherwise, the focus
3494 is lost... However the SetFocus should not be required...*/
3496 SetFocus(hwnd);
3500 return FALSE;
3504 static LRESULT
3505 TREEVIEW_GetScrollTime (HWND hwnd)
3507 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
3509 return infoPtr->uScrollTime;
3513 static LRESULT
3514 TREEVIEW_SetScrollTime (HWND hwnd, UINT uScrollTime)
3516 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
3517 UINT uOldScrollTime = infoPtr->uScrollTime;
3519 infoPtr->uScrollTime = min (uScrollTime, 100);
3521 return uOldScrollTime;
3525 static LRESULT WINAPI
3526 TREEVIEW_WindowProc (HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
3528 switch (uMsg) {
3529 case TVM_INSERTITEMA:
3530 return TREEVIEW_InsertItemA (hwnd, wParam, lParam);
3532 case TVM_INSERTITEMW:
3533 return TREEVIEW_InsertItemW(hwnd,wParam,lParam);;
3535 case TVM_DELETEITEM:
3536 return TREEVIEW_DeleteItem (hwnd, wParam, lParam);
3538 case TVM_EXPAND:
3539 return TREEVIEW_Expand (hwnd, wParam, lParam);
3541 case TVM_GETITEMRECT:
3542 return TREEVIEW_GetItemRect (hwnd, wParam, lParam);
3544 case TVM_GETCOUNT:
3545 return TREEVIEW_GetCount (hwnd, wParam, lParam);
3547 case TVM_GETINDENT:
3548 return TREEVIEW_GetIndent (hwnd);
3550 case TVM_SETINDENT:
3551 return TREEVIEW_SetIndent (hwnd, wParam);
3553 case TVM_GETIMAGELIST:
3554 return TREEVIEW_GetImageList (hwnd, wParam, lParam);
3556 case TVM_SETIMAGELIST:
3557 return TREEVIEW_SetImageList (hwnd, wParam, lParam);
3559 case TVM_GETNEXTITEM:
3560 return TREEVIEW_GetNextItem (hwnd, wParam, lParam);
3562 case TVM_SELECTITEM:
3563 return TREEVIEW_SelectItem (hwnd, wParam, lParam);
3565 case TVM_GETITEMA:
3566 return TREEVIEW_GetItemA (hwnd, wParam, lParam);
3568 case TVM_GETITEMW:
3569 FIXME("Unimplemented msg TVM_GETITEMW\n");
3570 return 0;
3572 case TVM_SETITEMA:
3573 return TREEVIEW_SetItemA (hwnd, wParam, lParam);
3575 case TVM_SETITEMW:
3576 FIXME("Unimplemented msg TVM_SETITEMW\n");
3577 return 0;
3579 case TVM_EDITLABELA:
3580 FIXME("Unimplemented msg TVM_EDITLABELA \n");
3581 return 0;
3583 case TVM_EDITLABELW:
3584 FIXME("Unimplemented msg TVM_EDITLABELW \n");
3585 return 0;
3587 case TVM_GETEDITCONTROL:
3588 return TREEVIEW_GetEditControl (hwnd);
3590 case TVM_GETVISIBLECOUNT:
3591 return TREEVIEW_GetVisibleCount (hwnd, wParam, lParam);
3593 case TVM_HITTEST:
3594 return TREEVIEW_HitTest (hwnd, lParam);
3596 case TVM_CREATEDRAGIMAGE:
3597 return TREEVIEW_CreateDragImage (hwnd, wParam, lParam);
3599 case TVM_SORTCHILDREN:
3600 return TREEVIEW_SortChildren (hwnd, wParam, lParam);
3602 case TVM_ENSUREVISIBLE:
3603 FIXME("Unimplemented msg TVM_ENSUREVISIBLE\n");
3604 return 0;
3606 case TVM_SORTCHILDRENCB:
3607 return TREEVIEW_SortChildrenCB(hwnd, wParam, lParam);
3609 case TVM_ENDEDITLABELNOW:
3610 return TREEVIEW_EndEditLabelNow (hwnd, wParam, lParam);
3612 case TVM_GETISEARCHSTRINGA:
3613 FIXME("Unimplemented msg TVM_GETISEARCHSTRINGA\n");
3614 return 0;
3616 case TVM_GETISEARCHSTRINGW:
3617 FIXME("Unimplemented msg TVM_GETISEARCHSTRINGW\n");
3618 return 0;
3620 case TVM_GETTOOLTIPS:
3621 return TREEVIEW_GetToolTips (hwnd);
3623 case TVM_SETTOOLTIPS:
3624 return TREEVIEW_SetToolTips (hwnd, wParam);
3626 case TVM_SETINSERTMARK:
3627 FIXME("Unimplemented msg TVM_SETINSERTMARK\n");
3628 return 0;
3630 case TVM_SETITEMHEIGHT:
3631 return TREEVIEW_SetItemHeight (hwnd, wParam);
3633 case TVM_GETITEMHEIGHT:
3634 return TREEVIEW_GetItemHeight (hwnd);
3636 case TVM_SETBKCOLOR:
3637 return TREEVIEW_SetBkColor (hwnd, wParam, lParam);
3639 case TVM_SETTEXTCOLOR:
3640 return TREEVIEW_SetTextColor (hwnd, wParam, lParam);
3642 case TVM_GETBKCOLOR:
3643 return TREEVIEW_GetBkColor (hwnd);
3645 case TVM_GETTEXTCOLOR:
3646 return TREEVIEW_GetTextColor (hwnd);
3648 case TVM_SETSCROLLTIME:
3649 return TREEVIEW_SetScrollTime (hwnd, (UINT)wParam);
3651 case TVM_GETSCROLLTIME:
3652 return TREEVIEW_GetScrollTime (hwnd);
3654 case TVM_GETITEMSTATE:
3655 return TREEVIEW_GetItemState (hwnd,wParam, lParam);
3657 case TVM_GETLINECOLOR:
3658 return TREEVIEW_GetLineColor (hwnd,wParam, lParam);
3660 case TVM_SETLINECOLOR:
3661 return TREEVIEW_SetLineColor (hwnd,wParam, lParam);
3663 case TVM_SETINSERTMARKCOLOR:
3664 FIXME("Unimplemented msg TVM_SETINSERTMARKCOLOR\n");
3665 return 0;
3667 case TVM_SETUNICODEFORMAT:
3668 FIXME("Unimplemented msg TVM_SETUNICODEFORMAT\n");
3669 return 0;
3671 case TVM_GETUNICODEFORMAT:
3672 FIXME("Unimplemented msg TVM_GETUNICODEFORMAT\n");
3673 return 0;
3675 case WM_COMMAND:
3676 return TREEVIEW_Command (hwnd, wParam, lParam);
3678 case WM_CREATE:
3679 return TREEVIEW_Create (hwnd, wParam, lParam);
3681 case WM_DESTROY:
3682 return TREEVIEW_Destroy (hwnd);
3684 /* case WM_ENABLE: */
3686 case WM_ERASEBKGND:
3687 return TREEVIEW_EraseBackground (hwnd, wParam, lParam);
3689 case WM_GETDLGCODE:
3690 return DLGC_WANTARROWS | DLGC_WANTCHARS;
3692 case WM_PAINT:
3693 return TREEVIEW_Paint (hwnd, wParam, lParam);
3695 case WM_GETFONT:
3696 return TREEVIEW_GetFont (hwnd, wParam, lParam);
3698 case WM_SETFONT:
3699 return TREEVIEW_SetFont (hwnd, wParam, lParam);
3701 case WM_KEYDOWN:
3702 return TREEVIEW_KeyDown (hwnd, wParam, lParam);
3704 case WM_SETFOCUS:
3705 return TREEVIEW_SetFocus (hwnd, wParam, lParam);
3707 case WM_KILLFOCUS:
3708 return TREEVIEW_KillFocus (hwnd, wParam, lParam);
3710 case WM_LBUTTONDOWN:
3711 return TREEVIEW_LButtonDown (hwnd, wParam, lParam);
3713 case WM_LBUTTONUP:
3714 return TREEVIEW_LButtonUp (hwnd, wParam, lParam);
3716 case WM_LBUTTONDBLCLK:
3717 return TREEVIEW_LButtonDoubleClick (hwnd, wParam, lParam);
3719 case WM_RBUTTONDOWN:
3720 return TREEVIEW_RButtonDown (hwnd, wParam, lParam);
3722 case WM_RBUTTONUP:
3723 return TREEVIEW_RButtonUp (hwnd, wParam, lParam);
3725 case WM_MOUSEMOVE:
3726 return TREEVIEW_MouseMove (hwnd, wParam, lParam);
3728 case WM_STYLECHANGED:
3729 return TREEVIEW_StyleChanged (hwnd, wParam, lParam);
3731 /* case WM_SYSCOLORCHANGE: */
3732 /* case WM_SETREDRAW: */
3734 case WM_TIMER:
3735 return TREEVIEW_HandleTimer (hwnd, wParam, lParam);
3737 case WM_SIZE:
3738 return TREEVIEW_Size (hwnd, wParam,lParam);
3740 case WM_HSCROLL:
3741 return TREEVIEW_HScroll (hwnd, wParam, lParam);
3742 case WM_VSCROLL:
3743 return TREEVIEW_VScroll (hwnd, wParam, lParam);
3745 case WM_DRAWITEM:
3746 TRACE ("drawItem\n");
3747 return DefWindowProcA (hwnd, uMsg, wParam, lParam);
3749 default:
3750 if (uMsg >= WM_USER)
3751 FIXME("Unknown msg %04x wp=%08x lp=%08lx\n",
3752 uMsg, wParam, lParam);
3753 return DefWindowProcA (hwnd, uMsg, wParam, lParam);
3755 return 0;
3759 VOID
3760 TREEVIEW_Register (void)
3762 WNDCLASSA wndClass;
3764 TRACE("\n");
3766 if (GlobalFindAtomA (WC_TREEVIEWA)) return;
3768 ZeroMemory (&wndClass, sizeof(WNDCLASSA));
3769 wndClass.style = CS_GLOBALCLASS | CS_DBLCLKS;
3770 wndClass.lpfnWndProc = (WNDPROC)TREEVIEW_WindowProc;
3771 wndClass.cbClsExtra = 0;
3772 wndClass.cbWndExtra = sizeof(TREEVIEW_INFO *);
3773 wndClass.hCursor = LoadCursorA (0, IDC_ARROWA);
3774 wndClass.hbrBackground = 0;
3775 wndClass.lpszClassName = WC_TREEVIEWA;
3777 RegisterClassA (&wndClass);
3781 VOID
3782 TREEVIEW_Unregister (void)
3784 if (GlobalFindAtomA (WC_TREEVIEWA))
3785 UnregisterClassA (WC_TREEVIEWA, (HINSTANCE)NULL);