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

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

comctl32 merged with WINE 991031

File size: 104.7 KB
Line 
1/* $Id: treeview.c,v 1.13 1999-11-02 21:44:04 achimha Exp $ */
2/* Treeview control
3 *
4 * Copyright 1998 Eric Kohl <ekohl@abo.rhein-zeitung.de>
5 * Copyright 1998,1999 Alex Priem <alexp@sci.kun.nl>
6 * Copyright 1999 Sylvain St-Germain
7 * Copyright 1999 Achim Hasenmueller
8 * Copyright 1999 Christoph Bratschi
9 *
10 *
11 * TODO:
12 * Using DPA to store the item ptr would be good.
13 * Node label edition is implemented but something appened in wine in the
14 * two last weeks of march 99 that broke it.
15 * refreshtreeview:
16 -small array containing info about positions.
17 -better implementation of RefreshItem:
18 1) draw lines between parents
19 2) draw items
20 3) draw lines from parent<->items.
21 -implement partial drawing?
22 * -drag&drop: TVM_CREATEDRAGIMAGE should create drag bitmap.
23 * -scrollbars: horizontal scrollbar doesn't work.
24 * -Unicode messages
25 * -check custom draw
26 * -I_CHILDRENCALLBACK
27 * FIXME: check fontsize. (uRealItemHeight)
28 * test focusItem (redraw in different color)
29 uHotItem
30 Edit: needs timer
31 better implementation.
32 * WM_HSCROLL is broken.
33 * use separate routine to get item text/image.
34 *
35 * Separate drawing/calculation.
36 *
37 * FIXMEs (for personal use)
38 Expand: -ctlmacro expands twice ->toggle.
39 -DblClick: ctlmacro.exe's NM_DBLCLK seems to go wrong (returns FALSE).
40 -treehelper: stack corruption makes big window.
41
42 */
43
44/* WINE 991031 level */
45
46#include <string.h>
47#include "winbase.h"
48#include "wingdi.h"
49#include "commctrl.h"
50#include "comctl32.h"
51#include "treeview.h"
52
53
54/* ffs should be in <string.h>. */
55
56/* Defines, since they do not need to return previous state, and nr
57 * has no side effects in this file.
58 */
59#define tv_test_bit(nr,bf) (((LPBYTE)bf)[nr>>3]&(1<<(nr&7)))
60#define tv_set_bit(nr,bf) ((LPBYTE)bf)[nr>>3]|=(1<<(nr&7))
61#define tv_clear_bit(nr,bf) ((LPBYTE)bf)[nr>>3]&=~(1<<(nr&7))
62
63
64#define TREEVIEW_GetInfoPtr(hwnd) \
65 ((TREEVIEW_INFO *) GetWindowLongA( hwnd, 0))
66
67static BOOL
68TREEVIEW_SendSimpleNotify (HWND hwnd, UINT code);
69static BOOL
70TREEVIEW_SendTreeviewNotify (HWND hwnd, UINT code, UINT action,
71 HTREEITEM oldItem, HTREEITEM newItem);
72static BOOL
73TREEVIEW_SendTreeviewDnDNotify (HWND hwnd, UINT code, HTREEITEM dragItem,
74 POINT pt);
75static BOOL
76TREEVIEW_SendDispInfoNotify (HWND hwnd, TREEVIEW_ITEM *wineItem,
77 UINT code, UINT what);
78static BOOL
79TREEVIEW_SendCustomDrawNotify (HWND hwnd, DWORD dwDrawStage, HDC hdc,
80 RECT rc);
81static BOOL
82TREEVIEW_SendCustomDrawItemNotify (HWND hwnd, HDC hdc,
83 TREEVIEW_ITEM *tvItem, UINT uItemDrawState);
84static LRESULT
85TREEVIEW_DoSelectItem (HWND hwnd, INT action, HTREEITEM newSelect, INT cause);
86static void
87TREEVIEW_Refresh (HWND hwnd);
88
89static LRESULT CALLBACK
90TREEVIEW_Edit_SubclassProc (HWND hwnd, UINT uMsg, WPARAM wParam,
91 LPARAM lParam);
92
93LRESULT WINAPI
94TREEVIEW_EndEditLabelNow (HWND hwnd, WPARAM wParam, LPARAM lParam);
95
96
97
98
99/* helper functions. Work with the assumption that validity of operands
100 is checked beforehand, and that tree state is valid. */
101
102/* FIXME: MS documentation says `GetNextVisibleItem' returns NULL
103 if not succesfull'. Probably only applies to derefencing infoPtr
104 (ie we are offered a valid treeview structure)
105 and not whether there is a next `visible' child.
106 FIXME: check other failures.
107 */
108
109/***************************************************************************
110 * This method returns the TREEVIEW_ITEM object given the handle
111 */
112static TREEVIEW_ITEM* TREEVIEW_ValidItem(
113 TREEVIEW_INFO *infoPtr,
114 HTREEITEM handle)
115{
116 if ((!handle) || (handle>infoPtr->uMaxHandle))
117 return NULL;
118
119 if (tv_test_bit ((INT)handle, infoPtr->freeList))
120 return NULL;
121
122 return &infoPtr->items[(INT)handle];
123}
124
125/***************************************************************************
126 * This method returns the last expanded child item of a tree node
127 */
128static TREEVIEW_ITEM *TREEVIEW_GetLastListItem(
129 TREEVIEW_INFO *infoPtr,
130 TREEVIEW_ITEM *tvItem)
131
132{
133 TREEVIEW_ITEM *wineItem = tvItem;
134
135 /*
136 * Get this item last sibling
137 */
138 while (wineItem->sibling)
139 wineItem=& infoPtr->items [(INT)wineItem->sibling];
140
141 /*
142 * If the last sibling has expanded children, restart.
143 */
144 if ( ( wineItem->cChildren > 0 ) && ( wineItem->state & TVIS_EXPANDED) )
145 return TREEVIEW_GetLastListItem(
146 infoPtr,
147 &(infoPtr->items[(INT)wineItem->firstChild]));
148
149 return wineItem;
150}
151
152/***************************************************************************
153 * This method returns the previous physical item in the list not
154 * considering the tree hierarchy.
155 */
156static TREEVIEW_ITEM *TREEVIEW_GetPrevListItem(
157 TREEVIEW_INFO *infoPtr,
158 TREEVIEW_ITEM *tvItem)
159{
160 if (tvItem->upsibling)
161 {
162 /*
163 * This item has a upsibling, get the last item. Since, GetLastListItem
164 * first looks at siblings, we must feed it with the first child.
165 */
166 TREEVIEW_ITEM *upItem = &infoPtr->items[(INT)tvItem->upsibling];
167
168 if ( ( upItem->cChildren > 0 ) && ( upItem->state & TVIS_EXPANDED) )
169 return TREEVIEW_GetLastListItem(
170 infoPtr,
171 &infoPtr->items[(INT)upItem->firstChild]);
172 else
173 return upItem;
174 }
175 else
176 {
177 /*
178 * this item does not have a upsibling, get the parent
179 */
180 if (tvItem->parent)
181 return &infoPtr->items[(INT)tvItem->parent];
182 }
183
184 return NULL;
185}
186
187
188/***************************************************************************
189 * This method returns the next physical item in the treeview not
190 * considering the tree hierarchy.
191 */
192static TREEVIEW_ITEM *TREEVIEW_GetNextListItem(
193 TREEVIEW_INFO *infoPtr,
194 TREEVIEW_ITEM *tvItem)
195{
196 TREEVIEW_ITEM *wineItem = NULL;
197
198 /*
199 * If this item has children and is expanded, return the first child
200 */
201 if ((tvItem->firstChild) && (tvItem->state & TVIS_EXPANDED))
202 return (& infoPtr->items[(INT)tvItem->firstChild]);
203
204
205 /*
206 * try to get the sibling
207 */
208 if (tvItem->sibling)
209 return (& infoPtr->items[(INT)tvItem->sibling]);
210
211 /*
212 * Otherwise, get the parent's sibling.
213 */
214 wineItem=tvItem;
215 while (wineItem->parent) {
216 wineItem=& infoPtr->items [(INT)wineItem->parent];
217 if (wineItem->sibling)
218 return (& infoPtr->items [(INT)wineItem->sibling]);
219 }
220
221 return NULL;
222}
223
224/***************************************************************************
225 * This method returns the nth item starting at the given item. It returns
226 * the last item (or first) we we run out of items.
227 *
228 * Will scroll backward if count is <0.
229 * forward if count is >0.
230 */
231static TREEVIEW_ITEM *TREEVIEW_GetListItem(
232 TREEVIEW_INFO *infoPtr,
233 TREEVIEW_ITEM *tvItem,
234 LONG count)
235{
236 TREEVIEW_ITEM *previousItem = NULL;
237 TREEVIEW_ITEM *wineItem = tvItem;
238 LONG iter = 0;
239
240 if (count > 0)
241 {
242 /* Find count item downward */
243 while ((iter++ < count) && (wineItem != NULL))
244 {
245 /* Keep a pointer to the previous in case we ask for more than we got */
246 previousItem = wineItem;
247 wineItem = TREEVIEW_GetNextListItem(infoPtr, wineItem);
248 }
249
250 if (wineItem == NULL)
251 wineItem = previousItem;
252 }
253 else if (count < 0)
254 {
255 /* Find count item upward */
256 while ((iter-- > count) && (wineItem != NULL))
257 {
258 /* Keep a pointer to the previous in case we ask for more than we got */
259 previousItem = wineItem;
260 wineItem = TREEVIEW_GetPrevListItem(infoPtr, wineItem);
261 }
262
263 if (wineItem == NULL)
264 wineItem = previousItem;
265 }
266 else
267 wineItem = NULL;
268
269 return wineItem;
270}
271
272
273/***************************************************************************
274 * This method
275 */
276static void TREEVIEW_RemoveAllChildren(
277 HWND hwnd,
278 TREEVIEW_ITEM *parentItem)
279{
280 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
281 TREEVIEW_ITEM *killItem;
282 INT kill;
283
284 kill=(INT)parentItem->firstChild;
285 while (kill) {
286 tv_set_bit ( kill, infoPtr->freeList);
287 killItem=& infoPtr->items[kill];
288 if (killItem->pszText!=LPSTR_TEXTCALLBACKA)
289 COMCTL32_Free (killItem->pszText);
290 TREEVIEW_SendTreeviewNotify (hwnd, TVN_DELETEITEM, 0, (HTREEITEM)kill, 0);
291 if (killItem->firstChild)
292 TREEVIEW_RemoveAllChildren (hwnd, killItem);
293 kill=(INT)killItem->sibling;
294 }
295
296 if (parentItem->cChildren>0) {
297 infoPtr->uNumItems -= parentItem->cChildren;
298 parentItem->firstChild = 0;
299 parentItem->cChildren = 0;
300 }
301
302}
303
304
305static void
306TREEVIEW_RemoveItem (HWND hwnd, TREEVIEW_ITEM *wineItem)
307
308{
309 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
310 TREEVIEW_ITEM *parentItem, *upsiblingItem, *siblingItem;
311 INT iItem;
312
313 iItem=(INT)wineItem->hItem;
314 tv_set_bit(iItem,infoPtr->freeList);
315 infoPtr->uNumItems--;
316 parentItem=NULL;
317 if (wineItem->pszText!=LPSTR_TEXTCALLBACKA)
318 COMCTL32_Free (wineItem->pszText);
319
320 TREEVIEW_SendTreeviewNotify (hwnd, TVN_DELETEITEM, 0, (HTREEITEM)iItem, 0);
321
322 if (wineItem->firstChild)
323 TREEVIEW_RemoveAllChildren (hwnd,wineItem);
324
325 if (wineItem->parent) {
326 parentItem=& infoPtr->items [(INT)wineItem->parent];
327 switch (parentItem->cChildren) {
328 case I_CHILDRENCALLBACK:
329// FIXME (treeview,"we don't handle I_CHILDRENCALLBACK yet\n");
330 break;
331 case 1:
332 parentItem->cChildren=0;
333 parentItem->firstChild=0;
334 return;
335 default:
336 parentItem->cChildren--;
337 if ((INT)parentItem->firstChild==iItem)
338 parentItem->firstChild=wineItem->sibling;
339 }
340 }
341
342 if (iItem==(INT)infoPtr->TopRootItem)
343 infoPtr->TopRootItem=(HTREEITEM)wineItem->sibling;
344 if (wineItem->upsibling) {
345 upsiblingItem=& infoPtr->items [(INT)wineItem->upsibling];
346 upsiblingItem->sibling=wineItem->sibling;
347 }
348 if (wineItem->sibling) {
349 siblingItem=& infoPtr->items [(INT)wineItem->sibling];
350 siblingItem->upsibling=wineItem->upsibling;
351 }
352}
353
354
355
356
357
358/* Note:TREEVIEW_RemoveTree doesn't remove infoPtr itself */
359
360static void TREEVIEW_RemoveTree (HWND hwnd)
361
362{
363 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
364 TREEVIEW_ITEM *killItem;
365 int i;
366
367 for (i=1; i<=(INT)infoPtr->uMaxHandle; i++)
368 if (!tv_test_bit (i, infoPtr->freeList)) {
369 killItem=& infoPtr->items [i];
370 if (killItem->pszText!=LPSTR_TEXTCALLBACKA)
371 COMCTL32_Free (killItem->pszText);
372 TREEVIEW_SendTreeviewNotify
373 (hwnd, TVN_DELETEITEM, 0, killItem->hItem, 0);
374 }
375
376 if (infoPtr->uNumPtrsAlloced) {
377 COMCTL32_Free (infoPtr->items);
378 COMCTL32_Free (infoPtr->freeList);
379 infoPtr->uNumItems=0;
380 infoPtr->uNumPtrsAlloced=0;
381 infoPtr->uMaxHandle=0;
382 }
383}
384
385
386
387
388
389
390
391static LRESULT
392TREEVIEW_GetImageList (HWND hwnd, WPARAM wParam, LPARAM lParam)
393{
394 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
395
396// TRACE (treeview,"\n");
397 if (infoPtr==NULL) return 0;
398
399 if ((INT)wParam == TVSIL_NORMAL)
400 return (LRESULT) infoPtr->himlNormal;
401 if ((INT)wParam == TVSIL_STATE)
402 return (LRESULT) infoPtr->himlState;
403
404 return 0;
405}
406
407static LRESULT
408TREEVIEW_SetImageList (HWND hwnd, WPARAM wParam, LPARAM lParam)
409{
410 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
411 HIMAGELIST himlTemp;
412
413// TRACE (treeview,"\n");
414 switch ((INT)wParam) {
415 case TVSIL_NORMAL:
416 himlTemp = infoPtr->himlNormal;
417 infoPtr->himlNormal = (HIMAGELIST)lParam;
418 return (LRESULT)himlTemp;
419
420 case TVSIL_STATE:
421 himlTemp = infoPtr->himlState;
422 infoPtr->himlState = (HIMAGELIST)lParam;
423 return (LRESULT)himlTemp;
424 }
425
426 return (LRESULT)NULL;
427}
428
429
430
431static LRESULT
432TREEVIEW_SetItemHeight (HWND hwnd, WPARAM wParam)
433{
434 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
435 INT cx,cy,prevHeight=infoPtr->uItemHeight;
436 HDC hdc;
437
438// TRACE (treeview,"\n");
439 if (wParam==-1) {
440 hdc=GetDC (hwnd);
441 infoPtr->uItemHeight=-1;
442 return prevHeight;
443 }
444
445 ImageList_GetIconSize (infoPtr->himlNormal, &cx, &cy);
446
447 if (wParam>cy) cy=wParam;
448 infoPtr->uItemHeight=cy;
449
450 if (!( GetWindowLongA( hwnd, GWL_STYLE) & TVS_NONEVENHEIGHT))
451 infoPtr->uItemHeight = (INT) wParam & 0xfffffffe;
452 return prevHeight;
453}
454
455static LRESULT
456TREEVIEW_GetItemHeight (HWND hwnd)
457{
458 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
459
460// TRACE (treeview,"\n");
461 return infoPtr->uItemHeight;
462}
463
464static LRESULT
465TREEVIEW_GetLineColor (HWND hwnd, WPARAM wParam, LPARAM lParam)
466{
467 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
468
469 //TRACE("\n");
470 return (LRESULT) infoPtr->clrLine;
471}
472
473static LRESULT
474TREEVIEW_SetLineColor (HWND hwnd, WPARAM wParam, LPARAM lParam)
475{
476 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
477 COLORREF prevColor=infoPtr->clrLine;
478
479 //TRACE("\n");
480 infoPtr->clrLine=(COLORREF) lParam;
481 return (LRESULT) prevColor;
482}
483
484static LRESULT
485TREEVIEW_SetTextColor (HWND hwnd, WPARAM wParam, LPARAM lParam)
486{
487 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
488 COLORREF prevColor=infoPtr->clrText;
489
490// TRACE (treeview,"\n");
491 infoPtr->clrText=(COLORREF) lParam;
492 return (LRESULT) prevColor;
493}
494
495static LRESULT
496TREEVIEW_GetBkColor (HWND hwnd)
497{
498 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
499
500 TRACE("\n");
501 return (LRESULT) infoPtr->clrBk;
502}
503
504static LRESULT
505TREEVIEW_SetBkColor (HWND hwnd, WPARAM wParam, LPARAM lParam)
506{
507 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
508 COLORREF prevColor=infoPtr->clrBk;
509
510 TRACE("\n");
511 infoPtr->clrBk=(COLORREF) lParam;
512 return (LRESULT) prevColor;
513}
514
515static LRESULT
516TREEVIEW_GetTextColor (HWND hwnd)
517{
518 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
519
520 TRACE("\n");
521 return (LRESULT) infoPtr->clrText;
522}
523
524
525/* cdmode: custom draw mode as received from app. in first NMCUSTOMDRAW
526 notification */
527
528#define TREEVIEW_LEFT_MARGIN 8
529
530
531static void
532TREEVIEW_DrawItem (HWND hwnd, HDC hdc, TREEVIEW_ITEM *wineItem)
533{
534 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
535 DWORD dwStyle = GetWindowLongA(hwnd,GWL_STYLE);
536 INT center,xpos,cx,cy, cditem, drawmode;
537 HFONT hOldFont;
538 UINT uTextJustify = DT_LEFT;
539 RECT r;
540
541
542 if (wineItem->state & TVIS_BOLD)
543 hOldFont = SelectObject (hdc, infoPtr->hBoldFont);
544 else
545 hOldFont = SelectObject (hdc, infoPtr->hFont);
546
547 cditem=0;
548 //TRACE ("cdmode:%x\n",infoPtr->cdmode);
549 if (infoPtr->cdmode & CDRF_NOTIFYITEMDRAW) {
550 drawmode=CDDS_ITEMPREPAINT;
551
552 if (infoPtr->cdmode & CDRF_NOTIFYSUBITEMDRAW)
553 drawmode|=CDDS_SUBITEM;
554
555 cditem=TREEVIEW_SendCustomDrawItemNotify (hwnd, hdc, wineItem, drawmode);
556
557// TRACE (treeview,"cditem:%d\n",cditem);
558
559 if (cditem & CDRF_SKIPDEFAULT)
560 return;
561 }
562
563 /*
564 * Set drawing starting points
565 */
566 r = wineItem->rect; /* this item rectangle */
567 center = (r.top+r.bottom)/2; /* this item vertical center */
568 xpos = r.left + TREEVIEW_LEFT_MARGIN;/* horizontal starting point */
569
570 /*
571 * Display the tree hierarchy
572 */
573 if ( dwStyle & TVS_HASLINES)
574 {
575 /*
576 * Write links to parent node
577 * we draw the L starting from the child to the parent
578 *
579 * points[0] is attached to the current item
580 * points[1] is the L corner
581 * points[2] is attached to the parent or the up sibling
582 */
583 if ( dwStyle & TVS_LINESATROOT)
584 {
585 TREEVIEW_ITEM *upNode = NULL;
586 BOOL hasParentOrSibling = TRUE;
587 RECT upRect = {0,0,0,0};
588 HPEN hOldPen, hnewPen;
589 POINT points[3];
590 /*
591 * determine the target location of the line at root, either be linked
592 * to the up sibling or to the parent node.
593 */
594 if (wineItem->upsibling)
595 upNode = TREEVIEW_ValidItem (infoPtr, wineItem->upsibling);
596 else if (wineItem->parent)
597 upNode = TREEVIEW_ValidItem (infoPtr, wineItem->parent);
598 else
599 hasParentOrSibling = FALSE;
600
601 if (upNode)
602 upRect = upNode->rect;
603
604 if ( wineItem->iLevel == 0 )
605 {
606 points[2].x = points[1].x = upRect.left+8;
607 points[0].x = points[2].x + 10;
608 points[2].y = upRect.bottom-3;
609 points[1].y = points[0].y = center;
610 }
611 else
612 {
613 points[2].x = points[1].x = 8 + (20*wineItem->iLevel);
614 points[2].y = ( upNode->cChildren == 0) ?
615 upRect.top : /* is linked to the "L" above */
616 ( wineItem->upsibling != NULL) ?
617 upRect.bottom-3: /* is linked to an icon */
618 upRect.bottom+1; /* is linked to a +/- box */
619 points[1].y = points[0].y = center;
620 points[0].x = points[1].x + 10;
621 }
622
623 /*
624 * Get a dotted pen
625 */
626 hnewPen = CreatePen(PS_DOT, 0, infoPtr->clrLine);
627 hOldPen = SelectObject( hdc, hnewPen );
628
629 if (hasParentOrSibling)
630 Polyline (hdc,points,3);
631 else
632 Polyline (hdc,points,2);
633
634 DeleteObject(hnewPen);
635 SelectObject(hdc, hOldPen);
636 }
637 }
638
639 /*
640 * Display the (+/-) signs
641 */
642 if (wineItem->iLevel != 0)/* update position only for non root node */
643 xpos+=(5*wineItem->iLevel);
644
645 if (( dwStyle & TVS_HASBUTTONS) && ( dwStyle & TVS_HASLINES))
646 {
647 if ( (wineItem->cChildren) ||
648 (wineItem->cChildren == I_CHILDRENCALLBACK))
649 {
650 /* Setup expand box coordinate to facilitate the LMBClick handling */
651 wineItem->expandBox.left = xpos-4;
652 wineItem->expandBox.top = center-4;
653 wineItem->expandBox.right = xpos+5;
654 wineItem->expandBox.bottom = center+5;
655
656 Rectangle (
657 hdc,
658 wineItem->expandBox.left,
659 wineItem->expandBox.top ,
660 wineItem->expandBox.right,
661 wineItem->expandBox.bottom);
662
663 MoveToEx (hdc, xpos-2, center, NULL);
664 LineTo (hdc, xpos+3, center);
665
666 if (!(wineItem->state & TVIS_EXPANDED)) {
667 MoveToEx (hdc, xpos, center-2, NULL);
668 LineTo (hdc, xpos, center+3);
669 }
670 }
671 }
672
673 /*
674 * Display the image assiciated with this item
675 */
676 xpos += 13; /* update position */
677 if (wineItem->mask & (TVIF_IMAGE|TVIF_SELECTEDIMAGE)) {
678 INT imageIndex;
679 HIMAGELIST *himlp = NULL;
680
681 if (infoPtr->himlNormal)
682 himlp=&infoPtr->himlNormal; /* get the image list */
683
684 if ( (wineItem->state & TVIS_SELECTED) &&
685 (wineItem->iSelectedImage)) {
686
687 /* State images are displayed to the left of the Normal image*/
688 if (infoPtr->himlState)
689 himlp=&infoPtr->himlState;
690
691 /* The item is curently selected */
692 if (wineItem->iSelectedImage == I_IMAGECALLBACK)
693 TREEVIEW_SendDispInfoNotify (
694 hwnd,
695 wineItem,
696 TVN_GETDISPINFO,
697 TVIF_SELECTEDIMAGE);
698
699 imageIndex = wineItem->iSelectedImage;
700
701 } else {
702 /* This item is not selected */
703 if (wineItem->iImage == I_IMAGECALLBACK)
704 TREEVIEW_SendDispInfoNotify (
705 hwnd,
706 wineItem,
707 TVN_GETDISPINFO,
708 TVIF_IMAGE);
709
710 imageIndex = wineItem->iImage;
711 }
712
713 if (himlp)
714 {
715 /* We found an image to display? Draw it. */
716 ImageList_Draw (
717 *himlp,
718 wineItem->iImage,
719 hdc,
720 xpos-2,
721 r.top+1,
722 ILD_NORMAL);
723
724 ImageList_GetIconSize (*himlp, &cx, &cy);
725 xpos+=cx;
726 }
727 }
728
729 /*
730 * Display the text assiciated with this item
731 */
732 r.left=xpos;
733 if ((wineItem->mask & TVIF_TEXT) && (wineItem->pszText))
734 {
735 COLORREF oldBkColor = 0;
736 COLORREF oldTextColor = 0;
737 INT oldBkMode;
738
739 r.left += 3;
740 r.right -= 3;
741
742 wineItem->text.left = r.left;
743 wineItem->text.right = r.right;
744 wineItem->text.top = r.top;
745 wineItem->text.bottom= r.bottom;
746
747 if (wineItem->state & (TVIS_SELECTED | TVIS_DROPHILITED) ) {
748 oldBkMode = SetBkMode (hdc, OPAQUE);
749 oldBkColor = SetBkColor (hdc, GetSysColor( COLOR_HIGHLIGHT));
750 oldTextColor = SetTextColor(hdc, GetSysColor( COLOR_HIGHLIGHTTEXT));
751 }
752 else
753 {
754 oldBkMode = SetBkMode(hdc, TRANSPARENT);
755 oldTextColor = SetTextColor(hdc, GetSysColor( COLOR_WINDOWTEXT));
756 }
757
758 if (wineItem->pszText== LPSTR_TEXTCALLBACKA) {
759// TRACE (treeview,"LPSTR_TEXTCALLBACK\n");
760 TREEVIEW_SendDispInfoNotify (hwnd, wineItem, TVN_GETDISPINFO, TVIF_TEXT);
761 }
762
763 /* Draw it */
764 DrawTextA (
765 hdc,
766 wineItem->pszText,
767 lstrlenA(wineItem->pszText),
768 &wineItem->text,
769 uTextJustify | DT_VCENTER | DT_SINGLELINE );
770
771 /* Obtain the text coordinate */
772 DrawTextA (
773 hdc,
774 wineItem->pszText,
775 lstrlenA(wineItem->pszText),
776 &wineItem->text,
777 uTextJustify | DT_VCENTER | DT_SINGLELINE | DT_CALCRECT);
778
779 /* Restore the hdc state */
780 SetTextColor( hdc, oldTextColor);
781
782 if (oldBkMode != TRANSPARENT)
783 SetBkMode(hdc, oldBkMode);
784 if (wineItem->state & (TVIS_SELECTED | TVIS_DROPHILITED))
785 SetBkColor (hdc, oldBkColor);
786
787 /* Draw the box arround the selected item */
788 if (wineItem->state & TVIS_SELECTED )
789 {
790 HPEN hnewPen = CreatePen(PS_DOT, 0, GetSysColor(COLOR_WINDOWTEXT) );
791 HPEN hOldPen = SelectObject( hdc, hnewPen );
792 POINT points[4];
793
794 points[0].x = wineItem->text.left-1;
795 points[0].y = wineItem->text.top+1;
796 points[1].x = wineItem->text.right;
797 points[1].y = wineItem->text.top+1;
798 points[2].x = wineItem->text.right;
799 points[2].y = wineItem->text.bottom;
800 points[3].x = wineItem->text.left-1;
801 points[3].y = wineItem->text.bottom;
802
803 Polyline (hdc,points,4);
804
805 DeleteObject(hnewPen);
806 SelectObject(hdc, hOldPen);
807 }
808 }
809
810 if (cditem & CDRF_NOTIFYPOSTPAINT)
811 TREEVIEW_SendCustomDrawItemNotify (hwnd, hdc, wineItem, CDDS_ITEMPOSTPAINT);
812
813 SelectObject (hdc, hOldFont);
814}
815
816static LRESULT
817TREEVIEW_GetItemRect (HWND hwnd, WPARAM wParam, LPARAM lParam)
818{
819 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
820 TREEVIEW_ITEM *wineItem;
821 HTREEITEM *iItem;
822 LPRECT lpRect = (LPRECT)lParam;
823
824// TRACE (treeview,"\n");
825 /*
826 * validate parameters
827 */
828 if ( (infoPtr==NULL) || (lpRect == NULL) )
829 return FALSE;
830
831 if (infoPtr->Timer & TV_REFRESH_TIMER_SET)
832 TREEVIEW_Refresh (hwnd); /* we want a rect for the current view */
833
834 /*
835 * retrive the item ptr
836 */
837 iItem = (HTREEITEM *) lParam;
838 wineItem = TREEVIEW_ValidItem (infoPtr, *iItem);
839 if ((!wineItem) || (!wineItem->visible))
840 return FALSE;
841
842 /*
843 * If wParam is TRUE return the text size otherwise return
844 * the whole item size
845 */
846 if ((INT) wParam) {
847 lpRect->left = wineItem->text.left;
848 lpRect->right = wineItem->text.right;
849 lpRect->bottom = wineItem->text.bottom;
850 lpRect->top = wineItem->text.top;
851 } else {
852 lpRect->left = wineItem->rect.left;
853 lpRect->right = wineItem->rect.right;
854 lpRect->bottom = wineItem->rect.bottom;
855 lpRect->top = wineItem->rect.top;
856 }
857
858// TRACE (treeview,"[L:%d R:%d T:%d B:%d]\n",
859// lpRect->left,lpRect->right,
860// lpRect->top,lpRect->bottom);
861
862 return TRUE;
863}
864
865static LRESULT
866TREEVIEW_GetVisibleCount (HWND hwnd, WPARAM wParam, LPARAM lParam)
867
868{
869 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
870
871 return (LRESULT) infoPtr->uVisibleHeight / infoPtr->uRealItemHeight;
872}
873
874
875
876static LRESULT
877TREEVIEW_SetItemA (HWND hwnd, WPARAM wParam, LPARAM lParam)
878{
879 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
880 TREEVIEW_ITEM *wineItem;
881 TVITEMEXA *tvItem;
882 INT iItem,len;
883
884 tvItem=(LPTVITEMEXA) lParam;
885 iItem=(INT)tvItem->hItem;
886// TRACE (treeview,"item %d,mask %x\n",iItem,tvItem->mask);
887
888 wineItem = TREEVIEW_ValidItem (infoPtr, (HTREEITEM)iItem);
889 if (!wineItem) return FALSE;
890
891 if (tvItem->mask & TVIF_CHILDREN) {
892 wineItem->cChildren=tvItem->cChildren;
893 }
894
895 if (tvItem->mask & TVIF_IMAGE) {
896 wineItem->iImage=tvItem->iImage;
897 }
898
899 if (tvItem->mask & TVIF_INTEGRAL) {
900 wineItem->iIntegral=tvItem->iIntegral;
901 }
902
903 if (tvItem->mask & TVIF_PARAM) {
904 wineItem->lParam=tvItem->lParam;
905 }
906
907 if (tvItem->mask & TVIF_SELECTEDIMAGE) {
908 wineItem->iSelectedImage=tvItem->iSelectedImage;
909 }
910
911 if (tvItem->mask & TVIF_STATE) {
912 wineItem->state=tvItem->state & tvItem->stateMask;
913 }
914
915 if (tvItem->mask & TVIF_TEXT) {
916 if (tvItem->pszText!=LPSTR_TEXTCALLBACKA) {
917 len = lstrlenA (tvItem->pszText);
918 if (len > wineItem->cchTextMax)
919 wineItem->pszText = COMCTL32_ReAlloc (wineItem->pszText, len+1);
920 lstrcpynA (wineItem->pszText, tvItem->pszText,len+1);
921 } else {
922 if (wineItem->cchTextMax) {
923 COMCTL32_Free (wineItem->pszText);
924 wineItem->cchTextMax=0;
925 }
926 wineItem->pszText = LPSTR_TEXTCALLBACKA;
927 }
928 }
929
930 return TRUE;
931}
932
933static LRESULT
934TREEVIEW_GetItemState (HWND hwnd, WPARAM wParam, LPARAM lParam)
935
936{
937 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
938 TREEVIEW_ITEM *wineItem;
939
940 wineItem = TREEVIEW_ValidItem (infoPtr, (HTREEITEM)wParam);
941 if (!wineItem) return 0;
942
943 return (wineItem->state & lParam);
944}
945
946static void
947TREEVIEW_Refresh (HWND hwnd)
948
949{
950 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
951 TEXTMETRICA tm;
952 HBRUSH hbrBk;
953 RECT rect;
954 HDC hdc;
955 INT iItem, indent, x, y, cx, height, itemHeight;
956 INT viewtop,viewbottom,viewleft,viewright;
957 TREEVIEW_ITEM *wineItem, *prevItem;
958
959 TRACE("\n");
960
961 hdc=GetDC (hwnd);
962
963 if (infoPtr->Timer & TV_REFRESH_TIMER_SET) {
964 KillTimer (hwnd, TV_REFRESH_TIMER);
965 infoPtr->Timer &= ~TV_REFRESH_TIMER_SET;
966 }
967
968
969 GetClientRect (hwnd, &rect);
970 if ((rect.left-rect.right ==0) || (rect.top-rect.bottom==0)) return;
971
972 infoPtr->cdmode=TREEVIEW_SendCustomDrawNotify
973 (hwnd, CDDS_PREPAINT, hdc, rect);
974
975 if (infoPtr->cdmode==CDRF_SKIPDEFAULT) {
976 ReleaseDC (hwnd, hdc);
977 return;
978 }
979
980 infoPtr->uVisibleHeight= rect.bottom-rect.top;
981 infoPtr->uVisibleWidth= rect.right-rect.left;
982
983 viewtop=infoPtr->cy;
984 viewbottom=infoPtr->cy + rect.bottom-rect.top;
985 viewleft=infoPtr->cx;
986 viewright=infoPtr->cx + rect.right-rect.left;
987
988 /* draw background */
989
990 hbrBk = CreateSolidBrush (infoPtr->clrBk);
991 FillRect(hdc, &rect, hbrBk);
992 DeleteObject(hbrBk);
993
994 iItem=(INT)infoPtr->TopRootItem;
995 infoPtr->firstVisible=0;
996 wineItem=NULL;
997 indent=0;
998 x=y=0;
999 TRACE("[%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("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 (sibItem==wineItem) break;
1736 if (wineItem->parent) {
1737 wineItem->sibling=parentItem->firstChild;
1738 parentItem->firstChild=(HTREEITEM)iItem;
1739 } else {
1740 wineItem->sibling=infoPtr->TopRootItem;
1741 infoPtr->TopRootItem=(HTREEITEM)iItem;
1742 }
1743 sibItem->upsibling=(HTREEITEM)iItem;
1744 break;
1745
1746 case (DWORD) TVI_SORT:
1747 if (sibItem==wineItem)
1748 /*
1749 * This item is the first child of the level and it
1750 * has already been inserted
1751 */
1752 break;
1753 else
1754 {
1755 TREEVIEW_ITEM *aChild;
1756
1757 TREEVIEW_ITEM *previousChild = NULL;
1758 BOOL bItemInserted = FALSE;
1759
1760 if (parentItem)
1761 aChild = &infoPtr->items[(INT)parentItem->firstChild];
1762 else
1763 aChild = &infoPtr->items[(INT)infoPtr->TopRootItem];
1764
1765 /* Iterate the parent children to see where we fit in */
1766 while ( aChild != NULL )
1767 {
1768 INT comp = strcmp(wineItem->pszText, aChild->pszText);
1769 if ( comp < 0 ) /* we are smaller than the current one */
1770 {
1771 TREEVIEW_InsertBefore(infoPtr, wineItem, aChild, parentItem);
1772 bItemInserted = TRUE;
1773 break;
1774 }
1775 else if ( comp > 0 ) /* we are bigger than the current one */
1776 {
1777 previousChild = aChild;
1778 aChild = (aChild->sibling == 0) /* This will help us to exit */
1779 ? NULL /* if there is no more sibling */
1780 : &infoPtr->items[(INT)aChild->sibling];
1781
1782 /* Look at the next item */
1783 continue;
1784 }
1785 else if ( comp == 0 )
1786 {
1787 /*
1788 * An item with this name is already existing, therefore,
1789 * we add after the one we found
1790 */
1791 TREEVIEW_InsertAfter(infoPtr, wineItem, aChild, parentItem);
1792 bItemInserted = TRUE;
1793 break;
1794 }
1795 }
1796
1797 /*
1798 * we reach the end of the child list and the item as not
1799 * yet been inserted, therefore, insert it after the last child.
1800 */
1801 if ( (! bItemInserted ) && (aChild == NULL) )
1802 TREEVIEW_InsertAfter(infoPtr, wineItem, previousChild, parentItem);
1803
1804 break;
1805 }
1806
1807
1808 case (DWORD) TVI_LAST:
1809 if (sibItem==wineItem) break;
1810 while (sibItem->sibling) {
1811 prevsib=sibItem;
1812 sibItem=&infoPtr->items [(INT)sibItem->sibling];
1813 }
1814 sibItem->sibling=(HTREEITEM)iItem;
1815 wineItem->upsibling=sibItem->hItem;
1816 break;
1817 default:
1818 while ((sibItem->sibling) && (sibItem->hItem!=ptdi->hInsertAfter))
1819 {
1820 prevsib=sibItem;
1821 sibItem=&infoPtr->items [(INT)sibItem->sibling];
1822 }
1823 if (sibItem->hItem!=ptdi->hInsertAfter) {
1824// ERR (treeview, "tried to insert item after nonexisting handle.\n");
1825 break;
1826 }
1827 prevsib=sibItem;
1828 if (sibItem->sibling) {
1829 sibItem=&infoPtr->items [(INT)sibItem->sibling];
1830 sibItem->upsibling=(HTREEITEM)iItem;
1831 wineItem->sibling=sibItem->hItem;
1832 }
1833 prevsib->sibling=(HTREEITEM)iItem;
1834 wineItem->upsibling=prevsib->hItem;
1835 break;
1836 }
1837 }
1838
1839
1840/* Fill in info structure */
1841
1842// TRACE (treeview,"new item %d; parent %d, mask %x\n", iItem,
1843// (INT)wineItem->parent,tvItem->mask);
1844
1845 wineItem->mask=tvItem->mask;
1846 wineItem->iIntegral=1;
1847
1848 if (tvItem->mask & TVIF_CHILDREN) {
1849 wineItem->cChildren=tvItem->cChildren;
1850// if (tvItem->cChildren==I_CHILDRENCALLBACK)
1851// FIXME (treeview," I_CHILDRENCALLBACK not supported\n");
1852 }
1853
1854 wineItem->expandBox.left = 0; /* Initialize the expandBox */
1855 wineItem->expandBox.top = 0;
1856 wineItem->expandBox.right = 0;
1857 wineItem->expandBox.bottom = 0;
1858
1859 if (tvItem->mask & TVIF_IMAGE)
1860 wineItem->iImage=tvItem->iImage;
1861
1862 /* If the application sets TVIF_INTEGRAL without
1863 supplying a TVITEMEX structure, it's toast */
1864
1865 if (tvItem->mask & TVIF_INTEGRAL)
1866 wineItem->iIntegral=tvItem->iIntegral;
1867
1868 if (tvItem->mask & TVIF_SELECTEDIMAGE)
1869 wineItem->iSelectedImage=tvItem->iSelectedImage;
1870
1871 if (tvItem->mask & TVIF_STATE) {
1872// TRACE(treeview, "Changing item state from %d to %d\n",
1873// wineItem->state,
1874// tvItem->state);
1875 wineItem->state=tvItem->state;
1876 wineItem->stateMask=tvItem->stateMask;
1877 }
1878
1879
1880 TREEVIEW_QueueRefresh (hwnd);
1881
1882 return (LRESULT) iItem;
1883}
1884
1885
1886static LRESULT
1887TREEVIEW_InsertItemW(HWND hwnd, WPARAM wParam, LPARAM lParam)
1888{
1889 TVINSERTSTRUCTW *tvisW;
1890 TVINSERTSTRUCTA tvisA;
1891 LRESULT lRes;
1892
1893 tvisW = (LPTVINSERTSTRUCTW)lParam;
1894
1895 tvisA.hParent = tvisW->hParent;
1896 tvisA.hInsertAfter = tvisW->hInsertAfter;
1897
1898 tvisA.DUMMYUNIONNAME.item.mask = tvisW->DUMMYUNIONNAME.item.mask;
1899 tvisA.DUMMYUNIONNAME.item.hItem = tvisW->DUMMYUNIONNAME.item.hItem;
1900 tvisA.DUMMYUNIONNAME.item.state = tvisW->DUMMYUNIONNAME.item.state;
1901 tvisA.DUMMYUNIONNAME.item.stateMask = tvisW->DUMMYUNIONNAME.item.stateMask;
1902 tvisA.DUMMYUNIONNAME.item.cchTextMax = tvisW->DUMMYUNIONNAME.item.cchTextMax;
1903
1904 if(tvisW->DUMMYUNIONNAME.item.pszText)
1905 {
1906 if (tvisW->DUMMYUNIONNAME.item.pszText!=LPSTR_TEXTCALLBACKW)
1907 {
1908 int len = lstrlenW (tvisW->DUMMYUNIONNAME.item.pszText)+1;
1909 tvisA.DUMMYUNIONNAME.item.pszText = COMCTL32_Alloc (len);
1910 lstrcpyWtoA (tvisA.DUMMYUNIONNAME.item.pszText,
1911 tvisW->DUMMYUNIONNAME.item.pszText );
1912 }
1913 else
1914 {
1915 tvisA.DUMMYUNIONNAME.item.pszText = LPSTR_TEXTCALLBACKA;
1916 tvisA.DUMMYUNIONNAME.item.cchTextMax = 0;
1917 }
1918 }
1919
1920 tvisA.DUMMYUNIONNAME.item.iImage = tvisW->DUMMYUNIONNAME.item.iImage;
1921 tvisA.DUMMYUNIONNAME.item.iSelectedImage = tvisW->DUMMYUNIONNAME.item.iSelectedImage;
1922 tvisA.DUMMYUNIONNAME.item.cChildren = tvisW->DUMMYUNIONNAME.item.cChildren;
1923 tvisA.DUMMYUNIONNAME.item.lParam = tvisW->DUMMYUNIONNAME.item.lParam;
1924
1925 lRes = TREEVIEW_InsertItemA(hwnd,wParam,(LPARAM)&tvisA);
1926
1927 if (tvisA.DUMMYUNIONNAME.item.pszText!=LPSTR_TEXTCALLBACKA)
1928 {
1929 COMCTL32_Free(tvisA.DUMMYUNIONNAME.item.pszText);
1930 }
1931
1932 return lRes;
1933
1934}
1935
1936
1937static LRESULT
1938TREEVIEW_DeleteItem (HWND hwnd, WPARAM wParam, LPARAM lParam)
1939{
1940 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
1941 INT iItem;
1942 TREEVIEW_ITEM *wineItem;
1943
1944// TRACE (treeview,"\n");
1945 if (!infoPtr) return FALSE;
1946
1947 if (lParam == (INT)TVI_ROOT) {
1948 TREEVIEW_RemoveTree (hwnd);
1949 } else {
1950 iItem= (INT) lParam;
1951 wineItem = TREEVIEW_ValidItem (infoPtr, (HTREEITEM)iItem);
1952 if (!wineItem) return FALSE;
1953// TRACE (treeview,"%s\n",wineItem->pszText);
1954 TREEVIEW_RemoveItem (hwnd, wineItem);
1955 }
1956
1957 TREEVIEW_QueueRefresh (hwnd);
1958
1959 return TRUE;
1960}
1961
1962
1963
1964static LRESULT
1965TREEVIEW_GetIndent (HWND hwnd)
1966{
1967 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
1968
1969// TRACE (treeview,"\n");
1970 return infoPtr->uIndent;
1971}
1972
1973static LRESULT
1974TREEVIEW_SetIndent (HWND hwnd, WPARAM wParam)
1975{
1976 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
1977 INT newIndent;
1978
1979// TRACE (treeview,"\n");
1980 newIndent=(INT) wParam;
1981 if (newIndent < MINIMUM_INDENT) newIndent=MINIMUM_INDENT;
1982 infoPtr->uIndent=newIndent;
1983
1984 return 0;
1985}
1986
1987static LRESULT
1988TREEVIEW_GetToolTips (HWND hwnd)
1989
1990{
1991 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
1992
1993// TRACE (treeview,"\n");
1994 return infoPtr->hwndToolTip;
1995}
1996
1997
1998static LRESULT
1999TREEVIEW_SetToolTips (HWND hwnd, WPARAM wParam)
2000
2001{
2002 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
2003 HWND prevToolTip;
2004
2005// TRACE (treeview,"\n");
2006 prevToolTip=infoPtr->hwndToolTip;
2007 infoPtr->hwndToolTip= (HWND) wParam;
2008
2009 return prevToolTip;
2010}
2011
2012
2013static LRESULT CALLBACK
2014TREEVIEW_GetEditControl (HWND hwnd)
2015
2016{
2017 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
2018
2019 return infoPtr->hwndEdit;
2020}
2021
2022LRESULT CALLBACK
2023TREEVIEW_Edit_SubclassProc (HWND hwnd, UINT uMsg, WPARAM wParam,
2024 LPARAM lParam)
2025{
2026 switch (uMsg)
2027 {
2028 case WM_ERASEBKGND:
2029 {
2030 RECT rc;
2031 HDC hdc = (HDC) wParam;
2032 GetClientRect (hwnd, &rc);
2033 Rectangle (hdc, rc.left, rc.top, rc.right, rc.bottom);
2034 return -1;
2035 }
2036
2037 case WM_GETDLGCODE:
2038 {
2039 return DLGC_WANTARROWS | DLGC_WANTALLKEYS;
2040 }
2041
2042 default:
2043 {
2044 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(GetParent(hwnd));
2045 return CallWindowProcA( infoPtr->wpEditOrig, hwnd, uMsg, wParam, lParam);
2046 }
2047 }
2048
2049 return 0;
2050}
2051
2052
2053/* should handle edit control messages here */
2054
2055static LRESULT
2056TREEVIEW_Command (HWND hwnd, WPARAM wParam, LPARAM lParam)
2057
2058{
2059// TRACE (treeview, "%x %ld\n",wParam, lParam);
2060
2061 switch (HIWORD(wParam))
2062 {
2063 case EN_UPDATE:
2064 {
2065 /*
2066 * Adjust the edit window size
2067 */
2068 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
2069 TREEVIEW_ITEM *editItem = TREEVIEW_ValidItem(infoPtr, infoPtr->editItem);
2070 INT iLength = GetWindowTextLengthA(infoPtr->hwndEdit);
2071 HDC hdc = GetDC(infoPtr->hwndEdit);
2072 TEXTMETRICA tm;
2073
2074 if ( GetTextMetricsA(hdc, &tm) )
2075 {
2076 LONG newWidth = (iLength * tm.tmAveCharWidth) + 15;
2077
2078 SetWindowPos (
2079 infoPtr->hwndEdit,
2080 HWND_TOP,
2081 editItem->text.left - 2,
2082 editItem->text.top - 1,
2083 newWidth,
2084 editItem->text.bottom - editItem->text.top + 3,
2085 SWP_DRAWFRAME );
2086 }
2087 ReleaseDC(hwnd, hdc);
2088
2089 break;
2090 }
2091
2092 case EN_KILLFOCUS:
2093/* TREEVIEW_EndEditLabelNow(hwnd, (WPARAM)FALSE, 0);
2094*/
2095 break;
2096
2097 default:
2098 return SendMessageA (GetParent (hwnd), WM_COMMAND, wParam, lParam);
2099 }
2100
2101 return 0;
2102}
2103
2104static LRESULT
2105TREEVIEW_Size (HWND hwnd, WPARAM wParam, LPARAM lParam)
2106
2107{
2108 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
2109
2110 if (infoPtr->bAutoSize)
2111 {
2112 infoPtr->bAutoSize = FALSE;
2113 return 0;
2114 }
2115 infoPtr->bAutoSize = TRUE;
2116
2117 if (wParam == SIZE_RESTORED)
2118 {
2119 infoPtr->uTotalWidth = LOWORD (lParam);
2120 infoPtr->uTotalHeight = HIWORD (lParam);
2121 } else {
2122// FIXME (treeview,"WM_SIZE flag %x %lx not handled\n", wParam, lParam);
2123 }
2124
2125 TREEVIEW_QueueRefresh (hwnd);
2126 return 0;
2127}
2128
2129
2130
2131static LRESULT
2132TREEVIEW_StyleChanged (HWND hwnd, WPARAM wParam, LPARAM lParam)
2133{
2134// TRACE (treeview,"(%x %lx)\n",wParam,lParam);
2135
2136 TREEVIEW_Refresh(hwnd);
2137
2138 return 0;
2139}
2140
2141static LRESULT
2142TREEVIEW_Create (HWND hwnd, WPARAM wParam, LPARAM lParam)
2143{
2144 TREEVIEW_INFO *infoPtr;
2145 LOGFONTA logFont;
2146 TEXTMETRICA tm;
2147 HDC hdc;
2148
2149// TRACE (treeview,"wnd %x\n",hwnd);
2150 /* allocate memory for info structure */
2151 infoPtr = (TREEVIEW_INFO *) COMCTL32_Alloc (sizeof(TREEVIEW_INFO));
2152
2153 SetWindowLongA( hwnd, 0, (DWORD)infoPtr);
2154
2155 if (infoPtr == NULL) {
2156// ERR (treeview, "could not allocate info memory!\n");
2157 return 0;
2158 }
2159
2160 if ((TREEVIEW_INFO*) GetWindowLongA( hwnd, 0) != infoPtr) {
2161// ERR (treeview, "pointer assignment error!\n");
2162 return 0;
2163 }
2164
2165 hdc=GetDC (hwnd);
2166
2167 /* set default settings */
2168 infoPtr->uInternalStatus=0;
2169 infoPtr->uNumItems=0;
2170 infoPtr->clrBk = GetSysColor (COLOR_WINDOW);
2171 infoPtr->clrText = GetSysColor (COLOR_BTNTEXT);
2172 infoPtr->clrLine = GetSysColor (COLOR_WINDOWTEXT);
2173 infoPtr->cy = 0;
2174 infoPtr->cx = 0;
2175 infoPtr->uIndent = 15;
2176 infoPtr->himlNormal = NULL;
2177 infoPtr->himlState = NULL;
2178 infoPtr->uItemHeight = -1;
2179 GetTextMetricsA (hdc, &tm);
2180 infoPtr->hFont = GetStockObject (DEFAULT_GUI_FONT);
2181 GetObjectA (infoPtr->hFont, sizeof (LOGFONTA), &logFont);
2182 logFont.lfWeight=FW_BOLD;
2183 infoPtr->hBoldFont = CreateFontIndirectA (&logFont);
2184
2185 infoPtr->items = NULL;
2186 infoPtr->selectedItem=0;
2187 infoPtr->clrText=-1; /* use system color */
2188 infoPtr->dropItem=0;
2189 infoPtr->pCallBackSort=NULL;
2190 infoPtr->uScrollTime = 300; /* milliseconds */
2191
2192/*
2193 infoPtr->hwndNotify = GetParent32 (hwnd);
2194 infoPtr->bTransparent = ( GetWindowLongA( hwnd, GWL_STYLE) & TBSTYLE_FLAT);
2195*/
2196
2197 infoPtr->hwndToolTip=0;
2198 if (!( GetWindowLongA( hwnd, GWL_STYLE) & TVS_NOTOOLTIPS)) { /* Create tooltip control */
2199 TTTOOLINFOA ti;
2200
2201 infoPtr->hwndToolTip =
2202 CreateWindowExA (0, TOOLTIPS_CLASSA, NULL, 0,
2203 CW_USEDEFAULT, CW_USEDEFAULT,
2204 CW_USEDEFAULT, CW_USEDEFAULT,
2205 hwnd, 0, 0, 0);
2206
2207 /* Send NM_TOOLTIPSCREATED notification */
2208 if (infoPtr->hwndToolTip) {
2209 NMTOOLTIPSCREATED nmttc;
2210
2211 nmttc.hdr.hwndFrom = hwnd;
2212 nmttc.hdr.idFrom = GetWindowLongA( hwnd, GWL_ID);
2213 nmttc.hdr.code = NM_TOOLTIPSCREATED;
2214 nmttc.hwndToolTips = infoPtr->hwndToolTip;
2215
2216 SendMessageA (GetParent (hwnd), WM_NOTIFY,
2217 (WPARAM) GetWindowLongA( hwnd, GWL_ID), (LPARAM)&nmttc);
2218 }
2219
2220 ZeroMemory (&ti, sizeof(TTTOOLINFOA));
2221 ti.cbSize = sizeof(TTTOOLINFOA);
2222 ti.uFlags = TTF_IDISHWND | TTF_TRACK | TTF_TRANSPARENT ;
2223 ti.hwnd = hwnd;
2224 ti.uId = 0;
2225 ti.lpszText = "Test"; /* LPSTR_TEXTCALLBACK; */
2226 SetRectEmpty (&ti.rect);
2227
2228 SendMessageA (infoPtr->hwndToolTip, TTM_ADDTOOLA, 0, (LPARAM)&ti);
2229 }
2230
2231 infoPtr->hwndEdit = CreateWindowExA (
2232 WS_EX_LEFT,
2233 "EDIT",
2234 0,
2235 WS_CHILD | WS_BORDER | ES_AUTOHSCROLL |
2236 ES_WANTRETURN | ES_LEFT,
2237 0, 0, 0, 0,
2238 hwnd,
2239 0,0,0); /* FIXME: (HMENU)IDTVEDIT,pcs->hInstance,0);*/
2240
2241 SendMessageA ( infoPtr->hwndEdit, WM_SETFONT, infoPtr->hFont, FALSE);
2242 infoPtr->wpEditOrig = (WNDPROC)SetWindowLongA (
2243 infoPtr->hwndEdit,
2244 GWL_WNDPROC,
2245 (LONG) TREEVIEW_Edit_SubclassProc);
2246
2247 ReleaseDC (hwnd, hdc);
2248 return 0;
2249}
2250
2251
2252
2253static LRESULT
2254TREEVIEW_Destroy (HWND hwnd)
2255{
2256 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
2257
2258// TRACE (treeview,"\n");
2259 TREEVIEW_RemoveTree (hwnd);
2260 if (infoPtr->Timer & TV_REFRESH_TIMER_SET)
2261 KillTimer (hwnd, TV_REFRESH_TIMER);
2262 if (infoPtr->hwndToolTip)
2263 DestroyWindow (infoPtr->hwndToolTip);
2264
2265 COMCTL32_Free (infoPtr);
2266 return 0;
2267}
2268
2269
2270static LRESULT
2271TREEVIEW_Paint (HWND hwnd, WPARAM wParam, LPARAM lParam)
2272{
2273 HDC hdc;
2274 PAINTSTRUCT ps;
2275
2276// TRACE (treeview,"\n");
2277 hdc = wParam==0 ? BeginPaint (hwnd, &ps) : (HDC)wParam;
2278 TREEVIEW_Refresh (hwnd);
2279 if(!wParam)
2280 EndPaint (hwnd, &ps);
2281// TRACE (treeview,"done\n");
2282
2283 return DefWindowProcA (hwnd, WM_PAINT, wParam, lParam);
2284}
2285
2286static LRESULT
2287TREEVIEW_SetFocus (HWND hwnd, WPARAM wParam, LPARAM lParam)
2288{
2289 TREEVIEW_SendSimpleNotify (hwnd, NM_SETFOCUS);
2290 InvalidateRect(hwnd, NULL, FALSE);
2291 return 0;
2292}
2293
2294static LRESULT
2295TREEVIEW_KillFocus (HWND hwnd, WPARAM wParam, LPARAM lParam)
2296{
2297 TREEVIEW_SendSimpleNotify (hwnd, NM_KILLFOCUS);
2298 InvalidateRect(hwnd, NULL, FALSE);
2299 return 0;
2300}
2301
2302static LRESULT
2303TREEVIEW_EraseBackground (HWND hwnd, WPARAM wParam, LPARAM lParam)
2304{
2305 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
2306 HBRUSH hBrush = CreateSolidBrush (infoPtr->clrBk);
2307 RECT rect;
2308
2309// TRACE (treeview,"\n");
2310 GetClientRect (hwnd, &rect);
2311 FillRect ((HDC)wParam, &rect, hBrush);
2312 DeleteObject (hBrush);
2313 return TRUE;
2314}
2315
2316
2317
2318
2319
2320
2321/* Notifications */
2322
2323
2324
2325
2326
2327static BOOL
2328TREEVIEW_SendSimpleNotify (HWND hwnd, UINT code)
2329{
2330 NMHDR nmhdr;
2331
2332// TRACE (treeview, "%x\n",code);
2333 nmhdr.hwndFrom = hwnd;
2334 nmhdr.idFrom = GetWindowLongA( hwnd, GWL_ID);
2335 nmhdr.code = code;
2336
2337 return (BOOL) SendMessageA (GetParent (hwnd), WM_NOTIFY,
2338 (WPARAM)nmhdr.idFrom, (LPARAM)&nmhdr);
2339}
2340
2341
2342
2343static BOOL
2344TREEVIEW_SendTreeviewNotify (HWND hwnd, UINT code, UINT action,
2345 HTREEITEM oldItem, HTREEITEM newItem)
2346
2347{
2348 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
2349 NMTREEVIEWA nmhdr;
2350 TREEVIEW_ITEM *wineItem;
2351
2352// TRACE (treeview,"code:%x action:%x olditem:%x newitem:%x\n",
2353// code,action,(INT)oldItem,(INT)newItem);
2354 nmhdr.hdr.hwndFrom = hwnd;
2355 nmhdr.hdr.idFrom = GetWindowLongA( hwnd, GWL_ID);
2356 nmhdr.hdr.code = code;
2357 nmhdr.action = action;
2358 if (oldItem) {
2359 wineItem=& infoPtr->items[(INT)oldItem];
2360 nmhdr.itemOld.mask = wineItem->mask;
2361 nmhdr.itemOld.hItem = wineItem->hItem;
2362 nmhdr.itemOld.state = wineItem->state;
2363 nmhdr.itemOld.stateMask = wineItem->stateMask;
2364 nmhdr.itemOld.iImage = wineItem->iImage;
2365 nmhdr.itemOld.pszText = wineItem->pszText;
2366 nmhdr.itemOld.cchTextMax= wineItem->cchTextMax;
2367 nmhdr.itemOld.iImage = wineItem->iImage;
2368 nmhdr.itemOld.iSelectedImage = wineItem->iSelectedImage;
2369 nmhdr.itemOld.cChildren = wineItem->cChildren;
2370 nmhdr.itemOld.lParam = wineItem->lParam;
2371 }
2372
2373 if (newItem) {
2374 wineItem=& infoPtr->items[(INT)newItem];
2375 nmhdr.itemNew.mask = wineItem->mask;
2376 nmhdr.itemNew.hItem = wineItem->hItem;
2377 nmhdr.itemNew.state = wineItem->state;
2378 nmhdr.itemNew.stateMask = wineItem->stateMask;
2379 nmhdr.itemNew.iImage = wineItem->iImage;
2380 nmhdr.itemNew.pszText = wineItem->pszText;
2381 nmhdr.itemNew.cchTextMax= wineItem->cchTextMax;
2382 nmhdr.itemNew.iImage = wineItem->iImage;
2383 nmhdr.itemNew.iSelectedImage = wineItem->iSelectedImage;
2384 nmhdr.itemNew.cChildren = wineItem->cChildren;
2385 nmhdr.itemNew.lParam = wineItem->lParam;
2386 }
2387
2388 nmhdr.ptDrag.x = 0;
2389 nmhdr.ptDrag.y = 0;
2390
2391 return (BOOL)SendMessageA (GetParent (hwnd), WM_NOTIFY,
2392 (WPARAM) GetWindowLongA( hwnd, GWL_ID), (LPARAM)&nmhdr);
2393
2394}
2395
2396static BOOL
2397TREEVIEW_SendTreeviewDnDNotify (HWND hwnd, UINT code, HTREEITEM dragItem,
2398 POINT pt)
2399{
2400 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
2401 NMTREEVIEWA nmhdr;
2402 TREEVIEW_ITEM *wineItem;
2403
2404// TRACE (treeview,"code:%x dragitem:%x\n", code,(INT)dragItem);
2405
2406 nmhdr.hdr.hwndFrom = hwnd;
2407 nmhdr.hdr.idFrom = GetWindowLongA( hwnd, GWL_ID);
2408 nmhdr.hdr.code = code;
2409 nmhdr.action = 0;
2410 wineItem=& infoPtr->items[(INT)dragItem];
2411 nmhdr.itemNew.mask = wineItem->mask;
2412 nmhdr.itemNew.hItem = wineItem->hItem;
2413 nmhdr.itemNew.state = wineItem->state;
2414 nmhdr.itemNew.lParam = wineItem->lParam;
2415
2416 nmhdr.ptDrag.x = pt.x;
2417 nmhdr.ptDrag.y = pt.y;
2418
2419 return (BOOL)SendMessageA (GetParent (hwnd), WM_NOTIFY,
2420 (WPARAM) GetWindowLongA( hwnd, GWL_ID), (LPARAM)&nmhdr);
2421
2422}
2423
2424
2425
2426static BOOL
2427TREEVIEW_SendDispInfoNotify (HWND hwnd, TREEVIEW_ITEM *wineItem,
2428 UINT code, UINT what)
2429{
2430 NMTVDISPINFOA tvdi;
2431 BOOL retval;
2432 char *buf;
2433
2434// TRACE (treeview,"item %d, action %x, state %d\n",
2435// (INT)wineItem->hItem,
2436// what,
2437// (INT)wineItem->state);
2438
2439 tvdi.hdr.hwndFrom = hwnd;
2440 tvdi.hdr.idFrom = GetWindowLongA( hwnd, GWL_ID);
2441 tvdi.hdr.code = code;
2442 tvdi.item.mask = what;
2443 tvdi.item.hItem = wineItem->hItem;
2444 tvdi.item.state = wineItem->state;
2445 tvdi.item.lParam = wineItem->lParam;
2446 tvdi.item.pszText = COMCTL32_Alloc (128*sizeof(char));
2447 buf = tvdi.item.pszText;
2448
2449 retval=(BOOL)SendMessageA (
2450 GetParent(hwnd),
2451 WM_NOTIFY,
2452 (WPARAM)tvdi.hdr.idFrom,
2453 (LPARAM)&tvdi);
2454
2455 if (what & TVIF_TEXT) {
2456 wineItem->pszText = tvdi.item.pszText;
2457 if (buf==tvdi.item.pszText) {
2458 wineItem->cchTextMax = 128;
2459 } else {
2460// TRACE (treeview,"user-supplied buffer\n");
2461 COMCTL32_Free (buf);
2462 wineItem->cchTextMax = 0;
2463 }
2464 }
2465 if (what & TVIF_SELECTEDIMAGE)
2466 wineItem->iSelectedImage = tvdi.item.iSelectedImage;
2467 if (what & TVIF_IMAGE)
2468 wineItem->iImage = tvdi.item.iImage;
2469 if (what & TVIF_CHILDREN)
2470 wineItem->cChildren = tvdi.item.cChildren;
2471
2472 return retval;
2473}
2474
2475
2476
2477static BOOL
2478TREEVIEW_SendCustomDrawNotify (HWND hwnd, DWORD dwDrawStage, HDC hdc,
2479 RECT rc)
2480{
2481 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
2482 NMTVCUSTOMDRAW nmcdhdr;
2483 LPNMCUSTOMDRAW nmcd;
2484
2485// TRACE (treeview,"drawstage:%lx hdc:%x\n", dwDrawStage, hdc);
2486
2487 nmcd= & nmcdhdr.nmcd;
2488 nmcd->hdr.hwndFrom = hwnd;
2489 nmcd->hdr.idFrom = GetWindowLongA( hwnd, GWL_ID);
2490 nmcd->hdr.code = NM_CUSTOMDRAW;
2491 nmcd->dwDrawStage= dwDrawStage;
2492 nmcd->hdc = hdc;
2493 nmcd->rc.left = rc.left;
2494 nmcd->rc.right = rc.right;
2495 nmcd->rc.bottom = rc.bottom;
2496 nmcd->rc.top = rc.top;
2497 nmcd->dwItemSpec = 0;
2498 nmcd->uItemState = 0;
2499 nmcd->lItemlParam= 0;
2500 nmcdhdr.clrText = infoPtr->clrText;
2501 nmcdhdr.clrTextBk= infoPtr->clrBk;
2502 nmcdhdr.iLevel = 0;
2503
2504 return (BOOL)SendMessageA (GetParent (hwnd), WM_NOTIFY,
2505 (WPARAM) GetWindowLongA( hwnd, GWL_ID), (LPARAM)&nmcdhdr);
2506
2507}
2508
2509
2510
2511/* FIXME: need to find out when the flags in uItemState need to be set */
2512
2513static BOOL
2514TREEVIEW_SendCustomDrawItemNotify (HWND hwnd, HDC hdc,
2515 TREEVIEW_ITEM *wineItem, UINT uItemDrawState)
2516{
2517 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
2518 NMTVCUSTOMDRAW nmcdhdr;
2519 LPNMCUSTOMDRAW nmcd;
2520 DWORD dwDrawStage,dwItemSpec;
2521 UINT uItemState;
2522
2523 dwDrawStage=CDDS_ITEM | uItemDrawState;
2524 dwItemSpec=(DWORD)wineItem->hItem;
2525 uItemState=0;
2526 if (wineItem->hItem==infoPtr->selectedItem) uItemState|=CDIS_SELECTED;
2527 if (wineItem->hItem==infoPtr->focusItem) uItemState|=CDIS_FOCUS;
2528 if (wineItem->hItem==infoPtr->hotItem) uItemState|=CDIS_HOT;
2529
2530 nmcd= & nmcdhdr.nmcd;
2531 nmcd->hdr.hwndFrom = hwnd;
2532 nmcd->hdr.idFrom = GetWindowLongA( hwnd, GWL_ID);
2533 nmcd->hdr.code = NM_CUSTOMDRAW;
2534 nmcd->dwDrawStage= dwDrawStage;
2535 nmcd->hdc = hdc;
2536 nmcd->rc.left = wineItem->rect.left;
2537 nmcd->rc.right = wineItem->rect.right;
2538 nmcd->rc.bottom = wineItem->rect.bottom;
2539 nmcd->rc.top = wineItem->rect.top;
2540 nmcd->dwItemSpec = dwItemSpec;
2541 nmcd->uItemState = uItemState;
2542 nmcd->lItemlParam= wineItem->lParam;
2543
2544 nmcdhdr.clrText = infoPtr->clrText;
2545 nmcdhdr.clrTextBk= infoPtr->clrBk;
2546 nmcdhdr.iLevel = wineItem->iLevel;
2547
2548// TRACE (treeview,"drawstage:%lx hdc:%x item:%lx, itemstate:%x\n",
2549// dwDrawStage, hdc, dwItemSpec, uItemState);
2550
2551 return (BOOL)SendMessageA (GetParent (hwnd), WM_NOTIFY,
2552 (WPARAM) GetWindowLongA( hwnd, GWL_ID), (LPARAM)&nmcdhdr);
2553}
2554
2555
2556
2557/* Note:If the specified item is the child of a collapsed parent item,
2558 the parent's list of child items is (recursively) expanded to reveal the
2559 specified item. This is mentioned for TREEVIEW_SelectItem; don't
2560 know if it also applies here.
2561*/
2562
2563static LRESULT
2564TREEVIEW_Expand (HWND hwnd, WPARAM wParam, LPARAM lParam)
2565{
2566 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
2567 TREEVIEW_ITEM *wineItem;
2568 UINT flag;
2569 INT expand;
2570
2571 flag = (UINT) wParam;
2572 expand = (INT) lParam;
2573
2574 wineItem = TREEVIEW_ValidItem (infoPtr, (HTREEITEM)expand);
2575
2576 if (!wineItem)
2577 return 0;
2578 if (!wineItem->cChildren)
2579 return 0;
2580
2581// TRACE (treeview,"For (%s) flags:%x item:%d state:%d\n",
2582// wineItem->pszText,
2583// flag,
2584// expand,
2585// wineItem->state);
2586
2587 if (wineItem->cChildren==I_CHILDRENCALLBACK) {
2588// FIXME (treeview,"we don't handle I_CHILDRENCALLBACK yet\n");
2589 return 0;
2590 }
2591
2592 if (flag == TVE_TOGGLE) { /* FIXME: check exact behaviour here */
2593 flag &= ~TVE_TOGGLE; /* ie: bitwise ops or 'case' ops */
2594 if (wineItem->state & TVIS_EXPANDED)
2595 flag |= TVE_COLLAPSE;
2596 else
2597 flag |= TVE_EXPAND;
2598 }
2599
2600 switch (flag)
2601 {
2602 case TVE_COLLAPSERESET:
2603// TRACE(treeview, " case TVE_COLLAPSERESET\n");
2604 if (!wineItem->state & TVIS_EXPANDED)
2605 return 0;
2606
2607 wineItem->state &= ~(TVIS_EXPANDEDONCE | TVIS_EXPANDED);
2608 TREEVIEW_RemoveAllChildren (hwnd, wineItem);
2609 break;
2610
2611 case TVE_COLLAPSE:
2612// TRACE(treeview, " case TVE_COLLAPSE\n");
2613 if (!wineItem->state & TVIS_EXPANDED)
2614 return 0;
2615
2616 wineItem->state &= ~TVIS_EXPANDED;
2617 break;
2618
2619 case TVE_EXPAND:
2620// TRACE(treeview, " case TVE_EXPAND\n");
2621 if (wineItem->state & TVIS_EXPANDED)
2622 return 0;
2623
2624// TRACE(treeview, " is not expanded...\n");
2625
2626 if (!(wineItem->state & TVIS_EXPANDEDONCE))
2627 {
2628// TRACE(treeview, " and has never been expanded...\n");
2629 wineItem->state |= TVIS_EXPANDED;
2630
2631 /* this item has never been expanded */
2632 if (TREEVIEW_SendTreeviewNotify (
2633 hwnd,
2634 TVN_ITEMEXPANDING,
2635 TVE_EXPAND,
2636 0,
2637 (HTREEITEM)expand))
2638 {
2639// TRACE(treeview, " TVN_ITEMEXPANDING returned TRUE, exiting...\n");
2640 return FALSE;
2641 }
2642
2643 /* FIXME
2644 * Since the TVN_ITEMEXPANDING message may has caused the parent to
2645 * insert new items which in turn may have cause items placeholder
2646 * reallocation, I reassign the current item pointer so we have
2647 * something valid to work with...
2648 * However, this should not be necessary,
2649 * investigation required in TREEVIEW_InsertItemA
2650 */
2651 wineItem = TREEVIEW_ValidItem (infoPtr, (HTREEITEM)expand);
2652 if (! wineItem)
2653 {
2654// ERR(treeview,
2655// "Catastropic situation, cannot retreive item #%d\n",
2656// expand);
2657 return FALSE;
2658 }
2659
2660 wineItem->state |= TVIS_EXPANDEDONCE;
2661// TRACE(treeview, " TVN_ITEMEXPANDING sent...\n");
2662
2663 TREEVIEW_SendTreeviewNotify (
2664 hwnd,
2665 TVN_ITEMEXPANDED,
2666 TVE_EXPAND,
2667 0,
2668 (HTREEITEM)expand);
2669
2670// TRACE(treeview, " TVN_ITEMEXPANDED sent...\n");
2671
2672 }
2673 else
2674 {
2675 /* this item has already been expanded */
2676 wineItem->state |= TVIS_EXPANDED;
2677 }
2678 break;
2679
2680 case TVE_EXPANDPARTIAL:
2681// TRACE(treeview, " case TVE_EXPANDPARTIAL\n");
2682// FIXME (treeview, "TVE_EXPANDPARTIAL not implemented\n");
2683 wineItem->state ^=TVIS_EXPANDED;
2684 wineItem->state |=TVIS_EXPANDEDONCE;
2685 break;
2686 }
2687
2688// TRACE(treeview, "Exiting, Item %d state is now %d...\n",
2689// expand,
2690// wineItem->state);
2691
2692 TREEVIEW_QueueRefresh (hwnd);
2693 return TRUE;
2694}
2695
2696
2697
2698static TREEVIEW_ITEM *
2699TREEVIEW_HitTestPoint (HWND hwnd, POINT pt)
2700{
2701 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
2702 TREEVIEW_ITEM *wineItem;
2703 RECT rect;
2704
2705 GetClientRect (hwnd, &rect);
2706
2707 if (!infoPtr->firstVisible) return NULL;
2708
2709 wineItem=&infoPtr->items [(INT)infoPtr->firstVisible];
2710
2711 while ((wineItem!=NULL) && (pt.y > wineItem->rect.bottom))
2712 wineItem=TREEVIEW_GetNextListItem (infoPtr,wineItem);
2713
2714 if (!wineItem)
2715 return NULL;
2716
2717 return wineItem;
2718}
2719
2720
2721
2722
2723static LRESULT
2724TREEVIEW_HitTest (HWND hwnd, LPARAM lParam)
2725{
2726 LPTVHITTESTINFO lpht=(LPTVHITTESTINFO) lParam;
2727 TREEVIEW_ITEM *wineItem;
2728 RECT rect;
2729 UINT status,x,y;
2730
2731 GetClientRect (hwnd, &rect);
2732 status=0;
2733 x=lpht->pt.x;
2734 y=lpht->pt.y;
2735 if (x < rect.left) status|=TVHT_TOLEFT;
2736 if (x > rect.right) status|=TVHT_TORIGHT;
2737 if (y < rect.top ) status|=TVHT_ABOVE;
2738 if (y > rect.bottom) status|=TVHT_BELOW;
2739
2740 if (status) {
2741 lpht->flags=status;
2742 return 0;
2743 }
2744
2745 wineItem=TREEVIEW_HitTestPoint (hwnd, lpht->pt);
2746 if (!wineItem) {
2747 lpht->flags=TVHT_NOWHERE;
2748 return 0;
2749 }
2750
2751 /* FIXME: implement other flags
2752 * Assign the appropriate flags depending on the click location
2753 * Intitialize flags before to "|=" it...
2754 */
2755 lpht->flags=0;
2756
2757 if (x < wineItem->expandBox.left)
2758 {
2759 lpht->flags |= TVHT_ONITEMINDENT;
2760 }
2761 else if ( ( x >= wineItem->expandBox.left) &&
2762 ( x <= wineItem->expandBox.right))
2763 {
2764 lpht->flags |= TVHT_ONITEMBUTTON;
2765 }
2766 else if (x < wineItem->rect.right)
2767 {
2768 lpht->flags |= TVHT_ONITEMLABEL;
2769 }
2770 else
2771 {
2772 lpht->flags|=TVHT_ONITEMRIGHT;
2773 }
2774
2775 lpht->hItem=wineItem->hItem;
2776
2777 return (LRESULT) wineItem->hItem;
2778}
2779
2780LRESULT WINAPI
2781TREEVIEW_EndEditLabelNow (HWND hwnd, WPARAM wParam, LPARAM lParam)
2782{
2783 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
2784 TREEVIEW_ITEM *editedItem = TREEVIEW_ValidItem (infoPtr, infoPtr->editItem);
2785 BOOL bRevert = (BOOL)wParam;
2786 BOOL bReturn = ! bRevert;
2787
2788 if ( ! (BOOL)wParam ) /* wParam is set to true to cancel the edition */
2789 {
2790 if ( TREEVIEW_SendDispInfoNotify( /* return true to cancel edition */
2791 hwnd,
2792 editedItem,
2793 TVN_ENDLABELEDIT,
2794 0))
2795 {
2796 bRevert = TRUE;
2797 bReturn = FALSE;
2798 }
2799 }
2800
2801 if (bRevert == FALSE) /* Apply the changes */
2802 {
2803 char tmpText[1024];
2804 int iLength = GetWindowTextA(infoPtr->hwndEdit, tmpText, 1023);
2805 bReturn = FALSE;
2806
2807 if (iLength == 0)
2808 {
2809// ERR( treeview, "Problem retreiving new item label.");
2810 }
2811 else if (iLength >= 1023)
2812 {
2813// ERR( treeview,
2814// "Insuficient space to retrieve new item label, new label ignored.");
2815 }
2816 else
2817 {
2818 if (strcmp( tmpText, editedItem->pszText ) == 0)
2819 /* Do nothing if the label has not changed */
2820 bReturn = TRUE;
2821 else
2822 {
2823 LPSTR tmpLabel = COMCTL32_Alloc( iLength+1 );
2824
2825// if ( tmpLabel == NULL )
2826// ERR( treeview,
2827// "OutOfMemory, cannot allocate space for label");
2828// else
2829 {
2830 COMCTL32_Free(editedItem->pszText);
2831 editedItem->pszText = tmpLabel;
2832 lstrcpyA( editedItem->pszText, tmpText);
2833 bReturn = TRUE;
2834 }
2835 }
2836 }
2837
2838 ShowWindow(infoPtr->hwndEdit, SW_HIDE);
2839 EnableWindow(infoPtr->hwndEdit, FALSE);
2840 infoPtr->editItem = 0;
2841 }
2842
2843 return bReturn;
2844}
2845
2846
2847
2848static LRESULT
2849TREEVIEW_LButtonDoubleClick (HWND hwnd, WPARAM wParam, LPARAM lParam)
2850{
2851 TREEVIEW_ITEM *wineItem;
2852 POINT pt;
2853
2854// TRACE (treeview,"\n");
2855 pt.x = (INT)LOWORD(lParam);
2856 pt.y = (INT)HIWORD(lParam);
2857 SetFocus (hwnd);
2858
2859 wineItem=TREEVIEW_HitTestPoint (hwnd, pt);
2860 if (!wineItem) return 0;
2861// TRACE (treeview,"item %d \n",(INT)wineItem->hItem);
2862
2863 if (TREEVIEW_SendSimpleNotify (hwnd, NM_DBLCLK)!=TRUE) { /* FIXME!*/
2864 TREEVIEW_Expand (hwnd, (WPARAM) TVE_TOGGLE, (LPARAM) wineItem->hItem);
2865 }
2866 return TRUE;
2867}
2868
2869
2870static LRESULT
2871TREEVIEW_LButtonDown (HWND hwnd, WPARAM wParam, LPARAM lParam)
2872{
2873 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
2874 INT iItem;
2875 TVHITTESTINFO ht;
2876
2877 ht.pt.x = (INT)LOWORD(lParam);
2878 ht.pt.y = (INT)HIWORD(lParam);
2879
2880 SetFocus (hwnd);
2881 iItem=TREEVIEW_HitTest (hwnd, (LPARAM) &ht);
2882// TRACE (treeview,"item %d \n",iItem);
2883
2884 if (ht.flags & TVHT_ONITEMBUTTON) {
2885 TREEVIEW_Expand (hwnd, (WPARAM) TVE_TOGGLE, (LPARAM) iItem);
2886 }
2887 else
2888 {
2889 infoPtr->uInternalStatus|=TV_LDRAG;
2890 }
2891
2892 return 0;
2893}
2894
2895static LRESULT
2896TREEVIEW_LButtonUp (HWND hwnd, WPARAM wParam, LPARAM lParam)
2897{
2898 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
2899 INT iItem;
2900 TREEVIEW_ITEM *editItem;
2901 TVHITTESTINFO ht;
2902
2903 ht.pt.x = (INT)LOWORD(lParam);
2904 ht.pt.y = (INT)HIWORD(lParam);
2905
2906// TRACE (treeview,"\n");
2907
2908 /* Return true to cancel default behaviour */
2909 if ( TREEVIEW_SendSimpleNotify (hwnd, NM_CLICK) )
2910 return 0;
2911
2912 /* Get the item */
2913 iItem = TREEVIEW_HitTest (hwnd, (LPARAM) &ht);
2914 if (!iItem)
2915 return 0;
2916
2917 editItem = TREEVIEW_ValidItem(infoPtr, (HTREEITEM)iItem);
2918
2919 infoPtr->uInternalStatus &= ~(TV_LDRAG | TV_LDRAGGING);
2920
2921 /*
2922 * If the style allow editing and the node is already selected
2923 * and the click occured on the item label...
2924 */
2925 if ( ( GetWindowLongA( hwnd, GWL_STYLE) & TVS_EDITLABELS ) &&
2926 ( editItem->state & TVIS_SELECTED ) &&
2927 ( ht.flags & TVHT_ONITEMLABEL ))
2928 {
2929 if ( infoPtr->editItem == 0 ) /* If we are not curently editing */
2930 {
2931 if ( TREEVIEW_SendDispInfoNotify( /* Return true to cancel edition */
2932 hwnd,
2933 editItem,
2934 TVN_BEGINLABELEDIT,
2935 0))
2936 {
2937 return 0;
2938 }
2939
2940// TRACE(treeview,"Edit started for %s.\n", editItem->pszText);
2941 infoPtr->editItem = editItem->hItem;
2942
2943 SetWindowPos (
2944 infoPtr->hwndEdit,
2945 HWND_TOP,
2946 editItem->text.left - 2,
2947 editItem->text.top - 1,
2948 editItem->text.right - editItem->text.left + 20 ,
2949 editItem->text.bottom - editItem->text.top + 3,
2950 SWP_DRAWFRAME );
2951
2952 SetWindowTextA( infoPtr->hwndEdit, editItem->pszText );
2953 SendMessageA ( infoPtr->hwndEdit, EM_SETSEL, 0, -1 );
2954 SetFocus ( infoPtr->hwndEdit);
2955 ShowWindow ( infoPtr->hwndEdit, SW_SHOW);
2956 }
2957 }
2958 else if ( infoPtr->editItem != 0 ) /* If we are curently editing */
2959 {
2960 TREEVIEW_EndEditLabelNow(hwnd, (WPARAM)FALSE, 0);
2961 }
2962 else if ( ht.flags & (TVHT_ONITEMLABEL | TVHT_ONITEMICON))
2963 {
2964 TREEVIEW_DoSelectItem (
2965 hwnd,
2966 TVGN_CARET,
2967 (HTREEITEM)iItem,
2968 TVC_BYMOUSE);
2969 }
2970
2971 return 0;
2972}
2973
2974
2975static LRESULT
2976TREEVIEW_RButtonDown (HWND hwnd, WPARAM wParam, LPARAM lParam)
2977{
2978 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
2979
2980// TRACE (treeview,"\n");
2981 infoPtr->uInternalStatus|=TV_RDRAG;
2982 return 0;
2983}
2984
2985static LRESULT
2986TREEVIEW_RButtonUp (HWND hwnd, WPARAM wParam, LPARAM lParam)
2987{
2988 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
2989
2990// TRACE (treeview,"\n");
2991 if (TREEVIEW_SendSimpleNotify (hwnd, NM_RCLICK)) return 0;
2992 infoPtr->uInternalStatus&= ~(TV_RDRAG | TV_RDRAGGING);
2993 return 0;
2994}
2995
2996
2997static LRESULT
2998TREEVIEW_MouseMove (HWND hwnd, WPARAM wParam, LPARAM lParam)
2999{
3000 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
3001 TREEVIEW_ITEM *hotItem;
3002 POINT pt;
3003
3004 pt.x=(INT) LOWORD (lParam);
3005 pt.y=(INT) HIWORD (lParam);
3006 hotItem=TREEVIEW_HitTestPoint (hwnd, pt);
3007 if (!hotItem) return 0;
3008 infoPtr->focusItem=hotItem->hItem;
3009
3010 if ( GetWindowLongA( hwnd, GWL_STYLE) & TVS_DISABLEDRAGDROP) return 0;
3011
3012 if (infoPtr->uInternalStatus & TV_LDRAG) {
3013 TREEVIEW_SendTreeviewDnDNotify (hwnd, TVN_BEGINDRAG, hotItem->hItem, pt);
3014 infoPtr->uInternalStatus &= ~TV_LDRAG;
3015 infoPtr->uInternalStatus |= TV_LDRAGGING;
3016 infoPtr->dropItem=hotItem->hItem;
3017 return 0;
3018 }
3019
3020 if (infoPtr->uInternalStatus & TV_RDRAG) {
3021 TREEVIEW_SendTreeviewDnDNotify (hwnd, TVN_BEGINRDRAG, hotItem->hItem, pt);
3022 infoPtr->uInternalStatus &= ~TV_RDRAG;
3023 infoPtr->uInternalStatus |= TV_RDRAGGING;
3024 infoPtr->dropItem=hotItem->hItem;
3025 return 0;
3026 }
3027
3028 return 0;
3029}
3030
3031
3032static LRESULT
3033TREEVIEW_CreateDragImage (HWND hwnd, WPARAM wParam, LPARAM lParam)
3034{
3035 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
3036 TREEVIEW_ITEM *dragItem;
3037 INT cx,cy;
3038 HDC hdc,htopdc;
3039 HWND hwtop;
3040 HBITMAP hbmp,hOldbmp;
3041 SIZE size;
3042 RECT rc;
3043 HFONT hOldFont;
3044 char *itemtxt;
3045
3046// TRACE (treeview,"\n");
3047 if (!(infoPtr->himlNormal)) return 0;
3048 dragItem=TREEVIEW_ValidItem (infoPtr, (HTREEITEM) lParam);
3049
3050 if (!dragItem) return 0;
3051 itemtxt=dragItem->pszText;
3052
3053 hwtop=GetDesktopWindow ();
3054 htopdc= GetDC (hwtop);
3055 hdc=CreateCompatibleDC (htopdc);
3056
3057 hOldFont=SelectObject (hdc, infoPtr->hFont);
3058 GetTextExtentPoint32A (hdc, itemtxt, lstrlenA (itemtxt), &size);
3059// TRACE (treeview,"%d %d %s %d\n",size.cx,size.cy,itemtxt,lstrlenA(itemtxt));
3060 hbmp=CreateCompatibleBitmap (htopdc, size.cx, size.cy);
3061 hOldbmp=SelectObject (hdc, hbmp);
3062
3063 ImageList_GetIconSize (infoPtr->himlNormal, &cx, &cy);
3064 size.cx+=cx;
3065 if (cy>size.cy) size.cy=cy;
3066
3067 infoPtr->dragList=ImageList_Create (size.cx, size.cy, ILC_COLOR, 10, 10);
3068 ImageList_Draw (infoPtr->himlNormal, dragItem->iImage, hdc, 0, 0, ILD_NORMAL);
3069
3070/*
3071 ImageList_GetImageInfo (infoPtr->himlNormal, dragItem->hItem, &iminfo);
3072 ImageList_AddMasked (infoPtr->dragList, iminfo.hbmImage, CLR_DEFAULT);
3073*/
3074
3075/* draw item text */
3076
3077 SetRect (&rc, cx, 0, size.cx,size.cy);
3078 DrawTextA (hdc, itemtxt, lstrlenA (itemtxt), &rc, DT_LEFT);
3079 SelectObject (hdc, hOldFont);
3080 SelectObject (hdc, hOldbmp);
3081
3082 ImageList_Add (infoPtr->dragList, hbmp, 0);
3083
3084 DeleteDC (hdc);
3085 DeleteObject (hbmp);
3086 ReleaseDC (hwtop, htopdc);
3087
3088 return (LRESULT)infoPtr->dragList;
3089}
3090
3091
3092static LRESULT
3093TREEVIEW_DoSelectItem (HWND hwnd, INT action, HTREEITEM newSelect, INT cause)
3094
3095{
3096 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
3097 TREEVIEW_ITEM *prevItem,*wineItem;
3098 INT prevSelect;
3099
3100 wineItem = TREEVIEW_ValidItem (infoPtr, (HTREEITEM)newSelect);
3101
3102// TRACE (treeview,"Entering item %d, flag %x, cause %x, state %d\n",
3103// (INT)newSelect,
3104// action,
3105// cause,
3106// wineItem->state);
3107
3108 if ( (wineItem) && (wineItem->parent))
3109 {
3110 /*
3111 * If the item has a collapse parent expand the parent so he
3112 * can expose the item
3113 */
3114 TREEVIEW_ITEM *parentItem = TREEVIEW_ValidItem (infoPtr, wineItem->parent);
3115 if ( !(parentItem->state & TVIS_EXPANDED))
3116 TREEVIEW_Expand (hwnd, TVE_EXPAND, (LPARAM) wineItem->parent);
3117 }
3118
3119 switch (action)
3120 {
3121 case TVGN_CARET:
3122 prevSelect=(INT)infoPtr->selectedItem;
3123
3124 if ((HTREEITEM)prevSelect==newSelect)
3125 return FALSE;
3126
3127 prevItem= TREEVIEW_ValidItem (infoPtr, (HTREEITEM)prevSelect);
3128
3129 if (newSelect)
3130 if (TREEVIEW_SendTreeviewNotify(
3131 hwnd,
3132 TVN_SELCHANGING,
3133 cause,
3134 (HTREEITEM)prevSelect,
3135 (HTREEITEM)newSelect))
3136 return FALSE; /* FIXME: OK? */
3137
3138 if (prevItem)
3139 prevItem->state &= ~TVIS_SELECTED;
3140 if (wineItem)
3141 wineItem->state |= TVIS_SELECTED;
3142
3143 infoPtr->selectedItem=(HTREEITEM)newSelect;
3144
3145 TREEVIEW_SendTreeviewNotify(
3146 hwnd,
3147 TVN_SELCHANGED,
3148 cause,
3149 (HTREEITEM)prevSelect,
3150 (HTREEITEM)newSelect);
3151
3152 break;
3153
3154 case TVGN_DROPHILITE:
3155 prevItem= TREEVIEW_ValidItem (infoPtr, infoPtr->dropItem);
3156
3157 if (prevItem)
3158 prevItem->state &= ~TVIS_DROPHILITED;
3159
3160 infoPtr->dropItem=(HTREEITEM)newSelect;
3161
3162 if (wineItem)
3163 wineItem->state |=TVIS_DROPHILITED;
3164
3165 break;
3166
3167 case TVGN_FIRSTVISIBLE:
3168// FIXME (treeview, "FIRSTVISIBLE not implemented\n");
3169 break;
3170 }
3171
3172 TREEVIEW_QueueRefresh (hwnd);
3173
3174// TRACE (treeview,"Leaving state %d\n", wineItem->state);
3175 return TRUE;
3176}
3177
3178/* FIXME: handle NM_KILLFocus enzo */
3179static LRESULT
3180TREEVIEW_SelectItem (HWND hwnd, WPARAM wParam, LPARAM lParam)
3181
3182{
3183 return TREEVIEW_DoSelectItem (hwnd, wParam, (HTREEITEM) lParam, TVC_UNKNOWN);
3184}
3185
3186
3187
3188
3189static LRESULT
3190TREEVIEW_GetFont (HWND hwnd, WPARAM wParam, LPARAM lParam)
3191
3192{
3193 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
3194
3195// TRACE (treeview,"%x\n",infoPtr->hFont);
3196 return infoPtr->hFont;
3197}
3198
3199static LRESULT
3200TREEVIEW_SetFont (HWND hwnd, WPARAM wParam, LPARAM lParam)
3201
3202{
3203 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
3204 TEXTMETRICA tm;
3205 LOGFONTA logFont;
3206 HFONT hFont, hOldFont;
3207 INT height;
3208 HDC hdc;
3209
3210// TRACE (treeview,"%x %lx\n",wParam, lParam);
3211
3212 infoPtr->hFont = (HFONT)wParam;
3213
3214 hFont = infoPtr->hFont ? infoPtr->hFont : GetStockObject (SYSTEM_FONT);
3215
3216 GetObjectA (infoPtr->hFont, sizeof (LOGFONTA), &logFont);
3217 logFont.lfWeight=FW_BOLD;
3218 infoPtr->hBoldFont = CreateFontIndirectA (&logFont);
3219
3220 hdc = GetDC (0);
3221 hOldFont = SelectObject (hdc, hFont);
3222 GetTextMetricsA (hdc, &tm);
3223 height= tm.tmHeight + tm.tmExternalLeading;
3224 if (height>infoPtr->uRealItemHeight)
3225 infoPtr->uRealItemHeight=height;
3226 SelectObject (hdc, hOldFont);
3227 ReleaseDC (0, hdc);
3228
3229 if (lParam)
3230 TREEVIEW_QueueRefresh (hwnd);
3231
3232 return 0;
3233}
3234
3235
3236
3237static LRESULT
3238TREEVIEW_VScroll (HWND hwnd, WPARAM wParam, LPARAM lParam)
3239
3240{
3241 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
3242 int maxHeight;
3243
3244// TRACE (treeview,"wp %x, lp %lx\n", wParam, lParam);
3245 if (!infoPtr->uInternalStatus & TV_VSCROLL) return FALSE;
3246
3247 switch (LOWORD (wParam)) {
3248 case SB_LINEUP:
3249 if (!infoPtr->cy) return FALSE;
3250 infoPtr->cy -= infoPtr->uRealItemHeight;
3251 if (infoPtr->cy < 0) infoPtr->cy=0;
3252 break;
3253 case SB_LINEDOWN:
3254 maxHeight=infoPtr->uTotalHeight-infoPtr->uVisibleHeight;
3255 if (infoPtr->cy == maxHeight) return FALSE;
3256 infoPtr->cy += infoPtr->uRealItemHeight;
3257 if (infoPtr->cy > maxHeight)
3258 infoPtr->cy = maxHeight;
3259 break;
3260 case SB_PAGEUP:
3261 if (!infoPtr->cy) return FALSE;
3262 infoPtr->cy -= infoPtr->uVisibleHeight;
3263 if (infoPtr->cy < 0) infoPtr->cy=0;
3264 break;
3265 case SB_PAGEDOWN:
3266 maxHeight=infoPtr->uTotalHeight-infoPtr->uVisibleHeight;
3267 if (infoPtr->cy == maxHeight) return FALSE;
3268 infoPtr->cy += infoPtr->uVisibleHeight;
3269 if (infoPtr->cy > maxHeight)
3270 infoPtr->cy = maxHeight;
3271 break;
3272 case SB_THUMBTRACK:
3273 infoPtr->cy = HIWORD (wParam);
3274 break;
3275
3276 }
3277
3278 TREEVIEW_QueueRefresh (hwnd);
3279 return TRUE;
3280}
3281
3282static LRESULT
3283TREEVIEW_HScroll (HWND hwnd, WPARAM wParam, LPARAM lParam)
3284{
3285 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
3286 int maxWidth;
3287
3288// TRACE (treeview,"wp %lx, lp %x\n", lParam, wParam);
3289
3290 if (!infoPtr->uInternalStatus & TV_HSCROLL) return FALSE;
3291
3292 switch (LOWORD (wParam)) {
3293 case SB_LINEUP:
3294 if (!infoPtr->cx) return FALSE;
3295 infoPtr->cx -= infoPtr->uRealItemHeight;
3296 if (infoPtr->cx < 0) infoPtr->cx=0;
3297 break;
3298 case SB_LINEDOWN:
3299 maxWidth=infoPtr->uTotalWidth-infoPtr->uVisibleWidth;
3300 if (infoPtr->cx == maxWidth) return FALSE;
3301 infoPtr->cx += infoPtr->uRealItemHeight; /*FIXME */
3302 if (infoPtr->cx > maxWidth)
3303 infoPtr->cx = maxWidth;
3304 break;
3305 case SB_PAGEUP:
3306 if (!infoPtr->cx) return FALSE;
3307 infoPtr->cx -= infoPtr->uVisibleWidth;
3308 if (infoPtr->cx < 0) infoPtr->cx=0;
3309 break;
3310 case SB_PAGEDOWN:
3311 maxWidth=infoPtr->uTotalWidth-infoPtr->uVisibleWidth;
3312 if (infoPtr->cx == maxWidth) return FALSE;
3313 infoPtr->cx += infoPtr->uVisibleWidth;
3314 if (infoPtr->cx > maxWidth)
3315 infoPtr->cx = maxWidth;
3316 break;
3317 case SB_THUMBTRACK:
3318 infoPtr->cx = HIWORD (wParam);
3319 break;
3320
3321 }
3322
3323 TREEVIEW_QueueRefresh (hwnd);
3324 return TRUE;
3325}
3326
3327
3328static LRESULT
3329TREEVIEW_KeyDown (HWND hwnd, WPARAM wParam, LPARAM lParam)
3330{
3331 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
3332 HTREEITEM hNewSelection = 0;
3333 INT scrollNeeds = -1;
3334 INT cyChangeNeeds = -1;
3335 INT prevSelect = (INT)infoPtr->selectedItem;
3336
3337 TREEVIEW_ITEM *prevItem =
3338 (prevSelect != 0 ) ?
3339 TREEVIEW_ValidItem (infoPtr, (HTREEITEM)prevSelect) :
3340 NULL;
3341
3342 TREEVIEW_ITEM *newItem = NULL;
3343
3344// TRACE (treeview,"%x %lx\n",wParam, lParam);
3345
3346 if (prevSelect == 0)
3347 return FALSE;
3348
3349 switch (wParam) {
3350 case VK_UP:
3351 newItem=TREEVIEW_GetPrevListItem (infoPtr, prevItem);
3352
3353 if (!newItem)
3354 newItem=& infoPtr->items[(INT)infoPtr->TopRootItem];
3355
3356 hNewSelection = newItem->hItem;
3357
3358 if (! newItem->visible)
3359 scrollNeeds = SB_LINEUP;
3360
3361 break;
3362
3363 case VK_DOWN:
3364 newItem=TREEVIEW_GetNextListItem (infoPtr, prevItem);
3365
3366 if (!newItem)
3367 newItem=prevItem;
3368
3369 hNewSelection = newItem->hItem;
3370
3371 if (! newItem->visible)
3372 scrollNeeds = SB_LINEDOWN;
3373
3374 break;
3375
3376 case VK_HOME:
3377 newItem = &infoPtr->items[(INT)infoPtr->TopRootItem];
3378 hNewSelection = newItem->hItem;
3379 cyChangeNeeds = 0;
3380 break;
3381
3382 case VK_END:
3383 newItem = &infoPtr->items[(INT)infoPtr->TopRootItem];
3384 newItem = TREEVIEW_GetLastListItem (infoPtr, newItem);
3385 hNewSelection = newItem->hItem;
3386
3387 if (! newItem->visible)
3388 cyChangeNeeds = infoPtr->uTotalHeight-infoPtr->uVisibleHeight;
3389
3390 break;
3391
3392 case VK_LEFT:
3393 if ( (prevItem->cChildren > 0) && (prevItem->state & TVIS_EXPANDED) )
3394 {
3395 TREEVIEW_Expand(hwnd, TVE_COLLAPSE, prevSelect );
3396 }
3397 else if ((INT)prevItem->parent)
3398 {
3399 newItem = (& infoPtr->items[(INT)prevItem->parent]);
3400 if (! newItem->visible)
3401 /* FIXME find a way to make this item the first visible... */
3402 newItem = NULL;
3403
3404 hNewSelection = newItem->hItem;
3405 }
3406
3407 break;
3408
3409 case VK_RIGHT:
3410 if ( ( prevItem->cChildren > 0) ||
3411 ( prevItem->cChildren == I_CHILDRENCALLBACK))
3412 {
3413 if (! (prevItem->state & TVIS_EXPANDED))
3414 TREEVIEW_Expand(hwnd, TVE_EXPAND, prevSelect );
3415 else
3416 {
3417 newItem = (& infoPtr->items[(INT)prevItem->firstChild]);
3418 hNewSelection = newItem->hItem;
3419 }
3420 }
3421
3422 break;
3423
3424 case VK_ADD:
3425 if (! (prevItem->state & TVIS_EXPANDED))
3426 TREEVIEW_Expand(hwnd, TVE_EXPAND, prevSelect );
3427 break;
3428
3429 case VK_SUBTRACT:
3430 if (prevItem->state & TVIS_EXPANDED)
3431 TREEVIEW_Expand(hwnd, TVE_COLLAPSE, prevSelect );
3432 break;
3433
3434 case VK_PRIOR:
3435
3436 newItem=TREEVIEW_GetListItem(
3437 infoPtr,
3438 prevItem,
3439 -1*(TREEVIEW_GetVisibleCount(hwnd,0,0)-3));
3440 if (!newItem)
3441 newItem=prevItem;
3442
3443 hNewSelection = newItem->hItem;
3444
3445 if (! newItem->visible)
3446 scrollNeeds = SB_PAGEUP;
3447
3448 break;
3449
3450 case VK_NEXT:
3451 newItem=TREEVIEW_GetListItem(
3452 infoPtr,
3453 prevItem,
3454 TREEVIEW_GetVisibleCount(hwnd,0,0)-3);
3455
3456 if (!newItem)
3457 newItem=prevItem;
3458
3459 hNewSelection = newItem->hItem;
3460
3461 if (! newItem->visible)
3462 scrollNeeds = SB_PAGEDOWN;
3463
3464 break;
3465
3466 case VK_BACK:
3467
3468 case VK_RETURN:
3469
3470 default:
3471// FIXME (treeview, "%x not implemented\n", wParam);
3472 break;
3473 }
3474
3475 if (hNewSelection)
3476 {
3477/*
3478 This works but does not send notification...
3479
3480 prevItem->state &= ~TVIS_SELECTED;
3481 newItem->state |= TVIS_SELECTED;
3482 infoPtr->selectedItem = hNewSelection;
3483 TREEVIEW_QueueRefresh (hwnd);
3484*/
3485
3486 if ( TREEVIEW_DoSelectItem(
3487 hwnd,
3488 TVGN_CARET,
3489 (HTREEITEM)hNewSelection,
3490 TVC_BYKEYBOARD))
3491 {
3492 /* If selection change is allowed for the new item, perform scrolling */
3493 if (scrollNeeds != -1)
3494 TREEVIEW_VScroll(hwnd, scrollNeeds, 0);
3495
3496 if (cyChangeNeeds != -1)
3497 infoPtr->cy = cyChangeNeeds;
3498
3499 /* FIXME: Something happen in the load the in the two weeks before
3500 april 1st 1999 which makes this SetFocus mandatory otherwise, the focus
3501 is lost... However the SetFocus should not be required...*/
3502
3503 SetFocus(hwnd);
3504 }
3505 }
3506
3507 return FALSE;
3508}
3509
3510static LRESULT
3511TREEVIEW_GetScrollTime (HWND hwnd)
3512{
3513 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
3514 return infoPtr->uScrollTime;
3515}
3516
3517static LRESULT
3518TREEVIEW_SetScrollTime (HWND hwnd, UINT uScrollTime)
3519{
3520 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
3521 UINT uOldScrollTime = infoPtr->uScrollTime;
3522 infoPtr->uScrollTime = min (uScrollTime, 100);
3523 return uOldScrollTime;
3524}
3525
3526static LRESULT WINAPI
3527TREEVIEW_WindowProc (HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
3528{
3529 switch (uMsg) {
3530 case TVM_INSERTITEMA:
3531 return TREEVIEW_InsertItemA (hwnd, wParam, lParam);
3532
3533 case TVM_INSERTITEMW:
3534 return TREEVIEW_InsertItemW(hwnd,wParam,lParam);
3535
3536 case TVM_DELETEITEM:
3537 return TREEVIEW_DeleteItem (hwnd, wParam, lParam);
3538
3539 case TVM_EXPAND:
3540 return TREEVIEW_Expand (hwnd, wParam, lParam);
3541
3542 case TVM_GETITEMRECT:
3543 return TREEVIEW_GetItemRect (hwnd, wParam, lParam);
3544
3545 case TVM_GETCOUNT:
3546 return TREEVIEW_GetCount (hwnd, wParam, lParam);
3547
3548 case TVM_GETINDENT:
3549 return TREEVIEW_GetIndent (hwnd);
3550
3551 case TVM_SETINDENT:
3552 return TREEVIEW_SetIndent (hwnd, wParam);
3553
3554 case TVM_GETIMAGELIST:
3555 return TREEVIEW_GetImageList (hwnd, wParam, lParam);
3556
3557 case TVM_SETIMAGELIST:
3558 return TREEVIEW_SetImageList (hwnd, wParam, lParam);
3559
3560 case TVM_GETNEXTITEM:
3561 return TREEVIEW_GetNextItem (hwnd, wParam, lParam);
3562
3563 case TVM_SELECTITEM:
3564 return TREEVIEW_SelectItem (hwnd, wParam, lParam);
3565
3566 case TVM_GETITEMA:
3567 return TREEVIEW_GetItemA (hwnd, wParam, lParam);
3568
3569 case TVM_GETITEMW:
3570// FIXME (treeview, "Unimplemented msg TVM_GETITEMW\n");
3571 return 0;
3572
3573 case TVM_SETITEMA:
3574 return TREEVIEW_SetItemA (hwnd, wParam, lParam);
3575
3576 case TVM_SETITEMW:
3577// FIXME (treeview, "Unimplemented msg TVM_SETITEMW\n");
3578 return 0;
3579
3580 case TVM_EDITLABELA:
3581// FIXME (treeview, "Unimplemented msg TVM_EDITLABELA \n");
3582 return 0;
3583
3584 case TVM_EDITLABELW:
3585// FIXME (treeview, "Unimplemented msg TVM_EDITLABELW \n");
3586 return 0;
3587
3588 case TVM_GETEDITCONTROL:
3589 return TREEVIEW_GetEditControl (hwnd);
3590
3591 case TVM_GETVISIBLECOUNT:
3592 return TREEVIEW_GetVisibleCount (hwnd, wParam, lParam);
3593
3594 case TVM_HITTEST:
3595 return TREEVIEW_HitTest (hwnd, lParam);
3596
3597 case TVM_CREATEDRAGIMAGE:
3598 return TREEVIEW_CreateDragImage (hwnd, wParam, lParam);
3599
3600 case TVM_SORTCHILDREN:
3601 //@@@PH 1999/10/25 TREEVIEW_SortChildrenCB is wrong
3602 return TREEVIEW_SortChildren(hwnd, wParam, lParam);
3603
3604 case TVM_ENSUREVISIBLE:
3605// FIXME (treeview, "Unimplemented msg TVM_ENSUREVISIBLE\n");
3606 return 0;
3607
3608 case TVM_SORTCHILDRENCB:
3609 return TREEVIEW_SortChildrenCB(hwnd, wParam, lParam);
3610
3611 case TVM_ENDEDITLABELNOW:
3612 return TREEVIEW_EndEditLabelNow (hwnd, wParam, lParam);
3613
3614 case TVM_GETISEARCHSTRINGA:
3615// FIXME (treeview, "Unimplemented msg TVM_GETISEARCHSTRINGA\n");
3616 return 0;
3617
3618 case TVM_GETISEARCHSTRINGW:
3619// FIXME (treeview, "Unimplemented msg TVM_GETISEARCHSTRINGW\n");
3620 return 0;
3621
3622 case TVM_GETTOOLTIPS:
3623 return TREEVIEW_GetToolTips (hwnd);
3624
3625 case TVM_SETTOOLTIPS:
3626 return TREEVIEW_SetToolTips (hwnd, wParam);
3627
3628 case TVM_SETINSERTMARK:
3629// FIXME (treeview, "Unimplemented msg TVM_SETINSERTMARK\n");
3630 return 0;
3631
3632 case TVM_SETITEMHEIGHT:
3633 return TREEVIEW_SetItemHeight (hwnd, wParam);
3634
3635 case TVM_GETITEMHEIGHT:
3636 return TREEVIEW_GetItemHeight (hwnd);
3637
3638 case TVM_SETBKCOLOR:
3639 return TREEVIEW_SetBkColor (hwnd, wParam, lParam);
3640
3641 case TVM_SETTEXTCOLOR:
3642 return TREEVIEW_SetTextColor (hwnd, wParam, lParam);
3643
3644 case TVM_GETBKCOLOR:
3645 return TREEVIEW_GetBkColor (hwnd);
3646
3647 case TVM_GETTEXTCOLOR:
3648 return TREEVIEW_GetTextColor (hwnd);
3649
3650 case TVM_SETSCROLLTIME:
3651 return TREEVIEW_SetScrollTime (hwnd, (UINT)wParam);
3652
3653 case TVM_GETSCROLLTIME:
3654 return TREEVIEW_GetScrollTime (hwnd);
3655
3656 case TVM_GETITEMSTATE:
3657 return TREEVIEW_GetItemState (hwnd,wParam, lParam);
3658
3659 case TVM_GETLINECOLOR:
3660 return TREEVIEW_GetLineColor (hwnd,wParam, lParam);
3661
3662 case TVM_SETLINECOLOR:
3663 return TREEVIEW_SetLineColor (hwnd,wParam, lParam);
3664
3665 case TVM_SETINSERTMARKCOLOR:
3666// FIXME (treeview, "Unimplemented msg TVM_SETINSERTMARKCOLOR\n");
3667 return 0;
3668
3669 case TVM_SETUNICODEFORMAT:
3670// FIXME (treeview, "Unimplemented msg TVM_SETUNICODEFORMAT\n");
3671 return 0;
3672
3673 case TVM_GETUNICODEFORMAT:
3674// FIXME (treeview, "Unimplemented msg TVM_GETUNICODEFORMAT\n");
3675 return 0;
3676
3677 case WM_COMMAND:
3678 return TREEVIEW_Command (hwnd, wParam, lParam);
3679
3680 case WM_CREATE:
3681 return TREEVIEW_Create (hwnd, wParam, lParam);
3682
3683 case WM_DESTROY:
3684 return TREEVIEW_Destroy (hwnd);
3685
3686/* case WM_ENABLE: */
3687
3688 case WM_ERASEBKGND:
3689 return TREEVIEW_EraseBackground (hwnd, wParam, lParam);
3690
3691 case WM_GETDLGCODE:
3692 return DLGC_WANTARROWS | DLGC_WANTCHARS;
3693
3694 case WM_PAINT:
3695 return TREEVIEW_Paint (hwnd, wParam, lParam);
3696
3697 case WM_GETFONT:
3698 return TREEVIEW_GetFont (hwnd, wParam, lParam);
3699
3700 case WM_SETFONT:
3701 return TREEVIEW_SetFont (hwnd, wParam, lParam);
3702
3703 case WM_KEYDOWN:
3704 return TREEVIEW_KeyDown (hwnd, wParam, lParam);
3705
3706 case WM_SETFOCUS:
3707 return TREEVIEW_SetFocus (hwnd, wParam, lParam);
3708
3709 case WM_KILLFOCUS:
3710 return TREEVIEW_KillFocus (hwnd, wParam, lParam);
3711
3712 case WM_LBUTTONDOWN:
3713 return TREEVIEW_LButtonDown (hwnd, wParam, lParam);
3714
3715 case WM_LBUTTONUP:
3716 return TREEVIEW_LButtonUp (hwnd, wParam, lParam);
3717
3718 case WM_LBUTTONDBLCLK:
3719 return TREEVIEW_LButtonDoubleClick (hwnd, wParam, lParam);
3720
3721 case WM_RBUTTONDOWN:
3722 return TREEVIEW_RButtonDown (hwnd, wParam, lParam);
3723
3724 case WM_RBUTTONUP:
3725 return TREEVIEW_RButtonUp (hwnd, wParam, lParam);
3726
3727 case WM_MOUSEMOVE:
3728 return TREEVIEW_MouseMove (hwnd, wParam, lParam);
3729
3730 case WM_STYLECHANGED:
3731 return TREEVIEW_StyleChanged (hwnd, wParam, lParam);
3732
3733/* case WM_SYSCOLORCHANGE: */
3734/* case WM_SETREDRAW: */
3735
3736 case WM_TIMER:
3737 return TREEVIEW_HandleTimer (hwnd, wParam, lParam);
3738
3739 case WM_SIZE:
3740 return TREEVIEW_Size (hwnd, wParam,lParam);
3741
3742 case WM_HSCROLL:
3743 return TREEVIEW_HScroll (hwnd, wParam, lParam);
3744
3745 case WM_VSCROLL:
3746 return TREEVIEW_VScroll (hwnd, wParam, lParam);
3747
3748 case WM_DRAWITEM:
3749// printf ("drawItem\n");
3750 return DefWindowProcA (hwnd, uMsg, wParam, lParam);
3751
3752 default:
3753// if (uMsg >= WM_USER)
3754// FIXME (treeview, "Unknown msg %04x wp=%08x lp=%08lx\n",
3755// uMsg, wParam, lParam);
3756 return DefWindowProcA (hwnd, uMsg, wParam, lParam);
3757 }
3758 return 0;
3759}
3760
3761
3762VOID
3763TREEVIEW_Register (VOID)
3764{
3765 WNDCLASSA wndClass;
3766
3767// TRACE (treeview,"\n");
3768
3769//SvL: Don't check this now
3770// if (GlobalFindAtomA (WC_TREEVIEWA)) return;
3771
3772 ZeroMemory (&wndClass, sizeof(WNDCLASSA));
3773 wndClass.style = CS_GLOBALCLASS | CS_DBLCLKS;
3774 wndClass.lpfnWndProc = (WNDPROC)TREEVIEW_WindowProc;
3775 wndClass.cbClsExtra = 0;
3776 wndClass.cbWndExtra = sizeof(TREEVIEW_INFO *);
3777 wndClass.hCursor = LoadCursorA (0, IDC_ARROWA);
3778 wndClass.hbrBackground = 0;
3779 wndClass.lpszClassName = WC_TREEVIEWA;
3780
3781 RegisterClassA (&wndClass);
3782}
3783
3784
3785VOID
3786TREEVIEW_Unregister (VOID)
3787{
3788 if (GlobalFindAtomA (WC_TREEVIEWA))
3789 UnregisterClassA (WC_TREEVIEWA, (HINSTANCE)NULL);
3790}
3791
Note: See TracBrowser for help on using the repository browser.