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

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

treeview item height fix

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