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

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

* empty log message *

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