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

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

Corel 20000317 merge, ccbase finished, bug fixes

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