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

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

wine-990704 updates, TBCUSTOMIZE implemented

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