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

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

next steps

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