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

Last change on this file since 1793 was 1793, checked in by achimha, 26 years ago

undo LoadMessageA/W workaround

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