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

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

treeview: finished soon

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