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

Last change on this file since 1431 was 1431, checked in by sandervl, 26 years ago

RegisterClass even if atom exists

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