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

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

ffs() implemented (Edgar Buerkle)

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