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

Last change on this file since 2875 was 2875, checked in by cbratschi, 26 years ago

C -> C++, WINE animate, treeview WM_VSCROLL fixed

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