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

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

listview: ver 4 feature complete

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