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

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

* empty log message *

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