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

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

* empty log message *

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