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

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

MoveTo fixes

File size: 106.4 KB
Line 
1/* $Id: treeview.c,v 1.15 1999-11-09 17:09:27 cbratschi Exp $ */
2/* Treeview control
3 *
4 * Copyright 1998 Eric Kohl <ekohl@abo.rhein-zeitung.de>
5 * Copyright 1998,1999 Alex Priem <alexp@sci.kun.nl>
6 * Copyright 1999 Sylvain St-Germain
7 * Copyright 1999 Achim Hasenmueller
8 * Copyright 1999 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 991031 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 return TRUE;
930}
931
932static LRESULT
933TREEVIEW_GetItemState (HWND hwnd, WPARAM wParam, LPARAM lParam)
934
935{
936 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
937 TREEVIEW_ITEM *wineItem;
938
939 wineItem = TREEVIEW_ValidItem (infoPtr, (HTREEITEM)wParam);
940 if (!wineItem) return 0;
941
942 return (wineItem->state & lParam);
943}
944
945static void
946TREEVIEW_Refresh (HWND hwnd)
947
948{
949 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
950 TEXTMETRICA tm;
951 HBRUSH hbrBk;
952 RECT rect;
953 HDC hdc;
954 INT iItem, indent, x, y, cx, height, itemHeight;
955 INT viewtop,viewbottom,viewleft,viewright;
956 TREEVIEW_ITEM *wineItem, *prevItem;
957
958 //TRACE("\n");
959
960 hdc=GetDC (hwnd);
961
962 if (infoPtr->Timer & TV_REFRESH_TIMER_SET) {
963 KillTimer (hwnd, TV_REFRESH_TIMER);
964 infoPtr->Timer &= ~TV_REFRESH_TIMER_SET;
965 }
966
967
968 GetClientRect (hwnd, &rect);
969 if ((rect.left-rect.right ==0) || (rect.top-rect.bottom==0)) return;
970
971 infoPtr->cdmode=TREEVIEW_SendCustomDrawNotify
972 (hwnd, CDDS_PREPAINT, hdc, rect);
973
974 if (infoPtr->cdmode==CDRF_SKIPDEFAULT) {
975 ReleaseDC (hwnd, hdc);
976 return;
977 }
978
979 infoPtr->uVisibleHeight= rect.bottom-rect.top;
980 infoPtr->uVisibleWidth= rect.right-rect.left;
981
982 viewtop=infoPtr->cy;
983 viewbottom=infoPtr->cy + rect.bottom-rect.top;
984 viewleft=infoPtr->cx;
985 viewright=infoPtr->cx + rect.right-rect.left;
986
987 /* draw background */
988
989 hbrBk = CreateSolidBrush (infoPtr->clrBk);
990 FillRect(hdc, &rect, hbrBk);
991 DeleteObject(hbrBk);
992
993 iItem=(INT)infoPtr->TopRootItem;
994 infoPtr->firstVisible=0;
995 wineItem=NULL;
996 indent=0;
997 x=y=0;
998 //TRACE("[%d %d %d %d]\n",viewtop,viewbottom,viewleft,viewright);
999
1000 while (iItem) {
1001 prevItem=wineItem;
1002 wineItem= & infoPtr->items[iItem];
1003 wineItem->iLevel=indent;
1004
1005 ImageList_GetIconSize (infoPtr->himlNormal, &cx, &itemHeight);
1006 if (infoPtr->uItemHeight>itemHeight)
1007 itemHeight=infoPtr->uItemHeight;
1008
1009 GetTextMetricsA (hdc, &tm);
1010 if ((tm.tmHeight + tm.tmExternalLeading) > itemHeight)
1011 itemHeight=tm.tmHeight + tm.tmExternalLeading;
1012
1013 infoPtr->uRealItemHeight=itemHeight;
1014
1015
1016/* FIXME: remove this in later stage */
1017/*
1018 if (wineItem->pszText!=LPSTR_TEXTCALLBACK32A)
1019 TRACE (treeview, "%d %d [%d %d %d %d] (%s)\n",y,x,
1020 wineItem->rect.top, wineItem->rect.bottom,
1021 wineItem->rect.left, wineItem->rect.right,
1022 wineItem->pszText);
1023 else
1024 TRACE (treeview, "%d [%d %d %d %d] (CALLBACK)\n",
1025 wineItem->hItem,
1026 wineItem->rect.top, wineItem->rect.bottom,
1027 wineItem->rect.left, wineItem->rect.right);
1028*/
1029
1030 height=itemHeight * wineItem->iIntegral +1;
1031 if ((y >= viewtop) && (y <= viewbottom) &&
1032 (x >= viewleft ) && (x <= viewright)) {
1033 wineItem->visible = TRUE;
1034 wineItem->rect.top = y - infoPtr->cy + rect.top;
1035 wineItem->rect.bottom = wineItem->rect.top + height ;
1036 wineItem->rect.left = x - infoPtr->cx + rect.left;
1037 wineItem->rect.right = rect.right;
1038 if (!infoPtr->firstVisible)
1039 infoPtr->firstVisible=wineItem->hItem;
1040 TREEVIEW_DrawItem (hwnd, hdc, wineItem);
1041 }
1042 else {
1043 wineItem->visible = FALSE;
1044 wineItem->rect.left = wineItem->rect.top = 0;
1045 wineItem->rect.right= wineItem->rect.bottom = 0;
1046 wineItem->text.left = wineItem->text.top = 0;
1047 wineItem->text.right= wineItem->text.bottom = 0;
1048 }
1049
1050 /* look up next item */
1051
1052 if ((wineItem->firstChild) && (wineItem->state & TVIS_EXPANDED)) {
1053 iItem=(INT)wineItem->firstChild;
1054 indent++;
1055 x+=infoPtr->uIndent;
1056 if (x>infoPtr->uTotalWidth)
1057 infoPtr->uTotalWidth=x;
1058 }
1059 else {
1060 iItem=(INT)wineItem->sibling;
1061 while ((!iItem) && (indent>0)) {
1062 indent--;
1063 x-=infoPtr->uIndent;
1064 prevItem=wineItem;
1065 wineItem=&infoPtr->items[(INT)wineItem->parent];
1066 iItem=(INT)wineItem->sibling;
1067 }
1068 }
1069 y +=height;
1070 } /* while */
1071
1072/* FIXME: infoPtr->uTotalWidth should also take item label into account */
1073/* FIXME: or should query item sizes (ie check CDRF_NEWFONT) */
1074
1075 infoPtr->uTotalHeight=y;
1076 if (y >= (viewbottom-viewtop)) {
1077 if (!(infoPtr->uInternalStatus & TV_VSCROLL))
1078 ShowScrollBar (hwnd, SB_VERT, TRUE);
1079 infoPtr->uInternalStatus |=TV_VSCROLL;
1080 SetScrollRange (hwnd, SB_VERT, 0,
1081 y - infoPtr->uVisibleHeight, FALSE);
1082 SetScrollPos (hwnd, SB_VERT, infoPtr->cy, TRUE);
1083 }
1084 else {
1085 if (infoPtr->uInternalStatus & TV_VSCROLL)
1086 ShowScrollBar (hwnd, SB_VERT, FALSE);
1087 infoPtr->uInternalStatus &= ~TV_VSCROLL;
1088 }
1089
1090
1091 if (infoPtr->cdmode & CDRF_NOTIFYPOSTPAINT)
1092 infoPtr->cdmode=TREEVIEW_SendCustomDrawNotify
1093 (hwnd, CDDS_POSTPAINT, hdc, rect);
1094
1095 ReleaseDC (hwnd, hdc);
1096 //TRACE("done\n");
1097}
1098
1099
1100static LRESULT
1101TREEVIEW_HandleTimer (HWND hwnd, WPARAM wParam, LPARAM lParam)
1102{
1103 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
1104
1105// TRACE (treeview, " %d\n",wParam);
1106 if (!infoPtr) return FALSE;
1107
1108 switch (wParam) {
1109 case TV_REFRESH_TIMER:
1110 KillTimer (hwnd, TV_REFRESH_TIMER);
1111 infoPtr->Timer &= ~TV_REFRESH_TIMER_SET;
1112 InvalidateRect(hwnd, NULL, FALSE);
1113 return 0;
1114 case TV_EDIT_TIMER:
1115 KillTimer (hwnd, TV_EDIT_TIMER);
1116 infoPtr->Timer &= ~TV_EDIT_TIMER_SET;
1117 return 0;
1118 default:
1119// ERR (treeview,"got unknown timer\n");
1120 break;
1121 }
1122
1123 return 1;
1124}
1125
1126
1127static void
1128TREEVIEW_QueueRefresh (HWND hwnd)
1129
1130{
1131 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
1132
1133// TRACE (treeview,"\n");
1134 if (infoPtr->Timer & TV_REFRESH_TIMER_SET) {
1135 KillTimer (hwnd, TV_REFRESH_TIMER);
1136 }
1137
1138 SetTimer (hwnd, TV_REFRESH_TIMER, TV_REFRESH_DELAY, 0);
1139 infoPtr->Timer|=TV_REFRESH_TIMER_SET;
1140}
1141
1142
1143
1144static LRESULT
1145TREEVIEW_GetItemA (HWND hwnd, WPARAM wParam, LPARAM lParam)
1146{
1147 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
1148 LPTVITEMEXA tvItem;
1149 TREEVIEW_ITEM *wineItem;
1150 INT iItem;
1151
1152 tvItem=(LPTVITEMEXA) lParam;
1153 iItem=(INT)tvItem->hItem;
1154
1155 wineItem = TREEVIEW_ValidItem (infoPtr, (HTREEITEM)iItem);
1156 if (!wineItem) return FALSE;
1157
1158 if (tvItem->mask & TVIF_CHILDREN) {
1159// if (TVIF_CHILDREN==I_CHILDRENCALLBACK)
1160// FIXME (treeview,"I_CHILDRENCALLBACK not supported\n");
1161 tvItem->cChildren=wineItem->cChildren;
1162 }
1163
1164 if (tvItem->mask & TVIF_HANDLE) {
1165 tvItem->hItem=wineItem->hItem;
1166 }
1167
1168 if (tvItem->mask & TVIF_IMAGE) {
1169 tvItem->iImage=wineItem->iImage;
1170 }
1171
1172 if (tvItem->mask & TVIF_INTEGRAL) {
1173 tvItem->iIntegral=wineItem->iIntegral;
1174 }
1175
1176 // undocumented: windows ignores TVIF_PARAM and
1177 // always sets lParam
1178 tvItem->lParam=wineItem->lParam;
1179
1180 if (tvItem->mask & TVIF_SELECTEDIMAGE) {
1181 tvItem->iSelectedImage=wineItem->iSelectedImage;
1182 }
1183
1184 if (tvItem->mask & TVIF_STATE) {
1185 tvItem->state=wineItem->state & tvItem->stateMask;
1186 }
1187
1188 if (tvItem->mask & TVIF_TEXT) {
1189 if (wineItem->pszText == LPSTR_TEXTCALLBACKA) {
1190 tvItem->pszText = LPSTR_TEXTCALLBACKA; /* FIXME:send notification? */
1191// ERR (treeview," GetItem called with LPSTR_TEXTCALLBACK\n");
1192 }
1193 else if (wineItem->pszText) {
1194 lstrcpynA (tvItem->pszText, wineItem->pszText,tvItem->cchTextMax);
1195 }
1196 }
1197
1198// TRACE(treeview,"item %d<%p>, txt %p, img %p, action %x\n",
1199// iItem,
1200// tvItem,
1201// tvItem->pszText,
1202// & tvItem->iImage,
1203// tvItem->mask);
1204
1205 return TRUE;
1206}
1207
1208
1209
1210/* FIXME: check implementation of TVGN_NEXT/TVGN_NEXTVISIBLE */
1211
1212static LRESULT
1213TREEVIEW_GetNextItem (HWND hwnd, WPARAM wParam, LPARAM lParam)
1214
1215{
1216 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
1217 TREEVIEW_ITEM *wineItem, *returnItem;
1218 INT iItem, retval, flag;
1219
1220
1221 if (!infoPtr) return FALSE;
1222 flag = (INT) wParam;
1223 iItem = (INT) lParam;
1224 retval=0;
1225 switch (flag) {
1226 case TVGN_ROOT: retval=(INT)infoPtr->TopRootItem;
1227 break;
1228 case TVGN_CARET:retval=(INT)infoPtr->selectedItem;
1229 break;
1230 case TVGN_FIRSTVISIBLE:
1231 TREEVIEW_Refresh (hwnd);
1232/* FIXME:we should only recalculate, not redraw */
1233 retval=(INT)infoPtr->firstVisible;
1234 break;
1235 case TVGN_DROPHILITE:
1236 retval=(INT)infoPtr->dropItem;
1237 break;
1238 }
1239 if (retval) {
1240// TRACE (treeview,"flags:%x, returns %u\n", flag, retval);
1241 return retval;
1242 }
1243
1244 wineItem = TREEVIEW_ValidItem (infoPtr, (HTREEITEM)iItem);
1245 returnItem = NULL;
1246 if (!wineItem) return FALSE;
1247
1248 switch (flag) {
1249 case TVGN_NEXT: retval=(INT)wineItem->sibling;
1250 break;
1251 case TVGN_PREVIOUS:
1252 retval=(INT)wineItem->upsibling;
1253 break;
1254 case TVGN_PARENT:
1255 retval=(INT)wineItem->parent;
1256 break;
1257 case TVGN_CHILD:
1258 retval=(INT)wineItem->firstChild;
1259 break;
1260 case TVGN_LASTVISIBLE:
1261 returnItem=TREEVIEW_GetLastListItem (infoPtr,wineItem);
1262 break;
1263 case TVGN_NEXTVISIBLE:
1264 returnItem=TREEVIEW_GetNextListItem (infoPtr,wineItem);
1265 break;
1266 case TVGN_PREVIOUSVISIBLE:
1267 returnItem=TREEVIEW_GetPrevListItem (infoPtr, wineItem);
1268 break;
1269 default: // FIXME (treeview,"Unknown msg %x,item %x\n", flag,iItem);
1270 break;
1271 }
1272
1273 if (returnItem) {
1274// TRACE (treeview,"flags:%x, item %d;returns %d\n", flag, iItem,
1275// (INT)returnItem->hItem);
1276 return (INT)returnItem->hItem;
1277 }
1278
1279// TRACE (treeview,"flags:%x, item %d;returns %d\n", flag, iItem,retval);
1280 return retval;
1281}
1282
1283
1284static LRESULT
1285TREEVIEW_GetCount (HWND hwnd, WPARAM wParam, LPARAM lParam)
1286{
1287 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
1288
1289// TRACE (treeview," %d\n",infoPtr->uNumItems);
1290 return (LRESULT) infoPtr->uNumItems;
1291}
1292
1293/***************************************************************************
1294 * This method does the chaining of the insertion of a treeview item
1295 * before an item.
1296 * If parent is NULL, we're inserting at the root of the list.
1297 */
1298static void TREEVIEW_InsertBefore(
1299 TREEVIEW_INFO *infoPtr,
1300 TREEVIEW_ITEM *newItem,
1301 TREEVIEW_ITEM *sibling,
1302 TREEVIEW_ITEM *parent)
1303{
1304 HTREEITEM siblingHandle = 0;
1305 HTREEITEM upSiblingHandle = 0;
1306 TREEVIEW_ITEM *upSibling = NULL;
1307
1308// if (newItem == NULL)
1309// ERR(treeview, "NULL newItem, impossible condition\n");
1310
1311// if (parent == NULL)
1312// ERR(treeview, "NULL parent, impossible condition\n");
1313
1314 if (sibling != NULL) /* Insert before this sibling for this parent */
1315 {
1316 /* Store the new item sibling up sibling and sibling tem handle */
1317 siblingHandle = sibling->hItem;
1318 upSiblingHandle = sibling->upsibling;
1319 /* As well as a pointer to the upsibling sibling object */
1320 if ( (INT)sibling->upsibling != 0 )
1321 upSibling = &infoPtr->items[(INT)sibling->upsibling];
1322
1323 /* Adjust the sibling pointer */
1324 sibling->upsibling = newItem->hItem;
1325
1326 /* Adjust the new item pointers */
1327 newItem->upsibling = upSiblingHandle;
1328 newItem->sibling = siblingHandle;
1329
1330 /* Adjust the up sibling pointer */
1331 if ( upSibling != NULL )
1332 upSibling->sibling = newItem->hItem;
1333 else
1334 if (parent)
1335 /* this item is the first child of this parent, adjust parent pointers */
1336 parent->firstChild = newItem->hItem;
1337 else infoPtr->TopRootItem = newItem->hItem;
1338 }
1339 else /* Insert as first child of this parent */
1340 if (parent)
1341 parent->firstChild = newItem->hItem;
1342}
1343
1344/***************************************************************************
1345 * This method does the chaining of the insertion of a treeview item
1346 * If parent is NULL, we're inserting at the root of the list.
1347 * after an item.
1348 */
1349static void TREEVIEW_InsertAfter(
1350 TREEVIEW_INFO *infoPtr,
1351 TREEVIEW_ITEM *newItem,
1352 TREEVIEW_ITEM *upSibling,
1353 TREEVIEW_ITEM *parent)
1354{
1355 HTREEITEM upSiblingHandle = 0;
1356 HTREEITEM siblingHandle = 0;
1357 TREEVIEW_ITEM *sibling = NULL;
1358
1359// if (newItem == NULL)
1360// ERR(treeview, "NULL newItem, impossible condition\n");
1361
1362// if (parent == NULL)
1363// ERR(treeview, "NULL parent, impossible condition\n");
1364
1365 if (upSibling != NULL) /* Insert after this upsibling for this parent */
1366 {
1367 /* Store the new item up sibling and sibling item handle */
1368 upSiblingHandle = upSibling->hItem;
1369 siblingHandle = upSibling->sibling;
1370 /* As well as a pointer to the upsibling sibling object */
1371 if ( (INT)upSibling->sibling != 0 )
1372 sibling = &infoPtr->items[(INT)upSibling->sibling];
1373
1374 /* Adjust the up sibling pointer */
1375 upSibling->sibling = newItem->hItem;
1376
1377 /* Adjust the new item pointers */
1378 newItem->upsibling = upSiblingHandle;
1379 newItem->sibling = siblingHandle;
1380
1381 /* Adjust the sibling pointer */
1382 if ( sibling != NULL )
1383 sibling->upsibling = newItem->hItem;
1384 /*
1385 else
1386 newItem is the last of the level, nothing else to do
1387 */
1388 }
1389 else /* Insert as first child of this parent */
1390 if (parent)
1391 parent->firstChild = newItem->hItem;
1392}
1393
1394/***************************************************************************
1395 * Forward the DPA local callback to the treeview owner callback
1396 */
1397static INT WINAPI TREEVIEW_CallBackCompare(
1398 LPVOID first,
1399 LPVOID second,
1400 LPARAM tvInfoPtr)
1401{
1402 /* Forward the call to the client define callback */
1403 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr((HWND)tvInfoPtr);
1404 return (infoPtr->pCallBackSort->lpfnCompare)(
1405 ((TREEVIEW_ITEM*)first)->lParam,
1406 ((TREEVIEW_ITEM*)second)->lParam,
1407 infoPtr->pCallBackSort->lParam);
1408}
1409
1410/***************************************************************************
1411 * Treeview native sort routine: sort on item text.
1412 */
1413static INT WINAPI TREEVIEW_SortOnName (
1414 LPVOID first,
1415 LPVOID second,
1416 LPARAM tvInfoPtr)
1417{
1418 HWND hwnd=(HWND) tvInfoPtr;
1419 char *txt1, *txt2;
1420 TREEVIEW_ITEM *item;
1421
1422
1423 item=(TREEVIEW_ITEM *) first;
1424 if (item->pszText==LPSTR_TEXTCALLBACKA) {
1425 TREEVIEW_SendDispInfoNotify (hwnd, item, TVN_GETDISPINFO, TVIF_TEXT);
1426 }
1427 txt1=item->pszText;
1428
1429 item=(TREEVIEW_ITEM *) second;
1430 if (item->pszText==LPSTR_TEXTCALLBACKA) {
1431 TREEVIEW_SendDispInfoNotify (hwnd, item, TVN_GETDISPINFO, TVIF_TEXT);
1432 }
1433 txt2=item->pszText;
1434
1435 return -strcmp (txt1,txt2);
1436}
1437
1438/***************************************************************************
1439 * Setup the treeview structure with regards of the sort method
1440 * and sort the children of the TV item specified in lParam
1441 * fRecurse: currently unused. Should be zero.
1442 * parent: if pSort!=NULL, should equal pSort->hParent.
1443 * otherwise, item which child items are to be sorted.
1444 * pSort: sort method info. if NULL, sort on item text.
1445 * if non-NULL, sort on item's lParam content, and let the
1446 * application decide what that means. See also TVM_SORTCHILDRENCB.
1447 */
1448
1449static LRESULT WINAPI TREEVIEW_Sort (
1450 HWND hwnd,
1451 BOOL fRecurse,
1452 HTREEITEM parent,
1453 LPTVSORTCB pSort
1454 )
1455{
1456 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
1457 TREEVIEW_ITEM *sortMe = NULL; /* Node for which we sort the children */
1458
1459 /* Obtain the TVSORTBC struct */
1460 infoPtr->pCallBackSort = pSort;
1461
1462 /* undocumented feature: TVI_ROOT means `sort the whole tree' */
1463
1464 if (parent==TVI_ROOT)
1465 parent=infoPtr->TopRootItem;
1466
1467 /* Check for a valid handle to the parent item */
1468 if (!TREEVIEW_ValidItem(infoPtr, parent))
1469 {
1470 //ERR ("invalid item hParent=%x\n", (INT)parent);
1471 return FALSE;
1472 }
1473
1474 /* Obtain the parent node to sort */
1475 sortMe = &infoPtr->items[ (INT)parent ];
1476
1477 /* Make sure there is something to sort */
1478 if ( sortMe->cChildren > 1 )
1479 {
1480 /* pointer organization */
1481 HDPA sortList = DPA_Create(sortMe->cChildren);
1482 HTREEITEM itemHandle = sortMe->firstChild;
1483 TREEVIEW_ITEM *itemPtr = & infoPtr->items[ (INT)itemHandle ];
1484
1485 /* TREEVIEW_ITEM rechaining */
1486 INT count = 0;
1487 VOID *item = 0;
1488 VOID *nextItem = 0;
1489 VOID *prevItem = 0;
1490
1491 /* Build the list of item to sort */
1492 do
1493 {
1494 DPA_InsertPtr(
1495 sortList, /* the list */
1496 sortMe->cChildren+1, /* force the insertion to be an append */
1497 itemPtr); /* the ptr to store */
1498
1499 /* Get the next sibling */
1500 itemHandle = itemPtr->sibling;
1501 itemPtr = & infoPtr->items[ (INT)itemHandle ];
1502 } while ( itemHandle != NULL );
1503
1504 /* let DPA perform the sort activity */
1505 if (pSort)
1506 DPA_Sort(
1507 sortList, /* what */
1508 TREEVIEW_CallBackCompare, /* how */
1509 hwnd); /* owner */
1510 else
1511 DPA_Sort (
1512 sortList, /* what */
1513 TREEVIEW_SortOnName, /* how */
1514 hwnd); /* owner */
1515
1516 /*
1517 * Reorganized TREEVIEW_ITEM structures.
1518 * Note that we know we have at least two elements.
1519 */
1520
1521 /* Get the first item and get ready to start... */
1522 item = DPA_GetPtr(sortList, count++);
1523 while ( (nextItem = DPA_GetPtr(sortList, count++)) != NULL )
1524 {
1525 /* link the two current item toghether */
1526 ((TREEVIEW_ITEM*)item)->sibling = ((TREEVIEW_ITEM*)nextItem)->hItem;
1527 ((TREEVIEW_ITEM*)nextItem)->upsibling = ((TREEVIEW_ITEM*)item)->hItem;
1528
1529 if (prevItem == NULL) /* this is the first item, update the parent */
1530 {
1531 sortMe->firstChild = ((TREEVIEW_ITEM*)item)->hItem;
1532 ((TREEVIEW_ITEM*)item)->upsibling = NULL;
1533 }
1534 else /* fix the back chaining */
1535 {
1536 ((TREEVIEW_ITEM*)item)->upsibling = ((TREEVIEW_ITEM*)prevItem)->hItem;
1537 }
1538
1539 /* get ready for the next one */
1540 prevItem = item;
1541 item = nextItem;
1542 }
1543
1544 /* the last item is pointed to by item and never has a sibling */
1545 ((TREEVIEW_ITEM*)item)->sibling = NULL;
1546
1547 DPA_Destroy(sortList);
1548
1549 return TRUE;
1550 }
1551 return FALSE;
1552}
1553
1554/***************************************************************************
1555 * Setup the treeview structure with regards of the sort method
1556 * and sort the children of the TV item specified in lParam
1557 */
1558static LRESULT WINAPI TREEVIEW_SortChildrenCB(
1559 HWND hwnd,
1560 WPARAM wParam,
1561 LPARAM lParam
1562 )
1563{
1564 LPTVSORTCB pSort=(LPTVSORTCB) lParam;
1565
1566 return TREEVIEW_Sort (hwnd, wParam, pSort->hParent, pSort);
1567}
1568
1569
1570/***************************************************************************
1571 * Sort the children of the TV item specified in lParam.
1572 */
1573static LRESULT WINAPI TREEVIEW_SortChildren (
1574 HWND hwnd,
1575 WPARAM wParam,
1576 LPARAM lParam)
1577{
1578 return TREEVIEW_Sort (hwnd, (BOOL) wParam, (HTREEITEM) lParam, NULL);
1579}
1580
1581
1582int ffs(int mask)
1583{
1584 int bit;
1585
1586 if (mask == 0)
1587 return(0);
1588 for (bit = 1; !(mask & 1); bit++)
1589 mask >>= 1;
1590 return(bit);
1591}
1592
1593/* the method used below isn't the most memory-friendly, but it avoids
1594 a lot of memory reallocations */
1595
1596/* BTW: we waste handle 0; 0 is not an allowed handle. */
1597
1598static LRESULT
1599TREEVIEW_InsertItemA (HWND hwnd, WPARAM wParam, LPARAM lParam)
1600
1601{
1602 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
1603 TVINSERTSTRUCTA *ptdi;
1604 TVITEMEXA *tvItem;
1605 TREEVIEW_ITEM *wineItem, *parentItem, *prevsib, *sibItem;
1606 INT iItem,listItems,i,len;
1607
1608 /* Item to insert */
1609 ptdi = (LPTVINSERTSTRUCTA) lParam;
1610
1611 /* check if memory is available */
1612
1613 if (infoPtr->uNumPtrsAlloced==0) {
1614 infoPtr->items = COMCTL32_Alloc (TVITEM_ALLOC*sizeof (TREEVIEW_ITEM));
1615 infoPtr->freeList= COMCTL32_Alloc ((1+(TVITEM_ALLOC>>5)) * sizeof (INT));
1616 infoPtr->uNumPtrsAlloced=TVITEM_ALLOC;
1617 infoPtr->TopRootItem=(HTREEITEM)1;
1618 }
1619
1620 /*
1621 * Reallocate contiguous space for items
1622 */
1623 if (infoPtr->uNumItems == (infoPtr->uNumPtrsAlloced-1) ) {
1624 TREEVIEW_ITEM *oldItems = infoPtr->items;
1625 INT *oldfreeList = infoPtr->freeList;
1626
1627 infoPtr->uNumPtrsAlloced*=2;
1628 infoPtr->items = COMCTL32_Alloc (infoPtr->uNumPtrsAlloced*sizeof (TREEVIEW_ITEM));
1629 infoPtr->freeList= COMCTL32_Alloc ((1+(infoPtr->uNumPtrsAlloced>>5))*sizeof (INT));
1630
1631 memcpy (&infoPtr->items[0], &oldItems[0],
1632 infoPtr->uNumPtrsAlloced/2 * sizeof(TREEVIEW_ITEM));
1633 memcpy (&infoPtr->freeList[0], &oldfreeList[0],
1634 (infoPtr->uNumPtrsAlloced>>6) * sizeof(INT));
1635
1636 COMCTL32_Free (oldItems);
1637 COMCTL32_Free (oldfreeList);
1638 }
1639
1640 /*
1641 * Reset infoPtr structure with new stat according to current TV picture
1642 */
1643 iItem=0;
1644 infoPtr->uNumItems++;
1645 if ((INT)infoPtr->uMaxHandle==(infoPtr->uNumItems-1)) {
1646 iItem=infoPtr->uNumItems;
1647 infoPtr->uMaxHandle = (HTREEITEM)((INT)infoPtr->uMaxHandle + 1);
1648 } else { /* check freelist */
1649 for (i=0; i<infoPtr->uNumPtrsAlloced>>5; i++) {
1650 if (infoPtr->freeList[i]) {
1651 iItem=ffs (infoPtr->freeList[i])-1;
1652
1653 tv_clear_bit(iItem,&infoPtr->freeList[i]);
1654 iItem+=i<<5;
1655 break;
1656 }
1657 }
1658 }
1659
1660// if (TRACE_ON(treeview)) {
1661// for (i=0; i<infoPtr->uNumPtrsAlloced>>5; i++)
1662// TRACE (treeview,"%8x\n",infoPtr->freeList[i]);
1663// }
1664
1665// if (!iItem) ERR (treeview, "Argh -- can't find free item.\n");
1666
1667 /*
1668 * Find the parent item of the new item
1669 */
1670 tvItem= & ptdi->DUMMYUNIONNAME.itemex;
1671 wineItem=& infoPtr->items[iItem];
1672
1673 if ((ptdi->hParent==TVI_ROOT) || (ptdi->hParent==0)) {
1674 parentItem = NULL;
1675 wineItem->parent = 0;
1676 sibItem = &infoPtr->items [(INT)infoPtr->TopRootItem];
1677 listItems = infoPtr->uNumItems;
1678 }
1679 else {
1680 parentItem = &infoPtr->items[(INT)ptdi->hParent];
1681
1682 /* Do the insertion here it if it's the only item of this parent */
1683 if (!parentItem->firstChild)
1684 parentItem->firstChild=(HTREEITEM)iItem;
1685
1686 wineItem->parent = ptdi->hParent;
1687 sibItem = &infoPtr->items [(INT)parentItem->firstChild];
1688 parentItem->cChildren++;
1689 listItems = parentItem->cChildren;
1690 }
1691
1692
1693 /* NOTE: I am moving some setup of the wineItem object that was initialy
1694 * done at the end of the function since some of the values are
1695 * required by the Callback sorting
1696 */
1697
1698 if (tvItem->mask & TVIF_TEXT)
1699 {
1700 /*
1701 * Setup the item text stuff here since it's required by the Sort method
1702 * when the insertion are ordered
1703 */
1704 if (tvItem->pszText!=LPSTR_TEXTCALLBACKA)
1705 {
1706// TRACE (treeview,"(%p,%s)\n", &tvItem->pszText, tvItem->pszText);
1707 len = lstrlenA (tvItem->pszText)+1;
1708 wineItem->pszText= COMCTL32_Alloc (len+1);
1709 lstrcpyA (wineItem->pszText, tvItem->pszText);
1710 wineItem->cchTextMax=len;
1711 }
1712 else
1713 {
1714// TRACE (treeview,"LPSTR_TEXTCALLBACK\n");
1715 wineItem->pszText = LPSTR_TEXTCALLBACKA;
1716 wineItem->cchTextMax = 0;
1717 }
1718 }
1719
1720 if (tvItem->mask & TVIF_PARAM)
1721 wineItem->lParam=tvItem->lParam;
1722
1723
1724 wineItem->upsibling=0; /* needed in case we're the first item in a list */
1725 wineItem->sibling=0;
1726 wineItem->firstChild=0;
1727 wineItem->hItem=(HTREEITEM)iItem;
1728
1729 if (listItems>1) {
1730 prevsib=NULL;
1731
1732 switch ((DWORD) ptdi->hInsertAfter) {
1733 case (DWORD) TVI_FIRST:
1734 if (sibItem==wineItem) break;
1735 if (wineItem->parent) {
1736 wineItem->sibling=parentItem->firstChild;
1737 parentItem->firstChild=(HTREEITEM)iItem;
1738 } else {
1739 wineItem->sibling=infoPtr->TopRootItem;
1740 infoPtr->TopRootItem=(HTREEITEM)iItem;
1741 }
1742 sibItem->upsibling=(HTREEITEM)iItem;
1743 break;
1744
1745 case (DWORD) TVI_SORT:
1746 if (sibItem==wineItem)
1747 /*
1748 * This item is the first child of the level and it
1749 * has already been inserted
1750 */
1751 break;
1752 else
1753 {
1754 TREEVIEW_ITEM *aChild;
1755
1756 TREEVIEW_ITEM *previousChild = NULL;
1757 BOOL bItemInserted = FALSE;
1758
1759 if (parentItem)
1760 aChild = &infoPtr->items[(INT)parentItem->firstChild];
1761 else
1762 aChild = &infoPtr->items[(INT)infoPtr->TopRootItem];
1763
1764 /* Iterate the parent children to see where we fit in */
1765 while ( aChild != NULL )
1766 {
1767 INT comp = strcmp(wineItem->pszText, aChild->pszText);
1768 if ( comp < 0 ) /* we are smaller than the current one */
1769 {
1770 TREEVIEW_InsertBefore(infoPtr, wineItem, aChild, parentItem);
1771 bItemInserted = TRUE;
1772 break;
1773 }
1774 else if ( comp > 0 ) /* we are bigger than the current one */
1775 {
1776 previousChild = aChild;
1777 aChild = (aChild->sibling == 0) /* This will help us to exit */
1778 ? NULL /* if there is no more sibling */
1779 : &infoPtr->items[(INT)aChild->sibling];
1780
1781 /* Look at the next item */
1782 continue;
1783 }
1784 else if ( comp == 0 )
1785 {
1786 /*
1787 * An item with this name is already existing, therefore,
1788 * we add after the one we found
1789 */
1790 TREEVIEW_InsertAfter(infoPtr, wineItem, aChild, parentItem);
1791 bItemInserted = TRUE;
1792 break;
1793 }
1794 }
1795
1796 /*
1797 * we reach the end of the child list and the item as not
1798 * yet been inserted, therefore, insert it after the last child.
1799 */
1800 if ( (! bItemInserted ) && (aChild == NULL) )
1801 TREEVIEW_InsertAfter(infoPtr, wineItem, previousChild, parentItem);
1802
1803 break;
1804 }
1805
1806
1807 case (DWORD) TVI_LAST:
1808 if (sibItem==wineItem) break;
1809 while (sibItem->sibling) {
1810 prevsib=sibItem;
1811 sibItem=&infoPtr->items [(INT)sibItem->sibling];
1812 }
1813 sibItem->sibling=(HTREEITEM)iItem;
1814 wineItem->upsibling=sibItem->hItem;
1815 break;
1816 default:
1817 while ((sibItem->sibling) && (sibItem->hItem!=ptdi->hInsertAfter))
1818 {
1819 prevsib=sibItem;
1820 sibItem=&infoPtr->items [(INT)sibItem->sibling];
1821 }
1822 if (sibItem->hItem!=ptdi->hInsertAfter) {
1823// ERR (treeview, "tried to insert item after nonexisting handle.\n");
1824 break;
1825 }
1826 prevsib=sibItem;
1827 if (sibItem->sibling) {
1828 sibItem=&infoPtr->items [(INT)sibItem->sibling];
1829 sibItem->upsibling=(HTREEITEM)iItem;
1830 wineItem->sibling=sibItem->hItem;
1831 }
1832 prevsib->sibling=(HTREEITEM)iItem;
1833 wineItem->upsibling=prevsib->hItem;
1834 break;
1835 }
1836 }
1837
1838
1839/* Fill in info structure */
1840
1841// TRACE (treeview,"new item %d; parent %d, mask %x\n", iItem,
1842// (INT)wineItem->parent,tvItem->mask);
1843
1844 wineItem->mask=tvItem->mask;
1845 wineItem->iIntegral=1;
1846
1847 if (tvItem->mask & TVIF_CHILDREN) {
1848 wineItem->cChildren=tvItem->cChildren;
1849// if (tvItem->cChildren==I_CHILDRENCALLBACK)
1850// FIXME (treeview," I_CHILDRENCALLBACK not supported\n");
1851 }
1852
1853 wineItem->expandBox.left = 0; /* Initialize the expandBox */
1854 wineItem->expandBox.top = 0;
1855 wineItem->expandBox.right = 0;
1856 wineItem->expandBox.bottom = 0;
1857
1858 if (tvItem->mask & TVIF_IMAGE)
1859 wineItem->iImage=tvItem->iImage;
1860
1861 /* If the application sets TVIF_INTEGRAL without
1862 supplying a TVITEMEX structure, it's toast */
1863
1864 if (tvItem->mask & TVIF_INTEGRAL)
1865 wineItem->iIntegral=tvItem->iIntegral;
1866
1867 if (tvItem->mask & TVIF_SELECTEDIMAGE)
1868 wineItem->iSelectedImage=tvItem->iSelectedImage;
1869
1870 if (tvItem->mask & TVIF_STATE) {
1871// TRACE(treeview, "Changing item state from %d to %d\n",
1872// wineItem->state,
1873// tvItem->state);
1874 wineItem->state=tvItem->state;
1875 wineItem->stateMask=tvItem->stateMask;
1876 }
1877
1878
1879 TREEVIEW_QueueRefresh (hwnd);
1880
1881 return (LRESULT) iItem;
1882}
1883
1884
1885static LRESULT
1886TREEVIEW_InsertItemW(HWND hwnd, WPARAM wParam, LPARAM lParam)
1887{
1888 TVINSERTSTRUCTW *tvisW;
1889 TVINSERTSTRUCTA tvisA;
1890 LRESULT lRes;
1891
1892 tvisW = (LPTVINSERTSTRUCTW)lParam;
1893
1894 tvisA.hParent = tvisW->hParent;
1895 tvisA.hInsertAfter = tvisW->hInsertAfter;
1896
1897 tvisA.DUMMYUNIONNAME.item.mask = tvisW->DUMMYUNIONNAME.item.mask;
1898 tvisA.DUMMYUNIONNAME.item.hItem = tvisW->DUMMYUNIONNAME.item.hItem;
1899 tvisA.DUMMYUNIONNAME.item.state = tvisW->DUMMYUNIONNAME.item.state;
1900 tvisA.DUMMYUNIONNAME.item.stateMask = tvisW->DUMMYUNIONNAME.item.stateMask;
1901 tvisA.DUMMYUNIONNAME.item.cchTextMax = tvisW->DUMMYUNIONNAME.item.cchTextMax;
1902
1903 if(tvisW->DUMMYUNIONNAME.item.pszText)
1904 {
1905 if (tvisW->DUMMYUNIONNAME.item.pszText!=LPSTR_TEXTCALLBACKW)
1906 {
1907 int len = lstrlenW (tvisW->DUMMYUNIONNAME.item.pszText)+1;
1908 tvisA.DUMMYUNIONNAME.item.pszText = COMCTL32_Alloc (len);
1909 lstrcpyWtoA (tvisA.DUMMYUNIONNAME.item.pszText,
1910 tvisW->DUMMYUNIONNAME.item.pszText );
1911 }
1912 else
1913 {
1914 tvisA.DUMMYUNIONNAME.item.pszText = LPSTR_TEXTCALLBACKA;
1915 tvisA.DUMMYUNIONNAME.item.cchTextMax = 0;
1916 }
1917 }
1918
1919 tvisA.DUMMYUNIONNAME.item.iImage = tvisW->DUMMYUNIONNAME.item.iImage;
1920 tvisA.DUMMYUNIONNAME.item.iSelectedImage = tvisW->DUMMYUNIONNAME.item.iSelectedImage;
1921 tvisA.DUMMYUNIONNAME.item.cChildren = tvisW->DUMMYUNIONNAME.item.cChildren;
1922 tvisA.DUMMYUNIONNAME.item.lParam = tvisW->DUMMYUNIONNAME.item.lParam;
1923
1924 lRes = TREEVIEW_InsertItemA(hwnd,wParam,(LPARAM)&tvisA);
1925
1926 if (tvisA.DUMMYUNIONNAME.item.pszText!=LPSTR_TEXTCALLBACKA)
1927 {
1928 COMCTL32_Free(tvisA.DUMMYUNIONNAME.item.pszText);
1929 }
1930
1931 return lRes;
1932
1933}
1934
1935
1936static LRESULT
1937TREEVIEW_DeleteItem (HWND hwnd, WPARAM wParam, LPARAM lParam)
1938{
1939 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
1940 INT iItem;
1941 TREEVIEW_ITEM *wineItem;
1942
1943// TRACE (treeview,"\n");
1944 if (!infoPtr) return FALSE;
1945
1946 if (lParam == (INT)TVI_ROOT) {
1947 TREEVIEW_RemoveTree (hwnd);
1948 } else {
1949 iItem= (INT) lParam;
1950 wineItem = TREEVIEW_ValidItem (infoPtr, (HTREEITEM)iItem);
1951 if (!wineItem) return FALSE;
1952// TRACE (treeview,"%s\n",wineItem->pszText);
1953 TREEVIEW_RemoveItem (hwnd, wineItem);
1954 }
1955
1956 TREEVIEW_QueueRefresh (hwnd);
1957
1958 return TRUE;
1959}
1960
1961
1962
1963static LRESULT
1964TREEVIEW_GetIndent (HWND hwnd)
1965{
1966 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
1967
1968// TRACE (treeview,"\n");
1969 return infoPtr->uIndent;
1970}
1971
1972static LRESULT
1973TREEVIEW_SetIndent (HWND hwnd, WPARAM wParam)
1974{
1975 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
1976 INT newIndent;
1977
1978// TRACE (treeview,"\n");
1979 newIndent=(INT) wParam;
1980 if (newIndent < MINIMUM_INDENT) newIndent=MINIMUM_INDENT;
1981 infoPtr->uIndent=newIndent;
1982
1983 return 0;
1984}
1985
1986static LRESULT
1987TREEVIEW_GetToolTips (HWND hwnd)
1988
1989{
1990 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
1991
1992// TRACE (treeview,"\n");
1993 return infoPtr->hwndToolTip;
1994}
1995
1996
1997static LRESULT
1998TREEVIEW_SetToolTips (HWND hwnd, WPARAM wParam)
1999
2000{
2001 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
2002 HWND prevToolTip;
2003
2004// TRACE (treeview,"\n");
2005 prevToolTip=infoPtr->hwndToolTip;
2006 infoPtr->hwndToolTip= (HWND) wParam;
2007
2008 return prevToolTip;
2009}
2010
2011
2012static LRESULT CALLBACK
2013TREEVIEW_GetEditControl (HWND hwnd)
2014
2015{
2016 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
2017
2018 return infoPtr->hwndEdit;
2019}
2020
2021
2022//@@@PH: Note - this SubclassProc is sometimes called with the
2023// wrong window handle. Therefore, infoPtr points to anything
2024// but the expected structure.
2025LRESULT CALLBACK
2026TREEVIEW_Edit_SubclassProc (HWND hwnd, UINT uMsg, WPARAM wParam,
2027 LPARAM lParam)
2028{
2029 switch (uMsg)
2030 {
2031 case WM_ERASEBKGND:
2032 {
2033 RECT rc;
2034 HDC hdc = (HDC) wParam;
2035 GetClientRect (hwnd, &rc);
2036 Rectangle (hdc, rc.left, rc.top, rc.right, rc.bottom);
2037 return -1;
2038 }
2039
2040 case WM_GETDLGCODE:
2041 {
2042 return DLGC_WANTARROWS | DLGC_WANTALLKEYS;
2043 }
2044
2045 default:
2046 {
2047 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(GetParent(hwnd));
2048
2049 //@@@PH 1999/11/05 method called with freed infoPtr memory object
2050 if (infoPtr != NULL)
2051 return CallWindowProcA( infoPtr->wpEditOrig, hwnd, uMsg, wParam, lParam);
2052 else
2053 break;
2054 }
2055 }
2056
2057 return 0;
2058}
2059
2060
2061/* should handle edit control messages here */
2062
2063static LRESULT
2064TREEVIEW_Command (HWND hwnd, WPARAM wParam, LPARAM lParam)
2065
2066{
2067// TRACE (treeview, "%x %ld\n",wParam, lParam);
2068
2069 switch (HIWORD(wParam))
2070 {
2071 case EN_UPDATE:
2072 {
2073 /*
2074 * Adjust the edit window size
2075 */
2076 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
2077 TREEVIEW_ITEM *editItem = TREEVIEW_ValidItem(infoPtr, infoPtr->editItem);
2078 INT iLength = GetWindowTextLengthA(infoPtr->hwndEdit);
2079 HDC hdc = GetDC(infoPtr->hwndEdit);
2080 TEXTMETRICA tm;
2081
2082 if ( GetTextMetricsA(hdc, &tm) )
2083 {
2084 LONG newWidth = (iLength * tm.tmAveCharWidth) + 15;
2085
2086 SetWindowPos (
2087 infoPtr->hwndEdit,
2088 HWND_TOP,
2089 editItem->text.left - 2,
2090 editItem->text.top - 1,
2091 newWidth,
2092 editItem->text.bottom - editItem->text.top + 3,
2093 SWP_DRAWFRAME );
2094 }
2095 ReleaseDC(hwnd, hdc);
2096
2097 break;
2098 }
2099
2100 case EN_KILLFOCUS:
2101/* TREEVIEW_EndEditLabelNow(hwnd, (WPARAM)FALSE, 0);
2102*/
2103 break;
2104
2105 default:
2106 return SendMessageA (GetParent (hwnd), WM_COMMAND, wParam, lParam);
2107 }
2108
2109 return 0;
2110}
2111
2112static LRESULT
2113TREEVIEW_Size (HWND hwnd, WPARAM wParam, LPARAM lParam)
2114
2115{
2116 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
2117
2118 if (infoPtr->bAutoSize)
2119 {
2120 infoPtr->bAutoSize = FALSE;
2121 return 0;
2122 }
2123 infoPtr->bAutoSize = TRUE;
2124
2125 if (wParam == SIZE_RESTORED)
2126 {
2127 infoPtr->uTotalWidth = LOWORD (lParam);
2128 infoPtr->uTotalHeight = HIWORD (lParam);
2129 } else {
2130// FIXME (treeview,"WM_SIZE flag %x %lx not handled\n", wParam, lParam);
2131 }
2132
2133 TREEVIEW_QueueRefresh (hwnd);
2134 return 0;
2135}
2136
2137
2138
2139static LRESULT
2140TREEVIEW_StyleChanged (HWND hwnd, WPARAM wParam, LPARAM lParam)
2141{
2142// TRACE (treeview,"(%x %lx)\n",wParam,lParam);
2143
2144 TREEVIEW_Refresh(hwnd);
2145
2146 return 0;
2147}
2148
2149static LRESULT
2150TREEVIEW_Create (HWND hwnd, WPARAM wParam, LPARAM lParam)
2151{
2152 TREEVIEW_INFO *infoPtr;
2153 LOGFONTA logFont;
2154 TEXTMETRICA tm;
2155 HDC hdc;
2156
2157// TRACE (treeview,"wnd %x\n",hwnd);
2158 /* allocate memory for info structure */
2159 infoPtr = (TREEVIEW_INFO *) COMCTL32_Alloc (sizeof(TREEVIEW_INFO));
2160
2161 SetWindowLongA( hwnd, 0, (DWORD)infoPtr);
2162
2163 if (infoPtr == NULL) {
2164// ERR (treeview, "could not allocate info memory!\n");
2165 return 0;
2166 }
2167
2168 if ((TREEVIEW_INFO*) GetWindowLongA( hwnd, 0) != infoPtr) {
2169// ERR (treeview, "pointer assignment error!\n");
2170 return 0;
2171 }
2172
2173 hdc=GetDC (hwnd);
2174
2175 /* set default settings */
2176 infoPtr->uInternalStatus=0;
2177 infoPtr->uNumItems=0;
2178 infoPtr->clrBk = GetSysColor (COLOR_WINDOW);
2179 infoPtr->clrText = GetSysColor (COLOR_BTNTEXT);
2180 infoPtr->clrLine = GetSysColor (COLOR_WINDOWTEXT);
2181 infoPtr->cy = 0;
2182 infoPtr->cx = 0;
2183 infoPtr->uIndent = 15;
2184 infoPtr->himlNormal = NULL;
2185 infoPtr->himlState = NULL;
2186 infoPtr->uItemHeight = -1;
2187 GetTextMetricsA (hdc, &tm);
2188 infoPtr->hFont = GetStockObject (DEFAULT_GUI_FONT);
2189 GetObjectA (infoPtr->hFont, sizeof (LOGFONTA), &logFont);
2190 logFont.lfWeight=FW_BOLD;
2191 infoPtr->hBoldFont = CreateFontIndirectA (&logFont);
2192
2193 infoPtr->items = NULL;
2194 infoPtr->selectedItem=0;
2195 infoPtr->clrText=-1; /* use system color */
2196 infoPtr->dropItem=0;
2197 infoPtr->pCallBackSort=NULL;
2198 infoPtr->uScrollTime = 300; /* milliseconds */
2199
2200 // @@@PH 1999/11/05
2201 infoPtr->wpEditOrig = NULL; /* no subclass */
2202
2203/*
2204 infoPtr->hwndNotify = GetParent32 (hwnd);
2205 infoPtr->bTransparent = ( GetWindowLongA( hwnd, GWL_STYLE) & TBSTYLE_FLAT);
2206*/
2207
2208 infoPtr->hwndToolTip=0;
2209 if (!( GetWindowLongA( hwnd, GWL_STYLE) & TVS_NOTOOLTIPS)) { /* Create tooltip control */
2210 TTTOOLINFOA ti;
2211
2212 infoPtr->hwndToolTip =
2213 CreateWindowExA (0, TOOLTIPS_CLASSA, NULL, 0,
2214 CW_USEDEFAULT, CW_USEDEFAULT,
2215 CW_USEDEFAULT, CW_USEDEFAULT,
2216 hwnd, 0, 0, 0);
2217
2218 /* Send NM_TOOLTIPSCREATED notification */
2219 if (infoPtr->hwndToolTip) {
2220 NMTOOLTIPSCREATED nmttc;
2221
2222 nmttc.hdr.hwndFrom = hwnd;
2223 nmttc.hdr.idFrom = GetWindowLongA( hwnd, GWL_ID);
2224 nmttc.hdr.code = NM_TOOLTIPSCREATED;
2225 nmttc.hwndToolTips = infoPtr->hwndToolTip;
2226
2227 SendMessageA (GetParent (hwnd), WM_NOTIFY,
2228 (WPARAM) GetWindowLongA( hwnd, GWL_ID), (LPARAM)&nmttc);
2229 }
2230
2231 ZeroMemory (&ti, sizeof(TTTOOLINFOA));
2232 ti.cbSize = sizeof(TTTOOLINFOA);
2233 ti.uFlags = TTF_IDISHWND | TTF_TRACK | TTF_TRANSPARENT ;
2234 ti.hwnd = hwnd;
2235 ti.uId = 0;
2236 ti.lpszText = "Test"; /* LPSTR_TEXTCALLBACK; */
2237 SetRectEmpty (&ti.rect);
2238
2239 SendMessageA (infoPtr->hwndToolTip, TTM_ADDTOOLA, 0, (LPARAM)&ti);
2240 }
2241
2242 infoPtr->hwndEdit = CreateWindowExA (
2243 WS_EX_LEFT,
2244 "EDIT",
2245 0,
2246 WS_CHILD | WS_BORDER | ES_AUTOHSCROLL |
2247 ES_WANTRETURN | ES_LEFT,
2248 0, 0, 0, 0,
2249 hwnd,
2250 0,0,0); /* FIXME: (HMENU)IDTVEDIT,pcs->hInstance,0);*/
2251
2252 SendMessageA ( infoPtr->hwndEdit, WM_SETFONT, infoPtr->hFont, FALSE);
2253 infoPtr->wpEditOrig = (WNDPROC)SetWindowLongA (
2254 infoPtr->hwndEdit,
2255 GWL_WNDPROC,
2256 (LONG) TREEVIEW_Edit_SubclassProc);
2257
2258 ReleaseDC (hwnd, hdc);
2259 return 0;
2260}
2261
2262
2263
2264static LRESULT
2265TREEVIEW_Destroy (HWND hwnd)
2266{
2267 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
2268
2269// TRACE (treeview,"\n");
2270
2271 TREEVIEW_RemoveTree (hwnd);
2272
2273 //@@@PH 1999/11/05 set infoPtr to NULL
2274 SetWindowLongA( hwnd, 0, (DWORD)NULL);
2275
2276 if (infoPtr->Timer & TV_REFRESH_TIMER_SET)
2277 KillTimer (hwnd, TV_REFRESH_TIMER);
2278 if (infoPtr->hwndToolTip)
2279 DestroyWindow (infoPtr->hwndToolTip);
2280
2281 COMCTL32_Free (infoPtr);
2282
2283 return 0;
2284}
2285
2286
2287static LRESULT
2288TREEVIEW_Paint (HWND hwnd, WPARAM wParam, LPARAM lParam)
2289{
2290 HDC hdc;
2291 PAINTSTRUCT ps;
2292
2293// TRACE (treeview,"\n");
2294 hdc = wParam==0 ? BeginPaint (hwnd, &ps) : (HDC)wParam;
2295 TREEVIEW_Refresh (hwnd);
2296 if(!wParam)
2297 EndPaint (hwnd, &ps);
2298// TRACE (treeview,"done\n");
2299
2300 return DefWindowProcA (hwnd, WM_PAINT, wParam, lParam);
2301}
2302
2303static LRESULT
2304TREEVIEW_SetFocus (HWND hwnd, WPARAM wParam, LPARAM lParam)
2305{
2306 TREEVIEW_SendSimpleNotify (hwnd, NM_SETFOCUS);
2307 InvalidateRect(hwnd, NULL, FALSE);
2308 return 0;
2309}
2310
2311static LRESULT
2312TREEVIEW_KillFocus (HWND hwnd, WPARAM wParam, LPARAM lParam)
2313{
2314 TREEVIEW_SendSimpleNotify (hwnd, NM_KILLFOCUS);
2315 InvalidateRect(hwnd, NULL, FALSE);
2316 return 0;
2317}
2318
2319static LRESULT
2320TREEVIEW_EraseBackground (HWND hwnd, WPARAM wParam, LPARAM lParam)
2321{
2322 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
2323 HBRUSH hBrush = CreateSolidBrush (infoPtr->clrBk);
2324 RECT rect;
2325
2326// TRACE (treeview,"\n");
2327 GetClientRect (hwnd, &rect);
2328 FillRect ((HDC)wParam, &rect, hBrush);
2329 DeleteObject (hBrush);
2330 return TRUE;
2331}
2332
2333
2334
2335
2336
2337
2338/* Notifications */
2339
2340
2341
2342
2343
2344static BOOL
2345TREEVIEW_SendSimpleNotify (HWND hwnd, UINT code)
2346{
2347 NMHDR nmhdr;
2348
2349// TRACE (treeview, "%x\n",code);
2350 nmhdr.hwndFrom = hwnd;
2351 nmhdr.idFrom = GetWindowLongA( hwnd, GWL_ID);
2352 nmhdr.code = code;
2353
2354 return (BOOL) SendMessageA (GetParent (hwnd), WM_NOTIFY,
2355 (WPARAM)nmhdr.idFrom, (LPARAM)&nmhdr);
2356}
2357
2358
2359
2360static BOOL
2361TREEVIEW_SendTreeviewNotify (HWND hwnd, UINT code, UINT action,
2362 HTREEITEM oldItem, HTREEITEM newItem)
2363
2364{
2365 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
2366 NMTREEVIEWA nmhdr;
2367 TREEVIEW_ITEM *wineItem;
2368
2369// TRACE (treeview,"code:%x action:%x olditem:%x newitem:%x\n",
2370// code,action,(INT)oldItem,(INT)newItem);
2371 nmhdr.hdr.hwndFrom = hwnd;
2372 nmhdr.hdr.idFrom = GetWindowLongA( hwnd, GWL_ID);
2373 nmhdr.hdr.code = code;
2374 nmhdr.action = action;
2375 if (oldItem) {
2376 wineItem=& infoPtr->items[(INT)oldItem];
2377 nmhdr.itemOld.mask = wineItem->mask;
2378 nmhdr.itemOld.hItem = wineItem->hItem;
2379 nmhdr.itemOld.state = wineItem->state;
2380 nmhdr.itemOld.stateMask = wineItem->stateMask;
2381 nmhdr.itemOld.iImage = wineItem->iImage;
2382 nmhdr.itemOld.pszText = wineItem->pszText;
2383 nmhdr.itemOld.cchTextMax= wineItem->cchTextMax;
2384 nmhdr.itemOld.iImage = wineItem->iImage;
2385 nmhdr.itemOld.iSelectedImage = wineItem->iSelectedImage;
2386 nmhdr.itemOld.cChildren = wineItem->cChildren;
2387 nmhdr.itemOld.lParam = wineItem->lParam;
2388 }
2389
2390 if (newItem) {
2391 wineItem=& infoPtr->items[(INT)newItem];
2392 nmhdr.itemNew.mask = wineItem->mask;
2393 nmhdr.itemNew.hItem = wineItem->hItem;
2394 nmhdr.itemNew.state = wineItem->state;
2395 nmhdr.itemNew.stateMask = wineItem->stateMask;
2396 nmhdr.itemNew.iImage = wineItem->iImage;
2397 nmhdr.itemNew.pszText = wineItem->pszText;
2398 nmhdr.itemNew.cchTextMax= wineItem->cchTextMax;
2399 nmhdr.itemNew.iImage = wineItem->iImage;
2400 nmhdr.itemNew.iSelectedImage = wineItem->iSelectedImage;
2401 nmhdr.itemNew.cChildren = wineItem->cChildren;
2402 nmhdr.itemNew.lParam = wineItem->lParam;
2403 }
2404
2405 nmhdr.ptDrag.x = 0;
2406 nmhdr.ptDrag.y = 0;
2407
2408 return (BOOL)SendMessageA (GetParent (hwnd), WM_NOTIFY,
2409 (WPARAM) GetWindowLongA( hwnd, GWL_ID), (LPARAM)&nmhdr);
2410
2411}
2412
2413static BOOL
2414TREEVIEW_SendTreeviewDnDNotify (HWND hwnd, UINT code, HTREEITEM dragItem,
2415 POINT pt)
2416{
2417 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
2418 NMTREEVIEWA nmhdr;
2419 TREEVIEW_ITEM *wineItem;
2420
2421// TRACE (treeview,"code:%x dragitem:%x\n", code,(INT)dragItem);
2422
2423 nmhdr.hdr.hwndFrom = hwnd;
2424 nmhdr.hdr.idFrom = GetWindowLongA( hwnd, GWL_ID);
2425 nmhdr.hdr.code = code;
2426 nmhdr.action = 0;
2427 wineItem=& infoPtr->items[(INT)dragItem];
2428 nmhdr.itemNew.mask = wineItem->mask;
2429 nmhdr.itemNew.hItem = wineItem->hItem;
2430 nmhdr.itemNew.state = wineItem->state;
2431 nmhdr.itemNew.lParam = wineItem->lParam;
2432
2433 nmhdr.ptDrag.x = pt.x;
2434 nmhdr.ptDrag.y = pt.y;
2435
2436 return (BOOL)SendMessageA (GetParent (hwnd), WM_NOTIFY,
2437 (WPARAM) GetWindowLongA( hwnd, GWL_ID), (LPARAM)&nmhdr);
2438
2439}
2440
2441
2442
2443static BOOL
2444TREEVIEW_SendDispInfoNotify (HWND hwnd, TREEVIEW_ITEM *wineItem,
2445 UINT code, UINT what)
2446{
2447 NMTVDISPINFOA tvdi;
2448 BOOL retval;
2449 char *buf;
2450
2451// TRACE (treeview,"item %d, action %x, state %d\n",
2452// (INT)wineItem->hItem,
2453// what,
2454// (INT)wineItem->state);
2455
2456 tvdi.hdr.hwndFrom = hwnd;
2457 tvdi.hdr.idFrom = GetWindowLongA( hwnd, GWL_ID);
2458 tvdi.hdr.code = code;
2459 tvdi.item.mask = what;
2460 tvdi.item.hItem = wineItem->hItem;
2461 tvdi.item.state = wineItem->state;
2462 tvdi.item.lParam = wineItem->lParam;
2463 tvdi.item.pszText = COMCTL32_Alloc (128*sizeof(char));
2464 buf = tvdi.item.pszText;
2465
2466 retval=(BOOL)SendMessageA (
2467 GetParent(hwnd),
2468 WM_NOTIFY,
2469 (WPARAM)tvdi.hdr.idFrom,
2470 (LPARAM)&tvdi);
2471
2472 if (what & TVIF_TEXT) {
2473 wineItem->pszText = tvdi.item.pszText;
2474 if (buf==tvdi.item.pszText) {
2475 wineItem->cchTextMax = 128;
2476 } else {
2477// TRACE (treeview,"user-supplied buffer\n");
2478 COMCTL32_Free (buf);
2479 wineItem->cchTextMax = 0;
2480 }
2481 }
2482 if (what & TVIF_SELECTEDIMAGE)
2483 wineItem->iSelectedImage = tvdi.item.iSelectedImage;
2484 if (what & TVIF_IMAGE)
2485 wineItem->iImage = tvdi.item.iImage;
2486 if (what & TVIF_CHILDREN)
2487 wineItem->cChildren = tvdi.item.cChildren;
2488
2489 return retval;
2490}
2491
2492
2493
2494static BOOL
2495TREEVIEW_SendCustomDrawNotify (HWND hwnd, DWORD dwDrawStage, HDC hdc,
2496 RECT rc)
2497{
2498 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
2499 NMTVCUSTOMDRAW nmcdhdr;
2500 LPNMCUSTOMDRAW nmcd;
2501
2502// TRACE (treeview,"drawstage:%lx hdc:%x\n", dwDrawStage, hdc);
2503
2504 nmcd= & nmcdhdr.nmcd;
2505 nmcd->hdr.hwndFrom = hwnd;
2506 nmcd->hdr.idFrom = GetWindowLongA( hwnd, GWL_ID);
2507 nmcd->hdr.code = NM_CUSTOMDRAW;
2508 nmcd->dwDrawStage= dwDrawStage;
2509 nmcd->hdc = hdc;
2510 nmcd->rc.left = rc.left;
2511 nmcd->rc.right = rc.right;
2512 nmcd->rc.bottom = rc.bottom;
2513 nmcd->rc.top = rc.top;
2514 nmcd->dwItemSpec = 0;
2515 nmcd->uItemState = 0;
2516 nmcd->lItemlParam= 0;
2517 nmcdhdr.clrText = infoPtr->clrText;
2518 nmcdhdr.clrTextBk= infoPtr->clrBk;
2519 nmcdhdr.iLevel = 0;
2520
2521 return (BOOL)SendMessageA (GetParent (hwnd), WM_NOTIFY,
2522 (WPARAM) GetWindowLongA( hwnd, GWL_ID), (LPARAM)&nmcdhdr);
2523
2524}
2525
2526
2527
2528/* FIXME: need to find out when the flags in uItemState need to be set */
2529
2530static BOOL
2531TREEVIEW_SendCustomDrawItemNotify (HWND hwnd, HDC hdc,
2532 TREEVIEW_ITEM *wineItem, UINT uItemDrawState)
2533{
2534 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
2535 NMTVCUSTOMDRAW nmcdhdr;
2536 LPNMCUSTOMDRAW nmcd;
2537 DWORD dwDrawStage,dwItemSpec;
2538 UINT uItemState;
2539
2540 dwDrawStage=CDDS_ITEM | uItemDrawState;
2541 dwItemSpec=(DWORD)wineItem->hItem;
2542 uItemState=0;
2543 if (wineItem->hItem==infoPtr->selectedItem) uItemState|=CDIS_SELECTED;
2544 if (wineItem->hItem==infoPtr->focusItem) uItemState|=CDIS_FOCUS;
2545 if (wineItem->hItem==infoPtr->hotItem) uItemState|=CDIS_HOT;
2546
2547 nmcd= & nmcdhdr.nmcd;
2548 nmcd->hdr.hwndFrom = hwnd;
2549 nmcd->hdr.idFrom = GetWindowLongA( hwnd, GWL_ID);
2550 nmcd->hdr.code = NM_CUSTOMDRAW;
2551 nmcd->dwDrawStage= dwDrawStage;
2552 nmcd->hdc = hdc;
2553 nmcd->rc.left = wineItem->rect.left;
2554 nmcd->rc.right = wineItem->rect.right;
2555 nmcd->rc.bottom = wineItem->rect.bottom;
2556 nmcd->rc.top = wineItem->rect.top;
2557 nmcd->dwItemSpec = dwItemSpec;
2558 nmcd->uItemState = uItemState;
2559 nmcd->lItemlParam= wineItem->lParam;
2560
2561 nmcdhdr.clrText = infoPtr->clrText;
2562 nmcdhdr.clrTextBk= infoPtr->clrBk;
2563 nmcdhdr.iLevel = wineItem->iLevel;
2564
2565// TRACE (treeview,"drawstage:%lx hdc:%x item:%lx, itemstate:%x\n",
2566// dwDrawStage, hdc, dwItemSpec, uItemState);
2567
2568 return (BOOL)SendMessageA (GetParent (hwnd), WM_NOTIFY,
2569 (WPARAM) GetWindowLongA( hwnd, GWL_ID), (LPARAM)&nmcdhdr);
2570}
2571
2572
2573
2574/* Note:If the specified item is the child of a collapsed parent item,
2575 the parent's list of child items is (recursively) expanded to reveal the
2576 specified item. This is mentioned for TREEVIEW_SelectItem; don't
2577 know if it also applies here.
2578*/
2579
2580static LRESULT
2581TREEVIEW_Expand (HWND hwnd, WPARAM wParam, LPARAM lParam)
2582{
2583 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
2584 TREEVIEW_ITEM *wineItem;
2585 UINT flag;
2586 INT expand;
2587
2588 flag = (UINT) wParam;
2589 expand = (INT) lParam;
2590
2591 wineItem = TREEVIEW_ValidItem (infoPtr, (HTREEITEM)expand);
2592
2593 if (!wineItem)
2594 return 0;
2595 if (!wineItem->cChildren)
2596 return 0;
2597
2598// TRACE (treeview,"For (%s) flags:%x item:%d state:%d\n",
2599// wineItem->pszText,
2600// flag,
2601// expand,
2602// wineItem->state);
2603
2604 if (wineItem->cChildren==I_CHILDRENCALLBACK) {
2605// FIXME (treeview,"we don't handle I_CHILDRENCALLBACK yet\n");
2606 return 0;
2607 }
2608
2609 if (flag == TVE_TOGGLE) { /* FIXME: check exact behaviour here */
2610 flag &= ~TVE_TOGGLE; /* ie: bitwise ops or 'case' ops */
2611 if (wineItem->state & TVIS_EXPANDED)
2612 flag |= TVE_COLLAPSE;
2613 else
2614 flag |= TVE_EXPAND;
2615 }
2616
2617 switch (flag)
2618 {
2619 case TVE_COLLAPSERESET:
2620// TRACE(treeview, " case TVE_COLLAPSERESET\n");
2621 if (!wineItem->state & TVIS_EXPANDED)
2622 return 0;
2623
2624 wineItem->state &= ~(TVIS_EXPANDEDONCE | TVIS_EXPANDED);
2625 TREEVIEW_RemoveAllChildren (hwnd, wineItem);
2626 break;
2627
2628 case TVE_COLLAPSE:
2629// TRACE(treeview, " case TVE_COLLAPSE\n");
2630 if (!wineItem->state & TVIS_EXPANDED)
2631 return 0;
2632
2633 wineItem->state &= ~TVIS_EXPANDED;
2634 break;
2635
2636 case TVE_EXPAND:
2637// TRACE(treeview, " case TVE_EXPAND\n");
2638 if (wineItem->state & TVIS_EXPANDED)
2639 return 0;
2640
2641// TRACE(treeview, " is not expanded...\n");
2642
2643 if (!(wineItem->state & TVIS_EXPANDEDONCE))
2644 {
2645// TRACE(treeview, " and has never been expanded...\n");
2646 wineItem->state |= TVIS_EXPANDED;
2647
2648 /* this item has never been expanded */
2649 if (TREEVIEW_SendTreeviewNotify (
2650 hwnd,
2651 TVN_ITEMEXPANDING,
2652 TVE_EXPAND,
2653 0,
2654 (HTREEITEM)expand))
2655 {
2656// TRACE(treeview, " TVN_ITEMEXPANDING returned TRUE, exiting...\n");
2657 return FALSE;
2658 }
2659
2660 /* FIXME
2661 * Since the TVN_ITEMEXPANDING message may has caused the parent to
2662 * insert new items which in turn may have cause items placeholder
2663 * reallocation, I reassign the current item pointer so we have
2664 * something valid to work with...
2665 * However, this should not be necessary,
2666 * investigation required in TREEVIEW_InsertItemA
2667 */
2668 wineItem = TREEVIEW_ValidItem (infoPtr, (HTREEITEM)expand);
2669 if (! wineItem)
2670 {
2671// ERR(treeview,
2672// "Catastropic situation, cannot retreive item #%d\n",
2673// expand);
2674 return FALSE;
2675 }
2676
2677 wineItem->state |= TVIS_EXPANDEDONCE;
2678// TRACE(treeview, " TVN_ITEMEXPANDING sent...\n");
2679
2680 TREEVIEW_SendTreeviewNotify (
2681 hwnd,
2682 TVN_ITEMEXPANDED,
2683 TVE_EXPAND,
2684 0,
2685 (HTREEITEM)expand);
2686
2687// TRACE(treeview, " TVN_ITEMEXPANDED sent...\n");
2688
2689 }
2690 else
2691 {
2692 /* this item has already been expanded */
2693 wineItem->state |= TVIS_EXPANDED;
2694 }
2695 break;
2696
2697 case TVE_EXPANDPARTIAL:
2698// TRACE(treeview, " case TVE_EXPANDPARTIAL\n");
2699// FIXME (treeview, "TVE_EXPANDPARTIAL not implemented\n");
2700 wineItem->state ^=TVIS_EXPANDED;
2701 wineItem->state |=TVIS_EXPANDEDONCE;
2702 break;
2703 }
2704
2705// TRACE(treeview, "Exiting, Item %d state is now %d...\n",
2706// expand,
2707// wineItem->state);
2708
2709 TREEVIEW_QueueRefresh (hwnd);
2710 return TRUE;
2711}
2712
2713
2714
2715static TREEVIEW_ITEM *
2716TREEVIEW_HitTestPoint (HWND hwnd, POINT pt)
2717{
2718 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
2719 TREEVIEW_ITEM *wineItem;
2720 RECT rect;
2721
2722 GetClientRect (hwnd, &rect);
2723
2724 if (!infoPtr->firstVisible) return NULL;
2725
2726 wineItem=&infoPtr->items [(INT)infoPtr->firstVisible];
2727
2728 while ((wineItem!=NULL) && (pt.y > wineItem->rect.bottom))
2729 wineItem=TREEVIEW_GetNextListItem (infoPtr,wineItem);
2730
2731 if (!wineItem)
2732 return NULL;
2733
2734 return wineItem;
2735}
2736
2737
2738
2739
2740static LRESULT
2741TREEVIEW_HitTest (HWND hwnd, LPARAM lParam)
2742{
2743 LPTVHITTESTINFO lpht=(LPTVHITTESTINFO) lParam;
2744 TREEVIEW_ITEM *wineItem;
2745 RECT rect;
2746 UINT status,x,y;
2747
2748 GetClientRect (hwnd, &rect);
2749 status=0;
2750 x=lpht->pt.x;
2751 y=lpht->pt.y;
2752 if (x < rect.left) status|=TVHT_TOLEFT;
2753 if (x > rect.right) status|=TVHT_TORIGHT;
2754 if (y < rect.top ) status|=TVHT_ABOVE;
2755 if (y > rect.bottom) status|=TVHT_BELOW;
2756
2757 if (status) {
2758 lpht->flags=status;
2759 return 0;
2760 }
2761
2762 wineItem=TREEVIEW_HitTestPoint (hwnd, lpht->pt);
2763 if (!wineItem) {
2764 lpht->flags=TVHT_NOWHERE;
2765 return 0;
2766 }
2767
2768 /* FIXME: implement other flags
2769 * Assign the appropriate flags depending on the click location
2770 * Intitialize flags before to "|=" it...
2771 */
2772 lpht->flags=0;
2773
2774 if (x < wineItem->expandBox.left)
2775 {
2776 lpht->flags |= TVHT_ONITEMINDENT;
2777 }
2778 else if ( ( x >= wineItem->expandBox.left) &&
2779 ( x <= wineItem->expandBox.right))
2780 {
2781 lpht->flags |= TVHT_ONITEMBUTTON;
2782 }
2783 else if (x < wineItem->rect.right)
2784 {
2785 lpht->flags |= TVHT_ONITEMLABEL;
2786 }
2787 else
2788 {
2789 lpht->flags|=TVHT_ONITEMRIGHT;
2790 }
2791
2792 lpht->hItem=wineItem->hItem;
2793
2794 return (LRESULT) wineItem->hItem;
2795}
2796
2797LRESULT WINAPI
2798TREEVIEW_EndEditLabelNow (HWND hwnd, WPARAM wParam, LPARAM lParam)
2799{
2800 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
2801 TREEVIEW_ITEM *editedItem = TREEVIEW_ValidItem (infoPtr, infoPtr->editItem);
2802 BOOL bRevert = (BOOL)wParam;
2803 BOOL bReturn = ! bRevert;
2804
2805 if ( ! (BOOL)wParam ) /* wParam is set to true to cancel the edition */
2806 {
2807 if ( TREEVIEW_SendDispInfoNotify( /* return true to cancel edition */
2808 hwnd,
2809 editedItem,
2810 TVN_ENDLABELEDIT,
2811 0))
2812 {
2813 bRevert = TRUE;
2814 bReturn = FALSE;
2815 }
2816 }
2817
2818 if (bRevert == FALSE) /* Apply the changes */
2819 {
2820 char tmpText[1024];
2821 int iLength = GetWindowTextA(infoPtr->hwndEdit, tmpText, 1023);
2822 bReturn = FALSE;
2823
2824 if (iLength == 0)
2825 {
2826// ERR( treeview, "Problem retreiving new item label.");
2827 }
2828 else if (iLength >= 1023)
2829 {
2830// ERR( treeview,
2831// "Insuficient space to retrieve new item label, new label ignored.");
2832 }
2833 else
2834 {
2835 if (strcmp( tmpText, editedItem->pszText ) == 0)
2836 /* Do nothing if the label has not changed */
2837 bReturn = TRUE;
2838 else
2839 {
2840 LPSTR tmpLabel = COMCTL32_Alloc( iLength+1 );
2841
2842// if ( tmpLabel == NULL )
2843// ERR( treeview,
2844// "OutOfMemory, cannot allocate space for label");
2845// else
2846 {
2847 COMCTL32_Free(editedItem->pszText);
2848 editedItem->pszText = tmpLabel;
2849 lstrcpyA( editedItem->pszText, tmpText);
2850 bReturn = TRUE;
2851 }
2852 }
2853 }
2854
2855 ShowWindow(infoPtr->hwndEdit, SW_HIDE);
2856 EnableWindow(infoPtr->hwndEdit, FALSE);
2857 infoPtr->editItem = 0;
2858 }
2859
2860 return bReturn;
2861}
2862
2863
2864
2865static LRESULT
2866TREEVIEW_LButtonDoubleClick (HWND hwnd, WPARAM wParam, LPARAM lParam)
2867{
2868 TREEVIEW_ITEM *wineItem;
2869 POINT pt;
2870
2871// TRACE (treeview,"\n");
2872 pt.x = (INT)LOWORD(lParam);
2873 pt.y = (INT)HIWORD(lParam);
2874 SetFocus (hwnd);
2875
2876 wineItem=TREEVIEW_HitTestPoint (hwnd, pt);
2877 if (!wineItem) return 0;
2878// TRACE (treeview,"item %d \n",(INT)wineItem->hItem);
2879
2880 if (TREEVIEW_SendSimpleNotify (hwnd, NM_DBLCLK)!=TRUE) { /* FIXME!*/
2881 TREEVIEW_Expand (hwnd, (WPARAM) TVE_TOGGLE, (LPARAM) wineItem->hItem);
2882 }
2883 return TRUE;
2884}
2885
2886
2887static LRESULT
2888TREEVIEW_LButtonDown (HWND hwnd, WPARAM wParam, LPARAM lParam)
2889{
2890 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
2891 INT iItem;
2892 TVHITTESTINFO ht;
2893
2894 ht.pt.x = (INT)LOWORD(lParam);
2895 ht.pt.y = (INT)HIWORD(lParam);
2896
2897 SetFocus (hwnd);
2898 iItem=TREEVIEW_HitTest (hwnd, (LPARAM) &ht);
2899// TRACE (treeview,"item %d \n",iItem);
2900
2901 if (ht.flags & TVHT_ONITEMBUTTON) {
2902 TREEVIEW_Expand (hwnd, (WPARAM) TVE_TOGGLE, (LPARAM) iItem);
2903 }
2904 else
2905 {
2906 infoPtr->uInternalStatus|=TV_LDRAG;
2907 }
2908
2909 return 0;
2910}
2911
2912static LRESULT
2913TREEVIEW_LButtonUp (HWND hwnd, WPARAM wParam, LPARAM lParam)
2914{
2915 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
2916 INT iItem;
2917 TREEVIEW_ITEM *editItem;
2918 TVHITTESTINFO ht;
2919
2920 ht.pt.x = (INT)LOWORD(lParam);
2921 ht.pt.y = (INT)HIWORD(lParam);
2922
2923// TRACE (treeview,"\n");
2924
2925 /* Return true to cancel default behaviour */
2926 if ( TREEVIEW_SendSimpleNotify (hwnd, NM_CLICK) )
2927 return 0;
2928
2929 /* Get the item */
2930 iItem = TREEVIEW_HitTest (hwnd, (LPARAM) &ht);
2931 if (!iItem)
2932 return 0;
2933
2934 editItem = TREEVIEW_ValidItem(infoPtr, (HTREEITEM)iItem);
2935
2936 infoPtr->uInternalStatus &= ~(TV_LDRAG | TV_LDRAGGING);
2937
2938 /*
2939 * If the style allow editing and the node is already selected
2940 * and the click occured on the item label...
2941 */
2942 if ( ( GetWindowLongA( hwnd, GWL_STYLE) & TVS_EDITLABELS ) &&
2943 ( editItem->state & TVIS_SELECTED ) &&
2944 ( ht.flags & TVHT_ONITEMLABEL ))
2945 {
2946 if ( infoPtr->editItem == 0 ) /* If we are not curently editing */
2947 {
2948 if ( TREEVIEW_SendDispInfoNotify( /* Return true to cancel edition */
2949 hwnd,
2950 editItem,
2951 TVN_BEGINLABELEDIT,
2952 0))
2953 {
2954 return 0;
2955 }
2956
2957// TRACE(treeview,"Edit started for %s.\n", editItem->pszText);
2958 infoPtr->editItem = editItem->hItem;
2959
2960 SetWindowPos (
2961 infoPtr->hwndEdit,
2962 HWND_TOP,
2963 editItem->text.left - 2,
2964 editItem->text.top - 1,
2965 editItem->text.right - editItem->text.left + 20 ,
2966 editItem->text.bottom - editItem->text.top + 3,
2967 SWP_DRAWFRAME );
2968
2969 SetWindowTextA( infoPtr->hwndEdit, editItem->pszText );
2970 SendMessageA ( infoPtr->hwndEdit, EM_SETSEL, 0, -1 );
2971 SetFocus ( infoPtr->hwndEdit);
2972 ShowWindow ( infoPtr->hwndEdit, SW_SHOW);
2973 }
2974 }
2975 else if ( infoPtr->editItem != 0 ) /* If we are curently editing */
2976 {
2977 TREEVIEW_EndEditLabelNow(hwnd, (WPARAM)FALSE, 0);
2978 }
2979 else if ( ht.flags & (TVHT_ONITEMLABEL | TVHT_ONITEMICON))
2980 {
2981 TREEVIEW_DoSelectItem (
2982 hwnd,
2983 TVGN_CARET,
2984 (HTREEITEM)iItem,
2985 TVC_BYMOUSE);
2986 }
2987
2988 return 0;
2989}
2990
2991
2992static LRESULT
2993TREEVIEW_RButtonDown (HWND hwnd, WPARAM wParam, LPARAM lParam)
2994{
2995 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
2996
2997// TRACE (treeview,"\n");
2998 infoPtr->uInternalStatus|=TV_RDRAG;
2999 return 0;
3000}
3001
3002static LRESULT
3003TREEVIEW_RButtonUp (HWND hwnd, WPARAM wParam, LPARAM lParam)
3004{
3005 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
3006
3007// TRACE (treeview,"\n");
3008 if (TREEVIEW_SendSimpleNotify (hwnd, NM_RCLICK)) return 0;
3009 infoPtr->uInternalStatus&= ~(TV_RDRAG | TV_RDRAGGING);
3010 return 0;
3011}
3012
3013
3014static LRESULT
3015TREEVIEW_MouseMove (HWND hwnd, WPARAM wParam, LPARAM lParam)
3016{
3017 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
3018 TREEVIEW_ITEM *hotItem;
3019 POINT pt;
3020
3021 pt.x=(INT) LOWORD (lParam);
3022 pt.y=(INT) HIWORD (lParam);
3023 hotItem=TREEVIEW_HitTestPoint (hwnd, pt);
3024 if (!hotItem) return 0;
3025 infoPtr->focusItem=hotItem->hItem;
3026
3027 if ( GetWindowLongA( hwnd, GWL_STYLE) & TVS_DISABLEDRAGDROP) return 0;
3028
3029 if (infoPtr->uInternalStatus & TV_LDRAG) {
3030 TREEVIEW_SendTreeviewDnDNotify (hwnd, TVN_BEGINDRAG, hotItem->hItem, pt);
3031 infoPtr->uInternalStatus &= ~TV_LDRAG;
3032 infoPtr->uInternalStatus |= TV_LDRAGGING;
3033 infoPtr->dropItem=hotItem->hItem;
3034 return 0;
3035 }
3036
3037 if (infoPtr->uInternalStatus & TV_RDRAG) {
3038 TREEVIEW_SendTreeviewDnDNotify (hwnd, TVN_BEGINRDRAG, hotItem->hItem, pt);
3039 infoPtr->uInternalStatus &= ~TV_RDRAG;
3040 infoPtr->uInternalStatus |= TV_RDRAGGING;
3041 infoPtr->dropItem=hotItem->hItem;
3042 return 0;
3043 }
3044
3045 return 0;
3046}
3047
3048
3049static LRESULT
3050TREEVIEW_CreateDragImage (HWND hwnd, WPARAM wParam, LPARAM lParam)
3051{
3052 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
3053 TREEVIEW_ITEM *dragItem;
3054 INT cx,cy;
3055 HDC hdc,htopdc;
3056 HWND hwtop;
3057 HBITMAP hbmp,hOldbmp;
3058 SIZE size;
3059 RECT rc;
3060 HFONT hOldFont;
3061 char *itemtxt;
3062
3063// TRACE (treeview,"\n");
3064 if (!(infoPtr->himlNormal)) return 0;
3065 dragItem=TREEVIEW_ValidItem (infoPtr, (HTREEITEM) lParam);
3066
3067 if (!dragItem) return 0;
3068 itemtxt=dragItem->pszText;
3069
3070 hwtop=GetDesktopWindow ();
3071 htopdc= GetDC (hwtop);
3072 hdc=CreateCompatibleDC (htopdc);
3073
3074 hOldFont=SelectObject (hdc, infoPtr->hFont);
3075 GetTextExtentPoint32A (hdc, itemtxt, lstrlenA (itemtxt), &size);
3076// TRACE (treeview,"%d %d %s %d\n",size.cx,size.cy,itemtxt,lstrlenA(itemtxt));
3077 hbmp=CreateCompatibleBitmap (htopdc, size.cx, size.cy);
3078 hOldbmp=SelectObject (hdc, hbmp);
3079
3080 ImageList_GetIconSize (infoPtr->himlNormal, &cx, &cy);
3081 size.cx+=cx;
3082 if (cy>size.cy) size.cy=cy;
3083
3084 infoPtr->dragList=ImageList_Create (size.cx, size.cy, ILC_COLOR, 10, 10);
3085 ImageList_Draw (infoPtr->himlNormal, dragItem->iImage, hdc, 0, 0, ILD_NORMAL);
3086
3087/*
3088 ImageList_GetImageInfo (infoPtr->himlNormal, dragItem->hItem, &iminfo);
3089 ImageList_AddMasked (infoPtr->dragList, iminfo.hbmImage, CLR_DEFAULT);
3090*/
3091
3092/* draw item text */
3093
3094 SetRect (&rc, cx, 0, size.cx,size.cy);
3095 DrawTextA (hdc, itemtxt, lstrlenA (itemtxt), &rc, DT_LEFT);
3096 SelectObject (hdc, hOldFont);
3097 SelectObject (hdc, hOldbmp);
3098
3099 ImageList_Add (infoPtr->dragList, hbmp, 0);
3100
3101 DeleteDC (hdc);
3102 DeleteObject (hbmp);
3103 ReleaseDC (hwtop, htopdc);
3104
3105 return (LRESULT)infoPtr->dragList;
3106}
3107
3108
3109static LRESULT
3110TREEVIEW_DoSelectItem (HWND hwnd, INT action, HTREEITEM newSelect, INT cause)
3111
3112{
3113 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
3114 TREEVIEW_ITEM *prevItem,*wineItem;
3115 INT prevSelect;
3116
3117 wineItem = TREEVIEW_ValidItem (infoPtr, (HTREEITEM)newSelect);
3118
3119// TRACE (treeview,"Entering item %d, flag %x, cause %x, state %d\n",
3120// (INT)newSelect,
3121// action,
3122// cause,
3123// wineItem->state);
3124
3125 if ( (wineItem) && (wineItem->parent))
3126 {
3127 /*
3128 * If the item has a collapse parent expand the parent so he
3129 * can expose the item
3130 */
3131 TREEVIEW_ITEM *parentItem = TREEVIEW_ValidItem (infoPtr, wineItem->parent);
3132 if ( !(parentItem->state & TVIS_EXPANDED))
3133 TREEVIEW_Expand (hwnd, TVE_EXPAND, (LPARAM) wineItem->parent);
3134 }
3135
3136 switch (action)
3137 {
3138 case TVGN_CARET:
3139 prevSelect=(INT)infoPtr->selectedItem;
3140
3141 if ((HTREEITEM)prevSelect==newSelect)
3142 return FALSE;
3143
3144 prevItem= TREEVIEW_ValidItem (infoPtr, (HTREEITEM)prevSelect);
3145
3146 if (newSelect)
3147 if (TREEVIEW_SendTreeviewNotify(
3148 hwnd,
3149 TVN_SELCHANGING,
3150 cause,
3151 (HTREEITEM)prevSelect,
3152 (HTREEITEM)newSelect))
3153 return FALSE; /* FIXME: OK? */
3154
3155 if (prevItem)
3156 prevItem->state &= ~TVIS_SELECTED;
3157 if (wineItem)
3158 wineItem->state |= TVIS_SELECTED;
3159
3160 infoPtr->selectedItem=(HTREEITEM)newSelect;
3161
3162 TREEVIEW_SendTreeviewNotify(
3163 hwnd,
3164 TVN_SELCHANGED,
3165 cause,
3166 (HTREEITEM)prevSelect,
3167 (HTREEITEM)newSelect);
3168
3169 break;
3170
3171 case TVGN_DROPHILITE:
3172 prevItem= TREEVIEW_ValidItem (infoPtr, infoPtr->dropItem);
3173
3174 if (prevItem)
3175 prevItem->state &= ~TVIS_DROPHILITED;
3176
3177 infoPtr->dropItem=(HTREEITEM)newSelect;
3178
3179 if (wineItem)
3180 wineItem->state |=TVIS_DROPHILITED;
3181
3182 break;
3183
3184 case TVGN_FIRSTVISIBLE:
3185// FIXME (treeview, "FIRSTVISIBLE not implemented\n");
3186 break;
3187 }
3188
3189 TREEVIEW_QueueRefresh (hwnd);
3190
3191// TRACE (treeview,"Leaving state %d\n", wineItem->state);
3192 return TRUE;
3193}
3194
3195/* FIXME: handle NM_KILLFocus enzo */
3196static LRESULT
3197TREEVIEW_SelectItem (HWND hwnd, WPARAM wParam, LPARAM lParam)
3198
3199{
3200 return TREEVIEW_DoSelectItem (hwnd, wParam, (HTREEITEM) lParam, TVC_UNKNOWN);
3201}
3202
3203
3204
3205
3206static LRESULT
3207TREEVIEW_GetFont (HWND hwnd, WPARAM wParam, LPARAM lParam)
3208
3209{
3210 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
3211
3212// TRACE (treeview,"%x\n",infoPtr->hFont);
3213 return infoPtr->hFont;
3214}
3215
3216static LRESULT
3217TREEVIEW_SetFont (HWND hwnd, WPARAM wParam, LPARAM lParam)
3218
3219{
3220 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
3221 TEXTMETRICA tm;
3222 LOGFONTA logFont;
3223 HFONT hFont, hOldFont;
3224 INT height;
3225 HDC hdc;
3226
3227// TRACE (treeview,"%x %lx\n",wParam, lParam);
3228
3229 infoPtr->hFont = (HFONT)wParam;
3230
3231 hFont = infoPtr->hFont ? infoPtr->hFont : GetStockObject (SYSTEM_FONT);
3232
3233 GetObjectA (infoPtr->hFont, sizeof (LOGFONTA), &logFont);
3234 logFont.lfWeight=FW_BOLD;
3235 infoPtr->hBoldFont = CreateFontIndirectA (&logFont);
3236
3237 hdc = GetDC (0);
3238 hOldFont = SelectObject (hdc, hFont);
3239 GetTextMetricsA (hdc, &tm);
3240 height= tm.tmHeight + tm.tmExternalLeading;
3241 if (height>infoPtr->uRealItemHeight)
3242 infoPtr->uRealItemHeight=height;
3243 SelectObject (hdc, hOldFont);
3244 ReleaseDC (0, hdc);
3245
3246 if (lParam)
3247 TREEVIEW_QueueRefresh (hwnd);
3248
3249 return 0;
3250}
3251
3252
3253
3254static LRESULT
3255TREEVIEW_VScroll (HWND hwnd, WPARAM wParam, LPARAM lParam)
3256
3257{
3258 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
3259 int maxHeight;
3260
3261// TRACE (treeview,"wp %x, lp %lx\n", wParam, lParam);
3262 if (!infoPtr->uInternalStatus & TV_VSCROLL) return FALSE;
3263
3264 switch (LOWORD (wParam)) {
3265 case SB_LINEUP:
3266 if (!infoPtr->cy) return FALSE;
3267 infoPtr->cy -= infoPtr->uRealItemHeight;
3268 if (infoPtr->cy < 0) infoPtr->cy=0;
3269 break;
3270 case SB_LINEDOWN:
3271 maxHeight=infoPtr->uTotalHeight-infoPtr->uVisibleHeight;
3272 if (infoPtr->cy == maxHeight) return FALSE;
3273 infoPtr->cy += infoPtr->uRealItemHeight;
3274 if (infoPtr->cy > maxHeight)
3275 infoPtr->cy = maxHeight;
3276 break;
3277 case SB_PAGEUP:
3278 if (!infoPtr->cy) return FALSE;
3279 infoPtr->cy -= infoPtr->uVisibleHeight;
3280 if (infoPtr->cy < 0) infoPtr->cy=0;
3281 break;
3282 case SB_PAGEDOWN:
3283 maxHeight=infoPtr->uTotalHeight-infoPtr->uVisibleHeight;
3284 if (infoPtr->cy == maxHeight) return FALSE;
3285 infoPtr->cy += infoPtr->uVisibleHeight;
3286 if (infoPtr->cy > maxHeight)
3287 infoPtr->cy = maxHeight;
3288 break;
3289 case SB_THUMBTRACK:
3290 infoPtr->cy = HIWORD (wParam);
3291 break;
3292
3293 }
3294
3295 TREEVIEW_QueueRefresh (hwnd);
3296 return TRUE;
3297}
3298
3299static LRESULT
3300TREEVIEW_HScroll (HWND hwnd, WPARAM wParam, LPARAM lParam)
3301{
3302 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
3303 int maxWidth;
3304
3305// TRACE (treeview,"wp %lx, lp %x\n", lParam, wParam);
3306
3307 if (!infoPtr->uInternalStatus & TV_HSCROLL) return FALSE;
3308
3309 switch (LOWORD (wParam)) {
3310 case SB_LINEUP:
3311 if (!infoPtr->cx) return FALSE;
3312 infoPtr->cx -= infoPtr->uRealItemHeight;
3313 if (infoPtr->cx < 0) infoPtr->cx=0;
3314 break;
3315 case SB_LINEDOWN:
3316 maxWidth=infoPtr->uTotalWidth-infoPtr->uVisibleWidth;
3317 if (infoPtr->cx == maxWidth) return FALSE;
3318 infoPtr->cx += infoPtr->uRealItemHeight; /*FIXME */
3319 if (infoPtr->cx > maxWidth)
3320 infoPtr->cx = maxWidth;
3321 break;
3322 case SB_PAGEUP:
3323 if (!infoPtr->cx) return FALSE;
3324 infoPtr->cx -= infoPtr->uVisibleWidth;
3325 if (infoPtr->cx < 0) infoPtr->cx=0;
3326 break;
3327 case SB_PAGEDOWN:
3328 maxWidth=infoPtr->uTotalWidth-infoPtr->uVisibleWidth;
3329 if (infoPtr->cx == maxWidth) return FALSE;
3330 infoPtr->cx += infoPtr->uVisibleWidth;
3331 if (infoPtr->cx > maxWidth)
3332 infoPtr->cx = maxWidth;
3333 break;
3334 case SB_THUMBTRACK:
3335 infoPtr->cx = HIWORD (wParam);
3336 break;
3337
3338 }
3339
3340 TREEVIEW_QueueRefresh (hwnd);
3341 return TRUE;
3342}
3343
3344
3345static LRESULT
3346TREEVIEW_KeyDown (HWND hwnd, WPARAM wParam, LPARAM lParam)
3347{
3348 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
3349 HTREEITEM hNewSelection = 0;
3350 INT scrollNeeds = -1;
3351 INT cyChangeNeeds = -1;
3352 INT prevSelect = (INT)infoPtr->selectedItem;
3353
3354 TREEVIEW_ITEM *prevItem =
3355 (prevSelect != 0 ) ?
3356 TREEVIEW_ValidItem (infoPtr, (HTREEITEM)prevSelect) :
3357 NULL;
3358
3359 TREEVIEW_ITEM *newItem = NULL;
3360
3361// TRACE (treeview,"%x %lx\n",wParam, lParam);
3362
3363 if (prevSelect == 0)
3364 return FALSE;
3365
3366 switch (wParam) {
3367 case VK_UP:
3368 newItem=TREEVIEW_GetPrevListItem (infoPtr, prevItem);
3369
3370 if (!newItem)
3371 newItem=& infoPtr->items[(INT)infoPtr->TopRootItem];
3372
3373 hNewSelection = newItem->hItem;
3374
3375 if (! newItem->visible)
3376 scrollNeeds = SB_LINEUP;
3377
3378 break;
3379
3380 case VK_DOWN:
3381 newItem=TREEVIEW_GetNextListItem (infoPtr, prevItem);
3382
3383 if (!newItem)
3384 newItem=prevItem;
3385
3386 hNewSelection = newItem->hItem;
3387
3388 if (! newItem->visible)
3389 scrollNeeds = SB_LINEDOWN;
3390
3391 break;
3392
3393 case VK_HOME:
3394 newItem = &infoPtr->items[(INT)infoPtr->TopRootItem];
3395 hNewSelection = newItem->hItem;
3396 cyChangeNeeds = 0;
3397 break;
3398
3399 case VK_END:
3400 newItem = &infoPtr->items[(INT)infoPtr->TopRootItem];
3401 newItem = TREEVIEW_GetLastListItem (infoPtr, newItem);
3402 hNewSelection = newItem->hItem;
3403
3404 if (! newItem->visible)
3405 cyChangeNeeds = infoPtr->uTotalHeight-infoPtr->uVisibleHeight;
3406
3407 break;
3408
3409 case VK_LEFT:
3410 if ( (prevItem->cChildren > 0) && (prevItem->state & TVIS_EXPANDED) )
3411 {
3412 TREEVIEW_Expand(hwnd, TVE_COLLAPSE, prevSelect );
3413 }
3414 else if ((INT)prevItem->parent)
3415 {
3416 newItem = (& infoPtr->items[(INT)prevItem->parent]);
3417 if (! newItem->visible)
3418 /* FIXME find a way to make this item the first visible... */
3419 newItem = NULL;
3420
3421 hNewSelection = newItem->hItem;
3422 }
3423
3424 break;
3425
3426 case VK_RIGHT:
3427 if ( ( prevItem->cChildren > 0) ||
3428 ( prevItem->cChildren == I_CHILDRENCALLBACK))
3429 {
3430 if (! (prevItem->state & TVIS_EXPANDED))
3431 TREEVIEW_Expand(hwnd, TVE_EXPAND, prevSelect );
3432 else
3433 {
3434 newItem = (& infoPtr->items[(INT)prevItem->firstChild]);
3435 hNewSelection = newItem->hItem;
3436 }
3437 }
3438
3439 break;
3440
3441 case VK_ADD:
3442 if (! (prevItem->state & TVIS_EXPANDED))
3443 TREEVIEW_Expand(hwnd, TVE_EXPAND, prevSelect );
3444 break;
3445
3446 case VK_SUBTRACT:
3447 if (prevItem->state & TVIS_EXPANDED)
3448 TREEVIEW_Expand(hwnd, TVE_COLLAPSE, prevSelect );
3449 break;
3450
3451 case VK_PRIOR:
3452
3453 newItem=TREEVIEW_GetListItem(
3454 infoPtr,
3455 prevItem,
3456 -1*(TREEVIEW_GetVisibleCount(hwnd,0,0)-3));
3457 if (!newItem)
3458 newItem=prevItem;
3459
3460 hNewSelection = newItem->hItem;
3461
3462 if (! newItem->visible)
3463 scrollNeeds = SB_PAGEUP;
3464
3465 break;
3466
3467 case VK_NEXT:
3468 newItem=TREEVIEW_GetListItem(
3469 infoPtr,
3470 prevItem,
3471 TREEVIEW_GetVisibleCount(hwnd,0,0)-3);
3472
3473 if (!newItem)
3474 newItem=prevItem;
3475
3476 hNewSelection = newItem->hItem;
3477
3478 if (! newItem->visible)
3479 scrollNeeds = SB_PAGEDOWN;
3480
3481 break;
3482
3483 case VK_BACK:
3484
3485 case VK_RETURN:
3486
3487 default:
3488// FIXME (treeview, "%x not implemented\n", wParam);
3489 break;
3490 }
3491
3492 if (hNewSelection)
3493 {
3494/*
3495 This works but does not send notification...
3496
3497 prevItem->state &= ~TVIS_SELECTED;
3498 newItem->state |= TVIS_SELECTED;
3499 infoPtr->selectedItem = hNewSelection;
3500 TREEVIEW_QueueRefresh (hwnd);
3501*/
3502
3503 if ( TREEVIEW_DoSelectItem(
3504 hwnd,
3505 TVGN_CARET,
3506 (HTREEITEM)hNewSelection,
3507 TVC_BYKEYBOARD))
3508 {
3509 /* If selection change is allowed for the new item, perform scrolling */
3510 if (scrollNeeds != -1)
3511 TREEVIEW_VScroll(hwnd, scrollNeeds, 0);
3512
3513 if (cyChangeNeeds != -1)
3514 infoPtr->cy = cyChangeNeeds;
3515
3516 /* FIXME: Something happen in the load the in the two weeks before
3517 april 1st 1999 which makes this SetFocus mandatory otherwise, the focus
3518 is lost... However the SetFocus should not be required...*/
3519
3520 SetFocus(hwnd);
3521 }
3522 }
3523
3524 return FALSE;
3525}
3526
3527static LRESULT
3528TREEVIEW_GetScrollTime (HWND hwnd)
3529{
3530 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
3531 return infoPtr->uScrollTime;
3532}
3533
3534static LRESULT
3535TREEVIEW_SetScrollTime (HWND hwnd, UINT uScrollTime)
3536{
3537 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
3538 UINT uOldScrollTime = infoPtr->uScrollTime;
3539 infoPtr->uScrollTime = min (uScrollTime, 100);
3540 return uOldScrollTime;
3541}
3542
3543static LRESULT WINAPI
3544TREEVIEW_WindowProc (HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
3545{
3546 switch (uMsg) {
3547 case TVM_INSERTITEMA:
3548 return TREEVIEW_InsertItemA (hwnd, wParam, lParam);
3549
3550 case TVM_INSERTITEMW:
3551 return TREEVIEW_InsertItemW(hwnd,wParam,lParam);
3552
3553 case TVM_DELETEITEM:
3554 return TREEVIEW_DeleteItem (hwnd, wParam, lParam);
3555
3556 case TVM_EXPAND:
3557 return TREEVIEW_Expand (hwnd, wParam, lParam);
3558
3559 case TVM_GETITEMRECT:
3560 return TREEVIEW_GetItemRect (hwnd, wParam, lParam);
3561
3562 case TVM_GETCOUNT:
3563 return TREEVIEW_GetCount (hwnd, wParam, lParam);
3564
3565 case TVM_GETINDENT:
3566 return TREEVIEW_GetIndent (hwnd);
3567
3568 case TVM_SETINDENT:
3569 return TREEVIEW_SetIndent (hwnd, wParam);
3570
3571 case TVM_GETIMAGELIST:
3572 return TREEVIEW_GetImageList (hwnd, wParam, lParam);
3573
3574 case TVM_SETIMAGELIST:
3575 return TREEVIEW_SetImageList (hwnd, wParam, lParam);
3576
3577 case TVM_GETNEXTITEM:
3578 return TREEVIEW_GetNextItem (hwnd, wParam, lParam);
3579
3580 case TVM_SELECTITEM:
3581 return TREEVIEW_SelectItem (hwnd, wParam, lParam);
3582
3583 case TVM_GETITEMA:
3584 return TREEVIEW_GetItemA (hwnd, wParam, lParam);
3585
3586 case TVM_GETITEMW:
3587// FIXME (treeview, "Unimplemented msg TVM_GETITEMW\n");
3588 return 0;
3589
3590 case TVM_SETITEMA:
3591 return TREEVIEW_SetItemA (hwnd, wParam, lParam);
3592
3593 case TVM_SETITEMW:
3594// FIXME (treeview, "Unimplemented msg TVM_SETITEMW\n");
3595 return 0;
3596
3597 case TVM_EDITLABELA:
3598// FIXME (treeview, "Unimplemented msg TVM_EDITLABELA \n");
3599 return 0;
3600
3601 case TVM_EDITLABELW:
3602// FIXME (treeview, "Unimplemented msg TVM_EDITLABELW \n");
3603 return 0;
3604
3605 case TVM_GETEDITCONTROL:
3606 return TREEVIEW_GetEditControl (hwnd);
3607
3608 case TVM_GETVISIBLECOUNT:
3609 return TREEVIEW_GetVisibleCount (hwnd, wParam, lParam);
3610
3611 case TVM_HITTEST:
3612 return TREEVIEW_HitTest (hwnd, lParam);
3613
3614 case TVM_CREATEDRAGIMAGE:
3615 return TREEVIEW_CreateDragImage (hwnd, wParam, lParam);
3616
3617 case TVM_SORTCHILDREN:
3618 //@@@PH 1999/10/25 TREEVIEW_SortChildrenCB is wrong
3619 return TREEVIEW_SortChildren(hwnd, wParam, lParam);
3620
3621 case TVM_ENSUREVISIBLE:
3622// FIXME (treeview, "Unimplemented msg TVM_ENSUREVISIBLE\n");
3623 return 0;
3624
3625 case TVM_SORTCHILDRENCB:
3626 return TREEVIEW_SortChildrenCB(hwnd, wParam, lParam);
3627
3628 case TVM_ENDEDITLABELNOW:
3629 return TREEVIEW_EndEditLabelNow (hwnd, wParam, lParam);
3630
3631 case TVM_GETISEARCHSTRINGA:
3632// FIXME (treeview, "Unimplemented msg TVM_GETISEARCHSTRINGA\n");
3633 return 0;
3634
3635 case TVM_GETISEARCHSTRINGW:
3636// FIXME (treeview, "Unimplemented msg TVM_GETISEARCHSTRINGW\n");
3637 return 0;
3638
3639 case TVM_GETTOOLTIPS:
3640 return TREEVIEW_GetToolTips (hwnd);
3641
3642 case TVM_SETTOOLTIPS:
3643 return TREEVIEW_SetToolTips (hwnd, wParam);
3644
3645 case TVM_SETINSERTMARK:
3646// FIXME (treeview, "Unimplemented msg TVM_SETINSERTMARK\n");
3647 return 0;
3648
3649 case TVM_SETITEMHEIGHT:
3650 return TREEVIEW_SetItemHeight (hwnd, wParam);
3651
3652 case TVM_GETITEMHEIGHT:
3653 return TREEVIEW_GetItemHeight (hwnd);
3654
3655 case TVM_SETBKCOLOR:
3656 return TREEVIEW_SetBkColor (hwnd, wParam, lParam);
3657
3658 case TVM_SETTEXTCOLOR:
3659 return TREEVIEW_SetTextColor (hwnd, wParam, lParam);
3660
3661 case TVM_GETBKCOLOR:
3662 return TREEVIEW_GetBkColor (hwnd);
3663
3664 case TVM_GETTEXTCOLOR:
3665 return TREEVIEW_GetTextColor (hwnd);
3666
3667 case TVM_SETSCROLLTIME:
3668 return TREEVIEW_SetScrollTime (hwnd, (UINT)wParam);
3669
3670 case TVM_GETSCROLLTIME:
3671 return TREEVIEW_GetScrollTime (hwnd);
3672
3673 case TVM_GETITEMSTATE:
3674 return TREEVIEW_GetItemState (hwnd,wParam, lParam);
3675
3676 case TVM_GETLINECOLOR:
3677 return TREEVIEW_GetLineColor (hwnd,wParam, lParam);
3678
3679 case TVM_SETLINECOLOR:
3680 return TREEVIEW_SetLineColor (hwnd,wParam, lParam);
3681
3682 case TVM_SETINSERTMARKCOLOR:
3683// FIXME (treeview, "Unimplemented msg TVM_SETINSERTMARKCOLOR\n");
3684 return 0;
3685
3686 case TVM_SETUNICODEFORMAT:
3687// FIXME (treeview, "Unimplemented msg TVM_SETUNICODEFORMAT\n");
3688 return 0;
3689
3690 case TVM_GETUNICODEFORMAT:
3691// FIXME (treeview, "Unimplemented msg TVM_GETUNICODEFORMAT\n");
3692 return 0;
3693
3694 case WM_COMMAND:
3695 return TREEVIEW_Command (hwnd, wParam, lParam);
3696
3697 case WM_CREATE:
3698 return TREEVIEW_Create (hwnd, wParam, lParam);
3699
3700 case WM_DESTROY:
3701 return TREEVIEW_Destroy (hwnd);
3702
3703/* case WM_ENABLE: */
3704
3705 case WM_ERASEBKGND:
3706 return TREEVIEW_EraseBackground (hwnd, wParam, lParam);
3707
3708 case WM_GETDLGCODE:
3709 return DLGC_WANTARROWS | DLGC_WANTCHARS;
3710
3711 case WM_PAINT:
3712 return TREEVIEW_Paint (hwnd, wParam, lParam);
3713
3714 case WM_GETFONT:
3715 return TREEVIEW_GetFont (hwnd, wParam, lParam);
3716
3717 case WM_SETFONT:
3718 return TREEVIEW_SetFont (hwnd, wParam, lParam);
3719
3720 case WM_KEYDOWN:
3721 return TREEVIEW_KeyDown (hwnd, wParam, lParam);
3722
3723 case WM_SETFOCUS:
3724 return TREEVIEW_SetFocus (hwnd, wParam, lParam);
3725
3726 case WM_KILLFOCUS:
3727 return TREEVIEW_KillFocus (hwnd, wParam, lParam);
3728
3729 case WM_LBUTTONDOWN:
3730 return TREEVIEW_LButtonDown (hwnd, wParam, lParam);
3731
3732 case WM_LBUTTONUP:
3733 return TREEVIEW_LButtonUp (hwnd, wParam, lParam);
3734
3735 case WM_LBUTTONDBLCLK:
3736 return TREEVIEW_LButtonDoubleClick (hwnd, wParam, lParam);
3737
3738 case WM_RBUTTONDOWN:
3739 return TREEVIEW_RButtonDown (hwnd, wParam, lParam);
3740
3741 case WM_RBUTTONUP:
3742 return TREEVIEW_RButtonUp (hwnd, wParam, lParam);
3743
3744 case WM_MOUSEMOVE:
3745 return TREEVIEW_MouseMove (hwnd, wParam, lParam);
3746
3747 case WM_STYLECHANGED:
3748 return TREEVIEW_StyleChanged (hwnd, wParam, lParam);
3749
3750/* case WM_SYSCOLORCHANGE: */
3751/* case WM_SETREDRAW: */
3752
3753 case WM_TIMER:
3754 return TREEVIEW_HandleTimer (hwnd, wParam, lParam);
3755
3756 case WM_SIZE:
3757 return TREEVIEW_Size (hwnd, wParam,lParam);
3758
3759 case WM_HSCROLL:
3760 return TREEVIEW_HScroll (hwnd, wParam, lParam);
3761
3762 case WM_VSCROLL:
3763 return TREEVIEW_VScroll (hwnd, wParam, lParam);
3764
3765 case WM_DRAWITEM:
3766// printf ("drawItem\n");
3767 return DefWindowProcA (hwnd, uMsg, wParam, lParam);
3768
3769 default:
3770// if (uMsg >= WM_USER)
3771// FIXME (treeview, "Unknown msg %04x wp=%08x lp=%08lx\n",
3772// uMsg, wParam, lParam);
3773 return DefWindowProcA (hwnd, uMsg, wParam, lParam);
3774 }
3775 return 0;
3776}
3777
3778
3779VOID
3780TREEVIEW_Register (VOID)
3781{
3782 WNDCLASSA wndClass;
3783
3784// TRACE (treeview,"\n");
3785
3786//SvL: Don't check this now
3787// if (GlobalFindAtomA (WC_TREEVIEWA)) return;
3788
3789 ZeroMemory (&wndClass, sizeof(WNDCLASSA));
3790 wndClass.style = CS_GLOBALCLASS | CS_DBLCLKS;
3791 wndClass.lpfnWndProc = (WNDPROC)TREEVIEW_WindowProc;
3792 wndClass.cbClsExtra = 0;
3793 wndClass.cbWndExtra = sizeof(TREEVIEW_INFO *);
3794 wndClass.hCursor = LoadCursorA (0, IDC_ARROWA);
3795 wndClass.hbrBackground = 0;
3796 wndClass.lpszClassName = WC_TREEVIEWA;
3797
3798 RegisterClassA (&wndClass);
3799}
3800
3801
3802VOID
3803TREEVIEW_Unregister (VOID)
3804{
3805 if (GlobalFindAtomA (WC_TREEVIEWA))
3806 UnregisterClassA (WC_TREEVIEWA, (HINSTANCE)NULL);
3807}
3808
Note: See TracBrowser for help on using the repository browser.