source: trunk/src/comctl32/treeview.c@ 2814

Last change on this file since 2814 was 2814, checked in by cbratschi, 26 years ago

COMCTL32 5.80 changes

File size: 125.1 KB
Line 
1/* $Id: treeview.c,v 1.27 2000-02-17 17:26:51 cbratschi Exp $ */
2/* Treeview control
3 *
4 * Copyright 1998 Eric Kohl <ekohl@abo.rhein-zeitung.de>
5 * Copyright 1998,1999 Alex Priem <alexp@sci.kun.nl>
6 * Copyright 1999 Sylvain St-Germain
7 * Copyright 1999 Achim Hasenmueller
8 * Copyright 1999-2000 Christoph Bratschi
9 *
10 *
11 * TODO:
12 * Using DPA to store the item ptr would be good.
13 * -check custom draw
14 * FIXME: check fontsize. (uRealItemHeight)
15 uHotItem
16 * use separate routine to get item text/image.
17 *
18 * FIXMEs (for personal use)
19 Expand: -ctlmacro expands twice ->toggle.
20 -DblClick: ctlmacro.exe's NM_DBLCLK seems to go wrong (returns FALSE).
21 -treehelper: stack corruption makes big window.
22 *
23 * Status: in progress
24 * Version: 5.80
25 */
26
27/*
28 Most identical with:
29 - Corel 20000212 level
30 - (WINE 991212 level)
31*/
32
33/* CB: todo
34
35 - bug in SetScrollInfo/ShowScrollBar: WM_SIZE and WM_NCPAINT problems (i.e. RegEdit)
36 - WM_HSCROLL: some rects are wrong
37 - TVS_FULLROWSELECT
38 - TVS_SINGLEEXPAND
39 - TVS_TRACKSELECT (called hottrack)
40 - TVS_INFOTIP, TVS_NOTOOLTIPS and tooltip support
41 - WM_ENABLE: draw disabled control
42*/
43
44#include <string.h>
45#include "winbase.h"
46#include "wingdi.h"
47#include "commctrl.h"
48#include "comctl32.h"
49#include "treeview.h"
50
51/* ffs should be in <string.h>. */
52
53//#define OS2LINEHACK //CB: too slow, but looks good
54
55/* Defines, since they do not need to return previous state, and nr
56 * has no side effects in this file.
57 */
58#define tv_test_bit(nr,bf) (((LPBYTE)bf)[nr>>3]&(1<<(nr&7)))
59#define tv_set_bit(nr,bf) ((LPBYTE)bf)[nr>>3]|=(1<<(nr&7))
60#define tv_clear_bit(nr,bf) ((LPBYTE)bf)[nr>>3]&=~(1<<(nr&7))
61
62
63#define TREEVIEW_GetInfoPtr(hwnd) \
64 ((TREEVIEW_INFO *) GetWindowLongA( hwnd, 0))
65
66static BOOL TREEVIEW_SendSimpleNotify (HWND hwnd, UINT code);
67static BOOL TREEVIEW_SendTreeviewNotify (HWND hwnd, UINT code, UINT action, HTREEITEM oldItem, HTREEITEM newItem);
68static BOOL TREEVIEW_SendTreeviewDnDNotify (HWND hwnd, UINT code, HTREEITEM dragItem, POINT pt);
69static BOOL TREEVIEW_SendDispInfoNotify (HWND hwnd, TREEVIEW_ITEM *wineItem, UINT code, UINT what);
70static BOOL TREEVIEW_SendCustomDrawNotify (HWND hwnd, DWORD dwDrawStage, HDC hdc, RECT rc);
71static BOOL TREEVIEW_SendCustomDrawItemNotify (HWND hwnd, HDC hdc, TREEVIEW_ITEM *tvItem, UINT uItemDrawState);
72static LRESULT TREEVIEW_SelectItem (HWND hwnd, WPARAM wParam, LPARAM lParam);
73static LRESULT TREEVIEW_DoSelectItem (HWND hwnd, INT action, HTREEITEM newSelect, INT cause);
74static void TREEVIEW_Refresh(HWND hwnd);
75static void TREEVIEW_Draw(HWND hwnd,HDC hdc,RECT *updateRect);
76static BOOL TREEVIEW_UnqueueRefresh(HWND hwnd,BOOL calc,BOOL refresh);
77static void TREEVIEW_QueueRefresh(HWND hwnd);
78static void TREEVIEW_CalcItem(HWND hwnd,HDC hdc,DWORD dwStyle,TREEVIEW_INFO *infoPtr,TREEVIEW_ITEM *item);
79static BOOL TREEVIEW_CalcItems(HWND hwnd,HDC hdc,TREEVIEW_INFO *infoPtr);
80static LRESULT TREEVIEW_EnsureVisible(HWND hwnd,HTREEITEM hItem);
81
82static LRESULT CALLBACK TREEVIEW_Edit_SubclassProc (HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
83
84LRESULT WINAPI TREEVIEW_EndEditLabelNow (HWND hwnd, BOOL bCancel);
85
86HWND TREEVIEW_EditLabel(HWND hwnd, HTREEITEM hItem,BOOL unicode);
87
88/* helper functions. Work with the assumption that validity of operands
89 is checked beforehand, and that tree state is valid. */
90
91/* FIXME: MS documentation says `GetNextVisibleItem' returns NULL
92 if not succesfull'. Probably only applies to derefencing infoPtr
93 (ie we are offered a valid treeview structure)
94 and not whether there is a next `visible' child.
95 FIXME: check other failures.
96 */
97
98/***************************************************************************
99 * This method returns the TREEVIEW_ITEM object given the handle
100 */
101static TREEVIEW_ITEM* TREEVIEW_ValidItem(
102 TREEVIEW_INFO *infoPtr,
103 HTREEITEM handle)
104{
105 if ((!handle) || (handle>infoPtr->uMaxHandle))
106 return NULL;
107
108 if (tv_test_bit ((INT)handle, infoPtr->freeList))
109 return NULL;
110
111 return &infoPtr->items[(INT)handle];
112}
113
114/***************************************************************************
115 * This function uses cChildren field to decide whether item has children
116 * or not.
117 * Note: return value doesn't reflect physical presence of children.
118 */
119static INT TREEVIEW_HasChildren(
120 HWND hwnd,
121 TREEVIEW_ITEM *wineItem)
122{
123 INT cChildren = 0;
124
125 if ( wineItem->mask & TVIF_CHILDREN )
126 {
127 if (wineItem->cChildren == I_CHILDRENCALLBACK)
128 {
129 TREEVIEW_ITEM tempItem;
130
131 tempItem.hItem = wineItem->hItem;
132 tempItem.state = wineItem->state;
133 tempItem.lParam = wineItem->lParam;
134
135 TREEVIEW_SendDispInfoNotify(hwnd, &tempItem, TVN_GETDISPINFO, TVIF_CHILDREN);
136 cChildren = tempItem.cChildren;
137 }
138 else
139 cChildren = wineItem->cChildren;
140 }
141 else if ( wineItem->firstChild )
142 cChildren = 1;
143
144 return cChildren;
145}
146
147/***************************************************************************
148 * This method returns the last expanded child item of a tree node
149 */
150static TREEVIEW_ITEM *TREEVIEW_GetLastListItem(
151 HWND hwnd,
152 TREEVIEW_INFO *infoPtr,
153 TREEVIEW_ITEM *tvItem)
154{
155 TREEVIEW_ITEM *wineItem = tvItem;
156
157 /*
158 * Get this item last sibling
159 */
160 while (wineItem->sibling)
161 wineItem=& infoPtr->items [(INT)wineItem->sibling];
162
163 /*
164 * If the last sibling has expanded children, restart.
165 */
166 if ((wineItem->state & TVIS_EXPANDED) &&
167 TREEVIEW_HasChildren(hwnd, wineItem))
168 {
169 return TREEVIEW_GetLastListItem(
170 hwnd,
171 infoPtr,
172 &(infoPtr->items[(INT)wineItem->firstChild]));
173 }
174
175 return wineItem;
176}
177
178/***************************************************************************
179 * This method returns the previous physical item in the list not
180 * considering the tree hierarchy.
181 */
182static TREEVIEW_ITEM *TREEVIEW_GetPrevListItem(
183 HWND hwnd,
184 TREEVIEW_INFO *infoPtr,
185 TREEVIEW_ITEM *tvItem)
186{
187 if (tvItem->upsibling)
188 {
189 /*
190 * This item has a upsibling, get the last item. Since, GetLastListItem
191 * first looks at siblings, we must feed it with the first child.
192 */
193 TREEVIEW_ITEM *upItem = &infoPtr->items[(INT)tvItem->upsibling];
194
195 if ( (upItem->state & TVIS_EXPANDED) &&
196 TREEVIEW_HasChildren(hwnd, upItem) )
197 {
198 return TREEVIEW_GetLastListItem(
199 hwnd,
200 infoPtr,
201 &infoPtr->items[(INT)upItem->firstChild]);
202 }
203 else
204 return upItem;
205 }
206 else
207 {
208 /*
209 * this item does not have a upsibling, get the parent
210 */
211 if (tvItem->parent)
212 return &infoPtr->items[(INT)tvItem->parent];
213 }
214
215 return NULL;
216}
217
218
219/***************************************************************************
220 * This method returns the next physical item in the treeview not
221 * considering the tree hierarchy.
222 */
223static TREEVIEW_ITEM *TREEVIEW_GetNextListItem(
224 HWND hwnd,
225 TREEVIEW_INFO *infoPtr,
226 TREEVIEW_ITEM *tvItem)
227{
228 TREEVIEW_ITEM *wineItem = NULL;
229
230 /*
231 * If this item has children and is expanded, return the first child
232 */
233 if ( (tvItem->state & TVIS_EXPANDED) &&
234 TREEVIEW_HasChildren(hwnd, tvItem) )
235 {
236 return (& infoPtr->items[(INT)tvItem->firstChild]);
237 }
238
239
240 /*
241 * try to get the sibling
242 */
243 if (tvItem->sibling)
244 return (& infoPtr->items[(INT)tvItem->sibling]);
245
246 /*
247 * Otherwise, get the parent's sibling.
248 */
249 wineItem=tvItem;
250 while (wineItem->parent) {
251 wineItem=& infoPtr->items [(INT)wineItem->parent];
252 if (wineItem->sibling)
253 return (& infoPtr->items [(INT)wineItem->sibling]);
254 }
255
256 return NULL;
257}
258
259/***************************************************************************
260 * This method returns the nth item starting at the given item. It returns
261 * the last item (or first) we we run out of items.
262 *
263 * Will scroll backward if count is <0.
264 * forward if count is >0.
265 */
266static TREEVIEW_ITEM *TREEVIEW_GetListItem(
267 HWND hwnd,
268 TREEVIEW_INFO *infoPtr,
269 TREEVIEW_ITEM *tvItem,
270 LONG count)
271{
272 TREEVIEW_ITEM *previousItem = NULL;
273 TREEVIEW_ITEM *wineItem = tvItem;
274 LONG iter = 0;
275
276 if (count > 0)
277 {
278 /* Find count item downward */
279 while ((iter++ < count) && (wineItem != NULL))
280 {
281 /* Keep a pointer to the previous in case we ask for more than we got */
282 previousItem = wineItem;
283 wineItem = TREEVIEW_GetNextListItem(hwnd,infoPtr,wineItem);
284 }
285
286 if (wineItem == NULL)
287 wineItem = previousItem;
288 }
289 else if (count < 0)
290 {
291 /* Find count item upward */
292 while ((iter-- > count) && (wineItem != NULL))
293 {
294 /* Keep a pointer to the previous in case we ask for more than we got */
295 previousItem = wineItem;
296 wineItem = TREEVIEW_GetPrevListItem(hwnd,infoPtr,wineItem);
297 }
298
299 if (wineItem == NULL)
300 wineItem = previousItem;
301 }
302 else
303 wineItem = NULL;
304
305 return wineItem;
306}
307
308
309/***************************************************************************
310 * This method
311 */
312static void TREEVIEW_RemoveAllChildren(
313 HWND hwnd,
314 TREEVIEW_ITEM *parentItem)
315{
316 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
317 TREEVIEW_ITEM *killItem;
318 INT kill;
319
320 kill=(INT)parentItem->firstChild;
321 while (kill)
322 {
323 tv_set_bit ( kill, infoPtr->freeList);
324 infoPtr->uNumItems--;
325 killItem=& infoPtr->items[kill];
326 if (killItem->pszText != LPSTR_TEXTCALLBACKW)
327 COMCTL32_Free (killItem->pszText);
328 TREEVIEW_SendTreeviewNotify (hwnd, TVN_DELETEITEM, 0, (HTREEITEM)kill, 0);
329 if (killItem->firstChild)
330 TREEVIEW_RemoveAllChildren (hwnd, killItem);
331 kill=(INT)killItem->sibling;
332 }
333
334 parentItem->firstChild = 0;
335}
336
337
338static void
339TREEVIEW_RemoveItem (HWND hwnd, TREEVIEW_ITEM *wineItem)
340
341{
342 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
343 TREEVIEW_ITEM *parentItem, *upsiblingItem, *siblingItem;
344 INT iItem;
345
346 if (infoPtr->hwndEdit) SetFocus(hwnd);
347
348 iItem=(INT)wineItem->hItem;
349 tv_set_bit(iItem,infoPtr->freeList);
350 infoPtr->uNumItems--;
351 parentItem=NULL;
352 if (wineItem->pszText != LPSTR_TEXTCALLBACKW)
353 COMCTL32_Free (wineItem->pszText);
354
355 TREEVIEW_SendTreeviewNotify (hwnd, TVN_DELETEITEM, 0, (HTREEITEM)iItem, 0);
356
357 if (wineItem->firstChild)
358 TREEVIEW_RemoveAllChildren (hwnd,wineItem);
359
360 if (wineItem->parent) {
361 parentItem=& infoPtr->items [(INT)wineItem->parent];
362 if ((INT)parentItem->firstChild==iItem)
363 parentItem->firstChild=wineItem->sibling;
364 }
365
366 if (iItem==(INT)infoPtr->TopRootItem)
367 infoPtr->TopRootItem=(HTREEITEM)wineItem->sibling;
368 if (wineItem->upsibling) {
369 upsiblingItem=& infoPtr->items [(INT)wineItem->upsibling];
370 upsiblingItem->sibling=wineItem->sibling;
371 }
372 if (wineItem->sibling) {
373 siblingItem=& infoPtr->items [(INT)wineItem->sibling];
374 siblingItem->upsibling=wineItem->upsibling;
375 }
376
377 if (iItem==(INT)infoPtr->selectedItem) {
378 if (!wineItem->upsibling)
379 infoPtr->selectedItem = 0;
380 else
381 TREEVIEW_DoSelectItem(hwnd, TVGN_CARET, wineItem->upsibling, TVC_UNKNOWN);
382 }
383}
384
385/* Note:TREEVIEW_RemoveTree doesn't remove infoPtr itself */
386
387static void TREEVIEW_RemoveTree (HWND hwnd)
388{
389 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
390 TREEVIEW_ITEM *killItem;
391 int i;
392
393 TREEVIEW_EndEditLabelNow(hwnd,TRUE);
394
395 for (i=1; i<=(INT)infoPtr->uMaxHandle; i++)
396 if (!tv_test_bit (i, infoPtr->freeList)) {
397 killItem=& infoPtr->items [i];
398 if (killItem->pszText != LPSTR_TEXTCALLBACKW)
399 COMCTL32_Free (killItem->pszText);
400 TREEVIEW_SendTreeviewNotify
401 (hwnd, TVN_DELETEITEM, 0, killItem->hItem, 0);
402 }
403
404 if (infoPtr->uNumPtrsAlloced) {
405 COMCTL32_Free (infoPtr->items);
406 COMCTL32_Free (infoPtr->freeList);
407 infoPtr->uNumItems=0;
408 infoPtr->uNumPtrsAlloced=0;
409 infoPtr->uMaxHandle=0;
410 infoPtr->cx = infoPtr->cy = 0;
411 }
412}
413
414
415static LRESULT
416TREEVIEW_GetImageList (HWND hwnd, WPARAM wParam, LPARAM lParam)
417{
418 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
419
420 if ((INT)wParam == TVSIL_NORMAL)
421 return (LRESULT) infoPtr->himlNormal;
422 if ((INT)wParam == TVSIL_STATE)
423 return (LRESULT) infoPtr->himlState;
424
425 return 0;
426}
427
428static LRESULT
429TREEVIEW_SetImageList (HWND hwnd, WPARAM wParam, LPARAM lParam)
430{
431 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
432 HIMAGELIST himlTemp;
433
434 switch ((INT)wParam) {
435 case TVSIL_NORMAL:
436 himlTemp = infoPtr->himlNormal;
437 infoPtr->himlNormal = (HIMAGELIST)lParam;
438 return (LRESULT)himlTemp;
439
440 case TVSIL_STATE:
441 himlTemp = infoPtr->himlState;
442 infoPtr->himlState = (HIMAGELIST)lParam;
443 return (LRESULT)himlTemp;
444 }
445
446 return (LRESULT)NULL;
447}
448
449
450
451static LRESULT
452TREEVIEW_SetItemHeight (HWND hwnd, WPARAM wParam)
453{
454 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
455 INT cx,cy,prevHeight = infoPtr->uItemHeight;
456
457 if (wParam==-1)
458 {
459 infoPtr->uItemHeight = -1;
460
461 return prevHeight;
462 }
463
464 ImageList_GetIconSize (infoPtr->himlNormal, &cx, &cy);
465
466 if (wParam > cy) cy = wParam;
467 infoPtr->uItemHeight = cy;
468
469 if (!(GetWindowLongA(hwnd, GWL_STYLE) & TVS_NONEVENHEIGHT))
470 if (infoPtr->uItemHeight & 0x1) infoPtr->uItemHeight += 1;
471
472 return prevHeight;
473}
474
475static LRESULT
476TREEVIEW_GetItemHeight (HWND hwnd)
477{
478 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
479
480 return infoPtr->uItemHeight;
481}
482
483static LRESULT
484TREEVIEW_GetLineColor (HWND hwnd, WPARAM wParam, LPARAM lParam)
485{
486 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
487
488 //TRACE("\n");
489 return (LRESULT) infoPtr->clrLine;
490}
491
492static LRESULT
493TREEVIEW_SetLineColor (HWND hwnd, WPARAM wParam, LPARAM lParam)
494{
495 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
496 COLORREF prevColor=infoPtr->clrLine;
497
498 //TRACE("\n");
499 infoPtr->clrLine=(COLORREF) lParam;
500 return (LRESULT) prevColor;
501}
502
503static LRESULT
504TREEVIEW_GetInsertMarkColor (HWND hwnd, WPARAM wParam, LPARAM lParam)
505{
506 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
507
508 //TRACE("\n");
509 return (LRESULT) infoPtr->clrInsertMark;
510}
511
512static LRESULT
513TREEVIEW_SetInsertMarkColor (HWND hwnd, WPARAM wParam, LPARAM lParam)
514{
515 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
516 COLORREF prevColor=infoPtr->clrInsertMark;
517
518 //TRACE("%d %ld\n",wParam,lParam);
519 infoPtr->clrInsertMark=(COLORREF) lParam;
520 return (LRESULT) prevColor;
521}
522
523static LRESULT
524TREEVIEW_SetInsertMark (HWND hwnd, WPARAM wParam, LPARAM lParam)
525{
526 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
527
528 //FIXME("%d %ld\n",wParam,lParam);
529 if (!TREEVIEW_ValidItem (infoPtr, (HTREEITEM)lParam)) return 0;
530 //FIXME("%d %ld\n",wParam,lParam);
531
532 infoPtr->insertBeforeorAfter=(BOOL) wParam;
533 infoPtr->insertMarkItem=(HTREEITEM) lParam;
534
535 TREEVIEW_Refresh(hwnd);
536
537 return 1;
538}
539
540static LRESULT
541TREEVIEW_SetTextColor (HWND hwnd, WPARAM wParam, LPARAM lParam)
542{
543 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
544 COLORREF prevColor=infoPtr->clrText;
545
546 infoPtr->clrText=(COLORREF) lParam;
547 if (infoPtr->clrText != prevColor)
548 TREEVIEW_Refresh(hwnd);
549
550 return (LRESULT) prevColor;
551}
552
553static LRESULT
554TREEVIEW_GetBkColor (HWND hwnd)
555{
556 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
557
558 return (LRESULT) infoPtr->clrBk;
559}
560
561static LRESULT
562TREEVIEW_SetBkColor (HWND hwnd, WPARAM wParam, LPARAM lParam)
563{
564 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
565 COLORREF prevColor=infoPtr->clrBk;
566
567 infoPtr->clrBk=(COLORREF) lParam;
568 if (infoPtr->clrBk != prevColor)
569 TREEVIEW_Refresh(hwnd);
570
571
572 return (LRESULT) prevColor;
573}
574
575static LRESULT
576TREEVIEW_GetTextColor (HWND hwnd)
577{
578 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
579
580 //TRACE("\n");
581 return (LRESULT) infoPtr->clrText;
582}
583
584
585/* cdmode: custom draw mode as received from app. in first NMCUSTOMDRAW
586 notification */
587
588#define TREEVIEW_LEFT_MARGIN 8
589
590#ifdef OS2LINEHACK
591//CB: hack for PS_DOT bug in Open32 pen handling
592
593BOOL drawPixel;
594HDC drawDC;
595
596VOID CALLBACK TREEVIEW_DDAProc(int x,int y,LPARAM lpData)
597{
598 if (drawPixel) SetPixel(drawDC,x,y,(COLORREF)lpData);
599 drawPixel = !drawPixel;
600}
601
602static void TREEVIEW_Polyline(HDC hdc,const POINT *lppt,int cPoints,COLORREF color)
603{
604 INT x;
605
606 drawPixel = TRUE;
607 drawDC = hdc;
608 for (x = 0;x < cPoints-1;x++)
609 LineDDA(lppt[x].x,lppt[x].y,lppt[x+1].x,lppt[x+1].y,TREEVIEW_DDAProc,color);
610}
611#endif
612
613//pen must be selected!
614
615static void TREEVIEW_DrawVLines(HDC hdc,TREEVIEW_INFO *infoPtr,TREEVIEW_ITEM *item)
616{
617 POINT points[2];
618 INT center;
619
620 center = (item->rect.top+item->rect.bottom)/2;
621//INT cChildren = TREEVIEW_HasChildren(hwnd,wineItem);
622 if ((item->iLevel == 0) && !item->upsibling && item->sibling)
623 {
624 TREEVIEW_ITEM *lastItem = &infoPtr->items[(INT)item->sibling];
625
626 while (lastItem->sibling) lastItem = &infoPtr->items[(INT)lastItem->sibling];
627
628 points[0].y = center;
629 points[1].x = points[0].x = 8;
630 points[1].y = (lastItem->rect.top+lastItem->rect.bottom)/2;
631#ifdef OS2LINEHACK
632 TREEVIEW_Polyline(hdc,points,2,infoPtr->clrLine);
633#else
634 Polyline(hdc,points,2);
635#endif
636 }
637
638 if (item->firstChild && (item->state & TVIS_EXPANDED))
639 {
640 TREEVIEW_ITEM *lastItem = &infoPtr->items[(INT)item->firstChild];
641
642 while (lastItem->sibling) lastItem = &infoPtr->items[(INT)lastItem->sibling];
643
644 points[0].y = (lastItem->upsibling != NULL) ?
645 item->rect.bottom-3: /* is linked to an icon */
646 item->rect.bottom+1; /* is linked to a +/- box */
647 points[1].x = points[0].x = 28+20*item->iLevel-infoPtr->cx;
648 points[1].y = (lastItem->rect.top+lastItem->rect.bottom)/2; /* is linked to a +/- box */
649#ifdef OS2LINEHACK
650 TREEVIEW_Polyline(hdc,points,2,infoPtr->clrLine);
651#else
652 Polyline(hdc,points,2);
653#endif
654 }
655}
656
657static void
658TREEVIEW_DrawItem(HWND hwnd,HDC hdc,TREEVIEW_ITEM *item,DWORD dwStyle,TREEVIEW_INFO *infoPtr)
659{
660 INT center,cditem;
661 HFONT hOldFont;
662
663 if (!item->calculated) TREEVIEW_CalcItem(hwnd,hdc,dwStyle,infoPtr,item);
664
665 if (item->state & TVIS_BOLD)
666 hOldFont = SelectObject(hdc,infoPtr->hBoldFont);
667 else
668 hOldFont = SelectObject(hdc,infoPtr->hFont);
669
670 cditem = 0;
671
672 if (infoPtr->cdmode & CDRF_NOTIFYITEMDRAW)
673 {
674 cditem = TREEVIEW_SendCustomDrawItemNotify(hwnd, hdc, item, CDDS_ITEMPREPAINT);
675
676 if (cditem & CDRF_SKIPDEFAULT) return;
677 }
678
679 //CB: what does COMCTL 5.0 with CDRF_NEWFONT? recalc items?
680
681 /*
682 * Set drawing starting points
683 */
684 center = (item->rect.top+item->rect.bottom)/2; /* this item vertical center */
685
686 /*
687 * Display the tree hierarchy
688 */
689 if (dwStyle & TVS_HASLINES)
690 {
691 /*
692 * Write links to parent node
693 * we draw the L starting from the child to the parent
694 *
695 * points[0] is attached to the current item
696 * points[1] is the L corner
697 * points[2] is attached to the parent or the up sibling
698 */
699 if (dwStyle & TVS_LINESATROOT)
700 {
701 TREEVIEW_ITEM *upNode = NULL;
702 RECT upRect = {0,0,0,0};
703 HPEN hOldPen, hnewPen;
704 POINT points[2];
705
706 hnewPen = CreatePen(PS_DOT, 0, infoPtr->clrLine);
707 hOldPen = SelectObject(hdc,hnewPen);
708
709 TREEVIEW_DrawVLines(hdc,infoPtr,item);
710
711 /*
712 * determine the target location of the line at root, either be linked
713 * to the up sibling or to the parent node.
714 */
715 if (item->upsibling)
716 upNode = TREEVIEW_ValidItem (infoPtr, item->upsibling);
717 else if (item->parent)
718 upNode = TREEVIEW_ValidItem (infoPtr, item->parent);
719
720 if (upNode) upRect = upNode->rect;
721
722 if (item->iLevel == 0)
723 {
724 points[1].x = upRect.left+8;
725 points[0].x = points[1].x + 10;
726 points[1].y = points[0].y = center;
727 } else
728 {
729 points[1].x = 8+(20*item->iLevel)-infoPtr->cx;
730 points[1].y = points[0].y = center;
731 points[0].x = points[1].x + 10;
732 }
733
734 /*
735 * Get a dotted pen
736 */
737#ifdef OS2LINEHACK //CB: workaround for Open32 PS_DOT bug
738 TREEVIEW_Polyline(hdc,points,2,infoPtr->clrLine);
739#else
740 Polyline(hdc,points,2);
741#endif
742 DeleteObject(hnewPen);
743 SelectObject(hdc, hOldPen);
744 }
745 }
746
747 /*
748 * Display the (+/-) signs
749 */
750 if ((dwStyle & TVS_HASBUTTONS) && (dwStyle & TVS_HASLINES))
751 {
752 if (TREEVIEW_HasChildren(hwnd,item))
753 {
754 INT vCenter = (item->expandBox.top+item->expandBox.bottom)/2;
755 INT hCenter = (item->expandBox.right+item->expandBox.left)/2;
756
757 Rectangle(hdc,item->expandBox.left,item->expandBox.top,item->expandBox.right,item->expandBox.bottom);
758
759 MoveToEx(hdc,item->expandBox.left+2,vCenter,NULL);
760 LineTo(hdc,item->expandBox.right-2,vCenter);
761
762 if (!(item->state & TVIS_EXPANDED))
763 {
764 MoveToEx(hdc,hCenter,item->expandBox.top+2,NULL);
765 LineTo(hdc,hCenter,item->expandBox.bottom-2);
766 }
767 }
768 }
769
770 /*
771 * Display the image associated with this item
772 */
773 if ((item->mask & (TVIF_IMAGE | TVIF_SELECTEDIMAGE)) || (dwStyle & TVS_CHECKBOXES))
774 {
775 INT imageIndex;
776 HIMAGELIST *himlp = NULL;
777
778 /* State images are displayed to the left of the Normal image
779 * image number is in state; zero should be `display no image'.
780 * FIXME: that last sentence looks like it needs some checking.
781 */
782 if (infoPtr->himlState)
783 himlp =& infoPtr->himlState;
784 imageIndex = item->state >> 12;
785
786 if (himlp && imageIndex)
787 {
788 imageIndex--; /* see FIXME */
789 ImageList_Draw(*himlp,imageIndex,hdc,item->statebitmap.left,item->statebitmap.top,ILD_NORMAL);
790 }
791
792 /* Now, draw the normal image; can be either selected or
793 * non-selected image.
794 */
795
796 himlp = NULL;
797 if (infoPtr->himlNormal)
798 himlp = &infoPtr->himlNormal; /* get the image list */
799
800 imageIndex = item->iImage;
801 if ((item->state & TVIS_SELECTED) && item->iSelectedImage)
802 {
803 /* The item is curently selected */
804 if (item->iSelectedImage == I_IMAGECALLBACK)
805 TREEVIEW_SendDispInfoNotify(hwnd, item, TVN_GETDISPINFO, TVIF_SELECTEDIMAGE);
806
807 imageIndex = item->iSelectedImage;
808 } else
809 {
810 /* The item is not selected */
811 if (item->iImage == I_IMAGECALLBACK)
812 TREEVIEW_SendDispInfoNotify(hwnd, item, TVN_GETDISPINFO, TVIF_IMAGE);
813
814 imageIndex = item->iImage;
815 }
816
817 if (himlp)
818 {
819 int ovlIdx = 0;
820
821 if(item->stateMask & TVIS_OVERLAYMASK)
822 ovlIdx = item->state & TVIS_OVERLAYMASK;
823
824 ImageList_Draw(*himlp,imageIndex,hdc,item->bitmap.left,item->bitmap.top,ILD_NORMAL | ovlIdx);
825 }
826 }
827
828 /*
829 * Display the text associated with this item
830 */
831 /* Don't paint item's text if it's being edited */
832 if (!infoPtr->hwndEdit || (infoPtr->editItem != item->hItem))
833 {
834 if ((item->mask & TVIF_TEXT) && (item->pszText))
835 {
836 UINT uTextJustify = DT_LEFT;
837 COLORREF oldTextColor = 0;
838 INT oldBkMode;
839 HBRUSH hbrBk = 0;
840 BOOL inFocus = GetFocus() == hwnd;
841
842 oldBkMode = SetBkMode(hdc, TRANSPARENT);
843
844 /* - If item is drop target or it is selected and window is in focus -
845 * use blue background (COLOR_HIGHLIGHT).
846 * - If item is selected, window is not in focus, but it has style
847 * TVS_SHOWSELALWAYS - use grey background (COLOR_BTNFACE)
848 * - Otherwise - don't fill background
849 */
850 if ((item->state & TVIS_DROPHILITED) ||
851 ((item->state & TVIS_SELECTED) &&
852 (inFocus || (GetWindowLongA( hwnd, GWL_STYLE) & TVS_SHOWSELALWAYS))))
853 {
854 if ((item->state & TVIS_DROPHILITED) || inFocus)
855 {
856 hbrBk = CreateSolidBrush(GetSysColor( COLOR_HIGHLIGHT));
857 oldTextColor = SetTextColor(hdc, GetSysColor( COLOR_HIGHLIGHTTEXT));
858 } else
859 {
860 hbrBk = CreateSolidBrush(GetSysColor( COLOR_BTNFACE));
861
862 if (infoPtr->clrText == -1)
863 oldTextColor = SetTextColor(hdc, GetSysColor( COLOR_WINDOWTEXT));
864 else
865 oldTextColor = SetTextColor(hdc, infoPtr->clrText);
866 }
867 } else
868 {
869 if (infoPtr->clrText == -1)
870 oldTextColor = SetTextColor(hdc, GetSysColor( COLOR_WINDOWTEXT));
871 else
872 oldTextColor = SetTextColor(hdc, infoPtr->clrText);
873 }
874
875 if (item->pszText == LPSTR_TEXTCALLBACKW)
876 TREEVIEW_SendDispInfoNotify (hwnd, item, TVN_GETDISPINFO, TVIF_TEXT);
877
878 if (hbrBk)
879 {
880 FillRect(hdc, &item->text, hbrBk);
881 DeleteObject(hbrBk);
882 }
883
884 item->text.left += 2;
885
886 /* Draw it */
887 DrawTextW ( hdc,
888 item->pszText,
889 lstrlenW(item->pszText),
890 &item->text,
891 uTextJustify | DT_VCENTER | DT_SINGLELINE | DT_NOPREFIX);
892
893 item->text.left -= 2;
894
895 /* Restore the hdc state */
896 SetTextColor( hdc, oldTextColor);
897
898 /* Draw the box arround the selected item */
899 if (item->state & TVIS_SELECTED)
900 {
901 HPEN hNewPen = CreatePen(PS_DOT, 0, GetSysColor(COLOR_WINDOWTEXT) );
902 HPEN hOldPen = SelectObject( hdc, hNewPen );
903 INT rop = SetROP2(hdc, R2_XORPEN);
904 POINT points[5];
905
906 points[4].x = points[0].x = item->text.left;
907 points[4].y = points[0].y = item->text.top;
908 points[1].x = item->text.right-1 ;
909 points[1].y = item->text.top;
910 points[2].x = item->text.right-1;
911 points[2].y = item->text.bottom-1;
912 points[3].x = item->text.left;
913 points[3].y = item->text.bottom-1;
914
915 Polyline (hdc,points,5);
916
917 SetROP2(hdc, rop);
918 DeleteObject(hNewPen);
919 SelectObject(hdc, hOldPen);
920 }
921
922 if (oldBkMode != TRANSPARENT)
923 SetBkMode(hdc, oldBkMode);
924 }
925 }
926
927 /* Draw insertion mark if necessary */
928
929 //if (infoPtr->insertMarkItem)
930 // TRACE ("item:%d,mark:%d\n", (int)wineItem->hItem,
931 // (int) infoPtr->insertMarkItem);
932 if (item->hItem == infoPtr->insertMarkItem)
933 {
934 HPEN hNewPen, hOldPen;
935 int offset;
936
937 hNewPen = CreatePen(PS_SOLID, 2, infoPtr->clrInsertMark);
938 hOldPen = SelectObject( hdc, hNewPen );
939
940 if (infoPtr->insertBeforeorAfter)
941 offset = item->text.top+1;
942 else
943 offset = item->text.bottom-1;
944
945 MoveToEx(hdc,item->text.left, offset-3, NULL);
946 LineTo(hdc,item->text.left, offset+3);
947
948 MoveToEx(hdc,item->text.left,offset,NULL);
949 LineTo(hdc,item->text.right-2,offset);
950
951 MoveToEx(hdc,item->text.right-2, offset+3, NULL);
952 LineTo(hdc,item->text.right-2, offset-3);
953
954 DeleteObject(hNewPen);
955
956 SelectObject(hdc, hOldPen);
957 }
958
959 if (cditem & CDRF_NOTIFYPOSTPAINT)
960 cditem = TREEVIEW_SendCustomDrawItemNotify(hwnd, hdc, item, CDDS_ITEMPOSTPAINT);
961
962 SelectObject (hdc, hOldFont);
963}
964
965static LRESULT
966TREEVIEW_GetItemRect (HWND hwnd, WPARAM wParam, LPARAM lParam)
967{
968 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
969 TREEVIEW_ITEM *wineItem;
970 HTREEITEM *iItem;
971 LPRECT lpRect = (LPRECT)lParam;
972
973 /*
974 * validate parameters
975 */
976 if ((infoPtr == NULL) || (lpRect == NULL))
977 return FALSE;
978
979 TREEVIEW_UnqueueRefresh(hwnd,TRUE,TRUE);
980
981 /*
982 * retrieve the item ptr
983 */
984 iItem = (HTREEITEM *) lParam;
985 wineItem = TREEVIEW_ValidItem (infoPtr, *iItem);
986 if (!wineItem || !wineItem->visible)
987 return FALSE;
988
989 /*
990 * If wParam is TRUE return the text size otherwise return
991 * the whole item size
992 */
993 if (wParam)
994 {
995 lpRect->left = wineItem->text.left;
996 lpRect->right = wineItem->text.right;
997 lpRect->bottom = wineItem->text.bottom;
998 lpRect->top = wineItem->text.top;
999 } else
1000 {
1001 lpRect->left = wineItem->rect.left;
1002 lpRect->right = wineItem->rect.right;
1003 lpRect->bottom = wineItem->rect.bottom;
1004 lpRect->top = wineItem->rect.top;
1005 }
1006
1007 return TRUE;
1008}
1009
1010static LRESULT
1011TREEVIEW_GetVisibleCount (HWND hwnd, WPARAM wParam, LPARAM lParam)
1012
1013{
1014 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
1015
1016 return (LRESULT)infoPtr->uVisibleHeight/infoPtr->uRealItemHeight;
1017}
1018
1019static LRESULT TREEVIEW_SetItem(HWND hwnd,WPARAM wParam,LPARAM lParam,BOOL unicode)
1020{
1021 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
1022 TREEVIEW_ITEM *wineItem;
1023 TVITEMEXW *tvItem;
1024 INT iItem,len;
1025
1026 if (infoPtr->hwndEdit) SetFocus(hwnd);
1027
1028 tvItem = (LPTVITEMEXW)lParam;
1029 iItem = (INT)tvItem->hItem;
1030
1031 wineItem = TREEVIEW_ValidItem (infoPtr, (HTREEITEM)iItem);
1032 if (!wineItem) return FALSE;
1033
1034 if (tvItem->mask & TVIF_CHILDREN)
1035 wineItem->cChildren = tvItem->cChildren;
1036
1037 if (tvItem->mask & TVIF_IMAGE)
1038 wineItem->iImage = tvItem->iImage;
1039
1040 if (tvItem->mask & TVIF_INTEGRAL)
1041 wineItem->iIntegral = tvItem->iIntegral;
1042
1043 if (tvItem->mask & TVIF_PARAM)
1044 wineItem->lParam = tvItem->lParam;
1045
1046 if (tvItem->mask & TVIF_SELECTEDIMAGE)
1047 wineItem->iSelectedImage = tvItem->iSelectedImage;
1048
1049 if (tvItem->mask & TVIF_STATE)
1050 {
1051 wineItem->state &= ~tvItem->stateMask;
1052 wineItem->state |= (tvItem->state & tvItem->stateMask);
1053 wineItem->stateMask |= tvItem->stateMask;
1054 }
1055
1056 if (tvItem->mask & TVIF_TEXT)
1057 {
1058 if (unicode)
1059 {
1060 if (tvItem->pszText != LPSTR_TEXTCALLBACKW)
1061 {
1062 len = lstrlenW(tvItem->pszText)+1;
1063 if (len > wineItem->cchTextMax)
1064 {
1065 wineItem->pszText= COMCTL32_ReAlloc(wineItem->pszText,len*sizeof(WCHAR));
1066 wineItem->cchTextMax = len;
1067 }
1068 lstrcpynW(wineItem->pszText,tvItem->pszText,len);
1069 } else
1070 {
1071 if (wineItem->cchTextMax)
1072 {
1073 COMCTL32_Free (wineItem->pszText);
1074 wineItem->cchTextMax = 0;
1075 }
1076 wineItem->pszText = LPSTR_TEXTCALLBACKW;
1077 }
1078 } else
1079 {
1080 LPTVITEMEXA tvItem;
1081
1082 tvItem = (LPTVITEMEXA)lParam;
1083
1084 if (tvItem->pszText != LPSTR_TEXTCALLBACKA)
1085 {
1086 len = lstrlenA (tvItem->pszText) + 1;
1087 if (len > wineItem->cchTextMax)
1088 {
1089 wineItem->pszText = COMCTL32_ReAlloc(wineItem->pszText,len*sizeof(WCHAR));
1090 wineItem->cchTextMax = len;
1091 }
1092 lstrcpynAtoW(wineItem->pszText,tvItem->pszText,len);
1093 } else
1094 {
1095 if (wineItem->cchTextMax)
1096 {
1097 COMCTL32_Free (wineItem->pszText);
1098 wineItem->cchTextMax = 0;
1099 }
1100 wineItem->pszText = LPSTR_TEXTCALLBACKW;
1101 }
1102 }
1103 }
1104
1105 wineItem->mask |= tvItem->mask;
1106 wineItem->calculated = FALSE;
1107 TREEVIEW_QueueRefresh(hwnd);
1108
1109 return TRUE;
1110}
1111
1112static LRESULT
1113TREEVIEW_GetItemState (HWND hwnd, WPARAM wParam, LPARAM lParam)
1114
1115{
1116 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
1117 TREEVIEW_ITEM *wineItem;
1118
1119 wineItem = TREEVIEW_ValidItem (infoPtr, (HTREEITEM)wParam);
1120 if (!wineItem) return 0;
1121
1122 return (wineItem->state & lParam);
1123}
1124
1125static void TREEVIEW_Refresh(HWND hwnd)
1126{
1127 TREEVIEW_UnqueueRefresh(hwnd,TRUE,FALSE);
1128
1129 InvalidateRect(hwnd,NULL,TRUE);
1130}
1131
1132static void TREEVIEW_RefreshItem(HWND hwnd,TREEVIEW_ITEM *item,BOOL wholeLine)
1133{
1134 if (item && item->visible)
1135 {
1136 RECT rect = item->rect;
1137
1138 if (wholeLine)
1139 {
1140 RECT client;
1141
1142 GetClientRect(hwnd,&client);
1143 rect.left = 0;
1144 rect.right = client.right;
1145 } else
1146 {
1147 DWORD dwStyle = GetWindowLongA(hwnd,GWL_STYLE);
1148
1149 //redraw text and image
1150 if (((item->mask & (TVIF_IMAGE | TVIF_SELECTEDIMAGE)) || (dwStyle & TVS_CHECKBOXES)) && (item->iSelectedImage != item->iImage))
1151 {
1152 rect.left += TREEVIEW_LEFT_MARGIN;
1153 if (item->iLevel != 0) rect.left += (5*item->iLevel);
1154 rect.left += 15;
1155 } else rect = item->text;
1156 }
1157
1158 InvalidateRect(hwnd,&rect,TRUE);
1159 }
1160}
1161
1162//HDC parameter is optional
1163
1164static void TREEVIEW_CalcItem(HWND hwnd,HDC hdc,DWORD dwStyle,TREEVIEW_INFO *infoPtr,TREEVIEW_ITEM *item)
1165{
1166 RECT r;
1167 INT center,xpos;
1168 BOOL ownDC = FALSE;
1169
1170 item->calculated = TRUE;
1171
1172 r = item->rect; /* this item rectangle */
1173 center = (r.top+r.bottom)/2; /* this item vertical center */
1174 xpos = r.left + TREEVIEW_LEFT_MARGIN;/* horizontal starting point */
1175
1176 if (item->iLevel != 0)/* update position only for non root node */
1177 xpos += 5*item->iLevel;
1178
1179 if ((dwStyle & TVS_HASBUTTONS) && (dwStyle & TVS_HASLINES))
1180 {
1181 if (TREEVIEW_HasChildren(hwnd,item))
1182 {
1183 /* Setup expand box coordinate to facilitate the LMBClick handling */
1184 item->expandBox.left = xpos-4;
1185 item->expandBox.top = center-4;
1186 item->expandBox.right = xpos+5;
1187 item->expandBox.bottom = center+5;
1188 } else SetRectEmpty(&item->expandBox);
1189 } else SetRectEmpty(&item->expandBox);
1190
1191 xpos += 13; /* update position */
1192
1193 if ((item->mask & (TVIF_IMAGE | TVIF_SELECTEDIMAGE)) || (dwStyle & TVS_CHECKBOXES))
1194 {
1195 INT imageIndex,cx,cy;
1196 HIMAGELIST *himlp = NULL;
1197
1198 /* State images are displayed to the left of the Normal image
1199 * image number is in state; zero should be `display no image'.
1200 * FIXME: that last sentence looks like it needs some checking.
1201 */
1202 if (infoPtr->himlState)
1203 himlp = &infoPtr->himlState;
1204 imageIndex = item->state >> 12;
1205
1206 if (himlp && imageIndex)
1207 {
1208 if (!hdc)
1209 {
1210 ownDC = TRUE;
1211 hdc = GetDC(hwnd);
1212 }
1213
1214 imageIndex--; /* see FIXME */
1215 ImageList_GetIconSize(*himlp,&cx,&cy);
1216 item->statebitmap.left = xpos-2;
1217 item->statebitmap.right = xpos-2+cx;
1218 item->statebitmap.top = r.top+1;
1219 item->statebitmap.bottom = r.top+1+cy;
1220 xpos += cx;
1221 } else SetRectEmpty(&item->statebitmap);
1222
1223 /* Now, draw the normal image; can be either selected or
1224 * non-selected image.
1225 */
1226
1227 himlp = NULL;
1228 if (infoPtr->himlNormal)
1229 himlp = &infoPtr->himlNormal; /* get the image list */
1230
1231 if (himlp)
1232 {
1233 int ovlIdx = 0;
1234
1235 if(item->stateMask & TVIS_OVERLAYMASK)
1236 ovlIdx = item->state & TVIS_OVERLAYMASK;
1237
1238 if (!hdc)
1239 {
1240 ownDC = TRUE;
1241 hdc = GetDC(hwnd);
1242 }
1243
1244 ImageList_GetIconSize(*himlp,&cx,&cy);
1245 item->bitmap.left = xpos-2;
1246 item->bitmap.right = xpos-2+cx;
1247 item->bitmap.top = r.top+1;
1248 item->bitmap.bottom = r.top+1+cy;
1249 xpos += cx;
1250 } else SetRectEmpty(&item->bitmap);
1251 } else
1252 {
1253 SetRectEmpty(&item->statebitmap);
1254 SetRectEmpty(&item->bitmap);
1255 }
1256
1257 r.left = xpos;
1258 if ((item->mask & TVIF_TEXT) && (item->pszText))
1259 {
1260 UINT uTextJustify = DT_LEFT;
1261 HFONT hOldFont;
1262
1263 r.left += 3;
1264 r.right -= 3;
1265
1266 item->text.left = r.left;
1267 item->text.right = r.right;
1268 item->text.top = r.top;
1269 item->text.bottom= r.bottom;
1270
1271 if (item->pszText == LPSTR_TEXTCALLBACKW)
1272 TREEVIEW_SendDispInfoNotify (hwnd, item, TVN_GETDISPINFO, TVIF_TEXT);
1273
1274 if (!hdc)
1275 {
1276 ownDC = TRUE;
1277 hdc = GetDC(hwnd);
1278 }
1279
1280 if (item->state & TVIS_BOLD)
1281 hOldFont = SelectObject (hdc, infoPtr->hBoldFont);
1282 else
1283 hOldFont = SelectObject (hdc, infoPtr->hFont);
1284
1285 /* Obtain the text coordinate */
1286 DrawTextW (
1287 hdc,
1288 item->pszText,
1289 lstrlenW(item->pszText),
1290 &item->text,
1291 uTextJustify | DT_VCENTER | DT_SINGLELINE | DT_CALCRECT | DT_NOPREFIX);
1292
1293 SelectObject(hdc,hOldFont);
1294
1295 /* We need to reset it to items height */
1296 item->text.top = r.top;
1297 item->text.bottom = r.bottom;
1298 item->text.right += 4; /* This is extra for focus rectangle */
1299
1300 xpos = item->text.right;
1301 } else SetRectEmpty(&item->text);
1302
1303 item->rect.right = xpos;
1304
1305 if (ownDC) ReleaseDC(hwnd,hdc);
1306}
1307
1308//HDC parameter is optional
1309
1310static BOOL TREEVIEW_CalcItems(HWND hwnd,HDC hdc,TREEVIEW_INFO *infoPtr)
1311{
1312 TREEVIEW_ITEM *item;
1313 INT iItem, indent,x,y,height,itemHeight;
1314 TEXTMETRICA tm;
1315 RECT rect,view;
1316 BOOL ownDC = FALSE,changedLeftTop = FALSE;
1317 DWORD dwStyle = GetWindowLongA(hwnd,GWL_STYLE);
1318
1319 GetClientRect(hwnd,&rect);
1320 infoPtr->uVisibleHeight = rect.bottom-rect.top;
1321 infoPtr->uVisibleWidth = rect.right-rect.left;
1322 view = rect;
1323 OffsetRect(&view,infoPtr->cx,infoPtr->cy);
1324
1325 itemHeight = 0;
1326 ImageList_GetIconSize (infoPtr->himlNormal, &x, &itemHeight);
1327 itemHeight = MAX(infoPtr->uItemHeight,itemHeight);
1328
1329 if (!hdc)
1330 {
1331 ownDC = TRUE;
1332 hdc = GetDC(hwnd);
1333 }
1334
1335 GetTextMetricsA (hdc, &tm);
1336 itemHeight = MAX(tm.tmHeight+tm.tmExternalLeading,itemHeight);
1337 if (!(dwStyle & TVS_NONEVENHEIGHT))
1338 if (itemHeight & 0x1) itemHeight += 1; //important for PS_DOT pen!
1339 infoPtr->uRealItemHeight = itemHeight;
1340 infoPtr->uTotalWidth = 0;
1341
1342 iItem = (INT)infoPtr->TopRootItem;
1343 infoPtr->firstVisible = 0;
1344 item = NULL;
1345 indent = x = y = 0;
1346
1347 while (iItem)
1348 {
1349 item = &infoPtr->items[iItem];
1350 item->iLevel = indent;
1351
1352 height = itemHeight * item->iIntegral +1;
1353
1354 //calculate size and fill rects
1355 if ((infoPtr->uInternalStatus & TV_CALCALL) || !item->calculated)
1356 {
1357 item->rect.top = y-infoPtr->cy;
1358 item->rect.bottom = item->rect.top+height;
1359 item->rect.left = x-infoPtr->cx;
1360 item->rect.right = rect.right; //dummy
1361 TREEVIEW_CalcItem(hwnd,hdc,dwStyle,infoPtr,item);
1362 } else
1363 {
1364 INT xOffset,yOffset;
1365
1366 xOffset = (x-infoPtr->cx)-item->rect.left;
1367 yOffset = (y-infoPtr->cy)-item->rect.top;
1368 OffsetRect(&item->rect,xOffset,yOffset);
1369 OffsetRect(&item->text,xOffset,yOffset);
1370 OffsetRect(&item->expandBox,xOffset,yOffset);
1371 OffsetRect(&item->bitmap,xOffset,yOffset);
1372 OffsetRect(&item->statebitmap,xOffset,yOffset);
1373 }
1374 infoPtr->uTotalWidth = MAX(infoPtr->uTotalWidth,item->rect.right+infoPtr->cx);
1375
1376 if (((y >= view.top) && (y <= view.bottom)) || ((y+height >= view.top) && (y+height <= view.bottom)))
1377 {
1378 item->visible = TRUE;
1379 if (!infoPtr->firstVisible)
1380 infoPtr->firstVisible = item->hItem;
1381 } else item->visible = FALSE;
1382
1383 /* look up next item */
1384
1385 if ((item->firstChild) && (item->state & TVIS_EXPANDED))
1386 {
1387 iItem = (INT)item->firstChild;
1388 indent++;
1389 x += infoPtr->uIndent;
1390 } else
1391 {
1392 iItem = (INT)item->sibling;
1393 while ((!iItem) && (indent > 0))
1394 {
1395 indent--;
1396 x -= infoPtr->uIndent;
1397 item = &infoPtr->items[(INT)item->parent];
1398 iItem = (INT)item->sibling;
1399 }
1400 }
1401 y += height;
1402 } /* while */
1403
1404 if (ownDC) ReleaseDC(hwnd,hdc);
1405
1406 infoPtr->uInternalStatus &= ~TV_CALCALL;
1407 infoPtr->uTotalHeight = y;
1408
1409 if (item && item->visible)
1410 {
1411 INT xDiff = 0,yDiff = 0;
1412
1413 //check cx and cy
1414#if 0 //CB: next step
1415 if ((infoPtr->cy > 0) && (item->rect.bottom < infoPtr->uVisibleHeight))
1416 {
1417 INT oldCY = infoPtr->cy;
1418
1419 infoPtr->cy = infoPtr->uTotalHeight-infoPtr->uVisibleHeight;
1420 if (infoPtr->cy < 0) infoPtr->cy = 0;
1421 yDiff = infoPtr->cy-oldCY;
1422 }
1423
1424 changedLeftTop = xDiff || yDiff;
1425 if (changedLeftTop)
1426 {
1427 iItem = (INT)infoPtr->TopRootItem;
1428 item = NULL;
1429 indent = 0;
1430MessageBeep(MB_OK);
1431 while (iItem)
1432 {
1433 item = &infoPtr->items[iItem];
1434 OffsetRect(&item->rect,xDiff,yDiff);
1435 OffsetRect(&item->text,xDiff,yDiff);
1436 OffsetRect(&item->expandBox,xDiff,yDiff);
1437 OffsetRect(&item->bitmap,xDiff,yDiff);
1438 OffsetRect(&item->statebitmap,xDiff,yDiff);
1439
1440
1441 if ((item->firstChild) && (item->state & TVIS_EXPANDED))
1442 {
1443 iItem = (INT)item->firstChild;
1444 indent++;
1445 } else
1446 {
1447 iItem = (INT)item->sibling;
1448 while ((!iItem) && (indent > 0))
1449 {
1450 indent--;
1451 item = &infoPtr->items[(INT)item->parent];
1452 iItem = (INT)item->sibling;
1453 }
1454 }
1455 } /* while */
1456 }
1457#endif
1458 }
1459
1460 if (!(dwStyle & TVS_NOSCROLL))
1461 {
1462 if (infoPtr->uTotalHeight > infoPtr->uVisibleHeight)
1463 {
1464 SCROLLINFO info;
1465
1466 info.cbSize = sizeof(info);
1467 info.nMin = 0;
1468 info.nMax = infoPtr->uTotalHeight-1;
1469 info.nPos = infoPtr->cy;
1470 info.nPage = MAX(infoPtr->uVisibleHeight,1);
1471 info.fMask = SIF_RANGE | SIF_POS | SIF_PAGE;
1472 infoPtr->uInternalStatus |= TV_VSCROLL;
1473 SetScrollInfo(hwnd,SB_VERT,&info,TRUE);
1474 } else
1475 {
1476 if (infoPtr->uInternalStatus & TV_VSCROLL)
1477 ShowScrollBar(hwnd,SB_VERT,FALSE);
1478 infoPtr->uInternalStatus &= ~TV_VSCROLL;
1479 }
1480 if (!(dwStyle & TVS_NOHSCROLL) && (infoPtr->uTotalWidth > infoPtr->uVisibleWidth))
1481 {
1482 SCROLLINFO info;
1483
1484 info.cbSize = sizeof(info);
1485 info.nMin = 0;
1486 info.nMax = infoPtr->uTotalWidth-1;
1487 info.nPos = infoPtr->cx;
1488 info.nPage = MAX(infoPtr->uVisibleWidth,1);
1489 info.fMask = SIF_RANGE | SIF_POS | SIF_PAGE;
1490 infoPtr->uInternalStatus |= TV_HSCROLL;
1491 SetScrollInfo(hwnd,SB_HORZ,&info,TRUE);
1492 } else
1493 {
1494 if (infoPtr->uInternalStatus & TV_HSCROLL)
1495 ShowScrollBar(hwnd,SB_HORZ,FALSE);
1496 infoPtr->uInternalStatus &= ~TV_HSCROLL;
1497 }
1498 } else
1499 {
1500 if (infoPtr->uInternalStatus & (TV_VSCROLL | TV_HSCROLL))
1501 ShowScrollBar(hwnd,SB_BOTH,FALSE);
1502 infoPtr->uInternalStatus &= ~(TV_VSCROLL | TV_HSCROLL);
1503 }
1504
1505 return changedLeftTop;
1506}
1507
1508static void
1509TREEVIEW_Draw(HWND hwnd,HDC hdc,RECT *updateRect)
1510{
1511 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
1512 DWORD dwStyle = GetWindowLongA(hwnd,GWL_STYLE);
1513 HBRUSH hbrBk;
1514 RECT rect;
1515 TREEVIEW_ITEM *item;
1516 INT iItem,indent;
1517 BOOL visFound = FALSE;
1518 HPEN hNewPen,hOldPen;
1519
1520 TREEVIEW_UnqueueRefresh(hwnd,TRUE,FALSE);
1521
1522 GetClientRect (hwnd, &rect);
1523 if ((rect.left == rect.right) || (rect.top == rect.bottom)) return;
1524
1525 infoPtr->cdmode = TREEVIEW_SendCustomDrawNotify(hwnd, CDDS_PREPAINT, hdc, rect);
1526
1527 if (infoPtr->cdmode == CDRF_SKIPDEFAULT) return;
1528
1529 //draw items
1530
1531 hNewPen = CreatePen(PS_DOT,0,infoPtr->clrLine);
1532
1533 iItem = (INT)infoPtr->TopRootItem;
1534 indent = 0;
1535
1536 while (iItem)
1537 {
1538 item = &infoPtr->items[iItem];
1539 if (item->visible)
1540 {
1541 if (updateRect && IntersectRect(NULL,&item->rect,updateRect))
1542 {
1543 TREEVIEW_DrawItem(hwnd,hdc,item,dwStyle,infoPtr);
1544 visFound = TRUE;
1545 }
1546 } else if (visFound) break;
1547 if (!visFound && (dwStyle & TVS_HASLINES) && (dwStyle & TVS_LINESATROOT))
1548 {
1549 //draw vertical connections
1550 hOldPen = SelectObject(hdc,hNewPen);
1551 TREEVIEW_DrawVLines(hdc,infoPtr,item);
1552 SelectObject(hdc,hOldPen);
1553 }
1554 if (item->firstChild && (item->state & TVIS_EXPANDED))
1555 {
1556 iItem = (INT)item->firstChild;
1557 indent++;
1558 } else
1559 {
1560 iItem = (INT)item->sibling;
1561 while (!iItem && (indent > 0))
1562 {
1563 item = &infoPtr->items[(INT)item->parent];
1564 iItem = (INT)item->sibling;
1565 indent--;
1566 }
1567 }
1568 }
1569
1570 DeleteObject(hNewPen);
1571
1572 if (infoPtr->cdmode & CDRF_NOTIFYPOSTPAINT)
1573 infoPtr->cdmode = TREEVIEW_SendCustomDrawNotify(hwnd, CDDS_POSTPAINT, hdc, rect);
1574}
1575
1576static LRESULT TREEVIEW_SetRedraw(HWND hwnd,WPARAM wParam,LPARAM lParam)
1577{
1578 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
1579
1580 if (wParam)
1581 {
1582 if (!(infoPtr->uInternalStatus & TV_NOREDRAW)) return 0;
1583 infoPtr->uInternalStatus &= ~TV_NOREDRAW;
1584 TREEVIEW_CalcItems(hwnd,0,infoPtr);
1585 TREEVIEW_Refresh(hwnd);
1586 } else
1587 {
1588 infoPtr->uInternalStatus |= TV_NOREDRAW;
1589 }
1590
1591 return 0;
1592}
1593
1594
1595static LRESULT
1596TREEVIEW_HandleTimer (HWND hwnd, WPARAM wParam, LPARAM lParam)
1597{
1598 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
1599
1600// TRACE (treeview, " %d\n",wParam);
1601
1602 switch (wParam) {
1603 case TV_REFRESH_TIMER:
1604 KillTimer (hwnd, TV_REFRESH_TIMER);
1605 infoPtr->Timer &= ~TV_REFRESH_TIMER_SET;
1606 TREEVIEW_CalcItems(hwnd,0,infoPtr);
1607 TREEVIEW_Refresh(hwnd);
1608 return 0;
1609
1610 case TV_EDIT_TIMER:
1611 KillTimer (hwnd, TV_EDIT_TIMER);
1612 infoPtr->Timer &= ~TV_EDIT_TIMER_SET;
1613 if (infoPtr->editItem)
1614 TREEVIEW_EditLabel(hwnd,infoPtr->editItem,TRUE);
1615 return 0;
1616 default:
1617// ERR (treeview,"got unknown timer\n");
1618 break;
1619 }
1620
1621 return 1;
1622}
1623
1624static void TREEVIEW_QueueRefresh(HWND hwnd)
1625
1626{
1627 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
1628
1629 if (infoPtr->Timer & TV_REFRESH_TIMER_SET)
1630 KillTimer (hwnd, TV_REFRESH_TIMER);
1631
1632 if (infoPtr->uInternalStatus & TV_NOREDRAW)
1633 {
1634 infoPtr->Timer &= ~TV_REFRESH_TIMER_SET;
1635
1636 return;
1637 }
1638
1639 SetTimer (hwnd, TV_REFRESH_TIMER, TV_REFRESH_DELAY, 0);
1640 infoPtr->Timer |= TV_REFRESH_TIMER_SET;
1641}
1642
1643static BOOL TREEVIEW_UnqueueRefresh(HWND hwnd,BOOL calc,BOOL refresh)
1644{
1645 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
1646
1647 if (infoPtr->Timer & TV_REFRESH_TIMER_SET)
1648 {
1649 KillTimer (hwnd, TV_REFRESH_TIMER);
1650 infoPtr->Timer &= ~TV_REFRESH_TIMER_SET;
1651 if (calc) TREEVIEW_CalcItems(hwnd,0,infoPtr);
1652 if (refresh) TREEVIEW_Refresh(hwnd);
1653
1654 return TRUE;
1655 }
1656
1657 return FALSE;
1658}
1659
1660static LRESULT
1661TREEVIEW_GetItem(HWND hwnd,WPARAM wParam,LPARAM lParam,BOOL unicode)
1662{
1663 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
1664 LPTVITEMEXW tvItem;
1665 TREEVIEW_ITEM *wineItem;
1666 INT iItem;
1667
1668 tvItem = (LPTVITEMEXW)lParam;
1669 iItem = (INT)tvItem->hItem;
1670
1671 wineItem = TREEVIEW_ValidItem (infoPtr, (HTREEITEM)iItem);
1672 if (!wineItem) return FALSE;
1673
1674 if (tvItem->mask & TVIF_CHILDREN)
1675 tvItem->cChildren = TREEVIEW_HasChildren(hwnd, wineItem);
1676
1677 if (tvItem->mask & TVIF_HANDLE)
1678 tvItem->hItem=wineItem->hItem;
1679
1680 if (tvItem->mask & TVIF_IMAGE)
1681 tvItem->iImage=wineItem->iImage;
1682
1683 if (tvItem->mask & TVIF_INTEGRAL)
1684 tvItem->iIntegral=wineItem->iIntegral;
1685
1686 // undocumented: windows ignores TVIF_PARAM and
1687 // always sets lParam
1688 tvItem->lParam = wineItem->lParam;
1689
1690 if (tvItem->mask & TVIF_SELECTEDIMAGE)
1691 tvItem->iSelectedImage=wineItem->iSelectedImage;
1692
1693 if (tvItem->mask & TVIF_STATE)
1694 tvItem->state=wineItem->state & tvItem->stateMask;
1695
1696 if (tvItem->mask & TVIF_TEXT)
1697 {
1698 if (wineItem->pszText == LPSTR_TEXTCALLBACKW)
1699 TREEVIEW_SendDispInfoNotify(hwnd,wineItem,TVN_GETDISPINFO,TVIF_TEXT);
1700 if (unicode)
1701 {
1702 if (wineItem->pszText == LPSTR_TEXTCALLBACKW)
1703 tvItem->pszText = LPSTR_TEXTCALLBACKW;
1704 else if (wineItem->pszText)
1705 lstrcpynW(tvItem->pszText,wineItem->pszText,tvItem->cchTextMax);
1706 } else
1707 {
1708 LPTVITEMEXA tvItem = (LPTVITEMEXA)lParam;
1709
1710 if (wineItem->pszText == LPSTR_TEXTCALLBACKW)
1711 tvItem->pszText = LPSTR_TEXTCALLBACKA;
1712 else if (wineItem->pszText)
1713 lstrcpynWtoA(tvItem->pszText, wineItem->pszText,tvItem->cchTextMax);
1714 }
1715 }
1716
1717 return TRUE;
1718}
1719
1720
1721
1722/* FIXME: check implementation of TVGN_NEXT/TVGN_NEXTVISIBLE */
1723
1724static LRESULT
1725TREEVIEW_GetNextItem (HWND hwnd, WPARAM wParam, LPARAM lParam)
1726
1727{
1728 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
1729 TREEVIEW_ITEM *wineItem, *returnItem;
1730 INT iItem, retval, flag;
1731
1732 flag = (INT) wParam;
1733 iItem = (INT) lParam;
1734 retval = 0;
1735 switch (flag)
1736 {
1737 case TVGN_CHILD: /* Special case: child of 0 is root */
1738 if (iItem) break;
1739
1740 case TVGN_ROOT:
1741 retval = (INT)infoPtr->TopRootItem;
1742 break;
1743
1744 case TVGN_CARET:
1745 retval = (INT)infoPtr->selectedItem;
1746 break;
1747
1748 case TVGN_FIRSTVISIBLE:
1749 TREEVIEW_UnqueueRefresh(hwnd,TRUE,TRUE);
1750 retval = (INT)infoPtr->firstVisible;
1751 break;
1752
1753 case TVGN_DROPHILITE:
1754 retval = (INT)infoPtr->dropItem;
1755 break;
1756 }
1757
1758 if (retval) return retval;
1759
1760 wineItem = TREEVIEW_ValidItem (infoPtr, (HTREEITEM)iItem);
1761 returnItem = NULL;
1762 if (!wineItem) return FALSE;
1763
1764 switch (flag)
1765 {
1766 case TVGN_NEXT:
1767 retval = (INT)wineItem->sibling;
1768 break;
1769
1770 case TVGN_PREVIOUS:
1771 retval = (INT)wineItem->upsibling;
1772 break;
1773
1774 case TVGN_PARENT:
1775 retval = (INT)wineItem->parent;
1776 break;
1777
1778 case TVGN_CHILD:
1779 retval = (INT)wineItem->firstChild;
1780 break;
1781
1782 case TVGN_LASTVISIBLE:
1783 returnItem = TREEVIEW_GetLastListItem (hwnd,infoPtr,wineItem);
1784 break;
1785
1786 case TVGN_NEXTVISIBLE:
1787 returnItem = TREEVIEW_GetNextListItem (hwnd,infoPtr,wineItem);
1788 break;
1789
1790 case TVGN_PREVIOUSVISIBLE:
1791 returnItem = TREEVIEW_GetPrevListItem (hwnd,infoPtr, wineItem);
1792 break;
1793
1794 default:
1795 break;
1796 }
1797
1798 if (returnItem) return (INT)returnItem->hItem;
1799
1800 return retval;
1801}
1802
1803
1804static LRESULT
1805TREEVIEW_GetCount (HWND hwnd, WPARAM wParam, LPARAM lParam)
1806{
1807 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
1808
1809 return (LRESULT) infoPtr->uNumItems;
1810}
1811
1812/***************************************************************************
1813 * This method does the chaining of the insertion of a treeview item
1814 * before an item.
1815 * If parent is NULL, we're inserting at the root of the list.
1816 */
1817static void TREEVIEW_InsertBefore(
1818 TREEVIEW_INFO *infoPtr,
1819 TREEVIEW_ITEM *newItem,
1820 TREEVIEW_ITEM *sibling,
1821 TREEVIEW_ITEM *parent)
1822{
1823 HTREEITEM siblingHandle = 0;
1824 HTREEITEM upSiblingHandle = 0;
1825 TREEVIEW_ITEM *upSibling = NULL;
1826
1827// if (newItem == NULL)
1828// ERR(treeview, "NULL newItem, impossible condition\n");
1829
1830// if (parent == NULL)
1831// ERR(treeview, "NULL parent, impossible condition\n");
1832
1833 if (sibling != NULL) /* Insert before this sibling for this parent */
1834 {
1835 /* Store the new item sibling up sibling and sibling tem handle */
1836 siblingHandle = sibling->hItem;
1837 upSiblingHandle = sibling->upsibling;
1838 /* As well as a pointer to the upsibling sibling object */
1839 if ( (INT)sibling->upsibling != 0 )
1840 upSibling = &infoPtr->items[(INT)sibling->upsibling];
1841
1842 /* Adjust the sibling pointer */
1843 sibling->upsibling = newItem->hItem;
1844
1845 /* Adjust the new item pointers */
1846 newItem->upsibling = upSiblingHandle;
1847 newItem->sibling = siblingHandle;
1848
1849 /* Adjust the up sibling pointer */
1850 if ( upSibling != NULL )
1851 upSibling->sibling = newItem->hItem;
1852 else
1853 if (parent)
1854 /* this item is the first child of this parent, adjust parent pointers */
1855 parent->firstChild = newItem->hItem;
1856 else infoPtr->TopRootItem = newItem->hItem;
1857 }
1858 else /* Insert as first child of this parent */
1859 if (parent)
1860 parent->firstChild = newItem->hItem;
1861}
1862
1863/***************************************************************************
1864 * This method does the chaining of the insertion of a treeview item
1865 * If parent is NULL, we're inserting at the root of the list.
1866 * after an item.
1867 */
1868static void TREEVIEW_InsertAfter(
1869 TREEVIEW_INFO *infoPtr,
1870 TREEVIEW_ITEM *newItem,
1871 TREEVIEW_ITEM *upSibling,
1872 TREEVIEW_ITEM *parent)
1873{
1874 HTREEITEM upSiblingHandle = 0;
1875 HTREEITEM siblingHandle = 0;
1876 TREEVIEW_ITEM *sibling = NULL;
1877
1878// if (newItem == NULL)
1879// ERR(treeview, "NULL newItem, impossible condition\n");
1880
1881// if (parent == NULL)
1882// ERR(treeview, "NULL parent, impossible condition\n");
1883
1884 if (upSibling != NULL) /* Insert after this upsibling for this parent */
1885 {
1886 /* Store the new item up sibling and sibling item handle */
1887 upSiblingHandle = upSibling->hItem;
1888 siblingHandle = upSibling->sibling;
1889 /* As well as a pointer to the upsibling sibling object */
1890 if ( (INT)upSibling->sibling != 0 )
1891 sibling = &infoPtr->items[(INT)upSibling->sibling];
1892
1893 /* Adjust the up sibling pointer */
1894 upSibling->sibling = newItem->hItem;
1895
1896 /* Adjust the new item pointers */
1897 newItem->upsibling = upSiblingHandle;
1898 newItem->sibling = siblingHandle;
1899
1900 /* Adjust the sibling pointer */
1901 if ( sibling != NULL )
1902 sibling->upsibling = newItem->hItem;
1903 /*
1904 else
1905 newItem is the last of the level, nothing else to do
1906 */
1907 }
1908 else /* Insert as first child of this parent */
1909 if (parent)
1910 parent->firstChild = newItem->hItem;
1911}
1912
1913/***************************************************************************
1914 * Forward the DPA local callback to the treeview owner callback
1915 */
1916static INT WINAPI TREEVIEW_CallBackCompare(
1917 LPVOID first,
1918 LPVOID second,
1919 LPARAM tvInfoPtr)
1920{
1921 /* Forward the call to the client define callback */
1922 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr((HWND)tvInfoPtr);
1923 return (infoPtr->pCallBackSort->lpfnCompare)(
1924 ((TREEVIEW_ITEM*)first)->lParam,
1925 ((TREEVIEW_ITEM*)second)->lParam,
1926 infoPtr->pCallBackSort->lParam);
1927}
1928
1929/***************************************************************************
1930 * Treeview native sort routine: sort on item text.
1931 */
1932static INT WINAPI TREEVIEW_SortOnName (
1933 LPVOID first,
1934 LPVOID second,
1935 LPARAM tvInfoPtr)
1936{
1937 HWND hwnd=(HWND) tvInfoPtr;
1938 WCHAR *txt1, *txt2;
1939 TREEVIEW_ITEM *item;
1940
1941
1942 item=(TREEVIEW_ITEM *) first;
1943 if (item->pszText == LPSTR_TEXTCALLBACKW) {
1944 TREEVIEW_SendDispInfoNotify (hwnd, item, TVN_GETDISPINFO, TVIF_TEXT);
1945 }
1946 txt1=item->pszText;
1947
1948 item=(TREEVIEW_ITEM *) second;
1949 if (item->pszText == LPSTR_TEXTCALLBACKW) {
1950 TREEVIEW_SendDispInfoNotify (hwnd, item, TVN_GETDISPINFO, TVIF_TEXT);
1951 }
1952 txt2=item->pszText;
1953
1954 return -lstrcmpW(txt1,txt2);
1955}
1956
1957/***************************************************************************
1958 * Setup the treeview structure with regards of the sort method
1959 * and sort the children of the TV item specified in lParam
1960 * fRecurse: currently unused. Should be zero.
1961 * parent: if pSort!=NULL, should equal pSort->hParent.
1962 * otherwise, item which child items are to be sorted.
1963 * pSort: sort method info. if NULL, sort on item text.
1964 * if non-NULL, sort on item's lParam content, and let the
1965 * application decide what that means. See also TVM_SORTCHILDRENCB.
1966 */
1967
1968static LRESULT WINAPI TREEVIEW_Sort (
1969 HWND hwnd,
1970 BOOL fRecurse,
1971 HTREEITEM parent,
1972 LPTVSORTCB pSort
1973 )
1974{
1975 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
1976 TREEVIEW_ITEM *sortMe = NULL; /* Node for which we sort the children */
1977 INT cChildren;
1978 HTREEITEM hti;
1979
1980 /* Obtain the TVSORTBC struct */
1981 infoPtr->pCallBackSort = pSort;
1982
1983 /* undocumented feature: TVI_ROOT means `sort the whole tree' */
1984
1985 if (parent==TVI_ROOT)
1986 parent=infoPtr->TopRootItem;
1987
1988 /* Check for a valid handle to the parent item */
1989 if (!TREEVIEW_ValidItem(infoPtr, parent))
1990 {
1991 //ERR ("invalid item hParent=%x\n", (INT)parent);
1992 return FALSE;
1993 }
1994
1995 /* Obtain the parent node to sort */
1996 sortMe = &infoPtr->items[ (INT)parent ];
1997
1998 cChildren = 0;
1999 for(hti = sortMe->firstChild; hti; hti = infoPtr->items[(INT)hti].sibling)
2000 cChildren++;
2001
2002 /* Make sure there is something to sort */
2003 if ( cChildren > 1 )
2004 {
2005 /* pointer organization */
2006 HDPA sortList = DPA_Create(cChildren);
2007 HTREEITEM itemHandle = sortMe->firstChild;
2008 TREEVIEW_ITEM *itemPtr = & infoPtr->items[ (INT)itemHandle ];
2009
2010 /* TREEVIEW_ITEM rechaining */
2011 INT count = 0;
2012 VOID *item = 0;
2013 VOID *nextItem = 0;
2014 VOID *prevItem = 0;
2015
2016 /* Build the list of item to sort */
2017 do
2018 {
2019 DPA_InsertPtr(
2020 sortList, /* the list */
2021 cChildren+1, /* force the insertion to be an append */
2022 itemPtr); /* the ptr to store */
2023
2024 /* Get the next sibling */
2025 itemHandle = itemPtr->sibling;
2026 itemPtr = & infoPtr->items[ (INT)itemHandle ];
2027 } while ( itemHandle != NULL );
2028
2029 /* let DPA perform the sort activity */
2030 if (pSort)
2031 DPA_Sort(
2032 sortList, /* what */
2033 TREEVIEW_CallBackCompare, /* how */
2034 hwnd); /* owner */
2035 else
2036 DPA_Sort (
2037 sortList, /* what */
2038 TREEVIEW_SortOnName, /* how */
2039 hwnd); /* owner */
2040
2041 /*
2042 * Reorganized TREEVIEW_ITEM structures.
2043 * Note that we know we have at least two elements.
2044 */
2045
2046 /* Get the first item and get ready to start... */
2047 item = DPA_GetPtr(sortList, count++);
2048 while ( (nextItem = DPA_GetPtr(sortList, count++)) != NULL )
2049 {
2050 /* link the two current item toghether */
2051 ((TREEVIEW_ITEM*)item)->sibling = ((TREEVIEW_ITEM*)nextItem)->hItem;
2052 ((TREEVIEW_ITEM*)nextItem)->upsibling = ((TREEVIEW_ITEM*)item)->hItem;
2053
2054 if (prevItem == NULL) /* this is the first item, update the parent */
2055 {
2056 sortMe->firstChild = ((TREEVIEW_ITEM*)item)->hItem;
2057 ((TREEVIEW_ITEM*)item)->upsibling = NULL;
2058 }
2059 else /* fix the back chaining */
2060 {
2061 ((TREEVIEW_ITEM*)item)->upsibling = ((TREEVIEW_ITEM*)prevItem)->hItem;
2062 }
2063
2064 /* get ready for the next one */
2065 prevItem = item;
2066 item = nextItem;
2067 }
2068
2069 /* the last item is pointed to by item and never has a sibling */
2070 ((TREEVIEW_ITEM*)item)->sibling = NULL;
2071
2072 DPA_Destroy(sortList);
2073
2074 return TRUE;
2075 }
2076 return FALSE;
2077}
2078
2079/***************************************************************************
2080 * Setup the treeview structure with regards of the sort method
2081 * and sort the children of the TV item specified in lParam
2082 */
2083static LRESULT WINAPI TREEVIEW_SortChildrenCB(
2084 HWND hwnd,
2085 WPARAM wParam,
2086 LPARAM lParam
2087 )
2088{
2089 LPTVSORTCB pSort=(LPTVSORTCB) lParam;
2090
2091 return TREEVIEW_Sort (hwnd, wParam, pSort->hParent, pSort);
2092}
2093
2094
2095/***************************************************************************
2096 * Sort the children of the TV item specified in lParam.
2097 */
2098static LRESULT WINAPI TREEVIEW_SortChildren (
2099 HWND hwnd,
2100 WPARAM wParam,
2101 LPARAM lParam)
2102{
2103 return TREEVIEW_Sort (hwnd, (BOOL) wParam, (HTREEITEM) lParam, NULL);
2104}
2105
2106
2107int ffs(int mask)
2108{
2109 int bit;
2110
2111 if (mask == 0)
2112 return(0);
2113 for (bit = 1; !(mask & 1); bit++)
2114 mask >>= 1;
2115 return(bit);
2116}
2117
2118/* the method used below isn't the most memory-friendly, but it avoids
2119 a lot of memory reallocations */
2120
2121/* BTW: we waste handle 0; 0 is not an allowed handle. */
2122
2123static LRESULT
2124TREEVIEW_InsertItem(HWND hwnd,WPARAM wParam,LPARAM lParam,BOOL unicode)
2125
2126{
2127 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
2128 TVINSERTSTRUCTW *ptdi;
2129 TVITEMEXW *tvItem;
2130 TREEVIEW_ITEM *wineItem, *parentItem, *prevsib, *sibItem;
2131 INT iItem,i,len;
2132
2133 if (infoPtr->hwndEdit) SetFocus(hwnd);
2134
2135 /* Item to insert */
2136 ptdi = (LPTVINSERTSTRUCTW)lParam;
2137
2138 /* check if memory is available */
2139
2140 if (infoPtr->uNumPtrsAlloced==0) {
2141 infoPtr->items = COMCTL32_Alloc (TVITEM_ALLOC*sizeof (TREEVIEW_ITEM));
2142 infoPtr->freeList= COMCTL32_Alloc (((TVITEM_ALLOC>>5)) * sizeof (INT));
2143 infoPtr->uNumPtrsAlloced=TVITEM_ALLOC;
2144 infoPtr->TopRootItem=(HTREEITEM)1;
2145 }
2146
2147 /*
2148 * Reallocate contiguous space for items
2149 */
2150 if (infoPtr->uNumItems == (infoPtr->uNumPtrsAlloced-1) ) {
2151 TREEVIEW_ITEM *oldItems = infoPtr->items;
2152 INT *oldfreeList = infoPtr->freeList;
2153
2154 infoPtr->uNumPtrsAlloced*=2;
2155 infoPtr->items = COMCTL32_Alloc (infoPtr->uNumPtrsAlloced*sizeof (TREEVIEW_ITEM));
2156 infoPtr->freeList= COMCTL32_Alloc (((infoPtr->uNumPtrsAlloced>>5))*sizeof (INT));
2157
2158 memcpy (&infoPtr->items[0], &oldItems[0],
2159 infoPtr->uNumPtrsAlloced/2 * sizeof(TREEVIEW_ITEM));
2160 memcpy (&infoPtr->freeList[0], &oldfreeList[0],
2161 (infoPtr->uNumPtrsAlloced>>6) * sizeof(INT));
2162
2163 COMCTL32_Free (oldItems);
2164 COMCTL32_Free (oldfreeList);
2165 }
2166
2167 /*
2168 * Reset infoPtr structure with new stat according to current TV picture
2169 */
2170 iItem=0;
2171 infoPtr->uNumItems++;
2172 if ((INT)infoPtr->uMaxHandle==(infoPtr->uNumItems-1)) {
2173 iItem=infoPtr->uNumItems;
2174 infoPtr->uMaxHandle = (HTREEITEM)((INT)infoPtr->uMaxHandle + 1);
2175 } else { /* check freelist */
2176 for (i=0; i<infoPtr->uNumPtrsAlloced>>5; i++) {
2177 if (infoPtr->freeList[i]) {
2178 for(iItem = 0; iItem < 32; iItem++)
2179 {
2180 if(tv_test_bit(iItem, &infoPtr->freeList[i]))
2181 break;
2182 }
2183 tv_clear_bit(iItem,&infoPtr->freeList[i]);
2184 iItem+=i<<5;
2185 break;
2186 }
2187 }
2188 }
2189
2190 /*
2191 * Find the parent item of the new item
2192 */
2193 tvItem= & ptdi->DUMMYUNIONNAME.itemex;
2194 wineItem=& infoPtr->items[iItem];
2195
2196 if ((ptdi->hParent==TVI_ROOT) || (ptdi->hParent==0)) {
2197 parentItem = NULL;
2198 wineItem->parent = 0;
2199 sibItem = &infoPtr->items [(INT)infoPtr->TopRootItem];
2200 }
2201 else {
2202 parentItem = &infoPtr->items[(INT)ptdi->hParent];
2203
2204 /* Do the insertion here it if it's the only item of this parent */
2205 if (!parentItem->firstChild)
2206 parentItem->firstChild=(HTREEITEM)iItem;
2207
2208 wineItem->parent = ptdi->hParent;
2209 sibItem = &infoPtr->items [(INT)parentItem->firstChild];
2210 }
2211
2212
2213 /* NOTE: I am moving some setup of the wineItem object that was initialy
2214 * done at the end of the function since some of the values are
2215 * required by the Callback sorting
2216 */
2217
2218 if (tvItem->mask & TVIF_TEXT)
2219 {
2220 /*
2221 * Setup the item text stuff here since it's required by the Sort method
2222 * when the insertion are ordered
2223 */
2224 if (unicode)
2225 {
2226 if (tvItem->pszText != LPSTR_TEXTCALLBACKW)
2227 {
2228 //TRACE (treeview,"(%p,%s)\n", &tvItem->pszText, tvItem->pszText);
2229 len = lstrlenW(tvItem->pszText)+1;
2230 wineItem->pszText = COMCTL32_Alloc(len*sizeof(WCHAR));
2231 lstrcpyW (wineItem->pszText, tvItem->pszText);
2232 wineItem->cchTextMax = len;
2233 } else
2234 {
2235 //TRACE (treeview,"LPSTR_TEXTCALLBACK\n");
2236 wineItem->pszText = LPSTR_TEXTCALLBACKW;
2237 wineItem->cchTextMax = 0;
2238 }
2239 } else
2240 {
2241 TVITEMEXA *tvItemA = (LPTVITEMEXA)tvItem;
2242
2243 if (tvItemA->pszText != LPSTR_TEXTCALLBACKA)
2244 {
2245 //TRACE (treeview,"(%p,%s)\n", &tvItem->pszText, tvItem->pszText);
2246 len = lstrlenA(tvItemA->pszText)+1;
2247 wineItem->pszText= COMCTL32_Alloc (len*sizeof(WCHAR));
2248 lstrcpyAtoW (wineItem->pszText, tvItemA->pszText);
2249 wineItem->cchTextMax = len;
2250 } else
2251 {
2252 //TRACE (treeview,"LPSTR_TEXTCALLBACK\n");
2253 wineItem->pszText = LPSTR_TEXTCALLBACKW;
2254 wineItem->cchTextMax = 0;
2255 }
2256 }
2257 }
2258
2259 if (tvItem->mask & TVIF_PARAM)
2260 wineItem->lParam=tvItem->lParam;
2261
2262
2263 wineItem->upsibling=0; /* needed in case we're the first item in a list */
2264 wineItem->sibling=0;
2265 wineItem->firstChild=0;
2266 wineItem->hItem=(HTREEITEM)iItem;
2267
2268 if (sibItem != wineItem) {
2269 prevsib=NULL;
2270
2271 switch ((DWORD) ptdi->hInsertAfter) {
2272 case (DWORD) TVI_FIRST:
2273 if (wineItem->parent) {
2274 wineItem->sibling=parentItem->firstChild;
2275 parentItem->firstChild=(HTREEITEM)iItem;
2276 } else {
2277 wineItem->sibling=infoPtr->TopRootItem;
2278 infoPtr->TopRootItem=(HTREEITEM)iItem;
2279 }
2280 sibItem->upsibling=(HTREEITEM)iItem;
2281 break;
2282
2283 case (DWORD) TVI_SORT:
2284 {
2285 TREEVIEW_ITEM *aChild;
2286
2287 TREEVIEW_ITEM *previousChild = NULL;
2288 BOOL bItemInserted = FALSE;
2289
2290 if (parentItem)
2291 aChild = &infoPtr->items[(INT)parentItem->firstChild];
2292 else
2293 aChild = &infoPtr->items[(INT)infoPtr->TopRootItem];
2294
2295 /* lookup the text if using LPSTR_TEXTCALLBACKs */
2296 if (wineItem->pszText==LPSTR_TEXTCALLBACKW) {
2297 TREEVIEW_SendDispInfoNotify (hwnd, wineItem, TVN_GETDISPINFO, TVIF_TEXT);
2298 }
2299
2300 /* Iterate the parent children to see where we fit in */
2301 while ( aChild != NULL )
2302 {
2303 INT comp;
2304
2305 /* lookup the text if using LPSTR_TEXTCALLBACKs */
2306 if (aChild->pszText==LPSTR_TEXTCALLBACKW) {
2307 TREEVIEW_SendDispInfoNotify (hwnd, aChild, TVN_GETDISPINFO, TVIF_TEXT);
2308 }
2309
2310 comp = lstrcmpW(wineItem->pszText, aChild->pszText);
2311 if ( comp < 0 ) /* we are smaller than the current one */
2312 {
2313 TREEVIEW_InsertBefore(infoPtr, wineItem, aChild, parentItem);
2314 bItemInserted = TRUE;
2315 break;
2316 }
2317 else if ( comp > 0 ) /* we are bigger than the current one */
2318 {
2319 previousChild = aChild;
2320 aChild = (aChild->sibling == 0) /* This will help us to exit */
2321 ? NULL /* if there is no more sibling */
2322 : &infoPtr->items[(INT)aChild->sibling];
2323
2324 /* Look at the next item */
2325 continue;
2326 }
2327 else if ( comp == 0 )
2328 {
2329 /*
2330 * An item with this name is already existing, therefore,
2331 * we add after the one we found
2332 */
2333 TREEVIEW_InsertAfter(infoPtr, wineItem, aChild, parentItem);
2334 bItemInserted = TRUE;
2335 break;
2336 }
2337 }
2338
2339 /*
2340 * we reach the end of the child list and the item as not
2341 * yet been inserted, therefore, insert it after the last child.
2342 */
2343 if ( (! bItemInserted ) && (aChild == NULL) )
2344 TREEVIEW_InsertAfter(infoPtr, wineItem, previousChild, parentItem);
2345
2346 break;
2347 }
2348
2349
2350 case (DWORD) TVI_LAST:
2351 TVI_LAST_CASE:
2352 while (sibItem->sibling) {
2353 prevsib=sibItem;
2354 sibItem=&infoPtr->items [(INT)sibItem->sibling];
2355 }
2356 sibItem->sibling=(HTREEITEM)iItem;
2357 wineItem->upsibling=sibItem->hItem;
2358 break;
2359 default:
2360 while ((sibItem->sibling) && (sibItem->hItem!=ptdi->hInsertAfter))
2361 {
2362 prevsib=sibItem;
2363 sibItem=&infoPtr->items [(INT)sibItem->sibling];
2364 }
2365 if (sibItem->hItem!=ptdi->hInsertAfter) {
2366 goto TVI_LAST_CASE;
2367 }
2368 prevsib=sibItem;
2369 if (sibItem->sibling) {
2370 sibItem=&infoPtr->items [(INT)sibItem->sibling];
2371 sibItem->upsibling=(HTREEITEM)iItem;
2372 wineItem->sibling=sibItem->hItem;
2373 }
2374 prevsib->sibling=(HTREEITEM)iItem;
2375 wineItem->upsibling=prevsib->hItem;
2376 break;
2377 }
2378 }
2379
2380
2381 /* Fill in info structure */
2382
2383 wineItem->mask=tvItem->mask;
2384 wineItem->iIntegral=1;
2385
2386 if (tvItem->mask & TVIF_CHILDREN)
2387 wineItem->cChildren=tvItem->cChildren;
2388
2389
2390 wineItem->expandBox.left = 0; /* Initialize the expandBox */
2391 wineItem->expandBox.top = 0;
2392 wineItem->expandBox.right = 0;
2393 wineItem->expandBox.bottom = 0;
2394
2395 if (tvItem->mask & TVIF_IMAGE)
2396 wineItem->iImage=tvItem->iImage;
2397
2398 /* If the application sets TVIF_INTEGRAL without
2399 supplying a TVITEMEX structure, it's toast */
2400
2401 if (tvItem->mask & TVIF_INTEGRAL)
2402 wineItem->iIntegral=tvItem->iIntegral;
2403
2404 if (tvItem->mask & TVIF_SELECTEDIMAGE)
2405 wineItem->iSelectedImage=tvItem->iSelectedImage;
2406
2407 if (tvItem->mask & TVIF_STATE) {
2408 wineItem->state=tvItem->state;
2409 wineItem->stateMask=tvItem->stateMask;
2410 }
2411
2412 wineItem->calculated = FALSE;
2413 TREEVIEW_QueueRefresh(hwnd);
2414
2415 return (LRESULT) iItem;
2416}
2417
2418
2419static LRESULT
2420TREEVIEW_DeleteItem (HWND hwnd, WPARAM wParam, LPARAM lParam)
2421{
2422 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
2423 INT iItem;
2424 TREEVIEW_ITEM *wineItem;
2425
2426 if (lParam == (INT)TVI_ROOT)
2427 {
2428 TREEVIEW_RemoveTree (hwnd);
2429 } else
2430 {
2431 iItem = (INT)lParam;
2432 wineItem = TREEVIEW_ValidItem (infoPtr, (HTREEITEM)iItem);
2433 if (!wineItem) return FALSE;
2434
2435 TREEVIEW_RemoveItem (hwnd, wineItem);
2436 }
2437
2438 TREEVIEW_QueueRefresh(hwnd);
2439
2440 return TRUE;
2441}
2442
2443
2444
2445static LRESULT
2446TREEVIEW_GetIndent (HWND hwnd)
2447{
2448 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
2449
2450 return infoPtr->uIndent;
2451}
2452
2453static LRESULT
2454TREEVIEW_SetIndent (HWND hwnd, WPARAM wParam)
2455{
2456 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
2457 INT newIndent;
2458
2459 newIndent = (INT)wParam;
2460 if (newIndent < MINIMUM_INDENT) newIndent = MINIMUM_INDENT;
2461 if (newIndent != infoPtr->uIndent)
2462 {
2463 infoPtr->uIndent=newIndent;
2464 infoPtr->uInternalStatus |= TV_CALCALL;
2465 TREEVIEW_QueueRefresh(hwnd);
2466 }
2467
2468 return 0;
2469}
2470
2471static LRESULT
2472TREEVIEW_GetToolTips (HWND hwnd)
2473{
2474 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
2475
2476 return infoPtr->hwndToolTip;
2477}
2478
2479
2480static LRESULT
2481TREEVIEW_SetToolTips (HWND hwnd, WPARAM wParam)
2482{
2483 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
2484 HWND prevToolTip;
2485
2486 prevToolTip = infoPtr->hwndToolTip;
2487 infoPtr->hwndToolTip = (HWND)wParam;
2488
2489 return prevToolTip;
2490}
2491
2492
2493static LRESULT CALLBACK
2494TREEVIEW_GetEditControl (HWND hwnd)
2495{
2496 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
2497
2498 return infoPtr->hwndEdit;
2499}
2500
2501
2502//@@@PH: Note - this SubclassProc is sometimes called with the
2503// wrong window handle. Therefore, infoPtr points to anything
2504// but the expected structure.
2505LRESULT CALLBACK
2506TREEVIEW_Edit_SubclassProc (HWND hwnd, UINT uMsg, WPARAM wParam,
2507 LPARAM lParam)
2508{
2509 BOOL bCancel = FALSE;
2510 static BOOL bIgnoreKillFocus = FALSE;
2511
2512 switch (uMsg)
2513 {
2514 case WM_PAINT:
2515 {
2516 LRESULT rc;
2517 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(GetParent(hwnd));
2518 //TRACE("WM_PAINT start\n");
2519 rc = CallWindowProcA( infoPtr->wpEditOrig, hwnd, uMsg, wParam, lParam);
2520 //TRACE("WM_PAINT done\n");
2521 return rc;
2522 }
2523
2524 case WM_KILLFOCUS:
2525 if(bIgnoreKillFocus)
2526 {
2527 return TRUE;
2528 }
2529 break;
2530
2531 case WM_GETDLGCODE:
2532 return DLGC_WANTARROWS | DLGC_WANTALLKEYS;
2533
2534 case WM_KEYDOWN:
2535 if (VK_ESCAPE == (INT)wParam)
2536 {
2537 bCancel = TRUE;
2538 break;
2539 }
2540 else if (VK_RETURN == (INT)wParam)
2541 {
2542 break;
2543 }
2544
2545 default:
2546 {
2547 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(GetParent(hwnd));
2548
2549 //@@@PH 1999/11/05 method called with freed infoPtr memory object
2550 if (infoPtr != NULL)
2551 return CallWindowProcA( infoPtr->wpEditOrig, hwnd, uMsg, wParam, lParam);
2552 else
2553 break;
2554 }
2555 }
2556
2557 /* Processing LVN_ENDLABELEDIT message could kill the focus */
2558 /* eg. Using a messagebox */
2559 bIgnoreKillFocus = TRUE;
2560 TREEVIEW_EndEditLabelNow(GetParent(hwnd), bCancel);
2561 bIgnoreKillFocus = FALSE;
2562
2563 return 0;
2564}
2565
2566/* should handle edit control messages here */
2567
2568static LRESULT
2569TREEVIEW_Command (HWND hwnd, WPARAM wParam, LPARAM lParam)
2570
2571{
2572 switch (HIWORD(wParam))
2573 {
2574 case EN_UPDATE:
2575 {
2576 /*
2577 * Adjust the edit window size
2578 */
2579 char buffer[1024];
2580 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
2581 TREEVIEW_ITEM *editItem = TREEVIEW_ValidItem(infoPtr, infoPtr->editItem);
2582 HDC hdc = GetDC(infoPtr->hwndEdit);
2583 SIZE sz;
2584 int len;
2585 HFONT hFont, hOldFont=0;
2586
2587 len = GetWindowTextA(infoPtr->hwndEdit, buffer, 1024);
2588
2589 /* Select font to get the right dimension of the string */
2590 hFont = SendMessageA(infoPtr->hwndEdit, WM_GETFONT, 0, 0);
2591 if(hFont != 0)
2592 {
2593 hOldFont = SelectObject(hdc, hFont);
2594 }
2595
2596 if (GetTextExtentPoint32A(hdc, buffer, strlen(buffer), &sz))
2597 {
2598 TEXTMETRICA textMetric;
2599 /* Add Extra spacing for the next character */
2600 GetTextMetricsA(hdc, &textMetric);
2601 sz.cx += (textMetric.tmMaxCharWidth * 2);
2602
2603 SetWindowPos (
2604 infoPtr->hwndEdit,
2605 HWND_TOP,
2606 0,
2607 0,
2608 sz.cx,
2609 editItem->text.bottom - editItem->text.top + 3,
2610 SWP_NOMOVE|SWP_DRAWFRAME);
2611 }
2612
2613 if(hFont != 0)
2614 {
2615 SelectObject(hdc, hOldFont);
2616 }
2617
2618 ReleaseDC(hwnd, hdc);
2619 break;
2620 }
2621
2622 default:
2623 return SendMessageA (GetParent (hwnd), WM_COMMAND, wParam, lParam);
2624 }
2625
2626 return 0;
2627}
2628
2629static LRESULT
2630TREEVIEW_Size (HWND hwnd, WPARAM wParam, LPARAM lParam)
2631{
2632 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
2633
2634 if (wParam == SIZE_RESTORED)
2635 {
2636 if (TREEVIEW_CalcItems(hwnd,0,infoPtr))
2637 TREEVIEW_Refresh(hwnd); //because size redraw isn't set
2638 }
2639
2640 return TRUE;
2641}
2642
2643static LRESULT
2644TREEVIEW_StyleChanged (HWND hwnd, WPARAM wParam, LPARAM lParam)
2645{
2646 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
2647
2648 if (infoPtr->hwndEdit) SetFocus(hwnd);
2649
2650 //CB: todo: check styles
2651 infoPtr->uInternalStatus |= TV_CALCALL;
2652 TREEVIEW_QueueRefresh(hwnd);
2653
2654 return 0;
2655}
2656
2657static LRESULT
2658TREEVIEW_SysColorChange(HWND hwnd,WPARAM wParam,LPARAM lParam)
2659{
2660 TREEVIEW_Refresh(hwnd);
2661
2662 return DefWindowProcA(hwnd,WM_SYSCOLORCHANGE,wParam,lParam);
2663}
2664
2665static LRESULT
2666TREEVIEW_Create (HWND hwnd, WPARAM wParam, LPARAM lParam)
2667{
2668 TREEVIEW_INFO *infoPtr;
2669 DWORD dwStyle = GetWindowLongA (hwnd, GWL_STYLE);
2670 LOGFONTA logFont;
2671 TEXTMETRICA tm;
2672 HDC hdc;
2673
2674 /* allocate memory for info structure */
2675 infoPtr = (TREEVIEW_INFO *) COMCTL32_Alloc (sizeof(TREEVIEW_INFO));
2676
2677 SetWindowLongA( hwnd, 0, (DWORD)infoPtr);
2678
2679 if (infoPtr == NULL) return 0;
2680
2681 if ((TREEVIEW_INFO*)GetWindowLongA(hwnd,0) != infoPtr) return 0;
2682
2683 hdc = GetDC(hwnd);
2684
2685 /* set default settings */
2686 infoPtr->uInternalStatus = 0;
2687 if (IsWindowUnicode(hwnd)) infoPtr->uInternalStatus |= TV_UNICODE;
2688 infoPtr->uNumItems=0;
2689 infoPtr->clrBk = GetSysColor (COLOR_WINDOW);
2690 infoPtr->clrLine = GetSysColor (COLOR_WINDOWTEXT);
2691 infoPtr->clrInsertMark = GetSysColor (COLOR_BTNTEXT);
2692 infoPtr->cy = 0;
2693 infoPtr->cx = 0;
2694 infoPtr->uIndent = 15;
2695 infoPtr->himlNormal = NULL;
2696 infoPtr->himlState = NULL;
2697 infoPtr->uItemHeight = -1;
2698 GetTextMetricsA (hdc, &tm);
2699 infoPtr->hFont = GetStockObject (DEFAULT_GUI_FONT);
2700 GetObjectA (infoPtr->hFont, sizeof (LOGFONTA), &logFont);
2701 logFont.lfWeight=FW_BOLD;
2702 infoPtr->hBoldFont = CreateFontIndirectA (&logFont);
2703
2704 infoPtr->items = NULL;
2705 infoPtr->selectedItem = 0;
2706 infoPtr->clrText=-1; /* use system color */
2707 infoPtr->dropItem=0;
2708 infoPtr->insertMarkItem=0;
2709 infoPtr->insertBeforeorAfter=0;
2710 infoPtr->pCallBackSort=NULL;
2711 infoPtr->uScrollTime = 300; /* milliseconds */
2712 infoPtr->hwndEdit = 0;
2713
2714 infoPtr->hwndToolTip=0;
2715 if (!(dwStyle & TVS_NOTOOLTIPS))
2716 {
2717 /* Create tooltip control */
2718 TTTOOLINFOA ti;
2719
2720 infoPtr->hwndToolTip =
2721 CreateWindowExA (0, TOOLTIPS_CLASSA, NULL, 0,
2722 CW_USEDEFAULT, CW_USEDEFAULT,
2723 CW_USEDEFAULT, CW_USEDEFAULT,
2724 hwnd, 0, 0, 0);
2725
2726 /* Send NM_TOOLTIPSCREATED notification */
2727 if (infoPtr->hwndToolTip)
2728 {
2729 NMTOOLTIPSCREATED nmttc;
2730
2731 nmttc.hdr.hwndFrom = hwnd;
2732 nmttc.hdr.idFrom = GetWindowLongA( hwnd, GWL_ID);
2733 nmttc.hdr.code = NM_TOOLTIPSCREATED;
2734 nmttc.hwndToolTips = infoPtr->hwndToolTip;
2735
2736 SendMessageA (GetParent(hwnd),WM_NOTIFY,(WPARAM)GetWindowLongA(hwnd,GWL_ID),(LPARAM)&nmttc);
2737 }
2738
2739 ZeroMemory (&ti, sizeof(TTTOOLINFOA));
2740 ti.cbSize = sizeof(TTTOOLINFOA);
2741 ti.uFlags = TTF_IDISHWND | TTF_TRACK | TTF_TRANSPARENT;
2742 ti.hwnd = hwnd;
2743 ti.uId = 0;
2744 ti.lpszText = "Test"; /* LPSTR_TEXTCALLBACK; */
2745 SetRectEmpty (&ti.rect);
2746
2747 SendMessageA (infoPtr->hwndToolTip, TTM_ADDTOOLA, 0, (LPARAM)&ti);
2748 }
2749
2750 if (dwStyle & TVS_CHECKBOXES)
2751 {
2752 HBITMAP hbmLoad;
2753 int nIndex;
2754
2755 infoPtr->himlState = ImageList_Create (16, 16,ILC_COLOR | ILC_MASK, 15, 1);
2756
2757 //CB: MSDN docu says: uses DrawFrameControl
2758 hbmLoad = LoadBitmapA (COMCTL32_hModule, MAKEINTRESOURCEA(IDT_CHECK));
2759 nIndex = ImageList_AddMasked (infoPtr->himlState, hbmLoad, CLR_DEFAULT);
2760 DeleteObject (hbmLoad);
2761 }
2762 ReleaseDC (hwnd, hdc);
2763
2764 return 0;
2765}
2766
2767static LRESULT
2768TREEVIEW_Destroy (HWND hwnd)
2769{
2770 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
2771
2772// TRACE (treeview,"\n");
2773
2774 TREEVIEW_RemoveTree (hwnd);
2775 if (infoPtr->Timer & TV_REFRESH_TIMER_SET)
2776 KillTimer (hwnd, TV_REFRESH_TIMER);
2777 if (infoPtr->hwndToolTip)
2778 DestroyWindow (infoPtr->hwndToolTip);
2779
2780 /* Restore original windproc so that we can free infoPtr */
2781 if (infoPtr->hwndEdit)
2782 SetWindowLongA (infoPtr->hwndEdit, GWL_WNDPROC, (DWORD)infoPtr->wpEditOrig);
2783
2784 DeleteObject(infoPtr->hBoldFont);
2785 COMCTL32_Free (infoPtr);
2786
2787 return 0;
2788}
2789
2790
2791static LRESULT
2792TREEVIEW_Paint (HWND hwnd, WPARAM wParam, LPARAM lParam)
2793{
2794 HDC hdc;
2795 PAINTSTRUCT ps;
2796
2797 hdc = wParam==0 ? BeginPaint (hwnd, &ps) : (HDC)wParam;
2798 TREEVIEW_Draw(hwnd,hdc,&ps.rcPaint);
2799 if(!wParam)
2800 EndPaint (hwnd, &ps);
2801
2802 return 0;
2803}
2804
2805static LRESULT
2806TREEVIEW_SetFocus (HWND hwnd, WPARAM wParam, LPARAM lParam)
2807{
2808 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
2809 DWORD dwStyle = GetWindowLongA(hwnd,GWL_STYLE);
2810
2811 TREEVIEW_SendSimpleNotify (hwnd, NM_SETFOCUS);
2812
2813 if (!(dwStyle & TVS_SHOWSELALWAYS) && infoPtr->selectedItem)
2814 TREEVIEW_RefreshItem(hwnd,TREEVIEW_ValidItem(infoPtr,infoPtr->selectedItem),FALSE);
2815
2816 return 0;
2817}
2818
2819static LRESULT
2820TREEVIEW_KillFocus (HWND hwnd, WPARAM wParam, LPARAM lParam)
2821{
2822 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
2823 DWORD dwStyle = GetWindowLongA(hwnd,GWL_STYLE);
2824
2825 TREEVIEW_SendSimpleNotify (hwnd, NM_KILLFOCUS);
2826
2827 if (!(dwStyle & TVS_SHOWSELALWAYS) && infoPtr->selectedItem)
2828 TREEVIEW_RefreshItem(hwnd,TREEVIEW_ValidItem(infoPtr,infoPtr->selectedItem),FALSE);
2829
2830 return 0;
2831}
2832
2833static LRESULT TREEVIEW_Enable(HWND hwnd,WPARAM wParam,LPARAM lParam)
2834{
2835 TREEVIEW_Refresh(hwnd);
2836
2837 return DefWindowProcA(hwnd,WM_ENABLE,wParam,lParam);
2838}
2839
2840static LRESULT
2841TREEVIEW_EraseBackground (HWND hwnd, WPARAM wParam, LPARAM lParam)
2842{
2843 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
2844 HBRUSH hBrush = CreateSolidBrush (infoPtr->clrBk);
2845 RECT rect;
2846
2847 GetClientRect (hwnd, &rect);
2848 FillRect ((HDC)wParam, &rect, hBrush);
2849 DeleteObject (hBrush);
2850
2851 return TRUE;
2852}
2853
2854static LRESULT TREEVIEW_GetDlgCode(HWND hwnd,WPARAM wParam,LPARAM lParam)
2855{
2856 return DLGC_WANTARROWS | DLGC_WANTCHARS;
2857}
2858
2859/* Notifications */
2860
2861static BOOL
2862TREEVIEW_SendSimpleNotify (HWND hwnd, UINT code)
2863{
2864 NMHDR nmhdr;
2865
2866 nmhdr.hwndFrom = hwnd;
2867 nmhdr.idFrom = GetWindowLongA( hwnd, GWL_ID);
2868 nmhdr.code = code;
2869
2870 return (BOOL) SendMessageA (GetParent (hwnd), WM_NOTIFY,
2871 (WPARAM)nmhdr.idFrom, (LPARAM)&nmhdr);
2872}
2873
2874static BOOL
2875TREEVIEW_SendTreeviewNotify (HWND hwnd, UINT code, UINT action,
2876 HTREEITEM oldItem, HTREEITEM newItem)
2877
2878{
2879 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
2880 NMTREEVIEWW nmhdr;
2881 TREEVIEW_ITEM *wineItem;
2882 HWND parent = GetParent(hwnd);
2883 BOOL unicode = IsWindowUnicode(parent),rc;
2884 CHAR *oldText = NULL,*newText = NULL;
2885
2886// TRACE (treeview,"code:%x action:%x olditem:%x newitem:%x\n",
2887// code,action,(INT)oldItem,(INT)newItem);
2888
2889 ZeroMemory(&nmhdr,sizeof(NMTREEVIEWW));
2890
2891 nmhdr.hdr.hwndFrom = hwnd;
2892 nmhdr.hdr.idFrom = GetWindowLongW( hwnd, GWL_ID);
2893 nmhdr.hdr.code = code;
2894 nmhdr.action = action;
2895 if (oldItem)
2896 {
2897 wineItem=& infoPtr->items[(INT)oldItem];
2898 nmhdr.itemOld.mask = wineItem->mask;
2899 nmhdr.itemOld.hItem = wineItem->hItem;
2900 nmhdr.itemOld.state = wineItem->state;
2901 nmhdr.itemOld.stateMask = wineItem->stateMask;
2902 nmhdr.itemOld.iImage = wineItem->iImage;
2903 if (!unicode)
2904 {
2905 if (!wineItem->pszText) nmhdr.itemOld.pszText = NULL; else
2906 {
2907 INT len = lstrlenW(wineItem->pszText)+1;
2908
2909 oldText = COMCTL32_Alloc(len);
2910 lstrcpyWtoA(oldText,wineItem->pszText);
2911 nmhdr.itemOld.pszText = (WCHAR*)oldText;
2912 }
2913 } else nmhdr.itemOld.pszText = wineItem->pszText;
2914 nmhdr.itemOld.cchTextMax= wineItem->cchTextMax;
2915 nmhdr.itemOld.iImage = wineItem->iImage;
2916 nmhdr.itemOld.iSelectedImage = wineItem->iSelectedImage;
2917 nmhdr.itemOld.cChildren = wineItem->cChildren;
2918 nmhdr.itemOld.lParam = wineItem->lParam;
2919 }
2920
2921 if (newItem)
2922 {
2923 wineItem=& infoPtr->items[(INT)newItem];
2924 nmhdr.itemNew.mask = wineItem->mask;
2925 nmhdr.itemNew.hItem = wineItem->hItem;
2926 nmhdr.itemNew.state = wineItem->state;
2927 nmhdr.itemNew.stateMask = wineItem->stateMask;
2928 nmhdr.itemNew.iImage = wineItem->iImage;
2929 if (!unicode)
2930 {
2931 if (!wineItem->pszText) nmhdr.itemOld.pszText = NULL; else
2932 {
2933 INT len = lstrlenW(wineItem->pszText)+1;
2934
2935 newText = COMCTL32_Alloc(len);
2936 lstrcpyWtoA(newText,wineItem->pszText);
2937 nmhdr.itemOld.pszText = (WCHAR*)newText;
2938 }
2939 } else nmhdr.itemNew.pszText = wineItem->pszText;
2940 nmhdr.itemNew.cchTextMax= wineItem->cchTextMax;
2941 nmhdr.itemNew.iImage = wineItem->iImage;
2942 nmhdr.itemNew.iSelectedImage = wineItem->iSelectedImage;
2943 nmhdr.itemNew.cChildren = wineItem->cChildren;
2944 nmhdr.itemNew.lParam = wineItem->lParam;
2945 }
2946
2947 nmhdr.ptDrag.x = 0;
2948 nmhdr.ptDrag.y = 0;
2949
2950 if (unicode)
2951 rc = (BOOL)SendMessageW(parent,WM_NOTIFY,(WPARAM)GetWindowLongW(hwnd,GWL_ID),(LPARAM)&nmhdr);
2952 else
2953 rc = (BOOL)SendMessageA(parent,WM_NOTIFY,(WPARAM)GetWindowLongA(hwnd,GWL_ID),(LPARAM)&nmhdr);
2954
2955 if (oldText) COMCTL32_Free(oldText);
2956 if (newText) COMCTL32_Free(newText);
2957
2958 return rc;
2959}
2960
2961static BOOL
2962TREEVIEW_SendTreeviewDnDNotify (HWND hwnd, UINT code, HTREEITEM dragItem,
2963 POINT pt)
2964{
2965 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
2966 NMTREEVIEWA nmhdr;
2967 TREEVIEW_ITEM *wineItem;
2968
2969// TRACE (treeview,"code:%x dragitem:%x\n", code,(INT)dragItem);
2970
2971 nmhdr.hdr.hwndFrom = hwnd;
2972 nmhdr.hdr.idFrom = GetWindowLongA( hwnd, GWL_ID);
2973 nmhdr.hdr.code = code;
2974 nmhdr.action = 0;
2975 wineItem=& infoPtr->items[(INT)dragItem];
2976 nmhdr.itemNew.mask = wineItem->mask;
2977 nmhdr.itemNew.hItem = wineItem->hItem;
2978 nmhdr.itemNew.state = wineItem->state;
2979 nmhdr.itemNew.lParam = wineItem->lParam;
2980
2981 nmhdr.ptDrag.x = pt.x;
2982 nmhdr.ptDrag.y = pt.y;
2983
2984 return (BOOL)SendMessageA (GetParent (hwnd), WM_NOTIFY,
2985 (WPARAM) GetWindowLongA( hwnd, GWL_ID), (LPARAM)&nmhdr);
2986
2987}
2988
2989static BOOL
2990TREEVIEW_SendDispInfoNotify (HWND hwnd, TREEVIEW_ITEM *wineItem, UINT code, UINT what)
2991{
2992 NMTVDISPINFOW tvdi;
2993 BOOL retval;
2994 WCHAR *buf;
2995 HWND parent = GetParent(hwnd);
2996 BOOL unicode = IsWindowUnicode(parent);
2997
2998 tvdi.hdr.hwndFrom = hwnd;
2999 tvdi.hdr.idFrom = GetWindowLongA( hwnd, GWL_ID);
3000 tvdi.hdr.code = code;
3001 tvdi.item.mask = what;
3002 tvdi.item.hItem = wineItem->hItem;
3003 tvdi.item.state = wineItem->state;
3004 tvdi.item.lParam = wineItem->lParam;
3005 tvdi.item.pszText = COMCTL32_Alloc(128*(unicode? sizeof(WCHAR):sizeof(char)));
3006 tvdi.item.cchTextMax = 128;
3007 buf = tvdi.item.pszText;
3008
3009 if (unicode)
3010 retval = (BOOL)SendMessageW(parent,WM_NOTIFY,(WPARAM)tvdi.hdr.idFrom,(LPARAM)&tvdi);
3011 else
3012 retval = (BOOL)SendMessageA(parent,WM_NOTIFY,(WPARAM)tvdi.hdr.idFrom,(LPARAM)&tvdi);
3013
3014 /* Ignore posible changes */
3015 if (code == TVN_BEGINLABELEDIT)
3016 return retval;
3017
3018 if (what & TVIF_TEXT)
3019 {
3020 if (unicode)
3021 {
3022 wineItem->pszText = tvdi.item.pszText;
3023 if (buf == tvdi.item.pszText)
3024 {
3025 wineItem->cchTextMax = 128;
3026 } else
3027 {
3028 //TRACE (treeview,"user-supplied buffer\n");
3029 COMCTL32_Free(buf);
3030 wineItem->cchTextMax = 0;
3031 }
3032 } else
3033 {
3034 if (buf == tvdi.item.pszText)
3035 {
3036 COMCTL32_Free(wineItem->pszText);
3037 wineItem->cchTextMax = 128;
3038 wineItem->pszText = COMCTL32_Alloc(128*sizeof(WCHAR));
3039 lstrcpynAtoW(wineItem->pszText,(CHAR*)tvdi.item.pszText,wineItem->cchTextMax);
3040 COMCTL32_Free(buf);
3041 } else
3042 {
3043 //TRACE (treeview,"user-supplied buffer\n");
3044 COMCTL32_Free(buf);
3045 wineItem->cchTextMax = 0;
3046 }
3047 }
3048 }
3049
3050 if (what & TVIF_SELECTEDIMAGE)
3051 wineItem->iSelectedImage = tvdi.item.iSelectedImage;
3052 if (what & TVIF_IMAGE)
3053 wineItem->iImage = tvdi.item.iImage;
3054 if (what & TVIF_CHILDREN)
3055 wineItem->cChildren = tvdi.item.cChildren;
3056
3057 return retval;
3058}
3059
3060static BOOL
3061TREEVIEW_SendCustomDrawNotify (HWND hwnd, DWORD dwDrawStage, HDC hdc,
3062 RECT rc)
3063{
3064 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
3065 NMTVCUSTOMDRAW nmcdhdr;
3066 LPNMCUSTOMDRAW nmcd;
3067
3068// TRACE (treeview,"drawstage:%lx hdc:%x\n", dwDrawStage, hdc);
3069
3070 nmcd= & nmcdhdr.nmcd;
3071 nmcd->hdr.hwndFrom = hwnd;
3072 nmcd->hdr.idFrom = GetWindowLongA( hwnd, GWL_ID);
3073 nmcd->hdr.code = NM_CUSTOMDRAW;
3074 nmcd->dwDrawStage= dwDrawStage;
3075 nmcd->hdc = hdc;
3076 nmcd->rc.left = rc.left;
3077 nmcd->rc.right = rc.right;
3078 nmcd->rc.bottom = rc.bottom;
3079 nmcd->rc.top = rc.top;
3080 nmcd->dwItemSpec = 0;
3081 nmcd->uItemState = 0;
3082 nmcd->lItemlParam= 0;
3083 nmcdhdr.clrText = infoPtr->clrText;
3084 nmcdhdr.clrTextBk= infoPtr->clrBk;
3085 nmcdhdr.iLevel = 0;
3086
3087 return (BOOL)SendMessageA (GetParent (hwnd), WM_NOTIFY,
3088 (WPARAM) GetWindowLongA( hwnd, GWL_ID), (LPARAM)&nmcdhdr);
3089}
3090
3091/* FIXME: need to find out when the flags in uItemState need to be set */
3092
3093static BOOL
3094TREEVIEW_SendCustomDrawItemNotify (HWND hwnd, HDC hdc,
3095 TREEVIEW_ITEM *wineItem, UINT uItemDrawState)
3096{
3097 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
3098 NMTVCUSTOMDRAW nmcdhdr;
3099 LPNMCUSTOMDRAW nmcd;
3100 DWORD dwDrawStage,dwItemSpec;
3101 UINT uItemState;
3102 INT retval;
3103
3104 dwDrawStage=CDDS_ITEM | uItemDrawState;
3105 dwItemSpec=(DWORD)wineItem->hItem;
3106 uItemState=0;
3107 if (wineItem->hItem == infoPtr->selectedItem)
3108 {
3109 uItemState|=CDIS_SELECTED;
3110 if (GetFocus() == hwnd) uItemState |= CDIS_FOCUS;
3111 }
3112 if (wineItem->hItem==infoPtr->hotItem) uItemState|=CDIS_HOT;
3113
3114 nmcd= & nmcdhdr.nmcd;
3115 nmcd->hdr.hwndFrom = hwnd;
3116 nmcd->hdr.idFrom = GetWindowLongA( hwnd, GWL_ID);
3117 nmcd->hdr.code = NM_CUSTOMDRAW;
3118 nmcd->dwDrawStage= dwDrawStage;
3119 nmcd->hdc = hdc;
3120 nmcd->rc.left = wineItem->rect.left;
3121 nmcd->rc.right = wineItem->rect.right;
3122 nmcd->rc.bottom = wineItem->rect.bottom;
3123 nmcd->rc.top = wineItem->rect.top;
3124 nmcd->dwItemSpec = dwItemSpec;
3125 nmcd->uItemState = uItemState;
3126 nmcd->lItemlParam= wineItem->lParam;
3127
3128 nmcdhdr.clrText = infoPtr->clrText;
3129 nmcdhdr.clrTextBk= infoPtr->clrBk;
3130 nmcdhdr.iLevel = wineItem->iLevel;
3131
3132 //TRACE (treeview,"drawstage:%lx hdc:%x item:%lx, itemstate:%x\n",
3133 // dwDrawStage, hdc, dwItemSpec, uItemState);
3134
3135 retval=SendMessageA (GetParent (hwnd), WM_NOTIFY,
3136 (WPARAM) GetWindowLongA( hwnd, GWL_ID), (LPARAM)&nmcdhdr);
3137
3138 infoPtr->clrText=nmcdhdr.clrText;
3139 infoPtr->clrBk =nmcdhdr.clrTextBk;
3140
3141 return (BOOL) retval;
3142}
3143
3144static VOID TREEVIEW_SendKeyDownNotify(HWND hwnd,UINT code,WORD wVKey)
3145{
3146 NMTVKEYDOWN nmkdhdr;
3147
3148 nmkdhdr.hdr.hwndFrom = hwnd;
3149 nmkdhdr.hdr.idFrom = GetWindowLongA( hwnd, GWL_ID);
3150 nmkdhdr.hdr.code = code;
3151 nmkdhdr.wVKey = wVKey;
3152 nmkdhdr.flags = 0;
3153
3154 SendMessageA(GetParent(hwnd),WM_NOTIFY,(WPARAM)nmkdhdr.hdr.idFrom,(LPARAM)&nmkdhdr);
3155}
3156
3157/* Note:If the specified item is the child of a collapsed parent item,
3158 the parent's list of child items is (recursively) expanded to reveal the
3159 specified item. This is mentioned for TREEVIEW_SelectItem; don't
3160 know if it also applies here.
3161*/
3162
3163static LRESULT
3164TREEVIEW_Expand (HWND hwnd, WPARAM wParam, LPARAM lParam)
3165{
3166 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
3167 TREEVIEW_ITEM *wineItem;
3168 UINT flag;
3169 INT expand;
3170
3171 flag = (UINT)wParam;
3172 expand = (INT)lParam;
3173
3174 wineItem = TREEVIEW_ValidItem (infoPtr,(HTREEITEM)expand);
3175
3176 if (!wineItem)
3177 return 0;
3178
3179 if (!TREEVIEW_HasChildren(hwnd, wineItem))
3180 return 0;
3181
3182 if ((flag & 0xF) == TVE_TOGGLE)
3183 {
3184 if (wineItem->state & TVIS_EXPANDED)
3185 flag = (flag & ~0xF)+TVE_COLLAPSE;
3186 else
3187 flag = (flag & ~0xF)+TVE_EXPAND;
3188 }
3189
3190 switch (flag & 0xF)
3191 {
3192 case TVE_COLLAPSE:
3193 if (!wineItem->state & TVIS_EXPANDED)
3194 return 0;
3195
3196 if (flag & TVE_COLLAPSERESET)
3197 {
3198 wineItem->state &= ~(TVIS_EXPANDEDONCE | TVIS_EXPANDED);
3199 TREEVIEW_RemoveAllChildren (hwnd, wineItem);
3200 } else wineItem->state &= ~TVIS_EXPANDED;
3201 break;
3202
3203 case TVE_EXPAND:
3204 if (wineItem->state & TVIS_EXPANDED)
3205 return 0;
3206
3207 if (flag & TVE_EXPANDPARTIAL)
3208 {
3209 //TRACE(treeview, " case TVE_EXPANDPARTIAL\n");
3210 //FIXME (treeview, "TVE_EXPANDPARTIAL not implemented\n");
3211return FALSE; //CB: to check
3212 wineItem->state ^=TVIS_EXPANDED;
3213 wineItem->state |=TVIS_EXPANDEDONCE;
3214 break;
3215 }
3216
3217 if (!(wineItem->state & TVIS_EXPANDEDONCE))
3218 {
3219 //TRACE(treeview, " and has never been expanded...\n");
3220 wineItem->state |= TVIS_EXPANDED;
3221
3222 /* this item has never been expanded */
3223 if (TREEVIEW_SendTreeviewNotify(hwnd,TVN_ITEMEXPANDING,TVE_EXPAND,0,(HTREEITEM)expand))
3224 return FALSE;
3225
3226
3227 /* FIXME
3228 * Since the TVN_ITEMEXPANDING message may has caused the parent to
3229 * insert new items which in turn may have cause items placeholder
3230 * reallocation, I reassign the current item pointer so we have
3231 * something valid to work with...
3232 * However, this should not be necessary,
3233 * investigation required in TREEVIEW_InsertItemA
3234 */
3235 wineItem = TREEVIEW_ValidItem (infoPtr, (HTREEITEM)expand);
3236 if (!wineItem)
3237 {
3238 //ERR(treeview,
3239 // "Catastropic situation, cannot retreive item #%d\n",
3240 // expand);
3241 return FALSE;
3242 }
3243
3244 wineItem->state |= TVIS_EXPANDEDONCE;
3245 //TRACE(treeview, " TVN_ITEMEXPANDING sent...\n");
3246
3247 TREEVIEW_SendTreeviewNotify (
3248 hwnd,
3249 TVN_ITEMEXPANDED,
3250 TVE_EXPAND,
3251 0,
3252 (HTREEITEM)expand);
3253
3254 //TRACE(treeview, " TVN_ITEMEXPANDED sent...\n");
3255
3256 } else
3257 {
3258 /* this item has already been expanded */
3259 wineItem->state |= TVIS_EXPANDED;
3260 }
3261 break;
3262
3263 default:
3264 return FALSE;
3265 }
3266
3267 //CB: todo: optimize!
3268 TREEVIEW_UnqueueRefresh(hwnd,FALSE,FALSE);
3269 //CB: todo: precalc expanded items here
3270 infoPtr->uInternalStatus |= TV_CALCALL;
3271 TREEVIEW_CalcItems(hwnd,0,infoPtr);
3272 TREEVIEW_Refresh(hwnd);
3273 //CB: todo: check cx and cy to fit ranges!
3274
3275 return TRUE;
3276}
3277
3278static LRESULT TREEVIEW_HitTest(HWND hwnd,LPTVHITTESTINFO lpht)
3279{
3280 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
3281 TREEVIEW_ITEM *wineItem;
3282 RECT rect;
3283 UINT status,x,y;
3284
3285 if (!lpht) return 0;
3286 GetClientRect (hwnd, &rect);
3287 status = 0;
3288 x = lpht->pt.x;
3289 y = lpht->pt.y;
3290 if (x < rect.left) status |= TVHT_TOLEFT;
3291 if (x > rect.right) status |= TVHT_TORIGHT;
3292 if (y < rect.top ) status |= TVHT_ABOVE;
3293 if (y > rect.bottom) status |= TVHT_BELOW;
3294
3295 if (status)
3296 {
3297 lpht->flags = status;
3298 lpht->hItem = 0;
3299
3300 return 0;
3301 }
3302
3303 if (infoPtr->firstVisible)
3304 {
3305 wineItem = &infoPtr->items [(INT)infoPtr->firstVisible];
3306
3307 while ((wineItem != NULL) && (y > wineItem->rect.bottom))
3308 wineItem = TREEVIEW_GetNextListItem(hwnd,infoPtr,wineItem);
3309 } else wineItem = NULL;
3310
3311 if (!wineItem)
3312 {
3313 lpht->flags = TVHT_NOWHERE;
3314 lpht->hItem = 0;
3315
3316 return 0;
3317 }
3318
3319 lpht->flags = 0;
3320
3321 if (x < wineItem->expandBox.left) {
3322 lpht->flags |= TVHT_ONITEMINDENT;
3323 goto done;
3324 }
3325 if ( PtInRect ( &wineItem->expandBox, lpht->pt)) {
3326 lpht->flags |= TVHT_ONITEMBUTTON;
3327 goto done;
3328 }
3329 if ( PtInRect ( &wineItem->bitmap, lpht->pt)) {
3330 lpht->flags |= TVHT_ONITEMICON;
3331 goto done;
3332 }
3333 if ( PtInRect ( &wineItem->statebitmap, lpht->pt)) {
3334 lpht->flags |= TVHT_ONITEMSTATEICON;
3335 goto done;
3336 }
3337 if ( PtInRect ( &wineItem->text, lpht->pt)) {
3338 lpht->flags |= TVHT_ONITEMLABEL;
3339 goto done;
3340 }
3341
3342 lpht->flags |= TVHT_ONITEMRIGHT;
3343
3344done:
3345 lpht->hItem = wineItem->hItem;
3346
3347 return (LRESULT) wineItem->hItem;
3348}
3349
3350HWND TREEVIEW_EditLabel(HWND hwnd,HTREEITEM hItem,BOOL unicode)
3351{
3352 SIZE sz;
3353 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
3354 TREEVIEW_ITEM *editItem = TREEVIEW_ValidItem(infoPtr, (HTREEITEM)hItem);
3355 HINSTANCE hinst = GetWindowLongA(hwnd, GWL_HINSTANCE);
3356 HDC hdc;
3357 HFONT hOldFont = 0;
3358 TEXTMETRICA textMetric;
3359
3360 if (!editItem)
3361 return FALSE;
3362
3363 if(infoPtr->hwndEdit)
3364 return infoPtr->hwndEdit;
3365
3366 /* Make shure that edit item is selected */
3367
3368 TREEVIEW_DoSelectItem ( hwnd, TVGN_CARET, hItem, TVC_UNKNOWN);
3369
3370 if (editItem->pszText == LPSTR_TEXTCALLBACKW)
3371 TREEVIEW_SendDispInfoNotify (hwnd, editItem, TVN_GETDISPINFO, TVIF_TEXT);
3372
3373 hdc = GetDC(hwnd);
3374 /* Select the font to get appropriate metric dimensions */
3375 if(infoPtr->hFont != 0)
3376 {
3377 hOldFont = SelectObject(hdc, infoPtr->hFont);
3378 }
3379
3380 /*Get String Lenght in pixels */
3381 GetTextExtentPoint32W(hdc, editItem->pszText, lstrlenW(editItem->pszText), &sz);
3382
3383 /*Add Extra spacing for the next character */
3384 GetTextMetricsA(hdc, &textMetric);
3385 sz.cx += (textMetric.tmMaxCharWidth * 2);
3386
3387 if(infoPtr->hFont != 0)
3388 {
3389 SelectObject(hdc, hOldFont);
3390 }
3391
3392 ReleaseDC(hwnd, hdc);
3393 infoPtr->hwndEdit = CreateWindowExA (
3394 WS_EX_LEFT,
3395 "EDIT",
3396 0,
3397 WS_CHILD | WS_BORDER | ES_AUTOHSCROLL | WS_CLIPSIBLINGS |
3398 ES_WANTRETURN | ES_LEFT,
3399 editItem->text.left - 2, editItem->text.top - 1,
3400 sz.cx+3, editItem->text.bottom - editItem->text.top + 3,
3401 hwnd,
3402 0,hinst,0); /* FIXME: (HMENU)IDTVEDIT,pcs->hInstance,0);*/
3403
3404 SendMessageA ( infoPtr->hwndEdit, WM_SETFONT, infoPtr->hFont, FALSE);
3405 infoPtr->wpEditOrig = (WNDPROC)SetWindowLongA (
3406 infoPtr->hwndEdit,
3407 GWL_WNDPROC,
3408 (DWORD) TREEVIEW_Edit_SubclassProc);
3409
3410 if (TREEVIEW_SendDispInfoNotify (hwnd, editItem, TVN_BEGINLABELEDIT,
3411 editItem->mask & (TVIF_HANDLE|TVIF_TEXT|TVIF_STATE|TVIF_PARAM)))
3412 {
3413 DestroyWindow(infoPtr->hwndEdit);
3414 infoPtr->hwndEdit = 0;
3415
3416 return (HWND)0;
3417 }
3418
3419 infoPtr->editItem = hItem;
3420 SetWindowTextW(infoPtr->hwndEdit, editItem->pszText);
3421 SetFocus(infoPtr->hwndEdit);
3422 SendMessageA(infoPtr->hwndEdit, EM_SETSEL, 0, -1);
3423 ShowWindow(infoPtr->hwndEdit, SW_SHOW);
3424
3425 return infoPtr->hwndEdit;
3426}
3427
3428
3429LRESULT WINAPI
3430TREEVIEW_EndEditLabelNow (HWND hwnd, BOOL bCancel)
3431{
3432 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
3433 TREEVIEW_ITEM *editedItem = TREEVIEW_ValidItem (infoPtr, infoPtr->editItem);
3434 NMTVDISPINFOW tvdi;
3435 DWORD dwStyle = GetWindowLongA(hwnd,GWL_STYLE);
3436 HWND parent = GetParent(hwnd);
3437 BOOL unicode = IsWindowUnicode(parent);
3438 BOOL bCommit;
3439 WCHAR *textW = NULL;
3440 CHAR *textA = NULL;
3441 int iLength = 0;
3442
3443 if (!infoPtr->hwndEdit)
3444 return FALSE;
3445
3446 tvdi.hdr.hwndFrom = hwnd;
3447 tvdi.hdr.idFrom = GetWindowLongA(hwnd, GWL_ID);
3448 tvdi.hdr.code = TVN_ENDLABELEDIT;
3449 tvdi.item.mask = 0;
3450 tvdi.item.hItem = editedItem->hItem;
3451 tvdi.item.state = editedItem->state;
3452 tvdi.item.lParam = editedItem->lParam;
3453
3454 if (!bCancel)
3455 {
3456 textW = COMCTL32_Alloc(1024*sizeof(WCHAR));
3457 iLength = GetWindowTextW(infoPtr->hwndEdit,textW,1023);
3458
3459 //if (iLength >= 1023)
3460 //{
3461 // ERR("Insuficient space to retrieve new item label.");
3462 //}
3463
3464 if (unicode) tvdi.item.pszText = textW; else
3465 {
3466 INT len = iLength+1;
3467
3468 textA = COMCTL32_Alloc(len);
3469 lstrcpynWtoA(textA,textW,len);
3470 tvdi.item.pszText = (WCHAR*)textA;
3471 }
3472 tvdi.item.cchTextMax = iLength + 1;
3473 }
3474 else
3475 {
3476 tvdi.item.pszText = NULL;
3477 tvdi.item.cchTextMax = 0;
3478 }
3479
3480 if (unicode)
3481 bCommit=(BOOL)SendMessageW(parent,WM_NOTIFY,(WPARAM)tvdi.hdr.idFrom,(LPARAM)&tvdi);
3482 else
3483 bCommit=(BOOL)SendMessageA(parent,WM_NOTIFY,(WPARAM)tvdi.hdr.idFrom,(LPARAM)&tvdi);
3484
3485 if (!bCancel && bCommit) /* Apply the changes */
3486 {
3487 if (!unicode)
3488 lstrcpynAtoW(textW,textA,iLength+1);
3489 if (lstrcmpW(textW,editedItem->pszText) != 0)
3490 {
3491 if(NULL == COMCTL32_ReAlloc(editedItem->pszText,(iLength+1)*sizeof(WCHAR)))
3492 {
3493 //ERR("OutOfMemory, cannot allocate space for label");
3494 DestroyWindow(infoPtr->hwndEdit);
3495 infoPtr->hwndEdit = 0;
3496 if (textA) COMCTL32_Free(textA);
3497 if (textW) COMCTL32_Free(textW);
3498
3499 return FALSE;
3500 }
3501 else
3502 {
3503 editedItem->cchTextMax = iLength + 1;
3504 lstrcpyW( editedItem->pszText,textW);
3505 }
3506 }
3507 }
3508
3509 ShowWindow(infoPtr->hwndEdit, SW_HIDE);
3510 DestroyWindow(infoPtr->hwndEdit);
3511 infoPtr->hwndEdit = 0;
3512 infoPtr->editItem = 0;
3513 if (textA) COMCTL32_Free(textA);
3514 if (textW) COMCTL32_Free(textW);
3515
3516 editedItem->calculated = FALSE;
3517 TREEVIEW_CalcItem(hwnd,0,dwStyle,infoPtr,editedItem);
3518 TREEVIEW_RefreshItem(hwnd,editedItem,TRUE);
3519
3520 return TRUE;
3521}
3522
3523
3524/***************************************************************************
3525 * This is quite unusual piece of code, but that's how it's implemented in
3526 * Windows.
3527 */
3528static LRESULT TREEVIEW_TrackMouse(HWND hwnd, POINT pt)
3529{
3530 INT cxDrag = GetSystemMetrics(SM_CXDRAG);
3531 INT cyDrag = GetSystemMetrics(SM_CYDRAG);
3532 RECT r;
3533 MSG msg;
3534
3535 r.top = pt.y - cyDrag;
3536 r.left = pt.x - cxDrag;
3537 r.bottom = pt.y + cyDrag;
3538 r.right = pt.x + cxDrag;
3539
3540 SetCapture(hwnd);
3541
3542 while(1)
3543 {
3544 if (PeekMessageA(&msg, 0, 0, 0, PM_REMOVE|PM_NOYIELD))
3545 {
3546 if (msg.message == WM_MOUSEMOVE)
3547 {
3548 pt.x = (INT)LOWORD(msg.lParam);
3549 pt.y = (INT)HIWORD(msg.lParam);
3550 if (PtInRect(&r, pt))
3551 continue;
3552 else
3553 {
3554 ReleaseCapture();
3555 return 1;
3556 }
3557 }
3558 else if (msg.message >= WM_LBUTTONDOWN &&
3559 msg.message <= WM_RBUTTONDBLCLK)
3560 {
3561 break;
3562 }
3563
3564 DispatchMessageA( &msg );
3565 }
3566
3567 if (GetCapture() != hwnd)
3568 return 0;
3569 }
3570
3571 ReleaseCapture();
3572 return 0;
3573}
3574
3575static LRESULT
3576TREEVIEW_LButtonDoubleClick (HWND hwnd, WPARAM wParam, LPARAM lParam)
3577{
3578 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
3579 TREEVIEW_ITEM *wineItem;
3580 TV_HITTESTINFO hitinfo;
3581
3582 hitinfo.pt.x = (INT)LOWORD(lParam);
3583 hitinfo.pt.y = (INT)HIWORD(lParam);
3584 SetFocus (hwnd);
3585
3586 if (infoPtr->Timer & TV_EDIT_TIMER_SET)
3587 {
3588 /* If there is pending 'edit label' event - kill it now */
3589 infoPtr->editItem = 0;
3590 KillTimer (hwnd, TV_EDIT_TIMER);
3591 }
3592
3593 if (!TREEVIEW_HitTest(hwnd,&hitinfo) || !(hitinfo.flags & TVHT_ONITEM)) return FALSE;
3594 wineItem = &infoPtr->items [(INT)hitinfo.hItem];
3595
3596 //MSDN documentation says: stop on non zero. but this isn't true in this case
3597 if (!TREEVIEW_SendSimpleNotify (hwnd, NM_DBLCLK))
3598 TREEVIEW_Expand (hwnd, (WPARAM) TVE_TOGGLE, (LPARAM) wineItem->hItem);
3599
3600 return TRUE;
3601}
3602
3603static LRESULT TREEVIEW_MouseMove(HWND hwnd,WPARAM wParam,LPARAM lParam)
3604{
3605 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
3606 TVHITTESTINFO ht;
3607 TREEVIEW_ITEM *item;
3608
3609 ht.pt.x = (INT)LOWORD(lParam);
3610 ht.pt.y = (INT)HIWORD(lParam);
3611
3612 if (!TREEVIEW_HitTest(hwnd,&ht)) return 0;
3613
3614 if (ht.hItem)
3615 {
3616 item = &infoPtr->items[(INT)ht.hItem];
3617 }
3618
3619 return 0;
3620}
3621
3622static LRESULT
3623TREEVIEW_LButtonDown (HWND hwnd, WPARAM wParam, LPARAM lParam)
3624{
3625 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
3626 TVHITTESTINFO ht;
3627 BOOL bTrack;
3628 DWORD dwStyle = GetWindowLongA (hwnd, GWL_STYLE);
3629
3630 /* If Edit control is active - kill it and return.
3631 * The best way to do it is to set focus to itself.
3632 * Edit control subclassed procedure will automatically call
3633 * EndEditLabelNow.
3634 */
3635 if (infoPtr->hwndEdit)
3636 {
3637 SetFocus (hwnd);
3638 return 0;
3639 }
3640
3641 ht.pt.x = (INT)LOWORD(lParam);
3642 ht.pt.y = (INT)HIWORD(lParam);
3643
3644 TREEVIEW_HitTest (hwnd,&ht);
3645
3646 bTrack = (ht.flags & TVHT_ONITEM) && !(dwStyle & TVS_DISABLEDRAGDROP);
3647
3648 /* Send NM_CLICK right away */
3649 if (!bTrack)
3650 if ( TREEVIEW_SendSimpleNotify (hwnd, NM_CLICK) )
3651 goto setfocus;
3652
3653 if (ht.flags & TVHT_ONITEMBUTTON)
3654 {
3655 TREEVIEW_Expand (hwnd, (WPARAM) TVE_TOGGLE, (LPARAM) ht.hItem);
3656 goto setfocus;
3657 }
3658 else if (bTrack)
3659 {
3660 if (TREEVIEW_TrackMouse(hwnd, ht.pt))
3661 {
3662 TREEVIEW_SendTreeviewDnDNotify (hwnd, TVN_BEGINDRAG, ht.hItem, ht.pt);
3663 infoPtr->dropItem = ht.hItem;
3664 return 0;
3665 }
3666 }
3667
3668 if(TREEVIEW_SendSimpleNotify (hwnd, NM_CLICK))
3669 goto setfocus;
3670
3671//CB: todo: check ht.flags for WM_HSCROLL!
3672
3673 /*
3674 * If the style allow editing and the node is already selected
3675 * and the click occured on the item label...
3676 */
3677 if ( ( dwStyle & TVS_EDITLABELS ) &&
3678 ( ht.flags & TVHT_ONITEMLABEL ) &&
3679 ( infoPtr->selectedItem == ht.hItem ) )
3680 {
3681 if (infoPtr->Timer & TV_EDIT_TIMER_SET)
3682 KillTimer (hwnd, TV_EDIT_TIMER);
3683
3684 infoPtr->editItem = ht.hItem;
3685 SetTimer (hwnd, TV_EDIT_TIMER, GetDoubleClickTime(), 0);
3686 infoPtr->Timer|=TV_EDIT_TIMER_SET;
3687 }
3688 else if ( ht.flags & (TVHT_ONITEMLABEL | TVHT_ONITEMICON))
3689 {
3690 TREEVIEW_DoSelectItem ( hwnd, TVGN_CARET, ht.hItem, TVC_BYMOUSE);
3691 }
3692 else if (ht.flags & TVHT_ONITEMSTATEICON)
3693 {
3694 if (dwStyle & TVS_CHECKBOXES) /* TVS_CHECKBOXES requires _us_ */
3695 { /* to toggle the current state */
3696 int state;
3697 TREEVIEW_ITEM *wineItem;
3698
3699 wineItem = TREEVIEW_ValidItem(infoPtr, ht.hItem);
3700
3701 state = 1-(wineItem->state>>12);
3702 wineItem->state &= ~TVIS_STATEIMAGEMASK;
3703 wineItem->state |= state<<12;
3704 if (wineItem->visible)
3705 {
3706 TREEVIEW_UnqueueRefresh(hwnd,TRUE,TRUE);
3707 InvalidateRect(hwnd,&wineItem->statebitmap,TRUE);
3708 }
3709 }
3710 }
3711
3712setfocus:
3713 SetFocus (hwnd);
3714 return 0;
3715}
3716
3717
3718static LRESULT
3719TREEVIEW_RButtonDown (HWND hwnd, WPARAM wParam, LPARAM lParam)
3720{
3721 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
3722 TVHITTESTINFO ht;
3723
3724 if (infoPtr->hwndEdit)
3725 {
3726 SetFocus(hwnd);
3727 return 0;
3728 }
3729
3730 ht.pt.x = (INT)LOWORD(lParam);
3731 ht.pt.y = (INT)HIWORD(lParam);
3732
3733 TREEVIEW_HitTest(hwnd,&ht);
3734
3735 if (TREEVIEW_TrackMouse(hwnd, ht.pt))
3736 {
3737 if (ht.hItem)
3738 {
3739 TREEVIEW_SendTreeviewDnDNotify (hwnd, TVN_BEGINRDRAG, ht.hItem, ht.pt);
3740 infoPtr->dropItem = ht.hItem;
3741 }
3742 }
3743 else
3744 {
3745 SetFocus(hwnd);
3746 TREEVIEW_SendSimpleNotify (hwnd, NM_RCLICK);
3747 }
3748
3749 return 0;
3750}
3751
3752static LRESULT TREEVIEW_RButtonDoubleClick(HWND hwnd,WPARAM wParam,LPARAM lParam)
3753{
3754 TREEVIEW_SendSimpleNotify(hwnd,NM_RDBLCLK);
3755
3756 return DefWindowProcA(hwnd,WM_RBUTTONDBLCLK,wParam,lParam);
3757}
3758
3759static LRESULT
3760TREEVIEW_CreateDragImage (HWND hwnd, WPARAM wParam, LPARAM lParam)
3761{
3762 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
3763 TREEVIEW_ITEM *dragItem;
3764 INT cx,cy;
3765 HDC hdc,htopdc;
3766 HWND hwtop;
3767 HBITMAP hbmp,hOldbmp;
3768 SIZE size;
3769 RECT rc;
3770 HFONT hOldFont;
3771 WCHAR *itemtxt;
3772
3773// TRACE (treeview,"\n");
3774 if (!(infoPtr->himlNormal)) return 0;
3775 dragItem=TREEVIEW_ValidItem (infoPtr, (HTREEITEM) lParam);
3776
3777 if (!dragItem) return 0;
3778
3779 if (dragItem->pszText==LPSTR_TEXTCALLBACKW) {
3780 TREEVIEW_SendDispInfoNotify (hwnd, dragItem, TVN_GETDISPINFO, TVIF_TEXT);
3781 }
3782 itemtxt=dragItem->pszText;
3783
3784 hwtop=GetDesktopWindow ();
3785 htopdc= GetDC (hwtop);
3786 hdc=CreateCompatibleDC (htopdc);
3787
3788 hOldFont=SelectObject (hdc, infoPtr->hFont);
3789 GetTextExtentPoint32W (hdc, itemtxt, lstrlenW (itemtxt), &size);
3790// TRACE (treeview,"%d %d %s %d\n",size.cx,size.cy,itemtxt,lstrlenA(itemtxt));
3791 hbmp=CreateCompatibleBitmap (htopdc, size.cx, size.cy);
3792 hOldbmp=SelectObject (hdc, hbmp);
3793
3794 ImageList_GetIconSize (infoPtr->himlNormal, &cx, &cy);
3795 size.cx+=cx;
3796 if (cy>size.cy) size.cy=cy;
3797
3798 infoPtr->dragList=ImageList_Create (size.cx, size.cy, ILC_COLOR, 10, 10);
3799 ImageList_Draw (infoPtr->himlNormal, dragItem->iImage, hdc, 0, 0, ILD_NORMAL);
3800
3801/*
3802 ImageList_GetImageInfo (infoPtr->himlNormal, dragItem->hItem, &iminfo);
3803 ImageList_AddMasked (infoPtr->dragList, iminfo.hbmImage, CLR_DEFAULT);
3804*/
3805
3806/* draw item text */
3807
3808 SetRect (&rc, cx, 0, size.cx,size.cy);
3809 DrawTextW (hdc, itemtxt, lstrlenW (itemtxt), &rc, DT_LEFT);
3810 SelectObject (hdc, hOldFont);
3811 SelectObject (hdc, hOldbmp);
3812
3813 ImageList_Add (infoPtr->dragList, hbmp, 0);
3814
3815 DeleteDC (hdc);
3816 DeleteObject (hbmp);
3817 ReleaseDC (hwtop, htopdc);
3818
3819 return (LRESULT)infoPtr->dragList;
3820}
3821
3822
3823static LRESULT
3824TREEVIEW_DoSelectItem (HWND hwnd, INT action, HTREEITEM newSelect, INT cause)
3825{
3826 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
3827 TREEVIEW_ITEM *prevItem,*wineItem;
3828 INT prevSelect;
3829
3830 wineItem = TREEVIEW_ValidItem (infoPtr, (HTREEITEM)newSelect);
3831
3832 if (wineItem && wineItem->parent)
3833 {
3834 TREEVIEW_ITEM *parent = TREEVIEW_ValidItem(infoPtr,wineItem->parent);
3835
3836 /*
3837 * If the item has a collapse parent expand the parent so he
3838 * can expose the item
3839 */
3840 while (parent && !(parent->state & TVIS_EXPANDED))
3841 {
3842 TREEVIEW_Expand(hwnd,TVE_EXPAND,(LPARAM)parent->hItem);
3843 parent = TREEVIEW_ValidItem(infoPtr,parent->parent);
3844 }
3845 }
3846
3847 switch (action)
3848 {
3849 case TVGN_CARET:
3850 prevSelect = (INT)infoPtr->selectedItem;
3851
3852 if ((HTREEITEM)prevSelect == newSelect)
3853 return FALSE;
3854
3855 prevItem= TREEVIEW_ValidItem (infoPtr, (HTREEITEM)prevSelect);
3856
3857 if (newSelect)
3858 if (TREEVIEW_SendTreeviewNotify(
3859 hwnd,
3860 TVN_SELCHANGING,
3861 cause,
3862 (HTREEITEM)prevSelect,
3863 (HTREEITEM)newSelect))
3864 return FALSE; /* FIXME: OK? */
3865
3866 if (prevItem)
3867 prevItem->state &= ~TVIS_SELECTED;
3868 if (wineItem)
3869 wineItem->state |= TVIS_SELECTED;
3870
3871 infoPtr->selectedItem = (HTREEITEM)newSelect;
3872
3873 TREEVIEW_UnqueueRefresh(hwnd,TRUE,TRUE);
3874 TREEVIEW_RefreshItem(hwnd,prevItem,FALSE);
3875 TREEVIEW_RefreshItem(hwnd,wineItem,FALSE);
3876
3877 TREEVIEW_SendTreeviewNotify(
3878 hwnd,
3879 TVN_SELCHANGED,
3880 cause,
3881 (HTREEITEM)prevSelect,
3882 (HTREEITEM)newSelect);
3883
3884 break;
3885
3886 case TVGN_DROPHILITE:
3887 prevItem= TREEVIEW_ValidItem (infoPtr, infoPtr->dropItem);
3888
3889 if (prevItem)
3890 prevItem->state &= ~TVIS_DROPHILITED;
3891
3892 infoPtr->dropItem = (HTREEITEM)newSelect;
3893
3894 if (wineItem)
3895 wineItem->state |=TVIS_DROPHILITED;
3896
3897 TREEVIEW_UnqueueRefresh(hwnd,TRUE,TRUE);
3898 TREEVIEW_RefreshItem(hwnd,prevItem,FALSE);
3899 TREEVIEW_RefreshItem(hwnd,wineItem,FALSE);
3900
3901 break;
3902
3903 case TVGN_FIRSTVISIBLE:
3904 {
3905 INT scrollY;
3906
3907 if (wineItem->rect.top < 0)
3908 scrollY = wineItem->rect.top;
3909 else
3910 {
3911 scrollY = MIN(wineItem->rect.top,infoPtr->uTotalHeight-infoPtr->uVisibleHeight);
3912 if (scrollY < 0) scrollY = 0;
3913 }
3914
3915 if (scrollY != 0)
3916 {
3917 infoPtr->cy += scrollY;
3918 if (!TREEVIEW_UnqueueRefresh(hwnd,TRUE,TRUE))
3919 TREEVIEW_CalcItems(hwnd,0,infoPtr);
3920
3921 ScrollWindowEx(hwnd,0,-scrollY,NULL,NULL,0,NULL,SW_INVALIDATE | SW_SCROLLCHILDREN | (infoPtr->uScrollTime << 16));
3922 }
3923
3924 break;
3925 }
3926 }
3927
3928 return TRUE;
3929}
3930
3931static LRESULT
3932TREEVIEW_SelectItem (HWND hwnd, WPARAM wParam, LPARAM lParam)
3933{
3934 return TREEVIEW_DoSelectItem (hwnd, wParam, (HTREEITEM) lParam, TVC_UNKNOWN);
3935}
3936
3937static LRESULT
3938TREEVIEW_GetFont (HWND hwnd, WPARAM wParam, LPARAM lParam)
3939{
3940 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
3941
3942// TRACE (treeview,"%x\n",infoPtr->hFont);
3943 return infoPtr->hFont;
3944}
3945
3946static LRESULT
3947TREEVIEW_SetFont (HWND hwnd, WPARAM wParam, LPARAM lParam)
3948{
3949 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
3950 TEXTMETRICA tm;
3951 LOGFONTA logFont;
3952 HFONT hFont, hOldFont;
3953 INT height;
3954 HDC hdc;
3955
3956 infoPtr->hFont = (HFONT)wParam;
3957
3958 hFont = infoPtr->hFont ? infoPtr->hFont : GetStockObject (SYSTEM_FONT);
3959
3960 GetObjectA (infoPtr->hFont, sizeof (LOGFONTA), &logFont);
3961 logFont.lfWeight=FW_BOLD;
3962 DeleteObject(infoPtr->hBoldFont);
3963 infoPtr->hBoldFont = CreateFontIndirectA (&logFont);
3964
3965 hdc = GetDC (0);
3966 hOldFont = SelectObject (hdc, hFont);
3967 GetTextMetricsA (hdc, &tm);
3968 height= tm.tmHeight + tm.tmExternalLeading;
3969 if (height>infoPtr->uRealItemHeight)
3970 infoPtr->uRealItemHeight=height;
3971 SelectObject (hdc, hOldFont);
3972 ReleaseDC (0, hdc);
3973
3974 if (lParam)
3975 {
3976 TREEVIEW_UnqueueRefresh(hwnd,FALSE,FALSE);
3977 infoPtr->uInternalStatus |= TV_CALCALL;
3978 TREEVIEW_CalcItems(hwnd,0,infoPtr);
3979 TREEVIEW_Refresh(hwnd);
3980 }
3981
3982 return 0;
3983}
3984
3985
3986static LRESULT
3987TREEVIEW_VScroll (HWND hwnd, WPARAM wParam, LPARAM lParam)
3988{
3989 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
3990 int maxHeight;
3991 int lastPos = infoPtr->cy;
3992
3993 if (!(infoPtr->uInternalStatus & TV_VSCROLL)) return FALSE;
3994
3995 switch (LOWORD (wParam))
3996 {
3997 case SB_LINEUP:
3998 if (!infoPtr->cy) return FALSE;
3999 infoPtr->cy -= infoPtr->uRealItemHeight;
4000 if (infoPtr->cy < 0) infoPtr->cy = 0;
4001 break;
4002
4003 case SB_LINEDOWN:
4004 maxHeight = infoPtr->uTotalHeight-infoPtr->uVisibleHeight;
4005 if (maxHeight <= 0) return FALSE;
4006 if (infoPtr->cy == maxHeight) return FALSE;
4007 infoPtr->cy += infoPtr->uRealItemHeight;
4008 if (infoPtr->cy > maxHeight)
4009 infoPtr->cy = maxHeight;
4010 break;
4011
4012 case SB_PAGEUP:
4013 if (!infoPtr->cy) return FALSE;
4014 infoPtr->cy -= infoPtr->uVisibleHeight;
4015 if (infoPtr->cy < 0) infoPtr->cy = 0;
4016 break;
4017
4018 case SB_PAGEDOWN:
4019 maxHeight = infoPtr->uTotalHeight-infoPtr->uVisibleHeight;
4020 if (maxHeight <= 0) return FALSE;
4021 if (infoPtr->cy == maxHeight) return FALSE;
4022 infoPtr->cy += infoPtr->uVisibleHeight;
4023 if (infoPtr->cy > maxHeight)
4024 infoPtr->cy = maxHeight;
4025 break;
4026
4027 case SB_THUMBTRACK:
4028 maxHeight = infoPtr->uTotalHeight-infoPtr->uVisibleHeight;
4029 if (maxHeight <= 0) return FALSE;
4030 infoPtr->cy = HIWORD(wParam);
4031 if (infoPtr->cy < 0) infoPtr->cy = 0;
4032 if (infoPtr->cy > maxHeight) infoPtr->cy = maxHeight;
4033 break;
4034
4035 default:
4036 return FALSE;
4037 }
4038
4039 if (lastPos != infoPtr->cy)
4040 {
4041 if (!TREEVIEW_UnqueueRefresh(hwnd,TRUE,TRUE))
4042 TREEVIEW_CalcItems(hwnd,0,infoPtr);
4043 ScrollWindowEx(hwnd,0,lastPos-infoPtr->cy,NULL,NULL,0,NULL,SW_INVALIDATE | SW_SCROLLCHILDREN | (infoPtr->uScrollTime << 16));
4044
4045 return TRUE;
4046 }
4047
4048 return FALSE;
4049}
4050
4051static LRESULT
4052TREEVIEW_HScroll (HWND hwnd, WPARAM wParam, LPARAM lParam)
4053{
4054 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
4055 int maxWidth;
4056 int lastPos = infoPtr->cx;
4057
4058 if (!(infoPtr->uInternalStatus & TV_HSCROLL)) return FALSE;
4059
4060 switch (LOWORD (wParam))
4061 {
4062 case SB_LINEUP:
4063 if (!infoPtr->cx) return FALSE;
4064 infoPtr->cx -= infoPtr->uRealItemHeight;
4065 if (infoPtr->cx < 0) infoPtr->cx = 0;
4066 break;
4067
4068 case SB_LINEDOWN:
4069 maxWidth = infoPtr->uTotalWidth-infoPtr->uVisibleWidth;
4070 if (maxWidth <= 0) return FALSE;
4071 if (infoPtr->cx == maxWidth) return FALSE;
4072 infoPtr->cx += infoPtr->uRealItemHeight; /*FIXME */
4073 if (infoPtr->cx > maxWidth)
4074 infoPtr->cx = maxWidth;
4075 break;
4076
4077 case SB_PAGEUP:
4078 if (!infoPtr->cx) return FALSE;
4079 infoPtr->cx -= infoPtr->uVisibleWidth;
4080 if (infoPtr->cx < 0) infoPtr->cx = 0;
4081 break;
4082
4083 case SB_PAGEDOWN:
4084 maxWidth = infoPtr->uTotalWidth-infoPtr->uVisibleWidth;
4085 if (maxWidth <= 0) return FALSE;
4086 if (infoPtr->cx == maxWidth) return FALSE;
4087 infoPtr->cx += infoPtr->uVisibleWidth;
4088 if (infoPtr->cx > maxWidth)
4089 infoPtr->cx = maxWidth;
4090 break;
4091
4092 case SB_THUMBTRACK:
4093 maxWidth = infoPtr->uTotalWidth-infoPtr->uVisibleWidth;
4094 if (maxWidth <= 0) return FALSE;
4095 infoPtr->cx = HIWORD(wParam);
4096 if (infoPtr->cx < 0) infoPtr->cx = 0;
4097 if (infoPtr->cx > maxWidth) infoPtr->cx = maxWidth;
4098 break;
4099
4100 default:
4101 return FALSE;
4102 }
4103
4104 if (lastPos != infoPtr->cx)
4105 {
4106 if (!TREEVIEW_UnqueueRefresh(hwnd,TRUE,TRUE))
4107 TREEVIEW_CalcItems(hwnd,0,infoPtr);
4108 ScrollWindowEx(hwnd,lastPos-infoPtr->cx,0,NULL,NULL,0,NULL,SW_INVALIDATE | SW_SCROLLCHILDREN | (infoPtr->uScrollTime << 16));
4109
4110 return TRUE;
4111 }
4112
4113 return FALSE;
4114}
4115
4116static LRESULT TREEVIEW_MouseWheel (HWND hwnd, WPARAM wParam, LPARAM lParam)
4117{
4118
4119 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
4120 short gcWheelDelta = 0;
4121 UINT pulScrollLines = 3;
4122
4123 if (wParam & (MK_SHIFT | MK_CONTROL))
4124 return DefWindowProcA( hwnd, WM_MOUSEWHEEL, wParam, lParam );
4125
4126
4127 SystemParametersInfoW(SPI_GETWHEELSCROLLLINES,0, &pulScrollLines, 0);
4128
4129 gcWheelDelta -= (short) HIWORD(wParam);
4130 pulScrollLines *= (gcWheelDelta / WHEEL_DELTA);
4131
4132 if (abs(gcWheelDelta) >= WHEEL_DELTA && pulScrollLines)
4133 {
4134 int wheelDy = pulScrollLines * infoPtr->uRealItemHeight;
4135 int newDy = infoPtr->cy + wheelDy;
4136 int maxDy = infoPtr->uTotalHeight - infoPtr->uVisibleHeight;
4137
4138 if (newDy > maxDy) newDy = maxDy;
4139 if (newDy < 0) newDy = 0;
4140
4141 if (newDy == infoPtr->cy) return TRUE;
4142
4143 TREEVIEW_VScroll(hwnd, MAKEWPARAM(SB_THUMBTRACK,newDy),0);
4144 }
4145 return TRUE;
4146}
4147
4148static LRESULT
4149TREEVIEW_KeyDown (HWND hwnd, WPARAM wParam, LPARAM lParam)
4150{
4151 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
4152 HTREEITEM hNewSelection = 0;
4153 INT prevSelect = (INT)infoPtr->selectedItem;
4154 TREEVIEW_ITEM *prevItem =
4155 (prevSelect != 0 ) ?
4156 TREEVIEW_ValidItem (infoPtr, (HTREEITEM)prevSelect) :
4157 NULL;
4158 TREEVIEW_ITEM *newItem = NULL;
4159 BOOL control;
4160
4161 TREEVIEW_SendKeyDownNotify(hwnd,TVN_KEYDOWN,wParam);
4162
4163 control = GetKeyState(VK_CONTROL) & 0x8000;
4164
4165 if (control)
4166 {
4167 DWORD scrollMsg = 0;
4168 BOOL horz = FALSE;
4169
4170 switch (wParam)
4171 {
4172 case VK_UP:
4173//CB: todo: VK_UP doesn't work
4174 scrollMsg = SB_LINELEFT;
4175 break;
4176
4177 case VK_DOWN:
4178 scrollMsg = SB_LINERIGHT;
4179 break;
4180
4181 case VK_LEFT:
4182 scrollMsg = SB_LINELEFT;
4183 horz = TRUE;
4184 break;
4185
4186 case VK_RIGHT:
4187 scrollMsg = SB_LINERIGHT;
4188 horz = TRUE;
4189 break;
4190
4191 case VK_PRIOR:
4192 scrollMsg = SB_PAGELEFT;
4193 break;
4194
4195 case VK_NEXT:
4196 scrollMsg = SB_PAGERIGHT;
4197 break;
4198 }
4199 if (scrollMsg)
4200 {
4201 SendMessageA(hwnd,horz ? WM_HSCROLL:WM_VSCROLL,MAKELONG(scrollMsg,0),0);
4202 return TRUE;
4203 }
4204 }
4205
4206 if (prevSelect == 0)
4207 return FALSE;
4208
4209 switch (wParam)
4210 {
4211 case VK_UP:
4212 newItem = TREEVIEW_GetPrevListItem(hwnd,infoPtr, prevItem);
4213
4214 if (!newItem)
4215 newItem = &infoPtr->items[(INT)infoPtr->TopRootItem];
4216
4217 hNewSelection = newItem->hItem;
4218
4219 break;
4220
4221 case VK_DOWN:
4222 newItem = TREEVIEW_GetNextListItem (hwnd,infoPtr, prevItem);
4223
4224 if (!newItem)
4225 newItem = prevItem;
4226
4227 hNewSelection = newItem->hItem;
4228
4229 break;
4230
4231 case VK_HOME:
4232 newItem = &infoPtr->items[(INT)infoPtr->TopRootItem];
4233 hNewSelection = newItem->hItem;
4234
4235 break;
4236
4237 case VK_END:
4238 newItem = &infoPtr->items[(INT)infoPtr->TopRootItem];
4239 newItem = TREEVIEW_GetLastListItem (hwnd,infoPtr, newItem);
4240 hNewSelection = newItem->hItem;
4241
4242 break;
4243
4244 case VK_LEFT:
4245 if ( (prevItem->state & TVIS_EXPANDED) &&
4246 TREEVIEW_HasChildren(hwnd, prevItem))
4247 {
4248 TREEVIEW_Expand(hwnd, TVE_COLLAPSE, prevSelect );
4249 } else if ((INT)prevItem->parent)
4250 {
4251 newItem = (& infoPtr->items[(INT)prevItem->parent]);
4252
4253 hNewSelection = newItem->hItem;
4254 }
4255
4256 break;
4257
4258 case VK_RIGHT:
4259 if (TREEVIEW_HasChildren(hwnd, prevItem))
4260 {
4261 if (!(prevItem->state & TVIS_EXPANDED))
4262 TREEVIEW_Expand(hwnd, TVE_EXPAND, prevSelect );
4263 else
4264 {
4265 newItem = (& infoPtr->items[(INT)prevItem->firstChild]);
4266 hNewSelection = newItem->hItem;
4267 }
4268 }
4269
4270 break;
4271
4272 case VK_ADD:
4273 if (!(prevItem->state & TVIS_EXPANDED))
4274 TREEVIEW_Expand(hwnd, TVE_EXPAND, prevSelect );
4275 break;
4276
4277 case VK_SUBTRACT:
4278 if (prevItem->state & TVIS_EXPANDED)
4279 TREEVIEW_Expand(hwnd, TVE_COLLAPSE, prevSelect );
4280 break;
4281
4282 case VK_PRIOR:
4283 newItem=TREEVIEW_GetListItem(
4284 hwnd,
4285 infoPtr,
4286 prevItem,
4287 -1*(TREEVIEW_GetVisibleCount(hwnd,0,0)-3));
4288 if (!newItem)
4289 newItem = prevItem;
4290
4291 hNewSelection = newItem->hItem;
4292
4293 break;
4294
4295 case VK_NEXT:
4296 newItem=TREEVIEW_GetListItem(
4297 hwnd,
4298 infoPtr,
4299 prevItem,
4300 TREEVIEW_GetVisibleCount(hwnd,0,0)-3);
4301
4302 if (!newItem)
4303 newItem = prevItem;
4304
4305 hNewSelection = newItem->hItem;
4306
4307 break;
4308
4309 case VK_RETURN:
4310 TREEVIEW_SendSimpleNotify(hwnd,NM_RETURN);
4311 break;
4312
4313 case VK_BACK:
4314
4315 default:
4316 break;
4317 }
4318
4319 if (hNewSelection)
4320 {
4321 if ( TREEVIEW_DoSelectItem(
4322 hwnd,
4323 TVGN_CARET,
4324 (HTREEITEM)hNewSelection,
4325 TVC_BYKEYBOARD))
4326 {
4327 TREEVIEW_EnsureVisible(hwnd,hNewSelection);
4328 }
4329 }
4330
4331 return FALSE;
4332}
4333
4334static LRESULT
4335TREEVIEW_GetScrollTime (HWND hwnd)
4336{
4337 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
4338
4339 return infoPtr->uScrollTime;
4340}
4341
4342static LRESULT
4343TREEVIEW_SetScrollTime (HWND hwnd, UINT uScrollTime)
4344{
4345 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
4346 UINT uOldScrollTime = infoPtr->uScrollTime;
4347
4348 infoPtr->uScrollTime = min (uScrollTime, 100);
4349
4350 return uOldScrollTime;
4351}
4352
4353static LRESULT TREEVIEW_EnsureVisible(HWND hwnd,HTREEITEM hItem)
4354{
4355 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
4356 TREEVIEW_ITEM *item;
4357 RECT rect;
4358 INT scrollY;
4359
4360 item = TREEVIEW_ValidItem(infoPtr,hItem);
4361
4362 if (!item) return FALSE;
4363
4364 if (item && item->parent)
4365 {
4366 TREEVIEW_ITEM *parent = TREEVIEW_ValidItem(infoPtr,item->parent);
4367
4368 while (parent && !(parent->state & TVIS_EXPANDED))
4369 {
4370 TREEVIEW_Expand(hwnd,TVE_EXPAND,(LPARAM)parent->hItem);
4371 parent = TREEVIEW_ValidItem(infoPtr,parent->parent);
4372 }
4373 }
4374
4375 TREEVIEW_UnqueueRefresh(hwnd,TRUE,TRUE);
4376 GetClientRect(hwnd,&rect);
4377 if (item->rect.top < 0)
4378 scrollY = item->rect.top;
4379 else if (item->rect.bottom > rect.bottom)
4380 scrollY = item->rect.bottom-rect.bottom;
4381 else return FALSE;
4382
4383 if (scrollY != 0)
4384 {
4385 infoPtr->cy += scrollY;
4386 TREEVIEW_CalcItems(hwnd,0,infoPtr);
4387 ScrollWindowEx(hwnd,0,-scrollY,NULL,NULL,0,NULL,SW_INVALIDATE | SW_SCROLLCHILDREN | (infoPtr->uScrollTime << 16));
4388
4389 return TRUE;
4390 }
4391
4392 return FALSE;
4393}
4394
4395static LRESULT TREEVIEW_GetISearchString(HWND hwnd,LPSTR lpsz,BOOL unicode)
4396{
4397 //CB: todo: MSDN documentation is useless
4398
4399 return 0;
4400}
4401
4402static LRESULT TREEVIEW_SetCursor(HWND hwnd,WPARAM wParam,LPARAM lParam)
4403{
4404 TREEVIEW_SendSimpleNotify(hwnd,NM_SETCURSOR); //CB: todo: result
4405
4406 return DefWindowProcA(hwnd,WM_SETCURSOR,wParam,lParam);
4407}
4408
4409static LRESULT TREEVIEW_GetUnicodeFormat(HWND hwnd,WPARAM wParam,LPARAM lParam)
4410{
4411 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
4412
4413 return infoPtr->uInternalStatus & TV_UNICODE;
4414}
4415
4416static LRESULT TREEVIEW_SetUnicodeFormat(HWND hwnd,WPARAM wParam,LPARAM lParam)
4417{
4418 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
4419 BOOL oldFormat = infoPtr->uInternalStatus & TV_UNICODE;
4420
4421 if (wParam)
4422 infoPtr->uInternalStatus |= TV_UNICODE;
4423 else
4424 infoPtr->uInternalStatus &= ~TV_UNICODE;
4425
4426 return oldFormat;
4427}
4428
4429static LRESULT WINAPI
4430TREEVIEW_WindowProc (HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
4431{
4432 if (uMsg==WM_CREATE)
4433 return TREEVIEW_Create (hwnd, wParam, lParam);
4434
4435 if (!TREEVIEW_GetInfoPtr(hwnd))
4436 return DefWindowProcA (hwnd, uMsg, wParam, lParam);
4437
4438 switch (uMsg) {
4439 case TVM_INSERTITEMA:
4440 return TREEVIEW_InsertItem(hwnd,wParam,lParam,FALSE);
4441
4442 case TVM_INSERTITEMW:
4443 return TREEVIEW_InsertItem(hwnd,wParam,lParam,TRUE);
4444
4445 case TVM_DELETEITEM:
4446 return TREEVIEW_DeleteItem (hwnd, wParam, lParam);
4447
4448 case TVM_EXPAND:
4449 return TREEVIEW_Expand (hwnd, wParam, lParam);
4450
4451 case TVM_GETITEMRECT:
4452 return TREEVIEW_GetItemRect (hwnd, wParam, lParam);
4453
4454 case TVM_GETCOUNT:
4455 return TREEVIEW_GetCount (hwnd, wParam, lParam);
4456
4457 case TVM_GETINDENT:
4458 return TREEVIEW_GetIndent (hwnd);
4459
4460 case TVM_SETINDENT:
4461 return TREEVIEW_SetIndent (hwnd, wParam);
4462
4463 case TVM_GETIMAGELIST:
4464 return TREEVIEW_GetImageList (hwnd, wParam, lParam);
4465
4466 case TVM_SETIMAGELIST:
4467 return TREEVIEW_SetImageList (hwnd, wParam, lParam);
4468
4469 case TVM_GETNEXTITEM:
4470 return TREEVIEW_GetNextItem (hwnd, wParam, lParam);
4471
4472 case TVM_SELECTITEM:
4473 return TREEVIEW_SelectItem (hwnd, wParam, lParam);
4474
4475 case TVM_GETITEMA:
4476 return TREEVIEW_GetItem(hwnd,wParam,lParam,FALSE);
4477
4478 case TVM_GETITEMW:
4479 return TREEVIEW_GetItem(hwnd,wParam,lParam,TRUE);
4480
4481 case TVM_SETITEMA:
4482 return TREEVIEW_SetItem(hwnd,wParam,lParam,FALSE);
4483
4484 case TVM_SETITEMW:
4485 return TREEVIEW_SetItem(hwnd,wParam,lParam,TRUE);
4486
4487 case TVM_EDITLABELA:
4488 return TREEVIEW_EditLabel(hwnd,(HTREEITEM)lParam,FALSE);
4489
4490 case TVM_EDITLABELW:
4491 return TREEVIEW_EditLabel(hwnd,(HTREEITEM)lParam,TRUE);
4492
4493 case TVM_GETEDITCONTROL:
4494 return TREEVIEW_GetEditControl (hwnd);
4495
4496 case TVM_GETVISIBLECOUNT:
4497 return TREEVIEW_GetVisibleCount (hwnd, wParam, lParam);
4498
4499 case TVM_HITTEST:
4500 return TREEVIEW_HitTest(hwnd,(LPTVHITTESTINFO)lParam);
4501
4502 case TVM_CREATEDRAGIMAGE:
4503 return TREEVIEW_CreateDragImage (hwnd, wParam, lParam);
4504
4505 case TVM_SORTCHILDREN:
4506 //@@@PH 1999/10/25 TREEVIEW_SortChildrenCB is wrong
4507 return TREEVIEW_SortChildren(hwnd, wParam, lParam);
4508
4509 case TVM_ENSUREVISIBLE:
4510 return TREEVIEW_EnsureVisible(hwnd,(HTREEITEM)lParam);
4511
4512 case TVM_SORTCHILDRENCB:
4513 return TREEVIEW_SortChildrenCB(hwnd, wParam, lParam);
4514
4515 case TVM_ENDEDITLABELNOW:
4516 return TREEVIEW_EndEditLabelNow (hwnd,(BOOL)wParam);
4517
4518 case TVM_GETISEARCHSTRINGA:
4519 return TREEVIEW_GetISearchString(hwnd,(LPSTR)lParam,FALSE);
4520
4521 case TVM_GETISEARCHSTRINGW:
4522 return TREEVIEW_GetISearchString(hwnd,(LPSTR)lParam,TRUE);
4523
4524 case TVM_GETTOOLTIPS:
4525 return TREEVIEW_GetToolTips (hwnd);
4526
4527 case TVM_SETTOOLTIPS:
4528 return TREEVIEW_SetToolTips (hwnd, wParam);
4529
4530 case TVM_SETINSERTMARK:
4531 return TREEVIEW_SetInsertMark (hwnd,wParam, lParam);
4532
4533 case TVM_SETITEMHEIGHT:
4534 return TREEVIEW_SetItemHeight (hwnd, wParam);
4535
4536 case TVM_GETITEMHEIGHT:
4537 return TREEVIEW_GetItemHeight (hwnd);
4538
4539 case TVM_SETBKCOLOR:
4540 return TREEVIEW_SetBkColor (hwnd, wParam, lParam);
4541
4542 case TVM_SETTEXTCOLOR:
4543 return TREEVIEW_SetTextColor (hwnd, wParam, lParam);
4544
4545 case TVM_GETBKCOLOR:
4546 return TREEVIEW_GetBkColor (hwnd);
4547
4548 case TVM_GETTEXTCOLOR:
4549 return TREEVIEW_GetTextColor (hwnd);
4550
4551 case TVM_SETSCROLLTIME:
4552 return TREEVIEW_SetScrollTime (hwnd, (UINT)wParam);
4553
4554 case TVM_GETSCROLLTIME:
4555 return TREEVIEW_GetScrollTime (hwnd);
4556
4557 case TVM_GETITEMSTATE:
4558 return TREEVIEW_GetItemState (hwnd,wParam, lParam);
4559
4560 case TVM_GETLINECOLOR:
4561 return TREEVIEW_GetLineColor (hwnd,wParam, lParam);
4562
4563 case TVM_SETLINECOLOR:
4564 return TREEVIEW_SetLineColor (hwnd,wParam, lParam);
4565
4566 case TVM_SETINSERTMARKCOLOR:
4567 return TREEVIEW_SetInsertMarkColor (hwnd,wParam, lParam);
4568
4569 case TVM_GETINSERTMARKCOLOR:
4570 return TREEVIEW_GetInsertMarkColor (hwnd,wParam, lParam);
4571
4572 case TVM_GETUNICODEFORMAT:
4573 return TREEVIEW_GetUnicodeFormat(hwnd,wParam,lParam);
4574
4575 case TVM_SETUNICODEFORMAT:
4576 return TREEVIEW_SetUnicodeFormat(hwnd,wParam,lParam);
4577
4578 case WM_COMMAND:
4579 return TREEVIEW_Command (hwnd, wParam, lParam);
4580
4581 case WM_DESTROY:
4582 return TREEVIEW_Destroy (hwnd);
4583
4584 case WM_ENABLE:
4585 return TREEVIEW_Enable(hwnd,wParam,lParam);
4586
4587 case WM_ERASEBKGND:
4588 return TREEVIEW_EraseBackground (hwnd, wParam, lParam);
4589
4590 case WM_GETDLGCODE:
4591 return TREEVIEW_GetDlgCode(hwnd,wParam,lParam);
4592
4593 case WM_PAINT:
4594 return TREEVIEW_Paint (hwnd, wParam, lParam);
4595
4596 case WM_GETFONT:
4597 return TREEVIEW_GetFont (hwnd, wParam, lParam);
4598
4599 case WM_SETFONT:
4600 return TREEVIEW_SetFont (hwnd, wParam, lParam);
4601
4602 case WM_KEYDOWN:
4603 return TREEVIEW_KeyDown (hwnd, wParam, lParam);
4604
4605 case WM_SETFOCUS:
4606 return TREEVIEW_SetFocus (hwnd, wParam, lParam);
4607
4608 case WM_KILLFOCUS:
4609 return TREEVIEW_KillFocus (hwnd, wParam, lParam);
4610
4611 case WM_MOUSEMOVE:
4612 return TREEVIEW_MouseMove(hwnd,wParam,lParam);
4613
4614 case WM_LBUTTONDOWN:
4615 return TREEVIEW_LButtonDown (hwnd, wParam, lParam);
4616
4617 case WM_LBUTTONDBLCLK:
4618 return TREEVIEW_LButtonDoubleClick (hwnd, wParam, lParam);
4619
4620 case WM_RBUTTONDOWN:
4621 return TREEVIEW_RButtonDown (hwnd, wParam, lParam);
4622
4623 case WM_RBUTTONDBLCLK:
4624 return TREEVIEW_RButtonDoubleClick(hwnd,wParam,lParam);
4625
4626 case WM_STYLECHANGED:
4627 return TREEVIEW_StyleChanged (hwnd, wParam, lParam);
4628
4629 case WM_SYSCOLORCHANGE:
4630 return TREEVIEW_SysColorChange(hwnd,wParam,lParam);
4631
4632 case WM_SETCURSOR:
4633 return TREEVIEW_SetCursor(hwnd,wParam,lParam);
4634
4635 case WM_SETREDRAW:
4636 return TREEVIEW_SetRedraw(hwnd,wParam,lParam);
4637
4638 case WM_TIMER:
4639 return TREEVIEW_HandleTimer (hwnd, wParam, lParam);
4640
4641 case WM_SIZE:
4642 return TREEVIEW_Size (hwnd, wParam,lParam);
4643
4644 case WM_HSCROLL:
4645 return TREEVIEW_HScroll (hwnd, wParam, lParam);
4646
4647 case WM_VSCROLL:
4648 return TREEVIEW_VScroll (hwnd, wParam, lParam);
4649
4650 case WM_MOUSEWHEEL:
4651 return TREEVIEW_MouseWheel (hwnd, wParam, lParam);
4652
4653 case WM_DRAWITEM:
4654 //printf ("drawItem\n");
4655 return DefWindowProcA (hwnd, uMsg, wParam, lParam);
4656
4657 default:
4658 //if (uMsg >= WM_USER)
4659 // FIXME (treeview, "Unknown msg %04x wp=%08x lp=%08lx\n",
4660 // uMsg, wParam, lParam);
4661 return DefWindowProcA (hwnd, uMsg, wParam, lParam);
4662 }
4663 return 0;
4664}
4665
4666
4667VOID TREEVIEW_Register (VOID)
4668{
4669 WNDCLASSA wndClass;
4670
4671 ZeroMemory (&wndClass, sizeof(WNDCLASSA));
4672 wndClass.style = CS_GLOBALCLASS | CS_DBLCLKS;
4673 wndClass.lpfnWndProc = (WNDPROC)TREEVIEW_WindowProc;
4674 wndClass.cbClsExtra = 0;
4675 wndClass.cbWndExtra = sizeof(TREEVIEW_INFO *);
4676 wndClass.hCursor = LoadCursorA (0, IDC_ARROWA);
4677 wndClass.hbrBackground = 0;
4678 wndClass.lpszClassName = WC_TREEVIEWA;
4679
4680 RegisterClassA (&wndClass);
4681}
4682
4683
4684VOID TREEVIEW_Unregister (VOID)
4685{
4686 UnregisterClassA (WC_TREEVIEWA, (HINSTANCE)NULL);
4687}
4688
Note: See TracBrowser for help on using the repository browser.