source: trunk/src/comctl32/treeview.cpp@ 6705

Last change on this file since 6705 was 6705, checked in by sandervl, 24 years ago

update

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