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

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

merged comctl32.c and treeview with corel 20000212 + my newest code

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