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

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

bug fixes (unicode) and improvements

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