source: trunk/src/comctl32/treeview.c@ 2835

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

* empty log message *

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