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

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

bug fixes and improvements

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