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

Last change on this file since 2895 was 2895, checked in by cbratschi, 25 years ago

unicode and other changes

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