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

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

Corel 20000212, TREEVIEW_Sort fix, CCBase

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