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

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

treeview info tip

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